summaryrefslogtreecommitdiffstats
path: root/src/VBox/Frontends/VirtualBox/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Frontends/VirtualBox/src
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Frontends/VirtualBox/src')
-rw-r--r--src/VBox/Frontends/VirtualBox/src/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/UITakeSnapshotDialog.cpp337
-rw-r--r--src/VBox/Frontends/VirtualBox/src/UITakeSnapshotDialog.h124
-rw-r--r--src/VBox/Frontends/VirtualBox/src/VBoxAboutDlg.cpp217
-rw-r--r--src/VBox/Frontends/VirtualBox/src/VBoxAboutDlg.h103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/VBoxLicenseViewer.cpp186
-rw-r--r--src/VBox/Frontends/VirtualBox/src/VBoxLicenseViewer.h94
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/UIMonitorCommon.cpp250
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/UIMonitorCommon.h82
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/overview/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityOverviewWidget.cpp1747
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityOverviewWidget.h146
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityToolWidget.cpp226
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityToolWidget.h119
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/vmactivity/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/vmactivity/UIVMActivityMonitor.cpp1605
-rw-r--r--src/VBox/Frontends/VirtualBox/src/activity/vmactivity/UIVMActivityMonitor.h263
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleDetailsWidget.cpp531
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleDetailsWidget.h258
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleManager.cpp1270
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleManager.h298
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialog.cpp203
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialog.h132
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialogPage.cpp139
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialogPage.h111
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileDetailsWidget.cpp424
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileDetailsWidget.h235
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileManager.cpp1063
-rw-r--r--src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileManager.h306
-rw-r--r--src/VBox/Frontends/VirtualBox/src/converter/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/converter/UIConverter.cpp47
-rw-r--r--src/VBox/Frontends/VirtualBox/src/converter/UIConverter.h142
-rw-r--r--src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackend.h354
-rw-r--r--src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendCOM.cpp860
-rw-r--r--src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendGlobal.cpp2905
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensionpackmanager/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensionpackmanager/UIExtensionPackManager.cpp658
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensionpackmanager/UIExtensionPackManager.h221
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIAdvancedSlider.cpp366
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIAdvancedSlider.h146
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonPress.cpp67
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonPress.h69
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonSwitch.cpp80
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonSwitch.h85
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIArrowSplitter.cpp378
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIArrowSplitter.h124
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIComboBox.cpp432
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIComboBox.h173
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIDialog.cpp149
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIDialog.h92
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIDialogButtonBox.cpp169
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIDialogButtonBox.h106
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIDialogContainer.cpp136
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIDialogContainer.h95
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIFileDialog.cpp243
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIFileDialog.h103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIFlowLayout.cpp272
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIFlowLayout.h146
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIInputDialog.cpp130
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIInputDialog.h99
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QILabel.cpp372
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QILabel.h147
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QILabelSeparator.cpp103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QILabelSeparator.h79
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QILineEdit.cpp269
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QILineEdit.h129
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIMainDialog.cpp349
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIMainDialog.h140
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIManagerDialog.cpp257
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIManagerDialog.h235
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIMenu.cpp46
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIMenu.h57
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIMessageBox.cpp428
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIMessageBox.h221
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIProcess.cpp70
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIProcess.h57
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIRichTextLabel.cpp292
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIRichTextLabel.h123
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIRichToolButton.cpp142
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIRichToolButton.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QISplitter.cpp428
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QISplitter.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBar.cpp80
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBar.h61
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBarIndicator.cpp169
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBarIndicator.h151
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIStyledItemDelegate.cpp71
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIStyledItemDelegate.h79
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITabWidget.cpp41
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITabWidget.h51
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITableView.cpp493
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITableView.h151
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITableWidget.cpp362
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITableWidget.h96
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIToolBar.cpp286
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIToolBar.h138
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIToolButton.cpp59
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIToolButton.h57
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITreeView.cpp525
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITreeView.h177
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITreeWidget.cpp511
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QITreeWidget.h147
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIWidgetValidator.cpp199
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/QIWidgetValidator.h239
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/graphics/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsView.cpp125
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsView.h65
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsWidget.cpp40
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsWidget.h54
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extradata/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataDefs.cpp326
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataDefs.h1186
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataManager.cpp5155
-rw-r--r--src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataManager.h969
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/CIShared.h421
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/COMDefs.cpp479
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/COMDefs.h1157
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/COMWrappers.xsl2042
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/QIWithRestorableGeometry.h164
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/QIWithRetranslateUI.h201
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIActionPool.cpp3695
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIActionPool.h672
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolManager.cpp4648
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolManager.h327
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolRuntime.cpp4451
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolRuntime.h337
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIAnimationFramework.cpp185
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIAnimationFramework.h185
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UICloudNetworkingStuff.cpp604
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UICloudNetworkingStuff.h191
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UICommon.cpp2952
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UICommon.h776
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UICursor.cpp139
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UICursor.h54
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UICustomFileSystemModel.cpp626
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UICustomFileSystemModel.h217
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIDefs.cpp42
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIDefs.h170
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.cpp1144
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.h225
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIDetailsGenerator.cpp1170
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIDetailsGenerator.h85
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIDialogPanel.cpp133
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIDialogPanel.h85
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIErrorString.cpp272
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIErrorString.h85
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIExtension.cpp121
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIExtension.h51
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIIconPool.cpp708
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIIconPool.h190
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIImageTools.cpp283
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIImageTools.h62
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UILibraryDefs.h44
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIMachineAttributeSetter.cpp327
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIMachineAttributeSetter.h91
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIMainEventListener.cpp647
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIMainEventListener.h262
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.cpp2413
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.h549
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIModalWindowManager.cpp284
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIModalWindowManager.h111
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIMousePointerShapeData.cpp61
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIMousePointerShapeData.h96
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIPathOperations.cpp163
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIPathOperations.h66
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIPopupCenter.cpp432
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIPopupCenter.h223
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIProgressEventHandler.cpp126
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIProgressEventHandler.h96
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIProgressObject.cpp165
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIProgressObject.h120
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIProgressTask.cpp158
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIProgressTask.h127
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIQObjectStuff.cpp85
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIQObjectStuff.h80
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIShortcutPool.cpp427
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIShortcutPool.h216
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIStarter.cpp167
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIStarter.h81
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UITask.cpp37
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UITask.h83
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UITextTable.cpp152
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UITextTable.h85
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIThreadPool.cpp343
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIThreadPool.h127
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UITranslator.cpp904
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UITranslator.h173
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIVersion.cpp124
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIVersion.h104
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxClientEventHandler.cpp220
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxClientEventHandler.h87
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxEventHandler.cpp382
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxEventHandler.h130
-rw-r--r--src/VBox/Frontends/VirtualBox/src/globals/VBoxUtils.h78
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManager.cpp859
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManager.h198
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerDialog.cpp191
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerDialog.h126
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerGuestTable.cpp1613
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerGuestTable.h180
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerHostTable.cpp555
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerHostTable.h88
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerLogPanel.cpp147
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerLogPanel.h69
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOperationsPanel.cpp480
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOperationsPanel.h104
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOptionsPanel.cpp202
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOptionsPanel.h85
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerTable.cpp1674
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerTable.h323
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlConsole.cpp326
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlConsole.h95
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlDefs.h41
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlInterface.cpp745
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlInterface.h113
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlTreeItem.cpp361
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlTreeItem.h166
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlDialog.cpp151
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlDialog.h123
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlWidget.cpp568
-rw-r--r--src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlWidget.h109
-rw-r--r--src/VBox/Frontends/VirtualBox/src/hardenedmain.cpp119
-rw-r--r--src/VBox/Frontends/VirtualBox/src/helpbrowser/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserDialog.cpp181
-rw-r--r--src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserDialog.h84
-rw-r--r--src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserWidget.cpp2277
-rw-r--r--src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserWidget.h223
-rw-r--r--src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpViewer.cpp1104
-rw-r--r--src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpViewer.h181
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogBookmark.h55
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogPage.cpp344
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogPage.h239
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerBookmarksPanel.cpp271
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerBookmarksPanel.h100
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerDialog.cpp239
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerDialog.h145
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerFilterPanel.cpp641
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerFilterPanel.h125
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerOptionsPanel.cpp204
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerOptionsPanel.h95
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerPanel.cpp93
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerPanel.h73
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerSearchPanel.cpp550
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerSearchPanel.h136
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerTextEdit.cpp574
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerTextEdit.h136
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerWidget.cpp1250
-rw-r--r--src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerWidget.h272
-rw-r--r--src/VBox/Frontends/VirtualBox/src/main.cpp878
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UICloudEntityKey.cpp75
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UICloudEntityKey.h79
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIErrorPane.cpp68
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIErrorPane.h63
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIManagerDefs.h54
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIProgressTaskReadCloudMachineList.cpp100
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIProgressTaskReadCloudMachineList.h87
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UITaskCloudGetSettingsForm.cpp103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UITaskCloudGetSettingsForm.h114
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneGlobal.cpp333
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneGlobal.h122
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneMachine.cpp398
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneMachine.h153
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManager.cpp3841
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManager.h516
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManagerWidget.cpp1144
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManagerWidget.h360
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItem.cpp88
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItem.h235
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemCloud.cpp433
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemCloud.h175
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemLocal.cpp312
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemLocal.h170
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIWelcomePane.cpp255
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/UIWelcomePane.h84
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooser.cpp354
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooser.h239
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.cpp1942
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.h407
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserDefs.h118
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerKeyboard.cpp428
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerKeyboard.h93
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerMouse.cpp263
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerMouse.h78
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItem.cpp729
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItem.h387
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGlobal.cpp861
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGlobal.h282
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGroup.cpp1921
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGroup.h446
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemMachine.cpp1315
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemMachine.h333
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.cpp2042
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.h503
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNode.cpp95
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNode.h165
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGlobal.cpp182
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGlobal.h114
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGroup.cpp328
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGroup.h153
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeMachine.cpp294
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeMachine.h145
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserSearchWidget.cpp218
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserSearchWidget.h104
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserView.cpp333
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserView.h162
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetails.cpp133
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetails.h112
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsContextMenu.cpp1006
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsContextMenu.h117
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElement.cpp1571
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElement.h416
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElements.cpp467
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElements.h615
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsGroup.cpp279
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsGroup.h175
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsItem.cpp308
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsItem.h200
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsModel.cpp1047
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsModel.h310
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsSet.cpp771
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsSet.h247
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsView.cpp207
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsView.h88
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIMachinePreview.cpp600
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/details/UIMachinePreview.h218
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UITools.cpp180
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UITools.h148
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerKeyboard.cpp144
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerKeyboard.h79
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerMouse.cpp94
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerMouse.h79
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsItem.cpp990
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsItem.h335
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsModel.cpp660
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsModel.h305
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsView.cpp221
-rw-r--r--src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsView.h121
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIFDCreationDialog.cpp313
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIFDCreationDialog.h133
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMedium.cpp669
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMedium.h444
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumDefs.cpp154
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumDefs.h138
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumDetailsWidget.cpp874
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumDetailsWidget.h368
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumEnumerator.cpp662
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumEnumerator.h179
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumItem.cpp628
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumItem.h247
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumManager.cpp1832
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumManager.h499
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumSearchWidget.cpp275
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumSearchWidget.h105
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumSelector.cpp811
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/UIMediumSelector.h189
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoBrowserBase.cpp349
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoBrowserBase.h105
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoConfigurationPanel.cpp170
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoConfigurationPanel.h82
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoContentBrowser.cpp786
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoContentBrowser.h141
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreator.cpp779
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreator.h234
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreatorOptionsPanel.cpp94
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreatorOptionsPanel.h74
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoHostBrowser.cpp351
-rw-r--r--src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoHostBrowser.h89
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIDownloader.cpp172
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIDownloader.h173
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderExtensionPack.cpp182
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderExtensionPack.h74
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderGuestAdditions.cpp199
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderGuestAdditions.h72
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderUserManual.cpp120
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderUserManual.h64
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINetworkCustomer.cpp53
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINetworkCustomer.h96
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINetworkDefs.h48
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINetworkReply.cpp895
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINetworkReply.h122
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequest.cpp198
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequest.h127
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequestManager.cpp218
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequestManager.h121
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINewVersionChecker.cpp211
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UINewVersionChecker.h92
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIUpdateDefs.cpp404
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIUpdateDefs.h176
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIUpdateManager.cpp370
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networking/UIUpdateManager.h101
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetCloudNetwork.cpp621
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetCloudNetwork.h303
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetHostNetwork.cpp1193
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetHostNetwork.h458
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetNATNetwork.cpp577
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetNATNetwork.h264
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManager.cpp2655
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManager.h416
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManagerUtils.cpp152
-rw-r--r--src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManagerUtils.h63
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationCenter.cpp891
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationCenter.h238
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationModel.cpp150
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationModel.h101
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObject.cpp341
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObject.h285
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjectItem.cpp444
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjectItem.h173
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjects.cpp4362
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjects.h2700
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationProgressTask.cpp64
-rw-r--r--src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationProgressTask.h76
-rw-r--r--src/VBox/Frontends/VirtualBox/src/objects/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/objects/UIExecutionQueue.cpp89
-rw-r--r--src/VBox/Frontends/VirtualBox/src/objects/UIExecutionQueue.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/objects/UIRichTextString.cpp240
-rw-r--r--src/VBox/Frontends/VirtualBox/src/objects/UIRichTextString.h130
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/UIDesktopServices.h51
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.h65
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.mm366
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp2197
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.h96
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/DockIconPreview.h49
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/Info.plist68
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/PkgInfo1
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.cpp144
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.h86
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.h135
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.mm494
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.h56
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.mm342
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.h99
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.mm179
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin.cpp48
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_cocoa.mm74
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_p.h47
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.cpp372
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxCocoaHelper.h73
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin-cocoa.mm730
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.cpp777
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.h319
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-Info.plist22
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-PkgInfo1
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/tstDarwinKeyboard.cpp108
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-Info.plist30
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-PkgInfo1
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter.mm140
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.asm71
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.cpp198
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.h49
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/UIDesktopServices_win.cpp88
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.cpp128
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.h57
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/VirtualBox.VisualElementsManifest.xml9
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.cpp310
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.h102
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/UIDesktopServices_x11.cpp86
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.cpp679
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.h140
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard-new.cpp289
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard.h55
-rw-r--r--src/VBox/Frontends/VirtualBox/src/precomp_gcc.h181
-rw-r--r--src/VBox/Frontends/VirtualBox/src/precomp_vcc.h171
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIBootFailureDialog.cpp268
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIBootFailureDialog.h120
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIConsoleEventHandler.cpp449
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIConsoleEventHandler.h139
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.cpp808
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.h131
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDropSource_win.cpp176
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDropSource_win.h79
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDEnumFormat_win.cpp201
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDEnumFormat_win.h67
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp912
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.h166
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDMIMEData.cpp311
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIDnDMIMEData.h153
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIFrameBuffer.cpp2501
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIFrameBuffer.h147
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIIndicatorsPool.cpp1675
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIIndicatorsPool.h140
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.cpp2021
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.h241
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMachine.cpp330
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMachine.h144
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMachineDefs.h76
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.cpp3394
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.h467
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.cpp2248
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.h492
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.cpp720
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.h229
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.cpp1345
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.h164
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.cpp326
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.h96
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UISession.cpp2228
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UISession.h632
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIVMCloseDialog.cpp643
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/UIVMCloseDialog.h194
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.cpp83
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.h56
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineLogicFullscreen.cpp825
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineLogicFullscreen.h179
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineViewFullscreen.cpp187
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineViewFullscreen.h85
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineWindowFullscreen.cpp612
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineWindowFullscreen.h164
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/information/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationConfiguration.cpp283
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationConfiguration.h114
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationRuntime.cpp596
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationRuntime.h96
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/information/UIVMInformationDialog.cpp316
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/information/UIVMInformationDialog.h127
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/normal/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.cpp124
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.h58
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineLogicNormal.cpp362
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineLogicNormal.h108
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineViewNormal.cpp248
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineViewNormal.h89
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.cpp780
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.h159
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/scale/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/scale/UIKeyboardHandlerScale.cpp91
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/scale/UIKeyboardHandlerScale.h58
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineLogicScale.cpp240
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineLogicScale.h90
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineViewScale.cpp228
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineViewScale.h72
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineWindowScale.cpp293
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineWindowScale.h90
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/seamless/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.cpp91
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.h58
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineLogicSeamless.cpp378
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineLogicSeamless.h119
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineViewSeamless.cpp192
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineViewSeamless.h82
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineWindowSeamless.cpp415
-rw-r--r--src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineWindowSeamless.h135
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsDefs.cpp54
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsDefs.h313
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialog.cpp897
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialog.h266
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialogSpecific.cpp846
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialogSpecific.h193
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsPage.cpp153
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsPage.h287
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsSelector.cpp822
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsSelector.h294
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsSerializer.cpp410
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/UISettingsSerializer.h257
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAccelerationFeaturesEditor.cpp123
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAccelerationFeaturesEditor.h96
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioControllerEditor.cpp169
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioControllerEditor.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioFeaturesEditor.cpp143
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioFeaturesEditor.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioHostDriverEditor.cpp169
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioHostDriverEditor.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioSettingsEditor.cpp238
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioSettingsEditor.h153
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAutoCaptureKeyboardEditor.cpp97
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIAutoCaptureKeyboardEditor.h75
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIBaseMemoryEditor.cpp443
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIBaseMemoryEditor.h119
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIBootOrderEditor.cpp608
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIBootOrderEditor.h151
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIChipsetEditor.cpp172
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIChipsetEditor.h103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIColorThemeEditor.cpp147
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIColorThemeEditor.h82
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDefaultMachineFolderEditor.cpp116
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDefaultMachineFolderEditor.h87
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDescriptionEditor.cpp91
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDescriptionEditor.h72
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDiskEncryptionSettingsEditor.cpp288
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDiskEncryptionSettingsEditor.h136
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayFeaturesEditor.cpp153
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayFeaturesEditor.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayScreenFeaturesEditor.cpp118
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayScreenFeaturesEditor.h94
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDragAndDropEditor.cpp169
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIDragAndDropEditor.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIExecutionCapEditor.cpp221
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIExecutionCapEditor.h118
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIFontScaleEditor.cpp235
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIFontScaleEditor.h109
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIGraphicsControllerEditor.cpp178
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIGraphicsControllerEditor.h108
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIHostComboEditor.cpp924
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIHostComboEditor.h261
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIHotKeyEditor.cpp505
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIHotKeyEditor.h201
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UILanguageSettingsEditor.cpp440
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UILanguageSettingsEditor.h103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIMaximumGuestScreenSizeEditor.cpp277
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIMaximumGuestScreenSizeEditor.h131
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIMiniToolbarSettingsEditor.cpp151
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIMiniToolbarSettingsEditor.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIMonitorCountEditor.cpp208
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIMonitorCountEditor.h112
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIMotherboardFeaturesEditor.cpp298
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIMotherboardFeaturesEditor.h143
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UINameAndSystemEditor.cpp700
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UINameAndSystemEditor.h251
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkAttachmentEditor.cpp545
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkAttachmentEditor.h162
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkFeaturesEditor.cpp519
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkFeaturesEditor.h201
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkSettingsEditor.cpp351
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkSettingsEditor.h205
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIParavirtProviderEditor.cpp169
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIParavirtProviderEditor.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIPointingHIDEditor.cpp171
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIPointingHIDEditor.h103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIProcessorFeaturesEditor.cpp161
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIProcessorFeaturesEditor.h109
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIProxyFeaturesEditor.cpp236
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIProxyFeaturesEditor.h115
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIRecordingSettingsEditor.cpp971
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIRecordingSettingsEditor.h273
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIScaleFactorEditor.cpp369
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIScaleFactorEditor.h127
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedClipboardEditor.cpp169
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedClipboardEditor.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFolderDetailsEditor.cpp347
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFolderDetailsEditor.h154
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFoldersEditor.cpp853
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFoldersEditor.h196
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIShortcutConfigurationEditor.cpp1000
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIShortcutConfigurationEditor.h222
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UISnapshotFolderEditor.cpp129
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UISnapshotFolderEditor.h94
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIStorageSettingsEditor.cpp5098
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIStorageSettingsEditor.h520
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UITpmEditor.cpp170
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UITpmEditor.h103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBControllerEditor.cpp186
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBControllerEditor.h97
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFilterDetailsEditor.cpp490
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFilterDetailsEditor.h170
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFiltersEditor.cpp700
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFiltersEditor.h205
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBSettingsEditor.cpp196
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBSettingsEditor.h138
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUpdateSettingsEditor.cpp316
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIUpdateSettingsEditor.h118
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDEAuthLibraryEditor.cpp116
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDEAuthLibraryEditor.h87
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDESettingsEditor.cpp359
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDESettingsEditor.h151
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVideoMemoryEditor.cpp385
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVideoMemoryEditor.h163
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVirtualCPUEditor.cpp225
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVirtualCPUEditor.h116
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVisualStateEditor.cpp191
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/editors/UIVisualStateEditor.h103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsDisplay.cpp300
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsDisplay.h107
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsGeneral.cpp245
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsGeneral.h101
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.cpp304
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.h106
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInterface.cpp211
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInterface.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsLanguage.cpp205
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsLanguage.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsProxy.cpp300
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsProxy.h104
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsUpdate.cpp217
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsUpdate.h99
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsAudio.cpp312
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsAudio.h103
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsDisplay.cpp1399
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsDisplay.h196
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsGeneral.cpp978
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsGeneral.h174
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsInterface.cpp592
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsInterface.h127
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsNetwork.cpp1311
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsNetwork.h178
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsPortForwardingDlg.cpp112
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsPortForwardingDlg.h71
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSF.cpp500
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSF.h124
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSerial.cpp1153
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSerial.h126
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsStorage.cpp1047
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsStorage.h155
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSystem.cpp1050
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSystem.h188
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsUSB.cpp729
-rw-r--r--src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsUSB.h122
-rw-r--r--src/VBox/Frontends/VirtualBox/src/snapshots/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotDetailsWidget.cpp2081
-rw-r--r--src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotDetailsWidget.h263
-rw-r--r--src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotPane.cpp1901
-rw-r--r--src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotPane.h273
-rw-r--r--src/VBox/Frontends/VirtualBox/src/softkeyboard/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/softkeyboard/UISoftKeyboard.cpp4481
-rw-r--r--src/VBox/Frontends/VirtualBox/src/softkeyboard/UISoftKeyboard.h148
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIAddDiskEncryptionPasswordDialog.cpp622
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIAddDiskEncryptionPasswordDialog.h107
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceEditorWidget.cpp1937
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceEditorWidget.h376
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceExportEditorWidget.cpp125
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceExportEditorWidget.h57
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceImportEditorWidget.cpp108
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceImportEditorWidget.h57
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIEmptyFilePathSelector.cpp272
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIEmptyFilePathSelector.h134
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIFilePathSelector.cpp665
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIFilePathSelector.h244
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIFilmContainer.cpp328
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIFilmContainer.h83
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIFormEditorWidget.cpp1584
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIFormEditorWidget.h120
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIGuestOSTypeSelectionButton.cpp112
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIGuestOSTypeSelectionButton.h84
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UILineTextEdit.cpp136
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UILineTextEdit.h101
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMediaComboBox.cpp278
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMediaComboBox.h140
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMediumSizeEditor.cpp303
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMediumSizeEditor.h126
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBar.cpp70
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBar.h66
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBarEditorWindow.cpp1408
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBarEditorWindow.h275
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMenuToolBar.cpp333
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMenuToolBar.h86
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMiniToolBar.cpp1215
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIMiniToolBar.h223
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupBox.cpp476
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupBox.h225
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPane.cpp559
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPane.h256
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneButtonPane.cpp242
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneButtonPane.h111
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneDetails.cpp269
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneDetails.h150
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneMessage.cpp219
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneMessage.h141
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStack.cpp358
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStack.h144
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStackViewport.cpp220
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStackViewport.h119
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPortForwardingTable.cpp1242
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIPortForwardingTable.h323
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIProgressDialog.cpp559
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIProgressDialog.h181
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UISearchLineEdit.cpp125
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UISearchLineEdit.h75
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UISlidingAnimation.cpp196
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UISlidingAnimation.h131
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UISlidingToolBar.cpp321
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UISlidingToolBar.h167
-rwxr-xr-xsrc/VBox/Frontends/VirtualBox/src/widgets/UISlidingWidget.cpp188
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UISlidingWidget.h147
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UISpecialControls.cpp199
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UISpecialControls.h199
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIStatusBarEditorWindow.cpp893
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIStatusBarEditorWindow.h192
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UITabBar.cpp1083
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UITabBar.h156
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIToolBox.cpp479
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIToolBox.h84
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIWarningPane.cpp204
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/UIWarningPane.h108
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsButton.cpp214
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsButton.h122
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsRotatorButton.cpp241
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsRotatorButton.h117
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollArea.cpp354
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollArea.h106
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollBar.cpp1077
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollBar.h245
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.cpp541
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.h142
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsToolBar.cpp119
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsToolBar.h78
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsZoomButton.cpp188
-rw-r--r--src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsZoomButton.h109
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.cpp760
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.h220
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.cpp52
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.h92
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVM.cpp126
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVM.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageExpert.cpp278
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageExpert.h105
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageSource.cpp544
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageSource.h153
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVD.cpp217
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVD.h106
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDExpertPage.cpp220
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDExpertPage.h97
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDFormatPage.cpp105
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDFormatPage.h81
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDPathSizePage.cpp155
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDPathSizePage.h83
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDVariantPage.cpp136
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDVariantPage.h79
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVM.cpp272
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVM.h107
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMExpertPage.cpp202
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMExpertPage.h85
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMModePage.cpp127
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMModePage.h81
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMNamePathPage.cpp191
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMNamePathPage.h83
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMTypePage.cpp124
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMTypePage.h69
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIHostnameDomainNameEditor.cpp199
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIHostnameDomainNameEditor.h99
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIUserNamePasswordEditor.cpp379
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIUserNamePasswordEditor.h145
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardCloneVMEditors.cpp590
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardCloneVMEditors.h212
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardDiskEditors.cpp700
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardDiskEditors.h244
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardNewVMEditors.cpp426
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardNewVMEditors.h191
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportApp.cpp346
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportApp.h287
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageExpert.cpp841
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageExpert.h208
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageFormat.cpp1214
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageFormat.h277
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageSettings.cpp290
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageSettings.h110
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageVMs.cpp262
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageVMs.h109
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIApplianceUnverifiedCertificateViewer.cpp128
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIApplianceUnverifiedCertificateViewer.h73
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportApp.cpp324
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportApp.h166
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageExpert.cpp648
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageExpert.h169
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSettings.cpp655
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSettings.h174
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSource.cpp876
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSource.h209
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVM.cpp141
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVM.h109
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageExpert.cpp370
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageExpert.h115
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageProperties.cpp177
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageProperties.h92
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageSource.cpp626
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageSource.h180
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVD.cpp226
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVD.h105
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDExpertPage.cpp229
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDExpertPage.h92
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDFileTypePage.cpp87
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDFileTypePage.h69
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDSizeLocationPage.cpp182
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDSizeLocationPage.h72
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDVariantPage.cpp127
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDVariantPage.h69
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVM.cpp967
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVM.h247
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMDiskPage.cpp497
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMDiskPage.h126
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMExpertPage.cpp1028
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMExpertPage.h168
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMHardwarePage.cpp144
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMHardwarePage.h78
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMNameOSTypePage.cpp745
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMNameOSTypePage.h114
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMSummaryPage.cpp526
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMSummaryPage.h66
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMUnattendedPage.cpp258
-rw-r--r--src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMUnattendedPage.h91
918 files changed, 314473 insertions, 0 deletions
diff --git a/src/VBox/Frontends/VirtualBox/src/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/UITakeSnapshotDialog.cpp b/src/VBox/Frontends/VirtualBox/src/UITakeSnapshotDialog.cpp
new file mode 100644
index 00000000..77b7fbb7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/UITakeSnapshotDialog.cpp
@@ -0,0 +1,337 @@
+/* $Id: UITakeSnapshotDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UITakeSnapshotDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QStyle>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QILabel.h"
+#include "VBoxUtils.h"
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIMessageCenter.h"
+#include "UITakeSnapshotDialog.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+#include "CMedium.h"
+#include "CMediumAttachment.h"
+
+
+UITakeSnapshotDialog::UITakeSnapshotDialog(QWidget *pParent, const CMachine &comMachine)
+ : QIWithRetranslateUI<QIDialog>(pParent)
+ , m_comMachine(comMachine)
+ , m_cImmutableMedia(0)
+ , m_pLabelIcon(0)
+ , m_pLabelName(0), m_pEditorName(0)
+ , m_pLabelDescription(0), m_pEditorDescription(0)
+ , m_pLabelInfo(0)
+ , m_pButtonBox(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UITakeSnapshotDialog::setIcon(const QIcon &icon)
+{
+ m_icon = icon;
+ updatePixmap();
+}
+
+void UITakeSnapshotDialog::setName(const QString &strName)
+{
+ m_pEditorName->setText(strName);
+}
+
+QString UITakeSnapshotDialog::name() const
+{
+ return m_pEditorName->text();
+}
+
+QString UITakeSnapshotDialog::description() const
+{
+ return m_pEditorDescription->toPlainText();
+}
+
+bool UITakeSnapshotDialog::event(QEvent *pEvent)
+{
+ /* Handle know event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Show:
+ case QEvent::ScreenChangeInternal:
+ {
+ /* Update pixmap: */
+ updatePixmap();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QIDialog>::event(pEvent);
+}
+
+void UITakeSnapshotDialog::retranslateUi()
+{
+ setWindowTitle(tr("Take Snapshot of Virtual Machine"));
+ m_pLabelName->setText(tr("Snapshot &Name"));
+ m_pEditorName->setToolTip(tr("Holds the snapshot name"));
+ m_pLabelDescription->setText(tr("Snapshot &Description"));
+ m_pEditorDescription->setToolTip(tr("Holds the snapshot description"));
+ m_pLabelInfo->setText(tr("Warning: You are taking a snapshot of a running machine which has %n immutable image(s) "
+ "attached to it. As long as you are working from this snapshot the immutable image(s) "
+ "will not be reset to avoid loss of data.", "", m_cImmutableMedia));
+
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText(tr("Ok"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
+ m_pButtonBox->button(QDialogButtonBox::Help)->setText(tr("Help"));
+
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(tr("Take Snapshot and close the dialog"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setStatusTip(tr("Close dialog without taking a snapshot"));
+ m_pButtonBox->button(QDialogButtonBox::Help)->setStatusTip(tr("Show dialog help"));
+
+ m_pButtonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents);
+
+ if (m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString().isEmpty())
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setToolTip(tr("Accept"));
+ else
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setToolTip(tr("Accept (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
+
+ if (m_pButtonBox->button(QDialogButtonBox::Cancel)->shortcut().toString().isEmpty())
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setToolTip(tr("Cancel"));
+ else
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setToolTip(tr("Cancel (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+
+ if (m_pButtonBox->button(QDialogButtonBox::Help)->shortcut().toString().isEmpty())
+ m_pButtonBox->button(QDialogButtonBox::Help)->setToolTip(tr("Show Help"));
+ else
+ m_pButtonBox->button(QDialogButtonBox::Help)->setToolTip(tr("Show Help (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Help)->shortcut().toString()));
+ }
+}
+
+void UITakeSnapshotDialog::sltHandleNameChanged(const QString &strName)
+{
+ /* Update button state depending on snapshot name value: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(!strName.trimmed().isEmpty());
+}
+
+void UITakeSnapshotDialog::prepare()
+{
+ /* Prepare contents: */
+ prepareContents();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Invent minimum size: */
+ QSize minimumSize;
+ const int iHostScreen = UIDesktopWidgetWatchdog::screenNumber(parentWidget());
+ if (iHostScreen >= 0 && iHostScreen < UIDesktopWidgetWatchdog::screenCount())
+ {
+ /* On the basis of current host-screen geometry if possible: */
+ const QRect screenGeometry = gpDesktop->screenGeometry(iHostScreen);
+ if (screenGeometry.isValid())
+ minimumSize = screenGeometry.size() / 4;
+ }
+ /* Fallback to default size if we failed: */
+ if (minimumSize.isNull())
+ minimumSize = QSize(800, 600);
+ /* Resize to initial size: */
+ setMinimumSize(minimumSize);
+}
+
+void UITakeSnapshotDialog::prepareContents()
+{
+ /* Create layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ pLayout->setSpacing(20);
+ pLayout->setContentsMargins(40, 20, 40, 20);
+#else
+ pLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) * 2);
+#endif
+
+ /* Create sub-layout: */
+ QVBoxLayout *pSubLayout1 = new QVBoxLayout;
+ if (pSubLayout1)
+ {
+ /* Create icon label: */
+ m_pLabelIcon = new QLabel;
+ if (m_pLabelIcon)
+ {
+ /* Configure label: */
+ m_pLabelIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ /* Add into layout: */
+ pSubLayout1->addWidget(m_pLabelIcon);
+ }
+
+ /* Add stretch: */
+ pSubLayout1->addStretch();
+
+ /* Add into layout: */
+ pLayout->addLayout(pSubLayout1, 0, 0, 2, 1);
+ }
+
+ /* Create sub-layout 2: */
+ QVBoxLayout *pSubLayout2 = new QVBoxLayout;
+ if (pSubLayout2)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ pSubLayout2->setSpacing(5);
+#else
+ pSubLayout2->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ /* Create name label: */
+ m_pLabelName = new QLabel;
+ if (m_pLabelName)
+ {
+ /* Add into layout: */
+ pSubLayout2->addWidget(m_pLabelName);
+ }
+
+ /* Create name editor: */
+ m_pEditorName = new QLineEdit;
+ if (m_pEditorName)
+ {
+ /* Configure editor: */
+ m_pLabelName->setBuddy(m_pEditorName);
+ connect(m_pEditorName, &QLineEdit::textChanged,
+ this, &UITakeSnapshotDialog::sltHandleNameChanged);
+
+ /* Add into layout: */
+ pSubLayout2->addWidget(m_pEditorName);
+ }
+
+ /* Add into layout: */
+ pLayout->addLayout(pSubLayout2, 0, 1);
+ }
+
+ /* Create sub-layout 3: */
+ QVBoxLayout *pSubLayout3 = new QVBoxLayout;
+ if (pSubLayout3)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ pSubLayout3->setSpacing(5);
+#else
+ pSubLayout3->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ /* Create description label: */
+ m_pLabelDescription = new QLabel;
+ if (m_pLabelDescription)
+ {
+ /* Add into layout: */
+ pSubLayout3->addWidget(m_pLabelDescription);
+ }
+
+ /* Create description editor: */
+ m_pEditorDescription = new QTextEdit;
+ if (m_pEditorDescription)
+ {
+ /* Configure editor: */
+ m_pLabelDescription->setBuddy(m_pEditorDescription);
+
+ /* Add into layout: */
+ pSubLayout3->addWidget(m_pEditorDescription);
+ }
+
+ /* Add into layout: */
+ pLayout->addLayout(pSubLayout3, 1, 1);
+ }
+
+ /* Create information label: */
+ m_pLabelInfo = new QILabel;
+ if (m_pLabelInfo)
+ {
+ /* Configure label: */
+ m_pLabelInfo->setWordWrap(true);
+ m_pLabelInfo->useSizeHintForWidth(400);
+
+ /* Calculate the amount of immutable attachments: */
+ if (m_comMachine.GetState() == KMachineState_Paused)
+ {
+ foreach (const CMediumAttachment &comAttachment, m_comMachine.GetMediumAttachments())
+ {
+ CMedium comMedium = comAttachment.GetMedium();
+ if ( !comMedium.isNull()
+ && !comMedium.GetParent().isNull()
+ && comMedium.GetBase().GetType() == KMediumType_Immutable)
+ ++m_cImmutableMedia;
+ }
+ }
+ /* Hide if machine have no immutable attachments: */
+ if (!m_cImmutableMedia)
+ m_pLabelInfo->setHidden(true);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pLabelInfo, 2, 0, 1, 2);
+ }
+
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ if (m_pButtonBox)
+ {
+ /* Configure button-box: */
+ m_pButtonBox->setStandardButtons( QDialogButtonBox::Ok
+ | QDialogButtonBox::Cancel
+ | QDialogButtonBox::Help);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted,
+ this, &UITakeSnapshotDialog::accept);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected,
+ this, &UITakeSnapshotDialog::reject);
+ connect(m_pButtonBox->button(QIDialogButtonBox::Help), &QPushButton::pressed,
+ &(msgCenter()), &UIMessageCenter::sltHandleHelpRequest);
+ m_pButtonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents);
+ uiCommon().setHelpKeyword(m_pButtonBox->button(QIDialogButtonBox::Help), "snapshots");
+ /* Add into layout: */
+ pLayout->addWidget(m_pButtonBox, 3, 0, 1, 2);
+ }
+ }
+}
+
+void UITakeSnapshotDialog::updatePixmap()
+{
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
+ m_pLabelIcon->setPixmap(m_icon.pixmap(windowHandle(), QSize(iIconMetric, iIconMetric)));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/UITakeSnapshotDialog.h b/src/VBox/Frontends/VirtualBox/src/UITakeSnapshotDialog.h
new file mode 100644
index 00000000..7d45a1d8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/UITakeSnapshotDialog.h
@@ -0,0 +1,124 @@
+/* $Id: UITakeSnapshotDialog.h $ */
+/** @file
+ * VBox Qt GUI - UITakeSnapshotDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_UITakeSnapshotDialog_h
+#define FEQT_INCLUDED_SRC_UITakeSnapshotDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class QLineEdit;
+class QTextEdit;
+class QIDialogButtonBox;
+class QILabel;
+class CMachine;
+
+/** QIDialog subclass for taking snapshot name/description. */
+class SHARED_LIBRARY_STUFF UITakeSnapshotDialog : public QIWithRetranslateUI<QIDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs take snapshot dialog passing @ pParent to the base-class.
+ * @param comMachine Brings the machine to take snapshot for. */
+ UITakeSnapshotDialog(QWidget *pParent, const CMachine &comMachine);
+
+ /** Defines snapshot @a icon. */
+ void setIcon(const QIcon &icon);
+
+ /** Defines snapshot @a strName. */
+ void setName(const QString &strName);
+
+ /** Returns snapshot name. */
+ QString name() const;
+ /** Returns snapshot description. */
+ QString description() const;
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles @a strName change signal. */
+ void sltHandleNameChanged(const QString &strName);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares contents. */
+ void prepareContents();
+
+ /** Updates pixmap. */
+ void updatePixmap();
+
+ /** Holds the wrapper of machine to take snapshot for. */
+ const CMachine &m_comMachine;
+
+ /** Holds the snapshot icon. */
+ QIcon m_icon;
+
+ /** Holds the amount of immutable attachments. */
+ int m_cImmutableMedia;
+
+ /** Holds the icon label instance. */
+ QLabel *m_pLabelIcon;
+
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the name editor instance. */
+ QLineEdit *m_pEditorName;
+
+ /** Holds the description label instance. */
+ QLabel *m_pLabelDescription;
+ /** Holds the description editor instance. */
+ QTextEdit *m_pEditorDescription;
+
+ /** Holds the information label instance. */
+ QILabel *m_pLabelInfo;
+
+ /** Holds the dialog button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_UITakeSnapshotDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/VBoxAboutDlg.cpp b/src/VBox/Frontends/VirtualBox/src/VBoxAboutDlg.cpp
new file mode 100644
index 00000000..876e5a0f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/VBoxAboutDlg.cpp
@@ -0,0 +1,217 @@
+/* $Id: VBoxAboutDlg.cpp $ */
+/** @file
+ * VBox Qt GUI - VBoxAboutDlg class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDialogButtonBox>
+#include <QDir>
+#include <QEvent>
+#include <QLabel>
+#include <QPainter>
+#include <QPushButton>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "VBoxAboutDlg.h"
+#include "UICommon.h"
+
+/* Other VBox includes: */
+#include <iprt/path.h>
+#include <VBox/version.h> /* VBOX_VENDOR */
+
+
+VBoxAboutDlg::VBoxAboutDlg(QWidget *pParent, const QString &strVersion)
+#ifdef VBOX_WS_MAC
+ // No need for About dialog parent on macOS.
+ // First of all, non of other native apps (Safari, App Store, iTunes) centers About dialog according the app itself, they do
+ // it according to screen instead, we should do it as well. Besides that since About dialog is not modal, it will be in
+ // conflict with modal dialogs if there will be a parent passed, because the dialog will not have own event-loop in that case.
+ : QIWithRetranslateUI2<QIDialog>(0)
+ , m_pPseudoParent(pParent)
+#else
+ // On other hosts we will keep the current behavior for now.
+ // First of all it's quite difficult to find native (Metro UI) Windows app which have About dialog at all. But non-native
+ // cross-platform apps (Qt Creator, VLC) centers About dialog according the app exactly.
+ : QIWithRetranslateUI2<QIDialog>(pParent)
+ , m_pPseudoParent(0)
+#endif
+ , m_strVersion(strVersion)
+ , m_pMainLayout(0)
+ , m_pLabel(0)
+ , m_fFixedSizeSet(false)
+{
+ /* Prepare: */
+ prepare();
+}
+
+bool VBoxAboutDlg::event(QEvent *pEvent)
+{
+ /* Set fixed-size for dialog: */
+ if (!m_fFixedSizeSet && pEvent->type() == QEvent::Show)
+ {
+ m_fFixedSizeSet = true;
+ setFixedSize(m_size);
+ }
+
+ /* Call to base-class: */
+ return QIDialog::event(pEvent);
+}
+
+void VBoxAboutDlg::paintEvent(QPaintEvent *)
+{
+ /* Draw About-VirtualBox background image: */
+ QPainter painter(this);
+ painter.drawPixmap(0, 0, m_pixmap);
+}
+
+void VBoxAboutDlg::retranslateUi()
+{
+ setWindowTitle(tr("VirtualBox - About"));
+ const QString strAboutText = tr("VirtualBox Graphical User Interface");
+#ifdef VBOX_BLEEDING_EDGE
+ const QString strVersionText = "EXPERIMENTAL build %1 - " + QString(VBOX_BLEEDING_EDGE);
+#else
+ const QString strVersionText = tr("Version %1");
+#endif
+#ifdef VBOX_OSE
+ m_strAboutText = strAboutText + " " + strVersionText.arg(m_strVersion) + "\n"
+ + QString("%1 2004-" VBOX_C_YEAR " " VBOX_VENDOR).arg(QChar(0xa9));
+#else
+ m_strAboutText = strAboutText + "\n" + strVersionText.arg(m_strVersion);
+#endif
+ m_strAboutText = m_strAboutText + QString(" (Qt%1)").arg(qVersion());
+ m_strAboutText = m_strAboutText + "\n" + QString("Copyright %1 %2 %3.")
+ .arg(QChar(0xa9)).arg(VBOX_C_YEAR).arg(VBOX_VENDOR);
+ AssertPtrReturnVoid(m_pLabel);
+ m_pLabel->setText(m_strAboutText);
+}
+
+void VBoxAboutDlg::prepare()
+{
+ /* Delete dialog on close: */
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ /* Make sure the dialog is deleted on pseudo-parent destruction: */
+ if (m_pPseudoParent)
+ connect(m_pPseudoParent, &QObject::destroyed, this, &VBoxAboutDlg::close);
+
+ /* Choose default image: */
+ QString strPath(":/about.png");
+
+ /* Branding: Use a custom about splash picture if set: */
+ const QString strSplash = uiCommon().brandingGetKey("UI/AboutSplash");
+ if (uiCommon().brandingIsActive() && !strSplash.isEmpty())
+ {
+ char szExecPath[1024];
+ RTPathExecDir(szExecPath, 1024);
+ QString strTmpPath = QString("%1/%2").arg(szExecPath).arg(strSplash);
+ if (QFile::exists(strTmpPath))
+ strPath = strTmpPath;
+ }
+
+ /* Load image: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
+ const double dRatio = (double)iIconMetric / 32;
+ const QIcon icon = UIIconPool::iconSet(strPath);
+ m_size = icon.availableSizes().value(0, QSize(640, 480));
+ m_size *= dRatio;
+ m_pixmap = icon.pixmap(m_size);
+
+ // WORKAROUND:
+ // Since we don't have x3 and x4 HiDPI icons yet,
+ // and we hadn't enabled automatic up-scaling for now,
+ // we have to make sure m_pixmap is upscaled to required size.
+ const QSize actualSize = m_pixmap.size() / m_pixmap.devicePixelRatio();
+ if ( actualSize.width() < m_size.width()
+ || actualSize.height() < m_size.height())
+ m_pixmap = m_pixmap.scaled(m_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+ /* Prepare main-layout: */
+ prepareMainLayout();
+
+ /* Translate: */
+ retranslateUi();
+}
+
+void VBoxAboutDlg::prepareMainLayout()
+{
+ /* Create main-layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (m_pMainLayout)
+ {
+ /* Prepare label: */
+ prepareLabel();
+
+ /* Prepare close-button: */
+ prepareCloseButton();
+ }
+}
+
+void VBoxAboutDlg::prepareLabel()
+{
+ /* Create label for version text: */
+ m_pLabel = new QLabel;
+ if (m_pLabel)
+ {
+ /* Prepare label for version text: */
+ QPalette palette;
+ /* Branding: Set a different text color (because splash also could be white),
+ * otherwise use white as default color: */
+ const QString strColor = uiCommon().brandingGetKey("UI/AboutTextColor");
+ if (!strColor.isEmpty())
+ palette.setColor(QPalette::WindowText, QColor(strColor).name());
+ else
+ palette.setColor(QPalette::WindowText, Qt::black);
+ m_pLabel->setPalette(palette);
+ m_pLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
+ m_pLabel->setFont(font());
+
+ /* Add label to the main-layout: */
+ m_pMainLayout->addWidget(m_pLabel);
+ m_pMainLayout->setAlignment(m_pLabel, Qt::AlignRight | Qt::AlignBottom);
+ }
+}
+
+void VBoxAboutDlg::prepareCloseButton()
+{
+ /* Create button-box: */
+ QDialogButtonBox *pButtonBox = new QDialogButtonBox;
+ if (pButtonBox)
+ {
+ /* Create close-button: */
+ QPushButton *pCloseButton = pButtonBox->addButton(QDialogButtonBox::Close);
+ AssertPtrReturnVoid(pCloseButton);
+ /* Prepare close-button: */
+ connect(pButtonBox, &QDialogButtonBox::rejected, this, &VBoxAboutDlg::reject);
+
+ /* Add button-box to the main-layout: */
+ m_pMainLayout->addWidget(pButtonBox);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/VBoxAboutDlg.h b/src/VBox/Frontends/VirtualBox/src/VBoxAboutDlg.h
new file mode 100644
index 00000000..d99f15fe
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/VBoxAboutDlg.h
@@ -0,0 +1,103 @@
+/* $Id: VBoxAboutDlg.h $ */
+/** @file
+ * VBox Qt GUI - VBoxAboutDlg class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_VBoxAboutDlg_h
+#define FEQT_INCLUDED_SRC_VBoxAboutDlg_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPixmap>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QEvent;
+class QLabel;
+class QVBoxLayout;
+
+/** QIDialog extension
+ * used to show the About-VirtualBox dialog. */
+class SHARED_LIBRARY_STUFF VBoxAboutDlg : public QIWithRetranslateUI2<QIDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs dialog passing @a pParent to the base-class.
+ * @param strVersion Brings the version number of VirtualBox. */
+ VBoxAboutDlg(QWidget *pParent, const QString &strVersion);
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares main-layout. */
+ void prepareMainLayout();
+ /** Prepares label. */
+ void prepareLabel();
+ /** Prepares close-button. */
+ void prepareCloseButton();
+
+ /** Holds the pseudo-parent widget reference. */
+ QObject *m_pPseudoParent;
+
+ /** Holds the About-VirtualBox text. */
+ QString m_strAboutText;
+ /** Holds the VirtualBox version number. */
+ QString m_strVersion;
+
+ /** Holds the About-VirtualBox image. */
+ QPixmap m_pixmap;
+ /** Holds the About-VirtualBox dialog size. */
+ QSize m_size;
+
+ /** Holds About-VirtualBox main-layout instance. */
+ QVBoxLayout *m_pMainLayout;
+ /** Holds About-VirtualBox text-label instance. */
+ QLabel *m_pLabel;
+ /** Holds size set flag to make sure dialog size if set only once. */
+ bool m_fFixedSizeSet;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_VBoxAboutDlg_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/VBoxLicenseViewer.cpp b/src/VBox/Frontends/VirtualBox/src/VBoxLicenseViewer.cpp
new file mode 100644
index 00000000..f1df4116
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/VBoxLicenseViewer.cpp
@@ -0,0 +1,186 @@
+/* $Id: VBoxLicenseViewer.cpp $ */
+/** @file
+ * VBox Qt GUI - VBoxLicenseViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QFile>
+#include <QPushButton>
+#include <QScrollBar>
+#include <QTextBrowser>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "VBoxLicenseViewer.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+
+
+VBoxLicenseViewer::VBoxLicenseViewer(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI2<QDialog>(pParent)
+ , m_pLicenseBrowser(0)
+ , m_pButtonAgree(0)
+ , m_pButtonDisagree(0)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/log_viewer_find_32px.png", ":/log_viewer_find_16px.png"));
+#endif
+
+ /* Create main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Create license browser: */
+ m_pLicenseBrowser = new QTextBrowser(this);
+ if (m_pLicenseBrowser)
+ {
+ /* Configure license browser: */
+ m_pLicenseBrowser->verticalScrollBar()->installEventFilter(this);
+ connect(m_pLicenseBrowser->verticalScrollBar(), &QScrollBar::valueChanged,
+ this, &VBoxLicenseViewer::sltHandleScrollBarMoved);
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pLicenseBrowser);
+ }
+
+ /* Create agree button: */
+ /** @todo rework buttons to be a part of button-box itself */
+ QDialogButtonBox *pDialogButtonBox = new QIDialogButtonBox;
+ if (pDialogButtonBox)
+ {
+ /* Create agree button: */
+ m_pButtonAgree = new QPushButton;
+ if (m_pButtonAgree)
+ {
+ /* Configure button: */
+ connect(m_pButtonAgree, &QPushButton::clicked, this, &QDialog::accept);
+
+ /* Add into button-box: */
+ pDialogButtonBox->addButton(m_pButtonAgree, QDialogButtonBox::AcceptRole);
+ }
+
+ /* Create agree button: */
+ m_pButtonDisagree = new QPushButton;
+ if (m_pButtonDisagree)
+ {
+ /* Configure button: */
+ connect(m_pButtonDisagree, &QPushButton::clicked, this, &QDialog::reject);
+
+ /* Add into button-box: */
+ pDialogButtonBox->addButton(m_pButtonDisagree, QDialogButtonBox::RejectRole);
+ }
+ }
+
+ /* Add into layout: */
+ pMainLayout->addWidget(pDialogButtonBox);
+ }
+
+ /* Configure self: */
+ resize(600, 450);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+int VBoxLicenseViewer::showLicenseFromString(const QString &strLicenseText)
+{
+ /* Set license text: */
+ m_pLicenseBrowser->setText(strLicenseText);
+ return exec();
+}
+
+int VBoxLicenseViewer::showLicenseFromFile(const QString &strLicenseFileName)
+{
+ /* Read license file: */
+ QFile file(strLicenseFileName);
+ if (file.open(QIODevice::ReadOnly))
+ return showLicenseFromString(file.readAll());
+ else
+ {
+ UINotificationMessage::cannotOpenLicenseFile(strLicenseFileName);
+ return QDialog::Rejected;
+ }
+}
+
+bool VBoxLicenseViewer::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Handle known event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Hide:
+ if (pObject == m_pLicenseBrowser->verticalScrollBar())
+ /* Doesn't work on wm's like ion3 where the window starts maximized: isActiveWindow() */
+ sltUnlockButtons();
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QDialog::eventFilter(pObject, pEvent);
+}
+
+void VBoxLicenseViewer::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QDialog::showEvent(pEvent);
+
+ /* Enable/disable buttons accordingly: */
+ bool fScrollBarHidden = !m_pLicenseBrowser->verticalScrollBar()->isVisible()
+ && !(windowState() & Qt::WindowMinimized);
+ m_pButtonAgree->setEnabled(fScrollBarHidden);
+ m_pButtonDisagree->setEnabled(fScrollBarHidden);
+}
+
+void VBoxLicenseViewer::retranslateUi()
+{
+ /* Translate dialog title: */
+ setWindowTitle(tr("VirtualBox License"));
+
+ /* Translate buttons: */
+ m_pButtonAgree->setText(tr("I &Agree"));
+ m_pButtonDisagree->setText(tr("I &Disagree"));
+}
+
+int VBoxLicenseViewer::exec()
+{
+ /* Nothing wrong with that, just hiding slot: */
+ return QDialog::exec();
+}
+
+void VBoxLicenseViewer::sltHandleScrollBarMoved(int iValue)
+{
+ if (iValue == m_pLicenseBrowser->verticalScrollBar()->maximum())
+ sltUnlockButtons();
+}
+
+void VBoxLicenseViewer::sltUnlockButtons()
+{
+ m_pButtonAgree->setEnabled(true);
+ m_pButtonDisagree->setEnabled(true);
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/VBoxLicenseViewer.h b/src/VBox/Frontends/VirtualBox/src/VBoxLicenseViewer.h
new file mode 100644
index 00000000..23aa595f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/VBoxLicenseViewer.h
@@ -0,0 +1,94 @@
+/* $Id: VBoxLicenseViewer.h $ */
+/** @file
+ * VBox Qt GUI - VBoxLicenseViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_VBoxLicenseViewer_h
+#define FEQT_INCLUDED_SRC_VBoxLicenseViewer_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDialog>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QTextBrowser;
+class QPushButton;
+
+/** QDialog subclass used to show a user license under linux. */
+class SHARED_LIBRARY_STUFF VBoxLicenseViewer : public QIWithRetranslateUI2<QDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs license viewer passing @a pParent to the base-class. */
+ VBoxLicenseViewer(QWidget *pParent = 0);
+
+ /** Shows license from passed @a strLicenseText. */
+ int showLicenseFromString(const QString &strLicenseText);
+ /** Shows license from file with passed @a strLicenseFileName. */
+ int showLicenseFromFile(const QString &strLicenseFileName);
+
+protected:
+
+ /** Preprocesses Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles Qt show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Executes the dialog. */
+ int exec();
+
+ /** Handles scroll-bar moving by a certain @a iValue. */
+ void sltHandleScrollBarMoved(int iValue);
+
+ /** Uplocks buttons. */
+ void sltUnlockButtons();
+
+private:
+
+ /** Holds the licence text browser instance. */
+ QTextBrowser *m_pLicenseBrowser;
+
+ /** Holds the licence agree button instance. */
+ QPushButton *m_pButtonAgree;
+ /** Holds the licence disagree button instance. */
+ QPushButton *m_pButtonDisagree;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_VBoxLicenseViewer_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/activity/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/UIMonitorCommon.cpp b/src/VBox/Frontends/VirtualBox/src/activity/UIMonitorCommon.cpp
new file mode 100644
index 00000000..6dfdc164
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/UIMonitorCommon.cpp
@@ -0,0 +1,250 @@
+/* $Id: UIMonitorCommon.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMonitorCommon class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPainter>
+#include <QPainterPath>
+#include <QPainterPathStroker>
+#include <QXmlStreamReader>
+
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIMonitorCommon.h"
+
+/* COM includes: */
+#include "CMachineDebugger.h"
+#include "CPerformanceCollector.h"
+
+/* static */
+void UIMonitorCommon::getNetworkLoad(CMachineDebugger &debugger, quint64 &uOutNetworkReceived, quint64 &uOutNetworkTransmitted)
+{
+ uOutNetworkReceived = 0;
+ uOutNetworkTransmitted = 0;
+ QVector<UIDebuggerMetricData> xmlData = getAndParseStatsFromDebugger(debugger, "/Public/NetAdapter/*/Bytes*");
+ foreach (const UIDebuggerMetricData &data, xmlData)
+ {
+ if (data.m_strName.endsWith("BytesReceived"))
+ uOutNetworkReceived += data.m_counter;
+ else if (data.m_strName.endsWith("BytesTransmitted"))
+ uOutNetworkTransmitted += data.m_counter;
+ else
+ AssertMsgFailed(("name=%s\n", data.m_strName.toLocal8Bit().data()));
+ }
+}
+
+/* static */
+void UIMonitorCommon::getDiskLoad(CMachineDebugger &debugger, quint64 &uOutDiskWritten, quint64 &uOutDiskRead)
+{
+ uOutDiskWritten = 0;
+ uOutDiskRead = 0;
+ QVector<UIDebuggerMetricData> xmlData = getAndParseStatsFromDebugger(debugger, "/Public/Storage/*/Port*/Bytes*");
+ foreach (const UIDebuggerMetricData &data, xmlData)
+ {
+ if (data.m_strName.endsWith("BytesWritten"))
+ uOutDiskWritten += data.m_counter;
+ else if (data.m_strName.endsWith("BytesRead"))
+ uOutDiskRead += data.m_counter;
+ else
+ AssertMsgFailed(("name=%s\n", data.m_strName.toLocal8Bit().data()));
+ }
+}
+
+/* static */
+void UIMonitorCommon::getVMMExitCount(CMachineDebugger &debugger, quint64 &uOutVMMExitCount)
+{
+ uOutVMMExitCount = 0;
+ QVector<UIDebuggerMetricData> xmlData = getAndParseStatsFromDebugger(debugger, "/PROF/CPU*/EM/RecordedExits");
+ foreach (const UIDebuggerMetricData &data, xmlData)
+ {
+ if (data.m_strName.endsWith("RecordedExits"))
+ uOutVMMExitCount += data.m_counter;
+ else
+ AssertMsgFailed(("name=%s\n", data.m_strName.toLocal8Bit().data()));
+ }
+}
+
+
+/* static */
+QVector<UIDebuggerMetricData> UIMonitorCommon::getAndParseStatsFromDebugger(CMachineDebugger &debugger, const QString &strQuery)
+{
+ QVector<UIDebuggerMetricData> xmlData;
+ if (strQuery.isEmpty())
+ return xmlData;
+ QString strStats = debugger.GetStats(strQuery, false);
+ QXmlStreamReader xmlReader;
+ xmlReader.addData(strStats);
+ if (xmlReader.readNextStartElement())
+ {
+ while (xmlReader.readNextStartElement())
+ {
+ if (xmlReader.name() == QLatin1String("Counter"))
+ {
+ QXmlStreamAttributes attributes = xmlReader.attributes();
+ quint64 iCounter = attributes.value("c").toULongLong();
+ xmlData.push_back(UIDebuggerMetricData(attributes.value("name").toString(), iCounter));
+ }
+ else if (xmlReader.name() == QLatin1String("U64"))
+ {
+ QXmlStreamAttributes attributes = xmlReader.attributes();
+ quint64 iCounter = attributes.value("val").toULongLong();
+ xmlData.push_back(UIDebuggerMetricData(attributes.value("name").toString(), iCounter));
+ }
+ xmlReader.skipCurrentElement();
+ }
+ }
+ return xmlData;
+}
+
+/* static */
+void UIMonitorCommon::getRAMLoad(CPerformanceCollector &comPerformanceCollector, QVector<QString> &nameList,
+ QVector<CUnknown>& objectList, quint64 &iOutTotalRAM, quint64 &iOutFreeRAM)
+{
+ iOutTotalRAM = 0;
+ iOutFreeRAM = 0;
+ QVector<QString> aReturnNames;
+ QVector<CUnknown> aReturnObjects;
+ QVector<QString> aReturnUnits;
+ QVector<ULONG> aReturnScales;
+ QVector<ULONG> aReturnSequenceNumbers;
+ QVector<ULONG> aReturnDataIndices;
+ QVector<ULONG> aReturnDataLengths;
+ /* Make a query to CPerformanceCollector to fetch some metrics (e.g RAM usage): */
+ QVector<LONG> returnData = comPerformanceCollector.QueryMetricsData(nameList,
+ objectList,
+ aReturnNames,
+ aReturnObjects,
+ aReturnUnits,
+ aReturnScales,
+ aReturnSequenceNumbers,
+ aReturnDataIndices,
+ aReturnDataLengths);
+ /* Parse the result we get from CPerformanceCollector to get respective values: */
+ for (int i = 0; i < aReturnNames.size(); ++i)
+ {
+ if (aReturnDataLengths[i] == 0)
+ continue;
+ /* Read the last of the return data disregarding the rest since we are caching the data in GUI side: */
+ float fData = returnData[aReturnDataIndices[i] + aReturnDataLengths[i] - 1] / (float)aReturnScales[i];
+ if (aReturnNames[i].contains("RAM", Qt::CaseInsensitive) && !aReturnNames[i].contains(":"))
+ {
+ if (aReturnNames[i].contains("Total", Qt::CaseInsensitive))
+ iOutTotalRAM = (quint64)fData;
+ if (aReturnNames[i].contains("Free", Qt::CaseInsensitive))
+ iOutFreeRAM = (quint64)fData;
+ }
+ }
+}
+
+/* static */
+QPainterPath UIMonitorCommon::doughnutSlice(const QRectF &outerRectangle, const QRectF &innerRectangle, float fStartAngle, float fSweepAngle)
+{
+ QPainterPath subPath1;
+ subPath1.moveTo(outerRectangle.center());
+ subPath1.arcTo(outerRectangle, fStartAngle,
+ -1.f * fSweepAngle);
+ subPath1.closeSubpath();
+
+ QPainterPath subPath2;
+ subPath2.moveTo(innerRectangle.center());
+ subPath2.arcTo(innerRectangle, fStartAngle,
+ -1.f * fSweepAngle);
+ subPath2.closeSubpath();
+
+ return subPath1.subtracted(subPath2);
+}
+
+/* static */
+QPainterPath UIMonitorCommon::wholeArc(const QRectF &rectangle)
+{
+ QPainterPath arc;
+ arc.addEllipse(rectangle);
+ return arc;
+}
+
+/* static */
+void UIMonitorCommon::drawCombinedDoughnutChart(quint64 data1, const QColor &data1Color,
+ quint64 data2, const QColor &data2Color,
+ QPainter &painter, quint64 iMaximum,
+ const QRectF &chartRect, const QRectF &innerRect, int iOverlayAlpha)
+{
+ (void)data2;
+ (void)data2Color;
+ (void)iOverlayAlpha;
+ /* Draw two arcs. one for the inner the other for the outer circle: */
+ painter.setPen(QPen(QColor(100, 100, 100, iOverlayAlpha), 1));
+ painter.drawArc(chartRect, 0, 3600 * 16);
+ painter.drawArc(innerRect, 0, 3600 * 16);
+
+ /* Draw a translucent white background: */
+ QPainterPath background = wholeArc(chartRect).subtracted(wholeArc(innerRect));
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(QColor(255, 255, 255, iOverlayAlpha));
+ painter.drawPath(background);
+
+ /* Draw a doughnut slice for the first data series: */
+ float fAngle = 360.f * data1 / (float)iMaximum;
+ painter.setBrush(data1Color);
+ painter.drawPath(doughnutSlice(chartRect, innerRect, 90, fAngle));
+
+ float fAngle2 = 360.f * data2 / (float)iMaximum;
+ painter.setBrush(data2Color);
+ painter.drawPath(doughnutSlice(chartRect, innerRect, 90 - fAngle, fAngle2));
+}
+
+/* static */
+QRectF UIMonitorCommon::getScaledRect(const QRectF &outerFrame, float fScaleX, float fScaleY)
+{
+ if (!outerFrame.isValid())
+ return QRectF();
+ QPointF center = outerFrame.center();
+ float iWidth = fScaleX * outerFrame.width();
+ float iHeight = fScaleY * outerFrame.height();
+ return QRectF(QPointF(center.x() - 0.5 * iWidth, center.y() - 0.5 * iHeight),
+ QSizeF(iWidth, iHeight));
+}
+
+/* static */
+void UIMonitorCommon::drawDoughnutChart(QPainter &painter, quint64 iMaximum, quint64 data,
+ const QRectF &chartRect, const QRectF &innerRect, int iOverlayAlpha, const QColor &color)
+{
+ /* Draw a whole non-filled circle: */
+ painter.setPen(QPen(QColor(100, 100, 100, iOverlayAlpha), 1));
+ painter.drawArc(chartRect, 0, 3600 * 16);
+ painter.drawArc(innerRect, 0, 3600 * 16);
+
+ /* Draw a white filled circle and the arc for data: */
+ QPainterPath background = UIMonitorCommon::wholeArc(chartRect).subtracted(UIMonitorCommon::wholeArc(innerRect));
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(QColor(255, 255, 255, iOverlayAlpha));
+ painter.drawPath(background);
+
+ /* Draw the doughnut slice for the data: */
+ float fAngle = 360.f * data / (float)iMaximum;
+ painter.setBrush(color);
+ painter.drawPath(UIMonitorCommon::doughnutSlice(chartRect, innerRect, 90, fAngle));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/UIMonitorCommon.h b/src/VBox/Frontends/VirtualBox/src/activity/UIMonitorCommon.h
new file mode 100644
index 00000000..ab1c2d42
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/UIMonitorCommon.h
@@ -0,0 +1,82 @@
+/* $Id: UIMonitorCommon.h $ */
+/** @file
+ * VBox Qt GUI - UIMonitorCommon class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_activity_UIMonitorCommon_h
+#define FEQT_INCLUDED_SRC_activity_UIMonitorCommon_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/** UIDebuggerMetricData is used as data storage while parsing the xml stream received from IMachineDebugger. */
+struct UIDebuggerMetricData
+{
+ UIDebuggerMetricData()
+ : m_counter(0){}
+ UIDebuggerMetricData(const QString &strName, quint64 counter)
+ : m_strName(strName)
+ , m_counter(counter){}
+ QString m_strName;
+ quint64 m_counter;
+};
+
+
+class SHARED_LIBRARY_STUFF UIMonitorCommon
+{
+
+public:
+
+ /** @name Static utility methods that query and parse IMachineDebugger outputs for specific metrix types.
+ * @{ */
+ static void getNetworkLoad(CMachineDebugger &debugger, quint64 &uOutNetworkReceived, quint64 &uOutNetworkTransmitted);
+ static void getDiskLoad(CMachineDebugger &debugger, quint64 &uOutDiskWritten, quint64 &uOutDiskRead);
+ static void getVMMExitCount(CMachineDebugger &debugger, quint64 &uOutVMMExitCount);
+ /** @} */
+ static void getRAMLoad(CPerformanceCollector &comPerformanceCollector, QVector<QString> &nameList,
+ QVector<CUnknown>& objectList, quint64 &iOutTotalRAM, quint64 &iOutFreeRAM);
+
+
+ static QPainterPath doughnutSlice(const QRectF &outerRectangle, const QRectF &innerRectangle, float fStartAngle, float fSweepAngle);
+ static QPainterPath wholeArc(const QRectF &rectangle);
+ static void drawCombinedDoughnutChart(quint64 data1, const QColor &data1Color,
+ quint64 data2, const QColor &data2Color,
+ QPainter &painter, quint64 iMaximum,
+ const QRectF &chartRect, const QRectF &innerRect, int iOverlayAlpha);
+
+ /* Returns a rectangle which is co-centric with @p outerFrame and scaled by @p fScaleX and fScaleY. */
+ static QRectF getScaledRect(const QRectF &outerFrame, float fScaleX, float fScaleY);
+
+ static void drawDoughnutChart(QPainter &painter, quint64 iMaximum, quint64 data,
+ const QRectF &chartRect, const QRectF &innerRect, int iOverlayAlpha, const QColor &color);
+
+private:
+
+ /** Parses the xml string we get from the IMachineDebugger and returns an array of UIDebuggerMetricData. */
+ static QVector<UIDebuggerMetricData> getAndParseStatsFromDebugger(CMachineDebugger &debugger, const QString &strQuery);
+
+};
+
+#endif /* !FEQT_INCLUDED_SRC_activity_UIMonitorCommon_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/overview/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/activity/overview/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/overview/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityOverviewWidget.cpp b/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityOverviewWidget.cpp
new file mode 100644
index 00000000..67400cc8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityOverviewWidget.cpp
@@ -0,0 +1,1747 @@
+/* $Id: UIVMActivityOverviewWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMActivityOverviewWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAbstractTableModel>
+#include <QCheckBox>
+#include <QHeaderView>
+#include <QItemDelegate>
+#include <QLabel>
+#include <QMenuBar>
+#include <QPainter>
+#include <QPushButton>
+#include <QTableView>
+#include <QTimer>
+#include <QVBoxLayout>
+#include <QSortFilterProxyModel>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIToolBar.h"
+#include "UIActionPoolManager.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIExtraDataDefs.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UITranslator.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVMActivityMonitor.h"
+#include "UIVMActivityOverviewWidget.h"
+
+#ifdef VBOX_WS_MAC
+# include "UIWindowMenuManager.h"
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CMachine.h"
+#include "CMachineDebugger.h"
+#include "CPerformanceMetric.h"
+
+/* Other VBox includes: */
+#include <iprt/cidr.h>
+
+struct ResourceColumn
+{
+ QString m_strName;
+ bool m_fEnabled;
+};
+
+/** Draws a doughnut shaped chart for the passed data values and can have a text drawn in the center. */
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewDoughnutChart definition. *
+*********************************************************************************************************************************/
+
+class UIVMActivityOverviewDoughnutChart : public QWidget
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIVMActivityOverviewDoughnutChart(QWidget *pParent = 0);
+ void updateData(quint64 iData0, quint64 iData1);
+ void setChartColors(const QColor &color0, const QColor &color1);
+ void setChartCenterString(const QString &strCenter);
+ void setDataMaximum(quint64 iMax);
+
+protected:
+
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ quint64 m_iData0;
+ quint64 m_iData1;
+ quint64 m_iDataMaximum;
+ int m_iMargin;
+ QColor m_color0;
+ QColor m_color1;
+ /** If not empty this text is drawn at the center of the doughnut chart. */
+ QString m_strCenter;
+};
+
+/** A simple container to store host related performance values. */
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewHostStats definition. *
+*********************************************************************************************************************************/
+
+class UIVMActivityOverviewHostStats
+{
+
+public:
+
+ UIVMActivityOverviewHostStats();
+ quint64 m_iCPUUserLoad;
+ quint64 m_iCPUKernelLoad;
+ quint64 m_iCPUFreq;
+ quint64 m_iRAMTotal;
+ quint64 m_iRAMFree;
+ quint64 m_iFSTotal;
+ quint64 m_iFSFree;
+};
+
+/** A container QWidget to layout host stats. related widgets. */
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewHostStatsWidget definition. *
+*********************************************************************************************************************************/
+
+class UIVMActivityOverviewHostStatsWidget : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIVMActivityOverviewHostStatsWidget(QWidget *pParent = 0);
+ void setHostStats(const UIVMActivityOverviewHostStats &hostStats);
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ void prepare();
+ void addVerticalLine(QHBoxLayout *pLayout);
+ void updateLabels();
+
+ UIVMActivityOverviewDoughnutChart *m_pHostCPUChart;
+ UIVMActivityOverviewDoughnutChart *m_pHostRAMChart;
+ UIVMActivityOverviewDoughnutChart *m_pHostFSChart;
+ QLabel *m_pCPUTitleLabel;
+ QLabel *m_pCPUUserLabel;
+ QLabel *m_pCPUKernelLabel;
+ QLabel *m_pCPUTotalLabel;
+ QLabel *m_pRAMTitleLabel;
+ QLabel *m_pRAMUsedLabel;
+ QLabel *m_pRAMFreeLabel;
+ QLabel *m_pRAMTotalLabel;
+ QLabel *m_pFSTitleLabel;
+ QLabel *m_pFSUsedLabel;
+ QLabel *m_pFSFreeLabel;
+ QLabel *m_pFSTotalLabel;
+ QColor m_CPUUserColor;
+ QColor m_CPUKernelColor;
+ QColor m_RAMFreeColor;
+ QColor m_RAMUsedColor;
+ UIVMActivityOverviewHostStats m_hostStats;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewTableView definition. *
+*********************************************************************************************************************************/
+/** A QTableView extension so manage the column width a bit better than what Qt offers out of box. */
+class UIVMActivityOverviewTableView : public QTableView
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+
+public:
+
+ UIVMActivityOverviewTableView(QWidget *pParent = 0);
+ void setMinimumColumnWidths(const QMap<int, int>& widths);
+ void updateColumVisibility();
+ int selectedItemIndex() const;
+ bool hasSelection() const;
+
+protected:
+
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+ virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) RT_OVERRIDE;
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+
+
+private:
+
+ /** Resizes all the columns in response to resizeEvent. Columns cannot be narrower than m_minimumColumnWidths values. */
+ void resizeHeaders();
+ /** Value is in pixels. Columns cannot be narrower than this width. */
+ QMap<int, int> m_minimumColumnWidths;
+};
+
+/** Each instance of UIVMActivityOverviewItem corresponds to a running vm whose stats are displayed.
+ * they are owned my the model. */
+/*********************************************************************************************************************************
+ * Class UIVMActivityOverviewItem definition. *
+ *********************************************************************************************************************************/
+class UIActivityOverviewItem
+{
+
+public:
+
+ UIActivityOverviewItem(const QUuid &uid, const QString &strVMName, KMachineState enmState);
+ //yUIActivityOverviewItem(const QUuid &uid);
+ UIActivityOverviewItem();
+ ~UIActivityOverviewItem();
+ bool operator==(const UIActivityOverviewItem& other) const;
+ bool isWithGuestAdditions();
+ void resetDebugger();
+
+ QUuid m_VMuid;
+ QString m_strVMName;
+ KMachineState m_enmMachineState;
+
+ quint64 m_uCPUGuestLoad;
+ quint64 m_uCPUVMMLoad;
+
+ quint64 m_uTotalRAM;
+ quint64 m_uFreeRAM;
+ quint64 m_uUsedRAM;
+ float m_fRAMUsagePercentage;
+
+ quint64 m_uNetworkDownRate;
+ quint64 m_uNetworkUpRate;
+ quint64 m_uNetworkDownTotal;
+ quint64 m_uNetworkUpTotal;
+
+ quint64 m_uDiskWriteRate;
+ quint64 m_uDiskReadRate;
+ quint64 m_uDiskWriteTotal;
+ quint64 m_uDiskReadTotal;
+
+ quint64 m_uVMExitRate;
+ quint64 m_uVMExitTotal;
+
+ CSession m_comSession;
+ CMachineDebugger m_comDebugger;
+ CGuest m_comGuest;
+ /** The strings of each column for the item. We update this during performance query
+ * instead of model's data function to know the string length earlier. */
+ QMap<int, QString> m_columnData;
+
+private:
+
+ void setupPerformanceCollector();
+};
+
+Q_DECLARE_METATYPE(UIActivityOverviewItem);
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewProxyModel definition. *
+*********************************************************************************************************************************/
+class UIActivityOverviewProxyModel : public QSortFilterProxyModel
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIActivityOverviewProxyModel(QObject *parent = 0);
+ void dataUpdate();
+ void setNotRunningVMVisibility(bool fShow);
+
+protected:
+
+ virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const /* override*/;
+ bool filterAcceptsRow(int iSourceRow, const QModelIndex &sourceParent) const /* override*/;
+
+private:
+
+ bool m_fShowNotRunningVMs;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIActivityOverviewModel definition. *
+*********************************************************************************************************************************/
+class UIActivityOverviewModel : public QAbstractTableModel
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigDataUpdate();
+ void sigHostStatsUpdate(const UIVMActivityOverviewHostStats &stats);
+
+public:
+
+ UIActivityOverviewModel(QObject *parent = 0);
+ int rowCount(const QModelIndex &parent = QModelIndex()) const RT_OVERRIDE;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const RT_OVERRIDE;
+ QVariant data(const QModelIndex &index, int role) const RT_OVERRIDE;
+ void clearData();
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ void setColumnCaptions(const QMap<int, QString>& captions);
+ void setColumnVisible(const QMap<int, bool>& columnVisible);
+ bool columnVisible(int iColumnId) const;
+ void setShouldUpdate(bool fShouldUpdate);
+ const QMap<int, int> dataLengths() const;
+ QUuid itemUid(int iIndex);
+ int itemIndex(const QUuid &uid);
+ /* Return the state of the machine represented by the item at @rowIndex. */
+ KMachineState machineState(int rowIndex) const;
+ void setDefaultViewFont(const QFont &font);
+ void setDefaultViewFontColor(const QColor &color);
+
+private slots:
+
+ void sltMachineStateChanged(const QUuid &uId, const KMachineState state);
+ void sltMachineRegistered(const QUuid &uId, bool fRegistered);
+ void sltTimeout();
+
+private:
+
+ void initialize();
+ void initializeItems();
+ void setupPerformanceCollector();
+ void queryPerformanceCollector();
+ void addItem(const QUuid& uMachineId, const QString& strMachineName, KMachineState enmState);
+ void removeItem(const QUuid& uMachineId);
+ void getHostRAMStats();
+
+ QVector<UIActivityOverviewItem> m_itemList;
+ QMap<int, QString> m_columnTitles;
+ QTimer *m_pTimer;
+ /** @name The following are used during UIPerformanceCollector::QueryMetricsData(..)
+ * @{ */
+ QVector<QString> m_nameList;
+ QVector<CUnknown> m_objectList;
+ /** @} */
+ CPerformanceCollector m_performanceCollector;
+ QMap<int, bool> m_columnVisible;
+ /** If true the table data and corresponding view is updated. Possibly set by host widget to true only
+ * when the widget is visible in the main UI. */
+ bool m_fShouldUpdate;
+ UIVMActivityOverviewHostStats m_hostStats;
+ QFont m_defaultViewFont;
+ QColor m_defaultViewFontColor;
+ /** Maximum length of string length of data displayed in column. Updated in UIActivityOverviewModel::data(..). */
+ mutable QMap<int, int> m_columnDataMaxLength;
+};
+
+
+/*********************************************************************************************************************************
+* UIVMActivityOverviewDelegate definition. *
+*********************************************************************************************************************************/
+/** A QItemDelegate child class to disable dashed lines drawn around selected cells in QTableViews */
+class UIVMActivityOverviewDelegate : public QItemDelegate
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIVMActivityOverviewDelegate(QObject *pParent = 0)
+ : QItemDelegate(pParent){}
+
+protected:
+
+ virtual void drawFocus ( QPainter * /*painter*/, const QStyleOptionViewItem & /*option*/, const QRect & /*rect*/ ) const {}
+};
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewDoughnutChart implementation. *
+*********************************************************************************************************************************/
+UIVMActivityOverviewDoughnutChart::UIVMActivityOverviewDoughnutChart(QWidget *pParent /* = 0 */)
+ :QWidget(pParent)
+ , m_iData0(0)
+ , m_iData1(0)
+ , m_iDataMaximum(0)
+ , m_iMargin(3)
+{
+}
+
+void UIVMActivityOverviewDoughnutChart::updateData(quint64 iData0, quint64 iData1)
+{
+ m_iData0 = iData0;
+ m_iData1 = iData1;
+ update();
+}
+
+void UIVMActivityOverviewDoughnutChart::setChartColors(const QColor &color0, const QColor &color1)
+{
+ m_color0 = color0;
+ m_color1 = color1;
+}
+
+void UIVMActivityOverviewDoughnutChart::setChartCenterString(const QString &strCenter)
+{
+ m_strCenter = strCenter;
+}
+
+void UIVMActivityOverviewDoughnutChart::setDataMaximum(quint64 iMax)
+{
+ m_iDataMaximum = iMax;
+}
+
+void UIVMActivityOverviewDoughnutChart::paintEvent(QPaintEvent *pEvent)
+{
+ QWidget::paintEvent(pEvent);
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ int iFrameHeight = height()- 2 * m_iMargin;
+ QRectF outerRect = QRectF(QPoint(m_iMargin,m_iMargin), QSize(iFrameHeight, iFrameHeight));
+ QRectF innerRect = UIMonitorCommon::getScaledRect(outerRect, 0.6f, 0.6f);
+ UIMonitorCommon::drawCombinedDoughnutChart(m_iData0, m_color0,
+ m_iData1, m_color1,
+ painter, m_iDataMaximum,
+ outerRect, innerRect, 80);
+ if (!m_strCenter.isEmpty())
+ {
+ float mul = 1.f / 1.4f;
+ QRectF textRect = UIMonitorCommon::getScaledRect(innerRect, mul, mul);
+ painter.setPen(Qt::black);
+ painter.drawText(textRect, Qt::AlignCenter, m_strCenter);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewHostStatsWidget implementation. *
+*********************************************************************************************************************************/
+
+UIVMActivityOverviewHostStatsWidget::UIVMActivityOverviewHostStatsWidget(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pHostCPUChart(0)
+ , m_pHostRAMChart(0)
+ , m_pHostFSChart(0)
+ , m_pCPUTitleLabel(0)
+ , m_pCPUUserLabel(0)
+ , m_pCPUKernelLabel(0)
+ , m_pCPUTotalLabel(0)
+ , m_pRAMTitleLabel(0)
+ , m_pRAMUsedLabel(0)
+ , m_pRAMFreeLabel(0)
+ , m_pRAMTotalLabel(0)
+ , m_pFSTitleLabel(0)
+ , m_pFSUsedLabel(0)
+ , m_pFSFreeLabel(0)
+ , m_pFSTotalLabel(0)
+ , m_CPUUserColor(Qt::red)
+ , m_CPUKernelColor(Qt::blue)
+ , m_RAMFreeColor(Qt::blue)
+ , m_RAMUsedColor(Qt::red)
+{
+ prepare();
+ retranslateUi();
+}
+
+void UIVMActivityOverviewHostStatsWidget::setHostStats(const UIVMActivityOverviewHostStats &hostStats)
+{
+ m_hostStats = hostStats;
+ if (m_pHostCPUChart)
+ {
+ m_pHostCPUChart->updateData(m_hostStats.m_iCPUUserLoad, m_hostStats.m_iCPUKernelLoad);
+ QString strCenter = QString("%1\nMHz").arg(m_hostStats.m_iCPUFreq);
+ m_pHostCPUChart->setChartCenterString(strCenter);
+ }
+ if (m_pHostRAMChart)
+ {
+ quint64 iUsedRAM = m_hostStats.m_iRAMTotal - m_hostStats.m_iRAMFree;
+ m_pHostRAMChart->updateData(iUsedRAM, m_hostStats.m_iRAMFree);
+ m_pHostRAMChart->setDataMaximum(m_hostStats.m_iRAMTotal);
+ if (m_hostStats.m_iRAMTotal != 0)
+ {
+ quint64 iUsedRamPer = 100 * (iUsedRAM / (float) m_hostStats.m_iRAMTotal);
+ QString strCenter = QString("%1%\n%2").arg(iUsedRamPer).arg(UIVMActivityOverviewWidget::tr("Used"));
+ m_pHostRAMChart->setChartCenterString(strCenter);
+ }
+ }
+ if (m_pHostFSChart)
+ {
+ quint64 iUsedFS = m_hostStats.m_iFSTotal - m_hostStats.m_iFSFree;
+ m_pHostFSChart->updateData(iUsedFS, m_hostStats.m_iFSFree);
+ m_pHostFSChart->setDataMaximum(m_hostStats.m_iFSTotal);
+ if (m_hostStats.m_iFSTotal != 0)
+ {
+ quint64 iUsedFSPer = 100 * (iUsedFS / (float) m_hostStats.m_iFSTotal);
+ QString strCenter = QString("%1%\n%2").arg(iUsedFSPer).arg(UIVMActivityOverviewWidget::tr("Used"));
+ m_pHostFSChart->setChartCenterString(strCenter);
+ }
+ }
+
+ updateLabels();
+}
+
+void UIVMActivityOverviewHostStatsWidget::retranslateUi()
+{
+ updateLabels();
+}
+
+void UIVMActivityOverviewHostStatsWidget::addVerticalLine(QHBoxLayout *pLayout)
+{
+ QFrame *pLine = new QFrame;
+ pLine->setFrameShape(QFrame::VLine);
+ pLine->setFrameShadow(QFrame::Sunken);
+ pLayout->addWidget(pLine);
+}
+
+void UIVMActivityOverviewHostStatsWidget::prepare()
+{
+ QHBoxLayout *pLayout = new QHBoxLayout;
+ setLayout(pLayout);
+ int iMinimumSize = 3 * QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
+
+ /* CPU Stuff: */
+ {
+ /* Host CPU Labels: */
+ QWidget *pCPULabelContainer = new QWidget;
+ pCPULabelContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
+ pLayout->addWidget(pCPULabelContainer);
+ QVBoxLayout *pCPULabelsLayout = new QVBoxLayout;
+ pCPULabelsLayout->setContentsMargins(0, 0, 0, 0);
+ pCPULabelContainer->setLayout(pCPULabelsLayout);
+ m_pCPUTitleLabel = new QLabel;
+ pCPULabelsLayout->addWidget(m_pCPUTitleLabel);
+ m_pCPUUserLabel = new QLabel;
+ pCPULabelsLayout->addWidget(m_pCPUUserLabel);
+ m_pCPUKernelLabel = new QLabel;
+ pCPULabelsLayout->addWidget(m_pCPUKernelLabel);
+ m_pCPUTotalLabel = new QLabel;
+ pCPULabelsLayout->addWidget(m_pCPUTotalLabel);
+ pCPULabelsLayout->setAlignment(Qt::AlignTop);
+ pCPULabelsLayout->setSpacing(0);
+ /* Host CPU chart widget: */
+ m_pHostCPUChart = new UIVMActivityOverviewDoughnutChart;
+ if (m_pHostCPUChart)
+ {
+ m_pHostCPUChart->setMinimumSize(iMinimumSize, iMinimumSize);
+ m_pHostCPUChart->setDataMaximum(100);
+ pLayout->addWidget(m_pHostCPUChart);
+ m_pHostCPUChart->setChartColors(m_CPUUserColor, m_CPUKernelColor);
+ }
+ }
+ addVerticalLine(pLayout);
+ /* RAM Stuff: */
+ {
+ QWidget *pRAMLabelContainer = new QWidget;
+ pRAMLabelContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
+
+ pLayout->addWidget(pRAMLabelContainer);
+ QVBoxLayout *pRAMLabelsLayout = new QVBoxLayout;
+ pRAMLabelsLayout->setContentsMargins(0, 0, 0, 0);
+ pRAMLabelsLayout->setSpacing(0);
+ pRAMLabelContainer->setLayout(pRAMLabelsLayout);
+ m_pRAMTitleLabel = new QLabel;
+ pRAMLabelsLayout->addWidget(m_pRAMTitleLabel);
+ m_pRAMUsedLabel = new QLabel;
+ pRAMLabelsLayout->addWidget(m_pRAMUsedLabel);
+ m_pRAMFreeLabel = new QLabel;
+ pRAMLabelsLayout->addWidget(m_pRAMFreeLabel);
+ m_pRAMTotalLabel = new QLabel;
+ pRAMLabelsLayout->addWidget(m_pRAMTotalLabel);
+
+ m_pHostRAMChart = new UIVMActivityOverviewDoughnutChart;
+ if (m_pHostRAMChart)
+ {
+ m_pHostRAMChart->setMinimumSize(iMinimumSize, iMinimumSize);
+ pLayout->addWidget(m_pHostRAMChart);
+ m_pHostRAMChart->setChartColors(m_RAMUsedColor, m_RAMFreeColor);
+ }
+ }
+ addVerticalLine(pLayout);
+ /* FS Stuff: */
+ {
+ QWidget *pFSLabelContainer = new QWidget;
+ pLayout->addWidget(pFSLabelContainer);
+ pFSLabelContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
+ QVBoxLayout *pFSLabelsLayout = new QVBoxLayout;
+ pFSLabelsLayout->setContentsMargins(0, 0, 0, 0);
+ pFSLabelsLayout->setSpacing(0);
+ pFSLabelContainer->setLayout(pFSLabelsLayout);
+ m_pFSTitleLabel = new QLabel;
+ pFSLabelsLayout->addWidget(m_pFSTitleLabel);
+ m_pFSUsedLabel = new QLabel;
+ pFSLabelsLayout->addWidget(m_pFSUsedLabel);
+ m_pFSFreeLabel = new QLabel;
+ pFSLabelsLayout->addWidget(m_pFSFreeLabel);
+ m_pFSTotalLabel = new QLabel;
+ pFSLabelsLayout->addWidget(m_pFSTotalLabel);
+
+ m_pHostFSChart = new UIVMActivityOverviewDoughnutChart;
+ if (m_pHostFSChart)
+ {
+ m_pHostFSChart->setMinimumSize(iMinimumSize, iMinimumSize);
+ pLayout->addWidget(m_pHostFSChart);
+ m_pHostFSChart->setChartColors(m_RAMUsedColor, m_RAMFreeColor);
+ }
+
+ }
+ pLayout->addStretch(2);
+}
+
+void UIVMActivityOverviewHostStatsWidget::updateLabels()
+{
+ if (m_pCPUTitleLabel)
+ m_pCPUTitleLabel->setText(QString("<b>%1</b>").arg(UIVMActivityOverviewWidget::tr("Host CPU Load")));
+ if (m_pCPUUserLabel)
+ {
+ QString strColor = QColor(m_CPUUserColor).name(QColor::HexRgb);
+ m_pCPUUserLabel->setText(QString("<font color=\"%1\">%2: %3%</font>").arg(strColor).arg(UIVMActivityOverviewWidget::tr("User")).arg(QString::number(m_hostStats.m_iCPUUserLoad)));
+ }
+ if (m_pCPUKernelLabel)
+ {
+ QString strColor = QColor(m_CPUKernelColor).name(QColor::HexRgb);
+ m_pCPUKernelLabel->setText(QString("<font color=\"%1\">%2: %3%</font>").arg(strColor).arg(UIVMActivityOverviewWidget::tr("Kernel")).arg(QString::number(m_hostStats.m_iCPUKernelLoad)));
+ }
+ if (m_pCPUTotalLabel)
+ m_pCPUTotalLabel->setText(QString("%1: %2%").arg(UIVMActivityOverviewWidget::tr("Total")).arg(m_hostStats.m_iCPUUserLoad + m_hostStats.m_iCPUKernelLoad));
+ if (m_pRAMTitleLabel)
+ m_pRAMTitleLabel->setText(QString("<b>%1</b>").arg(UIVMActivityOverviewWidget::tr("Host RAM Usage")));
+ if (m_pRAMFreeLabel)
+ {
+ QString strRAM = UITranslator::formatSize(m_hostStats.m_iRAMFree);
+ QString strColor = QColor(m_RAMFreeColor).name(QColor::HexRgb);
+ m_pRAMFreeLabel->setText(QString("<font color=\"%1\">%2: %3</font>").arg(strColor).arg(UIVMActivityOverviewWidget::tr("Free")).arg(strRAM));
+ }
+ if (m_pRAMUsedLabel)
+ {
+ QString strRAM = UITranslator::formatSize(m_hostStats.m_iRAMTotal - m_hostStats.m_iRAMFree);
+ QString strColor = QColor(m_RAMUsedColor).name(QColor::HexRgb);
+ m_pRAMUsedLabel->setText(QString("<font color=\"%1\">%2: %3</font>").arg(strColor).arg(UIVMActivityOverviewWidget::tr("Used")).arg(strRAM));
+ }
+ if (m_pRAMTotalLabel)
+ {
+ QString strRAM = UITranslator::formatSize(m_hostStats.m_iRAMTotal);
+ m_pRAMTotalLabel->setText(QString("%1: %2").arg(UIVMActivityOverviewWidget::tr("Total")).arg(strRAM));
+ }
+ if (m_pFSTitleLabel)
+ m_pFSTitleLabel->setText(QString("<b>%1</b>").arg(UIVMActivityOverviewWidget::tr("Host File System")));
+ if (m_pFSFreeLabel)
+ {
+ QString strFS = UITranslator::formatSize(m_hostStats.m_iFSFree);
+ QString strColor = QColor(m_RAMFreeColor).name(QColor::HexRgb);
+ m_pFSFreeLabel->setText(QString("<font color=\"%1\">%2: %3</font>").arg(strColor).arg(UIVMActivityOverviewWidget::tr("Free")).arg(strFS));
+ }
+ if (m_pFSUsedLabel)
+ {
+ QString strFS = UITranslator::formatSize(m_hostStats.m_iFSTotal - m_hostStats.m_iFSFree);
+ QString strColor = QColor(m_RAMUsedColor).name(QColor::HexRgb);
+ m_pFSUsedLabel->setText(QString("<font color=\"%1\">%2: %3</font>").arg(strColor).arg(UIVMActivityOverviewWidget::tr("Used")).arg(strFS));
+ }
+ if (m_pFSTotalLabel)
+ {
+ QString strFS = UITranslator::formatSize(m_hostStats.m_iFSTotal);
+ m_pFSTotalLabel->setText(QString("%1: %2").arg(UIVMActivityOverviewWidget::tr("Total")).arg(strFS));
+ }
+}
+
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewTableView implementation. *
+*********************************************************************************************************************************/
+
+UIVMActivityOverviewTableView::UIVMActivityOverviewTableView(QWidget *pParent /* = 0 */)
+ :QTableView(pParent)
+{
+}
+
+void UIVMActivityOverviewTableView::setMinimumColumnWidths(const QMap<int, int>& widths)
+{
+ m_minimumColumnWidths = widths;
+ resizeHeaders();
+}
+
+void UIVMActivityOverviewTableView::updateColumVisibility()
+{
+ UIActivityOverviewProxyModel *pProxyModel = qobject_cast<UIActivityOverviewProxyModel *>(model());
+ if (!pProxyModel)
+ return;
+ UIActivityOverviewModel *pModel = qobject_cast<UIActivityOverviewModel *>(pProxyModel->sourceModel());
+ QHeaderView *pHeader = horizontalHeader();
+
+ if (!pModel || !pHeader)
+ return;
+ for (int i = (int)VMActivityOverviewColumn_Name; i < (int)VMActivityOverviewColumn_Max; ++i)
+ {
+ if (!pModel->columnVisible(i))
+ pHeader->hideSection(i);
+ else
+ pHeader->showSection(i);
+ }
+ resizeHeaders();
+}
+
+int UIVMActivityOverviewTableView::selectedItemIndex() const
+{
+ UIActivityOverviewProxyModel *pModel = qobject_cast<UIActivityOverviewProxyModel*>(model());
+ if (!pModel)
+ return -1;
+
+ QItemSelectionModel *pSelectionModel = selectionModel();
+ if (!pSelectionModel)
+ return -1;
+ QModelIndexList selectedItemIndices = pSelectionModel->selectedRows();
+ if (selectedItemIndices.isEmpty())
+ return -1;
+
+ /* just use the the 1st index: */
+ QModelIndex modelIndex = pModel->mapToSource(selectedItemIndices[0]);
+
+ if (!modelIndex.isValid())
+ return -1;
+ return modelIndex.row();
+}
+
+bool UIVMActivityOverviewTableView::hasSelection() const
+{
+ if (!selectionModel())
+ return false;
+ return selectionModel()->hasSelection();
+}
+
+void UIVMActivityOverviewTableView::resizeEvent(QResizeEvent *pEvent)
+{
+ resizeHeaders();
+ QTableView::resizeEvent(pEvent);
+}
+
+void UIVMActivityOverviewTableView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
+{
+ emit sigSelectionChanged(selected, deselected);
+ QTableView::selectionChanged(selected, deselected);
+}
+
+void UIVMActivityOverviewTableView::mousePressEvent(QMouseEvent *pEvent)
+{
+ if (!indexAt(pEvent->pos()).isValid())
+ clearSelection();
+ QTableView::mousePressEvent(pEvent);
+}
+
+void UIVMActivityOverviewTableView::resizeHeaders()
+{
+ QHeaderView* pHeader = horizontalHeader();
+ if (!pHeader)
+ return;
+ int iSectionCount = pHeader->count();
+ int iHiddenSectionCount = pHeader->hiddenSectionCount();
+ int iWidth = width() / (iSectionCount - iHiddenSectionCount);
+ for (int i = 0; i < iSectionCount; ++i)
+ {
+ if (pHeader->isSectionHidden(i))
+ continue;
+ int iMinWidth = m_minimumColumnWidths.value((VMActivityOverviewColumn)i, 0);
+ pHeader->resizeSection(i, iWidth < iMinWidth ? iMinWidth : iWidth);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewItem implementation. *
+*********************************************************************************************************************************/
+UIActivityOverviewItem::UIActivityOverviewItem(const QUuid &uid, const QString &strVMName, KMachineState enmState)
+ : m_VMuid(uid)
+ , m_strVMName(strVMName)
+ , m_enmMachineState(enmState)
+ , m_uCPUGuestLoad(0)
+ , m_uCPUVMMLoad(0)
+ , m_uTotalRAM(0)
+ , m_uFreeRAM(0)
+ , m_uUsedRAM(0)
+ , m_fRAMUsagePercentage(0)
+ , m_uNetworkDownRate(0)
+ , m_uNetworkUpRate(0)
+ , m_uNetworkDownTotal(0)
+ , m_uNetworkUpTotal(0)
+ , m_uDiskWriteRate(0)
+ , m_uDiskReadRate(0)
+ , m_uDiskWriteTotal(0)
+ , m_uDiskReadTotal(0)
+ , m_uVMExitRate(0)
+ , m_uVMExitTotal(0)
+{
+ if (m_enmMachineState == KMachineState_Running)
+ resetDebugger();
+}
+
+UIActivityOverviewItem::UIActivityOverviewItem()
+ : m_VMuid(QUuid())
+ , m_uCPUGuestLoad(0)
+ , m_uCPUVMMLoad(0)
+ , m_uTotalRAM(0)
+ , m_uUsedRAM(0)
+ , m_fRAMUsagePercentage(0)
+ , m_uNetworkDownRate(0)
+ , m_uNetworkUpRate(0)
+ , m_uNetworkDownTotal(0)
+ , m_uNetworkUpTotal(0)
+ , m_uDiskWriteRate(0)
+ , m_uDiskReadRate(0)
+ , m_uDiskWriteTotal(0)
+ , m_uDiskReadTotal(0)
+ , m_uVMExitRate(0)
+ , m_uVMExitTotal(0)
+{
+}
+
+void UIActivityOverviewItem::resetDebugger()
+{
+ m_comSession = uiCommon().openSession(m_VMuid, KLockType_Shared);
+ if (!m_comSession.isNull())
+ {
+ CConsole comConsole = m_comSession.GetConsole();
+ if (!comConsole.isNull())
+ {
+ m_comGuest = comConsole.GetGuest();
+ m_comDebugger = comConsole.GetDebugger();
+ }
+ }
+}
+
+UIActivityOverviewItem::~UIActivityOverviewItem()
+{
+ if (!m_comSession.isNull())
+ m_comSession.UnlockMachine();
+}
+
+bool UIActivityOverviewItem::operator==(const UIActivityOverviewItem& other) const
+{
+ if (m_VMuid == other.m_VMuid)
+ return true;
+ return false;
+}
+
+bool UIActivityOverviewItem::isWithGuestAdditions()
+{
+ if (m_comGuest.isNull())
+ return false;
+ return m_comGuest.GetAdditionsStatus(m_comGuest.GetAdditionsRunLevel());
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewHostStats implementation. *
+*********************************************************************************************************************************/
+
+UIVMActivityOverviewHostStats::UIVMActivityOverviewHostStats()
+ : m_iCPUUserLoad(0)
+ , m_iCPUKernelLoad(0)
+ , m_iCPUFreq(0)
+ , m_iRAMTotal(0)
+ , m_iRAMFree(0)
+ , m_iFSTotal(0)
+ , m_iFSFree(0)
+{
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewProxyModel implementation. *
+*********************************************************************************************************************************/
+UIActivityOverviewProxyModel::UIActivityOverviewProxyModel(QObject *parent /* = 0 */)
+ :QSortFilterProxyModel(parent)
+{
+}
+
+void UIActivityOverviewProxyModel::dataUpdate()
+{
+ if (sourceModel())
+ emit dataChanged(index(0,0), index(sourceModel()->rowCount(), sourceModel()->columnCount()));
+ invalidate();
+}
+
+void UIActivityOverviewProxyModel::setNotRunningVMVisibility(bool fShow)
+{
+ m_fShowNotRunningVMs = fShow;
+ invalidateFilter();
+}
+
+
+bool UIActivityOverviewProxyModel::lessThan(const QModelIndex &sourceLeftIndex, const QModelIndex &sourceRightIndex) const
+{
+ UIActivityOverviewModel *pModel = qobject_cast<UIActivityOverviewModel*>(sourceModel());
+ if (pModel)
+ {
+ KMachineState enmLeftState = pModel->machineState(sourceLeftIndex.row());
+ KMachineState enmRightState = pModel->machineState(sourceRightIndex.row());
+ if ((enmLeftState == KMachineState_Running) && (enmRightState != KMachineState_Running))
+ {
+ if (sortOrder() == Qt::AscendingOrder)
+ return true;
+ else
+ return false;
+ }
+ if ((enmLeftState != KMachineState_Running) && (enmRightState == KMachineState_Running))
+ {
+ if (sortOrder() == Qt::AscendingOrder)
+ return false;
+ else
+ return true;
+ }
+
+ }
+ return QSortFilterProxyModel::lessThan(sourceLeftIndex, sourceRightIndex);
+}
+
+bool UIActivityOverviewProxyModel::filterAcceptsRow(int iSourceRow, const QModelIndex &sourceParent) const
+{
+ Q_UNUSED(sourceParent);
+ if (m_fShowNotRunningVMs)
+ return true;
+ UIActivityOverviewModel *pModel = qobject_cast<UIActivityOverviewModel*>(sourceModel());
+ if (!pModel)
+ return true;
+ if (pModel->machineState(iSourceRow) != KMachineState_Running)
+ return false;
+ return true;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIActivityOverviewModel implementation. *
+*********************************************************************************************************************************/
+UIActivityOverviewModel::UIActivityOverviewModel(QObject *parent /*= 0*/)
+ :QAbstractTableModel(parent)
+ , m_pTimer(new QTimer(this))
+ , m_fShouldUpdate(true)
+{
+ initialize();
+}
+
+void UIActivityOverviewModel::initialize()
+{
+ for (int i = 0; i < (int)VMActivityOverviewColumn_Max; ++i)
+ m_columnDataMaxLength[i] = 0;
+
+ initializeItems();
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UIActivityOverviewModel::sltMachineStateChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineRegistered,
+ this, &UIActivityOverviewModel::sltMachineRegistered);
+ if (m_pTimer)
+ {
+ connect(m_pTimer, &QTimer::timeout, this, &UIActivityOverviewModel::sltTimeout);
+ m_pTimer->start(1000);
+ }
+}
+
+int UIActivityOverviewModel::rowCount(const QModelIndex &parent /* = QModelIndex() */) const
+{
+ Q_UNUSED(parent);
+ return m_itemList.size();
+}
+
+int UIActivityOverviewModel::columnCount(const QModelIndex &parent /* = QModelIndex() */) const
+{
+ Q_UNUSED(parent);
+ return VMActivityOverviewColumn_Max;
+}
+
+void UIActivityOverviewModel::setShouldUpdate(bool fShouldUpdate)
+{
+ m_fShouldUpdate = fShouldUpdate;
+}
+
+const QMap<int, int> UIActivityOverviewModel::dataLengths() const
+{
+ return m_columnDataMaxLength;
+}
+
+QUuid UIActivityOverviewModel::itemUid(int iIndex)
+{
+ if (iIndex >= m_itemList.size())
+ return QUuid();
+ return m_itemList[iIndex].m_VMuid;
+}
+
+int UIActivityOverviewModel::itemIndex(const QUuid &uid)
+{
+ for (int i = 0; i < m_itemList.size(); ++i)
+ {
+ if (m_itemList[i].m_VMuid == uid)
+ return i;
+ }
+ return -1;
+}
+
+KMachineState UIActivityOverviewModel::machineState(int rowIndex) const
+{
+ if (rowIndex >= m_itemList.size() || rowIndex < 0)
+ return KMachineState_Null;
+ return m_itemList[rowIndex].m_enmMachineState;
+}
+
+void UIActivityOverviewModel::setDefaultViewFont(const QFont &font)
+{
+ m_defaultViewFont = font;
+}
+
+void UIActivityOverviewModel::setDefaultViewFontColor(const QColor &color)
+{
+ m_defaultViewFontColor = color;
+}
+
+QVariant UIActivityOverviewModel::data(const QModelIndex &index, int role) const
+{
+ if (machineState(index.row()) != KMachineState_Running)
+ {
+ if (role == Qt::FontRole)
+ {
+ QFont font(m_defaultViewFont);
+ font.setItalic(true);
+ return font;
+ }
+ if (role == Qt::ForegroundRole)
+ return m_defaultViewFontColor.lighter(250);
+ }
+ if (!index.isValid() || role != Qt::DisplayRole || index.row() >= rowCount())
+ return QVariant();
+ if (index.column() == VMActivityOverviewColumn_Name)
+ return m_itemList[index.row()].m_columnData[index.column()];
+ if (m_itemList[index.row()].m_enmMachineState != KMachineState_Running)
+ return gpConverter->toString(m_itemList[index.row()].m_enmMachineState);
+ return m_itemList[index.row()].m_columnData[index.column()];
+}
+
+void UIActivityOverviewModel::clearData()
+{
+ /* We have a request to detach COM stuff,
+ * first of all we are removing all the items,
+ * this will detach COM wrappers implicitly: */
+ m_itemList.clear();
+ /* Detaching perf. collector finally,
+ * please do not use it after all: */
+ m_performanceCollector.detach();
+}
+
+QVariant UIActivityOverviewModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
+ return m_columnTitles.value((VMActivityOverviewColumn)section, QString());;
+ return QVariant();
+}
+
+void UIActivityOverviewModel::setColumnCaptions(const QMap<int, QString>& captions)
+{
+ m_columnTitles = captions;
+}
+
+void UIActivityOverviewModel::initializeItems()
+{
+ foreach (const CMachine &comMachine, uiCommon().virtualBox().GetMachines())
+ {
+ if (!comMachine.isNull())
+ addItem(comMachine.GetId(), comMachine.GetName(), comMachine.GetState());
+ }
+ setupPerformanceCollector();
+}
+
+void UIActivityOverviewModel::sltMachineStateChanged(const QUuid &uId, const KMachineState state)
+{
+ int iIndex = itemIndex(uId);
+ if (iIndex != -1 && iIndex < m_itemList.size())
+ {
+ m_itemList[iIndex].m_enmMachineState = state;
+ if (state == KMachineState_Running)
+ m_itemList[iIndex].resetDebugger();
+ }
+}
+
+void UIActivityOverviewModel::sltMachineRegistered(const QUuid &uId, bool fRegistered)
+{
+ if (fRegistered)
+ {
+ CMachine comMachine = uiCommon().virtualBox().FindMachine(uId.toString());
+ if (!comMachine.isNull())
+ addItem(uId, comMachine.GetName(), comMachine.GetState());
+ }
+ else
+ removeItem(uId);
+ emit sigDataUpdate();
+}
+
+void UIActivityOverviewModel::getHostRAMStats()
+{
+ CHost comHost = uiCommon().host();
+ m_hostStats.m_iRAMTotal = _1M * (quint64)comHost.GetMemorySize();
+ m_hostStats.m_iRAMFree = _1M * (quint64)comHost.GetMemoryAvailable();
+}
+
+void UIActivityOverviewModel::sltTimeout()
+{
+ if (!m_fShouldUpdate)
+ return;
+ ULONG aPctExecuting;
+ ULONG aPctHalted;
+ ULONG aPctVMM;
+
+ bool fCPUColumns = columnVisible(VMActivityOverviewColumn_CPUVMMLoad) || columnVisible(VMActivityOverviewColumn_CPUGuestLoad);
+ bool fNetworkColumns = columnVisible(VMActivityOverviewColumn_NetworkUpRate)
+ || columnVisible(VMActivityOverviewColumn_NetworkDownRate)
+ || columnVisible(VMActivityOverviewColumn_NetworkUpTotal)
+ || columnVisible(VMActivityOverviewColumn_NetworkDownTotal);
+ bool fIOColumns = columnVisible(VMActivityOverviewColumn_DiskIOReadRate)
+ || columnVisible(VMActivityOverviewColumn_DiskIOWriteRate)
+ || columnVisible(VMActivityOverviewColumn_DiskIOReadTotal)
+ || columnVisible(VMActivityOverviewColumn_DiskIOWriteTotal);
+ bool fVMExitColumn = columnVisible(VMActivityOverviewColumn_VMExits);
+
+ /* Host's RAM usage is obtained from IHost not from IPerformanceCollectior: */
+ getHostRAMStats();
+
+ /* RAM usage and Host Stats: */
+ queryPerformanceCollector();
+
+ for (int i = 0; i < m_itemList.size(); ++i)
+ {
+ if (!m_itemList[i].m_comDebugger.isNull())
+ {
+ /* CPU Load: */
+ if (fCPUColumns)
+ {
+ m_itemList[i].m_comDebugger.GetCPULoad(0x7fffffff, aPctExecuting, aPctHalted, aPctVMM);
+ m_itemList[i].m_uCPUGuestLoad = aPctExecuting;
+ m_itemList[i].m_uCPUVMMLoad = aPctVMM;
+ }
+ /* Network rate: */
+ if (fNetworkColumns)
+ {
+ quint64 uPrevDownTotal = m_itemList[i].m_uNetworkDownTotal;
+ quint64 uPrevUpTotal = m_itemList[i].m_uNetworkUpTotal;
+ UIMonitorCommon::getNetworkLoad(m_itemList[i].m_comDebugger,
+ m_itemList[i].m_uNetworkDownTotal, m_itemList[i].m_uNetworkUpTotal);
+ m_itemList[i].m_uNetworkDownRate = m_itemList[i].m_uNetworkDownTotal - uPrevDownTotal;
+ m_itemList[i].m_uNetworkUpRate = m_itemList[i].m_uNetworkUpTotal - uPrevUpTotal;
+ }
+ /* IO rate: */
+ if (fIOColumns)
+ {
+ quint64 uPrevWriteTotal = m_itemList[i].m_uDiskWriteTotal;
+ quint64 uPrevReadTotal = m_itemList[i].m_uDiskReadTotal;
+ UIMonitorCommon::getDiskLoad(m_itemList[i].m_comDebugger,
+ m_itemList[i].m_uDiskWriteTotal, m_itemList[i].m_uDiskReadTotal);
+ m_itemList[i].m_uDiskWriteRate = m_itemList[i].m_uDiskWriteTotal - uPrevWriteTotal;
+ m_itemList[i].m_uDiskReadRate = m_itemList[i].m_uDiskReadTotal - uPrevReadTotal;
+ }
+ /* VM Exits: */
+ if (fVMExitColumn)
+ {
+ quint64 uPrevVMExitsTotal = m_itemList[i].m_uVMExitTotal;
+ UIMonitorCommon::getVMMExitCount(m_itemList[i].m_comDebugger, m_itemList[i].m_uVMExitTotal);
+ m_itemList[i].m_uVMExitRate = m_itemList[i].m_uVMExitTotal - uPrevVMExitsTotal;
+ }
+ }
+ }
+ int iDecimalCount = 2;
+ for (int i = 0; i < m_itemList.size(); ++i)
+ {
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_Name] = m_itemList[i].m_strVMName;
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_CPUGuestLoad] =
+ QString("%1%").arg(QString::number(m_itemList[i].m_uCPUGuestLoad));
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_CPUVMMLoad] =
+ QString("%1%").arg(QString::number(m_itemList[i].m_uCPUVMMLoad));
+
+ if (m_itemList[i].isWithGuestAdditions())
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_RAMUsedAndTotal] =
+ QString("%1/%2").arg(UITranslator::formatSize(_1K * m_itemList[i].m_uUsedRAM, iDecimalCount)).
+ arg(UITranslator::formatSize(_1K * m_itemList[i].m_uTotalRAM, iDecimalCount));
+ else
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_RAMUsedAndTotal] = UIVMActivityOverviewWidget::tr("N/A");
+
+ if (m_itemList[i].isWithGuestAdditions())
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_RAMUsedPercentage] =
+ QString("%1%").arg(QString::number(m_itemList[i].m_fRAMUsagePercentage, 'f', 2));
+ else
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_RAMUsedPercentage] = UIVMActivityOverviewWidget::tr("N/A");
+
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_NetworkUpRate] =
+ QString("%1").arg(UITranslator::formatSize(m_itemList[i].m_uNetworkUpRate, iDecimalCount));
+
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_NetworkDownRate] =
+ QString("%1").arg(UITranslator::formatSize(m_itemList[i].m_uNetworkDownRate, iDecimalCount));
+
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_NetworkUpTotal] =
+ QString("%1").arg(UITranslator::formatSize(m_itemList[i].m_uNetworkUpTotal, iDecimalCount));
+
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_NetworkDownTotal] =
+ QString("%1").arg(UITranslator::formatSize(m_itemList[i].m_uNetworkDownTotal, iDecimalCount));
+
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_DiskIOReadRate] =
+ QString("%1").arg(UITranslator::formatSize(m_itemList[i].m_uDiskReadRate, iDecimalCount));
+
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_DiskIOWriteRate] =
+ QString("%1").arg(UITranslator::formatSize(m_itemList[i].m_uDiskWriteRate, iDecimalCount));
+
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_DiskIOReadTotal] =
+ QString("%1").arg(UITranslator::formatSize(m_itemList[i].m_uDiskReadTotal, iDecimalCount));
+
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_DiskIOWriteTotal] =
+ QString("%1").arg(UITranslator::formatSize(m_itemList[i].m_uDiskWriteTotal, iDecimalCount));
+
+ m_itemList[i].m_columnData[VMActivityOverviewColumn_VMExits] =
+ QString("%1/%2").arg(UITranslator::addMetricSuffixToNumber(m_itemList[i].m_uVMExitRate)).
+ arg(UITranslator::addMetricSuffixToNumber(m_itemList[i].m_uVMExitTotal));
+ }
+
+ for (int i = 0; i < (int)VMActivityOverviewColumn_Max; ++i)
+ {
+ for (int j = 0; j < m_itemList.size(); ++j)
+ if (m_columnDataMaxLength.value(i, 0) < m_itemList[j].m_columnData[i].length())
+ m_columnDataMaxLength[i] = m_itemList[j].m_columnData[i].length();
+ }
+ emit sigDataUpdate();
+ emit sigHostStatsUpdate(m_hostStats);
+}
+
+void UIActivityOverviewModel::setupPerformanceCollector()
+{
+ m_nameList.clear();
+ m_objectList.clear();
+ /* Initialize and configure CPerformanceCollector: */
+ const ULONG iPeriod = 1;
+ const int iMetricSetupCount = 1;
+ if (m_performanceCollector.isNull())
+ m_performanceCollector = uiCommon().virtualBox().GetPerformanceCollector();
+ for (int i = 0; i < m_itemList.size(); ++i)
+ m_nameList << "Guest/RAM/Usage*";
+ /* This is for the host: */
+ m_nameList << "CPU*";
+ m_nameList << "FS*";
+ m_objectList = QVector<CUnknown>(m_nameList.size(), CUnknown());
+ m_performanceCollector.SetupMetrics(m_nameList, m_objectList, iPeriod, iMetricSetupCount);
+}
+
+void UIActivityOverviewModel::queryPerformanceCollector()
+{
+ QVector<QString> aReturnNames;
+ QVector<CUnknown> aReturnObjects;
+ QVector<QString> aReturnUnits;
+ QVector<ULONG> aReturnScales;
+ QVector<ULONG> aReturnSequenceNumbers;
+ QVector<ULONG> aReturnDataIndices;
+ QVector<ULONG> aReturnDataLengths;
+
+ QVector<LONG> returnData = m_performanceCollector.QueryMetricsData(m_nameList,
+ m_objectList,
+ aReturnNames,
+ aReturnObjects,
+ aReturnUnits,
+ aReturnScales,
+ aReturnSequenceNumbers,
+ aReturnDataIndices,
+ aReturnDataLengths);
+ /* Parse the result we get from CPerformanceCollector to get respective values: */
+ for (int i = 0; i < aReturnNames.size(); ++i)
+ {
+ if (aReturnDataLengths[i] == 0)
+ continue;
+ /* Read the last of the return data disregarding the rest since we are caching the data in GUI side: */
+ float fData = returnData[aReturnDataIndices[i] + aReturnDataLengths[i] - 1] / (float)aReturnScales[i];
+ if (aReturnNames[i].contains("RAM", Qt::CaseInsensitive) && !aReturnNames[i].contains(":"))
+ {
+ if (aReturnNames[i].contains("Total", Qt::CaseInsensitive) || aReturnNames[i].contains("Free", Qt::CaseInsensitive))
+ {
+ {
+ CMachine comMachine = (CMachine)aReturnObjects[i];
+ if (comMachine.isNull())
+ continue;
+ int iIndex = itemIndex(comMachine.GetId());
+ if (iIndex == -1 || iIndex >= m_itemList.size())
+ continue;
+ if (aReturnNames[i].contains("Total", Qt::CaseInsensitive))
+ m_itemList[iIndex].m_uTotalRAM = fData;
+ else
+ m_itemList[iIndex].m_uFreeRAM = fData;
+ }
+ }
+ }
+ else if (aReturnNames[i].contains("CPU/Load/User", Qt::CaseInsensitive) && !aReturnNames[i].contains(":"))
+ {
+ CHost comHost = (CHost)aReturnObjects[i];
+ if (!comHost.isNull())
+ m_hostStats.m_iCPUUserLoad = fData;
+ }
+ else if (aReturnNames[i].contains("CPU/Load/Kernel", Qt::CaseInsensitive) && !aReturnNames[i].contains(":"))
+ {
+ CHost comHost = (CHost)aReturnObjects[i];
+ if (!comHost.isNull())
+ m_hostStats.m_iCPUKernelLoad = fData;
+ }
+ else if (aReturnNames[i].contains("CPU/MHz", Qt::CaseInsensitive) && !aReturnNames[i].contains(":"))
+ {
+ CHost comHost = (CHost)aReturnObjects[i];
+ if (!comHost.isNull())
+ m_hostStats.m_iCPUFreq = fData;
+ }
+ else if (aReturnNames[i].contains("FS", Qt::CaseInsensitive) &&
+ aReturnNames[i].contains("Total", Qt::CaseInsensitive) &&
+ !aReturnNames[i].contains(":"))
+ {
+ CHost comHost = (CHost)aReturnObjects[i];
+ if (!comHost.isNull())
+ m_hostStats.m_iFSTotal = _1M * fData;
+ }
+ else if (aReturnNames[i].contains("FS", Qt::CaseInsensitive) &&
+ aReturnNames[i].contains("Free", Qt::CaseInsensitive) &&
+ !aReturnNames[i].contains(":"))
+ {
+ CHost comHost = (CHost)aReturnObjects[i];
+ if (!comHost.isNull())
+ m_hostStats.m_iFSFree = _1M * fData;
+ }
+ }
+ for (int i = 0; i < m_itemList.size(); ++i)
+ {
+ m_itemList[i].m_uUsedRAM = m_itemList[i].m_uTotalRAM - m_itemList[i].m_uFreeRAM;
+ if (m_itemList[i].m_uTotalRAM != 0)
+ m_itemList[i].m_fRAMUsagePercentage = 100.f * (m_itemList[i].m_uUsedRAM / (float)m_itemList[i].m_uTotalRAM);
+ }
+}
+
+void UIActivityOverviewModel::addItem(const QUuid& uMachineId, const QString& strMachineName, KMachineState enmState)
+{
+ m_itemList.append(UIActivityOverviewItem(uMachineId, strMachineName, enmState));
+}
+
+void UIActivityOverviewModel::removeItem(const QUuid& uMachineId)
+{
+ int iIndex = itemIndex(uMachineId);
+ if (iIndex == -1)
+ return;
+ m_itemList.remove(iIndex);
+}
+
+void UIActivityOverviewModel::setColumnVisible(const QMap<int, bool>& columnVisible)
+{
+ m_columnVisible = columnVisible;
+}
+
+bool UIActivityOverviewModel::columnVisible(int iColumnId) const
+{
+ return m_columnVisible.value(iColumnId, true);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVMActivityOverviewWidget implementation. *
+*********************************************************************************************************************************/
+
+UIVMActivityOverviewWidget::UIVMActivityOverviewWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar /* = true */, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_pToolBar(0)
+ , m_pTableView(0)
+ , m_pProxyModel(0)
+ , m_pModel(0)
+ , m_pColumnVisibilityToggleMenu(0)
+ , m_pHostStatsWidget(0)
+ , m_fIsCurrentTool(true)
+ , m_iSortIndicatorWidth(0)
+ , m_fShowNotRunningVMs(false)
+{
+ prepare();
+}
+
+QMenu *UIVMActivityOverviewWidget::menu() const
+{
+ return NULL;
+}
+
+QMenu *UIVMActivityOverviewWidget::columnVisiblityToggleMenu() const
+{
+ return m_pColumnVisibilityToggleMenu;
+}
+
+bool UIVMActivityOverviewWidget::isCurrentTool() const
+{
+ return m_fIsCurrentTool;
+}
+
+void UIVMActivityOverviewWidget::setIsCurrentTool(bool fIsCurrentTool)
+{
+ m_fIsCurrentTool = fIsCurrentTool;
+ if (m_pModel)
+ m_pModel->setShouldUpdate(fIsCurrentTool);
+}
+
+void UIVMActivityOverviewWidget::retranslateUi()
+{
+ m_columnTitles[VMActivityOverviewColumn_Name] = UIVMActivityOverviewWidget::tr("VM Name");
+ m_columnTitles[VMActivityOverviewColumn_CPUGuestLoad] = UIVMActivityOverviewWidget::tr("CPU Guest");
+ m_columnTitles[VMActivityOverviewColumn_CPUVMMLoad] = UIVMActivityOverviewWidget::tr("CPU VMM");
+ m_columnTitles[VMActivityOverviewColumn_RAMUsedAndTotal] = UIVMActivityOverviewWidget::tr("RAM Used/Total");
+ m_columnTitles[VMActivityOverviewColumn_RAMUsedPercentage] = UIVMActivityOverviewWidget::tr("RAM %");
+ m_columnTitles[VMActivityOverviewColumn_NetworkUpRate] = UIVMActivityOverviewWidget::tr("Network Up Rate");
+ m_columnTitles[VMActivityOverviewColumn_NetworkDownRate] = UIVMActivityOverviewWidget::tr("Network Down Rate");
+ m_columnTitles[VMActivityOverviewColumn_NetworkUpTotal] = UIVMActivityOverviewWidget::tr("Network Up Total");
+ m_columnTitles[VMActivityOverviewColumn_NetworkDownTotal] = UIVMActivityOverviewWidget::tr("Network Down Total");
+ m_columnTitles[VMActivityOverviewColumn_DiskIOReadRate] = UIVMActivityOverviewWidget::tr("Disk Read Rate");
+ m_columnTitles[VMActivityOverviewColumn_DiskIOWriteRate] = UIVMActivityOverviewWidget::tr("Disk Write Rate");
+ m_columnTitles[VMActivityOverviewColumn_DiskIOReadTotal] = UIVMActivityOverviewWidget::tr("Disk Read Total");
+ m_columnTitles[VMActivityOverviewColumn_DiskIOWriteTotal] = UIVMActivityOverviewWidget::tr("Disk Write Total");
+ m_columnTitles[VMActivityOverviewColumn_VMExits] = UIVMActivityOverviewWidget::tr("VM Exits");
+
+ updateColumnsMenu();
+
+ if (m_pModel)
+ m_pModel->setColumnCaptions(m_columnTitles);
+
+ computeMinimumColumnWidths();
+}
+
+void UIVMActivityOverviewWidget::showEvent(QShowEvent *pEvent)
+{
+ if (m_pVMActivityMonitorAction && m_pTableView)
+ m_pVMActivityMonitorAction->setEnabled(m_pTableView->hasSelection());
+
+ QIWithRetranslateUI<QWidget>::showEvent(pEvent);
+}
+
+void UIVMActivityOverviewWidget::prepare()
+{
+ /* Try to guest the sort indicator's width: */
+ int iIndicatorMargin = 3;
+ QIcon sortIndicator = qApp->QApplication::style()->standardIcon(QStyle::SP_TitleBarUnshadeButton);
+ QList<QSize> iconSizes = sortIndicator.availableSizes();
+ foreach(const QSize &msize, iconSizes)
+ m_iSortIndicatorWidth = qMax(m_iSortIndicatorWidth, msize.width());
+ if (m_iSortIndicatorWidth == 0)
+ m_iSortIndicatorWidth = 20;
+ m_iSortIndicatorWidth += 2 * iIndicatorMargin;
+
+ prepareWidgets();
+ loadSettings();
+ prepareActions();
+ retranslateUi();
+ updateModelColumVisibilityCache();
+ uiCommon().setHelpKeyword(this, "vm-activity-overview-widget");
+ connect(&uiCommon(), &UICommon::sigAskToCommitData,
+ this, &UIVMActivityOverviewWidget::sltSaveSettings);
+ connect(&uiCommon(), &UICommon::sigAskToDetachCOM,
+ this, &UIVMActivityOverviewWidget::sltClearCOMData);
+}
+
+void UIVMActivityOverviewWidget::prepareWidgets()
+{
+ /* Create main-layout: */
+ new QVBoxLayout(this);
+ if (!layout())
+ return;
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ layout()->setSpacing(10);
+#else
+ layout()->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ if (m_fShowToolbar)
+ prepareToolBar();
+
+ m_pHostStatsWidget = new UIVMActivityOverviewHostStatsWidget;
+ if (m_pHostStatsWidget)
+ layout()->addWidget(m_pHostStatsWidget);
+
+ m_pModel = new UIActivityOverviewModel(this);
+ m_pProxyModel = new UIActivityOverviewProxyModel(this);
+ m_pTableView = new UIVMActivityOverviewTableView();
+ if (m_pTableView && m_pModel && m_pProxyModel)
+ {
+ layout()->addWidget(m_pTableView);
+ m_pProxyModel->setSourceModel(m_pModel);
+ m_pProxyModel->setNotRunningVMVisibility(m_fShowNotRunningVMs);
+ m_pTableView->setModel(m_pProxyModel);
+ m_pTableView->setItemDelegate(new UIVMActivityOverviewDelegate(this));
+ m_pTableView->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_pTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ m_pTableView->setShowGrid(false);
+ m_pTableView->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTableView->horizontalHeader()->setHighlightSections(false);
+ m_pTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+ m_pTableView->verticalHeader()->setVisible(false);
+ m_pTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ /* Minimize the row height: */
+ m_pTableView->verticalHeader()->setDefaultSectionSize(m_pTableView->verticalHeader()->minimumSectionSize());
+ m_pTableView->setAlternatingRowColors(true);
+ m_pTableView->setSortingEnabled(true);
+ m_pTableView->sortByColumn(0, Qt::AscendingOrder);
+ /* Store the default font and its color of the table on the view. They are used in ::data(..): */
+ m_pModel->setDefaultViewFont(m_pTableView->font());
+ m_pModel->setDefaultViewFontColor(m_pTableView->palette().color(QPalette::WindowText));
+
+ connect(m_pModel, &UIActivityOverviewModel::sigDataUpdate,
+ this, &UIVMActivityOverviewWidget::sltHandleDataUpdate);
+ connect(m_pModel, &UIActivityOverviewModel::sigHostStatsUpdate,
+ this, &UIVMActivityOverviewWidget::sltHandleHostStatsUpdate);
+ connect(m_pTableView, &UIVMActivityOverviewTableView::customContextMenuRequested,
+ this, &UIVMActivityOverviewWidget::sltHandleTableContextMenuRequest);
+ connect(m_pTableView, &UIVMActivityOverviewTableView::sigSelectionChanged,
+ this, &UIVMActivityOverviewWidget::sltHandleTableSelectionChanged);
+ updateModelColumVisibilityCache();
+ }
+}
+
+void UIVMActivityOverviewWidget::updateColumnsMenu()
+{
+ UIMenu *pMenu = m_pActionPool->action(UIActionIndexMN_M_VMActivityOverview_M_Columns)->menu();
+ if (!pMenu)
+ return;
+ pMenu->clear();
+ for (int i = 0; i < VMActivityOverviewColumn_Max; ++i)
+ {
+ QAction *pAction = pMenu->addAction(m_columnTitles[i]);
+ pAction->setCheckable(true);
+ if (i == (int)VMActivityOverviewColumn_Name)
+ pAction->setEnabled(false);
+ pAction->setData(i);
+ pAction->setChecked(columnVisible(i));
+ connect(pAction, &QAction::toggled, this, &UIVMActivityOverviewWidget::sltHandleColumnAction);
+ }
+}
+
+void UIVMActivityOverviewWidget::prepareActions()
+{
+ updateColumnsMenu();
+ m_pVMActivityMonitorAction =
+ m_pActionPool->action(UIActionIndexMN_M_VMActivityOverview_S_SwitchToMachineActivity);
+
+ if (m_pVMActivityMonitorAction)
+ connect(m_pVMActivityMonitorAction, &QAction::triggered, this, &UIVMActivityOverviewWidget::sltHandleShowVMActivityMonitor);
+}
+
+void UIVMActivityOverviewWidget::prepareToolBar()
+{
+ /* Create toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ AssertPtrReturnVoid(m_pToolBar);
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+ }
+#else
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+#endif
+ }
+}
+
+void UIVMActivityOverviewWidget::loadSettings()
+{
+ /* Load the list of hidden columns: */
+ QStringList hiddenColumnList = gEDataManager->VMActivityOverviewHiddenColumnList();
+ for (int i = (int)VMActivityOverviewColumn_Name; i < (int)VMActivityOverviewColumn_Max; ++i)
+ m_columnVisible[i] = true;
+ foreach(const QString& strColumn, hiddenColumnList)
+ setColumnVisible((int)gpConverter->fromInternalString<VMActivityOverviewColumn>(strColumn), false);
+ /* Load other options: */
+ sltNotRunningVMVisibility(gEDataManager->VMActivityOverviewShowAllMachines());
+}
+
+void UIVMActivityOverviewWidget::sltSaveSettings()
+{
+ /* Save the list of hidden columns: */
+ QStringList hiddenColumnList;
+ for (int i = 0; i < m_columnVisible.size(); ++i)
+ {
+ if (!columnVisible(i))
+ hiddenColumnList << gpConverter->toInternalString((VMActivityOverviewColumn) i);
+ }
+ gEDataManager->setVMActivityOverviewHiddenColumnList(hiddenColumnList);
+ gEDataManager->setVMActivityOverviewShowAllMachines(m_fShowNotRunningVMs);
+}
+
+void UIVMActivityOverviewWidget::sltClearCOMData()
+{
+ if (m_pModel)
+ m_pModel->clearData();
+}
+
+void UIVMActivityOverviewWidget::sltToggleColumnSelectionMenu(bool fChecked)
+{
+ (void)fChecked;
+ if (!m_pColumnVisibilityToggleMenu)
+ return;
+ m_pColumnVisibilityToggleMenu->exec(this->mapToGlobal(QPoint(0,0)));
+}
+
+void UIVMActivityOverviewWidget::sltHandleColumnAction(bool fChecked)
+{
+ QAction* pSender = qobject_cast<QAction*>(sender());
+ if (!pSender)
+ return;
+ setColumnVisible(pSender->data().toInt(), fChecked);
+}
+
+void UIVMActivityOverviewWidget::sltHandleHostStatsUpdate(const UIVMActivityOverviewHostStats &stats)
+{
+ if (m_pHostStatsWidget)
+ m_pHostStatsWidget->setHostStats(stats);
+}
+
+void UIVMActivityOverviewWidget::sltHandleDataUpdate()
+{
+ computeMinimumColumnWidths();
+ if (m_pProxyModel)
+ m_pProxyModel->dataUpdate();
+}
+
+void UIVMActivityOverviewWidget::sltHandleTableContextMenuRequest(const QPoint &pos)
+{
+ if (!m_pTableView)
+ return;
+
+ QMenu menu;
+ if (m_pVMActivityMonitorAction)
+ menu.addAction(m_pVMActivityMonitorAction);
+ menu.addSeparator();
+ QAction *pHideNotRunningAction =
+ menu.addAction(UIVMActivityOverviewWidget::tr("List all virtual machines"));
+ pHideNotRunningAction->setCheckable(true);
+ pHideNotRunningAction->setChecked(m_fShowNotRunningVMs);
+ connect(pHideNotRunningAction, &QAction::triggered,
+ this, &UIVMActivityOverviewWidget::sltNotRunningVMVisibility);
+ menu.exec(m_pTableView->mapToGlobal(pos));
+}
+
+void UIVMActivityOverviewWidget::sltHandleTableSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
+{
+ Q_UNUSED(deselected);
+ if (!m_pVMActivityMonitorAction || !m_pModel || !m_pProxyModel)
+ return;
+
+ if (selected.indexes().empty())
+ {
+ m_pVMActivityMonitorAction->setEnabled(false);
+ return;
+ }
+ int iMachineIndex = m_pProxyModel->mapToSource(selected.indexes()[0]).row();
+ if (m_pModel->machineState(iMachineIndex) != KMachineState_Running)
+ {
+ m_pVMActivityMonitorAction->setEnabled(false);
+ return;
+ }
+ m_pVMActivityMonitorAction->setEnabled(true);
+}
+
+void UIVMActivityOverviewWidget::sltHandleShowVMActivityMonitor()
+{
+ if (!m_pTableView || !m_pModel)
+ return;
+ const QUuid uMachineId = m_pModel->itemUid(m_pTableView->selectedItemIndex());
+ if (uMachineId.isNull())
+ return;
+ emit sigSwitchToMachineActivityPane(uMachineId);
+}
+
+void UIVMActivityOverviewWidget::sltNotRunningVMVisibility(bool fShow)
+{
+ m_fShowNotRunningVMs = fShow;
+ if (m_pProxyModel)
+ m_pProxyModel->setNotRunningVMVisibility(m_fShowNotRunningVMs);
+}
+
+void UIVMActivityOverviewWidget::setColumnVisible(int iColumnId, bool fVisible)
+{
+ if (m_columnVisible.contains(iColumnId) && m_columnVisible[iColumnId] == fVisible)
+ return;
+ m_columnVisible[iColumnId] = fVisible;
+ updateModelColumVisibilityCache();
+}
+
+void UIVMActivityOverviewWidget::updateModelColumVisibilityCache()
+{
+ if (m_pModel)
+ m_pModel->setColumnVisible(m_columnVisible);
+ /* Notify the table view for the changed column visibility: */
+ if (m_pTableView)
+ m_pTableView->updateColumVisibility();
+}
+
+void UIVMActivityOverviewWidget::computeMinimumColumnWidths()
+{
+ if (!m_pTableView || !m_pModel)
+ return;
+ QFontMetrics fontMetrics(m_pTableView->font());
+ const QMap<int, int> &columnDataStringLengths = m_pModel->dataLengths();
+ QMap<int, int> columnWidthsInPixels;
+ for (int i = 0; i < (int)VMActivityOverviewColumn_Max; ++i)
+ {
+ int iColumnStringWidth = columnDataStringLengths.value(i, 0);
+ int iColumnTitleWidth = m_columnTitles.value(i, QString()).length();
+ int iMax = iColumnStringWidth > iColumnTitleWidth ? iColumnStringWidth : iColumnTitleWidth;
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ columnWidthsInPixels[i] = iMax * fontMetrics.horizontalAdvance('x') +
+#else
+ columnWidthsInPixels[i] = iMax * fontMetrics.width('x') +
+#endif
+ QApplication::style()->pixelMetric(QStyle::PM_LayoutLeftMargin) +
+ QApplication::style()->pixelMetric(QStyle::PM_LayoutRightMargin) +
+ m_iSortIndicatorWidth;
+ }
+ m_pTableView->setMinimumColumnWidths(columnWidthsInPixels);
+}
+
+bool UIVMActivityOverviewWidget::columnVisible(int iColumnId) const
+{
+ return m_columnVisible.value(iColumnId, true);
+}
+
+#include "UIVMActivityOverviewWidget.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityOverviewWidget.h b/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityOverviewWidget.h
new file mode 100644
index 00000000..5610a9ff
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityOverviewWidget.h
@@ -0,0 +1,146 @@
+/* $Id: UIVMActivityOverviewWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIVMActivityOverviewWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_activity_overview_UIVMActivityOverviewWidget_h
+#define FEQT_INCLUDED_SRC_activity_overview_UIVMActivityOverviewWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMainWindow>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QFrame;
+class QItemSelection;
+class QLabel;
+class QTableView;
+class QTreeWidgetItem;
+class QIDialogButtonBox;
+class UIActionPool;
+class QIToolBar;
+class UIActivityOverviewProxyModel;
+class UIActivityOverviewModel;
+class UIVMActivityOverviewHostStats;
+class UIVMActivityOverviewHostStatsWidget;
+class UIVMActivityOverviewTableView;
+
+/** QWidget extension to display a Linux top like utility that sort running vm wrt. resource allocations. */
+class UIVMActivityOverviewWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigSwitchToMachineActivityPane(const QUuid &uMachineId);
+
+public:
+
+ UIVMActivityOverviewWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar = true, QWidget *pParent = 0);
+ QMenu *columnVisiblityToggleMenu() const;
+ QMenu *menu() const;
+
+ bool isCurrentTool() const;
+ void setIsCurrentTool(bool fIsCurrentTool);
+
+#ifdef VBOX_WS_MAC
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ void sltHandleDataUpdate();
+ void sltToggleColumnSelectionMenu(bool fChecked);
+ void sltHandleColumnAction(bool fChecked);
+ void sltHandleHostStatsUpdate(const UIVMActivityOverviewHostStats &stats);
+ void sltHandleTableContextMenuRequest(const QPoint &pos);
+ void sltHandleShowVMActivityMonitor();
+ void sltHandleTableSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+ void sltNotRunningVMVisibility(bool fShow);
+ void sltSaveSettings();
+ void sltClearCOMData();
+
+private:
+
+ void setColumnVisible(int iColumnId, bool fVisible);
+ bool columnVisible(int iColumnId) const;
+ void updateModelColumVisibilityCache();
+ void computeMinimumColumnWidths();
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ void prepare();
+ void prepareWidgets();
+ void prepareHostStatsWidgets();
+ void prepareToolBar();
+ void prepareActions();
+ void updateColumnsMenu();
+ void loadSettings();
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ const EmbedTo m_enmEmbedding;
+ UIActionPool *m_pActionPool;
+ const bool m_fShowToolbar;
+ /** @} */
+
+ /** @name Misc members.
+ * @{ */
+ QIToolBar *m_pToolBar;
+ UIVMActivityOverviewTableView *m_pTableView;
+ UIActivityOverviewProxyModel *m_pProxyModel;
+ UIActivityOverviewModel *m_pModel;
+ QMenu *m_pColumnVisibilityToggleMenu;
+ /* The key is the column id (VMActivityOverviewColumn) and value is column title. */
+ QMap<int, QString> m_columnTitles;
+ /* The key is the column id (VMActivityOverviewColumn) and value is true if the column is visible. */
+ QMap<int, bool> m_columnVisible;
+ UIVMActivityOverviewHostStatsWidget *m_pHostStatsWidget;
+ QAction *m_pVMActivityMonitorAction;
+ /** @} */
+ /** Indicates if this widget's host tool is current tool. */
+ bool m_fIsCurrentTool;
+ int m_iSortIndicatorWidth;
+ bool m_fShowNotRunningVMs;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_activity_overview_UIVMActivityOverviewWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityToolWidget.cpp b/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityToolWidget.cpp
new file mode 100644
index 00000000..a8ce85c2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityToolWidget.cpp
@@ -0,0 +1,226 @@
+/* $Id: UIVMActivityToolWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMActivityToolWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QStyle>
+
+/* GUI includes: */
+#include "UIActionPoolManager.h"
+#include "UICommon.h"
+#include "UIVMActivityMonitor.h"
+#include "UIVMActivityToolWidget.h"
+#include "UIMessageCenter.h"
+#include "QIToolBar.h"
+#include "UIVirtualMachineItem.h"
+
+#ifdef VBOX_WS_MAC
+# include "UIWindowMenuManager.h"
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+
+
+UIVMActivityToolWidget::UIVMActivityToolWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar /* = true */, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QTabWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_pToolBar(0)
+ , m_pExportToFileAction(0)
+{
+ setTabPosition(QTabWidget::East);
+ prepare();
+ prepareActions();
+ prepareToolBar();
+ sltCurrentTabChanged(0);
+}
+
+QMenu *UIVMActivityToolWidget::menu() const
+{
+ return NULL;
+}
+
+bool UIVMActivityToolWidget::isCurrentTool() const
+{
+ return m_fIsCurrentTool;
+}
+
+void UIVMActivityToolWidget::setIsCurrentTool(bool fIsCurrentTool)
+{
+ m_fIsCurrentTool = fIsCurrentTool;
+}
+
+void UIVMActivityToolWidget::retranslateUi()
+{
+}
+
+void UIVMActivityToolWidget::showEvent(QShowEvent *pEvent)
+{
+ QIWithRetranslateUI<QTabWidget>::showEvent(pEvent);
+}
+
+void UIVMActivityToolWidget::prepare()
+{
+ setTabBarAutoHide(true);
+ setLayout(new QHBoxLayout);
+
+ connect(this, &UIVMActivityToolWidget::currentChanged,
+ this, &UIVMActivityToolWidget::sltCurrentTabChanged);
+}
+
+void UIVMActivityToolWidget::setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items)
+{
+ QVector<QUuid> selectedMachines;
+
+ foreach (const UIVirtualMachineItem *item, items)
+ {
+ if (!item)
+ continue;
+ selectedMachines << item->id();
+ }
+ setMachines(selectedMachines);
+}
+
+void UIVMActivityToolWidget::setMachines(const QVector<QUuid> &machineIds)
+{
+ /* List of machines that are newly added to selected machine list: */
+ QVector<QUuid> newSelections;
+ QVector<QUuid> unselectedMachines(m_machineIds);
+
+ foreach (const QUuid &id, machineIds)
+ {
+ unselectedMachines.removeAll(id);
+ if (!m_machineIds.contains(id))
+ newSelections << id;
+ }
+ m_machineIds = machineIds;
+
+ removeTabs(unselectedMachines);
+ addTabs(newSelections);
+}
+
+void UIVMActivityToolWidget::prepareActions()
+{
+ QAction *pToResourcesAction =
+ m_pActionPool->action(UIActionIndex_M_Activity_S_ToVMActivityOverview);
+ if (pToResourcesAction)
+ connect(pToResourcesAction, &QAction::triggered, this, &UIVMActivityToolWidget::sigSwitchToActivityOverviewPane);
+
+ m_pExportToFileAction =
+ m_pActionPool->action(UIActionIndex_M_Activity_S_Export);
+ if (m_pExportToFileAction)
+ connect(m_pExportToFileAction, &QAction::triggered, this, &UIVMActivityToolWidget::sltExportToFile);
+}
+
+void UIVMActivityToolWidget::prepareToolBar()
+{
+ /* Create toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ AssertPtrReturnVoid(m_pToolBar);
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+ }
+#else
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+#endif
+ }
+}
+
+void UIVMActivityToolWidget::loadSettings()
+{
+}
+
+void UIVMActivityToolWidget::removeTabs(const QVector<QUuid> &machineIdsToRemove)
+{
+ QVector<UIVMActivityMonitor*> removeList;
+
+ for (int i = count() - 1; i >= 0; --i)
+ {
+ UIVMActivityMonitor *pMonitor = qobject_cast<UIVMActivityMonitor*>(widget(i));
+ if (!pMonitor)
+ continue;
+ if (machineIdsToRemove.contains(pMonitor->machineId()))
+ {
+ removeList << pMonitor;
+ removeTab(i);
+ }
+ }
+ qDeleteAll(removeList.begin(), removeList.end());
+}
+
+void UIVMActivityToolWidget::addTabs(const QVector<QUuid> &machineIdsToAdd)
+{
+ foreach (const QUuid &id, machineIdsToAdd)
+ {
+ CMachine comMachine = uiCommon().virtualBox().FindMachine(id.toString());
+ if (comMachine.isNull())
+ continue;
+ addTab(new UIVMActivityMonitor(m_enmEmbedding, this, comMachine), comMachine.GetName());
+ }
+}
+
+void UIVMActivityToolWidget::sltExportToFile()
+{
+ UIVMActivityMonitor *pActivityMonitor = qobject_cast<UIVMActivityMonitor*>(currentWidget());
+ if (pActivityMonitor)
+ pActivityMonitor->sltExportMetricsToFile();
+}
+
+void UIVMActivityToolWidget::sltCurrentTabChanged(int iIndex)
+{
+ Q_UNUSED(iIndex);
+ UIVMActivityMonitor *pActivityMonitor = qobject_cast<UIVMActivityMonitor*>(currentWidget());
+ if (pActivityMonitor)
+ {
+ CMachine comMachine = uiCommon().virtualBox().FindMachine(pActivityMonitor->machineId().toString());
+ if (!comMachine.isNull())
+ {
+ setExportActionEnabled(comMachine.GetState() == KMachineState_Running);
+ }
+ }
+}
+
+void UIVMActivityToolWidget::setExportActionEnabled(bool fEnabled)
+{
+ if (m_pExportToFileAction)
+ m_pExportToFileAction->setEnabled(fEnabled);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityToolWidget.h b/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityToolWidget.h
new file mode 100644
index 00000000..b1865ea9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/overview/UIVMActivityToolWidget.h
@@ -0,0 +1,119 @@
+/* $Id: UIVMActivityToolWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIVMActivityToolWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_activity_overview_UIVMActivityToolWidget_h
+#define FEQT_INCLUDED_SRC_activity_overview_UIVMActivityToolWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTabWidget>
+#include <QUuid>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class UIActionPool;
+class QIToolBar;
+class UIVirtualMachineItem;
+class CMachine;
+
+/** QTabWidget extension host machine activity widget(s) in the Manager UI. */
+class UIVMActivityToolWidget : public QIWithRetranslateUI<QTabWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigSwitchToActivityOverviewPane();
+
+public:
+
+ UIVMActivityToolWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar = true, QWidget *pParent = 0);
+ QMenu *menu() const;
+
+ bool isCurrentTool() const;
+ void setIsCurrentTool(bool fIsCurrentTool);
+
+ void setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items);
+
+#ifdef VBOX_WS_MAC
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ void sltExportToFile();
+ void sltCurrentTabChanged(int iIndex);
+
+private:
+
+ void setMachines(const QVector<QUuid> &machineIDs);
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ void prepare();
+ void prepareToolBar();
+ void prepareActions();
+ void updateColumnsMenu();
+ void loadSettings();
+ /** @} */
+
+ /** Remove tabs conaining machine monitors with ids @machineIdsToRemove. */
+ void removeTabs(const QVector<QUuid> &machineIdsToRemove);
+ /** Add new tabs for each QUuid in @machineIdsToAdd. Does not check for duplicates. */
+ void addTabs(const QVector<QUuid> &machineIdsToAdd);
+ void setExportActionEnabled(bool fEnabled);
+
+ /** @name General variables.
+ * @{ */
+ const EmbedTo m_enmEmbedding;
+ UIActionPool *m_pActionPool;
+ const bool m_fShowToolbar;
+ /** @} */
+
+ QIToolBar *m_pToolBar;
+
+ /** Indicates if this widget's host tool is current tool. */
+ bool m_fIsCurrentTool;
+ QVector<QUuid> m_machineIds;
+ QAction *m_pExportToFileAction;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_activity_overview_UIVMActivityToolWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/UIVMActivityMonitor.cpp b/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/UIVMActivityMonitor.cpp
new file mode 100644
index 00000000..761e2422
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/UIVMActivityMonitor.cpp
@@ -0,0 +1,1605 @@
+/* $Id: UIVMActivityMonitor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMActivityMonitor class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QDateTime>
+#include <QLabel>
+#include <QMenu>
+#include <QPainter>
+#include <QGridLayout>
+#include <QScrollArea>
+#include <QStyle>
+#include <QXmlStreamReader>
+#include <QTimer>
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "UICommon.h"
+#include "UIIconPool.h"
+#include "UITranslator.h"
+#include "UIVMActivityMonitor.h"
+#include "UIVirtualBoxEventHandler.h"
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CGuest.h"
+#include "CPerformanceCollector.h"
+#include "CPerformanceMetric.h"
+#include <iprt/string.h>
+
+/* External includes: */
+#include <math.h>
+
+/** The time in seconds between metric inquries done to API. */
+const ULONG g_iPeriod = 1;
+/** The number of data points we store in UIChart. with g_iPeriod=1 it corresponds to 2 min. of data. */
+const int g_iMaximumQueueSize = 120;
+/** This is passed to IPerformanceCollector during its setup. When 1 that means IPerformanceCollector object does a data cache of size 1. */
+const int g_iMetricSetupCount = 1;
+const int g_iDecimalCount = 2;
+
+
+/*********************************************************************************************************************************
+* UIChart definition. *
+*********************************************************************************************************************************/
+
+class UIChart : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigExportMetricsToFile();
+
+public:
+
+ UIChart(QWidget *pParent, UIMetric *pMetric);
+ void setFontSize(int iFontSize);
+ int fontSize() const;
+ const QStringList &textList() const;
+
+ bool isPieChartAllowed() const;
+ void setIsPieChartAllowed(bool fWithPieChart);
+
+ bool usePieChart() const;
+ void setShowPieChart(bool fShowPieChart);
+
+ bool useGradientLineColor() const;
+ void setUseGradientLineColor(bool fUseGradintLineColor);
+
+ bool useAreaChart() const;
+ void setUseAreaChart(bool fUseAreaChart);
+
+ bool isAreaChartAllowed() const;
+ void setIsAreaChartAllowed(bool fIsAreaChartAllowed);
+
+ QColor dataSeriesColor(int iDataSeriesIndex, int iDark = 0);
+ void setDataSeriesColor(int iDataSeriesIndex, const QColor &color);
+
+ QString XAxisLabel();
+ void setXAxisLabel(const QString &strLabel);
+
+ bool isAvailable() const;
+ void setIsAvailable(bool fIsAvailable);
+
+ void setMouseOver(bool isOver);
+
+protected:
+
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+ virtual void mouseMoveEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+ virtual QSize minimumSizeHint() const RT_OVERRIDE;
+ virtual QSize sizeHint() const RT_OVERRIDE;
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ void sltCreateContextMenu(const QPoint &point);
+ void sltResetMetric();
+ void sltSetShowPieChart(bool fShowPieChart);
+ void sltSetUseAreaChart(bool fUseAreaChart);
+
+private:
+
+ /** @name Drawing helper functions.
+ * @{ */
+ void drawXAxisLabels(QPainter &painter, int iXSubAxisCount);
+ void drawPieChart(QPainter &painter, quint64 iMaximum, int iDataIndex, const QRectF &chartRect, bool fWithBorder = true);
+ void drawCombinedPieCharts(QPainter &painter, quint64 iMaximum);
+ QString YAxisValueLabel(quint64 iValue) const;
+ /** Drawing an overlay rectangle over the charts to indicate that they are disabled. */
+ void drawDisabledChartRectangle(QPainter &painter);
+ QConicalGradient conicalGradientForDataSeries(const QRectF &rectangle, int iDataIndex);
+ /** @} */
+
+ UIMetric *m_pMetric;
+ QSize m_size;
+ QFont m_axisFont;
+ int m_iMarginLeft;
+ int m_iMarginRight;
+ int m_iMarginTop;
+ int m_iMarginBottom;
+ int m_iOverlayAlpha;
+ QRect m_lineChartRect;
+ int m_iPieChartRadius;
+ int m_iPieChartSpacing;
+ float m_fPixelPerDataPoint;
+ /** is set to -1 if mouse cursor is not over a data point*/
+ int m_iDataIndexUnderCursor;
+ /** For some chart it is not possible to have a pie chart, Then We dont present the
+ * option to show it to user. see m_fIsPieChartAllowed. */
+ bool m_fIsPieChartAllowed;
+ /** m_fShowPieChart is considered only if m_fIsPieChartAllowed is true. */
+ bool m_fShowPieChart;
+ bool m_fUseGradientLineColor;
+ /** When it is true we draw an area graph where data series drawn on top of each other.
+ * We draw first data0 then data 1 on top. Makes sense where the summation of data is guaranteed not to exceed some max. */
+ bool m_fUseAreaChart;
+ /** False if the chart is not useable for some reason. For example it depends guest additions and they are not installed. */
+ bool m_fIsAvailable;
+ /** For some charts it does not make sense to have an area chart. */
+ bool m_fIsAreaChartAllowed;
+ QColor m_dataSeriesColor[DATA_SERIES_SIZE];
+ QString m_strXAxisLabel;
+ QString m_strGAWarning;
+ QString m_strResetActionLabel;
+ QString m_strPieChartToggleActionLabel;
+ QString m_strAreaChartToggleActionLabel;
+ bool m_fDrawCurenValueIndicators;
+ /** The width of the right margin in characters. */
+ int m_iRightMarginCharWidth;
+};
+
+/*********************************************************************************************************************************
+* UIChart implementation. *
+*********************************************************************************************************************************/
+
+UIChart::UIChart(QWidget *pParent, UIMetric *pMetric)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_pMetric(pMetric)
+ , m_size(QSize(50, 50))
+ , m_iOverlayAlpha(80)
+ , m_fPixelPerDataPoint(0.f)
+ , m_iDataIndexUnderCursor(-1)
+ , m_fIsPieChartAllowed(false)
+ , m_fShowPieChart(true)
+ , m_fUseGradientLineColor(false)
+ , m_fUseAreaChart(true)
+ , m_fIsAvailable(true)
+ , m_fIsAreaChartAllowed(false)
+ , m_fDrawCurenValueIndicators(false)
+ , m_iRightMarginCharWidth(10)
+{
+ m_axisFont = font();
+ m_axisFont.setPixelSize(14);
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ setMouseTracking(true);
+ connect(this, &UIChart::customContextMenuRequested,
+ this, &UIChart::sltCreateContextMenu);
+
+ m_dataSeriesColor[0] = QColor(200, 0, 0, 255);
+ m_dataSeriesColor[1] = QColor(0, 0, 200, 255);
+
+ m_iMarginLeft = 3 * QFontMetricsF(m_axisFont).averageCharWidth();
+ m_iMarginRight = m_iRightMarginCharWidth * QFontMetricsF(m_axisFont).averageCharWidth();
+ m_iMarginTop = 0.3 * qApp->QApplication::style()->pixelMetric(QStyle::PM_LayoutTopMargin);
+ m_iMarginBottom = QFontMetrics(m_axisFont).height();
+
+ float fAppIconSize = qApp->style()->pixelMetric(QStyle::PM_LargeIconSize);
+ m_size = QSize(14 * fAppIconSize, 3.5 * fAppIconSize);
+ m_iPieChartSpacing = 2;
+ m_iPieChartRadius = m_size.height() - (m_iMarginTop + m_iMarginBottom + 2 * m_iPieChartSpacing);
+
+ retranslateUi();
+}
+
+bool UIChart::isPieChartAllowed() const
+{
+ return m_fIsPieChartAllowed;
+}
+
+void UIChart::setIsPieChartAllowed(bool fWithPieChart)
+{
+ if (m_fIsPieChartAllowed == fWithPieChart)
+ return;
+ m_fIsPieChartAllowed = fWithPieChart;
+ update();
+}
+
+bool UIChart::usePieChart() const
+{
+ return m_fShowPieChart;
+}
+
+void UIChart::setShowPieChart(bool fDrawChart)
+{
+ if (m_fShowPieChart == fDrawChart)
+ return;
+ m_fShowPieChart = fDrawChart;
+ update();
+}
+
+bool UIChart::useGradientLineColor() const
+{
+ return m_fUseGradientLineColor;
+}
+
+void UIChart::setUseGradientLineColor(bool fUseGradintLineColor)
+{
+ if (m_fUseGradientLineColor == fUseGradintLineColor)
+ return;
+ m_fUseGradientLineColor = fUseGradintLineColor;
+ update();
+}
+
+bool UIChart::useAreaChart() const
+{
+ return m_fUseAreaChart;
+}
+
+void UIChart::setUseAreaChart(bool fUseAreaChart)
+{
+ if (m_fUseAreaChart == fUseAreaChart)
+ return;
+ m_fUseAreaChart = fUseAreaChart;
+ update();
+}
+
+bool UIChart::isAreaChartAllowed() const
+{
+ return m_fIsAreaChartAllowed;
+}
+
+void UIChart::setIsAreaChartAllowed(bool fIsAreaChartAllowed)
+{
+ m_fIsAreaChartAllowed = fIsAreaChartAllowed;
+}
+
+QColor UIChart::dataSeriesColor(int iDataSeriesIndex, int iDark /* = 0 */)
+{
+ if (iDataSeriesIndex >= DATA_SERIES_SIZE)
+ return QColor();
+ return QColor(qMax(m_dataSeriesColor[iDataSeriesIndex].red() - iDark, 0),
+ qMax(m_dataSeriesColor[iDataSeriesIndex].green() - iDark, 0),
+ qMax(m_dataSeriesColor[iDataSeriesIndex].blue() - iDark, 0),
+ m_dataSeriesColor[iDataSeriesIndex].alpha());
+}
+
+void UIChart::setDataSeriesColor(int iDataSeriesIndex, const QColor &color)
+{
+ if (iDataSeriesIndex >= DATA_SERIES_SIZE)
+ return;
+ if (m_dataSeriesColor[iDataSeriesIndex] == color)
+ return;
+ m_dataSeriesColor[iDataSeriesIndex] = color;
+ update();
+}
+
+QString UIChart::XAxisLabel()
+{
+ return m_strXAxisLabel;
+}
+
+void UIChart::setXAxisLabel(const QString &strLabel)
+{
+ m_strXAxisLabel = strLabel;
+}
+
+bool UIChart::isAvailable() const
+{
+ return m_fIsAvailable;
+}
+
+void UIChart::setIsAvailable(bool fIsAvailable)
+{
+ if (m_fIsAvailable == fIsAvailable)
+ return;
+ m_fIsAvailable = fIsAvailable;
+ update();
+}
+
+void UIChart::setMouseOver(bool isOver)
+{
+ if (!isOver)
+ m_iDataIndexUnderCursor = -1;
+}
+
+QSize UIChart::minimumSizeHint() const
+{
+ return m_size;
+}
+
+QSize UIChart::sizeHint() const
+{
+ return m_size;
+}
+
+void UIChart::retranslateUi()
+{
+ m_strGAWarning = QApplication::translate("UIVMInformationDialog", "This metric requires guest additions to work.");
+ m_strResetActionLabel = QApplication::translate("UIVMInformationDialog", "Reset");
+ m_strPieChartToggleActionLabel = QApplication::translate("UIVMInformationDialog", "Show Pie Chart");
+ m_strAreaChartToggleActionLabel = QApplication::translate("UIVMInformationDialog", "Draw Area Chart");
+ update();
+}
+
+void UIChart::resizeEvent(QResizeEvent *pEvent)
+{
+ int iWidth = width() - m_iMarginLeft - m_iMarginRight;
+ if (g_iMaximumQueueSize > 0)
+ m_fPixelPerDataPoint = iWidth / (float)g_iMaximumQueueSize;
+ QIWithRetranslateUI<QWidget>::resizeEvent(pEvent);
+}
+
+void UIChart::mouseMoveEvent(QMouseEvent *pEvent)
+{
+ int iX = width() - pEvent->x() - m_iMarginRight;
+ m_iDataIndexUnderCursor = -1;
+ if (iX > m_iMarginLeft && iX <= width() - m_iMarginRight)
+ m_iDataIndexUnderCursor = (int)((iX) / m_fPixelPerDataPoint) + 1;
+ update();
+ QIWithRetranslateUI<QWidget>::mouseMoveEvent(pEvent);
+}
+
+void UIChart::paintEvent(QPaintEvent *pEvent)
+{
+ Q_UNUSED(pEvent);
+ if (!m_pMetric || g_iMaximumQueueSize <= 1)
+ return;
+
+ QPainter painter(this);
+ painter.setFont(m_axisFont);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ /* Draw a rectanglar grid over which we will draw the line graphs: */
+ QPoint chartTopLeft(m_iMarginLeft, m_iMarginTop);
+ QSize chartSize(width() - (m_iMarginLeft + m_iMarginRight), height() - (m_iMarginTop + m_iMarginBottom));
+
+ m_lineChartRect = QRect(chartTopLeft, chartSize);
+ QColor mainAxisColor(120, 120, 120);
+ QColor subAxisColor(200, 200, 200);
+ /* Draw the main axes: */
+ painter.setPen(mainAxisColor);
+ painter.drawRect(m_lineChartRect);
+
+ /* draw Y subaxes: */
+ painter.setPen(subAxisColor);
+ int iYSubAxisCount = 3;
+ for (int i = 0; i < iYSubAxisCount; ++i)
+ {
+ float fSubAxisY = m_iMarginTop + (i + 1) * m_lineChartRect.height() / (float) (iYSubAxisCount + 1);
+ painter.drawLine(m_lineChartRect.left(), fSubAxisY,
+ m_lineChartRect.right(), fSubAxisY);
+ }
+
+ /* draw X subaxes: */
+ int iXSubAxisCount = 5;
+ for (int i = 0; i < iXSubAxisCount; ++i)
+ {
+ float fSubAxisX = m_lineChartRect.left() + (i + 1) * m_lineChartRect.width() / (float) (iXSubAxisCount + 1);
+ painter.drawLine(fSubAxisX, m_lineChartRect.top(), fSubAxisX, m_lineChartRect.bottom());
+ }
+
+ /* Draw XAxis tick labels: */
+ painter.setPen(mainAxisColor);
+ drawXAxisLabels(painter, iXSubAxisCount);
+
+ if (!isEnabled())
+ return;
+
+ /* Draw a half-transparent rectangle over the whole widget to indicate the it is not available: */
+ if (!isAvailable())
+ {
+ drawDisabledChartRectangle(painter);
+ return;
+ }
+
+ quint64 iMaximum = m_pMetric->maximum();
+ QFontMetrics fontMetrics(painter.font());
+ int iFontHeight = fontMetrics.height();
+ int iAverageFontWidth = fontMetrics.averageCharWidth();
+
+ /* Draw a straight line per data series: */
+ if (iMaximum == 0)
+ {
+ for (int k = 0; k < DATA_SERIES_SIZE; ++k)
+ {
+ QLineF bar(0 + m_iMarginLeft, height() - m_iMarginBottom,
+ width() - m_iMarginRight, height() - m_iMarginBottom);
+ painter.drawLine(bar);
+ painter.setPen(QPen(m_dataSeriesColor[k], 2.5));
+ painter.setBrush(m_dataSeriesColor[k]);
+ }
+ }
+ else
+ {
+ /* Draw the data lines: */
+ float fBarWidth = m_lineChartRect.width() / (float) (g_iMaximumQueueSize - 1);
+ float fH = m_lineChartRect.height() / (float)iMaximum;
+ for (int k = 0; k < DATA_SERIES_SIZE; ++k)
+ {
+ if (m_fUseGradientLineColor)
+ {
+ QLinearGradient gradient(0, 0, 0, m_lineChartRect.height());
+ gradient.setColorAt(0, Qt::black);
+ gradient.setColorAt(1, m_dataSeriesColor[k]);
+ painter.setPen(QPen(gradient, 2.5));
+ }
+ const QQueue<quint64> *data = m_pMetric->data(k);
+ if (!m_fUseGradientLineColor)
+ painter.setPen(QPen(m_dataSeriesColor[k], 2.5));
+ if (m_fUseAreaChart && m_fIsAreaChartAllowed)
+ {
+ QVector<QPointF> points;
+ for (int i = 0; i < data->size(); ++i)
+ {
+ float fHeight = fH * data->at(i);
+ if (k == 0)
+ {
+ if (m_pMetric->data(1) && m_pMetric->data(1)->size() > i)
+ fHeight += fH * m_pMetric->data(1)->at(i);
+ }
+ float fX = (width() - m_iMarginRight) - ((data->size() - i - 1) * fBarWidth);
+ if (i == 0)
+ points << QPointF(fX, height() - m_iMarginBottom);
+ points << QPointF(fX, height() - (fHeight + m_iMarginBottom));
+ if (i == data->size() - 1)
+ points << QPointF(fX, height() - + m_iMarginBottom);
+ }
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(m_dataSeriesColor[k]);
+ painter.drawPolygon(points, Qt::WindingFill);
+ }
+ else
+ {
+ for (int i = 0; i < data->size() - 1; ++i)
+ {
+ int j = i + 1;
+ float fHeight = fH * data->at(i);
+ float fX = (width() - m_iMarginRight) - ((data->size() -i - 1) * fBarWidth);
+ float fHeight2 = fH * data->at(j);
+ float fX2 = (width() - m_iMarginRight) - ((data->size() -j - 1) * fBarWidth);
+ QLineF bar(fX, height() - (fHeight + m_iMarginBottom), fX2, height() - (fHeight2 + m_iMarginBottom));
+ painter.drawLine(bar);
+ }
+ }
+ /* Draw a horizontal and vertical line on data point under the mouse cursor
+ * and draw the value on the left hand side of the chart: */
+ if (m_fDrawCurenValueIndicators && m_iDataIndexUnderCursor >= 0 && m_iDataIndexUnderCursor < data->size())
+ {
+ painter.setPen(QPen(m_dataSeriesColor[k], 0.5));
+ float fHeight = fH * data->at(data->size() - m_iDataIndexUnderCursor);
+ if (fHeight > 0)
+ {
+ painter.drawLine(m_iMarginLeft, height() - (fHeight + m_iMarginBottom),
+ width() - m_iMarginRight, height() - (fHeight + m_iMarginBottom));
+ QPoint cursorPosition = mapFromGlobal(cursor().pos());
+ painter.setPen(mainAxisColor);
+ painter.drawLine(cursorPosition.x(), 0,
+ cursorPosition.x(), height() - m_iMarginBottom);
+ QString strValue = QString::number(data->at(data->size() - m_iDataIndexUnderCursor));
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ painter.drawText(m_iMarginLeft - fontMetrics.horizontalAdvance(strValue) - iAverageFontWidth,
+ height() - (fHeight + m_iMarginBottom) + 0.5 * iFontHeight, strValue);
+#else
+ painter.drawText(m_iMarginLeft - fontMetrics.width(strValue) - iAverageFontWidth,
+ height() - (fHeight + m_iMarginBottom) + 0.5 * iFontHeight, strValue);
+#endif
+
+ }
+ }
+ }
+ }// else of if (iMaximum == 0)
+
+ /* Draw YAxis tick labels: */
+ painter.setPen(mainAxisColor);
+ for (int i = iYSubAxisCount + 1; i >= 0; --i)
+ {
+ /* Draw the bottom most label and skip others when data maximum is 0: */
+ if (iMaximum == 0 && i <= iYSubAxisCount)
+ break;
+ int iTextY = 0.5 * iFontHeight + m_iMarginTop + i * m_lineChartRect.height() / (float) (iYSubAxisCount + 1);
+ quint64 iValue = (iYSubAxisCount + 1 - i) * (iMaximum / (float) (iYSubAxisCount + 1));
+ QString strValue = YAxisValueLabel(iValue);
+ /* Leave space of one character between the text and chart rectangle: */
+ painter.drawText(width() - (m_iRightMarginCharWidth - 1) * QFontMetricsF(m_axisFont).averageCharWidth(), iTextY, strValue);
+ }
+
+ if (iMaximum != 0 && m_fIsPieChartAllowed && m_fShowPieChart)
+ drawCombinedPieCharts(painter, iMaximum);
+}
+
+QString UIChart::YAxisValueLabel(quint64 iValue) const
+{
+ if (m_pMetric->unit().compare("%", Qt::CaseInsensitive) == 0)
+ return QString::number(iValue);
+ if (m_pMetric->unit().compare("kb", Qt::CaseInsensitive) == 0)
+ return UITranslator::formatSize(_1K * (quint64)iValue, g_iDecimalCount);
+ if ( m_pMetric->unit().compare("b", Qt::CaseInsensitive) == 0
+ || m_pMetric->unit().compare("b/s", Qt::CaseInsensitive) == 0)
+ return UITranslator::formatSize(iValue, g_iDecimalCount);
+ if (m_pMetric->unit().compare("times", Qt::CaseInsensitive) == 0)
+ return UITranslator::addMetricSuffixToNumber(iValue);
+ return QString();
+}
+
+
+void UIChart::drawXAxisLabels(QPainter &painter, int iXSubAxisCount)
+{
+ QFont painterFont = painter.font();
+ QFontMetrics fontMetrics(painter.font());
+ int iFontHeight = fontMetrics.height();
+
+ int iTotalSeconds = g_iPeriod * g_iMaximumQueueSize;
+ for (int i = 0; i < iXSubAxisCount + 2; ++i)
+ {
+ int iTextX = m_lineChartRect.left() + i * m_lineChartRect.width() / (float) (iXSubAxisCount + 1);
+ QString strCurrentSec = QString::number(iTotalSeconds - i * iTotalSeconds / (float)(iXSubAxisCount + 1));
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iTextWidth = fontMetrics.horizontalAdvance(strCurrentSec);
+#else
+ int iTextWidth = fontMetrics.width(strCurrentSec);
+#endif
+ if (i == 0)
+ {
+ strCurrentSec += " " + m_strXAxisLabel;
+ painter.drawText(iTextX, m_lineChartRect.bottom() + iFontHeight, strCurrentSec);
+ }
+ else
+ painter.drawText(iTextX - 0.5 * iTextWidth, m_lineChartRect.bottom() + iFontHeight, strCurrentSec);
+ }
+}
+
+void UIChart::drawPieChart(QPainter &painter, quint64 iMaximum, int iDataIndex,
+ const QRectF &chartRect, bool fWithBorder /* = false */)
+{
+ if (!m_pMetric)
+ return;
+
+ const QQueue<quint64> *data = m_pMetric->data(iDataIndex);
+ if (!data || data->isEmpty())
+ return;
+
+ /* Draw a whole non-filled circle: */
+ if (fWithBorder)
+ {
+ painter.setPen(QPen(QColor(100, 100, 100, m_iOverlayAlpha), 1));
+ painter.drawArc(chartRect, 0, 3600 * 16);
+ painter.setPen(Qt::NoPen);
+ }
+
+ /* Draw a white filled circle and that the arc for data: */
+ QPainterPath background = UIMonitorCommon::wholeArc(chartRect);
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(QColor(255, 255, 255, m_iOverlayAlpha));
+ painter.drawPath(background);
+
+ float fAngle = 360.f * data->back() / (float)iMaximum;
+
+ QPainterPath dataPath;
+ dataPath.moveTo(chartRect.center());
+ dataPath.arcTo(chartRect, 90.f/*startAngle*/,
+ -1.f * fAngle /*sweepLength*/);
+ painter.setBrush(conicalGradientForDataSeries(chartRect, iDataIndex));
+ painter.drawPath(dataPath);
+}
+
+QConicalGradient UIChart::conicalGradientForDataSeries(const QRectF &rectangle, int iDataIndex)
+{
+ QConicalGradient gradient;
+ gradient.setCenter(rectangle.center());
+ gradient.setAngle(90);
+ gradient.setColorAt(0, QColor(0, 0, 0, m_iOverlayAlpha));
+ QColor pieColor(m_dataSeriesColor[iDataIndex]);
+ pieColor.setAlpha(m_iOverlayAlpha);
+ gradient.setColorAt(1, pieColor);
+ return gradient;
+}
+
+void UIChart::drawCombinedPieCharts(QPainter &painter, quint64 iMaximum)
+{
+ if (!m_pMetric)
+ return;
+
+ QRectF chartRect(QPointF(m_iPieChartSpacing + m_iMarginLeft, m_iPieChartSpacing + m_iMarginTop),
+ QSizeF(m_iPieChartRadius, m_iPieChartRadius));
+
+ bool fData0 = m_pMetric->data(0) && !m_pMetric->data(0)->isEmpty();
+ bool fData1 = m_pMetric->data(0) && !m_pMetric->data(1)->isEmpty();
+
+ if (fData0 && fData1)
+ {
+ /* Draw a doughnut chart where data series are stacked on to of each other: */
+ if (m_pMetric->data(0) && !m_pMetric->data(0)->isEmpty() &&
+ m_pMetric->data(1) && !m_pMetric->data(1)->isEmpty())
+ UIMonitorCommon::drawCombinedDoughnutChart(m_pMetric->data(1)->back(), dataSeriesColor(1, 50),
+ m_pMetric->data(0)->back(), dataSeriesColor(0, 50),
+ painter, iMaximum, chartRect,
+ UIMonitorCommon::getScaledRect(chartRect, 0.5f, 0.5f), m_iOverlayAlpha);
+#if 0
+ /* Draw a doughnut shaped chart and then pie chart inside it: */
+ UIMonitorCommon::drawDoughnutChart(painter, iMaximum, m_pMetric->data(0)->back(),
+ chartRect, innerRect, m_iOverlayAlpha, dataSeriesColor(0));
+ drawPieChart(painter, iMaximum, 1 /* iDataIndex */, innerRect, false);
+#endif
+ }
+ else if (fData0 && !fData1)
+ drawPieChart(painter, iMaximum, 0 /* iDataIndex */, chartRect);
+ else if (!fData0 && fData1)
+ drawPieChart(painter, iMaximum, 1 /* iDataIndex */, chartRect);
+}
+
+void UIChart::drawDisabledChartRectangle(QPainter &painter)
+{
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(QColor(255, 255, 255, 150));
+ painter.drawRect(m_lineChartRect);
+ painter.setPen(QColor(20, 20, 20, 180));
+ QFont font = painter.font();
+ int iFontSize = 64;
+ do {
+ font.setPixelSize(iFontSize);
+ --iFontSize;
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ } while (QFontMetrics(font).horizontalAdvance(m_strGAWarning) >= 0.8 * m_lineChartRect.width());
+#else
+ } while (QFontMetrics(font).width(m_strGAWarning) >= 0.8 * m_lineChartRect.width());
+#endif
+ font.setBold(true);
+ painter.setFont(font);
+ painter.drawText(m_lineChartRect, m_strGAWarning);
+}
+
+void UIChart::sltCreateContextMenu(const QPoint &point)
+{
+ QMenu menu;
+ QAction *pExportAction =
+ menu.addAction(QApplication::translate("UIVMInformationDialog", "Export"));
+ pExportAction->setIcon(UIIconPool::iconSet(":/performance_monitor_export_16px.png"));
+ connect(pExportAction, &QAction::triggered, this, &UIChart::sigExportMetricsToFile);
+ menu.addSeparator();
+ QAction *pResetAction = menu.addAction(m_strResetActionLabel);
+ connect(pResetAction, &QAction::triggered, this, &UIChart::sltResetMetric);
+ if (m_fIsPieChartAllowed)
+ {
+ QAction *pPieChartToggle = menu.addAction(m_strPieChartToggleActionLabel);
+ pPieChartToggle->setCheckable(true);
+ pPieChartToggle->setChecked(m_fShowPieChart);
+ connect(pPieChartToggle, &QAction::toggled, this, &UIChart::sltSetShowPieChart);
+ }
+ if (m_fIsAreaChartAllowed)
+ {
+ QAction *pAreaChartToggle = menu.addAction(m_strAreaChartToggleActionLabel);
+ pAreaChartToggle->setCheckable(true);
+ pAreaChartToggle->setChecked(m_fUseAreaChart);
+ connect(pAreaChartToggle, &QAction::toggled, this, &UIChart::sltSetUseAreaChart);
+ }
+
+ menu.exec(mapToGlobal(point));
+}
+
+void UIChart::sltResetMetric()
+{
+ if (m_pMetric)
+ m_pMetric->reset();
+}
+
+void UIChart::sltSetShowPieChart(bool fShowPieChart)
+{
+ setShowPieChart(fShowPieChart);
+}
+
+void UIChart::sltSetUseAreaChart(bool fUseAreaChart)
+{
+ setUseAreaChart(fUseAreaChart);
+}
+
+
+/*********************************************************************************************************************************
+* UIMetric implementation. *
+*********************************************************************************************************************************/
+
+UIMetric::UIMetric(const QString &strName, const QString &strUnit, int iMaximumQueueSize)
+ : m_strName(strName)
+ , m_strUnit(strUnit)
+ , m_iMaximum(0)
+#if 0 /* Unused according to Clang 11. */
+ , m_iMaximumQueueSize(iMaximumQueueSize) /** @todo r=bird: m_iMaximumQueueSize is not used anywhere that I can see. */
+#endif
+ , m_fRequiresGuestAdditions(false)
+ , m_fIsInitialized(false)
+ , m_fAutoUpdateMaximum(false)
+{
+ RT_NOREF(iMaximumQueueSize); /* Unused according to Clang 11. */
+ m_iTotal[0] = 0;
+ m_iTotal[1] = 0;
+}
+
+UIMetric::UIMetric()
+ : m_iMaximum(0)
+#if 0 /* Unused according to Clang 11. */
+ , m_iMaximumQueueSize(0)
+#endif
+ , m_fRequiresGuestAdditions(false)
+ , m_fIsInitialized(false)
+{
+}
+
+const QString &UIMetric::name() const
+{
+ return m_strName;
+}
+
+void UIMetric::setMaximum(quint64 iMaximum)
+{
+ m_iMaximum = iMaximum;
+}
+
+quint64 UIMetric::maximum() const
+{
+ return m_iMaximum;
+}
+
+void UIMetric::setUnit(QString strUnit)
+{
+ m_strUnit = strUnit;
+
+}
+const QString &UIMetric::unit() const
+{
+ return m_strUnit;
+}
+
+void UIMetric::addData(int iDataSeriesIndex, quint64 iData)
+{
+ if (iDataSeriesIndex >= DATA_SERIES_SIZE)
+ return;
+ m_data[iDataSeriesIndex].enqueue(iData);
+ if (m_fAutoUpdateMaximum)
+ m_iMaximum = qMax(m_iMaximum, iData);
+
+ if (m_data[iDataSeriesIndex].size() > g_iMaximumQueueSize)
+ {
+ bool fSearchMax = false;
+ /* Check if the dequeued value is the Max value. In which case we will scan and find a new Max: */
+ if (m_fAutoUpdateMaximum && m_data[iDataSeriesIndex].head() >= m_iMaximum)
+ fSearchMax = true;
+ m_data[iDataSeriesIndex].dequeue();
+ if (fSearchMax)
+ {
+ m_iMaximum = 0;
+ foreach (quint64 iVal, m_data[iDataSeriesIndex])
+ m_iMaximum = qMax(m_iMaximum, iVal);
+ }
+ }
+}
+
+const QQueue<quint64> *UIMetric::data(int iDataSeriesIndex) const
+{
+ if (iDataSeriesIndex >= DATA_SERIES_SIZE)
+ return 0;
+ return &m_data[iDataSeriesIndex];
+}
+
+int UIMetric::dataSize(int iDataSeriesIndex) const
+{
+ if (iDataSeriesIndex >= DATA_SERIES_SIZE)
+ return 0;
+ return m_data[iDataSeriesIndex].size();
+}
+
+void UIMetric::setDataSeriesName(int iDataSeriesIndex, const QString &strName)
+{
+ if (iDataSeriesIndex >= DATA_SERIES_SIZE)
+ return;
+ m_strDataSeriesName[iDataSeriesIndex] = strName;
+}
+
+QString UIMetric::dataSeriesName(int iDataSeriesIndex) const
+{
+ if (iDataSeriesIndex >= DATA_SERIES_SIZE)
+ return QString();
+ return m_strDataSeriesName[iDataSeriesIndex];
+}
+
+void UIMetric::setTotal(int iDataSeriesIndex, quint64 iTotal)
+{
+ if (iDataSeriesIndex >= DATA_SERIES_SIZE)
+ return;
+ m_iTotal[iDataSeriesIndex] = iTotal;
+}
+
+quint64 UIMetric::total(int iDataSeriesIndex) const
+{
+ if (iDataSeriesIndex >= DATA_SERIES_SIZE)
+ return 0;
+ return m_iTotal[iDataSeriesIndex];
+}
+
+bool UIMetric::requiresGuestAdditions() const
+{
+ return m_fRequiresGuestAdditions;
+}
+
+void UIMetric::setRequiresGuestAdditions(bool fRequiresGAs)
+{
+ m_fRequiresGuestAdditions = fRequiresGAs;
+}
+
+bool UIMetric::isInitialized() const
+{
+ return m_fIsInitialized;
+}
+
+void UIMetric::setIsInitialized(bool fIsInitialized)
+{
+ m_fIsInitialized = fIsInitialized;
+}
+
+void UIMetric::reset()
+{
+ m_fIsInitialized = false;
+ for (int i = 0; i < DATA_SERIES_SIZE; ++i)
+ {
+ m_iTotal[i] = 0;
+ m_data[i].clear();
+ }
+ m_iMaximum = 0;
+}
+
+void UIMetric::toFile(QTextStream &stream) const
+{
+ stream << "Metric Name: " << m_strName << "\n";
+ stream << "Unit: " << m_strUnit << "\n";
+ stream << "Maximum: " << m_iMaximum << "\n";
+ for (int i = 0; i < 2; ++i)
+ {
+ if (!m_data[i].isEmpty())
+ {
+ stream << "Data Series: " << m_strDataSeriesName[i] << "\n";
+ foreach (const quint64& data, m_data[i])
+ stream << data << " ";
+ stream << "\n";
+ }
+ }
+ stream << "\n";
+}
+
+void UIMetric::setAutoUpdateMaximum(bool fAuto)
+{
+ m_fAutoUpdateMaximum = fAuto;
+}
+
+bool UIMetric::autoUpdateMaximum() const
+{
+ return m_fAutoUpdateMaximum;
+}
+
+/*********************************************************************************************************************************
+* UIVMActivityMonitor implementation. *
+*********************************************************************************************************************************/
+
+UIVMActivityMonitor::UIVMActivityMonitor(EmbedTo enmEmbedding, QWidget *pParent,
+ const CMachine &machine)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fGuestAdditionsAvailable(false)
+ , m_pMainLayout(0)
+ , m_pTimer(0)
+ , m_strCPUMetricName("CPU Load")
+ , m_strRAMMetricName("RAM Usage")
+ , m_strDiskMetricName("Disk Usage")
+ , m_strNetworkMetricName("Network")
+ , m_strDiskIOMetricName("DiskIO")
+ , m_strVMExitMetricName("VMExits")
+ , m_iTimeStep(0)
+ , m_enmEmbedding(enmEmbedding)
+{
+ prepareMetrics();
+ prepareWidgets();
+ prepareActions();
+ retranslateUi();
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange, this, &UIVMActivityMonitor::sltMachineStateChange);
+ setMachine(machine);
+ uiCommon().setHelpKeyword(this, "vm-session-information");
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(this, &UIVMActivityMonitor::customContextMenuRequested,
+ this, &UIVMActivityMonitor::sltCreateContextMenu);
+ connect(&uiCommon(), &UICommon::sigAskToDetachCOM,
+ this, &UIVMActivityMonitor::sltClearCOMData);
+}
+
+UIVMActivityMonitor::~UIVMActivityMonitor()
+{
+ sltClearCOMData();
+}
+
+void UIVMActivityMonitor::setMachine(const CMachine &comMachine)
+{
+ reset();
+ if (comMachine.isNull())
+ return;
+
+ if (!m_comSession.isNull())
+ m_comSession.UnlockMachine();
+
+ m_comMachine = comMachine;
+
+ if (m_comMachine.GetState() == KMachineState_Running)
+ {
+ setEnabled(true);
+ openSession();
+ start();
+ }
+}
+
+QUuid UIVMActivityMonitor::machineId() const
+{
+ if (m_comMachine.isNull())
+ return QUuid();
+ return m_comMachine.GetId();
+}
+
+QString UIVMActivityMonitor::machineName() const
+{
+ if (m_comMachine.isNull())
+ return QString();
+ return m_comMachine.GetName();
+}
+
+void UIVMActivityMonitor::openSession()
+{
+ if (!m_comSession.isNull())
+ return;
+ m_comSession = uiCommon().openSession(m_comMachine.GetId(), KLockType_Shared);
+ AssertReturnVoid(!m_comSession.isNull());
+
+ CConsole comConsole = m_comSession.GetConsole();
+ AssertReturnVoid(!comConsole.isNull());
+ m_comGuest = comConsole.GetGuest();
+
+ m_comMachineDebugger = comConsole.GetDebugger();
+}
+
+void UIVMActivityMonitor::retranslateUi()
+{
+ foreach (UIChart *pChart, m_charts)
+ pChart->setXAxisLabel(QApplication::translate("UIVMInformationDialog", "Sec."));
+
+ /* Translate the chart info labels: */
+ int iMaximum = 0;
+ m_strCPUInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "CPU Load");
+ iMaximum = qMax(iMaximum, m_strCPUInfoLabelTitle.length());
+ m_strCPUInfoLabelGuest = QApplication::translate("UIVMInformationDialog", "Guest Load");
+ iMaximum = qMax(iMaximum, m_strCPUInfoLabelGuest.length());
+ m_strCPUInfoLabelVMM = QApplication::translate("UIVMInformationDialog", "VMM Load");
+ iMaximum = qMax(iMaximum, m_strCPUInfoLabelVMM.length());
+ m_strRAMInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "RAM Usage");
+ iMaximum = qMax(iMaximum, m_strRAMInfoLabelTitle.length());
+ m_strRAMInfoLabelTotal = QApplication::translate("UIVMInformationDialog", "Total");
+ iMaximum = qMax(iMaximum, m_strRAMInfoLabelTotal.length());
+ m_strRAMInfoLabelFree = QApplication::translate("UIVMInformationDialog", "Free");
+ iMaximum = qMax(iMaximum, m_strRAMInfoLabelFree.length());
+ m_strRAMInfoLabelUsed = QApplication::translate("UIVMInformationDialog", "Used");
+ iMaximum = qMax(iMaximum, m_strRAMInfoLabelUsed.length());
+ m_strNetworkInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "Network Rate");
+ iMaximum = qMax(iMaximum, m_strNetworkInfoLabelTitle.length());
+ m_strNetworkInfoLabelReceived = QApplication::translate("UIVMInformationDialog", "Receive Rate");
+ iMaximum = qMax(iMaximum, m_strNetworkInfoLabelReceived.length());
+ m_strNetworkInfoLabelTransmitted = QApplication::translate("UIVMInformationDialog", "Transmit Rate");
+ iMaximum = qMax(iMaximum, m_strNetworkInfoLabelTransmitted.length());
+ m_strNetworkInfoLabelReceivedTotal = QApplication::translate("UIVMInformationDialog", "Total Received");
+ iMaximum = qMax(iMaximum, m_strNetworkInfoLabelReceivedTotal.length());
+ m_strNetworkInfoLabelTransmittedTotal = QApplication::translate("UIVMInformationDialog", "Total Transmitted");
+ iMaximum = qMax(iMaximum, m_strNetworkInfoLabelReceivedTotal.length());
+ m_strDiskIOInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "Disk IO Rate");
+ iMaximum = qMax(iMaximum, m_strDiskIOInfoLabelTitle.length());
+ m_strDiskIOInfoLabelWritten = QApplication::translate("UIVMInformationDialog", "Write Rate");
+ iMaximum = qMax(iMaximum, m_strDiskIOInfoLabelWritten.length());
+ m_strDiskIOInfoLabelRead = QApplication::translate("UIVMInformationDialog", "Read Rate");
+ iMaximum = qMax(iMaximum, m_strDiskIOInfoLabelRead.length());
+ m_strDiskIOInfoLabelWrittenTotal = QApplication::translate("UIVMInformationDialog", "Total Written");
+ iMaximum = qMax(iMaximum, m_strDiskIOInfoLabelWrittenTotal.length());
+ m_strDiskIOInfoLabelReadTotal = QApplication::translate("UIVMInformationDialog", "Total Read");
+ iMaximum = qMax(iMaximum, m_strDiskIOInfoLabelReadTotal.length());
+ m_strVMExitInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "VM Exits");
+ iMaximum = qMax(iMaximum, m_strVMExitInfoLabelTitle.length());
+ m_strVMExitLabelCurrent = QApplication::translate("UIVMInformationDialog", "Current");
+ iMaximum = qMax(iMaximum, m_strVMExitLabelCurrent.length());
+ m_strVMExitLabelTotal = QApplication::translate("UIVMInformationDialog", "Total");
+ iMaximum = qMax(iMaximum, m_strVMExitLabelTotal.length());
+
+ /* Compute the maximum label string length and set it as a fixed width to labels to prevent always changing widths: */
+ /* Add m_iDecimalCount plus 4 characters for the number and 3 for unit string: */
+ iMaximum += (g_iDecimalCount + 7);
+ if (!m_infoLabels.isEmpty())
+ {
+ QLabel *pLabel = m_infoLabels.begin().value();
+ if (pLabel)
+ {
+ QFontMetrics labelFontMetric(pLabel->font());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iWidth = iMaximum * labelFontMetric.horizontalAdvance('X');
+#else
+ int iWidth = iMaximum * labelFontMetric.width('X');
+#endif
+ foreach (QLabel *pInfoLabel, m_infoLabels)
+ pInfoLabel->setFixedWidth(iWidth);
+ }
+ }
+}
+
+bool UIVMActivityMonitor::eventFilter(QObject *pObj, QEvent *pEvent)
+{
+ if (pEvent-> type() == QEvent::Enter ||
+ pEvent-> type() == QEvent::Leave)
+ {
+ UIChart *pChart = qobject_cast<UIChart*>(pObj);
+ if (pChart)
+ pChart->setMouseOver(pEvent-> type() == QEvent::Enter);
+ }
+ return false;
+}
+
+void UIVMActivityMonitor::prepareWidgets()
+{
+ m_pMainLayout = new QVBoxLayout(this);
+ if (!m_pMainLayout)
+ return;
+
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setSpacing(10);
+#else
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ m_pTimer = new QTimer(this);
+ if (m_pTimer)
+ connect(m_pTimer, &QTimer::timeout, this, &UIVMActivityMonitor::sltTimeout);
+
+ QScrollArea *pScrollArea = new QScrollArea(this);
+ m_pMainLayout->addWidget(pScrollArea);
+
+ QWidget *pContainerWidget = new QWidget(pScrollArea);
+ QGridLayout *pContainerLayout = new QGridLayout(pContainerWidget);
+ pContainerWidget->setLayout(pContainerLayout);
+ pContainerLayout->setSpacing(10);
+ pContainerWidget->show();
+ pScrollArea->setWidget(pContainerWidget);
+ pScrollArea->setWidgetResizable(true);
+
+ QStringList chartOrder;
+ chartOrder << m_strCPUMetricName << m_strRAMMetricName <<
+ m_strDiskMetricName << m_strNetworkMetricName << m_strDiskIOMetricName << m_strVMExitMetricName;
+ int iRow = 0;
+ foreach (const QString &strMetricName, chartOrder)
+ {
+ if (!m_metrics.contains(strMetricName))
+ continue;
+ QHBoxLayout *pChartLayout = new QHBoxLayout;
+ pChartLayout->setSpacing(0);
+
+ QLabel *pLabel = new QLabel(this);
+ pLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ pChartLayout->addWidget(pLabel);
+ m_infoLabels.insert(strMetricName, pLabel);
+
+ UIChart *pChart = new UIChart(this, &(m_metrics[strMetricName]));
+ pChart->installEventFilter(this);
+ connect(pChart, &UIChart::sigExportMetricsToFile,
+ this, &UIVMActivityMonitor::sltExportMetricsToFile);
+ m_charts.insert(strMetricName, pChart);
+ pChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ pChartLayout->addWidget(pChart);
+ pContainerLayout->addLayout(pChartLayout, iRow, 0, 1, 2);
+ ++iRow;
+ }
+
+ /* Configure charts: */
+ if (m_charts.contains(m_strCPUMetricName) && m_charts[m_strCPUMetricName])
+ {
+ m_charts[m_strCPUMetricName]->setIsPieChartAllowed(true);
+ m_charts[m_strCPUMetricName]->setIsAreaChartAllowed(true);
+ }
+
+ QWidget *bottomSpacerWidget = new QWidget(this);
+ bottomSpacerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+ bottomSpacerWidget->setVisible(true);
+ pContainerLayout->addWidget(bottomSpacerWidget, iRow, 0, 1, 2);
+}
+
+void UIVMActivityMonitor::sltTimeout()
+{
+ if (m_performanceCollector.isNull())
+ return;
+ ++m_iTimeStep;
+
+ if (m_metrics.contains(m_strRAMMetricName))
+ {
+ quint64 iTotalRAM = 0;
+ quint64 iFreeRAM = 0;
+ UIMonitorCommon::getRAMLoad(m_performanceCollector, m_nameList, m_objectList, iTotalRAM, iFreeRAM);
+ updateRAMGraphsAndMetric(iTotalRAM, iFreeRAM);
+ }
+
+ /* Update the CPU load chart with values we get from IMachineDebugger::getCPULoad(..): */
+ if (m_metrics.contains(m_strCPUMetricName))
+ {
+ ULONG aPctExecuting;
+ ULONG aPctHalted;
+ ULONG aPctOther;
+ m_comMachineDebugger.GetCPULoad(0x7fffffff, aPctExecuting, aPctHalted, aPctOther);
+ updateCPUGraphsAndMetric(aPctExecuting, aPctOther);
+ }
+
+ /* Update the network load chart with values we find under /Public/NetAdapter/: */
+ {
+ quint64 cbNetworkTotalReceived = 0;
+ quint64 cbNetworkTotalTransmitted = 0;
+ UIMonitorCommon::getNetworkLoad(m_comMachineDebugger, cbNetworkTotalReceived, cbNetworkTotalTransmitted);
+ updateNetworkGraphsAndMetric(cbNetworkTotalReceived, cbNetworkTotalTransmitted);
+ }
+
+ /* Update the Disk I/O chart with values we find under /Public/Storage/?/Port?/Bytes*: */
+ {
+ quint64 cbDiskIOTotalWritten = 0;
+ quint64 cbDiskIOTotalRead = 0;
+ UIMonitorCommon::getDiskLoad(m_comMachineDebugger, cbDiskIOTotalWritten, cbDiskIOTotalRead);
+ updateDiskIOGraphsAndMetric(cbDiskIOTotalWritten, cbDiskIOTotalRead);
+ }
+
+ /* Update the VM exit chart with values we find as /PROF/CPU?/EM/RecordedExits: */
+ {
+ quint64 cTotalVMExits = 0;
+ UIMonitorCommon::getVMMExitCount(m_comMachineDebugger, cTotalVMExits);
+ updateVMExitMetric(cTotalVMExits);
+ }
+}
+
+void UIVMActivityMonitor::sltMachineStateChange(const QUuid &uId)
+{
+ if (m_comMachine.isNull())
+ return;
+ if (m_comMachine.GetId() != uId)
+ return;
+ if (m_comMachine.GetState() == KMachineState_Running)
+ {
+ setEnabled(true);
+ openSession();
+ start();
+ }
+ else if (m_comMachine.GetState() == KMachineState_Paused)
+ {
+ /* If we are already active then stop: */
+ if (!m_comSession.isNull() && m_pTimer && m_pTimer->isActive())
+ m_pTimer->stop();
+ }
+ else
+ reset();
+}
+
+void UIVMActivityMonitor::sltExportMetricsToFile()
+{
+ QString strStartFileName = QString("%1/%2_%3").
+ arg(QFileInfo(m_comMachine.GetSettingsFilePath()).absolutePath()).
+ arg(m_comMachine.GetName()).
+ arg(QDateTime::currentDateTime().toString("dd-MM-yyyy_hh-mm-ss"));
+ QString strFileName = QIFileDialog::getSaveFileName(strStartFileName,"",this,
+ QApplication::translate("UIVMInformationDialog",
+ "Export activity data of the machine \"%1\"")
+ .arg(m_comMachine.GetName()));
+ QFile dataFile(strFileName);
+ if (dataFile.open(QFile::WriteOnly | QFile::Truncate))
+ {
+ QTextStream stream(&dataFile);
+ for (QMap<QString, UIMetric>::const_iterator iterator = m_metrics.begin(); iterator != m_metrics.end(); ++iterator)
+ iterator.value().toFile(stream);
+ dataFile.close();
+ }
+}
+
+void UIVMActivityMonitor::sltCreateContextMenu(const QPoint &point)
+{
+ QMenu menu;
+ QAction *pExportAction =
+ menu.addAction(QApplication::translate("UIVMInformationDialog", "Export"));
+ pExportAction->setIcon(UIIconPool::iconSet(":/performance_monitor_export_16px.png"));
+ connect(pExportAction, &QAction::triggered, this, &UIVMActivityMonitor::sltExportMetricsToFile);
+ menu.exec(mapToGlobal(point));
+}
+
+void UIVMActivityMonitor::sltGuestAdditionsStateChange()
+{
+ bool fGuestAdditionsAvailable = guestAdditionsAvailable("6.1");
+ if (m_fGuestAdditionsAvailable == fGuestAdditionsAvailable)
+ return;
+ m_fGuestAdditionsAvailable = fGuestAdditionsAvailable;
+ enableDisableGuestAdditionDependedWidgets(m_fGuestAdditionsAvailable);
+}
+
+void UIVMActivityMonitor::sltClearCOMData()
+{
+ if (!m_comSession.isNull())
+ {
+ m_comSession.UnlockMachine();
+ m_comSession.detach();
+ }
+}
+
+void UIVMActivityMonitor::prepareMetrics()
+{
+ m_performanceCollector = uiCommon().virtualBox().GetPerformanceCollector();
+ if (m_performanceCollector.isNull())
+ return;
+
+ m_nameList << "Guest/RAM/Usage*";
+ m_objectList = QVector<CUnknown>(m_nameList.size(), CUnknown());
+ m_performanceCollector.SetupMetrics(m_nameList, m_objectList, g_iPeriod, g_iMetricSetupCount);
+ {
+ QVector<CPerformanceMetric> metrics = m_performanceCollector.GetMetrics(m_nameList, m_objectList);
+ for (int i = 0; i < metrics.size(); ++i)
+ {
+ QString strName(metrics[i].GetMetricName());
+ if (!strName.contains(':'))
+ {
+ if (strName.contains("RAM", Qt::CaseInsensitive) && strName.contains("Free", Qt::CaseInsensitive))
+ {
+ UIMetric ramMetric(m_strRAMMetricName, metrics[i].GetUnit(), g_iMaximumQueueSize);
+ ramMetric.setDataSeriesName(0, "Free");
+ ramMetric.setDataSeriesName(1, "Used");
+ ramMetric.setRequiresGuestAdditions(true);
+ m_metrics.insert(m_strRAMMetricName, ramMetric);
+ }
+ }
+ }
+ }
+
+ /* CPU Metric: */
+ UIMetric cpuMetric(m_strCPUMetricName, "%", g_iMaximumQueueSize);
+ cpuMetric.setDataSeriesName(0, "Guest Load");
+ cpuMetric.setDataSeriesName(1, "VMM Load");
+ m_metrics.insert(m_strCPUMetricName, cpuMetric);
+
+ /* Network metric: */
+ UIMetric networkMetric(m_strNetworkMetricName, "B", g_iMaximumQueueSize);
+ networkMetric.setDataSeriesName(0, "Receive Rate");
+ networkMetric.setDataSeriesName(1, "Transmit Rate");
+ networkMetric.setAutoUpdateMaximum(true);
+ m_metrics.insert(m_strNetworkMetricName, networkMetric);
+
+ /* Disk IO metric */
+ UIMetric diskIOMetric(m_strDiskIOMetricName, "B", g_iMaximumQueueSize);
+ diskIOMetric.setDataSeriesName(0, "Write Rate");
+ diskIOMetric.setDataSeriesName(1, "Read Rate");
+ diskIOMetric.setAutoUpdateMaximum(true);
+ m_metrics.insert(m_strDiskIOMetricName, diskIOMetric);
+
+ /* VM exits metric */
+ UIMetric VMExitsMetric(m_strVMExitMetricName, "times", g_iMaximumQueueSize);
+ VMExitsMetric.setAutoUpdateMaximum(true);
+ m_metrics.insert(m_strVMExitMetricName, VMExitsMetric);
+}
+
+void UIVMActivityMonitor::prepareActions()
+{
+}
+
+bool UIVMActivityMonitor::guestAdditionsAvailable(const char *pszMinimumVersion)
+{
+ if (m_comGuest.isNull() || !pszMinimumVersion)
+ return false;
+
+ /* Guest control stuff is in userland: */
+ if (!m_comGuest.GetAdditionsStatus(KAdditionsRunLevelType_Userland))
+ return false;
+
+ if (!m_comGuest.isOk())
+ return false;
+
+ /* Check the related GA facility: */
+ LONG64 iLastUpdatedIgnored;
+ if (m_comGuest.GetFacilityStatus(KAdditionsFacilityType_VBoxService, iLastUpdatedIgnored) != KAdditionsFacilityStatus_Active)
+ return false;
+
+ if (!m_comGuest.isOk())
+ return false;
+
+ QString strGAVersion = m_comGuest.GetAdditionsVersion();
+ if (m_comGuest.isOk())
+ return (RTStrVersionCompare(strGAVersion.toUtf8().constData(), pszMinimumVersion) >= 0);
+
+ return false;
+}
+
+void UIVMActivityMonitor::enableDisableGuestAdditionDependedWidgets(bool fEnable)
+{
+ for (QMap<QString, UIMetric>::const_iterator iterator = m_metrics.begin();
+ iterator != m_metrics.end(); ++iterator)
+ {
+ if (!iterator.value().requiresGuestAdditions())
+ continue;
+ if (m_charts.contains(iterator.key()) && m_charts[iterator.key()])
+ m_charts[iterator.key()]->setIsAvailable(fEnable);
+ if (m_infoLabels.contains(iterator.key()) && m_infoLabels[iterator.key()])
+ {
+ m_infoLabels[iterator.key()]->setEnabled(fEnable);
+ m_infoLabels[iterator.key()]->update();
+ }
+ }
+}
+
+void UIVMActivityMonitor::updateCPUGraphsAndMetric(ULONG iExecutingPercentage, ULONG iOtherPercentage)
+{
+ UIMetric &CPUMetric = m_metrics[m_strCPUMetricName];
+ CPUMetric.addData(0, iExecutingPercentage);
+ CPUMetric.addData(1, iOtherPercentage);
+ CPUMetric.setMaximum(100);
+ if (m_infoLabels.contains(m_strCPUMetricName) && m_infoLabels[m_strCPUMetricName])
+ {
+ QString strInfo;
+
+ strInfo = QString("<b>%1</b></b><br/><font color=\"%2\">%3: %4%5</font><br/><font color=\"%6\">%7: %8%9</font>")
+ .arg(m_strCPUInfoLabelTitle)
+ .arg(dataColorString(m_strCPUMetricName, 0))
+ .arg(m_strCPUInfoLabelGuest).arg(QString::number(iExecutingPercentage)).arg(CPUMetric.unit())
+ .arg(dataColorString(m_strCPUMetricName, 1))
+ .arg(m_strCPUInfoLabelVMM).arg(QString::number(iOtherPercentage)).arg(CPUMetric.unit());
+ m_infoLabels[m_strCPUMetricName]->setText(strInfo);
+ }
+
+ if (m_charts.contains(m_strCPUMetricName))
+ m_charts[m_strCPUMetricName]->update();
+}
+
+void UIVMActivityMonitor::updateRAMGraphsAndMetric(quint64 iTotalRAM, quint64 iFreeRAM)
+{
+ UIMetric &RAMMetric = m_metrics[m_strRAMMetricName];
+ RAMMetric.setMaximum(iTotalRAM);
+ RAMMetric.addData(0, iTotalRAM - iFreeRAM);
+ if (m_infoLabels.contains(m_strRAMMetricName) && m_infoLabels[m_strRAMMetricName])
+ {
+ QString strInfo;
+ strInfo = QString("<b>%1</b><br/>%2: %3<br/>%4: %5<br/>%6: %7").arg(m_strRAMInfoLabelTitle).arg(m_strRAMInfoLabelTotal).arg(UITranslator::formatSize(_1K * iTotalRAM, g_iDecimalCount))
+ .arg(m_strRAMInfoLabelFree).arg(UITranslator::formatSize(_1K * (iFreeRAM), g_iDecimalCount))
+ .arg(m_strRAMInfoLabelUsed).arg(UITranslator::formatSize(_1K * (iTotalRAM - iFreeRAM), g_iDecimalCount));
+ m_infoLabels[m_strRAMMetricName]->setText(strInfo);
+ }
+ if (m_charts.contains(m_strRAMMetricName))
+ m_charts[m_strRAMMetricName]->update();
+}
+
+void UIVMActivityMonitor::updateNetworkGraphsAndMetric(quint64 iReceiveTotal, quint64 iTransmitTotal)
+{
+ UIMetric &NetMetric = m_metrics[m_strNetworkMetricName];
+
+ quint64 iReceiveRate = iReceiveTotal - NetMetric.total(0);
+ quint64 iTransmitRate = iTransmitTotal - NetMetric.total(1);
+
+ NetMetric.setTotal(0, iReceiveTotal);
+ NetMetric.setTotal(1, iTransmitTotal);
+
+ if (!NetMetric.isInitialized())
+ {
+ NetMetric.setIsInitialized(true);
+ return;
+ }
+
+ NetMetric.addData(0, iReceiveRate);
+ NetMetric.addData(1, iTransmitRate);
+
+ if (m_infoLabels.contains(m_strNetworkMetricName) && m_infoLabels[m_strNetworkMetricName])
+ {
+ QString strInfo;
+ strInfo = QString("<b>%1</b></b><br/><font color=\"%2\">%3: %4<br/>%5 %6</font><br/><font color=\"%7\">%8: %9<br/>%10 %11</font>")
+ .arg(m_strNetworkInfoLabelTitle)
+ .arg(dataColorString(m_strNetworkMetricName, 0)).arg(m_strNetworkInfoLabelReceived).arg(UITranslator::formatSize((quint64)iReceiveRate, g_iDecimalCount))
+ .arg(m_strNetworkInfoLabelReceivedTotal).arg(UITranslator::formatSize((quint64)iReceiveTotal, g_iDecimalCount))
+ .arg(dataColorString(m_strNetworkMetricName, 1)).arg(m_strNetworkInfoLabelTransmitted).arg(UITranslator::formatSize((quint64)iTransmitRate, g_iDecimalCount))
+ .arg(m_strNetworkInfoLabelTransmittedTotal).arg(UITranslator::formatSize((quint64)iTransmitTotal, g_iDecimalCount));
+ m_infoLabels[m_strNetworkMetricName]->setText(strInfo);
+ }
+ if (m_charts.contains(m_strNetworkMetricName))
+ m_charts[m_strNetworkMetricName]->update();
+}
+
+void UIVMActivityMonitor::resetCPUInfoLabel()
+{
+ if (m_infoLabels.contains(m_strCPUMetricName) && m_infoLabels[m_strCPUMetricName])
+ {
+ QString strInfo =QString("<b>%1</b></b><br/>%2: %3<br/>%4: %5")
+ .arg(m_strCPUInfoLabelTitle)
+ .arg(m_strCPUInfoLabelGuest).arg("--")
+ .arg(m_strCPUInfoLabelVMM).arg("--");
+ m_infoLabels[m_strCPUMetricName]->setText(strInfo);
+ }
+}
+
+void UIVMActivityMonitor::resetRAMInfoLabel()
+{
+ if (m_infoLabels.contains(m_strRAMMetricName) && m_infoLabels[m_strRAMMetricName])
+ {
+ QString strInfo = QString("<b>%1</b><br/>%2: %3<br/>%4: %5<br/>%6: %7").
+ arg(m_strRAMInfoLabelTitle).arg(m_strRAMInfoLabelTotal).arg("--")
+ .arg(m_strRAMInfoLabelFree).arg("--")
+ .arg(m_strRAMInfoLabelUsed).arg("--");
+ m_infoLabels[m_strRAMMetricName]->setText(strInfo);
+ }
+}
+
+void UIVMActivityMonitor::resetNetworkInfoLabel()
+{
+ if (m_infoLabels.contains(m_strNetworkMetricName) && m_infoLabels[m_strNetworkMetricName])
+ {
+ QString strInfo = QString("<b>%1</b></b><br/>%2: %3<br/>%4 %5<br/>%6: %7<br/>%8 %9")
+ .arg(m_strNetworkInfoLabelTitle)
+ .arg(m_strNetworkInfoLabelReceived).arg("--")
+ .arg(m_strNetworkInfoLabelReceivedTotal).arg("--")
+ .arg(m_strNetworkInfoLabelTransmitted).arg("--")
+ .arg(m_strNetworkInfoLabelTransmittedTotal).arg("--");
+ m_infoLabels[m_strNetworkMetricName]->setText(strInfo);
+ }
+}
+
+void UIVMActivityMonitor::resetVMExitInfoLabel()
+{
+ if (m_infoLabels.contains(m_strVMExitMetricName) && m_infoLabels[m_strVMExitMetricName])
+ {
+ QString strInfo;
+ strInfo = QString("<b>%1</b></b><br/>%2: %3<br/>%4: %5")
+ .arg(m_strVMExitInfoLabelTitle)
+ .arg(m_strVMExitLabelCurrent).arg("--")
+ .arg(m_strVMExitLabelTotal).arg("--");
+
+ m_infoLabels[m_strVMExitMetricName]->setText(strInfo);
+ }
+}
+
+void UIVMActivityMonitor::resetDiskIOInfoLabel()
+{
+ if (m_infoLabels.contains(m_strDiskIOMetricName) && m_infoLabels[m_strDiskIOMetricName])
+ {
+ QString strInfo = QString("<b>%1</b></b><br/>%2: %3<br/>%4 %5<br/>%6: %7<br/>%8 %9")
+ .arg(m_strDiskIOInfoLabelTitle)
+ .arg(m_strDiskIOInfoLabelWritten).arg("--")
+ .arg(m_strDiskIOInfoLabelWrittenTotal).arg("--")
+ .arg(m_strDiskIOInfoLabelRead).arg("--")
+ .arg(m_strDiskIOInfoLabelReadTotal).arg("--");
+ m_infoLabels[m_strDiskIOMetricName]->setText(strInfo);
+ }
+}
+
+void UIVMActivityMonitor::updateDiskIOGraphsAndMetric(quint64 uDiskIOTotalWritten, quint64 uDiskIOTotalRead)
+{
+ UIMetric &diskMetric = m_metrics[m_strDiskIOMetricName];
+
+ quint64 iWriteRate = uDiskIOTotalWritten - diskMetric.total(0);
+ quint64 iReadRate = uDiskIOTotalRead - diskMetric.total(1);
+
+ diskMetric.setTotal(0, uDiskIOTotalWritten);
+ diskMetric.setTotal(1, uDiskIOTotalRead);
+
+ /* Do not set data and maximum if the metric has not been initialized since we need to initialize totals "(t-1)" first: */
+ if (!diskMetric.isInitialized()){
+ diskMetric.setIsInitialized(true);
+ return;
+ }
+ diskMetric.addData(0, iWriteRate);
+ diskMetric.addData(1, iReadRate);
+
+ if (m_infoLabels.contains(m_strDiskIOMetricName) && m_infoLabels[m_strDiskIOMetricName])
+ {
+ QString strInfo = QString("<b>%1</b></b><br/><font color=\"%2\">%3: %4<br/>%5 %6</font><br/><font color=\"%7\">%8: %9<br/>%10 %11</font>")
+ .arg(m_strDiskIOInfoLabelTitle)
+ .arg(dataColorString(m_strDiskIOMetricName, 0)).arg(m_strDiskIOInfoLabelWritten).arg(UITranslator::formatSize((quint64)iWriteRate, g_iDecimalCount))
+ .arg(m_strDiskIOInfoLabelWrittenTotal).arg(UITranslator::formatSize((quint64)uDiskIOTotalWritten, g_iDecimalCount))
+ .arg(dataColorString(m_strDiskIOMetricName, 1)).arg(m_strDiskIOInfoLabelRead).arg(UITranslator::formatSize((quint64)iReadRate, g_iDecimalCount))
+ .arg(m_strDiskIOInfoLabelReadTotal).arg(UITranslator::formatSize((quint64)uDiskIOTotalRead, g_iDecimalCount));
+ m_infoLabels[m_strDiskIOMetricName]->setText(strInfo);
+ }
+ if (m_charts.contains(m_strDiskIOMetricName))
+ m_charts[m_strDiskIOMetricName]->update();
+}
+
+void UIVMActivityMonitor::updateVMExitMetric(quint64 uTotalVMExits)
+{
+ if (uTotalVMExits <= 0)
+ return;
+
+ UIMetric &VMExitMetric = m_metrics[m_strVMExitMetricName];
+ quint64 iRate = uTotalVMExits - VMExitMetric.total(0);
+ VMExitMetric.setTotal(0, uTotalVMExits);
+ /* Do not set data and maximum if the metric has not been initialized since we need to initialize totals "(t-1)" first: */
+ if (!VMExitMetric.isInitialized())
+ {
+ VMExitMetric.setIsInitialized(true);
+ return;
+ }
+ VMExitMetric.addData(0, iRate);
+ if (m_infoLabels.contains(m_strVMExitMetricName) && m_infoLabels[m_strVMExitMetricName])
+ {
+ QString strInfo;
+ strInfo = QString("<b>%1</b></b><br/>%2: %3 %4<br/>%5: %6 %7")
+ .arg(m_strVMExitInfoLabelTitle)
+ .arg(m_strVMExitLabelCurrent).arg(UITranslator::addMetricSuffixToNumber(iRate)).arg(VMExitMetric.unit())
+ .arg(m_strVMExitLabelTotal).arg(UITranslator::addMetricSuffixToNumber(uTotalVMExits)).arg(VMExitMetric.unit());
+ m_infoLabels[m_strVMExitMetricName]->setText(strInfo);
+ }
+ if (m_charts.contains(m_strVMExitMetricName))
+ m_charts[m_strVMExitMetricName]->update();
+}
+
+QString UIVMActivityMonitor::dataColorString(const QString &strChartName, int iDataIndex)
+{
+ if (!m_charts.contains(strChartName))
+ return QColor(Qt::black).name(QColor::HexRgb);
+ UIChart *pChart = m_charts[strChartName];
+ if (!pChart)
+ return QColor(Qt::black).name(QColor::HexRgb);
+ return pChart->dataSeriesColor(iDataIndex).name(QColor::HexRgb);
+}
+
+void UIVMActivityMonitor::reset()
+{
+ m_fGuestAdditionsAvailable = false;
+ setEnabled(false);
+
+ if (m_pTimer)
+ m_pTimer->stop();
+ /* reset the metrics. this will delete their data cache: */
+ for (QMap<QString, UIMetric>::iterator iterator = m_metrics.begin();
+ iterator != m_metrics.end(); ++iterator)
+ iterator.value().reset();
+ /* force update on the charts to draw now emptied metrics' data: */
+ for (QMap<QString, UIChart*>::iterator iterator = m_charts.begin();
+ iterator != m_charts.end(); ++iterator)
+ iterator.value()->update();
+ /* Reset the info labels: */
+ resetCPUInfoLabel();
+ resetRAMInfoLabel();
+ resetNetworkInfoLabel();
+ resetDiskIOInfoLabel();
+ resetVMExitInfoLabel();
+ update();
+ sltClearCOMData();
+}
+
+void UIVMActivityMonitor::start()
+{
+ if (m_comMachine.isNull() || m_comMachine.GetState() != KMachineState_Running)
+ return;
+
+ m_fGuestAdditionsAvailable = guestAdditionsAvailable("6.1");
+ enableDisableGuestAdditionDependedWidgets(m_fGuestAdditionsAvailable);
+ if (m_pTimer)
+ m_pTimer->start(1000 * g_iPeriod);
+}
+
+
+#include "UIVMActivityMonitor.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/UIVMActivityMonitor.h b/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/UIVMActivityMonitor.h
new file mode 100644
index 00000000..4cf0b25f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/UIVMActivityMonitor.h
@@ -0,0 +1,263 @@
+/* $Id: UIVMActivityMonitor.h $ */
+/** @file
+ * VBox Qt GUI - UIVMActivityMonitor class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_activity_vmactivity_UIVMActivityMonitor_h
+#define FEQT_INCLUDED_SRC_activity_vmactivity_UIVMActivityMonitor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+#include <QMap>
+#include <QQueue>
+#include <QTextStream>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuest.h"
+#include "CMachine.h"
+#include "CMachineDebugger.h"
+#include "CPerformanceCollector.h"
+#include "CSession.h"
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIMonitorCommon.h"
+
+/* Forward declarations: */
+class QTimer;
+class QVBoxLayout;
+class QLabel;
+class UIChart;
+class UISession;
+class UIRuntimeInfoWidget;
+
+#define DATA_SERIES_SIZE 2
+
+/** UIMetric represents a performance metric and is used to store data related to the corresponding metric. */
+class UIMetric
+{
+
+public:
+
+ UIMetric(const QString &strName, const QString &strUnit, int iMaximumQueueSize);
+ UIMetric();
+ const QString &name() const;
+
+ void setMaximum(quint64 iMaximum);
+ quint64 maximum() const;
+
+ void setUnit(QString strUnit);
+ const QString &unit() const;
+
+ void addData(int iDataSeriesIndex, quint64 fData);
+ const QQueue<quint64> *data(int iDataSeriesIndex) const;
+
+ /** # of the data point of the data series with index iDataSeriesIndex. */
+ int dataSize(int iDataSeriesIndex) const;
+
+ void setDataSeriesName(int iDataSeriesIndex, const QString &strName);
+ QString dataSeriesName(int iDataSeriesIndex) const;
+
+ void setTotal(int iDataSeriesIndex, quint64 iTotal);
+ quint64 total(int iDataSeriesIndex) const;
+
+ bool requiresGuestAdditions() const;
+ void setRequiresGuestAdditions(bool fRequiresGAs);
+
+ void setIsInitialized(bool fIsInitialized);
+ bool isInitialized() const;
+
+ void reset();
+ void toFile(QTextStream &stream) const;
+
+ void setAutoUpdateMaximum(bool fAuto);
+ bool autoUpdateMaximum() const;
+
+private:
+
+ QString m_strName;
+ QString m_strUnit;
+ QString m_strDataSeriesName[DATA_SERIES_SIZE];
+ quint64 m_iMaximum;
+ QQueue<quint64> m_data[DATA_SERIES_SIZE];
+ /** The total data (the counter value we get from IMachineDebugger API). For the metrics
+ * we get from IMachineDebugger m_data values are computed as deltas of total values t - (t-1) */
+ quint64 m_iTotal[DATA_SERIES_SIZE];
+#if 0 /* Unused according to Clang 11. */
+ int m_iMaximumQueueSize;
+#endif
+ bool m_fRequiresGuestAdditions;
+ /** Used for metrices whose data is computed as total deltas. That is we receieve only total value and
+ * compute time step data from total deltas. m_isInitialised is true if the total has been set first time. */
+ bool m_fIsInitialized;
+ /** Maximum is updated as a new data is added to data queue. */
+ bool m_fAutoUpdateMaximum;
+};
+
+/** UIVMActivityMonitor class displays some high level performance metrics of the guest system.
+ * The values are read in certain periods and cached in the GUI side. Currently we draw some line charts
+ * and pie charts (where applicable) alongside with some text. IPerformanceCollector and IMachineDebugger are
+ * two sources of the performance metrics. Unfortunately these two have very distinct APIs resulting a bit too much
+ * special casing etc.*/
+class SHARED_LIBRARY_STUFF UIVMActivityMonitor : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+public:
+
+ /** Constructs information-tab passing @a pParent to the QWidget base-class constructor.
+ * @param machine is machine reference. */
+ UIVMActivityMonitor(EmbedTo enmEmbedding, QWidget *pParent, const CMachine &machine);
+ ~UIVMActivityMonitor();
+ void setMachine(const CMachine &machine);
+ QUuid machineId() const;
+ QString machineName() const;
+
+ public slots:
+
+ /** @name These functions are connected to API events and implement necessary updates.
+ * @{ */
+ void sltGuestAdditionsStateChange();
+ /** @} */
+ void sltExportMetricsToFile();
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual bool eventFilter(QObject *pObj, QEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Reads the metric values for several sources and calls corresponding update functions. */
+ void sltTimeout();
+ /** Stop updating the charts if/when the machine state changes something other than KMachineState_Running. */
+ void sltMachineStateChange(const QUuid &uId);
+ void sltCreateContextMenu(const QPoint &point);
+ void sltClearCOMData();
+
+private:
+
+ void prepareWidgets();
+ void prepareMetrics();
+ void prepareActions();
+ bool guestAdditionsAvailable(const char *pszMinimumVersion);
+ void enableDisableGuestAdditionDependedWidgets(bool fEnable);
+
+ /** @name The following functions update corresponding metric charts and labels with new values
+ * @{ */
+ void updateCPUGraphsAndMetric(ULONG iLoadPercentage, ULONG iOtherPercentage);
+ void updateRAMGraphsAndMetric(quint64 iTotalRAM, quint64 iFreeRAM);
+ void updateNetworkGraphsAndMetric(quint64 iReceiveTotal, quint64 iTransmitTotal);
+ void updateDiskIOGraphsAndMetric(quint64 uDiskIOTotalWritten, quint64 uDiskIOTotalRead);
+ void updateVMExitMetric(quint64 uTotalVMExits);
+ /** @} */
+
+ /** @name The following functions reset corresponding info labels
+ * @{ */
+ void resetCPUInfoLabel();
+ void resetRAMInfoLabel();
+ void resetNetworkInfoLabel();
+ void resetDiskIOInfoLabel();
+ void resetVMExitInfoLabel();
+ /** @} */
+
+ /** Returns a QColor for the chart with @p strChartName and data series with @p iDataIndex. */
+ QString dataColorString(const QString &strChartName, int iDataIndex);
+ /* Starts the timer which in return collects data and updates charts/graphs. */
+ void start();
+ void reset();
+ void openSession();
+
+ bool m_fGuestAdditionsAvailable;
+ CMachine m_comMachine;
+ CSession m_comSession;
+ CGuest m_comGuest;
+
+ CPerformanceCollector m_performanceCollector;
+ CMachineDebugger m_comMachineDebugger;
+ /** Holds the instance of layout we create. */
+ QVBoxLayout *m_pMainLayout;
+ QTimer *m_pTimer;
+
+ /** @name The following are used during UIPerformanceCollector::QueryMetricsData(..)
+ * @{ */
+ QVector<QString> m_nameList;
+ QVector<CUnknown> m_objectList;
+ /** @} */
+
+ QMap<QString, UIMetric> m_metrics;
+ QMap<QString,UIChart*> m_charts;
+ /** Stores the QLabel instances which we show next to each UIChart. The value is the name of the metric. */
+ QMap<QString,QLabel*> m_infoLabels;
+
+ /** @name These metric names are used for map keys to identify metrics. They are not translated.
+ * @{ */
+ QString m_strCPUMetricName;
+ QString m_strRAMMetricName;
+ QString m_strDiskMetricName;
+ QString m_strNetworkMetricName;
+ QString m_strDiskIOMetricName;
+ QString m_strVMExitMetricName;
+ /** @} */
+
+ /** @name Cached translated strings.
+ * @{ */
+ /** CPU info label strings. */
+ QString m_strCPUInfoLabelTitle;
+ QString m_strCPUInfoLabelGuest;
+ QString m_strCPUInfoLabelVMM;
+ /** RAM usage info label strings. */
+ QString m_strRAMInfoLabelTitle;
+ QString m_strRAMInfoLabelTotal;
+ QString m_strRAMInfoLabelFree;
+ QString m_strRAMInfoLabelUsed;
+ /** Net traffic info label strings. */
+ QString m_strNetworkInfoLabelTitle;
+ QString m_strNetworkInfoLabelReceived;
+ QString m_strNetworkInfoLabelTransmitted;
+ QString m_strNetworkInfoLabelReceivedTotal;
+ QString m_strNetworkInfoLabelTransmittedTotal;
+ /** Disk IO info label strings. */
+ QString m_strDiskIOInfoLabelTitle;
+ QString m_strDiskIOInfoLabelWritten;
+ QString m_strDiskIOInfoLabelRead;
+ QString m_strDiskIOInfoLabelWrittenTotal;
+ QString m_strDiskIOInfoLabelReadTotal;
+ /** VM Exit info label strings. */
+ QString m_strVMExitInfoLabelTitle;
+ QString m_strVMExitLabelCurrent;
+ QString m_strVMExitLabelTotal;
+ /** @} */
+ quint64 m_iTimeStep;
+ EmbedTo m_enmEmbedding;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_activity_vmactivity_UIVMActivityMonitor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/cloud/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleDetailsWidget.cpp b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleDetailsWidget.cpp
new file mode 100644
index 00000000..03a4f313
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleDetailsWidget.cpp
@@ -0,0 +1,531 @@
+/* $Id: UICloudConsoleDetailsWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UICloudConsoleDetailsWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QStackedLayout>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "UICloudConsoleDetailsWidget.h"
+#include "UICloudConsoleManager.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+UICloudConsoleDetailsWidget::UICloudConsoleDetailsWidget(EmbedTo enmEmbedding, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pStackedLayout(0)
+ , m_pLabelApplicationName(0)
+ , m_pEditorApplicationName(0)
+ , m_pLabelApplicationPath(0)
+ , m_pEditorApplicationPath(0)
+ , m_pLabelApplicationArgument(0)
+ , m_pEditorApplicationArgument(0)
+ , m_pLabelProfileName(0)
+ , m_pEditorProfileName(0)
+ , m_pLabelProfileArgument(0)
+ , m_pEditorProfileArgument(0)
+ , m_pButtonBox(0)
+{
+ prepare();
+}
+
+void UICloudConsoleDetailsWidget::setApplicationData(const UIDataCloudConsoleApplication &data)
+{
+ /* Clear all data first: */
+ clearData();
+
+ /* Cache old/new data: */
+ m_oldApplicationData = data;
+ m_newApplicationData = m_oldApplicationData;
+
+ /* Switch to proper stack: */
+ m_pStackedLayout->setCurrentIndex(0);
+
+ /* Load data: */
+ loadData();
+}
+
+void UICloudConsoleDetailsWidget::setProfileData(const UIDataCloudConsoleProfile &data)
+{
+ /* Clear all data first: */
+ clearData();
+
+ /* Cache old/new data: */
+ m_oldProfileData = data;
+ m_newProfileData = m_oldProfileData;
+
+ /* Switch to proper stack: */
+ m_pStackedLayout->setCurrentIndex(1);
+
+ /* Load data: */
+ loadData();
+}
+
+void UICloudConsoleDetailsWidget::clearData()
+{
+ /* Clear widgets: */
+ m_pEditorApplicationName->setText(QString());
+ m_pEditorApplicationPath->setText(QString());
+ m_pEditorApplicationArgument->setText(QString());
+ m_pEditorProfileName->setText(QString());
+ m_pEditorProfileArgument->setText(QString());
+
+ /* Clear data: */
+ m_oldApplicationData = UIDataCloudConsoleApplication();
+ m_newApplicationData = m_oldApplicationData;
+ m_oldProfileData = UIDataCloudConsoleProfile();
+ m_newProfileData = m_oldProfileData;
+}
+
+void UICloudConsoleDetailsWidget::retranslateUi()
+{
+ /* Translate editor labels: */
+ m_pLabelApplicationName->setText(UICloudConsoleManager::tr("Name:"));
+ m_pLabelApplicationPath->setText(UICloudConsoleManager::tr("Path:"));
+ m_pLabelApplicationArgument->setText(UICloudConsoleManager::tr("Argument:"));
+ m_pLabelProfileName->setText(UICloudConsoleManager::tr("Name:"));
+ m_pLabelProfileArgument->setText(UICloudConsoleManager::tr("Argument:"));
+
+ /* Translate editor placeholders: */
+ m_pEditorApplicationName->setPlaceholderText(UICloudConsoleManager::tr("Enter a name for this console application..."));
+ m_pEditorApplicationPath->setPlaceholderText(UICloudConsoleManager::tr("Enter a path for this console application..."));
+ m_pEditorApplicationArgument->setPlaceholderText(UICloudConsoleManager::tr("Enter an argument for this console application..."));
+ m_pEditorProfileName->setPlaceholderText(UICloudConsoleManager::tr("Enter a name for this console profile..."));
+ m_pEditorProfileArgument->setPlaceholderText(UICloudConsoleManager::tr("Enter an argument for this console profile..."));
+
+ /* Translate buttons: */
+ if (m_pButtonBox)
+ {
+ /* 'Reset' button: */
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setText(UICloudConsoleManager::tr("Reset"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setStatusTip(UICloudConsoleManager::tr("Reset changes in current console details"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->
+ setToolTip(UICloudConsoleManager::tr("Reset Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+ /* 'Apply' button: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText(UICloudConsoleManager::tr("Apply"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(UICloudConsoleManager::tr("Apply changes in current console details"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->
+ setToolTip(UICloudConsoleManager::tr("Apply Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+
+ /* Retranslate validation: */
+ retranslateValidation();
+}
+
+void UICloudConsoleDetailsWidget::sltApplicationNameChanged(const QString &strName)
+{
+ /* Push changes back: */
+ m_newApplicationData.m_strName = strName;
+
+ /* Revalidate: */
+ revalidate(m_pEditorApplicationName);
+ /* Update button states: */
+ updateButtonStates();
+}
+
+void UICloudConsoleDetailsWidget::sltApplicationPathChanged(const QString &strPath)
+{
+ /* Push changes back: */
+ m_newApplicationData.m_strPath = strPath;
+
+ /* Revalidate: */
+ revalidate(m_pEditorApplicationPath);
+ /* Update button states: */
+ updateButtonStates();
+}
+
+void UICloudConsoleDetailsWidget::sltApplicationArgumentChanged(const QString &strArgument)
+{
+ /* Push changes back: */
+ m_newApplicationData.m_strArgument = strArgument;
+
+ /* Revalidate: */
+ revalidate(m_pEditorApplicationArgument);
+ /* Update button states: */
+ updateButtonStates();
+}
+
+void UICloudConsoleDetailsWidget::sltProfileNameChanged(const QString &strName)
+{
+ /* Push changes back: */
+ m_newProfileData.m_strName = strName;
+
+ /* Revalidate: */
+ revalidate(m_pEditorProfileName);
+ /* Update button states: */
+ updateButtonStates();
+}
+
+void UICloudConsoleDetailsWidget::sltProfileArgumentChanged(const QString &strArgument)
+{
+ /* Push changes back: */
+ m_newProfileData.m_strArgument = strArgument;
+
+ /* Revalidate: */
+ revalidate(m_pEditorProfileArgument);
+ /* Update button states: */
+ updateButtonStates();
+}
+
+void UICloudConsoleDetailsWidget::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+ /* Make sure button-box exists: */
+ AssertPtrReturnVoid(m_pButtonBox);
+
+ /* Disable buttons first of all: */
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if (pButton == m_pButtonBox->button(QDialogButtonBox::Cancel))
+ emit sigDataChangeRejected();
+ else
+ if (pButton == m_pButtonBox->button(QDialogButtonBox::Ok))
+ emit sigDataChangeAccepted();
+}
+
+void UICloudConsoleDetailsWidget::prepare()
+{
+ /* Prepare widgets: */
+ prepareWidgets();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Update button states finally: */
+ updateButtonStates();
+}
+
+void UICloudConsoleDetailsWidget::prepareWidgets()
+{
+ /* Create main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create stacked layout: */
+ m_pStackedLayout = new QStackedLayout;
+ if (m_pStackedLayout)
+ {
+ /* Add into layout: */
+ pMainLayout->addLayout(m_pStackedLayout);
+
+ /* Create application widget: */
+ QWidget *pWidgetApplication = new QWidget;
+ if (pWidgetApplication)
+ {
+ /* Create application layout: */
+ QGridLayout *pLayoutApplication = new QGridLayout(pWidgetApplication);
+ if (pLayoutApplication)
+ {
+ pLayoutApplication->setContentsMargins(0, 0, 0, 0);
+ pLayoutApplication->setRowStretch(3, 1);
+
+ if (m_enmEmbedding == EmbedTo_Dialog)
+ {
+ pLayoutApplication->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ pLayoutApplication->setSpacing(10);
+#else
+ pLayoutApplication->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+ }
+ else
+ {
+#ifdef VBOX_WS_MAC
+ pLayoutApplication->setContentsMargins(13, 0, 13, 13);
+ pLayoutApplication->setSpacing(10);
+#else
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) * 1.5;
+ const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) * 1.5;
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) * 1.5;
+ const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) * 1.5;
+ pLayoutApplication->setContentsMargins(iL, iT, iR, iB);
+#endif
+ }
+
+ /* Create name editor: */
+ m_pEditorApplicationName = new QLineEdit(pWidgetApplication);
+ if (m_pEditorApplicationName)
+ {
+ connect(m_pEditorApplicationName, &QLineEdit::textChanged,
+ this, &UICloudConsoleDetailsWidget::sltApplicationNameChanged);
+
+ /* Add into layout: */
+ pLayoutApplication->addWidget(m_pEditorApplicationName, 0, 1);
+ }
+ /* Create name label: */
+ m_pLabelApplicationName = new QLabel(pWidgetApplication);
+ if (m_pLabelApplicationName)
+ {
+ m_pLabelApplicationName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelApplicationName->setBuddy(m_pEditorApplicationName);
+
+ /* Add into layout: */
+ pLayoutApplication->addWidget(m_pLabelApplicationName, 0, 0);
+ }
+
+ /* Create path editor: */
+ m_pEditorApplicationPath = new QLineEdit(pWidgetApplication);
+ if (m_pEditorApplicationPath)
+ {
+ connect(m_pEditorApplicationPath, &QLineEdit::textChanged,
+ this, &UICloudConsoleDetailsWidget::sltApplicationPathChanged);
+
+ /* Add into layout: */
+ pLayoutApplication->addWidget(m_pEditorApplicationPath, 1, 1);
+ }
+ /* Create path label: */
+ m_pLabelApplicationPath = new QLabel(pWidgetApplication);
+ if (m_pLabelApplicationPath)
+ {
+ m_pLabelApplicationPath->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelApplicationPath->setBuddy(m_pEditorApplicationPath);
+
+ /* Add into layout: */
+ pLayoutApplication->addWidget(m_pLabelApplicationPath, 1, 0);
+ }
+
+ /* Create argument editor: */
+ m_pEditorApplicationArgument = new QLineEdit(pWidgetApplication);
+ if (m_pEditorApplicationArgument)
+ {
+ connect(m_pEditorApplicationArgument, &QLineEdit::textChanged,
+ this, &UICloudConsoleDetailsWidget::sltApplicationArgumentChanged);
+
+ /* Add into layout: */
+ pLayoutApplication->addWidget(m_pEditorApplicationArgument, 2, 1);
+ }
+ /* Create argument label: */
+ m_pLabelApplicationArgument = new QLabel(pWidgetApplication);
+ if (m_pLabelApplicationArgument)
+ {
+ m_pLabelApplicationArgument->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelApplicationArgument->setBuddy(m_pEditorApplicationArgument);
+
+ /* Add into layout: */
+ pLayoutApplication->addWidget(m_pLabelApplicationArgument, 2, 0);
+ }
+ }
+
+ /* Add into layout: */
+ m_pStackedLayout->addWidget(pWidgetApplication);
+ }
+
+ /* Create profile widget: */
+ QWidget *pWidgetProfile = new QWidget;
+ if (pWidgetProfile)
+ {
+ /* Create profile layout: */
+ QGridLayout *pLayoutProfile = new QGridLayout(pWidgetProfile);
+ if (pLayoutProfile)
+ {
+ pLayoutProfile->setContentsMargins(0, 0, 0, 0);
+ pLayoutProfile->setRowStretch(2, 1);
+
+ if (m_enmEmbedding == EmbedTo_Dialog)
+ {
+ pLayoutProfile->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ pLayoutProfile->setSpacing(10);
+#else
+ pLayoutProfile->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+ }
+ else
+ {
+#ifdef VBOX_WS_MAC
+ pLayoutProfile->setContentsMargins(13, 0, 13, 13);
+ pLayoutProfile->setSpacing(10);
+#else
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) * 1.5;
+ const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) * 1.5;
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) * 1.5;
+ const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) * 1.5;
+ pLayoutProfile->setContentsMargins(iL, iT, iR, iB);
+#endif
+ }
+
+ /* Create name editor: */
+ m_pEditorProfileName = new QLineEdit(pWidgetProfile);
+ if (m_pEditorProfileName)
+ {
+ connect(m_pEditorProfileName, &QLineEdit::textChanged,
+ this, &UICloudConsoleDetailsWidget::sltProfileNameChanged);
+
+ /* Add into layout: */
+ pLayoutProfile->addWidget(m_pEditorProfileName, 0, 1);
+ }
+ /* Create name label: */
+ m_pLabelProfileName = new QLabel(pWidgetProfile);
+ if (m_pLabelProfileName)
+ {
+ m_pLabelProfileName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelProfileName->setBuddy(m_pEditorProfileName);
+
+ /* Add into layout: */
+ pLayoutProfile->addWidget(m_pLabelProfileName, 0, 0);
+ }
+
+ /* Create argument editor: */
+ m_pEditorProfileArgument = new QLineEdit(pWidgetProfile);
+ if (m_pEditorProfileArgument)
+ {
+ connect(m_pEditorProfileArgument, &QLineEdit::textChanged,
+ this, &UICloudConsoleDetailsWidget::sltProfileArgumentChanged);
+
+ /* Add into layout: */
+ pLayoutProfile->addWidget(m_pEditorProfileArgument, 1, 1);
+ }
+ /* Create name label: */
+ m_pLabelProfileArgument = new QLabel(pWidgetProfile);
+ if (m_pLabelProfileArgument)
+ {
+ m_pLabelProfileArgument->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelProfileArgument->setBuddy(m_pEditorProfileArgument);
+
+ /* Add into layout: */
+ pLayoutProfile->addWidget(m_pLabelProfileArgument, 1, 0);
+ }
+ }
+
+ /* Add into layout: */
+ m_pStackedLayout->addWidget(pWidgetProfile);
+ }
+ }
+
+ /* If parent embedded into stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBox, &QIDialogButtonBox::clicked, this, &UICloudConsoleDetailsWidget::sltHandleButtonBoxClick);
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pButtonBox);
+ }
+ }
+ }
+}
+
+void UICloudConsoleDetailsWidget::loadData()
+{
+ /* If application pane is selected: */
+ if (m_pStackedLayout->currentIndex() == 0)
+ {
+ m_pEditorApplicationName->setText(m_oldApplicationData.m_strName);
+ m_pEditorApplicationPath->setText(m_oldApplicationData.m_strPath);
+ m_pEditorApplicationArgument->setText(m_oldApplicationData.m_strArgument);
+ }
+ /* If profile pane is selected: */
+ else
+ {
+ m_pEditorProfileName->setText(m_oldProfileData.m_strName);
+ m_pEditorProfileArgument->setText(m_oldProfileData.m_strArgument);
+ }
+}
+
+void UICloudConsoleDetailsWidget::revalidate(QWidget *pWidget /* = 0 */)
+{
+ /// @todo validate application/profile details!
+
+ /* Retranslate validation: */
+ retranslateValidation(pWidget);
+}
+
+void UICloudConsoleDetailsWidget::retranslateValidation(QWidget *pWidget /* = 0 */)
+{
+ Q_UNUSED(pWidget);
+
+ /// @todo translate vaidation errors!
+}
+
+void UICloudConsoleDetailsWidget::updateButtonStates()
+{
+#if 0
+ if (m_oldData != m_newData)
+ {
+ printf("Old data:\n");
+ foreach (const QString &strKey, m_oldData.m_data.keys())
+ {
+ const QString strValue = m_oldData.m_data.value(strKey).first;
+ const QString strDecription = m_oldData.m_data.value(strKey).second;
+ printf(" %s: %s, %s\n", strKey.toUtf8().constData(), strValue.toUtf8().constData(), strDecription.toUtf8().constData());
+ }
+ printf("New data:\n");
+ foreach (const QString &strKey, m_newData.m_data.keys())
+ {
+ const QString strValue = m_newData.m_data.value(strKey).first;
+ const QString strDecription = m_newData.m_data.value(strKey).second;
+ printf(" %s: %s, %s\n", strKey.toUtf8().constData(), strValue.toUtf8().constData(), strDecription.toUtf8().constData());
+ }
+ printf("\n");
+ }
+#endif
+
+ /* If application pane is selected: */
+ if (m_pStackedLayout->currentIndex() == 0)
+ {
+ /* Update 'Apply' / 'Reset' button states: */
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_oldApplicationData != m_newApplicationData);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(m_oldApplicationData != m_newApplicationData);
+ }
+
+ /* Notify listeners as well: */
+ emit sigDataChanged(m_oldApplicationData != m_newApplicationData);
+ }
+ /* If profile pane is selected: */
+ else
+ {
+ /* Update 'Apply' / 'Reset' button states: */
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_oldProfileData != m_newProfileData);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(m_oldProfileData != m_newProfileData);
+ }
+
+ /* Notify listeners as well: */
+ emit sigDataChanged(m_oldProfileData != m_newProfileData);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleDetailsWidget.h b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleDetailsWidget.h
new file mode 100644
index 00000000..b033e898
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleDetailsWidget.h
@@ -0,0 +1,258 @@
+/* $Id: UICloudConsoleDetailsWidget.h $ */
+/** @file
+ * VBox Qt GUI - UICloudConsoleDetailsWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_cloud_consolemanager_UICloudConsoleDetailsWidget_h
+#define FEQT_INCLUDED_SRC_cloud_consolemanager_UICloudConsoleDetailsWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QLabel;
+class QLineEdit;
+class QStackedLayout;
+class QIDialogButtonBox;
+
+
+/** Cloud Console Application data structure. */
+struct UIDataCloudConsoleApplication
+{
+ /** Constructs data. */
+ UIDataCloudConsoleApplication()
+ : m_fRestricted(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataCloudConsoleApplication &other) const
+ {
+ return true
+ && (m_strId == other.m_strId)
+ && (m_strName == other.m_strName)
+ && (m_strPath == other.m_strPath)
+ && (m_strArgument == other.m_strArgument)
+ && (m_fRestricted == other.m_fRestricted)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataCloudConsoleApplication &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataCloudConsoleApplication &other) const { return !equal(other); }
+
+ /** Holds the console application ID. */
+ QString m_strId;
+ /** Holds the console application name. */
+ QString m_strName;
+ /** Holds the console application path. */
+ QString m_strPath;
+ /** Holds the console application argument. */
+ QString m_strArgument;
+ /** Holds whether console application is restricted. */
+ bool m_fRestricted;
+};
+
+/** Cloud Console Profile data structure. */
+struct UIDataCloudConsoleProfile
+{
+ /** Constructs data. */
+ UIDataCloudConsoleProfile()
+ : m_fRestricted(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataCloudConsoleProfile &other) const
+ {
+ return true
+ && (m_strApplicationId == other.m_strApplicationId)
+ && (m_strId == other.m_strId)
+ && (m_strName == other.m_strName)
+ && (m_strArgument == other.m_strArgument)
+ && (m_fRestricted == other.m_fRestricted)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataCloudConsoleProfile &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataCloudConsoleProfile &other) const { return !equal(other); }
+
+ /** Holds the console profile application ID. */
+ QString m_strApplicationId;
+ /** Holds the console profile ID. */
+ QString m_strId;
+ /** Holds the console profile name. */
+ QString m_strName;
+ /** Holds the console profile argument. */
+ QString m_strArgument;
+ /** Holds whether console profile is restricted. */
+ bool m_fRestricted;
+};
+
+
+/** Cloud Console details widget. */
+class UICloudConsoleDetailsWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data changed and whether it @a fDiffers. */
+ void sigDataChanged(bool fDiffers);
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+public:
+
+ /** Constructs cloud console details widget passing @a pParent to the base-class.
+ * @param enmEmbedding Brings embedding type. */
+ UICloudConsoleDetailsWidget(EmbedTo enmEmbedding, QWidget *pParent = 0);
+
+ /** Returns the cloud console application data. */
+ const UIDataCloudConsoleApplication &applicationData() const { return m_newApplicationData; }
+ /** Returns the cloud console profile data. */
+ const UIDataCloudConsoleProfile &profileData() const { return m_newProfileData; }
+ /** Defines the cloud console application @a data. */
+ void setApplicationData(const UIDataCloudConsoleApplication &data);
+ /** Defines the cloud console profile @a data. */
+ void setProfileData(const UIDataCloudConsoleProfile &data);
+ /** Clears all the console data. */
+ void clearData();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** @name Change handling stuff.
+ * @{ */
+ /** Handles console application name change. */
+ void sltApplicationNameChanged(const QString &strName);
+ /** Handles console application path change. */
+ void sltApplicationPathChanged(const QString &strPath);
+ /** Handles console application argument change. */
+ void sltApplicationArgumentChanged(const QString &strArgument);
+ /** Handles console profile name change. */
+ void sltProfileNameChanged(const QString &strName);
+ /** Handles console profile argument change. */
+ void sltProfileArgumentChanged(const QString &strArgument);
+
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+ /** Loads data. */
+ void loadData();
+ /** @} */
+
+ /** @name Change handling stuff.
+ * @{ */
+ /** Revalidates changes for passed @a pWidget. */
+ void revalidate(QWidget *pWidget = 0);
+
+ /** Retranslates validation for passed @a pWidget. */
+ void retranslateValidation(QWidget *pWidget = 0);
+
+ /** Updates button states. */
+ void updateButtonStates();
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the parent widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+
+ /** Holds the old console application data copy. */
+ UIDataCloudConsoleApplication m_oldApplicationData;
+ /** Holds the new console application data copy. */
+ UIDataCloudConsoleApplication m_newApplicationData;
+
+ /** Holds the old console profile data copy. */
+ UIDataCloudConsoleProfile m_oldProfileData;
+ /** Holds the new console profile data copy. */
+ UIDataCloudConsoleProfile m_newProfileData;
+ /** @} */
+
+ /** @name Widget variables.
+ * @{ */
+ /** Holds the stacked layout isntance. */
+ QStackedLayout *m_pStackedLayout;
+
+ /** Holds the application name label instance. */
+ QLabel *m_pLabelApplicationName;
+ /** Holds the application name editor instance. */
+ QLineEdit *m_pEditorApplicationName;
+ /** Holds the application path label instance. */
+ QLabel *m_pLabelApplicationPath;
+ /** Holds the application path editor instance. */
+ QLineEdit *m_pEditorApplicationPath;
+ /** Holds the application argument label instance. */
+ QLabel *m_pLabelApplicationArgument;
+ /** Holds the application argument editor instance. */
+ QLineEdit *m_pEditorApplicationArgument;
+
+ /** Holds the profile name label instance. */
+ QLabel *m_pLabelProfileName;
+ /** Holds the profile name editor instance. */
+ QLineEdit *m_pEditorProfileName;
+ /** Holds the profile argument label instance. */
+ QLabel *m_pLabelProfileArgument;
+ /** Holds the profile argument editor instance. */
+ QLineEdit *m_pEditorProfileArgument;
+
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+ /** @} */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_cloud_consolemanager_UICloudConsoleDetailsWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleManager.cpp b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleManager.cpp
new file mode 100644
index 00000000..8a53f918
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleManager.cpp
@@ -0,0 +1,1270 @@
+/* $Id: UICloudConsoleManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UICloudConsoleManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDialog>
+#include <QGridLayout>
+#include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QRegularExpression>
+#include <QUuid>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QITreeWidget.h"
+#include "UICommon.h"
+#include "UIActionPoolManager.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UICloudConsoleDetailsWidget.h"
+#include "UICloudConsoleManager.h"
+#include "UIMessageCenter.h"
+#include "QIToolBar.h"
+
+
+/** Tree-widget item types. */
+enum CloudConsoleItemType
+{
+ CloudConsoleItemType_Invalid = 0,
+ CloudConsoleItemType_Application = 1,
+ CloudConsoleItemType_Profile = 2
+};
+Q_DECLARE_METATYPE(CloudConsoleItemType);
+
+/** Tree-widget data types. */
+enum
+{
+ Data_ItemType = Qt::UserRole + 1,
+ Data_ItemID = Qt::UserRole + 2,
+ Data_Definition = Qt::UserRole + 3,
+};
+
+/** Tree-widget column types. */
+enum
+{
+ Column_Name,
+ Column_ListInMenu,
+ Column_Max
+};
+
+
+/** Cloud Console Manager application's tree-widget item. */
+class UIItemCloudConsoleApplication : public QITreeWidgetItem, public UIDataCloudConsoleApplication
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs item. */
+ UIItemCloudConsoleApplication();
+
+ /** Updates item fields from base-class data. */
+ void updateFields();
+
+ /** Returns item id. */
+ QString id() const { return m_strId; }
+ /** Returns item name. */
+ QString name() const { return m_strName; }
+ /** Returns item path. */
+ QString path() const { return m_strPath; }
+ /** Returns item argument. */
+ QString argument() const { return m_strArgument; }
+};
+
+/** Cloud Console Manager profile's tree-widget item. */
+class UIItemCloudConsoleProfile : public QITreeWidgetItem, public UIDataCloudConsoleProfile
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs item. */
+ UIItemCloudConsoleProfile();
+
+ /** Updates item fields from base-class data. */
+ void updateFields();
+
+ /** Returns item application id. */
+ QString applicationId() const { return m_strApplicationId; }
+ /** Returns item id. */
+ QString id() const { return m_strId; }
+ /** Returns item name. */
+ QString name() const { return m_strName; }
+ /** Returns item argument. */
+ QString argument() const { return m_strArgument; }
+};
+
+/** QDialog extension used to acquire newly created console application parameters. */
+class UIInputDialogCloudConsoleApplication : public QIWithRetranslateUI<QDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs dialog passing @a pParent to the base-class. */
+ UIInputDialogCloudConsoleApplication(QWidget *pParent);
+
+ /** Returns application name. */
+ QString name() const;
+ /** Returns application path. */
+ QString path() const;
+ /** Returns application argument. */
+ QString argument() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the name editor instance. */
+ QLineEdit *m_pEditorName;
+ /** Holds the path label instance. */
+ QLabel *m_pLabelPath;
+ /** Holds the path editor instance. */
+ QLineEdit *m_pEditorPath;
+ /** Holds the argument label instance. */
+ QLabel *m_pLabelArgument;
+ /** Holds the argument editor instance. */
+ QLineEdit *m_pEditorArgument;
+
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+};
+
+/** QDialog extension used to acquire newly created console profile parameters. */
+class UIInputDialogCloudConsoleProfile : public QIWithRetranslateUI<QDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs dialog passing @a pParent to the base-class. */
+ UIInputDialogCloudConsoleProfile(QWidget *pParent);
+
+ /** Returns profile name. */
+ QString name() const;
+ /** Returns profile argument. */
+ QString argument() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the name editor instance. */
+ QLineEdit *m_pEditorName;
+ /** Holds the argument label instance. */
+ QLabel *m_pLabelArgument;
+ /** Holds the argument editor instance. */
+ QLineEdit *m_pEditorArgument;
+
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIItemCloudConsoleApplication implementation. *
+*********************************************************************************************************************************/
+
+UIItemCloudConsoleApplication::UIItemCloudConsoleApplication()
+{
+ /* Assign icon: */
+ setIcon(Column_Name, UIIconPool::iconSet(":/cloud_console_application_16px.png"));
+ /* Assign item data: */
+ setData(Column_Name, Data_ItemType, QVariant::fromValue(CloudConsoleItemType_Application));
+}
+
+void UIItemCloudConsoleApplication::updateFields()
+{
+ /* Update item fields: */
+ setText(Column_Name, m_strName);
+ setData(Column_Name, Data_ItemID, m_strId);
+ setData(Column_Name, Data_Definition, QVariant::fromValue(QString("/%1").arg(m_strId)));
+ setCheckState(Column_ListInMenu, m_fRestricted ? Qt::Unchecked : Qt::Checked);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIItemCloudConsoleProfile implementation. *
+*********************************************************************************************************************************/
+
+UIItemCloudConsoleProfile::UIItemCloudConsoleProfile()
+{
+ /* Assign icon: */
+ setIcon(Column_Name, UIIconPool::iconSet(":/cloud_console_profile_16px.png"));
+ /* Assign item data: */
+ setData(Column_Name, Data_ItemType, QVariant::fromValue(CloudConsoleItemType_Profile));
+}
+
+void UIItemCloudConsoleProfile::updateFields()
+{
+ /* Update item fields: */
+ setText(Column_Name, m_strName);
+ setData(Column_Name, Data_ItemID, m_strId);
+ setData(Column_Name, Data_Definition, QVariant::fromValue(QString("/%1/%2").arg(m_strApplicationId, m_strId)));
+ setCheckState(Column_ListInMenu, m_fRestricted ? Qt::Unchecked : Qt::Checked);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIInputDialogCloudConsoleApplication implementation. *
+*********************************************************************************************************************************/
+
+UIInputDialogCloudConsoleApplication::UIInputDialogCloudConsoleApplication(QWidget *pParent)
+ : QIWithRetranslateUI<QDialog>(pParent)
+ , m_pLabelName(0)
+ , m_pEditorName(0)
+ , m_pLabelPath(0)
+ , m_pEditorPath(0)
+ , m_pLabelArgument(0)
+ , m_pEditorArgument(0)
+ , m_pButtonBox(0)
+{
+ prepare();
+}
+
+QString UIInputDialogCloudConsoleApplication::name() const
+{
+ return m_pEditorName->text();
+}
+
+QString UIInputDialogCloudConsoleApplication::path() const
+{
+ return m_pEditorPath->text();
+}
+
+QString UIInputDialogCloudConsoleApplication::argument() const
+{
+ return m_pEditorArgument->text();
+}
+
+void UIInputDialogCloudConsoleApplication::retranslateUi()
+{
+ setWindowTitle(UICloudConsoleManager::tr("Add Application"));
+ m_pLabelName->setText(UICloudConsoleManager::tr("Name:"));
+ m_pLabelPath->setText(UICloudConsoleManager::tr("Path:"));
+ m_pLabelArgument->setText(UICloudConsoleManager::tr("Argument:"));
+}
+
+void UIInputDialogCloudConsoleApplication::prepare()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/cloud_console_application_add_32px.png" ,":/cloud_console_application_add_16px.png"));
+#endif
+
+ /* Prepare main layout: */
+ QGridLayout *pMainLayout = new QGridLayout(this);
+ if (pMainLayout)
+ {
+ pMainLayout->setRowStretch(3, 1);
+
+ /* Prepare name editor: */
+ m_pEditorName = new QLineEdit(this);
+ if (m_pEditorName)
+ {
+ pMainLayout->addWidget(m_pEditorName, 0, 1);
+ }
+ /* Prepare name editor label: */
+ m_pLabelName = new QLabel(this);
+ if (m_pLabelName)
+ {
+ m_pLabelName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelName->setBuddy(m_pEditorName);
+ pMainLayout->addWidget(m_pLabelName, 0, 0);
+ }
+
+ /* Prepare path editor: */
+ m_pEditorPath = new QLineEdit(this);
+ if (m_pEditorPath)
+ {
+ pMainLayout->addWidget(m_pEditorPath, 1, 1);
+ }
+ /* Prepare path editor label: */
+ m_pLabelPath = new QLabel(this);
+ if (m_pLabelPath)
+ {
+ m_pLabelPath->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelPath->setBuddy(m_pEditorPath);
+ pMainLayout->addWidget(m_pLabelPath, 1, 0);
+ }
+
+ /* Prepare argument editor: */
+ m_pEditorArgument = new QLineEdit(this);
+ if (m_pEditorArgument)
+ {
+ pMainLayout->addWidget(m_pEditorArgument, 2, 1);
+ }
+ /* Prepare argument editor label: */
+ m_pLabelArgument = new QLabel(this);
+ if (m_pLabelArgument)
+ {
+ m_pLabelArgument->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelArgument->setBuddy(m_pEditorArgument);
+ pMainLayout->addWidget(m_pLabelArgument, 2, 0);
+ }
+
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox(this);
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UIInputDialogCloudConsoleApplication::reject);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UIInputDialogCloudConsoleApplication::accept);
+ pMainLayout->addWidget(m_pButtonBox, 4, 0, 1, 2);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Resize to suitable size: */
+ const int iMinimumHeightHint = minimumSizeHint().height();
+ resize(iMinimumHeightHint * 3, iMinimumHeightHint);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIInputDialogCloudConsoleProfile implementation. *
+*********************************************************************************************************************************/
+
+UIInputDialogCloudConsoleProfile::UIInputDialogCloudConsoleProfile(QWidget *pParent)
+ : QIWithRetranslateUI<QDialog>(pParent)
+ , m_pLabelName(0)
+ , m_pEditorName(0)
+ , m_pLabelArgument(0)
+ , m_pEditorArgument(0)
+ , m_pButtonBox(0)
+{
+ prepare();
+}
+
+QString UIInputDialogCloudConsoleProfile::name() const
+{
+ return m_pEditorName->text();
+}
+
+QString UIInputDialogCloudConsoleProfile::argument() const
+{
+ return m_pEditorArgument->text();
+}
+
+void UIInputDialogCloudConsoleProfile::retranslateUi()
+{
+ setWindowTitle(UICloudConsoleManager::tr("Add Profile"));
+ m_pLabelName->setText(UICloudConsoleManager::tr("Name:"));
+ m_pLabelArgument->setText(UICloudConsoleManager::tr("Argument:"));
+}
+
+void UIInputDialogCloudConsoleProfile::prepare()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/cloud_console_profile_add_32px.png", ":/cloud_console_profile_add_16px.png"));
+#endif
+
+ /* Prepare main layout: */
+ QGridLayout *pMainLayout = new QGridLayout(this);
+ if (pMainLayout)
+ {
+ pMainLayout->setRowStretch(0, 0);
+ pMainLayout->setRowStretch(1, 0);
+ pMainLayout->setRowStretch(2, 1);
+ pMainLayout->setRowStretch(3, 0);
+
+ /* Prepare name editor: */
+ m_pEditorName = new QLineEdit(this);
+ if (m_pEditorName)
+ {
+ pMainLayout->addWidget(m_pEditorName, 0, 1);
+ }
+ /* Prepare name editor label: */
+ m_pLabelName = new QLabel(this);
+ if (m_pLabelName)
+ {
+ m_pLabelName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelName->setBuddy(m_pEditorName);
+ pMainLayout->addWidget(m_pLabelName, 0, 0);
+ }
+
+ /* Prepare argument editor: */
+ m_pEditorArgument = new QLineEdit(this);
+ if (m_pEditorArgument)
+ {
+ pMainLayout->addWidget(m_pEditorArgument, 1, 1);
+ }
+ /* Prepare argument editor label: */
+ m_pLabelArgument = new QLabel(this);
+ if (m_pLabelArgument)
+ {
+ m_pLabelArgument->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelArgument->setBuddy(m_pEditorArgument);
+ pMainLayout->addWidget(m_pLabelArgument, 1, 0);
+ }
+
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox(this);
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UIInputDialogCloudConsoleApplication::reject);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UIInputDialogCloudConsoleApplication::accept);
+ pMainLayout->addWidget(m_pButtonBox, 3, 0, 1, 2);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Resize to suitable size: */
+ const int iMinimumHeightHint = minimumSizeHint().height();
+ resize(iMinimumHeightHint * 3, iMinimumHeightHint);
+}
+
+
+/*********************************************************************************************************************************
+* Class UICloudConsoleManagerWidget implementation. *
+*********************************************************************************************************************************/
+
+UICloudConsoleManagerWidget::UICloudConsoleManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar /* = true */, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_pToolBar(0)
+ , m_pTreeWidget(0)
+ , m_pDetailsWidget(0)
+{
+ prepare();
+}
+
+QMenu *UICloudConsoleManagerWidget::menu() const
+{
+ return m_pActionPool->action(UIActionIndexMN_M_CloudConsoleWindow)->menu();
+}
+
+void UICloudConsoleManagerWidget::retranslateUi()
+{
+ /* Adjust toolbar: */
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text.
+ if (m_pToolBar)
+ m_pToolBar->updateLayout();
+#endif
+
+ /* Translate tree-widget: */
+ m_pTreeWidget->setHeaderLabels( QStringList()
+ << UICloudConsoleManager::tr("Application")
+ << UICloudConsoleManager::tr("List in Menu"));
+}
+
+void UICloudConsoleManagerWidget::sltResetCloudConsoleDetailsChanges()
+{
+ /* Just push the current-item data there again: */
+ sltHandleCurrentItemChange();
+}
+
+void UICloudConsoleManagerWidget::sltApplyCloudConsoleDetailsChanges()
+{
+ /* Check current-item type: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ AssertMsgReturnVoid(pItem, ("Current item must not be null!\n"));
+ switch (pItem->data(Column_Name, Data_ItemType).value<CloudConsoleItemType>())
+ {
+ case CloudConsoleItemType_Application:
+ {
+ /* Save application changes: */
+ UIItemCloudConsoleApplication *pItemApplication = qobject_cast<UIItemCloudConsoleApplication*>(pItem);
+ AssertPtrReturnVoid(pItemApplication);
+ const UIDataCloudConsoleApplication oldData = *pItemApplication;
+ const UIDataCloudConsoleApplication newData = m_pDetailsWidget->applicationData();
+ /* Save application settings if changed: */
+ if (newData != oldData)
+ gEDataManager->setCloudConsoleManagerApplication(newData.m_strId,
+ QString("%1,%2,%3").arg(newData.m_strName,
+ newData.m_strPath,
+ newData.m_strArgument));
+ break;
+ }
+ case CloudConsoleItemType_Profile:
+ {
+ /* Save profile changes: */
+ UIItemCloudConsoleProfile *pItemProfile = qobject_cast<UIItemCloudConsoleProfile*>(pItem);
+ AssertPtrReturnVoid(pItemProfile);
+ const UIDataCloudConsoleProfile oldData = *pItemProfile;
+ const UIDataCloudConsoleProfile newData = m_pDetailsWidget->profileData();
+ /* Save profile settings if changed: */
+ if (newData != oldData)
+ gEDataManager->setCloudConsoleManagerProfile(newData.m_strApplicationId,
+ newData.m_strId,
+ QString("%1,%2").arg(newData.m_strName, newData.m_strArgument));
+ break;
+ }
+ case CloudConsoleItemType_Invalid:
+ break;
+ }
+}
+
+void UICloudConsoleManagerWidget::sltAddCloudConsoleApplication()
+{
+ /* Acquire application attributes: */
+ QString strId;
+ QString strApplicationName;
+ QString strApplicationPath;
+ QString strApplicationArgument;
+ bool fCancelled = true;
+ QPointer<UIInputDialogCloudConsoleApplication> pDialog = new UIInputDialogCloudConsoleApplication(this);
+ if (pDialog)
+ {
+ if (pDialog->exec() == QDialog::Accepted)
+ {
+ strId = QUuid::createUuid().toString().remove(QRegularExpression("[{}]"));
+ strApplicationName = pDialog->name();
+ strApplicationPath = pDialog->path();
+ strApplicationArgument = pDialog->argument();
+ fCancelled = false;
+ }
+ delete pDialog;
+ }
+ if (fCancelled)
+ return;
+
+ /* Update current-item definition: */
+ m_strDefinition = QString("/%1").arg(strId);
+ /* Compose extra-data superset: */
+ const QString strValue = QString("%1,%2,%3").arg(strApplicationName, strApplicationPath, strApplicationArgument);
+
+ /* Save new console application to extra-data: */
+ gEDataManager->setCloudConsoleManagerApplication(strId, strValue);
+}
+
+void UICloudConsoleManagerWidget::sltRemoveCloudConsoleApplication()
+{
+ /* Get console application item: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemCloudConsoleApplication *pItemApplication = qobject_cast<UIItemCloudConsoleApplication*>(pItem);
+ AssertMsgReturnVoid(pItemApplication, ("Application item must not be null!\n"));
+ const QString strApplicationId = pItemApplication->id();
+
+ /* Confirm cloud console application removal: */
+ if (!msgCenter().confirmCloudConsoleApplicationRemoval(pItemApplication->name(), this))
+ return;
+
+ /* Enumerate all the application profiles: */
+ for (int i = 0; i < pItemApplication->childCount(); ++i)
+ {
+ /* Get console profile item: */
+ QITreeWidgetItem *pItem = pItemApplication->childItem(i);
+ UIItemCloudConsoleProfile *pItemProfile = qobject_cast<UIItemCloudConsoleProfile*>(pItem);
+ AssertMsgReturnVoid(pItemProfile, ("Profile item must not be null!\n"));
+
+ /* Delete profile from extra-data: */
+ gEDataManager->setCloudConsoleManagerProfile(strApplicationId, pItemProfile->id(), QString());
+ }
+
+ /* Delete application from extra-data: */
+ gEDataManager->setCloudConsoleManagerApplication(strApplicationId, QString());
+}
+
+void UICloudConsoleManagerWidget::sltAddCloudConsoleProfile()
+{
+ /* Check current-item type: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ AssertMsgReturnVoid(pItem, ("Current item must not be null!\n"));
+ UIItemCloudConsoleApplication *pItemApplication = 0;
+ switch (pItem->data(Column_Name, Data_ItemType).value<CloudConsoleItemType>())
+ {
+ case CloudConsoleItemType_Application:
+ pItemApplication = qobject_cast<UIItemCloudConsoleApplication*>(pItem);
+ break;
+ case CloudConsoleItemType_Profile:
+ pItemApplication = qobject_cast<UIItemCloudConsoleApplication*>(pItem->parentItem());
+ break;
+ case CloudConsoleItemType_Invalid:
+ break;
+ }
+ AssertMsgReturnVoid(pItemApplication, ("Application item must not be null!\n"));
+ const QString strApplicationId = pItemApplication->id();
+
+ /* Acquire profile attributes: */
+ QString strId;
+ QString strProfileName;
+ QString strProfileArgument;
+ bool fCancelled = true;
+ QPointer<UIInputDialogCloudConsoleProfile> pDialog = new UIInputDialogCloudConsoleProfile(this);
+ if (pDialog)
+ {
+ if (pDialog->exec() == QDialog::Accepted)
+ {
+ strId = QUuid::createUuid().toString().remove(QRegularExpression("[{}]"));
+ strProfileName = pDialog->name();
+ strProfileArgument = pDialog->argument();
+ fCancelled = false;
+ }
+ delete pDialog;
+ }
+ if (fCancelled)
+ return;
+
+ /* Update current-item definition: */
+ m_strDefinition = QString("/%1/%2").arg(strApplicationId, strId);
+ /* Compose extra-data superset: */
+ const QString strValue = QString("%1,%2").arg(strProfileName, strProfileArgument);
+
+ /* Save new console profile to extra-data: */
+ gEDataManager->setCloudConsoleManagerProfile(strApplicationId, strId, strValue);
+}
+
+void UICloudConsoleManagerWidget::sltRemoveCloudConsoleProfile()
+{
+ /* Get console profile item: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemCloudConsoleProfile *pItemProfile = qobject_cast<UIItemCloudConsoleProfile*>(pItem);
+ AssertMsgReturnVoid(pItemProfile, ("Profile item must not be null!\n"));
+
+ /* Confirm cloud console profile removal: */
+ if (!msgCenter().confirmCloudConsoleProfileRemoval(pItemProfile->name(), this))
+ return;
+
+ /* Delete profile from extra-data: */
+ gEDataManager->setCloudConsoleManagerProfile(pItemProfile->applicationId(), pItemProfile->id(), QString());
+}
+
+void UICloudConsoleManagerWidget::sltToggleCloudConsoleDetailsVisibility(bool fVisible)
+{
+ /* Save the setting: */
+ gEDataManager->setCloudConsoleManagerDetailsExpanded(fVisible);
+ /* Show/hide details area and Apply button: */
+ m_pDetailsWidget->setVisible(fVisible);
+ /* Notify external lsiteners: */
+ emit sigCloudConsoleDetailsVisibilityChanged(fVisible);
+}
+
+void UICloudConsoleManagerWidget::sltPerformTableAdjustment()
+{
+ AssertPtrReturnVoid(m_pTreeWidget);
+ AssertPtrReturnVoid(m_pTreeWidget->header());
+ AssertPtrReturnVoid(m_pTreeWidget->viewport());
+ m_pTreeWidget->header()->resizeSection(0, m_pTreeWidget->viewport()->width() - m_pTreeWidget->header()->sectionSize(1));
+}
+
+void UICloudConsoleManagerWidget::sltHandleCurrentItemChange()
+{
+ /* Check current-item type: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemCloudConsoleApplication *pItemApplication = qobject_cast<UIItemCloudConsoleApplication*>(pItem);
+ UIItemCloudConsoleProfile *pItemProfile = qobject_cast<UIItemCloudConsoleProfile*>(pItem);
+
+ /* Update actions availability: */
+ m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationAdd)->setEnabled(!pItem || pItemApplication);
+ m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationRemove)->setEnabled(pItemApplication);
+ m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileAdd)->setEnabled(pItemApplication || pItemProfile);
+ m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileRemove)->setEnabled(pItemProfile);
+ m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details)->setEnabled(pItemApplication || pItemProfile);
+
+ /* Update current-item definition: */
+ if (pItem)
+ m_strDefinition = pItem->data(Column_Name, Data_Definition).toString();
+
+ /* Update details data: */
+ if (pItemApplication)
+ m_pDetailsWidget->setApplicationData(*pItemApplication);
+ else if (pItemProfile)
+ m_pDetailsWidget->setProfileData(*pItemProfile);
+ else
+ m_pDetailsWidget->clearData();
+
+ /* Update details area visibility: */
+ sltToggleCloudConsoleDetailsVisibility(pItem && m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details)->isChecked());
+}
+
+void UICloudConsoleManagerWidget::sltHandleContextMenuRequest(const QPoint &position)
+{
+ /* Check item type: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->itemAt(position));
+ UIItemCloudConsoleApplication *pItemApplication = qobject_cast<UIItemCloudConsoleApplication*>(pItem);
+ UIItemCloudConsoleProfile *pItemProfile = qobject_cast<UIItemCloudConsoleProfile*>(pItem);
+
+ /* Compose temporary context-menu: */
+ QMenu menu;
+ if (pItemApplication)
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationRemove));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileAdd));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details));
+ }
+ else if (pItemProfile)
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileRemove));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details));
+ }
+ else
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationAdd));
+
+ /* And show it: */
+ menu.exec(m_pTreeWidget->viewport()->mapToGlobal(position));
+}
+
+void UICloudConsoleManagerWidget::sltHandleItemChange(QTreeWidgetItem *pItem)
+{
+ /* Check item type: */
+ QITreeWidgetItem *pChangedItem = QITreeWidgetItem::toItem(pItem);
+ UIItemCloudConsoleApplication *pItemApplication = qobject_cast<UIItemCloudConsoleApplication*>(pChangedItem);
+ UIItemCloudConsoleProfile *pItemProfile = qobject_cast<UIItemCloudConsoleProfile*>(pChangedItem);
+
+ /* Check whether item is of application or profile type, then check whether it changed: */
+ bool fChanged = false;
+ if (pItemApplication)
+ {
+ const UIDataCloudConsoleApplication oldData = *pItemApplication;
+ if ( (oldData.m_fRestricted && pItemApplication->checkState(Column_ListInMenu) == Qt::Checked)
+ || (!oldData.m_fRestricted && pItemApplication->checkState(Column_ListInMenu) == Qt::Unchecked))
+ fChanged = true;
+ }
+ else if (pItemProfile)
+ {
+ const UIDataCloudConsoleProfile oldData = *pItemProfile;
+ if ( (oldData.m_fRestricted && pItemProfile->checkState(Column_ListInMenu) == Qt::Checked)
+ || (!oldData.m_fRestricted && pItemProfile->checkState(Column_ListInMenu) == Qt::Unchecked))
+ fChanged = true;
+ }
+
+ /* Gather Cloud Console Manager restrictions and save them to extra-data: */
+ if (fChanged)
+ gEDataManager->setCloudConsoleManagerRestrictions(gatherCloudConsoleManagerRestrictions(m_pTreeWidget->invisibleRootItem()));
+}
+
+void UICloudConsoleManagerWidget::prepare()
+{
+ /* Prepare actions: */
+ prepareActions();
+ /* Prepare widgets: */
+ prepareWidgets();
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Load cloud console stuff: */
+ loadCloudConsoleStuff();
+}
+
+void UICloudConsoleManagerWidget::prepareActions()
+{
+ /* First of all, add actions which has smaller shortcut scope: */
+ addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationAdd));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationRemove));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileAdd));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileRemove));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details));
+}
+
+void UICloudConsoleManagerWidget::prepareWidgets()
+{
+ /* Create main-layout: */
+ new QVBoxLayout(this);
+ if (layout())
+ {
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ layout()->setSpacing(10);
+#else
+ layout()->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ /* Prepare toolbar, if requested: */
+ if (m_fShowToolbar)
+ prepareToolBar();
+ /* Prepare tree-widget: */
+ prepareTreeWidget();
+ /* Prepare details-widget: */
+ prepareDetailsWidget();
+ /* Prepare connections: */
+ prepareConnections();
+ }
+}
+
+void UICloudConsoleManagerWidget::prepareToolBar()
+{
+ /* Create toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ if (m_pToolBar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+
+ /* Add toolbar actions: */
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationAdd));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationRemove));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileAdd));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileRemove));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details));
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+ }
+#else
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+#endif
+ }
+}
+
+void UICloudConsoleManagerWidget::prepareTreeWidget()
+{
+ /* Create tree-widget: */
+ m_pTreeWidget = new QITreeWidget;
+ if (m_pTreeWidget)
+ {
+ /* Configure tree-widget: */
+ m_pTreeWidget->header()->setStretchLastSection(false);
+ m_pTreeWidget->setRootIsDecorated(false);
+ m_pTreeWidget->setAlternatingRowColors(true);
+ m_pTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTreeWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTreeWidget->setColumnCount(Column_Max);
+ m_pTreeWidget->setSortingEnabled(true);
+ m_pTreeWidget->sortByColumn(Column_Name, Qt::AscendingOrder);
+ m_pTreeWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pTreeWidget);
+ }
+}
+
+void UICloudConsoleManagerWidget::prepareDetailsWidget()
+{
+ /* Create details-widget: */
+ m_pDetailsWidget = new UICloudConsoleDetailsWidget(m_enmEmbedding);
+ if (m_pDetailsWidget)
+ {
+ /* Configure details-widget: */
+ m_pDetailsWidget->setVisible(false);
+ m_pDetailsWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pDetailsWidget);
+ }
+}
+
+void UICloudConsoleManagerWidget::prepareConnections()
+{
+ /* Action connections: */
+ connect(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationAdd), &QAction::triggered,
+ this, &UICloudConsoleManagerWidget::sltAddCloudConsoleApplication);
+ connect(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ApplicationRemove), &QAction::triggered,
+ this, &UICloudConsoleManagerWidget::sltRemoveCloudConsoleApplication);
+ connect(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileAdd), &QAction::triggered,
+ this, &UICloudConsoleManagerWidget::sltAddCloudConsoleProfile);
+ connect(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_S_ProfileRemove), &QAction::triggered,
+ this, &UICloudConsoleManagerWidget::sltRemoveCloudConsoleProfile);
+ connect(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details), &QAction::toggled,
+ this, &UICloudConsoleManagerWidget::sltToggleCloudConsoleDetailsVisibility);
+
+ /* Tree-widget connections: */
+ connect(m_pTreeWidget, &QITreeWidget::resized,
+ this, &UICloudConsoleManagerWidget::sltPerformTableAdjustment, Qt::QueuedConnection);
+ connect(m_pTreeWidget->header(), &QHeaderView::sectionResized,
+ this, &UICloudConsoleManagerWidget::sltPerformTableAdjustment, Qt::QueuedConnection);
+ connect(m_pTreeWidget, &QITreeWidget::currentItemChanged,
+ this, &UICloudConsoleManagerWidget::sltHandleCurrentItemChange);
+ connect(m_pTreeWidget, &QITreeWidget::customContextMenuRequested,
+ this, &UICloudConsoleManagerWidget::sltHandleContextMenuRequest);
+ connect(m_pTreeWidget, &QITreeWidget::itemDoubleClicked,
+ m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details), &QAction::setChecked);
+ connect(m_pTreeWidget, &QITreeWidget::itemChanged,
+ this, &UICloudConsoleManagerWidget::sltHandleItemChange);
+
+ /* Details-widget connections: */
+ connect(m_pDetailsWidget, &UICloudConsoleDetailsWidget::sigDataChanged,
+ this, &UICloudConsoleManagerWidget::sigCloudConsoleDetailsDataChanged);
+ connect(m_pDetailsWidget, &UICloudConsoleDetailsWidget::sigDataChangeRejected,
+ this, &UICloudConsoleManagerWidget::sltResetCloudConsoleDetailsChanges);
+ connect(m_pDetailsWidget, &UICloudConsoleDetailsWidget::sigDataChangeAccepted,
+ this, &UICloudConsoleManagerWidget::sltApplyCloudConsoleDetailsChanges);
+
+ /* Extra-data connections: */
+ connect(gEDataManager, &UIExtraDataManager::sigCloudConsoleManagerDataChange,
+ this, &UICloudConsoleManagerWidget::sltLoadCloudConsoleStuff);
+ connect(gEDataManager, &UIExtraDataManager::sigCloudConsoleManagerRestrictionChange,
+ this, &UICloudConsoleManagerWidget::sltLoadCloudConsoleStuff);
+}
+
+void UICloudConsoleManagerWidget::loadSettings()
+{
+ /* Details action/widget: */
+ m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details)->setChecked(gEDataManager->cloudConsoleManagerDetailsExpanded());
+ sltToggleCloudConsoleDetailsVisibility(m_pActionPool->action(UIActionIndexMN_M_CloudConsole_T_Details)->isChecked());
+}
+
+void UICloudConsoleManagerWidget::loadCloudConsoleStuff()
+{
+ /* Clear tree first of all: */
+ m_pTreeWidget->clear();
+
+ /* Acquire cloud console manager restrictions: */
+ const QStringList restrictions = gEDataManager->cloudConsoleManagerRestrictions();
+
+ /* Iterate through existing console applications: */
+ foreach (const QString &strApplicationId, gEDataManager->cloudConsoleManagerApplications())
+ {
+ /* Skip if we have nothing to populate: */
+ if (strApplicationId.isEmpty())
+ continue;
+
+ /* Compose extra-data superset: */
+ const QString strApplicationValue = gEDataManager->cloudConsoleManagerApplication(strApplicationId);
+ const QString strApplicationSuperset = QString("%1,%2").arg(strApplicationId, strApplicationValue);
+
+ /* Load console application data: */
+ UIDataCloudConsoleApplication applicationData;
+ loadCloudConsoleApplication(strApplicationSuperset, applicationData);
+ const QString strApplicationDefinition = QString("/%1").arg(applicationData.m_strId);
+ applicationData.m_fRestricted = restrictions.contains(strApplicationDefinition);
+ createItemForCloudConsoleApplication(applicationData, false);
+
+ /* Make sure console applications item is properly inserted: */
+ UIItemCloudConsoleApplication *pItem = searchApplicationItem(applicationData.m_strId);
+
+ /* Iterate through applications's profiles: */
+ foreach (const QString &strProfileId, gEDataManager->cloudConsoleManagerProfiles(strApplicationId))
+ {
+ /* Skip if we have nothing to populate: */
+ if (strProfileId.isEmpty())
+ continue;
+
+ /* Compose extra-data superset: */
+ const QString strProfileValue = gEDataManager->cloudConsoleManagerProfile(strApplicationId, strProfileId);
+ const QString strProfileSuperset = QString("%1,%2").arg(strProfileId, strProfileValue);
+
+ /* Load console profile data: */
+ UIDataCloudConsoleProfile profileData;
+ loadCloudConsoleProfile(strProfileSuperset, applicationData, profileData);
+ const QString strProfileDefinition = QString("/%1/%2").arg(applicationData.m_strId).arg(profileData.m_strId);
+ profileData.m_fRestricted = restrictions.contains(strProfileDefinition);
+ createItemForCloudConsoleProfile(pItem, profileData, false);
+ }
+
+ /* Expand console application item finally: */
+ pItem->setExpanded(true);
+ }
+
+ /* Choose previous current-item if possible: */
+ if (!m_strDefinition.isEmpty())
+ m_pTreeWidget->setCurrentItem(searchItemByDefinition(m_strDefinition));
+ /* Choose the 1st item as current if nothing chosen: */
+ if (!m_pTreeWidget->currentItem())
+ m_pTreeWidget->setCurrentItem(m_pTreeWidget->childItem(0));
+ /* Make sure current-item is fetched: */
+ sltHandleCurrentItemChange();
+}
+
+void UICloudConsoleManagerWidget::loadCloudConsoleApplication(const QString &strSuperset,
+ UIDataCloudConsoleApplication &applicationData)
+{
+ /* Parse superset: */
+ const QStringList values = strSuperset.split(',');
+
+ /* Gather application settings: */
+ applicationData.m_strId = values.value(0);
+ applicationData.m_strName = values.value(1);
+ applicationData.m_strPath = values.value(2);
+ applicationData.m_strArgument = values.value(3);
+}
+
+void UICloudConsoleManagerWidget::loadCloudConsoleProfile(const QString &strSuperset,
+ const UIDataCloudConsoleApplication &applicationData,
+ UIDataCloudConsoleProfile &profileData)
+{
+ /* Gather application settings: */
+ profileData.m_strApplicationId = applicationData.m_strId;
+
+ /* Parse superset: */
+ const QStringList values = strSuperset.split(',');
+
+ /* Gather profile settings: */
+ profileData.m_strId = values.value(0);
+ profileData.m_strName = values.value(1);
+ profileData.m_strArgument = values.value(2);
+}
+
+UIItemCloudConsoleApplication *UICloudConsoleManagerWidget::searchApplicationItem(const QString &strApplicationId) const
+{
+ /* Iterate through tree-widget children: */
+ for (int i = 0; i < m_pTreeWidget->childCount(); ++i)
+ if (m_pTreeWidget->childItem(i)->data(Column_Name, Data_ItemID).toString() == strApplicationId)
+ return qobject_cast<UIItemCloudConsoleApplication*>(m_pTreeWidget->childItem(i));
+ /* Null by default: */
+ return 0;
+}
+
+UIItemCloudConsoleProfile *UICloudConsoleManagerWidget::searchProfileItem(const QString &strApplicationId,
+ const QString &strProfileId) const
+{
+ /* Search for application item first: */
+ UIItemCloudConsoleApplication *pItemApplication = searchApplicationItem(strApplicationId);
+ /* Iterate through application children: */
+ for (int i = 0; i < pItemApplication->childCount(); ++i)
+ if (pItemApplication->childItem(i)->data(Column_Name, Data_ItemID).toString() == strProfileId)
+ return qobject_cast<UIItemCloudConsoleProfile*>(pItemApplication->childItem(i));
+ /* Null by default: */
+ return 0;
+}
+
+QITreeWidgetItem *UICloudConsoleManagerWidget::searchItemByDefinition(const QString &strDefinition) const
+{
+ /* Parse definition: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ const QStringList parts = strDefinition.split('/', Qt::SkipEmptyParts);
+#else
+ const QStringList parts = strDefinition.split('/', QString::SkipEmptyParts);
+#endif
+ /* Depending on parts amount: */
+ switch (parts.size())
+ {
+ case 1: return searchApplicationItem(parts.at(0));
+ case 2: return searchProfileItem(parts.at(0), parts.at(1));
+ default: break;
+ }
+ /* Null by default: */
+ return 0;
+}
+
+void UICloudConsoleManagerWidget::createItemForCloudConsoleApplication(const UIDataCloudConsoleApplication &applicationData,
+ bool fChooseItem)
+{
+ /* Create new console application item: */
+ UIItemCloudConsoleApplication *pItem = new UIItemCloudConsoleApplication;
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->UIDataCloudConsoleApplication::operator=(applicationData);
+ pItem->updateFields();
+ /* Add item to the tree: */
+ m_pTreeWidget->addTopLevelItem(pItem);
+ /* And choose it as current if necessary: */
+ if (fChooseItem)
+ m_pTreeWidget->setCurrentItem(pItem);
+ }
+}
+
+void UICloudConsoleManagerWidget::createItemForCloudConsoleProfile(QTreeWidgetItem *pParent,
+ const UIDataCloudConsoleProfile &profileData,
+ bool fChooseItem)
+{
+ /* Create new console profile item: */
+ UIItemCloudConsoleProfile *pItem = new UIItemCloudConsoleProfile;
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->UIDataCloudConsoleProfile::operator=(profileData);
+ pItem->updateFields();
+ /* Add item to the parent: */
+ pParent->addChild(pItem);
+ /* And choose it as current if necessary: */
+ if (fChooseItem)
+ m_pTreeWidget->setCurrentItem(pItem);
+ }
+}
+
+QStringList UICloudConsoleManagerWidget::gatherCloudConsoleManagerRestrictions(QTreeWidgetItem *pParentItem)
+{
+ /* Prepare result: */
+ QStringList result;
+ AssertPtrReturn(pParentItem, result);
+
+ /* Process unchecked QITreeWidgetItem(s) only: */
+ QITreeWidgetItem *pChangedItem = QITreeWidgetItem::toItem(pParentItem);
+ if ( pChangedItem
+ && pChangedItem->checkState(Column_ListInMenu) == Qt::Unchecked)
+ result << pChangedItem->data(Column_Name, Data_Definition).toString();
+
+ /* Iterate through children recursively: */
+ for (int i = 0; i < pParentItem->childCount(); ++i)
+ result << gatherCloudConsoleManagerRestrictions(pParentItem->child(i));
+
+ /* Return result: */
+ return result;
+}
+
+
+/*********************************************************************************************************************************
+* Class UICloudConsoleManagerFactory implementation. *
+*********************************************************************************************************************************/
+
+UICloudConsoleManagerFactory::UICloudConsoleManagerFactory(UIActionPool *pActionPool /* = 0 */)
+ : m_pActionPool(pActionPool)
+{
+}
+
+void UICloudConsoleManagerFactory::create(QIManagerDialog *&pDialog, QWidget *pCenterWidget)
+{
+ pDialog = new UICloudConsoleManager(pCenterWidget, m_pActionPool);
+}
+
+
+/*********************************************************************************************************************************
+* Class UICloudConsoleManager implementation. *
+*********************************************************************************************************************************/
+
+UICloudConsoleManager::UICloudConsoleManager(QWidget *pCenterWidget, UIActionPool *pActionPool)
+ : QIWithRetranslateUI<QIManagerDialog>(pCenterWidget)
+ , m_pActionPool(pActionPool)
+{
+}
+
+void UICloudConsoleManager::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+ /* Disable buttons first of all: */
+ button(ButtonType_Reset)->setEnabled(false);
+ button(ButtonType_Apply)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if (pButton == button(ButtonType_Reset))
+ emit sigDataChangeRejected();
+ else
+ if (pButton == button(ButtonType_Apply))
+ emit sigDataChangeAccepted();
+}
+
+void UICloudConsoleManager::retranslateUi()
+{
+ /* Translate window title: */
+ setWindowTitle(tr("Cloud Console Manager"));
+
+ /* Translate buttons: */
+ button(ButtonType_Reset)->setText(tr("Reset"));
+ button(ButtonType_Apply)->setText(tr("Apply"));
+ button(ButtonType_Close)->setText(tr("Close"));
+ button(ButtonType_Reset)->setStatusTip(tr("Reset changes in current cloud console details"));
+ button(ButtonType_Apply)->setStatusTip(tr("Apply changes in current cloud console details"));
+ button(ButtonType_Close)->setStatusTip(tr("Close dialog without saving"));
+ button(ButtonType_Reset)->setShortcut(QString("Ctrl+Backspace"));
+ button(ButtonType_Apply)->setShortcut(QString("Ctrl+Return"));
+ button(ButtonType_Close)->setShortcut(Qt::Key_Escape);
+ button(ButtonType_Reset)->setToolTip(tr("Reset Changes (%1)").arg(button(ButtonType_Reset)->shortcut().toString()));
+ button(ButtonType_Apply)->setToolTip(tr("Apply Changes (%1)").arg(button(ButtonType_Apply)->shortcut().toString()));
+ button(ButtonType_Close)->setToolTip(tr("Close Window (%1)").arg(button(ButtonType_Close)->shortcut().toString()));
+}
+
+void UICloudConsoleManager::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/cloud_console_manager_32px.png", ":/cloud_console_manager_16px.png"));
+#endif
+}
+
+void UICloudConsoleManager::configureCentralWidget()
+{
+ /* Create widget: */
+ UICloudConsoleManagerWidget *pWidget = new UICloudConsoleManagerWidget(EmbedTo_Dialog, m_pActionPool, true, this);
+ if (pWidget)
+ {
+ /* Configure widget: */
+ setWidget(pWidget);
+ setWidgetMenu(pWidget->menu());
+#ifdef VBOX_WS_MAC
+ setWidgetToolbar(pWidget->toolbar());
+#endif
+ connect(this, &UICloudConsoleManager::sigDataChangeRejected,
+ pWidget, &UICloudConsoleManagerWidget::sltResetCloudConsoleDetailsChanges);
+ connect(this, &UICloudConsoleManager::sigDataChangeAccepted,
+ pWidget, &UICloudConsoleManagerWidget::sltApplyCloudConsoleDetailsChanges);
+
+ /* Add into layout: */
+ centralWidget()->layout()->addWidget(pWidget);
+ }
+}
+
+void UICloudConsoleManager::configureButtonBox()
+{
+ /* Configure button-box: */
+ connect(widget(), &UICloudConsoleManagerWidget::sigCloudConsoleDetailsVisibilityChanged,
+ button(ButtonType_Apply), &QPushButton::setVisible);
+ connect(widget(), &UICloudConsoleManagerWidget::sigCloudConsoleDetailsVisibilityChanged,
+ button(ButtonType_Reset), &QPushButton::setVisible);
+ connect(widget(), &UICloudConsoleManagerWidget::sigCloudConsoleDetailsDataChanged,
+ button(ButtonType_Apply), &QPushButton::setEnabled);
+ connect(widget(), &UICloudConsoleManagerWidget::sigCloudConsoleDetailsDataChanged,
+ button(ButtonType_Reset), &QPushButton::setEnabled);
+ connect(buttonBox(), &QIDialogButtonBox::clicked,
+ this, &UICloudConsoleManager::sltHandleButtonBoxClick);
+ // WORKAROUND:
+ // Since we connected signals later than extra-data loaded
+ // for signals above, we should handle that stuff here again:
+ button(ButtonType_Apply)->setVisible(gEDataManager->cloudConsoleManagerDetailsExpanded());
+ button(ButtonType_Reset)->setVisible(gEDataManager->cloudConsoleManagerDetailsExpanded());
+}
+
+void UICloudConsoleManager::finalize()
+{
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UICloudConsoleManagerWidget *UICloudConsoleManager::widget()
+{
+ return qobject_cast<UICloudConsoleManagerWidget*>(QIManagerDialog::widget());
+}
+
+
+#include "UICloudConsoleManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleManager.h b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleManager.h
new file mode 100644
index 00000000..ce455cf2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/consolemanager/UICloudConsoleManager.h
@@ -0,0 +1,298 @@
+/* $Id: UICloudConsoleManager.h $ */
+/** @file
+ * VBox Qt GUI - UICloudConsoleManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_cloud_consolemanager_UICloudConsoleManager_h
+#define FEQT_INCLUDED_SRC_cloud_consolemanager_UICloudConsoleManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QTreeWidgetItem;
+class QITreeWidget;
+class QITreeWidgetItem;
+class UIActionPool;
+class UICloudConsoleDetailsWidget;
+class UIItemCloudConsoleApplication;
+class UIItemCloudConsoleProfile;
+class QIToolBar;
+struct UIDataCloudConsoleApplication;
+struct UIDataCloudConsoleProfile;
+
+
+/** QWidget extension providing GUI with the pane to control cloud console related functionality. */
+class UICloudConsoleManagerWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about cloud console details-widget @a fVisible. */
+ void sigCloudConsoleDetailsVisibilityChanged(bool fVisible);
+ /** Notifies listeners about cloud console details data @a fDiffers. */
+ void sigCloudConsoleDetailsDataChanged(bool fDiffers);
+
+public:
+
+ /** Constructs Cloud Console Manager widget.
+ * @param enmEmbedding Brings the type of widget embedding.
+ * @param pActionPool Brings the action-pool reference.
+ * @param fShowToolbar Brings whether we should create/show toolbar. */
+ UICloudConsoleManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar = true, QWidget *pParent = 0);
+
+ /** Returns the menu. */
+ QMenu *menu() const;
+
+#ifdef VBOX_WS_MAC
+ /** Returns the toolbar. */
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+public slots:
+
+ /** @name Details-widget stuff.
+ * @{ */
+ /** Handles command to reset cloud console details changes. */
+ void sltResetCloudConsoleDetailsChanges();
+ /** Handles command to apply cloud console details changes. */
+ void sltApplyCloudConsoleDetailsChanges();
+ /** @} */
+
+private slots:
+
+ /** @name Menu/action stuff.
+ * @{ */
+ /** Handles command to add cloud console application. */
+ void sltAddCloudConsoleApplication();
+ /** Handles command to remove cloud console application. */
+ void sltRemoveCloudConsoleApplication();
+ /** Handles command to add cloud console profile. */
+ void sltAddCloudConsoleProfile();
+ /** Handles command to remove cloud console profile. */
+ void sltRemoveCloudConsoleProfile();
+ /** Handles command to make cloud console details @a fVisible. */
+ void sltToggleCloudConsoleDetailsVisibility(bool fVisible);
+ /** @} */
+
+ /** @name Tree-widget stuff.
+ * @{ */
+ /** Handles request to load cloud console stuff. */
+ void sltLoadCloudConsoleStuff() { loadCloudConsoleStuff(); }
+ /** Adjusts tree-widget according content. */
+ void sltPerformTableAdjustment();
+ /** Handles tree-widget current item change. */
+ void sltHandleCurrentItemChange();
+ /** Handles context-menu request for tree-widget @a position. */
+ void sltHandleContextMenuRequest(const QPoint &position);
+ /** Handles tree-widget @a pItem change. */
+ void sltHandleItemChange(QTreeWidgetItem *pItem);
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares actions. */
+ void prepareActions();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares toolbar. */
+ void prepareToolBar();
+ /** Prepares tree-widget. */
+ void prepareTreeWidget();
+ /** Prepares details-widget. */
+ void prepareDetailsWidget();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Load settings: */
+ void loadSettings();
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+ /** Loads cloud console stuff. */
+ void loadCloudConsoleStuff();
+ /** Loads cloud console @a strSuperset data to passed @a applicationData container. */
+ void loadCloudConsoleApplication(const QString &strSuperset,
+ UIDataCloudConsoleApplication &applicationData);
+ /** Loads cloud console @a strSuperset data to passed @a profileData container, using @a applicationData as hint. */
+ void loadCloudConsoleProfile(const QString &strSuperset,
+ const UIDataCloudConsoleApplication &applicationData,
+ UIDataCloudConsoleProfile &profileData);
+ /** @} */
+
+ /** @name Tree-widget stuff.
+ * @{ */
+ /** Searches an application item with specified @a strApplicationId. */
+ UIItemCloudConsoleApplication *searchApplicationItem(const QString &strApplicationId) const;
+ /** Searches a profile child of application item with specified @a strApplicationId and @a strProfileId. */
+ UIItemCloudConsoleProfile *searchProfileItem(const QString &strApplicationId,
+ const QString &strProfileId) const;
+ /** Searches an item with specified @a strDefinition. */
+ QITreeWidgetItem *searchItemByDefinition(const QString &strDefinition) const;
+
+ /** Creates a new tree-widget item
+ * on the basis of passed @a applicationData, @a fChooseItem if requested. */
+ void createItemForCloudConsoleApplication(const UIDataCloudConsoleApplication &applicationData,
+ bool fChooseItem);
+
+ /** Creates a new tree-widget item as a child of certain @a pParent,
+ * on the basis of passed @a profileData, @a fChooseItem if requested. */
+ void createItemForCloudConsoleProfile(QTreeWidgetItem *pParent,
+ const UIDataCloudConsoleProfile &profileData,
+ bool fChooseItem);
+
+ /* Gathers a list of Cloud Console Manager restrictions starting from @a pParentItem. */
+ QStringList gatherCloudConsoleManagerRestrictions(QTreeWidgetItem *pParentItem);
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** Holds whether we should create/show toolbar. */
+ const bool m_fShowToolbar;
+ /** Holds current item definition. */
+ QString m_strDefinition;
+ /** @} */
+
+ /** @name Toolbar and menu variables.
+ * @{ */
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolBar;
+ /** @} */
+
+ /** @name Splitter variables.
+ * @{ */
+ /** Holds the tree-widget instance. */
+ QITreeWidget *m_pTreeWidget;
+ /** Holds the details-widget instance. */
+ UICloudConsoleDetailsWidget *m_pDetailsWidget;
+ /** @} */
+};
+
+
+/** QIManagerDialogFactory extension used as a factory for Cloud Console Manager dialog. */
+class UICloudConsoleManagerFactory : public QIManagerDialogFactory
+{
+public:
+
+ /** Constructs Cloud Console Manager actory acquiring additional arguments.
+ * @param pActionPool Brings the action-pool reference. */
+ UICloudConsoleManagerFactory(UIActionPool *pActionPool = 0);
+
+protected:
+
+ /** Creates derived @a pDialog instance.
+ * @param pCenterWidget Brings the widget reference to center according to. */
+ virtual void create(QIManagerDialog *&pDialog, QWidget *pCenterWidget) RT_OVERRIDE;
+
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+};
+
+
+/** QIManagerDialog extension providing GUI with the dialog to control cloud console related functionality. */
+class UICloudConsoleManager : public QIWithRetranslateUI<QIManagerDialog>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+private slots:
+
+ /** @name Button-box stuff.
+ * @{ */
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** Constructs Cloud Console Manager dialog.
+ * @param pCenterWidget Brings the widget reference to center according to.
+ * @param pActionPool Brings the action-pool reference. */
+ UICloudConsoleManager(QWidget *pCenterWidget, UIActionPool *pActionPool);
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ virtual void configure() RT_OVERRIDE;
+ /** Configures central-widget. */
+ virtual void configureCentralWidget() RT_OVERRIDE;
+ /** Configures button-box. */
+ virtual void configureButtonBox() RT_OVERRIDE;
+ /** Perform final preparations. */
+ virtual void finalize() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Widget stuff.
+ * @{ */
+ /** Returns the widget. */
+ virtual UICloudConsoleManagerWidget *widget() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Action related variables.
+ * @{ */
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** @} */
+
+ /** Allow factory access to private/protected members: */
+ friend class UICloudConsoleManagerFactory;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_cloud_consolemanager_UICloudConsoleManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialog.cpp b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialog.cpp
new file mode 100644
index 00000000..9206ee16
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialog.cpp
@@ -0,0 +1,203 @@
+/* $Id: UICloudMachineSettingsDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UICloudMachineSettingsDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPushButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "UICloudMachineSettingsDialog.h"
+#include "UICloudMachineSettingsDialogPage.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UINotificationCenter.h"
+
+
+UICloudMachineSettingsDialog::UICloudMachineSettingsDialog(QWidget *pParent, const CCloudMachine &comCloudMachine)
+ : QIWithRetranslateUI2<QWidget>(pParent, Qt::Window)
+ , m_fPolished(false)
+ , m_fClosable(true)
+ , m_fClosed(false)
+ , m_comCloudMachine(comCloudMachine)
+ , m_pPage(0)
+ , m_pButtonBox(0)
+ , m_pNotificationCenter(0)
+{
+ prepare();
+}
+
+UICloudMachineSettingsDialog::~UICloudMachineSettingsDialog()
+{
+ cleanup();
+}
+
+void UICloudMachineSettingsDialog::setCloudMachine(const CCloudMachine &comCloudMachine)
+{
+ m_comCloudMachine = comCloudMachine;
+ load();
+}
+
+void UICloudMachineSettingsDialog::retranslateUi()
+{
+ /* Translate title: */
+ const QString strCaption = tr("Settings");
+ if (m_strName.isNull())
+ setWindowTitle(strCaption);
+ else
+ setWindowTitle(QString("%1 - %2").arg(m_strName, strCaption));
+}
+
+void UICloudMachineSettingsDialog::showEvent(QShowEvent *pEvent)
+{
+ /* Polish stuff: */
+ if (!m_fPolished)
+ {
+ m_fPolished = true;
+ polishEvent(pEvent);
+ }
+
+ /* Call to base-class: */
+ QIWithRetranslateUI2<QWidget>::showEvent(pEvent);
+}
+
+void UICloudMachineSettingsDialog::polishEvent(QShowEvent*)
+{
+ /* Explicit centering according to our parent: */
+ gpDesktop->centerWidget(this, parentWidget(), false);
+ /* Call for load stuff asynchronously: */
+ QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
+}
+
+void UICloudMachineSettingsDialog::closeEvent(QCloseEvent *pEvent)
+{
+ /* Ignore event initially: */
+ pEvent->ignore();
+
+ /* Do not close if NOT closable: */
+ if (!m_fClosable)
+ return;
+
+ /* Tell the listener to close us (once): */
+ if (!m_fClosed)
+ {
+ m_fClosed = true;
+ emit sigClose();
+ }
+}
+
+void UICloudMachineSettingsDialog::setOkButtonEnabled(bool fEnabled)
+{
+ AssertPtrReturnVoid(m_pButtonBox);
+ AssertPtrReturnVoid(m_pButtonBox->button(QDialogButtonBox::Ok));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(fEnabled);
+}
+
+void UICloudMachineSettingsDialog::prepare()
+{
+ /* Prepare local notification-center (parent to be assigned in the end): */
+ m_pNotificationCenter = new UINotificationCenter(0);
+
+ /* Prepare layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare page: */
+ m_pPage = new UICloudMachineSettingsDialogPage(this);
+ if (m_pPage)
+ {
+ connect(m_pPage.data(), &UICloudMachineSettingsDialogPage::sigValidChanged,
+ this, &UICloudMachineSettingsDialog::setOkButtonEnabled);
+ /* Add into layout: */
+ pLayout->addWidget(m_pPage);
+ }
+
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UICloudMachineSettingsDialog::accept);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UICloudMachineSettingsDialog::close);
+ setOkButtonEnabled(false);
+ /* Add into layout: */
+ pLayout->addWidget(m_pButtonBox);
+ }
+ }
+
+ /* Assign notification-center parent (after everything else is done): */
+ m_pNotificationCenter->setParent(this);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UICloudMachineSettingsDialog::cleanup()
+{
+ /* Cleanup local notification-center: */
+ delete m_pNotificationCenter;
+ m_pNotificationCenter = 0;
+}
+
+void UICloudMachineSettingsDialog::load()
+{
+ /* Prevent closing: */
+ m_fClosable = false;
+
+ /* Update name: */
+ if (!cloudMachineName(m_comCloudMachine, m_strName, notificationCenter()))
+ close();
+
+ /* Retranslate title: */
+ retranslateUi();
+
+ /* Update form: */
+ if (!cloudMachineSettingsForm(m_comCloudMachine, m_comForm, notificationCenter()))
+ close();
+
+ /* Assign page with form: */
+ m_pPage->setForm(m_comForm);
+
+ /* Allow closing again: */
+ m_fClosable = true;
+}
+
+void UICloudMachineSettingsDialog::save()
+{
+ /* Makes sure page data committed: */
+ if (m_pPage)
+ m_pPage->makeSureDataCommitted();
+
+ /* Apply form: */
+ AssertReturnVoid(m_comForm.isNotNull());
+ if (!applyCloudMachineSettingsForm(m_comCloudMachine, m_comForm, notificationCenter()))
+ return;
+
+ /* Just close for now: */
+ close();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialog.h b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialog.h
new file mode 100644
index 00000000..4c364ccf
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialog.h
@@ -0,0 +1,132 @@
+/* $Id: UICloudMachineSettingsDialog.h $ */
+/** @file
+ * VBox Qt GUI - UICloudMachineSettingsDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_cloud_machinesettings_UICloudMachineSettingsDialog_h
+#define FEQT_INCLUDED_SRC_cloud_machinesettings_UICloudMachineSettingsDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPointer>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UICloudMachineSettingsDialogPage.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudMachine.h"
+#include "CForm.h"
+
+/* Forward declarations: */
+class QIDialogButtonBox;
+class UINotificationCenter;
+
+/** Cloud machine settings window. */
+class UICloudMachineSettingsDialog : public QIWithRetranslateUI2<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about dialog should be closed. */
+ void sigClose();
+
+public:
+
+ /** Constructs @a comCloudMachine settings dialog passing @a pParent to the base-class. */
+ UICloudMachineSettingsDialog(QWidget *pParent, const CCloudMachine &comCloudMachine);
+ /** Destructs cloud machine settings dialog. */
+ virtual ~UICloudMachineSettingsDialog() /* override final */;
+
+ /** Returns local notification-center reference. */
+ UINotificationCenter *notificationCenter() const { return m_pNotificationCenter; }
+
+ /** Defines @a comCloudMachine */
+ void setCloudMachine(const CCloudMachine &comCloudMachine);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles first show @a pEvent. */
+ virtual void polishEvent(QShowEvent*);
+ /** Handles close @a pEvent. */
+ virtual void closeEvent(QCloseEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Sets Ok button to be @a fEnabled. */
+ void setOkButtonEnabled(bool fEnabled);
+ /** Inits the dialog. */
+ void init() { load(); }
+ /** Accepts the dialog. */
+ void accept() { save(); }
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Loads the data. */
+ void load();
+ /** Saves the data and closes the dialog. */
+ void save();
+
+ /** Holds whether dialog is polished. */
+ bool m_fPolished;
+ /** Holds whether the dialod can really be closed. */
+ bool m_fClosable;
+ /** Holds whether the dialod had emitted signal to be closed. */
+ bool m_fClosed;
+
+ /** Holds the cloud machine object reference. */
+ CCloudMachine m_comCloudMachine;
+ /** Holds the cloud machine settings form object reference. */
+ CForm m_comForm;
+ /** Holds the cloud machine name. */
+ QString m_strName;
+
+ /** Holds the cloud machine settings dialog page instance. */
+ UISafePointerCloudMachineSettingsDialogPage m_pPage;
+ /** Holds the dialog button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+
+ /** Holds the local notification-center instance. */
+ UINotificationCenter *m_pNotificationCenter;
+};
+
+/** Safe pointer to cloud machine settings dialog. */
+typedef QPointer<UICloudMachineSettingsDialog> UISafePointerCloudMachineSettingsDialog;
+
+#endif /* !FEQT_INCLUDED_SRC_cloud_machinesettings_UICloudMachineSettingsDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialogPage.cpp b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialogPage.cpp
new file mode 100644
index 00000000..fec3c5c7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialogPage.cpp
@@ -0,0 +1,139 @@
+/* $Id: UICloudMachineSettingsDialogPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UICloudMachineSettingsDialogPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UICloudMachineSettingsDialog.h"
+#include "UICloudMachineSettingsDialogPage.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+UICloudMachineSettingsDialogPage::UICloudMachineSettingsDialogPage(QWidget *pParent,
+ bool fFullScale /* = true */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pParent(qobject_cast<UICloudMachineSettingsDialog*>(pParent))
+ , m_fFullScale(fFullScale)
+{
+ prepare();
+}
+
+void UICloudMachineSettingsDialogPage::setForm(const CForm &comForm)
+{
+ m_comForm = comForm;
+ updateEditor();
+}
+
+void UICloudMachineSettingsDialogPage::setFilter(const QString &strFilter)
+{
+ m_strFilter = strFilter;
+ updateEditor();
+}
+
+void UICloudMachineSettingsDialogPage::makeSureDataCommitted()
+{
+ AssertPtrReturnVoid(m_pFormEditor.data());
+ m_pFormEditor->makeSureEditorDataCommitted();
+}
+
+void UICloudMachineSettingsDialogPage::retranslateUi()
+{
+ AssertPtrReturnVoid(m_pFormEditor.data());
+ m_pFormEditor->setWhatsThis(UICloudMachineSettingsDialog::tr("Contains a list of cloud machine settings."));
+}
+
+void UICloudMachineSettingsDialogPage::prepare()
+{
+ /* Prepare layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare form editor widget: */
+ m_pFormEditor = new UIFormEditorWidget(this, m_pParent ? m_pParent->notificationCenter() : 0);
+ if (m_pFormEditor)
+ {
+ /* Make form-editor fit 12 sections in height by default: */
+ const int iDefaultSectionHeight = m_pFormEditor->verticalHeader()
+ ? m_pFormEditor->verticalHeader()->defaultSectionSize()
+ : 0;
+ if (iDefaultSectionHeight > 0)
+ {
+ const int iProposedHeight = iDefaultSectionHeight * (m_fFullScale ? 12 : 6);
+ const int iProposedWidth = iProposedHeight * 1.66;
+ m_pFormEditor->setMinimumSize(iProposedWidth, iProposedHeight);
+ }
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pFormEditor);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UICloudMachineSettingsDialogPage::updateEditor()
+{
+ /* Make sure editor present: */
+ AssertPtrReturnVoid(m_pFormEditor.data());
+
+ /* Make sure form isn't null: */
+ if (m_comForm.isNotNull())
+ {
+ /* Acquire initial values: */
+ const QVector<CFormValue> initialValues = m_comForm.GetValues();
+
+ /* If filter null: */
+ if (m_strFilter.isNull())
+ {
+ /* Push initial values to editor: */
+ m_pFormEditor->setValues(initialValues);
+ }
+ /* If filter present: */
+ else
+ {
+ /* Acquire group fields: */
+ const QVector<QString> groupFields = m_comForm.GetFieldGroup(m_strFilter);
+ /* Filter out unrelated values: */
+ QVector<CFormValue> filteredValues;
+ foreach (const CFormValue &comValue, initialValues)
+ if (groupFields.contains(comValue.GetLabel()))
+ filteredValues << comValue;
+ /* Push filtered values to editor: */
+ m_pFormEditor->setValues(filteredValues);
+ }
+ }
+
+ /* Revalidate: */
+ emit sigValidChanged(m_comForm.isNotNull());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialogPage.h b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialogPage.h
new file mode 100644
index 00000000..5b7f7c5f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/machinesettings/UICloudMachineSettingsDialogPage.h
@@ -0,0 +1,111 @@
+/* $Id: UICloudMachineSettingsDialogPage.h $ */
+/** @file
+ * VBox Qt GUI - UICloudMachineSettingsDialogPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_cloud_machinesettings_UICloudMachineSettingsDialogPage_h
+#define FEQT_INCLUDED_SRC_cloud_machinesettings_UICloudMachineSettingsDialogPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPointer>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIFormEditorWidget.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CForm.h"
+
+/* Forward declarations: */
+class UICloudMachineSettingsDialog;
+
+/** Cloud machine settings dialog page. */
+class UICloudMachineSettingsDialogPage : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value has became @a fValid. */
+ void sigValidChanged(bool fValid);
+
+public:
+
+ /** Constructs cloud machine settings dialog page passing @a pParent to the base-class.
+ * @param fFullScale Brings whether this page is full-scale and should reflect at least 12 fields. */
+ UICloudMachineSettingsDialogPage(QWidget *pParent, bool fFullScale = true);
+
+ /** Returns page form. */
+ CForm form() const { return m_comForm; }
+ /** Returns page filter. */
+ QString filter() const { return m_strFilter; }
+
+public slots:
+
+ /** Defines page @a comForm. */
+ void setForm(const CForm &comForm);
+ /** Defines page @a strFilter. */
+ void setFilter(const QString &strFilter);
+
+ /** Makes sure page data committed. */
+ void makeSureDataCommitted();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates editor. */
+ void updateEditor();
+
+ /** Holds the parent cloud machine settings dialog reference. */
+ UICloudMachineSettingsDialog *m_pParent;
+
+ /** Holds whether this page is full-scale and should reflect at least 12 fields. */
+ bool m_fFullScale;
+
+ /** Holds the form editor widget instance. */
+ UIFormEditorWidgetPointer m_pFormEditor;
+
+ /** Holds the page form. */
+ CForm m_comForm;
+ /** Holds the page filter. */
+ QString m_strFilter;
+};
+
+/** Safe pointer to Form Editor widget. */
+typedef QPointer<UICloudMachineSettingsDialogPage> UISafePointerCloudMachineSettingsDialogPage;
+
+#endif /* !FEQT_INCLUDED_SRC_cloud_machinesettings_UICloudMachineSettingsDialogPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileDetailsWidget.cpp b/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileDetailsWidget.cpp
new file mode 100644
index 00000000..4aeb845c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileDetailsWidget.cpp
@@ -0,0 +1,424 @@
+/* $Id: UICloudProfileDetailsWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UICloudProfileDetailsWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QITableWidget.h"
+#include "UICommon.h"
+#include "UICloudProfileDetailsWidget.h"
+#include "UICloudProfileManager.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+UICloudProfileDetailsWidget::UICloudProfileDetailsWidget(EmbedTo enmEmbedding, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pLabelName(0)
+ , m_pEditorName(0)
+ , m_pLabelTableWidget(0)
+ , m_pTableWidget(0)
+ , m_pButtonBox(0)
+{
+ prepare();
+}
+
+void UICloudProfileDetailsWidget::setData(const UIDataCloudProfile &data)
+{
+ /* Cache old/new data: */
+ m_oldData = data;
+ m_newData = m_oldData;
+
+ /* Load data: */
+ loadData();
+
+ /* Translate linked widgets: */
+ retranslateEditor();
+ retranslateButtons();
+}
+
+void UICloudProfileDetailsWidget::retranslateUi()
+{
+ /// @todo add description tool-tips
+
+ /* Translate name-editor label: */
+ m_pLabelName->setText(UICloudProfileManager::tr("Name:"));
+ /* Translate name-editor: */
+ retranslateEditor();
+
+ /* Translate table-widget label: */
+ m_pLabelTableWidget->setText(UICloudProfileManager::tr("Properties:"));
+ /* Translate table-widget: */
+ m_pTableWidget->setWhatsThis(UICloudProfileManager::tr("Contains cloud profile settings"));
+
+ /* Translate buttons: */
+ retranslateButtons();
+
+ /* Retranslate validation: */
+ retranslateValidation();
+
+ /* Update table tool-tips: */
+ updateTableToolTips();
+}
+
+void UICloudProfileDetailsWidget::retranslateEditor()
+{
+ /* Translate placeholders: */
+ m_pEditorName->setPlaceholderText( m_oldData.m_strName.isNull()
+ ? UICloudProfileManager::tr("Enter a name for the new profile...")
+ : UICloudProfileManager::tr("Enter a name for this profile..."));
+}
+
+void UICloudProfileDetailsWidget::retranslateButtons()
+{
+ /* Translate button-box: */
+ if (m_pButtonBox)
+ {
+ /* Common: 'Reset' button: */
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setText(UICloudProfileManager::tr("Reset"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setStatusTip(UICloudProfileManager::tr("Reset changes in current profile details"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->
+ setToolTip(UICloudProfileManager::tr("Reset Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+
+ if (m_oldData.m_strName.isNull())
+ {
+ /* Provider: 'Add' button: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText(UICloudProfileManager::tr("Add"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(UICloudProfileManager::tr("Add a new profile with following name"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->
+ setToolTip(UICloudProfileManager::tr("Add Profile (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+ else
+ {
+ /* Profile: 'Apply' button: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText(UICloudProfileManager::tr("Apply"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(UICloudProfileManager::tr("Apply changes in current profile details"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->
+ setToolTip(UICloudProfileManager::tr("Apply Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+ }
+}
+
+void UICloudProfileDetailsWidget::sltNameChanged(const QString &strName)
+{
+ /* Push changes back: */
+ m_newData.m_strName = strName;
+
+ /* Revalidate: */
+ revalidate(m_pEditorName);
+ /* Update button states: */
+ updateButtonStates();
+}
+
+void UICloudProfileDetailsWidget::sltTableChanged(QTableWidgetItem *pItem)
+{
+ /* Make sure item is valid: */
+ AssertPtrReturnVoid(pItem);
+ const int iRow = pItem->row();
+ AssertReturnVoid(iRow >= 0);
+
+ /* Skip if one of items isn't yet created.
+ * This can happen when 1st is already while 2nd isn't yet. */
+ QTableWidgetItem *pItemK = m_pTableWidget->item(iRow, 0);
+ QTableWidgetItem *pItemV = m_pTableWidget->item(iRow, 1);
+ if (!pItemK || !pItemV)
+ return;
+
+ /* Push changes back: */
+ const QString strKey = pItemK->text();
+ const QString strValue = pItemV->text();
+ m_newData.m_data[strKey] = qMakePair(strValue, m_newData.m_data.value(strKey).second);
+
+ /* Revalidate: */
+ revalidate(m_pTableWidget);
+ /* Update button states: */
+ updateButtonStates();
+}
+
+void UICloudProfileDetailsWidget::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+ /* Make sure button-box exists: */
+ AssertPtrReturnVoid(m_pButtonBox);
+
+ /* Disable buttons first of all: */
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if (pButton == m_pButtonBox->button(QDialogButtonBox::Cancel))
+ emit sigDataChangeRejected();
+ else
+ if (pButton == m_pButtonBox->button(QDialogButtonBox::Ok))
+ emit sigDataChangeAccepted();
+}
+
+void UICloudProfileDetailsWidget::prepare()
+{
+ /* Prepare widgets: */
+ prepareWidgets();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Update button states finally: */
+ updateButtonStates();
+ uiCommon().setHelpKeyword(this, "ovf-cloud-profile-manager");
+}
+
+void UICloudProfileDetailsWidget::prepareWidgets()
+{
+ /* Create layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ if (m_enmEmbedding == EmbedTo_Dialog)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ pLayout->setSpacing(10);
+#else
+ pLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+ }
+ else
+ {
+#ifdef VBOX_WS_MAC
+ pLayout->setContentsMargins(13, 0, 13, 13);
+ pLayout->setSpacing(10);
+#else
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) * 1.5;
+ const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) * 1.5;
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) * 1.5;
+ const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) * 1.5;
+ pLayout->setContentsMargins(iL, iT, iR, iB);
+#endif
+ }
+
+ /* Create name editor: */
+ m_pEditorName = new QLineEdit;
+ if (m_pEditorName)
+ {
+ connect(m_pEditorName, &QLineEdit::textChanged, this, &UICloudProfileDetailsWidget::sltNameChanged);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pEditorName, 0, 1);
+ }
+ /* Create name label: */
+ m_pLabelName = new QLabel;
+ if (m_pLabelName)
+ {
+ m_pLabelName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLabelName->setBuddy(m_pEditorName);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pLabelName, 0, 0);
+ }
+
+ /* Create tab-widget: */
+ m_pTableWidget = new QITableWidget;
+ if (m_pTableWidget)
+ {
+ m_pTableWidget->setAlternatingRowColors(true);
+ m_pTableWidget->horizontalHeader()->setVisible(false);
+ m_pTableWidget->verticalHeader()->setVisible(false);
+ m_pTableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+ connect(m_pTableWidget, &QITableWidget::itemChanged, this, &UICloudProfileDetailsWidget::sltTableChanged);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pTableWidget, 1, 1);
+ }
+ /* Create tab-widget label: */
+ m_pLabelTableWidget = new QLabel;
+ if (m_pLabelTableWidget)
+ {
+ m_pLabelTableWidget->setAlignment(Qt::AlignRight | Qt::AlignTop);
+ m_pLabelTableWidget->setBuddy(m_pTableWidget);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pLabelTableWidget, 1, 0);
+ }
+
+ /* If parent embedded into stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBox, &QIDialogButtonBox::clicked, this, &UICloudProfileDetailsWidget::sltHandleButtonBoxClick);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pButtonBox, 2, 0, 1, 2);
+ }
+ }
+ }
+}
+
+void UICloudProfileDetailsWidget::loadData()
+{
+ /* Clear table initially: */
+ m_pTableWidget->clear();
+
+ /* Fill name editor: */
+ m_pEditorName->setText(m_oldData.m_strName);
+
+ /* Configure table: */
+ m_pTableWidget->setRowCount(m_oldData.m_data.keys().size());
+ m_pTableWidget->setColumnCount(2);
+
+ /* Push acquired keys/values to data fields: */
+ for (int i = 0; i < m_pTableWidget->rowCount(); ++i)
+ {
+ /* Gather values: */
+ const QString strKey = m_oldData.m_data.keys().at(i);
+ const QString strValue = m_oldData.m_data.value(strKey).first;
+ const QString strToolTip = m_oldData.m_data.value(strKey).second;
+
+ /* Create key item: */
+ QITableWidgetItem *pItemK = new QITableWidgetItem(strKey);
+ if (pItemK)
+ {
+ /* Non-editable for sure, but non-selectable? */
+ pItemK->setFlags(pItemK->flags() & ~Qt::ItemIsEditable);
+ /* Use non-translated description as tool-tip: */
+ pItemK->setData(Qt::UserRole, strToolTip);
+
+ /* Insert into table: */
+ m_pTableWidget->setItem(i, 0, pItemK);
+ }
+
+ /* Create value item: */
+ QITableWidgetItem *pItemV = new QITableWidgetItem(strValue);
+ if (pItemV)
+ {
+ /* Use the value as tool-tip, there can be quite long values: */
+ pItemV->setToolTip(strValue);
+
+ /* Insert into table: */
+ m_pTableWidget->setItem(i, 1, pItemV);
+ }
+ }
+
+ /* Update table tooltips: */
+ updateTableToolTips();
+ /* Adjust table contents: */
+ adjustTableContents();
+}
+
+void UICloudProfileDetailsWidget::revalidate(QWidget *pWidget /* = 0 */)
+{
+ /// @todo validate profile settings table!
+
+ /* Retranslate validation: */
+ retranslateValidation(pWidget);
+}
+
+void UICloudProfileDetailsWidget::retranslateValidation(QWidget *pWidget /* = 0 */)
+{
+ Q_UNUSED(pWidget);
+
+ /// @todo translate vaidation errors!
+}
+
+void UICloudProfileDetailsWidget::updateTableToolTips()
+{
+ /* Iterate through all the key items: */
+ for (int i = 0; i < m_pTableWidget->rowCount(); ++i)
+ {
+ /* Acquire current key item: */
+ QTableWidgetItem *pItemK = m_pTableWidget->item(i, 0);
+ if (pItemK)
+ {
+ const QString strToolTip = pItemK->data(Qt::UserRole).toString();
+ pItemK->setToolTip(UICloudProfileManager::tr(strToolTip.toUtf8().constData()));
+ }
+ }
+}
+
+void UICloudProfileDetailsWidget::adjustTableContents()
+{
+ /* Disable last column stretching temporary: */
+ m_pTableWidget->horizontalHeader()->setStretchLastSection(false);
+
+ /* Resize both columns to contents: */
+ m_pTableWidget->resizeColumnsToContents();
+ /* Then acquire full available width: */
+ const int iFullWidth = m_pTableWidget->viewport()->width();
+ /* First column should not be less than it's minimum size, last gets the rest: */
+ const int iMinimumWidth0 = qMin(m_pTableWidget->horizontalHeader()->sectionSize(0), iFullWidth / 2);
+ m_pTableWidget->horizontalHeader()->resizeSection(0, iMinimumWidth0);
+
+ /* Enable last column stretching again: */
+ m_pTableWidget->horizontalHeader()->setStretchLastSection(true);
+}
+
+void UICloudProfileDetailsWidget::updateButtonStates()
+{
+#if 0
+ if (m_oldData != m_newData)
+ {
+ printf("Old data:\n");
+ foreach (const QString &strKey, m_oldData.m_data.keys())
+ {
+ const QString strValue = m_oldData.m_data.value(strKey).first;
+ const QString strDecription = m_oldData.m_data.value(strKey).second;
+ printf(" %s: %s, %s\n", strKey.toUtf8().constData(), strValue.toUtf8().constData(), strDecription.toUtf8().constData());
+ }
+ printf("New data:\n");
+ foreach (const QString &strKey, m_newData.m_data.keys())
+ {
+ const QString strValue = m_newData.m_data.value(strKey).first;
+ const QString strDecription = m_newData.m_data.value(strKey).second;
+ printf(" %s: %s, %s\n", strKey.toUtf8().constData(), strValue.toUtf8().constData(), strDecription.toUtf8().constData());
+ }
+ printf("\n");
+ }
+#endif
+
+ /* Update 'Apply' / 'Reset' button states: */
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(m_oldData != m_newData);
+ }
+
+ /* Notify listeners as well: */
+ emit sigDataChanged(m_oldData != m_newData);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileDetailsWidget.h b/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileDetailsWidget.h
new file mode 100644
index 00000000..5abfaac6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileDetailsWidget.h
@@ -0,0 +1,235 @@
+/* $Id: UICloudProfileDetailsWidget.h $ */
+/** @file
+ * VBox Qt GUI - UICloudProfileDetailsWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_cloud_profilemanager_UICloudProfileDetailsWidget_h
+#define FEQT_INCLUDED_SRC_cloud_profilemanager_UICloudProfileDetailsWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QUuid>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QLabel;
+class QLineEdit;
+class QTableWidgetItem;
+class QIDialogButtonBox;
+class QITableWidget;
+
+
+/** Cloud Provider data structure. */
+struct UIDataCloudProvider
+{
+ /** Constructs data. */
+ UIDataCloudProvider()
+ : m_fRestricted(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataCloudProvider &other) const
+ {
+ return true
+ && (m_uId == other.m_uId)
+ && (m_strShortName == other.m_strShortName)
+ && (m_strName == other.m_strName)
+ && (m_fRestricted == other.m_fRestricted)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataCloudProvider &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataCloudProvider &other) const { return !equal(other); }
+
+ /** Holds the provider ID. */
+ QUuid m_uId;
+ /** Holds the provider short name. */
+ QString m_strShortName;
+ /** Holds the provider name. */
+ QString m_strName;
+ /** Holds whether provider is restricted. */
+ bool m_fRestricted;
+
+ /** Holds the profile supported property descriptions. */
+ QMap<QString, QString> m_propertyDescriptions;
+};
+
+/** Cloud Profile data structure. */
+struct UIDataCloudProfile
+{
+ /** Constructs data. */
+ UIDataCloudProfile()
+ : m_fRestricted(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataCloudProfile &other) const
+ {
+ return true
+ && (m_strProviderShortName == other.m_strProviderShortName)
+ && (m_strName == other.m_strName)
+ && (m_fRestricted == other.m_fRestricted)
+ && (m_data == other.m_data)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataCloudProfile &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataCloudProfile &other) const { return !equal(other); }
+
+ /** Holds the provider short name. */
+ QString m_strProviderShortName;
+ /** Holds the profile name. */
+ QString m_strName;
+ /** Holds whether profile is restricted. */
+ bool m_fRestricted;
+
+ /** Holds the profile data. */
+ QMap<QString, QPair<QString, QString> > m_data;
+};
+
+
+/** Cloud Profile details widget. */
+class UICloudProfileDetailsWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data changed and whether it @a fDiffers. */
+ void sigDataChanged(bool fDiffers);
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+public:
+
+ /** Constructs cloud profile details widget passing @a pParent to the base-class.
+ * @param enmEmbedding Brings embedding type. */
+ UICloudProfileDetailsWidget(EmbedTo enmEmbedding, QWidget *pParent = 0);
+
+ /** Returns the cloud profile data. */
+ const UIDataCloudProfile &data() const { return m_newData; }
+ /** Defines the cloud profile @a data. */
+ void setData(const UIDataCloudProfile &data);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** Handles editor translation. */
+ void retranslateEditor();
+ /** Handles buttons translation. */
+ void retranslateButtons();
+
+private slots:
+
+ /** @name Change handling stuff.
+ * @{ */
+ /** Handles name change. */
+ void sltNameChanged(const QString &strName);
+ /** Handles table change. */
+ void sltTableChanged(QTableWidgetItem *pItem);
+
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+ /** Loads data. */
+ void loadData();
+ /** @} */
+
+ /** @name Change handling stuff.
+ * @{ */
+ /** Revalidates changes for passed @a pWidget. */
+ void revalidate(QWidget *pWidget = 0);
+
+ /** Retranslates validation for passed @a pWidget. */
+ void retranslateValidation(QWidget *pWidget = 0);
+
+ /** Updates table tooltips. */
+ void updateTableToolTips();
+ /** Adjusts table contents. */
+ void adjustTableContents();
+
+ /** Updates button states. */
+ void updateButtonStates();
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the parent widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+
+ /** Holds the old data copy. */
+ UIDataCloudProfile m_oldData;
+ /** Holds the new data copy. */
+ UIDataCloudProfile m_newData;
+ /** @} */
+
+ /** @name Widget variables.
+ * @{ */
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the name editor instance. */
+ QLineEdit *m_pEditorName;
+
+ /** Holds the table-widget label instance. */
+ QLabel *m_pLabelTableWidget;
+ /** Holds the table-widget instance. */
+ QITableWidget *m_pTableWidget;
+
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+ /** @} */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_cloud_profilemanager_UICloudProfileDetailsWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileManager.cpp b/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileManager.cpp
new file mode 100644
index 00000000..3d09dd93
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileManager.cpp
@@ -0,0 +1,1063 @@
+/* $Id: UICloudProfileManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UICloudProfileManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIInputDialog.h"
+#include "QIMessageBox.h"
+#include "QIToolBar.h"
+#include "QITreeWidget.h"
+#include "UIActionPoolManager.h"
+#include "UICloudNetworkingStuff.h"
+#include "UICloudProfileDetailsWidget.h"
+#include "UICloudProfileManager.h"
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+#include "UIVirtualBoxEventHandler.h"
+
+/* COM includes: */
+#include "CCloudProfile.h"
+#include "CCloudProvider.h"
+#include "CCloudProviderManager.h"
+
+
+/** Tree-widget item types. */
+enum CloudItemType
+{
+ CloudItemType_Invalid = 0,
+ CloudItemType_Provider = 1,
+ CloudItemType_Profile = 2
+};
+Q_DECLARE_METATYPE(CloudItemType);
+
+/** Tree-widget data types. */
+enum
+{
+ Data_ItemType = Qt::UserRole + 1,
+ Data_ProviderShortName = Qt::UserRole + 2,
+ Data_Definition = Qt::UserRole + 3,
+};
+
+/** Tree-widget column types. */
+enum
+{
+ Column_Name,
+ Column_ListVMs,
+ Column_Max
+};
+
+
+/** Cloud Profile Manager provider's tree-widget item. */
+class UIItemCloudProvider : public QITreeWidgetItem, public UIDataCloudProvider
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs item. */
+ UIItemCloudProvider();
+
+ /** Updates item fields from base-class data. */
+ void updateFields();
+
+ /** Returns item name. */
+ QString name() const { return m_strName; }
+
+ /** Returns definition composed on the basis of @a strShortName. */
+ static QString definition(const QString &strShortName);
+};
+
+/** Cloud Profile Manager profile's tree-widget item. */
+class UIItemCloudProfile : public QITreeWidgetItem, public UIDataCloudProfile
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs item. */
+ UIItemCloudProfile();
+
+ /** Updates item fields from base-class data. */
+ void updateFields();
+
+ /** Returns item name. */
+ QString name() const { return m_strName; }
+
+ /** Returns definition composed on the basis of @a strProviderShortName and @a strName. */
+ static QString definition(const QString &strProviderShortName, const QString &strName);
+};
+
+
+/*********************************************************************************************************************************
+* Class UIItemCloudProvider implementation. *
+*********************************************************************************************************************************/
+
+UIItemCloudProvider::UIItemCloudProvider()
+{
+ /* Assign icon: */
+ setIcon(Column_Name, UIIconPool::iconSet(":/provider_oracle_16px.png"));
+ /* Assign item type: */
+ setData(Column_Name, Data_ItemType, QVariant::fromValue(CloudItemType_Provider));
+}
+
+void UIItemCloudProvider::updateFields()
+{
+ /* Update item fields: */
+ setText(Column_Name, m_strName);
+ setData(Column_Name, Data_ProviderShortName, m_strShortName);
+ setData(Column_Name, Data_Definition, QVariant::fromValue(definition(m_strShortName)));
+ setCheckState(Column_ListVMs, m_fRestricted ? Qt::Unchecked : Qt::Checked);
+}
+
+/* static */
+QString UIItemCloudProvider::definition(const QString &strShortName)
+{
+ return QString("/%1").arg(strShortName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIItemCloudProfile implementation. *
+*********************************************************************************************************************************/
+
+UIItemCloudProfile::UIItemCloudProfile()
+{
+ /* Assign icon: */
+ setIcon(Column_Name, UIIconPool::iconSet(":/profile_16px.png"));
+ /* Assign item type: */
+ setData(Column_Name, Data_ItemType, QVariant::fromValue(CloudItemType_Profile));
+}
+
+void UIItemCloudProfile::updateFields()
+{
+ /* Update item fields: */
+ setText(Column_Name, m_strName);
+ setData(Column_Name, Data_Definition, QVariant::fromValue(definition(m_strProviderShortName, m_strName)));
+ setCheckState(Column_ListVMs, m_fRestricted ? Qt::Unchecked : Qt::Checked);
+}
+
+/* static */
+QString UIItemCloudProfile::definition(const QString &strProviderShortName, const QString &strName)
+{
+ return QString("/%1/%2").arg(strProviderShortName, strName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UICloudProfileManagerWidget implementation. *
+*********************************************************************************************************************************/
+
+UICloudProfileManagerWidget::UICloudProfileManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar /* = true */, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_pToolBar(0)
+ , m_pTreeWidget(0)
+ , m_pDetailsWidget(0)
+{
+ prepare();
+}
+
+QMenu *UICloudProfileManagerWidget::menu() const
+{
+ return m_pActionPool->action(UIActionIndexMN_M_CloudWindow)->menu();
+}
+
+void UICloudProfileManagerWidget::retranslateUi()
+{
+ /* Adjust toolbar: */
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text.
+ if (m_pToolBar)
+ m_pToolBar->updateLayout();
+#endif
+
+ /* Translate tree-widget: */
+ m_pTreeWidget->setHeaderLabels( QStringList()
+ << UICloudProfileManager::tr("Source")
+ << UICloudProfileManager::tr("List VMs"));
+ m_pTreeWidget->setWhatsThis(UICloudProfileManager::tr("Registered cloud providers and profiles"));
+}
+
+bool UICloudProfileManagerWidget::makeSureChangesResolved()
+{
+ /* Check if currently selected item is of profile type: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemCloudProfile *pProfileItem = qobject_cast<UIItemCloudProfile*>(pItem);
+ if (!pProfileItem)
+ return true;
+
+ /* Get item data: */
+ UIDataCloudProfile oldData = *pProfileItem;
+ UIDataCloudProfile newData = m_pDetailsWidget->data();
+
+ /* Check if data has changed: */
+ if (newData == oldData)
+ return true;
+
+ /* Ask whether user wants to Accept/Reset changes or still not sure: */
+ const int iResult = msgCenter().confirmCloudProfileManagerClosing(window());
+ switch (iResult)
+ {
+ case AlertButton_Choice1:
+ {
+ sltApplyCloudProfileDetailsChanges();
+ return true;
+ }
+ case AlertButton_Choice2:
+ {
+ sltResetCloudProfileDetailsChanges();
+ return true;
+ }
+ default:
+ break;
+ }
+
+ /* False by default: */
+ return false;
+}
+
+void UICloudProfileManagerWidget::sltResetCloudProfileDetailsChanges()
+{
+ /* Just push the current-item data there again: */
+ sltHandleCurrentItemChange();
+}
+
+void UICloudProfileManagerWidget::sltApplyCloudProfileDetailsChanges()
+{
+ /* It can be that this is provider item, not profile item currently selected.
+ * In such case we are not applying parameters, we are creating new one profile. */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemCloudProvider *pMaybeProviderItem = qobject_cast<UIItemCloudProvider*>(pItem);
+ if (pMaybeProviderItem)
+ return sltAddCloudProfile();
+
+ /* Get profile item: */
+ UIItemCloudProfile *pProfileItem = qobject_cast<UIItemCloudProfile*>(pItem);
+ AssertPtrReturnVoid(pProfileItem);
+ /* Get provider item: */
+ UIItemCloudProvider *pProviderItem = qobject_cast<UIItemCloudProvider*>(pProfileItem->parentItem());
+ AssertPtrReturnVoid(pProviderItem);
+
+ /* Acquire provider short name: */
+ const QString strShortName = pProviderItem->data(Column_Name, Data_ProviderShortName).toString();
+
+ /* Look for corresponding provider: */
+ CCloudProvider comCloudProvider = cloudProviderByShortName(strShortName);
+ if (comCloudProvider.isNotNull())
+ {
+ /* Get old/new data: */
+ UIDataCloudProfile oldData = *pProfileItem;
+ UIDataCloudProfile newData = m_pDetailsWidget->data();
+
+ /* Look for corresponding profile: */
+ CCloudProfile comCloudProfile = cloudProfileByName(strShortName, oldData.m_strName);
+ if (comCloudProfile.isNotNull())
+ {
+ /* Set profile name, if necessary: */
+ if (newData.m_strName != oldData.m_strName)
+ comCloudProfile.SetName(newData.m_strName);
+ /* Show error message if necessary: */
+ if (!comCloudProfile.isOk())
+ UINotificationMessage::cannotChangeCloudProfileParameter(comCloudProfile);
+ else
+ {
+ /* Iterate through old/new data: */
+ foreach (const QString &strKey, oldData.m_data.keys())
+ {
+ /* Get values: */
+ const QString strOldValue = oldData.m_data.value(strKey).first;
+ const QString strNewValue = newData.m_data.value(strKey).first;
+ if (strNewValue != strOldValue)
+ {
+ /* Apply property: */
+ comCloudProfile.SetProperty(strKey, strNewValue);
+ /* Show error message if necessary: */
+ if (!comCloudProfile.isOk())
+ {
+ UINotificationMessage::cannotChangeCloudProfileParameter(comCloudProfile);
+ break;
+ }
+ }
+ }
+ }
+
+ /* If profile is Ok finally: */
+ if (comCloudProfile.isOk())
+ {
+ /* Save profile changes: */
+ comCloudProvider.SaveProfiles();
+ /* Show error message if necessary: */
+ if (!comCloudProvider.isOk())
+ UINotificationMessage::cannotSaveCloudProfiles(comCloudProvider);
+ }
+ }
+ }
+}
+
+void UICloudProfileManagerWidget::sltAddCloudProfile()
+{
+ /* Get provider item: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemCloudProvider *pProviderItem = qobject_cast<UIItemCloudProvider*>(pItem);
+ AssertPtrReturnVoid(pProviderItem);
+
+ /* Acquire profile name if not proposed by details widget: */
+ QString strProfileName = m_pDetailsWidget->data().m_strName;
+ if (strProfileName.isEmpty())
+ {
+ bool fCancelled = true;
+ QISafePointerInputDialog pDialog = new QIInputDialog(this);
+ if (pDialog)
+ {
+#ifndef VBOX_WS_MAC
+ pDialog->setWindowIcon(UIIconPool::iconSetFull(":/cloud_profile_add_32px.png", ":/cloud_profile_add_16px.png"));
+#endif
+ pDialog->setWindowTitle(UICloudProfileManager::tr("Add Profile"));
+ if (pDialog->exec() == QDialog::Accepted)
+ {
+ strProfileName = pDialog->textValue();
+ fCancelled = false;
+ }
+ delete pDialog;
+ }
+ if (fCancelled)
+ return;
+ }
+
+ /* Acquire provider short name: */
+ const QString strShortName = pProviderItem->data(Column_Name, Data_ProviderShortName).toString();
+
+ /* Look for corresponding provider: */
+ CCloudProvider comCloudProvider = cloudProviderByShortName(strShortName);
+ if (comCloudProvider.isNotNull())
+ {
+ /* Create new profile: */
+ const QVector<QString> keys = pProviderItem->m_propertyDescriptions.keys().toVector();
+ const QVector<QString> values(keys.size());
+ comCloudProvider.CreateProfile(strProfileName, keys, values);
+ /* Show error message if necessary: */
+ if (!comCloudProvider.isOk())
+ UINotificationMessage::cannotCreateCloudProfile(comCloudProvider);
+ else
+ {
+ /* Save profile changes: */
+ comCloudProvider.SaveProfiles();
+ /* Show error message if necessary: */
+ if (!comCloudProvider.isOk())
+ UINotificationMessage::cannotSaveCloudProfiles(comCloudProvider);
+ }
+ }
+}
+
+void UICloudProfileManagerWidget::sltImportCloudProfiles()
+{
+ /* Get provider item: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemCloudProvider *pProviderItem = qobject_cast<UIItemCloudProvider*>(pItem);
+ AssertPtrReturnVoid(pProviderItem);
+
+ /* If there are profiles exist => confirm cloud profile import. */
+ if ( pProviderItem->childCount() != 0
+ && !msgCenter().confirmCloudProfilesImport(this))
+ return;
+
+ /* Acquire provider short name: */
+ const QString strShortName = pProviderItem->data(Column_Name, Data_ProviderShortName).toString();
+
+ /* Look for corresponding provider: */
+ CCloudProvider comCloudProvider = cloudProviderByShortName(strShortName);
+ if (comCloudProvider.isNotNull())
+ {
+ /* Import profiles: */
+ comCloudProvider.ImportProfiles();
+ /* Show error message if necessary: */
+ if (!comCloudProvider.isOk())
+ UINotificationMessage::cannotImportCloudProfiles(comCloudProvider);
+ }
+}
+
+void UICloudProfileManagerWidget::sltRemoveCloudProfile()
+{
+ /* Get profile item: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemCloudProfile *pProfileItem = qobject_cast<UIItemCloudProfile*>(pItem);
+ AssertPtrReturnVoid(pProfileItem);
+ /* Get provider item: */
+ UIItemCloudProvider *pProviderItem = qobject_cast<UIItemCloudProvider*>(pProfileItem->parentItem());
+ AssertPtrReturnVoid(pProviderItem);
+
+ /* Acquire profile name: */
+ const QString strProfileName = pProfileItem->name();
+
+ /* Confirm cloud profile removal: */
+ if (!msgCenter().confirmCloudProfileRemoval(strProfileName, this))
+ return;
+
+ /* Acquire provider short name: */
+ const QString strShortName = pProviderItem->data(Column_Name, Data_ProviderShortName).toString();
+
+ /* Look for corresponding provider: */
+ CCloudProvider comCloudProvider = cloudProviderByShortName(strShortName);
+ if (comCloudProvider.isNotNull())
+ {
+ /* Look for corresponding profile: */
+ CCloudProfile comCloudProfile = cloudProfileByName(strShortName, strProfileName);
+ if (comCloudProfile.isNotNull())
+ {
+ /* Remove current profile: */
+ comCloudProfile.Remove();
+ /* Show error message if necessary: */
+ if (!comCloudProfile.isOk())
+ UINotificationMessage::cannotRemoveCloudProfile(comCloudProfile);
+ else
+ {
+ /* Save profile changes: */
+ comCloudProvider.SaveProfiles();
+ /* Show error message if necessary: */
+ if (!comCloudProvider.isOk())
+ UINotificationMessage::cannotSaveCloudProfiles(comCloudProvider);
+ }
+ }
+ }
+}
+
+void UICloudProfileManagerWidget::sltToggleCloudProfileDetailsVisibility(bool fVisible)
+{
+ /* Save the setting: */
+ gEDataManager->setCloudProfileManagerDetailsExpanded(fVisible);
+ /* Show/hide details area and Apply button: */
+ m_pDetailsWidget->setVisible(fVisible);
+ /* Notify external lsiteners: */
+ emit sigCloudProfileDetailsVisibilityChanged(fVisible);
+}
+
+void UICloudProfileManagerWidget::sltShowCloudProfileTryPage()
+{
+ uiCommon().openURL("https://signup.cloud.oracle.com/");
+}
+
+void UICloudProfileManagerWidget::sltShowCloudProfileHelp()
+{
+ uiCommon().openURL("https://docs.cloud.oracle.com/iaas/Content/API/Concepts/sdkconfig.htm");
+}
+
+void UICloudProfileManagerWidget::sltPerformTableAdjustment()
+{
+ AssertPtrReturnVoid(m_pTreeWidget);
+ AssertPtrReturnVoid(m_pTreeWidget->header());
+ AssertPtrReturnVoid(m_pTreeWidget->viewport());
+ m_pTreeWidget->header()->resizeSection(0, m_pTreeWidget->viewport()->width() - m_pTreeWidget->header()->sectionSize(1));
+}
+
+void UICloudProfileManagerWidget::sltHandleCurrentItemChange()
+{
+ /* Check current-item type: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemCloudProvider *pItemProvider = qobject_cast<UIItemCloudProvider*>(pItem);
+ UIItemCloudProfile *pItemProfile = qobject_cast<UIItemCloudProfile*>(pItem);
+
+ /* Update actions availability: */
+ m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Add)->setEnabled(pItemProvider);
+ m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Import)->setEnabled(pItemProvider);
+ m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Remove)->setEnabled(pItemProfile);
+ m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details)->setEnabled(pItemProvider || pItemProfile);
+
+ /* If there is an item => update details data: */
+ if (pItemProfile)
+ m_pDetailsWidget->setData(*pItemProfile);
+ /* Otherwise => clear details data: */
+ else
+ m_pDetailsWidget->setData(UIDataCloudProfile());
+
+ /* Update details area visibility: */
+ sltToggleCloudProfileDetailsVisibility(pItem && m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details)->isChecked());
+}
+
+void UICloudProfileManagerWidget::sltHandleContextMenuRequest(const QPoint &position)
+{
+ /* Check clicked-item type: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->itemAt(position));
+ UIItemCloudProvider *pItemProvider = qobject_cast<UIItemCloudProvider*>(pItem);
+ UIItemCloudProfile *pItemProfile = qobject_cast<UIItemCloudProfile*>(pItem);
+
+ /* Compose temporary context-menu: */
+ QMenu menu;
+ if (pItemProfile)
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Remove));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details));
+ }
+ else if (pItemProvider)
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Add));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Import));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details));
+ }
+
+ /* And show it: */
+ menu.exec(m_pTreeWidget->viewport()->mapToGlobal(position));
+}
+
+void UICloudProfileManagerWidget::sltHandleItemChange(QTreeWidgetItem *pItem)
+{
+ /* Check item type: */
+ QITreeWidgetItem *pChangedItem = QITreeWidgetItem::toItem(pItem);
+ UIItemCloudProvider *pProviderItem = qobject_cast<UIItemCloudProvider*>(pChangedItem);
+ UIItemCloudProfile *pProfileItem = qobject_cast<UIItemCloudProfile*>(pChangedItem);
+
+ /* Check whether item is of provider or profile type, then check whether it changed: */
+ bool fChanged = false;
+ if (pProviderItem)
+ {
+ const UIDataCloudProvider oldData = *pProviderItem;
+ if ( (oldData.m_fRestricted && pProviderItem->checkState(Column_ListVMs) == Qt::Checked)
+ || (!oldData.m_fRestricted && pProviderItem->checkState(Column_ListVMs) == Qt::Unchecked))
+ fChanged = true;
+ }
+ else if (pProfileItem)
+ {
+ const UIDataCloudProfile oldData = *pProfileItem;
+ if ( (oldData.m_fRestricted && pProfileItem->checkState(Column_ListVMs) == Qt::Checked)
+ || (!oldData.m_fRestricted && pProfileItem->checkState(Column_ListVMs) == Qt::Unchecked))
+ fChanged = true;
+ }
+
+ /* Gather Cloud Profile Manager restrictions and save them to extra-data: */
+ if (fChanged)
+ gEDataManager->setCloudProfileManagerRestrictions(gatherCloudProfileManagerRestrictions(m_pTreeWidget->invisibleRootItem()));
+}
+
+void UICloudProfileManagerWidget::prepare()
+{
+ /* Prepare actions: */
+ prepareActions();
+ /* Prepare widgets: */
+ prepareWidgets();
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Load cloud stuff: */
+ loadCloudStuff();
+
+ /* Set help keyowrd for context sensitive help: */
+ uiCommon().setHelpKeyword(this, "cloud-using-cloud-profile-manager");
+
+}
+
+void UICloudProfileManagerWidget::prepareActions()
+{
+ /* First of all, add actions which has smaller shortcut scope: */
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Add));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Import));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Remove));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_TryPage));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Help));
+}
+
+void UICloudProfileManagerWidget::prepareWidgets()
+{
+ /* Create main-layout: */
+ new QVBoxLayout(this);
+ if (layout())
+ {
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ layout()->setSpacing(10);
+#else
+ layout()->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ /* Prepare toolbar, if requested: */
+ if (m_fShowToolbar)
+ prepareToolBar();
+ /* Prepare tree-widget: */
+ prepareTreeWidget();
+ /* Prepare details-widget: */
+ prepareDetailsWidget();
+ /* Prepare connections: */
+ prepareConnections();
+ }
+}
+
+void UICloudProfileManagerWidget::prepareToolBar()
+{
+ /* Create toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ if (m_pToolBar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+
+ /* Add toolbar actions: */
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Add));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Import));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Remove));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_TryPage));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Help));
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+ }
+#else
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+#endif
+ }
+}
+
+void UICloudProfileManagerWidget::prepareTreeWidget()
+{
+ /* Create tree-widget: */
+ m_pTreeWidget = new QITreeWidget;
+ if (m_pTreeWidget)
+ {
+ /* Configure tree-widget: */
+ m_pTreeWidget->header()->setStretchLastSection(false);
+ m_pTreeWidget->setRootIsDecorated(false);
+ m_pTreeWidget->setAlternatingRowColors(true);
+ m_pTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTreeWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTreeWidget->setColumnCount(Column_Max);
+ m_pTreeWidget->setSortingEnabled(true);
+ m_pTreeWidget->sortByColumn(Column_Name, Qt::AscendingOrder);
+ m_pTreeWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pTreeWidget);
+ }
+}
+
+void UICloudProfileManagerWidget::prepareDetailsWidget()
+{
+ /* Create details-widget: */
+ m_pDetailsWidget = new UICloudProfileDetailsWidget(m_enmEmbedding);
+ if (m_pDetailsWidget)
+ {
+ /* Configure details-widget: */
+ m_pDetailsWidget->setVisible(false);
+ m_pDetailsWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pDetailsWidget);
+ }
+}
+
+void UICloudProfileManagerWidget::prepareConnections()
+{
+ /* Action connections: */
+ connect(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Add), &QAction::triggered,
+ this, &UICloudProfileManagerWidget::sltAddCloudProfile);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Import), &QAction::triggered,
+ this, &UICloudProfileManagerWidget::sltImportCloudProfiles);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Remove), &QAction::triggered,
+ this, &UICloudProfileManagerWidget::sltRemoveCloudProfile);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details), &QAction::toggled,
+ this, &UICloudProfileManagerWidget::sltToggleCloudProfileDetailsVisibility);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_TryPage), &QAction::triggered,
+ this, &UICloudProfileManagerWidget::sltShowCloudProfileTryPage);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Cloud_S_Help), &QAction::triggered,
+ this, &UICloudProfileManagerWidget::sltShowCloudProfileHelp);
+
+ /* Tree-widget connections: */
+ connect(m_pTreeWidget, &QITreeWidget::resized,
+ this, &UICloudProfileManagerWidget::sltPerformTableAdjustment, Qt::QueuedConnection);
+ connect(m_pTreeWidget->header(), &QHeaderView::sectionResized,
+ this, &UICloudProfileManagerWidget::sltPerformTableAdjustment, Qt::QueuedConnection);
+ connect(m_pTreeWidget, &QITreeWidget::currentItemChanged,
+ this, &UICloudProfileManagerWidget::sltHandleCurrentItemChange);
+ connect(m_pTreeWidget, &QITreeWidget::customContextMenuRequested,
+ this, &UICloudProfileManagerWidget::sltHandleContextMenuRequest);
+ connect(m_pTreeWidget, &QITreeWidget::itemDoubleClicked,
+ m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details), &QAction::setChecked);
+ connect(m_pTreeWidget, &QITreeWidget::itemChanged,
+ this, &UICloudProfileManagerWidget::sltHandleItemChange);
+
+ /* Details-widget connections: */
+ connect(m_pDetailsWidget, &UICloudProfileDetailsWidget::sigDataChanged,
+ this, &UICloudProfileManagerWidget::sigCloudProfileDetailsDataChanged);
+ connect(m_pDetailsWidget, &UICloudProfileDetailsWidget::sigDataChangeRejected,
+ this, &UICloudProfileManagerWidget::sltResetCloudProfileDetailsChanges);
+ connect(m_pDetailsWidget, &UICloudProfileDetailsWidget::sigDataChangeAccepted,
+ this, &UICloudProfileManagerWidget::sltApplyCloudProfileDetailsChanges);
+
+ /* Extra-data connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProviderListChanged,
+ this, &UICloudProfileManagerWidget::sltLoadCloudStuff);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UICloudProfileManagerWidget::sltLoadCloudStuff);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UICloudProfileManagerWidget::sltLoadCloudStuff);
+ connect(gEDataManager, &UIExtraDataManager::sigCloudProfileManagerRestrictionChange,
+ this, &UICloudProfileManagerWidget::sltLoadCloudStuff);
+}
+
+void UICloudProfileManagerWidget::loadSettings()
+{
+ /* Details action/widget: */
+ m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details)->setChecked(gEDataManager->cloudProfileManagerDetailsExpanded());
+ sltToggleCloudProfileDetailsVisibility(m_pActionPool->action(UIActionIndexMN_M_Cloud_T_Details)->isChecked());
+}
+
+void UICloudProfileManagerWidget::loadCloudStuff()
+{
+ /* Save current item definition: */
+ QITreeWidgetItem *pCurrentItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ const QString strDefinition = pCurrentItem ? pCurrentItem->data(Column_Name, Data_Definition).toString() : QString();
+
+ /* Clear tree first of all: */
+ m_pTreeWidget->clear();
+
+ /* Acquire cloud profile manager restrictions: */
+ const QStringList restrictions = gEDataManager->cloudProfileManagerRestrictions();
+
+ /* Iterate through existing providers: */
+ foreach (const CCloudProvider &comCloudProvider, listCloudProviders())
+ {
+ /* Skip if we have nothing to populate: */
+ if (comCloudProvider.isNull())
+ continue;
+
+ /* Load provider data: */
+ UIDataCloudProvider providerData;
+ loadCloudProvider(comCloudProvider, restrictions, providerData);
+ createItemForCloudProvider(providerData);
+
+ /* Make sure provider item is properly inserted: */
+ QTreeWidgetItem *pItem = searchItem(UIItemCloudProvider::definition(providerData.m_strShortName));
+ AssertPtrReturnVoid(pItem);
+
+ /* Iterate through provider's profiles: */
+ foreach (const CCloudProfile &comCloudProfile, listCloudProfiles(comCloudProvider))
+ {
+ /* Skip if we have nothing to populate: */
+ if (comCloudProfile.isNull())
+ continue;
+
+ /* Load profile data: */
+ UIDataCloudProfile profileData;
+ loadCloudProfile(comCloudProfile, restrictions, providerData, profileData);
+ createItemForCloudProfile(pItem, profileData);
+ }
+
+ /* Expand provider item finally: */
+ pItem->setExpanded(true);
+ }
+
+ /* Try to restore current item by definition: */
+ if (!strDefinition.isEmpty())
+ m_pTreeWidget->setCurrentItem(searchItem(strDefinition));
+ /* Choose the 1st item as current if nothing chosen: */
+ if (!m_pTreeWidget->currentItem())
+ m_pTreeWidget->setCurrentItem(m_pTreeWidget->topLevelItem(0));
+ /* Handle current item change in any case: */
+ sltHandleCurrentItemChange();
+}
+
+void UICloudProfileManagerWidget::loadCloudProvider(const CCloudProvider &comProvider,
+ const QStringList &restrictions,
+ UIDataCloudProvider &providerData)
+{
+ /* Gather provider settings: */
+ if (comProvider.isOk())
+ cloudProviderId(comProvider, providerData.m_uId);
+ if (comProvider.isOk())
+ cloudProviderShortName(comProvider, providerData.m_strShortName);
+ if (comProvider.isOk())
+ cloudProviderName(comProvider, providerData.m_strName);
+ providerData.m_fRestricted = restrictions.contains(UIItemCloudProvider::definition(providerData.m_strShortName));
+ foreach (const QString &strSupportedPropertyName, comProvider.GetSupportedPropertyNames())
+ providerData.m_propertyDescriptions[strSupportedPropertyName] = comProvider.GetPropertyDescription(strSupportedPropertyName);
+}
+
+void UICloudProfileManagerWidget::loadCloudProfile(const CCloudProfile &comProfile,
+ const QStringList &restrictions,
+ const UIDataCloudProvider &providerData,
+ UIDataCloudProfile &profileData)
+{
+ /* Gather provider settings: */
+ profileData.m_strProviderShortName = providerData.m_strShortName;
+
+ /* Gather profile settings: */
+ if (comProfile.isOk())
+ cloudProfileName(comProfile, profileData.m_strName);
+ profileData.m_fRestricted = restrictions.contains(UIItemCloudProfile::definition(providerData.m_strShortName, profileData.m_strName));
+ if (comProfile.isOk())
+ {
+ QVector<QString> keys;
+ QVector<QString> values;
+ if (cloudProfileProperties(comProfile, keys, values))
+ for (int i = 0; i < keys.size(); ++i)
+ profileData.m_data[keys.at(i)] = qMakePair(values.at(i), providerData.m_propertyDescriptions.value(keys.at(i)));
+ }
+}
+
+QTreeWidgetItem *UICloudProfileManagerWidget::searchItem(const QString &strDefinition,
+ QTreeWidgetItem *pParentItem /* = 0 */) const
+{
+ /* If no parent-item passed => we will start from the invisible-root-item: */
+ if (!pParentItem)
+ pParentItem = m_pTreeWidget->invisibleRootItem();
+
+ /* Check whether parent-item is of required type: */
+ QITreeWidgetItem *pParentItemOfType = QITreeWidgetItem::toItem(pParentItem);
+ if (pParentItemOfType)
+ {
+ /* Check if parent-item has required definition: */
+ if (pParentItemOfType->data(Column_Name, Data_Definition).toString() == strDefinition)
+ return pParentItem;
+ }
+
+ /* Iterate through parent-item children: */
+ for (int i = 0; i < pParentItem->childCount(); ++i)
+ if (QTreeWidgetItem *pChildItem = searchItem(strDefinition, pParentItem->child(i)))
+ return pChildItem;
+
+ /* Null by default: */
+ return 0;
+}
+
+void UICloudProfileManagerWidget::createItemForCloudProvider(const UIDataCloudProvider &providerData)
+{
+ /* Create new provider item: */
+ UIItemCloudProvider *pItem = new UIItemCloudProvider;
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->UIDataCloudProvider::operator=(providerData);
+ pItem->updateFields();
+ /* Add item to the tree: */
+ m_pTreeWidget->addTopLevelItem(pItem);
+ }
+}
+
+void UICloudProfileManagerWidget::createItemForCloudProfile(QTreeWidgetItem *pParent,
+ const UIDataCloudProfile &profileData)
+{
+ /* Create new profile item: */
+ UIItemCloudProfile *pItem = new UIItemCloudProfile;
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->UIDataCloudProfile::operator=(profileData);
+ pItem->updateFields();
+ /* Add item to the parent: */
+ pParent->addChild(pItem);
+ }
+}
+
+QStringList UICloudProfileManagerWidget::gatherCloudProfileManagerRestrictions(QTreeWidgetItem *pParentItem)
+{
+ /* Prepare result: */
+ QStringList result;
+ AssertPtrReturn(pParentItem, result);
+
+ /* Process unchecked QITreeWidgetItem(s) only: */
+ QITreeWidgetItem *pChangedItem = QITreeWidgetItem::toItem(pParentItem);
+ if ( pChangedItem
+ && pChangedItem->checkState(Column_ListVMs) == Qt::Unchecked)
+ result << pChangedItem->data(Column_Name, Data_Definition).toString();
+
+ /* Iterate through children recursively: */
+ for (int i = 0; i < pParentItem->childCount(); ++i)
+ result << gatherCloudProfileManagerRestrictions(pParentItem->child(i));
+
+ /* Return result: */
+ return result;
+}
+
+
+/*********************************************************************************************************************************
+* Class UICloudProfileManagerFactory implementation. *
+*********************************************************************************************************************************/
+
+UICloudProfileManagerFactory::UICloudProfileManagerFactory(UIActionPool *pActionPool /* = 0 */)
+ : m_pActionPool(pActionPool)
+{
+}
+
+void UICloudProfileManagerFactory::create(QIManagerDialog *&pDialog, QWidget *pCenterWidget)
+{
+ pDialog = new UICloudProfileManager(pCenterWidget, m_pActionPool);
+}
+
+
+/*********************************************************************************************************************************
+* Class UICloudProfileManager implementation. *
+*********************************************************************************************************************************/
+
+UICloudProfileManager::UICloudProfileManager(QWidget *pCenterWidget, UIActionPool *pActionPool)
+ : QIWithRetranslateUI<QIManagerDialog>(pCenterWidget)
+ , m_pActionPool(pActionPool)
+{
+}
+
+void UICloudProfileManager::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+ /* Disable buttons first of all: */
+ button(ButtonType_Reset)->setEnabled(false);
+ button(ButtonType_Apply)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if (pButton == button(ButtonType_Reset))
+ emit sigDataChangeRejected();
+ else
+ if (pButton == button(ButtonType_Apply))
+ emit sigDataChangeAccepted();
+}
+
+void UICloudProfileManager::retranslateUi()
+{
+ /* Translate window title: */
+ setWindowTitle(tr("Cloud Profile Manager"));
+
+ /* Translate buttons: */
+ button(ButtonType_Reset)->setText(tr("Reset"));
+ button(ButtonType_Apply)->setText(tr("Apply"));
+ button(ButtonType_Close)->setText(tr("Close"));
+ button(ButtonType_Help)->setText(tr("Help"));
+ button(ButtonType_Reset)->setStatusTip(tr("Reset changes in current cloud profile details"));
+ button(ButtonType_Apply)->setStatusTip(tr("Apply changes in current cloud profile details"));
+ button(ButtonType_Close)->setStatusTip(tr("Close dialog without saving"));
+ button(ButtonType_Help)->setStatusTip(tr("Show dialog help"));
+ button(ButtonType_Reset)->setShortcut(QString("Ctrl+Backspace"));
+ button(ButtonType_Apply)->setShortcut(QString("Ctrl+Return"));
+ button(ButtonType_Close)->setShortcut(Qt::Key_Escape);
+ button(ButtonType_Help)->setShortcut(QKeySequence::HelpContents);
+ button(ButtonType_Reset)->setToolTip(tr("Reset Changes (%1)").arg(button(ButtonType_Reset)->shortcut().toString()));
+ button(ButtonType_Apply)->setToolTip(tr("Apply Changes (%1)").arg(button(ButtonType_Apply)->shortcut().toString()));
+ button(ButtonType_Close)->setToolTip(tr("Close Window (%1)").arg(button(ButtonType_Close)->shortcut().toString()));
+ button(ButtonType_Help)->setToolTip(tr("Show Help (%1)").arg(button(ButtonType_Help)->shortcut().toString()));
+}
+
+void UICloudProfileManager::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/cloud_profile_manager_32px.png", ":/cloud_profile_manager_16px.png"));
+#endif
+}
+
+void UICloudProfileManager::configureCentralWidget()
+{
+ /* Create widget: */
+ UICloudProfileManagerWidget *pWidget = new UICloudProfileManagerWidget(EmbedTo_Dialog, m_pActionPool, true, this);
+ if (pWidget)
+ {
+ /* Configure widget: */
+ setWidget(pWidget);
+ setWidgetMenu(pWidget->menu());
+#ifdef VBOX_WS_MAC
+ setWidgetToolbar(pWidget->toolbar());
+#endif
+ connect(this, &UICloudProfileManager::sigDataChangeRejected,
+ pWidget, &UICloudProfileManagerWidget::sltResetCloudProfileDetailsChanges);
+ connect(this, &UICloudProfileManager::sigDataChangeAccepted,
+ pWidget, &UICloudProfileManagerWidget::sltApplyCloudProfileDetailsChanges);
+
+ /* Add into layout: */
+ centralWidget()->layout()->addWidget(pWidget);
+ }
+}
+
+void UICloudProfileManager::configureButtonBox()
+{
+ /* Configure button-box: */
+ connect(widget(), &UICloudProfileManagerWidget::sigCloudProfileDetailsVisibilityChanged,
+ button(ButtonType_Apply), &QPushButton::setVisible);
+ connect(widget(), &UICloudProfileManagerWidget::sigCloudProfileDetailsVisibilityChanged,
+ button(ButtonType_Reset), &QPushButton::setVisible);
+ connect(widget(), &UICloudProfileManagerWidget::sigCloudProfileDetailsDataChanged,
+ button(ButtonType_Apply), &QPushButton::setEnabled);
+ connect(widget(), &UICloudProfileManagerWidget::sigCloudProfileDetailsDataChanged,
+ button(ButtonType_Reset), &QPushButton::setEnabled);
+ connect(buttonBox(), &QIDialogButtonBox::clicked,
+ this, &UICloudProfileManager::sltHandleButtonBoxClick);
+ // WORKAROUND:
+ // Since we connected signals later than extra-data loaded
+ // for signals above, we should handle that stuff here again:
+ button(ButtonType_Apply)->setVisible(gEDataManager->cloudProfileManagerDetailsExpanded());
+ button(ButtonType_Reset)->setVisible(gEDataManager->cloudProfileManagerDetailsExpanded());
+}
+
+void UICloudProfileManager::finalize()
+{
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UICloudProfileManagerWidget *UICloudProfileManager::widget()
+{
+ return qobject_cast<UICloudProfileManagerWidget*>(QIManagerDialog::widget());
+}
+
+void UICloudProfileManager::closeEvent(QCloseEvent *pEvent)
+{
+ /* Make sure all changes resolved: */
+ if (widget()->makeSureChangesResolved())
+ {
+ /* Call to base class: */
+ QIWithRetranslateUI<QIManagerDialog>::closeEvent(pEvent);
+ }
+ else
+ {
+ /* Just ignore the event otherwise: */
+ pEvent->ignore();
+ }
+}
+
+
+#include "UICloudProfileManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileManager.h b/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileManager.h
new file mode 100644
index 00000000..604cfb27
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/cloud/profilemanager/UICloudProfileManager.h
@@ -0,0 +1,306 @@
+/* $Id: UICloudProfileManager.h $ */
+/** @file
+ * VBox Qt GUI - UICloudProfileManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_cloud_profilemanager_UICloudProfileManager_h
+#define FEQT_INCLUDED_SRC_cloud_profilemanager_UICloudProfileManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QTreeWidgetItem;
+class QITreeWidget;
+class UIActionPool;
+class UICloudProfileDetailsWidget;
+class UIItemCloudProfile;
+class UIItemCloudProvider;
+class QIToolBar;
+struct UIDataCloudProfile;
+struct UIDataCloudProvider;
+class CCloudProfile;
+class CCloudProvider;
+
+
+/** QWidget extension providing GUI with the pane to control cloud profile related functionality. */
+class UICloudProfileManagerWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about cloud profile details-widget @a fVisible. */
+ void sigCloudProfileDetailsVisibilityChanged(bool fVisible);
+ /** Notifies listeners about cloud profile details data @a fDiffers. */
+ void sigCloudProfileDetailsDataChanged(bool fDiffers);
+
+public:
+
+ /** Constructs Cloud Profile Manager widget.
+ * @param enmEmbedding Brings the type of widget embedding.
+ * @param pActionPool Brings the action-pool reference.
+ * @param fShowToolbar Brings whether we should create/show toolbar. */
+ UICloudProfileManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar = true, QWidget *pParent = 0);
+
+ /** Returns the menu. */
+ QMenu *menu() const;
+
+#ifdef VBOX_WS_MAC
+ /** Returns the toolbar. */
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+ /** Check for changes committed.
+ * @returns Whether changes were resolved (accepted or discarded) or still a problem otherwise. */
+ bool makeSureChangesResolved();
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+public slots:
+
+ /** @name Details-widget stuff.
+ * @{ */
+ /** Handles command to reset cloud profile details changes. */
+ void sltResetCloudProfileDetailsChanges();
+ /** Handles command to apply cloud profile details changes. */
+ void sltApplyCloudProfileDetailsChanges();
+ /** @} */
+
+private slots:
+
+ /** @name Menu/action stuff.
+ * @{ */
+ /** Handles command to add cloud profile. */
+ void sltAddCloudProfile();
+ /** Handles command to import cloud profiles. */
+ void sltImportCloudProfiles();
+ /** Handles command to remove cloud profile. */
+ void sltRemoveCloudProfile();
+ /** Handles command to make cloud profile details @a fVisible. */
+ void sltToggleCloudProfileDetailsVisibility(bool fVisible);
+ /** Handles command to show cloud profile try page. */
+ void sltShowCloudProfileTryPage();
+ /** Handles command to show cloud profile help. */
+ void sltShowCloudProfileHelp();
+ /** @} */
+
+ /** @name Tree-widget stuff.
+ * @{ */
+ /** Handles request to load cloud stuff. */
+ void sltLoadCloudStuff() { loadCloudStuff(); }
+ /** Adjusts tree-widget according content. */
+ void sltPerformTableAdjustment();
+ /** Handles tree-widget current item change. */
+ void sltHandleCurrentItemChange();
+ /** Handles context-menu request for tree-widget @a position. */
+ void sltHandleContextMenuRequest(const QPoint &position);
+ /** Handles tree-widget @a pItem change. */
+ void sltHandleItemChange(QTreeWidgetItem *pItem);
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares actions. */
+ void prepareActions();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares toolbar. */
+ void prepareToolBar();
+ /** Prepares tree-widget. */
+ void prepareTreeWidget();
+ /** Prepares details-widget. */
+ void prepareDetailsWidget();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Load settings: */
+ void loadSettings();
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+ /** Loads cloud stuff. */
+ void loadCloudStuff();
+ /** Loads cloud @a comProvider data to passed @a providerData container,
+ * using @a restrictions as hint. */
+ void loadCloudProvider(const CCloudProvider &comProvider,
+ const QStringList &restrictions,
+ UIDataCloudProvider &providerData);
+ /** Loads cloud @a comProfile data to passed @a profileData container,
+ * using @a restrictions & @a providerData as hint. */
+ void loadCloudProfile(const CCloudProfile &comProfile,
+ const QStringList &restrictions,
+ const UIDataCloudProvider &providerData,
+ UIDataCloudProfile &profileData);
+ /** @} */
+
+ /** @name Tree-widget stuff.
+ * @{ */
+ /** Recursively searches for an item with specified @a strDefinition,
+ * using @a pParentItem as an item to start search from. */
+ QTreeWidgetItem *searchItem(const QString &strDefinition,
+ QTreeWidgetItem *pParentItem = 0) const;
+
+ /** Creates a new tree-widget item
+ * on the basis of passed @a providerData. */
+ void createItemForCloudProvider(const UIDataCloudProvider &providerData);
+ /** Creates a new tree-widget item as a child of certain @a pParent,
+ * on the basis of passed @a profileData. */
+ void createItemForCloudProfile(QTreeWidgetItem *pParent, const UIDataCloudProfile &profileData);
+
+ /* Gathers a list of Cloud Profile Manager restrictions starting from @a pParentItem. */
+ QStringList gatherCloudProfileManagerRestrictions(QTreeWidgetItem *pParentItem);
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** Holds whether we should create/show toolbar. */
+ const bool m_fShowToolbar;
+ /** @} */
+
+ /** @name Toolbar and menu variables.
+ * @{ */
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolBar;
+ /** @} */
+
+ /** @name Splitter variables.
+ * @{ */
+ /** Holds the tree-widget instance. */
+ QITreeWidget *m_pTreeWidget;
+ /** Holds the details-widget instance. */
+ UICloudProfileDetailsWidget *m_pDetailsWidget;
+ /** @} */
+};
+
+
+/** QIManagerDialogFactory extension used as a factory for Cloud Profile Manager dialog. */
+class UICloudProfileManagerFactory : public QIManagerDialogFactory
+{
+public:
+
+ /** Constructs Cloud Profile Manager factory acquiring additional arguments.
+ * @param pActionPool Brings the action-pool reference. */
+ UICloudProfileManagerFactory(UIActionPool *pActionPool = 0);
+
+protected:
+
+ /** Creates derived @a pDialog instance.
+ * @param pCenterWidget Brings the widget reference to center according to. */
+ virtual void create(QIManagerDialog *&pDialog, QWidget *pCenterWidget) RT_OVERRIDE;
+
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+};
+
+
+/** QIManagerDialog extension providing GUI with the dialog to control cloud profile related functionality. */
+class UICloudProfileManager : public QIWithRetranslateUI<QIManagerDialog>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+private slots:
+
+ /** @name Button-box stuff.
+ * @{ */
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** Constructs Cloud Profile Manager dialog.
+ * @param pCenterWidget Brings the widget reference to center according to.
+ * @param pActionPool Brings the action-pool reference. */
+ UICloudProfileManager(QWidget *pCenterWidget, UIActionPool *pActionPool);
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ virtual void configure() RT_OVERRIDE;
+ /** Configures central-widget. */
+ virtual void configureCentralWidget() RT_OVERRIDE;
+ /** Configures button-box. */
+ virtual void configureButtonBox() RT_OVERRIDE;
+ /** Perform final preparations. */
+ virtual void finalize() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Widget stuff.
+ * @{ */
+ /** Returns the widget. */
+ virtual UICloudProfileManagerWidget *widget() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles close @a pEvent. */
+ virtual void closeEvent(QCloseEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Action related variables.
+ * @{ */
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** @} */
+
+ /** Allow factory access to private/protected members: */
+ friend class UICloudProfileManagerFactory;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_cloud_profilemanager_UICloudProfileManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/converter/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/converter/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/converter/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/converter/UIConverter.cpp b/src/VBox/Frontends/VirtualBox/src/converter/UIConverter.cpp
new file mode 100644
index 00000000..5e7128b3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/converter/UIConverter.cpp
@@ -0,0 +1,47 @@
+/* $Id: UIConverter.cpp $ */
+/** @file
+ * VBox Qt GUI - UIConverter implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIConverter.h"
+
+
+/* static */
+UIConverter* UIConverter::s_pInstance = 0;
+
+/* static */
+void UIConverter::create()
+{
+ AssertReturnVoid(!s_pInstance);
+ new UIConverter;
+}
+
+/* static */
+void UIConverter::destroy()
+{
+ AssertPtrReturnVoid(s_pInstance);
+ delete s_pInstance;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/converter/UIConverter.h b/src/VBox/Frontends/VirtualBox/src/converter/UIConverter.h
new file mode 100644
index 00000000..bd59cd69
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/converter/UIConverter.h
@@ -0,0 +1,142 @@
+/* $Id: UIConverter.h $ */
+/** @file
+ * VBox Qt GUI - UIConverter declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_converter_UIConverter_h
+#define FEQT_INCLUDED_SRC_converter_UIConverter_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIConverterBackend.h"
+
+/** High-level interface for different conversions between GUI classes.
+ * @todo Replace singleton with static template interface. */
+class SHARED_LIBRARY_STUFF UIConverter
+{
+public:
+
+ /** Returns singleton instance. */
+ static UIConverter *instance() { return s_pInstance; }
+
+ /** Creates singleton instance. */
+ static void create();
+ /** Destroys singleton instance. */
+ static void destroy();
+
+ /** Converts QColor <= template class. */
+ template<class T> QColor toColor(const T &data) const
+ {
+ if (canConvert<T>())
+ return ::toColor(data);
+ AssertFailed();
+ return QColor();
+ }
+
+ /** Converts QIcon <= template class. */
+ template<class T> QIcon toIcon(const T &data) const
+ {
+ if (canConvert<T>())
+ return ::toIcon(data);
+ AssertFailed();
+ return QIcon();
+ }
+ /** Converts QPixmap <= template class. */
+ template<class T> QPixmap toWarningPixmap(const T &data) const
+ {
+ if (canConvert<T>())
+ return ::toWarningPixmap(data);
+ AssertFailed();
+ return QPixmap();
+ }
+
+ /** Converts QString <= template class. */
+ template<class T> QString toString(const T &data) const
+ {
+ if (canConvert<T>())
+ return ::toString(data);
+ AssertFailed();
+ return QString();
+ }
+ /** Converts template class <= QString. */
+ template<class T> T fromString(const QString &strData) const
+ {
+ if (canConvert<T>())
+ return ::fromString<T>(strData);
+ AssertFailed();
+ return T();
+ }
+
+ /** Converts QString <= template class. */
+ template<class T> QString toInternalString(const T &data) const
+ {
+ if (canConvert<T>())
+ return ::toInternalString(data);
+ AssertFailed();
+ return QString();
+ }
+ /** Converts template class <= QString. */
+ template<class T> T fromInternalString(const QString &strData) const
+ {
+ if (canConvert<T>())
+ return ::fromInternalString<T>(strData);
+ AssertFailed();
+ return T();
+ }
+
+ /** Converts int <= template class. */
+ template<class T> int toInternalInteger(const T &data) const
+ {
+ if (canConvert<T>())
+ return ::toInternalInteger(data);
+ AssertFailed();
+ return 0;
+ }
+ /** Converts template class <= int. */
+ template<class T> T fromInternalInteger(const int &iData) const
+ {
+ if (canConvert<T>())
+ return ::fromInternalInteger<T>(iData);
+ AssertFailed();
+ return T();
+ }
+
+private:
+
+ /** Constructs converter. */
+ UIConverter() { s_pInstance = this; }
+ /** Destructs converter. */
+ virtual ~UIConverter() /* override final */ { s_pInstance = 0; }
+
+ /** Holds the static instance. */
+ static UIConverter *s_pInstance;
+};
+
+/** Singleton UI converter 'official' name. */
+#define gpConverter UIConverter::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_converter_UIConverter_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackend.h b/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackend.h
new file mode 100644
index 00000000..68f1821c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackend.h
@@ -0,0 +1,354 @@
+/* $Id: UIConverterBackend.h $ */
+/** @file
+ * VBox Qt GUI - UIConverterBackend declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_converter_UIConverterBackend_h
+#define FEQT_INCLUDED_SRC_converter_UIConverterBackend_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QColor>
+#include <QIcon>
+#include <QPixmap>
+#include <QString>
+
+/* GUI includes: */
+#include "UIDefs.h"
+#include "UILibraryDefs.h"
+#include "UIMediumDefs.h"
+#include "UIExtraDataDefs.h"
+#include "UISettingsDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/* Determines if 'Object of type X' can be converted to object of other type.
+ * This function always returns 'false' until re-determined for specific object type. */
+template<class X> bool canConvert() { return false; }
+
+/* Converts passed 'Object X' to QColor.
+ * This function returns null QColor for any object type until re-determined for specific one. */
+template<class X> QColor toColor(const X & /* xobject */) { AssertFailed(); return QColor(); }
+
+/* Converts passed 'Object X' to QIcon.
+ * This function returns null QIcon for any object type until re-determined for specific one. */
+template<class X> QIcon toIcon(const X & /* xobject */) { AssertFailed(); return QIcon(); }
+
+/* Converts passed 'Object X' to QPixmap.
+ * This function returns null QPixmap for any object type until re-determined for specific one. */
+template<class X> QPixmap toWarningPixmap(const X & /* xobject */) { AssertFailed(); return QPixmap(); }
+
+/* Converts passed 'Object of type X' to QString.
+ * This function returns null QString for any object type until re-determined for specific one. */
+template<class X> QString toString(const X & /* xobject */) { AssertFailed(); return QString(); }
+/* Converts passed QString to 'Object of type X'.
+ * This function returns default constructed object for any object type until re-determined for specific one. */
+template<class X> X fromString(const QString & /* strData */) { AssertFailed(); return X(); }
+
+/* Converts passed 'Object of type X' to non-translated QString.
+ * This function returns null QString for any object type until re-determined for specific one. */
+template<class X> QString toInternalString(const X & /* xobject */) { AssertFailed(); return QString(); }
+/* Converts passed non-translated QString to 'Object of type X'.
+ * This function returns default constructed object for any object type until re-determined for specific one. */
+template<class X> X fromInternalString(const QString & /* strData */) { AssertFailed(); return X(); }
+
+/* Converts passed 'Object of type X' to abstract integer.
+ * This function returns 0 for any object type until re-determined for specific one. */
+template<class X> int toInternalInteger(const X & /* xobject */) { AssertFailed(); return 0; }
+/* Converts passed abstract integer to 'Object of type X'.
+ * This function returns default constructed object for any object type until re-determined for specific one. */
+template<class X> X fromInternalInteger(const int & /* iData */) { AssertFailed(); return X(); }
+
+
+/* Declare global canConvert specializations: */
+template<> SHARED_LIBRARY_STUFF bool canConvert<Qt::Alignment>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<Qt::SortOrder>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<SizeSuffix>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<StorageSlot>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<DesktopWatchdogPolicy_SynthTest>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DialogType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::MenuType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::MenuApplicationActionType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::MenuHelpActionType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::RuntimeMenuViewActionType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::RuntimeMenuInputActionType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>();
+#ifdef VBOX_WITH_DEBUGGER_GUI
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType>();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::MenuWindowActionType>();
+#endif /* VBOX_WS_MAC */
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIColorThemeType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UILaunchMode>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIToolType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIVisualStateType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<DetailsElementType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<PreviewUpdateIntervalType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIDiskEncryptionCipherType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<GUIFeatureType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<GlobalSettingsPageType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<MachineSettingsPageType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIRemoteMode>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<WizardType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<IndicatorType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<MachineCloseAction>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<MouseCapturePolicy>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<GuruMeditationHandlerType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<ScalingOptimizationType>();
+#ifndef VBOX_WS_MAC
+template<> SHARED_LIBRARY_STUFF bool canConvert<MiniToolbarAlignment>();
+#endif
+template<> SHARED_LIBRARY_STUFF bool canConvert<InformationElementType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<MaximumGuestScreenSizePolicy>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UIMediumFormat>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<UISettingsDefs::RecordingMode>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<VMActivityOverviewColumn>();
+
+/* Declare COM canConvert specializations: */
+template<> SHARED_LIBRARY_STUFF bool canConvert<KCloudMachineState>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KMachineState>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KSessionState>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KParavirtProvider>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KDeviceType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KClipboardMode>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KDnDMode>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KPointingHIDType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KGraphicsControllerType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KMediumType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KMediumVariant>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KNetworkAttachmentType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KNetworkAdapterType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KNetworkAdapterPromiscModePolicy>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KPortMode>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KUSBControllerType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KUSBDeviceState>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KUSBDeviceFilterAction>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KAudioDriverType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KAudioControllerType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KAuthType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KStorageBus>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KStorageControllerType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KChipsetType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KTpmType>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KNATProtocol>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KGuestSessionStatus>();
+template<> SHARED_LIBRARY_STUFF bool canConvert<KProcessStatus>();
+
+
+/* Declare global conversion specializations: */
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const Qt::Alignment &enmAlignment);
+template<> SHARED_LIBRARY_STUFF Qt::Alignment fromInternalString<Qt::Alignment>(const QString &strAlignment);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const Qt::SortOrder &enmSortOrder);
+template<> SHARED_LIBRARY_STUFF Qt::SortOrder fromInternalString<Qt::SortOrder>(const QString &strSortOrder);
+template<> SHARED_LIBRARY_STUFF QString toString(const SizeSuffix &sizeSuffix);
+template<> SHARED_LIBRARY_STUFF SizeSuffix fromString<SizeSuffix>(const QString &strSizeSuffix);
+template<> SHARED_LIBRARY_STUFF QString toString(const StorageSlot &storageSlot);
+template<> SHARED_LIBRARY_STUFF StorageSlot fromString<StorageSlot>(const QString &strStorageSlot);
+template<> SHARED_LIBRARY_STUFF DesktopWatchdogPolicy_SynthTest fromInternalString<DesktopWatchdogPolicy_SynthTest>(const QString &strPolicyType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DialogType &enmDialogType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DialogType fromInternalString<UIExtraDataMetaDefs::DialogType>(const QString &strDialogType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::MenuType &menuType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::MenuType fromInternalString<UIExtraDataMetaDefs::MenuType>(const QString &strMenuType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::MenuApplicationActionType &menuApplicationActionType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::MenuApplicationActionType fromInternalString<UIExtraDataMetaDefs::MenuApplicationActionType>(const QString &strMenuApplicationActionType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::MenuHelpActionType &menuHelpActionType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::MenuHelpActionType fromInternalString<UIExtraDataMetaDefs::MenuHelpActionType>(const QString &strMenuHelpActionType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuMachineActionType &runtimeMenuMachineActionType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::RuntimeMenuMachineActionType fromInternalString<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>(const QString &strRuntimeMenuMachineActionType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuViewActionType &runtimeMenuViewActionType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::RuntimeMenuViewActionType fromInternalString<UIExtraDataMetaDefs::RuntimeMenuViewActionType>(const QString &strRuntimeMenuViewActionType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuInputActionType &runtimeMenuInputActionType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::RuntimeMenuInputActionType fromInternalString<UIExtraDataMetaDefs::RuntimeMenuInputActionType>(const QString &strRuntimeMenuInputActionType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuDevicesActionType &runtimeMenuDevicesActionType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::RuntimeMenuDevicesActionType fromInternalString<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>(const QString &strRuntimeMenuDevicesActionType);
+#ifdef VBOX_WITH_DEBUGGER_GUI
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType &runtimeMenuDebuggerActionType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType fromInternalString<UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType>(const QString &strRuntimeMenuDebuggerActionType);
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::MenuWindowActionType &menuWindowActionType);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::MenuWindowActionType fromInternalString<UIExtraDataMetaDefs::MenuWindowActionType>(const QString &strMenuWindowActionType);
+#endif /* VBOX_WS_MAC */
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral &enmDetailsElementOptionTypeGeneral);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral &enmDetailsElementOptionTypeGeneral);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>(const QString &strDetailsElementOptionTypeGeneral);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem &enmDetailsElementOptionTypeSystem);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem &enmDetailsElementOptionTypeSystem);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeSystem fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>(const QString &strDetailsElementOptionTypeSystem);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay &enmDetailsElementOptionTypeDisplay);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay &enmDetailsElementOptionTypeDisplay);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>(const QString &strDetailsElementOptionTypeDisplay);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage &enmDetailsElementOptionTypeStorage);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage &enmDetailsElementOptionTypeStorage);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeStorage fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>(const QString &strDetailsElementOptionTypeStorage);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio &enmDetailsElementOptionTypeAudio);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio &enmDetailsElementOptionTypeAudio);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeAudio fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>(const QString &strDetailsElementOptionTypeAudio);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork &enmDetailsElementOptionTypeNetwork);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork &enmDetailsElementOptionTypeNetwork);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(const QString &strDetailsElementOptionTypeNetwork);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial &enmDetailsElementOptionTypeSerial);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial &enmDetailsElementOptionTypeSerial);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeSerial fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>(const QString &strDetailsElementOptionTypeSerial);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb &enmDetailsElementOptionTypeUsb);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb &enmDetailsElementOptionTypeUsb);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeUsb fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>(const QString &strDetailsElementOptionTypeUsb);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders &enmDetailsElementOptionTypeSharedFolders);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders &enmDetailsElementOptionTypeSharedFolders);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>(const QString &strDetailsElementOptionTypeSharedFolders);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface &enmDetailsElementOptionTypeUserInterface);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface &enmDetailsElementOptionTypeUserInterface);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>(const QString &strDetailsElementOptionTypeUserInterface);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription &enmDetailsElementOptionTypeDescription);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription &enmDetailsElementOptionTypeDescription);
+template<> SHARED_LIBRARY_STUFF UIExtraDataMetaDefs::DetailsElementOptionTypeDescription fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>(const QString &strDetailsElementOptionTypeDescription);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIColorThemeType &colorThemeType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIColorThemeType &colorThemeType);
+template<> SHARED_LIBRARY_STUFF UIColorThemeType fromInternalString<UIColorThemeType>(const QString &strColorThemeType);
+template<> SHARED_LIBRARY_STUFF UILaunchMode fromInternalString<UILaunchMode>(const QString &strDefaultFrontendType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIToolType &enmToolType);
+template<> SHARED_LIBRARY_STUFF UIToolType fromInternalString<UIToolType>(const QString &strToolType);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIVisualStateType &visualStateType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIVisualStateType &visualStateType);
+template<> SHARED_LIBRARY_STUFF UIVisualStateType fromInternalString<UIVisualStateType>(const QString &strVisualStateType);
+template<> SHARED_LIBRARY_STUFF QString toString(const DetailsElementType &detailsElementType);
+template<> SHARED_LIBRARY_STUFF DetailsElementType fromString<DetailsElementType>(const QString &strDetailsElementType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const DetailsElementType &detailsElementType);
+template<> SHARED_LIBRARY_STUFF DetailsElementType fromInternalString<DetailsElementType>(const QString &strDetailsElementType);
+template<> SHARED_LIBRARY_STUFF QIcon toIcon(const DetailsElementType &detailsElementType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const PreviewUpdateIntervalType &previewUpdateIntervalType);
+template<> SHARED_LIBRARY_STUFF PreviewUpdateIntervalType fromInternalString<PreviewUpdateIntervalType>(const QString &strPreviewUpdateIntervalType);
+template<> SHARED_LIBRARY_STUFF int toInternalInteger(const PreviewUpdateIntervalType &previewUpdateIntervalType);
+template<> SHARED_LIBRARY_STUFF PreviewUpdateIntervalType fromInternalInteger<PreviewUpdateIntervalType>(const int &iPreviewUpdateIntervalType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIDiskEncryptionCipherType &enmDiskEncryptionCipherType);
+template<> SHARED_LIBRARY_STUFF UIDiskEncryptionCipherType fromInternalString<UIDiskEncryptionCipherType>(const QString &strDiskEncryptionCipherType);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIDiskEncryptionCipherType &enmDiskEncryptionCipherType);
+template<> SHARED_LIBRARY_STUFF UIDiskEncryptionCipherType fromString<UIDiskEncryptionCipherType>(const QString &strDiskEncryptionCipherType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const GUIFeatureType &guiFeatureType);
+template<> SHARED_LIBRARY_STUFF GUIFeatureType fromInternalString<GUIFeatureType>(const QString &strGuiFeatureType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const GlobalSettingsPageType &globalSettingsPageType);
+template<> SHARED_LIBRARY_STUFF GlobalSettingsPageType fromInternalString<GlobalSettingsPageType>(const QString &strGlobalSettingsPageType);
+template<> SHARED_LIBRARY_STUFF QPixmap toWarningPixmap(const GlobalSettingsPageType &globalSettingsPageType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const MachineSettingsPageType &machineSettingsPageType);
+template<> SHARED_LIBRARY_STUFF MachineSettingsPageType fromInternalString<MachineSettingsPageType>(const QString &strMachineSettingsPageType);
+template<> SHARED_LIBRARY_STUFF QPixmap toWarningPixmap(const MachineSettingsPageType &machineSettingsPageType);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIRemoteMode &enmMode);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const WizardType &wizardType);
+template<> SHARED_LIBRARY_STUFF WizardType fromInternalString<WizardType>(const QString &strWizardType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const IndicatorType &indicatorType);
+template<> SHARED_LIBRARY_STUFF IndicatorType fromInternalString<IndicatorType>(const QString &strIndicatorType);
+template<> SHARED_LIBRARY_STUFF QString toString(const IndicatorType &indicatorType);
+template<> SHARED_LIBRARY_STUFF QIcon toIcon(const IndicatorType &indicatorType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const MachineCloseAction &machineCloseAction);
+template<> SHARED_LIBRARY_STUFF MachineCloseAction fromInternalString<MachineCloseAction>(const QString &strMachineCloseAction);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const MouseCapturePolicy &mouseCapturePolicy);
+template<> SHARED_LIBRARY_STUFF MouseCapturePolicy fromInternalString<MouseCapturePolicy>(const QString &strMouseCapturePolicy);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const GuruMeditationHandlerType &guruMeditationHandlerType);
+template<> SHARED_LIBRARY_STUFF GuruMeditationHandlerType fromInternalString<GuruMeditationHandlerType>(const QString &strGuruMeditationHandlerType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const ScalingOptimizationType &optimizationType);
+template<> SHARED_LIBRARY_STUFF ScalingOptimizationType fromInternalString<ScalingOptimizationType>(const QString &strOptimizationType);
+#ifndef VBOX_WS_MAC
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const MiniToolbarAlignment &miniToolbarAlignment);
+template<> SHARED_LIBRARY_STUFF MiniToolbarAlignment fromInternalString<MiniToolbarAlignment>(const QString &strMiniToolbarAlignment);
+#endif
+template<> SHARED_LIBRARY_STUFF QString toString(const InformationElementType &informationElementType);
+template<> SHARED_LIBRARY_STUFF InformationElementType fromString<InformationElementType>(const QString &strInformationElementType);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const InformationElementType &informationElementType);
+template<> SHARED_LIBRARY_STUFF InformationElementType fromInternalString<InformationElementType>(const QString &strInformationElementType);
+template<> SHARED_LIBRARY_STUFF QIcon toIcon(const InformationElementType &informationElementType);
+template<> SHARED_LIBRARY_STUFF QString toString(const MaximumGuestScreenSizePolicy &enmMaximumGuestScreenSizePolicy);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const MaximumGuestScreenSizePolicy &enmMaximumGuestScreenSizePolicy);
+template<> SHARED_LIBRARY_STUFF MaximumGuestScreenSizePolicy fromInternalString<MaximumGuestScreenSizePolicy>(const QString &strMaximumGuestScreenSizePolicy);
+template<> SHARED_LIBRARY_STUFF QString toString(const UIMediumFormat &enmUIMediumFormat);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const UIMediumFormat &enmUIMediumFormat);
+template<> SHARED_LIBRARY_STUFF UIMediumFormat fromInternalString<UIMediumFormat>(const QString &strUIMediumFormat);
+template<> SHARED_LIBRARY_STUFF QString toString(const UISettingsDefs::RecordingMode &enmRecordingMode);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const VMActivityOverviewColumn &enmVMActivityOverviewColumn);
+template<> SHARED_LIBRARY_STUFF VMActivityOverviewColumn fromInternalString<VMActivityOverviewColumn>(const QString &strVMActivityOverviewColumn);
+
+/* Declare COM conversion specializations: */
+template<> SHARED_LIBRARY_STUFF QIcon toIcon(const KCloudMachineState &state);
+template<> SHARED_LIBRARY_STUFF QString toString(const KCloudMachineState &state);
+template<> SHARED_LIBRARY_STUFF QColor toColor(const KMachineState &state);
+template<> SHARED_LIBRARY_STUFF QIcon toIcon(const KMachineState &state);
+template<> SHARED_LIBRARY_STUFF QString toString(const KMachineState &state);
+template<> SHARED_LIBRARY_STUFF QString toString(const KSessionState &state);
+template<> SHARED_LIBRARY_STUFF QString toString(const KParavirtProvider &type);
+template<> SHARED_LIBRARY_STUFF QString toString(const KDeviceType &type);
+template<> SHARED_LIBRARY_STUFF QString toString(const KClipboardMode &mode);
+template<> SHARED_LIBRARY_STUFF QString toString(const KDnDMode &mode);
+template<> SHARED_LIBRARY_STUFF QString toString(const KPointingHIDType &type);
+template<> SHARED_LIBRARY_STUFF QString toString(const KGraphicsControllerType &type);
+template<> SHARED_LIBRARY_STUFF KGraphicsControllerType fromString<KGraphicsControllerType>(const QString &strType);
+template<> SHARED_LIBRARY_STUFF QString toString(const KMediumType &type);
+template<> SHARED_LIBRARY_STUFF QString toString(const KMediumVariant &variant);
+template<> SHARED_LIBRARY_STUFF QString toString(const KNetworkAttachmentType &type);
+template<> SHARED_LIBRARY_STUFF QString toString(const KNetworkAdapterType &type);
+template<> SHARED_LIBRARY_STUFF QString toString(const KNetworkAdapterPromiscModePolicy &policy);
+template<> SHARED_LIBRARY_STUFF QString toString(const KPortMode &mode);
+template<> SHARED_LIBRARY_STUFF KPortMode fromString<KPortMode>(const QString &strMode);
+template<> SHARED_LIBRARY_STUFF QString toString(const KUSBControllerType &type);
+template<> SHARED_LIBRARY_STUFF QString toString(const KUSBDeviceState &state);
+template<> SHARED_LIBRARY_STUFF QString toString(const KUSBDeviceFilterAction &action);
+template<> SHARED_LIBRARY_STUFF KUSBDeviceFilterAction fromString<KUSBDeviceFilterAction>(const QString &strAction);
+template<> SHARED_LIBRARY_STUFF QString toString(const KAudioDriverType &type);
+template<> SHARED_LIBRARY_STUFF KAudioDriverType fromString<KAudioDriverType>(const QString &strType);
+template<> SHARED_LIBRARY_STUFF QString toString(const KAudioControllerType &type);
+template<> SHARED_LIBRARY_STUFF KAudioControllerType fromString<KAudioControllerType>(const QString &strType);
+template<> SHARED_LIBRARY_STUFF QString toString(const KAuthType &type);
+template<> SHARED_LIBRARY_STUFF KAuthType fromString<KAuthType>(const QString &strType);
+template<> SHARED_LIBRARY_STUFF QString toString(const KStorageBus &bus);
+template<> SHARED_LIBRARY_STUFF KStorageBus fromString<KStorageBus>(const QString &strType);
+template<> SHARED_LIBRARY_STUFF QString toString(const KStorageControllerType &type);
+template<> SHARED_LIBRARY_STUFF KStorageControllerType fromString<KStorageControllerType>(const QString &strType);
+template<> SHARED_LIBRARY_STUFF QString toString(const KChipsetType &type);
+template<> SHARED_LIBRARY_STUFF QString toString(const KTpmType &type);
+template<> SHARED_LIBRARY_STUFF QString toString(const KNATProtocol &protocol);
+template<> SHARED_LIBRARY_STUFF QString toInternalString(const KNATProtocol &protocol);
+template<> SHARED_LIBRARY_STUFF KNATProtocol fromInternalString<KNATProtocol>(const QString &strProtocol);
+template<> SHARED_LIBRARY_STUFF QString toString(const KGuestSessionStatus &status);
+template<> SHARED_LIBRARY_STUFF KGuestSessionStatus fromString<KGuestSessionStatus>(const QString &strStatus);
+template<> SHARED_LIBRARY_STUFF QString toString(const KProcessStatus &status);
+template<> SHARED_LIBRARY_STUFF KProcessStatus fromString<KProcessStatus>(const QString &strStatus);
+
+
+#endif /* !FEQT_INCLUDED_SRC_converter_UIConverterBackend_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendCOM.cpp b/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendCOM.cpp
new file mode 100644
index 00000000..3799bcab
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendCOM.cpp
@@ -0,0 +1,860 @@
+/* $Id: UIConverterBackendCOM.cpp $ */
+/** @file
+ * VBox Qt GUI - UIConverterBackendCOM implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QHash>
+
+/* GUI includes: */
+#include "UIConverterBackend.h"
+#include "UIIconPool.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+
+/* Determines if <Object of type X> can be converted to object of other type.
+ * These functions returns 'true' for all allowed conversions. */
+template<> bool canConvert<KCloudMachineState>() { return true; }
+template<> bool canConvert<KMachineState>() { return true; }
+template<> bool canConvert<KSessionState>() { return true; }
+template<> bool canConvert<KParavirtProvider>() { return true; }
+template<> bool canConvert<KDeviceType>() { return true; }
+template<> bool canConvert<KClipboardMode>() { return true; }
+template<> bool canConvert<KDnDMode>() { return true; }
+template<> bool canConvert<KPointingHIDType>() { return true; }
+template<> bool canConvert<KGraphicsControllerType>() { return true; }
+template<> bool canConvert<KMediumType>() { return true; }
+template<> bool canConvert<KMediumVariant>() { return true; }
+template<> bool canConvert<KNetworkAttachmentType>() { return true; }
+template<> bool canConvert<KNetworkAdapterType>() { return true; }
+template<> bool canConvert<KNetworkAdapterPromiscModePolicy>() { return true; }
+template<> bool canConvert<KPortMode>() { return true; }
+template<> bool canConvert<KUSBControllerType>() { return true; }
+template<> bool canConvert<KUSBDeviceState>() { return true; }
+template<> bool canConvert<KUSBDeviceFilterAction>() { return true; }
+template<> bool canConvert<KAudioDriverType>() { return true; }
+template<> bool canConvert<KAudioControllerType>() { return true; }
+template<> bool canConvert<KAuthType>() { return true; }
+template<> bool canConvert<KStorageBus>() { return true; }
+template<> bool canConvert<KStorageControllerType>() { return true; }
+template<> bool canConvert<KChipsetType>() { return true; }
+template<> bool canConvert<KTpmType>() { return true; }
+template<> bool canConvert<KNATProtocol>() { return true; }
+template<> bool canConvert<KGuestSessionStatus>() { return true; }
+template<> bool canConvert<KProcessStatus>() { return true; }
+
+/* QIcon <= KCloudMachineState: */
+template<> QIcon toIcon(const KCloudMachineState &state)
+{
+ switch (state)
+ {
+ case KCloudMachineState_Provisioning: return UIIconPool::iconSet(":/state_running_16px.png");
+ case KCloudMachineState_Running: return UIIconPool::iconSet(":/state_running_16px.png");
+ case KCloudMachineState_Starting: return UIIconPool::iconSet(":/state_running_16px.png");
+ case KCloudMachineState_Stopping: return UIIconPool::iconSet(":/state_saving_16px.png");
+ case KCloudMachineState_Stopped: return UIIconPool::iconSet(":/state_saved_16px.png");
+ case KCloudMachineState_CreatingImage: return UIIconPool::iconSet(":/state_saved_16px.png");
+ case KCloudMachineState_Terminating: return UIIconPool::iconSet(":/state_discarding_16px.png");
+ case KCloudMachineState_Terminated: return UIIconPool::iconSet(":/state_powered_off_16px.png");
+ default: AssertMsgFailed(("No icon for %d", state)); break;
+ }
+ return QIcon();
+}
+
+/* QString <= KCloudMachineState: */
+template<> QString toString(const KCloudMachineState &state)
+{
+ switch (state)
+ {
+ case KCloudMachineState_Provisioning: return QApplication::translate("UICommon", "Provisioning", "CloudMachineState");
+ case KCloudMachineState_Running: return QApplication::translate("UICommon", "Running", "CloudMachineState");
+ case KCloudMachineState_Starting: return QApplication::translate("UICommon", "Starting", "CloudMachineState");
+ case KCloudMachineState_Stopping: return QApplication::translate("UICommon", "Stopping", "CloudMachineState");
+ case KCloudMachineState_Stopped: return QApplication::translate("UICommon", "Stopped", "CloudMachineState");
+ case KCloudMachineState_CreatingImage: return QApplication::translate("UICommon", "Creating Image", "CloudMachineState");
+ case KCloudMachineState_Terminating: return QApplication::translate("UICommon", "Terminating", "CloudMachineState");
+ case KCloudMachineState_Terminated: return QApplication::translate("UICommon", "Terminated", "CloudMachineState");
+ default: AssertMsgFailed(("No text for %d", state)); break;
+ }
+ return QString();
+}
+
+/* QColor <= KMachineState: */
+template<> QColor toColor(const KMachineState &state)
+{
+ switch (state)
+ {
+ case KMachineState_PoweredOff: return QColor(Qt::gray);
+ case KMachineState_Saved: return QColor(Qt::yellow);
+ case KMachineState_Teleported: return QColor(Qt::red);
+ case KMachineState_Aborted: return QColor(Qt::darkRed);
+ case KMachineState_AbortedSaved: return QColor(Qt::yellow);
+ case KMachineState_Running: return QColor(Qt::green);
+ case KMachineState_Paused: return QColor(Qt::darkGreen);
+ case KMachineState_Stuck: return QColor(Qt::darkMagenta);
+ case KMachineState_Teleporting: return QColor(Qt::blue);
+ case KMachineState_Snapshotting: return QColor(Qt::green);
+ case KMachineState_OnlineSnapshotting: return QColor(Qt::green);
+ case KMachineState_LiveSnapshotting: return QColor(Qt::green);
+ case KMachineState_Starting: return QColor(Qt::green);
+ case KMachineState_Stopping: return QColor(Qt::green);
+ case KMachineState_Saving: return QColor(Qt::green);
+ case KMachineState_Restoring: return QColor(Qt::green);
+ case KMachineState_TeleportingPausedVM: return QColor(Qt::blue);
+ case KMachineState_TeleportingIn: return QColor(Qt::blue);
+ case KMachineState_DeletingSnapshotOnline: return QColor(Qt::green);
+ case KMachineState_DeletingSnapshotPaused: return QColor(Qt::darkGreen);
+ case KMachineState_RestoringSnapshot: return QColor(Qt::green);
+ case KMachineState_DeletingSnapshot: return QColor(Qt::green);
+ case KMachineState_SettingUp: return QColor(Qt::green);
+ // case KMachineState_FirstOnline:
+ // case KMachineState_LastOnline:
+ // case KMachineState_FirstTransient:
+ // case KMachineState_LastTransient:
+ default: AssertMsgFailed(("No color for %d", state)); break;
+ }
+ return QColor();
+}
+
+/* QIcon <= KMachineState: */
+template<> QIcon toIcon(const KMachineState &state)
+{
+ switch (state)
+ {
+ case KMachineState_PoweredOff: return UIIconPool::iconSet(":/state_powered_off_16px.png");
+ case KMachineState_Saved: return UIIconPool::iconSet(":/state_saved_16px.png");
+ case KMachineState_Teleported: return UIIconPool::iconSet(":/state_saved_16px.png");
+ case KMachineState_Aborted: return UIIconPool::iconSet(":/state_aborted_16px.png");
+ case KMachineState_AbortedSaved: return UIIconPool::iconSet(":/state_aborted_saved_16px.png");
+ case KMachineState_Running: return UIIconPool::iconSet(":/state_running_16px.png");
+ case KMachineState_Paused: return UIIconPool::iconSet(":/state_paused_16px.png");
+ case KMachineState_Stuck: return UIIconPool::iconSet(":/state_stuck_16px.png");
+ case KMachineState_Teleporting: return UIIconPool::iconSet(":/state_running_16px.png");
+ case KMachineState_Snapshotting: return UIIconPool::iconSet(":/state_saving_16px.png");
+ case KMachineState_OnlineSnapshotting: return UIIconPool::iconSet(":/state_running_16px.png");
+ case KMachineState_LiveSnapshotting: return UIIconPool::iconSet(":/state_running_16px.png");
+ case KMachineState_Starting: return UIIconPool::iconSet(":/state_running_16px.png");
+ case KMachineState_Stopping: return UIIconPool::iconSet(":/state_running_16px.png");
+ case KMachineState_Saving: return UIIconPool::iconSet(":/state_saving_16px.png");
+ case KMachineState_Restoring: return UIIconPool::iconSet(":/state_restoring_16px.png");
+ case KMachineState_TeleportingPausedVM: return UIIconPool::iconSet(":/state_saving_16px.png");
+ case KMachineState_TeleportingIn: return UIIconPool::iconSet(":/state_restoring_16px.png");
+ case KMachineState_DeletingSnapshotOnline: return UIIconPool::iconSet(":/state_discarding_16px.png");
+ case KMachineState_DeletingSnapshotPaused: return UIIconPool::iconSet(":/state_discarding_16px.png");
+ case KMachineState_RestoringSnapshot: return UIIconPool::iconSet(":/state_discarding_16px.png");
+ case KMachineState_DeletingSnapshot: return UIIconPool::iconSet(":/state_discarding_16px.png");
+ case KMachineState_SettingUp: return UIIconPool::iconSet(":/vm_settings_16px.png"); /// @todo Change icon!
+ // case KMachineState_FirstOnline:
+ // case KMachineState_LastOnline:
+ // case KMachineState_FirstTransient:
+ // case KMachineState_LastTransient:
+ default: AssertMsgFailed(("No icon for %d", state)); break;
+ }
+ return QIcon();
+}
+
+/* QString <= KMachineState: */
+template<> QString toString(const KMachineState &state)
+{
+ switch (state)
+ {
+ case KMachineState_PoweredOff: return QApplication::translate("UICommon", "Powered Off", "MachineState");
+ case KMachineState_Saved: return QApplication::translate("UICommon", "Saved", "MachineState");
+ case KMachineState_Teleported: return QApplication::translate("UICommon", "Teleported", "MachineState");
+ case KMachineState_Aborted: return QApplication::translate("UICommon", "Aborted", "MachineState");
+ case KMachineState_AbortedSaved: return QApplication::translate("UICommon", "Aborted-Saved", "MachineState");
+ case KMachineState_Running: return QApplication::translate("UICommon", "Running", "MachineState");
+ case KMachineState_Paused: return QApplication::translate("UICommon", "Paused", "MachineState");
+ case KMachineState_Stuck: return QApplication::translate("UICommon", "Guru Meditation", "MachineState");
+ case KMachineState_Teleporting: return QApplication::translate("UICommon", "Teleporting", "MachineState");
+ case KMachineState_Snapshotting: return QApplication::translate("UICommon", "Taking Snapshot", "MachineState");
+ case KMachineState_OnlineSnapshotting: return QApplication::translate("UICommon", "Taking Online Snapshot", "MachineState");
+ case KMachineState_LiveSnapshotting: return QApplication::translate("UICommon", "Taking Live Snapshot", "MachineState");
+ case KMachineState_Starting: return QApplication::translate("UICommon", "Starting", "MachineState");
+ case KMachineState_Stopping: return QApplication::translate("UICommon", "Stopping", "MachineState");
+ case KMachineState_Saving: return QApplication::translate("UICommon", "Saving", "MachineState");
+ case KMachineState_Restoring: return QApplication::translate("UICommon", "Restoring", "MachineState");
+ case KMachineState_TeleportingPausedVM: return QApplication::translate("UICommon", "Teleporting Paused VM", "MachineState");
+ case KMachineState_TeleportingIn: return QApplication::translate("UICommon", "Teleporting", "MachineState");
+ case KMachineState_DeletingSnapshotOnline: return QApplication::translate("UICommon", "Deleting Snapshot", "MachineState");
+ case KMachineState_DeletingSnapshotPaused: return QApplication::translate("UICommon", "Deleting Snapshot", "MachineState");
+ case KMachineState_RestoringSnapshot: return QApplication::translate("UICommon", "Restoring Snapshot", "MachineState");
+ case KMachineState_DeletingSnapshot: return QApplication::translate("UICommon", "Deleting Snapshot", "MachineState");
+ case KMachineState_SettingUp: return QApplication::translate("UICommon", "Setting Up", "MachineState");
+ // case KMachineState_FirstOnline:
+ // case KMachineState_LastOnline:
+ // case KMachineState_FirstTransient:
+ // case KMachineState_LastTransient:
+ default: AssertMsgFailed(("No text for %d", state)); break;
+ }
+ return QString();
+}
+
+/* QString <= KSessionState: */
+template<> QString toString(const KSessionState &state)
+{
+ switch (state)
+ {
+ case KSessionState_Unlocked: return QApplication::translate("UICommon", "Unlocked", "SessionState");
+ case KSessionState_Locked: return QApplication::translate("UICommon", "Locked", "SessionState");
+ case KSessionState_Spawning: return QApplication::translate("UICommon", "Spawning", "SessionState");
+ case KSessionState_Unlocking: return QApplication::translate("UICommon", "Unlocking", "SessionState");
+ default: AssertMsgFailed(("No text for %d", state)); break;
+ }
+ return QString();
+}
+
+/* QString <= KParavirtProvider: */
+template<> QString toString(const KParavirtProvider &type)
+{
+ switch (type)
+ {
+ case KParavirtProvider_None: return QApplication::translate("UICommon", "None", "ParavirtProvider");
+ case KParavirtProvider_Default: return QApplication::translate("UICommon", "Default", "ParavirtProvider");
+ case KParavirtProvider_Legacy: return QApplication::translate("UICommon", "Legacy", "ParavirtProvider");
+ case KParavirtProvider_Minimal: return QApplication::translate("UICommon", "Minimal", "ParavirtProvider");
+ case KParavirtProvider_HyperV: return QApplication::translate("UICommon", "Hyper-V", "ParavirtProvider");
+ case KParavirtProvider_KVM: return QApplication::translate("UICommon", "KVM", "ParavirtProvider");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* QString <= KDeviceType: */
+template<> QString toString(const KDeviceType &type)
+{
+ switch (type)
+ {
+ case KDeviceType_Null: return QApplication::translate("UICommon", "None", "DeviceType");
+ case KDeviceType_Floppy: return QApplication::translate("UICommon", "Floppy", "DeviceType");
+ case KDeviceType_DVD: return QApplication::translate("UICommon", "Optical", "DeviceType");
+ case KDeviceType_HardDisk: return QApplication::translate("UICommon", "Hard Disk", "DeviceType");
+ case KDeviceType_Network: return QApplication::translate("UICommon", "Network", "DeviceType");
+ case KDeviceType_USB: return QApplication::translate("UICommon", "USB", "DeviceType");
+ case KDeviceType_SharedFolder: return QApplication::translate("UICommon", "Shared Folder", "DeviceType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* QString <= KClipboardMode: */
+template<> QString toString(const KClipboardMode &mode)
+{
+ switch (mode)
+ {
+ case KClipboardMode_Disabled: return QApplication::translate("UICommon", "Disabled", "ClipboardType");
+ case KClipboardMode_HostToGuest: return QApplication::translate("UICommon", "Host To Guest", "ClipboardType");
+ case KClipboardMode_GuestToHost: return QApplication::translate("UICommon", "Guest To Host", "ClipboardType");
+ case KClipboardMode_Bidirectional: return QApplication::translate("UICommon", "Bidirectional", "ClipboardType");
+ default: AssertMsgFailed(("No text for %d", mode)); break;
+ }
+ return QString();
+}
+
+/* QString <= KDnDMode: */
+template<> QString toString(const KDnDMode &mode)
+{
+ switch (mode)
+ {
+ case KDnDMode_Disabled: return QApplication::translate("UICommon", "Disabled", "DragAndDropType");
+ case KDnDMode_HostToGuest: return QApplication::translate("UICommon", "Host To Guest", "DragAndDropType");
+ case KDnDMode_GuestToHost: return QApplication::translate("UICommon", "Guest To Host", "DragAndDropType");
+ case KDnDMode_Bidirectional: return QApplication::translate("UICommon", "Bidirectional", "DragAndDropType");
+ default: AssertMsgFailed(("No text for %d", mode)); break;
+ }
+ return QString();
+}
+
+/* QString <= KPointingHIDType: */
+template<> QString toString(const KPointingHIDType &type)
+{
+ switch (type)
+ {
+ case KPointingHIDType_None: return QApplication::translate("UICommon", "None", "PointingHIDType");
+ case KPointingHIDType_PS2Mouse: return QApplication::translate("UICommon", "PS/2 Mouse", "PointingHIDType");
+ case KPointingHIDType_USBMouse: return QApplication::translate("UICommon", "USB Mouse", "PointingHIDType");
+ case KPointingHIDType_USBTablet: return QApplication::translate("UICommon", "USB Tablet", "PointingHIDType");
+ case KPointingHIDType_ComboMouse: return QApplication::translate("UICommon", "PS/2 and USB Mouse", "PointingHIDType");
+ case KPointingHIDType_USBMultiTouch: return QApplication::translate("UICommon", "USB Multi-Touch Tablet", "PointingHIDType");
+ case KPointingHIDType_USBMultiTouchScreenPlusPad: return QApplication::translate("UICommon", "USB MT TouchScreen and TouchPad", "PointingHIDType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* QString <= KGraphicsControllerType: */
+template<> QString toString(const KGraphicsControllerType &type)
+{
+ switch (type)
+ {
+ case KGraphicsControllerType_Null: return QApplication::translate("UICommon", "None", "GraphicsControllerType");
+ case KGraphicsControllerType_VBoxVGA: return QApplication::translate("UICommon", "VBoxVGA", "GraphicsControllerType");
+ case KGraphicsControllerType_VMSVGA: return QApplication::translate("UICommon", "VMSVGA", "GraphicsControllerType");
+ case KGraphicsControllerType_VBoxSVGA: return QApplication::translate("UICommon", "VBoxSVGA", "GraphicsControllerType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* KGraphicsControllerType <= QString: */
+template<> KGraphicsControllerType fromString<KGraphicsControllerType>(const QString &strType)
+{
+ QHash<QString, KGraphicsControllerType> list;
+ list.insert(QApplication::translate("UICommon", "None", "GraphicsControllerType"), KGraphicsControllerType_Null);
+ list.insert(QApplication::translate("UICommon", "VBoxVGA", "GraphicsControllerType"), KGraphicsControllerType_VBoxVGA);
+ list.insert(QApplication::translate("UICommon", "VMSVGA", "GraphicsControllerType"), KGraphicsControllerType_VMSVGA);
+ list.insert(QApplication::translate("UICommon", "VBoxSVGA", "GraphicsControllerType"), KGraphicsControllerType_VBoxSVGA);
+ if (!list.contains(strType))
+ {
+ AssertMsgFailed(("No value for '%s'", strType.toUtf8().constData()));
+ }
+ return list.value(strType, KGraphicsControllerType_Null);
+}
+
+/* QString <= KMediumType: */
+template<> QString toString(const KMediumType &type)
+{
+ switch (type)
+ {
+ case KMediumType_Normal: return QApplication::translate("UICommon", "Normal", "MediumType");
+ case KMediumType_Immutable: return QApplication::translate("UICommon", "Immutable", "MediumType");
+ case KMediumType_Writethrough: return QApplication::translate("UICommon", "Writethrough", "MediumType");
+ case KMediumType_Shareable: return QApplication::translate("UICommon", "Shareable", "MediumType");
+ case KMediumType_Readonly: return QApplication::translate("UICommon", "Readonly", "MediumType");
+ case KMediumType_MultiAttach: return QApplication::translate("UICommon", "Multi-attach", "MediumType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* QString <= KMediumVariant: */
+template<> QString toString(const KMediumVariant &variant)
+{
+ /* Note: KMediumVariant_Diff and KMediumVariant_Fixed are so far mutually exclusive: */
+ switch ((int)variant)
+ {
+ case KMediumVariant_Standard:
+ return QApplication::translate("UICommon", "Dynamically allocated storage", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_VdiZeroExpand):
+ return QApplication::translate("UICommon", "New dynamically allocated storage", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_Diff):
+ return QApplication::translate("UICommon", "Dynamically allocated differencing storage", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_Fixed):
+ return QApplication::translate("UICommon", "Fixed size storage", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_VmdkSplit2G):
+ return QApplication::translate("UICommon", "Dynamically allocated storage split into files of less than 2GB", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_VmdkSplit2G | KMediumVariant_Diff):
+ return QApplication::translate("UICommon", "Dynamically allocated differencing storage split into files of less than 2GB", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_Fixed | KMediumVariant_VmdkSplit2G):
+ return QApplication::translate("UICommon", "Fixed size storage split into files of less than 2GB", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_VmdkStreamOptimized):
+ return QApplication::translate("UICommon", "Dynamically allocated compressed storage", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_VmdkStreamOptimized | KMediumVariant_Diff):
+ return QApplication::translate("UICommon", "Dynamically allocated differencing compressed storage", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_Fixed | KMediumVariant_VmdkESX):
+ return QApplication::translate("UICommon", "Fixed size ESX storage", "MediumVariant");
+ case (KMediumVariant)(KMediumVariant_Standard | KMediumVariant_Fixed | KMediumVariant_VmdkRawDisk):
+ return QApplication::translate("UICommon", "Fixed size storage on raw disk", "MediumVariant");
+ default:
+ AssertMsgFailed(("No text for %d", variant)); break;
+ }
+ return QString();
+}
+
+/* QString <= KNetworkAttachmentType: */
+template<> QString toString(const KNetworkAttachmentType &type)
+{
+ switch (type)
+ {
+ case KNetworkAttachmentType_Null: return QApplication::translate("UICommon", "Not attached", "NetworkAttachmentType");
+ case KNetworkAttachmentType_NAT: return QApplication::translate("UICommon", "NAT", "NetworkAttachmentType");
+ case KNetworkAttachmentType_Bridged: return QApplication::translate("UICommon", "Bridged Adapter", "NetworkAttachmentType");
+ case KNetworkAttachmentType_Internal: return QApplication::translate("UICommon", "Internal Network", "NetworkAttachmentType");
+#ifndef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnly: return QApplication::translate("UICommon", "Host-only Adapter", "NetworkAttachmentType");
+#else /* VBOX_WITH_VMNET */
+ case KNetworkAttachmentType_HostOnly: return QApplication::translate("UICommon", "Host-only Adapter [DEPRECATED]", "NetworkAttachmentType");
+ case KNetworkAttachmentType_HostOnlyNetwork: return QApplication::translate("UICommon", "Host-only Network", "NetworkAttachmentType");
+#endif /* VBOX_WITH_VMNET */
+ case KNetworkAttachmentType_Generic: return QApplication::translate("UICommon", "Generic Driver", "NetworkAttachmentType");
+ case KNetworkAttachmentType_NATNetwork: return QApplication::translate("UICommon", "NAT Network", "NetworkAttachmentType");
+#ifdef VBOX_WITH_CLOUD_NET
+ case KNetworkAttachmentType_Cloud: return QApplication::translate("UICommon", "Cloud Network [EXPERIMENTAL]", "NetworkAttachmentType");
+#endif /* VBOX_WITH_CLOUD_NET */
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* QString <= KNetworkAdapterType: */
+template<> QString toString(const KNetworkAdapterType &type)
+{
+ switch (type)
+ {
+ case KNetworkAdapterType_Am79C970A: return QApplication::translate("UICommon", "PCnet-PCI II (Am79C970A)", "NetworkAdapterType");
+ case KNetworkAdapterType_Am79C973: return QApplication::translate("UICommon", "PCnet-FAST III (Am79C973)", "NetworkAdapterType");
+ case KNetworkAdapterType_I82540EM: return QApplication::translate("UICommon", "Intel PRO/1000 MT Desktop (82540EM)", "NetworkAdapterType");
+ case KNetworkAdapterType_I82543GC: return QApplication::translate("UICommon", "Intel PRO/1000 T Server (82543GC)", "NetworkAdapterType");
+ case KNetworkAdapterType_I82545EM: return QApplication::translate("UICommon", "Intel PRO/1000 MT Server (82545EM)", "NetworkAdapterType");
+ case KNetworkAdapterType_Virtio: return QApplication::translate("UICommon", "Paravirtualized Network (virtio-net)", "NetworkAdapterType");
+ case KNetworkAdapterType_Am79C960: return QApplication::translate("UICommon", "PCnet-ISA (Am79C960)", "NetworkAdapterType");
+ case KNetworkAdapterType_NE2000: return QApplication::translate("UICommon", "Novell NE2000 (NE2000)", "NetworkAdapterType");
+ case KNetworkAdapterType_NE1000: return QApplication::translate("UICommon", "Novell NE1000 (NE1000)", "NetworkAdapterType");
+ case KNetworkAdapterType_WD8013: return QApplication::translate("UICommon", "WD EtherCard Plus 16 (WD8013EBT)", "NetworkAdapterType");
+ case KNetworkAdapterType_WD8003: return QApplication::translate("UICommon", "WD EtherCard Plus (WD8013E)", "NetworkAdapterType");
+ case KNetworkAdapterType_ELNK2: return QApplication::translate("UICommon", "3Com EtherLink II (3C503)", "NetworkAdapterType");
+ case KNetworkAdapterType_ELNK1: return QApplication::translate("UICommon", "3Com EtherLink (3C501)", "NetworkAdapterType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* QString <= KNetworkAdapterPromiscModePolicy: */
+template<> QString toString(const KNetworkAdapterPromiscModePolicy &policy)
+{
+ switch (policy)
+ {
+ case KNetworkAdapterPromiscModePolicy_Deny:
+ return QApplication::translate("UICommon", "Deny", "NetworkAdapterPromiscModePolicy");
+ case KNetworkAdapterPromiscModePolicy_AllowNetwork:
+ return QApplication::translate("UICommon", "Allow VMs", "NetworkAdapterPromiscModePolicy");
+ case KNetworkAdapterPromiscModePolicy_AllowAll:
+ return QApplication::translate("UICommon", "Allow All", "NetworkAdapterPromiscModePolicy");
+ default:
+ AssertMsgFailed(("No text for %d", policy)); break;
+ }
+ return QString();
+}
+
+/* QString <= KPortMode: */
+template<> QString toString(const KPortMode &mode)
+{
+ switch (mode)
+ {
+ case KPortMode_Disconnected: return QApplication::translate("UICommon", "Disconnected", "PortMode");
+ case KPortMode_HostPipe: return QApplication::translate("UICommon", "Host Pipe", "PortMode");
+ case KPortMode_HostDevice: return QApplication::translate("UICommon", "Host Device", "PortMode");
+ case KPortMode_RawFile: return QApplication::translate("UICommon", "Raw File", "PortMode");
+ case KPortMode_TCP: return QApplication::translate("UICommon", "TCP", "PortMode");
+ default: AssertMsgFailed(("No text for %d", mode)); break;
+ }
+ return QString();
+}
+
+/* KPortMode <= QString: */
+template<> KPortMode fromString<KPortMode>(const QString &strMode)
+{
+ QHash<QString, KPortMode> list;
+ list.insert(QApplication::translate("UICommon", "Disconnected", "PortMode"), KPortMode_Disconnected);
+ list.insert(QApplication::translate("UICommon", "Host Pipe", "PortMode"), KPortMode_HostPipe);
+ list.insert(QApplication::translate("UICommon", "Host Device", "PortMode"), KPortMode_HostDevice);
+ list.insert(QApplication::translate("UICommon", "Raw File", "PortMode"), KPortMode_RawFile);
+ list.insert(QApplication::translate("UICommon", "TCP", "PortMode"), KPortMode_TCP);
+ if (!list.contains(strMode))
+ {
+ AssertMsgFailed(("No value for '%s'", strMode.toUtf8().constData()));
+ }
+ return list.value(strMode, KPortMode_Disconnected);
+}
+
+/* QString <= KUSBControllerType: */
+template<> QString toString(const KUSBControllerType &type)
+{
+ switch (type)
+ {
+ case KUSBControllerType_OHCI: return QApplication::translate("UICommon", "OHCI", "USBControllerType");
+ case KUSBControllerType_EHCI: return QApplication::translate("UICommon", "EHCI", "USBControllerType");
+ case KUSBControllerType_XHCI: return QApplication::translate("UICommon", "xHCI", "USBControllerType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* QString <= KUSBDeviceState: */
+template<> QString toString(const KUSBDeviceState &state)
+{
+ switch (state)
+ {
+ case KUSBDeviceState_NotSupported: return QApplication::translate("UICommon", "Not supported", "USBDeviceState");
+ case KUSBDeviceState_Unavailable: return QApplication::translate("UICommon", "Unavailable", "USBDeviceState");
+ case KUSBDeviceState_Busy: return QApplication::translate("UICommon", "Busy", "USBDeviceState");
+ case KUSBDeviceState_Available: return QApplication::translate("UICommon", "Available", "USBDeviceState");
+ case KUSBDeviceState_Held: return QApplication::translate("UICommon", "Held", "USBDeviceState");
+ case KUSBDeviceState_Captured: return QApplication::translate("UICommon", "Captured", "USBDeviceState");
+ default: AssertMsgFailed(("No text for %d", state)); break;
+ }
+ return QString();
+}
+
+/* QString <= KUSBDeviceFilterAction: */
+template<> QString toString(const KUSBDeviceFilterAction &action)
+{
+ switch (action)
+ {
+ case KUSBDeviceFilterAction_Ignore: return QApplication::translate("UICommon", "Ignore", "USBDeviceFilterAction");
+ case KUSBDeviceFilterAction_Hold: return QApplication::translate("UICommon", "Hold", "USBDeviceFilterAction");
+ default: AssertMsgFailed(("No text for %d", action)); break;
+ }
+ return QString();
+}
+
+/* KUSBDeviceFilterAction <= QString: */
+template<> KUSBDeviceFilterAction fromString<KUSBDeviceFilterAction>(const QString &strAction)
+{
+ QHash<QString, KUSBDeviceFilterAction> list;
+ list.insert(QApplication::translate("UICommon", "Ignore", "USBDeviceFilterAction"), KUSBDeviceFilterAction_Ignore);
+ list.insert(QApplication::translate("UICommon", "Hold", "USBDeviceFilterAction"), KUSBDeviceFilterAction_Hold);
+ if (!list.contains(strAction))
+ {
+ AssertMsgFailed(("No value for '%s'", strAction.toUtf8().constData()));
+ }
+ return list.value(strAction, KUSBDeviceFilterAction_Null);
+}
+
+/* QString <= KAudioDriverType: */
+template<> QString toString(const KAudioDriverType &type)
+{
+ switch (type)
+ {
+ case KAudioDriverType_Default: return QApplication::translate("UICommon", "Default", "AudioDriverType");
+ case KAudioDriverType_Null: return QApplication::translate("UICommon", "Null Audio", "AudioDriverType");
+ case KAudioDriverType_OSS: return QApplication::translate("UICommon", "OSS Audio", "AudioDriverType");
+ case KAudioDriverType_ALSA: return QApplication::translate("UICommon", "ALSA Audio", "AudioDriverType");
+ case KAudioDriverType_Pulse: return QApplication::translate("UICommon", "PulseAudio", "AudioDriverType");
+ case KAudioDriverType_WinMM: return QApplication::translate("UICommon", "Windows Multimedia", "AudioDriverType");
+ case KAudioDriverType_DirectSound: return QApplication::translate("UICommon", "Windows DirectSound", "AudioDriverType");
+ case KAudioDriverType_WAS: return QApplication::translate("UICommon", "Windows Audio Session", "AudioDriverType");
+ case KAudioDriverType_CoreAudio: return QApplication::translate("UICommon", "Core Audio", "AudioDriverType");
+ // case KAudioDriverType_MMPM:
+ case KAudioDriverType_SolAudio: return QApplication::translate("UICommon", "Solaris Audio", "AudioDriverType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* KAudioDriverType <= QString: */
+template<> KAudioDriverType fromString<KAudioDriverType>(const QString &strType)
+{
+ QHash<QString, KAudioDriverType> list;
+ list.insert(QApplication::translate("UICommon", "Default", "AudioDriverType"), KAudioDriverType_Default);
+ list.insert(QApplication::translate("UICommon", "Null Audio", "AudioDriverType"), KAudioDriverType_Null);
+ list.insert(QApplication::translate("UICommon", "OSS Audio", "AudioDriverType"), KAudioDriverType_OSS);
+ list.insert(QApplication::translate("UICommon", "ALSA Audio", "AudioDriverType"), KAudioDriverType_ALSA);
+ list.insert(QApplication::translate("UICommon", "PulseAudio", "AudioDriverType"), KAudioDriverType_Pulse);
+ list.insert(QApplication::translate("UICommon", "Windows Multimedia", "AudioDriverType"), KAudioDriverType_WinMM);
+ list.insert(QApplication::translate("UICommon", "Windows DirectSound", "AudioDriverType"), KAudioDriverType_DirectSound);
+ list.insert(QApplication::translate("UICommon", "Windows Audio Session", "AudioDriverType"), KAudioDriverType_WAS);
+ list.insert(QApplication::translate("UICommon", "Core Audio", "AudioDriverType"), KAudioDriverType_CoreAudio);
+ // list.insert(..., KAudioDriverType_MMPM);
+ list.insert(QApplication::translate("UICommon", "Solaris Audio", "AudioDriverType"), KAudioDriverType_SolAudio);
+ if (!list.contains(strType))
+ {
+ AssertMsgFailed(("No value for '%s'", strType.toUtf8().constData()));
+ }
+ return list.value(strType, KAudioDriverType_Null);
+}
+
+/* QString <= KAudioControllerType: */
+template<> QString toString(const KAudioControllerType &type)
+{
+ switch (type)
+ {
+ case KAudioControllerType_AC97: return QApplication::translate("UICommon", "ICH AC97", "AudioControllerType");
+ case KAudioControllerType_SB16: return QApplication::translate("UICommon", "SoundBlaster 16", "AudioControllerType");
+ case KAudioControllerType_HDA: return QApplication::translate("UICommon", "Intel HD Audio", "AudioControllerType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* KAudioControllerType <= QString: */
+template<> KAudioControllerType fromString<KAudioControllerType>(const QString &strType)
+{
+ QHash<QString, KAudioControllerType> list;
+ list.insert(QApplication::translate("UICommon", "ICH AC97", "AudioControllerType"), KAudioControllerType_AC97);
+ list.insert(QApplication::translate("UICommon", "SoundBlaster 16", "AudioControllerType"), KAudioControllerType_SB16);
+ list.insert(QApplication::translate("UICommon", "Intel HD Audio", "AudioControllerType"), KAudioControllerType_HDA);
+ if (!list.contains(strType))
+ {
+ AssertMsgFailed(("No value for '%s'", strType.toUtf8().constData()));
+ }
+ return list.value(strType, KAudioControllerType_AC97);
+}
+
+/* QString <= KAuthType: */
+template<> QString toString(const KAuthType &type)
+{
+ switch (type)
+ {
+ case KAuthType_Null: return QApplication::translate("UICommon", "Null", "AuthType");
+ case KAuthType_External: return QApplication::translate("UICommon", "External", "AuthType");
+ case KAuthType_Guest: return QApplication::translate("UICommon", "Guest", "AuthType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* KAuthType <= QString: */
+template<> KAuthType fromString<KAuthType>(const QString &strType)
+{
+ QHash<QString, KAuthType> list;
+ list.insert(QApplication::translate("UICommon", "Null", "AuthType"), KAuthType_Null);
+ list.insert(QApplication::translate("UICommon", "External", "AuthType"), KAuthType_External);
+ list.insert(QApplication::translate("UICommon", "Guest", "AuthType"), KAuthType_Guest);
+ if (!list.contains(strType))
+ {
+ AssertMsgFailed(("No value for '%s'", strType.toUtf8().constData()));
+ }
+ return list.value(strType, KAuthType_Null);
+}
+
+/* QString <= KStorageBus: */
+template<> QString toString(const KStorageBus &bus)
+{
+ switch (bus)
+ {
+ case KStorageBus_IDE: return QApplication::translate("UICommon", "IDE", "StorageBus");
+ case KStorageBus_SATA: return QApplication::translate("UICommon", "SATA", "StorageBus");
+ case KStorageBus_SCSI: return QApplication::translate("UICommon", "SCSI", "StorageBus");
+ case KStorageBus_Floppy: return QApplication::translate("UICommon", "Floppy", "StorageBus");
+ case KStorageBus_SAS: return QApplication::translate("UICommon", "SAS", "StorageBus");
+ case KStorageBus_USB: return QApplication::translate("UICommon", "USB", "StorageBus");
+ case KStorageBus_PCIe: return QApplication::translate("UICommon", "PCIe", "StorageBus");
+ case KStorageBus_VirtioSCSI: return QApplication::translate("UICommon", "virtio-scsi", "StorageBus");
+ default: AssertMsgFailed(("No text for %d", bus)); break;
+ }
+ return QString();
+}
+
+/* KStorageBus <= QString: */
+template<> KStorageBus fromString<KStorageBus>(const QString &strType)
+{
+ QHash<QString, KStorageBus> list;
+ list.insert(QApplication::translate("UICommon", "IDE", "StorageBus"), KStorageBus_IDE);
+ list.insert(QApplication::translate("UICommon", "SATA", "StorageBus"), KStorageBus_SATA);
+ list.insert(QApplication::translate("UICommon", "SCSI", "StorageBus"), KStorageBus_SCSI);
+ list.insert(QApplication::translate("UICommon", "Floppy", "StorageBus"), KStorageBus_Floppy);
+ list.insert(QApplication::translate("UICommon", "SAS", "StorageBus"), KStorageBus_SAS);
+ list.insert(QApplication::translate("UICommon", "USB", "StorageBus"), KStorageBus_USB);
+ list.insert(QApplication::translate("UICommon", "PCIe", "StorageBus"), KStorageBus_PCIe);
+ list.insert(QApplication::translate("UICommon", "virtio-scsi", "StorageBus"), KStorageBus_VirtioSCSI);
+ if (!list.contains(strType))
+ {
+ AssertMsgFailed(("No value for '%s'", strType.toUtf8().constData()));
+ }
+ return list.value(strType, KStorageBus_Null);
+}
+
+/* QString <= KStorageControllerType: */
+template<> QString toString(const KStorageControllerType &type)
+{
+ switch (type)
+ {
+ case KStorageControllerType_LsiLogic: return QApplication::translate("UICommon", "LsiLogic", "StorageControllerType");
+ case KStorageControllerType_BusLogic: return QApplication::translate("UICommon", "BusLogic", "StorageControllerType");
+ case KStorageControllerType_IntelAhci: return QApplication::translate("UICommon", "AHCI", "StorageControllerType");
+ case KStorageControllerType_PIIX3: return QApplication::translate("UICommon", "PIIX3", "StorageControllerType");
+ case KStorageControllerType_PIIX4: return QApplication::translate("UICommon", "PIIX4", "StorageControllerType");
+ case KStorageControllerType_ICH6: return QApplication::translate("UICommon", "ICH6", "StorageControllerType");
+ case KStorageControllerType_I82078: return QApplication::translate("UICommon", "I82078", "StorageControllerType");
+ case KStorageControllerType_LsiLogicSas: return QApplication::translate("UICommon", "LsiLogic SAS", "StorageControllerType");
+ case KStorageControllerType_USB: return QApplication::translate("UICommon", "USB", "StorageControllerType");
+ case KStorageControllerType_NVMe: return QApplication::translate("UICommon", "NVMe", "StorageControllerType");
+ case KStorageControllerType_VirtioSCSI: return QApplication::translate("UICommon", "virtio-scsi", "StorageControllerType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* KStorageControllerType <= QString: */
+template<> KStorageControllerType fromString<KStorageControllerType>(const QString &strType)
+{
+ QHash<QString, KStorageControllerType> list;
+ list.insert(QApplication::translate("UICommon", "LsiLogic", "StorageControllerType"), KStorageControllerType_LsiLogic);
+ list.insert(QApplication::translate("UICommon", "BusLogic", "StorageControllerType"), KStorageControllerType_BusLogic);
+ list.insert(QApplication::translate("UICommon", "AHCI", "StorageControllerType"), KStorageControllerType_IntelAhci);
+ list.insert(QApplication::translate("UICommon", "PIIX3", "StorageControllerType"), KStorageControllerType_PIIX3);
+ list.insert(QApplication::translate("UICommon", "PIIX4", "StorageControllerType"), KStorageControllerType_PIIX4);
+ list.insert(QApplication::translate("UICommon", "ICH6", "StorageControllerType"), KStorageControllerType_ICH6);
+ list.insert(QApplication::translate("UICommon", "I82078", "StorageControllerType"), KStorageControllerType_I82078);
+ list.insert(QApplication::translate("UICommon", "LsiLogic SAS", "StorageControllerType"), KStorageControllerType_LsiLogicSas);
+ list.insert(QApplication::translate("UICommon", "USB", "StorageControllerType"), KStorageControllerType_USB);
+ list.insert(QApplication::translate("UICommon", "NVMe", "StorageControllerType"), KStorageControllerType_NVMe);
+ list.insert(QApplication::translate("UICommon", "virtio-scsi", "StorageControllerType"), KStorageControllerType_VirtioSCSI);
+ if (!list.contains(strType))
+ {
+ AssertMsgFailed(("No value for '%s'", strType.toUtf8().constData()));
+ }
+ return list.value(strType, KStorageControllerType_Null);
+}
+
+/* QString <= KChipsetType: */
+template<> QString toString(const KChipsetType &type)
+{
+ switch (type)
+ {
+ case KChipsetType_PIIX3: return QApplication::translate("UICommon", "PIIX3", "ChipsetType");
+ case KChipsetType_ICH9: return QApplication::translate("UICommon", "ICH9", "ChipsetType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* QString <= KTpmType: */
+template<> QString toString(const KTpmType &type)
+{
+ switch (type)
+ {
+ case KTpmType_None: return QApplication::translate("UICommon", "None", "TpmType");
+ case KTpmType_v1_2: return QApplication::translate("UICommon", "v1.2", "TpmType");
+ case KTpmType_v2_0: return QApplication::translate("UICommon", "v2.0", "TpmType");
+ case KTpmType_Host: return QApplication::translate("UICommon", "Host", "TpmType");
+ case KTpmType_Swtpm: return QApplication::translate("UICommon", "SWTPM", "TpmType");
+ default: AssertMsgFailed(("No text for %d", type)); break;
+ }
+ return QString();
+}
+
+/* QString <= KNATProtocol: */
+template<> QString toString(const KNATProtocol &protocol)
+{
+ switch (protocol)
+ {
+ case KNATProtocol_UDP: return QApplication::translate("UICommon", "UDP", "NATProtocol");
+ case KNATProtocol_TCP: return QApplication::translate("UICommon", "TCP", "NATProtocol");
+ default: AssertMsgFailed(("No text for %d", protocol)); break;
+ }
+ return QString();
+}
+
+/* QString <= KNATProtocol: */
+template<> QString toInternalString(const KNATProtocol &protocol)
+{
+ QString strResult;
+ switch (protocol)
+ {
+ case KNATProtocol_UDP: strResult = "udp"; break;
+ case KNATProtocol_TCP: strResult = "tcp"; break;
+ default: AssertMsgFailed(("No text for protocol type=%d", protocol)); break;
+ }
+ return strResult;
+}
+
+/* KNATProtocol <= QString: */
+template<> KNATProtocol fromInternalString<KNATProtocol>(const QString &strProtocol)
+{
+ if (strProtocol.compare("udp", Qt::CaseInsensitive) == 0)
+ return KNATProtocol_UDP;
+ if (strProtocol.compare("tcp", Qt::CaseInsensitive) == 0)
+ return KNATProtocol_TCP;
+ AssertMsgFailedReturn(("No value for '%s'", strProtocol.toUtf8().constData()), KNATProtocol_UDP);
+}
+
+/* QString <= KGuestSessionStatus: */
+template<> QString toString(const KGuestSessionStatus &status)
+{
+ switch (status)
+ {
+ case KGuestSessionStatus_Undefined: return QApplication::translate("UICommon", "Undefined", "GuestSessionStatus");
+ case KGuestSessionStatus_Starting: return QApplication::translate("UICommon", "Starting", "GuestSessionStatus");
+ case KGuestSessionStatus_Started: return QApplication::translate("UICommon", "Started", "GuestSessionStatus");
+ case KGuestSessionStatus_Terminating: return QApplication::translate("UICommon", "Terminating", "GuestSessionStatus");
+ case KGuestSessionStatus_Terminated: return QApplication::translate("UICommon", "Terminated", "GuestSessionStatus");
+ case KGuestSessionStatus_TimedOutKilled: return QApplication::translate("UICommon", "Timed Out (Killed)", "GuestSessionStatus");
+ case KGuestSessionStatus_TimedOutAbnormally: return QApplication::translate("UICommon", "Timed Out (Abnormally)", "GuestSessionStatus");
+ case KGuestSessionStatus_Down: return QApplication::translate("UICommon", "Down", "GuestSessionStatus");
+ case KGuestSessionStatus_Error: return QApplication::translate("UICommon", "Error", "GuestSessionStatus");
+ default: AssertMsgFailed(("No text for %d", status)); break;
+ }
+ return QString();
+}
+
+/* KGuestSessionStatus <= QString: */
+template<> KGuestSessionStatus fromString<KGuestSessionStatus>(const QString &strStatus)
+{
+ QHash<QString, KGuestSessionStatus> list;
+ list.insert(QApplication::translate("UICommon", "Undefined", "GuestSessionStatus"), KGuestSessionStatus_Undefined);
+ list.insert(QApplication::translate("UICommon", "Starting", "GuestSessionStatus"), KGuestSessionStatus_Starting);
+ list.insert(QApplication::translate("UICommon", "Started", "GuestSessionStatus"), KGuestSessionStatus_Started);
+ list.insert(QApplication::translate("UICommon", "Terminating", "GuestSessionStatus"), KGuestSessionStatus_Terminating);
+ list.insert(QApplication::translate("UICommon", "Terminated", "GuestSessionStatus"), KGuestSessionStatus_Terminated);
+ list.insert(QApplication::translate("UICommon", "Timed Out (Killed)", "GuestSessionStatus"), KGuestSessionStatus_TimedOutKilled);
+ list.insert(QApplication::translate("UICommon", "Timed Out (Abnormally)", "GuestSessionStatus"), KGuestSessionStatus_TimedOutAbnormally);
+ list.insert(QApplication::translate("UICommon", "Down", "GuestSessionStatus"), KGuestSessionStatus_Down);
+ list.insert(QApplication::translate("UICommon", "Error", "GuestSessionStatus"), KGuestSessionStatus_Error);
+ if (!list.contains(strStatus))
+ {
+ AssertMsgFailed(("No value for '%s'", strStatus.toUtf8().constData()));
+ }
+ return list.value(strStatus, KGuestSessionStatus_Undefined);
+}
+
+/* QString <= KProcessStatus: */
+template<> QString toString(const KProcessStatus &status)
+{
+ switch (status)
+ {
+ case KProcessStatus_Undefined: return QApplication::translate("UICommon", "Undefined", "ProcessStatus");
+ case KProcessStatus_Starting: return QApplication::translate("UICommon", "Starting", "ProcessStatus");
+ case KProcessStatus_Started: return QApplication::translate("UICommon", "Started", "ProcessStatus");
+ case KProcessStatus_Paused: return QApplication::translate("UICommon", "Paused", "ProcessStatus");
+ case KProcessStatus_Terminating: return QApplication::translate("UICommon", "Terminating", "ProcessStatus");
+ case KProcessStatus_TerminatedNormally: return QApplication::translate("UICommon", "Terminated (Normally)", "ProcessStatus");
+ case KProcessStatus_TerminatedSignal: return QApplication::translate("UICommon", "Terminated (Signal)", "ProcessStatus");
+ case KProcessStatus_TerminatedAbnormally: return QApplication::translate("UICommon", "Terminated (Abnormally)", "ProcessStatus");
+ case KProcessStatus_TimedOutKilled: return QApplication::translate("UICommon", "Timed Out (Killed)", "ProcessStatus");
+ case KProcessStatus_TimedOutAbnormally: return QApplication::translate("UICommon", "Timed Out (Abnormally)", "ProcessStatus");
+ case KProcessStatus_Down: return QApplication::translate("UICommon", "Down", "ProcessStatus");
+ case KProcessStatus_Error: return QApplication::translate("UICommon", "Error", "ProcessStatus");
+ default: AssertMsgFailed(("No text for %d", status)); break;
+ }
+ return QString();
+}
+
+/* KProcessStatus <= QString: */
+template<> KProcessStatus fromString<KProcessStatus>(const QString &strStatus)
+{
+ QHash<QString, KProcessStatus> list;
+ list.insert(QApplication::translate("UICommon", "Undefined", "ProcessStatus"), KProcessStatus_Undefined);
+ list.insert(QApplication::translate("UICommon", "Starting", "ProcessStatus"), KProcessStatus_Starting);
+ list.insert(QApplication::translate("UICommon", "Started", "ProcessStatus"), KProcessStatus_Started);
+ list.insert(QApplication::translate("UICommon", "Paused", "ProcessStatus"), KProcessStatus_Paused);
+ list.insert(QApplication::translate("UICommon", "Terminating", "ProcessStatus"), KProcessStatus_Terminating);
+ list.insert(QApplication::translate("UICommon", "Terminated (Normally)", "ProcessStatus"), KProcessStatus_TerminatedNormally);
+ list.insert(QApplication::translate("UICommon", "Terminated (Signal)", "ProcessStatus"), KProcessStatus_TerminatedSignal);
+ list.insert(QApplication::translate("UICommon", "Terminated (Abnormally)", "ProcessStatus"), KProcessStatus_TerminatedAbnormally);
+ list.insert(QApplication::translate("UICommon", "Timed Out (Killed)", "ProcessStatus"), KProcessStatus_TimedOutKilled);
+ list.insert(QApplication::translate("UICommon", "Timed Out (Abnormally)", "ProcessStatus"), KProcessStatus_TimedOutAbnormally);
+ list.insert(QApplication::translate("UICommon", "Down", "ProcessStatus"), KProcessStatus_Down);
+ list.insert(QApplication::translate("UICommon", "Error", "ProcessStatus"), KProcessStatus_Error);
+ if (!list.contains(strStatus))
+ {
+ AssertMsgFailed(("No value for '%s'", strStatus.toUtf8().constData()));
+ }
+ return list.value(strStatus, KProcessStatus_Undefined);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendGlobal.cpp b/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendGlobal.cpp
new file mode 100644
index 00000000..db5dee0a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendGlobal.cpp
@@ -0,0 +1,2905 @@
+/* $Id: UIConverterBackendGlobal.cpp $ */
+/** @file
+ * VBox Qt GUI - UIConverterBackendGlobal implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QHash>
+#include <QRegExp>
+#include <QRegularExpression>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverterBackend.h"
+#include "UIIconPool.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+/* Determines if <Object of type X> can be converted to object of other type.
+ * These functions returns 'true' for all allowed conversions. */
+template<> bool canConvert<Qt::Alignment>() { return true; }
+template<> bool canConvert<Qt::SortOrder>() { return true; }
+template<> bool canConvert<SizeSuffix>() { return true; }
+template<> bool canConvert<StorageSlot>() { return true; }
+template<> bool canConvert<DesktopWatchdogPolicy_SynthTest>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DialogType>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::MenuType>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::MenuApplicationActionType>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::MenuHelpActionType>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::RuntimeMenuViewActionType>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::RuntimeMenuInputActionType>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>() { return true; }
+#ifdef VBOX_WITH_DEBUGGER_GUI
+template<> bool canConvert<UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType>() { return true; }
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+template<> bool canConvert<UIExtraDataMetaDefs::MenuWindowActionType>() { return true; }
+#endif /* VBOX_WS_MAC */
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>() { return true; }
+template<> bool canConvert<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>() { return true; }
+template<> bool canConvert<UIColorThemeType>() { return true; }
+template<> bool canConvert<UILaunchMode>() { return true; }
+template<> bool canConvert<UIToolType>() { return true; }
+template<> bool canConvert<UIVisualStateType>() { return true; }
+template<> bool canConvert<DetailsElementType>() { return true; }
+template<> bool canConvert<PreviewUpdateIntervalType>() { return true; }
+template<> bool canConvert<UIDiskEncryptionCipherType>() { return true; }
+template<> bool canConvert<GUIFeatureType>() { return true; }
+template<> bool canConvert<GlobalSettingsPageType>() { return true; }
+template<> bool canConvert<MachineSettingsPageType>() { return true; }
+template<> bool canConvert<UIRemoteMode>() { return true; }
+template<> bool canConvert<WizardType>() { return true; }
+template<> bool canConvert<IndicatorType>() { return true; }
+template<> bool canConvert<MachineCloseAction>() { return true; }
+template<> bool canConvert<MouseCapturePolicy>() { return true; }
+template<> bool canConvert<GuruMeditationHandlerType>() { return true; }
+template<> bool canConvert<ScalingOptimizationType>() { return true; }
+#ifndef VBOX_WS_MAC
+template<> bool canConvert<MiniToolbarAlignment>() { return true; }
+#endif
+template<> bool canConvert<InformationElementType>() { return true; }
+template<> bool canConvert<MaximumGuestScreenSizePolicy>() { return true; }
+template<> bool canConvert<UIMediumFormat>() { return true; }
+template<> bool canConvert<UISettingsDefs::RecordingMode>() { return true; }
+template<> bool canConvert<VMActivityOverviewColumn>(){ return true; };
+
+
+/* QString <= Qt::Alignment: */
+template<> QString toInternalString(const Qt::Alignment &enmAlignment)
+{
+ QString strResult;
+ switch (enmAlignment)
+ {
+ case Qt::AlignTop: strResult = "Top"; break;
+ case Qt::AlignBottom: strResult = "Bottom"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for alignment=%d", enmAlignment));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* Qt::Alignment <= QString: */
+template<> Qt::Alignment fromInternalString<Qt::Alignment>(const QString &strAlignment)
+{
+ if (strAlignment.compare("Top", Qt::CaseInsensitive) == 0)
+ return Qt::AlignTop;
+ if (strAlignment.compare("Bottom", Qt::CaseInsensitive) == 0)
+ return Qt::AlignBottom;
+ return Qt::AlignTop;
+}
+
+/* QString <= Qt::SortOrder: */
+template<> QString toInternalString(const Qt::SortOrder &enmSortOrder)
+{
+ QString strResult;
+ switch (enmSortOrder)
+ {
+ case Qt::AscendingOrder: strResult = "Ascending"; break;
+ case Qt::DescendingOrder: strResult = "Descending"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for sort order=%d", enmSortOrder));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* Qt::SortOrder <= QString: */
+template<> Qt::SortOrder fromInternalString<Qt::SortOrder>(const QString &strSortOrder)
+{
+ if (strSortOrder.compare("Ascending", Qt::CaseInsensitive) == 0)
+ return Qt::AscendingOrder;
+ if (strSortOrder.compare("Descending", Qt::CaseInsensitive) == 0)
+ return Qt::DescendingOrder;
+ return Qt::AscendingOrder;
+}
+
+/* QString <= SizeSuffix: */
+template<> QString toString(const SizeSuffix &sizeSuffix)
+{
+ QString strResult;
+ switch (sizeSuffix)
+ {
+ case SizeSuffix_Byte: strResult = QApplication::translate("UICommon", "B", "size suffix Bytes"); break;
+ case SizeSuffix_KiloByte: strResult = QApplication::translate("UICommon", "KB", "size suffix KBytes=1024 Bytes"); break;
+ case SizeSuffix_MegaByte: strResult = QApplication::translate("UICommon", "MB", "size suffix MBytes=1024 KBytes"); break;
+ case SizeSuffix_GigaByte: strResult = QApplication::translate("UICommon", "GB", "size suffix GBytes=1024 MBytes"); break;
+ case SizeSuffix_TeraByte: strResult = QApplication::translate("UICommon", "TB", "size suffix TBytes=1024 GBytes"); break;
+ case SizeSuffix_PetaByte: strResult = QApplication::translate("UICommon", "PB", "size suffix PBytes=1024 TBytes"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for size suffix=%d", sizeSuffix));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* SizeSuffix <= QString: */
+template<> SizeSuffix fromString<SizeSuffix>(const QString &strSizeSuffix)
+{
+ QHash<QString, SizeSuffix> list;
+ list.insert(QApplication::translate("UICommon", "B", "size suffix Bytes"), SizeSuffix_Byte);
+ list.insert(QApplication::translate("UICommon", "KB", "size suffix KBytes=1024 Bytes"), SizeSuffix_KiloByte);
+ list.insert(QApplication::translate("UICommon", "MB", "size suffix MBytes=1024 KBytes"), SizeSuffix_MegaByte);
+ list.insert(QApplication::translate("UICommon", "GB", "size suffix GBytes=1024 MBytes"), SizeSuffix_GigaByte);
+ list.insert(QApplication::translate("UICommon", "TB", "size suffix TBytes=1024 GBytes"), SizeSuffix_TeraByte);
+ list.insert(QApplication::translate("UICommon", "PB", "size suffix PBytes=1024 TBytes"), SizeSuffix_PetaByte);
+ if (!list.contains(strSizeSuffix))
+ {
+ AssertMsgFailed(("No value for '%s'", strSizeSuffix.toUtf8().constData()));
+ }
+ return list.value(strSizeSuffix);
+}
+
+/* QString <= StorageSlot: */
+template<> QString toString(const StorageSlot &storageSlot)
+{
+ QString strResult;
+ switch (storageSlot.bus)
+ {
+ case KStorageBus_IDE:
+ {
+ int iMaxPort = uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(storageSlot.bus);
+ int iMaxDevice = uiCommon().virtualBox().GetSystemProperties().GetMaxDevicesPerPortForStorageBus(storageSlot.bus);
+ if (storageSlot.port < 0 || storageSlot.port > iMaxPort)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d", storageSlot.bus, storageSlot.port));
+ break;
+ }
+ if (storageSlot.device < 0 || storageSlot.device > iMaxDevice)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d & device=%d", storageSlot.bus, storageSlot.port, storageSlot.device));
+ break;
+ }
+ if (storageSlot.port == 0 && storageSlot.device == 0)
+ strResult = QApplication::translate("UICommon", "IDE Primary Device 0", "StorageSlot");
+ else if (storageSlot.port == 0 && storageSlot.device == 1)
+ strResult = QApplication::translate("UICommon", "IDE Primary Device 1", "StorageSlot");
+ else if (storageSlot.port == 1 && storageSlot.device == 0)
+ strResult = QApplication::translate("UICommon", "IDE Secondary Device 0", "StorageSlot");
+ else if (storageSlot.port == 1 && storageSlot.device == 1)
+ strResult = QApplication::translate("UICommon", "IDE Secondary Device 1", "StorageSlot");
+ break;
+ }
+ case KStorageBus_SATA:
+ {
+ int iMaxPort = uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(storageSlot.bus);
+ if (storageSlot.port < 0 || storageSlot.port > iMaxPort)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d", storageSlot.bus, storageSlot.port));
+ break;
+ }
+ if (storageSlot.device != 0)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d & device=%d", storageSlot.bus, storageSlot.port, storageSlot.device));
+ break;
+ }
+ strResult = QApplication::translate("UICommon", "SATA Port %1", "StorageSlot").arg(storageSlot.port);
+ break;
+ }
+ case KStorageBus_SCSI:
+ {
+ int iMaxPort = uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(storageSlot.bus);
+ if (storageSlot.port < 0 || storageSlot.port > iMaxPort)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d", storageSlot.bus, storageSlot.port));
+ break;
+ }
+ if (storageSlot.device != 0)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d & device=%d", storageSlot.bus, storageSlot.port, storageSlot.device));
+ break;
+ }
+ strResult = QApplication::translate("UICommon", "SCSI Port %1", "StorageSlot").arg(storageSlot.port);
+ break;
+ }
+ case KStorageBus_SAS:
+ {
+ int iMaxPort = uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(storageSlot.bus);
+ if (storageSlot.port < 0 || storageSlot.port > iMaxPort)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d", storageSlot.bus, storageSlot.port));
+ break;
+ }
+ if (storageSlot.device != 0)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d & device=%d", storageSlot.bus, storageSlot.port, storageSlot.device));
+ break;
+ }
+ strResult = QApplication::translate("UICommon", "SAS Port %1", "StorageSlot").arg(storageSlot.port);
+ break;
+ }
+ case KStorageBus_Floppy:
+ {
+ int iMaxDevice = uiCommon().virtualBox().GetSystemProperties().GetMaxDevicesPerPortForStorageBus(storageSlot.bus);
+ if (storageSlot.port != 0)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d", storageSlot.bus, storageSlot.port));
+ break;
+ }
+ if (storageSlot.device < 0 || storageSlot.device > iMaxDevice)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d & device=%d", storageSlot.bus, storageSlot.port, storageSlot.device));
+ break;
+ }
+ strResult = QApplication::translate("UICommon", "Floppy Device %1", "StorageSlot").arg(storageSlot.device);
+ break;
+ }
+ case KStorageBus_USB:
+ {
+ int iMaxPort = uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(storageSlot.bus);
+ if (storageSlot.port < 0 || storageSlot.port > iMaxPort)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d", storageSlot.bus, storageSlot.port));
+ break;
+ }
+ if (storageSlot.device != 0)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d & device=%d", storageSlot.bus, storageSlot.port, storageSlot.device));
+ break;
+ }
+ strResult = QApplication::translate("UICommon", "USB Port %1", "StorageSlot").arg(storageSlot.port);
+ break;
+ }
+ case KStorageBus_PCIe:
+ {
+ int iMaxPort = uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(storageSlot.bus);
+ if (storageSlot.port < 0 || storageSlot.port > iMaxPort)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d", storageSlot.bus, storageSlot.port));
+ break;
+ }
+ if (storageSlot.device != 0)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d & device=%d", storageSlot.bus, storageSlot.port, storageSlot.device));
+ break;
+ }
+ strResult = QApplication::translate("UICommon", "NVMe Port %1", "StorageSlot").arg(storageSlot.port);
+ break;
+ }
+ case KStorageBus_VirtioSCSI:
+ {
+ int iMaxPort = uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(storageSlot.bus);
+ if (storageSlot.port < 0 || storageSlot.port > iMaxPort)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d", storageSlot.bus, storageSlot.port));
+ break;
+ }
+ if (storageSlot.device != 0)
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d & device=%d", storageSlot.bus, storageSlot.port, storageSlot.device));
+ break;
+ }
+ strResult = QApplication::translate("UICommon", "virtio-scsi Port %1", "StorageSlot").arg(storageSlot.port);
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("No text for bus=%d & port=%d & device=%d", storageSlot.bus, storageSlot.port, storageSlot.device));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* StorageSlot <= QString: */
+template<> StorageSlot fromString<StorageSlot>(const QString &strStorageSlot)
+{
+ /* Prepare a hash of known port templates: */
+ QHash<int, QString> templates;
+ templates[0] = QApplication::translate("UICommon", "IDE Primary Device 0", "StorageSlot");
+ templates[1] = QApplication::translate("UICommon", "IDE Primary Device 1", "StorageSlot");
+ templates[2] = QApplication::translate("UICommon", "IDE Secondary Device 0", "StorageSlot");
+ templates[3] = QApplication::translate("UICommon", "IDE Secondary Device 1", "StorageSlot");
+ templates[4] = QApplication::translate("UICommon", "SATA Port %1", "StorageSlot");
+ templates[5] = QApplication::translate("UICommon", "SCSI Port %1", "StorageSlot");
+ templates[6] = QApplication::translate("UICommon", "SAS Port %1", "StorageSlot");
+ templates[7] = QApplication::translate("UICommon", "Floppy Device %1", "StorageSlot");
+ templates[8] = QApplication::translate("UICommon", "USB Port %1", "StorageSlot");
+ templates[9] = QApplication::translate("UICommon", "NVMe Port %1", "StorageSlot");
+ templates[10] = QApplication::translate("UICommon", "virtio-scsi Port %1", "StorageSlot");
+
+ /* Search for a template index strStorageSlot corresponds to: */
+ int iIndex = -1;
+ QRegExp regExp;
+ for (int i = 0; i < templates.size(); ++i)
+ {
+ regExp = QRegExp(i >= 0 && i <= 3 ? templates.value(i) : templates.value(i).arg("(\\d+)"));
+ if (regExp.indexIn(strStorageSlot) != -1)
+ {
+ iIndex = i;
+ break;
+ }
+ }
+
+ /* Compose result: */
+ StorageSlot result;
+
+ /* First we determine bus type: */
+ switch (iIndex)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3: result.bus = KStorageBus_IDE; break;
+ case 4: result.bus = KStorageBus_SATA; break;
+ case 5: result.bus = KStorageBus_SCSI; break;
+ case 6: result.bus = KStorageBus_SAS; break;
+ case 7: result.bus = KStorageBus_Floppy; break;
+ case 8: result.bus = KStorageBus_USB; break;
+ case 9: result.bus = KStorageBus_PCIe; break;
+ case 10: result.bus = KStorageBus_VirtioSCSI; break;
+ default: AssertMsgFailed(("No storage bus for text='%s'", strStorageSlot.toUtf8().constData())); break;
+ }
+
+ /* Second we determine port/device pair: */
+ switch (iIndex)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ {
+ if (result.bus == KStorageBus_Null)
+ break;
+ const int iMaxPort = uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(result.bus);
+ const int iMaxDevice = uiCommon().virtualBox().GetSystemProperties().GetMaxDevicesPerPortForStorageBus(result.bus);
+ const LONG iPort = iIndex / iMaxPort;
+ const LONG iDevice = iIndex % iMaxPort;
+ if (iPort < 0 || iPort > iMaxPort)
+ {
+ AssertMsgFailed(("No storage port for text='%s'", strStorageSlot.toUtf8().constData()));
+ break;
+ }
+ if (iDevice < 0 || iDevice > iMaxDevice)
+ {
+ AssertMsgFailed(("No storage device for text='%s'", strStorageSlot.toUtf8().constData()));
+ break;
+ }
+ result.port = iPort;
+ result.device = iDevice;
+ break;
+ }
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ {
+ if (result.bus == KStorageBus_Null)
+ break;
+ const int iMaxPort = uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(result.bus);
+ const LONG iPort = regExp.cap(1).toInt();
+ const LONG iDevice = 0;
+ if (iPort < 0 || iPort > iMaxPort)
+ {
+ AssertMsgFailed(("No storage port for text='%s'", strStorageSlot.toUtf8().constData()));
+ break;
+ }
+ result.port = iPort;
+ result.device = iDevice;
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("No storage slot for text='%s'", strStorageSlot.toUtf8().constData()));
+ break;
+ }
+ }
+
+ /* Return result: */
+ return result;
+}
+
+/* DesktopWatchdogPolicy_SynthTest <= QString: */
+template<> DesktopWatchdogPolicy_SynthTest fromInternalString<DesktopWatchdogPolicy_SynthTest>(const QString &strPolicyType)
+{
+ if (strPolicyType.compare("Disabled", Qt::CaseInsensitive) == 0)
+ return DesktopWatchdogPolicy_SynthTest_Disabled;
+ if (strPolicyType.compare("ManagerOnly", Qt::CaseInsensitive) == 0)
+ return DesktopWatchdogPolicy_SynthTest_ManagerOnly;
+ if (strPolicyType.compare("MachineOnly", Qt::CaseInsensitive) == 0)
+ return DesktopWatchdogPolicy_SynthTest_MachineOnly;
+ if (strPolicyType.compare("Both", Qt::CaseInsensitive) == 0)
+ return DesktopWatchdogPolicy_SynthTest_Both;
+ return DesktopWatchdogPolicy_SynthTest_Both;
+}
+
+/* QString <= UIExtraDataMetaDefs::DialogType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DialogType &enmDialogType)
+{
+ QString strResult;
+ switch (enmDialogType)
+ {
+ case UIExtraDataMetaDefs::DialogType_VISOCreator: strResult = "VISOCreator"; break;
+ case UIExtraDataMetaDefs::DialogType_BootFailure: strResult = "BootFailure"; break;
+ case UIExtraDataMetaDefs::DialogType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for dialog type=%d", enmDialogType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DialogType <= QString: */
+template<> UIExtraDataMetaDefs::DialogType fromInternalString<UIExtraDataMetaDefs::DialogType>(const QString &strDialogType)
+{
+ if (strDialogType.compare("VISOCreator", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DialogType_VISOCreator;
+ if (strDialogType.compare("BootFailure", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DialogType_BootFailure;
+ if (strDialogType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DialogType_All;
+ return UIExtraDataMetaDefs::DialogType_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::MenuType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::MenuType &menuType)
+{
+ QString strResult;
+ switch (menuType)
+ {
+ case UIExtraDataMetaDefs::MenuType_Application: strResult = "Application"; break;
+ case UIExtraDataMetaDefs::MenuType_Machine: strResult = "Machine"; break;
+ case UIExtraDataMetaDefs::MenuType_View: strResult = "View"; break;
+ case UIExtraDataMetaDefs::MenuType_Input: strResult = "Input"; break;
+ case UIExtraDataMetaDefs::MenuType_Devices: strResult = "Devices"; break;
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ case UIExtraDataMetaDefs::MenuType_Debug: strResult = "Debug"; break;
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef RT_OS_DARWIN
+ case UIExtraDataMetaDefs::MenuType_Window: strResult = "Window"; break;
+#endif /* RT_OS_DARWIN */
+ case UIExtraDataMetaDefs::MenuType_Help: strResult = "Help"; break;
+ case UIExtraDataMetaDefs::MenuType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for indicator type=%d", menuType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::MenuType <= QString: */
+template<> UIExtraDataMetaDefs::MenuType fromInternalString<UIExtraDataMetaDefs::MenuType>(const QString &strMenuType)
+{
+ if (strMenuType.compare("Application", Qt::CaseInsensitive) == 0) return UIExtraDataMetaDefs::MenuType_Application;
+ if (strMenuType.compare("Machine", Qt::CaseInsensitive) == 0) return UIExtraDataMetaDefs::MenuType_Machine;
+ if (strMenuType.compare("View", Qt::CaseInsensitive) == 0) return UIExtraDataMetaDefs::MenuType_View;
+ if (strMenuType.compare("Input", Qt::CaseInsensitive) == 0) return UIExtraDataMetaDefs::MenuType_Input;
+ if (strMenuType.compare("Devices", Qt::CaseInsensitive) == 0) return UIExtraDataMetaDefs::MenuType_Devices;
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ if (strMenuType.compare("Debug", Qt::CaseInsensitive) == 0) return UIExtraDataMetaDefs::MenuType_Debug;
+#endif
+#ifdef RT_OS_DARWIN
+ if (strMenuType.compare("Window", Qt::CaseInsensitive) == 0) return UIExtraDataMetaDefs::MenuType_Window;
+#endif
+ if (strMenuType.compare("Help", Qt::CaseInsensitive) == 0) return UIExtraDataMetaDefs::MenuType_Help;
+ if (strMenuType.compare("All", Qt::CaseInsensitive) == 0) return UIExtraDataMetaDefs::MenuType_All;
+ return UIExtraDataMetaDefs::MenuType_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::MenuApplicationActionType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::MenuApplicationActionType &menuApplicationActionType)
+{
+ QString strResult;
+ switch (menuApplicationActionType)
+ {
+#ifdef VBOX_WS_MAC
+ case UIExtraDataMetaDefs::MenuApplicationActionType_About: strResult = "About"; break;
+#endif /* VBOX_WS_MAC */
+ case UIExtraDataMetaDefs::MenuApplicationActionType_Preferences: strResult = "Preferences"; break;
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ case UIExtraDataMetaDefs::MenuApplicationActionType_NetworkAccessManager: strResult = "NetworkAccessManager"; break;
+ case UIExtraDataMetaDefs::MenuApplicationActionType_CheckForUpdates: strResult = "CheckForUpdates"; break;
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ case UIExtraDataMetaDefs::MenuApplicationActionType_ResetWarnings: strResult = "ResetWarnings"; break;
+ case UIExtraDataMetaDefs::MenuApplicationActionType_Close: strResult = "Close"; break;
+ case UIExtraDataMetaDefs::MenuApplicationActionType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for action type=%d", menuApplicationActionType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::MenuApplicationActionType <= QString: */
+template<> UIExtraDataMetaDefs::MenuApplicationActionType
+fromInternalString<UIExtraDataMetaDefs::MenuApplicationActionType>(const QString &strMenuApplicationActionType)
+{
+#ifdef VBOX_WS_MAC
+ if (strMenuApplicationActionType.compare("About", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuApplicationActionType_About;
+#endif
+ if (strMenuApplicationActionType.compare("Preferences", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuApplicationActionType_Preferences;
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ if (strMenuApplicationActionType.compare("NetworkAccessManager", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuApplicationActionType_NetworkAccessManager;
+ if (strMenuApplicationActionType.compare("CheckForUpdates", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuApplicationActionType_CheckForUpdates;
+#endif
+ if (strMenuApplicationActionType.compare("ResetWarnings", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuApplicationActionType_ResetWarnings;
+ if (strMenuApplicationActionType.compare("Close", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuApplicationActionType_Close;
+ if (strMenuApplicationActionType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuApplicationActionType_All;
+ return UIExtraDataMetaDefs::MenuApplicationActionType_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::MenuHelpActionType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::MenuHelpActionType &menuHelpActionType)
+{
+ QString strResult;
+ switch (menuHelpActionType)
+ {
+ case UIExtraDataMetaDefs::MenuHelpActionType_Contents: strResult = "Contents"; break;
+ case UIExtraDataMetaDefs::MenuHelpActionType_WebSite: strResult = "WebSite"; break;
+ case UIExtraDataMetaDefs::MenuHelpActionType_BugTracker: strResult = "BugTracker"; break;
+ case UIExtraDataMetaDefs::MenuHelpActionType_Forums: strResult = "Forums"; break;
+ case UIExtraDataMetaDefs::MenuHelpActionType_Oracle: strResult = "Oracle"; break;
+ case UIExtraDataMetaDefs::MenuHelpActionType_OnlineDocumentation: strResult = "OnlineDocumentation"; break;
+#ifndef VBOX_WS_MAC
+ case UIExtraDataMetaDefs::MenuHelpActionType_About: strResult = "About"; break;
+#endif /* !VBOX_WS_MAC */
+ case UIExtraDataMetaDefs::MenuHelpActionType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for action type=%d", menuHelpActionType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::MenuHelpActionType <= QString: */
+template<> UIExtraDataMetaDefs::MenuHelpActionType
+fromInternalString<UIExtraDataMetaDefs::MenuHelpActionType>(const QString &strMenuHelpActionType)
+{
+ if (strMenuHelpActionType.compare("Contents", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuHelpActionType_Contents;
+ if (strMenuHelpActionType.compare("WebSite", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuHelpActionType_WebSite;
+ if (strMenuHelpActionType.compare("BugTracker", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuHelpActionType_BugTracker;
+ if (strMenuHelpActionType.compare("Forums", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuHelpActionType_Forums;
+ if (strMenuHelpActionType.compare("Oracle", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuHelpActionType_Oracle;
+ if (strMenuHelpActionType.compare("OnlineDocumentation", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuHelpActionType_OnlineDocumentation;
+#ifndef VBOX_WS_MAC
+ if (strMenuHelpActionType.compare("About", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuHelpActionType_About;
+#endif
+ if (strMenuHelpActionType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuHelpActionType_All;
+ return UIExtraDataMetaDefs::MenuHelpActionType_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::RuntimeMenuMachineActionType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuMachineActionType &runtimeMenuMachineActionType)
+{
+ QString strResult;
+ switch (runtimeMenuMachineActionType)
+ {
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SettingsDialog: strResult = "SettingsDialog"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_TakeSnapshot: strResult = "TakeSnapshot"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_InformationDialog: strResult = "InformationDialog"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_FileManagerDialog: strResult = "FileManagerDialog"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_GuestProcessControlDialog: strResult = "GuestProcessControlDialog"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Pause: strResult = "Pause"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Reset: strResult = "Reset"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Detach: strResult = "Detach"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SaveState: strResult = "SaveState"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Shutdown: strResult = "Shutdown"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_PowerOff: strResult = "PowerOff"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_LogDialog: strResult = "LogDialog"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Nothing: strResult = "Nothing"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuMachineActionType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for action type=%d", runtimeMenuMachineActionType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::RuntimeMenuMachineActionType <= QString: */
+template<> UIExtraDataMetaDefs::RuntimeMenuMachineActionType
+fromInternalString<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>(const QString &strRuntimeMenuMachineActionType)
+{
+ if (strRuntimeMenuMachineActionType.compare("SettingsDialog", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SettingsDialog;
+ if (strRuntimeMenuMachineActionType.compare("TakeSnapshot", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_TakeSnapshot;
+ if (strRuntimeMenuMachineActionType.compare("InformationDialog", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_InformationDialog;
+ if (strRuntimeMenuMachineActionType.compare("FileManagerDialog", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_FileManagerDialog;
+ if (strRuntimeMenuMachineActionType.compare("GuestProcessControlDialog", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_GuestProcessControlDialog;
+ if (strRuntimeMenuMachineActionType.compare("Pause", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Pause;
+ if (strRuntimeMenuMachineActionType.compare("Reset", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Reset;
+ if (strRuntimeMenuMachineActionType.compare("Detach", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Detach;
+ if (strRuntimeMenuMachineActionType.compare("SaveState", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SaveState;
+ if (strRuntimeMenuMachineActionType.compare("Shutdown", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Shutdown;
+ if (strRuntimeMenuMachineActionType.compare("PowerOff", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_PowerOff;
+ if (strRuntimeMenuMachineActionType.compare("LogDialog", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_LogDialog;
+ if (strRuntimeMenuMachineActionType.compare("Nothing", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Nothing;
+ if (strRuntimeMenuMachineActionType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_All;
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::RuntimeMenuViewActionType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuViewActionType &runtimeMenuViewActionType)
+{
+ QString strResult;
+ switch (runtimeMenuViewActionType)
+ {
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_Fullscreen: strResult = "Fullscreen"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_Seamless: strResult = "Seamless"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_Scale: strResult = "Scale"; break;
+#ifndef VBOX_WS_MAC
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_MinimizeWindow: strResult = "MinimizeWindow"; break;
+#endif /* !VBOX_WS_MAC */
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow: strResult = "AdjustWindow"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_GuestAutoresize: strResult = "GuestAutoresize"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_TakeScreenshot: strResult = "TakeScreenshot"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_Recording: strResult = "Recording"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_RecordingSettings: strResult = "RecordingSettings"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_StartRecording: strResult = "StartRecording"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_VRDEServer: strResult = "VRDEServer"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar: strResult = "MenuBar"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBarSettings: strResult = "MenuBarSettings"; break;
+#ifndef VBOX_WS_MAC
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleMenuBar: strResult = "ToggleMenuBar"; break;
+#endif /* !VBOX_WS_MAC */
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar: strResult = "StatusBar"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBarSettings: strResult = "StatusBarSettings"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleStatusBar: strResult = "ToggleStatusBar"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize: strResult = "Resize"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_Remap: strResult = "Remap"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_Rescale: strResult = "Rescale"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuViewActionType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for action type=%d", runtimeMenuViewActionType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::RuntimeMenuViewActionType <= QString: */
+template<> UIExtraDataMetaDefs::RuntimeMenuViewActionType
+fromInternalString<UIExtraDataMetaDefs::RuntimeMenuViewActionType>(const QString &strRuntimeMenuViewActionType)
+{
+ if (strRuntimeMenuViewActionType.compare("Fullscreen", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Fullscreen;
+ if (strRuntimeMenuViewActionType.compare("Seamless", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Seamless;
+ if (strRuntimeMenuViewActionType.compare("Scale", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Scale;
+#ifndef VBOX_WS_MAC
+ if (strRuntimeMenuViewActionType.compare("MinimizeWindow", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_MinimizeWindow;
+#endif /* !VBOX_WS_MAC */
+ if (strRuntimeMenuViewActionType.compare("AdjustWindow", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow;
+ if (strRuntimeMenuViewActionType.compare("GuestAutoresize", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_GuestAutoresize;
+ if (strRuntimeMenuViewActionType.compare("TakeScreenshot", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_TakeScreenshot;
+ if (strRuntimeMenuViewActionType.compare("Recording", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Recording;
+ if (strRuntimeMenuViewActionType.compare("RecordingSettings", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_RecordingSettings;
+ if (strRuntimeMenuViewActionType.compare("StartRecording", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_StartRecording;
+ if (strRuntimeMenuViewActionType.compare("VRDEServer", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_VRDEServer;
+ if (strRuntimeMenuViewActionType.compare("MenuBar", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar;
+ if (strRuntimeMenuViewActionType.compare("MenuBarSettings", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBarSettings;
+#ifndef VBOX_WS_MAC
+ if (strRuntimeMenuViewActionType.compare("ToggleMenuBar", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleMenuBar;
+#endif /* !VBOX_WS_MAC */
+ if (strRuntimeMenuViewActionType.compare("StatusBar", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar;
+ if (strRuntimeMenuViewActionType.compare("StatusBarSettings", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBarSettings;
+ if (strRuntimeMenuViewActionType.compare("ToggleStatusBar", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleStatusBar;
+ if (strRuntimeMenuViewActionType.compare("Resize", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize;
+ if (strRuntimeMenuViewActionType.compare("Remap", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Remap;
+ if (strRuntimeMenuViewActionType.compare("Rescale", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Rescale;
+ if (strRuntimeMenuViewActionType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_All;
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::RuntimeMenuInputActionType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuInputActionType &runtimeMenuInputActionType)
+{
+ QString strResult;
+ switch (runtimeMenuInputActionType)
+ {
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_Keyboard: strResult = "Keyboard"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_KeyboardSettings: strResult = "KeyboardSettings"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_SoftKeyboard: strResult = "SoftKeyboard"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCAD: strResult = "TypeCAD"; break;
+#ifdef VBOX_WS_X11
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCABS: strResult = "TypeCABS"; break;
+#endif /* VBOX_WS_X11 */
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCtrlBreak: strResult = "TypeCtrlBreak"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeInsert: strResult = "TypeInsert"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypePrintScreen: strResult = "TypePrintScreen"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeAltPrintScreen: strResult = "TypeAltPrintScreen"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_Mouse: strResult = "Mouse"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_MouseIntegration: strResult = "MouseIntegration"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeHostKeyCombo: strResult = "TypeHostKeyCombo"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuInputActionType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for action type=%d", runtimeMenuInputActionType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::RuntimeMenuInputActionType <= QString: */
+template<> UIExtraDataMetaDefs::RuntimeMenuInputActionType
+fromInternalString<UIExtraDataMetaDefs::RuntimeMenuInputActionType>(const QString &strRuntimeMenuInputActionType)
+{
+ if (strRuntimeMenuInputActionType.compare("Keyboard", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_Keyboard;
+ if (strRuntimeMenuInputActionType.compare("KeyboardSettings", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_KeyboardSettings;
+ if (strRuntimeMenuInputActionType.compare("SoftKeyboard", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_SoftKeyboard;
+ if (strRuntimeMenuInputActionType.compare("TypeCAD", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCAD;
+#ifdef VBOX_WS_X11
+ if (strRuntimeMenuInputActionType.compare("TypeCABS", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCABS;
+#endif /* VBOX_WS_X11 */
+ if (strRuntimeMenuInputActionType.compare("TypeCtrlBreak", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCtrlBreak;
+ if (strRuntimeMenuInputActionType.compare("TypeInsert", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeInsert;
+ if (strRuntimeMenuInputActionType.compare("TypePrintScreen", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypePrintScreen;
+ if (strRuntimeMenuInputActionType.compare("TypeAltPrintScreen", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeAltPrintScreen;
+ if (strRuntimeMenuInputActionType.compare("Mouse", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_Mouse;
+ if (strRuntimeMenuInputActionType.compare("MouseIntegration", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_MouseIntegration;
+ if (strRuntimeMenuInputActionType.compare("TypeHostKeyCombo", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeHostKeyCombo;
+ if (strRuntimeMenuInputActionType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_All;
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::RuntimeMenuDevicesActionType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuDevicesActionType &runtimeMenuDevicesActionType)
+{
+ QString strResult;
+ switch (runtimeMenuDevicesActionType)
+ {
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrives: strResult = "HardDrives"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrivesSettings: strResult = "HardDrivesSettings"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_OpticalDevices: strResult = "OpticalDevices"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_FloppyDevices: strResult = "FloppyDevices"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Audio: strResult = "Audio"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioOutput: strResult = "AudioOutput"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioInput: strResult = "AudioInput"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Network: strResult = "Network"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_NetworkSettings: strResult = "NetworkSettings"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevices: strResult = "USBDevices"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevicesSettings: strResult = "USBDevicesSettings"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_WebCams: strResult = "WebCams"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedClipboard: strResult = "SharedClipboard"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_DragAndDrop: strResult = "DragAndDrop"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFolders: strResult = "SharedFolders"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFoldersSettings: strResult = "SharedFoldersSettings"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_InsertGuestAdditionsDisk: strResult = "InsertGuestAdditionsDisk"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_UpgradeGuestAdditions: strResult = "UpgradeGuestAdditions"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Nothing: strResult = "Nothing"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for action type=%d", runtimeMenuDevicesActionType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::RuntimeMenuDevicesActionType <= QString: */
+template<> UIExtraDataMetaDefs::RuntimeMenuDevicesActionType
+fromInternalString<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>(const QString &strRuntimeMenuDevicesActionType)
+{
+ if (strRuntimeMenuDevicesActionType.compare("HardDrives", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrives;
+ if (strRuntimeMenuDevicesActionType.compare("HardDrivesSettings", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrivesSettings;
+ if (strRuntimeMenuDevicesActionType.compare("OpticalDevices", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_OpticalDevices;
+ if (strRuntimeMenuDevicesActionType.compare("FloppyDevices", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_FloppyDevices;
+ if (strRuntimeMenuDevicesActionType.compare("Audio", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Audio;
+ if (strRuntimeMenuDevicesActionType.compare("AudioOutput", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioOutput;
+ if (strRuntimeMenuDevicesActionType.compare("AudioInput", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioInput;
+ if (strRuntimeMenuDevicesActionType.compare("Network", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Network;
+ if (strRuntimeMenuDevicesActionType.compare("NetworkSettings", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_NetworkSettings;
+ if (strRuntimeMenuDevicesActionType.compare("USBDevices", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevices;
+ if (strRuntimeMenuDevicesActionType.compare("USBDevicesSettings", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevicesSettings;
+ if (strRuntimeMenuDevicesActionType.compare("WebCams", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_WebCams;
+ if (strRuntimeMenuDevicesActionType.compare("SharedClipboard", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedClipboard;
+ if (strRuntimeMenuDevicesActionType.compare("DragAndDrop", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_DragAndDrop;
+ if (strRuntimeMenuDevicesActionType.compare("SharedFolders", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFolders;
+ if (strRuntimeMenuDevicesActionType.compare("SharedFoldersSettings", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFoldersSettings;
+ if (strRuntimeMenuDevicesActionType.compare("InsertGuestAdditionsDisk", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_InsertGuestAdditionsDisk;
+ if (strRuntimeMenuDevicesActionType.compare("UpgradeGuestAdditions", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_UpgradeGuestAdditions;
+ if (strRuntimeMenuDevicesActionType.compare("Nothing", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Nothing;
+ if (strRuntimeMenuDevicesActionType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_All;
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Invalid;
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+/* QString <= UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType &runtimeMenuDebuggerActionType)
+{
+ QString strResult;
+ switch (runtimeMenuDebuggerActionType)
+ {
+ case UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Statistics: strResult = "Statistics"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_CommandLine: strResult = "CommandLine"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Logging: strResult = "Logging"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_GuestControlConsole: strResult = "GuestControlConsole"; break;
+ case UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for action type=%d", runtimeMenuDebuggerActionType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType <= QString: */
+template<> UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType
+fromInternalString<UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType>(const QString &strRuntimeMenuDebuggerActionType)
+{
+ if (strRuntimeMenuDebuggerActionType.compare("Statistics", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Statistics;
+ if (strRuntimeMenuDebuggerActionType.compare("CommandLine", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_CommandLine;
+ if (strRuntimeMenuDebuggerActionType.compare("Logging", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Logging;
+ if (strRuntimeMenuDebuggerActionType.compare("GuestControlConsole", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_GuestControlConsole;
+ if (strRuntimeMenuDebuggerActionType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_All;
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Invalid;
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+/* QString <= UIExtraDataMetaDefs::MenuWindowActionType: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::MenuWindowActionType &menuWindowActionType)
+{
+ QString strResult;
+ switch (menuWindowActionType)
+ {
+ case UIExtraDataMetaDefs::MenuWindowActionType_Minimize: strResult = "Minimize"; break;
+ case UIExtraDataMetaDefs::MenuWindowActionType_Switch: strResult = "Switch"; break;
+ case UIExtraDataMetaDefs::MenuWindowActionType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for action type=%d", menuWindowActionType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::MenuWindowActionType <= QString: */
+template<> UIExtraDataMetaDefs::MenuWindowActionType
+fromInternalString<UIExtraDataMetaDefs::MenuWindowActionType>(const QString &strMenuWindowActionType)
+{
+ if (strMenuWindowActionType.compare("Minimize", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuWindowActionType_Minimize;
+ if (strMenuWindowActionType.compare("Switch", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuWindowActionType_Switch;
+ if (strMenuWindowActionType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::MenuWindowActionType_All;
+ return UIExtraDataMetaDefs::MenuWindowActionType_Invalid;
+}
+#endif /* VBOX_WS_MAC */
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral &enmDetailsElementOptionTypeGeneral)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeGeneral)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Name: strResult = QApplication::translate("UICommon", "Name"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_OS: strResult = QApplication::translate("UICommon", "OS"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Location: strResult = QApplication::translate("UICommon", "Location"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Groups: strResult = QApplication::translate("UICommon", "Groups"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeGeneral));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral &enmDetailsElementOptionTypeGeneral)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeGeneral)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Name: strResult = "Name"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_OS: strResult = "OS"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Location: strResult = "Location"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Groups: strResult = "Groups"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeGeneral));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>(const QString &strDetailsElementOptionTypeGeneral)
+{
+ if (strDetailsElementOptionTypeGeneral.compare("Name", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Name;
+ if (strDetailsElementOptionTypeGeneral.compare("OS", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_OS;
+ if (strDetailsElementOptionTypeGeneral.compare("Location", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Location;
+ if (strDetailsElementOptionTypeGeneral.compare("Groups", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Groups;
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeSystem: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem &enmDetailsElementOptionTypeSystem)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeSystem)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_RAM: strResult = QApplication::translate("UICommon", "RAM"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_CPUCount: strResult = QApplication::translate("UICommon", "CPU Count"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_CPUExecutionCap: strResult = QApplication::translate("UICommon", "CPU Execution Cap"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_BootOrder: strResult = QApplication::translate("UICommon", "Boot Order"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_ChipsetType: strResult = QApplication::translate("UICommon", "Chipset Type"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_TpmType: strResult = QApplication::translate("UICommon", "TPM Type"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Firmware: strResult = QApplication::translate("UICommon", "Firmware"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_SecureBoot: strResult = QApplication::translate("UICommon", "Secure Boot"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Acceleration: strResult = QApplication::translate("UICommon", "Acceleration"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeSystem));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeSystem: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem &enmDetailsElementOptionTypeSystem)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeSystem)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_RAM: strResult = "RAM"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_CPUCount: strResult = "CPUCount"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_CPUExecutionCap: strResult = "CPUExecutionCap"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_BootOrder: strResult = "BootOrder"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_ChipsetType: strResult = "ChipsetType"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_TpmType: strResult = "TPMType"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Firmware: strResult = "Firmware"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_SecureBoot: strResult = "SecureBoot"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Acceleration: strResult = "Acceleration"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeSystem));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeSystem <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeSystem
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>(const QString &strDetailsElementOptionTypeSystem)
+{
+ if (strDetailsElementOptionTypeSystem.compare("RAM", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_RAM;
+ if (strDetailsElementOptionTypeSystem.compare("CPUCount", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_CPUCount;
+ if (strDetailsElementOptionTypeSystem.compare("CPUExecutionCap", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_CPUExecutionCap;
+ if (strDetailsElementOptionTypeSystem.compare("BootOrder", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_BootOrder;
+ if (strDetailsElementOptionTypeSystem.compare("ChipsetType", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_ChipsetType;
+ if (strDetailsElementOptionTypeSystem.compare("TPMType", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_TpmType;
+ if (strDetailsElementOptionTypeSystem.compare("Firmware", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Firmware;
+ if (strDetailsElementOptionTypeSystem.compare("SecureBoot", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_SecureBoot;
+ if (strDetailsElementOptionTypeSystem.compare("Acceleration", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Acceleration;
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay &enmDetailsElementOptionTypeDisplay)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeDisplay)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_VRAM: strResult = QApplication::translate("UICommon", "VRAM"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_ScreenCount: strResult = QApplication::translate("UICommon", "Screen Count"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_ScaleFactor: strResult = QApplication::translate("UICommon", "Scale Factor"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_GraphicsController: strResult = QApplication::translate("UICommon", "Graphics Controller"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Acceleration: strResult = QApplication::translate("UICommon", "Acceleration"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_VRDE: strResult = QApplication::translate("UICommon", "VRDE"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Recording: strResult = QApplication::translate("UICommon", "Recording"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeDisplay));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay &enmDetailsElementOptionTypeDisplay)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeDisplay)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_VRAM: strResult = "VRAM"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_ScreenCount: strResult = "ScreenCount"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_ScaleFactor: strResult = "ScaleFactor"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_GraphicsController: strResult = "GraphicsController"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Acceleration: strResult = "Acceleration"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_VRDE: strResult = "VRDE"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Recording: strResult = "Recording"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeDisplay));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>(const QString &strDetailsElementOptionTypeDisplay)
+{
+ if (strDetailsElementOptionTypeDisplay.compare("VRAM", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_VRAM;
+ if (strDetailsElementOptionTypeDisplay.compare("ScreenCount", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_ScreenCount;
+ if (strDetailsElementOptionTypeDisplay.compare("ScaleFactor", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_ScaleFactor;
+ if (strDetailsElementOptionTypeDisplay.compare("GraphicsController", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_GraphicsController;
+ if (strDetailsElementOptionTypeDisplay.compare("Acceleration", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Acceleration;
+ if (strDetailsElementOptionTypeDisplay.compare("VRDE", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_VRDE;
+ if (strDetailsElementOptionTypeDisplay.compare("Recording", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Recording;
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeStorage: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage &enmDetailsElementOptionTypeStorage)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeStorage)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_HardDisks: strResult = QApplication::translate("UICommon", "Hard Disks"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_OpticalDevices: strResult = QApplication::translate("UICommon", "Optical Devices"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_FloppyDevices: strResult = QApplication::translate("UICommon", "Floppy Devices"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeStorage));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeStorage: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage &enmDetailsElementOptionTypeStorage)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeStorage)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_HardDisks: strResult = "HardDisks"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_OpticalDevices: strResult = "OpticalDevices"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_FloppyDevices: strResult = "FloppyDevices"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeStorage));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeStorage <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeStorage
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>(const QString &strDetailsElementOptionTypeStorage)
+{
+ if (strDetailsElementOptionTypeStorage.compare("HardDisks", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_HardDisks;
+ if (strDetailsElementOptionTypeStorage.compare("OpticalDevices", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_OpticalDevices;
+ if (strDetailsElementOptionTypeStorage.compare("FloppyDevices", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_FloppyDevices;
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeAudio: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio &enmDetailsElementOptionTypeAudio)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeAudio)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Driver: strResult = QApplication::translate("UICommon", "Driver"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Controller: strResult = QApplication::translate("UICommon", "Controller"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_IO: strResult = QApplication::translate("UICommon", "Input/Output"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeAudio));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeAudio: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio &enmDetailsElementOptionTypeAudio)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeAudio)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Driver: strResult = "Driver"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Controller: strResult = "Controller"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_IO: strResult = "IO"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeAudio));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeAudio <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeAudio
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>(const QString &strDetailsElementOptionTypeAudio)
+{
+ if (strDetailsElementOptionTypeAudio.compare("Driver", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Driver;
+ if (strDetailsElementOptionTypeAudio.compare("Controller", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Controller;
+ if (strDetailsElementOptionTypeAudio.compare("IO", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_IO;
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork &enmDetailsElementOptionTypeNetwork)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeNetwork)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NotAttached: strResult = QApplication::translate("UICommon", "Not Attached", "network adapter"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NAT: strResult = QApplication::translate("UICommon", "NAT"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_BridgedAdapter: strResult = QApplication::translate("UICommon", "Bridged Adapter"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_InternalNetwork: strResult = QApplication::translate("UICommon", "Internal Network"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyAdapter: strResult = QApplication::translate("UICommon", "Host Only Adapter"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_GenericDriver: strResult = QApplication::translate("UICommon", "Generic Driver"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NATNetwork: strResult = QApplication::translate("UICommon", "NAT Network"); break;
+#ifdef VBOX_WITH_CLOUD_NET
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_CloudNetwork: strResult = QApplication::translate("UICommon", "Cloud Network"); break;
+#endif
+#ifdef VBOX_WITH_VMNET
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyNetwork: strResult = QApplication::translate("UICommon", "Host Only Network"); break;
+#endif
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeNetwork));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork &enmDetailsElementOptionTypeNetwork)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeNetwork)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NotAttached: strResult = "NotAttached"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NAT: strResult = "NAT"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_BridgedAdapter: strResult = "BridgedAdapter"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_InternalNetwork: strResult = "InternalNetwork"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyAdapter: strResult = "HostOnlyAdapter"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_GenericDriver: strResult = "GenericDriver"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NATNetwork: strResult = "NATNetwork"; break;
+#ifdef VBOX_WITH_CLOUD_NET
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_CloudNetwork: strResult = "CloudNetwork"; break;
+#endif
+#ifdef VBOX_WITH_VMNET
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyNetwork: strResult = "HostOnlyNetwork"; break;
+#endif
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeNetwork));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(const QString &strDetailsElementOptionTypeNetwork)
+{
+ if (strDetailsElementOptionTypeNetwork.compare("NotAttached", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NotAttached;
+ if (strDetailsElementOptionTypeNetwork.compare("NAT", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NAT;
+ if (strDetailsElementOptionTypeNetwork.compare("BridgedAdapter", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_BridgedAdapter;
+ if (strDetailsElementOptionTypeNetwork.compare("InternalNetwork", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_InternalNetwork;
+ if (strDetailsElementOptionTypeNetwork.compare("HostOnlyAdapter", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyAdapter;
+ if (strDetailsElementOptionTypeNetwork.compare("GenericDriver", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_GenericDriver;
+ if (strDetailsElementOptionTypeNetwork.compare("NATNetwork", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NATNetwork;
+#ifdef VBOX_WITH_CLOUD_NET
+ if (strDetailsElementOptionTypeNetwork.compare("CloudNetwork", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_CloudNetwork;
+#endif
+#ifdef VBOX_WITH_VMNET
+ if (strDetailsElementOptionTypeNetwork.compare("HostOnlyNetwork", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyNetwork;
+#endif
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeSerial: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial &enmDetailsElementOptionTypeSerial)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeSerial)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Disconnected: strResult = QApplication::translate("UICommon", "Disconnected", "serial port"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_HostPipe: strResult = QApplication::translate("UICommon", "Host Pipe"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_HostDevice: strResult = QApplication::translate("UICommon", "Host Device"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_RawFile: strResult = QApplication::translate("UICommon", "Raw File"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_TCP: strResult = QApplication::translate("UICommon", "TCP"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeSerial));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeSerial: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial &enmDetailsElementOptionTypeSerial)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeSerial)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Disconnected: strResult = "Disconnected"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_HostPipe: strResult = "HostPipe"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_HostDevice: strResult = "HostDevice"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_RawFile: strResult = "RawFile"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_TCP: strResult = "TCP"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeSerial));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeSerial <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeSerial
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>(const QString &strDetailsElementOptionTypeSerial)
+{
+ if (strDetailsElementOptionTypeSerial.compare("Disconnected", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Disconnected;
+ if (strDetailsElementOptionTypeSerial.compare("HostPipe", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_HostPipe;
+ if (strDetailsElementOptionTypeSerial.compare("HostDevice", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_HostDevice;
+ if (strDetailsElementOptionTypeSerial.compare("RawFile", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_RawFile;
+ if (strDetailsElementOptionTypeSerial.compare("TCP", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_TCP;
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeUsb: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb &enmDetailsElementOptionTypeUsb)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeUsb)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Controller: strResult = QApplication::translate("UICommon", "Controller"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_DeviceFilters: strResult = QApplication::translate("UICommon", "Device Filters"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeUsb));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeUsb: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb &enmDetailsElementOptionTypeUsb)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeUsb)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Controller: strResult = "Controller"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_DeviceFilters: strResult = "DeviceFilters"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeUsb));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeUsb <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeUsb
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>(const QString &strDetailsElementOptionTypeUsb)
+{
+ if (strDetailsElementOptionTypeUsb.compare("Controller", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Controller;
+ if (strDetailsElementOptionTypeUsb.compare("DeviceFilters", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_DeviceFilters;
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders &enmDetailsElementOptionTypeSharedFolders)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeSharedFolders)
+ {
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeSharedFolders));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders &enmDetailsElementOptionTypeSharedFolders)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeSharedFolders)
+ {
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeSharedFolders));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>(const QString &strDetailsElementOptionTypeSharedFolders)
+{
+ RT_NOREF(strDetailsElementOptionTypeSharedFolders);
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface &enmDetailsElementOptionTypeUserInterface)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeUserInterface)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_VisualState: strResult = QApplication::translate("UICommon", "Visual State"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_MenuBar: strResult = QApplication::translate("UICommon", "Menu Bar"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_StatusBar: strResult = QApplication::translate("UICommon", "Status Bar"); break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_MiniToolbar: strResult = QApplication::translate("UICommon", "Mini Toolbar"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeUserInterface));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface &enmDetailsElementOptionTypeUserInterface)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeUserInterface)
+ {
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_VisualState: strResult = "VisualState"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_MenuBar: strResult = "MenuBar"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_StatusBar: strResult = "StatusBar"; break;
+ case UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_MiniToolbar: strResult = "MiniToolbar"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeUserInterface));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>(const QString &strDetailsElementOptionTypeUserInterface)
+{
+ if (strDetailsElementOptionTypeUserInterface.compare("VisualState", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_VisualState;
+ if (strDetailsElementOptionTypeUserInterface.compare("MenuBar", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_MenuBar;
+ if (strDetailsElementOptionTypeUserInterface.compare("StatusBar", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_StatusBar;
+ if (strDetailsElementOptionTypeUserInterface.compare("MiniToolbar", Qt::CaseInsensitive) == 0)
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_MiniToolbar;
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Invalid;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeDescription: */
+template<> QString toString(const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription &enmDetailsElementOptionTypeDescription)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeDescription)
+ {
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeDescription));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIExtraDataMetaDefs::DetailsElementOptionTypeDescription: */
+template<> QString toInternalString(const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription &enmDetailsElementOptionTypeDescription)
+{
+ QString strResult;
+ switch (enmDetailsElementOptionTypeDescription)
+ {
+ default:
+ {
+ AssertMsgFailed(("No text for details element option type=%d", enmDetailsElementOptionTypeDescription));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIExtraDataMetaDefs::DetailsElementOptionTypeDescription <= QString: */
+template<> UIExtraDataMetaDefs::DetailsElementOptionTypeDescription
+fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>(const QString &strDetailsElementOptionTypeDescription)
+{
+ RT_NOREF(strDetailsElementOptionTypeDescription);
+ return UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Invalid;
+}
+
+/* QString <= UIColorThemeType: */
+template<> QString toString(const UIColorThemeType &colorThemeType)
+{
+ QString strResult;
+ switch (colorThemeType)
+ {
+ case UIColorThemeType_Auto: strResult = QApplication::translate("UICommon", "Follow System Settings", "color theme"); break;
+ case UIColorThemeType_Light: strResult = QApplication::translate("UICommon", "Light", "color theme"); break;
+ case UIColorThemeType_Dark: strResult = QApplication::translate("UICommon", "Dark", "color theme"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for color theme type=%d", colorThemeType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIColorThemeType: */
+template<> QString toInternalString(const UIColorThemeType &colorThemeType)
+{
+ QString strResult;
+ switch (colorThemeType)
+ {
+ case UIColorThemeType_Auto: break;
+ case UIColorThemeType_Light: strResult = "Light"; break;
+ case UIColorThemeType_Dark: strResult = "Dark"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for color theme type=%d", colorThemeType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIColorThemeType <= QString: */
+template<> UIColorThemeType fromInternalString<UIColorThemeType>(const QString &strColorThemeType)
+{
+ if (strColorThemeType.compare("Light", Qt::CaseInsensitive) == 0)
+ return UIColorThemeType_Light;
+ if (strColorThemeType.compare("Dark", Qt::CaseInsensitive) == 0)
+ return UIColorThemeType_Dark;
+ return UIColorThemeType_Auto;
+}
+
+/* UILaunchMode <= QString: */
+template<> UILaunchMode fromInternalString<UILaunchMode>(const QString &strDefaultFrontendType)
+{
+ if (strDefaultFrontendType.compare("Default", Qt::CaseInsensitive) == 0)
+ return UILaunchMode_Default;
+ if (strDefaultFrontendType.compare("Headless", Qt::CaseInsensitive) == 0)
+ return UILaunchMode_Headless;
+ if (strDefaultFrontendType.compare("Separate", Qt::CaseInsensitive) == 0)
+ return UILaunchMode_Separate;
+ return UILaunchMode_Invalid;
+}
+
+/* QString <= UIToolType: */
+template<> QString toInternalString(const UIToolType &enmToolType)
+{
+ QString strResult;
+ switch (enmToolType)
+ {
+ case UIToolType_Welcome: strResult = "Welcome"; break;
+ case UIToolType_Extensions: strResult = "Extensions"; break;
+ case UIToolType_Media: strResult = "Media"; break;
+ case UIToolType_Network: strResult = "Network"; break;
+ case UIToolType_Cloud: strResult = "Cloud"; break;
+ case UIToolType_CloudConsole: strResult = "CloudConsole"; break;
+ case UIToolType_VMActivityOverview: strResult = "Activities"; break;
+ case UIToolType_Details: strResult = "Details"; break;
+ case UIToolType_Snapshots: strResult = "Snapshots"; break;
+ case UIToolType_Logs: strResult = "Logs"; break;
+ case UIToolType_VMActivity: strResult = "Activity"; break;
+ case UIToolType_FileManager: strResult = "FileManager"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for tool type=%d", enmToolType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIToolType <= QString: */
+template<> UIToolType fromInternalString<UIToolType>(const QString &strToolType)
+{
+ if (strToolType.compare("Welcome", Qt::CaseInsensitive) == 0)
+ return UIToolType_Welcome;
+ if (strToolType.compare("Extensions", Qt::CaseInsensitive) == 0)
+ return UIToolType_Extensions;
+ if (strToolType.compare("Media", Qt::CaseInsensitive) == 0)
+ return UIToolType_Media;
+ if (strToolType.compare("Network", Qt::CaseInsensitive) == 0)
+ return UIToolType_Network;
+ if (strToolType.compare("Cloud", Qt::CaseInsensitive) == 0)
+ return UIToolType_Cloud;
+ if (strToolType.compare("CloudConsole", Qt::CaseInsensitive) == 0)
+ return UIToolType_CloudConsole;
+ if (strToolType.compare("Activities", Qt::CaseInsensitive) == 0)
+ return UIToolType_VMActivityOverview;
+ if (strToolType.compare("Details", Qt::CaseInsensitive) == 0)
+ return UIToolType_Details;
+ if (strToolType.compare("Snapshots", Qt::CaseInsensitive) == 0)
+ return UIToolType_Snapshots;
+ if (strToolType.compare("Logs", Qt::CaseInsensitive) == 0)
+ return UIToolType_Logs;
+ if (strToolType.compare("Activity", Qt::CaseInsensitive) == 0)
+ return UIToolType_VMActivity;
+ if (strToolType.compare("FileManager", Qt::CaseInsensitive) == 0)
+ return UIToolType_FileManager;
+ return UIToolType_Invalid;
+}
+
+/* QString <= UIVisualStateType: */
+template<> QString toString(const UIVisualStateType &visualStateType)
+{
+ QString strResult;
+ switch (visualStateType)
+ {
+ case UIVisualStateType_Normal: strResult = QApplication::translate("UICommon", "Normal (window)", "visual state"); break;
+ case UIVisualStateType_Fullscreen: strResult = QApplication::translate("UICommon", "Full-screen", "visual state"); break;
+ case UIVisualStateType_Seamless: strResult = QApplication::translate("UICommon", "Seamless", "visual state"); break;
+ case UIVisualStateType_Scale: strResult = QApplication::translate("UICommon", "Scaled", "visual state"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for visual state type=%d", visualStateType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIVisualStateType: */
+template<> QString toInternalString(const UIVisualStateType &visualStateType)
+{
+ QString strResult;
+ switch (visualStateType)
+ {
+ case UIVisualStateType_Normal: strResult = "Normal"; break;
+ case UIVisualStateType_Fullscreen: strResult = "Fullscreen"; break;
+ case UIVisualStateType_Seamless: strResult = "Seamless"; break;
+ case UIVisualStateType_Scale: strResult = "Scale"; break;
+ case UIVisualStateType_All: strResult = "All"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for visual state type=%d", visualStateType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIVisualStateType <= QString: */
+template<> UIVisualStateType fromInternalString<UIVisualStateType>(const QString &strVisualStateType)
+{
+ if (strVisualStateType.compare("Normal", Qt::CaseInsensitive) == 0)
+ return UIVisualStateType_Normal;
+ if (strVisualStateType.compare("Fullscreen", Qt::CaseInsensitive) == 0)
+ return UIVisualStateType_Fullscreen;
+ if (strVisualStateType.compare("Seamless", Qt::CaseInsensitive) == 0)
+ return UIVisualStateType_Seamless;
+ if (strVisualStateType.compare("Scale", Qt::CaseInsensitive) == 0)
+ return UIVisualStateType_Scale;
+ if (strVisualStateType.compare("All", Qt::CaseInsensitive) == 0)
+ return UIVisualStateType_All;
+ return UIVisualStateType_Invalid;
+}
+
+/* QString <= DetailsElementType: */
+template<> QString toString(const DetailsElementType &detailsElementType)
+{
+ QString strResult;
+ switch (detailsElementType)
+ {
+ case DetailsElementType_General: strResult = QApplication::translate("UICommon", "General", "DetailsElementType"); break;
+ case DetailsElementType_Preview: strResult = QApplication::translate("UICommon", "Preview", "DetailsElementType"); break;
+ case DetailsElementType_System: strResult = QApplication::translate("UICommon", "System", "DetailsElementType"); break;
+ case DetailsElementType_Display: strResult = QApplication::translate("UICommon", "Display", "DetailsElementType"); break;
+ case DetailsElementType_Storage: strResult = QApplication::translate("UICommon", "Storage", "DetailsElementType"); break;
+ case DetailsElementType_Audio: strResult = QApplication::translate("UICommon", "Audio", "DetailsElementType"); break;
+ case DetailsElementType_Network: strResult = QApplication::translate("UICommon", "Network", "DetailsElementType"); break;
+ case DetailsElementType_Serial: strResult = QApplication::translate("UICommon", "Serial ports", "DetailsElementType"); break;
+ case DetailsElementType_USB: strResult = QApplication::translate("UICommon", "USB", "DetailsElementType"); break;
+ case DetailsElementType_SF: strResult = QApplication::translate("UICommon", "Shared folders", "DetailsElementType"); break;
+ case DetailsElementType_UI: strResult = QApplication::translate("UICommon", "User interface", "DetailsElementType"); break;
+ case DetailsElementType_Description: strResult = QApplication::translate("UICommon", "Description", "DetailsElementType"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element type=%d", detailsElementType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* DetailsElementType <= QString: */
+template<> DetailsElementType fromString<DetailsElementType>(const QString &strDetailsElementType)
+{
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "General", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_General;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "Preview", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Preview;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "System", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_System;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "Display", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Display;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "Storage", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Storage;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "Audio", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Audio;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "Network", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Network;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "Serial ports", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Serial;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "USB", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_USB;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "Shared folders", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_SF;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "User interface", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_UI;
+ if (strDetailsElementType.compare(QApplication::translate("UICommon", "Description", "DetailsElementType"), Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Description;
+ return DetailsElementType_Invalid;
+}
+
+/* QString <= DetailsElementType: */
+template<> QString toInternalString(const DetailsElementType &detailsElementType)
+{
+ QString strResult;
+ switch (detailsElementType)
+ {
+ case DetailsElementType_General: strResult = "general"; break;
+ case DetailsElementType_Preview: strResult = "preview"; break;
+ case DetailsElementType_System: strResult = "system"; break;
+ case DetailsElementType_Display: strResult = "display"; break;
+ case DetailsElementType_Storage: strResult = "storage"; break;
+ case DetailsElementType_Audio: strResult = "audio"; break;
+ case DetailsElementType_Network: strResult = "network"; break;
+ case DetailsElementType_Serial: strResult = "serialPorts"; break;
+ case DetailsElementType_USB: strResult = "usb"; break;
+ case DetailsElementType_SF: strResult = "sharedFolders"; break;
+ case DetailsElementType_UI: strResult = "userInterface"; break;
+ case DetailsElementType_Description: strResult = "description"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for details element type=%d", detailsElementType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* DetailsElementType <= QString: */
+template<> DetailsElementType fromInternalString<DetailsElementType>(const QString &strDetailsElementType)
+{
+ if (strDetailsElementType.compare("general", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_General;
+ if (strDetailsElementType.compare("preview", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Preview;
+ if (strDetailsElementType.compare("system", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_System;
+ if (strDetailsElementType.compare("display", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Display;
+ if (strDetailsElementType.compare("storage", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Storage;
+ if (strDetailsElementType.compare("audio", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Audio;
+ if (strDetailsElementType.compare("network", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Network;
+ if (strDetailsElementType.compare("serialPorts", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Serial;
+ if (strDetailsElementType.compare("usb", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_USB;
+ if (strDetailsElementType.compare("sharedFolders", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_SF;
+ if (strDetailsElementType.compare("userInterface", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_UI;
+ if (strDetailsElementType.compare("description", Qt::CaseInsensitive) == 0)
+ return DetailsElementType_Description;
+ return DetailsElementType_Invalid;
+}
+
+/* QIcon <= DetailsElementType: */
+template<> QIcon toIcon(const DetailsElementType &detailsElementType)
+{
+ switch (detailsElementType)
+ {
+ case DetailsElementType_General: return UIIconPool::iconSet(":/machine_16px.png");
+ case DetailsElementType_Preview: return UIIconPool::iconSet(":/machine_16px.png");
+ case DetailsElementType_System: return UIIconPool::iconSet(":/chipset_16px.png");
+ case DetailsElementType_Display: return UIIconPool::iconSet(":/vrdp_16px.png");
+ case DetailsElementType_Storage: return UIIconPool::iconSet(":/hd_16px.png");
+ case DetailsElementType_Audio: return UIIconPool::iconSet(":/sound_16px.png");
+ case DetailsElementType_Network: return UIIconPool::iconSet(":/nw_16px.png");
+ case DetailsElementType_Serial: return UIIconPool::iconSet(":/serial_port_16px.png");
+ case DetailsElementType_USB: return UIIconPool::iconSet(":/usb_16px.png");
+ case DetailsElementType_SF: return UIIconPool::iconSet(":/sf_16px.png");
+ case DetailsElementType_UI: return UIIconPool::iconSet(":/interface_16px.png");
+ case DetailsElementType_Description: return UIIconPool::iconSet(":/description_16px.png");
+ default:
+ {
+ AssertMsgFailed(("No icon for details element type=%d", detailsElementType));
+ break;
+ }
+ }
+ return QIcon();
+}
+
+/* QString <= PreviewUpdateIntervalType: */
+template<> QString toInternalString(const PreviewUpdateIntervalType &previewUpdateIntervalType)
+{
+ /* Return corresponding QString representation for passed enum value: */
+ switch (previewUpdateIntervalType)
+ {
+ case PreviewUpdateIntervalType_Disabled: return "disabled";
+ case PreviewUpdateIntervalType_500ms: return "500";
+ case PreviewUpdateIntervalType_1000ms: return "1000";
+ case PreviewUpdateIntervalType_2000ms: return "2000";
+ case PreviewUpdateIntervalType_5000ms: return "5000";
+ case PreviewUpdateIntervalType_10000ms: return "10000";
+ default: AssertMsgFailed(("No text for '%d'", previewUpdateIntervalType)); break;
+ }
+ /* Return QString() by default: */
+ return QString();
+}
+
+/* PreviewUpdateIntervalType <= QString: */
+template<> PreviewUpdateIntervalType fromInternalString<PreviewUpdateIntervalType>(const QString &strPreviewUpdateIntervalType)
+{
+ if (strPreviewUpdateIntervalType.compare("disabled", Qt::CaseInsensitive) == 0)
+ return PreviewUpdateIntervalType_Disabled;
+ if (strPreviewUpdateIntervalType.compare("500", Qt::CaseInsensitive) == 0)
+ return PreviewUpdateIntervalType_500ms;
+ if (strPreviewUpdateIntervalType.compare("1000", Qt::CaseInsensitive) == 0)
+ return PreviewUpdateIntervalType_1000ms;
+ if (strPreviewUpdateIntervalType.compare("2000", Qt::CaseInsensitive) == 0)
+ return PreviewUpdateIntervalType_2000ms;
+ if (strPreviewUpdateIntervalType.compare("5000", Qt::CaseInsensitive) == 0)
+ return PreviewUpdateIntervalType_5000ms;
+ if (strPreviewUpdateIntervalType.compare("10000", Qt::CaseInsensitive) == 0)
+ return PreviewUpdateIntervalType_10000ms;
+ /* 1000ms type for unknown input: */
+ return PreviewUpdateIntervalType_1000ms;
+}
+
+/* int <= PreviewUpdateIntervalType: */
+template<> int toInternalInteger(const PreviewUpdateIntervalType &previewUpdateIntervalType)
+{
+ /* Return corresponding integer representation for passed enum value: */
+ switch (previewUpdateIntervalType)
+ {
+ case PreviewUpdateIntervalType_Disabled: return 0;
+ case PreviewUpdateIntervalType_500ms: return 500;
+ case PreviewUpdateIntervalType_1000ms: return 1000;
+ case PreviewUpdateIntervalType_2000ms: return 2000;
+ case PreviewUpdateIntervalType_5000ms: return 5000;
+ case PreviewUpdateIntervalType_10000ms: return 10000;
+ default: AssertMsgFailed(("No value for '%d'", previewUpdateIntervalType)); break;
+ }
+ /* Return 0 by default: */
+ return 0;
+}
+
+/* PreviewUpdateIntervalType <= int: */
+template<> PreviewUpdateIntervalType fromInternalInteger<PreviewUpdateIntervalType>(const int &iPreviewUpdateIntervalType)
+{
+ /* Add all the enum values into the hash: */
+ QHash<int, PreviewUpdateIntervalType> hash;
+ hash.insert(0, PreviewUpdateIntervalType_Disabled);
+ hash.insert(500, PreviewUpdateIntervalType_500ms);
+ hash.insert(1000, PreviewUpdateIntervalType_1000ms);
+ hash.insert(2000, PreviewUpdateIntervalType_2000ms);
+ hash.insert(5000, PreviewUpdateIntervalType_5000ms);
+ hash.insert(10000, PreviewUpdateIntervalType_10000ms);
+ /* Make sure hash contains incoming integer representation: */
+ if (!hash.contains(iPreviewUpdateIntervalType))
+ AssertMsgFailed(("No value for '%d'", iPreviewUpdateIntervalType));
+ /* Return corresponding enum value for passed integer representation: */
+ return hash.value(iPreviewUpdateIntervalType);
+}
+
+/* QString <= UIDiskEncryptionCipherType: */
+template<> QString toInternalString(const UIDiskEncryptionCipherType &enmDiskEncryptionCipherType)
+{
+ switch (enmDiskEncryptionCipherType)
+ {
+ case UIDiskEncryptionCipherType_XTS256: return "AES-XTS256-PLAIN64";
+ case UIDiskEncryptionCipherType_XTS128: return "AES-XTS128-PLAIN64";
+ default: break;
+ }
+ return QString();
+}
+
+/* UIDiskEncryptionCipherType <= QString: */
+template<> UIDiskEncryptionCipherType fromInternalString<UIDiskEncryptionCipherType>(const QString &strDiskEncryptionCipherType)
+{
+ if (strDiskEncryptionCipherType.compare("AES-XTS256-PLAIN64", Qt::CaseInsensitive) == 0)
+ return UIDiskEncryptionCipherType_XTS256;
+ if (strDiskEncryptionCipherType.compare("AES-XTS128-PLAIN64", Qt::CaseInsensitive) == 0)
+ return UIDiskEncryptionCipherType_XTS128;
+ return UIDiskEncryptionCipherType_Unchanged;
+}
+
+/* QString <= UIDiskEncryptionCipherType: */
+template<> QString toString(const UIDiskEncryptionCipherType &enmDiskEncryptionCipherType)
+{
+ switch (enmDiskEncryptionCipherType)
+ {
+ case UIDiskEncryptionCipherType_XTS256: return "AES-XTS256-PLAIN64";
+ case UIDiskEncryptionCipherType_XTS128: return "AES-XTS128-PLAIN64";
+ default: break;
+ }
+ return QApplication::translate("UICommon", "Leave Unchanged", "cipher type");
+}
+
+/* UIDiskEncryptionCipherType <= QString: */
+template<> UIDiskEncryptionCipherType fromString<UIDiskEncryptionCipherType>(const QString &strDiskEncryptionCipherType)
+{
+ if (strDiskEncryptionCipherType.compare("AES-XTS256-PLAIN64", Qt::CaseInsensitive) == 0)
+ return UIDiskEncryptionCipherType_XTS256;
+ if (strDiskEncryptionCipherType.compare("AES-XTS128-PLAIN64", Qt::CaseInsensitive) == 0)
+ return UIDiskEncryptionCipherType_XTS128;
+ return UIDiskEncryptionCipherType_Unchanged;
+}
+
+/* QString <= GUIFeatureType: */
+template<> QString toInternalString(const GUIFeatureType &guiFeatureType)
+{
+ QString strResult;
+ switch (guiFeatureType)
+ {
+ case GUIFeatureType_NoSelector: strResult = "noSelector"; break;
+#ifdef VBOX_WS_MAC
+ case GUIFeatureType_NoUserElements: strResult = "noUserElements"; break;
+#else
+ case GUIFeatureType_NoMenuBar: strResult = "noMenuBar"; break;
+#endif
+ case GUIFeatureType_NoStatusBar: strResult = "noStatusBar"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for GUI feature type=%d", guiFeatureType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* GUIFeatureType <= QString: */
+template<> GUIFeatureType fromInternalString<GUIFeatureType>(const QString &strGuiFeatureType)
+{
+ if (strGuiFeatureType.compare("noSelector", Qt::CaseInsensitive) == 0)
+ return GUIFeatureType_NoSelector;
+#ifdef VBOX_WS_MAC
+ if (strGuiFeatureType.compare("noUserElements", Qt::CaseInsensitive) == 0)
+ return GUIFeatureType_NoUserElements;
+#else
+ if (strGuiFeatureType.compare("noMenuBar", Qt::CaseInsensitive) == 0)
+ return GUIFeatureType_NoMenuBar;
+#endif
+ if (strGuiFeatureType.compare("noStatusBar", Qt::CaseInsensitive) == 0)
+ return GUIFeatureType_NoStatusBar;
+ return GUIFeatureType_None;
+}
+
+/* QString <= GlobalSettingsPageType: */
+template<> QString toInternalString(const GlobalSettingsPageType &globalSettingsPageType)
+{
+ QString strResult;
+ switch (globalSettingsPageType)
+ {
+ case GlobalSettingsPageType_General: strResult = "General"; break;
+ case GlobalSettingsPageType_Input: strResult = "Input"; break;
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ case GlobalSettingsPageType_Update: strResult = "Update"; break;
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ case GlobalSettingsPageType_Language: strResult = "Language"; break;
+ case GlobalSettingsPageType_Display: strResult = "Display"; break;
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ case GlobalSettingsPageType_Proxy: strResult = "Proxy"; break;
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ case GlobalSettingsPageType_Interface: strResult = "Interface"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for settings page type=%d", globalSettingsPageType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* GlobalSettingsPageType <= QString: */
+template<> GlobalSettingsPageType fromInternalString<GlobalSettingsPageType>(const QString &strGlobalSettingsPageType)
+{
+ if (strGlobalSettingsPageType.compare("General", Qt::CaseInsensitive) == 0)
+ return GlobalSettingsPageType_General;
+ if (strGlobalSettingsPageType.compare("Input", Qt::CaseInsensitive) == 0)
+ return GlobalSettingsPageType_Input;
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ if (strGlobalSettingsPageType.compare("Update", Qt::CaseInsensitive) == 0)
+ return GlobalSettingsPageType_Update;
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ if (strGlobalSettingsPageType.compare("Language", Qt::CaseInsensitive) == 0)
+ return GlobalSettingsPageType_Language;
+ if (strGlobalSettingsPageType.compare("Display", Qt::CaseInsensitive) == 0)
+ return GlobalSettingsPageType_Display;
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ if (strGlobalSettingsPageType.compare("Proxy", Qt::CaseInsensitive) == 0)
+ return GlobalSettingsPageType_Proxy;
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ if (strGlobalSettingsPageType.compare("Interface", Qt::CaseInsensitive) == 0)
+ return GlobalSettingsPageType_Interface;
+ return GlobalSettingsPageType_Invalid;
+}
+
+/* QPixmap <= GlobalSettingsPageType: */
+template<> QPixmap toWarningPixmap(const GlobalSettingsPageType &type)
+{
+ switch (type)
+ {
+ case GlobalSettingsPageType_General: return UIIconPool::pixmap(":/machine_warning_16px.png");
+ case GlobalSettingsPageType_Input: return UIIconPool::pixmap(":/hostkey_warning_16px.png");
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ case GlobalSettingsPageType_Update: return UIIconPool::pixmap(":/refresh_warning_16px.png");
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ case GlobalSettingsPageType_Language: return UIIconPool::pixmap(":/site_warning_16px.png");
+ case GlobalSettingsPageType_Display: return UIIconPool::pixmap(":/vrdp_warning_16px.png");
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ case GlobalSettingsPageType_Proxy: return UIIconPool::pixmap(":/proxy_warning_16px.png");
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ case GlobalSettingsPageType_Interface: return UIIconPool::pixmap(":/interface_warning_16px.png");
+ default: AssertMsgFailed(("No pixmap for %d", type)); break;
+ }
+ return QPixmap();
+}
+
+/* QString <= MachineSettingsPageType: */
+template<> QString toInternalString(const MachineSettingsPageType &machineSettingsPageType)
+{
+ QString strResult;
+ switch (machineSettingsPageType)
+ {
+ case MachineSettingsPageType_General: strResult = "General"; break;
+ case MachineSettingsPageType_System: strResult = "System"; break;
+ case MachineSettingsPageType_Display: strResult = "Display"; break;
+ case MachineSettingsPageType_Storage: strResult = "Storage"; break;
+ case MachineSettingsPageType_Audio: strResult = "Audio"; break;
+ case MachineSettingsPageType_Network: strResult = "Network"; break;
+ case MachineSettingsPageType_Ports: strResult = "Ports"; break;
+ case MachineSettingsPageType_Serial: strResult = "Serial"; break;
+ case MachineSettingsPageType_USB: strResult = "USB"; break;
+ case MachineSettingsPageType_SF: strResult = "SharedFolders"; break;
+ case MachineSettingsPageType_Interface: strResult = "Interface"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for settings page type=%d", machineSettingsPageType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* MachineSettingsPageType <= QString: */
+template<> MachineSettingsPageType fromInternalString<MachineSettingsPageType>(const QString &strMachineSettingsPageType)
+{
+ if (strMachineSettingsPageType.compare("General", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_General;
+ if (strMachineSettingsPageType.compare("System", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_System;
+ if (strMachineSettingsPageType.compare("Display", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_Display;
+ if (strMachineSettingsPageType.compare("Storage", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_Storage;
+ if (strMachineSettingsPageType.compare("Audio", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_Audio;
+ if (strMachineSettingsPageType.compare("Network", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_Network;
+ if (strMachineSettingsPageType.compare("Ports", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_Ports;
+ if (strMachineSettingsPageType.compare("Serial", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_Serial;
+ if (strMachineSettingsPageType.compare("USB", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_USB;
+ if (strMachineSettingsPageType.compare("SharedFolders", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_SF;
+ if (strMachineSettingsPageType.compare("Interface", Qt::CaseInsensitive) == 0)
+ return MachineSettingsPageType_Interface;
+ return MachineSettingsPageType_Invalid;
+}
+
+/* QPixmap <= MachineSettingsPageType: */
+template<> QPixmap toWarningPixmap(const MachineSettingsPageType &type)
+{
+ switch (type)
+ {
+ case MachineSettingsPageType_General: return UIIconPool::pixmap(":/machine_warning_16px.png");
+ case MachineSettingsPageType_System: return UIIconPool::pixmap(":/chipset_warning_16px.png");
+ case MachineSettingsPageType_Display: return UIIconPool::pixmap(":/vrdp_warning_16px.png");
+ case MachineSettingsPageType_Storage: return UIIconPool::pixmap(":/hd_warning_16px.png");
+ case MachineSettingsPageType_Audio: return UIIconPool::pixmap(":/sound_warning_16px.png");
+ case MachineSettingsPageType_Network: return UIIconPool::pixmap(":/nw_warning_16px.png");
+ case MachineSettingsPageType_Ports: return UIIconPool::pixmap(":/serial_port_warning_16px.png");
+ case MachineSettingsPageType_Serial: return UIIconPool::pixmap(":/serial_port_warning_16px.png");
+ case MachineSettingsPageType_USB: return UIIconPool::pixmap(":/usb_warning_16px.png");
+ case MachineSettingsPageType_SF: return UIIconPool::pixmap(":/sf_warning_16px.png");
+ case MachineSettingsPageType_Interface: return UIIconPool::pixmap(":/interface_warning_16px.png");
+ default: AssertMsgFailed(("No pixmap for %d", type)); break;
+ }
+ return QPixmap();
+}
+
+/* QString <= UIRemoteMode: */
+template<> QString toString(const UIRemoteMode &enmMode)
+{
+ QString strResult;
+ switch (enmMode)
+ {
+ case UIRemoteMode_Any: strResult = QApplication::translate("UICommon", "Any", "USB filter remote"); break;
+ case UIRemoteMode_On: strResult = QApplication::translate("UICommon", "Yes", "USB filter remote"); break;
+ case UIRemoteMode_Off: strResult = QApplication::translate("UICommon", "No", "USB filter remote"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for USB filter remote mode=%d", enmMode));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= WizardType: */
+template<> QString toInternalString(const WizardType &wizardType)
+{
+ QString strResult;
+ switch (wizardType)
+ {
+ case WizardType_NewVM: strResult = "NewVM"; break;
+ case WizardType_CloneVM: strResult = "CloneVM"; break;
+ case WizardType_ExportAppliance: strResult = "ExportAppliance"; break;
+ case WizardType_ImportAppliance: strResult = "ImportAppliance"; break;
+ case WizardType_NewCloudVM: strResult = "NewCloudVM"; break;
+ case WizardType_AddCloudVM: strResult = "AddCloudVM"; break;
+ case WizardType_NewVD: strResult = "NewVD"; break;
+ case WizardType_CloneVD: strResult = "CloneVD"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for wizard type=%d", wizardType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* WizardType <= QString: */
+template<> WizardType fromInternalString<WizardType>(const QString &strWizardType)
+{
+ if (strWizardType.compare("NewVM", Qt::CaseInsensitive) == 0)
+ return WizardType_NewVM;
+ if (strWizardType.compare("CloneVM", Qt::CaseInsensitive) == 0)
+ return WizardType_CloneVM;
+ if (strWizardType.compare("ExportAppliance", Qt::CaseInsensitive) == 0)
+ return WizardType_ExportAppliance;
+ if (strWizardType.compare("ImportAppliance", Qt::CaseInsensitive) == 0)
+ return WizardType_ImportAppliance;
+ if (strWizardType.compare("NewCloudVM", Qt::CaseInsensitive) == 0)
+ return WizardType_NewCloudVM;
+ if (strWizardType.compare("AddCloudVM", Qt::CaseInsensitive) == 0)
+ return WizardType_AddCloudVM;
+ if (strWizardType.compare("NewVD", Qt::CaseInsensitive) == 0)
+ return WizardType_NewVD;
+ if (strWizardType.compare("CloneVD", Qt::CaseInsensitive) == 0)
+ return WizardType_CloneVD;
+ return WizardType_Invalid;
+}
+
+/* QString <= IndicatorType: */
+template<> QString toInternalString(const IndicatorType &indicatorType)
+{
+ QString strResult;
+ switch (indicatorType)
+ {
+ case IndicatorType_HardDisks: strResult = "HardDisks"; break;
+ case IndicatorType_OpticalDisks: strResult = "OpticalDisks"; break;
+ case IndicatorType_FloppyDisks: strResult = "FloppyDisks"; break;
+ case IndicatorType_Audio: strResult = "Audio"; break;
+ case IndicatorType_Network: strResult = "Network"; break;
+ case IndicatorType_USB: strResult = "USB"; break;
+ case IndicatorType_SharedFolders: strResult = "SharedFolders"; break;
+ case IndicatorType_Display: strResult = "Display"; break;
+ case IndicatorType_Recording: strResult = "Recording"; break;
+ case IndicatorType_Features: strResult = "Features"; break;
+ case IndicatorType_Mouse: strResult = "Mouse"; break;
+ case IndicatorType_Keyboard: strResult = "Keyboard"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for indicator type=%d", indicatorType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* IndicatorType <= QString: */
+template<> IndicatorType fromInternalString<IndicatorType>(const QString &strIndicatorType)
+{
+ if (strIndicatorType.compare("HardDisks", Qt::CaseInsensitive) == 0)
+ return IndicatorType_HardDisks;
+ if (strIndicatorType.compare("OpticalDisks", Qt::CaseInsensitive) == 0)
+ return IndicatorType_OpticalDisks;
+ if (strIndicatorType.compare("FloppyDisks", Qt::CaseInsensitive) == 0)
+ return IndicatorType_FloppyDisks;
+ if (strIndicatorType.compare("Audio", Qt::CaseInsensitive) == 0)
+ return IndicatorType_Audio;
+ if (strIndicatorType.compare("Network", Qt::CaseInsensitive) == 0)
+ return IndicatorType_Network;
+ if (strIndicatorType.compare("USB", Qt::CaseInsensitive) == 0)
+ return IndicatorType_USB;
+ if (strIndicatorType.compare("SharedFolders", Qt::CaseInsensitive) == 0)
+ return IndicatorType_SharedFolders;
+ if (strIndicatorType.compare("Display", Qt::CaseInsensitive) == 0)
+ return IndicatorType_Display;
+ if (strIndicatorType.compare("Recording", Qt::CaseInsensitive) == 0)
+ return IndicatorType_Recording;
+ if (strIndicatorType.compare("Features", Qt::CaseInsensitive) == 0)
+ return IndicatorType_Features;
+ if (strIndicatorType.compare("Mouse", Qt::CaseInsensitive) == 0)
+ return IndicatorType_Mouse;
+ if (strIndicatorType.compare("Keyboard", Qt::CaseInsensitive) == 0)
+ return IndicatorType_Keyboard;
+ return IndicatorType_Invalid;
+}
+
+/* QString <= IndicatorType: */
+template<> QString toString(const IndicatorType &indicatorType)
+{
+ QString strResult;
+ switch (indicatorType)
+ {
+ case IndicatorType_HardDisks: strResult = QApplication::translate("UICommon", "Hard Disks", "IndicatorType"); break;
+ case IndicatorType_OpticalDisks: strResult = QApplication::translate("UICommon", "Optical Disks", "IndicatorType"); break;
+ case IndicatorType_FloppyDisks: strResult = QApplication::translate("UICommon", "Floppy Disks", "IndicatorType"); break;
+ case IndicatorType_Audio: strResult = QApplication::translate("UICommon", "Audio", "IndicatorType"); break;
+ case IndicatorType_Network: strResult = QApplication::translate("UICommon", "Network", "IndicatorType"); break;
+ case IndicatorType_USB: strResult = QApplication::translate("UICommon", "USB", "IndicatorType"); break;
+ case IndicatorType_SharedFolders: strResult = QApplication::translate("UICommon", "Shared Folders", "IndicatorType"); break;
+ case IndicatorType_Display: strResult = QApplication::translate("UICommon", "Display", "IndicatorType"); break;
+ case IndicatorType_Recording: strResult = QApplication::translate("UICommon", "Recording", "IndicatorType"); break;
+ case IndicatorType_Features: strResult = QApplication::translate("UICommon", "Features", "IndicatorType"); break;
+ case IndicatorType_Mouse: strResult = QApplication::translate("UICommon", "Mouse", "IndicatorType"); break;
+ case IndicatorType_Keyboard: strResult = QApplication::translate("UICommon", "Keyboard", "IndicatorType"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for indicator type=%d", indicatorType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QIcon <= IndicatorType: */
+template<> QIcon toIcon(const IndicatorType &indicatorType)
+{
+ switch (indicatorType)
+ {
+ case IndicatorType_HardDisks: return UIIconPool::iconSet(":/hd_16px.png");
+ case IndicatorType_OpticalDisks: return UIIconPool::iconSet(":/cd_16px.png");
+ case IndicatorType_FloppyDisks: return UIIconPool::iconSet(":/fd_16px.png");
+ case IndicatorType_Audio: return UIIconPool::iconSet(":/audio_16px.png");
+ case IndicatorType_Network: return UIIconPool::iconSet(":/nw_16px.png");
+ case IndicatorType_USB: return UIIconPool::iconSet(":/usb_16px.png");
+ case IndicatorType_SharedFolders: return UIIconPool::iconSet(":/sf_16px.png");
+ case IndicatorType_Display: return UIIconPool::iconSet(":/display_software_16px.png");
+ case IndicatorType_Recording: return UIIconPool::iconSet(":/video_capture_16px.png");
+ case IndicatorType_Features: return UIIconPool::iconSet(":/vtx_amdv_16px.png");
+ case IndicatorType_Mouse: return UIIconPool::iconSet(":/mouse_16px.png");
+ case IndicatorType_Keyboard: return UIIconPool::iconSet(":/hostkey_16px.png");
+ default:
+ {
+ AssertMsgFailed(("No icon for indicator type=%d", indicatorType));
+ break;
+ }
+ }
+ return QIcon();
+}
+
+/* QString <= MachineCloseAction: */
+template<> QString toInternalString(const MachineCloseAction &machineCloseAction)
+{
+ QString strResult;
+ switch (machineCloseAction)
+ {
+ case MachineCloseAction_Detach: strResult = "Detach"; break;
+ case MachineCloseAction_SaveState: strResult = "SaveState"; break;
+ case MachineCloseAction_Shutdown: strResult = "Shutdown"; break;
+ case MachineCloseAction_PowerOff: strResult = "PowerOff"; break;
+ case MachineCloseAction_PowerOff_RestoringSnapshot: strResult = "PowerOffRestoringSnapshot"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for indicator type=%d", machineCloseAction));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* MachineCloseAction <= QString: */
+template<> MachineCloseAction fromInternalString<MachineCloseAction>(const QString &strMachineCloseAction)
+{
+ if (strMachineCloseAction.compare("Detach", Qt::CaseInsensitive) == 0)
+ return MachineCloseAction_Detach;
+ if (strMachineCloseAction.compare("SaveState", Qt::CaseInsensitive) == 0)
+ return MachineCloseAction_SaveState;
+ if (strMachineCloseAction.compare("Shutdown", Qt::CaseInsensitive) == 0)
+ return MachineCloseAction_Shutdown;
+ if (strMachineCloseAction.compare("PowerOff", Qt::CaseInsensitive) == 0)
+ return MachineCloseAction_PowerOff;
+ if (strMachineCloseAction.compare("PowerOffRestoringSnapshot", Qt::CaseInsensitive) == 0)
+ return MachineCloseAction_PowerOff_RestoringSnapshot;
+ return MachineCloseAction_Invalid;
+}
+
+/* QString <= MouseCapturePolicy: */
+template<> QString toInternalString(const MouseCapturePolicy &mouseCapturePolicy)
+{
+ /* Return corresponding QString representation for passed enum value: */
+ switch (mouseCapturePolicy)
+ {
+ case MouseCapturePolicy_Default: return "Default";
+ case MouseCapturePolicy_HostComboOnly: return "HostComboOnly";
+ case MouseCapturePolicy_Disabled: return "Disabled";
+ default: AssertMsgFailed(("No text for '%d'", mouseCapturePolicy)); break;
+ }
+ /* Return QString() by default: */
+ return QString();
+}
+
+/* MouseCapturePolicy <= QString: */
+template<> MouseCapturePolicy fromInternalString<MouseCapturePolicy>(const QString &strMouseCapturePolicy)
+{
+ if (strMouseCapturePolicy.compare("Default", Qt::CaseInsensitive) == 0)
+ return MouseCapturePolicy_Default;
+ if (strMouseCapturePolicy.compare("HostComboOnly", Qt::CaseInsensitive) == 0)
+ return MouseCapturePolicy_HostComboOnly;
+ if (strMouseCapturePolicy.compare("Disabled", Qt::CaseInsensitive) == 0)
+ return MouseCapturePolicy_Disabled;
+ return MouseCapturePolicy_Default;
+}
+
+/* QString <= GuruMeditationHandlerType: */
+template<> QString toInternalString(const GuruMeditationHandlerType &guruMeditationHandlerType)
+{
+ QString strResult;
+ switch (guruMeditationHandlerType)
+ {
+ case GuruMeditationHandlerType_Default: strResult = "Default"; break;
+ case GuruMeditationHandlerType_PowerOff: strResult = "PowerOff"; break;
+ case GuruMeditationHandlerType_Ignore: strResult = "Ignore"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for indicator type=%d", guruMeditationHandlerType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* GuruMeditationHandlerType <= QString: */
+template<> GuruMeditationHandlerType fromInternalString<GuruMeditationHandlerType>(const QString &strGuruMeditationHandlerType)
+{
+ if (strGuruMeditationHandlerType.compare("Default", Qt::CaseInsensitive) == 0)
+ return GuruMeditationHandlerType_Default;
+ if (strGuruMeditationHandlerType.compare("PowerOff", Qt::CaseInsensitive) == 0)
+ return GuruMeditationHandlerType_PowerOff;
+ if (strGuruMeditationHandlerType.compare("Ignore", Qt::CaseInsensitive) == 0)
+ return GuruMeditationHandlerType_Ignore;
+ return GuruMeditationHandlerType_Default;
+}
+
+/* QString <= ScalingOptimizationType: */
+template<> QString toInternalString(const ScalingOptimizationType &optimizationType)
+{
+ QString strResult;
+ switch (optimizationType)
+ {
+ case ScalingOptimizationType_None: strResult = "None"; break;
+ case ScalingOptimizationType_Performance: strResult = "Performance"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for type=%d", optimizationType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* ScalingOptimizationType <= QString: */
+template<> ScalingOptimizationType fromInternalString<ScalingOptimizationType>(const QString &strOptimizationType)
+{
+ if (strOptimizationType.compare("None", Qt::CaseInsensitive) == 0)
+ return ScalingOptimizationType_None;
+ if (strOptimizationType.compare("Performance", Qt::CaseInsensitive) == 0)
+ return ScalingOptimizationType_Performance;
+ return ScalingOptimizationType_None;
+}
+
+#ifndef VBOX_WS_MAC
+
+/* QString <= MiniToolbarAlignment: */
+template<> QString toInternalString(const MiniToolbarAlignment &miniToolbarAlignment)
+{
+ /* Return corresponding QString representation for passed enum value: */
+ switch (miniToolbarAlignment)
+ {
+ case MiniToolbarAlignment_Bottom: return "Bottom";
+ case MiniToolbarAlignment_Top: return "Top";
+ default: AssertMsgFailed(("No text for '%d'", miniToolbarAlignment)); break;
+ }
+ /* Return QString() by default: */
+ return QString();
+}
+
+/* MiniToolbarAlignment <= QString: */
+template<> MiniToolbarAlignment fromInternalString<MiniToolbarAlignment>(const QString &strMiniToolbarAlignment)
+{
+ if (strMiniToolbarAlignment.compare("Bottom", Qt::CaseInsensitive) == 0)
+ return MiniToolbarAlignment_Bottom;
+ if (strMiniToolbarAlignment.compare("Top", Qt::CaseInsensitive) == 0)
+ return MiniToolbarAlignment_Top;
+ return MiniToolbarAlignment_Bottom;
+}
+
+#endif /* !VBOX_WS_MAC */
+
+/* QString <= InformationElementType: */
+template<> QString toString(const InformationElementType &informationElementType)
+{
+ QString strResult;
+ switch (informationElementType)
+ {
+ case InformationElementType_General: strResult = QApplication::translate("UICommon", "General", "InformationElementType"); break;
+ case InformationElementType_Preview: strResult = QApplication::translate("UICommon", "Preview", "InformationElementType"); break;
+ case InformationElementType_System: strResult = QApplication::translate("UICommon", "System", "InformationElementType"); break;
+ case InformationElementType_Display: strResult = QApplication::translate("UICommon", "Display", "InformationElementType"); break;
+ case InformationElementType_Storage: strResult = QApplication::translate("UICommon", "Storage", "InformationElementType"); break;
+ case InformationElementType_Audio: strResult = QApplication::translate("UICommon", "Audio", "InformationElementType"); break;
+ case InformationElementType_Network: strResult = QApplication::translate("UICommon", "Network", "InformationElementType"); break;
+ case InformationElementType_Serial: strResult = QApplication::translate("UICommon", "Serial ports", "InformationElementType"); break;
+ case InformationElementType_USB: strResult = QApplication::translate("UICommon", "USB", "InformationElementType"); break;
+ case InformationElementType_SharedFolders: strResult = QApplication::translate("UICommon", "Shared folders", "InformationElementType"); break;
+ case InformationElementType_UI: strResult = QApplication::translate("UICommon", "User interface", "InformationElementType"); break;
+ case InformationElementType_Description: strResult = QApplication::translate("UICommon", "Description", "InformationElementType"); break;
+ case InformationElementType_RuntimeAttributes: strResult = QApplication::translate("UICommon", "Runtime attributes", "InformationElementType"); break;
+ case InformationElementType_StorageStatistics: strResult = QApplication::translate("UICommon", "Storage statistics", "InformationElementType"); break;
+ case InformationElementType_NetworkStatistics: strResult = QApplication::translate("UICommon", "Network statistics", "InformationElementType"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for information element type=%d", informationElementType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* InformationElementType <= QString: */
+template<> InformationElementType fromString<InformationElementType>(const QString &strInformationElementType)
+{
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "General", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_General;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Preview", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_Preview;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "System", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_System;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Display", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_Display;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Storage", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_Storage;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Audio", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_Audio;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Network", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_Network;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Serial ports", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_Serial;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "USB", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_USB;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Shared folders", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_SharedFolders;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "User interface", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_UI;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Description", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_Description;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Runtime attributes", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_RuntimeAttributes;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Storage statistics", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_StorageStatistics;
+ if (strInformationElementType.compare(QApplication::translate("UICommon", "Network statistics", "InformationElementType"), Qt::CaseInsensitive) == 0)
+ return InformationElementType_NetworkStatistics;
+ return InformationElementType_Invalid;
+}
+
+/* QString <= InformationElementType: */
+template<> QString toInternalString(const InformationElementType &informationElementType)
+{
+ QString strResult;
+ switch (informationElementType)
+ {
+ case InformationElementType_General: strResult = "general"; break;
+ case InformationElementType_Preview: strResult = "preview"; break;
+ case InformationElementType_System: strResult = "system"; break;
+ case InformationElementType_Display: strResult = "display"; break;
+ case InformationElementType_Storage: strResult = "storage"; break;
+ case InformationElementType_Audio: strResult = "audio"; break;
+ case InformationElementType_Network: strResult = "network"; break;
+ case InformationElementType_Serial: strResult = "serialPorts"; break;
+ case InformationElementType_USB: strResult = "usb"; break;
+ case InformationElementType_SharedFolders: strResult = "sharedFolders"; break;
+ case InformationElementType_UI: strResult = "userInterface"; break;
+ case InformationElementType_Description: strResult = "description"; break;
+ case InformationElementType_RuntimeAttributes: strResult = "runtime-attributes"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for information element type=%d", informationElementType));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* InformationElementType <= QString: */
+template<> InformationElementType fromInternalString<InformationElementType>(const QString &strInformationElementType)
+{
+ if (strInformationElementType.compare("general", Qt::CaseInsensitive) == 0)
+ return InformationElementType_General;
+ if (strInformationElementType.compare("preview", Qt::CaseInsensitive) == 0)
+ return InformationElementType_Preview;
+ if (strInformationElementType.compare("system", Qt::CaseInsensitive) == 0)
+ return InformationElementType_System;
+ if (strInformationElementType.compare("display", Qt::CaseInsensitive) == 0)
+ return InformationElementType_Display;
+ if (strInformationElementType.compare("storage", Qt::CaseInsensitive) == 0)
+ return InformationElementType_Storage;
+ if (strInformationElementType.compare("audio", Qt::CaseInsensitive) == 0)
+ return InformationElementType_Audio;
+ if (strInformationElementType.compare("network", Qt::CaseInsensitive) == 0)
+ return InformationElementType_Network;
+ if (strInformationElementType.compare("serialPorts", Qt::CaseInsensitive) == 0)
+ return InformationElementType_Serial;
+ if (strInformationElementType.compare("usb", Qt::CaseInsensitive) == 0)
+ return InformationElementType_USB;
+ if (strInformationElementType.compare("sharedFolders", Qt::CaseInsensitive) == 0)
+ return InformationElementType_SharedFolders;
+ if (strInformationElementType.compare("userInterface", Qt::CaseInsensitive) == 0)
+ return InformationElementType_UI;
+ if (strInformationElementType.compare("description", Qt::CaseInsensitive) == 0)
+ return InformationElementType_Description;
+ if (strInformationElementType.compare("runtime-attributes", Qt::CaseInsensitive) == 0)
+ return InformationElementType_RuntimeAttributes;
+ return InformationElementType_Invalid;
+}
+
+/* QIcon <= InformationElementType: */
+template<> QIcon toIcon(const InformationElementType &informationElementType)
+{
+ switch (informationElementType)
+ {
+ case InformationElementType_General: return UIIconPool::iconSet(":/machine_16px.png");
+ case InformationElementType_Preview: return UIIconPool::iconSet(":/machine_16px.png");
+ case InformationElementType_System: return UIIconPool::iconSet(":/chipset_16px.png");
+ case InformationElementType_Display: return UIIconPool::iconSet(":/vrdp_16px.png");
+ case InformationElementType_Storage: return UIIconPool::iconSet(":/hd_16px.png");
+ case InformationElementType_Audio: return UIIconPool::iconSet(":/sound_16px.png");
+ case InformationElementType_Network: return UIIconPool::iconSet(":/nw_16px.png");
+ case InformationElementType_Serial: return UIIconPool::iconSet(":/serial_port_16px.png");
+ case InformationElementType_USB: return UIIconPool::iconSet(":/usb_16px.png");
+ case InformationElementType_SharedFolders: return UIIconPool::iconSet(":/sf_16px.png");
+ case InformationElementType_UI: return UIIconPool::iconSet(":/interface_16px.png");
+ case InformationElementType_Description: return UIIconPool::iconSet(":/description_16px.png");
+ case InformationElementType_RuntimeAttributes: return UIIconPool::iconSet(":/state_running_16px.png");
+ case InformationElementType_StorageStatistics: return UIIconPool::iconSet(":/hd_16px.png");
+ case InformationElementType_NetworkStatistics: return UIIconPool::iconSet(":/nw_16px.png");
+ default:
+ {
+ AssertMsgFailed(("No icon for information element type=%d", informationElementType));
+ break;
+ }
+ }
+ return QIcon();
+}
+
+/* QString <= MaximumGuestScreenSizePolicy: */
+template<> QString toString(const MaximumGuestScreenSizePolicy &enmMaximumGuestScreenSizePolicy)
+{
+ QString strResult;
+ switch (enmMaximumGuestScreenSizePolicy)
+ {
+ case MaximumGuestScreenSizePolicy_Any: strResult = QApplication::translate("UICommon", "None", "Maximum Guest Screen Size"); break;
+ case MaximumGuestScreenSizePolicy_Fixed: strResult = QApplication::translate("UICommon", "Hint", "Maximum Guest Screen Size"); break;
+ case MaximumGuestScreenSizePolicy_Automatic: strResult = QApplication::translate("UICommon", "Automatic", "Maximum Guest Screen Size"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for maximum guest resolution policy=%d", enmMaximumGuestScreenSizePolicy));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= MaximumGuestScreenSizePolicy: */
+template<> QString toInternalString(const MaximumGuestScreenSizePolicy &enmMaximumGuestScreenSizePolicy)
+{
+ QString strResult;
+ switch (enmMaximumGuestScreenSizePolicy)
+ {
+ case MaximumGuestScreenSizePolicy_Automatic: strResult = ""; break;
+ case MaximumGuestScreenSizePolicy_Any: strResult = "any"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for maximum guest resolution policy=%d", enmMaximumGuestScreenSizePolicy));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* MaximumGuestScreenSizePolicy <= QString: */
+template<> MaximumGuestScreenSizePolicy
+fromInternalString<MaximumGuestScreenSizePolicy>(const QString &strMaximumGuestScreenSizePolicy)
+{
+ if ( strMaximumGuestScreenSizePolicy.isEmpty()
+ || strMaximumGuestScreenSizePolicy.compare("auto", Qt::CaseInsensitive) == 0)
+ return MaximumGuestScreenSizePolicy_Automatic;
+ if (strMaximumGuestScreenSizePolicy.compare("any", Qt::CaseInsensitive) == 0)
+ return MaximumGuestScreenSizePolicy_Any;
+ /* Fixed type for value which can be parsed: */
+ if (QRegularExpression("[1-9]\\d*,[1-9]\\d*").match(strMaximumGuestScreenSizePolicy).hasMatch())
+ return MaximumGuestScreenSizePolicy_Fixed;
+ return MaximumGuestScreenSizePolicy_Any;
+}
+
+/* QString <= UIMediumFormat: */
+template<> QString toString(const UIMediumFormat &enmUIMediumFormat)
+{
+ QString strResult;
+ switch (enmUIMediumFormat)
+ {
+ case UIMediumFormat_VDI: strResult = QApplication::translate("UICommon", "VDI (VirtualBox Disk Image)", "UIMediumFormat"); break;
+ case UIMediumFormat_VMDK: strResult = QApplication::translate("UICommon", "VMDK (Virtual Machine Disk)", "UIMediumFormat"); break;
+ case UIMediumFormat_VHD: strResult = QApplication::translate("UICommon", "VHD (Virtual Hard Disk)", "UIMediumFormat"); break;
+ case UIMediumFormat_Parallels: strResult = QApplication::translate("UICommon", "HDD (Parallels Hard Disk)", "UIMediumFormat"); break;
+ case UIMediumFormat_QED: strResult = QApplication::translate("UICommon", "QED (QEMU enhanced disk)", "UIMediumFormat"); break;
+ case UIMediumFormat_QCOW: strResult = QApplication::translate("UICommon", "QCOW (QEMU Copy-On-Write)", "UIMediumFormat"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for medium format=%d", enmUIMediumFormat));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* QString <= UIMediumFormat: */
+template<> QString toInternalString(const UIMediumFormat &enmUIMediumFormat)
+{
+ QString strResult;
+ switch (enmUIMediumFormat)
+ {
+ case UIMediumFormat_VDI: strResult = "VDI"; break;
+ case UIMediumFormat_VMDK: strResult = "VMDK"; break;
+ case UIMediumFormat_VHD: strResult = "VHD"; break;
+ case UIMediumFormat_Parallels: strResult = "Parallels"; break;
+ case UIMediumFormat_QED: strResult = "QED"; break;
+ case UIMediumFormat_QCOW: strResult = "QCOW"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for medium format=%d", enmUIMediumFormat));
+ break;
+ }
+ }
+ return strResult;
+}
+
+/* UIMediumFormat <= QString: */
+template<> UIMediumFormat fromInternalString<UIMediumFormat>(const QString &strUIMediumFormat)
+{
+ if (strUIMediumFormat.compare("VDI", Qt::CaseInsensitive) == 0)
+ return UIMediumFormat_VDI;
+ if (strUIMediumFormat.compare("VMDK", Qt::CaseInsensitive) == 0)
+ return UIMediumFormat_VMDK;
+ if (strUIMediumFormat.compare("VHD", Qt::CaseInsensitive) == 0)
+ return UIMediumFormat_VHD;
+ if (strUIMediumFormat.compare("Parallels", Qt::CaseInsensitive) == 0)
+ return UIMediumFormat_Parallels;
+ if (strUIMediumFormat.compare("QED", Qt::CaseInsensitive) == 0)
+ return UIMediumFormat_QED;
+ if (strUIMediumFormat.compare("QCOW", Qt::CaseInsensitive) == 0)
+ return UIMediumFormat_QCOW;
+ return UIMediumFormat_VDI;
+}
+
+/* QString <= UISettingsDefs::RecordingMode: */
+template<> QString toString(const UISettingsDefs::RecordingMode &enmRecordingMode)
+{
+ QString strResult;
+ switch (enmRecordingMode)
+ {
+ case UISettingsDefs::RecordingMode_None: strResult = QApplication::translate("UICommon", "None", "UISettingsDefs::RecordingMode"); break;
+ case UISettingsDefs::RecordingMode_VideoAudio: strResult = QApplication::translate("UICommon", "Video/Audio", "UISettingsDefs::RecordingMode"); break;
+ case UISettingsDefs::RecordingMode_VideoOnly: strResult = QApplication::translate("UICommon", "Video Only", "UISettingsDefs::RecordingMode"); break;
+ case UISettingsDefs::RecordingMode_AudioOnly: strResult = QApplication::translate("UICommon", "Audio Only", "UISettingsDefs::RecordingMode"); break;
+ default:
+ {
+ AssertMsgFailed(("No text for recording mode format=%d", enmRecordingMode));
+ break;
+ }
+ }
+ return strResult;
+}
+
+template<> QString toInternalString(const VMActivityOverviewColumn &enmVMActivityOverviewColumn)
+{
+ QString strResult;
+ switch (enmVMActivityOverviewColumn)
+ {
+ case VMActivityOverviewColumn_Name: strResult = "VMName"; break;
+ case VMActivityOverviewColumn_CPUGuestLoad: strResult = "CPUGuestLoad"; break;
+ case VMActivityOverviewColumn_CPUVMMLoad: strResult = "CPUVMMLoad"; break;
+ case VMActivityOverviewColumn_RAMUsedAndTotal: strResult = "RAMUsedAndTotal"; break;
+ case VMActivityOverviewColumn_RAMUsedPercentage: strResult = "RAMUsedPercentage"; break;
+ case VMActivityOverviewColumn_NetworkUpRate: strResult = "NetworkUpRate"; break;
+ case VMActivityOverviewColumn_NetworkDownRate: strResult = "NetworkDownRate"; break;
+ case VMActivityOverviewColumn_NetworkUpTotal: strResult = "NetworkUpTotal"; break;
+ case VMActivityOverviewColumn_NetworkDownTotal: strResult = "NetworkDownTotal"; break;
+ case VMActivityOverviewColumn_DiskIOReadRate: strResult = "DiskIOReadRate"; break;
+ case VMActivityOverviewColumn_DiskIOWriteRate: strResult = "DiskIOWriteRate"; break;
+ case VMActivityOverviewColumn_DiskIOReadTotal: strResult = "DiskIOReadTotal"; break;
+ case VMActivityOverviewColumn_DiskIOWriteTotal: strResult = "DiskIOWriteTotal"; break;
+ case VMActivityOverviewColumn_VMExits: strResult = "VMExits"; break;
+ default:
+ {
+ AssertMsgFailed(("No text for VM Activity Overview Column=%d", enmVMActivityOverviewColumn));
+ break;
+ }
+ }
+ return strResult;
+}
+
+template<> VMActivityOverviewColumn fromInternalString<VMActivityOverviewColumn>(const QString &strVMActivityOverviewColumn)
+{
+ if (strVMActivityOverviewColumn.compare("VMName", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_Name;
+ if (strVMActivityOverviewColumn.compare("CPUGuestLoad", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_CPUGuestLoad;
+ if (strVMActivityOverviewColumn.compare("CPUVMMLoad", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_CPUVMMLoad;
+ if (strVMActivityOverviewColumn.compare("RAMUsedAndTotal", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_RAMUsedAndTotal;
+ if (strVMActivityOverviewColumn.compare("RAMUsedPercentage", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_RAMUsedPercentage;
+ if (strVMActivityOverviewColumn.compare("NetworkUpRate", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_NetworkUpRate;
+ if (strVMActivityOverviewColumn.compare("NetworkDownRate", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_NetworkDownRate;
+ if (strVMActivityOverviewColumn.compare("NetworkUpTotal", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_NetworkUpTotal;
+ if (strVMActivityOverviewColumn.compare("NetworkDownTotal", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_NetworkDownTotal;
+ if (strVMActivityOverviewColumn.compare("DiskIOReadRate", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_DiskIOReadRate;
+ if (strVMActivityOverviewColumn.compare("DiskIOWriteRate", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_DiskIOWriteRate;
+ if (strVMActivityOverviewColumn.compare("DiskIOReadTotal", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_DiskIOReadTotal;
+ if (strVMActivityOverviewColumn.compare("DiskIOWriteTotal", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_DiskIOWriteTotal;
+ if (strVMActivityOverviewColumn.compare("VMExits", Qt::CaseInsensitive) == 0)
+ return VMActivityOverviewColumn_VMExits;
+ return VMActivityOverviewColumn_Max;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensionpackmanager/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/extensionpackmanager/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensionpackmanager/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/extensionpackmanager/UIExtensionPackManager.cpp b/src/VBox/Frontends/VirtualBox/src/extensionpackmanager/UIExtensionPackManager.cpp
new file mode 100644
index 00000000..1fb72d10
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensionpackmanager/UIExtensionPackManager.cpp
@@ -0,0 +1,658 @@
+/* $Id: UIExtensionPackManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIExtensionPackManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QHeaderView>
+#include <QPushButton>
+#include <QTextStream>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "QIToolBar.h"
+#include "QITreeWidget.h"
+#include "UIActionPoolManager.h"
+#include "UICommon.h"
+#include "UIExtension.h"
+#include "UIExtensionPackManager.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+
+/* COM includes: */
+#include "CExtPack.h"
+#include "CExtPackManager.h"
+
+
+/** Extension pack tree-widget column indexes. */
+enum ExtensionPackColumn
+{
+ ExtensionPackColumn_Usable,
+ ExtensionPackColumn_Name,
+ ExtensionPackColumn_Version,
+ ExtensionPackColumn_Max,
+};
+
+
+/** Extension Pack Manager: Extension Pack data structure. */
+struct UIDataExtensionPack
+{
+ /** Constructs data. */
+ UIDataExtensionPack()
+ : m_strName(QString())
+ , m_strDescription(QString())
+ , m_strVersion(QString())
+ , m_uRevision(0)
+ , m_fIsUsable(false)
+ , m_strWhyUnusable(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataExtensionPack &other) const
+ {
+ return true
+ && (m_strName == other.m_strName)
+ && (m_strDescription == other.m_strDescription)
+ && (m_strVersion == other.m_strVersion)
+ && (m_uRevision == other.m_uRevision)
+ && (m_fIsUsable == other.m_fIsUsable)
+ && (m_strWhyUnusable == other.m_strWhyUnusable)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataExtensionPack &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataExtensionPack &other) const { return !equal(other); }
+
+ /** Holds the extension item name. */
+ QString m_strName;
+ /** Holds the extension item description. */
+ QString m_strDescription;
+ /** Holds the extension item version. */
+ QString m_strVersion;
+ /** Holds the extension item revision. */
+ ULONG m_uRevision;
+ /** Holds whether the extension item usable. */
+ bool m_fIsUsable;
+ /** Holds why the extension item is unusable. */
+ QString m_strWhyUnusable;
+};
+
+
+/** Extension Pack Manager tree-widget item. */
+class UIItemExtensionPack : public QITreeWidgetItem, public UIDataExtensionPack
+{
+ Q_OBJECT;
+
+public:
+
+ /** Updates item fields from base-class data. */
+ void updateFields();
+
+ /** Returns item name. */
+ QString name() const { return m_strName; }
+
+protected:
+
+ /** Returns default text. */
+ virtual QString defaultText() const RT_OVERRIDE;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIItemExtensionPack implementation. *
+*********************************************************************************************************************************/
+
+void UIItemExtensionPack::updateFields()
+{
+ /* Icon: */
+ setIcon(ExtensionPackColumn_Usable, UIIconPool::iconSet( m_fIsUsable
+ ? ":/status_check_16px.png"
+ : ":/status_error_16px.png"));
+
+ /* Name: */
+ setText(ExtensionPackColumn_Name, m_strName);
+
+ /* Version, Revision, Edition: */
+ const QString strVersion(m_strVersion.section(QRegularExpression("[-_]"), 0, 0));
+ // WORKAROUND:
+ // for http://qt.gitorious.org/qt/qt/commit/7fc63dd0ff368a637dcd17e692b9d6b26278b538
+ QString strAppend;
+ if (m_strVersion.contains(QRegularExpression("[-_]")))
+ strAppend = m_strVersion.section(QRegularExpression("[-_]"), 1, -1, QString::SectionIncludeLeadingSep);
+ setText(ExtensionPackColumn_Version, QString("%1r%2%3").arg(strVersion).arg(m_uRevision).arg(strAppend));
+
+ /* Tool-tip: */
+ QString strTip = m_strDescription;
+ if (!m_fIsUsable)
+ {
+ strTip += QString("<hr>");
+ strTip += m_strWhyUnusable;
+ }
+ setToolTip(ExtensionPackColumn_Usable, strTip);
+ setToolTip(ExtensionPackColumn_Name, strTip);
+ setToolTip(ExtensionPackColumn_Version, strTip);
+}
+
+QString UIItemExtensionPack::defaultText() const
+{
+ return m_fIsUsable
+ ? QString("%1, %2: %3, %4")
+ .arg(text(1))
+ .arg(parentTree()->headerItem()->text(2)).arg(text(2))
+ .arg(parentTree()->headerItem()->text(0))
+ : QString("%1, %2: %3")
+ .arg(text(1))
+ .arg(parentTree()->headerItem()->text(2)).arg(text(2));
+}
+
+
+/*********************************************************************************************************************************
+* Class UIExtensionPackManagerWidget implementation. *
+*********************************************************************************************************************************/
+
+UIExtensionPackManagerWidget::UIExtensionPackManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar /* = true */, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_pToolBar(0)
+ , m_pTreeWidget(0)
+{
+ prepare();
+}
+
+QMenu *UIExtensionPackManagerWidget::menu() const
+{
+ return m_pActionPool->action(UIActionIndexMN_M_ExtensionWindow)->menu();
+}
+
+void UIExtensionPackManagerWidget::retranslateUi()
+{
+ /* Adjust toolbar: */
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text.
+ if (m_pToolBar)
+ m_pToolBar->updateLayout();
+#endif
+
+ /* Translate tree-widget: */
+ m_pTreeWidget->setHeaderLabels( QStringList()
+ << UIExtensionPackManager::tr("Active", "ext pack")
+ << UIExtensionPackManager::tr("Name")
+ << UIExtensionPackManager::tr("Version"));
+ m_pTreeWidget->setWhatsThis(UIExtensionPackManager::tr("Registered extension packs"));
+}
+
+void UIExtensionPackManagerWidget::sltInstallExtensionPack()
+{
+ /* Show file-open dialog to let user to choose package file.
+ * The default location is the user's Download or Downloads directory
+ * with the user's home directory as a fallback. ExtPacks are downloaded. */
+ QString strBaseFolder = QDir::homePath() + "/Downloads";
+ if (!QDir(strBaseFolder).exists())
+ {
+ strBaseFolder = QDir::homePath() + "/Download";
+ if (!QDir(strBaseFolder).exists())
+ strBaseFolder = QDir::homePath();
+ }
+ const QString strTitle = UIExtensionPackManager::tr("Select an extension package file");
+ QStringList extensions;
+ for (int i = 0; i < VBoxExtPackFileExts.size(); ++i)
+ extensions << QString("*.%1").arg(VBoxExtPackFileExts[i]);
+ const QString strFilter = UIExtensionPackManager::tr("Extension package files (%1)").arg(extensions.join(" "));
+ const QStringList fileNames = QIFileDialog::getOpenFileNames(strBaseFolder, strFilter, window(), strTitle, 0, true, true);
+ QString strFilePath;
+ if (!fileNames.isEmpty())
+ strFilePath = fileNames.at(0);
+
+ /* Install the chosen package: */
+ if (!strFilePath.isEmpty())
+ UIExtension::install(strFilePath, QString(), this, NULL);
+}
+
+void UIExtensionPackManagerWidget::sltUninstallExtensionPack()
+{
+ /* Get current item: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+ UIItemExtensionPack *pItemEP = qobject_cast<UIItemExtensionPack*>(pItem);
+
+ /* Uninstall chosen package: */
+ if (pItemEP)
+ {
+ /* Get name of current package: */
+ const QString strSelectedPackageName = pItemEP->name();
+ /* Ask user about package removing: */
+ if (msgCenter().confirmRemoveExtensionPack(strSelectedPackageName, this))
+ {
+ /* Get VirtualBox for further activities: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+ /* Get Extension Pack Manager for further activities: */
+ CExtPackManager comEPManager = comVBox.GetExtensionPackManager();
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotGetExtensionPackManager(comVBox);
+ else
+ {
+ /* Uninstall the package: */
+ /** @todo Refuse this if any VMs are running. */
+ QString displayInfo;
+#ifdef VBOX_WS_WIN
+ QTextStream stream(&displayInfo);
+ stream.setNumberFlags(QTextStream::ShowBase);
+ stream.setIntegerBase(16);
+ stream << "hwnd=" << winId();
+#endif
+
+ /* Uninstall extension pack: */
+ UINotificationProgressExtensionPackUninstall *pNotification =
+ new UINotificationProgressExtensionPackUninstall(comEPManager,
+ strSelectedPackageName,
+ displayInfo);
+ connect(pNotification, &UINotificationProgressExtensionPackUninstall::sigExtensionPackUninstalled,
+ this, &UIExtensionPackManagerWidget::sltHandleExtensionPackUninstalled);
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+ }
+}
+
+void UIExtensionPackManagerWidget::sltAdjustTreeWidget()
+{
+ /* Get the tree-widget abstract interface: */
+ QAbstractItemView *pItemView = m_pTreeWidget;
+ /* Get the tree-widget header-view: */
+ QHeaderView *pItemHeader = m_pTreeWidget->header();
+
+ /* Calculate the total tree-widget width: */
+ const int iTotal = m_pTreeWidget->viewport()->width();
+ /* Look for a minimum width hints for non-important columns: */
+ const int iMinWidth1 = qMax(pItemView->sizeHintForColumn(ExtensionPackColumn_Usable),
+ pItemHeader->sectionSizeHint(ExtensionPackColumn_Usable));
+ const int iMinWidth2 = qMax(pItemView->sizeHintForColumn(ExtensionPackColumn_Version),
+ pItemHeader->sectionSizeHint(ExtensionPackColumn_Version));
+ /* Propose suitable width hints for non-important columns: */
+ const int iWidth1 = iMinWidth1 < iTotal / ExtensionPackColumn_Max ? iMinWidth1 : iTotal / ExtensionPackColumn_Max;
+ const int iWidth2 = iMinWidth2 < iTotal / ExtensionPackColumn_Max ? iMinWidth2 : iTotal / ExtensionPackColumn_Max;
+ /* Apply the proposal: */
+ m_pTreeWidget->setColumnWidth(ExtensionPackColumn_Usable, iWidth1);
+ m_pTreeWidget->setColumnWidth(ExtensionPackColumn_Version, iWidth2);
+ m_pTreeWidget->setColumnWidth(ExtensionPackColumn_Name, iTotal - iWidth1 - iWidth2);
+}
+
+void UIExtensionPackManagerWidget::sltHandleCurrentItemChange()
+{
+ /* Check current-item type: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->currentItem());
+
+ /* Update actions availability: */
+ m_pActionPool->action(UIActionIndexMN_M_Extension_S_Uninstall)->setEnabled(pItem);
+}
+
+void UIExtensionPackManagerWidget::sltHandleContextMenuRequest(const QPoint &position)
+{
+ /* Check clicked-item type: */
+ QITreeWidgetItem *pItem = QITreeWidgetItem::toItem(m_pTreeWidget->itemAt(position));
+
+ /* Compose temporary context-menu: */
+ QMenu menu;
+ if (pItem)
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Extension_S_Uninstall));
+ else
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Extension_S_Install));
+
+ /* And show it: */
+ menu.exec(m_pTreeWidget->viewport()->mapToGlobal(position));
+}
+
+void UIExtensionPackManagerWidget::sltHandleExtensionPackInstalled(const QString &strName)
+{
+ /* Make sure the name was set: */
+ if (strName.isNull())
+ return;
+
+ /* Look for a list of items matching strName: */
+ const QList<QTreeWidgetItem*> items = m_pTreeWidget->findItems(strName, Qt::MatchCaseSensitive, ExtensionPackColumn_Name);
+ /* Remove first found item from the list if present: */
+ if (!items.isEmpty())
+ delete items.first();
+
+ /* [Re]insert it into the tree: */
+ CExtPackManager comManager = uiCommon().virtualBox().GetExtensionPackManager();
+ const CExtPack comExtensionPack = comManager.Find(strName);
+ if (comExtensionPack.isOk())
+ {
+ /* Load extension pack data: */
+ UIDataExtensionPack extensionPackData;
+ loadExtensionPack(comExtensionPack, extensionPackData);
+ createItemForExtensionPack(extensionPackData, true /* choose item? */);
+ }
+}
+
+void UIExtensionPackManagerWidget::sltHandleExtensionPackUninstalled(const QString &strName)
+{
+ /* Make sure the name was set: */
+ if (strName.isNull())
+ return;
+
+ /* Look for a list of items matching strName: */
+ const QList<QTreeWidgetItem*> items = m_pTreeWidget->findItems(strName, Qt::MatchCaseSensitive, ExtensionPackColumn_Name);
+ AssertReturnVoid(!items.isEmpty());
+ /* Remove first found item from the list: */
+ delete items.first();
+
+ /* Adjust tree-widget: */
+ sltAdjustTreeWidget();
+}
+
+void UIExtensionPackManagerWidget::prepare()
+{
+ /* Prepare self: */
+ uiCommon().setHelpKeyword(this, "ext-pack-manager");
+ connect(&uiCommon(), &UICommon::sigExtensionPackInstalled,
+ this, &UIExtensionPackManagerWidget::sltHandleExtensionPackInstalled);
+
+ /* Prepare stuff: */
+ prepareActions();
+ prepareWidgets();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Load extension packs: */
+ loadExtensionPacks();
+}
+
+void UIExtensionPackManagerWidget::prepareActions()
+{
+ /* First of all, add actions which has smaller shortcut scope: */
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Extension_S_Install));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Extension_S_Uninstall));
+
+ /* Connect actions: */
+ connect(m_pActionPool->action(UIActionIndexMN_M_Extension_S_Install), &QAction::triggered,
+ this, &UIExtensionPackManagerWidget::sltInstallExtensionPack);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Extension_S_Uninstall), &QAction::triggered,
+ this, &UIExtensionPackManagerWidget::sltUninstallExtensionPack);
+}
+
+void UIExtensionPackManagerWidget::prepareWidgets()
+{
+ /* Create main-layout: */
+ new QVBoxLayout(this);
+ if (layout())
+ {
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ layout()->setSpacing(10);
+#else
+ layout()->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ /* Prepare toolbar, if requested: */
+ if (m_fShowToolbar)
+ prepareToolBar();
+
+ /* Prepare tree-widget: */
+ prepareTreeWidget();
+ }
+}
+
+void UIExtensionPackManagerWidget::prepareToolBar()
+{
+ /* Prepare toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ if (m_pToolBar)
+ {
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Extension_S_Install));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Extension_S_Uninstall));
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+ }
+#else
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+#endif
+ }
+}
+
+void UIExtensionPackManagerWidget::prepareTreeWidget()
+{
+ /* Prepare tree-widget: */
+ m_pTreeWidget = new QITreeWidget(this);
+ if (m_pTreeWidget)
+ {
+ m_pTreeWidget->setRootIsDecorated(false);
+ m_pTreeWidget->setAlternatingRowColors(true);
+ m_pTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTreeWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTreeWidget->setColumnCount(ExtensionPackColumn_Max);
+ m_pTreeWidget->setSortingEnabled(true);
+ m_pTreeWidget->sortByColumn(ExtensionPackColumn_Name, Qt::AscendingOrder);
+ m_pTreeWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ connect(m_pTreeWidget, &QITreeWidget::resized,
+ this, &UIExtensionPackManagerWidget::sltAdjustTreeWidget, Qt::QueuedConnection);
+ connect(m_pTreeWidget->header(), &QHeaderView::sectionResized,
+ this, &UIExtensionPackManagerWidget::sltAdjustTreeWidget, Qt::QueuedConnection);
+ connect(m_pTreeWidget, &QITreeWidget::currentItemChanged,
+ this, &UIExtensionPackManagerWidget::sltHandleCurrentItemChange);
+ connect(m_pTreeWidget, &QITreeWidget::customContextMenuRequested,
+ this, &UIExtensionPackManagerWidget::sltHandleContextMenuRequest);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pTreeWidget);
+ }
+}
+
+void UIExtensionPackManagerWidget::loadExtensionPacks()
+{
+ /* Check tree-widget: */
+ if (!m_pTreeWidget)
+ return;
+
+ /* Clear tree first of all: */
+ m_pTreeWidget->clear();
+
+ /* Get VirtualBox for further activities: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+ /* Get Extension Pack Manager for further activities: */
+ const CExtPackManager comEPManager = comVBox.GetExtensionPackManager();
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotGetExtensionPackManager(comVBox);
+ else
+ {
+ /* Get extension packs for further activities: */
+ const QVector<CExtPack> extensionPacks = comEPManager.GetInstalledExtPacks();
+
+ /* Show error message if necessary: */
+ if (!comEPManager.isOk())
+ UINotificationMessage::cannotAcquireExtensionPackManagerParameter(comEPManager);
+ else
+ {
+ /* Iterate through existing extension packs: */
+ foreach (const CExtPack &comExtensionPack, extensionPacks)
+ {
+ /* Skip if we have nothing to populate: */
+ if (comExtensionPack.isNull())
+ continue;
+
+ /* Load extension pack data: */
+ UIDataExtensionPack extensionPackData;
+ loadExtensionPack(comExtensionPack, extensionPackData);
+ createItemForExtensionPack(extensionPackData, false /* choose item? */);
+ }
+
+ /* Choose the 1st item as current if nothing chosen: */
+ if (!m_pTreeWidget->currentItem())
+ m_pTreeWidget->setCurrentItem(m_pTreeWidget->topLevelItem(0));
+ /* Handle current item change in any case: */
+ sltHandleCurrentItemChange();
+ }
+ }
+}
+
+void UIExtensionPackManagerWidget::loadExtensionPack(const CExtPack &comExtensionPack, UIDataExtensionPack &extensionPackData)
+{
+ /* Gather extension pack settings: */
+ if (comExtensionPack.isOk())
+ extensionPackData.m_strName = comExtensionPack.GetName();
+ if (comExtensionPack.isOk())
+ extensionPackData.m_strDescription = comExtensionPack.GetDescription();
+ if (comExtensionPack.isOk())
+ extensionPackData.m_strVersion = comExtensionPack.GetVersion();
+ if (comExtensionPack.isOk())
+ extensionPackData.m_uRevision = comExtensionPack.GetRevision();
+ if (comExtensionPack.isOk())
+ {
+ extensionPackData.m_fIsUsable = comExtensionPack.GetUsable();
+ if (!extensionPackData.m_fIsUsable && comExtensionPack.isOk())
+ extensionPackData.m_strWhyUnusable = comExtensionPack.GetWhyUnusable();
+ }
+
+ /* Show error message if necessary: */
+ if (!comExtensionPack.isOk())
+ UINotificationMessage::cannotAcquireExtensionPackParameter(comExtensionPack);
+}
+
+void UIExtensionPackManagerWidget::createItemForExtensionPack(const UIDataExtensionPack &extensionPackData, bool fChooseItem)
+{
+ /* Prepare new provider item: */
+ UIItemExtensionPack *pItem = new UIItemExtensionPack;
+ if (pItem)
+ {
+ pItem->UIDataExtensionPack::operator=(extensionPackData);
+ pItem->updateFields();
+
+ /* Add item to the tree: */
+ m_pTreeWidget->addTopLevelItem(pItem);
+
+ /* And choose it as current if necessary: */
+ if (fChooseItem)
+ m_pTreeWidget->setCurrentItem(pItem);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIExtensionPackManagerFactory implementation. *
+*********************************************************************************************************************************/
+
+UIExtensionPackManagerFactory::UIExtensionPackManagerFactory(UIActionPool *pActionPool /* = 0 */)
+ : m_pActionPool(pActionPool)
+{
+}
+
+void UIExtensionPackManagerFactory::create(QIManagerDialog *&pDialog, QWidget *pCenterWidget)
+{
+ pDialog = new UIExtensionPackManager(pCenterWidget, m_pActionPool);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIExtensionPackManager implementation. *
+*********************************************************************************************************************************/
+
+UIExtensionPackManager::UIExtensionPackManager(QWidget *pCenterWidget, UIActionPool *pActionPool)
+ : QIWithRetranslateUI<QIManagerDialog>(pCenterWidget)
+ , m_pActionPool(pActionPool)
+{
+}
+
+void UIExtensionPackManager::retranslateUi()
+{
+ /* Translate window title: */
+ setWindowTitle(tr("Extension Pack Manager"));
+
+ /* Translate buttons: */
+ button(ButtonType_Close)->setText(tr("Close"));
+ button(ButtonType_Help)->setText(tr("Help"));
+ button(ButtonType_Close)->setStatusTip(tr("Close dialog"));
+ button(ButtonType_Help)->setStatusTip(tr("Show dialog help"));
+ button(ButtonType_Close)->setShortcut(Qt::Key_Escape);
+ button(ButtonType_Help)->setShortcut(QKeySequence::HelpContents);
+ button(ButtonType_Close)->setToolTip(tr("Close Window (%1)").arg(button(ButtonType_Close)->shortcut().toString()));
+ button(ButtonType_Help)->setToolTip(tr("Show Help (%1)").arg(button(ButtonType_Help)->shortcut().toString()));
+}
+
+void UIExtensionPackManager::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/extension_pack_manager_24px.png", ":/extension_pack_manager_16px.png"));
+#endif
+}
+
+void UIExtensionPackManager::configureCentralWidget()
+{
+ /* Prepare widget: */
+ UIExtensionPackManagerWidget *pWidget = new UIExtensionPackManagerWidget(EmbedTo_Dialog, m_pActionPool, true, this);
+ if (pWidget)
+ {
+ setWidget(pWidget);
+ setWidgetMenu(pWidget->menu());
+#ifdef VBOX_WS_MAC
+ setWidgetToolbar(pWidget->toolbar());
+#endif
+
+ /* Add into layout: */
+ centralWidget()->layout()->addWidget(pWidget);
+ }
+}
+
+void UIExtensionPackManager::finalize()
+{
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIExtensionPackManagerWidget *UIExtensionPackManager::widget()
+{
+ return qobject_cast<UIExtensionPackManagerWidget*>(QIManagerDialog::widget());
+}
+
+
+#include "UIExtensionPackManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/extensionpackmanager/UIExtensionPackManager.h b/src/VBox/Frontends/VirtualBox/src/extensionpackmanager/UIExtensionPackManager.h
new file mode 100644
index 00000000..5b448c0b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensionpackmanager/UIExtensionPackManager.h
@@ -0,0 +1,221 @@
+/* $Id: UIExtensionPackManager.h $ */
+/** @file
+ * VBox Qt GUI - UIExtensionPackManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensionpackmanager_UIExtensionPackManager_h
+#define FEQT_INCLUDED_SRC_extensionpackmanager_UIExtensionPackManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QIToolBar;
+class QITreeWidget;
+class UIActionPool;
+struct UIDataExtensionPack;
+class CExtPack;
+
+
+/** QWidget extension providing GUI with the pane to control extension pack related functionality. */
+class UIExtensionPackManagerWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Extension Pack Manager widget.
+ * @param enmEmbedding Brings the type of widget embedding.
+ * @param pActionPool Brings the action-pool reference.
+ * @param fShowToolbar Brings whether we should create/show toolbar. */
+ UIExtensionPackManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar = true, QWidget *pParent = 0);
+
+ /** Returns the menu. */
+ QMenu *menu() const;
+
+#ifdef VBOX_WS_MAC
+ /** Returns the toolbar. */
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Menu/action stuff.
+ * @{ */
+ /** Handles command to install extension pack. */
+ void sltInstallExtensionPack();
+ /** Handles command to uninstall extension pack. */
+ void sltUninstallExtensionPack();
+ /** @} */
+
+ /** @name Tree-widget stuff.
+ * @{ */
+ /** Handles command to adjust tree-widget. */
+ void sltAdjustTreeWidget();
+
+ /** Handles tree-widget current item change. */
+ void sltHandleCurrentItemChange();
+ /** Handles context-menu request for tree-widget @a position. */
+ void sltHandleContextMenuRequest(const QPoint &position);
+
+ /** Handles signal about extension pack @a strName installed. */
+ void sltHandleExtensionPackInstalled(const QString &strName);
+ /** Handles signal about extension pack @a strName uninstalled. */
+ void sltHandleExtensionPackUninstalled(const QString &strName);
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares actions. */
+ void prepareActions();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares toolbar. */
+ void prepareToolBar();
+ /** Prepares tree-widget. */
+ void prepareTreeWidget();
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+ /** Loads extension pack stuff. */
+ void loadExtensionPacks();
+ /** Loads extention @a comPackage data to passed @a extensionPackData container. */
+ void loadExtensionPack(const CExtPack &comPackage, UIDataExtensionPack &extensionPackData);
+ /** @} */
+
+ /** @name Tree-widget stuff.
+ * @{ */
+ /** Creates a new tree-widget item
+ * on the basis of passed @a extensionPackData, @a fChooseItem if requested. */
+ void createItemForExtensionPack(const UIDataExtensionPack &extensionPackData, bool fChooseItem);
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** Holds whether we should create/show toolbar. */
+ const bool m_fShowToolbar;
+ /** @} */
+
+ /** @name Toolbar and menu variables.
+ * @{ */
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolBar;
+ /** @} */
+
+ /** @name Widget variables.
+ * @{ */
+ /** Holds the tree-widget instance. */
+ QITreeWidget *m_pTreeWidget;
+ /** @} */
+};
+
+
+/** QIManagerDialogFactory extension used as a factory for Extension Pack Manager dialog. */
+class UIExtensionPackManagerFactory : public QIManagerDialogFactory
+{
+public:
+
+ /** Constructs Extension Pack Manager factory acquiring additional arguments.
+ * @param pActionPool Brings the action-pool reference. */
+ UIExtensionPackManagerFactory(UIActionPool *pActionPool = 0);
+
+protected:
+
+ /** Creates derived @a pDialog instance.
+ * @param pCenterWidget Brings the widget reference to center according to. */
+ virtual void create(QIManagerDialog *&pDialog, QWidget *pCenterWidget) RT_OVERRIDE;
+
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+};
+
+
+/** QIManagerDialog extension providing GUI with the dialog to control extension pack related functionality. */
+class UIExtensionPackManager : public QIWithRetranslateUI<QIManagerDialog>
+{
+ Q_OBJECT;
+
+private:
+
+ /** Constructs Extension Pack Manager dialog.
+ * @param pCenterWidget Brings the widget reference to center according to.
+ * @param pActionPool Brings the action-pool reference. */
+ UIExtensionPackManager(QWidget *pCenterWidget, UIActionPool *pActionPool);
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ virtual void configure() RT_OVERRIDE;
+ /** Configures central-widget. */
+ virtual void configureCentralWidget() RT_OVERRIDE;
+ /** Perform final preparations. */
+ virtual void finalize() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Widget stuff.
+ * @{ */
+ /** Returns the widget. */
+ virtual UIExtensionPackManagerWidget *widget() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Action related variables.
+ * @{ */
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** @} */
+
+ /** Allow factory access to private/protected members: */
+ friend class UIExtensionPackManagerFactory;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensionpackmanager_UIExtensionPackManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/extensions/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIAdvancedSlider.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIAdvancedSlider.cpp
new file mode 100644
index 00000000..3f3fde11
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIAdvancedSlider.cpp
@@ -0,0 +1,366 @@
+/* $Id: QIAdvancedSlider.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIAdvancedSlider class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPainter>
+#include <QSlider>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+
+/* Qt includes: */
+#include <QStyleOptionSlider>
+
+/* External includes: */
+#include <math.h>
+
+
+/** QSlider subclass for our private needs. */
+class UIPrivateSlider : public QSlider
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs private-slider passing @a pParent and @a enmOrientation to the base-class. */
+ UIPrivateSlider(Qt::Orientation enmOrientation, QWidget *pParent = 0);
+
+ /** Returns suitable position for @a iValue. */
+ int positionForValue(int iValue) const;
+
+ /** @todo encapsulate below stuff accordingly.. */
+
+ /** Holds the minimum optimal border. */
+ int m_minOpt;
+ /** Holds the maximum optimal border. */
+ int m_maxOpt;
+ /** Holds the minimum warning border. */
+ int m_minWrn;
+ /** Holds the maximum warning border. */
+ int m_maxWrn;
+ /** Holds the minimum error border. */
+ int m_minErr;
+ /** Holds the maximum error border. */
+ int m_maxErr;
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent);
+
+private:
+
+ /** Holds the optimal color. */
+ QColor m_optColor;
+ /** Holds the warning color. */
+ QColor m_wrnColor;
+ /** Holds the error color. */
+ QColor m_errColor;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIPrivateSlider implementation. *
+*********************************************************************************************************************************/
+
+UIPrivateSlider::UIPrivateSlider(Qt::Orientation enmOrientation, QWidget *pParent /* = 0 */)
+ : QSlider(enmOrientation, pParent)
+ , m_minOpt(-1)
+ , m_maxOpt(-1)
+ , m_minWrn(-1)
+ , m_maxWrn(-1)
+ , m_minErr(-1)
+ , m_maxErr(-1)
+ , m_optColor(0x0, 0xff, 0x0, 0x3c)
+ , m_wrnColor(0xff, 0x54, 0x0, 0x3c)
+ , m_errColor(0xff, 0x0, 0x0, 0x3c)
+{
+ /* Make sure ticks *always* positioned below: */
+ setTickPosition(QSlider::TicksBelow);
+}
+
+int UIPrivateSlider::positionForValue(int iValue) const
+{
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ opt.subControls = QStyle::SC_All;
+ int iAvailable = opt.rect.width() - style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
+ return QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, iValue, iAvailable);
+}
+
+void UIPrivateSlider::paintEvent(QPaintEvent *pEvent)
+{
+ QPainter p(this);
+
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ opt.subControls = QStyle::SC_All;
+
+ int iAvailable = opt.rect.width() - style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
+ QSize s = size();
+
+ /* We want to acquire SC_SliderTickmarks sub-control rectangle
+ * and fill it with necessary background colors: */
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // Under MacOS X SC_SliderTickmarks is not fully reliable
+ // source of the information we need, providing us with incorrect width.
+ // So we have to calculate tickmarks rectangle ourself.
+ QRect ticks = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderTickmarks, this);
+ ticks.setRect((s.width() - iAvailable) / 2, s.height() - ticks.y(), iAvailable, ticks.height());
+#else /* VBOX_WS_MAC */
+ // WORKAROUND:
+ // Under Windows SC_SliderTickmarks is fully unreliable
+ // source of the information we need, providing us with empty rectangle.
+ // Under X11 SC_SliderTickmarks is not fully reliable
+ // source of the information we need, providing us with different rectangles
+ // (correct or incorrect) under different look&feel styles.
+ // So we have to calculate tickmarks rectangle ourself.
+ QRect ticks = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this) |
+ style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
+ ticks.setRect((s.width() - iAvailable) / 2, ticks.bottom() + 1, iAvailable, s.height() - ticks.bottom() - 1);
+#endif /* VBOX_WS_MAC */
+
+ if ((m_minOpt != -1 &&
+ m_maxOpt != -1) &&
+ m_minOpt != m_maxOpt)
+ {
+ int iPosMinOpt = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_minOpt, iAvailable);
+ int iPosMaxOpt = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_maxOpt, iAvailable);
+ p.fillRect(ticks.x() + iPosMinOpt, ticks.y(), iPosMaxOpt - iPosMinOpt + 1, ticks.height(), m_optColor);
+ }
+ if ((m_minWrn != -1 &&
+ m_maxWrn != -1) &&
+ m_minWrn != m_maxWrn)
+ {
+ int iPosMinWrn = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_minWrn, iAvailable);
+ int iPosMaxWrn = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_maxWrn, iAvailable);
+ p.fillRect(ticks.x() + iPosMinWrn, ticks.y(), iPosMaxWrn - iPosMinWrn + 1, ticks.height(), m_wrnColor);
+ }
+ if ((m_minErr != -1 &&
+ m_maxErr != -1) &&
+ m_minErr != m_maxErr)
+ {
+ int iPosMinErr = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_minErr, iAvailable);
+ int iPosMaxErr = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_maxErr, iAvailable);
+ p.fillRect(ticks.x() + iPosMinErr, ticks.y(), iPosMaxErr - iPosMinErr + 1, ticks.height(), m_errColor);
+ }
+ p.end();
+
+ /* Call to base-class: */
+ QSlider::paintEvent(pEvent);
+}
+
+
+/*********************************************************************************************************************************
+* Class QIAdvancedSlider implementation. *
+*********************************************************************************************************************************/
+
+QIAdvancedSlider::QIAdvancedSlider(QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+{
+ prepare();
+}
+
+QIAdvancedSlider::QIAdvancedSlider(Qt::Orientation enmOrientation, QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+{
+ prepare(enmOrientation);
+}
+
+int QIAdvancedSlider::value() const
+{
+ return m_pSlider->value();
+}
+
+void QIAdvancedSlider::setRange(int iMin, int iMax)
+{
+ m_pSlider->setRange(iMin, iMax);
+}
+
+void QIAdvancedSlider::setMaximum(int iValue)
+{
+ m_pSlider->setMaximum(iValue);
+}
+
+int QIAdvancedSlider::maximum() const
+{
+ return m_pSlider->maximum();
+}
+
+void QIAdvancedSlider::setMinimum(int iValue)
+{
+ m_pSlider->setMinimum(iValue);
+}
+
+int QIAdvancedSlider::minimum() const
+{
+ return m_pSlider->minimum();
+}
+
+void QIAdvancedSlider::setPageStep(int iValue)
+{
+ m_pSlider->setPageStep(iValue);
+}
+
+int QIAdvancedSlider::pageStep() const
+{
+ return m_pSlider->pageStep();
+}
+
+void QIAdvancedSlider::setSingleStep(int iValue)
+{
+ m_pSlider->setSingleStep(iValue);
+}
+
+int QIAdvancedSlider::singelStep() const
+{
+ return m_pSlider->singleStep();
+}
+
+void QIAdvancedSlider::setTickInterval(int iValue)
+{
+ m_pSlider->setTickInterval(iValue);
+}
+
+int QIAdvancedSlider::tickInterval() const
+{
+ return m_pSlider->tickInterval();
+}
+
+Qt::Orientation QIAdvancedSlider::orientation() const
+{
+ return m_pSlider->orientation();
+}
+
+void QIAdvancedSlider::setSnappingEnabled(bool fOn)
+{
+ m_fSnappingEnabled = fOn;
+}
+
+bool QIAdvancedSlider::isSnappingEnabled() const
+{
+ return m_fSnappingEnabled;
+}
+
+void QIAdvancedSlider::setOptimalHint(int iMin, int iMax)
+{
+ m_pSlider->m_minOpt = iMin;
+ m_pSlider->m_maxOpt = iMax;
+
+ update();
+}
+
+void QIAdvancedSlider::setWarningHint(int iMin, int iMax)
+{
+ m_pSlider->m_minWrn = iMin;
+ m_pSlider->m_maxWrn = iMax;
+
+ update();
+}
+
+void QIAdvancedSlider::setErrorHint(int iMin, int iMax)
+{
+ m_pSlider->m_minErr = iMin;
+ m_pSlider->m_maxErr = iMax;
+
+ update();
+}
+
+void QIAdvancedSlider::setToolTip(const QString &strToolTip)
+{
+ m_pSlider->setToolTip(strToolTip);
+}
+
+void QIAdvancedSlider::setOrientation(Qt::Orientation enmOrientation)
+{
+ m_pSlider->setOrientation(enmOrientation);
+}
+
+void QIAdvancedSlider::setValue (int iValue)
+{
+ m_pSlider->setValue(iValue);
+}
+
+void QIAdvancedSlider::sltSliderMoved(int iValue)
+{
+ iValue = snapValue(iValue);
+ m_pSlider->setValue(iValue);
+ emit sliderMoved(iValue);
+}
+
+void QIAdvancedSlider::prepare(Qt::Orientation enmOrientation /* = Qt::Horizontal */)
+{
+ m_fSnappingEnabled = false;
+
+ /* Create layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Configure layout: */
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create private-slider: */
+ m_pSlider = new UIPrivateSlider(enmOrientation, this);
+ if (m_pSlider)
+ {
+ connect(m_pSlider, &UIPrivateSlider::sliderMoved, this, &QIAdvancedSlider::sltSliderMoved);
+ connect(m_pSlider, &UIPrivateSlider::valueChanged, this, &QIAdvancedSlider::valueChanged);
+ connect(m_pSlider, &UIPrivateSlider::sliderPressed, this, &QIAdvancedSlider::sliderPressed);
+ connect(m_pSlider, &UIPrivateSlider::sliderReleased, this, &QIAdvancedSlider::sliderReleased);
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pSlider);
+ }
+ }
+}
+
+int QIAdvancedSlider::snapValue(int iValue)
+{
+ if (m_fSnappingEnabled &&
+ iValue > 2)
+ {
+ float l2 = log((float)iValue)/log(2.0);
+ int iNewVal = (int)pow((float)2, (int)qRound(l2)); /* The value to snap on */
+ int iPos = m_pSlider->positionForValue(iValue); /* Get the relative screen pos for the original value */
+ int iNewPos = m_pSlider->positionForValue(iNewVal); /* Get the relative screen pos for the snap value */
+ if (abs(iNewPos - iPos) < 5) /* 10 pixel snapping range */
+ {
+ iValue = iNewVal;
+ if (iValue > m_pSlider->maximum())
+ iValue = m_pSlider->maximum();
+ else if (iValue < m_pSlider->minimum())
+ iValue = m_pSlider->minimum();
+ }
+ }
+ return iValue;
+}
+
+
+#include "QIAdvancedSlider.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIAdvancedSlider.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIAdvancedSlider.h
new file mode 100644
index 00000000..733c7b27
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIAdvancedSlider.h
@@ -0,0 +1,146 @@
+/* $Id: QIAdvancedSlider.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIAdvancedSlider class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIAdvancedSlider_h
+#define FEQT_INCLUDED_SRC_extensions_QIAdvancedSlider_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class UIPrivateSlider;
+
+/** QWidget extension providing GUI with advanced QSlider functionality. */
+class SHARED_LIBRARY_STUFF QIAdvancedSlider : public QWidget
+{
+ Q_OBJECT;
+ Q_PROPERTY(int value READ value WRITE setValue);
+
+signals:
+
+ /** Notifies about value changed to @a iValue. */
+ void valueChanged(int iValue);
+
+ /** Notifies about slider moved to @a iValue. */
+ void sliderMoved(int iValue);
+
+ /** Notifies about slider pressed. */
+ void sliderPressed();
+ /** Notifies about slider released. */
+ void sliderReleased();
+
+public:
+
+ /** Constructs advanced-slider passing @a pParent to the base-class. */
+ QIAdvancedSlider(QWidget *pParent = 0);
+ /** Constructs advanced-slider passing @a pParent to the base-class.
+ * @param enmOrientation Brings the slider orientation. */
+ QIAdvancedSlider(Qt::Orientation enmOrientation, QWidget *pParent = 0);
+
+ /** Returns the slider value. */
+ int value() const;
+
+ /** Defines the slider range to be from @a iMin to @a iMax. */
+ void setRange(int iMin, int iMax);
+
+ /** Defines the slider @a iMaximum. */
+ void setMaximum(int iMaximum);
+ /** Returns the slider maximum. */
+ int maximum() const;
+
+ /** Defines the slider @a iMinimum. */
+ void setMinimum(int iMinimum);
+ /** Returns the slider minimum. */
+ int minimum() const;
+
+ /** Defines the slider @a iPageStep. */
+ void setPageStep(int iPageStep);
+ /** Returns the slider page step. */
+ int pageStep() const;
+
+ /** Defines the slider @a iSingleStep. */
+ void setSingleStep(int val);
+ /** Returns the slider single step. */
+ int singelStep() const;
+
+ /** Defines the slider @a iTickInterval. */
+ void setTickInterval(int val);
+ /** Returns the slider tick interval. */
+ int tickInterval() const;
+
+ /** Returns the slider orientation. */
+ Qt::Orientation orientation() const;
+
+ /** Defines whether snapping is @a fEnabled. */
+ void setSnappingEnabled(bool fEnabled);
+ /** Returns whether snapping is enabled. */
+ bool isSnappingEnabled() const;
+
+ /** Defines the optimal hint to be from @a iMin to @a iMax. */
+ void setOptimalHint(int iMin, int iMax);
+ /** Defines the warning hint to be from @a iMin to @a iMax. */
+ void setWarningHint(int iMin, int iMax);
+ /** Defines the error hint to be from @a iMin to @a iMax. */
+ void setErrorHint(int iMin, int iMax);
+
+ /** Defines slider @a strToolTip. */
+ void setToolTip(const QString &strToolTip);
+
+public slots:
+
+ /** Defines the slider @a enmOrientation. */
+ void setOrientation(Qt::Orientation enmOrientation);
+
+ /** Defines current slider @a iValue. */
+ void setValue(int iValue);
+
+private slots:
+
+ /** Handles the slider move to @a iValue. */
+ void sltSliderMoved(int iValue);
+
+private:
+
+ /** Prepares all. */
+ void prepare(Qt::Orientation fOrientation = Qt::Horizontal);
+
+ /** Returns snapped value for passed @a iValue. */
+ int snapValue(int iValue);
+
+ /** Holds the private QSlider instance. */
+ UIPrivateSlider *m_pSlider;
+ /** Holds the whether slider snapping is enabled. */
+ bool m_fSnappingEnabled;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIAdvancedSlider_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonPress.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonPress.cpp
new file mode 100644
index 00000000..b0066d07
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonPress.cpp
@@ -0,0 +1,67 @@
+/* $Id: QIArrowButtonPress.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIArrowButtonPress class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QKeyEvent>
+
+/* GUI includes: */
+#include "QIArrowButtonPress.h"
+
+
+QIArrowButtonPress::QIArrowButtonPress(QIArrowButtonPress::ButtonType enmButtonType,
+ QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QIRichToolButton>(pParent)
+ , m_enmButtonType(enmButtonType)
+{
+ /* Retranslate UI: */
+ retranslateUi();
+}
+
+void QIArrowButtonPress::retranslateUi()
+{
+ /* Retranslate: */
+ switch (m_enmButtonType)
+ {
+ case ButtonType_Back: setText(tr("&Back")); break;
+ case ButtonType_Next: setText(tr("&Next")); break;
+ default: break;
+ }
+}
+
+void QIArrowButtonPress::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Handle different keys: */
+ switch (pEvent->key())
+ {
+ /* Animate-click for the Space key: */
+ case Qt::Key_PageUp: if (m_enmButtonType == ButtonType_Next) return animateClick(); break;
+ case Qt::Key_PageDown: if (m_enmButtonType == ButtonType_Back) return animateClick(); break;
+ default: break;
+ }
+ /* Call to base-class: */
+ QIWithRetranslateUI<QIRichToolButton>::keyPressEvent(pEvent);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonPress.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonPress.h
new file mode 100644
index 00000000..66900630
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonPress.h
@@ -0,0 +1,69 @@
+/* $Id: QIArrowButtonPress.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIArrowButtonPress class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIArrowButtonPress_h
+#define FEQT_INCLUDED_SRC_extensions_QIArrowButtonPress_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIRichToolButton.h"
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/** QIRichToolButton extension
+ * representing arrow tool-button with text-label,
+ * can be used as back/next buttons in various places. */
+class SHARED_LIBRARY_STUFF QIArrowButtonPress : public QIWithRetranslateUI<QIRichToolButton>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Button types. */
+ enum ButtonType { ButtonType_Back, ButtonType_Next };
+
+ /** Constructs button passing @a pParent to the base-class.
+ * @param enmButtonType Brings which type of the button it is. */
+ QIArrowButtonPress(ButtonType enmButtonType, QWidget *pParent = 0);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Holds the button-type. */
+ ButtonType m_enmButtonType;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIArrowButtonPress_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonSwitch.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonSwitch.cpp
new file mode 100644
index 00000000..0c57c424
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonSwitch.cpp
@@ -0,0 +1,80 @@
+/* $Id: QIArrowButtonSwitch.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIArrowButtonSwitch class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QKeyEvent>
+
+/* GUI includes: */
+#include "QIArrowButtonSwitch.h"
+
+
+QIArrowButtonSwitch::QIArrowButtonSwitch(QWidget *pParent /* = 0 */)
+ : QIRichToolButton(pParent)
+ , m_fExpanded(false)
+{
+ /* Update icon: */
+ updateIcon();
+}
+
+void QIArrowButtonSwitch::setIcons(const QIcon &iconCollapsed, const QIcon &iconExpanded)
+{
+ /* Assign icons: */
+ m_iconCollapsed = iconCollapsed;
+ m_iconExpanded = iconExpanded;
+ /* Update icon: */
+ updateIcon();
+}
+
+void QIArrowButtonSwitch::setExpanded(bool fExpanded)
+{
+ /* Set button state: */
+ m_fExpanded = fExpanded;
+ /* Update icon: */
+ updateIcon();
+}
+
+void QIArrowButtonSwitch::sltButtonClicked()
+{
+ /* Toggle button state: */
+ m_fExpanded = !m_fExpanded;
+ /* Update icon: */
+ updateIcon();
+}
+
+void QIArrowButtonSwitch::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Handle different keys: */
+ switch (pEvent->key())
+ {
+ /* Animate-click for the Space key: */
+ case Qt::Key_Minus: if (m_fExpanded) return animateClick(); break;
+ case Qt::Key_Plus: if (!m_fExpanded) return animateClick(); break;
+ default: break;
+ }
+ /* Call to base-class: */
+ QIRichToolButton::keyPressEvent(pEvent);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonSwitch.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonSwitch.h
new file mode 100644
index 00000000..311a3810
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowButtonSwitch.h
@@ -0,0 +1,85 @@
+/* $Id: QIArrowButtonSwitch.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIArrowButtonSwitch class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIArrowButtonSwitch_h
+#define FEQT_INCLUDED_SRC_extensions_QIArrowButtonSwitch_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+
+/* GUI includes: */
+#include "QIRichToolButton.h"
+#include "UILibraryDefs.h"
+
+/** QIRichToolButton extension
+ * representing arrow tool-button with text-label,
+ * can be used as collaps/expand switch in various places. */
+class SHARED_LIBRARY_STUFF QIArrowButtonSwitch : public QIRichToolButton
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs button passing @a pParent to the base-class. */
+ QIArrowButtonSwitch(QWidget *pParent = 0);
+
+ /** Defines the @a iconCollapsed and the @a iconExpanded. */
+ void setIcons(const QIcon &iconCollapsed, const QIcon &iconExpanded);
+
+ /** Defines whether the button is @a fExpanded. */
+ void setExpanded(bool fExpanded);
+ /** Returns whether the button is expanded. */
+ bool isExpanded() const { return m_fExpanded; }
+
+protected slots:
+
+ /** Handles button-click. */
+ virtual void sltButtonClicked();
+
+protected:
+
+ /** Handles key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Updates icon according button-state. */
+ void updateIcon() { setIcon(m_fExpanded ? m_iconExpanded : m_iconCollapsed); }
+
+ /** Holds whether the button is expanded. */
+ bool m_fExpanded;
+
+ /** Holds the icon for collapsed button. */
+ QIcon m_iconCollapsed;
+ /** Holds the icon for expanded button. */
+ QIcon m_iconExpanded;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIArrowButtonSwitch_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowSplitter.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowSplitter.cpp
new file mode 100644
index 00000000..04690d03
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowSplitter.cpp
@@ -0,0 +1,378 @@
+/* $Id: QIArrowSplitter.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIArrowSplitter class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QHBoxLayout>
+#include <QStyle>
+#include <QTextEdit>
+
+/* GUI includes: */
+#include "QIArrowSplitter.h"
+#include "QIArrowButtonPress.h"
+#include "QIArrowButtonSwitch.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIIconPool.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QTextEdit extension
+ * taking into account text-document size-hint.
+ * @note Used with QIMessageBox class only.
+ * @todo Should be moved/renamed accordingly. */
+class QIDetailsBrowser : public QTextEdit
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-browser passing @a pParent to the base-class. */
+ QIDetailsBrowser(QWidget *pParent = 0);
+
+ /** Returns minimum size-hint. */
+ QSize minimumSizeHint() const;
+ /** Returns size-hint. */
+ QSize sizeHint() const;
+
+ /** Update scroll-bars. */
+ void updateScrollBars();
+};
+
+
+/*********************************************************************************************************************************
+* Class QIDetailsBrowser implementation. *
+*********************************************************************************************************************************/
+
+QIDetailsBrowser::QIDetailsBrowser(QWidget *pParent /* = 0 */)
+ : QTextEdit(pParent)
+{
+ /* Prepare: */
+ setReadOnly(true);
+}
+
+QSize QIDetailsBrowser::minimumSizeHint() const
+{
+ /* Get document size as the basis: */
+ QSize documentSize = document()->size().toSize();
+ /* But only document ideal-width can advice wise width: */
+ const int iDocumentIdealWidth = (int)document()->idealWidth();
+ /* Moreover we should take document margins into account: */
+ const int iDocumentMargin = (int)document()->documentMargin();
+
+ /* Compose minimum size-hint on the basis of values above: */
+ documentSize.setWidth(iDocumentIdealWidth + iDocumentMargin);
+ documentSize.setHeight(documentSize.height() + iDocumentMargin);
+
+ /* Get 40% of the screen-area to limit the resulting hint: */
+ const QSize screenGeometryDot4 = gpDesktop->screenGeometry(this).size() * .4;
+
+ /* Calculate minimum size-hint which is document-size limited by screen-area: */
+ QSize mSizeHint = documentSize.boundedTo(screenGeometryDot4);
+
+ /* If there is not enough of vertical space: */
+ if (mSizeHint.height() < documentSize.height())
+ {
+ /* We should also take into account vertical scroll-bar extent: */
+ int iExtent = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+ mSizeHint.setWidth(mSizeHint.width() + iExtent);
+ }
+
+ /* Always bound cached hint by 40% of current screen-area: */
+ return mSizeHint;
+}
+
+QSize QIDetailsBrowser::sizeHint() const
+{
+ /* Return minimum size-hint: */
+ return minimumSizeHint();
+}
+
+void QIDetailsBrowser::updateScrollBars()
+{
+ /* Some Qt issue prevents scroll-bars from update.. */
+ Qt::ScrollBarPolicy horizontalPolicy = horizontalScrollBarPolicy();
+ Qt::ScrollBarPolicy verticalPolicy = verticalScrollBarPolicy();
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setHorizontalScrollBarPolicy(horizontalPolicy);
+ setVerticalScrollBarPolicy(verticalPolicy);
+}
+
+
+/*********************************************************************************************************************************
+* Class QIArrowSplitter implementation. *
+*********************************************************************************************************************************/
+
+QIArrowSplitter::QIArrowSplitter(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pMainLayout(0)
+ , m_pSwitchButton(0)
+ , m_pBackButton(0)
+ , m_pNextButton(0)
+ , m_pDetailsBrowser(0)
+ , m_iDetailsIndex(-1)
+{
+ /* Prepare: */
+ prepare();
+}
+
+QSize QIArrowSplitter::minimumSizeHint() const
+{
+ /* Get minimum size-hints: */
+ const QSize switchButtonHint = m_pSwitchButton->minimumSizeHint();
+ const QSize backButtonHint = m_pBackButton->minimumSizeHint();
+ const QSize nextButtonHint = m_pNextButton->minimumSizeHint();
+ const QSize detailsBrowserHint = m_pDetailsBrowser->minimumSizeHint();
+
+ /* Calculate width-hint: */
+ int iWidthHint = 0;
+ iWidthHint += switchButtonHint.width();
+ iWidthHint += 100 /* button spacing */;
+ iWidthHint += backButtonHint.width();
+ iWidthHint += nextButtonHint.width();
+ iWidthHint = qMax(iWidthHint, detailsBrowserHint.width());
+
+ /* Calculate height-hint: */
+ int iHeightHint = 0;
+ iHeightHint = qMax(iHeightHint, switchButtonHint.height());
+ iHeightHint = qMax(iHeightHint, backButtonHint.height());
+ iHeightHint = qMax(iHeightHint, nextButtonHint.height());
+ if (m_pDetailsBrowser->isVisible())
+ iHeightHint += m_pMainLayout->spacing() + detailsBrowserHint.height();
+
+ /* Return result: */
+ return QSize(iWidthHint, iHeightHint);
+}
+
+void QIArrowSplitter::setName(const QString &strName)
+{
+ /* Assign name for the switch-button: */
+ m_pSwitchButton->setText(strName);
+ /* Update size-hints: */
+ sltUpdateSizeHints();
+}
+
+void QIArrowSplitter::setDetails(const QStringPairList &details)
+{
+ /* Assign new details: */
+ m_details = details;
+ /* Reset the details-list index: */
+ m_iDetailsIndex = m_details.isEmpty() ? -1 : 0;
+ /* Update navigation-buttons visibility: */
+ sltUpdateNavigationButtonsVisibility();
+ /* Update details-browser visibility: */
+ sltUpdateDetailsBrowserVisibility();
+ /* Update details: */
+ updateDetails();
+}
+
+void QIArrowSplitter::sltUpdateSizeHints()
+{
+ /* Let parent layout know our size-hint changed: */
+ updateGeometry();
+ /* Notify parent about our size-hint changed: */
+ emit sigSizeHintChange();
+ /* Update details-browser scroll-bars: */
+ m_pDetailsBrowser->updateScrollBars();
+}
+
+void QIArrowSplitter::sltUpdateNavigationButtonsVisibility()
+{
+ /* Depending on switch-button state: */
+ const bool fExpanded = m_pSwitchButton->isExpanded();
+ /* Update back/next button visibility: */
+ m_pBackButton->setVisible(m_details.size() > 1 && fExpanded);
+ m_pNextButton->setVisible(m_details.size() > 1 && fExpanded);
+}
+
+void QIArrowSplitter::sltUpdateDetailsBrowserVisibility()
+{
+ /* Update details-browser visibility according switch-button state: */
+ m_pDetailsBrowser->setVisible(m_details.size() > 0 && m_pSwitchButton->isExpanded());
+ /* Update size-hints: */
+ sltUpdateSizeHints();
+}
+
+void QIArrowSplitter::sltSwitchDetailsPageBack()
+{
+ /* Make sure details-page index feats the bounds: */
+ AssertReturnVoid(m_iDetailsIndex > 0);
+ /* Decrease details-list index: */
+ --m_iDetailsIndex;
+ /* Update details: */
+ updateDetails();
+}
+
+void QIArrowSplitter::sltSwitchDetailsPageNext()
+{
+ /* Make sure details-page index feats the bounds: */
+ AssertReturnVoid(m_iDetailsIndex < m_details.size() - 1);
+ /* Increase details-list index: */
+ ++m_iDetailsIndex;
+ /* Update details: */
+ updateDetails();
+}
+
+void QIArrowSplitter::retranslateUi()
+{
+ /* Update details: */
+ updateDetails();
+}
+
+void QIArrowSplitter::prepare()
+{
+ /* Create main-layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ AssertPtrReturnVoid(m_pMainLayout);
+ {
+ /* Configure main-layout: */
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setSpacing(5);
+#else
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+ /* Create button-layout: */
+ QHBoxLayout *pButtonLayout = new QHBoxLayout;
+ AssertPtrReturnVoid(pButtonLayout);
+ {
+ /* Determine icon metric: */
+ const QStyle *pStyle = QApplication::style();
+ const int iIconMetric = (int)(pStyle->pixelMetric(QStyle::PM_SmallIconSize) * .625);
+ /* Configure button-layout: */
+ pButtonLayout->setContentsMargins(0, 0, 0, 0);
+ pButtonLayout->setSpacing(0);
+ /* Create switch-button: */
+ m_pSwitchButton = new QIArrowButtonSwitch;
+ AssertPtrReturnVoid(m_pSwitchButton);
+ {
+ /* Configure switch-button: */
+ m_pSwitchButton->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pSwitchButton->setIcons(UIIconPool::iconSet(":/arrow_right_10px.png"),
+ UIIconPool::iconSet(":/arrow_down_10px.png"));
+ connect(m_pSwitchButton, &QIArrowButtonSwitch::sigClicked, this, &QIArrowSplitter::sltUpdateNavigationButtonsVisibility);
+ connect(m_pSwitchButton, &QIArrowButtonSwitch::sigClicked, this, &QIArrowSplitter::sltUpdateDetailsBrowserVisibility);
+
+ /* Add switch-button into button-layout: */
+ pButtonLayout->addWidget(m_pSwitchButton);
+ }
+ /* Add stretch: */
+ pButtonLayout->addStretch();
+ /* Create back-button: */
+ m_pBackButton = new QIArrowButtonPress(QIArrowButtonPress::ButtonType_Back);
+ AssertPtrReturnVoid(m_pBackButton);
+ {
+ /* Configure back-button: */
+ m_pBackButton->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pBackButton->setIcon(UIIconPool::iconSet(":/arrow_left_10px.png"));
+ connect(m_pBackButton, &QIArrowButtonPress::sigClicked, this, &QIArrowSplitter::sltSwitchDetailsPageBack);
+
+ /* Add back-button into button-layout: */
+ pButtonLayout->addWidget(m_pBackButton);
+ }
+ /* Create next-button: */
+ m_pNextButton = new QIArrowButtonPress(QIArrowButtonPress::ButtonType_Next);
+ AssertPtrReturnVoid(m_pNextButton);
+ {
+ /* Configure next-button: */
+ m_pNextButton->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pNextButton->setIcon(UIIconPool::iconSet(":/arrow_right_10px.png"));
+ connect(m_pNextButton, &QIArrowButtonPress::sigClicked, this, &QIArrowSplitter::sltSwitchDetailsPageNext);
+
+ /* Add next-button into button-layout: */
+ pButtonLayout->addWidget(m_pNextButton);
+ }
+ /* Add button layout into main-layout: */
+ m_pMainLayout->addLayout(pButtonLayout);
+ /* Update navigation-buttons visibility: */
+ sltUpdateNavigationButtonsVisibility();
+ }
+ /* Create details-browser: */
+ m_pDetailsBrowser = new QIDetailsBrowser;
+ AssertPtrReturnVoid(m_pDetailsBrowser);
+ {
+ /* Add details-browser into main-layout: */
+ m_pMainLayout->addWidget(m_pDetailsBrowser);
+ /* Update details-browser visibility: */
+ sltUpdateDetailsBrowserVisibility();
+ /* Update details: */
+ updateDetails();
+ }
+ }
+
+ /* Apply size-policy finally: */
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+}
+
+void QIArrowSplitter::updateDetails()
+{
+ /* If details are empty: */
+ if (m_details.isEmpty())
+ {
+ /* Make sure details-list index is invalid: */
+ AssertReturnVoid(m_iDetailsIndex == -1);
+
+ /* Reset name: */
+ setName(QString());
+ }
+ /* If details are NOT empty: */
+ else
+ {
+ /* Make sure details-list index feats the bounds: */
+ AssertReturnVoid(m_iDetailsIndex >= 0 && m_iDetailsIndex < m_details.size());
+
+ /* Single page: */
+ if (m_details.size() == 1)
+ {
+ setName(tr("&Details"));
+ m_pBackButton->setEnabled(false);
+ m_pNextButton->setEnabled(false);
+ }
+ /* Multi-paging: */
+ else if (m_details.size() > 1)
+ {
+ setName(tr("&Details (%1 of %2)").arg(m_iDetailsIndex + 1).arg(m_details.size()));
+ m_pBackButton->setEnabled(m_iDetailsIndex > 0);
+ m_pNextButton->setEnabled(m_iDetailsIndex < m_details.size() - 1);
+ }
+
+ /* Update details-browser: */
+ const QString strFirstPart = m_details[m_iDetailsIndex].first;
+ const QString strSecondPart = m_details[m_iDetailsIndex].second;
+ if (strFirstPart.isEmpty())
+ m_pDetailsBrowser->setText(strSecondPart);
+ else
+ m_pDetailsBrowser->setText(QString("%1<br>%2").arg(strFirstPart, strSecondPart));
+ }
+ /* Update size-hints: */
+ sltUpdateSizeHints();
+}
+
+
+#include "QIArrowSplitter.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowSplitter.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowSplitter.h
new file mode 100644
index 00000000..ca7cf06f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowSplitter.h
@@ -0,0 +1,124 @@
+/* $Id: QIArrowSplitter.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIArrowSplitter class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIArrowSplitter_h
+#define FEQT_INCLUDED_SRC_extensions_QIArrowSplitter_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QVBoxLayout;
+class QIArrowButtonSwitch;
+class QIArrowButtonPress;
+class QIDetailsBrowser;
+
+/* Type definitions: */
+typedef QPair<QString, QString> QStringPair;
+typedef QList<QStringPair> QStringPairList;
+
+/** QWidget extension
+ * allowing to toggle visibility for any other child widget. */
+class SHARED_LIBRARY_STUFF QIArrowSplitter : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about size-hint change. */
+ void sigSizeHintChange();
+
+public:
+
+ /** Constructs arrow splitter passing @a pParent to the base-class. */
+ QIArrowSplitter(QWidget *pParent = 0);
+
+ /** Returns minimum size-hint. */
+ QSize minimumSizeHint() const;
+
+ /** Defines the @a strName for the switch-button. */
+ void setName(const QString &strName);
+
+ /** Returns splitter details. */
+ const QStringPairList& details() const { return m_details; }
+ /** Defines splitter @a details. */
+ void setDetails(const QStringPairList &details);
+
+public slots:
+
+ /** Updates size-hints. */
+ void sltUpdateSizeHints();
+
+ /** Updates navigation-buttons visibility. */
+ void sltUpdateNavigationButtonsVisibility();
+ /** Updates details-browser visibility. */
+ void sltUpdateDetailsBrowserVisibility();
+
+ /** Navigates through details-list backward. */
+ void sltSwitchDetailsPageBack();
+ /** Navigates through details-list forward. */
+ void sltSwitchDetailsPageNext();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates details. */
+ void updateDetails();
+
+ /** Holds the main-layout instance. */
+ QVBoxLayout *m_pMainLayout;
+
+ /** Holds the switch-button instance. */
+ QIArrowButtonSwitch *m_pSwitchButton;
+ /** Holds the back-button instance. */
+ QIArrowButtonPress *m_pBackButton;
+ /** Holds the next-button instance. */
+ QIArrowButtonPress *m_pNextButton;
+
+ /** Holds the details-browser. */
+ QIDetailsBrowser *m_pDetailsBrowser;
+ /** Holds details-list. */
+ QStringPairList m_details;
+ /** Holds details-list index. */
+ int m_iDetailsIndex;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIArrowSplitter_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIComboBox.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIComboBox.cpp
new file mode 100644
index 00000000..a721ec39
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIComboBox.cpp
@@ -0,0 +1,432 @@
+/* $Id: QIComboBox.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIComboBox class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QHBoxLayout>
+#include <QLineEdit>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QILineEdit.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QAccessibleWidget extension used as an accessibility interface for QIComboBox. */
+class QIAccessibilityInterfaceForQIComboBox : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QIComboBox accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QIComboBox"))
+ return new QIAccessibilityInterfaceForQIComboBox(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ QIAccessibilityInterfaceForQIComboBox(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::ComboBox)
+ {}
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QIComboBox. */
+ QIComboBox *combo() const { return qobject_cast<QIComboBox*>(widget()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQIComboBox implementation. *
+*********************************************************************************************************************************/
+
+int QIAccessibilityInterfaceForQIComboBox::childCount() const
+{
+ /* Make sure combo still alive: */
+ AssertPtrReturn(combo(), 0);
+
+ /* Return the number of children: */
+ return combo()->subElementCount();
+}
+
+QAccessibleInterface *QIAccessibilityInterfaceForQIComboBox::child(int iIndex) const
+{
+ /* Make sure combo still alive: */
+ AssertPtrReturn(combo(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Return the child with the passed iIndex: */
+ return QAccessible::queryAccessibleInterface(combo()->subElement(iIndex));
+}
+
+int QIAccessibilityInterfaceForQIComboBox::indexOfChild(const QAccessibleInterface *pChild) const
+{
+ /* Search for corresponding child: */
+ for (int i = 0; i < childCount(); ++i)
+ if (child(i) == pChild)
+ return i;
+
+ /* -1 by default: */
+ return -1;
+}
+
+
+
+/*********************************************************************************************************************************
+* Class QIComboBox implementation. *
+*********************************************************************************************************************************/
+
+QIComboBox::QIComboBox(QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_pComboBox(0)
+{
+ /* Prepare all: */
+ prepare();
+}
+
+int QIComboBox::subElementCount() const
+{
+ /* Depending on 'editable' property: */
+ return !isEditable() ? (int)SubElement_Max : (int)SubElementEditable_Max;
+}
+
+QWidget *QIComboBox::subElement(int iIndex) const
+{
+ /* Make sure index is inside the bounds: */
+ AssertReturn(iIndex >= 0 && iIndex < subElementCount(), 0);
+
+ /* For 'non-editable' case: */
+ if (!isEditable())
+ {
+ switch (iIndex)
+ {
+ case SubElement_Selector: return m_pComboBox;
+ default: break;
+ }
+ }
+ /* For 'editable' case: */
+ else
+ {
+ switch (iIndex)
+ {
+ case SubElementEditable_Editor: return lineEdit();
+ case SubElementEditable_Selector: return m_pComboBox;
+ default: break;
+ }
+ }
+
+ /* Null otherwise: */
+ return 0;
+}
+
+QLineEdit *QIComboBox::lineEdit() const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, 0);
+ return m_pComboBox->lineEdit();
+}
+
+QComboBox *QIComboBox::comboBox() const
+{
+ return m_pComboBox;
+}
+
+QAbstractItemView *QIComboBox::view() const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, 0);
+ return m_pComboBox->view();
+}
+
+QSize QIComboBox::iconSize() const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, QSize());
+ return m_pComboBox->iconSize();
+}
+
+QComboBox::InsertPolicy QIComboBox::insertPolicy() const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, QComboBox::NoInsert);
+ return m_pComboBox->insertPolicy();
+}
+
+bool QIComboBox::isEditable() const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, false);
+ return m_pComboBox->isEditable();
+}
+
+int QIComboBox::count() const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, 0);
+ return m_pComboBox->count();
+}
+
+int QIComboBox::currentIndex() const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, -1);
+ return m_pComboBox->currentIndex();
+}
+
+QString QIComboBox::currentText() const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, QString());
+ return m_pComboBox->currentText();
+}
+
+QVariant QIComboBox::currentData(int iRole /* = Qt::UserRole */) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, QVariant());
+ return m_pComboBox->currentData(iRole);
+}
+
+void QIComboBox::addItems(const QStringList &items) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ return m_pComboBox->addItems(items);
+}
+
+void QIComboBox::addItem(const QString &strText, const QVariant &userData /* = QVariant() */) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ return m_pComboBox->addItem(strText, userData);
+}
+
+void QIComboBox::insertItems(int iIndex, const QStringList &items)
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ return m_pComboBox->insertItems(iIndex, items);
+}
+
+void QIComboBox::insertItem(int iIndex, const QString &strText, const QVariant &userData /* = QVariant() */) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ return m_pComboBox->insertItem(iIndex, strText, userData);
+}
+
+void QIComboBox::removeItem(int iIndex) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ return m_pComboBox->removeItem(iIndex);
+}
+
+QVariant QIComboBox::itemData(int iIndex, int iRole /* = Qt::UserRole */) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, QVariant());
+ return m_pComboBox->itemData(iIndex, iRole);
+}
+
+QIcon QIComboBox::itemIcon(int iIndex) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, QIcon());
+ return m_pComboBox->itemIcon(iIndex);
+}
+
+QString QIComboBox::itemText(int iIndex) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, QString());
+ return m_pComboBox->itemText(iIndex);
+}
+
+int QIComboBox::findData(const QVariant &data,
+ int iRole /* = Qt::UserRole */,
+ Qt::MatchFlags flags /* = static_cast<Qt::MatchFlags>(Qt::MatchExactly | Qt::MatchCaseSensitive) */) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, -1);
+ return m_pComboBox->findData(data, iRole, flags);
+}
+
+int QIComboBox::findText(const QString &strText,
+ Qt::MatchFlags flags /* = static_cast<Qt::MatchFlags>(Qt::MatchExactly | Qt::MatchCaseSensitive) */) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, -1);
+ return m_pComboBox->findText(strText, flags);
+}
+
+QComboBox::SizeAdjustPolicy QIComboBox::sizeAdjustPolicy() const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturn(m_pComboBox, QComboBox::AdjustToContentsOnFirstShow);
+ return m_pComboBox->sizeAdjustPolicy();
+}
+
+void QIComboBox::setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy enmPolicy)
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->setSizeAdjustPolicy(enmPolicy);
+}
+
+void QIComboBox::mark(bool fError, const QString &strErrorMessage /* = QString() */)
+{
+ AssertPtrReturnVoid(m_pComboBox);
+ QILineEdit *pLineEdit = isEditable() ? qobject_cast<QILineEdit*>(m_pComboBox->lineEdit()) : 0;
+ if (pLineEdit)
+ pLineEdit->mark(fError, strErrorMessage);
+}
+
+void QIComboBox::insertSeparator(int iIndex)
+{
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->insertSeparator(iIndex);
+}
+
+void QIComboBox::clear()
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->clear();
+}
+
+void QIComboBox::setIconSize(const QSize &size) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->setIconSize(size);
+}
+
+void QIComboBox::setInsertPolicy(QComboBox::InsertPolicy policy) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->setInsertPolicy(policy);
+}
+
+void QIComboBox::setEditable(bool fEditable) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->setEditable(fEditable);
+
+ /* Replace the line-edit so that we can mark errors: */
+ if (isEditable())
+ m_pComboBox->setLineEdit(new QILineEdit);
+}
+
+void QIComboBox::setCurrentIndex(int iIndex) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->setCurrentIndex(iIndex);
+}
+
+void QIComboBox::setItemData(int iIndex, const QVariant &value, int iRole /* = Qt::UserRole */) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->setItemData(iIndex, value, iRole);
+}
+
+void QIComboBox::setItemIcon(int iIndex, const QIcon &icon) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->setItemIcon(iIndex, icon);
+}
+
+void QIComboBox::setItemText(int iIndex, const QString &strText) const
+{
+ /* Redirect to combo-box: */
+ AssertPtrReturnVoid(m_pComboBox);
+ m_pComboBox->setItemText(iIndex, strText);
+}
+
+void QIComboBox::prepare()
+{
+ /* Install QIComboBox accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQIComboBox::pFactory);
+
+ /* Create layout: */
+ QHBoxLayout *pLayout = new QHBoxLayout(this);
+ AssertPtrReturnVoid(pLayout);
+ {
+ /* Configure layout: */
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ pLayout->setSpacing(0);
+
+ /* Create combo-box: */
+ m_pComboBox = new QComboBox;
+ AssertPtrReturnVoid(m_pComboBox);
+ {
+ /* Configure combo-box: */
+ setFocusProxy(m_pComboBox);
+ connect(m_pComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
+ this, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::activated));
+#ifdef VBOX_IS_QT6_OR_LATER /* textActivated was added in 5.14 actually */
+ connect(m_pComboBox, &QComboBox::textActivated,
+ this, &QIComboBox::textActivated);
+#else
+ connect(m_pComboBox, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::activated),
+ this, static_cast<void(QIComboBox::*)(const QString &)>(&QIComboBox::textActivated));
+#endif
+ connect(m_pComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged));
+ connect(m_pComboBox, &QComboBox::currentTextChanged, this, &QIComboBox::currentTextChanged);
+ connect(m_pComboBox, &QComboBox::editTextChanged, this, &QIComboBox::editTextChanged);
+#ifdef VBOX_IS_QT6_OR_LATER /* textHighlighted was added in 5.14 actually */
+ connect(m_pComboBox, &QComboBox::textHighlighted,
+ this, &QIComboBox::textHighlighted);
+#else
+ connect(m_pComboBox, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::highlighted),
+ this, static_cast<void(QIComboBox::*)(const QString &)>(&QIComboBox::textHighlighted));
+#endif
+ /* Add combo-box into layout: */
+ pLayout->addWidget(m_pComboBox);
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIComboBox.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIComboBox.h
new file mode 100644
index 00000000..d74e6618
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIComboBox.h
@@ -0,0 +1,173 @@
+/* $Id: QIComboBox.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIComboBox class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIComboBox_h
+#define FEQT_INCLUDED_SRC_extensions_QIComboBox_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QComboBox>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QWidget subclass extending standard functionality of QComboBox. */
+class SHARED_LIBRARY_STUFF QIComboBox : public QWidget
+{
+ Q_OBJECT;
+
+ /** Enumerates sub-element indexes for basic case. */
+ enum { SubElement_Selector, SubElement_Max };
+ /** Enumerates sub-element indexes for editable case. */
+ enum { SubElementEditable_Editor, SubElementEditable_Selector, SubElementEditable_Max };
+
+signals:
+
+ /** Notifies listeners about user chooses an item with @a iIndex in the combo-box. */
+ void activated(int iIndex);
+ /** Notifies listeners about user chooses an item with @a strText in the combo-box. */
+ void textActivated(const QString &strText);
+
+ /** Notifies listeners about current item changed to item with @a iIndex. */
+ void currentIndexChanged(int iIndex);
+
+ /** Notifies listeners about current combo-box text is changed to @a strText. */
+ void currentTextChanged(const QString &strText);
+ /** Notifies listeners about current combo-box editable text is changed to @a strText. */
+ void editTextChanged(const QString &strText);
+
+ /** Notifies listeners about user highlighted an item with @a iIndex in the popup list-view. */
+ void highlighted(int iIndex);
+ /** Notifies listeners about user highlighted an item with @a strText in the popup list-view. */
+ void textHighlighted(const QString &strText);
+
+public:
+
+ /** Constructs combo-box passing @a pParent to the base-class. */
+ QIComboBox(QWidget *pParent = 0);
+
+ /** Returns sub-element count. */
+ int subElementCount() const;
+ /** Returns sub-element with passed @a iIndex. */
+ QWidget *subElement(int iIndex) const;
+
+ /** Returns the embedded line-editor reference. */
+ QLineEdit *lineEdit() const;
+ /** Returns the embedded list-view reference. */
+ QAbstractItemView *view() const;
+
+ /** Returns the size of the icons shown in the combo-box. */
+ QSize iconSize() const;
+ /** Returns the combo-box insert policy. */
+ QComboBox::InsertPolicy insertPolicy() const;
+ /** Returns whether the combo-box is editable. */
+ bool isEditable() const;
+
+ /** Returns the number of items in the combo-box. */
+ int count() const;
+ /** Returns the index of the current item in the combo-box. */
+ int currentIndex() const;
+ /** Returns the text of the current item in the combo-box. */
+ QString currentText() const;
+ /** Returns the data of the current item in the combo-box. */
+ QVariant currentData(int iRole = Qt::UserRole) const;
+
+ /** Adds the @a items into the combo-box. */
+ void addItems(const QStringList &items) const;
+ /** Adds the @a strText and userData (stored in the Qt::UserRole) into the combo-box. */
+ void addItem(const QString &strText, const QVariant &userData = QVariant()) const;
+ /** Inserts the @a items into the combo-box at the given @a iIndex. */
+ void insertItems(int iIndex, const QStringList &items);
+ /** Inserts the @a strText and userData (stored in the Qt::UserRole) into the combo-box at the given @a iIndex. */
+ void insertItem(int iIndex, const QString &strText, const QVariant &userData = QVariant()) const;
+ /** Removes the item from the combo-box at the given @a iIndex. */
+ void removeItem(int iIndex) const;
+
+ /** Returns the data for the item with the given @a iIndex and specified @a iRole. */
+ QVariant itemData(int iIndex, int iRole = Qt::UserRole) const;
+ /** Returns the icon for the item with the given @a iIndex. */
+ QIcon itemIcon(int iIndex) const;
+ /** Returns the text for the item with the given @a iIndex. */
+ QString itemText(int iIndex) const;
+
+ /** Returns the index of the item containing the given @a data for the given @a iRole; otherwise returns -1.
+ * @param flags Specifies how the items in the combobox are searched. */
+ int findData(const QVariant &data, int iRole = Qt::UserRole,
+ Qt::MatchFlags flags = static_cast<Qt::MatchFlags>(Qt::MatchExactly | Qt::MatchCaseSensitive)) const;
+ /** Returns the index of the item containing the given @a strText; otherwise returns -1.
+ * @param flags Specifies how the items in the combobox are searched. */
+ int findText(const QString &strText, Qt::MatchFlags flags = static_cast<Qt::MatchFlags>(Qt::MatchExactly | Qt::MatchCaseSensitive)) const;
+
+ /** Returns size adjust policy. */
+ QComboBox::SizeAdjustPolicy sizeAdjustPolicy() const;
+ /** Defines size adjust @a enmPolicy. */
+ void setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy enmPolicy);
+ /** Marks the line edit of the combobox. Refer to QILineEdit::mark(..). */
+ void mark(bool fError, const QString &strErrorMessage = QString());
+
+ /** Inserts separator at position with specified @a iIndex. */
+ void insertSeparator(int iIndex);
+
+public slots:
+
+ /** Clears the combobox, removing all items. */
+ void clear();
+
+ /** Defines the @a size of the icons shown in the combo-box. */
+ void setIconSize(const QSize &size) const;
+ /** Defines the combo-box insert @a policy. */
+ void setInsertPolicy(QComboBox::InsertPolicy policy) const;
+ /** Defines whether the combo-box is @a fEditable. */
+ void setEditable(bool fEditable) const;
+
+ /** Defines the @a iIndex of the current item in the combo-box. */
+ void setCurrentIndex(int iIndex) const;
+
+ /** Defines the @a data for the item with the given @a iIndex and specified @a iRole. */
+ void setItemData(int iIndex, const QVariant &value, int iRole = Qt::UserRole) const;
+ /** Defines the @a icon for the item with the given @a iIndex. */
+ void setItemIcon(int iIndex, const QIcon &icon) const;
+ /** Defines the @a strText for the item with the given @a iIndex. */
+ void setItemText(int iIndex, const QString &strText) const;
+
+protected:
+
+ /** Returns the embedded combo-box reference. */
+ QComboBox *comboBox() const;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the original combo-box instance. */
+ QComboBox *m_pComboBox;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIComboBox_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIDialog.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialog.cpp
new file mode 100644
index 00000000..20d43d8d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialog.cpp
@@ -0,0 +1,149 @@
+/* $Id: QIDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QEventLoop>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+
+
+QIDialog::QIDialog(QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ : QDialog(pParent, enmFlags)
+ , m_fPolished(false)
+{
+ /* Do not count that window as important for application,
+ * it will NOT be taken into account when other top-level windows will be closed: */
+ setAttribute(Qt::WA_QuitOnClose, false);
+}
+
+void QIDialog::setVisible(bool fVisible)
+{
+ /* Call to base-class: */
+ QDialog::setVisible(fVisible);
+
+ /* Exit from the event-loop if there is any and
+ * we are changing our state from visible to hidden. */
+ if (m_pEventLoop && !fVisible)
+ m_pEventLoop->exit();
+}
+
+int QIDialog::execute(bool fShow /* = true */, bool fApplicationModal /* = false */)
+{
+ /* Check for the recursive run: */
+ AssertMsgReturn(!m_pEventLoop, ("QIDialog::execute() is called recursively!\n"), QDialog::Rejected);
+
+ /* Reset the result-code: */
+ setResult(QDialog::Rejected);
+
+ /* Should we delete ourself on close in theory? */
+ const bool fOldDeleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
+ /* For the exec() time, set this attribute to 'false': */
+ setAttribute(Qt::WA_DeleteOnClose, false);
+
+ /* Which is the current window-modality? */
+ const Qt::WindowModality oldModality = windowModality();
+ /* For the exec() time, set this attribute to 'window-modal' or 'application-modal': */
+ setWindowModality(!fApplicationModal ? Qt::WindowModal : Qt::ApplicationModal);
+
+ /* Show ourself if requested: */
+ if (fShow)
+ show();
+
+ /* Create a local event-loop: */
+ {
+ QEventLoop eventLoop;
+ m_pEventLoop = &eventLoop;
+
+ /* Guard ourself for the case
+ * we destroyed ourself in our event-loop: */
+ QPointer<QIDialog> guard = this;
+
+ /* Start the blocking event-loop: */
+ eventLoop.exec();
+
+ /* Are we still valid? */
+ if (guard.isNull())
+ return QDialog::Rejected;
+
+ m_pEventLoop = 0;
+ }
+
+ /* Save the result-code early (we can delete ourself on close): */
+ const int iResultCode = result();
+
+ /* Return old modality: */
+ setWindowModality(oldModality);
+
+ /* Reset attribute to previous value: */
+ setAttribute(Qt::WA_DeleteOnClose, fOldDeleteOnClose);
+ /* Delete ourself if we should do that on close: */
+ if (fOldDeleteOnClose)
+ delete this;
+
+ /* Return the result-code: */
+ return iResultCode;
+}
+
+void QIDialog::done(int iResult)
+{
+ /* Call to base-class: */
+ QDialog::done(iResult);
+
+ /* Make sure event-loop exited even if no dialog visibility changed, s.a. QIDialog::setVisible above.
+ * That is necessary to exit event-loop if dialog was executed with fShow == false. */
+ if (m_pEventLoop && m_pEventLoop->isRunning() && !QDialog::isVisible())
+ m_pEventLoop->exit();
+}
+
+void QIDialog::showEvent(QShowEvent *pEvent)
+{
+ /* Make sure we should polish dialog: */
+ if (m_fPolished)
+ return;
+
+ /* Call to polish-event: */
+ polishEvent(pEvent);
+
+ /* Mark dialog as polished: */
+ m_fPolished = true;
+}
+
+void QIDialog::polishEvent(QShowEvent *)
+{
+ /* Make sure layout is polished: */
+ adjustSize();
+#ifdef VBOX_WS_MAC
+ /* And dialog have fixed size: */
+ setFixedSize(size());
+#endif /* VBOX_WS_MAC */
+
+ /* Explicit centering according to our parent: */
+ gpDesktop->centerWidget(this, parentWidget(), false);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIDialog.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialog.h
new file mode 100644
index 00000000..6326acb9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialog.h
@@ -0,0 +1,92 @@
+/* $Id: QIDialog.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIDialog_h
+#define FEQT_INCLUDED_SRC_extensions_QIDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDialog>
+#include <QPointer>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QEventLoop;
+
+/** QDialog extension providing the GUI with
+ * the advanced capabilities like delayed show. */
+class SHARED_LIBRARY_STUFF QIDialog : public QDialog
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs the dialog passing @a pParent and @a enmFlags to the base-class. */
+ QIDialog(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+
+ /** Defines whether the dialog is @a fVisible. */
+ void setVisible(bool fVisible);
+
+public slots:
+
+ /** Shows the dialog as a modal one, blocking until the user closes it.
+ * @param fShow Brings whether the dialog should be shown instantly.
+ * @param fApplicationModal Brings whether the dialog should be application-modal. */
+ virtual int execute(bool fShow = true, bool fApplicationModal = false);
+
+ /** Shows the dialog as a modal one, blocking until the user closes it. */
+ virtual int exec() RT_OVERRIDE { return execute(); }
+
+ /** Closes the dialog and sets its result code to iResult. */
+ virtual void done(int iResult) RT_OVERRIDE;
+
+protected:
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles show @a pEvent sent for the first time. */
+ virtual void polishEvent(QShowEvent *pEvent);
+
+private:
+
+ /** Holds whether the dialog is polished. */
+ bool m_fPolished;
+
+ /** Holds the separate event-loop instance.
+ * @note This event-loop is only used when the dialog being executed via the execute()
+ * functionality, allowing for the delayed show and advanced modality flag. */
+ QPointer<QEventLoop> m_pEventLoop;
+};
+
+/** Safe pointer to the QIDialog class. */
+typedef QPointer<QIDialog> UISafePointerDialog;
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogButtonBox.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogButtonBox.cpp
new file mode 100644
index 00000000..f3d5dd4a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogButtonBox.cpp
@@ -0,0 +1,169 @@
+/* $Id: QIDialogButtonBox.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIDialogButtonBox class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+/* Qt includes: */
+#include <QBoxLayout>
+#include <QEvent>
+#include <QPushButton>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "UISpecialControls.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+QIDialogButtonBox::QIDialogButtonBox(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QDialogButtonBox>(pParent)
+ , m_fDoNotPickDefaultButton(false)
+{
+}
+
+QIDialogButtonBox::QIDialogButtonBox(Qt::Orientation enmOrientation, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QDialogButtonBox>(pParent)
+ , m_fDoNotPickDefaultButton(false)
+{
+ setOrientation(enmOrientation);
+}
+
+QIDialogButtonBox::QIDialogButtonBox(StandardButtons enmButtonTypes, Qt::Orientation enmOrientation, QWidget *pParent)
+ : QIWithRetranslateUI<QDialogButtonBox>(pParent)
+ , m_fDoNotPickDefaultButton(false)
+{
+ setOrientation(enmOrientation);
+ setStandardButtons(enmButtonTypes);
+ retranslateUi();
+}
+
+QPushButton *QIDialogButtonBox::button(StandardButton enmButtonType) const
+{
+ QPushButton *pButton = QDialogButtonBox::button(enmButtonType);
+ if ( !pButton
+ && enmButtonType == QDialogButtonBox::Help)
+ pButton = m_pHelpButton;
+ return pButton;
+}
+
+QPushButton *QIDialogButtonBox::addButton(const QString &strText, ButtonRole enmRole)
+{
+ QPushButton *pButton = QDialogButtonBox::addButton(strText, enmRole);
+ retranslateUi();
+ return pButton;
+}
+
+QPushButton *QIDialogButtonBox::addButton(StandardButton enmButtonType)
+{
+ QPushButton *pButton = QDialogButtonBox::addButton(enmButtonType);
+ retranslateUi();
+ return pButton;
+}
+
+void QIDialogButtonBox::setStandardButtons(StandardButtons enmButtonTypes)
+{
+ QDialogButtonBox::setStandardButtons(enmButtonTypes);
+ retranslateUi();
+}
+
+void QIDialogButtonBox::addExtraWidget(QWidget *pInsertedWidget)
+{
+ QBoxLayout *pLayout = boxLayout();
+ if (pLayout)
+ {
+ int iIndex = findEmptySpace(pLayout);
+ pLayout->insertWidget(iIndex + 1, pInsertedWidget);
+ pLayout->insertStretch(iIndex + 2);
+ }
+}
+
+void QIDialogButtonBox::addExtraLayout(QLayout *pInsertedLayout)
+{
+ QBoxLayout *pLayout = boxLayout();
+ if (pLayout)
+ {
+ int iIndex = findEmptySpace(pLayout);
+ pLayout->insertLayout(iIndex + 1, pInsertedLayout);
+ pLayout->insertStretch(iIndex + 2);
+ }
+}
+
+void QIDialogButtonBox::setDoNotPickDefaultButton(bool fDoNotPickDefaultButton)
+{
+ m_fDoNotPickDefaultButton = fDoNotPickDefaultButton;
+}
+
+void QIDialogButtonBox::retranslateUi()
+{
+ QPushButton *pButton = QDialogButtonBox::button(QDialogButtonBox::Help);
+ if (pButton)
+ {
+ /* Use our very own help button if the user requested for one. */
+ if (!m_pHelpButton)
+ m_pHelpButton = new UIHelpButton;
+ m_pHelpButton->initFrom(pButton);
+ removeButton(pButton);
+ QDialogButtonBox::addButton(m_pHelpButton, QDialogButtonBox::HelpRole);
+ }
+}
+
+void QIDialogButtonBox::showEvent(QShowEvent *pEvent)
+{
+ // WORKAROUND:
+ // QDialogButtonBox has embedded functionality we'd like to avoid.
+ // It auto-picks default button if non is set, based on button role.
+ // Qt documentation states that happens in showEvent, so here we are.
+ // In rare case we'd like to have dialog with no default button at all.
+ if (m_fDoNotPickDefaultButton)
+ {
+ /* Unset all default-buttons in the dialog: */
+ foreach (QPushButton *pButton, findChildren<QPushButton*>())
+ if (pButton->isDefault())
+ pButton->setDefault(false);
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QDialogButtonBox>::showEvent(pEvent);
+}
+
+QBoxLayout *QIDialogButtonBox::boxLayout() const
+{
+ QBoxLayout *pLayout = qobject_cast<QBoxLayout*>(layout());
+ AssertMsg(RT_VALID_PTR(pLayout), ("Layout of the QDialogButtonBox isn't a box layout."));
+ return pLayout;
+}
+
+int QIDialogButtonBox::findEmptySpace(QBoxLayout *pLayout) const
+{
+ /* Search for the first occurrence of QSpacerItem and return the index. */
+ int i = 0;
+ for (; i < pLayout->count(); ++i)
+ {
+ QLayoutItem *pItem = pLayout->itemAt(i);
+ if (pItem && pItem->spacerItem())
+ break;
+ }
+ return i;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogButtonBox.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogButtonBox.h
new file mode 100644
index 00000000..3be4973b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogButtonBox.h
@@ -0,0 +1,106 @@
+/* $Id: QIDialogButtonBox.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIDialogButtonBox class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIDialogButtonBox_h
+#define FEQT_INCLUDED_SRC_extensions_QIDialogButtonBox_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDialogButtonBox>
+#include <QPointer>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QBoxLayout;
+class QPushButton;
+class UIHelpButton;
+
+/** QDialogButtonBox subclass extending standard functionality. */
+class SHARED_LIBRARY_STUFF QIDialogButtonBox : public QIWithRetranslateUI<QDialogButtonBox>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs dialog-button-box passing @a pParent to the base-class. */
+ QIDialogButtonBox(QWidget *pParent = 0);
+ /** Constructs dialog-button-box passing @a pParent to the base-class.
+ * @param enmOrientation Brings the button-box orientation. */
+ QIDialogButtonBox(Qt::Orientation enmOrientation, QWidget *pParent = 0);
+ /** Constructs dialog-button-box passing @a pParent to the base-class.
+ * @param enmButtonTypes Brings the set of button types.
+ * @param enmOrientation Brings the button-box orientation. */
+ QIDialogButtonBox(StandardButtons enmButtonTypes, Qt::Orientation enmOrientation = Qt::Horizontal, QWidget *pParent = 0);
+
+ /** Returns the button of requested @a enmButtonType. */
+ QPushButton *button(StandardButton enmButtonType) const;
+
+ /** Adds button with passed @a strText for specified @a enmRole. */
+ QPushButton *addButton(const QString &strText, ButtonRole enmRole);
+ /** Adds standard button of passed @a enmButtonType. */
+ QPushButton *addButton(StandardButton enmButtonType);
+
+ /** Defines a set of standard @a enmButtonTypes. */
+ void setStandardButtons(StandardButtons enmButtonTypes);
+
+ /** Adds extra @a pWidget. */
+ void addExtraWidget(QWidget *pWidget);
+ /** Adds extra @a pLayout. */
+ void addExtraLayout(QLayout *pLayout);
+
+ /** Defines whether button-box should avoid picking default button. */
+ void setDoNotPickDefaultButton(bool fDoNotPickDefaultButton);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Returns button layout. */
+ QBoxLayout *boxLayout() const;
+
+ /** Searchs for empty @a pLayout space. */
+ int findEmptySpace(QBoxLayout *pLayout) const;
+
+private:
+
+ /** Holds the Help button reference. */
+ QPointer<UIHelpButton> m_pHelpButton;
+
+ /** Holds whether button-box should avoid picking default button. */
+ bool m_fDoNotPickDefaultButton;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIDialogButtonBox_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogContainer.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogContainer.cpp
new file mode 100644
index 00000000..992566c6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogContainer.cpp
@@ -0,0 +1,136 @@
+/* $Id: QIDialogContainer.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIDialogContainer class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QProgressBar>
+#include <QPushButton>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIDialogContainer.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+QIDialogContainer::QIDialogContainer(QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ : QIWithRetranslateUI2<QDialog>(pParent, enmFlags)
+ , m_pLayout(0)
+ , m_pWidget(0)
+ , m_pProgressLabel(0)
+ , m_pProgressBar(0)
+ , m_pButtonBox(0)
+{
+ prepare();
+}
+
+void QIDialogContainer::setWidget(QWidget *pWidget)
+{
+ delete m_pWidget;
+ m_pWidget = pWidget;
+ if (m_pWidget)
+ m_pLayout->addWidget(m_pWidget, 0, 0);
+}
+
+void QIDialogContainer::setProgressBarHidden(bool fHidden)
+{
+ AssertPtrReturnVoid(m_pProgressLabel);
+ AssertPtrReturnVoid(m_pProgressBar);
+ m_pProgressLabel->setHidden(fHidden);
+ m_pProgressBar->setHidden(fHidden);
+}
+
+void QIDialogContainer::setOkButtonEnabled(bool fEnabled)
+{
+ AssertPtrReturnVoid(m_pButtonBox);
+ AssertPtrReturnVoid(m_pButtonBox->button(QDialogButtonBox::Ok));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(fEnabled);
+}
+
+void QIDialogContainer::retranslateUi()
+{
+ m_pProgressLabel->setText(tr("Loading"));
+}
+
+void QIDialogContainer::prepare()
+{
+ /* Prepare layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ /* Prepare dialog button-box: */
+ m_pButtonBox = new QIDialogButtonBox(this);
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted,
+ this, &QDialog::accept);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected,
+ this, &QDialog::reject);
+
+ /* Prepare progress-layout: */
+ QHBoxLayout *pHLayout = new QHBoxLayout;
+ if (pHLayout)
+ {
+ pHLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare progress-label: */
+ m_pProgressLabel = new QLabel(this);
+ if (m_pProgressLabel)
+ {
+ m_pProgressLabel->setHidden(true);
+
+ /* Add into layout: */
+ pHLayout->addWidget(m_pProgressLabel);
+ }
+ /* Prepare progress-bar: */
+ m_pProgressBar = new QProgressBar(this);
+ if (m_pProgressBar)
+ {
+ m_pProgressBar->setHidden(true);
+ m_pProgressBar->setTextVisible(false);
+ m_pProgressBar->setMinimum(0);
+ m_pProgressBar->setMaximum(0);
+
+ /* Add into layout: */
+ pHLayout->addWidget(m_pProgressBar);
+ }
+
+ /* Add into button-box: */
+ m_pButtonBox->addExtraLayout(pHLayout);
+ }
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pButtonBox, 1, 0);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogContainer.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogContainer.h
new file mode 100644
index 00000000..ea4782e6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIDialogContainer.h
@@ -0,0 +1,95 @@
+/* $Id: QIDialogContainer.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIDialogContainer class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIDialogContainer_h
+#define FEQT_INCLUDED_SRC_extensions_QIDialogContainer_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDialog>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QProgressBar;
+class QWidget;
+class QIDialogButtonBox;
+
+/** QDialog sub-class used as executable input container for passed widget.
+ * Should be used as popup or modal dialog wrapping functionality of the passed widget. */
+class SHARED_LIBRARY_STUFF QIDialogContainer : public QIWithRetranslateUI2<QDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs QIDialogContainer passing @a pParent & @a enmFlags to the base-class. */
+ QIDialogContainer(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+
+ /** Defines containing @a pWidget. */
+ void setWidget(QWidget *pWidget);
+
+public slots:
+
+ /** Activates window. */
+ void sltActivateWindow() { activateWindow(); }
+
+ /** Sets progress-bar to be @a fHidden. */
+ void setProgressBarHidden(bool fHidden);
+
+ /** Sets Ok button to be @a fEnabled. */
+ void setOkButtonEnabled(bool fEnabled);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the widget reference. */
+ QWidget *m_pWidget;
+ /** Holds the progress-bar instance. */
+ QLabel *m_pProgressLabel;
+ /** Holds the progress-bar instance. */
+ QProgressBar *m_pProgressBar;
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIDialogContainer_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIFileDialog.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIFileDialog.cpp
new file mode 100644
index 00000000..c91a4013
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIFileDialog.cpp
@@ -0,0 +1,243 @@
+/* $Id: QIFileDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIFileDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#ifdef VBOX_WS_MAC
+# include <QEventLoop>
+#endif
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "UIModalWindowManager.h"
+
+
+QIFileDialog::QIFileDialog(QWidget *pParent, Qt::WindowFlags enmFlags)
+ : QFileDialog(pParent, enmFlags)
+{
+}
+
+/* static */
+QString QIFileDialog::getExistingDirectory(const QString &strDir,
+ QWidget *pParent,
+ const QString &strCaption,
+ bool fDirOnly,
+ bool fResolveSymLinks)
+{
+#ifdef VBOX_WS_MAC
+
+ // WORKAROUND:
+ // After 4.5 exec ignores the Qt::Sheet flag.
+ // See "New Ways of Using Dialogs" in http://doc.trolltech.com/qq/QtQuarterly30.pdf why.
+ // We want the old behavior for file-save dialog. Unfortunately there is a bug in Qt 4.5.x
+ // which result in showing the native & the Qt dialog at the same time.
+ QWidget *pRealParent = windowManager().realParentWindow(pParent);
+ QFileDialog dlg(pRealParent);
+ windowManager().registerNewParent(&dlg, pRealParent);
+ dlg.setWindowTitle(strCaption);
+ dlg.setDirectory(strDir);
+ dlg.setOption(DontResolveSymlinks, !fResolveSymLinks);
+ dlg.setFileMode(QFileDialog::Directory);
+ if (fDirOnly)
+ dlg.setOption(ShowDirsOnly, true);
+
+ QEventLoop eventLoop;
+ QObject::connect(&dlg, &QFileDialog::finished,
+ &eventLoop, &QEventLoop::quit);
+ dlg.open();
+ eventLoop.exec();
+
+ return dlg.result() == QDialog::Accepted ? dlg.selectedFiles().value(0, QString()) : QString();
+
+#else /* !VBOX_WS_MAC */
+
+ QFileDialog::Options o;
+ if (fDirOnly)
+ o |= QFileDialog::ShowDirsOnly;
+ if (!fResolveSymLinks)
+ o |= QFileDialog::DontResolveSymlinks;
+ return QFileDialog::getExistingDirectory(pParent, strCaption, strDir, o);
+
+#endif /* !VBOX_WS_MAC */
+}
+
+/* static */
+QString QIFileDialog::getSaveFileName(const QString &strStartWith,
+ const QString &strFilters,
+ QWidget *pParent,
+ const QString &strCaption,
+ QString *pStrSelectedFilter /* = 0 */,
+ bool fResolveSymLinks /* = true */,
+ bool fConfirmOverwrite /* = false */)
+{
+#ifdef VBOX_WS_MAC
+
+ // WORKAROUND:
+ // After 4.5 exec ignores the Qt::Sheet flag.
+ // See "New Ways of Using Dialogs" in http://doc.trolltech.com/qq/QtQuarterly30.pdf why.
+ // We want the old behavior for file-save dialog. Unfortunately there is a bug in Qt 4.5.x
+ // which result in showing the native & the Qt dialog at the same time.
+ QWidget *pRealParent = windowManager().realParentWindow(pParent);
+ QFileDialog dlg(pRealParent);
+ windowManager().registerNewParent(&dlg, pRealParent);
+ dlg.setWindowTitle(strCaption);
+
+ /* Some predictive algorithm which seems missed in native code. */
+ QDir dir(strStartWith);
+ while (!dir.isRoot() && !dir.exists())
+ dir = QDir(QFileInfo(dir.absolutePath()).absolutePath());
+ const QString strDirectory = dir.absolutePath();
+ if (!strDirectory.isNull())
+ dlg.setDirectory(strDirectory);
+ if (strDirectory != strStartWith)
+ dlg.selectFile(QFileInfo(strStartWith).absoluteFilePath());
+
+ dlg.setNameFilter(strFilters);
+ dlg.setFileMode(QFileDialog::AnyFile);
+ dlg.setAcceptMode(QFileDialog::AcceptSave);
+ if (pStrSelectedFilter)
+ dlg.selectNameFilter(*pStrSelectedFilter);
+ dlg.setOption(DontResolveSymlinks, !fResolveSymLinks);
+ dlg.setOption(DontConfirmOverwrite, !fConfirmOverwrite);
+
+ QEventLoop eventLoop;
+ QObject::connect(&dlg, &QFileDialog::finished,
+ &eventLoop, &QEventLoop::quit);
+ dlg.open();
+ eventLoop.exec();
+
+ return dlg.result() == QDialog::Accepted ? dlg.selectedFiles().value(0, QString()) : QString();
+
+#else /* !VBOX_WS_MAC */
+
+ QFileDialog::Options o;
+ if (!fResolveSymLinks)
+ o |= QFileDialog::DontResolveSymlinks;
+ if (!fConfirmOverwrite)
+ o |= QFileDialog::DontConfirmOverwrite;
+ return QFileDialog::getSaveFileName(pParent, strCaption, strStartWith,
+ strFilters, pStrSelectedFilter, o);
+
+#endif /* !VBOX_WS_MAC */
+}
+
+/* static */
+QString QIFileDialog::getOpenFileName(const QString &strStartWith,
+ const QString &strFilters,
+ QWidget *pParent,
+ const QString &strCaption,
+ QString *pStrSelectedFilter /* = 0 */,
+ bool fResolveSymLinks /* = true */)
+{
+ return getOpenFileNames(strStartWith,
+ strFilters,
+ pParent,
+ strCaption,
+ pStrSelectedFilter,
+ fResolveSymLinks,
+ true /* fSingleFile */).value(0, "");
+}
+
+/* static */
+QStringList QIFileDialog::getOpenFileNames(const QString &strStartWith,
+ const QString &strFilters,
+ QWidget *pParent,
+ const QString &strCaption,
+ QString *pStrSelectedFilter /* = 0 */,
+ bool fResolveSymLinks /* = true */,
+ bool fSingleFile /* = false */)
+{
+#ifdef VBOX_WS_MAC
+
+ // WORKAROUND:
+ // After 4.5 exec ignores the Qt::Sheet flag.
+ // See "New Ways of Using Dialogs" in http://doc.trolltech.com/qq/QtQuarterly30.pdf why.
+ // We want the old behavior for file-save dialog. Unfortunately there is a bug in Qt 4.5.x
+ // which result in showing the native & the Qt dialog at the same time.
+ QWidget *pRealParent = windowManager().realParentWindow(pParent);
+ QFileDialog dlg(pRealParent);
+ windowManager().registerNewParent(&dlg, pRealParent);
+ dlg.setWindowTitle(strCaption);
+
+ /* Some predictive algorithm which seems missed in native code. */
+ QDir dir(strStartWith);
+ while (!dir.isRoot() && !dir.exists())
+ dir = QDir(QFileInfo(dir.absolutePath()).absolutePath());
+ const QString strDirectory = dir.absolutePath();
+ if (!strDirectory.isNull())
+ dlg.setDirectory(strDirectory);
+ if (strDirectory != strStartWith)
+ dlg.selectFile(QFileInfo(strStartWith).absoluteFilePath());
+
+ dlg.setNameFilter(strFilters);
+ if (fSingleFile)
+ dlg.setFileMode(QFileDialog::ExistingFile);
+ else
+ dlg.setFileMode(QFileDialog::ExistingFiles);
+ if (pStrSelectedFilter)
+ dlg.selectNameFilter(*pStrSelectedFilter);
+ dlg.setOption(DontResolveSymlinks, !fResolveSymLinks);
+
+ QEventLoop eventLoop;
+ QObject::connect(&dlg, &QFileDialog::finished,
+ &eventLoop, &QEventLoop::quit);
+ dlg.open();
+ eventLoop.exec();
+
+ return dlg.result() == QDialog::Accepted ? dlg.selectedFiles() : QStringList() << QString();
+
+#else
+
+ QFileDialog::Options o;
+ if (!fResolveSymLinks)
+ o |= QFileDialog::DontResolveSymlinks;
+
+ if (fSingleFile)
+ return QStringList() << QFileDialog::getOpenFileName(pParent, strCaption, strStartWith,
+ strFilters, pStrSelectedFilter, o);
+ else
+ return QFileDialog::getOpenFileNames(pParent, strCaption, strStartWith,
+ strFilters, pStrSelectedFilter, o);
+
+#endif
+}
+
+/* static */
+QString QIFileDialog::getFirstExistingDir(const QString &strStartDir)
+{
+ QString strResult = QString();
+ QDir dir(strStartDir);
+ while (!dir.exists() && !dir.isRoot())
+ {
+ QFileInfo dirInfo(dir.absolutePath());
+ if (dir == QDir(dirInfo.absolutePath()))
+ break;
+ dir = dirInfo.absolutePath();
+ }
+ if (dir.exists() && !dir.isRoot())
+ strResult = dir.absolutePath();
+ return strResult;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIFileDialog.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIFileDialog.h
new file mode 100644
index 00000000..86d053d1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIFileDialog.h
@@ -0,0 +1,103 @@
+/* $Id: QIFileDialog.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIFileDialog class declarations.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIFileDialog_h
+#define FEQT_INCLUDED_SRC_extensions_QIFileDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QFileDialog>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QFileDialog subclass simplifying access to it's static stuff. */
+class SHARED_LIBRARY_STUFF QIFileDialog : public QFileDialog
+{
+ Q_OBJECT;
+
+ /** Constructs our own file-dialog passing @a pParent and enmFlags to the base-class.
+ * Doesn't mean to be used directly, cause this subclass is a bunch of statics. */
+ QIFileDialog(QWidget *pParent, Qt::WindowFlags enmFlags);
+
+public:
+
+ /** Returns an existing directory selected by the user.
+ * @param strDir Brings the dir to start from.
+ * @param pParent Brings the parent.
+ * @param strCaption Brings the dialog caption.
+ * @param fDirOnly Brings whether dialog should show dirs only.
+ * @param fResolveSymLinks Brings whether dialog should resolve sym-links. */
+ static QString getExistingDirectory(const QString &strDir, QWidget *pParent,
+ const QString &strCaption = QString(),
+ bool fDirOnly = true,
+ bool fResolveSymLinks = true);
+
+ /** Returns a file name selected by the user. The file does not have to exist.
+ * @param strStartWith Brings the full file path to start from.
+ * @param strFilters Brings the filters.
+ * @param pParent Brings the parent.
+ * @param strCaption Brings the dialog caption.
+ * @param pStrSelectedFilter Brings the selected filter.
+ * @param fResolveSymLinks Brings whether dialog should resolve sym-links.
+ * @param fConfirmOverwrite Brings whether dialog should confirm overwrite. */
+ static QString getSaveFileName(const QString &strStartWith, const QString &strFilters, QWidget *pParent,
+ const QString &strCaption, QString *pStrSelectedFilter = 0,
+ bool fResolveSymLinks = true, bool fConfirmOverwrite = false);
+
+ /** Returns an existing file selected by the user. If the user presses Cancel, it returns a null string.
+ * @param strStartWith Brings the full file path to start from.
+ * @param strFilters Brings the filters.
+ * @param pParent Brings the parent.
+ * @param strCaption Brings the dialog caption.
+ * @param pStrSelectedFilter Brings the selected filter.
+ * @param fResolveSymLinks Brings whether dialog should resolve sym-links. */
+ static QString getOpenFileName(const QString &strStartWith, const QString &strFilters, QWidget *pParent,
+ const QString &strCaption, QString *pStrSelectedFilter = 0,
+ bool fResolveSymLinks = true);
+
+ /** Returns one or more existing files selected by the user.
+ * @param strStartWith Brings the full file path to start from.
+ * @param strFilters Brings the filters.
+ * @param pParent Brings the parent.
+ * @param strCaption Brings the dialog caption.
+ * @param pStrSelectedFilter Brings the selected filter.
+ * @param fResolveSymLinks Brings whether dialog should resolve sym-links.
+ * @param fSingleFile Brings whether dialog should allow chosing single file only. */
+ static QStringList getOpenFileNames(const QString &strStartWith, const QString &strFilters, QWidget *pParent,
+ const QString &strCaption, QString *pStrSelectedFilter = 0,
+ bool fResolveSymLinks = true,
+ bool fSingleFile = false);
+
+ /** Search for the first directory that exists starting from the
+ * passed one @a strStartDir and going up through its parents. */
+ static QString getFirstExistingDir(const QString &strStartDir);
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIFileDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIFlowLayout.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIFlowLayout.cpp
new file mode 100644
index 00000000..f8020267
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIFlowLayout.cpp
@@ -0,0 +1,272 @@
+/* $Id: QIFlowLayout.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIFlowLayout class implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIFlowLayout.h"
+
+
+QIFlowLayout::QIFlowLayout(QWidget *pParent, int iMargin /* = -1 */, int iSpacingH /* = -1 */, int iSpacingV /* = -1 */)
+ : QLayout(pParent)
+ , m_iSpacingH(iSpacingH)
+ , m_iSpacingV(iSpacingV)
+{
+ setContentsMargins(iMargin, iMargin, iMargin, iMargin);
+}
+
+QIFlowLayout::QIFlowLayout(int iMargin, int iSpacingH, int iSpacingV)
+ : m_iSpacingH(iSpacingH)
+ , m_iSpacingV(iSpacingV)
+{
+ setContentsMargins(iMargin, iMargin, iMargin, iMargin);
+}
+
+QIFlowLayout::~QIFlowLayout()
+{
+ /* Delete all the children: */
+ QLayoutItem *pItem = 0;
+ while ((pItem = takeAt(0)))
+ delete pItem;
+}
+
+int QIFlowLayout::count() const
+{
+ return m_items.size();
+}
+
+void QIFlowLayout::addItem(QLayoutItem *pItem)
+{
+ m_items.append(pItem);
+}
+
+QLayoutItem *QIFlowLayout::itemAt(int iIndex) const
+{
+ return m_items.value(iIndex);
+}
+
+QLayoutItem *QIFlowLayout::takeAt(int iIndex)
+{
+ return iIndex >= 0 && iIndex < m_items.size() ? m_items.takeAt(iIndex) : 0;
+}
+
+Qt::Orientations QIFlowLayout::expandingDirections() const
+{
+ return Qt::Horizontal;
+}
+
+bool QIFlowLayout::hasHeightForWidth() const
+{
+ return true;
+}
+
+int QIFlowLayout::heightForWidth(int iWidth) const
+{
+ return relayout(QRect(0, 0, iWidth, 0), false);
+}
+
+QSize QIFlowLayout::minimumSize() const
+{
+ /* Walk through all the children: */
+ QSize size;
+ foreach (QLayoutItem *pItem, m_items)
+ size = size.expandedTo(pItem->minimumSize());
+
+ /* Do not forget the margins: */
+ int iLeft, iTop, iRight, iBottom;
+ getContentsMargins(&iLeft, &iTop, &iRight, &iBottom);
+ size += QSize(iLeft + iRight, iTop + iBottom);
+
+ /* Return resulting size: */
+ return size;
+}
+
+QSize QIFlowLayout::sizeHint() const
+{
+ return minimumSize();
+}
+
+void QIFlowLayout::setGeometry(const QRect &rect)
+{
+ QLayout::setGeometry(rect);
+ relayout(rect, true);
+}
+
+int QIFlowLayout::relayout(const QRect &rect, bool fDoLayout) const
+{
+ /* Acquire contents margins: */
+ int iLeft, iTop, iRight, iBottom;
+ getContentsMargins(&iLeft, &iTop, &iRight, &iBottom);
+
+ /* Calculate available contents rectangle: */
+ const QRect contentsRect = rect.adjusted(iLeft, iTop, -iRight, -iBottom);
+
+ /* Acquire horizontal/vertical spacings: */
+ const int iSpaceX = horizontalSpacing();
+ //if (iSpaceX == -1)
+ // iSpaceX = pWidged->style()->layoutSpacing(
+ // QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
+ const int iSpaceY = verticalSpacing();
+ //if (iSpaceY == -1)
+ // iSpaceY = pWidged->style()->layoutSpacing(
+ // QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
+
+ /* Split items to rows: */
+ int iLastX = contentsRect.x();
+ LayoutDataTable rows;
+ LayoutDataList row;
+ foreach (QLayoutItem *pItem, m_items)
+ {
+ /* Skip items of zero width: */
+ if (pItem->sizeHint().width() == 0)
+ continue;
+
+ /* Get item policy and width: */
+ const ExpandPolicy enmPolicy = pItem->expandingDirections() & Qt::Horizontal ? ExpandPolicy_Dynamic : ExpandPolicy_Fixed;
+ const int iWidth = pItem->sizeHint().width();
+
+ /* Check whether it's possible to insert this item to current row: */
+ int iNextX = iLastX + iWidth + iSpaceX;
+ if (iNextX - iSpaceX <= contentsRect.right())
+ {
+ /* Append item to current row: */
+ row << LayoutData(pItem, enmPolicy, iWidth);
+ }
+ else
+ {
+ /* Flush the row to rows: */
+ rows << row;
+ row.clear();
+ /* Move the caret to the next row: */
+ iLastX = contentsRect.x();
+ iNextX = iLastX + iWidth + iSpaceX;
+ /* Append item to new row: */
+ row << LayoutData(pItem, enmPolicy, iWidth);
+ }
+
+ /* Remember the last caret position: */
+ iLastX = iNextX;
+ }
+ /* Flush the row to rows: */
+ rows << row;
+ row.clear();
+
+ /* Iterate through all the rows: */
+ for (int i = 0; i < rows.count(); ++i)
+ {
+ /* Acquire current row: */
+ LayoutDataList &row = rows[i];
+ /* Width expand delta is equal to total-width minus all spacing widths ... */
+ int iExpandingWidth = contentsRect.width() - (row.size() - 1) * iSpaceX;
+
+ /* Iterate through whole the row: */
+ int cExpandingItems = 0;
+ for (int j = 0; j < row.count(); ++j)
+ {
+ /* Acquire current record: */
+ const LayoutData &record = row.at(j);
+ /* Calcualte the amount of expandable items: */
+ if (record.policy == ExpandPolicy_Dynamic)
+ ++cExpandingItems;
+ /* ... minus all item widths ... */
+ iExpandingWidth -= record.width;
+ }
+
+ /* If there are expandable items: */
+ if (cExpandingItems > 0)
+ {
+ /* ... devided by the amount of ExpandPolicy_Dynamic items: */
+ iExpandingWidth /= cExpandingItems;
+ /* Expand all the expandable item widths with delta: */
+ for (int j = 0; j < row.count(); ++j)
+ if (row.at(j).policy == ExpandPolicy_Dynamic)
+ row[j].width += iExpandingWidth;
+ }
+ }
+
+ /* Iterate through all the items: */
+ int iX = contentsRect.x();
+ int iY = contentsRect.y();
+ for (int i = 0; i < rows.count(); ++i)
+ {
+ /* Acquire current row: */
+ const LayoutDataList &row = rows.at(i);
+ int iRowHeight = 0;
+ for (int j = 0; j < row.count(); ++j)
+ {
+ /* Acquire current record: */
+ const LayoutData &record = row.at(j);
+ /* Acquire the desired width/height: */
+ const int iDesiredWidth = record.width;
+ const int iDesiredHeight = record.item->sizeHint().height();
+
+ /* Do the layout if requested: */
+ if (fDoLayout)
+ record.item->setGeometry(QRect(QPoint(iX, iY), QSize(iDesiredWidth, iDesiredHeight)));
+
+ /* Acquire the next item location: */
+ iX = iX + iDesiredWidth + iSpaceX;
+ /* Remember the maximum row height: */
+ iRowHeight = qMax(iRowHeight, iDesiredHeight);
+ }
+ /* Move the caret to the next row: */
+ iX = contentsRect.x();
+ iY = iY + iRowHeight + iSpaceY;
+ }
+
+ /* Return effective layout height: */
+ return iY - iSpaceY - rect.y() + iBottom;
+}
+
+int QIFlowLayout::smartSpacing(QStyle::PixelMetric pm) const
+{
+ QObject *pParent = this->parent();
+ if (!pParent)
+ {
+ return -1;
+ }
+ else if (pParent->isWidgetType())
+ {
+ QWidget *pParentWidget = static_cast<QWidget*>(pParent);
+ return pParentWidget->style()->pixelMetric(pm, 0, pParentWidget);
+ }
+ else
+ {
+ return static_cast<QLayout*>(pParent)->spacing();
+ }
+}
+
+int QIFlowLayout::horizontalSpacing() const
+{
+ return m_iSpacingH >= 0 ? m_iSpacingH : smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
+}
+
+int QIFlowLayout::verticalSpacing() const
+{
+ return m_iSpacingV >= 0 ? m_iSpacingV : smartSpacing(QStyle::PM_LayoutVerticalSpacing);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIFlowLayout.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIFlowLayout.h
new file mode 100644
index 00000000..36cdc489
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIFlowLayout.h
@@ -0,0 +1,146 @@
+/* $Id: QIFlowLayout.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIFlowLayout class declaration.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIFlowLayout_h
+#define FEQT_INCLUDED_SRC_extensions_QIFlowLayout_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QLayout>
+#include <QStyle>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QLayout extension providing GUI with the possibility to build flow-layout.
+ * This kind of horizonal layout can wrap children down to the next line (row)
+ * performing calculations on the basis of layout size and children size-hints.
+ * It is also takes into account that some of the children can be expandable
+ * horizontally allowing them to grow up to all the available width. */
+class SHARED_LIBRARY_STUFF QIFlowLayout : public QLayout
+{
+ Q_OBJECT;
+
+ /** Layout item expand policy. */
+ enum ExpandPolicy
+ {
+ ExpandPolicy_Fixed,
+ ExpandPolicy_Dynamic
+ };
+
+ /** Layout item data. */
+ struct LayoutData
+ {
+ /** Constructs layout item data on the bases of passed @a pItem.
+ * @param enmPolicy Brings the layout item expand policy.
+ * @param iWidth Brings the layout item desired width. */
+ LayoutData(QLayoutItem *pItem, ExpandPolicy enmPolicy, int iWidth)
+ : item(pItem), policy(enmPolicy), width(iWidth)
+ {}
+
+ /** Holds the layout item. */
+ QLayoutItem *item;
+ /** Holds the layout item expand policy. */
+ ExpandPolicy policy;
+ /** Holds the layout item desired width. */
+ int width;
+ };
+ /** Layout item data list. */
+ typedef QList<LayoutData> LayoutDataList;
+ /** Layout item data table. */
+ typedef QList<LayoutDataList> LayoutDataTable;
+
+public:
+
+ /** Constructs flow-layout passing @a pParent to the base-class.
+ * @param iMargin Brings the layout contents margin.
+ * @param iSpacingH Brings the layout horizontal spacing.
+ * @param iSpacingV Brings the layout vertical spacing. */
+ QIFlowLayout(QWidget *pParent, int iMargin = -1, int iSpacingH = -1, int iSpacingV = -1);
+
+ /** Constructs flow-layout.
+ * @param iMargin Brings the layout contents margin.
+ * @param iSpacingH Brings the layout horizontal spacing.
+ * @param iSpacingV Brings the layout vertical spacing. */
+ QIFlowLayout(int iMargin = -1, int iSpacingH = -1, int iSpacingV = -1);
+
+ /** Destructs flow-layout. */
+ virtual ~QIFlowLayout() RT_OVERRIDE;
+
+ /** Returns the number of layout items. */
+ virtual int count() const RT_OVERRIDE;
+ /** Adds @a pItem into layout. */
+ virtual void addItem(QLayoutItem *pItem) RT_OVERRIDE;
+ /** Returns the layout item at passed @a iIndex. */
+ virtual QLayoutItem *itemAt(int iIndex) const RT_OVERRIDE;
+ /** Removes the layout item at passed @a iIndex and returns it. */
+ virtual QLayoutItem *takeAt(int index) RT_OVERRIDE;
+
+ /** Returns whether this layout can make use of more space than sizeHint().
+ * A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only one dimension,
+ * whereas Qt::Vertical | Qt::Horizontal means that it wants to grow in both dimensions. */
+ virtual Qt::Orientations expandingDirections() const RT_OVERRIDE;
+
+ /** Returns whether this layout's preferred height depends on its width. */
+ virtual bool hasHeightForWidth() const RT_OVERRIDE;
+ /** Returns the preferred height for this layout item, given the width. */
+ virtual int heightForWidth(int) const RT_OVERRIDE;
+
+ /** Returns the minimum layout size. */
+ virtual QSize minimumSize() const RT_OVERRIDE;
+ /** Returns this item's preferred size. */
+ virtual QSize sizeHint() const RT_OVERRIDE;
+
+ /** Defines this item's geometry to @a rect. */
+ virtual void setGeometry(const QRect &rect) RT_OVERRIDE;
+
+private:
+
+ /** Recalculates layout on the basis of passed @a rect.
+ * Adjusts layout items if @a fDoLayout is true.
+ * @returns recalculated layout height. */
+ int relayout(const QRect &rect, bool fDoLayout) const;
+
+ /** Returns smart spacing based on parent if present. */
+ int smartSpacing(QStyle::PixelMetric pm) const;
+ /** Returns horizontal spacing. */
+ int horizontalSpacing() const;
+ /** Returns vertical spacing. */
+ int verticalSpacing() const;
+
+ /** Holds the layout item list. */
+ QList<QLayoutItem *> m_items;
+
+ /** Holds the horizontal spacing. */
+ int m_iSpacingH;
+ /** Holds the vertical spacing. */
+ int m_iSpacingV;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIFlowLayout_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIInputDialog.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIInputDialog.cpp
new file mode 100644
index 00000000..f1fb804b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIInputDialog.cpp
@@ -0,0 +1,130 @@
+/* $Id: QIInputDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIInputDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIInputDialog.h"
+
+
+QIInputDialog::QIInputDialog(QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ : QDialog(pParent, enmFlags)
+ , m_fDefaultLabelTextRedefined(false)
+ , m_pLabel(0)
+ , m_pTextValueEditor(0)
+ , m_pButtonBox(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+QString QIInputDialog::labelText() const
+{
+ return m_pLabel ? m_pLabel->text() : QString();
+}
+
+void QIInputDialog::resetLabelText()
+{
+ m_fDefaultLabelTextRedefined = false;
+ retranslateUi();
+}
+
+void QIInputDialog::setLabelText(const QString &strText)
+{
+ m_fDefaultLabelTextRedefined = true;
+ if (m_pLabel)
+ m_pLabel->setText(strText);
+}
+
+QString QIInputDialog::textValue() const
+{
+ return m_pTextValueEditor ? m_pTextValueEditor->text() : QString();
+}
+
+void QIInputDialog::setTextValue(const QString &strText)
+{
+ if (m_pTextValueEditor)
+ m_pTextValueEditor->setText(strText);
+}
+
+void QIInputDialog::retranslateUi()
+{
+ if (m_pLabel && !m_fDefaultLabelTextRedefined)
+ m_pLabel->setText(tr("Name:"));
+}
+
+void QIInputDialog::sltTextChanged()
+{
+ if (m_pButtonBox)
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(!textValue().isEmpty());
+}
+
+void QIInputDialog::prepare()
+{
+ /* Do not count that window as important for application,
+ * it will NOT be taken into account when other
+ * top-level windows will be closed: */
+ setAttribute(Qt::WA_QuitOnClose, false);
+
+ /* Create main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ pMainLayout->addWidget(m_pLabel);
+
+ /* Create text value editor: */
+ m_pTextValueEditor = new QLineEdit(this);
+ if (m_pTextValueEditor)
+ {
+ connect(m_pTextValueEditor, &QLineEdit::textChanged, this, &QIInputDialog::sltTextChanged);
+ pMainLayout->addWidget(m_pTextValueEditor);
+ }
+
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
+ if (m_pButtonBox)
+ {
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &QIInputDialog::accept);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &QIInputDialog::reject);
+ pMainLayout->addWidget(m_pButtonBox);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Initialize editors: */
+ sltTextChanged();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIInputDialog.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIInputDialog.h
new file mode 100644
index 00000000..46005b43
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIInputDialog.h
@@ -0,0 +1,99 @@
+/* $Id: QIInputDialog.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIInputDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIInputDialog_h
+#define FEQT_INCLUDED_SRC_extensions_QIInputDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDialog>
+#include <QPointer>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class QLineEdit;
+class QIDialogButtonBox;
+
+/** QDialog extension providing the GUI with
+ * the advanced input dialog capabilities. */
+class SHARED_LIBRARY_STUFF QIInputDialog : public QDialog
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs the dialog passing @a pParent and @a enmFlags to the base-class. */
+ QIInputDialog(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+
+ /** Returns label text. */
+ QString labelText() const;
+ /** Undefines label text. */
+ void resetLabelText();
+ /** Defines label @a strText. */
+ void setLabelText(const QString &strText);
+
+ /** Returns text value. */
+ QString textValue() const;
+ /** Defines @a strText value. */
+ void setTextValue(const QString &strText);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi();
+
+private slots:
+
+ /** Handles text value change. */
+ void sltTextChanged();
+
+private:
+
+ /** Prepared all. */
+ void prepare();
+
+ /** Holds whether label text redefined. */
+ bool m_fDefaultLabelTextRedefined;
+
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the text value editor instance. */
+ QLineEdit *m_pTextValueEditor;
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+};
+
+/** Safe pointer to the QIInputDialog class. */
+typedef QPointer<QIInputDialog> QISafePointerInputDialog;
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIInputDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QILabel.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QILabel.cpp
new file mode 100644
index 00000000..81844ac7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QILabel.cpp
@@ -0,0 +1,372 @@
+/* $Id: QILabel.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QILabel class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This class is based on the original QLabel implementation.
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QClipboard>
+#include <QContextMenuEvent>
+#include <QDrag>
+#include <QFocusEvent>
+#include <QMenu>
+#include <QMimeData>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QStyleOptionFocusRect>
+
+/* GUI includes: */
+#include "QILabel.h"
+
+/* Type definitions: */
+#define HOR_PADDING 1
+
+
+/* static */
+const QRegularExpression QILabel::s_regExpCopy = QRegularExpression("<[^>]*>");
+QRegExp QILabel::s_regExpElide = QRegExp("(<compact\\s+elipsis=\"(start|middle|end)\"?>([^<]*)</compact>)");
+
+QILabel::QILabel(QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ : QLabel(pParent, enmFlags)
+{
+ init();
+}
+
+QILabel::QILabel(const QString &strText, QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ : QLabel(pParent, enmFlags)
+{
+ init();
+ setFullText(strText);
+}
+
+void QILabel::setFullSizeSelection(bool fEnabled)
+{
+ /* Remember new value: */
+ m_fFullSizeSelection = fEnabled;
+ if (m_fFullSizeSelection)
+ {
+ /* Enable mouse interaction only */
+ setTextInteractionFlags(Qt::LinksAccessibleByMouse);
+ /* The label should be able to get the focus */
+ setFocusPolicy(Qt::StrongFocus);
+ /* Change the appearance in the focus state a little bit.
+ * Note: Unfortunately QLabel, precisely the text of a QLabel isn't
+ * styleable. The trolls have forgotten the simplest case ... So this
+ * is done by changing the currently used palette in the In/Out-focus
+ * events below. Next broken feature is drawing a simple dotted line
+ * around the label. So this is done manually in the paintEvent. Not
+ * sure if the stylesheet stuff is ready for production environments. */
+ setStyleSheet(QString("QLabel::focus {\
+ background-color: palette(highlight);\
+ }\
+ QLabel {\
+ padding: 0px %1px 0px %1px;\
+ }").arg(HOR_PADDING));
+ }
+ else
+ {
+ /* Text should be selectable/copyable */
+ setTextInteractionFlags(Qt::TextBrowserInteraction);
+ /* No Focus an the label */
+ setFocusPolicy(Qt::NoFocus);
+ /* No focus style change */
+ setStyleSheet("");
+ }
+}
+
+void QILabel::useSizeHintForWidth(int iWidthHint) const
+{
+ /* Remember new value: */
+ m_iWidthHint = iWidthHint;
+ updateSizeHint();
+}
+
+QSize QILabel::sizeHint() const
+{
+ /* Update size-hint if it's invalid: */
+ if (!m_fHintValid)
+ updateSizeHint();
+
+ /* If there is an updated sizeHint() present - using it: */
+ return m_ownSizeHint.isValid() ? m_ownSizeHint : QLabel::sizeHint();
+}
+
+QSize QILabel::minimumSizeHint() const
+{
+ /* Update size-hint if it's invalid: */
+ if (!m_fHintValid)
+ updateSizeHint();
+
+ /* If there is an updated minimumSizeHint() present - using it. */
+ return m_ownSizeHint.isValid() ? m_ownSizeHint : QLabel::minimumSizeHint();
+}
+
+void QILabel::clear()
+{
+ QLabel::clear();
+ setFullText("");
+}
+
+void QILabel::setText(const QString &strText)
+{
+ /* Call to wrapper below: */
+ setFullText(strText);
+
+ /* If QILabel forced to be fixed vertically */
+ if (minimumHeight() == maximumHeight())
+ {
+ /* Check if new text requires label growing */
+ QSize sh(width(), heightForWidth(width()));
+ if (sh.height() > minimumHeight())
+ setFixedHeight(sh.height());
+ }
+}
+
+void QILabel::copy()
+{
+ /* Strip the text of all HTML subsets: */
+ QString strText = removeHtmlTags(m_strText);
+ /* Copy the current text to the global and selection clipboard. */
+ QApplication::clipboard()->setText(strText, QClipboard::Clipboard);
+ QApplication::clipboard()->setText(strText, QClipboard::Selection);
+}
+
+void QILabel::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QLabel::resizeEvent(pEvent);
+ /* Recalculate the elipsis of the text after every resize. */
+ updateText();
+}
+
+void QILabel::mousePressEvent(QMouseEvent *pEvent)
+{
+ /* Start dragging: */
+ if (pEvent->button() == Qt::LeftButton && geometry().contains(pEvent->pos()) && m_fFullSizeSelection)
+ m_fStartDragging = true;
+ /* Call to base-class: */
+ else
+ QLabel::mousePressEvent(pEvent);
+}
+
+void QILabel::mouseReleaseEvent(QMouseEvent *pEvent)
+{
+ /* Reset dragging: */
+ m_fStartDragging = false;
+ /* Call to base-class: */
+ QLabel::mouseReleaseEvent(pEvent);
+}
+
+void QILabel::mouseMoveEvent(QMouseEvent *pEvent)
+{
+ /* If we have an order to start dragging: */
+ if (m_fStartDragging)
+ {
+ /* Reset dragging: */
+ m_fStartDragging = false;
+ /* Create a drag object out of the given data: */
+ QDrag *pDrag = new QDrag(this);
+ QMimeData *pMimeData = new QMimeData;
+ pMimeData->setText(removeHtmlTags(m_strText));
+ pDrag->setMimeData(pMimeData);
+ /* Start the dragging finally: */
+ pDrag->exec();
+ }
+ /* Call to base-class: */
+ else
+ QLabel::mouseMoveEvent(pEvent);
+}
+
+void QILabel::contextMenuEvent(QContextMenuEvent *pEvent)
+{
+ /* If we have an order for full-size selection: */
+ if (m_fFullSizeSelection)
+ {
+ /* Create a context menu for the copy to clipboard action: */
+ QMenu menu;
+ m_pCopyAction->setText(tr("&Copy"));
+ menu.addAction(m_pCopyAction);
+ menu.exec(pEvent->globalPos());
+ }
+ /* Call to base-class: */
+ else
+ QLabel::contextMenuEvent(pEvent);
+}
+
+void QILabel::focusInEvent(QFocusEvent *)
+{
+ /* If we have an order for full-size selection: */
+ if (m_fFullSizeSelection)
+ {
+ /* Set the text color to the current used highlight text color: */
+ QPalette pal = qApp->palette();
+ pal.setBrush(QPalette::WindowText, pal.brush(QPalette::HighlightedText));
+ setPalette(pal);
+ }
+}
+
+void QILabel::focusOutEvent(QFocusEvent *pEvent)
+{
+ /* Reset to the default palette: */
+ if (m_fFullSizeSelection && pEvent->reason() != Qt::PopupFocusReason)
+ setPalette(qApp->palette());
+}
+
+void QILabel::paintEvent(QPaintEvent *pEvent)
+{
+ /* Call to base-class: */
+ QLabel::paintEvent(pEvent);
+
+ /* If we have an order for full-size selection and have focus: */
+ if (m_fFullSizeSelection && hasFocus())
+ {
+ /* Paint a focus rect based on the current style: */
+ QPainter painter(this);
+ QStyleOptionFocusRect option;
+ option.initFrom(this);
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
+ }
+}
+
+void QILabel::init()
+{
+ /* Initial setup: */
+ m_fHintValid = false;
+ m_iWidthHint = -1;
+ m_fStartDragging = false;
+ setFullSizeSelection(false);
+ setOpenExternalLinks(true);
+
+ /* Create invisible copy action: */
+ m_pCopyAction = new QAction(this);
+ if (m_pCopyAction)
+ {
+ /* Configure action: */
+ m_pCopyAction->setShortcut(QKeySequence(QKeySequence::Copy));
+ m_pCopyAction->setShortcutContext(Qt::WidgetShortcut);
+ connect(m_pCopyAction, &QAction::triggered, this, &QILabel::copy);
+ /* Add action to label: */
+ addAction(m_pCopyAction);
+ }
+}
+
+void QILabel::updateSizeHint() const
+{
+ /* Recalculate size-hint if necessary: */
+ m_ownSizeHint = m_iWidthHint == -1 ? QSize() : QSize(m_iWidthHint, heightForWidth(m_iWidthHint));
+ m_fHintValid = true;
+}
+
+void QILabel::setFullText(const QString &strText)
+{
+ /* Reapply size-policy: */
+ QSizePolicy sp = sizePolicy();
+ sp.setHeightForWidth(wordWrap());
+ setSizePolicy(sp);
+
+ /* Reset size-hint validity: */
+ m_fHintValid = false;
+
+ /* Remember new value: */
+ m_strText = strText;
+ updateText();
+}
+
+void QILabel::updateText()
+{
+ /* Compress text: */
+ const QString strCompText = compressText(m_strText);
+
+ /* Assign it: */
+ QLabel::setText(strCompText);
+
+ /* Only set the tool-tip if the text is shortened in any way: */
+ if (removeHtmlTags(strCompText) != removeHtmlTags(m_strText))
+ setToolTip(removeHtmlTags(m_strText));
+ else
+ setToolTip("");
+}
+
+QString QILabel::compressText(const QString &strText) const
+{
+ /* Prepare result: */
+ QStringList result;
+ QFontMetrics fm = fontMetrics();
+ /* Split up any multi-line text: */
+ foreach (QString strLine, strText.split(QRegularExpression("<br */?>")))
+ {
+ /* Search for the compact tag: */
+ if (s_regExpElide.indexIn(strLine) > -1)
+ {
+ /* USe the untouchable text to work on: */
+ const QString strWork = strLine;
+ /* Grep out the necessary info of the regexp: */
+ const QString strCompact = s_regExpElide.cap(1);
+ const QString strElideMode = s_regExpElide.cap(2);
+ const QString strElide = s_regExpElide.cap(3);
+ /* Remove the whole compact tag (also the text): */
+ const QString strFlat = removeHtmlTags(QString(strWork).remove(strCompact));
+ /* What size will the text have without the compact text: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const int iFlatWidth = fm.horizontalAdvance(strFlat);
+#else
+ const int iFlatWidth = fm.width(strFlat);
+#endif
+ /* Create the shortened text: */
+ const QString strNew = fm.elidedText(strElide, toTextElideMode(strElideMode), width() - (2 * HOR_PADDING) - iFlatWidth);
+ /* Replace the compact part with the shortened text in the initial string: */
+ strLine = QString(strWork).replace(strCompact, strNew);
+ }
+ /* Append the line: */
+ result << strLine;
+ }
+ /* Return result: */
+ return result.join("<br />");
+}
+
+/* static */
+QString QILabel::removeHtmlTags(const QString &strText)
+{
+ /* Remove all HTML tags from the text and return it: */
+ return QString(strText).remove(s_regExpCopy);
+}
+
+/* static */
+Qt::TextElideMode QILabel::toTextElideMode(const QString &strType)
+{
+ /* Converts a string-represented type to a Qt elide mode: */
+ Qt::TextElideMode enmMode = Qt::ElideNone;
+ if (strType == "start")
+ enmMode = Qt::ElideLeft;
+ else if (strType == "middle")
+ enmMode = Qt::ElideMiddle;
+ else if (strType == "end")
+ enmMode = Qt::ElideRight;
+ return enmMode;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QILabel.h b/src/VBox/Frontends/VirtualBox/src/extensions/QILabel.h
new file mode 100644
index 00000000..82856d03
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QILabel.h
@@ -0,0 +1,147 @@
+/* $Id: QILabel.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QILabel class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This class is based on the original QLabel implementation.
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QILabel_h
+#define FEQT_INCLUDED_SRC_extensions_QILabel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QLabel>
+#include <QRegularExpression>
+#include <QRegExp>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QLabel subclass extending it with advanced functionality. */
+class SHARED_LIBRARY_STUFF QILabel : public QLabel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs label passing @a pParent and @a enmFlags to the base-class. */
+ QILabel(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+ /** Constructs label passing @a strText, @a pParent and @a enmFlags to the base-class. */
+ QILabel(const QString &strText, QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+
+ /** Returns whether label full-size focusing selection is enabled. */
+ bool fullSizeSelection() const { return m_fFullSizeSelection; }
+ /** Defines whether label full-size focusing selection is @a fEnabled. */
+ void setFullSizeSelection(bool fEnabled);
+
+ /** Defines whether label should use size-hint based on passed @a iWidthHint. */
+ void useSizeHintForWidth(int iWidthHint) const;
+ /** Returns size-hint. */
+ QSize sizeHint() const;
+ /** Returns minimum size-hint. */
+ QSize minimumSizeHint() const;
+
+ /** Returns text. */
+ QString text() const { return m_strText; }
+
+public slots:
+
+ /** Clears text. */
+ void clear();
+ /** Defines text. */
+ void setText(const QString &strText);
+ /** Copies text into clipboard. */
+ void copy();
+
+protected:
+
+ /** Handles resize @a pEvent. */
+ void resizeEvent(QResizeEvent *pEvent);
+
+ /** Handles mouse-press @a pEvent. */
+ void mousePressEvent(QMouseEvent *pEvent);
+ /** Handles mouse-release @a pEvent. */
+ void mouseReleaseEvent(QMouseEvent *pEvent);
+ /** Handles mouse-move @a pEvent. */
+ void mouseMoveEvent(QMouseEvent *pEvent);
+
+ /** Handles context-menu @a pEvent. */
+ void contextMenuEvent(QContextMenuEvent *pEvent);
+
+ /** Handles focus-in @a pEvent. */
+ void focusInEvent(QFocusEvent *pEvent);
+ /** Handles focus-out @a pEvent. */
+ void focusOutEvent(QFocusEvent *pEvent);
+
+ /** Handles paint @a pEvent. */
+ void paintEvent(QPaintEvent *pEvent);
+
+private:
+
+ /** Performs initialization. */
+ void init();
+
+ /** Updates size-hint. */
+ void updateSizeHint() const;
+ /** Defines full-text. */
+ void setFullText(const QString &strText);
+ /** Updates text. */
+ void updateText();
+ /** Compresses passed @a strText. */
+ QString compressText(const QString &strText) const;
+ /** Returns text without HTML tags. */
+ static QString removeHtmlTags(const QString &strText);
+ /** Converts passed @a strType to text-elide mode flag. */
+ static Qt::TextElideMode toTextElideMode(const QString& strType);
+
+ /** Holds the text. */
+ QString m_strText;
+
+ /** Holds whether label full-size focusing selection is enabled. */
+ bool m_fFullSizeSelection;
+ /** Holds whether we started D&D. */
+ bool m_fStartDragging;
+
+ /** Holds whether the size-hint is valid. */
+ mutable bool m_fHintValid;
+ /** Holds the width-hint. */
+ mutable int m_iWidthHint;
+ /** Holds the size-hint. */
+ mutable QSize m_ownSizeHint;
+
+ /** Holds the Copy action instance. */
+ QAction *m_pCopyAction;
+
+ /** Holds text-copy reg-exp. */
+ static const QRegularExpression s_regExpCopy;
+ /** Holds text-elide reg-exp. */
+ static QRegExp s_regExpElide;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QILabel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QILabelSeparator.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QILabelSeparator.cpp
new file mode 100644
index 00000000..8052df1a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QILabelSeparator.cpp
@@ -0,0 +1,103 @@
+/* $Id: QILabelSeparator.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QILabelSeparator class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "QILabelSeparator.h"
+
+
+QILabelSeparator::QILabelSeparator(QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ : QWidget(pParent, enmFlags)
+ , m_pLabel(0)
+{
+ prepare();
+}
+
+QILabelSeparator::QILabelSeparator(const QString &strText, QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ : QWidget(pParent, enmFlags)
+ , m_pLabel(0)
+{
+ prepare();
+ setText(strText);
+}
+
+QString QILabelSeparator::text() const
+{
+ return m_pLabel->text();
+}
+
+void QILabelSeparator::setBuddy(QWidget *pBuddy)
+{
+ m_pLabel->setBuddy(pBuddy);
+}
+
+void QILabelSeparator::clear()
+{
+ m_pLabel->clear();
+}
+
+void QILabelSeparator::setText(const QString &strText)
+{
+ m_pLabel->setText(strText);
+}
+
+void QILabelSeparator::prepare()
+{
+ /* Create layout: */
+ QHBoxLayout *pLayout = new QHBoxLayout(this);
+ if (pLayout)
+ {
+ /* Configure layout: */
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel;
+ if (m_pLabel)
+ {
+ /* Add into layout: */
+ pLayout->addWidget(m_pLabel);
+ }
+
+ /* Create separator: */
+ QFrame *pSeparator = new QFrame;
+ {
+ /* Configure separator: */
+ pSeparator->setFrameShape(QFrame::HLine);
+ pSeparator->setFrameShadow(QFrame::Sunken);
+ pSeparator->setEnabled(false);
+ pSeparator->setContentsMargins(0, 0, 0, 0);
+ // pSeparator->setStyleSheet("QFrame {border: 1px outset black; }");
+ pSeparator->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
+
+ /* Add into layout: */
+ pLayout->addWidget(pSeparator, Qt::AlignBottom);
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QILabelSeparator.h b/src/VBox/Frontends/VirtualBox/src/extensions/QILabelSeparator.h
new file mode 100644
index 00000000..2ad706c5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QILabelSeparator.h
@@ -0,0 +1,79 @@
+/* $Id: QILabelSeparator.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QILabelSeparator class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QILabelSeparator_h
+#define FEQT_INCLUDED_SRC_extensions_QILabelSeparator_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI inlcudes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class QString;
+class QWidget;
+
+/** QWidget extension providing GUI with label-separator. */
+class SHARED_LIBRARY_STUFF QILabelSeparator : public QWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs label-separator passing @a pParent and @a enmFlags to the base-class. */
+ QILabelSeparator(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+ /** Constructs label-separator passing @a pParent and @a enmFlags to the base-class.
+ * @param strText Brings the label text. */
+ QILabelSeparator(const QString &strText, QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+
+ /** Returns the label text. */
+ QString text() const;
+ /** Defines the label buddy. */
+ void setBuddy(QWidget *pBuddy);
+
+public slots:
+
+ /** Clears the label text. */
+ void clear();
+ /** Defines the label @a strText. */
+ void setText(const QString &strText);
+
+protected:
+
+ /** Prepares all. */
+ virtual void prepare();
+
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QILabelSeparator_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QILineEdit.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QILineEdit.cpp
new file mode 100644
index 00000000..be5ea051
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QILineEdit.cpp
@@ -0,0 +1,269 @@
+/* $Id: QILineEdit.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QILineEdit class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QClipboard>
+#include <QContextMenuEvent>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QMenu>
+#include <QPalette>
+#include <QStyleOptionFrame>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "UIIconPool.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+QILineEdit::QILineEdit(QWidget *pParent /* = 0 */)
+ : QLineEdit(pParent)
+ , m_fAllowToCopyContentsWhenDisabled(false)
+ , m_pCopyAction(0)
+ , m_pIconLabel(0)
+ , m_fMarkForError(false)
+{
+ prepare();
+}
+
+QILineEdit::QILineEdit(const QString &strText, QWidget *pParent /* = 0 */)
+ : QLineEdit(strText, pParent)
+ , m_fAllowToCopyContentsWhenDisabled(false)
+ , m_pCopyAction(0)
+ , m_pIconLabel(0)
+ , m_fMarkForError(false)
+{
+ prepare();
+}
+
+void QILineEdit::setAllowToCopyContentsWhenDisabled(bool fAllow)
+{
+ m_fAllowToCopyContentsWhenDisabled = fAllow;
+}
+
+void QILineEdit::setMinimumWidthByText(const QString &strText)
+{
+ setMinimumWidth(fitTextWidth(strText).width());
+}
+
+void QILineEdit::setFixedWidthByText(const QString &strText)
+{
+ setFixedWidth(fitTextWidth(strText).width());
+}
+
+void QILineEdit::mark(bool fError, const QString &strErrorMessage /* = QString() */)
+{
+ /* Check if something really changed: */
+ if (fError == m_fMarkForError && m_strErrorMessage == strErrorMessage)
+ return;
+
+ /* Save new values: */
+ m_fMarkForError = fError;
+ m_strErrorMessage = strErrorMessage;
+
+ /* Update accordingly: */
+ if (m_fMarkForError)
+ {
+ /* Create label if absent: */
+ if (!m_pIconLabel)
+ m_pIconLabel = new QLabel(this);
+
+ /* Update label content, visibility & position: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) * .625;
+ const int iShift = height() > iIconMetric ? (height() - iIconMetric) / 2 : 0;
+ m_pIconLabel->setPixmap(m_markIcon.pixmap(windowHandle(), QSize(iIconMetric, iIconMetric)));
+ m_pIconLabel->setToolTip(m_strErrorMessage);
+ m_pIconLabel->move(width() - iIconMetric - iShift, iShift);
+ m_pIconLabel->show();
+ }
+ else
+ {
+ /* Hide label: */
+ if (m_pIconLabel)
+ m_pIconLabel->hide();
+ }
+}
+
+bool QILineEdit::event(QEvent *pEvent)
+{
+ switch (pEvent->type())
+ {
+ case QEvent::ContextMenu:
+ {
+ /* For disabled widget if requested: */
+ if (!isEnabled() && m_fAllowToCopyContentsWhenDisabled)
+ {
+ /* Create a context menu for the copy to clipboard action: */
+ QContextMenuEvent *pContextMenuEvent = static_cast<QContextMenuEvent*>(pEvent);
+ QMenu menu;
+ m_pCopyAction->setText(tr("&Copy"));
+ menu.addAction(m_pCopyAction);
+ menu.exec(pContextMenuEvent->globalPos());
+ pEvent->accept();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return QLineEdit::event(pEvent);
+}
+
+void QILineEdit::resizeEvent(QResizeEvent *pResizeEvent)
+{
+ /* Call to base-class: */
+ QLineEdit::resizeEvent(pResizeEvent);
+
+ /* Update error label position: */
+ if (m_pIconLabel)
+ {
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) * .625;
+ const int iShift = height() > iIconMetric ? (height() - iIconMetric) / 2 : 0;
+ m_pIconLabel->move(width() - iIconMetric - iShift, iShift);
+ }
+}
+
+void QILineEdit::copy()
+{
+ /* Copy the current text to the global and selection clipboards: */
+ QApplication::clipboard()->setText(text(), QClipboard::Clipboard);
+ QApplication::clipboard()->setText(text(), QClipboard::Selection);
+}
+
+void QILineEdit::prepare()
+{
+ /* Prepare invisible copy action: */
+ m_pCopyAction = new QAction(this);
+ if (m_pCopyAction)
+ {
+ m_pCopyAction->setShortcut(QKeySequence(QKeySequence::Copy));
+ m_pCopyAction->setShortcutContext(Qt::WidgetShortcut);
+ connect(m_pCopyAction, &QAction::triggered, this, &QILineEdit::copy);
+ addAction(m_pCopyAction);
+ }
+
+ /* Prepare warning icon: */
+ m_markIcon = UIIconPool::iconSet(":/status_error_16px.png");
+}
+
+QSize QILineEdit::fitTextWidth(const QString &strText) const
+{
+ QStyleOptionFrame sof;
+ sof.initFrom(this);
+ sof.rect = contentsRect();
+ sof.lineWidth = hasFrame() ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0;
+ sof.midLineWidth = 0;
+ sof.state |= QStyle::State_Sunken;
+
+ /** @todo make it wise.. */
+ // WORKAROUND:
+ // The margins are based on qlineedit.cpp of Qt.
+ // Maybe they where changed at some time in the future.
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ QSize sc(fontMetrics().horizontalAdvance(strText) + 2 * 2,
+ fontMetrics().xHeight() + 2 * 1);
+#else
+ QSize sc(fontMetrics().width(strText) + 2 * 2,
+ fontMetrics().xHeight() + 2 * 1);
+#endif
+ const QSize sa = style()->sizeFromContents(QStyle::CT_LineEdit, &sof, sc, this);
+
+ return sa;
+}
+
+UIMarkableLineEdit::UIMarkableLineEdit(QWidget *pParent /* = 0 */)
+ :QWidget(pParent)
+ , m_pLineEdit(0)
+ , m_pIconLabel(0)
+{
+ prepare();
+}
+
+void UIMarkableLineEdit::setText(const QString &strText)
+{
+ if (m_pLineEdit)
+ m_pLineEdit->setText(strText);
+}
+
+QString UIMarkableLineEdit::text() const
+{
+ if (!m_pLineEdit)
+ return QString();
+ return m_pLineEdit->text();
+}
+
+void UIMarkableLineEdit::setValidator(const QValidator *pValidator)
+{
+ if (m_pLineEdit)
+ m_pLineEdit->setValidator(pValidator);
+}
+
+bool UIMarkableLineEdit::hasAcceptableInput() const
+{
+ if (!m_pLineEdit)
+ return false;
+ return m_pLineEdit->hasAcceptableInput();
+}
+
+void UIMarkableLineEdit::setPlaceholderText(const QString &strText)
+{
+ if (m_pLineEdit)
+ m_pLineEdit->setPlaceholderText(strText);
+}
+
+void UIMarkableLineEdit::mark(bool fError, const QString &strErrorMessage /* = QString() */)
+{
+ m_pIconLabel->setVisible(true);
+ AssertReturnVoid(m_pIconLabel);
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+
+ if (fError)
+ m_pIconLabel->setPixmap(UIIconPool::iconSet(":/status_error_16px.png").pixmap(windowHandle(), QSize(iIconMetric, iIconMetric)));
+ else
+ m_pIconLabel->setPixmap(UIIconPool::iconSet(":/status_check_16px.png").pixmap(windowHandle(), QSize(iIconMetric, iIconMetric)));
+ m_pIconLabel->setToolTip(strErrorMessage);
+}
+
+void UIMarkableLineEdit::prepare()
+{
+ QHBoxLayout *pMainLayout = new QHBoxLayout(this);
+ AssertReturnVoid(pMainLayout);
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLineEdit = new QILineEdit;
+ AssertReturnVoid(m_pLineEdit);
+ m_pIconLabel = new QLabel;
+ AssertReturnVoid(m_pIconLabel);
+ /* Show the icon label only if line edit is marked for error/no error.*/
+ m_pIconLabel->hide();
+ pMainLayout->addWidget(m_pLineEdit);
+ pMainLayout->addWidget(m_pIconLabel);
+ setFocusProxy(m_pLineEdit);
+ connect(m_pLineEdit, &QILineEdit::textChanged, this, &UIMarkableLineEdit::textChanged);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QILineEdit.h b/src/VBox/Frontends/VirtualBox/src/extensions/QILineEdit.h
new file mode 100644
index 00000000..35ac73a9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QILineEdit.h
@@ -0,0 +1,129 @@
+/* $Id: QILineEdit.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QILineEdit class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QILineEdit_h
+#define FEQT_INCLUDED_SRC_extensions_QILineEdit_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes */
+#include <QIcon>
+#include <QLineEdit>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+class QLabel;
+
+/** QLineEdit extension with advanced functionality. */
+class SHARED_LIBRARY_STUFF QILineEdit : public QLineEdit
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs line-edit passing @a pParent to the base-class. */
+ QILineEdit(QWidget *pParent = 0);
+ /** Constructs line-edit passing @a pParent to the base-class.
+ * @param strText Brings the line-edit text. */
+ QILineEdit(const QString &strText, QWidget *pParent = 0);
+
+ /** Defines whether this is @a fAllowed to copy contents when disabled. */
+ void setAllowToCopyContentsWhenDisabled(bool fAllowed);
+
+ /** Forces line-edit to adjust minimum width acording to passed @a strText. */
+ void setMinimumWidthByText(const QString &strText);
+ /** Forces line-edit to adjust fixed width acording to passed @a strText. */
+ void setFixedWidthByText(const QString &strText);
+
+ /** Puts an icon to mark some error on the right hand side of the line edit. @p is used as tooltip of the icon. */
+ void mark(bool fError, const QString &strErrorMessage = QString());
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pResizeEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Copies text into clipboard. */
+ void copy();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Calculates suitable @a strText size. */
+ QSize fitTextWidth(const QString &strText) const;
+
+ /** Holds whether this is allowed to copy contents when disabled. */
+ bool m_fAllowToCopyContentsWhenDisabled;
+ /** Holds the copy to clipboard action. */
+ QAction *m_pCopyAction;
+
+ QLabel *m_pIconLabel;
+ QIcon m_markIcon;
+ bool m_fMarkForError;
+ QString m_strErrorMessage;
+};
+
+class SHARED_LIBRARY_STUFF UIMarkableLineEdit : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ void textChanged(const QString &strText);
+
+public:
+
+ UIMarkableLineEdit(QWidget *pParent = 0);
+ void mark(bool fError, const QString &strErrorMessage = QString());
+
+ /** @name Pass through functions for QILineEdit.
+ * @{ */
+ void setText(const QString &strText);
+ QString text() const;
+ void setValidator(const QValidator *pValidator);
+ bool hasAcceptableInput() const;
+ void setPlaceholderText(const QString &strText);
+ /** @} */
+
+private:
+
+ void prepare();
+
+ QILineEdit *m_pLineEdit;
+ QLabel *m_pIconLabel;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QILineEdit_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIMainDialog.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIMainDialog.cpp
new file mode 100644
index 00000000..d5ce85e8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIMainDialog.cpp
@@ -0,0 +1,349 @@
+/* $Id: QIMainDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIMainDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QDialogButtonBox>
+#include <QDir>
+#include <QEventLoop>
+#include <QMenu>
+#include <QProcess>
+#include <QPushButton>
+#include <QSizeGrip>
+#include <QUrl>
+
+/* GUI includes: */
+#include "QIMainDialog.h"
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "VBoxUtils.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+QIMainDialog::QIMainDialog(QWidget *pParent /* = 0 */,
+ Qt::WindowFlags enmFlags /* = Qt::Dialog */,
+ bool fIsAutoCentering /* = true */)
+ : QMainWindow(pParent, enmFlags)
+ , m_fIsAutoCentering(fIsAutoCentering)
+ , m_fPolished(false)
+ , m_iResult(QDialog::Rejected)
+ , m_fRejectByEscape(true)
+{
+ /* Install event-filter: */
+ qApp->installEventFilter(this);
+}
+
+int QIMainDialog::exec(bool fApplicationModal /* = true */)
+{
+ /* Check for the recursive run: */
+ AssertMsgReturn(!m_pEventLoop, ("QIMainDialog::exec() is called recursively!\n"), QDialog::Rejected);
+
+ /* Reset the result code: */
+ setResult(QDialog::Rejected);
+
+ /* Should we delete ourself on close in theory? */
+ const bool fOldDeleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
+ /* For the exec() time, set this attribute to 'false': */
+ setAttribute(Qt::WA_DeleteOnClose, false);
+
+ /* Which is the current window-modality? */
+ const Qt::WindowModality oldModality = windowModality();
+ /* For the exec() time, set this attribute to 'window-modal' or 'application-modal': */
+ setWindowModality(!fApplicationModal ? Qt::WindowModal : Qt::ApplicationModal);
+
+ /* Show ourself: */
+ show();
+
+ /* Create a local event-loop: */
+ {
+ QEventLoop eventLoop;
+ m_pEventLoop = &eventLoop;
+
+ /* Guard ourself for the case
+ * we destroyed ourself in our event-loop: */
+ QPointer<QIMainDialog> guard = this;
+
+ /* Start the blocking event-loop: */
+ eventLoop.exec();
+
+ /* Are we still valid? */
+ if (guard.isNull())
+ return QDialog::Rejected;
+
+ m_pEventLoop = 0;
+ }
+
+ /* Save the result code early (we can delete ourself on close): */
+ const int iResultCode = result();
+
+ /* Return old modality: */
+ setWindowModality(oldModality);
+
+ /* Reset attribute to previous value: */
+ setAttribute(Qt::WA_DeleteOnClose, fOldDeleteOnClose);
+ /* Delete ourself if we should do that on close: */
+ if (fOldDeleteOnClose)
+ delete this;
+
+ /* Return the result code: */
+ return iResultCode;
+}
+
+QPushButton *QIMainDialog::defaultButton() const
+{
+ return m_pDefaultButton;
+}
+
+void QIMainDialog::setDefaultButton(QPushButton *pButton)
+{
+ m_pDefaultButton = pButton;
+}
+
+bool QIMainDialog::isSizeGripEnabled() const
+{
+ return m_pSizeGrip;
+}
+
+void QIMainDialog::setSizeGripEnabled(bool fEnabled)
+{
+ /* Create if missed: */
+ if (!m_pSizeGrip && fEnabled)
+ {
+ m_pSizeGrip = new QSizeGrip(this);
+ m_pSizeGrip->resize(m_pSizeGrip->sizeHint());
+ m_pSizeGrip->show();
+ }
+ /* Destroy if present: */
+ else if (m_pSizeGrip && !fEnabled)
+ {
+ delete m_pSizeGrip;
+ m_pSizeGrip = 0;
+ }
+}
+
+void QIMainDialog::setVisible(bool fVisible)
+{
+ /* Call to base-class: */
+ QMainWindow::setVisible(fVisible);
+
+ /* Exit from the event-loop if there is any and
+ * we are changing our state from visible to hidden. */
+ if (m_pEventLoop && !fVisible)
+ m_pEventLoop->exit();
+}
+
+bool QIMainDialog::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Skip for inactive window: */
+ if (!isActiveWindow())
+ return QMainWindow::eventFilter(pObject, pEvent);
+
+ /* Skip for children of other than this one window: */
+ if (qobject_cast<QWidget*>(pObject) &&
+ qobject_cast<QWidget*>(pObject)->window() != this)
+ return QMainWindow::eventFilter(pObject, pEvent);
+
+ /* Depending on event-type: */
+ switch (pEvent->type())
+ {
+ /* Auto-default-button focus-in processor used to move the "default"
+ * button property into the currently focused button. */
+ case QEvent::FocusIn:
+ {
+ if (qobject_cast<QPushButton*>(pObject) &&
+ (pObject->parent() == centralWidget() ||
+ qobject_cast<QDialogButtonBox*>(pObject->parent())))
+ {
+ qobject_cast<QPushButton*>(pObject)->setDefault(pObject != m_pDefaultButton);
+ if (m_pDefaultButton)
+ m_pDefaultButton->setDefault(pObject == m_pDefaultButton);
+ }
+ break;
+ }
+ /* Auto-default-button focus-out processor used to remove the "default"
+ * button property from the previously focused button. */
+ case QEvent::FocusOut:
+ {
+ if (qobject_cast<QPushButton*>(pObject) &&
+ (pObject->parent() == centralWidget() ||
+ qobject_cast<QDialogButtonBox*>(pObject->parent())))
+ {
+ if (m_pDefaultButton)
+ m_pDefaultButton->setDefault(pObject != m_pDefaultButton);
+ qobject_cast<QPushButton*>(pObject)->setDefault(pObject == m_pDefaultButton);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QMainWindow::eventFilter(pObject, pEvent);
+}
+
+bool QIMainDialog::event(QEvent *pEvent)
+{
+ /* Depending on event-type: */
+ switch (pEvent->type())
+ {
+ case QEvent::Polish:
+ {
+ /* Initially search for the default-button: */
+ m_pDefaultButton = searchDefaultButton();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QMainWindow::event(pEvent);
+}
+
+void QIMainDialog::showEvent(QShowEvent *pEvent)
+{
+ /* Polish dialog if necessary: */
+ if (!m_fPolished)
+ {
+ polishEvent(pEvent);
+ m_fPolished = true;
+ }
+
+ /* Call to base-class: */
+ QMainWindow::showEvent(pEvent);
+}
+
+void QIMainDialog::polishEvent(QShowEvent *)
+{
+ /* Explicit centering according to our parent: */
+ if (m_fIsAutoCentering)
+ gpDesktop->centerWidget(this, parentWidget(), false);
+}
+
+void QIMainDialog::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QMainWindow::resizeEvent(pEvent);
+
+ /* Adjust the size-grip location for the current resize event: */
+ if (m_pSizeGrip)
+ {
+ if (isRightToLeft())
+ m_pSizeGrip->move(rect().bottomLeft() - m_pSizeGrip->rect().bottomLeft());
+ else
+ m_pSizeGrip->move(rect().bottomRight() - m_pSizeGrip->rect().bottomRight());
+ }
+}
+
+void QIMainDialog::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Make sure that we only proceed if no
+ * popup or other modal widgets are open. */
+ if (qApp->activePopupWidget() ||
+ (qApp->activeModalWidget() && qApp->activeModalWidget() != this))
+ {
+ /* Call to base-class: */
+ return QMainWindow::keyPressEvent(pEvent);
+ }
+
+ /* Special handling for some keys: */
+ switch (pEvent->key())
+ {
+ /* Special handling for Escape key: */
+ case Qt::Key_Escape:
+ {
+ if (pEvent->modifiers() == Qt::NoModifier && m_fRejectByEscape)
+ {
+ setResult(QDialog::Rejected);
+ close();
+ return;
+ }
+ break;
+ }
+#ifdef VBOX_WS_MAC
+ /* Special handling for Period key: */
+ case Qt::Key_Period:
+ {
+ if (pEvent->modifiers() == Qt::ControlModifier)
+ {
+ setResult(QDialog::Rejected);
+ close();
+ return;
+ }
+ break;
+ }
+#endif /* VBOX_WS_MAC */
+ /* Special handling for Return/Enter key: */
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ {
+ if (((pEvent->modifiers() == Qt::NoModifier) && (pEvent->key() == Qt::Key_Return)) ||
+ ((pEvent->modifiers() & Qt::KeypadModifier) && (pEvent->key() == Qt::Key_Enter)))
+ {
+ if (QPushButton *pCurrentDefault = searchDefaultButton())
+ {
+ pCurrentDefault->animateClick();
+ return;
+ }
+ }
+ break;
+ }
+ /* Default handling for others: */
+ default: break;
+ }
+
+ /* Call to base-class: */
+ QMainWindow::keyPressEvent(pEvent);
+}
+
+QPushButton *QIMainDialog::searchDefaultButton() const
+{
+ /* Search for the first default-button in the dialog: */
+ QList<QPushButton*> list = findChildren<QPushButton*>();
+ foreach (QPushButton *pButton, list)
+ if (pButton->isDefault() &&
+ (pButton->parent() == centralWidget() ||
+ qobject_cast<QDialogButtonBox*>(pButton->parent())))
+ return pButton;
+ return 0;
+}
+
+void QIMainDialog::done(int iResult)
+{
+ /* Set the final result: */
+ setResult(iResult);
+ /* Hide: */
+ hide();
+}
+
+void QIMainDialog::setRejectByEscape(bool fRejectByEscape)
+{
+ m_fRejectByEscape = fRejectByEscape;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIMainDialog.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIMainDialog.h
new file mode 100644
index 00000000..1976a9dd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIMainDialog.h
@@ -0,0 +1,140 @@
+/* $Id: QIMainDialog.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIMainDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIMainDialog_h
+#define FEQT_INCLUDED_SRC_extensions_QIMainDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDialog>
+#include <QMainWindow>
+#include <QPointer>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QPushButton;
+class QEventLoop;
+class QSizeGrip;
+
+/** QDialog analog based on QMainWindow. */
+class SHARED_LIBRARY_STUFF QIMainDialog : public QMainWindow
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs main-dialog passing @a pParent and @a enmFlags to the base-class.
+ * @param fIsAutoCentering Brigs whether this dialog should be centered according it's parent. */
+ QIMainDialog(QWidget *pParent = 0,
+ Qt::WindowFlags enmFlags = Qt::Dialog,
+ bool fIsAutoCentering = true);
+
+ /** Returns the dialog's result code. */
+ int result() const { return m_iResult; }
+
+ /** Executes the dialog, launching local event-loop.
+ * @param fApplicationModal defines whether this dialog should be modal to application or window. */
+ int exec(bool fApplicationModal = true);
+
+ /** Returns dialog's default button. */
+ QPushButton *defaultButton() const;
+ /** Defines dialog's default @a pButton. */
+ void setDefaultButton(QPushButton *pButton);
+
+ /** Returns whether size-grip was enabled for that dialog. */
+ bool isSizeGripEnabled() const;
+ /** Defines whether size-grip should be @a fEnabled for that dialog. */
+ void setSizeGripEnabled(bool fEnabled);
+
+public slots:
+
+ /** Defines whether the dialog is @a fVisible. */
+ virtual void setVisible(bool fVisible);
+
+protected:
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles first show @a pEvent. */
+ virtual void polishEvent(QShowEvent *pEvent);
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+ /** Searches for dialog's default button. */
+ QPushButton *searchDefaultButton() const;
+
+ /** Sets reject-by-escape-key flag. */
+ void setRejectByEscape(bool fRejectByEscape);
+
+protected slots:
+
+ /** Sets the modal dialog's result code to @a iResult. */
+ void setResult(int iResult) { m_iResult = iResult; }
+
+ /** Closes the modal dialog and sets its result code to @a iResult.
+ * If this dialog is shown with exec(), done() causes the local
+ * event-loop to finish, and exec() to return @a iResult. */
+ virtual void done(int iResult);
+ /** Hides the modal dialog and sets the result code to Accepted. */
+ virtual void accept() { done(QDialog::Accepted); }
+ /** Hides the modal dialog and sets the result code to Rejected. */
+ virtual void reject() { done(QDialog::Rejected); }
+
+private:
+
+ /** Holds whether this dialog should be centered according it's parent. */
+ const bool m_fIsAutoCentering;
+ /** Holds whether this dialog is polished. */
+ bool m_fPolished;
+
+ /** Holds modal dialog's result code. */
+ int m_iResult;
+ /** Holds modal dialog's event-loop. */
+ QPointer<QEventLoop> m_pEventLoop;
+
+ /** Holds dialog's default button. */
+ QPointer<QPushButton> m_pDefaultButton;
+ /** Holds dialog's size-grip. */
+ QPointer<QSizeGrip> m_pSizeGrip;
+ /** Holds reject by escape flag. When true pressing escape rejects the dialog. Default is true.*/
+ bool m_fRejectByEscape;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIMainDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIManagerDialog.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIManagerDialog.cpp
new file mode 100644
index 00000000..e6519b9b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIManagerDialog.cpp
@@ -0,0 +1,257 @@
+/* $Id: QIManagerDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIManagerDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QMenuBar>
+#include <QPushButton>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIManagerDialog.h"
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIMessageCenter.h"
+#ifdef VBOX_WS_MAC
+# include "QIToolBar.h"
+# include "UIWindowMenuManager.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Class QIManagerDialogFactory implementation. *
+*********************************************************************************************************************************/
+
+void QIManagerDialogFactory::prepare(QIManagerDialog *&pDialog, QWidget *pCenterWidget /* = 0 */)
+{
+ create(pDialog, pCenterWidget);
+ pDialog->prepare();
+}
+
+void QIManagerDialogFactory::cleanup(QIManagerDialog *&pDialog)
+{
+ pDialog->cleanup();
+ pDialog->deleteLater();
+ pDialog = 0;
+}
+
+
+/*********************************************************************************************************************************
+* Class QIManagerDialog implementation. *
+*********************************************************************************************************************************/
+
+QIManagerDialog::QIManagerDialog(QWidget *pCenterWidget)
+ : m_pCenterWidget(pCenterWidget)
+ , m_fCloseEmitted(false)
+ , m_pWidget(0)
+#ifdef VBOX_WS_MAC
+ , m_pWidgetToolbar(0)
+#endif
+ , m_pButtonBox(0)
+{
+}
+
+void QIManagerDialog::closeEvent(QCloseEvent *pEvent)
+{
+ /* Ignore the event itself: */
+ pEvent->ignore();
+ /* But tell the listener to close us (once): */
+ if (!m_fCloseEmitted)
+ {
+ m_fCloseEmitted = true;
+ emit sigClose();
+ }
+}
+
+void QIManagerDialog::sltHandleHelpRequested()
+{
+ emit sigHelpRequested(uiCommon().helpKeyword(m_pWidget));
+}
+
+void QIManagerDialog::prepare()
+{
+ /* Tell the application we are not that important: */
+ setAttribute(Qt::WA_QuitOnClose, false);
+
+ /* Invent initial size: */
+ QSize proposedSize;
+ const int iHostScreen = UIDesktopWidgetWatchdog::screenNumber(m_pCenterWidget);
+ if (iHostScreen >= 0 && iHostScreen < UIDesktopWidgetWatchdog::screenCount())
+ {
+ /* On the basis of current host-screen geometry if possible: */
+ const QRect screenGeometry = gpDesktop->screenGeometry(iHostScreen);
+ if (screenGeometry.isValid())
+ proposedSize = screenGeometry.size() * 7 / 15;
+ }
+ /* Fallback to default size if we failed: */
+ if (proposedSize.isNull())
+ proposedSize = QSize(800, 600);
+ /* Resize to initial size: */
+ resize(proposedSize);
+
+ /* Configure: */
+ configure();
+
+ /* Prepare central-widget: */
+ prepareCentralWidget();
+ /* Prepare menu-bar: */
+ prepareMenuBar();
+#ifdef VBOX_WS_MAC
+ /* Prepare toolbar: */
+ prepareToolBar();
+#endif
+
+ /* Finalize: */
+ finalize();
+
+ /* Center according requested widget: */
+ gpDesktop->centerWidget(this, m_pCenterWidget, false);
+
+ /* Load the dialog's settings from extradata */
+ loadSettings();
+}
+
+void QIManagerDialog::prepareCentralWidget()
+{
+ /* Create central-widget: */
+ setCentralWidget(new QWidget);
+ AssertPtrReturnVoid(centralWidget());
+ {
+ /* Create main-layout: */
+ new QVBoxLayout(centralWidget());
+ AssertPtrReturnVoid(centralWidget()->layout());
+ {
+ /* Configure layout: */
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 2;
+ const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) / 2;
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) / 2;
+ const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) / 2;
+ centralWidget()->layout()->setContentsMargins(iL, iT, iR, iB);
+
+ /* Configure central-widget: */
+ configureCentralWidget();
+
+ /* Prepare button-box: */
+ prepareButtonBox();
+ }
+ }
+}
+
+void QIManagerDialog::prepareButtonBox()
+{
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ AssertPtrReturnVoid(m_pButtonBox);
+ {
+ /* Configure button-box: */
+#ifdef VBOX_WS_WIN
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Reset | QDialogButtonBox::Save | QDialogButtonBox::Close | QDialogButtonBox::Help);
+#else
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Reset | QDialogButtonBox::Apply | QDialogButtonBox::Close | QDialogButtonBox::Help);
+#endif
+ m_buttons[ButtonType_Reset] = m_pButtonBox->button(QDialogButtonBox::Reset);
+#ifdef VBOX_WS_WIN
+ m_buttons[ButtonType_Apply] = m_pButtonBox->button(QDialogButtonBox::Save);
+#else
+ m_buttons[ButtonType_Apply] = m_pButtonBox->button(QDialogButtonBox::Apply);
+#endif
+ m_buttons[ButtonType_Close] = m_pButtonBox->button(QDialogButtonBox::Close);
+ m_buttons[ButtonType_Help] = m_pButtonBox->button(QDialogButtonBox::Help);
+
+ /* Assign shortcuts: */
+ button(ButtonType_Close)->setShortcut(Qt::Key_Escape);
+ button(ButtonType_Help)->setShortcut(QKeySequence::HelpContents);
+
+ /* Hide 'Reset' and 'Apply' initially: */
+ button(ButtonType_Reset)->hide();
+ button(ButtonType_Apply)->hide();
+ /* Disable 'Reset' and 'Apply' initially: */
+ button(ButtonType_Reset)->setEnabled(false);
+ button(ButtonType_Apply)->setEnabled(false);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &QIManagerDialog::close);
+ /* Connections to enable the context sensitive help: */
+ connect(m_pButtonBox, &QDialogButtonBox::helpRequested, this, &QIManagerDialog::sltHandleHelpRequested);
+ connect(this, &QIManagerDialog::sigHelpRequested, &msgCenter(), &UIMessageCenter::sltHandleHelpRequestWithKeyword);
+
+ /* Configure button-box: */
+ configureButtonBox();
+
+ /* Add into layout: */
+ centralWidget()->layout()->addWidget(m_pButtonBox);
+ }
+}
+
+void QIManagerDialog::prepareMenuBar()
+{
+ /* Skip the call if there are no menus to add: */
+ if (m_widgetMenus.isEmpty())
+ return;
+
+ /* Add all the widget menus: */
+ foreach (QMenu *pMenu, m_widgetMenus)
+ menuBar()->addMenu(pMenu);
+
+#ifdef VBOX_WS_MAC
+ /* Prepare 'Window' menu: */
+ if (gpWindowMenuManager)
+ {
+ menuBar()->addMenu(gpWindowMenuManager->createMenu(this));
+ gpWindowMenuManager->addWindow(this);
+ }
+#endif
+}
+
+#ifdef VBOX_WS_MAC
+void QIManagerDialog::prepareToolBar()
+{
+ if (!m_pWidgetToolbar)
+ return;
+ /* Enable unified toolbar on macOS: */
+ addToolBar(m_pWidgetToolbar);
+ m_pWidgetToolbar->enableMacToolbar();
+}
+#endif
+
+void QIManagerDialog::cleanupMenuBar()
+{
+#ifdef VBOX_WS_MAC
+ /* Cleanup 'Window' menu: */
+ if (gpWindowMenuManager)
+ {
+ gpWindowMenuManager->removeWindow(this);
+ gpWindowMenuManager->destroyMenu(this);
+ }
+#endif
+}
+
+void QIManagerDialog::cleanup()
+{
+ saveSettings();
+ /* Cleanup menu-bar: */
+ cleanupMenuBar();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIManagerDialog.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIManagerDialog.h
new file mode 100644
index 00000000..4f583345
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIManagerDialog.h
@@ -0,0 +1,235 @@
+/* $Id: QIManagerDialog.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIManagerDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIManagerDialog_h
+#define FEQT_INCLUDED_SRC_extensions_QIManagerDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMainWindow>
+#include <QMap>
+
+/* GUI includes: */
+#include "QIWithRestorableGeometry.h"
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+/* Forward declarations: */
+class QPushButton;
+class QIDialogButtonBox;
+class QIManagerDialog;
+#ifdef VBOX_WS_MAC
+class QIToolBar;
+#endif
+
+
+/** Widget embedding type. */
+enum EmbedTo
+{
+ EmbedTo_Dialog,
+ EmbedTo_Stack
+};
+
+
+/** Dialog button types. */
+enum ButtonType
+{
+ ButtonType_Invalid = 0,
+ ButtonType_Reset = RT_BIT(0),
+ ButtonType_Apply = RT_BIT(1),
+ ButtonType_Close = RT_BIT(2),
+ ButtonType_Help = RT_BIT(3)
+};
+
+
+/** Manager dialog factory insterface. */
+class SHARED_LIBRARY_STUFF QIManagerDialogFactory
+{
+
+public:
+
+ /** Constructs Manager dialog factory. */
+ QIManagerDialogFactory() {}
+ /** Destructs Manager dialog factory. */
+ virtual ~QIManagerDialogFactory() {}
+
+ /** Prepares Manager dialog @a pDialog instance.
+ * @param pCenterWidget Brings the widget reference to center according to. */
+ virtual void prepare(QIManagerDialog *&pDialog, QWidget *pCenterWidget = 0);
+ /** Cleanups Manager dialog @a pDialog instance. */
+ virtual void cleanup(QIManagerDialog *&pDialog);
+
+protected:
+
+ /** Creates derived @a pDialog instance.
+ * @param pCenterWidget Brings the widget reference to center according to. */
+ virtual void create(QIManagerDialog *&pDialog, QWidget *pCenterWidget) = 0;
+};
+
+
+/** QMainWindow sub-class used as various manager dialogs. */
+class SHARED_LIBRARY_STUFF QIManagerDialog : public QIWithRestorableGeometry<QMainWindow>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about dialog should be closed. */
+ void sigClose();
+ /** Notifies listeners about help requested.
+ * @param strHelpKeyword Brings the tag to find related section in the manual. */
+ void sigHelpRequested(const QString &strHelpKeyword);
+
+protected:
+
+ /** Constructs Manager dialog.
+ * @param pCenterWidget Brings the widget reference to center according to. */
+ QIManagerDialog(QWidget *pCenterWidget);
+
+ /** @name Virtual prepare/cleanup cascade.
+ * @{ */
+ /** Configures all.
+ * @note Injected into prepare(), reimplement to configure all there. */
+ virtual void configure() {}
+ /** Configures central-widget.
+ * @note Injected into prepareCentralWidget(), reimplement to configure central-widget there. */
+ virtual void configureCentralWidget() {}
+ /** Configures button-box.
+ * @note Injected into prepareButtonBox(), reimplement to configure button-box there. */
+ virtual void configureButtonBox() {}
+ /** Performs final preparations.
+ * @note Injected into prepare(), reimplement to postprocess all there. */
+ virtual void finalize() {}
+ /** Loads dialog setting from extradata. */
+ virtual void loadSettings() {}
+
+ /** Saves dialog setting into extradata. */
+ virtual void saveSettings() {}
+ /** @} */
+
+ /** @name Widget stuff.
+ * @{ */
+ /** Defines the @a pWidget instance. */
+ void setWidget(QWidget *pWidget) { m_pWidget = pWidget; }
+ /** Defines the reference to widget menu, replacing current one. */
+ void setWidgetMenu(QMenu *pWidgetMenu) { m_widgetMenus = QList<QMenu*>() << pWidgetMenu; }
+ /** Defines the list of references to widget menus, replacing current one. */
+ void setWidgetMenus(QList<QMenu*> widgetMenus) { m_widgetMenus = widgetMenus; }
+#ifdef VBOX_WS_MAC
+ /** Defines the @a pWidgetToolbar instance. */
+ void setWidgetToolbar(QIToolBar *pWidgetToolbar) { m_pWidgetToolbar = pWidgetToolbar; }
+#endif
+
+ /** Returns the widget. */
+ virtual QWidget *widget() { return m_pWidget; }
+ /** Returns the button-box instance. */
+ QIDialogButtonBox *buttonBox() { return m_pButtonBox; }
+ /** Returns button of passed @a enmType. */
+ QPushButton *button(ButtonType enmType) { return m_buttons.value(enmType); }
+ /** Returns the widget reference to center manager dialog according. */
+ QWidget *centerWidget() const { return m_pCenterWidget; }
+ /** @} */
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles close @a pEvent. */
+ virtual void closeEvent(QCloseEvent *pEvent) RT_OVERRIDE;
+
+ /** Returns whether the manager had emitted command to be closed. */
+ bool closeEmitted() const { return m_fCloseEmitted; }
+ /** @} */
+
+private slots:
+
+ /** Handles help request. */
+ void sltHandleHelpRequested();
+
+private:
+
+ /** @name Private prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares central-widget. */
+ void prepareCentralWidget();
+ /** Prepares button-box. */
+ void prepareButtonBox();
+ /** Prepares menu-bar. */
+ void prepareMenuBar();
+#ifdef VBOX_WS_MAC
+ /** Prepares toolbar. */
+ void prepareToolBar();
+#endif
+
+ /** Cleanup menu-bar. */
+ void cleanupMenuBar();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the widget reference to center manager dialog according. */
+ QWidget *m_pCenterWidget;
+
+ /** Holds whether the manager had emitted command to be closed. */
+ bool m_fCloseEmitted;
+ /** @} */
+
+ /** @name Widget stuff.
+ * @{ */
+ /** Holds the widget instance. */
+ QWidget *m_pWidget;
+
+ /** Holds a list of widget menu references. */
+ QList<QMenu*> m_widgetMenus;
+
+#ifdef VBOX_WS_MAC
+ /** Holds the widget toolbar instance. */
+ QIToolBar *m_pWidgetToolbar;
+#endif
+ /** @} */
+
+ /** @name Button-box stuff.
+ * @{ */
+ /** Holds the dialog button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+
+ /** Holds the button-box button references. */
+ QMap<ButtonType, QPushButton*> m_buttons;
+ /** @} */
+
+ /** Allow factory access to private/protected members: */
+ friend class QIManagerDialogFactory;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIManagerDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIMenu.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIMenu.cpp
new file mode 100644
index 00000000..d8208fe9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIMenu.cpp
@@ -0,0 +1,46 @@
+/* $Id: QIMenu.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIMenu class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "QIMenu.h"
+
+
+QIMenu::QIMenu(QWidget *pParent /* = 0 */)
+ : QMenu(pParent)
+{
+}
+
+void QIMenu::sltHighlightFirstAction()
+{
+#ifdef VBOX_WS_WIN
+ /* Windows host requires window-activation: */
+ activateWindow();
+#endif
+
+ /* Focus next child: */
+ QMenu::focusNextChild();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIMenu.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIMenu.h
new file mode 100644
index 00000000..75a996bd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIMenu.h
@@ -0,0 +1,57 @@
+/* $Id: QIMenu.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIMenu class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIMenu_h
+#define FEQT_INCLUDED_SRC_extensions_QIMenu_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMenu>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QMenu extension with advanced functionality.
+ * Allows to highlight first menu item for popped up menu. */
+class SHARED_LIBRARY_STUFF QIMenu : public QMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs menu passing @a pParent to the base-class. */
+ QIMenu(QWidget *pParent = 0);
+
+private slots:
+
+ /** Highlights first menu action for popped up menu. */
+ void sltHighlightFirstAction();
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIMenu_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIMessageBox.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIMessageBox.cpp
new file mode 100644
index 00000000..7d74cf81
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIMessageBox.cpp
@@ -0,0 +1,428 @@
+/* $Id: QIMessageBox.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIMessageBox class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QClipboard>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QMimeData>
+#include <QPushButton>
+#include <QRegExp>
+#include <QRegularExpression>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIArrowSplitter.h"
+#include "QIDialogButtonBox.h"
+#include "QIMessageBox.h"
+#include "QIRichTextLabel.h"
+#include "UICommon.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+QIMessageBox::QIMessageBox(const QString &strTitle, const QString &strMessage, AlertIconType iconType,
+ int iButton1 /* = 0*/, int iButton2 /* = 0*/, int iButton3 /* = 0*/, QWidget *pParent /* = 0*/,
+ const QString &strHelpKeyword /* = QString() */)
+ : QIDialog(pParent)
+ , m_strTitle(strTitle)
+ , m_iconType(iconType)
+ , m_pLabelIcon(0)
+ , m_strMessage(strMessage)
+ , m_pLabelText(0)
+ , m_pFlagCheckBox(0)
+ , m_pDetailsContainer(0)
+ , m_iButton1(iButton1)
+ , m_iButton2(iButton2)
+ , m_iButton3(iButton3)
+ , m_iButtonEsc(0)
+ , m_pButton1(0)
+ , m_pButton2(0)
+ , m_pButton3(0)
+ , m_pButtonHelp(0)
+ , m_pButtonBox(0)
+ , m_strHelpKeyword(strHelpKeyword)
+ , m_fDone(false)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void QIMessageBox::setDetailsText(const QString &strText)
+{
+ /* Make sure details-text is NOT empty: */
+ AssertReturnVoid(!strText.isEmpty());
+
+ /* Split details into paragraphs: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList paragraphs(strText.split("<!--EOP-->", Qt::SkipEmptyParts));
+#else
+ QStringList paragraphs(strText.split("<!--EOP-->", QString::SkipEmptyParts));
+#endif
+ /* Make sure details-text has at least one paragraph: */
+ AssertReturnVoid(!paragraphs.isEmpty());
+
+ /* Enumerate all the paragraphs: */
+ QStringPairList details;
+ foreach (const QString &strParagraph, paragraphs)
+ {
+ /* Split each paragraph into pairs: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList parts(strParagraph.split("<!--EOM-->", Qt::KeepEmptyParts));
+#else
+ QStringList parts(strParagraph.split("<!--EOM-->", QString::KeepEmptyParts));
+#endif
+ /* Make sure each paragraph consist of 2 parts: */
+ AssertReturnVoid(parts.size() == 2);
+ /* Append each pair into details-list: */
+ details << QStringPair(parts[0], parts[1]);
+ }
+
+ /* Pass details-list to details-container: */
+ m_pDetailsContainer->setDetails(details);
+ /* Update details-container finally: */
+ updateDetailsContainer();
+}
+
+bool QIMessageBox::flagChecked() const
+{
+ return m_pFlagCheckBox->isChecked();
+}
+
+void QIMessageBox::setFlagChecked(bool fChecked)
+{
+ m_pFlagCheckBox->setChecked(fChecked);
+}
+
+void QIMessageBox::setFlagText(const QString &strFlagText)
+{
+ /* Pass text to flag check-box: */
+ m_pFlagCheckBox->setText(strFlagText);
+ /* Update flag check-box finally: */
+ updateCheckBox();
+}
+
+void QIMessageBox::setButtonText(int iButton, const QString &strText)
+{
+ switch (iButton)
+ {
+ case 0: if (m_pButton1) m_pButton1->setText(strText); break;
+ case 1: if (m_pButton2) m_pButton2->setText(strText); break;
+ case 2: if (m_pButton3) m_pButton3->setText(strText); break;
+ default: break;
+ }
+}
+
+void QIMessageBox::polishEvent(QShowEvent *pPolishEvent)
+{
+ /* Call to base-class: */
+ QIDialog::polishEvent(pPolishEvent);
+
+ /* Update size finally: */
+ sltUpdateSize();
+}
+
+void QIMessageBox::closeEvent(QCloseEvent *pCloseEvent)
+{
+ if (m_fDone)
+ pCloseEvent->accept();
+ else
+ {
+ pCloseEvent->ignore();
+ reject();
+ }
+}
+
+void QIMessageBox::sltUpdateSize()
+{
+ /* Fix minimum possible size: */
+ setFixedSize(minimumSizeHint());
+}
+
+void QIMessageBox::sltCopy() const
+{
+ /* Create the error string with all errors. First the html version. */
+ QString strError = "<html><body><p>" + m_strMessage + "</p>";
+ foreach (const QStringPair &pair, m_pDetailsContainer->details())
+ strError += pair.first + pair.second + "<br>";
+ strError += "</body></html>";
+ strError.remove(QRegularExpression("</+qt>"));
+ strError = strError.replace(QRegularExpression("&nbsp;"), " ");
+ /* Create a new mime data object holding both the html and the plain text version. */
+ QMimeData *pMimeData = new QMimeData();
+ pMimeData->setHtml(strError);
+ /* Replace all the html entities. */
+ strError = strError.replace(QRegularExpression("<br>|</tr>"), "\n");
+ strError = strError.replace(QRegularExpression("</p>"), "\n\n");
+ strError = strError.remove(QRegularExpression("<[^>]*>"));
+ pMimeData->setText(strError);
+ /* Add the mime data to the global clipboard. */
+ QClipboard *pClipboard = QApplication::clipboard();
+ pClipboard->setMimeData(pMimeData);
+}
+
+void QIMessageBox::reject()
+{
+ if (m_iButtonEsc)
+ {
+ QDialog::reject();
+ setResult(m_iButtonEsc & AlertButtonMask);
+ }
+}
+
+void QIMessageBox::prepare()
+{
+ /* Set caption: */
+ setWindowTitle(m_strTitle);
+
+ /* Create main-layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ AssertPtrReturnVoid(pMainLayout);
+ {
+ /* Configure main-layout: */
+#ifdef VBOX_WS_MAC
+ pMainLayout->setContentsMargins(40, 20, 40, 20);
+ pMainLayout->setSpacing(15);
+#else
+ pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 2);
+#endif
+ /* Create top-layout: */
+ QHBoxLayout *pTopLayout = new QHBoxLayout;
+ AssertPtrReturnVoid(pTopLayout);
+ {
+ /* Configure top-layout: */
+ pTopLayout->setContentsMargins(0, 0, 0, 0);
+ /* Create icon-label: */
+ m_pLabelIcon = new QLabel;
+ AssertPtrReturnVoid(m_pLabelIcon);
+ {
+ /* Configure icon-label: */
+ m_pLabelIcon->setPixmap(standardPixmap(m_iconType, this));
+ m_pLabelIcon->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
+ m_pLabelIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ /* Add icon-label into top-layout: */
+ pTopLayout->addWidget(m_pLabelIcon);
+ }
+ /* Create text-label: */
+ m_pLabelText = new QIRichTextLabel;
+ AssertPtrReturnVoid(m_pLabelText);
+ {
+ /* Configure text-label: */
+ m_pLabelText->setText(compressLongWords(m_strMessage));
+ /* Add text-label into top-layout: */
+ pTopLayout->addWidget(m_pLabelText);
+ }
+ /* Add top-layout into main-layout: */
+ pMainLayout->addLayout(pTopLayout);
+ }
+ /* Create details-container: */
+ m_pDetailsContainer = new QIArrowSplitter;
+ AssertPtrReturnVoid(m_pDetailsContainer);
+ {
+ /* Configure container: */
+ connect(m_pDetailsContainer, &QIArrowSplitter::sigSizeHintChange,
+ this, &QIMessageBox::sltUpdateSize);
+ /* Add details-container into main-layout: */
+ pMainLayout->addWidget(m_pDetailsContainer);
+ /* Update details-container finally: */
+ updateDetailsContainer();
+ }
+ /* Create flag check-box: */
+ m_pFlagCheckBox = new QCheckBox;
+ AssertPtrReturnVoid(m_pFlagCheckBox);
+ {
+ /* Configure flag check-box: */
+ m_pFlagCheckBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ /* Add flag check-box into main-layout: */
+ pMainLayout->addWidget(m_pFlagCheckBox, 0, Qt::AlignHCenter | Qt::AlignVCenter);
+ /* Update flag check-box finally: */
+ updateCheckBox();
+ }
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ AssertPtrReturnVoid(m_pButtonBox);
+ {
+ /* Configure button-box: */
+ m_pButtonBox->setCenterButtons(true);
+ m_pButton1 = createButton(m_iButton1);
+ if (m_pButton1)
+ connect(m_pButton1, &QPushButton::clicked, this, &QIMessageBox::sltDone1);
+ m_pButton2 = createButton(m_iButton2);
+ if (m_pButton2)
+ connect(m_pButton2, &QPushButton::clicked, this, &QIMessageBox::sltDone2);
+ m_pButton3 = createButton(m_iButton3);
+ if (m_pButton3)
+ connect(m_pButton3, &QPushButton::clicked, this, &QIMessageBox::sltDone3);
+ /* Create the help button and connect it to relevant slot in case a help word is supplied: */
+ if (!m_strHelpKeyword.isEmpty())
+ {
+ m_pButtonHelp = createButton(AlertButton_Help);
+ if (m_pButtonHelp)
+ {
+ uiCommon().setHelpKeyword(m_pButtonHelp, m_strHelpKeyword);
+ connect(m_pButtonHelp, &QPushButton::clicked, &msgCenter(), &UIMessageCenter::sltHandleHelpRequest);
+ }
+ }
+
+ /* Make sure Escape button always set: */
+ Assert(m_iButtonEsc);
+ /* If this is a critical message add a "Copy to clipboard" button: */
+ if (m_iconType == AlertIconType_Critical)
+ {
+ QPushButton *pCopyButton = createButton(AlertButton_Copy);
+ pCopyButton->setToolTip(tr("Copy all errors to the clipboard"));
+ connect(pCopyButton, &QPushButton::clicked, this, &QIMessageBox::sltCopy);
+ }
+ /* Add button-box into main-layout: */
+ pMainLayout->addWidget(m_pButtonBox);
+
+ /* Prepare focus. It is important to prepare focus after adding button-box to the layout as
+ * parenting the button-box to the QDialog changes default button focus by Qt: */
+ prepareFocus();
+ }
+ }
+}
+
+void QIMessageBox::prepareFocus()
+{
+ /* Configure default button and focus: */
+ if (m_pButton1 && (m_iButton1 & AlertButtonOption_Default))
+ {
+ m_pButton1->setDefault(true);
+ m_pButton1->setFocus();
+ }
+ if (m_pButton2 && (m_iButton2 & AlertButtonOption_Default))
+ {
+ m_pButton2->setDefault(true);
+ m_pButton2->setFocus();
+ }
+ if (m_pButton3 && (m_iButton3 & AlertButtonOption_Default))
+ {
+ m_pButton3->setDefault(true);
+ m_pButton3->setFocus();
+ }
+}
+
+QPushButton *QIMessageBox::createButton(int iButton)
+{
+ /* Not for AlertButton_NoButton: */
+ if (iButton == 0)
+ return 0;
+
+ /* Prepare button text & role: */
+ QString strText;
+ QDialogButtonBox::ButtonRole role;
+ switch (iButton & AlertButtonMask)
+ {
+ case AlertButton_Ok: strText = tr("OK"); role = QDialogButtonBox::AcceptRole; break;
+ case AlertButton_Cancel: strText = tr("Cancel"); role = QDialogButtonBox::RejectRole; break;
+ case AlertButton_Choice1: strText = tr("Yes"); role = QDialogButtonBox::YesRole; break;
+ case AlertButton_Choice2: strText = tr("No"); role = QDialogButtonBox::NoRole; break;
+ case AlertButton_Copy: strText = tr("Copy"); role = QDialogButtonBox::ActionRole; break;
+ case AlertButton_Help: strText = tr("Help"); role = QDialogButtonBox::HelpRole; break;
+ default:
+ AssertMsgFailed(("Type %d is not supported!", iButton));
+ return 0;
+ }
+
+ /* Create push-button: */
+ QPushButton *pButton = m_pButtonBox->addButton(strText, role);
+
+ /* Configure <escape> button: */
+ if (iButton & AlertButtonOption_Escape)
+ m_iButtonEsc = iButton & AlertButtonMask;
+
+ /* Return button: */
+ return pButton;
+}
+
+void QIMessageBox::updateDetailsContainer()
+{
+ /* Details-container with details is always visible: */
+ m_pDetailsContainer->setVisible(!m_pDetailsContainer->details().isEmpty());
+ /* Update size: */
+ sltUpdateSize();
+}
+
+void QIMessageBox::updateCheckBox()
+{
+ /* Flag check-box with text is always visible: */
+ m_pFlagCheckBox->setVisible(!m_pFlagCheckBox->text().isEmpty());
+ /* Update size: */
+ sltUpdateSize();
+}
+
+/* static */
+QPixmap QIMessageBox::standardPixmap(AlertIconType iconType, QWidget *pWidget /* = 0*/)
+{
+ /* Prepare standard icon: */
+ QIcon icon;
+ switch (iconType)
+ {
+ case AlertIconType_Information: icon = UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_MessageBoxInformation, pWidget); break;
+ case AlertIconType_Warning: icon = UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_MessageBoxWarning, pWidget); break;
+ case AlertIconType_Critical: icon = UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_MessageBoxCritical, pWidget); break;
+ case AlertIconType_Question: icon = UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_MessageBoxQuestion, pWidget); break;
+ case AlertIconType_GuruMeditation: icon = UIIconPool::iconSet(":/meditation_32px.png"); break;
+ default: break;
+ }
+ /* Return empty pixmap if nothing found: */
+ if (icon.isNull())
+ return QPixmap();
+ /* Return pixmap of standard size if possible: */
+ QStyle *pStyle = pWidget ? pWidget->style() : QApplication::style();
+ int iSize = pStyle->pixelMetric(QStyle::PM_MessageBoxIconSize, 0, pWidget);
+ return icon.pixmap(iSize, iSize);
+}
+
+/* static */
+QString QIMessageBox::compressLongWords(QString strText)
+{
+ // WORKAROUND:
+ // The idea is to compress long words of more than 100 symbols in size consisting of alphanumeric
+ // characters with ellipsiss using the following template:
+ // "[50 first symbols]...[50 last symbols]"
+ QRegExp re("[a-zA-Z0-9]{101,}");
+ int iPosition = re.indexIn(strText);
+ bool fChangeAllowed = iPosition != -1;
+ while (fChangeAllowed)
+ {
+ QString strNewText = strText;
+ const QString strFound = re.cap(0);
+ strNewText.replace(iPosition, strFound.size(), strFound.left(50) + "..." + strFound.right(50));
+ fChangeAllowed = fChangeAllowed && strText != strNewText;
+ strText = strNewText;
+ iPosition = re.indexIn(strText);
+ fChangeAllowed = fChangeAllowed && iPosition != -1;
+ }
+ return strText;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIMessageBox.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIMessageBox.h
new file mode 100644
index 00000000..91624dd4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIMessageBox.h
@@ -0,0 +1,221 @@
+/* $Id: QIMessageBox.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIMessageBox class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIMessageBox_h
+#define FEQT_INCLUDED_SRC_extensions_QIMessageBox_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMessageBox>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QLabel;
+class QPushButton;
+class QIArrowSplitter;
+class QIDialogButtonBox;
+class QIRichTextLabel;
+
+
+/** Button types. */
+enum AlertButton
+{
+ AlertButton_NoButton = 0x0, /* 00000000 00000000 */
+ AlertButton_Ok = 0x1, /* 00000000 00000001 */
+ AlertButton_Cancel = 0x2, /* 00000000 00000010 */
+ AlertButton_Choice1 = 0x4, /* 00000000 00000100 */
+ AlertButton_Choice2 = 0x8, /* 00000000 00001000 */
+ AlertButton_Copy = 0x10, /* 00000000 00010000 */
+ AlertButton_Help = 0x11, /* 00000000 00010001 */
+ AlertButtonMask = 0xFF /* 00000000 11111111 */
+};
+
+
+/** Button options. */
+enum AlertButtonOption
+{
+ AlertButtonOption_Default = 0x100, /* 00000001 00000000 */
+ AlertButtonOption_Escape = 0x200, /* 00000010 00000000 */
+ AlertButtonOptionMask = 0x300 /* 00000011 00000000 */
+};
+
+
+/** Alert options. */
+enum AlertOption
+{
+ AlertOption_AutoConfirmed = 0x400, /* 00000100 00000000 */
+ AlertOption_CheckBox = 0x800, /* 00001000 00000000 */
+ AlertOptionMask = 0xFC00 /* 11111100 00000000 */
+};
+
+
+/** Icon types. */
+enum AlertIconType
+{
+ AlertIconType_NoIcon = QMessageBox::NoIcon,
+ AlertIconType_Information = QMessageBox::Information,
+ AlertIconType_Warning = QMessageBox::Warning,
+ AlertIconType_Critical = QMessageBox::Critical,
+ AlertIconType_Question = QMessageBox::Question,
+ AlertIconType_GuruMeditation
+};
+
+
+/** QIDialog extension representing GUI alerts. */
+class SHARED_LIBRARY_STUFF QIMessageBox : public QIDialog
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs message-box passing @a pParent to the base-class.
+ * @param strTitle Brings the title.
+ * @param strMessage Brings the message.
+ * @param strMessage Brings the help keyword for context sensitive help
+ * @param iconType Brings the icon-type.
+ * @param iButton1 Brings the integer-code for the 1st button.
+ * @param iButton2 Brings the integer-code for the 2nd button.
+ * @param iButton3 Brings the integer-code for the 3rd button. */
+ QIMessageBox(const QString &strTitle, const QString &strMessage, AlertIconType iconType,
+ int iButton1 = 0, int iButton2 = 0, int iButton3 = 0, QWidget *pParent = 0,
+ const QString &strHelpKeyword = QString());
+
+ /** Defines details-text. */
+ void setDetailsText(const QString &strText);
+
+ /** Returns whether flag is checked. */
+ bool flagChecked() const;
+ /** Defines whether flag is @a fChecked. */
+ void setFlagChecked(bool fChecked);
+ /** Defines @a strFlagText. */
+ void setFlagText(const QString &strFlagText);
+
+ /** Defines @a iButton @a strText. */
+ void setButtonText(int iButton, const QString &strText);
+
+protected:
+
+ /** Handles polish @a pEvent. */
+ virtual void polishEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles close @a pEvent. */
+ virtual void closeEvent(QCloseEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Updates dialog size: */
+ void sltUpdateSize();
+
+ /** Copy details-text. */
+ void sltCopy() const;
+
+ /** Closes dialog like user would press the Cancel button. */
+ virtual void reject() RT_OVERRIDE;
+
+ /** Closes dialog like user would press the 1st button. */
+ void sltDone1() { m_fDone = true; done(m_iButton1 & AlertButtonMask); }
+ /** Closes dialog like user would press the 2nd button. */
+ void sltDone2() { m_fDone = true; done(m_iButton2 & AlertButtonMask); }
+ /** Closes dialog like user would press the 3rd button. */
+ void sltDone3() { m_fDone = true; done(m_iButton3 & AlertButtonMask); }
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Prepares focus. */
+ void prepareFocus();
+
+ /** Push-button factory. */
+ QPushButton *createButton(int iButton);
+
+ /** Visibility update routine for details-container. */
+ void updateDetailsContainer();
+ /** Visibility update routine for check-box. */
+ void updateCheckBox();
+
+ /** Generates standard pixmap for passed @a iconType using @a pWidget as hint. */
+ static QPixmap standardPixmap(AlertIconType iconType, QWidget *pWidget = 0);
+
+ /** Compresses @a strText with ellipsis on the basis of certain logic. */
+ static QString compressLongWords(QString strText);
+
+ /** Holds the title. */
+ QString m_strTitle;
+
+ /** Holds the icon-type. */
+ AlertIconType m_iconType;
+ /** Holds the icon-label instance. */
+ QLabel *m_pLabelIcon;
+
+ /** Holds the message. */
+ QString m_strMessage;
+ /** Holds the message-label instance. */
+ QIRichTextLabel *m_pLabelText;
+
+ /** Holds the flag check-box instance. */
+ QCheckBox *m_pFlagCheckBox;
+
+ /** Holds the flag details-container instance. */
+ QIArrowSplitter *m_pDetailsContainer;
+
+ /** Holds the integer-code for the 1st button. */
+ int m_iButton1;
+ /** Holds the integer-code for the 2nd button. */
+ int m_iButton2;
+ /** Holds the integer-code for the 3rd button. */
+ int m_iButton3;
+ /** Holds the integer-code of the cancel-button. */
+ int m_iButtonEsc;
+ /** Holds the 1st button instance. */
+ QPushButton *m_pButton1;
+ /** Holds the 2nd button instance. */
+ QPushButton *m_pButton2;
+ /** Holds the 3rd button instance. */
+ QPushButton *m_pButton3;
+ /** Holds the help-button instance. */
+ QPushButton *m_pButtonHelp;
+
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+
+ /** Holds the help keyword string. */
+ QString m_strHelpKeyword;
+
+ /** Defines whether message was accepted. */
+ bool m_fDone : 1;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIMessageBox_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIProcess.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIProcess.cpp
new file mode 100644
index 00000000..f7525dc6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIProcess.cpp
@@ -0,0 +1,70 @@
+/* $Id: QIProcess.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIProcess class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "QIProcess.h"
+
+/* External includes: */
+#ifdef VBOX_WS_X11
+# include <sys/wait.h>
+#endif
+
+
+QIProcess::QIProcess(QObject *pParent /* = 0 */)
+ : QProcess(pParent)
+{
+}
+
+/* static */
+QByteArray QIProcess::singleShot(const QString &strProcessName, int iTimeout /* = 5000 */)
+{
+ // WORKAROUND:
+ // Why is it really needed is because of Qt4.3 bug with QProcess.
+ // This bug is about QProcess sometimes (~70%) do not receive
+ // notification about process was finished, so this makes
+ // 'bool QProcess::waitForFinished (int)' block the GUI thread and
+ // never dismissed with 'true' result even if process was really
+ // started&finished. So we just waiting for some information
+ // on process output and destroy the process with force. Due to
+ // QProcess::~QProcess() has the same 'waitForFinished (int)' blocker
+ // we have to change process state to QProcess::NotRunning.
+
+ /// @todo Do we still need this?
+ QByteArray result;
+ QIProcess process;
+ process.start(strProcessName);
+ bool firstShotReady = process.waitForReadyRead(iTimeout);
+ if (firstShotReady)
+ result = process.readAllStandardOutput();
+ process.setProcessState(QProcess::NotRunning);
+#ifdef VBOX_WS_X11
+ int iStatus;
+ if (process.processId() > 0)
+ waitpid(process.processId(), &iStatus, 0);
+#endif /* VBOX_WS_X11 */
+ return result;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIProcess.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIProcess.h
new file mode 100644
index 00000000..d6767fa2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIProcess.h
@@ -0,0 +1,57 @@
+/* $Id: QIProcess.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIProcess class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIProcess_h
+#define FEQT_INCLUDED_SRC_extensions_QIProcess_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QProcess>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QProcess extension for VBox GUI needs. */
+class SHARED_LIBRARY_STUFF QIProcess : public QProcess
+{
+ Q_OBJECT;
+
+ /** Constructs our own file-dialog passing @a pParent to the base-class.
+ * Doesn't mean to be used directly, cause this subclass is a bunch of statics. */
+ QIProcess(QObject *pParent = 0);
+
+public:
+
+ /** Execute certain script specified by @a strProcessName
+ * and wait up to specified @a iTimeout amount of time for responce. */
+ static QByteArray singleShot(const QString &strProcessName,
+ int iTimeout = 5000);
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIProcess_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIRichTextLabel.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIRichTextLabel.cpp
new file mode 100644
index 00000000..5f0be248
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIRichTextLabel.cpp
@@ -0,0 +1,292 @@
+/* $Id: QIRichTextLabel.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIRichTextLabel class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QAction>
+#include <QApplication>
+#include <QClipboard>
+#include <QtMath>
+#include <QUrl>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+/* Forward declarations: */
+class QIRichTextLabel;
+
+
+/** QAccessibleObject extension used as an accessibility interface for QIRichTextLabel. */
+class UIAccessibilityInterfaceForQIRichTextLabel : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QIRichTextLabel accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QIRichTextLabel"))
+ return new UIAccessibilityInterfaceForQIRichTextLabel(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ UIAccessibilityInterfaceForQIRichTextLabel(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::StaticText)
+ {}
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QIRichTextLabel. */
+ QIRichTextLabel *label() const;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIAccessibilityInterfaceForQIRichTextLabel implementation. *
+*********************************************************************************************************************************/
+
+QString UIAccessibilityInterfaceForQIRichTextLabel::text(QAccessible::Text enmTextRole) const
+{
+ /* Make sure label still alive: */
+ AssertPtrReturn(label(), QString());
+
+ /* Return the description: */
+ if (enmTextRole == QAccessible::Description)
+ return label()->plainText();
+
+ /* Null-string by default: */
+ return QString();
+}
+
+QIRichTextLabel *UIAccessibilityInterfaceForQIRichTextLabel::label() const
+{
+ return qobject_cast<QIRichTextLabel*>(widget());
+}
+
+
+/*********************************************************************************************************************************
+* Class QIRichTextLabel implementation. *
+*********************************************************************************************************************************/
+
+QIRichTextLabel::QIRichTextLabel(QWidget *pParent)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pTextBrowser()
+ , m_pActionCopy(0)
+ , m_fCopyAvailable(false)
+ , m_iMinimumTextWidth(0)
+{
+ /* Install QIRichTextLabel accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForQIRichTextLabel::pFactory);
+
+ /* Configure self: */
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+
+ /* Create main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Configure layout: */
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create text-browser: */
+ m_pTextBrowser = new QTextBrowser;
+ if (m_pTextBrowser)
+ {
+ /* Configure text-browser: */
+ m_pTextBrowser->setReadOnly(true);
+ m_pTextBrowser->setFocusPolicy(Qt::ClickFocus);
+ m_pTextBrowser->setFrameShape(QFrame::NoFrame);
+ m_pTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTextBrowser->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ m_pTextBrowser->setContextMenuPolicy(Qt::ActionsContextMenu);
+ m_pTextBrowser->setOpenExternalLinks(true);
+
+ /* Tune text-browser viewport palette: */
+ m_pTextBrowser->viewport()->setAutoFillBackground(false);
+ QPalette pal = m_pTextBrowser->viewport()->palette();
+ pal.setColor(QPalette::Active, QPalette::Text, pal.color(QPalette::Active, QPalette::WindowText));
+ pal.setColor(QPalette::Inactive, QPalette::Text, pal.color(QPalette::Inactive, QPalette::WindowText));
+ pal.setColor(QPalette::Disabled, QPalette::Text, pal.color(QPalette::Disabled, QPalette::WindowText));
+ m_pTextBrowser->viewport()->setPalette(pal);
+
+ /* Setup connections finally: */
+ connect(m_pTextBrowser, &QTextBrowser::anchorClicked, this, &QIRichTextLabel::sigLinkClicked);
+ connect(m_pTextBrowser, &QTextBrowser::copyAvailable, this, &QIRichTextLabel::sltHandleCopyAvailable);
+
+ /* Create context-menu copy action for text-browser: */
+ m_pActionCopy = new QAction(m_pTextBrowser);
+ if (m_pActionCopy)
+ {
+ m_pActionCopy->setShortcut(QKeySequence(QKeySequence::Copy));
+ m_pActionCopy->setShortcutContext(Qt::WidgetShortcut);
+ connect(m_pActionCopy, &QAction::triggered, this, &QIRichTextLabel::copy);
+ m_pTextBrowser->addAction(m_pActionCopy);
+ }
+ }
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pTextBrowser);
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+QString QIRichTextLabel::text() const
+{
+ return m_pTextBrowser->toHtml();
+}
+
+QString QIRichTextLabel::plainText() const
+{
+ return m_pTextBrowser->toPlainText();
+}
+
+void QIRichTextLabel::registerImage(const QImage &image, const QString &strName)
+{
+ m_pTextBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(strName), QVariant(image));
+}
+
+void QIRichTextLabel::registerPixmap(const QPixmap &pixmap, const QString &strName)
+{
+ m_pTextBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(strName), QVariant(pixmap));
+}
+
+QTextOption::WrapMode QIRichTextLabel::wordWrapMode() const
+{
+ return m_pTextBrowser->wordWrapMode();
+}
+
+void QIRichTextLabel::setWordWrapMode(QTextOption::WrapMode policy)
+{
+ m_pTextBrowser->setWordWrapMode(policy);
+}
+
+void QIRichTextLabel::installEventFilter(QObject *pFilterObj)
+{
+ QWidget::installEventFilter(pFilterObj);
+ m_pTextBrowser->installEventFilter(pFilterObj);
+}
+
+QFont QIRichTextLabel::browserFont() const
+{
+ return m_pTextBrowser->font();
+}
+
+void QIRichTextLabel::setBrowserFont(const QFont &newFont)
+{
+ m_pTextBrowser->setFont(newFont);
+}
+
+int QIRichTextLabel::minimumTextWidth() const
+{
+ return m_iMinimumTextWidth;
+}
+
+void QIRichTextLabel::setMinimumTextWidth(int iMinimumTextWidth)
+{
+ /* Remember minimum text width: */
+ m_iMinimumTextWidth = iMinimumTextWidth;
+
+ /* Get corresponding QTextDocument: */
+ QTextDocument *pTextDocument = m_pTextBrowser->document();
+ /* Bug in QTextDocument (?) : setTextWidth doesn't work from the first time. */
+ for (int iTry = 0; pTextDocument->textWidth() != m_iMinimumTextWidth && iTry < 3; ++iTry)
+ pTextDocument->setTextWidth(m_iMinimumTextWidth);
+ /* Get corresponding QTextDocument size: */
+ QSize size = pTextDocument->size().toSize();
+
+ /* Resize to content size: */
+ m_pTextBrowser->setMinimumSize(size);
+ layout()->activate();
+}
+
+void QIRichTextLabel::setText(const QString &strText)
+{
+ /* Set text: */
+ m_pTextBrowser->setHtml(strText);
+
+ /* Get corresponding QTextDocument: */
+ QTextDocument *pTextDocument = m_pTextBrowser->document();
+
+ // WORKAROUND:
+ // Ok, here is the trick. In Qt 5.6.x initial QTextDocument size is always 0x0
+ // even if contents present. To make QTextDocument calculate initial size we
+ // need to pass it some initial text-width, that way size should be calualated
+ // on the basis of passed width. No idea why but in Qt 5.6.x first calculated
+ // size doesn't actually linked to initially passed text-width, somehow it
+ // always have 640px width and various height which depends on currently set
+ // contents. So, we just using 640px as initial text-width.
+ pTextDocument->setTextWidth(640);
+
+ /* Now get that initial size which is 640xY, and propose new text-width as 4/3
+ * of hypothetical width current content would have laid out as square: */
+ const QSize oldSize = pTextDocument->size().toSize();
+ const int iProposedWidth = qSqrt(oldSize.width() * oldSize.height()) * 4 / 3;
+ pTextDocument->setTextWidth(iProposedWidth);
+
+ /* Get effective QTextDocument size: */
+ const QSize newSize = pTextDocument->size().toSize();
+
+ /* Set minimum text width to corresponding value: */
+ setMinimumTextWidth(m_iMinimumTextWidth == 0 ? newSize.width() : m_iMinimumTextWidth);
+}
+
+void QIRichTextLabel::copy()
+{
+ // WORKAROUND:
+ // We should distinguish whether copy() is available or not.
+ // If it is, we can use QTextBrowser::copy() directly to
+ // copy selected part of text. Otherwise we have to use
+ // QTextBrowser::toPlainText() to get the whole desirable
+ // text and put it to QClipboard ourselves.
+ if (m_fCopyAvailable)
+ m_pTextBrowser->copy();
+ else
+ {
+ /* Copy the current text to the global and selection clipboards: */
+ const QString strText = m_pTextBrowser->toPlainText();
+ QApplication::clipboard()->setText(strText, QClipboard::Clipboard);
+ QApplication::clipboard()->setText(strText, QClipboard::Selection);
+ }
+}
+
+void QIRichTextLabel::retranslateUi()
+{
+ if (m_pActionCopy)
+ m_pActionCopy->setText(tr("&Copy"));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIRichTextLabel.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIRichTextLabel.h
new file mode 100644
index 00000000..b56b6422
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIRichTextLabel.h
@@ -0,0 +1,123 @@
+/* $Id: QIRichTextLabel.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIRichTextLabel class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIRichTextLabel_h
+#define FEQT_INCLUDED_SRC_extensions_QIRichTextLabel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTextBrowser>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QAction;
+
+/** QLabel analog to reflect rich-text,
+ ** based on private QTextBrowser functionality. */
+class SHARED_LIBRARY_STUFF QIRichTextLabel : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+ Q_PROPERTY(QString text READ text WRITE setText);
+
+signals:
+
+ /** Notifies listeners about @a link clicked. */
+ void sigLinkClicked(const QUrl &link);
+
+public:
+
+ /** Constructs rich text-label passing @a pParent to the base-class. */
+ QIRichTextLabel(QWidget *pParent = 0);
+
+ /** Returns text. */
+ QString text() const;
+ /** Returns plain text. */
+ QString plainText() const;
+
+ /** Registers @a image under a passed @a strName. */
+ void registerImage(const QImage &image, const QString &strName);
+ /** Registers @a pixmap under a passed @a strName. */
+ void registerPixmap(const QPixmap &pixmap, const QString &strName);
+
+ /** Returns word wrapping policy. */
+ QTextOption::WrapMode wordWrapMode() const;
+ /** Defines word wrapping @a policy. */
+ void setWordWrapMode(QTextOption::WrapMode policy);
+
+ /** Installs event filter for a passed @ pFilterObj. */
+ void installEventFilter(QObject *pFilterObj);
+
+ /** Returns browser font. */
+ QFont browserFont() const;
+ /** Defines @a newFont for browser. */
+ void setBrowserFont(const QFont &newFont);
+
+public slots:
+
+ /** Returns minimum text width. */
+ int minimumTextWidth() const;
+ /** Defines @a iMinimumTextWidth. */
+ void setMinimumTextWidth(int iMinimumTextWidth);
+
+ /** Defines @a strText. */
+ void setText(const QString &strText);
+
+ /** Copies text-browser text into clipboard. */
+ void copy();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles the fact of text-browser text copy available.
+ * @param fYes Brings whether some text is selected and can
+ * be copied directly by QTextBrowser::copy() call. */
+ void sltHandleCopyAvailable(bool fYes) { m_fCopyAvailable = fYes; }
+
+private:
+
+ /** Holds the text-browser instance. */
+ QTextBrowser *m_pTextBrowser;
+
+ /** Holds the context-menu Copy action instance. */
+ QAction *m_pActionCopy;
+ /** Holds whether text-browser text copy is available. */
+ bool m_fCopyAvailable;
+
+ /** Holds the minimum text-width. */
+ int m_iMinimumTextWidth;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIRichTextLabel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIRichToolButton.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIRichToolButton.cpp
new file mode 100644
index 00000000..e6106a22
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIRichToolButton.cpp
@@ -0,0 +1,142 @@
+/* $Id: QIRichToolButton.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIRichToolButton class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QKeyEvent>
+#include <QLabel>
+#include <QStyleOptionFocusRect>
+#include <QStylePainter>
+
+/* GUI includes: */
+#include "QIRichToolButton.h"
+#include "QIToolButton.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+QIRichToolButton::QIRichToolButton(QWidget *pParent)
+ : QWidget(pParent)
+ , m_pButton(0)
+ , m_pLabel(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void QIRichToolButton::setIconSize(const QSize &iconSize)
+{
+ m_pButton->setIconSize(iconSize);
+}
+
+void QIRichToolButton::setIcon(const QIcon &icon)
+{
+ m_pButton->setIcon(icon);
+}
+
+void QIRichToolButton::animateClick()
+{
+ m_pButton->animateClick();
+}
+
+void QIRichToolButton::setText(const QString &strText)
+{
+ m_pLabel->setText(strText);
+}
+
+void QIRichToolButton::paintEvent(QPaintEvent *pEvent)
+{
+ /* Draw focus around whole button if focused: */
+ if (hasFocus())
+ {
+ QStylePainter painter(this);
+ QStyleOptionFocusRect option;
+ option.initFrom(this);
+ option.rect = geometry();
+ painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
+ }
+ /* Call to base-class: */
+ QWidget::paintEvent(pEvent);
+}
+
+void QIRichToolButton::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Handle different keys: */
+ switch (pEvent->key())
+ {
+ /* Animate-click for the Space key: */
+ case Qt::Key_Space: return animateClick();
+ default: break;
+ }
+ /* Call to base-class: */
+ QWidget::keyPressEvent(pEvent);
+}
+
+void QIRichToolButton::mousePressEvent(QMouseEvent *pEvent)
+{
+ NOREF(pEvent);
+ /* Animate-click: */
+ animateClick();
+}
+
+void QIRichToolButton::prepare()
+{
+ /* Enable string focus: */
+ setFocusPolicy(Qt::StrongFocus);
+
+ /* Create main-layout: */
+ QHBoxLayout *pMainLayout = new QHBoxLayout(this);
+ AssertPtrReturnVoid(pMainLayout);
+ {
+ /* Configure main-layout: */
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+ pMainLayout->setSpacing(0);
+ /* Create tool-button: */
+ m_pButton = new QIToolButton;
+ AssertPtrReturnVoid(m_pButton);
+ {
+ /* Configure tool-button: */
+ m_pButton->removeBorder();
+ m_pButton->setFocusPolicy(Qt::NoFocus);
+ connect(m_pButton, &QIToolButton::clicked, this, &QIRichToolButton::sltButtonClicked);
+ connect(m_pButton, &QIToolButton::clicked, this, &QIRichToolButton::sigClicked);
+ /* Add tool-button into main-layout: */
+ pMainLayout->addWidget(m_pButton);
+ }
+ /* Create text-label: */
+ m_pLabel = new QLabel;
+ AssertPtrReturnVoid(m_pLabel);
+ {
+ /* Configure text-label: */
+ m_pLabel->setBuddy(m_pButton);
+ m_pLabel->setStyleSheet("QLabel {padding: 2px 0px 2px 0px;}");
+ /* Add text-label into main-layout: */
+ pMainLayout->addWidget(m_pLabel);
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIRichToolButton.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIRichToolButton.h
new file mode 100644
index 00000000..58ef89f2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIRichToolButton.h
@@ -0,0 +1,98 @@
+/* $Id: QIRichToolButton.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIRichToolButton class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIRichToolButton_h
+#define FEQT_INCLUDED_SRC_extensions_QIRichToolButton_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class QString;
+class QIToolButton;
+
+/** QWidget extension
+ * representing tool-button with separate text-label. */
+class SHARED_LIBRARY_STUFF QIRichToolButton : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about button click. */
+ void sigClicked();
+
+public:
+
+ /** Constructs rich tool-button passing @a pParent to the base-class. */
+ QIRichToolButton(QWidget *pParent = 0);
+
+ /** Defines tool-button @a iconSize. */
+ void setIconSize(const QSize &iconSize);
+ /** Defines tool-button @a icon. */
+ void setIcon(const QIcon &icon);
+ /** Animates tool-button click. */
+ void animateClick();
+
+ /** Defines text-label @a strText. */
+ void setText(const QString &strText);
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+ /** Handles key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-press @a pEvent. */
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+
+protected slots:
+
+ /** Handles button-click. */
+ virtual void sltButtonClicked() {}
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the tool-button instance. */
+ QIToolButton *m_pButton;
+ /** Holds the text-label instance. */
+ QLabel *m_pLabel;
+ /** Holds the text for text-label instance. */
+ QString m_strName;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIRichToolButton_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QISplitter.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QISplitter.cpp
new file mode 100644
index 00000000..b0cb7f06
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QISplitter.cpp
@@ -0,0 +1,428 @@
+/* $Id: QISplitter.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QISplitter class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QEvent>
+#include <QPainter>
+#include <QPaintEvent>
+
+/* GUI includes: */
+#include "QISplitter.h"
+#ifdef VBOX_WS_MAC
+# include "UICursor.h"
+#endif
+
+
+/** QSplitterHandle subclass representing flat line. */
+class QIFlatSplitterHandle : public QSplitterHandle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs flat splitter handle passing @a enmOrientation and @a pParent to the base-class. */
+ QIFlatSplitterHandle(Qt::Orientation enmOrientation, QISplitter *pParent);
+
+ /** Defines @a color. */
+ void configureColor(const QColor &color);
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Holds the main color. */
+ QColor m_color;
+};
+
+
+/** QSplitterHandle subclass representing shaded line. */
+class QIShadeSplitterHandle : public QSplitterHandle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs shaded splitter handle passing @a enmOrientation and @a pParent to the base-class. */
+ QIShadeSplitterHandle(Qt::Orientation enmOrientation, QISplitter *pParent);
+
+ /** Defines colors to passed @a color1 and @a color2. */
+ void configureColors(const QColor &color1, const QColor &color2);
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Holds the main color. */
+ QColor m_color;
+ /** Holds the color1. */
+ QColor m_color1;
+ /** Holds the color2. */
+ QColor m_color2;
+};
+
+
+#ifdef VBOX_WS_MAC
+/** QSplitterHandle subclass representing shaded line for macOS. */
+class QIDarwinSplitterHandle : public QSplitterHandle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs shaded splitter handle passing @a enmOrientation and @a pParent to the base-class. */
+ QIDarwinSplitterHandle(Qt::Orientation enmOrientation, QISplitter *pParent);
+
+ /** Returns size-hint. */
+ QSize sizeHint() const;
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+};
+#endif /* VBOX_WS_MAC */
+
+
+/*********************************************************************************************************************************
+* Class QIFlatSplitterHandle implementation. *
+*********************************************************************************************************************************/
+
+QIFlatSplitterHandle::QIFlatSplitterHandle(Qt::Orientation enmOrientation, QISplitter *pParent)
+ : QSplitterHandle(enmOrientation, pParent)
+{
+}
+
+void QIFlatSplitterHandle::configureColor(const QColor &color)
+{
+ m_color = color;
+ update();
+}
+
+void QIFlatSplitterHandle::paintEvent(QPaintEvent *pEvent)
+{
+ QPainter painter(this);
+ painter.fillRect(pEvent->rect(), m_color);
+}
+
+
+/*********************************************************************************************************************************
+* Class QIShadeSplitterHandle implementation. *
+*********************************************************************************************************************************/
+
+QIShadeSplitterHandle::QIShadeSplitterHandle(Qt::Orientation enmOrientation, QISplitter *pParent)
+ : QSplitterHandle(enmOrientation, pParent)
+{
+ QColor windowColor = QApplication::palette().color(QPalette::Active, QPalette::Window);
+ QColor frameColor = QApplication::palette().color(QPalette::Active, QPalette::Text);
+ frameColor.setAlpha(100);
+ m_color1 = windowColor;
+ m_color2 = windowColor;
+ m_color = frameColor;
+}
+
+void QIShadeSplitterHandle::configureColors(const QColor &color1, const QColor &color2)
+{
+ m_color1 = color1;
+ m_color2 = color2;
+ update();
+}
+
+void QIShadeSplitterHandle::paintEvent(QPaintEvent *pEvent)
+{
+ QPainter painter(this);
+ QLinearGradient gradient;
+ QGradientStop point1(0, m_color1);
+ QGradientStop point2(0.5, m_color);
+ QGradientStop point3(1, m_color2);
+ QGradientStops stops;
+ stops << point1 << point2 << point3;
+ gradient.setStops(stops);
+ if (orientation() == Qt::Horizontal)
+ {
+ gradient.setStart(rect().left() + 1, 0);
+ gradient.setFinalStop(rect().right(), 0);
+ }
+ else
+ {
+ gradient.setStart(0, rect().top() + 1);
+ gradient.setFinalStop(0, rect().bottom());
+ }
+ painter.fillRect(pEvent->rect(), gradient);
+}
+
+
+/*********************************************************************************************************************************
+* Class QIDarwinSplitterHandle implementation. *
+*********************************************************************************************************************************/
+
+#ifdef VBOX_WS_MAC
+
+QIDarwinSplitterHandle::QIDarwinSplitterHandle(Qt::Orientation enmOrientation, QISplitter *pParent)
+ : QSplitterHandle(enmOrientation, pParent)
+{
+}
+
+QSize QIDarwinSplitterHandle::sizeHint() const
+{
+ QSize parent = QSplitterHandle::sizeHint();
+ if (orientation() == Qt::Vertical)
+ return parent + QSize(0, 3);
+ else
+ return QSize(1, parent.height());
+}
+
+void QIDarwinSplitterHandle::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+
+ QColor topColor(145, 145, 145);
+ QColor bottomColor(142, 142, 142);
+ QColor gradientStart(252, 252, 252);
+ QColor gradientStop(223, 223, 223);
+
+ if (orientation() == Qt::Vertical)
+ {
+ painter.setPen(topColor);
+ painter.drawLine(0, 0, width(), 0);
+ painter.setPen(bottomColor);
+ painter.drawLine(0, height() - 1, width(), height() - 1);
+
+ QLinearGradient linearGrad(QPointF(0, 0), QPointF(0, height() -3));
+ linearGrad.setColorAt(0, gradientStart);
+ linearGrad.setColorAt(1, gradientStop);
+ painter.fillRect(QRect(QPoint(0,1), size() - QSize(0, 2)), QBrush(linearGrad));
+ }
+ else
+ {
+ painter.setPen(topColor);
+ painter.drawLine(0, 0, 0, height());
+ }
+}
+
+#endif /* VBOX_WS_MAC */
+
+
+/*********************************************************************************************************************************
+* Class QISplitter implementation. *
+*********************************************************************************************************************************/
+
+QISplitter::QISplitter(QWidget *pParent /* = 0 */)
+ : QSplitter(pParent)
+ , m_enmType(Shade)
+ , m_fPolished(false)
+#ifdef VBOX_WS_MAC
+ , m_fHandleGrabbed(false)
+#endif
+{
+ qApp->installEventFilter(this);
+}
+
+QISplitter::QISplitter(Qt::Orientation enmOrientation, Type enmType, QWidget *pParent /* = 0 */)
+ : QSplitter(enmOrientation, pParent)
+ , m_enmType(enmType)
+ , m_fPolished(false)
+#ifdef VBOX_WS_MAC
+ , m_fHandleGrabbed(false)
+#endif
+{
+ qApp->installEventFilter(this);
+}
+
+void QISplitter::configureColor(const QColor &color)
+{
+ m_color = color;
+ for (int i = 1; i < count(); ++i)
+ {
+ QIFlatSplitterHandle *pHandle = qobject_cast<QIFlatSplitterHandle*>(handle(i));
+ if (pHandle && m_color.isValid())
+ pHandle->configureColor(m_color);
+ }
+}
+
+void QISplitter::configureColors(const QColor &color1, const QColor &color2)
+{
+ m_color1 = color1; m_color2 = color2;
+ for (int i = 1; i < count(); ++i)
+ {
+ QIShadeSplitterHandle *pHandle = qobject_cast<QIShadeSplitterHandle*>(handle(i));
+ if (pHandle && m_color1.isValid() && m_color2.isValid())
+ pHandle->configureColors(m_color1, m_color2);
+ }
+}
+
+bool QISplitter::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* Handles events for handle: */
+ if (pWatched == handle(1))
+ {
+ switch (pEvent->type())
+ {
+ /* Restore default position on double-click: */
+ case QEvent::MouseButtonDblClick:
+ restoreState(m_baseState);
+ break;
+ default:
+ break;
+ }
+ }
+
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // Special handling on the Mac. Cause there the horizontal handle is only 1
+ // pixel wide, its hard to catch. Therefor we make some invisible area
+ // around the handle and forwarding the mouse events to the handle, if the
+ // user presses the left mouse button.
+ else if ( m_enmType == Native
+ && orientation() == Qt::Horizontal
+ && count() > 1
+ && qApp->activeWindow() == window())
+ {
+ switch (pEvent->type())
+ {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseMove:
+ {
+ const int margin = 3;
+ QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
+ for (int i=1; i < count(); ++i)
+ {
+ QWidget *pHandle = handle(i);
+ if ( pHandle
+ && pHandle != pWatched)
+ {
+ /* Check if we hit the handle */
+ bool fMarginHit = QRect(pHandle->mapToGlobal(QPoint(0, 0)), pHandle->size()).adjusted(-margin, 0, margin, 0).contains(pMouseEvent->globalPos());
+ if (pEvent->type() == QEvent::MouseButtonPress)
+ {
+ /* If we have a handle position hit and the left button is pressed, start the grabbing. */
+ if ( fMarginHit
+ && pMouseEvent->buttons().testFlag(Qt::LeftButton))
+ {
+ m_fHandleGrabbed = true;
+ UICursor::setCursor(this, Qt::SplitHCursor);
+ qApp->postEvent(pHandle, new QMouseEvent(pMouseEvent->type(),
+ pHandle->mapFromGlobal(pMouseEvent->globalPos()),
+ pMouseEvent->button(),
+ pMouseEvent->buttons(),
+ pMouseEvent->modifiers()));
+ return true;
+ }
+ }
+ else if (pEvent->type() == QEvent::MouseMove)
+ {
+ /* If we are in the near of the handle or currently dragging, forward the mouse event. */
+ if ( fMarginHit
+ || ( m_fHandleGrabbed
+ && pMouseEvent->buttons().testFlag(Qt::LeftButton)))
+ {
+ UICursor::setCursor(this, Qt::SplitHCursor);
+ qApp->postEvent(pHandle, new QMouseEvent(pMouseEvent->type(),
+ pHandle->mapFromGlobal(pMouseEvent->globalPos()),
+ pMouseEvent->button(),
+ pMouseEvent->buttons(),
+ pMouseEvent->modifiers()));
+ return true;
+ }
+
+ /* If not, reset the state. */
+ m_fHandleGrabbed = false;
+ UICursor::setCursor(this, Qt::ArrowCursor);
+ }
+ }
+ }
+ break;
+ }
+ case QEvent::WindowDeactivate:
+ case QEvent::MouseButtonRelease:
+ {
+ m_fHandleGrabbed = false;
+ UICursor::setCursor(this, Qt::ArrowCursor);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+#endif /* VBOX_WS_MAC */
+
+ /* Call to base-class: */
+ return QSplitter::eventFilter(pWatched, pEvent);
+}
+
+void QISplitter::showEvent(QShowEvent *pEvent)
+{
+ /* Remember default position: */
+ if (!m_fPolished)
+ {
+ m_fPolished = true;
+ m_baseState = saveState();
+ }
+
+ /* Call to base-class: */
+ return QSplitter::showEvent(pEvent);
+}
+
+QSplitterHandle *QISplitter::createHandle()
+{
+ /* Create native handle: */
+ switch (m_enmType)
+ {
+ case Flat:
+ {
+ QIFlatSplitterHandle *pHandle = new QIFlatSplitterHandle(orientation(), this);
+ if (m_color.isValid())
+ pHandle->configureColor(m_color);
+ return pHandle;
+ }
+ case Shade:
+ {
+ QIShadeSplitterHandle *pHandle = new QIShadeSplitterHandle(orientation(), this);
+ if (m_color1.isValid() && m_color2.isValid())
+ pHandle->configureColors(m_color1, m_color2);
+ return pHandle;
+ }
+ case Native:
+ {
+#ifdef VBOX_WS_MAC
+ return new QIDarwinSplitterHandle(orientation(), this);
+#else
+ return new QSplitterHandle(orientation(), this);
+#endif
+ }
+ }
+ return 0;
+}
+
+
+#include "QISplitter.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QISplitter.h b/src/VBox/Frontends/VirtualBox/src/extensions/QISplitter.h
new file mode 100644
index 00000000..4834d061
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QISplitter.h
@@ -0,0 +1,98 @@
+/* $Id: QISplitter.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QISplitter class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QISplitter_h
+#define FEQT_INCLUDED_SRC_extensions_QISplitter_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSplitter>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QSplitterHandle;
+
+/** QSplitter subclass with extended functionality. */
+class SHARED_LIBRARY_STUFF QISplitter : public QSplitter
+{
+ Q_OBJECT;
+
+public:
+
+ /** Handle types. */
+ enum Type { Flat, Shade, Native };
+
+ /** Constructs splitter passing @a pParent to the base-class. */
+ QISplitter(QWidget *pParent = 0);
+ /** Constructs splitter passing @a enmOrientation and @a pParent to the base-class.
+ * @param enmType Brings the splitter handle type. */
+ QISplitter(Qt::Orientation enmOrientation, Type enmType, QWidget *pParent = 0);
+
+ /** Configure custom color defined as @a color. */
+ void configureColor(const QColor &color);
+ /** Configure custom colors defined as @a color1 and @a color2. */
+ void configureColors(const QColor &color1, const QColor &color2);
+
+protected:
+
+ /** Preprocesses Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ void showEvent(QShowEvent *pEvent);
+
+ /** Creates handle. */
+ QSplitterHandle *createHandle();
+
+private:
+
+ /** Holds the serialized base-state. */
+ QByteArray m_baseState;
+
+ /** Holds the handle type. */
+ Type m_enmType;
+
+ /** Holds whether the splitter is polished. */
+ bool m_fPolished : 1;
+#ifdef VBOX_WS_MAC
+ /** Holds whether handle is grabbed. */
+ bool m_fHandleGrabbed : 1;
+#endif
+
+ /** Holds color. */
+ QColor m_color;
+ /** Holds color1. */
+ QColor m_color1;
+ /** Holds color2. */
+ QColor m_color2;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QISplitter_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBar.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBar.cpp
new file mode 100644
index 00000000..cd320ede
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBar.cpp
@@ -0,0 +1,80 @@
+/* $Id: QIStatusBar.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIStatusBar class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+
+/* GUI includes: */
+#include "QIStatusBar.h"
+
+
+/** QAccessibleWidget extension used as an accessibility interface for QIStatusBar. */
+class QIAccessibilityInterfaceForQIStatusBar : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QIStatusBar accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QIStatusBar"))
+ return new QIAccessibilityInterfaceForQIStatusBar(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ QIAccessibilityInterfaceForQIStatusBar(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::ToolBar)
+ {
+ // We are not interested in status-bar text as it's a mean of
+ // accessibility in case when accessibility is disabled.
+ // Since accessibility is enabled in our case, we wish
+ // to pass control token to our sub-elements.
+ // So we are using QAccessible::ToolBar.
+ }
+};
+
+
+/*********************************************************************************************************************************
+* Class QIStatusBar implementation. *
+*********************************************************************************************************************************/
+
+QIStatusBar::QIStatusBar(QWidget *pParent)
+ : QStatusBar(pParent)
+{
+ /* Install QIStatusBar accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQIStatusBar::pFactory);
+
+ /* Make sure we remember the last one status message: */
+ connect(this, &QIStatusBar::messageChanged,
+ this, &QIStatusBar::sltRememberLastMessage);
+
+ /* Remove that ugly border around the status-bar items on every platform: */
+ setStyleSheet("QStatusBar::item { border: 0px none black; }");
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBar.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBar.h
new file mode 100644
index 00000000..9e496c91
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBar.h
@@ -0,0 +1,61 @@
+/* $Id: QIStatusBar.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIStatusBar class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIStatusBar_h
+#define FEQT_INCLUDED_SRC_extensions_QIStatusBar_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QStatusBar>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QStatusBar extension with advanced functionality. */
+class SHARED_LIBRARY_STUFF QIStatusBar : public QStatusBar
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs status-bar passing @a pParent to the base-class. */
+ QIStatusBar(QWidget *pParent = 0);
+
+protected slots:
+
+ /** Remembers the last status @a strMessage. */
+ void sltRememberLastMessage(const QString &strMessage) { m_strMessage = strMessage; }
+
+protected:
+
+ /** Holds the last status message. */
+ QString m_strMessage;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIStatusBar_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBarIndicator.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBarIndicator.cpp
new file mode 100644
index 00000000..d5ec4fb3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBarIndicator.cpp
@@ -0,0 +1,169 @@
+/* $Id: QIStatusBarIndicator.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIStatusBarIndicator interface implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QIcon>
+#include <QPainter>
+#include <QStyle>
+#ifdef VBOX_WS_MAC
+# include <QContextMenuEvent>
+#endif /* VBOX_WS_MAC */
+
+/* GUI includes: */
+#include "QIStatusBarIndicator.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/*********************************************************************************************************************************
+* Class QIStatusBarIndicator implementation. *
+*********************************************************************************************************************************/
+
+QIStatusBarIndicator::QIStatusBarIndicator(QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+{
+ /* Configure size-policy: */
+ setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+}
+
+#ifdef VBOX_WS_MAC
+void QIStatusBarIndicator::mousePressEvent(QMouseEvent *pEvent)
+{
+ // WORKAROUND:
+ // Do this for the left mouse button event only, cause in the case of the
+ // right mouse button it could happen that the context menu event is
+ // triggered twice. Also this isn't necessary for the middle mouse button
+ // which would be some kind of overstated.
+ if (pEvent->button() == Qt::LeftButton)
+ {
+ QContextMenuEvent cme(QContextMenuEvent::Mouse, pEvent->pos(), pEvent->globalPos());
+ emit sigContextMenuRequest(this, &cme);
+ if (cme.isAccepted())
+ pEvent->accept();
+ else
+ QWidget::mousePressEvent(pEvent);
+ }
+ else
+ QWidget::mousePressEvent(pEvent);
+}
+#endif /* VBOX_WS_MAC */
+
+void QIStatusBarIndicator::mouseDoubleClickEvent(QMouseEvent *pEvent)
+{
+ emit sigMouseDoubleClick(this, pEvent);
+}
+
+void QIStatusBarIndicator::contextMenuEvent(QContextMenuEvent *pEvent)
+{
+ emit sigContextMenuRequest(this, pEvent);
+}
+
+
+/*********************************************************************************************************************************
+* Class QIStateStatusBarIndicator implementation. *
+*********************************************************************************************************************************/
+
+QIStateStatusBarIndicator::QIStateStatusBarIndicator(QWidget *pParent /* = 0 */)
+ : QIStatusBarIndicator(pParent)
+ , m_iState(0)
+{
+}
+
+QIcon QIStateStatusBarIndicator::stateIcon(int iState) const
+{
+ /* Check if state-icon was set before: */
+ return m_icons.value(iState, QIcon());
+}
+
+void QIStateStatusBarIndicator::setStateIcon(int iState, const QIcon &icon)
+{
+ /* Adjust size-hint: */
+ const QStyle *pStyle = QApplication::style();
+ const int iIconMetric = pStyle->pixelMetric(QStyle::PM_SmallIconSize);
+ m_size = QSize(iIconMetric, iIconMetric);
+ /* Cache passed-icon: */
+ m_icons[iState] = icon;
+}
+
+void QIStateStatusBarIndicator::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+ drawContents(&painter);
+}
+
+void QIStateStatusBarIndicator::drawContents(QPainter *pPainter)
+{
+ if (m_icons.contains(m_iState))
+ {
+ if (window())
+ pPainter->drawPixmap(contentsRect().topLeft(), m_icons.value(m_iState).pixmap(window()->windowHandle(), m_size));
+ else
+ pPainter->drawPixmap(contentsRect().topLeft(), m_icons.value(m_iState).pixmap(m_size));
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class QITextStatusBarIndicator implementation. *
+*********************************************************************************************************************************/
+
+QITextStatusBarIndicator::QITextStatusBarIndicator(QWidget *pParent /* = 0 */)
+ : QIStatusBarIndicator(pParent)
+ , m_pLabel(0)
+{
+ /* Create main-layout: */
+ QHBoxLayout *pMainLayout = new QHBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Configure main-layout: */
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+ pMainLayout->setSpacing(0);
+ /* Create label: */
+ m_pLabel = new QLabel;
+ if (m_pLabel)
+ {
+ /* Add label into main-layout: */
+ pMainLayout->addWidget(m_pLabel);
+ }
+ }
+}
+
+QString QITextStatusBarIndicator::text() const
+{
+ AssertPtrReturn(m_pLabel, QString());
+ return m_pLabel->text();
+}
+
+void QITextStatusBarIndicator::setText(const QString &strText)
+{
+ AssertPtrReturnVoid(m_pLabel);
+ m_pLabel->setText(strText);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBarIndicator.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBarIndicator.h
new file mode 100644
index 00000000..15845d66
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIStatusBarIndicator.h
@@ -0,0 +1,151 @@
+/* $Id: QIStatusBarIndicator.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIStatusBarIndicator interface declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIStatusBarIndicator_h
+#define FEQT_INCLUDED_SRC_extensions_QIStatusBarIndicator_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QMap>
+#include <QWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QIcon;
+class QLabel;
+class QSize;
+class QString;
+class QWidget;
+
+
+/** QWidget extension used as status-bar indicator. */
+class SHARED_LIBRARY_STUFF QIStatusBarIndicator : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about mouse-double-click-event: */
+ void sigMouseDoubleClick(QIStatusBarIndicator *pIndicator, QMouseEvent *pEvent);
+ /** Notifies about context-menu-request-event: */
+ void sigContextMenuRequest(QIStatusBarIndicator *pIndicator, QContextMenuEvent *pEvent);
+
+public:
+
+ /** Constructs status-bar indicator passing @a pParent to the base-class. */
+ QIStatusBarIndicator(QWidget *pParent = 0);
+
+ /** Returns size-hint. */
+ virtual QSize sizeHint() const { return m_size.isValid() ? m_size : QWidget::sizeHint(); }
+
+protected:
+
+#ifdef VBOX_WS_MAC
+ /** Handles mouse-press @a pEvent. */
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+#endif /* VBOX_WS_MAC */
+ /** Handles mouse-double-click @a pEvent. */
+ virtual void mouseDoubleClickEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles context-menu @a pEvent. */
+ virtual void contextMenuEvent(QContextMenuEvent *pEvent) RT_OVERRIDE;
+
+ /** Holds currently cached size. */
+ QSize m_size;
+};
+
+
+/** QIStatusBarIndicator extension used as status-bar state indicator. */
+class SHARED_LIBRARY_STUFF QIStateStatusBarIndicator : public QIStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs state status-bar indicator passing @a pParent to the base-class. */
+ QIStateStatusBarIndicator(QWidget *pParent = 0);
+
+ /** Returns current state. */
+ int state() const { return m_iState; }
+
+ /** Returns state-icon for passed @a iState. */
+ QIcon stateIcon(int iState) const;
+ /** Defines state-icon for passed @a iState as @a icon. */
+ void setStateIcon(int iState, const QIcon &icon);
+
+public slots:
+
+ /** Defines int @a state. */
+ virtual void setState(int iState) { m_iState = iState; repaint(); }
+ /** Defines bool @a state. */
+ void setState(bool fState) { setState((int)fState); }
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Draws contents using passed @a pPainter. */
+ virtual void drawContents(QPainter *pPainter);
+
+private:
+
+ /** Holds current state. */
+ int m_iState;
+ /** Holds cached state icons. */
+ QMap<int, QIcon> m_icons;
+};
+
+
+/** QIStatusBarIndicator extension used as status-bar state indicator. */
+class SHARED_LIBRARY_STUFF QITextStatusBarIndicator : public QIStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs text status-bar indicator passing @a pParent to the base-class. */
+ QITextStatusBarIndicator(QWidget *pParent = 0);
+
+ /** Returns text. */
+ QString text() const;
+ /** Defines @a strText. */
+ void setText(const QString &strText);
+
+private:
+
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIStatusBarIndicator_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIStyledItemDelegate.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIStyledItemDelegate.cpp
new file mode 100644
index 00000000..6f6d7ee4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIStyledItemDelegate.cpp
@@ -0,0 +1,71 @@
+/* $Id: QIStyledItemDelegate.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIStyledItemDelegate class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "QIStyledItemDelegate.h"
+
+
+QIStyledItemDelegate::QIStyledItemDelegate(QObject *pParent)
+ : QStyledItemDelegate(pParent)
+ , m_fWatchForEditorDataCommits(false)
+ , m_fWatchForEditorEnterKeyTriggering(false)
+{
+}
+
+void QIStyledItemDelegate::setWatchForEditorDataCommits(bool fWatch)
+{
+ m_fWatchForEditorDataCommits = fWatch;
+}
+
+void QIStyledItemDelegate::setWatchForEditorEnterKeyTriggering(bool fWatch)
+{
+ m_fWatchForEditorEnterKeyTriggering = fWatch;
+}
+
+QWidget *QIStyledItemDelegate::createEditor(QWidget *pParent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ /* Call to base-class to get actual editor created: */
+ QWidget *pEditor = QStyledItemDelegate::createEditor(pParent, option, index);
+
+ /* Watch for editor data commits, redirect to listeners: */
+ if ( m_fWatchForEditorDataCommits
+ && pEditor->property("has_sigCommitData").toBool())
+ connect(pEditor, SIGNAL(sigCommitData(QWidget *)), this, SIGNAL(commitData(QWidget *)));
+
+ /* Watch for editor Enter key triggering, redirect to listeners: */
+ if ( m_fWatchForEditorEnterKeyTriggering
+ && pEditor->property("has_sigEnterKeyTriggered").toBool())
+ connect(pEditor, SIGNAL(sigEnterKeyTriggered()), this, SIGNAL(sigEditorEnterKeyTriggered()));
+
+ /* Notify listeners about editor created: */
+ emit sigEditorCreated(pEditor, index);
+
+ /* Return actual editor: */
+ return pEditor;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIStyledItemDelegate.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIStyledItemDelegate.h
new file mode 100644
index 00000000..8a7a1d73
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIStyledItemDelegate.h
@@ -0,0 +1,79 @@
+/* $Id: QIStyledItemDelegate.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIStyledItemDelegate class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIStyledItemDelegate_h
+#define FEQT_INCLUDED_SRC_extensions_QIStyledItemDelegate_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QStyledItemDelegate>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QStyledItemDelegate subclass extending standard functionality. */
+class SHARED_LIBRARY_STUFF QIStyledItemDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a pEditor created for particular model @a index. */
+ void sigEditorCreated(QWidget *pEditor, const QModelIndex &index) const;
+
+ /** Notifies listeners about editor's Enter key triggering. */
+ void sigEditorEnterKeyTriggered();
+
+public:
+
+ /** Constructs delegate passing @a pParent to the base-class. */
+ QIStyledItemDelegate(QObject *pParent);
+
+ /** Defines whether delegate should watch for the editor's data commits. */
+ void setWatchForEditorDataCommits(bool fWatch);
+ /** Defines whether delegate should watch for the editor's Enter key triggering. */
+ void setWatchForEditorEnterKeyTriggering(bool fWatch);
+
+protected:
+
+ /** Returns the widget used to edit the item specified by @a index.
+ * The @a pParent widget and style @a option are used to control how the editor widget appears. */
+ virtual QWidget *createEditor(QWidget *pParent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const RT_OVERRIDE;
+
+private:
+
+ /** Holds whether delegate should watch for the editor's data commits. */
+ bool m_fWatchForEditorDataCommits : 1;
+ /** Holds whether delegate should watch for the editor's Enter key triggering. */
+ bool m_fWatchForEditorEnterKeyTriggering : 1;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIStyledItemDelegate_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITabWidget.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QITabWidget.cpp
new file mode 100644
index 00000000..1ec5ff66
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITabWidget.cpp
@@ -0,0 +1,41 @@
+/* $Id: QITabWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITabWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "QITabWidget.h"
+
+
+QITabWidget::QITabWidget(QWidget *pParent /* = 0 */)
+ : QTabWidget(pParent)
+{
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // I don't know why, but for some languages there is
+ // ElideRight the default on Mac OS X. Fix this.
+ setElideMode(Qt::ElideNone);
+#endif
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITabWidget.h b/src/VBox/Frontends/VirtualBox/src/extensions/QITabWidget.h
new file mode 100644
index 00000000..9583511d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITabWidget.h
@@ -0,0 +1,51 @@
+/* $Id: QITabWidget.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITabWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QITabWidget_h
+#define FEQT_INCLUDED_SRC_extensions_QITabWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTabWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QTabWidget extension with advanced functionality. */
+class SHARED_LIBRARY_STUFF QITabWidget : public QTabWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs tab-widget passing @a pParent to the base-class. */
+ QITabWidget(QWidget *pParent = 0);
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QITabWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITableView.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QITableView.cpp
new file mode 100644
index 00000000..f38cd5ed
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITableView.cpp
@@ -0,0 +1,493 @@
+/* $Id: QITableView.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITableView class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+
+/* GUI includes: */
+#include "QIStyledItemDelegate.h"
+#include "QITableView.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QAccessibleObject extension used as an accessibility interface for QITableViewCell. */
+class QIAccessibilityInterfaceForQITableViewCell : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QITableViewCell accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QITableViewCell"))
+ return new QIAccessibilityInterfaceForQITableViewCell(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ QIAccessibilityInterfaceForQITableViewCell(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE;
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE { return 0; }
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int /* iIndex */) const RT_OVERRIDE { return 0; }
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface * /* pChild */) const RT_OVERRIDE { return -1; }
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE;
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE;
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QITableViewCell. */
+ QITableViewCell *cell() const { return qobject_cast<QITableViewCell*>(object()); }
+};
+
+
+/** QAccessibleObject extension used as an accessibility interface for QITableViewRow. */
+class QIAccessibilityInterfaceForQITableViewRow : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QITableViewRow accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QITableViewRow"))
+ return new QIAccessibilityInterfaceForQITableViewRow(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ QIAccessibilityInterfaceForQITableViewRow(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE;
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE;
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE;
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QITableViewRow. */
+ QITableViewRow *row() const { return qobject_cast<QITableViewRow*>(object()); }
+};
+
+
+/** QAccessibleWidget extension used as an accessibility interface for QITableView. */
+class QIAccessibilityInterfaceForQITableView : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QITableView accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QITableView"))
+ return new QIAccessibilityInterfaceForQITableView(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ QIAccessibilityInterfaceForQITableView(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::List)
+ {}
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QITableView. */
+ QITableView *table() const { return qobject_cast<QITableView*>(widget()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQITableViewCell implementation. *
+*********************************************************************************************************************************/
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITableViewCell::parent() const
+{
+ /* Make sure cell still alive: */
+ AssertPtrReturn(cell(), 0);
+
+ /* Return the parent: */
+ return QAccessible::queryAccessibleInterface(cell()->row());
+}
+
+QRect QIAccessibilityInterfaceForQITableViewCell::rect() const
+{
+ /* Make sure cell still alive: */
+ AssertPtrReturn(cell(), QRect());
+ AssertPtrReturn(cell()->row(), QRect());
+ AssertPtrReturn(cell()->row()->table(), QRect());
+
+ /* Calculate local item coordinates: */
+ const int iIndexInParent = parent()->indexOfChild(this);
+ const int iParentIndexInParent = parent()->parent()->indexOfChild(parent());
+ const int iX = cell()->row()->table()->columnViewportPosition(iIndexInParent);
+ const int iY = cell()->row()->table()->rowViewportPosition(iParentIndexInParent);
+ const int iWidth = cell()->row()->table()->columnWidth(iIndexInParent);
+ const int iHeight = cell()->row()->table()->rowHeight(iParentIndexInParent);
+
+ /* Map local item coordinates to global: */
+ const QPoint itemPosInScreen = cell()->row()->table()->viewport()->mapToGlobal(QPoint(iX, iY));
+
+ /* Return item rectangle: */
+ return QRect(itemPosInScreen, QSize(iWidth, iHeight));
+}
+
+QString QIAccessibilityInterfaceForQITableViewCell::text(QAccessible::Text enmTextRole) const
+{
+ /* Make sure cell still alive: */
+ AssertPtrReturn(cell(), QString());
+
+ /* Return a text for the passed enmTextRole: */
+ switch (enmTextRole)
+ {
+ case QAccessible::Name: return cell()->text();
+ default: break;
+ }
+
+ /* Null-string by default: */
+ return QString();
+}
+
+QAccessible::Role QIAccessibilityInterfaceForQITableViewCell::role() const
+{
+ /* Cell by default: */
+ return QAccessible::Cell;
+}
+
+QAccessible::State QIAccessibilityInterfaceForQITableViewCell::state() const
+{
+ /* Make sure cell still alive: */
+ AssertPtrReturn(cell(), QAccessible::State());
+
+ /* Empty state by default: */
+ return QAccessible::State();
+}
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQITableViewRow implementation. *
+*********************************************************************************************************************************/
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITableViewRow::parent() const
+{
+ /* Make sure row still alive: */
+ AssertPtrReturn(row(), 0);
+
+ /* Return the parent: */
+ return QAccessible::queryAccessibleInterface(row()->table());
+}
+
+int QIAccessibilityInterfaceForQITableViewRow::childCount() const
+{
+ /* Make sure row still alive: */
+ AssertPtrReturn(row(), 0);
+
+ /* Return the number of children: */
+ return row()->childCount();
+}
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITableViewRow::child(int iIndex) const /* override */
+{
+ /* Make sure row still alive: */
+ AssertPtrReturn(row(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Return the child with the passed iIndex: */
+ return QAccessible::queryAccessibleInterface(row()->childItem(iIndex));
+}
+
+int QIAccessibilityInterfaceForQITableViewRow::indexOfChild(const QAccessibleInterface *pChild) const /* override */
+{
+ /* Search for corresponding child: */
+ for (int i = 0; i < childCount(); ++i)
+ if (child(i) == pChild)
+ return i;
+
+ /* -1 by default: */
+ return -1;
+}
+
+QRect QIAccessibilityInterfaceForQITableViewRow::rect() const
+{
+ /* Make sure row still alive: */
+ AssertPtrReturn(row(), QRect());
+ AssertPtrReturn(row()->table(), QRect());
+
+ /* Calculate local item coordinates: */
+ const int iIndexInParent = parent()->indexOfChild(this);
+ const int iX = row()->table()->columnViewportPosition(0);
+ const int iY = row()->table()->rowViewportPosition(iIndexInParent);
+ int iWidth = 0;
+ int iHeight = 0;
+ for (int i = 0; i < childCount(); ++i)
+ iWidth += row()->table()->columnWidth(i);
+ iHeight += row()->table()->rowHeight(iIndexInParent);
+
+ /* Map local item coordinates to global: */
+ const QPoint itemPosInScreen = row()->table()->viewport()->mapToGlobal(QPoint(iX, iY));
+
+ /* Return item rectangle: */
+ return QRect(itemPosInScreen, QSize(iWidth, iHeight));
+}
+
+QString QIAccessibilityInterfaceForQITableViewRow::text(QAccessible::Text enmTextRole) const
+{
+ /* Make sure row still alive: */
+ AssertPtrReturn(row(), QString());
+
+ /* Return a text for the passed enmTextRole: */
+ switch (enmTextRole)
+ {
+ case QAccessible::Name: return childCount() > 0 && child(0) ? child(0)->text(enmTextRole) : QString();
+ default: break;
+ }
+
+ /* Null-string by default: */
+ return QString();
+}
+
+QAccessible::Role QIAccessibilityInterfaceForQITableViewRow::role() const
+{
+ /* Row by default: */
+ return QAccessible::Row;
+}
+
+QAccessible::State QIAccessibilityInterfaceForQITableViewRow::state() const
+{
+ /* Make sure row still alive: */
+ AssertPtrReturn(row(), QAccessible::State());
+
+ /* Empty state by default: */
+ return QAccessible::State();
+}
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQITableView implementation. *
+*********************************************************************************************************************************/
+
+int QIAccessibilityInterfaceForQITableView::childCount() const
+{
+ /* Make sure table still alive: */
+ AssertPtrReturn(table(), 0);
+
+ /* Return the number of children: */
+ return table()->childCount();
+}
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITableView::child(int iIndex) const
+{
+ /* Make sure table still alive: */
+ AssertPtrReturn(table(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0, 0);
+ if (iIndex >= childCount())
+ {
+ // WORKAROUND:
+ // Normally I would assert here, but Qt5 accessibility code has
+ // a hard-coded architecture for a table-views which we do not like
+ // but have to live with and this architecture enumerates cells
+ // including header column and row, so Qt5 can try to address
+ // our interface with index which surely out of bounds by our laws.
+ // So let's assume that's exactly such case and try to enumerate
+ // table cells including header column and row.
+ // printf("Invalid index: %d\n", iIndex);
+
+ // Split delimeter is overall column count, including vertical header:
+ const int iColumnCount = table()->model()->columnCount() + 1 /* v_header */;
+ // Real index is zero-based, incoming is 1-based:
+ const int iRealIndex = iIndex - 1;
+ // Real row index, excluding horizontal header:
+ const int iRealRowIndex = iRealIndex / iColumnCount - 1 /* h_header */;
+ // printf("Actual row index: %d\n", iRealRowIndex);
+
+ // Return what we found:
+ return iRealRowIndex >= 0 && iRealRowIndex < childCount() ?
+ QAccessible::queryAccessibleInterface(table()->childItem(iRealRowIndex)) : 0;
+ }
+
+ /* Return the child with the passed iIndex: */
+ return QAccessible::queryAccessibleInterface(table()->childItem(iIndex));
+}
+
+int QIAccessibilityInterfaceForQITableView::indexOfChild(const QAccessibleInterface *pChild) const
+{
+ /* Search for corresponding child: */
+ for (int i = 0; i < childCount(); ++i)
+ if (child(i) == pChild)
+ return i;
+
+ /* -1 by default: */
+ return -1;
+}
+
+QString QIAccessibilityInterfaceForQITableView::text(QAccessible::Text /* enmTextRole */) const
+{
+ /* Make sure table still alive: */
+ AssertPtrReturn(table(), QString());
+
+ /* Return table whats-this: */
+ return table()->whatsThis();
+}
+
+
+/*********************************************************************************************************************************
+* Class QITableView implementation. *
+*********************************************************************************************************************************/
+
+QITableView::QITableView(QWidget *pParent)
+ : QTableView(pParent)
+{
+ /* Prepare: */
+ prepare();
+}
+
+QITableView::~QITableView()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void QITableView::makeSureEditorDataCommitted()
+{
+ /* Do we have current editor at all? */
+ QObject *pEditorObject = m_editors.value(currentIndex());
+ if (pEditorObject && pEditorObject->isWidgetType())
+ {
+ /* Cast the editor to widget type: */
+ QWidget *pEditor = qobject_cast<QWidget*>(pEditorObject);
+ AssertPtrReturnVoid(pEditor);
+ {
+ /* Commit the editor data and closes it: */
+ commitData(pEditor);
+ closeEditor(pEditor, QAbstractItemDelegate::SubmitModelCache);
+ }
+ }
+}
+
+void QITableView::sltEditorCreated(QWidget *pEditor, const QModelIndex &index)
+{
+ /* Connect created editor to the table and store it: */
+ connect(pEditor, &QWidget::destroyed, this, &QITableView::sltEditorDestroyed);
+ m_editors[index] = pEditor;
+}
+
+void QITableView::sltEditorDestroyed(QObject *pEditor)
+{
+ /* Clear destroyed editor from the table: */
+ const QModelIndex index = m_editors.key(pEditor);
+ AssertReturnVoid(index.isValid());
+ m_editors.remove(index);
+}
+
+void QITableView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+ /* Notify listeners about index changed: */
+ emit sigCurrentChanged(current, previous);
+ /* Call to base-class: */
+ QTableView::currentChanged(current, previous);
+}
+
+void QITableView::prepare()
+{
+ /* Install QITableViewCell accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQITableViewCell::pFactory);
+ /* Install QITableViewRow accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQITableViewRow::pFactory);
+ /* Install QITableView accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQITableView::pFactory);
+
+ /* Delete old delegate: */
+ delete itemDelegate();
+ /* Create new delegate: */
+ QIStyledItemDelegate *pStyledItemDelegate = new QIStyledItemDelegate(this);
+ AssertPtrReturnVoid(pStyledItemDelegate);
+ {
+ /* Assign newly created delegate to the table: */
+ setItemDelegate(pStyledItemDelegate);
+ /* Connect newly created delegate to the table: */
+ connect(pStyledItemDelegate, &QIStyledItemDelegate::sigEditorCreated,
+ this, &QITableView::sltEditorCreated);
+ }
+}
+
+void QITableView::cleanup()
+{
+ /* Disconnect all the editors prematurelly: */
+ foreach (QObject *pEditor, m_editors.values())
+ disconnect(pEditor, 0, this, 0);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITableView.h b/src/VBox/Frontends/VirtualBox/src/extensions/QITableView.h
new file mode 100644
index 00000000..26c1c714
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITableView.h
@@ -0,0 +1,151 @@
+/* $Id: QITableView.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITableView class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QITableView_h
+#define FEQT_INCLUDED_SRC_extensions_QITableView_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTableView>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QITableViewCell;
+class QITableViewRow;
+class QITableView;
+
+
+/** OObject subclass used as cell for the QITableView. */
+class SHARED_LIBRARY_STUFF QITableViewCell : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs table-view cell for passed @a pParent. */
+ QITableViewCell(QITableViewRow *pParent)
+ : m_pRow(pParent)
+ {}
+
+ /** Defines the parent @a pRow reference. */
+ void setRow(QITableViewRow *pRow) { m_pRow = pRow; }
+ /** Returns the parent row reference. */
+ QITableViewRow *row() const { return m_pRow; }
+
+ /** Returns the cell text. */
+ virtual QString text() const = 0;
+
+private:
+
+ /** Holds the parent row reference. */
+ QITableViewRow *m_pRow;
+};
+
+
+/** OObject subclass used as row for the QITableView. */
+class SHARED_LIBRARY_STUFF QITableViewRow : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs table-view row for passed @a pParent. */
+ QITableViewRow(QITableView *pParent)
+ : m_pTable(pParent)
+ {}
+
+ /** Defines the parent @a pTable reference. */
+ void setTable(QITableView *pTable) { m_pTable = pTable; }
+ /** Returns the parent table reference. */
+ QITableView *table() const { return m_pTable; }
+
+ /** Returns the number of children. */
+ virtual int childCount() const = 0;
+ /** Returns the child item with @a iIndex. */
+ virtual QITableViewCell *childItem(int iIndex) const = 0;
+
+private:
+
+ /** Holds the parent table reference. */
+ QITableView *m_pTable;
+};
+
+
+/** QTableView subclass extending standard functionality. */
+class SHARED_LIBRARY_STUFF QITableView : public QTableView
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about index changed from @a previous to @a current. */
+ void sigCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
+
+public:
+
+ /** Constructs table-view passing @a pParent to the base-class. */
+ QITableView(QWidget *pParent = 0);
+ /** Destructs table-view. */
+ virtual ~QITableView() RT_OVERRIDE;
+
+ /** Returns the number of children. */
+ virtual int childCount() const { return 0; }
+ /** Returns the child item with @a iIndex. */
+ virtual QITableViewRow *childItem(int /* iIndex */) const { return 0; }
+
+ /** Makes sure current editor data committed. */
+ void makeSureEditorDataCommitted();
+
+protected slots:
+
+ /** Stores the created @a pEditor for passed @a index in the map. */
+ virtual void sltEditorCreated(QWidget *pEditor, const QModelIndex &index);
+ /** Clears the destoyed @a pEditor from the map. */
+ virtual void sltEditorDestroyed(QObject *pEditor);
+
+protected:
+
+ /** Handles index change from @a previous to @a current. */
+ virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous) RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the map of editors stored for passed indexes. */
+ QMap<QModelIndex, QObject*> m_editors;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QITableView_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITableWidget.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QITableWidget.cpp
new file mode 100644
index 00000000..33fba56b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITableWidget.cpp
@@ -0,0 +1,362 @@
+/* $Id: QITableWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITableWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QPainter>
+#include <QResizeEvent>
+
+/* GUI includes: */
+#include "QITableWidget.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QAccessibleObject extension used as an accessibility interface for QITableWidgetItem. */
+class QIAccessibilityInterfaceForQITableWidgetItem : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QITableWidgetItem accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QITableWidgetItem"))
+ return new QIAccessibilityInterfaceForQITableWidgetItem(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ QIAccessibilityInterfaceForQITableWidgetItem(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE;
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE { return 0; }
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE { Q_UNUSED(iIndex); return 0; }
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE { Q_UNUSED(pChild); return -1; }
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE;
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE;
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QITableWidgetItem. */
+ QITableWidgetItem *item() const { return qobject_cast<QITableWidgetItem*>(object()); }
+};
+
+
+/** QAccessibleWidget extension used as an accessibility interface for QITableWidget. */
+class QIAccessibilityInterfaceForQITableWidget : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QITableWidget accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QITableWidget"))
+ return new QIAccessibilityInterfaceForQITableWidget(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ QIAccessibilityInterfaceForQITableWidget(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::List)
+ {}
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QITableWidget. */
+ QITableWidget *table() const { return qobject_cast<QITableWidget*>(widget()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQITableWidgetItem implementation. *
+*********************************************************************************************************************************/
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITableWidgetItem::parent() const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Return the parent: */
+ return QAccessible::queryAccessibleInterface(item()->parentTable());
+}
+
+QRect QIAccessibilityInterfaceForQITableWidgetItem::rect() const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QRect());
+
+ /* Compose common region: */
+ QRegion region;
+
+ /* Append item rectangle: */
+ const QRect itemRectInViewport = item()->parentTable()->visualItemRect(item());
+ const QSize itemSize = itemRectInViewport.size();
+ const QPoint itemPosInViewport = itemRectInViewport.topLeft();
+ const QPoint itemPosInScreen = item()->parentTable()->viewport()->mapToGlobal(itemPosInViewport);
+ const QRect itemRectInScreen = QRect(itemPosInScreen, itemSize);
+ region += itemRectInScreen;
+
+ /* Return common region bounding rectangle: */
+ return region.boundingRect();
+}
+
+QString QIAccessibilityInterfaceForQITableWidgetItem::text(QAccessible::Text enmTextRole) const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QString());
+
+ /* Return a text for the passed enmTextRole: */
+ switch (enmTextRole)
+ {
+ case QAccessible::Name: return item()->text();
+ default: break;
+ }
+
+ /* Null-string by default: */
+ return QString();
+}
+
+QAccessible::Role QIAccessibilityInterfaceForQITableWidgetItem::role() const
+{
+ return QAccessible::ListItem;
+}
+
+QAccessible::State QIAccessibilityInterfaceForQITableWidgetItem::state() const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QAccessible::State());
+
+ /* Compose the state: */
+ QAccessible::State state;
+ state.focusable = true;
+ state.selectable = true;
+
+ /* Compose the state of current item: */
+ if ( item()
+ && item() == QITableWidgetItem::toItem(item()->tableWidget()->currentItem()))
+ {
+ state.active = true;
+ state.focused = true;
+ state.selected = true;
+ }
+
+ /* Compose the state of checked item: */
+ if ( item()
+ && item()->checkState() != Qt::Unchecked)
+ {
+ state.checked = true;
+ if (item()->checkState() == Qt::PartiallyChecked)
+ state.checkStateMixed = true;
+ }
+
+ /* Return the state: */
+ return state;
+}
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQITableWidget implementation. *
+*********************************************************************************************************************************/
+
+int QIAccessibilityInterfaceForQITableWidget::childCount() const
+{
+ /* Make sure table still alive: */
+ AssertPtrReturn(table(), 0);
+
+ /* Return the number of children: */
+ return table()->rowCount() * table()->columnCount();
+}
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITableWidget::child(int iIndex) const
+{
+ /* Make sure table still alive: */
+ AssertPtrReturn(table(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Return the child with the passed iIndex: */
+ const int iRow = iIndex / table()->columnCount();
+ const int iColumn = iIndex % table()->columnCount();
+ return QAccessible::queryAccessibleInterface(table()->childItem(iRow, iColumn));
+}
+
+int QIAccessibilityInterfaceForQITableWidget::indexOfChild(const QAccessibleInterface *pChild) const
+{
+ /* Search for corresponding child: */
+ for (int iIndex = 0; iIndex < childCount(); ++iIndex)
+ if (child(iIndex) == pChild)
+ return iIndex;
+
+ /* -1 by default: */
+ return -1;
+}
+
+QString QIAccessibilityInterfaceForQITableWidget::text(QAccessible::Text /* enmTextRole */) const
+{
+ /* Make sure table still alive: */
+ AssertPtrReturn(table(), QString());
+
+ /* Gather suitable text: */
+ QString strText = table()->toolTip();
+ if (strText.isEmpty())
+ strText = table()->whatsThis();
+ return strText;
+}
+
+
+/*********************************************************************************************************************************
+* Class QITableWidgetItem implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+QITableWidgetItem *QITableWidgetItem::toItem(QTableWidgetItem *pItem)
+{
+ /* Make sure alive QITableWidgetItem passed: */
+ if (!pItem || pItem->type() != ItemType)
+ return 0;
+
+ /* Return casted QITableWidgetItem: */
+ return static_cast<QITableWidgetItem*>(pItem);
+}
+
+/* static */
+const QITableWidgetItem *QITableWidgetItem::toItem(const QTableWidgetItem *pItem)
+{
+ /* Make sure alive QITableWidgetItem passed: */
+ if (!pItem || pItem->type() != ItemType)
+ return 0;
+
+ /* Return casted QITableWidgetItem: */
+ return static_cast<const QITableWidgetItem*>(pItem);
+}
+
+QITableWidgetItem::QITableWidgetItem(const QString &strText /* = QString() */)
+ : QTableWidgetItem(strText, ItemType)
+{
+}
+
+QITableWidget *QITableWidgetItem::parentTable() const
+{
+ return tableWidget() ? qobject_cast<QITableWidget*>(tableWidget()) : 0;
+}
+
+
+/*********************************************************************************************************************************
+* Class QITableWidget implementation. *
+*********************************************************************************************************************************/
+
+QITableWidget::QITableWidget(QWidget *pParent)
+ : QTableWidget(pParent)
+{
+ /* Install QITableWidget accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQITableWidget::pFactory);
+ /* Install QITableWidgetItem accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQITableWidgetItem::pFactory);
+
+ // WORKAROUND:
+ // Ok, what do we have here..
+ // There is a bug in QAccessible framework which might be just treated like
+ // a functionality flaw. It consist in fact that if an accessibility client
+ // is enabled, base-class can request an accessibility interface in own
+ // constructor before the sub-class registers own factory, so we have to
+ // recreate interface after we finished with our own initialization.
+ QAccessibleInterface *pInterface = QAccessible::queryAccessibleInterface(this);
+ if (pInterface)
+ {
+ QAccessible::deleteAccessibleInterface(QAccessible::uniqueId(pInterface));
+ QAccessible::queryAccessibleInterface(this); // <= new one, proper..
+ }
+}
+
+QITableWidgetItem *QITableWidget::childItem(int iRow, int iColumn) const
+{
+ return item(iRow, iColumn) ? QITableWidgetItem::toItem(item(iRow, iColumn)) : 0;
+}
+
+QModelIndex QITableWidget::itemIndex(QTableWidgetItem *pItem)
+{
+ return indexFromItem(pItem);
+}
+
+void QITableWidget::paintEvent(QPaintEvent *pEvent)
+{
+ /* Create item painter: */
+ QPainter painter;
+ painter.begin(viewport());
+
+ /* Notify listeners about painting: */
+ for (int iRow = 0; iRow < rowCount(); ++iRow)
+ for (int iColumn = 0; iColumn < rowCount(); ++iColumn)
+ emit painted(item(iRow, iColumn), &painter);
+
+ /* Close item painter: */
+ painter.end();
+
+ /* Call to base-class: */
+ QTableWidget::paintEvent(pEvent);
+}
+
+void QITableWidget::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QTableWidget::resizeEvent(pEvent);
+
+ /* Notify listeners about resizing: */
+ emit resized(pEvent->size(), pEvent->oldSize());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITableWidget.h b/src/VBox/Frontends/VirtualBox/src/extensions/QITableWidget.h
new file mode 100644
index 00000000..8355550f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITableWidget.h
@@ -0,0 +1,96 @@
+/* $Id: QITableWidget.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITableWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QITableWidget_h
+#define FEQT_INCLUDED_SRC_extensions_QITableWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTableWidget>
+#include <QTableWidgetItem>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QITableWidget;
+
+/** QTableWidgetItem subclass extending standard functionality. */
+class SHARED_LIBRARY_STUFF QITableWidgetItem : public QObject, public QTableWidgetItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Item type for QITableWidgetItem. */
+ enum { ItemType = QTableWidgetItem::UserType + 1 };
+
+ /** Casts QTableWidgetItem* to QITableWidgetItem* if possible. */
+ static QITableWidgetItem *toItem(QTableWidgetItem *pItem);
+ /** Casts const QTableWidgetItem* to const QITableWidgetItem* if possible. */
+ static const QITableWidgetItem *toItem(const QTableWidgetItem *pItem);
+
+ /** Constructs item passing @a strText into the base-class. */
+ QITableWidgetItem(const QString &strText = QString());
+
+ /** Returns the parent table-widget. */
+ QITableWidget *parentTable() const;
+};
+
+/** QTableWidget subclass extending standard functionality. */
+class SHARED_LIBRARY_STUFF QITableWidget : public QTableWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about particular tree-widget @a pItem is painted with @a pPainter. */
+ void painted(QTableWidgetItem *pItem, QPainter *pPainter);
+ /** Notifies about tree-widget being resized from @a oldSize to @a size. */
+ void resized(const QSize &size, const QSize &oldSize);
+
+public:
+
+ /** Constructs tree-widget passing @a pParent to the base-class. */
+ QITableWidget(QWidget *pParent = 0);
+
+ /** Returns the child item with @a iRow and @a iColumn. */
+ QITableWidgetItem *childItem(int iRow, int iColumn) const;
+ /** Returns a model-index of @a pItem specified. */
+ QModelIndex itemIndex(QTableWidgetItem *pItem);
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ void paintEvent(QPaintEvent *pEvent);
+ /** Handles resize @a pEvent. */
+ void resizeEvent(QResizeEvent *pEvent);
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QITableWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIToolBar.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIToolBar.cpp
new file mode 100644
index 00000000..e3cd0261
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIToolBar.cpp
@@ -0,0 +1,286 @@
+/* $Id: QIToolBar.cpp $ */
+/** @file
+ * VBox Qt GUI - QIToolBar class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QLayout>
+#include <QMainWindow>
+#include <QResizeEvent>
+#ifdef VBOX_WS_MAC
+# include <QApplication>
+# include <QPainter>
+# include <QPainterPath>
+#endif
+
+/* GUI includes: */
+#include "QIToolBar.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils.h"
+#endif
+
+
+QIToolBar::QIToolBar(QWidget *pParent /* = 0 */)
+ : QToolBar(pParent)
+ , m_pMainWindow(qobject_cast<QMainWindow*>(pParent))
+#ifdef VBOX_WS_MAC
+ , m_fEmulateUnifiedToolbar(false)
+ , m_iOverallContentsWidth(0)
+ , m_iBrandingWidth(0)
+#endif
+{
+ prepare();
+}
+
+void QIToolBar::setUseTextLabels(bool fEnable)
+{
+ /* Determine tool-button style on the basis of passed flag: */
+ Qt::ToolButtonStyle tbs = fEnable ? Qt::ToolButtonTextUnderIcon : Qt::ToolButtonIconOnly;
+
+ /* Depending on parent, assign this style: */
+ if (m_pMainWindow)
+ m_pMainWindow->setToolButtonStyle(tbs);
+ else
+ setToolButtonStyle(tbs);
+}
+
+bool QIToolBar::useTextLabels() const
+{
+ /* Depending on parent, return the style: */
+ if (m_pMainWindow)
+ return m_pMainWindow->toolButtonStyle() == Qt::ToolButtonTextUnderIcon;
+ else
+ return toolButtonStyle() == Qt::ToolButtonTextUnderIcon;
+}
+
+#ifdef VBOX_WS_MAC
+void QIToolBar::enableMacToolbar()
+{
+ /* Depending on parent, enable unified title/tool-bar: */
+ if (m_pMainWindow)
+ m_pMainWindow->setUnifiedTitleAndToolBarOnMac(true);
+}
+
+void QIToolBar::emulateMacToolbar()
+{
+ /* Remember request, to be used in paintEvent: */
+ m_fEmulateUnifiedToolbar = true;
+}
+
+void QIToolBar::setShowToolBarButton(bool fShow)
+{
+ ::darwinSetShowsToolbarButton(this, fShow);
+}
+
+void QIToolBar::updateLayout()
+{
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the tool-bar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text.
+ QSizePolicy sp = sizePolicy();
+ setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ adjustSize();
+ setSizePolicy(sp);
+ layout()->invalidate();
+ layout()->activate();
+}
+
+void QIToolBar::enableBranding(const QIcon &icnBranding,
+ const QString &strBranding,
+ const QColor &clrBranding,
+ int iBrandingWidth)
+{
+ m_icnBranding = icnBranding;
+ m_strBranding = strBranding;
+ m_clrBranding = clrBranding;
+ m_iBrandingWidth = iBrandingWidth;
+ update();
+}
+#endif /* VBOX_WS_MAC */
+
+bool QIToolBar::event(QEvent *pEvent)
+{
+ /* Sanity check: */
+ if (!pEvent)
+ return QToolBar::event(pEvent);
+
+ /* Handle required event types: */
+ switch (pEvent->type())
+ {
+#ifdef VBOX_WS_MAC
+ case QEvent::LayoutRequest:
+ {
+ /* Recalculate overall contents width on layout
+ * request if we have branding stuff: */
+ if (!m_icnBranding.isNull())
+ recalculateOverallContentsWidth();
+ break;
+ }
+#endif /* VBOX_WS_MAC */
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QToolBar::event(pEvent);
+}
+
+void QIToolBar::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QToolBar::resizeEvent(pEvent);
+
+ /* Notify listeners about new size: */
+ emit sigResized(pEvent->size());
+}
+
+#ifdef VBOX_WS_MAC
+void QIToolBar::paintEvent(QPaintEvent *pEvent)
+{
+ /* Call to base-class: */
+ QToolBar::paintEvent(pEvent);
+
+ /* If we have request to emulate unified tool-bar: */
+ if (m_fEmulateUnifiedToolbar)
+ {
+ /* Limit painting with incoming rectangle: */
+ QPainter painter(this);
+ painter.setClipRect(pEvent->rect());
+
+ /* Acquire full rectangle: */
+ const QRect rectangle = rect();
+
+ /* Prepare gradient: */
+ const QColor backgroundColor = QApplication::palette().color(QPalette::Active, QPalette::Window);
+ QLinearGradient gradient(rectangle.topLeft(), rectangle.bottomLeft());
+ gradient.setColorAt(0, backgroundColor.darker(105));
+ gradient.setColorAt(1, backgroundColor.darker(115));
+
+ /* Fill background: */
+ painter.fillRect(rectangle, gradient);
+
+ /* Do we have branding stuff and a place for it? */
+ if ( !m_icnBranding.isNull()
+ && width() >= m_iOverallContentsWidth + m_iBrandingWidth)
+ {
+ /* A bit of common stuff: */
+ QFont fnt = font();
+ int iTextWidth = 0;
+ int iTextHeight = 0;
+
+ /* Configure font to fit width (m_iBrandingWidth - 2 * 4): */
+ if (useTextLabels())
+ {
+ for (int i = 0; i <= 10; ++i) // no more than 10 tries ..
+ {
+ if (fnt.pixelSize() == -1)
+ fnt.setPointSize(fnt.pointSize() - i);
+ else
+ fnt.setPixelSize(fnt.pixelSize() - i);
+ iTextWidth = QFontMetrics(fnt).size(0, m_strBranding).width();
+ if (iTextWidth <= m_iBrandingWidth - 2 * 4)
+ break;
+ }
+ iTextHeight = QFontMetrics(fnt).height();
+ }
+
+ /* Draw pixmap: */
+ const int iIconSize = qMin(rectangle.height(), 32 /* default */);
+ const int iIconMarginH = (m_iBrandingWidth - iIconSize) / 2;
+ const int iIconMarginV = (rectangle.height() - iIconSize - iTextHeight) / 2;
+ const int iIconX = rectangle.width() - iIconSize - iIconMarginH;
+ const int iIconY = iIconMarginV;
+ painter.drawPixmap(iIconX, iIconY, m_icnBranding.pixmap(QSize(iIconSize, iIconSize)));
+
+ /* Draw text path: */
+ if (useTextLabels())
+ {
+ const int iTextMargingH = (m_iBrandingWidth - iTextWidth) / 2;
+ const int iTextX = rectangle.width() - iTextWidth - iTextMargingH;
+ const int iTextY = iIconY + iIconSize + iTextHeight;
+ QPainterPath textPath;
+ textPath.addText(0, 0, fnt, m_strBranding);
+ textPath.translate(iTextX, iTextY);
+ painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
+ painter.setPen(QPen(m_clrBranding.darker(80), 2, Qt::SolidLine, Qt::RoundCap));
+ painter.drawPath(QPainterPathStroker().createStroke(textPath));
+ painter.setBrush(Qt::black);
+ painter.setPen(Qt::NoPen);
+ painter.drawPath(textPath);
+ }
+ }
+ }
+}
+#endif /* VBOX_WS_MAC */
+
+void QIToolBar::prepare()
+{
+ /* Configure tool-bar: */
+ setFloatable(false);
+ setMovable(false);
+
+#ifdef VBOX_WS_MAC
+ setStyleSheet("QToolBar { border: 0px none black; }");
+#endif
+
+ /* Configure tool-bar' layout: */
+ if (layout())
+ layout()->setContentsMargins(0, 0, 0, 0);
+
+ /* Configure tool-bar' context-menu policy: */
+ setContextMenuPolicy(Qt::PreventContextMenu);
+}
+
+#ifdef VBOX_WS_MAC
+void QIToolBar::recalculateOverallContentsWidth()
+{
+ /* Reset contents width: */
+ m_iOverallContentsWidth = 0;
+
+ /* Caclulate new value: */
+ if (!layout())
+ return;
+ int iResult = 0;
+ const int iSpacing = layout()->spacing();
+ foreach (QAction *pAction, actions())
+ {
+ if (!pAction || !pAction->isVisible())
+ continue;
+ QWidget *pWidget = widgetForAction(pAction);
+ if (!pWidget)
+ continue;
+ /* Add each widget width and spacing: */
+ const int iWidth = pWidget->width() + iSpacing;
+ iResult += iWidth;
+ }
+ /* Subtract last spacing: */
+ iResult -= iSpacing;
+
+ /* Update result: */
+ m_iOverallContentsWidth = qMax(m_iOverallContentsWidth, iResult);
+}
+#endif /* VBOX_WS_MAC */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIToolBar.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIToolBar.h
new file mode 100644
index 00000000..db209d73
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIToolBar.h
@@ -0,0 +1,138 @@
+/* $Id: QIToolBar.h $ */
+/** @file
+ * VBox Qt GUI - QIToolBar class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIToolBar_h
+#define FEQT_INCLUDED_SRC_extensions_QIToolBar_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QToolBar>
+#ifdef VBOX_WS_MAC
+# include <QColor>
+# include <QIcon>
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QMainWindow;
+class QResizeEvent;
+class QWidget;
+#ifdef VBOX_WS_MAC
+class QPaintEvent;
+#endif
+
+/** QToolBar extension with few settings presets. */
+class SHARED_LIBRARY_STUFF QIToolBar : public QToolBar
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a newSize. */
+ void sigResized(const QSize &newSize);
+
+public:
+
+ /** Constructs tool-bar passing @a pParent to the base-class. */
+ QIToolBar(QWidget *pParent = 0);
+
+ /** Defines whether tool-bar should use text-labels. */
+ void setUseTextLabels(bool fEnable);
+ /** Returns whether tool-bar should use text-labels. */
+ bool useTextLabels() const;
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Defines whether native tool-bar should be enabled. */
+ void enableMacToolbar();
+ /** Mac OS X: Defines whether native tool-bar should be emulated. */
+ void emulateMacToolbar();
+
+ /** Mac OS X: Defines whether native tool-bar button should be shown. */
+ void setShowToolBarButton(bool fShow);
+ /** Mac OS X: Updates native tool-bar layout. */
+ void updateLayout();
+
+ /** Mac OS X: Defines branding stuff to be shown.
+ * @param icnBranding Brings branding icon to be shown.
+ * @param strBranding Brings branding text to be shown.
+ * @param clrBranding Brings branding color to be used.
+ * @param iBrandingWidth Holds the branding stuff width. */
+ void enableBranding(const QIcon &icnBranding,
+ const QString &strBranding,
+ const QColor &clrBranding,
+ int iBrandingWidth);
+#endif /* VBOX_WS_MAC */
+
+protected:
+
+ /** Handles @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+#ifdef VBOX_WS_MAC
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+#endif
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+#ifdef VBOX_WS_MAC
+ /** Recalculates overall contents width. */
+ void recalculateOverallContentsWidth();
+#endif /* VBOX_WS_MAC */
+
+ /** Holds the parent main-window isntance. */
+ QMainWindow *m_pMainWindow;
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Holds whether unified tool-bar should be emulated. */
+ bool m_fEmulateUnifiedToolbar;
+
+ /** Holds overall contents width. */
+ int m_iOverallContentsWidth;
+
+ /** Mac OS X: Holds branding icon to be shown. */
+ QIcon m_icnBranding;
+ /** Mac OS X: Holds branding text to be shown. */
+ QString m_strBranding;
+ /** Mac OS X: Holds branding color to be used. */
+ QColor m_clrBranding;
+ /** Mac OS X: Holds the branding stuff width. */
+ int m_iBrandingWidth;
+#endif /* VBOX_WS_MAC */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIToolBar_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIToolButton.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIToolButton.cpp
new file mode 100644
index 00000000..d74ca5dc
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIToolButton.cpp
@@ -0,0 +1,59 @@
+/* $Id: QIToolButton.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIToolButton class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "QIToolButton.h"
+
+
+QIToolButton::QIToolButton(QWidget *pParent /* = 0 */)
+ : QToolButton(pParent)
+{
+#ifdef VBOX_WS_MAC
+ /* Keep size-hint alive: */
+ const QSize sh = sizeHint();
+ setStyleSheet("QToolButton { border: 0px none black; margin: 0px 0px 0px 0px; } QToolButton::menu-indicator {image: none;}");
+ setFixedSize(sh);
+#else /* !VBOX_WS_MAC */
+ setAutoRaise(true);
+#endif /* !VBOX_WS_MAC */
+}
+
+void QIToolButton::setAutoRaise(bool fEnabled)
+{
+#ifdef VBOX_WS_MAC
+ /* Ignore for macOS: */
+ Q_UNUSED(fEnabled);
+#else /* !VBOX_WS_MAC */
+ /* Call to base-class: */
+ QToolButton::setAutoRaise(fEnabled);
+#endif /* !VBOX_WS_MAC */
+}
+
+void QIToolButton::removeBorder()
+{
+ setStyleSheet("QToolButton { border: 0px }");
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIToolButton.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIToolButton.h
new file mode 100644
index 00000000..43da28f5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIToolButton.h
@@ -0,0 +1,57 @@
+/* $Id: QIToolButton.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIToolButton class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIToolButton_h
+#define FEQT_INCLUDED_SRC_extensions_QIToolButton_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QToolButton>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QToolButton subclass with extended functionality. */
+class SHARED_LIBRARY_STUFF QIToolButton : public QToolButton
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs tool-button passing @a pParent to the base-class. */
+ QIToolButton(QWidget *pParent = 0);
+
+ /** Sets whether the auto-raise status @a fEnabled. */
+ virtual void setAutoRaise(bool fEnabled);
+
+ /** Removes the tool-button border. */
+ void removeBorder();
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIToolButton_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITreeView.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QITreeView.cpp
new file mode 100644
index 00000000..52feaa9c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITreeView.cpp
@@ -0,0 +1,525 @@
+/* $Id: QITreeView.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITreeView class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QDragLeaveEvent>
+#include <QDragMoveEvent>
+#include <QDropEvent>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QSortFilterProxyModel>
+
+/* GUI includes: */
+#include "QITreeView.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QAccessibleObject extension used as an accessibility interface for QITreeViewItem. */
+class QIAccessibilityInterfaceForQITreeViewItem : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QITreeViewItem accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QITreeViewItem"))
+ return new QIAccessibilityInterfaceForQITreeViewItem(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ QIAccessibilityInterfaceForQITreeViewItem(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE;
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE;
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE;
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QITreeViewItem. */
+ QITreeViewItem *item() const { return qobject_cast<QITreeViewItem*>(object()); }
+};
+
+
+/** QAccessibleWidget extension used as an accessibility interface for QITreeView. */
+class QIAccessibilityInterfaceForQITreeView : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QITreeView accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QITreeView"))
+ return new QIAccessibilityInterfaceForQITreeView(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ QIAccessibilityInterfaceForQITreeView(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::List)
+ {}
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QITreeView. */
+ QITreeView *tree() const { return qobject_cast<QITreeView*>(widget()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQITreeViewItem implementation. *
+*********************************************************************************************************************************/
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITreeViewItem::parent() const
+{
+ /* Sanity check: */
+ AssertPtrReturn(item(), 0);
+
+ /* Return the parent: */
+ return item()->parentItem() ?
+ QAccessible::queryAccessibleInterface(item()->parentItem()) :
+ QAccessible::queryAccessibleInterface(item()->parentTree());
+}
+
+int QIAccessibilityInterfaceForQITreeViewItem::childCount() const
+{
+ /* Sanity check: */
+ AssertPtrReturn(item(), 0);
+ AssertPtrReturn(item()->parentTree(), 0);
+ AssertPtrReturn(item()->parentTree()->model(), 0);
+
+ /* Acquire item model-index: */
+ const QModelIndex itemIndex = item()->modelIndex();
+
+ /* Return the number of children: */
+ return item()->parentTree()->model()->rowCount(itemIndex);;
+}
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITreeViewItem::child(int iIndex) const /* override */
+{
+ /* Sanity check: */
+ AssertPtrReturn(item(), 0);
+ AssertPtrReturn(item()->parentTree(), 0);
+ AssertPtrReturn(item()->parentTree()->model(), 0);
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Acquire item model-index: */
+ const QModelIndex itemIndex = item()->modelIndex();
+ /* Acquire child model-index: */
+ const QModelIndex childIndex = item()->parentTree()->model()->index(iIndex, 0, itemIndex);
+
+ /* Check whether we have proxy model set or source one otherwise: */
+ const QSortFilterProxyModel *pProxyModel = qobject_cast<const QSortFilterProxyModel*>(item()->parentTree()->model());
+ /* Acquire source child model-index, which can be the same as child model-index: */
+ const QModelIndex sourceChildIndex = pProxyModel ? pProxyModel->mapToSource(childIndex) : childIndex;
+ /* Acquire source child item: */
+ QITreeViewItem *pItem = reinterpret_cast<QITreeViewItem*>(sourceChildIndex.internalPointer());
+ /* Return the child with the passed iIndex: */
+ return QAccessible::queryAccessibleInterface(pItem);
+}
+
+int QIAccessibilityInterfaceForQITreeViewItem::indexOfChild(const QAccessibleInterface *pChild) const /* override */
+{
+ /* Search for corresponding child: */
+ for (int i = 0; i < childCount(); ++i)
+ if (child(i) == pChild)
+ return i;
+
+ /* -1 by default: */
+ return -1;
+}
+
+QRect QIAccessibilityInterfaceForQITreeViewItem::rect() const
+{
+ /* Sanity check: */
+ AssertPtrReturn(item(), QRect());
+ AssertPtrReturn(item()->parentTree(), QRect());
+ AssertPtrReturn(item()->parentTree()->viewport(), QRect());
+
+ /* Get the local rect: */
+ const QRect itemRectInViewport = item()->rect();
+ const QSize itemSize = itemRectInViewport.size();
+ const QPoint itemPosInViewport = itemRectInViewport.topLeft();
+ const QPoint itemPosInScreen = item()->parentTree()->viewport()->mapToGlobal(itemPosInViewport);
+ const QRect itemRectInScreen = QRect(itemPosInScreen, itemSize);
+
+ /* Return the rect: */
+ return itemRectInScreen;
+}
+
+QString QIAccessibilityInterfaceForQITreeViewItem::text(QAccessible::Text enmTextRole) const
+{
+ /* Sanity check: */
+ AssertPtrReturn(item(), QString());
+
+ /* Return a text for the passed enmTextRole: */
+ switch (enmTextRole)
+ {
+ case QAccessible::Name: return item()->text();
+ default: break;
+ }
+
+ /* Null-string by default: */
+ return QString();
+}
+
+QAccessible::Role QIAccessibilityInterfaceForQITreeViewItem::role() const
+{
+ /* List if there are children, ListItem by default: */
+ return childCount() ? QAccessible::List : QAccessible::ListItem;
+}
+
+QAccessible::State QIAccessibilityInterfaceForQITreeViewItem::state() const
+{
+ /* Empty state by default: */
+ return QAccessible::State();
+}
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQITreeView implementation. *
+*********************************************************************************************************************************/
+
+int QIAccessibilityInterfaceForQITreeView::childCount() const
+{
+ /* Sanity check: */
+ AssertPtrReturn(tree(), 0);
+ AssertPtrReturn(tree()->model(), 0);
+
+ /* Acquire root model-index: */
+ const QModelIndex rootIndex = tree()->rootIndex();
+
+ /* Return the number of children: */
+ return tree()->model()->rowCount(rootIndex);
+}
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITreeView::child(int iIndex) const
+{
+ /* Sanity check: */
+ AssertPtrReturn(tree(), 0);
+ AssertPtrReturn(tree()->model(), 0);
+ AssertReturn(iIndex >= 0, 0);
+ if (iIndex >= childCount())
+ {
+ // WORKAROUND:
+ // Normally I would assert here, but Qt5 accessibility code has
+ // a hard-coded architecture for a tree-views which we do not like
+ // but have to live with and this architecture enumerates children
+ // of all levels as children of level 0, so Qt5 can try to address
+ // our interface with index which surely out of bounds by our laws.
+ // So let's assume that's exactly such case and try to enumerate
+ // visible children like they are a part of the list, not tree.
+ // printf("Invalid index: %d\n", iIndex);
+
+ // Take into account we also have header with 'column count' indexes,
+ // so we should start enumerating tree indexes since 'column count'.
+ const int iColumnCount = tree()->model()->columnCount();
+ int iCurrentIndex = iColumnCount;
+
+ // Set iterator to root model-index initially:
+ QModelIndex index = tree()->rootIndex();
+ // But if it has child, go deeper:
+ if (tree()->model()->index(0, 0, index).isValid())
+ index = tree()->model()->index(0, 0, index);
+
+ // Search for sibling with corresponding index:
+ while (index.isValid() && iCurrentIndex < iIndex)
+ {
+ ++iCurrentIndex;
+ if (iCurrentIndex % iColumnCount == 0)
+ index = tree()->indexBelow(index);
+ }
+
+ // Check whether we have proxy model set or source one otherwise:
+ const QSortFilterProxyModel *pProxyModel = qobject_cast<const QSortFilterProxyModel*>(tree()->model());
+ // Acquire source model-index, which can be the same as model-index:
+ const QModelIndex sourceIndex = pProxyModel ? pProxyModel->mapToSource(index) : index;
+
+ // Return what we found:
+ // if (sourceIndex.isValid())
+ // printf("Item found: [%s]\n", ((QITreeViewItem*)sourceIndex.internalPointer())->text().toUtf8().constData());
+ // else
+ // printf("Item not found\n");
+ return sourceIndex.isValid() ? QAccessible::queryAccessibleInterface((QITreeViewItem*)sourceIndex.internalPointer()) : 0;
+ }
+
+ /* Acquire root model-index: */
+ const QModelIndex rootIndex = tree()->rootIndex();
+ /* Acquire child model-index: */
+ const QModelIndex childIndex = tree()->model()->index(iIndex, 0, rootIndex);
+
+ /* Check whether we have proxy model set or source one otherwise: */
+ const QSortFilterProxyModel *pProxyModel = qobject_cast<const QSortFilterProxyModel*>(tree()->model());
+ /* Acquire source child model-index, which can be the same as child model-index: */
+ const QModelIndex sourceChildIndex = pProxyModel ? pProxyModel->mapToSource(childIndex) : childIndex;
+ /* Acquire source child item: */
+ QITreeViewItem *pItem = reinterpret_cast<QITreeViewItem*>(sourceChildIndex.internalPointer());
+ /* Return the child with the passed iIndex: */
+ return QAccessible::queryAccessibleInterface(pItem);
+}
+
+int QIAccessibilityInterfaceForQITreeView::indexOfChild(const QAccessibleInterface *pChild) const
+{
+ /* Search for corresponding child: */
+ for (int i = 0; i < childCount(); ++i)
+ if (child(i) == pChild)
+ return i;
+
+ /* -1 by default: */
+ return -1;
+}
+
+QString QIAccessibilityInterfaceForQITreeView::text(QAccessible::Text /* enmTextRole */) const
+{
+ /* Sanity check: */
+ AssertPtrReturn(tree(), QString());
+
+ /* Return tree whats-this: */
+ return tree()->whatsThis();
+}
+
+
+/*********************************************************************************************************************************
+* Class QITreeViewItem implementation. *
+*********************************************************************************************************************************/
+
+QRect QITreeViewItem::rect() const
+{
+ /* Redirect call to parent-tree: */
+ return parentTree() ? parentTree()->visualRect(modelIndex()) : QRect();
+}
+
+QModelIndex QITreeViewItem::modelIndex() const
+{
+ /* Acquire model: */
+ const QAbstractItemModel *pModel = parentTree()->model();
+ /* Check whether we have proxy model set or source one otherwise: */
+ const QSortFilterProxyModel *pProxyModel = qobject_cast<const QSortFilterProxyModel*>(pModel);
+
+ /* Acquire root model-index: */
+ const QModelIndex rootIndex = parentTree()->rootIndex();
+ /* Acquire source root model-index, which can be the same as root model-index: */
+ const QModelIndex sourceRootModelIndex = pProxyModel ? pProxyModel->mapToSource(rootIndex) : rootIndex;
+
+ /* Check whether we have root model-index here: */
+ if ( sourceRootModelIndex.internalPointer()
+ && sourceRootModelIndex.internalPointer() == this)
+ return rootIndex;
+
+ /* Determine our parent model-index: */
+ const QModelIndex parentIndex = parentItem() ? parentItem()->modelIndex() : rootIndex;
+
+ /* Determine our position inside parent: */
+ int iPositionInParent = -1;
+ for (int i = 0; i < pModel->rowCount(parentIndex); ++i)
+ {
+ /* Acquire child model-index: */
+ const QModelIndex childIndex = pModel->index(i, 0, parentIndex);
+ /* Acquire source child model-index, which can be the same as child model-index: */
+ const QModelIndex sourceChildModelIndex = pProxyModel ? pProxyModel->mapToSource(childIndex) : childIndex;
+
+ /* Check whether we have child model-index here: */
+ if ( sourceChildModelIndex.internalPointer()
+ && sourceChildModelIndex.internalPointer() == this)
+ {
+ iPositionInParent = i;
+ break;
+ }
+ }
+ /* Make sure we found something: */
+ if (iPositionInParent == -1)
+ return QModelIndex();
+
+ /* Return model-index as child of parent model-index: */
+ return pModel->index(iPositionInParent, 0, parentIndex);
+}
+
+
+/*********************************************************************************************************************************
+* Class QITreeView implementation. *
+*********************************************************************************************************************************/
+
+QITreeView::QITreeView(QWidget *pParent)
+ : QTreeView(pParent)
+{
+ /* Prepare all: */
+ prepare();
+}
+
+void QITreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+ /* Notify listeners about it: */
+ emit currentItemChanged(current, previous);
+ /* Call to base-class: */
+ QTreeView::currentChanged(current, previous);
+}
+
+void QITreeView::drawBranches(QPainter *pPainter, const QRect &rect, const QModelIndex &index) const
+{
+ /* Notify listeners about it: */
+ emit drawItemBranches(pPainter, rect, index);
+ /* Call to base-class: */
+ QTreeView::drawBranches(pPainter, rect, index);
+}
+
+void QITreeView::mouseMoveEvent(QMouseEvent *pEvent)
+{
+ /* Reject event initially: */
+ pEvent->setAccepted(false);
+ /* Notify listeners about event allowing them to handle it: */
+ emit mouseMoved(pEvent);
+ /* Call to base-class only if event was not yet accepted: */
+ if (!pEvent->isAccepted())
+ QTreeView::mouseMoveEvent(pEvent);
+}
+
+void QITreeView::mousePressEvent(QMouseEvent *pEvent)
+{
+ /* Reject event initially: */
+ pEvent->setAccepted(false);
+ /* Notify listeners about event allowing them to handle it: */
+ emit mousePressed(pEvent);
+ /* Call to base-class only if event was not yet accepted: */
+ if (!pEvent->isAccepted())
+ QTreeView::mousePressEvent(pEvent);
+}
+
+void QITreeView::mouseReleaseEvent(QMouseEvent *pEvent)
+{
+ /* Reject event initially: */
+ pEvent->setAccepted(false);
+ /* Notify listeners about event allowing them to handle it: */
+ emit mouseReleased(pEvent);
+ /* Call to base-class only if event was not yet accepted: */
+ if (!pEvent->isAccepted())
+ QTreeView::mouseReleaseEvent(pEvent);
+}
+
+void QITreeView::mouseDoubleClickEvent(QMouseEvent *pEvent)
+{
+ /* Reject event initially: */
+ pEvent->setAccepted(false);
+ /* Notify listeners about event allowing them to handle it: */
+ emit mouseDoubleClicked(pEvent);
+ /* Call to base-class only if event was not yet accepted: */
+ if (!pEvent->isAccepted())
+ QTreeView::mouseDoubleClickEvent(pEvent);
+}
+
+void QITreeView::dragEnterEvent(QDragEnterEvent *pEvent)
+{
+ /* Reject event initially: */
+ pEvent->setAccepted(false);
+ /* Notify listeners about event allowing them to handle it: */
+ emit dragEntered(pEvent);
+ /* Call to base-class only if event was not yet accepted: */
+ if (!pEvent->isAccepted())
+ QTreeView::dragEnterEvent(pEvent);
+}
+
+void QITreeView::dragMoveEvent(QDragMoveEvent *pEvent)
+{
+ /* Reject event initially: */
+ pEvent->setAccepted(false);
+ /* Notify listeners about event allowing them to handle it: */
+ emit dragMoved(pEvent);
+ /* Call to base-class only if event was not yet accepted: */
+ if (!pEvent->isAccepted())
+ QTreeView::dragMoveEvent(pEvent);
+}
+
+void QITreeView::dragLeaveEvent(QDragLeaveEvent *pEvent)
+{
+ /* Reject event initially: */
+ pEvent->setAccepted(false);
+ /* Notify listeners about event allowing them to handle it: */
+ emit dragLeft(pEvent);
+ /* Call to base-class only if event was not yet accepted: */
+ if (!pEvent->isAccepted())
+ QTreeView::dragLeaveEvent(pEvent);
+}
+
+void QITreeView::dropEvent(QDropEvent *pEvent)
+{
+ /* Reject event initially: */
+ pEvent->setAccepted(false);
+ /* Notify listeners about event allowing them to handle it: */
+ emit dragDropped(pEvent);
+ /* Call to base-class only if event was not yet accepted: */
+ if (!pEvent->isAccepted())
+ QTreeView::dropEvent(pEvent);
+}
+
+void QITreeView::prepare()
+{
+ /* Install QITreeViewItem accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQITreeViewItem::pFactory);
+ /* Install QITreeView accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQITreeView::pFactory);
+
+ /* Mark header hidden: */
+ setHeaderHidden(true);
+ /* Mark root hidden: */
+ setRootIsDecorated(false);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITreeView.h b/src/VBox/Frontends/VirtualBox/src/extensions/QITreeView.h
new file mode 100644
index 00000000..621bf410
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITreeView.h
@@ -0,0 +1,177 @@
+/* $Id: QITreeView.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITreeView class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QITreeView_h
+#define FEQT_INCLUDED_SRC_extensions_QITreeView_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTreeView>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QITreeViewItem;
+class QITreeView;
+
+
+/** OObject subclass used as item for the QITreeView. */
+class SHARED_LIBRARY_STUFF QITreeViewItem : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs tree-view item for passed @a pParent. */
+ QITreeViewItem(QITreeView *pParent)
+ : m_pParentTree(pParent)
+ , m_pParentItem(0)
+ {}
+
+ /** Constructs tree-view item for passed @a pParent. */
+ QITreeViewItem(QITreeViewItem *pParentItem)
+ : m_pParentTree(pParentItem ? pParentItem->parentTree() : 0)
+ , m_pParentItem(pParentItem)
+ {}
+
+ /** Returns the parent tree-view. */
+ QITreeView *parentTree() const { return m_pParentTree; }
+ /** Returns the parent tree-view item. */
+ QITreeViewItem *parentItem() const { return m_pParentItem; }
+
+ /** Returns the number of children. */
+ virtual int childCount() const = 0;
+ /** Returns the child item with @a iIndex. */
+ virtual QITreeViewItem *childItem(int iIndex) const = 0;
+
+ /** Returns the item text. */
+ virtual QString text() const = 0;
+
+ /** Returns the rectangle. */
+ QRect rect() const;
+
+ /** Returns the model-index: */
+ QModelIndex modelIndex() const;
+
+private:
+
+ /** Holds the parent tree reference. */
+ QITreeView *m_pParentTree;
+ /** Holds the parent item reference. */
+ QITreeViewItem *m_pParentItem;
+};
+
+
+/** QTreeView subclass extending standard functionality. */
+class SHARED_LIBRARY_STUFF QITreeView : public QTreeView
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about index changed from @a previous to @a current.*/
+ void currentItemChanged(const QModelIndex &current, const QModelIndex &previous);
+
+ /** Notifies listeners about painting of item branches.
+ * @param pPainter Brings the painter to draw branches.
+ * @param rect Brings the rectangle embedding branches.
+ * @param index Brings the index of the item for which branches will be painted. */
+ void drawItemBranches(QPainter *pPainter, const QRect &rect, const QModelIndex &index) const;
+
+ /** Notifies listeners about mouse moved @a pEvent. */
+ void mouseMoved(QMouseEvent *pEvent);
+ /** Notifies listeners about mouse pressed @a pEvent. */
+ void mousePressed(QMouseEvent *pEvent);
+ /** Notifies listeners about mouse released @a pEvent. */
+ void mouseReleased(QMouseEvent *pEvent);
+ /** Notifies listeners about mouse double-clicked @a pEvent. */
+ void mouseDoubleClicked(QMouseEvent *pEvent);
+
+ /** Notifies listeners about mouse drag entered @a pEvent. */
+ void dragEntered(QDragEnterEvent *pEvent);
+ /** Notifies listeners about mouse drag moved @a pEvent. */
+ void dragMoved(QDragMoveEvent *pEvent);
+ /** Notifies listeners about mouse drag left @a pEvent. */
+ void dragLeft(QDragLeaveEvent *pEvent);
+ /** Notifies listeners about mouse drag dropped @a pEvent. */
+ void dragDropped(QDropEvent *pEvent);
+
+public:
+
+ /** Constructs tree-view passing @a pParent to the base-class. */
+ QITreeView(QWidget *pParent = 0);
+
+ /** Returns the number of children. */
+ virtual int childCount() const { return 0; }
+ /** Returns the child item with @a iIndex. */
+ virtual QITreeViewItem *childItem(int /* iIndex */) const { return 0; }
+
+ /** Returns child rectangle. */
+ QRect visualRect(const QModelIndex &index) const { return QTreeView::visualRect(index); }
+
+protected slots:
+
+ /** Handles index changed from @a previous to @a current.*/
+ void currentChanged(const QModelIndex &current, const QModelIndex &previous);
+
+protected:
+
+ /** Handles painting of item branches.
+ * @param pPainter Brings the painter to draw branches.
+ * @param rect Brings the rectangle embedding branches.
+ * @param index Brings the index of the item for which branches will be painted. */
+ virtual void drawBranches(QPainter *pPainter, const QRect &rect, const QModelIndex &index) const RT_OVERRIDE;
+
+ /** Handles mouse move @a pEvent. */
+ virtual void mouseMoveEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse press @a pEvent. */
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse release @a pEvent. */
+ virtual void mouseReleaseEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse double-click @a pEvent. */
+ virtual void mouseDoubleClickEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse drag enter @a pEvent. */
+ virtual void dragEnterEvent(QDragEnterEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse drag move @a pEvent. */
+ virtual void dragMoveEvent(QDragMoveEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse drag leave @a pEvent. */
+ virtual void dragLeaveEvent(QDragLeaveEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse drop @a pEvent. */
+ virtual void dropEvent(QDropEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QITreeView_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITreeWidget.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QITreeWidget.cpp
new file mode 100644
index 00000000..05dfb0b2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITreeWidget.cpp
@@ -0,0 +1,511 @@
+/* $Id: QITreeWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITreeWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QPainter>
+#include <QResizeEvent>
+
+/* GUI includes: */
+#include "QITreeWidget.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QAccessibleObject extension used as an accessibility interface for QITreeWidgetItem. */
+class QIAccessibilityInterfaceForQITreeWidgetItem : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QITreeWidgetItem accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QITreeWidgetItem"))
+ return new QIAccessibilityInterfaceForQITreeWidgetItem(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ QIAccessibilityInterfaceForQITreeWidgetItem(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE;
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE;
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE;
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QITreeWidgetItem. */
+ QITreeWidgetItem *item() const { return qobject_cast<QITreeWidgetItem*>(object()); }
+};
+
+
+/** QAccessibleWidget extension used as an accessibility interface for QITreeWidget. */
+class QIAccessibilityInterfaceForQITreeWidget : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating QITreeWidget accessibility interface: */
+ if (pObject && strClassname == QLatin1String("QITreeWidget"))
+ return new QIAccessibilityInterfaceForQITreeWidget(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ QIAccessibilityInterfaceForQITreeWidget(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::List)
+ {}
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding QITreeWidget. */
+ QITreeWidget *tree() const { return qobject_cast<QITreeWidget*>(widget()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQITreeWidgetItem implementation. *
+*********************************************************************************************************************************/
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITreeWidgetItem::parent() const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Return the parent: */
+ return item()->parentItem() ?
+ QAccessible::queryAccessibleInterface(item()->parentItem()) :
+ QAccessible::queryAccessibleInterface(item()->parentTree());
+}
+
+int QIAccessibilityInterfaceForQITreeWidgetItem::childCount() const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Return the number of children: */
+ return item()->childCount();
+}
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITreeWidgetItem::child(int iIndex) const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Return the child with the passed iIndex: */
+ return QAccessible::queryAccessibleInterface(item()->childItem(iIndex));
+}
+
+int QIAccessibilityInterfaceForQITreeWidgetItem::indexOfChild(const QAccessibleInterface *pChild) const
+{
+ /* Search for corresponding child: */
+ for (int iIndex = 0; iIndex < childCount(); ++iIndex)
+ if (child(iIndex) == pChild)
+ return iIndex;
+
+ /* -1 by default: */
+ return -1;
+}
+
+QRect QIAccessibilityInterfaceForQITreeWidgetItem::rect() const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QRect());
+
+ /* Compose common region: */
+ QRegion region;
+
+ /* Append item rectangle: */
+ const QRect itemRectInViewport = item()->parentTree()->visualItemRect(item());
+ const QSize itemSize = itemRectInViewport.size();
+ const QPoint itemPosInViewport = itemRectInViewport.topLeft();
+ const QPoint itemPosInScreen = item()->parentTree()->viewport()->mapToGlobal(itemPosInViewport);
+ const QRect itemRectInScreen = QRect(itemPosInScreen, itemSize);
+ region += itemRectInScreen;
+
+ /* Append children rectangles: */
+ for (int i = 0; i < childCount(); ++i)
+ region += child(i)->rect();
+
+ /* Return common region bounding rectangle: */
+ return region.boundingRect();
+}
+
+QString QIAccessibilityInterfaceForQITreeWidgetItem::text(QAccessible::Text enmTextRole) const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QString());
+
+ /* Return a text for the passed enmTextRole: */
+ switch (enmTextRole)
+ {
+ case QAccessible::Name: return item()->defaultText();
+ default: break;
+ }
+
+ /* Null-string by default: */
+ return QString();
+}
+
+QAccessible::Role QIAccessibilityInterfaceForQITreeWidgetItem::role() const
+{
+ /* Return the role of item with children: */
+ if (childCount() > 0)
+ return QAccessible::List;
+
+ /* ListItem by default: */
+ return QAccessible::ListItem;
+}
+
+QAccessible::State QIAccessibilityInterfaceForQITreeWidgetItem::state() const
+{
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QAccessible::State());
+
+ /* Compose the state: */
+ QAccessible::State state;
+ state.focusable = true;
+ state.selectable = true;
+
+ /* Compose the state of current item: */
+ if ( item()
+ && item() == QITreeWidgetItem::toItem(item()->treeWidget()->currentItem()))
+ {
+ state.active = true;
+ state.focused = true;
+ state.selected = true;
+ }
+
+ /* Compose the state of checked item: */
+ if ( item()
+ && item()->checkState(0) != Qt::Unchecked)
+ {
+ state.checked = true;
+ if (item()->checkState(0) == Qt::PartiallyChecked)
+ state.checkStateMixed = true;
+ }
+
+ /* Return the state: */
+ return state;
+}
+
+
+/*********************************************************************************************************************************
+* Class QIAccessibilityInterfaceForQITreeWidget implementation. *
+*********************************************************************************************************************************/
+
+int QIAccessibilityInterfaceForQITreeWidget::childCount() const
+{
+ /* Make sure tree still alive: */
+ AssertPtrReturn(tree(), 0);
+
+ /* Return the number of children: */
+ return tree()->childCount();
+}
+
+QAccessibleInterface *QIAccessibilityInterfaceForQITreeWidget::child(int iIndex) const
+{
+ /* Make sure tree still alive: */
+ AssertPtrReturn(tree(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0, 0);
+ if (iIndex >= childCount())
+ {
+ // WORKAROUND:
+ // Normally I would assert here, but Qt5 accessibility code has
+ // a hard-coded architecture for a tree-widgets which we do not like
+ // but have to live with and this architecture enumerates children
+ // of all levels as children of level 0, so Qt5 can try to address
+ // our interface with index which surely out of bounds by our laws.
+ // So let's assume that's exactly such case and try to enumerate
+ // visible children like they are a part of the list, not tree.
+ // printf("Invalid index: %d\n", iIndex);
+
+ // Take into account we also have header with 'column count' indexes,
+ // so we should start enumerating tree indexes since 'column count'.
+ const int iColumnCount = tree()->columnCount();
+ int iCurrentIndex = iColumnCount;
+
+ // Do some sanity check as well, enough?
+ AssertReturn(iIndex >= iColumnCount, 0);
+
+ // Search for sibling with corresponding index:
+ QTreeWidgetItem *pItem = tree()->topLevelItem(0);
+ while (pItem && iCurrentIndex < iIndex)
+ {
+ ++iCurrentIndex;
+ if (iCurrentIndex % iColumnCount == 0)
+ pItem = tree()->itemBelow(pItem);
+ }
+
+ // Return what we found:
+ // if (pItem)
+ // printf("Item found: [%s]\n", pItem->text(0).toUtf8().constData());
+ // else
+ // printf("Item not found\n");
+ return pItem ? QAccessible::queryAccessibleInterface(QITreeWidgetItem::toItem(pItem)) : 0;
+ }
+
+ /* Return the child with the passed iIndex: */
+ return QAccessible::queryAccessibleInterface(tree()->childItem(iIndex));
+}
+
+int QIAccessibilityInterfaceForQITreeWidget::indexOfChild(const QAccessibleInterface *pChild) const
+{
+ /* Make sure tree still alive: */
+ AssertPtrReturn(tree(), -1);
+ /* Make sure child is valid: */
+ AssertReturn(pChild, -1);
+
+ // WORKAROUND:
+ // Not yet sure how to handle this for tree widget with multiple columns, so this is a simple hack:
+ const QModelIndex index = tree()->itemIndex(qobject_cast<QITreeWidgetItem*>(pChild->object()));
+ const int iIndex = index.row();
+ return iIndex;
+}
+
+QString QIAccessibilityInterfaceForQITreeWidget::text(QAccessible::Text /* enmTextRole */) const
+{
+ /* Make sure tree still alive: */
+ AssertPtrReturn(tree(), QString());
+
+ /* Gather suitable text: */
+ QString strText = tree()->toolTip();
+ if (strText.isEmpty())
+ strText = tree()->whatsThis();
+ return strText;
+}
+
+
+/*********************************************************************************************************************************
+* Class QITreeWidgetItem implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+QITreeWidgetItem *QITreeWidgetItem::toItem(QTreeWidgetItem *pItem)
+{
+ /* Make sure alive QITreeWidgetItem passed: */
+ if (!pItem || pItem->type() != ItemType)
+ return 0;
+
+ /* Return casted QITreeWidgetItem: */
+ return static_cast<QITreeWidgetItem*>(pItem);
+}
+
+/* static */
+const QITreeWidgetItem *QITreeWidgetItem::toItem(const QTreeWidgetItem *pItem)
+{
+ /* Make sure alive QITreeWidgetItem passed: */
+ if (!pItem || pItem->type() != ItemType)
+ return 0;
+
+ /* Return casted QITreeWidgetItem: */
+ return static_cast<const QITreeWidgetItem*>(pItem);
+}
+
+QITreeWidgetItem::QITreeWidgetItem()
+ : QTreeWidgetItem(ItemType)
+{
+}
+
+QITreeWidgetItem::QITreeWidgetItem(QITreeWidget *pTreeWidget)
+ : QTreeWidgetItem(pTreeWidget, ItemType)
+{
+}
+
+QITreeWidgetItem::QITreeWidgetItem(QITreeWidgetItem *pTreeWidgetItem)
+ : QTreeWidgetItem(pTreeWidgetItem, ItemType)
+{
+}
+
+QITreeWidgetItem::QITreeWidgetItem(QITreeWidget *pTreeWidget, const QStringList &strings)
+ : QTreeWidgetItem(pTreeWidget, strings, ItemType)
+{
+}
+
+QITreeWidgetItem::QITreeWidgetItem(QITreeWidgetItem *pTreeWidgetItem, const QStringList &strings)
+ : QTreeWidgetItem(pTreeWidgetItem, strings, ItemType)
+{
+}
+
+QITreeWidget *QITreeWidgetItem::parentTree() const
+{
+ return treeWidget() ? qobject_cast<QITreeWidget*>(treeWidget()) : 0;
+}
+
+QITreeWidgetItem *QITreeWidgetItem::parentItem() const
+{
+ return QTreeWidgetItem::parent() ? toItem(QTreeWidgetItem::parent()) : 0;
+}
+
+QITreeWidgetItem *QITreeWidgetItem::childItem(int iIndex) const
+{
+ return QTreeWidgetItem::child(iIndex) ? toItem(QTreeWidgetItem::child(iIndex)) : 0;
+}
+
+QString QITreeWidgetItem::defaultText() const
+{
+ /* Return 1st cell text as default: */
+ return text(0);
+}
+
+
+/*********************************************************************************************************************************
+* Class QITreeWidget implementation. *
+*********************************************************************************************************************************/
+
+QITreeWidget::QITreeWidget(QWidget *pParent)
+ : QTreeWidget(pParent)
+{
+ /* Install QITreeWidget accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQITreeWidget::pFactory);
+ /* Install QITreeWidgetItem accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForQITreeWidgetItem::pFactory);
+
+ // WORKAROUND:
+ // Ok, what do we have here..
+ // There is a bug in QAccessible framework which might be just treated like
+ // a functionality flaw. It consist in fact that if an accessibility client
+ // is enabled, base-class can request an accessibility interface in own
+ // constructor before the sub-class registers own factory, so we have to
+ // recreate interface after we finished with our own initialization.
+ QAccessibleInterface *pInterface = QAccessible::queryAccessibleInterface(this);
+ if (pInterface)
+ {
+ QAccessible::deleteAccessibleInterface(QAccessible::uniqueId(pInterface));
+ QAccessible::queryAccessibleInterface(this); // <= new one, proper..
+ }
+}
+
+void QITreeWidget::setSizeHintForItems(const QSize &sizeHint)
+{
+ /* Pass the sizeHint to all the top-level items: */
+ for (int i = 0; i < topLevelItemCount(); ++i)
+ topLevelItem(i)->setSizeHint(0, sizeHint);
+}
+
+int QITreeWidget::childCount() const
+{
+ return invisibleRootItem()->childCount();
+}
+
+QITreeWidgetItem *QITreeWidget::childItem(int iIndex) const
+{
+ return invisibleRootItem()->child(iIndex) ? QITreeWidgetItem::toItem(invisibleRootItem()->child(iIndex)) : 0;
+}
+
+QModelIndex QITreeWidget::itemIndex(QTreeWidgetItem *pItem)
+{
+ return indexFromItem(pItem);
+}
+
+QList<QTreeWidgetItem*> QITreeWidget::filterItems(const QITreeWidgetItemFilter &filter, QTreeWidgetItem *pParent /* = 0 */)
+{
+ QList<QTreeWidgetItem*> filteredItemList;
+ filterItemsInternal(filter, pParent ? pParent : invisibleRootItem(), filteredItemList);
+ return filteredItemList;
+}
+
+void QITreeWidget::paintEvent(QPaintEvent *pEvent)
+{
+ /* Create item painter: */
+ QPainter painter;
+ painter.begin(viewport());
+
+ /* Notify listeners about painting: */
+ QTreeWidgetItemIterator it(this);
+ while (*it)
+ {
+ emit painted(*it, &painter);
+ ++it;
+ }
+
+ /* Close item painter: */
+ painter.end();
+
+ /* Call to base-class: */
+ QTreeWidget::paintEvent(pEvent);
+}
+
+void QITreeWidget::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QTreeWidget::resizeEvent(pEvent);
+
+ /* Notify listeners about resizing: */
+ emit resized(pEvent->size(), pEvent->oldSize());
+}
+
+void QITreeWidget::filterItemsInternal(const QITreeWidgetItemFilter &filter, QTreeWidgetItem *pParent,
+ QList<QTreeWidgetItem*> &filteredItemList)
+{
+ if (!pParent)
+ return;
+ if (filter(pParent))
+ filteredItemList.append(pParent);
+
+ for (int i = 0; i < pParent->childCount(); ++i)
+ filterItemsInternal(filter, pParent->child(i), filteredItemList);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QITreeWidget.h b/src/VBox/Frontends/VirtualBox/src/extensions/QITreeWidget.h
new file mode 100644
index 00000000..0dd2e275
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QITreeWidget.h
@@ -0,0 +1,147 @@
+/* $Id: QITreeWidget.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QITreeWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QITreeWidget_h
+#define FEQT_INCLUDED_SRC_extensions_QITreeWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTreeWidget>
+#include <QTreeWidgetItem>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QITreeWidget;
+
+/** A functor base to be passed to QITabWidget::filterItems(..).
+ * Overload operator()(..) to filter out tree items. */
+class SHARED_LIBRARY_STUFF QITreeWidgetItemFilter
+{
+public:
+
+ /** Destructs item filter. */
+ virtual ~QITreeWidgetItemFilter() { /* Make VC++ 19.2 happy. */ }
+
+ /** Returns whether item can pass the filter. */
+ virtual bool operator()(QTreeWidgetItem*) const
+ {
+ return true;
+ }
+};
+
+/** QTreeWidgetItem subclass extending standard functionality. */
+class SHARED_LIBRARY_STUFF QITreeWidgetItem : public QObject, public QTreeWidgetItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Item type for QITreeWidgetItem. */
+ enum { ItemType = QTreeWidgetItem::UserType + 1 };
+
+ /** Casts QTreeWidgetItem* to QITreeWidgetItem* if possible. */
+ static QITreeWidgetItem *toItem(QTreeWidgetItem *pItem);
+ /** Casts const QTreeWidgetItem* to const QITreeWidgetItem* if possible. */
+ static const QITreeWidgetItem *toItem(const QTreeWidgetItem *pItem);
+
+ /** Constructs item. */
+ QITreeWidgetItem();
+
+ /** Constructs item passing @a pTreeWidget into the base-class. */
+ QITreeWidgetItem(QITreeWidget *pTreeWidget);
+ /** Constructs item passing @a pTreeWidgetItem into the base-class. */
+ QITreeWidgetItem(QITreeWidgetItem *pTreeWidgetItem);
+
+ /** Constructs item passing @a pTreeWidget and @a strings into the base-class. */
+ QITreeWidgetItem(QITreeWidget *pTreeWidget, const QStringList &strings);
+ /** Constructs item passing @a pTreeWidgetItem and @a strings into the base-class. */
+ QITreeWidgetItem(QITreeWidgetItem *pTreeWidgetItem, const QStringList &strings);
+
+ /** Returns the parent tree-widget. */
+ QITreeWidget *parentTree() const;
+ /** Returns the parent tree-widget item. */
+ QITreeWidgetItem *parentItem() const;
+
+ /** Returns the child tree-widget item with @a iIndex. */
+ QITreeWidgetItem *childItem(int iIndex) const;
+
+ /** Returns default text. */
+ virtual QString defaultText() const;
+};
+
+
+/** QTreeWidget subclass extending standard functionality. */
+class SHARED_LIBRARY_STUFF QITreeWidget : public QTreeWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about particular tree-widget @a pItem is painted with @a pPainter. */
+ void painted(QTreeWidgetItem *pItem, QPainter *pPainter);
+ /** Notifies about tree-widget being resized from @a oldSize to @a size. */
+ void resized(const QSize &size, const QSize &oldSize);
+
+public:
+
+ /** Constructs tree-widget passing @a pParent to the base-class. */
+ QITreeWidget(QWidget *pParent = 0);
+
+ /** Defines @a sizeHint for tree-widget items. */
+ void setSizeHintForItems(const QSize &sizeHint);
+
+ /** Returns the number of children. */
+ int childCount() const;
+ /** Returns the child item with @a iIndex. */
+ QITreeWidgetItem *childItem(int iIndex) const;
+ /** Returns a model-index of @a pItem specified. */
+ QModelIndex itemIndex(QTreeWidgetItem *pItem);
+ /** Recurses thru the subtree with a root @a pParent and returns a list of tree-items filtered by @a filter.
+ * When @a pParent is null then QTreeWidget::invisibleRootItem() is used as the root item. */
+ QList<QTreeWidgetItem*> filterItems(const QITreeWidgetItemFilter &filter, QTreeWidgetItem *pParent = 0);
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ void paintEvent(QPaintEvent *pEvent);
+ /** Handles resize @a pEvent. */
+ void resizeEvent(QResizeEvent *pEvent);
+
+private:
+
+ /** Recurses thru the subtree with a root @a pParent and appends a
+ * list of tree-items filtered by @a filter to @a filteredItemList. */
+ void filterItemsInternal(const QITreeWidgetItemFilter &filter, QTreeWidgetItem *pParent,
+ QList<QTreeWidgetItem*> &filteredItemList);
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QITreeWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIWidgetValidator.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/QIWidgetValidator.cpp
new file mode 100644
index 00000000..54089351
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIWidgetValidator.cpp
@@ -0,0 +1,199 @@
+/* $Id: QIWidgetValidator.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIWidgetValidator class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "QIWidgetValidator.h"
+#include "UISettingsPage.h"
+
+
+/*********************************************************************************************************************************
+* Class QObjectValidator implementation. *
+*********************************************************************************************************************************/
+
+QObjectValidator::QObjectValidator(QValidator *pValidator, QObject *pParent /* = 0 */)
+ : QObject(pParent)
+ , m_pValidator(pValidator)
+ , m_enmState(QValidator::Invalid)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void QObjectValidator::sltValidate(QString strInput /* = QString() */)
+{
+ /* Make sure validator assigned: */
+ AssertPtrReturnVoid(m_pValidator);
+
+ /* Validate: */
+ int iPosition = 0;
+ const QValidator::State enmState = m_pValidator->validate(strInput, iPosition);
+
+ /* If validity state changed: */
+ if (m_enmState != enmState)
+ {
+ /* Update last validity state: */
+ m_enmState = enmState;
+
+ /* Notifies listener(s) about validity change: */
+ emit sigValidityChange(m_enmState);
+ }
+}
+
+void QObjectValidator::prepare()
+{
+ /* Make sure validator assigned: */
+ AssertPtrReturnVoid(m_pValidator);
+
+ /* Register validator as child: */
+ m_pValidator->setParent(this);
+
+ /* Validate: */
+ sltValidate();
+}
+
+
+/*********************************************************************************************************************************
+* Class QObjectValidatorGroup implementation. *
+*********************************************************************************************************************************/
+
+void QObjectValidatorGroup::addObjectValidator(QObjectValidator *pObjectValidator)
+{
+ /* Make sure object-validator passed: */
+ AssertPtrReturnVoid(pObjectValidator);
+
+ /* Register object-validator as child: */
+ pObjectValidator->setParent(this);
+
+ /* Insert object-validator to internal map: */
+ m_group.insert(pObjectValidator, toResult(pObjectValidator->state()));
+
+ /* Attach object-validator to group: */
+ connect(pObjectValidator, &QObjectValidator::sigValidityChange,
+ this, &QObjectValidatorGroup::sltValidate);
+}
+
+void QObjectValidatorGroup::sltValidate(QValidator::State enmState)
+{
+ /* Determine sender object-validator: */
+ QObjectValidator *pObjectValidatorSender = qobject_cast<QObjectValidator*>(sender());
+ /* Make sure that is one of our senders: */
+ AssertReturnVoid(pObjectValidatorSender && m_group.contains(pObjectValidatorSender));
+
+ /* Update internal map: */
+ m_group[pObjectValidatorSender] = toResult(enmState);
+
+ /* Enumerate all the registered object-validators: */
+ bool fResult = true;
+ foreach (QObjectValidator *pObjectValidator, m_group.keys())
+ if (!toResult(pObjectValidator->state()))
+ {
+ fResult = false;
+ break;
+ }
+
+ /* If validity state changed: */
+ if (m_fResult != fResult)
+ {
+ /* Update last validity state: */
+ m_fResult = fResult;
+
+ /* Notifies listener(s) about validity change: */
+ emit sigValidityChange(m_fResult);
+ }
+}
+
+/* static */
+bool QObjectValidatorGroup::toResult(QValidator::State enmState)
+{
+ return enmState == QValidator::Acceptable;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIPageValidator implementation. *
+*********************************************************************************************************************************/
+
+QPixmap UIPageValidator::warningPixmap() const
+{
+ return m_pPage->warningPixmap();
+}
+
+QString UIPageValidator::internalName() const
+{
+ return m_pPage->internalName();
+}
+
+void UIPageValidator::setLastMessage(const QString &strLastMessage)
+{
+ /* Remember new message: */
+ m_strLastMessage = strLastMessage;
+
+ /* Should we show corresponding warning icon? */
+ if (m_strLastMessage.isEmpty())
+ emit sigHideWarningIcon();
+ else
+ emit sigShowWarningIcon();
+}
+
+void UIPageValidator::revalidate()
+{
+ /* Notify listener(s) about validity change: */
+ emit sigValidityChanged(this);
+}
+
+
+/*********************************************************************************************************************************
+* Class QIULongValidator implementation. *
+*********************************************************************************************************************************/
+
+QValidator::State QIULongValidator::validate(QString &strInput, int &iPosition) const
+{
+ Q_UNUSED(iPosition);
+
+ /* Get the stripped string: */
+ QString strStripped = strInput.trimmed();
+
+ /* 'Intermediate' for empty string or started from '0x': */
+ if (strStripped.isEmpty() ||
+ strStripped.toUpper() == QString("0x").toUpper())
+ return Intermediate;
+
+ /* Convert to ulong: */
+ bool fOk;
+ ulong uEntered = strInput.toULong(&fOk, 0);
+
+ /* 'Invalid' if failed to convert: */
+ if (!fOk)
+ return Invalid;
+
+ /* 'Acceptable' if fits the bounds: */
+ if (uEntered >= m_uBottom && uEntered <= m_uTop)
+ return Acceptable;
+
+ /* 'Invalid' if more than top, 'Intermediate' if less than bottom: */
+ return uEntered > m_uTop ? Invalid : Intermediate;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/QIWidgetValidator.h b/src/VBox/Frontends/VirtualBox/src/extensions/QIWidgetValidator.h
new file mode 100644
index 00000000..e0514bd0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/QIWidgetValidator.h
@@ -0,0 +1,239 @@
+/* $Id: QIWidgetValidator.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIWidgetValidator class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_QIWidgetValidator_h
+#define FEQT_INCLUDED_SRC_extensions_QIWidgetValidator_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QPixmap>
+#include <QValidator>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QPixmap;
+class QString;
+class UISettingsPage;
+
+
+/** QObject extension,
+ * providing passed QObject with validation routine. */
+class SHARED_LIBRARY_STUFF QObjectValidator : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listener(s) about validity changed to @a enmState. */
+ void sigValidityChange(QValidator::State enmState);
+
+public:
+
+ /** Constructs object validator passing @a pParent to the base-class.
+ * @param pValidator Brings the validator passed on to the OObject
+ * children and used to perform validation itself. */
+ QObjectValidator(QValidator *pValidator, QObject *pParent = 0);
+
+ /** Returns last validation state. */
+ QValidator::State state() const { return m_enmState; }
+
+public slots:
+
+ /** Performs validation: */
+ void sltValidate(QString strInput = QString());
+
+private:
+
+ /** Prepare routine. */
+ void prepare();
+
+ /** Holds the validator reference. */
+ QValidator *m_pValidator;
+
+ /** Holds the validation state. */
+ QValidator::State m_enmState;
+};
+
+
+/** QObject extension,
+ * which can group various QObjectValidator instances to operate on. */
+class SHARED_LIBRARY_STUFF QObjectValidatorGroup : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listener(s) about validity changed to @a fValid. */
+ void sigValidityChange(bool fValid);
+
+public:
+
+ /** Constructs validation group passing @a pParent to the base-class. */
+ QObjectValidatorGroup(QObject *pParent)
+ : QObject(pParent)
+ , m_fResult(false)
+ {}
+
+ /** Adds @a pObjectValidator.
+ * @note The ownership of @a pObjectValidator is transferred to the group,
+ * and it's the group's responsibility to delete it. */
+ void addObjectValidator(QObjectValidator *pObjectValidator);
+
+ /** Returns last validation result. */
+ bool result() const { return m_fResult; }
+
+private slots:
+
+ /** Performs validation for a passed @a enmState. */
+ void sltValidate(QValidator::State enmState);
+
+private:
+
+ /** Converts QValidator::State to bool result. */
+ static bool toResult(QValidator::State enmState);
+
+ /** Holds object-validators and their states. */
+ QMap<QObjectValidator*, bool> m_group;
+
+ /** Holds validation result. */
+ bool m_fResult;
+};
+
+
+/** Page validator prototype. */
+class SHARED_LIBRARY_STUFF UIPageValidator : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about validity change for @a pValidator. */
+ void sigValidityChanged(UIPageValidator *pValidator);
+
+ /** Asks listener to show warning icon. */
+ void sigShowWarningIcon();
+ /** Asks listener to hide warning icon. */
+ void sigHideWarningIcon();
+
+public:
+
+ /** Constructs page validator for a certain @a pPage,
+ * passing @a pParent to the base-class. */
+ UIPageValidator(QObject *pParent, UISettingsPage *pPage)
+ : QObject(pParent)
+ , m_pPage(pPage)
+ , m_fIsValid(true)
+ {}
+
+ /** Returns page. */
+ UISettingsPage *page() const { return m_pPage; }
+ /** Returns warning pixmap. */
+ QPixmap warningPixmap() const;
+ /** Returns internal name. */
+ QString internalName() const;
+
+ /** Returns whether validator is valid. */
+ bool isValid() const { return m_fIsValid; }
+ /** Defines whether validator @a fIsValid. */
+ void setValid(bool fIsValid) { m_fIsValid = fIsValid; }
+
+ /** Returns last message. */
+ QString lastMessage() const { return m_strLastMessage; }
+ /** Defines @a strLastMessage. */
+ void setLastMessage(const QString &strLastMessage);
+
+public slots:
+
+ /** Performs revalidation. */
+ void revalidate();
+
+private:
+
+ /** Holds the validated page. */
+ UISettingsPage *m_pPage;
+
+ /** Holds whether the page is valid. */
+ bool m_fIsValid;
+
+ /** Holds the last message. */
+ QString m_strLastMessage;
+};
+
+
+/** QValidator extension,
+ * for long number validations. */
+class SHARED_LIBRARY_STUFF QIULongValidator : public QValidator
+{
+public:
+
+ /** Constructs long validator passing @a pParent to the base-class. */
+ QIULongValidator(QObject *pParent)
+ : QValidator(pParent)
+ , m_uBottom(0), m_uTop(ULONG_MAX)
+ {}
+
+ /** Constructs long validator passing @a pParent to the base-class.
+ * @param uMinimum Holds the minimum valid border.
+ * @param uMaximum Holds the maximum valid border. */
+ QIULongValidator(ulong uMinimum, ulong uMaximum,
+ QObject *pParent)
+ : QValidator(pParent)
+ , m_uBottom(uMinimum), m_uTop(uMaximum)
+ {}
+
+ /** Destructs long validator. */
+ virtual ~QIULongValidator() {}
+
+ /** Performs validation for @a strInput at @a iPosition. */
+ State validate(QString &strInput, int &iPosition) const;
+
+ /** Defines @a uBottom. */
+ void setBottom(ulong uBottom) { setRange(uBottom, m_uTop); }
+ /** Defines @a uTop. */
+ void setTop(ulong uTop) { setRange(m_uBottom, uTop); }
+ /** Defines range based on passed @a uBottom and @a uTop. */
+ void setRange(ulong uBottom, ulong uTop) { m_uBottom = uBottom; m_uTop = uTop; }
+ /** Returns bottom. */
+ ulong bottom() const { return m_uBottom; }
+ /** Returns top. */
+ ulong top() const { return m_uTop; }
+
+private:
+
+ /** Holds the bottom. */
+ ulong m_uBottom;
+ /** Holds the top. */
+ ulong m_uTop;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_QIWidgetValidator_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/graphics/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsView.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsView.cpp
new file mode 100644
index 00000000..73fa1e27
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsView.cpp
@@ -0,0 +1,125 @@
+/* $Id: QIGraphicsView.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIGraphicsView class implementation.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QScrollBar>
+#include <QTouchEvent>
+
+/* GUI includes: */
+#include "QIGraphicsView.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+QIGraphicsView::QIGraphicsView(QWidget *pParent /* = 0 */)
+ : QGraphicsView(pParent)
+ , m_iVerticalScrollBarPosition(0)
+{
+ /* Enable multi-touch support: */
+ setAttribute(Qt::WA_AcceptTouchEvents);
+ viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
+}
+
+bool QIGraphicsView::event(QEvent *pEvent)
+{
+ /* Handle known event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::TouchBegin:
+ {
+ /* Parse the touch event: */
+ QTouchEvent *pTouchEvent = static_cast<QTouchEvent*>(pEvent);
+ AssertPtrReturn(pTouchEvent, QGraphicsView::event(pEvent));
+ /* For touch-screen event we have something special: */
+#ifdef VBOX_IS_QT6_OR_LATER /* QTouchDevice was consumed by QInputDevice in 6.0 */
+ if (pTouchEvent->device()->type() == QInputDevice::DeviceType::TouchScreen)
+#else
+ if (pTouchEvent->device()->type() == QTouchDevice::TouchScreen)
+#endif
+ {
+ /* Remember where the scrolling was started: */
+ m_iVerticalScrollBarPosition = verticalScrollBar()->value();
+ /* Allow further touch events: */
+ pEvent->accept();
+ /* Mark event handled: */
+ return true;
+ }
+ break;
+ }
+ case QEvent::TouchUpdate:
+ {
+ /* Parse the touch-event: */
+ QTouchEvent *pTouchEvent = static_cast<QTouchEvent*>(pEvent);
+ AssertPtrReturn(pTouchEvent, QGraphicsView::event(pEvent));
+ /* For touch-screen event we have something special: */
+#ifdef VBOX_IS_QT6_OR_LATER /* QTouchDevice was consumed by QInputDevice in 6.0 */
+ if (pTouchEvent->device()->type() == QInputDevice::DeviceType::TouchScreen)
+#else
+ if (pTouchEvent->device()->type() == QTouchDevice::TouchScreen)
+#endif
+ {
+ /* Determine vertical shift (inverted): */
+ const QTouchEvent::TouchPoint point = pTouchEvent->touchPoints().first();
+ const int iShift = (int)(point.startPos().y() - point.pos().y());
+ /* Calculate new scroll-bar value according calculated shift: */
+ int iNewScrollBarValue = m_iVerticalScrollBarPosition + iShift;
+ /* Make sure new scroll-bar value is within the minimum/maximum bounds: */
+ iNewScrollBarValue = qMax(verticalScrollBar()->minimum(), iNewScrollBarValue);
+ iNewScrollBarValue = qMin(verticalScrollBar()->maximum(), iNewScrollBarValue);
+ /* Apply calculated scroll-bar shift finally: */
+ verticalScrollBar()->setValue(iNewScrollBarValue);
+ /* Mark event handled: */
+ return true;
+ }
+ break;
+ }
+ case QEvent::TouchEnd:
+ {
+ /* Parse the touch event: */
+ QTouchEvent *pTouchEvent = static_cast<QTouchEvent*>(pEvent);
+ AssertPtrReturn(pTouchEvent, QGraphicsView::event(pEvent));
+ /* For touch-screen event we have something special: */
+#ifdef VBOX_IS_QT6_OR_LATER /* QTouchDevice was consumed by QInputDevice in 6.0 */
+ if (pTouchEvent->device()->type() == QInputDevice::DeviceType::TouchScreen)
+#else
+ if (pTouchEvent->device()->type() == QTouchDevice::TouchScreen)
+#endif
+ {
+ /* Reset the scrolling start position: */
+ m_iVerticalScrollBarPosition = 0;
+ /* Mark event handled: */
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ /* Call to base-class: */
+ return QGraphicsView::event(pEvent);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsView.h b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsView.h
new file mode 100644
index 00000000..5ed529d1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsView.h
@@ -0,0 +1,65 @@
+/* $Id: QIGraphicsView.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIGraphicsView class declaration.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_graphics_QIGraphicsView_h
+#define FEQT_INCLUDED_SRC_extensions_graphics_QIGraphicsView_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QGraphicsView>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QEvent;
+class QWidget;
+
+/** QGraphicsView extension with advanced functionality. */
+class SHARED_LIBRARY_STUFF QIGraphicsView : public QGraphicsView
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs graphics-view passing @a pParent to the base-class. */
+ QIGraphicsView(QWidget *pParent = 0);
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Holds the vertical scroll-bar position. */
+ int m_iVerticalScrollBarPosition;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_graphics_QIGraphicsView_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsWidget.cpp b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsWidget.cpp
new file mode 100644
index 00000000..24947ac2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsWidget.cpp
@@ -0,0 +1,40 @@
+/* $Id: QIGraphicsWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIGraphicsWidget class definition.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "QIGraphicsWidget.h"
+
+
+QIGraphicsWidget::QIGraphicsWidget(QGraphicsWidget *pParent)
+ : QGraphicsWidget(pParent)
+{
+}
+
+QSizeF QIGraphicsWidget::minimumSizeHint() const
+{
+ return sizeHint(Qt::MinimumSize);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsWidget.h b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsWidget.h
new file mode 100644
index 00000000..2a2c1eca
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extensions/graphics/QIGraphicsWidget.h
@@ -0,0 +1,54 @@
+/* $Id: QIGraphicsWidget.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIGraphicsWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extensions_graphics_QIGraphicsWidget_h
+#define FEQT_INCLUDED_SRC_extensions_graphics_QIGraphicsWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QGraphicsWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QGraphicsWidget extension with advanced functionality. */
+class SHARED_LIBRARY_STUFF QIGraphicsWidget : public QGraphicsWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs graphics-widget passing @a pParent to the base-class. */
+ QIGraphicsWidget(QGraphicsWidget *pParent = 0);
+
+ /** Returns minimum size-hint. */
+ virtual QSizeF minimumSizeHint() const;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extensions_graphics_QIGraphicsWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extradata/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/extradata/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extradata/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataDefs.cpp b/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataDefs.cpp
new file mode 100644
index 00000000..0b673bcd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataDefs.cpp
@@ -0,0 +1,326 @@
+/* $Id: UIExtraDataDefs.cpp $ */
+/** @file
+ * VBox Qt GUI - Extra-data related definitions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+
+/* General: */
+const char *UIExtraDataDefs::GUI_RestrictedDialogs = "GUI/RestrictedDialogs";
+const char *UIExtraDataDefs::GUI_ColorTheme = "GUI/ColorTheme";
+
+
+/* Messaging: */
+const char *UIExtraDataDefs::GUI_SuppressMessages = "GUI/SuppressMessages";
+const char *UIExtraDataDefs::GUI_InvertMessageOption = "GUI/InvertMessageOption";
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+const char *UIExtraDataDefs::GUI_NotificationCenter_KeepSuccessfullProgresses = "GUI/NotificationCenter/KeepSuccessfullProgresses";
+#endif
+const char *UIExtraDataDefs::GUI_NotificationCenter_Alignment = "GUI/NotificationCenter/Alignment";
+const char *UIExtraDataDefs::GUI_NotificationCenter_Order = "GUI/NotificationCenter/Order";
+const char *UIExtraDataDefs::GUI_PreventBetaLabel = "GUI/PreventBetaLabel";
+#if !defined(VBOX_BLEEDING_EDGE) && !defined(DEBUG)
+const char *UIExtraDataDefs::GUI_PreventBetaWarning = "GUI/PreventBetaWarning";
+#endif
+
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+/* Application Update: */
+const char *UIExtraDataDefs::GUI_PreventApplicationUpdate = "GUI/PreventApplicationUpdate";
+const char *UIExtraDataDefs::GUI_UpdateDate = "GUI/UpdateDate";
+const char *UIExtraDataDefs::GUI_UpdateCheckCount = "GUI/UpdateCheckCount";
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+
+/* Progress: */
+const char *UIExtraDataDefs::GUI_Progress_LegacyMode = "GUI/Progress/LegacyMode";
+
+
+/* Settings: */
+const char *UIExtraDataDefs::GUI_Customizations = "GUI/Customizations";
+const char *UIExtraDataDefs::GUI_RestrictedGlobalSettingsPages = "GUI/RestrictedGlobalSettingsPages";
+const char *UIExtraDataDefs::GUI_RestrictedMachineSettingsPages = "GUI/RestrictedMachineSettingsPages";
+
+/* Settings: Language: */
+const char *UIExtraDataDefs::GUI_LanguageID = "GUI/LanguageID";
+
+/* Settings: Display: */
+const char *UIExtraDataDefs::GUI_MaxGuestResolution = "GUI/MaxGuestResolution";
+const char *UIExtraDataDefs::GUI_ActivateHoveredMachineWindow = "GUI/ActivateHoveredMachineWindow";
+const char *UIExtraDataDefs::GUI_DisableHostScreenSaver = "GUI/DisableHostScreenSaver";
+
+/* Settings: Keyboard: */
+const char *UIExtraDataDefs::GUI_Input_SelectorShortcuts = "GUI/Input/SelectorShortcuts";
+const char *UIExtraDataDefs::GUI_Input_MachineShortcuts = "GUI/Input/MachineShortcuts";
+const char *UIExtraDataDefs::GUI_Input_HostKeyCombination = "GUI/Input/HostKeyCombination";
+const char *UIExtraDataDefs::GUI_Input_AutoCapture = "GUI/Input/AutoCapture";
+const char *UIExtraDataDefs::GUI_RemapScancodes = "GUI/RemapScancodes";
+
+/* Settings: Proxy: */
+const char *UIExtraDataDefs::GUI_ProxySettings = "GUI/ProxySettings";
+
+/* Settings: Storage: */
+const char *UIExtraDataDefs::GUI_RecentFolderHD = "GUI/RecentFolderHD";
+const char *UIExtraDataDefs::GUI_RecentFolderCD = "GUI/RecentFolderCD";
+const char *UIExtraDataDefs::GUI_RecentFolderFD = "GUI/RecentFolderFD";
+const char *UIExtraDataDefs::GUI_RecentListHD = "GUI/RecentListHD";
+const char *UIExtraDataDefs::GUI_RecentListCD = "GUI/RecentListCD";
+const char *UIExtraDataDefs::GUI_RecentListFD = "GUI/RecentListFD";
+
+/* Settings: Network: */
+const char *UIExtraDataDefs::GUI_RestrictedNetworkAttachmentTypes = "GUI/RestrictedNetworkAttachmentTypes";
+
+/* VISO Creator: */
+const char *UIExtraDataDefs::GUI_VISOCreator_RecentFolder = "GUI/VISOCreator/RecentFolder";
+const char *UIExtraDataDefs::GUI_VISOCreator_DialogGeometry = "GUI/VISOCreator/DialogGeometry";
+
+/* VirtualBox Manager: */
+const char *UIExtraDataDefs::GUI_LastSelectorWindowPosition = "GUI/LastWindowPosition";
+const char *UIExtraDataDefs::GUI_SplitterSizes = "GUI/SplitterSizes";
+const char *UIExtraDataDefs::GUI_Toolbar = "GUI/Toolbar";
+const char *UIExtraDataDefs::GUI_Toolbar_Text = "GUI/Toolbar/Text";
+const char *UIExtraDataDefs::GUI_Toolbar_MachineTools_Order = "GUI/Toolbar/MachineTools/Order";
+const char *UIExtraDataDefs::GUI_Toolbar_GlobalTools_Order = "GUI/Toolbar/GlobalTools/Order";
+const char *UIExtraDataDefs::GUI_Tools_LastItemsSelected = "GUI/Tools/LastItemsSelected";
+const char *UIExtraDataDefs::GUI_Statusbar = "GUI/Statusbar";
+const char *UIExtraDataDefs::GUI_GroupDefinitions = "GUI/GroupDefinitions";
+const char *UIExtraDataDefs::GUI_LastItemSelected = "GUI/LastItemSelected";
+const char *UIExtraDataDefs::GUI_DetailsPageBoxes = "GUI/DetailsPageBoxes";
+const char *UIExtraDataDefs::GUI_PreviewUpdate = "GUI/PreviewUpdate";
+const char *UIExtraDataDefs::GUI_Details_Elements = "GUI/Details/Elements";
+const char *UIExtraDataDefs::GUI_Details_Elements_Preview_UpdateInterval = "GUI/Details/Elements/Preview/UpdateInterval";
+
+/* Snapshot Manager: */
+const char *UIExtraDataDefs::GUI_SnapshotManager_Details_Expanded = "GUI/SnapshotManager/Details/Expanded";
+
+/* Virtual Media Manager: */
+const char *UIExtraDataDefs::GUI_VirtualMediaManager_Details_Expanded = "GUI/VirtualMediaManager/Details/Expanded";
+const char *UIExtraDataDefs::GUI_VirtualMediaManager_Search_Widget_Expanded = "GUI/VirtualMediaManager/SearchWidget/Expanded";
+
+/* Host Network Manager: */
+const char *UIExtraDataDefs::GUI_HostNetworkManager_Details_Expanded = "GUI/HostNetworkManager/Details/Expanded";
+
+/* Cloud Profile Manager: */
+const char *UIExtraDataDefs::GUI_CloudProfileManager_Restrictions = "GUI/CloudProfileManager/Restrictions";
+const char *UIExtraDataDefs::GUI_CloudProfileManager_Details_Expanded = "GUI/CloudProfileManager/Details/Expanded";
+
+/* Cloud Console Manager: */
+const char *UIExtraDataDefs::GUI_CloudConsoleManager_Application = "GUI/CloudConsoleManager/Application";
+const char *UIExtraDataDefs::GUI_CloudConsoleManager_Restrictions = "GUI/CloudConsoleManager/Restrictions";
+const char *UIExtraDataDefs::GUI_CloudConsoleManager_Details_Expanded = "GUI/CloudConsoleManager/Details/Expanded";
+
+/* Cloud Console: */
+const char *UIExtraDataDefs::GUI_CloudConsole_PublicKey_Path = "GUI/CloudConsole/PublicKey/Path";
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+/* Extra-data Manager: */
+const char *UIExtraDataDefs::GUI_ExtraDataManager_Geometry = "GUI/ExtraDataManager/Geometry";
+const char *UIExtraDataDefs::GUI_ExtraDataManager_SplitterHints = "GUI/ExtraDataManager/SplitterHints";
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+/* Wizards: */
+const char *UIExtraDataDefs::GUI_HideDescriptionForWizards = "GUI/HideDescriptionForWizards";
+
+
+/* Virtual Machine: */
+const char *UIExtraDataDefs::GUI_HideFromManager = "GUI/HideFromManager";
+const char *UIExtraDataDefs::GUI_HideDetails = "GUI/HideDetails";
+const char *UIExtraDataDefs::GUI_PreventReconfiguration = "GUI/PreventReconfiguration";
+const char *UIExtraDataDefs::GUI_PreventSnapshotOperations = "GUI/PreventSnapshotOperations";
+const char *UIExtraDataDefs::GUI_MachineWindowIcons = "GUI/MachineWindowIcons";
+#ifndef VBOX_WS_MAC
+const char *UIExtraDataDefs::GUI_MachineWindowNamePostfix = "GUI/MachineWindowNamePostfix";
+#endif
+const char *UIExtraDataDefs::GUI_LastNormalWindowPosition = "GUI/LastNormalWindowPosition";
+const char *UIExtraDataDefs::GUI_LastScaleWindowPosition = "GUI/LastScaleWindowPosition";
+const char *UIExtraDataDefs::GUI_Geometry_State_Max = "max";
+#ifndef VBOX_WS_MAC
+const char *UIExtraDataDefs::GUI_MenuBar_Enabled = "GUI/MenuBar/Enabled";
+#endif
+const char *UIExtraDataDefs::GUI_MenuBar_ContextMenu_Enabled = "GUI/MenuBar/ContextMenu/Enabled";
+const char *UIExtraDataDefs::GUI_RestrictedRuntimeMenus = "GUI/RestrictedRuntimeMenus";
+const char *UIExtraDataDefs::GUI_RestrictedRuntimeApplicationMenuActions = "GUI/RestrictedRuntimeApplicationMenuActions";
+const char *UIExtraDataDefs::GUI_RestrictedRuntimeMachineMenuActions = "GUI/RestrictedRuntimeMachineMenuActions";
+const char *UIExtraDataDefs::GUI_RestrictedRuntimeViewMenuActions = "GUI/RestrictedRuntimeViewMenuActions";
+const char *UIExtraDataDefs::GUI_RestrictedRuntimeInputMenuActions = "GUI/RestrictedRuntimeInputMenuActions";
+const char *UIExtraDataDefs::GUI_RestrictedRuntimeDevicesMenuActions = "GUI/RestrictedRuntimeDevicesMenuActions";
+#ifdef VBOX_WITH_DEBUGGER_GUI
+const char *UIExtraDataDefs::GUI_RestrictedRuntimeDebuggerMenuActions = "GUI/RestrictedRuntimeDebuggerMenuActions";
+#endif
+#ifdef VBOX_WS_MAC
+const char *UIExtraDataDefs::GUI_RestrictedRuntimeWindowMenuActions = "GUI/RestrictedRuntimeWindowMenuActions";
+#endif
+const char *UIExtraDataDefs::GUI_RestrictedRuntimeHelpMenuActions = "GUI/RestrictedRuntimeHelpMenuActions";
+const char *UIExtraDataDefs::GUI_RestrictedVisualStates = "GUI/RestrictedVisualStates";
+const char *UIExtraDataDefs::GUI_Fullscreen = "GUI/Fullscreen";
+const char *UIExtraDataDefs::GUI_Seamless = "GUI/Seamless";
+const char *UIExtraDataDefs::GUI_Scale = "GUI/Scale";
+#ifdef VBOX_WS_X11
+const char *UIExtraDataDefs::GUI_Fullscreen_LegacyMode = "GUI/Fullscreen/LegacyMode";
+const char *UIExtraDataDefs::GUI_DistinguishMachineWindowGroups = "GUI/DistinguishMachineWindowGroups";
+#endif /* VBOX_WS_X11 */
+const char *UIExtraDataDefs::GUI_AutoresizeGuest = "GUI/AutoresizeGuest";
+const char *UIExtraDataDefs::GUI_LastVisibilityStatusForGuestScreen = "GUI/LastVisibilityStatusForGuestScreen";
+const char *UIExtraDataDefs::GUI_LastGuestSizeHint = "GUI/LastGuestSizeHint";
+const char *UIExtraDataDefs::GUI_VirtualScreenToHostScreen = "GUI/VirtualScreenToHostScreen";
+const char *UIExtraDataDefs::GUI_AutomountGuestScreens = "GUI/AutomountGuestScreens";
+#ifndef VBOX_WS_MAC
+const char *UIExtraDataDefs::GUI_ShowMiniToolBar = "GUI/ShowMiniToolBar";
+const char *UIExtraDataDefs::GUI_MiniToolBarAutoHide = "GUI/MiniToolBarAutoHide";
+const char *UIExtraDataDefs::GUI_MiniToolBarAlignment = "GUI/MiniToolBarAlignment";
+#endif /* !VBOX_WS_MAC */
+const char *UIExtraDataDefs::GUI_StatusBar_Enabled = "GUI/StatusBar/Enabled";
+const char *UIExtraDataDefs::GUI_StatusBar_ContextMenu_Enabled = "GUI/StatusBar/ContextMenu/Enabled";
+const char *UIExtraDataDefs::GUI_RestrictedStatusBarIndicators = "GUI/RestrictedStatusBarIndicators";
+const char *UIExtraDataDefs::GUI_StatusBar_IndicatorOrder = "GUI/StatusBar/IndicatorOrder";
+#ifdef VBOX_WS_MAC
+const char *UIExtraDataDefs::GUI_RealtimeDockIconUpdateEnabled = "GUI/RealtimeDockIconUpdateEnabled";
+const char *UIExtraDataDefs::GUI_RealtimeDockIconUpdateMonitor = "GUI/RealtimeDockIconUpdateMonitor";
+const char *UIExtraDataDefs::GUI_DockIconDisableOverlay = "GUI/DockIconDisableOverlay";
+#endif /* VBOX_WS_MAC */
+const char *UIExtraDataDefs::GUI_PassCAD = "GUI/PassCAD";
+const char *UIExtraDataDefs::GUI_MouseCapturePolicy = "GUI/MouseCapturePolicy";
+const char *UIExtraDataDefs::GUI_GuruMeditationHandler = "GUI/GuruMeditationHandler";
+const char *UIExtraDataDefs::GUI_HidLedsSync = "GUI/HidLedsSync";
+const char *UIExtraDataDefs::GUI_ScaleFactor = "GUI/ScaleFactor";
+const char *UIExtraDataDefs::GUI_Scaling_Optimization = "GUI/Scaling/Optimization";
+const char *UIExtraDataDefs::GUI_FontScaleFactor = "GUI/FontScaleFactor";
+
+/* Virtual Machine: Session Information Dialog: */
+const char *UIExtraDataDefs::GUI_SessionInformationDialogGeometry = "GUI/SessionInformationDialogGeometry";
+
+/* Guest control UI: */
+const char *UIExtraDataDefs::GUI_GuestControl_FileManagerDialogGeometry = "GUI/GuestControl/FileManagerDialogGeometry";
+const char *UIExtraDataDefs::GUI_GuestControl_FileManagerVisiblePanels = "GUI/GuestControl/FileManagerVisiblePanels";
+const char *UIExtraDataDefs::GUI_GuestControl_ProcessControlSplitterHints = "GUI/GuestControl/ProcessControlSplitterHints";
+const char *UIExtraDataDefs::GUI_GuestControl_ProcessControlDialogGeometry = "GUI/GuestControl/ProcessControlDialogGeometry";
+
+/* Soft Keyboard: */
+const char *UIExtraDataDefs::GUI_SoftKeyboard_DialogGeometry = "GUI/SoftKeyboardDialogGeometry";
+const char *UIExtraDataDefs::GUI_SoftKeyboard_ColorTheme = "GUI/SoftKeyboardColorTheme";
+const char *UIExtraDataDefs::GUI_SoftKeyboard_SelectedColorTheme = "GUI/SoftKeyboardSelectedColorTheme";
+const char *UIExtraDataDefs::GUI_SoftKeyboard_SelectedLayout = "GUI/SoftKeyboardSelectedLayout";
+const char *UIExtraDataDefs::GUI_SoftKeyboard_Options = "GUI/SoftKeyboardOptions";
+const char *UIExtraDataDefs::GUI_SoftKeyboard_HideNumPad = "GUI/SoftKeyboardHideNumPad";
+const char *UIExtraDataDefs::GUI_SoftKeyboard_HideOSMenuKeys = "GUI/SoftKeyboardHideOSMenuKeys";
+const char *UIExtraDataDefs::GUI_SoftKeyboard_HideMultimediaKeys = "GUI/SoftKeyboardHideMultimediaKeys";
+
+/* File Manager options: */
+const char *UIExtraDataDefs::GUI_GuestControl_FileManagerOptions = "GUI/GuestControl/FileManagerOptions";
+const char *UIExtraDataDefs::GUI_GuestControl_FileManagerListDirectoriesFirst = "ListDirectoriesFirst";
+const char *UIExtraDataDefs::GUI_GuestControl_FileManagerShowDeleteConfirmation = "ShowDeleteConfimation";
+const char *UIExtraDataDefs::GUI_GuestControl_FileManagerShowHumanReadableSizes = "ShowHumanReadableSizes";
+const char *UIExtraDataDefs::GUI_GuestControl_FileManagerShowHiddenObjects = "ShowHiddenObjects";
+
+/* Virtual Machine: Close dialog: */
+const char *UIExtraDataDefs::GUI_DefaultCloseAction = "GUI/DefaultCloseAction";
+const char *UIExtraDataDefs::GUI_RestrictedCloseActions = "GUI/RestrictedCloseActions";
+const char *UIExtraDataDefs::GUI_LastCloseAction = "GUI/LastCloseAction";
+const char *UIExtraDataDefs::GUI_CloseActionHook = "GUI/CloseActionHook";
+const char *UIExtraDataDefs::GUI_DiscardStateOnPowerOff = "GUI/DiscardStateOnPowerOff";
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+/* Virtual Machine: Debug UI: */
+const char *UIExtraDataDefs::GUI_Dbg_Enabled = "GUI/Dbg/Enabled";
+const char *UIExtraDataDefs::GUI_Dbg_AutoShow = "GUI/Dbg/AutoShow";
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+/* Virtual Machine: Log-viewer: */
+const char *UIExtraDataDefs::GUI_LogWindowGeometry = "GUI/LogWindowGeometry";
+const char *UIExtraDataDefs::GUI_LogViewerOptions = "GUI/LogViewerOptions";
+const char *UIExtraDataDefs::GUI_LogViewerWrapLinesEnabled = "WrapLines";
+const char *UIExtraDataDefs::GUI_LogViewerShowLineNumbersDisabled = "showLineNumbersDisabled";
+const char *UIExtraDataDefs::GUI_LogViewerNoFontStyleName = "noFontStyleName";
+const char *UIExtraDataDefs::GUI_GuestControl_LogViewerVisiblePanels = "GUI/LogViewerVisiblePanels";
+
+/* Help Browser */
+const char *UIExtraDataDefs::GUI_HelpBrowser_LastURLList = "GUI/HelpBrowserLastURLList";
+const char *UIExtraDataDefs::GUI_HelpBrowser_DialogGeometry = "GUI/HelpBrowserDialogGeomety";
+const char *UIExtraDataDefs::GUI_HelpBrowser_Bookmarks = "GUI/HelpBrowserBookmarks";
+const char *UIExtraDataDefs::GUI_HelpBrowser_ZoomPercentage = "GUI/HelpBrowserZoomPercentage";
+
+/* VM Activity Overview: */
+const char *UIExtraDataDefs::GUI_VMActivityOverview_HiddenColumns = "GUI/VMActivityOverviewHiddenColumns";
+const char *UIExtraDataDefs::GUI_VMActivityOverview_ShowAllMachines = "GUI/VMActivityOverviewShowAllMachines";
+
+const char *UIExtraDataDefs::GUI_MediumSelector_DialogGeometry = "GUI/MediumSelector/DialogGeometry";
+
+const int UIExtraDataDefs::iFontScaleMin = 20;
+const int UIExtraDataDefs::iFontScaleMax = 200;
+
+/* Obsolete keys: */
+QMultiMap<QString, QString> UIExtraDataDefs::prepareObsoleteKeysMap()
+{
+ QMultiMap<QString, QString> map;
+ map.insert(GUI_Details_Elements, GUI_DetailsPageBoxes);
+ map.insert(GUI_Details_Elements_Preview_UpdateInterval, GUI_PreviewUpdate);
+ return map;
+}
+QMultiMap<QString, QString> UIExtraDataDefs::g_mapOfObsoleteKeys = UIExtraDataDefs::prepareObsoleteKeysMap();
+
+
+bool UIToolStuff::isTypeOfClass(UIToolType enmType, UIToolClass enmClass)
+{
+ switch (enmClass)
+ {
+ case UIToolClass_Global:
+ {
+ switch (enmType)
+ {
+ case UIToolType_Welcome:
+ case UIToolType_Extensions:
+ case UIToolType_Media:
+ case UIToolType_Network:
+ case UIToolType_Cloud:
+ case UIToolType_CloudConsole:
+ case UIToolType_VMActivityOverview:
+ return true;
+ default:
+ break;
+ }
+ break;
+ }
+ case UIToolClass_Machine:
+ {
+ switch (enmType)
+ {
+ case UIToolType_Details:
+ case UIToolType_Snapshots:
+ case UIToolType_Logs:
+ case UIToolType_VMActivity:
+ case UIToolType_FileManager:
+ return true;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataDefs.h b/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataDefs.h
new file mode 100644
index 00000000..077468b0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataDefs.h
@@ -0,0 +1,1186 @@
+/* $Id: UIExtraDataDefs.h $ */
+/** @file
+ * VBox Qt GUI - Extra-data related definitions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extradata_UIExtraDataDefs_h
+#define FEQT_INCLUDED_SRC_extradata_UIExtraDataDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QMetaType>
+#include <QObject>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+
+/** Typedef for QPair of QStrings. */
+typedef QPair<QString, QString> QIStringPair;
+typedef QList<QIStringPair> QIStringPairList;
+
+
+/** Extra-data namespace. */
+namespace UIExtraDataDefs
+{
+ /** @name General
+ * @{ */
+ /** Holds restricted dialogs. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedDialogs;
+
+ /** Holds the color theme type. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_ColorTheme;
+ /** @} */
+
+ /** @name Messaging
+ * @{ */
+ /** Holds the list of supressed messages for the Message/Popup center frameworks. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_SuppressMessages;
+ /** Holds the list of messages for the Message/Popup center frameworks with inverted check-box state. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_InvertMessageOption;
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+ /** Holds whether successfull notification-progresses should NOT close automatically. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_NotificationCenter_KeepSuccessfullProgresses;
+#endif
+ /** Holds notification-center alignment. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_NotificationCenter_Alignment;
+ /** Holds notification-center order. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_NotificationCenter_Order;
+ /** Holds whether BETA build label should be hidden. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_PreventBetaLabel;
+#if !defined(VBOX_BLEEDING_EDGE) && !defined(DEBUG)
+ /** Holds version for which user wants to prevent BETA build warning. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_PreventBetaWarning;
+#endif
+ /** @} */
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /** @name Application Update
+ * @{ */
+ /** Holds whether Application Update functionality enabled. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_PreventApplicationUpdate;
+ /** Holds Application Update data. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_UpdateDate;
+ /** Holds Application Update check counter. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_UpdateCheckCount;
+ /** @} */
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+ /** @name Progress
+ * @{ */
+ /** Holds whether legacy progress handling method is requested. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Progress_LegacyMode;
+ /** @} */
+
+ /** @name Settings
+ * @{ */
+ /** Holds GUI feature list. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Customizations;
+ /** Holds restricted Global Settings pages. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedGlobalSettingsPages;
+ /** Holds restricted Machine Settings pages. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedMachineSettingsPages;
+ /** @} */
+
+ /** @name Settings: Language
+ * @{ */
+ /** Holds GUI language ID. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LanguageID;
+ /** @} */
+
+ /** @name Settings: Display
+ * @{ */
+ /** Holds maximum guest-screen resolution policy/value. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_MaxGuestResolution;
+ /** Holds whether hovered machine-window should be activated. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_ActivateHoveredMachineWindow;
+ /** Holds whether the host scrrn saver is disabled when a vm is running. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_DisableHostScreenSaver;
+ /** @} */
+
+ /** @name Settings: Keyboard
+ * @{ */
+ /** Holds Selector UI shortcut overrides. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Input_SelectorShortcuts;
+ /** Holds Runtime UI shortcut overrides. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Input_MachineShortcuts;
+ /** Holds Runtime UI host-key combination. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Input_HostKeyCombination;
+ /** Holds whether Runtime UI auto-capture is enabled. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Input_AutoCapture;
+ /** Holds Runtime UI remapped scan codes. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RemapScancodes;
+ /** @} */
+
+ /** @name Settings: Proxy
+ * @{ */
+ /** Holds VBox proxy settings. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_ProxySettings;
+ /** @} */
+
+ /** @name Settings: Storage
+ * @{ */
+ /** Holds recent folder for hard-drives. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RecentFolderHD;
+ /** Holds recent folder for optical-disks. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RecentFolderCD;
+ /** Holds recent folder for floppy-disks. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RecentFolderFD;
+ /** Holds the list of recently used hard-drives. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RecentListHD;
+ /** Holds the list of recently used optical-disks. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RecentListCD;
+ /** Holds the list of recently used floppy-disks. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RecentListFD;
+ /** @} */
+
+ /** @name Settings: Network
+ * @{ */
+ /** Holds the list of restricted network attachment types. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedNetworkAttachmentTypes;
+ /** @} */
+
+ /** @name VISO Creator
+ * @{ */
+ /** Holds recent folder for VISO creation content. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_VISOCreator_RecentFolder;
+ SHARED_LIBRARY_STUFF extern const char *GUI_VISOCreator_DialogGeometry;
+ /** @} */
+
+ /** @name VirtualBox Manager
+ * @{ */
+ /** Holds selector-window geometry. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LastSelectorWindowPosition;
+ /** Holds selector-window splitter hints. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_SplitterSizes;
+ /** Holds whether selector-window tool-bar visible. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Toolbar;
+ /** Holds whether selector-window tool-bar text visible. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Toolbar_Text;
+ /** Holds the selector-window machine tools order. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Toolbar_MachineTools_Order;
+ /** Holds the selector-window global tools order. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Toolbar_GlobalTools_Order;
+ /** Holds the last selected tool set of VirtualBox Manager. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Tools_LastItemsSelected;
+ /** Holds whether selector-window status-bar visible. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Statusbar;
+ /** Prefix used by composite extra-data keys,
+ * which holds selector-window chooser-pane' groups definitions. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_GroupDefinitions;
+ /** Holds last item chosen in selector-window chooser-pane. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LastItemSelected;
+ /** Holds selector-window details-pane' elements. */
+ /// @todo remove GUI_DetailsPageBoxes in 6.2
+ SHARED_LIBRARY_STUFF extern const char *GUI_DetailsPageBoxes;
+ /// @todo remove GUI_PreviewUpdate in 6.2
+ /** Holds selector-window details-pane' preview update interval. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_PreviewUpdate;
+ /** Holds VirtualBox Manager Details-pane elements. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Details_Elements;
+ /** Holds VirtualBox Manager Details-pane / Preview element update interval. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Details_Elements_Preview_UpdateInterval;
+ /** @} */
+
+ /** @name Snapshot Manager
+ * @{ */
+ /** Holds whether Snapshot Manager details expanded. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_SnapshotManager_Details_Expanded;
+ /** @} */
+
+ /** @name Virtual Media Manager
+ * @{ */
+ /** Holds whether Virtual Media Manager details expanded. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_VirtualMediaManager_Details_Expanded;
+ /** Holds whether Virtual Media Manager search widget expanded. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_VirtualMediaManager_Search_Widget_Expanded;
+ /** @} */
+
+ /** @name Host Network Manager
+ * @{ */
+ /** Holds whether Host Network Manager details expanded. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_HostNetworkManager_Details_Expanded;
+ /** @} */
+
+ /** @name Cloud Profile Manager
+ * @{ */
+ /** Holds Cloud Profile Manager restrictions. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_CloudProfileManager_Restrictions;
+ /** Holds whether Cloud Profile Manager details expanded. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_CloudProfileManager_Details_Expanded;
+ /** @} */
+
+ /** @name Cloud Console Manager
+ * @{ */
+ /** Holds Cloud Console Manager applications/profiles. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_CloudConsoleManager_Application;
+ /** Holds Cloud Console Manager restrictions. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_CloudConsoleManager_Restrictions;
+ /** Holds whether Cloud Console Manager details expanded. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_CloudConsoleManager_Details_Expanded;
+ /** @} */
+
+ /** @name Cloud Console
+ * @{ */
+ /** Holds Cloud Console public key path. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_CloudConsole_PublicKey_Path;
+ /** @} */
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ /** @name Extra-data Manager
+ * @{ */
+ /** Holds extra-data manager geometry. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_ExtraDataManager_Geometry;
+ /** Holds extra-data manager splitter hints. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_ExtraDataManager_SplitterHints;
+ /** @} */
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+ /** @name Wizards
+ * @{ */
+ /** Holds wizard types for which descriptions should be hidden. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_HideDescriptionForWizards;
+ /** @} */
+
+ /** @name Virtual Machine
+ * @{ */
+ /** Holds whether machine shouldn't be shown in selector-window chooser-pane. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_HideFromManager;
+ /** Holds whether machine shouldn't be shown in selector-window details-pane. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_HideDetails;
+ /** Holds whether machine reconfiguration disabled. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_PreventReconfiguration;
+ /** Holds whether machine snapshot operations disabled. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_PreventSnapshotOperations;
+ /** Except Mac OS X: Holds redefined machine-window icon names. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_MachineWindowIcons;
+#ifndef VBOX_WS_MAC
+ /** Except Mac OS X: Holds redefined machine-window name postfix. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_MachineWindowNamePostfix;
+#endif
+ /** Prefix used by composite extra-data keys,
+ * which holds normal machine-window geometry per screen-index. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LastNormalWindowPosition;
+ /** Prefix used by composite extra-data keys,
+ * which holds scaled machine-window geometry per screen-index. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LastScaleWindowPosition;
+ /** Holds machine-window geometry maximized state flag. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Geometry_State_Max;
+#ifndef VBOX_WS_MAC
+ /** Holds Runtime UI menu-bar availability status. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_MenuBar_Enabled;
+#endif
+ /** Holds Runtime UI menu-bar context-menu availability status. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_MenuBar_ContextMenu_Enabled;
+ /** Holds restricted Runtime UI menu types. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedRuntimeMenus;
+ /** Holds restricted Runtime UI action types for 'Application' menu. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedRuntimeApplicationMenuActions;
+ /** Holds restricted Runtime UI action types for Machine menu. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedRuntimeMachineMenuActions;
+ /** Holds restricted Runtime UI action types for View menu. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedRuntimeViewMenuActions;
+ /** Holds restricted Runtime UI action types for Input menu. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedRuntimeInputMenuActions;
+ /** Holds restricted Runtime UI action types for Devices menu. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedRuntimeDevicesMenuActions;
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Holds restricted Runtime UI action types for Debugger menu. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedRuntimeDebuggerMenuActions;
+#endif
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Holds restricted Runtime UI action types for 'Window' menu. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedRuntimeWindowMenuActions;
+#endif
+ /** Holds restricted Runtime UI action types for Help menu. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedRuntimeHelpMenuActions;
+ /** Holds restricted Runtime UI visual-states. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedVisualStates;
+ /** Holds whether full screen visual-state is requested. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Fullscreen;
+ /** Holds whether seamless visual-state is requested. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Seamless;
+ /** Holds whether scaled visual-state is requested. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Scale;
+#ifdef VBOX_WS_X11
+ /** Holds whether legacy full-screen mode is requested. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Fullscreen_LegacyMode;
+ /** Holds whether internal machine-window names should be unique. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_DistinguishMachineWindowGroups;
+#endif /* VBOX_WS_X11 */
+ /** Holds whether guest-screen auto-resize according machine-window size is enabled. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_AutoresizeGuest;
+ /** Prefix used by composite extra-data keys,
+ * which holds last guest-screen visibility status per screen-index. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LastVisibilityStatusForGuestScreen;
+ /** Prefix used by composite extra-data keys,
+ * which holds last guest-screen size-hint per screen-index. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LastGuestSizeHint;
+ /** Prefix used by composite extra-data keys,
+ * which holds host-screen index per guest-screen index. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_VirtualScreenToHostScreen;
+ /** Holds whether automatic mounting/unmounting of guest-screens enabled. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_AutomountGuestScreens;
+#ifndef VBOX_WS_MAC
+ /** Holds whether mini-toolbar is enabled for full and seamless screens. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_ShowMiniToolBar;
+ /** Holds whether mini-toolbar should auto-hide itself. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_MiniToolBarAutoHide;
+ /** Holds mini-toolbar alignment. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_MiniToolBarAlignment;
+#endif /* !VBOX_WS_MAC */
+ /** Holds Runtime UI status-bar availability status. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_StatusBar_Enabled;
+ /** Holds Runtime UI status-bar context-menu availability status. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_StatusBar_ContextMenu_Enabled;
+ /** Holds restricted Runtime UI status-bar indicators. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedStatusBarIndicators;
+ /** Holds Runtime UI status-bar indicator order. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_StatusBar_IndicatorOrder;
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Holds whether Dock icon should be updated at runtime. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RealtimeDockIconUpdateEnabled;
+ /** Mac OS X: Holds guest-screen which Dock icon should reflect at runtime. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RealtimeDockIconUpdateMonitor;
+ /** Mac OS X: Holds whether Dock icon should have overlay disabled. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_DockIconDisableOverlay;
+#endif /* VBOX_WS_MAC */
+ /** Holds whether machine should pass CAD to guest. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_PassCAD;
+ /** Holds the mouse capture policy. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_MouseCapturePolicy;
+ /** Holds redefined guru-meditation handler type. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_GuruMeditationHandler;
+ /** Holds whether machine should perform HID LEDs synchronization. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_HidLedsSync;
+ /** Holds the scale-factor. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_ScaleFactor;
+ /** Holds the scaling optimization type. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Scaling_Optimization;
+ /** Holds the font scale factor. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_FontScaleFactor;
+ /** @} */
+
+ /** @name Virtual Machine: Information dialog
+ * @{ */
+ /** Holds information-window geometry. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_SessionInformationDialogGeometry;
+ /** @} */
+
+ /** @name Guest Control UI related data
+ * @{ */
+ extern const char *GUI_GuestControl_FileManagerDialogGeometry;
+ extern const char *GUI_GuestControl_FileManagerVisiblePanels;
+ extern const char *GUI_GuestControl_ProcessControlSplitterHints;
+ extern const char *GUI_GuestControl_ProcessControlDialogGeometry;
+ /** @} */
+
+ /** @name Soft Keyboard related data
+ * @{ */
+ extern const char *GUI_SoftKeyboard_DialogGeometry;
+ extern const char *GUI_SoftKeyboard_ColorTheme;
+ extern const char *GUI_SoftKeyboard_SelectedColorTheme;
+ extern const char *GUI_SoftKeyboard_SelectedLayout;
+ extern const char *GUI_SoftKeyboard_Options;
+ extern const char *GUI_SoftKeyboard_HideNumPad;
+ extern const char *GUI_SoftKeyboard_HideOSMenuKeys;
+ extern const char *GUI_SoftKeyboard_HideMultimediaKeys;
+ /** @} */
+
+ /** @name File Manager options
+ * @{ */
+ extern const char *GUI_GuestControl_FileManagerOptions;
+ extern const char *GUI_GuestControl_FileManagerListDirectoriesFirst;
+ extern const char *GUI_GuestControl_FileManagerShowDeleteConfirmation;
+ extern const char *GUI_GuestControl_FileManagerShowHumanReadableSizes;
+ extern const char *GUI_GuestControl_FileManagerShowHiddenObjects;
+ /** @} */
+
+ /** @name Virtual Machine: Close dialog
+ * @{ */
+ /** Holds default machine close action. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_DefaultCloseAction;
+ /** Holds restricted machine close actions. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_RestrictedCloseActions;
+ /** Holds last machine close action. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LastCloseAction;
+ /** Holds machine close hook script name as simple string. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_CloseActionHook;
+ /** Holds whether machine should discard state on power off. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_DiscardStateOnPowerOff;
+ /** @} */
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** @name Virtual Machine: Debug UI
+ * @{ */
+ /** Holds whether debugger UI enabled. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Dbg_Enabled;
+ /** Holds whether debugger UI should be auto-shown. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_Dbg_AutoShow;
+ /** @} */
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+ /** @name Virtual Machine: Log-viewer
+ * @{ */
+ /** Holds log-viewer geometry. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LogWindowGeometry;
+ /** @} */
+ /** @name Virtual Machine: Log-viewer widget options
+ * @{ */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LogViewerOptions;
+ /** Holds log-viewer wrap line flag. */
+ SHARED_LIBRARY_STUFF extern const char *GUI_LogViewerWrapLinesEnabled;
+ SHARED_LIBRARY_STUFF extern const char *GUI_LogViewerShowLineNumbersDisabled;
+ SHARED_LIBRARY_STUFF extern const char *GUI_LogViewerNoFontStyleName;
+ SHARED_LIBRARY_STUFF extern const char *GUI_GuestControl_LogViewerVisiblePanels;
+ /** @} */
+
+ /** @name Help Browser
+ * @{ */
+ SHARED_LIBRARY_STUFF extern const char *GUI_HelpBrowser_LastURLList;
+ SHARED_LIBRARY_STUFF extern const char *GUI_HelpBrowser_DialogGeometry;
+ SHARED_LIBRARY_STUFF extern const char *GUI_HelpBrowser_Bookmarks;
+ SHARED_LIBRARY_STUFF extern const char *GUI_HelpBrowser_ZoomPercentage;
+ /** @} */
+
+ /** @name Manager UI: VM Activity Overview Related stuff
+ * @{ */
+ SHARED_LIBRARY_STUFF extern const char *GUI_VMActivityOverview_HiddenColumns;
+ SHARED_LIBRARY_STUFF extern const char *GUI_VMActivityOverview_ShowAllMachines;
+ /** @} */
+
+ /** @name Medium Selector stuff
+ * @{ */
+ SHARED_LIBRARY_STUFF extern const char *GUI_MediumSelector_DialogGeometry;
+ /** @} */
+
+ /** @name Old key support stuff.
+ * @{ */
+ /** Prepares obsolete keys map. */
+ SHARED_LIBRARY_STUFF QMultiMap<QString, QString> prepareObsoleteKeysMap();
+
+ /** Holds the obsolete keys map. */
+ SHARED_LIBRARY_STUFF extern QMultiMap<QString, QString> g_mapOfObsoleteKeys;
+ /** @} */
+
+ /** @name Font scaling factor min-max.
+ * @{ */
+ extern const int iFontScaleMin;
+ extern const int iFontScaleMax;
+ /** @} */
+}
+
+
+/** Extra-data meta definitions. */
+class SHARED_LIBRARY_STUFF UIExtraDataMetaDefs : public QObject
+{
+ Q_OBJECT;
+
+ /* Menu related stuff: */
+ Q_ENUMS(MenuType);
+ Q_ENUMS(MenuApplicationActionType);
+ Q_ENUMS(MenuHelpActionType);
+ Q_ENUMS(RuntimeMenuMachineActionType);
+ Q_ENUMS(RuntimeMenuViewActionType);
+ Q_ENUMS(RuntimeMenuInputActionType);
+ Q_ENUMS(RuntimeMenuDevicesActionType);
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ Q_ENUMS(RuntimeMenuDebuggerActionType);
+#endif
+#ifdef VBOX_WS_MAC
+ Q_ENUMS(MenuWindowActionType);
+#endif
+
+public:
+
+ /** Common UI: Dialog types. */
+ enum DialogType
+ {
+ DialogType_Invalid = 0,
+ DialogType_VISOCreator = RT_BIT(0),
+ DialogType_BootFailure = RT_BIT(1),
+ DialogType_All = 0xFFFF
+ };
+ Q_ENUM(DialogType);
+
+ /** Common UI: Menu types. */
+ enum MenuType
+ {
+ MenuType_Invalid = 0,
+ MenuType_Application = RT_BIT(0),
+ MenuType_Machine = RT_BIT(1),
+ MenuType_View = RT_BIT(2),
+ MenuType_Input = RT_BIT(3),
+ MenuType_Devices = RT_BIT(4),
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ MenuType_Debug = RT_BIT(5),
+#endif
+#ifdef VBOX_WS_MAC
+ MenuType_Window = RT_BIT(6),
+#endif
+ MenuType_Help = RT_BIT(7),
+ MenuType_All = 0xFF
+ };
+
+ /** Menu "Application": Action types. */
+ enum MenuApplicationActionType
+ {
+ MenuApplicationActionType_Invalid = 0,
+#ifdef VBOX_WS_MAC
+ MenuApplicationActionType_About = RT_BIT(0),
+#endif
+ MenuApplicationActionType_Preferences = RT_BIT(1),
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ MenuApplicationActionType_NetworkAccessManager = RT_BIT(2),
+ MenuApplicationActionType_CheckForUpdates = RT_BIT(3),
+#endif
+ MenuApplicationActionType_ResetWarnings = RT_BIT(4),
+ MenuApplicationActionType_Close = RT_BIT(5),
+ MenuApplicationActionType_All = 0xFFFF
+ };
+
+ /** Menu "Help": Action types. */
+ enum MenuHelpActionType
+ {
+ MenuHelpActionType_Invalid = 0,
+ MenuHelpActionType_Contents = RT_BIT(0),
+ MenuHelpActionType_WebSite = RT_BIT(1),
+ MenuHelpActionType_BugTracker = RT_BIT(2),
+ MenuHelpActionType_Forums = RT_BIT(3),
+ MenuHelpActionType_Oracle = RT_BIT(4),
+ MenuHelpActionType_OnlineDocumentation = RT_BIT(5),
+#ifndef VBOX_WS_MAC
+ MenuHelpActionType_About = RT_BIT(6),
+#endif
+ MenuHelpActionType_All = 0xFFFF
+ };
+
+ /** Runtime UI: Menu "Machine": Action types. */
+ enum RuntimeMenuMachineActionType
+ {
+ RuntimeMenuMachineActionType_Invalid = 0,
+ RuntimeMenuMachineActionType_SettingsDialog = RT_BIT(0),
+ RuntimeMenuMachineActionType_TakeSnapshot = RT_BIT(1),
+ RuntimeMenuMachineActionType_InformationDialog = RT_BIT(2),
+ RuntimeMenuMachineActionType_FileManagerDialog = RT_BIT(3),
+ RuntimeMenuMachineActionType_GuestProcessControlDialog = RT_BIT(4),
+ RuntimeMenuMachineActionType_Pause = RT_BIT(5),
+ RuntimeMenuMachineActionType_Reset = RT_BIT(6),
+ RuntimeMenuMachineActionType_Detach = RT_BIT(7),
+ RuntimeMenuMachineActionType_SaveState = RT_BIT(8),
+ RuntimeMenuMachineActionType_Shutdown = RT_BIT(9),
+ RuntimeMenuMachineActionType_PowerOff = RT_BIT(10),
+ RuntimeMenuMachineActionType_LogDialog = RT_BIT(11),
+ RuntimeMenuMachineActionType_Nothing = RT_BIT(12),
+ RuntimeMenuMachineActionType_All = 0xFFFF
+ };
+
+ /** Runtime UI: Menu "View": Action types. */
+ enum RuntimeMenuViewActionType
+ {
+ RuntimeMenuViewActionType_Invalid = 0,
+ RuntimeMenuViewActionType_Fullscreen = RT_BIT(0),
+ RuntimeMenuViewActionType_Seamless = RT_BIT(1),
+ RuntimeMenuViewActionType_Scale = RT_BIT(2),
+#ifndef VBOX_WS_MAC
+ RuntimeMenuViewActionType_MinimizeWindow = RT_BIT(3),
+#endif
+ RuntimeMenuViewActionType_AdjustWindow = RT_BIT(4),
+ RuntimeMenuViewActionType_GuestAutoresize = RT_BIT(5),
+ RuntimeMenuViewActionType_TakeScreenshot = RT_BIT(6),
+ RuntimeMenuViewActionType_Recording = RT_BIT(7),
+ RuntimeMenuViewActionType_RecordingSettings = RT_BIT(8),
+ RuntimeMenuViewActionType_StartRecording = RT_BIT(9),
+ RuntimeMenuViewActionType_VRDEServer = RT_BIT(10),
+ RuntimeMenuViewActionType_MenuBar = RT_BIT(11),
+ RuntimeMenuViewActionType_MenuBarSettings = RT_BIT(12),
+#ifndef VBOX_WS_MAC
+ RuntimeMenuViewActionType_ToggleMenuBar = RT_BIT(13),
+#endif
+ RuntimeMenuViewActionType_StatusBar = RT_BIT(14),
+ RuntimeMenuViewActionType_StatusBarSettings = RT_BIT(15),
+ RuntimeMenuViewActionType_ToggleStatusBar = RT_BIT(16),
+ RuntimeMenuViewActionType_Resize = RT_BIT(17),
+ RuntimeMenuViewActionType_Remap = RT_BIT(18),
+ RuntimeMenuViewActionType_Rescale = RT_BIT(19),
+ RuntimeMenuViewActionType_All = 0xFFFF
+ };
+
+ /** Runtime UI: Menu "Input": Action types. */
+ enum RuntimeMenuInputActionType
+ {
+ RuntimeMenuInputActionType_Invalid = 0,
+ RuntimeMenuInputActionType_Keyboard = RT_BIT(0),
+ RuntimeMenuInputActionType_KeyboardSettings = RT_BIT(1),
+ RuntimeMenuInputActionType_SoftKeyboard = RT_BIT(2),
+ RuntimeMenuInputActionType_TypeCAD = RT_BIT(3),
+#ifdef VBOX_WS_X11
+ RuntimeMenuInputActionType_TypeCABS = RT_BIT(4),
+#endif
+ RuntimeMenuInputActionType_TypeCtrlBreak = RT_BIT(5),
+ RuntimeMenuInputActionType_TypeInsert = RT_BIT(6),
+ RuntimeMenuInputActionType_TypePrintScreen = RT_BIT(7),
+ RuntimeMenuInputActionType_TypeAltPrintScreen = RT_BIT(8),
+ RuntimeMenuInputActionType_Mouse = RT_BIT(9),
+ RuntimeMenuInputActionType_MouseIntegration = RT_BIT(10),
+ RuntimeMenuInputActionType_TypeHostKeyCombo = RT_BIT(11),
+ RuntimeMenuInputActionType_All = 0xFFFF
+ };
+
+ /** Runtime UI: Menu "Devices": Action types. */
+ enum RuntimeMenuDevicesActionType
+ {
+ RuntimeMenuDevicesActionType_Invalid = 0,
+ RuntimeMenuDevicesActionType_HardDrives = RT_BIT(0),
+ RuntimeMenuDevicesActionType_HardDrivesSettings = RT_BIT(1),
+ RuntimeMenuDevicesActionType_OpticalDevices = RT_BIT(2),
+ RuntimeMenuDevicesActionType_FloppyDevices = RT_BIT(3),
+ RuntimeMenuDevicesActionType_Audio = RT_BIT(4),
+ RuntimeMenuDevicesActionType_AudioOutput = RT_BIT(5),
+ RuntimeMenuDevicesActionType_AudioInput = RT_BIT(6),
+ RuntimeMenuDevicesActionType_Network = RT_BIT(7),
+ RuntimeMenuDevicesActionType_NetworkSettings = RT_BIT(8),
+ RuntimeMenuDevicesActionType_USBDevices = RT_BIT(9),
+ RuntimeMenuDevicesActionType_USBDevicesSettings = RT_BIT(10),
+ RuntimeMenuDevicesActionType_WebCams = RT_BIT(11),
+ RuntimeMenuDevicesActionType_SharedClipboard = RT_BIT(12),
+ RuntimeMenuDevicesActionType_DragAndDrop = RT_BIT(13),
+ RuntimeMenuDevicesActionType_SharedFolders = RT_BIT(14),
+ RuntimeMenuDevicesActionType_SharedFoldersSettings = RT_BIT(15),
+ RuntimeMenuDevicesActionType_InsertGuestAdditionsDisk = RT_BIT(16),
+ RuntimeMenuDevicesActionType_UpgradeGuestAdditions = RT_BIT(17),
+ RuntimeMenuDevicesActionType_Nothing = RT_BIT(18),
+ RuntimeMenuDevicesActionType_All = 0xFFFF
+ };
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Runtime UI: Menu "Debugger": Action types. */
+ enum RuntimeMenuDebuggerActionType
+ {
+ RuntimeMenuDebuggerActionType_Invalid = 0,
+ RuntimeMenuDebuggerActionType_Statistics = RT_BIT(0),
+ RuntimeMenuDebuggerActionType_CommandLine = RT_BIT(1),
+ RuntimeMenuDebuggerActionType_Logging = RT_BIT(2),
+ RuntimeMenuDebuggerActionType_GuestControlConsole = RT_BIT(3),
+ RuntimeMenuDebuggerActionType_All = 0xFFFF
+ };
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+ /** Menu "Window": Action types. */
+ enum MenuWindowActionType
+ {
+ MenuWindowActionType_Invalid = 0,
+ MenuWindowActionType_Minimize = RT_BIT(0),
+ MenuWindowActionType_Switch = RT_BIT(1),
+ MenuWindowActionType_All = 0xFFFF
+ };
+#endif /* VBOX_WS_MAC */
+
+
+ /** VirtualBox Manager UI: Details element: "General" option types. */
+ enum DetailsElementOptionTypeGeneral
+ {
+ DetailsElementOptionTypeGeneral_Invalid = 0,
+ DetailsElementOptionTypeGeneral_Name = RT_BIT(0),
+ DetailsElementOptionTypeGeneral_OS = RT_BIT(1),
+ DetailsElementOptionTypeGeneral_Location = RT_BIT(2),
+ DetailsElementOptionTypeGeneral_Groups = RT_BIT(3),
+ DetailsElementOptionTypeGeneral_Default = 0xFFFB
+ };
+ Q_ENUM(DetailsElementOptionTypeGeneral);
+
+ /** VirtualBox Manager UI: Details element: "System" option types. */
+ enum DetailsElementOptionTypeSystem
+ {
+ DetailsElementOptionTypeSystem_Invalid = 0,
+ DetailsElementOptionTypeSystem_RAM = RT_BIT(0),
+ DetailsElementOptionTypeSystem_CPUCount = RT_BIT(1),
+ DetailsElementOptionTypeSystem_CPUExecutionCap = RT_BIT(2),
+ DetailsElementOptionTypeSystem_BootOrder = RT_BIT(3),
+ DetailsElementOptionTypeSystem_ChipsetType = RT_BIT(4),
+ DetailsElementOptionTypeSystem_TpmType = RT_BIT(5),
+ DetailsElementOptionTypeSystem_Firmware = RT_BIT(6),
+ DetailsElementOptionTypeSystem_SecureBoot = RT_BIT(7),
+ DetailsElementOptionTypeSystem_Acceleration = RT_BIT(8),
+ DetailsElementOptionTypeSystem_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeSystem);
+
+ /** VirtualBox Manager UI: Details element: "Display" option types. */
+ enum DetailsElementOptionTypeDisplay
+ {
+ DetailsElementOptionTypeDisplay_Invalid = 0,
+ DetailsElementOptionTypeDisplay_VRAM = RT_BIT(0),
+ DetailsElementOptionTypeDisplay_ScreenCount = RT_BIT(1),
+ DetailsElementOptionTypeDisplay_ScaleFactor = RT_BIT(2),
+ DetailsElementOptionTypeDisplay_GraphicsController = RT_BIT(3),
+ DetailsElementOptionTypeDisplay_Acceleration = RT_BIT(4),
+ DetailsElementOptionTypeDisplay_VRDE = RT_BIT(5),
+ DetailsElementOptionTypeDisplay_Recording = RT_BIT(6),
+ DetailsElementOptionTypeDisplay_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeDisplay);
+
+ /** VirtualBox Manager UI: Details element: "Storage" option types. */
+ enum DetailsElementOptionTypeStorage
+ {
+ DetailsElementOptionTypeStorage_Invalid = 0,
+ DetailsElementOptionTypeStorage_HardDisks = RT_BIT(0),
+ DetailsElementOptionTypeStorage_OpticalDevices = RT_BIT(1),
+ DetailsElementOptionTypeStorage_FloppyDevices = RT_BIT(2),
+ DetailsElementOptionTypeStorage_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeStorage);
+
+ /** VirtualBox Manager UI: Details element: "Audio" option types. */
+ enum DetailsElementOptionTypeAudio
+ {
+ DetailsElementOptionTypeAudio_Invalid = 0,
+ DetailsElementOptionTypeAudio_Driver = RT_BIT(0),
+ DetailsElementOptionTypeAudio_Controller = RT_BIT(1),
+ DetailsElementOptionTypeAudio_IO = RT_BIT(2),
+ DetailsElementOptionTypeAudio_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeAudio);
+
+ /** VirtualBox Manager UI: Details element: "Network" option types. */
+ enum DetailsElementOptionTypeNetwork
+ {
+ DetailsElementOptionTypeNetwork_Invalid = 0,
+ DetailsElementOptionTypeNetwork_NotAttached = RT_BIT(0),
+ DetailsElementOptionTypeNetwork_NAT = RT_BIT(1),
+ DetailsElementOptionTypeNetwork_BridgedAdapter = RT_BIT(2),
+ DetailsElementOptionTypeNetwork_InternalNetwork = RT_BIT(3),
+ DetailsElementOptionTypeNetwork_HostOnlyAdapter = RT_BIT(4),
+ DetailsElementOptionTypeNetwork_GenericDriver = RT_BIT(5),
+ DetailsElementOptionTypeNetwork_NATNetwork = RT_BIT(6),
+#ifdef VBOX_WITH_CLOUD_NET
+ DetailsElementOptionTypeNetwork_CloudNetwork = RT_BIT(7),
+#endif /* VBOX_WITH_CLOUD_NET */
+#ifdef VBOX_WITH_VMNET
+ DetailsElementOptionTypeNetwork_HostOnlyNetwork = RT_BIT(8),
+#endif /* VBOX_WITH_VMNET */
+ DetailsElementOptionTypeNetwork_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeNetwork);
+
+ /** VirtualBox Manager UI: Details element: "Serial" option types. */
+ enum DetailsElementOptionTypeSerial
+ {
+ DetailsElementOptionTypeSerial_Invalid = 0,
+ DetailsElementOptionTypeSerial_Disconnected = RT_BIT(0),
+ DetailsElementOptionTypeSerial_HostPipe = RT_BIT(1),
+ DetailsElementOptionTypeSerial_HostDevice = RT_BIT(2),
+ DetailsElementOptionTypeSerial_RawFile = RT_BIT(3),
+ DetailsElementOptionTypeSerial_TCP = RT_BIT(4),
+ DetailsElementOptionTypeSerial_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeSerial);
+
+ /** VirtualBox Manager UI: Details element: "USB" option types. */
+ enum DetailsElementOptionTypeUsb
+ {
+ DetailsElementOptionTypeUsb_Invalid = 0,
+ DetailsElementOptionTypeUsb_Controller = RT_BIT(0),
+ DetailsElementOptionTypeUsb_DeviceFilters = RT_BIT(1),
+ DetailsElementOptionTypeUsb_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeUsb);
+
+ /** VirtualBox Manager UI: Details element: "SharedFolders" option types. */
+ enum DetailsElementOptionTypeSharedFolders
+ {
+ DetailsElementOptionTypeSharedFolders_Invalid = 0,
+ DetailsElementOptionTypeSharedFolders_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeSharedFolders);
+
+ /** VirtualBox Manager UI: Details element: "UserInterface" option types. */
+ enum DetailsElementOptionTypeUserInterface
+ {
+ DetailsElementOptionTypeUserInterface_Invalid = 0,
+ DetailsElementOptionTypeUserInterface_VisualState = RT_BIT(0),
+ DetailsElementOptionTypeUserInterface_MenuBar = RT_BIT(1),
+ DetailsElementOptionTypeUserInterface_StatusBar = RT_BIT(2),
+ DetailsElementOptionTypeUserInterface_MiniToolbar = RT_BIT(3),
+ DetailsElementOptionTypeUserInterface_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeUserInterface);
+
+ /** VirtualBox Manager UI: Details element: "Description" option types. */
+ enum DetailsElementOptionTypeDescription
+ {
+ DetailsElementOptionTypeDescription_Invalid = 0,
+ DetailsElementOptionTypeDescription_Default = 0xFFFF
+ };
+ Q_ENUM(DetailsElementOptionTypeDescription);
+};
+
+
+/** Common UI: GUI feature types. */
+enum GUIFeatureType
+{
+ GUIFeatureType_None = 0,
+ GUIFeatureType_NoSelector = RT_BIT(0),
+#ifdef VBOX_WS_MAC
+ GUIFeatureType_NoUserElements = RT_BIT(1),
+#else
+ GUIFeatureType_NoMenuBar = RT_BIT(1),
+#endif
+ GUIFeatureType_NoStatusBar = RT_BIT(2),
+ GUIFeatureType_All = 0xFF
+};
+
+
+/** Common UI: Global settings page types. */
+enum GlobalSettingsPageType
+{
+ GlobalSettingsPageType_Invalid,
+ GlobalSettingsPageType_General,
+ GlobalSettingsPageType_Input,
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ GlobalSettingsPageType_Update,
+#endif
+ GlobalSettingsPageType_Language,
+ GlobalSettingsPageType_Display,
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ GlobalSettingsPageType_Proxy,
+#endif
+ GlobalSettingsPageType_Interface,
+ GlobalSettingsPageType_Max
+};
+Q_DECLARE_METATYPE(GlobalSettingsPageType);
+
+
+/** Common UI: Machine settings page types. */
+enum MachineSettingsPageType
+{
+ MachineSettingsPageType_Invalid,
+ MachineSettingsPageType_General,
+ MachineSettingsPageType_System,
+ MachineSettingsPageType_Display,
+ MachineSettingsPageType_Storage,
+ MachineSettingsPageType_Audio,
+ MachineSettingsPageType_Network,
+ MachineSettingsPageType_Ports,
+ MachineSettingsPageType_Serial,
+ MachineSettingsPageType_USB,
+ MachineSettingsPageType_SF,
+ MachineSettingsPageType_Interface,
+ MachineSettingsPageType_Max
+};
+Q_DECLARE_METATYPE(MachineSettingsPageType);
+
+
+/** Common UI: Shared Folder types. */
+enum UISharedFolderType
+{
+ UISharedFolderType_Machine,
+ UISharedFolderType_Console
+};
+
+
+/** Remote mode types. */
+enum UIRemoteMode
+{
+ UIRemoteMode_Any,
+ UIRemoteMode_On,
+ UIRemoteMode_Off
+};
+Q_DECLARE_METATYPE(UIRemoteMode);
+
+
+/** Common UI: Wizard types. */
+enum WizardType
+{
+ WizardType_Invalid,
+ WizardType_NewVM,
+ WizardType_CloneVM,
+ WizardType_ExportAppliance,
+ WizardType_ImportAppliance,
+ WizardType_NewCloudVM,
+ WizardType_AddCloudVM,
+ WizardType_NewVD,
+ WizardType_CloneVD
+};
+
+
+/** Common UI: Wizard modes. */
+enum WizardMode
+{
+ WizardMode_Auto,
+ WizardMode_Basic,
+ WizardMode_Expert
+};
+
+
+/** Common UI: Color Theme types. */
+enum UIColorThemeType
+{
+ UIColorThemeType_Auto,
+ UIColorThemeType_Light,
+ UIColorThemeType_Dark,
+};
+Q_DECLARE_METATYPE(UIColorThemeType);
+
+
+/** Tool item classes. */
+enum UIToolClass
+{
+ UIToolClass_Invalid,
+ UIToolClass_Global,
+ UIToolClass_Machine
+};
+
+
+/** Tool item types. */
+enum UIToolType
+{
+ UIToolType_Invalid,
+ /* Global types: */
+ UIToolType_Welcome,
+ UIToolType_Extensions,
+ UIToolType_Media,
+ UIToolType_Network,
+ UIToolType_Cloud,
+ UIToolType_CloudConsole,
+ UIToolType_VMActivityOverview,
+ /* Machine types: */
+ UIToolType_Error,
+ UIToolType_Details,
+ UIToolType_Snapshots,
+ UIToolType_Logs,
+ UIToolType_VMActivity,
+ UIToolType_FileManager
+};
+Q_DECLARE_METATYPE(UIToolType);
+
+
+/** Contains stuff related to tools handling. */
+namespace UIToolStuff
+{
+ /** Returns whether passed @a enmType is of passed @a enmClass. */
+ SHARED_LIBRARY_STUFF bool isTypeOfClass(UIToolType enmType, UIToolClass enmClass);
+}
+
+
+/** Selector UI: Details-element types. */
+enum DetailsElementType
+{
+ DetailsElementType_Invalid,
+ DetailsElementType_General,
+ DetailsElementType_System,
+ DetailsElementType_Preview,
+ DetailsElementType_Display,
+ DetailsElementType_Storage,
+ DetailsElementType_Audio,
+ DetailsElementType_Network,
+ DetailsElementType_Serial,
+ DetailsElementType_USB,
+ DetailsElementType_SF,
+ DetailsElementType_UI,
+ DetailsElementType_Description,
+ DetailsElementType_Max
+};
+Q_DECLARE_METATYPE(DetailsElementType);
+
+
+/** Selector UI: Preview update interval types. */
+enum PreviewUpdateIntervalType
+{
+ PreviewUpdateIntervalType_Disabled,
+ PreviewUpdateIntervalType_500ms,
+ PreviewUpdateIntervalType_1000ms,
+ PreviewUpdateIntervalType_2000ms,
+ PreviewUpdateIntervalType_5000ms,
+ PreviewUpdateIntervalType_10000ms,
+ PreviewUpdateIntervalType_Max
+};
+
+
+/** Selector UI: Disk encryption cipher types. */
+enum UIDiskEncryptionCipherType
+{
+ UIDiskEncryptionCipherType_Unchanged,
+ UIDiskEncryptionCipherType_XTS256,
+ UIDiskEncryptionCipherType_XTS128,
+ UIDiskEncryptionCipherType_Max
+};
+Q_DECLARE_METATYPE(UIDiskEncryptionCipherType);
+
+
+/** Runtime UI: Visual-state types. */
+enum UIVisualStateType
+{
+ UIVisualStateType_Invalid = 0,
+ UIVisualStateType_Normal = RT_BIT(0),
+ UIVisualStateType_Fullscreen = RT_BIT(1),
+ UIVisualStateType_Seamless = RT_BIT(2),
+ UIVisualStateType_Scale = RT_BIT(3),
+ UIVisualStateType_All = 0xFF
+};
+Q_DECLARE_METATYPE(UIVisualStateType);
+
+
+/** Runtime UI: Indicator types. */
+enum IndicatorType
+{
+ IndicatorType_Invalid,
+ IndicatorType_HardDisks,
+ IndicatorType_OpticalDisks,
+ IndicatorType_FloppyDisks,
+ IndicatorType_Audio,
+ IndicatorType_Network,
+ IndicatorType_USB,
+ IndicatorType_SharedFolders,
+ IndicatorType_Display,
+ IndicatorType_Recording,
+ IndicatorType_Features,
+ IndicatorType_Mouse,
+ IndicatorType_Keyboard,
+ IndicatorType_KeyboardExtension,
+ IndicatorType_Max
+};
+Q_DECLARE_METATYPE(IndicatorType);
+
+
+/** Runtime UI: Machine close actions. */
+enum MachineCloseAction
+{
+ MachineCloseAction_Invalid = 0,
+ MachineCloseAction_Detach = RT_BIT(0),
+ MachineCloseAction_SaveState = RT_BIT(1),
+ MachineCloseAction_Shutdown = RT_BIT(2),
+ MachineCloseAction_PowerOff = RT_BIT(3),
+ MachineCloseAction_PowerOff_RestoringSnapshot = RT_BIT(4),
+ MachineCloseAction_All = 0xFF
+};
+Q_DECLARE_METATYPE(MachineCloseAction);
+
+
+/** Runtime UI: Mouse capture policy types. */
+enum MouseCapturePolicy
+{
+ MouseCapturePolicy_Default,
+ MouseCapturePolicy_HostComboOnly,
+ MouseCapturePolicy_Disabled
+};
+
+
+/** Runtime UI: Guru Meditation handler types. */
+enum GuruMeditationHandlerType
+{
+ GuruMeditationHandlerType_Default,
+ GuruMeditationHandlerType_PowerOff,
+ GuruMeditationHandlerType_Ignore
+};
+
+
+/** Runtime UI: Scaling optimization types. */
+enum ScalingOptimizationType
+{
+ ScalingOptimizationType_None,
+ ScalingOptimizationType_Performance
+};
+
+
+#ifndef VBOX_WS_MAC
+/** Runtime UI: Mini-toolbar alignment. */
+enum MiniToolbarAlignment
+{
+ MiniToolbarAlignment_Disabled,
+ MiniToolbarAlignment_Bottom,
+ MiniToolbarAlignment_Top
+};
+#endif /* !VBOX_WS_MAC */
+
+
+/** Runtime UI: Information-element types. */
+enum InformationElementType
+{
+ InformationElementType_Invalid,
+ InformationElementType_General,
+ InformationElementType_System,
+ InformationElementType_Preview,
+ InformationElementType_Display,
+ InformationElementType_Storage,
+ InformationElementType_Audio,
+ InformationElementType_Network,
+ InformationElementType_Serial,
+ InformationElementType_USB,
+ InformationElementType_SharedFolders,
+ InformationElementType_UI,
+ InformationElementType_Description,
+ InformationElementType_RuntimeAttributes,
+ InformationElementType_StorageStatistics,
+ InformationElementType_NetworkStatistics
+};
+Q_DECLARE_METATYPE(InformationElementType);
+
+
+/** Runtime UI: Maximum guest-screen size policy types.
+ * @note This policy determines which guest-screen sizes we wish to
+ * handle. We also accept anything smaller than the current size. */
+enum MaximumGuestScreenSizePolicy
+{
+ /** Anything at all. */
+ MaximumGuestScreenSizePolicy_Any,
+ /** Anything up to a fixed size. */
+ MaximumGuestScreenSizePolicy_Fixed,
+ /** Anything up to host-screen available space. */
+ MaximumGuestScreenSizePolicy_Automatic
+};
+Q_DECLARE_METATYPE(MaximumGuestScreenSizePolicy);
+
+
+/** Manager UI: VM Activity Overview Column types.
+ * @note The first element must be 0 and the rest must be consecutive */
+enum VMActivityOverviewColumn
+{
+ VMActivityOverviewColumn_Name = 0,
+ VMActivityOverviewColumn_CPUGuestLoad,
+ VMActivityOverviewColumn_CPUVMMLoad,
+ VMActivityOverviewColumn_RAMUsedAndTotal,
+ VMActivityOverviewColumn_RAMUsedPercentage,
+ VMActivityOverviewColumn_NetworkUpRate,
+ VMActivityOverviewColumn_NetworkDownRate,
+ VMActivityOverviewColumn_NetworkUpTotal,
+ VMActivityOverviewColumn_NetworkDownTotal,
+ VMActivityOverviewColumn_DiskIOReadRate,
+ VMActivityOverviewColumn_DiskIOWriteRate,
+ VMActivityOverviewColumn_DiskIOReadTotal,
+ VMActivityOverviewColumn_DiskIOWriteTotal,
+ VMActivityOverviewColumn_VMExits,
+ VMActivityOverviewColumn_Max
+};
+
+#endif /* !FEQT_INCLUDED_SRC_extradata_UIExtraDataDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataManager.cpp b/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataManager.cpp
new file mode 100644
index 00000000..7a902e83
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataManager.cpp
@@ -0,0 +1,5155 @@
+/* $Id: UIExtraDataManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIExtraDataManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QFontDatabase>
+#include <QMetaEnum>
+#include <QMutex>
+#include <QRegExp>
+#include <QRegularExpression>
+#include <QRegularExpressionValidator>
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+# include <QComboBox>
+# include <QHeaderView>
+# include <QLabel>
+# include <QLineEdit>
+# include <QListView>
+# include <QMainWindow>
+# include <QMenuBar>
+# include <QPainter>
+# include <QPushButton>
+# include <QSortFilterProxyModel>
+# include <QStyledItemDelegate>
+# include <QTableView>
+# include <QVBoxLayout>
+# include <QStandardItemModel>
+# include <QXmlStreamWriter>
+# include <QXmlStreamReader>
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIActionPool.h"
+#include "UIConverter.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIHostComboEditor.h"
+#include "UIMainEventListener.h"
+#include "UIMessageCenter.h"
+#include "UISettingsDefs.h"
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+# include "QIDialog.h"
+# include "QIDialogButtonBox.h"
+# include "QIFileDialog.h"
+# include "QISplitter.h"
+# include "QIWidgetValidator.h"
+# include "QIWithRestorableGeometry.h"
+# include "VBoxUtils.h"
+# include "UIIconPool.h"
+# include "QIToolBar.h"
+# include "UIVirtualBoxEventHandler.h"
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CEventListener.h"
+#include "CEventSource.h"
+#include "CMachine.h"
+#include "CVirtualBox.h"
+
+
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+using namespace UISettingsDefs;
+
+
+/** Private QObject extension
+ * providing UIExtraDataManager with the CVirtualBox event-source. */
+class UIExtraDataEventHandler : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about 'extra-data change' event: */
+ void sigExtraDataChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue);
+
+public:
+
+ /** Constructs event proxy object on the basis of passed @a pParent. */
+ UIExtraDataEventHandler(QObject *pParent);
+ /** Destructs event proxy object. */
+ ~UIExtraDataEventHandler();
+
+protected slots:
+
+ /** Preprocess 'extra-data can change' event: */
+ void sltPreprocessExtraDataCanChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue, bool &fVeto, QString &strVetoReason);
+ /** Preprocess 'extra-data change' event: */
+ void sltPreprocessExtraDataChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue);
+
+protected:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares listener. */
+ void prepareListener();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups listener. */
+ void cleanupListener();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+private:
+
+ /** Holds the Qt event listener instance. */
+ ComObjPtr<UIMainEventListenerImpl> m_pQtListener;
+ /** Holds the COM event listener instance. */
+ CEventListener m_comEventListener;
+
+ /** Protects sltPreprocessExtraDataChange. */
+ QMutex m_mutex;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIExtraDataEventHandler implementation. *
+*********************************************************************************************************************************/
+
+UIExtraDataEventHandler::UIExtraDataEventHandler(QObject *pParent)
+ : QObject(pParent)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIExtraDataEventHandler::~UIExtraDataEventHandler()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIExtraDataEventHandler::prepare()
+{
+ /* Prepare: */
+ prepareListener();
+ prepareConnections();
+}
+
+void UIExtraDataEventHandler::prepareListener()
+{
+ /* Create event listener instance: */
+ m_pQtListener.createObject();
+ m_pQtListener->init(new UIMainEventListener, this);
+ m_comEventListener = CEventListener(m_pQtListener);
+
+ /* Get VirtualBox: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+ AssertWrapperOk(comVBox);
+ /* Get VirtualBox event source: */
+ CEventSource comEventSourceVBox = comVBox.GetEventSource();
+ AssertWrapperOk(comEventSourceVBox);
+
+ /* Enumerate all the required event-types: */
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes
+ << KVBoxEventType_OnExtraDataCanChange
+ << KVBoxEventType_OnExtraDataChanged;
+
+ /* Register event listener for VirtualBox event source: */
+ comEventSourceVBox.RegisterListener(m_comEventListener, eventTypes, FALSE /* active? */);
+ AssertWrapperOk(comEventSourceVBox);
+
+ /* Register event sources in their listeners as well: */
+ m_pQtListener->getWrapped()->registerSource(comEventSourceVBox, m_comEventListener);
+}
+
+void UIExtraDataEventHandler::prepareConnections()
+{
+ /* Create direct (sync) connections for signals of main listener: */
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigExtraDataCanChange,
+ this, &UIExtraDataEventHandler::sltPreprocessExtraDataCanChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigExtraDataChange,
+ this, &UIExtraDataEventHandler::sltPreprocessExtraDataChange,
+ Qt::DirectConnection);
+}
+
+void UIExtraDataEventHandler::cleanupConnections()
+{
+ /* Nothing for now. */
+}
+
+void UIExtraDataEventHandler::cleanupListener()
+{
+ /* Unregister everything: */
+ m_pQtListener->getWrapped()->unregisterSources();
+
+ /* Make sure VBoxSVC is available: */
+ if (!uiCommon().isVBoxSVCAvailable())
+ return;
+
+ /* Get VirtualBox: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+ AssertWrapperOk(comVBox);
+ /* Get VirtualBox event source: */
+ CEventSource comEventSourceVBox = comVBox.GetEventSource();
+ AssertWrapperOk(comEventSourceVBox);
+
+ /* Unregister event listener for VirtualBox event source: */
+ comEventSourceVBox.UnregisterListener(m_comEventListener);
+}
+
+void UIExtraDataEventHandler::cleanup()
+{
+ /* Cleanup: */
+ cleanupConnections();
+ cleanupListener();
+}
+
+void UIExtraDataEventHandler::sltPreprocessExtraDataCanChange(const QUuid &uMachineID, const QString &strKey, const QString &/* strValue */, bool & /* fVeto */, QString & /* strVetoReason */)
+{
+ /* Preprocess global 'extra-data can change' event: */
+ if (uMachineID.isNull())
+ {
+ if (strKey.startsWith("GUI/"))
+ {
+ /* Check whether global extra-data property can be applied: */
+ /// @todo Here can be various extra-data flags handling.
+ // Generally we should check whether one or another flag feats some rule (like reg-exp).
+ // For each required strValue we should set fVeto = true; and fill strVetoReason = "with some text".
+ }
+ }
+}
+
+void UIExtraDataEventHandler::sltPreprocessExtraDataChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue)
+{
+ /* Preprocess global 'extra-data change' event: */
+ if (uMachineID.isNull())
+ {
+ if (strKey.startsWith("GUI/"))
+ {
+ /* Apply global extra-data property: */
+ /// @todo Here can be various extra-data flags handling.
+ // Generally we should push one or another flag to various instances which want to handle
+ // those flags independently from UIExtraDataManager. Remember to process each required strValue
+ // from under the m_mutex lock (since we are in another thread) and unlock that m_mutex afterwards.
+ }
+ }
+
+ /* Motify listener about 'extra-data change' event: */
+ emit sigExtraDataChange(uMachineID, strKey, strValue);
+}
+
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+
+/** Data fields. */
+enum Field
+{
+ Field_ID = Qt::UserRole + 1,
+ Field_Name,
+ Field_OsTypeID,
+ Field_Known
+};
+
+
+/** QStyledItemDelegate extension
+ * reflecting items of Extra Data Manager window: Chooser pane. */
+class UIChooserPaneDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor. */
+ UIChooserPaneDelegate(QObject *pParent);
+
+private:
+
+ /** Size-hint calculation routine. */
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+ /** Paint routine. */
+ void paint(QPainter *pPainter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+ /** Fetch pixmap info for passed QModelIndex. */
+ static void fetchPixmapInfo(const QModelIndex &index, QPixmap &pixmap, QSize &pixmapSize);
+
+ /** Margin. */
+ int m_iMargin;
+ /** Spacing. */
+ int m_iSpacing;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIChooserPaneDelegate implementation. *
+*********************************************************************************************************************************/
+
+UIChooserPaneDelegate::UIChooserPaneDelegate(QObject *pParent)
+ : QStyledItemDelegate(pParent)
+ , m_iMargin(3)
+ , m_iSpacing(3)
+{
+}
+
+QSize UIChooserPaneDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ /* Font metrics: */
+ const QFontMetrics &fm = option.fontMetrics;
+ /* Pixmap: */
+ QPixmap pixmap;
+ QSize pixmapSize;
+ fetchPixmapInfo(index, pixmap, pixmapSize);
+
+ /* Calculate width: */
+ const int iWidth = m_iMargin +
+ pixmapSize.width() +
+ 2 * m_iSpacing +
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ qMax(fm.horizontalAdvance(index.data(Field_Name).toString()),
+ fm.horizontalAdvance(index.data(Field_ID).toString())) +
+#else
+ qMax(fm.width(index.data(Field_Name).toString()),
+ fm.width(index.data(Field_ID).toString())) +
+#endif
+ m_iMargin;
+ /* Calculate height: */
+ const int iHeight = m_iMargin +
+ qMax(pixmapSize.height(),
+ fm.height() + m_iSpacing + fm.height()) +
+ m_iMargin;
+
+ /* Return result: */
+ return QSize(iWidth, iHeight);
+}
+
+void UIChooserPaneDelegate::paint(QPainter *pPainter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ /* Item rect: */
+ const QRect &optionRect = option.rect;
+ /* Palette: */
+ const QPalette &palette = option.palette;
+ /* Font metrics: */
+ const QFontMetrics &fm = option.fontMetrics;
+ /* Pixmap: */
+ QPixmap pixmap;
+ QSize pixmapSize;
+ fetchPixmapInfo(index, pixmap, pixmapSize);
+
+ /* If item selected: */
+ if (option.state & QStyle::State_Selected)
+ {
+ /* Fill background with selection color: */
+ QColor highlight = palette.color(option.state & QStyle::State_Active ?
+ QPalette::Active : QPalette::Inactive,
+ QPalette::Highlight);
+ QLinearGradient bgGrad(optionRect.topLeft(), optionRect.bottomLeft());
+ bgGrad.setColorAt(0, highlight.lighter(120));
+ bgGrad.setColorAt(1, highlight);
+ pPainter->fillRect(optionRect, bgGrad);
+ /* Draw focus frame: */
+ QStyleOptionFocusRect focusOption;
+ focusOption.rect = optionRect;
+ QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOption, pPainter);
+ }
+
+ /* Draw pixmap: */
+ const QPoint pixmapOrigin = optionRect.topLeft() +
+ QPoint(m_iMargin, m_iMargin);
+ pPainter->drawPixmap(pixmapOrigin, pixmap);
+
+ /* Is that known item? */
+ bool fKnown = index.data(Field_Known).toBool();
+ if (fKnown)
+ {
+ pPainter->save();
+ QFont font = pPainter->font();
+ font.setBold(true);
+ pPainter->setFont(font);
+ }
+
+ /* Draw item name: */
+ const QPoint nameOrigin = pixmapOrigin +
+ QPoint(pixmapSize.width(), 0) +
+ QPoint(2 * m_iSpacing, 0) +
+ QPoint(0, fm.ascent());
+ pPainter->drawText(nameOrigin, index.data(Field_Name).toString());
+
+ /* Was that known item? */
+ if (fKnown)
+ pPainter->restore();
+
+ /* Draw item ID: */
+ const QPoint idOrigin = nameOrigin +
+ QPoint(0, m_iSpacing) +
+ QPoint(0, fm.height());
+ pPainter->drawText(idOrigin, index.data(Field_ID).toString());
+}
+
+/* static */
+void UIChooserPaneDelegate::fetchPixmapInfo(const QModelIndex &index, QPixmap &pixmap, QSize &pixmapSize)
+{
+ /* If proper machine ID passed => return corresponding pixmap/size: */
+ if (index.data(Field_ID).toUuid() != UIExtraDataManager::GlobalID)
+ pixmap = generalIconPool().guestOSTypePixmapDefault(index.data(Field_OsTypeID).toString(), &pixmapSize);
+ else
+ {
+ /* For global ID we return static pixmap/size: */
+ const QIcon icon = UIIconPool::iconSet(":/edata_global_32px.png");
+ pixmapSize = icon.availableSizes().value(0, QSize(32, 32));
+ pixmap = icon.pixmap(pixmapSize);
+ }
+}
+
+
+/** QSortFilterProxyModel extension
+ * used by the chooser-pane of the UIExtraDataManagerWindow. */
+class UIChooserPaneSortingModel : public QSortFilterProxyModel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pParent to the QIRichToolButton constructor. */
+ UIChooserPaneSortingModel(QObject *pParent) : QSortFilterProxyModel(pParent) {}
+
+protected:
+
+ /** Returns true if the value of the item referred to by the given index left
+ * is less than the value of the item referred to by the given index right,
+ * otherwise returns false. */
+ bool lessThan(const QModelIndex &leftIdx, const QModelIndex &rightIdx) const
+ {
+ /* Compare by ID first: */
+ const QUuid strID1 = leftIdx.data(Field_ID).toUuid();
+ const QUuid strID2 = rightIdx.data(Field_ID).toUuid();
+ if (strID1 == UIExtraDataManager::GlobalID)
+ return true;
+ else if (strID2 == UIExtraDataManager::GlobalID)
+ return false;
+ /* Compare role finally: */
+ return QSortFilterProxyModel::lessThan(leftIdx, rightIdx);
+ }
+};
+
+
+/** QMainWindow extension
+ * providing Extra Data Manager with UI features. */
+class UIExtraDataManagerWindow : public QIWithRestorableGeometry<QMainWindow>
+{
+ Q_OBJECT;
+
+public:
+
+ /** @name Constructor/Destructor
+ * @{ */
+ /** Extra-data Manager Window constructor. */
+ UIExtraDataManagerWindow(QWidget *pCenterWidget);
+ /** Extra-data Manager Window destructor. */
+ ~UIExtraDataManagerWindow();
+ /** @} */
+
+ /** @name Management
+ * @{ */
+ /** Show and raise. */
+ void showAndRaise(QWidget *pCenterWidget);
+ /** @} */
+
+public slots:
+
+ /** @name General
+ * @{ */
+ /** Handles extra-data map acknowledging. */
+ void sltExtraDataMapAcknowledging(const QUuid &uID);
+ /** Handles extra-data change. */
+ void sltExtraDataChange(const QUuid &uID, const QString &strKey, const QString &strValue);
+ /** @} */
+
+private slots:
+
+ /** @name General
+ * @{ */
+ /** Handles machine (un)registration. */
+ void sltMachineRegistered(const QUuid &uID, bool fAdded);
+ /** @} */
+
+ /** @name Chooser-pane
+ * @{ */
+ /** Handles filter-apply signal for the chooser-pane. */
+ void sltChooserApplyFilter(const QString &strFilter);
+ /** Handles current-changed signal for the chooser-pane: */
+ void sltChooserHandleCurrentChanged(const QModelIndex &index);
+ /** Handles item-selection-changed signal for the chooser-pane: */
+ void sltChooserHandleSelectionChanged(const QItemSelection &selected,
+ const QItemSelection &deselected);
+ /** @} */
+
+ /** @name Data-pane
+ * @{ */
+ /** Handles filter-apply signal for the data-pane. */
+ void sltDataApplyFilter(const QString &strFilter);
+ /** Handles item-selection-changed signal for the data-pane: */
+ void sltDataHandleSelectionChanged(const QItemSelection &selected,
+ const QItemSelection &deselected);
+ /** Handles item-changed signal for the data-pane: */
+ void sltDataHandleItemChanged(QStandardItem *pItem);
+ /** Handles context-menu-requested signal for the data-pane: */
+ void sltDataHandleCustomContextMenuRequested(const QPoint &pos);
+ /** @} */
+
+ /** @name Actions
+ * @{ */
+ /** Add handler. */
+ void sltAdd();
+ /** Remove handler. */
+ void sltDel();
+ /** Save handler. */
+ void sltSave();
+ /** Load handler. */
+ void sltLoad();
+ /** @} */
+
+private:
+
+ /** @name General
+ * @{ */
+ /** Returns whether the window should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/Cleanup
+ * @{ */
+ /** Prepare instance. */
+ void prepare();
+ /** Prepare this. */
+ void prepareThis();
+ /** Prepare connections. */
+ void prepareConnections();
+ /** Prepare menu. */
+ void prepareMenu();
+ /** Prepare central widget. */
+ void prepareCentralWidget();
+ /** Prepare tool-bar. */
+ void prepareToolBar();
+ /** Prepare splitter. */
+ void prepareSplitter();
+ /** Prepare panes: */
+ void preparePanes();
+ /** Prepare chooser pane. */
+ void preparePaneChooser();
+ /** Prepare data pane. */
+ void preparePaneData();
+ /** Prepare button-box. */
+ void prepareButtonBox();
+ /** Load window settings. */
+ void loadSettings();
+
+ /** Save window settings. */
+ void saveSettings();
+ /** Cleanup instance. */
+ void cleanup();
+ /** @} */
+
+ /** @name Actions
+ * @{ */
+ /** Updates action availability. */
+ void updateActionsAvailability();
+ /** @} */
+
+ /** @name Chooser-pane
+ * @{ */
+ /** Returns chooser index for @a iRow. */
+ QModelIndex chooserIndex(int iRow) const;
+ /** Returns current chooser index. */
+ QModelIndex currentChooserIndex() const;
+
+ /** Returns chooser ID for @a iRow. */
+ QUuid chooserID(int iRow) const;
+ /** Returns current chooser ID. */
+ QUuid currentChooserID() const;
+
+ /** Returns chooser Name for @a iRow. */
+ QString chooserName(int iRow) const;
+ /** Returns current Name. */
+ QString currentChooserName() const;
+
+ /** Adds chooser item. */
+ void addChooserItem(const QUuid &uID,
+ const QString &strName,
+ const QString &strOsTypeID,
+ const int iPosition = -1);
+ /** Adds chooser item by machine. */
+ void addChooserItemByMachine(const CMachine &machine,
+ const int iPosition = -1);
+ /** Adds chooser item by ID. */
+ void addChooserItemByID(const QUuid &uID,
+ const int iPosition = -1);
+
+ /** Make sure chooser have current-index if possible. */
+ void makeSureChooserHaveCurrentIndexIfPossible();
+ /** @} */
+
+ /** @name Data-pane
+ * @{ */
+ /** Returns data index for @a iRow and @a iColumn. */
+ QModelIndex dataIndex(int iRow, int iColumn) const;
+
+ /** Returns data-key index for @a iRow. */
+ QModelIndex dataKeyIndex(int iRow) const;
+
+ /** Returns data-value index for @a iRow. */
+ QModelIndex dataValueIndex(int iRow) const;
+
+ /** Returns current data-key. */
+ QString dataKey(int iRow) const;
+
+ /** Returns current data-value. */
+ QString dataValue(int iRow) const;
+
+ /** Adds data item. */
+ void addDataItem(const QString &strKey,
+ const QString &strValue,
+ const int iPosition = -1);
+
+ /** Sorts data items. */
+ void sortData();
+
+ /** Returns the list of known extra-data keys. */
+ static QStringList knownExtraDataKeys();
+ /** @} */
+
+
+ /** @name Arguments
+ * @{ */
+ /** Holds the center widget reference. */
+ QWidget *m_pCenterWidget;
+ /** @} */
+
+ /** @name General
+ * @{ */
+ QVBoxLayout *m_pMainLayout;
+ /** Data pane: Tool-bar. */
+ QIToolBar *m_pToolBar;
+ /** Splitter. */
+ QISplitter *m_pSplitter;
+ /** @} */
+
+ /** @name Chooser-pane
+ * @{ */
+ /** Chooser pane. */
+ QWidget *m_pPaneOfChooser;
+ /** Chooser filter. */
+ QLineEdit *m_pFilterOfChooser;
+ /** Chooser pane: List-view. */
+ QListView *m_pViewOfChooser;
+ /** Chooser pane: Source-model. */
+ QStandardItemModel *m_pModelSourceOfChooser;
+ /** Chooser pane: Proxy-model. */
+ UIChooserPaneSortingModel *m_pModelProxyOfChooser;
+ /** @} */
+
+ /** @name Data-pane
+ * @{ */
+ /** Data pane. */
+ QWidget *m_pPaneOfData;
+ /** Data filter. */
+ QLineEdit *m_pFilterOfData;
+ /** Data pane: Table-view. */
+ QTableView *m_pViewOfData;
+ /** Data pane: Item-model. */
+ QStandardItemModel *m_pModelSourceOfData;
+ /** Data pane: Proxy-model. */
+ QSortFilterProxyModel *m_pModelProxyOfData;
+ /** @} */
+
+ /** @name Button Box
+ * @{ */
+ /** Dialog button-box. */
+ QIDialogButtonBox *m_pButtonBox;
+ /** @} */
+
+ /** @name Actions
+ * @{ */
+ /** Add action. */
+ QAction *m_pActionAdd;
+ /** Del action. */
+ QAction *m_pActionDel;
+ /** Load action. */
+ QAction *m_pActionLoad;
+ /** Save action. */
+ QAction *m_pActionSave;
+ /** @} */
+};
+
+
+/*********************************************************************************************************************************
+* Class UIExtraDataManagerWindow implementation. *
+*********************************************************************************************************************************/
+
+UIExtraDataManagerWindow::UIExtraDataManagerWindow(QWidget *pCenterWidget)
+ : m_pCenterWidget(pCenterWidget)
+ , m_pMainLayout(0), m_pToolBar(0), m_pSplitter(0)
+ , m_pPaneOfChooser(0), m_pFilterOfChooser(0), m_pViewOfChooser(0)
+ , m_pModelSourceOfChooser(0), m_pModelProxyOfChooser(0)
+ , m_pPaneOfData(0), m_pFilterOfData(0), m_pViewOfData(0),
+ m_pModelSourceOfData(0), m_pModelProxyOfData(0)
+ , m_pButtonBox(0)
+ , m_pActionAdd(0), m_pActionDel(0)
+ , m_pActionLoad(0), m_pActionSave(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIExtraDataManagerWindow::~UIExtraDataManagerWindow()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIExtraDataManagerWindow::showAndRaise(QWidget*)
+{
+ /* Show: */
+ show();
+ /* Restore from minimized state: */
+ setWindowState(windowState() & ~Qt::WindowMinimized);
+ /* Raise: */
+ activateWindow();
+// /* Center according passed widget: */
+// gpDesktop->centerWidget(this, pCenterWidget, false);
+}
+
+void UIExtraDataManagerWindow::sltMachineRegistered(const QUuid &uID, bool fRegistered)
+{
+ /* Machine registered: */
+ if (fRegistered)
+ {
+ /* Gather list of 'known IDs': */
+ QList<QUuid> knownIDs;
+ for (int iRow = 0; iRow < m_pModelSourceOfChooser->rowCount(); ++iRow)
+ knownIDs.append(chooserID(iRow));
+
+ /* Get machine items: */
+ const CMachineVector machines = uiCommon().virtualBox().GetMachines();
+ /* Look for the proper place to insert new machine item: */
+ QUuid uPositionID = UIExtraDataManager::GlobalID;
+ foreach (const CMachine &machine, machines)
+ {
+ /* Get iterated machine ID: */
+ const QUuid uIteratedID = machine.GetId();
+ /* If 'iterated ID' equal to 'added ID' => break now: */
+ if (uIteratedID == uID)
+ break;
+ /* If 'iterated ID' is 'known ID' => remember it: */
+ if (knownIDs.contains(uIteratedID))
+ uPositionID = uIteratedID;
+ }
+
+ /* Add new chooser item into source-model: */
+ addChooserItemByID(uID, knownIDs.indexOf(uPositionID) + 1);
+ /* And sort proxy-model: */
+ m_pModelProxyOfChooser->sort(0, Qt::AscendingOrder);
+ /* Make sure chooser have current-index if possible: */
+ makeSureChooserHaveCurrentIndexIfPossible();
+ }
+ /* Machine unregistered: */
+ else
+ {
+ /* Remove chooser item with 'removed ID' if it is among 'known IDs': */
+ for (int iRow = 0; iRow < m_pModelSourceOfChooser->rowCount(); ++iRow)
+ if (chooserID(iRow) == uID)
+ m_pModelSourceOfChooser->removeRow(iRow);
+ }
+}
+
+void UIExtraDataManagerWindow::sltExtraDataMapAcknowledging(const QUuid &uID)
+{
+ /* Update item with 'changed ID' if it is among 'known IDs': */
+ for (int iRow = 0; iRow < m_pModelSourceOfChooser->rowCount(); ++iRow)
+ if (chooserID(iRow) == uID)
+ m_pModelSourceOfChooser->itemFromIndex(chooserIndex(iRow))->setData(true, Field_Known);
+}
+
+void UIExtraDataManagerWindow::sltExtraDataChange(const QUuid &uID, const QString &strKey, const QString &strValue)
+{
+ /* Skip unrelated IDs: */
+ if (currentChooserID() != uID)
+ return;
+
+ /* List of 'known keys': */
+ QStringList knownKeys;
+ for (int iRow = 0; iRow < m_pModelSourceOfData->rowCount(); ++iRow)
+ knownKeys << dataKey(iRow);
+
+ /* Check if 'changed key' is 'known key': */
+ int iPosition = knownKeys.indexOf(strKey);
+ /* If that is 'known key': */
+ if (iPosition != -1)
+ {
+ /* If 'changed value' is empty => REMOVE item: */
+ if (strValue.isEmpty())
+ m_pModelSourceOfData->removeRow(iPosition);
+ /* If 'changed value' is NOT empty => UPDATE item: */
+ else
+ {
+ m_pModelSourceOfData->itemFromIndex(dataKeyIndex(iPosition))->setData(strKey, Qt::UserRole);
+ m_pModelSourceOfData->itemFromIndex(dataValueIndex(iPosition))->setText(strValue);
+ }
+ }
+ /* Else if 'changed value' is NOT empty: */
+ else if (!strValue.isEmpty())
+ {
+ /* Look for the proper place for 'changed key': */
+ QString strPositionKey;
+ foreach (const QString &strIteratedKey, gEDataManager->map(uID).keys())
+ {
+ /* If 'iterated key' equal to 'changed key' => break now: */
+ if (strIteratedKey == strKey)
+ break;
+ /* If 'iterated key' is 'known key' => remember it: */
+ if (knownKeys.contains(strIteratedKey))
+ strPositionKey = strIteratedKey;
+ }
+ /* Calculate resulting position: */
+ iPosition = knownKeys.indexOf(strPositionKey) + 1;
+ /* INSERT item to the required position: */
+ addDataItem(strKey, strValue, iPosition);
+ /* And sort proxy-model: */
+ sortData();
+ }
+}
+
+void UIExtraDataManagerWindow::sltChooserApplyFilter(const QString &strFilter)
+{
+ /* Apply filtering rule: */
+ m_pModelProxyOfChooser->setFilterWildcard(strFilter);
+ /* Make sure chooser have current-index if possible: */
+ makeSureChooserHaveCurrentIndexIfPossible();
+}
+
+void UIExtraDataManagerWindow::sltChooserHandleCurrentChanged(const QModelIndex &index)
+{
+ /* Remove all the old items first: */
+ while (m_pModelSourceOfData->rowCount())
+ m_pModelSourceOfData->removeRow(0);
+
+ /* Ignore invalid indexes: */
+ if (!index.isValid())
+ return;
+
+ /* Add all the new items finally: */
+ const QUuid uID = index.data(Field_ID).toUuid();
+ if (!gEDataManager->contains(uID))
+ gEDataManager->hotloadMachineExtraDataMap(uID);
+ const ExtraDataMap data = gEDataManager->map(uID);
+ foreach (const QString &strKey, data.keys())
+ addDataItem(strKey, data.value(strKey));
+ /* And sort proxy-model: */
+ sortData();
+}
+
+void UIExtraDataManagerWindow::sltChooserHandleSelectionChanged(const QItemSelection&,
+ const QItemSelection&)
+{
+ /* Update actions availability: */
+ updateActionsAvailability();
+}
+
+void UIExtraDataManagerWindow::sltDataApplyFilter(const QString &strFilter)
+{
+ /* Apply filtering rule: */
+ m_pModelProxyOfData->setFilterWildcard(strFilter);
+}
+
+void UIExtraDataManagerWindow::sltDataHandleSelectionChanged(const QItemSelection&,
+ const QItemSelection&)
+{
+ /* Update actions availability: */
+ updateActionsAvailability();
+}
+
+void UIExtraDataManagerWindow::sltDataHandleItemChanged(QStandardItem *pItem)
+{
+ /* Make sure passed item is valid: */
+ AssertPtrReturnVoid(pItem);
+
+ /* Item-data index: */
+ const QModelIndex itemIndex = m_pModelSourceOfData->indexFromItem(pItem);
+ const int iRow = itemIndex.row();
+ const int iColumn = itemIndex.column();
+
+ /* Key-data is changed: */
+ if (iColumn == 0)
+ {
+ /* Should we replace changed key? */
+ bool fReplace = true;
+
+ /* List of 'known keys': */
+ QStringList knownKeys;
+ for (int iKeyRow = 0; iKeyRow < m_pModelSourceOfData->rowCount(); ++iKeyRow)
+ {
+ /* Do not consider the row we are changing as Qt's model is not yet updated: */
+ if (iKeyRow != iRow)
+ knownKeys << dataKey(iKeyRow);
+ }
+
+ /* If changed key exists: */
+ if (knownKeys.contains(itemIndex.data().toString()))
+ {
+ /* Show warning and ask for overwriting approval: */
+ if (!msgCenter().questionBinary(this, MessageType_Question,
+ QString("Overwriting already existing key, Continue?"),
+ 0 /* auto-confirm id */,
+ QString("Overwrite") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */))
+ {
+ /* Cancel the operation, restore the original extra-data key: */
+ pItem->setData(itemIndex.data(Qt::UserRole).toString(), Qt::DisplayRole);
+ fReplace = false;
+ }
+ else
+ {
+ /* Delete previous extra-data key: */
+ gEDataManager->setExtraDataString(itemIndex.data().toString(),
+ QString(),
+ currentChooserID());
+ }
+ }
+
+ /* Replace changed extra-data key if necessary: */
+ if (fReplace)
+ {
+ gEDataManager->setExtraDataString(itemIndex.data(Qt::UserRole).toString(),
+ QString(),
+ currentChooserID());
+ gEDataManager->setExtraDataString(itemIndex.data().toString(),
+ dataValue(iRow),
+ currentChooserID());
+ }
+ }
+ /* Value-data is changed: */
+ else
+ {
+ /* Key-data index: */
+ const QModelIndex keyIndex = dataKeyIndex(iRow);
+ /* Update extra-data: */
+ gEDataManager->setExtraDataString(keyIndex.data().toString(),
+ itemIndex.data().toString(),
+ currentChooserID());
+ }
+}
+
+void UIExtraDataManagerWindow::sltDataHandleCustomContextMenuRequested(const QPoint &pos)
+{
+ /* Prepare menu: */
+ QMenu menu;
+ menu.addAction(m_pActionAdd);
+ menu.addAction(m_pActionDel);
+ menu.addSeparator();
+ menu.addAction(m_pActionSave);
+ /* Execute menu: */
+ m_pActionSave->setProperty("CalledFromContextMenu", true);
+ menu.exec(m_pViewOfData->viewport()->mapToGlobal(pos));
+ m_pActionSave->setProperty("CalledFromContextMenu", QVariant());
+}
+
+void UIExtraDataManagerWindow::sltAdd()
+{
+ /* Make sure this slot called by corresponding action only: */
+ QAction *pSenderAction = qobject_cast<QAction*>(sender());
+ AssertReturnVoid(pSenderAction && m_pActionAdd);
+
+ /* Create input-dialog: */
+ QPointer<QIDialog> pInputDialog = new QIDialog(this);
+ AssertPtrReturnVoid(pInputDialog.data());
+ {
+ /* Configure input-dialog: */
+ pInputDialog->setWindowTitle("Add extra-data record..");
+ pInputDialog->setMinimumWidth(400);
+ /* Create main-layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(pInputDialog);
+ AssertPtrReturnVoid(pMainLayout);
+ {
+ /* Create dialog validator group: */
+ QObjectValidatorGroup *pValidatorGroup = new QObjectValidatorGroup(pInputDialog);
+ AssertReturnVoid(pValidatorGroup);
+ /* Create input-layout: */
+ QGridLayout *pInputLayout = new QGridLayout;
+ AssertPtrReturnVoid(pInputLayout);
+ {
+ /* Create key-label: */
+ QLabel *pLabelKey = new QLabel("&Name:");
+ {
+ /* Configure key-label: */
+ pLabelKey->setAlignment(Qt::AlignRight);
+ /* Add key-label into input-layout: */
+ pInputLayout->addWidget(pLabelKey, 0, 0);
+ }
+ /* Create key-editor: */
+ QComboBox *pEditorKey = new QComboBox;
+ {
+ /* Configure key-editor: */
+ pEditorKey->setEditable(true);
+ pEditorKey->addItems(knownExtraDataKeys());
+ pLabelKey->setBuddy(pEditorKey);
+ /* Create key-editor property setter: */
+ QObjectPropertySetter *pKeyPropertySetter = new QObjectPropertySetter(pInputDialog, "Key");
+ AssertPtrReturnVoid(pKeyPropertySetter);
+ {
+ /* Configure key-editor property setter: */
+ connect(pEditorKey, &QComboBox::editTextChanged,
+ pKeyPropertySetter, &QObjectPropertySetter::sltAssignProperty);
+ }
+ /* Create key-editor validator: */
+ QObjectValidator *pKeyValidator
+ = new QObjectValidator(new QRegularExpressionValidator(QRegularExpression("[\\s\\S]+"), this));
+ AssertPtrReturnVoid(pKeyValidator);
+ {
+ /* Configure key-editor validator: */
+ connect(pEditorKey, &QComboBox::editTextChanged,
+ pKeyValidator, &QObjectValidator::sltValidate);
+ /* Add key-editor validator into dialog validator group: */
+ pValidatorGroup->addObjectValidator(pKeyValidator);
+ }
+ /* Add key-editor into input-layout: */
+ pInputLayout->addWidget(pEditorKey, 0, 1);
+ }
+ /* Create value-label: */
+ QLabel *pLabelValue = new QLabel("&Value:");
+ {
+ /* Configure value-label: */
+ pLabelValue->setAlignment(Qt::AlignRight);
+ /* Add value-label into input-layout: */
+ pInputLayout->addWidget(pLabelValue, 1, 0);
+ }
+ /* Create value-editor: */
+ QLineEdit *pEditorValue = new QLineEdit;
+ {
+ /* Configure value-editor: */
+ pLabelValue->setBuddy(pEditorValue);
+ /* Create value-editor property setter: */
+ QObjectPropertySetter *pValuePropertySetter = new QObjectPropertySetter(pInputDialog, "Value");
+ AssertPtrReturnVoid(pValuePropertySetter);
+ {
+ /* Configure value-editor property setter: */
+ connect(pEditorValue, &QLineEdit::textEdited,
+ pValuePropertySetter, &QObjectPropertySetter::sltAssignProperty);
+ }
+ /* Create value-editor validator: */
+ QObjectValidator *pValueValidator
+ = new QObjectValidator(new QRegularExpressionValidator(QRegularExpression("[\\s\\S]+"), this));
+ AssertPtrReturnVoid(pValueValidator);
+ {
+ /* Configure value-editor validator: */
+ connect(pEditorValue, &QLineEdit::textEdited,
+ pValueValidator, &QObjectValidator::sltValidate);
+ /* Add value-editor validator into dialog validator group: */
+ pValidatorGroup->addObjectValidator(pValueValidator);
+ }
+ /* Add value-editor into input-layout: */
+ pInputLayout->addWidget(pEditorValue, 1, 1);
+ }
+ /* Add input-layout into main-layout: */
+ pMainLayout->addLayout(pInputLayout);
+ }
+ /* Create stretch: */
+ pMainLayout->addStretch();
+ /* Create dialog button-box: */
+ QIDialogButtonBox *pButtonBox = new QIDialogButtonBox;
+ AssertPtrReturnVoid(pButtonBox);
+ {
+ /* Configure button-box: */
+ pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ pButtonBox->button(QDialogButtonBox::Ok)->setAutoDefault(true);
+ pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(pValidatorGroup->result());
+ pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ connect(pValidatorGroup, &QObjectValidatorGroup::sigValidityChange,
+ pButtonBox->button(QDialogButtonBox::Ok), &QPushButton::setEnabled);
+ connect(pButtonBox, &QIDialogButtonBox::accepted, pInputDialog.data(), &QIDialog::accept);
+ connect(pButtonBox, &QIDialogButtonBox::rejected, pInputDialog.data(), &QIDialog::reject);
+ /* Add button-box into main-layout: */
+ pMainLayout->addWidget(pButtonBox);
+ }
+ }
+ }
+
+ /* Execute input-dialog: */
+ if (pInputDialog->exec() == QDialog::Accepted)
+ {
+ /* Should we add new key? */
+ bool fAdd = true;
+
+ /* List of 'known keys': */
+ QStringList knownKeys;
+ for (int iKeyRow = 0; iKeyRow < m_pModelSourceOfData->rowCount(); ++iKeyRow)
+ knownKeys << dataKey(iKeyRow);
+
+ /* If new key exists: */
+ if (knownKeys.contains(pInputDialog->property("Key").toString()))
+ {
+ /* Show warning and ask for overwriting approval: */
+ if (!msgCenter().questionBinary(this, MessageType_Question,
+ QString("Overwriting already existing key, Continue?"),
+ 0 /* auto-confirm id */,
+ QString("Overwrite") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */))
+ {
+ /* Cancel the operation: */
+ fAdd = false;
+ }
+ }
+
+ /* Add new extra-data key if necessary: */
+ if (fAdd)
+ gEDataManager->setExtraDataString(pInputDialog->property("Key").toString(),
+ pInputDialog->property("Value").toString(),
+ currentChooserID());
+ }
+
+ /* Destroy input-dialog: */
+ if (pInputDialog)
+ delete pInputDialog;
+}
+
+void UIExtraDataManagerWindow::sltDel()
+{
+ /* Make sure this slot called by corresponding action only: */
+ QAction *pSenderAction = qobject_cast<QAction*>(sender());
+ AssertReturnVoid(pSenderAction && m_pActionDel);
+
+ /* Gather the map of chosen items: */
+ QMap<QString, QString> items;
+ foreach (const QModelIndex &keyIndex, m_pViewOfData->selectionModel()->selectedRows(0))
+ items.insert(keyIndex.data().toString(), dataValueIndex(keyIndex.row()).data().toString());
+
+ /* Prepare details: */
+ const QString strTableTemplate("<!--EOM--><table border=0 cellspacing=10 cellpadding=0 width=500>%1</table>");
+ const QString strRowTemplate("<tr><td><tt>%1</tt></td><td align=right><tt>%2</tt></td></tr>");
+ QString strDetails;
+ foreach (const QString &strKey, items.keys())
+ strDetails += strRowTemplate.arg(strKey, items.value(strKey));
+ strDetails = strTableTemplate.arg(strDetails);
+
+ /* Ask for user' confirmation: */
+ if (!msgCenter().errorWithQuestion(this, MessageType_Question,
+ QString("<p>Do you really wish to "
+ "remove chosen records?</p>"),
+ strDetails))
+ return;
+
+ /* Erase all the chosen extra-data records: */
+ foreach (const QString &strKey, items.keys())
+ gEDataManager->setExtraDataString(strKey, QString(), currentChooserID());
+}
+
+void UIExtraDataManagerWindow::sltSave()
+{
+ /* Make sure this slot called by corresponding action only: */
+ QAction *pSenderAction = qobject_cast<QAction*>(sender());
+ AssertReturnVoid(pSenderAction && m_pActionSave);
+
+ /* Compose initial file-name: */
+ const QString strInitialFileName = QDir(uiCommon().homeFolder()).absoluteFilePath(QString("%1_ExtraData.xml").arg(currentChooserName()));
+ /* Open file-save dialog to choose file to save extra-data into: */
+ const QString strFileName = QIFileDialog::getSaveFileName(strInitialFileName, "XML files (*.xml)", this,
+ "Choose file to save extra-data into..", 0, true, true);
+ /* Make sure file-name was chosen: */
+ if (strFileName.isEmpty())
+ return;
+
+ /* Create file: */
+ QFile output(strFileName);
+ /* Open file for writing: */
+ bool fOpened = output.open(QIODevice::WriteOnly);
+ AssertReturnVoid(fOpened);
+ {
+ /* Create XML stream writer: */
+ QXmlStreamWriter stream(&output);
+ /* Configure XML stream writer: */
+ stream.setAutoFormatting(true);
+ stream.setAutoFormattingIndent(2);
+ /* Write document: */
+ stream.writeStartDocument();
+ {
+ stream.writeStartElement("VirtualBox");
+ {
+ const QUuid uID = currentChooserID();
+ bool fIsMachine = uID != UIExtraDataManager::GlobalID;
+ const QString strType = fIsMachine ? "Machine" : "Global";
+ stream.writeStartElement(strType);
+ {
+ if (fIsMachine)
+ stream.writeAttribute("uuid", QString("{%1}").arg(uID.toString()));
+ stream.writeStartElement("ExtraData");
+ {
+ /* Called from context-menu: */
+ if (pSenderAction->property("CalledFromContextMenu").toBool() &&
+ !m_pViewOfData->selectionModel()->selection().isEmpty())
+ {
+ foreach (const QModelIndex &keyIndex, m_pViewOfData->selectionModel()->selectedRows())
+ {
+ /* Get data-value index: */
+ const QModelIndex valueIndex = dataValueIndex(keyIndex.row());
+ /* Write corresponding extra-data item into stream: */
+ stream.writeStartElement("ExtraDataItem");
+ {
+ stream.writeAttribute("name", keyIndex.data().toString());
+ stream.writeAttribute("value", valueIndex.data().toString());
+ }
+ stream.writeEndElement(); /* ExtraDataItem */
+ }
+ }
+ /* Called from menu-bar/tool-bar: */
+ else
+ {
+ for (int iRow = 0; iRow < m_pModelProxyOfData->rowCount(); ++iRow)
+ {
+ /* Get indexes: */
+ const QModelIndex keyIndex = m_pModelProxyOfData->index(iRow, 0);
+ const QModelIndex valueIndex = m_pModelProxyOfData->index(iRow, 1);
+ /* Write corresponding extra-data item into stream: */
+ stream.writeStartElement("ExtraDataItem");
+ {
+ stream.writeAttribute("name", keyIndex.data().toString());
+ stream.writeAttribute("value", valueIndex.data().toString());
+ }
+ stream.writeEndElement(); /* ExtraDataItem */
+ }
+ }
+ }
+ stream.writeEndElement(); /* ExtraData */
+ }
+ stream.writeEndElement(); /* strType */
+ }
+ stream.writeEndElement(); /* VirtualBox */
+ }
+ stream.writeEndDocument();
+ /* Close file: */
+ output.close();
+ }
+}
+
+void UIExtraDataManagerWindow::sltLoad()
+{
+ /* Make sure this slot called by corresponding action only: */
+ QAction *pSenderAction = qobject_cast<QAction*>(sender());
+ AssertReturnVoid(pSenderAction && m_pActionLoad);
+
+ /* Compose initial file-name: */
+ const QString strInitialFileName = QDir(uiCommon().homeFolder()).absoluteFilePath(QString("%1_ExtraData.xml").arg(currentChooserName()));
+ /* Open file-open dialog to choose file to open extra-data into: */
+ const QString strFileName = QIFileDialog::getOpenFileName(strInitialFileName, "XML files (*.xml)", this,
+ "Choose file to load extra-data from..");
+ /* Make sure file-name was chosen: */
+ if (strFileName.isEmpty())
+ return;
+
+ /* Create file: */
+ QFile input(strFileName);
+ /* Open file for writing: */
+ bool fOpened = input.open(QIODevice::ReadOnly);
+ AssertReturnVoid(fOpened);
+ {
+ /* Create XML stream reader: */
+ QXmlStreamReader stream(&input);
+ /* Read XML stream: */
+ while (!stream.atEnd())
+ {
+ /* Read subsequent token: */
+ const QXmlStreamReader::TokenType tokenType = stream.readNext();
+ /* Skip non-interesting tokens: */
+ if (tokenType != QXmlStreamReader::StartElement)
+ continue;
+
+ /* Get the name of the current element: */
+ const QString strElementName = stream.name().toString();
+
+ /* Search for the scope ID: */
+ QUuid uLoadingID;
+ if (strElementName == "Global")
+ uLoadingID = UIExtraDataManager::GlobalID;
+ else if (strElementName == "Machine")
+ {
+ const QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("uuid"))
+ {
+ const QString strUuid = attributes.value("uuid").toString();
+ const QUuid uLoadingID(strUuid);
+ if (uLoadingID.isNull())
+ msgCenter().alert(this, MessageType_Warning,
+ QString("<p>Invalid extra-data ID:</p>"
+ "<p>%1</p>").arg(strUuid));
+ }
+ }
+ /* Look particular extra-data entries: */
+ else if (strElementName == "ExtraDataItem")
+ {
+ const QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("name") && attributes.hasAttribute("value"))
+ {
+ const QString strName = attributes.value("name").toString();
+ const QString strValue = attributes.value("value").toString();
+ gEDataManager->setExtraDataString(strName, strValue, currentChooserID());
+ }
+ }
+
+ /* Check extra-data ID: */
+ if (!uLoadingID.isNull() && uLoadingID != currentChooserID() &&
+ !msgCenter().questionBinary(this, MessageType_Question,
+ QString("<p>Inconsistent extra-data ID:</p>"
+ "<p>Current: {%1}</p>"
+ "<p>Loading: {%2}</p>"
+ "<p>Continue with loading?</p>")
+ .arg(currentChooserID().toString(), uLoadingID.toString())))
+ break;
+ }
+ /* Handle XML stream error: */
+ if (stream.hasError())
+ msgCenter().alert(this, MessageType_Warning,
+ QString("<p>Error reading XML file:</p>"
+ "<p>%1</p>").arg(stream.error()));
+ /* Close file: */
+ input.close();
+ }
+}
+
+bool UIExtraDataManagerWindow::shouldBeMaximized() const
+{
+ return gEDataManager->extraDataManagerShouldBeMaximized();
+}
+
+void UIExtraDataManagerWindow::prepare()
+{
+ /* Prepare this: */
+ prepareThis();
+ /* Prepare connections: */
+ prepareConnections();
+ /* Prepare menu: */
+ prepareMenu();
+ /* Prepare central-widget: */
+ prepareCentralWidget();
+ /* Load settings: */
+ loadSettings();
+}
+
+void UIExtraDataManagerWindow::prepareThis()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/edata_manager_32px.png", ":/edata_manager_16px.png"));
+#endif
+
+ /* Apply window title: */
+ setWindowTitle("Extra-data Manager");
+
+ /* Do not count that window as important for application,
+ * it will NOT be taken into account when other top-level windows will be closed: */
+ setAttribute(Qt::WA_QuitOnClose, false);
+
+ /* Delete window when closed: */
+ setAttribute(Qt::WA_DeleteOnClose);
+}
+
+void UIExtraDataManagerWindow::prepareConnections()
+{
+ /* Prepare connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineRegistered,
+ this, &UIExtraDataManagerWindow::sltMachineRegistered);
+}
+
+void UIExtraDataManagerWindow::prepareMenu()
+{
+ /* Create 'Actions' menu: */
+ QMenu *pActionsMenu = menuBar()->addMenu("Actions");
+ AssertReturnVoid(pActionsMenu);
+ {
+ /* Create 'Add' action: */
+ m_pActionAdd = pActionsMenu->addAction("Add");
+ AssertReturnVoid(m_pActionAdd);
+ {
+ /* Configure 'Add' action: */
+ m_pActionAdd->setIcon(UIIconPool::iconSetFull(":/edata_add_24px.png", ":/edata_add_16px.png",
+ ":/edata_add_disabled_24px.png", ":/edata_add_disabled_16px.png"));
+ m_pActionAdd->setShortcut(QKeySequence("Ctrl+T"));
+ connect(m_pActionAdd, &QAction::triggered, this, &UIExtraDataManagerWindow::sltAdd);
+ }
+ /* Create 'Del' action: */
+ m_pActionDel = pActionsMenu->addAction("Remove");
+ AssertReturnVoid(m_pActionDel);
+ {
+ /* Configure 'Del' action: */
+ m_pActionDel->setIcon(UIIconPool::iconSetFull(":/edata_remove_24px.png", ":/edata_remove_16px.png",
+ ":/edata_remove_disabled_24px.png", ":/edata_remove_disabled_16px.png"));
+ m_pActionDel->setShortcut(QKeySequence("Ctrl+R"));
+ connect(m_pActionDel, &QAction::triggered, this, &UIExtraDataManagerWindow::sltDel);
+ }
+
+ /* Add separator: */
+ pActionsMenu->addSeparator();
+
+ /* Create 'Load' action: */
+ m_pActionLoad = pActionsMenu->addAction("Load");
+ AssertReturnVoid(m_pActionLoad);
+ {
+ /* Configure 'Load' action: */
+ m_pActionLoad->setIcon(UIIconPool::iconSetFull(":/edata_load_24px.png", ":/edata_load_16px.png",
+ ":/edata_load_disabled_24px.png", ":/edata_load_disabled_16px.png"));
+ m_pActionLoad->setShortcut(QKeySequence("Ctrl+L"));
+ connect(m_pActionLoad, &QAction::triggered, this, &UIExtraDataManagerWindow::sltLoad);
+ }
+ /* Create 'Save' action: */
+ m_pActionSave = pActionsMenu->addAction("Save As...");
+ AssertReturnVoid(m_pActionSave);
+ {
+ /* Configure 'Save' action: */
+ m_pActionSave->setIcon(UIIconPool::iconSetFull(":/edata_save_24px.png", ":/edata_save_16px.png",
+ ":/edata_save_disabled_24px.png", ":/edata_save_disabled_16px.png"));
+ m_pActionSave->setShortcut(QKeySequence("Ctrl+S"));
+ connect(m_pActionSave, &QAction::triggered, this, &UIExtraDataManagerWindow::sltSave);
+ }
+ }
+}
+
+void UIExtraDataManagerWindow::prepareCentralWidget()
+{
+ /* Prepare central-widget: */
+ setCentralWidget(new QWidget);
+ AssertPtrReturnVoid(centralWidget());
+ {
+ /* Prepare layout: */
+ m_pMainLayout = new QVBoxLayout(centralWidget());
+ AssertReturnVoid(m_pMainLayout && centralWidget()->layout() &&
+ m_pMainLayout == centralWidget()->layout());
+ {
+#ifdef VBOX_WS_MAC
+ /* No spacing/margins on the Mac: */
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+ m_pMainLayout->insertSpacing(0, 10);
+#else /* !VBOX_WS_MAC */
+ /* Set spacing/margin like in the selector window: */
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 2;
+ const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) / 2;
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) / 2;
+ const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) / 2;
+ m_pMainLayout->setContentsMargins(iL, iT, iR, iB);
+#endif /* !VBOX_WS_MAC */
+ /* Prepare tool-bar: */
+ prepareToolBar();
+ /* Prepare splitter: */
+ prepareSplitter();
+ /* Prepare button-box: */
+ prepareButtonBox();
+ }
+ /* Initial focus: */
+ if (m_pViewOfChooser)
+ m_pViewOfChooser->setFocus();
+ }
+}
+
+void UIExtraDataManagerWindow::prepareToolBar()
+{
+ /* Create tool-bar: */
+ m_pToolBar = new QIToolBar(this);
+ AssertPtrReturnVoid(m_pToolBar);
+ {
+ /* Configure tool-bar: */
+ m_pToolBar->setIconSize(QSize(24, 24));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+ /* Add actions: */
+ m_pToolBar->addAction(m_pActionAdd);
+ m_pToolBar->addAction(m_pActionDel);
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionLoad);
+ m_pToolBar->addAction(m_pActionSave);
+ /* Integrate tool-bar into dialog: */
+#ifdef VBOX_WS_MAC
+ /* Enable unified tool-bars on Mac OS X. Available on Qt >= 4.3: */
+ addToolBar(m_pToolBar);
+ m_pToolBar->enableMacToolbar();
+#else /* !VBOX_WS_MAC */
+ /* Add tool-bar into main-layout: */
+ m_pMainLayout->addWidget(m_pToolBar);
+#endif /* !VBOX_WS_MAC */
+ }
+}
+
+void UIExtraDataManagerWindow::prepareSplitter()
+{
+ /* Create splitter: */
+ m_pSplitter = new QISplitter;
+ AssertPtrReturnVoid(m_pSplitter);
+ {
+ /* Prepare panes: */
+ preparePanes();
+ /* Configure splitter: */
+ m_pSplitter->setChildrenCollapsible(false);
+ m_pSplitter->setStretchFactor(0, 0);
+ m_pSplitter->setStretchFactor(1, 1);
+ /* Add splitter into main layout: */
+ m_pMainLayout->addWidget(m_pSplitter);
+ }
+}
+
+void UIExtraDataManagerWindow::preparePanes()
+{
+ /* Prepare chooser-pane: */
+ preparePaneChooser();
+ /* Prepare data-pane: */
+ preparePaneData();
+ /* Link chooser and data panes: */
+ connect(m_pViewOfChooser->selectionModel(), &QItemSelectionModel::currentChanged,
+ this, &UIExtraDataManagerWindow::sltChooserHandleCurrentChanged);
+ connect(m_pViewOfChooser->selectionModel(), &QItemSelectionModel::selectionChanged,
+ this, &UIExtraDataManagerWindow::sltChooserHandleSelectionChanged);
+ connect(m_pViewOfData->selectionModel(), &QItemSelectionModel::selectionChanged,
+ this, &UIExtraDataManagerWindow::sltDataHandleSelectionChanged);
+ connect(m_pModelSourceOfData, &QStandardItemModel::itemChanged,
+ this, &UIExtraDataManagerWindow::sltDataHandleItemChanged);
+ /* Make sure chooser have current-index if possible: */
+ makeSureChooserHaveCurrentIndexIfPossible();
+}
+
+void UIExtraDataManagerWindow::preparePaneChooser()
+{
+ /* Create chooser-pane: */
+ m_pPaneOfChooser = new QWidget;
+ AssertPtrReturnVoid(m_pPaneOfChooser);
+ {
+ /* Create layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(m_pPaneOfChooser);
+ AssertReturnVoid(pLayout && m_pPaneOfChooser->layout() &&
+ pLayout == m_pPaneOfChooser->layout());
+ {
+ /* Configure layout: */
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) / 3;
+ pLayout->setContentsMargins(0, 0, iR, 0);
+ /* Create chooser-filter: */
+ m_pFilterOfChooser = new QLineEdit;
+ {
+ /* Configure chooser-filter: */
+ m_pFilterOfChooser->setPlaceholderText("Search..");
+ connect(m_pFilterOfChooser, &QLineEdit::textChanged,
+ this, &UIExtraDataManagerWindow::sltChooserApplyFilter);
+ /* Add chooser-filter into layout: */
+ pLayout->addWidget(m_pFilterOfChooser);
+ }
+ /* Create chooser-view: */
+ m_pViewOfChooser = new QListView;
+ AssertPtrReturnVoid(m_pViewOfChooser);
+ {
+ /* Configure chooser-view: */
+ delete m_pViewOfChooser->itemDelegate();
+ m_pViewOfChooser->setItemDelegate(new UIChooserPaneDelegate(m_pViewOfChooser));
+ m_pViewOfChooser->setSelectionMode(QAbstractItemView::SingleSelection);
+ /* Create source-model: */
+ m_pModelSourceOfChooser = new QStandardItemModel(m_pViewOfChooser);
+ AssertPtrReturnVoid(m_pModelSourceOfChooser);
+ {
+ /* Create proxy-model: */
+ m_pModelProxyOfChooser = new UIChooserPaneSortingModel(m_pViewOfChooser);
+ AssertPtrReturnVoid(m_pModelProxyOfChooser);
+ {
+ /* Configure proxy-model: */
+ m_pModelProxyOfChooser->setSortRole(Field_Name);
+ m_pModelProxyOfChooser->setFilterRole(Field_Name);
+ m_pModelProxyOfChooser->setSortCaseSensitivity(Qt::CaseInsensitive);
+ m_pModelProxyOfChooser->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ m_pModelProxyOfChooser->setSourceModel(m_pModelSourceOfChooser);
+ m_pViewOfChooser->setModel(m_pModelProxyOfChooser);
+ }
+ /* Add global chooser item into source-model: */
+ addChooserItemByID(UIExtraDataManager::GlobalID);
+ /* Add machine chooser items into source-model: */
+ CMachineVector machines = uiCommon().virtualBox().GetMachines();
+ foreach (const CMachine &machine, machines)
+ addChooserItemByMachine(machine);
+ /* And sort proxy-model: */
+ m_pModelProxyOfChooser->sort(0, Qt::AscendingOrder);
+ }
+ /* Add chooser-view into layout: */
+ pLayout->addWidget(m_pViewOfChooser);
+ }
+ }
+ /* Add chooser-pane into splitter: */
+ m_pSplitter->addWidget(m_pPaneOfChooser);
+ }
+}
+
+void UIExtraDataManagerWindow::preparePaneData()
+{
+ /* Create data-pane: */
+ m_pPaneOfData = new QWidget;
+ AssertPtrReturnVoid(m_pPaneOfData);
+ {
+ /* Create layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(m_pPaneOfData);
+ AssertReturnVoid(pLayout && m_pPaneOfData->layout() &&
+ pLayout == m_pPaneOfData->layout());
+ {
+ /* Configure layout: */
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 3;
+ pLayout->setContentsMargins(iL, 0, 0, 0);
+ /* Create data-filter: */
+ m_pFilterOfData = new QLineEdit;
+ {
+ /* Configure data-filter: */
+ m_pFilterOfData->setPlaceholderText("Search..");
+ connect(m_pFilterOfData, &QLineEdit::textChanged,
+ this, &UIExtraDataManagerWindow::sltDataApplyFilter);
+ /* Add data-filter into layout: */
+ pLayout->addWidget(m_pFilterOfData);
+ }
+ /* Create data-view: */
+ m_pViewOfData = new QTableView;
+ AssertPtrReturnVoid(m_pViewOfData);
+ {
+ /* Create item-model: */
+ m_pModelSourceOfData = new QStandardItemModel(0, 2, m_pViewOfData);
+ AssertPtrReturnVoid(m_pModelSourceOfData);
+ {
+ /* Create proxy-model: */
+ m_pModelProxyOfData = new QSortFilterProxyModel(m_pViewOfChooser);
+ AssertPtrReturnVoid(m_pModelProxyOfData);
+ {
+ /* Configure proxy-model: */
+ m_pModelProxyOfData->setSortCaseSensitivity(Qt::CaseInsensitive);
+ m_pModelProxyOfData->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ m_pModelProxyOfData->setSourceModel(m_pModelSourceOfData);
+ m_pViewOfData->setModel(m_pModelProxyOfData);
+ }
+ /* Configure item-model: */
+ m_pModelSourceOfData->setHorizontalHeaderLabels(QStringList() << "Key" << "Value");
+ }
+ /* Configure data-view: */
+ m_pViewOfData->setSortingEnabled(true);
+ m_pViewOfData->setAlternatingRowColors(true);
+ m_pViewOfData->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pViewOfData->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ m_pViewOfData->setSelectionBehavior(QAbstractItemView::SelectRows);
+ connect(m_pViewOfData, &QTableView::customContextMenuRequested,
+ this, &UIExtraDataManagerWindow::sltDataHandleCustomContextMenuRequested);
+ QHeaderView *pVHeader = m_pViewOfData->verticalHeader();
+ QHeaderView *pHHeader = m_pViewOfData->horizontalHeader();
+ pVHeader->hide();
+ pHHeader->setSortIndicator(0, Qt::AscendingOrder);
+ pHHeader->resizeSection(0, qMin(300, pHHeader->width() / 3));
+ pHHeader->setStretchLastSection(true);
+ /* Add data-view into layout: */
+ pLayout->addWidget(m_pViewOfData);
+ }
+ }
+ /* Add data-pane into splitter: */
+ m_pSplitter->addWidget(m_pPaneOfData);
+ }
+}
+
+void UIExtraDataManagerWindow::prepareButtonBox()
+{
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ AssertPtrReturnVoid(m_pButtonBox);
+ {
+ /* Configure button-box: */
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::Close);
+ m_pButtonBox->button(QDialogButtonBox::Close)->setShortcut(Qt::Key_Escape);
+ connect(m_pButtonBox, &QIDialogButtonBox::helpRequested, &msgCenter(), &UIMessageCenter::sltShowHelpHelpDialog);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UIExtraDataManagerWindow::close);
+ /* Add button-box into main layout: */
+ m_pMainLayout->addWidget(m_pButtonBox);
+ }
+}
+
+void UIExtraDataManagerWindow::loadSettings()
+{
+ /* Load window geometry: */
+ {
+ const QRect geo = gEDataManager->extraDataManagerGeometry(this, m_pCenterWidget);
+ LogRel2(("GUI: UIExtraDataManagerWindow: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ restoreGeometry(geo);
+ }
+
+ /* Load splitter hints: */
+ {
+ m_pSplitter->setSizes(gEDataManager->extraDataManagerSplitterHints(this));
+ }
+}
+
+void UIExtraDataManagerWindow::saveSettings()
+{
+ /* Save splitter hints: */
+ {
+ gEDataManager->setExtraDataManagerSplitterHints(m_pSplitter->sizes());
+ }
+
+ /* Save window geometry: */
+ {
+ const QRect geo = currentGeometry();
+ LogRel2(("GUI: UIExtraDataManagerWindow: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ gEDataManager->setExtraDataManagerGeometry(geo, isCurrentlyMaximized());
+ }
+}
+
+void UIExtraDataManagerWindow::cleanup()
+{
+ /* Save settings: */
+ saveSettings();
+}
+
+void UIExtraDataManagerWindow::updateActionsAvailability()
+{
+ /* Is there something selected in chooser-view? */
+ bool fChooserHasSelection = !m_pViewOfChooser->selectionModel()->selection().isEmpty();
+ /* Is there something selected in data-view? */
+ bool fDataHasSelection = !m_pViewOfData->selectionModel()->selection().isEmpty();
+
+ /* Enable/disable corresponding actions: */
+ m_pActionAdd->setEnabled(fChooserHasSelection);
+ m_pActionDel->setEnabled(fChooserHasSelection && fDataHasSelection);
+ m_pActionLoad->setEnabled(fChooserHasSelection);
+ m_pActionSave->setEnabled(fChooserHasSelection);
+}
+
+QModelIndex UIExtraDataManagerWindow::chooserIndex(int iRow) const
+{
+ return m_pModelSourceOfChooser->index(iRow, 0);
+}
+
+QModelIndex UIExtraDataManagerWindow::currentChooserIndex() const
+{
+ return m_pViewOfChooser->currentIndex();
+}
+
+QUuid UIExtraDataManagerWindow::chooserID(int iRow) const
+{
+ return chooserIndex(iRow).data(Field_ID).toUuid();
+}
+
+QUuid UIExtraDataManagerWindow::currentChooserID() const
+{
+ return currentChooserIndex().data(Field_ID).toUuid();
+}
+
+QString UIExtraDataManagerWindow::chooserName(int iRow) const
+{
+ return chooserIndex(iRow).data(Field_Name).toString();
+}
+
+QString UIExtraDataManagerWindow::currentChooserName() const
+{
+ return currentChooserIndex().data(Field_Name).toString();
+}
+
+void UIExtraDataManagerWindow::addChooserItem(const QUuid &uID,
+ const QString &strName,
+ const QString &strOsTypeID,
+ const int iPosition /* = -1 */)
+{
+ /* Create item: */
+ QStandardItem *pItem = new QStandardItem;
+ AssertPtrReturnVoid(pItem);
+ {
+ /* Which is NOT editable: */
+ pItem->setEditable(false);
+ /* Contains passed ID: */
+ pItem->setData(uID, Field_ID);
+ /* Contains passed name: */
+ pItem->setData(strName, Field_Name);
+ /* Contains passed OS Type ID: */
+ pItem->setData(strOsTypeID, Field_OsTypeID);
+ /* And designated as known/unknown depending on extra-data manager status: */
+ pItem->setData(gEDataManager->contains(uID), Field_Known);
+ /* If insert position defined: */
+ if (iPosition != -1)
+ {
+ /* Insert this item at specified position: */
+ m_pModelSourceOfChooser->insertRow(iPosition, pItem);
+ }
+ /* If insert position undefined: */
+ else
+ {
+ /* Add this item as the last one: */
+ m_pModelSourceOfChooser->appendRow(pItem);
+ }
+ }
+}
+
+void UIExtraDataManagerWindow::addChooserItemByMachine(const CMachine &machine,
+ const int iPosition /* = -1 */)
+{
+ /* Make sure VM is accessible: */
+ if (!machine.isNull() && machine.GetAccessible())
+ return addChooserItem(machine.GetId(), machine.GetName(), machine.GetOSTypeId(), iPosition);
+}
+
+void UIExtraDataManagerWindow::addChooserItemByID(const QUuid &uID,
+ const int iPosition /* = -1 */)
+{
+ /* Global ID? */
+ if (uID == UIExtraDataManager::GlobalID)
+ return addChooserItem(uID, QString("Global"), QString(), iPosition);
+
+ /* Search for the corresponding machine by ID: */
+ CVirtualBox vbox = uiCommon().virtualBox();
+ const CMachine machine = vbox.FindMachine(uID.toString());
+ /* Make sure VM is accessible: */
+ if (vbox.isOk() && !machine.isNull() && machine.GetAccessible())
+ return addChooserItem(uID, machine.GetName(), machine.GetOSTypeId(), iPosition);
+}
+
+void UIExtraDataManagerWindow::makeSureChooserHaveCurrentIndexIfPossible()
+{
+ /* Make sure chooser have current-index if possible: */
+ if (!m_pViewOfChooser->currentIndex().isValid())
+ {
+ /* Do we still have anything to select? */
+ const QModelIndex firstIndex = m_pModelProxyOfChooser->index(0, 0);
+ if (firstIndex.isValid())
+ m_pViewOfChooser->setCurrentIndex(firstIndex);
+ }
+}
+
+QModelIndex UIExtraDataManagerWindow::dataIndex(int iRow, int iColumn) const
+{
+ return m_pModelSourceOfData->index(iRow, iColumn);
+}
+
+QModelIndex UIExtraDataManagerWindow::dataKeyIndex(int iRow) const
+{
+ return dataIndex(iRow, 0);
+}
+
+QModelIndex UIExtraDataManagerWindow::dataValueIndex(int iRow) const
+{
+ return dataIndex(iRow, 1);
+}
+
+QString UIExtraDataManagerWindow::dataKey(int iRow) const
+{
+ return dataKeyIndex(iRow).data().toString();
+}
+
+QString UIExtraDataManagerWindow::dataValue(int iRow) const
+{
+ return dataValueIndex(iRow).data().toString();
+}
+
+void UIExtraDataManagerWindow::addDataItem(const QString &strKey,
+ const QString &strValue,
+ const int iPosition /* = -1 */)
+{
+ /* Prepare items: */
+ QList<QStandardItem*> items;
+ /* Create key item: */
+ items << new QStandardItem(strKey);
+ items.last()->setData(strKey, Qt::UserRole);
+ AssertPtrReturnVoid(items.last());
+ /* Create value item: */
+ items << new QStandardItem(strValue);
+ AssertPtrReturnVoid(items.last());
+ /* If insert position defined: */
+ if (iPosition != -1)
+ {
+ /* Insert these items as the row at the required position: */
+ m_pModelSourceOfData->insertRow(iPosition, items);
+ }
+ /* If insert position undefined: */
+ else
+ {
+ /* Add these items as the last one row: */
+ m_pModelSourceOfData->appendRow(items);
+ }
+}
+
+void UIExtraDataManagerWindow::sortData()
+{
+ /* Sort using current rules: */
+ const QHeaderView *pHHeader = m_pViewOfData->horizontalHeader();
+ const int iSortSection = pHHeader->sortIndicatorSection();
+ const Qt::SortOrder sortOrder = pHHeader->sortIndicatorOrder();
+ m_pModelProxyOfData->sort(iSortSection, sortOrder);
+}
+
+/* static */
+QStringList UIExtraDataManagerWindow::knownExtraDataKeys()
+{
+ return QStringList()
+ << QString()
+ << GUI_RestrictedDialogs
+ << GUI_SuppressMessages << GUI_InvertMessageOption
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+ << GUI_NotificationCenter_KeepSuccessfullProgresses
+#endif
+ << GUI_NotificationCenter_Alignment
+ << GUI_NotificationCenter_Order
+ << GUI_PreventBetaLabel
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ << GUI_PreventApplicationUpdate << GUI_UpdateDate << GUI_UpdateCheckCount
+#endif
+ << GUI_Progress_LegacyMode
+ << GUI_Customizations
+ << GUI_RestrictedGlobalSettingsPages << GUI_RestrictedMachineSettingsPages
+ << GUI_LanguageID
+ << GUI_ActivateHoveredMachineWindow
+ << GUI_DisableHostScreenSaver
+ << GUI_Input_SelectorShortcuts << GUI_Input_MachineShortcuts
+ << GUI_RecentFolderHD << GUI_RecentFolderCD << GUI_RecentFolderFD
+ << GUI_VISOCreator_RecentFolder << GUI_VISOCreator_DialogGeometry
+ << GUI_RecentListHD << GUI_RecentListCD << GUI_RecentListFD
+ << GUI_RestrictedNetworkAttachmentTypes
+ << GUI_LastSelectorWindowPosition << GUI_SplitterSizes
+ << GUI_Toolbar << GUI_Toolbar_Text
+ << GUI_Toolbar_MachineTools_Order << GUI_Toolbar_GlobalTools_Order
+ << GUI_Tools_LastItemsSelected
+ << GUI_Statusbar
+ << GUI_GroupDefinitions << GUI_LastItemSelected
+ << GUI_Details_Elements
+ << GUI_Details_Elements_Preview_UpdateInterval
+ << GUI_SnapshotManager_Details_Expanded
+ << GUI_VirtualMediaManager_Details_Expanded
+ << GUI_HostNetworkManager_Details_Expanded
+ << GUI_CloudProfileManager_Restrictions
+ << GUI_CloudProfileManager_Details_Expanded
+ << GUI_CloudConsoleManager_Restrictions
+ << GUI_CloudConsoleManager_Details_Expanded
+ << GUI_CloudConsole_PublicKey_Path
+ << GUI_HideDescriptionForWizards
+ << GUI_HideFromManager << GUI_HideDetails
+ << GUI_PreventReconfiguration << GUI_PreventSnapshotOperations
+#ifndef VBOX_WS_MAC
+ << GUI_MachineWindowIcons << GUI_MachineWindowNamePostfix
+#endif
+ << GUI_LastNormalWindowPosition << GUI_LastScaleWindowPosition
+#ifndef VBOX_WS_MAC
+ << GUI_MenuBar_Enabled
+#endif
+ << GUI_MenuBar_ContextMenu_Enabled
+ << GUI_RestrictedRuntimeMenus
+ << GUI_RestrictedRuntimeApplicationMenuActions
+ << GUI_RestrictedRuntimeMachineMenuActions
+ << GUI_RestrictedRuntimeViewMenuActions
+ << GUI_RestrictedRuntimeInputMenuActions
+ << GUI_RestrictedRuntimeDevicesMenuActions
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ << GUI_RestrictedRuntimeDebuggerMenuActions
+#endif
+#ifdef VBOX_WS_MAC
+ << GUI_RestrictedRuntimeWindowMenuActions
+#endif
+ << GUI_RestrictedRuntimeHelpMenuActions
+ << GUI_RestrictedVisualStates
+ << GUI_Fullscreen << GUI_Seamless << GUI_Scale
+#ifdef VBOX_WS_X11
+ << GUI_Fullscreen_LegacyMode
+ << GUI_DistinguishMachineWindowGroups
+#endif
+ << GUI_AutoresizeGuest << GUI_LastVisibilityStatusForGuestScreen << GUI_LastGuestSizeHint
+ << GUI_VirtualScreenToHostScreen << GUI_AutomountGuestScreens
+#ifndef VBOX_WS_MAC
+ << GUI_ShowMiniToolBar << GUI_MiniToolBarAutoHide << GUI_MiniToolBarAlignment
+#endif
+ << GUI_StatusBar_Enabled << GUI_StatusBar_ContextMenu_Enabled << GUI_RestrictedStatusBarIndicators << GUI_StatusBar_IndicatorOrder
+#ifdef VBOX_WS_MAC
+ << GUI_RealtimeDockIconUpdateEnabled << GUI_RealtimeDockIconUpdateMonitor << GUI_DockIconDisableOverlay
+#endif
+ << GUI_PassCAD
+ << GUI_MouseCapturePolicy
+ << GUI_GuruMeditationHandler
+ << GUI_HidLedsSync
+ << GUI_ScaleFactor << GUI_Scaling_Optimization
+ << GUI_SessionInformationDialogGeometry
+ << GUI_GuestControl_ProcessControlSplitterHints
+ << GUI_GuestControl_FileManagerDialogGeometry
+ << GUI_GuestControl_FileManagerOptions
+ << GUI_GuestControl_ProcessControlDialogGeometry
+ << GUI_DefaultCloseAction << GUI_RestrictedCloseActions
+ << GUI_LastCloseAction << GUI_CloseActionHook << GUI_DiscardStateOnPowerOff
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ << GUI_Dbg_Enabled << GUI_Dbg_AutoShow
+#endif
+ << GUI_ExtraDataManager_Geometry << GUI_ExtraDataManager_SplitterHints
+ << GUI_LogWindowGeometry
+ << GUI_HelpBrowser_LastURLList
+ << GUI_HelpBrowser_DialogGeometry
+ << GUI_HelpBrowser_Bookmarks
+ << GUI_HelpBrowser_ZoomPercentage;
+}
+
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+
+/*********************************************************************************************************************************
+* Class UIExtraDataManager implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIExtraDataManager *UIExtraDataManager::s_pInstance = 0;
+const QUuid UIExtraDataManager::GlobalID;
+
+/* static */
+UIExtraDataManager* UIExtraDataManager::instance()
+{
+ /* Create/prepare instance if not yet exists: */
+ if (!s_pInstance)
+ {
+ new UIExtraDataManager;
+ s_pInstance->prepare();
+ }
+ /* Return instance: */
+ return s_pInstance;
+}
+
+/* static */
+void UIExtraDataManager::destroy()
+{
+ /* Destroy/cleanup instance if still exists: */
+ if (s_pInstance)
+ {
+ s_pInstance->cleanup();
+ delete s_pInstance;
+ }
+}
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+/* static */
+void UIExtraDataManager::openWindow(QWidget *pCenterWidget)
+{
+ /* Pass to instance: */
+ instance()->open(pCenterWidget);
+}
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+void UIExtraDataManager::hotloadMachineExtraDataMap(const QUuid &uID)
+{
+ /* Make sure it is valid ID: */
+ AssertMsgReturnVoid(!uID.isNull() && uID != GlobalID,
+ ("Invalid VM ID = {%s}\n", uID.toString().toUtf8().constData()));
+ /* Which is not loaded yet: */
+ AssertReturnVoid(!m_data.contains(uID));
+
+ /* Search for corresponding machine: */
+ CVirtualBox vbox = uiCommon().virtualBox();
+ CMachine machine = vbox.FindMachine(uID.toString());
+ if (machine.isNull())
+ return;
+
+ /* Make sure at least empty map is created: */
+ m_data[uID] = ExtraDataMap();
+
+ /* Do not handle inaccessible machine: */
+ if (!machine.GetAccessible())
+ return;
+
+ /* Load machine extra-data map: */
+ foreach (const QString &strKey, machine.GetExtraDataKeys())
+ m_data[uID][strKey] = machine.GetExtraData(strKey);
+
+ /* Notifies about extra-data map acknowledged: */
+ emit sigExtraDataMapAcknowledging(uID);
+}
+
+QString UIExtraDataManager::extraDataString(const QString &strKey, const QUuid &uID /* = GlobalID */)
+{
+ /* Get the actual value: */
+ QString strValue = extraDataStringUnion(strKey, uID);
+ /* If actual value is null we might be able to find old one: */
+ if (strValue.isNull())
+ {
+ foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
+ {
+ strValue = extraDataStringUnion(strOldKey, uID);
+ if (!strValue.isNull())
+ break;
+ }
+ }
+ /* Return null string if result is empty: */
+ if (strValue.isEmpty())
+ return QString();
+
+ /* Returns corresponding value: */
+ return strValue;
+}
+
+void UIExtraDataManager::setExtraDataString(const QString &strKey, const QString &strValue, const QUuid &uID /* = GlobalID */)
+{
+ /* Make sure VBoxSVC is available: */
+ if (!uiCommon().isVBoxSVCAvailable())
+ return;
+
+ /* Hot-load machine extra-data map if necessary: */
+ if (uID != GlobalID && !m_data.contains(uID))
+ hotloadMachineExtraDataMap(uID);
+
+ /* Access corresponding map: */
+ ExtraDataMap &data = m_data[uID];
+
+ /* [Re]cache passed value: */
+ data[strKey] = strValue;
+
+ /* Global extra-data: */
+ if (uID == GlobalID)
+ {
+ /* Get global object: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ /* Update global extra-data: */
+ comVBox.SetExtraData(strKey, strValue);
+ if (!comVBox.isOk())
+ msgCenter().cannotSetExtraData(comVBox, strKey, strValue);
+ /* Wipe out old keys: */
+ foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
+ {
+ comVBox.SetExtraData(strOldKey, QString());
+ if (!comVBox.isOk())
+ {
+ msgCenter().cannotSetExtraData(comVBox, strOldKey, strValue);
+ break;
+ }
+ }
+ }
+ /* Machine extra-data: */
+ else
+ {
+ /* Search for corresponding machine: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ const CMachine comMachine = comVBox.FindMachine(uID.toString());
+ AssertReturnVoid(comVBox.isOk() && !comMachine.isNull());
+ /* Check the configuration access-level: */
+ const KMachineState enmMachineState = comMachine.GetState();
+ const KSessionState enmSessionState = comMachine.GetSessionState();
+ const ConfigurationAccessLevel enmLevel = configurationAccessLevel(enmSessionState, enmMachineState);
+ /* Prepare machine session: */
+ CSession comSession;
+ if (enmLevel == ConfigurationAccessLevel_Full)
+ comSession = uiCommon().openSession(uID);
+ else
+ comSession = uiCommon().openExistingSession(uID);
+ AssertReturnVoid(!comSession.isNull());
+ /* Get machine from that session: */
+ CMachine comSessionMachine = comSession.GetMachine();
+ /* Update machine extra-data: */
+ comSessionMachine.SetExtraData(strKey, strValue);
+ if (!comSessionMachine.isOk())
+ msgCenter().cannotSetExtraData(comSessionMachine, strKey, strValue);
+ /* Wipe out old keys: */
+ foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
+ {
+ comSessionMachine.SetExtraData(strOldKey, QString());
+ if (!comSessionMachine.isOk())
+ {
+ msgCenter().cannotSetExtraData(comSessionMachine, strOldKey, strValue);
+ break;
+ }
+ }
+ comSession.UnlockMachine();
+ }
+}
+
+QStringList UIExtraDataManager::extraDataStringList(const QString &strKey, const QUuid &uID /* = GlobalID */)
+{
+ /* Get the actual value: */
+ QString strValue = extraDataStringUnion(strKey, uID);
+ /* If actual value is null we might be able to find old one: */
+ if (strValue.isNull())
+ {
+ foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
+ {
+ strValue = extraDataStringUnion(strOldKey, uID);
+ if (!strValue.isNull())
+ break;
+ }
+ }
+ /* Return empty string list if result is empty: */
+ if (strValue.isEmpty())
+ return QStringList();
+
+ /* Few old extra-data string-lists were separated with 'semicolon' symbol.
+ * All new separated by 'comma'. We have to take that into account. */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ return strValue.split(QRegularExpression("[;,]"), Qt::SkipEmptyParts);
+#else
+ return strValue.split(QRegularExpression("[;,]"), QString::SkipEmptyParts);
+#endif
+}
+
+void UIExtraDataManager::setExtraDataStringList(const QString &strKey, const QStringList &value, const QUuid &uID /* = GlobalID */)
+{
+ /* Make sure VBoxSVC is available: */
+ if (!uiCommon().isVBoxSVCAvailable())
+ return;
+
+ /* Hot-load machine extra-data map if necessary: */
+ if (uID != GlobalID && !m_data.contains(uID))
+ hotloadMachineExtraDataMap(uID);
+
+ /* Access corresponding map: */
+ ExtraDataMap &data = m_data[uID];
+
+ /* [Re]cache passed value: */
+ data[strKey] = value.join(",");
+
+ /* Global extra-data: */
+ if (uID == GlobalID)
+ {
+ /* Get global object: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ /* Update global extra-data: */
+ comVBox.SetExtraDataStringList(strKey, value);
+ if (!comVBox.isOk())
+ msgCenter().cannotSetExtraData(comVBox, strKey, value.join(","));
+ /* Wipe out old keys: */
+ foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
+ {
+ comVBox.SetExtraData(strOldKey, QString());
+ if (!comVBox.isOk())
+ {
+ msgCenter().cannotSetExtraData(comVBox, strOldKey, value.join(","));
+ break;
+ }
+ }
+ }
+ /* Machine extra-data: */
+ else
+ {
+ /* Search for corresponding machine: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ const CMachine comMachine = comVBox.FindMachine(uID.toString());
+ AssertReturnVoid(comVBox.isOk() && !comMachine.isNull());
+ /* Check the configuration access-level: */
+ const KMachineState enmMachineState = comMachine.GetState();
+ const KSessionState enmSessionState = comMachine.GetSessionState();
+ const ConfigurationAccessLevel enmLevel = configurationAccessLevel(enmSessionState, enmMachineState);
+ /* Prepare machine session: */
+ CSession comSession;
+ if (enmLevel == ConfigurationAccessLevel_Full)
+ comSession = uiCommon().openSession(uID);
+ else
+ comSession = uiCommon().openExistingSession(uID);
+ AssertReturnVoid(!comSession.isNull());
+ /* Get machine from that session: */
+ CMachine comSessionMachine = comSession.GetMachine();
+ /* Update machine extra-data: */
+ comSessionMachine.SetExtraDataStringList(strKey, value);
+ if (!comSessionMachine.isOk())
+ msgCenter().cannotSetExtraData(comSessionMachine, strKey, value.join(","));
+ /* Wipe out old keys: */
+ foreach (const QString &strOldKey, g_mapOfObsoleteKeys.values(strKey))
+ {
+ comSessionMachine.SetExtraData(strOldKey, QString());
+ if (!comSessionMachine.isOk())
+ {
+ msgCenter().cannotSetExtraData(comSessionMachine, strOldKey, value.join(","));
+ break;
+ }
+ }
+ comSession.UnlockMachine();
+ }
+}
+
+UIExtraDataManager::UIExtraDataManager()
+ : m_pHandler(0)
+{
+ /* Connect to static instance: */
+ s_pInstance = this;
+}
+
+UIExtraDataManager::~UIExtraDataManager()
+{
+ /* Disconnect from static instance: */
+ s_pInstance = 0;
+}
+
+UIExtraDataMetaDefs::DialogType UIExtraDataManager::restrictedDialogTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::DialogType result = UIExtraDataMetaDefs::DialogType_Invalid;
+ /* Get restricted runtime-menu-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedDialogs, uID))
+ {
+ UIExtraDataMetaDefs::DialogType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::DialogType>(strValue);
+ if (value != UIExtraDataMetaDefs::DialogType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::DialogType>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedDialogTypes(UIExtraDataMetaDefs::DialogType dialogs, const QUuid &uID)
+{
+ /* We have MenuType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DialogType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle DialogType_All enum-value: */
+ if (dialogs == UIExtraDataMetaDefs::DialogType_All)
+ result << gpConverter->toInternalString(dialogs);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::DialogType enumValue =
+ static_cast<UIExtraDataMetaDefs::DialogType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip DialogType_Invalid & DialogType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::DialogType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::DialogType_All)
+ continue;
+ if (dialogs & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedDialogs, result, uID);
+}
+
+UIColorThemeType UIExtraDataManager::colorTheme()
+{
+ return gpConverter->fromInternalString<UIColorThemeType>(extraDataString(GUI_ColorTheme));
+}
+
+void UIExtraDataManager::setColorTheme(const UIColorThemeType &enmType)
+{
+ setExtraDataString(GUI_ColorTheme, gpConverter->toInternalString(enmType));
+}
+
+QStringList UIExtraDataManager::suppressedMessages(const QUuid &uID /* = GlobalID */)
+{
+ return extraDataStringList(GUI_SuppressMessages, uID);
+}
+
+void UIExtraDataManager::setSuppressedMessages(const QStringList &list)
+{
+ setExtraDataStringList(GUI_SuppressMessages, list);
+}
+
+QStringList UIExtraDataManager::messagesWithInvertedOption()
+{
+ return extraDataStringList(GUI_InvertMessageOption);
+}
+
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+bool UIExtraDataManager::keepSuccessfullNotificationProgresses()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_NotificationCenter_KeepSuccessfullProgresses);
+}
+
+void UIExtraDataManager::setKeepSuccessfullNotificationProgresses(bool fKeep)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ setExtraDataString(GUI_NotificationCenter_KeepSuccessfullProgresses, toFeatureAllowed(fKeep));
+}
+#endif /* VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON */
+
+Qt::Alignment UIExtraDataManager::notificationCenterAlignment()
+{
+ const QString strValue = extraDataString(GUI_NotificationCenter_Alignment);
+ return strValue.isEmpty() ? Qt::AlignTop : gpConverter->fromInternalString<Qt::Alignment>(strValue);
+}
+
+void UIExtraDataManager::setNotificationCenterAlignment(Qt::Alignment enmOrder)
+{
+ const QString strValue = enmOrder == Qt::AlignTop ? QString() : gpConverter->toInternalString(enmOrder);
+ setExtraDataString(GUI_NotificationCenter_Alignment, strValue);
+}
+
+Qt::SortOrder UIExtraDataManager::notificationCenterOrder()
+{
+ const QString strValue = extraDataString(GUI_NotificationCenter_Order);
+ return strValue.isEmpty() ? Qt::DescendingOrder : gpConverter->fromInternalString<Qt::SortOrder>(strValue);
+}
+
+void UIExtraDataManager::setNotificationCenterOrder(Qt::SortOrder enmOrder)
+{
+ const QString strValue = enmOrder == Qt::DescendingOrder ? QString() : gpConverter->toInternalString(enmOrder);
+ setExtraDataString(GUI_NotificationCenter_Order, strValue);
+}
+
+bool UIExtraDataManager::preventBetaBuildLavel()
+{
+ return isFeatureAllowed(GUI_PreventBetaLabel);
+}
+
+#if !defined(VBOX_BLEEDING_EDGE) && !defined(DEBUG)
+QString UIExtraDataManager::preventBetaBuildWarningForVersion()
+{
+ return extraDataString(GUI_PreventBetaWarning);
+}
+#endif /* !defined(VBOX_BLEEDING_EDGE) && !defined(DEBUG) */
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+bool UIExtraDataManager::applicationUpdateEnabled()
+{
+ /* 'True' unless 'restriction' feature allowed: */
+ return !isFeatureAllowed(GUI_PreventApplicationUpdate);
+}
+
+QString UIExtraDataManager::applicationUpdateData()
+{
+ return extraDataString(GUI_UpdateDate);
+}
+
+void UIExtraDataManager::setApplicationUpdateData(const QString &strValue)
+{
+ setExtraDataString(GUI_UpdateDate, strValue);
+}
+
+qulonglong UIExtraDataManager::applicationUpdateCheckCounter()
+{
+ /* Read subsequent update check counter value: */
+ qulonglong uResult = 1;
+ const QString strCheckCount = extraDataString(GUI_UpdateCheckCount);
+ if (!strCheckCount.isEmpty())
+ {
+ bool ok = false;
+ qulonglong uCheckCount = strCheckCount.toULongLong(&ok);
+ if (ok) uResult = uCheckCount;
+ }
+ /* Return update check counter value: */
+ return uResult;
+}
+
+void UIExtraDataManager::incrementApplicationUpdateCheckCounter()
+{
+ /* Increment update check counter value: */
+ setExtraDataString(GUI_UpdateCheckCount, QString::number(applicationUpdateCheckCounter() + 1));
+}
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+bool UIExtraDataManager::legacyProgressHandlingRequested()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_Progress_LegacyMode);
+}
+
+bool UIExtraDataManager::guiFeatureEnabled(GUIFeatureType enmFeature)
+{
+ /* Acquire GUI feature list: */
+ GUIFeatureType enmFeatures = GUIFeatureType_None;
+ foreach (const QString &strValue, extraDataStringList(GUI_Customizations))
+ enmFeatures = static_cast<GUIFeatureType>(enmFeatures | gpConverter->fromInternalString<GUIFeatureType>(strValue));
+ /* Return whether the requested feature is enabled: */
+ return enmFeatures & enmFeature;
+}
+
+QList<GlobalSettingsPageType> UIExtraDataManager::restrictedGlobalSettingsPages()
+{
+ /* Prepare result: */
+ QList<GlobalSettingsPageType> result;
+ /* Get restricted global-settings-pages: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedGlobalSettingsPages))
+ {
+ GlobalSettingsPageType value = gpConverter->fromInternalString<GlobalSettingsPageType>(strValue);
+ if (value != GlobalSettingsPageType_Invalid)
+ result << value;
+ }
+ /* Return result: */
+ return result;
+}
+
+QList<MachineSettingsPageType> UIExtraDataManager::restrictedMachineSettingsPages(const QUuid &uID)
+{
+ /* Prepare result: */
+ QList<MachineSettingsPageType> result;
+ /* Get restricted machine-settings-pages: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedMachineSettingsPages, uID))
+ {
+ MachineSettingsPageType value = gpConverter->fromInternalString<MachineSettingsPageType>(strValue);
+ if (value != MachineSettingsPageType_Invalid)
+ result << value;
+ }
+ /* Return result: */
+ return result;
+}
+
+QString UIExtraDataManager::languageId()
+{
+ /* Load language ID: */
+ return extraDataString(GUI_LanguageID);
+}
+
+void UIExtraDataManager::setLanguageId(const QString &strLanguageId)
+{
+ /* Save language ID: */
+ setExtraDataString(GUI_LanguageID, strLanguageId);
+}
+
+MaximumGuestScreenSizePolicy UIExtraDataManager::maxGuestResolutionPolicy()
+{
+ /* Return maximum guest-screen resolution policy: */
+ return gpConverter->fromInternalString<MaximumGuestScreenSizePolicy>(extraDataString(GUI_MaxGuestResolution));
+}
+
+void UIExtraDataManager::setMaxGuestScreenResolution(MaximumGuestScreenSizePolicy enmPolicy, const QSize resolution /* = QSize() */)
+{
+ /* If policy is 'Fixed' => call the wrapper: */
+ if (enmPolicy == MaximumGuestScreenSizePolicy_Fixed)
+ setMaxGuestResolutionForPolicyFixed(resolution);
+ /* Otherwise => just store the value: */
+ else
+ setExtraDataString(GUI_MaxGuestResolution, gpConverter->toInternalString(enmPolicy));
+}
+
+QSize UIExtraDataManager::maxGuestResolutionForPolicyFixed()
+{
+ /* Acquire maximum guest-screen resolution policy: */
+ const QString strPolicy = extraDataString(GUI_MaxGuestResolution);
+ const MaximumGuestScreenSizePolicy enmPolicy = gpConverter->fromInternalString<MaximumGuestScreenSizePolicy>(strPolicy);
+
+ /* Make sure maximum guest-screen resolution policy is really Fixed: */
+ if (enmPolicy != MaximumGuestScreenSizePolicy_Fixed)
+ return QSize();
+
+ /* Parse maximum guest-screen resolution: */
+ const QStringList values = strPolicy.split(',');
+ int iWidth = values.at(0).toInt();
+ int iHeight = values.at(1).toInt();
+ if (iWidth <= 0)
+ iWidth = 640;
+ if (iHeight <= 0)
+ iHeight = 480;
+
+ /* Return maximum guest-screen resolution: */
+ return QSize(iWidth, iHeight);
+}
+
+void UIExtraDataManager::setMaxGuestResolutionForPolicyFixed(const QSize &resolution)
+{
+ /* If resolution is 'empty' => call the wrapper: */
+ if (resolution.isEmpty())
+ setMaxGuestScreenResolution(MaximumGuestScreenSizePolicy_Automatic);
+ /* Otherwise => just store the value: */
+ else
+ setExtraDataString(GUI_MaxGuestResolution, QString("%1,%2").arg(resolution.width()).arg(resolution.height()));
+}
+
+bool UIExtraDataManager::activateHoveredMachineWindow()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_ActivateHoveredMachineWindow);
+}
+
+void UIExtraDataManager::setActivateHoveredMachineWindow(bool fActivate)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ setExtraDataString(GUI_ActivateHoveredMachineWindow, toFeatureAllowed(fActivate));
+}
+
+bool UIExtraDataManager::disableHostScreenSaver()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_DisableHostScreenSaver);
+}
+
+void UIExtraDataManager::setDisableHostScreenSaver(bool fActivate)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ setExtraDataString(GUI_DisableHostScreenSaver, toFeatureAllowed(fActivate));
+}
+
+QString UIExtraDataManager::hostKeyCombination()
+{
+ /* Acquire host-key combination: */
+ QString strHostCombo = extraDataString(GUI_Input_HostKeyCombination);
+ /* Invent some sane default if it's absolutely wrong or invalid: */
+ QRegularExpression reTemplate("0|[1-9]\\d*(,[1-9]\\d*)?(,[1-9]\\d*)?");
+ if (!reTemplate.match(strHostCombo).hasMatch() || !UIHostCombo::isValidKeyCombo(strHostCombo))
+ {
+#if defined (VBOX_WS_MAC)
+ strHostCombo = "55"; // QZ_LMETA
+#elif defined (VBOX_WS_WIN)
+ strHostCombo = "163"; // VK_RCONTROL
+#elif defined (VBOX_WS_X11)
+ strHostCombo = "65508"; // XK_Control_R
+#else
+# warning "port me!"
+#endif
+ }
+ /* Return host-key combination: */
+ return strHostCombo;
+}
+
+void UIExtraDataManager::setFontScaleFactor(int iFontScaleFactor)
+{
+ if (iFontScaleFactor < UIExtraDataDefs::iFontScaleMin || iFontScaleFactor > UIExtraDataDefs::iFontScaleMax)
+ return;
+ setExtraDataString(GUI_FontScaleFactor, QString::number(iFontScaleFactor));
+}
+
+int UIExtraDataManager::fontScaleFactor()
+{
+ QString strFontScaleFactor = extraDataString(GUI_FontScaleFactor);
+ bool fConversion = false;
+ int iScaleFactor = strFontScaleFactor.toInt(&fConversion);
+ if (!fConversion || iScaleFactor < UIExtraDataDefs::iFontScaleMin || iScaleFactor > UIExtraDataDefs::iFontScaleMax)
+ return 100;
+ return iScaleFactor;
+}
+
+void UIExtraDataManager::setHostKeyCombination(const QString &strHostCombo)
+{
+ /* Do not save anything if it's absolutely wrong or invalid: */
+ QRegularExpression reTemplate("0|[1-9]\\d*(,[1-9]\\d*)?(,[1-9]\\d*)?");
+ if (!reTemplate.match(strHostCombo).hasMatch() || !UIHostCombo::isValidKeyCombo(strHostCombo))
+ return;
+ /* Define host-combo: */
+ setExtraDataString(GUI_Input_HostKeyCombination, strHostCombo);
+}
+
+QStringList UIExtraDataManager::shortcutOverrides(const QString &strPoolExtraDataID)
+{
+ if (strPoolExtraDataID == GUI_Input_SelectorShortcuts)
+ return extraDataStringList(GUI_Input_SelectorShortcuts);
+ if (strPoolExtraDataID == GUI_Input_MachineShortcuts)
+ return extraDataStringList(GUI_Input_MachineShortcuts);
+ return QStringList();
+}
+
+bool UIExtraDataManager::autoCaptureEnabled()
+{
+ /* Prepare auto-capture flag: */
+ bool fAutoCapture = true /* indifferently */;
+ /* Acquire whether the auto-capture is restricted: */
+ QString strAutoCapture = extraDataString(GUI_Input_AutoCapture);
+ /* Invent some sane default if it's empty: */
+ if (strAutoCapture.isEmpty())
+ {
+#if defined(VBOX_WS_X11) && defined(DEBUG)
+ fAutoCapture = false;
+#else
+ fAutoCapture = true;
+#endif
+ }
+ /* 'True' unless feature restricted: */
+ else
+ fAutoCapture = !isFeatureRestricted(GUI_Input_AutoCapture);
+ /* Return auto-capture flag: */
+ return fAutoCapture;
+}
+
+void UIExtraDataManager::setAutoCaptureEnabled(bool fEnabled)
+{
+ /* Store actual feature state, whether it is "true" or "false",
+ * because absent state means default, different on various hosts: */
+ setExtraDataString(GUI_Input_AutoCapture, toFeatureState(fEnabled));
+}
+
+QString UIExtraDataManager::remappedScanCodes()
+{
+ /* Acquire remapped scan codes: */
+ QString strRemappedScanCodes = extraDataString(GUI_RemapScancodes);
+ /* Clear the record if it's absolutely wrong: */
+ QRegularExpression reTemplate("(\\d+=\\d+,)*\\d+=\\d+");
+ if (!reTemplate.match(strRemappedScanCodes).hasMatch())
+ strRemappedScanCodes.clear();
+ /* Return remapped scan codes: */
+ return strRemappedScanCodes;
+}
+
+QString UIExtraDataManager::proxySettings()
+{
+ return extraDataString(GUI_ProxySettings);
+}
+
+void UIExtraDataManager::setProxySettings(const QString &strSettings)
+{
+ setExtraDataString(GUI_ProxySettings, strSettings);
+}
+
+QString UIExtraDataManager::recentFolderForHardDrives()
+{
+ return extraDataString(GUI_RecentFolderHD);
+}
+
+QString UIExtraDataManager::recentFolderForOpticalDisks()
+{
+ return extraDataString(GUI_RecentFolderCD);
+}
+
+QString UIExtraDataManager::recentFolderForFloppyDisks()
+{
+ return extraDataString(GUI_RecentFolderFD);
+}
+
+void UIExtraDataManager::setRecentFolderForHardDrives(const QString &strValue)
+{
+ setExtraDataString(GUI_RecentFolderHD, strValue);
+}
+
+void UIExtraDataManager::setRecentFolderForOpticalDisks(const QString &strValue)
+{
+ setExtraDataString(GUI_RecentFolderCD, strValue);
+}
+
+void UIExtraDataManager::setRecentFolderForFloppyDisks(const QString &strValue)
+{
+ setExtraDataString(GUI_RecentFolderFD, strValue);
+}
+
+QStringList UIExtraDataManager::recentListOfHardDrives()
+{
+ return extraDataStringList(GUI_RecentListHD);
+}
+
+QStringList UIExtraDataManager::recentListOfOpticalDisks()
+{
+ return extraDataStringList(GUI_RecentListCD);
+}
+
+QStringList UIExtraDataManager::recentListOfFloppyDisks()
+{
+ return extraDataStringList(GUI_RecentListFD);
+}
+
+void UIExtraDataManager::setRecentListOfHardDrives(const QStringList &value)
+{
+ setExtraDataStringList(GUI_RecentListHD, value);
+}
+
+void UIExtraDataManager::setRecentListOfOpticalDisks(const QStringList &value)
+{
+ setExtraDataStringList(GUI_RecentListCD, value);
+}
+
+void UIExtraDataManager::setRecentListOfFloppyDisks(const QStringList &value)
+{
+ setExtraDataStringList(GUI_RecentListFD, value);
+}
+
+UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork UIExtraDataManager::restrictedNetworkAttachmentTypes()
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmResult =
+ UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid;
+ /* Get restricted network attachment types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedNetworkAttachmentTypes))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmValue =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(strValue);
+ if (enmValue != UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid && !(enmResult & enmValue))
+ enmResult = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(enmResult | enmValue);
+ }
+ /* Return result: */
+ return enmResult;
+}
+
+QString UIExtraDataManager::visoCreatorRecentFolder()
+{
+ return extraDataString(GUI_VISOCreator_RecentFolder);
+}
+
+void UIExtraDataManager::setVISOCreatorRecentFolder(const QString &strValue)
+{
+ setExtraDataString(GUI_VISOCreator_RecentFolder, strValue);
+}
+
+QRect UIExtraDataManager::visoCreatorDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry)
+{
+ return dialogGeometry(GUI_VISOCreator_DialogGeometry, pWidget, pParentWidget, defaultGeometry);
+}
+
+void UIExtraDataManager::setVisoCreatorDialogGeometry(const QRect &geometry, bool fMaximized)
+{
+ setDialogGeometry(GUI_VISOCreator_DialogGeometry, geometry, fMaximized);
+}
+
+bool UIExtraDataManager::visoCreatorDialogShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_VISOCreator_DialogGeometry);
+}
+
+QRect UIExtraDataManager::selectorWindowGeometry(QWidget *pWidget)
+{
+ return dialogGeometry(GUI_LastSelectorWindowPosition, pWidget);
+}
+
+bool UIExtraDataManager::selectorWindowShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_LastSelectorWindowPosition);
+}
+
+void UIExtraDataManager::setSelectorWindowGeometry(const QRect &geometry, bool fMaximized)
+{
+ /* Serialize passed values: */
+ QStringList data;
+ data << QString::number(geometry.x());
+ data << QString::number(geometry.y());
+ data << QString::number(geometry.width());
+ data << QString::number(geometry.height());
+ if (fMaximized)
+ data << GUI_Geometry_State_Max;
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_LastSelectorWindowPosition, data);
+}
+
+QList<int> UIExtraDataManager::selectorWindowSplitterHints()
+{
+ /* Get corresponding extra-data: */
+ const QStringList data = extraDataStringList(GUI_SplitterSizes);
+
+ /* Parse loaded data: */
+ QList<int> hints;
+ hints << (data.size() > 0 ? data[0].toInt() : 0);
+ hints << (data.size() > 1 ? data[1].toInt() : 0);
+
+ /* Return hints: */
+ return hints;
+}
+
+void UIExtraDataManager::setSelectorWindowSplitterHints(const QList<int> &hints)
+{
+ /* Parse passed hints: */
+ QStringList data;
+ data << (hints.size() > 0 ? QString::number(hints[0]) : QString());
+ data << (hints.size() > 1 ? QString::number(hints[1]) : QString());
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_SplitterSizes, data);
+}
+
+bool UIExtraDataManager::selectorWindowToolBarVisible()
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_Toolbar);
+}
+
+void UIExtraDataManager::setSelectorWindowToolBarVisible(bool fVisible)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_Toolbar, toFeatureRestricted(!fVisible));
+}
+
+bool UIExtraDataManager::selectorWindowToolBarTextVisible()
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_Toolbar_Text);
+}
+
+void UIExtraDataManager::setSelectorWindowToolBarTextVisible(bool fVisible)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_Toolbar_Text, toFeatureRestricted(!fVisible));
+}
+
+QList<UIToolType> UIExtraDataManager::toolsPaneLastItemsChosen()
+{
+ /* Parse loaded data: */
+ QList<UIToolType> result;
+ foreach (const QString &strValue, extraDataStringList(GUI_Tools_LastItemsSelected))
+ {
+ const UIToolType enmType = gpConverter->fromInternalString<UIToolType>(strValue);
+ if (enmType != UIToolType_Invalid)
+ result << enmType;
+ }
+
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setToolsPaneLastItemsChosen(const QList<UIToolType> &set)
+{
+ /* Serialize passed values: */
+ QStringList data;
+ foreach (const UIToolType &enmType, set)
+ data << gpConverter->toInternalString(enmType);
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_Tools_LastItemsSelected, data);
+}
+
+bool UIExtraDataManager::selectorWindowStatusBarVisible()
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_Statusbar);
+}
+
+void UIExtraDataManager::setSelectorWindowStatusBarVisible(bool fVisible)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_Statusbar, toFeatureRestricted(!fVisible));
+}
+
+QStringList UIExtraDataManager::knownMachineGroupDefinitionKeys()
+{
+ /* Acquire a list of known group definition keys: */
+ QStringList result;
+ foreach (const QString &strKey, m_data.value(GlobalID).keys())
+ if (strKey.startsWith(GUI_GroupDefinitions))
+ {
+ QString strGroupID = strKey;
+ strGroupID.remove(GUI_GroupDefinitions);
+ result << strGroupID;
+ }
+
+ /* Return result: */
+ return result;
+}
+
+QStringList UIExtraDataManager::machineGroupDefinitions(const QString &strGroupID)
+{
+ return extraDataStringList(GUI_GroupDefinitions + strGroupID);
+}
+
+void UIExtraDataManager::setMachineGroupDefinitions(const QString &strGroupID, const QStringList &definitions)
+{
+ setExtraDataStringList(GUI_GroupDefinitions + strGroupID, definitions);
+}
+
+QString UIExtraDataManager::selectorWindowLastItemChosen()
+{
+ return extraDataString(GUI_LastItemSelected);
+}
+
+void UIExtraDataManager::setSelectorWindowLastItemChosen(const QString &strItemID)
+{
+ setExtraDataString(GUI_LastItemSelected, strItemID);
+}
+
+QMap<DetailsElementType, bool> UIExtraDataManager::selectorWindowDetailsElements()
+{
+ /* Get corresponding extra-data: */
+ const QStringList data = extraDataStringList(GUI_Details_Elements);
+
+ /* Desearialize passed elements: */
+ QMap<DetailsElementType, bool> elements;
+ foreach (QString strItem, data)
+ {
+ bool fOpened = true;
+ if (strItem.endsWith("Closed", Qt::CaseInsensitive))
+ {
+ fOpened = false;
+ strItem.remove("Closed", Qt::CaseInsensitive);
+ }
+ const DetailsElementType enmType = gpConverter->fromInternalString<DetailsElementType>(strItem);
+ if (enmType != DetailsElementType_Invalid)
+ elements[enmType] = fOpened;
+ }
+
+ /* If settings are empty: */
+ if (elements.isEmpty())
+ {
+ /* Propose the defaults: */
+ elements[DetailsElementType_General] = true;
+ elements[DetailsElementType_Preview] = true;
+ elements[DetailsElementType_System] = true;
+ elements[DetailsElementType_Display] = true;
+ elements[DetailsElementType_Storage] = true;
+ elements[DetailsElementType_Audio] = true;
+ elements[DetailsElementType_Network] = true;
+ elements[DetailsElementType_USB] = true;
+ elements[DetailsElementType_SF] = true;
+ elements[DetailsElementType_Description] = true;
+ }
+
+ /* Return elements: */
+ return elements;
+}
+
+void UIExtraDataManager::setSelectorWindowDetailsElements(const QMap<DetailsElementType, bool> &elements)
+{
+ /* Prepare corresponding extra-data: */
+ QStringList data;
+
+ /* Searialize passed elements: */
+ foreach (DetailsElementType enmType, elements.keys())
+ {
+ QString strValue = gpConverter->toInternalString(enmType);
+ if (!elements[enmType])
+ strValue += "Closed";
+ data << strValue;
+ }
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_Details_Elements, data);
+}
+
+PreviewUpdateIntervalType UIExtraDataManager::selectorWindowPreviewUpdateInterval()
+{
+ return gpConverter->fromInternalString<PreviewUpdateIntervalType>(extraDataString(GUI_Details_Elements_Preview_UpdateInterval));
+}
+
+void UIExtraDataManager::setSelectorWindowPreviewUpdateInterval(PreviewUpdateIntervalType interval)
+{
+ setExtraDataString(GUI_Details_Elements_Preview_UpdateInterval, gpConverter->toInternalString(interval));
+}
+
+QStringList UIExtraDataManager::vboxManagerDetailsPaneElementOptions(DetailsElementType enmElementType)
+{
+ /* Compose full key from GUI_Details_Elements and enmElementType: */
+ QString strElementType = gpConverter->toInternalString(enmElementType);
+ AssertReturn(!strElementType.isEmpty(), QStringList());
+ strElementType[0] = strElementType.at(0).toUpper();
+ const QString strFullKey = QString("%1/%2").arg(GUI_Details_Elements).arg(strElementType);
+
+ /* Return option list: */
+ return extraDataStringList(strFullKey);
+}
+
+void UIExtraDataManager::setVBoxManagerDetailsPaneElementOptions(DetailsElementType enmElementType, const QStringList &options)
+{
+ /* Compose full key from GUI_Details_Elements and enmElementType: */
+ QString strElementType = gpConverter->toInternalString(enmElementType);
+ AssertReturnVoid(!strElementType.isEmpty());
+ strElementType[0] = strElementType.at(0).toUpper();
+ const QString strFullKey = QString("%1/%2").arg(GUI_Details_Elements).arg(strElementType);
+
+ /* Store option list: */
+ setExtraDataStringList(strFullKey, options);
+}
+
+bool UIExtraDataManager::snapshotManagerDetailsExpanded()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_SnapshotManager_Details_Expanded);
+}
+
+void UIExtraDataManager::setSnapshotManagerDetailsExpanded(bool fExpanded)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ return setExtraDataString(GUI_SnapshotManager_Details_Expanded, toFeatureAllowed(fExpanded));
+}
+
+bool UIExtraDataManager::virtualMediaManagerDetailsExpanded()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_VirtualMediaManager_Details_Expanded);
+}
+
+void UIExtraDataManager::setVirtualMediaManagerDetailsExpanded(bool fExpanded)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ return setExtraDataString(GUI_VirtualMediaManager_Details_Expanded, toFeatureAllowed(fExpanded));
+}
+
+bool UIExtraDataManager::virtualMediaManagerSearchWidgetExpanded()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_VirtualMediaManager_Search_Widget_Expanded);
+}
+
+void UIExtraDataManager::setVirtualMediaManagerSearchWidgetExpanded(bool fExpanded)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ return setExtraDataString(GUI_VirtualMediaManager_Search_Widget_Expanded, toFeatureAllowed(fExpanded));
+}
+
+bool UIExtraDataManager::hostNetworkManagerDetailsExpanded()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_HostNetworkManager_Details_Expanded);
+}
+
+void UIExtraDataManager::setHostNetworkManagerDetailsExpanded(bool fExpanded)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ return setExtraDataString(GUI_HostNetworkManager_Details_Expanded, toFeatureAllowed(fExpanded));
+}
+
+QStringList UIExtraDataManager::cloudProfileManagerRestrictions()
+{
+ return extraDataStringList(GUI_CloudProfileManager_Restrictions);
+}
+
+void UIExtraDataManager::setCloudProfileManagerRestrictions(const QStringList &restrictions)
+{
+ return setExtraDataStringList(GUI_CloudProfileManager_Restrictions, restrictions);
+}
+
+bool UIExtraDataManager::cloudProfileManagerDetailsExpanded()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_CloudProfileManager_Details_Expanded);
+}
+
+void UIExtraDataManager::setCloudProfileManagerDetailsExpanded(bool fExpanded)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ return setExtraDataString(GUI_CloudProfileManager_Details_Expanded, toFeatureAllowed(fExpanded));
+}
+
+QStringList UIExtraDataManager::cloudConsoleManagerApplications()
+{
+ /* Gather a list of keys matching required expression: */
+ QStringList result;
+ QRegExp re(QString("^%1/([^/]+)$").arg(GUI_CloudConsoleManager_Application));
+ foreach (const QString &strKey, m_data.value(GlobalID).keys())
+ if (re.indexIn(strKey) != -1)
+ result << re.cap(1);
+ return result;
+}
+
+QStringList UIExtraDataManager::cloudConsoleManagerProfiles(const QString &strId)
+{
+ /* Gather a list of keys matching required expression: */
+ QStringList result;
+ QRegExp re(QString("^%1/%2/([^/]+)$").arg(GUI_CloudConsoleManager_Application, strId));
+ foreach (const QString &strKey, m_data.value(GlobalID).keys())
+ if (re.indexIn(strKey) != -1)
+ result << re.cap(1);
+ return result;
+}
+
+QString UIExtraDataManager::cloudConsoleManagerApplication(const QString &strId)
+{
+ return extraDataString(QString("%1/%2").arg(GUI_CloudConsoleManager_Application, strId));
+}
+
+void UIExtraDataManager::setCloudConsoleManagerApplication(const QString &strId, const QString &strDefinition)
+{
+ setExtraDataString(QString("%1/%2").arg(GUI_CloudConsoleManager_Application, strId), strDefinition);
+}
+
+QString UIExtraDataManager::cloudConsoleManagerProfile(const QString &strApplicationId, const QString &strProfileId)
+{
+ return extraDataString(QString("%1/%2/%3").arg(GUI_CloudConsoleManager_Application, strApplicationId, strProfileId));
+}
+
+void UIExtraDataManager::setCloudConsoleManagerProfile(const QString &strApplicationId, const QString &strProfileId, const QString &strDefinition)
+{
+ setExtraDataString(QString("%1/%2/%3").arg(GUI_CloudConsoleManager_Application, strApplicationId, strProfileId), strDefinition);
+}
+
+QStringList UIExtraDataManager::cloudConsoleManagerRestrictions()
+{
+ return extraDataStringList(GUI_CloudConsoleManager_Restrictions);
+}
+
+void UIExtraDataManager::setCloudConsoleManagerRestrictions(const QStringList &restrictions)
+{
+ return setExtraDataStringList(GUI_CloudConsoleManager_Restrictions, restrictions);
+}
+
+bool UIExtraDataManager::cloudConsoleManagerDetailsExpanded()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_CloudConsoleManager_Details_Expanded);
+}
+
+void UIExtraDataManager::setCloudConsoleManagerDetailsExpanded(bool fExpanded)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ return setExtraDataString(GUI_CloudConsoleManager_Details_Expanded, toFeatureAllowed(fExpanded));
+}
+
+QString UIExtraDataManager::cloudConsolePublicKeyPath()
+{
+ return extraDataString(GUI_CloudConsole_PublicKey_Path);
+}
+
+void UIExtraDataManager::setCloudConsolePublicKeyPath(const QString &strPath)
+{
+ setExtraDataString(GUI_CloudConsole_PublicKey_Path, strPath);
+}
+
+WizardMode UIExtraDataManager::modeForWizardType(WizardType type)
+{
+ /* Otherwise get mode from cached extra-data: */
+ return extraDataStringList(GUI_HideDescriptionForWizards).contains(gpConverter->toInternalString(type))
+ ? WizardMode_Expert : WizardMode_Basic;
+}
+
+void UIExtraDataManager::setModeForWizardType(WizardType type, WizardMode mode)
+{
+ /* Get wizard name: */
+ const QString strWizardName = gpConverter->toInternalString(type);
+ /* Get current value: */
+ const QStringList oldValue = extraDataStringList(GUI_HideDescriptionForWizards);
+ QStringList newValue = oldValue;
+ /* Include wizard-name into expert-mode wizard list if necessary: */
+ if (mode == WizardMode_Expert && !newValue.contains(strWizardName))
+ newValue << strWizardName;
+ /* Exclude wizard-name from expert-mode wizard list if necessary: */
+ else if (mode == WizardMode_Basic && newValue.contains(strWizardName))
+ newValue.removeAll(strWizardName);
+ /* Update extra-data if necessary: */
+ if (newValue != oldValue)
+ setExtraDataStringList(GUI_HideDescriptionForWizards, newValue);
+}
+
+bool UIExtraDataManager::showMachineInVirtualBoxManagerChooser(const QUuid &uID)
+{
+ /* 'True' unless 'restriction' feature allowed: */
+ return !isFeatureAllowed(GUI_HideFromManager, uID);
+}
+
+bool UIExtraDataManager::showMachineInVirtualBoxManagerDetails(const QUuid &uID)
+{
+ /* 'True' unless 'restriction' feature allowed: */
+ return !isFeatureAllowed(GUI_HideDetails, uID);
+}
+
+bool UIExtraDataManager::machineReconfigurationEnabled(const QUuid &uID)
+{
+ /* 'True' unless 'restriction' feature allowed: */
+ return !isFeatureAllowed(GUI_PreventReconfiguration, uID);
+}
+
+bool UIExtraDataManager::machineSnapshotOperationsEnabled(const QUuid &uID)
+{
+ /* 'True' unless 'restriction' feature allowed: */
+ return !isFeatureAllowed(GUI_PreventSnapshotOperations, uID);
+}
+
+QStringList UIExtraDataManager::machineWindowIconNames(const QUuid &uID)
+{
+ return extraDataStringList(GUI_MachineWindowIcons, uID);
+}
+
+#ifndef VBOX_WS_MAC
+QString UIExtraDataManager::machineWindowNamePostfix(const QUuid &uID)
+{
+ return extraDataString(GUI_MachineWindowNamePostfix, uID);
+}
+#endif /* !VBOX_WS_MAC */
+
+QRect UIExtraDataManager::machineWindowGeometry(UIVisualStateType visualStateType, ulong uScreenIndex, const QUuid &uID)
+{
+ /* Choose corresponding key: */
+ QString strKey;
+ switch (visualStateType)
+ {
+ case UIVisualStateType_Normal: strKey = extraDataKeyPerScreen(GUI_LastNormalWindowPosition, uScreenIndex); break;
+ case UIVisualStateType_Scale: strKey = extraDataKeyPerScreen(GUI_LastScaleWindowPosition, uScreenIndex); break;
+ default: AssertFailedReturn(QRect());
+ }
+
+ /* Get corresponding extra-data: */
+ const QStringList data = extraDataStringList(strKey, uID);
+
+ /* Parse loaded data: */
+ int iX = 0, iY = 0, iW = 0, iH = 0;
+ bool fOk = data.size() >= 4;
+ do
+ {
+ if (!fOk) break;
+ iX = data[0].toInt(&fOk);
+ if (!fOk) break;
+ iY = data[1].toInt(&fOk);
+ if (!fOk) break;
+ iW = data[2].toInt(&fOk);
+ if (!fOk) break;
+ iH = data[3].toInt(&fOk);
+ }
+ while (0);
+
+ /* Return geometry (loaded or null): */
+ return fOk ? QRect(iX, iY, iW, iH) : QRect();
+}
+
+bool UIExtraDataManager::machineWindowShouldBeMaximized(UIVisualStateType visualStateType, ulong uScreenIndex, const QUuid &uID)
+{
+ /* Choose corresponding key: */
+ QString strKey;
+ switch (visualStateType)
+ {
+ case UIVisualStateType_Normal: strKey = extraDataKeyPerScreen(GUI_LastNormalWindowPosition, uScreenIndex); break;
+ case UIVisualStateType_Scale: strKey = extraDataKeyPerScreen(GUI_LastScaleWindowPosition, uScreenIndex); break;
+ default: AssertFailedReturn(false);
+ }
+
+ /* Get corresponding extra-data: */
+ const QStringList data = extraDataStringList(strKey, uID);
+
+ /* Make sure 5th item has required value: */
+ return data.size() == 5 && data[4] == GUI_Geometry_State_Max;
+}
+
+void UIExtraDataManager::setMachineWindowGeometry(UIVisualStateType visualStateType, ulong uScreenIndex, const QRect &geometry, bool fMaximized, const QUuid &uID)
+{
+ /* Choose corresponding key: */
+ QString strKey;
+ switch (visualStateType)
+ {
+ case UIVisualStateType_Normal: strKey = extraDataKeyPerScreen(GUI_LastNormalWindowPosition, uScreenIndex); break;
+ case UIVisualStateType_Scale: strKey = extraDataKeyPerScreen(GUI_LastScaleWindowPosition, uScreenIndex); break;
+ default: AssertFailedReturnVoid();
+ }
+
+ /* Serialize passed values: */
+ QStringList data;
+ data << QString::number(geometry.x());
+ data << QString::number(geometry.y());
+ data << QString::number(geometry.width());
+ data << QString::number(geometry.height());
+ if (fMaximized)
+ data << GUI_Geometry_State_Max;
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(strKey, data, uID);
+}
+
+#ifndef VBOX_WS_MAC
+bool UIExtraDataManager::menuBarEnabled(const QUuid &uID)
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_MenuBar_Enabled, uID);
+}
+
+void UIExtraDataManager::setMenuBarEnabled(bool fEnabled, const QUuid &uID)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_MenuBar_Enabled, toFeatureRestricted(!fEnabled), uID);
+}
+#endif /* !VBOX_WS_MAC */
+
+bool UIExtraDataManager::menuBarContextMenuEnabled(const QUuid &uID)
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_MenuBar_ContextMenu_Enabled, uID);
+}
+
+void UIExtraDataManager::setMenuBarContextMenuEnabled(bool fEnabled, const QUuid &uID)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_MenuBar_ContextMenu_Enabled, toFeatureRestricted(!fEnabled), uID);
+}
+
+UIExtraDataMetaDefs::MenuType UIExtraDataManager::restrictedRuntimeMenuTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::MenuType result = UIExtraDataMetaDefs::MenuType_Invalid;
+ /* Get restricted runtime-menu-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedRuntimeMenus, uID))
+ {
+ UIExtraDataMetaDefs::MenuType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::MenuType>(strValue);
+ if (value != UIExtraDataMetaDefs::MenuType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::MenuType>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedRuntimeMenuTypes(UIExtraDataMetaDefs::MenuType types, const QUuid &uID)
+{
+ /* We have MenuType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("MenuType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle MenuType_All enum-value: */
+ if (types == UIExtraDataMetaDefs::MenuType_All)
+ result << gpConverter->toInternalString(types);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::MenuType enumValue =
+ static_cast<UIExtraDataMetaDefs::MenuType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip MenuType_Invalid & MenuType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::MenuType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::MenuType_All)
+ continue;
+ if (types & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedRuntimeMenus, result, uID);
+}
+
+UIExtraDataMetaDefs::MenuApplicationActionType UIExtraDataManager::restrictedRuntimeMenuApplicationActionTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::MenuApplicationActionType result = UIExtraDataMetaDefs::MenuApplicationActionType_Invalid;
+ /* Get restricted runtime-application-menu action-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedRuntimeApplicationMenuActions, uID))
+ {
+ UIExtraDataMetaDefs::MenuApplicationActionType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::MenuApplicationActionType>(strValue);
+ if (value != UIExtraDataMetaDefs::MenuApplicationActionType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::MenuApplicationActionType>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedRuntimeMenuApplicationActionTypes(UIExtraDataMetaDefs::MenuApplicationActionType types, const QUuid &uID)
+{
+ /* We have MenuApplicationActionType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("MenuApplicationActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle MenuApplicationActionType_All enum-value: */
+ if (types == UIExtraDataMetaDefs::MenuApplicationActionType_All)
+ result << gpConverter->toInternalString(types);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::MenuApplicationActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::MenuApplicationActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip MenuApplicationActionType_Invalid & MenuApplicationActionType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::MenuApplicationActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::MenuApplicationActionType_All)
+ continue;
+ if (types & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedRuntimeApplicationMenuActions, result, uID);
+}
+
+UIExtraDataMetaDefs::RuntimeMenuMachineActionType UIExtraDataManager::restrictedRuntimeMenuMachineActionTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::RuntimeMenuMachineActionType result = UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Invalid;
+ /* Get restricted runtime-machine-menu action-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedRuntimeMachineMenuActions, uID))
+ {
+ UIExtraDataMetaDefs::RuntimeMenuMachineActionType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>(strValue);
+ /* Since empty value has default restriction, we are supporting special 'Nothing' value: */
+ if (value == UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Nothing)
+ {
+ result = UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Nothing;
+ break;
+ }
+ if (value != UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>(result | value);
+ }
+ /* Defaults: */
+ if (result == UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Invalid)
+ {
+ result = static_cast<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>(result | UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SaveState);
+ result = static_cast<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>(result | UIExtraDataMetaDefs::RuntimeMenuMachineActionType_PowerOff);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedRuntimeMenuMachineActionTypes(UIExtraDataMetaDefs::RuntimeMenuMachineActionType types, const QUuid &uID)
+{
+ /* We have RuntimeMenuMachineActionType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuMachineActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle RuntimeMenuMachineActionType_All enum-value: */
+ if (types == UIExtraDataMetaDefs::RuntimeMenuMachineActionType_All)
+ result << gpConverter->toInternalString(types);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuMachineActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuMachineActionType_Invalid, RuntimeMenuMachineActionType_Nothing & RuntimeMenuMachineActionType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Nothing ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuMachineActionType_All)
+ continue;
+ if (types & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Since empty value has default restriction, we are supporting special 'Nothing' value: */
+ if (result.isEmpty())
+ result << gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Nothing);
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedRuntimeMachineMenuActions, result, uID);
+}
+
+UIExtraDataMetaDefs::RuntimeMenuViewActionType UIExtraDataManager::restrictedRuntimeMenuViewActionTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType result = UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid;
+ /* Get restricted runtime-view-menu action-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedRuntimeViewMenuActions, uID))
+ {
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::RuntimeMenuViewActionType>(strValue);
+ if (value != UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::RuntimeMenuViewActionType>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedRuntimeMenuViewActionTypes(UIExtraDataMetaDefs::RuntimeMenuViewActionType types, const QUuid &uID)
+{
+ /* We have RuntimeMenuViewActionType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuViewActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle RuntimeMenuViewActionType_All enum-value: */
+ if (types == UIExtraDataMetaDefs::RuntimeMenuViewActionType_All)
+ result << gpConverter->toInternalString(types);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuViewActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuViewActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuViewActionType_Invalid & RuntimeMenuViewActionType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuViewActionType_All)
+ continue;
+ if (types & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedRuntimeViewMenuActions, result, uID);
+}
+
+UIExtraDataMetaDefs::RuntimeMenuInputActionType UIExtraDataManager::restrictedRuntimeMenuInputActionTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::RuntimeMenuInputActionType result = UIExtraDataMetaDefs::RuntimeMenuInputActionType_Invalid;
+ /* Get restricted runtime-machine-menu action-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedRuntimeInputMenuActions, uID))
+ {
+ UIExtraDataMetaDefs::RuntimeMenuInputActionType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::RuntimeMenuInputActionType>(strValue);
+ if (value != UIExtraDataMetaDefs::RuntimeMenuInputActionType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::RuntimeMenuInputActionType>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedRuntimeMenuInputActionTypes(UIExtraDataMetaDefs::RuntimeMenuInputActionType types, const QUuid &uID)
+{
+ /* We have RuntimeMenuInputActionType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuInputActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle RuntimeMenuInputActionType_All enum-value: */
+ if (types == UIExtraDataMetaDefs::RuntimeMenuInputActionType_All)
+ result << gpConverter->toInternalString(types);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuInputActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuInputActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuInputActionType_Invalid & RuntimeMenuInputActionType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuInputActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuInputActionType_All)
+ continue;
+ if (types & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedRuntimeInputMenuActions, result, uID);
+}
+
+UIExtraDataMetaDefs::RuntimeMenuDevicesActionType UIExtraDataManager::restrictedRuntimeMenuDevicesActionTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::RuntimeMenuDevicesActionType result = UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Invalid;
+ /* Get restricted runtime-devices-menu action-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedRuntimeDevicesMenuActions, uID))
+ {
+ UIExtraDataMetaDefs::RuntimeMenuDevicesActionType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>(strValue);
+ /* Since empty value has default restriction, we are supporting special 'Nothing' value: */
+ if (value == UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Nothing)
+ {
+ result = UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Nothing;
+ break;
+ }
+ if (value != UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>(result | value);
+ }
+ /* Defaults: */
+ if (result == UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Invalid)
+ {
+ result = static_cast<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>(result | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrives);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedRuntimeMenuDevicesActionTypes(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType types, const QUuid &uID)
+{
+ /* We have RuntimeMenuDevicesActionType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuDevicesActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle RuntimeMenuDevicesActionType_All enum-value: */
+ if (types == UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_All)
+ result << gpConverter->toInternalString(types);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuDevicesActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuDevicesActionType_Invalid, RuntimeMenuDevicesActionType_Nothing & RuntimeMenuDevicesActionType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Nothing ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_All)
+ continue;
+ if (types & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Since empty value has default restriction, we are supporting special 'Nothing' value: */
+ if (result.isEmpty())
+ result << gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Nothing);
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedRuntimeDevicesMenuActions, result, uID);
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType UIExtraDataManager::restrictedRuntimeMenuDebuggerActionTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType result = UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Invalid;
+ /* Get restricted runtime-debugger-menu action-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedRuntimeDebuggerMenuActions, uID))
+ {
+ UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType>(strValue);
+ if (value != UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedRuntimeMenuDebuggerActionTypes(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType types, const QUuid &uID)
+{
+ /* We have RuntimeMenuDebuggerActionType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuDebuggerActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle RuntimeMenuDebuggerActionType_All enum-value: */
+ if (types == UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_All)
+ result << gpConverter->toInternalString(types);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuDebuggerActionType_Invalid & RuntimeMenuDebuggerActionType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_All)
+ continue;
+ if (types & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedRuntimeDebuggerMenuActions, result, uID);
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+UIExtraDataMetaDefs::MenuWindowActionType UIExtraDataManager::restrictedRuntimeMenuWindowActionTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::MenuWindowActionType result = UIExtraDataMetaDefs::MenuWindowActionType_Invalid;
+ /* Get restricted runtime-window-menu action-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedRuntimeWindowMenuActions, uID))
+ {
+ UIExtraDataMetaDefs::MenuWindowActionType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::MenuWindowActionType>(strValue);
+ if (value != UIExtraDataMetaDefs::MenuWindowActionType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::MenuWindowActionType>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedRuntimeMenuWindowActionTypes(UIExtraDataMetaDefs::MenuWindowActionType types, const QUuid &uID)
+{
+ /* We have MenuWindowActionType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("MenuWindowActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle MenuWindowActionType_All enum-value: */
+ if (types == UIExtraDataMetaDefs::MenuWindowActionType_All)
+ result << gpConverter->toInternalString(types);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::MenuWindowActionType enumValue =
+ static_cast<const UIExtraDataMetaDefs::MenuWindowActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip MenuWindowActionType_Invalid & MenuWindowActionType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::MenuWindowActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::MenuWindowActionType_All)
+ continue;
+ if (types & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedRuntimeWindowMenuActions, result, uID);
+}
+#endif /* VBOX_WS_MAC */
+
+UIExtraDataMetaDefs::MenuHelpActionType UIExtraDataManager::restrictedRuntimeMenuHelpActionTypes(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIExtraDataMetaDefs::MenuHelpActionType result = UIExtraDataMetaDefs::MenuHelpActionType_Invalid;
+ /* Get restricted runtime-help-menu action-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedRuntimeHelpMenuActions, uID))
+ {
+ UIExtraDataMetaDefs::MenuHelpActionType value = gpConverter->fromInternalString<UIExtraDataMetaDefs::MenuHelpActionType>(strValue);
+ if (value != UIExtraDataMetaDefs::MenuHelpActionType_Invalid)
+ result = static_cast<UIExtraDataMetaDefs::MenuHelpActionType>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedRuntimeMenuHelpActionTypes(UIExtraDataMetaDefs::MenuHelpActionType types, const QUuid &uID)
+{
+ /* We have MenuHelpActionType enum registered, so we can enumerate it: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("MenuHelpActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+
+ /* Prepare result: */
+ QStringList result;
+ /* Handle MenuHelpActionType_All enum-value: */
+ if (types == UIExtraDataMetaDefs::MenuHelpActionType_All)
+ result << gpConverter->toInternalString(types);
+ else
+ {
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::MenuHelpActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::MenuHelpActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip MenuHelpActionType_Invalid && MenuHelpActionType_All enum-values: */
+ if (enumValue == UIExtraDataMetaDefs::MenuHelpActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::MenuHelpActionType_All)
+ continue;
+ if (types & enumValue)
+ result << gpConverter->toInternalString(enumValue);
+ }
+ }
+ /* Save result: */
+ setExtraDataStringList(GUI_RestrictedRuntimeHelpMenuActions, result, uID);
+}
+
+UIVisualStateType UIExtraDataManager::restrictedVisualStates(const QUuid &uID)
+{
+ /* Prepare result: */
+ UIVisualStateType result = UIVisualStateType_Invalid;
+ /* Get restricted visual-state-types: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedVisualStates, uID))
+ {
+ UIVisualStateType value = gpConverter->fromInternalString<UIVisualStateType>(strValue);
+ if (value != UIVisualStateType_Invalid)
+ result = static_cast<UIVisualStateType>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+UIVisualStateType UIExtraDataManager::requestedVisualState(const QUuid &uID)
+{
+ if (isFeatureAllowed(GUI_Fullscreen, uID)) return UIVisualStateType_Fullscreen;
+ if (isFeatureAllowed(GUI_Seamless, uID)) return UIVisualStateType_Seamless;
+ if (isFeatureAllowed(GUI_Scale, uID)) return UIVisualStateType_Scale;
+ return UIVisualStateType_Normal;
+}
+
+void UIExtraDataManager::setRequestedVisualState(UIVisualStateType visualState, const QUuid &uID)
+{
+ setExtraDataString(GUI_Fullscreen, toFeatureAllowed(visualState == UIVisualStateType_Fullscreen), uID);
+ setExtraDataString(GUI_Seamless, toFeatureAllowed(visualState == UIVisualStateType_Seamless), uID);
+ setExtraDataString(GUI_Scale, toFeatureAllowed(visualState == UIVisualStateType_Scale), uID);
+}
+
+#ifdef VBOX_WS_X11
+bool UIExtraDataManager::legacyFullscreenModeRequested()
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_Fullscreen_LegacyMode);
+}
+
+bool UIExtraDataManager::distinguishMachineWindowGroups(const QUuid &uID)
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_DistinguishMachineWindowGroups, uID);
+}
+
+void UIExtraDataManager::setDistinguishMachineWindowGroups(const QUuid &uID, bool fEnabled)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ setExtraDataString(GUI_DistinguishMachineWindowGroups, toFeatureAllowed(fEnabled), uID);
+}
+#endif /* VBOX_WS_X11 */
+
+bool UIExtraDataManager::guestScreenAutoResizeEnabled(const QUuid &uID)
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_AutoresizeGuest, uID);
+}
+
+void UIExtraDataManager::setGuestScreenAutoResizeEnabled(bool fEnabled, const QUuid &uID)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_AutoresizeGuest, toFeatureRestricted(!fEnabled), uID);
+}
+
+bool UIExtraDataManager::lastGuestScreenVisibilityStatus(ulong uScreenIndex, const QUuid &uID)
+{
+ /* Not for primary screen: */
+ if (uScreenIndex == 0)
+ return true;
+
+ /* Compose corresponding key: */
+ const QString strKey = extraDataKeyPerScreen(GUI_LastVisibilityStatusForGuestScreen, uScreenIndex);
+
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(strKey, uID);
+}
+
+void UIExtraDataManager::setLastGuestScreenVisibilityStatus(ulong uScreenIndex, bool fEnabled, const QUuid &uID)
+{
+ /* Not for primary screen: */
+ if (uScreenIndex == 0)
+ return;
+
+ /* Compose corresponding key: */
+ const QString strKey = extraDataKeyPerScreen(GUI_LastVisibilityStatusForGuestScreen, uScreenIndex);
+
+ /* 'True' if feature allowed, null-string otherwise: */
+ return setExtraDataString(strKey, toFeatureAllowed(fEnabled), uID);
+}
+
+QSize UIExtraDataManager::lastGuestScreenSizeHint(ulong uScreenIndex, const QUuid &uID)
+{
+ /* Choose corresponding key: */
+ const QString strKey = extraDataKeyPerScreen(GUI_LastGuestSizeHint, uScreenIndex);
+
+ /* Get corresponding extra-data: */
+ const QStringList data = extraDataStringList(strKey, uID);
+
+ /* Parse loaded data: */
+ int iW = 0, iH = 0;
+ bool fOk = data.size() == 2;
+ do
+ {
+ if (!fOk) break;
+ iW = data[0].toInt(&fOk);
+ if (!fOk) break;
+ iH = data[1].toInt(&fOk);
+ }
+ while (0);
+
+ /* Return size (loaded or invalid): */
+ return fOk ? QSize(iW, iH) : QSize();
+}
+
+void UIExtraDataManager::setLastGuestScreenSizeHint(ulong uScreenIndex, const QSize &sizeHint, const QUuid &uID)
+{
+ /* Choose corresponding key: */
+ const QString strKey = extraDataKeyPerScreen(GUI_LastGuestSizeHint, uScreenIndex);
+
+ /* Serialize passed values: */
+ QStringList data;
+ data << QString::number(sizeHint.width());
+ data << QString::number(sizeHint.height());
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(strKey, data, uID);
+}
+
+int UIExtraDataManager::hostScreenForPassedGuestScreen(int iGuestScreenIndex, const QUuid &uID)
+{
+ /* Choose corresponding key: */
+ const QString strKey = extraDataKeyPerScreen(GUI_VirtualScreenToHostScreen, iGuestScreenIndex, true);
+
+ /* Get value and convert it to index: */
+ const QString strValue = extraDataString(strKey, uID);
+ bool fOk = false;
+ const int iHostScreenIndex = strValue.toULong(&fOk);
+
+ /* Return corresponding index: */
+ return fOk ? iHostScreenIndex : -1;
+}
+
+void UIExtraDataManager::setHostScreenForPassedGuestScreen(int iGuestScreenIndex, int iHostScreenIndex, const QUuid &uID)
+{
+ /* Choose corresponding key: */
+ const QString strKey = extraDataKeyPerScreen(GUI_VirtualScreenToHostScreen, iGuestScreenIndex, true);
+
+ /* Save passed index under corresponding value: */
+ setExtraDataString(strKey, iHostScreenIndex != -1 ? QString::number(iHostScreenIndex) : QString(), uID);
+}
+
+bool UIExtraDataManager::autoMountGuestScreensEnabled(const QUuid &uID)
+{
+ /* Show only if 'allowed' flag is set: */
+ return isFeatureAllowed(GUI_AutomountGuestScreens, uID);
+}
+
+#ifndef VBOX_WS_MAC
+bool UIExtraDataManager::miniToolbarEnabled(const QUuid &uID)
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_ShowMiniToolBar, uID);
+}
+
+void UIExtraDataManager::setMiniToolbarEnabled(bool fEnabled, const QUuid &uID)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_ShowMiniToolBar, toFeatureRestricted(!fEnabled), uID);
+}
+
+bool UIExtraDataManager::autoHideMiniToolbar(const QUuid &uID)
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_MiniToolBarAutoHide, uID);
+}
+
+void UIExtraDataManager::setAutoHideMiniToolbar(bool fAutoHide, const QUuid &uID)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_MiniToolBarAutoHide, toFeatureRestricted(!fAutoHide), uID);
+}
+
+Qt::AlignmentFlag UIExtraDataManager::miniToolbarAlignment(const QUuid &uID)
+{
+ /* Return Qt::AlignBottom unless MiniToolbarAlignment_Top specified separately: */
+ switch (gpConverter->fromInternalString<MiniToolbarAlignment>(extraDataString(GUI_MiniToolBarAlignment, uID)))
+ {
+ case MiniToolbarAlignment_Top: return Qt::AlignTop;
+ default: break;
+ }
+ return Qt::AlignBottom;
+}
+
+void UIExtraDataManager::setMiniToolbarAlignment(Qt::AlignmentFlag alignment, const QUuid &uID)
+{
+ /* Remove record unless Qt::AlignTop specified separately: */
+ switch (alignment)
+ {
+ case Qt::AlignTop: setExtraDataString(GUI_MiniToolBarAlignment, gpConverter->toInternalString(MiniToolbarAlignment_Top), uID); return;
+ default: break;
+ }
+ setExtraDataString(GUI_MiniToolBarAlignment, QString(), uID);
+}
+#endif /* VBOX_WS_MAC */
+
+bool UIExtraDataManager::statusBarEnabled(const QUuid &uID)
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_StatusBar_Enabled, uID);
+}
+
+void UIExtraDataManager::setStatusBarEnabled(bool fEnabled, const QUuid &uID)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_StatusBar_Enabled, toFeatureRestricted(!fEnabled), uID);
+}
+
+bool UIExtraDataManager::statusBarContextMenuEnabled(const QUuid &uID)
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_StatusBar_ContextMenu_Enabled, uID);
+}
+
+void UIExtraDataManager::setStatusBarContextMenuEnabled(bool fEnabled, const QUuid &uID)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_StatusBar_ContextMenu_Enabled, toFeatureRestricted(!fEnabled), uID);
+}
+
+QList<IndicatorType> UIExtraDataManager::restrictedStatusBarIndicators(const QUuid &uID)
+{
+ /* Prepare result: */
+ QList<IndicatorType> result;
+ /* Get restricted status-bar indicators: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedStatusBarIndicators, uID))
+ {
+ const IndicatorType value = gpConverter->fromInternalString<IndicatorType>(strValue);
+ if (value != IndicatorType_Invalid && !result.contains(value))
+ result << value;
+ }
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setRestrictedStatusBarIndicators(const QList<IndicatorType> &list, const QUuid &uID)
+{
+ /* Parse passed list: */
+ QStringList data;
+ foreach (const IndicatorType &indicatorType, list)
+ data << gpConverter->toInternalString(indicatorType);
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_RestrictedStatusBarIndicators, data, uID);
+}
+
+QList<IndicatorType> UIExtraDataManager::statusBarIndicatorOrder(const QUuid &uID)
+{
+ /* Prepare result: */
+ QList<IndicatorType> result;
+ /* Get status-bar indicator order: */
+ foreach (const QString &strValue, extraDataStringList(GUI_StatusBar_IndicatorOrder, uID))
+ {
+ const IndicatorType value = gpConverter->fromInternalString<IndicatorType>(strValue);
+ if (value != IndicatorType_Invalid && !result.contains(value))
+ result << value;
+ }
+
+ /* We should update the list with missing indicators: */
+ for (int i = (int)IndicatorType_Invalid; i < (int)IndicatorType_Max; ++i)
+ {
+ /* Skip the IndicatorType_Invalid (we used it as start of this loop): */
+ if (i == (int)IndicatorType_Invalid)
+ continue;
+ /* Skip the IndicatorType_KeyboardExtension (special handling): */
+ if (i == (int)IndicatorType_KeyboardExtension)
+ continue;
+
+ /* Get the current one: */
+ const IndicatorType enmCurrent = (IndicatorType)i;
+
+ /* Skip the current one if it's present: */
+ if (result.contains(enmCurrent))
+ continue;
+
+ /* Let's find the first of those which stays before it and is not missing: */
+ IndicatorType enmPrevious = (IndicatorType)(enmCurrent - 1);
+ while (enmPrevious != IndicatorType_Invalid && !result.contains(enmPrevious))
+ enmPrevious = (IndicatorType)(enmPrevious - 1);
+
+ /* Calculate position to insert missing one: */
+ const int iInsertPosition = enmPrevious != IndicatorType_Invalid
+ ? result.indexOf(enmPrevious) + 1
+ : 0;
+
+ /* Finally insert missing indicator at required position: */
+ result.insert(iInsertPosition, enmCurrent);
+ }
+
+ /* Return result: */
+ return result;
+}
+
+void UIExtraDataManager::setStatusBarIndicatorOrder(const QList<IndicatorType> &list, const QUuid &uID)
+{
+ /* Parse passed list: */
+ QStringList data;
+ foreach (const IndicatorType &indicatorType, list)
+ data << gpConverter->toInternalString(indicatorType);
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_StatusBar_IndicatorOrder, data, uID);
+}
+
+#ifdef VBOX_WS_MAC
+bool UIExtraDataManager::realtimeDockIconUpdateEnabled(const QUuid &uID)
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_RealtimeDockIconUpdateEnabled, uID);
+}
+
+void UIExtraDataManager::setRealtimeDockIconUpdateEnabled(bool fEnabled, const QUuid &uID)
+{
+ /* 'False' if feature restricted, null-string otherwise: */
+ setExtraDataString(GUI_RealtimeDockIconUpdateEnabled, toFeatureRestricted(!fEnabled), uID);
+}
+
+int UIExtraDataManager::realtimeDockIconUpdateMonitor(const QUuid &uID)
+{
+ return extraDataString(GUI_RealtimeDockIconUpdateMonitor, uID).toInt();
+}
+
+void UIExtraDataManager::setRealtimeDockIconUpdateMonitor(int iIndex, const QUuid &uID)
+{
+ setExtraDataString(GUI_RealtimeDockIconUpdateMonitor, iIndex ? QString::number(iIndex) : QString(), uID);
+}
+
+bool UIExtraDataManager::dockIconDisableOverlay(const QUuid &uID)
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_DockIconDisableOverlay, uID);
+}
+
+void UIExtraDataManager::setDockIconDisableOverlay(bool fDisabled, const QUuid &uID)
+{
+ /* 'True' if feature allowed, null-string otherwise: */
+ setExtraDataString(GUI_DockIconDisableOverlay, toFeatureAllowed(fDisabled), uID);
+}
+#endif /* VBOX_WS_MAC */
+
+bool UIExtraDataManager::passCADtoGuest(const QUuid &uID)
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_PassCAD, uID);
+}
+
+MouseCapturePolicy UIExtraDataManager::mouseCapturePolicy(const QUuid &uID)
+{
+ return gpConverter->fromInternalString<MouseCapturePolicy>(extraDataString(GUI_MouseCapturePolicy, uID));
+}
+
+GuruMeditationHandlerType UIExtraDataManager::guruMeditationHandlerType(const QUuid &uID)
+{
+ return gpConverter->fromInternalString<GuruMeditationHandlerType>(extraDataString(GUI_GuruMeditationHandler, uID));
+}
+
+bool UIExtraDataManager::hidLedsSyncState(const QUuid &uID)
+{
+ /* 'True' unless feature restricted: */
+ return !isFeatureRestricted(GUI_HidLedsSync, uID);
+}
+
+double UIExtraDataManager::scaleFactor(const QUuid &uID, const int uScreenIndex)
+{
+ /* Get corresponding extra-data for this machine: */
+ QStringList data = extraDataStringList(GUI_ScaleFactor, uID);
+
+ /* 1.0 is default scale factor: */
+ if (data.size() == 0)
+ return 1.0;
+
+ int index = uScreenIndex;
+ /* use the 0th. scale factor in case we dont have a scale factor for @p uScreenIndex: */
+ if (data.size() <= uScreenIndex)
+ index = 0;
+
+ bool fOk = false;
+ double scaleFactor = data[index].toDouble(&fOk);
+ if (!fOk)
+ return 1.0;
+ return scaleFactor;
+}
+
+QList<double> UIExtraDataManager::scaleFactors(const QUuid &uID)
+{
+ /* Look for the scale factor for this machine first: */
+ QStringList data = extraDataStringList(GUI_ScaleFactor, uID);
+
+ QList<double> scaleFactorList;
+ /* 1.0 is default scale factor: */
+ if (data.size() == 0)
+ {
+ scaleFactorList.append(1.0);
+ return scaleFactorList;
+ }
+
+ bool fOk = false;
+ double scaleFactor;
+ for (int i = 0; i < data.size(); ++i)
+ {
+ scaleFactor = data[i].toDouble(&fOk);
+ if (!fOk)
+ scaleFactor = 1.0;
+ scaleFactorList.append(scaleFactor);
+ }
+ return scaleFactorList;
+}
+
+void UIExtraDataManager::setScaleFactor(double dScaleFactor, const QUuid &uID, const int uScreenIndex)
+{
+ QStringList data = extraDataStringList(GUI_ScaleFactor, uID);
+
+ /* Just make sure that we have corresponding data item: */
+ if (data.size() <= uScreenIndex)
+ {
+ int listSize = data.size();
+ for (int i = listSize; i <= uScreenIndex; ++i)
+ data.append(QString::number(1.0));
+ }
+
+ data[uScreenIndex] = QString::number(dScaleFactor);
+ setExtraDataStringList(GUI_ScaleFactor, data, uID);
+}
+
+void UIExtraDataManager::setScaleFactors(const QList<double> &scaleFactors, const QUuid &uID)
+{
+ QStringList data;
+ for (int i = 0; i < scaleFactors.size(); ++i)
+ data.append(QString::number(scaleFactors[i]));
+ setExtraDataStringList(GUI_ScaleFactor, data, uID);
+}
+
+ScalingOptimizationType UIExtraDataManager::scalingOptimizationType(const QUuid &uID)
+{
+ return gpConverter->fromInternalString<ScalingOptimizationType>(extraDataString(GUI_Scaling_Optimization, uID));
+}
+
+QRect UIExtraDataManager::sessionInformationDialogGeometry(QWidget *pWidget, QWidget *pParentWidget)
+{
+ return dialogGeometry(GUI_SessionInformationDialogGeometry, pWidget, pParentWidget);
+}
+
+bool UIExtraDataManager::sessionInformationDialogShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_SessionInformationDialogGeometry);
+}
+
+void UIExtraDataManager::setSessionInformationDialogGeometry(const QRect &geometry, bool fMaximized)
+{
+ /* Serialize passed values: */
+ QStringList data;
+ data << QString::number(geometry.x());
+ data << QString::number(geometry.y());
+ data << QString::number(geometry.width());
+ data << QString::number(geometry.height());
+ if (fMaximized)
+ data << GUI_Geometry_State_Max;
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_SessionInformationDialogGeometry, data);
+}
+
+
+void UIExtraDataManager::setGuestControlProcessControlSplitterHints(const QList<int> &hints)
+{
+ QStringList data;
+ data << (hints.size() > 0 ? QString::number(hints[0]) : QString());
+ data << (hints.size() > 1 ? QString::number(hints[1]) : QString());
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_GuestControl_ProcessControlSplitterHints, data);
+}
+
+QList<int> UIExtraDataManager::guestControlProcessControlSplitterHints()
+{
+ /* Get corresponding extra-data: */
+ const QStringList data = extraDataStringList(GUI_GuestControl_ProcessControlSplitterHints);
+
+ /* Parse loaded data: */
+ QList<int> hints;
+ hints << (data.size() > 0 ? data[0].toInt() : 0);
+ hints << (data.size() > 1 ? data[1].toInt() : 0);
+
+ /* Return hints: */
+ return hints;
+}
+
+QRect UIExtraDataManager::fileManagerDialogGeometry(QWidget *pWidget, QWidget *pParentWidget)
+{
+ return dialogGeometry(GUI_GuestControl_FileManagerDialogGeometry, pWidget, pParentWidget);
+}
+
+bool UIExtraDataManager::fileManagerDialogShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_GuestControl_FileManagerDialogGeometry);
+}
+
+void UIExtraDataManager::setFileManagerDialogGeometry(const QRect &geometry, bool fMaximized)
+{
+ setDialogGeometry(GUI_GuestControl_FileManagerDialogGeometry, geometry, fMaximized);
+}
+
+void UIExtraDataManager::setFileManagerVisiblePanels(const QStringList &panelNameList)
+{
+ setExtraDataStringList(GUI_GuestControl_FileManagerVisiblePanels, panelNameList);
+}
+
+QStringList UIExtraDataManager::fileManagerVisiblePanels()
+{
+ return extraDataStringList(GUI_GuestControl_FileManagerVisiblePanels);
+}
+
+QRect UIExtraDataManager::softKeyboardDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry)
+{
+ return dialogGeometry(GUI_SoftKeyboard_DialogGeometry, pWidget, pParentWidget, defaultGeometry);
+}
+
+void UIExtraDataManager::setSoftKeyboardDialogGeometry(const QRect &geometry, bool fMaximized)
+{
+ setDialogGeometry(GUI_SoftKeyboard_DialogGeometry, geometry, fMaximized);
+}
+
+bool UIExtraDataManager::softKeyboardDialogShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_SoftKeyboard_DialogGeometry);
+}
+
+void UIExtraDataManager::setSoftKeyboardOptions(bool fHideNumPad, bool fHideOSMenuKeys, bool fMultimediaKeys)
+{
+ QStringList data;
+
+ if (fHideNumPad)
+ data << GUI_SoftKeyboard_HideNumPad;
+ if (fHideOSMenuKeys)
+ data << GUI_SoftKeyboard_HideOSMenuKeys;
+ if (fMultimediaKeys)
+ data << GUI_SoftKeyboard_HideMultimediaKeys;
+
+ setExtraDataStringList(GUI_SoftKeyboard_Options, data);
+}
+
+void UIExtraDataManager::softKeyboardOptions(bool &fOutHideNumPad, bool &fOutHideOSMenuKeys, bool &fOutHideMultimediaKeys)
+{
+ fOutHideNumPad = false;
+ fOutHideOSMenuKeys = false;
+ const QStringList data = extraDataStringList(GUI_SoftKeyboard_Options);
+ for (int i = 0; i < data.size(); ++i)
+ {
+ if (data[i] == GUI_SoftKeyboard_HideNumPad)
+ fOutHideNumPad = true;
+ if (data[i] == GUI_SoftKeyboard_HideOSMenuKeys)
+ fOutHideOSMenuKeys = true;
+ if (data[i] == GUI_SoftKeyboard_HideMultimediaKeys)
+ fOutHideMultimediaKeys = true;
+ }
+}
+
+void UIExtraDataManager::setSoftKeyboardColorTheme(const QStringList &colorStringList)
+{
+ setExtraDataStringList(GUI_SoftKeyboard_ColorTheme, colorStringList);
+}
+
+QStringList UIExtraDataManager::softKeyboardColorTheme()
+{
+ return extraDataStringList(GUI_SoftKeyboard_ColorTheme);
+}
+
+void UIExtraDataManager::setSoftKeyboardSelectedColorTheme(const QString &strColorThemeName)
+{
+ setExtraDataString(GUI_SoftKeyboard_SelectedColorTheme, strColorThemeName);
+}
+
+QString UIExtraDataManager::softKeyboardSelectedColorTheme()
+{
+ return extraDataString(GUI_SoftKeyboard_SelectedColorTheme);
+}
+
+void UIExtraDataManager::setSoftKeyboardSelectedLayout(const QUuid &uLayoutUid)
+{
+ setExtraDataString(GUI_SoftKeyboard_SelectedLayout, uLayoutUid.toString());
+}
+
+QUuid UIExtraDataManager::softKeyboardSelectedLayout()
+{
+ return QUuid(extraDataString(GUI_SoftKeyboard_SelectedLayout));
+}
+
+void UIExtraDataManager::setFileManagerOptions(bool fListDirectoriesFirst,
+ bool fShowDeleteConfirmation,
+ bool fShowHumanReadableSizes,
+ bool fShowHiddenObjects)
+{
+ /* Serialize passed values: */
+ QStringList data;
+
+ if (fListDirectoriesFirst)
+ data << GUI_GuestControl_FileManagerListDirectoriesFirst;
+ if (fShowDeleteConfirmation)
+ data << GUI_GuestControl_FileManagerShowDeleteConfirmation;
+ if (fShowHumanReadableSizes)
+ data << GUI_GuestControl_FileManagerShowHumanReadableSizes;
+ if (fShowHiddenObjects)
+ data << GUI_GuestControl_FileManagerShowHiddenObjects;
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_GuestControl_FileManagerOptions, data);
+}
+
+bool UIExtraDataManager::fileManagerListDirectoriesFirst()
+{
+ const QStringList data = extraDataStringList(GUI_GuestControl_FileManagerOptions);
+ for (int i = 0; i < data.size(); ++i)
+ {
+ if (data[i] == GUI_GuestControl_FileManagerListDirectoriesFirst)
+ return true;
+ }
+ return false;
+}
+
+bool UIExtraDataManager::fileManagerShowDeleteConfirmation()
+{
+ const QStringList data = extraDataStringList(GUI_GuestControl_FileManagerOptions);
+ for (int i = 0; i < data.size(); ++i)
+ {
+ if (data[i] == GUI_GuestControl_FileManagerShowDeleteConfirmation)
+ return true;
+ }
+ return false;
+}
+
+bool UIExtraDataManager::fileManagerShowHumanReadableSizes()
+{
+ const QStringList data = extraDataStringList(GUI_GuestControl_FileManagerOptions);
+ for (int i = 0; i < data.size(); ++i)
+ {
+ if (data[i] == GUI_GuestControl_FileManagerShowHumanReadableSizes)
+ return true;
+ }
+ return false;
+}
+
+bool UIExtraDataManager::fileManagerShowHiddenObjects()
+{
+ const QStringList data = extraDataStringList(GUI_GuestControl_FileManagerOptions);
+ for (int i = 0; i < data.size(); ++i)
+ {
+ if (data[i] == GUI_GuestControl_FileManagerShowHiddenObjects)
+ return true;
+ }
+ return false;
+}
+
+QRect UIExtraDataManager::guestProcessControlDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry)
+{
+ return dialogGeometry(GUI_GuestControl_ProcessControlDialogGeometry, pWidget, pParentWidget, defaultGeometry);
+}
+
+bool UIExtraDataManager::guestProcessControlDialogShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_GuestControl_ProcessControlDialogGeometry);
+}
+
+void UIExtraDataManager::setGuestProcessControlDialogGeometry(const QRect &geometry, bool fMaximized)
+{
+ setDialogGeometry(GUI_GuestControl_ProcessControlDialogGeometry, geometry, fMaximized);
+}
+
+MachineCloseAction UIExtraDataManager::defaultMachineCloseAction(const QUuid &uID)
+{
+ return gpConverter->fromInternalString<MachineCloseAction>(extraDataString(GUI_DefaultCloseAction, uID));
+}
+
+MachineCloseAction UIExtraDataManager::restrictedMachineCloseActions(const QUuid &uID)
+{
+ /* Prepare result: */
+ MachineCloseAction result = MachineCloseAction_Invalid;
+ /* Get restricted machine-close-actions: */
+ foreach (const QString &strValue, extraDataStringList(GUI_RestrictedCloseActions, uID))
+ {
+ MachineCloseAction value = gpConverter->fromInternalString<MachineCloseAction>(strValue);
+ if (value != MachineCloseAction_Invalid)
+ result = static_cast<MachineCloseAction>(result | value);
+ }
+ /* Return result: */
+ return result;
+}
+
+MachineCloseAction UIExtraDataManager::lastMachineCloseAction(const QUuid &uID)
+{
+ return gpConverter->fromInternalString<MachineCloseAction>(extraDataString(GUI_LastCloseAction, uID));
+}
+
+void UIExtraDataManager::setLastMachineCloseAction(MachineCloseAction machineCloseAction, const QUuid &uID)
+{
+ setExtraDataString(GUI_LastCloseAction, gpConverter->toInternalString(machineCloseAction), uID);
+}
+
+QString UIExtraDataManager::machineCloseHookScript(const QUuid &uID)
+{
+ return extraDataString(GUI_CloseActionHook, uID);
+}
+
+bool UIExtraDataManager::discardStateOnPowerOff(const QUuid &uID)
+{
+ /* 'False' unless feature allowed: */
+ return isFeatureAllowed(GUI_DiscardStateOnPowerOff, uID);
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+QString UIExtraDataManager::debugFlagValue(const QString &strDebugFlagKey)
+{
+ return extraDataString(strDebugFlagKey).toLower().trimmed();
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+QRect UIExtraDataManager::extraDataManagerGeometry(QWidget *pWidget, QWidget *pParentWidget)
+{
+ return dialogGeometry(GUI_ExtraDataManager_Geometry, pWidget, pParentWidget);
+}
+
+bool UIExtraDataManager::extraDataManagerShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_ExtraDataManager_Geometry);
+}
+
+void UIExtraDataManager::setExtraDataManagerGeometry(const QRect &geometry, bool fMaximized)
+{
+ /* Serialize passed values: */
+ QStringList data;
+ data << QString::number(geometry.x());
+ data << QString::number(geometry.y());
+ data << QString::number(geometry.width());
+ data << QString::number(geometry.height());
+ if (fMaximized)
+ data << GUI_Geometry_State_Max;
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_ExtraDataManager_Geometry, data);
+}
+
+QList<int> UIExtraDataManager::extraDataManagerSplitterHints(QWidget *pWidget)
+{
+ /* Get corresponding extra-data: */
+ const QStringList data = extraDataStringList(GUI_ExtraDataManager_SplitterHints);
+
+ /* Parse loaded data: */
+ int iLeft = 0, iRight = 0;
+ bool fOk = data.size() == 2;
+ do
+ {
+ if (!fOk) break;
+ iLeft = data[0].toInt(&fOk);
+ if (!fOk) break;
+ iRight = data[1].toInt(&fOk);
+ }
+ while (0);
+
+ /* Prepare hints (loaded or adviced): */
+ QList<int> hints;
+ if (fOk)
+ {
+ hints << iLeft;
+ hints << iRight;
+ }
+ else
+ {
+ hints << (int)(pWidget->width() * .9 * (1.0 / 3));
+ hints << (int)(pWidget->width() * .9 * (2.0 / 3));
+ }
+
+ /* Return hints: */
+ return hints;
+}
+
+void UIExtraDataManager::setExtraDataManagerSplitterHints(const QList<int> &hints)
+{
+ /* Parse passed hints: */
+ QStringList data;
+ data << (hints.size() > 0 ? QString::number(hints[0]) : QString());
+ data << (hints.size() > 1 ? QString::number(hints[1]) : QString());
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_ExtraDataManager_SplitterHints, data);
+}
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+QRect UIExtraDataManager::logWindowGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry)
+{
+ return dialogGeometry(GUI_LogWindowGeometry, pWidget, pParentWidget, defaultGeometry);
+}
+
+bool UIExtraDataManager::logWindowShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_LogWindowGeometry);
+}
+
+void UIExtraDataManager::setLogWindowGeometry(const QRect &geometry, bool fMaximized)
+{
+ /* Serialize passed values: */
+ QStringList data;
+ data << QString::number(geometry.x());
+ data << QString::number(geometry.y());
+ data << QString::number(geometry.width());
+ data << QString::number(geometry.height());
+ if (fMaximized)
+ data << GUI_Geometry_State_Max;
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_LogWindowGeometry, data);
+}
+
+void UIExtraDataManager::setLogViweverOptions(const QFont &font, bool wrapLines, bool showLineNumbers)
+{
+ /* Serialize passed values: */
+ QStringList data;
+ data << font.family();
+ /* Make sure that we have some non-empty string as font style name: */
+ QString strStyleName = font.styleName();
+ if (strStyleName.isEmpty())
+ data << GUI_LogViewerNoFontStyleName;
+ else
+ data << font.styleName();
+ data << QString::number(font.pointSize());
+
+ if (wrapLines)
+ data << GUI_LogViewerWrapLinesEnabled;
+ if (!showLineNumbers)
+ data << GUI_LogViewerShowLineNumbersDisabled;
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_LogViewerOptions, data);
+}
+
+bool UIExtraDataManager::logViewerWrapLines()
+{
+ const QStringList data = extraDataStringList(GUI_LogViewerOptions);
+ for (int i = 0; i < data.size(); ++i)
+ {
+ if (data[i] == GUI_LogViewerWrapLinesEnabled)
+ return true;
+ }
+ return false;
+}
+
+bool UIExtraDataManager::logViewerShowLineNumbers()
+{
+ const QStringList data = extraDataStringList(GUI_LogViewerOptions);
+ for (int i = 0; i < data.size(); ++i)
+ {
+ if (data[i] == GUI_LogViewerShowLineNumbersDisabled)
+ return false;
+ }
+ return true;
+}
+
+QFont UIExtraDataManager::logViewerFont()
+{
+ const QStringList data = extraDataStringList(GUI_LogViewerOptions);
+ if (data.size() < 3)
+ return QFont();
+ QString strFamily = data[0];
+ QString strStyleName = data[1];
+ if (strStyleName == GUI_LogViewerNoFontStyleName)
+ strStyleName.clear();
+ bool fOk = false;
+ int iFontSize = data[2].toInt(&fOk);
+ if (!fOk)
+ iFontSize = 9;
+ QFontDatabase dataBase;
+ return dataBase.font(strFamily, strStyleName, iFontSize);
+}
+
+void UIExtraDataManager::setLogViewerVisiblePanels(const QStringList &panelNameList)
+{
+ setExtraDataStringList(GUI_GuestControl_LogViewerVisiblePanels, panelNameList);
+}
+
+QStringList UIExtraDataManager::logViewerVisiblePanels()
+{
+ return extraDataStringList(GUI_GuestControl_LogViewerVisiblePanels);
+}
+
+void UIExtraDataManager::setHelpBrowserLastUrlList(const QStringList &urlList)
+{
+ setExtraDataStringList(GUI_HelpBrowser_LastURLList, urlList);
+}
+
+QStringList UIExtraDataManager::helpBrowserLastUrlList()
+{
+ return extraDataStringList(GUI_HelpBrowser_LastURLList);
+}
+
+void UIExtraDataManager::setHelpBrowserZoomPercentage(int iZoomPercentage)
+{
+ setExtraDataString(GUI_HelpBrowser_ZoomPercentage, QString::number(iZoomPercentage));
+}
+
+int UIExtraDataManager::helpBrowserZoomPercentage()
+{
+ return extraDataString(GUI_HelpBrowser_ZoomPercentage).toInt();
+}
+
+QRect UIExtraDataManager::helpBrowserDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry)
+{
+ return dialogGeometry(GUI_HelpBrowser_DialogGeometry, pWidget, pParentWidget, defaultGeometry);
+}
+
+void UIExtraDataManager::setHelpBrowserDialogGeometry(const QRect &geometry, bool fMaximized)
+{
+ /* Serialize passed values: */
+ QStringList data;
+ data << QString::number(geometry.x());
+ data << QString::number(geometry.y());
+ data << QString::number(geometry.width());
+ data << QString::number(geometry.height());
+ if (fMaximized)
+ data << GUI_Geometry_State_Max;
+
+ /* Re-cache corresponding extra-data: */
+ setExtraDataStringList(GUI_HelpBrowser_DialogGeometry, data);
+}
+
+bool UIExtraDataManager::helpBrowserDialogShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_HelpBrowser_DialogGeometry);
+}
+
+void UIExtraDataManager::setHelpBrowserBookmarks(const QStringList &bookmarks)
+{
+ setExtraDataStringList(GUI_HelpBrowser_Bookmarks, bookmarks);
+}
+
+QStringList UIExtraDataManager::helpBrowserBookmarks()
+{
+ return extraDataStringList(GUI_HelpBrowser_Bookmarks);
+}
+
+void UIExtraDataManager::setVMActivityOverviewHiddenColumnList(const QStringList &hiddenColumnList)
+{
+ setExtraDataStringList(GUI_VMActivityOverview_HiddenColumns, hiddenColumnList);
+}
+
+QStringList UIExtraDataManager::VMActivityOverviewHiddenColumnList()
+{
+ return extraDataStringList(GUI_VMActivityOverview_HiddenColumns);
+}
+
+bool UIExtraDataManager::VMActivityOverviewShowAllMachines()
+{
+ return isFeatureAllowed(GUI_VMActivityOverview_ShowAllMachines);
+}
+
+void UIExtraDataManager::setVMActivityOverviewShowAllMachines(bool fShow)
+{
+ setExtraDataString(GUI_VMActivityOverview_ShowAllMachines, toFeatureAllowed(fShow));
+}
+
+QRect UIExtraDataManager::mediumSelectorDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry)
+{
+ return dialogGeometry(GUI_MediumSelector_DialogGeometry, pWidget, pParentWidget, defaultGeometry);
+}
+
+void UIExtraDataManager::setMediumSelectorDialogGeometry(const QRect &geometry, bool fMaximized)
+{
+ setDialogGeometry(GUI_MediumSelector_DialogGeometry, geometry, fMaximized);
+}
+
+bool UIExtraDataManager::mediumSelectorDialogShouldBeMaximized()
+{
+ return dialogShouldBeMaximized(GUI_MediumSelector_DialogGeometry);
+}
+
+void UIExtraDataManager::sltExtraDataChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue)
+{
+ /* Re-cache value only if uMachineID known already: */
+ if (m_data.contains(uMachineID))
+ {
+ if (!strValue.isEmpty())
+ m_data[uMachineID][strKey] = strValue;
+ else
+ m_data[uMachineID].remove(strKey);
+ }
+
+ /* Global extra-data 'change' event: */
+ if (uMachineID == GlobalID)
+ {
+ if (strKey.startsWith("GUI/"))
+ {
+ /* Notification-center alignment? */
+ if (strKey == GUI_NotificationCenter_Alignment)
+ emit sigNotificationCenterAlignmentChange();
+ /* Notification-center order? */
+ if (strKey == GUI_NotificationCenter_Order)
+ emit sigNotificationCenterOrderChange();
+ /* Language changed? */
+ if (strKey == GUI_LanguageID)
+ emit sigLanguageChange(extraDataString(strKey));
+ /* Selector UI shortcut changed? */
+ else if (strKey == GUI_Input_SelectorShortcuts)
+ emit sigSelectorUIShortcutChange();
+ /* Runtime UI shortcut changed? */
+ else if (strKey == GUI_Input_MachineShortcuts)
+ emit sigRuntimeUIShortcutChange();
+ /* Runtime UI host-key combintation changed? */
+ else if (strKey == GUI_Input_HostKeyCombination)
+ emit sigRuntimeUIHostKeyCombinationChange();
+ /* Cloud Profile Manager restrictions changed: */
+ else if (strKey == GUI_CloudProfileManager_Restrictions)
+ emit sigCloudProfileManagerRestrictionChange();
+ /* Cloud Console Manager data changed: */
+ else if (strKey.startsWith(QString(GUI_CloudConsoleManager_Application) + '/'))
+ emit sigCloudConsoleManagerDataChange();
+ /* Cloud Console Manager restrictions changed: */
+ else if (strKey == GUI_CloudConsoleManager_Restrictions)
+ emit sigCloudConsoleManagerRestrictionChange();
+#if defined(VBOX_WS_X11) || defined(VBOX_WS_WIN)
+ else if (strKey == GUI_DisableHostScreenSaver)
+ emit sigDisableHostScreenSaverStateChange(isFeatureAllowed(GUI_DisableHostScreenSaver));
+#endif
+ /* Details categories: */
+ else if (strKey == GUI_Details_Elements)
+ emit sigDetailsCategoriesChange();
+ /* Details options: */
+ else if (strKey.startsWith(QString(GUI_Details_Elements) + '/'))
+ {
+ QString strLeftover = strKey;
+ strLeftover.remove(QString(GUI_Details_Elements) + '/');
+ const DetailsElementType enmType = gpConverter->fromInternalString<DetailsElementType>(strLeftover);
+ if (enmType != DetailsElementType_Invalid)
+ emit sigDetailsOptionsChange(enmType);
+ }
+ /* Font scaling factor has changed: */
+ else if (strKey == GUI_FontScaleFactor)
+ emit sigFontScaleFactorChanged(fontScaleFactor());
+ }
+ }
+ /* Machine extra-data 'change' event: */
+ else
+ {
+ /* Current VM only: */
+ if ( uiCommon().uiType() == UICommon::UIType_RuntimeUI
+ && uMachineID == uiCommon().managedVMUuid())
+ {
+ /* HID LEDs sync state changed (allowed if not restricted)? */
+ if (strKey == GUI_HidLedsSync)
+ emit sigHidLedsSyncStateChange(!isFeatureRestricted(strKey, uMachineID));
+#ifdef VBOX_WS_MAC
+ /* 'Dock icon' appearance changed (allowed if not restricted)? */
+ else if (strKey == GUI_RealtimeDockIconUpdateEnabled ||
+ strKey == GUI_RealtimeDockIconUpdateMonitor)
+ emit sigDockIconAppearanceChange(!isFeatureRestricted(strKey, uMachineID));
+ /* 'Dock icon overlay' appearance changed (restricted if not allowed)? */
+ else if (strKey == GUI_DockIconDisableOverlay)
+ emit sigDockIconOverlayAppearanceChange(isFeatureAllowed(strKey, uMachineID));
+#endif /* VBOX_WS_MAC */
+ }
+
+ /* Menu-bar configuration change: */
+ if (
+#ifndef VBOX_WS_MAC
+ strKey == GUI_MenuBar_Enabled ||
+#endif /* !VBOX_WS_MAC */
+ strKey == GUI_RestrictedRuntimeMenus ||
+ strKey == GUI_RestrictedRuntimeApplicationMenuActions ||
+ strKey == GUI_RestrictedRuntimeMachineMenuActions ||
+ strKey == GUI_RestrictedRuntimeViewMenuActions ||
+ strKey == GUI_RestrictedRuntimeInputMenuActions ||
+ strKey == GUI_RestrictedRuntimeDevicesMenuActions ||
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ strKey == GUI_RestrictedRuntimeDebuggerMenuActions ||
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ strKey == GUI_RestrictedRuntimeWindowMenuActions ||
+#endif /* VBOX_WS_MAC */
+ strKey == GUI_RestrictedRuntimeHelpMenuActions)
+ emit sigMenuBarConfigurationChange(uMachineID);
+ /* Status-bar configuration change: */
+ else if (strKey == GUI_StatusBar_Enabled ||
+ strKey == GUI_RestrictedStatusBarIndicators ||
+ strKey == GUI_StatusBar_IndicatorOrder)
+ emit sigStatusBarConfigurationChange(uMachineID);
+ /* Visual state change: */
+ else if (strKey == GUI_Fullscreen ||
+ strKey == GUI_Seamless ||
+ strKey == GUI_Scale)
+ emit sigVisualStateChange(uMachineID);
+ /* Scale-factor change: */
+ else if (strKey == GUI_ScaleFactor)
+ emit sigScaleFactorChange(uMachineID);
+ /* Scaling optimization type change: */
+ else if (strKey == GUI_Scaling_Optimization)
+ emit sigScalingOptimizationTypeChange(uMachineID);
+ }
+
+ /* Notify listeners: */
+ emit sigExtraDataChange(uMachineID, strKey, strValue);
+}
+
+void UIExtraDataManager::prepare()
+{
+ /* Prepare global extra-data map: */
+ prepareGlobalExtraDataMap();
+ /* Prepare extra-data event-handler: */
+ prepareExtraDataEventHandler();
+}
+
+void UIExtraDataManager::prepareGlobalExtraDataMap()
+{
+ /* Get CVirtualBox: */
+ CVirtualBox vbox = uiCommon().virtualBox();
+
+ /* Make sure at least empty map is created: */
+ m_data[GlobalID] = ExtraDataMap();
+
+ /* Load global extra-data map: */
+ foreach (const QString &strKey, vbox.GetExtraDataKeys())
+ m_data[GlobalID][strKey] = vbox.GetExtraData(strKey);
+}
+
+void UIExtraDataManager::prepareExtraDataEventHandler()
+{
+ /* Create extra-data event-handler: */
+ m_pHandler = new UIExtraDataEventHandler(this);
+ /* Configure extra-data event-handler: */
+ AssertPtrReturnVoid(m_pHandler);
+ {
+ /* Create queued (async) connections for signals of event proxy object: */
+ connect(m_pHandler, &UIExtraDataEventHandler::sigExtraDataChange,
+ this, &UIExtraDataManager::sltExtraDataChange,
+ Qt::QueuedConnection);
+ }
+}
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+void UIExtraDataManager::cleanupWindow()
+{
+ delete m_pWindow;
+}
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+void UIExtraDataManager::cleanupExtraDataEventHandler()
+{
+ /* Destroy extra-data event-handler: */
+ delete m_pHandler;
+ m_pHandler = 0;
+}
+
+void UIExtraDataManager::cleanup()
+{
+ /* Cleanup extra-data event-handler: */
+ cleanupExtraDataEventHandler();
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ /* Cleanup window: */
+ cleanupWindow();
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+}
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+void UIExtraDataManager::open(QWidget *pCenterWidget)
+{
+ /* If necessary: */
+ if (!m_pWindow)
+ {
+ /* Create window: */
+ m_pWindow = new UIExtraDataManagerWindow(pCenterWidget);
+ /* Configure window connections: */
+ connect(this, &UIExtraDataManager::sigExtraDataMapAcknowledging,
+ m_pWindow.data(), &UIExtraDataManagerWindow::sltExtraDataMapAcknowledging);
+ connect(this, &UIExtraDataManager::sigExtraDataChange,
+ m_pWindow.data(), &UIExtraDataManagerWindow::sltExtraDataChange);
+ }
+ /* Show and raise window: */
+ m_pWindow->showAndRaise(pCenterWidget);
+}
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+QString UIExtraDataManager::extraDataStringUnion(const QString &strKey, const QUuid &uID)
+{
+ /* If passed uID differs from the GlobalID: */
+ if (uID != GlobalID)
+ {
+ /* Search through the machine extra-data first: */
+ MapOfExtraDataMaps::const_iterator itMap = m_data.constFind(uID);
+ /* Hot-load machine extra-data map if necessary: */
+ if (itMap == m_data.constEnd())
+ {
+ hotloadMachineExtraDataMap(uID);
+ itMap = m_data.constFind(uID);
+ }
+ if (itMap != m_data.constEnd())
+ {
+ /* Return string if present in the map: */
+ ExtraDataMap::const_iterator itValue = itMap->constFind(strKey);
+ if (itValue != itMap->constEnd())
+ return *itValue;
+ }
+ }
+
+ /* Search through the global extra-data finally: */
+ MapOfExtraDataMaps::const_iterator itMap = m_data.constFind(GlobalID);
+ if (itMap != m_data.constEnd())
+ {
+ /* Return string if present in the map: */
+ ExtraDataMap::const_iterator itValue = itMap->constFind(strKey);
+ if (itValue != itMap->constEnd())
+ return *itValue;
+ }
+
+ /* Not found, return null string: */
+ return QString();
+}
+
+bool UIExtraDataManager::isFeatureAllowed(const QString &strKey, const QUuid &uID /* = GlobalID */)
+{
+ /* Get the value. Return 'false' if not found: */
+ const QString strValue = extraDataStringUnion(strKey, uID);
+ if (strValue.isNull())
+ return false;
+
+ /* Check corresponding value: */
+ return strValue.compare("true", Qt::CaseInsensitive) == 0
+ || strValue.compare("yes", Qt::CaseInsensitive) == 0
+ || strValue.compare("on", Qt::CaseInsensitive) == 0
+ || strValue == "1";
+}
+
+bool UIExtraDataManager::isFeatureRestricted(const QString &strKey, const QUuid &uID /* = GlobalID */)
+{
+ /* Get the value. Return 'false' if not found: */
+ const QString strValue = extraDataStringUnion(strKey, uID);
+ if (strValue.isNull())
+ return false;
+
+ /* Check corresponding value: */
+ return strValue.compare("false", Qt::CaseInsensitive) == 0
+ || strValue.compare("no", Qt::CaseInsensitive) == 0
+ || strValue.compare("off", Qt::CaseInsensitive) == 0
+ || strValue == "0";
+}
+
+QString UIExtraDataManager::toFeatureState(bool fState)
+{
+ return fState ? QString("true") : QString("false");
+}
+
+QString UIExtraDataManager::toFeatureAllowed(bool fAllowed)
+{
+ return fAllowed ? QString("true") : QString();
+}
+
+QString UIExtraDataManager::toFeatureRestricted(bool fRestricted)
+{
+ return fRestricted ? QString("false") : QString();
+}
+
+void UIExtraDataManager::setDialogGeometry(const QString &strKey, const QRect &geometry, bool fMaximized)
+{
+ /* Serialize passed values: */
+ QStringList data;
+ data << QString::number(geometry.x());
+ data << QString::number(geometry.y());
+ data << QString::number(geometry.width());
+ data << QString::number(geometry.height());
+ if (fMaximized)
+ data << GUI_Geometry_State_Max;
+
+ /* Save corresponding extra-data: */
+ setExtraDataStringList(strKey, data);
+}
+
+QRect UIExtraDataManager::dialogGeometry(const QString &strKey,
+ QWidget *pWidget,
+ QWidget *pParentWidget /* = 0 */,
+ const QRect &defaultGeometry /* = QRect() */)
+{
+ /* Get corresponding extra-data: */
+ const QStringList data = extraDataStringList(strKey);
+
+ /* Parse loaded data: */
+ int iX = 0, iY = 0, iW = 0, iH = 0;
+ bool fOk = data.size() >= 4;
+ do
+ {
+ if (!fOk) break;
+ iX = data[0].toInt(&fOk);
+ if (!fOk) break;
+ iY = data[1].toInt(&fOk);
+ if (!fOk) break;
+ iW = data[2].toInt(&fOk);
+ if (!fOk) break;
+ iH = data[3].toInt(&fOk);
+ }
+ while (0);
+
+ /* Get available-geometry [of screen with point (iX, iY) if possible]: */
+ const QRect availableGeometry = fOk ? gpDesktop->availableGeometry(QPoint(iX, iY)) :
+ gpDesktop->availableGeometry();
+
+ /* Use geometry (loaded or default): */
+ QRect geometry = fOk
+ ? QRect(iX, iY, iW, iH)
+ : !defaultGeometry.isNull()
+ ? defaultGeometry
+ : QRect(QPoint(0, 0), availableGeometry.size() * .50 /* % */);
+
+ /* Take hint-widget into account: */
+ if (pWidget)
+ geometry.setSize(geometry.size().expandedTo(pWidget->minimumSizeHint()));
+
+ /* As a fallback, move default-geometry to pParentWidget' geometry center: */
+ if (!fOk && pParentWidget)
+ geometry.moveCenter(pParentWidget->geometry().center());
+ /* As final fallback, move default-geometry to available-geometry' center: */
+ else if (!fOk)
+ geometry.moveCenter(availableGeometry.center());
+
+ /* In Windows Qt fails to reposition out of screen window properly, so doing it ourselves: */
+#ifdef VBOX_WS_WIN
+ /* Make sure resulting geometry is within current bounds: */
+ if (!availableGeometry.contains(geometry))
+ geometry = UIDesktopWidgetWatchdog::getNormalized(geometry, QRegion(availableGeometry));
+#endif /* VBOX_WS_WIN */
+
+ /* Return result: */
+ return geometry;
+}
+
+bool UIExtraDataManager::dialogShouldBeMaximized(const QString &strKey)
+{
+ /* Get corresponding extra-data: */
+ const QStringList data = extraDataStringList(strKey);
+
+ /* Make sure 5th item has required value: */
+ return data.size() == 5 && data[4] == GUI_Geometry_State_Max;
+}
+
+/* static */
+QString UIExtraDataManager::extraDataKeyPerScreen(const QString &strBase, ulong uScreenIndex, bool fSameRuleForPrimary /* = false */)
+{
+ return fSameRuleForPrimary || uScreenIndex ? strBase + QString::number(uScreenIndex) : strBase;
+}
+
+#include "UIExtraDataManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataManager.h b/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataManager.h
new file mode 100644
index 00000000..640fb7f7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/extradata/UIExtraDataManager.h
@@ -0,0 +1,969 @@
+/* $Id: UIExtraDataManager.h $ */
+/** @file
+ * VBox Qt GUI - UIExtraDataManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_extradata_UIExtraDataManager_h
+#define FEQT_INCLUDED_SRC_extradata_UIExtraDataManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QObject>
+#include <QRect>
+#include <QSize>
+#include <QUuid>
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+# include <QPointer>
+#endif
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+/* Forward declarations: */
+class UIExtraDataEventHandler;
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+class UIExtraDataManagerWindow;
+#endif
+
+/** Defines the map of extra data values. The index is an extra-data key. */
+typedef QMap<QString, QString> ExtraDataMap;
+/** Defines the map of extra data maps. */
+typedef QMap<QUuid, ExtraDataMap> MapOfExtraDataMaps;
+
+/** Singleton QObject extension
+ * providing GUI with corresponding extra-data values,
+ * and notifying it whenever any of those values changed. */
+class SHARED_LIBRARY_STUFF UIExtraDataManager : public QObject
+{
+ Q_OBJECT;
+
+ /** Extra-data Manager constructor. */
+ UIExtraDataManager();
+ /** Extra-data Manager destructor. */
+ ~UIExtraDataManager();
+
+signals:
+
+ /** Notifies about extra-data map acknowledging. */
+ void sigExtraDataMapAcknowledging(const QUuid &uID);
+
+ /** Notifies about extra-data change. */
+ void sigExtraDataChange(const QUuid &uID, const QString &strKey, const QString &strValue);
+
+ /** Notifies about notification-center alignment change. */
+ void sigNotificationCenterAlignmentChange();
+ /** Notifies about notification-center order change. */
+ void sigNotificationCenterOrderChange();
+
+ /** Notifies about GUI language change. */
+ void sigLanguageChange(QString strLanguage);
+
+ /** Notifies about Selector UI keyboard shortcut change. */
+ void sigSelectorUIShortcutChange();
+ /** Notifies about Runtime UI keyboard shortcut change. */
+ void sigRuntimeUIShortcutChange();
+ /** Notifies about Runtime UI host-key combination change. */
+ void sigRuntimeUIHostKeyCombinationChange();
+
+ /** Notifies about Cloud Profile Manager restriction change. */
+ void sigCloudProfileManagerRestrictionChange();
+
+ /** Notifies about Cloud Console Manager data change. */
+ void sigCloudConsoleManagerDataChange();
+ /** Notifies about Cloud Console Manager restriction change. */
+ void sigCloudConsoleManagerRestrictionChange();
+
+ /** Notifies about VirtualBox Manager / Details pane categories change. */
+ void sigDetailsCategoriesChange();
+ /** Notifies about VirtualBox Manager / Details pane options change. */
+ void sigDetailsOptionsChange(DetailsElementType enmType);
+
+ /** Notifies about visual state change. */
+ void sigVisualStateChange(const QUuid &uMachineID);
+
+ /** Notifies about menu-bar configuration change. */
+ void sigMenuBarConfigurationChange(const QUuid &uMachineID);
+ /** Notifies about status-bar configuration change. */
+ void sigStatusBarConfigurationChange(const QUuid &uMachineID);
+
+ /** Notifies about HID LEDs synchronization state change. */
+ void sigHidLedsSyncStateChange(bool fEnabled);
+
+ /** Notifies about the scale-factor change. */
+ void sigScaleFactorChange(const QUuid &uMachineID);
+
+ /** Notifies about the scaling optimization type change. */
+ void sigScalingOptimizationTypeChange(const QUuid &uMachineID);
+
+ /** Notifies about font scale factor. */
+ void sigFontScaleFactorChanged(int iFontScaleFactor);
+
+#ifdef VBOX_WS_MAC
+ /** Notifies about the HiDPI optimization type change. */
+ void sigHiDPIOptimizationTypeChange(const QUuid &uMachineID);
+
+ /** Mac OS X: Notifies about 'dock icon' appearance change. */
+ void sigDockIconAppearanceChange(bool fEnabled);
+ /** Mac OS X: Notifies about 'dock icon overlay' appearance change. */
+ void sigDockIconOverlayAppearanceChange(bool fEnabled);
+#endif /* VBOX_WS_MAC */
+
+#if defined (VBOX_WS_X11) || defined (VBOX_WS_WIN)
+ /* Is emitted when host screen saver inhibition state changes. */
+ void sigDisableHostScreenSaverStateChange(bool fDisable);
+#endif
+
+public:
+
+ /** Global extra-data ID. */
+ static const QUuid GlobalID;
+
+ /** Static Extra-data Manager instance/constructor. */
+ static UIExtraDataManager* instance();
+ /** Static Extra-data Manager destructor. */
+ static void destroy();
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ /** Static show and raise API. */
+ static void openWindow(QWidget *pCenterWidget);
+#endif
+
+ /** @name Base
+ * @{ */
+ /** Returns whether Extra-data Manager cached the map with passed @a uID. */
+ bool contains(const QUuid &uID) const { return m_data.contains(uID); }
+ /** Returns read-only extra-data map for passed @a uID. */
+ const ExtraDataMap map(const QUuid &uID) const { return m_data.value(uID); }
+
+ /** Hot-load machine extra-data map. */
+ void hotloadMachineExtraDataMap(const QUuid &uID);
+
+ /** Returns extra-data value corresponding to passed @a strKey as QString.
+ * If valid @a uID is set => applies to machine extra-data, otherwise => to global one. */
+ QString extraDataString(const QString &strKey, const QUuid &uID = GlobalID);
+ /** Defines extra-data value corresponding to passed @a strKey as strValue.
+ * If valid @a uID is set => applies to machine extra-data, otherwise => to global one. */
+ void setExtraDataString(const QString &strKey, const QString &strValue, const QUuid &uID = GlobalID);
+
+ /** Returns extra-data value corresponding to passed @a strKey as QStringList.
+ * If valid @a uID is set => applies to machine extra-data, otherwise => to global one. */
+ QStringList extraDataStringList(const QString &strKey, const QUuid &uID = GlobalID);
+ /** Defines extra-data value corresponding to passed @a strKey as value.
+ * If valid @a uID is set => applies to machine extra-data, otherwise => to global one. */
+ void setExtraDataStringList(const QString &strKey, const QStringList &value, const QUuid &uID = GlobalID);
+ /** @} */
+
+ /** @name General
+ * @{ */
+ /** Returns a list of restricted dialogs. */
+ UIExtraDataMetaDefs::DialogType restrictedDialogTypes(const QUuid &uID);
+ /** Defines a list of restricted dialogs. */
+ void setRestrictedDialogTypes(UIExtraDataMetaDefs::DialogType enmTypes, const QUuid &uID);
+
+ /** Returns color theme type. */
+ UIColorThemeType colorTheme();
+ /** Defines color theme @a enmType. */
+ void setColorTheme(const UIColorThemeType &enmType);
+ /** @} */
+
+ /** @name Messaging
+ * @{ */
+ /** Returns the list of supressed messages for the Message/Popup center frameworks. */
+ QStringList suppressedMessages(const QUuid &uID = GlobalID);
+ /** Defines the @a list of supressed messages for the Message/Popup center frameworks. */
+ void setSuppressedMessages(const QStringList &list);
+
+ /** Returns the list of messages for the Message/Popup center frameworks with inverted check-box state. */
+ QStringList messagesWithInvertedOption();
+
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+ /** Returns whether successfull notification-progresses should NOT close automatically. */
+ bool keepSuccessfullNotificationProgresses();
+ /** Defines whether successfull notification-progresses should NOT close (@a fKeep) automatically. */
+ void setKeepSuccessfullNotificationProgresses(bool fKeep);
+#endif /* VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON */
+
+ /** Returns notification-center alignment. */
+ Qt::Alignment notificationCenterAlignment();
+ /** Defines notification-progresses @a enmOrder. */
+ void setNotificationCenterAlignment(Qt::Alignment enmOrder);
+
+ /** Returns notification-center order. */
+ Qt::SortOrder notificationCenterOrder();
+ /** Defines notification-progresses @a enmOrder. */
+ void setNotificationCenterOrder(Qt::SortOrder enmOrder);
+
+ /** Returns whether BETA build label should be hidden. */
+ bool preventBetaBuildLavel();
+#if !defined(VBOX_BLEEDING_EDGE) && !defined(DEBUG)
+ /** Returns version for which user wants to prevent BETA build warning. */
+ QString preventBetaBuildWarningForVersion();
+#endif
+ /** @} */
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /** @name Application Update
+ * @{ */
+ /** Returns whether Application Update functionality enabled. */
+ bool applicationUpdateEnabled();
+
+ /** Returns Application Update data. */
+ QString applicationUpdateData();
+ /** Defines Application Update data as @a strValue. */
+ void setApplicationUpdateData(const QString &strValue);
+
+ /** Returns Application Update check counter. */
+ qulonglong applicationUpdateCheckCounter();
+ /** Increments Application Update check counter. */
+ void incrementApplicationUpdateCheckCounter();
+ /** @} */
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+ /** @name Progress
+ * @{ */
+ /** Returns whether legacy progress handling method is requested. */
+ bool legacyProgressHandlingRequested();
+ /** @} */
+
+ /** @name Settings
+ * @{ */
+ /** Returns whether GUI @a enmFeature is enabled. */
+ bool guiFeatureEnabled(GUIFeatureType enmFeature);
+
+ /** Returns restricted global settings pages. */
+ QList<GlobalSettingsPageType> restrictedGlobalSettingsPages();
+ /** Returns restricted machine settings pages. */
+ QList<MachineSettingsPageType> restrictedMachineSettingsPages(const QUuid &uID);
+ /** @} */
+
+ /** @name Settings: Language
+ * @{ */
+ /** Returns the GUI language ID. */
+ QString languageId();
+ /** Defines the GUI @a strLanguageId. */
+ void setLanguageId(const QString &strLanguageId);
+ /** @} */
+
+ /** @name Settings: Display
+ * @{ */
+ /** Returns maximum guest-screen resolution policy. */
+ MaximumGuestScreenSizePolicy maxGuestResolutionPolicy();
+ /** Defines maximum guest-screen resolution @a enmPolicy or @a resolution itself for Fixed policy. */
+ void setMaxGuestScreenResolution(MaximumGuestScreenSizePolicy enmPolicy, const QSize resolution = QSize());
+ /** Returns maximum guest-screen resolution for fixed policy. */
+ QSize maxGuestResolutionForPolicyFixed();
+ /** Defines maximum guest-screen @a resolution for fixed policy. */
+ void setMaxGuestResolutionForPolicyFixed(const QSize &resolution);
+
+ /** Returns whether hovered machine-window should be activated. */
+ bool activateHoveredMachineWindow();
+ /** Defines whether hovered machine-window should be @a fActivated. */
+ void setActivateHoveredMachineWindow(bool fActivate);
+ /* Return whether host screen saver is disabled when a vm is running. */
+ bool disableHostScreenSaver();
+ /* Sets whether host screen saver is disabled when a vm is running. */
+ void setDisableHostScreenSaver(bool fActivate);
+ /* Set global font scale factor as percentage. 100% is for no scaling. */
+ void setFontScaleFactor(int iFontScaleFactor);
+ int fontScaleFactor();
+ /** @} */
+
+ /** @name Settings: Keyboard
+ * @{ */
+ /** Returns the Runtime UI host-key combination. */
+ QString hostKeyCombination();
+ /** Defines the Runtime UI host-key combination. */
+ void setHostKeyCombination(const QString &strHostCombo);
+
+ /** Returns shortcut overrides for shortcut-pool with @a strPoolExtraDataID. */
+ QStringList shortcutOverrides(const QString &strPoolExtraDataID);
+
+ /** Returns whether the Runtime UI auto-capture is enabled. */
+ bool autoCaptureEnabled();
+ /** Defines whether the Runtime UI auto-capture is @a fEnabled. */
+ void setAutoCaptureEnabled(bool fEnabled);
+
+ /** Returns the Runtime UI remapped scan codes. */
+ QString remappedScanCodes();
+ /** @} */
+
+ /** @name Settings: Proxy
+ * @{ */
+ /** Returns VBox proxy settings. */
+ QString proxySettings();
+ /** Defines VBox proxy @a strSettings. */
+ void setProxySettings(const QString &strSettings);
+ /** @} */
+
+ /** @name Settings: Storage
+ * @{ */
+ /** Returns recent folder for hard-drives. */
+ QString recentFolderForHardDrives();
+ /** Returns recent folder for optical-disks. */
+ QString recentFolderForOpticalDisks();
+ /** Returns recent folder for floppy-disks. */
+ QString recentFolderForFloppyDisks();
+ /** Defines recent folder for hard-drives as @a strValue. */
+ void setRecentFolderForHardDrives(const QString &strValue);
+ /** Defines recent folder for optical-disk as @a strValue. */
+ void setRecentFolderForOpticalDisks(const QString &strValue);
+ /** Defines recent folder for floppy-disk as @a strValue. */
+ void setRecentFolderForFloppyDisks(const QString &strValue);
+
+ /** Returns the list of recently used hard-drives. */
+ QStringList recentListOfHardDrives();
+ /** Returns the list of recently used optical-disk. */
+ QStringList recentListOfOpticalDisks();
+ /** Returns the list of recently used floppy-disk. */
+ QStringList recentListOfFloppyDisks();
+ /** Defines the list of recently used hard-drives as @a value. */
+ void setRecentListOfHardDrives(const QStringList &value);
+ /** Defines the list of recently used optical-disks as @a value. */
+ void setRecentListOfOpticalDisks(const QStringList &value);
+ /** Defines the list of recently used floppy-disks as @a value. */
+ void setRecentListOfFloppyDisks(const QStringList &value);
+ /** @} */
+
+ /** @name Settings: Network
+ * @{ */
+ /** Returns the list of restricted network attachment types. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork restrictedNetworkAttachmentTypes();
+ /** @} */
+
+ /** @name VISO Creator
+ * @{ */
+ /** Returns recent folder for VISO creation content. */
+ QString visoCreatorRecentFolder();
+ /** Defines recent folder for VISO creation content as @a strValue. */
+ void setVISOCreatorRecentFolder(const QString &strValue);
+ /** Returns viso creator geometry using @a pWidget as the hint. */
+ QRect visoCreatorDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry);
+ /** Set viso creator geometry. */
+ void setVisoCreatorDialogGeometry(const QRect &geometry, bool fMaximized);
+ /** Returns whether viso creator dialog should be maximized. */
+ bool visoCreatorDialogShouldBeMaximized();
+ /** @} */
+
+ /** @name VirtualBox Manager
+ * @{ */
+ /** Returns selector-window geometry using @a pWidget as the hint. */
+ QRect selectorWindowGeometry(QWidget *pWidget);
+ /** Returns whether selector-window should be maximized. */
+ bool selectorWindowShouldBeMaximized();
+ /** Defines selector-window @a geometry and @a fMaximized state. */
+ void setSelectorWindowGeometry(const QRect &geometry, bool fMaximized);
+
+ /** Returns selector-window splitter hints. */
+ QList<int> selectorWindowSplitterHints();
+ /** Defines selector-window splitter @a hints. */
+ void setSelectorWindowSplitterHints(const QList<int> &hints);
+
+ /** Returns whether selector-window tool-bar visible. */
+ bool selectorWindowToolBarVisible();
+ /** Defines whether selector-window tool-bar @a fVisible. */
+ void setSelectorWindowToolBarVisible(bool fVisible);
+
+ /** Returns whether selector-window tool-bar text visible. */
+ bool selectorWindowToolBarTextVisible();
+ /** Defines whether selector-window tool-bar text @a fVisible. */
+ void setSelectorWindowToolBarTextVisible(bool fVisible);
+
+ /** Returns last selected tool set of VirtualBox Manager. */
+ QList<UIToolType> toolsPaneLastItemsChosen();
+ /** Defines last selected tool @a set of VirtualBox Manager. */
+ void setToolsPaneLastItemsChosen(const QList<UIToolType> &set);
+
+ /** Returns whether selector-window status-bar visible. */
+ bool selectorWindowStatusBarVisible();
+ /** Defines whether selector-window status-bar @a fVisible. */
+ void setSelectorWindowStatusBarVisible(bool fVisible);
+
+ /** Returns all the existing selector-window chooser-pane' group definition keys. */
+ QStringList knownMachineGroupDefinitionKeys();
+ /** Returns selector-window chooser-pane' group definitions for passed @a strGroupID. */
+ QStringList machineGroupDefinitions(const QString &strGroupID);
+ /** Defines selector-window chooser-pane' group @a definitions for passed @a strGroupID. */
+ void setMachineGroupDefinitions(const QString &strGroupID, const QStringList &definitions);
+
+ /** Returns last-item ID of the item chosen in selector-window chooser-pane. */
+ QString selectorWindowLastItemChosen();
+ /** Defines @a lastItemID of the item chosen in selector-window chooser-pane. */
+ void setSelectorWindowLastItemChosen(const QString &strItemID);
+
+ /** Returns selector-window details-pane' elements. */
+ QMap<DetailsElementType, bool> selectorWindowDetailsElements();
+ /** Defines selector-window details-pane' @a elements. */
+ void setSelectorWindowDetailsElements(const QMap<DetailsElementType, bool> &elements);
+
+ /** Returns selector-window details-pane' preview update interval. */
+ PreviewUpdateIntervalType selectorWindowPreviewUpdateInterval();
+ /** Defines selector-window details-pane' preview update @a interval. */
+ void setSelectorWindowPreviewUpdateInterval(PreviewUpdateIntervalType interval);
+
+ /** Returns VirtualBox Manager / Details pane options for certain @a enmElementType. */
+ QStringList vboxManagerDetailsPaneElementOptions(DetailsElementType enmElementType);
+ /** Defines VirtualBox Manager / Details pane @a options for certain @a enmElementType. */
+ void setVBoxManagerDetailsPaneElementOptions(DetailsElementType enmElementType, const QStringList &options);
+ /** @} */
+
+ /** @name Snapshot Manager
+ * @{ */
+ /** Returns whether Snapshot Manager details expanded. */
+ bool snapshotManagerDetailsExpanded();
+ /** Defines whether Snapshot Manager details @a fExpanded. */
+ void setSnapshotManagerDetailsExpanded(bool fExpanded);
+ /** @} */
+
+ /** @name Virtual Media Manager
+ * @{ */
+ /** Returns whether Virtual Media Manager details expanded. */
+ bool virtualMediaManagerDetailsExpanded();
+ /** Defines whether Virtual Media Manager details @a fExpanded. */
+ void setVirtualMediaManagerDetailsExpanded(bool fExpanded);
+ /** Returns whether Virtual Media Manager search widget expanded. */
+ bool virtualMediaManagerSearchWidgetExpanded();
+ /** Defines whether Virtual Media Manager search widget @a fExpanded. */
+ void setVirtualMediaManagerSearchWidgetExpanded(bool fExpanded);
+ /** @} */
+
+ /** @name Host Network Manager
+ * @{ */
+ /** Returns whether Host Network Manager details expanded. */
+ bool hostNetworkManagerDetailsExpanded();
+ /** Defines whether Host Network Manager details @a fExpanded. */
+ void setHostNetworkManagerDetailsExpanded(bool fExpanded);
+ /** @} */
+
+ /** @name Cloud Profile Manager
+ * @{ */
+ /** Returns Cloud Profile Manager restrictions. */
+ QStringList cloudProfileManagerRestrictions();
+ /** Defines Cloud Profile Manager @a restrictions. */
+ void setCloudProfileManagerRestrictions(const QStringList &restrictions);
+
+ /** Returns whether Cloud Profile Manager details expanded. */
+ bool cloudProfileManagerDetailsExpanded();
+ /** Defines whether Cloud Profile Manager details @a fExpanded. */
+ void setCloudProfileManagerDetailsExpanded(bool fExpanded);
+ /** @} */
+
+ /** @name Cloud Console Manager
+ * @{ */
+ /** Returns registered Cloud Console Manager applications. */
+ QStringList cloudConsoleManagerApplications();
+ /** Returns registered Cloud Console Manager profiles for application with @a strId. */
+ QStringList cloudConsoleManagerProfiles(const QString &strId);
+
+ /** Returns definition for Cloud Console Manager application with @a strId. */
+ QString cloudConsoleManagerApplication(const QString &strId);
+ /** Defines @a strDefinition for Cloud Console Manager application with @a strId. */
+ void setCloudConsoleManagerApplication(const QString &strId, const QString &strDefinition);
+
+ /** Returns definition for Cloud Console Manager profile with @a strProfileId for application with @a strApplicationId. */
+ QString cloudConsoleManagerProfile(const QString &strApplicationId, const QString &strProfileId);
+ /** Returns @a strDefinition for Cloud Console Manager profile with @a strProfileId for application with @a strApplicationId. */
+ void setCloudConsoleManagerProfile(const QString &strApplicationId, const QString &strProfileId, const QString &strDefinition);
+
+ /** Returns Cloud Console Manager restrictions. */
+ QStringList cloudConsoleManagerRestrictions();
+ /** Defines Cloud Console Manager @a restrictions. */
+ void setCloudConsoleManagerRestrictions(const QStringList &restrictions);
+
+ /** Returns whether Cloud Console Manager details expanded. */
+ bool cloudConsoleManagerDetailsExpanded();
+ /** Defines whether Cloud Console Manager details @a fExpanded. */
+ void setCloudConsoleManagerDetailsExpanded(bool fExpanded);
+ /** @} */
+
+ /** @name Cloud Console
+ * @{ */
+ /** Returns Cloud Console public key path. */
+ QString cloudConsolePublicKeyPath();
+ /** Defines Cloud Console public key @a strPath. */
+ void setCloudConsolePublicKeyPath(const QString &strPath);
+ /** @} */
+
+ /** @name Wizards
+ * @{ */
+ /** Returns mode for wizard of passed @a type. */
+ WizardMode modeForWizardType(WizardType type);
+ /** Defines @a mode for wizard of passed @a type. */
+ void setModeForWizardType(WizardType type, WizardMode mode);
+ /** @} */
+
+ /** @name Virtual Machine
+ * @{ */
+ /** Returns whether machine should be shown in VirtualBox Manager Chooser-pane. */
+ bool showMachineInVirtualBoxManagerChooser(const QUuid &uID);
+ /** Returns whether machine should be shown in VirtualBox Manager Details-pane. */
+ bool showMachineInVirtualBoxManagerDetails(const QUuid &uID);
+
+ /** Returns whether machine reconfiguration enabled. */
+ bool machineReconfigurationEnabled(const QUuid &uID);
+ /** Returns whether machine snapshot operations enabled. */
+ bool machineSnapshotOperationsEnabled(const QUuid &uID);
+
+ /** Except Mac OS X: Returns redefined machine-window icon names. */
+ QStringList machineWindowIconNames(const QUuid &uID);
+#ifndef VBOX_WS_MAC
+ /** Except Mac OS X: Returns redefined machine-window name postfix. */
+ QString machineWindowNamePostfix(const QUuid &uID);
+#endif
+
+ /** Returns geometry for machine-window with @a uScreenIndex in @a visualStateType. */
+ QRect machineWindowGeometry(UIVisualStateType visualStateType, ulong uScreenIndex, const QUuid &uID);
+ /** Returns whether machine-window with @a uScreenIndex in @a visualStateType should be maximized. */
+ bool machineWindowShouldBeMaximized(UIVisualStateType visualStateType, ulong uScreenIndex, const QUuid &uID);
+ /** Defines @a geometry and @a fMaximized state for machine-window with @a uScreenIndex in @a visualStateType. */
+ void setMachineWindowGeometry(UIVisualStateType visualStateType, ulong uScreenIndex, const QRect &geometry, bool fMaximized, const QUuid &uID);
+
+#ifndef VBOX_WS_MAC
+ /** Returns whether Runtime UI menu-bar is enabled. */
+ bool menuBarEnabled(const QUuid &uID);
+ /** Defines whether Runtime UI menu-bar is @a fEnabled. */
+ void setMenuBarEnabled(bool fEnabled, const QUuid &uID);
+#endif /* !VBOX_WS_MAC */
+
+ /** Returns whether Runtime UI menu-bar context-menu is enabled. */
+ bool menuBarContextMenuEnabled(const QUuid &uID);
+ /** Defines whether Runtime UI menu-bar context-menu is @a fEnabled. */
+ void setMenuBarContextMenuEnabled(bool fEnabled, const QUuid &uID);
+
+ /** Returns restricted Runtime UI menu types. */
+ UIExtraDataMetaDefs::MenuType restrictedRuntimeMenuTypes(const QUuid &uID);
+ /** Defines restricted Runtime UI menu types. */
+ void setRestrictedRuntimeMenuTypes(UIExtraDataMetaDefs::MenuType types, const QUuid &uID);
+
+ /** Returns restricted Runtime UI action types for Application menu. */
+ UIExtraDataMetaDefs::MenuApplicationActionType restrictedRuntimeMenuApplicationActionTypes(const QUuid &uID);
+ /** Defines restricted Runtime UI action types for Application menu. */
+ void setRestrictedRuntimeMenuApplicationActionTypes(UIExtraDataMetaDefs::MenuApplicationActionType types, const QUuid &uID);
+
+ /** Returns restricted Runtime UI action types for Machine menu. */
+ UIExtraDataMetaDefs::RuntimeMenuMachineActionType restrictedRuntimeMenuMachineActionTypes(const QUuid &uID);
+ /** Defines restricted Runtime UI action types for Machine menu. */
+ void setRestrictedRuntimeMenuMachineActionTypes(UIExtraDataMetaDefs::RuntimeMenuMachineActionType types, const QUuid &uID);
+
+ /** Returns restricted Runtime UI action types for View menu. */
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType restrictedRuntimeMenuViewActionTypes(const QUuid &uID);
+ /** Defines restricted Runtime UI action types for View menu. */
+ void setRestrictedRuntimeMenuViewActionTypes(UIExtraDataMetaDefs::RuntimeMenuViewActionType types, const QUuid &uID);
+
+ /** Returns restricted Runtime UI action types for Input menu. */
+ UIExtraDataMetaDefs::RuntimeMenuInputActionType restrictedRuntimeMenuInputActionTypes(const QUuid &uID);
+ /** Defines restricted Runtime UI action types for Input menu. */
+ void setRestrictedRuntimeMenuInputActionTypes(UIExtraDataMetaDefs::RuntimeMenuInputActionType types, const QUuid &uID);
+
+ /** Returns restricted Runtime UI action types for Devices menu. */
+ UIExtraDataMetaDefs::RuntimeMenuDevicesActionType restrictedRuntimeMenuDevicesActionTypes(const QUuid &uID);
+ /** Defines restricted Runtime UI action types for Devices menu. */
+ void setRestrictedRuntimeMenuDevicesActionTypes(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType types, const QUuid &uID);
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Returns restricted Runtime UI action types for Debugger menu. */
+ UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType restrictedRuntimeMenuDebuggerActionTypes(const QUuid &uID);
+ /** Defines restricted Runtime UI action types for Debugger menu. */
+ void setRestrictedRuntimeMenuDebuggerActionTypes(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType types, const QUuid &uID);
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Returns restricted Runtime UI action types for Window menu. */
+ UIExtraDataMetaDefs::MenuWindowActionType restrictedRuntimeMenuWindowActionTypes(const QUuid &uID);
+ /** Mac OS X: Defines restricted Runtime UI action types for Window menu. */
+ void setRestrictedRuntimeMenuWindowActionTypes(UIExtraDataMetaDefs::MenuWindowActionType types, const QUuid &uID);
+#endif /* VBOX_WS_MAC */
+
+ /** Returns restricted Runtime UI action types for Help menu. */
+ UIExtraDataMetaDefs::MenuHelpActionType restrictedRuntimeMenuHelpActionTypes(const QUuid &uID);
+ /** Defines restricted Runtime UI action types for Help menu. */
+ void setRestrictedRuntimeMenuHelpActionTypes(UIExtraDataMetaDefs::MenuHelpActionType types, const QUuid &uID);
+
+ /** Returns restricted Runtime UI visual-states. */
+ UIVisualStateType restrictedVisualStates(const QUuid &uID);
+
+ /** Returns requested Runtime UI visual-state. */
+ UIVisualStateType requestedVisualState(const QUuid &uID);
+ /** Defines requested Runtime UI visual-state as @a visualState. */
+ void setRequestedVisualState(UIVisualStateType visualState, const QUuid &uID);
+
+#ifdef VBOX_WS_X11
+ /** Returns whether legacy full-screen mode is requested. */
+ bool legacyFullscreenModeRequested();
+
+ /** Returns whether internal machine-window name should be unique. */
+ bool distinguishMachineWindowGroups(const QUuid &uID);
+ /** Defines whether internal machine-window name should be unique. */
+ void setDistinguishMachineWindowGroups(const QUuid &uID, bool fEnabled);
+#endif /* VBOX_WS_X11 */
+
+ /** Returns whether guest-screen auto-resize according machine-window size is enabled. */
+ bool guestScreenAutoResizeEnabled(const QUuid &uID);
+ /** Defines whether guest-screen auto-resize according machine-window size is @a fEnabled. */
+ void setGuestScreenAutoResizeEnabled(bool fEnabled, const QUuid &uID);
+
+ /** Returns last guest-screen visibility status for screen with @a uScreenIndex. */
+ bool lastGuestScreenVisibilityStatus(ulong uScreenIndex, const QUuid &uID);
+ /** Defines whether last guest-screen visibility status was @a fEnabled for screen with @a uScreenIndex. */
+ void setLastGuestScreenVisibilityStatus(ulong uScreenIndex, bool fEnabled, const QUuid &uID);
+
+ /** Returns last guest-screen size-hint for screen with @a uScreenIndex. */
+ QSize lastGuestScreenSizeHint(ulong uScreenIndex, const QUuid &uID);
+ /** Defines last guest-screen @a sizeHint for screen with @a uScreenIndex. */
+ void setLastGuestScreenSizeHint(ulong uScreenIndex, const QSize &sizeHint, const QUuid &uID);
+
+ /** Returns host-screen index corresponding to passed guest-screen @a iGuestScreenIndex. */
+ int hostScreenForPassedGuestScreen(int iGuestScreenIndex, const QUuid &uID);
+ /** Defines @a iHostScreenIndex corresponding to passed guest-screen @a iGuestScreenIndex. */
+ void setHostScreenForPassedGuestScreen(int iGuestScreenIndex, int iHostScreenIndex, const QUuid &uID);
+
+ /** Returns whether automatic mounting/unmounting of guest-screens enabled. */
+ bool autoMountGuestScreensEnabled(const QUuid &uID);
+
+#ifndef VBOX_WS_MAC
+ /** Returns whether mini-toolbar is enabled for full and seamless screens. */
+ bool miniToolbarEnabled(const QUuid &uID);
+ /** Defines whether mini-toolbar is @a fEnabled for full and seamless screens. */
+ void setMiniToolbarEnabled(bool fEnabled, const QUuid &uID);
+
+ /** Returns whether mini-toolbar should auto-hide itself. */
+ bool autoHideMiniToolbar(const QUuid &uID);
+ /** Defines whether mini-toolbar should @a fAutoHide itself. */
+ void setAutoHideMiniToolbar(bool fAutoHide, const QUuid &uID);
+
+ /** Returns mini-toolbar alignment. */
+ Qt::AlignmentFlag miniToolbarAlignment(const QUuid &uID);
+ /** Returns mini-toolbar @a alignment. */
+ void setMiniToolbarAlignment(Qt::AlignmentFlag alignment, const QUuid &uID);
+#endif /* VBOX_WS_MAC */
+
+ /** Returns whether Runtime UI status-bar is enabled. */
+ bool statusBarEnabled(const QUuid &uID);
+ /** Defines whether Runtime UI status-bar is @a fEnabled. */
+ void setStatusBarEnabled(bool fEnabled, const QUuid &uID);
+
+ /** Returns whether Runtime UI status-bar context-menu is enabled. */
+ bool statusBarContextMenuEnabled(const QUuid &uID);
+ /** Defines whether Runtime UI status-bar context-menu is @a fEnabled. */
+ void setStatusBarContextMenuEnabled(bool fEnabled, const QUuid &uID);
+
+ /** Returns restricted Runtime UI status-bar indicator list. */
+ QList<IndicatorType> restrictedStatusBarIndicators(const QUuid &uID);
+ /** Defines restricted Runtime UI status-bar indicator @a list. */
+ void setRestrictedStatusBarIndicators(const QList<IndicatorType> &list, const QUuid &uID);
+
+ /** Returns Runtime UI status-bar indicator order list. */
+ QList<IndicatorType> statusBarIndicatorOrder(const QUuid &uID);
+ /** Defines Runtime UI status-bar indicator order @a list. */
+ void setStatusBarIndicatorOrder(const QList<IndicatorType> &list, const QUuid &uID);
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Returns whether Dock icon should be updated at runtime. */
+ bool realtimeDockIconUpdateEnabled(const QUuid &uID);
+ /** Mac OS X: Defines whether Dock icon update should be fEnabled at runtime. */
+ void setRealtimeDockIconUpdateEnabled(bool fEnabled, const QUuid &uID);
+
+ /** Mac OS X: Returns guest-screen which Dock icon should reflect at runtime. */
+ int realtimeDockIconUpdateMonitor(const QUuid &uID);
+ /** Mac OS X: Defines guest-screen @a iIndex which Dock icon should reflect at runtime. */
+ void setRealtimeDockIconUpdateMonitor(int iIndex, const QUuid &uID);
+
+ /** Mac OS X: Returns whether Dock icon overlay is disabled. */
+ bool dockIconDisableOverlay(const QUuid &uID);
+ /** Mac OS X: Defines whether Dock icon overlay is @a fDisabled. */
+ void setDockIconDisableOverlay(bool fDisabled, const QUuid &uID);
+#endif /* VBOX_WS_MAC */
+
+ /** Returns whether machine should pass CAD to guest. */
+ bool passCADtoGuest(const QUuid &uID);
+
+ /** Returns the mouse-capture policy. */
+ MouseCapturePolicy mouseCapturePolicy(const QUuid &uID);
+
+ /** Returns redefined guru-meditation handler type. */
+ GuruMeditationHandlerType guruMeditationHandlerType(const QUuid &uID);
+
+ /** Returns whether machine should perform HID LEDs synchronization. */
+ bool hidLedsSyncState(const QUuid &uID);
+
+ /** Returns the scale-factor. */
+ double scaleFactor(const QUuid &uID, const int uScreenIndex);
+ QList<double> scaleFactors(const QUuid &uID);
+ /** Saves the @a dScaleFactor for the monitor with @a uScreenIndex. If the existing scale factor
+ * list (from extra data) does not have scale factors for the screens with ids in [0, uScreenIndex)
+ * the this function appends a default scale factor for said screens.*/
+ void setScaleFactor(double dScaleFactor, const QUuid &uID, const int uScreenIndex);
+ /** Replaces the scale factor list of the machine with @a uID with @a scaleFactors. */
+ void setScaleFactors(const QList<double> &scaleFactors, const QUuid &uID);
+
+ /** Returns the scaling optimization type. */
+ ScalingOptimizationType scalingOptimizationType(const QUuid &uID);
+ /** @} */
+
+ /** @name Virtual Machine: Session Information dialog
+ * @{ */
+ /** Returns session information dialog geometry using @a pWidget and @a pParentWidget as hints. */
+ QRect sessionInformationDialogGeometry(QWidget *pWidget, QWidget *pParentWidget);
+ /** Returns whether information-window should be maximized or not. */
+ bool sessionInformationDialogShouldBeMaximized();
+ /** Defines information-window @a geometry and @a fMaximized state. */
+ void setSessionInformationDialogGeometry(const QRect &geometry, bool fMaximized);
+ /** @} */
+
+ /** @name Guest Control related dialogs
+ * @{ */
+ void setGuestControlProcessControlSplitterHints(const QList<int> &hints);
+ QList<int> guestControlProcessControlSplitterHints();
+ QRect fileManagerDialogGeometry(QWidget *pWidget, QWidget *pParentWidget);
+ bool fileManagerDialogShouldBeMaximized();
+ void setFileManagerDialogGeometry(const QRect &geometry, bool fMaximized);
+ QRect guestProcessControlDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry);
+ bool guestProcessControlDialogShouldBeMaximized();
+ void setGuestProcessControlDialogGeometry(const QRect &geometry, bool fMaximized);
+ void setFileManagerVisiblePanels(const QStringList &panelNameList);
+ QStringList fileManagerVisiblePanels();
+ /** @} */
+
+ /** @name Soft Keyboard
+ * @{ */
+ QRect softKeyboardDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry);
+ void setSoftKeyboardDialogGeometry(const QRect &geometry, bool fMaximized);
+ bool softKeyboardDialogShouldBeMaximized();
+ void setSoftKeyboardOptions(bool fShowNumPad, bool fHideOSMenuKeys, bool fMultimediaKeys);
+ void softKeyboardOptions(bool &fOutShowNumPad, bool &fOutHideOSMenuKeys, bool &fOutHideMultimediaKeys);
+ void setSoftKeyboardColorTheme(const QStringList &colorStringList);
+ QStringList softKeyboardColorTheme();
+ void setSoftKeyboardSelectedColorTheme(const QString &strColorThemeName);
+ QString softKeyboardSelectedColorTheme();
+ void setSoftKeyboardSelectedLayout(const QUuid &uLayoutUid);
+ QUuid softKeyboardSelectedLayout();
+ /** @} */
+
+ /** @name File Manager options
+ * @{ */
+ void setFileManagerOptions(bool fListDirectoriesFirst,
+ bool fShowDeleteConfirmation,
+ bool fshowHumanReadableSizes,
+ bool fShowHiddenObjects);
+ bool fileManagerListDirectoriesFirst();
+ bool fileManagerShowDeleteConfirmation();
+ bool fileManagerShowHumanReadableSizes();
+ bool fileManagerShowHiddenObjects();
+ /** @} */
+
+ /** @name Virtual Machine: Close dialog
+ * @{ */
+ /** Returns default machine close action. */
+ MachineCloseAction defaultMachineCloseAction(const QUuid &uID);
+ /** Returns restricted machine close actions. */
+ MachineCloseAction restrictedMachineCloseActions(const QUuid &uID);
+
+ /** Returns last machine close action. */
+ MachineCloseAction lastMachineCloseAction(const QUuid &uID);
+ /** Defines last @a machineCloseAction. */
+ void setLastMachineCloseAction(MachineCloseAction machineCloseAction, const QUuid &uID);
+
+ /** Returns machine close hook script name as simple string. */
+ QString machineCloseHookScript(const QUuid &uID);
+
+ /** Returns whether machine should discard state on power off. */
+ bool discardStateOnPowerOff(const QUuid &uID);
+ /** @} */
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** @name Virtual Machine: Debug UI
+ * @{ */
+ /** Returns debug flag value for passed @a strDebugFlagKey. */
+ QString debugFlagValue(const QString &strDebugFlagKey);
+ /** @} */
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ /** @name VirtualBox: Extra-data Manager window
+ * @{ */
+ /** Returns Extra-data Manager geometry using @a pWidget and @a pParentWidget as hint. */
+ QRect extraDataManagerGeometry(QWidget *pWidget, QWidget *pParentWidget);
+ /** Returns whether Extra-data Manager should be maximized or not. */
+ bool extraDataManagerShouldBeMaximized();
+ /** Defines Extra-data Manager @a geometry and @a fMaximized state. */
+ void setExtraDataManagerGeometry(const QRect &geometry, bool fMaximized);
+
+ /** Returns Extra-data Manager splitter hints using @a pWidget as hint. */
+ QList<int> extraDataManagerSplitterHints(QWidget *pWidget);
+ /** Defines Extra-data Manager splitter @a hints. */
+ void setExtraDataManagerSplitterHints(const QList<int> &hints);
+ /** @} */
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+ /** @name Virtual Machine: Log Viewer dialog
+ * @{ */
+ /** Returns log-window geometry using @a pWidget, @a pParentWidget and @a defaultGeometry as hints. */
+ QRect logWindowGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry);
+ /** Returns whether log-window should be maximized or not. */
+ bool logWindowShouldBeMaximized();
+ /** Defines log-window @a geometry and @a fMaximized state. */
+ void setLogWindowGeometry(const QRect &geometry, bool fMaximized);
+ /** @} */
+
+ /** @name Virtual Machine: Log Viewer widget options
+ * @{ */
+ void setLogViweverOptions(const QFont &font, bool wrapLines, bool showLineNumbers);
+ /** Returns log-viewer line wrapping flag. */
+ bool logViewerWrapLines();
+ /** Returns log-viewer show line numbers flag. */
+ bool logViewerShowLineNumbers();
+ /** Tries to find system font by searching by family and style strings within the font database. */
+ QFont logViewerFont();
+ void setLogViewerVisiblePanels(const QStringList &panelNameList);
+ QStringList logViewerVisiblePanels();
+ /** @} */
+
+ /** @name Help Browser
+ * @{ */
+ void setHelpBrowserLastUrlList(const QStringList &urlList);
+ QStringList helpBrowserLastUrlList();
+ void setHelpBrowserZoomPercentage(int iZoomPercentage);
+ int helpBrowserZoomPercentage();
+ QRect helpBrowserDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry);
+ void setHelpBrowserDialogGeometry(const QRect &geometry, bool fMaximized);
+ bool helpBrowserDialogShouldBeMaximized();
+ void setHelpBrowserBookmarks(const QStringList &bookmarks);
+ QStringList helpBrowserBookmarks();
+ /** @} */
+
+ /** @name Manager UI: VM Activity Overview
+ * @{ */
+ void setVMActivityOverviewHiddenColumnList(const QStringList &hiddenColumnList);
+ QStringList VMActivityOverviewHiddenColumnList();
+ bool VMActivityOverviewShowAllMachines();
+ void setVMActivityOverviewShowAllMachines(bool fShow);
+ /** @} */
+
+ /** @name Medium Selector
+ * @{ */
+ QRect mediumSelectorDialogGeometry(QWidget *pWidget, QWidget *pParentWidget, const QRect &defaultGeometry);
+ void setMediumSelectorDialogGeometry(const QRect &geometry, bool fMaximized);
+ bool mediumSelectorDialogShouldBeMaximized();
+ /** @} */
+
+private slots:
+
+ /** Handles 'extra-data change' event: */
+ void sltExtraDataChange(const QUuid &uMachineID, const QString &strKey, const QString &strValue);
+
+private:
+
+ /** Prepare Extra-data Manager. */
+ void prepare();
+ /** Prepare global extra-data map. */
+ void prepareGlobalExtraDataMap();
+ /** Prepare extra-data event-handler. */
+ void prepareExtraDataEventHandler();
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ // /** Prepare window. */
+ // void prepareWindow();
+
+ /** Cleanup window. */
+ void cleanupWindow();
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+ /** Cleanup extra-data event-handler. */
+ void cleanupExtraDataEventHandler();
+ // /** Cleanup extra-data map. */
+ // void cleanupExtraDataMap();
+ /** Cleanup Extra-data Manager. */
+ void cleanup();
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ /** Open window. */
+ void open(QWidget *pCenterWidget);
+#endif
+
+ /** Retrieves an extra-data key from both machine and global sources.
+ *
+ * If @a uID isn't #GlobalID, this will first check the extra-data associated
+ * with the machine given by @a uID then fallback on the global extra-data.
+ *
+ * @returns String value if found, null string if not.
+ * @param strKey The extra-data key to get.
+ * @param uID Machine UUID or #GlobalID.
+ * @param strValue Where to return the value when found. */
+ QString extraDataStringUnion(const QString &strKey, const QUuid &uID);
+ /** Determines whether feature corresponding to passed @a strKey is allowed.
+ * If valid @a uID is set => applies to machine and global extra-data,
+ * otherwise => only to global one. */
+ bool isFeatureAllowed(const QString &strKey, const QUuid &uID = GlobalID);
+ /** Determines whether feature corresponding to passed @a strKey is restricted.
+ * If valid @a uID is set => applies to machine and global extra-data,
+ * otherwise => only to global one. */
+ bool isFeatureRestricted(const QString &strKey, const QUuid &uID = GlobalID);
+
+ /** Translates bool flag into QString value. */
+ QString toFeatureState(bool fState);
+ /** Translates bool flag into 'allowed' value. */
+ QString toFeatureAllowed(bool fAllowed);
+ /** Translates bool flag into 'restricted' value. */
+ QString toFeatureRestricted(bool fRestricted);
+
+ /** Defines saved dialog geometry according to specified attributes.
+ * @param strKey Brings geometry extra-data key of particular dialog.
+ * @param geometry Brings the dialog geometry to save.
+ * @param fMaximized Brings whether saved dialog geometry should be marked as maximized. */
+ void setDialogGeometry(const QString &strKey, const QRect &geometry, bool fMaximized);
+ /** Returns saved dialog geometry according to specified attributes.
+ * @param strKey Brings geometry extra-data key of particular dialog.
+ * @param pWidget Brings the widget to limit geometry bounds according to.
+ * @param pParentWidget Brings the widget to center geometry rectangle according to.
+ * @param defaultGeometry Brings the default geometry which should be used to
+ * calculate resulting geometry if saved was not found. */
+ QRect dialogGeometry(const QString &strKey, QWidget *pWidget, QWidget *pParentWidget = 0, const QRect &defaultGeometry = QRect());
+ /** Returns true if the dialog should be maximized.
+ * @param strKey Brings geometry extra-data key of particular dialog. */
+ bool dialogShouldBeMaximized(const QString &strKey);
+
+ /** Returns string consisting of @a strBase appended with @a uScreenIndex for the *non-primary* screen-index.
+ * If @a fSameRuleForPrimary is 'true' same rule will be used for *primary* screen-index. Used for storing per-screen extra-data. */
+ static QString extraDataKeyPerScreen(const QString &strBase, ulong uScreenIndex, bool fSameRuleForPrimary = false);
+
+ /** Holds the singleton instance. */
+ static UIExtraDataManager *s_pInstance;
+
+ /** Holds extra-data event-handler instance. */
+ UIExtraDataEventHandler *m_pHandler;
+
+ /** Holds extra-data map instance. */
+ MapOfExtraDataMaps m_data;
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ /** Holds Extra-data Manager window instance. */
+ QPointer<UIExtraDataManagerWindow> m_pWindow;
+#endif
+};
+
+/** Singleton Extra-data Manager 'official' name. */
+#define gEDataManager UIExtraDataManager::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_extradata_UIExtraDataManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/CIShared.h b/src/VBox/Frontends/VirtualBox/src/globals/CIShared.h
new file mode 100644
index 00000000..a5aff479
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/CIShared.h
@@ -0,0 +1,421 @@
+/* $Id: CIShared.h $ */
+/** @file
+ * VBox Qt GUI - Common VirtualBox classes: CIShared class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_CIShared_h
+#define FEQT_INCLUDED_SRC_globals_CIShared_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef VBOX_CHECK_STATE
+#include <stdio.h>
+#endif
+
+template< class D >
+class CIShared
+{
+ /** @internal
+ *
+ * A class that derives the data structure managed by the CIShared template
+ * (passed as a template parameter) for some internal purposes, such as the
+ * reference count, etc. There is no need to use this class directly.
+ */
+ class Data : public D
+ {
+ enum { Orig = 0x01, Null = 0x02 };
+
+ Data() : cnt( 1 ), state( Orig ) {}
+ Data( const Data &d ) : D( d ), cnt( 1 ), state( d.state & (~Orig) ) {}
+ Data &operator=( const Data &d ) {
+ D::operator=( d );
+ state &= ~Orig;
+ return *this;
+ }
+ // a special constructor to create a null value
+ Data( void* ) : cnt( 1 ), state( Null ) {}
+#ifdef VBOX_CHECK_STATE
+ virtual ~Data();
+ void ref();
+ bool deref();
+#else
+ virtual ~Data() {}
+ void ref() { cnt++; }
+ bool deref() { return !--cnt; }
+#endif // VBOX_CHECK_STATE
+
+ int cnt;
+ int state;
+
+ friend class CIShared<D>;
+ };
+
+public:
+ CIShared( bool null = true ) : d( null ? Null.d->ref(), Null.d : new Data() ) {}
+ CIShared( const CIShared &that ) : d( that.d ) { d->ref(); }
+ CIShared &operator=( const CIShared &that ) {
+ that.d->ref();
+ if ( d->deref() ) delete d;
+ d = that.d;
+ return *this;
+ }
+ virtual ~CIShared() { if ( d->deref() ) delete d; }
+
+ bool isOriginal() const { return (d->state != 0); }
+ bool isNull() const { return ((d->state & Data::Null) != 0); }
+
+ bool detach();
+ bool detachOriginal();
+
+ CIShared copy() const {
+ return isNull() ? CIShared( Null ) : CIShared( new Data( *d ) );
+ }
+
+ const D *data() const { return d; }
+ inline D *mData();
+
+ bool operator==( const CIShared &that ) const {
+ return (d == that.d) || (*d == *(that.d));
+ }
+
+ // convenience operators
+ const D *operator->() const { return data(); }
+ bool operator!() const { return isNull(); }
+
+private:
+ CIShared( Data *aData ) : d( aData ) {}
+ Data *d;
+
+ static CIShared Null;
+};
+
+/** @class CIShared
+ *
+ * This template allows to implement the implicit sharing
+ * semantics for user-defined data structures.
+ *
+ * The template argument is a structure (or a class) whose objects
+ * need to be implicitly shared by different pieces of code. A class
+ * generated from this template acts as a wrapper for that structure
+ * and provides a safe access (from the shared usage point of view) to its
+ * members. Note that simple C++ types (such as int) cannot be used as
+ * template arguments.
+ *
+ * Implicit sharing means that instances of the generated class point to the
+ * same data object of the managed structure until any one of them tries
+ * to change it. When it happens that instance makes a deep copy of the object
+ * (through its copy constructor) and does the actual change on that copy,
+ * keeping the original data unchanged. This technique is also called
+ * "copy on write". Also, any instance can excplicitly stop sharing the data
+ * it references at any time by calling the detach() method directly, which
+ * makes a copy if the data is referenced by more than one instance.
+ *
+ * The read-only access to the managed data can be obtained using the
+ * data() method that returns a pointer to the constant data of the type
+ * used as a template argument. The pointer to the non-constant data
+ * is returned by the mData() method, that automatically detaches the
+ * instance if necessary. This method should be used with care, and only
+ * when it is really necessary to change the data -- if you will use it for
+ * the read-only access the implicit sharing will not work because every
+ * instance will have its data detached.
+ *
+ * To be able to be used with the VShared template the structure/class
+ * must have public (or protected) constructors and a destructor. If it
+ * doesn't contain pointers as its members then the two constructors
+ * (the default and the copy constructor) and the destructor automatically
+ * generated by the compiler are enough, there's no need to define them
+ * explicitly. If the destructor is defined explicitly it must be
+ * virtual.
+ *
+ * The default constructor implemented by this template (it is actually
+ * a constructor with one bool argument that defaults to false) creates
+ * a null instance (i.e. its isNull() method returns false). All null
+ * instances share the same internal data object (created by the default
+ * constructor of the managed structure) and provide only a read-only access
+ * to its members. This means that the mData() method of such an instance
+ * will always return a null pointer and an attempt to access its members
+ * through that pointer will most likely cause a memory access violation
+ * exception. The template doesn't provide any other constructors (except
+ * the copy constructor) because it doesn't know how to initialize the
+ * object of the managed structure, so the only way to create a non-null
+ * instance is to pass true to the constructor mentioned above.
+ *
+ * It's a good practice not to use instantiations of this template directly
+ * but derive them instead. This gives an opportunity to define necessary
+ * constructors with arguments that initialize the managed structure, as
+ * well as to define convenient methods to access structure members (instead
+ * of defining them in the structure itself). For example:
+ *
+ * @code
+ *
+ * // a data structure
+ * struct ACardData {
+ * string name;
+ * // commented out -- not so convenient:
+ * // void setName( const string &n ) { name = n; }
+ * }
+ *
+ * // a wrapper
+ * class ACard : publc CIShared< ACardData > {
+ * ACardData() {} // the default constructor should be visible
+ * ACardData( const string &name ) :
+ * CIShared< ACardData >( false ) // make non-null
+ * {
+ * mData()->name = name;
+ * }
+ * string name() const { return data()->name; }
+ * void setName( const string &name ) { mData()->name = name; }
+ * }
+ *
+ * // ...
+ * ACard c( "John" );
+ * // ...
+ * c.setName( "Ivan" );
+ * // the above is shorter than c.data()->name or c.mData()->setName()
+ *
+ * @endcode
+ *
+ * If some members of the structure need to be private (and therefore
+ * inaccessible through the pointers returned by data() and vData()) you can
+ * simply declare the wrapper class (the ACard class in the example above)
+ * as a friend of the structure and still use the above approach.
+ *
+ * For public members of the original structure it's also possible to use
+ * the overloaded operator->(), which is the equivalent of calling the data()
+ * method, i.e.:
+ *
+ * @code
+ * // ...
+ * cout << c->name;
+ * @endcode
+ *
+ * The operator!() is overloaded for convenience and is equivalent to the
+ * isNull() method.
+ *
+ * The operator==() makes a comparison of two instances.
+ *
+ * @todo put the "original" state definition here...
+ */
+
+/** @internal
+ *
+ * A special null value for internal usage. All null instances created
+ * with the default constructor share the data object it contains.
+ */
+template< class D > CIShared<D> CIShared<D>::Null = CIShared( new Data( 0 ) );
+
+/** @fn CIShared::CIShared( bool null = true )
+ *
+ * Creates a new instance. If the argument is true (which is the default)
+ * a null instance is created. All null instances share the same data
+ * object created using the default constructor of the managed structure
+ * (i.e. specified as template argument when instantiating).
+ *
+ * If the argument is false an empty instance is created. The empty instance
+ * differs from the null instance such that the created data object is
+ * initially non-shared and the mData() method returns a valid pointer
+ * suitable for modifying the data.
+ *
+ * The instance created by this constructor is initially original.
+ *
+ * @see isNull, isOriginal
+ */
+
+/** @fn CIShared::CIShared( const CIShared & )
+ *
+ * Creates a new instance and initializes it by a reference to the same data
+ * object as managed by the argument. No copies of the data are created.
+ * The created instance becomes null and/or original if the argument is null
+ * and/or original, respectively.
+ *
+ * @see isNull, isOriginal
+ */
+
+/** @fn CIShared::operator=( const CIShared & )
+ *
+ * Assigns a new value to this instance by instructing it to refer to the
+ * same data as managed by the argument. No copies of the data are created.
+ * The previous data is automatically deleted if there are no more references
+ * to it. The instance becomes null and/or original if the argument is null
+ * and/or original, respectively.
+ */
+
+/** @fn CIShared::copy() const
+ *
+ * Returns a "deep" copy of the instance. The returned instance always
+ * contains its own (not yet shared) copy of the managed data, even if the
+ * data wasn't shared before this call. The new copy becomes not original
+ * if it is not null, otherwise it remains null.
+ *
+ * @see isNull, isOriginal
+ */
+
+/** @fn CIShared::data() const
+ *
+ * Returns a pointer to the object of the managed structure that is suitable
+ * for a read-only access. Does <b>not</b> do an implicit detach(), the
+ * data remains shared.
+ *
+ * @see mData()
+ */
+
+/** @fn CIShared::operator==( const CIShared & ) const
+ *
+ * Compares this instance and the argument. Two instances are considered
+ * to be equal if they share the same data object or if data objects they
+ * share are equal. Data objects are compared using the comparison operator
+ * of the managed structure.
+ */
+
+/**
+ * Detaches this instance from other instances it shares the data with by
+ * making the copy of the data. This instance becomes "non-original". The
+ * method does nothing and returns false if this instance is null or its
+ * data is not shared among (referenced by) other instances.
+ *
+ * @return true if it does a real detach and false otherwise.
+ *
+ * @see isOriginal, isNull
+ */
+template< class D > bool CIShared<D>::detach() {
+ if ( !(d->state & Data::Null) && d->cnt > 1 ) {
+ d->deref();
+ d = new Data( *d );
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Detaches this instance from other instances it shares the data with by
+ * making the copy of the data. This instance becomes "original" (even if
+ * it wasn't original before a detach), all other instances that previously
+ * shared the same data will become "non-original". The method does nothing
+ * and returns false if this instance is null. If its data is not shared
+ * among (referenced by) other instances it marks it as original and
+ * also returns false.
+ *
+ * @return true if it does a real detach and false otherwise.
+ *
+ * @see isOriginal, isNull
+ */
+template< class D > bool CIShared<D>::detachOriginal() {
+ if ( !(d->state & Data::Null) ) {
+ if ( d->cnt > 1 ) {
+ d->deref();
+ d->state &= ~Data::Orig;
+ d = new Data( *d );
+ d->state |= Data::Orig;
+ return true;
+ }
+ d->state |= Data::Orig;
+ }
+ return false;
+}
+
+/** @fn CIShared::isOriginal() const
+ *
+ * Returns true if the data is the original data and false otherwise.
+ * The data is considered to be original until it is changed through the
+ * mData() member or directly detached by detach(). Also, the data can be
+ * made original at any time using the detachOriginal() method.
+ *
+ * Note, that this method always returns true for null instances.
+ *
+ * @see detachOriginal, isNull
+ */
+
+/** @fn CIShared::isNull() const
+ *
+ * Returns true if this instance is a special null value. All null values
+ * share the same data object created by the default constructor of
+ * the managed structure. A null instance gives a read-only access to the
+ * managed data.
+ *
+ * @see vData
+ */
+
+/**
+ * Returns a pointer to the object of the managed structure that is suitable
+ * for modifying data. Does an implicit detach() if this data object is
+ * referenced by more than one instance, making this instance non-original.
+ *
+ * This method should be called only when it's really necessary to change
+ * the data object, read-only access should be obtained using the data()
+ * member. Otherwise there all data objects will be detached and non-shared.
+ *
+ * @warning This method returns a null pointer for instances that are
+ * null. Accessing data through that pointer will most likely cause a
+ * memory access violation exception.
+ *
+ * @see data, isNull, isOriginal
+ */
+template< class D > inline D *CIShared<D>::mData() {
+ if ( d->state & Data::Null ) {
+#ifdef VBOX_CHECK_STATE
+ printf( "CIShared::mData(): a null instance, returning a null pointer!" );
+#endif
+ return 0;
+ }
+ if ( d->cnt > 1 )
+ detach();
+ return d;
+}
+
+// CIShared<D>::Data debug methods
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef VBOX_CHECK_STATE
+
+template< class D > CIShared<D>::Data::~Data() {
+ if ( cnt )
+ printf( "~Data(): ref count is %d, but must be zero!\n", cnt );
+}
+
+template< class D > void CIShared<D>::Data::ref() {
+ if ( cnt <= 0 )
+ printf(
+ "Data::ref() ref count was %d, "
+ "but must be greater than zero!\n",
+ cnt
+ );
+ cnt++;
+}
+
+template< class D > bool CIShared<D>::Data::deref() {
+ if ( cnt <= 0 )
+ printf(
+ "Data::ref() ref count was %d, "
+ "but must be greater than zero!\n",
+ cnt
+ );
+ return !--cnt;
+}
+
+#endif // VBOX_CHECK_STATE
+
+#endif /* !FEQT_INCLUDED_SRC_globals_CIShared_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/COMDefs.cpp b/src/VBox/Frontends/VirtualBox/src/globals/COMDefs.cpp
new file mode 100644
index 00000000..f51c3e18
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/COMDefs.cpp
@@ -0,0 +1,479 @@
+/* $Id: COMDefs.cpp $ */
+/** @file
+ * VBox Qt GUI - CInterface implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QSocketNotifier>
+
+/* GUI includes: */
+#include "COMDefs.h"
+
+/* COM includes: */
+#include "CVirtualBoxErrorInfo.h"
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+/* Other VBox includes: */
+#include <iprt/log.h>
+
+#ifdef VBOX_WITH_XPCOM
+
+/* Other VBox includes: */
+# include <nsEventQueueUtils.h>
+# include <nsIEventQueue.h>
+# include <nsIExceptionService.h>
+
+/* Mac OS X (Carbon mode) and OS/2 will notify the native queue
+ internally in plevent.c. Because moc doesn't seems to respect
+ #ifdefs, we still have to include the definition of the class.
+ very silly. */
+# if !defined (Q_OS_MAC) && !defined (Q_OS_OS2)
+XPCOMEventQSocketListener *COMBase::sSocketListener = 0;
+
+# endif
+
+/**
+ * Internal class to asynchronously handle IPC events on the GUI thread
+ * using the event queue socket FD and QSocketNotifier.
+ */
+class XPCOMEventQSocketListener : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ XPCOMEventQSocketListener (nsIEventQueue *eq)
+ {
+ mEventQ = eq;
+ mNotifier = new QSocketNotifier (mEventQ->GetEventQueueSelectFD(),
+ QSocketNotifier::Read, this);
+ QObject::connect (mNotifier, SIGNAL (activated (int)),
+ this, SLOT (processEvents()));
+ }
+
+ virtual ~XPCOMEventQSocketListener()
+ {
+ delete mNotifier;
+ }
+
+public slots:
+
+ void processEvents() { mEventQ->ProcessPendingEvents(); }
+
+private:
+
+ QSocketNotifier *mNotifier;
+ nsCOMPtr <nsIEventQueue> mEventQ;
+};
+
+#endif /* !defined (VBOX_WITH_XPCOM) */
+
+/**
+ * Initializes COM/XPCOM.
+ */
+HRESULT COMBase::InitializeCOM(bool fGui)
+{
+ LogFlowFuncEnter();
+
+ HRESULT rc = com::Initialize(fGui ? VBOX_COM_INIT_F_DEFAULT | VBOX_COM_INIT_F_GUI : VBOX_COM_INIT_F_DEFAULT);
+
+#if defined (VBOX_WITH_XPCOM)
+
+# if !defined (RT_OS_DARWIN) && !defined (RT_OS_OS2)
+
+ if (NS_SUCCEEDED (rc))
+ {
+ nsCOMPtr <nsIEventQueue> eventQ;
+ rc = NS_GetMainEventQ (getter_AddRefs (eventQ));
+ if (NS_SUCCEEDED (rc))
+ {
+# ifdef DEBUG
+ BOOL isNative = FALSE;
+ eventQ->IsQueueNative (&isNative);
+ AssertMsg (isNative, ("The event queue must be native"));
+# endif
+ BOOL isOnMainThread = FALSE;
+ rc = eventQ->IsOnCurrentThread (&isOnMainThread);
+ if (NS_SUCCEEDED (rc) && isOnMainThread)
+ {
+ sSocketListener = new XPCOMEventQSocketListener (eventQ);
+ }
+ }
+ }
+
+# endif /* !defined (RT_OS_DARWIN) && !defined (RT_OS_OS) */
+
+#endif /* defined (VBOX_WITH_XPCOM) */
+
+ if (FAILED (rc))
+ CleanupCOM();
+
+ AssertComRC (rc);
+
+ LogFlowFunc (("rc=%08X\n", rc));
+ LogFlowFuncLeave();
+ return rc;
+
+}
+
+/**
+ * Cleans up COM/XPCOM.
+ */
+HRESULT COMBase::CleanupCOM()
+{
+ LogFlowFuncEnter();
+
+ HRESULT rc = S_OK;
+
+#if defined (VBOX_WITH_XPCOM)
+
+ /* scope the code to make smart references are released before calling
+ * com::Shutdown() */
+ {
+ nsCOMPtr <nsIEventQueue> eventQ;
+ rc = NS_GetMainEventQ (getter_AddRefs (eventQ));
+ if (NS_SUCCEEDED (rc))
+ {
+ BOOL isOnMainThread = FALSE;
+ rc = eventQ->IsOnCurrentThread (&isOnMainThread);
+ if (NS_SUCCEEDED (rc) && isOnMainThread)
+ {
+# if !defined (RT_OS_DARWIN) && !defined (RT_OS_OS2)
+ if (sSocketListener)
+ {
+ delete sSocketListener;
+ sSocketListener = NULL;
+ }
+# endif
+ }
+ }
+ }
+
+#endif /* defined (VBOX_WITH_XPCOM) */
+
+ HRESULT rc2 = com::Shutdown();
+ if (SUCCEEDED (rc))
+ rc = rc2;
+
+ AssertComRC (rc);
+
+ LogFlowFunc (("rc=%08X\n", rc));
+ LogFlowFuncLeave();
+ return rc;
+}
+
+/* static */
+void COMBase::ToSafeArray (const QVector <QString> &aVec,
+ com::SafeArray <BSTR> &aArr)
+{
+ aArr.reset (aVec.size());
+ for (int i = 0; i < aVec.size(); ++ i)
+ aArr [i] = SysAllocString ((const OLECHAR *)
+ (aVec.at (i).isNull() ? 0 : aVec.at (i).utf16()));
+}
+
+/* static */
+void COMBase::FromSafeArray (const com::SafeArray <BSTR> &aArr,
+ QVector <QString> &aVec)
+{
+ aVec.resize (static_cast <int> (aArr.size()));
+ for (int i = 0; i < aVec.size(); ++ i)
+ aVec [i] = QString::fromUtf16 (aArr [i]);
+}
+
+/* static */
+void COMBase::ToSafeArray (const QVector <QUuid> &aVec,
+ com::SafeGUIDArray &aArr)
+{
+ AssertCompileSize (GUID, sizeof (QUuid));
+ aArr.reset (aVec.size());
+ for (int i = 0; i < aVec.size(); ++ i)
+ aArr [i] = *(GUID*) &aVec [i];
+}
+
+/* static */
+void COMBase::FromSafeArray (const com::SafeGUIDArray &aArr,
+ QVector <QUuid> &aVec)
+{
+ AssertCompileSize (GUID, sizeof (QUuid));
+ aVec.resize (static_cast <int> (aArr.size()));
+ for (int i = 0; i < aVec.size(); ++ i)
+ {
+#ifdef VBOX_WITH_XPCOM
+ aVec [i] = *(QUuid*) &aArr [i];
+#else
+ /* No by-reference accessor, only by-value. So spell it out to avoid warnings. */
+ GUID Tmp = aArr[i];
+ aVec[i] = *(QUuid *)&Tmp;
+#endif
+ }
+}
+
+/* static */
+void COMBase::ToSafeArray (const QVector <QUuid> &aVec,
+ com::SafeArray <BSTR> &aArr)
+{
+ aArr.reset (aVec.size());
+ for (int i = 0; i < aVec.size(); ++ i)
+ aArr [i] = SysAllocString ((const OLECHAR *)
+ (aVec.at (i).isNull() ? 0 : aVec.at(i).toString().utf16()));
+}
+
+/* static */
+void COMBase::FromSafeArray (const com::SafeArray <BSTR> &aArr,
+ QVector <QUuid> &aVec)
+{
+ aVec.resize (static_cast <int> (aArr.size()));
+ for (int i = 0; i < aVec.size(); ++ i)
+ aVec [i] = QUuid(QString::fromUtf16 (aArr [i]));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void COMErrorInfo::init(const CVirtualBoxErrorInfo &info)
+{
+ if (info.isNull())
+ {
+ mIsNull = true;
+ mIsBasicAvailable = false;
+ mIsFullAvailable = false;
+ mResultCode = S_OK;
+ m_pNext = NULL;
+ AssertMsgFailedReturnVoid(("error info is NULL!\n"));
+ }
+
+ bool gotSomething = false;
+ bool gotAll = true;
+
+ mResultCode = info.GetResultCode();
+ gotSomething |= info.isOk();
+ gotAll &= info.isOk();
+
+ mInterfaceID = info.GetInterfaceID();
+ gotSomething |= info.isOk();
+ gotAll &= info.isOk();
+ if (info.isOk())
+ mInterfaceName = getInterfaceNameFromIID (mInterfaceID);
+
+ mComponent = info.GetComponent();
+ gotSomething |= info.isOk();
+ gotAll &= info.isOk();
+
+ mText = info.GetText();
+ gotSomething |= info.isOk();
+ gotAll &= info.isOk();
+
+ m_pNext = NULL;
+
+ CVirtualBoxErrorInfo next = info.GetNext();
+ if (info.isOk() && !next.isNull())
+ {
+ m_pNext = new COMErrorInfo(next);
+ Assert(m_pNext);
+ }
+
+ gotSomething |= info.isOk();
+ gotAll &= info.isOk();
+
+ mIsBasicAvailable = gotSomething;
+ mIsFullAvailable = gotAll;
+
+ mIsNull = !gotSomething;
+
+ AssertMsg (gotSomething, ("Nothing to fetch!\n"));
+}
+
+void COMErrorInfo::copyFrom(const COMErrorInfo &x)
+{
+ mIsNull = x.mIsNull;
+ mIsBasicAvailable = x.mIsBasicAvailable;
+ mIsFullAvailable = x.mIsFullAvailable;
+
+ mResultCode = x.mResultCode;
+ mInterfaceID = x.mInterfaceID;
+ mComponent = x.mComponent;
+ mText = x.mText;
+
+ if (x.m_pNext)
+ m_pNext = new COMErrorInfo(*x.m_pNext);
+ else
+ m_pNext = NULL;
+
+ mInterfaceName = x.mInterfaceName;
+ mCalleeIID = x.mCalleeIID;
+ mCalleeName = x.mCalleeName;
+}
+
+void COMErrorInfo::cleanup()
+{
+ if (m_pNext)
+ {
+ delete m_pNext;
+ m_pNext = NULL;
+ }
+}
+
+/**
+ * Fetches error info from the current thread.
+ * If callee is NULL, then error info is fetched in "interfaceless"
+ * manner (so calleeIID() and calleeName() will return null).
+ *
+ * @param callee
+ * pointer to the interface whose method returned an error
+ * @param calleeIID
+ * UUID of the callee's interface. Ignored when callee is NULL
+ */
+void COMErrorInfo::fetchFromCurrentThread(IUnknown *callee, const GUID *calleeIID)
+{
+ mIsNull = true;
+ mIsFullAvailable = mIsBasicAvailable = false;
+
+ AssertReturnVoid(!callee || calleeIID);
+
+ HRESULT rc = E_FAIL;
+
+#if !defined(VBOX_WITH_XPCOM)
+
+ if (callee)
+ {
+ ComPtr<IUnknown> iface(callee);
+ ComPtr<ISupportErrorInfo> serr(iface);
+ if (!serr)
+ return;
+ rc = serr->InterfaceSupportsErrorInfo(*calleeIID);
+ if (!SUCCEEDED(rc))
+ return;
+ }
+
+ ComPtr<IErrorInfo> err;
+ rc = ::GetErrorInfo(0, err.asOutParam());
+ if (rc == S_OK && err)
+ {
+ ComPtr<IVirtualBoxErrorInfo> info(err);
+ if (info)
+ init(CVirtualBoxErrorInfo(info));
+
+ if (!mIsFullAvailable)
+ {
+ bool gotSomething = false;
+
+ rc = err->GetGUID(COMBase::GUIDOut(mInterfaceID));
+ gotSomething |= SUCCEEDED(rc);
+ if (SUCCEEDED(rc))
+ mInterfaceName = getInterfaceNameFromIID(mInterfaceID);
+
+ rc = err->GetSource(COMBase::BSTROut(mComponent));
+ gotSomething |= SUCCEEDED(rc);
+
+ rc = err->GetDescription(COMBase::BSTROut(mText));
+ gotSomething |= SUCCEEDED(rc);
+
+ if (gotSomething)
+ mIsBasicAvailable = true;
+
+ mIsNull = !gotSomething;
+
+ AssertMsg(gotSomething,("Nothing to fetch!\n"));
+ }
+ }
+
+#else /* defined(VBOX_WITH_XPCOM) */
+
+ nsCOMPtr<nsIExceptionService> es;
+ es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
+ if (NS_SUCCEEDED(rc))
+ {
+ nsCOMPtr<nsIExceptionManager> em;
+ rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
+ if (NS_SUCCEEDED(rc))
+ {
+ nsCOMPtr<nsIException> ex;
+ rc = em->GetCurrentException(getter_AddRefs(ex));
+ if (NS_SUCCEEDED(rc) && ex)
+ {
+ nsCOMPtr<IVirtualBoxErrorInfo> info;
+ info = do_QueryInterface(ex, &rc);
+ if (NS_SUCCEEDED(rc) && info)
+ init(CVirtualBoxErrorInfo(info));
+
+ if (!mIsFullAvailable)
+ {
+ bool gotSomething = false;
+
+ rc = ex->GetResult(&mResultCode);
+ gotSomething |= NS_SUCCEEDED(rc);
+
+ char *message = NULL; // utf8
+ rc = ex->GetMessage(&message);
+ gotSomething |= NS_SUCCEEDED(rc);
+ if (NS_SUCCEEDED(rc) && message)
+ {
+ mText = QString::fromUtf8(message);
+ nsMemory::Free(message);
+ }
+
+ if (gotSomething)
+ mIsBasicAvailable = true;
+
+ mIsNull = !gotSomething;
+
+ AssertMsg(gotSomething, ("Nothing to fetch!\n"));
+ }
+
+ // set the exception to NULL (to emulate Win32 behavior)
+ em->SetCurrentException(NULL);
+
+ rc = NS_OK;
+ }
+ }
+ }
+
+ AssertComRC(rc);
+
+#endif /* !defined(VBOX_WITH_XPCOM) */
+
+ if (callee && calleeIID && mIsBasicAvailable)
+ {
+ mCalleeIID = COMBase::ToQUuid(*calleeIID);
+ mCalleeName = getInterfaceNameFromIID(mCalleeIID);
+ }
+}
+
+// static
+QString COMErrorInfo::getInterfaceNameFromIID (const QUuid &id)
+{
+ QString name;
+
+ com::GetInterfaceNameByIID (COMBase::GUIDIn (id), COMBase::BSTROut (name));
+
+ return name;
+}
+
+#if defined (VBOX_WITH_XPCOM)
+#include "COMDefs.moc"
+#endif
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/COMDefs.h b/src/VBox/Frontends/VirtualBox/src/globals/COMDefs.h
new file mode 100644
index 00000000..6dd094de
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/COMDefs.h
@@ -0,0 +1,1157 @@
+/* $Id: COMDefs.h $ */
+/** @file
+ * VBox Qt GUI - Various COM definitions and COM wrapper class declarations.
+ *
+ * This header is used in conjunction with the header generated from
+ * XIDL expressed interface definitions to provide cross-platform Qt-based
+ * interface wrapper classes.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_COMDefs_h
+#define FEQT_INCLUDED_SRC_globals_COMDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/** @defgroup grp_QT_COM Qt-COM Support Layer
+ * @{
+ *
+ * The Qt-COM support layer provides a set of definitions and smart classes for
+ * writing simple, clean and platform-independent code to access COM/XPCOM
+ * components through exposed COM interfaces. This layer is based on the
+ * COM/XPCOM Abstraction Layer library (the VBoxCOM glue library defined in
+ * include/VBox/com and implemented in src/VBox/Main/glue).
+ *
+ * ...
+ *
+ * @defgroup grp_QT_COM_arrays Arrays
+ * @{
+ *
+ * COM/XPCOM arrays are mapped to QVector objects. QVector templates declared
+ * with a type that corresponds to the COM type of elements in the array using
+ * normal Qt-COM type mapping rules. Here is a code example that demonstrates
+ * how to call interface methods that take and return arrays (this example is
+ * based on examples given in @ref grp_COM_arrays):
+ * @code
+
+ CSomething component;
+
+ // ...
+
+ QVector<LONG> in(3);
+ in[0] = -1;
+ in[1] = -2;
+ in[2] = -3;
+
+ QVector<LONG> out;
+ QVector<LONG> ret;
+
+ ret = component.TestArrays(in, out);
+
+ for (size_t i = 0; i < ret.size(); ++ i)
+ LogFlow(("*** ret[%u]=%d\n", i, ret[i]));
+
+ * @endcode
+ * @}
+ */
+
+/* Both VBox/com/assert.h and qglobal.h contain a definition of ASSERT.
+ * Either of them can be already included here, so try to shut them up. */
+#undef ASSERT
+
+#include <VBox/com/com.h>
+#include <VBox/com/array.h>
+
+#undef ASSERT
+
+/* Qt includes */
+#include <QString>
+#include <QUuid>
+#include <QVector>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/*
+ * Additional COM / XPCOM defines and includes
+ */
+
+#if !defined(VBOX_WITH_XPCOM)
+
+#else /* !defined(VBOX_WITH_XPCOM) */
+
+#include <nsXPCOM.h>
+#include <nsMemory.h>
+#include <nsIComponentManager.h>
+
+class XPCOMEventQSocketListener;
+
+#endif /* !defined(VBOX_WITH_XPCOM) */
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CVirtualBoxErrorInfo;
+
+/** Represents extended error information */
+class SHARED_LIBRARY_STUFF COMErrorInfo
+{
+public:
+
+ COMErrorInfo()
+ : mIsNull(true),
+ mIsBasicAvailable(false),
+ mIsFullAvailable(false),
+ mResultCode(S_OK),
+ m_pNext(NULL)
+ {}
+
+ COMErrorInfo(const COMErrorInfo &info)
+ {
+ copyFrom(info);
+ }
+
+ COMErrorInfo(const CVirtualBoxErrorInfo &info)
+ {
+ init(info);
+ }
+
+ ~COMErrorInfo()
+ {
+ cleanup();
+ }
+
+ COMErrorInfo& operator=(const COMErrorInfo &info)
+ {
+ cleanup();
+ copyFrom(info);
+ return *this;
+ }
+
+ bool isNull() const { return mIsNull; }
+
+ bool isBasicAvailable() const { return mIsBasicAvailable; }
+ bool isFullAvailable() const { return mIsFullAvailable; }
+
+ HRESULT resultCode() const { return mResultCode; }
+ QUuid interfaceID() const { return mInterfaceID; }
+ QString component() const { return mComponent; }
+ QString text() const { return mText; }
+
+ const COMErrorInfo *next() const { return m_pNext; }
+
+ QString interfaceName() const { return mInterfaceName; }
+ QUuid calleeIID() const { return mCalleeIID; }
+ QString calleeName() const { return mCalleeName; }
+
+private:
+ void init(const CVirtualBoxErrorInfo &info);
+ void copyFrom(const COMErrorInfo &x);
+ void cleanup();
+
+ void fetchFromCurrentThread(IUnknown *callee, const GUID *calleeIID);
+
+ static QString getInterfaceNameFromIID(const QUuid &id);
+
+ bool mIsNull : 1;
+ bool mIsBasicAvailable : 1;
+ bool mIsFullAvailable : 1;
+
+ HRESULT mResultCode;
+ QUuid mInterfaceID;
+ QString mComponent;
+ QString mText;
+
+ COMErrorInfo *m_pNext;
+
+ QString mInterfaceName;
+ QUuid mCalleeIID;
+ QString mCalleeName;
+
+ friend class COMBaseWithEI;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Base COM class the CInterface template and all wrapper classes are derived
+ * from. Provides common functionality for all COM wrappers.
+ */
+class SHARED_LIBRARY_STUFF COMBase
+{
+public:
+
+ static HRESULT InitializeCOM(bool fGui);
+ static HRESULT CleanupCOM();
+
+ /**
+ * Returns the result code of the last interface method called by the
+ * wrapper instance or the result of CInterface::createInstance()
+ * operation.
+ */
+ HRESULT lastRC() const { return mRC; }
+
+#if !defined(VBOX_WITH_XPCOM)
+
+ /** Converts a GUID value to QUuid */
+ static QUuid ToQUuid(const GUID &id)
+ {
+ return QUuid(id.Data1, id.Data2, id.Data3,
+ id.Data4[0], id.Data4[1], id.Data4[2], id.Data4[3],
+ id.Data4[4], id.Data4[5], id.Data4[6], id.Data4[7]);
+ }
+
+#else /* !defined(VBOX_WITH_XPCOM) */
+
+ /** Converts a GUID value to QUuid */
+ static QUuid ToQUuid(const nsID &id)
+ {
+ return QUuid(id.m0, id.m1, id.m2,
+ id.m3[0], id.m3[1], id.m3[2], id.m3[3],
+ id.m3[4], id.m3[5], id.m3[6], id.m3[7]);
+ }
+
+#endif /* !defined(VBOX_WITH_XPCOM) */
+
+ /* Arrays of arbitrary types */
+
+ template <typename QT, typename CT>
+ static void ToSafeArray(const QVector<QT> &aVec, com::SafeArray<CT> &aArr)
+ {
+ aArr.reset(aVec.size());
+ for (int i = 0; i < aVec.size(); ++i)
+ aArr[i] = static_cast<CT>(aVec.at(i));
+ }
+
+ template <typename CT, typename QT>
+ static void FromSafeArray(const com::SafeArray<CT> &aArr, QVector<QT> &aVec)
+ {
+ aVec.resize(static_cast<int>(aArr.size()));
+ for (int i = 0; i < aVec.size(); ++i)
+ aVec[i] = static_cast<QT>(aArr[i]);
+ }
+
+ template <typename QT, typename CT>
+ static void ToSafeArray(const QVector<QT *> &aVec, com::SafeArray<CT *> &aArr)
+ {
+ Q_UNUSED(aVec);
+ Q_UNUSED(aArr);
+ AssertMsgFailedReturnVoid(("No conversion!\n"));
+ }
+
+ template <typename CT, typename QT>
+ static void FromSafeArray(const com::SafeArray<CT *> &aArr, QVector<QT *> &aVec)
+ {
+ Q_UNUSED(aArr);
+ Q_UNUSED(aVec);
+ AssertMsgFailedReturnVoid(("No conversion!\n"));
+ }
+
+ /* Arrays of equal types */
+
+ template <typename T>
+ static void ToSafeArray(const QVector<T> &aVec, com::SafeArray<T> &aArr)
+ {
+ aArr.reset(aVec.size());
+ for (int i = 0; i < aVec.size(); ++i)
+ aArr[i] = aVec.at(i);
+ }
+
+ template <typename T>
+ static void FromSafeArray(const com::SafeArray<T> &aArr, QVector<T> &aVec)
+ {
+ aVec.resize(static_cast<int>(aArr.size()));
+ memcpy(&aVec[0], aArr.raw(), aArr.size() * sizeof(T));
+ }
+
+ /* Arrays of strings */
+
+ static void ToSafeArray(const QVector<QString> &aVec,
+ com::SafeArray<BSTR> &aArr);
+ static void FromSafeArray(const com::SafeArray<BSTR> &aArr,
+ QVector<QString> &aVec);
+
+ /* Arrays of GUID */
+
+ static void ToSafeArray(const QVector<QUuid> &aVec,
+ com::SafeGUIDArray &aArr);
+ static void FromSafeArray(const com::SafeGUIDArray &aArr,
+ QVector<QUuid> &aVec);
+
+ /* Arrays of GUID as BSTR */
+
+ static void ToSafeArray(const QVector<QUuid> &aVec,
+ com::SafeArray<BSTR> &aArr);
+ static void FromSafeArray(const com::SafeArray<BSTR> &aArr,
+ QVector<QUuid> &aVec);
+
+ /* Arrays of enums. Does a cast similar to what ENUMOut does. */
+
+ template <typename QE, typename CE>
+ static void ToSafeArray(const QVector<QE> &aVec,
+ com::SafeIfaceArray<CE> &aArr)
+ {
+ aArr.reset(static_cast<int>(aVec.size()));
+ for (int i = 0; i < aVec.size(); ++i)
+ aArr[i] = static_cast<CE>(aVec.at(i));
+ }
+
+ template <typename CE, typename QE>
+ static void FromSafeArray(const com::SafeIfaceArray<CE> &aArr,
+ QVector<QE> &aVec)
+ {
+ aVec.resize(static_cast<int>(aArr.size()));
+ for (int i = 0; i < aVec.size(); ++i)
+ aVec[i] = static_cast<QE>(aArr[i]);
+ }
+
+ /* Arrays of interface pointers. Note: we need a separate pair of names
+ * only because the MSVC8 template matching algorithm is poor and tries to
+ * instantiate a com::SafeIfaceArray<BSTR> (!!!) template otherwise for
+ * *no* reason and fails. Note that it's also not possible to choose the
+ * correct function by specifying template arguments explicitly because then
+ * it starts to try to instantiate the com::SafeArray<I> template for
+ * *no* reason again and fails too. Definitely, broken. Works in GCC like a
+ * charm. */
+
+ template <class CI, class I>
+ static void ToSafeIfaceArray(const QVector<CI> &aVec,
+ com::SafeIfaceArray<I> &aArr)
+ {
+ aArr.reset(static_cast<int>(aVec.size()));
+ for (int i = 0; i < aVec.size(); ++i)
+ {
+ aArr[i] = aVec.at(i).raw();
+ if (aArr[i])
+ aArr[i]->AddRef();
+ }
+ }
+
+ template <class I, class CI>
+ static void FromSafeIfaceArray(const com::SafeIfaceArray<I> &aArr,
+ QVector<CI> &aVec)
+ {
+ aVec.resize(static_cast<int>(aArr.size()));
+ for (int i = 0; i < aVec.size(); ++i)
+ aVec[i].attach(aArr[i]);
+ }
+
+protected:
+
+ /* no arbitrary instance creations */
+ COMBase() : mRC(S_OK) {}
+
+#if defined(VBOX_WITH_XPCOM)
+ static XPCOMEventQSocketListener *sSocketListener;
+#endif
+
+ /** Adapter to pass QString as input BSTR params */
+ class BSTRIn
+ {
+ public:
+
+ BSTRIn(const QString &s) : bstr(SysAllocString((const OLECHAR *)
+ (s.isNull() ? 0 : s.utf16()))) {}
+
+ ~BSTRIn()
+ {
+ if (bstr)
+ SysFreeString(bstr);
+ }
+
+ operator BSTR() const { return bstr; }
+
+ private:
+
+ BSTR bstr;
+ };
+
+ /** Adapter to pass QString as output BSTR params */
+ class BSTROut
+ {
+ public:
+
+ BSTROut(QString &s) : str(s), bstr(0) {}
+
+ ~BSTROut()
+ {
+ if (bstr) {
+ str = QString::fromUtf16(bstr);
+ SysFreeString(bstr);
+ }
+ }
+
+ operator BSTR *() { return &bstr; }
+
+ private:
+
+ QString &str;
+ BSTR bstr;
+ };
+
+ /** Adapter to pass QUuid as input BSTR params */
+ class GuidAsBStrIn
+ {
+ public:
+
+ GuidAsBStrIn(const QUuid &s) : bstr(SysAllocString((const OLECHAR *)
+ (s.isNull() ? 0 : s.toString().utf16()))) {}
+
+ ~GuidAsBStrIn()
+ {
+ if (bstr)
+ SysFreeString(bstr);
+ }
+
+ operator BSTR() const { return bstr; }
+
+ private:
+
+ BSTR bstr;
+ };
+
+ /** Adapter to pass QUuid as output BSTR params */
+ class GuidAsBStrOut
+ {
+ public:
+
+ GuidAsBStrOut(QUuid &s) : uuid(s), bstr(0) {}
+
+ ~GuidAsBStrOut()
+ {
+ if (bstr) {
+ uuid = QUuid(QString::fromUtf16(bstr));
+ SysFreeString(bstr);
+ }
+ }
+
+ operator BSTR *() { return &bstr; }
+
+ private:
+
+ QUuid &uuid;
+ BSTR bstr;
+ };
+
+ /**
+ * Adapter to pass K* enums as output COM enum params (*_T).
+ *
+ * @param QE K* enum.
+ * @param CE COM enum.
+ */
+ template <typename QE, typename CE>
+ class ENUMOut
+ {
+ public:
+
+ ENUMOut(QE &e) : qe(e), ce((CE)0) {}
+ ~ENUMOut() { qe = (QE)ce; }
+ operator CE *() { return &ce; }
+
+ private:
+
+ QE &qe;
+ CE ce;
+ };
+
+#if !defined(VBOX_WITH_XPCOM)
+
+ /** Adapter to pass QUuid as input GUID params */
+ static GUID GUIDIn(const QUuid &uuid) { return uuid; }
+
+ /** Adapter to pass QUuid as output GUID params */
+ class GUIDOut
+ {
+ public:
+
+ GUIDOut(QUuid &id) : uuid(id)
+ {
+ ::memset(&guid, 0, sizeof(GUID));
+ }
+
+ ~GUIDOut()
+ {
+ uuid = QUuid(
+ guid.Data1, guid.Data2, guid.Data3,
+ guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
+ guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
+ }
+
+ operator GUID *() { return &guid; }
+
+ private:
+
+ QUuid &uuid;
+ GUID guid;
+ };
+
+#else /* !defined(VBOX_WITH_XPCOM) */
+
+ /** Adapter to pass QUuid as input GUID params */
+ static const nsID &GUIDIn(const QUuid &uuid)
+ {
+ return *(const nsID *) &uuid;
+ }
+
+ /** Adapter to pass QUuid as output GUID params */
+ class GUIDOut
+ {
+ public:
+
+ GUIDOut(QUuid &id) : uuid(id), nsid(0) {}
+
+ ~GUIDOut()
+ {
+ if (nsid)
+ {
+ uuid = QUuid(
+ nsid->m0, nsid->m1, nsid->m2,
+ nsid->m3[0], nsid->m3[1], nsid->m3[2], nsid->m3[3],
+ nsid->m3[4], nsid->m3[5], nsid->m3[6], nsid->m3[7]);
+ nsMemory::Free(nsid);
+ }
+ }
+
+ operator nsID **() { return &nsid; }
+
+ private:
+
+ QUuid &uuid;
+ nsID *nsid;
+ };
+
+#endif /* !defined(VBOX_WITH_XPCOM) */
+
+ static void addref(IUnknown *aIface) { if (aIface) aIface->AddRef(); }
+ static void release(IUnknown *aIface) { if (aIface) aIface->Release(); }
+
+protected:
+
+ mutable HRESULT mRC;
+
+ friend class COMErrorInfo;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Alternative base class for the CInterface template that adds the errorInfo()
+ * method for providing extended error info about unsuccessful invocation of the
+ * last called interface method.
+ */
+class COMBaseWithEI : public COMBase
+{
+public:
+
+ /**
+ * Returns error info set by the last unsuccessfully invoked interface
+ * method. Returned error info is useful only if CInterface::lastRC()
+ * represents a failure or a warning (i.e. CInterface::isReallyOk() is
+ * false).
+ */
+ const COMErrorInfo &errorInfo() const { return mErrInfo; }
+
+protected:
+
+ /* no arbitrary instance creation */
+ COMBaseWithEI() : COMBase() {};
+
+ void setErrorInfo(const COMErrorInfo &aErrInfo) { mErrInfo = aErrInfo; }
+
+ void fetchErrorInfo(IUnknown *aCallee, const GUID *aCalleeIID) const
+ {
+ mErrInfo.fetchFromCurrentThread(aCallee, aCalleeIID);
+ }
+
+ mutable COMErrorInfo mErrInfo;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Simple class that encapsulates the result code and COMErrorInfo.
+ */
+class COMResult
+{
+public:
+
+ COMResult() : mRC(S_OK) {}
+
+ /**
+ * Queries the current result code from the given component.
+ */
+ explicit COMResult(const COMBase &aComponent)
+ : mRC(aComponent.lastRC()) {}
+
+ /**
+ * Queries the current result code and error info from the given component.
+ */
+ COMResult(const COMBaseWithEI &aComponent)
+ : mRC(aComponent.lastRC()),
+ mErrInfo(aComponent.errorInfo())
+ { }
+
+ /**
+ * Queries the current result code from the given component.
+ */
+ COMResult &operator=(const COMBase &aComponent)
+ {
+ mRC = aComponent.lastRC();
+ return *this;
+ }
+
+ /**
+ * Queries the current result code and error info from the given component.
+ */
+ COMResult &operator=(const COMBaseWithEI &aComponent)
+ {
+ mRC = aComponent.lastRC();
+ mErrInfo = aComponent.errorInfo();
+ return *this;
+ }
+
+ bool isNull() const { return mErrInfo.isNull(); }
+
+ /**
+ * Returns @c true if the result code represents success (with or without
+ * warnings).
+ */
+ bool isOk() const { return SUCCEEDED(mRC); }
+
+ /**
+ * Returns @c true if the result code represents success with one or more
+ * warnings.
+ */
+ bool isWarning() const { return SUCCEEDED_WARNING(mRC); }
+
+ /**
+ * Returns @c true if the result code represents success with no warnings.
+ */
+ bool isReallyOk() const { return mRC == S_OK; }
+
+ COMErrorInfo errorInfo() const { return mErrInfo; }
+ HRESULT rc() const { return mRC; }
+
+private:
+
+ HRESULT mRC;
+ COMErrorInfo mErrInfo;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Wrapper template class for all interfaces.
+ *
+ * All interface methods named as they are in the original, i.e. starting
+ * with the capital letter. All utility non-interface methods are named
+ * starting with the small letter. Utility methods should be not normally
+ * called by the end-user client application.
+ *
+ * @param I Interface class (i.e. derived from IUnknown/nsISupports).
+ * @param B Base class, either COMBase (by default) or COMBaseWithEI.
+ */
+template <class I, class B = COMBase>
+class CInterface : public B
+{
+public:
+
+ typedef B Base;
+ typedef I Iface;
+
+ // constructors & destructor
+
+ CInterface()
+ {
+ clear();
+ }
+
+ CInterface(const CInterface &that) : B(that)
+ {
+ clear();
+ mIface = that.mIface;
+ this->addref((IUnknown*)ptr());
+ }
+
+ CInterface(I *aIface)
+ {
+ clear();
+ setPtr(aIface);
+ this->addref((IUnknown*)aIface);
+ }
+
+ virtual ~CInterface()
+ {
+ detach();
+#ifdef RT_STRICT
+ mDead = true;
+#endif
+ }
+
+#ifdef VBOX_WITH_LESS_VIRTUALBOX_INCLUDING
+ virtual IID const &getIID() const = 0;
+#else
+ IID const &getIID() const { return COM_IIDOF(I); }
+#endif
+
+ // utility methods
+ void createInstance(const CLSID &aClsId)
+ {
+ AssertMsg(ptr() == NULL, ("Instance is already non-NULL\n"));
+ if (ptr() == NULL)
+ {
+ I* pObj = NULL;
+#if !defined(VBOX_WITH_XPCOM)
+ B::mRC = CoCreateInstance(aClsId, NULL, CLSCTX_ALL, getIID(), (void **)&pObj);
+#else
+ nsCOMPtr<nsIComponentManager> manager;
+ B::mRC = NS_GetComponentManager(getter_AddRefs(manager));
+ if (SUCCEEDED(B::mRC))
+ B::mRC = manager->CreateInstance(aClsId, nsnull, getIID(), (void **)&pObj);
+#endif
+
+ if (SUCCEEDED(B::mRC))
+ setPtr(pObj);
+ else
+ setPtr(NULL);
+
+ /* fetch error info, but don't assert if it's missing -- many other
+ * reasons can lead to an error (w/o providing error info), not only
+ * the instance initialization code (that should always provide it) */
+ B::fetchErrorInfo(NULL, NULL);
+ }
+ }
+
+ /**
+ * Attaches to the given foreign interface pointer by querying the own
+ * interface on it. The operation may fail.
+ */
+ template <class OI>
+ void attach(OI *aIface)
+ {
+ Assert(!mDead);
+ /* be aware of self assignment */
+ I* amIface = ptr();
+ this->addref((IUnknown*)aIface);
+ this->release((IUnknown*)amIface);
+ if (aIface)
+ {
+ amIface = NULL;
+ B::mRC = aIface->QueryInterface(getIID(), (void **)&amIface);
+ this->release((IUnknown*)aIface);
+ setPtr(amIface);
+ }
+ else
+ {
+ setPtr(NULL);
+ B::mRC = S_OK;
+ }
+ };
+
+ /** Specialization of attach() for our own interface I. Never fails. */
+ void attach(I *aIface)
+ {
+ Assert(!mDead);
+ /* be aware of self assignment */
+ this->addref((IUnknown*)aIface);
+ this->release((IUnknown*)ptr());
+ setPtr(aIface);
+ B::mRC = S_OK;
+ };
+
+ /** Detaches from the underlying interface pointer. */
+ void detach()
+ {
+ Assert(!mDead);
+ this->release((IUnknown*)ptr());
+ setPtr(NULL);
+ }
+
+ /** Returns @c true if not attached to any interface pointer. */
+ bool isNull() const
+ {
+ Assert(!mDead);
+ return mIface == NULL;
+ }
+
+ /** Returns @c true if attached to an interface pointer. */
+ bool isNotNull() const
+ {
+ Assert(!mDead);
+ return mIface != NULL;
+ }
+
+ /**
+ * Returns @c true if the result code represents success (with or without
+ * warnings).
+ */
+ bool isOk() const { return !isNull() && SUCCEEDED(B::mRC); }
+
+ /**
+ * Returns @c true if the result code represents success with one or more
+ * warnings.
+ */
+ bool isWarning() const { return !isNull() && SUCCEEDED_WARNING(B::mRC); }
+
+ /**
+ * Returns @c true if the result code represents success with no warnings.
+ */
+ bool isReallyOk() const { return !isNull() && B::mRC == S_OK; }
+
+ // utility operators
+
+ CInterface &operator=(const CInterface &that)
+ {
+ attach(that.ptr());
+ B::operator=(that);
+ return *this;
+ }
+
+ CInterface &operator=(I *aIface)
+ {
+ attach(aIface);
+ return *this;
+ }
+
+ /**
+ * Returns the raw interface pointer. Not intended to be used for anything
+ * else but in generated wrappers and for debugging. You've been warned.
+ */
+ I *raw() const
+ {
+ return ptr();
+ }
+
+ bool operator==(const CInterface &that) const { return ptr() == that.ptr(); }
+ bool operator!=(const CInterface &that) const { return ptr() != that.ptr(); }
+
+ I *ptr() const
+ {
+ Assert(!mDead);
+ return mIface;
+ }
+
+ void setPtr(I* aObj) const
+ {
+ Assert(!mDead);
+ mIface = aObj;
+ }
+
+private:
+#ifdef RT_STRICT
+ bool mDead;
+#endif
+ mutable I * mIface;
+
+ void clear()
+ {
+ mIface = NULL;
+#ifdef RT_STRICT
+ mDead = false;
+#endif
+ }
+};
+
+/**
+ * Partial specialization for CInterface template class above for a case when B == COMBase.
+ *
+ * We had to add it because on exporting template to a library at least on Windows there is
+ * an implicit instantiation of the createInstance() member (even if it's not used) which
+ * in case of base template uses API present in COMBaseWithEI class only, not in COMBase.
+ *
+ * @param I Brings the interface class (i.e. derived from IUnknown/nsISupports).
+ */
+template <class I>
+class CInterface<I, COMBase> : public COMBase
+{
+public:
+
+ typedef COMBase Base;
+ typedef I Iface;
+
+ // constructors & destructor
+
+ CInterface()
+ {
+ clear();
+ }
+
+ CInterface(const CInterface &that) : COMBase(that)
+ {
+ clear();
+ mIface = that.mIface;
+ this->addref((IUnknown*)ptr());
+ }
+
+ CInterface(I *pIface)
+ {
+ clear();
+ setPtr(pIface);
+ this->addref((IUnknown*)pIface);
+ }
+
+ virtual ~CInterface()
+ {
+ detach();
+#ifdef RT_STRICT
+ mDead = true;
+#endif
+ }
+
+#ifdef VBOX_WITH_LESS_VIRTUALBOX_INCLUDING
+ virtual IID const &getIID() const = 0;
+#else
+ IID const &getIID() const { return COM_IIDOF(I); }
+#endif
+
+ // utility methods
+
+ void createInstance(const CLSID &clsId)
+ {
+ AssertMsg(ptr() == NULL, ("Instance is already non-NULL\n"));
+ if (ptr() == NULL)
+ {
+ I* pObj = NULL;
+#if !defined(VBOX_WITH_XPCOM)
+ COMBase::mRC = CoCreateInstance(clsId, NULL, CLSCTX_ALL, getIID(), (void **)&pObj);
+#else
+ nsCOMPtr<nsIComponentManager> manager;
+ COMBase::mRC = NS_GetComponentManager(getter_AddRefs(manager));
+ if (SUCCEEDED(COMBase::mRC))
+ COMBase::mRC = manager->CreateInstance(clsId, nsnull, getIID(), (void **)&pObj);
+#endif
+
+ if (SUCCEEDED(COMBase::mRC))
+ setPtr(pObj);
+ else
+ setPtr(NULL);
+ }
+ }
+
+ /**
+ * Attaches to the given foreign interface pointer by querying the own
+ * interface on it. The operation may fail.
+ */
+ template <class OI>
+ void attach(OI *pIface)
+ {
+ Assert(!mDead);
+ /* Be aware of self assignment: */
+ I *pmIface = ptr();
+ this->addref((IUnknown*)pIface);
+ this->release((IUnknown*)pmIface);
+ if (pIface)
+ {
+ pmIface = NULL;
+ COMBase::mRC = pIface->QueryInterface(getIID(), (void **)&pmIface);
+ this->release((IUnknown*)pIface);
+ setPtr(pmIface);
+ }
+ else
+ {
+ setPtr(NULL);
+ COMBase::mRC = S_OK;
+ }
+ };
+
+ /** Specialization of attach() for our own interface I. Never fails. */
+ void attach(I *pIface)
+ {
+ Assert(!mDead);
+ /* Be aware of self assignment: */
+ this->addref((IUnknown*)pIface);
+ this->release((IUnknown*)ptr());
+ setPtr(pIface);
+ COMBase::mRC = S_OK;
+ };
+
+ /** Detaches from the underlying interface pointer. */
+ void detach()
+ {
+ Assert(!mDead);
+ this->release((IUnknown*)ptr());
+ setPtr(NULL);
+ }
+
+ /** Returns @c true if not attached to any interface pointer. */
+ bool isNull() const
+ {
+ Assert(!mDead);
+ return mIface == NULL;
+ }
+
+ /** Returns @c true if attached to an interface pointer. */
+ bool isNotNull() const
+ {
+ Assert(!mDead);
+ return mIface != NULL;
+ }
+
+ /** Returns @c true if the result code represents success (with or without warnings). */
+ bool isOk() const { return !isNull() && SUCCEEDED(COMBase::mRC); }
+
+ /** Returns @c true if the result code represents success with one or more warnings. */
+ bool isWarning() const { return !isNull() && SUCCEEDED_WARNING(COMBase::mRC); }
+
+ /** Returns @c true if the result code represents success with no warnings. */
+ bool isReallyOk() const { return !isNull() && COMBase::mRC == S_OK; }
+
+ // utility operators
+
+ CInterface &operator=(const CInterface &that)
+ {
+ attach(that.ptr());
+ COMBase::operator=(that);
+ return *this;
+ }
+
+ CInterface &operator=(I *pIface)
+ {
+ attach(pIface);
+ return *this;
+ }
+
+ /**
+ * Returns the raw interface pointer. Not intended to be used for anything
+ * else but in generated wrappers and for debugging. You've been warned.
+ */
+ I *raw() const
+ {
+ return ptr();
+ }
+
+ bool operator==(const CInterface &that) const { return ptr() == that.ptr(); }
+ bool operator!=(const CInterface &that) const { return ptr() != that.ptr(); }
+
+ I *ptr() const
+ {
+ Assert(!mDead);
+ return mIface;
+ }
+
+ void setPtr(I* aObj) const
+ {
+ Assert(!mDead);
+ mIface = aObj;
+ }
+
+private:
+
+#ifdef RT_STRICT
+ bool mDead;
+#endif
+ mutable I *mIface;
+
+ void clear()
+ {
+ mIface = NULL;
+#ifdef RT_STRICT
+ mDead = false;
+#endif
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CUnknown : public CInterface<IUnknown, COMBaseWithEI>
+{
+public:
+
+ typedef CInterface<IUnknown, COMBaseWithEI> Base;
+
+ CUnknown() {}
+
+ /** Creates an instance given another CInterface-based instance. */
+ template <class OI, class OB>
+ explicit CUnknown(const CInterface<OI, OB> &that)
+ {
+ attach(that.ptr());
+ if (SUCCEEDED(mRC))
+ {
+ /* preserve old error info if any */
+ mRC = that.lastRC();
+ setErrorInfo(that.errorInfo());
+ }
+ }
+
+ /** Constructor specialization for IUnknown. */
+ CUnknown(const CUnknown &that) : Base(that) {}
+
+ /** Creates an instance given a foreign interface pointer. */
+ template <class OI>
+ explicit CUnknown(OI *aIface)
+ {
+ attach(aIface);
+ }
+
+ /** Constructor specialization for IUnknown. */
+ explicit CUnknown(IUnknown *aIface) : Base(aIface) {}
+
+ /** Assigns from another CInterface-based instance. */
+ template <class OI, class OB>
+ CUnknown &operator=(const CInterface<OI, OB> &that)
+ {
+ attach(that.ptr());
+ if (SUCCEEDED(mRC))
+ {
+ /* preserve old error info if any */
+ mRC = that.lastRC();
+ setErrorInfo(that.errorInfo());
+ }
+ return *this;
+ }
+
+ /** Assignment specialization for CUnknown. */
+ CUnknown &operator=(const CUnknown &that)
+ {
+ Base::operator=(that);
+ return *this;
+ }
+
+ /** Assigns from a foreign interface pointer. */
+ template <class OI>
+ CUnknown &operator=(OI *aIface)
+ {
+ attach(aIface);
+ return *this;
+ }
+
+ /** Assignment specialization for IUnknown. */
+ CUnknown &operator=(IUnknown *aIface)
+ {
+ Base::operator=(aIface);
+ return *this;
+ }
+
+#ifdef VBOX_WITH_LESS_VIRTUALBOX_INCLUDING
+ IID const &getIID() const RT_OVERRIDE { return COM_IIDOF(IUnknown); }
+#else
+ IID const &getIID() const { return COM_IIDOF(IUnknown); }
+#endif
+};
+
+/** @} */
+
+#endif /* !FEQT_INCLUDED_SRC_globals_COMDefs_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/COMWrappers.xsl b/src/VBox/Frontends/VirtualBox/src/globals/COMWrappers.xsl
new file mode 100644
index 00000000..33d2ffc6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/COMWrappers.xsl
@@ -0,0 +1,2042 @@
+<?xml version="1.0"?>
+
+<!--
+ * A template to generate wrapper classes for [XP]COM interfaces
+ * (defined in XIDL) to use them in the main Qt-based GUI
+ * in platform-independent script-like manner.
+-->
+<!--
+ Copyright (C) 2006-2023 Oracle and/or its affiliates.
+
+ This file is part of VirtualBox base platform packages, as
+ available from https://www.virtualbox.org.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, in version 3 of the
+ License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses>.
+
+ SPDX-License-Identifier: GPL-3.0-only
+-->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="text"/>
+<xsl:strip-space elements="*"/>
+
+<xsl:include href="../../../../Main/idl/typemap-shared.inc.xsl" />
+
+
+<!--
+ * Keys for more efficiently looking up of types.
+-->
+<xsl:key name="G_keyEnumsByName" match="//enum[@name]" use="@name"/>
+<xsl:key name="G_keyInterfacesByName" match="//interface[@name]" use="@name"/>
+
+
+<!--
+ * File start bracket
+-->
+<xsl:template name="startFile">
+ <xsl:param name="file" />
+ <xsl:value-of select="concat('&#10;// ##### BEGINFILE &quot;', $file, '&quot;&#10;')" />
+</xsl:template>
+
+<!--
+ * File end bracket
+-->
+<xsl:template name="endFile">
+ <xsl:param name="file" />
+ <xsl:call-template name="xsltprocNewlineOutputHack"/>
+ <xsl:value-of select="concat('// ##### ENDFILE &quot;', $file, '&quot;&#10;&#10;')" />
+</xsl:template>
+
+
+<!--
+ * Shut down all implicit templates
+-->
+<xsl:template match="*"/>
+<xsl:template match="*|/" mode="declare"/>
+<xsl:template match="*|/" mode="include"/>
+<xsl:template match="*|/" mode="define"/>
+<xsl:template match="*|/" mode="end"/>
+<xsl:template match="*|/" mode="begin"/>
+
+
+<!--
+ * Main entry point (idl):
+-->
+<xsl:template match="idl">
+ <!-- Apply underlying template (library): -->
+ <xsl:apply-templates/>
+</xsl:template>
+
+
+<!--
+ * Encloses |if| element's contents (unconditionally expanded by
+ * <apply-templates mode="include"/>) with #ifdef / #endif.
+ *
+ * @note this can produce an empty #if/#endif block if |if|'s children
+ * expand to nothing (such as |cpp|). I see no need to handle this situation
+ * specially.
+-->
+<xsl:template match="if" mode="include">
+ <xsl:if test="(@target='xpidl') or (@target='midl')">
+ <xsl:apply-templates select="." mode="begin"/>
+ <xsl:apply-templates mode="include"/>
+ <xsl:apply-templates select="." mode="end"/>
+ </xsl:if>
+</xsl:template>
+
+<!--
+ * Encloses |if| element's contents (unconditionally expanded by
+ * <apply-templates mode="define"/>) with #ifdef / #endif.
+ *
+ * @note this can produce an empty #if/#endif block if |if|'s children
+ * expand to nothing (such as |cpp|). I see no need to handle this situation
+ * specially.
+-->
+<xsl:template match="if" mode="define">
+ <xsl:if test="(@target='xpidl') or (@target='midl')">
+ <xsl:apply-templates select="." mode="begin"/>
+ <xsl:apply-templates mode="define"/>
+ <xsl:apply-templates select="." mode="end"/>
+ <xsl:text>&#x0A;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+<!--
+ * Encloses |if| element's contents (unconditionally expanded by
+ * <apply-templates mode="declare"/>) with #ifdef / #endif.
+ *
+ * @note this can produce an empty #if/#endif block if |if|'s children
+ * expand to nothing (such as |cpp|). I see no need to handle this situation
+ * specially.
+-->
+<xsl:template match="if" mode="declare">
+ <xsl:if test="(@target='xpidl') or (@target='midl')">
+ <xsl:apply-templates select="." mode="begin"/>
+ <xsl:apply-templates mode="declare"/>
+ <xsl:apply-templates select="." mode="end"/>
+ <xsl:text>&#x0A;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+<!--
+ * |<if target="...">| element): begin and end.
+-->
+<xsl:template match="if" mode="begin">
+ <xsl:if test="@target='xpidl'">
+ <xsl:text>#if !defined(Q_WS_WIN)&#x0A;</xsl:text>
+ </xsl:if>
+ <xsl:if test="@target='midl'">
+ <xsl:text>#if defined(Q_WS_WIN)&#x0A;</xsl:text>
+ </xsl:if>
+</xsl:template>
+<xsl:template match="if" mode="end">
+ <xsl:if test="(@target='xpidl') or (@target='midl')">
+ <xsl:text>#endif&#x0A;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+
+<!--
+ * cpp_quote
+-->
+<xsl:template match="cpp"/>
+
+
+<!--
+ * #ifdef statement (@if attribute): begin and end
+-->
+<xsl:template match="@if" mode="begin">
+ <xsl:text>#if </xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text>&#x0A;</xsl:text>
+</xsl:template>
+<xsl:template match="@if" mode="end">
+ <xsl:text>#endif&#x0A;</xsl:text>
+</xsl:template>
+
+
+<!--
+ * Library
+-->
+<xsl:template match="library">
+ <!-- Declare enums: -->
+ <xsl:call-template name="declareEnums"/>
+
+ <!-- Declare interfaces: -->
+ <xsl:apply-templates select="application/if | application/interface[not(@internal='yes')]" mode="declare"/>
+
+ <!-- Define interfaces: -->
+ <xsl:call-template name="defineInterfaces"/>
+</xsl:template>
+
+
+<!--
+ * Declare enums:
+-->
+<xsl:template name="declareEnums">
+ <!-- Starting COMEnums.h file: -->
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'COMEnums.h'" />
+ </xsl:call-template>
+
+ <!-- Write down file header: -->
+ <xsl:text>/*&#x0A;</xsl:text>
+ <xsl:text> * DO NOT EDIT! This is a generated file.&#x0A;</xsl:text>
+ <xsl:text> *&#x0A;</xsl:text>
+ <xsl:text> * Qt-based wrappers for VirtualBox Main API (COM) enums.&#x0A;</xsl:text>
+ <xsl:text> * Generated from XIDL (XML interface definition).&#x0A;</xsl:text>
+ <xsl:text> *&#x0A;</xsl:text>
+ <xsl:text> * Source : src/VBox/Main/idl/VirtualBox.xidl&#x0A;</xsl:text>
+ <xsl:text> * Generator : src/VBox/Frontends/VirtualBox/src/globals/COMWrappers.xsl&#x0A;</xsl:text>
+ <xsl:text> */&#x0A;&#x0A;</xsl:text>
+ <xsl:text>#ifndef ___COMEnums_h___&#x0A;</xsl:text>
+ <xsl:text>#define ___COMEnums_h___&#x0A;&#x0A;</xsl:text>
+ <xsl:text>/* GUI includes: */&#x0A;</xsl:text>
+ <xsl:text>#include "QMetaType"&#x0A;&#x0A;</xsl:text>
+
+ <!-- Enumerate all enums: -->
+ <xsl:for-each select="application/enum">
+ <xsl:text>/* </xsl:text>
+ <xsl:value-of select="concat('K',@name)"/>
+ <xsl:text> enum: */&#x0A;</xsl:text>
+ <xsl:text>enum </xsl:text>
+ <xsl:value-of select="concat('K',@name)"/>
+ <xsl:text>&#x0A;{&#x0A;</xsl:text>
+ <xsl:for-each select="const">
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="concat('K',../@name,'_',@name)"/>
+ <xsl:text> = </xsl:text>
+ <xsl:value-of select="@value"/>
+ <xsl:text>,&#x0A;</xsl:text>
+ </xsl:for-each>
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="concat('K',@name)"/>
+ <xsl:text>_Max&#x0A;</xsl:text>
+ <xsl:text>};&#x0A;&#x0A;</xsl:text>
+ </xsl:for-each>
+
+ <!-- Declare enums to QMetaObject: -->
+ <xsl:text>/* Let QMetaType know about generated enums: */&#x0A;</xsl:text>
+ <xsl:for-each select="application/enum">
+ <xsl:text>Q_DECLARE_METATYPE(</xsl:text>
+ <xsl:value-of select="concat('K',@name)"/>
+ <xsl:text>)&#x0A;</xsl:text>
+ </xsl:for-each>
+ <xsl:text>&#x0A;</xsl:text>
+
+ <!-- Write down file footer: -->
+ <xsl:text>#endif /* __COMEnums_h__ */&#x0A;&#x0A;</xsl:text>
+
+ <!-- Finishing COMEnums.h file: -->
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'COMEnums.h'" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<!--
+ * Define interfaces:
+-->
+<xsl:template name="defineInterfaces">
+ <!-- Starting COMWrappers.cpp file: -->
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'COMWrappers.cpp'" />
+ </xsl:call-template>
+
+ <!-- Write down file header: -->
+ <xsl:text>/*&#x0A;</xsl:text>
+ <xsl:text> * DO NOT EDIT! This is a generated file.&#x0A;</xsl:text>
+ <xsl:text> *&#x0A;</xsl:text>
+ <xsl:text> * Qt-based wrappers definitions for VirtualBox Main API (COM) interfaces.&#x0A;</xsl:text>
+ <xsl:text> * Generated from XIDL (XML interface definition).&#x0A;</xsl:text>
+ <xsl:text> *&#x0A;</xsl:text>
+ <xsl:text> * Source : src/VBox/Main/idl/VirtualBox.xidl&#x0A;</xsl:text>
+ <xsl:text> * Generator : src/VBox/Frontends/VirtualBox/src/globals/COMWrappers.xsl&#x0A;</xsl:text>
+ <xsl:text> */&#x0A;&#x0A;</xsl:text>
+
+ <xsl:text>#include "VBox/com/VirtualBox.h"&#x0A;&#x0A;</xsl:text>
+
+ <xsl:text>/* COM includes: */&#x0A;</xsl:text>
+ <xsl:text>#include "COMEnums.h"&#x0A;</xsl:text>
+
+ <!-- Enumerate all interface definitions: -->
+ <xsl:apply-templates select="application/if | application/interface[not(@internal='yes')]" mode="include"/>
+ <xsl:text>&#x0A;</xsl:text>
+ <xsl:apply-templates select="application/if | application/interface[not(@internal='yes')]" mode="define"/>
+
+ <!-- Finishing COMWrappers.cpp file: -->
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'COMWrappers.cpp'" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<!--
+ * Declare interface:
+-->
+<xsl:template match="interface" mode="declare">
+ <!-- Starting file: -->
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="concat('C', substring(@name,2), '.h')" />
+ </xsl:call-template>
+
+ <!-- Write down file header: -->
+ <xsl:text>/*&#x0A;</xsl:text>
+ <xsl:text> * DO NOT EDIT! This is a generated file.&#x0A;</xsl:text>
+ <xsl:text> *&#x0A;</xsl:text>
+ <xsl:text> * Qt-based wrapper declaration for VirtualBox Main API (COM) interface.&#x0A;</xsl:text>
+ <xsl:text> * Generated from XIDL (XML interface definition).&#x0A;</xsl:text>
+ <xsl:text> *&#x0A;</xsl:text>
+ <xsl:text> * Source : src/VBox/Main/idl/VirtualBox.xidl&#x0A;</xsl:text>
+ <xsl:text> * Generator : src/VBox/Frontends/VirtualBox/src/globals/COMWrappers.xsl&#x0A;</xsl:text>
+ <xsl:text> */&#x0A;&#x0A;</xsl:text>
+ <xsl:text>#ifndef __C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>_h__&#x0A;</xsl:text>
+ <xsl:text>#define __C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>_h__&#x0A;&#x0A;</xsl:text>
+ <xsl:if test="@name='IVirtualBox' or @name='IMachine'">
+ <xsl:text>/* Qt includes: */&#x0A;</xsl:text>
+ <xsl:text>#include &lt;QList&gt;&#x0A;</xsl:text>
+ <xsl:text>#include &lt;QRect&gt;&#x0A;</xsl:text>
+ <xsl:text>#include &lt;QStringList&gt;&#x0A;&#x0A;</xsl:text>
+ </xsl:if>
+ <xsl:text>/* GUI includes: */&#x0A;</xsl:text>
+ <xsl:text>#include "COMDefs.h"&#x0A;</xsl:text>
+ <xsl:text>#include "UILibraryDefs.h"&#x0A;&#x0A;</xsl:text>
+ <xsl:text>/* VirtualBox interface declarations: */&#x0A;</xsl:text>
+ <xsl:text>#ifndef VBOX_WITH_LESS_VIRTUALBOX_INCLUDING&#x0A;</xsl:text>
+ <xsl:text># include &lt;VBox/com/VirtualBox.h&gt;&#x0A;</xsl:text>
+ <xsl:text>#else&#x0A;</xsl:text>
+ <xsl:text>COM_STRUCT_OR_CLASS(</xsl:text><xsl:value-of select="@name"/><xsl:text>);&#x0A;</xsl:text>
+ <xsl:text>#endif&#x0A;</xsl:text>
+
+ <!-- Forward declarations: -->
+ <xsl:text>/* Forward declarations: */&#x0A;</xsl:text>
+ <xsl:for-each select="//interface[not(@internal='yes')]">
+ <xsl:text>class C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>;&#x0A;</xsl:text>
+ </xsl:for-each>
+ <xsl:text>&#x0A;</xsl:text>
+
+ <!-- Interface forward declaration: -->
+ <xsl:text>/* Interface forward declaration: */&#x0A;</xsl:text>
+ <xsl:text>COM_STRUCT_OR_CLASS(I</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>);&#x0A;&#x0A;</xsl:text>
+
+ <!-- Interface wrapper declaration: -->
+ <xsl:text>/* Interface wrapper declaration: */&#x0A;</xsl:text>
+ <xsl:text>class SHARED_LIBRARY_STUFF C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text> : public CInterface&lt;</xsl:text>
+ <xsl:value-of select="@name"/>
+
+ <!-- Use the correct base if supportsErrorInfo: -->
+ <xsl:call-template name="tryComposeFetchErrorInfo">
+ <xsl:with-param name="mode" select="'getBaseClassName'"/>
+ </xsl:call-template>
+ <xsl:text>&gt;&#x0A;{&#x0A;public:&#x0A;&#x0A;</xsl:text>
+
+ <!-- Generate the Base typedef: -->
+ <xsl:text> typedef CInterface&lt;</xsl:text>
+ <xsl:value-of select="@name"/>
+
+ <!-- Use the correct base if supportsErrorInfo: -->
+ <xsl:call-template name="tryComposeFetchErrorInfo">
+ <xsl:with-param name="mode" select="'getBaseClassName'"/>
+ </xsl:call-template>
+ <xsl:text>&gt; Base;&#x0A;&#x0A;</xsl:text>
+
+ <!-- Generate member declarations: -->
+ <xsl:if test="name()='interface'">
+ <xsl:call-template name="declareMembers"/>
+ </xsl:if>
+
+ <!-- Interface declaration: -->
+ <xsl:text>};&#x0A;&#x0A;</xsl:text>
+
+ <!-- Declare metatype: -->
+ <xsl:text>/* Let QMetaType know about generated interface: */&#x0A;</xsl:text>
+ <xsl:text>Q_DECLARE_METATYPE(</xsl:text>
+ <xsl:value-of select="concat('C',substring(@name,2))"/>
+ <xsl:text>);&#x0A;&#x0A;</xsl:text>
+
+ <!-- Declare safe-array -->
+ <xsl:if test="
+ (name()='interface')
+ and
+ ((//attribute[@safearray='yes' and not(@internal='yes') and @type=current()/@name])
+ or
+ (//param[@safearray='yes' and not(../@internal='yes') and @type=current()/@name]))
+ ">
+ <xsl:text>/* Declare safe-array: */&#x0A;</xsl:text>
+ <xsl:text>typedef QVector&lt;C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>&gt; C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>Vector;&#x0A;&#x0A;</xsl:text>
+ </xsl:if>
+
+ <!-- Write down file footer: -->
+ <xsl:text>#endif /* __C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>_h__ */&#x0A;&#x0A;</xsl:text>
+
+ <!-- Finishing file: -->
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="concat('C', substring(@name,2), '.h')" />
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="declareAttributes">
+
+ <xsl:param name="iface"/>
+
+ <xsl:apply-templates select="$iface//attribute[not(@internal='yes')]" mode="declare"/>
+ <xsl:if test="$iface//attribute[not(@internal='yes')]">
+ <xsl:text>&#x0A;</xsl:text>
+ </xsl:if>
+ <!-- go to the base interface -->
+ <xsl:if test="$iface/@extends and $iface/@extends!='$unknown'">
+ <xsl:choose>
+ <!-- interfaces within application/if -->
+ <xsl:when test="name(..)='if'">
+ <xsl:call-template name="declareAttributes">
+ <xsl:with-param name="iface" select="
+ preceding-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ following-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ ../preceding-sibling::if[@target=../@target]/
+ *[self::interface and @name=$iface/@extends] |
+ ../following-sibling::if[@target=../@target]/
+ *[self::interface and @name=$iface/@extends]
+ "/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- interfaces within application -->
+ <xsl:otherwise>
+ <xsl:call-template name="declareAttributes">
+ <xsl:with-param name="iface" select="
+ preceding-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ following-sibling::
+ *[self::interface and @name=$iface/@extends]
+ "/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="declareMethods">
+
+ <xsl:param name="iface"/>
+
+ <xsl:apply-templates select="$iface//method[not(@internal='yes')]" mode="declare"/>
+ <xsl:if test="$iface//method[not(@internal='yes')]">
+ <xsl:text>&#x0A;</xsl:text>
+ </xsl:if>
+ <!-- go to the base interface -->
+ <xsl:if test="$iface/@extends and $iface/@extends!='$unknown'">
+ <xsl:choose>
+ <!-- interfaces within application/if -->
+ <xsl:when test="name(..)='if'">
+ <xsl:call-template name="declareMethods">
+ <xsl:with-param name="iface" select="
+ preceding-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ following-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ ../preceding-sibling::if[@target=../@target]/
+ *[self::interface and @name=$iface/@extends] |
+ ../following-sibling::if[@target=../@target]/
+ *[self::interface and @name=$iface/@extends]
+ "/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- interfaces within application -->
+ <xsl:otherwise>
+ <xsl:call-template name="declareMethods">
+ <xsl:with-param name="iface" select="
+ preceding-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ following-sibling::
+ *[self::interface and @name=$iface/@extends]
+ "/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="declareExtraDataHelpers">
+
+<xsl:text> void SetExtraDataBool(const QString &amp;strKey, bool fValue);
+ bool GetExtraDataBool(const QString &amp;strKey, bool fDef = true);
+ void SetExtraDataInt(const QString &amp;strKey, int value);
+ int GetExtraDataInt(const QString &amp;strKey, int def = 0);
+ void SetExtraDataRect(const QString &amp;strKey, const QRect &amp;value);
+ QRect GetExtraDataRect(const QString &amp;strKey, const QRect &amp;def = QRect());
+ void SetExtraDataStringList(const QString &amp;strKey, const QStringList &amp;value);
+ QStringList GetExtraDataStringList(const QString &amp;strKey, QStringList def = QStringList());
+ void SetExtraDataIntList(const QString &amp;strKey, const QList&lt;int&gt; &amp;value);
+ QList&lt;int&gt; GetExtraDataIntList(const QString &amp;strKey, QList&lt;int&gt; def = QList&lt;int&gt;());
+
+</xsl:text>
+
+</xsl:template>
+
+<xsl:template name="declareMembers">
+
+ <xsl:text> /* Constructors and assignments taking CUnknown and raw iface pointer: */&#x0A;&#x0A;</xsl:text>
+ <!-- default constructor -->
+ <xsl:text> C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>();&#x0A;</xsl:text>
+ <!-- default destructor -->
+ <xsl:text> ~C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>();&#x0A;&#x0A;</xsl:text>
+ <!-- constructor taking CWhatever -->
+ <xsl:text> template&lt;class OI, class OB&gt; explicit C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+<xsl:text>(const CInterface&lt;OI, OB&gt; &amp; that)
+ {
+ attach(that.raw());
+ if (SUCCEEDED(mRC))
+ {
+ mRC = that.lastRC();
+ setErrorInfo(that.errorInfo());
+ }
+ }
+</xsl:text>
+ <xsl:text>&#x0A;</xsl:text>
+ <!-- specialization for ourselves (copy constructor) -->
+ <xsl:text> C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>(const C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text> &amp; that);&#x0A;&#x0A;</xsl:text>
+ <!-- constructor taking a raw iface pointer -->
+ <xsl:text> template&lt;class OI&gt; explicit C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>(OI * aIface) { attach(aIface); }&#x0A;&#x0A;</xsl:text>
+ <!-- specialization for ourselves -->
+ <xsl:text> explicit C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text> * aIface);&#x0A;&#x0A;</xsl:text>
+ <!-- assignment taking CWhatever -->
+ <xsl:text> template&lt;class OI, class OB&gt; C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+<xsl:text> &amp; operator=(const CInterface&lt;OI, OB&gt; &amp; that)
+ {
+ attach(that.raw());
+ if (SUCCEEDED(mRC))
+ {
+ mRC = that.lastRC();
+ setErrorInfo(that.errorInfo());
+ }
+ return *this;
+ }
+</xsl:text>
+ <xsl:text>&#x0A;</xsl:text>
+ <!-- specialization for ourselves -->
+ <xsl:text> C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text> &amp; operator=(const C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+<xsl:text> &amp; that);&#x0A;</xsl:text>
+ <xsl:text>&#x0A;</xsl:text>
+ <!-- assignment taking a raw iface pointer -->
+ <xsl:text> template&lt;class OI&gt; C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+<xsl:text> &amp; operator=(OI * aIface)
+ {
+ attach(aIface);
+ return *this;
+ }
+</xsl:text>
+ <xsl:text>&#x0A;</xsl:text>
+ <!-- specialization for ourselves -->
+ <xsl:text> C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text> &amp; operator=(</xsl:text>
+ <xsl:value-of select="@name"/>
+<xsl:text> * aIface);&#x0A;</xsl:text>
+ <xsl:text>&#x0A;</xsl:text>
+
+ <xsl:text>#ifdef VBOX_WITH_LESS_VIRTUALBOX_INCLUDING&#x0A;</xsl:text>
+ <xsl:text>const IID &amp;getIID() const RT_OVERRIDE;&#x0A;</xsl:text>
+ <xsl:text>#endif&#x0A;&#x0A;</xsl:text>
+
+ <xsl:text> /* Attributes (properties): */&#x0A;</xsl:text>
+ <xsl:call-template name="declareAttributes">
+ <xsl:with-param name="iface" select="."/>
+ </xsl:call-template>
+
+ <xsl:text> /* Methods: */&#x0A;</xsl:text>
+ <xsl:call-template name="declareMethods">
+ <xsl:with-param name="iface" select="."/>
+ </xsl:call-template>
+
+ <xsl:if test="@name='IVirtualBox' or @name='IMachine'">
+ <xsl:text> /* ExtraData helpers: */&#x0A;</xsl:text>
+ <xsl:call-template name="declareExtraDataHelpers">
+ <xsl:with-param name="iface" select="."/>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:text> /* Friend wrappers: */&#x0A;</xsl:text>
+ <xsl:text> friend class CUnknown;&#x0A;</xsl:text>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="parent" select=".."/>
+ <!-- for definitions inside <if> -->
+ <xsl:if test="name(..)='if'">
+ <xsl:for-each select="
+ preceding-sibling::*[self::interface] |
+ following-sibling::*[self::interface] |
+ ../preceding-sibling::*[self::interface] |
+ ../following-sibling::*[self::interface] |
+ ../preceding-sibling::if[@target=$parent/@target]/*[self::interface] |
+ ../following-sibling::if[@target=$parent/@target]/*[self::interface]
+ ">
+ <xsl:if test="
+ ((name()='interface')
+ and
+ ((name(..)!='if' and (if[@target=$parent/@target]/method/param[@type=$name]
+ or
+ if[@target=$parent/@target]/attribute[@type=$name]))
+ or
+ (.//method/param[@type=$name] or attribute[@type=$name])))
+ ">
+ <xsl:text> friend class C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>;&#x0A;</xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:if>
+ <!-- for definitions outside <if> (i.e. inside <application>) -->
+ <xsl:if test="name(..)!='if'">
+ <xsl:for-each select="
+ preceding-sibling::*[self::interface] |
+ following-sibling::*[self::interface] |
+ preceding-sibling::if/*[self::interface] |
+ following-sibling::if/*[self::interface]
+ ">
+ <xsl:if test="
+ name()='interface' and (.//method/param[@type=$name] or attribute[@type=$name])
+ ">
+ <xsl:text> friend class C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>;&#x0A;</xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:if>
+
+</xsl:template>
+
+<!-- attribute declarations -->
+<xsl:template match="interface//attribute" mode="declare">
+ <xsl:apply-templates select="parent::node()" mode="begin"/>
+ <xsl:apply-templates select="@if" mode="begin"/>
+ <xsl:call-template name="composeMethod">
+ <xsl:with-param name="return" select="."/>
+ </xsl:call-template>
+ <xsl:if test="not(@readonly='yes')">
+ <xsl:call-template name="composeMethod">
+ <xsl:with-param name="return" select="''"/>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="@if" mode="end"/>
+ <xsl:apply-templates select="parent::node()" mode="end"/>
+</xsl:template>
+
+<!-- method declarations -->
+<xsl:template match="interface//method" mode="declare">
+ <xsl:apply-templates select="parent::node()" mode="begin"/>
+ <xsl:apply-templates select="@if" mode="begin"/>
+ <xsl:call-template name="composeMethod"/>
+ <xsl:apply-templates select="@if" mode="end"/>
+ <xsl:apply-templates select="parent::node()" mode="end"/>
+</xsl:template>
+
+
+<!--
+ * interface includes
+-->
+<xsl:template match="interface" mode="include">
+
+ <xsl:text>#include "C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>.h"&#x0A;</xsl:text>
+</xsl:template>
+
+
+<!--
+ * interface definitions
+-->
+<xsl:template match="interface" mode="define">
+
+ <xsl:text>// </xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text> wrapper&#x0A;</xsl:text>
+ <xsl:call-template name="xsltprocNewlineOutputHack"/>
+
+ <xsl:if test="name()='interface'">
+ <xsl:call-template name="defineMembers"/>
+ </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="defineConstructors">
+
+ <!-- default constructor -->
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>() {}&#x0A;&#x0A;</xsl:text>
+
+ <!-- default destructor -->
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::~C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>() {}&#x0A;&#x0A;</xsl:text>
+
+ <!-- copy constructor -->
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>(const C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text> &amp;that) : Base(that) {}&#x0A;&#x0A;</xsl:text>
+
+ <!-- copy constructor taking interface pointer -->
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text> *pIface) : Base(pIface) {}&#x0A;&#x0A;</xsl:text>
+
+ <!-- operator= -->
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>&amp; </xsl:text>
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::operator=(const C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+<xsl:text> &amp;that)
+{
+ Base::operator=(that);
+ return *this;
+}
+</xsl:text>
+ <xsl:text>&#x0A;</xsl:text>
+
+ <!-- operator= taking interface pointer -->
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>&amp; </xsl:text>
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::operator=(</xsl:text>
+ <xsl:value-of select="@name"/>
+<xsl:text> *pIface)
+{
+ Base::operator=(pIface);
+ return *this;
+}
+</xsl:text>
+ <xsl:text>&#x0A;</xsl:text>
+
+</xsl:template>
+
+<xsl:template name="defineIIDGetter">
+ <xsl:text>#ifdef VBOX_WITH_LESS_VIRTUALBOX_INCLUDING&#x0A;</xsl:text>
+ <xsl:text>const IID &amp;C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::getIID() const&#x0A;</xsl:text>
+ <xsl:text>{&#x0A;</xsl:text>
+ <xsl:text> return COM_IIDOF(</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>);&#x0A;</xsl:text>
+ <xsl:text>}&#x0A;</xsl:text>
+ <xsl:text>#endif&#x0A;&#x0A;</xsl:text>
+
+</xsl:template>
+
+<xsl:template name="defineAttributes">
+
+ <xsl:param name="iface"/>
+
+ <xsl:apply-templates select="$iface//attribute[not(@internal='yes')]" mode="define">
+ <xsl:with-param name="namespace" select="."/>
+ </xsl:apply-templates>
+
+ <!-- go to the base interface -->
+ <xsl:if test="$iface/@extends and $iface/@extends!='$unknown'">
+ <xsl:choose>
+ <!-- interfaces within application/if -->
+ <xsl:when test="name(..)='if'">
+ <xsl:call-template name="defineAttributes">
+ <xsl:with-param name="iface" select="
+ preceding-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ following-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ ../preceding-sibling::if[@target=../@target]/
+ *[self::interface and @name=$iface/@extends] |
+ ../following-sibling::if[@target=../@target]/
+ *[self::interface and @name=$iface/@extends]
+ "/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- interfaces within application -->
+ <xsl:otherwise>
+ <xsl:call-template name="defineAttributes">
+ <xsl:with-param name="iface" select="
+ preceding-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ following-sibling::
+ *[self::interface and @name=$iface/@extends]
+ "/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="defineMethods">
+
+ <xsl:param name="iface"/>
+
+ <xsl:apply-templates select="$iface//method[not(@internal='yes')]" mode="define">
+ <xsl:with-param name="namespace" select="."/>
+ </xsl:apply-templates>
+
+ <!-- go to the base interface -->
+ <xsl:if test="$iface/@extends and $iface/@extends!='$unknown'">
+ <xsl:choose>
+ <!-- interfaces within application/if -->
+ <xsl:when test="name(..)='if'">
+ <xsl:call-template name="defineMethods">
+ <xsl:with-param name="iface" select="
+ preceding-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ following-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ ../preceding-sibling::if[@target=../@target]/
+ *[self::interface and @name=$iface/@extends] |
+ ../following-sibling::if[@target=../@target]/
+ *[self::interface and @name=$iface/@extends]
+ "/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- interfaces within application -->
+ <xsl:otherwise>
+ <xsl:call-template name="defineMethods">
+ <xsl:with-param name="iface" select="
+ preceding-sibling::
+ *[self::interface and @name=$iface/@extends] |
+ following-sibling::
+ *[self::interface and @name=$iface/@extends]
+ "/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="defineExtraDataHelpers">
+
+ <xsl:param name="iface"/>
+
+ <xsl:text>void C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::SetExtraDataBool(const QString &amp;strKey, bool fValue)</xsl:text>
+<xsl:text>
+{
+ SetExtraData(strKey, fValue == true ? "true" : "false");
+}
+
+</xsl:text>
+
+ <xsl:text>bool C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::GetExtraDataBool(const QString &amp;strKey, bool fDef /* = true */)</xsl:text>
+<xsl:text>
+{
+ bool fResult = fDef;
+ QString value = GetExtraData(strKey);
+ if ( value == "true"
+ || value == "on"
+ || value == "yes")
+ fResult = true;
+ else if ( value == "false"
+ || value == "off"
+ || value == "no")
+ fResult = false;
+ return fResult;
+}
+
+</xsl:text>
+
+ <xsl:text>void C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::SetExtraDataInt(const QString &amp;strKey, int value)</xsl:text>
+<xsl:text>
+{
+ SetExtraData(strKey, QString::number(value));
+}
+
+</xsl:text>
+
+ <xsl:text>int C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::GetExtraDataInt(const QString &amp;strKey, int def /* = 0 */)</xsl:text>
+<xsl:text>
+{
+ QString value = GetExtraData(strKey);
+ bool fOk;
+ int result = value.toInt(&amp;fOk);
+ if (fOk)
+ return result;
+ return def;
+}
+
+</xsl:text>
+
+ <xsl:text>void C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::SetExtraDataRect(const QString &amp;strKey, const QRect &amp;value)</xsl:text>
+<xsl:text>
+{
+ SetExtraData(strKey, QString("%1,%2,%3,%4")
+ .arg(value.x())
+ .arg(value.y())
+ .arg(value.width())
+ .arg(value.height()));
+}
+
+</xsl:text>
+
+ <xsl:text>QRect C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::GetExtraDataRect(const QString &amp;strKey, const QRect &amp;def /* = QRect() */)</xsl:text>
+<xsl:text>
+{
+ QRect result = def;
+ QList&lt;int&gt; intList = GetExtraDataIntList(strKey);
+ if (intList.size() == 4)
+ {
+ result.setRect(intList.at(0),
+ intList.at(1),
+ intList.at(2),
+ intList.at(3));
+ }
+ return result;
+}
+
+</xsl:text>
+
+ <xsl:text>void C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::SetExtraDataStringList(const QString &amp;strKey, const QStringList &amp;value)</xsl:text>
+<xsl:text>
+{
+ SetExtraData(strKey, value.join(","));
+}
+
+</xsl:text>
+
+ <xsl:text>QStringList C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::GetExtraDataStringList(const QString &amp;strKey, QStringList def /* = QStringList() */)</xsl:text>
+<xsl:text>
+{
+ QString strValue = GetExtraData(strKey);
+ if (strValue.isEmpty())
+ return def;
+ else
+ return strValue.split(",");
+}
+
+</xsl:text>
+
+ <xsl:text>void C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::SetExtraDataIntList(const QString &amp;strKey, const QList&lt;int&gt; &amp;value)</xsl:text>
+<xsl:text>
+{
+ QStringList strList;
+ for (int i=0; i &lt; value.size(); ++i)
+ strList &lt;&lt; QString::number(value.at(i));
+ SetExtraDataStringList(strKey, strList);
+}
+
+</xsl:text>
+
+ <xsl:text>QList&lt;int&gt; C</xsl:text>
+ <xsl:value-of select="substring(@name,2)"/>
+ <xsl:text>::GetExtraDataIntList(const QString &amp;strKey, QList&lt;int&gt; def /* = QList&lt;int&gt;() */)</xsl:text>
+<xsl:text>
+{
+ QStringList strList = GetExtraDataStringList(strKey);
+ if (strList.size() > 0)
+ {
+ QList&lt;int&gt; intList;
+ bool fOk;
+ for (int i=0; i &lt; strList.size(); ++i)
+ {
+ intList &lt;&lt; strList.at(i).toInt(&amp;fOk);
+ if (!fOk)
+ return def;
+ }
+ return intList;
+ }
+ return def;
+}
+
+</xsl:text>
+
+</xsl:template>
+
+<xsl:template name="defineMembers">
+ <xsl:call-template name="defineConstructors">
+ <xsl:with-param name="iface" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="defineIIDGetter">
+ <xsl:with-param name="iface" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="defineAttributes">
+ <xsl:with-param name="iface" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="defineMethods">
+ <xsl:with-param name="iface" select="."/>
+ </xsl:call-template>
+ <xsl:if test="@name='IVirtualBox' or @name='IMachine'">
+ <xsl:text>/* ExtraData helpers: */&#x0A;</xsl:text>
+ <xsl:call-template name="defineExtraDataHelpers">
+ <xsl:with-param name="iface" select="."/>
+ </xsl:call-template>
+ </xsl:if>
+</xsl:template>
+
+<!-- attribute definitions -->
+<xsl:template match="interface//attribute" mode="define">
+
+ <xsl:param name="namespace" select="ancestor::interface[1]"/>
+
+ <xsl:apply-templates select="parent::node()" mode="begin"/>
+ <xsl:apply-templates select="@if" mode="begin"/>
+ <xsl:call-template name="composeMethod">
+ <xsl:with-param name="return" select="."/>
+ <xsl:with-param name="define" select="'yes'"/>
+ <xsl:with-param name="namespace" select="$namespace"/>
+ </xsl:call-template>
+ <xsl:if test="not(@readonly='yes')">
+ <xsl:call-template name="composeMethod">
+ <xsl:with-param name="return" select="''"/>
+ <xsl:with-param name="define" select="'yes'"/>
+ <xsl:with-param name="namespace" select="$namespace"/>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="@if" mode="end"/>
+ <xsl:apply-templates select="parent::node()" mode="end"/>
+ <xsl:text>&#x0A;</xsl:text>
+
+</xsl:template>
+
+<!-- method definitions -->
+<xsl:template match="interface//method" mode="define">
+
+ <xsl:param name="namespace" select="ancestor::interface[1]"/>
+
+ <xsl:apply-templates select="parent::node()" mode="begin"/>
+ <xsl:apply-templates select="@if" mode="begin"/>
+ <xsl:call-template name="composeMethod">
+ <xsl:with-param name="define" select="'yes'"/>
+ <xsl:with-param name="namespace" select="$namespace"/>
+ </xsl:call-template>
+ <xsl:apply-templates select="@if" mode="end"/>
+ <xsl:apply-templates select="parent::node()" mode="end"/>
+ <xsl:text>&#x0A;</xsl:text>
+
+</xsl:template>
+
+
+<!--
+ * co-classes
+-->
+<xsl:template match="module/class"/>
+
+
+<!--
+ * enums
+-->
+<xsl:template match="enum"/>
+
+
+<!--
+ * base template to produce interface methods
+ *
+ * @param return
+ * - in <attribute> context, must be '.' for getters and
+ * '' for setters
+ * - in <method> context, must not be specified (the default value
+ * will apply)
+ * @param define
+ * 'yes' to produce inlined definition outside the class
+ * declaration, or
+ * empty string to produce method declaration only (w/o body)
+ * @param namespace
+ * actual interface node for which this method is being defined
+ * (necessary to properly set a class name for inherited methods).
+ * If not specified, will default to the parent interface
+ * node of the method being defined.
+-->
+<xsl:template name="composeMethod">
+ <xsl:param name="return" select="param[@dir='return']"/>
+ <xsl:param name="define" select="''"/>
+ <xsl:param name="namespace" select="ancestor::interface[1]"/>
+ <xsl:choose>
+ <!-- no return value -->
+ <xsl:when test="not($return)">
+ <xsl:choose>
+ <xsl:when test="$define">
+ <xsl:text></xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>void </xsl:text>
+ <xsl:if test="$define">
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring($namespace/@name,2)"/>
+ <xsl:text>::</xsl:text>
+ </xsl:if>
+ <xsl:call-template name="composeMethodDecl">
+ <xsl:with-param name="isSetter" select="'yes'"/>
+ </xsl:call-template>
+ <xsl:if test="$define">
+ <xsl:text>&#x0A;{&#x0A;</xsl:text>
+ <!-- iface assertion -->
+ <xsl:text> AssertReturnVoid(ptr());&#x0A;</xsl:text>
+ <!-- method call -->
+ <xsl:call-template name="composeMethodCall">
+ <xsl:with-param name="isSetter" select="'yes'"/>
+ </xsl:call-template>
+ <xsl:text>}&#x0A;</xsl:text>
+ </xsl:if>
+ <xsl:if test="not($define)">
+ <xsl:text>;&#x0A;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <!-- has a return value -->
+ <xsl:when test="count($return) = 1">
+ <xsl:choose>
+ <xsl:when test="$define">
+ <xsl:text></xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:apply-templates select="$return/@type"/>
+ <xsl:text> </xsl:text>
+ <xsl:if test="$define">
+ <xsl:text>C</xsl:text>
+ <xsl:value-of select="substring($namespace/@name,2)"/>
+ <xsl:text>::</xsl:text>
+ </xsl:if>
+ <xsl:call-template name="composeMethodDecl"/>
+ <xsl:if test="$define">
+ <xsl:text>&#x0A;{&#x0A; </xsl:text>
+ <xsl:apply-templates select="$return/@type"/>
+ <xsl:text> a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="$return/@name"/>
+ </xsl:call-template>
+ <xsl:apply-templates select="$return/@type" mode="initializer"/>
+ <xsl:text>;&#x0A;</xsl:text>
+ <!-- iface assertion -->
+ <xsl:text> AssertReturn(ptr(), a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="$return/@name"/>
+ </xsl:call-template>
+ <xsl:text>);&#x0A;</xsl:text>
+ <!-- method call -->
+ <xsl:call-template name="composeMethodCall"/>
+ <!-- return statement -->
+ <xsl:text> return a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="$return/@name"/>
+ </xsl:call-template>
+ <xsl:text>;&#x0A;}&#x0A;</xsl:text>
+ </xsl:if>
+ <xsl:if test="not($define)">
+ <xsl:text>;&#x0A;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <!-- otherwise error -->
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>More than one return value in method: </xsl:text>
+ <xsl:value-of select="$namespace/@name"/>
+ <xsl:text>::</xsl:text>
+ <xsl:value-of select="@name"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="composeMethodDecl">
+ <xsl:param name="isSetter" select="''"/>
+ <xsl:choose>
+ <!-- attribute method call -->
+ <xsl:when test="name()='attribute'">
+ <xsl:choose>
+ <xsl:when test="$isSetter">
+ <!-- name -->
+ <xsl:text>Set</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>(</xsl:text>
+ <!-- parameter -->
+ <xsl:apply-templates select="@type" mode="param"/>
+ <xsl:text> a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- name -->
+ <xsl:text>Get</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>(</xsl:text>
+ <!-- const method -->
+ <xsl:text>) const</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- regular method call -->
+ <xsl:when test="name()='method'">
+ <!-- name -->
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>(</xsl:text>
+ <!-- parameters -->
+ <xsl:for-each select="param[@dir!='return']">
+ <xsl:apply-templates select="@type" mode="param"/>
+ <xsl:text> a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:if test="position() != last()">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:text>)</xsl:text>
+ <!-- const method -->
+ <xsl:if test="@const='yes'"> const</xsl:if>
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="composeMethodCall">
+ <xsl:param name="isSetter" select="''"/>
+ <!-- apply 'pre-call' hooks -->
+ <xsl:choose>
+ <xsl:when test="name()='attribute'">
+ <xsl:call-template name="hooks">
+ <xsl:with-param name="when" select="'pre-call'"/>
+ <xsl:with-param name="isSetter" select="$isSetter"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="name()='method'">
+ <xsl:for-each select="param">
+ <xsl:call-template name="hooks">
+ <xsl:with-param name="when" select="'pre-call'"/>
+ </xsl:call-template>
+ </xsl:for-each>
+ </xsl:when>
+ </xsl:choose>
+ <!-- start the call -->
+ <xsl:text> mRC = ptr()-></xsl:text>
+ <xsl:choose>
+ <!-- attribute method call -->
+ <xsl:when test="name()='attribute'">
+ <!-- method name -->
+ <xsl:choose>
+ <xsl:when test="$isSetter">
+ <xsl:text>COMSETTER(</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>COMGETTER(</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>)(</xsl:text>
+ <!-- parameter -->
+ <xsl:call-template name="composeMethodCallParam">
+ <xsl:with-param name="isIn" select="$isSetter"/>
+ <xsl:with-param name="isOut" select="not($isSetter)"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- regular method call -->
+ <xsl:when test="name()='method'">
+ <!-- method name -->
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>(</xsl:text>
+ <!-- parameters -->
+ <xsl:for-each select="param">
+ <xsl:call-template name="composeMethodCallParam"/>
+ <xsl:if test="position() != last()">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:text>);&#x0A;</xsl:text>
+
+ <xsl:text>#ifdef RT_OS_WINDOWS&#x0A;</xsl:text>
+ <xsl:text> Assert(mRC != RPC_E_WRONG_THREAD);&#x0A;</xsl:text>
+ <xsl:text> Assert(mRC != CO_E_NOTINITIALIZED);&#x0A;</xsl:text>
+ <xsl:text> Assert(mRC != RPC_E_CANTCALLOUT_ININPUTSYNCCALL);&#x0A;</xsl:text>
+ <xsl:text>#endif&#x0A;</xsl:text>
+
+ <!-- apply 'post-call' hooks -->
+ <xsl:choose>
+ <xsl:when test="name()='attribute'">
+ <xsl:call-template name="hooks">
+ <xsl:with-param name="when" select="'post-call'"/>
+ <xsl:with-param name="isSetter" select="$isSetter"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="name()='method'">
+ <xsl:for-each select="param">
+ <xsl:call-template name="hooks">
+ <xsl:with-param name="when" select="'post-call'"/>
+ </xsl:call-template>
+ </xsl:for-each>
+ </xsl:when>
+ </xsl:choose>
+ <!-- -->
+ <xsl:call-template name="tryComposeFetchErrorInfo"/>
+</xsl:template>
+
+<!--
+ * Composes a 'fetch error info' call or returns the name of the
+ * appropriate base class name that provides error info functionality
+ * (depending on the mode parameter). Does nothing if the current
+ * interface does not support error info.
+ *
+ * @param mode
+ * - 'getBaseClassName': expands to the base class name
+ * - any other value: composes a 'fetch error info' method call
+-->
+<xsl:template name="tryComposeFetchErrorInfo">
+ <xsl:param name="mode" select="''"/>
+
+ <xsl:variable name="ifaceSupportsErrorInfo" select="
+ ancestor-or-self::interface[1]/@supportsErrorInfo
+ "/>
+ <xsl:variable name="applicationSupportsErrorInfo" select="ancestor::application/@supportsErrorInfo"/>
+
+ <xsl:choose>
+ <xsl:when test="$ifaceSupportsErrorInfo">
+ <xsl:call-template name="composeFetchErrorInfo">
+ <xsl:with-param name="supports" select="string($ifaceSupportsErrorInfo)"/>
+ <xsl:with-param name="mode" select="$mode"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$applicationSupportsErrorInfo">
+ <xsl:call-template name="composeFetchErrorInfo">
+ <xsl:with-param name="supports" select="string($applicationSupportsErrorInfo)"/>
+ <xsl:with-param name="mode" select="$mode"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="composeFetchErrorInfo">
+ <xsl:param name="supports" select="''"/>
+ <xsl:param name="mode" select="''"/>
+
+ <xsl:choose>
+ <xsl:when test="$mode='getBaseClassName'">
+ <xsl:if test="$supports='strict' or $supports='yes'">
+ <xsl:text>, COMBaseWithEI</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:if test="$supports='strict' or $supports='yes'">
+ <xsl:text> if (RT_UNLIKELY(mRC != S_OK))&#x0A; {&#x0A;</xsl:text>
+ <xsl:text> fetchErrorInfo(ptr(), &amp;COM_IIDOF(Base::Iface));&#x0A;</xsl:text>
+ <xsl:if test="$supports='strict'">
+ <xsl:text> AssertMsg(errInfo.isFullAvailable(), </xsl:text>
+ <xsl:text>("for RC=0x%08X\n", mRC));&#x0A;</xsl:text>
+ </xsl:if>
+ <xsl:text> }&#x0A;</xsl:text>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="composeMethodCallParam">
+ <xsl:param name="isIn" select="@dir='in'"/>
+ <xsl:param name="isOut" select="@dir='out' or @dir='return'"/>
+
+ <xsl:choose>
+ <!-- safearrays -->
+ <xsl:when test="@safearray='yes'">
+ <xsl:choose>
+ <xsl:when test="$isIn">
+ <xsl:text>ComSafeArrayAsInParam(</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:when test="$isOut">
+ <xsl:text>ComSafeArrayAsOutParam(</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <!-- string types -->
+ <xsl:when test="@type = 'wstring'">
+ <xsl:choose>
+ <xsl:when test="$isIn">
+ <xsl:text>BSTRIn(a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:when test="$isOut">
+ <xsl:text>BSTROut(a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <!-- uuid is represented as string in the com -->
+ <xsl:when test="@type = 'uuid'">
+ <xsl:choose>
+ <xsl:when test="$isIn">
+ <xsl:text>GuidAsBStrIn(a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:when test="$isOut">
+ <xsl:text>GuidAsBStrOut(a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <!-- enum types -->
+ <xsl:when test="count(key('G_keyEnumsByName', current()/@type)) > 0">
+ <xsl:choose>
+ <xsl:when test="$isIn">
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="@type"/>
+ <xsl:text>_T) a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$isOut">
+ <xsl:text>ENUMOut&lt;K</xsl:text>
+ <xsl:value-of select="@type"/>
+ <xsl:text>, </xsl:text>
+ <xsl:value-of select="@type"/>
+ <xsl:text>_T&gt;(a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <!-- interface types -->
+ <xsl:when test="@type='$unknown' or (count(key('G_keyInterfacesByName', current()/@type)) > 0)">
+ <xsl:choose>
+ <xsl:when test="$isIn">
+ <xsl:text>a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>.ptr()</xsl:text>
+ </xsl:when>
+ <xsl:when test="$isOut">
+ <xsl:value-of select="concat('&amp;', @name, 'Ptr')"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <!-- currently unsupported types -->
+ <xsl:when test="@type = 'string'">
+ <xsl:message terminate="yes">
+ <xsl:text>Parameter type </xsl:text>
+ <xsl:value-of select="@type"/>
+ <xsl:text>is not currently supported</xsl:text>
+ </xsl:message>
+ </xsl:when>
+ <!-- assuming scalar types -->
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="$isIn">
+ <xsl:text>a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$isOut">
+ <xsl:text>&amp;a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ * attribute/parameter type conversion (returns plain Qt type name)
+-->
+<xsl:template match="attribute/@type | param/@type">
+ <xsl:choose>
+ <!-- modifiers -->
+ <xsl:when test="name(current())='type' and ../@mod">
+ <xsl:if test="../@safearray='yes' and ../@mod='ptr'">
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:text>either 'safearray' or 'mod' attribute is allowed, but not both!</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="../@mod='ptr'">
+ <xsl:choose>
+ <!-- standard types -->
+ <!--xsl:when test=".='result'">??</xsl:when-->
+ <xsl:when test=".='boolean'">BOOL *</xsl:when>
+ <xsl:when test=".='octet'">BYTE *</xsl:when>
+ <xsl:when test=".='short'">SHORT *</xsl:when>
+ <xsl:when test=".='unsigned short'">USHORT *</xsl:when>
+ <xsl:when test=".='long'">LONG *</xsl:when>
+ <xsl:when test=".='long long'">LONG64 *</xsl:when>
+ <xsl:when test=".='unsigned long'">ULONG *</xsl:when>
+ <xsl:when test=".='unsigned long long'">ULONG64 *</xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:text>attribute 'mod=</xsl:text>
+ <xsl:value-of select="concat('&quot;',../@mod,'&quot;')"/>
+ <xsl:text>' cannot be used with type </xsl:text>
+ <xsl:value-of select="concat('&quot;',current(),'&quot;!')"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="../@mod='string'">
+ <xsl:if test="../@safearray='yes'">
+ <xsl:text>QVector&lt;</xsl:text>
+ </xsl:if>
+ <xsl:choose>
+ <!-- standard types -->
+ <!--xsl:when test=".='result'">??</xsl:when-->
+ <xsl:when test=".='uuid'">QUuid</xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:text>attribute 'mod=</xsl:text>
+ <xsl:value-of select="concat('&quot;',../@mod,'&quot;')"/>
+ <xsl:text>' cannot be used with type </xsl:text>
+ <xsl:value-of select="concat('&quot;',current(),'&quot;!')"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="../@safearray='yes'">
+ <xsl:text>&gt;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:value-of select="concat('value &quot;',../@mod,'&quot; ')"/>
+ <xsl:text>of attribute 'mod' is invalid!</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- no modifiers -->
+ <xsl:otherwise>
+ <xsl:if test="../@safearray='yes'">
+ <xsl:text>QVector&lt;</xsl:text>
+ </xsl:if>
+ <xsl:choose>
+ <!-- standard types -->
+ <xsl:when test=".='result'">HRESULT</xsl:when>
+ <xsl:when test=".='boolean'">BOOL</xsl:when>
+ <xsl:when test=".='octet'">BYTE</xsl:when>
+ <xsl:when test=".='short'">SHORT</xsl:when>
+ <xsl:when test=".='unsigned short'">USHORT</xsl:when>
+ <xsl:when test=".='long'">LONG</xsl:when>
+ <xsl:when test=".='long long'">LONG64</xsl:when>
+ <xsl:when test=".='unsigned long'">ULONG</xsl:when>
+ <xsl:when test=".='unsigned long long'">ULONG64</xsl:when>
+ <xsl:when test=".='char'">CHAR</xsl:when>
+ <xsl:when test=".='string'">CHAR *</xsl:when>
+ <xsl:when test=".='wchar'">OLECHAR</xsl:when>
+ <xsl:when test=".='wstring'">QString</xsl:when>
+ <!-- UUID type -->
+ <xsl:when test=".='uuid'">QUuid</xsl:when>
+ <!-- system interface types -->
+ <xsl:when test=".='$unknown'">CUnknown</xsl:when>
+ <!-- enum types -->
+ <xsl:when test="count(key('G_keyEnumsByName', current())) > 0">
+ <xsl:value-of select="concat('K',string(.))"/>
+ </xsl:when>
+ <!-- custom interface types -->
+ <xsl:when test="count(key('G_keyInterfacesByName', current())) > 0">
+ <xsl:value-of select="concat('C',substring(.,2))"/>
+ </xsl:when>
+ <!-- other types -->
+ <xsl:otherwise>
+ <xsl:message terminate="yes"><xsl:text>Unknown parameter type: </xsl:text><xsl:value-of select="."/></xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="../@safearray='yes'">
+ <xsl:text>&gt;</xsl:text>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ * generates a null initializer for all scalar types (such as bool or long)
+ * and enum types in the form of ' = <null_initializer>', or nothing for other
+ * types.
+-->
+<xsl:template match="attribute/@type | param/@type" mode="initializer">
+ <xsl:choose>
+ <!-- safearrays don't need initializers -->
+ <xsl:when test="../@safearray='yes'">
+ </xsl:when>
+ <!-- modifiers -->
+ <xsl:when test="name(current())='type' and ../@mod">
+ <xsl:choose>
+ <xsl:when test="../@mod='ptr'">
+ <xsl:choose>
+ <!-- standard types -->
+ <!--xsl:when test=".='result'">??</xsl:when-->
+ <xsl:when test=".='boolean'"> = NULL</xsl:when>
+ <xsl:when test=".='octet'"> = NULL</xsl:when>
+ <xsl:when test=".='short'"> = NULL</xsl:when>
+ <xsl:when test=".='unsigned short'"> = NULL</xsl:when>
+ <xsl:when test=".='long'"> = NULL</xsl:when>
+ <xsl:when test=".='long long'"> = NULL</xsl:when>
+ <xsl:when test=".='unsigned long'"> = NULL</xsl:when>
+ <xsl:when test=".='unsigned long long'"> = NULL</xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:text>attribute 'mod=</xsl:text>
+ <xsl:value-of select="concat('&quot;',../@mod,'&quot;')"/>
+ <xsl:text>' cannot be used with type </xsl:text>
+ <xsl:value-of select="concat('&quot;',current(),'&quot;!')"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="../@mod='string'">
+ <xsl:choose>
+ <!-- standard types -->
+ <!--xsl:when test=".='result'">??</xsl:when-->
+ <xsl:when test=".='uuid'"></xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:text>attribute 'mod=</xsl:text>
+ <xsl:value-of select="concat('&quot;',../@mod,'&quot;')"/>
+ <xsl:text>' cannot be used with type </xsl:text>
+ <xsl:value-of select="concat('&quot;',current(),'&quot;!')"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:value-of select="concat('value &quot;',../@mod,'&quot; ')"/>
+ <xsl:text>of attribute 'mod' is invalid!</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- no modifiers -->
+ <xsl:otherwise>
+ <xsl:choose>
+ <!-- standard types that need a zero initializer -->
+ <xsl:when test=".='result'"> = S_OK</xsl:when>
+ <xsl:when test=".='boolean'"> = FALSE</xsl:when>
+ <xsl:when test=".='octet'"> = 0</xsl:when>
+ <xsl:when test=".='short'"> = 0</xsl:when>
+ <xsl:when test=".='unsigned short'"> = 0</xsl:when>
+ <xsl:when test=".='long'"> = 0</xsl:when>
+ <xsl:when test=".='long long'"> = 0</xsl:when>
+ <xsl:when test=".='unsigned long'"> = 0</xsl:when>
+ <xsl:when test=".='unsigned long long'"> = 0</xsl:when>
+ <xsl:when test=".='char'"> = 0</xsl:when>
+ <xsl:when test=".='string'"> = NULL</xsl:when>
+ <xsl:when test=".='wchar'"> = 0</xsl:when>
+ <!-- enum types initialized with 0 -->
+ <xsl:when test="count(key('G_keyEnumsByName', current())) > 0">
+ <xsl:value-of select="concat(' = (K',string(.),') 0')"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ * attribute/parameter type conversion (for method declaration)
+-->
+<xsl:template match="attribute/@type | param/@type" mode="param">
+ <xsl:choose>
+ <!-- class types -->
+ <xsl:when test="
+ . = 'string'
+ or . = 'wstring'
+ or . = '$unknown'
+ or ../@safearray = 'yes'
+ or (count(key('G_keyEnumsByName', current())) > 0)
+ or (count(key('G_keyInterfacesByName', current())) > 0)
+ ">
+ <xsl:choose>
+ <!-- <attribute> context -->
+ <xsl:when test="name(..)='attribute'">
+ <xsl:text>const </xsl:text>
+ <xsl:apply-templates select="."/>
+ <xsl:text> &amp;</xsl:text>
+ </xsl:when>
+ <!-- <param> context -->
+ <xsl:when test="name(..)='param'">
+ <xsl:choose>
+ <xsl:when test="../@dir='in'">
+ <xsl:text>const </xsl:text>
+ <xsl:apply-templates select="."/>
+ <xsl:text> &amp;</xsl:text>
+ </xsl:when>
+ <xsl:when test="../@dir='out'">
+ <xsl:apply-templates select="."/>
+ <xsl:text> &amp;</xsl:text>
+ </xsl:when>
+ <xsl:when test="../@dir='return'">
+ <xsl:apply-templates select="."/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <!-- assume scalar types -->
+ <xsl:otherwise>
+ <xsl:choose>
+ <!-- <attribute> context -->
+ <xsl:when test="name(..)='attribute'">
+ <xsl:apply-templates select="."/>
+ </xsl:when>
+ <!-- <param> context -->
+ <xsl:when test="name(..)='param'">
+ <xsl:apply-templates select="."/>
+ <xsl:if test="../@dir='out'">
+ <xsl:text> &amp;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ * attribute/parameter type conversion (returns plain COM type name)
+ * (basically, copied from midl.xsl)
+-->
+<xsl:template match="attribute/@type | param/@type" mode="com">
+ <xsl:choose>
+ <!-- modifiers -->
+ <xsl:when test="name(current())='type' and ../@mod">
+ <xsl:choose>
+ <xsl:when test="../@mod='ptr'">
+ <xsl:choose>
+ <!-- standard types -->
+ <!--xsl:when test=".='result'">??</xsl:when-->
+ <xsl:when test=".='boolean'">BOOL *</xsl:when>
+ <xsl:when test=".='octet'">BYTE *</xsl:when>
+ <xsl:when test=".='short'">SHORT *</xsl:when>
+ <xsl:when test=".='unsigned short'">USHORT *</xsl:when>
+ <xsl:when test=".='long'">LONG *</xsl:when>
+ <xsl:when test=".='long long'">LONG64 *</xsl:when>
+ <xsl:when test=".='unsigned long'">ULONG *</xsl:when>
+ <xsl:when test=".='unsigned long long'">ULONG64 *</xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:text>attribute 'mod=</xsl:text>
+ <xsl:value-of select="concat('&quot;',../@mod,'&quot;')"/>
+ <xsl:text>' cannot be used with type </xsl:text>
+ <xsl:value-of select="concat('&quot;',current(),'&quot;!')"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="../@mod='string'">
+ <xsl:choose>
+ <!-- standard types -->
+ <!--xsl:when test=".='result'">??</xsl:when-->
+ <xsl:when test=".='uuid'">BSTR</xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:text>attribute 'mod=</xsl:text>
+ <xsl:value-of select="concat('&quot;',../@mod,'&quot;')"/>
+ <xsl:text>' cannot be used with type </xsl:text>
+ <xsl:value-of select="concat('&quot;',current(),'&quot;!')"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat(../../../@name,'::',../../@name,'::',../@name,': ')"/>
+ <xsl:value-of select="concat('value &quot;',../@mod,'&quot; ')"/>
+ <xsl:text>of attribute 'mod' is invalid!</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- no modifiers -->
+ <xsl:otherwise>
+ <xsl:choose>
+ <!-- standard types -->
+ <xsl:when test=".='result'">HRESULT</xsl:when>
+ <xsl:when test=".='boolean'">BOOL</xsl:when>
+ <xsl:when test=".='octet'">BYTE</xsl:when>
+ <xsl:when test=".='short'">SHORT</xsl:when>
+ <xsl:when test=".='unsigned short'">USHORT</xsl:when>
+ <xsl:when test=".='long'">LONG</xsl:when>
+ <xsl:when test=".='long long'">LONG64</xsl:when>
+ <xsl:when test=".='unsigned long'">ULONG</xsl:when>
+ <xsl:when test=".='unsigned long long'">ULONG64</xsl:when>
+ <xsl:when test=".='char'">CHAR</xsl:when>
+ <xsl:when test=".='string'">CHAR *</xsl:when>
+ <xsl:when test=".='wchar'">OLECHAR</xsl:when>
+ <xsl:when test=".='wstring'">BSTR</xsl:when>
+ <!-- UUID type -->
+ <xsl:when test=".='uuid'">GUID</xsl:when>
+ <!-- system interface types -->
+ <xsl:when test=".='$unknown'">IUnknown *</xsl:when>
+ <!-- enum types -->
+ <xsl:when test="count(key('G_keyEnumsByName', current())) > 0">
+ <xsl:value-of select="."/>
+ </xsl:when>
+ <!-- custom interface types -->
+ <xsl:when test="count(key('G_keyInterfacesByName', current())) > 0">
+ <xsl:value-of select="."/><xsl:text> *</xsl:text>
+ </xsl:when>
+ <!-- other types -->
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>Unknown parameter type: </xsl:text>
+ <xsl:value-of select="."/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ * attribute/parameter type additional hooks.
+ *
+ * Called in the context of <attribute> or <param> elements.
+ *
+ * @param when When the hook is being called:
+ * 'pre-call' - right before the method call
+ * 'post-call' - right after the method call
+ * @param isSetter Non-empty if called in the cotext of the attribute setter
+ * call.
+-->
+<xsl:template name="hooks">
+ <xsl:param name="when" select="''"/>
+ <xsl:param name="isSetter" select="''"/>
+
+ <xsl:variable name="is_iface" select="count(key('G_keyInterfacesByName', current()/@type)) > 0"/>
+ <xsl:variable name="is_out" select="(
+ (name()='attribute' and not($isSetter)) or
+ (name()='param' and (@dir='out' or @dir='return'))
+ )"/>
+
+ <xsl:choose>
+ <xsl:when test="$when='pre-call'">
+ <xsl:variable name="is_enum" select="count(key('G_keyEnumsByName', current()/@type)) > 0"/>
+ <xsl:choose>
+ <xsl:when test="@safearray='yes'">
+ <!-- declare a SafeArray variable -->
+ <xsl:choose>
+ <!-- interface types need special treatment here -->
+ <xsl:when test="@type='$unknown'">
+ <xsl:text> com::SafeIfaceArray &lt;IUnknown&gt; </xsl:text>
+ </xsl:when>
+ <xsl:when test="$is_iface">
+ <xsl:text> com::SafeIfaceArray &lt;</xsl:text>
+ <xsl:value-of select="@type"/>
+ <xsl:text>&gt; </xsl:text>
+ </xsl:when>
+ <!-- enums need the _T prefix -->
+ <xsl:when test="$is_enum">
+ <xsl:text> com::SafeArray &lt;</xsl:text>
+ <xsl:value-of select="@type"/>
+ <xsl:text>_T&gt; </xsl:text>
+ </xsl:when>
+ <!-- GUID is special too -->
+ <xsl:when test="@type='uuid' and @mod!='string'">
+ <xsl:text> com::SafeGUIDArray </xsl:text>
+ </xsl:when>
+ <!-- everything else is not -->
+ <xsl:otherwise>
+ <xsl:text> com::SafeArray &lt;</xsl:text>
+ <xsl:apply-templates select="@type" mode="com"/>
+ <xsl:text>&gt; </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="@name"/>
+ <xsl:text>;&#x0A;</xsl:text>
+ <xsl:if test="(name()='attribute' and $isSetter) or
+ (name()='param' and @dir='in')">
+ <!-- convert QVector to SafeArray -->
+ <xsl:choose>
+ <!-- interface types need special treatment here -->
+ <xsl:when test="@type='$unknown' or $is_iface">
+ <xsl:text> ToSafeIfaceArray(</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> ToSafeArray(</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>, </xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>);&#x0A;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="$is_out and ($is_iface or (@type='$unknown'))">
+ <xsl:text> </xsl:text>
+ <xsl:choose>
+ <xsl:when test="@type='$unknown'">
+ <xsl:text>IUnknown</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@type"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="concat('* ',@name,'Ptr = NULL;&#10;')"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="$when='post-call'">
+ <xsl:choose>
+ <xsl:when test="@safearray='yes'">
+ <xsl:if test="$is_out">
+ <!-- convert SafeArray to QVector -->
+ <xsl:choose>
+ <!-- interface types need special treatment here -->
+ <xsl:when test="@type='$unknown' or $is_iface">
+ <xsl:text> FromSafeIfaceArray(</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> FromSafeArray(</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="@name"/>
+ <xsl:text>, </xsl:text>
+ <xsl:text>a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:text>);&#x0A;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="$is_out and ($is_iface or (@type='$unknown'))">
+ <xsl:text> a</xsl:text>
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="@name"/>
+ </xsl:call-template>
+ <xsl:value-of select="concat('.setPtr(',@name,'Ptr);&#10;')"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>Invalid when value: </xsl:text>
+ <xsl:value-of select="$when"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+
+</xsl:template>
+
+
+</xsl:stylesheet>
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/globals/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/QIWithRestorableGeometry.h b/src/VBox/Frontends/VirtualBox/src/globals/QIWithRestorableGeometry.h
new file mode 100644
index 00000000..655a2970
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/QIWithRestorableGeometry.h
@@ -0,0 +1,164 @@
+/* $Id: QIWithRestorableGeometry.h $ */
+/** @file
+ * VBox Qt GUI - QIWithRestorableGeometry class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_QIWithRestorableGeometry_h
+#define FEQT_INCLUDED_SRC_globals_QIWithRestorableGeometry_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMainWindow>
+#include <QRect>
+#include <QResizeEvent>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+#ifdef VBOX_WS_X11
+# include "UICommon.h"
+# include "UIDesktopWidgetWatchdog.h"
+#endif
+
+/* Other VBox includes: */
+#ifdef VBOX_WS_MAC
+# include "iprt/cpp/utils.h"
+#endif
+
+/** Template with geometry saving/restoring capabilities. */
+template <class Base>
+class QIWithRestorableGeometry : public Base
+{
+public:
+
+ /** Constructs main window passing @a pParent and @a enmFlags to base-class. */
+ QIWithRestorableGeometry(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags())
+ : Base(pParent, enmFlags)
+ {}
+
+protected:
+
+ /** Handles move @a pEvent. */
+ virtual void moveEvent(QMoveEvent *pEvent) RT_OVERRIDE
+ {
+ /* Call to base-class: */
+ QMainWindow::moveEvent(pEvent);
+
+#ifdef VBOX_WS_X11
+ /* Prevent further handling if fake screen detected: */
+ if (UIDesktopWidgetWatchdog::isFakeScreenDetected())
+ return;
+#endif
+
+ /* Prevent handling for yet/already invisible window or if window is in minimized state: */
+ if (this->isVisible() && (this->windowState() & Qt::WindowMinimized) == 0)
+ {
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+ /* Use the old approach for OSX/Win: */
+ m_geometry.moveTo(this->frameGeometry().x(), this->frameGeometry().y());
+#else
+ /* Use the new approach otherwise: */
+ m_geometry.moveTo(this->geometry().x(), this->geometry().y());
+#endif
+ }
+ }
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE
+ {
+ /* Call to base-class: */
+ QMainWindow::resizeEvent(pEvent);
+
+#ifdef VBOX_WS_X11
+ /* Prevent handling if fake screen detected: */
+ if (UIDesktopWidgetWatchdog::isFakeScreenDetected())
+ return;
+#endif
+
+ /* Prevent handling for yet/already invisible window or if window is in minimized state: */
+ if (this->isVisible() && (this->windowState() & Qt::WindowMinimized) == 0)
+ {
+ QResizeEvent *pResizeEvent = static_cast<QResizeEvent*>(pEvent);
+ m_geometry.setSize(pResizeEvent->size());
+ }
+ }
+
+ /** Returns whether the window should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const { return false; }
+
+ /** Restores the window geometry to passed @a rect. */
+ void restoreGeometry(const QRect &rect)
+ {
+ m_geometry = rect;
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+ /* Use the old approach for OSX/Win: */
+ this->move(m_geometry.topLeft());
+ this->resize(m_geometry.size());
+#else
+ /* Use the new approach otherwise: */
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, m_geometry);
+#endif
+
+ /* Maximize (if necessary): */
+ if (shouldBeMaximized())
+ this->showMaximized();
+ }
+
+ /** Returns current window geometry. */
+ QRect currentGeometry() const
+ {
+ return m_geometry;
+ }
+
+ /** Returns whether the window is currently maximized. */
+ bool isCurrentlyMaximized() const
+ {
+#ifdef VBOX_WS_MAC
+ return ::darwinIsWindowMaximized(unconst(this));
+#else
+ return this->isMaximized();
+#endif
+ }
+
+private:
+
+ /** Holds the cached window geometry. */
+ QRect m_geometry;
+};
+
+/** Explicit QIWithRestorableGeometry instantiation for QMainWindow class.
+ * @note On Windows it's important that all template cases are instantiated just once across
+ * the linking space. In case we have particular template case instantiated from both
+ * library and executable sides, - we have multiple definition case and need to strictly
+ * ask compiler to do it just once and link such cases against library only.
+ * I would also note that it would be incorrect to just make whole the template exported
+ * to library because latter can have lack of required instantiations (current case). */
+template class SHARED_LIBRARY_STUFF QIWithRestorableGeometry<QMainWindow>;
+
+#endif /* !FEQT_INCLUDED_SRC_globals_QIWithRestorableGeometry_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/QIWithRetranslateUI.h b/src/VBox/Frontends/VirtualBox/src/globals/QIWithRetranslateUI.h
new file mode 100644
index 00000000..7b6cbffb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/QIWithRetranslateUI.h
@@ -0,0 +1,201 @@
+/* $Id: QIWithRetranslateUI.h $ */
+/** @file
+ * VBox Qt GUI - Qt extensions: QIWithRetranslateUI class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_QIWithRetranslateUI_h
+#define FEQT_INCLUDED_SRC_globals_QIWithRetranslateUI_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QApplication>
+#include <QDialog>
+#include <QEvent>
+#include <QGraphicsWidget>
+#include <QObject>
+#include <QWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UITranslator.h"
+
+
+/** Template for automatic language translations of underlying QWidget. */
+template <class Base>
+class QIWithRetranslateUI : public Base
+{
+public:
+
+ /** Constructs translatable widget passing @a pParent to the base-class. */
+ QIWithRetranslateUI(QWidget *pParent = 0) : Base(pParent)
+ {
+ qApp->installEventFilter(this);
+ }
+
+protected:
+
+ /** Pre-handles standard Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent)
+ {
+ /* If translation is NOT currently in progress handle
+ * LanguageChange events for qApp or this object: */
+ if ( !UITranslator::isTranslationInProgress()
+ && pEvent->type() == QEvent::LanguageChange
+ && (pObject == qApp || pObject == this))
+ retranslateUi();
+
+ /* Call to base-class: */
+ return Base::eventFilter(pObject, pEvent);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() = 0;
+};
+
+/** Explicit QIWithRetranslateUI instantiation for QWidget & QDialog classes.
+ * @note On Windows it's important that all template cases are instantiated just once across
+ * the linking space. In case we have particular template case instantiated from both
+ * library and executable sides, - we have multiple definition case and need to strictly
+ * ask compiler to do it just once and link such cases against library only.
+ * I would also note that it would be incorrect to just make whole the template exported
+ * to library because latter can have lack of required instantiations (current case). */
+template class SHARED_LIBRARY_STUFF QIWithRetranslateUI<QWidget>;
+template class SHARED_LIBRARY_STUFF QIWithRetranslateUI<QDialog>;
+
+
+/** Template for automatic language translations of underlying QWidget with certain flags. */
+template <class Base>
+class QIWithRetranslateUI2 : public Base
+{
+public:
+
+ /** Constructs translatable widget passing @a pParent and @a enmFlags to the base-class. */
+ QIWithRetranslateUI2(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags()) : Base(pParent, enmFlags)
+ {
+ qApp->installEventFilter(this);
+ }
+
+protected:
+
+ /** Pre-handles standard Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent)
+ {
+ /* If translation is NOT currently in progress handle
+ * LanguageChange events for qApp or this object: */
+ if ( !UITranslator::isTranslationInProgress()
+ && pEvent->type() == QEvent::LanguageChange
+ && (pObject == qApp || pObject == this))
+ retranslateUi();
+
+ /* Call to base-class: */
+ return Base::eventFilter(pObject, pEvent);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() = 0;
+};
+
+
+/** Template for automatic language translations of underlying QObject. */
+template <class Base>
+class QIWithRetranslateUI3 : public Base
+{
+public:
+
+ /** Constructs translatable widget passing @a pParent to the base-class. */
+ QIWithRetranslateUI3(QObject *pParent = 0)
+ : Base(pParent)
+ {
+ qApp->installEventFilter(this);
+ }
+
+protected:
+
+ /** Pre-handles standard Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent)
+ {
+ /* If translation is NOT currently in progress handle
+ * LanguageChange events for qApp or this object: */
+ if ( !UITranslator::isTranslationInProgress()
+ && pEvent->type() == QEvent::LanguageChange
+ && (pObject == qApp || pObject == this))
+ retranslateUi();
+
+ /* Call to base-class: */
+ return Base::eventFilter(pObject, pEvent);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() = 0;
+};
+
+/** Explicit QIWithRetranslateUI3 instantiation for QObject class.
+ * @note On Windows it's important that all template cases are instantiated just once across
+ * the linking space. In case we have particular template case instantiated from both
+ * library and executable sides, - we have multiple definition case and need to strictly
+ * ask compiler to do it just once and link such cases against library only.
+ * I would also note that it would be incorrect to just make whole the template exported
+ * to library because latter can have lack of required instantiations (current case). */
+template class SHARED_LIBRARY_STUFF QIWithRetranslateUI3<QObject>;
+
+
+/** Template for automatic language translations of underlying QGraphicsWidget. */
+template <class Base>
+class QIWithRetranslateUI4 : public Base
+{
+public:
+
+ /** Constructs translatable widget passing @a pParent to the base-class. */
+ QIWithRetranslateUI4(QGraphicsWidget *pParent = 0)
+ : Base(pParent)
+ {
+ qApp->installEventFilter(this);
+ }
+
+protected:
+
+ /** Pre-handles standard Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent)
+ {
+ /* If translation is NOT currently in progress handle
+ * LanguageChange events for qApp or this object: */
+ if ( !UITranslator::isTranslationInProgress()
+ && pEvent->type() == QEvent::LanguageChange
+ && (pObject == qApp || pObject == this))
+ retranslateUi();
+
+ /* Call to base-class: */
+ return Base::eventFilter(pObject, pEvent);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() = 0;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_QIWithRetranslateUI_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIActionPool.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPool.cpp
new file mode 100644
index 00000000..34c4b640
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPool.cpp
@@ -0,0 +1,3695 @@
+/* $Id: UIActionPool.cpp $ */
+/** @file
+ * VBox Qt GUI - UIActionPool class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QActionGroup>
+#include <QHelpEvent>
+#include <QToolTip>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIActionPool.h"
+#include "UIActionPoolManager.h"
+#include "UIActionPoolRuntime.h"
+#include "UIConverter.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIShortcutPool.h"
+#include "UITranslator.h"
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+# include "UIExtraDataManager.h"
+# include "UINetworkRequestManager.h"
+# include "UIUpdateManager.h"
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+
+/** QEvent extension
+ * representing action-activation event. */
+class ActivateActionEvent : public QEvent
+{
+public:
+
+ /** Constructs @a pAction event. */
+ ActivateActionEvent(QAction *pAction)
+ : QEvent((QEvent::Type)ActivateActionEventType)
+ , m_pAction(pAction)
+ {}
+
+ /** Returns the action this event corresponds to. */
+ QAction *action() const { return m_pAction; }
+
+private:
+
+ /** Holds the action this event corresponds to. */
+ QAction *m_pAction;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMenu implementation. *
+*********************************************************************************************************************************/
+
+UIMenu::UIMenu()
+ : m_fShowToolTip(false)
+#ifdef VBOX_WS_MAC
+ , m_fConsumable(false)
+ , m_fConsumed(false)
+#endif
+{
+}
+
+bool UIMenu::event(QEvent *pEvent)
+{
+ /* Handle particular event-types: */
+ switch (pEvent->type())
+ {
+ /* Tool-tip request handler: */
+ case QEvent::ToolTip:
+ {
+ /* Get current help-event: */
+ QHelpEvent *pHelpEvent = static_cast<QHelpEvent*>(pEvent);
+ /* Get action which caused help-event: */
+ QAction *pAction = actionAt(pHelpEvent->pos());
+ /* If action present => show action's tool-tip if needed: */
+ if (pAction && m_fShowToolTip)
+ QToolTip::showText(pHelpEvent->globalPos(), pAction->toolTip());
+ break;
+ }
+ default:
+ break;
+ }
+ /* Call to base-class: */
+ return QMenu::event(pEvent);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIAction implementation. *
+*********************************************************************************************************************************/
+
+UIAction::UIAction(UIActionPool *pParent, UIActionType enmType, bool fMachineMenuAction /* = false */)
+ : QAction(pParent)
+ , m_pActionPool(pParent)
+ , m_enmActionPoolType(pParent->type())
+ , m_enmType(enmType)
+ , m_fMachineMenuAction(fMachineMenuAction)
+ , m_iState(0)
+ , m_fShortcutHidden(false)
+{
+ /* By default there is no specific menu role.
+ * It will be set explicitly later. */
+ setMenuRole(QAction::NoRole);
+
+#ifdef VBOX_WS_MAC
+ /* Make sure each action notifies it's parent about hovering: */
+ connect(this, &UIAction::hovered,
+ static_cast<UIActionPool*>(parent()), &UIActionPool::sltActionHovered);
+#endif
+}
+
+UIMenu *UIAction::menu() const
+{
+ return QAction::menu() ? qobject_cast<UIMenu*>(QAction::menu()) : 0;
+}
+
+void UIAction::setState(int iState)
+{
+ m_iState = iState;
+ updateIcon();
+ retranslateUi();
+ handleStateChange();
+}
+
+void UIAction::setIcon(int iState, const QIcon &icon)
+{
+ m_icons.resize(iState + 1);
+ m_icons[iState] = icon;
+ updateIcon();
+}
+
+void UIAction::setIcon(const QIcon &icon)
+{
+ setIcon(0, icon);
+}
+
+void UIAction::setName(const QString &strName)
+{
+ /* Remember internal name: */
+ m_strName = strName;
+ /* Update text according new name: */
+ updateText();
+}
+
+void UIAction::setShortcuts(const QList<QKeySequence> &shortcuts)
+{
+ /* Only for manager's action-pool: */
+ if (m_enmActionPoolType == UIActionPoolType_Manager)
+ {
+ /* If primary shortcut should be visible: */
+ if (!m_fShortcutHidden)
+ /* Call to base-class: */
+ QAction::setShortcuts(shortcuts);
+ /* Remember shortcuts: */
+ m_shortcuts = shortcuts;
+ }
+ /* Update text according to new primary shortcut: */
+ updateText();
+}
+
+void UIAction::showShortcut()
+{
+ m_fShortcutHidden = false;
+ if (!m_shortcuts.isEmpty())
+ QAction::setShortcuts(m_shortcuts);
+}
+
+void UIAction::hideShortcut()
+{
+ m_fShortcutHidden = true;
+ if (!shortcut().isEmpty())
+ QAction::setShortcuts(QList<QKeySequence>());
+}
+
+QString UIAction::nameInMenu() const
+{
+ /* Action-name format depends on action-pool type: */
+ switch (m_enmActionPoolType)
+ {
+ /* Unchanged name for Manager UI: */
+ case UIActionPoolType_Manager: return name();
+ /* Filtered name for Runtime UI: */
+ case UIActionPoolType_Runtime: return UITranslator::removeAccelMark(name());
+ }
+ /* Nothing by default: */
+ return QString();
+}
+
+void UIAction::updateIcon()
+{
+ QAction::setIcon(m_icons.value(m_iState, m_icons.value(0)));
+}
+
+void UIAction::updateText()
+{
+ /* First of all, action-text depends on action type: */
+ switch (m_enmType)
+ {
+ case UIActionType_Menu:
+ {
+ /* For menu types it's very easy: */
+ setText(nameInMenu());
+ break;
+ }
+ default:
+ {
+ /* For rest of action types it depends on action-pool type: */
+ switch (m_enmActionPoolType)
+ {
+ /* The same as menu name for Manager UI: */
+ case UIActionPoolType_Manager:
+ {
+ setText(nameInMenu());
+ break;
+ }
+ /* With shortcut appended for Runtime UI: */
+ case UIActionPoolType_Runtime:
+ {
+ if (m_fMachineMenuAction)
+ setText(UITranslator::insertKeyToActionText(nameInMenu(),
+ gShortcutPool->shortcut(actionPool(), this).primaryToPortableText()));
+ else
+ setText(nameInMenu());
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+/* static */
+QString UIAction::simplifyText(QString strText)
+{
+ return strText.remove('.').remove('&');
+}
+
+
+/*********************************************************************************************************************************
+* Class UIActionMenu implementation. *
+*********************************************************************************************************************************/
+
+UIActionMenu::UIActionMenu(UIActionPool *pParent,
+ const QString &strIcon, const QString &strIconDisabled)
+ : UIAction(pParent, UIActionType_Menu)
+ , m_pMenu(0)
+{
+ if (!strIcon.isNull())
+ setIcon(UIIconPool::iconSet(strIcon, strIconDisabled));
+ prepare();
+}
+
+UIActionMenu::UIActionMenu(UIActionPool *pParent,
+ const QString &strIconNormal, const QString &strIconSmall,
+ const QString &strIconNormalDisabled, const QString &strIconSmallDisabled)
+ : UIAction(pParent, UIActionType_Menu)
+ , m_pMenu(0)
+{
+ if (!strIconNormal.isNull())
+ setIcon(UIIconPool::iconSetFull(strIconNormal, strIconSmall, strIconNormalDisabled, strIconSmallDisabled));
+ prepare();
+}
+
+UIActionMenu::UIActionMenu(UIActionPool *pParent,
+ const QIcon &icon)
+ : UIAction(pParent, UIActionType_Menu)
+ , m_pMenu(0)
+{
+ if (!icon.isNull())
+ setIcon(icon);
+ prepare();
+}
+
+UIActionMenu::~UIActionMenu()
+{
+#if !defined(VBOX_IS_QT6_OR_LATER) || !defined(RT_OS_DARWIN) /** @todo qt6: Tcrashes in QCocoaMenuBar::menuForTag during GUI
+ * termination, so disabled it for now and hope it isn't needed. */
+ /* Hide menu: */
+ hideMenu();
+#endif
+ /* Delete menu: */
+ delete m_pMenu;
+ m_pMenu = 0;
+}
+
+void UIActionMenu::setShowToolTip(bool fShowToolTip)
+{
+ AssertPtrReturnVoid(m_pMenu);
+ m_pMenu->setShowToolTip(fShowToolTip);
+}
+
+void UIActionMenu::showMenu()
+{
+ /* Show menu if necessary: */
+ if (!menu())
+ setMenu(m_pMenu);
+}
+
+void UIActionMenu::hideMenu()
+{
+ /* Hide menu if necessary: */
+ if (menu())
+ setMenu((QMenu *)0);
+}
+
+void UIActionMenu::prepare()
+{
+ /* Create menu: */
+ m_pMenu = new UIMenu;
+ AssertPtrReturnVoid(m_pMenu);
+ {
+ /* Prepare menu: */
+ connect(m_pMenu, &UIMenu::aboutToShow,
+ actionPool(), &UIActionPool::sltHandleMenuPrepare);
+ /* Show menu: */
+ showMenu();
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIActionSimple implementation. *
+*********************************************************************************************************************************/
+
+UIActionSimple::UIActionSimple(UIActionPool *pParent,
+ bool fMachineMenuAction /* = false */)
+ : UIAction(pParent, UIActionType_Simple, fMachineMenuAction)
+{
+}
+
+UIActionSimple::UIActionSimple(UIActionPool *pParent,
+ const QString &strIcon, const QString &strIconDisabled,
+ bool fMachineMenuAction /* = false */)
+ : UIAction(pParent, UIActionType_Simple, fMachineMenuAction)
+{
+ if (!strIcon.isNull())
+ setIcon(UIIconPool::iconSet(strIcon, strIconDisabled));
+}
+
+UIActionSimple::UIActionSimple(UIActionPool *pParent,
+ const QString &strIconNormal, const QString &strIconSmall,
+ const QString &strIconNormalDisabled, const QString &strIconSmallDisabled,
+ bool fMachineMenuAction /* = false */)
+ : UIAction(pParent, UIActionType_Simple, fMachineMenuAction)
+{
+ if (!strIconNormal.isNull())
+ setIcon(UIIconPool::iconSetFull(strIconNormal, strIconSmall, strIconNormalDisabled, strIconSmallDisabled));
+}
+
+UIActionSimple::UIActionSimple(UIActionPool *pParent,
+ const QIcon &icon,
+ bool fMachineMenuAction /* = false */)
+ : UIAction(pParent, UIActionType_Simple, fMachineMenuAction)
+{
+ if (!icon.isNull())
+ setIcon(icon);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIActionToggle implementation. *
+*********************************************************************************************************************************/
+
+UIActionToggle::UIActionToggle(UIActionPool *pParent,
+ bool fMachineMenuAction /* = false */)
+ : UIAction(pParent, UIActionType_Toggle, fMachineMenuAction)
+{
+ prepare();
+}
+
+UIActionToggle::UIActionToggle(UIActionPool *pParent,
+ const QString &strIcon, const QString &strIconDisabled,
+ bool fMachineMenuAction /* = false */)
+ : UIAction(pParent, UIActionType_Toggle, fMachineMenuAction)
+{
+ if (!strIcon.isNull())
+ setIcon(UIIconPool::iconSet(strIcon, strIconDisabled));
+ prepare();
+}
+
+UIActionToggle::UIActionToggle(UIActionPool *pParent,
+ const QString &strIconOn, const QString &strIconOff,
+ const QString &strIconOnDisabled, const QString &strIconOffDisabled,
+ bool fMachineMenuAction /* = false */)
+ : UIAction(pParent, UIActionType_Toggle, fMachineMenuAction)
+{
+ if (!strIconOn.isNull())
+ setIcon(UIIconPool::iconSetOnOff(strIconOn, strIconOff, strIconOnDisabled, strIconOffDisabled));
+ prepare();
+}
+
+UIActionToggle::UIActionToggle(UIActionPool *pParent,
+ const QIcon &icon,
+ bool fMachineMenuAction /* = false */)
+ : UIAction(pParent, UIActionType_Toggle, fMachineMenuAction)
+{
+ if (!icon.isNull())
+ setIcon(icon);
+ prepare();
+}
+
+void UIActionToggle::prepare()
+{
+ setCheckable(true);
+}
+
+
+/** Menu action extension, used as 'Application' menu class. */
+class UIActionMenuApplication : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuApplication(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {
+#ifdef VBOX_WS_MAC
+ menu()->setConsumable(true);
+#endif
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuType_Application;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuType_Application);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType_Application);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+#ifdef VBOX_WS_MAC
+ setName(QApplication::translate("UIActionPool", "&VirtualBox"));
+#else
+ setName(QApplication::translate("UIActionPool", "&File"));
+#endif
+ }
+};
+
+/** Simple action extension, used as 'Close' action class. */
+class UIActionSimplePerformClose : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimplePerformClose(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/exit_16px.png", ":/exit_16px.png", true)
+ {
+ setMenuRole(QAction::QuitRole);
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuApplicationActionType_Close;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuApplicationActionType_Close);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType_Close);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Close");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType actionPoolType) const RT_OVERRIDE
+ {
+ switch (actionPoolType)
+ {
+ case UIActionPoolType_Manager: break;
+ case UIActionPoolType_Runtime: return QKeySequence("Q");
+ }
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Close..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Close the virtual machine"));
+ }
+};
+
+#ifdef VBOX_WS_MAC
+/** Menu action extension, used as 'Window' menu class. */
+class UIActionMenuWindow : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuWindow(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuType_Window;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuType_Window);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType_Window);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Window"));
+ }
+};
+
+/** Simple action extension, used as 'Minimize' action class. */
+class UIActionSimpleMinimize : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleMinimize(UIActionPool *pParent)
+ : UIActionSimple(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuWindowActionType_Minimize;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuWindowActionType_Minimize);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuWindow(UIExtraDataMetaDefs::MenuWindowActionType_Minimize);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Minimize");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Minimize"));
+ setStatusTip(QApplication::translate("UIActionPool", "Minimize active window"));
+ }
+};
+#endif /* VBOX_WS_MAC */
+
+/** Menu action extension, used as 'Help' menu class. */
+class UIActionMenuHelp : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuHelp(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuType_Help;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuType_Help);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType_Help);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Help"));
+ }
+};
+
+/** Simple action extension, used as 'Contents' action class. */
+class UIActionSimpleContents : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleContents(UIActionPool *pParent)
+ : UIActionSimple(pParent, UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_DialogHelp), true)
+ {
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuHelpActionType_Contents;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuHelpActionType_Contents);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType_Contents);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Help");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType actionPoolType) const RT_OVERRIDE
+ {
+ switch (actionPoolType)
+ {
+ case UIActionPoolType_Manager: return QKeySequence(QKeySequence::HelpContents);
+ case UIActionPoolType_Runtime: break;
+ }
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Contents..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Show help contents"));
+ }
+};
+
+/** Simple action extension, used as 'Web Site' action class. */
+class UIActionSimpleWebSite : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleWebSite(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/site_16px.png", ":/site_16px.png", true)
+ {
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuHelpActionType_WebSite;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuHelpActionType_WebSite);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType_WebSite);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Web");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&VirtualBox Web Site..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the browser and go to the VirtualBox product web site"));
+ }
+};
+
+/** Simple action extension, used as 'Bug Tracker' action class. */
+class UIActionSimpleBugTracker : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleBugTracker(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/site_bugtracker_16px.png", ":/site_bugtracker_16px.png", true)
+ {
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuHelpActionType_BugTracker;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuHelpActionType_BugTracker);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType_BugTracker);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("BugTracker");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&VirtualBox Bug Tracker..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the browser and go to the VirtualBox product bug tracker"));
+ }
+};
+
+/** Simple action extension, used as 'Forums' action class. */
+class UIActionSimpleForums : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleForums(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/site_forum_16px.png", ":/site_forum_16px.png", true)
+ {
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuHelpActionType_Forums;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuHelpActionType_Forums);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType_Forums);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Forums");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&VirtualBox Forums..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the browser and go to the VirtualBox product forums"));
+ }
+};
+
+
+/** Simple action extension, used as 'Oracle' action class. */
+class UIActionSimpleOracle : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleOracle(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/site_oracle_16px.png", ":/site_oracle_16px.png", true)
+ {
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuHelpActionType_Oracle;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuHelpActionType_Oracle);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType_Oracle);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Oracle");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Oracle Web Site..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the browser and go to the Oracle web site"));
+ }
+};
+
+
+/** Simple action extension, used as 'Online Documentation' action class. */
+class UIActionSimpleOnlineDocumentation : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleOnlineDocumentation(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/site_oracle_16px.png", ":/site_oracle_16px.png", true)
+ {
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuHelpActionType_OnlineDocumentation;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuHelpActionType_OnlineDocumentation);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType_OnlineDocumentation);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("OnlineDocumentation");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Online Documentation..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the browser and go to the VirtualBox user manual"));
+ }
+};
+
+/** Simple action extension, used as 'Reset Warnings' action class. */
+class UIActionSimpleResetWarnings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleResetWarnings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/reset_warnings_16px.png", ":/reset_warnings_16px.png", true)
+ {
+ setMenuRole(QAction::ApplicationSpecificRole);
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuApplicationActionType_ResetWarnings;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuApplicationActionType_ResetWarnings);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType_ResetWarnings);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ResetWarnings");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Reset All Warnings"));
+ setStatusTip(QApplication::translate("UIActionPool", "Go back to showing all suppressed warnings and messages"));
+ }
+};
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+/** Simple action extension, used as 'Check for Updates' action class. */
+class UIActionSimpleCheckForUpdates : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleCheckForUpdates(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/refresh_16px.png", ":/refresh_disabled_16px.png", true)
+ {
+ setMenuRole(QAction::ApplicationSpecificRole);
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuApplicationActionType_CheckForUpdates;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuApplicationActionType_CheckForUpdates);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType_CheckForUpdates);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Update");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "C&heck for Updates..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Check for a new VirtualBox version"));
+ }
+};
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+/** Simple action extension, used as 'About' action class. */
+class UIActionSimpleAbout : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleAbout(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/about_16px.png", ":/about_16px.png", true)
+ {
+ setMenuRole(QAction::AboutRole);
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+#ifdef VBOX_WS_MAC
+ return UIExtraDataMetaDefs::MenuApplicationActionType_About;
+#else
+ return UIExtraDataMetaDefs::MenuHelpActionType_About;
+#endif
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+#ifdef VBOX_WS_MAC
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuApplicationActionType_About);
+#else
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuHelpActionType_About);
+#endif
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+#ifdef VBOX_WS_MAC
+ return actionPool()->isAllowedInMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType_About);
+#else
+ return actionPool()->isAllowedInMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType_About);
+#endif
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("About");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&About VirtualBox..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display a window with product information"));
+ }
+};
+
+/** Simple action extension, used as 'Preferences' action class. */
+class UIActionSimplePreferences : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimplePreferences(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/global_settings_32px.png", ":/global_settings_16px.png",
+ ":/global_settings_disabled_32px.png", ":/global_settings_disabled_16px.png",
+ true)
+ {
+ setMenuRole(QAction::PreferencesRole);
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuApplicationActionType_Preferences;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuApplicationActionType_Preferences);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType_Preferences);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Preferences");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ switch (actionPool()->type())
+ {
+ case UIActionPoolType_Manager: return QKeySequence("Ctrl+G");
+ case UIActionPoolType_Runtime: break;
+ }
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Preferences...", "global preferences window"));
+ setStatusTip(QApplication::translate("UIActionPool", "Display the global preferences window"));
+ setToolTip( QApplication::translate("UIActionPool", "Display Global Preferences")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Menu action extension, used as 'Log' menu class. */
+class UIActionMenuSelectorLog : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorLog(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("LogViewerMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Log"));
+ }
+};
+
+/** Simple action extension, used as 'Toggle Pane Find' action class. */
+class UIActionMenuSelectorLogTogglePaneFind : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorLogTogglePaneFind(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/log_viewer_find_32px.png", ":/log_viewer_find_16px.png",
+ ":/log_viewer_find_disabled_32px.png", ":/log_viewer_find_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleLogFind");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+F");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Find"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Log Viewer"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open pane with searching options"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Find Pane")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Toggle Pane Filter' action class. */
+class UIActionMenuSelectorLogTogglePaneFilter : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorLogTogglePaneFilter(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/log_viewer_filter_32px.png", ":/log_viewer_filter_16px.png",
+ ":/log_viewer_filter_disabled_32px.png", ":/log_viewer_filter_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleLogFilter");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+T");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Filter"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Log Viewer"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open pane with filtering options"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Filter Pane")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Toggle Pane Bookmark' action class. */
+class UIActionMenuSelectorLogTogglePaneBookmark : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorLogTogglePaneBookmark(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/log_viewer_bookmark_32px.png", ":/log_viewer_bookmark_16px.png",
+ ":/log_viewer_bookmark_disabled_32px.png", ":/log_viewer_bookmark_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleLogBookmark");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+D");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Bookmark"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Log Viewer"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open pane with bookmarking options"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Bookmark Pane")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Toggle Pane Options' action class. */
+class UIActionMenuSelectorLogTogglePaneOptions : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorLogTogglePaneOptions(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/log_viewer_options_32px.png", ":/log_viewer_options_16px.png",
+ ":/log_viewer_options_disabled_32px.png", ":/log_viewer_options_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleLogOptions");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+P");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Options"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Log Viewer"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open pane with log viewer options"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Options Pane")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Refresh' action class. */
+class UIActionMenuSelectorLogPerformRefresh : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorLogPerformRefresh(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/log_viewer_refresh_32px.png", ":/log_viewer_refresh_16px.png",
+ ":/log_viewer_refresh_disabled_32px.png", ":/log_viewer_refresh_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RefreshLog");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+R");
+ }
+
+ /** Returns standard shortcut. */
+ virtual QKeySequence standardShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return actionPool()->isTemporary() ? QKeySequence() : QKeySequence(QKeySequence::Refresh);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Refresh"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Log Viewer"));
+ setStatusTip(QApplication::translate("UIActionPool", "Refresh the currently viewed log"));
+ setToolTip( QApplication::translate("UIActionPool", "Refresh Viewed Log")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Reload' action class. */
+class UIActionMenuSelectorLogPerformReload : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorLogPerformReload(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/log_viewer_refresh_32px.png", ":/log_viewer_refresh_16px.png",
+ ":/log_viewer_refresh_disabled_32px.png", ":/log_viewer_refresh_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ReloadAllLogs");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Returns standard shortcut. */
+ virtual QKeySequence standardShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Reload"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Log Viewer"));
+ setStatusTip(QApplication::translate("UIActionPool", "Reread all the log files and refresh pages"));
+ setToolTip( QApplication::translate("UIActionPool", "Reload Log Files")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Save' action class. */
+class UIActionMenuSelectorLogPerformSave : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorLogPerformSave(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/log_viewer_save_32px.png", ":/log_viewer_save_16px.png",
+ ":/log_viewer_save_disabled_32px.png", ":/log_viewer_save_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SaveLog");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+S");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Save..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Log Viewer"));
+ setStatusTip(QApplication::translate("UIActionPool", "Save selected virtual machine log"));
+ setToolTip( QApplication::translate("UIActionPool", "Save Virtual Machine Log")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Menu action extension, used as 'File Manager' menu class. */
+class UIActionMenuFileManager : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManager(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "File Manager"));
+ }
+};
+
+class UIActionMenuFileManagerHostSubmenu : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerHostSubmenu(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerHostSubmenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Host"));
+ }
+};
+
+class UIActionMenuFileManagerGuestSubmenu : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerGuestSubmenu(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerGuestSubmenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Guest"));
+ }
+};
+
+/** Simple action extension, used as 'Copy to Guest' in file manager action class. */
+class UIActionMenuFileManagerCopyToGuest : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerCopyToGuest(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_copy_to_guest_24px.png", ":/file_manager_copy_to_guest_16px.png",
+ ":/file_manager_copy_to_guest_disabled_24px.png", ":/file_manager_copy_to_guest_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerCopyToGuest");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Copy to guest"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Copy the selected object(s) from host to guest"));
+ setToolTip( QApplication::translate("UIActionPool", "Copy from Host to Guest")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Copy to Host' in file manager action class. */
+class UIActionMenuFileManagerCopyToHost : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerCopyToHost(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_copy_to_host_24px.png", ":/file_manager_copy_to_host_16px.png",
+ ":/file_manager_copy_to_host_disabled_24px.png", ":/file_manager_copy_to_host_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerCopyToHost");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Copy to host"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Copy the selected object(s) from guest to host"));
+ setToolTip( QApplication::translate("UIActionPool", "Copy from Guest to Host")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used to toggle 'File Manager Options' panel in file manager. */
+class UIActionMenuFileManagerOptions : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerOptions(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/file_manager_options_32px.png", ":/file_manager_options_16px.png",
+ ":/file_manager_options_disabled_32px.png", ":/file_manager_options_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleFileManagerOptionsPanel");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Options"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open panel with file manager options"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Options Pane")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used to toggle 'File Manager Log' panel in file manager. */
+class UIActionMenuFileManagerLog : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerLog(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/file_manager_log_32px.png", ":/file_manager_log_16px.png",
+ ":/file_manager_log_disabled_32px.png", ":/file_manager_log_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleFileManagerLogPanel");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Log"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open panel with file manager log"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Log Pane")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used to toggle 'File Manager Operations' panel in file manager. */
+class UIActionMenuFileManagerOperations : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerOperations(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/file_manager_operations_32px.png", ":/file_manager_operations_16px.png",
+ ":/file_manager_operations_disabled_32px.png", ":/file_manager_operations_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleFileManagerOperationsPanel");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Operations"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open panel with file manager operations"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Operations Pane")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used to toggle 'File Manager Guest Session' panel in file manager. */
+class UIActionMenuFileManagerGuestSession : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerGuestSession(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/file_manager_session_32px.png", ":/file_manager_session_16px.png",
+ ":/file_manager_session_disabled_32px.png", ":/file_manager_session_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleFileManagerGuestSessionPanel");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Session"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Toggle guest session panel of the file manager"));
+ setToolTip( QApplication::translate("UIActionPool", "Toggle Guest Session Panel")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform GoUp' in file manager action class. */
+class UIActionMenuFileManagerGoUp : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerGoUp(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_go_up_24px.png", ":/file_manager_go_up_16px.png",
+ ":/file_manager_go_up_disabled_24px.png", ":/file_manager_go_up_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerGoUp");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Go Up"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Go one level up to parent folder"));
+ setToolTip( QApplication::translate("UIActionPool", "Go One Level Up")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform GoHome' in file manager action class. */
+class UIActionMenuFileManagerGoHome : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerGoHome(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_go_home_24px.png", ":/file_manager_go_home_16px.png",
+ ":/file_manager_go_home_disabled_24px.png", ":/file_manager_go_home_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerGoHome");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Go Home"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Go to home folder"));
+ setToolTip( QApplication::translate("UIActionPool", "Go to Home Folder")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Delete' in file manager action class. */
+class UIActionMenuFileManagerDelete : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerDelete(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_delete_24px.png", ":/file_manager_delete_16px.png",
+ ":/file_manager_delete_disabled_24px.png", ":/file_manager_delete_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerDelete");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Delete"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Delete selected file object(s)"));
+ setToolTip( QApplication::translate("UIActionPool", "Delete Selected Object(s)")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Refresh' in file manager action class. */
+class UIActionMenuFileManagerRefresh : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerRefresh(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_refresh_24px.png", ":/file_manager_refresh_16px.png",
+ ":/file_manager_refresh_disabled_24px.png", ":/file_manager_refresh_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerRefresh");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Refresh"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Refresh"));
+ setToolTip( QApplication::translate("UIActionPool", "Refresh Contents")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Rename' in file manager action class. */
+class UIActionMenuFileManagerRename : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerRename(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_rename_24px.png", ":/file_manager_rename_16px.png",
+ ":/file_manager_rename_disabled_24px.png", ":/file_manager_rename_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerRename");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Rename"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Rename selected file object"));
+ setToolTip( QApplication::translate("UIActionPool", "Rename Selected Object")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Rename' in file manager action class. */
+class UIActionMenuFileManagerCreateNewDirectory : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerCreateNewDirectory(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_new_directory_24px.png", ":/file_manager_new_directory_16px.png",
+ ":/file_manager_new_directory_disabled_24px.png", ":/file_manager_new_directory_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerCreateNewDirectory");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Create New Directory"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create New Directory"));
+ setToolTip( QApplication::translate("UIActionPool", "Create New Directory")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Copy' in file manager action class. */
+class UIActionMenuFileManagerCopy : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerCopy(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_copy_24px.png", ":/file_manager_copy_16px.png",
+ ":/file_manager_copy_disabled_24px.png", ":/file_manager_copy_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerCopy");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Copy"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Copy selected file object(s)"));
+ setToolTip( QApplication::translate("UIActionPool", "Copy Selected Object(s)")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Cut' in file manager action class. */
+class UIActionMenuFileManagerCut : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerCut(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_cut_24px.png", ":/file_manager_cut_16px.png",
+ ":/file_manager_cut_disabled_24px.png", ":/file_manager_cut_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerCut");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Cut"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Cut selected file object(s)"));
+ setToolTip( QApplication::translate("UIActionPool", "Cut Selected Object(s)")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Paste' in file manager action class. */
+class UIActionMenuFileManagerPaste : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerPaste(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_paste_24px.png", ":/file_manager_paste_16px.png",
+ ":/file_manager_paste_disabled_24px.png", ":/file_manager_paste_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerPaste");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Paste"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Paste copied/cut file object(s)"));
+ setToolTip( QApplication::translate("UIActionPool", "Paste Copied/Cut Object(s)")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Select All' in file manager action class. */
+class UIActionMenuFileManagerSelectAll : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerSelectAll(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_select_all_24px.png", ":/file_manager_select_all_16px.png",
+ ":/file_manager_select_all_disabled_24px.png", ":/file_manager_select_all_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerSelectAll");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Select All"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Select all files objects"));
+ setToolTip( QApplication::translate("UIActionPool", "Select All Objects")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Invert Selection' in file manager action class. */
+class UIActionMenuFileManagerInvertSelection : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerInvertSelection(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_invert_selection_24px.png", ":/file_manager_invert_selection_16px.png",
+ ":/file_manager_invert_selection_disabled_24px.png", ":/file_manager_invert_selection_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerInvertSelection");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Invert Selection"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Invert the current selection"));
+ setToolTip( QApplication::translate("UIActionPool", "Invert Current Selection")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Show Properties' in file manager action class. */
+class UIActionMenuFileManagerShowProperties : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuFileManagerShowProperties(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_properties_24px.png", ":/file_manager_properties_16px.png",
+ ":/file_manager_properties_disabled_24px.png", ":/file_manager_properties_disabled_16px.png"){}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerShowProperties");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Show Properties"));
+ setShortcutScope(QApplication::translate("UIActionPool", "File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Show the properties of currently selected file object(s)"));
+ setToolTip( QApplication::translate("UIActionPool", "Show Properties of Current Object(s)")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Menu action extension, used as 'VISO Creator' menu class. */
+class UIActionMenuVISOCreator : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuVISOCreator(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VISOCreatorMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "VISO Creator"));
+ }
+};
+
+/** Toggle action extension, used to toggle 'VISO Creator configuration' panel in file manager. */
+class UIActionMenuVISOCreatorToggleConfigPanel : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuVISOCreatorToggleConfigPanel(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/file_manager_options_32px.png",
+ ":/%file_manager_options_16px.png",
+ ":/file_manager_options_disabled_32px.png",
+ ":/file_manager_options_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleVISOCreatorConfigurationPanel");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Configuration"));
+ setShortcutScope(QApplication::translate("UIActionPool", "VISO Creator"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open panel for VISO Creator configuration"));
+ setToolTip(QApplication::translate("UIActionPool", "Open Configuration Panel")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used to toggle 'VISO Creator options' panel in file manager. */
+class UIActionMenuVISOCreatorToggleOptionsPanel : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuVISOCreatorToggleOptionsPanel(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(UIIconPool::iconSetFull(":/file_manager_options_32px.png",
+ ":/%file_manager_options_16px.png",
+ ":/file_manager_options_disabled_32px.png",
+ ":/file_manager_options_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleVISOCreatorOptionsPanel");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Options"));
+ setShortcutScope(QApplication::translate("UIActionPool", "VISO Creator"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open panel for VISO Creator options"));
+ setToolTip(QApplication::translate("UIActionPool", "Open Options Panel")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+class UIActionMenuVISOCreatorAdd : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuVISOCreatorAdd(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_copy_to_guest_24px.png",
+ ":/file_manager_copy_to_guest_16px.png",
+ ":/file_manager_copy_to_guest_disabled_24px.png",
+ ":/file_manager_copy_to_guest_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VISOAddItem");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Add"));
+ setShortcutScope(QApplication::translate("UIActionPool", "VISO Creator"));
+ setStatusTip(QApplication::translate("UIActionPool", "Add selected item(s) to VISO"));
+ setToolTip(QApplication::translate("UIActionPool", "Add Item(s) to VISO")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+class UIActionMenuVISOCreatorRemove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuVISOCreatorRemove(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_delete_24px.png",
+ ":/file_manager_delete_16px.png",
+ ":/file_manager_delete_disabled_24px.png",
+ ":/file_manager_delete_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VISORemoveItem");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Remove"));
+ setShortcutScope(QApplication::translate("UIActionPool", "VISO Creator"));
+ setStatusTip(QApplication::translate("UIActionPool", "Remove selected item(s) from VISO"));
+ setToolTip(QApplication::translate("UIActionPool", "Remove Item(s) from VISO")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+class UIActionMenuVISOCreatorCreateNewDirectory : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuVISOCreatorCreateNewDirectory(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_new_directory_24px.png",
+ ":/file_manager_new_directory_16px.png",
+ ":/file_manager_new_directory_disabled_24px.png",
+ ":/file_manager_new_directory_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VISONewDirectory");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&New Directory"));
+ setShortcutScope(QApplication::translate("UIActionPool", "VISO Creator"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create a new directory under the current location"));
+ setToolTip(QApplication::translate("UIActionPool", "Create New Directory")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+class UIActionMenuVISOCreatorRename : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuVISOCreatorRename(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/file_manager_rename_24px.png",
+ ":/file_manager_rename_16px.png",
+ ":/file_manager_rename_disabled_24px.png",
+ ":/file_manager_rename_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VISORenameItem");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Rename"));
+ setShortcutScope(QApplication::translate("UIActionPool", "VISO Creator"));
+ setStatusTip(QApplication::translate("UIActionPool", "Rename the selected object"));
+ setToolTip(QApplication::translate("UIActionPool", "Rename Object")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+class UIActionMenuVISOCreatorReset : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuVISOCreatorReset(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cd_remove_16px.png", ":/cd_remove_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VISOReset");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "R&eset"));
+ setShortcutScope(QApplication::translate("UIActionPool", "VISO Creator"));
+ setStatusTip(QApplication::translate("UIActionPool", "Reset the VISO content."));
+ setToolTip(QApplication::translate("UIActionPool", "Reset the VISO content.")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Menu action extension, used as 'Menu Selector' menu class. */
+class UIActionMenuMediumSelector : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuMediumSelector(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MediumSelector");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Medium Selector"));
+ }
+};
+
+/** Simple action extension, used as 'Add' action class. */
+class UIActionMenuMediumSelectorAddHD : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuMediumSelectorAddHD(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/hd_add_32px.png", ":/hd_add_16px.png",
+ ":/hd_add_disabled_32px.png", ":/hd_add_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MediumSelectorAddHD");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Add..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Medium Selector"));
+ setStatusTip(QApplication::translate("UIActionPool", "Add existing disk image file"));
+ setToolTip( QApplication::translate("UIActionPool", "Add Disk Image File")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Add' action class. */
+class UIActionMenuMediumSelectorAddCD : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuMediumSelectorAddCD(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/cd_add_32px.png", ":/cd_add_16px.png",
+ ":/cd_add_disabled_32px.png", ":/cd_add_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MediumSelectorAddCD");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Add..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Medium Selector"));
+ setStatusTip(QApplication::translate("UIActionPool", "Add existing disk image file"));
+ setToolTip( QApplication::translate("UIActionPool", "Add Disk Image File")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Add' action class. */
+class UIActionMenuMediumSelectorAddFD : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuMediumSelectorAddFD(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/fd_add_32px.png", ":/fd_add_16px.png",
+ ":/fd_add_disabled_32px.png", ":/fd_add_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MediumSelectorAddFD");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Add..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Medium Selector"));
+ setStatusTip(QApplication::translate("UIActionPool", "Add existing disk image file"));
+ setToolTip( QApplication::translate("UIActionPool", "Add Disk Image File")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Create' action class. */
+class UIActionMenuMediumSelectorCreateHD : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuMediumSelectorCreateHD(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/hd_create_32px.png", ":/hd_create_16px.png",
+ ":/hd_create_disabled_32px.png", ":/hd_create_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MediumSelectorCreateHD");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Create..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Medium Selector"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create a new disk image file"));
+ setToolTip( QApplication::translate("UIActionPool", "Create Disk Image File")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Create' action class. */
+class UIActionMenuMediumSelectorCreateCD : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuMediumSelectorCreateCD(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/cd_create_32px.png", ":/cd_create_16px.png",
+ ":/cd_create_disabled_32px.png", ":/cd_create_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MediumSelectorCreateCD");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Create..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Medium Selector"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create a new disk image file"));
+ setToolTip( QApplication::translate("UIActionPool", "Create Disk Image File")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Create' action class. */
+class UIActionMenuMediumSelectorCreateFD : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuMediumSelectorCreateFD(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/fd_create_32px.png", ":/fd_create_16px.png",
+ ":/fd_create_disabled_32px.png", ":/fd_create_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MediumSelectorCreateFD");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Create..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Medium Selector"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create a new disk image file"));
+ setToolTip( QApplication::translate("UIActionPool", "Create Disk Image File")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Create' action class. */
+class UIActionMenuMediumSelectorRefresh : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuMediumSelectorRefresh(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/refresh_32px.png", ":/refresh_16px.png",
+ ":/refresh_disabled_32px.png", ":/refresh_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MediumSelectorRefresh");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Refresh..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Medium Selector"));
+ setStatusTip(QApplication::translate("UIActionPool", "Refresh disk images"));
+ setToolTip( QApplication::translate("UIActionPool", "Refresh Disk Images")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Menu action extension, used as 'Activity' menu class. */
+class UIActionMenuSelectorActivity : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorActivity(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VMActivityMonitorMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Activity"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Export' action class. */
+class UIActionMenuSelectorActivityPerformExport : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorActivityPerformExport(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/performance_monitor_export_32px.png", ":/performance_monitor_export_16px.png",
+ ":/performance_monitor_export_disabled_32px.png", ":/performance_monitor_export_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VMActivityMonitorExportCharts");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Export..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "VM Activity Monitor"));
+ setStatusTip(QApplication::translate("UIActionPool", "Export the chart data into a text file"));
+ setToolTip( QApplication::translate("UIActionPool", "Export Data to File")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'To VM Activity Overview' action class. */
+class UIActionMenuSelectorActivityToVMActivityOverview : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuSelectorActivityToVMActivityOverview(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/resources_monitor_24px.png", ":/resource_monitor_16px.png",
+ ":/resource_monitor_disabled_24px.png", ":/resource_monitor_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToVMActivityOverview");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Activity Overview..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Activity Monitor"));
+ setStatusTip(QApplication::translate("UIActionPool", "Navigate to the vm activity overview"));
+ setToolTip( QApplication::translate("UIActionPool", "Navigate to VM Activity Overview")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIActionPool implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIActionPool *UIActionPool::create(UIActionPoolType enmType)
+{
+ UIActionPool *pActionPool = 0;
+ switch (enmType)
+ {
+ case UIActionPoolType_Manager: pActionPool = new UIActionPoolManager; break;
+ case UIActionPoolType_Runtime: pActionPool = new UIActionPoolRuntime; break;
+ default: AssertFailedReturn(0);
+ }
+ AssertPtrReturn(pActionPool, 0);
+ pActionPool->prepare();
+ return pActionPool;
+}
+
+/* static */
+void UIActionPool::destroy(UIActionPool *pActionPool)
+{
+ AssertPtrReturnVoid(pActionPool);
+ pActionPool->cleanup();
+ delete pActionPool;
+}
+
+/* static */
+void UIActionPool::createTemporary(UIActionPoolType enmType)
+{
+ UIActionPool *pActionPool = 0;
+ switch (enmType)
+ {
+ case UIActionPoolType_Manager: pActionPool = new UIActionPoolManager(true); break;
+ case UIActionPoolType_Runtime: pActionPool = new UIActionPoolRuntime(true); break;
+ default: AssertFailedReturnVoid();
+ }
+ AssertPtrReturnVoid(pActionPool);
+ pActionPool->prepare();
+ pActionPool->cleanup();
+ delete pActionPool;
+}
+
+UIActionPoolManager *UIActionPool::toManager()
+{
+ return qobject_cast<UIActionPoolManager*>(this);
+}
+
+UIActionPoolRuntime *UIActionPool::toRuntime()
+{
+ return qobject_cast<UIActionPoolRuntime*>(this);
+}
+
+UIAction *UIActionPool::action(int iIndex) const
+{
+ AssertReturn(m_pool.contains(iIndex), 0);
+ return m_pool.value(iIndex);
+}
+
+QList<UIAction*> UIActionPool::actions() const
+{
+ return m_pool.values();
+}
+
+QActionGroup *UIActionPool::actionGroup(int iIndex) const
+{
+ AssertReturn(m_groupPool.contains(iIndex), 0);
+ return m_groupPool.value(iIndex);
+}
+
+bool UIActionPool::isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType enmType) const
+{
+ foreach (const UIExtraDataMetaDefs::MenuType &enmRestriction, m_restrictedMenus.values())
+ if (enmRestriction & enmType)
+ return false;
+ return true;
+}
+
+void UIActionPool::setRestrictionForMenuBar(UIActionRestrictionLevel enmLevel, UIExtraDataMetaDefs::MenuType enmRestriction)
+{
+ m_restrictedMenus[enmLevel] = enmRestriction;
+ updateMenus();
+}
+
+bool UIActionPool::isAllowedInMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType enmType) const
+{
+ foreach (const UIExtraDataMetaDefs::MenuApplicationActionType &enmRestriction, m_restrictedActionsMenuApplication.values())
+ if (enmRestriction & enmType)
+ return false;
+ return true;
+}
+
+void UIActionPool::setRestrictionForMenuApplication(UIActionRestrictionLevel enmLevel, UIExtraDataMetaDefs::MenuApplicationActionType enmRestriction)
+{
+ m_restrictedActionsMenuApplication[enmLevel] = enmRestriction;
+ m_invalidations << UIActionIndex_M_Application;
+}
+
+#ifdef VBOX_WS_MAC
+bool UIActionPool::isAllowedInMenuWindow(UIExtraDataMetaDefs::MenuWindowActionType enmType) const
+{
+ foreach (const UIExtraDataMetaDefs::MenuWindowActionType &enmRestriction, m_restrictedActionsMenuWindow.values())
+ if (enmRestriction & enmType)
+ return false;
+ return true;
+}
+
+void UIActionPool::setRestrictionForMenuWindow(UIActionRestrictionLevel enmLevel, UIExtraDataMetaDefs::MenuWindowActionType enmRestriction)
+{
+ m_restrictedActionsMenuWindow[enmLevel] = enmRestriction;
+ m_invalidations << UIActionIndex_M_Window;
+}
+#endif /* VBOX_WS_MAC */
+
+bool UIActionPool::isAllowedInMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType enmType) const
+{
+ foreach (const UIExtraDataMetaDefs::MenuHelpActionType &enmRestriction, m_restrictedActionsMenuHelp.values())
+ if (enmRestriction & enmType)
+ return false;
+ return true;
+}
+
+void UIActionPool::setRestrictionForMenuHelp(UIActionRestrictionLevel enmLevel, UIExtraDataMetaDefs::MenuHelpActionType enmRestriction)
+{
+ m_restrictedActionsMenuHelp[enmLevel] = enmRestriction;
+ m_invalidations << UIActionIndex_Menu_Help;
+}
+
+bool UIActionPool::processHotKey(const QKeySequence &key)
+{
+ /* Iterate through the whole list of keys: */
+ foreach (const int &iKey, m_pool.keys())
+ {
+ /* Get current action: */
+ UIAction *pAction = m_pool.value(iKey);
+ /* Skip menus/separators: */
+ if (pAction->type() == UIActionType_Menu)
+ continue;
+ /* Get the hot-key of the current action: */
+ const QString strHotKey = gShortcutPool->shortcut(this, pAction).primaryToPortableText();
+ if (pAction->isEnabled() && pAction->isAllowed() && !strHotKey.isEmpty())
+ {
+ if (key.matches(QKeySequence(strHotKey)) == QKeySequence::ExactMatch)
+ {
+ /* We asynchronously post a special event instead of calling
+ * pAction->trigger() directly, to let key presses and
+ * releases be processed correctly by Qt first.
+ * Note: we assume that nobody will delete the menu item
+ * corresponding to the key sequence, so that the pointer to
+ * menu data posted along with the event will remain valid in
+ * the event handler, at least until the main window is closed. */
+ QApplication::postEvent(this, new ActivateActionEvent(pAction));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void UIActionPool::sltHandleMenuPrepare()
+{
+ /* Make sure menu is valid: */
+ AssertPtrReturnVoid(sender());
+ UIMenu *pMenu = qobject_cast<UIMenu*>(sender());
+ AssertPtrReturnVoid(pMenu);
+ /* Make sure action is valid: */
+ AssertPtrReturnVoid(pMenu->menuAction());
+ UIAction *pAction = qobject_cast<UIAction*>(pMenu->menuAction());
+ AssertPtrReturnVoid(pAction);
+
+ /* Determine action index: */
+ const int iIndex = m_pool.key(pAction);
+
+ /* Update menu if necessary: */
+ updateMenu(iIndex);
+
+ /* Notify listeners about menu prepared: */
+ emit sigNotifyAboutMenuPrepare(iIndex, pMenu);
+}
+
+#ifdef VBOX_WS_MAC
+void UIActionPool::sltActionHovered()
+{
+ /* Acquire sender action: */
+ UIAction *pAction = qobject_cast<UIAction*>(sender());
+ AssertPtrReturnVoid(pAction);
+ //printf("Action hovered: {%s}\n", pAction->name().toUtf8().constData());
+
+ /* Notify listener about action hevering: */
+ emit sigActionHovered(pAction);
+}
+#endif /* VBOX_WS_MAC */
+
+UIActionPool::UIActionPool(UIActionPoolType enmType, bool fTemporary /* = false */)
+ : m_enmType(enmType)
+ , m_fTemporary(fTemporary)
+{
+}
+
+void UIActionPool::preparePool()
+{
+ /* Create 'Application' actions: */
+ m_pool[UIActionIndex_M_Application] = new UIActionMenuApplication(this);
+#ifdef VBOX_WS_MAC
+ m_pool[UIActionIndex_M_Application_S_About] = new UIActionSimpleAbout(this);
+#endif
+ m_pool[UIActionIndex_M_Application_S_Preferences] = new UIActionSimplePreferences(this);
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ m_pool[UIActionIndex_M_Application_S_CheckForUpdates] = new UIActionSimpleCheckForUpdates(this);
+#endif
+ m_pool[UIActionIndex_M_Application_S_ResetWarnings] = new UIActionSimpleResetWarnings(this);
+ m_pool[UIActionIndex_M_Application_S_Close] = new UIActionSimplePerformClose(this);
+
+#ifdef VBOX_WS_MAC
+ /* Create 'Window' actions: */
+ m_pool[UIActionIndex_M_Window] = new UIActionMenuWindow(this);
+ m_pool[UIActionIndex_M_Window_S_Minimize] = new UIActionSimpleMinimize(this);
+#endif
+
+ /* Create 'Help' actions: */
+ m_pool[UIActionIndex_Menu_Help] = new UIActionMenuHelp(this);
+ m_pool[UIActionIndex_Simple_Contents] = new UIActionSimpleContents(this);
+ m_pool[UIActionIndex_Simple_WebSite] = new UIActionSimpleWebSite(this);
+ m_pool[UIActionIndex_Simple_BugTracker] = new UIActionSimpleBugTracker(this);
+ m_pool[UIActionIndex_Simple_Forums] = new UIActionSimpleForums(this);
+ m_pool[UIActionIndex_Simple_Oracle] = new UIActionSimpleOracle(this);
+ m_pool[UIActionIndex_Simple_OnlineDocumentation] = new UIActionSimpleOnlineDocumentation(this);
+#ifndef VBOX_WS_MAC
+ m_pool[UIActionIndex_Simple_About] = new UIActionSimpleAbout(this);
+#endif
+
+ /* Create 'Log Viewer' actions: */
+ m_pool[UIActionIndex_M_LogWindow] = new UIActionMenuSelectorLog(this);
+ m_pool[UIActionIndex_M_Log] = new UIActionMenuSelectorLog(this);
+ m_pool[UIActionIndex_M_Log_T_Find] = new UIActionMenuSelectorLogTogglePaneFind(this);
+ m_pool[UIActionIndex_M_Log_T_Filter] = new UIActionMenuSelectorLogTogglePaneFilter(this);
+ m_pool[UIActionIndex_M_Log_T_Bookmark] = new UIActionMenuSelectorLogTogglePaneBookmark(this);
+ m_pool[UIActionIndex_M_Log_T_Options] = new UIActionMenuSelectorLogTogglePaneOptions(this);
+ m_pool[UIActionIndex_M_Log_S_Refresh] = new UIActionMenuSelectorLogPerformRefresh(this);
+ m_pool[UIActionIndex_M_Log_S_Reload] = new UIActionMenuSelectorLogPerformReload(this);
+ m_pool[UIActionIndex_M_Log_S_Save] = new UIActionMenuSelectorLogPerformSave(this);
+
+ /* Create 'Performance Monitor' actions: */
+ m_pool[UIActionIndex_M_Activity] = new UIActionMenuSelectorActivity(this);
+ m_pool[UIActionIndex_M_Activity_S_Export] = new UIActionMenuSelectorActivityPerformExport(this);
+ m_pool[UIActionIndex_M_Activity_S_ToVMActivityOverview] = new UIActionMenuSelectorActivityToVMActivityOverview(this);
+
+ /* Create 'File Manager' actions: */
+ m_pool[UIActionIndex_M_FileManager] = new UIActionMenuFileManager(this);
+ m_pool[UIActionIndex_M_FileManager_M_HostSubmenu] = new UIActionMenuFileManagerHostSubmenu(this);
+ m_pool[UIActionIndex_M_FileManager_M_GuestSubmenu] = new UIActionMenuFileManagerGuestSubmenu(this);
+ m_pool[UIActionIndex_M_FileManager_S_CopyToGuest] = new UIActionMenuFileManagerCopyToGuest(this);
+ m_pool[UIActionIndex_M_FileManager_S_CopyToHost] = new UIActionMenuFileManagerCopyToHost(this);
+ m_pool[UIActionIndex_M_FileManager_T_Options] = new UIActionMenuFileManagerOptions(this);
+ m_pool[UIActionIndex_M_FileManager_T_Log] = new UIActionMenuFileManagerLog(this);
+ m_pool[UIActionIndex_M_FileManager_T_Operations] = new UIActionMenuFileManagerOperations(this);
+ m_pool[UIActionIndex_M_FileManager_T_GuestSession] = new UIActionMenuFileManagerGuestSession(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_GoUp] = new UIActionMenuFileManagerGoUp(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_GoUp] = new UIActionMenuFileManagerGoUp(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_GoHome] = new UIActionMenuFileManagerGoHome(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_GoHome] = new UIActionMenuFileManagerGoHome(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_Refresh] = new UIActionMenuFileManagerRefresh(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_Refresh] = new UIActionMenuFileManagerRefresh(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_Delete] = new UIActionMenuFileManagerDelete(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_Delete] = new UIActionMenuFileManagerDelete(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_Rename] = new UIActionMenuFileManagerRename(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_Rename] = new UIActionMenuFileManagerRename(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_CreateNewDirectory] = new UIActionMenuFileManagerCreateNewDirectory(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_CreateNewDirectory] = new UIActionMenuFileManagerCreateNewDirectory(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_Copy] = new UIActionMenuFileManagerCopy(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_Copy] = new UIActionMenuFileManagerCopy(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_Cut] = new UIActionMenuFileManagerCut(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_Cut] = new UIActionMenuFileManagerCut(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_Paste] = new UIActionMenuFileManagerPaste(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_Paste] = new UIActionMenuFileManagerPaste(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_SelectAll] = new UIActionMenuFileManagerSelectAll(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_SelectAll] = new UIActionMenuFileManagerSelectAll(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_InvertSelection] = new UIActionMenuFileManagerInvertSelection(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_InvertSelection] = new UIActionMenuFileManagerInvertSelection(this);
+ m_pool[UIActionIndex_M_FileManager_S_Host_ShowProperties] = new UIActionMenuFileManagerShowProperties(this);
+ m_pool[UIActionIndex_M_FileManager_S_Guest_ShowProperties] = new UIActionMenuFileManagerShowProperties(this);
+
+ /* Create VISO Creator actions: */
+ m_pool[UIActionIndex_M_VISOCreator] = new UIActionMenuVISOCreator(this);
+ m_pool[UIActionIndex_M_VISOCreator_ToggleConfigPanel] = new UIActionMenuVISOCreatorToggleConfigPanel(this);
+ m_pool[UIActionIndex_M_VISOCreator_ToggleOptionsPanel] = new UIActionMenuVISOCreatorToggleOptionsPanel(this);
+ m_pool[UIActionIndex_M_VISOCreator_Add] = new UIActionMenuVISOCreatorAdd(this);
+ m_pool[UIActionIndex_M_VISOCreator_Remove] = new UIActionMenuVISOCreatorRemove(this);
+ m_pool[UIActionIndex_M_VISOCreator_CreateNewDirectory] = new UIActionMenuVISOCreatorCreateNewDirectory(this);
+ m_pool[UIActionIndex_M_VISOCreator_Rename] = new UIActionMenuVISOCreatorRename(this);
+ m_pool[UIActionIndex_M_VISOCreator_Reset] = new UIActionMenuVISOCreatorReset(this);
+
+ /* Medium Selector actions: */
+ m_pool[UIActionIndex_M_MediumSelector] = new UIActionMenuMediumSelector(this);
+ m_pool[UIActionIndex_M_MediumSelector_AddHD] = new UIActionMenuMediumSelectorAddHD(this);
+ m_pool[UIActionIndex_M_MediumSelector_AddCD] = new UIActionMenuMediumSelectorAddCD(this);
+ m_pool[UIActionIndex_M_MediumSelector_AddFD] = new UIActionMenuMediumSelectorAddFD(this);
+ m_pool[UIActionIndex_M_MediumSelector_CreateHD] = new UIActionMenuMediumSelectorCreateHD(this);
+ m_pool[UIActionIndex_M_MediumSelector_CreateCD] = new UIActionMenuMediumSelectorCreateCD(this);
+ m_pool[UIActionIndex_M_MediumSelector_CreateFD] = new UIActionMenuMediumSelectorCreateFD(this);
+ m_pool[UIActionIndex_M_MediumSelector_Refresh] = new UIActionMenuMediumSelectorRefresh(this);
+
+ /* Prepare update-handlers for known menus: */
+#ifdef VBOX_WS_MAC
+ m_menuUpdateHandlers[UIActionIndex_M_Application].ptf = &UIActionPool::updateMenuApplication;
+ m_menuUpdateHandlers[UIActionIndex_M_Window].ptf = &UIActionPool::updateMenuWindow;
+#endif
+ m_menuUpdateHandlers[UIActionIndex_Menu_Help].ptf = &UIActionPool::updateMenuHelp;
+ m_menuUpdateHandlers[UIActionIndex_M_LogWindow].ptf = &UIActionPool::updateMenuLogViewerWindow;
+ m_menuUpdateHandlers[UIActionIndex_M_Log].ptf = &UIActionPool::updateMenuLogViewer;
+ m_menuUpdateHandlers[UIActionIndex_M_Activity].ptf = &UIActionPool::updateMenuVMActivityMonitor;
+ m_menuUpdateHandlers[UIActionIndex_M_FileManager].ptf = &UIActionPool::updateMenuFileManager;
+
+ /* Invalidate all known menus: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QList<int> const updateHandlerKeys = m_menuUpdateHandlers.keys();
+ m_invalidations.unite(QSet<int>(updateHandlerKeys.begin(), updateHandlerKeys.end()));
+#else
+ m_invalidations.unite(m_menuUpdateHandlers.keys().toSet());
+#endif
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIActionPool::prepareConnections()
+{
+ /* 'Application' menu connections: */
+#ifdef VBOX_WS_MAC
+ connect(action(UIActionIndex_M_Application_S_About), &UIAction::triggered,
+ &msgCenter(), &UIMessageCenter::sltShowHelpAboutDialog, Qt::UniqueConnection);
+#endif
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ connect(action(UIActionIndex_M_Application_S_CheckForUpdates), &UIAction::triggered,
+ gUpdateManager, &UIUpdateManager::sltForceCheck, Qt::UniqueConnection);
+#endif
+ connect(action(UIActionIndex_M_Application_S_ResetWarnings), &UIAction::triggered,
+ &msgCenter(), &UIMessageCenter::sltResetSuppressedMessages, Qt::UniqueConnection);
+
+ /* 'Help' menu connections. Note that connections for UIActionIndex_Simple_Contents is done
+ * in manager and runtime uis separately in their respective classes: */
+ connect(action(UIActionIndex_Simple_WebSite), &UIAction::triggered,
+ &msgCenter(), &UIMessageCenter::sltShowHelpWebDialog, Qt::UniqueConnection);
+ connect(action(UIActionIndex_Simple_BugTracker), &UIAction::triggered,
+ &msgCenter(), &UIMessageCenter::sltShowBugTracker, Qt::UniqueConnection);
+ connect(action(UIActionIndex_Simple_Forums), &UIAction::triggered,
+ &msgCenter(), &UIMessageCenter::sltShowForums, Qt::UniqueConnection);
+ connect(action(UIActionIndex_Simple_Oracle), &UIAction::triggered,
+ &msgCenter(), &UIMessageCenter::sltShowOracle, Qt::UniqueConnection);
+ connect(action(UIActionIndex_Simple_OnlineDocumentation), &UIAction::triggered,
+ &msgCenter(), &UIMessageCenter::sltShowOnlineDocumentation, Qt::UniqueConnection);
+#ifndef VBOX_WS_MAC
+ connect(action(UIActionIndex_Simple_About), &UIAction::triggered,
+ &msgCenter(), &UIMessageCenter::sltShowHelpAboutDialog, Qt::UniqueConnection);
+#endif
+}
+
+void UIActionPool::cleanupConnections()
+{
+ /* Nothing for now.. */
+}
+
+void UIActionPool::cleanupPool()
+{
+ qDeleteAll(m_groupPool);
+ qDeleteAll(m_pool);
+}
+
+void UIActionPool::updateConfiguration()
+{
+ /* Recache common action restrictions: */
+ // Nothing here for now..
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* Recache update action restrictions: */
+ bool fUpdateAllowed = gEDataManager->applicationUpdateEnabled();
+ if (!fUpdateAllowed)
+ {
+ m_restrictedActionsMenuApplication[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::MenuApplicationActionType)
+ (m_restrictedActionsMenuApplication[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::MenuApplicationActionType_CheckForUpdates);
+ }
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+ /* Update menus: */
+ updateMenus();
+}
+
+void UIActionPool::updateMenu(int iIndex)
+{
+ /* Make sure index belongs to this class: */
+ AssertReturnVoid(iIndex < UIActionIndex_Max);
+
+ /* If menu with such index is invalidated
+ * and there is update-handler => handle it here: */
+ if ( m_invalidations.contains(iIndex)
+ && m_menuUpdateHandlers.contains(iIndex))
+ (this->*(m_menuUpdateHandlers.value(iIndex).ptf))();
+}
+
+void UIActionPool::updateShortcuts()
+{
+ gShortcutPool->applyShortcuts(this);
+}
+
+bool UIActionPool::event(QEvent *pEvent)
+{
+ /* Depending on event-type: */
+ switch ((UIEventType)pEvent->type())
+ {
+ case ActivateActionEventType:
+ {
+ /* Process specific event: */
+ ActivateActionEvent *pActionEvent = static_cast<ActivateActionEvent*>(pEvent);
+ pActionEvent->action()->trigger();
+ pEvent->accept();
+ return true;
+ }
+ default:
+ break;
+ }
+ /* Pass to the base-class: */
+ return QObject::event(pEvent);
+}
+
+void UIActionPool::retranslateUi()
+{
+ /* Translate all the actions: */
+ foreach (const int iActionPoolKey, m_pool.keys())
+ m_pool[iActionPoolKey]->retranslateUi();
+ /* Update shortcuts: */
+ updateShortcuts();
+}
+
+bool UIActionPool::addAction(UIMenu *pMenu, UIAction *pAction, bool fReallyAdd /* = true */)
+{
+ /* Check if action is allowed: */
+ const bool fIsActionAllowed = pAction->isAllowed();
+
+#ifdef VBOX_WS_MAC
+ /* Check if menu is consumable: */
+ const bool fIsMenuConsumable = pMenu->isConsumable();
+ /* Check if menu is NOT yet consumed: */
+ const bool fIsMenuConsumed = pMenu->isConsumed();
+#endif
+
+ /* Make this action visible
+ * depending on clearance state. */
+ pAction->setVisible(fIsActionAllowed);
+
+#ifdef VBOX_WS_MAC
+ /* If menu is consumable: */
+ if (fIsMenuConsumable)
+ {
+ /* Add action only if menu was not yet consumed: */
+ if (!fIsMenuConsumed)
+ pMenu->addAction(pAction);
+ }
+ /* If menu is NOT consumable: */
+ else
+#endif
+ {
+ /* Add action only if is allowed: */
+ if (fIsActionAllowed && fReallyAdd)
+ pMenu->addAction(pAction);
+ }
+
+ /* Return if action is allowed: */
+ return fIsActionAllowed;
+}
+
+bool UIActionPool::addMenu(QList<QMenu*> &menuList, UIAction *pAction, bool fReallyAdd /* = true */)
+{
+ /* Check if action is allowed: */
+ const bool fIsActionAllowed = pAction->isAllowed();
+
+ /* Get action's menu: */
+ UIMenu *pMenu = pAction->menu();
+
+#ifdef VBOX_WS_MAC
+ /* Check if menu is consumable: */
+ const bool fIsMenuConsumable = pMenu->isConsumable();
+ /* Check if menu is NOT yet consumed: */
+ const bool fIsMenuConsumed = pMenu->isConsumed();
+#endif
+
+ /* Make this action visible
+ * depending on clearance state. */
+ pAction->setVisible( fIsActionAllowed
+#ifdef VBOX_WS_MAC
+ || fIsMenuConsumable
+#endif
+ );
+
+#ifdef VBOX_WS_MAC
+ /* If menu is consumable: */
+ if (fIsMenuConsumable)
+ {
+ /* Add action's menu only if menu was not yet consumed: */
+ if (!fIsMenuConsumed)
+ menuList << pMenu;
+ }
+ /* If menu is NOT consumable: */
+ else
+#endif
+ {
+ /* Add action only if is allowed: */
+ if (fIsActionAllowed && fReallyAdd)
+ menuList << pMenu;
+ }
+
+ /* Return if action is allowed: */
+ return fIsActionAllowed;
+}
+
+void UIActionPool::updateMenuApplication()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndex_M_Application)->menu();
+ AssertPtrReturnVoid(pMenu);
+#ifdef VBOX_WS_MAC
+ AssertReturnVoid(pMenu->isConsumable());
+#endif
+ /* Clear contents: */
+#ifdef VBOX_WS_MAC
+ if (!pMenu->isConsumed())
+#endif
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+#ifdef VBOX_WS_MAC
+ /* 'About' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Application_S_About)) || fSeparator;
+#endif
+
+ /* 'Preferences' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Application_S_Preferences)) || fSeparator;
+
+#ifndef VBOX_WS_MAC
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+#endif
+
+ /* 'Reset Warnings' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Application_S_ResetWarnings)) || fSeparator;
+
+#ifndef VBOX_WS_MAC
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+#endif
+
+ /* 'Close' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Application_S_Close)) || fSeparator;
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndex_M_Application);
+}
+
+#ifdef VBOX_WS_MAC
+void UIActionPool::updateMenuWindow()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndex_M_Window)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'Minimize' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Window_S_Minimize)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* This menu always remains invalid.. */
+}
+#endif /* VBOX_WS_MAC */
+
+void UIActionPool::updateMenuHelp()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndex_Menu_Help)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator? */
+ bool fSeparator = false;
+
+ /* 'Contents' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_Simple_Contents)) || fSeparator;
+ /* 'Online Documentation' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_Simple_OnlineDocumentation)) || fSeparator;
+ /* 'Web Site' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_Simple_WebSite)) || fSeparator;
+ /* 'Bug Tracker' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_Simple_BugTracker)) || fSeparator;
+ /* 'Forums' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_Simple_Forums)) || fSeparator;
+ /* 'Oracle' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_Simple_Oracle)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+#ifndef VBOX_WS_MAC
+ /* 'About' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_Simple_About)) || fSeparator;
+#endif
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndex_Menu_Help);
+}
+
+void UIActionPool::updateMenuLogViewerWindow()
+{
+ /* Update corresponding menu: */
+ updateMenuLogViewerWrapper(action(UIActionIndex_M_LogWindow)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndex_M_LogWindow);
+}
+
+void UIActionPool::updateMenuLogViewer()
+{
+ /* Update corresponding menu: */
+ updateMenuLogViewerWrapper(action(UIActionIndex_M_Log)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndex_M_Log);
+}
+
+void UIActionPool::updateMenuLogViewerWrapper(UIMenu *pMenu)
+{
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator? */
+ bool fSeparator = false;
+
+ /* 'Save' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Log_S_Save)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Find' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Log_T_Find)) || fSeparator;
+ /* 'Filter' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Log_T_Filter)) || fSeparator;
+ /* 'Bookmarks' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Log_T_Bookmark)) || fSeparator;
+ /* 'Options' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Log_T_Options)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Refresh' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Log_S_Refresh)) || fSeparator;
+ fSeparator = addAction(pMenu, action(UIActionIndex_M_Log_S_Reload)) || fSeparator;
+}
+
+void UIActionPool::updateMenuVMActivityMonitor()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndex_M_Activity)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* 'Export' and 'Switch to VM Activity Overview" actions: */
+ pMenu->addAction(action(UIActionIndex_M_Activity_S_Export));
+ pMenu->addAction(action(UIActionIndex_M_Activity_S_ToVMActivityOverview));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndex_M_Activity);
+}
+
+void UIActionPool::updateMenuFileManager()
+{
+ updateMenuFileManagerWrapper(action(UIActionIndex_M_FileManager)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndex_M_FileManager);
+}
+
+void UIActionPool::updateMenuFileManagerWrapper(UIMenu *pMenu)
+{
+ addAction(pMenu, action(UIActionIndex_M_FileManager_T_Options));
+ addAction(pMenu, action(UIActionIndex_M_FileManager_T_Operations));
+ addAction(pMenu, action(UIActionIndex_M_FileManager_T_Log));
+
+ addAction(pMenu, action(UIActionIndex_M_FileManager_M_HostSubmenu));
+ addAction(pMenu, action(UIActionIndex_M_FileManager_M_GuestSubmenu));
+
+ UIMenu *pHostSubmenu = action(UIActionIndex_M_FileManager_M_HostSubmenu)->menu();
+ if (pHostSubmenu)
+ {
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_GoUp));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_GoHome));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_Refresh));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_Delete));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_Rename));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_CreateNewDirectory));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_Copy));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_Cut));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_Paste));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_SelectAll));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_InvertSelection));
+ addAction(pHostSubmenu, action(UIActionIndex_M_FileManager_S_Host_ShowProperties));
+ }
+
+ UIMenu *pGuestSubmenu = action(UIActionIndex_M_FileManager_M_GuestSubmenu)->menu();
+ if (pGuestSubmenu)
+ {
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Host_GoUp));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_GoHome));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_Refresh));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_Delete));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_Rename));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_CreateNewDirectory));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_Copy));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_Cut));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_Paste));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_SelectAll));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_InvertSelection));
+ addAction(pGuestSubmenu, action(UIActionIndex_M_FileManager_S_Guest_ShowProperties));
+ }
+}
+
+void UIActionPool::prepare()
+{
+ /* Prepare pool: */
+ preparePool();
+ /* Prepare connections: */
+ prepareConnections();
+
+ /* Update configuration: */
+ updateConfiguration();
+ /* Update shortcuts: */
+ updateShortcuts();
+}
+
+void UIActionPool::cleanup()
+{
+ /* Cleanup connections: */
+ cleanupConnections();
+ /* Cleanup pool: */
+ cleanupPool();
+}
+
+
+#include "UIActionPool.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIActionPool.h b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPool.h
new file mode 100644
index 00000000..35377fb0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPool.h
@@ -0,0 +1,672 @@
+/* $Id: UIActionPool.h $ */
+/** @file
+ * VBox Qt GUI - UIActionPool class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIActionPool_h
+#define FEQT_INCLUDED_SRC_globals_UIActionPool_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QAction>
+#include <QMenu>
+#include <QVector>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QKeySequence;
+class QString;
+class UIActionPool;
+class UIActionPoolRuntime;
+class UIActionPoolManager;
+
+
+/** Action-pool types. */
+enum UIActionPoolType
+{
+ UIActionPoolType_Manager,
+ UIActionPoolType_Runtime
+};
+
+/** Action types. */
+enum UIActionType
+{
+ UIActionType_Menu,
+ UIActionType_Simple,
+ UIActionType_Toggle
+};
+
+/** Action indexes. */
+enum UIActionIndex
+{
+ /* 'Application' menu actions: */
+ UIActionIndex_M_Application,
+#ifdef VBOX_WS_MAC
+ UIActionIndex_M_Application_S_About,
+#endif
+ UIActionIndex_M_Application_S_Preferences,
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ UIActionIndex_M_Application_S_CheckForUpdates,
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ UIActionIndex_M_Application_S_ResetWarnings,
+ UIActionIndex_M_Application_S_Close,
+
+#ifdef VBOX_WS_MAC
+ /* 'Window' menu actions: */
+ UIActionIndex_M_Window,
+ UIActionIndex_M_Window_S_Minimize,
+#endif
+
+ /* 'Help' menu actions: */
+ UIActionIndex_Menu_Help,
+ UIActionIndex_Simple_Contents,
+ UIActionIndex_Simple_WebSite,
+ UIActionIndex_Simple_BugTracker,
+ UIActionIndex_Simple_Forums,
+ UIActionIndex_Simple_Oracle,
+ UIActionIndex_Simple_OnlineDocumentation,
+#ifndef VBOX_WS_MAC
+ UIActionIndex_Simple_About,
+#endif
+
+ /* 'Log' menu actions: */
+ UIActionIndex_M_LogWindow,
+ UIActionIndex_M_Log,
+ UIActionIndex_M_Log_T_Find,
+ UIActionIndex_M_Log_T_Filter,
+ UIActionIndex_M_Log_T_Bookmark,
+ UIActionIndex_M_Log_T_Options,
+ UIActionIndex_M_Log_S_Refresh,
+ UIActionIndex_M_Log_S_Reload,
+ UIActionIndex_M_Log_S_Save,
+
+ /* 'Performance' menu actions: */
+ UIActionIndex_M_Activity,
+ UIActionIndex_M_Activity_S_Export,
+ UIActionIndex_M_Activity_S_ToVMActivityOverview,
+
+ /* File Manager actions: */
+ UIActionIndex_M_FileManager,
+ UIActionIndex_M_FileManager_M_HostSubmenu,
+ UIActionIndex_M_FileManager_M_GuestSubmenu,
+ UIActionIndex_M_FileManager_S_CopyToGuest,
+ UIActionIndex_M_FileManager_S_CopyToHost,
+ UIActionIndex_M_FileManager_T_Options,
+ UIActionIndex_M_FileManager_T_Log,
+ UIActionIndex_M_FileManager_T_Operations,
+ UIActionIndex_M_FileManager_T_GuestSession,
+ UIActionIndex_M_FileManager_S_Host_GoUp,
+ UIActionIndex_M_FileManager_S_Guest_GoUp,
+ UIActionIndex_M_FileManager_S_Host_GoHome,
+ UIActionIndex_M_FileManager_S_Guest_GoHome,
+ UIActionIndex_M_FileManager_S_Host_Refresh,
+ UIActionIndex_M_FileManager_S_Guest_Refresh,
+ UIActionIndex_M_FileManager_S_Host_Delete,
+ UIActionIndex_M_FileManager_S_Guest_Delete,
+ UIActionIndex_M_FileManager_S_Host_Rename,
+ UIActionIndex_M_FileManager_S_Guest_Rename,
+ UIActionIndex_M_FileManager_S_Host_CreateNewDirectory,
+ UIActionIndex_M_FileManager_S_Guest_CreateNewDirectory,
+ UIActionIndex_M_FileManager_S_Host_Copy,
+ UIActionIndex_M_FileManager_S_Guest_Copy,
+ UIActionIndex_M_FileManager_S_Host_Cut,
+ UIActionIndex_M_FileManager_S_Guest_Cut,
+ UIActionIndex_M_FileManager_S_Host_Paste,
+ UIActionIndex_M_FileManager_S_Guest_Paste,
+ UIActionIndex_M_FileManager_S_Host_SelectAll,
+ UIActionIndex_M_FileManager_S_Guest_SelectAll,
+ UIActionIndex_M_FileManager_S_Host_InvertSelection,
+ UIActionIndex_M_FileManager_S_Guest_InvertSelection,
+ UIActionIndex_M_FileManager_S_Host_ShowProperties,
+ UIActionIndex_M_FileManager_S_Guest_ShowProperties,
+
+ /* VISO Creator actions: */
+ UIActionIndex_M_VISOCreator,
+ UIActionIndex_M_VISOCreator_ToggleConfigPanel,
+ UIActionIndex_M_VISOCreator_ToggleOptionsPanel,
+ UIActionIndex_M_VISOCreator_Add,
+ UIActionIndex_M_VISOCreator_Remove,
+ UIActionIndex_M_VISOCreator_CreateNewDirectory,
+ UIActionIndex_M_VISOCreator_Rename,
+ UIActionIndex_M_VISOCreator_Reset,
+
+ /* Medium selector actions : */
+ UIActionIndex_M_MediumSelector,
+ UIActionIndex_M_MediumSelector_AddHD,
+ UIActionIndex_M_MediumSelector_AddFD,
+ UIActionIndex_M_MediumSelector_AddCD,
+ UIActionIndex_M_MediumSelector_CreateHD,
+ UIActionIndex_M_MediumSelector_CreateCD,
+ UIActionIndex_M_MediumSelector_CreateFD,
+ UIActionIndex_M_MediumSelector_Refresh,
+
+ /* Maximum index: */
+ UIActionIndex_Max
+};
+
+/** Action restriction levels. */
+enum UIActionRestrictionLevel
+{
+ UIActionRestrictionLevel_Base,
+ UIActionRestrictionLevel_Session,
+ UIActionRestrictionLevel_Logic
+};
+
+
+/** QMenu extension. */
+class SHARED_LIBRARY_STUFF UIMenu : public QMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs menu. */
+ UIMenu();
+
+ /** Defines whether tool-tip should be shown. */
+ void setShowToolTip(bool fShowToolTips) { m_fShowToolTip = fShowToolTips; }
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Returns whether this menu is consumable by the menu-bar. */
+ bool isConsumable() const { return m_fConsumable; }
+ /** Mac OS X: Defines whether this menu is @a fConsumable by the menu-bar. */
+ void setConsumable(bool fConsumable) { m_fConsumable = fConsumable; }
+
+ /** Mac OS X: Returns whether this menu is consumed by the menu-bar. */
+ bool isConsumed() const { return m_fConsumed; }
+ /** Mac OS X: Defines whether this menu is @a fConsumed by the menu-bar. */
+ void setConsumed(bool fConsumed) { m_fConsumed = fConsumed; }
+#endif /* VBOX_WS_MAC */
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent);
+
+private:
+
+ /** Holds whether tool-tip should be shown. */
+ bool m_fShowToolTip;
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Holds whether this menu can be consumed by the menu-bar. */
+ bool m_fConsumable;
+ /** Mac OS X: Holds whether this menu is consumed by the menu-bar. */
+ bool m_fConsumed;
+#endif /* VBOX_WS_MAC */
+};
+
+
+/** Abstract QAction extension. */
+class SHARED_LIBRARY_STUFF UIAction : public QAction
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class.
+ * @param enmType Brings the action type. */
+ UIAction(UIActionPool *pParent, UIActionType enmType, bool fMachineMenuAction = false);
+ /** Destructs action. */
+ virtual ~UIAction() RT_OVERRIDE { delete menu(); QAction::setShortcuts(QList<QKeySequence>() /*unregister*/); }
+
+ /** Returns action-pool this action belongs to. */
+ UIActionPool *actionPool() const { return m_pActionPool; }
+ /** Returns action type. */
+ UIActionType type() const { return m_enmType; }
+
+ /** Returns menu contained by this action. */
+ UIMenu *menu() const;
+
+ /** Returns current action state. */
+ int state() const { return m_iState; }
+ /** Defines current action @a iState. */
+ void setState(int iState);
+
+ /** Defines @a icon for certain @a iState. */
+ void setIcon(int iState, const QIcon &icon);
+ /** Defines @a icon. */
+ void setIcon(const QIcon &icon);
+
+ /** Returns current action name. */
+ QString name() const { return m_strName; }
+ /** Defines current action name. */
+ void setName(const QString &strName);
+
+ /** Returns action shortcut scope. */
+ QString shortcutScope() const { return m_strShortcutScope; }
+ /** Defines action @a strShortcutScope. */
+ void setShortcutScope(const QString &strShortcutScope) { m_strShortcutScope = strShortcutScope; }
+
+ /** Defines current keyboard shortcuts for this action. */
+ void setShortcuts(const QList<QKeySequence> &shortcuts);
+ /** Make action show keyboard shortcut. */
+ void showShortcut();
+ /** Make action hide keyboard shortcut. */
+ void hideShortcut();
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const { return 0; }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const { return QString(); }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const { return true; }
+
+ /** Returns extra-data ID to save keyboard shortcut under. */
+ virtual QString shortcutExtraDataID() const { return QString(); }
+ /** Returns default keyboard shortcut for this action. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const { return QKeySequence(); }
+ /** Returns standard keyboard shortcut for this action. */
+ virtual QKeySequence standardShortcut(UIActionPoolType) const { return QKeySequence(); }
+
+ /** Retranslates action. */
+ virtual void retranslateUi() = 0;
+
+protected:
+
+ /** Handles state change. */
+ virtual void handleStateChange() {}
+
+ /** Returns current action name in menu. */
+ QString nameInMenu() const;
+
+ /** Updates action icon. */
+ void updateIcon();
+ /** Updates action text. */
+ void updateText();
+
+ /** Simplifies passed @a strText by removing dots and ampersands.
+ * @note Used to simplify action names for tool-tip needs. */
+ static QString simplifyText(QString strText);
+
+ /** Holds the reference to the action-pool this action belongs to. */
+ UIActionPool *m_pActionPool;
+ /** Holds the type of the action-pool this action belongs to. */
+ const UIActionPoolType m_enmActionPoolType;
+
+ /** Holds the action type. */
+ const UIActionType m_enmType;
+ /** Holds whether this is machine-menu action. */
+ const bool m_fMachineMenuAction;
+
+ /** Holds current action state. */
+ int m_iState;
+ /** Holds action icons. */
+ QVector<QIcon> m_icons;
+
+ /** Holds the action name. */
+ QString m_strName;
+
+ /** Holds the action shortcut scope. */
+ QString m_strShortcutScope;
+ /** Holds the action shortcuts. */
+ QList<QKeySequence> m_shortcuts;
+ /** Holds whether action shortcut hidden. */
+ bool m_fShortcutHidden;
+};
+
+
+/** Abstract UIAction extension for 'Menu' action type. */
+class SHARED_LIBRARY_STUFF UIActionMenu : public UIAction
+{
+ Q_OBJECT;
+
+protected:
+
+ /** Constructs menu action passing @a pParent to the base-class.
+ * @param strIcon Brings the normal-icon name.
+ * @param strIconDisabled Brings the disabled-icon name. */
+ UIActionMenu(UIActionPool *pParent,
+ const QString &strIcon = QString(), const QString &strIconDisabled = QString());
+ /** Constructs menu action passing @a pParent to the base-class.
+ * @param strIconNormal Brings the normal-icon name.
+ * @param strIconSmall Brings the small-icon name.
+ * @param strIconNormalDisabled Brings the normal-disabled-icon name.
+ * @param strIconSmallDisabled Brings the small-disabled-icon name. */
+ UIActionMenu(UIActionPool *pParent,
+ const QString &strIconNormal, const QString &strIconSmall,
+ const QString &strIconNormalDisabled, const QString &strIconSmallDisabled);
+ /** Constructs menu action passing @a pParent to the base-class.
+ * @param icon Brings the icon. */
+ UIActionMenu(UIActionPool *pParent,
+ const QIcon &icon);
+
+ /** Destructs menu action. */
+ virtual ~UIActionMenu() RT_OVERRIDE;
+
+ /** Defines whether tool-tip should be shown. */
+ void setShowToolTip(bool fShowToolTip);
+
+ /** Shows menu. */
+ void showMenu();
+ /** Hides menu. */
+ void hideMenu();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the menu instance. */
+ UIMenu *m_pMenu;
+};
+
+
+/** Abstract UIAction extension for 'Simple' action type. */
+class SHARED_LIBRARY_STUFF UIActionSimple : public UIAction
+{
+ Q_OBJECT;
+
+protected:
+
+ /** Constructs simple action passing @a pParent to the base-class.
+ * @param fMachineMenuAction Brings whether this action is a part of machine menu. */
+ UIActionSimple(UIActionPool *pParent,
+ bool fMachineMenuAction = false);
+ /** Constructs simple action passing @a pParent to the base-class.
+ * @param strIcon Brings the normal-icon name.
+ * @param strIconDisabled Brings the disabled-icon name.
+ * @param fMachineMenuAction Brings whether this action is a part of machine menu. */
+ UIActionSimple(UIActionPool *pParent,
+ const QString &strIcon, const QString &strIconDisabled,
+ bool fMachineMenuAction = false);
+ /** Constructs simple action passing @a pParent to the base-class.
+ * @param strIconNormal Brings the normal-icon name.
+ * @param strIconSmall Brings the small-icon name.
+ * @param strIconNormalDisabled Brings the normal-disabled-icon name.
+ * @param strIconSmallDisabled Brings the small-disabled-icon name.
+ * @param fMachineMenuAction Brings whether this action is a part of machine menu. */
+ UIActionSimple(UIActionPool *pParent,
+ const QString &strIconNormal, const QString &strIconSmall,
+ const QString &strIconNormalDisabled, const QString &strIconSmallDisabled,
+ bool fMachineMenuAction = false);
+ /** Constructs simple action passing @a pParent to the base-class.
+ * @param icon Brings the icon.
+ * @param fMachineMenuAction Brings whether this action is a part of machine menu. */
+ UIActionSimple(UIActionPool *pParent,
+ const QIcon &icon,
+ bool fMachineMenuAction = false);
+};
+
+
+/** Abstract UIAction extension for 'Toggle' action type. */
+class SHARED_LIBRARY_STUFF UIActionToggle : public UIAction
+{
+ Q_OBJECT;
+
+protected:
+
+ /** Constructs toggle action passing @a pParent to the base-class.
+ * @param fMachineMenuAction Brings whether this action is a part of machine menu. */
+ UIActionToggle(UIActionPool *pParent,
+ bool fMachineMenuAction = false);
+ /** Constructs toggle action passing @a pParent to the base-class.
+ * @param strIcon Brings the normal-icon name.
+ * @param strIconDisabled Brings the disabled-icon name.
+ * @param fMachineMenuAction Brings whether this action is a part of machine menu. */
+ UIActionToggle(UIActionPool *pParent,
+ const QString &strIcon, const QString &strIconDisabled,
+ bool fMachineMenuAction = false);
+ /** Constructs toggle action passing @a pParent to the base-class.
+ * @param strIconOn Brings the on-icon name.
+ * @param strIconOff Brings the off-icon name.
+ * @param strIconOnDisabled Brings the on-disabled-icon name.
+ * @param strIconOffDisabled Brings the off-disabled-icon name.
+ * @param fMachineMenuAction Brings whether this action is a part of machine menu. */
+ UIActionToggle(UIActionPool *pParent,
+ const QString &strIconOn, const QString &strIconOff,
+ const QString &strIconOnDisabled, const QString &strIconOffDisabled,
+ bool fMachineMenuAction = false);
+ /** Constructs toggle action passing @a pParent to the base-class.
+ * @param icon Brings the icon.
+ * @param fMachineMenuAction Brings whether this action is a part of machine menu. */
+ UIActionToggle(UIActionPool *pParent,
+ const QIcon &icon,
+ bool fMachineMenuAction = false);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+};
+
+
+/** Abstract QObject extension
+ * representing action-pool interface and factory. */
+class SHARED_LIBRARY_STUFF UIActionPool : public QIWithRetranslateUI3<QObject>
+{
+ Q_OBJECT;
+
+#if RT_MSC_PREREQ(RT_MSC_VER_VS2019_U11)
+# pragma warning(push)
+# pragma warning(disable: 5243) /* warning C5243: 'UIActionPool::PTFActionPoolManager': using incomplete class 'UIActionPoolManager' can cause potential one definition rule violation due to ABI limitation */
+#endif
+ /** Pointer to menu update-handler for this class. */
+ typedef void (UIActionPool::*PTFActionPool)();
+ /** Pointer to menu update-handler for Manager sub-class. */
+ typedef void (UIActionPoolManager::*PTFActionPoolManager)();
+ /** Pointer to menu update-handler for Runtime sub-class. */
+ typedef void (UIActionPoolRuntime::*PTFActionPoolRuntime)();
+ /** Union for three defines above. */
+ union PointerToFunction
+ {
+ PTFActionPool ptf;
+ PTFActionPoolManager ptfm;
+ PTFActionPoolRuntime ptfr;
+ };
+#if RT_MSC_PREREQ(RT_MSC_VER_VS2019_U11)
+# pragma warning(pop)
+#endif
+
+signals:
+
+ /** Notifies about menu prepare. */
+ void sigNotifyAboutMenuPrepare(int iIndex, QMenu *pMenu);
+
+#ifdef VBOX_WS_MAC
+ /** Notifies about @a pAction hovered. */
+ void sigActionHovered(UIAction *pAction);
+#endif
+
+public:
+
+ /** Creates singleton instance. */
+ static UIActionPool *create(UIActionPoolType enmType);
+ /** Destroys singleton instance. */
+ static void destroy(UIActionPool *pActionPool);
+
+ /** Creates temporary singleton instance,
+ * used to initialize shortcuts-pool from action-pool of passed @a enmType. */
+ static void createTemporary(UIActionPoolType enmType);
+
+ /** Cast action-pool to Manager one. */
+ UIActionPoolManager *toManager();
+ /** Cast action-pool to Runtime one. */
+ UIActionPoolRuntime *toRuntime();
+
+ /** Returns action-pool type. */
+ UIActionPoolType type() const { return m_enmType; }
+ /** Returns whether this action-pool is temporary. */
+ bool isTemporary() const { return m_fTemporary; }
+
+ /** Returns the action for the passed @a iIndex. */
+ UIAction *action(int iIndex) const;
+ /** Returns all the actions action-pool contains. */
+ QList<UIAction*> actions() const;
+
+ /** Returns the action group for the passed @a iIndex.
+ * @note Only menu actions can have action groups. */
+ QActionGroup *actionGroup(int iIndex) const;
+
+ /** Returns the list of main menus. */
+ QList<QMenu*> menus() const { return m_mainMenus; }
+
+ /** Returns whether the menu with passed @a type is allowed in menu-bar. */
+ bool isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType type) const;
+ /** Defines menu-bar @a enmRestriction for passed @a level. */
+ void setRestrictionForMenuBar(UIActionRestrictionLevel level, UIExtraDataMetaDefs::MenuType enmRestriction);
+
+ /** Returns whether the action with passed @a type is allowed in the 'Application' menu. */
+ bool isAllowedInMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType type) const;
+ /** Defines 'Application' menu @a enmRestriction for passed @a level. */
+ void setRestrictionForMenuApplication(UIActionRestrictionLevel level, UIExtraDataMetaDefs::MenuApplicationActionType enmRestriction);
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Returns whether the action with passed @a type is allowed in the 'Window' menu. */
+ bool isAllowedInMenuWindow(UIExtraDataMetaDefs::MenuWindowActionType type) const;
+ /** Mac OS X: Defines 'Window' menu @a enmRestriction for passed @a level. */
+ void setRestrictionForMenuWindow(UIActionRestrictionLevel level, UIExtraDataMetaDefs::MenuWindowActionType enmRestriction);
+#endif /* VBOX_WS_MAC */
+
+ /** Returns whether the action with passed @a type is allowed in the 'Help' menu. */
+ bool isAllowedInMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType enmType) const;
+ /** Defines 'Help' menu @a enmRestriction for passed @a level. */
+ void setRestrictionForMenuHelp(UIActionRestrictionLevel enmLevel, UIExtraDataMetaDefs::MenuHelpActionType enmRestriction);
+
+ /** Hot-key processing delegate. */
+ bool processHotKey(const QKeySequence &key);
+
+ /** Defines whether shortcuts of menu actions with specified @a iIndex should be visible. */
+ virtual void setShortcutsVisible(int iIndex, bool fVisible) { Q_UNUSED(iIndex); Q_UNUSED(fVisible); }
+ /** Returns extra-data ID to save keyboard shortcuts under. */
+ virtual QString shortcutsExtraDataID() const = 0;
+
+public slots:
+
+ /** Handles menu prepare. */
+ void sltHandleMenuPrepare();
+
+#ifdef VBOX_WS_MAC
+ /** Handles action hovered signal. */
+ void sltActionHovered();
+#endif
+
+protected slots:
+
+ /** Loads keyboard shortcuts of action-pool into shortcuts-pool. */
+ void sltApplyShortcuts() { updateShortcuts(); }
+
+protected:
+
+ /** Constructs probably @a fTemporary action-pool of passed @a enmType. */
+ UIActionPool(UIActionPoolType enmType, bool fTemporary = false);
+
+ /** Prepares pool. */
+ virtual void preparePool();
+ /** Prepares connections. */
+ virtual void prepareConnections();
+ /** Cleanups connections. */
+ virtual void cleanupConnections();
+ /** Cleanups pool. */
+ virtual void cleanupPool();
+
+ /** Updates configuration. */
+ virtual void updateConfiguration();
+
+ /** Updates menu with certain @a iIndex. */
+ virtual void updateMenu(int iIndex);
+ /** Updates menus. */
+ virtual void updateMenus() = 0;
+
+ /** Updates shortcuts. */
+ virtual void updateShortcuts();
+
+ /** Handles any Qt @a pEvent */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Adds action into corresponding menu. */
+ bool addAction(UIMenu *pMenu, UIAction *pAction, bool fReallyAdd = true);
+ /** Adds action's menu into corresponding menu list. */
+ bool addMenu(QList<QMenu*> &menuList, UIAction *pAction, bool fReallyAdd = true);
+
+ /** Updates 'Application' menu. */
+ void updateMenuApplication();
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Updates 'Window' menu. */
+ void updateMenuWindow();
+#endif
+ /** Updates 'Help' menu. */
+ void updateMenuHelp();
+ /** Updates 'Log Viewer Window' menu. */
+ void updateMenuLogViewerWindow();
+ /** Updates 'Log Viewer' menu. */
+ void updateMenuLogViewer();
+ /** Updates 'Log Viewer' @a pMenu. */
+ void updateMenuLogViewerWrapper(UIMenu *pMenu);
+ /** Updates 'Performance Monitor' menu. */
+ void updateMenuVMActivityMonitor();
+ /** Updates 'File Manager' menu. */
+ void updateMenuFileManager();
+ /** Updates 'File Manager' @a pMenu. */
+ void updateMenuFileManagerWrapper(UIMenu *pMenu);
+
+ /** Holds the map of actions. */
+ QMap<int, UIAction*> m_pool;
+ /** Holds the map of action groups.
+ * @note Only menu actions can have action groups. */
+ QMap<int, QActionGroup*> m_groupPool;
+ /** Holds the map of validation handlers. */
+ QMap<int, PointerToFunction> m_menuUpdateHandlers;
+ /** Holds the set of invalidated action indexes. */
+ QSet<int> m_invalidations;
+
+ /** Holds the list of main menus. */
+ QList<QMenu*> m_mainMenus;
+
+ /** Holds restricted menu types. */
+ QMap<UIActionRestrictionLevel, UIExtraDataMetaDefs::MenuType> m_restrictedMenus;
+ /** Holds restricted action types of the 'Application' menu. */
+ QMap<UIActionRestrictionLevel, UIExtraDataMetaDefs::MenuApplicationActionType> m_restrictedActionsMenuApplication;
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Holds restricted action types of the 'Window' menu. */
+ QMap<UIActionRestrictionLevel, UIExtraDataMetaDefs::MenuWindowActionType> m_restrictedActionsMenuWindow;
+#endif
+ /** Holds restricted action types of the Help menu. */
+ QMap<UIActionRestrictionLevel, UIExtraDataMetaDefs::MenuHelpActionType> m_restrictedActionsMenuHelp;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the action-pool type. */
+ const UIActionPoolType m_enmType;
+ /** Holds whether this action-pool is temporary. */
+ const bool m_fTemporary;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIActionPool_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolManager.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolManager.cpp
new file mode 100644
index 00000000..ebcaebe3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolManager.cpp
@@ -0,0 +1,4648 @@
+/* $Id: UIActionPoolManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIActionPoolManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QActionGroup>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIActionPoolManager.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIShortcutPool.h"
+#include "UIDefs.h"
+
+/* COM includes: */
+#include "CExtPack.h"
+#include "CExtPackManager.h"
+
+/* TEMPORARY! */
+#if defined(_MSC_VER) && !defined(RT_ARCH_AMD64)
+# pragma optimize("g", off)
+#endif
+
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+
+
+/** Menu action extension, used as 'File' menu class. */
+class UIActionMenuManagerFile : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerFile(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+#ifdef VBOX_WS_MAC
+ setName(QApplication::translate("UIActionPool", "&File", "Mac OS X version"));
+#else /* VBOX_WS_MAC */
+ setName(QApplication::translate("UIActionPool", "&File", "Non Mac OS X version"));
+#endif /* !VBOX_WS_MAC */
+ }
+};
+
+/** Simple action extension, used as 'Show Import Appliance Wizard' action class. */
+class UIActionSimpleManagerFileShowImportApplianceWizard : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerFileShowImportApplianceWizard(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/import_32px.png", ":/import_16px.png",
+ ":/import_disabled_32px.png", ":/import_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ImportAppliance");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+I");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Import"));
+ setName(QApplication::translate("UIActionPool", "&Import Appliance..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Import an appliance into VirtualBox"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Show Export Appliance Wizard' action class. */
+class UIActionSimpleManagerFileShowExportApplianceWizard : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerFileShowExportApplianceWizard(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/export_32px.png", ":/export_16px.png",
+ ":/export_disabled_32px.png", ":/export_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ExportAppliance");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+E");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Export"));
+ setName(QApplication::translate("UIActionPool", "&Export Appliance..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Export one or more VirtualBox virtual machines as an appliance"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Menu action extension, used as 'Global Tools' menu class. */
+class UIActionMenuManagerToolsGlobal : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerToolsGlobal(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/tools_menu_24px.png") /// @todo replace with 16px icon
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToolsGlobalMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Tools"));
+ }
+};
+
+/** Simple action extension, used as 'Show Welcome Screen' action class. */
+class UIActionToggleManagerToolsGlobalShowWelcomeScreen : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsGlobalShowWelcomeScreen(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_Welcome));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/welcome_screen_24px.png", ":/welcome_screen_24px.png",
+ ":/welcome_screen_24px.png", ":/welcome_screen_24px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("WelcomeScreen");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Welcome Screen"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the Welcome Screen"));
+ }
+};
+
+/** Simple action extension, used as 'Show Extension Pack Manager' action class. */
+class UIActionToggleManagerToolsGlobalShowExtensionPackManager : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsGlobalShowExtensionPackManager(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_Extensions));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/extension_pack_manager_24px.png", ":/extension_pack_manager_16px.png",
+ ":/extension_pack_manager_disabled_24px.png", ":/extension_pack_manager_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ExtensionPackManager");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+T");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Extension Pack Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the Extension Pack Manager"));
+ }
+};
+
+/** Simple action extension, used as 'Show Virtual Media Manager' action class. */
+class UIActionToggleManagerToolsGlobalShowVirtualMediaManager : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsGlobalShowVirtualMediaManager(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_Media));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/media_manager_24px.png", ":/media_manager_16px.png",
+ ":/media_manager_disabled_24px.png", ":/media_manager_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VirtualMediaManager");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+D");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Virtual Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the Virtual Media Manager"));
+ }
+};
+
+/** Simple action extension, used as 'Show Network Manager' action class. */
+class UIActionToggleManagerToolsGlobalShowNetworkManager : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsGlobalShowNetworkManager(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_Network));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/host_iface_manager_24px.png", ":/host_iface_manager_16px.png",
+ ":/host_iface_manager_disabled_24px.png", ":/host_iface_manager_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("HostNetworkManager");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+H");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Network Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the Network Manager"));
+ }
+};
+
+/** Simple action extension, used as 'Show Cloud Profile Manager' action class. */
+class UIActionToggleManagerToolsGlobalShowCloudProfileManager : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsGlobalShowCloudProfileManager(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_Cloud));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/cloud_profile_manager_24px.png", ":/cloud_profile_manager_16px.png",
+ ":/cloud_profile_manager_disabled_24px.png", ":/cloud_profile_manager_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CloudProfileManager");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+P");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Cloud Profile Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the Cloud Profile Manager"));
+ }
+};
+
+/** Simple action extension, used as 'Show VM Activity Overview' action class. */
+class UIActionToggleManagerToolsGlobalShowVMActivityOverview : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsGlobalShowVMActivityOverview(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_VMActivityOverview));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/resources_monitor_24px.png", ":/resources_monitor_16px.png",
+ ":/resources_monitor_disabled_24px.png", ":/resources_monitor_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToolsGlobalVMActivityOverview");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&VM Activity Overview"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the VM Activity Overview"));
+ }
+};
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+/** Simple action extension, used as 'Show Extra-data Manager' action class. */
+class UIActionSimpleManagerFileShowExtraDataManager : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerFileShowExtraDataManager(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/edata_manager_16px.png", ":/edata_manager_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ExtraDataManager");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+X");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "E&xtra Data Manager..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display the Extra Data Manager window"));
+ }
+};
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+/** Simple action extension, used as 'Perform Exit' action class. */
+class UIActionSimpleManagerFilePerformExit : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerFilePerformExit(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/exit_16px.png", ":/exit_16px.png")
+ {
+ setMenuRole(QAction::QuitRole);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Exit");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Q");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Quit"));
+ setStatusTip(QApplication::translate("UIActionPool", "Close application"));
+ }
+};
+
+
+/** Menu action extension, used as 'Group' menu class. */
+class UIActionMenuManagerGroup : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerGroup(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Group"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Create Machine' action class. */
+class UIActionSimpleManagerGroupPerformCreateMachine : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerGroupPerformCreateMachine(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_new_32px.png", ":/vm_new_16px.png",
+ ":/vm_new_disabled_32px.png", ":/vm_new_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("NewVM");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+N");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ /// @todo replace that one with separate "New" before 6.2
+ setIconText(QApplication::translate("UIActionPool", "&New...").remove('.'));
+ setName(QApplication::translate("UIActionPool", "&New Machine..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Create new virtual machine"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Add Machine' action class. */
+class UIActionSimpleManagerGroupPerformAddMachine : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerGroupPerformAddMachine(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_add_32px.png", ":/vm_add_16px.png",
+ ":/vm_add_disabled_32px.png", ":/vm_add_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("AddVM");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+A");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ /// @todo replace that one with separate "Add" before 6.2
+ setIconText(QApplication::translate("UIActionPool", "&Add...").remove('.'));
+ setName(QApplication::translate("UIActionPool", "&Add Machine..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Add existing virtual machine"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Rename Group' action class. */
+class UIActionSimpleManagerGroupPerformRename : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerGroupPerformRename(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_group_name_16px.png", ":/vm_group_name_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RenameVMGroup");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Rena&me Group..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Rename selected virtual machine group"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Remove Group' action class. */
+class UIActionSimpleManagerGroupPerformRemove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerGroupPerformRemove(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_group_remove_16px.png", ":/vm_group_remove_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("AddVMGroup");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Ungroup"));
+ setStatusTip(QApplication::translate("UIActionPool", "Ungroup items of selected virtual machine group"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Sort Group' action class. */
+class UIActionSimpleManagerGroupPerformSort : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerGroupPerformSort(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/sort_16px.png", ":/sort_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SortGroup");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Sort"));
+ setStatusTip(QApplication::translate("UIActionPool", "Sort items of selected virtual machine group alphabetically"));
+ }
+};
+
+
+/** Menu action extension, used as 'Machine' menu class. */
+class UIActionMenuManagerMachine : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMachine(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Machine"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Create Machine' action class. */
+class UIActionSimpleManagerMachinePerformCreate : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachinePerformCreate(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_new_32px.png", ":/vm_new_16px.png",
+ ":/vm_new_disabled_32px.png", ":/vm_new_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("NewVM");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+N");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&New..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Create new virtual machine"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Add Machine' action class. */
+class UIActionSimpleManagerMachinePerformAdd : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachinePerformAdd(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_add_32px.png", ":/vm_add_16px.png",
+ ":/vm_add_disabled_32px.png", ":/vm_add_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("AddVM");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+A");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Add..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Add existing virtual machine"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Move to Group => New' action class. */
+class UIActionSimpleManagerMachineMoveToGroupNew : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachineMoveToGroupNew(UIActionPool *pParent)
+ : UIActionSimple(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("AddVMGroup");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "[New]", "group"));
+ setStatusTip(QApplication::translate("UIActionPool", "Add new group based on selected virtual machines"));
+ }
+};
+
+/** Simple action extension, used as 'Show Machine Settings' action class. */
+class UIActionSimpleManagerMachineShowSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachineShowSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_settings_32px.png", ":/vm_settings_16px.png",
+ ":/vm_settings_disabled_32px.png", ":/vm_settings_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SettingsVM");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+S");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display the virtual machine settings window"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Clone Machine' action class. */
+class UIActionSimpleManagerMachinePerformClone : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachinePerformClone(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_clone_16px.png", ":/vm_clone_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CloneVM");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+O");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Cl&one..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Clone selected virtual machine"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Move Machine' action class. */
+class UIActionSimpleManagerMachinePerformMove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachinePerformMove(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_move_16px.png", ":/vm_move_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MoveVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Move..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Move selected virtual machine"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Export Machine locally' action class. */
+class UIActionSimpleManagerMachinePerformExportLocally : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachinePerformExportLocally(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/export_16px.png", ":/export_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ExportLocally");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "E&xport Locally..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Export selected virtual machine locally"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Export Machine to OCI' action class. */
+class UIActionSimpleManagerMachinePerformExportToOCI : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachinePerformExportToOCI(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/export_16px.png", ":/export_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ExportToOCI");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "E&xport to OCI..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Export selected virtual machine to OCI"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Remove Machine' action class. */
+class UIActionSimpleManagerMachinePerformRemove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachinePerformRemove(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_delete_32px.png", ":/vm_delete_16px.png",
+ ":/vm_delete_disabled_32px.png", ":/vm_delete_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RemoveVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Remove..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Remove selected virtual machines"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Sort Parent' action class. */
+class UIActionSimpleManagerMachinePerformSortParent : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerMachinePerformSortParent(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/sort_16px.png", ":/sort_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SortGroup");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Sort"));
+ setStatusTip(QApplication::translate("UIActionPool", "Sort group of first selected virtual machine alphabetically"));
+ }
+};
+
+
+/** Menu action extension, used as 'Move to Group' menu class. */
+class UIActionMenuManagerCommonMoveToGroup : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCommonMoveToGroup(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/vm_group_create_16px.png", ":/vm_group_create_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Move to Gro&up"));
+ }
+};
+
+/** Menu action extension, used as 'Start or Show' menu class. */
+class UIActionStateManagerCommonStartOrShow : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionStateManagerCommonStartOrShow(UIActionPool *pParent)
+ : UIActionMenu(pParent,
+ ":/vm_start_32px.png", ":/vm_start_16px.png",
+ ":/vm_start_disabled_32px.png", ":/vm_start_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("StartVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ switch (state())
+ {
+ case 0:
+ {
+ setName(QApplication::translate("UIActionPool", "S&tart"));
+ setStatusTip(QApplication::translate("UIActionPool", "Start selected virtual machines"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ break;
+ }
+ case 1:
+ {
+ setName(QApplication::translate("UIActionPool", "S&how"));
+ setStatusTip(QApplication::translate("UIActionPool", "Switch to the windows of selected virtual machines"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /** Handles state change. */
+ virtual void handleStateChange() RT_OVERRIDE
+ {
+ switch (state())
+ {
+ case 0: showMenu(); break;
+ case 1: hideMenu(); break;
+ default: break;
+ }
+ }
+};
+
+/** Simple action extension, used as 'Perform Normal Start' action class. */
+class UIActionSimpleManagerCommonPerformStartNormal : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonPerformStartNormal(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_start_16px.png", ":/vm_start_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("StartVMNormal");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Normal Start"));
+ setStatusTip(QApplication::translate("UIActionPool", "Start selected virtual machines"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Headless Start' action class. */
+class UIActionSimpleManagerCommonPerformStartHeadless : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonPerformStartHeadless(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_start_headless_16px.png", ":/vm_start_headless_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("StartVMHeadless");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Headless Start"));
+ setStatusTip(QApplication::translate("UIActionPool", "Start selected virtual machines in the background"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Detachable Start' action class. */
+class UIActionSimpleManagerCommonPerformStartDetachable : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonPerformStartDetachable(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_start_separate_16px.png", ":/vm_start_separate_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("StartVMDetachable");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Detachable Start"));
+ setStatusTip(QApplication::translate("UIActionPool", "Start selected virtual machines with option of continuing in background"));
+ }
+};
+
+/** Toggle action extension, used as 'Pause and Resume' action class. */
+class UIActionToggleManagerCommonPauseAndResume : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerCommonPauseAndResume(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/vm_pause_on_16px.png", ":/vm_pause_16px.png",
+ ":/vm_pause_on_disabled_16px.png", ":/vm_pause_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("PauseVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Pause"));
+ setStatusTip(QApplication::translate("UIActionPool", "Suspend execution of selected virtual machines"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Reset' action class. */
+class UIActionSimpleManagerCommonPerformReset : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonPerformReset(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_reset_16px.png", ":/vm_reset_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ResetVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Reset"));
+ setStatusTip(QApplication::translate("UIActionPool", "Reset selected virtual machines"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Detach' action class. */
+class UIActionSimpleManagerCommonPerformDetach : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonPerformDetach(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_create_shortcut_16px.png", ":/vm_create_shortcut_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("DetachUIVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Detach GUI"));
+ setStatusTip(QApplication::translate("UIActionPool", "Detach the GUI from headless VM"));
+ }
+};
+
+/** Simple menu action extension, used as 'Perform Discard' action class. */
+class UIActionSimpleManagerCommonPerformDiscard : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonPerformDiscard(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_discard_32px.png", ":/vm_discard_16px.png",
+ ":/vm_discard_disabled_32px.png", ":/vm_discard_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("DiscardVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Discard"));
+ setName(QApplication::translate("UIActionPool", "D&iscard Saved State..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Discard saved state of selected virtual machines"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Show Machine Logs' action class. */
+class UIActionSimpleManagerCommonShowMachineLogs : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonShowMachineLogs(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_show_logs_32px.png", ":/vm_show_logs_16px.png",
+ ":/vm_show_logs_disabled_32px.png", ":/vm_show_logs_disabled_16px.png")
+ {
+ retranslateUi();
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("LogViewer");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+L");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Show &Log..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Show log files of selected virtual machines"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Refresh' action class. */
+class UIActionSimpleManagerCommonPerformRefresh : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonPerformRefresh(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/refresh_32px.png", ":/refresh_16px.png",
+ ":/refresh_disabled_32px.png", ":/refresh_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RefreshVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Re&fresh"));
+ setStatusTip(QApplication::translate("UIActionPool", "Refresh accessibility state of selected virtual machines"));
+ }
+};
+
+/** Simple action extension, used as 'Show in File Manager' action class. */
+class UIActionSimpleManagerCommonShowInFileManager : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonShowInFileManager(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_open_filemanager_16px.png", ":/vm_open_filemanager_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ShowVMInFileManager");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+#if defined(VBOX_WS_MAC)
+ setName(QApplication::translate("UIActionPool", "S&how in Finder"));
+ setStatusTip(QApplication::translate("UIActionPool", "Show the VirtualBox Machine Definition files in Finder"));
+#elif defined(VBOX_WS_WIN)
+ setName(QApplication::translate("UIActionPool", "S&how in Explorer"));
+ setStatusTip(QApplication::translate("UIActionPool", "Show the VirtualBox Machine Definition files in Explorer"));
+#else
+ setName(QApplication::translate("UIActionPool", "S&how in File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Show the VirtualBox Machine Definition files in the File Manager"));
+#endif
+ }
+};
+
+/** Simple action extension, used as 'Perform Create Shortcut' action class. */
+class UIActionSimpleManagerCommonPerformCreateShortcut : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerCommonPerformCreateShortcut(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_create_shortcut_16px.png", ":/vm_create_shortcut_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CreateVMAlias");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+#if defined(VBOX_WS_MAC)
+ setName(QApplication::translate("UIActionPool", "Cr&eate Alias on Desktop"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create alias files to the VirtualBox Machine Definition files on your desktop"));
+#else
+ setName(QApplication::translate("UIActionPool", "Cr&eate Shortcut on Desktop"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create shortcut files to the VirtualBox Machine Definition files on your desktop"));
+#endif
+ }
+};
+
+/** Toggle action extension, used as 'Search' action class. */
+class UIActionToggleManagerCommonToggleSearch : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerCommonToggleSearch(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/search_16px.png", ":/search_16px.png",
+ ":/search_16px.png", ":/search_16px.png") /// @todo use icons with check-boxes
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SearchVM");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+F");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "S&earch"));
+ setStatusTip(QApplication::translate("UIActionPool", "Search virtual machines with respect to a search term"));
+ }
+};
+
+
+/** Menu action extension, used as 'Console' menu class. */
+class UIActionMenuManagerConsole : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerConsole(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/cloud_machine_console_16px.png")
+ {}
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "C&onsole"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Create Console Connection' action class. */
+class UIActionSimpleManagerConsolePerformCreateConnection : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerConsolePerformCreateConnection(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_machine_console_create_connection_16px.png",
+ ":/cloud_machine_console_create_connection_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CreateConsoleConnection");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Create Connection"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create console connection to be able to use ssh/vnc clients"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Delete Console Connection' action class. */
+class UIActionSimpleManagerConsolePerformDeleteConnection : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerConsolePerformDeleteConnection(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_machine_console_delete_connection_16px.png",
+ ":/cloud_machine_console_delete_connection_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("DeleteConsoleConnection");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Delete Connection"));
+ setStatusTip(QApplication::translate("UIActionPool", "Delete console connection to disconnect ssh/vnc clients"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Configure Applications' action class. */
+class UIActionSimpleManagerConsolePerformConfigureApplications : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerConsolePerformConfigureApplications(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_machine_console_configure_external_terminal_16px.png",
+ ":/cloud_machine_console_configure_external_terminal_disabled_16px.png")
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_CloudConsole));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ConfigureConsoleApplications");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Configure Console Applications"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open configuration dialog to edit console application settings"));
+ }
+};
+
+/** Simple action extension, used as 'Copy Command' action class. */
+class UIActionSimpleManagerConsolePerformCopyCommand : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerConsolePerformCopyCommand(UIActionPool *pParent, bool fSerial, bool fUnix)
+ : UIActionSimple(pParent)
+ , m_fSerial(fSerial)
+ , m_fUnix(fUnix)
+ {
+ if (m_fSerial)
+ setIcon(UIIconPool::iconSet(":/cloud_machine_console_get_serial_console_command_16px.png",
+ ":/cloud_machine_console_get_serial_console_command_disabled_16px.png"));
+ else
+ setIcon(UIIconPool::iconSet(":/cloud_machine_console_get_vnc_console_command_16px.png",
+ ":/cloud_machine_console_get_vnc_console_command_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return m_fSerial
+ ? QString("CopyConsoleCommandSerial")
+ : QString("CopyConsoleCommandVNC");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ if (m_fSerial)
+ {
+ if (m_fUnix)
+ setName(QApplication::translate("UIActionPool", "&Copy Command (serial) for Unix"));
+ else
+ setName(QApplication::translate("UIActionPool", "&Copy Command (serial) for Windows"));
+ setStatusTip(QApplication::translate("UIActionPool", "Copy console command for serial connection"));
+ }
+ else
+ {
+ if (m_fUnix)
+ setName(QApplication::translate("UIActionPool", "&Copy Command (VNC) for Unix"));
+ else
+ setName(QApplication::translate("UIActionPool", "&Copy Command (VNC) for Windows"));
+ setStatusTip(QApplication::translate("UIActionPool", "Copy console command for VNC connection"));
+ }
+ }
+
+private:
+
+ /** Holds whether this command is of serial type. */
+ bool m_fSerial;
+ /** Holds whether this command is for unix. */
+ bool m_fUnix;
+};
+
+/** Simple action extension, used as 'Show Log' action class. */
+class UIActionSimpleManagerConsolePerformShowLog : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerConsolePerformShowLog(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_show_logs_16px.png",
+ ":/vm_show_logs_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ShowConsoleLog");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Show &Log"));
+ setStatusTip(QApplication::translate("UIActionPool", "Show cloud console log"));
+ }
+};
+
+
+/** Menu action extension, used as 'Stop' menu class. */
+class UIActionMenuManagerStop : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerStop(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/exit_16px.png")
+ {}
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Stop"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Save' action class. */
+class UIActionSimpleManagerStopPerformSave : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerStopPerformSave(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_save_state_16px.png", ":/vm_save_state_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SaveVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Save State"));
+ setStatusTip(QApplication::translate("UIActionPool", "Save state of selected virtual machines"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Terminate' action class. */
+class UIActionSimpleManagerStopPerformTerminate : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerStopPerformTerminate(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_discard_16px.png", ":/vm_discard_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TerminateVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Terminate"));
+ setName(QApplication::translate("UIActionPool", "&Terminate Cloud Instance..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Terminate cloud instance of selected virtual machines"));
+ setToolTip(simplifyText(text()) + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Shutdown' action class. */
+class UIActionSimpleManagerStopPerformShutdown : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerStopPerformShutdown(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_shutdown_16px.png", ":/vm_shutdown_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ACPIShutdownVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "ACPI Sh&utdown"));
+ setStatusTip(QApplication::translate("UIActionPool", "Send ACPI Shutdown signal to selected virtual machines"));
+ }
+};
+
+/** Simple action extension, used as 'Perform PowerOff' action class. */
+class UIActionSimpleManagerStopPerformPowerOff : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerStopPerformPowerOff(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_poweroff_16px.png", ":/vm_poweroff_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("PowerOffVM");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Po&wer Off"));
+ setStatusTip(QApplication::translate("UIActionPool", "Power off selected virtual machines"));
+ }
+};
+
+
+/** Menu action extension, used as 'Machine Tools' menu class. */
+class UIActionMenuManagerToolsMachine : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerToolsMachine(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/tools_menu_24px.png") /// @todo replace with 16px icon
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToolsMachineMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Tools"));
+ }
+};
+
+/** Simple action extension, used as 'Show Machine Details' action class. */
+class UIActionToggleManagerToolsMachineShowDetails : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsMachineShowDetails(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_Details));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/machine_details_manager_24px.png", ":/machine_details_manager_16px.png",
+ ":/machine_details_manager_disabled_24px.png", ":/machine_details_manager_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToolsMachineDetails");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Details"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the machine details pane"));
+ }
+};
+
+/** Simple action extension, used as 'Show Machine Snapshots' action class. */
+class UIActionToggleManagerToolsMachineShowSnapshots : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsMachineShowSnapshots(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_Snapshots));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/snapshot_manager_24px.png", ":/snapshot_manager_16px.png",
+ ":/snapshot_manager_disabled_24px.png", ":/snapshot_manager_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToolsMachineSnapshots");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Snapshots"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the machine snapshots pane"));
+ }
+};
+
+/** Simple action extension, used as 'Show Machine Logs' action class. */
+class UIActionToggleManagerToolsMachineShowLogs : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsMachineShowLogs(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_Logs));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/vm_show_logs_32px.png", ":/vm_show_logs_16px.png",
+ ":/vm_show_logs_disabled_32px.png", ":/vm_show_logs_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToolsMachineLogViewer");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Logs"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the machine logs pane"));
+ }
+};
+
+/** Simple action extension, used as 'Show VM Activity Monitor' action class. */
+class UIActionToggleManagerToolsMachineShowActivity : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsMachineShowActivity(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_VMActivity));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/performance_monitor_32px.png", ":/performance_monitor_16px.png",
+ ":/performance_monitor_disabled_32px.png", ":/performance_monitor_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToolsMachineVMActivityMonitor");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Activity"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the machine activity monitor pane"));
+ }
+};
+
+/** Simple action extension, used as 'Show File Manager' action class. */
+class UIActionToggleManagerToolsMachineShowFileManager : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleManagerToolsMachineShowFileManager(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setProperty("UIToolType", QVariant::fromValue(UIToolType_FileManager));
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/file_manager_24px.png", ":/file_manager_16px.png",
+ ":/file_manager_disabled_24px.png", ":/file_manager_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToolsMachineFileManager");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&File Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the File Manager"));
+ }
+};
+
+
+/** Menu action extension, used as 'Snapshot' menu class. */
+class UIActionMenuManagerSnapshot : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerSnapshot(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SnapshotMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Snapshot"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Take' action class. */
+class UIActionMenuManagerSnapshotPerformTake : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerSnapshotPerformTake(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/snapshot_take_32px.png", ":/snapshot_take_16px.png",
+ ":/snapshot_take_disabled_32px.png", ":/snapshot_take_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TakeSnapshot");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+T");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Take..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Snapshot Pane"));
+ setStatusTip(QApplication::translate("UIActionPool", "Take a snapshot of the current virtual machine state"));
+ setToolTip( QApplication::translate("UIActionPool", "Take Snapshot")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Delete' action class. */
+class UIActionMenuManagerSnapshotPerformDelete : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerSnapshotPerformDelete(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/snapshot_delete_32px.png", ":/snapshot_delete_16px.png",
+ ":/snapshot_delete_disabled_32px.png", ":/snapshot_delete_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("DeleteSnapshot");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+D");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Delete..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Snapshot Pane"));
+ setStatusTip(QApplication::translate("UIActionPool", "Delete selected snapshot of the virtual machine"));
+ setToolTip( QApplication::translate("UIActionPool", "Delete Snapshot")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Restore' action class. */
+class UIActionMenuManagerSnapshotPerformRestore : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerSnapshotPerformRestore(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/snapshot_restore_32px.png", ":/snapshot_restore_16px.png",
+ ":/snapshot_restore_disabled_32px.png", ":/snapshot_restore_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RestoreSnapshot");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+R");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Restore..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Snapshot Pane"));
+ setStatusTip(QApplication::translate("UIActionPool", "Restore selected snapshot of the virtual machine"));
+ setToolTip( QApplication::translate("UIActionPool", "Restore Snapshot")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used as 'Toggle Snapshot Properties' action class. */
+class UIActionMenuManagerSnapshotToggleProperties : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerSnapshotToggleProperties(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/snapshot_show_details_32px.png", ":/snapshot_show_details_16px.png",
+ ":/snapshot_show_details_disabled_32px.png", ":/snapshot_show_details_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleSnapshotProperties");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+P");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Properties"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Snapshot Pane"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open pane with the selected snapshot properties"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Snapshot Properties")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Clone' action class. */
+class UIActionMenuManagerSnapshotPerformClone : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerSnapshotPerformClone(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/vm_clone_32px.png", ":/vm_clone_16px.png",
+ ":/vm_clone_disabled_32px.png", ":/vm_clone_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CloneSnapshot");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+C");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Clone..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Snapshot Pane"));
+ setStatusTip(QApplication::translate("UIActionPool", "Clone selected virtual machine"));
+ setToolTip( QApplication::translate("UIActionPool", "Clone Virtual Machine")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+
+/** Menu action extension, used as 'Extension' menu class. */
+class UIActionMenuManagerExtension : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerExtension(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ExtensionMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Extension"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Install' action class. */
+class UIActionSimpleManagerExtensionPerformInstall : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerExtensionPerformInstall(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/extension_pack_install_32px.png", ":/extension_pack_install_16px.png",
+ ":/extension_pack_install_disabled_32px.png", ":/extension_pack_install_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("InstallExtension");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+I");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Install..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Extension Pack Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Install extension pack"));
+ setToolTip( QApplication::translate("UIActionPool", "Install Extension Pack")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Uninstall' action class. */
+class UIActionSimpleManagerExtensionPerformUninstall : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleManagerExtensionPerformUninstall(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/extension_pack_uninstall_32px.png", ":/extension_pack_uninstall_16px.png",
+ ":/extension_pack_uninstall_disabled_32px.png", ":/extension_pack_uninstall_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("UninstallExtension");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+U");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Uninstall..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Extension Pack Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Uninstall selected extension pack"));
+ setToolTip( QApplication::translate("UIActionPool", "Uninstall Extension Pack")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+
+/** Menu action extension, used as 'Medium' menu class. */
+class UIActionMenuManagerMedium : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMedium(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MediumMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Medium"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Add' action class. */
+class UIActionMenuManagerMediumPerformAdd : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumPerformAdd(UIActionPool *pParent)
+ : UIActionSimple(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(0, UIIconPool::iconSetFull(":/hd_add_32px.png", ":/hd_add_16px.png",
+ ":/hd_add_disabled_32px.png", ":/hd_add_disabled_16px.png"));
+ setIcon(1, UIIconPool::iconSetFull(":/cd_add_32px.png", ":/cd_add_16px.png",
+ ":/cd_add_disabled_32px.png", ":/cd_add_disabled_16px.png"));
+ setIcon(2, UIIconPool::iconSetFull(":/fd_add_32px.png", ":/fd_add_16px.png",
+ ":/fd_add_disabled_32px.png", ":/fd_add_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("AddMedium");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+A");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Add..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Add a disk image"));
+ setToolTip( QApplication::translate("UIActionPool", "Add Disk Image")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Create' action class. */
+class UIActionMenuManagerMediumPerformCreate : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumPerformCreate(UIActionPool *pParent)
+ : UIActionSimple(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(0, UIIconPool::iconSetFull(":/hd_create_32px.png", ":/hd_create_16px.png",
+ ":/hd_create_disabled_32px.png", ":/hd_create_disabled_16px.png"));
+ setIcon(1, UIIconPool::iconSetFull(":/cd_create_32px.png", ":/cd_create_16px.png",
+ ":/cd_create_disabled_32px.png", ":/cd_create_disabled_16px.png"));
+ setIcon(2, UIIconPool::iconSetFull(":/fd_create_32px.png", ":/fd_create_16px.png",
+ ":/fd_create_disabled_32px.png", ":/fd_create_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CreateMedium");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Create..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create a new disk image"));
+ setToolTip( QApplication::translate("UIActionPool", "Create Disk Image")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Copy' action class. */
+class UIActionMenuManagerMediumPerformCopy : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumPerformCopy(UIActionPool *pParent)
+ : UIActionSimple(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(0, UIIconPool::iconSetFull(":/hd_copy_32px.png", ":/hd_copy_16px.png",
+ ":/hd_copy_disabled_32px.png", ":/hd_copy_disabled_16px.png"));
+ setIcon(1, UIIconPool::iconSetFull(":/cd_copy_32px.png", ":/cd_copy_16px.png",
+ ":/cd_copy_disabled_32px.png", ":/cd_copy_disabled_16px.png"));
+ setIcon(2, UIIconPool::iconSetFull(":/fd_copy_32px.png", ":/fd_copy_16px.png",
+ ":/fd_copy_disabled_32px.png", ":/fd_copy_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CopyMedium");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+C");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Copy..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Copy selected disk image"));
+ setToolTip( QApplication::translate("UIActionPool", "Copy Disk Image")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Move' action class. */
+class UIActionMenuManagerMediumPerformMove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumPerformMove(UIActionPool *pParent)
+ : UIActionSimple(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(0, UIIconPool::iconSetFull(":/hd_move_32px.png", ":/hd_move_16px.png",
+ ":/hd_move_disabled_32px.png", ":/hd_move_disabled_16px.png"));
+ setIcon(1, UIIconPool::iconSetFull(":/cd_move_32px.png", ":/cd_move_16px.png",
+ ":/cd_move_disabled_32px.png", ":/cd_move_disabled_16px.png"));
+ setIcon(2, UIIconPool::iconSetFull(":/fd_move_32px.png", ":/fd_move_16px.png",
+ ":/fd_move_disabled_32px.png", ":/fd_move_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MoveMedium");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+M");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Move..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Move selected disk image"));
+ setToolTip( QApplication::translate("UIActionPool", "Move Disk Image")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Remove' action class. */
+class UIActionMenuManagerMediumPerformRemove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumPerformRemove(UIActionPool *pParent)
+ : UIActionSimple(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(0, UIIconPool::iconSetFull(":/hd_remove_32px.png", ":/hd_remove_16px.png",
+ ":/hd_remove_disabled_32px.png", ":/hd_remove_disabled_16px.png"));
+ setIcon(1, UIIconPool::iconSetFull(":/cd_remove_32px.png", ":/cd_remove_16px.png",
+ ":/cd_remove_disabled_32px.png", ":/cd_remove_disabled_16px.png"));
+ setIcon(2, UIIconPool::iconSetFull(":/fd_remove_32px.png", ":/fd_remove_16px.png",
+ ":/fd_remove_disabled_32px.png", ":/fd_remove_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RemoveMedium");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+R");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Remove..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Remove selected disk image"));
+ setToolTip( QApplication::translate("UIActionPool", "Remove Disk Image")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Release' action class. */
+class UIActionMenuManagerMediumPerformRelease : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumPerformRelease(UIActionPool *pParent)
+ : UIActionSimple(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(0, UIIconPool::iconSetFull(":/hd_release_32px.png", ":/hd_release_16px.png",
+ ":/hd_release_disabled_32px.png", ":/hd_release_disabled_16px.png"));
+ setIcon(1, UIIconPool::iconSetFull(":/cd_release_32px.png", ":/cd_release_16px.png",
+ ":/cd_release_disabled_32px.png", ":/cd_release_disabled_16px.png"));
+ setIcon(2, UIIconPool::iconSetFull(":/fd_release_32px.png", ":/fd_release_16px.png",
+ ":/fd_release_disabled_32px.png", ":/fd_release_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ReleaseMedium");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+L");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Re&lease..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Release selected disk image"));
+ setToolTip( QApplication::translate("UIActionPool", "Release Disk Image")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used as 'Toggle Medium Properties' action class. */
+class UIActionMenuManagerMediumToggleProperties : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumToggleProperties(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ /// @todo use icons with check-boxes
+ setIcon(0, UIIconPool::iconSetFull(":/hd_modify_32px.png", ":/hd_modify_16px.png",
+ ":/hd_modify_disabled_32px.png", ":/hd_modify_disabled_16px.png"));
+ setIcon(1, UIIconPool::iconSetFull(":/cd_modify_32px.png", ":/cd_modify_16px.png",
+ ":/cd_modify_disabled_32px.png", ":/cd_modify_disabled_16px.png"));
+ setIcon(2, UIIconPool::iconSetFull(":/fd_modify_32px.png", ":/fd_modify_16px.png",
+ ":/fd_modify_disabled_32px.png", ":/fd_modify_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleMediumProperties");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+P");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Properties"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open pane with selected disk image properties"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Disk Image Properties")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used as 'Toggle Search Pane' action class. */
+class UIActionMenuManagerMediumToggleSearch : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumToggleSearch(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ /// @todo use icons with check-boxes
+ setIcon(0, UIIconPool::iconSetFull(":/hd_search_32px.png", ":/hd_search_16px.png",
+ ":/hd_search_disabled_32px.png", ":/hd_search_disabled_16px.png"));
+ setIcon(1, UIIconPool::iconSetFull(":/cd_search_32px.png", ":/cd_search_16px.png",
+ ":/cd_search_disabled_32px.png", ":/cd_search_disabled_16px.png"));
+ setIcon(2, UIIconPool::iconSetFull(":/fd_search_32px.png", ":/fd_search_16px.png",
+ ":/fd_search_disabled_32px.png", ":/fd_search_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleMediumSearch");
+ }
+
+ /** Returns standard shortcut. */
+ virtual QKeySequence standardShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return actionPool()->isTemporary() ? QKeySequence() : QKeySequence(QKeySequence::Find);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Search"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open the disk image search pane"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Disk Image Search Pane")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Refresh' action class. */
+class UIActionMenuManagerMediumPerformRefresh : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumPerformRefresh(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/refresh_32px.png", ":/refresh_16px.png",
+ ":/refresh_disabled_32px.png", ":/refresh_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RefreshMedia");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+F");
+ }
+
+ /** Returns standard shortcut. */
+ virtual QKeySequence standardShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return actionPool()->isTemporary() ? QKeySequence() : QKeySequence(QKeySequence::Refresh);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Re&fresh..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Refresh the list of disk images"));
+ setToolTip( QApplication::translate("UIActionPool", "Refresh Disk Images")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Clear' action class. */
+class UIActionMenuManagerMediumPerformClear : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerMediumPerformClear(UIActionPool *pParent)
+ : UIActionSimple(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ setIcon(1, UIIconPool::iconSetFull(":/cd_clear_32px.png", ":/cd_clear_16px.png",
+ ":/cd_clear_disabled_32px.png", ":/cd_clear_disabled_16px.png"));
+ setIcon(2, UIIconPool::iconSetFull(":/fd_clear_32px.png", ":/fd_clear_16px.png",
+ ":/fd_clear_disabled_32px.png", ":/fd_clear_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Clear");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Clear"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Media Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Remove all inaccessible media"));
+ setToolTip( QApplication::translate("UIActionPool", "Remove Inaccessible Media")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Menu action extension, used as 'Network' menu class. */
+class UIActionMenuManagerNetwork : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerNetwork(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("NetworkMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Network"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Create' action class. */
+class UIActionMenuManagerNetworkPerformCreate : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerNetworkPerformCreate(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/host_iface_add_32px.png", ":/host_iface_add_16px.png",
+ ":/host_iface_add_disabled_32px.png", ":/host_iface_add_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CreateNetwork");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+C");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Create..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Network Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Create new host-only network"));
+ setToolTip( QApplication::translate("UIActionPool", "Create Host-only Network")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Remove' action class. */
+class UIActionMenuManagerNetworkPerformRemove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerNetworkPerformRemove(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/host_iface_remove_32px.png", ":/host_iface_remove_16px.png",
+ ":/host_iface_remove_disabled_32px.png", ":/host_iface_remove_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RemoveNetwork");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+R");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Remove..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Network Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Remove selected host-only network"));
+ setToolTip( QApplication::translate("UIActionPool", "Remove Host-only Network")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used as 'Toggle Network Properties' action class. */
+class UIActionMenuManagerNetworkToggleProperties : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerNetworkToggleProperties(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/host_iface_edit_32px.png", ":/host_iface_edit_16px.png",
+ ":/host_iface_edit_disabled_32px.png", ":/host_iface_edit_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleNetworkProperties");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+P");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Properties"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Network Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open pane with selected host-only network properties"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Host-only Network Properties")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Refresh' action class. */
+class UIActionMenuManagerNetworkPerformRefresh : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerNetworkPerformRefresh(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/refresh_32px.png", ":/refresh_16px.png",
+ ":/refresh_disabled_32px.png", ":/refresh_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RefreshNetworks");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+F");
+ }
+
+ /** Returns standard shortcut. */
+ virtual QKeySequence standardShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return actionPool()->isTemporary() ? QKeySequence() : QKeySequence(QKeySequence::Refresh);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Re&fresh..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Network Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Refresh the list of host-only networks"));
+ setToolTip( QApplication::translate("UIActionPool", "Refresh Host-only Networks")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+
+/** Menu action extension, used as 'Cloud' menu class. */
+class UIActionMenuManagerCloud : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloud(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CloudProfileMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Cloud"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Add' action class. */
+class UIActionMenuManagerCloudPerformAdd : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudPerformAdd(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_profile_add_32px.png", ":/cloud_profile_add_16px.png",
+ ":/cloud_profile_add_disabled_32px.png", ":/cloud_profile_add_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("AddCloudProfile");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+A");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Add"));
+ setName(QApplication::translate("UIActionPool", "&Add Profile..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Profile Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Add new cloud profile"));
+ setToolTip( QApplication::translate("UIActionPool", "Add Cloud Profile")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Import' action class. */
+class UIActionMenuManagerCloudPerformImport : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudPerformImport(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_profile_restore_32px.png", ":/cloud_profile_restore_16px.png",
+ ":/cloud_profile_restore_disabled_32px.png", ":/cloud_profile_restore_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ImportCloudProfiles");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+I");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Import"));
+ setName(QApplication::translate("UIActionPool", "&Import Profiles..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Profile Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Import the list of cloud profiles from external files"));
+ setToolTip( QApplication::translate("UIActionPool", "Import Cloud Profiles")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Remove' action class. */
+class UIActionMenuManagerCloudPerformRemove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudPerformRemove(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_profile_remove_32px.png", ":/cloud_profile_remove_16px.png",
+ ":/cloud_profile_remove_disabled_32px.png", ":/cloud_profile_remove_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RemoveCloudProfile");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+R");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Remove"));
+ setName(QApplication::translate("UIActionPool", "&Remove Profile..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Profile Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Remove selected cloud profile"));
+ setToolTip( QApplication::translate("UIActionPool", "Remove Cloud Profile")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used as 'Toggle Properties' action class. */
+class UIActionMenuManagerCloudToggleProperties : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudToggleProperties(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/cloud_profile_edit_32px.png", ":/cloud_profile_edit_16px.png",
+ ":/cloud_profile_edit_disabled_32px.png", ":/cloud_profile_edit_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleCloudProfileProperties");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+P");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Properties"));
+ setName(QApplication::translate("UIActionPool", "Profile &Properties"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Profile Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open pane with selected cloud profile properties"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Cloud Profile Properties")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Try Page' action class. */
+class UIActionMenuManagerCloudShowTryPage : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudShowTryPage(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_profile_try_32px.png", ":/cloud_profile_try_16px.png",
+ ":/cloud_profile_try_disabled_32px.png", ":/cloud_profile_try_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ShowCloudProfileTryPage");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+T");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Try"));
+ setName(QApplication::translate("UIActionPool", "&Try Oracle Cloud for Free..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Profile Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Try Oracle cloud for free"));
+ setToolTip( QApplication::translate("UIActionPool", "Try Oracle Cloud for Free")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Show Help' action class. */
+class UIActionMenuManagerCloudShowHelp : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudShowHelp(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_profile_help_32px.png", ":/cloud_profile_help_16px.png",
+ ":/cloud_profile_help_disabled_32px.png", ":/cloud_profile_help_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ShowCloudProfileHelp");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+H");
+ }
+
+ /** Returns standard shortcut. */
+ virtual QKeySequence standardShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return actionPool()->isTemporary() ? QKeySequence() : QKeySequence(QKeySequence::HelpContents);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Help"));
+ setName(QApplication::translate("UIActionPool", "&Show Help..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Profile Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Show cloud profile help"));
+ setToolTip( QApplication::translate("UIActionPool", "Show Cloud Profile Help")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+
+/** Menu action extension, used as 'Cloud Console' menu class. */
+class UIActionMenuManagerCloudConsole : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudConsole(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CloudConsoleMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Console"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Console Application Add' action class. */
+class UIActionMenuManagerCloudConsolePerformApplicationAdd : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudConsolePerformApplicationAdd(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_console_application_add_32px.png", ":/cloud_console_application_add_16px.png",
+ ":/cloud_console_application_add_disabled_32px.png", ":/cloud_console_application_add_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("AddCloudConsoleApplication");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Add Application..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Console Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Add new cloud console application"));
+ setToolTip( QApplication::translate("UIActionPool", "Add Cloud Console Application")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Console Application Remove' action class. */
+class UIActionMenuManagerCloudConsolePerformApplicationRemove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudConsolePerformApplicationRemove(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_console_application_remove_32px.png", ":/cloud_console_application_remove_16px.png",
+ ":/cloud_console_application_remove_disabled_32px.png", ":/cloud_console_application_remove_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RemoveCloudConsoleApplication");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Remove Application..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Console Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Remove selected cloud console application"));
+ setToolTip( QApplication::translate("UIActionPool", "Remove Cloud Console Application")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Console Profile Add' action class. */
+class UIActionMenuManagerCloudConsolePerformProfileAdd : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudConsolePerformProfileAdd(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_console_profile_add_32px.png", ":/cloud_console_profile_add_16px.png",
+ ":/cloud_console_profile_add_disabled_32px.png", ":/cloud_console_profile_add_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("AddCloudConsoleProfile");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Add Profile..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Console Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Add new cloud console profile"));
+ setToolTip( QApplication::translate("UIActionPool", "Add Cloud Console Profile")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Perform Console Profile Remove' action class. */
+class UIActionMenuManagerCloudConsolePerformProfileRemove : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudConsolePerformProfileRemove(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/cloud_console_profile_remove_32px.png", ":/cloud_console_profile_remove_16px.png",
+ ":/cloud_console_profile_remove_disabled_32px.png", ":/cloud_console_profile_remove_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RemoveCloudConsoleProfile");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Remove Profile..."));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Console Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Remove selected cloud console profile"));
+ setToolTip( QApplication::translate("UIActionPool", "Remove Cloud Console Profile")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Toggle action extension, used as 'Toggle Cloud Console Properties' action class. */
+class UIActionMenuManagerCloudConsoleToggleProperties : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerCloudConsoleToggleProperties(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ /// @todo use icons with check-boxes
+ setIcon(UIIconPool::iconSetFull(":/cloud_console_edit_32px.png", ":/cloud_console_edit_16px.png",
+ ":/cloud_console_edit_disabled_32px.png", ":/cloud_console_edit_disabled_16px.png"));
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleCloudConsoleProperties");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Ctrl+Shift+P");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setIconText(QApplication::translate("UIActionPool", "Properties"));
+ setName(QApplication::translate("UIActionPool", "Console &Properties"));
+ setShortcutScope(QApplication::translate("UIActionPool", "Cloud Console Manager"));
+ setStatusTip(QApplication::translate("UIActionPool", "Open pane with selected cloud console properties"));
+ setToolTip( QApplication::translate("UIActionPool", "Open Cloud Console Properties")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+
+/** Menu action extension, used as 'Resources' menu class. */
+class UIActionMenuVMActivityOverview : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuVMActivityOverview(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VMActivityOverviewMenu");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Resources"));
+ }
+};
+
+/** Menu action extension, used as 'Columns' menu class. */
+class UIActionMenuManagerVMActivityOverviewColumns : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerVMActivityOverviewColumns(UIActionPool *pParent)
+ : UIActionMenu(pParent,
+ ":/resources_monitor_columns_32px.png", ":/resources_monitor_columns_16px.png",
+ ":/resources_monitor_columns_disabled_32px.png", ":/resources_monitor_columns_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VMActivityOverviewColumns");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Columns"));
+ setShortcutScope(QApplication::translate("UIActionPool", "VM Activity Overview"));
+ setStatusTip(QApplication::translate("UIActionPool", "Show/Hide Columns"));
+ setToolTip( QApplication::translate("UIActionPool", "Show/Hide Columns")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+/** Simple action extension, used as 'Switch to Machine Activity' action class. */
+class UIActionMenuManagerVMActivityOverviewSwitchToMachineActivity : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuManagerVMActivityOverviewSwitchToMachineActivity(UIActionPool *pParent)
+ : UIActionSimple(pParent,
+ ":/resources_monitor_jump_to_vm_32px.png", ":/resources_monitor_jump_to_vm_16px.png",
+ ":/resources_monitor_jump_to_vm_disabled_32px.png", ":/resources_monitor_jump_to_vm_disabled_16px.png")
+ {
+ setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ }
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VMActivityOverviewSwitchToMachineActivity");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "VM Activity"));
+ setShortcutScope(QApplication::translate("UIActionPool", "VM Activity Overview"));
+ setStatusTip(QApplication::translate("UIActionPool", "Switch to selected virtual machine's activity monitor pane"));
+ setToolTip( QApplication::translate("UIActionPool", "Switch to selected virtual machine's activity monitor pane")
+ + (shortcut().isEmpty() ? QString() : QString(" (%1)").arg(shortcut().toString())));
+ }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIActionPoolManager implementation. *
+*********************************************************************************************************************************/
+
+UIActionPoolManager::UIActionPoolManager(bool fTemporary /* = false */)
+ : UIActionPool(UIActionPoolType_Manager, fTemporary)
+{
+}
+
+void UIActionPoolManager::preparePool()
+{
+ /* 'File' actions: */
+ m_pool[UIActionIndexMN_M_File] = new UIActionMenuManagerFile(this);
+ m_pool[UIActionIndexMN_M_File_S_ImportAppliance] = new UIActionSimpleManagerFileShowImportApplianceWizard(this);
+ m_pool[UIActionIndexMN_M_File_S_ExportAppliance] = new UIActionSimpleManagerFileShowExportApplianceWizard(this);
+ m_pool[UIActionIndexMN_M_File_M_Tools] = new UIActionMenuManagerToolsGlobal(this);
+ m_pool[UIActionIndexMN_M_File_M_Tools_T_WelcomeScreen] = new UIActionToggleManagerToolsGlobalShowWelcomeScreen(this);
+ m_pool[UIActionIndexMN_M_File_M_Tools_T_ExtensionPackManager] = new UIActionToggleManagerToolsGlobalShowExtensionPackManager(this);
+ m_pool[UIActionIndexMN_M_File_M_Tools_T_VirtualMediaManager] = new UIActionToggleManagerToolsGlobalShowVirtualMediaManager(this);
+ m_pool[UIActionIndexMN_M_File_M_Tools_T_NetworkManager] = new UIActionToggleManagerToolsGlobalShowNetworkManager(this);
+ m_pool[UIActionIndexMN_M_File_M_Tools_T_CloudProfileManager] = new UIActionToggleManagerToolsGlobalShowCloudProfileManager(this);
+ m_pool[UIActionIndexMN_M_File_M_Tools_T_VMActivityOverview] = new UIActionToggleManagerToolsGlobalShowVMActivityOverview(this);
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ m_pool[UIActionIndexMN_M_File_S_ShowExtraDataManager] = new UIActionSimpleManagerFileShowExtraDataManager(this);
+#endif
+ m_pool[UIActionIndexMN_M_File_S_Close] = new UIActionSimpleManagerFilePerformExit(this);
+
+ /* 'Welcome' actions: */
+ m_pool[UIActionIndexMN_M_Welcome] = new UIActionMenuManagerMachine(this);
+ m_pool[UIActionIndexMN_M_Welcome_S_New] = new UIActionSimpleManagerMachinePerformCreate(this);
+ m_pool[UIActionIndexMN_M_Welcome_S_Add] = new UIActionSimpleManagerMachinePerformAdd(this);
+
+ /* 'Group' actions: */
+ m_pool[UIActionIndexMN_M_Group] = new UIActionMenuManagerGroup(this);
+ m_pool[UIActionIndexMN_M_Group_S_New] = new UIActionSimpleManagerGroupPerformCreateMachine(this);
+ m_pool[UIActionIndexMN_M_Group_S_Add] = new UIActionSimpleManagerGroupPerformAddMachine(this);
+ m_pool[UIActionIndexMN_M_Group_S_Rename] = new UIActionSimpleManagerGroupPerformRename(this);
+ m_pool[UIActionIndexMN_M_Group_S_Remove] = new UIActionSimpleManagerGroupPerformRemove(this);
+ m_pool[UIActionIndexMN_M_Group_M_MoveToGroup] = new UIActionMenuManagerCommonMoveToGroup(this);
+ m_pool[UIActionIndexMN_M_Group_M_StartOrShow] = new UIActionStateManagerCommonStartOrShow(this);
+ m_pool[UIActionIndexMN_M_Group_M_StartOrShow_S_StartNormal] = new UIActionSimpleManagerCommonPerformStartNormal(this);
+ m_pool[UIActionIndexMN_M_Group_M_StartOrShow_S_StartHeadless] = new UIActionSimpleManagerCommonPerformStartHeadless(this);
+ m_pool[UIActionIndexMN_M_Group_M_StartOrShow_S_StartDetachable] = new UIActionSimpleManagerCommonPerformStartDetachable(this);
+ m_pool[UIActionIndexMN_M_Group_T_Pause] = new UIActionToggleManagerCommonPauseAndResume(this);
+ m_pool[UIActionIndexMN_M_Group_S_Reset] = new UIActionSimpleManagerCommonPerformReset(this);
+ m_pool[UIActionIndexMN_M_Group_S_Detach] = new UIActionSimpleManagerCommonPerformDetach(this);
+ m_pool[UIActionIndexMN_M_Group_M_Console] = new UIActionMenuManagerConsole(this);
+ m_pool[UIActionIndexMN_M_Group_M_Console_S_CreateConnection] = new UIActionSimpleManagerConsolePerformCreateConnection(this);
+ m_pool[UIActionIndexMN_M_Group_M_Console_S_DeleteConnection] = new UIActionSimpleManagerConsolePerformDeleteConnection(this);
+ m_pool[UIActionIndexMN_M_Group_M_Console_S_ConfigureApplications] = new UIActionSimpleManagerConsolePerformConfigureApplications(this);
+ m_pool[UIActionIndexMN_M_Group_M_Stop] = new UIActionMenuManagerStop(this);
+ m_pool[UIActionIndexMN_M_Group_M_Stop_S_SaveState] = new UIActionSimpleManagerStopPerformSave(this);
+ m_pool[UIActionIndexMN_M_Group_M_Stop_S_Terminate] = new UIActionSimpleManagerStopPerformTerminate(this);
+ m_pool[UIActionIndexMN_M_Group_M_Stop_S_Shutdown] = new UIActionSimpleManagerStopPerformShutdown(this);
+ m_pool[UIActionIndexMN_M_Group_M_Stop_S_PowerOff] = new UIActionSimpleManagerStopPerformPowerOff(this);
+ m_pool[UIActionIndexMN_M_Group_M_Tools] = new UIActionMenuManagerToolsMachine(this);
+ m_pool[UIActionIndexMN_M_Group_M_Tools_T_Details] = new UIActionToggleManagerToolsMachineShowDetails(this);
+ m_pool[UIActionIndexMN_M_Group_M_Tools_T_Snapshots] = new UIActionToggleManagerToolsMachineShowSnapshots(this);
+ m_pool[UIActionIndexMN_M_Group_M_Tools_T_Logs] = new UIActionToggleManagerToolsMachineShowLogs(this);
+ m_pool[UIActionIndexMN_M_Group_M_Tools_T_Activity] = new UIActionToggleManagerToolsMachineShowActivity(this);
+ m_pool[UIActionIndexMN_M_Group_M_Tools_T_FileManager] = new UIActionToggleManagerToolsMachineShowFileManager(this);
+ m_pool[UIActionIndexMN_M_Group_S_Discard] = new UIActionSimpleManagerCommonPerformDiscard(this);
+ m_pool[UIActionIndexMN_M_Group_S_ShowLogDialog] = new UIActionSimpleManagerCommonShowMachineLogs(this);
+ m_pool[UIActionIndexMN_M_Group_S_ShowLogDialog] = new UIActionSimpleManagerCommonShowMachineLogs(this);
+ m_pool[UIActionIndexMN_M_Group_S_Refresh] = new UIActionSimpleManagerCommonPerformRefresh(this);
+ m_pool[UIActionIndexMN_M_Group_S_ShowInFileManager] = new UIActionSimpleManagerCommonShowInFileManager(this);
+ m_pool[UIActionIndexMN_M_Group_S_CreateShortcut] = new UIActionSimpleManagerCommonPerformCreateShortcut(this);
+ m_pool[UIActionIndexMN_M_Group_S_Sort] = new UIActionSimpleManagerGroupPerformSort(this);
+ m_pool[UIActionIndexMN_M_Group_T_Search] = new UIActionToggleManagerCommonToggleSearch(this);
+
+ /* 'Machine' actions: */
+ m_pool[UIActionIndexMN_M_Machine] = new UIActionMenuManagerMachine(this);
+ m_pool[UIActionIndexMN_M_Machine_S_New] = new UIActionSimpleManagerMachinePerformCreate(this);
+ m_pool[UIActionIndexMN_M_Machine_S_Add] = new UIActionSimpleManagerMachinePerformAdd(this);
+ m_pool[UIActionIndexMN_M_Machine_S_Settings] = new UIActionSimpleManagerMachineShowSettings(this);
+ m_pool[UIActionIndexMN_M_Machine_S_Clone] = new UIActionSimpleManagerMachinePerformClone(this);
+ m_pool[UIActionIndexMN_M_Machine_S_Move] = new UIActionSimpleManagerMachinePerformMove(this);
+ m_pool[UIActionIndexMN_M_Machine_S_ExportToOCI] = new UIActionSimpleManagerMachinePerformExportToOCI(this);
+ m_pool[UIActionIndexMN_M_Machine_S_Remove] = new UIActionSimpleManagerMachinePerformRemove(this);
+ m_pool[UIActionIndexMN_M_Machine_M_MoveToGroup] = new UIActionMenuManagerCommonMoveToGroup(this);
+ m_pool[UIActionIndexMN_M_Machine_M_MoveToGroup_S_New] = new UIActionSimpleManagerMachineMoveToGroupNew(this);
+ m_pool[UIActionIndexMN_M_Machine_M_StartOrShow] = new UIActionStateManagerCommonStartOrShow(this);
+ m_pool[UIActionIndexMN_M_Machine_M_StartOrShow_S_StartNormal] = new UIActionSimpleManagerCommonPerformStartNormal(this);
+ m_pool[UIActionIndexMN_M_Machine_M_StartOrShow_S_StartHeadless] = new UIActionSimpleManagerCommonPerformStartHeadless(this);
+ m_pool[UIActionIndexMN_M_Machine_M_StartOrShow_S_StartDetachable] = new UIActionSimpleManagerCommonPerformStartDetachable(this);
+ m_pool[UIActionIndexMN_M_Machine_T_Pause] = new UIActionToggleManagerCommonPauseAndResume(this);
+ m_pool[UIActionIndexMN_M_Machine_S_Reset] = new UIActionSimpleManagerCommonPerformReset(this);
+ m_pool[UIActionIndexMN_M_Machine_S_Detach] = new UIActionSimpleManagerCommonPerformDetach(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Console] = new UIActionMenuManagerConsole(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Console_S_CreateConnection] = new UIActionSimpleManagerConsolePerformCreateConnection(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Console_S_DeleteConnection] = new UIActionSimpleManagerConsolePerformDeleteConnection(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialUnix] = new UIActionSimpleManagerConsolePerformCopyCommand(this, true, true);
+ m_pool[UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialWindows] = new UIActionSimpleManagerConsolePerformCopyCommand(this, true, false);
+ m_pool[UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCUnix] = new UIActionSimpleManagerConsolePerformCopyCommand(this, false, true);
+ m_pool[UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCWindows] = new UIActionSimpleManagerConsolePerformCopyCommand(this, false, false);
+ m_pool[UIActionIndexMN_M_Machine_M_Console_S_ConfigureApplications] = new UIActionSimpleManagerConsolePerformConfigureApplications(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Console_S_ShowLog] = new UIActionSimpleManagerConsolePerformShowLog(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Stop] = new UIActionMenuManagerStop(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Stop_S_SaveState] = new UIActionSimpleManagerStopPerformSave(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Stop_S_Terminate] = new UIActionSimpleManagerStopPerformTerminate(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Stop_S_Shutdown] = new UIActionSimpleManagerStopPerformShutdown(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Stop_S_PowerOff] = new UIActionSimpleManagerStopPerformPowerOff(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Tools] = new UIActionMenuManagerToolsMachine(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Tools_T_Details] = new UIActionToggleManagerToolsMachineShowDetails(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Tools_T_Snapshots] = new UIActionToggleManagerToolsMachineShowSnapshots(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Tools_T_Logs] = new UIActionToggleManagerToolsMachineShowLogs(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Tools_T_Activity] = new UIActionToggleManagerToolsMachineShowActivity(this);
+ m_pool[UIActionIndexMN_M_Machine_M_Tools_T_FileManager] = new UIActionToggleManagerToolsMachineShowFileManager(this);
+ m_pool[UIActionIndexMN_M_Machine_S_Discard] = new UIActionSimpleManagerCommonPerformDiscard(this);
+ m_pool[UIActionIndexMN_M_Machine_S_ShowLogDialog] = new UIActionSimpleManagerCommonShowMachineLogs(this);
+ m_pool[UIActionIndexMN_M_Machine_S_Refresh] = new UIActionSimpleManagerCommonPerformRefresh(this);
+ m_pool[UIActionIndexMN_M_Machine_S_ShowInFileManager] = new UIActionSimpleManagerCommonShowInFileManager(this);
+ m_pool[UIActionIndexMN_M_Machine_S_CreateShortcut] = new UIActionSimpleManagerCommonPerformCreateShortcut(this);
+ m_pool[UIActionIndexMN_M_Machine_S_SortParent] = new UIActionSimpleManagerMachinePerformSortParent(this);
+ m_pool[UIActionIndexMN_M_Machine_T_Search] = new UIActionToggleManagerCommonToggleSearch(this);
+
+ /* Snapshot Pane actions: */
+ m_pool[UIActionIndexMN_M_Snapshot] = new UIActionMenuManagerSnapshot(this);
+ m_pool[UIActionIndexMN_M_Snapshot_S_Take] = new UIActionMenuManagerSnapshotPerformTake(this);
+ m_pool[UIActionIndexMN_M_Snapshot_S_Delete] = new UIActionMenuManagerSnapshotPerformDelete(this);
+ m_pool[UIActionIndexMN_M_Snapshot_S_Restore] = new UIActionMenuManagerSnapshotPerformRestore(this);
+ m_pool[UIActionIndexMN_M_Snapshot_T_Properties] = new UIActionMenuManagerSnapshotToggleProperties(this);
+ m_pool[UIActionIndexMN_M_Snapshot_S_Clone] = new UIActionMenuManagerSnapshotPerformClone(this);
+
+ /* Extension Pack Manager actions: */
+ m_pool[UIActionIndexMN_M_ExtensionWindow] = new UIActionMenuManagerExtension(this);
+ m_pool[UIActionIndexMN_M_Extension] = new UIActionMenuManagerExtension(this);
+ m_pool[UIActionIndexMN_M_Extension_S_Install] = new UIActionSimpleManagerExtensionPerformInstall(this);
+ m_pool[UIActionIndexMN_M_Extension_S_Uninstall] = new UIActionSimpleManagerExtensionPerformUninstall(this);
+
+ /* Virtual Medium Manager actions: */
+ m_pool[UIActionIndexMN_M_MediumWindow] = new UIActionMenuManagerMedium(this);
+ m_pool[UIActionIndexMN_M_Medium] = new UIActionMenuManagerMedium(this);
+ m_pool[UIActionIndexMN_M_Medium_S_Add] = new UIActionMenuManagerMediumPerformAdd(this);
+ m_pool[UIActionIndexMN_M_Medium_S_Create] = new UIActionMenuManagerMediumPerformCreate(this);
+ m_pool[UIActionIndexMN_M_Medium_S_Copy] = new UIActionMenuManagerMediumPerformCopy(this);
+ m_pool[UIActionIndexMN_M_Medium_S_Move] = new UIActionMenuManagerMediumPerformMove(this);
+ m_pool[UIActionIndexMN_M_Medium_S_Remove] = new UIActionMenuManagerMediumPerformRemove(this);
+ m_pool[UIActionIndexMN_M_Medium_S_Release] = new UIActionMenuManagerMediumPerformRelease(this);
+ m_pool[UIActionIndexMN_M_Medium_T_Details] = new UIActionMenuManagerMediumToggleProperties(this);
+ m_pool[UIActionIndexMN_M_Medium_T_Search] = new UIActionMenuManagerMediumToggleSearch(this);
+ m_pool[UIActionIndexMN_M_Medium_S_Refresh] = new UIActionMenuManagerMediumPerformRefresh(this);
+ m_pool[UIActionIndexMN_M_Medium_S_Clear] = new UIActionMenuManagerMediumPerformClear(this);
+
+ /* Network Manager actions: */
+ m_pool[UIActionIndexMN_M_NetworkWindow] = new UIActionMenuManagerNetwork(this);
+ m_pool[UIActionIndexMN_M_Network] = new UIActionMenuManagerNetwork(this);
+ m_pool[UIActionIndexMN_M_Network_S_Create] = new UIActionMenuManagerNetworkPerformCreate(this);
+ m_pool[UIActionIndexMN_M_Network_S_Remove] = new UIActionMenuManagerNetworkPerformRemove(this);
+ m_pool[UIActionIndexMN_M_Network_T_Details] = new UIActionMenuManagerNetworkToggleProperties(this);
+ m_pool[UIActionIndexMN_M_Network_S_Refresh] = new UIActionMenuManagerNetworkPerformRefresh(this);
+
+ /* Cloud Profile Manager actions: */
+ m_pool[UIActionIndexMN_M_CloudWindow] = new UIActionMenuManagerCloud(this);
+ m_pool[UIActionIndexMN_M_Cloud] = new UIActionMenuManagerCloud(this);
+ m_pool[UIActionIndexMN_M_Cloud_S_Add] = new UIActionMenuManagerCloudPerformAdd(this);
+ m_pool[UIActionIndexMN_M_Cloud_S_Import] = new UIActionMenuManagerCloudPerformImport(this);
+ m_pool[UIActionIndexMN_M_Cloud_S_Remove] = new UIActionMenuManagerCloudPerformRemove(this);
+ m_pool[UIActionIndexMN_M_Cloud_T_Details] = new UIActionMenuManagerCloudToggleProperties(this);
+ m_pool[UIActionIndexMN_M_Cloud_S_TryPage] = new UIActionMenuManagerCloudShowTryPage(this);
+ m_pool[UIActionIndexMN_M_Cloud_S_Help] = new UIActionMenuManagerCloudShowHelp(this);
+
+ /* Cloud Console Manager actions: */
+ m_pool[UIActionIndexMN_M_CloudConsoleWindow] = new UIActionMenuManagerCloudConsole(this);
+ m_pool[UIActionIndexMN_M_CloudConsole] = new UIActionMenuManagerCloudConsole(this);
+ m_pool[UIActionIndexMN_M_CloudConsole_S_ApplicationAdd] = new UIActionMenuManagerCloudConsolePerformApplicationAdd(this);
+ m_pool[UIActionIndexMN_M_CloudConsole_S_ApplicationRemove] = new UIActionMenuManagerCloudConsolePerformApplicationRemove(this);
+ m_pool[UIActionIndexMN_M_CloudConsole_S_ProfileAdd] = new UIActionMenuManagerCloudConsolePerformProfileAdd(this);
+ m_pool[UIActionIndexMN_M_CloudConsole_S_ProfileRemove] = new UIActionMenuManagerCloudConsolePerformProfileRemove(this);
+ m_pool[UIActionIndexMN_M_CloudConsole_T_Details] = new UIActionMenuManagerCloudConsoleToggleProperties(this);
+
+ /* VM Activity Overview actions: */
+ m_pool[UIActionIndexMN_M_VMActivityOverview] = new UIActionMenuVMActivityOverview(this);
+ m_pool[UIActionIndexMN_M_VMActivityOverview_M_Columns] = new UIActionMenuManagerVMActivityOverviewColumns(this);
+ m_pool[UIActionIndexMN_M_VMActivityOverview_S_SwitchToMachineActivity] = new UIActionMenuManagerVMActivityOverviewSwitchToMachineActivity(this);
+
+ /* 'File' action groups: */
+ m_groupPool[UIActionIndexMN_M_File_M_Tools] = new QActionGroup(m_pool.value(UIActionIndexMN_M_File_M_Tools));
+ m_groupPool[UIActionIndexMN_M_File_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_File_M_Tools_T_WelcomeScreen));
+ m_groupPool[UIActionIndexMN_M_File_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_File_M_Tools_T_ExtensionPackManager));
+ m_groupPool[UIActionIndexMN_M_File_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_File_M_Tools_T_VirtualMediaManager));
+ m_groupPool[UIActionIndexMN_M_File_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_File_M_Tools_T_NetworkManager));
+ m_groupPool[UIActionIndexMN_M_File_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_File_M_Tools_T_CloudProfileManager));
+ m_groupPool[UIActionIndexMN_M_File_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_File_M_Tools_T_VMActivityOverview));
+
+ /* 'Group' action groups: */
+ m_groupPool[UIActionIndexMN_M_Group_M_Tools] = new QActionGroup(m_pool.value(UIActionIndexMN_M_Group_M_Tools));
+ m_groupPool[UIActionIndexMN_M_Group_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Group_M_Tools_T_Details));
+ m_groupPool[UIActionIndexMN_M_Group_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Group_M_Tools_T_Snapshots));
+ m_groupPool[UIActionIndexMN_M_Group_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Group_M_Tools_T_Logs));
+ m_groupPool[UIActionIndexMN_M_Group_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Group_M_Tools_T_Activity));
+ m_groupPool[UIActionIndexMN_M_Group_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Group_M_Tools_T_FileManager));
+
+ /* 'Machine' action groups: */
+ m_groupPool[UIActionIndexMN_M_Machine_M_Tools] = new QActionGroup(m_pool.value(UIActionIndexMN_M_Machine_M_Tools));
+ m_groupPool[UIActionIndexMN_M_Machine_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Machine_M_Tools_T_Details));
+ m_groupPool[UIActionIndexMN_M_Machine_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Machine_M_Tools_T_Snapshots));
+ m_groupPool[UIActionIndexMN_M_Machine_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Machine_M_Tools_T_Logs));
+ m_groupPool[UIActionIndexMN_M_Machine_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Machine_M_Tools_T_Activity));
+ m_groupPool[UIActionIndexMN_M_Machine_M_Tools]->addAction(m_pool.value(UIActionIndexMN_M_Machine_M_Tools_T_FileManager));
+
+ /* Prepare update-handlers for known menus: */
+ m_menuUpdateHandlers[UIActionIndexMN_M_File].ptfm = &UIActionPoolManager::updateMenuFile;
+ m_menuUpdateHandlers[UIActionIndexMN_M_File_M_Tools].ptfm = &UIActionPoolManager::updateMenuFileTools;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Welcome].ptfm = &UIActionPoolManager::updateMenuWelcome;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group].ptfm = &UIActionPoolManager::updateMenuGroup;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine].ptfm = &UIActionPoolManager::updateMenuMachine;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group_M_MoveToGroup].ptfm = &UIActionPoolManager::updateMenuGroupMoveToGroup;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine_M_MoveToGroup].ptfm = &UIActionPoolManager::updateMenuMachineMoveToGroup;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group_M_StartOrShow].ptfm = &UIActionPoolManager::updateMenuGroupStartOrShow;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine_M_StartOrShow].ptfm = &UIActionPoolManager::updateMenuMachineStartOrShow;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group_M_Console].ptfm = &UIActionPoolManager::updateMenuGroupConsole;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine_M_Console].ptfm = &UIActionPoolManager::updateMenuMachineConsole;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group_M_Stop].ptfm = &UIActionPoolManager::updateMenuGroupClose;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine_M_Stop].ptfm = &UIActionPoolManager::updateMenuMachineClose;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group_M_Tools].ptfm = &UIActionPoolManager::updateMenuGroupTools;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine_M_Tools].ptfm = &UIActionPoolManager::updateMenuMachineTools;
+ m_menuUpdateHandlers[UIActionIndexMN_M_ExtensionWindow].ptfm = &UIActionPoolManager::updateMenuExtensionWindow;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Extension].ptfm = &UIActionPoolManager::updateMenuExtension;
+ m_menuUpdateHandlers[UIActionIndexMN_M_MediumWindow].ptfm = &UIActionPoolManager::updateMenuMediumWindow;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Medium].ptfm = &UIActionPoolManager::updateMenuMedium;
+ m_menuUpdateHandlers[UIActionIndexMN_M_NetworkWindow].ptfm = &UIActionPoolManager::updateMenuNetworkWindow;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Network].ptfm = &UIActionPoolManager::updateMenuNetwork;
+ m_menuUpdateHandlers[UIActionIndexMN_M_CloudWindow].ptfm = &UIActionPoolManager::updateMenuCloudWindow;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Cloud].ptfm = &UIActionPoolManager::updateMenuCloud;
+ m_menuUpdateHandlers[UIActionIndexMN_M_CloudConsoleWindow].ptfm = &UIActionPoolManager::updateMenuCloudConsoleWindow;
+ m_menuUpdateHandlers[UIActionIndexMN_M_CloudConsole].ptfm = &UIActionPoolManager::updateMenuCloudConsole;
+ m_menuUpdateHandlers[UIActionIndexMN_M_VMActivityOverview].ptfm = &UIActionPoolManager::updateMenuVMActivityOverview;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Snapshot].ptfm = &UIActionPoolManager::updateMenuSnapshot;
+
+ /* Call to base-class: */
+ UIActionPool::preparePool();
+}
+
+void UIActionPoolManager::prepareConnections()
+{
+ /* Prepare connections: */
+ connect(gShortcutPool, &UIShortcutPool::sigManagerShortcutsReloaded, this, &UIActionPoolManager::sltApplyShortcuts);
+ connect(gShortcutPool, &UIShortcutPool::sigRuntimeShortcutsReloaded, this, &UIActionPoolManager::sltApplyShortcuts);
+
+ /* Call to base-class: */
+ UIActionPool::prepareConnections();
+}
+
+void UIActionPoolManager::updateMenu(int iIndex)
+{
+ /* If index belongs to base-class => delegate to base-class: */
+ if (iIndex < UIActionIndex_Max)
+ UIActionPool::updateMenu(iIndex);
+ /* Otherwise,
+ * if menu with such index is invalidated
+ * and there is update-handler => handle it here: */
+ else if ( iIndex > UIActionIndex_Max
+ && m_invalidations.contains(iIndex)
+ && m_menuUpdateHandlers.contains(iIndex))
+ (this->*(m_menuUpdateHandlers.value(iIndex).ptfm))();
+}
+
+void UIActionPoolManager::updateMenus()
+{
+ /* Clear menu list: */
+ m_mainMenus.clear();
+
+ /* 'File' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_File));
+ updateMenuFile();
+
+ /* 'File' / 'Tools' menu: */
+ updateMenuFileTools();
+
+ /* 'Welcome' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_Welcome));
+ updateMenuWelcome();
+ /* 'Group' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_Group));
+ updateMenuGroup();
+ /* 'Machine' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_Machine));
+ updateMenuMachine();
+
+ /* 'Machine' / 'Move to Group' menu: */
+ updateMenuMachineMoveToGroup();
+ /* 'Group' / 'Start or Show' menu: */
+ updateMenuGroupStartOrShow();
+ /* 'Machine' / 'Start or Show' menu: */
+ updateMenuMachineStartOrShow();
+ /* 'Group' / 'Close' menu: */
+ updateMenuGroupClose();
+ /* 'Machine' / 'Close' menu: */
+ updateMenuMachineClose();
+ /* 'Group' / 'Tools' menu: */
+ updateMenuGroupTools();
+ /* 'Machine' / 'Tools' menu: */
+ updateMenuMachineTools();
+
+ /* 'Extension Pack Manager' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_Extension));
+ updateMenuExtensionWindow();
+ updateMenuExtension();
+ /* 'Virtual Media Manager' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_Medium));
+ updateMenuMediumWindow();
+ updateMenuMedium();
+ /* 'Network Manager' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_Network));
+ updateMenuNetworkWindow();
+ updateMenuNetwork();
+ /* 'Cloud Profile Manager' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_Cloud));
+ updateMenuCloudWindow();
+ updateMenuCloud();
+ /* 'VM Activity Overview' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_VMActivityOverview));
+ updateMenuVMActivityOverview();
+
+ /* 'Snapshot' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexMN_M_Snapshot));
+ updateMenuSnapshot();
+ /* 'Log' menu: */
+ addMenu(m_mainMenus, action(UIActionIndex_M_Log));
+ updateMenuLogViewerWindow();
+ updateMenuLogViewer();
+ /* 'Activity' menu: */
+ addMenu(m_mainMenus, action(UIActionIndex_M_Activity));
+ updateMenuVMActivityMonitor();
+
+ /* 'File Manager' menu*/
+ addMenu(m_mainMenus, action(UIActionIndex_M_FileManager));
+ updateMenuFileManager();
+
+ /* 'Help' menu: */
+ addMenu(m_mainMenus, action(UIActionIndex_Menu_Help));
+ updateMenuHelp();
+}
+
+void UIActionPoolManager::setShortcutsVisible(int iIndex, bool fVisible)
+{
+ /* Prepare a list of actions: */
+ QList<UIAction*> actions;
+
+ /* Handle known menus: */
+ switch (iIndex)
+ {
+ case UIActionIndexMN_M_Welcome:
+ {
+ actions << action(UIActionIndexMN_M_Welcome_S_New)
+ << action(UIActionIndexMN_M_Welcome_S_Add);
+ break;
+ }
+ case UIActionIndexMN_M_Group:
+ {
+ actions << action(UIActionIndexMN_M_Group_S_New)
+ << action(UIActionIndexMN_M_Group_S_Add)
+ << action(UIActionIndexMN_M_Group_S_Rename)
+ << action(UIActionIndexMN_M_Group_S_Remove)
+ << action(UIActionIndexMN_M_Group_M_MoveToGroup)
+ << action(UIActionIndexMN_M_Group_M_StartOrShow)
+ << action(UIActionIndexMN_M_Group_T_Pause)
+ << action(UIActionIndexMN_M_Group_S_Reset)
+ // << action(UIActionIndexMN_M_Group_S_Detach)
+ << action(UIActionIndexMN_M_Group_S_Discard)
+ << action(UIActionIndexMN_M_Group_S_ShowLogDialog)
+ << action(UIActionIndexMN_M_Group_S_Refresh)
+ << action(UIActionIndexMN_M_Group_S_ShowInFileManager)
+ << action(UIActionIndexMN_M_Group_S_CreateShortcut)
+ << action(UIActionIndexMN_M_Group_S_Sort)
+ << action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartNormal)
+ << action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartHeadless)
+ << action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartDetachable)
+ << action(UIActionIndexMN_M_Group_M_Console_S_CreateConnection)
+ << action(UIActionIndexMN_M_Group_M_Console_S_DeleteConnection)
+ << action(UIActionIndexMN_M_Group_M_Console_S_ConfigureApplications)
+ << action(UIActionIndexMN_M_Group_M_Stop_S_SaveState)
+ << action(UIActionIndexMN_M_Group_M_Stop_S_Terminate)
+ << action(UIActionIndexMN_M_Group_M_Stop_S_Shutdown)
+ << action(UIActionIndexMN_M_Group_M_Stop_S_PowerOff)
+ << action(UIActionIndexMN_M_Group_M_Tools_T_Details)
+ << action(UIActionIndexMN_M_Group_M_Tools_T_Snapshots)
+ << action(UIActionIndexMN_M_Group_M_Tools_T_Logs)
+ << action(UIActionIndexMN_M_Group_M_Tools_T_Activity);
+ break;
+ }
+ case UIActionIndexMN_M_Machine:
+ {
+ actions << action(UIActionIndexMN_M_Machine_S_New)
+ << action(UIActionIndexMN_M_Machine_S_Add)
+ << action(UIActionIndexMN_M_Machine_S_Settings)
+ << action(UIActionIndexMN_M_Machine_S_Clone)
+ << action(UIActionIndexMN_M_Machine_S_Move)
+ << action(UIActionIndexMN_M_Machine_S_ExportToOCI)
+ << action(UIActionIndexMN_M_Machine_S_Remove)
+ << action(UIActionIndexMN_M_Machine_M_MoveToGroup)
+ << action(UIActionIndexMN_M_Machine_M_StartOrShow)
+ << action(UIActionIndexMN_M_Machine_T_Pause)
+ << action(UIActionIndexMN_M_Machine_S_Reset)
+ // << action(UIActionIndexMN_M_Machine_S_Detach)
+ << action(UIActionIndexMN_M_Machine_S_Discard)
+ << action(UIActionIndexMN_M_Machine_S_ShowLogDialog)
+ << action(UIActionIndexMN_M_Machine_S_Refresh)
+ << action(UIActionIndexMN_M_Machine_S_ShowInFileManager)
+ << action(UIActionIndexMN_M_Machine_S_CreateShortcut)
+ << action(UIActionIndexMN_M_Machine_S_SortParent)
+ << action(UIActionIndexMN_M_Machine_M_MoveToGroup_S_New)
+ << action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartNormal)
+ << action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartHeadless)
+ << action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartDetachable)
+ << action(UIActionIndexMN_M_Machine_M_Console_S_CreateConnection)
+ << action(UIActionIndexMN_M_Machine_M_Console_S_DeleteConnection)
+ << action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialUnix)
+ << action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialWindows)
+ << action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCUnix)
+ << action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCWindows)
+ << action(UIActionIndexMN_M_Machine_M_Console_S_ConfigureApplications)
+ << action(UIActionIndexMN_M_Machine_M_Console_S_ShowLog)
+ << action(UIActionIndexMN_M_Machine_M_Stop_S_SaveState)
+ << action(UIActionIndexMN_M_Machine_M_Stop_S_Terminate)
+ << action(UIActionIndexMN_M_Machine_M_Stop_S_Shutdown)
+ << action(UIActionIndexMN_M_Machine_M_Stop_S_PowerOff)
+ << action(UIActionIndexMN_M_Machine_M_Tools_T_Details)
+ << action(UIActionIndexMN_M_Machine_M_Tools_T_Snapshots)
+ << action(UIActionIndexMN_M_Machine_M_Tools_T_Logs)
+ << action(UIActionIndexMN_M_Machine_M_Tools_T_Activity);
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Update shortcut visibility: */
+ foreach (UIAction *pAction, actions)
+ fVisible ? pAction->showShortcut() : pAction->hideShortcut();
+}
+
+QString UIActionPoolManager::shortcutsExtraDataID() const
+{
+ return GUI_Input_SelectorShortcuts;
+}
+
+void UIActionPoolManager::updateShortcuts()
+{
+ /* Call to base-class: */
+ UIActionPool::updateShortcuts();
+ /* Create temporary Runtime UI pool to do the same: */
+ if (!isTemporary())
+ UIActionPool::createTemporary(UIActionPoolType_Runtime);
+}
+
+void UIActionPoolManager::updateMenuFile()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_File)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* The Application / 'File' menu contents is very different depending on host type. */
+
+#ifdef VBOX_WS_MAC
+
+ /* 'About' action goes to Application menu: */
+ pMenu->addAction(action(UIActionIndex_M_Application_S_About));
+# ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* 'Check for Updates' action goes to Application menu: */
+ if (gEDataManager->applicationUpdateEnabled())
+ pMenu->addAction(action(UIActionIndex_M_Application_S_CheckForUpdates));
+# endif
+ /* 'Reset Warnings' action goes to Application menu: */
+ pMenu->addAction(action(UIActionIndex_M_Application_S_ResetWarnings));
+ /* 'Preferences' action goes to Application menu: */
+ pMenu->addAction(action(UIActionIndex_M_Application_S_Preferences));
+ /* 'Close' action goes to Application menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_File_S_Close));
+
+ /* 'Import Appliance' action goes to 'File' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_File_S_ImportAppliance));
+ /* 'Export Appliance' action goes to 'File' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_File_S_ExportAppliance));
+# ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ /* 'Show Extra-data Manager' action goes to 'File' menu for Debug build: */
+ pMenu->addAction(action(UIActionIndexMN_M_File_S_ShowExtraDataManager));
+# endif
+ /* Separator after Import/Export actions of the 'File' menu: */
+ pMenu->addSeparator();
+ /* 'Tools' submenu goes to 'File' menu: */
+ pMenu->addMenu(action(UIActionIndexMN_M_File_M_Tools)->menu());
+#else /* !VBOX_WS_MAC */
+
+ /* 'Preferences' action goes to 'File' menu: */
+ pMenu->addAction(action(UIActionIndex_M_Application_S_Preferences));
+ /* Separator after 'Preferences' action of the 'File' menu: */
+ pMenu->addSeparator();
+ /* 'Import Appliance' action goes to 'File' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_File_S_ImportAppliance));
+ /* 'Export Appliance' action goes to 'File' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_File_S_ExportAppliance));
+ /* Separator after 'Export Appliance' action of the 'File' menu: */
+ pMenu->addSeparator();
+# ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ /* 'Extra-data Manager' action goes to 'File' menu for Debug build: */
+ pMenu->addAction(action(UIActionIndexMN_M_File_S_ShowExtraDataManager));
+ /* Separator after 'Extra-data Manager' action of the 'File' menu: */
+ pMenu->addSeparator();
+# endif
+ /* 'Tools' submenu goes to 'File' menu: */
+ pMenu->addMenu(action(UIActionIndexMN_M_File_M_Tools)->menu());
+ /* Separator after 'Tools' submenu of the 'File' menu: */
+ pMenu->addSeparator();
+# ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* 'Check for Updates' action goes to 'File' menu: */
+ if (gEDataManager->applicationUpdateEnabled())
+ pMenu->addAction(action(UIActionIndex_M_Application_S_CheckForUpdates));
+# endif
+ /* 'Reset Warnings' action goes 'File' menu: */
+ pMenu->addAction(action(UIActionIndex_M_Application_S_ResetWarnings));
+ /* Separator after 'Reset Warnings' action of the 'File' menu: */
+ pMenu->addSeparator();
+ /* 'Close' action goes to 'File' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_File_S_Close));
+
+#endif /* !VBOX_WS_MAC */
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_File);
+}
+
+void UIActionPoolManager::updateMenuFileTools()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_File_M_Tools)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Populate 'File' / 'Tools' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_File_M_Tools_T_ExtensionPackManager));
+ pMenu->addAction(action(UIActionIndexMN_M_File_M_Tools_T_VirtualMediaManager));
+ pMenu->addAction(action(UIActionIndexMN_M_File_M_Tools_T_NetworkManager));
+ pMenu->addAction(action(UIActionIndexMN_M_File_M_Tools_T_CloudProfileManager));
+ pMenu->addAction(action(UIActionIndexMN_M_File_M_Tools_T_VMActivityOverview));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_File_M_Tools);
+}
+
+void UIActionPoolManager::updateMenuWelcome()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Welcome)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Populate 'Welcome' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_Welcome_S_New));
+ pMenu->addAction(action(UIActionIndexMN_M_Welcome_S_Add));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Welcome);
+}
+
+void UIActionPoolManager::updateMenuGroup()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Group)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // On macOS you can't leave menu empty and still have it in
+ // the menu-bar, you have to leave there at least something.
+ // Remaining stuff will be appended from UIVirtualBoxManager.
+ pMenu->addAction(action(UIActionIndexMN_M_Group_S_New));
+#endif
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolManager::updateMenuMachine()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Machine)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // On macOS you can't leave menu empty and still have it in
+ // the menu-bar, you have to leave there at least something.
+ // Remaining stuff will be appended from UIVirtualBoxManager.
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_S_New));
+#endif
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolManager::updateMenuGroupMoveToGroup()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Group_M_MoveToGroup)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolManager::updateMenuMachineMoveToGroup()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Machine_M_MoveToGroup)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Populate 'Machine' / 'Move to Group' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_MoveToGroup_S_New));
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolManager::updateMenuGroupStartOrShow()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Group_M_StartOrShow)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Populate 'Group' / 'Start or Show' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartNormal));
+ pMenu->addAction(action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartHeadless));
+ pMenu->addAction(action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartDetachable));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Group_M_StartOrShow);
+}
+
+void UIActionPoolManager::updateMenuMachineStartOrShow()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Machine_M_StartOrShow)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Populate 'Machine' / 'Start or Show' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartNormal));
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartHeadless));
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartDetachable));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Machine_M_StartOrShow);
+}
+
+void UIActionPoolManager::updateMenuGroupConsole()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Group_M_Console)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolManager::updateMenuMachineConsole()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Machine_M_Console)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolManager::updateMenuGroupClose()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Group_M_Stop)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // On macOS you can't leave menu empty and still have it in
+ // the menu-bar, you have to leave there at least something.
+ // Remaining stuff will be appended from UIVirtualBoxManager.
+ pMenu->addAction(action(UIActionIndexMN_M_Group_M_Stop_S_PowerOff));
+#endif
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolManager::updateMenuMachineClose()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Machine_M_Stop)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // On macOS you can't leave menu empty and still have it in
+ // the menu-bar, you have to leave there at least something.
+ // Remaining stuff will be appended from UIVirtualBoxManager.
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_Stop_S_PowerOff));
+#endif
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolManager::updateMenuGroupTools()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Group_M_Tools)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Populate 'Group' / 'Tools' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_Group_M_Tools_T_Details));
+ pMenu->addAction(action(UIActionIndexMN_M_Group_M_Tools_T_Snapshots));
+ pMenu->addAction(action(UIActionIndexMN_M_Group_M_Tools_T_Logs));
+ pMenu->addAction(action(UIActionIndexMN_M_Group_M_Tools_T_Activity));
+ pMenu->addAction(action(UIActionIndexMN_M_Group_M_Tools_T_FileManager));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Group_M_Tools);
+}
+
+void UIActionPoolManager::updateMenuMachineTools()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Machine_M_Tools)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Populate 'Machine' / 'Tools' menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_Tools_T_Details));
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_Tools_T_Snapshots));
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_Tools_T_Logs));
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_Tools_T_Activity));
+ pMenu->addAction(action(UIActionIndexMN_M_Machine_M_Tools_T_FileManager));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Machine_M_Tools);
+}
+
+void UIActionPoolManager::updateMenuExtensionWindow()
+{
+ /* Update corresponding menu: */
+ updateMenuExtensionWrapper(action(UIActionIndexMN_M_ExtensionWindow)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_ExtensionWindow);
+}
+
+void UIActionPoolManager::updateMenuExtension()
+{
+ /* Update corresponding menu: */
+ updateMenuExtensionWrapper(action(UIActionIndexMN_M_Extension)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Extension);
+}
+
+void UIActionPoolManager::updateMenuExtensionWrapper(UIMenu *pMenu)
+{
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* 'Add' action: */
+ addAction(pMenu, action(UIActionIndexMN_M_Extension_S_Install));
+ /* 'Remove' action: */
+ addAction(pMenu, action(UIActionIndexMN_M_Extension_S_Uninstall));
+}
+
+void UIActionPoolManager::updateMenuMediumWindow()
+{
+ /* Update corresponding menu: */
+ updateMenuMediumWrapper(action(UIActionIndexMN_M_MediumWindow)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_MediumWindow);
+}
+
+void UIActionPoolManager::updateMenuMedium()
+{
+ /* Update corresponding menu: */
+ updateMenuMediumWrapper(action(UIActionIndexMN_M_Medium)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Medium);
+}
+
+void UIActionPoolManager::updateMenuMediumWrapper(UIMenu *pMenu)
+{
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator? */
+ bool fSeparator = false;
+
+ /* 'Add' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_S_Add)) || fSeparator;
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_S_Create)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Copy' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_S_Copy)) || fSeparator;
+ /* 'Move' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_S_Move)) || fSeparator;
+ /* 'Remove' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_S_Remove)) || fSeparator;
+ /* 'Release' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_S_Release)) || fSeparator;
+ /* 'Clear' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_S_Clear)) || fSeparator;
+ /* 'Search' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_T_Search)) || fSeparator;
+ /* 'Properties' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_T_Details)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Refresh' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Medium_S_Refresh)) || fSeparator;;
+}
+
+void UIActionPoolManager::updateMenuNetworkWindow()
+{
+ /* Update corresponding menu: */
+ updateMenuNetworkWrapper(action(UIActionIndexMN_M_NetworkWindow)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_NetworkWindow);
+}
+
+void UIActionPoolManager::updateMenuNetwork()
+{
+ /* Update corresponding menu: */
+ updateMenuNetworkWrapper(action(UIActionIndexMN_M_Network)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Network);
+}
+
+void UIActionPoolManager::updateMenuNetworkWrapper(UIMenu *pMenu)
+{
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator? */
+ bool fSeparator = false;
+
+ /* 'Create' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Network_S_Create)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Remove' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Network_S_Remove)) || fSeparator;
+ /* 'Properties' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Network_T_Details)) || fSeparator;
+
+// /* Separator? */
+// if (fSeparator)
+// {
+// pMenu->addSeparator();
+// fSeparator = false;
+// }
+
+// /* 'Refresh' action: */
+// fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Network_S_Refresh)) || fSeparator;;
+}
+
+void UIActionPoolManager::updateMenuCloudWindow()
+{
+ /* Update corresponding menu: */
+ updateMenuCloudWrapper(action(UIActionIndexMN_M_CloudWindow)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_CloudWindow);
+}
+
+void UIActionPoolManager::updateMenuCloud()
+{
+ /* Update corresponding menu: */
+ updateMenuCloudWrapper(action(UIActionIndexMN_M_Cloud)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Cloud);
+}
+
+void UIActionPoolManager::updateMenuCloudWrapper(UIMenu *pMenu)
+{
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator? */
+ bool fSeparator = false;
+
+ /* 'Add' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Cloud_S_Add)) || fSeparator;
+ /* 'Import' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Cloud_S_Import)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Remove' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Cloud_S_Remove)) || fSeparator;
+ /* 'Properties' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Cloud_T_Details)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Try Page' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Cloud_S_TryPage)) || fSeparator;
+ /* 'Help' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_Cloud_S_Help)) || fSeparator;
+}
+
+void UIActionPoolManager::updateMenuCloudConsoleWindow()
+{
+ /* Update corresponding menu: */
+ updateMenuCloudConsoleWrapper(action(UIActionIndexMN_M_CloudConsoleWindow)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_CloudConsoleWindow);
+}
+
+void UIActionPoolManager::updateMenuCloudConsole()
+{
+ /* Update corresponding menu: */
+ updateMenuCloudConsoleWrapper(action(UIActionIndexMN_M_CloudConsole)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_CloudConsole);
+}
+
+void UIActionPoolManager::updateMenuCloudConsoleWrapper(UIMenu *pMenu)
+{
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator? */
+ bool fSeparator = false;
+
+ /* 'Add Application' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_CloudConsole_S_ApplicationAdd)) || fSeparator;
+ /* 'Remove Application' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_CloudConsole_S_ApplicationRemove)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Add Profile' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_CloudConsole_S_ProfileAdd)) || fSeparator;
+ /* 'Remove Profile' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_CloudConsole_S_ProfileRemove)) || fSeparator;
+
+ /* Separator? */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Properties' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexMN_M_CloudConsole_T_Details)) || fSeparator;
+}
+
+void UIActionPoolManager::updateMenuVMActivityOverview()
+{
+ /* Update corresponding menu: */
+ updateMenuVMActivityOverviewWrapper(action(UIActionIndexMN_M_VMActivityOverview)->menu());
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_VMActivityOverview);
+}
+
+void UIActionPoolManager::updateMenuVMActivityOverviewWrapper(UIMenu *pMenu)
+{
+ /* Clear contents: */
+ pMenu->clear();
+ addAction(pMenu, action(UIActionIndexMN_M_VMActivityOverview_M_Columns));
+ addAction(pMenu, action(UIActionIndexMN_M_VMActivityOverview_S_SwitchToMachineActivity));
+}
+
+void UIActionPoolManager::updateMenuSnapshot()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexMN_M_Snapshot)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Populate Snapshot-menu: */
+ pMenu->addAction(action(UIActionIndexMN_M_Snapshot_S_Take));
+ pMenu->addAction(action(UIActionIndexMN_M_Snapshot_S_Delete));
+ pMenu->addAction(action(UIActionIndexMN_M_Snapshot_S_Restore));
+ pMenu->addAction(action(UIActionIndexMN_M_Snapshot_T_Properties));
+ pMenu->addAction(action(UIActionIndexMN_M_Snapshot_S_Clone));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexMN_M_Snapshot);
+}
+
+
+#include "UIActionPoolManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolManager.h b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolManager.h
new file mode 100644
index 00000000..f1ddd581
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolManager.h
@@ -0,0 +1,327 @@
+/* $Id: UIActionPoolManager.h $ */
+/** @file
+ * VBox Qt GUI - UIActionPoolManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIActionPoolManager_h
+#define FEQT_INCLUDED_SRC_globals_UIActionPoolManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIActionPool.h"
+#include "UILibraryDefs.h"
+
+
+/** VirtualBox Manager action-pool index enum.
+ * Naming convention is following:
+ * 1. Every menu index prepended with 'M',
+ * 2. Every simple-action index prepended with 'S',
+ * 3. Every toggle-action index presended with 'T',
+ * 5. Every sub-index contains full parent-index name. */
+enum UIActionIndexMN
+{
+ /* 'File' menu actions: */
+ UIActionIndexMN_M_File = UIActionIndex_Max + 1,
+ UIActionIndexMN_M_File_S_ImportAppliance,
+ UIActionIndexMN_M_File_S_ExportAppliance,
+ UIActionIndexMN_M_File_M_Tools,
+ UIActionIndexMN_M_File_M_Tools_T_WelcomeScreen,
+ UIActionIndexMN_M_File_M_Tools_T_ExtensionPackManager,
+ UIActionIndexMN_M_File_M_Tools_T_VirtualMediaManager,
+ UIActionIndexMN_M_File_M_Tools_T_NetworkManager,
+ UIActionIndexMN_M_File_M_Tools_T_CloudProfileManager,
+ UIActionIndexMN_M_File_M_Tools_T_VMActivityOverview,
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ UIActionIndexMN_M_File_S_ShowExtraDataManager,
+#endif
+ UIActionIndexMN_M_File_S_Close,
+
+ /* 'Welcome' menu actions: */
+ UIActionIndexMN_M_Welcome,
+ UIActionIndexMN_M_Welcome_S_New,
+ UIActionIndexMN_M_Welcome_S_Add,
+
+ /* 'Group' menu actions: */
+ UIActionIndexMN_M_Group,
+ UIActionIndexMN_M_Group_S_New,
+ UIActionIndexMN_M_Group_S_Add,
+ UIActionIndexMN_M_Group_S_Rename,
+ UIActionIndexMN_M_Group_S_Remove,
+ UIActionIndexMN_M_Group_M_MoveToGroup,
+ UIActionIndexMN_M_Group_M_StartOrShow,
+ UIActionIndexMN_M_Group_M_StartOrShow_S_StartNormal,
+ UIActionIndexMN_M_Group_M_StartOrShow_S_StartHeadless,
+ UIActionIndexMN_M_Group_M_StartOrShow_S_StartDetachable,
+ UIActionIndexMN_M_Group_T_Pause,
+ UIActionIndexMN_M_Group_S_Reset,
+ UIActionIndexMN_M_Group_S_Detach,
+ UIActionIndexMN_M_Group_M_Console,
+ UIActionIndexMN_M_Group_M_Console_S_CreateConnection,
+ UIActionIndexMN_M_Group_M_Console_S_DeleteConnection,
+ UIActionIndexMN_M_Group_M_Console_S_ConfigureApplications,
+ UIActionIndexMN_M_Group_M_Stop,
+ UIActionIndexMN_M_Group_M_Stop_S_SaveState,
+ UIActionIndexMN_M_Group_M_Stop_S_Terminate,
+ UIActionIndexMN_M_Group_M_Stop_S_Shutdown,
+ UIActionIndexMN_M_Group_M_Stop_S_PowerOff,
+ UIActionIndexMN_M_Group_M_Tools,
+ UIActionIndexMN_M_Group_M_Tools_T_Details,
+ UIActionIndexMN_M_Group_M_Tools_T_Snapshots,
+ UIActionIndexMN_M_Group_M_Tools_T_Logs,
+ UIActionIndexMN_M_Group_M_Tools_T_Activity,
+ UIActionIndexMN_M_Group_M_Tools_T_FileManager,
+ UIActionIndexMN_M_Group_S_Discard,
+ UIActionIndexMN_M_Group_S_ShowLogDialog,
+ UIActionIndexMN_M_Group_S_Refresh,
+ UIActionIndexMN_M_Group_S_ShowInFileManager,
+ UIActionIndexMN_M_Group_S_CreateShortcut,
+ UIActionIndexMN_M_Group_S_Sort,
+ UIActionIndexMN_M_Group_T_Search,
+
+ /* 'Machine' menu actions: */
+ UIActionIndexMN_M_Machine,
+ UIActionIndexMN_M_Machine_S_New,
+ UIActionIndexMN_M_Machine_S_Add,
+ UIActionIndexMN_M_Machine_S_Settings,
+ UIActionIndexMN_M_Machine_S_Clone,
+ UIActionIndexMN_M_Machine_S_Move,
+ UIActionIndexMN_M_Machine_S_ExportToOCI,
+ UIActionIndexMN_M_Machine_S_Remove,
+ UIActionIndexMN_M_Machine_M_MoveToGroup,
+ UIActionIndexMN_M_Machine_M_MoveToGroup_S_New,
+ UIActionIndexMN_M_Machine_M_StartOrShow,
+ UIActionIndexMN_M_Machine_M_StartOrShow_S_StartNormal,
+ UIActionIndexMN_M_Machine_M_StartOrShow_S_StartHeadless,
+ UIActionIndexMN_M_Machine_M_StartOrShow_S_StartDetachable,
+ UIActionIndexMN_M_Machine_T_Pause,
+ UIActionIndexMN_M_Machine_S_Reset,
+ UIActionIndexMN_M_Machine_S_Detach,
+ UIActionIndexMN_M_Machine_M_Console,
+ UIActionIndexMN_M_Machine_M_Console_S_CreateConnection,
+ UIActionIndexMN_M_Machine_M_Console_S_DeleteConnection,
+ UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialUnix,
+ UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialWindows,
+ UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCUnix,
+ UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCWindows,
+ UIActionIndexMN_M_Machine_M_Console_S_ConfigureApplications,
+ UIActionIndexMN_M_Machine_M_Console_S_ShowLog,
+ UIActionIndexMN_M_Machine_M_Stop,
+ UIActionIndexMN_M_Machine_M_Stop_S_SaveState,
+ UIActionIndexMN_M_Machine_M_Stop_S_Terminate,
+ UIActionIndexMN_M_Machine_M_Stop_S_Shutdown,
+ UIActionIndexMN_M_Machine_M_Stop_S_PowerOff,
+ UIActionIndexMN_M_Machine_M_Tools,
+ UIActionIndexMN_M_Machine_M_Tools_T_Details,
+ UIActionIndexMN_M_Machine_M_Tools_T_Snapshots,
+ UIActionIndexMN_M_Machine_M_Tools_T_Logs,
+ UIActionIndexMN_M_Machine_M_Tools_T_Activity,
+ UIActionIndexMN_M_Machine_M_Tools_T_FileManager,
+ UIActionIndexMN_M_Machine_S_Discard,
+ UIActionIndexMN_M_Machine_S_ShowLogDialog,
+ UIActionIndexMN_M_Machine_S_Refresh,
+ UIActionIndexMN_M_Machine_S_ShowInFileManager,
+ UIActionIndexMN_M_Machine_S_CreateShortcut,
+ UIActionIndexMN_M_Machine_S_SortParent,
+ UIActionIndexMN_M_Machine_T_Search,
+
+ /* Snapshot Pane actions: */
+ UIActionIndexMN_M_Snapshot,
+ UIActionIndexMN_M_Snapshot_S_Take,
+ UIActionIndexMN_M_Snapshot_S_Delete,
+ UIActionIndexMN_M_Snapshot_S_Restore,
+ UIActionIndexMN_M_Snapshot_T_Properties,
+ UIActionIndexMN_M_Snapshot_S_Clone,
+
+ /* Extension Pack Manager actions: */
+ UIActionIndexMN_M_ExtensionWindow,
+ UIActionIndexMN_M_Extension,
+ UIActionIndexMN_M_Extension_S_Install,
+ UIActionIndexMN_M_Extension_S_Uninstall,
+
+ /* Virtual Media Manager actions: */
+ UIActionIndexMN_M_MediumWindow,
+ UIActionIndexMN_M_Medium,
+ UIActionIndexMN_M_Medium_S_Add,
+ UIActionIndexMN_M_Medium_S_Create,
+ UIActionIndexMN_M_Medium_S_Copy,
+ UIActionIndexMN_M_Medium_S_Move,
+ UIActionIndexMN_M_Medium_S_Remove,
+ UIActionIndexMN_M_Medium_S_Release,
+ UIActionIndexMN_M_Medium_T_Details,
+ UIActionIndexMN_M_Medium_T_Search,
+ UIActionIndexMN_M_Medium_S_Refresh,
+ UIActionIndexMN_M_Medium_S_Clear,
+
+ /* Network Manager actions: */
+ UIActionIndexMN_M_NetworkWindow,
+ UIActionIndexMN_M_Network,
+ UIActionIndexMN_M_Network_S_Create,
+ UIActionIndexMN_M_Network_S_Remove,
+ UIActionIndexMN_M_Network_T_Details,
+ UIActionIndexMN_M_Network_S_Refresh,
+
+ /* Cloud Profile Manager actions: */
+ UIActionIndexMN_M_CloudWindow,
+ UIActionIndexMN_M_Cloud,
+ UIActionIndexMN_M_Cloud_S_Add,
+ UIActionIndexMN_M_Cloud_S_Import,
+ UIActionIndexMN_M_Cloud_S_Remove,
+ UIActionIndexMN_M_Cloud_T_Details,
+ UIActionIndexMN_M_Cloud_S_TryPage,
+ UIActionIndexMN_M_Cloud_S_Help,
+
+ /* Cloud Console Manager actions: */
+ UIActionIndexMN_M_CloudConsoleWindow,
+ UIActionIndexMN_M_CloudConsole,
+ UIActionIndexMN_M_CloudConsole_S_ApplicationAdd,
+ UIActionIndexMN_M_CloudConsole_S_ApplicationRemove,
+ UIActionIndexMN_M_CloudConsole_S_ProfileAdd,
+ UIActionIndexMN_M_CloudConsole_S_ProfileRemove,
+ UIActionIndexMN_M_CloudConsole_T_Details,
+
+ /* VM VM Activity Overview actions: */
+ UIActionIndexMN_M_VMActivityOverview,
+ UIActionIndexMN_M_VMActivityOverview_M_Columns,
+ UIActionIndexMN_M_VMActivityOverview_S_SwitchToMachineActivity,
+
+ /* Maximum index: */
+ UIActionIndexMN_Max
+};
+
+
+/** UIActionPool extension
+ * representing action-pool singleton for Manager UI. */
+class SHARED_LIBRARY_STUFF UIActionPoolManager : public UIActionPool
+{
+ Q_OBJECT;
+
+protected:
+
+ /** Constructs action-pool.
+ * @param fTemporary Brings whether this action-pool is temporary,
+ * used to (re-)initialize shortcuts-pool. */
+ UIActionPoolManager(bool fTemporary = false);
+
+ /** Prepares pool. */
+ virtual void preparePool() RT_OVERRIDE;
+ /** Prepares connections. */
+ virtual void prepareConnections() RT_OVERRIDE;
+
+ /** Updates menu. */
+ virtual void updateMenu(int iIndex) RT_OVERRIDE;
+ /** Updates menus. */
+ virtual void updateMenus() RT_OVERRIDE;
+
+ /** Defines whether shortcuts of menu actions with specified @a iIndex should be visible. */
+ virtual void setShortcutsVisible(int iIndex, bool fVisible) RT_OVERRIDE;
+ /** Returns extra-data ID to save keyboard shortcuts under. */
+ virtual QString shortcutsExtraDataID() const RT_OVERRIDE;
+ /** Updates shortcuts. */
+ virtual void updateShortcuts() RT_OVERRIDE;
+
+private:
+
+ /** Updates 'File' menu. */
+ void updateMenuFile();
+ /** Updates 'File' / 'Tools' menu. */
+ void updateMenuFileTools();
+ /** Updates 'Welcome' menu. */
+ void updateMenuWelcome();
+ /** Updates 'Group' menu. */
+ void updateMenuGroup();
+ /** Updates 'Machine' menu. */
+ void updateMenuMachine();
+ /** Updates 'Group' / 'Move to Group' menu. */
+ void updateMenuGroupMoveToGroup();
+ /** Updates 'Machine' / 'Move to Group' menu. */
+ void updateMenuMachineMoveToGroup();
+ /** Updates 'Group' / 'Start or Show' menu. */
+ void updateMenuGroupStartOrShow();
+ /** Updates 'Machine' / 'Start or Show' menu. */
+ void updateMenuMachineStartOrShow();
+ /** Updates 'Group' / 'Console' menu. */
+ void updateMenuGroupConsole();
+ /** Updates 'Machine' / 'Console' menu. */
+ void updateMenuMachineConsole();
+ /** Updates 'Group' / 'Close' menu. */
+ void updateMenuGroupClose();
+ /** Updates 'Machine' / 'Close' menu. */
+ void updateMenuMachineClose();
+ /** Updates 'Group' / 'Tools' menu. */
+ void updateMenuGroupTools();
+ /** Updates 'Machine' / 'Tools' menu. */
+ void updateMenuMachineTools();
+
+ /** Updates 'Extension Pack' window menu. */
+ void updateMenuExtensionWindow();
+ /** Updates 'Extension Pack' menu. */
+ void updateMenuExtension();
+ /** Updates 'Extension Pack' @a pMenu. */
+ void updateMenuExtensionWrapper(UIMenu *pMenu);
+
+ /** Updates 'Medium' window menu. */
+ void updateMenuMediumWindow();
+ /** Updates 'Medium' menu. */
+ void updateMenuMedium();
+ /** Updates 'Medium' @a pMenu. */
+ void updateMenuMediumWrapper(UIMenu *pMenu);
+
+ /** Updates 'Network' window menu. */
+ void updateMenuNetworkWindow();
+ /** Updates 'Network' menu. */
+ void updateMenuNetwork();
+ /** Updates 'Network' @a pMenu. */
+ void updateMenuNetworkWrapper(UIMenu *pMenu);
+
+ /** Updates 'Cloud' window menu. */
+ void updateMenuCloudWindow();
+ /** Updates 'Cloud' menu. */
+ void updateMenuCloud();
+ /** Updates 'Cloud' @a pMenu. */
+ void updateMenuCloudWrapper(UIMenu *pMenu);
+
+ /** Updates 'Cloud Console' window menu. */
+ void updateMenuCloudConsoleWindow();
+ /** Updates 'Cloud Console' menu. */
+ void updateMenuCloudConsole();
+ /** Updates 'Cloud Console' @a pMenu. */
+ void updateMenuCloudConsoleWrapper(UIMenu *pMenu);
+
+ /** Updates 'VM VM Activity Overview' menu. */
+ void updateMenuVMActivityOverview();
+ /** Updates 'VM VM Activity Overview' @a pMenu. */
+ void updateMenuVMActivityOverviewWrapper(UIMenu *pMenu);
+
+ /** Updates 'Snapshot' menu. */
+ void updateMenuSnapshot();
+
+ /** Enables factory in base-class. */
+ friend class UIActionPool;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIActionPoolManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolRuntime.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolRuntime.cpp
new file mode 100644
index 00000000..02e3f44c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolRuntime.cpp
@@ -0,0 +1,4451 @@
+/* $Id: UIActionPoolRuntime.cpp $ */
+/** @file
+ * VBox Qt GUI - UIActionPoolRuntime class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QActionGroup>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIActionPoolRuntime.h"
+#include "UIConverter.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIShortcutPool.h"
+
+/* COM includes: */
+#include "CExtPack.h"
+#include "CExtPackManager.h"
+
+/* External includes: */
+#include <math.h>
+
+
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+
+
+/** Menu action extension, used as 'Machine' menu class. */
+class UIActionMenuRuntimeMachine : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeMachine(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuType_Machine;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuType_Machine);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType_Machine);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Machine"));
+ }
+};
+
+/** Simple action extension, used as 'Show Settings' action class. */
+class UIActionSimpleRuntimeShowSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_settings_16px.png", ":/vm_settings_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SettingsDialog;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SettingsDialog);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SettingsDialog);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SettingsDialog");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("S");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display the virtual machine settings window"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Take Snapshot' action class. */
+class UIActionSimpleRuntimePerformTakeSnapshot : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformTakeSnapshot(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/snapshot_take_16px.png", ":/snapshot_take_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_TakeSnapshot;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_TakeSnapshot);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_TakeSnapshot);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TakeSnapshot");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("T");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Take Sn&apshot..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Take a snapshot of the virtual machine"));
+ }
+};
+
+/** Simple action extension, used as 'Show Information Dialog' action class. */
+class UIActionSimpleRuntimeShowInformationDialog : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowInformationDialog(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/session_info_16px.png", ":/session_info_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_InformationDialog;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_InformationDialog);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_InformationDialog);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("InformationDialog");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("N");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Session I&nformation..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display the virtual machine session information window"));
+ }
+};
+
+/** Simple action extension, used as 'Show File Manager Dialog' action class. */
+class UIActionSimpleRuntimeShowFileManagerDialog : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowFileManagerDialog(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/file_manager_16px.png", ":/file_manager_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_FileManagerDialog;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_FileManagerDialog);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_FileManagerDialog);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FileManagerDialog");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence();
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "File Manager..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display the virtual machine file manager window"));
+ }
+};
+
+/** Toggle action extension, used as 'Pause' action class. */
+class UIActionToggleRuntimePause : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimePause(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/vm_pause_on_16px.png", ":/vm_pause_16px.png",
+ ":/vm_pause_on_disabled_16px.png", ":/vm_pause_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Pause;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Pause);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Pause);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Pause");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("P");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Pause"));
+ setStatusTip(QApplication::translate("UIActionPool", "Suspend the execution of the virtual machine"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Reset' action class. */
+class UIActionSimpleRuntimePerformReset : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformReset(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_reset_16px.png", ":/vm_reset_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Reset;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Reset);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Reset);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Reset");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("R");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Reset"));
+ setStatusTip(QApplication::translate("UIActionPool", "Reset the virtual machine"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Detach' action class. */
+class UIActionSimpleRuntimePerformDetach : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformDetach(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_create_shortcut_16px.png", ":/vm_create_shortcut_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Detach;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Detach);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Detach);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("DetachUI");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Detach GUI"));
+ setStatusTip(QApplication::translate("UIActionPool", "Detach the GUI from headless VM"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Save State' action class. */
+class UIActionSimpleRuntimePerformSaveState : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformSaveState(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_save_state_16px.png", ":/vm_save_state_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SaveState;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SaveState);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SaveState);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SaveState");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Save State"));
+ setStatusTip(QApplication::translate("UIActionPool", "Save the state of the virtual machine"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Shutdown' action class. */
+class UIActionSimpleRuntimePerformShutdown : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformShutdown(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_shutdown_16px.png", ":/vm_shutdown_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Shutdown;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Shutdown);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Shutdown);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Shutdown");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+#ifdef VBOX_WS_MAC
+ return QKeySequence("U");
+#else
+ return QKeySequence("H");
+#endif
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "ACPI Sh&utdown"));
+ setStatusTip(QApplication::translate("UIActionPool", "Send the ACPI Shutdown signal to the virtual machine"));
+ }
+};
+
+/** Simple action extension, used as 'Perform PowerOff' action class. */
+class UIActionSimpleRuntimePerformPowerOff : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformPowerOff(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_poweroff_16px.png", ":/vm_poweroff_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_PowerOff;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_PowerOff);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_PowerOff);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("PowerOff");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Po&wer Off"));
+ setStatusTip(QApplication::translate("UIActionPool", "Power off the virtual machine"));
+ }
+};
+
+/** Simple action extension, used as 'Show Logs' action class. */
+class UIActionSimpleRuntimeShowLogs : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowLogs(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/vm_show_logs_16px.png", ":/vm_show_logs_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuMachineActionType_LogDialog;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_LogDialog);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_LogDialog);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("LogWindow");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Show &Log..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display the log viewer window"));
+ }
+};
+
+/** Menu action extension, used as 'View' menu class. */
+class UIActionMenuRuntimeView : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeView(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuType_View;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuType_View);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType_View);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&View"));
+ }
+};
+
+/** Menu action extension, used as 'View Popup' menu class. */
+class UIActionMenuRuntimeViewPopup : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeViewPopup(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuType_View;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuType_View);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType_View);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE {}
+};
+
+/** Toggle action extension, used as 'Full-screen Mode' action class. */
+class UIActionToggleRuntimeFullscreenMode : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeFullscreenMode(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/fullscreen_on_16px.png", ":/fullscreen_16px.png",
+ ":/fullscreen_on_disabled_16px.png", ":/fullscreen_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Fullscreen;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Fullscreen);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Fullscreen);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("FullscreenMode");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("F");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Full-screen Mode"));
+ setStatusTip(QApplication::translate("UIActionPool", "Switch between normal and full-screen mode"));
+ }
+};
+
+/** Toggle action extension, used as 'Seamless Mode' action class. */
+class UIActionToggleRuntimeSeamlessMode : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeSeamlessMode(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/seamless_on_16px.png", ":/seamless_16px.png",
+ ":/seamless_on_disabled_16px.png", ":/seamless_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Seamless;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Seamless);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Seamless);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SeamlessMode");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("L");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Seam&less Mode"));
+ setStatusTip(QApplication::translate("UIActionPool", "Switch between normal and seamless desktop integration mode"));
+ }
+};
+
+/** Toggle action extension, used as 'Scaled Mode' action class. */
+class UIActionToggleRuntimeScaledMode : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeScaledMode(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/scale_on_16px.png", ":/scale_16px.png",
+ ":/scale_on_disabled_16px.png", ":/scale_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Scale;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Scale);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Scale);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ScaleMode");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("C");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "S&caled Mode"));
+ setStatusTip(QApplication::translate("UIActionPool", "Switch between normal and scaled mode"));
+ }
+};
+
+#ifndef VBOX_WS_MAC
+/** Simple action extension, used as 'Perform Minimize Window' action class. */
+class UIActionSimpleRuntimePerformMinimizeWindow : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformMinimizeWindow(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/minimize_16px.png", ":/minimize_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_MinimizeWindow;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_MinimizeWindow);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_MinimizeWindow);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("WindowMinimize");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("M");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Minimize Window"));
+ setStatusTip(QApplication::translate("UIActionPool", "Minimize active window"));
+ }
+};
+#endif /* !VBOX_WS_MAC */
+
+/** Simple action extension, used as 'Perform Window Adjust' action class. */
+class UIActionSimpleRuntimePerformWindowAdjust : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformWindowAdjust(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/adjust_win_size_16px.png", ":/adjust_win_size_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("WindowAdjust");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("A");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Adjust Window Size"));
+ setStatusTip(QApplication::translate("UIActionPool", "Adjust window size and position to best fit the guest display"));
+ }
+};
+
+/** Toggle action extension, used as 'Guest Autoresize' action class. */
+class UIActionToggleRuntimeGuestAutoresize : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeGuestAutoresize(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/auto_resize_on_on_16px.png", ":/auto_resize_on_16px.png",
+ ":/auto_resize_on_on_disabled_16px.png", ":/auto_resize_on_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_GuestAutoresize;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_GuestAutoresize);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_GuestAutoresize);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("GuestAutoresize");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Auto-resize &Guest Display"));
+ setStatusTip(QApplication::translate("UIActionPool", "Automatically resize the guest display when the window is resized"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Take Screenshot' action class. */
+class UIActionSimpleRuntimePerformTakeScreenshot : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformTakeScreenshot(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/screenshot_take_16px.png", ":/screenshot_take_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_TakeScreenshot;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_TakeScreenshot);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_TakeScreenshot);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TakeScreenshot");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("E");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Take Screensh&ot..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Take guest display screenshot"));
+ }
+};
+
+/** Menu action extension, used as 'View' menu class. */
+class UIActionMenuRuntimeRecording : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeRecording(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_Recording;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Recording);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Recording);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Recording"));
+ }
+};
+
+/** Simple action extension, used as 'Show Recording Settings' action class. */
+class UIActionSimpleRuntimeShowRecordingSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowRecordingSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/video_capture_settings_16px.png", ":/video_capture_settings_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_RecordingSettings;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_RecordingSettings);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_RecordingSettings);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("RecordingSettingsDialog");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Recording Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display virtual machine settings window to configure video/audio recording"));
+ }
+};
+
+/** Toggle action extension, used as 'Recording' action class. */
+class UIActionToggleRuntimeRecording : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeRecording(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/video_capture_on_16px.png", ":/video_capture_16px.png",
+ ":/video_capture_on_disabled_16px.png", ":/video_capture_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_StartRecording;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_StartRecording);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_StartRecording);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Recording");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Recording"));
+ setStatusTip(QApplication::translate("UIActionPool", "Enable guest video/audio recording"));
+ }
+};
+
+/** Toggle action extension, used as 'VRDE Server' action class. */
+class UIActionToggleRuntimeVRDEServer : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeVRDEServer(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/vrdp_on_16px.png", ":/vrdp_16px.png",
+ ":/vrdp_on_disabled_16px.png", ":/vrdp_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_VRDEServer;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_VRDEServer);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_VRDEServer);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("VRDPServer");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "R&emote Display"));
+ setStatusTip(QApplication::translate("UIActionPool", "Allow remote desktop (RDP) connections to this machine"));
+ }
+};
+
+/** Menu action extension, used as 'MenuBar' menu class. */
+class UIActionMenuRuntimeMenuBar : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeMenuBar(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/menubar_16px.png", ":/menubar_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Menu Bar"));
+ }
+};
+
+/** Simple action extension, used as 'Show MenuBar Settings Window' action class. */
+class UIActionSimpleRuntimeShowMenuBarSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowMenuBarSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/menubar_settings_16px.png", ":/menubar_settings_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBarSettings;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBarSettings);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBarSettings);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MenuBarSettings");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Menu Bar Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display window to configure menu-bar"));
+ }
+};
+
+#ifndef VBOX_WS_MAC
+/** Toggle action extension, used as 'MenuBar' action class. */
+class UIActionToggleRuntimeMenuBar : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeMenuBar(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/menubar_on_16px.png", ":/menubar_16px.png",
+ ":/menubar_on_disabled_16px.png", ":/menubar_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleMenuBar;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleMenuBar);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleMenuBar);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleMenuBar");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Show Menu &Bar"));
+ setStatusTip(QApplication::translate("UIActionPool", "Enable menu-bar"));
+ }
+};
+#endif /* !VBOX_WS_MAC */
+
+/** Menu action extension, used as 'StatusBar' menu class. */
+class UIActionMenuRuntimeStatusBar : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeStatusBar(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/statusbar_16px.png", ":/statusbar_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Status Bar"));
+ }
+};
+
+/** Simple action extension, used as 'Show StatusBar Settings Window' action class. */
+class UIActionSimpleRuntimeShowStatusBarSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowStatusBarSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/statusbar_settings_16px.png", ":/statusbar_settings_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBarSettings;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBarSettings);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBarSettings);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("StatusBarSettings");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Status Bar Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display window to configure status-bar"));
+ }
+};
+
+/** Toggle action extension, used as 'StatusBar' action class. */
+class UIActionToggleRuntimeStatusBar : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeStatusBar(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/statusbar_on_16px.png", ":/statusbar_16px.png",
+ ":/statusbar_on_disabled_16px.png", ":/statusbar_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleStatusBar;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleStatusBar);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_ToggleStatusBar);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleStatusBar");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Show Status &Bar"));
+ setStatusTip(QApplication::translate("UIActionPool", "Enable status-bar"));
+ }
+};
+
+/** Menu action extension, used as 'Input' menu class. */
+class UIActionMenuRuntimeInput : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeInput(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuType_Input;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuType_Input);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType_Input);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Input"));
+ }
+};
+
+/** Menu action extension, used as 'Keyboard' menu class. */
+class UIActionMenuRuntimeKeyboard : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeKeyboard(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/keyboard_16px.png")
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_Keyboard;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_Keyboard);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_Keyboard);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Keyboard"));
+ }
+};
+
+/** Simple action extension, used as 'Show Keyboard Settings' action class. */
+class UIActionSimpleRuntimeShowKeyboardSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowKeyboardSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/keyboard_settings_16px.png", ":/keyboard_settings_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_KeyboardSettings;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_KeyboardSettings);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_KeyboardSettings);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("KeyboardSettings");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Keyboard Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display global preferences window to configure keyboard shortcuts"));
+ }
+};
+
+/** Simple action extension, used as 'Show Soft Keyboard' action class. */
+class UIActionSimpleRuntimeShowSoftKeyboard : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowSoftKeyboard(UIActionPool *pParent)
+ : UIActionSimple(pParent, UIIconPool::iconSet(":/soft_keyboard_16px.png"), true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_SoftKeyboard;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_SoftKeyboard);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_SoftKeyboard);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SoftKeyboard");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Soft Keyboard..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display soft keyboard"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Type CAD' action class. */
+class UIActionSimpleRuntimePerformTypeCAD : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformTypeCAD(UIActionPool *pParent)
+ : UIActionSimple(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCAD;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCAD);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCAD);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TypeCAD");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Del");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Insert %1", "that means send the %1 key sequence to the virtual machine").arg("Ctrl-Alt-Del"));
+ setStatusTip(QApplication::translate("UIActionPool", "Send the %1 sequence to the virtual machine").arg("Ctrl-Alt-Del"));
+ }
+};
+
+#ifdef VBOX_WS_X11
+/** X11: Simple action extension, used as 'Perform Type CABS' action class. */
+class UIActionSimpleRuntimePerformTypeCABS : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformTypeCABS(UIActionPool *pParent)
+ : UIActionSimple(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCABS;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCABS);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCABS);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TypeCABS");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+ return QKeySequence("Backspace");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Insert %1", "that means send the %1 key sequence to the virtual machine").arg("Ctrl-Alt-Backspace"));
+ setStatusTip(QApplication::translate("UIActionPool", "Send the %1 sequence to the virtual machine").arg("Ctrl-Alt-Backspace"));
+ }
+};
+#endif /* VBOX_WS_X11 */
+
+/** Simple action extension, used as 'Perform Type Ctrl Break' action class. */
+class UIActionSimpleRuntimePerformTypeCtrlBreak : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformTypeCtrlBreak(UIActionPool *pParent)
+ : UIActionSimple(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCtrlBreak;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCtrlBreak);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeCtrlBreak);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TypeCtrlBreak");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Insert %1", "that means send the %1 key sequence to the virtual machine").arg("Ctrl-Break"));
+ setStatusTip(QApplication::translate("UIActionPool", "Send the %1 sequence to the virtual machine").arg("Ctrl-Break"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Type Insert' action class. */
+class UIActionSimpleRuntimePerformTypeInsert : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformTypeInsert(UIActionPool *pParent)
+ : UIActionSimple(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeInsert;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeInsert);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeInsert);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TypeInsert");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Insert %1", "that means send the %1 key sequence to the virtual machine").arg("Insert"));
+ setStatusTip(QApplication::translate("UIActionPool", "Send the %1 sequence to the virtual machine").arg("Insert"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Type PrintScreen' action class. */
+class UIActionSimpleRuntimePerformTypePrintScreen : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformTypePrintScreen(UIActionPool *pParent)
+ : UIActionSimple(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypePrintScreen;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypePrintScreen);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypePrintScreen);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TypePrintScreen");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Insert %1", "that means send the %1 key sequence to the virtual machine").arg("Print Screen"));
+ setStatusTip(QApplication::translate("UIActionPool", "Send the %1 sequence to the virtual machine").arg("Print Screen"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Type Alt PrintScreen' action class. */
+class UIActionSimpleRuntimePerformTypeAltPrintScreen : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformTypeAltPrintScreen(UIActionPool *pParent)
+ : UIActionSimple(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeAltPrintScreen;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeAltPrintScreen);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeAltPrintScreen);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TypeAltPrintScreen");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Insert %1", "that means send the %1 key sequence to the virtual machine").arg("Alt Print Screen"));
+ setStatusTip(QApplication::translate("UIActionPool", "Send the %1 sequence to the virtual machine").arg("Alt Print Screen"));
+ }
+};
+
+/** Toggle action extension, used as 'Perform Host Key Combo Press/Release' action class. */
+class UIActionToggleRuntimePerformTypeHostKeyCombo : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimePerformTypeHostKeyCombo(UIActionPool *pParent)
+ : UIActionToggle(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeHostKeyCombo;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeHostKeyCombo);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_TypeHostKeyCombo);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("TypeHostKeyCombo");
+ }
+
+ /** Returns default shortcut. */
+ virtual QKeySequence defaultShortcut(UIActionPoolType) const RT_OVERRIDE
+ {
+#ifdef VBOX_WS_MAC
+ return QKeySequence("Insert");
+#else
+ return QKeySequence("Insert");
+#endif
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Insert %1", "that means send the %1 key sequence to the virtual machine").arg("Host Key Combo"));
+ setStatusTip(QApplication::translate("UIActionPool", "Send the %1 sequence to the virtual machine").arg("Host Key Combo"));
+ }
+};
+
+/** Menu action extension, used as 'Mouse' menu class. */
+class UIActionMenuRuntimeMouse : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeMouse(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_Mouse;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_Mouse);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_Mouse);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Mouse"));
+ }
+};
+
+/** Toggle action extension, used as 'Mouse Integration' action class. */
+class UIActionToggleRuntimeMouseIntegration : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeMouseIntegration(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/mouse_can_seamless_on_16px.png", ":/mouse_can_seamless_16px.png",
+ ":/mouse_can_seamless_on_disabled_16px.png", ":/mouse_can_seamless_disabled_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuInputActionType_MouseIntegration;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuInputActionType_MouseIntegration);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_MouseIntegration);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("MouseIntegration");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Mouse Integration"));
+ setStatusTip(QApplication::translate("UIActionPool", "Enable host mouse pointer integration"));
+ }
+};
+
+
+/** Menu action extension, used as 'Devices' menu class. */
+class UIActionMenuRuntimeDevices : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeDevices(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuType_Devices;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuType_Devices);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType_Devices);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Devices"));
+ }
+};
+
+/** Menu action extension, used as 'Hard Drives' menu class. */
+class UIActionMenuRuntimeHardDrives : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeHardDrives(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/hd_16px.png", ":/hd_disabled_16px.png")
+ {
+ setShowToolTip(true);
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrives;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrives);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrives);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Hard Disks"));
+ }
+};
+
+/** Simple action extension, used as 'Show Hard Drives Settings' action class. */
+class UIActionSimpleRuntimeShowHardDrivesSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowHardDrivesSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/hd_settings_16px.png", ":/hd_settings_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrivesSettings;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrivesSettings);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrivesSettings);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("HardDriveSettingsDialog");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Hard Disk Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display virtual machine settings window to configure hard disks"));
+ }
+};
+
+/** Menu action extension, used as 'Optical Drives' menu class. */
+class UIActionMenuRuntimeOpticalDevices : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeOpticalDevices(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/cd_16px.png", ":/cd_disabled_16px.png")
+ {
+ setShowToolTip(true);
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_OpticalDevices;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_OpticalDevices);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_OpticalDevices);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Optical Drives"));
+ }
+};
+
+/** Menu action extension, used as 'Floppy Drives' menu class. */
+class UIActionMenuRuntimeFloppyDevices : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeFloppyDevices(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/fd_16px.png", ":/fd_disabled_16px.png")
+ {
+ setShowToolTip(true);
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_FloppyDevices;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_FloppyDevices);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_FloppyDevices);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Floppy Drives"));
+ }
+};
+
+/** Menu action extension, used as 'Audio' menu class. */
+class UIActionMenuRuntimeAudio : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeAudio(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/audio_16px.png", ":/audio_all_off_16px.png")
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Audio;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Audio);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Audio);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Audio"));
+ }
+};
+
+/** Toggle action extension, used as 'Audio Output' action class. */
+class UIActionToggleRuntimeAudioOutput : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeAudioOutput(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/audio_output_on_16px.png", ":/audio_output_16px.png",
+ ":/audio_output_on_16px.png", ":/audio_output_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioOutput;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioOutput);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioOutput);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleAudioOutput");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Audio Output"));
+ setStatusTip(QApplication::translate("UIActionPool", "Enable audio output"));
+ }
+};
+
+/** Toggle action extension, used as 'Audio Input' action class. */
+class UIActionToggleRuntimeAudioInput : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeAudioInput(UIActionPool *pParent)
+ : UIActionToggle(pParent,
+ ":/audio_input_on_16px.png", ":/audio_input_16px.png",
+ ":/audio_input_on_16px.png", ":/audio_input_16px.png",
+ true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioInput;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioInput);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_AudioInput);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("ToggleAudioInput");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Audio Input"));
+ setStatusTip(QApplication::translate("UIActionPool", "Enable audio input"));
+ }
+};
+
+/** Menu action extension, used as 'Network Adapters' menu class. */
+class UIActionMenuRuntimeNetworkAdapters : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeNetworkAdapters(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/nw_16px.png", ":/nw_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Network;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Network);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Network);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Network"));
+ }
+};
+
+/** Simple action extension, used as 'Show Network Settings' action class. */
+class UIActionSimpleRuntimeShowNetworkSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowNetworkSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/nw_settings_16px.png", ":/nw_settings_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_NetworkSettings;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_NetworkSettings);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_NetworkSettings);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("NetworkSettingsDialog");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Network Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display virtual machine settings window to configure network adapters"));
+ }
+};
+
+/** Menu action extension, used as 'USB Devices' menu class. */
+class UIActionMenuRuntimeUSBDevices : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeUSBDevices(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/usb_16px.png", ":/usb_disabled_16px.png")
+ {
+ setShowToolTip(true);
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevices;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevices);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevices);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&USB"));
+ }
+};
+
+/** Simple action extension, used as 'Show USB Devices Settings' action class. */
+class UIActionSimpleRuntimeShowUSBDevicesSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowUSBDevicesSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/usb_settings_16px.png", ":/usb_settings_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevicesSettings;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevicesSettings);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevicesSettings);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("USBDevicesSettingsDialog");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&USB Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display virtual machine settings window to configure USB devices"));
+ }
+};
+
+/** Menu action extension, used as 'Web Cams' menu class. */
+class UIActionMenuRuntimeWebCams : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeWebCams(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/web_camera_16px.png", ":/web_camera_disabled_16px.png")
+ {
+ setShowToolTip(true);
+ }
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_WebCams;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_WebCams);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_WebCams);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Webcams"));
+ }
+};
+
+/** Menu action extension, used as 'Shared Clipboard' menu class. */
+class UIActionMenuRuntimeSharedClipboard : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeSharedClipboard(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/shared_clipboard_16px.png", ":/shared_clipboard_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedClipboard;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedClipboard);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedClipboard);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Shared &Clipboard"));
+ }
+};
+
+/** Menu action extension, used as 'Drag & Drop' menu class. */
+class UIActionMenuRuntimeDragAndDrop : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeDragAndDrop(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/drag_drop_16px.png", ":/drag_drop_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_DragAndDrop;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_DragAndDrop);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_DragAndDrop);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Drag and Drop"));
+ }
+};
+
+/** Menu action extension, used as 'Shared Folders' menu class. */
+class UIActionMenuRuntimeSharedFolders : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeSharedFolders(UIActionPool *pParent)
+ : UIActionMenu(pParent, ":/sf_16px.png", ":/sf_disabled_16px.png")
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFolders;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFolders);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFolders);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Shared Folders"));
+ }
+};
+
+/** Simple action extension, used as 'Show Shared Folders Settings' action class. */
+class UIActionSimpleRuntimeShowSharedFoldersSettings : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowSharedFoldersSettings(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/sf_settings_16px.png", ":/sf_settings_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFoldersSettings;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFoldersSettings);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFoldersSettings);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("SharedFoldersSettingsDialog");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Shared Folders Settings..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Display virtual machine settings window to configure shared folders"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Insert Guest Additions Disk' action class. */
+class UIActionSimpleRuntimePerformInsertGuestAdditionsDisk : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformInsertGuestAdditionsDisk(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/guesttools_16px.png", ":/guesttools_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_InsertGuestAdditionsDisk;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_InsertGuestAdditionsDisk);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_InsertGuestAdditionsDisk);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("InsertGuestAdditionsDisk");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Insert Guest Additions CD image..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Insert the Guest Additions disk file into the virtual optical drive"));
+ }
+};
+
+/** Simple action extension, used as 'Perform Upgrade Guest Additions' action class. */
+class UIActionSimpleRuntimePerformUpgradeGuestAdditions : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimePerformUpgradeGuestAdditions(UIActionPool *pParent)
+ : UIActionSimple(pParent, ":/guesttools_update_16px.png", ":/guesttools_update_disabled_16px.png", true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_UpgradeGuestAdditions;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_UpgradeGuestAdditions);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_UpgradeGuestAdditions);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("UpgradeGuestAdditions");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Upgrade Guest Additions..."));
+ setStatusTip(QApplication::translate("UIActionPool", "Upgrade Guest Additions"));
+ }
+};
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+/** Menu action extension, used as 'Debug' menu class. */
+class UIActionMenuRuntimeDebug : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuRuntimeDebug(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::MenuType_Debug;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::MenuType_Debug);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->isAllowedInMenuBar(UIExtraDataMetaDefs::MenuType_Debug);
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "De&bug"));
+ }
+};
+
+/** Simple action extension, used as 'Show Statistics' action class. */
+class UIActionSimpleRuntimeShowStatistics : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowStatistics(UIActionPool *pParent)
+ : UIActionSimple(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Statistics;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Statistics);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Statistics);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("StatisticWindow");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Statistics...", "debug action"));
+ }
+};
+
+/** Simple action extension, used as 'Show Command Line' action class. */
+class UIActionSimpleRuntimeShowCommandLine : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeShowCommandLine(UIActionPool *pParent)
+ : UIActionSimple(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_CommandLine;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_CommandLine);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_CommandLine);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("CommandLineWindow");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Command Line...", "debug action"));
+ }
+};
+
+/** Toggle action extension, used as 'Logging' action class. */
+class UIActionToggleRuntimeLogging : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleRuntimeLogging(UIActionPool *pParent)
+ : UIActionToggle(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Logging;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Logging);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Logging);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("Logging");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "&Logging", "debug action"));
+ }
+};
+
+/** Simple action extension, used as 'Guest Control Terminal' action class. */
+class UIActionSimpleRuntimeGuestControlConsole : public UIActionSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionSimpleRuntimeGuestControlConsole(UIActionPool *pParent)
+ : UIActionSimple(pParent, true)
+ {}
+
+protected:
+
+ /** Returns action extra-data ID. */
+ virtual int extraDataID() const RT_OVERRIDE
+ {
+ return UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_GuestControlConsole;
+ }
+ /** Returns action extra-data key. */
+ virtual QString extraDataKey() const RT_OVERRIDE
+ {
+ return gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_GuestControlConsole);
+ }
+ /** Returns whether action is allowed. */
+ virtual bool isAllowed() const RT_OVERRIDE
+ {
+ return actionPool()->toRuntime()->isAllowedInMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_GuestControlConsole);
+ }
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("GuestControlConsole");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Guest Control Terminal...", "debug action"));
+ }
+};
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+/** macOS: Menu action extension, used as 'Dock' menu class. */
+class UIActionMenuDock : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuDock(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE {}
+};
+
+/** macOS: Menu action extension, used as 'Dock Settings' menu class. */
+class UIActionMenuDockSettings : public UIActionMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionMenuDockSettings(UIActionPool *pParent)
+ : UIActionMenu(pParent)
+ {}
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Dock Icon"));
+ }
+};
+
+/** macOS: Toggle action extension, used as 'Dock Preview Monitor' action class. */
+class UIActionToggleDockPreviewMonitor : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleDockPreviewMonitor(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("DockPreviewMonitor");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Show Monitor Preview"));
+ }
+};
+
+/** macOS: Toggle action extension, used as 'Dock Disable Monitor' action class. */
+class UIActionToggleDockDisableMonitor : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleDockDisableMonitor(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("DockDisableMonitor");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Show Application Icon"));
+ }
+};
+
+/** macOS: Toggle action extension, used as 'Dock Icon Disable Overlay' action class. */
+class UIActionToggleDockIconDisableOverlay : public UIActionToggle
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs action passing @a pParent to the base-class. */
+ UIActionToggleDockIconDisableOverlay(UIActionPool *pParent)
+ : UIActionToggle(pParent)
+ {}
+
+protected:
+
+ /** Returns shortcut extra-data ID. */
+ virtual QString shortcutExtraDataID() const RT_OVERRIDE
+ {
+ return QString("DockOverlayDisable");
+ }
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE
+ {
+ setName(QApplication::translate("UIActionPool", "Disable Dock Icon Overlay"));
+ }
+};
+#endif /* VBOX_WS_MAC */
+
+
+/*********************************************************************************************************************************
+* Class UIActionPoolRuntime implementation. *
+*********************************************************************************************************************************/
+
+UIActionPoolRuntime::UIActionPoolRuntime(bool fTemporary /* = false */)
+ : UIActionPool(UIActionPoolType_Runtime, fTemporary)
+ , m_cHostScreens(0)
+ , m_cGuestScreens(0)
+ , m_fGuestSupportsGraphics(false)
+{
+}
+
+void UIActionPoolRuntime::setHostScreenCount(int cCount)
+{
+ m_cHostScreens = cCount;
+ m_invalidations << UIActionIndexRT_M_View << UIActionIndexRT_M_ViewPopup;
+}
+
+void UIActionPoolRuntime::setGuestScreenCount(int cCount)
+{
+ m_cGuestScreens = cCount;
+ m_invalidations << UIActionIndexRT_M_View << UIActionIndexRT_M_ViewPopup;
+}
+
+void UIActionPoolRuntime::setGuestScreenSize(int iGuestScreen, const QSize &size)
+{
+ m_mapGuestScreenSize[iGuestScreen] = size;
+ m_invalidations << UIActionIndexRT_M_View << UIActionIndexRT_M_ViewPopup;
+}
+
+void UIActionPoolRuntime::setGuestScreenVisible(int iGuestScreen, bool fVisible)
+{
+ m_mapGuestScreenIsVisible[iGuestScreen] = fVisible;
+ m_invalidations << UIActionIndexRT_M_View << UIActionIndexRT_M_ViewPopup;
+}
+
+void UIActionPoolRuntime::setGuestSupportsGraphics(bool fSupports)
+{
+ m_fGuestSupportsGraphics = fSupports;
+ m_invalidations << UIActionIndexRT_M_View << UIActionIndexRT_M_ViewPopup;
+}
+
+void UIActionPoolRuntime::setHostScreenForGuestScreenMap(const QMap<int, int> &scheme)
+{
+ m_mapHostScreenForGuestScreen = scheme;
+ m_invalidations << UIActionIndexRT_M_View << UIActionIndexRT_M_ViewPopup;
+}
+
+QMap<int, int> UIActionPoolRuntime::hostScreenForGuestScreenMap() const
+{
+ return m_mapHostScreenForGuestScreen;
+}
+
+bool UIActionPoolRuntime::isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType type) const
+{
+ foreach (const UIExtraDataMetaDefs::RuntimeMenuMachineActionType &restriction, m_restrictedActionsMenuMachine.values())
+ if (restriction & type)
+ return false;
+ return true;
+}
+
+void UIActionPoolRuntime::setRestrictionForMenuMachine(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuMachineActionType restriction)
+{
+ m_restrictedActionsMenuMachine[level] = restriction;
+ m_invalidations << UIActionIndexRT_M_Machine;
+}
+
+bool UIActionPoolRuntime::isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType type) const
+{
+ foreach (const UIExtraDataMetaDefs::RuntimeMenuViewActionType &restriction, m_restrictedActionsMenuView.values())
+ if (restriction & type)
+ return false;
+ return true;
+}
+
+void UIActionPoolRuntime::setRestrictionForMenuView(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType restriction)
+{
+ m_restrictedActionsMenuView[level] = restriction;
+ m_invalidations << UIActionIndexRT_M_View << UIActionIndexRT_M_ViewPopup;
+}
+
+bool UIActionPoolRuntime::isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType type) const
+{
+ foreach (const UIExtraDataMetaDefs::RuntimeMenuInputActionType &restriction, m_restrictedActionsMenuInput.values())
+ if (restriction & type)
+ return false;
+ return true;
+}
+
+void UIActionPoolRuntime::setRestrictionForMenuInput(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuInputActionType restriction)
+{
+ m_restrictedActionsMenuInput[level] = restriction;
+ m_invalidations << UIActionIndexRT_M_Input;
+}
+
+bool UIActionPoolRuntime::isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType type) const
+{
+ foreach (const UIExtraDataMetaDefs::RuntimeMenuDevicesActionType &restriction, m_restrictedActionsMenuDevices.values())
+ if (restriction & type)
+ return false;
+ return true;
+}
+
+void UIActionPoolRuntime::setRestrictionForMenuDevices(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuDevicesActionType restriction)
+{
+ m_restrictedActionsMenuDevices[level] = restriction;
+ m_invalidations << UIActionIndexRT_M_Devices;
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+bool UIActionPoolRuntime::isAllowedInMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType type) const
+{
+ foreach (const UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType &restriction, m_restrictedActionsMenuDebug.values())
+ if (restriction & type)
+ return false;
+ return true;
+}
+
+void UIActionPoolRuntime::setRestrictionForMenuDebugger(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType restriction)
+{
+ m_restrictedActionsMenuDebug[level] = restriction;
+ m_invalidations << UIActionIndexRT_M_Debug;
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+void UIActionPoolRuntime::preparePool()
+{
+ /* 'Machine' actions: */
+ m_pool[UIActionIndexRT_M_Machine] = new UIActionMenuRuntimeMachine(this);
+ m_pool[UIActionIndexRT_M_Machine_S_Settings] = new UIActionSimpleRuntimeShowSettings(this);
+ m_pool[UIActionIndexRT_M_Machine_S_TakeSnapshot] = new UIActionSimpleRuntimePerformTakeSnapshot(this);
+ m_pool[UIActionIndexRT_M_Machine_S_ShowInformation] = new UIActionSimpleRuntimeShowInformationDialog(this);
+ m_pool[UIActionIndexRT_M_Machine_S_ShowFileManager] = new UIActionSimpleRuntimeShowFileManagerDialog(this);
+ m_pool[UIActionIndexRT_M_Machine_T_Pause] = new UIActionToggleRuntimePause(this);
+ m_pool[UIActionIndexRT_M_Machine_S_Reset] = new UIActionSimpleRuntimePerformReset(this);
+ m_pool[UIActionIndexRT_M_Machine_S_Detach] = new UIActionSimpleRuntimePerformDetach(this);
+ m_pool[UIActionIndexRT_M_Machine_S_SaveState] = new UIActionSimpleRuntimePerformSaveState(this);
+ m_pool[UIActionIndexRT_M_Machine_S_Shutdown] = new UIActionSimpleRuntimePerformShutdown(this);
+ m_pool[UIActionIndexRT_M_Machine_S_PowerOff] = new UIActionSimpleRuntimePerformPowerOff(this);
+ m_pool[UIActionIndexRT_M_Machine_S_ShowLogDialog] = new UIActionSimpleRuntimeShowLogs(this);
+
+ /* 'View' actions: */
+ m_pool[UIActionIndexRT_M_View] = new UIActionMenuRuntimeView(this);
+ m_pool[UIActionIndexRT_M_ViewPopup] = new UIActionMenuRuntimeViewPopup(this);
+ m_pool[UIActionIndexRT_M_View_T_Fullscreen] = new UIActionToggleRuntimeFullscreenMode(this);
+ m_pool[UIActionIndexRT_M_View_T_Seamless] = new UIActionToggleRuntimeSeamlessMode(this);
+ m_pool[UIActionIndexRT_M_View_T_Scale] = new UIActionToggleRuntimeScaledMode(this);
+#ifndef VBOX_WS_MAC
+ m_pool[UIActionIndexRT_M_View_S_MinimizeWindow] = new UIActionSimpleRuntimePerformMinimizeWindow(this);
+#endif /* !VBOX_WS_MAC */
+ m_pool[UIActionIndexRT_M_View_S_AdjustWindow] = new UIActionSimpleRuntimePerformWindowAdjust(this);
+ m_pool[UIActionIndexRT_M_View_T_GuestAutoresize] = new UIActionToggleRuntimeGuestAutoresize(this);
+ m_pool[UIActionIndexRT_M_View_S_TakeScreenshot] = new UIActionSimpleRuntimePerformTakeScreenshot(this);
+ m_pool[UIActionIndexRT_M_View_M_Recording] = new UIActionMenuRuntimeRecording(this);
+ m_pool[UIActionIndexRT_M_View_M_Recording_S_Settings] = new UIActionSimpleRuntimeShowRecordingSettings(this);
+ m_pool[UIActionIndexRT_M_View_M_Recording_T_Start] = new UIActionToggleRuntimeRecording(this);
+ m_pool[UIActionIndexRT_M_View_T_VRDEServer] = new UIActionToggleRuntimeVRDEServer(this);
+ m_pool[UIActionIndexRT_M_View_M_MenuBar] = new UIActionMenuRuntimeMenuBar(this);
+ m_pool[UIActionIndexRT_M_View_M_MenuBar_S_Settings] = new UIActionSimpleRuntimeShowMenuBarSettings(this);
+#ifndef VBOX_WS_MAC
+ m_pool[UIActionIndexRT_M_View_M_MenuBar_T_Visibility] = new UIActionToggleRuntimeMenuBar(this);
+#endif /* !VBOX_WS_MAC */
+ m_pool[UIActionIndexRT_M_View_M_StatusBar] = new UIActionMenuRuntimeStatusBar(this);
+ m_pool[UIActionIndexRT_M_View_M_StatusBar_S_Settings] = new UIActionSimpleRuntimeShowStatusBarSettings(this);
+ m_pool[UIActionIndexRT_M_View_M_StatusBar_T_Visibility] = new UIActionToggleRuntimeStatusBar(this);
+
+ /* 'Input' actions: */
+ m_pool[UIActionIndexRT_M_Input] = new UIActionMenuRuntimeInput(this);
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard] = new UIActionMenuRuntimeKeyboard(this);
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard_S_Settings] = new UIActionSimpleRuntimeShowKeyboardSettings(this);
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard_S_SoftKeyboard] = new UIActionSimpleRuntimeShowSoftKeyboard(this);
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard_S_TypeCAD] = new UIActionSimpleRuntimePerformTypeCAD(this);
+#ifdef VBOX_WS_X11
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard_S_TypeCABS] = new UIActionSimpleRuntimePerformTypeCABS(this);
+#endif /* VBOX_WS_X11 */
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard_S_TypeCtrlBreak] = new UIActionSimpleRuntimePerformTypeCtrlBreak(this);
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard_S_TypeInsert] = new UIActionSimpleRuntimePerformTypeInsert(this);
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard_S_TypePrintScreen] = new UIActionSimpleRuntimePerformTypePrintScreen(this);
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard_S_TypeAltPrintScreen] = new UIActionSimpleRuntimePerformTypeAltPrintScreen(this);
+ m_pool[UIActionIndexRT_M_Input_M_Keyboard_T_TypeHostKeyCombo] = new UIActionToggleRuntimePerformTypeHostKeyCombo(this);
+ m_pool[UIActionIndexRT_M_Input_M_Mouse] = new UIActionMenuRuntimeMouse(this);
+ m_pool[UIActionIndexRT_M_Input_M_Mouse_T_Integration] = new UIActionToggleRuntimeMouseIntegration(this);
+
+ /* 'Devices' actions: */
+ m_pool[UIActionIndexRT_M_Devices] = new UIActionMenuRuntimeDevices(this);
+ m_pool[UIActionIndexRT_M_Devices_M_HardDrives] = new UIActionMenuRuntimeHardDrives(this);
+ m_pool[UIActionIndexRT_M_Devices_M_HardDrives_S_Settings] = new UIActionSimpleRuntimeShowHardDrivesSettings(this);
+ m_pool[UIActionIndexRT_M_Devices_M_OpticalDevices] = new UIActionMenuRuntimeOpticalDevices(this);
+ m_pool[UIActionIndexRT_M_Devices_M_FloppyDevices] = new UIActionMenuRuntimeFloppyDevices(this);
+ m_pool[UIActionIndexRT_M_Devices_M_Audio] = new UIActionMenuRuntimeAudio(this);
+ m_pool[UIActionIndexRT_M_Devices_M_Audio_T_Output] = new UIActionToggleRuntimeAudioOutput(this);
+ m_pool[UIActionIndexRT_M_Devices_M_Audio_T_Input] = new UIActionToggleRuntimeAudioInput(this);
+ m_pool[UIActionIndexRT_M_Devices_M_Network] = new UIActionMenuRuntimeNetworkAdapters(this);
+ m_pool[UIActionIndexRT_M_Devices_M_Network_S_Settings] = new UIActionSimpleRuntimeShowNetworkSettings(this);
+ m_pool[UIActionIndexRT_M_Devices_M_USBDevices] = new UIActionMenuRuntimeUSBDevices(this);
+ m_pool[UIActionIndexRT_M_Devices_M_USBDevices_S_Settings] = new UIActionSimpleRuntimeShowUSBDevicesSettings(this);
+ m_pool[UIActionIndexRT_M_Devices_M_WebCams] = new UIActionMenuRuntimeWebCams(this);
+ m_pool[UIActionIndexRT_M_Devices_M_SharedClipboard] = new UIActionMenuRuntimeSharedClipboard(this);
+ m_pool[UIActionIndexRT_M_Devices_M_DragAndDrop] = new UIActionMenuRuntimeDragAndDrop(this);
+ m_pool[UIActionIndexRT_M_Devices_M_SharedFolders] = new UIActionMenuRuntimeSharedFolders(this);
+ m_pool[UIActionIndexRT_M_Devices_M_SharedFolders_S_Settings] = new UIActionSimpleRuntimeShowSharedFoldersSettings(this);
+ m_pool[UIActionIndexRT_M_Devices_S_InsertGuestAdditionsDisk] = new UIActionSimpleRuntimePerformInsertGuestAdditionsDisk(this);
+ m_pool[UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions] = new UIActionSimpleRuntimePerformUpgradeGuestAdditions(this);
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* 'Debug' actions: */
+ m_pool[UIActionIndexRT_M_Debug] = new UIActionMenuRuntimeDebug(this);
+ m_pool[UIActionIndexRT_M_Debug_S_ShowStatistics] = new UIActionSimpleRuntimeShowStatistics(this);
+ m_pool[UIActionIndexRT_M_Debug_S_ShowCommandLine] = new UIActionSimpleRuntimeShowCommandLine(this);
+ m_pool[UIActionIndexRT_M_Debug_T_Logging] = new UIActionToggleRuntimeLogging(this);
+ m_pool[UIActionIndexRT_M_Debug_S_GuestControlConsole] = new UIActionSimpleRuntimeGuestControlConsole(this);
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+ /* 'Dock' actions: */
+ m_pool[UIActionIndexRT_M_Dock] = new UIActionMenuDock(this);
+ m_pool[UIActionIndexRT_M_Dock_M_DockSettings] = new UIActionMenuDockSettings(this);
+ m_pool[UIActionIndexRT_M_Dock_M_DockSettings_T_PreviewMonitor] = new UIActionToggleDockPreviewMonitor(this);
+ m_pool[UIActionIndexRT_M_Dock_M_DockSettings_T_DisableMonitor] = new UIActionToggleDockDisableMonitor(this);
+ m_pool[UIActionIndexRT_M_Dock_M_DockSettings_T_DisableOverlay] = new UIActionToggleDockIconDisableOverlay(this);
+#endif /* VBOX_WS_MAC */
+
+ /* Prepare update-handlers for known menus: */
+ m_menuUpdateHandlers[UIActionIndexRT_M_Machine].ptfr = &UIActionPoolRuntime::updateMenuMachine;
+ m_menuUpdateHandlers[UIActionIndexRT_M_View].ptfr = &UIActionPoolRuntime::updateMenuView;
+ m_menuUpdateHandlers[UIActionIndexRT_M_ViewPopup].ptfr = &UIActionPoolRuntime::updateMenuViewPopup;
+ m_menuUpdateHandlers[UIActionIndexRT_M_View_M_Recording].ptfr = &UIActionPoolRuntime::updateMenuViewRecording;
+ m_menuUpdateHandlers[UIActionIndexRT_M_View_M_MenuBar].ptfr = &UIActionPoolRuntime::updateMenuViewMenuBar;
+ m_menuUpdateHandlers[UIActionIndexRT_M_View_M_StatusBar].ptfr = &UIActionPoolRuntime::updateMenuViewStatusBar;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Input].ptfr = &UIActionPoolRuntime::updateMenuInput;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Input_M_Keyboard].ptfr = &UIActionPoolRuntime::updateMenuInputKeyboard;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Input_M_Mouse].ptfr = &UIActionPoolRuntime::updateMenuInputMouse;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices].ptfr = &UIActionPoolRuntime::updateMenuDevices;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_HardDrives].ptfr = &UIActionPoolRuntime::updateMenuDevicesHardDrives;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_Audio].ptfr = &UIActionPoolRuntime::updateMenuDevicesAudio;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_Network].ptfr = &UIActionPoolRuntime::updateMenuDevicesNetwork;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_USBDevices].ptfr = &UIActionPoolRuntime::updateMenuDevicesUSBDevices;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_SharedFolders].ptfr = &UIActionPoolRuntime::updateMenuDevicesSharedFolders;
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ m_menuUpdateHandlers[UIActionIndexRT_M_Debug].ptfr = &UIActionPoolRuntime::updateMenuDebug;
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+ /* Call to base-class: */
+ UIActionPool::preparePool();
+}
+
+void UIActionPoolRuntime::prepareConnections()
+{
+ /* Prepare connections: */
+ connect(gShortcutPool, &UIShortcutPool::sigManagerShortcutsReloaded,
+ this, &UIActionPoolRuntime::sltApplyShortcuts);
+ connect(gShortcutPool, &UIShortcutPool::sigRuntimeShortcutsReloaded,
+ this, &UIActionPoolRuntime::sltApplyShortcuts);
+ connect(gEDataManager, &UIExtraDataManager::sigMenuBarConfigurationChange,
+ this, &UIActionPoolRuntime::sltHandleConfigurationChange);
+
+ /* Call to base-class: */
+ UIActionPool::prepareConnections();
+}
+
+void UIActionPoolRuntime::updateConfiguration()
+{
+ /* Get machine ID: */
+ const QUuid uMachineID = uiCommon().managedVMUuid();
+ if (uMachineID.isNull())
+ return;
+
+ /* Recache common action restrictions: */
+ m_restrictedMenus[UIActionRestrictionLevel_Base] = gEDataManager->restrictedRuntimeMenuTypes(uMachineID);
+ m_restrictedActionsMenuApplication[UIActionRestrictionLevel_Base] = gEDataManager->restrictedRuntimeMenuApplicationActionTypes(uMachineID);
+ m_restrictedActionsMenuMachine[UIActionRestrictionLevel_Base] = gEDataManager->restrictedRuntimeMenuMachineActionTypes(uMachineID);
+ m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] = gEDataManager->restrictedRuntimeMenuViewActionTypes(uMachineID);
+ m_restrictedActionsMenuInput[UIActionRestrictionLevel_Base] = gEDataManager->restrictedRuntimeMenuInputActionTypes(uMachineID);
+ m_restrictedActionsMenuDevices[UIActionRestrictionLevel_Base] = gEDataManager->restrictedRuntimeMenuDevicesActionTypes(uMachineID);
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ m_restrictedActionsMenuDebug[UIActionRestrictionLevel_Base] = gEDataManager->restrictedRuntimeMenuDebuggerActionTypes(uMachineID);
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ m_restrictedActionsMenuWindow[UIActionRestrictionLevel_Base] = gEDataManager->restrictedRuntimeMenuWindowActionTypes(uMachineID);
+#endif /* VBOX_WS_MAC */
+ m_restrictedActionsMenuHelp[UIActionRestrictionLevel_Base] = gEDataManager->restrictedRuntimeMenuHelpActionTypes(uMachineID);
+
+ /* Recache visual state action restrictions: */
+ UIVisualStateType restrictedVisualStates = gEDataManager->restrictedVisualStates(uMachineID);
+ {
+ if (restrictedVisualStates & UIVisualStateType_Fullscreen)
+ m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
+ (m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuViewActionType_Fullscreen);
+ if (restrictedVisualStates & UIVisualStateType_Seamless)
+ m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
+ (m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuViewActionType_Seamless);
+ if (restrictedVisualStates & UIVisualStateType_Scale)
+ m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
+ (m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuViewActionType_Scale);
+ }
+
+ /* Recache reconfiguration action restrictions: */
+ bool fReconfigurationAllowed = gEDataManager->machineReconfigurationEnabled(uMachineID);
+ if (!fReconfigurationAllowed)
+ {
+ m_restrictedActionsMenuMachine[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuMachineActionType)
+ (m_restrictedActionsMenuMachine[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuMachineActionType_SettingsDialog);
+ m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
+ (m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuViewActionType_RecordingSettings);
+ m_restrictedActionsMenuInput[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuInputActionType)
+ (m_restrictedActionsMenuInput[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuInputActionType_KeyboardSettings);
+ m_restrictedActionsMenuInput[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuInputActionType)
+ (m_restrictedActionsMenuInput[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuInputActionType_SoftKeyboard);
+ m_restrictedActionsMenuDevices[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)
+ (m_restrictedActionsMenuDevices[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_HardDrivesSettings);
+ m_restrictedActionsMenuDevices[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)
+ (m_restrictedActionsMenuDevices[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_NetworkSettings);
+ m_restrictedActionsMenuDevices[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)
+ (m_restrictedActionsMenuDevices[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevicesSettings);
+ m_restrictedActionsMenuDevices[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)
+ (m_restrictedActionsMenuDevices[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_SharedFoldersSettings);
+ }
+
+ /* Recache snapshot related action restrictions: */
+ bool fSnapshotOperationsAllowed = gEDataManager->machineSnapshotOperationsEnabled(uMachineID);
+ if (!fSnapshotOperationsAllowed)
+ {
+ m_restrictedActionsMenuMachine[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuMachineActionType)
+ (m_restrictedActionsMenuMachine[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuMachineActionType_TakeSnapshot);
+ }
+
+ /* Recache extension-pack related action restrictions: */
+ CExtPackManager extPackManager = uiCommon().virtualBox().GetExtensionPackManager();
+ if (!extPackManager.isNull() && !extPackManager.IsExtPackUsable(GUI_ExtPackName))
+ {
+ m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
+ (m_restrictedActionsMenuView[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::RuntimeMenuViewActionType_VRDEServer);
+ }
+
+ /* Recache close related action restrictions: */
+ MachineCloseAction restrictedCloseActions = gEDataManager->restrictedMachineCloseActions(uMachineID);
+ bool fAllCloseActionsRestricted = (!uiCommon().isSeparateProcess() || (restrictedCloseActions & MachineCloseAction_Detach))
+ && (restrictedCloseActions & MachineCloseAction_SaveState)
+ && (restrictedCloseActions & MachineCloseAction_Shutdown)
+ && (restrictedCloseActions & MachineCloseAction_PowerOff);
+ if (fAllCloseActionsRestricted)
+ {
+ m_restrictedActionsMenuApplication[UIActionRestrictionLevel_Base] = (UIExtraDataMetaDefs::MenuApplicationActionType)
+ (m_restrictedActionsMenuApplication[UIActionRestrictionLevel_Base] | UIExtraDataMetaDefs::MenuApplicationActionType_Close);
+ }
+
+ /* Call to base-class: */
+ UIActionPool::updateConfiguration();
+}
+
+void UIActionPoolRuntime::updateMenu(int iIndex)
+{
+ /* If index belongs to base-class => delegate to base-class: */
+ if (iIndex < UIActionIndex_Max)
+ UIActionPool::updateMenu(iIndex);
+ /* Otherwise,
+ * if menu with such index is invalidated
+ * and there is update-handler => handle it here: */
+ else if ( iIndex > UIActionIndex_Max
+ && m_invalidations.contains(iIndex)
+ && m_menuUpdateHandlers.contains(iIndex))
+ (this->*(m_menuUpdateHandlers.value(iIndex).ptfr))();
+}
+
+void UIActionPoolRuntime::updateMenus()
+{
+ /* Clear menu list: */
+ m_mainMenus.clear();
+
+ /* 'Application' menu: */
+ addMenu(m_mainMenus, action(UIActionIndex_M_Application));
+ updateMenuApplication();
+
+ /* 'Machine' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexRT_M_Machine));
+ updateMenuMachine();
+
+ /* 'View' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexRT_M_View));
+ updateMenuView();
+ /* 'View' popup menu: */
+ addMenu(m_mainMenus, action(UIActionIndexRT_M_ViewPopup), false);
+ updateMenuViewPopup();
+
+ /* 'Input' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexRT_M_Input));
+ updateMenuInput();
+
+ /* 'Devices' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexRT_M_Devices));
+ updateMenuDevices();
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* 'Debug' menu: */
+ addMenu(m_mainMenus, action(UIActionIndexRT_M_Debug), uiCommon().isDebuggerEnabled());
+ updateMenuDebug();
+#endif
+
+#ifdef VBOX_WS_MAC
+ /* 'Window' menu: */
+ addMenu(m_mainMenus, action(UIActionIndex_M_Window));
+ updateMenuWindow();
+#endif
+
+ /* 'Help' menu: */
+ addMenu(m_mainMenus, action(UIActionIndex_Menu_Help));
+ updateMenuHelp();
+
+ /* 'Log Viewer' menu: */
+ updateMenuLogViewerWindow();
+
+ /* 'File Manager' menu: */
+ updateMenuFileManager();
+}
+
+QString UIActionPoolRuntime::shortcutsExtraDataID() const
+{
+ return GUI_Input_MachineShortcuts;
+}
+
+void UIActionPoolRuntime::updateShortcuts()
+{
+ /* Call to base-class: */
+ UIActionPool::updateShortcuts();
+ /* Create temporary Manager UI pool to do the same: */
+ if (!isTemporary())
+ UIActionPool::createTemporary(UIActionPoolType_Manager);
+}
+
+void UIActionPoolRuntime::sltHandleConfigurationChange(const QUuid &uMachineID)
+{
+ /* Skip unrelated machine IDs: */
+ if (uiCommon().managedVMUuid() != uMachineID)
+ return;
+
+ /* Update configuration: */
+ updateConfiguration();
+}
+
+void UIActionPoolRuntime::sltPrepareMenuViewScreen()
+{
+ /* Make sure sender is valid: */
+ QMenu *pMenu = qobject_cast<QMenu*>(sender());
+ AssertPtrReturnVoid(pMenu);
+
+ /* Do we have to show resize, remap or rescale actions? */
+ const bool fAllowToShowActionResize = isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize);
+ const bool fAllowToShowActionRemap = isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Remap);
+ const bool fAllowToShowActionRescale = isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Rescale);
+
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* Resize actions: */
+ if (fAllowToShowActionResize)
+ {
+ updateMenuViewResize(pMenu);
+ fSeparator = true;
+ }
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* Remap actions: */
+ if (fAllowToShowActionRemap && (m_cHostScreens > 1 || m_cGuestScreens > 1))
+ {
+ updateMenuViewRemap(pMenu);
+ fSeparator = true;
+ }
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* Rescale actions: */
+ if (fAllowToShowActionRescale)
+ {
+ updateMenuViewRescale(pMenu);
+ fSeparator = true;
+ }
+}
+
+void UIActionPoolRuntime::sltHandleActionTriggerViewScreenToggle()
+{
+ /* Make sure sender is valid: */
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertPtrReturnVoid(pAction);
+
+ /* Send request to enable/disable guest-screen: */
+ const int iGuestScreenIndex = pAction->property("Guest Screen Index").toInt();
+ const bool fScreenEnabled = pAction->isChecked();
+ emit sigNotifyAboutTriggeringViewScreenToggle(iGuestScreenIndex, fScreenEnabled);
+}
+
+void UIActionPoolRuntime::sltHandleActionTriggerViewScreenResize(QAction *pAction)
+{
+ /* Make sure sender is valid: */
+ AssertPtrReturnVoid(pAction);
+
+ /* Send request to resize guest-screen to required size: */
+ const int iGuestScreenIndex = pAction->property("Guest Screen Index").toInt();
+ const QSize size = pAction->property("Requested Size").toSize();
+ emit sigNotifyAboutTriggeringViewScreenResize(iGuestScreenIndex, size);
+}
+
+void UIActionPoolRuntime::sltHandleActionTriggerViewScreenRemap(QAction *pAction)
+{
+ /* Make sure sender is valid: */
+ AssertPtrReturnVoid(pAction);
+
+ /* Send request to remap guest-screen to required host-screen: */
+ const int iGuestScreenIndex = pAction->property("Guest Screen Index").toInt();
+ const int iHostScreenIndex = pAction->property("Host Screen Index").toInt();
+ emit sigNotifyAboutTriggeringViewScreenRemap(iGuestScreenIndex, iHostScreenIndex);
+}
+
+void UIActionPoolRuntime::sltHandleActionTriggerViewScreenRescale(QAction *pAction)
+{
+ /* Make sure sender is valid: */
+ AssertPtrReturnVoid(pAction);
+
+ /* Change scale-factor directly: */
+ const double dScaleFactor = pAction->property("Requested Scale Factor").toDouble();
+ const int iGuestScreenIndex = pAction->property("Guest Screen Index").toInt();
+ gEDataManager->setScaleFactor(dScaleFactor, uiCommon().managedVMUuid(), iGuestScreenIndex);
+}
+
+void UIActionPoolRuntime::updateMenuMachine()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Machine)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'Settings Dialog' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_Settings)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Take Snapshot' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_TakeSnapshot)) || fSeparator;
+ /* 'Information Dialog' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_ShowInformation)) || fSeparator;
+ /* 'File Manager' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_ShowFileManager)) || fSeparator;
+ /* 'Log Dialog' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_ShowLogDialog)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Pause' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_T_Pause)) || fSeparator;
+ /* 'Reset' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_Reset)) || fSeparator;
+ /* 'Detach' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_Detach)) || fSeparator;
+ /* 'SaveState' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_SaveState)) || fSeparator;
+ /* 'Shutdown' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_Shutdown)) || fSeparator;
+ /* 'PowerOff' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Machine_S_PowerOff)) || fSeparator;
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_Machine);
+}
+
+void UIActionPoolRuntime::updateMenuView()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_View)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'Fullscreen' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_T_Fullscreen)) || fSeparator;
+ /* 'Seamless' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_T_Seamless)) || fSeparator;
+ /* 'Scale' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_T_Scale)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Adjust Window' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_S_AdjustWindow)) || fSeparator;
+ /* 'Guest Autoresize' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_T_GuestAutoresize)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Take Screenshot' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_S_TakeScreenshot)) || fSeparator;
+ /* 'Recording' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_M_Recording), false) || fSeparator;
+ updateMenuViewRecording();
+ /* 'Recording Start' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_M_Recording_T_Start)) || fSeparator;
+ /* 'VRDE Server' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_T_VRDEServer)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Menu Bar' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_M_MenuBar)) || fSeparator;
+ updateMenuViewMenuBar();
+ /* 'Status Bar' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_M_StatusBar)) || fSeparator;
+ updateMenuViewStatusBar();
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* Do we have to show resize, remap or rescale actions? */
+ const bool fAllowToShowActionResize = isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize);
+ const bool fAllowToShowActionRemap = isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Remap);
+ const bool fAllowToShowActionRescale = isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Rescale);
+ if (fAllowToShowActionResize || fAllowToShowActionRemap || fAllowToShowActionRescale)
+ {
+ for (int iGuestScreenIndex = 0; iGuestScreenIndex < m_cGuestScreens; ++iGuestScreenIndex)
+ {
+ /* Add 'Virtual Screen %1' menu: */
+ QMenu *pSubMenu = pMenu->addMenu(UIIconPool::iconSet(":/virtual_screen_16px.png",
+ ":/virtual_screen_disabled_16px.png"),
+ QApplication::translate("UIActionPool", "Virtual Screen %1").arg(iGuestScreenIndex + 1));
+ pSubMenu->setProperty("Guest Screen Index", iGuestScreenIndex);
+ connect(pSubMenu, &QMenu::aboutToShow, this, &UIActionPoolRuntime::sltPrepareMenuViewScreen);
+ }
+ }
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_View);
+}
+
+void UIActionPoolRuntime::updateMenuViewPopup()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_ViewPopup)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'Adjust Window' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_S_AdjustWindow)) || fSeparator;
+ /* 'Guest Autoresize' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_T_GuestAutoresize)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* Do we have to show resize or rescale actions? */
+ const bool fAllowToShowActionResize = isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize);
+ const bool fAllowToShowActionRescale = isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Rescale);
+ if (fAllowToShowActionResize || fAllowToShowActionRescale)
+ {
+ for (int iGuestScreenIndex = 0; iGuestScreenIndex < m_cGuestScreens; ++iGuestScreenIndex)
+ {
+ /* Add 'Virtual Screen %1' menu: */
+ QMenu *pSubMenu = pMenu->addMenu(UIIconPool::iconSet(":/virtual_screen_16px.png",
+ ":/virtual_screen_disabled_16px.png"),
+ QApplication::translate("UIActionPool", "Virtual Screen %1").arg(iGuestScreenIndex + 1));
+ pSubMenu->setProperty("Guest Screen Index", iGuestScreenIndex);
+ connect(pSubMenu, &QMenu::aboutToShow, this, &UIActionPoolRuntime::sltPrepareMenuViewScreen);
+ }
+ }
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_ViewPopup);
+}
+
+void UIActionPoolRuntime::updateMenuViewRecording()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_View_M_Recording)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'Recording Settings' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_M_Recording_S_Settings)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Start Recording' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_View_M_Recording_T_Start)) || fSeparator;
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_View_M_Recording);
+}
+
+void UIActionPoolRuntime::updateMenuViewMenuBar()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_View_M_MenuBar)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* 'Menu Bar Settings' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_View_M_MenuBar_S_Settings));
+#ifndef VBOX_WS_MAC
+ /* 'Toggle Menu Bar' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility));
+#endif
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_View_M_MenuBar);
+}
+
+void UIActionPoolRuntime::updateMenuViewStatusBar()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_View_M_StatusBar)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* 'Status Bar Settings' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_View_M_StatusBar_S_Settings));
+ /* 'Toggle Status Bar' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_View_M_StatusBar);
+}
+
+void UIActionPoolRuntime::updateMenuViewResize(QMenu *pMenu)
+{
+ AssertPtrReturnVoid(pMenu);
+
+ /* Prepare content: */
+ const QList<QSize> sizes = QList<QSize>()
+ << QSize(640, 480)
+ << QSize(800, 600)
+ << QSize(1024, 768)
+ << QSize(1152, 864)
+ << QSize(1280, 720)
+ << QSize(1280, 800)
+ << QSize(1366, 768)
+ << QSize(1440, 900)
+ << QSize(1600, 900)
+ << QSize(1680, 1050)
+ << QSize(1920, 1080)
+ << QSize(1920, 1200);
+
+ /* Get corresponding screen index and frame-buffer size: */
+ const int iGuestScreenIndex = pMenu->property("Guest Screen Index").toInt();
+ const QSize screenSize = m_mapGuestScreenSize.value(iGuestScreenIndex);
+ const bool fScreenEnabled = m_mapGuestScreenIsVisible.value(iGuestScreenIndex);
+
+ /* For non-primary screens: */
+ if (iGuestScreenIndex > 0)
+ {
+ /* Create 'toggle' action: */
+ QAction *pToggleAction = pMenu->addAction(QApplication::translate("UIActionPool", "Enable", "Virtual Screen"),
+ this, SLOT(sltHandleActionTriggerViewScreenToggle()));
+ if (pToggleAction)
+ {
+ /* Configure 'toggle' action: */
+ pToggleAction->setEnabled(m_fGuestSupportsGraphics);
+ pToggleAction->setProperty("Guest Screen Index", iGuestScreenIndex);
+ pToggleAction->setCheckable(true);
+ pToggleAction->setChecked(fScreenEnabled);
+ /* Add separator: */
+ pMenu->addSeparator();
+ }
+ }
+
+ /* Create exclusive 'resize' action-group: */
+ QActionGroup *pActionGroup = new QActionGroup(pMenu);
+ if (pActionGroup)
+ {
+ /* Configure exclusive 'resize' action-group: */
+ pActionGroup->setExclusive(true);
+
+ /* For every available size: */
+ foreach (const QSize &size, sizes)
+ {
+ /* Create exclusive 'resize' action: */
+ QAction *pAction = pActionGroup->addAction(QApplication::translate("UIActionPool", "Resize to %1x%2", "Virtual Screen")
+ .arg(size.width()).arg(size.height()));
+ if (pAction)
+ {
+ /* Configure exclusive 'resize' action: */
+ pAction->setEnabled(m_fGuestSupportsGraphics && fScreenEnabled);
+ pAction->setProperty("Guest Screen Index", iGuestScreenIndex);
+ pAction->setProperty("Requested Size", size);
+ pAction->setCheckable(true);
+ if ( screenSize.width() == size.width()
+ && screenSize.height() == size.height())
+ pAction->setChecked(true);
+ }
+ }
+
+ /* Insert group actions into menu: */
+ pMenu->addActions(pActionGroup->actions());
+ /* Install listener for exclusive action-group: */
+ connect(pActionGroup, &QActionGroup::triggered,
+ this, &UIActionPoolRuntime::sltHandleActionTriggerViewScreenResize);
+ }
+}
+
+void UIActionPoolRuntime::updateMenuViewRemap(QMenu *pMenu)
+{
+ AssertPtrReturnVoid(pMenu);
+
+ /* Get corresponding screen index: */
+ const int iGuestScreenIndex = pMenu->property("Guest Screen Index").toInt();
+ const bool fScreenEnabled = m_mapGuestScreenIsVisible.value(iGuestScreenIndex);
+
+ /* For non-primary screens: */
+ if (iGuestScreenIndex > 0)
+ {
+ /* Create 'toggle' action: */
+ QAction *pToggleAction = pMenu->addAction(QApplication::translate("UIActionPool", "Enable", "Virtual Screen"),
+ this, SLOT(sltHandleActionTriggerViewScreenToggle()));
+ if (pToggleAction)
+ {
+ /* Configure 'toggle' action: */
+ pToggleAction->setEnabled(m_fGuestSupportsGraphics);
+ pToggleAction->setProperty("Guest Screen Index", iGuestScreenIndex);
+ pToggleAction->setCheckable(true);
+ pToggleAction->setChecked(fScreenEnabled);
+ /* Add separator: */
+ pMenu->addSeparator();
+ }
+ }
+
+ /* Create exclusive 'remap' action-group: */
+ QActionGroup *pActionGroup = new QActionGroup(pMenu);
+ if (pActionGroup)
+ {
+ /* Configure exclusive 'remap' action-group: */
+ pActionGroup->setExclusive(true);
+
+ /* For every host-screen index: */
+ for (int iHostScreenIndex = 0; iHostScreenIndex < m_cHostScreens; ++iHostScreenIndex)
+ {
+ /* Create exclusive 'remap' action: */
+ QAction *pAction = pActionGroup->addAction(QApplication::translate("UIActionPool", "Use Host Screen %1")
+ .arg(iHostScreenIndex + 1));
+ if (pAction)
+ {
+ /* Configure exclusive 'remap' action: */
+ pAction->setEnabled(m_fGuestSupportsGraphics && fScreenEnabled);
+ pAction->setProperty("Guest Screen Index", iGuestScreenIndex);
+ pAction->setProperty("Host Screen Index", iHostScreenIndex);
+ pAction->setCheckable(true);
+ if ( m_mapHostScreenForGuestScreen.contains(iGuestScreenIndex)
+ && m_mapHostScreenForGuestScreen.value(iGuestScreenIndex) == iHostScreenIndex)
+ pAction->setChecked(true);
+ }
+ }
+
+ /* Insert group actions into menu: */
+ pMenu->addActions(pActionGroup->actions());
+ /* Install listener for exclusive action-group: */
+ connect(pActionGroup, &QActionGroup::triggered,
+ this, &UIActionPoolRuntime::sltHandleActionTriggerViewScreenRemap);
+ }
+}
+
+void UIActionPoolRuntime::updateMenuViewRescale(QMenu *pMenu)
+{
+ AssertPtrReturnVoid(pMenu);
+
+ /* Get corresponding screen index and scale-factor: */
+ const int iGuestScreenIndex = pMenu->property("Guest Screen Index").toInt();
+ const double dCurrentScaleFactor = gEDataManager->scaleFactor(uiCommon().managedVMUuid(), iGuestScreenIndex);
+
+ /* Create exclusive 'rescale' action-group: */
+ QActionGroup *pActionGroup = new QActionGroup(pMenu);
+ if (pActionGroup)
+ {
+ /* Configure exclusive 'rescale' action-group: */
+ pActionGroup->setExclusive(true);
+
+ /* Get device-pixel-ratio: */
+ bool fDevicePixelRatioMentioned = false;
+ const double dDevicePixelRatioActual = qMin(UIDesktopWidgetWatchdog::devicePixelRatioActual(m_mapHostScreenForGuestScreen.value(iGuestScreenIndex)),
+ 10.0 /* meh, who knows? */);
+
+ /* Calculate minimum, maximum and step: */
+ const double dMinimum = 1.0;
+ const double dMaximum = ceil(dMinimum + dDevicePixelRatioActual);
+ const double dStep = 0.25;
+
+ /* Now, iterate possible scale-factors: */
+ double dScaleFactor = dMinimum;
+ do
+ {
+ /* Create exclusive 'rescale' action: */
+ QAction *pAction = pActionGroup->addAction(QString());
+ if (pAction)
+ {
+ pAction->setProperty("Guest Screen Index", iGuestScreenIndex);
+ /* For the 'unscaled' action: */
+ if (dScaleFactor == 1.0)
+ {
+ pAction->setProperty("Requested Scale Factor", dScaleFactor);
+ if (dDevicePixelRatioActual == 1.0)
+ pAction->setText(QApplication::translate("UIActionPool", "Scale to %1%", "scale-factor")
+ .arg(dScaleFactor * 100));
+ else
+ pAction->setText(QApplication::translate("UIActionPool", "Scale to %1% (unscaled output)", "scale-factor")
+ .arg(dScaleFactor * 100));
+ }
+ /* For the 'autoscaled' action: */
+ else if ( (dScaleFactor >= dDevicePixelRatioActual)
+ && (dDevicePixelRatioActual != 1.0)
+ && !fDevicePixelRatioMentioned)
+ {
+ pAction->setProperty("Requested Scale Factor", dDevicePixelRatioActual);
+ pAction->setText(QApplication::translate("UIActionPool", "Scale to %1% (autoscaled output)", "scale-factor")
+ .arg(dDevicePixelRatioActual * 100));
+ fDevicePixelRatioMentioned = true;
+ }
+ /* For other actions: */
+ else
+ {
+ pAction->setProperty("Requested Scale Factor", dScaleFactor);
+ pAction->setText(QApplication::translate("UIActionPool", "Scale to %1%", "scale-factor")
+ .arg(dScaleFactor * 100));
+ }
+
+ /* Configure exclusive 'scale-factor' action: */
+ pAction->setCheckable(true);
+ if (dScaleFactor == dCurrentScaleFactor)
+ pAction->setChecked(true);
+ }
+
+ /* Increment scale-factor: */
+ dScaleFactor += dStep;
+ }
+ while (dScaleFactor <= dMaximum);
+
+ /* Insert group actions into menu: */
+ pMenu->addActions(pActionGroup->actions());
+ /* Install listener for exclusive action-group: */
+ connect(pActionGroup, &QActionGroup::triggered,
+ this, &UIActionPoolRuntime::sltHandleActionTriggerViewScreenRescale);
+ }
+}
+
+void UIActionPoolRuntime::updateMenuInput()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Input)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'Keyboard' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard)) || fSeparator;
+ updateMenuInputKeyboard();
+ /* 'Mouse' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Mouse), false) || fSeparator;
+ updateMenuInputMouse();
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Mouse Integration' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Mouse_T_Integration)) || fSeparator;
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_Input);
+}
+
+void UIActionPoolRuntime::updateMenuInputKeyboard()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Input_M_Keyboard)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'Keyboard Settings' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard_S_Settings)) || fSeparator;
+ /* 'Soft Keyboard' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard_S_SoftKeyboard)) || fSeparator;
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Type CAD' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeCAD)) || fSeparator;
+#ifdef VBOX_WS_X11
+ /* 'Type CABS' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeCABS)) || fSeparator;
+#endif
+ /* 'Type Ctrl-Break' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeCtrlBreak)) || fSeparator;
+ /* 'Type Insert' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeInsert)) || fSeparator;
+ /* 'Type Print Screen' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard_S_TypePrintScreen)) || fSeparator;
+ /* 'Type Alt Print Screen' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeAltPrintScreen)) || fSeparator;
+ /* 'Type Host Key Combo' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Input_M_Keyboard_T_TypeHostKeyCombo)) || fSeparator;
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_Input_M_Keyboard);
+}
+
+void UIActionPoolRuntime::updateMenuInputMouse()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Input_M_Mouse)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* 'Machine Integration' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_Input_M_Mouse_T_Integration));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_Input_M_Mouse);
+}
+
+void UIActionPoolRuntime::updateMenuDevices()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Devices)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'Hard Drives' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_HardDrives)) || fSeparator;
+ updateMenuDevicesHardDrives();
+ /* 'Optical Devices' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_OpticalDevices)) || fSeparator;
+ /* 'Floppy Devices' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_FloppyDevices)) || fSeparator;
+ /* 'Audio' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_Audio)) || fSeparator;
+ updateMenuDevicesAudio();
+ /* 'Network' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_Network)) || fSeparator;
+ updateMenuDevicesNetwork();
+ /* 'USB Devices' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_USBDevices)) || fSeparator;
+ updateMenuDevicesUSBDevices();
+ /* 'Web Cams' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_WebCams)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* 'Shared Folders' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_SharedFolders)) || fSeparator;
+ updateMenuDevicesSharedFolders();
+ /* 'Shared Clipboard' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_SharedClipboard)) || fSeparator;
+ /* 'Drag&Drop' submenu: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_DragAndDrop)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* Insert Guest Additions Disk action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_S_InsertGuestAdditionsDisk)) || fSeparator;
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions)) || fSeparator;
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_Devices);
+}
+
+void UIActionPoolRuntime::updateMenuDevicesHardDrives()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Devices_M_HardDrives)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* 'Hard Drives Settings' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_Devices_M_HardDrives_S_Settings));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_Devices_M_HardDrives);
+}
+
+void UIActionPoolRuntime::updateMenuDevicesAudio()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Devices_M_Audio)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* 'Output' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_Devices_M_Audio_T_Output));
+ /* 'Input' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_Devices_M_Audio_T_Input));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_Devices_M_Audio);
+}
+
+void UIActionPoolRuntime::updateMenuDevicesNetwork()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Devices_M_Network)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'Network Settings' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_Network_S_Settings)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolRuntime::updateMenuDevicesUSBDevices()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Devices_M_USBDevices)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Separator: */
+ bool fSeparator = false;
+
+ /* 'USB Devices Settings' action: */
+ fSeparator = addAction(pMenu, action(UIActionIndexRT_M_Devices_M_USBDevices_S_Settings)) || fSeparator;
+
+ /* Separator: */
+ if (fSeparator)
+ {
+ pMenu->addSeparator();
+ fSeparator = false;
+ }
+
+ /* This menu always remains invalid.. */
+}
+
+void UIActionPoolRuntime::updateMenuDevicesSharedFolders()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Devices_M_SharedFolders)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* 'Shared Folders Settings' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_Devices_M_SharedFolders_S_Settings));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_Devices_M_SharedFolders);
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+void UIActionPoolRuntime::updateMenuDebug()
+{
+ /* Get corresponding menu: */
+ UIMenu *pMenu = action(UIActionIndexRT_M_Debug)->menu();
+ AssertPtrReturnVoid(pMenu);
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* 'Statistics' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_Debug_S_ShowStatistics));
+ /* 'Command Line' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_Debug_S_ShowCommandLine));
+ /* 'Logging' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_Debug_T_Logging));
+ /* 'Guest Control Terminal' action: */
+ addAction(pMenu, action(UIActionIndexRT_M_Debug_S_GuestControlConsole));
+
+ /* Mark menu as valid: */
+ m_invalidations.remove(UIActionIndexRT_M_Debug);
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+
+#include "UIActionPoolRuntime.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolRuntime.h b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolRuntime.h
new file mode 100644
index 00000000..ccd04276
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIActionPoolRuntime.h
@@ -0,0 +1,337 @@
+/* $Id: UIActionPoolRuntime.h $ */
+/** @file
+ * VBox Qt GUI - UIActionPoolRuntime class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIActionPoolRuntime_h
+#define FEQT_INCLUDED_SRC_globals_UIActionPoolRuntime_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+
+/* GUI includes: */
+#include "UIActionPool.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+
+
+/** VirtualBox Runtime action-pool index enum.
+ * Naming convention is following:
+ * 1. Every menu index prepended with 'M',
+ * 2. Every simple-action index prepended with 'S',
+ * 3. Every toggle-action index presended with 'T',
+ * 5. Every sub-index contains full parent-index name. */
+enum UIActionIndexRT
+{
+ /* 'Machine' menu actions: */
+ UIActionIndexRT_M_Machine = UIActionIndex_Max + 1,
+ UIActionIndexRT_M_Machine_S_Settings,
+ UIActionIndexRT_M_Machine_S_TakeSnapshot,
+ UIActionIndexRT_M_Machine_S_ShowInformation,
+ UIActionIndexRT_M_Machine_S_ShowFileManager,
+ UIActionIndexRT_M_Machine_T_Pause,
+ UIActionIndexRT_M_Machine_S_Reset,
+ UIActionIndexRT_M_Machine_S_Detach,
+ UIActionIndexRT_M_Machine_S_SaveState,
+ UIActionIndexRT_M_Machine_S_Shutdown,
+ UIActionIndexRT_M_Machine_S_PowerOff,
+ UIActionIndexRT_M_Machine_S_ShowLogDialog,
+
+ /* 'View' menu actions: */
+ UIActionIndexRT_M_View,
+ UIActionIndexRT_M_ViewPopup,
+ UIActionIndexRT_M_View_T_Fullscreen,
+ UIActionIndexRT_M_View_T_Seamless,
+ UIActionIndexRT_M_View_T_Scale,
+#ifndef VBOX_WS_MAC
+ UIActionIndexRT_M_View_S_MinimizeWindow,
+#endif
+ UIActionIndexRT_M_View_S_AdjustWindow,
+ UIActionIndexRT_M_View_T_GuestAutoresize,
+ UIActionIndexRT_M_View_S_TakeScreenshot,
+ UIActionIndexRT_M_View_M_Recording,
+ UIActionIndexRT_M_View_M_Recording_S_Settings,
+ UIActionIndexRT_M_View_M_Recording_T_Start,
+ UIActionIndexRT_M_View_T_VRDEServer,
+ UIActionIndexRT_M_View_M_MenuBar,
+ UIActionIndexRT_M_View_M_MenuBar_S_Settings,
+#ifndef VBOX_WS_MAC
+ UIActionIndexRT_M_View_M_MenuBar_T_Visibility,
+#endif
+ UIActionIndexRT_M_View_M_StatusBar,
+ UIActionIndexRT_M_View_M_StatusBar_S_Settings,
+ UIActionIndexRT_M_View_M_StatusBar_T_Visibility,
+
+ /* 'Input' menu actions: */
+ UIActionIndexRT_M_Input,
+ UIActionIndexRT_M_Input_M_Keyboard,
+ UIActionIndexRT_M_Input_M_Keyboard_S_Settings,
+ UIActionIndexRT_M_Input_M_Keyboard_S_SoftKeyboard,
+ UIActionIndexRT_M_Input_M_Keyboard_S_TypeCAD,
+#ifdef VBOX_WS_X11
+ UIActionIndexRT_M_Input_M_Keyboard_S_TypeCABS,
+#endif
+ UIActionIndexRT_M_Input_M_Keyboard_S_TypeCtrlBreak,
+ UIActionIndexRT_M_Input_M_Keyboard_S_TypeInsert,
+ UIActionIndexRT_M_Input_M_Keyboard_S_TypePrintScreen,
+ UIActionIndexRT_M_Input_M_Keyboard_S_TypeAltPrintScreen,
+ UIActionIndexRT_M_Input_M_Keyboard_T_TypeHostKeyCombo,
+ UIActionIndexRT_M_Input_M_Mouse,
+ UIActionIndexRT_M_Input_M_Mouse_T_Integration,
+
+ /* 'Devices' menu actions: */
+ UIActionIndexRT_M_Devices,
+ UIActionIndexRT_M_Devices_M_HardDrives,
+ UIActionIndexRT_M_Devices_M_HardDrives_S_Settings,
+ UIActionIndexRT_M_Devices_M_OpticalDevices,
+ UIActionIndexRT_M_Devices_M_FloppyDevices,
+ UIActionIndexRT_M_Devices_M_Audio,
+ UIActionIndexRT_M_Devices_M_Audio_T_Output,
+ UIActionIndexRT_M_Devices_M_Audio_T_Input,
+ UIActionIndexRT_M_Devices_M_Network,
+ UIActionIndexRT_M_Devices_M_Network_S_Settings,
+ UIActionIndexRT_M_Devices_M_USBDevices,
+ UIActionIndexRT_M_Devices_M_USBDevices_S_Settings,
+ UIActionIndexRT_M_Devices_M_WebCams,
+ UIActionIndexRT_M_Devices_M_SharedClipboard,
+ UIActionIndexRT_M_Devices_M_DragAndDrop,
+ UIActionIndexRT_M_Devices_M_SharedFolders,
+ UIActionIndexRT_M_Devices_M_SharedFolders_S_Settings,
+ UIActionIndexRT_M_Devices_S_InsertGuestAdditionsDisk,
+ UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions,
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* 'Debugger' menu actions: */
+ UIActionIndexRT_M_Debug,
+ UIActionIndexRT_M_Debug_S_ShowStatistics,
+ UIActionIndexRT_M_Debug_S_ShowCommandLine,
+ UIActionIndexRT_M_Debug_T_Logging,
+ UIActionIndexRT_M_Debug_S_GuestControlConsole,
+#endif
+
+#ifdef VBOX_WS_MAC
+ /* 'Dock' menu actions: */
+ UIActionIndexRT_M_Dock,
+ UIActionIndexRT_M_Dock_M_DockSettings,
+ UIActionIndexRT_M_Dock_M_DockSettings_T_PreviewMonitor,
+ UIActionIndexRT_M_Dock_M_DockSettings_T_DisableMonitor,
+ UIActionIndexRT_M_Dock_M_DockSettings_T_DisableOverlay,
+#endif
+
+ /* Maximum index: */
+ UIActionIndexRT_Max
+};
+
+
+/** UIActionPool extension
+ * representing action-pool singleton for Runtime UI. */
+class SHARED_LIBRARY_STUFF UIActionPoolRuntime : public UIActionPool
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about 'View' : 'Virtual Screen #' menu : 'Toggle' action trigger. */
+ void sigNotifyAboutTriggeringViewScreenToggle(int iGuestScreenIndex, bool fEnabled);
+ /** Notifies about 'View' : 'Virtual Screen #' menu : 'Resize' action trigger. */
+ void sigNotifyAboutTriggeringViewScreenResize(int iGuestScreenIndex, const QSize &size);
+ /** Notifies about 'View' : 'Virtual Screen #' menu : 'Remap' action trigger. */
+ void sigNotifyAboutTriggeringViewScreenRemap(int iGuestScreenIndex, int iHostScreenIndex);
+
+public:
+
+ /** Defines host-screen @a cCount. */
+ void setHostScreenCount(int cCount);
+ /** Defines guest-screen @a cCount. */
+ void setGuestScreenCount(int cCount);
+
+ /** Defines @a iGuestScreen @a size. */
+ void setGuestScreenSize(int iGuestScreen, const QSize &size);
+ /** Defines whether @a iGuestScreen is @a fVisible. */
+ void setGuestScreenVisible(int iGuestScreen, bool fVisible);
+
+ /** Defines whether guest supports graphics. */
+ void setGuestSupportsGraphics(bool fSupports);
+
+ /** Defines host-to-guest mapping @a scheme. */
+ void setHostScreenForGuestScreenMap(const QMap<int, int> &scheme);
+ /** Returns host-to-guest mapping scheme. */
+ QMap<int, int> hostScreenForGuestScreenMap() const;
+
+ /** Returns whether the action with passed @a type is allowed in the 'Machine' menu. */
+ bool isAllowedInMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType type) const;
+ /** Defines 'Machine' menu @a restriction for passed @a level. */
+ void setRestrictionForMenuMachine(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuMachineActionType restriction);
+
+ /** Returns whether the action with passed @a type is allowed in the 'View' menu. */
+ bool isAllowedInMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType type) const;
+ /** Defines 'View' menu @a restriction for passed @a level. */
+ void setRestrictionForMenuView(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType restriction);
+
+ /** Returns whether the action with passed @a type is allowed in the 'Input' menu. */
+ bool isAllowedInMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType type) const;
+ /** Defines 'Input' menu @a restriction for passed @a level. */
+ void setRestrictionForMenuInput(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuInputActionType restriction);
+
+ /** Returns whether the action with passed @a type is allowed in the 'Devices' menu. */
+ bool isAllowedInMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType type) const;
+ /** Defines 'Devices' menu @a restriction for passed @a level. */
+ void setRestrictionForMenuDevices(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuDevicesActionType restriction);
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Returns whether the action with passed @a type is allowed in the 'Debug' menu. */
+ bool isAllowedInMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType type) const;
+ /** Defines 'Debug' menu @a restriction for passed @a level. */
+ void setRestrictionForMenuDebugger(UIActionRestrictionLevel level,
+ UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType restriction);
+#endif
+
+protected:
+
+ /** Constructs action-pool.
+ * @param fTemporary Brings whether this action-pool is temporary,
+ * used to (re-)initialize shortcuts-pool. */
+ UIActionPoolRuntime(bool fTemporary = false);
+
+ /** Prepares pool. */
+ virtual void preparePool() RT_OVERRIDE;
+ /** Prepares connections. */
+ virtual void prepareConnections() RT_OVERRIDE;
+
+ /** Updates configuration. */
+ virtual void updateConfiguration() RT_OVERRIDE;
+
+ /** Updates menu. */
+ virtual void updateMenu(int iIndex) RT_OVERRIDE;
+ /** Updates menus. */
+ virtual void updateMenus() RT_OVERRIDE;
+
+ /** Returns extra-data ID to save keyboard shortcuts under. */
+ virtual QString shortcutsExtraDataID() const RT_OVERRIDE;
+ /** Updates shortcuts. */
+ virtual void updateShortcuts() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles configuration-change. */
+ void sltHandleConfigurationChange(const QUuid &uMachineID);
+
+ /** Prepares 'View' : 'Virtual Screen #' menu (Normal, Scale). */
+ void sltPrepareMenuViewScreen();
+
+ /** Handles 'View' : 'Virtual Screen #' menu : 'Toggle' action trigger. */
+ void sltHandleActionTriggerViewScreenToggle();
+ /** Handles 'View' : 'Virtual Screen #' menu : 'Resize' @a pAction trigger. */
+ void sltHandleActionTriggerViewScreenResize(QAction *pAction);
+ /** Handles 'View' : 'Virtual Screen #' menu : 'Remap' @a pAction trigger. */
+ void sltHandleActionTriggerViewScreenRemap(QAction *pAction);
+ /** Handles 'View' : 'Virtual Screen #' menu : 'Rescale' @a pAction trigger. */
+ void sltHandleActionTriggerViewScreenRescale(QAction *pAction);
+
+private:
+
+ /** Updates 'Machine' menu. */
+ void updateMenuMachine();
+ /** Updates 'View' menu. */
+ void updateMenuView();
+ /** Updates 'View' : 'Popup' menu. */
+ void updateMenuViewPopup();
+ /** Updates 'View' : 'Recording' menu. */
+ void updateMenuViewRecording();
+ /** Updates 'View' : 'Menu Bar' menu. */
+ void updateMenuViewMenuBar();
+ /** Updates 'View' : 'Status Bar' menu. */
+ void updateMenuViewStatusBar();
+ /** Updates 'View' : 'Virtual Screen #' @a pMenu with "Resize to <Width> x <Height>" actions. */
+ void updateMenuViewResize(QMenu *pMenu);
+ /** Updates 'View' : 'Virtual Screen #' @a pMenu with "Use Host Screen <Number>" actions. */
+ void updateMenuViewRemap(QMenu *pMenu);
+ /** Updates 'View' : 'Virtual Screen #' @a pMenu with "Scale to <Scale>" actions. */
+ void updateMenuViewRescale(QMenu *pMenu);
+ /** Updates 'Input' menu. */
+ void updateMenuInput();
+ /** Updates 'Input' : 'Keyboard' menu. */
+ void updateMenuInputKeyboard();
+ /** Updates 'Input' : 'Mouse' menu. */
+ void updateMenuInputMouse();
+ /** Updates 'Devices' menu. */
+ void updateMenuDevices();
+ /** Updates 'Devices' : 'Hard Drives' menu. */
+ void updateMenuDevicesHardDrives();
+ /** Updates 'Devices' : 'Audio' menu. */
+ void updateMenuDevicesAudio();
+ /** Updates 'Devices' : 'Network' menu. */
+ void updateMenuDevicesNetwork();
+ /** Updates 'Devices' : 'USB' menu. */
+ void updateMenuDevicesUSBDevices();
+ /** Updates 'Devices' : 'Shared Folders' menu. */
+ void updateMenuDevicesSharedFolders();
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Updates 'Debug' menu. */
+ void updateMenuDebug();
+#endif
+
+ /** Holds the host-screen count. */
+ int m_cHostScreens;
+ /** Holds the guest-screen count. */
+ int m_cGuestScreens;
+
+ /** Holds the map of guest-screen sizes. */
+ QMap<int, QSize> m_mapGuestScreenSize;
+ /** Holds the map of guest-screen visibility states. */
+ QMap<int, bool> m_mapGuestScreenIsVisible;
+
+ /** Holds whether guest supports graphics. */
+ bool m_fGuestSupportsGraphics;
+
+ /** Holds the host-to-guest mapping scheme. */
+ QMap<int, int> m_mapHostScreenForGuestScreen;
+
+ /** Holds restricted action types of the Machine menu. */
+ QMap<UIActionRestrictionLevel, UIExtraDataMetaDefs::RuntimeMenuMachineActionType> m_restrictedActionsMenuMachine;
+ /** Holds restricted action types of the View menu. */
+ QMap<UIActionRestrictionLevel, UIExtraDataMetaDefs::RuntimeMenuViewActionType> m_restrictedActionsMenuView;
+ /** Holds restricted action types of the Input menu. */
+ QMap<UIActionRestrictionLevel, UIExtraDataMetaDefs::RuntimeMenuInputActionType> m_restrictedActionsMenuInput;
+ /** Holds restricted action types of the Devices menu. */
+ QMap<UIActionRestrictionLevel, UIExtraDataMetaDefs::RuntimeMenuDevicesActionType> m_restrictedActionsMenuDevices;
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Holds restricted action types of the Debugger menu. */
+ QMap<UIActionRestrictionLevel, UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType> m_restrictedActionsMenuDebug;
+#endif
+
+ /** Enables factory in base-class. */
+ friend class UIActionPool;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIActionPoolRuntime_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIAnimationFramework.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIAnimationFramework.cpp
new file mode 100644
index 00000000..d72d6b23
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIAnimationFramework.cpp
@@ -0,0 +1,185 @@
+/* $Id: UIAnimationFramework.cpp $ */
+/** @file
+ * VBox Qt GUI - UIAnimationFramework class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPropertyAnimation>
+#include <QSignalTransition>
+#include <QStateMachine>
+#include <QWidget>
+
+/* GUI includes: */
+#include "UIAnimationFramework.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/*********************************************************************************************************************************
+* Class UIAnimation implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIAnimation* UIAnimation::installPropertyAnimation(QWidget *pTarget, const char *pszPropertyName,
+ const char *pszValuePropertyNameStart, const char *pszValuePropertyNameFinal,
+ const char *pszSignalForward, const char *pszSignalReverse,
+ bool fReverse /* = false */, int iAnimationDuration /* = 300 */)
+{
+ /* Return newly created animation-machine: */
+ return new UIAnimation(pTarget, pszPropertyName,
+ pszValuePropertyNameStart, pszValuePropertyNameFinal,
+ pszSignalForward, pszSignalReverse,
+ fReverse, iAnimationDuration);
+}
+
+void UIAnimation::update()
+{
+ /* Update 'forward' animation: */
+ m_pForwardAnimation->setStartValue(parent()->property(m_pszValuePropertyNameStart));
+ m_pForwardAnimation->setEndValue(parent()->property(m_pszValuePropertyNameFinal));
+ m_pStateStart->assignProperty(parent(), m_pszPropertyName, parent()->property(m_pszValuePropertyNameStart));
+ /* Update 'reverse' animation: */
+ m_pReverseAnimation->setStartValue(parent()->property(m_pszValuePropertyNameFinal));
+ m_pReverseAnimation->setEndValue(parent()->property(m_pszValuePropertyNameStart));
+ m_pStateFinal->assignProperty(parent(), m_pszPropertyName, parent()->property(m_pszValuePropertyNameFinal));
+}
+
+UIAnimation::UIAnimation(QWidget *pParent, const char *pszPropertyName,
+ const char *pszValuePropertyNameStart, const char *pszValuePropertyNameFinal,
+ const char *pszSignalForward, const char *pszSignalReverse,
+ bool fReverse, int iAnimationDuration)
+ : QObject(pParent)
+ , m_pszPropertyName(pszPropertyName)
+ , m_pszValuePropertyNameStart(pszValuePropertyNameStart), m_pszValuePropertyNameFinal(pszValuePropertyNameFinal)
+ , m_pszSignalForward(pszSignalForward), m_pszSignalReverse(pszSignalReverse)
+ , m_fReverse(fReverse), m_iAnimationDuration(iAnimationDuration)
+ , m_pAnimationMachine(0), m_pForwardAnimation(0), m_pReverseAnimation(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIAnimation::prepare()
+{
+ /* Make sure parent asigned: */
+ AssertPtrReturnVoid(parent());
+
+ /* Prepare animation-machine: */
+ m_pAnimationMachine = new QStateMachine(this);
+ /* Create 'start' state: */
+ m_pStateStart = new QState(m_pAnimationMachine);
+ m_pStateStart->assignProperty(parent(), "AnimationState", QString("Start"));
+ connect(m_pStateStart, &QState::propertiesAssigned, this, &UIAnimation::sigStateEnteredStart);
+ /* Create 'final' state: */
+ m_pStateFinal = new QState(m_pAnimationMachine);
+ m_pStateFinal->assignProperty(parent(), "AnimationState", QString("Final"));
+ connect(m_pStateFinal, &QState::propertiesAssigned, this, &UIAnimation::sigStateEnteredFinal);
+
+ /* Prepare 'forward' animation: */
+ m_pForwardAnimation = new QPropertyAnimation(parent(), m_pszPropertyName, m_pAnimationMachine);
+ m_pForwardAnimation->setEasingCurve(QEasingCurve(QEasingCurve::InOutCubic));
+ m_pForwardAnimation->setDuration(m_iAnimationDuration);
+ /* Prepare 'reverse' animation: */
+ m_pReverseAnimation = new QPropertyAnimation(parent(), m_pszPropertyName, m_pAnimationMachine);
+ m_pReverseAnimation->setEasingCurve(QEasingCurve(QEasingCurve::InOutCubic));
+ m_pReverseAnimation->setDuration(m_iAnimationDuration);
+
+ /* Prepare state-transitions: */
+ QSignalTransition *pStartToFinal = m_pStateStart->addTransition(parent(), m_pszSignalForward, m_pStateFinal);
+ AssertPtrReturnVoid(pStartToFinal);
+ pStartToFinal->addAnimation(m_pForwardAnimation);
+ QSignalTransition *pFinalToStart = m_pStateFinal->addTransition(parent(), m_pszSignalReverse, m_pStateStart);
+ AssertPtrReturnVoid(pFinalToStart);
+ pFinalToStart->addAnimation(m_pReverseAnimation);
+
+ /* Fetch animation-borders: */
+ update();
+
+ /* Choose initial state: */
+ m_pAnimationMachine->setInitialState(!m_fReverse ? m_pStateStart : m_pStateFinal);
+ /* Start animation-machine: */
+ m_pAnimationMachine->start();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIAnimation implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIAnimationLoop* UIAnimationLoop::installAnimationLoop(QWidget *pTarget, const char *pszPropertyName,
+ const char *pszValuePropertyNameStart, const char *pszValuePropertyNameFinal,
+ int iAnimationDuration /* = 300*/)
+{
+ /* Return newly created animation-loop: */
+ return new UIAnimationLoop(pTarget, pszPropertyName,
+ pszValuePropertyNameStart, pszValuePropertyNameFinal,
+ iAnimationDuration);
+}
+
+void UIAnimationLoop::update()
+{
+ /* Update animation: */
+ m_pAnimation->setStartValue(parent()->property(m_pszValuePropertyNameStart));
+ m_pAnimation->setEndValue(parent()->property(m_pszValuePropertyNameFinal));
+}
+
+void UIAnimationLoop::start()
+{
+ /* Start animation: */
+ m_pAnimation->start();
+}
+
+void UIAnimationLoop::stop()
+{
+ /* Stop animation: */
+ m_pAnimation->stop();
+}
+
+UIAnimationLoop::UIAnimationLoop(QWidget *pParent, const char *pszPropertyName,
+ const char *pszValuePropertyNameStart, const char *pszValuePropertyNameFinal,
+ int iAnimationDuration)
+ : QObject(pParent)
+ , m_pszPropertyName(pszPropertyName)
+ , m_pszValuePropertyNameStart(pszValuePropertyNameStart), m_pszValuePropertyNameFinal(pszValuePropertyNameFinal)
+ , m_iAnimationDuration(iAnimationDuration)
+ , m_pAnimation(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIAnimationLoop::prepare()
+{
+ /* Prepare loop: */
+ m_pAnimation = new QPropertyAnimation(parent(), m_pszPropertyName, this);
+ m_pAnimation->setDuration(m_iAnimationDuration);
+ m_pAnimation->setLoopCount(-1);
+
+ /* Fetch animation-borders: */
+ update();
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIAnimationFramework.h b/src/VBox/Frontends/VirtualBox/src/globals/UIAnimationFramework.h
new file mode 100644
index 00000000..b4dd7ed6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIAnimationFramework.h
@@ -0,0 +1,185 @@
+/* $Id: UIAnimationFramework.h $ */
+/** @file
+ * VBox Qt GUI - UIAnimationFramework class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIAnimationFramework_h
+#define FEQT_INCLUDED_SRC_globals_UIAnimationFramework_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declaration: */
+class QPropertyAnimation;
+class QState;
+class QStateMachine;
+
+
+/** QObject subclass used as animation factory. */
+class SHARED_LIBRARY_STUFF UIAnimation : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listener about 'Start' state entered. */
+ void sigStateEnteredStart();
+ /** Notifies listener about 'Final' state entered. */
+ void sigStateEnteredFinal();
+
+public:
+
+ /** Installs property animation.
+ * @param pTarget Brings the object being animated.
+ * @param pszPropertyName Brings the name of property being animated.
+ * @param pszValuePropertyNameStart Brings the name of the property holding 'start' value.
+ * @param pszValuePropertyNameFinal Brings the name of the property holding 'final' value.
+ * @param pszSignalForward Brings the signal to start forward animation.
+ * @param pszSignalReverse Brings the signal to start reverse animation.
+ * @param fReverse Brings whether the animation should be inverted.
+ * @param iAnimationDuration Brings the animation duration. */
+ static UIAnimation *installPropertyAnimation(QWidget *pTarget, const char *pszPropertyName,
+ const char *pszValuePropertyNameStart, const char *pszValuePropertyNameFinal,
+ const char *pszSignalForward, const char *pszSignalReverse,
+ bool fReverse = false, int iAnimationDuration = 300);
+
+ /** Updates the animation, fetching new initial values. */
+ void update();
+
+private:
+
+ /** Constructs animation. Doesn't mean to be used directly.
+ * @param pParent Brings the object animation being applied to.
+ * @param pszPropertyName Brings the name of property being animated.
+ * @param pszValuePropertyNameStart Brings the name of the property holding 'start' value.
+ * @param pszValuePropertyNameFinal Brings the name of the property holding 'final' value.
+ * @param pszSignalForward Brings the signal to start forward animation.
+ * @param pszSignalReverse Brings the signal to start reverse animation.
+ * @param fReverse Brings whether the animation should be inverted.
+ * @param iAnimationDuration Brings the animation duration. */
+ UIAnimation(QWidget *pParent, const char *pszPropertyName,
+ const char *pszValuePropertyNameStart, const char *pszValuePropertyNameFinal,
+ const char *pszSignalForward, const char *pszSignalReverse,
+ bool fReverse, int iAnimationDuration);
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the name of property being animated. */
+ const char *m_pszPropertyName;
+
+ /** Holds the name of the property holding 'start' value. */
+ const char *m_pszValuePropertyNameStart;
+ /** Holds the name of the property holding 'final' value. */
+ const char *m_pszValuePropertyNameFinal;
+
+ /** Holds the signal to start forward animation. */
+ const char *m_pszSignalForward;
+ /** Holds the signal to start reverse animation. */
+ const char *m_pszSignalReverse;
+
+ /** Holds whether the animation should be inverted. */
+ bool m_fReverse;
+
+ /** Holds the animation duration. */
+ int m_iAnimationDuration;
+
+ /** Holds the animation machine instance. */
+ QStateMachine *m_pAnimationMachine;
+ /** Holds the instance of the animation 'Start' state. */
+ QState *m_pStateStart;
+ /** Holds the instance of the animation 'Final' state. */
+ QState *m_pStateFinal;
+ /** Holds the instance of the 'Forward' animation. */
+ QPropertyAnimation *m_pForwardAnimation;
+ /** Holds the instance of the 'Reverse' animation. */
+ QPropertyAnimation *m_pReverseAnimation;
+};
+
+
+/** QObject subclass used as animation loop factory. */
+class SHARED_LIBRARY_STUFF UIAnimationLoop : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Installs property animation.
+ * @param pTarget Brings the object being animated.
+ * @param pszPropertyName Brings the name of property being animated.
+ * @param pszValuePropertyNameStart Brings the name of the property holding 'start' value.
+ * @param pszValuePropertyNameFinal Brings the name of the property holding 'final' value.
+ * @param iAnimationDuration Brings the animation duration. */
+ static UIAnimationLoop *installAnimationLoop(QWidget *pTarget, const char *pszPropertyName,
+ const char *pszValuePropertyNameStart, const char *pszValuePropertyNameFinal,
+ int iAnimationDuration = 300);
+
+ /** Updates the animation, fetching new initial values. */
+ void update();
+
+ /** Starts the loop. */
+ void start();
+ /** Stops the loop. */
+ void stop();
+
+private:
+
+ /** Constructs animation loop. Doesn't mean to be used directly.
+ * @param pParent Brings the object animation being applied to.
+ * @param pszPropertyName Brings the name of property being animated.
+ * @param pszValuePropertyNameStart Brings the name of the property holding 'start' value.
+ * @param pszValuePropertyNameFinal Brings the name of the property holding 'final' value.
+ * @param iAnimationDuration Brings the animation duration. */
+ UIAnimationLoop(QWidget *pParent, const char *pszPropertyName,
+ const char *pszValuePropertyNameStart, const char *pszValuePropertyNameFinal,
+ int iAnimationDuration);
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the name of property being animated. */
+ const char *m_pszPropertyName;
+
+ /** Holds the name of the property holding 'start' value. */
+ const char *m_pszValuePropertyNameStart;
+ /** Holds the name of the property holding 'final' value. */
+ const char *m_pszValuePropertyNameFinal;
+
+ /** Holds the animation duration. */
+ int m_iAnimationDuration;
+
+ /** Holds the instance of the animation. */
+ QPropertyAnimation *m_pAnimation;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIAnimationFramework_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UICloudNetworkingStuff.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UICloudNetworkingStuff.cpp
new file mode 100644
index 00000000..6749efce
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UICloudNetworkingStuff.cpp
@@ -0,0 +1,604 @@
+/* $Id: UICloudNetworkingStuff.cpp $ */
+/** @file
+ * VBox Qt GUI - UICloudNetworkingStuff namespace implementation.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICloudNetworkingStuff.h"
+#include "UICommon.h"
+#include "UIErrorString.h"
+#include "UIMessageCenter.h"
+
+/* COM includes: */
+#include "CAppliance.h"
+#include "CForm.h"
+#include "CProgress.h"
+#include "CStringArray.h"
+#include "CVirtualBox.h"
+#include "CVirtualBoxErrorInfo.h"
+#include "CVirtualSystemDescription.h"
+
+
+CCloudProviderManager UICloudNetworkingStuff::cloudProviderManager(UINotificationCenter *pParent /* = 0 */)
+{
+ /* Acquire VBox: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+ if (comVBox.isNotNull())
+ {
+ /* Acquire cloud provider manager: */
+ CCloudProviderManager comProviderManager = comVBox.GetCloudProviderManager();
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotAcquireVirtualBoxParameter(comVBox, pParent);
+ else
+ return comProviderManager;
+ }
+ /* Null by default: */
+ return CCloudProviderManager();
+}
+
+CCloudProviderManager UICloudNetworkingStuff::cloudProviderManager(QString &strErrorMessage)
+{
+ /* Acquire VBox: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+ if (comVBox.isNotNull())
+ {
+ /* Acquire cloud provider manager: */
+ CCloudProviderManager comProviderManager = comVBox.GetCloudProviderManager();
+ if (!comVBox.isOk())
+ strErrorMessage = UIErrorString::formatErrorInfo(comVBox);
+ else
+ return comProviderManager;
+ }
+ /* Null by default: */
+ return CCloudProviderManager();
+}
+
+CCloudProvider UICloudNetworkingStuff::cloudProviderByShortName(const QString &strProviderShortName,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ /* Acquire cloud provider manager: */
+ CCloudProviderManager comProviderManager = cloudProviderManager(pParent);
+ if (comProviderManager.isNotNull())
+ {
+ /* Acquire cloud provider: */
+ CCloudProvider comProvider = comProviderManager.GetProviderByShortName(strProviderShortName);
+ if (!comProviderManager.isOk())
+ UINotificationMessage::cannotAcquireCloudProviderManagerParameter(comProviderManager, pParent);
+ else
+ return comProvider;
+ }
+ /* Null by default: */
+ return CCloudProvider();
+}
+
+CCloudProvider UICloudNetworkingStuff::cloudProviderByShortName(const QString &strProviderShortName,
+ QString &strErrorMessage)
+{
+ /* Acquire cloud provider manager: */
+ CCloudProviderManager comProviderManager = cloudProviderManager(strErrorMessage);
+ if (comProviderManager.isNotNull())
+ {
+ /* Acquire cloud provider: */
+ CCloudProvider comProvider = comProviderManager.GetProviderByShortName(strProviderShortName);
+ if (!comProviderManager.isOk())
+ strErrorMessage = UIErrorString::formatErrorInfo(comProviderManager);
+ else
+ return comProvider;
+ }
+ /* Null by default: */
+ return CCloudProvider();
+}
+
+CCloudProfile UICloudNetworkingStuff::cloudProfileByName(const QString &strProviderShortName,
+ const QString &strProfileName,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ /* Acquire cloud provider: */
+ CCloudProvider comProvider = cloudProviderByShortName(strProviderShortName, pParent);
+ if (comProvider.isNotNull())
+ {
+ /* Acquire cloud profile: */
+ CCloudProfile comProfile = comProvider.GetProfileByName(strProfileName);
+ if (!comProvider.isOk())
+ UINotificationMessage::cannotAcquireCloudProviderParameter(comProvider, pParent);
+ else
+ return comProfile;
+ }
+ /* Null by default: */
+ return CCloudProfile();
+}
+
+CCloudProfile UICloudNetworkingStuff::cloudProfileByName(const QString &strProviderShortName,
+ const QString &strProfileName,
+ QString &strErrorMessage)
+{
+ /* Acquire cloud provider: */
+ CCloudProvider comProvider = cloudProviderByShortName(strProviderShortName, strErrorMessage);
+ if (comProvider.isNotNull())
+ {
+ /* Acquire cloud profile: */
+ CCloudProfile comProfile = comProvider.GetProfileByName(strProfileName);
+ if (!comProvider.isOk())
+ strErrorMessage = UIErrorString::formatErrorInfo(comProvider);
+ else
+ return comProfile;
+ }
+ /* Null by default: */
+ return CCloudProfile();
+}
+
+CCloudClient UICloudNetworkingStuff::cloudClient(CCloudProfile comProfile,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ /* Create cloud client: */
+ CCloudClient comClient = comProfile.CreateCloudClient();
+ if (!comProfile.isOk())
+ UINotificationMessage::cannotCreateCloudClient(comProfile, pParent);
+ else
+ return comClient;
+ /* Null by default: */
+ return CCloudClient();
+}
+
+CCloudClient UICloudNetworkingStuff::cloudClient(CCloudProfile comProfile,
+ QString &strErrorMessage)
+{
+ /* Create cloud client: */
+ CCloudClient comClient = comProfile.CreateCloudClient();
+ if (!comProfile.isOk())
+ strErrorMessage = UIErrorString::formatErrorInfo(comProfile);
+ else
+ return comClient;
+ /* Null by default: */
+ return CCloudClient();
+}
+
+CCloudClient UICloudNetworkingStuff::cloudClientByName(const QString &strProviderShortName,
+ const QString &strProfileName,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ /* Acquire cloud profile: */
+ CCloudProfile comProfile = cloudProfileByName(strProviderShortName, strProfileName, pParent);
+ if (comProfile.isNotNull())
+ return cloudClient(comProfile, pParent);
+ /* Null by default: */
+ return CCloudClient();
+}
+
+CCloudClient UICloudNetworkingStuff::cloudClientByName(const QString &strProviderShortName,
+ const QString &strProfileName,
+ QString &strErrorMessage)
+{
+ /* Acquire cloud profile: */
+ CCloudProfile comProfile = cloudProfileByName(strProviderShortName, strProfileName, strErrorMessage);
+ if (comProfile.isNotNull())
+ return cloudClient(comProfile, strErrorMessage);
+ /* Null by default: */
+ return CCloudClient();
+}
+
+CVirtualSystemDescription UICloudNetworkingStuff::createVirtualSystemDescription(UINotificationCenter *pParent /* = 0 */)
+{
+ /* Acquire VBox: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ if (comVBox.isNotNull())
+ {
+ /* Create appliance: */
+ CAppliance comAppliance = comVBox.CreateAppliance();
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotCreateAppliance(comVBox, pParent);
+ else
+ {
+ /* Append it with one (1) description we need: */
+ comAppliance.CreateVirtualSystemDescriptions(1);
+ if (!comAppliance.isOk())
+ UINotificationMessage::cannotCreateVirtualSystemDescription(comAppliance, pParent);
+ else
+ {
+ /* Get received description: */
+ const QVector<CVirtualSystemDescription> descriptions = comAppliance.GetVirtualSystemDescriptions();
+ AssertReturn(!descriptions.isEmpty(), CVirtualSystemDescription());
+ return descriptions.at(0);
+ }
+ }
+ }
+ /* Null by default: */
+ return CVirtualSystemDescription();
+}
+
+QVector<CCloudProvider> UICloudNetworkingStuff::listCloudProviders(UINotificationCenter *pParent /* = 0 */)
+{
+ /* Acquire cloud provider manager: */
+ CCloudProviderManager comProviderManager = cloudProviderManager(pParent);
+ if (comProviderManager.isNotNull())
+ {
+ /* Acquire cloud providers: */
+ QVector<CCloudProvider> providers = comProviderManager.GetProviders();
+ if (!comProviderManager.isOk())
+ UINotificationMessage::cannotAcquireCloudProviderManagerParameter(comProviderManager, pParent);
+ else
+ return providers;
+ }
+ /* Return empty list by default: */
+ return QVector<CCloudProvider>();
+}
+
+bool UICloudNetworkingStuff::cloudProviderId(const CCloudProvider &comCloudProvider,
+ QUuid &uResult,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ const QUuid uId = comCloudProvider.GetId();
+ if (!comCloudProvider.isOk())
+ UINotificationMessage::cannotAcquireCloudProviderParameter(comCloudProvider, pParent);
+ else
+ {
+ uResult = uId;
+ return true;
+ }
+ return false;
+}
+
+bool UICloudNetworkingStuff::cloudProviderShortName(const CCloudProvider &comCloudProvider,
+ QString &strResult,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ const QString strShortName = comCloudProvider.GetShortName();
+ if (!comCloudProvider.isOk())
+ UINotificationMessage::cannotAcquireCloudProviderParameter(comCloudProvider, pParent);
+ else
+ {
+ strResult = strShortName;
+ return true;
+ }
+ return false;
+}
+
+bool UICloudNetworkingStuff::cloudProviderName(const CCloudProvider &comCloudProvider,
+ QString &strResult,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ const QString strName = comCloudProvider.GetName();
+ if (!comCloudProvider.isOk())
+ UINotificationMessage::cannotAcquireCloudProviderParameter(comCloudProvider, pParent);
+ else
+ {
+ strResult = strName;
+ return true;
+ }
+ return false;
+}
+
+QVector<CCloudProfile> UICloudNetworkingStuff::listCloudProfiles(const CCloudProvider &comCloudProvider,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ /* Check cloud provider: */
+ if (comCloudProvider.isNotNull())
+ {
+ /* Acquire cloud providers: */
+ QVector<CCloudProfile> profiles = comCloudProvider.GetProfiles();
+ if (!comCloudProvider.isOk())
+ UINotificationMessage::cannotAcquireCloudProviderParameter(comCloudProvider, pParent);
+ else
+ return profiles;
+ }
+ /* Return empty list by default: */
+ return QVector<CCloudProfile>();
+}
+
+bool UICloudNetworkingStuff::cloudProfileName(const CCloudProfile &comCloudProfile,
+ QString &strResult,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ const QString strName = comCloudProfile.GetName();
+ if (!comCloudProfile.isOk())
+ UINotificationMessage::cannotAcquireCloudProfileParameter(comCloudProfile, pParent);
+ else
+ {
+ strResult = strName;
+ return true;
+ }
+ return false;
+}
+
+bool UICloudNetworkingStuff::cloudProfileProperties(const CCloudProfile &comCloudProfile,
+ QVector<QString> &keys,
+ QVector<QString> &values,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ QVector<QString> aKeys;
+ QVector<QString> aValues;
+ aValues = comCloudProfile.GetProperties(QString(), aKeys);
+ if (!comCloudProfile.isOk())
+ UINotificationMessage::cannotAcquireCloudProfileParameter(comCloudProfile, pParent);
+ else
+ {
+ aValues.resize(aKeys.size());
+ keys = aKeys;
+ values = aValues;
+ return true;
+ }
+ return false;
+}
+
+bool UICloudNetworkingStuff::listCloudImages(const CCloudClient &comCloudClient,
+ CStringArray &comNames,
+ CStringArray &comIDs,
+ UINotificationCenter *pParent)
+{
+ /* Currently we are interested in Available images only: */
+ const QVector<KCloudImageState> cloudImageStates = QVector<KCloudImageState>()
+ << KCloudImageState_Available;
+
+ /* List cloud images: */
+ UINotificationProgressCloudImageList *pNotification =
+ new UINotificationProgressCloudImageList(comCloudClient, cloudImageStates);
+ UINotificationReceiver receiver1;
+ UINotificationReceiver receiver2;
+ QObject::connect(pNotification, &UINotificationProgressCloudImageList::sigImageNamesReceived,
+ &receiver1, &UINotificationReceiver::setReceiverProperty);
+ QObject::connect(pNotification, &UINotificationProgressCloudImageList::sigImageIdsReceived,
+ &receiver2, &UINotificationReceiver::setReceiverProperty);
+ if (pParent->handleNow(pNotification))
+ {
+ comNames = receiver1.property("received_value").value<CStringArray>();
+ comIDs = receiver2.property("received_value").value<CStringArray>();
+ return true;
+ }
+
+ /* Return false by default: */
+ return false;
+}
+
+bool UICloudNetworkingStuff::listCloudSourceBootVolumes(const CCloudClient &comCloudClient,
+ CStringArray &comNames,
+ CStringArray &comIDs,
+ UINotificationCenter *pParent)
+{
+ /* List cloud source boot volumes: */
+ UINotificationProgressCloudSourceBootVolumeList *pNotification =
+ new UINotificationProgressCloudSourceBootVolumeList(comCloudClient);
+ UINotificationReceiver receiver1;
+ UINotificationReceiver receiver2;
+ QObject::connect(pNotification, &UINotificationProgressCloudSourceBootVolumeList::sigImageNamesReceived,
+ &receiver1, &UINotificationReceiver::setReceiverProperty);
+ QObject::connect(pNotification, &UINotificationProgressCloudSourceBootVolumeList::sigImageIdsReceived,
+ &receiver2, &UINotificationReceiver::setReceiverProperty);
+ if (pParent->handleNow(pNotification))
+ {
+ comNames = receiver1.property("received_value").value<CStringArray>();
+ comIDs = receiver2.property("received_value").value<CStringArray>();
+ return true;
+ }
+
+ /* Return false by default: */
+ return false;
+}
+
+bool UICloudNetworkingStuff::listCloudInstances(const CCloudClient &comCloudClient,
+ CStringArray &comNames,
+ CStringArray &comIDs,
+ UINotificationCenter *pParent)
+{
+ /* List cloud instances: */
+ UINotificationProgressCloudInstanceList *pNotification =
+ new UINotificationProgressCloudInstanceList(comCloudClient);
+ UINotificationReceiver receiver1;
+ UINotificationReceiver receiver2;
+ QObject::connect(pNotification, &UINotificationProgressCloudInstanceList::sigImageNamesReceived,
+ &receiver1, &UINotificationReceiver::setReceiverProperty);
+ QObject::connect(pNotification, &UINotificationProgressCloudInstanceList::sigImageIdsReceived,
+ &receiver2, &UINotificationReceiver::setReceiverProperty);
+ if (pParent->handleNow(pNotification))
+ {
+ comNames = receiver1.property("received_value").value<CStringArray>();
+ comIDs = receiver2.property("received_value").value<CStringArray>();
+ return true;
+ }
+
+ /* Return false by default: */
+ return false;
+}
+
+bool UICloudNetworkingStuff::listCloudSourceInstances(const CCloudClient &comCloudClient,
+ CStringArray &comNames,
+ CStringArray &comIDs,
+ UINotificationCenter *pParent)
+{
+ /* List cloud source instances: */
+ UINotificationProgressCloudSourceInstanceList *pNotification =
+ new UINotificationProgressCloudSourceInstanceList(comCloudClient);
+ UINotificationReceiver receiver1;
+ UINotificationReceiver receiver2;
+ QObject::connect(pNotification, &UINotificationProgressCloudSourceInstanceList::sigImageNamesReceived,
+ &receiver1, &UINotificationReceiver::setReceiverProperty);
+ QObject::connect(pNotification, &UINotificationProgressCloudSourceInstanceList::sigImageIdsReceived,
+ &receiver2, &UINotificationReceiver::setReceiverProperty);
+ if (pParent->handleNow(pNotification))
+ {
+ comNames = receiver1.property("received_value").value<CStringArray>();
+ comIDs = receiver2.property("received_value").value<CStringArray>();
+ return true;
+ }
+
+ /* Return false by default: */
+ return false;
+}
+
+bool UICloudNetworkingStuff::exportDescriptionForm(const CCloudClient &comCloudClient,
+ const CVirtualSystemDescription &comDescription,
+ CVirtualSystemDescriptionForm &comResult,
+ UINotificationCenter *pParent)
+{
+ /* Prepare export VSD form: */
+ UINotificationProgressExportVSDFormCreate *pNotification =
+ new UINotificationProgressExportVSDFormCreate(comCloudClient, comDescription);
+ UINotificationReceiver receiver;
+ QObject::connect(pNotification, &UINotificationProgressExportVSDFormCreate::sigVSDFormCreated,
+ &receiver, &UINotificationReceiver::setReceiverProperty);
+ if (pParent->handleNow(pNotification))
+ {
+ comResult = receiver.property("received_value").value<CVirtualSystemDescriptionForm>();
+ return true;
+ }
+
+ /* False by default: */
+ return false;
+}
+
+bool UICloudNetworkingStuff::importDescriptionForm(const CCloudClient &comCloudClient,
+ const CVirtualSystemDescription &comDescription,
+ CVirtualSystemDescriptionForm &comResult,
+ UINotificationCenter *pParent)
+{
+ /* Prepare import VSD form: */
+ UINotificationProgressImportVSDFormCreate *pNotification =
+ new UINotificationProgressImportVSDFormCreate(comCloudClient, comDescription);
+ UINotificationReceiver receiver;
+ QObject::connect(pNotification, &UINotificationProgressImportVSDFormCreate::sigVSDFormCreated,
+ &receiver, &UINotificationReceiver::setReceiverProperty);
+ if (pParent->handleNow(pNotification))
+ {
+ comResult = receiver.property("received_value").value<CVirtualSystemDescriptionForm>();
+ return true;
+ }
+
+ /* False by default: */
+ return false;
+}
+
+bool UICloudNetworkingStuff::cloudMachineId(const CCloudMachine &comCloudMachine,
+ QUuid &uResult,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ const QUuid uId = comCloudMachine.GetId();
+ if (!comCloudMachine.isOk())
+ UINotificationMessage::cannotAcquireCloudMachineParameter(comCloudMachine, pParent);
+ else
+ {
+ uResult = uId;
+ return true;
+ }
+ return false;
+}
+
+bool UICloudNetworkingStuff::cloudMachineName(const CCloudMachine &comCloudMachine,
+ QString &strResult,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ const QString strName = comCloudMachine.GetName();
+ if (!comCloudMachine.isOk())
+ UINotificationMessage::cannotAcquireCloudMachineParameter(comCloudMachine, pParent);
+ else
+ {
+ strResult = strName;
+ return true;
+ }
+ return false;
+}
+
+bool UICloudNetworkingStuff::cloudMachineConsoleConnectionFingerprint(const CCloudMachine &comCloudMachine,
+ QString &strResult,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ const QString strConsoleConnectionFingerprint = comCloudMachine.GetConsoleConnectionFingerprint();
+ if (!comCloudMachine.isOk())
+ UINotificationMessage::cannotAcquireCloudMachineParameter(comCloudMachine, pParent);
+ else
+ {
+ strResult = strConsoleConnectionFingerprint;
+ return true;
+ }
+ return false;
+}
+
+bool UICloudNetworkingStuff::cloudMachineSettingsForm(const CCloudMachine &comCloudMachine,
+ CForm &comResult,
+ UINotificationCenter *pParent)
+{
+ /* Acquire machine name first: */
+ QString strMachineName;
+ if (!cloudMachineName(comCloudMachine, strMachineName))
+ return false;
+
+ /* Prepare VM settings form: */
+ UINotificationProgressCloudMachineSettingsFormCreate *pNotification =
+ new UINotificationProgressCloudMachineSettingsFormCreate(comCloudMachine, strMachineName);
+ UINotificationReceiver receiver;
+ QObject::connect(pNotification, &UINotificationProgressCloudMachineSettingsFormCreate::sigSettingsFormCreated,
+ &receiver, &UINotificationReceiver::setReceiverProperty);
+ if (pParent->handleNow(pNotification))
+ {
+ comResult = receiver.property("received_value").value<CForm>();
+ return true;
+ }
+
+ /* False by default: */
+ return false;
+}
+
+bool UICloudNetworkingStuff::cloudMachineSettingsForm(CCloudMachine comCloudMachine,
+ CForm &comResult,
+ QString &strErrorMessage)
+{
+ /* Prepare settings form: */
+ CForm comForm;
+
+ /* Now execute GetSettingsForm async method: */
+ CProgress comProgress = comCloudMachine.GetSettingsForm(comForm);
+ if (!comCloudMachine.isOk())
+ {
+ strErrorMessage = UIErrorString::formatErrorInfo(comCloudMachine);
+ return false;
+ }
+
+ /* Wait for "Get settings form" progress: */
+ comProgress.WaitForCompletion(-1);
+ if (comProgress.GetCanceled())
+ return false;
+ if (!comProgress.isOk() || comProgress.GetResultCode() != 0)
+ {
+ strErrorMessage = UIErrorString::formatErrorInfo(comProgress);
+ return false;
+ }
+
+ /* Return result: */
+ comResult = comForm;
+ return true;
+}
+
+bool UICloudNetworkingStuff::applyCloudMachineSettingsForm(const CCloudMachine &comCloudMachine,
+ const CForm &comForm,
+ UINotificationCenter *pParent)
+{
+ /* Acquire machine name first: */
+ QString strMachineName;
+ if (!cloudMachineName(comCloudMachine, strMachineName))
+ return false;
+
+ /* Apply VM settings form: */
+ UINotificationProgressCloudMachineSettingsFormApply *pNotification =
+ new UINotificationProgressCloudMachineSettingsFormApply(comForm, strMachineName);
+ return pParent->handleNow(pNotification);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UICloudNetworkingStuff.h b/src/VBox/Frontends/VirtualBox/src/globals/UICloudNetworkingStuff.h
new file mode 100644
index 00000000..ce898be3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UICloudNetworkingStuff.h
@@ -0,0 +1,191 @@
+/* $Id: UICloudNetworkingStuff.h $ */
+/** @file
+ * VBox Qt GUI - UICloudNetworkingStuff namespace declaration.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UICloudNetworkingStuff_h
+#define FEQT_INCLUDED_SRC_globals_UICloudNetworkingStuff_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UINotificationCenter.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudClient.h"
+#include "CCloudMachine.h"
+#include "CCloudProfile.h"
+#include "CCloudProvider.h"
+#include "CCloudProviderManager.h"
+#include "CForm.h"
+
+/** Cloud networking stuff namespace. */
+namespace UICloudNetworkingStuff
+{
+ /** Acquires cloud provider manager,
+ * using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF CCloudProviderManager cloudProviderManager(UINotificationCenter *pParent = 0);
+ /** Acquires cloud provider manager,
+ * using @a strErrorMessage to store messages to. */
+ SHARED_LIBRARY_STUFF CCloudProviderManager cloudProviderManager(QString &strErrorMessage);
+ /** Acquires cloud provider specified by @a strProviderShortName,
+ * using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF CCloudProvider cloudProviderByShortName(const QString &strProviderShortName,
+ UINotificationCenter *pParent = 0);
+ /** Acquires cloud provider specified by @a strProviderShortName,
+ * using @a strErrorMessage to store messages to. */
+ SHARED_LIBRARY_STUFF CCloudProvider cloudProviderByShortName(const QString &strProviderShortName,
+ QString &strErrorMessage);
+ /** Acquires cloud profile specified by @a strProviderShortName and @a strProfileName,
+ * using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF CCloudProfile cloudProfileByName(const QString &strProviderShortName,
+ const QString &strProfileName,
+ UINotificationCenter *pParent = 0);
+ /** Acquires cloud profile specified by @a strProviderShortName and @a strProfileName,
+ * using @a strErrorMessage to store messages to. */
+ SHARED_LIBRARY_STUFF CCloudProfile cloudProfileByName(const QString &strProviderShortName,
+ const QString &strProfileName,
+ QString &strErrorMessage);
+ /** Acquires cloud client created for @a comProfile,
+ * using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF CCloudClient cloudClient(CCloudProfile comProfile,
+ UINotificationCenter *pParent = 0);
+ /** Acquires cloud client created for @a comProfile,
+ * using @a strErrorMessage to store messages to. */
+ SHARED_LIBRARY_STUFF CCloudClient cloudClient(CCloudProfile comProfile,
+ QString &strErrorMessage);
+ /** Acquires cloud client specified by @a strProviderShortName and @a strProfileName,
+ * using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF CCloudClient cloudClientByName(const QString &strProviderShortName,
+ const QString &strProfileName,
+ UINotificationCenter *pParent = 0);
+ /** Acquires cloud client specified by @a strProviderShortName and @a strProfileName,
+ * using @a strErrorMessage to store messages to. */
+ SHARED_LIBRARY_STUFF CCloudClient cloudClientByName(const QString &strProviderShortName,
+ const QString &strProfileName,
+ QString &strErrorMessage);
+
+ /** Creates virtual system description, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF CVirtualSystemDescription createVirtualSystemDescription(UINotificationCenter *pParent = 0);
+
+ /** Acquires cloud providers, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF QVector<CCloudProvider> listCloudProviders(UINotificationCenter *pParent = 0);
+
+ /** Acquires @a comCloudProvider ID as a @a uResult, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool cloudProviderId(const CCloudProvider &comCloudProvider,
+ QUuid &uResult,
+ UINotificationCenter *pParent = 0);
+ /** Acquires @a comCloudProvider short name as a @a strResult, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool cloudProviderShortName(const CCloudProvider &comCloudProvider,
+ QString &strResult,
+ UINotificationCenter *pParent = 0);
+ /** Acquires @a comCloudProvider name as a @a strResult, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool cloudProviderName(const CCloudProvider &comCloudProvider,
+ QString &strResult,
+ UINotificationCenter *pParent = 0);
+
+ /** Acquires cloud profiles of certain @a comCloudProvider, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF QVector<CCloudProfile> listCloudProfiles(const CCloudProvider &comCloudProvider,
+ UINotificationCenter *pParent = 0);
+
+ /** Acquires @a comCloudProfile name as a @a strResult, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool cloudProfileName(const CCloudProfile &comCloudProfile,
+ QString &strResult,
+ UINotificationCenter *pParent = 0);
+ /** Acquires @a comCloudProfile properties as a @a keys/values using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool cloudProfileProperties(const CCloudProfile &comCloudProfile,
+ QVector<QString> &keys,
+ QVector<QString> &values,
+ UINotificationCenter *pParent = 0);
+
+ /** Acquires cloud images of certain @a comCloudClient, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool listCloudImages(const CCloudClient &comCloudClient,
+ CStringArray &comNames,
+ CStringArray &comIDs,
+ UINotificationCenter *pParent);
+ /** Acquires cloud source boot volumes of certain @a comCloudClient, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool listCloudSourceBootVolumes(const CCloudClient &comCloudClient,
+ CStringArray &comNames,
+ CStringArray &comIDs,
+ UINotificationCenter *pParent);
+ /** Acquires cloud instances of certain @a comCloudClient, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool listCloudInstances(const CCloudClient &comCloudClient,
+ CStringArray &comNames,
+ CStringArray &comIDs,
+ UINotificationCenter *pParent);
+ /** Acquires cloud source instances of certain @a comCloudClient, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool listCloudSourceInstances(const CCloudClient &comCloudClient,
+ CStringArray &comNames,
+ CStringArray &comIDs,
+ UINotificationCenter *pParent);
+
+ /** Acquires @a comCloudClient export description form as a @a comResult, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool exportDescriptionForm(const CCloudClient &comCloudClient,
+ const CVirtualSystemDescription &comDescription,
+ CVirtualSystemDescriptionForm &comResult,
+ UINotificationCenter *pParent);
+ /** Acquires @a comCloudClient import description form as a @a comResult, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool importDescriptionForm(const CCloudClient &comCloudClient,
+ const CVirtualSystemDescription &comDescription,
+ CVirtualSystemDescriptionForm &comResult,
+ UINotificationCenter *pParent);
+
+ /** Acquires @a comCloudMachine ID as a @a uResult, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool cloudMachineId(const CCloudMachine &comCloudMachine,
+ QUuid &uResult,
+ UINotificationCenter *pParent = 0);
+ /** Acquires @a comCloudMachine name as a @a strResult, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool cloudMachineName(const CCloudMachine &comCloudMachine,
+ QString &strResult,
+ UINotificationCenter *pParent = 0);
+ /** Acquires @a comCloudMachine console connection fingerprint as a @a strResult,
+ * using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool cloudMachineConsoleConnectionFingerprint(const CCloudMachine &comCloudMachine,
+ QString &strResult,
+ UINotificationCenter *pParent = 0);
+
+ /** Acquires @a comCloudMachine settings form as a @a comResult, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool cloudMachineSettingsForm(const CCloudMachine &comCloudMachine,
+ CForm &comResult,
+ UINotificationCenter *pParent);
+ /** Acquires @a comCloudMachine settings form as a @a comResult, using @a strErrorMessage to store messages to.
+ * @note Be aware, this is a blocking function, it will hang for a time of progress being executed. */
+ SHARED_LIBRARY_STUFF bool cloudMachineSettingsForm(CCloudMachine comCloudMachine,
+ CForm &comResult,
+ QString &strErrorMessage);
+
+ /** Applies @a comCloudMachine @a comForm settings, using @a pParent to show messages according to. */
+ SHARED_LIBRARY_STUFF bool applyCloudMachineSettingsForm(const CCloudMachine &comCloudMachine,
+ const CForm &comForm,
+ UINotificationCenter *pParent);
+}
+
+/* Using across any module who included us: */
+using namespace UICloudNetworkingStuff;
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UICloudNetworkingStuff_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UICommon.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UICommon.cpp
new file mode 100644
index 00000000..cc1ba02d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UICommon.cpp
@@ -0,0 +1,2952 @@
+/* $Id: UICommon.cpp $ */
+/** @file
+ * VBox Qt GUI - UICommon class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDesktopServices>
+#include <QDir>
+#include <QFileDialog>
+#include <QGraphicsWidget>
+#include <QLibraryInfo>
+#include <QLocale>
+#include <QMenu>
+#include <QMutex>
+#include <QProcess>
+#include <QProgressDialog>
+#include <QRegularExpression>
+#include <QSessionManager>
+#include <QSettings>
+#include <QSpinBox>
+#include <QStandardPaths>
+#include <QStyleOptionSpinBox>
+#include <QThread>
+#include <QTimer>
+#include <QToolButton>
+#include <QToolTip>
+#include <QTranslator>
+#ifdef VBOX_WS_WIN
+# include <QStyleFactory>
+#endif
+#ifdef VBOX_GUI_WITH_PIDFILE
+# include <QTextStream>
+#endif
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIFileDialog.h"
+#include "QIMessageBox.h"
+#include "QIWithRestorableGeometry.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataDefs.h"
+#include "UIExtraDataManager.h"
+#include "UIFDCreationDialog.h"
+#include "UIIconPool.h"
+#include "UIMedium.h"
+#include "UIMediumEnumerator.h"
+#include "UIMediumSelector.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINotificationCenter.h"
+#include "UIPopupCenter.h"
+#include "UIShortcutPool.h"
+#include "UIThreadPool.h"
+#include "UITranslator.h"
+#include "UIVirtualBoxClientEventHandler.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVisoCreator.h"
+#include "UIWizardNewVD.h"
+#ifdef VBOX_WS_MAC
+# include "UIMachineWindowFullscreen.h"
+# include "UIMachineWindowSeamless.h"
+#endif
+#ifdef VBOX_WS_WIN
+# include "VBoxUtils-win.h"
+#endif
+#ifdef VBOX_WS_X11
+# include "UIHostComboEditor.h"
+#endif
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+# include "UINetworkRequestManager.h"
+# include "UIUpdateManager.h"
+#endif
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CBIOSSettings.h"
+#include "CCloudMachine.h"
+#include "CConsole.h"
+#include "CExtPack.h"
+#include "CExtPackFile.h"
+#include "CExtPackManager.h"
+#include "CHostUSBDevice.h"
+#include "CHostVideoInputDevice.h"
+#include "CMachine.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CSerialPort.h"
+#include "CSharedFolder.h"
+#include "CSnapshot.h"
+#include "CStorageController.h"
+#include "CSystemProperties.h"
+#include "CUSBController.h"
+#include "CUSBDevice.h"
+#include "CUSBDeviceFilter.h"
+#include "CUSBDeviceFilters.h"
+#include "CVRDEServer.h"
+
+/* Other VBox includes: */
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/ldr.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/system.h>
+#include <VBox/sup.h>
+#include <VBox/VBoxOGL.h>
+#include <VBox/vd.h>
+#include <VBox/com/Guid.h>
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+/* External includes: */
+#ifdef VBOX_WS_MAC
+# include <sys/utsname.h>
+#endif
+#ifdef VBOX_WS_X11
+# include <xcb/xcb.h>
+#endif
+
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+using namespace UIMediumDefs;
+
+
+/* static */
+UICommon *UICommon::s_pInstance = 0;
+
+/* static */
+void UICommon::create(UIType enmType)
+{
+ /* Make sure instance is NOT created yet: */
+ AssertReturnVoid(!s_pInstance);
+
+ /* Create instance: */
+ new UICommon(enmType);
+ /* Prepare instance: */
+ s_pInstance->prepare();
+}
+
+/* static */
+void UICommon::destroy()
+{
+ /* Make sure instance is NOT destroyed yet: */
+ AssertPtrReturnVoid(s_pInstance);
+
+ /* Cleanup instance:
+ * 1. By default, automatically on QApplication::aboutToQuit() signal.
+ * 2. But if QApplication was not started at all and we perform
+ * early shutdown, we should do cleanup ourselves. */
+ if (s_pInstance->isValid())
+ s_pInstance->cleanup();
+ /* Destroy instance: */
+ delete s_pInstance;
+}
+
+UICommon::UICommon(UIType enmType)
+ : m_enmType(enmType)
+ , m_fValid(false)
+ , m_fCleaningUp(false)
+#ifdef VBOX_WS_WIN
+ , m_fDataCommitted(false)
+#endif
+#ifdef VBOX_WS_X11
+ , m_enmWindowManagerType(X11WMType_Unknown)
+ , m_fCompositingManagerRunning(false)
+#endif
+ , m_fSeparateProcess(false)
+ , m_fShowStartVMErrors(true)
+#if defined(DEBUG_bird)
+ , m_fAgressiveCaching(false)
+#else
+ , m_fAgressiveCaching(true)
+#endif
+ , m_fRestoreCurrentSnapshot(false)
+ , m_fExecuteAllInIem(false)
+ , m_uWarpPct(100)
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ , m_fDbgEnabled(0)
+ , m_fDbgAutoShow(0)
+ , m_fDbgAutoShowCommandLine(0)
+ , m_fDbgAutoShowStatistics(0)
+ , m_hVBoxDbg(NIL_RTLDRMOD)
+ , m_enmLaunchRunning(LaunchRunning_Default)
+#endif
+ , m_fSettingsPwSet(false)
+ , m_fWrappersValid(false)
+ , m_fVBoxSVCAvailable(true)
+ , m_pThreadPool(0)
+ , m_pThreadPoolCloud(0)
+ , m_pMediumEnumerator(0)
+{
+ /* Assign instance: */
+ s_pInstance = this;
+}
+
+UICommon::~UICommon()
+{
+ /* Unassign instance: */
+ s_pInstance = 0;
+}
+
+void UICommon::prepare()
+{
+ /* Make sure QApplication cleanup us on exit: */
+#ifndef VBOX_IS_QT6_OR_LATER /** @todo qt6: ... */
+ qApp->setFallbackSessionManagementEnabled(false);
+#endif
+ connect(qApp, &QGuiApplication::aboutToQuit,
+ this, &UICommon::sltCleanup);
+#ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ /* Make sure we handle host OS session shutdown as well: */
+ connect(qApp, &QGuiApplication::commitDataRequest,
+ this, &UICommon::sltHandleCommitDataRequest);
+#endif /* VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+ /* Create converter: */
+ UIConverter::create();
+
+ /* Create desktop-widget watchdog: */
+ UIDesktopWidgetWatchdog::create();
+
+ /* Create message-center: */
+ UIMessageCenter::create();
+ /* Create popup-center: */
+ UIPopupCenter::create();
+
+ /* Prepare general icon-pool: */
+ UIIconPoolGeneral::create();
+
+ /* Load translation based on the current locale: */
+ UITranslator::loadLanguage();
+
+ HRESULT rc = COMBase::InitializeCOM(true);
+ if (FAILED(rc))
+ {
+#ifdef VBOX_WITH_XPCOM
+ if (rc == NS_ERROR_FILE_ACCESS_DENIED)
+ {
+ char szHome[RTPATH_MAX] = "";
+ com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
+ msgCenter().cannotInitUserHome(QString(szHome));
+ }
+ else
+#endif
+ msgCenter().cannotInitCOM(rc);
+ return;
+ }
+
+ /* Make sure VirtualBoxClient instance created: */
+ m_comVBoxClient.createInstance(CLSID_VirtualBoxClient);
+ if (!m_comVBoxClient.isOk())
+ {
+ msgCenter().cannotCreateVirtualBoxClient(m_comVBoxClient);
+ return;
+ }
+ /* Make sure VirtualBox instance acquired: */
+ m_comVBox = m_comVBoxClient.GetVirtualBox();
+ if (!m_comVBoxClient.isOk())
+ {
+ msgCenter().cannotAcquireVirtualBox(m_comVBoxClient);
+ return;
+ }
+ /* Init wrappers: */
+ comWrappersReinit();
+
+ /* Watch for the VBoxSVC availability changes: */
+ connect(gVBoxClientEvents, &UIVirtualBoxClientEventHandler::sigVBoxSVCAvailabilityChange,
+ this, &UICommon::sltHandleVBoxSVCAvailabilityChange);
+
+ /* Prepare thread-pool instances: */
+ m_pThreadPool = new UIThreadPool(3 /* worker count */, 5000 /* worker timeout */);
+ m_pThreadPoolCloud = new UIThreadPool(2 /* worker count */, 1000 /* worker timeout */);
+
+#ifdef VBOX_WS_WIN
+ /* Load color theme: */
+ loadColorTheme();
+#endif
+
+ /* Load translation based on the user settings: */
+ QString strLanguageId = gEDataManager->languageId();
+ if (!strLanguageId.isNull())
+ UITranslator::loadLanguage(strLanguageId);
+
+ retranslateUi();
+
+ connect(gEDataManager, &UIExtraDataManager::sigLanguageChange,
+ this, &UICommon::sltGUILanguageChange);
+ connect(gEDataManager, &UIExtraDataManager::sigFontScaleFactorChanged,
+ this, &UICommon::sltHandleFontScaleFactorChanged);
+
+ qApp->installEventFilter(this);
+
+ /* process command line */
+
+ UIVisualStateType visualStateType = UIVisualStateType_Invalid;
+
+#ifdef VBOX_WS_X11
+ /* Check whether we have compositing manager running: */
+ m_fCompositingManagerRunning = NativeWindowSubsystem::X11IsCompositingManagerRunning();
+
+ /* Acquire current Window Manager type: */
+ m_enmWindowManagerType = NativeWindowSubsystem::X11WindowManagerType();
+#endif /* VBOX_WS_X11 */
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+# ifdef VBOX_WITH_DEBUGGER_GUI_MENU
+ initDebuggerVar(&m_fDbgEnabled, "VBOX_GUI_DBG_ENABLED", GUI_Dbg_Enabled, true);
+# else
+ initDebuggerVar(&m_fDbgEnabled, "VBOX_GUI_DBG_ENABLED", GUI_Dbg_Enabled, false);
+# endif
+ initDebuggerVar(&m_fDbgAutoShow, "VBOX_GUI_DBG_AUTO_SHOW", GUI_Dbg_AutoShow, false);
+ m_fDbgAutoShowCommandLine = m_fDbgAutoShowStatistics = m_fDbgAutoShow;
+#endif
+
+ /*
+ * Parse the command line options.
+ *
+ * This is a little sloppy but we're trying to tighten it up. Unfortuately,
+ * both on X11 and darwin (IIRC) there might be additional arguments aimed
+ * for client libraries with GUI processes. So, using RTGetOpt or similar
+ * is a bit hard since we have to cope with unknown options.
+ */
+ m_fShowStartVMErrors = true;
+ bool startVM = false;
+ bool fSeparateProcess = false;
+ QString vmNameOrUuid;
+
+ const QStringList &arguments = QCoreApplication::arguments();
+ const int argc = arguments.size();
+ int i = 1;
+ while (i < argc)
+ {
+ const QByteArray &argBytes = arguments.at(i).toUtf8();
+ const char *arg = argBytes.constData();
+ enum { OptType_Unknown, OptType_VMRunner, OptType_VMSelector, OptType_MaybeBoth } enmOptType = OptType_Unknown;
+ /* NOTE: the check here must match the corresponding check for the
+ * options to start a VM in main.cpp and hardenedmain.cpp exactly,
+ * otherwise there will be weird error messages. */
+ if ( !::strcmp(arg, "--startvm")
+ || !::strcmp(arg, "-startvm"))
+ {
+ enmOptType = OptType_VMRunner;
+ if (++i < argc)
+ {
+ vmNameOrUuid = arguments.at(i);
+ startVM = true;
+ }
+ }
+ else if (!::strcmp(arg, "-separate") || !::strcmp(arg, "--separate"))
+ {
+ enmOptType = OptType_VMRunner;
+ fSeparateProcess = true;
+ }
+#ifdef VBOX_GUI_WITH_PIDFILE
+ else if (!::strcmp(arg, "-pidfile") || !::strcmp(arg, "--pidfile"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ if (++i < argc)
+ m_strPidFile = arguments.at(i);
+ }
+#endif /* VBOX_GUI_WITH_PIDFILE */
+ /* Visual state type options: */
+ else if (!::strcmp(arg, "-normal") || !::strcmp(arg, "--normal"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ visualStateType = UIVisualStateType_Normal;
+ }
+ else if (!::strcmp(arg, "-fullscreen") || !::strcmp(arg, "--fullscreen"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ visualStateType = UIVisualStateType_Fullscreen;
+ }
+ else if (!::strcmp(arg, "-seamless") || !::strcmp(arg, "--seamless"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ visualStateType = UIVisualStateType_Seamless;
+ }
+ else if (!::strcmp(arg, "-scale") || !::strcmp(arg, "--scale"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ visualStateType = UIVisualStateType_Scale;
+ }
+ /* Passwords: */
+ else if (!::strcmp(arg, "--settingspw"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ if (++i < argc)
+ {
+ RTStrCopy(m_astrSettingsPw, sizeof(m_astrSettingsPw), arguments.at(i).toLocal8Bit().constData());
+ m_fSettingsPwSet = true;
+ }
+ }
+ else if (!::strcmp(arg, "--settingspwfile"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ if (++i < argc)
+ {
+ const QByteArray &argFileBytes = arguments.at(i).toLocal8Bit();
+ const char *pszFile = argFileBytes.constData();
+ bool fStdIn = !::strcmp(pszFile, "stdin");
+ int vrc = VINF_SUCCESS;
+ PRTSTREAM pStrm;
+ if (!fStdIn)
+ vrc = RTStrmOpen(pszFile, "r", &pStrm);
+ else
+ pStrm = g_pStdIn;
+ if (RT_SUCCESS(vrc))
+ {
+ size_t cbFile;
+ vrc = RTStrmReadEx(pStrm, m_astrSettingsPw, sizeof(m_astrSettingsPw) - 1, &cbFile);
+ if (RT_SUCCESS(vrc))
+ {
+ if (cbFile >= sizeof(m_astrSettingsPw) - 1)
+ cbFile = sizeof(m_astrSettingsPw) - 1;
+ unsigned i;
+ for (i = 0; i < cbFile && !RT_C_IS_CNTRL(m_astrSettingsPw[i]); i++)
+ ;
+ m_astrSettingsPw[i] = '\0';
+ m_fSettingsPwSet = true;
+ }
+ if (!fStdIn)
+ RTStrmClose(pStrm);
+ }
+ }
+ }
+ /* Misc options: */
+ else if (!::strcmp(arg, "-comment") || !::strcmp(arg, "--comment"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ ++i;
+ }
+ else if (!::strcmp(arg, "--no-startvm-errormsgbox"))
+ {
+ enmOptType = OptType_VMRunner;
+ m_fShowStartVMErrors = false;
+ }
+ else if (!::strcmp(arg, "--aggressive-caching"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ m_fAgressiveCaching = true;
+ }
+ else if (!::strcmp(arg, "--no-aggressive-caching"))
+ {
+ enmOptType = OptType_MaybeBoth;
+ m_fAgressiveCaching = false;
+ }
+ else if (!::strcmp(arg, "--restore-current"))
+ {
+ enmOptType = OptType_VMRunner;
+ m_fRestoreCurrentSnapshot = true;
+ }
+ /* Ad hoc VM reconfig options: */
+ else if (!::strcmp(arg, "--fda"))
+ {
+ enmOptType = OptType_VMRunner;
+ if (++i < argc)
+ m_uFloppyImage = QUuid(arguments.at(i));
+ }
+ else if (!::strcmp(arg, "--dvd") || !::strcmp(arg, "--cdrom"))
+ {
+ enmOptType = OptType_VMRunner;
+ if (++i < argc)
+ m_uDvdImage = QUuid(arguments.at(i));
+ }
+ /* VMM Options: */
+ else if (!::strcmp(arg, "--execute-all-in-iem"))
+ {
+ enmOptType = OptType_VMRunner;
+ m_fExecuteAllInIem = true;
+ }
+ else if (!::strcmp(arg, "--driverless"))
+ enmOptType = OptType_VMRunner;
+ else if (!::strcmp(arg, "--warp-pct"))
+ {
+ enmOptType = OptType_VMRunner;
+ if (++i < argc)
+ m_uWarpPct = RTStrToUInt32(arguments.at(i).toLocal8Bit().constData());
+ }
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Debugger/Debugging options: */
+ else if (!::strcmp(arg, "-dbg") || !::strcmp(arg, "--dbg"))
+ {
+ enmOptType = OptType_VMRunner;
+ setDebuggerVar(&m_fDbgEnabled, true);
+ }
+ else if (!::strcmp( arg, "-debug") || !::strcmp(arg, "--debug"))
+ {
+ enmOptType = OptType_VMRunner;
+ setDebuggerVar(&m_fDbgEnabled, true);
+ setDebuggerVar(&m_fDbgAutoShow, true);
+ setDebuggerVar(&m_fDbgAutoShowCommandLine, true);
+ setDebuggerVar(&m_fDbgAutoShowStatistics, true);
+ }
+ else if (!::strcmp(arg, "--debug-command-line"))
+ {
+ enmOptType = OptType_VMRunner;
+ setDebuggerVar(&m_fDbgEnabled, true);
+ setDebuggerVar(&m_fDbgAutoShow, true);
+ setDebuggerVar(&m_fDbgAutoShowCommandLine, true);
+ }
+ else if (!::strcmp(arg, "--debug-statistics"))
+ {
+ enmOptType = OptType_VMRunner;
+ setDebuggerVar(&m_fDbgEnabled, true);
+ setDebuggerVar(&m_fDbgAutoShow, true);
+ setDebuggerVar(&m_fDbgAutoShowStatistics, true);
+ }
+ else if (!::strcmp(arg, "--statistics-expand") || !::strcmp(arg, "--stats-expand"))
+ {
+ enmOptType = OptType_VMRunner;
+ if (++i < argc)
+ {
+ if (!m_strDbgStatisticsExpand.isEmpty())
+ m_strDbgStatisticsExpand.append('|');
+ m_strDbgStatisticsExpand.append(arguments.at(i));
+ }
+ }
+ else if (!::strncmp(arg, RT_STR_TUPLE("--statistics-expand=")) || !::strncmp(arg, RT_STR_TUPLE("--stats-expand=")))
+ {
+ enmOptType = OptType_VMRunner;
+ if (!m_strDbgStatisticsExpand.isEmpty())
+ m_strDbgStatisticsExpand.append('|');
+ m_strDbgStatisticsExpand.append(arguments.at(i).section('=', 1));
+ }
+ else if (!::strcmp(arg, "--statistics-filter") || !::strcmp(arg, "--stats-filter"))
+ {
+ enmOptType = OptType_VMRunner;
+ if (++i < argc)
+ m_strDbgStatisticsFilter = arguments.at(i);
+ }
+ else if (!::strncmp(arg, RT_STR_TUPLE("--statistics-filter=")) || !::strncmp(arg, RT_STR_TUPLE("--stats-filter=")))
+ {
+ enmOptType = OptType_VMRunner;
+ m_strDbgStatisticsFilter = arguments.at(i).section('=', 1);
+ }
+ else if (!::strcmp(arg, "-no-debug") || !::strcmp(arg, "--no-debug"))
+ {
+ enmOptType = OptType_VMRunner;
+ setDebuggerVar(&m_fDbgEnabled, false);
+ setDebuggerVar(&m_fDbgAutoShow, false);
+ setDebuggerVar(&m_fDbgAutoShowCommandLine, false);
+ setDebuggerVar(&m_fDbgAutoShowStatistics, false);
+ }
+ /* Not quite debug options, but they're only useful with the debugger bits. */
+ else if (!::strcmp(arg, "--start-paused"))
+ {
+ enmOptType = OptType_VMRunner;
+ m_enmLaunchRunning = LaunchRunning_No;
+ }
+ else if (!::strcmp(arg, "--start-running"))
+ {
+ enmOptType = OptType_VMRunner;
+ m_enmLaunchRunning = LaunchRunning_Yes;
+ }
+#endif
+ if (enmOptType == OptType_VMRunner && m_enmType != UIType_RuntimeUI)
+ msgCenter().cannotHandleRuntimeOption(arg);
+
+ i++;
+ }
+
+ if (m_enmType == UIType_RuntimeUI && startVM)
+ {
+ /* m_fSeparateProcess makes sense only if a VM is started. */
+ m_fSeparateProcess = fSeparateProcess;
+
+ /* Search for corresponding VM: */
+ QUuid uuid = QUuid(vmNameOrUuid);
+ const CMachine machine = m_comVBox.FindMachine(vmNameOrUuid);
+ if (!uuid.isNull())
+ {
+ if (machine.isNull() && showStartVMErrors())
+ return msgCenter().cannotFindMachineById(m_comVBox, uuid);
+ }
+ else
+ {
+ if (machine.isNull() && showStartVMErrors())
+ return msgCenter().cannotFindMachineByName(m_comVBox, vmNameOrUuid);
+ }
+ m_strManagedVMId = machine.GetId();
+
+ if (m_fSeparateProcess)
+ {
+ /* Create a log file for VirtualBoxVM process. */
+ QString str = machine.GetLogFolder();
+ com::Utf8Str logDir(str.toUtf8().constData());
+
+ /* make sure the Logs folder exists */
+ if (!RTDirExists(logDir.c_str()))
+ RTDirCreateFullPath(logDir.c_str(), 0700);
+
+ com::Utf8Str logFile = com::Utf8StrFmt("%s%cVBoxUI.log",
+ logDir.c_str(), RTPATH_DELIMITER);
+
+ com::VBoxLogRelCreate("GUI (separate)", logFile.c_str(),
+ RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS,
+ "all all.restrict -default.restrict",
+ "VBOX_RELEASE_LOG", RTLOGDEST_FILE,
+ 32768 /* cMaxEntriesPerGroup */,
+ 0 /* cHistory */, 0 /* uHistoryFileTime */,
+ 0 /* uHistoryFileSize */, NULL);
+ }
+ }
+
+ /* For Selector UI: */
+ if (uiType() == UIType_SelectorUI)
+ {
+ /* We should create separate logging file for VM selector: */
+ char szLogFile[RTPATH_MAX];
+ const char *pszLogFile = NULL;
+ com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
+ RTPathAppend(szLogFile, sizeof(szLogFile), "selectorwindow.log");
+ pszLogFile = szLogFile;
+ /* Create release logger, to file: */
+ com::VBoxLogRelCreate("GUI VM Selector Window",
+ pszLogFile,
+ RTLOGFLAGS_PREFIX_TIME_PROG,
+ "all",
+ "VBOX_GUI_SELECTORWINDOW_RELEASE_LOG",
+ RTLOGDEST_FILE | RTLOGDEST_F_NO_DENY,
+ UINT32_MAX,
+ 10,
+ 60 * 60,
+ _1M,
+ NULL /*pErrInfo*/);
+
+ LogRel(("Qt version: %s\n", qtRTVersionString().toUtf8().constData()));
+ }
+
+ if (m_fSettingsPwSet)
+ m_comVBox.SetSettingsSecret(m_astrSettingsPw);
+
+ if (visualStateType != UIVisualStateType_Invalid && !m_strManagedVMId.isNull())
+ gEDataManager->setRequestedVisualState(visualStateType, m_strManagedVMId);
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* For Runtime UI: */
+ if (uiType() == UIType_RuntimeUI)
+ {
+ /* Setup the debugger GUI: */
+ if (RTEnvExist("VBOX_GUI_NO_DEBUGGER"))
+ m_fDbgEnabled = m_fDbgAutoShow = m_fDbgAutoShowCommandLine = m_fDbgAutoShowStatistics = false;
+ if (m_fDbgEnabled)
+ {
+ RTERRINFOSTATIC ErrInfo;
+ RTErrInfoInitStatic(&ErrInfo);
+ int vrc = SUPR3HardenedLdrLoadAppPriv("VBoxDbg", &m_hVBoxDbg, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
+ if (RT_FAILURE(vrc))
+ {
+ m_hVBoxDbg = NIL_RTLDRMOD;
+ m_fDbgAutoShow = m_fDbgAutoShowCommandLine = m_fDbgAutoShowStatistics = false;
+ LogRel(("Failed to load VBoxDbg, rc=%Rrc - %s\n", vrc, ErrInfo.Core.pszMsg));
+ }
+ }
+ }
+#endif
+
+ m_fValid = true;
+
+ /* Create medium-enumerator but don't do any immediate caching: */
+ m_pMediumEnumerator = new UIMediumEnumerator;
+ {
+ /* Prepare medium-enumerator: */
+ connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumCreated,
+ this, &UICommon::sigMediumCreated);
+ connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumDeleted,
+ this, &UICommon::sigMediumDeleted);
+ connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumEnumerationStarted,
+ this, &UICommon::sigMediumEnumerationStarted);
+ connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumEnumerated,
+ this, &UICommon::sigMediumEnumerated);
+ connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumEnumerationFinished,
+ this, &UICommon::sigMediumEnumerationFinished);
+ }
+
+ /* Create shortcut pool: */
+ UIShortcutPool::create();
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* Create network manager: */
+ UINetworkRequestManager::create();
+
+ /* Schedule update manager: */
+ UIUpdateManager::schedule();
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+#ifdef RT_OS_LINUX
+ /* Make sure no wrong USB mounted: */
+ checkForWrongUSBMounted();
+#endif /* RT_OS_LINUX */
+
+ /* Populate the list of medium names to be excluded from the
+ recently used media extra data: */
+#if 0 /* bird: This is counter productive as it is _frequently_ necessary to re-insert the
+ viso to refresh the files (like after you rebuilt them on the host).
+ The guest caches ISOs aggressively and files sizes may change. */
+ m_recentMediaExcludeList << "ad-hoc.viso";
+#endif
+
+
+ iOriginalFontPixelSize = qApp->font().pixelSize();
+ iOriginalFontPointSize = qApp->font().pointSize();
+ sltHandleFontScaleFactorChanged(gEDataManager->fontScaleFactor());
+}
+
+void UICommon::cleanup()
+{
+ LogRel(("GUI: UICommon: Handling aboutToQuit request..\n"));
+
+ /// @todo Shouldn't that be protected with a mutex or something?
+ /* Remember that the cleanup is in progress preventing any unwanted
+ * stuff which could be called from the other threads: */
+ m_fCleaningUp = true;
+
+#ifdef VBOX_WS_WIN
+ /* Ask listeners to commit data if haven't yet: */
+ if (!m_fDataCommitted)
+ {
+ emit sigAskToCommitData();
+ m_fDataCommitted = true;
+ }
+#else
+ /* Ask listeners to commit data: */
+ emit sigAskToCommitData();
+#endif
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* For Runtime UI: */
+ if ( uiType() == UIType_RuntimeUI
+ && m_hVBoxDbg != NIL_RTLDRMOD)
+ {
+ RTLdrClose(m_hVBoxDbg);
+ m_hVBoxDbg = NIL_RTLDRMOD;
+ }
+#endif
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* Shutdown update manager: */
+ UIUpdateManager::shutdown();
+
+ /* Destroy network manager: */
+ UINetworkRequestManager::destroy();
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+ /* Destroy shortcut pool: */
+ UIShortcutPool::destroy();
+
+#ifdef VBOX_GUI_WITH_PIDFILE
+ deletePidfile();
+#endif /* VBOX_GUI_WITH_PIDFILE */
+
+ /* Starting medium-enumerator cleanup: */
+ m_meCleanupProtectionToken.lockForWrite();
+ {
+ /* Destroy medium-enumerator: */
+ delete m_pMediumEnumerator;
+ m_pMediumEnumerator = 0;
+ }
+ /* Finishing medium-enumerator cleanup: */
+ m_meCleanupProtectionToken.unlock();
+
+ /* Destroy the global (VirtualBox and VirtualBoxClient) Main event
+ * handlers which are used in both Manager and Runtime UIs. */
+ UIVirtualBoxEventHandler::destroy();
+ UIVirtualBoxClientEventHandler::destroy();
+
+ /* Destroy the extra-data manager finally after everything
+ * above which could use it already destroyed: */
+ UIExtraDataManager::destroy();
+
+ /* Destroy converter: */
+ UIConverter::destroy();
+
+ /* Cleanup thread-pools: */
+ delete m_pThreadPool;
+ m_pThreadPool = 0;
+ delete m_pThreadPoolCloud;
+ m_pThreadPoolCloud = 0;
+
+ /* Ensure CGuestOSType objects are no longer used: */
+ m_guestOSFamilyIDs.clear();
+ m_guestOSTypes.clear();
+
+ /* Starting COM cleanup: */
+ m_comCleanupProtectionToken.lockForWrite();
+ {
+ /* First, make sure we don't use COM any more: */
+ emit sigAskToDetachCOM();
+ m_comHost.detach();
+ m_comVBox.detach();
+ m_comVBoxClient.detach();
+
+ /* There may be UIMedium(s)EnumeratedEvent instances still in the message
+ * queue which reference COM objects. Remove them to release those objects
+ * before uninitializing the COM subsystem. */
+ QApplication::removePostedEvents(this);
+
+ /* Finally cleanup COM itself: */
+ COMBase::CleanupCOM();
+ }
+ /* Finishing COM cleanup: */
+ m_comCleanupProtectionToken.unlock();
+
+ /* Notify listener it can close UI now: */
+ emit sigAskToCloseUI();
+
+ /* Cleanup general icon-pool: */
+ UIIconPoolGeneral::destroy();
+
+ /* Destroy popup-center: */
+ UIPopupCenter::destroy();
+ /* Destroy message-center: */
+ UIMessageCenter::destroy();
+
+ /* Destroy desktop-widget watchdog: */
+ UIDesktopWidgetWatchdog::destroy();
+
+ m_fValid = false;
+
+ LogRel(("GUI: UICommon: aboutToQuit request handled!\n"));
+}
+
+/* static */
+QString UICommon::qtRTVersionString()
+{
+ return QString::fromLatin1(qVersion());
+}
+
+/* static */
+uint UICommon::qtRTVersion()
+{
+ const QString strVersionRT = UICommon::qtRTVersionString();
+ return (strVersionRT.section('.', 0, 0).toInt() << 16) +
+ (strVersionRT.section('.', 1, 1).toInt() << 8) +
+ strVersionRT.section('.', 2, 2).toInt();
+}
+
+/* static */
+uint UICommon::qtRTMajorVersion()
+{
+ return UICommon::qtRTVersionString().section('.', 0, 0).toInt();
+}
+
+/* static */
+uint UICommon::qtRTMinorVersion()
+{
+ return UICommon::qtRTVersionString().section('.', 1, 1).toInt();
+}
+
+/* static */
+uint UICommon::qtRTRevisionNumber()
+{
+ return UICommon::qtRTVersionString().section('.', 2, 2).toInt();
+}
+
+/* static */
+QString UICommon::qtCTVersionString()
+{
+ return QString::fromLatin1(QT_VERSION_STR);
+}
+
+/* static */
+uint UICommon::qtCTVersion()
+{
+ const QString strVersionCompiled = UICommon::qtCTVersionString();
+ return (strVersionCompiled.section('.', 0, 0).toInt() << 16) +
+ (strVersionCompiled.section('.', 1, 1).toInt() << 8) +
+ strVersionCompiled.section('.', 2, 2).toInt();
+}
+
+QString UICommon::vboxVersionString() const
+{
+ return m_comVBox.GetVersion();
+}
+
+QString UICommon::vboxVersionStringNormalized() const
+{
+ return m_comVBox.GetVersionNormalized();
+}
+
+bool UICommon::isBeta() const
+{
+ return vboxVersionString().contains(QRegularExpression("BETA|ALPHA", QRegularExpression::CaseInsensitiveOption));
+}
+
+bool UICommon::showBetaLabel() const
+{
+ return isBeta()
+ && !gEDataManager->preventBetaBuildLavel();
+}
+
+bool UICommon::brandingIsActive(bool fForce /* = false */)
+{
+ if (fForce)
+ return true;
+
+ if (m_strBrandingConfigFilePath.isEmpty())
+ {
+ m_strBrandingConfigFilePath = QDir(QApplication::applicationDirPath()).absolutePath();
+ m_strBrandingConfigFilePath += "/custom/custom.ini";
+ }
+
+ return QFile::exists(m_strBrandingConfigFilePath);
+}
+
+QString UICommon::brandingGetKey(QString strKey) const
+{
+ QSettings settings(m_strBrandingConfigFilePath, QSettings::IniFormat);
+ return settings.value(QString("%1").arg(strKey)).toString();
+}
+
+#ifdef VBOX_WS_WIN
+/* static */
+void UICommon::loadColorTheme()
+{
+ /* Load saved color theme: */
+ UIColorThemeType enmColorTheme = gEDataManager->colorTheme();
+
+ /* Check whether we have dark system theme requested: */
+ if (enmColorTheme == UIColorThemeType_Auto)
+ {
+ QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
+ QSettings::NativeFormat);
+ if (settings.value("AppsUseLightTheme") == 0)
+ enmColorTheme = UIColorThemeType_Dark;
+ }
+
+ /* Check whether dark theme was requested by any means: */
+ if (enmColorTheme == UIColorThemeType_Dark)
+ {
+ qApp->setStyle(QStyleFactory::create("Fusion"));
+ QPalette darkPalette;
+ QColor windowColor1 = QColor(59, 60, 61);
+ QColor windowColor2 = QColor(63, 64, 65);
+ QColor baseColor1 = QColor(46, 47, 48);
+ QColor baseColor2 = QColor(56, 57, 58);
+ QColor disabledColor = QColor(113, 114, 115);
+ darkPalette.setColor(QPalette::Window, windowColor1);
+ darkPalette.setColor(QPalette::WindowText, Qt::white);
+ darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, disabledColor);
+ darkPalette.setColor(QPalette::Base, baseColor1);
+ darkPalette.setColor(QPalette::AlternateBase, baseColor2);
+ darkPalette.setColor(QPalette::PlaceholderText, disabledColor);
+ darkPalette.setColor(QPalette::Text, Qt::white);
+ darkPalette.setColor(QPalette::Disabled, QPalette::Text, disabledColor);
+ darkPalette.setColor(QPalette::Button, windowColor2);
+ darkPalette.setColor(QPalette::ButtonText, Qt::white);
+ darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledColor);
+ darkPalette.setColor(QPalette::BrightText, Qt::red);
+ darkPalette.setColor(QPalette::Link, QColor(179, 214, 242));
+ darkPalette.setColor(QPalette::Highlight, QColor(29, 84, 92));
+ darkPalette.setColor(QPalette::HighlightedText, Qt::white);
+ darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, disabledColor);
+ qApp->setPalette(darkPalette);
+ qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2b2b2b; border: 1px solid #737373; }");
+ }
+}
+#endif /* VBOX_WS_WIN */
+
+bool UICommon::processArgs()
+{
+ /* Among those arguments: */
+ bool fResult = false;
+ const QStringList args = qApp->arguments();
+
+ /* We are looking for a list of file URLs passed to the executable: */
+ QList<QUrl> listArgUrls;
+ for (int i = 1; i < args.size(); ++i)
+ {
+ /* But we break out after the first parameter, cause there
+ * could be parameters with arguments (e.g. --comment comment). */
+ if (args.at(i).startsWith("-"))
+ break;
+
+#ifdef VBOX_WS_MAC
+ const QString strArg = ::darwinResolveAlias(args.at(i));
+#else
+ const QString strArg = args.at(i);
+#endif
+
+ /* So if the argument file exists, we add it to URL list: */
+ if ( !strArg.isEmpty()
+ && QFile::exists(strArg))
+ listArgUrls << QUrl::fromLocalFile(QFileInfo(strArg).absoluteFilePath());
+ }
+
+ /* If there are file URLs: */
+ if (!listArgUrls.isEmpty())
+ {
+ /* We enumerate them and: */
+ for (int i = 0; i < listArgUrls.size(); ++i)
+ {
+ /* Check which of them has allowed VM extensions: */
+ const QUrl url = listArgUrls.at(i);
+ const QString strFile = url.toLocalFile();
+ if (UICommon::hasAllowedExtension(strFile, VBoxFileExts))
+ {
+ /* So that we could run existing VMs: */
+ CVirtualBox comVBox = virtualBox();
+ CMachine comMachine = comVBox.FindMachine(strFile);
+ if (!comMachine.isNull())
+ {
+ fResult = true;
+ launchMachine(comMachine);
+ /* And remove their URLs from the ULR list: */
+ listArgUrls.removeAll(url);
+ }
+ }
+ }
+ }
+
+ /* And if there are *still* URLs: */
+ if (!listArgUrls.isEmpty())
+ {
+ /* We store them, they will be handled later: */
+ m_listArgUrls = listArgUrls;
+ }
+
+ return fResult;
+}
+
+bool UICommon::argumentUrlsPresent() const
+{
+ return !m_listArgUrls.isEmpty();
+}
+
+QList<QUrl> UICommon::takeArgumentUrls()
+{
+ const QList<QUrl> result = m_listArgUrls;
+ m_listArgUrls.clear();
+ return result;
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+
+bool UICommon::isDebuggerEnabled() const
+{
+ return isDebuggerWorker(&m_fDbgEnabled, GUI_Dbg_Enabled);
+}
+
+bool UICommon::isDebuggerAutoShowEnabled() const
+{
+ return isDebuggerWorker(&m_fDbgAutoShow, GUI_Dbg_AutoShow);
+}
+
+bool UICommon::isDebuggerAutoShowCommandLineEnabled() const
+{
+ return isDebuggerWorker(&m_fDbgAutoShowCommandLine, GUI_Dbg_AutoShow);
+}
+
+bool UICommon::isDebuggerAutoShowStatisticsEnabled() const
+{
+ return isDebuggerWorker(&m_fDbgAutoShowStatistics, GUI_Dbg_AutoShow);
+}
+
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+bool UICommon::shouldStartPaused() const
+{
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ return m_enmLaunchRunning == LaunchRunning_Default ? isDebuggerAutoShowEnabled() : m_enmLaunchRunning == LaunchRunning_No;
+#else
+ return false;
+#endif
+}
+
+#ifdef VBOX_GUI_WITH_PIDFILE
+
+void UICommon::createPidfile()
+{
+ if (!m_strPidFile.isEmpty())
+ {
+ const qint64 iPid = qApp->applicationPid();
+ QFile file(m_strPidFile);
+ if (file.open(QIODevice::WriteOnly | QIODevice::Truncate))
+ {
+ QTextStream out(&file);
+ out << iPid << endl;
+ }
+ else
+ LogRel(("Failed to create pid file %s\n", m_strPidFile.toUtf8().constData()));
+ }
+}
+
+void UICommon::deletePidfile()
+{
+ if ( !m_strPidFile.isEmpty()
+ && QFile::exists(m_strPidFile))
+ QFile::remove(m_strPidFile);
+}
+
+#endif /* VBOX_GUI_WITH_PIDFILE */
+
+QString UICommon::vmGuestOSFamilyDescription(const QString &strFamilyId) const
+{
+ AssertMsg(m_guestOSFamilyDescriptions.contains(strFamilyId),
+ ("Family ID incorrect: '%s'.", strFamilyId.toLatin1().constData()));
+ return m_guestOSFamilyDescriptions.value(strFamilyId);
+}
+
+QList<CGuestOSType> UICommon::vmGuestOSTypeList(const QString &strFamilyId) const
+{
+ AssertMsg(m_guestOSFamilyIDs.contains(strFamilyId),
+ ("Family ID incorrect: '%s'.", strFamilyId.toLatin1().constData()));
+ return m_guestOSFamilyIDs.contains(strFamilyId) ?
+ m_guestOSTypes[m_guestOSFamilyIDs.indexOf(strFamilyId)] : QList<CGuestOSType>();
+}
+
+CGuestOSType UICommon::vmGuestOSType(const QString &strTypeId,
+ const QString &strFamilyId /* = QString() */) const
+{
+ QList<CGuestOSType> list;
+ if (m_guestOSFamilyIDs.contains(strFamilyId))
+ {
+ list = m_guestOSTypes.at(m_guestOSFamilyIDs.indexOf(strFamilyId));
+ }
+ else
+ {
+ for (int i = 0; i < m_guestOSFamilyIDs.size(); ++i)
+ list += m_guestOSTypes.at(i);
+ }
+ for (int j = 0; j < list.size(); ++j)
+ if (!list.at(j).GetId().compare(strTypeId))
+ return list.at(j);
+ return CGuestOSType();
+}
+
+QString UICommon::vmGuestOSTypeDescription(const QString &strTypeId) const
+{
+ for (int i = 0; i < m_guestOSFamilyIDs.size(); ++i)
+ {
+ QList<CGuestOSType> list(m_guestOSTypes[i]);
+ for (int j = 0; j < list.size(); ++j)
+ if (!list.at(j).GetId().compare(strTypeId))
+ return list.at(j).GetDescription();
+ }
+ return QString();
+}
+
+/* static */
+bool UICommon::isDOSType(const QString &strOSTypeId)
+{
+ if ( strOSTypeId.left(3) == "dos"
+ || strOSTypeId.left(3) == "win"
+ || strOSTypeId.left(3) == "os2")
+ return true;
+
+ return false;
+}
+
+/* static */
+bool UICommon::switchToMachine(CMachine &comMachine)
+{
+#ifdef VBOX_WS_MAC
+ const ULONG64 id = comMachine.ShowConsoleWindow();
+#else
+ const WId id = (WId)comMachine.ShowConsoleWindow();
+#endif
+ AssertWrapperOk(comMachine);
+ if (!comMachine.isOk())
+ return false;
+
+ // WORKAROUND:
+ // id == 0 means the console window has already done everything
+ // necessary to implement the "show window" semantics.
+ if (id == 0)
+ return true;
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+
+ return UIDesktopWidgetWatchdog::activateWindow(id, true);
+
+#elif defined(VBOX_WS_MAC)
+
+ // WORKAROUND:
+ // This is just for the case were the other process cannot steal
+ // the focus from us. It will send us a PSN so we can try.
+ ProcessSerialNumber psn;
+ psn.highLongOfPSN = id >> 32;
+ psn.lowLongOfPSN = (UInt32)id;
+# ifdef __clang__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ OSErr rc = ::SetFrontProcess(&psn);
+# pragma GCC diagnostic pop
+# else
+ OSErr rc = ::SetFrontProcess(&psn);
+# endif
+ if (!rc)
+ Log(("GUI: %#RX64 couldn't do SetFrontProcess on itself, the selector (we) had to do it...\n", id));
+ else
+ Log(("GUI: Failed to bring %#RX64 to front. rc=%#x\n", id, rc));
+ return !rc;
+
+#else
+
+ return false;
+
+#endif
+}
+
+/* static */
+bool UICommon::launchMachine(CMachine &comMachine, UILaunchMode enmLaunchMode /* = UILaunchMode_Default */)
+{
+ /* Switch to machine window(s) if possible: */
+ if ( comMachine.GetSessionState() == KSessionState_Locked /* precondition for CanShowConsoleWindow() */
+ && comMachine.CanShowConsoleWindow())
+ {
+ switch (uiCommon().uiType())
+ {
+ /* For Selector UI: */
+ case UIType_SelectorUI:
+ {
+ /* Just switch to existing VM window: */
+ return switchToMachine(comMachine);
+ }
+ /* For Runtime UI: */
+ case UIType_RuntimeUI:
+ {
+ /* Only separate UI process can reach that place.
+ * Switch to existing VM window and exit. */
+ switchToMachine(comMachine);
+ return false;
+ }
+ }
+ }
+
+ /* Not for separate UI (which can connect to machine in any state): */
+ if (enmLaunchMode != UILaunchMode_Separate)
+ {
+ /* Make sure machine-state is one of required: */
+ const KMachineState enmState = comMachine.GetState(); NOREF(enmState);
+ AssertMsg( enmState == KMachineState_PoweredOff
+ || enmState == KMachineState_Saved
+ || enmState == KMachineState_Teleported
+ || enmState == KMachineState_Aborted
+ || enmState == KMachineState_AbortedSaved
+ , ("Machine must be PoweredOff/Saved/Teleported/Aborted (%d)", enmState));
+ }
+
+ /* Create empty session instance: */
+ CSession comSession;
+ comSession.createInstance(CLSID_Session);
+ if (comSession.isNull())
+ {
+ msgCenter().cannotOpenSession(comSession);
+ return false;
+ }
+
+ /* Configure environment: */
+ QVector<QString> astrEnv;
+#ifdef VBOX_WS_WIN
+ /* Allow started VM process to be foreground window: */
+ AllowSetForegroundWindow(ASFW_ANY);
+#endif
+#ifdef VBOX_WS_X11
+ /* Make sure VM process will start on the same
+ * display as window this wrapper is called from: */
+ const char *pDisplay = RTEnvGet("DISPLAY");
+ if (pDisplay)
+ astrEnv.append(QString("DISPLAY=%1").arg(pDisplay));
+ const char *pXauth = RTEnvGet("XAUTHORITY");
+ if (pXauth)
+ astrEnv.append(QString("XAUTHORITY=%1").arg(pXauth));
+#endif
+ QString strType;
+ switch (enmLaunchMode)
+ {
+ case UILaunchMode_Default: strType = ""; break;
+ case UILaunchMode_Separate: strType = uiCommon().isSeparateProcess() ? "headless" : "separate"; break;
+ case UILaunchMode_Headless: strType = "headless"; break;
+ default: AssertFailedReturn(false);
+ }
+
+ /* Prepare "VM spawning" progress: */
+ CProgress comProgress = comMachine.LaunchVMProcess(comSession, strType, astrEnv);
+ if (!comMachine.isOk())
+ {
+ /* If the VM is started separately and the VM process is already running, then it is OK. */
+ if (enmLaunchMode == UILaunchMode_Separate)
+ {
+ const KMachineState enmState = comMachine.GetState();
+ if ( enmState >= KMachineState_FirstOnline
+ && enmState <= KMachineState_LastOnline)
+ {
+ /* Already running: */
+ return true;
+ }
+ }
+
+ msgCenter().cannotOpenSession(comMachine);
+ return false;
+ }
+
+ /* Show "VM spawning" progress: */
+ msgCenter().showModalProgressDialog(comProgress, comMachine.GetName(),
+ ":/progress_start_90px.png", 0, 0);
+ if (!comProgress.isOk() || comProgress.GetResultCode() != 0)
+ msgCenter().cannotOpenSession(comProgress, comMachine.GetName());
+
+ /* Unlock machine, close session: */
+ comSession.UnlockMachine();
+
+ /* True finally: */
+ return true;
+}
+
+CSession UICommon::openSession(const QUuid &uId, KLockType lockType /* = KLockType_Shared */)
+{
+ /* Prepare session: */
+ CSession comSession;
+
+ /* Simulate try-catch block: */
+ bool fSuccess = false;
+ do
+ {
+ /* Create empty session instance: */
+ comSession.createInstance(CLSID_Session);
+ if (comSession.isNull())
+ {
+ msgCenter().cannotOpenSession(comSession);
+ break;
+ }
+
+ /* Search for the corresponding machine: */
+ CMachine comMachine = m_comVBox.FindMachine(uId.toString());
+ if (comMachine.isNull())
+ {
+ msgCenter().cannotFindMachineById(m_comVBox, uId);
+ break;
+ }
+
+ if (lockType == KLockType_VM)
+ comSession.SetName("GUI/Qt");
+
+ /* Lock found machine to session: */
+ comMachine.LockMachine(comSession, lockType);
+ if (!comMachine.isOk())
+ {
+ msgCenter().cannotOpenSession(comMachine);
+ break;
+ }
+
+ /* Pass the language ID as the property to the guest: */
+ if (comSession.GetType() == KSessionType_Shared)
+ {
+ CMachine comStartedMachine = comSession.GetMachine();
+ /* Make sure that the language is in two letter code.
+ * Note: if languageId() returns an empty string lang.name() will
+ * return "C" which is an valid language code. */
+ QLocale lang(UITranslator::languageId());
+ comStartedMachine.SetGuestPropertyValue("/VirtualBox/HostInfo/GUI/LanguageID", lang.name());
+ }
+
+ /* Success finally: */
+ fSuccess = true;
+ }
+ while (0);
+ /* Cleanup try-catch block: */
+ if (!fSuccess)
+ comSession.detach();
+
+ /* Return session: */
+ return comSession;
+}
+
+CSession UICommon::tryToOpenSessionFor(CMachine &comMachine)
+{
+ /* Prepare session: */
+ CSession comSession;
+
+ /* Session state unlocked? */
+ if (comMachine.GetSessionState() == KSessionState_Unlocked)
+ {
+ /* Open own 'write' session: */
+ comSession = openSession(comMachine.GetId());
+ AssertReturn(!comSession.isNull(), CSession());
+ comMachine = comSession.GetMachine();
+ }
+ /* Is this a Selector UI call? */
+ else if (uiType() == UIType_SelectorUI)
+ {
+ /* Open existing 'shared' session: */
+ comSession = openExistingSession(comMachine.GetId());
+ AssertReturn(!comSession.isNull(), CSession());
+ comMachine = comSession.GetMachine();
+ }
+ /* Else this is Runtime UI call
+ * which has session locked for itself. */
+
+ /* Return session: */
+ return comSession;
+}
+
+void UICommon::notifyCloudMachineUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QUuid &uId)
+{
+ emit sigCloudMachineUnregistered(strProviderShortName, strProfileName, uId);
+}
+
+void UICommon::notifyCloudMachineRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine)
+{
+ emit sigCloudMachineRegistered(strProviderShortName, strProfileName, comMachine);
+}
+
+void UICommon::enumerateMedia(const CMediumVector &comMedia /* = CMediumVector() */)
+{
+ /* Make sure UICommon is already valid: */
+ AssertReturnVoid(m_fValid);
+ /* Ignore the request during UICommon cleanup: */
+ if (m_fCleaningUp)
+ return;
+ /* Ignore the request during startup snapshot restoring: */
+ if (shouldRestoreCurrentSnapshot())
+ return;
+
+ /* Make sure medium-enumerator is already created: */
+ if (!m_pMediumEnumerator)
+ return;
+
+ /* Redirect request to medium-enumerator under proper lock: */
+ if (m_meCleanupProtectionToken.tryLockForRead())
+ {
+ if (m_pMediumEnumerator)
+ m_pMediumEnumerator->enumerateMedia(comMedia);
+ m_meCleanupProtectionToken.unlock();
+ }
+}
+
+void UICommon::refreshMedia()
+{
+ /* Make sure UICommon is already valid: */
+ AssertReturnVoid(m_fValid);
+ /* Ignore the request during UICommon cleanup: */
+ if (m_fCleaningUp)
+ return;
+ /* Ignore the request during startup snapshot restoring: */
+ if (shouldRestoreCurrentSnapshot())
+ return;
+
+ /* Make sure medium-enumerator is already created: */
+ if (!m_pMediumEnumerator)
+ return;
+ /* Make sure enumeration is not already started: */
+ if (m_pMediumEnumerator->isMediumEnumerationInProgress())
+ return;
+
+ /* We assume it's safe to call it without locking,
+ * since we are performing blocking operation here. */
+ m_pMediumEnumerator->refreshMedia();
+}
+
+bool UICommon::isFullMediumEnumerationRequested() const
+{
+ /* Redirect request to medium-enumerator: */
+ return m_pMediumEnumerator
+ && m_pMediumEnumerator->isFullMediumEnumerationRequested();
+}
+
+bool UICommon::isMediumEnumerationInProgress() const
+{
+ /* Redirect request to medium-enumerator: */
+ return m_pMediumEnumerator
+ && m_pMediumEnumerator->isMediumEnumerationInProgress();
+}
+
+UIMedium UICommon::medium(const QUuid &uMediumID) const
+{
+ if (m_meCleanupProtectionToken.tryLockForRead())
+ {
+ /* Redirect call to medium-enumerator: */
+ UIMedium guiMedium;
+ if (m_pMediumEnumerator)
+ guiMedium = m_pMediumEnumerator->medium(uMediumID);
+ m_meCleanupProtectionToken.unlock();
+ return guiMedium;
+ }
+ return UIMedium();
+}
+
+QList<QUuid> UICommon::mediumIDs() const
+{
+ if (m_meCleanupProtectionToken.tryLockForRead())
+ {
+ /* Redirect call to medium-enumerator: */
+ QList<QUuid> listOfMedia;
+ if (m_pMediumEnumerator)
+ listOfMedia = m_pMediumEnumerator->mediumIDs();
+ m_meCleanupProtectionToken.unlock();
+ return listOfMedia;
+ }
+ return QList<QUuid>();
+}
+
+void UICommon::createMedium(const UIMedium &guiMedium)
+{
+ if (m_meCleanupProtectionToken.tryLockForRead())
+ {
+ /* Create medium in medium-enumerator: */
+ if (m_pMediumEnumerator)
+ m_pMediumEnumerator->createMedium(guiMedium);
+ m_meCleanupProtectionToken.unlock();
+ }
+}
+
+QUuid UICommon::openMedium(UIMediumDeviceType enmMediumType, QString strMediumLocation, QWidget *pParent /* = 0 */)
+{
+ /* Convert to native separators: */
+ strMediumLocation = QDir::toNativeSeparators(strMediumLocation);
+
+ /* Initialize variables: */
+ CVirtualBox comVBox = virtualBox();
+
+ /* Open corresponding medium: */
+ CMedium comMedium = comVBox.OpenMedium(strMediumLocation, mediumTypeToGlobal(enmMediumType), KAccessMode_ReadWrite, false);
+
+ if (comVBox.isOk())
+ {
+ /* Prepare vbox medium wrapper: */
+ UIMedium guiMedium = medium(comMedium.GetId());
+
+ /* First of all we should test if that medium already opened: */
+ if (guiMedium.isNull())
+ {
+ /* And create new otherwise: */
+ guiMedium = UIMedium(comMedium, enmMediumType, KMediumState_Created);
+ createMedium(guiMedium);
+ }
+
+ /* Return guiMedium id: */
+ return guiMedium.id();
+ }
+ else
+ msgCenter().cannotOpenMedium(comVBox, strMediumLocation, pParent);
+
+ return QUuid();
+}
+
+QUuid UICommon::openMediumWithFileOpenDialog(UIMediumDeviceType enmMediumType, QWidget *pParent,
+ const QString &strDefaultFolder /* = QString() */,
+ bool fUseLastFolder /* = false */)
+{
+ /* Initialize variables: */
+ QList<QPair <QString, QString> > filters;
+ QStringList backends;
+ QStringList prefixes;
+ QString strFilter;
+ QString strTitle;
+ QString allType;
+ QString strLastFolder = defaultFolderPathForType(enmMediumType);
+
+ /* For DVDs and Floppies always check first the last recently used medium folder. For hard disk use
+ the caller's setting: */
+ fUseLastFolder = (enmMediumType == UIMediumDeviceType_DVD) || (enmMediumType == UIMediumDeviceType_Floppy);
+
+ switch (enmMediumType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ {
+ filters = HDDBackends(virtualBox());
+ strTitle = tr("Please choose a virtual hard disk file");
+ allType = tr("All virtual hard disk files (%1)");
+ break;
+ }
+ case UIMediumDeviceType_DVD:
+ {
+ filters = DVDBackends(virtualBox());
+ strTitle = tr("Please choose a virtual optical disk file");
+ allType = tr("All virtual optical disk files (%1)");
+ break;
+ }
+ case UIMediumDeviceType_Floppy:
+ {
+ filters = FloppyBackends(virtualBox());
+ strTitle = tr("Please choose a virtual floppy disk file");
+ allType = tr("All virtual floppy disk files (%1)");
+ break;
+ }
+ default:
+ break;
+ }
+ QString strHomeFolder = fUseLastFolder && !strLastFolder.isEmpty() ? strLastFolder :
+ strDefaultFolder.isEmpty() ? homeFolder() : strDefaultFolder;
+
+ /* Prepare filters and backends: */
+ for (int i = 0; i < filters.count(); ++i)
+ {
+ /* Get iterated filter: */
+ QPair<QString, QString> item = filters.at(i);
+ /* Create one backend filter string: */
+ backends << QString("%1 (%2)").arg(item.first).arg(item.second);
+ /* Save the suffix's for the "All" entry: */
+ prefixes << item.second;
+ }
+ if (!prefixes.isEmpty())
+ backends.insert(0, allType.arg(prefixes.join(" ").trimmed()));
+ backends << tr("All files (*)");
+ strFilter = backends.join(";;").trimmed();
+
+ /* Create open file dialog: */
+ QStringList files = QIFileDialog::getOpenFileNames(strHomeFolder, strFilter, pParent, strTitle, 0, true, true);
+
+ /* If dialog has some result: */
+ if (!files.empty() && !files[0].isEmpty())
+ {
+ QUuid uMediumId = openMedium(enmMediumType, files[0], pParent);
+ if (enmMediumType == UIMediumDeviceType_DVD || enmMediumType == UIMediumDeviceType_Floppy ||
+ (enmMediumType == UIMediumDeviceType_HardDisk && fUseLastFolder))
+ updateRecentlyUsedMediumListAndFolder(enmMediumType, medium(uMediumId).location());
+ return uMediumId;
+ }
+ return QUuid();
+}
+
+QUuid UICommon::openMediumCreatorDialog(UIActionPool *pActionPool, QWidget *pParent, UIMediumDeviceType enmMediumType,
+ const QString &strDefaultFolder /* = QString() */,
+ const QString &strMachineName /* = QString() */,
+ const QString &strMachineGuestOSTypeId /*= QString() */)
+{
+ /* Depending on medium-type: */
+ QUuid uMediumId;
+ switch (enmMediumType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ uMediumId = UIWizardNewVD::createVDWithWizard(pParent, strDefaultFolder, strMachineName, strMachineGuestOSTypeId);
+ break;
+ case UIMediumDeviceType_DVD:
+ uMediumId = UIVisoCreatorWidget::createViso(pActionPool, pParent, strDefaultFolder, strMachineName);
+ break;
+ case UIMediumDeviceType_Floppy:
+ uMediumId = UIFDCreationDialog::createFloppyDisk(pParent, strDefaultFolder, strMachineName);
+ break;
+ default:
+ break;
+ }
+ if (uMediumId.isNull())
+ return QUuid();
+
+ /* Update the recent medium list only if the medium type is DVD or floppy: */
+ if (enmMediumType == UIMediumDeviceType_DVD || enmMediumType == UIMediumDeviceType_Floppy)
+ updateRecentlyUsedMediumListAndFolder(enmMediumType, medium(uMediumId).location());
+ return uMediumId;
+}
+
+void UICommon::prepareStorageMenu(QMenu &menu,
+ QObject *pListener, const char *pszSlotName,
+ const CMachine &comMachine, const QString &strControllerName, const StorageSlot &storageSlot)
+{
+ /* Current attachment attributes: */
+ const CMediumAttachment comCurrentAttachment = comMachine.GetMediumAttachment(strControllerName,
+ storageSlot.port,
+ storageSlot.device);
+ const CMedium comCurrentMedium = comCurrentAttachment.GetMedium();
+ const QUuid uCurrentID = comCurrentMedium.isNull() ? QUuid() : comCurrentMedium.GetId();
+ const QString strCurrentLocation = comCurrentMedium.isNull() ? QString() : comCurrentMedium.GetLocation();
+
+ /* Other medium-attachments of same machine: */
+ const CMediumAttachmentVector comAttachments = comMachine.GetMediumAttachments();
+
+ /* Determine device & medium types: */
+ const UIMediumDeviceType enmMediumType = mediumTypeToLocal(comCurrentAttachment.GetType());
+ AssertMsgReturnVoid(enmMediumType != UIMediumDeviceType_Invalid, ("Incorrect storage medium type!\n"));
+
+ /* Prepare open-existing-medium action: */
+ QAction *pActionOpenExistingMedium = menu.addAction(UIIconPool::iconSet(":/select_file_16px.png"),
+ QString(), pListener, pszSlotName);
+ pActionOpenExistingMedium->setData(QVariant::fromValue(UIMediumTarget(strControllerName, comCurrentAttachment.GetPort(),
+ comCurrentAttachment.GetDevice(), enmMediumType)));
+ pActionOpenExistingMedium->setText(QApplication::translate("UIMachineSettingsStorage", "Choose/Create a disk image..."));
+
+
+ /* Prepare open medium file action: */
+ QAction *pActionFileSelector = menu.addAction(UIIconPool::iconSet(":/select_file_16px.png"),
+ QString(), pListener, pszSlotName);
+ pActionFileSelector->setData(QVariant::fromValue(UIMediumTarget(strControllerName, comCurrentAttachment.GetPort(),
+ comCurrentAttachment.GetDevice(), enmMediumType,
+ UIMediumTarget::UIMediumTargetType_WithFileDialog)));
+ pActionFileSelector->setText(QApplication::translate("UIMachineSettingsStorage", "Choose a disk file..."));
+
+
+ /* Insert separator: */
+ menu.addSeparator();
+
+ /* Get existing-host-drive vector: */
+ CMediumVector comMedia;
+ switch (enmMediumType)
+ {
+ case UIMediumDeviceType_DVD: comMedia = host().GetDVDDrives(); break;
+ case UIMediumDeviceType_Floppy: comMedia = host().GetFloppyDrives(); break;
+ default: break;
+ }
+ /* Prepare choose-existing-host-drive actions: */
+ foreach (const CMedium &comMedium, comMedia)
+ {
+ /* Make sure host-drive usage is unique: */
+ bool fIsHostDriveUsed = false;
+ foreach (const CMediumAttachment &comOtherAttachment, comAttachments)
+ {
+ if (comOtherAttachment != comCurrentAttachment)
+ {
+ const CMedium &comOtherMedium = comOtherAttachment.GetMedium();
+ if (!comOtherMedium.isNull() && comOtherMedium.GetId() == comMedium.GetId())
+ {
+ fIsHostDriveUsed = true;
+ break;
+ }
+ }
+ }
+ /* If host-drives usage is unique: */
+ if (!fIsHostDriveUsed)
+ {
+ QAction *pActionChooseHostDrive = menu.addAction(UIMedium(comMedium, enmMediumType).name(), pListener, pszSlotName);
+ pActionChooseHostDrive->setCheckable(true);
+ pActionChooseHostDrive->setChecked(!comCurrentMedium.isNull() && comMedium.GetId() == uCurrentID);
+ pActionChooseHostDrive->setData(QVariant::fromValue(UIMediumTarget(strControllerName,
+ comCurrentAttachment.GetPort(),
+ comCurrentAttachment.GetDevice(),
+ enmMediumType,
+ UIMediumTarget::UIMediumTargetType_WithID,
+ comMedium.GetId().toString())));
+ }
+ }
+
+ /* Get recent-medium list: */
+ QStringList recentMediumList;
+ QStringList recentMediumListUsed;
+ switch (enmMediumType)
+ {
+ case UIMediumDeviceType_HardDisk: recentMediumList = gEDataManager->recentListOfHardDrives(); break;
+ case UIMediumDeviceType_DVD: recentMediumList = gEDataManager->recentListOfOpticalDisks(); break;
+ case UIMediumDeviceType_Floppy: recentMediumList = gEDataManager->recentListOfFloppyDisks(); break;
+ default: break;
+ }
+ /* Prepare choose-recent-medium actions: */
+ foreach (const QString &strRecentMediumLocationBase, recentMediumList)
+ {
+ /* Confirm medium uniqueness: */
+ if (recentMediumListUsed.contains(strRecentMediumLocationBase))
+ continue;
+ /* Mark medium as known: */
+ recentMediumListUsed << strRecentMediumLocationBase;
+ /* Convert separators to native: */
+ const QString strRecentMediumLocation = QDir::toNativeSeparators(strRecentMediumLocationBase);
+ /* Confirm medium presence: */
+ if (!QFile::exists(strRecentMediumLocation))
+ continue;
+ /* Make sure recent-medium usage is unique: */
+ bool fIsRecentMediumUsed = false;
+ if (enmMediumType != UIMediumDeviceType_DVD)
+ {
+ foreach (const CMediumAttachment &otherAttachment, comAttachments)
+ {
+ if (otherAttachment != comCurrentAttachment)
+ {
+ const CMedium &comOtherMedium = otherAttachment.GetMedium();
+ if (!comOtherMedium.isNull() && comOtherMedium.GetLocation() == strRecentMediumLocation)
+ {
+ fIsRecentMediumUsed = true;
+ break;
+ }
+ }
+ }
+ }
+ /* If recent-medium usage is unique: */
+ if (!fIsRecentMediumUsed)
+ {
+ QAction *pActionChooseRecentMedium = menu.addAction(QFileInfo(strRecentMediumLocation).fileName(),
+ pListener, pszSlotName);
+ pActionChooseRecentMedium->setCheckable(true);
+ pActionChooseRecentMedium->setChecked(!comCurrentMedium.isNull() && strRecentMediumLocation == strCurrentLocation);
+ pActionChooseRecentMedium->setData(QVariant::fromValue(UIMediumTarget(strControllerName,
+ comCurrentAttachment.GetPort(),
+ comCurrentAttachment.GetDevice(),
+ enmMediumType,
+ UIMediumTarget::UIMediumTargetType_WithLocation,
+ strRecentMediumLocation)));
+ pActionChooseRecentMedium->setToolTip(strRecentMediumLocation);
+ }
+ }
+
+ /* Last action for optical/floppy attachments only: */
+ if (enmMediumType == UIMediumDeviceType_DVD || enmMediumType == UIMediumDeviceType_Floppy)
+ {
+ /* Insert separator: */
+ menu.addSeparator();
+
+ /* Prepare unmount-current-medium action: */
+ QAction *pActionUnmountMedium = menu.addAction(QString(), pListener, pszSlotName);
+ pActionUnmountMedium->setEnabled(!comCurrentMedium.isNull());
+ pActionUnmountMedium->setData(QVariant::fromValue(UIMediumTarget(strControllerName, comCurrentAttachment.GetPort(),
+ comCurrentAttachment.GetDevice())));
+ pActionUnmountMedium->setText(QApplication::translate("UIMachineSettingsStorage", "Remove disk from virtual drive"));
+ if (enmMediumType == UIMediumDeviceType_DVD)
+ pActionUnmountMedium->setIcon(UIIconPool::iconSet(":/cd_unmount_16px.png", ":/cd_unmount_disabled_16px.png"));
+ else if (enmMediumType == UIMediumDeviceType_Floppy)
+ pActionUnmountMedium->setIcon(UIIconPool::iconSet(":/fd_unmount_16px.png", ":/fd_unmount_disabled_16px.png"));
+ }
+}
+
+void UICommon::updateMachineStorage(const CMachine &comConstMachine, const UIMediumTarget &target, UIActionPool *pActionPool)
+{
+ /* Mount (by default): */
+ bool fMount = true;
+ /* Null medium (by default): */
+ CMedium comMedium;
+ /* With null ID (by default): */
+ QUuid uActualID;
+
+ /* Current mount-target attributes: */
+ const CStorageController comCurrentController = comConstMachine.GetStorageControllerByName(target.name);
+ const KStorageBus enmCurrentStorageBus = comCurrentController.GetBus();
+ const CMediumAttachment comCurrentAttachment = comConstMachine.GetMediumAttachment(target.name, target.port, target.device);
+ const CMedium comCurrentMedium = comCurrentAttachment.GetMedium();
+ const QUuid uCurrentID = comCurrentMedium.isNull() ? QUuid() : comCurrentMedium.GetId();
+ const QString strCurrentLocation = comCurrentMedium.isNull() ? QString() : comCurrentMedium.GetLocation();
+
+ /* Which additional info do we have? */
+ switch (target.type)
+ {
+ /* Do we have an exact ID or do we let the user open a medium? */
+ case UIMediumTarget::UIMediumTargetType_WithID:
+ case UIMediumTarget::UIMediumTargetType_WithFileDialog:
+ case UIMediumTarget::UIMediumTargetType_CreateAdHocVISO:
+ case UIMediumTarget::UIMediumTargetType_CreateFloppyDisk:
+ {
+ /* New mount-target attributes: */
+ QUuid uNewID;
+
+ /* Invoke file-open dialog to choose medium ID: */
+ if (target.mediumType != UIMediumDeviceType_Invalid && target.data.isNull())
+ {
+ /* Keyboard can be captured by machine-view.
+ * So we should clear machine-view focus to let file-open dialog get it.
+ * That way the keyboard will be released too.. */
+ QWidget *pLastFocusedWidget = 0;
+ if (QApplication::focusWidget())
+ {
+ pLastFocusedWidget = QApplication::focusWidget();
+ pLastFocusedWidget->clearFocus();
+ }
+ /* Call for file-open dialog: */
+ const QString strMachineFolder(QFileInfo(comConstMachine.GetSettingsFilePath()).absolutePath());
+ QUuid uMediumID;
+ if (target.type == UIMediumTarget::UIMediumTargetType_WithID)
+ {
+ int iDialogReturn = UIMediumSelector::openMediumSelectorDialog(windowManager().mainWindowShown(), target.mediumType,
+ uCurrentID, uMediumID,
+ strMachineFolder, comConstMachine.GetName(),
+ comConstMachine.GetOSTypeId(), true /*fEnableCreate */,
+ comConstMachine.GetId(), pActionPool);
+ if (iDialogReturn == UIMediumSelector::ReturnCode_LeftEmpty &&
+ (target.mediumType == UIMediumDeviceType_DVD || target.mediumType == UIMediumDeviceType_Floppy))
+ fMount = false;
+ }
+ else if (target.type == UIMediumTarget::UIMediumTargetType_WithFileDialog)
+ {
+ uMediumID = openMediumWithFileOpenDialog(target.mediumType, windowManager().mainWindowShown(),
+ strMachineFolder, false /* fUseLastFolder */);
+ }
+ else if(target.type == UIMediumTarget::UIMediumTargetType_CreateAdHocVISO)
+ uMediumID = UIVisoCreatorWidget::createViso(pActionPool, windowManager().mainWindowShown(),
+ strMachineFolder, comConstMachine.GetName());
+
+ else if(target.type == UIMediumTarget::UIMediumTargetType_CreateFloppyDisk)
+ uMediumID = UIFDCreationDialog::createFloppyDisk(windowManager().mainWindowShown(), strMachineFolder, comConstMachine.GetName());
+
+ /* Return focus back: */
+ if (pLastFocusedWidget)
+ pLastFocusedWidget->setFocus();
+ /* Accept new medium ID: */
+ if (!uMediumID.isNull())
+ uNewID = uMediumID;
+ else
+ /* Else just exit in case left empty is not chosen in medium selector dialog: */
+ if (fMount)
+ return;
+ }
+ /* Use medium ID which was passed: */
+ else if (!target.data.isNull() && target.data != uCurrentID.toString())
+ uNewID = QUuid(target.data);
+
+ /* Should we mount or unmount? */
+ fMount = !uNewID.isNull();
+
+ /* Prepare target medium: */
+ const UIMedium guiMedium = medium(uNewID);
+ comMedium = guiMedium.medium();
+ uActualID = fMount ? uNewID : uCurrentID;
+ break;
+ }
+ /* Do we have a recent location? */
+ case UIMediumTarget::UIMediumTargetType_WithLocation:
+ {
+ /* Open medium by location and get new medium ID if any: */
+ const QUuid uNewID = openMedium(target.mediumType, target.data);
+ /* Else just exit: */
+ if (uNewID.isNull())
+ return;
+
+ /* Should we mount or unmount? */
+ fMount = uNewID != uCurrentID;
+
+ /* Prepare target medium: */
+ const UIMedium guiMedium = fMount ? medium(uNewID) : UIMedium();
+ comMedium = fMount ? guiMedium.medium() : CMedium();
+ uActualID = fMount ? uNewID : uCurrentID;
+ break;
+ }
+ }
+
+ /* Do not unmount hard-drives: */
+ if (target.mediumType == UIMediumDeviceType_HardDisk && !fMount)
+ return;
+
+ /* Get editable machine & session: */
+ CMachine comMachine = comConstMachine;
+ CSession comSession = tryToOpenSessionFor(comMachine);
+
+ /* Remount medium to the predefined port/device: */
+ bool fWasMounted = false;
+ /* Hard drive case: */
+ if (target.mediumType == UIMediumDeviceType_HardDisk)
+ {
+ /* Detaching: */
+ comMachine.DetachDevice(target.name, target.port, target.device);
+ fWasMounted = comMachine.isOk();
+ if (!fWasMounted)
+ msgCenter().cannotDetachDevice(comMachine, UIMediumDeviceType_HardDisk, strCurrentLocation,
+ StorageSlot(enmCurrentStorageBus, target.port, target.device));
+ else
+ {
+ /* Attaching: */
+ comMachine.AttachDevice(target.name, target.port, target.device, KDeviceType_HardDisk, comMedium);
+ fWasMounted = comMachine.isOk();
+ if (!fWasMounted)
+ msgCenter().cannotAttachDevice(comMachine, UIMediumDeviceType_HardDisk, strCurrentLocation,
+ StorageSlot(enmCurrentStorageBus, target.port, target.device));
+ }
+ }
+ /* Optical/floppy drive case: */
+ else
+ {
+ /* Remounting: */
+ comMachine.MountMedium(target.name, target.port, target.device, comMedium, false /* force? */);
+ fWasMounted = comMachine.isOk();
+ if (!fWasMounted)
+ {
+ /* Ask for force remounting: */
+ if (msgCenter().cannotRemountMedium(comMachine, medium(uActualID),
+ fMount, true /* retry? */))
+ {
+ /* Force remounting: */
+ comMachine.MountMedium(target.name, target.port, target.device, comMedium, true /* force? */);
+ fWasMounted = comMachine.isOk();
+ if (!fWasMounted)
+ msgCenter().cannotRemountMedium(comMachine, medium(uActualID),
+ fMount, false /* retry? */);
+ }
+ }
+ }
+
+ /* Save settings: */
+ if (fWasMounted)
+ {
+ comMachine.SaveSettings();
+ if (!comMachine.isOk())
+ msgCenter().cannotSaveMachineSettings(comMachine, windowManager().mainWindowShown());
+ }
+
+ /* Close session to editable comMachine if necessary: */
+ if (!comSession.isNull())
+ comSession.UnlockMachine();
+}
+
+QString UICommon::storageDetails(const CMedium &comMedium, bool fPredictDiff, bool fUseHtml /* = true */)
+{
+ /* Search for corresponding UI medium: */
+ const QUuid uMediumID = comMedium.isNull() ? UIMedium::nullID() : comMedium.GetId();
+ UIMedium guiMedium = medium(uMediumID);
+ if (!comMedium.isNull() && guiMedium.isNull())
+ {
+ /* UI medium may be new and not among cached media, request enumeration: */
+ enumerateMedia(CMediumVector() << comMedium);
+
+ /* Search for corresponding UI medium again: */
+ guiMedium = medium(uMediumID);
+ if (guiMedium.isNull())
+ {
+ /* Medium might be deleted already, return null string: */
+ return QString();
+ }
+ }
+
+ /* For differencing hard-disk we have to request
+ * enumeration of whole tree based in it's root item: */
+ if ( comMedium.isNotNull()
+ && comMedium.GetDeviceType() == KDeviceType_HardDisk)
+ {
+ /* Traverse through parents to root to catch it: */
+ CMedium comRootMedium;
+ CMedium comParentMedium = comMedium.GetParent();
+ while (comParentMedium.isNotNull())
+ {
+ comRootMedium = comParentMedium;
+ comParentMedium = comParentMedium.GetParent();
+ }
+ /* Enumerate root if it's found and wasn't cached: */
+ if (comRootMedium.isNotNull())
+ {
+ const QUuid uRootId = comRootMedium.GetId();
+ if (medium(uRootId).isNull())
+ enumerateMedia(CMediumVector() << comRootMedium);
+ }
+ }
+
+ /* Return UI medium details: */
+ return fUseHtml ? guiMedium.detailsHTML(true /* no diffs? */, fPredictDiff) :
+ guiMedium.details(true /* no diffs? */, fPredictDiff);
+}
+
+void UICommon::updateRecentlyUsedMediumListAndFolder(UIMediumDeviceType enmMediumType, QString strMediumLocation)
+{
+ /** Don't add the medium to extra data if its name is in exclude list, m_recentMediaExcludeList: */
+ foreach (QString strExcludeName, m_recentMediaExcludeList)
+ {
+ if (strMediumLocation.contains(strExcludeName))
+ return;
+ }
+
+ /* Remember the path of the last chosen medium: */
+ switch (enmMediumType)
+ {
+ case UIMediumDeviceType_HardDisk: gEDataManager->setRecentFolderForHardDrives(QFileInfo(strMediumLocation).absolutePath()); break;
+ case UIMediumDeviceType_DVD: gEDataManager->setRecentFolderForOpticalDisks(QFileInfo(strMediumLocation).absolutePath()); break;
+ case UIMediumDeviceType_Floppy: gEDataManager->setRecentFolderForFloppyDisks(QFileInfo(strMediumLocation).absolutePath()); break;
+ default: break;
+ }
+
+ /* Update recently used list: */
+ QStringList recentMediumList;
+ switch (enmMediumType)
+ {
+ case UIMediumDeviceType_HardDisk: recentMediumList = gEDataManager->recentListOfHardDrives(); break;
+ case UIMediumDeviceType_DVD: recentMediumList = gEDataManager->recentListOfOpticalDisks(); break;
+ case UIMediumDeviceType_Floppy: recentMediumList = gEDataManager->recentListOfFloppyDisks(); break;
+ default: break;
+ }
+ if (recentMediumList.contains(strMediumLocation))
+ recentMediumList.removeAll(strMediumLocation);
+ recentMediumList.prepend(strMediumLocation);
+ while(recentMediumList.size() > 5)
+ recentMediumList.removeLast();
+ switch (enmMediumType)
+ {
+ case UIMediumDeviceType_HardDisk: gEDataManager->setRecentListOfHardDrives(recentMediumList); break;
+ case UIMediumDeviceType_DVD: gEDataManager->setRecentListOfOpticalDisks(recentMediumList); break;
+ case UIMediumDeviceType_Floppy: gEDataManager->setRecentListOfFloppyDisks(recentMediumList); break;
+ default: break;
+ }
+ emit sigRecentMediaListUpdated(enmMediumType);
+}
+
+QString UICommon::defaultFolderPathForType(UIMediumDeviceType enmMediumType)
+{
+ QString strLastFolder;
+ switch (enmMediumType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ strLastFolder = gEDataManager->recentFolderForHardDrives();
+ if (strLastFolder.isEmpty())
+ strLastFolder = gEDataManager->recentFolderForOpticalDisks();
+ if (strLastFolder.isEmpty())
+ strLastFolder = gEDataManager->recentFolderForFloppyDisks();
+ break;
+ case UIMediumDeviceType_DVD:
+ strLastFolder = gEDataManager->recentFolderForOpticalDisks();
+ if (strLastFolder.isEmpty())
+ strLastFolder = gEDataManager->recentFolderForFloppyDisks();
+ if (strLastFolder.isEmpty())
+ strLastFolder = gEDataManager->recentFolderForHardDrives();
+ break;
+ case UIMediumDeviceType_Floppy:
+ strLastFolder = gEDataManager->recentFolderForFloppyDisks();
+ if (strLastFolder.isEmpty())
+ strLastFolder = gEDataManager->recentFolderForOpticalDisks();
+ if (strLastFolder.isEmpty())
+ strLastFolder = gEDataManager->recentFolderForHardDrives();
+ break;
+ default:
+ break;
+ }
+
+ if (strLastFolder.isEmpty())
+ return virtualBox().GetSystemProperties().GetDefaultMachineFolder();
+
+ return strLastFolder;
+}
+
+#ifdef RT_OS_LINUX
+/* static */
+void UICommon::checkForWrongUSBMounted()
+{
+ /* Make sure '/proc/mounts' exists and can be opened: */
+ QFile file("/proc/mounts");
+ if (!file.exists() || !file.open(QIODevice::ReadOnly | QIODevice::Text))
+ return;
+
+ /* Fetch contents: */
+ QStringList contents;
+ for (;;)
+ {
+ QByteArray line = file.readLine();
+ if (line.isEmpty())
+ break;
+ contents << line;
+ }
+ /* Grep contents for usbfs presence: */
+ QStringList grep1(contents.filter("/sys/bus/usb/drivers"));
+ QStringList grep2(grep1.filter("usbfs"));
+ if (grep2.isEmpty())
+ return;
+
+ /* Show corresponding warning: */
+ msgCenter().warnAboutWrongUSBMounted();
+}
+#endif /* RT_OS_LINUX */
+
+/* static */
+QString UICommon::usbDetails(const CUSBDevice &comDevice)
+{
+ QString strDetails;
+ if (comDevice.isNull())
+ strDetails = tr("Unknown device", "USB device details");
+ else
+ {
+ QVector<QString> devInfoVector = comDevice.GetDeviceInfo();
+ QString strManufacturer;
+ QString strProduct;
+
+ if (devInfoVector.size() >= 1)
+ strManufacturer = devInfoVector[0].trimmed();
+ if (devInfoVector.size() >= 2)
+ strProduct = devInfoVector[1].trimmed();
+
+ if (strManufacturer.isEmpty() && strProduct.isEmpty())
+ {
+ strDetails =
+ tr("Unknown device %1:%2", "USB device details")
+ .arg(QString::number(comDevice.GetVendorId(), 16).toUpper().rightJustified(4, '0'))
+ .arg(QString::number(comDevice.GetProductId(), 16).toUpper().rightJustified(4, '0'));
+ }
+ else
+ {
+ if (strProduct.toUpper().startsWith(strManufacturer.toUpper()))
+ strDetails = strProduct;
+ else
+ strDetails = strManufacturer + " " + strProduct;
+ }
+ ushort iRev = comDevice.GetRevision();
+ if (iRev != 0)
+ {
+ strDetails += " [";
+ strDetails += QString::number(iRev, 16).toUpper().rightJustified(4, '0');
+ strDetails += "]";
+ }
+ }
+
+ return strDetails.trimmed();
+}
+
+/* static */
+QString UICommon::usbToolTip(const CUSBDevice &comDevice)
+{
+ QString strTip =
+ tr("<nobr>Vendor ID: %1</nobr><br>"
+ "<nobr>Product ID: %2</nobr><br>"
+ "<nobr>Revision: %3</nobr>", "USB device tooltip")
+ .arg(QString::number(comDevice.GetVendorId(), 16).toUpper().rightJustified(4, '0'))
+ .arg(QString::number(comDevice.GetProductId(), 16).toUpper().rightJustified(4, '0'))
+ .arg(QString::number(comDevice.GetRevision(), 16).toUpper().rightJustified(4, '0'));
+
+ const QString strSerial = comDevice.GetSerialNumber();
+ if (!strSerial.isEmpty())
+ strTip += QString(tr("<br><nobr>Serial No. %1</nobr>", "USB device tooltip"))
+ .arg(strSerial);
+
+ /* Add the state field if it's a host USB device: */
+ CHostUSBDevice hostDev(comDevice);
+ if (!hostDev.isNull())
+ {
+ strTip += QString(tr("<br><nobr>State: %1</nobr>", "USB device tooltip"))
+ .arg(gpConverter->toString(hostDev.GetState()));
+ }
+
+ return strTip;
+}
+
+/* static */
+QString UICommon::usbToolTip(const CUSBDeviceFilter &comFilter)
+{
+ QString strTip;
+
+ const QString strVendorId = comFilter.GetVendorId();
+ if (!strVendorId.isEmpty())
+ strTip += tr("<nobr>Vendor ID: %1</nobr>", "USB filter tooltip")
+ .arg(strVendorId);
+
+ const QString strProductId = comFilter.GetProductId();
+ if (!strProductId.isEmpty())
+ strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Product ID: %2</nobr>", "USB filter tooltip")
+ .arg(strProductId);
+
+ const QString strRevision = comFilter.GetRevision();
+ if (!strRevision.isEmpty())
+ strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Revision: %3</nobr>", "USB filter tooltip")
+ .arg(strRevision);
+
+ const QString strProduct = comFilter.GetProduct();
+ if (!strProduct.isEmpty())
+ strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Product: %4</nobr>", "USB filter tooltip")
+ .arg(strProduct);
+
+ const QString strManufacturer = comFilter.GetManufacturer();
+ if (!strManufacturer.isEmpty())
+ strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Manufacturer: %5</nobr>", "USB filter tooltip")
+ .arg(strManufacturer);
+
+ const QString strSerial = comFilter.GetSerialNumber();
+ if (!strSerial.isEmpty())
+ strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Serial No.: %1</nobr>", "USB filter tooltip")
+ .arg(strSerial);
+
+ const QString strPort = comFilter.GetPort();
+ if (!strPort.isEmpty())
+ strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Port: %1</nobr>", "USB filter tooltip")
+ .arg(strPort);
+
+ /* Add the state field if it's a host USB device: */
+ CHostUSBDevice hostDev(comFilter);
+ if (!hostDev.isNull())
+ {
+ strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>State: %1</nobr>", "USB filter tooltip")
+ .arg(gpConverter->toString(hostDev.GetState()));
+ }
+
+ return strTip;
+}
+
+/* static */
+QString UICommon::usbToolTip(const CHostVideoInputDevice &comWebcam)
+{
+ QStringList records;
+
+ const QString strName = comWebcam.GetName();
+ if (!strName.isEmpty())
+ records << strName;
+
+ const QString strPath = comWebcam.GetPath();
+ if (!strPath.isEmpty())
+ records << strPath;
+
+ return records.join("<br>");
+}
+
+int UICommon::supportedRecordingFeatures() const
+{
+ int iSupportedFlag = 0;
+ CSystemProperties comProperties = virtualBox().GetSystemProperties();
+ foreach (const KRecordingFeature &enmFeature, comProperties.GetSupportedRecordingFeatures())
+ iSupportedFlag |= enmFeature;
+ return iSupportedFlag;
+}
+
+/* static */
+QString UICommon::helpFile()
+{
+#if defined (VBOX_WITH_QHELP_VIEWER)
+ const QString strName = "UserManual";
+ const QString strSuffix = "qhc";
+#else
+ #if defined(VBOX_WS_WIN)
+ const QString strName = "VirtualBox";
+ const QString strSuffix = "chm";
+ #elif defined(VBOX_WS_MAC)
+ const QString strName = "UserManual";
+ const QString strSuffix = "pdf";
+ #elif defined(VBOX_WS_X11)
+ //# if defined(VBOX_OSE) || !defined(VBOX_WITH_KCHMVIEWER)
+ const QString strName = "UserManual";
+ const QString strSuffix = "pdf";
+ #endif
+#endif
+ /* Where are the docs located? */
+ char szDocsPath[RTPATH_MAX];
+ int rc = RTPathAppDocs(szDocsPath, sizeof(szDocsPath));
+ AssertRC(rc);
+
+ /* Make sure that the language is in two letter code.
+ * Note: if languageId() returns an empty string lang.name() will
+ * return "C" which is an valid language code. */
+ QLocale lang(UITranslator::languageId());
+
+ /* Construct the path and the filename: */
+ QString strManual = QString("%1/%2_%3.%4").arg(szDocsPath)
+ .arg(strName)
+ .arg(lang.name())
+ .arg(strSuffix);
+
+ /* Check if a help file with that name exists: */
+ QFileInfo fi(strManual);
+ if (fi.exists())
+ return strManual;
+
+ /* Fall back to the standard: */
+ strManual = QString("%1/%2.%4").arg(szDocsPath)
+ .arg(strName)
+ .arg(strSuffix);
+ return strManual;
+}
+
+/* static */
+QString UICommon::documentsPath()
+{
+ QString strPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
+ QDir dir(strPath);
+ if (dir.exists())
+ return QDir::cleanPath(dir.canonicalPath());
+ else
+ {
+ dir.setPath(QDir::homePath() + "/Documents");
+ if (dir.exists())
+ return QDir::cleanPath(dir.canonicalPath());
+ else
+ return QDir::homePath();
+ }
+}
+
+/* static */
+bool UICommon::hasAllowedExtension(const QString &strFileName, const QStringList &extensions)
+{
+ foreach (const QString &strExtension, extensions)
+ if (strFileName.endsWith(strExtension, Qt::CaseInsensitive))
+ return true;
+ return false;
+}
+
+/* static */
+QString UICommon::findUniqueFileName(const QString &strFullFolderPath, const QString &strBaseFileName)
+{
+ QDir folder(strFullFolderPath);
+ if (!folder.exists())
+ return strBaseFileName;
+ QFileInfoList folderContent = folder.entryInfoList();
+ QSet<QString> fileNameSet;
+ foreach (const QFileInfo &fileInfo, folderContent)
+ {
+ /* Remove the extension : */
+ fileNameSet.insert(fileInfo.completeBaseName());
+ }
+ int iSuffix = 0;
+ QString strNewName(strBaseFileName);
+ while (fileNameSet.contains(strNewName))
+ {
+ strNewName = strBaseFileName + QString("_") + QString::number(++iSuffix);
+ }
+ return strNewName;
+}
+
+/* static */
+void UICommon::setMinimumWidthAccordingSymbolCount(QSpinBox *pSpinBox, int cCount)
+{
+ /* Shame on Qt it hasn't stuff for tuning
+ * widget size suitable for reflecting content of desired size.
+ * For example QLineEdit, QSpinBox and similar widgets should have a methods
+ * to strict the minimum width to reflect at least [n] symbols. */
+
+ /* Load options: */
+ QStyleOptionSpinBox option;
+ option.initFrom(pSpinBox);
+
+ /* Acquire edit-field rectangle: */
+ QRect rect = pSpinBox->style()->subControlRect(QStyle::CC_SpinBox,
+ &option,
+ QStyle::SC_SpinBoxEditField,
+ pSpinBox);
+
+ /* Calculate minimum-width magic: */
+ const int iSpinBoxWidth = pSpinBox->width();
+ const int iSpinBoxEditFieldWidth = rect.width();
+ const int iSpinBoxDelta = qMax(0, iSpinBoxWidth - iSpinBoxEditFieldWidth);
+ QFontMetrics metrics(pSpinBox->font(), pSpinBox);
+ const QString strDummy(cCount, '0');
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const int iTextWidth = metrics.horizontalAdvance(strDummy);
+#else
+ const int iTextWidth = metrics.width(strDummy);
+#endif
+
+ /* Tune spin-box minimum-width: */
+ pSpinBox->setMinimumWidth(iTextWidth + iSpinBoxDelta);
+}
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+/* static */
+bool UICommon::isWddmCompatibleOsType(const QString &strGuestOSTypeId)
+{
+ return strGuestOSTypeId.startsWith("WindowsVista")
+ || strGuestOSTypeId.startsWith("Windows7")
+ || strGuestOSTypeId.startsWith("Windows8")
+ || strGuestOSTypeId.startsWith("Windows81")
+ || strGuestOSTypeId.startsWith("Windows10")
+ || strGuestOSTypeId.startsWith("Windows11")
+ || strGuestOSTypeId.startsWith("Windows2008")
+ || strGuestOSTypeId.startsWith("Windows2012")
+ || strGuestOSTypeId.startsWith("Windows2016")
+ || strGuestOSTypeId.startsWith("Windows2019");
+}
+#endif /* VBOX_WITH_3D_ACCELERATION */
+
+/* static */
+quint64 UICommon::requiredVideoMemory(const QString &strGuestOSTypeId, int cMonitors /* = 1 */)
+{
+ /* We create a list of the size of all available host monitors. This list
+ * is sorted by value and by starting with the biggest one, we calculate
+ * the memory requirements for every guest screen. This is of course not
+ * correct, but as we can't predict on which host screens the user will
+ * open the guest windows, this is the best assumption we can do, cause it
+ * is the worst case. */
+ const int cHostScreens = UIDesktopWidgetWatchdog::screenCount();
+ QVector<int> screenSize(qMax(cMonitors, cHostScreens), 0);
+ for (int i = 0; i < cHostScreens; ++i)
+ {
+ QRect r = gpDesktop->screenGeometry(i);
+ screenSize[i] = r.width() * r.height();
+ }
+ /* Now sort the vector: */
+ std::sort(screenSize.begin(), screenSize.end(), std::greater<int>());
+ /* For the case that there are more guest screens configured then host
+ * screens available, replace all zeros with the greatest value in the
+ * vector. */
+ for (int i = 0; i < screenSize.size(); ++i)
+ if (screenSize.at(i) == 0)
+ screenSize.replace(i, screenSize.at(0));
+
+ quint64 uNeedBits = 0;
+ for (int i = 0; i < cMonitors; ++i)
+ {
+ /* Calculate summary required memory amount in bits: */
+ uNeedBits += (screenSize.at(i) * /* with x height */
+ 32 + /* we will take the maximum possible bpp for now */
+ 8 * _1M) + /* current cache per screen - may be changed in future */
+ 8 * 4096; /* adapter info */
+ }
+ /* Translate value into megabytes with rounding to highest side: */
+ quint64 uNeedMBytes = uNeedBits % (8 * _1M)
+ ? uNeedBits / (8 * _1M) + 1
+ : uNeedBits / (8 * _1M) /* convert to megabytes */;
+
+ if (strGuestOSTypeId.startsWith("Windows"))
+ {
+ /* Windows guests need offscreen VRAM too for graphics acceleration features: */
+#ifdef VBOX_WITH_3D_ACCELERATION
+ if (isWddmCompatibleOsType(strGuestOSTypeId))
+ {
+ /* WDDM mode, there are two surfaces for each screen: shadow & primary: */
+ uNeedMBytes *= 3;
+ }
+ else
+#endif /* VBOX_WITH_3D_ACCELERATION */
+ {
+ uNeedMBytes *= 2;
+ }
+ }
+
+ return uNeedMBytes * _1M;
+}
+
+/* static */
+void UICommon::setHelpKeyword(QObject *pObject, const QString &strHelpKeyword)
+{
+ if (pObject)
+ pObject->setProperty("helpkeyword", strHelpKeyword);
+}
+
+/* static */
+QString UICommon::helpKeyword(const QObject *pObject)
+{
+ if (!pObject)
+ return QString();
+ return pObject->property("helpkeyword").toString();
+}
+
+bool UICommon::openURL(const QString &strUrl) const
+{
+ /** Service event. */
+ class ServiceEvent : public QEvent
+ {
+ public:
+
+ /** Constructs service event on th basis of passed @a fResult. */
+ ServiceEvent(bool fResult)
+ : QEvent(QEvent::User)
+ , m_fResult(fResult)
+ {}
+
+ /** Returns the result which event brings. */
+ bool result() const { return m_fResult; }
+
+ private:
+
+ /** Holds the result which event brings. */
+ bool m_fResult;
+ };
+
+ /** Service client object. */
+ class ServiceClient : public QEventLoop
+ {
+ public:
+
+ /** Constructs service client on the basis of passed @a fResult. */
+ ServiceClient()
+ : m_fResult(false)
+ {}
+
+ /** Returns the result which event brings. */
+ bool result() const { return m_fResult; }
+
+ private:
+
+ /** Handles any Qt @a pEvent. */
+ bool event(QEvent *pEvent)
+ {
+ /* Handle service event: */
+ if (pEvent->type() == QEvent::User)
+ {
+ ServiceEvent *pServiceEvent = static_cast<ServiceEvent*>(pEvent);
+ m_fResult = pServiceEvent->result();
+ pServiceEvent->accept();
+ quit();
+ return true;
+ }
+ return false;
+ }
+
+ bool m_fResult;
+ };
+
+ /** Service server object. */
+ class ServiceServer : public QThread
+ {
+ public:
+
+ /** Constructs service server on the basis of passed @a client and @a strUrl. */
+ ServiceServer(ServiceClient &client, const QString &strUrl)
+ : m_client(client), m_strUrl(strUrl) {}
+
+ private:
+
+ /** Executes thread task. */
+ void run()
+ {
+ QApplication::postEvent(&m_client, new ServiceEvent(QDesktopServices::openUrl(m_strUrl)));
+ }
+
+ /** Holds the client reference. */
+ ServiceClient &m_client;
+ /** Holds the URL to be processed. */
+ const QString &m_strUrl;
+ };
+
+ /* Create client & server: */
+ ServiceClient client;
+ ServiceServer server(client, strUrl);
+ server.start();
+ client.exec();
+ server.wait();
+
+ /* Acquire client result: */
+ bool fResult = client.result();
+ if (!fResult)
+ UINotificationMessage::cannotOpenURL(strUrl);
+
+ return fResult;
+}
+
+void UICommon::sltGUILanguageChange(QString strLanguage)
+{
+ /* Make sure medium-enumeration is not in progress! */
+ AssertReturnVoid(!isMediumEnumerationInProgress());
+ /* Load passed language: */
+ UITranslator::loadLanguage(strLanguage);
+}
+
+void UICommon::sltHandleMediumCreated(const CMedium &comMedium)
+{
+ /* Acquire device type: */
+ const KDeviceType enmDeviceType = comMedium.GetDeviceType();
+ if (!comMedium.isOk())
+ UINotificationMessage::cannotAcquireMediumParameter(comMedium);
+ else
+ {
+ /* Convert to medium type: */
+ const UIMediumDeviceType enmMediumType = mediumTypeToLocal(enmDeviceType);
+
+ /* Make sure we cached created medium in GUI: */
+ createMedium(UIMedium(comMedium, enmMediumType, KMediumState_Created));
+ }
+}
+
+void UICommon::sltHandleMachineCreated(const CMachine &comMachine)
+{
+ /* Register created machine. */
+ CVirtualBox comVBox = virtualBox();
+ comVBox.RegisterMachine(comMachine);
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotRegisterMachine(comVBox, comMachine.GetName());
+}
+
+void UICommon::sltHandleCloudMachineAdded(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine)
+{
+ /* Make sure we cached added cloud VM in GUI: */
+ notifyCloudMachineRegistered(strProviderShortName,
+ strProfileName,
+ comMachine);
+}
+
+bool UICommon::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /** @todo Just use the QIWithRetranslateUI3 template wrapper. */
+
+ if ( pEvent->type() == QEvent::LanguageChange
+ && pObject->isWidgetType()
+ && static_cast<QWidget*>(pObject)->isTopLevel())
+ {
+ /* Catch the language change event before any other widget gets it in
+ * order to invalidate cached string resources (like the details view
+ * templates) that may be used by other widgets. */
+ QWidgetList list = QApplication::topLevelWidgets();
+ if (list.first() == pObject)
+ {
+ /* Call this only once per every language change (see
+ * QApplication::installTranslator() for details): */
+ retranslateUi();
+ }
+ }
+
+ /* Call to base-class: */
+ return QObject::eventFilter(pObject, pEvent);
+}
+
+
+void UICommon::sltHandleFontScaleFactorChanged(int iFontScaleFactor)
+{
+ QFont appFont = qApp->font();
+
+ if (iOriginalFontPixelSize != -1)
+ appFont.setPixelSize(iFontScaleFactor / 100.f * iOriginalFontPixelSize);
+ else
+ appFont.setPointSize(iFontScaleFactor / 100.f * iOriginalFontPointSize);
+ qApp->setFont(appFont);
+}
+
+void UICommon::retranslateUi()
+{
+ /* Re-enumerate uimedium since they contain some translations too: */
+ if (m_fValid)
+ refreshMedia();
+
+#ifdef VBOX_WS_X11
+ // WORKAROUND:
+ // As X11 do not have functionality for providing human readable key names,
+ // we keep a table of them, which must be updated when the language is changed.
+ UINativeHotKey::retranslateKeyNames();
+#endif
+}
+
+#ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
+void UICommon::sltHandleCommitDataRequest(QSessionManager &manager)
+{
+ LogRel(("GUI: UICommon: Commit data request...\n"));
+
+ /* Ask listener to commit data: */
+ emit sigAskToCommitData();
+# ifdef VBOX_WS_WIN
+ m_fDataCommitted = true;
+# endif
+
+ /* Depending on UI type: */
+ switch (uiType())
+ {
+ /* For Runtime UI: */
+ case UIType_RuntimeUI:
+ {
+ /* Thin clients will be able to shutdown properly,
+ * but for fat clients: */
+ if (!isSeparateProcess())
+ {
+# if defined(VBOX_WS_MAC) && defined(VBOX_IS_QT6_OR_LATER) /** @todo qt6: ... */
+ /* This code prevents QWindowSystemInterface::handleApplicationTermination
+ for running, so among other things QApplication::closeAllWindows isn't
+ called and we're somehow stuck in a half closed down state. That said,
+ just disabling this isn't sufficent, there we also have to accept() the
+ QCloseEvent in UIMachineWindow. */
+ /** @todo qt6: This isn't quite the right fix, I bet... I'm sure I haven't
+ * quite understood all that's going on here. So, leaving this for
+ * the real GUI experts to look into... :-) */
+# else
+ // WORKAROUND:
+ // We can't save VM state in one go for fat clients, so we have to ask session manager to cancel shutdown.
+ // To next major release this should be removed in any case, since there will be no fat clients after all.
+ manager.cancel();
+
+# ifdef VBOX_WS_WIN
+ // WORKAROUND:
+ // In theory that's Qt5 who should allow us to provide canceling reason as well, but that functionality
+ // seems to be missed in Windows platform plugin, so we are making that ourselves.
+ NativeWindowSubsystem::ShutdownBlockReasonCreateAPI((HWND)windowManager().mainWindowShown()->winId(), L"VM is still running.");
+# endif
+# endif
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+#endif /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+void UICommon::sltHandleVBoxSVCAvailabilityChange(bool fAvailable)
+{
+ /* Make sure the VBoxSVC availability changed: */
+ if (m_fVBoxSVCAvailable == fAvailable)
+ return;
+
+ /* Cache the new VBoxSVC availability value: */
+ m_fVBoxSVCAvailable = fAvailable;
+
+ /* If VBoxSVC is not available: */
+ if (!m_fVBoxSVCAvailable)
+ {
+ /* Mark wrappers invalid: */
+ m_fWrappersValid = false;
+ /* Re-fetch corresponding CVirtualBox to restart VBoxSVC: */
+ m_comVBox = m_comVBoxClient.GetVirtualBox();
+ if (!m_comVBoxClient.isOk())
+ {
+ // The proper behavior would be to show the message and to exit the app, e.g.:
+ // msgCenter().cannotAcquireVirtualBox(m_comVBoxClient);
+ // return QApplication::quit();
+ // But CVirtualBox is still NULL in current Main implementation,
+ // and this call do not restart anything, so we are waiting
+ // for subsequent event about VBoxSVC is available again.
+ }
+ }
+ /* If VBoxSVC is available: */
+ else
+ {
+ if (!m_fWrappersValid)
+ {
+ /* Re-fetch corresponding CVirtualBox: */
+ m_comVBox = m_comVBoxClient.GetVirtualBox();
+ if (!m_comVBoxClient.isOk())
+ {
+ msgCenter().cannotAcquireVirtualBox(m_comVBoxClient);
+ return QApplication::quit();
+ }
+ /* Re-init wrappers: */
+ comWrappersReinit();
+
+ /* For Selector UI: */
+ if (uiType() == UIType_SelectorUI)
+ {
+ /* Recreate Main event listeners: */
+ UIVirtualBoxEventHandler::destroy();
+ UIVirtualBoxClientEventHandler::destroy();
+ UIExtraDataManager::destroy();
+ UIExtraDataManager::instance();
+ UIVirtualBoxEventHandler::instance();
+ UIVirtualBoxClientEventHandler::instance();
+ /* Ask UIStarter to restart UI: */
+ emit sigAskToRestartUI();
+ }
+ }
+ }
+
+ /* Notify listeners about the VBoxSVC availability change: */
+ emit sigVBoxSVCAvailabilityChange();
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+
+# define UICOMMON_DBG_CFG_VAR_FALSE (0)
+# define UICOMMON_DBG_CFG_VAR_TRUE (1)
+# define UICOMMON_DBG_CFG_VAR_MASK (1)
+# define UICOMMON_DBG_CFG_VAR_CMD_LINE RT_BIT(3)
+# define UICOMMON_DBG_CFG_VAR_DONE RT_BIT(4)
+
+void UICommon::initDebuggerVar(int *piDbgCfgVar, const char *pszEnvVar, const char *pszExtraDataName, bool fDefault)
+{
+ QString strEnvValue;
+ char szEnvValue[256];
+ int rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, szEnvValue, sizeof(szEnvValue), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ strEnvValue = QString::fromUtf8(&szEnvValue[0]).toLower().trimmed();
+ if (strEnvValue.isEmpty())
+ strEnvValue = "yes";
+ }
+ else if (rc != VERR_ENV_VAR_NOT_FOUND)
+ strEnvValue = "veto";
+
+ QString strExtraValue = m_comVBox.GetExtraData(pszExtraDataName).toLower().trimmed();
+ if (strExtraValue.isEmpty())
+ strExtraValue = QString();
+
+ if ( strEnvValue.contains("veto") || strExtraValue.contains("veto"))
+ *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_FALSE;
+ else if (strEnvValue.isNull() && strExtraValue.isNull())
+ *piDbgCfgVar = fDefault ? UICOMMON_DBG_CFG_VAR_TRUE : UICOMMON_DBG_CFG_VAR_FALSE;
+ else
+ {
+ QString *pStr = !strEnvValue.isEmpty() ? &strEnvValue : &strExtraValue;
+ if ( pStr->startsWith("y") // yes
+ || pStr->startsWith("e") // enabled
+ || pStr->startsWith("t") // true
+ || pStr->startsWith("on")
+ || pStr->toLongLong() != 0)
+ *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_TRUE;
+ else if ( pStr->startsWith("n") // o
+ || pStr->startsWith("d") // disable
+ || pStr->startsWith("f") // false
+ || pStr->startsWith("off")
+ || pStr->contains("veto") /* paranoia */
+ || pStr->toLongLong() == 0)
+ *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_FALSE;
+ else
+ {
+ LogFunc(("Ignoring unknown value '%s' for '%s'\n", pStr->toUtf8().constData(), pStr == &strEnvValue ? pszEnvVar : pszExtraDataName));
+ *piDbgCfgVar = fDefault ? UICOMMON_DBG_CFG_VAR_TRUE : UICOMMON_DBG_CFG_VAR_FALSE;
+ }
+ }
+}
+
+void UICommon::setDebuggerVar(int *piDbgCfgVar, bool fState)
+{
+ if (!(*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_DONE))
+ *piDbgCfgVar = (fState ? UICOMMON_DBG_CFG_VAR_TRUE : UICOMMON_DBG_CFG_VAR_FALSE)
+ | UICOMMON_DBG_CFG_VAR_CMD_LINE;
+}
+
+bool UICommon::isDebuggerWorker(int *piDbgCfgVar, const char *pszExtraDataName) const
+{
+ if (!(*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_DONE))
+ {
+ const QString str = gEDataManager->debugFlagValue(pszExtraDataName);
+ if (str.contains("veto"))
+ *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_FALSE;
+ else if (str.isEmpty() || (*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_CMD_LINE))
+ *piDbgCfgVar |= UICOMMON_DBG_CFG_VAR_DONE;
+ else if ( str.startsWith("y") // yes
+ || str.startsWith("e") // enabled
+ || str.startsWith("t") // true
+ || str.startsWith("on")
+ || str.toLongLong() != 0)
+ *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_TRUE;
+ else if ( str.startsWith("n") // no
+ || str.startsWith("d") // disable
+ || str.startsWith("f") // false
+ || str.toLongLong() == 0)
+ *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_FALSE;
+ else
+ *piDbgCfgVar |= UICOMMON_DBG_CFG_VAR_DONE;
+ }
+
+ return (*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_MASK) == UICOMMON_DBG_CFG_VAR_TRUE;
+}
+
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+void UICommon::comWrappersReinit()
+{
+ /* Re-fetch corresponding objects/values: */
+ m_comHost = virtualBox().GetHost();
+ m_strHomeFolder = virtualBox().GetHomeFolder();
+
+ /* Re-initialize guest OS Type list: */
+ m_guestOSFamilyIDs.clear();
+ m_guestOSTypes.clear();
+ const CGuestOSTypeVector guestOSTypes = m_comVBox.GetGuestOSTypes();
+ const int cGuestOSTypeCount = guestOSTypes.size();
+ AssertMsg(cGuestOSTypeCount > 0, ("Number of OS types must not be zero"));
+ if (cGuestOSTypeCount > 0)
+ {
+ /* Here we ASSUME the 'Other' types are always the first,
+ * so we remember them and will append them to the list when finished.
+ * We do a two pass, first adding the specific types, then the two 'Other' types. */
+ for (int j = 0; j < 2; ++j)
+ {
+ int cMax = j == 0 ? cGuestOSTypeCount : RT_MIN(2, cGuestOSTypeCount);
+ for (int i = j == 0 ? 2 : 0; i < cMax; ++i)
+ {
+ const CGuestOSType os = guestOSTypes.at(i);
+ const QString strFamilyID = os.GetFamilyId();
+ const QString strFamilyDescription = os.GetFamilyDescription();
+ if (!m_guestOSFamilyIDs.contains(strFamilyID))
+ {
+ m_guestOSFamilyIDs << strFamilyID;
+ m_guestOSFamilyDescriptions[strFamilyID] = strFamilyDescription;
+ m_guestOSTypes << QList<CGuestOSType>();
+ }
+ m_guestOSTypes[m_guestOSFamilyIDs.indexOf(strFamilyID)].append(os);
+ }
+ }
+ }
+
+ /* Mark wrappers valid: */
+ m_fWrappersValid = true;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UICommon.h b/src/VBox/Frontends/VirtualBox/src/globals/UICommon.h
new file mode 100644
index 00000000..c04e3ac1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UICommon.h
@@ -0,0 +1,776 @@
+/* $Id: UICommon.h $ */
+/** @file
+ * VBox Qt GUI - UICommon class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UICommon_h
+#define FEQT_INCLUDED_SRC_globals_UICommon_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QReadWriteLock>
+#include <QObject>
+
+/* GUI includes: */
+#include "UIDefs.h"
+#include "UILibraryDefs.h"
+#include "UIMediumDefs.h"
+#ifdef VBOX_WS_X11
+# include "VBoxUtils-x11.h"
+#endif
+
+/* COM includes: */
+#include "CGuestOSType.h"
+#include "CHost.h"
+#include "CMedium.h"
+#include "CSession.h"
+#include "CVirtualBox.h"
+#include "CVirtualBoxClient.h"
+
+/* Other VBox includes: */
+#include "VBox/com/Guid.h"
+
+/* Forward declarations: */
+class QGraphicsWidget;
+class QMenu;
+class QSessionManager;
+class QSpinBox;
+class QToolButton;
+class CCloudMachine;
+class CHostVideoInputDevice;
+class CMachine;
+class CUSBDevice;
+class UIActionPool;
+class UIMedium;
+class UIMediumEnumerator;
+class UIThreadPool;
+
+/** QObject subclass containing common GUI functionality. */
+class SHARED_LIBRARY_STUFF UICommon : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name Common stuff.
+ * @{ */
+ /** Asks #UIStarter listener to restart UI. */
+ void sigAskToRestartUI();
+ /** Asks #UIStarter listener to close UI. */
+ void sigAskToCloseUI();
+
+ /** Notifies listeners about the VBoxSVC availability change. */
+ void sigVBoxSVCAvailabilityChange();
+
+ /** Asks listeners to commit data. */
+ void sigAskToCommitData();
+ /** Asks listeners to detach COM. */
+ void sigAskToDetachCOM();
+ /** @} */
+
+ /** @name COM: Extension Pack stuff.
+ * @{ */
+ /** Notifies listeners about extension pack @a strName was installed. */
+ void sigExtensionPackInstalled(const QString &strName);
+ /** @} */
+
+ /** @name Cloud Virtual Machine stuff.
+ * @{ */
+ /** Notifies listeners about cloud VM was unregistered.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name.
+ * @param uId Brings cloud VM id. */
+ void sigCloudMachineUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QUuid &uId);
+ /** Notifies listeners about cloud VM was registered.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name.
+ * @param comMachine Brings cloud VM. */
+ void sigCloudMachineRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine);
+ /** @} */
+
+ /** @name COM: Virtual Media stuff.
+ * @{ */
+ /** Notifies listeners about medium with certain @a uMediumID created. */
+ void sigMediumCreated(const QUuid &uMediumID);
+ /** Notifies listeners about medium with certain @a uMediumID deleted. */
+ void sigMediumDeleted(const QUuid &uMediumID);
+
+ /** Notifies listeners about medium-enumeration started. */
+ void sigMediumEnumerationStarted();
+ /** Notifies listeners about medium with certain @a uMediumID enumerated. */
+ void sigMediumEnumerated(const QUuid &uMediumID);
+ /** Notifies listeners about medium-enumeration finished. */
+ void sigMediumEnumerationFinished();
+ /** Notifies listeners about update of recently media list. */
+ void sigRecentMediaListUpdated(UIMediumDeviceType enmMediumType);
+ /** @} */
+
+public:
+
+ /** UI types. */
+ enum UIType
+ {
+ UIType_SelectorUI,
+ UIType_RuntimeUI
+ };
+
+ /** VM launch running options. */
+ enum LaunchRunning
+ {
+ LaunchRunning_Default, /**< Default (depends on debug settings). */
+ LaunchRunning_No, /**< Start the VM paused. */
+ LaunchRunning_Yes /**< Start the VM running. */
+ };
+
+ /** Returns UICommon instance. */
+ static UICommon *instance() { return s_pInstance; }
+ /** Creates UICommon instance of passed @a enmType. */
+ static void create(UIType enmType);
+ /** Destroys UICommon instance. */
+ static void destroy();
+
+ /** @name General stuff.
+ * @{ */
+ /** Returns the UI type. */
+ UIType uiType() const { return m_enmType; }
+
+ /** Returns whether UICommon instance is properly initialized. */
+ bool isValid() const { return m_fValid; }
+ /** Returns whether UICommon instance cleanup is in progress. */
+ bool isCleaningUp() const { return m_fCleaningUp; }
+ /** @} */
+
+ /** @name Versioning stuff.
+ * @{ */
+ /** Returns Qt runtime version string. */
+ static QString qtRTVersionString();
+ /** Returns Qt runtime version. */
+ static uint qtRTVersion();
+ /** Returns Qt runtime major version. */
+ static uint qtRTMajorVersion();
+ /** Returns Qt runtime minor version. */
+ static uint qtRTMinorVersion();
+ /** Returns Qt runtime revision number. */
+ static uint qtRTRevisionNumber();
+
+ /** Returns Qt compiled version string. */
+ static QString qtCTVersionString();
+ /** Returns Qt compiled version. */
+ static uint qtCTVersion();
+
+ /** Returns VBox version string. */
+ QString vboxVersionString() const;
+ /** Returns normalized VBox version string. */
+ QString vboxVersionStringNormalized() const;
+ /** Returns whether VBox version string contains BETA word. */
+ bool isBeta() const;
+ /** Returns whether BETA label should be shown. */
+ bool showBetaLabel() const;
+
+ /** Returns whether branding is active. */
+ bool brandingIsActive(bool fForce = false);
+ /** Returns value for certain branding @a strKey from custom.ini file. */
+ QString brandingGetKey(QString strKey) const;
+ /** @} */
+
+ /** @name Host OS stuff.
+ * @{ */
+#ifdef VBOX_WS_WIN
+ /** Loads the color theme. */
+ static void loadColorTheme();
+#endif
+
+#ifdef VBOX_WS_X11
+ /** X11: Returns the type of the Window Manager we are running under. */
+ X11WMType typeOfWindowManager() const { return m_enmWindowManagerType; }
+ /** X11: Returns whether the Window Manager we are running under is composition one. */
+ bool isCompositingManagerRunning() const { return m_fCompositingManagerRunning; }
+#endif
+ /** @} */
+
+ /** @name Process arguments stuff.
+ * @{ */
+ /** Process application args. */
+ bool processArgs();
+
+ /** Returns whether there are unhandled URL arguments present. */
+ bool argumentUrlsPresent() const;
+ /** Takes and returns the URL argument list while clearing the source. */
+ QList<QUrl> takeArgumentUrls();
+
+ /** Returns the --startvm option value (managed VM id). */
+ QUuid managedVMUuid() const { return m_strManagedVMId; }
+ /** Returns the --separate option value (whether GUI process is separate from VM process). */
+ bool isSeparateProcess() const { return m_fSeparateProcess; }
+ /** Returns the --no-startvm-errormsgbox option value (whether startup VM errors are disabled). */
+ bool showStartVMErrors() const { return m_fShowStartVMErrors; }
+
+ /** Returns the --aggressive-caching / --no-aggressive-caching option value (whether medium-enumeration is required). */
+ bool agressiveCaching() const { return m_fAgressiveCaching; }
+
+ /** Returns the --restore-current option value (whether we should restore current snapshot before VM started). */
+ bool shouldRestoreCurrentSnapshot() const { return m_fRestoreCurrentSnapshot; }
+ /** Defines whether we should fRestore current snapshot before VM started. */
+ void setShouldRestoreCurrentSnapshot(bool fRestore) { m_fRestoreCurrentSnapshot = fRestore; }
+
+ /** Returns the --fda option value (whether we have floppy image). */
+ bool hasFloppyImageToMount() const { return !m_uFloppyImage.isNull(); }
+ /** Returns the --dvd | --cdrom option value (whether we have DVD image). */
+ bool hasDvdImageToMount() const { return !m_uDvdImage.isNull(); }
+ /** Returns floppy image name. */
+ QUuid getFloppyImage() const { return m_uFloppyImage; }
+ /** Returns DVD image name. */
+ QUuid getDvdImage() const { return m_uDvdImage; }
+
+ /** Returns the --execute-all-in-iem option value. */
+ bool areWeToExecuteAllInIem() const { return m_fExecuteAllInIem; }
+ /** Returns whether --warp-factor option value is equal to 100. */
+ bool isDefaultWarpPct() const { return m_uWarpPct == 100; }
+ /** Returns the --warp-factor option value. */
+ uint32_t getWarpPct() const { return m_uWarpPct; }
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Holds whether the debugger should be accessible. */
+ bool isDebuggerEnabled() const;
+ /** Holds whether to show the debugger automatically with the console. */
+ bool isDebuggerAutoShowEnabled() const;
+ /** Holds whether to show the command line window when m_fDbgAutoShow is set. */
+ bool isDebuggerAutoShowCommandLineEnabled() const;
+ /** Holds whether to show the statistics window when m_fDbgAutoShow is set. */
+ bool isDebuggerAutoShowStatisticsEnabled() const;
+ /** Returns the combined --statistics-expand values. */
+ QString const getDebuggerStatisticsExpand() const { return m_strDbgStatisticsExpand; }
+ /** Returns the --statistics-filter value. */
+ QString const getDebuggerStatisticsFilter() const { return m_strDbgStatisticsFilter; }
+
+ /** VBoxDbg module handle. */
+ RTLDRMOD getDebuggerModule() const { return m_hVBoxDbg; }
+#endif
+
+ /** Returns whether VM should start paused. */
+ bool shouldStartPaused() const;
+
+#ifdef VBOX_GUI_WITH_PIDFILE
+ /** Creates PID file. */
+ void createPidfile();
+ /** Deletes PID file. */
+ void deletePidfile();
+#endif
+ /** @} */
+
+ /** @name COM stuff.
+ * @{ */
+ /** Try to acquire COM cleanup protection token for reading. */
+ bool comTokenTryLockForRead() { return m_comCleanupProtectionToken.tryLockForRead(); }
+ /** Unlock previously acquired COM cleanup protection token. */
+ void comTokenUnlock() { return m_comCleanupProtectionToken.unlock(); }
+
+ /** Returns the copy of VirtualBox client wrapper. */
+ CVirtualBoxClient virtualBoxClient() const { return m_comVBoxClient; }
+ /** Returns the copy of VirtualBox object wrapper. */
+ CVirtualBox virtualBox() const { return m_comVBox; }
+ /** Returns the copy of VirtualBox host-object wrapper. */
+ CHost host() const { return m_comHost; }
+ /** Returns the symbolic VirtualBox home-folder representation. */
+ QString homeFolder() const { return m_strHomeFolder; }
+
+ /** Returns the VBoxSVC availability value. */
+ bool isVBoxSVCAvailable() const { return m_fVBoxSVCAvailable; }
+ /** @} */
+
+ /** @name COM: Guest OS Type stuff.
+ * @{ */
+ /** Returns the list of family IDs. */
+ QList<QString> vmGuestOSFamilyIDs() const { return m_guestOSFamilyIDs; }
+
+ /** Returns a family description with passed @a strFamilyId. */
+ QString vmGuestOSFamilyDescription(const QString &strFamilyId) const;
+ /** Returns a list of all guest OS types with passed @a strFamilyId. */
+ QList<CGuestOSType> vmGuestOSTypeList(const QString &strFamilyId) const;
+
+ /** Returns the guest OS type for passed @a strTypeId.
+ * It is being serached through the list of family with passed @a strFamilyId if specified. */
+ CGuestOSType vmGuestOSType(const QString &strTypeId, const QString &strFamilyId = QString()) const;
+ /** Returns a type description with passed @a strTypeId. */
+ QString vmGuestOSTypeDescription(const QString &strTypeId) const;
+
+ /** Returns whether guest type with passed @a strOSTypeId is one of DOS types. */
+ static bool isDOSType(const QString &strOSTypeId);
+ /** @} */
+
+ /** @name COM: Virtual Machine stuff.
+ * @{ */
+ /** Switches to certain @a comMachine. */
+ static bool switchToMachine(CMachine &comMachine);
+ /** Launches certain @a comMachine in specified @a enmLaunchMode. */
+ static bool launchMachine(CMachine &comMachine, UILaunchMode enmLaunchMode = UILaunchMode_Default);
+
+ /** Opens session of certain @a enmLockType for VM with certain @a uId. */
+ CSession openSession(const QUuid &uId, KLockType enmLockType = KLockType_Write);
+ /** Opens session of KLockType_Shared type for VM with certain @a uId. */
+ CSession openExistingSession(const QUuid &uId) { return openSession(uId, KLockType_Shared); }
+ /** Tries to guess if new @a comSession needs to be opened for certain @a comMachine,
+ * if yes, new session of required type will be opened and machine will be updated,
+ * otherwise, no session will be created and machine will be left unchanged. */
+ CSession tryToOpenSessionFor(CMachine &comMachine);
+ /** @} */
+
+ /** @name COM: Cloud Virtual Machine stuff.
+ * @{ */
+ /** Notifies listeners about cloud VM was unregistered.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name.
+ * @param uId Brings cloud VM id. */
+ void notifyCloudMachineUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QUuid &uId);
+ /** Notifies listeners about cloud VM was registered.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name.
+ * @param comMachine Brings cloud VM. */
+ void notifyCloudMachineRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine);
+ /** @} */
+
+ /** @name COM: Virtual Media stuff.
+ * @{ */
+ /** Enumerates passed @a comMedia. */
+ void enumerateMedia(const CMediumVector &comMedia = CMediumVector());
+ /** Calls refresh for each medium which has been already enumerated. */
+ void refreshMedia();
+ /** Returns whether full medium-enumeration is requested. */
+ bool isFullMediumEnumerationRequested() const;
+ /** Returns whether any medium-enumeration is in progress. */
+ bool isMediumEnumerationInProgress() const;
+ /** Returns enumerated medium with certain @a uMediumID. */
+ UIMedium medium(const QUuid &uMediumID) const;
+ /** Returns enumerated medium IDs. */
+ QList<QUuid> mediumIDs() const;
+ /** Creates medium on the basis of passed @a guiMedium description. */
+ void createMedium(const UIMedium &guiMedium);
+
+ /** Opens external medium by passed @a strMediumLocation.
+ * @param enmMediumType Brings the medium type.
+ * @param pParent Brings the dialog parent.
+ * @param strMediumLocation Brings the file path to load medium from.
+ * @param pParent Brings the dialog parent. */
+ QUuid openMedium(UIMediumDeviceType enmMediumType, QString strMediumLocation, QWidget *pParent = 0);
+
+ /** Opens external medium using file-open dialog.
+ * @param enmMediumType Brings the medium type.
+ * @param pParent Brings the dialog parent.
+ * @param strDefaultFolder Brings the folder to browse for medium.
+ * @param fUseLastFolder Brings whether we should propose to use last used folder. */
+ QUuid openMediumWithFileOpenDialog(UIMediumDeviceType enmMediumType, QWidget *pParent = 0,
+ const QString &strDefaultFolder = QString(), bool fUseLastFolder = false);
+
+ /** Creates and shows a dialog (wizard) to create a medium of type @a enmMediumType.
+ * @param pParent Passes the parent of the dialog,
+ * @param enmMediumType Passes the medium type,
+ * @param strMachineName Passes the name of the machine,
+ * @param strMachineFolder Passes the machine folder,
+ * @param strMachineGuestOSTypeId Passes the type ID of machine's guest os,
+ * @param fEnableCreate Passes whether to show/enable create action in the medium selector dialog,
+ * returns QUuid of the new medium */
+ QUuid openMediumCreatorDialog(UIActionPool *pActionPool, QWidget *pParent, UIMediumDeviceType enmMediumType,
+ const QString &strMachineFolder = QString(), const QString &strMachineName = QString(),
+ const QString &strMachineGuestOSTypeId = QString());
+
+ /** Prepares storage menu according passed parameters.
+ * @param menu Brings the #QMenu to be prepared.
+ * @param pListener Brings the listener #QObject, this @a menu being prepared for.
+ * @param pszSlotName Brings the name of the SLOT in the @a pListener above, this menu will be handled with.
+ * @param comMachine Brings the #CMachine object, this @a menu being prepared for.
+ * @param strControllerName Brings the name of the #CStorageController in the @a machine above.
+ * @param storageSlot Brings the #StorageSlot of the storage controller with @a strControllerName above. */
+ void prepareStorageMenu(QMenu &menu,
+ QObject *pListener, const char *pszSlotName,
+ const CMachine &comMachine, const QString &strControllerName, const StorageSlot &storageSlot);
+ /** Updates @a comConstMachine storage with data described by @a target. */
+ void updateMachineStorage(const CMachine &comConstMachine, const UIMediumTarget &target, UIActionPool *pActionPool);
+
+ /** Generates details for passed @a comMedium.
+ * @param fPredictDiff Brings whether medium will be marked differencing on attaching.
+ * @param fUseHtml Brings whether HTML subsets should be used in the generated output. */
+ QString storageDetails(const CMedium &comMedium, bool fPredictDiff, bool fUseHtml = true);
+
+ /** Update extra data related to recently used/referred media.
+ * @param enmMediumType Passes the medium type.
+ * @param strMediumLocation Passes the medium location. */
+ void updateRecentlyUsedMediumListAndFolder(UIMediumDeviceType enmMediumType, QString strMediumLocation);
+
+ /** Searches extra data for the recently used folder path which corresponds to @a enmMediumType. When that search fails
+ it looks for recent folder extra data for other medium types. As the last resort returns default vm folder path.
+ * @param enmMediumType Passes the medium type. */
+ QString defaultFolderPathForType(UIMediumDeviceType enmMediumType);
+ /** @} */
+
+ /** @name COM: USB stuff.
+ * @{ */
+#ifdef RT_OS_LINUX
+ /** Verifies that USB drivers are properly configured on Linux. */
+ static void checkForWrongUSBMounted();
+#endif
+
+ /** Generates details for passed USB @a comDevice. */
+ static QString usbDetails(const CUSBDevice &comDevice);
+ /** Generates tool-tip for passed USB @a comDevice. */
+ static QString usbToolTip(const CUSBDevice &comDevice);
+ /** Generates tool-tip for passed USB @a comFilter. */
+ static QString usbToolTip(const CUSBDeviceFilter &comFilter);
+ /** Generates tool-tip for passed USB @a comWebcam. */
+ static QString usbToolTip(const CHostVideoInputDevice &comWebcam);
+ /** @} */
+
+ /** @name COM: Recording stuff.
+ * @{ */
+ /** Returns supported recording features flag. */
+ int supportedRecordingFeatures() const;
+ /** @} */
+
+ /** @name File-system stuff.
+ * @{ */
+ /** Returns full help file name. */
+ static QString helpFile();
+
+ /** Returns documents path. */
+ static QString documentsPath();
+
+ /** Returns whether passed @a strFileName ends with one of allowed extension in the @a extensions list. */
+ static bool hasAllowedExtension(const QString &strFileName, const QStringList &extensions);
+
+ /** Returns a file name (unique up to extension) wrt. @a strFullFolderPath folder content. Starts
+ * searching strBaseFileName and adds suffixes until a unique file name is found. */
+ static QString findUniqueFileName(const QString &strFullFolderPath, const QString &strBaseFileName);
+ /** @} */
+
+ /** @name Widget stuff.
+ * @{ */
+ /** Assigns minimum @a pSpinBox to correspond to @a cCount digits. */
+ static void setMinimumWidthAccordingSymbolCount(QSpinBox *pSpinBox, int cCount);
+ /** @} */
+
+ /** @name Display stuff.
+ * @{ */
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /** Returns whether guest OS type with passed @a strGuestOSTypeId is WDDM compatible. */
+ static bool isWddmCompatibleOsType(const QString &strGuestOSTypeId);
+#endif
+ /** Returns the required video memory in bytes for the current desktop
+ * resolution at maximum possible screen depth in bpp. */
+ static quint64 requiredVideoMemory(const QString &strGuestOSTypeId, int cMonitors = 1);
+ /** @} */
+
+ /** @name Thread stuff.
+ * @{ */
+ /** Returns the thread-pool instance. */
+ UIThreadPool *threadPool() const { return m_pThreadPool; }
+ /** Returns the thread-pool instance for cloud needs. */
+ UIThreadPool *threadPoolCloud() const { return m_pThreadPoolCloud; }
+ /** @} */
+
+ /** @name Context sensitive help related functionality
+ * @{ */
+ /** Sets the property for help keyword on a QObject
+ * @param pObject The object to set the help keyword property on
+ * @param strKeyword The values of the key word property. */
+ static void setHelpKeyword(QObject *pObject, const QString &strHelpKeyword);
+ /** Returns the property for help keyword of a QObject. If no such property exists returns an empty QString.
+ * @param pWidget The object to get the help keyword property from. */
+ static QString helpKeyword(const QObject *pWidget);
+ /** @} */
+
+public slots:
+
+ /** @name Process arguments stuff.
+ * @{ */
+ /** Opens the specified URL using OS/Desktop capabilities. */
+ bool openURL(const QString &strURL) const;
+ /** @} */
+
+ /** @name Localization stuff.
+ * @{ */
+ /** Handles language change to new @a strLanguage. */
+ void sltGUILanguageChange(QString strLanguage);
+ /** @} */
+
+ /** @name Media related stuff.
+ * @{ */
+ /** Handles signal about medium was created. */
+ void sltHandleMediumCreated(const CMedium &comMedium);
+ /** @} */
+
+ /** @name Machine related stuff.
+ * @{ */
+ /** Handles signal about machine was created. */
+ void sltHandleMachineCreated(const CMachine &comMachine);
+ /** @} */
+
+ /** @name Cloud Machine related stuff.
+ * @{ */
+ /** Handles signal about cloud machine was added. */
+ void sltHandleCloudMachineAdded(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine);
+ /** @} */
+
+protected:
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi();
+
+protected slots:
+
+ /** Calls for cleanup() functionality. */
+ void sltCleanup() { cleanup(); }
+
+#ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ /** @name Common stuff.
+ * @{ */
+ /** Handles @a manager request for emergency session shutdown. */
+ void sltHandleCommitDataRequest(QSessionManager &manager);
+ /** @} */
+#endif /* VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+ /** @name COM stuff.
+ * @{ */
+ /** Handles the VBoxSVC availability change. */
+ void sltHandleVBoxSVCAvailabilityChange(bool fAvailable);
+ /** @} */
+
+ /* Handle font scale factor change. */
+ void sltHandleFontScaleFactorChanged(int iFontScaleFactor);
+
+private:
+
+ /** Construcs global VirtualBox object of passed @a enmType. */
+ UICommon(UIType enmType);
+ /** Destrucs global VirtualBox object. */
+ virtual ~UICommon() /* override final */;
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** @name Process arguments stuff.
+ * @{ */
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Initializes a debugger config variable.
+ * @param piDbgCfgVar Brings the debugger config variable to init.
+ * @param pszEnvVar Brings the environment variable name relating to this variable.
+ * @param pszExtraDataName Brings the extra data name relating to this variable.
+ * @param fDefault Brings the default value. */
+ void initDebuggerVar(int *piDbgCfgVar, const char *pszEnvVar, const char *pszExtraDataName, bool fDefault = false);
+ /** Set a debugger config variable according according to start up argument.
+ * @param piDbgCfgVar Brings the debugger config variable to set.
+ * @param fState Brings the value from the command line. */
+ void setDebuggerVar(int *piDbgCfgVar, bool fState);
+ /** Checks the state of a debugger config variable, updating it with the machine settings on the first invocation.
+ * @param piDbgCfgVar Brings the debugger config variable to consult.
+ * @param pszExtraDataName Brings the extra data name relating to this variable. */
+ bool isDebuggerWorker(int *piDbgCfgVar, const char *pszExtraDataName) const;
+#endif
+ /** @} */
+
+ /** @name COM stuff.
+ * @{ */
+ /** Re-initializes COM wrappers and containers. */
+ void comWrappersReinit();
+ /** @} */
+
+ /** Holds the singleton UICommon instance. */
+ static UICommon *s_pInstance;
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the UI type. */
+ UIType m_enmType;
+
+ /** Holds whether UICommon instance is properly initialized. */
+ bool m_fValid;
+ /** Holds whether UICommon instance cleanup is in progress. */
+ bool m_fCleaningUp;
+#ifdef VBOX_WS_WIN
+ /** Holds whether overall GUI data is committed. */
+ bool m_fDataCommitted;
+#endif
+ /** @} */
+
+ /** @name Versioning stuff.
+ * @{ */
+ /** Holds the VBox branding config file path. */
+ QString m_strBrandingConfigFilePath;
+ /** @} */
+
+ /** @name Host OS stuff.
+ * @{ */
+#ifdef VBOX_WS_X11
+ /** X11: Holds the #X11WMType of the Window Manager we are running under. */
+ X11WMType m_enmWindowManagerType;
+ /** X11: Holds whether the Window Manager we are running at is composition one. */
+ bool m_fCompositingManagerRunning;
+#endif
+ /** @} */
+
+ /** @name Process arguments stuff.
+ * @{ */
+ /** Holds the URL arguments list. */
+ QList<QUrl> m_listArgUrls;
+
+ /** Holds the --startvm option value (managed VM id). */
+ QUuid m_strManagedVMId;
+ /** Holds the --separate option value (whether GUI process is separate from VM process). */
+ bool m_fSeparateProcess;
+ /** Holds the --no-startvm-errormsgbox option value (whether startup VM errors are disabled). */
+ bool m_fShowStartVMErrors;
+
+ /** Holds the --aggressive-caching / --no-aggressive-caching option value (whether medium-enumeration is required). */
+ bool m_fAgressiveCaching;
+
+ /** Holds the --restore-current option value. */
+ bool m_fRestoreCurrentSnapshot;
+
+ /** Holds the --fda option value (floppy image). */
+ QUuid m_uFloppyImage;
+ /** Holds the --dvd | --cdrom option value (DVD image). */
+ QUuid m_uDvdImage;
+
+ /** Holds the --execute-all-in-iem option value. */
+ bool m_fExecuteAllInIem;
+ /** Holds the --warp-factor option value. */
+ uint32_t m_uWarpPct;
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Holds whether the debugger should be accessible. */
+ mutable int m_fDbgEnabled;
+ /** Holds whether to show the debugger automatically with the console. */
+ mutable int m_fDbgAutoShow;
+ /** Holds whether to show the command line window when m_fDbgAutoShow is set. */
+ mutable int m_fDbgAutoShowCommandLine;
+ /** Holds whether to show the statistics window when m_fDbgAutoShow is set. */
+ mutable int m_fDbgAutoShowStatistics;
+ /** Pattern of statistics to expand when opening the viewer. */
+ QString m_strDbgStatisticsExpand;
+ /** The statistics viewer filter. */
+ QString m_strDbgStatisticsFilter;
+
+ /** VBoxDbg module handle. */
+ RTLDRMOD m_hVBoxDbg;
+
+ /** Holds whether --start-running, --start-paused or nothing was given. */
+ LaunchRunning m_enmLaunchRunning;
+#endif
+
+ /** Holds the --settingspw option value or the content of --settingspwfile. */
+ char m_astrSettingsPw[256];
+ /** Holds the --settingspwfile option value. */
+ bool m_fSettingsPwSet;
+
+#ifdef VBOX_GUI_WITH_PIDFILE
+ /** Holds the --pidfile option value (application PID file path). */
+ QString m_strPidFile;
+#endif
+ /** @} */
+
+ /** @name COM stuff.
+ * @{ */
+ /** Holds the COM cleanup protection token. */
+ QReadWriteLock m_comCleanupProtectionToken;
+
+ /** Holds the instance of VirtualBox client wrapper. */
+ CVirtualBoxClient m_comVBoxClient;
+ /** Holds the copy of VirtualBox object wrapper. */
+ CVirtualBox m_comVBox;
+ /** Holds the copy of VirtualBox host-object wrapper. */
+ CHost m_comHost;
+ /** Holds the symbolic VirtualBox home-folder representation. */
+ QString m_strHomeFolder;
+
+ /** Holds whether acquired COM wrappers are currently valid. */
+ bool m_fWrappersValid;
+ /** Holds whether VBoxSVC is currently available. */
+ bool m_fVBoxSVCAvailable;
+
+ /** Holds the guest OS family IDs. */
+ QList<QString> m_guestOSFamilyIDs;
+ /** Holds the guest OS family descriptions. */
+ QMap<QString, QString> m_guestOSFamilyDescriptions;
+ /** Holds the guest OS types for each family ID. */
+ QList<QList<CGuestOSType> > m_guestOSTypes;
+ /** @} */
+
+ /** @name Thread stuff.
+ * @{ */
+ /** Holds the thread-pool instance. */
+ UIThreadPool *m_pThreadPool;
+ /** Holds the thread-pool instance for cloud needs. */
+ UIThreadPool *m_pThreadPoolCloud;
+ /** @} */
+
+ /** @name Media related stuff.
+ * @{ */
+ /** Holds the medium enumerator cleanup protection token. */
+ mutable QReadWriteLock m_meCleanupProtectionToken;
+
+ /** Holds the medium enumerator. */
+ UIMediumEnumerator *m_pMediumEnumerator;
+ /** List of medium names that should not appears in the recently used media extra data. */
+ QStringList m_recentMediaExcludeList;
+ /** @} */
+
+#ifdef VBOX_WS_WIN
+ /** @name ATL stuff.
+ * @{ */
+ /** Holds the ATL module instance (for use with UICommon shared library only).
+ * @note Required internally by ATL (constructor records instance in global variable). */
+ ATL::CComModule _Module;
+ /** @} */
+#endif
+ /** @name Font scaling related variables.
+ * @{ */
+ int iOriginalFontPixelSize;
+ int iOriginalFontPointSize;
+ /** @} */
+
+ /** Allows for shortcut access. */
+ friend UICommon &uiCommon();
+};
+
+/** Singleton UICommon 'official' name. */
+inline UICommon &uiCommon() { return *UICommon::instance(); }
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UICommon_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UICursor.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UICursor.cpp
new file mode 100644
index 00000000..d5a3e165
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UICursor.cpp
@@ -0,0 +1,139 @@
+/* $Id: UICursor.cpp $ */
+/** @file
+ * VBox Qt GUI - UICursor namespace implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGraphicsWidget>
+#include <QWidget>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UICursor.h"
+
+
+/* static */
+void UICursor::setCursor(QWidget *pWidget, const QCursor &cursor)
+{
+ if (!pWidget)
+ return;
+
+#ifdef VBOX_WS_X11
+ /* As reported in https://www.virtualbox.org/ticket/16348,
+ * in X11 QWidget::setCursor(..) call uses RENDER
+ * extension. Qt (before 5.11) fails to handle the case where the mentioned extension
+ * is missing. Please see https://codereview.qt-project.org/#/c/225665/ for Qt patch: */
+ if ((UICommon::qtRTMajorVersion() < 5) ||
+ (UICommon::qtRTMajorVersion() == 5 && UICommon::qtRTMinorVersion() < 11))
+ {
+ if (NativeWindowSubsystem::X11CheckExtension("RENDER"))
+ pWidget->setCursor(cursor);
+ }
+ else
+ {
+ pWidget->setCursor(cursor);
+ }
+#else
+ pWidget->setCursor(cursor);
+#endif
+}
+
+/* static */
+void UICursor::setCursor(QGraphicsWidget *pWidget, const QCursor &cursor)
+{
+ if (!pWidget)
+ return;
+
+#ifdef VBOX_WS_X11
+ /* As reported in https://www.virtualbox.org/ticket/16348,
+ * in X11 QGraphicsWidget::setCursor(..) call uses RENDER
+ * extension. Qt (before 5.11) fails to handle the case where the mentioned extension
+ * is missing. Please see https://codereview.qt-project.org/#/c/225665/ for Qt patch: */
+ if ((UICommon::qtRTMajorVersion() < 5) ||
+ (UICommon::qtRTMajorVersion() == 5 && UICommon::qtRTMinorVersion() < 11))
+ {
+ if (NativeWindowSubsystem::X11CheckExtension("RENDER"))
+ pWidget->setCursor(cursor);
+ }
+ else
+ {
+ pWidget->setCursor(cursor);
+ }
+#else
+ pWidget->setCursor(cursor);
+#endif
+}
+
+/* static */
+void UICursor::unsetCursor(QWidget *pWidget)
+{
+ if (!pWidget)
+ return;
+
+#ifdef VBOX_WS_X11
+ /* As reported in https://www.virtualbox.org/ticket/16348,
+ * in X11 QWidget::unsetCursor(..) call uses RENDER
+ * extension. Qt (before 5.11) fails to handle the case where the mentioned extension
+ * is missing. Please see https://codereview.qt-project.org/#/c/225665/ for Qt patch: */
+ if ((UICommon::qtRTMajorVersion() < 5) ||
+ (UICommon::qtRTMajorVersion() == 5 && UICommon::qtRTMinorVersion() < 11))
+ {
+ if (NativeWindowSubsystem::X11CheckExtension("RENDER"))
+ pWidget->unsetCursor();
+ }
+ else
+ {
+ pWidget->unsetCursor();
+ }
+#else
+ pWidget->unsetCursor();
+#endif
+}
+
+/* static */
+void UICursor::unsetCursor(QGraphicsWidget *pWidget)
+{
+ if (!pWidget)
+ return;
+
+#ifdef VBOX_WS_X11
+ /* As reported in https://www.virtualbox.org/ticket/16348,
+ * in X11 QGraphicsWidget::unsetCursor(..) call uses RENDER
+ * extension. Qt (before 5.11) fails to handle the case where the mentioned extension
+ * is missing. Please see https://codereview.qt-project.org/#/c/225665/ for Qt patch: */
+ if ((UICommon::qtRTMajorVersion() < 5) ||
+ (UICommon::qtRTMajorVersion() == 5 && UICommon::qtRTMinorVersion() < 11))
+ {
+ if (NativeWindowSubsystem::X11CheckExtension("RENDER"))
+ pWidget->unsetCursor();
+ }
+ else
+ {
+ pWidget->unsetCursor();
+ }
+#else
+ pWidget->unsetCursor();
+#endif
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UICursor.h b/src/VBox/Frontends/VirtualBox/src/globals/UICursor.h
new file mode 100644
index 00000000..72c27186
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UICursor.h
@@ -0,0 +1,54 @@
+/* $Id: UICursor.h $ */
+/** @file
+ * VBox Qt GUI - UICursor namespace declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UICursor_h
+#define FEQT_INCLUDED_SRC_globals_UICursor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QGraphicsWidget;
+class QWidget;
+
+/** QObject subclass containing common GUI functionality. */
+namespace UICursor
+{
+ /** Does some checks on certain platforms before calling QWidget::setCursor(...). */
+ SHARED_LIBRARY_STUFF void setCursor(QWidget *pWidget, const QCursor &cursor);
+ /** Does some checks on certain platforms before calling QGraphicsWidget::setCursor(...). */
+ SHARED_LIBRARY_STUFF void setCursor(QGraphicsWidget *pWidget, const QCursor &cursor);
+ /** Does some checks on certain platforms before calling QWidget::unsetCursor(). */
+ SHARED_LIBRARY_STUFF void unsetCursor(QWidget *pWidget);
+ /** Does some checks on certain platforms before calling QGraphicsWidget::unsetCursor(). */
+ SHARED_LIBRARY_STUFF void unsetCursor(QGraphicsWidget *pWidget);
+}
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UICursor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UICustomFileSystemModel.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UICustomFileSystemModel.cpp
new file mode 100644
index 00000000..15ce7525
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UICustomFileSystemModel.cpp
@@ -0,0 +1,626 @@
+/* $Id: UICustomFileSystemModel.cpp $ */
+/** @file
+ * VBox Qt GUI - UICustomFileSystemModel class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QDateTime>
+#include <QHeaderView>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UICustomFileSystemModel.h"
+#include "UIErrorString.h"
+#include "UIPathOperations.h"
+#include "UITranslator.h"
+
+const char *UICustomFileSystemModel::strUpDirectoryString = "..";
+
+
+/*********************************************************************************************************************************
+* UICustomFileSystemItem implementation. *
+*********************************************************************************************************************************/
+
+UICustomFileSystemItem::UICustomFileSystemItem(const QString &strName, UICustomFileSystemItem *parent, KFsObjType type)
+ : m_parentItem(parent)
+ , m_bIsOpened(false)
+ , m_fIsTargetADirectory(false)
+ , m_type(type)
+ , m_fIsDriveItem(false)
+ , m_fIsHidden(false)
+{
+ for (int i = static_cast<int>(UICustomFileSystemModelColumn_Name);
+ i < static_cast<int>(UICustomFileSystemModelColumn_Max); ++i)
+ m_itemData[static_cast<UICustomFileSystemModelColumn>(i)] = QVariant();
+ m_itemData[UICustomFileSystemModelColumn_Name] = strName;
+ if (parent)
+ parent->appendChild(this);
+}
+
+UICustomFileSystemItem::~UICustomFileSystemItem()
+{
+ reset();
+}
+
+void UICustomFileSystemItem::appendChild(UICustomFileSystemItem *item)
+{
+ if (!item)
+ return;
+ if (m_childItems.contains(item))
+ return;
+ m_childItems.append(item);
+ m_childMap.insert(item->name(), item);
+}
+
+void UICustomFileSystemItem::reset()
+{
+ qDeleteAll(m_childItems);
+ m_childItems.clear();
+ m_childMap.clear();
+ m_bIsOpened = false;
+}
+
+UICustomFileSystemItem *UICustomFileSystemItem::child(int row) const
+{
+ return m_childItems.value(row);
+}
+
+UICustomFileSystemItem *UICustomFileSystemItem::child(const QString &path) const
+{
+ if (!m_childMap.contains(path))
+ return 0;
+ return m_childMap.value(path);
+}
+
+int UICustomFileSystemItem::childCount() const
+{
+ return m_childItems.count();
+}
+
+QList<UICustomFileSystemItem*> UICustomFileSystemItem::children() const
+{
+ QList<UICustomFileSystemItem*> childList;
+ foreach (UICustomFileSystemItem *child, m_childItems)
+ childList << child;
+ return childList;
+}
+
+void UICustomFileSystemItem::removeChild(UICustomFileSystemItem *pItem)
+{
+ int iIndex = m_childItems.indexOf(pItem);
+ if (iIndex == -1 || iIndex > m_childItems.size())
+ return;
+ m_childItems.removeAt(iIndex);
+ m_childMap.remove(pItem->name());
+ delete pItem;
+ pItem = 0;
+}
+
+void UICustomFileSystemItem::removeChildren()
+{
+ reset();
+}
+
+int UICustomFileSystemItem::columnCount() const
+{
+ return m_itemData.count();
+}
+
+QVariant UICustomFileSystemItem::data(int column) const
+{
+ return m_itemData.value(static_cast<UICustomFileSystemModelColumn>(column), QVariant());
+}
+
+QString UICustomFileSystemItem::name() const
+{
+ QVariant data = m_itemData.value(UICustomFileSystemModelColumn_Name, QVariant());
+ if (!data.canConvert(QMetaType::QString))
+ return QString();
+ return data.toString();
+}
+
+void UICustomFileSystemItem::setData(const QVariant &data, int index)
+{
+ m_itemData[static_cast<UICustomFileSystemModelColumn>(index)] = data;
+}
+
+void UICustomFileSystemItem::setData(const QVariant &data, UICustomFileSystemModelColumn enmColumn)
+{
+ m_itemData[enmColumn] = data;
+}
+
+UICustomFileSystemItem *UICustomFileSystemItem::parentItem()
+{
+ return m_parentItem;
+}
+
+int UICustomFileSystemItem::row() const
+{
+ if (m_parentItem)
+ return m_parentItem->m_childItems.indexOf(const_cast<UICustomFileSystemItem*>(this));
+ return 0;
+}
+
+bool UICustomFileSystemItem::isDirectory() const
+{
+ return m_type == KFsObjType_Directory;
+}
+
+bool UICustomFileSystemItem::isSymLink() const
+{
+ return m_type == KFsObjType_Symlink;
+}
+
+bool UICustomFileSystemItem::isFile() const
+{
+ return m_type == KFsObjType_File;
+}
+
+void UICustomFileSystemItem::clearChildren()
+{
+ qDeleteAll(m_childItems);
+ m_childItems.clear();
+ m_childMap.clear();
+}
+
+bool UICustomFileSystemItem::isOpened() const
+{
+ return m_bIsOpened;
+}
+
+void UICustomFileSystemItem::setIsOpened(bool flag)
+{
+ m_bIsOpened = flag;
+}
+
+QString UICustomFileSystemItem::path(bool fRemoveTrailingDelimiters /* = false */) const
+{
+ const QString &strPath = m_itemData.value(UICustomFileSystemModelColumn_Path, QString()).toString();
+
+ if (fRemoveTrailingDelimiters)
+ return UIPathOperations::removeTrailingDelimiters(strPath);
+
+ return strPath;
+}
+
+void UICustomFileSystemItem::setPath(const QString &path)
+{
+ if (path.isNull() || path.isEmpty())
+ return;
+ m_itemData[UICustomFileSystemModelColumn_Path] = path;
+}
+
+bool UICustomFileSystemItem::isUpDirectory() const
+{
+ if (!isDirectory())
+ return false;
+ if (data(0) == UICustomFileSystemModel::strUpDirectoryString)
+ return true;
+ return false;
+}
+
+KFsObjType UICustomFileSystemItem::type() const
+{
+ return m_type;
+}
+
+const QString &UICustomFileSystemItem::targetPath() const
+{
+ return m_strTargetPath;
+}
+
+void UICustomFileSystemItem::setTargetPath(const QString &path)
+{
+ m_strTargetPath = path;
+}
+
+bool UICustomFileSystemItem::isSymLinkToADirectory() const
+{
+ return m_fIsTargetADirectory;
+}
+
+void UICustomFileSystemItem::setIsSymLinkToADirectory(bool flag)
+{
+ m_fIsTargetADirectory = flag;
+}
+
+bool UICustomFileSystemItem::isSymLinkToAFile() const
+{
+ return isSymLink() && !m_fIsTargetADirectory;
+}
+
+void UICustomFileSystemItem::setIsDriveItem(bool flag)
+{
+ m_fIsDriveItem = flag;
+}
+
+bool UICustomFileSystemItem::isDriveItem() const
+{
+ return m_fIsDriveItem;
+}
+
+void UICustomFileSystemItem::setIsHidden(bool flag)
+{
+ m_fIsHidden = flag;
+}
+
+bool UICustomFileSystemItem::isHidden() const
+{
+ return m_fIsHidden;
+}
+
+
+/*********************************************************************************************************************************
+* UICustomFileSystemProxyModel implementation. *
+*********************************************************************************************************************************/
+
+UICustomFileSystemProxyModel::UICustomFileSystemProxyModel(QObject *parent /* = 0 */)
+ :QSortFilterProxyModel(parent)
+ , m_fListDirectoriesOnTop(false)
+ , m_fShowHiddenObjects(true)
+{
+}
+
+bool UICustomFileSystemProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+ UICustomFileSystemItem *pLeftItem = static_cast<UICustomFileSystemItem*>(left.internalPointer());
+ UICustomFileSystemItem *pRightItem = static_cast<UICustomFileSystemItem*>(right.internalPointer());
+
+ if (pLeftItem && pRightItem)
+ {
+ /* List the directories before the files if options say so: */
+ if (m_fListDirectoriesOnTop)
+ {
+ if ((pLeftItem->isDirectory() || pLeftItem->isSymLinkToADirectory()) && !pRightItem->isDirectory())
+ return (sortOrder() == Qt::AscendingOrder);
+ if ((pRightItem->isDirectory() || pRightItem->isSymLinkToADirectory()) && !pLeftItem->isDirectory())
+ return (sortOrder() == Qt::DescendingOrder);
+ }
+ /* Up directory item should be always the first item: */
+ if (pLeftItem->isUpDirectory())
+ return (sortOrder() == Qt::AscendingOrder);
+ else if (pRightItem->isUpDirectory())
+ return (sortOrder() == Qt::DescendingOrder);
+
+ /* If the sort column is QDateTime than handle it correctly: */
+ if (sortColumn() == UICustomFileSystemModelColumn_ChangeTime)
+ {
+ QVariant dataLeft = pLeftItem->data(UICustomFileSystemModelColumn_ChangeTime);
+ QVariant dataRight = pRightItem->data(UICustomFileSystemModelColumn_ChangeTime);
+ QDateTime leftDateTime = dataLeft.toDateTime();
+ QDateTime rightDateTime = dataRight.toDateTime();
+ return (leftDateTime < rightDateTime);
+ }
+ /* When we show human readble sizes in size column sorting gets confused, so do it here: */
+ else if(sortColumn() == UICustomFileSystemModelColumn_Size)
+ {
+ qulonglong leftSize = pLeftItem->data(UICustomFileSystemModelColumn_Size).toULongLong();
+ qulonglong rightSize = pRightItem->data(UICustomFileSystemModelColumn_Size).toULongLong();
+ return (leftSize < rightSize);
+
+ }
+ }
+ return QSortFilterProxyModel::lessThan(left, right);
+}
+
+bool UICustomFileSystemProxyModel::filterAcceptsRow(int iSourceRow, const QModelIndex &sourceParent) const
+{
+ if (m_fShowHiddenObjects)
+ return true;
+
+ QModelIndex itemIndex = sourceModel()->index(iSourceRow, 0, sourceParent);
+ if (!itemIndex.isValid())
+ return false;
+
+ UICustomFileSystemItem *item = static_cast<UICustomFileSystemItem*>(itemIndex.internalPointer());
+ if (!item)
+ return false;
+
+ if (item->isHidden())
+ return false;
+
+ return true;
+}
+
+void UICustomFileSystemProxyModel::setListDirectoriesOnTop(bool fListDirectoriesOnTop)
+{
+ m_fListDirectoriesOnTop = fListDirectoriesOnTop;
+}
+
+bool UICustomFileSystemProxyModel::listDirectoriesOnTop() const
+{
+ return m_fListDirectoriesOnTop;
+}
+
+void UICustomFileSystemProxyModel::setShowHiddenObjects(bool fShowHiddenObjects)
+{
+ m_fShowHiddenObjects = fShowHiddenObjects;
+}
+
+bool UICustomFileSystemProxyModel::showHiddenObjects() const
+{
+ return m_fShowHiddenObjects;
+}
+
+
+/*********************************************************************************************************************************
+* UICustomFileSystemModel implementation. *
+*********************************************************************************************************************************/
+
+UICustomFileSystemModel::UICustomFileSystemModel(QObject *parent)
+ : QAbstractItemModel(parent)
+ , m_fShowHumanReadableSizes(false)
+{
+ initializeTree();
+}
+
+UICustomFileSystemItem* UICustomFileSystemModel::rootItem()
+{
+ return m_pRootItem;
+}
+
+const UICustomFileSystemItem* UICustomFileSystemModel::rootItem() const
+{
+ return m_pRootItem;
+}
+
+UICustomFileSystemModel::~UICustomFileSystemModel()
+{
+ delete m_pRootItem;
+}
+
+int UICustomFileSystemModel::columnCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return static_cast<UICustomFileSystemItem*>(parent.internalPointer())->columnCount();
+ else
+ {
+ if (!rootItem())
+ return 0;
+ else
+ return rootItem()->columnCount();
+ }
+}
+
+bool UICustomFileSystemModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (index.isValid() && role == Qt::EditRole)
+ {
+ if (index.column() == 0 && value.canConvert(QMetaType::QString))
+ {
+ UICustomFileSystemItem *pItem = static_cast<UICustomFileSystemItem*>(index.internalPointer());
+ if (!pItem)
+ return false;
+ QString strOldName = pItem->name();
+ pItem->setData(value, index.column());
+ emit dataChanged(index, index);
+ emit sigItemRenamed(pItem, strOldName, value.toString());
+ return true;
+ }
+ }
+ return false;
+}
+
+QVariant UICustomFileSystemModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+ UICustomFileSystemItem *item = static_cast<UICustomFileSystemItem*>(index.internalPointer());
+ if (!item)
+ return QVariant();
+
+ if (role == Qt::DisplayRole || role == Qt::EditRole)
+ {
+ /* dont show anything but the name for up directories: */
+ if (item->isUpDirectory() && index.column() != UICustomFileSystemModelColumn_Name)
+ return QVariant();
+ /* Format date/time column: */
+ if (item->data(index.column()).canConvert(QMetaType::QDateTime))
+ {
+ QDateTime dateTime = item->data(index.column()).toDateTime();
+ if (dateTime.isValid())
+ return dateTime.toString("dd.MM.yyyy hh:mm:ss");
+ }
+ /* Decide whether to show human-readable file object sizes: */
+ if (index.column() == UICustomFileSystemModelColumn_Size)
+ {
+ if (m_fShowHumanReadableSizes)
+ {
+ qulonglong size = item->data(index.column()).toULongLong();
+ return UITranslator::formatSize(size);
+ }
+ else
+ return item->data(index.column());
+ }
+ return item->data(index.column());
+ }
+ /* Show the up directory array: */
+ if (role == Qt::DecorationRole && index.column() == 0)
+ {
+ if (item->isDirectory())
+ {
+ if (item->isUpDirectory())
+ return QIcon(":/arrow_up_10px_x2.png");
+ else if(item->isDriveItem())
+ return QIcon(":/hd_32px.png");
+ else
+ return QIcon(":/file_manager_folder_16px.png");
+ }
+ else if (item->isFile())
+ return QIcon(":/file_manager_file_16px.png");
+ else if (item->isSymLink())
+ {
+ if (item->isSymLinkToADirectory())
+ return QIcon(":/file_manager_folder_symlink_16px.png");
+ else
+ return QIcon(":/file_manager_file_symlink_16px.png");
+ }
+ }
+
+ return QVariant();
+}
+
+Qt::ItemFlags UICustomFileSystemModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::ItemFlags();
+ UICustomFileSystemItem *item = static_cast<UICustomFileSystemItem*>(index.internalPointer());
+ if (!item)
+ return QAbstractItemModel::flags(index);
+
+ if (!item->isUpDirectory() && index.column() == 0)
+ return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
+ return QAbstractItemModel::flags(index);
+}
+
+QVariant UICustomFileSystemModel::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ {
+ if (!rootItem())
+ return QVariant();
+ else
+ return rootItem()->data(section);
+ }
+ return QVariant();
+}
+
+QModelIndex UICustomFileSystemModel::index(UICustomFileSystemItem* item)
+{
+ if (!item)
+ return QModelIndex();
+ return createIndex(item->row(), 0, item);
+}
+
+QModelIndex UICustomFileSystemModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ const UICustomFileSystemItem* parentItem = rootItem();
+
+ if (parent.isValid())
+ parentItem = static_cast<UICustomFileSystemItem*>(parent.internalPointer());
+
+ if (!parentItem)
+ return QModelIndex();
+
+ UICustomFileSystemItem *childItem = parentItem->child(row);
+ if (childItem)
+ return createIndex(row, column, childItem);
+ else
+ return QModelIndex();
+}
+
+
+QModelIndex UICustomFileSystemModel::parent(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ UICustomFileSystemItem *childItem = static_cast<UICustomFileSystemItem*>(index.internalPointer());
+ UICustomFileSystemItem *parentItem = childItem->parentItem();
+
+ if (!parentItem || parentItem == rootItem())
+ return QModelIndex();
+
+ return createIndex(parentItem->row(), 0, parentItem);
+}
+
+int UICustomFileSystemModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.column() > 0)
+ return 0;
+ const UICustomFileSystemItem *parentItem = rootItem();
+ if (parent.isValid())
+ parentItem = static_cast<UICustomFileSystemItem*>(parent.internalPointer());
+ if (!parentItem)
+ return 0;
+ return parentItem->childCount();
+}
+
+void UICustomFileSystemModel::signalUpdate()
+{
+ emit layoutChanged();
+}
+
+QModelIndex UICustomFileSystemModel::rootIndex() const
+{
+ if (!rootItem())
+ return QModelIndex();
+ if (!rootItem()->child(0))
+ return QModelIndex();
+ return createIndex(rootItem()->child(0)->row(), 0,
+ rootItem()->child(0));
+}
+
+void UICustomFileSystemModel::beginReset()
+{
+ beginResetModel();
+}
+
+void UICustomFileSystemModel::endReset()
+{
+ endResetModel();
+}
+
+void UICustomFileSystemModel::reset()
+{
+ beginResetModel();
+ m_pRootItem->reset();
+ endResetModel();
+}
+
+void UICustomFileSystemModel::setShowHumanReadableSizes(bool fShowHumanReadableSizes)
+{
+ m_fShowHumanReadableSizes = fShowHumanReadableSizes;
+}
+
+bool UICustomFileSystemModel::showHumanReadableSizes() const
+{
+ return m_fShowHumanReadableSizes;
+}
+
+void UICustomFileSystemModel::deleteItem(UICustomFileSystemItem* pItem)
+{
+ if (!pItem)
+ return;
+ UICustomFileSystemItem *pParent = pItem->parentItem();
+ if (pParent)
+ pParent->removeChild(pItem);
+}
+
+void UICustomFileSystemModel::initializeTree()
+{
+ m_pRootItem = new UICustomFileSystemItem(UICustomFileSystemModel::tr("Name"), 0, KFsObjType_Directory);
+ m_pRootItem->setData(UICustomFileSystemModel::tr("Size"), UICustomFileSystemModelColumn_Size);
+ m_pRootItem->setData(UICustomFileSystemModel::tr("Change Time"), UICustomFileSystemModelColumn_ChangeTime);
+ m_pRootItem->setData(UICustomFileSystemModel::tr("Owner"), UICustomFileSystemModelColumn_Owner);
+ m_pRootItem->setData(UICustomFileSystemModel::tr("Permissions"), UICustomFileSystemModelColumn_Permissions);
+ m_pRootItem->setData(UICustomFileSystemModel::tr("Local Path"), UICustomFileSystemModelColumn_LocalPath);
+ m_pRootItem->setData(UICustomFileSystemModel::tr("Path"), UICustomFileSystemModelColumn_Path);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UICustomFileSystemModel.h b/src/VBox/Frontends/VirtualBox/src/globals/UICustomFileSystemModel.h
new file mode 100644
index 00000000..3d9dd1e1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UICustomFileSystemModel.h
@@ -0,0 +1,217 @@
+/* $Id: UICustomFileSystemModel.h $ */
+/** @file
+ * VBox Qt GUI - UICustomFileSystemModel class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UICustomFileSystemModel_h
+#define FEQT_INCLUDED_SRC_globals_UICustomFileSystemModel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QAbstractItemModel>
+#include <QSortFilterProxyModel>
+
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+enum UICustomFileSystemModelColumn
+{
+ UICustomFileSystemModelColumn_Name = 0,
+ UICustomFileSystemModelColumn_Size,
+ UICustomFileSystemModelColumn_ChangeTime,
+ UICustomFileSystemModelColumn_Owner,
+ UICustomFileSystemModelColumn_Permissions,
+ UICustomFileSystemModelColumn_Path,
+ UICustomFileSystemModelColumn_LocalPath,
+ UICustomFileSystemModelColumn_Max
+};
+
+/** A UICustomFileSystemItem instance is a tree node representing a file object (file, directory, etc). The tree contructed
+ by these instances is the data source for the UICustomFileSystemModel. */
+class SHARED_LIBRARY_STUFF UICustomFileSystemItem
+{
+public:
+
+ /** @p strName contains file object name which is assumed to be unique among a parent object's children. */
+ UICustomFileSystemItem(const QString &strName, UICustomFileSystemItem *parentItem, KFsObjType type);
+ virtual ~UICustomFileSystemItem();
+
+ void reset();
+ virtual UICustomFileSystemItem *child(int row) const;
+ /** Searches for the child by path and returns it if found. */
+ UICustomFileSystemItem *child(const QString &path) const;
+ int childCount() const;
+ QList<UICustomFileSystemItem*> children() const;
+ /** Removes the item from the list of children and !!DELETES!! the item. */
+ void removeChild(UICustomFileSystemItem *pItem);
+ void removeChildren();
+ int columnCount() const;
+ QVariant data(int column) const;
+ void setData(const QVariant &data, int index);
+ void setData(const QVariant &data, UICustomFileSystemModelColumn enmColumn);
+ int row() const;
+ UICustomFileSystemItem *parentItem();
+
+ bool isDirectory() const;
+ bool isSymLink() const;
+ bool isFile() const;
+
+ bool isOpened() const;
+ void setIsOpened(bool flag);
+
+ /** Full absolute path of the item. With or without the trailing '/' */
+ QString path(bool fRemoveTrailingDelimiters = false) const;
+ void setPath(const QString &path);
+
+ /** Returns true if this is directory and name is ".." */
+ bool isUpDirectory() const;
+ void clearChildren();
+
+ KFsObjType type() const;
+
+ const QString &targetPath() const;
+ void setTargetPath(const QString &path);
+
+ bool isSymLinkToADirectory() const;
+ void setIsSymLinkToADirectory(bool flag);
+
+ bool isSymLinkToAFile() const;
+
+ const QString &owner() const;
+ void setOwner(const QString &owner);
+
+ QString name() const;
+
+ void setIsDriveItem(bool flag);
+ bool isDriveItem() const;
+
+ void setIsHidden(bool flag);
+ bool isHidden() const;
+
+private:
+ void appendChild(UICustomFileSystemItem *child);
+
+ QList<UICustomFileSystemItem*> m_childItems;
+ /** Used to find children by name */
+ QMap<QString, UICustomFileSystemItem*> m_childMap;
+ QMap<UICustomFileSystemModelColumn, QVariant> m_itemData;
+ UICustomFileSystemItem *m_parentItem;
+ bool m_bIsOpened;
+ /** If this is a symlink m_targetPath keeps the absolute path of the target */
+ QString m_strTargetPath;
+ /** True if this is a symlink and the target is a directory */
+ bool m_fIsTargetADirectory;
+ KFsObjType m_type;
+ /** True if only this item represents a DOS style drive letter item */
+ bool m_fIsDriveItem;
+ /** True if the file object is hidden in the file system. */
+ bool m_fIsHidden;
+};
+
+/** A QSortFilterProxyModel extension used in file tables. Modifies some
+ * of the base class behavior like lessThan(..) */
+class SHARED_LIBRARY_STUFF UICustomFileSystemProxyModel : public QSortFilterProxyModel
+{
+
+ Q_OBJECT;
+
+public:
+
+ UICustomFileSystemProxyModel(QObject *parent = 0);
+
+ void setListDirectoriesOnTop(bool fListDirectoriesOnTop);
+ bool listDirectoriesOnTop() const;
+
+ void setShowHiddenObjects(bool fShowHiddenObjects);
+ bool showHiddenObjects() const;
+
+protected:
+
+ virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const RT_OVERRIDE;
+ /** Currently filters out hidden objects if options is set to "not showing them". */
+ virtual bool filterAcceptsRow(int iSourceRow, const QModelIndex &sourceParent) const RT_OVERRIDE;
+
+private:
+
+ bool m_fListDirectoriesOnTop;
+ bool m_fShowHiddenObjects;
+};
+
+/** UICustomFileSystemModel serves as the model for a file structure.
+ * it supports a tree level hierarchy which can be displayed with
+ * QTableView and/or QTreeView.*/
+class SHARED_LIBRARY_STUFF UICustomFileSystemModel : public QAbstractItemModel
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigItemRenamed(UICustomFileSystemItem *pItem, QString strOldName, QString strNewName);
+
+public:
+
+ explicit UICustomFileSystemModel(QObject *parent = 0);
+ ~UICustomFileSystemModel();
+
+ QVariant data(const QModelIndex &index, int role) const RT_OVERRIDE;
+ bool setData(const QModelIndex &index, const QVariant &value, int role);
+
+ Qt::ItemFlags flags(const QModelIndex &index) const RT_OVERRIDE;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const RT_OVERRIDE;
+ QModelIndex index(int row, int column,
+ const QModelIndex &parent = QModelIndex()) const RT_OVERRIDE;
+ QModelIndex index(UICustomFileSystemItem* item);
+ QModelIndex parent(const QModelIndex &index) const RT_OVERRIDE;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const RT_OVERRIDE;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const RT_OVERRIDE;
+ void signalUpdate();
+ QModelIndex rootIndex() const;
+ void beginReset();
+ void endReset();
+ void reset();
+
+ void setShowHumanReadableSizes(bool fShowHumanReadableSizes);
+ bool showHumanReadableSizes() const;
+ void deleteItem(UICustomFileSystemItem* pItem);
+ UICustomFileSystemItem* rootItem();
+ const UICustomFileSystemItem* rootItem() const;
+
+ static const char* strUpDirectoryString;
+
+private:
+ void initializeTree();
+ UICustomFileSystemItem *m_pRootItem;
+ void setupModelData(const QStringList &lines, UICustomFileSystemItem *parent);
+ bool m_fShowHumanReadableSizes;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UICustomFileSystemModel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIDefs.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIDefs.cpp
new file mode 100644
index 00000000..1a758f1c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIDefs.cpp
@@ -0,0 +1,42 @@
+/* $Id: UIDefs.cpp $ */
+/** @file
+ * VBox Qt GUI - Global definitions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIDefs.h"
+
+
+/* File name definitions: */
+const char* UIDefs::GUI_GuestAdditionsName = "VBoxGuestAdditions";
+const char* UIDefs::GUI_ExtPackName = "Oracle VM VirtualBox Extension Pack";
+
+/* File extensions definitions: */
+QStringList UIDefs::VBoxFileExts = QStringList() << "xml" << "vbox";
+QStringList UIDefs::VBoxExtPackFileExts = QStringList() << "vbox-extpack";
+QStringList UIDefs::OVFFileExts = QStringList() << "ovf" << "ova";
+
+/** Environment variable names: */
+const char *UIDefs::VBox_DesktopWatchdogPolicy_SynthTest = "VBOX_DESKTOPWATCHDOGPOLICY_SYNTHTEST";
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIDefs.h b/src/VBox/Frontends/VirtualBox/src/globals/UIDefs.h
new file mode 100644
index 00000000..e5c3c224
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIDefs.h
@@ -0,0 +1,170 @@
+/* $Id: UIDefs.h $ */
+/** @file
+ * VBox Qt GUI - Global definitions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIDefs_h
+#define FEQT_INCLUDED_SRC_globals_UIDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Define GUI log group: */
+// WORKAROUND:
+// This define should go *before* VBox/log.h include!
+#ifndef LOG_GROUP
+# define LOG_GROUP LOG_GROUP_GUI
+#endif
+
+/* Qt includes: */
+#include <QEvent>
+#include <QStringList>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Other VBox includes: */
+#include <VBox/log.h>
+#include <VBox/com/defs.h>
+
+/* Defines: */
+#ifdef RT_STRICT
+# define AssertWrapperOk(w) AssertMsg(w.isOk(), (#w " is not okay (RC=0x%08X)", w.lastRC()))
+# define AssertWrapperOkMsg(w, m) AssertMsg(w.isOk(), (#w ": " m " (RC=0x%08X)", w.lastRC()))
+#else
+# define AssertWrapperOk(w) do {} while (0)
+# define AssertWrapperOkMsg(w, m) do {} while (0)
+#endif
+
+
+/** Global namespace. */
+namespace UIDefs
+{
+ /** Additional Qt event types. */
+ enum UIEventType
+ {
+ ActivateActionEventType = QEvent::User + 101,
+#ifdef VBOX_WS_MAC
+ ShowWindowEventType,
+#endif
+ };
+
+ /** Size formatting types. */
+ enum FormatSize
+ {
+ FormatSize_Round,
+ FormatSize_RoundDown,
+ FormatSize_RoundUp
+ };
+
+ /** Default guest additions image name. */
+ SHARED_LIBRARY_STUFF extern const char* GUI_GuestAdditionsName;
+ /** Default extension pack name. */
+ SHARED_LIBRARY_STUFF extern const char* GUI_ExtPackName;
+
+ /** Allowed VBox file extensions. */
+ SHARED_LIBRARY_STUFF extern QStringList VBoxFileExts;
+ /** Allowed VBox Extension Pack file extensions. */
+ SHARED_LIBRARY_STUFF extern QStringList VBoxExtPackFileExts;
+ /** Allowed OVF file extensions. */
+ SHARED_LIBRARY_STUFF extern QStringList OVFFileExts;
+
+ /** Holds environment variable name for Desktop Watchdog / Synthetic Test policy type. */
+ SHARED_LIBRARY_STUFF extern const char *VBox_DesktopWatchdogPolicy_SynthTest;
+}
+using namespace UIDefs /* if header included */;
+
+
+/** Size suffixes. */
+enum SizeSuffix
+{
+ SizeSuffix_Byte = 0,
+ SizeSuffix_KiloByte,
+ SizeSuffix_MegaByte,
+ SizeSuffix_GigaByte,
+ SizeSuffix_TeraByte,
+ SizeSuffix_PetaByte,
+ SizeSuffix_Max
+};
+
+
+/** VM launch modes. */
+enum UILaunchMode
+{
+ UILaunchMode_Invalid,
+ UILaunchMode_Default,
+ UILaunchMode_Headless,
+ UILaunchMode_Separate
+};
+
+
+/** Storage-slot struct. */
+struct StorageSlot
+{
+ StorageSlot() : bus(KStorageBus_Null), port(0), device(0) {}
+ StorageSlot(const StorageSlot &other) : bus(other.bus), port(other.port), device(other.device) {}
+ StorageSlot(KStorageBus otherBus, LONG iPort, LONG iDevice) : bus(otherBus), port(iPort), device(iDevice) {}
+ StorageSlot& operator=(const StorageSlot &other) { bus = other.bus; port = other.port; device = other.device; return *this; }
+ bool operator==(const StorageSlot &other) const { return bus == other.bus && port == other.port && device == other.device; }
+ bool operator!=(const StorageSlot &other) const { return bus != other.bus || port != other.port || device != other.device; }
+ bool operator<(const StorageSlot &other) const { return (bus < other.bus) ||
+ (bus == other.bus && port < other.port) ||
+ (bus == other.bus && port == other.port && device < other.device); }
+ bool operator>(const StorageSlot &other) const { return (bus > other.bus) ||
+ (bus == other.bus && port > other.port) ||
+ (bus == other.bus && port == other.port && device > other.device); }
+ bool isNull() const { return bus == KStorageBus_Null; }
+ KStorageBus bus; LONG port; LONG device;
+};
+Q_DECLARE_METATYPE(StorageSlot);
+
+
+/** Storage-slot struct extension with exact controller name. */
+struct ExactStorageSlot : public StorageSlot
+{
+ ExactStorageSlot(const QString &strController,
+ KStorageBus enmBus, LONG iPort, LONG iDevice)
+ : StorageSlot(enmBus, iPort, iDevice)
+ , controller(strController)
+ {}
+ QString controller;
+};
+
+
+/** Desktop Watchdog / Synthetic Test policy type. */
+enum DesktopWatchdogPolicy_SynthTest
+{
+ DesktopWatchdogPolicy_SynthTest_Disabled,
+ DesktopWatchdogPolicy_SynthTest_ManagerOnly,
+ DesktopWatchdogPolicy_SynthTest_MachineOnly,
+ DesktopWatchdogPolicy_SynthTest_Both
+};
+Q_DECLARE_METATYPE(DesktopWatchdogPolicy_SynthTest);
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.cpp
new file mode 100644
index 00000000..01aa93c6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.cpp
@@ -0,0 +1,1144 @@
+/* $Id: UIDesktopWidgetWatchdog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDesktopWidgetWatchdog class implementation.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QWidget>
+#include <QScreen>
+#ifdef VBOX_WS_WIN
+# include <QLibrary>
+#endif
+#ifdef VBOX_WS_X11
+# include <QTimer>
+#endif
+#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
+# include <QDesktopWidget>
+#endif /* Qt < 5.10 */
+
+/* GUI includes: */
+#include "UIDesktopWidgetWatchdog.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+#ifdef VBOX_WS_WIN
+# include "VBoxUtils-win.h"
+#endif
+#ifdef VBOX_WS_X11
+# include "UICommon.h"
+# include "VBoxUtils-x11.h"
+# ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
+# include "UIConverter.h"
+# endif
+#endif
+
+/* Other VBox includes: */
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ldr.h>
+#include <VBox/log.h>
+#ifdef VBOX_WS_WIN
+# include <iprt/win/windows.h>
+#endif
+
+/* External includes: */
+#include <math.h>
+#ifdef VBOX_WS_X11
+# include <xcb/xcb.h>
+#endif
+
+
+#ifdef VBOX_WS_WIN
+
+# ifndef DPI_ENUMS_DECLARED
+typedef enum _MONITOR_DPI_TYPE // gently stolen from MSDN
+{
+ MDT_EFFECTIVE_DPI = 0,
+ MDT_ANGULAR_DPI = 1,
+ MDT_RAW_DPI = 2,
+ MDT_DEFAULT = MDT_EFFECTIVE_DPI
+} MONITOR_DPI_TYPE;
+# endif
+typedef void (WINAPI *PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
+
+/** Set when dynamic API import is reoslved. */
+static bool volatile g_fResolved;
+/** Pointer to Shcore.dll!GetDpiForMonitor, introduced in windows 8.1. */
+static PFN_GetDpiForMonitor g_pfnGetDpiForMonitor = NULL;
+
+/** @returns true if all APIs found, false if missing APIs */
+static bool ResolveDynamicImports(void)
+{
+ if (!g_fResolved)
+ {
+ PFN_GetDpiForMonitor pfn = (decltype(pfn))RTLdrGetSystemSymbol("Shcore.dll", "GetDpiForMonitor");
+ g_pfnGetDpiForMonitor = pfn;
+ ASMCompilerBarrier();
+
+ g_fResolved = true;
+ }
+ return g_pfnGetDpiForMonitor != NULL;
+}
+
+static BOOL CALLBACK MonitorEnumProcF(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lpClipRect, LPARAM dwData) RT_NOTHROW_DEF
+{
+ /* These required for clipped screens only: */
+ RT_NOREF(hdcMonitor, lpClipRect);
+
+ /* Acquire effective DPI (available since Windows 8.1): */
+ AssertReturn(g_pfnGetDpiForMonitor, false);
+ UINT uOutX = 0;
+ UINT uOutY = 0;
+ g_pfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &uOutX, &uOutY);
+ reinterpret_cast<QList<QPair<int, int> >*>(dwData)->append(qMakePair(uOutX, uOutY));
+
+ return TRUE;
+}
+
+#endif /* VBOX_WS_WIN */
+
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+
+/** QWidget extension used as
+ * an invisible window on the basis of which we
+ * can calculate available host-screen geometry. */
+class UIInvisibleWindow : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about host-screen available-geometry was calulated.
+ * @param iHostScreenIndex holds the index of the host-screen this window created for.
+ * @param availableGeometry holds the available-geometry of the host-screen this window created for. */
+ void sigHostScreenAvailableGeometryCalculated(int iHostScreenIndex, QRect availableGeometry);
+
+public:
+
+ /** Constructs invisible window for the host-screen with @a iHostScreenIndex. */
+ UIInvisibleWindow(int iHostScreenIndex);
+
+private slots:
+
+ /** Performs fallback drop. */
+ void sltFallback();
+
+private:
+
+ /** Move @a pEvent handler. */
+ void moveEvent(QMoveEvent *pEvent);
+ /** Resize @a pEvent handler. */
+ void resizeEvent(QResizeEvent *pEvent);
+
+ /** Holds the index of the host-screen this window created for. */
+ const int m_iHostScreenIndex;
+
+ /** Holds whether the move event came. */
+ bool m_fMoveCame;
+ /** Holds whether the resize event came. */
+ bool m_fResizeCame;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIInvisibleWindow implementation. *
+*********************************************************************************************************************************/
+
+UIInvisibleWindow::UIInvisibleWindow(int iHostScreenIndex)
+ : QWidget(0, Qt::Window | Qt::FramelessWindowHint)
+ , m_iHostScreenIndex(iHostScreenIndex)
+ , m_fMoveCame(false)
+ , m_fResizeCame(false)
+{
+ /* Resize to minimum size of 1 pixel: */
+ resize(1, 1);
+ /* Apply visual and mouse-event mask for that 1 pixel: */
+ setMask(QRect(0, 0, 1, 1));
+ /* For composite WMs make this 1 pixel transparent: */
+ if (uiCommon().isCompositingManagerRunning())
+ setAttribute(Qt::WA_TranslucentBackground);
+ /* Install fallback handler: */
+ QTimer::singleShot(5000, this, SLOT(sltFallback()));
+}
+
+void UIInvisibleWindow::sltFallback()
+{
+ /* Sanity check for fallback geometry: */
+ QRect fallbackGeometry(x(), y(), width(), height());
+ if ( fallbackGeometry.width() <= 1
+ || fallbackGeometry.height() <= 1)
+ fallbackGeometry = gpDesktop->screenGeometry(m_iHostScreenIndex);
+ LogRel(("GUI: UIInvisibleWindow::sltFallback: %s event haven't came. "
+ "Screen: %d, work area: %dx%d x %dx%d\n",
+ !m_fMoveCame ? "Move" : !m_fResizeCame ? "Resize" : "Some",
+ m_iHostScreenIndex, fallbackGeometry.x(), fallbackGeometry.y(), fallbackGeometry.width(), fallbackGeometry.height()));
+ emit sigHostScreenAvailableGeometryCalculated(m_iHostScreenIndex, fallbackGeometry);
+}
+
+void UIInvisibleWindow::moveEvent(QMoveEvent *pEvent)
+{
+ /* We do have both move and resize events,
+ * with no idea who will come first, but we need
+ * to send a final signal after last of events arrived. */
+
+ /* Call to base-class: */
+ QWidget::moveEvent(pEvent);
+
+ /* Ignore 'not-yet-shown' case: */
+ if (!isVisible())
+ return;
+
+ /* Mark move event as received: */
+ m_fMoveCame = true;
+
+ /* If the resize event already came: */
+ if (m_fResizeCame)
+ {
+ /* Notify listeners about host-screen available-geometry was calulated: */
+ LogRel2(("GUI: UIInvisibleWindow::moveEvent: Screen: %d, work area: %dx%d x %dx%d\n", m_iHostScreenIndex,
+ x(), y(), width(), height()));
+ emit sigHostScreenAvailableGeometryCalculated(m_iHostScreenIndex, QRect(x(), y(), width(), height()));
+ }
+}
+
+void UIInvisibleWindow::resizeEvent(QResizeEvent *pEvent)
+{
+ /* We do have both move and resize events,
+ * with no idea who will come first, but we need
+ * to send a final signal after last of events arrived. */
+
+ /* Call to base-class: */
+ QWidget::resizeEvent(pEvent);
+
+ /* Ignore 'not-yet-shown' case: */
+ if (!isVisible())
+ return;
+
+ /* Mark resize event as received: */
+ m_fResizeCame = true;
+
+ /* If the move event already came: */
+ if (m_fMoveCame)
+ {
+ /* Notify listeners about host-screen available-geometry was calulated: */
+ LogRel2(("GUI: UIInvisibleWindow::resizeEvent: Screen: %d, work area: %dx%d x %dx%d\n", m_iHostScreenIndex,
+ x(), y(), width(), height()));
+ emit sigHostScreenAvailableGeometryCalculated(m_iHostScreenIndex, QRect(x(), y(), width(), height()));
+ }
+}
+
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+
+/*********************************************************************************************************************************
+* Class UIDesktopWidgetWatchdog implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIDesktopWidgetWatchdog *UIDesktopWidgetWatchdog::s_pInstance = 0;
+
+/* static */
+void UIDesktopWidgetWatchdog::create()
+{
+ /* Make sure instance isn't created: */
+ AssertReturnVoid(!s_pInstance);
+
+ /* Create/prepare instance: */
+ new UIDesktopWidgetWatchdog;
+ AssertReturnVoid(s_pInstance);
+ s_pInstance->prepare();
+}
+
+/* static */
+void UIDesktopWidgetWatchdog::destroy()
+{
+ /* Make sure instance is created: */
+ AssertReturnVoid(s_pInstance);
+
+ /* Cleanup/destroy instance: */
+ s_pInstance->cleanup();
+ delete s_pInstance;
+ AssertReturnVoid(!s_pInstance);
+}
+
+UIDesktopWidgetWatchdog::UIDesktopWidgetWatchdog()
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ : m_enmSynthTestPolicy(DesktopWatchdogPolicy_SynthTest_Both)
+#endif
+{
+ /* Initialize instance: */
+ s_pInstance = this;
+}
+
+UIDesktopWidgetWatchdog::~UIDesktopWidgetWatchdog()
+{
+ /* Deinitialize instance: */
+ s_pInstance = 0;
+}
+
+/* static */
+int UIDesktopWidgetWatchdog::screenCount()
+{
+ return QGuiApplication::screens().size();
+}
+
+/* static */
+int UIDesktopWidgetWatchdog::primaryScreenNumber()
+{
+ return screenToIndex(QGuiApplication::primaryScreen());
+}
+
+/* static */
+int UIDesktopWidgetWatchdog::screenNumber(const QWidget *pWidget)
+{
+ QScreen *pScreen = 0;
+ if (pWidget)
+ if (QWindow *pWindow = pWidget->windowHandle())
+ pScreen = pWindow->screen();
+
+ return screenToIndex(pScreen);
+}
+
+/* static */
+int UIDesktopWidgetWatchdog::screenNumber(const QPoint &point)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+ return screenToIndex(QGuiApplication::screenAt(point));
+#else /* Qt < 5.10 */
+ return QApplication::desktop()->screenNumber(point);
+#endif /* Qt < 5.10 */
+}
+
+QRect UIDesktopWidgetWatchdog::screenGeometry(QScreen *pScreen) const
+{
+ /* Just return screen geometry: */
+ return pScreen->geometry();
+}
+
+QRect UIDesktopWidgetWatchdog::screenGeometry(int iHostScreenIndex /* = -1 */) const
+{
+ /* Gather suitable screen, use primary if failed: */
+ QScreen *pScreen = QGuiApplication::screens().value(iHostScreenIndex, QGuiApplication::primaryScreen());
+
+ /* Redirect call to wrapper above: */
+ return screenGeometry(pScreen);
+}
+
+QRect UIDesktopWidgetWatchdog::screenGeometry(const QWidget *pWidget) const
+{
+ /* Gather suitable screen, use primary if failed: */
+ QScreen *pScreen = QGuiApplication::primaryScreen();
+ if (pWidget)
+ if (QWindow *pWindow = pWidget->windowHandle())
+ pScreen = pWindow->screen();
+
+ /* Redirect call to wrapper above: */
+ return screenGeometry(pScreen);
+}
+
+QRect UIDesktopWidgetWatchdog::screenGeometry(const QPoint &point) const
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+ /* Gather suitable screen, use primary if failed: */
+ QScreen *pScreen = QGuiApplication::screenAt(point);
+ if (!pScreen)
+ pScreen = QGuiApplication::primaryScreen();
+
+ /* Redirect call to wrapper above: */
+ return screenGeometry(pScreen);
+#else /* Qt < 5.10 */
+ /* Gather suitable screen index: */
+ const int iHostScreenIndex = QApplication::desktop()->screenNumber(point);
+
+ /* Redirect call to wrapper above: */
+ return screenGeometry(iHostScreenIndex);
+#endif /* Qt < 5.10 */
+}
+
+QRect UIDesktopWidgetWatchdog::availableGeometry(QScreen *pScreen) const
+{
+#ifdef VBOX_WS_X11
+# ifdef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ // WORKAROUND:
+ // For customer WM we don't want Qt to return wrong available geometry,
+ // so we are returning fallback screen geometry in any case..
+ return screenGeometry(pScreen);
+# else /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+ /* Get cached available-geometry: */
+ const QRect availableGeometry = m_availableGeometryData.value(screenToIndex(pScreen));
+ /* Return cached available-geometry if it's valid or screen-geometry otherwise: */
+ return availableGeometry.isValid() ? availableGeometry : screenGeometry(pScreen);
+# endif /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+#else /* !VBOX_WS_X11 */
+ /* Just return screen available-geometry: */
+ return pScreen->availableGeometry();
+#endif /* !VBOX_WS_X11 */
+}
+
+QRect UIDesktopWidgetWatchdog::availableGeometry(int iHostScreenIndex /* = -1 */) const
+{
+ /* Gather suitable screen, use primary if failed: */
+ QScreen *pScreen = QGuiApplication::screens().value(iHostScreenIndex, QGuiApplication::primaryScreen());
+
+ /* Redirect call to wrapper above: */
+ return availableGeometry(pScreen);
+}
+
+QRect UIDesktopWidgetWatchdog::availableGeometry(const QWidget *pWidget) const
+{
+ /* Gather suitable screen, use primary if failed: */
+ QScreen *pScreen = QGuiApplication::primaryScreen();
+ if (pWidget)
+ if (QWindow *pWindow = pWidget->windowHandle())
+ pScreen = pWindow->screen();
+
+ /* Redirect call to wrapper above: */
+ return availableGeometry(pScreen);
+}
+
+QRect UIDesktopWidgetWatchdog::availableGeometry(const QPoint &point) const
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+ /* Gather suitable screen, use primary if failed: */
+ QScreen *pScreen = QGuiApplication::screenAt(point);
+ if (!pScreen)
+ pScreen = QGuiApplication::primaryScreen();
+
+ /* Redirect call to wrapper above: */
+ return availableGeometry(pScreen);
+#else /* Qt < 5.10 */
+ /* Gather suitable screen index: */
+ const int iHostScreenIndex = QApplication::desktop()->screenNumber(point);
+
+ /* Redirect call to wrapper above: */
+ return availableGeometry(iHostScreenIndex);
+#endif /* Qt < 5.10 */
+}
+
+/* static */
+QRegion UIDesktopWidgetWatchdog::overallScreenRegion()
+{
+ /* Calculate region: */
+ QRegion region;
+ foreach (QScreen *pScreen, QGuiApplication::screens())
+ region += gpDesktop->screenGeometry(pScreen);
+ return region;
+}
+
+/* static */
+QRegion UIDesktopWidgetWatchdog::overallAvailableRegion()
+{
+ /* Calculate region: */
+ QRegion region;
+ foreach (QScreen *pScreen, QGuiApplication::screens())
+ {
+ /* Get enumerated screen's available area: */
+ QRect rect = gpDesktop->availableGeometry(pScreen);
+#ifdef VBOX_WS_WIN
+ /* On Windows host window can exceed the available
+ * area in maximized/sticky-borders state: */
+ rect.adjust(-10, -10, 10, 10);
+#endif /* VBOX_WS_WIN */
+ /* Append rectangle: */
+ region += rect;
+ }
+ /* Return region: */
+ return region;
+}
+
+#ifdef VBOX_WS_X11
+/* static */
+bool UIDesktopWidgetWatchdog::isFakeScreenDetected()
+{
+ // WORKAROUND:
+ // In 5.6.1 Qt devs taught the XCB plugin to silently swap last detached screen
+ // with a fake one, and there is no API-way to distinguish fake from real one
+ // because all they do is erasing output for the last real screen, keeping
+ // all other screen attributes stale. Gladly output influencing screen name
+ // so we can use that horrible workaround to detect a fake XCB screen.
+ return qApp->screens().size() == 0 /* zero-screen case is impossible after 5.6.1 */
+ || (qApp->screens().size() == 1 && qApp->screens().first()->name() == ":0.0");
+}
+#endif /* VBOX_WS_X11 */
+
+/* static */
+double UIDesktopWidgetWatchdog::devicePixelRatio(int iHostScreenIndex /* = -1 */)
+{
+ /* First, we should check whether the screen is valid: */
+ QScreen *pScreen = iHostScreenIndex == -1
+ ? QGuiApplication::primaryScreen()
+ : QGuiApplication::screens().value(iHostScreenIndex);
+ AssertPtrReturn(pScreen, 1.0);
+
+ /* Then acquire device-pixel-ratio: */
+ return pScreen->devicePixelRatio();
+}
+
+/* static */
+double UIDesktopWidgetWatchdog::devicePixelRatio(QWidget *pWidget)
+{
+ /* Redirect call to wrapper above: */
+ return devicePixelRatio(screenNumber(pWidget));
+}
+
+/* static */
+double UIDesktopWidgetWatchdog::devicePixelRatioActual(int iHostScreenIndex /* = -1 */)
+{
+ /* First, we should check whether the screen is valid: */
+ QScreen *pScreen = 0;
+ if (iHostScreenIndex == -1)
+ {
+ pScreen = QGuiApplication::primaryScreen();
+ iHostScreenIndex = QGuiApplication::screens().indexOf(pScreen);
+ }
+ else
+ pScreen = QGuiApplication::screens().value(iHostScreenIndex);
+ AssertPtrReturn(pScreen, 1.0);
+
+#ifdef VBOX_WS_WIN
+ /* Enumerate available monitors through EnumDisplayMonitors if GetDpiForMonitor is available: */
+ if (ResolveDynamicImports())
+ {
+ QList<QPair<int, int> > listOfScreenDPI;
+ EnumDisplayMonitors(0, 0, MonitorEnumProcF, (LPARAM)&listOfScreenDPI);
+ if (iHostScreenIndex >= 0 && iHostScreenIndex < listOfScreenDPI.size())
+ {
+ const QPair<int, int> dpiPair = listOfScreenDPI.at(iHostScreenIndex);
+ if (dpiPair.first > 0)
+ return (double)dpiPair.first / 96 /* dpi unawarness value */;
+ }
+ }
+#endif /* VBOX_WS_WIN */
+
+ /* Then acquire device-pixel-ratio: */
+ return pScreen->devicePixelRatio();
+}
+
+/* static */
+double UIDesktopWidgetWatchdog::devicePixelRatioActual(QWidget *pWidget)
+{
+ /* Redirect call to wrapper above: */
+ return devicePixelRatioActual(screenNumber(pWidget));
+}
+
+/* static */
+QRect UIDesktopWidgetWatchdog::normalizeGeometry(const QRect &rectangle,
+ const QRegion &boundRegion,
+ bool fCanResize /* = true */)
+{
+ /* Perform direct and flipped search of position for @a rectangle to make sure it is fully contained
+ * inside @a boundRegion region by moving & resizing (if @a fCanResize is specified) @a rectangle if
+ * necessary. Selects the minimum shifted result between direct and flipped variants. */
+
+ /* Direct search for normalized rectangle: */
+ QRect var1(getNormalized(rectangle, boundRegion, fCanResize));
+
+ /* Flipped search for normalized rectangle: */
+ QRect var2(flip(getNormalized(flip(rectangle).boundingRect(),
+ flip(boundRegion), fCanResize)).boundingRect());
+
+ /* Calculate shift from starting position for both variants: */
+ double dLength1 = sqrt(pow((double)(var1.x() - rectangle.x()), (double)2) +
+ pow((double)(var1.y() - rectangle.y()), (double)2));
+ double dLength2 = sqrt(pow((double)(var2.x() - rectangle.x()), (double)2) +
+ pow((double)(var2.y() - rectangle.y()), (double)2));
+
+ /* Return minimum shifted variant: */
+ return dLength1 > dLength2 ? var2 : var1;
+}
+
+/* static */
+QRect UIDesktopWidgetWatchdog::getNormalized(const QRect &rectangle,
+ const QRegion &boundRegion,
+ bool /* fCanResize = true */)
+{
+ /* Ensures that the given rectangle @a rectangle is fully contained within the region @a boundRegion
+ * by moving @a rectangle if necessary. If @a rectangle is larger than @a boundRegion, top left
+ * corner of @a rectangle is aligned with the top left corner of maximum available rectangle and,
+ * if @a fCanResize is true, @a rectangle is shrinked to become fully visible. */
+
+ /* Storing available horizontal sub-rectangles & vertical shifts: */
+ const int iWindowVertical = rectangle.center().y();
+ QList<QRect> rectanglesList;
+ QList<int> shiftsList;
+ for (QRegion::const_iterator it = boundRegion.begin(); it != boundRegion.end(); ++it)
+ {
+ QRect currentItem = *it;
+ const int iCurrentDelta = qAbs(iWindowVertical - currentItem.center().y());
+ const int iShift2Top = currentItem.top() - rectangle.top();
+ const int iShift2Bot = currentItem.bottom() - rectangle.bottom();
+
+ int iTtemPosition = 0;
+ foreach (QRect item, rectanglesList)
+ {
+ const int iDelta = qAbs(iWindowVertical - item.center().y());
+ if (iDelta > iCurrentDelta)
+ break;
+ else
+ ++iTtemPosition;
+ }
+ rectanglesList.insert(iTtemPosition, currentItem);
+
+ int iShift2TopPos = 0;
+ foreach (int iShift, shiftsList)
+ if (qAbs(iShift) > qAbs(iShift2Top))
+ break;
+ else
+ ++iShift2TopPos;
+ shiftsList.insert(iShift2TopPos, iShift2Top);
+
+ int iShift2BotPos = 0;
+ foreach (int iShift, shiftsList)
+ if (qAbs(iShift) > qAbs(iShift2Bot))
+ break;
+ else
+ ++iShift2BotPos;
+ shiftsList.insert(iShift2BotPos, iShift2Bot);
+ }
+
+ /* Trying to find the appropriate place for window: */
+ QRect result;
+ for (int i = -1; i < shiftsList.size(); ++i)
+ {
+ /* Move to appropriate vertical: */
+ QRect newRectangle(rectangle);
+ if (i >= 0)
+ newRectangle.translate(0, shiftsList[i]);
+
+ /* Search horizontal shift: */
+ int iMaxShift = 0;
+ foreach (QRect item, rectanglesList)
+ {
+ QRect trectangle(newRectangle.translated(item.left() - newRectangle.left(), 0));
+ if (!item.intersects(trectangle))
+ continue;
+
+ if (newRectangle.left() < item.left())
+ {
+ const int iShift = item.left() - newRectangle.left();
+ iMaxShift = qAbs(iShift) > qAbs(iMaxShift) ? iShift : iMaxShift;
+ }
+ else if (newRectangle.right() > item.right())
+ {
+ const int iShift = item.right() - newRectangle.right();
+ iMaxShift = qAbs(iShift) > qAbs(iMaxShift) ? iShift : iMaxShift;
+ }
+ }
+
+ /* Shift across the horizontal direction: */
+ newRectangle.translate(iMaxShift, 0);
+
+ /* Check the translated rectangle to feat the rules: */
+ if (boundRegion.united(newRectangle) == boundRegion)
+ result = newRectangle;
+
+ if (!result.isNull())
+ break;
+ }
+
+ if (result.isNull())
+ {
+ /* Resize window to feat desirable size
+ * using max of available rectangles: */
+ QRect maxRectangle;
+ quint64 uMaxSquare = 0;
+ foreach (QRect item, rectanglesList)
+ {
+ const quint64 uSquare = item.width() * item.height();
+ if (uSquare > uMaxSquare)
+ {
+ uMaxSquare = uSquare;
+ maxRectangle = item;
+ }
+ }
+
+ result = rectangle;
+ result.moveTo(maxRectangle.x(), maxRectangle.y());
+ if (maxRectangle.right() < result.right())
+ result.setRight(maxRectangle.right());
+ if (maxRectangle.bottom() < result.bottom())
+ result.setBottom(maxRectangle.bottom());
+ }
+
+ return result;
+}
+
+void UIDesktopWidgetWatchdog::centerWidget(QWidget *pWidget,
+ QWidget *pRelative,
+ bool fCanResize /* = true */) const
+{
+ /* If necessary, pWidget's position is adjusted to make it fully visible within
+ * the available desktop area. If pWidget is bigger then this area, it will also
+ * be resized unless fCanResize is false or there is an inappropriate minimum
+ * size limit (in which case the top left corner will be simply aligned with the top
+ * left corner of the available desktop area). pWidget must be a top-level widget.
+ * pRelative may be any widget, but if it's not top-level itself, its top-level
+ * widget will be used for calculations. pRelative can also be NULL, in which case
+ * pWidget will be centered relative to the available desktop area. */
+
+ AssertReturnVoid(pWidget);
+ AssertReturnVoid(pWidget->isTopLevel());
+
+ QRect deskGeo, parentGeo;
+ if (pRelative)
+ {
+ pRelative = pRelative->window();
+ deskGeo = availableGeometry(pRelative);
+ parentGeo = pRelative->frameGeometry();
+ // WORKAROUND:
+ // On X11/Gnome, geo/frameGeo.x() and y() are always 0 for top level
+ // widgets with parents, what a shame. Use mapToGlobal() to workaround.
+ QPoint d = pRelative->mapToGlobal(QPoint(0, 0));
+ d.rx() -= pRelative->geometry().x() - pRelative->x();
+ d.ry() -= pRelative->geometry().y() - pRelative->y();
+ parentGeo.moveTopLeft(d);
+ }
+ else
+ {
+ deskGeo = availableGeometry();
+ parentGeo = deskGeo;
+ }
+
+ // WORKAROUND:
+ // On X11, there is no way to determine frame geometry (including WM
+ // decorations) before the widget is shown for the first time. Stupidly
+ // enumerate other top level widgets to find the thickest frame. The code
+ // is based on the idea taken from QDialog::adjustPositionInternal().
+
+ int iExtraW = 0;
+ int iExtraH = 0;
+
+ QWidgetList list = QApplication::topLevelWidgets();
+ QListIterator<QWidget*> it(list);
+ while ((iExtraW == 0 || iExtraH == 0) && it.hasNext())
+ {
+ int iFrameW, iFrameH;
+ QWidget *pCurrent = it.next();
+ if (!pCurrent->isVisible())
+ continue;
+
+ iFrameW = pCurrent->frameGeometry().width() - pCurrent->width();
+ iFrameH = pCurrent->frameGeometry().height() - pCurrent->height();
+
+ iExtraW = qMax(iExtraW, iFrameW);
+ iExtraH = qMax(iExtraH, iFrameH);
+ }
+
+ /* On non-X11 platforms, the following would be enough instead of the above workaround: */
+ // QRect geo = frameGeometry();
+ QRect geo = QRect(0, 0, pWidget->width() + iExtraW,
+ pWidget->height() + iExtraH);
+
+ geo.moveCenter(QPoint(parentGeo.x() + (parentGeo.width() - 1) / 2,
+ parentGeo.y() + (parentGeo.height() - 1) / 2));
+
+ /* Ensure the widget is within the available desktop area: */
+ QRect newGeo = normalizeGeometry(geo, deskGeo, fCanResize);
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // No idea why, but Qt doesn't respect if there is a unified toolbar on the
+ // ::move call. So manually add the height of the toolbar before setting
+ // the position.
+ if (pRelative)
+ newGeo.translate(0, ::darwinWindowToolBarHeight(pWidget));
+#endif /* VBOX_WS_MAC */
+
+ pWidget->move(newGeo.topLeft());
+
+ if ( fCanResize
+ && (geo.width() != newGeo.width() || geo.height() != newGeo.height()))
+ pWidget->resize(newGeo.width() - iExtraW, newGeo.height() - iExtraH);
+}
+
+/* static */
+void UIDesktopWidgetWatchdog::restoreWidget(QWidget *pWidget)
+{
+ pWidget->show();
+ pWidget->setWindowState(pWidget->windowState() & ~Qt::WindowMinimized);
+ pWidget->activateWindow();
+ pWidget->raise();
+}
+
+/* static */
+void UIDesktopWidgetWatchdog::setTopLevelGeometry(QWidget *pWidget, int x, int y, int w, int h)
+{
+ AssertPtrReturnVoid(pWidget);
+#ifdef VBOX_WS_X11
+# define QWINDOWSIZE_MAX ((1<<24)-1)
+ if (pWidget->isWindow() && pWidget->isVisible())
+ {
+ // WORKAROUND:
+ // X11 window managers are not required to accept geometry changes on
+ // the top-level window. Unfortunately, current at Qt 5.6 and 5.7, Qt
+ // assumes that the change will succeed, and resizes all sub-windows
+ // unconditionally. By calling ConfigureWindow directly, Qt will see
+ // our change request as an externally triggered one on success and not
+ // at all if it is rejected.
+ const double dDPR = devicePixelRatio(pWidget);
+ uint16_t fMask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
+ | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
+ uint32_t values[] = { (uint32_t)(x * dDPR), (uint32_t)(y * dDPR), (uint32_t)(w * dDPR), (uint32_t)(h * dDPR) };
+ xcb_configure_window(NativeWindowSubsystem::X11GetConnection(), (xcb_window_t)pWidget->winId(),
+ fMask, values);
+ xcb_size_hints_t hints;
+ hints.flags = 1 /* XCB_ICCCM_SIZE_HINT_US_POSITION */
+ | 2 /* XCB_ICCCM_SIZE_HINT_US_SIZE */
+ | 512 /* XCB_ICCCM_SIZE_P_WIN_GRAVITY */;
+ hints.x = x * dDPR;
+ hints.y = y * dDPR;
+ hints.width = w * dDPR;
+ hints.height = h * dDPR;
+ hints.min_width = pWidget->minimumSize().width() * dDPR;
+ hints.min_height = pWidget->minimumSize().height() * dDPR;
+ hints.max_width = pWidget->maximumSize().width() * dDPR;
+ hints.max_height = pWidget->maximumSize().height() * dDPR;
+ hints.width_inc = pWidget->sizeIncrement().width() * dDPR;
+ hints.height_inc = pWidget->sizeIncrement().height() * dDPR;
+ hints.base_width = pWidget->baseSize().width() * dDPR;
+ hints.base_height = pWidget->baseSize().height() * dDPR;
+ hints.win_gravity = XCB_GRAVITY_STATIC;
+ if (hints.min_width > 0 || hints.min_height > 0)
+ hints.flags |= 16 /* XCB_ICCCM_SIZE_HINT_P_MIN_SIZE */;
+ if (hints.max_width < QWINDOWSIZE_MAX || hints.max_height < QWINDOWSIZE_MAX)
+ hints.flags |= 32 /* XCB_ICCCM_SIZE_HINT_P_MAX_SIZE */;
+ if (hints.width_inc > 0 || hints.height_inc)
+ hints.flags |= 64 /* XCB_ICCCM_SIZE_HINT_P_MIN_SIZE */
+ | 256 /* XCB_ICCCM_SIZE_HINT_BASE_SIZE */;
+ xcb_change_property(NativeWindowSubsystem::X11GetConnection(), XCB_PROP_MODE_REPLACE,
+ (xcb_window_t)pWidget->winId(), XCB_ATOM_WM_NORMAL_HINTS,
+ XCB_ATOM_WM_SIZE_HINTS, 32, sizeof(hints) >> 2, &hints);
+ xcb_flush(NativeWindowSubsystem::X11GetConnection());
+ }
+ else
+ // WORKAROUND:
+ // Call the Qt method if the window is not visible as otherwise no
+ // Configure event will arrive to tell Qt what geometry we want.
+ pWidget->setGeometry(x, y, w, h);
+# else /* !VBOX_WS_X11 */
+ pWidget->setGeometry(x, y, w, h);
+# endif /* !VBOX_WS_X11 */
+}
+
+/* static */
+void UIDesktopWidgetWatchdog::setTopLevelGeometry(QWidget *pWidget, const QRect &rect)
+{
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(pWidget, rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+/* static */
+bool UIDesktopWidgetWatchdog::activateWindow(WId wId, bool fSwitchDesktop /* = true */)
+{
+ Q_UNUSED(fSwitchDesktop);
+ bool fResult = true;
+
+#if defined(VBOX_WS_WIN)
+
+ fResult &= NativeWindowSubsystem::WinActivateWindow(wId, fSwitchDesktop);
+
+#elif defined(VBOX_WS_X11)
+
+ fResult &= NativeWindowSubsystem::X11ActivateWindow(wId, fSwitchDesktop);
+
+#else
+
+ NOREF(wId);
+ NOREF(fSwitchDesktop);
+ AssertFailed();
+ fResult = false;
+
+#endif
+
+ if (!fResult)
+ Log1WarningFunc(("Couldn't activate wId=%08X\n", wId));
+
+ return fResult;
+}
+
+void UIDesktopWidgetWatchdog::sltHostScreenAdded(QScreen *pHostScreen)
+{
+// printf("UIDesktopWidgetWatchdog::sltHostScreenAdded(%d)\n", screenCount());
+
+ /* Listen for screen signals: */
+ connect(pHostScreen, &QScreen::geometryChanged,
+ this, &UIDesktopWidgetWatchdog::sltHandleHostScreenResized);
+ connect(pHostScreen, &QScreen::availableGeometryChanged,
+ this, &UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized);
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /* Update host-screen configuration: */
+ updateHostScreenConfiguration();
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+ /* Notify listeners: */
+ emit sigHostScreenCountChanged(screenCount());
+}
+
+void UIDesktopWidgetWatchdog::sltHostScreenRemoved(QScreen *pHostScreen)
+{
+// printf("UIDesktopWidgetWatchdog::sltHostScreenRemoved(%d)\n", screenCount());
+
+ /* Forget about screen signals: */
+ disconnect(pHostScreen, &QScreen::geometryChanged,
+ this, &UIDesktopWidgetWatchdog::sltHandleHostScreenResized);
+ disconnect(pHostScreen, &QScreen::availableGeometryChanged,
+ this, &UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized);
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /* Update host-screen configuration: */
+ updateHostScreenConfiguration();
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+ /* Notify listeners: */
+ emit sigHostScreenCountChanged(screenCount());
+}
+
+void UIDesktopWidgetWatchdog::sltHandleHostScreenResized(const QRect &geometry)
+{
+ /* Get the screen: */
+ QScreen *pScreen = sender() ? qobject_cast<QScreen*>(sender()) : 0;
+ AssertPtrReturnVoid(pScreen);
+
+ /* Determine screen index: */
+ const int iHostScreenIndex = qApp->screens().indexOf(pScreen);
+ AssertReturnVoid(iHostScreenIndex != -1);
+ LogRel(("GUI: UIDesktopWidgetWatchdog::sltHandleHostScreenResized: "
+ "Screen %d is formally resized to: %dx%d x %dx%d\n",
+ iHostScreenIndex, geometry.x(), geometry.y(),
+ geometry.width(), geometry.height()));
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /* Update host-screen available-geometry: */
+ updateHostScreenAvailableGeometry(iHostScreenIndex);
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+ /* Notify listeners: */
+ emit sigHostScreenResized(iHostScreenIndex);
+}
+
+void UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized(const QRect &availableGeometry)
+{
+ /* Get the screen: */
+ QScreen *pScreen = sender() ? qobject_cast<QScreen*>(sender()) : 0;
+ AssertPtrReturnVoid(pScreen);
+
+ /* Determine screen index: */
+ const int iHostScreenIndex = qApp->screens().indexOf(pScreen);
+ AssertReturnVoid(iHostScreenIndex != -1);
+ LogRel(("GUI: UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized: "
+ "Screen %d work area is formally resized to: %dx%d x %dx%d\n",
+ iHostScreenIndex, availableGeometry.x(), availableGeometry.y(),
+ availableGeometry.width(), availableGeometry.height()));
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /* Update host-screen available-geometry: */
+ updateHostScreenAvailableGeometry(iHostScreenIndex);
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+ /* Notify listeners: */
+ emit sigHostScreenWorkAreaResized(iHostScreenIndex);
+}
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+void UIDesktopWidgetWatchdog::sltHandleHostScreenAvailableGeometryCalculated(int iHostScreenIndex, QRect availableGeometry)
+{
+ LogRel(("GUI: UIDesktopWidgetWatchdog::sltHandleHostScreenAvailableGeometryCalculated: "
+ "Screen %d work area is actually resized to: %dx%d x %dx%d\n",
+ iHostScreenIndex, availableGeometry.x(), availableGeometry.y(),
+ availableGeometry.width(), availableGeometry.height()));
+
+ /* Apply received data: */
+ const bool fSendSignal = m_availableGeometryData.value(iHostScreenIndex).isValid();
+ m_availableGeometryData[iHostScreenIndex] = availableGeometry;
+ /* Forget finished worker: */
+ AssertPtrReturnVoid(m_availableGeometryWorkers.value(iHostScreenIndex));
+ m_availableGeometryWorkers.value(iHostScreenIndex)->disconnect();
+ m_availableGeometryWorkers.value(iHostScreenIndex)->deleteLater();
+ m_availableGeometryWorkers[iHostScreenIndex] = 0;
+
+ /* Notify listeners: */
+ if (fSendSignal)
+ emit sigHostScreenWorkAreaRecalculated(iHostScreenIndex);
+}
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+void UIDesktopWidgetWatchdog::prepare()
+{
+ /* Prepare connections: */
+ connect(qApp, &QGuiApplication::screenAdded,
+ this, &UIDesktopWidgetWatchdog::sltHostScreenAdded);
+ connect(qApp, &QGuiApplication::screenRemoved,
+ this, &UIDesktopWidgetWatchdog::sltHostScreenRemoved);
+ foreach (QScreen *pHostScreen, qApp->screens())
+ {
+ connect(pHostScreen, &QScreen::geometryChanged,
+ this, &UIDesktopWidgetWatchdog::sltHandleHostScreenResized);
+ connect(pHostScreen, &QScreen::availableGeometryChanged,
+ this, &UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized);
+ }
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /* Load Synthetic Test policy: */
+ const QString strSynthTestPolicy = QString::fromLocal8Bit(qgetenv(VBox_DesktopWatchdogPolicy_SynthTest));
+ m_enmSynthTestPolicy = gpConverter->fromInternalString<DesktopWatchdogPolicy_SynthTest>(strSynthTestPolicy);
+
+ /* Update host-screen configuration: */
+ updateHostScreenConfiguration();
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+}
+
+void UIDesktopWidgetWatchdog::cleanup()
+{
+ /* Cleanup connections: */
+ disconnect(qApp, &QGuiApplication::screenAdded,
+ this, &UIDesktopWidgetWatchdog::sltHostScreenAdded);
+ disconnect(qApp, &QGuiApplication::screenRemoved,
+ this, &UIDesktopWidgetWatchdog::sltHostScreenRemoved);
+ foreach (QScreen *pHostScreen, qApp->screens())
+ {
+ disconnect(pHostScreen, &QScreen::geometryChanged,
+ this, &UIDesktopWidgetWatchdog::sltHandleHostScreenResized);
+ disconnect(pHostScreen, &QScreen::availableGeometryChanged,
+ this, &UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized);
+ }
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /* Cleanup existing workers finally: */
+ cleanupExistingWorkers();
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+}
+
+/* static */
+int UIDesktopWidgetWatchdog::screenToIndex(QScreen *pScreen)
+{
+ if (pScreen)
+ {
+ unsigned iScreen = 0;
+ foreach (QScreen *pCurScreen, QGuiApplication::screens())
+ {
+ if ( pCurScreen == pScreen
+ || ( pCurScreen->geometry() == pScreen->geometry()
+ && pCurScreen->serialNumber() == pScreen->serialNumber()))
+ return iScreen;
+ ++iScreen;
+ }
+ }
+ return -1;
+}
+
+/* static */
+QRegion UIDesktopWidgetWatchdog::flip(const QRegion &region)
+{
+ QRegion result;
+ for (QRegion::const_iterator it = region.begin(); it != region.end(); ++it)
+ result += QRect(it->y(), it->x(),
+ it->height(), it->width());
+ return result;
+}
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+bool UIDesktopWidgetWatchdog::isSynchTestRestricted() const
+{
+ return m_enmSynthTestPolicy == DesktopWatchdogPolicy_SynthTest_Disabled
+ || ( m_enmSynthTestPolicy == DesktopWatchdogPolicy_SynthTest_ManagerOnly
+ && uiCommon().uiType() == UICommon::UIType_RuntimeUI)
+ || ( m_enmSynthTestPolicy == DesktopWatchdogPolicy_SynthTest_MachineOnly
+ && uiCommon().uiType() == UICommon::UIType_SelectorUI);
+}
+
+void UIDesktopWidgetWatchdog::updateHostScreenConfiguration(int cHostScreenCount /* = -1 */)
+{
+ /* Check the policy: */
+ if (isSynchTestRestricted())
+ return;
+
+ /* Acquire new host-screen count: */
+ if (cHostScreenCount == -1)
+ cHostScreenCount = screenCount();
+
+ /* Cleanup existing workers first: */
+ cleanupExistingWorkers();
+
+ /* Resize workers vectors to new host-screen count: */
+ m_availableGeometryWorkers.resize(cHostScreenCount);
+ m_availableGeometryData.resize(cHostScreenCount);
+
+ /* Update host-screen available-geometry for each particular host-screen: */
+ for (int iHostScreenIndex = 0; iHostScreenIndex < cHostScreenCount; ++iHostScreenIndex)
+ updateHostScreenAvailableGeometry(iHostScreenIndex);
+}
+
+void UIDesktopWidgetWatchdog::updateHostScreenAvailableGeometry(int iHostScreenIndex)
+{
+ /* Check the policy: */
+ if (isSynchTestRestricted())
+ return;
+
+ /* Make sure index is valid: */
+ if (iHostScreenIndex < 0 || iHostScreenIndex >= screenCount())
+ {
+ iHostScreenIndex = UIDesktopWidgetWatchdog::primaryScreenNumber();
+ AssertReturnVoid(iHostScreenIndex >= 0 && iHostScreenIndex < screenCount());
+ }
+
+ /* Create invisible frame-less window worker: */
+ UIInvisibleWindow *pWorker = new UIInvisibleWindow(iHostScreenIndex);
+ AssertPtrReturnVoid(pWorker);
+ {
+ /* Remember created worker (replace if necessary): */
+ if (m_availableGeometryWorkers.value(iHostScreenIndex))
+ delete m_availableGeometryWorkers.value(iHostScreenIndex);
+ m_availableGeometryWorkers[iHostScreenIndex] = pWorker;
+
+ /* Get the screen-geometry: */
+ const QRect hostScreenGeometry = screenGeometry(iHostScreenIndex);
+
+ /* Connect worker listener: */
+ connect(pWorker, &UIInvisibleWindow::sigHostScreenAvailableGeometryCalculated,
+ this, &UIDesktopWidgetWatchdog::sltHandleHostScreenAvailableGeometryCalculated);
+
+ /* Place worker to corresponding host-screen: */
+ pWorker->move(hostScreenGeometry.center());
+ /* And finally, maximize it: */
+ pWorker->showMaximized();
+ }
+}
+
+void UIDesktopWidgetWatchdog::cleanupExistingWorkers()
+{
+ /* Check the policy: */
+ if (isSynchTestRestricted())
+ return;
+
+ /* Destroy existing workers: */
+ qDeleteAll(m_availableGeometryWorkers);
+ /* And clear their vector: */
+ m_availableGeometryWorkers.clear();
+}
+
+# include "UIDesktopWidgetWatchdog.moc"
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.h b/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.h
new file mode 100644
index 00000000..7e99a54b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.h
@@ -0,0 +1,225 @@
+/* $Id: UIDesktopWidgetWatchdog.h $ */
+/** @file
+ * VBox Qt GUI - UIDesktopWidgetWatchdog class declaration.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIDesktopWidgetWatchdog_h
+#define FEQT_INCLUDED_SRC_globals_UIDesktopWidgetWatchdog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QWindow>
+#ifdef VBOX_WS_X11
+# include <QRect>
+# include <QVector>
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+# include "UIDefs.h"
+#endif
+
+/* Forward declarations: */
+class QScreen;
+
+/** Singleton QObject extension used as desktop-widget
+ * watchdog aware of the host-screen geometry changes. */
+class SHARED_LIBRARY_STUFF UIDesktopWidgetWatchdog : public QObject
+{
+ Q_OBJECT;
+
+ /** Constructs desktop-widget watchdog. */
+ UIDesktopWidgetWatchdog();
+ /** Destructs desktop-widget watchdog. */
+ virtual ~UIDesktopWidgetWatchdog() /* override final */;
+
+signals:
+
+ /** Notifies about host-screen count change to @a cHostScreenCount. */
+ void sigHostScreenCountChanged(int cHostScreenCount);
+
+ /** Notifies about resize for the host-screen with @a iHostScreenIndex. */
+ void sigHostScreenResized(int iHostScreenIndex);
+
+ /** Notifies about work-area resize for the host-screen with @a iHostScreenIndex. */
+ void sigHostScreenWorkAreaResized(int iHostScreenIndex);
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /** Notifies about work-area recalculated for the host-screen with @a iHostScreenIndex. */
+ void sigHostScreenWorkAreaRecalculated(int iHostScreenIndex);
+#endif
+
+public:
+
+ /** Returns the static instance of the desktop-widget watchdog. */
+ static UIDesktopWidgetWatchdog *instance() { return s_pInstance; }
+
+ /** Creates the static instance of the desktop-widget watchdog. */
+ static void create();
+ /** Destroys the static instance of the desktop-widget watchdog. */
+ static void destroy();
+
+ /** Returns the number of host-screens currently available on the system. */
+ static int screenCount();
+
+ /** Returns primary screen index. */
+ static int primaryScreenNumber();
+ /** Returns the index of the screen which contains contains @a pWidget. */
+ static int screenNumber(const QWidget *pWidget);
+ /** Returns the index of the screen which contains contains @a point. */
+ static int screenNumber(const QPoint &point);
+
+ /** Returns the geometry of the host @a pScreen. */
+ QRect screenGeometry(QScreen *pScreen) const;
+ /** Returns the geometry of the host-screen with @a iHostScreenIndex.
+ * @note The default screen is used if @a iHostScreenIndex is -1. */
+ QRect screenGeometry(int iHostScreenIndex = -1) const;
+ /** Returns the geometry of the host-screen which contains @a pWidget. */
+ QRect screenGeometry(const QWidget *pWidget) const;
+ /** Returns the geometry of the host-screen which contains @a point. */
+ QRect screenGeometry(const QPoint &point) const;
+
+ /** Returns the available-geometry of the host @a pScreen. */
+ QRect availableGeometry(QScreen *pScreen) const;
+ /** Returns the available-geometry of the host-screen with @a iHostScreenIndex.
+ * @note The default screen is used if @a iHostScreenIndex is -1. */
+ QRect availableGeometry(int iHostScreenIndex = -1) const;
+ /** Returns the available-geometry of the host-screen which contains @a pWidget. */
+ QRect availableGeometry(const QWidget *pWidget) const;
+ /** Returns the available-geometry of the host-screen which contains @a point. */
+ QRect availableGeometry(const QPoint &point) const;
+
+ /** Returns overall region unifying all the host-screen geometries. */
+ static QRegion overallScreenRegion();
+ /** Returns overall region unifying all the host-screen available-geometries. */
+ static QRegion overallAvailableRegion();
+
+#ifdef VBOX_WS_X11
+ /** Qt5: X11: Returns whether no or fake screen detected. */
+ static bool isFakeScreenDetected();
+#endif
+
+ /** Returns device-pixel-ratio of the host-screen with @a iHostScreenIndex. */
+ static double devicePixelRatio(int iHostScreenIndex = -1);
+ /** Returns device-pixel-ratio of the host-screen which contains @a pWidget. */
+ static double devicePixelRatio(QWidget *pWidget);
+
+ /** Returns actual device-pixel-ratio of the host-screen with @a iHostScreenIndex. */
+ static double devicePixelRatioActual(int iHostScreenIndex = -1);
+ /** Returns actual device-pixel-ratio of the host-screen which contains @a pWidget. */
+ static double devicePixelRatioActual(QWidget *pWidget);
+
+ /** Search position for @a rectangle to make sure it is fully
+ * contained within @a boundRegion, performing resize if allowed. */
+ static QRect normalizeGeometry(const QRect &rectangle,
+ const QRegion &boundRegion,
+ bool fCanResize = true);
+ /** Ensures that the given rectangle @a rectangle is fully
+ * contained within the region @a boundRegion, performing resize if allowed. */
+ static QRect getNormalized(const QRect &rectangle,
+ const QRegion &boundRegion,
+ bool fCanResize = true);
+ /** Aligns the center of @a pWidget with the center
+ * of @a pRelative, performing resize if allowed. */
+ void centerWidget(QWidget *pWidget,
+ QWidget *pRelative,
+ bool fCanResize = true) const;
+ /** Restores and exposes the @a pWidget on desktop. */
+ static void restoreWidget(QWidget *pWidget);
+
+ /** Assigns top-level @a pWidget geometry passed as QRect coordinates.
+ * @note Take into account that this request may fail on X11. */
+ static void setTopLevelGeometry(QWidget *pWidget, int x, int y, int w, int h);
+ /** Assigns top-level @a pWidget geometry passed as @a rect.
+ * @note Take into account that this request may fail on X11. */
+ static void setTopLevelGeometry(QWidget *pWidget, const QRect &rect);
+
+ /** Activates the specified window with given @a wId. Can @a fSwitchDesktop if requested. */
+ static bool activateWindow(WId wId, bool fSwitchDesktop = true);
+
+private slots:
+
+ /** Handles @a pHostScreen adding. */
+ void sltHostScreenAdded(QScreen *pHostScreen);
+ /** Handles @a pHostScreen removing. */
+ void sltHostScreenRemoved(QScreen *pHostScreen);
+ /** Handles host-screen resize to passed @a geometry. */
+ void sltHandleHostScreenResized(const QRect &geometry);
+ /** Handles host-screen work-area resize to passed @a availableGeometry. */
+ void sltHandleHostScreenWorkAreaResized(const QRect &availableGeometry);
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /** Handles @a availableGeometry calculation result for the host-screen with @a iHostScreenIndex. */
+ void sltHandleHostScreenAvailableGeometryCalculated(int iHostScreenIndex, QRect availableGeometry);
+#endif
+
+private:
+
+ /** Prepare routine. */
+ void prepare();
+ /** Cleanup routine. */
+ void cleanup();
+
+ /** Returns index of passed @a pScreen. */
+ static int screenToIndex(QScreen *pScreen);
+
+ /** Returns the flipped (transposed) @a region. */
+ static QRegion flip(const QRegion &region);
+
+ /** Holds the static instance of the desktop-widget watchdog. */
+ static UIDesktopWidgetWatchdog *s_pInstance;
+
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /** Returns whether Synthetic Test is restricted according to cached policy. */
+ bool isSynchTestRestricted() const;
+
+ /** Updates host-screen configuration according to new @a cHostScreenCount.
+ * @note If cHostScreenCount is equal to -1 we have to acquire it ourselves. */
+ void updateHostScreenConfiguration(int cHostScreenCount = -1);
+
+ /** Update available-geometry for the host-screen with @a iHostScreenIndex. */
+ void updateHostScreenAvailableGeometry(int iHostScreenIndex);
+
+ /** Cleanups existing workers. */
+ void cleanupExistingWorkers();
+
+ /** Holds the cached Synthetic Test policy. */
+ DesktopWatchdogPolicy_SynthTest m_enmSynthTestPolicy;
+
+ /** Holds current host-screen available-geometries. */
+ QVector<QRect> m_availableGeometryData;
+ /** Holds current workers determining host-screen available-geometries. */
+ QVector<QWidget*> m_availableGeometryWorkers;
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+};
+
+/** 'Official' name for the desktop-widget watchdog singleton. */
+#define gpDesktop UIDesktopWidgetWatchdog::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIDesktopWidgetWatchdog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIDetailsGenerator.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIDetailsGenerator.cpp
new file mode 100644
index 00000000..1cb9076f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIDetailsGenerator.cpp
@@ -0,0 +1,1170 @@
+/* $Id: UIDetailsGenerator.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsGenerator implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QDir>
+#include <QRegularExpression>
+
+/* GUI includes: */
+#include "UIBootOrderEditor.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIDetailsGenerator.h"
+#include "UIErrorString.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CAudioAdapter.h"
+#include "CAudioSettings.h"
+#include "CBooleanFormValue.h"
+#include "CChoiceFormValue.h"
+#include "CCloudMachine.h"
+#include "CForm.h"
+#include "CFormValue.h"
+#include "CGraphicsAdapter.h"
+#include "CMachine.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CNvramStore.h"
+#include "CProgress.h"
+#include "CRangedIntegerFormValue.h"
+#include "CRecordingScreenSettings.h"
+#include "CRecordingSettings.h"
+#include "CSerialPort.h"
+#include "CSharedFolder.h"
+#include "CStorageController.h"
+#include "CStringFormValue.h"
+#include "CSystemProperties.h"
+#include "CTrustedPlatformModule.h"
+#include "CUefiVariableStore.h"
+#include "CUSBController.h"
+#include "CUSBDeviceFilter.h"
+#include "CUSBDeviceFilters.h"
+#include "CVRDEServer.h"
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+
+UITextTable UIDetailsGenerator::generateMachineInformationGeneral(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral &fOptions)
+{
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Name: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Name)
+ {
+ /* Configure hovering anchor: */
+ const QString strAnchorType = QString("machine_name");
+ const QString strName = comMachine.GetName();
+ table << UITextTableLine(QApplication::translate("UIDetails", "Name", "details (general)"),
+ QString("<a href=#%1,%2>%2</a>")
+ .arg(strAnchorType,
+ strName));
+ }
+
+ /* Operating system: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_OS)
+ {
+ /* Configure hovering anchor: */
+ const QString strAnchorType = QString("os_type");
+ const QString strOsTypeId = comMachine.GetOSTypeId();
+ table << UITextTableLine(QApplication::translate("UIDetails", "Operating System", "details (general)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType,
+ strOsTypeId,
+ uiCommon().vmGuestOSTypeDescription(strOsTypeId)));
+ }
+
+ /* Settings file location: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Location)
+ {
+ /* Configure hovering anchor: */
+ const QString strAnchorType = QString("machine_location");
+ const QString strMachineLocation = comMachine.GetSettingsFilePath();
+ table << UITextTableLine(QApplication::translate("UIDetails", "Settings File Location", "details (general)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType,
+ strMachineLocation,
+ QDir::toNativeSeparators(QFileInfo(strMachineLocation).absolutePath())));
+ }
+
+ /* Groups: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Groups)
+ {
+ QStringList groups = comMachine.GetGroups().toList();
+ /* Do not show groups for machine which is in root group only: */
+ if (groups.size() == 1)
+ groups.removeAll("/");
+ /* If group list still not empty: */
+ if (!groups.isEmpty())
+ {
+ /* For every group: */
+ for (int i = 0; i < groups.size(); ++i)
+ {
+ /* Trim first '/' symbol: */
+ QString &strGroup = groups[i];
+ if (strGroup.startsWith("/") && strGroup != "/")
+ strGroup.remove(0, 1);
+ }
+ table << UITextTableLine(QApplication::translate("UIDetails", "Groups", "details (general)"), groups.join(", "));
+ }
+ }
+
+ return table;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationGeneral(CCloudMachine &comCloudMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral &)
+{
+ UITextTable table;
+
+ if (comCloudMachine.isNull())
+ return table;
+
+ if (!comCloudMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Acquire details form: */
+ CForm comForm = comCloudMachine.GetDetailsForm();
+ /* Ignore cloud machine errors: */
+ if (comCloudMachine.isOk())
+ {
+ /* Common anchor for all fields: */
+ const QString strAnchorType = "cloud";
+
+ /* For each form value: */
+ const QVector<CFormValue> values = comForm.GetValues();
+ foreach (const CFormValue &comIteratedValue, values)
+ {
+ /* Ignore invisible values: */
+ if (!comIteratedValue.GetVisible())
+ continue;
+
+ /* Acquire label: */
+ const QString strLabel = comIteratedValue.GetLabel();
+ /* Generate value: */
+ const QString strValue = generateFormValueInformation(comIteratedValue);
+
+ /* Generate table string: */
+ table << UITextTableLine(strLabel, QString("<a href=#%1,%2>%3</a>").arg(strAnchorType, strLabel, strValue));
+ }
+ }
+
+ return table;
+}
+
+QString UIDetailsGenerator::generateFormValueInformation(const CFormValue &comFormValue, bool fFull /* = false */)
+{
+ /* Handle possible form value types: */
+ QString strResult;
+ switch (comFormValue.GetType())
+ {
+ case KFormValueType_Boolean:
+ {
+ CBooleanFormValue comValue(comFormValue);
+ const bool fBool = comValue.GetSelected();
+ strResult = fBool ? QApplication::translate("UIDetails", "Enabled", "details (cloud value)")
+ : QApplication::translate("UIDetails", "Disabled", "details (cloud value)");
+ break;
+ }
+ case KFormValueType_String:
+ {
+ CStringFormValue comValue(comFormValue);
+ const QString strValue = comValue.GetString();
+ const QString strClipboardValue = comValue.GetClipboardString();
+ strResult = fFull && !strClipboardValue.isEmpty() ? strClipboardValue : strValue;
+ break;
+ }
+ case KFormValueType_Choice:
+ {
+ AssertMsgFailed(("Aren't we decided to convert all choices to strings?\n"));
+ CChoiceFormValue comValue(comFormValue);
+ const QVector<QString> possibleValues = comValue.GetValues();
+ const int iCurrentIndex = comValue.GetSelectedIndex();
+ strResult = possibleValues.value(iCurrentIndex);
+ break;
+ }
+ case KFormValueType_RangedInteger:
+ {
+ CRangedIntegerFormValue comValue(comFormValue);
+ strResult = QString("%1 %2")
+ .arg(comValue.GetInteger())
+ .arg(QApplication::translate("UICommon", comValue.GetSuffix().toUtf8().constData()));
+ break;
+ }
+ default:
+ break;
+ }
+ /* Return result: */
+ return strResult;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationSystem(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem &fOptions)
+{
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Base memory: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_RAM)
+ {
+ /* Configure hovering anchor: */
+ const QString strAnchorType = QString("base_memory");
+ const int iBaseMemory = comMachine.GetMemorySize();
+ table << UITextTableLine(QApplication::translate("UIDetails", "Base Memory", "details (system)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg(iBaseMemory)
+ .arg(QApplication::translate("UIDetails", "%1 MB").arg(iBaseMemory)));
+ }
+
+ /* Processors: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_CPUCount)
+ {
+ const int cCPU = comMachine.GetCPUCount();
+ if (cCPU > 1)
+ table << UITextTableLine(QApplication::translate("UIDetails", "Processors", "details (system)"),
+ QString::number(cCPU));
+ }
+
+ /* Execution cap: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_CPUExecutionCap)
+ {
+ const int iCPUExecutionCap = comMachine.GetCPUExecutionCap();
+ if (iCPUExecutionCap < 100)
+ table << UITextTableLine(QApplication::translate("UIDetails", "Execution Cap", "details (system)"),
+ QApplication::translate("UIDetails", "%1%", "details").arg(iCPUExecutionCap));
+ }
+
+ /* Boot order: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_BootOrder)
+ {
+ /* Configure hovering anchor: */
+ const QString strAnchorType = QString("boot_order");
+ const UIBootItemDataList bootItems = loadBootItems(comMachine);
+ table << UITextTableLine(QApplication::translate("UIDetails", "Boot Order", "details (system)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType,
+ bootItemsToSerializedString(bootItems),
+ bootItemsToReadableString(bootItems)));
+ }
+
+ /* Chipset type: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_ChipsetType)
+ {
+ const KChipsetType enmChipsetType = comMachine.GetChipsetType();
+ if (enmChipsetType == KChipsetType_ICH9)
+ table << UITextTableLine(QApplication::translate("UIDetails", "Chipset Type", "details (system)"),
+ gpConverter->toString(enmChipsetType));
+ }
+
+ /* TPM type: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_TpmType)
+ {
+ CTrustedPlatformModule comModule = comMachine.GetTrustedPlatformModule();
+ const KTpmType enmTpmType = comModule.GetType();
+ if (enmTpmType != KTpmType_None)
+ table << UITextTableLine(QApplication::translate("UIDetails", "TPM Type", "details (system)"),
+ gpConverter->toString(enmTpmType));
+ }
+
+ /* EFI: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Firmware)
+ {
+ switch (comMachine.GetFirmwareType())
+ {
+ case KFirmwareType_EFI:
+ case KFirmwareType_EFI32:
+ case KFirmwareType_EFI64:
+ case KFirmwareType_EFIDUAL:
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "EFI", "details (system)"),
+ QApplication::translate("UIDetails", "Enabled", "details (system/EFI)"));
+ break;
+ }
+ default:
+ {
+ // For NLS purpose:
+ QApplication::translate("UIDetails", "Disabled", "details (system/EFI)");
+ break;
+ }
+ }
+ }
+
+ /* Secure Boot: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_SecureBoot)
+ {
+ CNvramStore comStoreLvl1 = comMachine.GetNonVolatileStore();
+ if (comStoreLvl1.isNotNull())
+ {
+ CUefiVariableStore comStoreLvl2 = comStoreLvl1.GetUefiVariableStore();
+ /// @todo this comStoreLvl2.isNotNull() will never work for
+ /// now since VM reference is immutable in Details pane
+ if ( comStoreLvl2.isNotNull()
+ && comStoreLvl2.GetSecureBootEnabled())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Secure Boot", "details (system)"),
+ QApplication::translate("UIDetails", "Enabled", "details (system/secure boot)"));
+ }
+ }
+ }
+
+ /* Acceleration: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Acceleration)
+ {
+ QStringList acceleration;
+ if (uiCommon().virtualBox().GetHost().GetProcessorFeature(KProcessorFeature_HWVirtEx))
+ {
+ /* Nested Paging: */
+ if (comMachine.GetHWVirtExProperty(KHWVirtExPropertyType_NestedPaging))
+ acceleration << QApplication::translate("UIDetails", "Nested Paging", "details (system)");
+ }
+ /* Nested VT-x/AMD-V: */
+ if (comMachine.GetCPUProperty(KCPUPropertyType_HWVirt))
+ acceleration << QApplication::translate("UIDetails", "Nested VT-x/AMD-V", "details (system)");
+ /* PAE/NX: */
+ if (comMachine.GetCPUProperty(KCPUPropertyType_PAE))
+ acceleration << QApplication::translate("UIDetails", "PAE/NX", "details (system)");
+ /* Paravirtualization provider: */
+ switch (comMachine.GetEffectiveParavirtProvider())
+ {
+ case KParavirtProvider_Minimal: acceleration << QApplication::translate("UIDetails", "Minimal Paravirtualization", "details (system)"); break;
+ case KParavirtProvider_HyperV: acceleration << QApplication::translate("UIDetails", "Hyper-V Paravirtualization", "details (system)"); break;
+ case KParavirtProvider_KVM: acceleration << QApplication::translate("UIDetails", "KVM Paravirtualization", "details (system)"); break;
+ default: break;
+ }
+ if (!acceleration.isEmpty())
+ table << UITextTableLine(QApplication::translate("UIDetails", "Acceleration", "details (system)"),
+ acceleration.join(", "));
+ }
+
+ return table;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationDisplay(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay &fOptions)
+{
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ const CGraphicsAdapter comGraphics = comMachine.GetGraphicsAdapter();
+
+ /* Video memory: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_VRAM)
+ {
+ /* Configure hovering anchor: */
+ const QString strAnchorType = QString("video_memory");
+ const int iVideoMemory = comGraphics.GetVRAMSize();
+ table << UITextTableLine(QApplication::translate("UIDetails", "Video Memory", "details (display)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg(iVideoMemory)
+ .arg(QApplication::translate("UIDetails", "%1 MB").arg(iVideoMemory)));
+ }
+
+ /* Screens: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_ScreenCount)
+ {
+ const int cGuestScreens = comGraphics.GetMonitorCount();
+ if (cGuestScreens > 1)
+ table << UITextTableLine(QApplication::translate("UIDetails", "Screens", "details (display)"),
+ QString::number(cGuestScreens));
+ }
+
+ /* Scale-factor: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_ScaleFactor)
+ {
+ const QString strScaleFactor = comMachine.GetExtraData(UIExtraDataDefs::GUI_ScaleFactor);
+ {
+ /* Try to convert loaded data to double: */
+ bool fOk = false;
+ double dValue = strScaleFactor.toDouble(&fOk);
+ /* Invent the default value: */
+ if (!fOk || !dValue)
+ dValue = 1.0;
+ /* Append information: */
+ if (dValue != 1.0)
+ table << UITextTableLine(QApplication::translate("UIDetails", "Scale-factor", "details (display)"),
+ QString::number(dValue, 'f', 2));
+ }
+ }
+
+ /* Graphics Controller: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_GraphicsController)
+ {
+ const QString strAnchorType = QString("graphics_controller_type");
+ const KGraphicsControllerType enmType = comGraphics.GetGraphicsControllerType();
+ table << UITextTableLine(QApplication::translate("UIDetails", "Graphics Controller", "details (display)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg((int)enmType)
+ .arg(gpConverter->toString(enmType)));
+ }
+
+ /* Acceleration: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Acceleration)
+ {
+ QStringList acceleration;
+ /* 3D acceleration: */
+ if (comGraphics.GetAccelerate3DEnabled())
+ acceleration << QApplication::translate("UIDetails", "3D", "details (display)");
+ if (!acceleration.isEmpty())
+ table << UITextTableLine(QApplication::translate("UIDetails", "Acceleration", "details (display)"),
+ acceleration.join(", "));
+ }
+
+ /* Remote desktop server: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_VRDE)
+ {
+ const CVRDEServer comServer = comMachine.GetVRDEServer();
+ if (!comServer.isNull())
+ {
+ if (comServer.GetEnabled())
+ table << UITextTableLine(QApplication::translate("UIDetails", "Remote Desktop Server Port", "details (display/vrde)"),
+ comServer.GetVRDEProperty("TCP/Ports"));
+ else
+ table << UITextTableLine(QApplication::translate("UIDetails", "Remote Desktop Server", "details (display/vrde)"),
+ QApplication::translate("UIDetails", "Disabled", "details (display/vrde/VRDE server)"));
+ }
+ }
+
+ /* Recording: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Recording)
+ {
+ CRecordingSettings comRecordingSettings = comMachine.GetRecordingSettings();
+ if (comRecordingSettings.GetEnabled())
+ {
+ /* For now all screens have the same config: */
+ const CRecordingScreenSettings comRecordingScreen0Settings = comRecordingSettings.GetScreenSettings(0);
+
+ /** @todo r=andy Refine these texts (wrt audio and/or video). */
+ table << UITextTableLine(QApplication::translate("UIDetails", "Recording File", "details (display/recording)"),
+ comRecordingScreen0Settings.GetFilename());
+ table << UITextTableLine(QApplication::translate("UIDetails", "Recording Attributes", "details (display/recording)"),
+ QApplication::translate("UIDetails", "Frame Size: %1x%2, Frame Rate: %3fps, Bit Rate: %4kbps")
+ .arg(comRecordingScreen0Settings.GetVideoWidth()).arg(comRecordingScreen0Settings.GetVideoHeight())
+ .arg(comRecordingScreen0Settings.GetVideoFPS()).arg(comRecordingScreen0Settings.GetVideoRate()));
+ }
+ else
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Recording", "details (display/recording)"),
+ QApplication::translate("UIDetails", "Disabled", "details (display/recording)"));
+ }
+ }
+
+ return table;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationStorage(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage &fOptions,
+ bool fLink /* = true */)
+{
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Iterate over all the machine controllers: */
+ foreach (const CStorageController &comController, comMachine.GetStorageControllers())
+ {
+ /* Add controller information: */
+ const QString strControllerName = QApplication::translate("UIMachineSettingsStorage", "Controller: %1");
+ table << UITextTableLine(strControllerName.arg(comController.GetName()), QString());
+ /* Populate map (its sorted!): */
+ QMap<StorageSlot, QString> attachmentsMap;
+ foreach (const CMediumAttachment &attachment, comMachine.GetMediumAttachmentsOfController(comController.GetName()))
+ {
+ /* Acquire device type first of all: */
+ const KDeviceType enmDeviceType = attachment.GetType();
+
+ /* Ignore restricted device types: */
+ if ( ( !(fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_HardDisks)
+ && enmDeviceType == KDeviceType_HardDisk)
+ || ( !(fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_OpticalDevices)
+ && enmDeviceType == KDeviceType_DVD)
+ || ( !(fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_FloppyDevices)
+ && enmDeviceType == KDeviceType_Floppy))
+ continue;
+
+ /* Prepare current storage slot: */
+ const StorageSlot attachmentSlot(comController.GetBus(), attachment.GetPort(), attachment.GetDevice());
+ AssertMsg(comController.isOk(),
+ ("Unable to acquire controller data: %s\n",
+ UIErrorString::formatRC(comController.lastRC()).toUtf8().constData()));
+ if (!comController.isOk())
+ continue;
+
+ /* Prepare attachment information: */
+ QString strAttachmentInfo = uiCommon().storageDetails(attachment.GetMedium(), false, false);
+ /* That hack makes sure 'Inaccessible' word is always bold: */
+ { // hack
+ const QString strInaccessibleString(UICommon::tr("Inaccessible", "medium"));
+ const QString strBoldInaccessibleString(QString("<b>%1</b>").arg(strInaccessibleString));
+ strAttachmentInfo.replace(strInaccessibleString, strBoldInaccessibleString);
+ } // hack
+
+ /* Append 'device slot name' with 'device type name' for optical devices only: */
+ QString strDeviceType = enmDeviceType == KDeviceType_DVD
+ ? QApplication::translate("UIDetails", "[Optical Drive]", "details (storage)")
+ : QString();
+ if (!strDeviceType.isNull())
+ strDeviceType.append(' ');
+
+ /* Insert that attachment information into the map: */
+ if (!strAttachmentInfo.isNull())
+ {
+ /* Configure hovering anchors: */
+ const QString strAnchorType = enmDeviceType == KDeviceType_DVD || enmDeviceType == KDeviceType_Floppy ? QString("mount") :
+ enmDeviceType == KDeviceType_HardDisk ? QString("attach") : QString();
+ const CMedium medium = attachment.GetMedium();
+ const QString strMediumLocation = medium.isNull() ? QString() : medium.GetLocation();
+ if (fLink)
+ attachmentsMap.insert(attachmentSlot,
+ QString("<a href=#%1,%2,%3,%4>%5</a>")
+ .arg(strAnchorType,
+ comController.GetName(),
+ gpConverter->toString(attachmentSlot),
+ strMediumLocation,
+ strDeviceType + strAttachmentInfo));
+ else
+ attachmentsMap.insert(attachmentSlot,
+ QString("%1")
+ .arg(strDeviceType + strAttachmentInfo));
+ }
+ }
+
+ /* Iterate over the sorted map: */
+ const QList<StorageSlot> storageSlots = attachmentsMap.keys();
+ const QList<QString> storageInfo = attachmentsMap.values();
+ for (int i = 0; i < storageSlots.size(); ++i)
+ table << UITextTableLine(QString(" ") + gpConverter->toString(storageSlots[i]), storageInfo[i]);
+ }
+ if (table.isEmpty())
+ table << UITextTableLine(QApplication::translate("UIDetails", "Not Attached", "details (storage)"), QString());
+
+ return table;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationAudio(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio &fOptions)
+{
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ const CAudioSettings comAudioSettings = comMachine.GetAudioSettings();
+ const CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ if (comAdapter.GetEnabled())
+ {
+ /* Host driver: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Driver)
+ {
+ const QString strAnchorType = QString("audio_host_driver_type");
+ const KAudioDriverType enmType = comAdapter.GetAudioDriver();
+ table << UITextTableLine(QApplication::translate("UIDetails", "Host Driver", "details (audio)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg((int)enmType)
+ .arg(gpConverter->toString(enmType)));
+ }
+
+ /* Controller: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Controller)
+ {
+ const QString strAnchorType = QString("audio_controller_type");
+ const KAudioControllerType enmType = comAdapter.GetAudioController();
+ table << UITextTableLine(QApplication::translate("UIDetails", "Controller", "details (audio)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg((int)enmType)
+ .arg(gpConverter->toString(enmType)));
+ }
+
+#ifdef VBOX_WITH_AUDIO_INOUT_INFO
+ /* Audio I/O: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_IO)
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Audio Input", "details (audio)"),
+ comAdapter.GetEnabledIn() ?
+ QApplication::translate("UIDetails", "Enabled", "details (audio/input)") :
+ QApplication::translate("UIDetails", "Disabled", "details (audio/input)"));
+ table << UITextTableLine(QApplication::translate("UIDetails", "Audio Output", "details (audio)"),
+ comAdapter.GetEnabledOut() ?
+ QApplication::translate("UIDetails", "Enabled", "details (audio/output)") :
+ QApplication::translate("UIDetails", "Disabled", "details (audio/output)"));
+ }
+#endif /* VBOX_WITH_AUDIO_INOUT_INFO */
+ }
+ else
+ table << UITextTableLine(QApplication::translate("UIDetails", "Disabled", "details (audio)"),
+ QString());
+
+ return table;
+}
+
+QString summarizeGenericProperties(const CNetworkAdapter &comAdapter)
+{
+ QVector<QString> names;
+ QVector<QString> props;
+ props = comAdapter.GetProperties(QString(), names);
+ QString strResult;
+ for (int i = 0; i < names.size(); ++i)
+ {
+ strResult += names[i] + "=" + props[i];
+ if (i < names.size() - 1)
+ strResult += ", ";
+ }
+ return strResult;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationNetwork(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork &fOptions)
+{
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Iterate over all the adapters: */
+ const ulong uCount = uiCommon().virtualBox().GetSystemProperties().GetMaxNetworkAdapters(comMachine.GetChipsetType());
+ for (ulong uSlot = 0; uSlot < uCount; ++uSlot)
+ {
+ const QString strAnchorType = QString("network_attachment_type");
+ const CNetworkAdapter comAdapter = comMachine.GetNetworkAdapter(uSlot);
+
+ /* Skip disabled adapters: */
+ if (!comAdapter.GetEnabled())
+ continue;
+
+ /* Gather adapter information: */
+ const KNetworkAttachmentType enmAttachmentType = comAdapter.GetAttachmentType();
+ const QString strAttachmentTemplate = gpConverter->toString(comAdapter.GetAdapterType()).replace(QRegularExpression("\\s\\(.+\\)"),
+ " (<a href=#%1,%2;%3;%4>%5</a>)");
+ QString strAttachmentType;
+ switch (enmAttachmentType)
+ {
+ case KNetworkAttachmentType_NAT:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NAT)
+ strAttachmentType = strAttachmentTemplate
+ .arg(strAnchorType)
+ .arg(uSlot)
+ .arg((int)KNetworkAttachmentType_NAT)
+ .arg(QString())
+ .arg(gpConverter->toString(KNetworkAttachmentType_NAT));
+ break;
+ }
+ case KNetworkAttachmentType_Bridged:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_BridgedAdapter)
+ {
+ const QString strName = comAdapter.GetBridgedInterface();
+ strAttachmentType = strAttachmentTemplate
+ .arg(strAnchorType)
+ .arg(uSlot)
+ .arg((int)KNetworkAttachmentType_Bridged)
+ .arg(strName)
+ .arg(QApplication::translate("UIDetails", "Bridged Adapter, %1", "details (network)")
+ .arg(strName));
+ }
+ break;
+ }
+ case KNetworkAttachmentType_Internal:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_InternalNetwork)
+ {
+ const QString strName = comAdapter.GetInternalNetwork();
+ strAttachmentType = strAttachmentTemplate
+ .arg(strAnchorType)
+ .arg(uSlot)
+ .arg((int)KNetworkAttachmentType_Internal)
+ .arg(strName)
+ .arg(QApplication::translate("UIDetails", "Internal Network, '%1'", "details (network)")
+ .arg(strName));
+ }
+ break;
+ }
+ case KNetworkAttachmentType_HostOnly:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyAdapter)
+ {
+ const QString strName = comAdapter.GetHostOnlyInterface();
+ strAttachmentType = strAttachmentTemplate
+ .arg(strAnchorType)
+ .arg(uSlot)
+ .arg((int)KNetworkAttachmentType_HostOnly)
+ .arg(strName)
+ .arg(QApplication::translate("UIDetails", "Host-only Adapter, '%1'", "details (network)")
+ .arg(strName));
+ }
+ break;
+ }
+#ifdef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnlyNetwork:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyNetwork)
+ {
+ const QString strName = comAdapter.GetHostOnlyNetwork();
+ strAttachmentType = strAttachmentTemplate
+ .arg(strAnchorType)
+ .arg(uSlot)
+ .arg((int)KNetworkAttachmentType_HostOnly)
+ .arg(strName)
+ .arg(QApplication::translate("UIDetails", "Host-only Network, '%1'", "details (network)")
+ .arg(strName));
+ }
+ break;
+ }
+#endif /* VBOX_WITH_VMNET */
+ case KNetworkAttachmentType_Generic:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_GenericDriver)
+ {
+ const QString strName = comAdapter.GetGenericDriver();
+ const QString strGenericDriverProperties(summarizeGenericProperties(comAdapter));
+ strAttachmentType = strGenericDriverProperties.isNull()
+ ? strAttachmentTemplate
+ .arg(strAnchorType)
+ .arg(uSlot)
+ .arg((int)KNetworkAttachmentType_Generic)
+ .arg(strName)
+ .arg(QApplication::translate("UIDetails", "Generic Driver, '%1'", "details (network)")
+ .arg(strName))
+ : strAttachmentTemplate
+ .arg(strAnchorType)
+ .arg(uSlot)
+ .arg((int)KNetworkAttachmentType_Generic)
+ .arg(strName)
+ .arg(QApplication::translate("UIDetails", "Generic Driver, '%1' { %2 }", "details (network)")
+ .arg(strName, strGenericDriverProperties));
+ }
+ break;
+ }
+ case KNetworkAttachmentType_NATNetwork:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NATNetwork)
+ {
+ const QString strName = comAdapter.GetNATNetwork();
+ strAttachmentType = strAttachmentTemplate
+ .arg(strAnchorType)
+ .arg(uSlot)
+ .arg((int)KNetworkAttachmentType_NATNetwork)
+ .arg(strName)
+ .arg(QApplication::translate("UIDetails", "NAT Network, '%1'", "details (network)")
+ .arg(strName));
+ }
+ break;
+ }
+ default:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NotAttached)
+ strAttachmentType = strAttachmentTemplate
+ .arg(strAnchorType)
+ .arg(uSlot)
+ .arg((int)enmAttachmentType)
+ .arg(QString())
+ .arg(gpConverter->toString(enmAttachmentType));
+ break;
+ }
+ }
+ if (!strAttachmentType.isNull())
+ table << UITextTableLine(QApplication::translate("UIDetails", "Adapter %1", "details (network)").arg(comAdapter.GetSlot() + 1), strAttachmentType);
+ }
+ if (table.isEmpty())
+ table << UITextTableLine(QApplication::translate("UIDetails", "Disabled", "details (network/adapter)"), QString());
+
+ return table;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationSerial(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial &fOptions)
+{
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Iterate over all the ports: */
+ const ulong uCount = uiCommon().virtualBox().GetSystemProperties().GetSerialPortCount();
+ for (ulong uSlot = 0; uSlot < uCount; ++uSlot)
+ {
+ const CSerialPort comPort = comMachine.GetSerialPort(uSlot);
+
+ /* Skip disabled adapters: */
+ if (!comPort.GetEnabled())
+ continue;
+
+ /* Gather port information: */
+ const KPortMode enmMode = comPort.GetHostMode();
+ const QString strModeTemplate = UITranslator::toCOMPortName(comPort.GetIRQ(), comPort.GetIOBase()) + ", ";
+ QString strModeType;
+ switch (enmMode)
+ {
+ case KPortMode_HostPipe:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_HostPipe)
+ strModeType = strModeTemplate + QString("%1 (%2)").arg(gpConverter->toString(enmMode)).arg(QDir::toNativeSeparators(comPort.GetPath()));
+ break;
+ }
+ case KPortMode_HostDevice:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_HostDevice)
+ strModeType = strModeTemplate + QString("%1 (%2)").arg(gpConverter->toString(enmMode)).arg(QDir::toNativeSeparators(comPort.GetPath()));
+ break;
+ }
+ case KPortMode_RawFile:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_RawFile)
+ strModeType = strModeTemplate + QString("%1 (%2)").arg(gpConverter->toString(enmMode)).arg(QDir::toNativeSeparators(comPort.GetPath()));
+ break;
+ }
+ case KPortMode_TCP:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_TCP)
+ strModeType = strModeTemplate + QString("%1 (%2)").arg(gpConverter->toString(enmMode)).arg(QDir::toNativeSeparators(comPort.GetPath()));
+ break;
+ }
+ default:
+ {
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Disconnected)
+ strModeType = strModeTemplate + gpConverter->toString(enmMode);
+ break;
+ }
+ }
+ if (!strModeType.isNull())
+ table << UITextTableLine(QApplication::translate("UIDetails", "Port %1", "details (serial)").arg(comPort.GetSlot() + 1), strModeType);
+ }
+ if (table.isEmpty())
+ table << UITextTableLine(QApplication::translate("UIDetails", "Disabled", "details (serial)"), QString());
+
+ return table;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationUSB(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb &fOptions)
+{
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Iterate over all the USB filters: */
+ const CUSBDeviceFilters comFilterObject = comMachine.GetUSBDeviceFilters();
+ if (!comFilterObject.isNull() && comMachine.GetUSBProxyAvailable())
+ {
+ const QString strAnchorType = QString("usb_controller_type");
+ const CUSBControllerVector controllers = comMachine.GetUSBControllers();
+ if (!controllers.isEmpty())
+ {
+ /* USB controllers: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Controller)
+ {
+ QStringList controllerInternal;
+ QStringList controllersReadable;
+ foreach (const CUSBController &comController, controllers)
+ {
+ const KUSBControllerType enmType = comController.GetType();
+ controllerInternal << QString::number((int)enmType);
+ controllersReadable << gpConverter->toString(enmType);
+ }
+ table << UITextTableLine(QApplication::translate("UIDetails", "USB Controller", "details (usb)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg(controllerInternal.join(';'))
+ .arg(controllersReadable.join(", ")));
+ }
+
+ /* Device filters: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_DeviceFilters)
+ {
+ const CUSBDeviceFilterVector filters = comFilterObject.GetDeviceFilters();
+ uint uActive = 0;
+ for (int i = 0; i < filters.size(); ++i)
+ if (filters.at(i).GetActive())
+ ++uActive;
+ table << UITextTableLine(QApplication::translate("UIDetails", "Device Filters", "details (usb)"),
+ QApplication::translate("UIDetails", "%1 (%2 active)", "details (usb)").arg(filters.size()).arg(uActive));
+ }
+ }
+ else
+ table << UITextTableLine(QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg(QString::number((int)KUSBControllerType_Null))
+ .arg(QApplication::translate("UIDetails", "Disabled", "details (usb)")),
+ QString());
+ }
+ else
+ table << UITextTableLine(QApplication::translate("UIDetails", "USB Controller Inaccessible", "details (usb)"), QString());
+
+ return table;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationSharedFolders(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders &fOptions)
+{
+ Q_UNUSED(fOptions);
+
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Summary: */
+ const ulong uCount = comMachine.GetSharedFolders().size();
+ if (uCount > 0)
+ table << UITextTableLine(QApplication::translate("UIDetails", "Shared Folders", "details (shared folders)"), QString::number(uCount));
+ else
+ table << UITextTableLine(QApplication::translate("UIDetails", "None", "details (shared folders)"), QString());
+
+ return table;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationUI(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface &fOptions)
+{
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Visual state: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_VisualState)
+ {
+ const QString strAnchorType = QString("visual_state");
+ const QString strEnabledFullscreen = comMachine.GetExtraData(UIExtraDataDefs::GUI_Fullscreen);
+ const QString strEnabledSeamless = comMachine.GetExtraData(UIExtraDataDefs::GUI_Seamless);
+ const QString strEnabledScale = comMachine.GetExtraData(UIExtraDataDefs::GUI_Scale);
+ UIVisualStateType enmType = UIVisualStateType_Normal;
+ if ( strEnabledFullscreen.compare("true", Qt::CaseInsensitive) == 0
+ || strEnabledFullscreen.compare("yes", Qt::CaseInsensitive) == 0
+ || strEnabledFullscreen.compare("on", Qt::CaseInsensitive) == 0
+ || strEnabledFullscreen == "1")
+ enmType = UIVisualStateType_Fullscreen;
+ else
+ if ( strEnabledSeamless.compare("true", Qt::CaseInsensitive) == 0
+ || strEnabledSeamless.compare("yes", Qt::CaseInsensitive) == 0
+ || strEnabledSeamless.compare("on", Qt::CaseInsensitive) == 0
+ || strEnabledSeamless == "1")
+ enmType = UIVisualStateType_Seamless;
+ else
+ if ( strEnabledScale.compare("true", Qt::CaseInsensitive) == 0
+ || strEnabledScale.compare("yes", Qt::CaseInsensitive) == 0
+ || strEnabledScale.compare("on", Qt::CaseInsensitive) == 0
+ || strEnabledScale == "1")
+ enmType = UIVisualStateType_Scale;
+ const QString strVisualState = gpConverter->toString(enmType);
+ table << UITextTableLine(QApplication::translate("UIDetails", "Visual State", "details (user interface)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg(enmType)
+ .arg(strVisualState));
+ }
+
+#ifndef VBOX_WS_MAC
+ /* Menu-bar: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_MenuBar)
+ {
+ const QString strAnchorType = QString("menu_bar");
+ const QString strMenubarEnabled = comMachine.GetExtraData(UIExtraDataDefs::GUI_MenuBar_Enabled);
+ const bool fEnabled = !( strMenubarEnabled.compare("false", Qt::CaseInsensitive) == 0
+ || strMenubarEnabled.compare("no", Qt::CaseInsensitive) == 0
+ || strMenubarEnabled.compare("off", Qt::CaseInsensitive) == 0
+ || strMenubarEnabled == "0");
+ table << UITextTableLine(QApplication::translate("UIDetails", "Menu-bar", "details (user interface)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg((int)fEnabled)
+ .arg(fEnabled ? QApplication::translate("UIDetails", "Enabled", "details (user interface/menu-bar)")
+ : QApplication::translate("UIDetails", "Disabled", "details (user interface/menu-bar)")));
+ }
+#endif /* !VBOX_WS_MAC */
+
+ /* Status-bar: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_StatusBar)
+ {
+ const QString strAnchorType = QString("status_bar");
+ const QString strStatusbarEnabled = comMachine.GetExtraData(UIExtraDataDefs::GUI_StatusBar_Enabled);
+ const bool fEnabled = !( strStatusbarEnabled.compare("false", Qt::CaseInsensitive) == 0
+ || strStatusbarEnabled.compare("no", Qt::CaseInsensitive) == 0
+ || strStatusbarEnabled.compare("off", Qt::CaseInsensitive) == 0
+ || strStatusbarEnabled == "0");
+ table << UITextTableLine(QApplication::translate("UIDetails", "Status-bar", "details (user interface)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg((int)fEnabled)
+ .arg(fEnabled ? QApplication::translate("UIDetails", "Enabled", "details (user interface/status-bar)")
+ : QApplication::translate("UIDetails", "Disabled", "details (user interface/status-bar)")));
+ }
+
+#ifndef VBOX_WS_MAC
+ /* Mini-toolbar: */
+ if (fOptions & UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_MiniToolbar)
+ {
+ const QString strAnchorType = QString("mini_toolbar");
+ const QString strMiniToolbarEnabled = comMachine.GetExtraData(UIExtraDataDefs::GUI_ShowMiniToolBar);
+ const bool fEnabled = !( strMiniToolbarEnabled.compare("false", Qt::CaseInsensitive) == 0
+ || strMiniToolbarEnabled.compare("no", Qt::CaseInsensitive) == 0
+ || strMiniToolbarEnabled.compare("off", Qt::CaseInsensitive) == 0
+ || strMiniToolbarEnabled == "0");
+ if (fEnabled)
+ {
+ /* Get mini-toolbar position: */
+ const QString strMiniToolbarPosition = comMachine.GetExtraData(UIExtraDataDefs::GUI_MiniToolBarAlignment);
+ {
+ /* Try to convert loaded data to alignment: */
+ switch (gpConverter->fromInternalString<MiniToolbarAlignment>(strMiniToolbarPosition))
+ {
+ case MiniToolbarAlignment_Top:
+ table << UITextTableLine(QApplication::translate("UIDetails", "Mini-toolbar Position", "details (user interface)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg((int)MiniToolbarAlignment_Top)
+ .arg(QApplication::translate("UIDetails", "Top", "details (user interface/mini-toolbar position)")));
+ break;
+ case MiniToolbarAlignment_Bottom:
+ table << UITextTableLine(QApplication::translate("UIDetails", "Mini-toolbar Position", "details (user interface)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg((int)MiniToolbarAlignment_Bottom)
+ .arg(QApplication::translate("UIDetails", "Bottom", "details (user interface/mini-toolbar position)")));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ table << UITextTableLine(QApplication::translate("UIDetails", "Mini-toolbar", "details (user interface)"),
+ QString("<a href=#%1,%2>%3</a>")
+ .arg(strAnchorType)
+ .arg((int)MiniToolbarAlignment_Disabled)
+ .arg(QApplication::translate("UIDetails", "Disabled", "details (user interface/mini-toolbar)")));
+ }
+#endif /* !VBOX_WS_MAC */
+
+ return table;
+}
+
+UITextTable UIDetailsGenerator::generateMachineInformationDescription(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription &fOptions)
+{
+ Q_UNUSED(fOptions);
+
+ UITextTable table;
+
+ if (comMachine.isNull())
+ return table;
+
+ if (!comMachine.GetAccessible())
+ {
+ table << UITextTableLine(QApplication::translate("UIDetails", "Information Inaccessible", "details"), QString());
+ return table;
+ }
+
+ /* Summary: */
+ const QString strDescription = comMachine.GetDescription();
+ if (!strDescription.isEmpty())
+ table << UITextTableLine(strDescription, QString());
+ else
+ table << UITextTableLine(QApplication::translate("UIDetails", "None", "details (description)"), QString());
+
+ return table;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIDetailsGenerator.h b/src/VBox/Frontends/VirtualBox/src/globals/UIDetailsGenerator.h
new file mode 100644
index 00000000..c012c7e7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIDetailsGenerator.h
@@ -0,0 +1,85 @@
+/* $Id: UIDetailsGenerator.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsGenerator declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIDetailsGenerator_h
+#define FEQT_INCLUDED_SRC_globals_UIDetailsGenerator_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+#include "UITextTable.h"
+
+/* Forward declarations: */
+class CCloudMachine;
+class CFormValue;
+class CMachine;
+
+/** Details generation namespace. */
+namespace UIDetailsGenerator
+{
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationGeneral(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral &fOptions);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationGeneral(CCloudMachine &comCloudMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral &fOptions);
+ SHARED_LIBRARY_STUFF QString generateFormValueInformation(const CFormValue &comFormValue, bool fFull = false);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationSystem(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem &fOptions);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationDisplay(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay &fOptions);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationStorage(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage &fOptions,
+ bool fLink = true);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationAudio(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio &fOptions);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationNetwork(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork &fOptions);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationSerial(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial &fOptions);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationUSB(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb &fOptions);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationSharedFolders(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders &fOptions);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationUI(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface &fOptions);
+
+ SHARED_LIBRARY_STUFF UITextTable generateMachineInformationDescription(CMachine &comMachine,
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription &fOptions);
+}
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIDetailsGenerator_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIDialogPanel.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIDialogPanel.cpp
new file mode 100644
index 00000000..043272ad
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIDialogPanel.cpp
@@ -0,0 +1,133 @@
+/* $Id: UIDialogPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPlainTextEdit>
+#include <QTextCursor>
+#include <QToolButton>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIDialogPanel.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+
+UIDialogPanel::UIDialogPanel(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pMainLayout(0)
+ , m_pCloseButton(0)
+{
+ prepare();
+}
+
+void UIDialogPanel::setCloseButtonShortCut(QKeySequence shortCut)
+{
+ if (!m_pCloseButton)
+ return;
+ m_pCloseButton->setShortcut(shortCut);
+}
+
+QHBoxLayout* UIDialogPanel::mainLayout()
+{
+ return m_pMainLayout;
+}
+
+void UIDialogPanel::prepare()
+{
+ prepareWidgets();
+ prepareConnections();
+ retranslateUi();
+}
+
+void UIDialogPanel::prepareWidgets()
+{
+ m_pMainLayout = new QHBoxLayout(this);
+ if (m_pMainLayout)
+ {
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setContentsMargins(5 /* since there is always a button */, 0, 10 /* standard */, 0);
+ m_pMainLayout->setSpacing(10);
+#else
+ m_pMainLayout->setContentsMargins(qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 2, 0,
+ qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) / 2,
+ qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) / 2);
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing));
+#endif
+ }
+ m_pCloseButton = new QIToolButton;
+ if (m_pCloseButton)
+ {
+ m_pCloseButton->setIcon(UIIconPool::iconSet(":/close_16px.png"));
+ m_pMainLayout->addWidget(m_pCloseButton, 0, Qt::AlignLeft);
+ }
+}
+
+void UIDialogPanel::prepareConnections()
+{
+ if (m_pCloseButton)
+ connect(m_pCloseButton, &QIToolButton::clicked, this, &UIDialogPanel::hide);
+}
+
+void UIDialogPanel::retranslateUi()
+{
+ if (m_pCloseButton)
+ m_pCloseButton->setToolTip(tr("Close the pane"));
+}
+
+void UIDialogPanel::showEvent(QShowEvent *pEvent)
+{
+ QWidget::showEvent(pEvent);
+}
+
+void UIDialogPanel::hideEvent(QHideEvent *pEvent)
+{
+ /* Get focused widget: */
+ QWidget *pFocus = QApplication::focusWidget();
+ /* If focus-widget is valid and child-widget of search-panel,
+ * focus next child-widget in line: */
+ if (pFocus && pFocus->parent() == this)
+ focusNextPrevChild(true);
+ emit sigHidePanel(this);
+
+ QWidget::hideEvent(pEvent);
+}
+
+void UIDialogPanel::addVerticalSeparator()
+{
+ QFrame *pSeparator = new QFrame();
+ if (!pSeparator)
+ return;
+ pSeparator->setFrameShape(QFrame::VLine);
+ pSeparator->setFrameShadow(QFrame::Sunken);
+ mainLayout()->addWidget(pSeparator);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIDialogPanel.h b/src/VBox/Frontends/VirtualBox/src/globals/UIDialogPanel.h
new file mode 100644
index 00000000..bdfe75e9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIDialogPanel.h
@@ -0,0 +1,85 @@
+/* $Id: UIDialogPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIDialogPanel_h
+#define FEQT_INCLUDED_SRC_globals_UIDialogPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+#include <QKeySequence>
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QIToolButton;
+
+
+/** QWidget extension acting as the base class for all the dialog panels like file manager, logviewer etc. */
+class SHARED_LIBRARY_STUFF UIDialogPanel : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ UIDialogPanel(QWidget *pParent = 0);
+ void setCloseButtonShortCut(QKeySequence shortCut);
+ virtual QString panelName() const = 0;
+
+signals:
+
+ void sigHidePanel(UIDialogPanel *pPanel);
+ void sigShowPanel(UIDialogPanel *pPanel);
+
+protected:
+
+ virtual void prepare();
+ virtual void prepareWidgets();
+ virtual void prepareConnections();
+
+ /* Access functions for children classes. */
+ QHBoxLayout* mainLayout();
+
+ /** Handles the translation event. */
+ void retranslateUi() RT_OVERRIDE;
+
+ /** Handles the Qt show @a pEvent. */
+ void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles the Qt hide @a pEvent. */
+ void hideEvent(QHideEvent *pEvent) RT_OVERRIDE;
+ void addVerticalSeparator();
+
+private:
+
+ QHBoxLayout *m_pMainLayout;
+ QIToolButton *m_pCloseButton;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIDialogPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIErrorString.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIErrorString.cpp
new file mode 100644
index 00000000..17630dfd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIErrorString.cpp
@@ -0,0 +1,272 @@
+/* $Id: UIErrorString.cpp $ */
+/** @file
+ * VBox Qt GUI - UIErrorString class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QObject>
+#include <QPalette>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIErrorString.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "COMDefs.h"
+#include "CProgress.h"
+#include "CVirtualBoxErrorInfo.h"
+
+
+/* static */
+QString UIErrorString::formatRC(HRESULT rc)
+{
+ /** @todo r=bird: Not sure why we set the sign bit 31 bit for warnings.
+ * Maybe to try get the error variant? It won't really work for S_FALSE and
+ * probably a bunch of others too. I've modified it on windows to try get
+ * the exact one, the one with the top bit set, or just the value. */
+#ifdef RT_OS_WINDOWS
+ char szDefine[80];
+ if ( !SUCCEEDED_WARNING(rc)
+ || ( RTErrWinQueryDefine(rc, szDefine, sizeof(szDefine), true /*fFailIfUnknown*/) == VERR_NOT_FOUND
+ && RTErrWinQueryDefine(rc | 0x80000000, szDefine, sizeof(szDefine), true /*fFailIfUnknown*/) == VERR_NOT_FOUND))
+ RTErrWinQueryDefine(rc, szDefine, sizeof(szDefine), false /*fFailIfUnknown*/);
+
+ QString str;
+ str.sprintf("%s", szDefine);
+ return str;
+#else
+ const char *pszDefine = RTErrCOMGet(SUCCEEDED_WARNING(rc) ? rc | 0x80000000 : rc)->pszDefine;
+ Assert(pszDefine);
+
+ return QString(pszDefine);
+#endif
+}
+
+/* static */
+QString UIErrorString::formatRCFull(HRESULT rc)
+{
+ /** @todo r=bird: See UIErrorString::formatRC for 31th bit discussion. */
+ char szHex[32];
+ RTStrPrintf(szHex, sizeof(szHex), "%#010X", rc);
+
+#ifdef RT_OS_WINDOWS
+ char szDefine[80];
+ ssize_t cchRet = RTErrWinQueryDefine(rc, szDefine, sizeof(szDefine), true /*fFailIfUnknown*/);
+ if (cchRet == VERR_NOT_FOUND && SUCCEEDED_WARNING(rc))
+ cchRet = RTErrWinQueryDefine(rc | 0x80000000, szDefine, sizeof(szDefine), true /*fFailIfUnknown*/);
+
+ if (cchRet != VERR_NOT_FOUND)
+ return QString(szDefine).append(" (").append(szHex).append(")");
+#else
+ const char *pszDefine = RTErrCOMGet(SUCCEEDED_WARNING(rc) ? rc | 0x80000000 : rc)->pszDefine;
+ Assert(pszDefine);
+
+ if (strncmp(pszDefine, RT_STR_TUPLE("Unknown ")))
+ return QString(pszDefine).append(" (").append(szHex).append(")");
+#endif
+ return QString(szHex);
+}
+
+/* static */
+QString UIErrorString::formatErrorInfo(const CProgress &comProgress)
+{
+ /* Check for API errors first: */
+ if (!comProgress.isOk())
+ return formatErrorInfo(static_cast<COMBaseWithEI>(comProgress));
+
+ /* For progress errors otherwise: */
+ CVirtualBoxErrorInfo comErrorInfo = comProgress.GetErrorInfo();
+ /* Handle valid error-info first: */
+ if (!comErrorInfo.isNull())
+ return formatErrorInfo(comErrorInfo);
+ /* Handle NULL error-info otherwise: */
+ return QString("<table bgcolor=%1 border=0 cellspacing=5 cellpadding=0 width=100%>"
+ "<tr><td>%2</td><td><tt>%3</tt></td></tr></table>")
+ .arg(QApplication::palette().color(QPalette::Active, QPalette::Window).name(QColor::HexRgb))
+ .arg(QApplication::translate("UIErrorString", "Result&nbsp;Code:", "error info"))
+ .arg(formatRCFull(comProgress.GetResultCode()))
+ .prepend("<!--EOM-->") /* move to details */;
+}
+
+/* static */
+QString UIErrorString::formatErrorInfo(const COMErrorInfo &comInfo, HRESULT wrapperRC /* = S_OK */)
+{
+ return QString("<qt>%1</qt>").arg(UIErrorString::errorInfoToString(comInfo, wrapperRC));
+}
+
+/* static */
+QString UIErrorString::formatErrorInfo(const CVirtualBoxErrorInfo &comInfo)
+{
+ return formatErrorInfo(COMErrorInfo(comInfo));
+}
+
+/* static */
+QString UIErrorString::formatErrorInfo(const COMBaseWithEI &comWrapper)
+{
+ Assert(comWrapper.lastRC() != S_OK);
+ return formatErrorInfo(comWrapper.errorInfo(), comWrapper.lastRC());
+}
+
+/* static */
+QString UIErrorString::formatErrorInfo(const COMResult &comRc)
+{
+ Assert(comRc.rc() != S_OK);
+ return formatErrorInfo(comRc.errorInfo(), comRc.rc());
+}
+
+/* static */
+QString UIErrorString::simplifiedErrorInfo(const COMErrorInfo &comInfo, HRESULT wrapperRC /* = S_OK */)
+{
+ return UIErrorString::errorInfoToSimpleString(comInfo, wrapperRC);
+}
+
+/* static */
+QString UIErrorString::simplifiedErrorInfo(const COMBaseWithEI &comWrapper)
+{
+ Assert(comWrapper.lastRC() != S_OK);
+ return simplifiedErrorInfo(comWrapper.errorInfo(), comWrapper.lastRC());
+}
+
+/* static */
+QString UIErrorString::errorInfoToString(const COMErrorInfo &comInfo, HRESULT wrapperRC)
+{
+ /* Compose complex details string with internal <!--EOM--> delimiter to
+ * make it possible to split string into info & details parts which will
+ * be used separately in QIMessageBox. */
+ QString strFormatted;
+
+ /* Check if details text is NOT empty: */
+ const QString strDetailsInfo = comInfo.text();
+ if (!strDetailsInfo.isEmpty())
+ {
+ /* Check if details text written in English (latin1) and translated: */
+ if ( strDetailsInfo == QString::fromLatin1(strDetailsInfo.toLatin1())
+ && strDetailsInfo != QObject::tr(strDetailsInfo.toLatin1().constData()))
+ strFormatted += QString("<p>%1.</p>").arg(UITranslator::emphasize(QObject::tr(strDetailsInfo.toLatin1().constData())));
+ else
+ strFormatted += QString("<p>%1.</p>").arg(UITranslator::emphasize(strDetailsInfo));
+ }
+
+ strFormatted += QString("<!--EOM--><table bgcolor=%1 border=0 cellspacing=5 cellpadding=0 width=100%>")
+ .arg(QApplication::palette().color(QPalette::Active, QPalette::Window).name(QColor::HexRgb));
+
+ bool fHaveResultCode = false;
+
+ if (comInfo.isBasicAvailable())
+ {
+#ifdef VBOX_WS_WIN
+ fHaveResultCode = comInfo.isFullAvailable();
+ bool fHaveComponent = true;
+ bool fHaveInterfaceID = true;
+#else /* !VBOX_WS_WIN */
+ fHaveResultCode = true;
+ bool fHaveComponent = comInfo.isFullAvailable();
+ bool fHaveInterfaceID = comInfo.isFullAvailable();
+#endif
+
+ if (fHaveResultCode)
+ {
+ strFormatted += QString("<tr><td>%1</td><td><tt>%2</tt></td></tr>")
+ .arg(QApplication::translate("UIErrorString", "Result&nbsp;Code:", "error info"))
+ .arg(formatRCFull(comInfo.resultCode()));
+ }
+
+ if (fHaveComponent)
+ strFormatted += QString("<tr><td>%1</td><td>%2</td></tr>")
+ .arg(QApplication::translate("UIErrorString", "Component:", "error info"), comInfo.component());
+
+ if (fHaveInterfaceID)
+ {
+ QString s = comInfo.interfaceID().toString();
+ if (!comInfo.interfaceName().isEmpty())
+ s = comInfo.interfaceName() + ' ' + s;
+ strFormatted += QString("<tr><td>%1</td><td>%2</td></tr>")
+ .arg(QApplication::translate("UIErrorString", "Interface:", "error info"), s);
+ }
+
+ if (!comInfo.calleeIID().isNull() && comInfo.calleeIID() != comInfo.interfaceID())
+ {
+ QString s = comInfo.calleeIID().toString();
+ if (!comInfo.calleeName().isEmpty())
+ s = comInfo.calleeName() + ' ' + s;
+ strFormatted += QString("<tr><td>%1</td><td>%2</td></tr>")
+ .arg(QApplication::translate("UIErrorString", "Callee:", "error info"), s);
+ }
+ }
+
+ if ( FAILED(wrapperRC)
+ && (!fHaveResultCode || wrapperRC != comInfo.resultCode()))
+ {
+ strFormatted += QString("<tr><td>%1</td><td><tt>%2</tt></td></tr>")
+ .arg(QApplication::translate("UIErrorString", "Callee&nbsp;RC:", "error info"))
+ .arg(formatRCFull(wrapperRC));
+ }
+
+ strFormatted += "</table>";
+
+ if (comInfo.next())
+ strFormatted = strFormatted + "<!--EOP-->" + errorInfoToString(*comInfo.next());
+
+ return strFormatted;
+}
+
+/* static */
+QString UIErrorString::errorInfoToSimpleString(const COMErrorInfo &comInfo, HRESULT wrapperRC /* = S_OK */)
+{
+ /* Compose complex details string with text and status code: */
+ QString strFormatted;
+
+ /* Check if details text is NOT empty: */
+ const QString strDetailsInfo = comInfo.text();
+ if (!strDetailsInfo.isEmpty())
+ strFormatted += strDetailsInfo;
+
+ /* Check if we have result code: */
+ bool fHaveResultCode = false;
+
+ if (comInfo.isBasicAvailable())
+ {
+#ifdef VBOX_WS_WIN
+ fHaveResultCode = comInfo.isFullAvailable();
+#else
+ fHaveResultCode = true;
+#endif
+
+ if (fHaveResultCode)
+ strFormatted += "; " + QString("Result Code: ") + formatRCFull(comInfo.resultCode());
+ }
+
+ if ( FAILED(wrapperRC)
+ && (!fHaveResultCode || wrapperRC != comInfo.resultCode()))
+ strFormatted += "; " + QString("Callee RC: ") + formatRCFull(wrapperRC);
+
+ /* Check if we have next error queued: */
+ if (comInfo.next())
+ strFormatted += "; " + errorInfoToSimpleString(*comInfo.next());
+
+ return strFormatted;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIErrorString.h b/src/VBox/Frontends/VirtualBox/src/globals/UIErrorString.h
new file mode 100644
index 00000000..f1339208
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIErrorString.h
@@ -0,0 +1,85 @@
+/* $Id: UIErrorString.h $ */
+/** @file
+ * VBox Qt GUI - UIErrorString class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIErrorString_h
+#define FEQT_INCLUDED_SRC_globals_UIErrorString_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <VBox/com/defs.h>
+
+/* Forward declarations: */
+class COMBaseWithEI;
+class COMErrorInfo;
+class COMResult;
+class CProgress;
+class CVirtualBoxErrorInfo;
+
+/** Namespace simplifying COM error formatting. */
+class SHARED_LIBRARY_STUFF UIErrorString
+{
+public:
+
+ /** Returns formatted @a rc information. */
+ static QString formatRC(HRESULT rc);
+ /** Returns full formatted @a rc information. */
+ static QString formatRCFull(HRESULT rc);
+ /** Returns formatted error information for passed @a comProgress. */
+ static QString formatErrorInfo(const CProgress &comProgress);
+ /** Returns formatted error information for passed @a comInfo and @a wrapperRC. */
+ static QString formatErrorInfo(const COMErrorInfo &comInfo, HRESULT wrapperRC = S_OK);
+ /** Returns formatted error information for passed @a comInfo. */
+ static QString formatErrorInfo(const CVirtualBoxErrorInfo &comInfo);
+ /** Returns formatted error information for passed @a comWrapper. */
+ static QString formatErrorInfo(const COMBaseWithEI &comWrapper);
+ /** Returns formatted error information for passed @a comRc. */
+ static QString formatErrorInfo(const COMResult &comRc);
+
+ /** Returns simplified error information for passed @a comInfo and @a wrapperRC. */
+ static QString simplifiedErrorInfo(const COMErrorInfo &comInfo, HRESULT wrapperRC = S_OK);
+ /** Returns simplified error information for passed @a comWrapper. */
+ static QString simplifiedErrorInfo(const COMBaseWithEI &comWrapper);
+
+private:
+
+ /** Converts passed @a comInfo and @a wrapperRC to string. */
+ static QString errorInfoToString(const COMErrorInfo &comInfo, HRESULT wrapperRC = S_OK);
+
+ /** Converts passed @a comInfo and @a wrapperRC to simplified string. */
+ static QString errorInfoToSimpleString(const COMErrorInfo &comInfo, HRESULT wrapperRC = S_OK);
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIErrorString_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIExtension.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIExtension.cpp
new file mode 100644
index 00000000..081615f0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIExtension.cpp
@@ -0,0 +1,121 @@
+/* $Id: UIExtension.cpp $ */
+/** @file
+ * VBox Qt GUI - UIExtension namespace implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIExtension.h"
+#include "UINotificationCenter.h"
+#include "UIMessageCenter.h"
+#include "VBoxLicenseViewer.h"
+
+/* COM includes: */
+#include "CExtPack.h"
+
+
+void UIExtension::install(QString const &strFilePath,
+ QString const &strDigest,
+ QWidget *pParent,
+ QString *pstrExtPackName)
+{
+ /* If the extension pack manager isn't available, skip any attempts to install: */
+ CExtPackManager comExtPackManager = uiCommon().virtualBox().GetExtensionPackManager();
+ if (comExtPackManager.isNull())
+ return;
+ /* Open the extpack tarball via IExtPackManager: */
+ CExtPackFile comExtPackFile;
+ if (strDigest.isEmpty())
+ comExtPackFile = comExtPackManager.OpenExtPackFile(strFilePath);
+ else
+ {
+ QString strFileAndHash = QString("%1::SHA-256=%2").arg(strFilePath).arg(strDigest);
+ comExtPackFile = comExtPackManager.OpenExtPackFile(strFileAndHash);
+ }
+ if (!comExtPackManager.isOk())
+ {
+ UINotificationMessage::cannotOpenExtPack(comExtPackManager, strFilePath);
+ return;
+ }
+
+ if (!comExtPackFile.GetUsable())
+ {
+ UINotificationMessage::cannotReadExtPack(comExtPackFile, strFilePath);
+ return;
+ }
+
+ const QString strPackName = comExtPackFile.GetName();
+ const QString strPackDescription = comExtPackFile.GetDescription();
+ const QString strPackVersion = QString("%1r%2%3").arg(comExtPackFile.GetVersion()).arg(comExtPackFile.GetRevision()).arg(comExtPackFile.GetEdition());
+
+ /* Check if there is a version of the extension pack already
+ * installed on the system and let the user decide what to do about it. */
+ CExtPack comExtPackCur = comExtPackManager.Find(strPackName);
+ bool fReplaceIt = comExtPackCur.isOk();
+ if (fReplaceIt)
+ {
+ QString strPackVersionCur = QString("%1r%2%3").arg(comExtPackCur.GetVersion()).arg(comExtPackCur.GetRevision()).arg(comExtPackCur.GetEdition());
+ if (!msgCenter().confirmReplaceExtensionPack(strPackName, strPackVersion, strPackVersionCur, strPackDescription, pParent))
+ return;
+ }
+ /* If it's a new package just ask for general confirmation. */
+ else
+ {
+ if (!msgCenter().confirmInstallExtensionPack(strPackName, strPackVersion, strPackDescription, pParent))
+ return;
+ }
+
+ /* Display the license dialog if required by the extension pack. */
+ if (comExtPackFile.GetShowLicense())
+ {
+ QString strLicense = comExtPackFile.GetLicense();
+ VBoxLicenseViewer licenseViewer(pParent);
+ if (licenseViewer.showLicenseFromString(strLicense) != QDialog::Accepted)
+ return;
+ }
+
+ /* Install the selected package.
+ * Set the package name return value before doing
+ * this as the caller should do a refresh even on failure. */
+ QString strDisplayInfo;
+#ifdef VBOX_WS_WIN
+ if (pParent)
+ strDisplayInfo.sprintf("hwnd=%#llx", (uint64_t)(uintptr_t)pParent->winId());
+#endif
+
+ /* Install extension pack: */
+ UINotificationProgressExtensionPackInstall *pNotification =
+ new UINotificationProgressExtensionPackInstall(comExtPackFile,
+ fReplaceIt,
+ strPackName,
+ strDisplayInfo);
+ QObject::connect(pNotification, &UINotificationProgressExtensionPackInstall::sigExtensionPackInstalled,
+ &uiCommon(), &UICommon::sigExtensionPackInstalled);
+ gpNotificationCenter->append(pNotification);
+
+ /* Store the name: */
+ if (pstrExtPackName)
+ *pstrExtPackName = strPackName;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIExtension.h b/src/VBox/Frontends/VirtualBox/src/globals/UIExtension.h
new file mode 100644
index 00000000..50fa952c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIExtension.h
@@ -0,0 +1,51 @@
+/* $Id: UIExtension.h $ */
+/** @file
+ * VBox Qt GUI - UIExtension namespace declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIExtension_h
+#define FEQT_INCLUDED_SRC_globals_UIExtension_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** Namespace with common extension pack stuff. */
+namespace UIExtension
+{
+ /** Initiates the extension pack installation process.
+ * @param strFilePath Brings the extension pack file path.
+ * @param strDigest Brings the extension pack file digest.
+ * @param pParent Brings the parent dialog reference.
+ * @param pstrExtPackName Brings the extension pack name. */
+ void SHARED_LIBRARY_STUFF install(QString const &strFilePath,
+ QString const &strDigest,
+ QWidget *pParent,
+ QString *pstrExtPackName);
+}
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIExtension_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIIconPool.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIIconPool.cpp
new file mode 100644
index 00000000..2743f41e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIIconPool.cpp
@@ -0,0 +1,708 @@
+/* $Id: UIIconPool.cpp $ */
+/** @file
+ * VBox Qt GUI - UIIconPool class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QFile>
+#include <QPainter>
+#include <QStyle>
+#include <QWidget>
+
+/* GUI includes: */
+#include "UIIconPool.h"
+#include "UIExtraDataManager.h"
+#include "UIModalWindowManager.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/*********************************************************************************************************************************
+* Class UIIconPool implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+QPixmap UIIconPool::pixmap(const QString &strName)
+{
+ /* Reuse iconSet API: */
+ QIcon icon = iconSet(strName);
+
+ /* Return pixmap of first available size: */
+ const int iHint = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ return icon.pixmap(icon.availableSizes().value(0, QSize(iHint, iHint)));
+}
+
+/* static */
+QIcon UIIconPool::iconSet(const QString &strNormal,
+ const QString &strDisabled /* = QString() */,
+ const QString &strActive /* = QString() */)
+{
+ /* Prepare fallback icon: */
+ static QIcon nullIcon;
+
+ /* Prepare icon: */
+ QIcon icon;
+
+ /* Add 'normal' pixmap: */
+ AssertReturn(!strNormal.isEmpty(), nullIcon);
+ addName(icon, strNormal, QIcon::Normal);
+
+ /* Add 'disabled' pixmap (if any): */
+ if (!strDisabled.isEmpty())
+ addName(icon, strDisabled, QIcon::Disabled);
+
+ /* Add 'active' pixmap (if any): */
+ if (!strActive.isEmpty())
+ addName(icon, strActive, QIcon::Active);
+
+ /* Return icon: */
+ return icon;
+}
+
+/* static */
+QIcon UIIconPool::iconSetOnOff(const QString &strNormal, const QString strNormalOff,
+ const QString &strDisabled /* = QString() */, const QString &strDisabledOff /* = QString() */,
+ const QString &strActive /* = QString() */, const QString &strActiveOff /* = QString() */)
+{
+ /* Prepare fallback icon: */
+ static QIcon nullIcon;
+
+ /* Prepare icon: */
+ QIcon icon;
+
+ /* Add 'normal' on/off pixmaps: */
+ AssertReturn(!strNormal.isEmpty(), nullIcon);
+ addName(icon, strNormal, QIcon::Normal, QIcon::On);
+ AssertReturn(!strNormalOff.isEmpty(), nullIcon);
+ addName(icon, strNormalOff, QIcon::Normal, QIcon::Off);
+
+ /* Add 'disabled' on/off pixmaps (if any): */
+ if (!strDisabled.isEmpty())
+ addName(icon, strDisabled, QIcon::Disabled, QIcon::On);
+ if (!strDisabledOff.isEmpty())
+ addName(icon, strDisabledOff, QIcon::Disabled, QIcon::Off);
+
+ /* Add 'active' on/off pixmaps (if any): */
+ if (!strActive.isEmpty())
+ addName(icon, strActive, QIcon::Active, QIcon::On);
+ if (!strActiveOff.isEmpty())
+ addName(icon, strActiveOff, QIcon::Active, QIcon::Off);
+
+ /* Return icon: */
+ return icon;
+}
+
+/* static */
+QIcon UIIconPool::iconSetFull(const QString &strNormal, const QString &strSmall,
+ const QString &strNormalDisabled /* = QString() */, const QString &strSmallDisabled /* = QString() */,
+ const QString &strNormalActive /* = QString() */, const QString &strSmallActive /* = QString() */)
+{
+ /* Prepare fallback icon: */
+ static QIcon nullIcon;
+
+ /* Prepare icon: */
+ QIcon icon;
+
+ /* Add 'normal' & 'small normal' pixmaps: */
+ AssertReturn(!strNormal.isEmpty(), nullIcon);
+ addName(icon, strNormal, QIcon::Normal);
+ AssertReturn(!strSmall.isEmpty(), nullIcon);
+ addName(icon, strSmall, QIcon::Normal);
+
+ /* Add 'disabled' & 'small disabled' pixmaps (if any): */
+ if (!strNormalDisabled.isEmpty())
+ addName(icon, strNormalDisabled, QIcon::Disabled);
+ if (!strSmallDisabled.isEmpty())
+ addName(icon, strSmallDisabled, QIcon::Disabled);
+
+ /* Add 'active' & 'small active' pixmaps (if any): */
+ if (!strNormalActive.isEmpty())
+ addName(icon, strNormalActive, QIcon::Active);
+ if (!strSmallActive.isEmpty())
+ addName(icon, strSmallActive, QIcon::Active);
+
+ /* Return icon: */
+ return icon;
+}
+
+/* static */
+QIcon UIIconPool::iconSet(const QPixmap &normal,
+ const QPixmap &disabled /* = QPixmap() */,
+ const QPixmap &active /* = QPixmap() */)
+{
+ QIcon iconSet;
+
+ Assert(!normal.isNull());
+ iconSet.addPixmap(normal, QIcon::Normal);
+
+ if (!disabled.isNull())
+ iconSet.addPixmap(disabled, QIcon::Disabled);
+
+ if (!active.isNull())
+ iconSet.addPixmap(active, QIcon::Active);
+
+ return iconSet;
+}
+
+/* static */
+QIcon UIIconPool::defaultIcon(UIDefaultIconType defaultIconType, const QWidget *pWidget /* = 0 */)
+{
+ QIcon icon;
+ QStyle *pStyle = pWidget ? pWidget->style() : QApplication::style();
+ switch (defaultIconType)
+ {
+ case UIDefaultIconType_MessageBoxInformation:
+ {
+ icon = pStyle->standardIcon(QStyle::SP_MessageBoxInformation, 0, pWidget);
+ break;
+ }
+ case UIDefaultIconType_MessageBoxQuestion:
+ {
+ icon = pStyle->standardIcon(QStyle::SP_MessageBoxQuestion, 0, pWidget);
+ break;
+ }
+ case UIDefaultIconType_MessageBoxWarning:
+ {
+#ifdef VBOX_WS_MAC
+ /* At least in Qt 4.3.4/4.4 RC1 SP_MessageBoxWarning is the application
+ * icon. So change this to the critical icon. (Maybe this would be
+ * fixed in a later Qt version) */
+ icon = pStyle->standardIcon(QStyle::SP_MessageBoxCritical, 0, pWidget);
+#else /* VBOX_WS_MAC */
+ icon = pStyle->standardIcon(QStyle::SP_MessageBoxWarning, 0, pWidget);
+#endif /* !VBOX_WS_MAC */
+ break;
+ }
+ case UIDefaultIconType_MessageBoxCritical:
+ {
+ icon = pStyle->standardIcon(QStyle::SP_MessageBoxCritical, 0, pWidget);
+ break;
+ }
+ case UIDefaultIconType_DialogCancel:
+ {
+ icon = pStyle->standardIcon(QStyle::SP_DialogCancelButton, 0, pWidget);
+ if (icon.isNull())
+ icon = iconSet(":/cancel_16px.png");
+ break;
+ }
+ case UIDefaultIconType_DialogHelp:
+ {
+ icon = pStyle->standardIcon(QStyle::SP_DialogHelpButton, 0, pWidget);
+ if (icon.isNull())
+ icon = iconSet(":/help_16px.png");
+ break;
+ }
+ case UIDefaultIconType_ArrowBack:
+ {
+ icon = pStyle->standardIcon(QStyle::SP_ArrowBack, 0, pWidget);
+ if (icon.isNull())
+ icon = iconSet(":/list_moveup_16px.png",
+ ":/list_moveup_disabled_16px.png");
+ break;
+ }
+ case UIDefaultIconType_ArrowForward:
+ {
+ icon = pStyle->standardIcon(QStyle::SP_ArrowForward, 0, pWidget);
+ if (icon.isNull())
+ icon = iconSet(":/list_movedown_16px.png",
+ ":/list_movedown_disabled_16px.png");
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Unknown default icon type!"));
+ break;
+ }
+ }
+ return icon;
+}
+
+/* static */
+QPixmap UIIconPool::joinPixmaps(const QPixmap &pixmap1, const QPixmap &pixmap2)
+{
+ if (pixmap1.isNull())
+ return pixmap2;
+ if (pixmap2.isNull())
+ return pixmap1;
+
+ QPixmap result(pixmap1.width() + pixmap2.width() + 2,
+ qMax(pixmap1.height(), pixmap2.height()));
+ result.fill(Qt::transparent);
+
+ QPainter painter(&result);
+ painter.drawPixmap(0, 0, pixmap1);
+ painter.drawPixmap(pixmap1.width() + 2, result.height() - pixmap2.height(), pixmap2);
+ painter.end();
+
+ return result;
+}
+
+/* static */
+void UIIconPool::addName(QIcon &icon, const QString &strName,
+ QIcon::Mode mode /* = QIcon::Normal */, QIcon::State state /* = QIcon::Off */)
+{
+ /* Prepare pixmap on the basis of passed value: */
+ QPixmap pixmap(strName);
+ /* Add pixmap: */
+ icon.addPixmap(pixmap, mode, state);
+
+#ifdef VBOX_WS_MAC
+ /* Test if HiDPI icons are enabled: */
+ if (!qApp->testAttribute(Qt::AA_UseHighDpiPixmaps))
+ return;
+#endif /* VBOX_WS_MAC */
+
+ /* Parse name to prefix and suffix: */
+ QString strPrefix = strName.section('.', 0, -2);
+ QString strSuffix = strName.section('.', -1, -1);
+ /* Prepare HiDPI pixmaps: */
+ const QStringList aPixmapNames = QStringList() << (strPrefix + "_x2." + strSuffix)
+ << (strPrefix + "_x3." + strSuffix)
+ << (strPrefix + "_x4." + strSuffix);
+ foreach (const QString &strPixmapName, aPixmapNames)
+ {
+ QPixmap pixmapHiDPI(strPixmapName);
+ if (!pixmapHiDPI.isNull())
+ icon.addPixmap(pixmapHiDPI, mode, state);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIIconPoolGeneral implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIIconPoolGeneral *UIIconPoolGeneral::s_pInstance = 0;
+
+/* static */
+void UIIconPoolGeneral::create()
+{
+ AssertReturnVoid(!s_pInstance);
+ new UIIconPoolGeneral;
+}
+
+/* static */
+void UIIconPoolGeneral::destroy()
+{
+ AssertPtrReturnVoid(s_pInstance);
+ delete s_pInstance;
+}
+
+/* static */
+UIIconPoolGeneral *UIIconPoolGeneral::instance()
+{
+ return s_pInstance;
+}
+
+UIIconPoolGeneral::UIIconPoolGeneral()
+{
+ /* Init instance: */
+ s_pInstance = this;
+
+ /* Prepare OS type icon-name hash: */
+ m_guestOSTypeIconNames.insert("Other", ":/os_other.png");
+ m_guestOSTypeIconNames.insert("Other_64", ":/os_other_64.png");
+ m_guestOSTypeIconNames.insert("DOS", ":/os_dos.png");
+ m_guestOSTypeIconNames.insert("Netware", ":/os_netware.png");
+ m_guestOSTypeIconNames.insert("L4", ":/os_l4.png");
+ m_guestOSTypeIconNames.insert("Windows31", ":/os_win31.png");
+ m_guestOSTypeIconNames.insert("Windows95", ":/os_win95.png");
+ m_guestOSTypeIconNames.insert("Windows98", ":/os_win98.png");
+ m_guestOSTypeIconNames.insert("WindowsMe", ":/os_winme.png");
+ m_guestOSTypeIconNames.insert("WindowsNT3x", ":/os_winnt4.png");
+ m_guestOSTypeIconNames.insert("WindowsNT4", ":/os_winnt4.png");
+ m_guestOSTypeIconNames.insert("Windows2000", ":/os_win2k.png");
+ m_guestOSTypeIconNames.insert("WindowsXP", ":/os_winxp.png");
+ m_guestOSTypeIconNames.insert("WindowsXP_64", ":/os_winxp_64.png");
+ m_guestOSTypeIconNames.insert("Windows2003", ":/os_win2k3.png");
+ m_guestOSTypeIconNames.insert("Windows2003_64", ":/os_win2k3_64.png");
+ m_guestOSTypeIconNames.insert("WindowsVista", ":/os_winvista.png");
+ m_guestOSTypeIconNames.insert("WindowsVista_64", ":/os_winvista_64.png");
+ m_guestOSTypeIconNames.insert("Windows2008", ":/os_win2k8.png");
+ m_guestOSTypeIconNames.insert("Windows2008_64", ":/os_win2k8_64.png");
+ m_guestOSTypeIconNames.insert("Windows7", ":/os_win7.png");
+ m_guestOSTypeIconNames.insert("Windows7_64", ":/os_win7_64.png");
+ m_guestOSTypeIconNames.insert("Windows8", ":/os_win8.png");
+ m_guestOSTypeIconNames.insert("Windows8_64", ":/os_win8_64.png");
+ m_guestOSTypeIconNames.insert("Windows81", ":/os_win81.png");
+ m_guestOSTypeIconNames.insert("Windows81_64", ":/os_win81_64.png");
+ m_guestOSTypeIconNames.insert("Windows2012_64", ":/os_win2k12_64.png");
+ m_guestOSTypeIconNames.insert("Windows10", ":/os_win10.png");
+ m_guestOSTypeIconNames.insert("Windows10_64", ":/os_win10_64.png");
+ m_guestOSTypeIconNames.insert("Windows11_64", ":/os_win11_64.png");
+ m_guestOSTypeIconNames.insert("Windows2016_64", ":/os_win2k16_64.png");
+ m_guestOSTypeIconNames.insert("Windows2019_64", ":/os_win2k19_64.png");
+ m_guestOSTypeIconNames.insert("Windows2022_64", ":/os_win2k19_64.png"); /** @todo new icon */
+ m_guestOSTypeIconNames.insert("WindowsNT", ":/os_win_other.png");
+ m_guestOSTypeIconNames.insert("WindowsNT_64", ":/os_win_other_64.png");
+ m_guestOSTypeIconNames.insert("OS2Warp3", ":/os_os2warp3.png");
+ m_guestOSTypeIconNames.insert("OS2Warp4", ":/os_os2warp4.png");
+ m_guestOSTypeIconNames.insert("OS2Warp45", ":/os_os2warp45.png");
+ m_guestOSTypeIconNames.insert("OS2eCS", ":/os_os2ecs.png");
+ m_guestOSTypeIconNames.insert("OS2ArcaOS", ":/os_os2_other.png"); /** @todo icon? */
+ m_guestOSTypeIconNames.insert("OS21x", ":/os_os2_other.png");
+ m_guestOSTypeIconNames.insert("OS2", ":/os_os2_other.png");
+ m_guestOSTypeIconNames.insert("Linux22", ":/os_linux22.png");
+ m_guestOSTypeIconNames.insert("Linux24", ":/os_linux24.png");
+ m_guestOSTypeIconNames.insert("Linux24_64", ":/os_linux24_64.png");
+ m_guestOSTypeIconNames.insert("Linux26", ":/os_linux26.png");
+ m_guestOSTypeIconNames.insert("Linux26_64", ":/os_linux26_64.png");
+ m_guestOSTypeIconNames.insert("ArchLinux", ":/os_archlinux.png");
+ m_guestOSTypeIconNames.insert("ArchLinux_64", ":/os_archlinux_64.png");
+ m_guestOSTypeIconNames.insert("Debian", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian31", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian4", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian4_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian5", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian5_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian5", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian5_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian6", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian6_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian7", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian7_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian8", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian8_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian9", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian9_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian10", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian10_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian11", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian11_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("Debian12", ":/os_debian.png");
+ m_guestOSTypeIconNames.insert("Debian12_64", ":/os_debian_64.png");
+ m_guestOSTypeIconNames.insert("OpenSUSE", ":/os_opensuse.png");
+ m_guestOSTypeIconNames.insert("OpenSUSE_64", ":/os_opensuse_64.png");
+ m_guestOSTypeIconNames.insert("OpenSUSE_Leap_64", ":/os_opensuse_64.png");
+ m_guestOSTypeIconNames.insert("OpenSUSE_Tumbleweed", ":/os_opensuse.png");
+ m_guestOSTypeIconNames.insert("OpenSUSE_Tumbleweed_64", ":/os_opensuse_64.png");
+ m_guestOSTypeIconNames.insert("SUSE_LE", ":/os_opensuse.png");
+ m_guestOSTypeIconNames.insert("SUSE_LE_64", ":/os_opensuse_64.png");
+ m_guestOSTypeIconNames.insert("Fedora", ":/os_fedora.png");
+ m_guestOSTypeIconNames.insert("Fedora_64", ":/os_fedora_64.png");
+ m_guestOSTypeIconNames.insert("Gentoo", ":/os_gentoo.png");
+ m_guestOSTypeIconNames.insert("Gentoo_64", ":/os_gentoo_64.png");
+ m_guestOSTypeIconNames.insert("Mandriva", ":/os_mandriva.png");
+ m_guestOSTypeIconNames.insert("Mandriva_64", ":/os_mandriva_64.png");
+ m_guestOSTypeIconNames.insert("OpenMandriva_Lx", ":/os_mandriva.png");
+ m_guestOSTypeIconNames.insert("OpenMandriva_Lx_64", ":/os_mandriva_64.png");
+ m_guestOSTypeIconNames.insert("PCLinuxOS", ":/os_mandriva.png");
+ m_guestOSTypeIconNames.insert("PCLinuxOS_64", ":/os_mandriva_64.png");
+ m_guestOSTypeIconNames.insert("Mageia", ":/os_mandriva.png");
+ m_guestOSTypeIconNames.insert("Mageia_64", ":/os_mandriva_64.png");
+ m_guestOSTypeIconNames.insert("RedHat", ":/os_redhat.png");
+ m_guestOSTypeIconNames.insert("RedHat_64", ":/os_redhat_64.png");
+ m_guestOSTypeIconNames.insert("RedHat3", ":/os_redhat.png");
+ m_guestOSTypeIconNames.insert("RedHat3_64", ":/os_redhat_64.png");
+ m_guestOSTypeIconNames.insert("RedHat4", ":/os_redhat.png");
+ m_guestOSTypeIconNames.insert("RedHat4_64", ":/os_redhat_64.png");
+ m_guestOSTypeIconNames.insert("RedHat5", ":/os_redhat.png");
+ m_guestOSTypeIconNames.insert("RedHat5_64", ":/os_redhat_64.png");
+ m_guestOSTypeIconNames.insert("RedHat6", ":/os_redhat.png");
+ m_guestOSTypeIconNames.insert("RedHat6_64", ":/os_redhat_64.png");
+ m_guestOSTypeIconNames.insert("RedHat7_64", ":/os_redhat_64.png");
+ m_guestOSTypeIconNames.insert("RedHat8_64", ":/os_redhat_64.png");
+ m_guestOSTypeIconNames.insert("RedHat9_64", ":/os_redhat_64.png");
+ m_guestOSTypeIconNames.insert("Turbolinux", ":/os_turbolinux.png");
+ m_guestOSTypeIconNames.insert("Turbolinux_64", ":/os_turbolinux_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu10_LTS", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu10_LTS_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu10", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu10_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu11", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu11_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu12_LTS", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu12_LTS_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu12", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu12_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu13", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu13_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu14_LTS", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu14_LTS_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu14", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu14_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu15", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu15_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu16_LTS", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu16_LTS_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu16", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu16_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu17", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu17_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu18_LTS", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu18_LTS_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu18", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu18_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu19", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Ubuntu19_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu20_LTS_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu20_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu21_LTS_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu21_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu22_LTS_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu22_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Ubuntu23_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Lubuntu", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Lubuntu_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Xubuntu", ":/os_ubuntu.png");
+ m_guestOSTypeIconNames.insert("Xubuntu_64", ":/os_ubuntu_64.png");
+ m_guestOSTypeIconNames.insert("Xandros", ":/os_xandros.png");
+ m_guestOSTypeIconNames.insert("Xandros_64", ":/os_xandros_64.png");
+ m_guestOSTypeIconNames.insert("Oracle", ":/os_oracle.png");
+ m_guestOSTypeIconNames.insert("Oracle_64", ":/os_oracle_64.png");
+ m_guestOSTypeIconNames.insert("Oracle3", ":/os_oracle.png");
+ m_guestOSTypeIconNames.insert("Oracle3_64", ":/os_oracle_64.png");
+ m_guestOSTypeIconNames.insert("Oracle4", ":/os_oracle.png");
+ m_guestOSTypeIconNames.insert("Oracle4_64", ":/os_oracle_64.png");
+ m_guestOSTypeIconNames.insert("Oracle5", ":/os_oracle.png");
+ m_guestOSTypeIconNames.insert("Oracle5_64", ":/os_oracle_64.png");
+ m_guestOSTypeIconNames.insert("Oracle6", ":/os_oracle.png");
+ m_guestOSTypeIconNames.insert("Oracle6_64", ":/os_oracle_64.png");
+ m_guestOSTypeIconNames.insert("Oracle7_64", ":/os_oracle_64.png");
+ m_guestOSTypeIconNames.insert("Oracle8_64", ":/os_oracle_64.png");
+ m_guestOSTypeIconNames.insert("Oracle9_64", ":/os_oracle_64.png");
+ m_guestOSTypeIconNames.insert("Oracle", ":/os_oracle.png");
+ m_guestOSTypeIconNames.insert("Oracle_64", ":/os_oracle_64.png");
+ m_guestOSTypeIconNames.insert("Linux", ":/os_linux.png");
+ m_guestOSTypeIconNames.insert("Linux_64", ":/os_linux_64.png");
+ m_guestOSTypeIconNames.insert("FreeBSD", ":/os_freebsd.png");
+ m_guestOSTypeIconNames.insert("FreeBSD_64", ":/os_freebsd_64.png");
+ m_guestOSTypeIconNames.insert("OpenBSD", ":/os_openbsd.png");
+ m_guestOSTypeIconNames.insert("OpenBSD_64", ":/os_openbsd_64.png");
+ m_guestOSTypeIconNames.insert("NetBSD", ":/os_netbsd.png");
+ m_guestOSTypeIconNames.insert("NetBSD_64", ":/os_netbsd_64.png");
+ m_guestOSTypeIconNames.insert("Solaris", ":/os_solaris.png");
+ m_guestOSTypeIconNames.insert("Solaris_64", ":/os_solaris_64.png");
+ m_guestOSTypeIconNames.insert("Solaris10U8_or_later", ":/os_solaris.png");
+ m_guestOSTypeIconNames.insert("Solaris10U8_or_later_64", ":/os_solaris_64.png");
+ m_guestOSTypeIconNames.insert("OpenSolaris", ":/os_oraclesolaris.png");
+ m_guestOSTypeIconNames.insert("OpenSolaris_64", ":/os_oraclesolaris_64.png");
+ m_guestOSTypeIconNames.insert("Solaris11_64", ":/os_oraclesolaris_64.png");
+ m_guestOSTypeIconNames.insert("QNX", ":/os_qnx.png");
+ m_guestOSTypeIconNames.insert("MacOS", ":/os_macosx.png");
+ m_guestOSTypeIconNames.insert("MacOS_64", ":/os_macosx_64.png");
+ m_guestOSTypeIconNames.insert("MacOS106", ":/os_macosx.png");
+ m_guestOSTypeIconNames.insert("MacOS106_64", ":/os_macosx_64.png");
+ m_guestOSTypeIconNames.insert("MacOS107_64", ":/os_macosx_64.png");
+ m_guestOSTypeIconNames.insert("MacOS108_64", ":/os_macosx_64.png");
+ m_guestOSTypeIconNames.insert("MacOS109_64", ":/os_macosx_64.png");
+ m_guestOSTypeIconNames.insert("MacOS1010_64", ":/os_macosx_64.png");
+ m_guestOSTypeIconNames.insert("MacOS1011_64", ":/os_macosx_64.png");
+ m_guestOSTypeIconNames.insert("MacOS1012_64", ":/os_macosx_64.png");
+ m_guestOSTypeIconNames.insert("MacOS1013_64", ":/os_macosx_64.png");
+ m_guestOSTypeIconNames.insert("JRockitVE", ":/os_jrockitve.png");
+ m_guestOSTypeIconNames.insert("VBoxBS_64", ":/os_other_64.png");
+ m_guestOSTypeIconNames.insert("Cloud", ":/os_cloud.png");
+
+ /* Prepare warning/error icons: */
+ m_pixWarning = defaultIcon(UIDefaultIconType_MessageBoxWarning).pixmap(16, 16);
+ Assert(!m_pixWarning.isNull());
+ m_pixError = defaultIcon(UIDefaultIconType_MessageBoxCritical).pixmap(16, 16);
+ Assert(!m_pixError.isNull());
+}
+
+UIIconPoolGeneral::~UIIconPoolGeneral()
+{
+ /* Deinit instance: */
+ s_pInstance = 0;
+}
+
+QIcon UIIconPoolGeneral::userMachineIcon(const CMachine &comMachine) const
+{
+ /* Get machine ID: */
+ const QUuid uMachineId = comMachine.GetId();
+ AssertReturn(comMachine.isOk(), QPixmap());
+
+ /* Prepare icon: */
+ QIcon icon;
+
+ /* 1. First, load icon from IMachine extra-data: */
+ if (icon.isNull())
+ {
+ foreach (const QString &strIconName, gEDataManager->machineWindowIconNames(uMachineId))
+ if (!strIconName.isEmpty() && QFile::exists(strIconName))
+ icon.addFile(strIconName);
+ }
+
+ /* 2. Otherwise, load icon from IMachine interface itself: */
+ if (icon.isNull())
+ {
+ const QVector<BYTE> byteVector = comMachine.GetIcon();
+ AssertReturn(comMachine.isOk(), QPixmap());
+ const QByteArray byteArray = QByteArray::fromRawData(reinterpret_cast<const char*>(byteVector.constData()), byteVector.size());
+ const QImage image = QImage::fromData(byteArray);
+ if (!image.isNull())
+ {
+ QPixmap pixmap = QPixmap::fromImage(image);
+ const int iMinimumLength = qMin(pixmap.width(), pixmap.height());
+ if (pixmap.width() != iMinimumLength || pixmap.height() != iMinimumLength)
+ pixmap = pixmap.scaled(QSize(iMinimumLength, iMinimumLength), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ icon.addPixmap(pixmap);
+ }
+ }
+
+ /* Return icon: */
+ return icon;
+}
+
+QPixmap UIIconPoolGeneral::userMachinePixmap(const CMachine &comMachine, const QSize &size) const
+{
+ /* Acquire icon: */
+ const QIcon icon = userMachineIcon(comMachine);
+
+ /* Prepare pixmap: */
+ QPixmap pixmap;
+
+ /* Check whether we have valid icon: */
+ if (!icon.isNull())
+ {
+ /* Get pixmap of requested size: */
+ pixmap = icon.pixmap(size);
+ /* And even scale it if size is not valid: */
+ if (pixmap.size() != size)
+ pixmap = pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ }
+
+ /* Return pixmap: */
+ return pixmap;
+}
+
+QPixmap UIIconPoolGeneral::userMachinePixmapDefault(const CMachine &comMachine, QSize *pLogicalSize /* = 0 */) const
+{
+ /* Acquire icon: */
+ const QIcon icon = userMachineIcon(comMachine);
+
+ /* Prepare pixmap: */
+ QPixmap pixmap;
+
+ /* Check whether we have valid icon: */
+ if (!icon.isNull())
+ {
+ /* Determine desired icon size: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
+ const QSize iconSize = QSize(iIconMetric, iIconMetric);
+
+ /* Pass up logical size if necessary: */
+ if (pLogicalSize)
+ *pLogicalSize = iconSize;
+
+ /* Get pixmap of requested size: */
+ pixmap = icon.pixmap(iconSize);
+ }
+
+ /* Return pixmap: */
+ return pixmap;
+}
+
+QIcon UIIconPoolGeneral::guestOSTypeIcon(const QString &strOSTypeID) const
+{
+ /* Prepare fallback icon: */
+ static QPixmap nullIcon;
+
+ /* If we do NOT have that 'guest OS type' icon cached already: */
+ if (!m_guestOSTypeIcons.contains(strOSTypeID))
+ {
+ /* Compose proper icon if we have that 'guest OS type' known: */
+ if (m_guestOSTypeIconNames.contains(strOSTypeID))
+ m_guestOSTypeIcons[strOSTypeID] = iconSet(m_guestOSTypeIconNames[strOSTypeID]);
+ /* Assign fallback icon if we do NOT have that 'guest OS type' registered: */
+ else if (!strOSTypeID.isNull())
+ m_guestOSTypeIcons[strOSTypeID] = iconSet(m_guestOSTypeIconNames["Other"]);
+ /* Assign fallback icon if we do NOT have that 'guest OS type' known: */
+ else
+ m_guestOSTypeIcons[strOSTypeID] = iconSet(nullIcon);
+ }
+
+ /* Retrieve corresponding icon: */
+ const QIcon &icon = m_guestOSTypeIcons[strOSTypeID];
+ AssertMsgReturn(!icon.isNull(),
+ ("Undefined icon for type '%s'.", strOSTypeID.toLatin1().constData()),
+ nullIcon);
+
+ /* Return icon: */
+ return icon;
+}
+
+QPixmap UIIconPoolGeneral::guestOSTypePixmap(const QString &strOSTypeID, const QSize &size) const
+{
+ /* Acquire icon: */
+ const QIcon icon = guestOSTypeIcon(strOSTypeID);
+
+ /* Prepare pixmap: */
+ QPixmap pixmap;
+
+ /* Check whether we have valid icon: */
+ if (!icon.isNull())
+ {
+ /* Get pixmap of requested size: */
+ pixmap = icon.pixmap(size);
+ /* And even scale it if size is not valid: */
+ if (pixmap.size() != size)
+ pixmap = pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ }
+
+ /* Return pixmap: */
+ return pixmap;
+}
+
+QPixmap UIIconPoolGeneral::guestOSTypePixmapDefault(const QString &strOSTypeID, QSize *pLogicalSize /* = 0 */) const
+{
+ /* Acquire icon: */
+ const QIcon icon = guestOSTypeIcon(strOSTypeID);
+
+ /* Prepare pixmap: */
+ QPixmap pixmap;
+
+ /* Check whether we have valid icon: */
+ if (!icon.isNull())
+ {
+ /* Determine desired icon size: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
+ const QSize iconSize = QSize(iIconMetric, iIconMetric);
+
+ /* Pass up logical size if necessary: */
+ if (pLogicalSize)
+ *pLogicalSize = iconSize;
+
+ /* Get pixmap of requested size (take into account the DPI of the main shown window, if possible): */
+ if (windowManager().mainWindowShown() && windowManager().mainWindowShown()->windowHandle())
+ pixmap = icon.pixmap(windowManager().mainWindowShown()->windowHandle(), iconSize);
+ else
+ pixmap = icon.pixmap(iconSize);
+ }
+
+ /* Return pixmap: */
+ return pixmap;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIIconPool.h b/src/VBox/Frontends/VirtualBox/src/globals/UIIconPool.h
new file mode 100644
index 00000000..12726e0e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIIconPool.h
@@ -0,0 +1,190 @@
+/* $Id: UIIconPool.h $ */
+/** @file
+ * VBox Qt GUI - UIIconPool class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIIconPool_h
+#define FEQT_INCLUDED_SRC_globals_UIIconPool_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QFileIconProvider>
+#include <QIcon>
+#include <QPixmap>
+#include <QHash>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class CMachine;
+
+/** Interface which provides GUI with static API
+ * allowing to dynamically compose icons at runtime. */
+class SHARED_LIBRARY_STUFF UIIconPool
+{
+public:
+
+ /** Default icon types. */
+ enum UIDefaultIconType
+ {
+ /* Message-box related stuff: */
+ UIDefaultIconType_MessageBoxInformation,
+ UIDefaultIconType_MessageBoxQuestion,
+ UIDefaultIconType_MessageBoxWarning,
+ UIDefaultIconType_MessageBoxCritical,
+ /* Dialog related stuff: */
+ UIDefaultIconType_DialogCancel,
+ UIDefaultIconType_DialogHelp,
+ UIDefaultIconType_ArrowBack,
+ UIDefaultIconType_ArrowForward
+ };
+
+ /** Creates pixmap from passed pixmap @a strName. */
+ static QPixmap pixmap(const QString &strName);
+
+ /** Creates icon from passed pixmap names for
+ * @a strNormal, @a strDisabled and @a strActive icon states. */
+ static QIcon iconSet(const QString &strNormal,
+ const QString &strDisabled = QString(),
+ const QString &strActive = QString());
+
+ /** Creates icon from passed pixmap names for
+ * @a strNormal, @a strDisabled, @a strActive icon states and
+ * their analogs for toggled-off case. Used for toggle actions. */
+ static QIcon iconSetOnOff(const QString &strNormal, const QString strNormalOff,
+ const QString &strDisabled = QString(), const QString &strDisabledOff = QString(),
+ const QString &strActive = QString(), const QString &strActiveOff = QString());
+
+ /** Creates icon from passed pixmap names for
+ * @a strNormal, @a strDisabled, @a strActive icon states and
+ * their analogs for small-icon case. Used for setting pages. */
+ static QIcon iconSetFull(const QString &strNormal, const QString &strSmall,
+ const QString &strNormalDisabled = QString(), const QString &strSmallDisabled = QString(),
+ const QString &strNormalActive = QString(), const QString &strSmallActive = QString());
+
+ /** Creates icon from passed pixmaps for
+ * @a normal, @a disabled and @a active icon states. */
+ static QIcon iconSet(const QPixmap &normal,
+ const QPixmap &disabled = QPixmap(),
+ const QPixmap &active = QPixmap());
+
+ /** Creates icon of passed @a defaultIconType
+ * based on passed @a pWidget style (if any) or application style (otherwise). */
+ static QIcon defaultIcon(UIDefaultIconType defaultIconType, const QWidget *pWidget = 0);
+
+ /** Joins two pixmaps horizontally with 2px space between them and returns the result. */
+ static QPixmap joinPixmaps(const QPixmap &pixmap1, const QPixmap &pixmap2);
+
+protected:
+
+ /** Constructs icon-pool.
+ * Doesn't mean to be used directly,
+ * cause this class is a bunch of statics. */
+ UIIconPool() {}
+
+ /** Destructs icon-pool. */
+ virtual ~UIIconPool() {}
+
+private:
+
+ /** Adds resource named @a strName to passed @a icon
+ * for @a mode (QIcon::Normal by default) and @a state (QIcon::Off by default). */
+ static void addName(QIcon &icon, const QString &strName,
+ QIcon::Mode mode = QIcon::Normal, QIcon::State state = QIcon::Off);
+};
+
+/** UIIconPool interface extension used as general GUI icon-pool.
+ * Provides GUI with guest OS types pixmap cache. */
+class SHARED_LIBRARY_STUFF UIIconPoolGeneral : public UIIconPool
+{
+public:
+
+ /** Creates singleton instance. */
+ static void create();
+ /** Destroys singleton instance. */
+ static void destroy();
+ /** Returns singleton instance. */
+ static UIIconPoolGeneral *instance();
+
+ /** Returns icon defined for a passed @a comMachine. */
+ QIcon userMachineIcon(const CMachine &comMachine) const;
+ /** Returns pixmap of a passed @a size defined for a passed @a comMachine. */
+ QPixmap userMachinePixmap(const CMachine &comMachine, const QSize &size) const;
+ /** Returns pixmap defined for a passed @a comMachine.
+ * In case if non-null @a pLogicalSize pointer provided, it will be updated properly. */
+ QPixmap userMachinePixmapDefault(const CMachine &comMachine, QSize *pLogicalSize = 0) const;
+
+ /** Returns icon corresponding to passed @a strOSTypeID. */
+ QIcon guestOSTypeIcon(const QString &strOSTypeID) const;
+ /** Returns pixmap corresponding to passed @a strOSTypeID and @a size. */
+ QPixmap guestOSTypePixmap(const QString &strOSTypeID, const QSize &size) const;
+ /** Returns pixmap corresponding to passed @a strOSTypeID.
+ * In case if non-null @a pLogicalSize pointer provided, it will be updated properly. */
+ QPixmap guestOSTypePixmapDefault(const QString &strOSTypeID, QSize *pLogicalSize = 0) const;
+
+ /** Returns default system icon of certain @a enmType. */
+ QIcon defaultSystemIcon(QFileIconProvider::IconType enmType) { return m_fileIconProvider.icon(enmType); }
+ /** Returns file icon fetched from passed file @a info. */
+ QIcon defaultFileIcon(const QFileInfo &info) { return m_fileIconProvider.icon(info); }
+
+ /** Returns cached default warning pixmap. */
+ QPixmap warningIcon() const { return m_pixWarning; }
+ /** Returns cached default error pixmap. */
+ QPixmap errorIcon() const { return m_pixError; }
+
+private:
+
+ /** Constructs general icon-pool. */
+ UIIconPoolGeneral();
+ /** Destructs general icon-pool. */
+ virtual ~UIIconPoolGeneral() /* override final */;
+
+ /** Holds the singleton instance. */
+ static UIIconPoolGeneral *s_pInstance;
+
+ /** Holds the global file icon provider instance. */
+ QFileIconProvider m_fileIconProvider;
+
+ /** Guest OS type icon-names cache. */
+ QHash<QString, QString> m_guestOSTypeIconNames;
+ /** Guest OS type icons cache. */
+ mutable QHash<QString, QIcon> m_guestOSTypeIcons;
+
+ /** Holds the warning pixmap. */
+ QPixmap m_pixWarning;
+ /** Holds the error pixmap. */
+ QPixmap m_pixError;
+
+ /** Allows for shortcut access. */
+ friend UIIconPoolGeneral &generalIconPool();
+};
+
+/** Singleton UIIconPoolGeneral 'official' name. */
+inline UIIconPoolGeneral &generalIconPool() { return *UIIconPoolGeneral::instance(); }
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIIconPool_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIImageTools.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIImageTools.cpp
new file mode 100644
index 00000000..97bba62c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIImageTools.cpp
@@ -0,0 +1,283 @@
+/* $Id: UIImageTools.cpp $ */
+/** @file
+ * VBox Qt GUI - Implementation of utility classes and functions for image manipulation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPainter>
+#include <QPainterPath>
+#include <QPainterPathStroker>
+
+/* GUI include */
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIImageTools.h"
+
+/* External includes: */
+#include <math.h>
+
+
+QImage UIImageTools::toGray(const QImage &image)
+{
+ QImage result = image.convertToFormat(QImage::Format_ARGB32);
+ for (int y = 0; y < result.height(); ++y)
+ {
+ QRgb *pScanLine = (QRgb*)result.scanLine(y);
+ for (int x = 0; x < result.width(); ++x)
+ {
+ const int g = qGray(pScanLine[x]);
+ pScanLine[x] = qRgba(g, g, g, qAlpha(pScanLine[x]));
+ }
+ }
+ return result;
+}
+
+void UIImageTools::dimImage(QImage &image)
+{
+ for (int y = 0; y < image.height(); ++y)
+ {
+ QRgb *pScanLine = (QRgb*)image.scanLine(y);
+ if (y % 2)
+ {
+ if (image.depth() == 32)
+ {
+ for (int x = 0; x < image.width(); ++x)
+ {
+ const int iGray = qGray(pScanLine[x]) / 2;
+ pScanLine[x] = qRgba(iGray, iGray, iGray, qAlpha(pScanLine[x]));
+ }
+ }
+ else
+ ::memset(pScanLine, 0, image.bytesPerLine());
+ }
+ else
+ {
+ if (image.depth() == 32)
+ {
+ for (int x = 0; x < image.width(); ++x)
+ {
+ const int iGray = (2 * qGray(pScanLine[x])) / 3;
+ pScanLine[x] = qRgba(iGray, iGray, iGray, qAlpha(pScanLine[x]));
+ }
+ }
+ }
+ }
+}
+
+void UIImageTools::blurImage(const QImage &source, QImage &destination, int iRadius)
+{
+ /* Blur in two steps, first horizontal and then vertical: */
+ QImage tmpImage(source.size(), QImage::Format_ARGB32);
+ blurImageHorizontal(source, tmpImage, iRadius);
+ blurImageVertical(tmpImage, destination, iRadius);
+}
+
+void UIImageTools::blurImageHorizontal(const QImage &source, QImage &destination, int iRadius)
+{
+ QSize s = source.size();
+ for (int y = 0; y < s.height(); ++y)
+ {
+ int rt = 0;
+ int gt = 0;
+ int bt = 0;
+ int at = 0;
+
+ /* In the horizontal case we can just use the scanline, which is
+ * much faster than accessing every pixel with the QImage::pixel
+ * method. Unfortunately this doesn't work in the vertical case. */
+ QRgb *ssl = (QRgb*)source.scanLine(y);
+ QRgb *dsl = (QRgb*)destination.scanLine(y);
+ /* First process the horizontal zero line at once: */
+ int b = iRadius + 1;
+ for (int x1 = 0; x1 <= iRadius; ++x1)
+ {
+ QRgb rgba = ssl[x1];
+ rt += qRed(rgba);
+ gt += qGreen(rgba);
+ bt += qBlue(rgba);
+ at += qAlpha(rgba);
+ }
+ /* Set the new weighted pixel: */
+ dsl[0] = qRgba(rt / b, gt / b, bt / b, at / b);
+
+ /* Now process the rest */
+ for (int x = 1; x < s.width(); ++x)
+ {
+ /* Subtract the pixel which fall out of our blur matrix: */
+ int x1 = x - iRadius - 1;
+ if (x1 >= 0)
+ {
+ /* Adjust the weight (necessary for the border case): */
+ --b;
+ QRgb rgba = ssl[x1];
+ rt -= qRed(rgba);
+ gt -= qGreen(rgba);
+ bt -= qBlue(rgba);
+ at -= qAlpha(rgba);
+ }
+
+ /* Add the pixel which get into our blur matrix: */
+ int x2 = x + iRadius;
+ if (x2 < s.width())
+ {
+ /* Adjust the weight (necessary for the border case): */
+ ++b;
+ QRgb rgba = ssl[x2];
+ rt += qRed(rgba);
+ gt += qGreen(rgba);
+ bt += qBlue(rgba);
+ at += qAlpha(rgba);
+ }
+ /* Set the new weighted pixel: */
+ dsl[x] = qRgba(rt / b, gt / b, bt / b, at / b);
+ }
+ }
+}
+
+void UIImageTools::blurImageVertical(const QImage &source, QImage &destination, int iRadius)
+{
+ QSize s = source.size();
+ destination = QImage(s, source.format());
+ for (int x = 0; x < s.width(); ++x)
+ {
+ int rt = 0;
+ int gt = 0;
+ int bt = 0;
+ int at = 0;
+
+ /* First process the vertical zero line at once: */
+ int b = iRadius + 1;
+ for (int y1 = 0; y1 <= iRadius; ++y1)
+ {
+ QRgb rgba = source.pixel(x, y1);
+ rt += qRed(rgba);
+ gt += qGreen(rgba);
+ bt += qBlue(rgba);
+ at += qAlpha(rgba);
+ }
+ /* Set the new weighted pixel: */
+ destination.setPixel(x, 0, qRgba(rt / b, gt / b, bt / b, at / b));
+
+ /* Now process the rest: */
+ for (int y = 1; y < s.height(); ++y)
+ {
+ /* Subtract the pixel which fall out of our blur matrix: */
+ int y1 = y - iRadius - 1;
+ if (y1 >= 0)
+ {
+ --b; /* Adjust the weight (necessary for the border case): */
+ QRgb rgba = source.pixel(x, y1);
+ rt -= qRed(rgba);
+ gt -= qGreen(rgba);
+ bt -= qBlue(rgba);
+ at -= qAlpha(rgba);
+ }
+
+ /* Add the pixel which get into our blur matrix: */
+ int y2 = y + iRadius;
+ if (y2 < s.height())
+ {
+ ++b; /* Adjust the weight (necessary for the border case): */
+ QRgb rgba = source.pixel(x, y2);
+ rt += qRed(rgba);
+ gt += qGreen(rgba);
+ bt += qBlue(rgba);
+ at += qAlpha(rgba);
+ }
+ /* Set the new weighted pixel: */
+ destination.setPixel(x, y, qRgba(rt / b, gt / b, bt / b, at / b));
+ }
+ }
+}
+
+static QImage betaLabelImage(QSize size, QWidget *pHint)
+{
+ /* Calculate device pixel ratio: */
+ const double dDpr = pHint ? UIDesktopWidgetWatchdog::devicePixelRatio(pHint) : UIDesktopWidgetWatchdog::devicePixelRatio(-1);
+ if (dDpr > 1.0)
+ size *= dDpr;
+
+ /* Beta label: */
+ QColor bgc(246, 179, 0);
+ QImage i(size, QImage::Format_ARGB32);
+ i.fill(Qt::transparent);
+ QPainter p(&i);
+ p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
+ p.setPen(Qt::NoPen);
+
+ /* Background: */
+ p.setBrush(bgc);
+ p.drawRect(0, 0, size.width(), size.height());
+
+ /* The black stripes: */
+ p.setPen(QPen(QColor(70, 70, 70), 5));
+ float c = ((float)size.width() / size.height()) + 1;
+ float g = (size.width() / (c - 1));
+ for (int i = 0; i < c; ++i)
+ p.drawLine((int)(-g / 2 + g * i), size.height(), (int)(-g / 2 + g * (i + 1)), 0);
+
+ /* The text: */
+ QFont f = p.font();
+ if (dDpr > 1.0)
+ f.setPointSize(f.pointSize() * dDpr);
+ f.setBold(true);
+ QPainterPath tp;
+ tp.addText(0, 0, f, "BETA");
+ QRectF r = tp.boundingRect();
+
+ /* Center the text path: */
+ p.translate((size.width() - r.width()) / 2, size.height() - (size.height() - r.height()) / 2);
+ QPainterPathStroker pps;
+ QPainterPath pp = pps.createStroke(tp);
+ p.setPen(QPen(bgc.darker(80), 2, Qt::SolidLine, Qt::RoundCap));
+ p.drawPath(pp);
+ p.setBrush(Qt::black);
+ p.setPen(Qt::NoPen);
+ p.drawPath(tp);
+ p.end();
+
+ /* Smoothing: */
+ QImage i1(size, QImage::Format_ARGB32);
+ i1.fill(Qt::transparent);
+ QPainter p1(&i1);
+ p1.setCompositionMode(QPainter::CompositionMode_Source);
+ p1.drawImage(0, 0, i);
+ p1.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+ QLinearGradient lg(0, 0, size.width(), 0);
+ lg.setColorAt(0, QColor(Qt::transparent));
+ lg.setColorAt(0.20, QColor(Qt::white));
+ lg.setColorAt(0.80, QColor(Qt::white));
+ lg.setColorAt(1, QColor(Qt::transparent));
+ p1.fillRect(0, 0, size.width(), size.height(), lg);
+ p1.end();
+ if (dDpr > 1.0)
+ i1.setDevicePixelRatio(dDpr);
+
+ return i1;
+}
+
+QPixmap UIImageTools::betaLabel(const QSize &size /* = QSize(80, 16) */, QWidget *pHint /* = 0 */)
+{
+ return QPixmap::fromImage(betaLabelImage(size, pHint));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIImageTools.h b/src/VBox/Frontends/VirtualBox/src/globals/UIImageTools.h
new file mode 100644
index 00000000..cd1a2a2c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIImageTools.h
@@ -0,0 +1,62 @@
+/* $Id: UIImageTools.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for image manipulation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIImageTools_h
+#define FEQT_INCLUDED_SRC_globals_UIImageTools_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QImage>
+#include <QPixmap>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** Image operation namespace. */
+namespace UIImageTools
+{
+ /** Converts @a image to gray-scale. */
+ SHARED_LIBRARY_STUFF QImage toGray(const QImage &image);
+
+ /** Makes @a image more dark and dim. */
+ SHARED_LIBRARY_STUFF void dimImage(QImage &image);
+
+ /** Blurs passed @a source image to @a destination cropping by certain @a iRadius. */
+ SHARED_LIBRARY_STUFF void blurImage(const QImage &source, QImage &destination, int iRadius);
+ /** Blurs passed @a source image horizontally to @a destination cropping by certain @a iRadius. */
+ SHARED_LIBRARY_STUFF void blurImageHorizontal(const QImage &source, QImage &destination, int iRadius);
+ /** Blurs passed @a source image vertically to @a destination cropping by certain @a iRadius. */
+ SHARED_LIBRARY_STUFF void blurImageVertical(const QImage &source, QImage &destination, int iRadius);
+
+ /** Applies BET-label of passed @a size. */
+ SHARED_LIBRARY_STUFF QPixmap betaLabel(const QSize &size = QSize(80, 16), QWidget *pHint = 0);
+}
+using namespace UIImageTools /* if header included */;
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIImageTools_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UILibraryDefs.h b/src/VBox/Frontends/VirtualBox/src/globals/UILibraryDefs.h
new file mode 100644
index 00000000..ae60e9d9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UILibraryDefs.h
@@ -0,0 +1,44 @@
+/* $Id: UILibraryDefs.h $ */
+/** @file
+ * VBox Qt GUI - Global library definitions.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UILibraryDefs_h
+#define FEQT_INCLUDED_SRC_globals_UILibraryDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+/* Define shared library stuff: */
+#ifdef VBOX_GUI_LIBRARY
+# define SHARED_LIBRARY_STUFF DECLEXPORT_CLASS
+#else
+# define SHARED_LIBRARY_STUFF DECLIMPORT_CLASS
+#endif
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UILibraryDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIMachineAttributeSetter.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIMachineAttributeSetter.cpp
new file mode 100644
index 00000000..bacb278f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIMachineAttributeSetter.cpp
@@ -0,0 +1,327 @@
+/* $Id: UIMachineAttributeSetter.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineAttributeSetter namespace implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVariant>
+
+/* GUI includes: */
+#include "UIBootOrderEditor.h"
+#include "UICommon.h"
+#include "UIMachineAttributeSetter.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CAudioSettings.h"
+#include "CGraphicsAdapter.h"
+#include "CNetworkAdapter.h"
+#include "CUSBController.h"
+
+
+void removeUSBControllers(CMachine &comMachine, const UIUSBControllerTypeSet &controllerSet = UIUSBControllerTypeSet())
+{
+ /* Get controllers for further activities: */
+ const CUSBControllerVector &controllers = comMachine.GetUSBControllers();
+ if (!comMachine.isOk())
+ return;
+
+ /* For each controller: */
+ foreach (const CUSBController &comController, controllers)
+ {
+ /* Get controller type&name for further activities: */
+ const KUSBControllerType enmType = comController.GetType();
+ const QString strName = comController.GetName();
+
+ /* Pass only if requested types were not defined or contains the one we found: */
+ if (!controllerSet.isEmpty() && !controllerSet.contains(enmType))
+ continue;
+
+ /* Remove controller: */
+ comMachine.RemoveUSBController(strName);
+ if (!comMachine.isOk())
+ break;
+ }
+}
+
+void createUSBControllers(CMachine &comMachine, const UIUSBControllerTypeSet &controllerSet)
+{
+ /* For each requested USB controller type: */
+ foreach (const KUSBControllerType &enmType, controllerSet)
+ {
+ switch (enmType)
+ {
+ case KUSBControllerType_OHCI: comMachine.AddUSBController("OHCI", KUSBControllerType_OHCI); break;
+ case KUSBControllerType_EHCI: comMachine.AddUSBController("EHCI", KUSBControllerType_EHCI); break;
+ case KUSBControllerType_XHCI: comMachine.AddUSBController("xHCI", KUSBControllerType_XHCI); break;
+ default: break;
+ }
+ }
+}
+
+void UIMachineAttributeSetter::setMachineAttribute(const CMachine &comConstMachine,
+ const MachineAttribute &enmType,
+ const QVariant &guiAttribute)
+{
+ /* Get editable machine & session: */
+ CMachine comMachine = comConstMachine;
+ CSession comSession = uiCommon().tryToOpenSessionFor(comMachine);
+
+ /* Main API block: */
+ do
+ {
+ /* Save machine settings? */
+ bool fSaveSettings = true;
+ /* Error happened? */
+ bool fErrorHappened = false;
+
+ /* Assign attribute depending on passed type: */
+ switch (enmType)
+ {
+ case MachineAttribute_Name:
+ {
+ /* Change machine name: */
+ comMachine.SetName(guiAttribute.toString());
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotChangeMachineParameter(comMachine);
+ fErrorHappened = true;
+ }
+ break;
+ }
+ case MachineAttribute_OSType:
+ {
+ /* Change machine OS type: */
+ comMachine.SetOSTypeId(guiAttribute.toString());
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotChangeMachineParameter(comMachine);
+ fErrorHappened = true;
+ }
+ break;
+ }
+ case MachineAttribute_BaseMemory:
+ {
+ /* Change machine base memory (RAM): */
+ comMachine.SetMemorySize(guiAttribute.toInt());
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotChangeMachineParameter(comMachine);
+ fErrorHappened = true;
+ }
+ break;
+ }
+ case MachineAttribute_BootOrder:
+ {
+ /* Change machine boot order: */
+ saveBootItems(guiAttribute.value<UIBootItemDataList>(), comMachine);
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotChangeMachineParameter(comMachine);
+ fErrorHappened = true;
+ }
+ break;
+ }
+ case MachineAttribute_VideoMemory:
+ {
+ /* Acquire graphics adapter: */
+ CGraphicsAdapter comGraphics = comMachine.GetGraphicsAdapter();
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotAcquireMachineParameter(comMachine);
+ fErrorHappened = true;
+ break;
+ }
+ /* Change machine video memory (VRAM): */
+ comGraphics.SetVRAMSize(guiAttribute.toInt());
+ if (!comGraphics.isOk())
+ {
+ UINotificationMessage::cannotChangeGraphicsAdapterParameter(comGraphics);
+ fErrorHappened = true;
+ }
+ break;
+ }
+ case MachineAttribute_GraphicsControllerType:
+ {
+ /* Acquire graphics adapter: */
+ CGraphicsAdapter comGraphics = comMachine.GetGraphicsAdapter();
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotAcquireMachineParameter(comMachine);
+ fErrorHappened = true;
+ break;
+ }
+ /* Change machine graphics controller type: */
+ comGraphics.SetGraphicsControllerType(guiAttribute.value<KGraphicsControllerType>());
+ if (!comGraphics.isOk())
+ {
+ UINotificationMessage::cannotChangeGraphicsAdapterParameter(comGraphics);
+ fErrorHappened = true;
+ }
+ break;
+ }
+ case MachineAttribute_AudioHostDriverType:
+ {
+ /* Acquire audio adapter: */
+ CAudioSettings const comAudioSettings = comMachine.GetAudioSettings();
+ CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ if (!comAudioSettings.isOk())
+ {
+ UINotificationMessage::cannotAcquireMachineParameter(comMachine);
+ fErrorHappened = true;
+ break;
+ }
+ /* Change audio host driver type: */
+ comAdapter.SetAudioDriver(guiAttribute.value<KAudioDriverType>());
+ if (!comAdapter.isOk())
+ {
+ UINotificationMessage::cannotChangeAudioAdapterParameter(comAdapter);
+ fErrorHappened = true;
+ }
+ break;
+ }
+ case MachineAttribute_AudioControllerType:
+ {
+ /* Acquire audio adapter: */
+ CAudioSettings const comAudioSettings = comMachine.GetAudioSettings();
+ CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ if (!comAudioSettings.isOk())
+ {
+ UINotificationMessage::cannotAcquireMachineParameter(comMachine);
+ fErrorHappened = true;
+ break;
+ }
+ /* Change audio controller type: */
+ comAdapter.SetAudioController(guiAttribute.value<KAudioControllerType>());
+ if (!comAdapter.isOk())
+ {
+ UINotificationMessage::cannotChangeAudioAdapterParameter(comAdapter);
+ fErrorHappened = true;
+ }
+ break;
+ }
+ case MachineAttribute_NetworkAttachmentType:
+ {
+ /* Acquire value itself: */
+ const UINetworkAdapterDescriptor nad = guiAttribute.value<UINetworkAdapterDescriptor>();
+ /* Acquire network adapter: */
+ CNetworkAdapter comAdapter = comMachine.GetNetworkAdapter(nad.m_iSlot);
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotAcquireMachineParameter(comMachine);
+ fErrorHappened = true;
+ break;
+ }
+ /* Change network adapter attachment type: */
+ comAdapter.SetAttachmentType(nad.m_enmType);
+ if (!comAdapter.isOk())
+ {
+ UINotificationMessage::cannotChangeNetworkAdapterParameter(comAdapter);
+ fErrorHappened = true;
+ break;
+ }
+ /* Change network adapter name: */
+ switch (nad.m_enmType)
+ {
+ case KNetworkAttachmentType_Bridged: comAdapter.SetBridgedInterface(nad.m_strName); break;
+ case KNetworkAttachmentType_Internal: comAdapter.SetInternalNetwork(nad.m_strName); break;
+ case KNetworkAttachmentType_HostOnly: comAdapter.SetHostOnlyInterface(nad.m_strName); break;
+ case KNetworkAttachmentType_Generic: comAdapter.SetGenericDriver(nad.m_strName); break;
+ case KNetworkAttachmentType_NATNetwork: comAdapter.SetNATNetwork(nad.m_strName); break;
+#ifdef VBOX_WITH_CLOUD_NET
+ case KNetworkAttachmentType_Cloud: comAdapter.SetCloudNetwork(nad.m_strName); break;
+#endif
+#ifdef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnlyNetwork: comAdapter.SetHostOnlyNetwork(nad.m_strName); break;
+#endif
+ default: break;
+ }
+ if (!comAdapter.isOk())
+ {
+ UINotificationMessage::cannotChangeNetworkAdapterParameter(comAdapter);
+ fErrorHappened = true;
+ }
+ break;
+ }
+ case MachineAttribute_USBControllerType:
+ {
+ /* Remove all existing controller first of all: */
+ removeUSBControllers(comMachine);
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotChangeMachineParameter(comMachine);
+ fErrorHappened = true;
+ break;
+ }
+ /* Add new controllers afterwards: */
+ const UIUSBControllerTypeSet controllerSet = guiAttribute.value<UIUSBControllerTypeSet>();
+ if (!controllerSet.contains(KUSBControllerType_Null))
+ {
+ createUSBControllers(comMachine, controllerSet);
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotChangeMachineParameter(comMachine);
+ fErrorHappened = true;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Error happened? */
+ if (fErrorHappened)
+ break;
+ /* Save machine settings? */
+ if (!fSaveSettings)
+ break;
+
+ /* Save machine settings: */
+ comMachine.SaveSettings();
+ if (!comMachine.isOk())
+ {
+ msgCenter().cannotSaveMachineSettings(comMachine);
+ break;
+ }
+ }
+ while (0);
+
+ /* Close session to editable comMachine if necessary: */
+ if (!comSession.isNull())
+ comSession.UnlockMachine();
+}
+
+void UIMachineAttributeSetter::setMachineLocation(const QUuid &uMachineId,
+ const QString &strLocation)
+{
+ /* Move machine: */
+ UINotificationProgressMachineMove *pNotification = new UINotificationProgressMachineMove(uMachineId,
+ strLocation,
+ "basic");
+ gpNotificationCenter->append(pNotification);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIMachineAttributeSetter.h b/src/VBox/Frontends/VirtualBox/src/globals/UIMachineAttributeSetter.h
new file mode 100644
index 00000000..a733a7a5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIMachineAttributeSetter.h
@@ -0,0 +1,91 @@
+/* $Id: UIMachineAttributeSetter.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineAttributeSetter namespace declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIMachineAttributeSetter_h
+#define FEQT_INCLUDED_SRC_globals_UIMachineAttributeSetter_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+
+/** Known machine attributes. */
+enum MachineAttribute
+{
+ MachineAttribute_Invalid,
+ MachineAttribute_Name,
+ MachineAttribute_OSType,
+ MachineAttribute_BaseMemory,
+ MachineAttribute_BootOrder,
+ MachineAttribute_VideoMemory,
+ MachineAttribute_GraphicsControllerType,
+ MachineAttribute_AudioHostDriverType,
+ MachineAttribute_AudioControllerType,
+ MachineAttribute_NetworkAttachmentType,
+ MachineAttribute_USBControllerType,
+};
+
+/** Contains short network adapter description. */
+struct UINetworkAdapterDescriptor
+{
+ /** Composes network adapter descriptor for certain @a iSlot, @a enmType and @a strName. */
+ UINetworkAdapterDescriptor(int iSlot = -1,
+ KNetworkAttachmentType enmType = KNetworkAttachmentType_Null,
+ const QString &strName = QString())
+ : m_iSlot(iSlot), m_enmType(enmType), m_strName(strName)
+ {}
+
+ /** Holds the slot of described network adapter. */
+ int m_iSlot;
+ /** Holds the attachment type of described network adapter. */
+ KNetworkAttachmentType m_enmType;
+ /** Holds the adapter name of described network adapter. */
+ QString m_strName;
+};
+Q_DECLARE_METATYPE(UINetworkAdapterDescriptor);
+
+/** A set of USB controller types. */
+typedef QSet<KUSBControllerType> UIUSBControllerTypeSet;
+Q_DECLARE_METATYPE(UIUSBControllerTypeSet);
+
+/** Namespace used to assign CMachine attributes on more convenient basis. */
+namespace UIMachineAttributeSetter
+{
+ /** Assigns @a comMachine @a guiAttribute of specified @a enmType. */
+ SHARED_LIBRARY_STUFF void setMachineAttribute(const CMachine &comMachine,
+ const MachineAttribute &enmType,
+ const QVariant &guiAttribute);
+
+ /** Assigns @a comMachine @a strLocation. */
+ SHARED_LIBRARY_STUFF void setMachineLocation(const QUuid &uMachineId,
+ const QString &strLocation);
+}
+using namespace UIMachineAttributeSetter /* if header included */;
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIMachineAttributeSetter_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIMainEventListener.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIMainEventListener.cpp
new file mode 100644
index 00000000..7a991b84
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIMainEventListener.cpp
@@ -0,0 +1,647 @@
+/* $Id: UIMainEventListener.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMainEventListener class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QMutex>
+#include <QThread>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIMainEventListener.h"
+#include "UIMousePointerShapeData.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCanShowWindowEvent.h"
+#include "CClipboardModeChangedEvent.h"
+#include "CCloudProfileChangedEvent.h"
+#include "CCloudProfileRegisteredEvent.h"
+#include "CCloudProviderListChangedEvent.h"
+#include "CCloudProviderUninstallEvent.h"
+#include "CCursorPositionChangedEvent.h"
+#include "CDnDModeChangedEvent.h"
+#include "CEvent.h"
+#include "CEventSource.h"
+#include "CEventListener.h"
+#include "CExtraDataCanChangeEvent.h"
+#include "CExtraDataChangedEvent.h"
+#include "CGuestMonitorChangedEvent.h"
+#include "CGuestProcessIOEvent.h"
+#include "CGuestProcessRegisteredEvent.h"
+#include "CGuestProcessStateChangedEvent.h"
+#include "CGuestSessionRegisteredEvent.h"
+#include "CGuestSessionStateChangedEvent.h"
+#include "CKeyboardLedsChangedEvent.h"
+#include "CMachineDataChangedEvent.h"
+#include "CMachineStateChangedEvent.h"
+#include "CMachineRegisteredEvent.h"
+#include "CMachineGroupsChangedEvent.h"
+#include "CMediumChangedEvent.h"
+#include "CMediumConfigChangedEvent.h"
+#include "CMediumRegisteredEvent.h"
+#include "CMouseCapabilityChangedEvent.h"
+#include "CMousePointerShapeChangedEvent.h"
+#include "CNetworkAdapterChangedEvent.h"
+#include "CProgressPercentageChangedEvent.h"
+#include "CProgressTaskCompletedEvent.h"
+#include "CRuntimeErrorEvent.h"
+#include "CSessionStateChangedEvent.h"
+#include "CShowWindowEvent.h"
+#include "CSnapshotChangedEvent.h"
+#include "CSnapshotDeletedEvent.h"
+#include "CSnapshotRestoredEvent.h"
+#include "CSnapshotTakenEvent.h"
+#include "CStateChangedEvent.h"
+#include "CStorageControllerChangedEvent.h"
+#include "CStorageDeviceChangedEvent.h"
+#include "CUSBDevice.h"
+#include "CUSBDeviceStateChangedEvent.h"
+#include "CVBoxSVCAvailabilityChangedEvent.h"
+#include "CVirtualBoxErrorInfo.h"
+
+
+/** Private QThread extension allowing to listen for Main events in separate thread.
+ * This thread listens for a Main events infinitely unless creator calls for #setShutdown. */
+class UIMainEventListeningThread : public QThread
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Main events listener thread redirecting events from @a comSource to @a comListener.
+ * @param comSource Brings event source we are creating this thread for.
+ * @param comListener Brings event listener we are creating this thread for.
+ * @param escapeEventTypes Brings a set of escape event types which commands this thread to finish. */
+ UIMainEventListeningThread(const CEventSource &comSource,
+ const CEventListener &comListener,
+ const QSet<KVBoxEventType> &escapeEventTypes);
+ /** Destructs Main events listener thread. */
+ virtual ~UIMainEventListeningThread() RT_OVERRIDE;
+
+protected:
+
+ /** Contains the thread excution body. */
+ virtual void run() RT_OVERRIDE;
+
+ /** Returns whether the thread asked to shutdown prematurely. */
+ bool isShutdown() const;
+ /** Defines whether the thread asked to @a fShutdown prematurely. */
+ void setShutdown(bool fShutdown);
+
+private:
+
+ /** Holds the Main event source reference. */
+ CEventSource m_comSource;
+ /** Holds the Main event listener reference. */
+ CEventListener m_comListener;
+ /** Holds a set of event types this thread should finish job on. */
+ QSet<KVBoxEventType> m_escapeEventTypes;
+
+ /** Holds the mutex instance which protects thread access. */
+ mutable QMutex m_mutex;
+ /** Holds whether the thread asked to shutdown prematurely. */
+ bool m_fShutdown;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMainEventListeningThread implementation. *
+*********************************************************************************************************************************/
+
+UIMainEventListeningThread::UIMainEventListeningThread(const CEventSource &comSource,
+ const CEventListener &comListener,
+ const QSet<KVBoxEventType> &escapeEventTypes)
+ : m_comSource(comSource)
+ , m_comListener(comListener)
+ , m_escapeEventTypes(escapeEventTypes)
+ , m_fShutdown(false)
+{
+ setObjectName("UIMainEventListeningThread");
+}
+
+UIMainEventListeningThread::~UIMainEventListeningThread()
+{
+ /* Make a request to shutdown: */
+ setShutdown(true);
+
+ /* And wait 30 seconds for run() to finish (1 sec increments to help with
+ delays incurred debugging and prevent suicidal use-after-free behaviour): */
+ uint32_t i = 30000;
+ do
+ wait(1000);
+ while (i-- > 0 && !isFinished());
+}
+
+void UIMainEventListeningThread::run()
+{
+ /* Initialize COM: */
+ COMBase::InitializeCOM(false);
+
+ /* Copy source wrapper to this thread: */
+ CEventSource comSource = m_comSource;
+ /* Copy listener wrapper to this thread: */
+ CEventListener comListener = m_comListener;
+
+ /* While we are not in shutdown: */
+ while (!isShutdown())
+ {
+ /* Fetch the event from the queue: */
+ CEvent comEvent = comSource.GetEvent(comListener, 500);
+ if (!comEvent.isNull())
+ {
+ /* Process the event and tell the listener: */
+ comListener.HandleEvent(comEvent);
+ if (comEvent.GetWaitable())
+ {
+ comSource.EventProcessed(comListener, comEvent);
+ LogRel2(("GUI: UIMainEventListener/ThreadRun: EventProcessed set for waitable event\n"));
+ }
+
+ /* Check whether we should finish our job on this event: */
+ if (m_escapeEventTypes.contains(comEvent.GetType()))
+ setShutdown(true);
+ }
+ }
+
+ /* Cleanup COM: */
+ COMBase::CleanupCOM();
+}
+
+bool UIMainEventListeningThread::isShutdown() const
+{
+ m_mutex.lock();
+ bool fShutdown = m_fShutdown;
+ m_mutex.unlock();
+ return fShutdown;
+}
+
+void UIMainEventListeningThread::setShutdown(bool fShutdown)
+{
+ m_mutex.lock();
+ m_fShutdown = fShutdown;
+ m_mutex.unlock();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMainEventListener implementation. *
+*********************************************************************************************************************************/
+
+UIMainEventListener::UIMainEventListener()
+{
+ /* Register meta-types for required enums. */
+ qRegisterMetaType<KDeviceType>("KDeviceType");
+ qRegisterMetaType<KMachineState>("KMachineState");
+ qRegisterMetaType<KSessionState>("KSessionState");
+ qRegisterMetaType< QVector<uint8_t> >("QVector<uint8_t>");
+ qRegisterMetaType<CNetworkAdapter>("CNetworkAdapter");
+ qRegisterMetaType<CMedium>("CMedium");
+ qRegisterMetaType<CMediumAttachment>("CMediumAttachment");
+ qRegisterMetaType<CUSBDevice>("CUSBDevice");
+ qRegisterMetaType<CVirtualBoxErrorInfo>("CVirtualBoxErrorInfo");
+ qRegisterMetaType<KGuestMonitorChangedEventType>("KGuestMonitorChangedEventType");
+ qRegisterMetaType<CGuestSession>("CGuestSession");
+}
+
+void UIMainEventListener::registerSource(const CEventSource &comSource,
+ const CEventListener &comListener,
+ const QSet<KVBoxEventType> &escapeEventTypes /* = QSet<KVBoxEventType>() */)
+{
+ /* Make sure source and listener are valid: */
+ AssertReturnVoid(!comSource.isNull());
+ AssertReturnVoid(!comListener.isNull());
+
+ /* Create thread for passed source: */
+ UIMainEventListeningThread *pThread = new UIMainEventListeningThread(comSource, comListener, escapeEventTypes);
+ if (pThread)
+ {
+ /* Listen for thread finished signal: */
+ connect(pThread, &UIMainEventListeningThread::finished,
+ this, &UIMainEventListener::sltHandleThreadFinished);
+ /* Register & start it: */
+ m_threads << pThread;
+ pThread->start();
+ }
+}
+
+void UIMainEventListener::unregisterSources()
+{
+ /* Stop listening for thread finished thread signals,
+ * we are about to destroy these threads anyway: */
+ foreach (UIMainEventListeningThread *pThread, m_threads)
+ disconnect(pThread, &UIMainEventListeningThread::finished,
+ this, &UIMainEventListener::sltHandleThreadFinished);
+
+ /* Wipe out the threads: */
+ /** @todo r=bird: The use of qDeleteAll here is unsafe because it won't take
+ * QThread::wait() timeouts into account, and may delete the QThread object
+ * while the thread is still running, causing heap corruption/crashes once
+ * the thread awakens and gets on with its termination.
+ * Observed with debugger + paged heap.
+ *
+ * Should use specialized thread list which only deletes the threads after
+ * isFinished() returns true, leaving them alone on timeout failures. */
+ qDeleteAll(m_threads);
+}
+
+STDMETHODIMP UIMainEventListener::HandleEvent(VBoxEventType_T, IEvent *pEvent)
+{
+ /* Try to acquire COM cleanup protection token first: */
+ if (!uiCommon().comTokenTryLockForRead())
+ return S_OK;
+
+ CEvent comEvent(pEvent);
+ //printf("Event received: %d\n", comEvent.GetType());
+ switch (comEvent.GetType())
+ {
+ case KVBoxEventType_OnVBoxSVCAvailabilityChanged:
+ {
+ CVBoxSVCAvailabilityChangedEvent comEventSpecific(pEvent);
+ emit sigVBoxSVCAvailabilityChange(comEventSpecific.GetAvailable());
+ break;
+ }
+
+ case KVBoxEventType_OnMachineStateChanged:
+ {
+ CMachineStateChangedEvent comEventSpecific(pEvent);
+ emit sigMachineStateChange(comEventSpecific.GetMachineId(), comEventSpecific.GetState());
+ break;
+ }
+ case KVBoxEventType_OnMachineDataChanged:
+ {
+ CMachineDataChangedEvent comEventSpecific(pEvent);
+ emit sigMachineDataChange(comEventSpecific.GetMachineId());
+ break;
+ }
+ case KVBoxEventType_OnMachineRegistered:
+ {
+ CMachineRegisteredEvent comEventSpecific(pEvent);
+ emit sigMachineRegistered(comEventSpecific.GetMachineId(), comEventSpecific.GetRegistered());
+ break;
+ }
+ case KVBoxEventType_OnMachineGroupsChanged:
+ {
+ CMachineGroupsChangedEvent comEventSpecific(pEvent);
+ emit sigMachineGroupsChange(comEventSpecific.GetMachineId());
+ break;
+ }
+ case KVBoxEventType_OnSessionStateChanged:
+ {
+ CSessionStateChangedEvent comEventSpecific(pEvent);
+ emit sigSessionStateChange(comEventSpecific.GetMachineId(), comEventSpecific.GetState());
+ break;
+ }
+ case KVBoxEventType_OnSnapshotTaken:
+ {
+ CSnapshotTakenEvent comEventSpecific(pEvent);
+ emit sigSnapshotTake(comEventSpecific.GetMachineId(), comEventSpecific.GetSnapshotId());
+ break;
+ }
+ case KVBoxEventType_OnSnapshotDeleted:
+ {
+ CSnapshotDeletedEvent comEventSpecific(pEvent);
+ emit sigSnapshotDelete(comEventSpecific.GetMachineId(), comEventSpecific.GetSnapshotId());
+ break;
+ }
+ case KVBoxEventType_OnSnapshotChanged:
+ {
+ CSnapshotChangedEvent comEventSpecific(pEvent);
+ emit sigSnapshotChange(comEventSpecific.GetMachineId(), comEventSpecific.GetSnapshotId());
+ break;
+ }
+ case KVBoxEventType_OnSnapshotRestored:
+ {
+ CSnapshotRestoredEvent comEventSpecific(pEvent);
+ emit sigSnapshotRestore(comEventSpecific.GetMachineId(), comEventSpecific.GetSnapshotId());
+ break;
+ }
+ case KVBoxEventType_OnCloudProviderListChanged:
+ {
+ emit sigCloudProviderListChanged();
+ break;
+ }
+ case KVBoxEventType_OnCloudProviderUninstall:
+ {
+ LogRel(("GUI: UIMainEventListener/HandleEvent: KVBoxEventType_OnCloudProviderUninstall event came\n"));
+ CCloudProviderUninstallEvent comEventSpecific(pEvent);
+ emit sigCloudProviderUninstall(comEventSpecific.GetId());
+ LogRel(("GUI: UIMainEventListener/HandleEvent: KVBoxEventType_OnCloudProviderUninstall event done\n"));
+ break;
+ }
+ case KVBoxEventType_OnCloudProfileRegistered:
+ {
+ CCloudProfileRegisteredEvent comEventSpecific(pEvent);
+ emit sigCloudProfileRegistered(comEventSpecific.GetProviderId(), comEventSpecific.GetName(), comEventSpecific.GetRegistered());
+ break;
+ }
+ case KVBoxEventType_OnCloudProfileChanged:
+ {
+ CCloudProfileChangedEvent comEventSpecific(pEvent);
+ emit sigCloudProfileChanged(comEventSpecific.GetProviderId(), comEventSpecific.GetName());
+ break;
+ }
+
+ case KVBoxEventType_OnExtraDataCanChange:
+ {
+ CExtraDataCanChangeEvent comEventSpecific(pEvent);
+ /* Has to be done in place to give an answer: */
+ bool fVeto = false;
+ QString strReason;
+ emit sigExtraDataCanChange(comEventSpecific.GetMachineId(), comEventSpecific.GetKey(),
+ comEventSpecific.GetValue(), fVeto, strReason);
+ if (fVeto)
+ comEventSpecific.AddVeto(strReason);
+ break;
+ }
+ case KVBoxEventType_OnExtraDataChanged:
+ {
+ CExtraDataChangedEvent comEventSpecific(pEvent);
+ emit sigExtraDataChange(comEventSpecific.GetMachineId(), comEventSpecific.GetKey(), comEventSpecific.GetValue());
+ break;
+ }
+
+ case KVBoxEventType_OnStorageControllerChanged:
+ {
+ CStorageControllerChangedEvent comEventSpecific(pEvent);
+ emit sigStorageControllerChange(comEventSpecific.GetMachinId(),
+ comEventSpecific.GetControllerName());
+ break;
+ }
+ case KVBoxEventType_OnStorageDeviceChanged:
+ {
+ CStorageDeviceChangedEvent comEventSpecific(pEvent);
+ emit sigStorageDeviceChange(comEventSpecific.GetStorageDevice(),
+ comEventSpecific.GetRemoved(),
+ comEventSpecific.GetSilent());
+ break;
+ }
+ case KVBoxEventType_OnMediumChanged:
+ {
+ CMediumChangedEvent comEventSpecific(pEvent);
+ emit sigMediumChange(comEventSpecific.GetMediumAttachment());
+ break;
+ }
+ case KVBoxEventType_OnMediumConfigChanged:
+ {
+ CMediumConfigChangedEvent comEventSpecific(pEvent);
+ emit sigMediumConfigChange(comEventSpecific.GetMedium());
+ break;
+ }
+ case KVBoxEventType_OnMediumRegistered:
+ {
+ CMediumRegisteredEvent comEventSpecific(pEvent);
+ emit sigMediumRegistered(comEventSpecific.GetMediumId(),
+ comEventSpecific.GetMediumType(),
+ comEventSpecific.GetRegistered());
+ break;
+ }
+
+ case KVBoxEventType_OnMousePointerShapeChanged:
+ {
+ CMousePointerShapeChangedEvent comEventSpecific(pEvent);
+ UIMousePointerShapeData shapeData(comEventSpecific.GetVisible(),
+ comEventSpecific.GetAlpha(),
+ QPoint(comEventSpecific.GetXhot(), comEventSpecific.GetYhot()),
+ QSize(comEventSpecific.GetWidth(), comEventSpecific.GetHeight()),
+ comEventSpecific.GetShape());
+ emit sigMousePointerShapeChange(shapeData);
+ break;
+ }
+ case KVBoxEventType_OnMouseCapabilityChanged:
+ {
+ CMouseCapabilityChangedEvent comEventSpecific(pEvent);
+ emit sigMouseCapabilityChange(comEventSpecific.GetSupportsAbsolute(), comEventSpecific.GetSupportsRelative(),
+ comEventSpecific.GetSupportsTouchScreen(), comEventSpecific.GetSupportsTouchPad(),
+ comEventSpecific.GetNeedsHostCursor());
+ break;
+ }
+ case KVBoxEventType_OnCursorPositionChanged:
+ {
+ CCursorPositionChangedEvent comEventSpecific(pEvent);
+ emit sigCursorPositionChange(comEventSpecific.GetHasData(),
+ (unsigned long)comEventSpecific.GetX(), (unsigned long)comEventSpecific.GetY());
+ break;
+ }
+ case KVBoxEventType_OnKeyboardLedsChanged:
+ {
+ CKeyboardLedsChangedEvent comEventSpecific(pEvent);
+ emit sigKeyboardLedsChangeEvent(comEventSpecific.GetNumLock(),
+ comEventSpecific.GetCapsLock(),
+ comEventSpecific.GetScrollLock());
+ break;
+ }
+ case KVBoxEventType_OnStateChanged:
+ {
+ CStateChangedEvent comEventSpecific(pEvent);
+ emit sigStateChange(comEventSpecific.GetState());
+ break;
+ }
+ case KVBoxEventType_OnAdditionsStateChanged:
+ {
+ emit sigAdditionsChange();
+ break;
+ }
+ case KVBoxEventType_OnNetworkAdapterChanged:
+ {
+ CNetworkAdapterChangedEvent comEventSpecific(pEvent);
+ emit sigNetworkAdapterChange(comEventSpecific.GetNetworkAdapter());
+ break;
+ }
+ case KVBoxEventType_OnVRDEServerChanged:
+ case KVBoxEventType_OnVRDEServerInfoChanged:
+ {
+ emit sigVRDEChange();
+ break;
+ }
+ case KVBoxEventType_OnRecordingChanged:
+ {
+ emit sigRecordingChange();
+ break;
+ }
+ case KVBoxEventType_OnUSBControllerChanged:
+ {
+ emit sigUSBControllerChange();
+ break;
+ }
+ case KVBoxEventType_OnUSBDeviceStateChanged:
+ {
+ CUSBDeviceStateChangedEvent comEventSpecific(pEvent);
+ emit sigUSBDeviceStateChange(comEventSpecific.GetDevice(),
+ comEventSpecific.GetAttached(),
+ comEventSpecific.GetError());
+ break;
+ }
+ case KVBoxEventType_OnSharedFolderChanged:
+ {
+ emit sigSharedFolderChange();
+ break;
+ }
+ case KVBoxEventType_OnCPUExecutionCapChanged:
+ {
+ emit sigCPUExecutionCapChange();
+ break;
+ }
+ case KVBoxEventType_OnGuestMonitorChanged:
+ {
+ CGuestMonitorChangedEvent comEventSpecific(pEvent);
+ emit sigGuestMonitorChange(comEventSpecific.GetChangeType(), comEventSpecific.GetScreenId(),
+ QRect(comEventSpecific.GetOriginX(), comEventSpecific.GetOriginY(),
+ comEventSpecific.GetWidth(), comEventSpecific.GetHeight()));
+ break;
+ }
+ case KVBoxEventType_OnRuntimeError:
+ {
+ CRuntimeErrorEvent comEventSpecific(pEvent);
+ emit sigRuntimeError(comEventSpecific.GetFatal(), comEventSpecific.GetId(), comEventSpecific.GetMessage());
+ break;
+ }
+ case KVBoxEventType_OnCanShowWindow:
+ {
+ CCanShowWindowEvent comEventSpecific(pEvent);
+ /* Has to be done in place to give an answer: */
+ bool fVeto = false;
+ QString strReason;
+ emit sigCanShowWindow(fVeto, strReason);
+ if (fVeto)
+ comEventSpecific.AddVeto(strReason);
+ else
+ comEventSpecific.AddApproval(strReason);
+ break;
+ }
+ case KVBoxEventType_OnShowWindow:
+ {
+ CShowWindowEvent comEventSpecific(pEvent);
+ /* Has to be done in place to give an answer: */
+ qint64 winId = comEventSpecific.GetWinId();
+ if (winId != 0)
+ break; /* Already set by some listener. */
+ emit sigShowWindow(winId);
+ comEventSpecific.SetWinId(winId);
+ break;
+ }
+ case KVBoxEventType_OnAudioAdapterChanged:
+ {
+ emit sigAudioAdapterChange();
+ break;
+ }
+
+ case KVBoxEventType_OnProgressPercentageChanged:
+ {
+ CProgressPercentageChangedEvent comEventSpecific(pEvent);
+ emit sigProgressPercentageChange(comEventSpecific.GetProgressId(), (int)comEventSpecific.GetPercent());
+ break;
+ }
+ case KVBoxEventType_OnProgressTaskCompleted:
+ {
+ CProgressTaskCompletedEvent comEventSpecific(pEvent);
+ emit sigProgressTaskComplete(comEventSpecific.GetProgressId());
+ break;
+ }
+
+ case KVBoxEventType_OnGuestSessionRegistered:
+ {
+ CGuestSessionRegisteredEvent comEventSpecific(pEvent);
+ if (comEventSpecific.GetRegistered())
+ emit sigGuestSessionRegistered(comEventSpecific.GetSession());
+ else
+ emit sigGuestSessionUnregistered(comEventSpecific.GetSession());
+ break;
+ }
+ case KVBoxEventType_OnGuestProcessRegistered:
+ {
+ CGuestProcessRegisteredEvent comEventSpecific(pEvent);
+ if (comEventSpecific.GetRegistered())
+ emit sigGuestProcessRegistered(comEventSpecific.GetProcess());
+ else
+ emit sigGuestProcessUnregistered(comEventSpecific.GetProcess());
+ break;
+ }
+ case KVBoxEventType_OnGuestSessionStateChanged:
+ {
+ CGuestSessionStateChangedEvent comEventSpecific(pEvent);
+ emit sigGuestSessionStatedChanged(comEventSpecific);
+ break;
+ }
+ case KVBoxEventType_OnGuestProcessInputNotify:
+ case KVBoxEventType_OnGuestProcessOutput:
+ {
+ break;
+ }
+ case KVBoxEventType_OnGuestProcessStateChanged:
+ {
+ CGuestProcessStateChangedEvent comEventSpecific(pEvent);
+ comEventSpecific.GetError();
+ emit sigGuestProcessStateChanged(comEventSpecific);
+ break;
+ }
+ case KVBoxEventType_OnGuestFileRegistered:
+ case KVBoxEventType_OnGuestFileStateChanged:
+ case KVBoxEventType_OnGuestFileOffsetChanged:
+ case KVBoxEventType_OnGuestFileRead:
+ case KVBoxEventType_OnGuestFileWrite:
+ {
+ break;
+ }
+ case KVBoxEventType_OnClipboardModeChanged:
+ {
+ CClipboardModeChangedEvent comEventSpecific(pEvent);
+ emit sigClipboardModeChange(comEventSpecific.GetClipboardMode());
+ break;
+ }
+ case KVBoxEventType_OnDnDModeChanged:
+ {
+ CDnDModeChangedEvent comEventSpecific(pEvent);
+ emit sigDnDModeChange(comEventSpecific.GetDndMode());
+ break;
+ }
+ default: break;
+ }
+
+ /* Unlock COM cleanup protection token: */
+ uiCommon().comTokenUnlock();
+
+ return S_OK;
+}
+
+void UIMainEventListener::sltHandleThreadFinished()
+{
+ /* We have received a signal about thread finished, that means we were
+ * patiently waiting for it, instead of killing UIMainEventListener object. */
+ UIMainEventListeningThread *pSender = qobject_cast<UIMainEventListeningThread*>(sender());
+ AssertPtrReturnVoid(pSender);
+
+ /* We should remove corresponding thread from the list: */
+ const int iIndex = m_threads.indexOf(pSender);
+ delete m_threads.value(iIndex);
+ m_threads.removeAt(iIndex);
+
+ /* And notify listeners we have really finished: */
+ if (m_threads.isEmpty())
+ emit sigListeningFinished();
+}
+
+#include "UIMainEventListener.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIMainEventListener.h b/src/VBox/Frontends/VirtualBox/src/globals/UIMainEventListener.h
new file mode 100644
index 00000000..95f30687
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIMainEventListener.h
@@ -0,0 +1,262 @@
+/* $Id: UIMainEventListener.h $ */
+/** @file
+ * VBox Qt GUI - UIMainEventListener class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIMainEventListener_h
+#define FEQT_INCLUDED_SRC_globals_UIMainEventListener_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QList>
+#include <QObject>
+#include <QRect>
+#include <QSet>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuestProcess.h"
+#include "CGuestSession.h"
+#include "CMedium.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CUSBDevice.h"
+#include "CVirtualBoxErrorInfo.h"
+
+/* Other VBox includes: */
+#include <VBox/com/listeners.h> /** @todo This drags in VirtualBox.h! It may be possible avoid it for XPCOM, but not COM due to VBoxEventType_T. */
+
+/* Forward declarations: */
+class QPoint;
+class QString;
+class UIMainEventListeningThread;
+class UIMousePointerShapeData;
+class CEventListener;
+class CEventSource;
+class CGuestProcessStateChangedEvent;
+class CGuestSessionStateChangedEvent;
+
+/* Note: On a first look this may seems a little bit complicated.
+ * There are two reasons to use a separate class here which handles the events
+ * and forward them to the public class as signals. The first one is that on
+ * some platforms (e.g. Win32) this events not arrive in the main GUI thread.
+ * So there we have to make sure they are first delivered to the main GUI
+ * thread and later executed there. The second reason is, that the initiator
+ * method may hold a lock on a object which has to be manipulated in the event
+ * consumer. Doing this without being asynchronous would lead to a dead lock. To
+ * avoid both problems we send signals as a queued connection to the event
+ * consumer. Qt will create a event for us, place it in the main GUI event
+ * queue and deliver it later on. */
+
+/** Main event listener. */
+class SHARED_LIBRARY_STUFF UIMainEventListener : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name General signals
+ * @{ */
+ /** Notifies about listening has finished. */
+ void sigListeningFinished();
+ /** @} */
+
+ /** @name VirtualBoxClient related signals
+ * @{ */
+ /** Notifies about the VBoxSVC become @a fAvailable. */
+ void sigVBoxSVCAvailabilityChange(bool fAvailable);
+ /** @} */
+
+ /** @name VirtualBox related signals
+ * @{ */
+ /** Notifies about @a state change event for the machine with @a uId. */
+ void sigMachineStateChange(const QUuid &uId, const KMachineState state);
+ /** Notifies about data change event for the machine with @a uId. */
+ void sigMachineDataChange(const QUuid &uId);
+ /** Notifies about machine with @a uId was @a fRegistered. */
+ void sigMachineRegistered(const QUuid &uId, const bool fRegistered);
+ /** Notifies about machine with @a uId has groups changed. */
+ void sigMachineGroupsChange(const QUuid &uId);
+ /** Notifies about @a state change event for the session of the machine with @a uId. */
+ void sigSessionStateChange(const QUuid &uId, const KSessionState state);
+ /** Notifies about snapshot with @a uSnapshotId was taken for the machine with @a uId. */
+ void sigSnapshotTake(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about snapshot with @a uSnapshotId was deleted for the machine with @a uId. */
+ void sigSnapshotDelete(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about snapshot with @a uSnapshotId was changed for the machine with @a uId. */
+ void sigSnapshotChange(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about snapshot with @a uSnapshotId was restored for the machine with @a uId. */
+ void sigSnapshotRestore(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about request to uninstall cloud provider with @a uId. */
+ void sigCloudProviderUninstall(const QUuid &uId);
+ /** Notifies about cloud provider list changed. */
+ void sigCloudProviderListChanged();
+ /** Notifies about cloud profile with specified @a strName of provider with specified @a uProviderId is @a fRegistered. */
+ void sigCloudProfileRegistered(const QUuid &uProviderId, const QString &strName, bool fRegistered);
+ /** Notifies about cloud profile with specified @a strName of provider with specified @a uProviderId is changed. */
+ void sigCloudProfileChanged(const QUuid &uProviderId, const QString &strName);
+ /** @} */
+
+ /** @name VirtualBox Extra-data related signals
+ * @{ */
+ /** Notifies about extra-data of the machine with @a uId can be changed for the key @a strKey to value @a strValue. */
+ void sigExtraDataCanChange(const QUuid &uId, const QString &strKey, const QString &strValue, bool &fVeto, QString &strVetoReason); /* use Qt::DirectConnection */
+ /** Notifies about extra-data of the machine with @a uId changed for the key @a strKey to value @a strValue. */
+ void sigExtraDataChange(const QUuid &uId, const QString &strKey, const QString &strValue);
+ /** @} */
+
+ /** @name VirtualBox Medium related signals
+ * @{ */
+ /** Notifies about storage controller change.
+ * @param uMachineId Brings the ID of machine corresponding controller belongs to.
+ * @param strControllerName Brings the name of controller this event is related to. */
+ void sigStorageControllerChange(const QUuid &uMachineId, const QString &strControllerName);
+ /** Notifies about storage device change.
+ * @param comAttachment Brings corresponding attachment.
+ * @param fRemoved Brings whether medium is removed or added.
+ * @param fSilent Brings whether this change has gone silent for guest. */
+ void sigStorageDeviceChange(CMediumAttachment comAttachment, bool fRemoved, bool fSilent);
+
+ /** Notifies about storage medium @a comAttachment state change. */
+ void sigMediumChange(CMediumAttachment comAttachment);
+ /** Notifies about storage @a comMedium config change. */
+ void sigMediumConfigChange(CMedium comMedium);
+ /** Notifies about storage medium is (un)registered.
+ * @param uMediumId Brings corresponding medium ID.
+ * @param enmMediumType Brings corresponding medium type.
+ * @param fRegistered Brings whether medium is registered or unregistered. */
+ void sigMediumRegistered(const QUuid &uMediumId, KDeviceType enmMediumType, bool fRegistered);
+ /** @} */
+
+ /** @name Console related signals
+ * @{ */
+ /** Notifies about mouse pointer @a shapeData change. */
+ void sigMousePointerShapeChange(const UIMousePointerShapeData &shapeData);
+ /** Notifies about mouse capability change to @a fSupportsAbsolute, @a fSupportsRelative,
+ * @a fSupportsTouchScreen, @a fSupportsTouchPad and @a fNeedsHostCursor. */
+ void sigMouseCapabilityChange(bool fSupportsAbsolute, bool fSupportsRelative,
+ bool fSupportsTouchScreen, bool fSupportsTouchPad,
+ bool fNeedsHostCursor);
+ /** Notifies about guest request to change the cursor position to @a uX * @a uY.
+ * @param fContainsData Brings whether the @a uX and @a uY values are valid and could be used by the GUI now. */
+ void sigCursorPositionChange(bool fContainsData, unsigned long uX, unsigned long uY);
+ /** Notifies about keyboard LEDs change for @a fNumLock, @a fCapsLock and @a fScrollLock. */
+ void sigKeyboardLedsChangeEvent(bool fNumLock, bool fCapsLock, bool fScrollLock);
+ /** Notifies about machine @a state change. */
+ void sigStateChange(KMachineState state);
+ /** Notifies about guest additions state change. */
+ void sigAdditionsChange();
+ /** Notifies about network @a adapter state change. */
+ void sigNetworkAdapterChange(CNetworkAdapter comAdapter);
+ /** Notifies about VRDE device state change. */
+ void sigVRDEChange();
+ /** Notifies about recording state change. */
+ void sigRecordingChange();
+ /** Notifies about USB controller state change. */
+ void sigUSBControllerChange();
+ /** Notifies about USB @a device state change to @a fAttached, holding additional @a error information. */
+ void sigUSBDeviceStateChange(CUSBDevice comDevice, bool fAttached, CVirtualBoxErrorInfo comError);
+ /** Notifies about shared folder state change. */
+ void sigSharedFolderChange();
+ /** Notifies about CPU execution-cap change. */
+ void sigCPUExecutionCapChange();
+ /** Notifies about guest-screen configuration change of @a type for @a uScreenId with @a screenGeo. */
+ void sigGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo);
+ /** Notifies about Runtime error with @a strErrorId which is @a fFatal and have @a strMessage. */
+ void sigRuntimeError(bool fFatal, QString strErrorId, QString strMessage);
+ /** Notifies about VM window can be shown, allowing to prevent it by @a fVeto with @a strReason. */
+ void sigCanShowWindow(bool &fVeto, QString &strReason); /* use Qt::DirectConnection */
+ /** Notifies about VM window with specified @a winId should be shown. */
+ void sigShowWindow(qint64 &winId); /* use Qt::DirectConnection */
+ /** Notifies about audio adapter state change. */
+ void sigAudioAdapterChange();
+ /** Notifies about the clipboard mode change. */
+ void sigClipboardModeChange(KClipboardMode enmClipboardMode);
+ /** Notifies about the drag and drop mode change. */
+ void sigDnDModeChange(KDnDMode enmDnDMode);
+ /** @} */
+
+ /** @name Progress related signals
+ * @{ */
+ /** Notifies about @a iPercent change for progress with @a uProgressId. */
+ void sigProgressPercentageChange(const QUuid &uProgressId, const int iPercent);
+ /** Notifies about task complete for progress with @a uProgressId. */
+ void sigProgressTaskComplete(const QUuid &uProgressId);
+ /** @} */
+
+ /** @name Guest Session related signals
+ * @{ */
+ /** Notifies about guest session (un)registered event @a is the (un)registed guest session. */
+ void sigGuestSessionRegistered(CGuestSession comGuestSession);
+ void sigGuestSessionUnregistered(CGuestSession comGuestSession);
+
+ /** Notifies about guest process (un)registered event @a is the (un)registed guest process. */
+ void sigGuestProcessRegistered(CGuestProcess comGuestProcess);
+ void sigGuestProcessUnregistered(CGuestProcess comGuestProcess);
+ void sigGuestSessionStatedChanged(const CGuestSessionStateChangedEvent &comEvent);
+ void sigGuestProcessStateChanged(const CGuestProcessStateChangedEvent &comEvent);
+ /** @} */
+
+public:
+
+ /** Constructs main event listener. */
+ UIMainEventListener();
+
+ /** Initialization routine. */
+ HRESULT init(QObject *pParent) { Q_UNUSED(pParent); return S_OK; }
+ /** Deinitialization routine. */
+ void uninit() {}
+
+ /** Registers event source for passive event listener by creating a listening thread.
+ * @param comSource Brings event source we are creating listening thread for.
+ * @param comListener Brings event listener we are creating listening thread for.
+ * @param escapeEventTypes Brings a set of escape event types which commands listener to finish. */
+ void registerSource(const CEventSource &comSource,
+ const CEventListener &comListener,
+ const QSet<KVBoxEventType> &escapeEventTypes = QSet<KVBoxEventType>());
+ /** Unregisters event sources. */
+ void unregisterSources();
+
+ /** Main event handler routine. */
+ STDMETHOD(HandleEvent)(VBoxEventType_T enmType, IEvent *pEvent);
+
+ /** Holds the list of threads handling passive event listening. */
+ QList<UIMainEventListeningThread*> m_threads;
+
+private slots:
+
+ /** Handles thread finished signal. */
+ void sltHandleThreadFinished();
+};
+
+/** Wraps the IListener interface around our implementation class. */
+typedef ListenerImpl<UIMainEventListener, QObject*> UIMainEventListenerImpl;
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIMainEventListener_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.cpp
new file mode 100644
index 00000000..6e562a0b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.cpp
@@ -0,0 +1,2413 @@
+/* $Id: UIMessageCenter.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMessageCenter class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAbstractButton>
+#include <QDir>
+#include <QFileInfo>
+#include <QLocale>
+#include <QProcess>
+#include <QThread>
+#ifdef VBOX_WS_MAC
+# include <QPushButton>
+#endif
+
+/* GUI includes: */
+#include "QIMessageBox.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIErrorString.h"
+#include "UIExtraDataManager.h"
+#include "UIHelpBrowserDialog.h"
+#include "UIHostComboEditor.h"
+#include "UIIconPool.h"
+#include "UIMedium.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINotificationCenter.h"
+#include "UIProgressDialog.h"
+#include "UITranslator.h"
+#include "VBoxAboutDlg.h"
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+# include "UINetworkRequestManager.h"
+#endif
+#ifdef VBOX_OSE
+# include "UINotificationCenter.h"
+#endif
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+#ifdef VBOX_WS_WIN
+# include <Htmlhelp.h>
+#endif
+
+/* COM includes: */
+#include "CAppliance.h"
+#include "CBooleanFormValue.h"
+#include "CChoiceFormValue.h"
+#include "CCloudClient.h"
+#include "CCloudMachine.h"
+#include "CCloudProfile.h"
+#include "CCloudProvider.h"
+#include "CCloudProviderManager.h"
+#include "CConsole.h"
+#include "CDHCPServer.h"
+#include "CDisplay.h"
+#include "CExtPack.h"
+#include "CExtPackFile.h"
+#include "CExtPackManager.h"
+#include "CForm.h"
+#include "CHostNetworkInterface.h"
+#include "CMachine.h"
+#include "CMediumAttachment.h"
+#include "CMediumFormat.h"
+#include "CNATEngine.h"
+#include "CNATNetwork.h"
+#include "CRangedIntegerFormValue.h"
+#include "CSerialPort.h"
+#include "CSharedFolder.h"
+#include "CSnapshot.h"
+#include "CStorageController.h"
+#include "CStringFormValue.h"
+#include "CSystemProperties.h"
+#include "CUnattended.h"
+#include "CVFSExplorer.h"
+#include "CVirtualBoxErrorInfo.h"
+#include "CVirtualSystemDescription.h"
+#include "CVirtualSystemDescriptionForm.h"
+#ifdef VBOX_WITH_DRAG_AND_DROP
+# include "CDnDSource.h"
+# include "CDnDTarget.h"
+# include "CGuest.h"
+#endif
+
+/* Other VBox includes: */
+#include <iprt/errcore.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+
+
+/* static */
+UIMessageCenter *UIMessageCenter::s_pInstance = 0;
+UIMessageCenter *UIMessageCenter::instance() { return s_pInstance; }
+
+/* static */
+void UIMessageCenter::create()
+{
+ /* Make sure instance is NOT created yet: */
+ if (s_pInstance)
+ {
+ AssertMsgFailed(("UIMessageCenter instance is already created!"));
+ return;
+ }
+
+ /* Create instance: */
+ new UIMessageCenter;
+ /* Prepare instance: */
+ s_pInstance->prepare();
+}
+
+/* static */
+void UIMessageCenter::destroy()
+{
+ /* Make sure instance is NOT destroyed yet: */
+ if (!s_pInstance)
+ {
+ AssertMsgFailed(("UIMessageCenter instance is already destroyed!"));
+ return;
+ }
+
+ /* Cleanup instance: */
+ s_pInstance->cleanup();
+ /* Destroy instance: */
+ delete s_pInstance;
+}
+
+void UIMessageCenter::setWarningShown(const QString &strWarningName, bool fWarningShown) const
+{
+ if (fWarningShown && !m_warnings.contains(strWarningName))
+ m_warnings.append(strWarningName);
+ else if (!fWarningShown && m_warnings.contains(strWarningName))
+ m_warnings.removeAll(strWarningName);
+}
+
+bool UIMessageCenter::warningShown(const QString &strWarningName) const
+{
+ return m_warnings.contains(strWarningName);
+}
+
+int UIMessageCenter::message(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const QString &strDetails,
+ const char *pcszAutoConfirmId /* = 0*/,
+ int iButton1 /* = 0*/,
+ int iButton2 /* = 0*/,
+ int iButton3 /* = 0*/,
+ const QString &strButtonText1 /* = QString() */,
+ const QString &strButtonText2 /* = QString() */,
+ const QString &strButtonText3 /* = QString() */,
+ const QString &strHelpKeyword /* = QString() */) const
+{
+ /* If this is NOT a GUI thread: */
+ if (thread() != QThread::currentThread())
+ {
+ /* We have to throw a blocking signal
+ * to show a message-box in the GUI thread: */
+ emit sigToShowMessageBox(pParent, enmType,
+ strMessage, strDetails,
+ iButton1, iButton2, iButton3,
+ strButtonText1, strButtonText2, strButtonText3,
+ QString(pcszAutoConfirmId), strHelpKeyword);
+ /* Inter-thread communications are not yet implemented: */
+ return 0;
+ }
+ /* In usual case we can chow a message-box directly: */
+ return showMessageBox(pParent, enmType,
+ strMessage, strDetails,
+ iButton1, iButton2, iButton3,
+ strButtonText1, strButtonText2, strButtonText3,
+ QString(pcszAutoConfirmId), strHelpKeyword);
+}
+
+void UIMessageCenter::error(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const QString &strDetails,
+ const char *pcszAutoConfirmId /* = 0*/,
+ const QString &strHelpKeyword /* = QString() */) const
+{
+ message(pParent, enmType, strMessage, strDetails, pcszAutoConfirmId,
+ AlertButton_Ok | AlertButtonOption_Default | AlertButtonOption_Escape, 0 /* Button 2 */, 0 /* Button 3 */,
+ QString() /* strButtonText1 */, QString() /* strButtonText2 */, QString() /* strButtonText3 */, strHelpKeyword);
+}
+
+bool UIMessageCenter::errorWithQuestion(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const QString &strDetails,
+ const char *pcszAutoConfirmId /* = 0*/,
+ const QString &strOkButtonText /* = QString()*/,
+ const QString &strCancelButtonText /* = QString()*/,
+ const QString &strHelpKeyword /* = QString()*/) const
+{
+ return (message(pParent, enmType, strMessage, strDetails, pcszAutoConfirmId,
+ AlertButton_Ok | AlertButtonOption_Default,
+ AlertButton_Cancel | AlertButtonOption_Escape,
+ 0 /* third button */,
+ strOkButtonText,
+ strCancelButtonText,
+ QString() /* third button text*/,
+ strHelpKeyword) &
+ AlertButtonMask) == AlertButton_Ok;
+}
+
+void UIMessageCenter::alert(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const char *pcszAutoConfirmId /* = 0*/,
+ const QString &strHelpKeyword /* = QString()*/) const
+{
+ error(pParent, enmType, strMessage, QString(), pcszAutoConfirmId, strHelpKeyword);
+}
+
+int UIMessageCenter::question(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const char *pcszAutoConfirmId/* = 0*/,
+ int iButton1 /* = 0*/,
+ int iButton2 /* = 0*/,
+ int iButton3 /* = 0*/,
+ const QString &strButtonText1 /* = QString()*/,
+ const QString &strButtonText2 /* = QString()*/,
+ const QString &strButtonText3 /* = QString()*/) const
+{
+ return message(pParent, enmType, strMessage, QString(), pcszAutoConfirmId,
+ iButton1, iButton2, iButton3, strButtonText1, strButtonText2, strButtonText3);
+}
+
+bool UIMessageCenter::questionBinary(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const char *pcszAutoConfirmId /* = 0*/,
+ const QString &strOkButtonText /* = QString()*/,
+ const QString &strCancelButtonText /* = QString()*/,
+ bool fDefaultFocusForOk /* = true*/) const
+{
+ return fDefaultFocusForOk ?
+ ((question(pParent, enmType, strMessage, pcszAutoConfirmId,
+ AlertButton_Ok | AlertButtonOption_Default,
+ AlertButton_Cancel | AlertButtonOption_Escape,
+ 0 /* third button */,
+ strOkButtonText,
+ strCancelButtonText,
+ QString() /* third button */) &
+ AlertButtonMask) == AlertButton_Ok) :
+ ((question(pParent, enmType, strMessage, pcszAutoConfirmId,
+ AlertButton_Ok,
+ AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape,
+ 0 /* third button */,
+ strOkButtonText,
+ strCancelButtonText,
+ QString() /* third button */) &
+ AlertButtonMask) == AlertButton_Ok);
+}
+
+int UIMessageCenter::questionTrinary(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const char *pcszAutoConfirmId /* = 0*/,
+ const QString &strChoice1ButtonText /* = QString()*/,
+ const QString &strChoice2ButtonText /* = QString()*/,
+ const QString &strCancelButtonText /* = QString()*/) const
+{
+ return question(pParent, enmType, strMessage, pcszAutoConfirmId,
+ AlertButton_Choice1,
+ AlertButton_Choice2 | AlertButtonOption_Default,
+ AlertButton_Cancel | AlertButtonOption_Escape,
+ strChoice1ButtonText,
+ strChoice2ButtonText,
+ strCancelButtonText);
+}
+
+int UIMessageCenter::messageWithOption(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const QString &strOptionText,
+ bool fDefaultOptionValue /* = true */,
+ int iButton1 /* = 0*/,
+ int iButton2 /* = 0*/,
+ int iButton3 /* = 0*/,
+ const QString &strButtonName1 /* = QString() */,
+ const QString &strButtonName2 /* = QString() */,
+ const QString &strButtonName3 /* = QString() */) const
+{
+ /* If no buttons are set, using single 'OK' button: */
+ if (iButton1 == 0 && iButton2 == 0 && iButton3 == 0)
+ iButton1 = AlertButton_Ok | AlertButtonOption_Default;
+
+ /* Assign corresponding title and icon: */
+ QString strTitle;
+ AlertIconType icon;
+ switch (enmType)
+ {
+ default:
+ case MessageType_Info:
+ strTitle = tr("VirtualBox - Information", "msg box title");
+ icon = AlertIconType_Information;
+ break;
+ case MessageType_Question:
+ strTitle = tr("VirtualBox - Question", "msg box title");
+ icon = AlertIconType_Question;
+ break;
+ case MessageType_Warning:
+ strTitle = tr("VirtualBox - Warning", "msg box title");
+ icon = AlertIconType_Warning;
+ break;
+ case MessageType_Error:
+ strTitle = tr("VirtualBox - Error", "msg box title");
+ icon = AlertIconType_Critical;
+ break;
+ case MessageType_Critical:
+ strTitle = tr("VirtualBox - Critical Error", "msg box title");
+ icon = AlertIconType_Critical;
+ break;
+ case MessageType_GuruMeditation:
+ strTitle = "VirtualBox - Guru Meditation"; /* don't translate this */
+ icon = AlertIconType_GuruMeditation;
+ break;
+ }
+
+ /* Create message-box: */
+ QWidget *pBoxParent = windowManager().realParentWindow(pParent ? pParent : windowManager().mainWindowShown());
+ QPointer<QIMessageBox> pBox = new QIMessageBox(strTitle, strMessage, icon,
+ iButton1, iButton2, iButton3, pBoxParent);
+ windowManager().registerNewParent(pBox, pBoxParent);
+
+ /* Load option: */
+ if (!strOptionText.isNull())
+ {
+ pBox->setFlagText(strOptionText);
+ pBox->setFlagChecked(fDefaultOptionValue);
+ }
+
+ /* Configure button-text: */
+ if (!strButtonName1.isNull())
+ pBox->setButtonText(0, strButtonName1);
+ if (!strButtonName2.isNull())
+ pBox->setButtonText(1, strButtonName2);
+ if (!strButtonName3.isNull())
+ pBox->setButtonText(2, strButtonName3);
+
+ /* Show box: */
+ int rc = pBox->exec();
+
+ /* Make sure box still valid: */
+ if (!pBox)
+ return rc;
+
+ /* Save option: */
+ if (pBox->flagChecked())
+ rc |= AlertOption_CheckBox;
+
+ /* Delete message-box: */
+ if (pBox)
+ delete pBox;
+
+ return rc;
+}
+
+bool UIMessageCenter::showModalProgressDialog(CProgress &progress,
+ const QString &strTitle,
+ const QString &strImage /* = "" */,
+ QWidget *pParent /* = 0*/,
+ int cMinDuration /* = 2000 */)
+{
+ /* Prepare result: */
+ bool fRc = false;
+
+ /* Gather suitable dialog parent: */
+ QWidget *pDlgParent = windowManager().realParentWindow(pParent ? pParent : windowManager().mainWindowShown());
+
+ /* Prepare pixmap: */
+ QPixmap pixmap;
+ if (!strImage.isEmpty())
+ pixmap = pDlgParent
+ ? UIIconPool::iconSet(strImage).pixmap(pDlgParent->windowHandle(), QSize(90, 90))
+ : UIIconPool::iconSet(strImage).pixmap(QSize(90, 90));
+
+ /* Create progress-dialog: */
+ QPointer<UIProgressDialog> pProgressDlg = new UIProgressDialog(progress, strTitle, &pixmap, cMinDuration, pDlgParent);
+ if (pProgressDlg)
+ {
+ /* Register it as new parent: */
+ windowManager().registerNewParent(pProgressDlg, pDlgParent);
+
+ /* Run the dialog with the 350 ms refresh interval. */
+ pProgressDlg->run(350);
+
+ /* Make sure progress-dialog still valid: */
+ if (pProgressDlg)
+ {
+ /* Delete progress-dialog: */
+ delete pProgressDlg;
+ fRc = true;
+ }
+ }
+
+ /* Return result: */
+ return fRc;
+}
+
+void UIMessageCenter::cannotFindLanguage(const QString &strLangId, const QString &strNlsPath) const
+{
+ alert(0, MessageType_Error,
+ tr("<p>Could not find a language file for the language <b>%1</b> in the directory <b><nobr>%2</nobr></b>.</p>"
+ "<p>The language will be temporarily reset to the system default language. "
+ "Please go to the <b>Preferences</b> window which you can open from the <b>File</b> menu of the "
+ "VirtualBox Manager window, and select one of the existing languages on the <b>Language</b> page.</p>")
+ .arg(strLangId).arg(strNlsPath));
+}
+
+void UIMessageCenter::cannotLoadLanguage(const QString &strLangFile) const
+{
+ alert(0, MessageType_Error,
+ tr("<p>Could not load the language file <b><nobr>%1</nobr></b>. "
+ "<p>The language will be temporarily reset to English (built-in). "
+ "Please go to the <b>Preferences</b> window which you can open from the <b>File</b> menu of the "
+ "VirtualBox Manager window, and select one of the existing languages on the <b>Language</b> page.</p>")
+ .arg(strLangFile));
+}
+
+void UIMessageCenter::cannotInitUserHome(const QString &strUserHome) const
+{
+ error(0, MessageType_Critical,
+ tr("<p>Failed to initialize COM because the VirtualBox global "
+ "configuration directory <b><nobr>%1</nobr></b> is not accessible. "
+ "Please check the permissions of this directory and of its parent directory.</p>"
+ "<p>The application will now terminate.</p>")
+ .arg(strUserHome),
+ UIErrorString::formatErrorInfo(COMErrorInfo()));
+}
+
+void UIMessageCenter::cannotInitCOM(HRESULT rc) const
+{
+ error(0, MessageType_Critical,
+ tr("<p>Failed to initialize COM or to find the VirtualBox COM server. "
+ "Most likely, the VirtualBox server is not running or failed to start.</p>"
+ "<p>The application will now terminate.</p>"),
+ UIErrorString::formatErrorInfo(COMErrorInfo(), rc));
+}
+
+void UIMessageCenter::cannotHandleRuntimeOption(const QString &strOption) const
+{
+ alert(0, MessageType_Error,
+ tr("<b>%1</b> is an option for the VirtualBox VM runner (VirtualBoxVM) application, not the VirtualBox Manager.")
+ .arg(strOption));
+}
+
+#ifdef RT_OS_LINUX
+void UIMessageCenter::warnAboutWrongUSBMounted() const
+{
+ alert(0, MessageType_Warning,
+ tr("You seem to have the USBFS filesystem mounted at /sys/bus/usb/drivers. "
+ "We strongly recommend that you change this, as it is a severe mis-configuration of "
+ "your system which could cause USB devices to fail in unexpected ways."),
+ "warnAboutWrongUSBMounted");
+}
+#endif /* RT_OS_LINUX */
+
+void UIMessageCenter::cannotStartSelector() const
+{
+ alert(0, MessageType_Critical,
+ tr("<p>Cannot start the VirtualBox Manager due to local restrictions.</p>"
+ "<p>The application will now terminate.</p>"));
+}
+
+void UIMessageCenter::cannotStartRuntime() const
+{
+ /* Prepare error string: */
+ const QString strError = tr("<p>You must specify a machine to start, using the command line.</p><p>%1</p>",
+ "There will be a usage text passed as argument.");
+
+ /* Prepare Usage, it can change in future: */
+ const QString strTable = QString("<table cellspacing=0 style='white-space:pre'>%1</table>");
+ const QString strUsage = tr("<tr>"
+ "<td>Usage: VirtualBoxVM --startvm &lt;name|UUID&gt;</td>"
+ "</tr>"
+ "<tr>"
+ "<td>Starts the VirtualBox virtual machine with the given "
+ "name or unique identifier (UUID).</td>"
+ "</tr>");
+
+ /* Show error: */
+ alert(0, MessageType_Error, strError.arg(strTable.arg(strUsage)));
+}
+
+void UIMessageCenter::cannotCreateVirtualBoxClient(const CVirtualBoxClient &comClient) const
+{
+ error(0, MessageType_Critical,
+ tr("<p>Failed to create the VirtualBoxClient COM object.</p>"
+ "<p>The application will now terminate.</p>"),
+ UIErrorString::formatErrorInfo(comClient));
+}
+
+void UIMessageCenter::cannotAcquireVirtualBox(const CVirtualBoxClient &comClient) const
+{
+ QString err = tr("<p>Failed to acquire the VirtualBox COM object.</p>"
+ "<p>The application will now terminate.</p>");
+#if defined(VBOX_WS_X11) || defined(VBOX_WS_MAC)
+ if (comClient.lastRC() == NS_ERROR_SOCKET_FAIL)
+ err += tr("<p>The reason for this error are most likely wrong permissions of the IPC "
+ "daemon socket due to an installation problem. Please check the permissions of "
+ "<font color=blue>'/tmp'</font> and <font color=blue>'/tmp/.vbox-*-ipc/'</font></p>");
+#endif
+ error(0, MessageType_Critical, err, UIErrorString::formatErrorInfo(comClient));
+}
+
+void UIMessageCenter::cannotFindMachineByName(const CVirtualBox &comVBox, const QString &strName) const
+{
+ error(0, MessageType_Error,
+ tr("There is no virtual machine named <b>%1</b>.")
+ .arg(strName),
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+void UIMessageCenter::cannotFindMachineById(const CVirtualBox &comVBox, const QUuid &uId) const
+{
+ error(0, MessageType_Error,
+ tr("There is no virtual machine with the identifier <b>%1</b>.")
+ .arg(uId.toString()),
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+void UIMessageCenter::cannotSetExtraData(const CVirtualBox &comVBox, const QString &strKey, const QString &strValue)
+{
+ error(0, MessageType_Error,
+ tr("Failed to set the global VirtualBox extra data for key <i>%1</i> to value <i>{%2}</i>.")
+ .arg(strKey, strValue),
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+void UIMessageCenter::cannotOpenMedium(const CVirtualBox &comVBox, const QString &strLocation, QWidget *pParent /* = 0 */) const
+{
+ /* Show the error: */
+ error(pParent, MessageType_Error,
+ tr("Failed to open the disk image file <nobr><b>%1</b></nobr>.").arg(strLocation), UIErrorString::formatErrorInfo(comVBox));
+}
+
+void UIMessageCenter::cannotOpenSession(const CSession &comSession) const
+{
+ error(0, MessageType_Error,
+ tr("Failed to create a new session."),
+ UIErrorString::formatErrorInfo(comSession));
+}
+
+void UIMessageCenter::cannotOpenSession(const CMachine &comMachine) const
+{
+ error(0, MessageType_Error,
+ tr("Failed to open a session for the virtual machine <b>%1</b>.")
+ .arg(CMachine(comMachine).GetName()),
+ UIErrorString::formatErrorInfo(comMachine));
+}
+
+void UIMessageCenter::cannotOpenSession(const CProgress &comProgress, const QString &strMachineName) const
+{
+ error(0, MessageType_Error,
+ tr("Failed to open a session for the virtual machine <b>%1</b>.")
+ .arg(strMachineName),
+ UIErrorString::formatErrorInfo(comProgress));
+}
+
+void UIMessageCenter::cannotSetExtraData(const CMachine &machine, const QString &strKey, const QString &strValue)
+{
+ error(0, MessageType_Error,
+ tr("Failed to set the extra data for key <i>%1</i> of machine <i>%2</i> to value <i>{%3}</i>.")
+ .arg(strKey, CMachine(machine).GetName(), strValue),
+ UIErrorString::formatErrorInfo(machine));
+}
+
+void UIMessageCenter::cannotAttachDevice(const CMachine &machine, UIMediumDeviceType enmType,
+ const QString &strLocation, const StorageSlot &storageSlot,
+ QWidget *pParent /* = 0*/)
+{
+ QString strMessage;
+ switch (enmType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ {
+ strMessage = tr("Failed to attach the hard disk (<nobr><b>%1</b></nobr>) to the slot <i>%2</i> of the machine <b>%3</b>.")
+ .arg(strLocation).arg(gpConverter->toString(storageSlot)).arg(CMachine(machine).GetName());
+ break;
+ }
+ case UIMediumDeviceType_DVD:
+ {
+ strMessage = tr("Failed to attach the optical drive (<nobr><b>%1</b></nobr>) to the slot <i>%2</i> of the machine <b>%3</b>.")
+ .arg(strLocation).arg(gpConverter->toString(storageSlot)).arg(CMachine(machine).GetName());
+ break;
+ }
+ case UIMediumDeviceType_Floppy:
+ {
+ strMessage = tr("Failed to attach the floppy drive (<nobr><b>%1</b></nobr>) to the slot <i>%2</i> of the machine <b>%3</b>.")
+ .arg(strLocation).arg(gpConverter->toString(storageSlot)).arg(CMachine(machine).GetName());
+ break;
+ }
+ default:
+ break;
+ }
+ error(pParent, MessageType_Error,
+ strMessage, UIErrorString::formatErrorInfo(machine));
+}
+
+void UIMessageCenter::cannotDetachDevice(const CMachine &machine, UIMediumDeviceType enmType,
+ const QString &strLocation, const StorageSlot &storageSlot,
+ QWidget *pParent /* = 0*/) const
+{
+ /* Prepare the message: */
+ QString strMessage;
+ switch (enmType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ {
+ strMessage = tr("Failed to detach the hard disk (<nobr><b>%1</b></nobr>) from the slot <i>%2</i> of the machine <b>%3</b>.")
+ .arg(strLocation, gpConverter->toString(storageSlot), CMachine(machine).GetName());
+ break;
+ }
+ case UIMediumDeviceType_DVD:
+ {
+ strMessage = tr("Failed to detach the optical drive (<nobr><b>%1</b></nobr>) from the slot <i>%2</i> of the machine <b>%3</b>.")
+ .arg(strLocation, gpConverter->toString(storageSlot), CMachine(machine).GetName());
+ break;
+ }
+ case UIMediumDeviceType_Floppy:
+ {
+ strMessage = tr("Failed to detach the floppy drive (<nobr><b>%1</b></nobr>) from the slot <i>%2</i> of the machine <b>%3</b>.")
+ .arg(strLocation, gpConverter->toString(storageSlot), CMachine(machine).GetName());
+ break;
+ }
+ default:
+ break;
+ }
+ /* Show the error: */
+ error(pParent, MessageType_Error, strMessage, UIErrorString::formatErrorInfo(machine));
+}
+
+bool UIMessageCenter::cannotRemountMedium(const CMachine &machine, const UIMedium &medium, bool fMount,
+ bool fRetry, QWidget *pParent /* = 0*/) const
+{
+ /* Compose the message: */
+ QString strMessage;
+ switch (medium.type())
+ {
+ case UIMediumDeviceType_DVD:
+ {
+ if (fMount)
+ {
+ strMessage = tr("<p>Unable to insert the virtual optical disk <nobr><b>%1</b></nobr> into the machine <b>%2</b>.</p>");
+ if (fRetry)
+ strMessage += tr("<p>Would you like to try to force insertion of this disk?</p>");
+ }
+ else
+ {
+ strMessage = tr("<p>Unable to eject the virtual optical disk <nobr><b>%1</b></nobr> from the machine <b>%2</b>.</p>");
+ if (fRetry)
+ strMessage += tr("<p>Would you like to try to force ejection of this disk?</p>");
+ }
+ break;
+ }
+ case UIMediumDeviceType_Floppy:
+ {
+ if (fMount)
+ {
+ strMessage = tr("<p>Unable to insert the virtual floppy disk <nobr><b>%1</b></nobr> into the machine <b>%2</b>.</p>");
+ if (fRetry)
+ strMessage += tr("<p>Would you like to try to force insertion of this disk?</p>");
+ }
+ else
+ {
+ strMessage = tr("<p>Unable to eject the virtual floppy disk <nobr><b>%1</b></nobr> from the machine <b>%2</b>.</p>");
+ if (fRetry)
+ strMessage += tr("<p>Would you like to try to force ejection of this disk?</p>");
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ /* Show the messsage: */
+ if (fRetry)
+ return errorWithQuestion(pParent, MessageType_Question,
+ strMessage.arg(medium.isHostDrive() ? medium.name() : medium.location(), CMachine(machine).GetName()),
+ UIErrorString::formatErrorInfo(machine),
+ 0 /* Auto Confirm ID */,
+ tr("Force Unmount"));
+ error(pParent, MessageType_Error,
+ strMessage.arg(medium.isHostDrive() ? medium.name() : medium.location(), CMachine(machine).GetName()),
+ UIErrorString::formatErrorInfo(machine));
+ return false;
+}
+
+void UIMessageCenter::cannotSetHostSettings(const CHost &comHost, QWidget *pParent /* = 0 */) const
+{
+ error(pParent, MessageType_Critical,
+ tr("Failed to set global host settings."),
+ UIErrorString::formatErrorInfo(comHost));
+}
+
+void UIMessageCenter::cannotSetSystemProperties(const CSystemProperties &properties, QWidget *pParent /* = 0*/) const
+{
+ error(pParent, MessageType_Critical,
+ tr("Failed to set global VirtualBox properties."),
+ UIErrorString::formatErrorInfo(properties));
+}
+
+void UIMessageCenter::cannotSaveMachineSettings(const CMachine &machine, QWidget *pParent /* = 0*/) const
+{
+ error(pParent, MessageType_Error,
+ tr("Failed to save the settings of the virtual machine <b>%1</b> to <b><nobr>%2</nobr></b>.")
+ .arg(CMachine(machine).GetName(), CMachine(machine).GetSettingsFilePath()),
+ UIErrorString::formatErrorInfo(machine));
+}
+
+void UIMessageCenter::cannotAddDiskEncryptionPassword(const CConsole &console)
+{
+ error(0, MessageType_Error,
+ tr("Bad password or authentication failure."),
+ UIErrorString::formatErrorInfo(console));
+}
+
+bool UIMessageCenter::confirmResetMachine(const QString &strNames) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>Do you really want to reset the following virtual machines?</p>"
+ "<p><b>%1</b></p><p>This will cause any unsaved data "
+ "in applications running inside it to be lost.</p>")
+ .arg(strNames),
+ "confirmResetMachine" /* auto-confirm id */,
+ tr("Reset", "machine"));
+}
+
+void UIMessageCenter::cannotSaveSettings(const QString strDetails, QWidget *pParent /* = 0 */) const
+{
+ error(pParent, MessageType_Error,
+ tr("Failed to save the settings."),
+ strDetails);
+}
+
+void UIMessageCenter::warnAboutUnaccessibleUSB(const COMBaseWithEI &object, QWidget *pParent /* = 0*/) const
+{
+ /* If IMachine::GetUSBController(), IHost::GetUSBDevices() etc. return
+ * E_NOTIMPL, it means the USB support is intentionally missing
+ * (as in the OSE version). Don't show the error message in this case. */
+ COMResult res(object);
+ if (res.rc() == E_NOTIMPL)
+ return;
+ /* Show the error: */
+ error(pParent, res.isWarning() ? MessageType_Warning : MessageType_Error,
+ tr("Failed to access the USB subsystem."),
+ UIErrorString::formatErrorInfo(res),
+ "warnAboutUnaccessibleUSB");
+}
+
+void UIMessageCenter::warnAboutStateChange(QWidget *pParent /* = 0*/) const
+{
+ if (warningShown("warnAboutStateChange"))
+ return;
+ setWarningShown("warnAboutStateChange", true);
+
+ alert(pParent, MessageType_Warning,
+ tr("The virtual machine that you are changing has been started. "
+ "Only certain settings can be changed while a machine is running. "
+ "All other changes will be lost if you close this window now."));
+
+ setWarningShown("warnAboutStateChange", false);
+}
+
+bool UIMessageCenter::confirmSettingsDiscarding(QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>The machine settings were changed.</p>"
+ "<p>Would you like to discard the changed settings or to keep editing them?</p>"),
+ 0 /* auto-confirm id */,
+ tr("Discard changes"), tr("Keep editing"));
+
+}
+
+bool UIMessageCenter::confirmSettingsReloading(QWidget *pParent /* = 0 */) const
+{
+ if (warningShown("confirmSettingsReloading"))
+ return false;
+ setWarningShown("confirmSettingsReloading", true);
+
+ const bool fResult = questionBinary(pParent, MessageType_Question,
+ tr("<p>The machine settings were changed while you were editing them. "
+ "You currently have unsaved setting changes.</p>"
+ "<p>Would you like to reload the changed settings or to keep your own changes?</p>"),
+ 0 /* auto-confirm id */,
+ tr("Reload settings"), tr("Keep changes"));
+
+ setWarningShown("confirmSettingsReloading", false);
+
+ return fResult;
+}
+
+int UIMessageCenter::confirmRemovingOfLastDVDDevice(QWidget *pParent /* = 0*/) const
+{
+ return questionBinary(pParent, MessageType_Info,
+ tr("<p>Are you sure you want to delete the optical drive?</p>"
+ "<p>You will not be able to insert any optical disks or ISO images "
+ "or install the Guest Additions without it!</p>"),
+ 0 /* auto-confirm id */,
+ tr("&Remove", "medium") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmStorageBusChangeWithOpticalRemoval(QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>This controller has optical devices attached. You have requested storage bus "
+ "change to type which doesn't support optical devices.</p><p>If you proceed optical "
+ "devices will be removed.</p>"));
+}
+
+bool UIMessageCenter::confirmStorageBusChangeWithExcessiveRemoval(QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>This controller has devices attached. You have requested storage bus change to "
+ "type which supports smaller amount of attached devices.</p><p>If you proceed "
+ "excessive devices will be removed.</p>"));
+}
+
+bool UIMessageCenter::warnAboutIncorrectPort(QWidget *pParent /* = 0 */) const
+{
+ alert(pParent, MessageType_Error,
+ tr("The current port forwarding rules are not valid. "
+ "None of the host or guest port values may be set to zero."));
+ return false;
+}
+
+bool UIMessageCenter::warnAboutIncorrectAddress(QWidget *pParent /* = 0 */) const
+{
+ alert(pParent, MessageType_Error,
+ tr("The current port forwarding rules are not valid. "
+ "All of the host or guest address values should be correct or empty."));
+ return false;
+}
+
+bool UIMessageCenter::warnAboutEmptyGuestAddress(QWidget *pParent /* = 0 */) const
+{
+ alert(pParent, MessageType_Error,
+ tr("The current port forwarding rules are not valid. "
+ "None of the guest address values may be empty."));
+ return false;
+}
+
+bool UIMessageCenter::warnAboutNameShouldBeUnique(QWidget *pParent /* = 0 */) const
+{
+ alert(pParent, MessageType_Error,
+ tr("The current port forwarding rules are not valid. "
+ "Rule names should be unique."));
+ return false;
+}
+
+bool UIMessageCenter::warnAboutRulesConflict(QWidget *pParent /* = 0 */) const
+{
+ alert(pParent, MessageType_Error,
+ tr("The current port forwarding rules are not valid. "
+ "Few rules have same host ports and conflicting IP addresses."));
+ return false;
+}
+
+bool UIMessageCenter::confirmCancelingPortForwardingDialog(QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>There are unsaved changes in the port forwarding configuration.</p>"
+ "<p>If you proceed your changes will be discarded.</p>"),
+ 0 /* auto-confirm id */,
+ QString() /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmRestoringDefaultKeys(QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>Are you going to restore default secure boot keys.</p>"
+ "<p>If you proceed your current keys will be rewritten. "
+ "You may not be able to boot affected VM anymore.</p>"),
+ 0 /* auto-confirm id */,
+ QString() /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::warnAboutInaccessibleMedia() const
+{
+ return questionBinary(0, MessageType_Warning,
+ tr("<p>One or more disk image files are not currently accessible. As a result, you will "
+ "not be able to operate virtual machines that use these files until "
+ "they become accessible later.</p>"
+ "<p>Press <b>Check</b> to open the Virtual Media Manager window and "
+ "see which files are inaccessible, or press <b>Ignore</b> to "
+ "ignore this message.</p>"),
+ "warnAboutInaccessibleMedia",
+ tr("Check", "inaccessible media message box"), tr("Ignore"));
+}
+
+bool UIMessageCenter::confirmDiscardSavedState(const QString &strNames) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>Are you sure you want to discard the saved state of "
+ "the following virtual machines?</p><p><b>%1</b></p>"
+ "<p>This operation is equivalent to resetting or powering off "
+ "the machine without doing a proper shutdown of the guest OS.</p>")
+ .arg(strNames),
+ 0 /* auto-confirm id */,
+ tr("Discard", "saved state"));
+}
+
+bool UIMessageCenter::confirmTerminateCloudInstance(const QString &strNames) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>Are you sure you want to terminate the cloud instance "
+ "of the following virtual machines?</p><p><b>%1</b></p>")
+ .arg(strNames),
+ 0 /* auto-confirm id */,
+ tr("Terminate", "cloud instance"));
+}
+
+bool UIMessageCenter::confirmACPIShutdownMachine(const QString &strNames) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>Do you really want to send an ACPI shutdown signal "
+ "to the following virtual machines?</p><p><b>%1</b></p>")
+ .arg(strNames),
+ "confirmACPIShutdownMachine" /* auto-confirm id */,
+ tr("ACPI Shutdown", "machine"));
+}
+
+bool UIMessageCenter::confirmPowerOffMachine(const QString &strNames) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>Do you really want to power off the following virtual machines?</p>"
+ "<p><b>%1</b></p><p>This will cause any unsaved data in applications "
+ "running inside it to be lost.</p>")
+ .arg(strNames),
+ "confirmPowerOffMachine" /* auto-confirm id */,
+ tr("Power Off", "machine"));
+}
+
+bool UIMessageCenter::confirmStartMultipleMachines(const QString &strNames) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>You are about to start all of the following virtual machines:</p>"
+ "<p><b>%1</b></p><p>This could take some time and consume a lot of "
+ "host system resources. Do you wish to proceed?</p>").arg(strNames),
+ "confirmStartMultipleMachines" /* auto-confirm id */);
+}
+
+bool UIMessageCenter::confirmAutomaticCollisionResolve(const QString &strName, const QString &strGroupName) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>You are trying to move group <nobr><b>%1</b></nobr> to group "
+ "<nobr><b>%2</b></nobr> which already have another item with the same name.</p>"
+ "<p>Would you like to automatically rename it?</p>")
+ .arg(strName, strGroupName),
+ 0 /* auto-confirm id */,
+ tr("Rename"));
+}
+
+void UIMessageCenter::cannotSetGroups(const CMachine &machine) const
+{
+ /* Compose machine name: */
+ QString strName = CMachine(machine).GetName();
+ if (strName.isEmpty())
+ strName = QFileInfo(CMachine(machine).GetSettingsFilePath()).baseName();
+ /* Show the error: */
+ error(0, MessageType_Error,
+ tr("Failed to set groups of the virtual machine <b>%1</b>.")
+ .arg(strName),
+ UIErrorString::formatErrorInfo(machine));
+}
+
+bool UIMessageCenter::confirmMachineItemRemoval(const QStringList &names) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>You are about to remove following virtual machine items from the machine list:</p>"
+ "<p><b>%1</b></p><p>Do you wish to proceed?</p>")
+ .arg(names.join(", ")),
+ 0 /* auto-confirm id */,
+ tr("Remove") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+int UIMessageCenter::confirmMachineRemoval(const QList<CMachine> &machines) const
+{
+ /* Enumerate the machines: */
+ int cInacessibleMachineCount = 0;
+ bool fMachineWithHardDiskPresent = false;
+ QString strMachineNames;
+ foreach (const CMachine &machine, machines)
+ {
+ /* Prepare machine name: */
+ QString strMachineName;
+ if (machine.GetAccessible())
+ {
+ /* Just get machine name: */
+ strMachineName = machine.GetName();
+ /* Enumerate the attachments: */
+ const CMediumAttachmentVector &attachments = machine.GetMediumAttachments();
+ foreach (const CMediumAttachment &attachment, attachments)
+ {
+ /* Check if the medium is a hard disk: */
+ if (attachment.GetType() == KDeviceType_HardDisk)
+ {
+ /* Check if that hard disk isn't shared.
+ * If hard disk is shared, it will *never* be deleted: */
+ QVector<QUuid> usedMachineList = attachment.GetMedium().GetMachineIds();
+ if (usedMachineList.size() == 1)
+ {
+ fMachineWithHardDiskPresent = true;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Compose machine name: */
+ QFileInfo fi(machine.GetSettingsFilePath());
+ strMachineName = UICommon::hasAllowedExtension(fi.completeSuffix(), VBoxFileExts) ? fi.completeBaseName() : fi.fileName();
+ /* Increment inacessible machine count: */
+ ++cInacessibleMachineCount;
+ }
+ /* Append machine name to the full name string: */
+ strMachineNames += QString(strMachineNames.isEmpty() ? "<b>%1</b>" : ", <b>%1</b>").arg(strMachineName);
+ }
+
+ /* Prepare message text: */
+ QString strText = cInacessibleMachineCount == machines.size() ?
+ tr("<p>You are about to remove following inaccessible virtual machines from the machine list:</p>"
+ "<p>%1</p>"
+ "<p>Do you wish to proceed?</p>")
+ .arg(strMachineNames) :
+ fMachineWithHardDiskPresent ?
+ tr("<p>You are about to remove following virtual machines from the machine list:</p>"
+ "<p>%1</p>"
+ "<p>Would you like to delete the files containing the virtual machine from your hard disk as well? "
+ "Doing this will also remove the files containing the machine's virtual hard disks "
+ "if they are not in use by another machine.</p>")
+ .arg(strMachineNames) :
+ tr("<p>You are about to remove following virtual machines from the machine list:</p>"
+ "<p>%1</p>"
+ "<p>Would you like to delete the files containing the virtual machine from your hard disk as well?</p>")
+ .arg(strMachineNames);
+
+ /* Prepare message itself: */
+ return cInacessibleMachineCount == machines.size() ?
+ message(0, MessageType_Question,
+ strText, QString(),
+ 0 /* auto-confirm id */,
+ AlertButton_Ok,
+ AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape,
+ 0,
+ tr("Remove")) :
+ message(0, MessageType_Question,
+ strText, QString(),
+ 0 /* auto-confirm id */,
+ AlertButton_Choice1,
+ AlertButton_Choice2,
+ AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape,
+ tr("Delete all files"),
+ tr("Remove only"));
+}
+
+int UIMessageCenter::confirmCloudMachineRemoval(const QList<CCloudMachine> &machines) const
+{
+ /* Enumerate the machines: */
+ QStringList machineNames;
+ foreach (const CCloudMachine &comMachine, machines)
+ {
+ /* Append machine name to the full name string: */
+ if (comMachine.GetAccessible())
+ machineNames << QString("<b>%1</b>").arg(comMachine.GetName());
+ }
+
+ /* Prepare message text: */
+ QString strText = tr("<p>You are about to remove following cloud virtual machines from the machine list:</p>"
+ "<p>%1</p>"
+ "<p>Would you like to delete the instances and boot volumes of these machines as well?</p>")
+ .arg(machineNames.join(", "));
+
+ /* Prepare message itself: */
+ return message(0, MessageType_Question,
+ strText, QString(),
+ 0 /* auto-confirm id */,
+ AlertButton_Choice1,
+ AlertButton_Choice2,
+ AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape,
+ tr("Delete everything"),
+ tr("Remove only"));
+}
+
+int UIMessageCenter::confirmSnapshotRestoring(const QString &strSnapshotName, bool fAlsoCreateNewSnapshot) const
+{
+ return fAlsoCreateNewSnapshot ?
+ messageWithOption(0, MessageType_Question,
+ tr("<p>You are about to restore snapshot <nobr><b>%1</b></nobr>.</p>"
+ "<p>You can create a snapshot of the current state of the virtual machine first by checking the box below; "
+ "if you do not do this the current state will be permanently lost. Do you wish to proceed?</p>")
+ .arg(strSnapshotName),
+ tr("Create a snapshot of the current machine state"),
+ !gEDataManager->messagesWithInvertedOption().contains("confirmSnapshotRestoring"),
+ AlertButton_Ok,
+ AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape,
+ 0 /* 3rd button */,
+ tr("Restore"), tr("Cancel"), QString() /* 3rd button text */) :
+ message(0, MessageType_Question,
+ tr("<p>Are you sure you want to restore snapshot <nobr><b>%1</b></nobr>?</p>")
+ .arg(strSnapshotName),
+ QString() /* details */,
+ 0 /* auto-confirm id */,
+ AlertButton_Ok,
+ AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape,
+ 0 /* 3rd button */,
+ tr("Restore"), tr("Cancel"), QString() /* 3rd button text */);
+}
+
+bool UIMessageCenter::confirmSnapshotRemoval(const QString &strSnapshotName) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>Deleting the snapshot will cause the state information saved in it to be lost, and storage data spread over "
+ "several image files that VirtualBox has created together with the snapshot will be merged into one file. "
+ "This can be a lengthy process, and the information in the snapshot cannot be recovered.</p>"
+ "</p>Are you sure you want to delete the selected snapshot <b>%1</b>?</p>")
+ .arg(strSnapshotName),
+ 0 /* auto-confirm id */,
+ tr("Delete") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::warnAboutSnapshotRemovalFreeSpace(const QString &strSnapshotName,
+ const QString &strTargetImageName,
+ const QString &strTargetImageMaxSize,
+ const QString &strTargetFileSystemFree) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>Deleting the snapshot %1 will temporarily need more storage space. In the worst case the size of image %2 will grow by %3, "
+ "however on this filesystem there is only %4 free.</p><p>Running out of storage space during the merge operation can result in "
+ "corruption of the image and the VM configuration, i.e. loss of the VM and its data.</p><p>You may continue with deleting "
+ "the snapshot at your own risk.</p>")
+ .arg(strSnapshotName, strTargetImageName, strTargetImageMaxSize, strTargetFileSystemFree),
+ 0 /* auto-confirm id */,
+ tr("Delete") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmInstallExtensionPack(const QString &strPackName, const QString &strPackVersion,
+ const QString &strPackDescription, QWidget *pParent /* = 0*/) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>You are about to install a VirtualBox extension pack. "
+ "Extension packs complement the functionality of VirtualBox and can contain system level software "
+ "that could be potentially harmful to your system. Please review the description below and only proceed "
+ "if you have obtained the extension pack from a trusted source.</p>"
+ "<p><table cellpadding=0 cellspacing=5>"
+ "<tr><td><b>Name:&nbsp;&nbsp;</b></td><td>%1</td></tr>"
+ "<tr><td><b>Version:&nbsp;&nbsp;</b></td><td>%2</td></tr>"
+ "<tr><td><b>Description:&nbsp;&nbsp;</b></td><td>%3</td></tr>"
+ "</table></p>")
+ .arg(strPackName).arg(strPackVersion).arg(strPackDescription),
+ 0 /* auto-confirm id */,
+ tr("Install", "extension pack"));
+}
+
+bool UIMessageCenter::confirmReplaceExtensionPack(const QString &strPackName, const QString &strPackVersionNew,
+ const QString &strPackVersionOld, const QString &strPackDescription,
+ QWidget *pParent /* = 0*/) const
+{
+ /* Prepare initial message: */
+ QString strBelehrung = tr("Extension packs complement the functionality of VirtualBox and can contain "
+ "system level software that could be potentially harmful to your system. "
+ "Please review the description below and only proceed if you have obtained "
+ "the extension pack from a trusted source.");
+
+ /* Compare versions: */
+ QByteArray ba1 = strPackVersionNew.toUtf8();
+ QByteArray ba2 = strPackVersionOld.toUtf8();
+ int iVerCmp = RTStrVersionCompare(ba1.constData(), ba2.constData());
+
+ /* Show the question: */
+ bool fRc;
+ if (iVerCmp > 0)
+ fRc = questionBinary(pParent, MessageType_Question,
+ tr("<p>An older version of the extension pack is already installed, would you like to upgrade? "
+ "<p>%1</p>"
+ "<p><table cellpadding=0 cellspacing=5>"
+ "<tr><td><b>Name:&nbsp;&nbsp;</b></td><td>%2</td></tr>"
+ "<tr><td><b>New Version:&nbsp;&nbsp;</b></td><td>%3</td></tr>"
+ "<tr><td><b>Current Version:&nbsp;&nbsp;</b></td><td>%4</td></tr>"
+ "<tr><td><b>Description:&nbsp;&nbsp;</b></td><td>%5</td></tr>"
+ "</table></p>")
+ .arg(strBelehrung).arg(strPackName).arg(strPackVersionNew).arg(strPackVersionOld).arg(strPackDescription),
+ 0 /* auto-confirm id */,
+ tr("&Upgrade"));
+ else if (iVerCmp < 0)
+ fRc = questionBinary(pParent, MessageType_Question,
+ tr("<p>An newer version of the extension pack is already installed, would you like to downgrade? "
+ "<p>%1</p>"
+ "<p><table cellpadding=0 cellspacing=5>"
+ "<tr><td><b>Name:&nbsp;&nbsp;</b></td><td>%2</td></tr>"
+ "<tr><td><b>New Version:&nbsp;&nbsp;</b></td><td>%3</td></tr>"
+ "<tr><td><b>Current Version:&nbsp;&nbsp;</b></td><td>%4</td></tr>"
+ "<tr><td><b>Description:&nbsp;&nbsp;</b></td><td>%5</td></tr>"
+ "</table></p>")
+ .arg(strBelehrung).arg(strPackName).arg(strPackVersionNew).arg(strPackVersionOld).arg(strPackDescription),
+ 0 /* auto-confirm id */,
+ tr("&Downgrade"));
+ else
+ fRc = questionBinary(pParent, MessageType_Question,
+ tr("<p>The extension pack is already installed with the same version, would you like reinstall it? "
+ "<p>%1</p>"
+ "<p><table cellpadding=0 cellspacing=5>"
+ "<tr><td><b>Name:&nbsp;&nbsp;</b></td><td>%2</td></tr>"
+ "<tr><td><b>Version:&nbsp;&nbsp;</b></td><td>%3</td></tr>"
+ "<tr><td><b>Description:&nbsp;&nbsp;</b></td><td>%4</td></tr>"
+ "</table></p>")
+ .arg(strBelehrung).arg(strPackName).arg(strPackVersionOld).arg(strPackDescription),
+ 0 /* auto-confirm id */,
+ tr("&Reinstall"));
+ return fRc;
+}
+
+bool UIMessageCenter::confirmRemoveExtensionPack(const QString &strPackName, QWidget *pParent /* = 0*/) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>You are about to remove the VirtualBox extension pack <b>%1</b>.</p>"
+ "<p>Are you sure you want to proceed?</p>")
+ .arg(strPackName),
+ 0 /* auto-confirm id */,
+ tr("&Remove") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmMediumRelease(const UIMedium &medium, bool fInduced, QWidget *pParent /* = 0 */) const
+{
+ /* Prepare the usage: */
+ QStringList usage;
+ CVirtualBox vbox = uiCommon().virtualBox();
+ foreach (const QUuid &uMachineID, medium.curStateMachineIds())
+ {
+ CMachine machine = vbox.FindMachine(uMachineID.toString());
+ if (!vbox.isOk() || machine.isNull())
+ continue;
+ usage << machine.GetName();
+ }
+ /* Show the question: */
+ return !fInduced
+ ? questionBinary(pParent, MessageType_Question,
+ tr("<p>Are you sure you want to release the disk image file <nobr><b>%1</b></nobr>?</p>"
+ "<p>This will detach it from the following virtual machine(s): <b>%2</b>.</p>")
+ .arg(medium.location(), usage.join(", ")),
+ 0 /* auto-confirm id */,
+ tr("Release", "detach medium"))
+ : questionBinary(pParent, MessageType_Question,
+ tr("<p>The changes you requested require this disk to "
+ "be released from the machines it is attached to.</p>"
+ "<p>Are you sure you want to release the disk image file <nobr><b>%1</b></nobr>?</p>"
+ "<p>This will detach it from the following virtual machine(s): <b>%2</b>.</p>")
+ .arg(medium.location(), usage.join(", ")),
+ 0 /* auto-confirm id */,
+ tr("Release", "detach medium"));
+}
+
+bool UIMessageCenter::confirmMediumRemoval(const UIMedium &medium, QWidget *pParent /* = 0*/) const
+{
+ /* Prepare the message: */
+ QString strMessage;
+ switch (medium.type())
+ {
+ case UIMediumDeviceType_HardDisk:
+ {
+ strMessage = tr("<p>Are you sure you want to remove the virtual hard disk "
+ "<nobr><b>%1</b></nobr> from the list of known disk image files?</p>");
+ /* Compose capabilities flag: */
+ qulonglong caps = 0;
+ QVector<KMediumFormatCapabilities> capabilities;
+ capabilities = medium.medium().GetMediumFormat().GetCapabilities();
+ for (int i = 0; i < capabilities.size(); ++i)
+ caps |= capabilities[i];
+ /* Check capabilities for additional options: */
+ if (caps & KMediumFormatCapabilities_File)
+ {
+ if (medium.state() == KMediumState_Inaccessible)
+ strMessage += tr("<p>As this hard disk is inaccessible its image file"
+ " cannot be deleted.</p>");
+ }
+ break;
+ }
+ case UIMediumDeviceType_DVD:
+ {
+ strMessage = tr("<p>Are you sure you want to remove the virtual optical disk "
+ "<nobr><b>%1</b></nobr> from the list of known disk image files?</p>");
+ strMessage += tr("<p>Note that the storage unit of this medium will not be "
+ "deleted and that it will be possible to use it later again.</p>");
+ break;
+ }
+ case UIMediumDeviceType_Floppy:
+ {
+ strMessage = tr("<p>Are you sure you want to remove the virtual floppy disk "
+ "<nobr><b>%1</b></nobr> from the list of known disk image files?</p>");
+ strMessage += tr("<p>Note that the storage unit of this medium will not be "
+ "deleted and that it will be possible to use it later again.</p>");
+ break;
+ }
+ default:
+ break;
+ }
+ /* Show the question: */
+ return questionBinary(pParent, MessageType_Question,
+ strMessage.arg(medium.location()),
+ 0 /* auto-confirm id */,
+ tr("Remove", "medium") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+int UIMessageCenter::confirmDeleteHardDiskStorage(const QString &strLocation, QWidget *pParent /* = 0*/) const
+{
+ return questionTrinary(pParent, MessageType_Question,
+ tr("<p>Do you want to delete the storage unit of the virtual hard disk "
+ "<nobr><b>%1</b></nobr>?</p>"
+ "<p>If you select <b>Delete</b> then the specified storage unit "
+ "will be permanently deleted. This operation <b>cannot be "
+ "undone</b>.</p>"
+ "<p>If you select <b>Keep</b> then the hard disk will be only "
+ "removed from the list of known hard disks, but the storage unit "
+ "will be left untouched which makes it possible to add this hard "
+ "disk to the list later again.</p>")
+ .arg(strLocation),
+ 0 /* auto-confirm id */,
+ tr("Delete", "hard disk storage"),
+ tr("Keep", "hard disk storage"));
+}
+
+bool UIMessageCenter::confirmInaccesibleMediaClear(const QStringList &mediaNameList, UIMediumDeviceType enmType, QWidget *pParent /* = 0 */)
+{
+ if (mediaNameList.isEmpty())
+ return false;
+
+ if (enmType != UIMediumDeviceType_DVD && enmType != UIMediumDeviceType_Floppy)
+ return false;
+
+ QString strDetails("<!--EOM-->");
+ QString strDetailMessage;
+
+ if (enmType == UIMediumDeviceType_DVD)
+ strDetailMessage = tr("The list of inaccessible DVDs is as follows:");
+ else
+ strDetailMessage = tr("The list of inaccessible floppy disks is as follows:");
+
+
+ if (!strDetailMessage.isEmpty())
+ strDetails.prepend(QString("<p>%1.</p>").arg(UITranslator::emphasize(strDetailMessage)));
+
+ strDetails += QString("<table bgcolor=%1 border=0 cellspacing=5 cellpadding=0 width=100%>")
+ .arg(QApplication::palette().color(QPalette::Active, QPalette::Window).name(QColor::HexRgb));
+ foreach (const QString &strDVD, mediaNameList)
+ strDetails += QString("<tr><td>%1</td></tr>").arg(strDVD);
+ strDetails += QString("</table>");
+
+ if (!strDetails.isEmpty())
+ strDetails = "<qt>" + strDetails + "</qt>";
+
+ if (enmType == UIMediumDeviceType_DVD)
+ return message(pParent,
+ MessageType_Question,
+ tr("<p>This will clear the optical disk list by releasing inaccessible DVDs"
+ " from the virtual machines they are attached to"
+ " and removing them from the list of registered media.<p>"
+ "Are you sure?"),
+ strDetails,
+ 0 /* auto-confirm id */,
+ AlertButton_Ok,
+ AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape,
+ 0 /* third button */,
+ tr("Clear") /* ok button text */,
+ QString() /* cancel button text */,
+ QString() /* 3rd button text */,
+ QString() /* help keyword */);
+ else
+ return message(pParent,
+ MessageType_Question,
+ tr("<p>This will clear the floppy disk list by releasing inaccessible disks"
+ " from the virtual machines they are attached to"
+ " and removing them from the list of registered media.<p>"
+ "Are you sure?"),
+ strDetails,
+ 0 /* auto-confirm id */,
+ AlertButton_Ok,
+ AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape,
+ 0 /* third button */,
+ tr("Clear") /* ok button text */,
+ QString() /* cancel button text */,
+ QString() /* 3rd button text */,
+ QString() /* help keyword */);
+}
+
+bool UIMessageCenter::confirmCloudNetworkRemoval(const QString &strName, QWidget *pParent /* = 0*/) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>Do you want to remove the cloud network <nobr><b>%1</b>?</nobr></p>"
+ "<p>If this network is in use by one or more virtual "
+ "machine network adapters these adapters will no longer be "
+ "usable until you correct their settings by either choosing "
+ "a different network name or a different adapter attachment "
+ "type.</p>")
+ .arg(strName),
+ 0 /* auto-confirm id */,
+ tr("Remove") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmHostNetworkInterfaceRemoval(const QString &strName, QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>Deleting this host-only network will remove "
+ "the host-only interface this network is based on. Do you want to "
+ "remove the (host-only network) interface <nobr><b>%1</b>?</nobr></p>"
+ "<p><b>Note:</b> this interface may be in use by one or more "
+ "virtual network adapters belonging to one of your VMs. "
+ "After it is removed, these adapters will no longer be usable until "
+ "you correct their settings by either choosing a different interface "
+ "name or a different adapter attachment type.</p>")
+ .arg(strName),
+ 0 /* auto-confirm id */,
+ tr("Remove") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmHostOnlyNetworkRemoval(const QString &strName, QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>Do you want to remove the host-only network <nobr><b>%1</b>?</nobr></p>"
+ "<p>If this network is in use by one or more virtual "
+ "machine network adapters these adapters will no longer be "
+ "usable until you correct their settings by either choosing "
+ "a different network name or a different adapter attachment "
+ "type.</p>")
+ .arg(strName),
+ 0 /* auto-confirm id */,
+ tr("Remove") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmNATNetworkRemoval(const QString &strName, QWidget *pParent /* = 0*/) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>Do you want to remove the NAT network <nobr><b>%1</b>?</nobr></p>"
+ "<p>If this network is in use by one or more virtual "
+ "machine network adapters these adapters will no longer be "
+ "usable until you correct their settings by either choosing "
+ "a different network name or a different adapter attachment "
+ "type.</p>")
+ .arg(strName),
+ 0 /* auto-confirm id */,
+ tr("Remove") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmCloudProfileRemoval(const QString &strName, QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>Do you want to remove the cloud profile <nobr><b>%1</b>?</nobr></p>")
+ .arg(strName),
+ 0 /* auto-confirm id */,
+ tr("Remove") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmCloudProfilesImport(QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>Do you want to import cloud profiles from external files?</p>"
+ "<p>VirtualBox cloud profiles will be overwritten and their data will be lost.</p>"),
+ 0 /* auto-confirm id */,
+ tr("Import") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+int UIMessageCenter::confirmCloudProfileManagerClosing(QWidget *pParent /* = 0 */) const
+{
+ return question(pParent, MessageType_Question,
+ tr("<p>Do you want to close the Cloud Profile Manager?</p>"
+ "<p>There seems to be an unsaved changes. "
+ "You can choose to <b>Accept</b> or <b>Reject</b> them automatically "
+ "or cancel to keep the dialog opened.</p>"),
+ 0 /* auto-confirm id */,
+ AlertButton_Choice1,
+ AlertButton_Choice2,
+ AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape,
+ tr("Accept", "cloud profile manager changes"),
+ tr("Reject", "cloud profile manager changes"));
+}
+
+bool UIMessageCenter::confirmCloudConsoleApplicationRemoval(const QString &strName, QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>Do you want to remove the cloud console application <nobr><b>%1</b>?</nobr></p>")
+ .arg(strName),
+ 0 /* auto-confirm id */,
+ tr("Remove") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmCloudConsoleProfileRemoval(const QString &strName, QWidget *pParent /* = 0 */) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("<p>Do you want to remove the cloud console profile <nobr><b>%1</b>?</nobr></p>")
+ .arg(strName),
+ 0 /* auto-confirm id */,
+ tr("Remove") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+bool UIMessageCenter::confirmLookingForGuestAdditions() const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>Could not find the <b>VirtualBox Guest Additions</b> disk image file.</p>"
+ "<p>Do you wish to download this disk image file from the Internet?</p>"),
+ 0 /* auto-confirm id */,
+ tr("Download"));
+}
+
+bool UIMessageCenter::confirmDownloadGuestAdditions(const QString &strUrl, qulonglong uSize) const
+{
+ return questionBinary(windowManager().mainWindowShown(), MessageType_Question,
+ tr("<p>Are you sure you want to download the <b>VirtualBox Guest Additions</b> disk image file "
+ "from <nobr><a href=\"%1\">%1</a></nobr> (size %2 bytes)?</p>")
+ .arg(strUrl, QLocale(UITranslator::languageId()).toString(uSize)),
+ 0 /* auto-confirm id */,
+ tr("Download"));
+}
+
+void UIMessageCenter::cannotSaveGuestAdditions(const QString &strURL, const QString &strTarget) const
+{
+ alert(windowManager().mainWindowShown(), MessageType_Error,
+ tr("<p>The <b>VirtualBox Guest Additions</b> disk image file has been successfully downloaded "
+ "from <nobr><a href=\"%1\">%1</a></nobr> "
+ "but can't be saved locally as <nobr><b>%2</b>.</nobr></p>"
+ "<p>Please choose another location for that file.</p>")
+ .arg(strURL, strTarget));
+}
+
+bool UIMessageCenter::proposeMountGuestAdditions(const QString &strUrl, const QString &strSrc) const
+{
+ return questionBinary(windowManager().mainWindowShown(), MessageType_Question,
+ tr("<p>The <b>VirtualBox Guest Additions</b> disk image file has been successfully downloaded "
+ "from <nobr><a href=\"%1\">%1</a></nobr> "
+ "and saved locally as <nobr><b>%2</b>.</nobr></p>"
+ "<p>Do you wish to register this disk image file and insert it into the virtual optical drive?</p>")
+ .arg(strUrl, strSrc),
+ 0 /* auto-confirm id */,
+ tr("Insert", "additions"));
+}
+
+bool UIMessageCenter::confirmLookingForUserManual(const QString &strMissedLocation) const
+{
+ return questionBinary(0, MessageType_Question,
+ tr("<p>Could not find the <b>VirtualBox User Manual</b> <nobr><b>%1</b>.</nobr></p>"
+ "<p>Do you wish to download this file from the Internet?</p>")
+ .arg(strMissedLocation),
+ 0 /* auto-confirm id */,
+ tr("Download"));
+}
+
+bool UIMessageCenter::confirmDownloadUserManual(const QString &strURL, qulonglong uSize) const
+{
+ return questionBinary(windowManager().mainWindowShown(), MessageType_Question,
+ tr("<p>Are you sure you want to download the <b>VirtualBox User Manual</b> "
+ "from <nobr><a href=\"%1\">%1</a></nobr> (size %2 bytes)?</p>")
+ .arg(strURL, QLocale(UITranslator::languageId()).toString(uSize)),
+ 0 /* auto-confirm id */,
+ tr("Download"));
+}
+
+void UIMessageCenter::cannotSaveUserManual(const QString &strURL, const QString &strTarget) const
+{
+ alert(windowManager().mainWindowShown(), MessageType_Error,
+ tr("<p>The VirtualBox User Manual has been successfully downloaded "
+ "from <nobr><a href=\"%1\">%1</a></nobr> "
+ "but can't be saved locally as <nobr><b>%2</b>.</nobr></p>"
+ "<p>Please choose another location for that file.</p>")
+ .arg(strURL, strTarget));
+}
+
+bool UIMessageCenter::confirmLookingForExtensionPack(const QString &strExtPackName, const QString &strExtPackVersion) const
+{
+ return questionBinary(windowManager().mainWindowShown(), MessageType_Question,
+ tr("<p>You have an old version (%1) of the <b><nobr>%2</nobr></b> installed.</p>"
+ "<p>Do you wish to download latest one from the Internet?</p>")
+ .arg(strExtPackVersion).arg(strExtPackName),
+ 0 /* auto-confirm id */,
+ tr("Download"));
+}
+
+bool UIMessageCenter::confirmDownloadExtensionPack(const QString &strExtPackName, const QString &strURL, qulonglong uSize) const
+{
+ return questionBinary(windowManager().mainWindowShown(), MessageType_Question,
+ tr("<p>Are you sure you want to download the <b><nobr>%1</nobr></b> "
+ "from <nobr><a href=\"%2\">%2</a></nobr> (size %3 bytes)?</p>")
+ .arg(strExtPackName, strURL, QLocale(UITranslator::languageId()).toString(uSize)),
+ 0 /* auto-confirm id */,
+ tr("Download"));
+}
+
+void UIMessageCenter::cannotSaveExtensionPack(const QString &strExtPackName, const QString &strFrom, const QString &strTo) const
+{
+ alert(windowManager().mainWindowShown(), MessageType_Error,
+ tr("<p>The <b><nobr>%1</nobr></b> has been successfully downloaded "
+ "from <nobr><a href=\"%2\">%2</a></nobr> "
+ "but can't be saved locally as <nobr><b>%3</b>.</nobr></p>"
+ "<p>Please choose another location for that file.</p>")
+ .arg(strExtPackName, strFrom, strTo));
+}
+
+bool UIMessageCenter::proposeInstallExtentionPack(const QString &strExtPackName, const QString &strFrom, const QString &strTo) const
+{
+ return questionBinary(windowManager().mainWindowShown(), MessageType_Question,
+ tr("<p>The <b><nobr>%1</nobr></b> has been successfully downloaded "
+ "from <nobr><a href=\"%2\">%2</a></nobr> "
+ "and saved locally as <nobr><b>%3</b>.</nobr></p>"
+ "<p>Do you wish to install this extension pack?</p>")
+ .arg(strExtPackName, strFrom, strTo),
+ 0 /* auto-confirm id */,
+ tr("Install", "extension pack"));
+}
+
+bool UIMessageCenter::proposeDeleteExtentionPack(const QString &strTo) const
+{
+ return questionBinary(windowManager().mainWindowShown(), MessageType_Question,
+ tr("Do you want to delete the downloaded file <nobr><b>%1</b></nobr>?")
+ .arg(strTo),
+ 0 /* auto-confirm id */,
+ tr("Delete", "extension pack"));
+}
+
+bool UIMessageCenter::proposeDeleteOldExtentionPacks(const QStringList &strFiles) const
+{
+ return questionBinary(windowManager().mainWindowShown(), MessageType_Question,
+ tr("Do you want to delete following list of files <nobr><b>%1</b></nobr>?")
+ .arg(strFiles.join(",")),
+ 0 /* auto-confirm id */,
+ tr("Delete", "extension pack"));
+}
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+bool UIMessageCenter::cannotRestoreSnapshot(const CMachine &machine, const QString &strSnapshotName, const QString &strMachineName) const
+{
+ error(0, MessageType_Error,
+ tr("Failed to restore the snapshot <b>%1</b> of the virtual machine <b>%2</b>.")
+ .arg(strSnapshotName, strMachineName),
+ UIErrorString::formatErrorInfo(machine));
+ return false;
+}
+
+bool UIMessageCenter::cannotRestoreSnapshot(const CProgress &progress, const QString &strSnapshotName, const QString &strMachineName) const
+{
+ error(0, MessageType_Error,
+ tr("Failed to restore the snapshot <b>%1</b> of the virtual machine <b>%2</b>.")
+ .arg(strSnapshotName, strMachineName),
+ UIErrorString::formatErrorInfo(progress));
+ return false;
+}
+
+void UIMessageCenter::cannotStartMachine(const CConsole &console, const QString &strName) const
+{
+ error(0, MessageType_Error,
+ tr("Failed to start the virtual machine <b>%1</b>.")
+ .arg(strName),
+ UIErrorString::formatErrorInfo(console));
+}
+
+void UIMessageCenter::cannotStartMachine(const CProgress &progress, const QString &strName) const
+{
+ error(0, MessageType_Error,
+ tr("Failed to start the virtual machine <b>%1</b>.")
+ .arg(strName),
+ UIErrorString::formatErrorInfo(progress));
+}
+
+bool UIMessageCenter::warnAboutNetworkInterfaceNotFound(const QString &strMachineName, const QString &strIfNames) const
+{
+ return questionBinary(0, MessageType_Error,
+ tr("<p>Could not start the machine <b>%1</b> because the following "
+ "physical network interfaces were not found:</p><p><b>%2</b></p>"
+ "<p>You can either change the machine's network settings or stop the machine.</p>")
+ .arg(strMachineName, strIfNames),
+ 0 /* auto-confirm id */,
+ tr("Change Network Settings"), tr("Close VM"));
+}
+
+void UIMessageCenter::warnAboutVBoxSVCUnavailable() const
+{
+ alert(0, MessageType_Critical,
+ tr("<p>A critical error has occurred while running the virtual "
+ "machine and the machine execution should be stopped.</p>"
+ ""
+ "<p>For help, please see the Community section on "
+ "<a href=https://www.virtualbox.org>https://www.virtualbox.org</a> "
+ "or your support contract. Please provide the contents of the "
+ "log file <tt>VBox.log</tt>, "
+ "which you can find in the virtual machine log directory, "
+ "as well as a description of what you were doing when this error happened. "
+ ""
+ "Note that you can also access the above file by selecting <b>Show Log</b> "
+ "from the <b>Machine</b> menu of the main VirtualBox window.</p>"
+ ""
+ "<p>Press <b>OK</b> to power off the machine.</p>"),
+ 0 /* auto-confirm id */);
+}
+
+bool UIMessageCenter::warnAboutGuruMeditation(const QString &strLogFolder)
+{
+ return questionBinary(0, MessageType_GuruMeditation,
+ tr("<p>A critical error has occurred while running the virtual "
+ "machine and the machine execution has been stopped.</p>"
+ ""
+ "<p>For help, please see the Community section on "
+ "<a href=https://www.virtualbox.org>https://www.virtualbox.org</a> "
+ "or your support contract. Please provide the contents of the "
+ "log file <tt>VBox.log</tt> and the image file <tt>VBox.png</tt>, "
+ "which you can find in the <nobr><b>%1</b></nobr> directory, "
+ "as well as a description of what you were doing when this error happened. "
+ ""
+ "Note that you can also access the above files by selecting <b>Show Log</b> "
+ "from the <b>Machine</b> menu of the main VirtualBox window.</p>"
+ ""
+ "<p>Press <b>OK</b> if you want to power off the machine "
+ "or press <b>Ignore</b> if you want to leave it as is for debugging. "
+ "Please note that debugging requires special knowledge and tools, "
+ "so it is recommended to press <b>OK</b> now.</p>")
+ .arg(strLogFolder),
+ 0 /* auto-confirm id */,
+ QIMessageBox::tr("OK"),
+ tr("Ignore"));
+}
+
+void UIMessageCenter::showRuntimeError(const CConsole &console, bool fFatal, const QString &strErrorId, const QString &strErrorMsg) const
+{
+ /* Prepare auto-confirm id: */
+ QByteArray autoConfimId = "showRuntimeError.";
+
+ /* Prepare variables: */
+ CConsole console1 = console;
+ KMachineState state = console1.GetState();
+ MessageType enmType;
+ QString severity;
+
+ /// @todo Move to Runtime UI!
+ /* Preprocessing: */
+ if (fFatal)
+ {
+ /* The machine must be paused on fFatal errors: */
+ Assert(state == KMachineState_Paused);
+ if (state != KMachineState_Paused)
+ console1.Pause();
+ }
+
+ /* Compose type, severity, advance confirm id: */
+ if (fFatal)
+ {
+ enmType = MessageType_Critical;
+ severity = tr("<nobr>Fatal Error</nobr>", "runtime error info");
+ autoConfimId += "fatal.";
+ }
+ else if (state == KMachineState_Paused)
+ {
+ enmType = MessageType_Error;
+ severity = tr("<nobr>Non-Fatal Error</nobr>", "runtime error info");
+ autoConfimId += "error.";
+ }
+ else
+ {
+ enmType = MessageType_Warning;
+ severity = tr("<nobr>Warning</nobr>", "runtime error info");
+ autoConfimId += "warning.";
+ }
+ /* Advance auto-confirm id: */
+ autoConfimId += strErrorId.toUtf8();
+
+ /* Format error-details: */
+ QString formatted("<!--EOM-->");
+ if (!strErrorMsg.isEmpty())
+ formatted.prepend(QString("<p>%1.</p>").arg(UITranslator::emphasize(strErrorMsg)));
+ if (!strErrorId.isEmpty())
+ formatted += QString("<table bgcolor=%1 border=0 cellspacing=5 "
+ "cellpadding=0 width=100%>"
+ "<tr><td>%2</td><td>%3</td></tr>"
+ "<tr><td>%4</td><td>%5</td></tr>"
+ "</table>")
+ .arg(QApplication::palette().color(QPalette::Active, QPalette::Window).name(QColor::HexRgb))
+ .arg(tr("<nobr>Error ID:</nobr>", "runtime error info"), strErrorId)
+ .arg(tr("Severity:", "runtime error info"), severity);
+ if (!formatted.isEmpty())
+ formatted = "<qt>" + formatted + "</qt>";
+
+ /* Show the error: */
+ if (enmType == MessageType_Critical)
+ {
+ error(0, enmType,
+ tr("<p>A fatal error has occurred during virtual machine execution! "
+ "The virtual machine will be powered off. Please copy the following error message "
+ "using the clipboard to help diagnose the problem:</p>"),
+ formatted, autoConfimId.data());
+ }
+ else if (enmType == MessageType_Error)
+ {
+ error(0, enmType,
+ tr("<p>An error has occurred during virtual machine execution! "
+ "The error details are shown below. You may try to correct the error "
+ "and resume the virtual machine execution.</p>"),
+ formatted, autoConfimId.data());
+ }
+ else
+ {
+ /** @todo r=bird: This is a very annoying message as it refers to invisible text
+ * below. User have to expand "Details" to see what actually went wrong.
+ * Probably a good idea to check strErrorId and see if we can come up with better
+ * messages here, at least for common stuff like DvdOrFloppyImageInaccesssible... */
+ error(0, enmType,
+ tr("<p>The virtual machine execution ran into a non-fatal problem as described below. "
+ "We suggest that you take appropriate action to prevent the problem from recurring.</p>"),
+ formatted, autoConfimId.data());
+ }
+
+ /// @todo Move to Runtime UI!
+ /* Postprocessing: */
+ if (fFatal)
+ {
+ /* Power off after a fFatal error: */
+ LogRel(("GUI: Powering VM off after a fatal runtime error...\n"));
+ console1.PowerDown();
+ }
+}
+
+bool UIMessageCenter::confirmInputCapture(bool &fAutoConfirmed) const
+{
+ int rc = question(0, MessageType_Info,
+ tr("<p>You have <b>clicked the mouse</b> inside the Virtual Machine display or pressed the <b>host key</b>. "
+ "This will cause the Virtual Machine to <b>capture</b> the host mouse pointer (only if the mouse pointer "
+ "integration is not currently supported by the guest OS) and the keyboard, which will make them "
+ "unavailable to other applications running on your host machine.</p>"
+ "<p>You can press the <b>host key</b> at any time to <b>uncapture</b> the keyboard and mouse "
+ "(if it is captured) and return them to normal operation. "
+ "The currently assigned host key is shown on the status bar at the bottom of the Virtual Machine window, "
+ "next to the&nbsp;<img src=:/hostkey_16px.png/>&nbsp;icon. "
+ "This icon, together with the mouse icon placed nearby, indicate the current keyboard and mouse capture state.</p>") +
+ tr("<p>The host key is currently defined as <b>%1</b>.</p>", "additional message box paragraph")
+ .arg(UIHostCombo::toReadableString(gEDataManager->hostKeyCombination())),
+ "confirmInputCapture",
+ AlertButton_Ok | AlertButtonOption_Default,
+ AlertButton_Cancel | AlertButtonOption_Escape,
+ 0,
+ tr("Capture", "do input capture"));
+ /* Was the message auto-confirmed? */
+ fAutoConfirmed = (rc & AlertOption_AutoConfirmed);
+ /* True if "Ok" was pressed: */
+ return (rc & AlertButtonMask) == AlertButton_Ok;
+}
+
+bool UIMessageCenter::confirmGoingFullscreen(const QString &strHotKey) const
+{
+ return questionBinary(0, MessageType_Info,
+ tr("<p>The virtual machine window will be now switched to <b>full-screen</b> mode. "
+ "You can go back to windowed mode at any time by pressing <b>%1</b>.</p>"
+ "<p>Note that the <i>Host</i> key is currently defined as <b>%2</b>.</p>"
+ "<p>Note that the main menu bar is hidden in full-screen mode. "
+ "You can access it by pressing <b>Host+Home</b>.</p>")
+ .arg(strHotKey, UIHostCombo::toReadableString(gEDataManager->hostKeyCombination())),
+ "confirmGoingFullscreen",
+ tr("Switch"));
+}
+
+bool UIMessageCenter::confirmGoingSeamless(const QString &strHotKey) const
+{
+ return questionBinary(0, MessageType_Info,
+ tr("<p>The virtual machine window will be now switched to <b>Seamless</b> mode. "
+ "You can go back to windowed mode at any time by pressing <b>%1</b>.</p>"
+ "<p>Note that the <i>Host</i> key is currently defined as <b>%2</b>.</p>"
+ "<p>Note that the main menu bar is hidden in seamless mode. "
+ "You can access it by pressing <b>Host+Home</b>.</p>")
+ .arg(strHotKey, UIHostCombo::toReadableString(gEDataManager->hostKeyCombination())),
+ "confirmGoingSeamless",
+ tr("Switch"));
+}
+
+bool UIMessageCenter::confirmGoingScale(const QString &strHotKey) const
+{
+ return questionBinary(0, MessageType_Info,
+ tr("<p>The virtual machine window will be now switched to <b>Scale</b> mode. "
+ "You can go back to windowed mode at any time by pressing <b>%1</b>.</p>"
+ "<p>Note that the <i>Host</i> key is currently defined as <b>%2</b>.</p>"
+ "<p>Note that the main menu bar is hidden in scaled mode. "
+ "You can access it by pressing <b>Host+Home</b>.</p>")
+ .arg(strHotKey, UIHostCombo::toReadableString(gEDataManager->hostKeyCombination())),
+ "confirmGoingScale",
+ tr("Switch"));
+}
+
+bool UIMessageCenter::cannotEnterFullscreenMode(ULONG /* uWidth */, ULONG /* uHeight */, ULONG /* uBpp */, ULONG64 uMinVRAM) const
+{
+ return questionBinary(0, MessageType_Warning,
+ tr("<p>Could not switch the guest display to full-screen mode due to insufficient guest video memory.</p>"
+ "<p>You should configure the virtual machine to have at least <b>%1</b> of video memory.</p>"
+ "<p>Press <b>Ignore</b> to switch to full-screen mode anyway or press <b>Cancel</b> to cancel the operation.</p>")
+ .arg(UITranslator::formatSize(uMinVRAM)),
+ 0 /* auto-confirm id */,
+ tr("Ignore"));
+}
+
+void UIMessageCenter::cannotEnterSeamlessMode(ULONG /* uWidth */, ULONG /* uHeight */, ULONG /* uBpp */, ULONG64 uMinVRAM) const
+{
+ alert(0, MessageType_Error,
+ tr("<p>Could not enter seamless mode due to insufficient guest "
+ "video memory.</p>"
+ "<p>You should configure the virtual machine to have at "
+ "least <b>%1</b> of video memory.</p>")
+ .arg(UITranslator::formatSize(uMinVRAM)));
+}
+
+bool UIMessageCenter::cannotSwitchScreenInFullscreen(quint64 uMinVRAM) const
+{
+ return questionBinary(0, MessageType_Warning,
+ tr("<p>Could not change the guest screen to this host screen due to insufficient guest video memory.</p>"
+ "<p>You should configure the virtual machine to have at least <b>%1</b> of video memory.</p>"
+ "<p>Press <b>Ignore</b> to switch the screen anyway or press <b>Cancel</b> to cancel the operation.</p>")
+ .arg(UITranslator::formatSize(uMinVRAM)),
+ 0 /* auto-confirm id */,
+ tr("Ignore"));
+}
+
+void UIMessageCenter::cannotSwitchScreenInSeamless(quint64 uMinVRAM) const
+{
+ alert(0, MessageType_Error,
+ tr("<p>Could not change the guest screen to this host screen "
+ "due to insufficient guest video memory.</p>"
+ "<p>You should configure the virtual machine to have at "
+ "least <b>%1</b> of video memory.</p>")
+ .arg(UITranslator::formatSize(uMinVRAM)));
+}
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+void UIMessageCenter::cannotDropDataToGuest(const CDnDTarget &dndTarget, QWidget *pParent /* = 0 */) const
+{
+ error(pParent, MessageType_Error,
+ tr("Drag and drop operation from host to guest failed."),
+ UIErrorString::formatErrorInfo(dndTarget));
+}
+
+void UIMessageCenter::cannotDropDataToGuest(const CProgress &progress, QWidget *pParent /* = 0 */) const
+{
+ error(pParent, MessageType_Error,
+ tr("Drag and drop operation from host to guest failed."),
+ UIErrorString::formatErrorInfo(progress));
+}
+
+void UIMessageCenter::cannotDropDataToHost(const CDnDSource &dndSource, QWidget *pParent /* = 0 */) const
+{
+ error(pParent, MessageType_Error,
+ tr("Drag and drop operation from guest to host failed."),
+ UIErrorString::formatErrorInfo(dndSource));
+}
+
+void UIMessageCenter::cannotDropDataToHost(const CProgress &progress, QWidget *pParent /* = 0 */) const
+{
+ error(pParent, MessageType_Error,
+ tr("Drag and drop operation from guest to host failed."),
+ UIErrorString::formatErrorInfo(progress));
+}
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+
+bool UIMessageCenter::confirmHardDisklessMachine(QWidget *pParent /* = 0*/) const
+{
+ return questionBinary(pParent, MessageType_Warning,
+ tr("You are about to create a new virtual machine without a hard disk. "
+ "You will not be able to install an operating system on the machine "
+ "until you add one. In the mean time you will only be able to start the "
+ "machine using a virtual optical disk or from the network."),
+ 0 /* auto-confirm id */,
+ tr("Continue", "no hard disk attached"),
+ tr("Go Back", "no hard disk attached"));
+}
+
+bool UIMessageCenter::confirmExportMachinesInSaveState(const QStringList &machineNames, QWidget *pParent /* = 0*/) const
+{
+ return questionBinary(pParent, MessageType_Warning,
+ tr("<p>The %n following virtual machine(s) are currently in a saved state: <b>%1</b></p>"
+ "<p>If you continue the runtime state of the exported machine(s) will be discarded. "
+ "The other machine(s) will not be changed.</p>",
+ "This text is never used with n == 0. Feel free to drop the %n where possible, "
+ "we only included it because of problems with Qt Linguist (but the user can see "
+ "how many machines are in the list and doesn't need to be told).", machineNames.size())
+ .arg(machineNames.join(", ")),
+ 0 /* auto-confirm id */,
+ tr("Continue"));
+}
+
+bool UIMessageCenter::confirmOverridingFile(const QString &strPath, QWidget *pParent /* = 0*/) const
+{
+ return questionBinary(pParent, MessageType_Question,
+ tr("A file named <b>%1</b> already exists. "
+ "Are you sure you want to replace it?<br /><br />"
+ "Replacing it will overwrite its contents.")
+ .arg(strPath),
+ 0 /* auto-confirm id */,
+ QString() /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+}
+
+bool UIMessageCenter::confirmOverridingFiles(const QVector<QString> &strPaths, QWidget *pParent /* = 0*/) const
+{
+ /* If it is only one file use the single question versions above: */
+ if (strPaths.size() == 1)
+ return confirmOverridingFile(strPaths.at(0), pParent);
+ else if (strPaths.size() > 1)
+ return questionBinary(pParent, MessageType_Question,
+ tr("The following files already exist:<br /><br />%1<br /><br />"
+ "Are you sure you want to replace them? "
+ "Replacing them will overwrite their contents.")
+ .arg(QStringList(strPaths.toList()).join("<br />")),
+ 0 /* auto-confirm id */,
+ QString() /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */);
+ else
+ return true;
+}
+
+void UIMessageCenter::cannotCreateMediumStorage(const CVirtualBox &comVBox, const QString &strLocation, QWidget *pParent /* = 0 */) const
+{
+ error(pParent, MessageType_Error,
+ tr("Failed to create the virtual disk image storage <nobr><b>%1</b>.</nobr>")
+ .arg(strLocation),
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+void UIMessageCenter::sltShowHelpWebDialog()
+{
+ uiCommon().openURL("https://www.virtualbox.org");
+}
+
+void UIMessageCenter::sltShowBugTracker()
+{
+ uiCommon().openURL("https://www.virtualbox.org/wiki/Bugtracker");
+}
+
+void UIMessageCenter::sltShowForums()
+{
+ uiCommon().openURL("https://forums.virtualbox.org/");
+}
+
+void UIMessageCenter::sltShowOracle()
+{
+ uiCommon().openURL("https://www.oracle.com/us/technologies/virtualization/virtualbox/overview/index.html");
+}
+
+void UIMessageCenter::sltShowOnlineDocumentation()
+{
+ uiCommon().openURL("https://docs.oracle.com/en/virtualization/virtualbox/7.0/user/index.html");
+}
+
+void UIMessageCenter::sltShowHelpAboutDialog()
+{
+ CVirtualBox vbox = uiCommon().virtualBox();
+ QString strFullVersion;
+ if (uiCommon().brandingIsActive())
+ {
+ strFullVersion = QString("%1 r%2 - %3").arg(vbox.GetVersion())
+ .arg(vbox.GetRevision())
+ .arg(uiCommon().brandingGetKey("Name"));
+ }
+ else
+ {
+ strFullVersion = QString("%1 r%2").arg(vbox.GetVersion())
+ .arg(vbox.GetRevision());
+ }
+ AssertWrapperOk(vbox);
+
+ (new VBoxAboutDlg(windowManager().mainWindowShown(), strFullVersion))->show();
+}
+
+void UIMessageCenter::sltShowHelpHelpDialog()
+{
+ /* Currently I am sure how this logic should be changed. I will just disable it for now: */
+ sltShowUserManual(uiCommon().helpFile());
+#if 0
+#ifndef VBOX_OSE
+ /* For non-OSE version we just open it: */
+ sltShowUserManual(uiCommon().helpFile());
+#else /* #ifndef VBOX_OSE */
+ /* For OSE version we have to check if it present first: */
+ QString strUserManualFileName1 = uiCommon().helpFile();
+ QString strShortFileName = QFileInfo(strUserManualFileName1).fileName();
+ QString strUserManualFileName2 = QDir(uiCommon().homeFolder()).absoluteFilePath(strShortFileName);
+ /* Show if user manual already present: */
+ if (QFile::exists(strUserManualFileName1))
+ sltShowUserManual(strUserManualFileName1);
+ else if (QFile::exists(strUserManualFileName2))
+ sltShowUserManual(strUserManualFileName2);
+# ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* If downloader is running already: */
+ else if (UINotificationDownloaderUserManual::exists())
+ gpNotificationCenter->invoke();
+ /* Else propose to download user manual: */
+ else if (confirmLookingForUserManual(strUserManualFileName1))
+ {
+ /* Download user manual: */
+ UINotificationDownloaderUserManual *pNotification = UINotificationDownloaderUserManual::instance(UICommon::helpFile());
+ /* After downloading finished => show User Manual: */
+ connect(pNotification, &UINotificationDownloaderUserManual::sigUserManualDownloaded,
+ this, &UIMessageCenter::sltShowUserManual);
+ /* Append and start notification: */
+ gpNotificationCenter->append(pNotification);
+ }
+# endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+#endif /* #ifdef VBOX_OSE */
+#endif
+}
+
+void UIMessageCenter::sltResetSuppressedMessages()
+{
+ /* Nullify suppressed message list: */
+ gEDataManager->setSuppressedMessages(QStringList());
+}
+
+void UIMessageCenter::sltShowUserManual(const QString &strLocation)
+{
+ Q_UNUSED(strLocation);
+#if defined (VBOX_WITH_QHELP_VIEWER)
+ showHelpBrowser(strLocation);
+#else
+ #if defined (VBOX_WS_WIN)
+ HtmlHelp(GetDesktopWindow(), strLocation.utf16(), HH_DISPLAY_TOPIC, NULL);
+ #endif
+
+ #if !defined(VBOX_OSE)
+ char szViewerPath[RTPATH_MAX];
+ int rc;
+ rc = RTPathAppPrivateArch(szViewerPath, sizeof(szViewerPath));
+ AssertRC(rc);
+ QProcess::startDetached(QString(szViewerPath) + "/kchmviewer", QStringList(strLocation));
+ # else /* #ifndef VBOX_OSE */
+ uiCommon().openURL("file://" + strLocation);
+ # endif /* #ifdef VBOX_OSE */
+ #if defined (VBOX_WS_MAC)
+ uiCommon().openURL("file://" + strLocation);
+ #endif
+#endif
+}
+
+void UIMessageCenter::sltHelpBrowserClosed()
+{
+ m_pHelpBrowserDialog = 0;
+}
+
+void UIMessageCenter::sltHandleHelpRequest()
+{
+#if defined(VBOX_WITH_QHELP_VIEWER)
+ sltHandleHelpRequestWithKeyword(uiCommon().helpKeyword(sender()));
+#endif /* #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))&& (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) */
+}
+
+void UIMessageCenter::sltHandleHelpRequestWithKeyword(const QString &strHelpKeyword)
+{
+#if defined(VBOX_WITH_QHELP_VIEWER)
+ /* First open or show the help browser: */
+ showHelpBrowser(uiCommon().helpFile());
+ /* Show the help page for the @p strHelpKeyword: */
+ if (m_pHelpBrowserDialog)
+ m_pHelpBrowserDialog->showHelpForKeyword(strHelpKeyword);
+#else
+ Q_UNUSED(strHelpKeyword);
+# endif /* #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))&& (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) */
+}
+
+void UIMessageCenter::sltShowMessageBox(QWidget *pParent, MessageType enmType,
+ const QString &strMessage, const QString &strDetails,
+ int iButton1, int iButton2, int iButton3,
+ const QString &strButtonText1, const QString &strButtonText2, const QString &strButtonText3,
+ const QString &strAutoConfirmId, const QString &strHelpKeyword) const
+{
+ /* Now we can show a message-box directly: */
+ showMessageBox(pParent, enmType,
+ strMessage, strDetails,
+ iButton1, iButton2, iButton3,
+ strButtonText1, strButtonText2, strButtonText3,
+ strAutoConfirmId, strHelpKeyword);
+}
+
+UIMessageCenter::UIMessageCenter()
+ : m_pHelpBrowserDialog(0)
+{
+ /* Assign instance: */
+ s_pInstance = this;
+}
+
+UIMessageCenter::~UIMessageCenter()
+{
+ /* Unassign instance: */
+ s_pInstance = 0;
+}
+
+void UIMessageCenter::prepare()
+{
+ /* Register required objects as meta-types: */
+ qRegisterMetaType<CProgress>();
+ qRegisterMetaType<CHost>();
+ qRegisterMetaType<CMachine>();
+ qRegisterMetaType<CConsole>();
+ qRegisterMetaType<CHostNetworkInterface>();
+ qRegisterMetaType<UIMediumDeviceType>();
+ qRegisterMetaType<StorageSlot>();
+
+ /* Prepare interthread connection: */
+ qRegisterMetaType<MessageType>();
+ // Won't go until we are supporting C++11 or at least variadic templates everywhere.
+ // connect(this, &UIMessageCenter::sigToShowMessageBox,
+ // this, &UIMessageCenter::sltShowMessageBox,
+ connect(this, SIGNAL(sigToShowMessageBox(QWidget*, MessageType,
+ const QString&, const QString&,
+ int, int, int,
+ const QString&, const QString&, const QString&,
+ const QString&, const QString&)),
+ this, SLOT(sltShowMessageBox(QWidget*, MessageType,
+ const QString&, const QString&,
+ int, int, int,
+ const QString&, const QString&, const QString&,
+ const QString&, const QString&)),
+ Qt::BlockingQueuedConnection);
+
+ /* Translations for Main.
+ * Please make sure they corresponds to the strings coming from Main one-by-one symbol! */
+ tr("Could not load the Host USB Proxy Service (VERR_FILE_NOT_FOUND). The service might not be installed on the host computer");
+ tr("VirtualBox is not currently allowed to access USB devices. You can change this by adding your user to the 'vboxusers' group. Please see the user manual for a more detailed explanation");
+ tr("VirtualBox is not currently allowed to access USB devices. You can change this by allowing your user to access the 'usbfs' folder and files. Please see the user manual for a more detailed explanation");
+ tr("The USB Proxy Service has not yet been ported to this host");
+ tr("Could not load the Host USB Proxy service");
+}
+
+void UIMessageCenter::cleanup()
+{
+ /* Nothing for now... */
+}
+
+int UIMessageCenter::showMessageBox(QWidget *pParent, MessageType enmType,
+ const QString &strMessage, const QString &strDetails,
+ int iButton1, int iButton2, int iButton3,
+ const QString &strButtonText1, const QString &strButtonText2, const QString &strButtonText3,
+ const QString &strAutoConfirmId, const QString &strHelpKeyword) const
+{
+ /* Choose the 'default' button: */
+ if (iButton1 == 0 && iButton2 == 0 && iButton3 == 0)
+ iButton1 = AlertButton_Ok | AlertButtonOption_Default;
+
+ /* Check if message-box was auto-confirmed before: */
+ QStringList confirmedMessageList;
+ if (!strAutoConfirmId.isEmpty())
+ {
+ const QUuid uID = uiCommon().uiType() == UICommon::UIType_RuntimeUI
+ ? uiCommon().managedVMUuid()
+ : UIExtraDataManager::GlobalID;
+ confirmedMessageList = gEDataManager->suppressedMessages(uID);
+ if ( confirmedMessageList.contains(strAutoConfirmId)
+ || confirmedMessageList.contains("allMessageBoxes")
+ || confirmedMessageList.contains("all") )
+ {
+ int iResultCode = AlertOption_AutoConfirmed;
+ if (iButton1 & AlertButtonOption_Default)
+ iResultCode |= (iButton1 & AlertButtonMask);
+ if (iButton2 & AlertButtonOption_Default)
+ iResultCode |= (iButton2 & AlertButtonMask);
+ if (iButton3 & AlertButtonOption_Default)
+ iResultCode |= (iButton3 & AlertButtonMask);
+ return iResultCode;
+ }
+ }
+
+ /* Choose title and icon: */
+ QString title;
+ AlertIconType icon;
+ switch (enmType)
+ {
+ default:
+ case MessageType_Info:
+ title = tr("VirtualBox - Information", "msg box title");
+ icon = AlertIconType_Information;
+ break;
+ case MessageType_Question:
+ title = tr("VirtualBox - Question", "msg box title");
+ icon = AlertIconType_Question;
+ break;
+ case MessageType_Warning:
+ title = tr("VirtualBox - Warning", "msg box title");
+ icon = AlertIconType_Warning;
+ break;
+ case MessageType_Error:
+ title = tr("VirtualBox - Error", "msg box title");
+ icon = AlertIconType_Critical;
+ break;
+ case MessageType_Critical:
+ title = tr("VirtualBox - Critical Error", "msg box title");
+ icon = AlertIconType_Critical;
+ break;
+ case MessageType_GuruMeditation:
+ title = "VirtualBox - Guru Meditation"; /* don't translate this */
+ icon = AlertIconType_GuruMeditation;
+ break;
+ }
+
+ /* Create message-box: */
+ QWidget *pMessageBoxParent = windowManager().realParentWindow(pParent ? pParent : windowManager().mainWindowShown());
+ QPointer<QIMessageBox> pMessageBox = new QIMessageBox(title, strMessage, icon,
+ iButton1, iButton2, iButton3,
+ pMessageBoxParent, strHelpKeyword);
+ windowManager().registerNewParent(pMessageBox, pMessageBoxParent);
+
+ /* Prepare auto-confirmation check-box: */
+ if (!strAutoConfirmId.isEmpty())
+ {
+ pMessageBox->setFlagText(tr("Do not show this message again", "msg box flag"));
+ pMessageBox->setFlagChecked(false);
+ }
+
+ /* Configure details: */
+ if (!strDetails.isEmpty())
+ pMessageBox->setDetailsText(strDetails);
+
+ /* Configure button-text: */
+ if (!strButtonText1.isNull())
+ pMessageBox->setButtonText(0, strButtonText1);
+ if (!strButtonText2.isNull())
+ pMessageBox->setButtonText(1, strButtonText2);
+ if (!strButtonText3.isNull())
+ pMessageBox->setButtonText(2, strButtonText3);
+
+ /* Show message-box: */
+ int iResultCode = pMessageBox->exec();
+
+ /* Make sure message-box still valid: */
+ if (!pMessageBox)
+ return iResultCode;
+
+ /* Remember auto-confirmation check-box value: */
+ if (!strAutoConfirmId.isEmpty())
+ {
+ if (pMessageBox->flagChecked())
+ {
+ confirmedMessageList << strAutoConfirmId;
+ gEDataManager->setSuppressedMessages(confirmedMessageList);
+ }
+ }
+
+ /* Delete message-box: */
+ delete pMessageBox;
+
+ /* Return result-code: */
+ return iResultCode;
+}
+
+void UIMessageCenter::showHelpBrowser(const QString &strHelpFilePath, QWidget *pParent /* = 0 */)
+{
+ Q_UNUSED(pParent);
+#if defined(VBOX_WITH_QHELP_VIEWER)
+ if (!QFileInfo(strHelpFilePath).exists())
+ {
+ UINotificationMessage::cannotFindHelpFile(strHelpFilePath);
+ return;
+ }
+ if (!m_pHelpBrowserDialog)
+ {
+ m_pHelpBrowserDialog = new UIHelpBrowserDialog(0 /* parent */, 0 /* Center Widget */, strHelpFilePath);
+ AssertReturnVoid(m_pHelpBrowserDialog);
+ connect(m_pHelpBrowserDialog, &QMainWindow::destroyed, this, &UIMessageCenter::sltHelpBrowserClosed);
+ }
+
+ m_pHelpBrowserDialog->show();
+ m_pHelpBrowserDialog->setWindowState(m_pHelpBrowserDialog->windowState() & ~Qt::WindowMinimized);
+ m_pHelpBrowserDialog->activateWindow();
+#else
+ Q_UNUSED(strHelpFilePath);
+#endif
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.h b/src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.h
new file mode 100644
index 00000000..432d0827
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.h
@@ -0,0 +1,549 @@
+/* $Id: UIMessageCenter.h $ */
+/** @file
+ * VBox Qt GUI - UIMessageCenter class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIMessageCenter_h
+#define FEQT_INCLUDED_SRC_globals_UIMessageCenter_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UIMediumDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CProgress.h"
+
+/* Forward declarations: */
+class UIHelpBrowserDialog;
+class UIMedium;
+struct StorageSlot;
+#ifdef VBOX_WITH_DRAG_AND_DROP
+class CGuest;
+#endif
+
+
+/** Possible message types. */
+enum MessageType
+{
+ MessageType_Info = 1,
+ MessageType_Question,
+ MessageType_Warning,
+ MessageType_Error,
+ MessageType_Critical,
+ MessageType_GuruMeditation
+};
+Q_DECLARE_METATYPE(MessageType);
+
+
+/** Singleton QObject extension
+ * providing GUI with corresponding messages. */
+class SHARED_LIBRARY_STUFF UIMessageCenter : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Asks to show message-box.
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param strDetails Brings the details.
+ * @param iButton1 Brings the button 1 type.
+ * @param iButton2 Brings the button 2 type.
+ * @param iButton3 Brings the button 3 type.
+ * @param strButtonText1 Brings the button 1 text.
+ * @param strButtonText2 Brings the button 2 text.
+ * @param strButtonText3 Brings the button 3 text.
+ * @param strAutoConfirmId Brings whether this message can be auto-confirmed. */
+ void sigToShowMessageBox(QWidget *pParent, MessageType enmType,
+ const QString &strMessage, const QString &strDetails,
+ int iButton1, int iButton2, int iButton3,
+ const QString &strButtonText1, const QString &strButtonText2, const QString &strButtonText3,
+ const QString &strAutoConfirmId, const QString &strHelpKeyword) const;
+
+public:
+
+ /** Creates message-center singleton. */
+ static void create();
+ /** Destroys message-center singleton. */
+ static void destroy();
+
+ /** Defines whether warning with particular @a strWarningName is @a fShown. */
+ void setWarningShown(const QString &strWarningName, bool fShown) const;
+ /** Returns whether warning with particular @a strWarningName is shown. */
+ bool warningShown(const QString &strWarningName) const;
+
+ /** Shows a general type of 'Message'.
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param strDetails Brings the details.
+ * @param pcszAutoConfirmId Brings the auto-confirm ID.
+ * @param iButton1 Brings the button 1 type.
+ * @param iButton2 Brings the button 2 type.
+ * @param iButton3 Brings the button 3 type.
+ * @param strButtonText1 Brings the button 1 text.
+ * @param strButtonText2 Brings the button 2 text.
+ * @param strButtonText3 Brings the button 3 text.
+ * @param strHelpKeyword Brings the help keyword string. */
+ int message(QWidget *pParent, MessageType enmType,
+ const QString &strMessage, const QString &strDetails,
+ const char *pcszAutoConfirmId = 0,
+ int iButton1 = 0, int iButton2 = 0, int iButton3 = 0,
+ const QString &strButtonText1 = QString(),
+ const QString &strButtonText2 = QString(),
+ const QString &strButtonText3 = QString(),
+ const QString &strHelpKeyword = QString()) const;
+
+ /** Shows an 'Error' type of 'Message'.
+ * Provides single Ok button.
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param strDetails Brings the details.
+ * @param pcszAutoConfirmId Brings the auto-confirm ID.
+ * @param strHelpKeyword Brings the help keyword string. */
+ void error(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const QString &strDetails,
+ const char *pcszAutoConfirmId = 0,
+ const QString &strHelpKeyword = QString()) const;
+
+ /** Shows an 'Error with Question' type of 'Message'.
+ * Provides Ok and Cancel buttons (called same way by default).
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param strDetails Brings the details.
+ * @param pcszAutoConfirmId Brings the auto-confirm ID.
+ * @param strOkButtonText Brings the Ok button text.
+ * @param strCancelButtonText Brings the Cancel button text.
+ * @param strHelpKeyword Brings the help keyword string. */
+ bool errorWithQuestion(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const QString &strDetails,
+ const char *pcszAutoConfirmId = 0,
+ const QString &strOkButtonText = QString(),
+ const QString &strCancelButtonText = QString(),
+ const QString &strHelpKeyword = QString()) const;
+
+ /** Shows an 'Alert' type of 'Error'.
+ * Omit details.
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param pcszAutoConfirmId Brings the auto-confirm ID.
+ * @param strHelpKeyword Brings the help keyword string. */
+ void alert(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const char *pcszAutoConfirmId = 0,
+ const QString &strHelpKeyword = QString()) const;
+
+ /** Shows a 'Question' type of 'Message'.
+ * Omit details.
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param pcszAutoConfirmId Brings the auto-confirm ID.
+ * @param iButton1 Brings the button 1 type.
+ * @param iButton2 Brings the button 2 type.
+ * @param iButton3 Brings the button 3 type.
+ * @param strButtonText1 Brings the button 1 text.
+ * @param strButtonText2 Brings the button 2 text.
+ * @param strButtonText3 Brings the button 3 text. */
+ int question(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const char *pcszAutoConfirmId = 0,
+ int iButton1 = 0, int iButton2 = 0, int iButton3 = 0,
+ const QString &strButtonText1 = QString(),
+ const QString &strButtonText2 = QString(),
+ const QString &strButtonText3 = QString()) const;
+
+ /** Shows a 'Binary' type of 'Question'.
+ * Omit details. Provides Ok and Cancel buttons (called same way by default).
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param pcszAutoConfirmId Brings the auto-confirm ID.
+ * @param strOkButtonText Brings the button 1 text.
+ * @param strCancelButtonText Brings the button 2 text.
+ * @param fDefaultFocusForOk Brings whether Ok button should be focused initially. */
+ bool questionBinary(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const char *pcszAutoConfirmId = 0,
+ const QString &strOkButtonText = QString(),
+ const QString &strCancelButtonText = QString(),
+ bool fDefaultFocusForOk = true) const;
+
+ /** Shows a 'Trinary' type of 'Question'.
+ * Omit details. Provides Yes, No and Cancel buttons (called same way by default).
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param pcszAutoConfirmId Brings the auto-confirm ID.
+ * @param strChoice1ButtonText Brings the button 1 text.
+ * @param strChoice2ButtonText Brings the button 2 text.
+ * @param strCancelButtonText Brings the button 3 text. */
+ int questionTrinary(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const char *pcszAutoConfirmId = 0,
+ const QString &strChoice1ButtonText = QString(),
+ const QString &strChoice2ButtonText = QString(),
+ const QString &strCancelButtonText = QString()) const;
+
+ /** Shows a general type of 'Message with Option'.
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param strOptionText Brings the option text.
+ * @param fDefaultOptionValue Brings the default option value.
+ * @param iButton1 Brings the button 1 type.
+ * @param iButton2 Brings the button 2 type.
+ * @param iButton3 Brings the button 3 type.
+ * @param strButtonText1 Brings the button 1 text.
+ * @param strButtonText2 Brings the button 2 text.
+ * @param strButtonText3 Brings the button 3 text. */
+ int messageWithOption(QWidget *pParent, MessageType enmType,
+ const QString &strMessage,
+ const QString &strOptionText,
+ bool fDefaultOptionValue = true,
+ int iButton1 = 0, int iButton2 = 0, int iButton3 = 0,
+ const QString &strButtonText1 = QString(),
+ const QString &strButtonText2 = QString(),
+ const QString &strButtonText3 = QString()) const;
+
+ /** Shows modal progress-dialog.
+ * @param comProgress Brings the progress this dialog is based on.
+ * @param strTitle Brings the title.
+ * @param strImage Brings the image.
+ * @param pParent Brings the parent.
+ * @param cMinDuration Brings the minimum diration to show this dialog after expiring it. */
+ bool showModalProgressDialog(CProgress &comProgress, const QString &strTitle,
+ const QString &strImage = "", QWidget *pParent = 0,
+ int cMinDuration = 2000);
+
+ /** @name Startup warnings.
+ * @{ */
+ void cannotFindLanguage(const QString &strLangId, const QString &strNlsPath) const;
+ void cannotLoadLanguage(const QString &strLangFile) const;
+
+ void cannotInitUserHome(const QString &strUserHome) const;
+ void cannotInitCOM(HRESULT rc) const;
+
+ void cannotHandleRuntimeOption(const QString &strOption) const;
+
+#ifdef RT_OS_LINUX
+ void warnAboutWrongUSBMounted() const;
+#endif
+
+ void cannotStartSelector() const;
+ void cannotStartRuntime() const;
+ /** @} */
+
+ /** @name General COM warnings.
+ * @{ */
+ void cannotCreateVirtualBoxClient(const CVirtualBoxClient &comClient) const;
+ void cannotAcquireVirtualBox(const CVirtualBoxClient &comClient) const;
+
+ void cannotFindMachineByName(const CVirtualBox &comVBox, const QString &strName) const;
+ void cannotFindMachineById(const CVirtualBox &comVBox, const QUuid &uId) const;
+ void cannotSetExtraData(const CVirtualBox &comVBox, const QString &strKey, const QString &strValue);
+ void cannotOpenMedium(const CVirtualBox &comVBox, const QString &strLocation, QWidget *pParent = 0) const;
+
+ void cannotOpenSession(const CSession &comSession) const;
+ void cannotOpenSession(const CMachine &comMachine) const;
+ void cannotOpenSession(const CProgress &comProgress, const QString &strMachineName) const;
+
+ void cannotSetExtraData(const CMachine &machine, const QString &strKey, const QString &strValue);
+
+ void cannotAttachDevice(const CMachine &machine, UIMediumDeviceType type, const QString &strLocation,
+ const StorageSlot &storageSlot, QWidget *pParent = 0);
+ void cannotDetachDevice(const CMachine &machine, UIMediumDeviceType type, const QString &strLocation,
+ const StorageSlot &storageSlot, QWidget *pParent = 0) const;
+ bool cannotRemountMedium(const CMachine &machine, const UIMedium &medium,
+ bool fMount, bool fRetry, QWidget *pParent = 0) const;
+
+ void cannotSetHostSettings(const CHost &comHost, QWidget *pParent = 0) const;
+ void cannotSetSystemProperties(const CSystemProperties &properties, QWidget *pParent = 0) const;
+ void cannotSaveMachineSettings(const CMachine &machine, QWidget *pParent = 0) const;
+
+ void cannotAddDiskEncryptionPassword(const CConsole &console);
+ /** @} */
+
+ /** @name Common warnings.
+ * @{ */
+ bool confirmResetMachine(const QString &strNames) const;
+
+ void cannotSaveSettings(const QString strDetails, QWidget *pParent = 0) const;
+ void warnAboutUnaccessibleUSB(const COMBaseWithEI &object, QWidget *pParent = 0) const;
+ void warnAboutStateChange(QWidget *pParent = 0) const;
+ bool confirmSettingsDiscarding(QWidget *pParent = 0) const;
+ bool confirmSettingsReloading(QWidget *pParent = 0) const;
+ int confirmRemovingOfLastDVDDevice(QWidget *pParent = 0) const;
+ bool confirmStorageBusChangeWithOpticalRemoval(QWidget *pParent = 0) const;
+ bool confirmStorageBusChangeWithExcessiveRemoval(QWidget *pParent = 0) const;
+ bool warnAboutIncorrectPort(QWidget *pParent = 0) const;
+ bool warnAboutIncorrectAddress(QWidget *pParent = 0) const;
+ bool warnAboutEmptyGuestAddress(QWidget *pParent = 0) const;
+ bool warnAboutNameShouldBeUnique(QWidget *pParent = 0) const;
+ bool warnAboutRulesConflict(QWidget *pParent = 0) const;
+ bool confirmCancelingPortForwardingDialog(QWidget *pParent = 0) const;
+ bool confirmRestoringDefaultKeys(QWidget *pParent = 0) const;
+ /** @} */
+
+ /** @name VirtualBox Manager warnings.
+ * @{ */
+ bool warnAboutInaccessibleMedia() const;
+
+ bool confirmDiscardSavedState(const QString &strNames) const;
+ bool confirmTerminateCloudInstance(const QString &strNames) const;
+ bool confirmACPIShutdownMachine(const QString &strNames) const;
+ bool confirmPowerOffMachine(const QString &strNames) const;
+ bool confirmStartMultipleMachines(const QString &strNames) const;
+ /** @} */
+
+ /** @name VirtualBox Manager / Chooser Pane warnings.
+ * @{ */
+ bool confirmAutomaticCollisionResolve(const QString &strName, const QString &strGroupName) const;
+ /// @todo move after fixing thread stuff
+ void cannotSetGroups(const CMachine &machine) const;
+ bool confirmMachineItemRemoval(const QStringList &names) const;
+ int confirmMachineRemoval(const QList<CMachine> &machines) const;
+ int confirmCloudMachineRemoval(const QList<CCloudMachine> &machines) const;
+ /** @} */
+
+ /** @name VirtualBox Manager / Snapshot Pane warnings.
+ * @{ */
+ int confirmSnapshotRestoring(const QString &strSnapshotName, bool fAlsoCreateNewSnapshot) const;
+ bool confirmSnapshotRemoval(const QString &strSnapshotName) const;
+ bool warnAboutSnapshotRemovalFreeSpace(const QString &strSnapshotName, const QString &strTargetImageName,
+ const QString &strTargetImageMaxSize, const QString &strTargetFileSystemFree) const;
+ /** @} */
+
+ /** @name VirtualBox Manager / Extension Manager warnings.
+ * @{ */
+ bool confirmInstallExtensionPack(const QString &strPackName, const QString &strPackVersion,
+ const QString &strPackDescription, QWidget *pParent = 0) const;
+ bool confirmReplaceExtensionPack(const QString &strPackName, const QString &strPackVersionNew,
+ const QString &strPackVersionOld, const QString &strPackDescription,
+ QWidget *pParent = 0) const;
+ bool confirmRemoveExtensionPack(const QString &strPackName, QWidget *pParent = 0) const;
+ /** @} */
+
+ /** @name VirtualBox Manager / Media Manager warnings.
+ * @{ */
+ bool confirmMediumRelease(const UIMedium &medium, bool fInduced, QWidget *pParent = 0) const;
+ bool confirmMediumRemoval(const UIMedium &medium, QWidget *pParent = 0) const;
+ int confirmDeleteHardDiskStorage(const QString &strLocation, QWidget *pParent = 0) const;
+ bool confirmInaccesibleMediaClear(const QStringList &mediaNameList, UIMediumDeviceType enmType, QWidget *pParent = 0);
+ /** @} */
+
+ /** @name VirtualBox Manager / Network Manager warnings.
+ * @{ */
+ bool confirmCloudNetworkRemoval(const QString &strName, QWidget *pParent = 0) const;
+ bool confirmHostNetworkInterfaceRemoval(const QString &strName, QWidget *pParent = 0) const;
+ bool confirmHostOnlyNetworkRemoval(const QString &strName, QWidget *pParent = 0) const;
+ bool confirmNATNetworkRemoval(const QString &strName, QWidget *pParent = 0) const;
+ /** @} */
+
+ /** @name VirtualBox Manager / Cloud Profile Manager warnings.
+ * @{ */
+ bool confirmCloudProfileRemoval(const QString &strName, QWidget *pParent = 0) const;
+ bool confirmCloudProfilesImport(QWidget *pParent = 0) const;
+ int confirmCloudProfileManagerClosing(QWidget *pParent = 0) const;
+ /** @} */
+
+ /** @name VirtualBox Manager / Cloud Console Manager warnings.
+ * @{ */
+ bool confirmCloudConsoleApplicationRemoval(const QString &strName, QWidget *pParent = 0) const;
+ bool confirmCloudConsoleProfileRemoval(const QString &strName, QWidget *pParent = 0) const;
+ /** @} */
+
+ /** @name VirtualBox Manager / Downloading warnings.
+ * @{ */
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ bool confirmLookingForGuestAdditions() const;
+ bool confirmDownloadGuestAdditions(const QString &strUrl, qulonglong uSize) const;
+ void cannotSaveGuestAdditions(const QString &strURL, const QString &strTarget) const;
+ bool proposeMountGuestAdditions(const QString &strUrl, const QString &strSrc) const;
+
+ bool confirmLookingForUserManual(const QString &strMissedLocation) const;
+ bool confirmDownloadUserManual(const QString &strURL, qulonglong uSize) const;
+ void cannotSaveUserManual(const QString &strURL, const QString &strTarget) const;
+
+ bool confirmLookingForExtensionPack(const QString &strExtPackName, const QString &strExtPackVersion) const;
+ bool confirmDownloadExtensionPack(const QString &strExtPackName, const QString &strURL, qulonglong uSize) const;
+ void cannotSaveExtensionPack(const QString &strExtPackName, const QString &strFrom, const QString &strTo) const;
+ bool proposeInstallExtentionPack(const QString &strExtPackName, const QString &strFrom, const QString &strTo) const;
+ bool proposeDeleteExtentionPack(const QString &strTo) const;
+ bool proposeDeleteOldExtentionPacks(const QStringList &strFiles) const;
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ /** @} */
+
+ /** @name Runtime UI warnings.
+ * @{ */
+ bool cannotRestoreSnapshot(const CMachine &machine, const QString &strSnapshotName, const QString &strMachineName) const;
+ bool cannotRestoreSnapshot(const CProgress &progress, const QString &strSnapshotName, const QString &strMachineName) const;
+ void cannotStartMachine(const CConsole &console, const QString &strName) const;
+ void cannotStartMachine(const CProgress &progress, const QString &strName) const;
+
+ bool warnAboutNetworkInterfaceNotFound(const QString &strMachineName, const QString &strIfNames) const;
+
+ void warnAboutVBoxSVCUnavailable() const;
+ bool warnAboutGuruMeditation(const QString &strLogFolder);
+ void showRuntimeError(const CConsole &console, bool fFatal, const QString &strErrorId, const QString &strErrorMsg) const;
+
+ bool confirmInputCapture(bool &fAutoConfirmed) const;
+ bool confirmGoingFullscreen(const QString &strHotKey) const;
+ bool confirmGoingSeamless(const QString &strHotKey) const;
+ bool confirmGoingScale(const QString &strHotKey) const;
+
+ bool cannotEnterFullscreenMode(ULONG uWidth, ULONG uHeight, ULONG uBpp, ULONG64 uMinVRAM) const;
+ void cannotEnterSeamlessMode(ULONG uWidth, ULONG uHeight, ULONG uBpp, ULONG64 uMinVRAM) const;
+ bool cannotSwitchScreenInFullscreen(quint64 uMinVRAM) const;
+ void cannotSwitchScreenInSeamless(quint64 uMinVRAM) const;
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+ /// @todo move to notification-center as progress notification .. one day :)
+ void cannotDropDataToGuest(const CDnDTarget &dndTarget, QWidget *pParent = 0) const;
+ void cannotDropDataToGuest(const CProgress &progress, QWidget *pParent = 0) const;
+ void cannotDropDataToHost(const CDnDSource &dndSource, QWidget *pParent = 0) const;
+ void cannotDropDataToHost(const CProgress &progress, QWidget *pParent = 0) const;
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+ /** @} */
+
+ /** @name VirtualBox Manager / Wizard warnings.
+ * @{ */
+ /// @todo move to notification-center after wizards get theirs.. :)
+ bool confirmHardDisklessMachine(QWidget *pParent = 0) const;
+ bool confirmExportMachinesInSaveState(const QStringList &machineNames, QWidget *pParent = 0) const;
+ bool confirmOverridingFile(const QString &strPath, QWidget *pParent = 0) const;
+ bool confirmOverridingFiles(const QVector<QString> &strPaths, QWidget *pParent = 0) const;
+ /** @} */
+
+ /** @name VirtualBox Manager / FD Creation Dialog warnings.
+ * @{ */
+ void cannotCreateMediumStorage(const CVirtualBox &comVBox, const QString &strLocation, QWidget *pParent = 0) const;
+ /** @} */
+
+public slots:
+
+ /* Handlers: Help menu stuff: */
+ void sltShowHelpWebDialog();
+ void sltShowBugTracker();
+ void sltShowForums();
+ void sltShowOracle();
+ void sltShowOnlineDocumentation();
+ void sltShowHelpAboutDialog();
+ void sltShowHelpHelpDialog();
+ void sltResetSuppressedMessages();
+ void sltShowUserManual(const QString &strLocation);
+
+ /// @todo move it away ..
+ void sltHelpBrowserClosed();
+ void sltHandleHelpRequest();
+ void sltHandleHelpRequestWithKeyword(const QString &strHelpKeyword);
+
+private slots:
+
+ /** Shows message-box.
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param strDetails Brings the details.
+ * @param iButton1 Brings the button 1 type.
+ * @param iButton2 Brings the button 2 type.
+ * @param iButton3 Brings the button 3 type.
+ * @param strButtonText1 Brings the button 1 text.
+ * @param strButtonText2 Brings the button 2 text.
+ * @param strButtonText3 Brings the button 3 text.
+ * @param strAutoConfirmId Brings whether this message can be auto-confirmed.
+ * @param strHelpKeyword Brings the help keyword string. */
+ void sltShowMessageBox(QWidget *pParent, MessageType enmType,
+ const QString &strMessage, const QString &strDetails,
+ int iButton1, int iButton2, int iButton3,
+ const QString &strButtonText1, const QString &strButtonText2, const QString &strButtonText3,
+ const QString &strAutoConfirmId, const QString &strHelpKeyword) const;
+
+private:
+
+ /** Constructs message-center. */
+ UIMessageCenter();
+ /** Destructs message-center. */
+ ~UIMessageCenter();
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Shows message-box.
+ * @param pParent Brings the message-box parent.
+ * @param enmType Brings the message-box type.
+ * @param strMessage Brings the message.
+ * @param strDetails Brings the details.
+ * @param iButton1 Brings the button 1 type.
+ * @param iButton2 Brings the button 2 type.
+ * @param iButton3 Brings the button 3 type.
+ * @param strButtonText1 Brings the button 1 text.
+ * @param strButtonText2 Brings the button 2 text.
+ * @param strButtonText3 Brings the button 3 text.
+ * @param strAutoConfirmId Brings whether this message can be auto-confirmed.
+ * @param strHelpKeyword Brings the help keyowrd. */
+ int showMessageBox(QWidget *pParent, MessageType type,
+ const QString &strMessage, const QString &strDetails,
+ int iButton1, int iButton2, int iButton3,
+ const QString &strButtonText1, const QString &strButtonText2, const QString &strButtonText3,
+ const QString &strAutoConfirmId, const QString &strHelpKeyword) const;
+
+ /// @todo move it away ..
+ void showHelpBrowser(const QString &strHelpFilePath, QWidget *pParent = 0);
+
+ /** Holds the list of shown warnings. */
+ mutable QStringList m_warnings;
+
+ /** Holds UIHelpBrowserDialog instance. */
+ UIHelpBrowserDialog *m_pHelpBrowserDialog;
+
+ /** Holds the singleton message-center instance. */
+ static UIMessageCenter *s_pInstance;
+ /** Returns the singleton message-center instance. */
+ static UIMessageCenter *instance();
+ /** Allows for shortcut access. */
+ friend UIMessageCenter &msgCenter();
+};
+
+/** Singleton Message Center 'official' name. */
+inline UIMessageCenter &msgCenter() { return *UIMessageCenter::instance(); }
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIMessageCenter_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIModalWindowManager.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIModalWindowManager.cpp
new file mode 100644
index 00000000..9520b713
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIModalWindowManager.cpp
@@ -0,0 +1,284 @@
+/* $Id: UIModalWindowManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIModalWindowManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIModalWindowManager.h"
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+# include "UINetworkRequestManager.h"
+#endif
+#include "UIProgressDialog.h"
+
+/* Other VBox includes: */
+#include <VBox/sup.h>
+
+
+/* static */
+UIModalWindowManager *UIModalWindowManager::s_pInstance = 0;
+UIModalWindowManager *UIModalWindowManager::instance() { return s_pInstance; }
+
+/* static */
+void UIModalWindowManager::create()
+{
+ /* Make sure instance is NOT created yet: */
+ if (s_pInstance)
+ {
+ AssertMsgFailed(("UIModalWindowManager instance is already created!"));
+ return;
+ }
+
+ /* Create instance: */
+ new UIModalWindowManager;
+}
+
+/* static */
+void UIModalWindowManager::destroy()
+{
+ /* Make sure instance is NOT destroyed yet: */
+ if (!s_pInstance)
+ {
+ AssertMsgFailed(("UIModalWindowManager instance is already destroyed!"));
+ return;
+ }
+
+ /* Destroy instance: */
+ delete s_pInstance;
+}
+
+UIModalWindowManager::UIModalWindowManager()
+ : m_pMainWindowShown(0)
+{
+ /* Assign instance: */
+ s_pInstance = this;
+}
+
+UIModalWindowManager::~UIModalWindowManager()
+{
+ /* Unassign instance: */
+ s_pInstance = 0;
+}
+
+QWidget *UIModalWindowManager::realParentWindow(QWidget *pWidget)
+{
+ /* Null if widget pointer is null: */
+ if (!pWidget)
+ return 0;
+
+ /* Get the top-level window for the passed-widget: */
+ QWidget *pTopLevelWindow = pWidget->window();
+
+ /* Search through all the stack(s) we have: */
+ foreach (const QList<QWidget*> &iteratedWindowStack, m_windows)
+ {
+ /* Search through all the window(s) iterated-stack contains: */
+ foreach (QWidget *pIteratedWindow, iteratedWindowStack)
+ {
+ /* If possible-parent-window found: */
+ if (pIteratedWindow == pTopLevelWindow)
+ {
+ /* Return the 'top' of the iterated-window-stack as the result: */
+ QWidget *pTopWindow = iteratedWindowStack.last();
+ preprocessRealParent(pTopWindow);
+ return pTopWindow;
+ }
+ }
+ }
+
+ /* If we unable to found the possible-parent-window among all ours,
+ * we have to add it as the new-window-stack only element: */
+ registerNewParent(pTopLevelWindow);
+ /* And return as the result: */
+ return pTopLevelWindow;
+}
+
+bool UIModalWindowManager::isWindowInTheModalWindowStack(QWidget *pWindow)
+{
+ return contains(pWindow);
+}
+
+bool UIModalWindowManager::isWindowOnTheTopOfTheModalWindowStack(QWidget *pWindow)
+{
+ return contains(pWindow, true);
+}
+
+void UIModalWindowManager::registerNewParent(QWidget *pWindow, QWidget *pParentWindow /* = 0 */)
+{
+ /* Make sure passed-widget-pointer is not null: */
+ if (!pWindow)
+ {
+ AssertMsgFailed(("Passed pointer is NULL!"));
+ return;
+ }
+
+ /* Make sure passed-widget is of 'top-level window' type: */
+ if (!pWindow->isWindow())
+ {
+ AssertMsgFailed(("Passed widget is NOT top-level window!"));
+ return;
+ }
+
+ /* Make sure passed-parent-widget is of 'top-level window' type: */
+ if (pParentWindow && !pParentWindow->isWindow())
+ {
+ AssertMsgFailed(("Passed parent widget is NOT top-level window!"));
+ return;
+ }
+
+ /* If parent-window really passed: */
+ if (pParentWindow)
+ {
+ /* Make sure we have passed-parent-window registered already.
+ * If so, we have to make sure its the 'top' element in his stack also.
+ * If so, we have to register passed-window as the new 'top' in that stack. */
+ for (int iIteratedStackIndex = 0; iIteratedStackIndex < m_windows.size(); ++iIteratedStackIndex)
+ {
+ /* Get current-stack: */
+ QList<QWidget*> &iteratedWindowStack = m_windows[iIteratedStackIndex];
+ /* Search through all the window(s) iterated-stack contains: */
+ int iIteratedWindwStackSize = iteratedWindowStack.size();
+ for (int iIteratedWindowIndex = 0; iIteratedWindowIndex < iIteratedWindwStackSize; ++iIteratedWindowIndex)
+ {
+ /* Get iterated-window: */
+ QWidget *pIteratedWindow = iteratedWindowStack[iIteratedWindowIndex];
+ /* If passed-parent-window found: */
+ if (pIteratedWindow == pParentWindow)
+ {
+ /* Make sure it was the last one of the iterated-window(s): */
+ if (iIteratedWindowIndex != iIteratedWindwStackSize - 1)
+ {
+ AssertMsgFailed(("Passed parent window is not on the top of his current-stack!"));
+ return;
+ }
+ /* Register passed-window as the new 'top' in iterated-window-stack: */
+ iteratedWindowStack << pWindow;
+ connect(pWindow, &QWidget::destroyed, this, &UIModalWindowManager::sltRemoveFromStack);
+ return;
+ }
+ }
+ }
+ /* Passed-parent-window was not found: */
+ AssertMsgFailed(("Passed parent window is not registered!"));
+ return;
+ }
+ /* If no parent-window passed: */
+ else
+ {
+ /* Register passed-window as the only one item in new-window-stack: */
+ QList<QWidget*> newWindowStack(QList<QWidget*>() << pWindow);
+ m_windows << newWindowStack;
+ connect(pWindow, &QWidget::destroyed, this, &UIModalWindowManager::sltRemoveFromStack);
+ }
+
+ /* Notify listeners that their stack may have changed: */
+ emit sigStackChanged();
+}
+
+void UIModalWindowManager::sltRemoveFromStack(QObject *pObject)
+{
+ /* Make sure passed-object still valid: */
+ if (!pObject)
+ return;
+
+ /* Object is already of QObject type,
+ * because inheritance wrapper(s) destructor(s) already called
+ * so we can't search through the m_windows stack
+ * using the standard algorithm functionality.
+ * Lets do it manually: */
+ for (int iIteratedStackIndex = 0; iIteratedStackIndex < m_windows.size(); ++iIteratedStackIndex)
+ {
+ /* Get iterated-stack: */
+ QList<QWidget*> &iteratedWindowStack = m_windows[iIteratedStackIndex];
+ /* Search through all the window(s) iterated-stack contains: */
+ int iIteratedWindowStackSize = iteratedWindowStack.size();
+ for (int iIteratedWindowIndex = 0; iIteratedWindowIndex < iIteratedWindowStackSize; ++iIteratedWindowIndex)
+ {
+ /* Get iterated-window: */
+ QWidget *pIteratedWindow = iteratedWindowStack[iIteratedWindowIndex];
+ /* If passed-object is almost-destroyed iterated-window: */
+ if (pIteratedWindow == pObject)
+ {
+ /* Make sure it was the last added window: */
+ AssertMsg(iIteratedWindowIndex == iIteratedWindowStackSize - 1, ("Removing element from the middle of the stack!"));
+ /* Cleanup window pointer: */
+ iteratedWindowStack.removeAt(iIteratedWindowIndex);
+ /* And stack itself if necessary: */
+ if (iteratedWindowStack.isEmpty())
+ m_windows.removeAt(iIteratedStackIndex);
+ }
+ }
+ }
+
+ /* Notify listeners that their stack may have changed: */
+ emit sigStackChanged();
+}
+
+bool UIModalWindowManager::contains(QWidget *pParentWindow, bool fAsTheTopOfStack /* = false */)
+{
+ /* False if passed-parent-widget pointer is null: */
+ if (!pParentWindow)
+ {
+ AssertMsgFailed(("Passed pointer is NULL!"));
+ return false;
+ }
+
+ /* False if passed-parent-widget is not of 'top-level window' type: */
+ if (!pParentWindow->isWindow())
+ {
+ AssertMsgFailed(("Passed widget is NOT top-level window!"));
+ return false;
+ }
+
+ /* Search through all the stack(s) we have: */
+ foreach (const QList<QWidget*> &iteratedWindowStack, m_windows)
+ {
+ /* Search through all the window(s) iterated-stack contains: */
+ int iIteratedWindowStackSize = iteratedWindowStack.size();
+ for (int iIteratedWidnowIndex = 0; iIteratedWidnowIndex < iIteratedWindowStackSize; ++iIteratedWidnowIndex)
+ {
+ /* Get iterated-window: */
+ QWidget *pIteratedWindow = iteratedWindowStack[iIteratedWidnowIndex];
+ /* If passed-parent-window found: */
+ if (pIteratedWindow == pParentWindow)
+ {
+ /* True if we are not looking for 'top' of the stack or its the 'top': */
+ return !fAsTheTopOfStack || iIteratedWidnowIndex == iIteratedWindowStackSize - 1;
+ }
+ }
+ }
+
+ /* False by default: */
+ return false;
+}
+
+/* static */
+void UIModalWindowManager::preprocessRealParent(QWidget *pParent)
+{
+ /* Progress dialog can be hidden while we are trying to use it as top-most modal parent,
+ * We should show it in such cases because else on MacOS X there will be a problem. */
+ if (UIProgressDialog *pProgressDialog = qobject_cast<UIProgressDialog*>(pParent))
+ pProgressDialog->show();
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIModalWindowManager.h b/src/VBox/Frontends/VirtualBox/src/globals/UIModalWindowManager.h
new file mode 100644
index 00000000..95d6f4dd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIModalWindowManager.h
@@ -0,0 +1,111 @@
+/* $Id: UIModalWindowManager.h $ */
+/** @file
+ * VBox Qt GUI - UIModalWindowManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIModalWindowManager_h
+#define FEQT_INCLUDED_SRC_globals_UIModalWindowManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QWidget>
+#include <QList>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QObject subclass which contains a stack(s) of guarded-pointer(s) to the current top-level
+ * modal-window(s) which could be used to determine parents for new top-level modal-dialog(s). */
+class SHARED_LIBRARY_STUFF UIModalWindowManager : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about stack changed. */
+ void sigStackChanged();
+
+public:
+
+ /** Creates the static singleton instance. */
+ static void create();
+ /** Destroys the static singleton instance. */
+ static void destroy();
+
+ /** Returns actual top-level parent window for a passed @a pPossibleParentWidget. */
+ QWidget *realParentWindow(QWidget *pPossibleParentWidget);
+ /** Returns whether passed @a pWindow is in the modal window stack. */
+ bool isWindowInTheModalWindowStack(QWidget *pWindow);
+ /** Returns whether passed @a pWindow is on the top of the modal window stack. */
+ bool isWindowOnTheTopOfTheModalWindowStack(QWidget *pWindow);
+
+ /** Registers new parent @a pWindow above the passed @a pParentWindow or as separate stack. */
+ void registerNewParent(QWidget *pWindow, QWidget *pParentWindow = 0);
+
+ /** Defines the main application @a pWindow shown. */
+ void setMainWindowShown(QWidget *pWindow) { m_pMainWindowShown = pWindow; }
+ /** Returns the main application window shown. */
+ QWidget *mainWindowShown() const { return m_pMainWindowShown; }
+
+private slots:
+
+ /** Removes window with base-class @a pObject pointer from the stack. */
+ void sltRemoveFromStack(QObject *pObject);
+
+private:
+
+ /** Constructs Modal Window Manager instance. */
+ UIModalWindowManager();
+ /** Destructs Modal Window Manager instance. */
+ ~UIModalWindowManager();
+
+ /** Returns whether stack contains @a pParentWindow at all or @a fAsTheTopOfStack. */
+ bool contains(QWidget *pParentWindow, bool fAsTheTopOfStack = false);
+
+ /** WORKAROUND: Preprocess (show) real parent for a passed @a pParent. */
+ static void preprocessRealParent(QWidget *pParent);
+
+ /** Holds the list of the top-level window stacks. */
+ QList<QList<QWidget*> > m_windows;
+
+ /** Holds the main application window shown. */
+ QWidget *m_pMainWindowShown;
+
+ /** Holds the static singleton instance. */
+ static UIModalWindowManager *s_pInstance;
+ /** Returns the static singleton instance. */
+ static UIModalWindowManager *instance();
+ /** Allows friend-access for static singleton instance. */
+ friend UIModalWindowManager &windowManager();
+};
+
+/** Singleton Modal Window Manager 'official' name. */
+inline UIModalWindowManager &windowManager() { return *(UIModalWindowManager::instance()); }
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIModalWindowManager_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIMousePointerShapeData.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIMousePointerShapeData.cpp
new file mode 100644
index 00000000..dd727ca3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIMousePointerShapeData.cpp
@@ -0,0 +1,61 @@
+/* $Id: UIMousePointerShapeData.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMousePointerShapeData class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIMousePointerShapeData.h"
+
+UIMousePointerShapeData::UIMousePointerShapeData(bool fVisible /* = false */,
+ bool fAlpha /* = false */,
+ const QPoint &hotSpot /* = QPoint() */,
+ const QSize &shapeSize /* = QSize() */,
+ const QVector<BYTE> &shape /* = QVector<BYTE>() */)
+ : m_fVisible(fVisible)
+ , m_fAlpha(fAlpha)
+ , m_hotSpot(hotSpot)
+ , m_shapeSize(shapeSize)
+ , m_shape(shape)
+{
+}
+
+UIMousePointerShapeData::UIMousePointerShapeData(const UIMousePointerShapeData &another)
+ : m_fVisible(another.isVisible())
+ , m_fAlpha(another.hasAlpha())
+ , m_hotSpot(another.hotSpot())
+ , m_shapeSize(another.shapeSize())
+ , m_shape(another.shape())
+{
+}
+
+UIMousePointerShapeData &UIMousePointerShapeData::operator=(const UIMousePointerShapeData &another)
+{
+ m_fVisible = another.isVisible();
+ m_fAlpha = another.hasAlpha();
+ m_hotSpot = another.hotSpot();
+ m_shapeSize = another.shapeSize();
+ m_shape = another.shape();
+ return *this;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIMousePointerShapeData.h b/src/VBox/Frontends/VirtualBox/src/globals/UIMousePointerShapeData.h
new file mode 100644
index 00000000..c8f9f18c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIMousePointerShapeData.h
@@ -0,0 +1,96 @@
+/* $Id: UIMousePointerShapeData.h $ */
+/** @file
+ * VBox Qt GUI - UIMousePointerShapeData class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIMousePointerShapeData_h
+#define FEQT_INCLUDED_SRC_globals_UIMousePointerShapeData_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMetaType>
+#include <QPoint>
+#include <QSize>
+#include <QVector>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox inlcudes: */
+#include <VBox/com/defs.h>
+
+/** Holds the mouse shape data to be able
+ * to pass it through signal-slot mechanism. */
+class SHARED_LIBRARY_STUFF UIMousePointerShapeData
+{
+public:
+
+ /** Constructs mouse pointer shape data.
+ * @param fVisible Brings whether mouse pointer should be visible.
+ * @param fAlpha Brings whether mouse pointer chape has alpha channel.
+ * @param hotSpot Brings the mouse pointer hot-spot.
+ * @param shapeSize Brings the mouse pointer shape size.
+ * @param shape Brings the mouse pointer shape byte array. */
+ UIMousePointerShapeData(bool fVisible = false,
+ bool fAlpha = false,
+ const QPoint &hotSpot = QPoint(),
+ const QSize &shapeSize = QSize(),
+ const QVector<BYTE> &shape = QVector<BYTE>());
+
+ /** Constructs mouse pointer shape data on the basis of another. */
+ UIMousePointerShapeData(const UIMousePointerShapeData &another);
+
+ /** Assigns this mouse pointer shape data with values of @a another. */
+ UIMousePointerShapeData &operator=(const UIMousePointerShapeData &another);
+
+ /** Returns whether mouse pointer should be visible. */
+ bool isVisible() const { return m_fVisible; }
+ /** Returns whether mouse pointer chape has alpha channel. */
+ bool hasAlpha() const { return m_fAlpha; }
+ /** Returns the mouse pointer hot-spot. */
+ const QPoint &hotSpot() const { return m_hotSpot; }
+ /** Returns the mouse pointer shape size. */
+ const QSize &shapeSize() const { return m_shapeSize; }
+ /** Returns the mouse pointer shape byte array. */
+ const QVector<BYTE> &shape() const { return m_shape; }
+
+private:
+
+ /** Holds whether mouse pointer should be visible. */
+ bool m_fVisible;
+ /** Holds whether mouse pointer chape has alpha channel. */
+ bool m_fAlpha;
+ /** Holds the mouse pointer hot-spot. */
+ QPoint m_hotSpot;
+ /** Holds the mouse pointer shape size. */
+ QSize m_shapeSize;
+ /** Holds the mouse pointer shape byte array. */
+ QVector<BYTE> m_shape;
+};
+Q_DECLARE_METATYPE(UIMousePointerShapeData);
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIMousePointerShapeData_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIPathOperations.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIPathOperations.cpp
new file mode 100644
index 00000000..055ff5ee
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIPathOperations.cpp
@@ -0,0 +1,163 @@
+/* $Id: UIPathOperations.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPathOperations class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QList>
+
+/* GUI includes: */
+#include "UIPathOperations.h"
+
+const QChar UIPathOperations::delimiter = QChar('/');
+const QChar UIPathOperations::dosDelimiter = QChar('\\');
+
+/* static */ QString UIPathOperations::removeMultipleDelimiters(const QString &path)
+{
+ QString newPath(path);
+ QString doubleDelimiter(2, delimiter);
+
+ while (newPath.contains(doubleDelimiter) && !newPath.isEmpty())
+ newPath = newPath.replace(doubleDelimiter, delimiter);
+ return newPath;
+}
+
+/* static */ QString UIPathOperations::removeTrailingDelimiters(const QString &path)
+{
+ if (path.isNull() || path.isEmpty())
+ return QString();
+ QString newPath(path);
+ /* Make sure for we dont have any trailing delimiters: */
+ while (newPath.length() > 1 && newPath.at(newPath.length() - 1) == UIPathOperations::delimiter)
+ newPath.chop(1);
+ return newPath;
+}
+
+/* static */ QString UIPathOperations::addTrailingDelimiters(const QString &path)
+{
+ if (path.isNull() || path.isEmpty())
+ return QString();
+ QString newPath(path);
+ while (newPath.length() > 1 && newPath.at(newPath.length() - 1) != UIPathOperations::delimiter)
+ newPath += UIPathOperations::delimiter;
+ return newPath;
+}
+
+/* static */ QString UIPathOperations::addStartDelimiter(const QString &path)
+{
+ if (path.isEmpty())
+ return QString(path);
+ QString newPath(path);
+
+ if (doesPathStartWithDriveLetter(newPath))
+ {
+ if (newPath.length() == 2)
+ {
+ newPath += delimiter;
+ return newPath;
+ }
+ if (newPath.at(2) != delimiter)
+ newPath.insert(2, delimiter);
+ return newPath;
+ }
+ if (newPath.at(0) != delimiter)
+ newPath.insert(0, delimiter);
+ return newPath;
+}
+
+/* static */ QString UIPathOperations::sanitize(const QString &path)
+{
+ QString newPath = addStartDelimiter(removeTrailingDelimiters(removeMultipleDelimiters(path))).replace(dosDelimiter, delimiter);
+ return newPath;
+}
+
+/* static */ QString UIPathOperations::mergePaths(const QString &path, const QString &baseName)
+{
+ QString newBase(baseName);
+ newBase = newBase.remove(delimiter);
+
+ /* make sure we have one and only one trailing '/': */
+ QString newPath(sanitize(path));
+ if(newPath.isEmpty())
+ newPath = delimiter;
+ if(newPath.at(newPath.length() - 1) != delimiter)
+ newPath += UIPathOperations::delimiter;
+ newPath += newBase;
+ return sanitize(newPath);
+}
+
+/* static */ QString UIPathOperations::getObjectName(const QString &path)
+{
+ if (path.length() <= 1)
+ return QString(path);
+
+ QString strTemp(sanitize(path));
+ if (strTemp.length() < 2)
+ return strTemp;
+ int lastSlashPosition = strTemp.lastIndexOf(UIPathOperations::delimiter);
+ if (lastSlashPosition == -1)
+ return QString();
+ return strTemp.right(strTemp.length() - lastSlashPosition - 1);
+}
+
+/* static */ QString UIPathOperations::getPathExceptObjectName(const QString &path)
+{
+ if (path.length() <= 1)
+ return QString(path);
+
+ QString strTemp(sanitize(path));
+ int lastSlashPosition = strTemp.lastIndexOf(UIPathOperations::delimiter);
+ if (lastSlashPosition == -1)
+ return QString();
+ return strTemp.left(lastSlashPosition + 1);
+}
+
+/* static */ QString UIPathOperations::constructNewItemPath(const QString &previousPath, const QString &newBaseName)
+{
+ if (previousPath.length() <= 1)
+ return QString(previousPath);
+ return sanitize(mergePaths(getPathExceptObjectName(previousPath), newBaseName));
+}
+
+/* static */ QStringList UIPathOperations::pathTrail(const QString &path)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ return path.split(UIPathOperations::delimiter, Qt::SkipEmptyParts);
+#else
+ return path.split(UIPathOperations::delimiter, QString::SkipEmptyParts);
+#endif
+}
+
+/* static */ bool UIPathOperations::doesPathStartWithDriveLetter(const QString &path)
+{
+ if (path.length() < 2)
+ return false;
+ /* search for ':' with the path: */
+ if (!path[0].isLetter())
+ return false;
+ if (path[1] != ':')
+ return false;
+ return true;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIPathOperations.h b/src/VBox/Frontends/VirtualBox/src/globals/UIPathOperations.h
new file mode 100644
index 00000000..e0f434df
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIPathOperations.h
@@ -0,0 +1,66 @@
+/* $Id: UIPathOperations.h $ */
+/** @file
+ * VBox Qt GUI - UIFileManagerTable class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIPathOperations_h
+#define FEQT_INCLUDED_SRC_globals_UIPathOperations_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** A collection of simple utility functions to manipulate path strings */
+class SHARED_LIBRARY_STUFF UIPathOperations
+{
+public:
+ static QString removeMultipleDelimiters(const QString &path);
+ static QString removeTrailingDelimiters(const QString &path);
+ static QString addTrailingDelimiters(const QString &path);
+ static QString addStartDelimiter(const QString &path);
+ static QString sanitize(const QString &path);
+ /** Merges prefix and suffix by making sure they have a single '/' in between */
+ static QString mergePaths(const QString &path, const QString &baseName);
+ /** Returns the last part of the @p path. That is the filename or directory name without the path */
+ static QString getObjectName(const QString &path);
+ /** Removes the object name and return the path */
+ static QString getPathExceptObjectName(const QString &path);
+ /** Replaces the last part of the @p previusPath with newBaseName */
+ static QString constructNewItemPath(const QString &previousPath, const QString &newBaseName);
+ /** Splits the path and return it as a QStringList, top most being the 0th element. No delimiters */
+ static QStringList pathTrail(const QString &path);
+ static const QChar delimiter;
+ static const QChar dosDelimiter;
+ /** Tries to determine if the path starts with DOS style drive letters. */
+ static bool doesPathStartWithDriveLetter(const QString &path);
+
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIPathOperations_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIPopupCenter.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIPopupCenter.cpp
new file mode 100644
index 00000000..d36e48f6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIPopupCenter.cpp
@@ -0,0 +1,432 @@
+/* $Id: UIPopupCenter.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPopupCenter class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "QIMessageBox.h"
+#include "UICommon.h"
+#include "UIErrorString.h"
+#include "UIExtraDataManager.h"
+#include "UIHostComboEditor.h"
+#include "UIPopupCenter.h"
+#include "UIPopupStack.h"
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CConsole.h"
+#include "CEmulatedUSB.h"
+#include "CMachine.h"
+#include "CNetworkAdapter.h"
+#include "CVRDEServer.h"
+
+/* Other VBox includes: */
+#include <VBox/sup.h>
+
+
+/* static */
+UIPopupCenter* UIPopupCenter::s_pInstance = 0;
+UIPopupCenter* UIPopupCenter::instance() { return s_pInstance; }
+
+/* static */
+void UIPopupCenter::create()
+{
+ /* Make sure instance is NOT created yet: */
+ if (s_pInstance)
+ return;
+
+ /* Create instance: */
+ new UIPopupCenter;
+ /* Prepare instance: */
+ s_pInstance->prepare();
+}
+
+/* static */
+void UIPopupCenter::destroy()
+{
+ /* Make sure instance is NOT destroyed yet: */
+ if (!s_pInstance)
+ return;
+
+ /* Cleanup instance: */
+ s_pInstance->cleanup();
+ /* Destroy instance: */
+ delete s_pInstance;
+}
+
+UIPopupCenter::UIPopupCenter()
+{
+ /* Assign instance: */
+ s_pInstance = this;
+}
+
+UIPopupCenter::~UIPopupCenter()
+{
+ /* Unassign instance: */
+ s_pInstance = 0;
+}
+
+void UIPopupCenter::prepare()
+{
+}
+
+void UIPopupCenter::cleanup()
+{
+ /* Make sure all the popup-stack types destroyed: */
+ foreach (const QString &strTypeID, m_stackTypes.keys())
+ m_stackTypes.remove(strTypeID);
+ /* Make sure all the popup-stacks destroyed: */
+ foreach (const QString &strID, m_stacks.keys())
+ {
+ delete m_stacks[strID];
+ m_stacks.remove(strID);
+ }
+}
+
+void UIPopupCenter::showPopupStack(QWidget *pParent)
+{
+ /* Make sure parent is set! */
+ AssertPtrReturnVoid(pParent);
+
+ /* Make sure corresponding popup-stack *exists*: */
+ const QString strPopupStackID(popupStackID(pParent));
+ if (!m_stacks.contains(strPopupStackID))
+ return;
+
+ /* Assign stack with passed parent: */
+ UIPopupStack *pPopupStack = m_stacks[strPopupStackID];
+ assignPopupStackParent(pPopupStack, pParent, m_stackTypes[strPopupStackID]);
+ pPopupStack->show();
+}
+
+void UIPopupCenter::hidePopupStack(QWidget *pParent)
+{
+ /* Make sure parent is set! */
+ AssertPtrReturnVoid(pParent);
+
+ /* Make sure corresponding popup-stack *exists*: */
+ const QString strPopupStackID(popupStackID(pParent));
+ if (!m_stacks.contains(strPopupStackID))
+ return;
+
+ /* Unassign stack with passed parent: */
+ UIPopupStack *pPopupStack = m_stacks[strPopupStackID];
+ pPopupStack->hide();
+ unassignPopupStackParent(pPopupStack, pParent);
+}
+
+void UIPopupCenter::setPopupStackType(QWidget *pParent, UIPopupStackType enmType)
+{
+ /* Make sure parent is set! */
+ AssertPtrReturnVoid(pParent);
+
+ /* Composing corresponding popup-stack ID: */
+ const QString strPopupStackID(popupStackID(pParent));
+
+ /* Looking for current popup-stack type, create if it doesn't exists: */
+ UIPopupStackType &enmCurrentType = m_stackTypes[strPopupStackID];
+
+ /* Make sure stack-type has changed: */
+ if (enmCurrentType == enmType)
+ return;
+
+ /* Remember new stack type: */
+ LogRelFlow(("UIPopupCenter::setPopupStackType: Changing type of popup-stack with ID = '%s' from '%s' to '%s'.\n",
+ strPopupStackID.toLatin1().constData(),
+ enmCurrentType == UIPopupStackType_Separate ? "separate window" : "embedded widget",
+ enmType == UIPopupStackType_Separate ? "separate window" : "embedded widget"));
+ enmCurrentType = enmType;
+}
+
+void UIPopupCenter::setPopupStackOrientation(QWidget *pParent, UIPopupStackOrientation newStackOrientation)
+{
+ /* Make sure parent is set! */
+ AssertPtrReturnVoid(pParent);
+
+ /* Composing corresponding popup-stack ID: */
+ const QString strPopupStackID(popupStackID(pParent));
+
+ /* Looking for current popup-stack orientation, create if it doesn't exists: */
+ UIPopupStackOrientation &stackOrientation = m_stackOrientations[strPopupStackID];
+
+ /* Make sure stack-orientation has changed: */
+ if (stackOrientation == newStackOrientation)
+ return;
+
+ /* Remember new stack orientation: */
+ LogRelFlow(("UIPopupCenter::setPopupStackType: Changing orientation of popup-stack with ID = '%s' from '%s' to '%s'.\n",
+ strPopupStackID.toLatin1().constData(),
+ stackOrientation == UIPopupStackOrientation_Top ? "top oriented" : "bottom oriented",
+ newStackOrientation == UIPopupStackOrientation_Top ? "top oriented" : "bottom oriented"));
+ stackOrientation = newStackOrientation;
+
+ /* Update orientation for popup-stack if it currently exists: */
+ if (m_stacks.contains(strPopupStackID))
+ m_stacks[strPopupStackID]->setOrientation(stackOrientation);
+}
+
+void UIPopupCenter::message(QWidget *pParent, const QString &strID,
+ const QString &strMessage, const QString &strDetails,
+ const QString &strButtonText1 /* = QString() */,
+ const QString &strButtonText2 /* = QString() */,
+ bool fProposeAutoConfirmation /* = false */)
+{
+ showPopupPane(pParent, strID,
+ strMessage, strDetails,
+ strButtonText1, strButtonText2,
+ fProposeAutoConfirmation);
+}
+
+void UIPopupCenter::popup(QWidget *pParent, const QString &strID,
+ const QString &strMessage)
+{
+ message(pParent, strID, strMessage, QString());
+}
+
+void UIPopupCenter::alert(QWidget *pParent, const QString &strID,
+ const QString &strMessage,
+ bool fProposeAutoConfirmation /* = false */)
+{
+ message(pParent, strID, strMessage, QString(),
+ QApplication::translate("UIMessageCenter", "Close") /* 1st button text */,
+ QString() /* 2nd button text */,
+ fProposeAutoConfirmation);
+}
+
+void UIPopupCenter::alertWithDetails(QWidget *pParent, const QString &strID,
+ const QString &strMessage,
+ const QString &strDetails,
+ bool fProposeAutoConfirmation /* = false */)
+{
+ message(pParent, strID, strMessage, strDetails,
+ QApplication::translate("UIMessageCenter", "Close") /* 1st button text */,
+ QString() /* 2nd button text */,
+ fProposeAutoConfirmation);
+}
+
+void UIPopupCenter::question(QWidget *pParent, const QString &strID,
+ const QString &strMessage,
+ const QString &strButtonText1 /* = QString() */,
+ const QString &strButtonText2 /* = QString() */,
+ bool fProposeAutoConfirmation /* = false */)
+{
+ message(pParent, strID, strMessage, QString(),
+ strButtonText1, strButtonText2,
+ fProposeAutoConfirmation);
+}
+
+void UIPopupCenter::recall(QWidget *pParent, const QString &strID)
+{
+ hidePopupPane(pParent, strID);
+}
+
+void UIPopupCenter::showPopupPane(QWidget *pParent, const QString &strID,
+ const QString &strMessage, const QString &strDetails,
+ QString strButtonText1 /* = QString() */, QString strButtonText2 /* = QString() */,
+ bool fProposeAutoConfirmation /* = false */)
+{
+ /* Make sure parent is set! */
+ AssertPtrReturnVoid(pParent);
+
+ /* Prepare buttons: */
+ int iButton1 = 0;
+ int iButton2 = 0;
+ /* Make sure single button is properly configured: */
+ if (!strButtonText1.isEmpty() && strButtonText2.isEmpty())
+ iButton1 = AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape;
+ else if (strButtonText1.isEmpty() && !strButtonText2.isEmpty())
+ iButton2 = AlertButton_Cancel | AlertButtonOption_Default | AlertButtonOption_Escape;
+ /* Make sure buttons are unique if set both: */
+ else if (!strButtonText1.isEmpty() && !strButtonText2.isEmpty())
+ {
+ iButton1 = AlertButton_Ok | AlertButtonOption_Default;
+ iButton2 = AlertButton_Cancel | AlertButtonOption_Escape;
+ /* If user made a mistake in button names, we will fix that: */
+ if (strButtonText1 == strButtonText2)
+ {
+ strButtonText1 = QApplication::translate("UIMessageCenter", "Ok");
+ strButtonText1 = QApplication::translate("UIMessageCenter", "Cancel");
+ }
+ }
+
+ /* Check if popup-pane was auto-confirmed before: */
+ if ((iButton1 || iButton2) && fProposeAutoConfirmation)
+ {
+ const QStringList confirmedPopupList = gEDataManager->suppressedMessages();
+ if ( confirmedPopupList.contains(strID)
+ || confirmedPopupList.contains("allPopupPanes")
+ || confirmedPopupList.contains("all") )
+ {
+ int iResultCode = AlertOption_AutoConfirmed;
+ if (iButton1 & AlertButtonOption_Default)
+ iResultCode |= (iButton1 & AlertButtonMask);
+ else if (iButton2 & AlertButtonOption_Default)
+ iResultCode |= (iButton2 & AlertButtonMask);
+ emit sigPopupPaneDone(strID, iResultCode);
+ return;
+ }
+ }
+
+ /* Looking for corresponding popup-stack: */
+ const QString strPopupStackID(popupStackID(pParent));
+ UIPopupStack *pPopupStack = 0;
+ /* If there is already popup-stack with such ID: */
+ if (m_stacks.contains(strPopupStackID))
+ {
+ /* Just get existing one: */
+ pPopupStack = m_stacks[strPopupStackID];
+ }
+ /* If there is no popup-stack with such ID: */
+ else
+ {
+ /* Create new one: */
+ pPopupStack = m_stacks[strPopupStackID] = new UIPopupStack(strPopupStackID, m_stackOrientations[strPopupStackID]);
+ /* Attach popup-stack connections: */
+ connect(pPopupStack, &UIPopupStack::sigPopupPaneDone, this, &UIPopupCenter::sltPopupPaneDone);
+ connect(pPopupStack, &UIPopupStack::sigRemove, this, &UIPopupCenter::sltRemovePopupStack);
+ }
+
+ /* If there is already popup-pane with such ID: */
+ if (pPopupStack->exists(strID))
+ {
+ /* Just update existing one: */
+ pPopupStack->updatePopupPane(strID, strMessage, strDetails);
+ }
+ /* If there is no popup-pane with such ID: */
+ else
+ {
+ /* Compose button description map: */
+ QMap<int, QString> buttonDescriptions;
+ if (iButton1 != 0)
+ buttonDescriptions[iButton1] = strButtonText1;
+ if (iButton2 != 0)
+ buttonDescriptions[iButton2] = strButtonText2;
+ if (fProposeAutoConfirmation)
+ buttonDescriptions[AlertButton_Cancel | AlertOption_AutoConfirmed] = QString();
+ /* Create new one: */
+ pPopupStack->createPopupPane(strID, strMessage, strDetails, buttonDescriptions);
+ }
+
+ /* Show popup-stack: */
+ showPopupStack(pParent);
+}
+
+void UIPopupCenter::hidePopupPane(QWidget *pParent, const QString &strID)
+{
+ /* Make sure parent is set! */
+ AssertPtrReturnVoid(pParent);
+
+ /* Make sure corresponding popup-stack *exists*: */
+ const QString strPopupStackID(popupStackID(pParent));
+ if (!m_stacks.contains(strPopupStackID))
+ return;
+
+ /* Make sure corresponding popup-pane *exists*: */
+ UIPopupStack *pPopupStack = m_stacks[strPopupStackID];
+ if (!pPopupStack->exists(strID))
+ return;
+
+ /* Recall corresponding popup-pane: */
+ pPopupStack->recallPopupPane(strID);
+}
+
+void UIPopupCenter::sltPopupPaneDone(QString strID, int iResultCode)
+{
+ /* Remember auto-confirmation fact (if necessary): */
+ if (iResultCode & AlertOption_AutoConfirmed)
+ gEDataManager->setSuppressedMessages(gEDataManager->suppressedMessages() << strID);
+
+ /* Notify listeners: */
+ emit sigPopupPaneDone(strID, iResultCode);
+}
+
+void UIPopupCenter::sltRemovePopupStack(QString strID)
+{
+ /* Make sure corresponding popup-stack *exists*: */
+ if (!m_stacks.contains(strID))
+ {
+ AssertMsgFailed(("Popup-stack already destroyed!\n"));
+ return;
+ }
+
+ /* Delete popup-stack asyncronously.
+ * To avoid issues with events which already posted: */
+ UIPopupStack *pPopupStack = m_stacks[strID];
+ m_stacks.remove(strID);
+ pPopupStack->deleteLater();
+}
+
+/* static */
+QString UIPopupCenter::popupStackID(QWidget *pParent)
+{
+ /* Make sure parent is set! */
+ AssertPtrReturn(pParent, QString());
+
+ /* Special handling for Runtime UI: */
+ if (pParent->inherits("UIMachineWindow"))
+ return QString("UIMachineWindow");
+
+ /* Common handling for other cases: */
+ return pParent->metaObject()->className();
+}
+
+/* static */
+void UIPopupCenter::assignPopupStackParent(UIPopupStack *pPopupStack, QWidget *pParent, UIPopupStackType enmStackType)
+{
+ /* Make sure parent is set! */
+ AssertPtrReturnVoid(pParent);
+
+ /* Assign event-filter: */
+ pParent->window()->installEventFilter(pPopupStack);
+
+ /* Assign parent depending on passed *stack* type: */
+ switch (enmStackType)
+ {
+ case UIPopupStackType_Embedded:
+ {
+ pPopupStack->setParent(pParent);
+ break;
+ }
+ case UIPopupStackType_Separate:
+ {
+ pPopupStack->setParent(pParent, Qt::Tool | Qt::FramelessWindowHint);
+ break;
+ }
+ default: break;
+ }
+}
+
+/* static */
+void UIPopupCenter::unassignPopupStackParent(UIPopupStack *pPopupStack, QWidget *pParent)
+{
+ /* Make sure parent is set! */
+ AssertPtrReturnVoid(pParent);
+
+ /* Unassign parent: */
+ pPopupStack->setParent(0);
+
+ /* Unassign event-filter: */
+ pParent->window()->removeEventFilter(pPopupStack);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIPopupCenter.h b/src/VBox/Frontends/VirtualBox/src/globals/UIPopupCenter.h
new file mode 100644
index 00000000..b3486d21
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIPopupCenter.h
@@ -0,0 +1,223 @@
+/* $Id: UIPopupCenter.h $ */
+/** @file
+ * VBox Qt GUI - UIPopupCenter class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIPopupCenter_h
+#define FEQT_INCLUDED_SRC_globals_UIPopupCenter_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QObject>
+#include <QPointer>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UIMediumDefs.h"
+
+/* Forward declaration: */
+class QWidget;
+class UIPopupStack;
+class CAudioAdapter;
+class CConsole;
+class CEmulatedUSB;
+class CMachine;
+class CNetworkAdapter;
+class CVirtualBox;
+class CVirtualBoxErrorInfo;
+class CVRDEServer;
+
+
+/** Popup-stack types. */
+enum UIPopupStackType
+{
+ UIPopupStackType_Embedded,
+ UIPopupStackType_Separate
+};
+
+/** Popup-stack orientations. */
+enum UIPopupStackOrientation
+{
+ UIPopupStackOrientation_Top,
+ UIPopupStackOrientation_Bottom
+};
+
+
+/** Singleton QObject extension
+ * providing GUI with various popup messages. */
+class SHARED_LIBRARY_STUFF UIPopupCenter: public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about popup-pane with @a strID is closed with @a iResultCode. */
+ void sigPopupPaneDone(QString strID, int iResultCode);
+
+public:
+
+ /** Creates popup-center singleton. */
+ static void create();
+ /** Destroys message-center singleton. */
+ static void destroy();
+
+ /** Shows popup-stack for @a pParent. */
+ void showPopupStack(QWidget *pParent);
+ /** Hides popup-stack for @a pParent. */
+ void hidePopupStack(QWidget *pParent);
+
+ /** Defines popup-stack @a enmType for @a pParent. */
+ void setPopupStackType(QWidget *pParent, UIPopupStackType enmType);
+ /** Defines popup-stack @a enmOrientation for @a pParent. */
+ void setPopupStackOrientation(QWidget *pParent, UIPopupStackOrientation enmOrientation);
+
+ /** Shows a general type of 'Message'.
+ * @param pParent Brings the popup-pane parent.
+ * @param strID Brings the popup-pane ID.
+ * @param strMessage Brings the message.
+ * @param strDetails Brings the details.
+ * @param strButtonText1 Brings the button 1 text.
+ * @param strButtonText2 Brings the button 2 text.
+ * @param fProposeAutoConfirmation Brings whether auto-confirmation if possible. */
+ void message(QWidget *pParent, const QString &strID,
+ const QString &strMessage, const QString &strDetails,
+ const QString &strButtonText1 = QString(),
+ const QString &strButtonText2 = QString(),
+ bool fProposeAutoConfirmation = false);
+
+ /** Shows 'Popup' type of 'Message'.
+ * Omits details, provides no buttons.
+ * @param pParent Brings the popup-pane parent.
+ * @param strID Brings the popup-pane ID.
+ * @param strMessage Brings the message. */
+ void popup(QWidget *pParent, const QString &strID,
+ const QString &strMessage);
+
+ /** Shows 'Alert' type of 'Message'.
+ * Omits details, provides one button.
+ * @param pParent Brings the popup-pane parent.
+ * @param strID Brings the popup-pane ID.
+ * @param strMessage Brings the message.
+ * @param fProposeAutoConfirmation Brings whether auto-confirmation if possible. */
+ void alert(QWidget *pParent, const QString &strID,
+ const QString &strMessage,
+ bool fProposeAutoConfirmation = false);
+
+ /** Shows 'Alert with Details' type of 'Message'.
+ * Provides one button.
+ * @param pParent Brings the popup-pane parent.
+ * @param strID Brings the popup-pane ID.
+ * @param strMessage Brings the message.
+ * @param strDetails Brings the details.
+ * @param fProposeAutoConfirmation Brings whether auto-confirmation if possible. */
+ void alertWithDetails(QWidget *pParent, const QString &strID,
+ const QString &strMessage, const QString &strDetails,
+ bool fProposeAutoConfirmation = false);
+
+ /** Shows 'Question' type of 'Message'.
+ * Omits details, provides up to two buttons.
+ * @param pParent Brings the popup-pane parent.
+ * @param strID Brings the popup-pane ID.
+ * @param strMessage Brings the message.
+ * @param strButtonText1 Brings the button 1 text.
+ * @param strButtonText2 Brings the button 2 text.
+ * @param fProposeAutoConfirmation Brings whether auto-confirmation if possible. */
+ void question(QWidget *pParent, const QString &strID,
+ const QString &strMessage,
+ const QString &strButtonText1 = QString(),
+ const QString &strButtonText2 = QString(),
+ bool fProposeAutoConfirmation = false);
+
+ /** Recalls popup with @a strID of passed @a pParent. */
+ void recall(QWidget *pParent, const QString &strID);
+
+private slots:
+
+ /** Handles request to close popup-pane with @a strID and @a iResultCode. */
+ void sltPopupPaneDone(QString strID, int iResultCode);
+
+ /** Handles request to remove popup-stack with @a strID. */
+ void sltRemovePopupStack(QString strID);
+
+private:
+
+ /** Constructs popup-center. */
+ UIPopupCenter();
+ /** Destructs popup-center. */
+ ~UIPopupCenter();
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Shows popup-pane.
+ * @param pParent Brings the popup-pane parent.
+ * @param strID Brings the popup-pane ID.
+ * @param strMessage Brings the message.
+ * @param strDetails Brings the details.
+ * @param strButtonText1 Brings the button 1 text.
+ * @param strButtonText2 Brings the button 2 text.
+ * @param fProposeAutoConfirmation Brings whether auto-confirmation if possible. */
+ void showPopupPane(QWidget *pParent, const QString &strID,
+ const QString &strMessage, const QString &strDetails,
+ QString strButtonText1 = QString(), QString strButtonText2 = QString(),
+ bool fProposeAutoConfirmation = false);
+ /** Hides popup-pane.
+ * @param pParent Brings the popup-pane parent.
+ * @param strID Brings the popup-pane ID. */
+ void hidePopupPane(QWidget *pParent, const QString &strID);
+
+ /** Returns popup-stack ID for passed @a pParent. */
+ static QString popupStackID(QWidget *pParent);
+ /** Assigns @a pPopupStack @a pParent of passed @a enmStackType. */
+ static void assignPopupStackParent(UIPopupStack *pPopupStack, QWidget *pParent, UIPopupStackType enmStackType);
+ /** Unassigns @a pPopupStack @a pParent. */
+ static void unassignPopupStackParent(UIPopupStack *pPopupStack, QWidget *pParent);
+
+ /** Holds the popup-stack type on per stack ID basis. */
+ QMap<QString, UIPopupStackType> m_stackTypes;
+ /** Holds the popup-stack orientations on per stack ID basis. */
+ QMap<QString, UIPopupStackOrientation> m_stackOrientations;
+ /** Holds the popup-stacks on per stack ID basis. */
+ QMap<QString, QPointer<UIPopupStack> > m_stacks;
+
+ /** Holds the singleton message-center instance. */
+ static UIPopupCenter *s_pInstance;
+ /** Returns the singleton message-center instance. */
+ static UIPopupCenter *instance();
+ /** Allows for shortcut access. */
+ friend UIPopupCenter &popupCenter();
+};
+
+/** Singleton Popup Center 'official' name. */
+inline UIPopupCenter &popupCenter() { return *UIPopupCenter::instance(); }
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIPopupCenter_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIProgressEventHandler.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressEventHandler.cpp
new file mode 100644
index 00000000..1814fa64
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressEventHandler.cpp
@@ -0,0 +1,126 @@
+/* $Id: UIProgressEventHandler.cpp $ */
+/** @file
+ * VBox Qt GUI - UIProgressEventHandler class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIExtraDataManager.h"
+#include "UIMainEventListener.h"
+#include "UIProgressEventHandler.h"
+#include "UICommon.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif /* VBOX_WS_MAC */
+
+UIProgressEventHandler::UIProgressEventHandler(QObject *pParent, const CProgress &comProgress)
+ : QObject(pParent)
+ , m_comProgress(comProgress)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIProgressEventHandler::~UIProgressEventHandler()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIProgressEventHandler::prepare()
+{
+ /* Prepare: */
+ prepareListener();
+ prepareConnections();
+}
+
+void UIProgressEventHandler::prepareListener()
+{
+ /* Create event listener instance: */
+ m_pQtListener.createObject();
+ m_pQtListener->init(new UIMainEventListener, this);
+ m_comEventListener = CEventListener(m_pQtListener);
+
+ /* Get CProgress event source: */
+ CEventSource comEventSourceProgress = m_comProgress.GetEventSource();
+ AssertWrapperOk(comEventSourceProgress);
+
+ /* Enumerate all the required event-types: */
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes
+ << KVBoxEventType_OnProgressPercentageChanged
+ << KVBoxEventType_OnProgressTaskCompleted;
+
+ /* Register event listener for CProgress event source: */
+ comEventSourceProgress.RegisterListener(m_comEventListener, eventTypes, FALSE /* active? */);
+ AssertWrapperOk(comEventSourceProgress);
+
+ /* Register event sources in their listeners as well: */
+ m_pQtListener->getWrapped()->registerSource(comEventSourceProgress,
+ m_comEventListener,
+ QSet<KVBoxEventType>() << KVBoxEventType_OnProgressTaskCompleted);
+}
+
+void UIProgressEventHandler::prepareConnections()
+{
+ /* Create direct (sync) connections for signals of main listener: */
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigListeningFinished,
+ this, &UIProgressEventHandler::sigHandlingFinished,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigProgressPercentageChange,
+ this, &UIProgressEventHandler::sigProgressPercentageChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigProgressTaskComplete,
+ this, &UIProgressEventHandler::sigProgressTaskComplete,
+ Qt::DirectConnection);
+}
+
+void UIProgressEventHandler::cleanupConnections()
+{
+ /* Nothing for now. */
+}
+
+void UIProgressEventHandler::cleanupListener()
+{
+ /* Unregister everything: */
+ m_pQtListener->getWrapped()->unregisterSources();
+
+ /* Make sure VBoxSVC is available: */
+ if (!uiCommon().isVBoxSVCAvailable())
+ return;
+
+ /* Get CProgress event source: */
+ CEventSource comEventSourceProgress = m_comProgress.GetEventSource();
+ AssertWrapperOk(comEventSourceProgress);
+
+ /* Unregister event listener for CProgress event source: */
+ comEventSourceProgress.UnregisterListener(m_comEventListener);
+}
+
+void UIProgressEventHandler::cleanup()
+{
+ /* Cleanup: */
+ cleanupConnections();
+ cleanupListener();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIProgressEventHandler.h b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressEventHandler.h
new file mode 100644
index 00000000..ffd9ecc5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressEventHandler.h
@@ -0,0 +1,96 @@
+/* $Id: UIProgressEventHandler.h $ */
+/** @file
+ * VBox Qt GUI - UIProgressEventHandler class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIProgressEventHandler_h
+#define FEQT_INCLUDED_SRC_globals_UIProgressEventHandler_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+ #include "UIMainEventListener.h"
+
+/* COM includes: */
+# include "CEventListener.h"
+# include "CEventSource.h"
+# include "CProgress.h"
+
+
+/** Private QObject extension
+ * providing UIExtraDataManager with the CVirtualBox event-source. */
+class SHARED_LIBRARY_STUFF UIProgressEventHandler : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about @a iPercent change for progress with @a uProgressId. */
+ void sigProgressPercentageChange(const QUuid &uProgressId, const int iPercent);
+ /** Notifies about task complete for progress with @a uProgressId. */
+ void sigProgressTaskComplete(const QUuid &uProgressId);
+ /** Notifies about handling has finished. */
+ void sigHandlingFinished();
+
+public:
+
+ /** Constructs event proxy object on the basis of passed @a pParent. */
+ UIProgressEventHandler(QObject *pParent, const CProgress &comProgress);
+ /** Destructs event proxy object. */
+ virtual ~UIProgressEventHandler() RT_OVERRIDE;
+
+protected:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares listener. */
+ void prepareListener();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups listener. */
+ void cleanupListener();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+private:
+
+ /** Holds the progress wrapper. */
+ CProgress m_comProgress;
+
+ /** Holds the Qt event listener instance. */
+ ComObjPtr<UIMainEventListenerImpl> m_pQtListener;
+ /** Holds the COM event listener instance. */
+ CEventListener m_comEventListener;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIProgressEventHandler_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIProgressObject.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressObject.cpp
new file mode 100644
index 00000000..ebe80a94
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressObject.cpp
@@ -0,0 +1,165 @@
+/* $Id: UIProgressObject.cpp $ */
+/** @file
+ * VBox Qt GUI - UIProgressObject class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIErrorString.h"
+#include "UIProgressEventHandler.h"
+#include "UIProgressObject.h"
+
+/* COM includes: */
+#include "CProgress.h"
+
+
+UIProgressObject::UIProgressObject(CProgress &comProgress, QObject *pParent /* = 0 */)
+ : QObject(pParent)
+ , m_comProgress(comProgress)
+ , m_fCancelable(false)
+ , m_pEventHandler(0)
+{
+ prepare();
+}
+
+UIProgressObject::~UIProgressObject()
+{
+ cleanup();
+}
+
+void UIProgressObject::exec()
+{
+ /* Make sure progress hasn't aborted/finished already: */
+ if (!m_comProgress.isOk() || m_comProgress.GetCompleted())
+ return;
+
+ /* We are creating a locally-scoped event-loop object,
+ * but holding a pointer to it for a control needs: */
+ QEventLoop eventLoop;
+ m_pEventLoopExec = &eventLoop;
+
+ /* Guard ourself for the case
+ * we self-destroyed in our event-loop: */
+ QPointer<UIProgressObject> guard = this;
+
+ /* Start the blocking event-loop: */
+ eventLoop.exec();
+
+ /* Event-loop object unblocked,
+ * Are we still valid? */
+ if (guard.isNull())
+ return;
+
+ /* Cleanup the pointer finally: */
+ m_pEventLoopExec = 0;
+}
+
+void UIProgressObject::cancel()
+{
+ /* Make sure progress hasn't aborted/finished already: */
+ if (!m_comProgress.isOk() || m_comProgress.GetCompleted())
+ return;
+
+ /* Cancel progress first of all: */
+ m_comProgress.Cancel();
+
+ /* We are creating a locally-scoped event-loop object,
+ * but holding a pointer to it for a control needs: */
+ QEventLoop eventLoop;
+ m_pEventLoopCancel = &eventLoop;
+
+ /* Guard ourself for the case
+ * we self-destroyed in our event-loop: */
+ QPointer<UIProgressObject> guard = this;
+
+ /* Start the blocking event-loop: */
+ eventLoop.exec();
+
+ /* Event-loop object unblocked,
+ * Are we still valid? */
+ if (guard.isNull())
+ return;
+
+ /* Cleanup the pointer finally: */
+ m_pEventLoopCancel = 0;
+}
+
+void UIProgressObject::sltHandleProgressPercentageChange(const QUuid &, const int iPercent)
+{
+ /* Update cancelable value: */
+ m_fCancelable = m_comProgress.GetCancelable();
+
+ /* Notify listeners: */
+ emit sigProgressChange(m_comProgress.GetOperationCount(),
+ m_comProgress.GetOperationDescription(),
+ m_comProgress.GetOperation(),
+ iPercent);
+}
+
+void UIProgressObject::sltHandleProgressTaskComplete(const QUuid &)
+{
+ /* Notify listeners about the operation progress error: */
+ if (!m_comProgress.isOk() || m_comProgress.GetResultCode() != 0)
+ emit sigProgressError(UIErrorString::formatErrorInfo(m_comProgress));
+
+ /* Exit from the exec event-loop if there is any: */
+ if (m_pEventLoopExec)
+ m_pEventLoopExec->exit();
+ /* Exit from the cancel event-loop if there is any: */
+ if (m_pEventLoopCancel)
+ m_pEventLoopCancel->exit();
+
+ emit sigProgressComplete();
+}
+
+void UIProgressObject::prepare()
+{
+ /* Init cancelable value: */
+ m_fCancelable = m_comProgress.GetCancelable();
+
+ /* Create CProgress event handler: */
+ m_pEventHandler = new UIProgressEventHandler(this, m_comProgress);
+ if (m_pEventHandler)
+ {
+ connect(m_pEventHandler, &UIProgressEventHandler::sigProgressPercentageChange,
+ this, &UIProgressObject::sltHandleProgressPercentageChange);
+ connect(m_pEventHandler, &UIProgressEventHandler::sigProgressTaskComplete,
+ this, &UIProgressObject::sltHandleProgressTaskComplete);
+ connect(m_pEventHandler, &UIProgressEventHandler::sigHandlingFinished,
+ this, &UIProgressObject::sigProgressEventHandlingFinished);
+ }
+}
+
+void UIProgressObject::cleanup()
+{
+ /* Destroy CProgress event handler: */
+ disconnect(m_pEventHandler, &UIProgressEventHandler::sigProgressPercentageChange,
+ this, &UIProgressObject::sltHandleProgressPercentageChange);
+ disconnect(m_pEventHandler, &UIProgressEventHandler::sigProgressTaskComplete,
+ this, &UIProgressObject::sltHandleProgressTaskComplete);
+ disconnect(m_pEventHandler, &UIProgressEventHandler::sigHandlingFinished,
+ this, &UIProgressObject::sigProgressEventHandlingFinished);
+ delete m_pEventHandler;
+ m_pEventHandler = 0;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIProgressObject.h b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressObject.h
new file mode 100644
index 00000000..4ce8adde
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressObject.h
@@ -0,0 +1,120 @@
+/* $Id: UIProgressObject.h $ */
+/** @file
+ * VBox Qt GUI - UIProgressObject class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIProgressObject_h
+#define FEQT_INCLUDED_SRC_globals_UIProgressObject_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QEventLoop>
+#include <QObject>
+#include <QPointer>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class UIProgressEventHandler;
+class CProgress;
+
+/** QObject reimplementation allowing to effectively track the CProgress object completion
+ * (w/o using CProgress::waitForCompletion() and w/o blocking the calling thread in any other way for too long).
+ * @note The CProgress instance is passed as a non-const reference to the constructor
+ * (to memorize COM errors if they happen), and therefore must not be destroyed
+ * before the created UIProgressObject instance is destroyed. */
+class SHARED_LIBRARY_STUFF UIProgressObject : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about wrapped CProgress change.
+ * @param iOperations Brings the number of operations CProgress have.
+ * @param strOperation Brings the description of the current CProgress operation.
+ * @param iOperation Brings the index of the current CProgress operation.
+ * @param iPercent Brings the percentage of the current CProgress operation. */
+ void sigProgressChange(ulong iOperations, QString strOperation,
+ ulong iOperation, ulong iPercent);
+
+ /** Notifies listeners about particular COM error.
+ * @param strErrorInfo holds the details of the error happened. */
+ void sigProgressError(QString strErrorInfo);
+
+ /** Notifies listeners about wrapped CProgress complete. */
+ void sigProgressComplete();
+
+ /** Notifies listeners about CProgress event handling finished. */
+ void sigProgressEventHandlingFinished();
+
+public:
+
+ /** Constructs progress-object passing @a pParent to the base-class.
+ * @param comProgress Brings the progress reference. */
+ UIProgressObject(CProgress &comProgress, QObject *pParent = 0);
+ /** Destructs progress handler. */
+ virtual ~UIProgressObject() RT_OVERRIDE;
+
+ /** Returns whether progress is cancelable. */
+ bool isCancelable() const { return m_fCancelable; }
+
+ /** Executes the progress within local event-loop. */
+ void exec();
+ /** Cancels the progress within local event-loop. */
+ void cancel();
+
+private slots:
+
+ /** Handles percentage changed event for progress with @a uProgressId to @a iPercent. */
+ void sltHandleProgressPercentageChange(const QUuid &uProgressId, const int iPercent);
+ /** Handles task completed event for progress with @a uProgressId. */
+ void sltHandleProgressTaskComplete(const QUuid &uProgressId);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the progress reference. */
+ CProgress &m_comProgress;
+
+ /** Holds whether progress is cancelable. */
+ bool m_fCancelable;
+
+ /** Holds the progress event handler instance. */
+ UIProgressEventHandler *m_pEventHandler;
+
+ /** Holds the exec event-loop instance. */
+ QPointer<QEventLoop> m_pEventLoopExec;
+ /** Holds the cancel event-loop instance. */
+ QPointer<QEventLoop> m_pEventLoopCancel;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIProgressObject_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIProgressTask.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressTask.cpp
new file mode 100644
index 00000000..2b4e83bc
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressTask.cpp
@@ -0,0 +1,158 @@
+/* $Id: UIProgressTask.cpp $ */
+/** @file
+ * VBox Qt GUI - UIProgressTask class implementation.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QTimer>
+
+/* GUI includes: */
+#include "UIProgressTask.h"
+
+
+UIProgressTask::UIProgressTask(QObject *pParent)
+ : QObject(pParent)
+ , m_pTimer(0)
+{
+ prepare();
+}
+
+UIProgressTask::~UIProgressTask()
+{
+ cleanup();
+}
+
+bool UIProgressTask::isScheduled() const
+{
+ AssertPtrReturn(m_pTimer, false);
+ return m_pTimer->isActive();
+}
+
+bool UIProgressTask::isRunning() const
+{
+ return m_pProgressObject;
+}
+
+bool UIProgressTask::isCancelable() const
+{
+ return m_pProgressObject ? m_pProgressObject->isCancelable() : false;
+}
+
+void UIProgressTask::schedule(int iMsec)
+{
+ AssertPtrReturnVoid(m_pTimer);
+ m_pTimer->setInterval(iMsec);
+ m_pTimer->start();
+}
+
+void UIProgressTask::start()
+{
+ /* Ignore request if already running: */
+ if (isRunning())
+ return;
+
+ /* Call for a virtual stuff to create progress-wrapper itself: */
+ m_comProgress = createProgress();
+
+ /* Make sure progress valid: */
+ if ( m_comProgress.isNull()
+ || m_comProgress.GetCompleted())
+ {
+ /* Notify external listeners: */
+ emit sigProgressStarted();
+ sltHandleProgressEventHandlingFinished();
+ }
+ else
+ {
+ /* Prepare progress-object: */
+ m_pProgressObject = new UIProgressObject(m_comProgress, this);
+ if (m_pProgressObject)
+ {
+ /* Setup connections: */
+ connect(m_pProgressObject.data(), &UIProgressObject::sigProgressChange,
+ this, &UIProgressTask::sltHandleProgressChange);
+ connect(m_pProgressObject.data(), &UIProgressObject::sigProgressEventHandlingFinished,
+ this, &UIProgressTask::sltHandleProgressEventHandlingFinished);
+
+ /* Notify external listeners: */
+ emit sigProgressStarted();
+ if (m_comProgress.GetCompleted())
+ sltHandleProgressEventHandlingFinished();
+ }
+ }
+}
+
+void UIProgressTask::cancel()
+{
+ if (m_pProgressObject)
+ {
+ m_pProgressObject->cancel();
+ /* Notify external listeners: */
+ emit sigProgressCanceled();
+ }
+}
+
+void UIProgressTask::sltHandleProgressChange(ulong /*uOperations*/, QString /*strOperation*/,
+ ulong /*uOperation*/, ulong uPercent)
+{
+ /* Notify external listeners: */
+ emit sigProgressChange(uPercent);
+}
+
+void UIProgressTask::sltHandleProgressEventHandlingFinished()
+{
+ /* Call for a virtual stuff to let sub-class handle result: */
+ handleProgressFinished(m_comProgress);
+
+ /* Cleanup progress-object and progress-wrapper: */
+ delete m_pProgressObject;
+ m_comProgress = CProgress();
+
+ /* Notify external listeners: */
+ emit sigProgressFinished();
+}
+
+void UIProgressTask::prepare()
+{
+ /* Prepare schedule-timer: */
+ m_pTimer = new QTimer(this);
+ if (m_pTimer)
+ {
+ m_pTimer->setSingleShot(true);
+ connect(m_pTimer, &QTimer::timeout,
+ this, &UIProgressTask::start);
+ }
+}
+
+void UIProgressTask::cleanup()
+{
+ /* Cleanup progress-object and progress-wrapper: */
+ delete m_pProgressObject;
+ m_comProgress = CProgress();
+
+ /* Cleanup schedule-timer: */
+ delete m_pTimer;
+ m_pTimer = 0;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIProgressTask.h b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressTask.h
new file mode 100644
index 00000000..cd4c07bb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIProgressTask.h
@@ -0,0 +1,127 @@
+/* $Id: UIProgressTask.h $ */
+/** @file
+ * VBox Qt GUI - UIProgressTask class declaration.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIProgressTask_h
+#define FEQT_INCLUDED_SRC_globals_UIProgressTask_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QPointer>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UIProgressObject.h"
+
+/* COM includes: */
+#include "CProgress.h"
+
+/* Forward declarations: */
+class QTimer;
+
+/** QObject-based interface allowing to plan UIProgressObject-based tasks
+ * to be seamlessly and asynchronously scheduled (in time) and executed. */
+class SHARED_LIBRARY_STUFF UIProgressTask : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about progress has started. */
+ void sigProgressStarted();
+ /** Notifies listeners about progress has changed.
+ * @param uPercent Brings the progress percentage. */
+ void sigProgressChange(ulong uPercent);
+ /** Notifies listeners about progress was canceled. */
+ void sigProgressCanceled();
+ /** Notifies listeners about progress has finished. */
+ void sigProgressFinished();
+
+public:
+
+ /** Creates progress task passing @a pParent to the base-class. */
+ UIProgressTask(QObject *pParent);
+ /** Creates progress task passing @a pParent to the base-class. */
+ virtual ~UIProgressTask() RT_OVERRIDE;
+
+ /** Returns whether task is scheduled. */
+ bool isScheduled() const;
+
+ /** Returns whether task is running. */
+ bool isRunning() const;
+
+ /** Returns whether task is cancelable. */
+ bool isCancelable() const;
+
+public slots:
+
+ /** Schedules task to be executed in @a iMsec. */
+ void schedule(int iMsec);
+
+ /** Starts the task directly.
+ * @note It will also be started automatically if scheduled. */
+ void start();
+ /** Cancels the task directly. */
+ void cancel();
+
+protected:
+
+ /** Creates and returns started progress-wrapper required to init UIProgressObject. */
+ virtual CProgress createProgress() = 0;
+ /** Allows sub-class to handle finished @a comProgress wrapper. */
+ virtual void handleProgressFinished(CProgress &comProgress) = 0;
+
+private slots:
+
+ /** Handles progress change.
+ * @param iOperations Brings the number of operations CProgress have.
+ * @param strOperation Brings the description of the current CProgress operation.
+ * @param iOperation Brings the index of the current CProgress operation.
+ * @param iPercent Brings the percentage of the current CProgress operation. */
+ void sltHandleProgressChange(ulong uOperations, QString strOperation,
+ ulong uOperation, ulong uPercent);
+ /** Handles progress event handling finished signal. */
+ void sltHandleProgressEventHandlingFinished();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the schedule timer instance. */
+ QTimer *m_pTimer;
+ /** Holds the progress-wrapper instance. */
+ CProgress m_comProgress;
+ /** Holds the progress-object instance. */
+ QPointer<UIProgressObject> m_pProgressObject;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIProgressTask_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIQObjectStuff.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIQObjectStuff.cpp
new file mode 100644
index 00000000..f2d72e8a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIQObjectStuff.cpp
@@ -0,0 +1,85 @@
+/* $Id: UIQObjectStuff.cpp $ */
+/** @file
+ * VBox Qt GUI - UIQObjectStuff class implementation.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIQObjectStuff.h"
+
+
+UIQObjectPropertySetter::UIQObjectPropertySetter(QObject *pObject, const QString &strPropertyName, const QVariant &value)
+ : m_strPropertyName(strPropertyName)
+ , m_value(value)
+{
+ /* Add object into list: */
+ m_objects << pObject;
+ /* Init properties: */
+ init();
+}
+
+UIQObjectPropertySetter::UIQObjectPropertySetter(const QList<QObject*> &objects, const QString &strPropertyName, const QVariant &value)
+ : m_strPropertyName(strPropertyName)
+ , m_value(value)
+{
+ /* Add objects into list: */
+ foreach (QObject *pObject, objects)
+ m_objects << pObject;
+ /* Init properties: */
+ init();
+}
+
+UIQObjectPropertySetter::~UIQObjectPropertySetter()
+{
+ /* Deinit properties: */
+ deinit();
+ /* Notify listeners that we are done: */
+ emit sigAboutToBeDestroyed();
+}
+
+void UIQObjectPropertySetter::init()
+{
+ foreach (const QPointer<QObject> &pObject, m_objects)
+ {
+ if (pObject)
+ {
+ pObject->setProperty(m_strPropertyName.toLatin1().constData(), m_value);
+ //printf("UIQObjectPropertySetter::UIQObjectPropertySetter: Property {%s} set.\n",
+ // m_strPropertyName.toLatin1().constData());
+ }
+ }
+}
+
+void UIQObjectPropertySetter::deinit()
+{
+ foreach (const QPointer<QObject> &pObject, m_objects)
+ {
+ if (pObject)
+ {
+ pObject->setProperty(m_strPropertyName.toLatin1().constData(), QVariant());
+ //printf("UIQObjectPropertySetter::~UIQObjectPropertySetter: Property {%s} cleared.\n",
+ // m_strPropertyName.toLatin1().constData());
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIQObjectStuff.h b/src/VBox/Frontends/VirtualBox/src/globals/UIQObjectStuff.h
new file mode 100644
index 00000000..b6292704
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIQObjectStuff.h
@@ -0,0 +1,80 @@
+/* $Id: UIQObjectStuff.h $ */
+/** @file
+ * VBox Qt GUI - UIQObjectStuff class declaration.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIQObjectStuff_h
+#define FEQT_INCLUDED_SRC_globals_UIQObjectStuff_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QPointer>
+#include <QVariant>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** Guard block which sets/clears QObject property on RAII basis. */
+class SHARED_LIBRARY_STUFF UIQObjectPropertySetter : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notify listeners that we are about to be destroyed.
+ * @note This signal is emitted one call-stack frame earlier
+ * than QObject::destroyed(QObject *obj = Q_NULLPTR),
+ * so we still can use info stored in this object. */
+ void sigAboutToBeDestroyed();
+
+public:
+
+ /** Constructs guard block which sets for @a pObject a property with certain @a strPropertyName and @a value. */
+ UIQObjectPropertySetter(QObject *pObject, const QString &strPropertyName, const QVariant &value);
+ /** Constructs guard block which sets for @a objects a property with certain @a strPropertyName and @a value. */
+ UIQObjectPropertySetter(const QList<QObject*> &objects, const QString &strPropertyName, const QVariant &value);
+
+ /** Destructs guard block clearing previously set property for good. */
+ virtual ~UIQObjectPropertySetter() RT_OVERRIDE;
+
+private:
+
+ /** Inits properties. */
+ void init();
+ /** Deinits properties. */
+ void deinit();
+
+ /** Holds the list of live QObject pointers. */
+ QList<QPointer<QObject> > m_objects;
+ /** Holds the property name. */
+ QString m_strPropertyName;
+ /** Holds the property value. */
+ QVariant m_value;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIQObjectStuff_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIShortcutPool.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIShortcutPool.cpp
new file mode 100644
index 00000000..5eb199ea
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIShortcutPool.cpp
@@ -0,0 +1,427 @@
+/* $Id: UIShortcutPool.cpp $ */
+/** @file
+ * VBox Qt GUI - UIShortcutPool class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIActionPool.h"
+#include "UIExtraDataManager.h"
+#include "UIShortcutPool.h"
+
+
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+
+
+/*********************************************************************************************************************************
+* Class UIShortcut implementation. *
+*********************************************************************************************************************************/
+
+void UIShortcut::setScope(const QString &strScope)
+{
+ m_strScope = strScope;
+}
+
+const QString &UIShortcut::scope() const
+{
+ return m_strScope;
+}
+
+void UIShortcut::setDescription(const QString &strDescription)
+{
+ m_strDescription = strDescription;
+}
+
+const QString &UIShortcut::description() const
+{
+ return m_strDescription;
+}
+
+void UIShortcut::setSequences(const QList<QKeySequence> &sequences)
+{
+ m_sequences = sequences;
+}
+
+const QList<QKeySequence> &UIShortcut::sequences() const
+{
+ return m_sequences;
+}
+
+void UIShortcut::setDefaultSequence(const QKeySequence &defaultSequence)
+{
+ m_defaultSequence = defaultSequence;
+}
+
+const QKeySequence &UIShortcut::defaultSequence() const
+{
+ return m_defaultSequence;
+}
+
+void UIShortcut::setStandardSequence(const QKeySequence &standardSequence)
+{
+ m_standardSequence = standardSequence;
+}
+
+const QKeySequence &UIShortcut::standardSequence() const
+{
+ return m_standardSequence;
+}
+
+QString UIShortcut::primaryToNativeText() const
+{
+ return m_sequences.isEmpty() ? QString() : m_sequences.first().toString(QKeySequence::NativeText);
+}
+
+QString UIShortcut::primaryToPortableText() const
+{
+ return m_sequences.isEmpty() ? QString() : m_sequences.first().toString(QKeySequence::PortableText);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIShortcutPool implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIShortcutPool *UIShortcutPool::s_pInstance = 0;
+const QString UIShortcutPool::s_strShortcutKeyTemplate = QString("%1/%2");
+const QString UIShortcutPool::s_strShortcutKeyTemplateRuntime = s_strShortcutKeyTemplate.arg(GUI_Input_MachineShortcuts);
+
+void UIShortcutPool::create()
+{
+ /* Check that instance do NOT exists: */
+ if (s_pInstance)
+ return;
+
+ /* Create instance: */
+ new UIShortcutPool;
+
+ /* Prepare instance: */
+ s_pInstance->prepare();
+}
+
+void UIShortcutPool::destroy()
+{
+ /* Check that instance exists: */
+ if (!s_pInstance)
+ return;
+
+ /* Cleanup instance: */
+ s_pInstance->cleanup();
+
+ /* Delete instance: */
+ delete s_pInstance;
+}
+
+UIShortcut &UIShortcutPool::shortcut(UIActionPool *pActionPool, UIAction *pAction)
+{
+ /* Compose shortcut key: */
+ const QString strShortcutKey(s_strShortcutKeyTemplate.arg(pActionPool->shortcutsExtraDataID(),
+ pAction->shortcutExtraDataID()));
+ /* Return existing if any: */
+ if (m_shortcuts.contains(strShortcutKey))
+ return shortcut(strShortcutKey);
+ /* Create and return new one: */
+ UIShortcut &newShortcut = m_shortcuts[strShortcutKey];
+ newShortcut.setScope(pAction->shortcutScope());
+ newShortcut.setDescription(pAction->name());
+ const QKeySequence &defaultSequence = pAction->defaultShortcut(pActionPool->type());
+ const QKeySequence &standardSequence = pAction->standardShortcut(pActionPool->type());
+ newShortcut.setSequences(QList<QKeySequence>() << defaultSequence << standardSequence);
+ newShortcut.setDefaultSequence(defaultSequence);
+ newShortcut.setStandardSequence(standardSequence);
+ return newShortcut;
+}
+
+UIShortcut &UIShortcutPool::shortcut(const QString &strPoolID, const QString &strActionID)
+{
+ /* Return if present, autocreate if necessary: */
+ return shortcut(s_strShortcutKeyTemplate.arg(strPoolID, strActionID));
+}
+
+void UIShortcutPool::setOverrides(const QMap<QString, QString> &overrides)
+{
+ /* Iterate over all the overrides: */
+ const QList<QString> shortcutKeys = overrides.keys();
+ foreach (const QString &strShortcutKey, shortcutKeys)
+ {
+ /* Make no changes if there is no such shortcut: */
+ if (!m_shortcuts.contains(strShortcutKey))
+ continue;
+ /* Assign overridden sequences to the shortcut: */
+ m_shortcuts[strShortcutKey].setSequences(QList<QKeySequence>() << overrides[strShortcutKey]);
+ }
+ /* Save overrides: */
+ saveOverrides();
+}
+
+void UIShortcutPool::applyShortcuts(UIActionPool *pActionPool)
+{
+ /* For each the action of the passed action-pool: */
+ foreach (UIAction *pAction, pActionPool->actions())
+ {
+ /* Skip menu actions: */
+ if (pAction->type() == UIActionType_Menu)
+ continue;
+
+ /* Compose shortcut key: */
+ const QString strShortcutKey = s_strShortcutKeyTemplate.arg(pActionPool->shortcutsExtraDataID(),
+ pAction->shortcutExtraDataID());
+ /* If shortcut key is already known: */
+ if (m_shortcuts.contains(strShortcutKey))
+ {
+ /* Get corresponding shortcut: */
+ UIShortcut &existingShortcut = m_shortcuts[strShortcutKey];
+ /* Copy the scope from the action to the shortcut: */
+ existingShortcut.setScope(pAction->shortcutScope());
+ /* Copy the description from the action to the shortcut: */
+ existingShortcut.setDescription(pAction->name());
+ /* Copy the sequences from the shortcut to the action: */
+ pAction->setShortcuts(existingShortcut.sequences());
+ pAction->retranslateUi();
+ /* Copy default and standard sequences from the action to the shortcut: */
+ existingShortcut.setDefaultSequence(pAction->defaultShortcut(pActionPool->type()));
+ existingShortcut.setStandardSequence(pAction->standardShortcut(pActionPool->type()));
+ }
+ /* If shortcut key is NOT known yet: */
+ else
+ {
+ /* Create corresponding shortcut: */
+ UIShortcut &newShortcut = m_shortcuts[strShortcutKey];
+ /* Copy the action's default sequence to both the shortcut & the action: */
+ const QKeySequence &defaultSequence = pAction->defaultShortcut(pActionPool->type());
+ const QKeySequence &standardSequence = pAction->standardShortcut(pActionPool->type());
+ newShortcut.setSequences(QList<QKeySequence>() << defaultSequence << standardSequence);
+ newShortcut.setDefaultSequence(defaultSequence);
+ newShortcut.setStandardSequence(standardSequence);
+ pAction->setShortcuts(newShortcut.sequences());
+ pAction->retranslateUi();
+ /* Copy the description from the action to the shortcut: */
+ newShortcut.setScope(pAction->shortcutScope());
+ newShortcut.setDescription(pAction->name());
+ }
+ }
+}
+
+void UIShortcutPool::retranslateUi()
+{
+ /* Translate own defaults: */
+ m_shortcuts[s_strShortcutKeyTemplateRuntime.arg("PopupMenu")]
+ .setDescription(QApplication::translate("UIActionPool", "Popup Menu"));
+}
+
+void UIShortcutPool::sltReloadSelectorShortcuts()
+{
+ /* Clear selector shortcuts first: */
+ const QList<QString> shortcutKeyList = m_shortcuts.keys();
+ foreach (const QString &strShortcutKey, shortcutKeyList)
+ if (strShortcutKey.startsWith(GUI_Input_SelectorShortcuts))
+ m_shortcuts.remove(strShortcutKey);
+
+ /* Load selector defaults: */
+ loadDefaultsFor(GUI_Input_SelectorShortcuts);
+ /* Load selector overrides: */
+ loadOverridesFor(GUI_Input_SelectorShortcuts);
+
+ /* Notify manager shortcuts reloaded: */
+ emit sigManagerShortcutsReloaded();
+}
+
+void UIShortcutPool::sltReloadMachineShortcuts()
+{
+ /* Clear machine shortcuts first: */
+ const QList<QString> shortcutKeyList = m_shortcuts.keys();
+ foreach (const QString &strShortcutKey, shortcutKeyList)
+ if (strShortcutKey.startsWith(GUI_Input_MachineShortcuts))
+ m_shortcuts.remove(strShortcutKey);
+
+ /* Load machine defaults: */
+ loadDefaultsFor(GUI_Input_MachineShortcuts);
+ /* Load machine overrides: */
+ loadOverridesFor(GUI_Input_MachineShortcuts);
+
+ /* Notify runtime shortcuts reloaded: */
+ emit sigRuntimeShortcutsReloaded();
+}
+
+UIShortcutPool::UIShortcutPool()
+{
+ /* Prepare instance: */
+ if (!s_pInstance)
+ s_pInstance = this;
+}
+
+UIShortcutPool::~UIShortcutPool()
+{
+ /* Cleanup instance: */
+ if (s_pInstance == this)
+ s_pInstance = 0;
+}
+
+void UIShortcutPool::prepare()
+{
+ /* Load defaults: */
+ loadDefaults();
+ /* Load overrides: */
+ loadOverrides();
+ /* Prepare connections: */
+ prepareConnections();
+}
+
+void UIShortcutPool::prepareConnections()
+{
+ /* Connect to extra-data signals: */
+ connect(gEDataManager, &UIExtraDataManager::sigSelectorUIShortcutChange,
+ this, &UIShortcutPool::sltReloadSelectorShortcuts);
+ connect(gEDataManager, &UIExtraDataManager::sigRuntimeUIShortcutChange,
+ this, &UIShortcutPool::sltReloadMachineShortcuts);
+}
+
+void UIShortcutPool::loadDefaults()
+{
+ /* Load selector defaults: */
+ loadDefaultsFor(GUI_Input_SelectorShortcuts);
+ /* Load machine defaults: */
+ loadDefaultsFor(GUI_Input_MachineShortcuts);
+}
+
+void UIShortcutPool::loadDefaultsFor(const QString &strPoolExtraDataID)
+{
+ /* Default shortcuts for Selector UI: */
+ if (strPoolExtraDataID == GUI_Input_SelectorShortcuts)
+ {
+ /* Nothing for now.. */
+ }
+ /* Default shortcuts for Runtime UI: */
+ else if (strPoolExtraDataID == GUI_Input_MachineShortcuts)
+ {
+ /* Default shortcut for the Runtime Popup Menu: */
+ m_shortcuts.insert(s_strShortcutKeyTemplateRuntime.arg("PopupMenu"),
+ UIShortcut(QString(),
+ QApplication::translate("UIActionPool", "Popup Menu"),
+ QList<QKeySequence>() << QString("Home"),
+ QString("Home"),
+ QString()));
+ }
+}
+
+void UIShortcutPool::loadOverrides()
+{
+ /* Load selector overrides: */
+ loadOverridesFor(GUI_Input_SelectorShortcuts);
+ /* Load machine overrides: */
+ loadOverridesFor(GUI_Input_MachineShortcuts);
+}
+
+void UIShortcutPool::loadOverridesFor(const QString &strPoolExtraDataID)
+{
+ /* Compose shortcut key template: */
+ const QString strShortcutKeyTemplate(s_strShortcutKeyTemplate.arg(strPoolExtraDataID));
+ /* Iterate over all the overrides: */
+ const QStringList overrides = gEDataManager->shortcutOverrides(strPoolExtraDataID);
+ foreach (const QString &strKeyValuePair, overrides)
+ {
+ /* Make sure override structure is valid: */
+ int iDelimiterPosition = strKeyValuePair.indexOf('=');
+ if (iDelimiterPosition < 0)
+ continue;
+
+ /* Get shortcut ID/sequence: */
+ QString strShortcutExtraDataID = strKeyValuePair.left(iDelimiterPosition);
+ const QString strShortcutSequence = strKeyValuePair.right(strKeyValuePair.length() - iDelimiterPosition - 1);
+
+ // Hack for handling "Save" as "SaveState":
+ if (strShortcutExtraDataID == "Save")
+ strShortcutExtraDataID = "SaveState";
+
+ /* Compose corresponding shortcut key: */
+ const QString strShortcutKey(strShortcutKeyTemplate.arg(strShortcutExtraDataID));
+ /* Modify map with composed key/value: */
+ if (!m_shortcuts.contains(strShortcutKey))
+ m_shortcuts.insert(strShortcutKey,
+ UIShortcut(QString(),
+ QString(),
+ QList<QKeySequence>() << strShortcutSequence,
+ QString(),
+ QString()));
+ else
+ {
+ /* Get corresponding value: */
+ UIShortcut &shortcut = m_shortcuts[strShortcutKey];
+ /* Check if corresponding shortcut overridden by value: */
+ if (shortcut.primaryToPortableText().compare(strShortcutSequence, Qt::CaseInsensitive) != 0)
+ {
+ /* Shortcut unassigned? */
+ if (strShortcutSequence.compare("None", Qt::CaseInsensitive) == 0)
+ shortcut.setSequences(QList<QKeySequence>());
+ /* Or reassigned? */
+ else
+ shortcut.setSequences(QList<QKeySequence>() << strShortcutSequence);
+ }
+ }
+ }
+}
+
+void UIShortcutPool::saveOverrides()
+{
+ /* Load selector overrides: */
+ saveOverridesFor(GUI_Input_SelectorShortcuts);
+ /* Load machine overrides: */
+ saveOverridesFor(GUI_Input_MachineShortcuts);
+}
+
+void UIShortcutPool::saveOverridesFor(const QString &strPoolExtraDataID)
+{
+ /* Compose shortcut prefix: */
+ const QString strShortcutPrefix(s_strShortcutKeyTemplate.arg(strPoolExtraDataID, QString()));
+ /* Populate the list of all the known overrides: */
+ QStringList overrides;
+ const QList<QString> shortcutKeys = m_shortcuts.keys();
+ foreach (const QString &strShortcutKey, shortcutKeys)
+ {
+ /* Check if the key starts from the proper prefix: */
+ if (!strShortcutKey.startsWith(strShortcutPrefix))
+ continue;
+ /* Get corresponding shortcut: */
+ const UIShortcut &shortcut = m_shortcuts[strShortcutKey];
+ /* Check if the sequence for that shortcut differs from default or standard: */
+ if ( shortcut.sequences().contains(shortcut.defaultSequence())
+ || ( !shortcut.standardSequence().isEmpty()
+ && shortcut.sequences().contains(shortcut.standardSequence())))
+ continue;
+ /* Add the shortcut sequence into overrides list: */
+ overrides << QString("%1=%2").arg(QString(strShortcutKey).remove(strShortcutPrefix),
+ shortcut.primaryToPortableText());
+ }
+ /* Save overrides into the extra-data: */
+ uiCommon().virtualBox().SetExtraDataStringList(strPoolExtraDataID, overrides);
+}
+
+UIShortcut &UIShortcutPool::shortcut(const QString &strShortcutKey)
+{
+ return m_shortcuts[strShortcutKey];
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIShortcutPool.h b/src/VBox/Frontends/VirtualBox/src/globals/UIShortcutPool.h
new file mode 100644
index 00000000..c1eae365
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIShortcutPool.h
@@ -0,0 +1,216 @@
+/* $Id: UIShortcutPool.h $ */
+/** @file
+ * VBox Qt GUI - UIShortcutPool class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIShortcutPool_h
+#define FEQT_INCLUDED_SRC_globals_UIShortcutPool_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QKeySequence;
+class QString;
+class UIActionPool;
+class UIAction;
+
+
+/** Shortcut descriptor prototype. */
+class SHARED_LIBRARY_STUFF UIShortcut
+{
+public:
+
+ /** Constructs empty shortcut descriptor. */
+ UIShortcut()
+ : m_strScope(QString())
+ , m_strDescription(QString())
+ , m_sequences(QList<QKeySequence>())
+ , m_defaultSequence(QKeySequence())
+ , m_standardSequence(QKeySequence())
+ {}
+ /** Constructs shortcut descriptor.
+ * @param strScope Brings the shortcut scope.
+ * @param strDescription Brings the shortcut description.
+ * @param sequences Brings the shortcut sequences.
+ * @param defaultSequence Brings the default shortcut sequence.
+ * @param standardSequence Brings the standard shortcut sequence. */
+ UIShortcut(const QString &strScope,
+ const QString &strDescription,
+ const QList<QKeySequence> &sequences,
+ const QKeySequence &defaultSequence,
+ const QKeySequence &standardSequence)
+ : m_strScope(strScope)
+ , m_strDescription(strDescription)
+ , m_sequences(sequences)
+ , m_defaultSequence(defaultSequence)
+ , m_standardSequence(standardSequence)
+ {}
+
+ /** Defines the shortcut @a strScope. */
+ void setScope(const QString &strScope);
+ /** Returns the shortcut scope. */
+ const QString &scope() const;
+
+ /** Defines the shortcut @a strDescription. */
+ void setDescription(const QString &strDescription);
+ /** Returns the shortcut description. */
+ const QString &description() const;
+
+ /** Defines the shortcut @a sequences. */
+ void setSequences(const QList<QKeySequence> &sequences);
+ /** Returns the shortcut sequences. */
+ const QList<QKeySequence> &sequences() const;
+
+ /** Defines the default shortcut @a sequence. */
+ void setDefaultSequence(const QKeySequence &sequence);
+ /** Returns the default shortcut sequence. */
+ const QKeySequence &defaultSequence() const;
+
+ /** Defines the standard shortcut @a sequence. */
+ void setStandardSequence(const QKeySequence &sequence);
+ /** Returns the standard shortcut sequence. */
+ const QKeySequence &standardSequence() const;
+
+ /** Converts primary shortcut sequence to native text. */
+ QString primaryToNativeText() const;
+ /** Converts primary shortcut sequence to portable text. */
+ QString primaryToPortableText() const;
+
+private:
+
+ /** Holds the shortcut scope. */
+ QString m_strScope;
+ /** Holds the shortcut description. */
+ QString m_strDescription;
+ /** Holds the shortcut sequences. */
+ QList<QKeySequence> m_sequences;
+ /** Holds the default shortcut sequence. */
+ QKeySequence m_defaultSequence;
+ /** Holds the standard shortcut sequence. */
+ QKeySequence m_standardSequence;
+};
+
+
+/** QObject extension used as shortcut pool singleton. */
+class SHARED_LIBRARY_STUFF UIShortcutPool : public QIWithRetranslateUI3<QObject>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about Manager UI shortcuts changed. */
+ void sigManagerShortcutsReloaded();
+ /** Notifies about Runtime UI shortcuts changed. */
+ void sigRuntimeShortcutsReloaded();
+
+public:
+
+ /** Returns singleton instance. */
+ static UIShortcutPool *instance() { return s_pInstance; }
+ /** Creates singleton instance. */
+ static void create();
+ /** Destroys singleton instance. */
+ static void destroy();
+
+ /** Returns shortcuts of particular @a pActionPool for specified @a pAction. */
+ UIShortcut &shortcut(UIActionPool *pActionPool, UIAction *pAction);
+ /** Returns shortcuts of action-pool with @a strPoolID for action with @a strActionID. */
+ UIShortcut &shortcut(const QString &strPoolID, const QString &strActionID);
+ /** Returns all the shortcuts. */
+ const QMap<QString, UIShortcut> &shortcuts() const { return m_shortcuts; }
+ /** Defines shortcut overrides. */
+ void setOverrides(const QMap<QString, QString> &overrides);
+
+ /** Applies shortcuts for specified @a pActionPool. */
+ void applyShortcuts(UIActionPool *pActionPool);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Reloads Selector UI shortcuts. */
+ void sltReloadSelectorShortcuts();
+ /** Reloads Runtime UI shortcuts. */
+ void sltReloadMachineShortcuts();
+
+private:
+
+ /** Constructs shortcut pool. */
+ UIShortcutPool();
+ /** Destructs shortcut pool. */
+ ~UIShortcutPool();
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups all. */
+ void cleanup() {}
+
+ /** Loads defaults. */
+ void loadDefaults();
+ /** Loads defaults for pool with specified @a strPoolExtraDataID. */
+ void loadDefaultsFor(const QString &strPoolExtraDataID);
+ /** Loads overrides. */
+ void loadOverrides();
+ /** Loads overrides for pool with specified @a strPoolExtraDataID. */
+ void loadOverridesFor(const QString &strPoolExtraDataID);
+ /** Saves overrides. */
+ void saveOverrides();
+ /** Saves overrides for pool with specified @a strPoolExtraDataID. */
+ void saveOverridesFor(const QString &strPoolExtraDataID);
+
+ /** Returns shortcut with specified @a strShortcutKey. */
+ UIShortcut &shortcut(const QString &strShortcutKey);
+
+ /** Holds the singleton instance. */
+ static UIShortcutPool *s_pInstance;
+ /** Shortcut key template. */
+ static const QString s_strShortcutKeyTemplate;
+ /** Shortcut key template for Runtime UI. */
+ static const QString s_strShortcutKeyTemplateRuntime;
+
+ /** Holds the pool shortcuts. */
+ QMap<QString, UIShortcut> m_shortcuts;
+};
+
+/** Singleton Shortcut Pool 'official' name. */
+#define gShortcutPool UIShortcutPool::instance()
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIShortcutPool_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIStarter.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIStarter.cpp
new file mode 100644
index 00000000..2c173215
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIStarter.cpp
@@ -0,0 +1,167 @@
+/* $Id: UIStarter.cpp $ */
+/** @file
+ * VBox Qt GUI - UIStarter class implementation.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+#include "UIStarter.h"
+#ifndef VBOX_RUNTIME_UI
+# include "UIVirtualBoxManager.h"
+#else
+# include "UIMachine.h"
+# include "UISession.h"
+#endif
+
+
+/* static */
+UIStarter *UIStarter::s_pInstance = 0;
+
+/* static */
+void UIStarter::create()
+{
+ /* Pretect versus double 'new': */
+ if (s_pInstance)
+ return;
+
+ /* Create instance: */
+ new UIStarter;
+}
+
+/* static */
+void UIStarter::destroy()
+{
+ /* Pretect versus double 'delete': */
+ if (!s_pInstance)
+ return;
+
+ /* Destroy instance: */
+ delete s_pInstance;
+}
+
+UIStarter::UIStarter()
+{
+ /* Assign instance: */
+ s_pInstance = this;
+}
+
+UIStarter::~UIStarter()
+{
+ /* Unassign instance: */
+ s_pInstance = 0;
+}
+
+void UIStarter::init()
+{
+ /* Listen for UICommon signals: */
+ connect(&uiCommon(), &UICommon::sigAskToRestartUI,
+ this, &UIStarter::sltRestartUI);
+ connect(&uiCommon(), &UICommon::sigAskToCloseUI,
+ this, &UIStarter::sltCloseUI);
+}
+
+void UIStarter::deinit()
+{
+ /* Listen for UICommon signals no more: */
+ disconnect(&uiCommon(), &UICommon::sigAskToRestartUI,
+ this, &UIStarter::sltRestartUI);
+ disconnect(&uiCommon(), &UICommon::sigAskToCloseUI,
+ this, &UIStarter::sltCloseUI);
+}
+
+void UIStarter::sltStartUI()
+{
+ /* Exit if UICommon is not valid: */
+ if (!uiCommon().isValid())
+ return;
+
+#ifndef VBOX_RUNTIME_UI
+
+ /* Make sure Selector UI is permitted, quit if not: */
+ if (gEDataManager->guiFeatureEnabled(GUIFeatureType_NoSelector))
+ {
+ msgCenter().cannotStartSelector();
+ return QApplication::quit();
+ }
+
+ /* Create/show manager-window: */
+ UIVirtualBoxManager::create();
+
+# ifdef VBOX_BLEEDING_EDGE
+ /* Show EXPERIMENTAL BUILD warning: */
+ UINotificationMessage::remindAboutExperimentalBuild();
+# else /* !VBOX_BLEEDING_EDGE */
+# ifndef DEBUG
+ /* Show BETA warning if necessary: */
+ const QString vboxVersion(uiCommon().virtualBox().GetVersion());
+ if ( vboxVersion.contains("BETA")
+ && gEDataManager->preventBetaBuildWarningForVersion() != vboxVersion)
+ UINotificationMessage::remindAboutBetaBuild();
+# endif /* !DEBUG */
+# endif /* !VBOX_BLEEDING_EDGE */
+
+#else /* VBOX_RUNTIME_UI */
+
+ /* Make sure Runtime UI is even possible, quit if not: */
+ if (uiCommon().managedVMUuid().isNull())
+ {
+ msgCenter().cannotStartRuntime();
+ return QApplication::quit();
+ }
+
+ /* Make sure machine is started, quit if not: */
+ if (!UIMachine::startMachine(uiCommon().managedVMUuid()))
+ return QApplication::quit();
+
+#endif /* VBOX_RUNTIME_UI */
+}
+
+void UIStarter::sltRestartUI()
+{
+#ifndef VBOX_RUNTIME_UI
+ /* Recreate/show manager-window: */
+ UIVirtualBoxManager::destroy();
+ UIVirtualBoxManager::create();
+#endif
+}
+
+void UIStarter::sltCloseUI()
+{
+#ifndef VBOX_RUNTIME_UI
+ /* Destroy Manager UI: */
+ if (gpManager)
+ UIVirtualBoxManager::destroy();
+#else
+ /* Destroy Runtime UI: */
+ if (gpMachine)
+ UIMachine::destroy();
+#endif
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIStarter.h b/src/VBox/Frontends/VirtualBox/src/globals/UIStarter.h
new file mode 100644
index 00000000..89992cb1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIStarter.h
@@ -0,0 +1,81 @@
+/* $Id: UIStarter.h $ */
+/** @file
+ * VBox Qt GUI - UIStarter class declaration.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIStarter_h
+#define FEQT_INCLUDED_SRC_globals_UIStarter_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/** QObject subclass allowing to control GUI part
+ * of VirtualBox application in sync/async modes. */
+class UIStarter : public QObject
+{
+ Q_OBJECT;
+
+ /** Constructs UI starter. */
+ UIStarter();
+ /** Destructs UI starter. */
+ virtual ~UIStarter();
+
+public:
+
+ /** Returns the singleton UI starter instance. */
+ static UIStarter *instance() { return s_pInstance; }
+
+ /** Create the singleton UI starter instance. */
+ static void create();
+ /** Create the singleton UI starter instance. */
+ static void destroy();
+
+ /** Init UICommon connections. */
+ void init();
+ /** Deinit UICommon connections. */
+ void deinit();
+
+private slots:
+
+ /** Starts corresponding part of the UI. */
+ void sltStartUI();
+ /** Restarts corresponding part of the UI. */
+ void sltRestartUI();
+ /** Closes corresponding part of the UI. */
+ void sltCloseUI();
+
+private:
+
+ /** Holds the singleton UI starter instance. */
+ static UIStarter *s_pInstance;
+};
+
+/** Singleton UI starter 'official' name. */
+#define gStarter UIStarter::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIStarter_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UITask.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UITask.cpp
new file mode 100644
index 00000000..df67d813
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UITask.cpp
@@ -0,0 +1,37 @@
+/* $Id: UITask.cpp $ */
+/** @file
+ * VBox Qt GUI - UITask class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UITask.h"
+
+void UITask::start()
+{
+ /* Run task: */
+ run();
+ /* Notify listeners: */
+ emit sigComplete(this);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UITask.h b/src/VBox/Frontends/VirtualBox/src/globals/UITask.h
new file mode 100644
index 00000000..91f0f576
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UITask.h
@@ -0,0 +1,83 @@
+/* $Id: UITask.h $ */
+/** @file
+ * VBox Qt GUI - UITask class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UITask_h
+#define FEQT_INCLUDED_SRC_globals_UITask_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QObject extension used as worker-thread task interface.
+ * Describes task to be handled by the UIThreadPool object. */
+class SHARED_LIBRARY_STUFF UITask : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a pTask complete. */
+ void sigComplete(UITask *pTask);
+
+public:
+
+ /** Task types. */
+ enum Type
+ {
+ Type_MediumEnumeration = 1,
+ Type_DetailsPopulation = 2,
+ Type_CloudListMachines = 3,
+ Type_CloudRefreshMachineInfo = 4,
+ Type_CloudGetSettingsForm = 5,
+ };
+
+ /** Constructs the task of passed @a enmType. */
+ UITask(UITask::Type enmType) : m_enmType(enmType) {}
+
+ /** Returns the type of the task. */
+ UITask::Type type() const { return m_enmType; }
+
+ /** Starts the task. */
+ void start();
+
+protected:
+
+ /** Contains the abstract task body. */
+ virtual void run() = 0;
+
+private:
+
+ /** Holds the type of the task. */
+ const UITask::Type m_enmType;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UITask_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UITextTable.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UITextTable.cpp
new file mode 100644
index 00000000..46f3ed01
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UITextTable.cpp
@@ -0,0 +1,152 @@
+/* $Id: UITextTable.cpp $ */
+/** @file
+ * VBox Qt GUI - UITextTable class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleObject>
+#include <QRegularExpression>
+
+/* GUI includes: */
+#include "UITextTable.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QAccessibleObject extension used as an accessibility interface for UITextTableLine. */
+class UIAccessibilityInterfaceForUITextTableLine : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating UITextTableLine accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UITextTableLine"))
+ return new UIAccessibilityInterfaceForUITextTableLine(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ UIAccessibilityInterfaceForUITextTableLine(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(line(), 0);
+
+ /* Always return parent object: */
+ return QAccessible::queryAccessibleInterface(line()->parent());
+ }
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE { return 0; }
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE { Q_UNUSED(iIndex); return 0; }
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE { Q_UNUSED(pChild); return -1; }
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE
+ {
+ /* Make sure line still alive: */
+ AssertPtrReturn(line(), QString());
+
+ /* Return the description: */
+ if (enmTextRole == QAccessible::Description)
+ {
+ const QString str1 = line()->string1();
+ QString str2 = line()->string2();
+ if (!str2.isEmpty())
+ str2.remove(QRegularExpression("<a[^>]*>|</a>"));
+ return str2.isEmpty() ? str1 : QString("%1: %2").arg(str1, str2);
+ }
+
+ /* Null-string by default: */
+ return QString();
+ }
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE
+ {
+ /* Return the role: */
+ return QAccessible::ListItem;
+ }
+
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE
+ {
+ /* Return the state: */
+ return QAccessible::State();
+ }
+
+private:
+
+ /** Returns corresponding UITextTableLine. */
+ UITextTableLine *line() const { return qobject_cast<UITextTableLine*>(object()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class UITextTableLine implementation. *
+*********************************************************************************************************************************/
+
+UITextTableLine::UITextTableLine(const QString &str1, const QString &str2, QObject *pParent /* = 0 */)
+ : QObject(pParent)
+ , m_str1(str1)
+ , m_str2(str2)
+{
+ /* Install UITextTableLine accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUITextTableLine::pFactory);
+}
+
+UITextTableLine::UITextTableLine(const UITextTableLine &other)
+ : QObject(other.parent())
+ , m_str1(other.string1())
+ , m_str2(other.string2())
+{
+ /* Install UITextTableLine accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUITextTableLine::pFactory);
+}
+
+UITextTableLine &UITextTableLine::operator=(const UITextTableLine &other)
+{
+ setParent(other.parent());
+ set1(other.string1());
+ set2(other.string2());
+ return *this;
+}
+
+bool UITextTableLine::operator==(const UITextTableLine &other) const
+{
+ return string1() == other.string1()
+ && string2() == other.string2();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UITextTable.h b/src/VBox/Frontends/VirtualBox/src/globals/UITextTable.h
new file mode 100644
index 00000000..e5206aba
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UITextTable.h
@@ -0,0 +1,85 @@
+/* $Id: UITextTable.h $ */
+/** @file
+ * VBox Qt GUI - UITextTable class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UITextTable_h
+#define FEQT_INCLUDED_SRC_globals_UITextTable_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QString>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** QObject extension used as an
+ * accessible wrapper for QString pairs. */
+class SHARED_LIBRARY_STUFF UITextTableLine : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs text-table line passing @a pParent to the base-class.
+ * @param str1 Brings the 1st table string.
+ * @param str2 Brings the 2nd table string. */
+ UITextTableLine(const QString &str1, const QString &str2, QObject *pParent = 0);
+
+ /** Constructs text-table line on the basis of passed @a other. */
+ UITextTableLine(const UITextTableLine &other);
+
+ /** Assigns @a other to this. */
+ UITextTableLine &operator=(const UITextTableLine &other);
+
+ /** Compares @a other to this. */
+ bool operator==(const UITextTableLine &other) const;
+
+ /** Defines 1st table @a strString. */
+ void set1(const QString &strString) { m_str1 = strString; }
+ /** Returns 1st table string. */
+ const QString &string1() const { return m_str1; }
+
+ /** Defines 2nd table @a strString. */
+ void set2(const QString &strString) { m_str2 = strString; }
+ /** Returns 2nd table string. */
+ const QString &string2() const { return m_str2; }
+
+private:
+
+ /** Holds the 1st table string. */
+ QString m_str1;
+ /** Holds the 2nd table string. */
+ QString m_str2;
+};
+
+/** Defines the list of UITextTableLine instances. */
+typedef QList<UITextTableLine> UITextTable;
+Q_DECLARE_METATYPE(UITextTable);
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UITextTable_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIThreadPool.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIThreadPool.cpp
new file mode 100644
index 00000000..3b6b574a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIThreadPool.cpp
@@ -0,0 +1,343 @@
+/* $Id: UIThreadPool.cpp $ */
+/** @file
+ * VBox Qt GUI - UIThreadPool class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QThread>
+
+/* GUI includes: */
+#include "COMDefs.h"
+#include "UIDefs.h"
+#include "UITask.h"
+#include "UIThreadPool.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/** QThread extension used as worker-thread.
+ * Capable of executing COM-related tasks. */
+class UIThreadWorker : public QThread
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a pWorker finished. */
+ void sigFinished(UIThreadWorker *pWorker);
+
+public:
+
+ /** Constructs worker-thread for parent worker-thread @a pPool.
+ * @param iIndex Brings worker-thread index within the worker-thread pool registry. */
+ UIThreadWorker(UIThreadPool *pPool, int iIndex);
+
+ /** Returns worker-thread index within the worker-thread pool registry. */
+ int index() const { return m_iIndex; }
+
+ /** Disables sigFinished signal, for optimizing worker-thread pool termination. */
+ void setNoFinishedSignal() { m_fNoFinishedSignal = true; }
+
+private:
+
+ /** Contains the worker-thread body. */
+ void run();
+
+ /** Holds the worker-thread pool reference. */
+ UIThreadPool *m_pPool;
+
+ /** Holds the worker-thread index within the worker-thread pool registry. */
+ int m_iIndex;
+
+ /** Holds whether sigFinished signal should be emitted or not. */
+ bool m_fNoFinishedSignal;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIThreadPool implementation. *
+*********************************************************************************************************************************/
+
+UIThreadPool::UIThreadPool(ulong cMaxWorkers /* = 3 */, ulong cMsWorkerIdleTimeout /* = 5000 */)
+ : m_cMsIdleTimeout(cMsWorkerIdleTimeout)
+ , m_workers(cMaxWorkers)
+ , m_cWorkers(0)
+ , m_cIdleWorkers(0)
+ , m_fTerminating(false)
+{
+}
+
+UIThreadPool::~UIThreadPool()
+{
+ /* Set termination status: */
+ setTerminating();
+
+ /* Lock initially: */
+ m_everythingLocker.lock();
+
+ /* Cleanup all the workers: */
+ for (int idxWorker = 0; idxWorker < m_workers.size(); ++idxWorker)
+ {
+ /* Acquire the worker: */
+ UIThreadWorker *pWorker = m_workers.at(idxWorker);
+ /* Remove it from the registry: */
+ m_workers[idxWorker] = 0;
+
+ /* Clean up the worker, if there was one: */
+ if (pWorker)
+ {
+ /* Decrease the number of workers: */
+ --m_cWorkers;
+ /* Unlock temporary to let the worker finish: */
+ m_everythingLocker.unlock();
+ /* Wait for the worker to finish: */
+ pWorker->wait();
+ /* Lock again: */
+ m_everythingLocker.lock();
+ /* Delete the worker finally: */
+ delete pWorker;
+ }
+ }
+
+ /* Cleanup all the tasks: */
+ qDeleteAll(m_pendingTasks);
+ qDeleteAll(m_executingTasks);
+ m_pendingTasks.clear();
+ m_executingTasks.clear();
+
+ /* Unlock finally: */
+ m_everythingLocker.unlock();
+}
+
+bool UIThreadPool::isTerminating() const
+{
+ /* Lock initially: */
+ m_everythingLocker.lock();
+
+ /* Acquire termination-flag: */
+ bool fTerminating = m_fTerminating;
+
+ /* Unlock finally: */
+ m_everythingLocker.unlock();
+
+ /* Return termination-flag: */
+ return fTerminating;
+}
+
+void UIThreadPool::setTerminating()
+{
+ /* Lock initially: */
+ m_everythingLocker.lock();
+
+ /* Assign termination-flag: */
+ m_fTerminating = true;
+
+ /* Tell all threads to NOT queue any termination signals: */
+ for (int idxWorker = 0; idxWorker < m_workers.size(); ++idxWorker)
+ {
+ UIThreadWorker *pWorker = m_workers.at(idxWorker);
+ if (pWorker)
+ pWorker->setNoFinishedSignal();
+ }
+
+ /* Wake up all idle worker threads: */
+ m_taskCondition.wakeAll();
+
+ /* Unlock finally: */
+ m_everythingLocker.unlock();
+}
+
+void UIThreadPool::enqueueTask(UITask *pTask)
+{
+ /* Do nothing if terminating: */
+ AssertReturnVoid(!isTerminating());
+
+ /* Prepare task: */
+ connect(pTask, &UITask::sigComplete,
+ this, &UIThreadPool::sltHandleTaskComplete, Qt::QueuedConnection);
+
+ /* Lock initially: */
+ m_everythingLocker.lock();
+
+ /* Put the task into the queue: */
+ m_pendingTasks.enqueue(pTask);
+
+ /* Wake up an idle worker if we got one: */
+ if (m_cIdleWorkers > 0)
+ {
+ m_taskCondition.wakeOne();
+ }
+ /* No idle worker threads, should we create a new one? */
+ else if (m_cWorkers < m_workers.size())
+ {
+ /* Find free slot: */
+ int idxFirstUnused = m_workers.size();
+ while (idxFirstUnused-- > 0)
+ if (m_workers.at(idxFirstUnused) == 0)
+ {
+ /* Prepare the new worker: */
+ UIThreadWorker *pWorker = new UIThreadWorker(this, idxFirstUnused);
+ connect(pWorker, &UIThreadWorker::sigFinished,
+ this, &UIThreadPool::sltHandleWorkerFinished, Qt::QueuedConnection);
+ m_workers[idxFirstUnused] = pWorker;
+ ++m_cWorkers;
+
+ /* And start it: */
+ pWorker->start();
+ break;
+ }
+ }
+ /* else: wait for some worker to complete
+ * whatever it's busy with and jump to it. */
+
+ /* Unlock finally: */
+ m_everythingLocker.unlock();
+}
+
+UITask *UIThreadPool::dequeueTask(UIThreadWorker *pWorker)
+{
+ /* Lock initially: */
+ m_everythingLocker.lock();
+
+ /* Dequeue a task, watching out for terminations.
+ * For optimal efficiency in enqueueTask() we keep count of idle threads.
+ * If the wait times out, we'll return 0 and terminate the thread. */
+ bool fIdleTimedOut = false;
+ while (!m_fTerminating)
+ {
+ /* Make sure that worker has proper index: */
+ Assert(m_workers.at(pWorker->index()) == pWorker);
+
+ /* Dequeue task if there is one: */
+ if (!m_pendingTasks.isEmpty())
+ {
+ UITask *pTask = m_pendingTasks.dequeue();
+ if (pTask)
+ {
+ /* Put into the set of executing tasks: */
+ m_executingTasks << pTask;
+
+ /* Unlock finally: */
+ m_everythingLocker.unlock();
+
+ /* Return dequeued task: */
+ return pTask;
+ }
+ }
+
+ /* If we timed out already, then quit the worker thread. To prevent a
+ * race between enqueueTask and the queue removal of the thread from
+ * the workers vector, we remove it here already. (This does not apply
+ * to the termination scenario.) */
+ if (fIdleTimedOut)
+ {
+ m_workers[pWorker->index()] = 0;
+ --m_cWorkers;
+ break;
+ }
+
+ /* Wait for a task or timeout: */
+ ++m_cIdleWorkers;
+ fIdleTimedOut = !m_taskCondition.wait(&m_everythingLocker, m_cMsIdleTimeout);
+ --m_cIdleWorkers;
+ }
+
+ /* Unlock finally: */
+ m_everythingLocker.unlock();
+
+ /* Return 0 by default: */
+ return 0;
+}
+
+void UIThreadPool::sltHandleTaskComplete(UITask *pTask)
+{
+ /* Skip on termination: */
+ if (isTerminating())
+ return;
+
+ /* Notify listeners: */
+ emit sigTaskComplete(pTask);
+
+ /* Lock initially: */
+ m_everythingLocker.lock();
+
+ /* Delete task finally: */
+ if ( !m_executingTasks.contains(pTask)
+ || !m_executingTasks.remove(pTask))
+ AssertMsgFailed(("Unable to find or remove complete task!"));
+ delete pTask;
+
+ /* Unlock finally: */
+ m_everythingLocker.unlock();
+}
+
+void UIThreadPool::sltHandleWorkerFinished(UIThreadWorker *pWorker)
+{
+ /* Wait for the thread to finish completely, then delete the thread
+ * object. We have already removed the thread from the workers vector.
+ * Note! We don't want to use 'this' here, in case it's invalid. */
+ pWorker->wait();
+ delete pWorker;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIThreadWorker implementation. *
+*********************************************************************************************************************************/
+
+UIThreadWorker::UIThreadWorker(UIThreadPool *pPool, int iIndex)
+ : m_pPool(pPool)
+ , m_iIndex(iIndex)
+ , m_fNoFinishedSignal(false)
+{
+}
+
+void UIThreadWorker::run()
+{
+ /* Initialize COM: */
+ COMBase::InitializeCOM(false);
+
+ /* Try get a task from the pool queue: */
+ while (UITask *pTask = m_pPool->dequeueTask(this))
+ {
+ /* Process the task if we are not terminating.
+ * Please take into account tasks are cleared by the UIThreadPool
+ * after all listeners notified about task is complete and handled it. */
+ if (!m_pPool->isTerminating())
+ pTask->start();
+ }
+
+ /* Cleanup COM: */
+ COMBase::CleanupCOM();
+
+ /* Queue a signal for the pool to do thread cleanup, unless the pool is
+ already terminating and doesn't need the signal. */
+ if (!m_fNoFinishedSignal)
+ emit sigFinished(this);
+}
+
+
+#include "UIThreadPool.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIThreadPool.h b/src/VBox/Frontends/VirtualBox/src/globals/UIThreadPool.h
new file mode 100644
index 00000000..878cfbb5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIThreadPool.h
@@ -0,0 +1,127 @@
+/* $Id: UIThreadPool.h $ */
+/** @file
+ * VBox Qt GUI - UIThreadPool class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIThreadPool_h
+#define FEQT_INCLUDED_SRC_globals_UIThreadPool_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMutex>
+#include <QObject>
+#include <QQueue>
+#include <QSet>
+#include <QVector>
+#include <QWaitCondition>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class UITask;
+class UIThreadWorker;
+
+/** QObject extension used as worker-thread pool.
+ * Schedules COM-related GUI tasks to multiple worker-threads. */
+class SHARED_LIBRARY_STUFF UIThreadPool : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a pTask complete. */
+ void sigTaskComplete(UITask *pTask);
+
+public:
+
+ /** Constructs worker-thread pool.
+ * @param cMaxWorkers Brings the maximum amount of worker-threads.
+ * @param cMsWorkerIdleTimeout Brings the maximum amount of time (in ms) which
+ * pool will wait for the worker-thread on cleanup. */
+ UIThreadPool(ulong cMaxWorkers = 3, ulong cMsWorkerIdleTimeout = 5000);
+ /** Destructs worker-thread pool. */
+ virtual ~UIThreadPool() RT_OVERRIDE;
+
+ /** Returns whether the 'termination sequence' is started. */
+ bool isTerminating() const;
+ /** Defines that the 'termination sequence' is started. */
+ void setTerminating();
+
+ /** Enqueues @a pTask into the task-queue. */
+ void enqueueTask(UITask *pTask);
+ /** Returns dequeued top-most task from the task-queue. */
+ UITask *dequeueTask(UIThreadWorker *pWorker);
+
+private slots:
+
+ /** Handles @a pTask complete signal. */
+ void sltHandleTaskComplete(UITask *pTask);
+
+ /** Handles @a pWorker finished signal. */
+ void sltHandleWorkerFinished(UIThreadWorker *pWorker);
+
+private:
+
+ /** @name Worker-thread stuff.
+ * @{ */
+ /** Holds the maximum amount of time (in ms) which
+ * pool will wait for the worker-thread on cleanup. */
+ const ulong m_cMsIdleTimeout;
+ /** Holds the vector of worker-threads. */
+ QVector<UIThreadWorker*> m_workers;
+ /** Holds the number of worker-threads.
+ * @remarks We cannot use the vector size since it may contain 0 pointers. */
+ int m_cWorkers;
+ /** Holds the number of idle worker-threads. */
+ int m_cIdleWorkers;
+ /** Holds whether the 'termination sequence' is started
+ * and all worker-threads should terminate ASAP. */
+ bool m_fTerminating;
+ /** @} */
+
+ /** @name Task stuff
+ * @{ */
+ /** Holds the queue of pending tasks. */
+ QQueue<UITask*> m_pendingTasks;
+ /** Holds the set of executing tasks. */
+ QSet<UITask*> m_executingTasks;
+ /** Holds the condition variable that gets signalled when
+ * queuing a new task and there are idle worker threads around.
+ * @remarks Idle threads sits in dequeueTask waiting for this.
+ * Thus on thermination, setTerminating() will send a
+ * broadcast signal to wake up all workers (after
+ * setting m_fTerminating of course). */
+ QWaitCondition m_taskCondition;
+ /** @} */
+
+ /** Holds the guard mutex object protecting
+ * all the inter-thread variables. */
+ mutable QMutex m_everythingLocker;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIThreadPool_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UITranslator.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UITranslator.cpp
new file mode 100644
index 00000000..1b8900e4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UITranslator.cpp
@@ -0,0 +1,904 @@
+/* $Id: UITranslator.cpp $ */
+/** @file
+ * VBox Qt GUI - UITranslator class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QDir>
+#include <QKeySequence>
+#ifdef Q_OS_UNIX
+# include <QLibraryInfo>
+#endif
+#include <QRegularExpression>
+#include <QRegExp>
+
+/* GUI includes: */
+#include "UIConverter.h"
+#include "UIMessageCenter.h"
+#include "UITranslator.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+#include <iprt/path.h>
+#ifdef Q_OS_UNIX
+# include <iprt/env.h>
+#endif
+
+/* External includes: */
+#include <math.h>
+
+
+/** Port config cache. */
+struct PortConfig
+{
+ const char *name;
+ const ulong IRQ;
+ const ulong IOBase;
+};
+
+/** Known port config COM ports. */
+static const PortConfig kComKnownPorts[] =
+{
+ { "COM1", 4, 0x3F8 },
+ { "COM2", 3, 0x2F8 },
+ { "COM3", 4, 0x3E8 },
+ { "COM4", 3, 0x2E8 },
+ /* Must not contain an element with IRQ=0 and IOBase=0 used to cause
+ * toCOMPortName() to return the "User-defined" string for these values. */
+};
+
+
+/* static */
+UITranslator *UITranslator::s_pTranslator = 0;
+bool UITranslator::s_fTranslationInProgress = false;
+QString UITranslator::s_strLoadedLanguageId = UITranslator::vboxBuiltInLanguageName();
+
+/* static */
+void UITranslator::loadLanguage(const QString &strLangId /* = QString() */)
+{
+ QString strEffectiveLangId = strLangId.isEmpty()
+ ? systemLanguageId()
+ : strLangId;
+ QString strLanguageFileName;
+ QString strSelectedLangId = vboxBuiltInLanguageName();
+
+ /* If C is selected we change it temporary to en. This makes sure any extra
+ * "en" translation file will be loaded. This is necessary for loading the
+ * plural forms of some of our translations. */
+ bool fResetToC = false;
+ if (strEffectiveLangId == "C")
+ {
+ strEffectiveLangId = "en";
+ fResetToC = true;
+ }
+
+ char szNlsPath[RTPATH_MAX];
+ int rc;
+
+ rc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
+ AssertRC(rc);
+
+ QString strNlsPath = QString(szNlsPath) + vboxLanguageSubDirectory();
+ QDir nlsDir(strNlsPath);
+
+ Assert(!strEffectiveLangId.isEmpty());
+ if (!strEffectiveLangId.isEmpty() && strEffectiveLangId != vboxBuiltInLanguageName())
+ {
+ QRegExp regExp(vboxLanguageIdRegExp());
+ int iPos = regExp.indexIn(strEffectiveLangId);
+ /* The language ID should match the regexp completely: */
+ AssertReturnVoid(iPos == 0);
+
+ QString strStrippedLangId = regExp.cap(2);
+
+ if (nlsDir.exists(vboxLanguageFileBase() + strEffectiveLangId + vboxLanguageFileExtension()))
+ {
+ strLanguageFileName = nlsDir.absoluteFilePath(vboxLanguageFileBase() +
+ strEffectiveLangId +
+ vboxLanguageFileExtension());
+ strSelectedLangId = strEffectiveLangId;
+ }
+ else if (nlsDir.exists(vboxLanguageFileBase() + strStrippedLangId + vboxLanguageFileExtension()))
+ {
+ strLanguageFileName = nlsDir.absoluteFilePath(vboxLanguageFileBase() +
+ strStrippedLangId +
+ vboxLanguageFileExtension());
+ strSelectedLangId = strStrippedLangId;
+ }
+ else
+ {
+ /* Never complain when the default language is requested. In any
+ * case, if no explicit language file exists, we will simply
+ * fall-back to English (built-in). */
+ if (!strLangId.isNull() && strEffectiveLangId != "en")
+ msgCenter().cannotFindLanguage(strEffectiveLangId, strNlsPath);
+ /* strSelectedLangId remains built-in here: */
+ AssertReturnVoid(strSelectedLangId == vboxBuiltInLanguageName());
+ }
+ }
+
+ /* Lock listener: */
+ s_fTranslationInProgress = true;
+ /* A list of translators to install: */
+ QList<QTranslator*> translators;
+
+ /* Delete the old translator if there is one: */
+ if (s_pTranslator)
+ {
+ /* QTranslator destructor will call qApp->removeTranslator() for
+ * us. It will also delete all its child translations we attach to it
+ * below, so we don't have to care about them specially. */
+ delete s_pTranslator;
+ }
+
+ /* Load new language files: */
+ s_pTranslator = new UITranslator(qApp);
+ Assert(s_pTranslator);
+ bool fLoadOk = true;
+ if (s_pTranslator)
+ {
+ if (strSelectedLangId != vboxBuiltInLanguageName())
+ {
+ Assert(!strLanguageFileName.isNull());
+ fLoadOk = s_pTranslator->loadFile(strLanguageFileName);
+ }
+ /* We install the translator in any case: on failure, this will
+ * activate an empty translator that will give us English (built-in): */
+ translators << s_pTranslator;
+ }
+ else
+ fLoadOk = false;
+
+ if (fLoadOk)
+ s_strLoadedLanguageId = strSelectedLangId;
+ else
+ {
+ msgCenter().cannotLoadLanguage(strLanguageFileName);
+ s_strLoadedLanguageId = vboxBuiltInLanguageName();
+ }
+
+ /* Try to load the corresponding Qt translation: */
+ if (languageId() != vboxBuiltInLanguageName() && languageId() != "en")
+ {
+#ifdef Q_OS_UNIX
+ // We use system installations of Qt on Linux systems, so first, try
+ // to load the Qt translation from the system location.
+ strLanguageFileName = QLibraryInfo::location(QLibraryInfo::TranslationsPath) + "/qt_" +
+ languageId() + vboxLanguageFileExtension();
+ QTranslator *pQtSysTr = new QTranslator(s_pTranslator);
+ Assert(pQtSysTr);
+ if (pQtSysTr && pQtSysTr->load(strLanguageFileName))
+ translators << pQtSysTr;
+ // Note that the Qt translation supplied by Oracle is always loaded
+ // afterwards to make sure it will take precedence over the system
+ // translation (it may contain more decent variants of translation
+ // that better correspond to VirtualBox UI). We need to load both
+ // because a newer version of Qt may be installed on the user computer
+ // and the Oracle version may not fully support it. We don't do it on
+ // Win32 because we supply a Qt library there and therefore the
+ // Oracle translation is always the best one. */
+#endif
+ strLanguageFileName = nlsDir.absoluteFilePath(QString("qt_") +
+ languageId() +
+ vboxLanguageFileExtension());
+ QTranslator *pQtTr = new QTranslator(s_pTranslator);
+ Assert(pQtTr);
+ if (pQtTr && (fLoadOk = pQtTr->load(strLanguageFileName)))
+ translators << pQtTr;
+ /* The below message doesn't fit 100% (because it's an additional
+ * language and the main one won't be reset to built-in on failure)
+ * but the load failure is so rare here that it's not worth a separate
+ * message (but still, having something is better than having none) */
+ if (!fLoadOk && !strLangId.isNull())
+ msgCenter().cannotLoadLanguage(strLanguageFileName);
+ }
+ if (fResetToC)
+ s_strLoadedLanguageId = vboxBuiltInLanguageName();
+#ifdef VBOX_WS_MAC
+ // Qt doesn't translate the items in the Application menu initially.
+ // Manually trigger an update.
+ ::darwinRetranslateAppMenu();
+#endif
+
+ /* Iterate through all the translators: */
+ for (int i = 0; i < translators.size(); ++i)
+ {
+ /* Unlock listener before the last one translator: */
+ if (i == translators.size() - 1)
+ {
+ QCoreApplication::sendPostedEvents(0, QEvent::LanguageChange);
+ s_fTranslationInProgress = false;
+ }
+
+ /* Install current one: */
+ qApp->installTranslator(translators.at(i));
+ }
+
+ /* Unlock listener in case if it's still locked: */
+ s_fTranslationInProgress = false;
+}
+
+/* static */
+QString UITranslator::vboxLanguageSubDirectory()
+{
+ return "/nls";
+}
+
+/* static */
+QString UITranslator::vboxLanguageFileBase()
+{
+ return "VirtualBox_";
+}
+
+/* static */
+QString UITranslator::vboxLanguageFileExtension()
+{
+ return ".qm";
+}
+
+/* static */
+QString UITranslator::vboxLanguageIdRegExp()
+{
+ return "(([a-z]{2})(?:_([A-Z]{2}))?)|(C)";
+}
+
+/* static */
+QString UITranslator::vboxBuiltInLanguageName()
+{
+ return "C";
+}
+
+/* static */
+QString UITranslator::languageId()
+{
+ /* Note that it may not match with UIExtraDataManager::languageId() if the specified language cannot be loaded.
+ *
+ * If the built-in language is active, this method returns "C". "C" is treated as the built-in language for
+ * simplicity -- the C locale is used in unix environments as a fallback when the requested locale is invalid.
+ * This way we don't need to process both the "built_in" language and the "C" language (which is a valid
+ * environment setting) separately. */
+
+ return s_strLoadedLanguageId;
+}
+
+/* static */
+QString UITranslator::yearsToString(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n year(s)", "", cVal);
+}
+
+/* static */
+QString UITranslator::monthsToString(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n month(s)", "", cVal);
+}
+
+/* static */
+QString UITranslator::daysToString(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n day(s)", "", cVal);
+}
+
+/* static */
+QString UITranslator::hoursToString(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n hour(s)", "", cVal);
+}
+
+/* static */
+QString UITranslator::minutesToString(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n minute(s)", "", cVal);
+}
+
+/* static */
+QString UITranslator::secondsToString(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n second(s)", "", cVal);
+}
+
+/* static */
+QString UITranslator::yearsToStringAgo(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n year(s) ago", "", cVal);
+}
+
+/* static */
+QString UITranslator::monthsToStringAgo(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n month(s) ago", "", cVal);
+}
+
+/* static */
+QString UITranslator::daysToStringAgo(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n day(s) ago", "", cVal);
+}
+
+/* static */
+QString UITranslator::hoursToStringAgo(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n hour(s) ago", "", cVal);
+}
+
+/* static */
+QString UITranslator::minutesToStringAgo(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n minute(s) ago", "", cVal);
+}
+
+/* static */
+QString UITranslator::secondsToStringAgo(uint32_t cVal)
+{
+ return QApplication::translate("UITranslator", "%n second(s) ago", "", cVal);
+}
+
+/* static */
+QString UITranslator::decimalSep()
+{
+ return QString(QLocale::system().decimalPoint());
+}
+
+/* static */
+QString UITranslator::sizeRegexp()
+{
+ /* This regexp will capture 5 groups of text:
+ * - cap(1): integer number in case when no decimal point is present
+ * (if empty, it means that decimal point is present)
+ * - cap(2): size suffix in case when no decimal point is present (may be empty)
+ * - cap(3): integer number in case when decimal point is present (may be empty)
+ * - cap(4): fraction number (hundredth) in case when decimal point is present
+ * - cap(5): size suffix in case when decimal point is present (note that
+ * B cannot appear there). */
+
+ const QString strRegexp =
+ QString("^(?:(?:(\\d+)(?:\\s?(%2|%3|%4|%5|%6|%7))?)|(?:(\\d*)%1(\\d{1,2})(?:\\s?(%3|%4|%5|%6|%7))))$")
+ .arg(decimalSep())
+ .arg(tr("B", "size suffix Bytes"))
+ .arg(tr("KB", "size suffix KBytes=1024 Bytes"))
+ .arg(tr("MB", "size suffix MBytes=1024 KBytes"))
+ .arg(tr("GB", "size suffix GBytes=1024 MBytes"))
+ .arg(tr("TB", "size suffix TBytes=1024 GBytes"))
+ .arg(tr("PB", "size suffix PBytes=1024 TBytes"));
+ return strRegexp;
+}
+
+/* static */
+quint64 UITranslator::parseSize(const QString &strText)
+{
+ /* Text should be in form of B|KB|MB|GB|TB|PB. */
+ QRegExp regexp(sizeRegexp());
+ int iPos = regexp.indexIn(strText);
+ if (iPos != -1)
+ {
+ QString strInteger = regexp.cap(1);
+ QString strHundred;
+ QString strSuff = regexp.cap(2);
+ if (strInteger.isEmpty())
+ {
+ strInteger = regexp.cap(3);
+ strHundred = regexp.cap(4);
+ strSuff = regexp.cap(5);
+ }
+
+ quint64 uDenominator = 0;
+ if (strSuff.isEmpty() || strSuff == tr("B", "size suffix Bytes"))
+ uDenominator = 1;
+ else if (strSuff == tr("KB", "size suffix KBytes=1024 Bytes"))
+ uDenominator = _1K;
+ else if (strSuff == tr("MB", "size suffix MBytes=1024 KBytes"))
+ uDenominator = _1M;
+ else if (strSuff == tr("GB", "size suffix GBytes=1024 MBytes"))
+ uDenominator = _1G;
+ else if (strSuff == tr("TB", "size suffix TBytes=1024 GBytes"))
+ uDenominator = _1T;
+ else if (strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
+ uDenominator = _1P;
+
+ quint64 iInteger = strInteger.toULongLong();
+ if (uDenominator == 1)
+ return iInteger;
+
+ quint64 iHundred = strHundred.leftJustified(2, '0').toULongLong();
+ iHundred = iHundred * uDenominator / 100;
+ iInteger = iInteger * uDenominator + iHundred;
+ return iInteger;
+ }
+ else
+ return 0;
+}
+
+/* static */
+SizeSuffix UITranslator::parseSizeSuffix(const QString &strText)
+{
+ /* Text should be in form of B|KB|MB|GB|TB|PB. */
+ QRegExp regexp(sizeRegexp());
+ int iPos = regexp.indexIn(strText);
+ if (iPos != -1)
+ {
+ QString strInteger = regexp.cap(1);
+ QString strSuff = regexp.cap(2);
+ if (strInteger.isEmpty())
+ {
+ strInteger = regexp.cap(3);
+ strSuff = regexp.cap(5);
+ }
+
+ SizeSuffix enmSizeSuffix = SizeSuffix_Byte;
+
+ if (strSuff.isEmpty() || strSuff == tr("B", "size suffix Bytes"))
+ enmSizeSuffix = SizeSuffix_Byte;
+ else if (strSuff == tr("KB", "size suffix KBytes=1024 Bytes"))
+ enmSizeSuffix = SizeSuffix_KiloByte;
+ else if (strSuff == tr("MB", "size suffix MBytes=1024 KBytes"))
+ enmSizeSuffix = SizeSuffix_MegaByte;
+ else if (strSuff == tr("GB", "size suffix GBytes=1024 MBytes"))
+ enmSizeSuffix = SizeSuffix_GigaByte;
+ else if (strSuff == tr("TB", "size suffix TBytes=1024 GBytes"))
+ enmSizeSuffix = SizeSuffix_TeraByte;
+ else if (strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
+ enmSizeSuffix = SizeSuffix_PetaByte;
+ return enmSizeSuffix;
+ }
+ else
+ return SizeSuffix_Byte;
+}
+
+/* static */
+bool UITranslator::hasSizeSuffix(const QString &strText)
+{
+ /* Text should be in form of B|KB|MB|GB|TB|PB. */
+ QRegExp regexp(sizeRegexp());
+ int iPos = regexp.indexIn(strText);
+ if (iPos != -1)
+ {
+ QString strInteger = regexp.cap(1);
+ QString strSuff = regexp.cap(2);
+ if (strInteger.isEmpty())
+ {
+ strInteger = regexp.cap(3);
+ strSuff = regexp.cap(5);
+ }
+
+ if (strSuff.isEmpty())
+ return false;
+ if (strSuff == tr("B", "size suffix Bytes") ||
+ strSuff == tr("KB", "size suffix KBytes=1024 Bytes") ||
+ strSuff == tr("MB", "size suffix MBytes=1024 KBytes") ||
+ strSuff == tr("GB", "size suffix GBytes=1024 MBytes") ||
+ strSuff == tr("TB", "size suffix TBytes=1024 GBytes") ||
+ strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
+ return true;
+ return false;
+ }
+ else
+ return false;
+}
+
+/* static */
+QString UITranslator::formatSize(quint64 uSize, uint cDecimal /* = 2 */,
+ FormatSize enmMode /* = FormatSize_Round */)
+{
+ /* Text will be in form of B|KB|MB|GB|TB|PB.
+ *
+ * When enmMode is FormatSize_Round, the result is rounded to the
+ * closest number containing @a aDecimal decimal digits.
+ * When enmMode is FormatSize_RoundDown, the result is rounded to the
+ * largest number with @a aDecimal decimal digits that is not greater than
+ * the result. This guarantees that converting the resulting string back to
+ * the integer value in bytes will not produce a value greater that the
+ * initial size parameter.
+ * When enmMode is FormatSize_RoundUp, the result is rounded to the
+ * smallest number with @a aDecimal decimal digits that is not less than the
+ * result. This guarantees that converting the resulting string back to the
+ * integer value in bytes will not produce a value less that the initial
+ * size parameter. */
+
+ quint64 uDenominator = 0;
+ int iSuffix = 0;
+
+ if (uSize < _1K)
+ {
+ uDenominator = 1;
+ iSuffix = 0;
+ }
+ else if (uSize < _1M)
+ {
+ uDenominator = _1K;
+ iSuffix = 1;
+ }
+ else if (uSize < _1G)
+ {
+ uDenominator = _1M;
+ iSuffix = 2;
+ }
+ else if (uSize < _1T)
+ {
+ uDenominator = _1G;
+ iSuffix = 3;
+ }
+ else if (uSize < _1P)
+ {
+ uDenominator = _1T;
+ iSuffix = 4;
+ }
+ else
+ {
+ uDenominator = _1P;
+ iSuffix = 5;
+ }
+
+ quint64 uInteger = uSize / uDenominator;
+ quint64 uDecimal = uSize % uDenominator;
+ quint64 uMult = 1;
+ for (uint i = 0; i < cDecimal; ++i)
+ uMult *= 10;
+
+ QString strNumber;
+ if (uDenominator > 1)
+ {
+ if (uDecimal)
+ {
+ uDecimal *= uMult;
+ /* Not greater: */
+ if (enmMode == FormatSize_RoundDown)
+ uDecimal = uDecimal / uDenominator;
+ /* Not less: */
+ else if (enmMode == FormatSize_RoundUp)
+ uDecimal = (uDecimal + uDenominator - 1) / uDenominator;
+ /* Nearest: */
+ else
+ uDecimal = (uDecimal + uDenominator / 2) / uDenominator;
+ }
+ /* Check for the fractional part overflow due to rounding: */
+ if (uDecimal == uMult)
+ {
+ uDecimal = 0;
+ ++uInteger;
+ /* Check if we've got 1024 XB after rounding and scale down if so: */
+ if (uInteger == 1024 && iSuffix + 1 < (int)SizeSuffix_Max)
+ {
+ uInteger /= 1024;
+ ++iSuffix;
+ }
+ }
+ strNumber = QString::number(uInteger);
+ if (cDecimal)
+ strNumber += QString("%1%2").arg(decimalSep())
+ .arg(QString::number(uDecimal).rightJustified(cDecimal, '0'));
+ }
+ else
+ {
+ strNumber = QString::number(uInteger);
+ }
+
+ return QString("%1 %2").arg(strNumber).arg(gpConverter->toString(static_cast<SizeSuffix>(iSuffix)));
+}
+
+/* static */
+QString UITranslator::addMetricSuffixToNumber(quint64 uNumber)
+{
+ if (uNumber <= 0)
+ return QString();
+ /* See https://en.wikipedia.org/wiki/Metric_prefix for metric suffixes:*/
+ char suffixes[] = {'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
+ int zeroCount = (int)log10((long double)uNumber);
+ if (zeroCount < 3)
+ return QString::number(uNumber);
+ int h = 3 * (zeroCount / 3);
+ char result[128];
+ sprintf(result, "%.2f", uNumber / (float)pow((double)10, h));
+ return QString("%1%2").arg(result).arg(suffixes[h / 3 - 1]);
+}
+
+/* static */
+QStringList UITranslator::COMPortNames()
+{
+ QStringList list;
+ for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
+ list << kComKnownPorts[i].name;
+
+ return list;
+}
+
+/* static */
+QString UITranslator::toCOMPortName(ulong uIRQ, ulong uIOBase)
+{
+ for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
+ if (kComKnownPorts[i].IRQ == uIRQ &&
+ kComKnownPorts[i].IOBase == uIOBase)
+ return kComKnownPorts[i].name;
+
+ return tr("User-defined", "serial port");;
+}
+
+/* static */
+bool UITranslator::toCOMPortNumbers(const QString &strName, ulong &uIRQ, ulong &uIOBase)
+{
+ for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
+ if (strcmp(kComKnownPorts[i].name, strName.toUtf8().data()) == 0)
+ {
+ uIRQ = kComKnownPorts[i].IRQ;
+ uIOBase = kComKnownPorts[i].IOBase;
+ return true;
+ }
+
+ return false;
+}
+
+/* Regular expressions used by both highlight and emphasize. They use the
+ same prefix and suffix expression. Unfortunately, QRegularExpression isn't
+ thread safe, so we only store the string contstants here. */
+/** @todo qt6: Both these had bogus suffix sets '[:.-!);]', I've changed them to '[-:.!);]', hope that's correct. */
+static char const g_szRxSingleQuotes[] = "((?:^|\\s)[(]?)"
+ "'([^']*)'"
+ "(?=[-:.!);]?(?:\\s|$))";
+static const char g_szRxUuid[] = "((?:^|\\s)[(]?)"
+ "(\\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\})"
+ "(?=[-:.!);]?(?:\\s|$))";
+
+/* static */
+QString UITranslator::highlight(QString strText, bool fToolTip /* = false */)
+{
+ /* We should reformat the input strText so that:
+ * - strings in single quotes will be put inside <nobr> and marked
+ * with blue color;
+ * - UUIDs be put inside <nobr> and marked
+ * with green color;
+ * - replaces new line chars with </p><p> constructs to form paragraphs
+ * (note that <p\> and </p> are not appended to the beginning and to the
+ * end of the string respectively, to allow the result be appended
+ * or prepended to the existing paragraph).
+ *
+ * If @a fToolTip is true, colouring is not applied, only the <nobr> tag
+ * is added. Also, new line chars are replaced with <br> instead of <p>. */
+
+ QString strFont;
+ QString uuidFont;
+ QString endFont;
+ if (!fToolTip)
+ {
+ strFont = "<font color=#0000CC>";
+ uuidFont = "<font color=#008000>";
+ endFont = "</font>";
+ }
+
+ /* Replace special entities, '&' -- first! */
+ strText.replace('&', "&amp;");
+ strText.replace('<', "&lt;");
+ strText.replace('>', "&gt;");
+ strText.replace('\"', "&quot;");
+
+ /* Mark strings in single quotes with color: */
+ strText.replace(QRegularExpression(g_szRxSingleQuotes), QString("\\1%1<nobr>'\\2'</nobr>%2").arg(strFont).arg(endFont));
+
+ /* Mark UUIDs with color: */
+ strText.replace(QRegularExpression(g_szRxUuid), QString("\\1%1<nobr>\\2</nobr>%2").arg(uuidFont).arg(endFont));
+
+ /* Split to paragraphs at \n chars: */
+ if (!fToolTip)
+ strText.replace('\n', "</p><p>");
+ else
+ strText.replace('\n', "<br>");
+
+ return strText;
+}
+
+/* static */
+QString UITranslator::emphasize(QString strText)
+{
+ /* We should reformat the input string @a strText so that:
+ * - strings in single quotes will be put inside \<nobr\> and marked
+ * with bold style;
+ * - UUIDs be put inside \<nobr\> and marked
+ * with italic style;
+ * - replaces new line chars with \</p\>\<p\> constructs to form paragraphs
+ * (note that \<p\> and \</p\> are not appended to the beginning and to the
+ * end of the string respectively, to allow the result be appended
+ * or prepended to the existing paragraph). */
+
+ QString strEmphStart("<b>");
+ QString strEmphEnd("</b>");
+ QString uuidEmphStart("<i>");
+ QString uuidEmphEnd("</i>");
+
+ /* Replace special entities, '&' -- first! */
+ strText.replace('&', "&amp;");
+ strText.replace('<', "&lt;");
+ strText.replace('>', "&gt;");
+ strText.replace('\"', "&quot;");
+
+ /* Mark strings in single quotes with bold style: */
+ strText.replace(QRegularExpression(g_szRxSingleQuotes), QString("\\1%1<nobr>'\\2'</nobr>%2").arg(strEmphStart).arg(strEmphEnd));
+
+ /* Mark UUIDs with italic style: */
+ strText.replace(QRegularExpression(g_szRxUuid), QString("\\1%1<nobr>\\2</nobr>%2").arg(uuidEmphStart).arg(uuidEmphEnd));
+
+ /* Split to paragraphs at \n chars: */
+ strText.replace('\n', "</p><p>");
+
+ return strText;
+}
+
+/* static */
+QString UITranslator::removeAccelMark(QString strText)
+{
+ /* In order to support accelerators used in non-alphabet languages
+ * (e.g. Japanese) that has a form of "(&<L>)" (where <L> is a latin letter),
+ * this method first searches for this pattern and, if found, removes it as a
+ * whole. If such a pattern is not found, then the '&' character is simply
+ * removed from the string. */
+
+ QRegExp accel("\\(&[a-zA-Z]\\)");
+ int iPos = accel.indexIn(strText);
+ if (iPos >= 0)
+ strText.remove(iPos, accel.cap().length());
+ else
+ {
+ iPos = strText.indexOf('&');
+ if (iPos >= 0)
+ strText.remove(iPos, 1);
+ }
+
+ return strText;
+}
+
+/* static */
+QString UITranslator::insertKeyToActionText(const QString &strText, const QString &strKey)
+{
+#ifdef VBOX_WS_MAC
+ QString strPattern("%1 (Host+%2)");
+#else
+ QString strPattern("%1 \tHost+%2");
+#endif
+ if ( strKey.isEmpty()
+ || strKey.compare("None", Qt::CaseInsensitive) == 0)
+ return strText;
+ else
+ return strPattern.arg(strText).arg(QKeySequence(strKey).toString(QKeySequence::NativeText));
+}
+
+/* static */
+bool UITranslator::isTranslationInProgress()
+{
+ return s_fTranslationInProgress;
+}
+
+/* static */
+QString UITranslator::byteStringToMegaByteString(const QString &strByteString)
+{
+ if (strByteString.isEmpty())
+ return QString();
+ bool fConversionSuccess = false;
+ qulonglong uByte = strByteString.toULongLong(&fConversionSuccess);
+ AssertReturn(fConversionSuccess, QString());
+ return QString::number(uByte / _1M);
+}
+
+/* static */
+QString UITranslator::megabyteStringToByteString(const QString &strMegaByteString)
+{
+ if (strMegaByteString.isEmpty())
+ return QString();
+ bool fConversionSuccess = false;
+ qulonglong uMegaByte = strMegaByteString.toULongLong(&fConversionSuccess);
+ AssertReturn(fConversionSuccess, QString());
+ return QString::number(uMegaByte * _1M);
+}
+
+UITranslator::UITranslator(QObject *pParent /* = 0 */)
+ : QTranslator(pParent)
+{
+}
+
+bool UITranslator::loadFile(const QString &strFileName)
+{
+ QFile file(strFileName);
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+ m_data = file.readAll();
+ return load((uchar*)m_data.data(), m_data.size());
+}
+
+/* static */
+QString UITranslator::languageName()
+{
+ /* Returns "English" if no translation is installed
+ * or if the translation file is invalid. */
+ return QApplication::translate("@@@", "English",
+ "Native language name");
+}
+
+/* static */
+QString UITranslator::languageCountry()
+{
+ /* Returns "--" if no translation is installed or if the translation file
+ * is invalid, or if the language is independent on the country. */
+ return QApplication::translate("@@@", "--",
+ "Native language country name "
+ "(empty if this language is for all countries)");
+}
+
+/* static */
+QString UITranslator::languageNameEnglish()
+{
+ /* Returns "English" if no translation is installed
+ * or if the translation file is invalid. */
+ return QApplication::translate("@@@", "English",
+ "Language name, in English");
+}
+
+/* static */
+QString UITranslator::languageCountryEnglish()
+{
+ /* Returns "--" if no translation is installed or if the translation file
+ * is invalid, or if the language is independent on the country. */
+ return QApplication::translate("@@@", "--",
+ "Language country name, in English "
+ "(empty if native country name is empty)");
+}
+
+/* static */
+QString UITranslator::languageTranslators()
+{
+ /* Returns "Oracle Corporation" if no translation is installed or if the translation file
+ * is invalid, or if the translation is supplied by Oracle Corporation. */
+ return QApplication::translate("@@@", "Oracle Corporation",
+ "Comma-separated list of translators");
+}
+
+/* static */
+QString UITranslator::systemLanguageId()
+{
+ /* This does exactly the same as QLocale::system().name() but corrects its wrong behavior on Linux systems
+ * (LC_NUMERIC for some strange reason takes precedence over any other locale setting in the QLocale::system()
+ * implementation). This implementation first looks at LC_ALL (as defined by SUS), then looks at LC_MESSAGES
+ * which is designed to define a language for program messages in case if it differs from the language for
+ * other locale categories. Then it looks for LANG and finally falls back to QLocale::system().name().
+ *
+ * The order of precedence is well defined here:
+ * http://opengroup.org/onlinepubs/007908799/xbd/envvar.html
+ *
+ * This method will return "C" when the requested locale is invalid or when the "C" locale is set explicitly. */
+
+#if defined(VBOX_WS_MAC)
+ // QLocale return the right id only if the user select the format
+ // of the language also. So we use our own implementation */
+ return ::darwinSystemLanguage();
+#elif defined(Q_OS_UNIX)
+ const char *pszValue = RTEnvGet("LC_ALL");
+ if (pszValue == 0)
+ pszValue = RTEnvGet("LC_MESSAGES");
+ if (pszValue == 0)
+ pszValue = RTEnvGet("LANG");
+ if (pszValue != 0)
+ return QLocale(pszValue).name();
+#endif
+ return QLocale::system().name();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UITranslator.h b/src/VBox/Frontends/VirtualBox/src/globals/UITranslator.h
new file mode 100644
index 00000000..fc0c6b0e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UITranslator.h
@@ -0,0 +1,173 @@
+/* $Id: UITranslator.h $ */
+/** @file
+ * VBox Qt GUI - UITranslator class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UITranslator_h
+#define FEQT_INCLUDED_SRC_globals_UITranslator_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTranslator>
+
+/* GUI includes: */
+#include "UIDefs.h"
+#include "UILibraryDefs.h"
+
+/** QTranslator subclass for VBox needs. */
+class SHARED_LIBRARY_STUFF UITranslator : public QTranslator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Loads the language by language ID.
+ * @param strLangId Brings the language ID in in form of xx_YY.
+ * QString() means the system default language. */
+ static void loadLanguage(const QString &strLangId = QString());
+
+ /** Returns VBox language sub-directory. */
+ static QString vboxLanguageSubDirectory();
+ /** Returns VBox language file-base. */
+ static QString vboxLanguageFileBase();
+ /** Returns VBox language file-extension. */
+ static QString vboxLanguageFileExtension();
+ /** Returns VBox language ID reg-exp. */
+ static QString vboxLanguageIdRegExp();
+ /** Returns built in language name. */
+ static QString vboxBuiltInLanguageName();
+
+ /** Returns the loaded (active) language ID. */
+ static QString languageId();
+
+ /** Returns tr("%n year(s)"). */
+ static QString yearsToString(uint32_t cVal);
+ /** Returns tr("%n month(s)"). */
+ static QString monthsToString(uint32_t cVal);
+ /** Returns tr("%n day(s)"). */
+ static QString daysToString(uint32_t cVal);
+ /** Returns tr("%n hour(s)"). */
+ static QString hoursToString(uint32_t cVal);
+ /** Returns tr("%n minute(s)"). */
+ static QString minutesToString(uint32_t cVal);
+ /** Returns tr("%n second(s)"). */
+ static QString secondsToString(uint32_t cVal);
+
+ /** Returns tr("%n year(s) ago"). */
+ static QString yearsToStringAgo(uint32_t cVal);
+ /** Returns tr("%n month(s) ago"). */
+ static QString monthsToStringAgo(uint32_t cVal);
+ /** Returns tr("%n day(s) ago"). */
+ static QString daysToStringAgo(uint32_t cVal);
+ /** Returns tr("%n hour(s) ago"). */
+ static QString hoursToStringAgo(uint32_t cVal);
+ /** Returns tr("%n minute(s) ago"). */
+ static QString minutesToStringAgo(uint32_t cVal);
+ /** Returns tr("%n second(s) ago"). */
+ static QString secondsToStringAgo(uint32_t cVal);
+
+ /** Returns the decimal separator for the current locale. */
+ static QString decimalSep();
+ /** Returns the regexp string that defines the format of the human-readable size representation. */
+ static QString sizeRegexp();
+ /** Parses the given size strText and returns the size value in bytes. */
+ static quint64 parseSize(const QString &strText);
+ /** Parses the given size strText and returns the size suffix. */
+ static SizeSuffix parseSizeSuffix(const QString &strText);
+ /** Parses the given string @a strText and returns true if it includes a size suffix. */
+ static bool hasSizeSuffix(const QString &strText);
+ /** Formats the given @a uSize value in bytes to a human readable string.
+ * @param uSize Brings the size value in bytes.
+ * @param enmMode Brings the conversion mode.
+ * @param cDecimal Brings the number of decimal digits in result. */
+ static QString formatSize(quint64 uSize, uint cDecimal = 2, FormatSize enmMode = FormatSize_Round);
+ /** Formats the given @a uNumber to that 'k' is added for thousand, 'M' for million and so on. */
+ static QString addMetricSuffixToNumber(quint64 uNumber);
+
+ /** Returns the list of the standard COM port names (i.e. "COMx"). */
+ static QStringList COMPortNames();
+ /** Returns the name of the standard COM port corresponding to the given parameters,
+ * or "User-defined" (which is also returned when both @a uIRQ and @a uIOBase are 0). */
+ static QString toCOMPortName(ulong uIRQ, ulong uIOBase);
+ /** Returns port parameters corresponding to the given standard COM name.
+ * Returns @c true on success, or @c false if the given port name is not one of the standard names (i.e. "COMx"). */
+ static bool toCOMPortNumbers(const QString &strName, ulong &uIRQ, ulong &uIOBase);
+
+ /** Reformats the input @a strText to highlight it. */
+ static QString highlight(QString strText, bool fToolTip = false);
+ /** Reformats the input @a strText to emphasize it. */
+ static QString emphasize(QString strText);
+ /** Removes the first occurrence of the accelerator mark (the ampersand symbol) from the given @a strText. */
+ static QString removeAccelMark(QString strText);
+ /** Inserts a passed @a strKey into action @a strText. */
+ static QString insertKeyToActionText(const QString &strText, const QString &strKey);
+
+ /** Returns whether we are performing translation currently. */
+ static bool isTranslationInProgress();
+
+ /* Converts bytes string to megabytes string. */
+ static QString byteStringToMegaByteString(const QString &strByteString);
+ /* Converts megabytes string to bytes string. */
+ static QString megabyteStringToByteString(const QString &strMegaByteString);
+
+private:
+
+ /** Constructs translator passing @a pParent to the base-class. */
+ UITranslator(QObject *pParent = 0);
+
+ /** Loads language file with gained @a strFileName. */
+ bool loadFile(const QString &strFileName);
+
+ /** Native language name of the currently installed translation. */
+ static QString languageName();
+ /** Native language country name of the currently installed translation. */
+ static QString languageCountry();
+ /** Language name of the currently installed translation, in English. */
+ static QString languageNameEnglish();
+ /** Language country name of the currently installed translation, in English. */
+ static QString languageCountryEnglish();
+ /** Comma-separated list of authors of the currently installed translation. */
+ static QString languageTranslators();
+
+ /** Returns the system language ID. */
+ static QString systemLanguageId();
+
+ /** Holds the singleton instance. */
+ static UITranslator *s_pTranslator;
+
+ /** Holds whether we are performing translation currently. */
+ static bool s_fTranslationInProgress;
+
+ /** Holds the currently loaded language ID. */
+ static QString s_strLoadedLanguageId;
+
+ /** Holds the loaded data. */
+ QByteArray m_data;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UITranslator_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIVersion.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIVersion.cpp
new file mode 100644
index 00000000..3fea7d9c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIVersion.cpp
@@ -0,0 +1,124 @@
+/* $Id: UIVersion.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVersion class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStringList>
+
+/* GUI includes: */
+#include "UIVersion.h"
+
+/* Other VBox includes: */
+#include <iprt/string.h>
+
+
+UIVersion::UIVersion()
+ : m_x(-1)
+ , m_y(-1)
+ , m_z(-1)
+{
+}
+
+UIVersion::UIVersion(const QString &strFullVersionInfo)
+ : m_x(-1)
+ , m_y(-1)
+ , m_z(-1)
+{
+ const QStringList fullVersionInfo = strFullVersionInfo.split('_');
+ if (fullVersionInfo.size() > 0)
+ {
+ const QStringList versionIndexes = fullVersionInfo.at(0).split('.');
+ if (versionIndexes.size() > 0)
+ m_x = versionIndexes[0].toInt();
+ if (versionIndexes.size() > 1)
+ m_y = versionIndexes[1].toInt();
+ if (versionIndexes.size() > 2)
+ m_z = versionIndexes[2].toInt();
+ }
+ if (fullVersionInfo.size() > 1)
+ m_strPostfix = fullVersionInfo.at(1);
+}
+
+bool UIVersion::isValid() const
+{
+ return (m_x != -1)
+ && (m_y != -1)
+ && (m_z != -1);
+}
+
+bool UIVersion::equal(const UIVersion &other) const
+{
+ return (m_x == other.m_x)
+ && (m_y == other.m_y)
+ && (m_z == other.m_z)
+ && (m_strPostfix == other.m_strPostfix);
+}
+
+bool UIVersion::operator<(const UIVersion &other) const
+{
+ return RTStrVersionCompare(toString().toUtf8().constData(), other.toString().toUtf8().constData()) < 0;
+}
+
+bool UIVersion::operator<=(const UIVersion &other) const
+{
+ return RTStrVersionCompare(toString().toUtf8().constData(), other.toString().toUtf8().constData()) <= 0;
+}
+
+bool UIVersion::operator>(const UIVersion &other) const
+{
+ return RTStrVersionCompare(toString().toUtf8().constData(), other.toString().toUtf8().constData()) > 0;
+}
+
+bool UIVersion::operator>=(const UIVersion &other) const
+{
+ return RTStrVersionCompare(toString().toUtf8().constData(), other.toString().toUtf8().constData()) >= 0;
+}
+
+QString UIVersion::toString() const
+{
+ return m_strPostfix.isEmpty() ? QString("%1.%2.%3").arg(m_x).arg(m_y).arg(m_z)
+ : QString("%1.%2.%3_%4").arg(m_x).arg(m_y).arg(m_z).arg(m_strPostfix);
+}
+
+UIVersion UIVersion::effectiveReleasedVersion() const
+{
+ /* First, we just copy the current one: */
+ UIVersion version = *this;
+
+ /* If this version being developed: */
+ if (version.z() % 2 == 1)
+ {
+ /* If this version being developed on release branch (we guess the right one): */
+ if (version.z() < 97)
+ version.setZ(version.z() - 1);
+ /* If this version being developed on trunk (we use hardcoded one for now): */
+ else
+ version.setZ(8); /* Current .z for 6.0.z */
+ }
+
+ /* Finally, we just return that we have: */
+ return version;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIVersion.h b/src/VBox/Frontends/VirtualBox/src/globals/UIVersion.h
new file mode 100644
index 00000000..615431b4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIVersion.h
@@ -0,0 +1,104 @@
+/* $Id: UIVersion.h $ */
+/** @file
+ * VBox Qt GUI - UIVersion class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIVersion_h
+#define FEQT_INCLUDED_SRC_globals_UIVersion_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+
+/** Represents VirtualBox version wrapper. */
+class UIVersion
+{
+public:
+
+ /** Constructs default object. */
+ UIVersion();
+ /** Constructs object based on parsed @a strFullVersionInfo. */
+ UIVersion(const QString &strFullVersionInfo);
+
+ /** Returns whether this object is valid. */
+ bool isValid() const;
+
+ /** Returns whether this object is equal to @a other. */
+ bool equal(const UIVersion &other) const;
+ /** Checks whether this object is equal to @a other. */
+ bool operator==(const UIVersion &other) const { return equal(other); }
+ /** Checks whether this object is NOT equal to @a other. */
+ bool operator!=(const UIVersion &other) const { return !equal(other); }
+
+ /** Checks whether this object is less than @a other. */
+ bool operator<(const UIVersion &other) const;
+ /** Checks whether this object is less or equal than @a other. */
+ bool operator<=(const UIVersion &other) const;
+ /** Checks whether this object is greater than @a other. */
+ bool operator>(const UIVersion &other) const;
+ /** Checks whether this object is greater or equal than @a other. */
+ bool operator>=(const UIVersion &other) const;
+
+ /** Returns object string representation. */
+ QString toString() const;
+
+ /** Returns the object X value. */
+ int x() const { return m_x; }
+ /** Returns the object Y value. */
+ int y() const { return m_y; }
+ /** Returns the object Z value. */
+ int z() const { return m_z; }
+ /** Returns the object postfix. */
+ QString postfix() const { return m_strPostfix; }
+
+ /** Defines the object @a x value. */
+ void setX(int x) { m_x = x; }
+ /** Defines the object @a y value. */
+ void setY(int y) { m_y = y; }
+ /** Defines the object @a z value. */
+ void setZ(int z) { m_z = z; }
+ /** Defines the object @a strPostfix. */
+ void setPostfix(const QString &strPostfix) { m_strPostfix = strPostfix; }
+
+ /** Returns effective released version guessed or hardcoded for this one version.
+ * This can be even the version itself. */
+ UIVersion effectiveReleasedVersion() const;
+
+private:
+
+ /** Holds the object X value. */
+ int m_x;
+ /** Holds the object Y value. */
+ int m_y;
+ /** Holds the object Z value. */
+ int m_z;
+
+ /** Holds the object postfix. */
+ QString m_strPostfix;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIVersion_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxClientEventHandler.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxClientEventHandler.cpp
new file mode 100644
index 00000000..1388ab24
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxClientEventHandler.cpp
@@ -0,0 +1,220 @@
+/* $Id: UIVirtualBoxClientEventHandler.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVirtualBoxClientEventHandler class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIMainEventListener.h"
+#include "UIVirtualBoxClientEventHandler.h"
+
+/* COM includes: */
+#include "CEventListener.h"
+#include "CEventSource.h"
+#include "CVirtualBoxClient.h"
+
+
+/** Private QObject extension providing UIVirtualBoxClientEventHandler with CVirtualBoxClient event-source. */
+class UIVirtualBoxClientEventHandlerProxy : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about the VBoxSVC become @a fAvailable. */
+ void sigVBoxSVCAvailabilityChange(bool fAvailable);
+
+public:
+
+ /** Constructs event proxy object on the basis of passed @a pParent. */
+ UIVirtualBoxClientEventHandlerProxy(QObject *pParent);
+ /** Destructs event proxy object. */
+ ~UIVirtualBoxClientEventHandlerProxy();
+
+protected:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares listener. */
+ void prepareListener();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups listener. */
+ void cleanupListener();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+private:
+
+ /** Holds the COM event source instance. */
+ CEventSource m_comEventSource;
+
+ /** Holds the Qt event listener instance. */
+ ComObjPtr<UIMainEventListenerImpl> m_pQtListener;
+ /** Holds the COM event listener instance. */
+ CEventListener m_comEventListener;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualBoxClientEventHandlerProxy implementation. *
+*********************************************************************************************************************************/
+
+UIVirtualBoxClientEventHandlerProxy::UIVirtualBoxClientEventHandlerProxy(QObject *pParent)
+ : QObject(pParent)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIVirtualBoxClientEventHandlerProxy::~UIVirtualBoxClientEventHandlerProxy()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIVirtualBoxClientEventHandlerProxy::prepare()
+{
+ /* Prepare: */
+ prepareListener();
+ prepareConnections();
+}
+
+void UIVirtualBoxClientEventHandlerProxy::prepareListener()
+{
+ /* Create Main event listener instance: */
+ m_pQtListener.createObject();
+ m_pQtListener->init(new UIMainEventListener, this);
+ m_comEventListener = CEventListener(m_pQtListener);
+
+ /* Get VirtualBoxClient: */
+ const CVirtualBoxClient comVBoxClient = uiCommon().virtualBoxClient();
+ AssertWrapperOk(comVBoxClient);
+ /* Get VirtualBoxClient event source: */
+ m_comEventSource = comVBoxClient.GetEventSource();
+ AssertWrapperOk(m_comEventSource);
+
+ /* Enumerate all the required event-types: */
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes
+ << KVBoxEventType_OnVBoxSVCAvailabilityChanged;
+
+ /* Register event listener for event source aggregator: */
+ m_comEventSource.RegisterListener(m_comEventListener, eventTypes, FALSE /* active? */);
+ AssertWrapperOk(m_comEventSource);
+
+ /* Register event sources in their listeners as well: */
+ m_pQtListener->getWrapped()->registerSource(m_comEventSource, m_comEventListener);
+}
+
+void UIVirtualBoxClientEventHandlerProxy::prepareConnections()
+{
+ /* Create direct (sync) connections for signals of main event listener.
+ * Keep in mind that the abstract Qt4 connection notation should be used here. */
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigVBoxSVCAvailabilityChange(bool)),
+ this, SIGNAL(sigVBoxSVCAvailabilityChange(bool)),
+ Qt::DirectConnection);
+}
+
+void UIVirtualBoxClientEventHandlerProxy::cleanupConnections()
+{
+ /* Nothing for now. */
+}
+
+void UIVirtualBoxClientEventHandlerProxy::cleanupListener()
+{
+ /* Unregister everything: */
+ m_pQtListener->getWrapped()->unregisterSources();
+
+ /* Unregister event listener for event source aggregator: */
+ m_comEventSource.UnregisterListener(m_comEventListener);
+ m_comEventSource.detach();
+}
+
+void UIVirtualBoxClientEventHandlerProxy::cleanup()
+{
+ /* Cleanup: */
+ cleanupConnections();
+ cleanupListener();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualBoxClientEventHandler implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIVirtualBoxClientEventHandler *UIVirtualBoxClientEventHandler::s_pInstance = 0;
+
+/* static */
+UIVirtualBoxClientEventHandler *UIVirtualBoxClientEventHandler::instance()
+{
+ if (!s_pInstance)
+ s_pInstance = new UIVirtualBoxClientEventHandler;
+ return s_pInstance;
+}
+
+/* static */
+void UIVirtualBoxClientEventHandler::destroy()
+{
+ if (s_pInstance)
+ {
+ delete s_pInstance;
+ s_pInstance = 0;
+ }
+}
+
+UIVirtualBoxClientEventHandler::UIVirtualBoxClientEventHandler()
+ : m_pProxy(new UIVirtualBoxClientEventHandlerProxy(this))
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIVirtualBoxClientEventHandler::prepare()
+{
+ /* Prepare connections: */
+ prepareConnections();
+}
+
+void UIVirtualBoxClientEventHandler::prepareConnections()
+{
+ /* Create queued (async) connections for signals of event proxy object.
+ * Keep in mind that the abstract Qt4 connection notation should be used here. */
+ connect(m_pProxy, SIGNAL(sigVBoxSVCAvailabilityChange(bool)),
+ this, SIGNAL(sigVBoxSVCAvailabilityChange(bool)),
+ Qt::QueuedConnection);
+}
+
+
+#include "UIVirtualBoxClientEventHandler.moc"
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxClientEventHandler.h b/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxClientEventHandler.h
new file mode 100644
index 00000000..af0636e7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxClientEventHandler.h
@@ -0,0 +1,87 @@
+/* $Id: UIVirtualBoxClientEventHandler.h $ */
+/** @file
+ * VBox Qt GUI - UIVirtualBoxClientEventHandler class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIVirtualBoxClientEventHandler_h
+#define FEQT_INCLUDED_SRC_globals_UIVirtualBoxClientEventHandler_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMedium.h"
+#include "CMediumAttachment.h"
+
+/* Forward declarations: */
+class UIVirtualBoxClientEventHandlerProxy;
+
+/** Singleton QObject extension providing GUI with CVirtualBoxClient event-source. */
+class SHARED_LIBRARY_STUFF UIVirtualBoxClientEventHandler : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about the VBoxSVC become @a fAvailable. */
+ void sigVBoxSVCAvailabilityChange(bool fAvailable);
+
+public:
+
+ /** Returns singleton instance. */
+ static UIVirtualBoxClientEventHandler *instance();
+ /** Destroys singleton instance. */
+ static void destroy();
+
+protected:
+
+ /** Constructs VirtualBoxClient event handler. */
+ UIVirtualBoxClientEventHandler();
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares connections. */
+ void prepareConnections();
+
+private:
+
+ /** Holds the singleton instance. */
+ static UIVirtualBoxClientEventHandler *s_pInstance;
+
+ /** Holds the VirtualBoxClient event proxy instance. */
+ UIVirtualBoxClientEventHandlerProxy *m_pProxy;
+};
+
+/** Singleton VirtualBoxClient Event Handler 'official' name. */
+#define gVBoxClientEvents UIVirtualBoxClientEventHandler::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIVirtualBoxClientEventHandler_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxEventHandler.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxEventHandler.cpp
new file mode 100644
index 00000000..f3bfaf7c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxEventHandler.cpp
@@ -0,0 +1,382 @@
+/* $Id: UIVirtualBoxEventHandler.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVirtualBoxEventHandler class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIMainEventListener.h"
+#include "UIVirtualBoxEventHandler.h"
+
+/* COM includes: */
+#include "CEventListener.h"
+#include "CEventSource.h"
+#include "CVirtualBox.h"
+
+
+/** Private QObject extension providing UIVirtualBoxEventHandler with CVirtualBox event-source. */
+class UIVirtualBoxEventHandlerProxy : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about @a state change event for the machine with @a uId. */
+ void sigMachineStateChange(const QUuid &uId, const KMachineState state);
+ /** Notifies about data change event for the machine with @a uId. */
+ void sigMachineDataChange(const QUuid &uId);
+ /** Notifies about machine with @a uId was @a fRegistered. */
+ void sigMachineRegistered(const QUuid &uId, const bool fRegistered);
+ /** Notifies about machine with @a uId has groups changed. */
+ void sigMachineGroupsChange(const QUuid &uId);
+ /** Notifies about @a state change event for the session of the machine with @a uId. */
+ void sigSessionStateChange(const QUuid &uId, const KSessionState state);
+ /** Notifies about snapshot with @a uSnapshotId was taken for the machine with @a uId. */
+ void sigSnapshotTake(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about snapshot with @a uSnapshotId was deleted for the machine with @a uId. */
+ void sigSnapshotDelete(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about snapshot with @a uSnapshotId was changed for the machine with @a uId. */
+ void sigSnapshotChange(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about snapshot with @a uSnapshotId was restored for the machine with @a uId. */
+ void sigSnapshotRestore(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about request to uninstall cloud provider with @a uId. */
+ void sigCloudProviderUninstall(const QUuid &uId);
+ /** Notifies about cloud provider list changed. */
+ void sigCloudProviderListChanged();
+ /** Notifies about cloud profile with specified @a strName of provider with specified @a uProviderId is @a fRegistered. */
+ void sigCloudProfileRegistered(const QUuid &uProviderId, const QString &strName, bool fRegistered);
+ /** Notifies about cloud profile with specified @a strName of provider with specified @a uProviderId is changed. */
+ void sigCloudProfileChanged(const QUuid &uProviderId, const QString &strName);
+
+ /** Notifies about storage controller change.
+ * @param uMachineId Brings the ID of machine corresponding controller belongs to.
+ * @param strControllerName Brings the name of controller this event is related to. */
+ void sigStorageControllerChange(const QUuid &uMachineId, const QString &strControllerName);
+ /** Notifies about storage device change.
+ * @param comAttachment Brings corresponding attachment.
+ * @param fRemoved Brings whether medium is removed or added.
+ * @param fSilent Brings whether this change has gone silent for guest. */
+ void sigStorageDeviceChange(CMediumAttachment comAttachment, bool fRemoved, bool fSilent);
+ /** Notifies about storage medium @a comAttachment state change. */
+ void sigMediumChange(CMediumAttachment comAttachment);
+ /** Notifies about storage @a comMedium config change. */
+ void sigMediumConfigChange(CMedium comMedium);
+ /** Notifies about storage medium is (un)registered.
+ * @param uMediumId Brings corresponding medium ID.
+ * @param enmMediumType Brings corresponding medium type.
+ * @param fRegistered Brings whether medium is registered or unregistered. */
+ void sigMediumRegistered(const QUuid &uMediumId, KDeviceType enmMediumType, bool fRegistered);
+
+public:
+
+ /** Constructs event proxy object on the basis of passed @a pParent. */
+ UIVirtualBoxEventHandlerProxy(QObject *pParent);
+ /** Destructs event proxy object. */
+ ~UIVirtualBoxEventHandlerProxy();
+
+protected:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares listener. */
+ void prepareListener();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups listener. */
+ void cleanupListener();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+private:
+
+ /** Holds the COM event source instance. */
+ CEventSource m_comEventSource;
+
+ /** Holds the Qt event listener instance. */
+ ComObjPtr<UIMainEventListenerImpl> m_pQtListener;
+ /** Holds the COM event listener instance. */
+ CEventListener m_comEventListener;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualBoxEventHandlerProxy implementation. *
+*********************************************************************************************************************************/
+
+UIVirtualBoxEventHandlerProxy::UIVirtualBoxEventHandlerProxy(QObject *pParent)
+ : QObject(pParent)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIVirtualBoxEventHandlerProxy::~UIVirtualBoxEventHandlerProxy()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIVirtualBoxEventHandlerProxy::prepare()
+{
+ /* Prepare: */
+ prepareListener();
+ prepareConnections();
+}
+
+void UIVirtualBoxEventHandlerProxy::prepareListener()
+{
+ /* Create Main event listener instance: */
+ m_pQtListener.createObject();
+ m_pQtListener->init(new UIMainEventListener, this);
+ m_comEventListener = CEventListener(m_pQtListener);
+
+ /* Get VirtualBox: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+ AssertWrapperOk(comVBox);
+ /* Get VirtualBox event source: */
+ m_comEventSource = comVBox.GetEventSource();
+ AssertWrapperOk(m_comEventSource);
+
+ /* Enumerate all the required event-types: */
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes
+ << KVBoxEventType_OnMachineStateChanged
+ << KVBoxEventType_OnMachineDataChanged
+ << KVBoxEventType_OnMachineRegistered
+ << KVBoxEventType_OnMachineGroupsChanged
+ << KVBoxEventType_OnSessionStateChanged
+ << KVBoxEventType_OnSnapshotTaken
+ << KVBoxEventType_OnSnapshotDeleted
+ << KVBoxEventType_OnSnapshotChanged
+ << KVBoxEventType_OnSnapshotRestored
+ << KVBoxEventType_OnCloudProviderListChanged
+ << KVBoxEventType_OnCloudProviderUninstall
+ << KVBoxEventType_OnCloudProfileRegistered
+ << KVBoxEventType_OnCloudProfileChanged
+ << KVBoxEventType_OnStorageControllerChanged
+ << KVBoxEventType_OnStorageDeviceChanged
+ << KVBoxEventType_OnMediumChanged
+ << KVBoxEventType_OnMediumConfigChanged
+ << KVBoxEventType_OnMediumRegistered;
+
+ /* Register event listener for event source aggregator: */
+ m_comEventSource.RegisterListener(m_comEventListener, eventTypes, FALSE /* active? */);
+ AssertWrapperOk(m_comEventSource);
+
+ /* Register event sources in their listeners as well: */
+ m_pQtListener->getWrapped()->registerSource(m_comEventSource, m_comEventListener);
+}
+
+void UIVirtualBoxEventHandlerProxy::prepareConnections()
+{
+ /* Create direct (sync) connections for signals of main event listener.
+ * Keep in mind that the abstract Qt4 connection notation should be used here. */
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigMachineStateChange(QUuid, KMachineState)),
+ this, SIGNAL(sigMachineStateChange(QUuid, KMachineState)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigMachineDataChange(QUuid)),
+ this, SIGNAL(sigMachineDataChange(QUuid)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigMachineRegistered(QUuid, bool)),
+ this, SIGNAL(sigMachineRegistered(QUuid, bool)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigMachineGroupsChange(QUuid)),
+ this, SIGNAL(sigMachineGroupsChange(QUuid)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigSessionStateChange(QUuid, KSessionState)),
+ this, SIGNAL(sigSessionStateChange(QUuid, KSessionState)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigSnapshotTake(QUuid, QUuid)),
+ this, SIGNAL(sigSnapshotTake(QUuid, QUuid)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigSnapshotDelete(QUuid, QUuid)),
+ this, SIGNAL(sigSnapshotDelete(QUuid, QUuid)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigSnapshotChange(QUuid, QUuid)),
+ this, SIGNAL(sigSnapshotChange(QUuid, QUuid)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigSnapshotRestore(QUuid, QUuid)),
+ this, SIGNAL(sigSnapshotRestore(QUuid, QUuid)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigCloudProviderListChanged()),
+ this, SIGNAL(sigCloudProviderListChanged()),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigCloudProviderUninstall(QUuid)),
+ this, SIGNAL(sigCloudProviderUninstall(QUuid)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigCloudProfileRegistered(QUuid, QString, bool)),
+ this, SIGNAL(sigCloudProfileRegistered(QUuid, QString, bool)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigCloudProfileChanged(QUuid, QString)),
+ this, SIGNAL(sigCloudProfileChanged(QUuid, QString)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigStorageControllerChange(QUuid, QString)),
+ this, SIGNAL(sigStorageControllerChange(QUuid, QString)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigStorageDeviceChange(CMediumAttachment, bool, bool)),
+ this, SIGNAL(sigStorageDeviceChange(CMediumAttachment, bool, bool)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigMediumChange(CMediumAttachment)),
+ this, SIGNAL(sigMediumChange(CMediumAttachment)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigMediumConfigChange(CMedium)),
+ this, SIGNAL(sigMediumConfigChange(CMedium)),
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), SIGNAL(sigMediumRegistered(QUuid, KDeviceType, bool)),
+ this, SIGNAL(sigMediumRegistered(QUuid, KDeviceType, bool)),
+ Qt::DirectConnection);
+}
+
+void UIVirtualBoxEventHandlerProxy::cleanupConnections()
+{
+ /* Nothing for now. */
+}
+
+void UIVirtualBoxEventHandlerProxy::cleanupListener()
+{
+ /* Unregister everything: */
+ m_pQtListener->getWrapped()->unregisterSources();
+
+ /* Unregister event listener for event source aggregator: */
+ m_comEventSource.UnregisterListener(m_comEventListener);
+ m_comEventSource.detach();
+}
+
+void UIVirtualBoxEventHandlerProxy::cleanup()
+{
+ /* Cleanup: */
+ cleanupConnections();
+ cleanupListener();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualBoxEventHandler implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIVirtualBoxEventHandler *UIVirtualBoxEventHandler::s_pInstance = 0;
+
+/* static */
+UIVirtualBoxEventHandler *UIVirtualBoxEventHandler::instance()
+{
+ if (!s_pInstance)
+ s_pInstance = new UIVirtualBoxEventHandler;
+ return s_pInstance;
+}
+
+/* static */
+void UIVirtualBoxEventHandler::destroy()
+{
+ if (s_pInstance)
+ {
+ delete s_pInstance;
+ s_pInstance = 0;
+ }
+}
+
+UIVirtualBoxEventHandler::UIVirtualBoxEventHandler()
+ : m_pProxy(new UIVirtualBoxEventHandlerProxy(this))
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIVirtualBoxEventHandler::prepare()
+{
+ /* Prepare connections: */
+ prepareConnections();
+}
+
+void UIVirtualBoxEventHandler::prepareConnections()
+{
+ /* Create queued (async) connections for signals of event proxy object.
+ * Keep in mind that the abstract Qt4 connection notation should be used here. */
+ connect(m_pProxy, SIGNAL(sigMachineStateChange(QUuid, KMachineState)),
+ this, SIGNAL(sigMachineStateChange(QUuid, KMachineState)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigMachineDataChange(QUuid)),
+ this, SIGNAL(sigMachineDataChange(QUuid)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigMachineRegistered(QUuid, bool)),
+ this, SIGNAL(sigMachineRegistered(QUuid, bool)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigMachineGroupsChange(QUuid)),
+ this, SIGNAL(sigMachineGroupsChange(QUuid)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigSessionStateChange(QUuid, KSessionState)),
+ this, SIGNAL(sigSessionStateChange(QUuid, KSessionState)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigSnapshotTake(QUuid, QUuid)),
+ this, SIGNAL(sigSnapshotTake(QUuid, QUuid)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigSnapshotDelete(QUuid, QUuid)),
+ this, SIGNAL(sigSnapshotDelete(QUuid, QUuid)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigSnapshotChange(QUuid, QUuid)),
+ this, SIGNAL(sigSnapshotChange(QUuid, QUuid)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigSnapshotRestore(QUuid, QUuid)),
+ this, SIGNAL(sigSnapshotRestore(QUuid, QUuid)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigCloudProviderListChanged()),
+ this, SIGNAL(sigCloudProviderListChanged()),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigCloudProviderUninstall(QUuid)),
+ this, SIGNAL(sigCloudProviderUninstall(QUuid)),
+ Qt::BlockingQueuedConnection);
+ connect(m_pProxy, SIGNAL(sigCloudProfileRegistered(QUuid, QString, bool)),
+ this, SIGNAL(sigCloudProfileRegistered(QUuid, QString, bool)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigCloudProfileChanged(QUuid, QString)),
+ this, SIGNAL(sigCloudProfileChanged(QUuid, QString)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigStorageControllerChange(QUuid, QString)),
+ this, SIGNAL(sigStorageControllerChange(QUuid, QString)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigStorageDeviceChange(CMediumAttachment, bool, bool)),
+ this, SIGNAL(sigStorageDeviceChange(CMediumAttachment, bool, bool)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigMediumChange(CMediumAttachment)),
+ this, SIGNAL(sigMediumChange(CMediumAttachment)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigMediumConfigChange(CMedium)),
+ this, SIGNAL(sigMediumConfigChange(CMedium)),
+ Qt::QueuedConnection);
+ connect(m_pProxy, SIGNAL(sigMediumRegistered(QUuid, KDeviceType, bool)),
+ this, SIGNAL(sigMediumRegistered(QUuid, KDeviceType, bool)),
+ Qt::QueuedConnection);
+}
+
+
+#include "UIVirtualBoxEventHandler.moc"
+
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxEventHandler.h b/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxEventHandler.h
new file mode 100644
index 00000000..3059aba8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/UIVirtualBoxEventHandler.h
@@ -0,0 +1,130 @@
+/* $Id: UIVirtualBoxEventHandler.h $ */
+/** @file
+ * VBox Qt GUI - UIVirtualBoxEventHandler class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_UIVirtualBoxEventHandler_h
+#define FEQT_INCLUDED_SRC_globals_UIVirtualBoxEventHandler_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMedium.h"
+#include "CMediumAttachment.h"
+
+/* Forward declarations: */
+class UIVirtualBoxEventHandlerProxy;
+
+/** Singleton QObject extension providing GUI with CVirtualBox event-source. */
+class SHARED_LIBRARY_STUFF UIVirtualBoxEventHandler : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about @a state change event for the machine with @a uId. */
+ void sigMachineStateChange(const QUuid &uId, const KMachineState state);
+ /** Notifies about data change event for the machine with @a uId. */
+ void sigMachineDataChange(const QUuid &uId);
+ /** Notifies about machine with @a uId was @a fRegistered. */
+ void sigMachineRegistered(const QUuid &uId, const bool fRegistered);
+ /** Notifies about machine with @a uId has groups changed. */
+ void sigMachineGroupsChange(const QUuid &uId);
+ /** Notifies about @a state change event for the session of the machine with @a uId. */
+ void sigSessionStateChange(const QUuid &uId, const KSessionState state);
+ /** Notifies about snapshot with @a uSnapshotId was taken for the machine with @a uId. */
+ void sigSnapshotTake(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about snapshot with @a uSnapshotId was deleted for the machine with @a uId. */
+ void sigSnapshotDelete(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about snapshot with @a uSnapshotId was changed for the machine with @a uId. */
+ void sigSnapshotChange(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about snapshot with @a uSnapshotId was restored for the machine with @a uId. */
+ void sigSnapshotRestore(const QUuid &uId, const QUuid &uSnapshotId);
+ /** Notifies about request to uninstall cloud provider with @a uId. */
+ void sigCloudProviderUninstall(const QUuid &uId);
+ /** Notifies about cloud provider list changed. */
+ void sigCloudProviderListChanged();
+ /** Notifies about cloud profile with specified @a strName of provider with specified @a uProviderId is @a fRegistered. */
+ void sigCloudProfileRegistered(const QUuid &uProviderId, const QString &strName, bool fRegistered);
+ /** Notifies about cloud profile with specified @a strName of provider with specified @a uProviderId is changed. */
+ void sigCloudProfileChanged(const QUuid &uProviderId, const QString &strName);
+
+ /** Notifies about storage controller change.
+ * @param uMachineId Brings the ID of machine corresponding controller belongs to.
+ * @param strControllerName Brings the name of controller this event is related to. */
+ void sigStorageControllerChange(const QUuid &uMachineId, const QString &strControllerName);
+ /** Notifies about storage device change.
+ * @param comAttachment Brings corresponding attachment.
+ * @param fRemoved Brings whether medium is removed or added.
+ * @param fSilent Brings whether this change has gone silent for guest. */
+ void sigStorageDeviceChange(CMediumAttachment comAttachment, bool fRemoved, bool fSilent);
+ /** Notifies about storage medium @a comAttachment state change. */
+ void sigMediumChange(CMediumAttachment comAttachment);
+ /** Notifies about storage @a comMedium config change. */
+ void sigMediumConfigChange(CMedium comMedium);
+ /** Notifies about storage medium is (un)registered.
+ * @param uMediumId Brings corresponding medium ID.
+ * @param enmMediumType Brings corresponding medium type.
+ * @param fRegistered Brings whether medium is registered or unregistered. */
+ void sigMediumRegistered(const QUuid &uMediumId, KDeviceType enmMediumType, bool fRegistered);
+
+public:
+
+ /** Returns singleton instance. */
+ static UIVirtualBoxEventHandler *instance();
+ /** Destroys singleton instance. */
+ static void destroy();
+
+protected:
+
+ /** Constructs VirtualBox event handler. */
+ UIVirtualBoxEventHandler();
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares connections. */
+ void prepareConnections();
+
+private:
+
+ /** Holds the singleton instance. */
+ static UIVirtualBoxEventHandler *s_pInstance;
+
+ /** Holds the VirtualBox event proxy instance. */
+ UIVirtualBoxEventHandlerProxy *m_pProxy;
+};
+
+/** Singleton VirtualBox Event Handler 'official' name. */
+#define gVBoxEvents UIVirtualBoxEventHandler::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_globals_UIVirtualBoxEventHandler_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/globals/VBoxUtils.h b/src/VBox/Frontends/VirtualBox/src/globals/VBoxUtils.h
new file mode 100644
index 00000000..810235e0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/globals/VBoxUtils.h
@@ -0,0 +1,78 @@
+/* $Id: VBoxUtils.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_globals_VBoxUtils_h
+#define FEQT_INCLUDED_SRC_globals_VBoxUtils_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMouseEvent>
+#include <QWidget>
+#include <QTextBrowser>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+/* Other VBox includes: */
+#include <iprt/types.h>
+
+
+/** QObject subclass,
+ * allowing to apply string-property value for a certain QObject. */
+class SHARED_LIBRARY_STUFF QObjectPropertySetter : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs setter for a property with certain @a strName, passing @a pParent to the base-class. */
+ QObjectPropertySetter(QObject *pParent, const QString &strName)
+ : QObject(pParent), m_strName(strName)
+ {}
+
+public slots:
+
+ /** Assigns string property @a strValue. */
+ void sltAssignProperty(const QString &strValue)
+ {
+ parent()->setProperty(m_strName.toLatin1().constData(), strValue);
+ }
+
+private:
+
+ /** Holds the property name. */
+ const QString m_strName;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_globals_VBoxUtils_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/guestctrl/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManager.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManager.cpp
new file mode 100644
index 00000000..bdd84e5c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManager.cpp
@@ -0,0 +1,859 @@
+/* $Id: UIFileManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFileManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QSplitter>
+
+/* GUI includes: */
+#include "QITabWidget.h"
+#include "QITreeWidget.h"
+#include "QIToolBar.h"
+#include "UIActionPool.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIErrorString.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIFileManager.h"
+#include "UIFileManagerOptionsPanel.h"
+#include "UIFileManagerLogPanel.h"
+#include "UIFileManagerOperationsPanel.h"
+#include "UIFileManagerGuestTable.h"
+#include "UIFileManagerHostTable.h"
+#include "UIGuestControlInterface.h"
+#include "UIVirtualMachineItem.h"
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CFsObjInfo.h"
+#include "CGuestDirectory.h"
+#include "CGuestFsObjInfo.h"
+#include "CGuestSession.h"
+
+
+/*********************************************************************************************************************************
+* UIFileOperationsList definition. *
+*********************************************************************************************************************************/
+
+class UIFileOperationsList : public QITreeWidget
+{
+ Q_OBJECT;
+public:
+
+ UIFileOperationsList(QWidget *pParent = 0);
+};
+
+
+/*********************************************************************************************************************************
+* UIFileManagerOptions implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerOptions *UIFileManagerOptions::m_pInstance = 0;
+
+UIFileManagerOptions* UIFileManagerOptions::instance()
+{
+ if (!m_pInstance)
+ m_pInstance = new UIFileManagerOptions;
+ return m_pInstance;
+}
+
+void UIFileManagerOptions::create()
+{
+ if (m_pInstance)
+ return;
+ m_pInstance = new UIFileManagerOptions;
+}
+
+void UIFileManagerOptions::destroy()
+{
+ delete m_pInstance;
+ m_pInstance = 0;
+}
+
+ UIFileManagerOptions::~UIFileManagerOptions()
+{
+}
+
+UIFileManagerOptions::UIFileManagerOptions()
+ : fListDirectoriesOnTop(true)
+ , fAskDeleteConfirmation(false)
+ , fShowHumanReadableSizes(true)
+ , fShowHiddenObjects(true)
+{
+}
+
+/*********************************************************************************************************************************
+* UIFileOperationsList implementation. *
+*********************************************************************************************************************************/
+
+UIFileOperationsList::UIFileOperationsList(QWidget *pParent)
+ :QITreeWidget(pParent)
+{}
+
+
+/*********************************************************************************************************************************
+* UIFileManager implementation. *
+*********************************************************************************************************************************/
+
+UIFileManager::UIFileManager(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ const CMachine &comMachine, QWidget *pParent, bool fShowToolbar)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pMainLayout(0)
+ , m_pVerticalSplitter(0)
+ , m_pFileTableSplitter(0)
+ , m_pToolBar(0)
+ , m_pVerticalToolBar(0)
+ , m_pHostFileTable(0)
+ , m_pGuestTablesContainer(0)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_pOptionsPanel(0)
+ , m_pLogPanel(0)
+ , m_pOperationsPanel(0)
+ , m_fCommitDataSignalReceived(false)
+{
+ loadOptions();
+ prepareObjects();
+ prepareConnections();
+ retranslateUi();
+ restorePanelVisibility();
+ UIFileManagerOptions::create();
+ uiCommon().setHelpKeyword(this, "guestadd-gc-file-manager");
+
+ if (!comMachine.isNull())
+ setMachines( QVector<QUuid>() << comMachine.GetId());
+}
+
+UIFileManager::~UIFileManager()
+{
+ UIFileManagerOptions::destroy();
+ if (m_pGuestTablesContainer)
+ {
+ for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
+ {
+ UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
+ if (pTable)
+ pTable->disconnect();
+ }
+ }
+}
+
+QMenu *UIFileManager::menu() const
+{
+ if (!m_pActionPool)
+ return 0;
+ return m_pActionPool->action(UIActionIndex_M_FileManager)->menu();
+}
+
+void UIFileManager::retranslateUi()
+{
+}
+
+void UIFileManager::prepareObjects()
+{
+ /* m_pMainLayout is the outer most layout containing the main toolbar and splitter widget: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (!m_pMainLayout)
+ return;
+
+ /* Configure layout: */
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setSpacing(10);
+#else
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ if (m_fShowToolbar)
+ prepareToolBar();
+
+ QWidget *pTopWidget = new QWidget;
+ QVBoxLayout *pTopLayout = new QVBoxLayout;
+ pTopLayout->setSpacing(0);
+ pTopLayout->setContentsMargins(0, 0, 0, 0);
+ pTopWidget->setLayout(pTopLayout);
+
+ m_pFileTableSplitter = new QSplitter;
+
+ if (m_pFileTableSplitter)
+ {
+ m_pFileTableSplitter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+ m_pFileTableSplitter->setContentsMargins(0, 0, 0, 0);
+
+ /* This widget hosts host file table and vertical toolbar. */
+ QWidget *pHostTableAndVerticalToolbarWidget = new QWidget;
+ QHBoxLayout *pHostTableAndVerticalToolbarLayout = new QHBoxLayout(pHostTableAndVerticalToolbarWidget);
+ pHostTableAndVerticalToolbarLayout->setSpacing(0);
+ pHostTableAndVerticalToolbarLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pHostFileTable = new UIFileManagerHostTable(m_pActionPool);
+ if (m_pHostFileTable)
+ pHostTableAndVerticalToolbarLayout->addWidget(m_pHostFileTable);
+
+ m_pFileTableSplitter->addWidget(pHostTableAndVerticalToolbarWidget);
+ prepareVerticalToolBar(pHostTableAndVerticalToolbarLayout);
+
+ m_pGuestTablesContainer = new QITabWidget;
+ if (m_pGuestTablesContainer)
+ {
+ m_pGuestTablesContainer->setTabPosition(QTabWidget::East);
+ m_pGuestTablesContainer->setTabBarAutoHide(true);
+ m_pFileTableSplitter->addWidget(m_pGuestTablesContainer);
+ }
+ m_pFileTableSplitter->setStretchFactor(0, 1);
+ m_pFileTableSplitter->setStretchFactor(1, 1);
+ }
+
+ pTopLayout->addWidget(m_pFileTableSplitter);
+ for (int i = 0; i < m_pFileTableSplitter->count(); ++i)
+ m_pFileTableSplitter->setCollapsible(i, false);
+
+ /* Create options and session panels and insert them into pTopLayout: */
+ prepareOptionsAndSessionPanels(pTopLayout);
+
+ /** Vertical splitter has 3 widgets. Log panel as bottom most one, operations panel on top of it,
+ * and pTopWidget which contains everthing else: */
+ m_pVerticalSplitter = new QSplitter;
+ if (m_pVerticalSplitter)
+ {
+ m_pMainLayout->addWidget(m_pVerticalSplitter);
+ m_pVerticalSplitter->setOrientation(Qt::Vertical);
+ m_pVerticalSplitter->setHandleWidth(4);
+
+ m_pVerticalSplitter->addWidget(pTopWidget);
+ /* Prepare operations and log panels and insert them into splitter: */
+ prepareOperationsAndLogPanels(m_pVerticalSplitter);
+
+ for (int i = 0; i < m_pVerticalSplitter->count(); ++i)
+ m_pVerticalSplitter->setCollapsible(i, false);
+ m_pVerticalSplitter->setStretchFactor(0, 3);
+ m_pVerticalSplitter->setStretchFactor(1, 1);
+ m_pVerticalSplitter->setStretchFactor(2, 1);
+ }
+}
+
+void UIFileManager::prepareVerticalToolBar(QHBoxLayout *layout)
+{
+ m_pVerticalToolBar = new QIToolBar;
+ if (!m_pVerticalToolBar && !m_pActionPool)
+ return;
+
+ m_pVerticalToolBar->setOrientation(Qt::Vertical);
+
+ /* Add to dummy QWidget to toolbar to center the action icons vertically: */
+ QWidget *topSpacerWidget = new QWidget(this);
+ topSpacerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+ topSpacerWidget->setVisible(true);
+ QWidget *bottomSpacerWidget = new QWidget(this);
+ bottomSpacerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+ bottomSpacerWidget->setVisible(true);
+
+ m_pVerticalToolBar->addWidget(topSpacerWidget);
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost))
+ {
+ m_pVerticalToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost));
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost)->setEnabled(false);
+ }
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest))
+ {
+ m_pVerticalToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest));
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest)->setEnabled(false);
+ }
+
+ m_pVerticalToolBar->addWidget(bottomSpacerWidget);
+
+ layout ->addWidget(m_pVerticalToolBar);
+}
+
+void UIFileManager::prepareConnections()
+{
+ if (m_pActionPool)
+ {
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_T_Options))
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_T_Options), &QAction::toggled,
+ this, &UIFileManager::sltPanelActionToggled);
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_T_Log))
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_T_Log), &QAction::toggled,
+ this, &UIFileManager::sltPanelActionToggled);
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_T_Operations))
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_T_Operations), &QAction::toggled,
+ this, &UIFileManager::sltPanelActionToggled);
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost))
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost), &QAction::triggered,
+ this, &UIFileManager::sltCopyGuestToHost);
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest))
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest), &QAction::triggered,
+ this, &UIFileManager::sltCopyHostToGuest);
+ }
+ if (m_pOptionsPanel)
+ {
+ connect(m_pOptionsPanel, &UIFileManagerOptionsPanel::sigHidePanel,
+ this, &UIFileManager::sltHandleHidePanel);
+ connect(m_pOptionsPanel, &UIFileManagerOptionsPanel::sigShowPanel,
+ this, &UIFileManager::sltHandleShowPanel);
+ connect(m_pOptionsPanel, &UIFileManagerOptionsPanel::sigOptionsChanged,
+ this, &UIFileManager::sltHandleOptionsUpdated);
+ }
+ if (m_pLogPanel)
+ {
+ connect(m_pLogPanel, &UIFileManagerLogPanel::sigHidePanel,
+ this, &UIFileManager::sltHandleHidePanel);
+ connect(m_pLogPanel, &UIFileManagerLogPanel::sigShowPanel,
+ this, &UIFileManager::sltHandleShowPanel);
+ }
+
+ if (m_pOperationsPanel)
+ {
+ connect(m_pOperationsPanel, &UIFileManagerOperationsPanel::sigHidePanel,
+ this, &UIFileManager::sltHandleHidePanel);
+ connect(m_pOperationsPanel, &UIFileManagerOperationsPanel::sigShowPanel,
+ this, &UIFileManager::sltHandleShowPanel);
+ }
+ if (m_pHostFileTable)
+ {
+ connect(m_pHostFileTable, &UIFileManagerHostTable::sigLogOutput,
+ this, &UIFileManager::sltReceieveLogOutput);
+ connect(m_pHostFileTable, &UIFileManagerHostTable::sigDeleteConfirmationOptionChanged,
+ this, &UIFileManager::sltHandleOptionsUpdated);
+ connect(m_pHostFileTable, &UIFileManagerGuestTable::sigSelectionChanged,
+ this, &UIFileManager::sltFileTableSelectionChanged);
+ }
+ if (m_pGuestTablesContainer)
+ connect(m_pGuestTablesContainer, &QITabWidget::currentChanged, this,
+ &UIFileManager::sltCurrentTabChanged);
+
+ connect(&uiCommon(), &UICommon::sigAskToCommitData,
+ this, &UIFileManager::sltCommitDataSignalReceived);
+}
+
+void UIFileManager::prepareToolBar()
+{
+ /* Create toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ if (m_pToolBar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_Options));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_Operations));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_Log));
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pToolBar);
+ }
+#else
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pToolBar);
+#endif
+ }
+}
+
+void UIFileManager::sltReceieveLogOutput(QString strOutput, const QString &strMachineName, FileManagerLogType eLogType)
+{
+ appendLog(strOutput, strMachineName, eLogType);
+}
+
+void UIFileManager::sltCopyGuestToHost()
+{
+ copyToHost();
+}
+
+void UIFileManager::sltCopyHostToGuest()
+{
+ copyToGuest();
+}
+
+void UIFileManager::sltPanelActionToggled(bool fChecked)
+{
+ QAction *pSenderAction = qobject_cast<QAction*>(sender());
+ if (!pSenderAction)
+ return;
+ UIDialogPanel* pPanel = 0;
+ /* Look for the sender() within the m_panelActionMap's values: */
+ for (QMap<UIDialogPanel*, QAction*>::const_iterator iterator = m_panelActionMap.begin();
+ iterator != m_panelActionMap.end(); ++iterator)
+ {
+ if (iterator.value() == pSenderAction)
+ pPanel = iterator.key();
+ }
+ if (!pPanel)
+ return;
+ if (fChecked)
+ showPanel(pPanel);
+ else
+ hidePanel(pPanel);
+}
+
+void UIFileManager::sltReceieveNewFileOperation(const CProgress &comProgress, const QString &strTableName)
+{
+ if (m_pOperationsPanel)
+ m_pOperationsPanel->addNewProgress(comProgress, strTableName);
+}
+
+void UIFileManager::sltFileOperationComplete(QUuid progressId)
+{
+ Q_UNUSED(progressId);
+ if (m_pHostFileTable)
+ m_pHostFileTable->refresh();
+ /// @todo we need to refresh only the table from which the completed file operation has originated
+ for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
+ {
+ UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
+ if (pTable)
+ pTable->refresh();
+ }
+}
+
+void UIFileManager::sltHandleOptionsUpdated()
+{
+ if (m_pOptionsPanel)
+ m_pOptionsPanel->update();
+
+ for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
+ {
+ UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
+ if (pTable)
+ pTable->optionsUpdated();
+ }
+ if (m_pHostFileTable)
+ m_pHostFileTable->optionsUpdated();
+ saveOptions();
+}
+
+void UIFileManager::sltHandleHidePanel(UIDialogPanel *pPanel)
+{
+ hidePanel(pPanel);
+}
+
+void UIFileManager::sltHandleShowPanel(UIDialogPanel *pPanel)
+{
+ showPanel(pPanel);
+}
+
+void UIFileManager::sltCommitDataSignalReceived()
+{
+ m_fCommitDataSignalReceived = true;
+}
+
+void UIFileManager::sltFileTableSelectionChanged(bool fHasSelection)
+{
+ /* If we dont have a guest session running that actions should stay disabled: */
+ if (!currentGuestTable() || !currentGuestTable()->isGuestSessionRunning())
+ {
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest)->setEnabled(false);
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost)->setEnabled(false);
+ return;
+ }
+
+ /* Enable/disable vertical toolbar actions: */
+ UIFileManagerGuestTable *pGuestTable = qobject_cast<UIFileManagerGuestTable*>(sender());
+
+ /* If the signal is coming from a guest table which is not the current one just dont do anything: */
+ if (pGuestTable && pGuestTable != currentGuestTable())
+ return;
+
+ if (pGuestTable)
+ {
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost))
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost)->setEnabled(fHasSelection);
+ return;
+ }
+
+ if (sender() == m_pHostFileTable && m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest))
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest)->setEnabled(fHasSelection);
+}
+
+void UIFileManager::sltCurrentTabChanged(int iIndex)
+{
+ Q_UNUSED(iIndex);
+ setVerticalToolBarActionsEnabled();
+
+ /* Mark the current guest table: */
+ UIFileManagerGuestTable *pCurrentGuestTable = currentGuestTable();
+ if (!pCurrentGuestTable)
+ return;
+ for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
+ {
+ UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
+ if (!pTable)
+ continue;
+ pTable->setIsCurrent(pTable == pCurrentGuestTable);
+ }
+ /* Disable host file table if guest session is not running: */
+ if (m_pHostFileTable)
+ m_pHostFileTable->setEnabled(pCurrentGuestTable->isGuestSessionRunning());
+ /* Disable/enable file table submenus of the menu: */
+ UIMenu *pGuestSubmenu = m_pActionPool->action(UIActionIndex_M_FileManager_M_GuestSubmenu)->menu();
+ if (pGuestSubmenu)
+ pGuestSubmenu->setEnabled(pCurrentGuestTable->isGuestSessionRunning());
+ UIMenu *pHostSubmenu = m_pActionPool->action(UIActionIndex_M_FileManager_M_HostSubmenu)->menu();
+ if (pHostSubmenu)
+ pHostSubmenu->setEnabled(pCurrentGuestTable->isGuestSessionRunning());
+}
+
+void UIFileManager::sltGuestFileTableStateChanged(bool fIsRunning)
+{
+ if (m_pHostFileTable)
+ m_pHostFileTable->setEnabled(fIsRunning);
+}
+
+void UIFileManager::setVerticalToolBarActionsEnabled()
+{
+ if (!m_pGuestTablesContainer)
+ return;
+ UIFileManagerGuestTable *pTable = currentGuestTable();
+ if (!pTable)
+ return;
+
+ bool fRunning = pTable->isGuestSessionRunning();
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost))
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToHost)->setEnabled(fRunning && pTable->hasSelection());
+
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest))
+ {
+ bool fHostHasSelection = m_pHostFileTable ? m_pHostFileTable->hasSelection() : false;
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_CopyToGuest)->setEnabled(fRunning && fHostHasSelection);
+ }
+}
+
+void UIFileManager::copyToHost()
+{
+ if (m_pGuestTablesContainer && m_pHostFileTable)
+ {
+ UIFileManagerGuestTable *pGuestFileTable = currentGuestTable();
+ if (pGuestFileTable)
+ pGuestFileTable->copyGuestToHost(m_pHostFileTable->currentDirectoryPath());
+ }
+}
+
+void UIFileManager::copyToGuest()
+{
+ if (m_pGuestTablesContainer && m_pHostFileTable)
+ {
+ UIFileManagerGuestTable *pGuestFileTable = currentGuestTable();
+ if (pGuestFileTable)
+ pGuestFileTable->copyHostToGuest(m_pHostFileTable->selectedItemPathList());
+ }
+}
+
+void UIFileManager::prepareOptionsAndSessionPanels(QVBoxLayout *pLayout)
+{
+ if (!pLayout)
+ return;
+
+ m_pOptionsPanel = new UIFileManagerOptionsPanel(0 /*parent */, UIFileManagerOptions::instance());
+ if (m_pOptionsPanel)
+ {
+ m_pOptionsPanel->hide();
+ m_panelActionMap.insert(m_pOptionsPanel, m_pActionPool->action(UIActionIndex_M_FileManager_T_Options));
+ pLayout->addWidget(m_pOptionsPanel);
+ }
+}
+
+void UIFileManager::prepareOperationsAndLogPanels(QSplitter *pSplitter)
+{
+ if (!pSplitter)
+ return;
+ m_pOperationsPanel = new UIFileManagerOperationsPanel;
+ if (m_pOperationsPanel)
+ {
+ m_pOperationsPanel->hide();
+ connect(m_pOperationsPanel, &UIFileManagerOperationsPanel::sigFileOperationComplete,
+ this, &UIFileManager::sltFileOperationComplete);
+ connect(m_pOperationsPanel, &UIFileManagerOperationsPanel::sigFileOperationFail,
+ this, &UIFileManager::sltReceieveLogOutput);
+ m_panelActionMap.insert(m_pOperationsPanel, m_pActionPool->action(UIActionIndex_M_FileManager_T_Operations));
+ }
+ pSplitter->addWidget(m_pOperationsPanel);
+ m_pLogPanel = new UIFileManagerLogPanel;
+ if (m_pLogPanel)
+ {
+ m_pLogPanel->hide();
+ m_panelActionMap.insert(m_pLogPanel, m_pActionPool->action(UIActionIndex_M_FileManager_T_Log));
+ }
+ pSplitter->addWidget(m_pLogPanel);
+}
+
+
+template<typename T>
+QStringList UIFileManager::getFsObjInfoStringList(const T &fsObjectInfo) const
+{
+ QStringList objectInfo;
+ if (!fsObjectInfo.isOk())
+ return objectInfo;
+ objectInfo << fsObjectInfo.GetName();
+ return objectInfo;
+}
+
+void UIFileManager::saveOptions()
+{
+ if (m_fCommitDataSignalReceived)
+ return;
+ /* Save the options: */
+ UIFileManagerOptions *pOptions = UIFileManagerOptions::instance();
+ if (pOptions)
+ {
+ gEDataManager->setFileManagerOptions(pOptions->fListDirectoriesOnTop,
+ pOptions->fAskDeleteConfirmation,
+ pOptions->fShowHumanReadableSizes,
+ pOptions->fShowHiddenObjects);
+ }
+}
+
+void UIFileManager::restorePanelVisibility()
+{
+ /** Make sure the actions are set to not-checked. this prevents an unlikely
+ * bug when the extrakey for the visible panels are manually modified: */
+ foreach(QAction* pAction, m_panelActionMap.values())
+ {
+ pAction->blockSignals(true);
+ pAction->setChecked(false);
+ pAction->blockSignals(false);
+ }
+
+ /* Load the visible panel list and show them: */
+ QStringList strNameList = gEDataManager->fileManagerVisiblePanels();
+ foreach(const QString strName, strNameList)
+ {
+ foreach(UIDialogPanel* pPanel, m_panelActionMap.keys())
+ {
+ if (strName == pPanel->panelName())
+ {
+ showPanel(pPanel);
+ break;
+ }
+ }
+ }
+}
+
+void UIFileManager::loadOptions()
+{
+ /* Load options: */
+ UIFileManagerOptions *pOptions = UIFileManagerOptions::instance();
+ if (pOptions)
+ {
+ pOptions->fListDirectoriesOnTop = gEDataManager->fileManagerListDirectoriesFirst();
+ pOptions->fAskDeleteConfirmation = gEDataManager->fileManagerShowDeleteConfirmation();
+ pOptions->fShowHumanReadableSizes = gEDataManager->fileManagerShowHumanReadableSizes();
+ pOptions->fShowHiddenObjects = gEDataManager->fileManagerShowHiddenObjects();
+ }
+}
+
+void UIFileManager::hidePanel(UIDialogPanel* panel)
+{
+ if (!m_pActionPool)
+ return;
+ if (panel && panel->isVisible())
+ panel->setVisible(false);
+ QMap<UIDialogPanel*, QAction*>::iterator iterator = m_panelActionMap.find(panel);
+ if (iterator != m_panelActionMap.end())
+ {
+ if (iterator.value() && iterator.value()->isChecked())
+ iterator.value()->setChecked(false);
+ }
+ m_visiblePanelsList.removeAll(panel);
+ manageEscapeShortCut();
+ savePanelVisibility();
+}
+
+void UIFileManager::showPanel(UIDialogPanel* panel)
+{
+ if (panel && panel->isHidden())
+ panel->setVisible(true);
+ QMap<UIDialogPanel*, QAction*>::iterator iterator = m_panelActionMap.find(panel);
+ if (iterator != m_panelActionMap.end())
+ {
+ if (!iterator.value()->isChecked())
+ iterator.value()->setChecked(true);
+ }
+ if (!m_visiblePanelsList.contains(panel))
+ m_visiblePanelsList.push_back(panel);
+ manageEscapeShortCut();
+ savePanelVisibility();
+}
+
+void UIFileManager::manageEscapeShortCut()
+{
+ /* if there is no visible panels give the escape shortcut to parent dialog: */
+ if (m_visiblePanelsList.isEmpty())
+ {
+ emit sigSetCloseButtonShortCut(QKeySequence(Qt::Key_Escape));
+ return;
+ }
+ /* Take the escape shortcut from the dialog: */
+ emit sigSetCloseButtonShortCut(QKeySequence());
+ /* Just loop thru the visible panel list and set the esc key to the
+ panel which made visible latest */
+ for (int i = 0; i < m_visiblePanelsList.size() - 1; ++i)
+ m_visiblePanelsList[i]->setCloseButtonShortCut(QKeySequence());
+
+ m_visiblePanelsList.back()->setCloseButtonShortCut(QKeySequence(Qt::Key_Escape));
+}
+
+void UIFileManager::appendLog(const QString &strLog, const QString &strMachineName, FileManagerLogType eLogType)
+{
+ if (!m_pLogPanel)
+ return;
+ m_pLogPanel->appendLog(strLog, strMachineName, eLogType);
+}
+
+void UIFileManager::savePanelVisibility()
+{
+ if (m_fCommitDataSignalReceived)
+ return;
+ /* Save a list of currently visible panels: */
+ QStringList strNameList;
+ foreach(UIDialogPanel* pPanel, m_visiblePanelsList)
+ strNameList.append(pPanel->panelName());
+ gEDataManager->setFileManagerVisiblePanels(strNameList);
+}
+
+void UIFileManager::setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items)
+{
+ AssertReturnVoid(m_pGuestTablesContainer);
+ QVector<QUuid> selectedMachines;
+
+ foreach (const UIVirtualMachineItem *item, items)
+ {
+ if (!item)
+ continue;
+ selectedMachines << item->id();
+ }
+ QUuid lastSelection = selectedMachines.isEmpty() ? QUuid() : selectedMachines.last();
+ /** Iterate through the current tabs and add any machine id for which we have a running guest session to the
+ * list of machine ids we want to have a tab for: */
+ for (int i = 0; i < m_pGuestTablesContainer->count(); ++i)
+ {
+ UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
+ if (!pTable || !pTable->isGuestSessionRunning())
+ continue;
+ if (!selectedMachines.contains(pTable->machineId()))
+ selectedMachines << pTable->machineId();
+ }
+
+ setMachines(selectedMachines, lastSelection);
+}
+
+void UIFileManager::setMachines(const QVector<QUuid> &machineIds, const QUuid &lastSelectedMachineId /* = QUuid() */)
+{
+ AssertReturnVoid(m_pGuestTablesContainer);
+
+ /* List of machines that are newly added to selected machine list: */
+ QVector<QUuid> newSelections;
+ QVector<QUuid> unselectedMachines(m_machineIds);
+
+ foreach (const QUuid &id, machineIds)
+ {
+ unselectedMachines.removeAll(id);
+ if (!m_machineIds.contains(id))
+ newSelections << id;
+ }
+ m_machineIds = machineIds;
+
+ addTabs(newSelections);
+ removeTabs(unselectedMachines);
+ if (!lastSelectedMachineId.isNull())
+ {
+ int iIndexToSelect = -1;
+ for (int i = 0; i < m_pGuestTablesContainer->count() && iIndexToSelect == -1; ++i)
+ {
+ UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
+ if (!pTable)
+ continue;
+ if (lastSelectedMachineId == pTable->machineId())
+ iIndexToSelect = i;
+ }
+ if (iIndexToSelect != -1)
+ m_pGuestTablesContainer->setCurrentIndex(iIndexToSelect);
+ }
+}
+
+void UIFileManager::removeTabs(const QVector<QUuid> &machineIdsToRemove)
+{
+ if (!m_pGuestTablesContainer)
+ return;
+ QVector<UIFileManagerGuestTable*> removeList;
+
+ for (int i = m_pGuestTablesContainer->count() - 1; i >= 0; --i)
+ {
+ UIFileManagerGuestTable *pTable = qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->widget(i));
+ if (!pTable)
+ continue;
+ if (machineIdsToRemove.contains(pTable->machineId()))
+ {
+ removeList << pTable;
+ m_pGuestTablesContainer->removeTab(i);
+ }
+ }
+ qDeleteAll(removeList.begin(), removeList.end());
+}
+
+void UIFileManager::addTabs(const QVector<QUuid> &machineIdsToAdd)
+{
+ if (!m_pGuestTablesContainer)
+ return;
+
+ foreach (const QUuid &id, machineIdsToAdd)
+ {
+ CMachine comMachine = uiCommon().virtualBox().FindMachine(id.toString());
+ if (comMachine.isNull())
+ continue;
+ UIFileManagerGuestTable *pGuestFileTable = new UIFileManagerGuestTable(m_pActionPool, comMachine, m_pGuestTablesContainer);
+ m_pGuestTablesContainer->addTab(pGuestFileTable, comMachine.GetName());
+ if (pGuestFileTable)
+ {
+ connect(pGuestFileTable, &UIFileManagerGuestTable::sigLogOutput,
+ this, &UIFileManager::sltReceieveLogOutput);
+ connect(pGuestFileTable, &UIFileManagerGuestTable::sigSelectionChanged,
+ this, &UIFileManager::sltFileTableSelectionChanged);
+ connect(pGuestFileTable, &UIFileManagerGuestTable::sigNewFileOperation,
+ this, &UIFileManager::sltReceieveNewFileOperation);
+ connect(pGuestFileTable, &UIFileManagerGuestTable::sigDeleteConfirmationOptionChanged,
+ this, &UIFileManager::sltHandleOptionsUpdated);
+ connect(pGuestFileTable, &UIFileManagerGuestTable::sigStateChanged,
+ this, &UIFileManager::sltGuestFileTableStateChanged);
+ }
+ }
+}
+
+UIFileManagerGuestTable *UIFileManager::currentGuestTable()
+{
+ if (!m_pGuestTablesContainer)
+ return 0;
+ return qobject_cast<UIFileManagerGuestTable*>(m_pGuestTablesContainer->currentWidget());
+}
+#include "UIFileManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManager.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManager.h
new file mode 100644
index 00000000..7d1dbe79
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManager.h
@@ -0,0 +1,198 @@
+/* $Id: UIFileManager.h $ */
+/** @file
+ * VBox Qt GUI - UIFileManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIFileManager_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIFileManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPointer>
+#include <QWidget>
+#include <QString>
+#include <QUuid>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIGuestControlDefs.h"
+
+
+/* Forward declarations: */
+class CMachine;
+class CProgress;
+class QHBoxLayout;
+class QSplitter;
+class QTextEdit;
+class QVBoxLayout;
+class UIActionPool;
+class UIDialogPanel;
+class UIFileManagerLogPanel;
+class UIFileManagerOperationsPanel;
+class UIFileManagerOptionsPanel;
+class UIFileManagerGuestTable;
+class UIFileManagerHostTable;
+class UIVirtualMachineItem;
+class QITabWidget;
+class QIToolBar;
+
+/** A Utility class to manage file manager options. */
+class UIFileManagerOptions
+{
+
+public:
+
+ static UIFileManagerOptions* instance();
+ static void create();
+ static void destroy();
+
+ bool fListDirectoriesOnTop;
+ bool fAskDeleteConfirmation;
+ bool fShowHumanReadableSizes;
+ bool fShowHiddenObjects;
+
+private:
+
+ UIFileManagerOptions();
+ ~UIFileManagerOptions();
+
+ static UIFileManagerOptions *m_pInstance;
+};
+
+/** A QWidget extension. it includes a QWidget extension for initiating a guest session
+ * one host and one guest file table views, a log viewer
+ * and some other file manager related widgets. */
+class SHARED_LIBRARY_STUFF UIFileManager : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigSetCloseButtonShortCut(QKeySequence);
+
+public:
+
+ UIFileManager(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ const CMachine &comMachine, QWidget *pParent, bool fShowToolbar);
+ ~UIFileManager();
+ QMenu *menu() const;
+
+#ifdef VBOX_WS_MAC
+ /** Returns the toolbar. */
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+ void setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items);
+
+protected:
+
+ void retranslateUi();
+
+private slots:
+
+ void sltReceieveLogOutput(QString strOutput, const QString &strMachineName, FileManagerLogType eLogType);
+ void sltCopyGuestToHost();
+ void sltCopyHostToGuest();
+ void sltPanelActionToggled(bool fChecked);
+ void sltReceieveNewFileOperation(const CProgress &comProgress, const QString &strTableName);
+ void sltFileOperationComplete(QUuid progressId);
+ /** Performs whatever necessary when some signal about option change has been receieved. */
+ void sltHandleOptionsUpdated();
+ void sltHandleHidePanel(UIDialogPanel *pPanel);
+ void sltHandleShowPanel(UIDialogPanel *pPanel);
+ void sltCommitDataSignalReceived();
+ void sltFileTableSelectionChanged(bool fHasSelection);
+ void sltCurrentTabChanged(int iIndex);
+ void sltGuestFileTableStateChanged(bool fIsRunning);
+
+private:
+
+ void prepareObjects();
+ void prepareConnections();
+ void prepareVerticalToolBar(QHBoxLayout *layout);
+ void prepareToolBar();
+ /** Creates options and sessions panels and adds them to @p pLayout. */
+ void prepareOptionsAndSessionPanels(QVBoxLayout *pLayout);
+ void prepareOperationsAndLogPanels(QSplitter *pSplitter);
+
+ /** Saves list of panels and file manager options to the extra data. */
+ void saveOptions();
+ /** Show the panels that have been visible the last time file manager is closed. */
+ void restorePanelVisibility();
+ /** Loads file manager options. This should be done before widget creation
+ * since some widgets are initilized with these options */
+ void loadOptions();
+ void hidePanel(UIDialogPanel *panel);
+ void showPanel(UIDialogPanel *panel);
+ /** Makes sure escape key is assigned to only a single widget. This is done by checking
+ several things in the following order:
+ - when there are no more panels visible assign it to the parent dialog
+ - grab it from the dialog as soon as a panel becomes visible again
+ - assign it to the most recently "unhidden" panel */
+ void manageEscapeShortCut();
+ void copyToGuest();
+ void copyToHost();
+ template<typename T>
+ QStringList getFsObjInfoStringList(const T &fsObjectInfo) const;
+ void appendLog(const QString &strLog, const QString &strMachineName, FileManagerLogType eLogType);
+ void savePanelVisibility();
+
+ void setMachines(const QVector<QUuid> &machineIDs, const QUuid &lastSelectedMachineId = QUuid());
+ void removeTabs(const QVector<QUuid> &machineIdsToRemove);
+ void addTabs(const QVector<QUuid> &machineIdsToAdd);
+ void setVerticalToolBarActionsEnabled();
+ UIFileManagerGuestTable *currentGuestTable();
+
+ QVBoxLayout *m_pMainLayout;
+ QSplitter *m_pVerticalSplitter;
+ /** Splitter hosting host and guest file system tables. */
+ QSplitter *m_pFileTableSplitter;
+ QIToolBar *m_pToolBar;
+ QIToolBar *m_pVerticalToolBar;
+
+ UIFileManagerHostTable *m_pHostFileTable;
+
+ QITabWidget *m_pGuestTablesContainer;
+ const EmbedTo m_enmEmbedding;
+ QPointer<UIActionPool> m_pActionPool;
+ const bool m_fShowToolbar;
+ QMap<UIDialogPanel*, QAction*> m_panelActionMap;
+ QList<UIDialogPanel*> m_visiblePanelsList;
+ UIFileManagerOptionsPanel *m_pOptionsPanel;
+ UIFileManagerLogPanel *m_pLogPanel;
+ UIFileManagerOperationsPanel *m_pOperationsPanel;
+
+ bool m_fCommitDataSignalReceived;
+
+ QVector<QUuid> m_machineIds;
+
+ friend class UIFileManagerOptionsPanel;
+ friend class UIFileManagerDialog;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIFileManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerDialog.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerDialog.cpp
new file mode 100644
index 00000000..fa0e9b65
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerDialog.cpp
@@ -0,0 +1,191 @@
+/* $Id: UIFileManagerDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFileManagerDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPushButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIFileManager.h"
+#include "UIFileManagerDialog.h"
+#include "UICommon.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+
+/*********************************************************************************************************************************
+* Class UIFileManagerDialogFactory implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerDialogFactory::UIFileManagerDialogFactory(UIActionPool *pActionPool, const QUuid &uMachineId, const QString &strMachineName)
+ : m_pActionPool(pActionPool)
+ , m_uMachineId(uMachineId)
+ , m_strMachineName(strMachineName)
+{
+}
+
+
+UIFileManagerDialogFactory::UIFileManagerDialogFactory()
+ : m_pActionPool(0)
+ , m_uMachineId(QUuid())
+{
+}
+
+void UIFileManagerDialogFactory::create(QIManagerDialog *&pDialog, QWidget *pCenterWidget)
+{
+ pDialog = new UIFileManagerDialog(pCenterWidget, m_pActionPool, m_uMachineId, m_strMachineName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIFileManagerDialog implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerDialog::UIFileManagerDialog(QWidget *pCenterWidget,
+ UIActionPool *pActionPool,
+ const QUuid &uMachineId,
+ const QString &strMachineName)
+ : QIWithRetranslateUI<QIManagerDialog>(pCenterWidget)
+ , m_pActionPool(pActionPool)
+ , m_uMachineId(uMachineId)
+ , m_strMachineName(strMachineName)
+{
+}
+
+UIFileManagerDialog::~UIFileManagerDialog()
+{
+}
+
+void UIFileManagerDialog::retranslateUi()
+{
+ if (!m_strMachineName.isEmpty())
+ setWindowTitle(UIFileManager::tr("%1 - File Manager").arg(m_strMachineName));
+ else
+ setWindowTitle(UIFileManager::tr("File Manager"));
+
+ /* Retranslate button box buttons: */
+ if (button(ButtonType_Close))
+ {
+ button(ButtonType_Close)->setText(UIFileManager::tr("Close"));
+ button(ButtonType_Close)->setStatusTip(UIFileManager::tr("Close dialog without saving"));
+ button(ButtonType_Close)->setShortcut(Qt::Key_Escape);
+ button(ButtonType_Close)->setToolTip(UIFileManager::tr("Reset Changes (%1)").arg(button(ButtonType_Close)->shortcut().toString()));
+ }
+
+ if (button(ButtonType_Help))
+ {
+ button(ButtonType_Help)->setText(UIFileManager::tr("Help"));
+ button(ButtonType_Help)->setStatusTip(UIFileManager::tr("Show dialog help"));
+ button(ButtonType_Help)->setShortcut(QKeySequence::HelpContents);
+ button(ButtonType_Help)->setToolTip(UIFileManager::tr("Show Help (%1)").arg(button(ButtonType_Help)->shortcut().toString()));
+ }
+}
+
+void UIFileManagerDialog::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/file_manager_32px.png", ":/file_manager_16px.png"));
+#endif
+}
+
+void UIFileManagerDialog::configureCentralWidget()
+{
+ CMachine comMachine;
+ CVirtualBox vbox = uiCommon().virtualBox();
+ if (!vbox.isNull() && !m_uMachineId.isNull())
+ comMachine = vbox.FindMachine(m_uMachineId.toString());
+ /* Create widget: */
+ UIFileManager *pWidget = new UIFileManager(EmbedTo_Dialog, m_pActionPool,
+ comMachine, this, true);
+
+ if (pWidget)
+ {
+ /* Configure widget: */
+ setWidget(pWidget);
+ setWidgetMenu(pWidget->menu());
+#ifdef VBOX_WS_MAC
+ setWidgetToolbar(pWidget->toolbar());
+#endif
+ connect(pWidget, &UIFileManager::sigSetCloseButtonShortCut,
+ this, &UIFileManagerDialog::sltSetCloseButtonShortCut);
+
+ /* Add into layout: */
+ centralWidget()->layout()->addWidget(pWidget);
+ }
+}
+
+void UIFileManagerDialog::finalize()
+{
+ /* Apply language settings: */
+ retranslateUi();
+ manageEscapeShortCut();
+}
+
+void UIFileManagerDialog::loadSettings()
+{
+ /* Load geometry from extradata: */
+ const QRect geo = gEDataManager->fileManagerDialogGeometry(this, centerWidget());
+ LogRel2(("GUI: UIFileManagerDialog: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ restoreGeometry(geo);
+}
+
+void UIFileManagerDialog::saveSettings()
+{
+ /* Save geometry to extradata: */
+ const QRect geo = currentGeometry();
+ LogRel2(("GUI: UIFileManagerDialog: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ gEDataManager->setFileManagerDialogGeometry(geo, isCurrentlyMaximized());
+}
+
+bool UIFileManagerDialog::shouldBeMaximized() const
+{
+ return gEDataManager->fileManagerDialogShouldBeMaximized();
+}
+
+void UIFileManagerDialog::sltSetCloseButtonShortCut(QKeySequence shortcut)
+{
+ if (!closeEmitted() && button(ButtonType_Close))
+ button(ButtonType_Close)->setShortcut(shortcut);
+}
+
+void UIFileManagerDialog::manageEscapeShortCut()
+{
+ UIFileManager *pWidget = qobject_cast<UIFileManager*>(widget());
+ if (!pWidget)
+ return;
+ pWidget->manageEscapeShortCut();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerDialog.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerDialog.h
new file mode 100644
index 00000000..126a1fd2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerDialog.h
@@ -0,0 +1,126 @@
+/* $Id: UIFileManagerDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIFileManagerDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIFileManagerDialog_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIFileManagerDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+#include <QUuid>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+
+
+/* Forward declarations: */
+class QDialogButtonBox;
+class QVBoxLayout;
+class UIActionPool;
+class UIFileManagerDialog;
+
+
+/** QIManagerDialogFactory extension used as a factory for the file manager dialog. */
+class UIFileManagerDialogFactory : public QIManagerDialogFactory
+{
+public:
+
+ UIFileManagerDialogFactory(UIActionPool *pActionPool, const QUuid &uMachineId, const QString &strMachineName);
+ UIFileManagerDialogFactory();
+
+protected:
+
+ /** Creates derived @a pDialog instance.
+ * @param pCenterWidget Passes the widget to center wrt. pCenterWidget. */
+ virtual void create(QIManagerDialog *&pDialog, QWidget *pCenterWidget) RT_OVERRIDE;
+
+ UIActionPool *m_pActionPool;
+ QUuid m_uMachineId;
+ QString m_strMachineName;
+};
+
+/** QIManagerDialog extension providing GUI with the dialog displaying file manager releated logs. */
+class UIFileManagerDialog : public QIWithRetranslateUI<QIManagerDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs File Manager dialog.
+ * @param pCenterWidget Passes the widget reference to center according to.
+ * @param pActionPool Passes the action-pool reference.
+ * @param uMachineId Passes the machine id. */
+ UIFileManagerDialog(QWidget *pCenterWidget, UIActionPool *pActionPool, const QUuid &uMachineId, const QString &strMachineName);
+ ~UIFileManagerDialog();
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ virtual void configure() RT_OVERRIDE;
+ /** Configures central-widget. */
+ virtual void configureCentralWidget() RT_OVERRIDE;
+ /** Perform final preparations. */
+ virtual void finalize() RT_OVERRIDE;
+ /** Loads dialog setting from extradata. */
+ virtual void loadSettings() RT_OVERRIDE;
+
+ /** Saves dialog setting into extradata. */
+ virtual void saveSettings() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Functions related to geometry restoration.
+ * @{ */
+ /** Returns whether the window should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ void sltSetCloseButtonShortCut(QKeySequence shortcut);
+
+private:
+
+ void manageEscapeShortCut();
+ UIActionPool *m_pActionPool;
+ QUuid m_uMachineId;
+ QString m_strMachineName;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIFileManagerDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerGuestTable.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerGuestTable.cpp
new file mode 100644
index 00000000..263ebeb0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerGuestTable.cpp
@@ -0,0 +1,1613 @@
+/* $Id: UIFileManagerGuestTable.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFileManagerGuestTable class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDateTime>
+#include <QFileInfo>
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QUuid>
+
+/* GUI includes: */
+#include "QILabel.h"
+#include "UIActionPool.h"
+#include "UIConverter.h"
+#include "UICommon.h"
+#include "UICustomFileSystemModel.h"
+#include "UIErrorString.h"
+#include "UIFileManager.h"
+#include "UIFileManagerHostTable.h"
+#include "UIFileManagerGuestTable.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIPathOperations.h"
+#include "UIUserNamePasswordEditor.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "QILineEdit.h"
+#include "QIToolBar.h"
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CFsObjInfo.h"
+#include "CGuestFsObjInfo.h"
+#include "CGuestDirectory.h"
+#include "CProgress.h"
+#include "CGuestSessionStateChangedEvent.h"
+
+#include <iprt/path.h>
+#include <iprt/err.h>
+
+/*********************************************************************************************************************************
+* UIGuestSessionWidget definition. *
+*********************************************************************************************************************************/
+/** A QWidget extension containing text entry fields for password and username and buttons to
+ * start/stop a guest session. */
+class UIGuestSessionWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigOpenSession(QString strUserName, QString strPassword);
+ void sigCloseSession();
+
+public:
+
+ UIGuestSessionWidget(QWidget *pParent = 0);
+ /** Disables certain widget after a guest session has been opened. */
+ void switchSessionOpenMode();
+ /** Makes sure certain widgets are enabled so that a guest session can be opened. */
+ void switchSessionCloseMode();
+ void markForError(bool fMarkForError);
+ void setStatusLabelIconAndToolTip(const QIcon &icon, const QString &strToolTip);
+ void setLoginWidgetsEnabled(bool fEnabled);
+
+protected:
+
+ void retranslateUi() RT_OVERRIDE;
+ void keyPressEvent(QKeyEvent * pEvent) RT_OVERRIDE;
+ void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ void sltButtonClick();
+ void sltHandleTextChanged(const QString &strText);
+
+private:
+
+ enum ButtonMode
+ {
+ ButtonMode_Open,
+ ButtonMode_Close
+ };
+
+ void prepareWidgets();
+ void updateButton();
+
+ ButtonMode m_enmButtonMode;
+ QILineEdit *m_pUserNameEdit;
+ UIPasswordLineEdit *m_pPasswordEdit;
+ QPushButton *m_pButton;
+ QHBoxLayout *m_pMainLayout;
+ QColor m_defaultBaseColor;
+ QColor m_errorBaseColor;
+ bool m_fMarkedForError;
+ QLabel *m_pStatusIconLabel;
+};
+
+
+/*********************************************************************************************************************************
+* UIGuestSessionWidget implementation. *
+*********************************************************************************************************************************/
+
+UIGuestSessionWidget::UIGuestSessionWidget(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmButtonMode(ButtonMode_Open)
+ , m_pUserNameEdit(0)
+ , m_pPasswordEdit(0)
+ , m_pButton(0)
+ , m_pMainLayout(0)
+ , m_fMarkedForError(0)
+ , m_pStatusIconLabel(0)
+{
+ prepareWidgets();
+}
+
+void UIGuestSessionWidget::prepareWidgets()
+{
+ m_pMainLayout = new QHBoxLayout(this);
+ if (!m_pMainLayout)
+ return;
+
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pUserNameEdit = new QILineEdit;
+ if (m_pUserNameEdit)
+ {
+ m_pMainLayout->addWidget(m_pUserNameEdit, 2);
+ m_pUserNameEdit->setPlaceholderText(QApplication::translate("UIFileManager", "User Name"));
+ m_defaultBaseColor = m_pUserNameEdit->palette().color(QPalette::Base);
+ m_errorBaseColor = QColor(m_defaultBaseColor.red(),
+ 0.5 * m_defaultBaseColor.green(),
+ 0.5 * m_defaultBaseColor.blue());
+ connect(m_pUserNameEdit, &QILineEdit::textChanged,
+ this, &UIGuestSessionWidget::sltHandleTextChanged);
+ }
+
+ m_pPasswordEdit = new UIPasswordLineEdit;
+ if (m_pPasswordEdit)
+ {
+ m_pMainLayout->addWidget(m_pPasswordEdit, 2);
+ m_pPasswordEdit->setPlaceholderText(QApplication::translate("UIFileManager", "Password"));
+ m_pPasswordEdit->setEchoMode(QLineEdit::Password);
+ connect(m_pPasswordEdit, &UIPasswordLineEdit::textChanged,
+ this, &UIGuestSessionWidget::sltHandleTextChanged);
+ }
+
+ m_pButton = new QPushButton;
+ if (m_pButton)
+ {
+ m_pMainLayout->addWidget(m_pButton);
+ connect(m_pButton, &QPushButton::clicked, this, &UIGuestSessionWidget::sltButtonClick);
+ }
+ m_pStatusIconLabel = new QLabel(this);
+ if (m_pStatusIconLabel)
+ {
+ m_pMainLayout->addWidget(m_pStatusIconLabel);
+ m_pStatusIconLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+ }
+
+ m_pMainLayout->insertStretch(-1, 1);
+ switchSessionOpenMode();
+ retranslateUi();
+}
+
+void UIGuestSessionWidget::sltButtonClick()
+{
+ if (m_enmButtonMode == ButtonMode_Open && m_pUserNameEdit && m_pPasswordEdit)
+ emit sigOpenSession(m_pUserNameEdit->text(), m_pPasswordEdit->text());
+ else if (m_enmButtonMode == ButtonMode_Close)
+ emit sigCloseSession();
+}
+
+void UIGuestSessionWidget::sltHandleTextChanged(const QString &strText)
+{
+ Q_UNUSED(strText);
+ markForError(false);
+}
+
+void UIGuestSessionWidget::retranslateUi()
+{
+ if (m_pUserNameEdit)
+ {
+ m_pUserNameEdit->setToolTip(QApplication::translate("UIFileManager", "User name to authenticate session creation"));
+ m_pUserNameEdit->setPlaceholderText(QApplication::translate("UIFileManager", "User Name"));
+
+ }
+ if (m_pPasswordEdit)
+ {
+ m_pPasswordEdit->setToolTip(QApplication::translate("UIFileManager", "Password to authenticate session creation"));
+ m_pPasswordEdit->setPlaceholderText(QApplication::translate("UIFileManager", "Password"));
+ }
+
+ if (m_pButton)
+ {
+ if (m_enmButtonMode == ButtonMode_Open)
+ {
+ m_pButton->setText(QApplication::translate("UIFileManager", "Open Session"));
+ m_pButton->setToolTip(QApplication::translate("UIFileManager", "Open Session"));
+ }
+ else
+ {
+ m_pButton->setText(QApplication::translate("UIFileManager", "Close Session"));
+ m_pButton->setToolTip(QApplication::translate("UIFileManager", "Close Session"));
+ }
+ }
+}
+
+void UIGuestSessionWidget::keyPressEvent(QKeyEvent * pEvent)
+{
+ /* Emit sigOpenSession upon enter press: */
+ if (pEvent->key() == Qt::Key_Enter || pEvent->key() == Qt::Key_Return)
+ {
+ if ((m_pUserNameEdit && m_pUserNameEdit->hasFocus()) ||
+ (m_pPasswordEdit && m_pPasswordEdit->hasFocus()))
+ sigOpenSession(m_pUserNameEdit->text(), m_pPasswordEdit->text());
+ }
+ QWidget::keyPressEvent(pEvent);
+}
+
+void UIGuestSessionWidget::showEvent(QShowEvent *pEvent)
+{
+ QIWithRetranslateUI<QWidget>::showEvent(pEvent);
+ if (m_pUserNameEdit)
+ m_pUserNameEdit->setFocus();
+}
+
+void UIGuestSessionWidget::switchSessionOpenMode()
+{
+ if (m_pUserNameEdit)
+ m_pUserNameEdit->setEnabled(true);
+ if (m_pPasswordEdit)
+ m_pPasswordEdit->setEnabled(true);
+ m_enmButtonMode = ButtonMode_Open;
+ retranslateUi();
+}
+
+void UIGuestSessionWidget::switchSessionCloseMode()
+{
+ if (m_pUserNameEdit)
+ m_pUserNameEdit->setEnabled(false);
+ if (m_pPasswordEdit)
+ m_pPasswordEdit->setEnabled(false);
+ m_enmButtonMode = ButtonMode_Close;
+ retranslateUi();
+}
+
+void UIGuestSessionWidget::markForError(bool fMarkForError)
+{
+ if (m_fMarkedForError == fMarkForError)
+ return;
+ m_fMarkedForError = fMarkForError;
+
+ if (m_pUserNameEdit)
+ {
+ QPalette mPalette = m_pUserNameEdit->palette();
+ if (m_fMarkedForError)
+ mPalette.setColor(QPalette::Base, m_errorBaseColor);
+ else
+ mPalette.setColor(QPalette::Base, m_defaultBaseColor);
+ m_pUserNameEdit->setPalette(mPalette);
+ }
+ if (m_pPasswordEdit)
+ {
+ QPalette mPalette = m_pPasswordEdit->palette();
+ if (m_fMarkedForError)
+ mPalette.setColor(QPalette::Base, m_errorBaseColor);
+ else
+ mPalette.setColor(QPalette::Base, m_defaultBaseColor);
+ m_pPasswordEdit->setPalette(mPalette);
+ }
+}
+
+void UIGuestSessionWidget::setStatusLabelIconAndToolTip(const QIcon &icon, const QString &strToolTip)
+{
+ if (!m_pStatusIconLabel)
+ return;
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize);
+ m_pStatusIconLabel->setPixmap(icon.pixmap(QSize(iIconMetric, iIconMetric)));
+ m_pStatusIconLabel->setToolTip(strToolTip);
+}
+
+void UIGuestSessionWidget::setLoginWidgetsEnabled(bool fEnabled)
+{
+ if (m_pUserNameEdit)
+ m_pUserNameEdit->setEnabled(fEnabled);
+ if (m_pPasswordEdit)
+ m_pPasswordEdit->setEnabled(fEnabled);
+ if (m_pButton)
+ m_pButton->setEnabled(fEnabled);
+}
+
+
+/*********************************************************************************************************************************
+* UIGuestDirectoryDiskUsageComputer definition. *
+*********************************************************************************************************************************/
+
+/** Open directories recursively and sum the disk usage. Don't block the GUI thread while doing this */
+class UIGuestDirectoryDiskUsageComputer : public UIDirectoryDiskUsageComputer
+{
+ Q_OBJECT;
+
+public:
+
+ UIGuestDirectoryDiskUsageComputer(QObject *parent, QStringList strStartPath, const CGuestSession &session);
+
+protected:
+
+ virtual void run() RT_OVERRIDE;
+ virtual void directoryStatisticsRecursive(const QString &path, UIDirectoryStatistics &statistics) RT_OVERRIDE;
+
+private:
+
+ CGuestSession m_comGuestSession;
+};
+
+
+/*********************************************************************************************************************************
+* UIGuestDirectoryDiskUsageComputer implementation. *
+*********************************************************************************************************************************/
+
+UIGuestDirectoryDiskUsageComputer::UIGuestDirectoryDiskUsageComputer(QObject *parent, QStringList pathList, const CGuestSession &session)
+ :UIDirectoryDiskUsageComputer(parent, pathList)
+ , m_comGuestSession(session)
+{
+}
+
+void UIGuestDirectoryDiskUsageComputer::run()
+{
+ /* Initialize COM: */
+ COMBase::InitializeCOM(false);
+ UIDirectoryDiskUsageComputer::run();
+ /* Cleanup COM: */
+ COMBase::CleanupCOM();
+}
+
+void UIGuestDirectoryDiskUsageComputer::directoryStatisticsRecursive(const QString &path, UIDirectoryStatistics &statistics)
+{
+ if (m_comGuestSession.isNull())
+ return;
+ /* Prevent modification of the continue flag while reading: */
+ m_mutex.lock();
+ /* Check if m_fOkToContinue is set to false. if so just end recursion: */
+ if (!isOkToContinue())
+ {
+ m_mutex.unlock();
+ return;
+ }
+ m_mutex.unlock();
+
+ CGuestFsObjInfo fileInfo = m_comGuestSession.FsObjQueryInfo(path, true);
+
+ if (!m_comGuestSession.isOk())
+ return;
+ /* if the object is a file or a symlink then read the size and return: */
+ if (fileInfo.GetType() == KFsObjType_File)
+ {
+ statistics.m_totalSize += fileInfo.GetObjectSize();
+ ++statistics.m_uFileCount;
+ sigResultUpdated(statistics);
+ return;
+ }
+ else if (fileInfo.GetType() == KFsObjType_Symlink)
+ {
+ statistics.m_totalSize += fileInfo.GetObjectSize();
+ ++statistics.m_uSymlinkCount;
+ sigResultUpdated(statistics);
+ return;
+ }
+
+ if (fileInfo.GetType() != KFsObjType_Directory)
+ return;
+ /* Open the directory to start reading its content: */
+ QVector<KDirectoryOpenFlag> flag(1, KDirectoryOpenFlag_None);
+ CGuestDirectory directory = m_comGuestSession.DirectoryOpen(path, /*aFilter*/ "", flag);
+ if (!m_comGuestSession.isOk())
+ return;
+
+ if (directory.isOk())
+ {
+ CFsObjInfo fsInfo = directory.Read();
+ while (fsInfo.isOk())
+ {
+ if (fsInfo.GetType() == KFsObjType_File)
+ statistics.m_uFileCount++;
+ else if (fsInfo.GetType() == KFsObjType_Symlink)
+ statistics.m_uSymlinkCount++;
+ else if(fsInfo.GetType() == KFsObjType_Directory)
+ {
+ QString dirPath = UIPathOperations::mergePaths(path, fsInfo.GetName());
+ directoryStatisticsRecursive(dirPath, statistics);
+ }
+ }
+ }
+ sigResultUpdated(statistics);
+}
+
+UIFileManagerGuestTable::UIFileManagerGuestTable(UIActionPool *pActionPool, const CMachine &comMachine, QWidget *pParent /*= 0*/)
+ :UIFileManagerTable(pActionPool, pParent)
+ , m_comMachine(comMachine)
+ , m_pGuestSessionWidget(0)
+ , m_fIsCurrent(false)
+ , pszMinimumGuestAdditionVersion("6.1")
+{
+ if (!m_comMachine.isNull())
+ m_strTableName = m_comMachine.GetName();
+ prepareToolbar();
+ prepareGuestSessionPanel();
+ prepareActionConnections();
+
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UIFileManagerGuestTable::sltMachineStateChange);
+ connect(&uiCommon(), &UICommon::sigAskToCommitData,
+ this, &UIFileManagerGuestTable::sltCommitDataSignalReceived);
+
+ if (m_pActionPool && m_pActionPool->action(UIActionIndex_M_FileManager_T_GuestSession))
+ m_pActionPool->action(UIActionIndex_M_FileManager_T_GuestSession)->setChecked(true);
+
+ if (!m_comMachine.isNull() && m_comMachine.GetState() == KMachineState_Running)
+ openMachineSession();
+ setStateAndEnableWidgets();
+
+ retranslateUi();
+}
+
+UIFileManagerGuestTable::~UIFileManagerGuestTable()
+{
+ cleanAll();
+}
+
+void UIFileManagerGuestTable::initFileTable()
+{
+ if (!m_comGuestSession.isOk() || m_comGuestSession.GetStatus() != KGuestSessionStatus_Started)
+ return;
+ /* To determine the path separator we need to have a valid guest session: */
+ determinePathSeparator();
+ initializeFileTree();
+}
+
+void UIFileManagerGuestTable::retranslateUi()
+{
+ if (m_pLocationLabel)
+ m_pLocationLabel->setText(UIFileManager::tr("Guest File System:"));
+
+ if (m_pGuestSessionWidget)
+ {
+ QIcon icon;
+ QString strWarningText;
+ switch (m_enmState)
+ {
+ case State_InvalidMachineReference:
+ strWarningText = UIFileManager::tr("Machine reference is invalid.");
+ icon = UIIconPool::iconSet(":/status_error_16px.png");
+ break;
+ case State_MachineNotRunning:
+ strWarningText = UIFileManager::tr("File manager cannot work since the selected guest is not currently running.");
+ icon = UIIconPool::iconSet(":/status_error_16px.png");
+ break;
+ case State_MachinePaused:
+ strWarningText = UIFileManager::tr("File manager cannot work since the guest is paused.");
+ icon = UIIconPool::iconSet(":/session_info_16px.png");
+ break;
+ case State_NoGuestAdditions:
+ strWarningText = UIFileManager::tr("File manager cannot work since no guest additions were detected.");
+ icon = UIIconPool::iconSet(":/status_error_16px.png");
+ break;
+ case State_GuestAdditionsTooOld:
+ strWarningText = UIFileManager::tr("File manager cannot work. The guest additions need to be updated.");
+ icon = UIIconPool::iconSet(":/status_error_16px.png");
+ break;
+ case State_SessionPossible:
+ strWarningText = UIFileManager::tr("Enter a valid user name and password to initiate the file manager.");
+ icon = UIIconPool::iconSet(":/session_info_16px.png");
+ break;
+ case State_SessionRunning:
+ strWarningText = UIFileManager::tr("Guest control session is running.");
+ icon = UIIconPool::iconSet(":/status_check_16px.png");
+ break;
+ case State_SessionError:
+ strWarningText = UIFileManager::tr("Some error has occurred. Please check the log panel.");
+ icon = UIIconPool::iconSet(":/status_error_16px.png");
+ break;
+ default:
+ break;
+ }
+ m_pGuestSessionWidget->setStatusLabelIconAndToolTip(icon, strWarningText);
+ }
+
+ UIFileManagerTable::retranslateUi();
+}
+
+void UIFileManagerGuestTable::readDirectory(const QString& strPath,
+ UICustomFileSystemItem *parent, bool isStartDir /*= false*/)
+{
+ if (!parent)
+ return;
+
+ CGuestDirectory directory;
+ QVector<KDirectoryOpenFlag> flag;
+ flag.push_back(KDirectoryOpenFlag_None);
+
+ directory = m_comGuestSession.DirectoryOpen(UIPathOperations::sanitize(strPath), /*aFilter*/ "", flag);
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ return;
+ }
+
+ parent->setIsOpened(true);
+ if (directory.isOk())
+ {
+ CFsObjInfo fsInfo = directory.Read();
+ QMap<QString, UICustomFileSystemItem*> fileObjects;
+
+ while (fsInfo.isOk())
+ {
+ if (fsInfo.GetName() != "." && fsInfo.GetName() != "..")
+ {
+ QVector<QVariant> data;
+ QDateTime changeTime = QDateTime::fromMSecsSinceEpoch(fsInfo.GetChangeTime()/RT_NS_1MS);
+ KFsObjType fsObjectType = fileType(fsInfo);
+ UICustomFileSystemItem *item = new UICustomFileSystemItem(fsInfo.GetName(), parent, fsObjectType);
+ if (!item)
+ continue;
+ item->setData(static_cast<qulonglong>(fsInfo.GetObjectSize()), UICustomFileSystemModelColumn_Size);
+ item->setData(changeTime, UICustomFileSystemModelColumn_ChangeTime);
+ item->setData(fsInfo.GetUserName(), UICustomFileSystemModelColumn_Owner);
+ item->setData(permissionString(fsInfo), UICustomFileSystemModelColumn_Permissions);
+ item->setPath(UIPathOperations::removeTrailingDelimiters(UIPathOperations::mergePaths(strPath, fsInfo.GetName())));
+ item->setIsOpened(false);
+ item->setIsHidden(isFileObjectHidden(fsInfo));
+ fileObjects.insert(fsInfo.GetName(), item);
+ /* @todo. We will need to wait a fully implemented SymlinkRead function
+ * to be able to handle sym links properly: */
+ // QString path = UIPathOperations::mergePaths(strPath, fsInfo.GetName());
+ // QVector<KSymlinkReadFlag> aFlags;
+ // printf("%s %s %s\n", qPrintable(fsInfo.GetName()), qPrintable(path),
+ // qPrintable(m_comGuestSession.SymlinkRead(path, aFlags)));
+ }
+ fsInfo = directory.Read();
+ }
+ checkDotDot(fileObjects, parent, isStartDir);
+ }
+ directory.Close();
+}
+
+void UIFileManagerGuestTable::deleteByItem(UICustomFileSystemItem *item)
+{
+ if (!item)
+ return;
+ if (item->isUpDirectory())
+ return;
+
+ if (item->isDirectory())
+ {
+ QVector<KDirectoryRemoveRecFlag> aFlags(1, KDirectoryRemoveRecFlag_ContentAndDir);
+ m_comGuestSession.DirectoryRemoveRecursive(item->path(), aFlags);
+ }
+ else
+ m_comGuestSession.FsObjRemove(item->path());
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(QString(item->path()).append(" could not be deleted"), m_strTableName, FileManagerLogType_Error);
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ }
+}
+
+void UIFileManagerGuestTable::deleteByPath(const QStringList &pathList)
+{
+ foreach (const QString &strPath, pathList)
+ {
+ CGuestFsObjInfo fileInfo = m_comGuestSession.FsObjQueryInfo(strPath, true);
+ KFsObjType eType = fileType(fileInfo);
+ if (eType == KFsObjType_File || eType == KFsObjType_Symlink)
+ {
+ m_comGuestSession.FsObjRemove(strPath);
+ }
+ else if (eType == KFsObjType_Directory)
+ {
+ QVector<KDirectoryRemoveRecFlag> aFlags(1, KDirectoryRemoveRecFlag_ContentAndDir);
+ m_comGuestSession.DirectoryRemoveRecursive(strPath, aFlags);
+ }
+ }
+}
+
+void UIFileManagerGuestTable::goToHomeDirectory()
+{
+ if (m_comGuestSession.isNull())
+ return;
+ if (!rootItem() || rootItem()->childCount() <= 0)
+ return;
+ UICustomFileSystemItem *startDirItem = rootItem()->child(0);
+ if (!startDirItem)
+ return;
+
+ QString userHome = UIPathOperations::sanitize(m_comGuestSession.GetUserHome());
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ return;
+ }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList pathList = userHome.split(UIPathOperations::delimiter, Qt::SkipEmptyParts);
+#else
+ QStringList pathList = userHome.split(UIPathOperations::delimiter, QString::SkipEmptyParts);
+#endif
+ goIntoDirectory(UIPathOperations::pathTrail(userHome));
+}
+
+bool UIFileManagerGuestTable::renameItem(UICustomFileSystemItem *item, QString newBaseName)
+{
+
+ if (!item || item->isUpDirectory() || newBaseName.isEmpty())
+ return false;
+ QString newPath = UIPathOperations::removeTrailingDelimiters(UIPathOperations::constructNewItemPath(item->path(), newBaseName));
+ QVector<KFsObjRenameFlag> aFlags(1, KFsObjRenameFlag_Replace);
+
+ m_comGuestSession.FsObjRename(item->path(), newPath, aFlags);
+
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+ item->setPath(newPath);
+ return true;
+}
+
+bool UIFileManagerGuestTable::createDirectory(const QString &path, const QString &directoryName)
+{
+ QString newDirectoryPath = UIPathOperations::mergePaths(path, directoryName);
+ QVector<KDirectoryCreateFlag> flags(1, KDirectoryCreateFlag_None);
+
+ m_comGuestSession.DirectoryCreate(newDirectoryPath, 0/*aMode*/, flags);
+
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(newDirectoryPath.append(" could not be created"), m_strTableName, FileManagerLogType_Error);
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+ emit sigLogOutput(newDirectoryPath.append(" has been created"), m_strTableName, FileManagerLogType_Info);
+ return true;
+}
+
+void UIFileManagerGuestTable::copyHostToGuest(const QStringList &hostSourcePathList,
+ const QString &strDestination /* = QString() */)
+{
+ if (!checkGuestSession())
+ return;
+ QVector<QString> sourcePaths = hostSourcePathList.toVector();
+ QVector<QString> aFilters;
+ QVector<QString> aFlags;
+ QString strDestinationPath = strDestination;
+
+ /* Remove empty source paths. Typically happens when up directory is selected: */
+ sourcePaths.removeAll(QString());
+
+ if (strDestinationPath.isEmpty())
+ strDestinationPath = currentDirectoryPath();
+
+ if (strDestinationPath.isEmpty())
+ {
+ emit sigLogOutput("No destination for copy operation", m_strTableName, FileManagerLogType_Error);
+ return;
+ }
+ if (sourcePaths.empty())
+ {
+ emit sigLogOutput("No source for copy operation", m_strTableName, FileManagerLogType_Error);
+ return;
+ }
+ QString strDirectoryFlags("CopyIntoExisting,Recursive,FollowLinks");
+ QString strFileFlags("FollowLinks");
+ foreach (const QString &strSource, sourcePaths)
+ {
+ KFsObjType enmFileType = UIFileManagerHostTable::fileType(strSource);
+ if (enmFileType == KFsObjType_Unknown)
+ emit sigLogOutput(QString("Querying information for host item %1 failed.").arg(strSource), m_strTableName, FileManagerLogType_Error);
+ /* If the source is an directory, make sure to add the appropriate flag to make copying work
+ * into existing directories on the guest. This otherwise would fail (default): */
+ else if (enmFileType == KFsObjType_Directory)
+ {
+ /* Make sure that if the source is a directory, that we append a trailing delimiter to it,
+ * so that it gets copied *into* the destination directory as a whole, and not just it's contents. */
+ strDestinationPath = UIPathOperations::addTrailingDelimiters(strDestinationPath);
+ aFlags << strDirectoryFlags;
+ }
+ else
+ {
+ /* Ditto goes for source files, as the destination always is a directory path. */
+ strDestinationPath = UIPathOperations::addTrailingDelimiters(strDestinationPath);
+ aFlags << strFileFlags;
+ }
+ }
+
+ CProgress progress = m_comGuestSession.CopyToGuest(sourcePaths, aFilters, aFlags, strDestinationPath);
+ if (!checkGuestSession())
+ return;
+ emit sigNewFileOperation(progress, m_strTableName);
+}
+
+QUuid UIFileManagerGuestTable::machineId()
+{
+ if (m_comMachine.isNull())
+ return QUuid();
+ return m_comMachine.GetId();
+}
+
+bool UIFileManagerGuestTable::isGuestSessionRunning() const
+{
+ return m_enmState == State_SessionRunning;
+}
+
+void UIFileManagerGuestTable::setIsCurrent(bool fIsCurrent)
+{
+ if (m_fIsCurrent == fIsCurrent)
+ return;
+ m_fIsCurrent = fIsCurrent;
+ prepareActionConnections();
+}
+
+void UIFileManagerGuestTable::copyGuestToHost(const QString& hostDestinationPath)
+{
+ if (!checkGuestSession())
+ return;
+ QVector<QString> sourcePaths = selectedItemPathList().toVector();
+ QVector<QString> aFilters;
+ QVector<QString> aFlags;
+
+ /* Remove empty source paths. Typically happens when up directory is selected: */
+ sourcePaths.removeAll(QString());
+
+ if (hostDestinationPath.isEmpty())
+ {
+ emit sigLogOutput("No destination for copy operation", m_strTableName, FileManagerLogType_Error);
+ return;
+ }
+ if (sourcePaths.empty())
+ {
+ emit sigLogOutput("No source for copy operation", m_strTableName, FileManagerLogType_Error);
+ return;
+ }
+
+ QString strDestinationPath = hostDestinationPath;
+ QString strDirectoryFlags("CopyIntoExisting,Recursive,FollowLinks");
+ QString strFileFlags;
+ foreach (const QString &strSource, sourcePaths)
+ {
+ /** @todo Cache this info and use the item directly, which has this info already? */
+
+ /* If the source is an directory, make sure to add the appropriate flag to make copying work
+ * into existing directories on the guest. This otherwise would fail (default). */
+ CGuestFsObjInfo fileInfo = m_comGuestSession.FsObjQueryInfo(strSource, true);
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ return;
+ }
+
+ if (fileType(fileInfo) == KFsObjType_Directory)
+ {
+ /* Make sure that if the source is a directory, that we append a trailing delimiter to the destination,
+ * so that the source directory gets copied *into* the destination directory as a whole, and not
+ * just it's contents. */
+ strDestinationPath = UIPathOperations::addTrailingDelimiters(strDestinationPath);
+ aFlags << strDirectoryFlags;
+ }
+ else
+ {
+ /* Ditto goes for source files, as the destination always is a directory path. */
+ strDestinationPath = UIPathOperations::addTrailingDelimiters(strDestinationPath);
+ aFlags << strFileFlags;
+ }
+ }
+
+ CProgress progress = m_comGuestSession.CopyFromGuest(sourcePaths, aFilters, aFlags, strDestinationPath);
+ if (!checkGuestSession())
+ return;
+ emit sigNewFileOperation(progress, m_strTableName);
+}
+
+KFsObjType UIFileManagerGuestTable::fileType(const CFsObjInfo &fsInfo)
+{
+ if (fsInfo.isNull() || !fsInfo.isOk())
+ return KFsObjType_Unknown;
+ if (fsInfo.GetType() == KFsObjType_Directory)
+ return KFsObjType_Directory;
+ else if (fsInfo.GetType() == KFsObjType_File)
+ return KFsObjType_File;
+ else if (fsInfo.GetType() == KFsObjType_Symlink)
+ return KFsObjType_Symlink;
+
+ return KFsObjType_Unknown;
+}
+
+KFsObjType UIFileManagerGuestTable::fileType(const CGuestFsObjInfo &fsInfo)
+{
+ if (fsInfo.isNull() || !fsInfo.isOk())
+ return KFsObjType_Unknown;
+ if (fsInfo.GetType() == KFsObjType_Directory)
+ return KFsObjType_Directory;
+ else if (fsInfo.GetType() == KFsObjType_File)
+ return KFsObjType_File;
+ else if (fsInfo.GetType() == KFsObjType_Symlink)
+ return KFsObjType_Symlink;
+
+ return KFsObjType_Unknown;
+}
+
+
+QString UIFileManagerGuestTable::fsObjectPropertyString()
+{
+ QStringList selectedObjects = selectedItemPathList();
+ if (selectedObjects.isEmpty())
+ return QString();
+ if (selectedObjects.size() == 1)
+ {
+ if (selectedObjects.at(0).isNull())
+ return QString();
+
+ CGuestFsObjInfo fileInfo = m_comGuestSession.FsObjQueryInfo(selectedObjects.at(0), false /*aFollowSymlinks*/);
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ return QString();
+ }
+
+ QStringList propertyStringList;
+
+ /* Name: */
+ propertyStringList << UIFileManager::tr("<b>Name:</b> %1<br/>").arg(UIPathOperations::getObjectName(fileInfo.GetName()));
+
+ /* Size: */
+ LONG64 size = fileInfo.GetObjectSize();
+ propertyStringList << UIFileManager::tr("<b>Size:</b> %1 bytes").arg(QString::number(size));
+ if (size >= UIFileManagerTable::m_iKiloByte)
+ propertyStringList << QString(" (%1)<br/>").arg(humanReadableSize(size));
+ else
+ propertyStringList << QString("<br/>");
+
+ /* Allocated size: */
+ size = fileInfo.GetAllocatedSize();
+ propertyStringList << UIFileManager::tr("<b>Allocated:</b> %1 bytes").arg(QString::number(size));
+ if (size >= UIFileManagerTable::m_iKiloByte)
+ propertyStringList << QString(" (%1)<br/>").arg(humanReadableSize(size));
+ else
+ propertyStringList << QString("<br/>");
+
+ /* Type: */
+ QString str;
+ KFsObjType const enmType = fileInfo.GetType();
+ switch (enmType)
+ {
+ case KFsObjType_Directory: str = UIFileManager::tr("directory"); break;
+ case KFsObjType_File: str = UIFileManager::tr("file"); break;
+ case KFsObjType_Symlink: str = UIFileManager::tr("symbolic link"); break;
+ case KFsObjType_DevChar: str = UIFileManager::tr("character device"); break;
+ case KFsObjType_DevBlock: str = UIFileManager::tr("block device"); break;
+ case KFsObjType_Fifo: str = UIFileManager::tr("fifo"); break;
+ case KFsObjType_Socket: str = UIFileManager::tr("socket"); break;
+ case KFsObjType_WhiteOut: str = UIFileManager::tr("whiteout"); break;
+ case KFsObjType_Unknown: str = UIFileManager::tr("unknown"); break;
+ default: str = UIFileManager::tr("illegal-value"); break;
+ }
+ propertyStringList << UIFileManager::tr("<b>Type:</b> %1<br/>").arg(str);
+
+ /* INode number, device, link count: */
+ propertyStringList << UIFileManager::tr("<b>INode:</b> %1<br/>").arg(fileInfo.GetNodeId());
+ propertyStringList << UIFileManager::tr("<b>Device:</b> %1<br/>").arg(fileInfo.GetNodeIdDevice()); /** @todo hex */
+ propertyStringList << UIFileManager::tr("<b>Hardlinks:</b> %1<br/>").arg(fileInfo.GetHardLinks());
+
+ /* Attributes: */
+ str = fileInfo.GetFileAttributes();
+ if (!str.isEmpty())
+ {
+ int offSpace = str.indexOf(' ');
+ if (offSpace < 0)
+ offSpace = str.length();
+ propertyStringList << UIFileManager::tr("<b>Mode:</b> %1<br/>").arg(str.left(offSpace));
+ propertyStringList << UIFileManager::tr("<b>Attributes:</b> %1<br/>").arg(str.mid(offSpace + 1).trimmed());
+ }
+
+ /* Character/block device ID: */
+ ULONG uDeviceNo = fileInfo.GetDeviceNumber();
+ if (uDeviceNo != 0 || enmType == KFsObjType_DevChar || enmType == KFsObjType_DevBlock)
+ propertyStringList << UIFileManager::tr("<b>Device ID:</b> %1<br/>").arg(uDeviceNo); /** @todo hex */
+
+ /* Owner: */
+ propertyStringList << UIFileManager::tr("<b>Owner:</b> %1 (%2)<br/>").
+ arg(fileInfo.GetUserName()).arg(fileInfo.GetUID());
+ propertyStringList << UIFileManager::tr("<b>Group:</b> %1 (%2)<br/>").
+ arg(fileInfo.GetGroupName()).arg(fileInfo.GetGID());
+
+ /* Timestamps: */
+ propertyStringList << UIFileManager::tr("<b>Birth:</b> %1<br/>").
+ arg(QDateTime::fromMSecsSinceEpoch(fileInfo.GetBirthTime() / RT_NS_1MS).toString());
+ propertyStringList << UIFileManager::tr("<b>Change:</b> %1<br/>").
+ arg(QDateTime::fromMSecsSinceEpoch(fileInfo.GetChangeTime() / RT_NS_1MS).toString());
+ propertyStringList << UIFileManager::tr("<b>Modified:</b> %1<br/>").
+ arg(QDateTime::fromMSecsSinceEpoch(fileInfo.GetModificationTime() / RT_NS_1MS).toString());
+ propertyStringList << UIFileManager::tr("<b>Access:</b> %1<br/>").
+ arg(QDateTime::fromMSecsSinceEpoch(fileInfo.GetAccessTime() / RT_NS_1MS).toString());
+
+ /* Join the list elements into a single string seperated by empty string: */
+ return propertyStringList.join(QString());
+ }
+
+ int fileCount = 0;
+ int directoryCount = 0;
+ ULONG64 totalSize = 0;
+
+ for(int i = 0; i < selectedObjects.size(); ++i)
+ {
+ CGuestFsObjInfo fileInfo = m_comGuestSession.FsObjQueryInfo(selectedObjects.at(0), true);
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ continue;
+ }
+
+ KFsObjType type = fileType(fileInfo);
+
+ if (type == KFsObjType_File)
+ ++fileCount;
+ if (type == KFsObjType_Directory)
+ ++directoryCount;
+ totalSize += fileInfo.GetObjectSize();
+ }
+ QStringList propertyStringList;
+ propertyStringList << UIFileManager::tr("<b>Selected:</b> %1 files and %2 directories<br/>").
+ arg(QString::number(fileCount)).arg(QString::number(directoryCount));
+ propertyStringList << UIFileManager::tr("<b>Size (non-recursive):</b> %1 bytes").arg(QString::number(totalSize));
+ if (totalSize >= m_iKiloByte)
+ propertyStringList << QString(" (%1)").arg(humanReadableSize(totalSize));
+
+ return propertyStringList.join(QString());;
+}
+
+void UIFileManagerGuestTable::showProperties()
+{
+ if (m_comGuestSession.isNull())
+ return;
+ QString fsPropertyString = fsObjectPropertyString();
+ if (fsPropertyString.isEmpty())
+ return;
+
+ m_pPropertiesDialog = new UIPropertiesDialog(this);
+ if (!m_pPropertiesDialog)
+ return;
+
+ QStringList selectedObjects = selectedItemPathList();
+ if (selectedObjects.size() == 0)
+ return;
+
+ m_pPropertiesDialog->setWindowTitle(UIFileManager::tr("Properties"));
+ m_pPropertiesDialog->setPropertyText(fsPropertyString);
+ m_pPropertiesDialog->execute();
+
+ delete m_pPropertiesDialog;
+ m_pPropertiesDialog = 0;
+}
+
+void UIFileManagerGuestTable::determineDriveLetters()
+{
+ if (m_comGuestSession.isNull())
+ return;
+ KPathStyle pathStyle = m_comGuestSession.GetPathStyle();
+ if (pathStyle != KPathStyle_DOS)
+ return;
+
+ /** @todo Currently API lacks a way to query windows drive letters.
+ * so we enumarate them by using CGuestSession::DirectoryExists() */
+ m_driveLetterList.clear();
+ for (int i = 'A'; i <= 'Z'; ++i)
+ {
+ QString path((char)i);
+ path += ":/";
+ bool exists = m_comGuestSession.DirectoryExists(path, false /* aFollowSymlinks */);
+ if (exists)
+ m_driveLetterList.push_back(path);
+ }
+}
+
+void UIFileManagerGuestTable::determinePathSeparator()
+{
+ if (m_comGuestSession.isNull())
+ return;
+ KPathStyle pathStyle = m_comGuestSession.GetPathStyle();
+ if (pathStyle == KPathStyle_DOS)
+ setPathSeparator(UIPathOperations::dosDelimiter);
+}
+
+void UIFileManagerGuestTable::prepareToolbar()
+{
+ if (m_pToolBar && m_pActionPool)
+ {
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_GoUp));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_GoHome));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Refresh));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Delete));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Rename));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_CreateNewDirectory));
+
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Copy));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Cut));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Paste));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_SelectAll));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_InvertSelection));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_ShowProperties));
+ m_selectionDependentActions.insert(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Delete));
+ m_selectionDependentActions.insert(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Rename));
+ m_selectionDependentActions.insert(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Copy));
+ m_selectionDependentActions.insert(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Cut));
+ m_selectionDependentActions.insert(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_ShowProperties));
+
+ /* Hide these actions for now until we have a suitable guest-to-guest copy function: */
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Copy)->setVisible(false);
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Cut)->setVisible(false);
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Paste)->setVisible(false);
+
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_GuestSession));
+ }
+
+ setSelectionDependentActionsEnabled(false);
+ setPasteActionEnabled(false);
+}
+
+void UIFileManagerGuestTable::createFileViewContextMenu(const QWidget *pWidget, const QPoint &point)
+{
+ if (!pWidget)
+ return;
+
+ QMenu menu;
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_GoUp));
+
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_GoHome));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Refresh));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Delete));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Rename));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_CreateNewDirectory));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Copy));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Cut));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Paste));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_SelectAll));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_InvertSelection));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_ShowProperties));
+ menu.exec(pWidget->mapToGlobal(point));
+}
+
+void UIFileManagerGuestTable::setPasteActionEnabled(bool fEnabled)
+{
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Paste)->setEnabled(fEnabled);
+}
+
+void UIFileManagerGuestTable::pasteCutCopiedObjects()
+{
+}
+
+void UIFileManagerGuestTable::manageConnection(bool fConnect, QAction *pAction, void (UIFileManagerGuestTable::*fptr)(void))
+{
+ if (!pAction || !fptr)
+ return;
+ if (fConnect)
+ connect(pAction, &QAction::triggered, this, fptr);
+ else
+ disconnect(pAction, 0, this, 0);
+}
+
+void UIFileManagerGuestTable::prepareActionConnections()
+{
+ if (m_pActionPool->action(UIActionIndex_M_FileManager_T_GuestSession))
+ {
+ if (m_fIsCurrent)
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_T_GuestSession), &QAction::toggled,
+ this, &UIFileManagerGuestTable::sltGuestSessionPanelToggled);
+ else
+ disconnect(m_pActionPool->action(UIActionIndex_M_FileManager_T_GuestSession), 0, this, 0);
+ }
+
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_GoUp), &UIFileManagerTable::sltGoUp);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_GoHome), &UIFileManagerTable::sltGoHome);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Refresh), &UIFileManagerTable::sltRefresh);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Delete), &UIFileManagerTable::sltDelete);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Rename), &UIFileManagerTable::sltRename);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Copy), &UIFileManagerTable::sltCopy);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Cut), &UIFileManagerTable::sltCut);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_Paste), &UIFileManagerTable::sltPaste);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_SelectAll), &UIFileManagerTable::sltSelectAll);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_InvertSelection), &UIFileManagerTable::sltInvertSelection);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_ShowProperties), &UIFileManagerTable::sltShowProperties);
+ manageConnection(m_fIsCurrent, m_pActionPool->action(UIActionIndex_M_FileManager_S_Guest_CreateNewDirectory), &UIFileManagerTable::sltCreateNewDirectory);
+}
+
+void UIFileManagerGuestTable::prepareGuestSessionPanel()
+{
+ if (m_pMainLayout)
+ {
+ m_pGuestSessionWidget = new UIGuestSessionWidget;
+ if (m_pGuestSessionWidget)
+ {
+ m_pMainLayout->addWidget(m_pGuestSessionWidget, m_pMainLayout->rowCount(), 0, 1, m_pMainLayout->columnCount());
+ m_pGuestSessionWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
+ connect(m_pGuestSessionWidget, &UIGuestSessionWidget::sigOpenSession,
+ this, &UIFileManagerGuestTable::sltOpenGuestSession);
+ connect(m_pGuestSessionWidget, &UIGuestSessionWidget::sigCloseSession,
+ this, &UIFileManagerGuestTable::sltHandleCloseSessionRequest);
+ }
+ }
+}
+
+bool UIFileManagerGuestTable::checkGuestSession()
+{
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+ return true;
+}
+
+QString UIFileManagerGuestTable::permissionString(const CFsObjInfo &fsInfo)
+{
+ /* Attributes: */
+ QString strAttributes = fsInfo.GetFileAttributes();
+
+ if (strAttributes.isEmpty())
+ return strAttributes;
+
+ int offSpace = strAttributes.indexOf(' ');
+ if (offSpace < 0)
+ offSpace = strAttributes.length();
+ return strAttributes.left(offSpace);
+}
+
+bool UIFileManagerGuestTable::isFileObjectHidden(const CFsObjInfo &fsInfo)
+{
+ QString strAttributes = fsInfo.GetFileAttributes();
+
+ if (strAttributes.isEmpty())
+ return false;
+
+ int offSpace = strAttributes.indexOf(' ');
+ if (offSpace < 0)
+ offSpace = strAttributes.length();
+ QString strRight(strAttributes.mid(offSpace + 1).trimmed());
+
+ if (strRight.indexOf('H', Qt::CaseSensitive) == -1)
+ return false;
+ return true;
+}
+
+void UIFileManagerGuestTable::sltGuestSessionPanelToggled(bool fChecked)
+{
+ if (m_pGuestSessionWidget)
+ m_pGuestSessionWidget->setVisible(fChecked);
+}
+
+void UIFileManagerGuestTable::sltMachineStateChange(const QUuid &uMachineId, const KMachineState enmMachineState)
+{
+ if (uMachineId.isNull() || m_comMachine.isNull() || uMachineId != m_comMachine.GetId())
+ return;
+
+ if (enmMachineState == KMachineState_Running)
+ openMachineSession();
+ else if (enmMachineState != KMachineState_Paused)
+ cleanAll();
+ setStateAndEnableWidgets();
+}
+
+bool UIFileManagerGuestTable::closeMachineSession()
+{
+ if (!m_comGuest.isNull())
+ m_comGuest.detach();
+
+ if (!m_comConsole.isNull())
+ m_comConsole.detach();
+
+ if (!m_comSession.isNull())
+ {
+ m_comSession.UnlockMachine();
+ m_comSession.detach();
+ }
+ return true;
+}
+
+bool UIFileManagerGuestTable::openMachineSession()
+{
+ if (m_comMachine.isNull())
+ {
+ emit sigLogOutput("Invalid machine reference", m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+ m_comSession = uiCommon().openSession(m_comMachine.GetId(), KLockType_Shared);
+ if (m_comSession.isNull())
+ {
+ emit sigLogOutput("Could not open machine session", m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+
+ m_comConsole = m_comSession.GetConsole();
+ if (m_comConsole.isNull())
+ {
+ emit sigLogOutput("Machine console is invalid", m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+
+ m_comGuest = m_comConsole.GetGuest();
+ if (m_comGuest.isNull())
+ {
+ emit sigLogOutput("Guest reference is invalid", m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+
+ /* Prepare guest listener for guest session related events: */
+ {
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes << KVBoxEventType_OnGuestSessionRegistered;
+ prepareListener(m_pQtGuestListener, m_comGuestListener, m_comGuest.GetEventSource(), eventTypes);
+ connect(m_pQtGuestListener->getWrapped(), &UIMainEventListener::sigGuestSessionUnregistered,
+ this, &UIFileManagerGuestTable::sltGuestSessionUnregistered);
+ connect(m_pQtGuestListener->getWrapped(), &UIMainEventListener::sigGuestSessionRegistered,
+ this, &UIFileManagerGuestTable::sltGuestSessionRegistered);
+ }
+
+ /* Prepare console listener for guest additions state change events: */
+ {
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes << KVBoxEventType_OnAdditionsStateChanged;
+ prepareListener(m_pQtConsoleListener, m_comConsoleListener, m_comConsole.GetEventSource(), eventTypes);
+ connect(m_pQtConsoleListener->getWrapped(), &UIMainEventListener::sigAdditionsChange,
+ this, &UIFileManagerGuestTable::sltAdditionsStateChange);
+ }
+ emit sigLogOutput("Shared machine session opened", m_strTableName, FileManagerLogType_Info);
+ return true;
+}
+
+int UIFileManagerGuestTable::isGuestAdditionsAvailable(const char* pszMinimumVersion)
+{
+ if (m_comGuest.isNull() || !pszMinimumVersion)
+ return 0;
+
+ /* Guest control stuff is in userland: */
+ if (!m_comGuest.GetAdditionsStatus(KAdditionsRunLevelType_Userland))
+ return 0;
+
+ if (!m_comGuest.isOk())
+ return 0;
+
+ /* Check the related GA facility: */
+ LONG64 iLastUpdatedIgnored;
+ if (m_comGuest.GetFacilityStatus(KAdditionsFacilityType_VBoxService, iLastUpdatedIgnored) != KAdditionsFacilityStatus_Active)
+ return 0;
+
+ if (!m_comGuest.isOk())
+ return 0;
+
+ /* Check if GA is new enough to have the goodies: */
+ QString strGAVersion = m_comGuest.GetAdditionsVersion();
+ int iCode = RTStrVersionCompare(strGAVersion.toUtf8().constData(), pszMinimumVersion);
+ if (iCode >= 0)
+ return 1;
+ else
+ return -1;
+
+ return 0;
+}
+
+void UIFileManagerGuestTable::cleanupGuestListener()
+{
+ if (!m_pQtGuestListener.isNull())
+ {
+ m_pQtGuestListener->getWrapped()->disconnect();
+ if (!m_comGuest.isNull())
+ cleanupListener(m_pQtGuestListener, m_comGuestListener, m_comGuest.GetEventSource());
+ }
+}
+
+void UIFileManagerGuestTable::cleanupGuestSessionListener()
+{
+ if (!m_pQtSessionListener.isNull())
+ {
+ m_pQtSessionListener->getWrapped()->disconnect();
+ if (!m_comGuestSession.isNull())
+ cleanupListener(m_pQtSessionListener, m_comSessionListener, m_comGuestSession.GetEventSource());
+ }
+}
+
+void UIFileManagerGuestTable::cleanupConsoleListener()
+{
+ if (!m_pQtConsoleListener.isNull())
+ {
+ m_pQtConsoleListener->getWrapped()->disconnect();
+ if (!m_comConsole.isNull())
+ cleanupListener(m_pQtConsoleListener, m_comConsoleListener, m_comConsole.GetEventSource());
+ }
+}
+
+void UIFileManagerGuestTable::prepareListener(ComObjPtr<UIMainEventListenerImpl> &QtListener,
+ CEventListener &comEventListener,
+ CEventSource comEventSource, QVector<KVBoxEventType>& eventTypes)
+{
+ if (!comEventSource.isOk())
+ return;
+ /* Create event listener instance: */
+ QtListener.createObject();
+ QtListener->init(new UIMainEventListener, this);
+ comEventListener = CEventListener(QtListener);
+
+ /* Register event listener for CProgress event source: */
+ comEventSource.RegisterListener(comEventListener, eventTypes, FALSE /* active? */);
+
+ /* Register event sources in their listeners as well: */
+ QtListener->getWrapped()->registerSource(comEventSource, comEventListener);
+}
+
+void UIFileManagerGuestTable::cleanupListener(ComObjPtr<UIMainEventListenerImpl> &QtListener,
+ CEventListener &comEventListener,
+ CEventSource comEventSource)
+{
+ if (!comEventSource.isOk())
+ return;
+ /* Unregister everything: */
+ QtListener->getWrapped()->unregisterSources();
+ QtListener.setNull();
+ /* Make sure VBoxSVC is available: */
+ if (!uiCommon().isVBoxSVCAvailable())
+ return;
+
+ /* Unregister event listener for CProgress event source: */
+ comEventSource.UnregisterListener(comEventListener);
+}
+
+void UIFileManagerGuestTable::sltGuestSessionUnregistered(CGuestSession guestSession)
+{
+ if (guestSession.isNull())
+ return;
+ if (guestSession == m_comGuestSession && !m_comGuestSession.isNull())
+ {
+ m_comGuestSession.detach();
+ emit sigLogOutput("Guest session unregistered", m_strTableName, FileManagerLogType_Info);
+ }
+}
+
+void UIFileManagerGuestTable::sltGuestSessionRegistered(CGuestSession guestSession)
+{
+ if (guestSession == m_comGuestSession && !m_comGuestSession.isNull())
+ emit sigLogOutput("Guest session registered", m_strTableName, FileManagerLogType_Info);
+}
+
+void UIFileManagerGuestTable::sltGuestSessionStateChanged(const CGuestSessionStateChangedEvent &cEvent)
+{
+ if (cEvent.isOk())
+ {
+ CVirtualBoxErrorInfo cErrorInfo = cEvent.GetError();
+ if (cErrorInfo.GetResultDetail() < VINF_SUCCESS)
+ emit sigLogOutput(cErrorInfo.GetText(), m_strTableName, FileManagerLogType_Error);
+
+ if (m_pGuestSessionWidget)
+ m_pGuestSessionWidget->markForError(cErrorInfo.GetResultDetail() == VERR_AUTHENTICATION_FAILURE);
+ }
+
+ setStateAndEnableWidgets();
+
+ if (m_comGuestSession.isNull())
+ emit sigLogOutput("Guest session is invalid!", m_strTableName, FileManagerLogType_Error);
+ else
+ {
+ if (m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(QString("%1: %2").arg("Guest session status has changed").arg(gpConverter->toString(m_comGuestSession.GetStatus())),
+ m_strTableName, FileManagerLogType_Info);
+
+ switch (m_comGuestSession.GetStatus())
+ {
+ case KGuestSessionStatus_Started:
+ {
+ initFileTable();
+ break;
+ }
+ case KGuestSessionStatus_Terminating:
+ case KGuestSessionStatus_Terminated:
+ case KGuestSessionStatus_TimedOutKilled:
+ case KGuestSessionStatus_TimedOutAbnormally:
+ case KGuestSessionStatus_Down:
+ case KGuestSessionStatus_Error:
+ {
+ cleanupGuestSessionListener();
+ closeGuestSession();
+ break;
+ }
+ case KGuestSessionStatus_Undefined:
+ case KGuestSessionStatus_Starting:
+ case KGuestSessionStatus_Max:
+ default:
+ break;
+ }
+ }
+ else
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ }
+}
+
+void UIFileManagerGuestTable::sltOpenGuestSession(QString strUserName, QString strPassword)
+{
+ if (strUserName.isEmpty())
+ {
+ emit sigLogOutput("No user name is given", m_strTableName, FileManagerLogType_Error);
+ if (m_pGuestSessionWidget)
+ m_pGuestSessionWidget->markForError(true);
+ return;
+ }
+ openGuestSession(strUserName, strPassword);
+}
+
+void UIFileManagerGuestTable::setState()
+{
+ if (m_comMachine.isNull())
+ {
+ m_enmState = State_InvalidMachineReference;
+ return;
+ }
+ if (m_comMachine.GetState() == KMachineState_Paused)
+ {
+ m_enmState = State_MachinePaused;
+ return;
+ }
+ if (m_comMachine.GetState() != KMachineState_Running)
+ {
+ m_enmState = State_MachineNotRunning;
+ return;
+ }
+
+ int iGADetectCode = isGuestAdditionsAvailable(pszMinimumGuestAdditionVersion);
+ if (iGADetectCode == 0)
+ {
+ m_enmState = State_NoGuestAdditions;
+ return;
+ }
+ else if (iGADetectCode == -1)
+ {
+ m_enmState = State_GuestAdditionsTooOld;
+ return;
+ }
+
+ if (!m_comGuestSession.isNull() && m_comGuestSession.GetStatus() == KGuestSessionStatus_Started)
+ {
+ m_enmState = State_SessionRunning;
+ return;
+ }
+ if (!m_comGuestSession.isNull() && m_comGuestSession.GetStatus() == KGuestSessionStatus_Error)
+ {
+ m_enmState = State_SessionError;
+ return;
+ }
+ m_enmState = State_SessionPossible;
+}
+
+void UIFileManagerGuestTable::setStateAndEnableWidgets()
+{
+ setState();
+ setSessionDependentWidgetsEnabled();
+ retranslateUi();
+}
+
+void UIFileManagerGuestTable::sltHandleCloseSessionRequest()
+{
+ cleanupGuestSessionListener();
+ closeGuestSession();
+ setStateAndEnableWidgets();
+}
+
+void UIFileManagerGuestTable::sltCommitDataSignalReceived()
+{
+ cleanAll();
+ if (!m_comMachine.isNull())
+ m_comMachine.detach();
+}
+
+void UIFileManagerGuestTable::sltAdditionsStateChange()
+{
+ setStateAndEnableWidgets();
+}
+
+void UIFileManagerGuestTable::setSessionDependentWidgetsEnabled()
+{
+ /* Disable menu actions if guest session is not running: */
+ UIMenu *pGuestSubmenu = m_pActionPool->action(UIActionIndex_M_FileManager_M_GuestSubmenu)->menu();
+ if (pGuestSubmenu)
+ pGuestSubmenu->setEnabled(m_enmState == State_SessionRunning);
+ UIMenu *pHostSubmenu = m_pActionPool->action(UIActionIndex_M_FileManager_M_HostSubmenu)->menu();
+ if (pHostSubmenu)
+ pHostSubmenu->setEnabled(m_enmState == State_SessionRunning);
+
+ /*Manage the guest session (login) widget: */
+ if (m_pGuestSessionWidget)
+ {
+ m_pGuestSessionWidget->setLoginWidgetsEnabled(m_enmState == State_SessionPossible ||
+ m_enmState == State_SessionRunning ||
+ m_enmState == State_SessionError);
+ if (m_enmState == State_SessionPossible)
+ m_pGuestSessionWidget->switchSessionOpenMode();
+ else if (m_enmState == State_SessionRunning)
+ m_pGuestSessionWidget->switchSessionCloseMode();
+ }
+ /* Call to parent: */
+ setSessionWidgetsEnabled(m_enmState == State_SessionRunning);
+
+ emit sigStateChanged(m_enmState == State_SessionRunning);
+}
+
+bool UIFileManagerGuestTable::openGuestSession(const QString &strUserName, const QString &strPassword)
+{
+ if (m_comGuest.isNull())
+ {
+ emit sigLogOutput("Guest reference is invalid", m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+
+ int iGADetectCode = isGuestAdditionsAvailable(pszMinimumGuestAdditionVersion);
+ if (iGADetectCode == 0)
+ {
+ emit sigLogOutput("Could not find Guest Additions",
+ m_strTableName, FileManagerLogType_Error);
+ if (m_pGuestSessionWidget)
+ m_pGuestSessionWidget->markForError(true);
+ return false;
+ }
+ else if (iGADetectCode == -1)
+ {
+ emit sigLogOutput(QString("%1 %2").arg("The Guest Additions are older than ").arg(pszMinimumGuestAdditionVersion),
+ m_strTableName, FileManagerLogType_Error);
+ if (m_pGuestSessionWidget)
+ m_pGuestSessionWidget->markForError(true);
+ return false;
+ }
+
+ m_comGuestSession = m_comGuest.CreateSession(strUserName, strPassword,
+ QString() /* Domain */, "File Manager Session");
+ if (m_comGuestSession.isNull())
+ {
+ emit sigLogOutput("Could not create guest session", m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+
+ if (!m_comGuestSession.isOk())
+ {
+ emit sigLogOutput(UIErrorString::formatErrorInfo(m_comGuestSession), m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+
+ QVector<KVBoxEventType> eventTypes(QVector<KVBoxEventType>() << KVBoxEventType_OnGuestSessionStateChanged);
+ prepareListener(m_pQtSessionListener, m_comSessionListener, m_comGuestSession.GetEventSource(), eventTypes);
+ qRegisterMetaType<CGuestSessionStateChangedEvent>();
+ connect(m_pQtSessionListener->getWrapped(), &UIMainEventListener::sigGuestSessionStatedChanged,
+ this, &UIFileManagerGuestTable::sltGuestSessionStateChanged);
+
+ return true;
+}
+
+void UIFileManagerGuestTable::closeGuestSession()
+{
+ if (!m_comGuestSession.isNull())
+ {
+ m_comGuestSession.Close();
+ m_comGuestSession.detach();
+ emit sigLogOutput("Guest session is closed", m_strTableName, FileManagerLogType_Info);
+ }
+ reset();
+}
+
+void UIFileManagerGuestTable::cleanAll()
+{
+ cleanupConsoleListener();
+ cleanupGuestListener();
+ cleanupGuestSessionListener();
+
+ closeGuestSession();
+ closeMachineSession();
+}
+
+
+#include "UIFileManagerGuestTable.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerGuestTable.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerGuestTable.h
new file mode 100644
index 00000000..d5cc5a62
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerGuestTable.h
@@ -0,0 +1,180 @@
+/* $Id: UIFileManagerGuestTable.h $ */
+/** @file
+ * VBox Qt GUI - UIFileManagerGuestTable class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIFileManagerGuestTable_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIFileManagerGuestTable_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+# include <QUuid>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CEventListener.h"
+#include "CEventSource.h"
+#include "CGuest.h"
+#include "CGuestSession.h"
+#include "CMachine.h"
+#include "CSession.h"
+#include "CConsole.h"
+
+
+/* GUI includes: */
+#include "UIFileManagerTable.h"
+#include "UIMainEventListener.h"
+
+/* Forward declarations: */
+class CGuestSessionStateChangedEvent;
+class UIActionPool;
+class UICustomFileSystemItem;
+class UIGuestSessionWidget;
+
+/** This class scans the guest file system by using the VBox Guest Control API
+ * and populates the UIGuestControlFileModel*/
+class UIFileManagerGuestTable : public UIFileManagerTable
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigNewFileOperation(const CProgress &comProgress, const QString &strTableName);
+ void sigStateChanged(bool fSessionRunning);
+
+public:
+
+ UIFileManagerGuestTable(UIActionPool *pActionPool, const CMachine &comMachine, QWidget *pParent = 0);
+ ~UIFileManagerGuestTable();
+ void copyGuestToHost(const QString& hostDestinationPath);
+ void copyHostToGuest(const QStringList &hostSourcePathList,
+ const QString &strDestination = QString());
+ QUuid machineId();
+ bool isGuestSessionRunning() const;
+ void setIsCurrent(bool fIsCurrent);
+
+protected:
+
+ void retranslateUi() override final;
+ virtual void readDirectory(const QString& strPath, UICustomFileSystemItem *parent, bool isStartDir = false) override final;
+ virtual void deleteByItem(UICustomFileSystemItem *item) override final;
+ virtual void deleteByPath(const QStringList &pathList) override final;
+ virtual void goToHomeDirectory() override final;
+ virtual bool renameItem(UICustomFileSystemItem *item, QString newBaseName) override final;
+ virtual bool createDirectory(const QString &path, const QString &directoryName) override final;
+ virtual QString fsObjectPropertyString() override final;
+ virtual void showProperties() override final;
+ virtual void determineDriveLetters() override final;
+ virtual void determinePathSeparator() override final;
+ virtual void prepareToolbar() override final;
+ virtual void createFileViewContextMenu(const QWidget *pWidget, const QPoint &point) override final;
+ /** @name Copy/Cut guest-to-guest stuff.
+ * @{ */
+ /** Disable/enable paste action depending on the m_eFileOperationType. */
+ virtual void setPasteActionEnabled(bool fEnabled) override final;
+ virtual void pasteCutCopiedObjects() override final;
+ /** @} */
+ virtual void setState();
+ virtual void setSessionDependentWidgetsEnabled();
+
+private slots:
+
+ void sltGuestSessionPanelToggled(bool fChecked);
+ void sltGuestSessionUnregistered(CGuestSession guestSession);
+ void sltGuestSessionRegistered(CGuestSession guestSession);
+ void sltGuestSessionStateChanged(const CGuestSessionStateChangedEvent &cEvent);
+ void sltOpenGuestSession(QString strUserName, QString strPassword);
+ void sltHandleCloseSessionRequest();
+ void sltMachineStateChange(const QUuid &uMachineId, const KMachineState state);
+ void sltCommitDataSignalReceived();
+ void sltAdditionsStateChange();
+
+private:
+
+ enum State
+ {
+ State_InvalidMachineReference,
+ State_MachineNotRunning,
+ State_NoGuestAdditions,
+ State_GuestAdditionsTooOld,
+ State_SessionPossible,
+ State_SessionRunning,
+ State_MachinePaused,
+ State_SessionError,
+ State_Max
+ };
+
+ KFsObjType fileType(const CFsObjInfo &fsInfo);
+ KFsObjType fileType(const CGuestFsObjInfo &fsInfo);
+
+ void prepareActionConnections();
+ bool checkGuestSession();
+ QString permissionString(const CFsObjInfo &fsInfo);
+ bool isFileObjectHidden(const CFsObjInfo &fsInfo);
+
+ void prepareListener(ComObjPtr<UIMainEventListenerImpl> &Qtistener,
+ CEventListener &comEventListener,
+ CEventSource comEventSource, QVector<KVBoxEventType>& eventTypes);
+
+ void cleanupListener(ComObjPtr<UIMainEventListenerImpl> &QtListener,
+ CEventListener &comEventListener,
+ CEventSource comEventSource);
+ void cleanupGuestListener();
+ void cleanupGuestSessionListener();
+ void cleanupConsoleListener();
+ void prepareGuestSessionPanel();
+ bool openGuestSession(const QString& strUserName, const QString& strPassword);
+ void closeGuestSession();
+ bool openMachineSession();
+ bool closeMachineSession();
+ /* Return 0 if GA is not detected, -1 if it is there but older than @p pszMinimumGuestAdditionVersion, and 1 otherwise. */
+ int isGuestAdditionsAvailable(const char* pszMinimumVersion);
+ void setStateAndEnableWidgets();
+
+ void initFileTable();
+ void cleanAll();
+ void manageConnection(bool fConnect, QAction *pAction, void (UIFileManagerGuestTable::*fptr)(void));
+ CGuest m_comGuest;
+ CGuestSession m_comGuestSession;
+ CSession m_comSession;
+ CMachine m_comMachine;
+ CConsole m_comConsole;
+
+ ComObjPtr<UIMainEventListenerImpl> m_pQtGuestListener;
+ ComObjPtr<UIMainEventListenerImpl> m_pQtSessionListener;
+ ComObjPtr<UIMainEventListenerImpl> m_pQtConsoleListener;
+ CEventListener m_comSessionListener;
+ CEventListener m_comGuestListener;
+ CEventListener m_comConsoleListener;
+ UIGuestSessionWidget *m_pGuestSessionWidget;
+ /** True if this table is the current table in parents tab widget. */
+ bool m_fIsCurrent;
+ State m_enmState;
+ const char *pszMinimumGuestAdditionVersion;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIFileManagerGuestTable_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerHostTable.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerHostTable.cpp
new file mode 100644
index 00000000..293f5948
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerHostTable.cpp
@@ -0,0 +1,555 @@
+/* $Id: UIFileManagerHostTable.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFileManagerHostTable class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QDateTime>
+#include <QDir>
+
+/* GUI includes: */
+#include "QILabel.h"
+#include "UIActionPool.h"
+#include "UIFileManager.h"
+#include "UICustomFileSystemModel.h"
+#include "UIFileManagerHostTable.h"
+#include "UIPathOperations.h"
+#include "QIToolBar.h"
+
+
+/*********************************************************************************************************************************
+* UIHostDirectoryDiskUsageComputer definition. *
+*********************************************************************************************************************************/
+
+/** Open directories recursively and sum the disk usage. Don't block the GUI thread while doing this */
+class UIHostDirectoryDiskUsageComputer : public UIDirectoryDiskUsageComputer
+{
+ Q_OBJECT;
+
+public:
+
+ UIHostDirectoryDiskUsageComputer(QObject *parent, QStringList strStartPath);
+
+protected:
+
+ virtual void directoryStatisticsRecursive(const QString &path, UIDirectoryStatistics &statistics) RT_OVERRIDE;
+};
+
+
+/*********************************************************************************************************************************
+* UIHostDirectoryDiskUsageComputer implementation. *
+*********************************************************************************************************************************/
+
+UIHostDirectoryDiskUsageComputer::UIHostDirectoryDiskUsageComputer(QObject *parent, QStringList pathList)
+ :UIDirectoryDiskUsageComputer(parent, pathList)
+{
+}
+
+void UIHostDirectoryDiskUsageComputer::directoryStatisticsRecursive(const QString &path, UIDirectoryStatistics &statistics)
+{
+ /* Prevent modification of the continue flag while reading: */
+ m_mutex.lock();
+ /* Check if m_fOkToContinue is set to false. if so just end recursion: */
+ if (!isOkToContinue())
+ {
+ m_mutex.unlock();
+ return;
+ }
+ m_mutex.unlock();
+
+ QFileInfo fileInfo(path);
+ if (!fileInfo.exists())
+ return;
+ /* if the object is a file or a symlink then read the size and return: */
+ if (fileInfo.isFile())
+ {
+ statistics.m_totalSize += fileInfo.size();
+ ++statistics.m_uFileCount;
+ sigResultUpdated(statistics);
+ return;
+ }
+ else if (fileInfo.isSymLink())
+ {
+ statistics.m_totalSize += fileInfo.size();
+ ++statistics.m_uSymlinkCount;
+ sigResultUpdated(statistics);
+ return;
+ }
+
+ /* if it is a directory then read the content: */
+ QDir dir(path);
+ if (!dir.exists())
+ return;
+
+ QFileInfoList entryList = dir.entryInfoList();
+ for (int i = 0; i < entryList.size(); ++i)
+ {
+ const QFileInfo &entryInfo = entryList.at(i);
+ if (entryInfo.baseName().isEmpty() || entryInfo.baseName() == "." ||
+ entryInfo.baseName() == UICustomFileSystemModel::strUpDirectoryString)
+ continue;
+ statistics.m_totalSize += entryInfo.size();
+ if (entryInfo.isSymLink())
+ statistics.m_uSymlinkCount++;
+ else if(entryInfo.isFile())
+ statistics.m_uFileCount++;
+ else if (entryInfo.isDir())
+ {
+ statistics.m_uDirectoryCount++;
+ directoryStatisticsRecursive(entryInfo.absoluteFilePath(), statistics);
+ }
+ }
+ sigResultUpdated(statistics);
+}
+
+
+/*********************************************************************************************************************************
+* UIFileManagerHostTable implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerHostTable::UIFileManagerHostTable(UIActionPool *pActionPool, QWidget *pParent /* = 0 */)
+ :UIFileManagerTable(pActionPool, pParent)
+{
+ initializeFileTree();
+ prepareToolbar();
+ prepareActionConnections();
+ determinePathSeparator();
+ retranslateUi();
+}
+
+/* static */ void UIFileManagerHostTable::scanDirectory(const QString& strPath, UICustomFileSystemItem *parent,
+ QMap<QString, UICustomFileSystemItem*> &fileObjects)
+{
+
+ QDir directory(strPath);
+ /* For some reason when this filter is applied, folder content QDir::entryInfoList()
+ returns an empty list: */
+ /*directory.setFilter(QDir::NoDotAndDotDot);*/
+ parent->setIsOpened(true);
+ if (!directory.exists())
+ return;
+ QFileInfoList entries = directory.entryInfoList(QDir::Hidden|QDir::AllEntries|QDir::NoDotAndDotDot);
+ for (int i = 0; i < entries.size(); ++i)
+ {
+ const QFileInfo &fileInfo = entries.at(i);
+
+ UICustomFileSystemItem *item = new UICustomFileSystemItem(fileInfo.fileName(), parent, fileType(fileInfo));
+ if (!item)
+ continue;
+
+ item->setData(fileInfo.size(), UICustomFileSystemModelColumn_Size);
+ item->setData(fileInfo.lastModified(), UICustomFileSystemModelColumn_ChangeTime);
+ item->setData(fileInfo.owner(), UICustomFileSystemModelColumn_Owner);
+ item->setData(permissionString(fileInfo.permissions()), UICustomFileSystemModelColumn_Permissions);
+ item->setPath(fileInfo.absoluteFilePath());
+ /* if the item is a symlink set the target path and
+ check the target if it is a directory: */
+ if (fileInfo.isSymLink()) /** @todo No symlinks here on windows, while fsObjectPropertyString() does see them. RTDirReadEx works wrt symlinks, btw. */
+ {
+ item->setTargetPath(fileInfo.symLinkTarget());
+ item->setIsSymLinkToADirectory(QFileInfo(fileInfo.symLinkTarget()).isDir());
+ }
+ item->setIsHidden(fileInfo.isHidden());
+ fileObjects.insert(fileInfo.fileName(), item);
+ item->setIsOpened(false);
+ }
+}
+
+void UIFileManagerHostTable::retranslateUi()
+{
+ if (m_pLocationLabel)
+ m_pLocationLabel->setText(UIFileManager::tr("Host File System:"));
+ m_strTableName = UIFileManager::tr("Host");
+ UIFileManagerTable::retranslateUi();
+}
+
+void UIFileManagerHostTable::prepareToolbar()
+{
+ if (m_pToolBar && m_pActionPool)
+ {
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_GoUp));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_GoHome));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Refresh));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Delete));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Rename));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_CreateNewDirectory));
+ // m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Copy));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Cut));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Paste));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_SelectAll));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_InvertSelection));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_ShowProperties));
+
+ m_selectionDependentActions.insert(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Delete));
+ m_selectionDependentActions.insert(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Rename));
+ m_selectionDependentActions.insert(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_ShowProperties));
+
+ /* Hide cut, copy, and paste for now. We will implement those
+ when we have an API for host file operations: */
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Copy)->setVisible(false);
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Cut)->setVisible(false);
+ m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Paste)->setVisible(false);
+ }
+ setSelectionDependentActionsEnabled(false);
+}
+
+void UIFileManagerHostTable::createFileViewContextMenu(const QWidget *pWidget, const QPoint &point)
+{
+ if (!pWidget)
+ return;
+
+ QMenu menu;
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_GoUp));
+
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_GoHome));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Refresh));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Delete));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Rename));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_CreateNewDirectory));
+ // menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Copy));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Cut));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Paste));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_SelectAll));
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_InvertSelection));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_ShowProperties));
+ menu.exec(pWidget->mapToGlobal(point));
+}
+
+void UIFileManagerHostTable::readDirectory(const QString& strPath, UICustomFileSystemItem *parent, bool isStartDir /*= false*/)
+{
+ if (!parent)
+ return;
+
+ QMap<QString, UICustomFileSystemItem*> fileObjects;
+ scanDirectory(strPath, parent, fileObjects);
+ checkDotDot(fileObjects, parent, isStartDir);
+}
+
+void UIFileManagerHostTable::deleteByItem(UICustomFileSystemItem *item)
+{
+ if (item->isUpDirectory())
+ return;
+ if (!item->isDirectory())
+ {
+ QDir itemToDelete;
+ itemToDelete.remove(item->path());
+ }
+ QDir itemToDelete(item->path());
+ itemToDelete.setFilter(QDir::NoDotAndDotDot);
+ /* Try to delete item recursively (in case of directories).
+ note that this is no good way of deleting big directory
+ trees. We need a better error reporting and a kind of progress
+ indicator: */
+ /** @todo replace this recursive delete by a better implementation: */
+ bool deleteSuccess = itemToDelete.removeRecursively();
+
+ if (!deleteSuccess)
+ emit sigLogOutput(QString(item->path()).append(" could not be deleted"), m_strTableName, FileManagerLogType_Error);
+}
+
+void UIFileManagerHostTable::deleteByPath(const QStringList &pathList)
+{
+ foreach (const QString &strPath, pathList)
+ {
+ bool deleteSuccess = true;
+ KFsObjType eType = fileType(QFileInfo(strPath));
+ if (eType == KFsObjType_File || eType == KFsObjType_Symlink)
+ {
+ deleteSuccess = QDir().remove(strPath);
+ }
+ else if (eType == KFsObjType_Directory)
+ {
+ QDir itemToDelete(strPath);
+ itemToDelete.setFilter(QDir::NoDotAndDotDot);
+ deleteSuccess = itemToDelete.removeRecursively();
+ }
+ if (!deleteSuccess)
+ emit sigLogOutput(QString(strPath).append(" could not be deleted"), m_strTableName, FileManagerLogType_Error);
+ }
+}
+
+void UIFileManagerHostTable::goToHomeDirectory()
+{
+ if (!rootItem() || rootItem()->childCount() <= 0)
+ return;
+ UICustomFileSystemItem *startDirItem = rootItem()->child(0);
+ if (!startDirItem)
+ return;
+
+ QString userHome = UIPathOperations::sanitize(QDir::homePath());
+ goIntoDirectory(UIPathOperations::pathTrail(userHome));
+}
+
+bool UIFileManagerHostTable::renameItem(UICustomFileSystemItem *item, QString newBaseName)
+{
+ if (!item || item->isUpDirectory() || newBaseName.isEmpty())
+ return false;
+ QString newPath = UIPathOperations::constructNewItemPath(item->path(), newBaseName);
+ QDir tempDir;
+ if (tempDir.rename(item->path(), newPath))
+ {
+ item->setPath(newPath);
+ return true;
+ }
+ return false;
+}
+
+bool UIFileManagerHostTable::createDirectory(const QString &path, const QString &directoryName)
+{
+ QDir parentDir(path);
+ if (!parentDir.mkdir(directoryName))
+ {
+ emit sigLogOutput(UIPathOperations::mergePaths(path, directoryName).append(" could not be created"), m_strTableName, FileManagerLogType_Error);
+ return false;
+ }
+
+ return true;
+}
+
+/* static */
+KFsObjType UIFileManagerHostTable::fileType(const QFileInfo &fsInfo)
+{
+ if (!fsInfo.exists())
+ return KFsObjType_Unknown;
+ /* first check if it is symlink becacuse for Qt
+ being smylin and directory/file is not mutually exclusive: */
+ if (fsInfo.isSymLink())
+ return KFsObjType_Symlink;
+ else if (fsInfo.isFile())
+ return KFsObjType_File;
+ else if (fsInfo.isDir())
+ return KFsObjType_Directory;
+ return KFsObjType_Unknown;
+}
+
+/* static */
+KFsObjType UIFileManagerHostTable::fileType(const QString &strPath)
+{
+ return fileType(QFileInfo(strPath));
+}
+
+QString UIFileManagerHostTable::fsObjectPropertyString()
+{
+ QStringList selectedObjects = selectedItemPathList();
+ if (selectedObjects.isEmpty())
+ return QString();
+ if (selectedObjects.size() == 1)
+ {
+ if (selectedObjects.at(0).isNull())
+ return QString();
+ QFileInfo fileInfo(selectedObjects.at(0));
+ if (!fileInfo.exists())
+ return QString();
+ QStringList propertyStringList;
+ /* Name: */
+ propertyStringList << UIFileManager::tr("<b>Name:</b> %1<br/>").arg(fileInfo.fileName());
+ /* Size: */
+ propertyStringList << UIFileManager::tr("<b>Size:</b> %1 bytes").arg(QString::number(fileInfo.size()));
+ if (fileInfo.size() >= m_iKiloByte)
+ propertyStringList << QString(" (%1)").arg(humanReadableSize(fileInfo.size()));
+ propertyStringList << "<br/>";
+ /* Type: */
+ propertyStringList << UIFileManager::tr("<b>Type:</b> %1<br/>").arg(fileTypeString(fileType(fileInfo)));
+ /* Creation Date: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+ propertyStringList << UIFileManager::tr("<b>Created:</b> %1<br/>").arg(fileInfo.birthTime().toString());
+#else
+ propertyStringList << UIFileManager::tr("<b>Created:</b> %1<br/>").arg(fileInfo.created().toString());
+#endif
+ /* Last Modification Date: */
+ propertyStringList << UIFileManager::tr("<b>Modified:</b> %1<br/>").arg(fileInfo.lastModified().toString());
+ /* Owner: */
+ propertyStringList << UIFileManager::tr("<b>Owner:</b> %1").arg(fileInfo.owner());
+
+ return propertyStringList.join(QString());
+ }
+
+ int fileCount = 0;
+ int directoryCount = 0;
+ ULONG64 totalSize = 0;
+
+ for(int i = 0; i < selectedObjects.size(); ++i)
+ {
+ QFileInfo fileInfo(selectedObjects.at(i));
+ if (!fileInfo.exists())
+ continue;
+ if (fileInfo.isFile())
+ ++fileCount;
+ if (fileInfo.isDir())
+ ++directoryCount;
+ totalSize += fileInfo.size();
+ }
+ QStringList propertyStringList;
+ propertyStringList << UIFileManager::tr("<b>Selected:</b> %1 files and %2 directories<br/>").
+ arg(QString::number(fileCount)).arg(QString::number(directoryCount));
+ propertyStringList << UIFileManager::tr("<b>Size:</b> %1 bytes").arg(QString::number(totalSize));
+ if (totalSize >= m_iKiloByte)
+ propertyStringList << QString(" (%1)").arg(humanReadableSize(totalSize));
+
+ return propertyStringList.join(QString());
+}
+
+void UIFileManagerHostTable::showProperties()
+{
+ qRegisterMetaType<UIDirectoryStatistics>();
+ QString fsPropertyString = fsObjectPropertyString();
+ if (fsPropertyString.isEmpty())
+ return;
+ if (!m_pPropertiesDialog)
+ m_pPropertiesDialog = new UIPropertiesDialog(this);
+ if (!m_pPropertiesDialog)
+ return;
+
+ UIHostDirectoryDiskUsageComputer *directoryThread = 0;
+
+ QStringList selectedObjects = selectedItemPathList();
+ if ((selectedObjects.size() == 1 && QFileInfo(selectedObjects.at(0)).isDir())
+ || selectedObjects.size() > 1)
+ {
+ directoryThread = new UIHostDirectoryDiskUsageComputer(this, selectedObjects);
+ if (directoryThread)
+ {
+ connect(directoryThread, &UIHostDirectoryDiskUsageComputer::sigResultUpdated,
+ this, &UIFileManagerHostTable::sltReceiveDirectoryStatistics/*, Qt::DirectConnection*/);
+ directoryThread->start();
+ }
+ }
+ m_pPropertiesDialog->setWindowTitle("Properties");
+ m_pPropertiesDialog->setPropertyText(fsPropertyString);
+ m_pPropertiesDialog->execute();
+ if (directoryThread)
+ {
+ if (directoryThread->isRunning())
+ directoryThread->stopRecursion();
+ disconnect(directoryThread, &UIHostDirectoryDiskUsageComputer::sigResultUpdated,
+ this, &UIFileManagerHostTable::sltReceiveDirectoryStatistics/*, Qt::DirectConnection*/);
+ directoryThread->wait();
+ }
+}
+
+void UIFileManagerHostTable::determineDriveLetters()
+{
+ QFileInfoList drive = QDir::drives();
+ m_driveLetterList.clear();
+ for (int i = 0; i < drive.size(); ++i)
+ {
+ if (UIPathOperations::doesPathStartWithDriveLetter(drive[i].filePath()))
+ m_driveLetterList.push_back(drive[i].filePath());
+ }
+}
+
+void UIFileManagerHostTable::determinePathSeparator()
+{
+ setPathSeparator(QDir::separator());
+}
+
+/* static */QString UIFileManagerHostTable::permissionString(QFileDevice::Permissions permissions)
+{
+ QString strPermissions;
+ if (permissions & QFileDevice::ReadOwner)
+ strPermissions += 'r';
+ else
+ strPermissions += '-';
+
+ if (permissions & QFileDevice::WriteOwner)
+ strPermissions += 'w';
+ else
+ strPermissions += '-';
+
+ if (permissions & QFileDevice::ExeOwner)
+ strPermissions += 'x';
+ else
+ strPermissions += '-';
+
+ if (permissions & QFileDevice::ReadGroup)
+ strPermissions += 'r';
+ else
+ strPermissions += '-';
+
+ if (permissions & QFileDevice::WriteGroup)
+ strPermissions += 'w';
+ else
+ strPermissions += '-';
+
+ if (permissions & QFileDevice::ExeGroup)
+ strPermissions += 'x';
+ else
+ strPermissions += '-';
+
+ if (permissions & QFileDevice::ReadOther)
+ strPermissions += 'r';
+ else
+ strPermissions += '-';
+
+ if (permissions & QFileDevice::WriteOther)
+ strPermissions += 'w';
+ else
+ strPermissions += '-';
+
+ if (permissions & QFileDevice::ExeOther)
+ strPermissions += 'x';
+ else
+ strPermissions += '-';
+ return strPermissions;
+}
+
+void UIFileManagerHostTable::prepareActionConnections()
+{
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_GoUp), &QAction::triggered,
+ this, &UIFileManagerTable::sltGoUp);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_GoHome), &QAction::triggered,
+ this, &UIFileManagerTable::sltGoHome);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Refresh), &QAction::triggered,
+ this, &UIFileManagerTable::sltRefresh);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Delete), &QAction::triggered,
+ this, &UIFileManagerTable::sltDelete);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Rename), &QAction::triggered,
+ this, &UIFileManagerTable::sltRename);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Copy), &QAction::triggered,
+ this, &UIFileManagerTable::sltCopy);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Cut), &QAction::triggered,
+ this, &UIFileManagerTable::sltCut);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_Paste), &QAction::triggered,
+ this, &UIFileManagerTable::sltPaste);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_SelectAll), &QAction::triggered,
+ this, &UIFileManagerTable::sltSelectAll);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_InvertSelection), &QAction::triggered,
+ this, &UIFileManagerTable::sltInvertSelection);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_ShowProperties), &QAction::triggered,
+ this, &UIFileManagerTable::sltShowProperties);
+ connect(m_pActionPool->action(UIActionIndex_M_FileManager_S_Host_CreateNewDirectory), &QAction::triggered,
+ this, &UIFileManagerTable::sltCreateNewDirectory);
+}
+
+#include "UIFileManagerHostTable.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerHostTable.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerHostTable.h
new file mode 100644
index 00000000..13c31748
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerHostTable.h
@@ -0,0 +1,88 @@
+/* $Id: UIFileManagerHostTable.h $ */
+/** @file
+ * VBox Qt GUI - UIFileManagerHostTable class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIFileManagerHostTable_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIFileManagerHostTable_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QFileDevice>
+
+/* GUI includes: */
+#include "UIFileManagerTable.h"
+
+/* Forward declarations: */
+class UIActionPool;
+class UICustomFileSystemItem;
+
+/** This class scans the host file system by using the Qt API
+ and connects to the UICustomFileSystemModel*/
+class UIFileManagerHostTable : public UIFileManagerTable
+{
+ Q_OBJECT;
+
+public:
+
+ UIFileManagerHostTable(UIActionPool *pActionPool, QWidget *pParent = 0);
+ static KFsObjType fileType(const QFileInfo &fsInfo);
+ static KFsObjType fileType(const QString &strPath);
+
+protected:
+
+ /** Scans the directory with the path @strPath and inserts items to the
+ * tree under the @p parent. */
+ static void scanDirectory(const QString& strPath, UICustomFileSystemItem *parent,
+ QMap<QString, UICustomFileSystemItem*> &fileObjects);
+ void retranslateUi() override final;
+ virtual void readDirectory(const QString& strPath, UICustomFileSystemItem *parent, bool isStartDir = false) override final;
+ virtual void deleteByItem(UICustomFileSystemItem *item) override final;
+ virtual void deleteByPath(const QStringList &pathList) override final;
+ virtual void goToHomeDirectory() override final;
+ virtual bool renameItem(UICustomFileSystemItem *item, QString newBaseName) override final;
+ virtual bool createDirectory(const QString &path, const QString &directoryName) override final;
+ virtual QString fsObjectPropertyString() override final;
+ virtual void showProperties() override final;
+ virtual void determineDriveLetters() override final;
+ virtual void determinePathSeparator() override final;
+ virtual void prepareToolbar() override final;
+ virtual void createFileViewContextMenu(const QWidget *pWidget, const QPoint &point) override final;
+ /** @name Copy/Cut host-to-host stuff. Currently not implemented.
+ * @{ */
+ /** Disable/enable paste action depending on the m_eFileOperationType. */
+ virtual void setPasteActionEnabled(bool) override final {}
+ virtual void pasteCutCopiedObjects() override final {}
+ /** @} */
+
+private:
+
+ static QString permissionString(QFileDevice::Permissions permissions);
+ void prepareActionConnections();
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIFileManagerHostTable_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerLogPanel.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerLogPanel.cpp
new file mode 100644
index 00000000..0513253e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerLogPanel.cpp
@@ -0,0 +1,147 @@
+/* $Id: UIFileManagerLogPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QMenu>
+#include <QSpinBox>
+#include <QTextEdit>
+#include <QTime>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIFileManager.h"
+#include "UIFileManagerLogPanel.h"
+
+
+/*********************************************************************************************************************************
+* UIFileManagerLogViewer definition. *
+*********************************************************************************************************************************/
+
+class UIFileManagerLogViewer : public QTextEdit
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIFileManagerLogViewer(QWidget *pParent = 0);
+
+protected:
+
+ virtual void contextMenuEvent(QContextMenuEvent * event) RT_OVERRIDE;
+
+private slots:
+
+ void sltClear();
+};
+
+/*********************************************************************************************************************************
+* UIFileManagerLogViewer implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerLogViewer::UIFileManagerLogViewer(QWidget *pParent /* = 0 */)
+ :QTextEdit(pParent)
+{
+ setUndoRedoEnabled(false);
+ setReadOnly(true);
+}
+
+void UIFileManagerLogViewer::contextMenuEvent(QContextMenuEvent *event)
+{
+ QMenu *menu = createStandardContextMenu();
+
+ QAction *pClearAction = menu->addAction(UIFileManager::tr("Clear"));
+ connect(pClearAction, &QAction::triggered, this, &UIFileManagerLogViewer::sltClear);
+ menu->exec(event->globalPos());
+ delete menu;
+}
+
+void UIFileManagerLogViewer::sltClear()
+{
+ clear();
+}
+
+
+/*********************************************************************************************************************************
+* UIFileManagerLogPanel implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerLogPanel::UIFileManagerLogPanel(QWidget *pParent /* = 0 */)
+ : UIDialogPanel(pParent)
+ , m_pLogTextEdit(0)
+{
+ prepare();
+}
+
+void UIFileManagerLogPanel::appendLog(const QString &strLog, const QString &strMachineName, FileManagerLogType eLogType)
+{
+ if (!m_pLogTextEdit)
+ return;
+ QString strStartTag("<font color=\"Black\">");
+ QString strEndTag("</font>");
+ if (eLogType == FileManagerLogType_Error)
+ {
+ strStartTag = "<b><font color=\"Red\">";
+ strEndTag = "</font></b>";
+ }
+ QString strColoredLog = QString("%1 %2: %3 %4 %5").arg(strStartTag).arg(QTime::currentTime().toString("hh:mm:ss:z")).arg(strMachineName).arg(strLog).arg(strEndTag);
+ m_pLogTextEdit->append(strColoredLog);
+ m_pLogTextEdit->moveCursor(QTextCursor::End);
+ m_pLogTextEdit->ensureCursorVisible();
+ emit sigShowPanel(this);
+}
+
+QString UIFileManagerLogPanel::panelName() const
+{
+ return "LogPanel";
+}
+
+void UIFileManagerLogPanel::prepareWidgets()
+{
+ if (!mainLayout())
+ return;
+ m_pLogTextEdit = new UIFileManagerLogViewer;
+ if (m_pLogTextEdit)
+ {
+ mainLayout()->addWidget(m_pLogTextEdit);
+ }
+}
+
+void UIFileManagerLogPanel::prepareConnections()
+{
+}
+
+void UIFileManagerLogPanel::retranslateUi()
+{
+ UIDialogPanel::retranslateUi();
+
+}
+
+
+#include "UIFileManagerLogPanel.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerLogPanel.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerLogPanel.h
new file mode 100644
index 00000000..e26596c4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerLogPanel.h
@@ -0,0 +1,69 @@
+/* $Id: UIFileManagerLogPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIFileManagerLogPanel_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIFileManagerLogPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIGuestControlDefs.h"
+#include "UIDialogPanel.h"
+
+/* Forward declarations: */
+class QTextEdit;
+class UIFileManager;
+
+/** UIDialogPanel extension to display file manager logs. */
+class UIFileManagerLogPanel : public UIDialogPanel
+{
+ Q_OBJECT;
+
+public:
+
+ UIFileManagerLogPanel(QWidget *pParent = 0);
+ void appendLog(const QString &str, const QString &strMachineName, FileManagerLogType eLogType);
+ virtual QString panelName() const RT_OVERRIDE;
+
+protected:
+
+ virtual void prepareWidgets() RT_OVERRIDE;
+ virtual void prepareConnections() RT_OVERRIDE;
+
+ /** Handles the translation event. */
+ void retranslateUi();
+
+private slots:
+
+
+private:
+
+ QTextEdit *m_pLogTextEdit;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIFileManagerLogPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOperationsPanel.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOperationsPanel.cpp
new file mode 100644
index 00000000..4e9fcbbd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOperationsPanel.cpp
@@ -0,0 +1,480 @@
+/* $Id: UIFileManagerOperationsPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QGridLayout>
+#include <QLabel>
+#include <QMenu>
+#include <QProgressBar>
+#include <QScrollArea>
+#include <QScrollBar>
+#include <QStyle>
+
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "QILabel.h"
+#include "UIErrorString.h"
+#include "UIIconPool.h"
+#include "UIFileManager.h"
+#include "UIFileManagerOperationsPanel.h"
+#include "UIProgressEventHandler.h"
+
+/* COM includes: */
+#include "CProgress.h"
+
+
+/*********************************************************************************************************************************
+* UIFileOperationProgressWidget definition. *
+*********************************************************************************************************************************/
+
+class UIFileOperationProgressWidget : public QIWithRetranslateUI<QFrame>
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigProgressComplete(QUuid progressId);
+ void sigProgressFail(QString strErrorString, QString strSourceTableName, FileManagerLogType eLogType);
+ void sigFocusIn(QWidget *pWidget);
+ void sigFocusOut(QWidget *pWidget);
+
+public:
+
+ UIFileOperationProgressWidget(const CProgress &comProgress, const QString &strSourceTableName, QWidget *pParent = 0);
+ ~UIFileOperationProgressWidget();
+ bool isCompleted() const;
+ bool isCanceled() const;
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual void focusInEvent(QFocusEvent *pEvent) RT_OVERRIDE;
+ virtual void focusOutEvent(QFocusEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ void sltHandleProgressPercentageChange(const QUuid &uProgressId, const int iPercent);
+ void sltHandleProgressComplete(const QUuid &uProgressId);
+ void sltCancelProgress();
+
+private:
+ enum OperationStatus
+ {
+ OperationStatus_NotStarted,
+ OperationStatus_Working,
+ OperationStatus_Paused,
+ OperationStatus_Canceled,
+ OperationStatus_Succeded,
+ OperationStatus_Failed,
+ OperationStatus_Invalid,
+ OperationStatus_Max
+ };
+
+ void prepare();
+ void prepareWidgets();
+ void prepareEventHandler();
+ void cleanupEventHandler();
+
+ OperationStatus m_eStatus;
+ CProgress m_comProgress;
+ UIProgressEventHandler *m_pEventHandler;
+ QGridLayout *m_pMainLayout;
+ QProgressBar *m_pProgressBar;
+ QIToolButton *m_pCancelButton;
+ QILabel *m_pStatusLabel;
+ QILabel *m_pOperationDescriptionLabel;
+ /** Name of the table from which this operation has originated. */
+ QString m_strSourceTableName;
+};
+
+
+/*********************************************************************************************************************************
+* UIFileOperationProgressWidget implementation. *
+*********************************************************************************************************************************/
+
+UIFileOperationProgressWidget::UIFileOperationProgressWidget(const CProgress &comProgress, const QString &strSourceTableName, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QFrame>(pParent)
+ , m_eStatus(OperationStatus_NotStarted)
+ , m_comProgress(comProgress)
+ , m_pEventHandler(0)
+ , m_pMainLayout(0)
+ , m_pProgressBar(0)
+ , m_pCancelButton(0)
+ , m_pStatusLabel(0)
+ , m_pOperationDescriptionLabel(0)
+ , m_strSourceTableName(strSourceTableName)
+{
+ prepare();
+ setFocusPolicy(Qt::ClickFocus);
+ setStyleSheet("QFrame:focus { border-width: 1px; border-style: dashed; border-color: black; }");
+}
+
+UIFileOperationProgressWidget::~UIFileOperationProgressWidget()
+{
+ cleanupEventHandler();
+}
+
+bool UIFileOperationProgressWidget::isCompleted() const
+{
+ if (m_comProgress.isNull())
+ return true;
+ return m_comProgress.GetCompleted();
+}
+
+bool UIFileOperationProgressWidget::isCanceled() const
+{
+ if (m_comProgress.isNull())
+ return true;
+ return m_comProgress.GetCanceled();
+}
+
+void UIFileOperationProgressWidget::retranslateUi()
+{
+ if (m_pCancelButton)
+ m_pCancelButton->setToolTip(UIFileManager::tr("Cancel"));
+
+ switch (m_eStatus)
+ {
+ case OperationStatus_NotStarted:
+ m_pStatusLabel->setText(UIFileManager::tr("Not yet started"));
+ break;
+ case OperationStatus_Working:
+ m_pStatusLabel->setText(UIFileManager::tr("Working"));
+ break;
+ case OperationStatus_Paused:
+ m_pStatusLabel->setText(UIFileManager::tr("Paused"));
+ break;
+ case OperationStatus_Canceled:
+ m_pStatusLabel->setText(UIFileManager::tr("Canceled"));
+ break;
+ case OperationStatus_Succeded:
+ m_pStatusLabel->setText(UIFileManager::tr("Succeded"));
+ break;
+ case OperationStatus_Failed:
+ m_pStatusLabel->setText(UIFileManager::tr("Failed"));
+ break;
+ case OperationStatus_Invalid:
+ case OperationStatus_Max:
+ default:
+ m_pStatusLabel->setText(UIFileManager::tr("Invalid"));
+ break;
+ }
+}
+
+void UIFileOperationProgressWidget::focusInEvent(QFocusEvent *pEvent)
+{
+ QFrame::focusInEvent(pEvent);
+ emit sigFocusIn(this);
+}
+
+void UIFileOperationProgressWidget::focusOutEvent(QFocusEvent *pEvent)
+{
+ QFrame::focusOutEvent(pEvent);
+ emit sigFocusOut(this);
+}
+
+void UIFileOperationProgressWidget::prepare()
+{
+ prepareWidgets();
+ prepareEventHandler();
+ retranslateUi();
+}
+
+void UIFileOperationProgressWidget::prepareWidgets()
+{
+ m_pMainLayout = new QGridLayout;
+ if (!m_pMainLayout)
+ return;
+ //m_pMainLayout->setSpacing(0);
+
+ m_pOperationDescriptionLabel = new QILabel;
+ if (m_pOperationDescriptionLabel)
+ {
+ m_pOperationDescriptionLabel->setContextMenuPolicy(Qt::NoContextMenu);
+ m_pMainLayout->addWidget(m_pOperationDescriptionLabel, 0, 0, 1, 3);
+ if (!m_comProgress.isNull())
+ m_pOperationDescriptionLabel->setText(m_comProgress.GetDescription());
+ }
+
+ m_pProgressBar = new QProgressBar;
+ if (m_pProgressBar)
+ {
+ m_pProgressBar->setMinimum(0);
+ m_pProgressBar->setMaximum(100);
+ m_pProgressBar->setTextVisible(true);
+ m_pMainLayout->addWidget(m_pProgressBar, 1, 0, 1, 2);
+ }
+
+ m_pCancelButton = new QIToolButton;
+ if (m_pCancelButton)
+ {
+ m_pCancelButton->setIcon(QApplication::style()->standardIcon(QStyle::SP_DockWidgetCloseButton));
+ connect(m_pCancelButton, &QIToolButton::clicked, this, &UIFileOperationProgressWidget::sltCancelProgress);
+ if (!m_comProgress.isNull() && !m_comProgress.GetCancelable())
+ m_pCancelButton->setEnabled(false);
+ m_pMainLayout->addWidget(m_pCancelButton, 1, 2, 1, 1);
+ }
+
+ m_pStatusLabel = new QILabel;
+ if (m_pStatusLabel)
+ {
+ m_pStatusLabel->setContextMenuPolicy(Qt::NoContextMenu);
+ m_pMainLayout->addWidget(m_pStatusLabel, 1, 3, 1, 1);
+ }
+
+ setLayout(m_pMainLayout);
+ retranslateUi();
+}
+
+void UIFileOperationProgressWidget::prepareEventHandler()
+{
+ if (m_comProgress.isNull())
+ return;
+ m_pEventHandler = new UIProgressEventHandler(this, m_comProgress);
+ connect(m_pEventHandler, &UIProgressEventHandler::sigProgressPercentageChange,
+ this, &UIFileOperationProgressWidget::sltHandleProgressPercentageChange);
+ connect(m_pEventHandler, &UIProgressEventHandler::sigProgressTaskComplete,
+ this, &UIFileOperationProgressWidget::sltHandleProgressComplete);
+ m_eStatus = OperationStatus_Working;
+ retranslateUi();
+}
+
+void UIFileOperationProgressWidget::cleanupEventHandler()
+{
+ delete m_pEventHandler;
+ m_pEventHandler = 0;
+}
+
+void UIFileOperationProgressWidget::sltHandleProgressPercentageChange(const QUuid &uProgressId, const int iPercent)
+{
+ Q_UNUSED(uProgressId);
+ m_pProgressBar->setValue(iPercent);
+}
+
+void UIFileOperationProgressWidget::sltHandleProgressComplete(const QUuid &uProgressId)
+{
+ Q_UNUSED(uProgressId);
+ if (m_pCancelButton)
+ m_pCancelButton->setEnabled(false);
+
+ if (!m_comProgress.isOk() || m_comProgress.GetResultCode() != 0)
+ {
+ emit sigProgressFail(UIErrorString::formatErrorInfo(m_comProgress), m_strSourceTableName, FileManagerLogType_Error);
+ m_eStatus = OperationStatus_Failed;
+ }
+ else
+ {
+ emit sigProgressComplete(m_comProgress.GetId());
+ m_eStatus = OperationStatus_Succeded;
+ }
+ if (m_pProgressBar)
+ m_pProgressBar->setValue(100);
+
+ cleanupEventHandler();
+ retranslateUi();
+}
+
+void UIFileOperationProgressWidget::sltCancelProgress()
+{
+ m_comProgress.Cancel();
+ /* Since we dont have a "progress canceled" event we have to do this here: */
+ if (m_pCancelButton)
+ m_pCancelButton->setEnabled(false);
+ if (m_pProgressBar)
+ m_pProgressBar->setEnabled(false);
+ m_eStatus = OperationStatus_Canceled;
+ cleanupEventHandler();
+ retranslateUi();
+}
+
+
+/*********************************************************************************************************************************
+* UIFileManagerOperationsPanel implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerOperationsPanel::UIFileManagerOperationsPanel(QWidget *pParent /* = 0 */)
+ : UIDialogPanel(pParent)
+ , m_pScrollArea(0)
+ , m_pContainerWidget(0)
+ , m_pContainerLayout(0)
+ , m_pContainerSpaceItem(0)
+ , m_pWidgetInFocus(0)
+{
+ prepare();
+}
+
+void UIFileManagerOperationsPanel::addNewProgress(const CProgress &comProgress, const QString &strSourceTableName)
+{
+ if (!m_pContainerLayout)
+ return;
+
+ UIFileOperationProgressWidget *pOperationsWidget = new UIFileOperationProgressWidget(comProgress, strSourceTableName);
+ if (!pOperationsWidget)
+ return;
+ m_widgetSet.insert(pOperationsWidget);
+ m_pContainerLayout->insertWidget(m_pContainerLayout->count() - 1, pOperationsWidget);
+
+ connect(pOperationsWidget, &UIFileOperationProgressWidget::sigProgressComplete,
+ this, &UIFileManagerOperationsPanel::sigFileOperationComplete);
+ connect(pOperationsWidget, &UIFileOperationProgressWidget::sigProgressFail,
+ this, &UIFileManagerOperationsPanel::sigFileOperationFail);
+
+ connect(pOperationsWidget, &UIFileOperationProgressWidget::sigFocusIn,
+ this, &UIFileManagerOperationsPanel::sltHandleWidgetFocusIn);
+ connect(pOperationsWidget, &UIFileOperationProgressWidget::sigFocusOut,
+ this, &UIFileManagerOperationsPanel::sltHandleWidgetFocusOut);
+ sigShowPanel(this);
+}
+
+QString UIFileManagerOperationsPanel::panelName() const
+{
+ return "OperationsPanel";
+}
+
+void UIFileManagerOperationsPanel::prepareWidgets()
+{
+ if (!mainLayout())
+ return;
+
+ QPalette pal = QApplication::palette();
+ pal.setColor(QPalette::Active, QPalette::Window, pal.color(QPalette::Active, QPalette::Base));
+ setPalette(pal);
+
+ m_pScrollArea = new QScrollArea;
+ m_pContainerWidget = new QWidget;
+ m_pContainerLayout = new QVBoxLayout;
+ if (!m_pScrollArea || !m_pContainerWidget || !m_pContainerLayout)
+ return;
+
+ QScrollBar *pVerticalScrollBar = m_pScrollArea->verticalScrollBar();
+ if (pVerticalScrollBar)
+ QObject::connect(pVerticalScrollBar, &QScrollBar::rangeChanged, this, &UIFileManagerOperationsPanel::sltScrollToBottom);
+
+ m_pScrollArea->setBackgroundRole(QPalette::Window);
+ m_pScrollArea->setWidgetResizable(true);
+
+ mainLayout()->addWidget(m_pScrollArea);
+
+ m_pScrollArea->setWidget(m_pContainerWidget);
+ m_pContainerWidget->setLayout(m_pContainerLayout);
+ m_pContainerLayout->addStretch(4);
+}
+
+void UIFileManagerOperationsPanel::prepareConnections()
+{
+
+}
+
+void UIFileManagerOperationsPanel::retranslateUi()
+{
+ UIDialogPanel::retranslateUi();
+}
+
+void UIFileManagerOperationsPanel::contextMenuEvent(QContextMenuEvent *pEvent)
+{
+ QMenu *menu = new QMenu(this);
+
+ if (m_pWidgetInFocus)
+ {
+ QAction *pRemoveSelected = menu->addAction(UIFileManager::tr("Remove Selected"));
+ connect(pRemoveSelected, &QAction::triggered,
+ this, &UIFileManagerOperationsPanel::sltRemoveSelected);
+ }
+
+ QAction *pRemoveFinished = menu->addAction(UIFileManager::tr("Remove Finished"));
+ QAction *pRemoveAll = menu->addAction(UIFileManager::tr("Remove All"));
+
+ connect(pRemoveFinished, &QAction::triggered,
+ this, &UIFileManagerOperationsPanel::sltRemoveFinished);
+ connect(pRemoveAll, &QAction::triggered,
+ this, &UIFileManagerOperationsPanel::sltRemoveAll);
+
+ menu->exec(pEvent->globalPos());
+ delete menu;
+}
+
+void UIFileManagerOperationsPanel::sltRemoveFinished()
+{
+ QList<UIFileOperationProgressWidget*> widgetsToRemove;
+ foreach (QWidget *pWidget, m_widgetSet)
+ {
+ UIFileOperationProgressWidget *pProgressWidget = qobject_cast<UIFileOperationProgressWidget*>(pWidget);
+ if (pProgressWidget && pProgressWidget->isCompleted())
+ {
+ delete pProgressWidget;
+ widgetsToRemove << pProgressWidget;
+ }
+ }
+ foreach (UIFileOperationProgressWidget *pWidget, widgetsToRemove)
+ m_widgetSet.remove(pWidget);
+}
+
+void UIFileManagerOperationsPanel::sltRemoveAll()
+{
+ foreach (QWidget *pWidget, m_widgetSet)
+ {
+ if (pWidget)
+ {
+ delete pWidget;
+ }
+ }
+ m_widgetSet.clear();
+}
+
+void UIFileManagerOperationsPanel::sltRemoveSelected()
+{
+ if (!m_pWidgetInFocus)
+ return;
+ delete m_pWidgetInFocus;
+ m_widgetSet.remove(m_pWidgetInFocus);
+}
+
+void UIFileManagerOperationsPanel::sltHandleWidgetFocusIn(QWidget *pWidget)
+{
+ if (!pWidget)
+ return;
+ m_pWidgetInFocus = pWidget;
+}
+
+void UIFileManagerOperationsPanel::sltHandleWidgetFocusOut(QWidget *pWidget)
+{
+ if (!pWidget)
+ return;
+ m_pWidgetInFocus = 0;
+}
+
+void UIFileManagerOperationsPanel::sltScrollToBottom(int iMin, int iMax)
+{
+ Q_UNUSED(iMin);
+ if (m_pScrollArea)
+ m_pScrollArea->verticalScrollBar()->setValue(iMax);
+}
+
+#include "UIFileManagerOperationsPanel.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOperationsPanel.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOperationsPanel.h
new file mode 100644
index 00000000..c104bb0c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOperationsPanel.h
@@ -0,0 +1,104 @@
+/* $Id: UIFileManagerOperationsPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIFileManagerOperationsPanel_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIFileManagerOperationsPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+# include <QUuid>
+
+/* GUI includes: */
+#include "UIGuestControlDefs.h"
+#include "UIDialogPanel.h"
+
+/* Forward declarations: */
+class CProgress;
+class QScrollArea;
+class QSpacerItem;
+class QVBoxLayout;
+
+class UIFileOperationModel;
+class UIFileOperationProgressWidget;
+class UIFileManager;
+
+
+/** UIVMLogViewerPanel extension hosting a QListWidget which in turn has a special QWidget extension
+ * to manage multiple CProgress instances. This is particulary used in monitoring file operations. */
+class UIFileManagerOperationsPanel : public UIDialogPanel
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigFileOperationComplete(QUuid progressId);
+ void sigFileOperationFail(QString strErrorString, QString strSourceTableName, FileManagerLogType eLogType);
+
+public:
+
+ UIFileManagerOperationsPanel(QWidget *pParent = 0);
+ virtual QString panelName() const RT_OVERRIDE;
+ void addNewProgress(const CProgress &comProgress, const QString &strSourceTableName);
+
+protected:
+
+ /** @name Preparation specific functions.
+ * @{ */
+ virtual void prepareWidgets() RT_OVERRIDE;
+ virtual void prepareConnections() RT_OVERRIDE;
+ /** @} */
+
+ /** Handles the translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual void contextMenuEvent(QContextMenuEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ void sltRemoveFinished();
+ void sltRemoveAll();
+ void sltRemoveSelected();
+
+ void sltHandleWidgetFocusIn(QWidget *pWidget);
+ void sltHandleWidgetFocusOut(QWidget *pWidget);
+ void sltScrollToBottom(int iMin, int iMax);
+
+private:
+
+ /** @name Member variables.
+ * @{ */
+ QScrollArea *m_pScrollArea;
+ QWidget *m_pContainerWidget;
+ QVBoxLayout *m_pContainerLayout;
+ QSpacerItem *m_pContainerSpaceItem;
+ QWidget *m_pWidgetInFocus;
+ QSet<QWidget*> m_widgetSet;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIFileManagerOperationsPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOptionsPanel.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOptionsPanel.cpp
new file mode 100644
index 00000000..a70f1116
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOptionsPanel.cpp
@@ -0,0 +1,202 @@
+/* $Id: UIFileManagerOptionsPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QCheckBox>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIFileManager.h"
+#include "UIFileManagerOptionsPanel.h"
+
+UIFileManagerOptionsPanel::UIFileManagerOptionsPanel(QWidget *pParent, UIFileManagerOptions *pFileManagerOptions)
+ : UIDialogPanel(pParent)
+ , m_pListDirectoriesOnTopCheckBox(0)
+ , m_pDeleteConfirmationCheckBox(0)
+ , m_pHumanReabableSizesCheckBox(0)
+ , m_pShowHiddenObjectsCheckBox(0)
+ , m_pFileManagerOptions(pFileManagerOptions)
+{
+ prepare();
+}
+
+QString UIFileManagerOptionsPanel::panelName() const
+{
+ return "OptionsPanel";
+}
+
+void UIFileManagerOptionsPanel::update()
+{
+ if (!m_pFileManagerOptions)
+ return;
+
+ if (m_pListDirectoriesOnTopCheckBox)
+ {
+ m_pListDirectoriesOnTopCheckBox->blockSignals(true);
+ m_pListDirectoriesOnTopCheckBox->setChecked(m_pFileManagerOptions->fListDirectoriesOnTop);
+ m_pListDirectoriesOnTopCheckBox->blockSignals(false);
+ }
+
+ if (m_pDeleteConfirmationCheckBox)
+ {
+ m_pDeleteConfirmationCheckBox->blockSignals(true);
+ m_pDeleteConfirmationCheckBox->setChecked(m_pFileManagerOptions->fAskDeleteConfirmation);
+ m_pDeleteConfirmationCheckBox->blockSignals(false);
+ }
+
+ if (m_pHumanReabableSizesCheckBox)
+ {
+ m_pHumanReabableSizesCheckBox->blockSignals(true);
+ m_pHumanReabableSizesCheckBox->setChecked(m_pFileManagerOptions->fShowHumanReadableSizes);
+ m_pHumanReabableSizesCheckBox->blockSignals(false);
+ }
+
+ if (m_pShowHiddenObjectsCheckBox)
+ {
+ m_pShowHiddenObjectsCheckBox->blockSignals(true);
+ m_pShowHiddenObjectsCheckBox->setChecked(m_pFileManagerOptions->fShowHiddenObjects);
+ m_pShowHiddenObjectsCheckBox->blockSignals(false);
+ }
+}
+
+void UIFileManagerOptionsPanel::prepareWidgets()
+{
+ if (!mainLayout())
+ return;
+
+ m_pListDirectoriesOnTopCheckBox = new QCheckBox;
+ if (m_pListDirectoriesOnTopCheckBox)
+ mainLayout()->addWidget(m_pListDirectoriesOnTopCheckBox, 0, Qt::AlignLeft);
+
+ m_pDeleteConfirmationCheckBox = new QCheckBox;
+ if (m_pDeleteConfirmationCheckBox)
+ mainLayout()->addWidget(m_pDeleteConfirmationCheckBox, 0, Qt::AlignLeft);
+
+ m_pHumanReabableSizesCheckBox = new QCheckBox;
+ if (m_pHumanReabableSizesCheckBox)
+ mainLayout()->addWidget(m_pHumanReabableSizesCheckBox, 0, Qt::AlignLeft);
+
+ m_pShowHiddenObjectsCheckBox = new QCheckBox;
+ if (m_pShowHiddenObjectsCheckBox)
+ mainLayout()->addWidget(m_pShowHiddenObjectsCheckBox, 0, Qt::AlignLeft);
+
+ /* Set initial checkbox status wrt. options: */
+ if (m_pFileManagerOptions)
+ {
+ if (m_pListDirectoriesOnTopCheckBox)
+ m_pListDirectoriesOnTopCheckBox->setChecked(m_pFileManagerOptions->fListDirectoriesOnTop);
+ if (m_pDeleteConfirmationCheckBox)
+ m_pDeleteConfirmationCheckBox->setChecked(m_pFileManagerOptions->fAskDeleteConfirmation);
+ if (m_pHumanReabableSizesCheckBox)
+ m_pHumanReabableSizesCheckBox->setChecked(m_pFileManagerOptions->fShowHumanReadableSizes);
+ if (m_pShowHiddenObjectsCheckBox)
+ m_pShowHiddenObjectsCheckBox->setChecked(m_pFileManagerOptions->fShowHiddenObjects);
+
+ }
+ retranslateUi();
+ mainLayout()->addStretch(2);
+}
+
+void UIFileManagerOptionsPanel::sltListDirectoryCheckBoxToogled(bool bChecked)
+{
+ if (!m_pFileManagerOptions)
+ return;
+ m_pFileManagerOptions->fListDirectoriesOnTop = bChecked;
+ emit sigOptionsChanged();
+}
+
+void UIFileManagerOptionsPanel::sltDeleteConfirmationCheckBoxToogled(bool bChecked)
+{
+ if (!m_pFileManagerOptions)
+ return;
+ m_pFileManagerOptions->fAskDeleteConfirmation = bChecked;
+ emit sigOptionsChanged();
+}
+
+void UIFileManagerOptionsPanel::sltHumanReabableSizesCheckBoxToogled(bool bChecked)
+{
+ if (!m_pFileManagerOptions)
+ return;
+ m_pFileManagerOptions->fShowHumanReadableSizes = bChecked;
+ emit sigOptionsChanged();
+}
+
+void UIFileManagerOptionsPanel::sltShowHiddenObjectsCheckBoxToggled(bool bChecked)
+{
+ if (!m_pFileManagerOptions)
+ return;
+ m_pFileManagerOptions->fShowHiddenObjects = bChecked;
+ emit sigOptionsChanged();
+}
+
+void UIFileManagerOptionsPanel::prepareConnections()
+{
+ if (m_pListDirectoriesOnTopCheckBox)
+ connect(m_pListDirectoriesOnTopCheckBox, &QCheckBox::toggled,
+ this, &UIFileManagerOptionsPanel::sltListDirectoryCheckBoxToogled);
+ if (m_pDeleteConfirmationCheckBox)
+ connect(m_pDeleteConfirmationCheckBox, &QCheckBox::toggled,
+ this, &UIFileManagerOptionsPanel::sltDeleteConfirmationCheckBoxToogled);
+ if (m_pHumanReabableSizesCheckBox)
+ connect(m_pHumanReabableSizesCheckBox, &QCheckBox::toggled,
+ this, &UIFileManagerOptionsPanel::sltHumanReabableSizesCheckBoxToogled);
+
+ if (m_pShowHiddenObjectsCheckBox)
+ connect(m_pShowHiddenObjectsCheckBox, &QCheckBox::toggled,
+ this, &UIFileManagerOptionsPanel::sltShowHiddenObjectsCheckBoxToggled);
+}
+
+void UIFileManagerOptionsPanel::retranslateUi()
+{
+ UIDialogPanel::retranslateUi();
+ if (m_pListDirectoriesOnTopCheckBox)
+ {
+ m_pListDirectoriesOnTopCheckBox->setText(UIFileManager::tr("List directories on top"));
+ m_pListDirectoriesOnTopCheckBox->setToolTip(UIFileManager::tr("List directories before files"));
+ }
+
+ if (m_pDeleteConfirmationCheckBox)
+ {
+ m_pDeleteConfirmationCheckBox->setText(UIFileManager::tr("Ask before delete"));
+ m_pDeleteConfirmationCheckBox->setToolTip(UIFileManager::tr("Show a confirmation dialog "
+ "before deleting files and directories"));
+ }
+
+ if (m_pHumanReabableSizesCheckBox)
+ {
+ m_pHumanReabableSizesCheckBox->setText(UIFileManager::tr("Human readable sizes"));
+ m_pHumanReabableSizesCheckBox->setToolTip(UIFileManager::tr("Show file/directory sizes in human "
+ "readable format rather than in bytes"));
+ }
+
+ if (m_pShowHiddenObjectsCheckBox)
+ {
+ m_pShowHiddenObjectsCheckBox->setText(UIFileManager::tr("Show hidden objects"));
+ m_pShowHiddenObjectsCheckBox->setToolTip(UIFileManager::tr("Show hidden files/directories"));
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOptionsPanel.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOptionsPanel.h
new file mode 100644
index 00000000..a536a2a4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerOptionsPanel.h
@@ -0,0 +1,85 @@
+/* $Id: UIFileManagerOptionsPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIFileManagerOptionsPanel_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIFileManagerOptionsPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIDialogPanel.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QLabel;
+class QIToolButton;
+class UIFileManagerOptions;
+
+/** UIDialogPanel extension to change file manager options. It directly
+ * modifies the options through the passed UIFileManagerOptions instance. */
+class UIFileManagerOptionsPanel : public UIDialogPanel
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigOptionsChanged();
+
+public:
+
+ UIFileManagerOptionsPanel(QWidget *pParent, UIFileManagerOptions *pFileManagerOptions);
+ virtual QString panelName() const RT_OVERRIDE;
+ /** Reads the file manager options and updates the widget accordingly. This functions is typically called
+ * when file manager options have been changed by other means and this panel needs to adapt. */
+ void update();
+
+protected:
+
+ virtual void prepareWidgets() RT_OVERRIDE;
+ virtual void prepareConnections() RT_OVERRIDE;
+
+ /** Handles the translation event. */
+ void retranslateUi();
+
+private slots:
+
+ void sltListDirectoryCheckBoxToogled(bool bChecked);
+ void sltDeleteConfirmationCheckBoxToogled(bool bChecked);
+ void sltHumanReabableSizesCheckBoxToogled(bool bChecked);
+ void sltShowHiddenObjectsCheckBoxToggled(bool bChecked);
+
+private:
+
+ QCheckBox *m_pListDirectoriesOnTopCheckBox;
+ QCheckBox *m_pDeleteConfirmationCheckBox;
+ QCheckBox *m_pHumanReabableSizesCheckBox;
+ QCheckBox *m_pShowHiddenObjectsCheckBox;
+ UIFileManagerOptions *m_pFileManagerOptions;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIFileManagerOptionsPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerTable.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerTable.cpp
new file mode 100644
index 00000000..885becf2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerTable.cpp
@@ -0,0 +1,1674 @@
+/* $Id: UIFileManagerTable.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFileManagerTable class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QDir>
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <QItemDelegate>
+#include <QGridLayout>
+#include <QStackedWidget>
+#include <QTextEdit>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIDialogButtonBox.h"
+#include "QILabel.h"
+#include "QILineEdit.h"
+#include "QIToolBar.h"
+#include "QIToolButton.h"
+#include "UIActionPool.h"
+#include "UICommon.h"
+#include "UICustomFileSystemModel.h"
+#include "UIErrorString.h"
+#include "UIFileManager.h"
+#include "UIFileManagerGuestTable.h"
+#include "UIFileManagerTable.h"
+#include "UIIconPool.h"
+#include "UIPathOperations.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "CFsObjInfo.h"
+#include "CGuestFsObjInfo.h"
+#include "CGuestDirectory.h"
+#include "CProgress.h"
+
+
+/*********************************************************************************************************************************
+* UIFileManagerHistoryComboBox definition. *
+*********************************************************************************************************************************/
+/** A QCombo extension used as location history list in the UIFileManagerNavigationWidget. */
+class UIFileManagerHistoryComboBox : public QComboBox
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigHidePopup();
+
+public:
+
+ UIFileManagerHistoryComboBox(QWidget *pParent = 0);
+ /** Emit sigHidePopup as the popup is hidded. */
+ virtual void hidePopup() RT_OVERRIDE;
+};
+
+
+/*********************************************************************************************************************************
+* UIFileManagerBreadCrumbs definition. *
+*********************************************************************************************************************************/
+/** A QLabel extension. It shows the current path as text and hightligts the folder name
+ * as the mouse hovers over it. Clicking on the highlighted folder name make the file table to
+ * navigate to that folder. */
+class UIFileManagerBreadCrumbs : public QLabel
+{
+ Q_OBJECT;
+
+public:
+
+ UIFileManagerBreadCrumbs(QWidget *pParent = 0);
+ void setPath(const QString &strPath);
+ void setPathSeparator(const QChar &separator);
+
+protected:
+
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ QString m_strPath;
+ QChar m_pathSeparator;
+};
+
+
+/*********************************************************************************************************************************
+* UIFileManagerNavigationWidget definition. *
+*********************************************************************************************************************************/
+/** UIFileManagerNavigationWidget contains a UIFileManagerBreadCrumbs, QComboBox for history and a QToolButton.
+ * basically it is a container for these mentioned widgets. */
+class UIFileManagerNavigationWidget : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigPathChanged(const QString &strPath);
+
+public:
+
+ UIFileManagerNavigationWidget(QWidget *pParent = 0);
+ void setPath(const QString &strLocation);
+ void reset();
+ void setPathSeparator(const QChar &separator);
+ bool eventFilter(QObject *pObject, QEvent *pEvent) override;
+
+private slots:
+ void sltHandleSwitch();
+ /* Makes sure that we switch to breadcrumbs widget as soon as the combo box popup is hidden. */
+ void sltHandleHidePopup();
+ void sltHandlePathChange(const QString &strPath);
+ void sltAddressLineEdited();
+
+private:
+
+ enum StackedWidgets
+ {
+ StackedWidgets_History = 0,
+ StackedWidgets_BreadCrumbs,
+ StackedWidgets_AddressLine
+ };
+
+ void prepare();
+
+ QStackedWidget *m_pContainer;
+ UIFileManagerBreadCrumbs *m_pBreadCrumbs;
+ UIFileManagerHistoryComboBox *m_pHistoryComboBox;
+ QLineEdit *m_pAddressLineEdit;
+ QToolButton *m_pSwitchButton;
+ QChar m_pathSeparator;
+ /* With non-native separators. */
+ QString m_strCurrentPath;
+};
+
+
+/*********************************************************************************************************************************
+* UIGuestControlFileView definition. *
+*********************************************************************************************************************************/
+
+/** Using QITableView causes the following problem when I click on the table items
+ Qt WARNING: Cannot creat accessible child interface for object: UIGuestControlFileView.....
+ so for now subclass QTableView */
+class UIGuestControlFileView : public QTableView
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected);
+
+public:
+
+ UIGuestControlFileView(QWidget * parent = 0);
+ bool hasSelection() const;
+ bool isInEditState() const;
+
+protected:
+
+ virtual void selectionChanged(const QItemSelection & selected, const QItemSelection & deselected) /*override */;
+
+private:
+
+ void configure();
+ QWidget *m_pParent;
+};
+
+
+/*********************************************************************************************************************************
+* UIFileDelegate definition. *
+*********************************************************************************************************************************/
+/** A QItemDelegate child class to disable dashed lines drawn around selected cells in QTableViews */
+class UIFileDelegate : public QItemDelegate
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIFileDelegate(QObject *pParent)
+ : QItemDelegate(pParent){}
+
+protected:
+
+ virtual void drawFocus ( QPainter * /*painter*/, const QStyleOptionViewItem & /*option*/, const QRect & /*rect*/ ) const {}
+};
+
+
+/*********************************************************************************************************************************
+* UStringInputDialog definition. *
+*********************************************************************************************************************************/
+
+/** A QIDialog child including a line edit whose text exposed when the dialog is accepted */
+class UIStringInputDialog : public QIDialog
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIStringInputDialog(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+ QString getString() const;
+
+private:
+
+ QILineEdit *m_pLineEdit;
+};
+
+
+/*********************************************************************************************************************************
+* UIFileDeleteConfirmationDialog definition. *
+*********************************************************************************************************************************/
+
+/** A QIDialog child including a line edit whose text exposed when the dialog is accepted */
+class UIFileDeleteConfirmationDialog : public QIDialog
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIFileDeleteConfirmationDialog(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+ /** Returns whether m_pAskNextTimeCheckBox is checked or not. */
+ bool askDeleteConfirmationNextTime() const;
+
+private:
+
+ QCheckBox *m_pAskNextTimeCheckBox;
+ QILabel *m_pQuestionLabel;
+};
+
+
+/*********************************************************************************************************************************
+* UIFileManagerHistoryComboBox implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerHistoryComboBox::UIFileManagerHistoryComboBox(QWidget *pParent /* = 0 */)
+ :QComboBox(pParent)
+{
+
+}
+
+void UIFileManagerHistoryComboBox::hidePopup()
+{
+ QComboBox::hidePopup();
+ emit sigHidePopup();
+}
+
+
+/*********************************************************************************************************************************
+* UIFileManagerNavigationWidget implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerNavigationWidget::UIFileManagerNavigationWidget(QWidget *pParent /* = 0 */)
+ :QWidget(pParent)
+ , m_pContainer(0)
+ , m_pBreadCrumbs(0)
+ , m_pHistoryComboBox(0)
+ , m_pAddressLineEdit(0)
+ , m_pSwitchButton(0)
+ , m_pathSeparator('/')
+{
+ prepare();
+}
+
+void UIFileManagerNavigationWidget::setPath(const QString &strLocation)
+{
+ if (m_strCurrentPath == QDir::fromNativeSeparators(strLocation))
+ return;
+
+ m_strCurrentPath = QDir::fromNativeSeparators(strLocation);
+
+ if (m_pBreadCrumbs)
+ m_pBreadCrumbs->setPath(strLocation);
+
+ if (m_pHistoryComboBox)
+ {
+ QString strNativeLocation(strLocation);
+ strNativeLocation.replace('/', m_pathSeparator);
+ int itemIndex = m_pHistoryComboBox->findText(strNativeLocation,
+ Qt::MatchExactly | Qt::MatchCaseSensitive);
+ if (itemIndex == -1)
+ {
+ m_pHistoryComboBox->insertItem(m_pHistoryComboBox->count(), strNativeLocation);
+ itemIndex = m_pHistoryComboBox->count() - 1;
+ }
+ m_pHistoryComboBox->setCurrentIndex(itemIndex);
+ }
+}
+
+void UIFileManagerNavigationWidget::reset()
+{
+ if (m_pHistoryComboBox)
+ {
+ m_pHistoryComboBox->blockSignals(true);
+ m_pHistoryComboBox->clear();
+ m_pHistoryComboBox->blockSignals(false);
+ }
+
+ if (m_pBreadCrumbs)
+ m_pBreadCrumbs->setPath(QString());
+}
+
+void UIFileManagerNavigationWidget::setPathSeparator(const QChar &separator)
+{
+ m_pathSeparator = separator;
+ if (m_pBreadCrumbs)
+ m_pBreadCrumbs->setPathSeparator(m_pathSeparator);
+}
+
+void UIFileManagerNavigationWidget::prepare()
+{
+ QHBoxLayout *pLayout = new QHBoxLayout;
+ if (!pLayout)
+ return;
+ pLayout->setSpacing(0);
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pContainer = new QStackedWidget;
+ if (m_pContainer)
+ {
+ m_pBreadCrumbs = new UIFileManagerBreadCrumbs;
+ m_pHistoryComboBox = new UIFileManagerHistoryComboBox;
+ m_pAddressLineEdit = new QLineEdit;
+ if (m_pBreadCrumbs && m_pHistoryComboBox)
+ {
+ m_pBreadCrumbs->setIndent(0.5 * qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin));
+ m_pBreadCrumbs->installEventFilter(this);
+ m_pAddressLineEdit->installEventFilter(this);
+ connect(m_pBreadCrumbs, &UIFileManagerBreadCrumbs::linkActivated,
+ this, &UIFileManagerNavigationWidget::sltHandlePathChange);
+ connect(m_pHistoryComboBox, &UIFileManagerHistoryComboBox::sigHidePopup,
+ this, &UIFileManagerNavigationWidget::sltHandleHidePopup);
+ connect(m_pHistoryComboBox, &UIFileManagerHistoryComboBox::currentTextChanged,
+ this, &UIFileManagerNavigationWidget::sltHandlePathChange);
+ connect(m_pAddressLineEdit, &QLineEdit::returnPressed,
+ this, &UIFileManagerNavigationWidget::sltAddressLineEdited);
+ m_pContainer->insertWidget(StackedWidgets_BreadCrumbs, m_pBreadCrumbs);
+ m_pContainer->insertWidget(StackedWidgets_History, m_pHistoryComboBox);
+ m_pContainer->insertWidget(StackedWidgets_AddressLine, m_pAddressLineEdit);
+ m_pContainer->setCurrentIndex(StackedWidgets_BreadCrumbs);
+ }
+ pLayout->addWidget(m_pContainer);
+ }
+
+ m_pSwitchButton = new QToolButton;
+ if (m_pSwitchButton)
+ {
+ QStyle *pStyle = QApplication::style();
+ QIcon buttonIcon;
+ if (pStyle)
+ {
+ buttonIcon = pStyle->standardIcon(QStyle::SP_TitleBarUnshadeButton);
+ m_pSwitchButton->setIcon(buttonIcon);
+ }
+ pLayout->addWidget(m_pSwitchButton);
+ connect(m_pSwitchButton, &QToolButton::clicked,
+ this, &UIFileManagerNavigationWidget::sltHandleSwitch);
+ }
+ setLayout(pLayout);
+}
+
+bool UIFileManagerNavigationWidget::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ if (pObject == m_pBreadCrumbs && pEvent && pEvent->type() == QEvent::MouseButtonDblClick)
+ {
+ m_pContainer->setCurrentIndex(StackedWidgets_AddressLine);
+ m_pAddressLineEdit->setText(QDir::toNativeSeparators(m_strCurrentPath));
+ m_pAddressLineEdit->setFocus();
+
+ }
+ else if(pObject == m_pAddressLineEdit && pEvent && pEvent->type() == QEvent::FocusOut)
+ m_pContainer->setCurrentIndex(StackedWidgets_BreadCrumbs);
+
+ return QWidget::eventFilter(pObject, pEvent);
+}
+
+void UIFileManagerNavigationWidget::sltHandleHidePopup()
+{
+ m_pContainer->setCurrentIndex(StackedWidgets_BreadCrumbs);
+}
+
+void UIFileManagerNavigationWidget::sltHandlePathChange(const QString &strPath)
+{
+ emit sigPathChanged(QDir::fromNativeSeparators(strPath));
+}
+
+void UIFileManagerNavigationWidget::sltHandleSwitch()
+{
+ if (m_pContainer->currentIndex() == StackedWidgets_BreadCrumbs)
+ {
+ m_pContainer->setCurrentIndex(StackedWidgets_History);
+ m_pHistoryComboBox->showPopup();
+ }
+ else
+ {
+ m_pContainer->setCurrentIndex(StackedWidgets_BreadCrumbs);
+ m_pHistoryComboBox->hidePopup();
+ }
+}
+
+void UIFileManagerNavigationWidget::sltAddressLineEdited()
+{
+ sigPathChanged(QDir::fromNativeSeparators(m_pAddressLineEdit->text()));
+}
+
+
+/*********************************************************************************************************************************
+* UIFileManagerBreadCrumbs implementation. *
+*********************************************************************************************************************************/
+
+UIFileManagerBreadCrumbs::UIFileManagerBreadCrumbs(QWidget *pParent /* = 0 */)
+ :QLabel(pParent)
+ , m_pathSeparator('/')
+{
+ float fFontMult = 1.f;
+ QFont mFont = font();
+ if (mFont.pixelSize() == -1)
+ mFont.setPointSize(fFontMult * mFont.pointSize());
+ else
+ mFont.setPixelSize(fFontMult * mFont.pixelSize());
+ setFont(mFont);
+
+ setFrameShape(QFrame::Box);
+ setLineWidth(1);
+ setAutoFillBackground(true);
+ QPalette pal = QApplication::palette();
+ pal.setColor(QPalette::Active, QPalette::Window, pal.color(QPalette::Active, QPalette::Base));
+ setPalette(pal);
+ /* Allow the label become smaller than the current text. calling setpath in resizeEvent truncated the text anyway: */
+ setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+}
+
+void UIFileManagerBreadCrumbs::setPath(const QString &strPath)
+{
+ m_strPath = strPath;
+
+ const QChar separator('/');
+ clear();
+
+ if (strPath.isEmpty())
+ return;
+
+ QStringList folderList = UIPathOperations::pathTrail(strPath);
+ folderList.push_front(separator);
+
+ QString strLabelText;
+ QVector<QString> strPathUpto;
+ strPathUpto.resize(folderList.size());
+
+ for (int i = 0; i < folderList.size(); ++i)
+ {
+ QString strFolder = UIPathOperations::removeTrailingDelimiters(folderList.at(i));
+ if (i != 0)
+ strPathUpto[i] = strPathUpto[i - 1];
+ if (i == 0 || i == folderList.size() - 1)
+ strPathUpto[i].append(QString("%1").arg(strFolder));
+ else
+ strPathUpto[i].append(QString("%1%2").arg(strFolder).arg(separator));
+ }
+
+ int iWidth = 0;
+ for (int i = folderList.size() - 1; i >= 0; --i)
+ {
+ QString strFolder = UIPathOperations::removeTrailingDelimiters(folderList.at(i)).replace('/', m_pathSeparator);
+ QString strWord = QString("<a href=\"%1\" style=\"color:black;text-decoration:none;\">%2</a>").arg(strPathUpto[i]).arg(strFolder);
+
+ if (i < folderList.size() - 1)
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ iWidth += fontMetrics().horizontalAdvance(" > ");
+#else
+ iWidth += fontMetrics().width(" > ");
+#endif
+ strWord.append("<b> > </b>");
+ }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ iWidth += fontMetrics().horizontalAdvance(strFolder);
+#else
+ iWidth += fontMetrics().width(strFolder);
+#endif
+
+ if (iWidth < width())
+ strLabelText.prepend(strWord);
+ }
+ setText(strLabelText);
+}
+
+void UIFileManagerBreadCrumbs::setPathSeparator(const QChar &separator)
+{
+ m_pathSeparator = separator;
+}
+
+void UIFileManagerBreadCrumbs::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Truncate the text the way we want: */
+ setPath(m_strPath);
+ QLabel::resizeEvent(pEvent);
+}
+
+
+/*********************************************************************************************************************************
+* UIHostDirectoryDiskUsageComputer implementation. *
+*********************************************************************************************************************************/
+
+UIDirectoryDiskUsageComputer::UIDirectoryDiskUsageComputer(QObject *parent, QStringList pathList)
+ :QThread(parent)
+ , m_pathList(pathList)
+ , m_fOkToContinue(true)
+{
+}
+
+void UIDirectoryDiskUsageComputer::run()
+{
+ for (int i = 0; i < m_pathList.size(); ++i)
+ directoryStatisticsRecursive(m_pathList[i], m_resultStatistics);
+}
+
+void UIDirectoryDiskUsageComputer::stopRecursion()
+{
+ m_mutex.lock();
+ m_fOkToContinue = false;
+ m_mutex.unlock();
+}
+
+bool UIDirectoryDiskUsageComputer::isOkToContinue() const
+{
+ return m_fOkToContinue;
+}
+
+
+/*********************************************************************************************************************************
+* UIGuestControlFileView implementation. *
+*********************************************************************************************************************************/
+
+UIGuestControlFileView::UIGuestControlFileView(QWidget *parent)
+ :QTableView(parent)
+ , m_pParent(parent)
+{
+ configure();
+}
+
+void UIGuestControlFileView::configure()
+{
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ setShowGrid(false);
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ verticalHeader()->setVisible(false);
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
+ /* Minimize the row height: */
+ verticalHeader()->setDefaultSectionSize(verticalHeader()->minimumSectionSize());
+ setAlternatingRowColors(true);
+ installEventFilter(m_pParent);
+}
+
+bool UIGuestControlFileView::hasSelection() const
+{
+ QItemSelectionModel *pSelectionModel = selectionModel();
+ if (!pSelectionModel)
+ return false;
+ return pSelectionModel->hasSelection();
+}
+
+bool UIGuestControlFileView::isInEditState() const
+{
+ return state() == QAbstractItemView::EditingState;
+}
+
+void UIGuestControlFileView::selectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
+{
+ emit sigSelectionChanged(selected, deselected);
+ QTableView::selectionChanged(selected, deselected);
+}
+
+
+/*********************************************************************************************************************************
+* UIFileStringInputDialog implementation. *
+*********************************************************************************************************************************/
+
+UIStringInputDialog::UIStringInputDialog(QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ :QIDialog(pParent, enmFlags)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ m_pLineEdit = new QILineEdit(this);
+ layout->addWidget(m_pLineEdit);
+
+ QIDialogButtonBox *pButtonBox =
+ new QIDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
+ layout->addWidget(pButtonBox);
+ connect(pButtonBox, &QIDialogButtonBox::accepted, this, &UIStringInputDialog::accept);
+ connect(pButtonBox, &QIDialogButtonBox::rejected, this, &UIStringInputDialog::reject);
+}
+
+QString UIStringInputDialog::getString() const
+{
+ if (!m_pLineEdit)
+ return QString();
+ return m_pLineEdit->text();
+}
+
+
+/*********************************************************************************************************************************
+* UIPropertiesDialog implementation. *
+*********************************************************************************************************************************/
+
+UIPropertiesDialog::UIPropertiesDialog(QWidget *pParent, Qt::WindowFlags enmFlags)
+ :QIDialog(pParent, enmFlags)
+ , m_pMainLayout(new QVBoxLayout)
+ , m_pInfoEdit(new QTextEdit)
+{
+ setLayout(m_pMainLayout);
+
+ if (m_pMainLayout)
+ m_pMainLayout->addWidget(m_pInfoEdit);
+ if (m_pInfoEdit)
+ {
+ m_pInfoEdit->setReadOnly(true);
+ m_pInfoEdit->setFrameStyle(QFrame::NoFrame);
+ }
+ QIDialogButtonBox *pButtonBox =
+ new QIDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, this);
+ m_pMainLayout->addWidget(pButtonBox);
+ connect(pButtonBox, &QIDialogButtonBox::accepted, this, &UIStringInputDialog::accept);
+}
+
+void UIPropertiesDialog::setPropertyText(const QString &strProperty)
+{
+ if (!m_pInfoEdit)
+ return;
+ m_strProperty = strProperty;
+ m_pInfoEdit->setHtml(strProperty);
+}
+
+void UIPropertiesDialog::addDirectoryStatistics(UIDirectoryStatistics directoryStatistics)
+{
+ if (!m_pInfoEdit)
+ return;
+ // QString propertyString = m_pInfoEdit->toHtml();
+ // propertyString += "<b>Total Size:</b> " + QString::number(directoryStatistics.m_totalSize) + QString(" bytes");
+ // if (directoryStatistics.m_totalSize >= UIFileManagerTable::m_iKiloByte)
+ // propertyString += " (" + UIFileManagerTable::humanReadableSize(directoryStatistics.m_totalSize) + ")";
+ // propertyString += "<br/>";
+ // propertyString += "<b>File Count:</b> " + QString::number(directoryStatistics.m_uFileCount);
+
+ // m_pInfoEdit->setHtml(propertyString);
+
+ QString detailsString(m_strProperty);
+ detailsString += "<br/>";
+ detailsString += "<b>" + UIFileManager::tr("Total Size") + "</b> " +
+ QString::number(directoryStatistics.m_totalSize) + UIFileManager::tr(" bytes");
+ if (directoryStatistics.m_totalSize >= UIFileManagerTable::m_iKiloByte)
+ detailsString += " (" + UIFileManagerTable::humanReadableSize(directoryStatistics.m_totalSize) + ")";
+ detailsString += "<br/>";
+
+ detailsString += "<b>" + UIFileManager::tr("File Count") + ":</b> " +
+ QString::number(directoryStatistics.m_uFileCount);
+
+ m_pInfoEdit->setHtml(detailsString);
+}
+
+
+/*********************************************************************************************************************************
+* UIDirectoryStatistics implementation. *
+*********************************************************************************************************************************/
+
+UIDirectoryStatistics::UIDirectoryStatistics()
+ : m_totalSize(0)
+ , m_uFileCount(0)
+ , m_uDirectoryCount(0)
+ , m_uSymlinkCount(0)
+{
+}
+
+/*********************************************************************************************************************************
++* UIFileDeleteConfirmationDialog implementation. *
++*********************************************************************************************************************************/
+
+UIFileDeleteConfirmationDialog::UIFileDeleteConfirmationDialog(QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ :QIDialog(pParent, enmFlags)
+ , m_pAskNextTimeCheckBox(0)
+ , m_pQuestionLabel(0)
+{
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+
+ m_pQuestionLabel = new QILabel;
+ if (m_pQuestionLabel)
+ {
+ pLayout->addWidget(m_pQuestionLabel);
+ m_pQuestionLabel->setText(UIFileManager::tr("Delete the selected file(s) and/or folder(s)"));
+ }
+
+ QIDialogButtonBox *pButtonBox =
+ new QIDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
+ if (pButtonBox)
+ {
+ pLayout->addWidget(pButtonBox, 0, Qt::AlignCenter);
+ connect(pButtonBox, &QIDialogButtonBox::accepted, this, &UIStringInputDialog::accept);
+ connect(pButtonBox, &QIDialogButtonBox::rejected, this, &UIStringInputDialog::reject);
+ }
+
+ m_pAskNextTimeCheckBox = new QCheckBox;
+
+ if (m_pAskNextTimeCheckBox)
+ {
+ UIFileManagerOptions *pFileManagerOptions = UIFileManagerOptions::instance();
+ if (pFileManagerOptions)
+ m_pAskNextTimeCheckBox->setChecked(pFileManagerOptions->fAskDeleteConfirmation);
+
+ pLayout->addWidget(m_pAskNextTimeCheckBox);
+ m_pAskNextTimeCheckBox->setText(UIFileManager::tr("Ask for this confirmation next time"));
+ m_pAskNextTimeCheckBox->setToolTip(UIFileManager::tr("Delete confirmation can be "
+ "disabled/enabled also from the Options panel."));
+ }
+}
+
+bool UIFileDeleteConfirmationDialog::askDeleteConfirmationNextTime() const
+{
+ if (!m_pAskNextTimeCheckBox)
+ return true;
+ return m_pAskNextTimeCheckBox->isChecked();
+}
+
+
+/*********************************************************************************************************************************
+* UIFileManagerTable implementation. *
+*********************************************************************************************************************************/
+const unsigned UIFileManagerTable::m_iKiloByte = 1024; /**< Our kilo bytes are a power of two! (bird) */
+
+UIFileManagerTable::UIFileManagerTable(UIActionPool *pActionPool, QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_eFileOperationType(FileOperationType_None)
+ , m_pLocationLabel(0)
+ , m_pPropertiesDialog(0)
+ , m_pActionPool(pActionPool)
+ , m_pToolBar(0)
+ , m_pMainLayout(0)
+ , m_pModel(0)
+ , m_pView(0)
+ , m_pProxyModel(0)
+ , m_pNavigationWidget(0)
+ , m_pathSeparator('/')
+ , m_pToolBarLayout(0)
+{
+ prepareObjects();
+}
+
+UIFileManagerTable::~UIFileManagerTable()
+{
+}
+
+void UIFileManagerTable::reset()
+{
+ if (m_pModel)
+ m_pModel->reset();
+
+ if (m_pNavigationWidget)
+ m_pNavigationWidget->reset();
+}
+
+void UIFileManagerTable::prepareObjects()
+{
+ m_pMainLayout = new QGridLayout();
+ if (!m_pMainLayout)
+ return;
+ m_pMainLayout->setSpacing(0);
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+ setLayout(m_pMainLayout);
+
+ m_pToolBarLayout = new QHBoxLayout;
+ if (m_pToolBarLayout)
+ {
+ m_pToolBarLayout->setSpacing(0);
+ m_pToolBarLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pToolBar = new QIToolBar;
+ if (m_pToolBar)
+ {
+ m_pToolBarLayout->addWidget(m_pToolBar);
+ m_sessionWidgets << m_pToolBar;
+ }
+
+ m_pMainLayout->addLayout(m_pToolBarLayout, 0, 0, 1, 7);
+ }
+
+ m_pLocationLabel = new QILabel;
+ if (m_pLocationLabel)
+ {
+ m_pMainLayout->addWidget(m_pLocationLabel, 1, 0, 1, 1);
+ m_sessionWidgets << m_pLocationLabel;
+ }
+
+ m_pNavigationWidget = new UIFileManagerNavigationWidget;
+ if (m_pNavigationWidget)
+ {
+ m_pNavigationWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
+ connect(m_pNavigationWidget, &UIFileManagerNavigationWidget::sigPathChanged,
+ this, &UIFileManagerTable::sltHandleNavigationWidgetPathChange);
+ m_pMainLayout->addWidget(m_pNavigationWidget, 1, 1, 1, 6);
+ m_sessionWidgets << m_pNavigationWidget;
+ }
+
+ m_pModel = new UICustomFileSystemModel(this);
+ if (!m_pModel)
+ return;
+ connect(m_pModel, &UICustomFileSystemModel::sigItemRenamed,
+ this, &UIFileManagerTable::sltHandleItemRenameAttempt);
+
+ m_pProxyModel = new UICustomFileSystemProxyModel(this);
+ if (!m_pProxyModel)
+ return;
+ m_pProxyModel->setSourceModel(m_pModel);
+
+ m_pView = new UIGuestControlFileView(this);
+ if (m_pView)
+ {
+ m_pView->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
+ m_pMainLayout->addWidget(m_pView, 2, 0, 5, 7);
+
+ QHeaderView *pHorizontalHeader = m_pView->horizontalHeader();
+ if (pHorizontalHeader)
+ {
+ pHorizontalHeader->setHighlightSections(false);
+ pHorizontalHeader->setSectionResizeMode(QHeaderView::ResizeToContents);
+ pHorizontalHeader->setStretchLastSection(true);
+ }
+
+ m_pView->setModel(m_pProxyModel);
+ m_pView->setItemDelegate(new UIFileDelegate(this));
+ m_pView->setSortingEnabled(true);
+ m_pView->sortByColumn(0, Qt::AscendingOrder);
+
+ connect(m_pView, &UIGuestControlFileView::doubleClicked,
+ this, &UIFileManagerTable::sltItemDoubleClicked);
+ connect(m_pView, &UIGuestControlFileView::clicked,
+ this, &UIFileManagerTable::sltItemClicked);
+ connect(m_pView, &UIGuestControlFileView::sigSelectionChanged,
+ this, &UIFileManagerTable::sltSelectionChanged);
+ connect(m_pView, &UIGuestControlFileView::customContextMenuRequested,
+ this, &UIFileManagerTable::sltCreateFileViewContextMenu);
+ m_pView->hideColumn(UICustomFileSystemModelColumn_Path);
+ m_pView->hideColumn(UICustomFileSystemModelColumn_LocalPath);
+ m_sessionWidgets << m_pView;
+ }
+
+ m_pSearchLineEdit = new QILineEdit;
+ if (m_pSearchLineEdit)
+ {
+ m_pMainLayout->addWidget(m_pSearchLineEdit, 8, 0, 1, 7);
+ m_pSearchLineEdit->hide();
+ m_pSearchLineEdit->setClearButtonEnabled(true);
+ m_searchLineUnmarkColor = m_pSearchLineEdit->palette().color(QPalette::Base);
+ m_searchLineMarkColor = QColor(m_searchLineUnmarkColor.green(),
+ 0.5 * m_searchLineUnmarkColor.green(),
+ 0.5 * m_searchLineUnmarkColor.blue());
+ connect(m_pSearchLineEdit, &QLineEdit::textChanged,
+ this, &UIFileManagerTable::sltSearchTextChanged);
+ }
+ optionsUpdated();
+}
+
+void UIFileManagerTable::updateCurrentLocationEdit(const QString& strLocation)
+{
+ if (m_pNavigationWidget)
+ {
+ m_pNavigationWidget->setPath(strLocation);
+ }
+}
+
+void UIFileManagerTable::changeLocation(const QModelIndex &index)
+{
+ if (!index.isValid() || !m_pView)
+ return;
+ m_pView->setRootIndex(m_pProxyModel->mapFromSource(index));
+
+ if (m_pView->selectionModel())
+ m_pView->selectionModel()->reset();
+
+ UICustomFileSystemItem *item = static_cast<UICustomFileSystemItem*>(index.internalPointer());
+ if (item)
+ {
+ updateCurrentLocationEdit(item->path());
+ }
+ setSelectionDependentActionsEnabled(false);
+
+ m_pView->scrollToTop();
+}
+
+void UIFileManagerTable::initializeFileTree()
+{
+ if (m_pModel)
+ m_pModel->reset();
+ if (!rootItem())
+ return;
+
+ const QString startPath("/");
+ UICustomFileSystemItem* startItem = new UICustomFileSystemItem(startPath, rootItem(), KFsObjType_Directory);
+ startItem->setPath(startPath);
+ startItem->setIsOpened(false);
+ populateStartDirectory(startItem);
+
+ m_pModel->signalUpdate();
+ updateCurrentLocationEdit(startPath);
+ m_pView->setRootIndex(m_pProxyModel->mapFromSource(m_pModel->rootIndex()));
+}
+
+void UIFileManagerTable::populateStartDirectory(UICustomFileSystemItem *startItem)
+{
+ determineDriveLetters();
+ if (m_driveLetterList.isEmpty())
+ {
+ /* Read the root directory and get the list: */
+ readDirectory(startItem->path(), startItem, true);
+ }
+ else
+ {
+ for (int i = 0; i < m_driveLetterList.size(); ++i)
+ {
+ UICustomFileSystemItem* driveItem = new UICustomFileSystemItem(UIPathOperations::removeTrailingDelimiters(m_driveLetterList[i]),
+ startItem, KFsObjType_Directory);
+ driveItem->setPath(m_driveLetterList[i]);
+ driveItem->setIsOpened(false);
+ driveItem->setIsDriveItem(true);
+ startItem->setIsOpened(true);
+ }
+ }
+}
+
+void UIFileManagerTable::checkDotDot(QMap<QString,UICustomFileSystemItem*> &map,
+ UICustomFileSystemItem *parent, bool isStartDir)
+{
+ if (!parent)
+ return;
+ /* Make sure we have an item representing up directory, and make sure it is not there for the start dir: */
+ if (!map.contains(UICustomFileSystemModel::strUpDirectoryString) && !isStartDir)
+ {
+ UICustomFileSystemItem *item = new UICustomFileSystemItem(UICustomFileSystemModel::strUpDirectoryString,
+ parent, KFsObjType_Directory);
+ item->setIsOpened(false);
+ map.insert(UICustomFileSystemModel::strUpDirectoryString, item);
+ }
+ else if (map.contains(UICustomFileSystemModel::strUpDirectoryString) && isStartDir)
+ {
+ map.remove(UICustomFileSystemModel::strUpDirectoryString);
+ }
+}
+
+void UIFileManagerTable::sltItemDoubleClicked(const QModelIndex &index)
+{
+ if (!index.isValid() || !m_pModel || !m_pView)
+ return;
+ QModelIndex nIndex = m_pProxyModel ? m_pProxyModel->mapToSource(index) : index;
+ goIntoDirectory(nIndex);
+}
+
+void UIFileManagerTable::sltItemClicked(const QModelIndex &index)
+{
+ Q_UNUSED(index);
+ disableSelectionSearch();
+}
+
+void UIFileManagerTable::sltGoUp()
+{
+ if (!m_pView || !m_pModel)
+ return;
+ QModelIndex currentRoot = currentRootIndex();
+
+ if (!currentRoot.isValid())
+ return;
+ if (currentRoot != m_pModel->rootIndex())
+ {
+ QModelIndex parentIndex = currentRoot.parent();
+ if (parentIndex.isValid())
+ {
+ changeLocation(currentRoot.parent());
+ m_pView->selectRow(currentRoot.row());
+ }
+ }
+}
+
+void UIFileManagerTable::sltGoHome()
+{
+ goToHomeDirectory();
+}
+
+void UIFileManagerTable::sltRefresh()
+{
+ refresh();
+}
+
+void UIFileManagerTable::goIntoDirectory(const QModelIndex &itemIndex)
+{
+ if (!m_pModel)
+ return;
+
+ /* Make sure the colum is 0: */
+ QModelIndex index = m_pModel->index(itemIndex.row(), 0, itemIndex.parent());
+ if (!index.isValid())
+ return;
+
+ UICustomFileSystemItem *item = static_cast<UICustomFileSystemItem*>(index.internalPointer());
+ if (!item)
+ return;
+
+ /* check if we need to go up: */
+ if (item->isUpDirectory())
+ {
+ QModelIndex parentIndex = m_pModel->parent(m_pModel->parent(index));
+ if (parentIndex.isValid())
+ changeLocation(parentIndex);
+ return;
+ }
+
+ if (item->isDirectory() || item->isSymLinkToADirectory())
+ {
+ if (!item->isOpened())
+ readDirectory(item->path(),item);
+ changeLocation(index);
+ }
+}
+
+void UIFileManagerTable::goIntoDirectory(const QStringList &pathTrail)
+{
+ UICustomFileSystemItem *parent = getStartDirectoryItem();
+
+ for(int i = 0; i < pathTrail.size(); ++i)
+ {
+ if (!parent)
+ return;
+ /* Make sure parent is already opened: */
+ if (!parent->isOpened())
+ readDirectory(parent->path(), parent, parent == getStartDirectoryItem());
+ /* search the current path item among the parent's children: */
+ UICustomFileSystemItem *item = parent->child(pathTrail.at(i));
+ if (!item)
+ return;
+ parent = item;
+ }
+ if (!parent)
+ return;
+ if (!parent->isOpened())
+ readDirectory(parent->path(), parent, parent == getStartDirectoryItem());
+ goIntoDirectory(parent);
+}
+
+void UIFileManagerTable::goIntoDirectory(UICustomFileSystemItem *item)
+{
+ if (!item || !m_pModel)
+ return;
+ goIntoDirectory(m_pModel->index(item));
+}
+
+UICustomFileSystemItem* UIFileManagerTable::indexData(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return 0;
+ return static_cast<UICustomFileSystemItem*>(index.internalPointer());
+}
+
+void UIFileManagerTable::refresh()
+{
+ if (!m_pView || !m_pModel)
+ return;
+ QModelIndex currentIndex = currentRootIndex();
+
+ UICustomFileSystemItem *treeItem = indexData(currentIndex);
+ if (!treeItem)
+ return;
+ bool isRootDir = (m_pModel->rootIndex() == currentIndex);
+ m_pModel->beginReset();
+ /* For now we clear the whole subtree (that isrecursively) which is an overkill: */
+ treeItem->clearChildren();
+ if (isRootDir)
+ populateStartDirectory(treeItem);
+ else
+ readDirectory(treeItem->path(), treeItem, isRootDir);
+ m_pModel->endReset();
+ m_pView->setRootIndex(m_pProxyModel->mapFromSource(currentIndex));
+ setSelectionDependentActionsEnabled(m_pView->hasSelection());
+}
+
+void UIFileManagerTable::relist()
+{
+ if (!m_pProxyModel)
+ return;
+ m_pProxyModel->invalidate();
+}
+
+void UIFileManagerTable::sltDelete()
+{
+ if (!checkIfDeleteOK())
+ return;
+
+ if (!m_pView || !m_pModel)
+ return;
+
+ if (!m_pView || !m_pModel)
+ return;
+ QItemSelectionModel *selectionModel = m_pView->selectionModel();
+ if (!selectionModel)
+ return;
+
+ QModelIndexList selectedItemIndices = selectionModel->selectedRows();
+ for(int i = 0; i < selectedItemIndices.size(); ++i)
+ {
+ QModelIndex index =
+ m_pProxyModel ? m_pProxyModel->mapToSource(selectedItemIndices.at(i)) : selectedItemIndices.at(i);
+ deleteByIndex(index);
+ }
+ /** @todo dont refresh here, just delete the rows and update the table view: */
+ refresh();
+}
+
+void UIFileManagerTable::sltRename()
+{
+ if (!m_pView || !m_pModel)
+ return;
+ QItemSelectionModel *selectionModel = m_pView->selectionModel();
+ if (!selectionModel)
+ return;
+
+ QModelIndexList selectedItemIndices = selectionModel->selectedRows();
+ if (selectedItemIndices.size() == 0)
+ return;
+ QModelIndex modelIndex =
+ m_pProxyModel ? m_pProxyModel->mapToSource(selectedItemIndices.at(0)) : selectedItemIndices.at(0);
+ UICustomFileSystemItem *item = indexData(modelIndex);
+ if (!item || item->isUpDirectory())
+ return;
+ m_pView->edit(selectedItemIndices.at(0));
+}
+
+void UIFileManagerTable::sltCreateNewDirectory()
+{
+ if (!m_pModel || !m_pView)
+ return;
+ QModelIndex currentIndex = currentRootIndex();
+ if (!currentIndex.isValid())
+ return;
+ UICustomFileSystemItem *parentFolderItem = static_cast<UICustomFileSystemItem*>(currentIndex.internalPointer());
+ if (!parentFolderItem)
+ return;
+
+ QString newDirectoryName(UICustomFileSystemModel::tr("New Directory"));
+
+ if (!createDirectory(parentFolderItem->path(), newDirectoryName))
+ return;
+
+ /* Refesh the current directory so that we correctly populate the child list of parentFolderItem: */
+ /** @todo instead of refreshing here (an overkill) just add the
+ rows and update the tabel view: */
+ sltRefresh();
+
+ /* Now we try to edit the newly created item thereby enabling the user to rename the new item: */
+ QList<UICustomFileSystemItem*> content = parentFolderItem->children();
+ UICustomFileSystemItem* newItem = 0;
+ /* Search the new item: */
+ foreach (UICustomFileSystemItem* childItem, content)
+ {
+
+ if (childItem && newDirectoryName == childItem->name())
+ newItem = childItem;
+ }
+
+ if (!newItem)
+ return;
+ QModelIndex newItemIndex = m_pProxyModel->mapFromSource(m_pModel->index(newItem));
+ if (!newItemIndex.isValid())
+ return;
+ m_pView->edit(newItemIndex);
+}
+
+void UIFileManagerTable::sltCopy()
+{
+ m_copyCutBuffer = selectedItemPathList();
+ m_eFileOperationType = FileOperationType_Copy;
+ setPasteActionEnabled(true);
+}
+
+void UIFileManagerTable::sltCut()
+{
+ m_copyCutBuffer = selectedItemPathList();
+ m_eFileOperationType = FileOperationType_Cut;
+ setPasteActionEnabled(true);
+}
+
+void UIFileManagerTable::sltPaste()
+{
+ m_copyCutBuffer.clear();
+
+ m_eFileOperationType = FileOperationType_None;
+ setPasteActionEnabled(false);
+}
+
+void UIFileManagerTable::sltShowProperties()
+{
+ showProperties();
+}
+
+void UIFileManagerTable::sltSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
+{
+ Q_UNUSED(selected);
+ Q_UNUSED(deselected);
+ setSelectionDependentActionsEnabled(m_pView->hasSelection());
+}
+
+void UIFileManagerTable::sltSelectAll()
+{
+ if (!m_pModel || !m_pView)
+ return;
+ m_pView->selectAll();
+ deSelectUpDirectoryItem();
+}
+
+void UIFileManagerTable::sltInvertSelection()
+{
+ setSelectionForAll(QItemSelectionModel::Toggle | QItemSelectionModel::Rows);
+ deSelectUpDirectoryItem();
+}
+
+void UIFileManagerTable::sltSearchTextChanged(const QString &strText)
+{
+ performSelectionSearch(strText);
+}
+
+void UIFileManagerTable::sltHandleItemRenameAttempt(UICustomFileSystemItem *pItem, QString strOldName, QString strNewName)
+{
+ if (!pItem)
+ return;
+ /* Attempt to chage item name in the file system: */
+ if (!renameItem(pItem, strNewName))
+ {
+ /* Restore the previous name. relist the view: */
+ pItem->setData(strOldName, static_cast<int>(UICustomFileSystemModelColumn_Name));
+ relist();
+ emit sigLogOutput(QString(pItem->path()).append(" could not be renamed"), QString(), FileManagerLogType_Error);
+ }
+}
+
+void UIFileManagerTable::sltCreateFileViewContextMenu(const QPoint &point)
+{
+ QWidget *pSender = qobject_cast<QWidget*>(sender());
+ if (!pSender)
+ return;
+ createFileViewContextMenu(pSender, point);
+}
+
+void UIFileManagerTable::sltHandleNavigationWidgetPathChange(const QString& strPath)
+{
+ goIntoDirectory(UIPathOperations::pathTrail(strPath));
+}
+
+void UIFileManagerTable::deSelectUpDirectoryItem()
+{
+ if (!m_pView)
+ return;
+ QItemSelectionModel *pSelectionModel = m_pView->selectionModel();
+ if (!pSelectionModel)
+ return;
+ QModelIndex currentRoot = currentRootIndex();
+ if (!currentRoot.isValid())
+ return;
+
+ /* Make sure that "up directory item" (if exists) is deselected: */
+ for (int i = 0; i < m_pModel->rowCount(currentRoot); ++i)
+ {
+ QModelIndex index = m_pModel->index(i, 0, currentRoot);
+ if (!index.isValid())
+ continue;
+
+ UICustomFileSystemItem *item = static_cast<UICustomFileSystemItem*>(index.internalPointer());
+ if (item && item->isUpDirectory())
+ {
+ QModelIndex indexToDeselect = m_pProxyModel ? m_pProxyModel->mapFromSource(index) : index;
+ pSelectionModel->select(indexToDeselect, QItemSelectionModel::Deselect | QItemSelectionModel::Rows);
+ }
+ }
+}
+
+void UIFileManagerTable::setSelectionForAll(QItemSelectionModel::SelectionFlags flags)
+{
+ if (!m_pView)
+ return;
+ QItemSelectionModel *pSelectionModel = m_pView->selectionModel();
+ if (!pSelectionModel)
+ return;
+ QModelIndex currentRoot = currentRootIndex();
+ if (!currentRoot.isValid())
+ return;
+
+ for (int i = 0; i < m_pModel->rowCount(currentRoot); ++i)
+ {
+ QModelIndex index = m_pModel->index(i, 0, currentRoot);
+ if (!index.isValid())
+ continue;
+ QModelIndex indexToSelect = m_pProxyModel ? m_pProxyModel->mapFromSource(index) : index;
+ pSelectionModel->select(indexToSelect, flags);
+ }
+}
+
+void UIFileManagerTable::setSelection(const QModelIndex &indexInProxyModel)
+{
+ if (!m_pView)
+ return;
+ QItemSelectionModel *selectionModel = m_pView->selectionModel();
+ if (!selectionModel)
+ return;
+ selectionModel->select(indexInProxyModel, QItemSelectionModel::Current | QItemSelectionModel::Rows | QItemSelectionModel::Select);
+ m_pView->scrollTo(indexInProxyModel, QAbstractItemView::EnsureVisible);
+}
+
+void UIFileManagerTable::deleteByIndex(const QModelIndex &itemIndex)
+{
+ UICustomFileSystemItem *treeItem = indexData(itemIndex);
+ if (!treeItem)
+ return;
+ deleteByItem(treeItem);
+}
+
+void UIFileManagerTable::retranslateUi()
+{
+ UICustomFileSystemItem *pRootItem = rootItem();
+ if (pRootItem)
+ {
+ pRootItem->setData(UIFileManager::tr("Name"), UICustomFileSystemModelColumn_Name);
+ pRootItem->setData(UIFileManager::tr("Size"), UICustomFileSystemModelColumn_Size);
+ pRootItem->setData(UIFileManager::tr("Change Time"), UICustomFileSystemModelColumn_ChangeTime);
+ pRootItem->setData(UIFileManager::tr("Owner"), UICustomFileSystemModelColumn_Owner);
+ pRootItem->setData(UIFileManager::tr("Permissions"), UICustomFileSystemModelColumn_Permissions);
+ }
+}
+
+bool UIFileManagerTable::eventFilter(QObject *pObject, QEvent *pEvent) /* override */
+{
+ /* Handle only events sent to m_pView: */
+ if (pObject != m_pView)
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
+
+ if (pEvent->type() == QEvent::KeyPress)
+ {
+ QKeyEvent *pKeyEvent = dynamic_cast<QKeyEvent*>(pEvent);
+ if (pKeyEvent)
+ {
+ if (pKeyEvent->key() == Qt::Key_Enter || pKeyEvent->key() == Qt::Key_Return)
+ {
+ if (m_pView && m_pModel && !m_pView->isInEditState())
+ {
+ /* Get the selected item. If there are 0 or more than 1 selection do nothing: */
+ QItemSelectionModel *selectionModel = m_pView->selectionModel();
+ if (selectionModel)
+ {
+ QModelIndexList selectedItemIndices = selectionModel->selectedRows();
+ if (selectedItemIndices.size() == 1 && m_pModel)
+ goIntoDirectory( m_pProxyModel->mapToSource(selectedItemIndices.at(0)));
+ }
+ }
+ return true;
+ }
+ else if (pKeyEvent->key() == Qt::Key_Delete)
+ {
+ sltDelete();
+ return true;
+ }
+ else if (pKeyEvent->key() == Qt::Key_Backspace)
+ {
+ sltGoUp();
+ return true;
+ }
+ else if (pKeyEvent->text().length() == 1 &&
+ (pKeyEvent->text().at(0).isDigit() ||
+ pKeyEvent->text().at(0).isLetter()))
+ {
+ if (m_pSearchLineEdit)
+ {
+ markUnmarkSearchLineEdit(false);
+ m_pSearchLineEdit->clear();
+ m_pSearchLineEdit->show();
+ m_pSearchLineEdit->setFocus();
+ m_pSearchLineEdit->setText(pKeyEvent->text());
+ }
+ }
+ else if (pKeyEvent->key() == Qt::Key_Tab)
+ {
+ return true;
+ }
+ }
+ }
+ else if (pEvent->type() == QEvent::FocusOut)
+ {
+ disableSelectionSearch();
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
+}
+
+UICustomFileSystemItem *UIFileManagerTable::getStartDirectoryItem()
+{
+ UICustomFileSystemItem* pRootItem = rootItem();
+ if (!pRootItem)
+ return 0;
+ if (pRootItem->childCount() <= 0)
+ return 0;
+ return pRootItem->child(0);
+}
+
+QString UIFileManagerTable::currentDirectoryPath() const
+{
+ if (!m_pView)
+ return QString();
+ QModelIndex currentRoot = currentRootIndex();
+ if (!currentRoot.isValid())
+ return QString();
+ UICustomFileSystemItem *item = static_cast<UICustomFileSystemItem*>(currentRoot.internalPointer());
+ if (!item)
+ return QString();
+ /* be paranoid: */
+ if (!item->isDirectory())
+ return QString();
+ return item->path();
+}
+
+QStringList UIFileManagerTable::selectedItemPathList()
+{
+ QItemSelectionModel *selectionModel = m_pView->selectionModel();
+ if (!selectionModel)
+ return QStringList();
+
+ QStringList pathList;
+ QModelIndexList selectedItemIndices = selectionModel->selectedRows();
+ for(int i = 0; i < selectedItemIndices.size(); ++i)
+ {
+ QModelIndex index =
+ m_pProxyModel ? m_pProxyModel->mapToSource(selectedItemIndices.at(i)) : selectedItemIndices.at(i);
+ UICustomFileSystemItem *item = static_cast<UICustomFileSystemItem*>(index.internalPointer());
+ if (!item)
+ continue;
+
+ /* Make sure to remove any trailing delimiters for directory paths here (e.g. "C:\foo\bar\" -> "C:\foo\bar"),
+ * as we want to copy entire directories, not only its contents (see Guest Control SDK docs). */
+ pathList.push_back(item->path(true /* fRemoveTrailingDelimiters */));
+ }
+ return pathList;
+}
+
+CGuestFsObjInfo UIFileManagerTable::guestFsObjectInfo(const QString& path, CGuestSession &comGuestSession) const
+{
+ if (comGuestSession.isNull())
+ return CGuestFsObjInfo();
+ CGuestFsObjInfo comFsObjInfo = comGuestSession.FsObjQueryInfo(path, true /*aFollowSymlinks*/);
+ if (!comFsObjInfo.isOk())
+ return CGuestFsObjInfo();
+ return comFsObjInfo;
+}
+
+void UIFileManagerTable::setSelectionDependentActionsEnabled(bool fIsEnabled)
+{
+ foreach (QAction *pAction, m_selectionDependentActions)
+ pAction->setEnabled(fIsEnabled);
+ if (m_pView)
+ emit sigSelectionChanged(m_pView->hasSelection());
+}
+
+UICustomFileSystemItem* UIFileManagerTable::rootItem()
+{
+ if (!m_pModel)
+ return 0;
+ return m_pModel->rootItem();
+}
+
+void UIFileManagerTable::setPathSeparator(const QChar &separator)
+{
+ m_pathSeparator = separator;
+ if (m_pNavigationWidget)
+ m_pNavigationWidget->setPathSeparator(m_pathSeparator);
+}
+
+QHBoxLayout* UIFileManagerTable::toolBarLayout()
+{
+ return m_pToolBarLayout;
+}
+
+bool UIFileManagerTable::event(QEvent *pEvent)
+{
+ if (pEvent->type() == QEvent::EnabledChange)
+ retranslateUi();
+ return QIWithRetranslateUI<QWidget>::event(pEvent);
+}
+
+QString UIFileManagerTable::fileTypeString(KFsObjType type)
+{
+ QString strType = UIFileManager::tr("Unknown");
+ switch (type)
+ {
+ case KFsObjType_File:
+ strType = UIFileManager::tr("File");
+ break;
+ case KFsObjType_Directory:
+ strType = UIFileManager::tr("Directory");
+ break;
+ case KFsObjType_Symlink:
+ strType = UIFileManager::tr("Symbolic Link");
+ break;
+ case KFsObjType_Unknown:
+ default:
+ strType = UIFileManager::tr("Unknown");
+ break;
+ }
+ return strType;
+}
+
+/* static */ QString UIFileManagerTable::humanReadableSize(ULONG64 size)
+{
+ return UITranslator::formatSize(size);
+}
+
+void UIFileManagerTable::optionsUpdated()
+{
+ UIFileManagerOptions *pOptions = UIFileManagerOptions::instance();
+ if (pOptions)
+ {
+ if (m_pProxyModel)
+ {
+ m_pProxyModel->setListDirectoriesOnTop(pOptions->fListDirectoriesOnTop);
+ m_pProxyModel->setShowHiddenObjects(pOptions->fShowHiddenObjects);
+ }
+ if (m_pModel)
+ m_pModel->setShowHumanReadableSizes(pOptions->fShowHumanReadableSizes);
+ }
+ relist();
+}
+
+bool UIFileManagerTable::hasSelection() const
+{
+ if (m_pView)
+ return m_pView->hasSelection();
+ return false;
+}
+
+void UIFileManagerTable::sltReceiveDirectoryStatistics(UIDirectoryStatistics statistics)
+{
+ if (!m_pPropertiesDialog)
+ return;
+ m_pPropertiesDialog->addDirectoryStatistics(statistics);
+}
+
+QModelIndex UIFileManagerTable::currentRootIndex() const
+{
+ if (!m_pView)
+ return QModelIndex();
+ if (!m_pProxyModel)
+ return m_pView->rootIndex();
+ return m_pProxyModel->mapToSource(m_pView->rootIndex());
+}
+
+void UIFileManagerTable::performSelectionSearch(const QString &strSearchText)
+{
+ if (!m_pProxyModel | !m_pView)
+ return;
+
+ if (strSearchText.isEmpty())
+ {
+ markUnmarkSearchLineEdit(false);
+ return;
+ }
+
+ int rowCount = m_pProxyModel->rowCount(m_pView->rootIndex());
+ UICustomFileSystemItem *pFoundItem = 0;
+ QModelIndex index;
+ for (int i = 0; i < rowCount && !pFoundItem; ++i)
+ {
+ index = m_pProxyModel->index(i, 0, m_pView->rootIndex());
+ if (!index.isValid())
+ continue;
+ pFoundItem = static_cast<UICustomFileSystemItem*>(m_pProxyModel->mapToSource(index).internalPointer());
+ if (!pFoundItem)
+ continue;
+ const QString &strName = pFoundItem->name();
+ if (!strName.startsWith(m_pSearchLineEdit->text(), Qt::CaseInsensitive))
+ pFoundItem = 0;
+ }
+ if (pFoundItem)
+ {
+ /* Deselect anything that is already selected: */
+ m_pView->clearSelection();
+ setSelection(index);
+ }
+ markUnmarkSearchLineEdit(!pFoundItem);
+}
+
+void UIFileManagerTable::disableSelectionSearch()
+{
+ if (!m_pSearchLineEdit)
+ return;
+ m_pSearchLineEdit->blockSignals(true);
+ m_pSearchLineEdit->clear();
+ m_pSearchLineEdit->hide();
+ m_pSearchLineEdit->blockSignals(false);
+}
+
+bool UIFileManagerTable::checkIfDeleteOK()
+{
+ UIFileManagerOptions *pFileManagerOptions = UIFileManagerOptions::instance();
+ if (!pFileManagerOptions)
+ return true;
+ if (!pFileManagerOptions->fAskDeleteConfirmation)
+ return true;
+ UIFileDeleteConfirmationDialog *pDialog =
+ new UIFileDeleteConfirmationDialog(this);
+
+ bool fContinueWithDelete = (pDialog->execute() == QDialog::Accepted);
+ bool bAskNextTime = pDialog->askDeleteConfirmationNextTime();
+
+ /* Update the file manager options only if it is necessary: */
+ if (pFileManagerOptions->fAskDeleteConfirmation != bAskNextTime)
+ {
+ pFileManagerOptions->fAskDeleteConfirmation = bAskNextTime;
+ /* Notify file manager options panel so that the check box there is updated: */
+ emit sigDeleteConfirmationOptionChanged();
+ }
+
+ delete pDialog;
+
+ return fContinueWithDelete;
+
+}
+
+void UIFileManagerTable::markUnmarkSearchLineEdit(bool fMark)
+{
+ if (!m_pSearchLineEdit)
+ return;
+ QPalette palette = m_pSearchLineEdit->palette();
+
+ if (fMark)
+ palette.setColor(QPalette::Base, m_searchLineMarkColor);
+ else
+ palette.setColor(QPalette::Base, m_searchLineUnmarkColor);
+ m_pSearchLineEdit->setPalette(palette);
+}
+
+void UIFileManagerTable::setSessionWidgetsEnabled(bool fEnabled)
+{
+ foreach (QWidget *pWidget, m_sessionWidgets)
+ {
+ if (pWidget)
+ pWidget->setEnabled(fEnabled);
+ }
+}
+#include "UIFileManagerTable.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerTable.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerTable.h
new file mode 100644
index 00000000..5db4ff3d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIFileManagerTable.h
@@ -0,0 +1,323 @@
+/* $Id: UIFileManagerTable.h $ */
+/** @file
+ * VBox Qt GUI - UIFileManagerTable class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIFileManagerTable_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIFileManagerTable_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QItemSelectionModel>
+#include <QMutex>
+#include <QThread>
+#include <QWidget>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuestSession.h"
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QITableView.h"
+#include "QIWithRetranslateUI.h"
+#include "UIGuestControlDefs.h"
+
+/* Forward declarations: */
+class QAction;
+class QFileInfo;
+class QComboBox;
+class QILabel;
+class QILineEdit;
+class QGridLayout;
+class QSortFilterProxyModel;
+class QStackedWidget;
+class QTextEdit;
+class QHBoxLayout;
+class QVBoxLayout;
+class UIActionPool;
+class UICustomFileSystemItem;
+class UICustomFileSystemModel;
+class UICustomFileSystemProxyModel;
+class UIFileManagerNavigationWidget;
+class UIGuestControlFileView;
+class QIToolBar;
+
+/** A simple struck to store some statictics for a directory. Mainly used by UIDirectoryDiskUsageComputer instances. */
+class UIDirectoryStatistics
+{
+public:
+ UIDirectoryStatistics();
+ ULONG64 m_totalSize;
+ unsigned m_uFileCount;
+ unsigned m_uDirectoryCount;
+ unsigned m_uSymlinkCount;
+};
+
+Q_DECLARE_METATYPE(UIDirectoryStatistics);
+
+
+/** Examines the paths in @p strStartPath and collects some staticstics from them recursively (in case directories)
+ * Runs on a worker thread to avoid GUI freezes. UIGuestFileTable and UIHostFileTable uses specialized children
+ * of this class since the calls made on file objects are different. */
+class UIDirectoryDiskUsageComputer : public QThread
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigResultUpdated(UIDirectoryStatistics);
+
+public:
+
+ UIDirectoryDiskUsageComputer(QObject *parent, QStringList strStartPath);
+ /** Sets the m_fOkToContinue to false. This results an early termination
+ * of the directoryStatisticsRecursive member function. */
+ void stopRecursion();
+
+protected:
+
+ /** Read the directory with the path @p path recursively and collect #of objects and total size */
+ virtual void directoryStatisticsRecursive(const QString &path, UIDirectoryStatistics &statistics) = 0;
+ virtual void run() RT_OVERRIDE;
+ /** Returns the m_fOkToContinue flag */
+ bool isOkToContinue() const;
+ /** Stores a list of paths whose statistics are accumulated, can be file, directory etc: */
+ QStringList m_pathList;
+ UIDirectoryStatistics m_resultStatistics;
+ QMutex m_mutex;
+
+private:
+
+ bool m_fOkToContinue;
+};
+
+/** A QIDialog child to display properties of a file object */
+class UIPropertiesDialog : public QIDialog
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIPropertiesDialog(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+ void setPropertyText(const QString &strProperty);
+ void addDirectoryStatistics(UIDirectoryStatistics statictics);
+
+private:
+
+ QVBoxLayout *m_pMainLayout;
+ QTextEdit *m_pInfoEdit;
+ QString m_strProperty;
+};
+
+/** This class serves a base class for file table. Currently a guest version
+ * and a host version are derived from this base. Each of these children
+ * populates the UICustomFileSystemModel by scanning the file system
+ * differently. The file structure kept in this class as a tree. */
+class UIFileManagerTable : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigLogOutput(QString strLog, const QString &strMachineName, FileManagerLogType eLogType);
+ void sigDeleteConfirmationOptionChanged();
+ void sigSelectionChanged(bool fHasSelection);
+
+public:
+
+ UIFileManagerTable(UIActionPool *pActionPool, QWidget *pParent = 0);
+ virtual ~UIFileManagerTable();
+ /** Deletes all the tree nodes */
+ void reset();
+ /** Returns the path of the rootIndex */
+ QString currentDirectoryPath() const;
+ /** Returns the paths of the selected items (if any) as a list */
+ QStringList selectedItemPathList();
+ virtual void refresh();
+ static const unsigned m_iKiloByte;
+ static QString humanReadableSize(ULONG64 size);
+ /** Peroforms whatever is necessary after a UIFileManagerOptions change. */
+ void optionsUpdated();
+ bool hasSelection() const;
+
+public slots:
+
+ void sltReceiveDirectoryStatistics(UIDirectoryStatistics statictics);
+ void sltCreateNewDirectory();
+ /* index is passed by the item view and represents the double clicked object's 'proxy' model index */
+ void sltItemDoubleClicked(const QModelIndex &index);
+ void sltItemClicked(const QModelIndex &index);
+ void sltGoUp();
+ void sltGoHome();
+ void sltRefresh();
+ void sltDelete();
+ /** Calls the edit on the data item over m_pView. This causes setData(..) call on the model. After setting
+ * user entered text as the name of the item m_pModel signals. This signal is handled by sltHandleItemRenameAttempt which
+ * tries to rename the corresponding file object by calling renameItem(...). If this rename fails the old name of the
+ * model item is restored and view is refreshed by sltHandleItemRenameAttempt. */
+ void sltRename();
+ void sltCopy();
+ void sltCut();
+ void sltPaste();
+ void sltShowProperties();
+ void sltSelectAll();
+ void sltInvertSelection();
+
+protected:
+
+ /** This enum is used when performing a gueest-to-guest or host-to-host
+ * file operations. Paths of source file objects are kept in a single buffer
+ * and a flag to determine if it is a cut or copy operation is needed */
+ enum FileOperationType
+ {
+ FileOperationType_Copy,
+ FileOperationType_Cut,
+ FileOperationType_None,
+ FileOperationType_Max
+ };
+
+ void retranslateUi();
+ void updateCurrentLocationEdit(const QString& strLocation);
+ /* @p index is for model not for 'proxy' model */
+ void changeLocation(const QModelIndex &index);
+ void initializeFileTree();
+ void checkDotDot(QMap<QString,UICustomFileSystemItem*> &map, UICustomFileSystemItem *parent, bool isStartDir);
+
+ virtual void readDirectory(const QString& strPath, UICustomFileSystemItem *parent, bool isStartDir = false) = 0;
+ virtual void deleteByItem(UICustomFileSystemItem *item) = 0;
+ virtual void deleteByPath(const QStringList &pathList) = 0;
+ virtual void goToHomeDirectory() = 0;
+ virtual bool renameItem(UICustomFileSystemItem *item, QString newBaseName) = 0;
+ virtual bool createDirectory(const QString &path, const QString &directoryName) = 0;
+ virtual QString fsObjectPropertyString() = 0;
+ virtual void showProperties() = 0;
+ /** For non-windows system does nothing and for windows systems populates m_driveLetterList with
+ * drive letters */
+ virtual void determineDriveLetters() = 0;
+ virtual void determinePathSeparator() = 0;
+ virtual void prepareToolbar() = 0;
+ virtual void createFileViewContextMenu(const QWidget *pWidget, const QPoint &point) = 0;
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** @name Copy/Cut guest-to-guest (host-to-host) stuff.
+ * @{ */
+ /** Disable/enable paste action depending on the m_eFileOperationType. */
+ virtual void setPasteActionEnabled(bool fEnabled) = 0;
+ virtual void pasteCutCopiedObjects() = 0;
+ /** stores the type of the pending guest-to-guest (host-to-host) file operation. */
+ FileOperationType m_eFileOperationType;
+ /** @} */
+
+ QString fileTypeString(KFsObjType type);
+ /* @p item index is item location in model not in 'proxy' model */
+ void goIntoDirectory(const QModelIndex &itemIndex);
+ /** Follows the path trail, opens directories as it descends */
+ void goIntoDirectory(const QStringList &pathTrail);
+ /** Goes into directory pointed by the @p item */
+ void goIntoDirectory(UICustomFileSystemItem *item);
+ UICustomFileSystemItem* indexData(const QModelIndex &index) const;
+ bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+ CGuestFsObjInfo guestFsObjectInfo(const QString& path, CGuestSession &comGuestSession) const;
+ void setSelectionDependentActionsEnabled(bool fIsEnabled);
+ UICustomFileSystemItem* rootItem();
+ void setPathSeparator(const QChar &separator);
+ QHBoxLayout* toolBarLayout();
+ void setSessionWidgetsEnabled(bool fEnabled);
+
+ QILabel *m_pLocationLabel;
+ UIPropertiesDialog *m_pPropertiesDialog;
+ UIActionPool *m_pActionPool;
+ QIToolBar *m_pToolBar;
+ QGridLayout *m_pMainLayout;
+ /** Stores the drive letters the file system has (for windows system). For non-windows
+ * systems this is empty and for windows system it should at least contain C:/ */
+ QStringList m_driveLetterList;
+ /** The set of actions which need some selection to work on. Like cut, copy etc. */
+ QSet<QAction*> m_selectionDependentActions;
+ /** The absolute path list of the file objects which user has chosen to cut/copy. this
+ * list will be cleaned after a paste operation or overwritten by a subsequent cut/copy.
+ * Currently only used by the guest side. */
+ QStringList m_copyCutBuffer;
+ /** This name is appended to the log messages which are shown in the log panel. */
+ QString m_strTableName;
+
+private slots:
+
+ void sltCreateFileViewContextMenu(const QPoint &point);
+ void sltSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected);
+ void sltSearchTextChanged(const QString &strText);
+ /** m_pModel signals when an tree item is renamed. we try to apply this rename to the file system.
+ * if the file system rename fails we restore the old name of the item. See the comment of
+ * sltRename() for more details. */
+ void sltHandleItemRenameAttempt(UICustomFileSystemItem *pItem, QString strOldName, QString strNewName);
+ void sltHandleNavigationWidgetPathChange(const QString& strPath);
+
+private:
+
+ void relist();
+ void prepareObjects();
+ /** @p itemIndex is assumed to be 'model' index not 'proxy model' index */
+ void deleteByIndex(const QModelIndex &itemIndex);
+ /** Returns the UICustomFileSystemItem for path / which is a direct (and single) child of m_pRootItem */
+ UICustomFileSystemItem *getStartDirectoryItem();
+ void deSelectUpDirectoryItem();
+ void setSelectionForAll(QItemSelectionModel::SelectionFlags flags);
+ void setSelection(const QModelIndex &indexInProxyModel);
+ /** The start directory requires a special attention since on file systems with drive letters
+ * drive letter are direct children of the start directory. On other systems start directory is '/' */
+ void populateStartDirectory(UICustomFileSystemItem *startItem);
+ /** Root index of the m_pModel */
+ QModelIndex currentRootIndex() const;
+ /* Searches the content of m_pSearchLineEdit within the current items' names and selects the item if found. */
+ void performSelectionSearch(const QString &strSearchText);
+ /** Clears the m_pSearchLineEdit and hides it. */
+ void disableSelectionSearch();
+ /** Checks if delete confirmation dialog is shown and users choice. Returns true
+ * if deletion can continue */
+ bool checkIfDeleteOK();
+ /** Marks/umarks the search line edit to signal that there are no matches for the current search.
+ * uses m_searchLineUnmarkColor and m_searchLineMarkColor. */
+ void markUnmarkSearchLineEdit(bool fMark);
+
+ UICustomFileSystemModel *m_pModel;
+ UIGuestControlFileView *m_pView;
+ UICustomFileSystemProxyModel *m_pProxyModel;
+ /** Contains m_pBreadCrumbsWidget and m_pLocationComboBox. */
+ UIFileManagerNavigationWidget *m_pNavigationWidget;
+
+ QILineEdit *m_pSearchLineEdit;
+ QColor m_searchLineUnmarkColor;
+ QColor m_searchLineMarkColor;
+ QChar m_pathSeparator;
+ QHBoxLayout *m_pToolBarLayout;
+ QVector<QWidget*> m_sessionWidgets;
+ friend class UICustomFileSystemModel;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIFileManagerTable_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlConsole.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlConsole.cpp
new file mode 100644
index 00000000..82ec2d3c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlConsole.cpp
@@ -0,0 +1,326 @@
+/* $Id: UIGuestControlConsole.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGuestControlConsole class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+#include <QApplication>
+#include <QTextBlock>
+
+/* GUI includes: */
+#include "UIGuestControlConsole.h"
+#include "UIGuestControlInterface.h"
+
+UIGuestControlConsole::UIGuestControlConsole(const CGuest &comGuest, QWidget* parent /* = 0 */)
+ :QPlainTextEdit(parent)
+ , m_comGuest(comGuest)
+ , m_strGreet("Welcome to 'Guest Control Console'. Type 'help' for help\n")
+ , m_strPrompt("$>")
+ , m_uCommandHistoryIndex(0)
+ , m_pControlInterface(0)
+{
+ m_pControlInterface = new UIGuestControlInterface(this, m_comGuest);
+
+ connect(m_pControlInterface, &UIGuestControlInterface::sigOutputString,
+ this, &UIGuestControlConsole::sltOutputReceived);
+
+ /* Configure this: */
+ setUndoRedoEnabled(false);
+ setWordWrapMode(QTextOption::NoWrap);
+ reset();
+
+ m_tabDictinary.insert("username", 0);
+ m_tabDictinary.insert("createsession", 0);
+ m_tabDictinary.insert("exe", 0);
+ m_tabDictinary.insert("sessionid", 0);
+ m_tabDictinary.insert("sessionname", 0);
+ m_tabDictinary.insert("timeout", 0);
+ m_tabDictinary.insert("password", 0);
+ m_tabDictinary.insert("start", 0);
+ m_tabDictinary.insert("ls", 0);
+ m_tabDictinary.insert("stat", 0);
+}
+
+void UIGuestControlConsole::commandEntered(const QString &strCommand)
+{
+ if (m_pControlInterface)
+ m_pControlInterface->putCommand(strCommand);
+}
+
+void UIGuestControlConsole::sltOutputReceived(const QString &strOutput)
+{
+ putOutput(strOutput);
+}
+
+void UIGuestControlConsole::reset()
+{
+ clear();
+ startNextLine();
+ insertPlainText(m_strGreet);
+ startNextLine();
+}
+
+void UIGuestControlConsole::startNextLine()
+{
+ moveCursor(QTextCursor::End);
+ insertPlainText(m_strPrompt);
+ moveCursor(QTextCursor::End);
+}
+
+
+void UIGuestControlConsole::putOutput(const QString &strOutput)
+{
+ if (strOutput.isNull() || strOutput.length() <= 0)
+ return;
+
+ bool newLineNeeded = getCommandString().isEmpty();
+
+ QString strOwn("\n");
+ strOwn.append(strOutput);
+ moveCursor(QTextCursor::End);
+ insertPlainText(strOwn);
+ moveCursor(QTextCursor::End);
+
+ if (newLineNeeded)
+ {
+ insertPlainText("\n");
+ startNextLine();
+ }
+ }
+
+void UIGuestControlConsole::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Check if we at the bottom most line.*/
+ bool lastLine = blockCount() == (textCursor().blockNumber() +1);
+
+ switch (pEvent->key()) {
+ case Qt::Key_PageUp:
+ case Qt::Key_Up:
+ {
+ replaceLineContent(getPreviousCommandFromHistory(getCommandString()));
+ break;
+ }
+ case Qt::Key_PageDown:
+ case Qt::Key_Down:
+ {
+ replaceLineContent(getNextCommandFromHistory(getCommandString()));
+ break;
+ }
+ case Qt::Key_Backspace:
+ {
+ QTextCursor cursor = textCursor();
+ if (lastLine && cursor.positionInBlock() > m_strPrompt.length())
+ cursor.deletePreviousChar();
+ break;
+ }
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ {
+ if (textCursor().positionInBlock() > m_strPrompt.length()-1)
+ QPlainTextEdit::keyPressEvent(pEvent);
+ break;
+ }
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ {
+ if (lastLine)
+ {
+ QString strCommand(getCommandString());
+ if (!strCommand.isEmpty())
+ {
+ commandEntered(strCommand);
+ if (!m_tCommandHistory.contains(strCommand))
+ m_tCommandHistory.push_back(strCommand);
+ m_uCommandHistoryIndex = m_tCommandHistory.size()-1;
+ moveCursor(QTextCursor::End);
+ QPlainTextEdit::keyPressEvent(pEvent);
+ startNextLine();
+ }
+ }
+ break;
+ }
+ case Qt::Key_Home:
+ {
+ QTextCursor cursor = textCursor();
+ cursor.movePosition(QTextCursor::StartOfLine);
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, m_strPrompt.length());
+ setTextCursor(cursor);
+ break;
+ }
+ case Qt::Key_Tab:
+ completeByTab();
+ break;
+ default:
+ {
+ if (pEvent->modifiers() == Qt::ControlModifier && pEvent->key() == Qt::Key_C)
+ {
+ QPlainTextEdit::keyPressEvent(pEvent);
+ }
+ else
+ {
+ if (lastLine)
+ QPlainTextEdit::keyPressEvent(pEvent);
+ }
+ }
+ break;
+ }
+}
+
+void UIGuestControlConsole::mousePressEvent(QMouseEvent *pEvent)
+{
+ // Q_UNUSED(pEvent);
+ // setFocus();
+ QPlainTextEdit::mousePressEvent(pEvent);
+}
+
+void UIGuestControlConsole::mouseDoubleClickEvent(QMouseEvent *pEvent)
+{
+ //Q_UNUSED(pEvent);
+ QPlainTextEdit::mouseDoubleClickEvent(pEvent);
+}
+
+void UIGuestControlConsole::contextMenuEvent(QContextMenuEvent *pEvent)
+{
+ Q_UNUSED(pEvent);
+ //QPlainTextEdit::contextMenuEvent(pEvent);
+}
+
+QString UIGuestControlConsole::getCommandString()
+{
+ QTextDocument* pDocument = document();
+ if (!pDocument)
+ return QString();
+ QTextBlock block = pDocument->lastBlock();//findBlockByLineNumber(pDocument->lineCount()-1);
+ if (!block.isValid())
+ return QString();
+ QString lineStr = block.text();
+ if (lineStr.isNull() || lineStr.length() <= 1)
+ return QString();
+ /* Remove m_strPrompt from the line string: */
+ return (lineStr.right(lineStr.length()-m_strPrompt.length()));
+}
+
+void UIGuestControlConsole::replaceLineContent(const QString &stringNewContent)
+{
+ moveCursor(QTextCursor::End);
+ QTextCursor cursor = textCursor();
+ cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
+ cursor.removeSelectedText();
+
+ QString newString(m_strPrompt);
+ newString.append(stringNewContent);
+ insertPlainText(newString);
+ moveCursor(QTextCursor::End);
+}
+
+QString UIGuestControlConsole::getNextCommandFromHistory(const QString &originalString /* = QString() */)
+{
+ if (m_tCommandHistory.empty())
+ return originalString;
+
+ if (m_uCommandHistoryIndex == (unsigned)(m_tCommandHistory.size() - 1))
+ m_uCommandHistoryIndex = 0;
+ else
+ ++m_uCommandHistoryIndex;
+
+ return m_tCommandHistory.at(m_uCommandHistoryIndex);
+}
+
+
+QString UIGuestControlConsole::getPreviousCommandFromHistory(const QString &originalString /* = QString() */)
+{
+ if (m_tCommandHistory.empty())
+ return originalString;
+ if (m_uCommandHistoryIndex == 0)
+ m_uCommandHistoryIndex = m_tCommandHistory.size() - 1;
+ else
+ --m_uCommandHistoryIndex;
+
+ return m_tCommandHistory.at(m_uCommandHistoryIndex);
+}
+
+void UIGuestControlConsole::completeByTab()
+{
+ bool lastLine = blockCount() == (textCursor().blockNumber() +1);
+ if (!lastLine)
+ return;
+ /* Save whatever we have currently on this line: */
+ QString currentCommand = getCommandString();
+
+ QTextCursor cursor = textCursor();
+ /* Save the cursor's position within the line */
+ int cursorBlockPosition = cursor.positionInBlock();
+
+ /* Find out on which word the cursor is. This is the word we will
+ complete: */
+ cursor.select(QTextCursor::WordUnderCursor);
+ QString currentWord = cursor.selectedText();
+
+ const QList<QString> &matches = matchedWords(currentWord);
+ /* If there are no matches do nothing: */
+ if (matches.empty())
+ return;
+ /* if there are more than one match list them all and
+ reprint the line: */
+ if (matches.size() > 1)
+ {
+ moveCursor(QTextCursor::End);
+ QString strMatches;
+ for (int i = 0; i < matches.size(); ++i)
+ {
+ strMatches.append(matches.at(i));
+ strMatches.append(" ");
+ }
+ appendPlainText(strMatches);
+ insertPlainText(QString("\n").append(m_strPrompt));
+ insertPlainText(currentCommand);
+ /* Put the cursor in its previous position within the line: */
+ int blockPosition = textCursor().block().position();
+ QTextCursor nCursor = textCursor();
+ nCursor.setPosition(blockPosition + cursorBlockPosition);
+ setTextCursor(nCursor);
+ return;
+ }
+ /* if there is only one word just complete: */
+ /* some sanity checks */
+ if (matches.at(0).length() > currentWord.length())
+ insertPlainText(matches.at(0).right(matches.at(0).length() - currentWord.length()));
+}
+
+
+QList<QString> UIGuestControlConsole::matchedWords(const QString &strSearch) const
+{
+ QList<QString> list;
+ /* Go thru the map and find which of its elements start with @pstrSearch: */
+ for (TabDictionary::const_iterator iterator = m_tabDictinary.begin();
+ iterator != m_tabDictinary.end(); ++iterator)
+ {
+ const QString &strMap = iterator.key();
+ if (strMap.startsWith(strSearch))
+ list.push_back(strMap);
+ }
+ return list;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlConsole.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlConsole.h
new file mode 100644
index 00000000..be734add
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlConsole.h
@@ -0,0 +1,95 @@
+/* $Id: UIGuestControlConsole.h $ */
+/** @file
+ * VBox Qt GUI - UIGuestControlConsole class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIGuestControlConsole_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIGuestControlConsole_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+# include <QPlainTextEdit>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuest.h"
+
+class UIGuestControlInterface;
+/** QPlainTextEdit extension to provide a simple terminal like widget. */
+class UIGuestControlConsole : public QPlainTextEdit
+{
+
+ Q_OBJECT;
+
+
+public:
+
+ UIGuestControlConsole(const CGuest &comGuest, QWidget* parent = 0);
+ /* @p strOutput is displayed in the console */
+ void putOutput(const QString &strOutput);
+
+protected:
+
+ void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+ void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ void mouseDoubleClickEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ void contextMenuEvent(QContextMenuEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ void sltOutputReceived(const QString &strOutput);
+
+private:
+
+ typedef QVector<QString> CommandHistory;
+ typedef QMap<QString, int> TabDictionary;
+
+ void reset();
+ void startNextLine();
+ /** Return the text of the curent line */
+ QString getCommandString();
+ /** Replaces the content of the last line with m_strPromt + @p stringNewContent */
+ void replaceLineContent(const QString &stringNewContent);
+ /** Get next/prev command from history. Return @p originalString if history is empty. */
+ QString getNextCommandFromHistory(const QString &originalString = QString());
+ QString getPreviousCommandFromHistory(const QString &originalString = QString());
+ void completeByTab();
+ void commandEntered(const QString &strCommand);
+
+ /* Return a list of words that start with @p strSearch */
+ QList<QString> matchedWords(const QString &strSearch) const;
+ CGuest m_comGuest;
+ const QString m_strGreet;
+ const QString m_strPrompt;
+ TabDictionary m_tabDictinary;
+ /* A vector of entered commands */
+ CommandHistory m_tCommandHistory;
+ unsigned m_uCommandHistoryIndex;
+ UIGuestControlInterface *m_pControlInterface;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIGuestControlConsole_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlDefs.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlDefs.h
new file mode 100644
index 00000000..af67e437
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlDefs.h
@@ -0,0 +1,41 @@
+/* $Id: UIGuestControlDefs.h $ */
+/** @file
+ * VBox Qt GUI - Header with definitions and functions related to settings configuration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIGuestControlDefs_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIGuestControlDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+enum FileManagerLogType
+{
+ FileManagerLogType_Info,
+ FileManagerLogType_Error,
+ FileManagerLogType_Max
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIGuestControlDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlInterface.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlInterface.cpp
new file mode 100644
index 00000000..d12ca748
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlInterface.cpp
@@ -0,0 +1,745 @@
+/* $Id: UIGuestControlInterface.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGuestControlInterface class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIErrorString.h"
+#include "UIGuestControlInterface.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CFsObjInfo.h"
+#include "CGuestDirectory.h"
+#include "CGuestProcess.h"
+#include "CGuestSession.h"
+#include "CGuestFsObjInfo.h"
+
+/* Misc. includes: */
+#include <iprt/err.h>
+#include <iprt/getopt.h>
+
+
+#define GCTLCMD_COMMON_OPT_USER 999 /**< The --username option number. */
+#define GCTLCMD_COMMON_OPT_PASSWORD 998 /**< The --password option number. */
+#define GCTLCMD_COMMON_OPT_PASSWORD_FILE 997 /**< The --password-file option number. */
+#define GCTLCMD_COMMON_OPT_DOMAIN 996 /**< The --domain option number. */
+#define GCTLCMD_COMMON_OPT_SESSION_NAME 995 /**< The --sessionname option number. */
+#define GCTLCMD_COMMON_OPT_SESSION_ID 994 /**< The --sessionid option number. */
+
+#define RETURN_ERROR(strError) \
+ do { \
+ m_strStatus.append(strError); \
+ return false; \
+ } while (0)
+
+#define RETURN_MESSAGE(strMessage) \
+ do { \
+ m_strStatus.append(strMessage); \
+ return true; \
+ } while (0)
+
+#define GCTLCMD_COMMON_OPTION_DEFS() \
+ { "--username", GCTLCMD_COMMON_OPT_USER, RTGETOPT_REQ_STRING }, \
+ { "--passwordfile", GCTLCMD_COMMON_OPT_PASSWORD_FILE, RTGETOPT_REQ_STRING }, \
+ { "--password", GCTLCMD_COMMON_OPT_PASSWORD, RTGETOPT_REQ_STRING }, \
+ { "--domain", GCTLCMD_COMMON_OPT_DOMAIN, RTGETOPT_REQ_STRING }, \
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING }, \
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+
+#define HANDLE_COMMON_OPTION_DEFS() \
+ case GCTLCMD_COMMON_OPT_USER: \
+ commandData.m_strUserName = ValueUnion.psz; \
+ break; \
+ case GCTLCMD_COMMON_OPT_PASSWORD: \
+ commandData.m_strPassword = ValueUnion.psz; \
+ break;
+
+/* static */ QString UIGuestControlInterface::getFsObjTypeString(KFsObjType type)
+{
+ QString strType;
+ switch(type)
+ {
+ case KFsObjType_Unknown:
+ strType = "Unknown";
+ break;
+ case KFsObjType_Fifo:
+ strType = "Fifo";
+ break;
+ case KFsObjType_DevChar:
+ strType = "DevChar";
+ break;
+ case KFsObjType_Directory:
+ strType = "Directory";
+ break;
+ case KFsObjType_DevBlock:
+ strType = "DevBlock";
+ break;
+ case KFsObjType_File:
+ strType = "File";
+ break;
+ case KFsObjType_Symlink:
+ strType = "Symlink";
+ break;
+ case KFsObjType_Socket:
+ strType = "Socket";
+ break;
+ case KFsObjType_WhiteOut:
+ strType = "WhiteOut";
+ break;
+ default:
+ strType = "Unknown";
+ break;
+ }
+ return strType;
+};
+
+QString generateErrorString(int getOptErrorCode, const RTGETOPTUNION &/*valueUnion*/)
+{
+ QString errorString;
+ // if (valueUnion.pDef)
+ // {
+ // if (valueUnion.pDef->pszLong)
+ // {
+ // errorString = QString(valueUnion.pDef->pszLong);
+ // }
+ // }
+
+ switch (getOptErrorCode)
+ {
+ case VERR_GETOPT_UNKNOWN_OPTION:
+ errorString = errorString.append("RTGetOpt: Command line option not recognized.");
+ break;
+ case VERR_GETOPT_REQUIRED_ARGUMENT_MISSING:
+ errorString = errorString.append("RTGetOpt: Command line option needs argument.");
+ break;
+ case VERR_GETOPT_INVALID_ARGUMENT_FORMAT:
+ errorString = errorString.append("RTGetOpt: Command line option has argument with bad format.");
+ break;
+ case VINF_GETOPT_NOT_OPTION:
+ errorString = errorString.append("RTGetOpt: Not an option.");
+ break;
+ case VERR_GETOPT_INDEX_MISSING:
+ errorString = errorString.append("RTGetOpt: Command line option needs an index.");
+ break;
+ default:
+ break;
+ }
+ return errorString;
+}
+
+/** Common option definitions: */
+class CommandData
+{
+public:
+ CommandData()
+ : m_bSessionIdGiven(false)
+ , m_bSessionNameGiven(false)
+ , m_bCreateParentDirectories(false){}
+ QString m_strUserName;
+ QString m_strPassword;
+ QString m_strExePath;
+ QString m_strSessionName;
+ QString m_strPath;
+ ULONG m_uSessionId;
+ QString m_strDomain;
+ bool m_bSessionIdGiven;
+ bool m_bSessionNameGiven;
+ /* Create the whole path during mkdir */
+ bool m_bCreateParentDirectories;
+ QVector<QString> m_arguments;
+ QVector<QString> m_environmentChanges;
+};
+
+UIGuestControlInterface::UIGuestControlInterface(QObject* parent, const CGuest &comGuest)
+ :QObject(parent)
+ , m_comGuest(comGuest)
+ , m_strHelp("[common-options]\t[--username <name>] [--domain <domain>]\n"
+ "\t\t[--passwordfile <file> | --password <password>]\n"
+ "start\t\t[common-options]\n"
+ "\t\t[--exe <path to executable>] [--timeout <msec>]\n"
+ "\t\t[--sessionid <id> | [sessionname <name>]]\n"
+ "\t\t[-E|--putenv <NAME>[=<VALUE>]] [--unquoted-args]\n"
+ "\t\t[--ignore-orphaned-processes] [--profile]\n"
+ "\t\t-- <program/arg0> [argument1] ... [argumentN]]\n"
+ "createsession\t\t[common-options] [--sessionname <name>]\n"
+ "mkdir\t\t[common-options]\n"
+ "\t\t[-P|--parents] [<guest directory>\n"
+ "\t\t[--sessionid <id> | --sessionname <name>]\n"
+ "stat|ls\t\t[common-options]\n"
+ "\t\t[--sessionid <id> | --sessionname <name>]\n"
+ "list\n"
+ )
+{
+ prepareSubCommandHandlers();
+}
+
+bool UIGuestControlInterface::handleMkdir(int argc , char** argv)
+{
+
+ CommandData commandData;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--sessionname", GCTLCMD_COMMON_OPT_SESSION_NAME, RTGETOPT_REQ_STRING },
+ { "--sessionid", GCTLCMD_COMMON_OPT_SESSION_ID, RTGETOPT_REQ_UINT32 },
+ { "--parents", 'P', RTGETOPT_REQ_NOTHING }
+ };
+
+ int ch;
+ bool pathFound = false;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /* ignore 0th element (command) */, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ HANDLE_COMMON_OPTION_DEFS()
+ case GCTLCMD_COMMON_OPT_SESSION_NAME:
+ commandData.m_bSessionNameGiven = true;
+ commandData.m_strSessionName = ValueUnion.psz;
+ break;
+ case GCTLCMD_COMMON_OPT_SESSION_ID:
+ commandData.m_bSessionIdGiven = true;
+ commandData.m_uSessionId = ValueUnion.i32;
+ break;
+ case 'P':
+ commandData.m_bCreateParentDirectories = true;
+ break;
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pathFound)
+ {
+ commandData.m_strPath = ValueUnion.psz;
+ pathFound = true;
+ }
+ /* Allow only a single NOT_OPTION */
+ else
+ RETURN_ERROR(generateErrorString(ch, ValueUnion));
+
+ break;
+ default:
+ RETURN_ERROR(generateErrorString(ch, ValueUnion));
+ }
+ }
+ if (commandData.m_strPath.isEmpty())
+ RETURN_ERROR(QString(m_strHelp).append("Syntax error! No path is given\n"));
+
+ CGuestSession guestSession;
+ if (!findOrCreateSession(commandData, guestSession) || !guestSession.isOk())
+ return false;
+
+
+ //const QString &strErr = comProgressInstall.GetErrorInfo().GetText();
+ QVector<KDirectoryCreateFlag> creationFlags;
+ if (commandData.m_bCreateParentDirectories)
+ creationFlags.push_back(KDirectoryCreateFlag_None);
+ else
+ creationFlags.push_back(KDirectoryCreateFlag_Parents);
+
+ guestSession.DirectoryCreate(commandData.m_strPath, 0 /*ULONG aMode*/, creationFlags);
+
+ //startProcess(commandData, guestSession);
+ return true;
+}
+
+bool UIGuestControlInterface::handleStat(int argc, char** argv)
+{
+ CommandData commandData;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--sessionname", GCTLCMD_COMMON_OPT_SESSION_NAME, RTGETOPT_REQ_STRING },
+ { "--sessionid", GCTLCMD_COMMON_OPT_SESSION_ID, RTGETOPT_REQ_UINT32 }
+ };
+
+ int ch;
+ bool pathFound = false;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /* ignore 0th element (command) */, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ HANDLE_COMMON_OPTION_DEFS()
+ case GCTLCMD_COMMON_OPT_SESSION_NAME:
+ commandData.m_bSessionNameGiven = true;
+ commandData.m_strSessionName = ValueUnion.psz;
+ break;
+ case GCTLCMD_COMMON_OPT_SESSION_ID:
+ commandData.m_bSessionIdGiven = true;
+ commandData.m_uSessionId = ValueUnion.i32;
+ break;
+ case 'P':
+ commandData.m_bCreateParentDirectories = true;
+ break;
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pathFound)
+ {
+ commandData.m_strPath = ValueUnion.psz;
+ pathFound = true;
+ }
+ /* Allow only a single NOT_OPTION */
+ else
+ RETURN_ERROR(generateErrorString(ch, ValueUnion));
+
+ break;
+ default:
+ RETURN_ERROR(generateErrorString(ch, ValueUnion));
+ }
+ }
+ if (commandData.m_strPath.isEmpty())
+ RETURN_ERROR(QString(m_strHelp).append("Syntax error! No path is given\n"));
+
+ CGuestSession guestSession;
+ if (!findOrCreateSession(commandData, guestSession) || !guestSession.isOk())
+ return false;
+ if (guestSession.GetStatus() != KGuestSessionStatus_Started)
+ RETURN_ERROR("The guest session is not valid");
+
+ bool isADirectory =
+ guestSession.DirectoryExists(commandData.m_strPath, false /*BOOL aFollowSymlinks*/);
+
+ bool isAFile = false;
+ if (!isADirectory)
+ isAFile = guestSession.FileExists(commandData.m_strPath, false /*BOOL aFollowSymlinks*/);
+
+ if (!isADirectory && !isAFile)
+ RETURN_ERROR("Specified object does not exist");
+
+ CGuestFsObjInfo fsObjectInfo = guestSession.FsObjQueryInfo(commandData.m_strPath, false /*BOOL aFollowSymlinks*/);
+ if (!fsObjectInfo.isOk())
+ RETURN_ERROR("Cannot get object info");
+ QString strObjectInfo = getFsObjInfoString<CGuestFsObjInfo>(fsObjectInfo);
+
+ /* In case it is a directory get a list of its content: */
+ if (isADirectory)
+ {
+ QVector<KDirectoryOpenFlag> aFlags;
+ aFlags.push_back(KDirectoryOpenFlag_None);
+ CGuestDirectory directory = guestSession.DirectoryOpen(commandData.m_strPath, /*aFilter*/ "", aFlags);
+ if (directory.isOk())
+ {
+ CFsObjInfo directoryInfo = directory.Read();
+ while (directoryInfo.isOk())
+ {
+ strObjectInfo.append("\n");
+ strObjectInfo.append(getFsObjInfoString<CFsObjInfo>(directoryInfo));
+ directoryInfo = directory.Read();
+ }
+ }
+ }
+ RETURN_MESSAGE(strObjectInfo);
+}
+
+bool UIGuestControlInterface::handleList(int, char**)
+{
+ if (!m_comGuest.isOk())
+ RETURN_ERROR("The guest session is not valid");
+
+ QString strSessionInfo;
+ QVector<CGuestSession> sessions = m_comGuest.GetSessions();
+ if (sessions.isEmpty())
+ {
+ strSessionInfo.append("No guest sessions");
+ RETURN_MESSAGE(strSessionInfo);
+ }
+ strSessionInfo += QString("Listing %1 guest sessions in total:\n").arg(QString::number(sessions.size()));
+ //strSessionInfo += QString("\t%1\t%2\n").arg("Session Name").arg("Session Id");
+
+ for (int i = 0; i < sessions.size(); ++i)
+ {
+ strSessionInfo += QString("\tName: %1\t\tID: %2\n").arg(sessions[i].GetName()).arg(QString::number(sessions[i].GetId()));
+ QVector<CGuestProcess> processes = sessions[i].GetProcesses();
+ strSessionInfo += QString("\t%1 guest prcesses for this session:\n").arg(QString::number(processes.size()));
+
+ for (int j = 0; j < processes.size(); ++j)
+ {
+ strSessionInfo += QString("\t\tName: %1\t\tID: %2\n").arg(processes[j].GetName()).arg(QString::number(processes[j].GetPID()));
+
+ }
+ }
+ RETURN_MESSAGE(strSessionInfo);
+}
+
+bool UIGuestControlInterface::handleStart(int argc, char** argv)
+{
+ enum kGstCtrlRunOpt
+ {
+ kGstCtrlRunOpt_IgnoreOrphanedProcesses = 1000,
+ kGstCtrlRunOpt_NoProfile, /** @todo Deprecated and will be removed soon; use kGstCtrlRunOpt_Profile instead, if needed. */
+ kGstCtrlRunOpt_Profile,
+ kGstCtrlRunOpt_Dos2Unix,
+ kGstCtrlRunOpt_Unix2Dos,
+ kGstCtrlRunOpt_WaitForStdOut,
+ kGstCtrlRunOpt_NoWaitForStdOut,
+ kGstCtrlRunOpt_WaitForStdErr,
+ kGstCtrlRunOpt_NoWaitForStdErr
+ };
+
+ CommandData commandData;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--sessionname", GCTLCMD_COMMON_OPT_SESSION_NAME, RTGETOPT_REQ_STRING },
+ { "--sessionid", GCTLCMD_COMMON_OPT_SESSION_ID, RTGETOPT_REQ_UINT32 },
+ { "--putenv", 'E', RTGETOPT_REQ_STRING },
+ { "--exe", 'e', RTGETOPT_REQ_STRING },
+ { "--timeout", 't', RTGETOPT_REQ_UINT32 },
+ { "--unquoted-args", 'u', RTGETOPT_REQ_NOTHING },
+ { "--ignore-orphaned-processes", kGstCtrlRunOpt_IgnoreOrphanedProcesses, RTGETOPT_REQ_NOTHING },
+ { "--no-profile", kGstCtrlRunOpt_NoProfile, RTGETOPT_REQ_NOTHING }, /** @todo Deprecated. */
+ { "--profile", kGstCtrlRunOpt_Profile, RTGETOPT_REQ_NOTHING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /* ignore 0th element (command) */, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ HANDLE_COMMON_OPTION_DEFS()
+ case GCTLCMD_COMMON_OPT_SESSION_NAME:
+ commandData.m_bSessionNameGiven = true;
+ commandData.m_strSessionName = ValueUnion.psz;
+ break;
+ case GCTLCMD_COMMON_OPT_SESSION_ID:
+ commandData.m_bSessionIdGiven = true;
+ commandData.m_uSessionId = ValueUnion.i32;
+ break;
+ case 'e':
+ commandData.m_strExePath = ValueUnion.psz;
+ break;
+ default:
+ RETURN_ERROR(generateErrorString(ch, ValueUnion));
+ }
+ }
+ if (commandData.m_strExePath.isEmpty())
+ RETURN_ERROR(QString(m_strHelp).append("Syntax error! No executable is given\n"));
+
+ CGuestSession guestSession;
+ if (!findOrCreateSession(commandData, guestSession) || !guestSession.isOk())
+ return false;
+ startProcess(commandData, guestSession);
+ return true;
+}
+
+bool UIGuestControlInterface::findOrCreateSession(const CommandData &commandData, CGuestSession &outGuestSession)
+{
+ if (commandData.m_bSessionNameGiven && commandData.m_strSessionName.isEmpty())
+ RETURN_ERROR(QString(m_strHelp).append("'Session Name' is not name valid\n"));
+
+ /* Check if sessionname and sessionid are both supplied */
+ if (commandData.m_bSessionIdGiven && commandData.m_bSessionNameGiven)
+ RETURN_ERROR(QString(m_strHelp).append("Both 'Session Name' and 'Session Id' are supplied\n"));
+
+ /* If sessionid is given then look for the session. if not found return without starting the process: */
+ else if (commandData.m_bSessionIdGiven && !commandData.m_bSessionNameGiven)
+ {
+ if (!findSession(commandData.m_uSessionId, outGuestSession))
+ {
+ RETURN_ERROR(QString(m_strHelp).append("No session with id %1 found.\n").arg(commandData.m_uSessionId));
+ }
+ else
+ return true;
+ }
+ /* If sessionname is given then look for the session. if not try to create a session with the provided name: */
+ else if (!commandData.m_bSessionIdGiven && commandData.m_bSessionNameGiven)
+ {
+ if (!findSession(commandData.m_strSessionName, outGuestSession))
+ {
+ if (!createSession(commandData, outGuestSession))
+ return false;
+ else
+ return true;
+ }
+ else
+ return true;
+ }
+ /* search within the existing CGuestSessions and return a valid one if found: */
+ if (findAValidGuestSession(outGuestSession))
+ return true;
+ /* if neither sessionname and session id is given then create a new session */
+ if (!createSession(commandData, outGuestSession))
+ return false;
+ return true;
+}
+
+bool UIGuestControlInterface::findAValidGuestSession(CGuestSession &outGuestSession)
+{
+ if (!m_comGuest.isOk())
+ return false;
+
+ QVector<CGuestSession> sessions = m_comGuest.GetSessions();
+ for (int i = 0; i < sessions.size(); ++i)
+ {
+ if (sessions[i].isOk() && sessions[i].GetStatus() == KGuestSessionStatus_Started)
+ {
+ outGuestSession = sessions[i];
+ return true;
+ }
+ }
+ return false;
+}
+
+bool UIGuestControlInterface::handleHelp(int, char**)
+{
+ emit sigOutputString(m_strHelp);
+ return true;
+}
+
+bool UIGuestControlInterface::handleCreateSession(int argc, char** argv)
+{
+ CommandData commandData;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--sessionname", GCTLCMD_COMMON_OPT_SESSION_NAME, RTGETOPT_REQ_STRING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ HANDLE_COMMON_OPTION_DEFS()
+ case GCTLCMD_COMMON_OPT_SESSION_NAME:
+ commandData.m_strSessionName = ValueUnion.psz;
+ if (commandData.m_strSessionName.isEmpty())
+ {
+ RETURN_ERROR(QString("'Session Name' is not name valid\n").append(m_strHelp));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ CGuestSession guestSession;
+ if (!createSession(commandData, guestSession))
+ return false;
+ return true;
+}
+
+bool UIGuestControlInterface::startProcess(const CommandData &commandData, CGuestSession &guestSession)
+{
+ QVector<KProcessCreateFlag> createFlags;
+ createFlags.push_back(KProcessCreateFlag_WaitForProcessStartOnly);
+ CGuestProcess process = guestSession.ProcessCreate(commandData.m_strExePath,
+ commandData.m_arguments,
+ commandData.m_environmentChanges,
+ createFlags,
+ 0);
+ if (!process.isOk())
+ return false;
+ return true;
+}
+
+UIGuestControlInterface::~UIGuestControlInterface()
+{
+}
+
+void UIGuestControlInterface::prepareSubCommandHandlers()
+{
+ m_subCommandHandlers.insert("createsession" , &UIGuestControlInterface::handleCreateSession);
+ m_subCommandHandlers.insert("start", &UIGuestControlInterface::handleStart);
+ m_subCommandHandlers.insert("help" , &UIGuestControlInterface::handleHelp);
+ m_subCommandHandlers.insert("mkdir" , &UIGuestControlInterface::handleMkdir);
+ m_subCommandHandlers.insert("stat" , &UIGuestControlInterface::handleStat);
+ m_subCommandHandlers.insert("ls" , &UIGuestControlInterface::handleStat);
+ m_subCommandHandlers.insert("list" , &UIGuestControlInterface::handleList);
+}
+
+void UIGuestControlInterface::putCommand(const QString &strCommand)
+{
+ if (!isGuestAdditionsAvailable(m_comGuest, "6.1"))
+ {
+ emit sigOutputString("No guest addtions detected. Guest control requires guest additions");
+ return;
+ }
+
+ char **argv;
+ int argc;
+ QByteArray array = strCommand.toLocal8Bit();
+ RTGetOptArgvFromString(&argv, &argc, array.data(), RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, 0);
+ m_strStatus.clear();
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ /* Try to map ValueUnion.psz to a sub command handler: */
+ QString strNoOption(ValueUnion.psz);
+ if (!strNoOption.isNull())
+ {
+ QMap<QString, HandleFuncPtr>::iterator iterator =
+ m_subCommandHandlers.find(strNoOption);
+ if (iterator != m_subCommandHandlers.end())
+ {
+ (this->*(iterator.value()))(argc, argv);
+ RTGetOptArgvFree(argv);
+ if (!m_strStatus.isEmpty())
+ emit sigOutputString(m_strStatus);
+ return;
+ }
+ else
+ {
+ emit sigOutputString(QString(m_strHelp).append("\nSyntax Error. Unknown Command '%1'").arg(ValueUnion.psz));
+ RTGetOptArgvFree(argv);
+ return;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ if (!m_strStatus.isEmpty())
+ emit sigOutputString(m_strStatus);
+
+ RTGetOptArgvFree(argv);
+}
+
+bool UIGuestControlInterface::findSession(ULONG sessionId, CGuestSession& outSession)
+{
+ if (!m_comGuest.isOk())
+ return false;
+ QVector<CGuestSession> sessionVector = m_comGuest.GetSessions();
+ if (sessionVector.isEmpty())
+ return false;
+ for (int i = 0; i < sessionVector.size(); ++i)
+ {
+ if (sessionVector.at(i).isOk() && sessionId == sessionVector.at(i).GetId())
+ {
+ outSession = sessionVector.at(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool UIGuestControlInterface::findSession(const QString& strSessionName, CGuestSession& outSession)
+{
+ if (!m_comGuest.isOk())
+ return false;
+ QVector<CGuestSession> sessionVector = m_comGuest.FindSession(strSessionName);
+ if (sessionVector.isEmpty())
+ return false;
+ /* Return the first session with @a sessionName */
+ outSession = sessionVector.at(0);
+ return false;
+}
+
+bool UIGuestControlInterface::createSession(const CommandData &commandData, CGuestSession& outSession)
+{
+ if (!m_comGuest.isOk())
+ return false;
+ if (commandData.m_strUserName.isEmpty())
+ RETURN_ERROR("No user name has been given");
+ CGuestSession guestSession = m_comGuest.CreateSession(commandData.m_strUserName,
+ commandData.m_strPassword,
+ commandData.m_strDomain,
+ commandData.m_strSessionName);
+
+ if (!guestSession.isOk())
+ return false;
+
+ /* Wait session to start: */
+ const ULONG waitTimeout = 2000;
+ KGuestSessionWaitResult waitResult = guestSession.WaitFor(KGuestSessionWaitForFlag_Start, waitTimeout);
+ if (waitResult != KGuestSessionWaitResult_Start)
+ return false;
+
+ outSession = guestSession;
+ return true;
+}
+
+/* static */
+bool UIGuestControlInterface::isGuestAdditionsAvailable(const CGuest &guest, const char *pszMinimumVersion)
+{
+ CGuest guestNonConst = const_cast<CGuest&>(guest);
+
+ if (guestNonConst.isNull() || !pszMinimumVersion)
+ return false;
+
+ /* Guest control stuff is in userland: */
+ if (!guestNonConst.GetAdditionsStatus(KAdditionsRunLevelType_Userland))
+ return false;
+
+ if (!guestNonConst.isOk())
+ return false;
+
+ /* Check the related GA facility: */
+ LONG64 iLastUpdatedIgnored;
+ if (guestNonConst.GetFacilityStatus(KAdditionsFacilityType_VBoxService, iLastUpdatedIgnored) != KAdditionsFacilityStatus_Active)
+ return false;
+
+ if (!guestNonConst.isOk())
+ return false;
+
+ QString strGAVersion = guestNonConst.GetAdditionsVersion();
+ if (guestNonConst.isOk())
+ return (RTStrVersionCompare(strGAVersion.toUtf8().constData(), pszMinimumVersion) >= 0);
+
+ return false;
+}
+
+template<typename T>
+QString UIGuestControlInterface::getFsObjInfoString(const T &fsObjectInfo) const
+{
+ QString strObjectInfo;
+ if (!fsObjectInfo.isOk())
+ return strObjectInfo;
+
+ strObjectInfo.append(getFsObjTypeString(fsObjectInfo.GetType()).append("\t"));
+ strObjectInfo.append(fsObjectInfo.GetName().append("\t"));
+ strObjectInfo.append(QString::number(fsObjectInfo.GetObjectSize()).append("\t"));
+
+ /* Currently I dont know a way to convert these into a meaningful date/time: */
+ // strObjectInfo.append("BirthTime", QString::number(fsObjectInfo.GetBirthTime()));
+ // strObjectInfo.append("ChangeTime", QString::number(fsObjectInfo.GetChangeTime()));
+
+ return strObjectInfo;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlInterface.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlInterface.h
new file mode 100644
index 00000000..793d1cfe
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlInterface.h
@@ -0,0 +1,113 @@
+/* $Id: UIGuestControlInterface.h $ */
+/** @file
+ * VBox Qt GUI - UIGuestControlInterface class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIGuestControlInterface_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIGuestControlInterface_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/* Qt includes: */
+#include <QObject>
+#include <QMap>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuest.h"
+
+class UIGuestControlSubCommandBase;
+class CommandData;
+
+/** UIGuestControlInterface parses a command string and issues API calls
+ accordingly to achive guest control related operations */
+class UIGuestControlInterface : public QObject
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigOutputString(const QString &strOutput);
+
+public:
+
+ UIGuestControlInterface(QObject *parent, const CGuest &comGeust);
+ ~UIGuestControlInterface();
+
+ /** Receives a command string */
+ void putCommand(const QString &strCommand);
+
+ /** @name Some utility functions
+ * @{ */
+ /** Pass a non-const ref since for some reason CGuest::GetAdditionsStatus
+ is non-const?! */
+ static bool isGuestAdditionsAvailable(const CGuest &guest, const char *pszMinimumVersion);
+ static QString getFsObjTypeString(KFsObjType type);
+ /** @} */
+
+private slots:
+
+private:
+
+ typedef bool (UIGuestControlInterface::*HandleFuncPtr)(int, char**);
+
+ /** findOrCreateSession parses command options and determines if an existing session
+ to be returned or a new one to be created */
+ bool findOrCreateSession(const CommandData &commandData, CGuestSession &outGuestSession);
+ /** Search a valid gurst session among existing ones, assign @p outGuestSession if found and return true */
+ bool findAValidGuestSession(CGuestSession &outGuestSession);
+ bool findSession(const QString& strSessionName, CGuestSession& outSession);
+ bool findSession(ULONG strSessionId, CGuestSession& outSession);
+ bool createSession(const CommandData &commandData, CGuestSession &outSession);
+
+ void prepareSubCommandHandlers();
+ bool startProcess(const CommandData &commandData, CGuestSession &guestSession);
+ bool createDirectory(const CommandData &commandData, CGuestSession &guestSession);
+
+ /** Handles the 'start' process command */
+ bool handleStart(int, char**);
+ /* Handles the 'help' process command */
+ bool handleHelp(int, char**);
+ /** Handles the 'create' session command */
+ bool handleCreateSession(int, char**);
+ /** Handles the 'mkdir' session command to create guest directories */
+ bool handleMkdir(int, char**);
+ bool handleStat(int, char**);
+ /** Handles the list command and lists all the guest sessions and processes. */
+ bool handleList(int, char**);
+ template<typename T>
+ QString getFsObjInfoString(const T &fsObjectInfo) const;
+
+ CGuest m_comGuest;
+ const QString m_strHelp;
+ QString m_strStatus;
+ /** A map of function pointers to handleXXXX functions */
+ QMap<QString, HandleFuncPtr> m_subCommandHandlers;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIGuestControlInterface_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlTreeItem.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlTreeItem.cpp
new file mode 100644
index 00000000..acd57789
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlTreeItem.cpp
@@ -0,0 +1,361 @@
+/* $Id: UIGuestControlTreeItem.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGuestSessionTreeItem class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIGuestControlTreeItem.h"
+#include "UIGuestProcessControlWidget.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CGuest.h"
+#include "CEventSource.h"
+#include "CGuestProcessStateChangedEvent.h"
+#include "CGuestSessionStateChangedEvent.h"
+
+
+/*********************************************************************************************************************************
+* UIGuestControlTreeItem implementation. *
+*********************************************************************************************************************************/
+
+UIGuestControlTreeItem::UIGuestControlTreeItem(QITreeWidget *pTreeWidget, const QStringList &strings /* = QStringList() */)
+ :QITreeWidgetItem(pTreeWidget,strings)
+{
+}
+
+UIGuestControlTreeItem::UIGuestControlTreeItem(UIGuestControlTreeItem *pTreeWidgetItem,
+ const QStringList &strings/* = QStringList() */)
+ :QITreeWidgetItem(pTreeWidgetItem, strings)
+{
+
+}
+
+UIGuestControlTreeItem::~UIGuestControlTreeItem()
+{
+}
+
+void UIGuestControlTreeItem::prepare()
+{
+ prepareListener();
+ prepareConnections();
+ setColumnText();
+}
+
+void UIGuestControlTreeItem::prepareListener(CEventSource comEventSource, QVector<KVBoxEventType>& eventTypes)
+{
+ if (!comEventSource.isOk())
+ return;
+ /* Create event listener instance: */
+ m_pQtListener.createObject();
+ m_pQtListener->init(new UIMainEventListener, this);
+ m_comEventListener = CEventListener(m_pQtListener);
+
+ /* Register event listener for CProgress event source: */
+ comEventSource.RegisterListener(m_comEventListener, eventTypes, FALSE /* active? */);
+
+ /* Register event sources in their listeners as well: */
+ m_pQtListener->getWrapped()->registerSource(comEventSource, m_comEventListener);
+}
+
+void UIGuestControlTreeItem::cleanupListener(CEventSource comEventSource)
+{
+ if (!comEventSource.isOk())
+ return;
+ /* Unregister everything: */
+ m_pQtListener->getWrapped()->unregisterSources();
+
+ /* Make sure VBoxSVC is available: */
+ if (!uiCommon().isVBoxSVCAvailable())
+ return;
+
+ /* Unregister event listener for CProgress event source: */
+ comEventSource.UnregisterListener(m_comEventListener);
+}
+
+
+/*********************************************************************************************************************************
+* UIGuestSessionTreeItem implementation. *
+*********************************************************************************************************************************/
+
+UIGuestSessionTreeItem::UIGuestSessionTreeItem(QITreeWidget *pTreeWidget, CGuestSession& guestSession,
+ const QStringList &strings /* = QStringList() */)
+ :UIGuestControlTreeItem(pTreeWidget, strings)
+ , m_comGuestSession(guestSession)
+{
+ prepare();
+ initProcessSubTree();
+}
+
+UIGuestSessionTreeItem::UIGuestSessionTreeItem(UIGuestControlTreeItem *pTreeWidgetItem, CGuestSession& guestSession,
+ const QStringList &strings /* = QStringList() */)
+ :UIGuestControlTreeItem(pTreeWidgetItem, strings)
+ , m_comGuestSession(guestSession)
+{
+ prepare();
+ initProcessSubTree();
+}
+
+UIGuestSessionTreeItem::~UIGuestSessionTreeItem()
+{
+ cleanupListener();
+}
+
+const CGuestSession& UIGuestSessionTreeItem::guestSession() const
+{
+ return m_comGuestSession;
+}
+
+void UIGuestSessionTreeItem::prepareConnections()
+{
+
+ qRegisterMetaType<CGuestProcess>();
+ qRegisterMetaType<CGuestSessionStateChangedEvent>();
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigGuestSessionStatedChanged,
+ this, &UIGuestSessionTreeItem::sltGuestSessionUpdated);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigGuestProcessRegistered,
+ this, &UIGuestSessionTreeItem::sltGuestProcessRegistered);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigGuestProcessUnregistered,
+ this, &UIGuestSessionTreeItem::sltGuestProcessUnregistered);
+}
+
+void UIGuestSessionTreeItem::prepareListener()
+{
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes << KVBoxEventType_OnGuestSessionStateChanged
+ << KVBoxEventType_OnGuestProcessRegistered;
+
+ UIGuestControlTreeItem::prepareListener(m_comGuestSession.GetEventSource(), eventTypes);
+}
+
+void UIGuestSessionTreeItem::cleanupListener()
+{
+ UIGuestControlTreeItem::cleanupListener(m_comGuestSession.GetEventSource());
+}
+
+void UIGuestSessionTreeItem::initProcessSubTree()
+{
+ if (!m_comGuestSession.isOk())
+ return;
+ QVector<CGuestProcess> processes = m_comGuestSession.GetProcesses();
+ for (int i =0; i < processes.size(); ++i)
+ addGuestProcess(processes[i]);
+}
+
+void UIGuestSessionTreeItem::sltGuestSessionUpdated(const CGuestSessionStateChangedEvent& cEvent)
+{
+ if (cEvent.isOk() && m_comGuestSession.isOk() && m_comGuestSession.GetStatus() == KGuestSessionStatus_Error)
+ {
+ CVirtualBoxErrorInfo cErrorInfo = cEvent.GetError();
+ if (cErrorInfo.isOk() && cErrorInfo.GetResultCode() != S_OK)
+ {
+ emit sigGuestSessionErrorText(cErrorInfo.GetText());
+ }
+ }
+ setColumnText();
+ emit sigGuessSessionUpdated();
+}
+
+void UIGuestSessionTreeItem::sltGuestProcessRegistered(CGuestProcess guestProcess)
+{
+ const ULONG waitTimeout = 2000;
+ KProcessWaitResult waitResult = guestProcess.WaitFor(KProcessWaitForFlag_Start, waitTimeout);
+ if (waitResult != KProcessWaitResult_Start)
+ {
+ return ;
+ }
+
+ if (!guestProcess.isOk())
+ return;
+ addGuestProcess(guestProcess);
+}
+
+void UIGuestSessionTreeItem::addGuestProcess(CGuestProcess guestProcess)
+{
+ /* Dont add the tree items for already terminated or currently being terminated
+ guest processes: */
+ KProcessStatus processStatus = guestProcess.GetStatus();
+ if (processStatus != KProcessStatus_Starting &&
+ processStatus != KProcessStatus_Started &&
+ processStatus != KProcessStatus_Paused)
+ return;
+
+ UIGuestProcessTreeItem *newItem = new UIGuestProcessTreeItem(this, guestProcess);
+ connect(newItem, &UIGuestProcessTreeItem::sigGuestProcessErrorText,
+ this, &UIGuestSessionTreeItem::sigGuestSessionErrorText);
+ setExpanded(true);
+}
+
+void UIGuestSessionTreeItem::errorString(QString strError)
+{
+ emit sigGuestSessionErrorText(strError);
+}
+
+KGuestSessionStatus UIGuestSessionTreeItem::status() const
+{
+ if (!m_comGuestSession.isOk())
+ return KGuestSessionStatus_Undefined;
+ return m_comGuestSession.GetStatus();
+}
+
+QString UIGuestSessionTreeItem::propertyString() const
+{
+ QString strProperty;
+ strProperty += QString("<b>%1: </b>%2<br/>").arg(tr("Session Name")).arg(m_comGuestSession.GetName());
+ strProperty += QString("<b>%1: </b>%2<br/>").arg(tr("Session Id")).arg(m_comGuestSession.GetId());
+ strProperty += QString("<b>%1: </b>%2<br/>").arg(tr("Session Status")).arg(gpConverter->toString(m_comGuestSession.GetStatus()));
+ return strProperty;
+}
+
+void UIGuestSessionTreeItem::sltGuestProcessUnregistered(CGuestProcess guestProcess)
+{
+ if (!UIGuestProcessControlWidget::m_fDeleteAfterUnregister)
+ return;
+ for (int i = 0; i < childCount(); ++i)
+ {
+ UIGuestProcessTreeItem* item = dynamic_cast<UIGuestProcessTreeItem*>(child(i));
+ if (item && item->guestProcess() == guestProcess)
+ {
+ delete item;
+ break;
+ }
+ }
+}
+
+void UIGuestSessionTreeItem::setColumnText()
+{
+ if (!m_comGuestSession.isOk())
+ return;
+ setText(0, QString("%1").arg(m_comGuestSession.GetId()));
+ setText(1, QString("%1").arg(m_comGuestSession.GetName()));
+ setText(2, QString("%1").arg(gpConverter->toString(m_comGuestSession.GetStatus())));
+}
+
+
+/*********************************************************************************************************************************
+* UIGuestProcessTreeItem implementation. *
+*********************************************************************************************************************************/
+UIGuestProcessTreeItem::UIGuestProcessTreeItem(QITreeWidget *pTreeWidget, CGuestProcess& guestProcess,
+ const QStringList &strings /* = QStringList() */)
+ :UIGuestControlTreeItem(pTreeWidget, strings)
+ , m_comGuestProcess(guestProcess)
+{
+ prepare();
+}
+
+UIGuestProcessTreeItem::UIGuestProcessTreeItem(UIGuestControlTreeItem *pTreeWidgetItem, CGuestProcess& guestProcess,
+ const QStringList &strings /* = QStringList() */)
+ :UIGuestControlTreeItem(pTreeWidgetItem, strings)
+ , m_comGuestProcess(guestProcess)
+{
+ prepare();
+}
+
+void UIGuestProcessTreeItem::prepareConnections()
+{
+ qRegisterMetaType<CGuestProcessStateChangedEvent>();
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigGuestProcessStateChanged,
+ this, &UIGuestProcessTreeItem::sltGuestProcessUpdated);
+}
+
+UIGuestProcessTreeItem::~UIGuestProcessTreeItem()
+{
+ cleanupListener();
+}
+
+KProcessStatus UIGuestProcessTreeItem::status() const
+{
+ if (!m_comGuestProcess.isOk())
+ return KProcessStatus_Undefined;
+ return m_comGuestProcess.GetStatus();
+}
+
+QString UIGuestProcessTreeItem::propertyString() const
+{
+ QString strProperty;
+ strProperty += QString("<b>%1: </b>%2<br/>").arg(tr("Process Name")).arg(m_comGuestProcess.GetName());
+ strProperty += QString("<b>%1: </b>%2<br/>").arg(tr("Process Id")).arg(m_comGuestProcess.GetPID());
+ strProperty += QString("<b>%1: </b>%2<br/>").arg(tr("Process Status")).arg(gpConverter->toString(m_comGuestProcess.GetStatus()));
+ strProperty += QString("<b>%1: </b>%2<br/>").arg(tr("Executable Path")).arg(m_comGuestProcess.GetExecutablePath());
+
+ strProperty += QString("<b>%1: </b>").arg(tr("Arguments"));
+ QVector<QString> processArguments = m_comGuestProcess.GetArguments();
+ for (int i = 0; i < processArguments.size() - 1; ++i)
+ strProperty += QString("%1, ").arg(processArguments.at(i));
+ if (processArguments.size() > 0)
+ strProperty += QString("%1<br/> ").arg(processArguments.last());
+
+ return strProperty;
+}
+
+void UIGuestProcessTreeItem::prepareListener()
+{
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes << KVBoxEventType_OnGuestProcessStateChanged
+ << KVBoxEventType_OnGuestProcessInputNotify
+ << KVBoxEventType_OnGuestProcessOutput;
+ UIGuestControlTreeItem::prepareListener(m_comGuestProcess.GetEventSource(), eventTypes);
+}
+
+void UIGuestProcessTreeItem::cleanupListener()
+{
+ UIGuestControlTreeItem::cleanupListener(m_comGuestProcess.GetEventSource());
+}
+
+void UIGuestProcessTreeItem::sltGuestProcessUpdated(const CGuestProcessStateChangedEvent &cEvent)
+{
+ if (cEvent.isOk() && m_comGuestProcess.isOk() && m_comGuestProcess.GetStatus() == KProcessStatus_Error)
+ {
+ CVirtualBoxErrorInfo cErrorInfo = cEvent.GetError();
+ if (cErrorInfo.isOk() && cErrorInfo.GetResultCode() != S_OK)
+ emit sigGuestProcessErrorText(cErrorInfo.GetText());
+
+ }
+ setColumnText();
+ KProcessStatus processStatus = m_comGuestProcess.GetStatus();
+ if (processStatus != KProcessStatus_Starting &&
+ processStatus != KProcessStatus_Started &&
+ processStatus != KProcessStatus_Paused)
+ {
+ if (UIGuestProcessControlWidget::m_fDeleteAfterUnregister)
+ this->deleteLater();
+ }
+}
+
+ void UIGuestProcessTreeItem::setColumnText()
+{
+ if (!m_comGuestProcess.isOk())
+ return;
+ setText(0, QString("%1").arg(m_comGuestProcess.GetPID()));
+ setText(1, QString("%1").arg(m_comGuestProcess.GetExecutablePath()));
+ setText(2, QString("%1").arg(gpConverter->toString(m_comGuestProcess.GetStatus())));
+}
+
+const CGuestProcess& UIGuestProcessTreeItem::guestProcess() const
+{
+ return m_comGuestProcess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlTreeItem.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlTreeItem.h
new file mode 100644
index 00000000..91e3f751
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestControlTreeItem.h
@@ -0,0 +1,166 @@
+/* $Id: UIGuestControlTreeItem.h $ */
+/** @file
+ * VBox Qt GUI - UIGuestControlTreeItem class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIGuestControlTreeItem_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIGuestControlTreeItem_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QITreeWidget.h"
+#include "UIMainEventListener.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CEventListener.h"
+#include "CGuestSession.h"
+
+/* Forward declarations: */
+class CEventSource;
+class CGuestProcessStateChangedEvent;
+class CGuestSessionStateChangedEvent;
+
+/** QITreeWidgetItem extension serving as a base class
+ to UIGuestSessionTreeItem and UIGuestProcessTreeItem classes */
+class UIGuestControlTreeItem : public QITreeWidgetItem
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIGuestControlTreeItem(QITreeWidget *pTreeWidget, const QStringList &strings = QStringList());
+ UIGuestControlTreeItem(UIGuestControlTreeItem *pTreeWidgetItem, const QStringList &strings = QStringList());
+ virtual ~UIGuestControlTreeItem();
+ virtual QString propertyString() const = 0;
+
+private slots:
+
+protected:
+
+ void prepareListener(CEventSource comEventSource, QVector<KVBoxEventType>& eventTypes);
+ void cleanupListener(CEventSource comEventSource);
+ void prepare();
+
+ ComObjPtr<UIMainEventListenerImpl> m_pQtListener;
+
+private:
+
+ virtual void prepareListener() = 0;
+ virtual void prepareConnections() = 0;
+ virtual void cleanupListener() = 0;
+ virtual void setColumnText() = 0;
+
+ /** Holds the COM event listener instance. */
+ CEventListener m_comEventListener;
+
+};
+
+/** UIGuestControlTreeItem extension. Represents a instance of CGuestSession
+ and acts as an event listener for this com object. */
+class UIGuestSessionTreeItem : public UIGuestControlTreeItem
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigGuessSessionUpdated();
+ void sigGuestSessionErrorText(QString strError);
+
+public:
+
+ UIGuestSessionTreeItem(QITreeWidget *pTreeWidget, CGuestSession& guestSession, const QStringList &strings = QStringList());
+ UIGuestSessionTreeItem(UIGuestControlTreeItem *pTreeWidgetItem, CGuestSession& guestSession, const QStringList &strings = QStringList());
+ virtual ~UIGuestSessionTreeItem();
+ const CGuestSession& guestSession() const;
+ void errorString(QString strError);
+ KGuestSessionStatus status() const;
+ virtual QString propertyString() const RT_OVERRIDE;
+
+protected:
+
+ void prepareListener(CEventSource comEventSource, QVector<KVBoxEventType>& eventTypes);
+ void cleanupListener(CEventSource comEventSource);
+
+private slots:
+
+ void sltGuestSessionUpdated(const CGuestSessionStateChangedEvent& cEvent);
+ void sltGuestProcessRegistered(CGuestProcess guestProcess);
+ void sltGuestProcessUnregistered(CGuestProcess guestProcess);
+
+
+private:
+
+ virtual void prepareListener() RT_OVERRIDE;
+ virtual void prepareConnections() RT_OVERRIDE;
+ virtual void cleanupListener() RT_OVERRIDE;
+ virtual void setColumnText() RT_OVERRIDE;
+ void addGuestProcess(CGuestProcess guestProcess);
+ void initProcessSubTree();
+ CGuestSession m_comGuestSession;
+};
+
+/** UIGuestControlTreeItem extension. Represents a instance of CGuestProcess
+ and acts as an event listener for this com object. */
+class UIGuestProcessTreeItem : public UIGuestControlTreeItem
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigGuestProcessErrorText(QString strError);
+
+public:
+
+ UIGuestProcessTreeItem(QITreeWidget *pTreeWidget, CGuestProcess& guestProcess, const QStringList &strings = QStringList());
+ UIGuestProcessTreeItem(UIGuestControlTreeItem *pTreeWidgetItem, CGuestProcess& guestProcess, const QStringList &strings = QStringList());
+ const CGuestProcess& guestProcess() const;
+ virtual ~UIGuestProcessTreeItem();
+ KProcessStatus status() const;
+ virtual QString propertyString() const RT_OVERRIDE;
+
+protected:
+
+ void prepareListener(CEventSource comEventSource, QVector<KVBoxEventType>& eventTypes);
+ void cleanupListener(CEventSource comEventSource);
+
+private slots:
+
+ void sltGuestProcessUpdated(const CGuestProcessStateChangedEvent &cEvent);
+
+private:
+
+ virtual void prepareListener() RT_OVERRIDE;
+ virtual void prepareConnections() RT_OVERRIDE;
+ virtual void cleanupListener() RT_OVERRIDE;
+ virtual void setColumnText() RT_OVERRIDE;
+
+ CGuestProcess m_comGuestProcess;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIGuestControlTreeItem_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlDialog.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlDialog.cpp
new file mode 100644
index 00000000..cd504d61
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlDialog.cpp
@@ -0,0 +1,151 @@
+/* $Id: UIGuestProcessControlDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGuestProcessControlDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPushButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIGuestControlConsole.h"
+#include "UIGuestProcessControlDialog.h"
+#include "UICommon.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Class UIGuestProcessControlDialogFactory implementation. *
+*********************************************************************************************************************************/
+
+UIGuestProcessControlDialogFactory::UIGuestProcessControlDialogFactory(UIActionPool *pActionPool /* = 0 */,
+ const CGuest &comGuest /* = CGuest() */,
+ const QString &strMachineName /* = QString() */)
+ : m_pActionPool(pActionPool)
+ , m_comGuest(comGuest)
+ , m_strMachineName(strMachineName)
+{
+}
+
+void UIGuestProcessControlDialogFactory::create(QIManagerDialog *&pDialog, QWidget *pCenterWidget)
+{
+ pDialog = new UIGuestProcessControlDialog(pCenterWidget, m_pActionPool, m_comGuest, m_strMachineName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIGuestProcessControlDialog implementation. *
+*********************************************************************************************************************************/
+
+UIGuestProcessControlDialog::UIGuestProcessControlDialog(QWidget *pCenterWidget,
+ UIActionPool *pActionPool,
+ const CGuest &comGuest,
+ const QString &strMachineName /* = QString() */)
+ : QIWithRetranslateUI<QIManagerDialog>(pCenterWidget)
+ , m_pActionPool(pActionPool)
+ , m_comGuest(comGuest)
+ , m_strMachineName(strMachineName)
+{
+}
+
+void UIGuestProcessControlDialog::retranslateUi()
+{
+ /* Translate window title: */
+ setWindowTitle(tr("%1 - Guest Control").arg(m_strMachineName));
+ /* Translate buttons: */
+ button(ButtonType_Close)->setText(tr("Close"));
+}
+
+void UIGuestProcessControlDialog::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/performance_monitor_32px.png" ,":/performance_monitor_16px.png"));
+#endif
+}
+
+void UIGuestProcessControlDialog::configureCentralWidget()
+{
+ /* Create widget: */
+ UIGuestControlConsole *pConsole = new UIGuestControlConsole(m_comGuest);
+
+ if (pConsole)
+ {
+ /* Configure widget: */
+ setWidget(pConsole);
+ //setWidgetMenu(pWidget->menu());
+#ifdef VBOX_WS_MAC
+ //setWidgetToolbar(pWidget->toolbar());
+#endif
+ /* Add into layout: */
+ centralWidget()->layout()->addWidget(pConsole);
+ }
+}
+
+void UIGuestProcessControlDialog::finalize()
+{
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIGuestProcessControlDialog::loadSettings()
+{
+ /* Invent default window geometry: */
+ const QRect availableGeo = gpDesktop->availableGeometry(this);
+ const int iDefaultWidth = availableGeo.width() / 2;
+ const int iDefaultHeight = availableGeo.height() * 3 / 4;
+ QRect defaultGeo(0, 0, iDefaultWidth, iDefaultHeight);
+
+ /* Load geometry from extradata: */
+ QRect geo = gEDataManager->guestProcessControlDialogGeometry(this, centerWidget(), defaultGeo);
+ LogRel2(("GUI: UIGuestProcessControlDialog: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ restoreGeometry(geo);
+}
+
+void UIGuestProcessControlDialog::saveSettings()
+{
+ /* Save geometry to extradata: */
+ const QRect geo = currentGeometry();
+ LogRel2(("GUI: UIGuestProcessControlDialog: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ gEDataManager->setGuestProcessControlDialogGeometry(geo, isCurrentlyMaximized());
+}
+
+bool UIGuestProcessControlDialog::shouldBeMaximized() const
+{
+ return gEDataManager->guestProcessControlDialogShouldBeMaximized();
+}
+
+void UIGuestProcessControlDialog::sltSetCloseButtonShortCut(QKeySequence shortcut)
+{
+ if (button(ButtonType_Close))
+ button(ButtonType_Close)->setShortcut(shortcut);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlDialog.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlDialog.h
new file mode 100644
index 00000000..48f61ba5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlDialog.h
@@ -0,0 +1,123 @@
+/* $Id: UIGuestProcessControlDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIGuestProcessControlDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIGuestProcessControlDialog_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIGuestProcessControlDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuest.h"
+
+/* Forward declarations: */
+class UIActionPool;
+class UIGuestProcessControlDialog;
+class CGuest;
+
+/** QIManagerDialogFactory extension used as a factory for the Guest Control dialog. */
+class UIGuestProcessControlDialogFactory : public QIManagerDialogFactory
+{
+public:
+
+ UIGuestProcessControlDialogFactory(UIActionPool *pActionPool = 0, const CGuest &comGuest = CGuest(), const QString &strMachineName = QString());
+
+protected:
+
+ /** Creates derived @a pDialog instance.
+ * @param pCenterWidget Passes the widget to center wrt. pCenterWidget. */
+ virtual void create(QIManagerDialog *&pDialog, QWidget *pCenterWidget) RT_OVERRIDE;
+
+ UIActionPool *m_pActionPool;
+ CGuest m_comGuest;
+ QString m_strMachineName;
+};
+
+
+/** QIManagerDialog extension providing GUI with the dialog displaying guest control releated logs. */
+class UIGuestProcessControlDialog : public QIWithRetranslateUI<QIManagerDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Guest Control dialog.
+ * @param pCenterWidget Passes the widget reference to center according to.
+ * @param pActionPool Passes the action-pool reference.
+ * @param comGuest Passes the com-guest reference. */
+ UIGuestProcessControlDialog(QWidget *pCenterWidget, UIActionPool *pActionPool, const CGuest &comGuest, const QString &strMachineName = QString());
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ virtual void configure() RT_OVERRIDE;
+ /** Configures central-widget. */
+ virtual void configureCentralWidget() RT_OVERRIDE;
+ /** Perform final preparations. */
+ virtual void finalize() RT_OVERRIDE;
+ /** Loads dialog setting from extradata. */
+ virtual void loadSettings() RT_OVERRIDE;
+
+ /** Saves dialog setting into extradata. */
+ virtual void saveSettings() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Functions related to geometry restoration.
+ * @{ */
+ /** Returns whether the window should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ void sltSetCloseButtonShortCut(QKeySequence shortcut);
+
+private:
+
+ UIActionPool *m_pActionPool;
+ CGuest m_comGuest;
+ QString m_strMachineName;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIGuestProcessControlDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlWidget.cpp b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlWidget.cpp
new file mode 100644
index 00000000..3a0dbaf0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlWidget.cpp
@@ -0,0 +1,568 @@
+/* $Id: UIGuestProcessControlWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGuestProcessControlWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QMenu>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIDialogButtonBox.h"
+#include "UIExtraDataManager.h"
+#include "UIGuestControlConsole.h"
+#include "UIGuestControlInterface.h"
+#include "UIGuestControlTreeItem.h"
+#include "UIGuestProcessControlWidget.h"
+#include "QIToolBar.h"
+#include "UIIconPool.h"
+#include "UIVMInformationDialog.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CGuest.h"
+#include "CEventSource.h"
+
+const bool UIGuestProcessControlWidget::m_fDeleteAfterUnregister = false;
+
+/** A QIDialog child to display properties of a guest session on process. */
+class UISessionProcessPropertiesDialog : public QIDialog
+{
+
+ Q_OBJECT;
+
+public:
+
+ UISessionProcessPropertiesDialog(QWidget *pParent = 0, Qt::WindowFlags enmFlags = Qt::WindowFlags());
+ void setPropertyText(const QString &strProperty);
+
+private:
+
+ QVBoxLayout *m_pMainLayout;
+ QTextEdit *m_pInfoEdit;
+ QString m_strProperty;
+};
+
+
+/*********************************************************************************************************************************
+* UIGuestControlTreeWidget definition. *
+*********************************************************************************************************************************/
+
+class UIGuestControlTreeWidget : public QITreeWidget
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigCloseSessionOrProcess();
+ void sigShowProperties();
+
+public:
+
+ UIGuestControlTreeWidget(QWidget *pParent = 0);
+ UIGuestControlTreeItem *selectedItem();
+
+protected:
+
+ void contextMenuEvent(QContextMenuEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ void sltExpandAll();
+ void sltCollapseAll();
+ void sltRemoveAllTerminateSessionsProcesses();
+
+private:
+
+ void expandCollapseAll(bool bFlag);
+};
+
+
+/*********************************************************************************************************************************
+* UISessionProcessPropertiesDialog implementation. *
+*********************************************************************************************************************************/
+
+UISessionProcessPropertiesDialog::UISessionProcessPropertiesDialog(QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
+ :QIDialog(pParent, enmFlags)
+ , m_pMainLayout(new QVBoxLayout)
+ , m_pInfoEdit(new QTextEdit)
+{
+ setLayout(m_pMainLayout);
+
+ if (m_pMainLayout)
+ m_pMainLayout->addWidget(m_pInfoEdit);
+ if (m_pInfoEdit)
+ {
+ m_pInfoEdit->setReadOnly(true);
+ m_pInfoEdit->setFrameStyle(QFrame::NoFrame);
+ }
+ QIDialogButtonBox *pButtonBox =
+ new QIDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, this);
+ m_pMainLayout->addWidget(pButtonBox);
+ connect(pButtonBox, &QIDialogButtonBox::accepted, this, &UISessionProcessPropertiesDialog::accept);
+}
+
+void UISessionProcessPropertiesDialog::setPropertyText(const QString &strProperty)
+{
+ if (!m_pInfoEdit)
+ return;
+ m_strProperty = strProperty;
+ m_pInfoEdit->setHtml(strProperty);
+}
+
+
+/*********************************************************************************************************************************
+* UIGuestControlTreeWidget implementation. *
+*********************************************************************************************************************************/
+
+UIGuestControlTreeWidget::UIGuestControlTreeWidget(QWidget *pParent /* = 0 */)
+ :QITreeWidget(pParent)
+{
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ setAlternatingRowColors(true);
+}
+
+UIGuestControlTreeItem *UIGuestControlTreeWidget::selectedItem()
+{
+ QList<QTreeWidgetItem*> selectedList = selectedItems();
+ if (selectedList.isEmpty())
+ return 0;
+ UIGuestControlTreeItem *item =
+ dynamic_cast<UIGuestControlTreeItem*>(selectedList[0]);
+ /* Return the firstof the selected items */
+ return item;
+}
+
+void UIGuestControlTreeWidget::contextMenuEvent(QContextMenuEvent *pEvent) /* override */
+{
+ QMenu menu(this);
+ QList<QTreeWidgetItem *> selectedList = selectedItems();
+
+ UIGuestSessionTreeItem *sessionTreeItem = 0;
+ if (!selectedList.isEmpty())
+ sessionTreeItem = dynamic_cast<UIGuestSessionTreeItem*>(selectedList[0]);
+ QAction *pSessionCloseAction = 0;
+ bool fHasAnyItems = topLevelItemCount() != 0;
+ /* Create a guest session related context menu */
+ if (sessionTreeItem)
+ {
+ pSessionCloseAction = menu.addAction(tr("Terminate Session"));
+ if (pSessionCloseAction)
+ connect(pSessionCloseAction, &QAction::triggered,
+ this, &UIGuestControlTreeWidget::sigCloseSessionOrProcess);
+ }
+ UIGuestProcessTreeItem *processTreeItem = 0;
+ if (!selectedList.isEmpty())
+ processTreeItem = dynamic_cast<UIGuestProcessTreeItem*>(selectedList[0]);
+ QAction *pProcessTerminateAction = 0;
+ if (processTreeItem)
+ {
+ pProcessTerminateAction = menu.addAction(tr("Terminate Process"));
+ if (pProcessTerminateAction)
+ {
+ connect(pProcessTerminateAction, &QAction::triggered,
+ this, &UIGuestControlTreeWidget::sigCloseSessionOrProcess);
+ pProcessTerminateAction->setIcon(UIIconPool::iconSet(":/file_manager_delete_16px.png"));
+ }
+ }
+ if (pProcessTerminateAction || pSessionCloseAction)
+ menu.addSeparator();
+
+ QAction *pRemoveAllTerminated = menu.addAction(tr("Remove All Terminated Sessions/Processes"));
+ if (pRemoveAllTerminated)
+ {
+
+ pRemoveAllTerminated->setEnabled(fHasAnyItems);
+ pRemoveAllTerminated->setIcon(UIIconPool::iconSet(":/state_aborted_16px.png"));
+
+ connect(pRemoveAllTerminated, &QAction::triggered,
+ this, &UIGuestControlTreeWidget::sltRemoveAllTerminateSessionsProcesses);
+ }
+
+ // Add actions to expand/collapse all tree items
+ QAction *pExpandAllAction = menu.addAction(tr("Expand All"));
+ if (pExpandAllAction)
+ {
+ pExpandAllAction->setIcon(UIIconPool::iconSet(":/expand_all_16px.png"));
+ connect(pExpandAllAction, &QAction::triggered,
+ this, &UIGuestControlTreeWidget::sltExpandAll);
+ }
+
+ QAction *pCollapseAllAction = menu.addAction(tr("Collapse All"));
+ if (pCollapseAllAction)
+ {
+ pCollapseAllAction->setIcon(UIIconPool::iconSet(":/collapse_all_16px.png"));
+ connect(pCollapseAllAction, &QAction::triggered,
+ this, &UIGuestControlTreeWidget::sltCollapseAll);
+ }
+ menu.addSeparator();
+ QAction *pShowPropertiesAction = menu.addAction(tr("Properties"));
+ if (pShowPropertiesAction)
+ {
+ pShowPropertiesAction->setIcon(UIIconPool::iconSet(":/file_manager_properties_16px.png"));
+ pShowPropertiesAction->setEnabled(fHasAnyItems);
+ connect(pShowPropertiesAction, &QAction::triggered,
+ this, &UIGuestControlTreeWidget::sigShowProperties);
+ }
+
+ menu.exec(pEvent->globalPos());
+}
+
+void UIGuestControlTreeWidget::sltExpandAll()
+{
+ expandCollapseAll(true);
+}
+
+void UIGuestControlTreeWidget::sltCollapseAll()
+{
+ expandCollapseAll(false);
+}
+
+void UIGuestControlTreeWidget::sltRemoveAllTerminateSessionsProcesses()
+{
+ for (int i = 0; i < topLevelItemCount(); ++i)
+ {
+ if (!topLevelItem(i))
+ break;
+ UIGuestSessionTreeItem *pSessionItem = dynamic_cast<UIGuestSessionTreeItem*>(topLevelItem(i));
+
+ if (!pSessionItem)
+ continue;
+
+ if (pSessionItem->status() != KGuestSessionStatus_Starting &&
+ pSessionItem->status() != KGuestSessionStatus_Started)
+ {
+ delete pSessionItem;
+ continue;
+ }
+
+ for (int j = 0; j < topLevelItem(i)->childCount(); ++j)
+ {
+ UIGuestProcessTreeItem *pProcessItem = dynamic_cast<UIGuestProcessTreeItem*>(topLevelItem(i)->child(j));
+
+ if (pProcessItem)
+ {
+ if (pProcessItem->status() != KProcessStatus_Starting &&
+ pProcessItem->status() != KProcessStatus_Started)
+ delete pProcessItem;
+ }
+ }
+ }
+
+}
+
+void UIGuestControlTreeWidget::expandCollapseAll(bool bFlag)
+{
+ for (int i = 0; i < topLevelItemCount(); ++i)
+ {
+ if (!topLevelItem(i))
+ break;
+ topLevelItem(i)->setExpanded(bFlag);
+ for (int j = 0; j < topLevelItem(i)->childCount(); ++j)
+ {
+ if (topLevelItem(i)->child(j))
+ {
+ topLevelItem(i)->child(j)->setExpanded(bFlag);
+ }
+ }
+ }
+}
+
+
+/*********************************************************************************************************************************
+* UIGuestProcessControlWidget implementation. *
+*********************************************************************************************************************************/
+
+UIGuestProcessControlWidget::UIGuestProcessControlWidget(EmbedTo enmEmbedding, const CGuest &comGuest,
+ QWidget *pParent, QString strMachineName /* = QString()*/,
+ bool fShowToolbar /* = false */)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_comGuest(comGuest)
+ , m_pMainLayout(0)
+ , m_pTreeWidget(0)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pToolBar(0)
+ , m_pQtListener(0)
+ , m_fShowToolbar(fShowToolbar)
+ , m_strMachineName(strMachineName)
+{
+ prepareListener();
+ prepareObjects();
+ prepareConnections();
+ prepareToolBar();
+ initGuestSessionTree();
+ retranslateUi();
+}
+
+UIGuestProcessControlWidget::~UIGuestProcessControlWidget()
+{
+ sltCleanupListener();
+}
+
+void UIGuestProcessControlWidget::retranslateUi()
+{
+ if (m_pTreeWidget)
+ {
+ QStringList labels;
+ labels << tr("Session/Process ID") << tr("Session Name/Process Command") << tr("Session/Process Status");
+ m_pTreeWidget->setHeaderLabels(labels);
+ }
+}
+
+
+void UIGuestProcessControlWidget::prepareObjects()
+{
+ /* Create layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (!m_pMainLayout)
+ return;
+
+ /* Configure layout: */
+ m_pMainLayout->setSpacing(0);
+ m_pTreeWidget = new UIGuestControlTreeWidget;
+
+ if (m_pTreeWidget)
+ {
+ m_pMainLayout->addWidget(m_pTreeWidget);
+ m_pTreeWidget->setColumnCount(3);
+ }
+ updateTreeWidget();
+}
+
+void UIGuestProcessControlWidget::updateTreeWidget()
+{
+ if (!m_pTreeWidget)
+ return;
+
+ m_pTreeWidget->clear();
+ QVector<QITreeWidgetItem> treeItemVector;
+ update();
+}
+
+void UIGuestProcessControlWidget::prepareConnections()
+{
+ qRegisterMetaType<QVector<int> >();
+
+ if (m_pTreeWidget)
+ {
+ connect(m_pTreeWidget, &UIGuestControlTreeWidget::sigCloseSessionOrProcess,
+ this, &UIGuestProcessControlWidget::sltCloseSessionOrProcess);
+ connect(m_pTreeWidget, &UIGuestControlTreeWidget::sigShowProperties,
+ this, &UIGuestProcessControlWidget::sltShowProperties);
+ }
+
+ if (m_pQtListener)
+ {
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigGuestSessionRegistered,
+ this, &UIGuestProcessControlWidget::sltGuestSessionRegistered);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigGuestSessionUnregistered,
+ this, &UIGuestProcessControlWidget::sltGuestSessionUnregistered);
+ }
+}
+
+void UIGuestProcessControlWidget::sltGuestSessionsUpdated()
+{
+ updateTreeWidget();
+}
+
+void UIGuestProcessControlWidget::sltCloseSessionOrProcess()
+{
+ if (!m_pTreeWidget)
+ return;
+ UIGuestControlTreeItem *selectedTreeItem =
+ m_pTreeWidget->selectedItem();
+ if (!selectedTreeItem)
+ return;
+ UIGuestProcessTreeItem *processTreeItem =
+ dynamic_cast<UIGuestProcessTreeItem*>(selectedTreeItem);
+ if (processTreeItem)
+ {
+ CGuestProcess guestProcess = processTreeItem->guestProcess();
+ if (guestProcess.isOk())
+ {
+ guestProcess.Terminate();
+ }
+ return;
+ }
+ UIGuestSessionTreeItem *sessionTreeItem =
+ dynamic_cast<UIGuestSessionTreeItem*>(selectedTreeItem);
+ if (!sessionTreeItem)
+ return;
+ CGuestSession guestSession = sessionTreeItem->guestSession();
+ if (!guestSession.isOk())
+ return;
+ guestSession.Close();
+}
+
+void UIGuestProcessControlWidget::sltShowProperties()
+{
+ UIGuestControlTreeItem *pItem = m_pTreeWidget->selectedItem();
+ if (!pItem)
+ return;
+
+ UISessionProcessPropertiesDialog *pPropertiesDialog = new UISessionProcessPropertiesDialog(this);
+ if (!m_strMachineName.isEmpty())
+ {
+ pPropertiesDialog->setWindowTitle(m_strMachineName);
+ }
+ if (!pPropertiesDialog)
+ return;
+
+ pPropertiesDialog->setPropertyText(pItem->propertyString());
+ pPropertiesDialog->exec();
+
+ delete pPropertiesDialog;
+}
+
+void UIGuestProcessControlWidget::prepareListener()
+{
+ /* Create event listener instance: */
+ m_pQtListener.createObject();
+ m_pQtListener->init(new UIMainEventListener, this);
+ m_comEventListener = CEventListener(m_pQtListener);
+
+ /* Get CProgress event source: */
+ CEventSource comEventSource = m_comGuest.GetEventSource();
+ AssertWrapperOk(comEventSource);
+
+ /* Enumerate all the required event-types: */
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes << KVBoxEventType_OnGuestSessionRegistered;
+
+
+ /* Register event listener for CProgress event source: */
+ comEventSource.RegisterListener(m_comEventListener, eventTypes, FALSE /* active? */);
+ AssertWrapperOk(comEventSource);
+
+ /* Register event sources in their listeners as well: */
+ m_pQtListener->getWrapped()->registerSource(comEventSource, m_comEventListener);
+}
+
+void UIGuestProcessControlWidget::prepareToolBar()
+{
+ /* Create toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ if (m_pToolBar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+
+ /* Add toolbar actions: */
+ m_pToolBar->addSeparator();
+ m_pToolBar->addSeparator();
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pToolBar);
+ }
+#else
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pToolBar);
+#endif
+ }
+}
+
+void UIGuestProcessControlWidget::initGuestSessionTree()
+{
+ if (!m_comGuest.isOk())
+ return;
+
+ QVector<CGuestSession> sessions = m_comGuest.GetSessions();
+ for (int i = 0; i < sessions.size(); ++i)
+ {
+ addGuestSession(sessions.at(i));
+ }
+}
+
+void UIGuestProcessControlWidget::sltGuestSessionRegistered(CGuestSession guestSession)
+{
+ if (!guestSession.isOk())
+ return;
+ addGuestSession(guestSession);
+}
+
+void UIGuestProcessControlWidget::addGuestSession(CGuestSession guestSession)
+{
+ UIGuestSessionTreeItem* sessionTreeItem = new UIGuestSessionTreeItem(m_pTreeWidget, guestSession);
+ connect(sessionTreeItem, &UIGuestSessionTreeItem::sigGuessSessionUpdated,
+ this, &UIGuestProcessControlWidget::sltTreeItemUpdated);
+}
+
+void UIGuestProcessControlWidget::sltTreeItemUpdated()
+{
+ if (m_pTreeWidget)
+ m_pTreeWidget->update();
+}
+
+void UIGuestProcessControlWidget::sltGuestSessionUnregistered(CGuestSession guestSession)
+{
+ if (!guestSession.isOk())
+ return;
+ if (!m_pTreeWidget)
+ return;
+
+ UIGuestSessionTreeItem *selectedItem = NULL;
+
+ for (int i = 0; i < m_pTreeWidget->topLevelItemCount(); ++i)
+ {
+ QTreeWidgetItem *item = m_pTreeWidget->topLevelItem( i );
+
+ UIGuestSessionTreeItem *treeItem = dynamic_cast<UIGuestSessionTreeItem*>(item);
+ if (treeItem && treeItem->guestSession() == guestSession)
+ {
+ selectedItem = treeItem;
+ break;
+ }
+ }
+ if (m_fDeleteAfterUnregister)
+ delete selectedItem;
+}
+
+void UIGuestProcessControlWidget::sltCleanupListener()
+{
+ /* Unregister everything: */
+ m_pQtListener->getWrapped()->unregisterSources();
+
+ /* Make sure VBoxSVC is available: */
+ if (!uiCommon().isVBoxSVCAvailable())
+ return;
+
+ /* Get CProgress event source: */
+ CEventSource comEventSource = m_comGuest.GetEventSource();
+ AssertWrapperOk(comEventSource);
+
+ /* Unregister event listener for CProgress event source: */
+ comEventSource.UnregisterListener(m_comEventListener);
+}
+
+#include "UIGuestProcessControlWidget.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlWidget.h b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlWidget.h
new file mode 100644
index 00000000..d6f3601c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/guestctrl/UIGuestProcessControlWidget.h
@@ -0,0 +1,109 @@
+/* $Id: UIGuestProcessControlWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIGuestProcessControlWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_guestctrl_UIGuestProcessControlWidget_h
+#define FEQT_INCLUDED_SRC_guestctrl_UIGuestProcessControlWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuest.h"
+#include "CEventListener.h"
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIMainEventListener.h"
+
+/* Forward declarations: */
+class QITreeWidget;
+class QVBoxLayout;
+class QSplitter;
+class UIGuestControlConsole;
+class UIGuestControlInterface;
+class UIGuestSessionsEventHandler;
+class UIGuestControlTreeWidget;
+class QIToolBar;
+
+/** QWidget extension
+ * providing GUI with guest session information and control tab in session-information window. */
+class UIGuestProcessControlWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ UIGuestProcessControlWidget(EmbedTo enmEmbedding, const CGuest &comGuest, QWidget *pParent,
+ QString strMachineName = QString(), bool fShowToolbar = false);
+ ~UIGuestProcessControlWidget();
+ /** When true we delete the corresponding tree item as soon as the guest session/process is unregistered. */
+ static const bool m_fDeleteAfterUnregister;
+
+protected:
+
+ void retranslateUi();
+
+private slots:
+
+ void sltGuestSessionsUpdated();
+ void sltGuestSessionRegistered(CGuestSession guestSession);
+ void sltGuestSessionUnregistered(CGuestSession guestSession);
+ void sltTreeItemUpdated();
+ void sltCloseSessionOrProcess();
+ void sltShowProperties();
+ void sltCleanupListener();
+
+private:
+
+ void prepareObjects();
+ void prepareConnections();
+ void prepareToolBar();
+ void prepareListener();
+ void initGuestSessionTree();
+ void updateTreeWidget();
+ void addGuestSession(CGuestSession guestSession);
+
+ CGuest m_comGuest;
+ QVBoxLayout *m_pMainLayout;
+ UIGuestControlTreeWidget *m_pTreeWidget;
+ const EmbedTo m_enmEmbedding;
+ QIToolBar *m_pToolBar;
+
+ /** Holds the Qt event listener instance. */
+ ComObjPtr<UIMainEventListenerImpl> m_pQtListener;
+ /** Holds the COM event listener instance. */
+ CEventListener m_comEventListener;
+ const bool m_fShowToolbar;
+ QString m_strMachineName;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_guestctrl_UIGuestProcessControlWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/hardenedmain.cpp b/src/VBox/Frontends/VirtualBox/src/hardenedmain.cpp
new file mode 100644
index 00000000..ac2386f2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/hardenedmain.cpp
@@ -0,0 +1,119 @@
+/* $Id: hardenedmain.cpp $ */
+/** @file
+ * VBox Qt GUI - Hardened main().
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <VBox/sup.h>
+
+
+/**
+ * No CRT on windows, so cook our own strcmp.
+ *
+ * @returns See man strcmp.
+ * @param psz1 The first string.
+ * @param psz2 The second string.
+ */
+static int MyStrCmp(const char *psz1, const char *psz2)
+{
+ for (;;)
+ {
+ char ch1 = *psz1++;
+ char ch2 = *psz2++;
+ if (ch1 != ch2)
+ return ch1 < ch2 ? -1 : 1;
+ if (!ch1)
+ return 0;
+ }
+}
+
+
+int main(int argc, char **argv, char **envp)
+{
+ /*
+ * Do partial option parsing to see if we're starting a VM and how we're
+ * going about that.
+ *
+ * Note! This must must match the corresponding parsing in main.cpp and
+ * UICommon.cpp exactly, otherwise there will be weird error messages.
+ *
+ * Note! ASSUMES that argv is in an ASCII compatible codeset.
+ */
+ unsigned cOptionsLeft = 4;
+ bool fStartVM = false;
+ bool fSeparateProcess = false;
+ bool fExecuteAllInIem = false;
+ bool fDriverless = false;
+ for (int i = 1; i < argc && cOptionsLeft > 0; ++i)
+ {
+ if ( !MyStrCmp(argv[i], "--startvm")
+ || !MyStrCmp(argv[i], "-startvm"))
+ {
+ cOptionsLeft -= fStartVM == false;
+ fStartVM = true;
+ i++;
+ }
+ else if ( !MyStrCmp(argv[i], "--separate")
+ || !MyStrCmp(argv[i], "-separate"))
+ {
+ cOptionsLeft -= fSeparateProcess == false;
+ fSeparateProcess = true;
+ }
+ else if (!MyStrCmp(argv[i], "--execute-all-in-iem"))
+ {
+ cOptionsLeft -= fExecuteAllInIem == false;
+ fExecuteAllInIem = true;
+ }
+ else if (!MyStrCmp(argv[i], "--driverless"))
+ {
+ cOptionsLeft -= fDriverless == false;
+ fDriverless = true;
+ }
+ }
+
+ /*
+ * Convert the command line options to SUPSECMAIN_FLAGS_XXX flags
+ * and call the hardened main code.
+ */
+ uint32_t fFlags = SUPSECMAIN_FLAGS_TRUSTED_ERROR;
+#ifdef RT_OS_DARWIN
+ fFlags |= SUPSECMAIN_FLAGS_LOC_OSX_HLP_APP;
+#endif
+ if (!fStartVM || fSeparateProcess)
+ fFlags |= SUPSECMAIN_FLAGS_DONT_OPEN_DEV;
+ else
+ {
+ if (fExecuteAllInIem)
+ fFlags |= SUPSECMAIN_FLAGS_DRIVERLESS_IEM_ALLOWED;
+#ifdef VBOX_WITH_DRIVERLESS_NEM_FALLBACK
+ else
+ fFlags |= SUPSECMAIN_FLAGS_DRIVERLESS_NEM_FALLBACK;
+#endif
+ if (fDriverless)
+ fFlags |= SUPSECMAIN_FLAGS_DRIVERLESS;
+ }
+
+ return SUPR3HardenedMain("VirtualBoxVM", fFlags, argc, argv, envp);
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/helpbrowser/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/helpbrowser/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/helpbrowser/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserDialog.cpp b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserDialog.cpp
new file mode 100644
index 00000000..a44a179c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserDialog.cpp
@@ -0,0 +1,181 @@
+/* $Id: UIHelpBrowserDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIHelpBrowserDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#if defined(RT_OS_SOLARIS)
+# include <QFontDatabase>
+#endif
+#include <QLabel>
+#include <QMenuBar>
+#include <QStatusBar>
+
+/* GUI includes: */
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIHelpBrowserDialog.h"
+#include "UIHelpBrowserWidget.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+#include <iprt/assert.h>
+
+
+/*********************************************************************************************************************************
+* Class UIHelpBrowserDialog implementation. *
+*********************************************************************************************************************************/
+
+UIHelpBrowserDialog::UIHelpBrowserDialog(QWidget *pParent, QWidget *pCenterWidget, const QString &strHelpFilePath)
+ : QIWithRetranslateUI<QIWithRestorableGeometry<QMainWindow> >(pParent)
+ , m_strHelpFilePath(strHelpFilePath)
+ , m_pWidget(0)
+ , m_pCenterWidget(pCenterWidget)
+ , m_iGeometrySaveTimerId(-1)
+ , m_pZoomLabel(0)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/log_viewer_find_32px.png", ":/log_viewer_find_16px.png"));
+#endif
+
+ setAttribute(Qt::WA_DeleteOnClose);
+ statusBar()->show();
+ m_pZoomLabel = new QLabel;
+ statusBar()->addPermanentWidget(m_pZoomLabel);
+
+ prepareCentralWidget();
+ loadSettings();
+ retranslateUi();
+}
+
+void UIHelpBrowserDialog::showHelpForKeyword(const QString &strKeyword)
+{
+#ifdef VBOX_WITH_QHELP_VIEWER
+ if (m_pWidget)
+ m_pWidget->showHelpForKeyword(strKeyword);
+#else
+ Q_UNUSED(strKeyword);
+#endif
+}
+
+void UIHelpBrowserDialog::retranslateUi()
+{
+#ifdef VBOX_WITH_QHELP_VIEWER
+ setWindowTitle(UIHelpBrowserWidget::tr("Oracle VM VirtualBox User Manual"));
+#endif
+}
+
+bool UIHelpBrowserDialog::event(QEvent *pEvent)
+{
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ case QEvent::Move:
+ {
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+ break;
+ }
+ case QEvent::Timer:
+ {
+ QTimerEvent *pTimerEvent = static_cast<QTimerEvent*>(pEvent);
+ if (pTimerEvent->timerId() == m_iGeometrySaveTimerId)
+ {
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = -1;
+ saveDialogGeometry();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return QIWithRetranslateUI<QIWithRestorableGeometry<QMainWindow> >::event(pEvent);
+}
+
+
+void UIHelpBrowserDialog::prepareCentralWidget()
+{
+#ifdef VBOX_WITH_QHELP_VIEWER
+ m_pWidget = new UIHelpBrowserWidget(EmbedTo_Dialog, m_strHelpFilePath);
+ AssertPtrReturnVoid(m_pWidget);
+ setCentralWidget((m_pWidget));
+ sltZoomPercentageChanged(m_pWidget->zoomPercentage());
+ connect(m_pWidget, &UIHelpBrowserWidget::sigCloseDialog,
+ this, &UIHelpBrowserDialog::close);
+ connect(m_pWidget, &UIHelpBrowserWidget::sigStatusBarMessage,
+ this, &UIHelpBrowserDialog::sltStatusBarMessage);
+ connect(m_pWidget, &UIHelpBrowserWidget::sigStatusBarVisible,
+ this, &UIHelpBrowserDialog::sltStatusBarVisibilityChange);
+ connect(m_pWidget, &UIHelpBrowserWidget::sigZoomPercentageChanged,
+ this, &UIHelpBrowserDialog::sltZoomPercentageChanged);
+
+ const QList<QMenu*> menuList = m_pWidget->menus();
+ foreach (QMenu *pMenu, menuList)
+ menuBar()->addMenu(pMenu);
+#endif
+}
+
+void UIHelpBrowserDialog::loadSettings()
+{
+ const QRect availableGeo = gpDesktop->availableGeometry(this);
+ int iDefaultWidth = availableGeo.width() / 2;
+ int iDefaultHeight = availableGeo.height() * 3 / 4;
+ QRect defaultGeo(0, 0, iDefaultWidth, iDefaultHeight);
+
+ const QRect geo = gEDataManager->helpBrowserDialogGeometry(this, m_pCenterWidget, defaultGeo);
+ restoreGeometry(geo);
+}
+
+void UIHelpBrowserDialog::saveDialogGeometry()
+{
+ const QRect geo = currentGeometry();
+ gEDataManager->setHelpBrowserDialogGeometry(geo, isCurrentlyMaximized());
+}
+
+bool UIHelpBrowserDialog::shouldBeMaximized() const
+{
+ return gEDataManager->helpBrowserDialogShouldBeMaximized();
+}
+
+void UIHelpBrowserDialog::sltStatusBarMessage(const QString& strLink, int iTimeOut)
+{
+ statusBar()->showMessage(strLink, iTimeOut);
+}
+
+void UIHelpBrowserDialog::sltStatusBarVisibilityChange(bool fVisible)
+{
+ statusBar()->setVisible(fVisible);
+}
+
+void UIHelpBrowserDialog::sltZoomPercentageChanged(int iPercentage)
+{
+ if (m_pZoomLabel)
+ m_pZoomLabel->setText(QString("%1%").arg(QString::number(iPercentage)));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserDialog.h b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserDialog.h
new file mode 100644
index 00000000..d6a23b20
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserDialog.h
@@ -0,0 +1,84 @@
+/* $Id: UIHelpBrowserDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIHelpBrowserDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_helpbrowser_UIHelpBrowserDialog_h
+#define FEQT_INCLUDED_SRC_helpbrowser_UIHelpBrowserDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRestorableGeometry.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QLabel;
+class UIHelpBrowserWidget;
+
+class SHARED_LIBRARY_STUFF UIHelpBrowserDialog : public QIWithRetranslateUI<QIWithRestorableGeometry<QMainWindow> >
+{
+ Q_OBJECT;
+
+public:
+
+ UIHelpBrowserDialog(QWidget *pParent, QWidget *pCenterWidget, const QString &strHelpFilePath);
+ /** A passthru function for QHelpIndexWidget::showHelpForKeyword. */
+ void showHelpForKeyword(const QString &strKeyword);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ virtual void prepareCentralWidget();
+ virtual void loadSettings();
+ virtual void saveDialogGeometry();
+ /** @} */
+
+ /** Returns whether the window should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const RT_OVERRIDE;
+
+private slots:
+
+ void sltStatusBarMessage(const QString& strLink, int iTimeOut);
+ void sltStatusBarVisibilityChange(bool fVisible);
+ void sltZoomPercentageChanged(int iPercentage);
+
+private:
+
+ QString m_strHelpFilePath;
+ UIHelpBrowserWidget *m_pWidget;
+ QWidget *m_pCenterWidget;
+ int m_iGeometrySaveTimerId;
+ QLabel *m_pZoomLabel;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_helpbrowser_UIHelpBrowserDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserWidget.cpp b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserWidget.cpp
new file mode 100644
index 00000000..bdbf700c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserWidget.cpp
@@ -0,0 +1,2277 @@
+/* $Id: UIHelpBrowserWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIHelpBrowserWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QClipboard>
+#include <QComboBox>
+#include <QtGlobal>
+#ifdef VBOX_WITH_QHELP_VIEWER
+# include <QtHelp/QHelpEngine>
+# include <QtHelp/QHelpContentWidget>
+# include <QtHelp/QHelpIndexWidget>
+# include <QtHelp/QHelpSearchEngine>
+# include <QtHelp/QHelpSearchQueryWidget>
+# include <QtHelp/QHelpSearchResultWidget>
+# if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+# include <QtHelp/QHelpLink>
+# endif
+#endif
+#include <QLabel>
+#include <QListWidget>
+#include <QMenu>
+#include <QMouseEvent>
+#include <QtPrintSupport/QPrintDialog>
+#include <QtPrintSupport/QPrinter>
+#include <QSplitter>
+#include <QVBoxLayout>
+#ifdef RT_OS_SOLARIS
+# include <QFontDatabase>
+#endif
+#include <QWidgetAction>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+#include "QITabWidget.h"
+#include "QIToolBar.h"
+#include "QIToolButton.h"
+#include "UIActionPool.h"
+#include "UIExtraDataManager.h"
+#include "UIHelpViewer.h"
+#include "UIHelpBrowserWidget.h"
+#include "UIIconPool.h"
+
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CSystemProperties.h"
+
+#ifdef VBOX_WITH_QHELP_VIEWER
+
+enum HelpBrowserTabs
+{
+ HelpBrowserTabs_TOC = 0,
+ HelpBrowserTabs_Search,
+ HelpBrowserTabs_Bookmarks,
+ HelpBrowserTabs_Index,
+ HelpBrowserTabs_Max
+};
+Q_DECLARE_METATYPE(HelpBrowserTabs);
+
+static const int iBookmarkUrlDataType = 6;
+
+static int iZoomPercentageStep = 20;
+const QPair<int, int> zoomPercentageMinMax = QPair<int, int>(20, 300);
+
+
+/*********************************************************************************************************************************
+* UIZoomMenuAction definition. *
+*********************************************************************************************************************************/
+class UIZoomMenuAction : public QIWithRetranslateUI<QWidgetAction>
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigZoomChanged(int iOperation);
+
+public:
+
+ UIZoomMenuAction(QWidget *pParent = 0);
+ void setZoomPercentage(int iZoomPercentage);
+
+protected:
+
+ void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ void sltZoomOperation();
+
+private:
+
+ void prepare();
+
+ QIToolButton *m_pMinusButton;
+ QIToolButton *m_pResetButton;
+ QIToolButton *m_pPlusButton;
+ QLabel *m_pValueLabel;
+ QLabel *m_pLabel;
+};
+
+
+/*********************************************************************************************************************************
+* UIBookmarksListWidget definition. *
+*********************************************************************************************************************************/
+class UIBookmarksListWidget : public QListWidget
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigBookmarkDoubleClick(const QUrl &url);
+
+public:
+
+ UIBookmarksListWidget(QWidget *pParent = 0);
+
+protected:
+
+ void mouseDoubleClickEvent(QMouseEvent *event) RT_OVERRIDE;
+ void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+};
+
+
+/*********************************************************************************************************************************
+* UIBookmarksListContainer definition. *
+*********************************************************************************************************************************/
+class UIBookmarksListContainer : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigBookmarkDoubleClick(const QUrl &url);
+ void sigListWidgetContextMenuRequest(const QPoint &listWidgetLocalPos);
+
+public:
+
+ UIBookmarksListContainer(QWidget *pParent = 0);
+ void addBookmark(const QUrl &url, const QString &strTitle);
+ /** Return all bookmarks a url, title pair list. */
+ QStringList bookmarks() const;
+ QUrl currentBookmarkUrl();
+
+public:
+
+ void sltDeleteSelectedBookmark();
+ void sltDeleteAllBookmarks();
+
+protected:
+
+ void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+private:
+
+ void prepare();
+ int itemIndex(const QUrl &url);
+
+ QVBoxLayout *m_pMainLayout;
+ UIBookmarksListWidget *m_pListWidget;
+};
+
+/*********************************************************************************************************************************
+* UIHelpBrowserTab definition. *
+*********************************************************************************************************************************/
+
+class UIHelpBrowserTab : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigSourceChanged(const QUrl &url);
+ void sigCopyAvailableChanged(bool fAvailable);
+ void sigTitleUpdate(const QString &strTitle);
+ void sigOpenLinkInNewTab(const QUrl &url, bool fBackground);
+ void sigAddBookmark(const QUrl &url, const QString &strTitle);
+ void sigLinkHighlighted(const QUrl &url);
+ void sigFindInPageWidgetVisibilityChanged(bool fVisible);
+ void sigHistoryChanged(bool fBackwardAvailable, bool fForwardAvailable);
+ void sigMouseOverImage(const QString &strImageName);
+ void sigZoomRequest(UIHelpViewer::ZoomOperation enmZoomOperation);
+
+public:
+
+ UIHelpBrowserTab(const QHelpEngine *pHelpEngine, const QUrl &homeUrl,
+ const QUrl &initialUrl, QWidget *pParent = 0);
+
+ QUrl source() const;
+ void setSource(const QUrl &url);
+ QString documentTitle() const;
+ void setToolBarVisible(bool fVisible);
+ void print(QPrinter &printer);
+ void setZoomPercentage(int iZoomPercentage);
+ void setHelpFileList(const QList<QUrl> &helpFileList);
+ void copySelectedText() const;
+ bool hasSelectedText() const;
+ bool isFindInPageWidgetVisible() const;
+ void findNext();
+ void findPrevious();
+
+public slots:
+
+ void sltFindInPageAction(bool fToggled);
+ void sltHomeAction();
+ void sltForwardAction();
+ void sltBackwardAction();
+ void sltAddBookmarkAction();
+ void sltReloadPageAction();
+
+private slots:
+
+ void sltHistoryChanged();
+ void sltAddressBarIndexChanged(int index);
+ void sltAnchorClicked(const QUrl &link);
+ void sltFindInPageWidgetVisibilityChanged(bool fVisible);
+
+private:
+
+ void prepare(const QUrl &initialUrl);
+ void prepareWidgets(const QUrl &initialUrl);
+ void prepareToolBarAndAddressBar();
+ virtual void retranslateUi() RT_OVERRIDE;
+ void setActionTextAndToolTip(QAction *pAction, const QString &strText, const QString &strToolTip);
+
+ QAction *m_pHomeAction;
+ QAction *m_pForwardAction;
+ QAction *m_pBackwardAction;
+ QAction *m_pAddBookmarkAction;
+ QAction *m_pFindInPageAction;
+ QAction *m_pReloadPageAction;
+
+ QVBoxLayout *m_pMainLayout;
+ QIToolBar *m_pToolBar;
+ QComboBox *m_pAddressBar;
+ UIHelpViewer *m_pContentViewer;
+ const QHelpEngine* m_pHelpEngine;
+ QUrl m_homeUrl;
+};
+
+
+/*********************************************************************************************************************************
+* UIHelpBrowserTabManager definition. *
+*********************************************************************************************************************************/
+
+class UIHelpBrowserTabManager : public QITabWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigSourceChanged(const QUrl &url);
+ void sigAddBookmark(const QUrl &url, const QString &strTitle);
+ /** list.first is tab title and list.second is tab's index. */
+ void sigTabsListChanged(const QStringList &titleList);
+ void sigLinkHighlighted(const QUrl &url);
+ void sigZoomPercentageChanged(int iPercentage);
+ void sigCopyAvailableChanged(bool fAvailable);
+ void sigFindInPageWidgetVisibilityChanged(bool fVisible);
+ void sigHistoryChanged(bool fBackwardAvailable, bool fForwardAvailable);
+ void sigMouseOverImage(const QString &strImageName);
+
+public:
+
+ UIHelpBrowserTabManager(const QHelpEngine *pHelpEngine, const QUrl &homeUrl,
+ const QStringList &urlList, QWidget *pParent = 0);
+ /* Returns the list of urls of all open tabs as QStringList. */
+ QStringList tabUrlList() const;
+ QStringList tabTitleList() const;
+
+ /** Either start with a single tab showin the home url or saved tab(s). Depending on the params. passed to ctor. */
+ void initializeTabs();
+ /* Url of the current tab. */
+ QUrl currentSource() const;
+ void setSource(const QUrl &url, bool fNewTab = false);
+ void setToolBarVisible(bool fVisible);
+ void printCurrent(QPrinter &printer);
+ void switchToTab(int iIndex);
+ int zoomPercentage() const;
+ void setZoomPercentage(int iZoomPercentage);
+ void setHelpFileList(const QList<QUrl> &helpFileList);
+ void copySelectedText() const;
+ bool hasCurrentTabSelectedText() const;
+ bool isFindInPageWidgetVisible() const;
+ void toggleFindInPage(bool fTrigger);
+ void findNext();
+ void findPrevious();
+
+public slots:
+
+ void sltCloseCurrentTab();
+ void sltCloseOtherTabs();
+ void sltHomeAction();
+ void sltAddBookmarkAction();
+ void sltForwardAction();
+ void sltBackwardAction();
+ void sltReloadPageAction();
+ void sltHandleZoomRequest(UIHelpViewer::ZoomOperation enmOperation);
+
+private slots:
+
+ void sltTabTitleChange(const QString &strTitle);
+ void sltOpenLinkInNewTab(const QUrl &url, bool fBackground);
+ void sltTabClose(int iTabIndex);
+ void sltContextMenuTabClose();
+ void sltCurrentChanged(int iTabIndex);
+ void sltShowTabBarContextMenu(const QPoint &pos);
+ void sltCloseOtherTabsContextMenuAction();
+ void sltCopyAvailableChanged(bool fAvailable);
+
+private:
+
+ void prepare();
+ void clearAndDeleteTabs();
+ void addNewTab(const QUrl &initialUrl, bool fBackground);
+ /** Check if lists of tab url/title has changed. if so emit a signal. */
+ void updateTabUrlTitleList();
+ /** Closes all tabs other than the one with index @param iTabIndex. */
+ void closeAllTabsBut(int iTabIndex);
+ /* Returns the tab index with @Url if there is one. Returns -1 otherwise. */
+ int findTab(const QUrl &Url) const;
+
+ const QHelpEngine* m_pHelpEngine;
+ QUrl m_homeUrl;
+ QStringList m_savedUrlList;
+ /** Immediately switch the newly created tab. Otherwise open the tab in background. */
+ bool m_fSwitchToNewTab;
+ bool m_fToolBarVisible;
+ QStringList m_tabTitleList;
+ QList<QUrl> m_helpFileList;
+ /** As percentage. */
+ int m_iZoomPercentage;
+};
+
+
+/*********************************************************************************************************************************
+* UIZoomMenuAction implementation. *
+*********************************************************************************************************************************/
+UIZoomMenuAction::UIZoomMenuAction(QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QWidgetAction>(pParent)
+ , m_pMinusButton(0)
+ , m_pResetButton(0)
+ , m_pPlusButton(0)
+ , m_pValueLabel(0)
+ , m_pLabel(0)
+{
+ prepare();
+ retranslateUi();
+}
+
+void UIZoomMenuAction::setZoomPercentage(int iZoomPercentage)
+{
+ if (m_pValueLabel)
+ m_pValueLabel->setText(QString("%1%2").arg(QString::number(iZoomPercentage)).arg("%"));
+}
+
+void UIZoomMenuAction::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(UIHelpBrowserWidget::tr("Zoom"));
+}
+
+void UIZoomMenuAction::prepare()
+{
+ QWidget *pWidget = new QWidget;
+ setDefaultWidget(pWidget);
+
+ QHBoxLayout *pMainLayout = new QHBoxLayout(pWidget);
+ pMainLayout->setSpacing(0);
+ AssertReturnVoid(pMainLayout);
+
+ m_pLabel = new QLabel;
+ m_pMinusButton = new QIToolButton;
+ m_pResetButton = new QIToolButton;
+ m_pPlusButton = new QIToolButton;
+ m_pValueLabel = new QLabel;
+ m_pValueLabel->setAlignment(Qt::AlignCenter);
+ m_pValueLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
+ AssertReturnVoid(m_pMinusButton &&
+ m_pResetButton &&
+ m_pPlusButton &&
+ m_pValueLabel);
+
+ m_pMinusButton->setIcon(UIIconPool::iconSet(":/help_browser_minus_16px.png", ":/help_browser_minus_disabled_16px.png"));
+ m_pResetButton->setIcon(UIIconPool::iconSet(":/help_browser_reset_16px.png", ":/help_browser_reset_disabled_16px.png"));
+ m_pPlusButton->setIcon(UIIconPool::iconSet(":/help_browser_plus_16px.png", ":/help_browser_plus_disabled_16px.png"));
+
+ connect(m_pPlusButton, &QIToolButton::pressed, this, &UIZoomMenuAction::sltZoomOperation);
+ connect(m_pMinusButton, &QIToolButton::pressed, this, &UIZoomMenuAction::sltZoomOperation);
+ connect(m_pResetButton, &QIToolButton::pressed, this, &UIZoomMenuAction::sltZoomOperation);
+
+ pMainLayout->addWidget(m_pLabel);
+ pMainLayout->addWidget(m_pResetButton);
+ pMainLayout->addWidget(m_pMinusButton);
+ pMainLayout->addWidget(m_pValueLabel, Qt::AlignCenter);
+ pMainLayout->addWidget(m_pPlusButton);
+ setZoomPercentage(100);
+}
+
+void UIZoomMenuAction::sltZoomOperation()
+{
+ if (!sender())
+ return;
+ UIHelpViewer::ZoomOperation enmOperation = UIHelpViewer::ZoomOperation_In;
+ if (sender() == m_pMinusButton)
+ enmOperation = UIHelpViewer::ZoomOperation_Out;
+ else if (sender() == m_pPlusButton)
+ enmOperation = UIHelpViewer::ZoomOperation_In;
+ else if (sender() == m_pResetButton)
+ enmOperation = UIHelpViewer::ZoomOperation_Reset;
+ emit sigZoomChanged((int)enmOperation);
+}
+
+
+/*********************************************************************************************************************************
+* UIBookmarksListWidget implementation. *
+*********************************************************************************************************************************/
+UIBookmarksListWidget::UIBookmarksListWidget(QWidget *pParent /* = 0 */)
+ :QListWidget(pParent)
+{
+ setSelectionMode(QAbstractItemView::SingleSelection);
+}
+
+void UIBookmarksListWidget::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ QListWidgetItem *pItem = currentItem();
+ if (!pItem)
+ return;
+ emit sigBookmarkDoubleClick(pItem->data(iBookmarkUrlDataType).toUrl());
+ QListWidget::mouseDoubleClickEvent(event);
+}
+
+void UIBookmarksListWidget::mousePressEvent(QMouseEvent *pEvent)
+{
+ if (!indexAt(pEvent->pos()).isValid())
+ {
+ clearSelection();
+ setCurrentItem(0);
+ }
+ QListWidget::mousePressEvent(pEvent);
+}
+
+
+/*********************************************************************************************************************************
+* UIBookmarksListContainer implementation. *
+*********************************************************************************************************************************/
+
+UIBookmarksListContainer::UIBookmarksListContainer(QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_pMainLayout(0)
+ , m_pListWidget(0)
+{
+ prepare();
+}
+
+void UIBookmarksListContainer::addBookmark(const QUrl &url, const QString &strTitle)
+{
+ if (!m_pListWidget)
+ return;
+ if (itemIndex(url) != -1)
+ return;
+ QListWidgetItem *pNewItem = new QListWidgetItem(strTitle, m_pListWidget);
+ pNewItem->setData(iBookmarkUrlDataType, url);
+ pNewItem->setToolTip(url.toString());
+}
+
+QStringList UIBookmarksListContainer::bookmarks() const
+{
+ if (!m_pListWidget)
+ return QStringList();
+ QStringList bookmarks;
+ for (int i = 0; i < m_pListWidget->count(); ++i)
+ {
+ QListWidgetItem *pItem = m_pListWidget->item(i);
+ if (!pItem)
+ continue;
+ bookmarks << pItem->data(iBookmarkUrlDataType).toUrl().toString() << pItem->text();
+ }
+ return bookmarks;
+}
+
+QUrl UIBookmarksListContainer::currentBookmarkUrl()
+{
+ if (!m_pListWidget || !m_pListWidget->currentItem())
+ return QUrl();
+ return m_pListWidget->currentItem()->data(iBookmarkUrlDataType).toUrl();
+}
+
+void UIBookmarksListContainer::sltDeleteSelectedBookmark()
+{
+ if (!m_pListWidget || !m_pListWidget->currentItem())
+ return;
+ QListWidgetItem *pCurrentItem = m_pListWidget->takeItem(m_pListWidget->currentRow());
+ delete pCurrentItem;
+}
+
+void UIBookmarksListContainer::sltDeleteAllBookmarks()
+{
+ if (m_pListWidget)
+ m_pListWidget->clear();
+}
+
+void UIBookmarksListContainer::retranslateUi()
+{
+}
+
+void UIBookmarksListContainer::prepare()
+{
+ m_pMainLayout = new QVBoxLayout(this);
+ AssertReturnVoid(m_pMainLayout);
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pListWidget = new UIBookmarksListWidget;
+ AssertReturnVoid(m_pListWidget);
+ m_pMainLayout->addWidget(m_pListWidget);
+ m_pListWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_pListWidget, &UIBookmarksListWidget::sigBookmarkDoubleClick,
+ this, &UIBookmarksListContainer::sigBookmarkDoubleClick);
+ connect(m_pListWidget, &UIBookmarksListWidget::customContextMenuRequested,
+ this, &UIBookmarksListContainer::sigListWidgetContextMenuRequest);
+}
+
+int UIBookmarksListContainer::itemIndex(const QUrl &url)
+{
+ if (!m_pListWidget || !url.isValid())
+ return -1;
+ for (int i = 0; i < m_pListWidget->count(); ++i)
+ {
+ if (m_pListWidget->item(i)->data(iBookmarkUrlDataType).toUrl() == url)
+ return i;
+ }
+ return -1;
+}
+
+/*********************************************************************************************************************************
+* UIHelpBrowserTab implementation. *
+*********************************************************************************************************************************/
+
+UIHelpBrowserTab::UIHelpBrowserTab(const QHelpEngine *pHelpEngine, const QUrl &homeUrl,
+ const QUrl &initialUrl, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pHomeAction(0)
+ , m_pForwardAction(0)
+ , m_pBackwardAction(0)
+ , m_pAddBookmarkAction(0)
+ , m_pFindInPageAction(0)
+ , m_pReloadPageAction(0)
+ , m_pMainLayout(0)
+ , m_pToolBar(0)
+ , m_pAddressBar(0)
+ , m_pContentViewer(0)
+ , m_pHelpEngine(pHelpEngine)
+ , m_homeUrl(homeUrl)
+{
+ if (initialUrl.isValid())
+ prepare(initialUrl);
+ else
+ prepare(m_homeUrl);
+}
+
+QUrl UIHelpBrowserTab::source() const
+{
+ if (!m_pContentViewer)
+ return QUrl();
+ return m_pContentViewer->source();
+}
+
+void UIHelpBrowserTab::setSource(const QUrl &url)
+{
+ if (m_pContentViewer)
+ {
+ m_pContentViewer->blockSignals(true);
+ m_pContentViewer->setSource(url);
+ m_pContentViewer->blockSignals(false);
+ /* emit historyChanged signal explicitly since we have blocked the signals: */
+ m_pContentViewer->emitHistoryChangedSignal();
+ }
+}
+
+QString UIHelpBrowserTab::documentTitle() const
+{
+ if (!m_pContentViewer)
+ return QString();
+ return m_pContentViewer->documentTitle();
+}
+
+void UIHelpBrowserTab::setToolBarVisible(bool fVisible)
+{
+ if (m_pToolBar)
+ m_pToolBar->setVisible(fVisible);
+ if (m_pAddressBar)
+ m_pAddressBar->setVisible(fVisible);
+}
+
+void UIHelpBrowserTab::print(QPrinter &printer)
+{
+ if (m_pContentViewer)
+ m_pContentViewer->print(&printer);
+}
+
+void UIHelpBrowserTab::setZoomPercentage(int iZoomPercentage)
+{
+ if (m_pContentViewer)
+ m_pContentViewer->setZoomPercentage(iZoomPercentage);
+}
+
+void UIHelpBrowserTab::setHelpFileList(const QList<QUrl> &helpFileList)
+{
+ if (m_pContentViewer)
+ m_pContentViewer->setHelpFileList(helpFileList);
+}
+
+void UIHelpBrowserTab::copySelectedText() const
+{
+ if (m_pContentViewer && m_pContentViewer->hasSelectedText())
+ m_pContentViewer->copy();
+}
+
+bool UIHelpBrowserTab::hasSelectedText() const
+{
+ if (m_pContentViewer)
+ return m_pContentViewer->textCursor().hasSelection();
+ return false;
+}
+
+bool UIHelpBrowserTab::isFindInPageWidgetVisible() const
+{
+ if (m_pContentViewer)
+ return m_pContentViewer->isFindInPageWidgetVisible();
+ return false;
+}
+
+void UIHelpBrowserTab::findNext()
+{
+ if (m_pContentViewer)
+ return m_pContentViewer->sltSelectNextMatch();
+}
+
+void UIHelpBrowserTab::findPrevious()
+{
+ if (m_pContentViewer)
+ return m_pContentViewer->sltSelectPreviousMatch();
+}
+
+void UIHelpBrowserTab::prepare(const QUrl &initialUrl)
+{
+ m_pMainLayout = new QVBoxLayout(this);
+ AssertReturnVoid(m_pMainLayout);
+ prepareToolBarAndAddressBar();
+ prepareWidgets(initialUrl);
+ retranslateUi();
+}
+
+void UIHelpBrowserTab::prepareWidgets(const QUrl &initialUrl)
+{
+ m_pContentViewer = new UIHelpViewer(m_pHelpEngine);
+ AssertReturnVoid(m_pContentViewer);
+ setFocusProxy(m_pContentViewer);
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+ m_pMainLayout->setSpacing(0);
+
+ m_pMainLayout->addWidget(m_pContentViewer);
+ m_pContentViewer->setOpenExternalLinks(false);
+ connect(m_pContentViewer, &UIHelpViewer::sourceChanged,
+ this, &UIHelpBrowserTab::sigSourceChanged);
+ connect(m_pContentViewer, &UIHelpViewer::historyChanged,
+ this, &UIHelpBrowserTab::sltHistoryChanged);
+ connect(m_pContentViewer, &UIHelpViewer::anchorClicked,
+ this, &UIHelpBrowserTab::sltAnchorClicked);
+ connect(m_pContentViewer, &UIHelpViewer::sigOpenLinkInNewTab,
+ this, &UIHelpBrowserTab::sigOpenLinkInNewTab);
+ connect(m_pContentViewer, &UIHelpViewer::sigGoBackward,
+ this, &UIHelpBrowserTab::sltBackwardAction);
+ connect(m_pContentViewer, &UIHelpViewer::sigGoForward,
+ this, &UIHelpBrowserTab::sltForwardAction);
+ connect(m_pContentViewer, &UIHelpViewer::sigGoHome,
+ this, &UIHelpBrowserTab::sltHomeAction);
+ connect(m_pContentViewer, &UIHelpViewer::sigAddBookmark,
+ this, &UIHelpBrowserTab::sltAddBookmarkAction);
+ connect(m_pContentViewer, static_cast<void(UIHelpViewer::*)(const QUrl&)>(&UIHelpViewer::highlighted),
+ this, &UIHelpBrowserTab::sigLinkHighlighted);
+ connect(m_pContentViewer, &UIHelpViewer::copyAvailable,
+ this, &UIHelpBrowserTab::sigCopyAvailableChanged);
+ connect(m_pContentViewer, &UIHelpViewer::sigFindInPageWidgetToogle,
+ this, &UIHelpBrowserTab::sltFindInPageWidgetVisibilityChanged);
+ connect(m_pContentViewer, &UIHelpViewer::sigMouseOverImage,
+ this, &UIHelpBrowserTab::sigMouseOverImage);
+ connect(m_pContentViewer, &UIHelpViewer::sigZoomRequest,
+ this, &UIHelpBrowserTab::sigZoomRequest);
+
+ m_pContentViewer->setSource(initialUrl);
+}
+
+void UIHelpBrowserTab::prepareToolBarAndAddressBar()
+{
+ m_pHomeAction =
+ new QAction(UIIconPool::iconSetFull(":/help_browser_home_32px.png", ":/help_browser_home_16px.png",
+ ":/help_browser_home_disabled_32px.png", ":/help_browser_home_disabled_16px.png"), QString(), this);
+ m_pForwardAction =
+ new QAction(UIIconPool::iconSetFull(":/help_browser_forward_32px.png", ":/help_browser_forward_16px.png",
+ ":/help_browser_forward_disabled_32px.png", ":/help_browser_forward_disabled_16px.png"), QString(), this);
+ m_pBackwardAction =
+ new QAction(UIIconPool::iconSetFull(":/help_browser_backward_32px.png", ":/help_browser_backward_16px.png",
+ ":/help_browser_backward_disabled_32px.png", ":/help_browser_backward_disabled_16px.png"), QString(), this);
+ m_pAddBookmarkAction =
+ new QAction(UIIconPool::iconSetFull(":/help_browser_add_bookmark_32px.png", ":/help_browser_add_bookmark_16px.png",
+ ":/help_browser_add_bookmark_disabled_32px.png", ":/help_browser_add_bookmark_disabled_16px.png"), QString(), this);
+ m_pFindInPageAction =
+ new QAction(UIIconPool::iconSetFull(":/help_browser_search_32px.png", ":/help_browser_search_16px.png",
+ ":/help_browser_search_disabled_32px.png", ":/help_browser_search_disabled_16px.png"), QString(), this);
+ m_pReloadPageAction =
+ new QAction(UIIconPool::iconSetFull(":/help_browser_reload_32px.png", ":/help_browser_reload_16px.png",
+ ":/help_browser_reload_disabled_32px.png", ":/help_browser_reload_disabled_16px.png"), QString(), this);
+
+ AssertReturnVoid(m_pHomeAction && m_pForwardAction &&
+ m_pBackwardAction && m_pAddBookmarkAction &&
+ m_pFindInPageAction);
+ m_pFindInPageAction->setCheckable(true);
+
+ connect(m_pHomeAction, &QAction::triggered, this, &UIHelpBrowserTab::sltHomeAction);
+ connect(m_pAddBookmarkAction, &QAction::triggered, this, &UIHelpBrowserTab::sltAddBookmarkAction);
+ connect(m_pForwardAction, &QAction::triggered, this, &UIHelpBrowserTab::sltForwardAction);
+ connect(m_pBackwardAction, &QAction::triggered, this, &UIHelpBrowserTab::sltBackwardAction);
+ connect(m_pFindInPageAction, &QAction::toggled, this, &UIHelpBrowserTab::sltFindInPageAction);
+ connect(m_pReloadPageAction, &QAction::triggered, this, &UIHelpBrowserTab::sltReloadPageAction);
+
+ m_pForwardAction->setEnabled(false);
+ m_pBackwardAction->setEnabled(false);
+
+ m_pToolBar = new QIToolBar;
+ AssertReturnVoid(m_pToolBar);
+ m_pToolBar->addAction(m_pBackwardAction);
+ m_pToolBar->addAction(m_pForwardAction);
+ m_pToolBar->addAction(m_pHomeAction);
+ m_pToolBar->addAction(m_pReloadPageAction);
+ m_pToolBar->addAction(m_pAddBookmarkAction);
+ m_pToolBar->addAction(m_pFindInPageAction);
+
+ m_pAddressBar = new QComboBox();
+ m_pAddressBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ connect(m_pAddressBar, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIHelpBrowserTab::sltAddressBarIndexChanged);
+
+
+ QHBoxLayout *pTopLayout = new QHBoxLayout;
+ pTopLayout->addWidget(m_pToolBar);
+ pTopLayout->addWidget(m_pAddressBar);
+ m_pMainLayout->addLayout(pTopLayout);
+}
+
+void UIHelpBrowserTab::setActionTextAndToolTip(QAction *pAction, const QString &strText, const QString &strToolTip)
+{
+ if (!pAction)
+ return;
+ pAction->setText(strText);
+ pAction->setToolTip(strToolTip);
+}
+
+void UIHelpBrowserTab::retranslateUi()
+{
+ setActionTextAndToolTip(m_pHomeAction, UIHelpBrowserWidget::tr("Home"), UIHelpBrowserWidget::tr("Return to Start Page"));
+ setActionTextAndToolTip(m_pBackwardAction, UIHelpBrowserWidget::tr("Backward"), UIHelpBrowserWidget::tr("Go Back to Previous Page"));
+ setActionTextAndToolTip(m_pForwardAction, UIHelpBrowserWidget::tr("Forward"), UIHelpBrowserWidget::tr("Go Forward to Next Page"));
+ setActionTextAndToolTip(m_pAddBookmarkAction, UIHelpBrowserWidget::tr("Add Bookmark"), UIHelpBrowserWidget::tr("Add a New Bookmark"));
+ setActionTextAndToolTip(m_pReloadPageAction, UIHelpBrowserWidget::tr("Reload"), UIHelpBrowserWidget::tr("Reload the Current Page"));
+ setActionTextAndToolTip(m_pFindInPageAction, UIHelpBrowserWidget::tr("Find in Page"), UIHelpBrowserWidget::tr("Find a String in the Current Page"));
+}
+
+void UIHelpBrowserTab::sltHomeAction()
+{
+ if (!m_pContentViewer)
+ return;
+ m_pContentViewer->setSource(m_homeUrl);
+}
+
+void UIHelpBrowserTab::sltForwardAction()
+{
+ if (m_pContentViewer)
+ {
+ m_pContentViewer->forward();
+ /* when we dont reload our overload imag hack does not work and images look ugly: */
+ m_pContentViewer->reload();
+ }
+}
+
+void UIHelpBrowserTab::sltBackwardAction()
+{
+ if (m_pContentViewer)
+ {
+ m_pContentViewer->backward();
+ /* when we dont reload our overload imag hack does not work and images look ugly: */
+ m_pContentViewer->reload();
+ }
+}
+
+void UIHelpBrowserTab::sltFindInPageAction(bool fToggled)
+{
+ if (m_pContentViewer)
+ m_pContentViewer->toggleFindInPageWidget(fToggled);
+}
+
+void UIHelpBrowserTab::sltReloadPageAction()
+{
+ if (m_pContentViewer)
+ m_pContentViewer->reload();
+}
+
+void UIHelpBrowserTab::sltHistoryChanged()
+{
+ if (!m_pContentViewer)
+ return;
+ int iCurrentIndex = 0;
+ /* QTextBrower history has negative and positive indices for bacward and forward items, respectively.
+ * 0 is the current item: */
+ m_pAddressBar->blockSignals(true);
+ m_pAddressBar->clear();
+ for (int i = -1 * m_pContentViewer->backwardHistoryCount(); i <= m_pContentViewer->forwardHistoryCount(); ++i)
+ {
+ int iIndex = m_pAddressBar->count();
+ m_pAddressBar->addItem(m_pContentViewer->historyUrl(i).toString(), i);
+ m_pAddressBar->setItemData(iIndex, m_pContentViewer->historyTitle(i), Qt::ToolTipRole);
+ if (i == 0)
+ iCurrentIndex = m_pAddressBar->count();
+ }
+ /* Make sure address bar show the current item: */
+ m_pAddressBar->setCurrentIndex(iCurrentIndex - 1);
+ m_pAddressBar->blockSignals(false);
+
+ if (m_pBackwardAction)
+ m_pBackwardAction->setEnabled(m_pContentViewer->isBackwardAvailable());
+ if (m_pForwardAction)
+ m_pForwardAction->setEnabled(m_pContentViewer->isForwardAvailable());
+
+ emit sigTitleUpdate(m_pContentViewer->historyTitle(0));
+ emit sigHistoryChanged(m_pContentViewer->isBackwardAvailable(), m_pContentViewer->isForwardAvailable());
+}
+
+void UIHelpBrowserTab::sltAddressBarIndexChanged(int iIndex)
+{
+ if (!m_pAddressBar || iIndex >= m_pAddressBar->count())
+ return;
+ int iHistoryIndex = m_pAddressBar->itemData(iIndex).toInt();
+ /* There seems to be no way to one-step-jump to a history item: */
+ if (iHistoryIndex == 0)
+ return;
+ if (iHistoryIndex > 0)
+ for (int i = 0; i < iHistoryIndex; ++i)
+ m_pContentViewer->forward();
+ else
+ for (int i = 0; i > iHistoryIndex ; --i)
+ m_pContentViewer->backward();
+}
+
+void UIHelpBrowserTab::sltAddBookmarkAction()
+{
+ emit sigAddBookmark(source(), documentTitle());
+}
+
+void UIHelpBrowserTab::sltAnchorClicked(const QUrl &link)
+{
+ Q_UNUSED(link);
+}
+
+void UIHelpBrowserTab::sltFindInPageWidgetVisibilityChanged(bool fVisible)
+{
+ if (m_pFindInPageAction)
+ {
+ m_pFindInPageAction->blockSignals(true);
+ m_pFindInPageAction->setChecked(fVisible);
+ m_pFindInPageAction->blockSignals(false);
+ }
+ emit sigFindInPageWidgetVisibilityChanged(fVisible);
+}
+
+
+/*********************************************************************************************************************************
+* UIHelpBrowserTabManager definition. *
+*********************************************************************************************************************************/
+
+UIHelpBrowserTabManager::UIHelpBrowserTabManager(const QHelpEngine *pHelpEngine, const QUrl &homeUrl,
+ const QStringList &urlList, QWidget *pParent /* = 0 */)
+ : QITabWidget(pParent)
+ , m_pHelpEngine(pHelpEngine)
+ , m_homeUrl(homeUrl)
+ , m_savedUrlList(urlList)
+ , m_fSwitchToNewTab(true)
+ , m_fToolBarVisible(true)
+ , m_iZoomPercentage(100)
+{
+ Q_UNUSED(m_fSwitchToNewTab);
+ prepare();
+}
+
+void UIHelpBrowserTabManager::addNewTab(const QUrl &initialUrl, bool fBackground)
+{
+ /* If there is already a tab with a source which is equal to @initialUrl then make it current: */
+ int iExistIndex = findTab(initialUrl);
+ if (iExistIndex != -1)
+ {
+ setCurrentIndex(iExistIndex);
+ return;
+ }
+
+ UIHelpBrowserTab *pTabWidget = new UIHelpBrowserTab(m_pHelpEngine, m_homeUrl, initialUrl);
+ AssertReturnVoid(pTabWidget);
+ pTabWidget->setToolBarVisible(m_fToolBarVisible);
+ int index = addTab(pTabWidget, pTabWidget->documentTitle());
+ connect(pTabWidget, &UIHelpBrowserTab::sigSourceChanged,
+ this, &UIHelpBrowserTabManager::sigSourceChanged);
+ connect(pTabWidget, &UIHelpBrowserTab::sigTitleUpdate,
+ this, &UIHelpBrowserTabManager::sltTabTitleChange);
+ connect(pTabWidget, &UIHelpBrowserTab::sigOpenLinkInNewTab,
+ this, &UIHelpBrowserTabManager::sltOpenLinkInNewTab);
+ connect(pTabWidget, &UIHelpBrowserTab::sigAddBookmark,
+ this, &UIHelpBrowserTabManager::sigAddBookmark);
+ connect(pTabWidget, &UIHelpBrowserTab::sigLinkHighlighted,
+ this, &UIHelpBrowserTabManager::sigLinkHighlighted);
+ connect(pTabWidget, &UIHelpBrowserTab::sigCopyAvailableChanged,
+ this, &UIHelpBrowserTabManager::sltCopyAvailableChanged);
+ connect(pTabWidget, &UIHelpBrowserTab::sigFindInPageWidgetVisibilityChanged,
+ this, &UIHelpBrowserTabManager::sigFindInPageWidgetVisibilityChanged);
+ connect(pTabWidget, &UIHelpBrowserTab::sigHistoryChanged,
+ this, &UIHelpBrowserTabManager::sigHistoryChanged);
+ connect(pTabWidget, &UIHelpBrowserTab::sigMouseOverImage,
+ this, &UIHelpBrowserTabManager::sigMouseOverImage);
+ connect(pTabWidget, &UIHelpBrowserTab::sigZoomRequest,
+ this, &UIHelpBrowserTabManager::sltHandleZoomRequest);
+
+ pTabWidget->setZoomPercentage(zoomPercentage());
+ pTabWidget->setHelpFileList(m_helpFileList);
+ setFocusProxy(pTabWidget);
+ if (!fBackground)
+ setCurrentIndex(index);
+}
+
+void UIHelpBrowserTabManager::updateTabUrlTitleList()
+{
+ QList<QPair<QString, int> > newList;
+
+ QStringList titles = tabTitleList();
+
+ if (titles == m_tabTitleList)
+ return;
+
+ m_tabTitleList = titles;
+ emit sigTabsListChanged(m_tabTitleList);
+}
+
+void UIHelpBrowserTabManager::closeAllTabsBut(int iTabIndex)
+{
+ QString strTitle = tabText(iTabIndex);
+ QList<QWidget*> widgetList;
+ for (int i = 0; i < count(); ++i)
+ widgetList.append(widget(i));
+ clear();
+ for (int i = 0; i < widgetList.size(); ++i)
+ {
+ if (i != iTabIndex)
+ delete widgetList[i];
+ }
+ addTab(widgetList[iTabIndex], strTitle);
+ updateTabUrlTitleList();
+}
+
+int UIHelpBrowserTabManager::findTab(const QUrl &Url) const
+{
+ for (int i = 0; i < count(); ++i)
+ {
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(widget(i));
+ if (!pTab || !pTab->source().isValid())
+ continue;
+ if (pTab->source() == Url)
+ return i;
+ }
+ return -1;
+}
+
+void UIHelpBrowserTabManager::initializeTabs()
+{
+ clearAndDeleteTabs();
+ /* Start with a single tab showing the home URL: */
+ if (m_savedUrlList.isEmpty())
+ addNewTab(QUrl(), false);
+ /* Start with saved tab(s): */
+ else
+ for (int i = 0; i < m_savedUrlList.size(); ++i)
+ addNewTab(m_savedUrlList[i], false);
+ updateTabUrlTitleList();
+}
+
+QUrl UIHelpBrowserTabManager::currentSource() const
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (!pTab)
+ return QUrl();
+ return pTab->source();
+}
+
+void UIHelpBrowserTabManager::setSource(const QUrl &url, bool fNewTab /* = false */)
+{
+ if (!fNewTab)
+ {
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (!pTab)
+ return;
+ pTab->setSource(url);
+ }
+ else
+ addNewTab(url, false);
+ updateTabUrlTitleList();
+}
+
+QStringList UIHelpBrowserTabManager::tabUrlList() const
+{
+ QStringList list;
+ for (int i = 0; i < count(); ++i)
+ {
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(widget(i));
+ if (!pTab || !pTab->source().isValid())
+ continue;
+ list << pTab->source().toString();
+ }
+ return list;
+}
+
+QStringList UIHelpBrowserTabManager::tabTitleList() const
+{
+ QStringList list;
+ for (int i = 0; i < count(); ++i)
+ {
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(widget(i));
+ if (!pTab || !pTab->source().isValid())
+ continue;
+ list << pTab->documentTitle();
+ }
+ return list;
+}
+
+void UIHelpBrowserTabManager::setToolBarVisible(bool fVisible)
+{
+ /* Make sure existing tabs are configured: */
+ for (int i = 0; i < count(); ++i)
+ {
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(widget(i));
+ if (!pTab)
+ continue;
+ pTab->setToolBarVisible(fVisible);
+ }
+ /* This is for the tabs that will be created later: */
+ m_fToolBarVisible = fVisible;
+}
+
+void UIHelpBrowserTabManager::printCurrent(QPrinter &printer)
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (!pTab)
+ return;
+ return pTab->print(printer);
+}
+
+void UIHelpBrowserTabManager::switchToTab(int iIndex)
+{
+ if (iIndex == currentIndex())
+ return;
+ setCurrentIndex(iIndex);
+}
+
+int UIHelpBrowserTabManager::zoomPercentage() const
+{
+ return m_iZoomPercentage;
+}
+
+void UIHelpBrowserTabManager::setHelpFileList(const QList<QUrl> &helpFileList)
+{
+ m_helpFileList = helpFileList;
+}
+
+void UIHelpBrowserTabManager::copySelectedText() const
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (!pTab)
+ return;
+ return pTab->copySelectedText();
+}
+
+bool UIHelpBrowserTabManager::hasCurrentTabSelectedText() const
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (!pTab)
+ return false;
+ return pTab->hasSelectedText();
+}
+
+bool UIHelpBrowserTabManager::isFindInPageWidgetVisible() const
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (!pTab)
+ return false;
+ return pTab->isFindInPageWidgetVisible();
+}
+
+void UIHelpBrowserTabManager::toggleFindInPage(bool fTrigger)
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (pTab)
+ pTab->sltFindInPageAction(fTrigger);
+}
+
+void UIHelpBrowserTabManager::findNext()
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (pTab)
+ pTab->findNext();
+}
+
+void UIHelpBrowserTabManager::findPrevious()
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (pTab)
+ pTab->findPrevious();
+}
+
+void UIHelpBrowserTabManager::sltTabTitleChange(const QString &strTitle)
+{
+ for (int i = 0; i < count(); ++i)
+ {
+ if (sender() == widget(i))
+ {
+ setTabText(i, strTitle);
+ setTabToolTip(i, strTitle);
+ continue;
+ }
+ }
+ updateTabUrlTitleList();
+}
+
+void UIHelpBrowserTabManager::sltOpenLinkInNewTab(const QUrl &url, bool fBackground)
+{
+ if (url.isValid())
+ addNewTab(url, fBackground);
+ updateTabUrlTitleList();
+}
+
+void UIHelpBrowserTabManager::sltCopyAvailableChanged(bool fAvailable)
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ /* Emit coresponding signal only if sender is the current tab: */
+ if (pTab && sender() == pTab)
+ emit sigCopyAvailableChanged(fAvailable);
+}
+
+void UIHelpBrowserTabManager::sltTabClose(int iTabIndex)
+{
+ if (count() <= 1)
+ return;
+ QWidget *pWidget = widget(iTabIndex);
+ if (!pWidget)
+ return;
+ removeTab(iTabIndex);
+ delete pWidget;
+ updateTabUrlTitleList();
+}
+
+void UIHelpBrowserTabManager::sltContextMenuTabClose()
+{
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ if (!pAction)
+ return;
+ int iTabIndex = pAction->data().toInt();
+ if (iTabIndex < 0 || iTabIndex >= count())
+ return;
+ sltTabClose(iTabIndex);
+}
+
+void UIHelpBrowserTabManager::sltCloseOtherTabsContextMenuAction()
+{
+ /* Find the index of the sender tab. we will close all tabs but sender tab: */
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ if (!pAction)
+ return;
+ int iTabIndex = pAction->data().toInt();
+ if (iTabIndex < 0 || iTabIndex >= count())
+ return;
+ closeAllTabsBut(iTabIndex);
+}
+
+void UIHelpBrowserTabManager::sltCloseCurrentTab()
+{
+ sltTabClose(currentIndex());
+}
+
+void UIHelpBrowserTabManager::sltCloseOtherTabs()
+{
+ closeAllTabsBut(currentIndex());
+}
+
+void UIHelpBrowserTabManager::sltCurrentChanged(int iTabIndex)
+{
+ Q_UNUSED(iTabIndex);
+ emit sigSourceChanged(currentSource());
+}
+
+void UIHelpBrowserTabManager::sltShowTabBarContextMenu(const QPoint &pos)
+{
+ if (!tabBar())
+ return;
+ QMenu menu;
+ QAction *pCloseAll = menu.addAction(UIHelpBrowserWidget::tr("Close Other Tabs"));
+ connect(pCloseAll, &QAction::triggered, this, &UIHelpBrowserTabManager::sltCloseOtherTabsContextMenuAction);
+ pCloseAll->setData(tabBar()->tabAt(pos));
+
+ QAction *pClose = menu.addAction(UIHelpBrowserWidget::tr("Close Tab"));
+ connect(pClose, &QAction::triggered, this, &UIHelpBrowserTabManager::sltContextMenuTabClose);
+ pClose->setData(tabBar()->tabAt(pos));
+
+ menu.exec(tabBar()->mapToGlobal(pos));
+}
+
+void UIHelpBrowserTabManager::sltHomeAction()
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (pTab)
+ pTab->sltHomeAction();
+}
+
+void UIHelpBrowserTabManager::sltAddBookmarkAction()
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (pTab)
+ pTab->sltAddBookmarkAction();
+}
+
+void UIHelpBrowserTabManager::sltForwardAction()
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (pTab)
+ pTab->sltForwardAction();
+}
+
+void UIHelpBrowserTabManager::sltBackwardAction()
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (pTab)
+ pTab->sltBackwardAction();
+}
+
+void UIHelpBrowserTabManager::sltHandleZoomRequest(UIHelpViewer::ZoomOperation enmOperation)
+{
+ int iZoomPercentage = m_iZoomPercentage;
+ switch (enmOperation)
+ {
+ case UIHelpViewer::ZoomOperation_In:
+ iZoomPercentage += iZoomPercentageStep;
+ break;
+ case UIHelpViewer::ZoomOperation_Out:
+ iZoomPercentage -= iZoomPercentageStep;
+ break;
+ case UIHelpViewer::ZoomOperation_Reset:
+ default:
+ iZoomPercentage = 100;
+ break;
+ }
+ setZoomPercentage(iZoomPercentage);
+}
+
+void UIHelpBrowserTabManager::setZoomPercentage(int iZoomPercentage)
+{
+
+ if (iZoomPercentage > zoomPercentageMinMax.second ||
+ iZoomPercentage < zoomPercentageMinMax.first)
+ return;
+
+ m_iZoomPercentage = iZoomPercentage;
+
+ for (int i = 0; i < count(); ++i)
+ {
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(widget(i));
+ if (pTab)
+ pTab->setZoomPercentage(m_iZoomPercentage);
+ }
+ emit sigZoomPercentageChanged(m_iZoomPercentage);
+}
+
+void UIHelpBrowserTabManager::sltReloadPageAction()
+{
+ UIHelpBrowserTab *pTab = qobject_cast<UIHelpBrowserTab*>(currentWidget());
+ if (pTab)
+ pTab->sltReloadPageAction();
+}
+
+void UIHelpBrowserTabManager::prepare()
+{
+ setTabsClosable(true);
+ setTabBarAutoHide(true);
+ connect(this, &UIHelpBrowserTabManager::tabCloseRequested, this, &UIHelpBrowserTabManager::sltTabClose);
+ connect(this, &UIHelpBrowserTabManager::currentChanged, this, &UIHelpBrowserTabManager::sltCurrentChanged);
+ if (tabBar())
+ {
+ tabBar()->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(tabBar(), &QTabBar::customContextMenuRequested, this, &UIHelpBrowserTabManager::sltShowTabBarContextMenu);
+ }
+}
+
+void UIHelpBrowserTabManager::clearAndDeleteTabs()
+{
+ QList<QWidget*> tabList;
+ for (int i = 0; i < count(); ++i)
+ tabList << widget(i);
+ /* QTabWidget::clear() does not delete tab widgets: */
+ clear();
+ foreach (QWidget *pWidget, tabList)
+ delete pWidget;
+}
+
+
+/*********************************************************************************************************************************
+* UIHelpBrowserWidget implementation. *
+*********************************************************************************************************************************/
+
+UIHelpBrowserWidget::UIHelpBrowserWidget(EmbedTo enmEmbedding, const QString &strHelpFilePath, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_fIsPolished(false)
+ , m_pMainLayout(0)
+ , m_pTopLayout(0)
+ , m_pTabWidget(0)
+ , m_pToolBar(0)
+ , m_strHelpFilePath(strHelpFilePath)
+ , m_pHelpEngine(0)
+ , m_pSplitter(0)
+ , m_pFileMenu(0)
+ , m_pEditMenu(0)
+ , m_pViewMenu(0)
+ , m_pTabsMenu(0)
+ , m_pNavigationMenu(0)
+ , m_pContentWidget(0)
+ , m_pIndexWidget(0)
+ , m_pContentModel(0)
+ , m_pSearchEngine(0)
+ , m_pSearchQueryWidget(0)
+ , m_pSearchResultWidget(0)
+ , m_pTabManager(0)
+ , m_pBookmarksWidget(0)
+ , m_pSearchContainerWidget(0)
+ , m_pPrintAction(0)
+ , m_pShowHideSideBarAction(0)
+ , m_pShowHideToolBarAction(0)
+ , m_pShowHideStatusBarAction(0)
+ , m_pCopySelectedTextAction(0)
+ , m_pFindInPageAction(0)
+ , m_pFindNextInPageAction(0)
+ , m_pFindPreviousInPageAction(0)
+ , m_pBackwardAction(0)
+ , m_pForwardAction(0)
+ , m_pHomeAction(0)
+ , m_pReloadPageAction(0)
+ , m_pAddBookmarkAction(0)
+ , m_pZoomMenuAction(0)
+ , m_fModelContentCreated(false)
+ , m_fIndexingFinished(false)
+{
+ qRegisterMetaType<HelpBrowserTabs>("HelpBrowserTabs");
+ prepare();
+ loadOptions();
+}
+
+UIHelpBrowserWidget::~UIHelpBrowserWidget()
+{
+ cleanup();
+}
+
+QList<QMenu*> UIHelpBrowserWidget::menus() const
+{
+ QList<QMenu*> menuList;
+ menuList
+ << m_pFileMenu
+ << m_pEditMenu
+ << m_pNavigationMenu
+ << m_pViewMenu
+ << m_pTabsMenu;
+ return menuList;
+}
+
+void UIHelpBrowserWidget::showHelpForKeyword(const QString &strKeyword)
+{
+ if (m_fIndexingFinished)
+ findAndShowUrlForKeyword(strKeyword);
+ else
+ m_keywordList.append(strKeyword);
+}
+
+int UIHelpBrowserWidget::zoomPercentage() const
+{
+ if (m_pTabManager)
+ return m_pTabManager->zoomPercentage();
+ return 0;
+}
+
+bool UIHelpBrowserWidget::shouldBeMaximized() const
+{
+ return gEDataManager->logWindowShouldBeMaximized();
+}
+
+void UIHelpBrowserWidget::prepare()
+{
+ m_pMainLayout = new QVBoxLayout(this);
+ m_pMainLayout->setContentsMargins(0.2 * qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
+ qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
+ 0.2 * qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
+ 0.2 * qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
+
+ AssertReturnVoid(m_pMainLayout);
+
+ prepareActions();
+ prepareMenu();
+ prepareWidgets();
+ prepareConnections();
+ prepareSearchWidgets();
+ loadBookmarks();
+ retranslateUi();
+}
+
+void UIHelpBrowserWidget::prepareActions()
+{
+ m_pShowHideSideBarAction = new QAction(this);
+ m_pShowHideSideBarAction->setCheckable(true);
+ m_pShowHideSideBarAction->setChecked(true);
+ connect(m_pShowHideSideBarAction, &QAction::toggled,
+ this, &UIHelpBrowserWidget::sltWidgetVisibilityToggle);
+
+ m_pShowHideToolBarAction = new QAction(this);
+ m_pShowHideToolBarAction->setCheckable(true);
+ m_pShowHideToolBarAction->setChecked(true);
+ connect(m_pShowHideToolBarAction, &QAction::toggled,
+ this, &UIHelpBrowserWidget::sltWidgetVisibilityToggle);
+
+ m_pShowHideStatusBarAction = new QAction(this);
+ m_pShowHideStatusBarAction->setCheckable(true);
+ m_pShowHideStatusBarAction->setChecked(true);
+ connect(m_pShowHideStatusBarAction, &QAction::toggled,
+ this, &UIHelpBrowserWidget::sltWidgetVisibilityToggle);
+
+ m_pCopySelectedTextAction = new QAction(this);
+ connect(m_pCopySelectedTextAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sltCopySelectedText);
+ m_pCopySelectedTextAction->setShortcut(QString("Ctrl+C"));
+
+ m_pFindInPageAction = new QAction(this);
+ m_pFindInPageAction->setCheckable(true);
+ m_pFindInPageAction->setChecked(false);
+ connect(m_pFindInPageAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sltFindInPage);
+ m_pFindInPageAction->setShortcut(QKeySequence::Find);
+
+ m_pFindNextInPageAction = new QAction(this);
+ m_pFindNextInPageAction->setEnabled(false);
+ connect(m_pFindNextInPageAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sltFindNextInPage);
+ m_pFindNextInPageAction->setShortcut(QKeySequence::FindNext);
+
+ m_pFindPreviousInPageAction = new QAction(this);
+ m_pFindPreviousInPageAction->setEnabled(false);
+ connect(m_pFindPreviousInPageAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sltFindPreviousInPage);
+ m_pFindPreviousInPageAction->setShortcut(QKeySequence::FindPrevious);
+
+ m_pPrintAction = new QAction(this);
+ connect(m_pPrintAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sltShowPrintDialog);
+ m_pPrintAction->setShortcut(QString("Ctrl+P"));
+
+ m_pQuitAction = new QAction(this);
+ connect(m_pQuitAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sigCloseDialog);
+ m_pQuitAction->setShortcut(QString("Ctrl+Q"));
+
+ m_pBackwardAction = new QAction(this);
+ m_pBackwardAction->setShortcut(QKeySequence::Back);
+ connect(m_pBackwardAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sigGoBackward);
+ m_pBackwardAction->setEnabled(false);
+
+ m_pForwardAction = new QAction(this);
+ m_pForwardAction->setShortcut(QKeySequence::Forward);
+ connect(m_pForwardAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sigGoForward);
+ m_pForwardAction->setEnabled(false);
+
+ m_pHomeAction = new QAction(this);
+ connect(m_pHomeAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sigGoHome);
+
+ m_pReloadPageAction = new QAction(this);
+ m_pReloadPageAction->setShortcut(QKeySequence::Refresh);
+ connect(m_pReloadPageAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sigReloadPage);
+
+ m_pAddBookmarkAction = new QAction(this);
+ m_pAddBookmarkAction->setShortcut(QKeySequence("Ctrl+D"));
+ connect(m_pAddBookmarkAction, &QAction::triggered,
+ this, &UIHelpBrowserWidget::sigAddBookmark);
+
+ m_pZoomMenuAction = new UIZoomMenuAction(this);
+ connect(m_pZoomMenuAction, &UIZoomMenuAction::sigZoomChanged,
+ this, &UIHelpBrowserWidget::sltZoomActions);
+}
+
+void UIHelpBrowserWidget::prepareConnections()
+{
+ if (m_pTabManager)
+ {
+ connect(m_pHomeAction, &QAction::triggered, m_pTabManager, &UIHelpBrowserTabManager::sltHomeAction);
+ connect(m_pAddBookmarkAction, &QAction::triggered, m_pTabManager, &UIHelpBrowserTabManager::sltAddBookmarkAction);
+ connect(m_pForwardAction, &QAction::triggered, m_pTabManager, &UIHelpBrowserTabManager::sltForwardAction);
+ connect(m_pBackwardAction, &QAction::triggered, m_pTabManager, &UIHelpBrowserTabManager::sltBackwardAction);
+ connect(m_pReloadPageAction, &QAction::triggered, m_pTabManager, &UIHelpBrowserTabManager::sltReloadPageAction);
+ }
+}
+
+void UIHelpBrowserWidget::prepareWidgets()
+{
+ m_pSplitter = new QSplitter;
+ AssertReturnVoid(m_pSplitter);
+
+ m_pMainLayout->addWidget(m_pSplitter);
+ m_pHelpEngine = new QHelpEngine(m_strHelpFilePath, this);
+ m_pBookmarksWidget = new UIBookmarksListContainer(this);
+ m_pTabWidget = new QITabWidget;
+ m_pTabManager = new UIHelpBrowserTabManager(m_pHelpEngine, findIndexHtml(), loadSavedUrlList());
+ m_pTabManager->setHelpFileList(m_pHelpEngine->files(m_pHelpEngine->namespaceName(m_strHelpFilePath), QStringList()));
+
+ AssertReturnVoid(m_pTabWidget &&
+ m_pHelpEngine &&
+ m_pBookmarksWidget &&
+ m_pTabManager);
+
+ m_pContentWidget = m_pHelpEngine->contentWidget();
+ m_pIndexWidget = m_pHelpEngine->indexWidget();
+ m_pContentModel = m_pHelpEngine->contentModel();
+
+ AssertReturnVoid(m_pContentWidget && m_pIndexWidget && m_pContentModel);
+ m_pSplitter->addWidget(m_pTabWidget);
+ m_pContentWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ m_pTabWidget->insertTab(HelpBrowserTabs_TOC, m_pContentWidget, QString());
+ m_pTabWidget->insertTab(HelpBrowserTabs_Bookmarks, m_pBookmarksWidget, QString());
+ /* Dont insert the index widget since we only have automatically generated indexes: */
+#if 0
+ m_pTabWidget->insertTab(HelpBrowserTabs_Index, m_pIndexWidget, QString());
+#endif
+
+ m_pSplitter->addWidget(m_pTabManager);
+ m_pSplitter->setStretchFactor(0,0);
+ m_pSplitter->setStretchFactor(1,1);
+
+ m_pSplitter->setChildrenCollapsible(false);
+
+ connect(m_pTabManager, &UIHelpBrowserTabManager::sigSourceChanged,
+ this, &UIHelpBrowserWidget::sltViewerSourceChange);
+ connect(m_pTabManager, &UIHelpBrowserTabManager::sigAddBookmark,
+ this, &UIHelpBrowserWidget::sltAddNewBookmark);
+ connect(m_pTabManager, &UIHelpBrowserTabManager::sigTabsListChanged,
+ this, &UIHelpBrowserWidget::sltTabListChanged);
+ connect(m_pTabManager, &UIHelpBrowserTabManager::currentChanged,
+ this, &UIHelpBrowserWidget::sltCurrentTabChanged);
+ connect(m_pTabManager, &UIHelpBrowserTabManager::sigLinkHighlighted,
+ this, &UIHelpBrowserWidget::sltLinkHighlighted);
+ connect(m_pTabManager, &UIHelpBrowserTabManager::sigZoomPercentageChanged,
+ this, &UIHelpBrowserWidget::sltZoomPercentageChanged);
+ connect(m_pTabManager, &UIHelpBrowserTabManager::sigCopyAvailableChanged,
+ this, &UIHelpBrowserWidget::sltCopyAvailableChanged);
+ connect(m_pTabManager, &UIHelpBrowserTabManager::sigFindInPageWidgetVisibilityChanged,
+ this, &UIHelpBrowserWidget::sltFindInPageWidgetVisibilityChanged);
+ connect(m_pTabManager, &UIHelpBrowserTabManager::sigHistoryChanged,
+ this, &UIHelpBrowserWidget::sltHistoryChanged);
+ connect(m_pTabManager, &UIHelpBrowserTabManager::sigMouseOverImage,
+ this, &UIHelpBrowserWidget::sltMouseOverImage);
+
+ connect(m_pHelpEngine, &QHelpEngine::setupFinished,
+ this, &UIHelpBrowserWidget::sltHelpEngineSetupFinished);
+ connect(m_pContentWidget, &QHelpContentWidget::clicked,
+ this, &UIHelpBrowserWidget::sltContentWidgetItemClicked);
+ connect(m_pContentModel, &QHelpContentModel::contentsCreated,
+ this, &UIHelpBrowserWidget::sltContentsCreated);
+ connect(m_pContentWidget, &QHelpContentWidget::customContextMenuRequested,
+ this, &UIHelpBrowserWidget::sltShowLinksContextMenu);
+ connect(m_pBookmarksWidget, &UIBookmarksListContainer::sigBookmarkDoubleClick,
+ this, &UIHelpBrowserWidget::sltOpenLinkWithUrl);
+ connect(m_pBookmarksWidget, &UIBookmarksListContainer::sigListWidgetContextMenuRequest,
+ this, &UIHelpBrowserWidget::sltShowLinksContextMenu);
+
+ if (QFile(m_strHelpFilePath).exists() && m_pHelpEngine)
+ m_pHelpEngine->setupData();
+}
+
+void UIHelpBrowserWidget::prepareSearchWidgets()
+{
+ AssertReturnVoid(m_pTabWidget && m_pHelpEngine);
+
+ m_pSearchContainerWidget = new QWidget;
+ m_pTabWidget->insertTab(HelpBrowserTabs_Search, m_pSearchContainerWidget, QString());
+ m_pTabWidget->setTabPosition(QTabWidget::South);
+
+ m_pSearchEngine = m_pHelpEngine->searchEngine();
+ AssertReturnVoid(m_pSearchEngine);
+
+ m_pSearchQueryWidget = m_pSearchEngine->queryWidget();
+ m_pSearchResultWidget = m_pSearchEngine->resultWidget();
+ AssertReturnVoid(m_pSearchQueryWidget && m_pSearchResultWidget);
+ m_pSearchResultWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pSearchQueryWidget->setCompactMode(false);
+
+ QVBoxLayout *pSearchLayout = new QVBoxLayout(m_pSearchContainerWidget);
+ pSearchLayout->addWidget(m_pSearchQueryWidget);
+ pSearchLayout->addWidget(m_pSearchResultWidget);
+ m_pSearchQueryWidget->expandExtendedSearch();
+
+ connect(m_pSearchQueryWidget, &QHelpSearchQueryWidget::search,
+ this, &UIHelpBrowserWidget::sltSearchStart);
+ connect(m_pSearchResultWidget, &QHelpSearchResultWidget::requestShowLink,
+ this, &UIHelpBrowserWidget::sltOpenLinkWithUrl);
+ connect(m_pSearchResultWidget, &QHelpContentWidget::customContextMenuRequested,
+ this, &UIHelpBrowserWidget::sltShowLinksContextMenu);
+ connect(m_pSearchEngine, &QHelpSearchEngine::indexingStarted,
+ this, &UIHelpBrowserWidget::sltIndexingStarted);
+ connect(m_pSearchEngine, &QHelpSearchEngine::indexingFinished,
+ this, &UIHelpBrowserWidget::sltIndexingFinished);
+ connect(m_pSearchEngine, &QHelpSearchEngine::searchingStarted,
+ this, &UIHelpBrowserWidget::sltSearchingStarted);
+
+ m_pSearchEngine->reindexDocumentation();
+}
+
+void UIHelpBrowserWidget::prepareToolBar()
+{
+ m_pTopLayout = new QHBoxLayout;
+ m_pToolBar = new QIToolBar(parentWidget());
+ if (m_pToolBar)
+ {
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ m_pTopLayout->addWidget(m_pToolBar);
+ m_pMainLayout->addLayout(m_pTopLayout);
+ }
+#else
+ /* Add into layout: */
+ m_pTopLayout->addWidget(m_pToolBar);
+ m_pMainLayout->addLayout(m_pTopLayout);
+#endif
+ }
+}
+
+void UIHelpBrowserWidget::prepareMenu()
+{
+ m_pFileMenu = new QMenu(tr("&File"), this);
+ m_pEditMenu = new QMenu(tr("&Edit"), this);
+ m_pNavigationMenu = new QMenu(tr("&Navigation"), this);
+ m_pViewMenu = new QMenu(tr("&View"), this);
+ m_pTabsMenu = new QMenu(tr("&Tabs"), this);
+
+ AssertReturnVoid(m_pFileMenu && m_pViewMenu &&
+ m_pTabsMenu && m_pNavigationMenu);
+
+ addActionToMenu(m_pFileMenu, m_pPrintAction);
+ addActionToMenu(m_pFileMenu, m_pQuitAction);
+
+ addActionToMenu(m_pEditMenu, m_pCopySelectedTextAction);
+ addActionToMenu(m_pEditMenu, m_pFindInPageAction);
+ addActionToMenu(m_pEditMenu, m_pFindNextInPageAction);
+ addActionToMenu(m_pEditMenu, m_pFindPreviousInPageAction);
+
+ addActionToMenu(m_pViewMenu, m_pZoomMenuAction);
+ addActionToMenu(m_pViewMenu, m_pShowHideSideBarAction);
+ addActionToMenu(m_pViewMenu, m_pShowHideToolBarAction);
+ addActionToMenu(m_pViewMenu, m_pShowHideStatusBarAction);
+
+ addActionToMenu(m_pNavigationMenu, m_pBackwardAction);
+ addActionToMenu(m_pNavigationMenu, m_pForwardAction);
+ addActionToMenu(m_pNavigationMenu, m_pHomeAction);
+ addActionToMenu(m_pNavigationMenu, m_pReloadPageAction);
+ addActionToMenu(m_pNavigationMenu, m_pAddBookmarkAction);
+}
+
+void UIHelpBrowserWidget::loadOptions()
+{
+ if (m_pTabManager)
+ m_pTabManager->setZoomPercentage(gEDataManager->helpBrowserZoomPercentage());
+}
+
+QStringList UIHelpBrowserWidget::loadSavedUrlList()
+{
+ return gEDataManager->helpBrowserLastUrlList();
+}
+
+void UIHelpBrowserWidget::loadBookmarks()
+{
+ if (!m_pBookmarksWidget)
+ return;
+
+ QStringList bookmarks = gEDataManager->helpBrowserBookmarks();
+ /* bookmarks list is supposed to have url title pair: */
+ for (int i = 0; i < bookmarks.size(); ++i)
+ {
+ const QString &url = bookmarks[i];
+ if (i+1 >= bookmarks.size())
+ break;
+ ++i;
+ const QString &strTitle = bookmarks[i];
+ m_pBookmarksWidget->addBookmark(url, strTitle);
+ }
+}
+
+void UIHelpBrowserWidget::saveBookmarks()
+{
+ if (!m_pBookmarksWidget)
+ return;
+ gEDataManager->setHelpBrowserBookmarks(m_pBookmarksWidget->bookmarks());
+}
+
+void UIHelpBrowserWidget::saveOptions()
+{
+ if (m_pTabManager)
+ {
+ gEDataManager->setHelpBrowserLastUrlList(m_pTabManager->tabUrlList());
+ gEDataManager->setHelpBrowserZoomPercentage(m_pTabManager->zoomPercentage());
+ }
+}
+
+QUrl UIHelpBrowserWidget::findIndexHtml() const
+{
+ QList<QUrl> files = m_pHelpEngine->files(m_pHelpEngine->namespaceName(m_strHelpFilePath), QStringList());
+ int iIndex = -1;
+ for (int i = 0; i < files.size(); ++i)
+ {
+ if (files[i].toString().contains("index.html", Qt::CaseInsensitive))
+ {
+ iIndex = i;
+ break;
+ }
+ }
+ if (iIndex == -1)
+ {
+ /* If index html/htm could not be found try to find a html file at least: */
+ for (int i = 0; i < files.size(); ++i)
+ {
+ if (files[i].toString().contains(".html", Qt::CaseInsensitive) ||
+ files[i].toString().contains(".htm", Qt::CaseInsensitive))
+ {
+ iIndex = i;
+ break;
+ }
+ }
+ }
+ if (iIndex != -1 && files.size() > iIndex)
+ return files[iIndex];
+ else
+ return QUrl();
+}
+
+QUrl UIHelpBrowserWidget::contentWidgetUrl(const QModelIndex &itemIndex)
+{
+ QHelpContentModel *pContentModel =
+ qobject_cast<QHelpContentModel*>(m_pContentWidget->model());
+ if (!pContentModel)
+ return QUrl();
+ QHelpContentItem *pItem = pContentModel->contentItemAt(itemIndex);
+ if (!pItem)
+ return QUrl();
+ return pItem->url();
+}
+
+void UIHelpBrowserWidget::cleanup()
+{
+ saveOptions();
+ saveBookmarks();
+}
+
+void UIHelpBrowserWidget::retranslateUi()
+{
+ /* Translate toolbar: */
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text. */
+ if (m_pToolBar)
+ m_pToolBar->updateLayout();
+#endif
+ if (m_pTabWidget)
+ {
+ m_pTabWidget->setTabText(HelpBrowserTabs_TOC, tr("Contents"));
+ m_pTabWidget->setTabText(HelpBrowserTabs_Index, tr("Index"));
+ m_pTabWidget->setTabText(HelpBrowserTabs_Search, tr("Search"));
+ m_pTabWidget->setTabText(HelpBrowserTabs_Bookmarks, tr("Bookmarks"));
+ }
+
+ if (m_pShowHideSideBarAction)
+ m_pShowHideSideBarAction->setText(tr("Show &Side Bar"));
+ if (m_pShowHideToolBarAction)
+ m_pShowHideToolBarAction->setText(tr("Show &Tool Bar"));
+ if (m_pShowHideStatusBarAction)
+ m_pShowHideStatusBarAction->setText(tr("Show St&atus Bar"));
+
+ if (m_pPrintAction)
+ m_pPrintAction->setText(tr("&Print..."));
+ if (m_pQuitAction)
+ m_pQuitAction->setText(tr("&Quit"));
+
+ if (m_pCopySelectedTextAction)
+ m_pCopySelectedTextAction->setText(tr("&Copy Selected Text"));
+ if (m_pFindInPageAction)
+ m_pFindInPageAction->setText(tr("&Find in Page"));
+ if (m_pFindNextInPageAction)
+ m_pFindNextInPageAction->setText(tr("Find Ne&xt"));
+ if (m_pFindPreviousInPageAction)
+ m_pFindPreviousInPageAction->setText(tr("Find &Previous"));
+
+ if (m_pBackwardAction)
+ m_pBackwardAction->setText(tr("Go Backward"));
+ if (m_pForwardAction)
+ m_pForwardAction->setText(tr("Go Forward"));
+ if (m_pHomeAction)
+ m_pHomeAction->setText(tr("Go to Start Page"));
+ if (m_pReloadPageAction)
+ m_pReloadPageAction->setText(tr("Reload Page"));
+ if (m_pAddBookmarkAction)
+ m_pAddBookmarkAction->setText(tr("Add Bookmark"));
+}
+
+
+void UIHelpBrowserWidget::showEvent(QShowEvent *pEvent)
+{
+ QWidget::showEvent(pEvent);
+ if (m_fIsPolished)
+ return;
+ m_fIsPolished = true;
+ if (m_pTabManager)
+ m_pTabManager->setFocus();
+}
+
+void UIHelpBrowserWidget::keyPressEvent(QKeyEvent *pEvent)
+{
+ QWidget::keyPressEvent(pEvent);
+}
+
+void UIHelpBrowserWidget::findAndShowUrlForKeyword(const QString &strKeyword)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ QList<QHelpLink> links = m_pHelpEngine->documentsForIdentifier(strKeyword);
+ if (!links.isEmpty())
+ {
+ /* We have to a have a single url per keyword in this case: */
+ m_pTabManager->setSource(links.first().url, true /* new tab */);
+ }
+#else
+ QMap<QString, QUrl> map = m_pHelpEngine->linksForIdentifier(strKeyword);
+ if (!map.isEmpty())
+ {
+ /* We have to a have a single url per keyword in this case: */
+ QUrl keywordUrl = map.first();
+ m_pTabManager->setSource(keywordUrl, true /* new tab */);
+ }
+#endif
+}
+
+void UIHelpBrowserWidget::sltWidgetVisibilityToggle(bool fToggled)
+{
+ if (sender() == m_pShowHideSideBarAction)
+ {
+ if (m_pTabWidget)
+ m_pTabWidget->setVisible(fToggled);
+ }
+ else if (sender() == m_pShowHideToolBarAction)
+ {
+ if (m_pTabManager)
+ m_pTabManager->setToolBarVisible(fToggled);
+ }
+ else if (sender() == m_pShowHideStatusBarAction)
+ emit sigStatusBarVisible(fToggled);
+}
+
+void UIHelpBrowserWidget::sltCopySelectedText()
+{
+ if (m_pTabManager)
+ m_pTabManager->copySelectedText();
+}
+
+void UIHelpBrowserWidget::sltFindInPage(bool fChecked)
+{
+ if (m_pTabManager)
+ m_pTabManager->toggleFindInPage(fChecked);
+}
+
+void UIHelpBrowserWidget::sltFindNextInPage()
+{
+ if (m_pTabManager)
+ m_pTabManager->findNext();
+}
+
+void UIHelpBrowserWidget::sltFindPreviousInPage()
+{
+ if (m_pTabManager)
+ m_pTabManager->findPrevious();
+}
+
+void UIHelpBrowserWidget::sltHistoryChanged(bool fBackwardAvailable, bool fForwardAvailable)
+{
+ if (m_pBackwardAction)
+ m_pBackwardAction->setEnabled(fBackwardAvailable);
+ if (m_pForwardAction)
+ m_pForwardAction->setEnabled(fForwardAvailable);
+}
+
+void UIHelpBrowserWidget::sltLinkHighlighted(const QUrl &url)
+{
+ QString strMessage = url.url();
+ if (url.scheme() != "qthelp")
+ strMessage = QString("%1: %2").arg(tr("Click to open this link in an external browser")).arg(strMessage);
+
+ emit sigStatusBarMessage(strMessage, 0);
+}
+
+void UIHelpBrowserWidget::sltMouseOverImage(const QString &strImageName)
+{
+ emit sigStatusBarMessage(QString("%1: %2").arg(tr("Click to enlarge the image")).arg(strImageName), 3000);
+}
+
+void UIHelpBrowserWidget::sltCopyAvailableChanged(bool fAvailable)
+{
+ if (m_pCopySelectedTextAction)
+ m_pCopySelectedTextAction->setEnabled(fAvailable);
+}
+
+void UIHelpBrowserWidget::sltFindInPageWidgetVisibilityChanged(bool fVisible)
+{
+ if (m_pFindInPageAction)
+ {
+ m_pFindInPageAction->blockSignals(true);
+ m_pFindInPageAction->setChecked(fVisible);
+ m_pFindInPageAction->blockSignals(false);
+ }
+ if (m_pFindNextInPageAction)
+ m_pFindNextInPageAction->setEnabled(fVisible);
+
+ if (m_pFindPreviousInPageAction)
+ m_pFindPreviousInPageAction->setEnabled(fVisible);
+}
+
+void UIHelpBrowserWidget::sltShowPrintDialog()
+{
+#ifdef VBOX_WS_X11
+ if (!m_pTabManager)
+ return;
+ QPrinter printer;
+ QPrintDialog printDialog(&printer, this);
+ if (printDialog.exec() == QDialog::Accepted)
+ m_pTabManager->printCurrent(printer);
+#endif
+}
+
+void UIHelpBrowserWidget::sltHelpEngineSetupFinished()
+{
+ AssertReturnVoid(m_pTabManager);
+ m_fIndexingFinished = true;
+ m_pTabManager->initializeTabs();
+}
+
+void UIHelpBrowserWidget::sltContentWidgetItemClicked(const QModelIndex & index)
+{
+ AssertReturnVoid(m_pTabManager && m_pHelpEngine && m_pContentWidget);
+ QUrl url = contentWidgetUrl(index);
+ if (!url.isValid())
+ return;
+ m_pTabManager->setSource(url);
+
+ m_pContentWidget->scrollTo(index, QAbstractItemView::EnsureVisible);
+ m_pContentWidget->expand(index);
+}
+
+void UIHelpBrowserWidget::sltViewerSourceChange(const QUrl &source)
+{
+ if (m_fModelContentCreated && m_pContentWidget && source.isValid() && m_pContentModel)
+ {
+ QModelIndex index = m_pContentWidget->indexOf(source);
+ QItemSelectionModel *pSelectionModel = m_pContentWidget->selectionModel();
+ if (pSelectionModel && index.isValid())
+ {
+ m_pContentWidget->blockSignals(true);
+ pSelectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
+ m_pContentWidget->scrollTo(index, QAbstractItemView::EnsureVisible);
+ m_pContentWidget->expand(index);
+ m_pContentWidget->blockSignals(false);
+ }
+ }
+}
+
+void UIHelpBrowserWidget::sltContentsCreated()
+{
+ m_fModelContentCreated = true;
+ if (m_pTabManager)
+ sltViewerSourceChange(m_pTabManager->currentSource());
+}
+
+void UIHelpBrowserWidget::sltIndexingStarted()
+{
+ if (m_pSearchContainerWidget)
+ m_pSearchContainerWidget->setEnabled(false);
+}
+
+void UIHelpBrowserWidget::sltIndexingFinished()
+{
+ AssertReturnVoid(m_pTabManager &&
+ m_pHelpEngine &&
+ m_pSearchContainerWidget);
+
+ m_pSearchContainerWidget->setEnabled(true);
+ m_fIndexingFinished = true;
+ /* Process the keyword queue. */
+ foreach (const QString strKeyword, m_keywordList)
+ findAndShowUrlForKeyword(strKeyword);
+ m_keywordList.clear();
+
+}
+
+void UIHelpBrowserWidget::sltSearchingStarted()
+{
+}
+
+void UIHelpBrowserWidget::sltSearchStart()
+{
+ AssertReturnVoid(m_pSearchEngine && m_pSearchQueryWidget);
+ m_pSearchEngine->search(m_pSearchQueryWidget->searchInput());
+}
+
+void UIHelpBrowserWidget::sltShowLinksContextMenu(const QPoint &pos)
+{
+ QWidget *pSender = qobject_cast<QWidget*>(sender());
+ if (!pSender)
+ return;
+
+ QUrl url;
+ if (pSender == m_pContentWidget)
+ url = contentWidgetUrl(m_pContentWidget->currentIndex());
+ else if (pSender == m_pSearchResultWidget)
+ {
+ QTextBrowser* browser = m_pSearchResultWidget->findChild<QTextBrowser*>();
+ if (!browser)
+ return;
+ QPoint browserPos = browser->mapFromGlobal(m_pSearchResultWidget->mapToGlobal(pos));
+ url = browser->anchorAt(browserPos);
+ }
+ else if (pSender == m_pBookmarksWidget)
+ {
+ /* Assuming that only the UIBookmarksListWidget under the m_pBookmarksWidget sends the context menu request: */
+ UIBookmarksListWidget *pListWidget = m_pBookmarksWidget->findChild<UIBookmarksListWidget*>();
+ if (!pListWidget)
+ return;
+ url = m_pBookmarksWidget->currentBookmarkUrl();
+ }
+ else
+ return;
+
+ bool fURLValid = url.isValid();
+
+ QMenu menu;
+ QAction *pOpen = menu.addAction(tr("Open Link"));
+ QAction *pOpenInNewTab = menu.addAction(tr("Open Link in New Tab"));
+ QAction *pCopyLink = menu.addAction(tr("Copy Link"));
+
+ pOpen->setData(url);
+ pOpenInNewTab->setData(url);
+ pCopyLink->setData(url);
+
+ pOpen->setEnabled(fURLValid);
+ pOpenInNewTab->setEnabled(fURLValid);
+ pCopyLink->setEnabled(fURLValid);
+
+ connect(pOpenInNewTab, &QAction::triggered, this, &UIHelpBrowserWidget::sltOpenLinkInNewTab);
+ connect(pOpen, &QAction::triggered, this, &UIHelpBrowserWidget::sltOpenLink);
+ connect(pCopyLink, &QAction::triggered, this, &UIHelpBrowserWidget::sltCopyLink);
+
+ if (pSender == m_pBookmarksWidget)
+ {
+ menu.addSeparator();
+ QAction *pDeleteBookmark = menu.addAction(tr("Delete Bookmark"));
+ QAction *pDeleteAllBookmarks = menu.addAction(tr("Delete All Bookmarks"));
+ pDeleteBookmark->setEnabled(fURLValid);
+
+ connect(pDeleteBookmark, &QAction::triggered, m_pBookmarksWidget, &UIBookmarksListContainer::sltDeleteSelectedBookmark);
+ connect(pDeleteAllBookmarks, &QAction::triggered, m_pBookmarksWidget, &UIBookmarksListContainer::sltDeleteAllBookmarks);
+ }
+
+ menu.exec(pSender->mapToGlobal(pos));
+}
+
+void UIHelpBrowserWidget::sltOpenLinkInNewTab()
+{
+ openLinkSlotHandler(sender(), true);
+}
+
+void UIHelpBrowserWidget::sltOpenLink()
+{
+ openLinkSlotHandler(sender(), false);
+}
+
+void UIHelpBrowserWidget::sltCopyLink()
+{
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ if (!pAction)
+ return;
+ QUrl url = pAction->data().toUrl();
+ if (url.isValid())
+ {
+ QClipboard *pClipboard = QApplication::clipboard();
+ if (pClipboard)
+ pClipboard->setText(url.toString());
+ }
+}
+
+void UIHelpBrowserWidget::sltAddNewBookmark(const QUrl &url, const QString &strTitle)
+{
+ if (m_pBookmarksWidget)
+ m_pBookmarksWidget->addBookmark(url, strTitle);
+ Q_UNUSED(url);
+ emit sigStatusBarMessage(QString("%1 %2").arg(tr("Bookmark added:")).arg(strTitle), 3000);
+}
+
+void UIHelpBrowserWidget::openLinkSlotHandler(QObject *pSenderObject, bool fOpenInNewTab)
+{
+ QAction *pAction = qobject_cast<QAction*>(pSenderObject);
+ if (!pAction)
+ return;
+ QUrl url = pAction->data().toUrl();
+ if (m_pTabManager && url.isValid())
+ m_pTabManager->setSource(url, fOpenInNewTab);
+}
+
+void UIHelpBrowserWidget::updateTabsMenu(const QStringList &titles)
+{
+ if (!m_pTabsMenu)
+ return;
+ m_pTabsMenu->clear();
+
+ QAction *pCloseTabAction = m_pTabsMenu->addAction(tr("Close T&ab"));
+ QAction *pCloseOtherTabsAction = m_pTabsMenu->addAction(tr("Close &Other Tabs"));
+
+ pCloseTabAction->setShortcut(QString("Ctrl+W"));
+ pCloseOtherTabsAction->setShortcut(QString("Ctrl+Shift+W"));
+
+ pCloseTabAction->setEnabled(titles.size() > 1);
+ pCloseOtherTabsAction->setEnabled(titles.size() > 1);
+
+ connect(pCloseTabAction, &QAction::triggered, m_pTabManager, &UIHelpBrowserTabManager::sltCloseCurrentTab);
+ connect(pCloseOtherTabsAction, &QAction::triggered, m_pTabManager, &UIHelpBrowserTabManager::sltCloseOtherTabs);
+
+ m_pTabsMenu->addSeparator();
+
+ for (int i = 0; i < titles.size(); ++i)
+ {
+ QAction *pAction = m_pTabsMenu->addAction(titles[i]);
+ pAction->setData(i);
+ connect(pAction, &QAction::triggered, this, &UIHelpBrowserWidget::sltTabChoose);
+ }
+ if (m_pTabManager)
+ sltCurrentTabChanged(m_pTabManager->currentIndex());
+}
+
+void UIHelpBrowserWidget::sltOpenLinkWithUrl(const QUrl &url)
+{
+ if (m_pTabManager && url.isValid())
+ m_pTabManager->setSource(url, false);
+}
+
+void UIHelpBrowserWidget::sltZoomActions(int iZoomOperation)
+{
+ if (iZoomOperation >= (int) UIHelpViewer::ZoomOperation_Max)
+ return;
+ UIHelpViewer::ZoomOperation enmOperation = (UIHelpViewer::ZoomOperation)(iZoomOperation);
+ m_pTabManager->sltHandleZoomRequest(enmOperation);
+}
+
+void UIHelpBrowserWidget::sltTabListChanged(const QStringList &titleList)
+{
+ updateTabsMenu(titleList);
+}
+
+void UIHelpBrowserWidget::sltTabChoose()
+{
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ if (!pAction)
+ return;
+ int iIndex = pAction->data().toInt();
+ if (m_pTabManager)
+ m_pTabManager->switchToTab(iIndex);
+}
+
+void UIHelpBrowserWidget::sltCurrentTabChanged(int iIndex)
+{
+ Q_UNUSED(iIndex);
+ if (!m_pTabsMenu)
+ return;
+
+ /** Mark the action with iIndex+3 by assigning an icon to it. it is iIndex+3 and not iIndex since we have
+ * two additional (close tab, close other tabs and a separator) action on top of the tab selection actions: */
+ QList<QAction*> list = m_pTabsMenu->actions();
+ for (int i = 0; i < list.size(); ++i)
+ list[i]->setIcon(QIcon());
+ if (iIndex+3 >= list.size())
+ return;
+ list[iIndex+3]->setIcon(UIIconPool::iconSet(":/help_browser_star_16px.png"));
+
+ if (m_pTabManager)
+ {
+ if (m_pCopySelectedTextAction)
+ m_pCopySelectedTextAction->setEnabled(m_pTabManager->hasCurrentTabSelectedText());
+ if (m_pFindInPageAction)
+ m_pFindInPageAction->setChecked(m_pTabManager->isFindInPageWidgetVisible());
+ if (m_pFindNextInPageAction)
+ m_pFindNextInPageAction->setEnabled(m_pTabManager->isFindInPageWidgetVisible());
+ if (m_pFindPreviousInPageAction)
+ m_pFindPreviousInPageAction->setEnabled(m_pTabManager->isFindInPageWidgetVisible());
+ }
+}
+
+void UIHelpBrowserWidget::sltZoomPercentageChanged(int iPercentage)
+{
+ if (m_pZoomMenuAction)
+ m_pZoomMenuAction->setZoomPercentage(iPercentage);
+ emit sigZoomPercentageChanged(iPercentage);
+}
+
+void UIHelpBrowserWidget::addActionToMenu(QMenu *pMenu, QAction *pAction)
+{
+ if (!pMenu || !pAction)
+ return;
+ pMenu->addAction(pAction);
+}
+
+#include "UIHelpBrowserWidget.moc"
+
+#endif /*#ifdef VBOX_WITH_QHELP_VIEWER*/
diff --git a/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserWidget.h b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserWidget.h
new file mode 100644
index 00000000..c4bb0297
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpBrowserWidget.h
@@ -0,0 +1,223 @@
+/* $Id: UIHelpBrowserWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIHelpBrowserWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_helpbrowser_UIHelpBrowserWidget_h
+#define FEQT_INCLUDED_SRC_helpbrowser_UIHelpBrowserWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPair>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QVBoxLayout;
+class QHelpEngine;
+class QHelpContentModel;
+class QHelpContentWidget;
+class QHelpIndexWidget;
+class QHelpSearchEngine;
+class QHelpSearchQueryWidget;
+class QHelpSearchResultWidget;
+class QSplitter;
+class QITabWidget;
+class QIToolBar;
+class UIActionPool;
+class UIBookmarksListContainer;
+class UIHelpBrowserTabManager;
+class UIZoomMenuAction;
+
+#ifdef VBOX_WITH_QHELP_VIEWER
+class SHARED_LIBRARY_STUFF UIHelpBrowserWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigCloseDialog();
+ void sigStatusBarVisible(bool fToggled);
+ void sigZoomPercentageChanged(int iPercentage);
+ void sigGoBackward();
+ void sigGoForward();
+ void sigGoHome();
+ void sigReloadPage();
+ void sigAddBookmark();
+ void sigStatusBarMessage(const QString &strMessage, int iTimeOut);
+
+public:
+
+ UIHelpBrowserWidget(EmbedTo enmEmbedding, const QString &strHelpFilePath, QWidget *pParent = 0);
+ ~UIHelpBrowserWidget();
+ QList<QMenu*> menus() const;
+ void showHelpForKeyword(const QString &strKeyword);
+#ifdef VBOX_WS_MAC
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+ int zoomPercentage() const;
+
+protected:
+
+ /** Returns whether the window should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const;
+
+private slots:
+
+ void sltHelpEngineSetupFinished();
+ void sltContentWidgetItemClicked(const QModelIndex &index);
+ void sltWidgetVisibilityToggle(bool togggled);
+ void sltShowPrintDialog();
+ void sltContentsCreated();
+ void sltIndexingStarted();
+ void sltIndexingFinished();
+ void sltSearchingStarted();
+ void sltSearchStart();
+ void sltViewerSourceChange(const QUrl &source);
+ void sltOpenLinkWithUrl(const QUrl &url);
+ void sltShowLinksContextMenu(const QPoint &pos);
+ void sltOpenLinkInNewTab();
+ void sltOpenLink();
+ void sltCopyLink();
+ void sltAddNewBookmark(const QUrl &url, const QString &strTitle);
+ void sltZoomActions(int iZoomOperation);
+ void sltTabListChanged(const QStringList &titleList);
+ void sltTabChoose();
+ void sltCurrentTabChanged(int iIndex);
+ void sltZoomPercentageChanged(int iPercentage);
+ void sltCopySelectedText();
+ void sltCopyAvailableChanged(bool fAvailable);
+ void sltFindInPage(bool fChecked);
+ void sltFindInPageWidgetVisibilityChanged(bool fVisible);
+ void sltFindNextInPage();
+ void sltFindPreviousInPage();
+ void sltHistoryChanged(bool fBackwardAvailable, bool fForwardAvailable);
+ void sltLinkHighlighted(const QUrl &url);
+ void sltMouseOverImage(const QString &strImageName);
+
+private:
+
+ void prepare();
+ void prepareActions();
+ void prepareWidgets();
+ void prepareSearchWidgets();
+ void prepareToolBar();
+ void prepareMenu();
+ void prepareConnections();
+
+ void loadOptions();
+ QStringList loadSavedUrlList();
+ /** Bookmark list is save as url-title pairs. */
+ void loadBookmarks();
+ void saveBookmarks();
+ void saveOptions();
+ void cleanup();
+ QUrl findIndexHtml() const;
+ /* Returns the url of the item with @p itemIndex. */
+ QUrl contentWidgetUrl(const QModelIndex &itemIndex);
+ void openLinkSlotHandler(QObject *pSenderObject, bool fOpenInNewTab);
+ void updateTabsMenu(const QStringList &titleList);
+
+ /** @name Event handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles Qt show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles Qt key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+ /* Looks for Url for the keyword using QHelpEngine API and shows it in a new tab whne successful. */
+ void findAndShowUrlForKeyword(const QString &strKeyword);
+ void addActionToMenu(QMenu *pMenu, QAction *pAction);
+
+ /** Holds the widget's embedding type. */
+ const EmbedTo m_enmEmbedding;
+ UIActionPool *m_pActionPool;
+ bool m_fIsPolished;
+
+ QVBoxLayout *m_pMainLayout;
+ QHBoxLayout *m_pTopLayout;
+ /** Container tab widget for content, index, bookmark widgets. Sits on a side bar. */
+ QITabWidget *m_pTabWidget;
+
+ /** @name Toolbar and menu variables.
+ * @{ */
+ QIToolBar *m_pToolBar;
+ /** @} */
+
+ QString m_strHelpFilePath;
+ /** Start the browser with this keyword. When not empty widget is shown `only` with html viewer and single tab.*/
+ QHelpEngine *m_pHelpEngine;
+ QSplitter *m_pSplitter;
+ QMenu *m_pFileMenu;
+ QMenu *m_pEditMenu;
+ QMenu *m_pViewMenu;
+ QMenu *m_pTabsMenu;
+ QMenu *m_pNavigationMenu;
+ QHelpContentWidget *m_pContentWidget;
+ QHelpIndexWidget *m_pIndexWidget;
+ QHelpContentModel *m_pContentModel;
+ QHelpSearchEngine *m_pSearchEngine;
+ QHelpSearchQueryWidget *m_pSearchQueryWidget;
+ QHelpSearchResultWidget *m_pSearchResultWidget;
+ UIHelpBrowserTabManager *m_pTabManager;
+ UIBookmarksListContainer *m_pBookmarksWidget;
+ QWidget *m_pSearchContainerWidget;
+ QAction *m_pPrintAction;
+ QAction *m_pQuitAction;
+ QAction *m_pShowHideSideBarAction;
+ QAction *m_pShowHideToolBarAction;
+ QAction *m_pShowHideStatusBarAction;
+ QAction *m_pCopySelectedTextAction;
+ QAction *m_pFindInPageAction;
+ QAction *m_pFindNextInPageAction;
+ QAction *m_pFindPreviousInPageAction;
+ QAction *m_pBackwardAction;
+ QAction *m_pForwardAction;
+ QAction *m_pHomeAction;
+ QAction *m_pReloadPageAction;
+ QAction *m_pAddBookmarkAction;
+
+ UIZoomMenuAction *m_pZoomMenuAction;
+
+ /* This is set t true when handling QHelpContentModel::contentsCreated signal. */
+ bool m_fModelContentCreated;
+ bool m_fIndexingFinished;
+ /** This queue is used in unlikely case where possibly several keywords are requested to be shown
+ * but indexing is not yet finished. In that case we queue the keywords and process them after
+ * after indexing is finished. */
+ QStringList m_keywordList;
+};
+
+#endif /* #ifdef VBOX_WITH_QHELP_VIEWER */
+#endif /* !FEQT_INCLUDED_SRC_helpbrowser_UIHelpBrowserWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpViewer.cpp b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpViewer.cpp
new file mode 100644
index 00000000..f8790531
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpViewer.cpp
@@ -0,0 +1,1104 @@
+/* $Id: UIHelpViewer.cpp $ */
+/** @file
+ * VBox Qt GUI - UIHelpViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QClipboard>
+#include <QtGlobal>
+#ifdef VBOX_WITH_QHELP_VIEWER
+ #include <QtHelp/QHelpEngine>
+ #include <QtHelp/QHelpContentWidget>
+ #include <QtHelp/QHelpIndexWidget>
+ #include <QtHelp/QHelpSearchEngine>
+ #include <QtHelp/QHelpSearchQueryWidget>
+ #include <QtHelp/QHelpSearchResultWidget>
+#endif
+#include <QLabel>
+#include <QMenu>
+#include <QHBoxLayout>
+#include <QGraphicsBlurEffect>
+#include <QLabel>
+#include <QPainter>
+#include <QScrollBar>
+#include <QTextBlock>
+#include <QWidgetAction>
+#ifdef RT_OS_SOLARIS
+# include <QFontDatabase>
+#endif
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UICursor.h"
+#include "UICommon.h"
+#include "UIHelpViewer.h"
+#include "UIHelpBrowserWidget.h"
+#include "UIIconPool.h"
+#include "UISearchLineEdit.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CSystemProperties.h"
+
+#ifdef VBOX_WITH_QHELP_VIEWER
+
+
+/*********************************************************************************************************************************
+* UIContextMenuNavigationAction definition. *
+*********************************************************************************************************************************/
+class UIContextMenuNavigationAction : public QWidgetAction
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigGoBackward();
+ void sigGoForward();
+ void sigGoHome();
+ void sigReloadPage();
+ void sigAddBookmark();
+
+public:
+
+ UIContextMenuNavigationAction(QObject *pParent = 0);
+ void setBackwardAvailable(bool fAvailable);
+ void setForwardAvailable(bool fAvailable);
+
+private slots:
+
+ void sltGoBackward();
+ void sltGoForward();
+ void sltGoHome();
+ void sltReloadPage();
+ void sltAddBookmark();
+
+private:
+
+ void prepare();
+ QIToolButton *m_pBackwardButton;
+ QIToolButton *m_pForwardButton;
+ QIToolButton *m_pHomeButton;
+ QIToolButton *m_pReloadPageButton;
+ QIToolButton *m_pAddBookmarkButton;
+};
+
+/*********************************************************************************************************************************
+* UIFindInPageWidget definition. *
+*********************************************************************************************************************************/
+class UIFindInPageWidget : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigDragging(const QPoint &delta);
+ void sigSearchTextChanged(const QString &strSearchText);
+ void sigSelectNextMatch();
+ void sigSelectPreviousMatch();
+ void sigClose();
+
+public:
+
+ UIFindInPageWidget(QWidget *pParent = 0);
+ void setMatchCountAndCurrentIndex(int iTotalMatchCount, int iCurrentlyScrolledIndex);
+ void clearSearchField();
+
+protected:
+
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ void prepare();
+ void retranslateUi();
+ UISearchLineEdit *m_pSearchLineEdit;
+ QIToolButton *m_pNextButton;
+ QIToolButton *m_pPreviousButton;
+ QIToolButton *m_pCloseButton;
+ QLabel *m_pDragMoveLabel;
+ QPoint m_previousMousePosition;
+};
+
+
+/*********************************************************************************************************************************
+* UIContextMenuNavigationAction implementation. *
+*********************************************************************************************************************************/
+UIContextMenuNavigationAction::UIContextMenuNavigationAction(QObject *pParent /* = 0 */)
+ :QWidgetAction(pParent)
+ , m_pBackwardButton(0)
+ , m_pForwardButton(0)
+ , m_pHomeButton(0)
+ , m_pReloadPageButton(0)
+ , m_pAddBookmarkButton(0)
+{
+ prepare();
+}
+
+void UIContextMenuNavigationAction::setBackwardAvailable(bool fAvailable)
+{
+ if (m_pBackwardButton)
+ m_pBackwardButton->setEnabled(fAvailable);
+}
+
+void UIContextMenuNavigationAction::setForwardAvailable(bool fAvailable)
+{
+ if (m_pForwardButton)
+ m_pForwardButton->setEnabled(fAvailable);
+}
+
+void UIContextMenuNavigationAction::sltGoBackward()
+{
+ emit sigGoBackward();
+ emit triggered();
+}
+
+void UIContextMenuNavigationAction::sltGoForward()
+{
+ emit sigGoForward();
+ emit triggered();
+}
+
+void UIContextMenuNavigationAction::sltGoHome()
+{
+ emit sigGoHome();
+ emit triggered();
+}
+
+void UIContextMenuNavigationAction::sltReloadPage()
+{
+ emit sigReloadPage();
+ emit triggered();
+}
+
+void UIContextMenuNavigationAction::sltAddBookmark()
+{
+ emit sigAddBookmark();
+ emit triggered();
+}
+
+void UIContextMenuNavigationAction::prepare()
+{
+ QWidget *pWidget = new QWidget;
+ setDefaultWidget(pWidget);
+ QHBoxLayout *pMainLayout = new QHBoxLayout(pWidget);
+ AssertReturnVoid(pMainLayout);
+
+ m_pBackwardButton = new QIToolButton;
+ m_pForwardButton = new QIToolButton;
+ m_pHomeButton = new QIToolButton;
+ m_pReloadPageButton = new QIToolButton;
+ m_pAddBookmarkButton = new QIToolButton;
+
+ AssertReturnVoid(m_pBackwardButton &&
+ m_pForwardButton &&
+ m_pHomeButton &&
+ m_pReloadPageButton);
+
+ m_pForwardButton->setEnabled(false);
+ m_pBackwardButton->setEnabled(false);
+ m_pHomeButton->setIcon(UIIconPool::iconSet(":/help_browser_home_16px.png", ":/help_browser_home_disabled_16px.png"));
+ m_pReloadPageButton->setIcon(UIIconPool::iconSet(":/help_browser_reload_16px.png", ":/help_browser_reload_disabled_16px.png"));
+ m_pForwardButton->setIcon(UIIconPool::iconSet(":/help_browser_forward_16px.png", ":/help_browser_forward_disabled_16px.png"));
+ m_pBackwardButton->setIcon(UIIconPool::iconSet(":/help_browser_backward_16px.png", ":/help_browser_backward_disabled_16px.png"));
+ m_pAddBookmarkButton->setIcon(UIIconPool::iconSet(":/help_browser_add_bookmark_16px.png", ":/help_browser_add_bookmark_disabled_16px.png"));
+
+ m_pHomeButton->setToolTip(UIHelpBrowserWidget::tr("Return to Start Page"));
+ m_pReloadPageButton->setToolTip(UIHelpBrowserWidget::tr("Reload the Current Page"));
+ m_pForwardButton->setToolTip(UIHelpBrowserWidget::tr("Go Forward to Next Page"));
+ m_pBackwardButton->setToolTip(UIHelpBrowserWidget::tr("Go Back to Previous Page"));
+ m_pAddBookmarkButton->setToolTip(UIHelpBrowserWidget::tr("Add a New Bookmark"));
+
+ pMainLayout->addWidget(m_pBackwardButton);
+ pMainLayout->addWidget(m_pForwardButton);
+ pMainLayout->addWidget(m_pHomeButton);
+ pMainLayout->addWidget(m_pReloadPageButton);
+ pMainLayout->addWidget(m_pAddBookmarkButton);
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ connect(m_pBackwardButton, &QIToolButton::pressed,
+ this, &UIContextMenuNavigationAction::sltGoBackward);
+ connect(m_pForwardButton, &QIToolButton::pressed,
+ this, &UIContextMenuNavigationAction::sltGoForward);
+ connect(m_pHomeButton, &QIToolButton::pressed,
+ this, &UIContextMenuNavigationAction::sltGoHome);
+ connect(m_pReloadPageButton, &QIToolButton::pressed,
+ this, &UIContextMenuNavigationAction::sltReloadPage);
+ connect(m_pAddBookmarkButton, &QIToolButton::pressed,
+ this, &UIContextMenuNavigationAction::sltAddBookmark);
+ connect(m_pReloadPageButton, &QIToolButton::pressed,
+ this, &UIContextMenuNavigationAction::sltAddBookmark);
+}
+
+
+/*********************************************************************************************************************************
+* UIFindInPageWidget implementation. *
+*********************************************************************************************************************************/
+UIFindInPageWidget::UIFindInPageWidget(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pSearchLineEdit(0)
+ , m_pNextButton(0)
+ , m_pPreviousButton(0)
+ , m_pCloseButton(0)
+ , m_previousMousePosition(-1, -1)
+{
+ prepare();
+}
+
+void UIFindInPageWidget::setMatchCountAndCurrentIndex(int iTotalMatchCount, int iCurrentlyScrolledIndex)
+{
+ if (!m_pSearchLineEdit)
+ return;
+ m_pSearchLineEdit->setMatchCount(iTotalMatchCount);
+ m_pSearchLineEdit->setScrollToIndex(iCurrentlyScrolledIndex);
+}
+
+void UIFindInPageWidget::clearSearchField()
+{
+ if (!m_pSearchLineEdit)
+ return;
+ m_pSearchLineEdit->blockSignals(true);
+ m_pSearchLineEdit->reset();
+ m_pSearchLineEdit->blockSignals(false);
+}
+
+bool UIFindInPageWidget::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ if (pObject == m_pDragMoveLabel)
+ {
+ if (pEvent->type() == QEvent::Enter)
+ UICursor::setCursor(m_pDragMoveLabel, Qt::CrossCursor);
+ else if (pEvent->type() == QEvent::Leave)
+ {
+ if (parentWidget())
+ UICursor::setCursor(m_pDragMoveLabel, parentWidget()->cursor());
+ }
+ else if (pEvent->type() == QEvent::MouseMove)
+ {
+ QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
+ if (pMouseEvent->buttons() == Qt::LeftButton)
+ {
+ if (m_previousMousePosition != QPoint(-1, -1))
+ emit sigDragging(pMouseEvent->globalPos() - m_previousMousePosition);
+ m_previousMousePosition = pMouseEvent->globalPos();
+ UICursor::setCursor(m_pDragMoveLabel, Qt::ClosedHandCursor);
+ }
+ }
+ else if (pEvent->type() == QEvent::MouseButtonRelease)
+ {
+ m_previousMousePosition = QPoint(-1, -1);
+ UICursor::setCursor(m_pDragMoveLabel, Qt::CrossCursor);
+ }
+ }
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
+}
+
+void UIFindInPageWidget::keyPressEvent(QKeyEvent *pEvent)
+{
+ switch (pEvent->key())
+ {
+ case Qt::Key_Escape:
+ emit sigClose();
+ return;
+ break;
+ case Qt::Key_Down:
+ emit sigSelectNextMatch();
+ return;
+ break;
+ case Qt::Key_Up:
+ emit sigSelectPreviousMatch();
+ return;
+ break;
+ default:
+ QIWithRetranslateUI<QWidget>::keyPressEvent(pEvent);
+ break;
+ }
+}
+
+void UIFindInPageWidget::prepare()
+{
+ setAutoFillBackground(true);
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
+
+ QHBoxLayout *pLayout = new QHBoxLayout(this);
+ m_pSearchLineEdit = new UISearchLineEdit;
+ AssertReturnVoid(pLayout && m_pSearchLineEdit);
+ setFocusProxy(m_pSearchLineEdit);
+ QFontMetrics fontMetric(m_pSearchLineEdit->font());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ setMinimumSize(40 * fontMetric.horizontalAdvance("x"),
+ fontMetric.height() +
+ qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) +
+ qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin));
+
+#else
+ setMinimumSize(40 * fontMetric.width("x"),
+ fontMetric.height() +
+ qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) +
+ qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin));
+#endif
+ connect(m_pSearchLineEdit, &UISearchLineEdit::textChanged,
+ this, &UIFindInPageWidget::sigSearchTextChanged);
+
+ m_pDragMoveLabel = new QLabel;
+ AssertReturnVoid(m_pDragMoveLabel);
+ m_pDragMoveLabel->installEventFilter(this);
+ m_pDragMoveLabel->setPixmap(QPixmap(":/drag_move_16px.png"));
+ pLayout->addWidget(m_pDragMoveLabel);
+
+
+ pLayout->setSpacing(0);
+ pLayout->addWidget(m_pSearchLineEdit);
+
+ m_pPreviousButton = new QIToolButton;
+ m_pNextButton = new QIToolButton;
+ m_pCloseButton = new QIToolButton;
+
+ pLayout->addWidget(m_pPreviousButton);
+ pLayout->addWidget(m_pNextButton);
+ pLayout->addWidget(m_pCloseButton);
+
+ m_pPreviousButton->setIcon(UIIconPool::iconSet(":/arrow_up_10px.png"));
+ m_pNextButton->setIcon(UIIconPool::iconSet(":/arrow_down_10px.png"));
+ m_pCloseButton->setIcon(UIIconPool::iconSet(":/close_16px.png"));
+
+ connect(m_pPreviousButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigSelectPreviousMatch);
+ connect(m_pNextButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigSelectNextMatch);
+ connect(m_pCloseButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigClose);
+}
+
+void UIFindInPageWidget::retranslateUi()
+{
+}
+
+
+/*********************************************************************************************************************************
+* UIHelpViewer implementation. *
+*********************************************************************************************************************************/
+
+UIHelpViewer::UIHelpViewer(const QHelpEngine *pHelpEngine, QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QTextBrowser>(pParent)
+ , m_pHelpEngine(pHelpEngine)
+ , m_pFindInPageWidget(new UIFindInPageWidget(this))
+ , m_fFindWidgetDragged(false)
+ , m_iMarginForFindWidget(qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin))
+ , m_iSelectedMatchIndex(0)
+ , m_iSearchTermLength(0)
+ , m_fOverlayMode(false)
+ , m_fCursorChanged(false)
+ , m_pOverlayLabel(0)
+ , m_iZoomPercentage(100)
+{
+ m_iInitialFontPointSize = font().pointSize();
+ setUndoRedoEnabled(true);
+ connect(m_pFindInPageWidget, &UIFindInPageWidget::sigDragging,
+ this, &UIHelpViewer::sltFindWidgetDrag);
+ connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSearchTextChanged,
+ this, &UIHelpViewer::sltFindInPageSearchTextChange);
+
+ connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSelectPreviousMatch,
+ this, &UIHelpViewer::sltSelectPreviousMatch);
+ connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSelectNextMatch,
+ this, &UIHelpViewer::sltSelectNextMatch);
+ connect(m_pFindInPageWidget, &UIFindInPageWidget::sigClose,
+ this, &UIHelpViewer::sltCloseFindInPageWidget);
+
+ m_defaultCursor = cursor();
+ m_handCursor = QCursor(Qt::PointingHandCursor);
+
+ m_pFindInPageWidget->setVisible(false);
+
+ m_pOverlayLabel = new QLabel(this);
+ if (m_pOverlayLabel)
+ {
+ m_pOverlayLabel->hide();
+ m_pOverlayLabel->installEventFilter(this);
+ }
+
+ m_pOverlayBlurEffect = new QGraphicsBlurEffect(this);
+ if (m_pOverlayBlurEffect)
+ {
+ viewport()->setGraphicsEffect(m_pOverlayBlurEffect);
+ m_pOverlayBlurEffect->setEnabled(false);
+ m_pOverlayBlurEffect->setBlurRadius(8);
+ }
+ retranslateUi();
+}
+
+QVariant UIHelpViewer::loadResource(int type, const QUrl &name)
+{
+ if (name.scheme() == "qthelp" && m_pHelpEngine)
+ return QVariant(m_pHelpEngine->fileData(name));
+ else
+ return QTextBrowser::loadResource(type, name);
+}
+
+void UIHelpViewer::emitHistoryChangedSignal()
+{
+ emit historyChanged();
+ emit backwardAvailable(true);
+}
+
+#ifdef VBOX_IS_QT6_OR_LATER /* it was setSource before 6.0 */
+void UIHelpViewer::doSetSource(const QUrl &url, QTextDocument::ResourceType type)
+#else
+void UIHelpViewer::setSource(const QUrl &url)
+#endif
+{
+ clearOverlay();
+ if (url.scheme() != "qthelp")
+ return;
+#ifdef VBOX_IS_QT6_OR_LATER /* it was setSource before 6.0 */
+ QTextBrowser::doSetSource(url, type);
+#else
+ QTextBrowser::setSource(url);
+#endif
+ QTextDocument *pDocument = document();
+ if (!pDocument || pDocument->isEmpty())
+ setText(UIHelpBrowserWidget::tr("<div><p><h3>404. Not found.</h3>The page <b>%1</b> could not be found.</p></div>").arg(url.toString()));
+ if (m_pFindInPageWidget && m_pFindInPageWidget->isVisible())
+ {
+ document()->undo();
+ m_pFindInPageWidget->clearSearchField();
+ }
+ iterateDocumentImages();
+ scaleImages();
+}
+
+void UIHelpViewer::toggleFindInPageWidget(bool fVisible)
+{
+ if (!m_pFindInPageWidget)
+ return;
+
+ /* Closing the find in page widget causes QTextBrowser to jump to the top of the document. This hack puts it back into position: */
+ int iPosition = verticalScrollBar()->value();
+ m_iMarginForFindWidget = verticalScrollBar()->width() +
+ qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
+ /* Try to position the widget somewhere meaningful initially: */
+ if (!m_fFindWidgetDragged)
+ m_pFindInPageWidget->move(width() - m_iMarginForFindWidget - m_pFindInPageWidget->width(),
+ m_iMarginForFindWidget);
+
+ m_pFindInPageWidget->setVisible(fVisible);
+
+ if (!fVisible)
+ {
+ /* Clear highlights: */
+ setExtraSelections(QList<QTextEdit::ExtraSelection>());
+ m_pFindInPageWidget->clearSearchField();
+ verticalScrollBar()->setValue(iPosition);
+ }
+ else
+ m_pFindInPageWidget->setFocus();
+ emit sigFindInPageWidgetToogle(fVisible);
+}
+
+void UIHelpViewer::reload()
+{
+ setSource(source());
+}
+
+void UIHelpViewer::sltToggleFindInPageWidget(bool fVisible)
+{
+ clearOverlay();
+ toggleFindInPageWidget(fVisible);
+}
+
+void UIHelpViewer::sltCloseFindInPageWidget()
+{
+ sltToggleFindInPageWidget(false);
+}
+
+void UIHelpViewer::setFont(const QFont &font)
+{
+ QIWithRetranslateUI<QTextBrowser>::setFont(font);
+ /* Make sure the font size of the find in widget stays constant: */
+ if (m_pFindInPageWidget)
+ {
+ QFont wFont(font);
+ wFont.setPointSize(m_iInitialFontPointSize);
+ m_pFindInPageWidget->setFont(wFont);
+ }
+}
+
+bool UIHelpViewer::isFindInPageWidgetVisible() const
+{
+ if (m_pFindInPageWidget)
+ return m_pFindInPageWidget->isVisible();
+ return false;
+}
+
+void UIHelpViewer::setZoomPercentage(int iZoomPercentage)
+{
+ m_iZoomPercentage = iZoomPercentage;
+ clearOverlay();
+ scaleFont();
+ scaleImages();
+}
+
+void UIHelpViewer::setHelpFileList(const QList<QUrl> &helpFileList)
+{
+ m_helpFileList = helpFileList;
+ /* File list necessary to get the image data from the help engine: */
+ iterateDocumentImages();
+ scaleImages();
+}
+
+bool UIHelpViewer::hasSelectedText() const
+{
+ return textCursor().hasSelection();
+}
+
+void UIHelpViewer::contextMenuEvent(QContextMenuEvent *event)
+{
+ QMenu menu;
+
+ if (textCursor().hasSelection())
+ {
+ QAction *pCopySelectedTextAction = new QAction(UIHelpBrowserWidget::tr("Copy Selected Text"));
+ connect(pCopySelectedTextAction, &QAction::triggered,
+ this, &UIHelpViewer::copy);
+ menu.addAction(pCopySelectedTextAction);
+ menu.addSeparator();
+ }
+
+ UIContextMenuNavigationAction *pNavigationActions = new UIContextMenuNavigationAction;
+ pNavigationActions->setBackwardAvailable(isBackwardAvailable());
+ pNavigationActions->setForwardAvailable(isForwardAvailable());
+
+ connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoBackward,
+ this, &UIHelpViewer::sigGoBackward);
+ connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoForward,
+ this, &UIHelpViewer::sigGoForward);
+ connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoHome,
+ this, &UIHelpViewer::sigGoHome);
+ connect(pNavigationActions, &UIContextMenuNavigationAction::sigReloadPage,
+ this, &UIHelpViewer::reload);
+ connect(pNavigationActions, &UIContextMenuNavigationAction::sigAddBookmark,
+ this, &UIHelpViewer::sigAddBookmark);
+
+ QAction *pOpenLinkAction = new QAction(UIHelpBrowserWidget::tr("Open Link"));
+ connect(pOpenLinkAction, &QAction::triggered,
+ this, &UIHelpViewer::sltOpenLink);
+
+ QAction *pOpenInNewTabAction = new QAction(UIHelpBrowserWidget::tr("Open Link in New Tab"));
+ connect(pOpenInNewTabAction, &QAction::triggered,
+ this, &UIHelpViewer::sltOpenLinkInNewTab);
+
+ QAction *pCopyLink = new QAction(UIHelpBrowserWidget::tr("Copy Link"));
+ connect(pCopyLink, &QAction::triggered,
+ this, &UIHelpViewer::sltCopyLink);
+
+ QAction *pFindInPage = new QAction(UIHelpBrowserWidget::tr("Find in Page"));
+ pFindInPage->setCheckable(true);
+ if (m_pFindInPageWidget)
+ pFindInPage->setChecked(m_pFindInPageWidget->isVisible());
+ connect(pFindInPage, &QAction::toggled, this, &UIHelpViewer::sltToggleFindInPageWidget);
+
+ menu.addAction(pNavigationActions);
+ menu.addAction(pOpenLinkAction);
+ menu.addAction(pOpenInNewTabAction);
+ menu.addAction(pCopyLink);
+ menu.addAction(pFindInPage);
+
+ QString strAnchor = anchorAt(event->pos());
+ if (!strAnchor.isEmpty())
+ {
+ QString strLink = source().resolved(anchorAt(event->pos())).toString();
+ pOpenLinkAction->setData(strLink);
+ pOpenInNewTabAction->setData(strLink);
+ pCopyLink->setData(strLink);
+ }
+ else
+ {
+ pOpenLinkAction->setEnabled(false);
+ pOpenInNewTabAction->setEnabled(false);
+ pCopyLink->setEnabled(false);
+ }
+
+ menu.exec(event->globalPos());
+}
+
+void UIHelpViewer::resizeEvent(QResizeEvent *pEvent)
+{
+ if (m_fOverlayMode)
+ clearOverlay();
+ /* Make sure the widget stays inside the parent during parent resize: */
+ if (m_pFindInPageWidget)
+ {
+ if (!isRectInside(m_pFindInPageWidget->geometry(), m_iMarginForFindWidget))
+ moveFindWidgetIn(m_iMarginForFindWidget);
+ }
+ QIWithRetranslateUI<QTextBrowser>::resizeEvent(pEvent);
+}
+
+void UIHelpViewer::wheelEvent(QWheelEvent *pEvent)
+{
+ if (m_fOverlayMode && !pEvent)
+ return;
+ /* QTextBrowser::wheelEvent scales font when some modifiers are pressed. We dont want that: */
+ if (pEvent->modifiers() == Qt::NoModifier)
+ QTextBrowser::wheelEvent(pEvent);
+ else if (pEvent->modifiers() & Qt::ControlModifier)
+ {
+ if (pEvent->angleDelta().y() > 0)
+ emit sigZoomRequest(ZoomOperation_In);
+ else if (pEvent->angleDelta().y() < 0)
+ emit sigZoomRequest(ZoomOperation_Out);
+ }
+}
+
+void UIHelpViewer::mouseReleaseEvent(QMouseEvent *pEvent)
+{
+ bool fOverlayMode = m_fOverlayMode;
+ clearOverlay();
+
+ QString strAnchor = anchorAt(pEvent->pos());
+
+ if (!strAnchor.isEmpty())
+ {
+
+ QString strLink = source().resolved(strAnchor).toString();
+
+ if (source().resolved(strAnchor).scheme() != "qthelp" && pEvent->button() == Qt::LeftButton)
+ {
+ uiCommon().openURL(strLink);
+ return;
+ }
+
+ if ((pEvent->modifiers() & Qt::ControlModifier) ||
+ pEvent->button() == Qt::MiddleButton)
+ {
+
+ emit sigOpenLinkInNewTab(strLink, true);
+ return;
+ }
+ }
+ QIWithRetranslateUI<QTextBrowser>::mousePressEvent(pEvent);
+
+ if (!fOverlayMode)
+ loadImageAtPosition(pEvent->globalPos());
+}
+
+void UIHelpViewer::mousePressEvent(QMouseEvent *pEvent)
+{
+ QIWithRetranslateUI<QTextBrowser>::mousePressEvent(pEvent);
+}
+
+void UIHelpViewer::setImageOverCursor(QPoint globalPosition)
+{
+ QPoint viewportCoordinates = viewport()->mapFromGlobal(globalPosition);
+ QTextCursor cursor = cursorForPosition(viewportCoordinates);
+ if (!m_fCursorChanged && cursor.charFormat().isImageFormat())
+ {
+ m_fCursorChanged = true;
+ UICursor::setCursor(viewport(), m_handCursor);
+ emit sigMouseOverImage(cursor.charFormat().toImageFormat().name());
+ }
+ if (m_fCursorChanged && !cursor.charFormat().isImageFormat())
+ {
+ UICursor::setCursor(viewport(), m_defaultCursor);
+ m_fCursorChanged = false;
+ }
+
+}
+
+void UIHelpViewer::mouseMoveEvent(QMouseEvent *pEvent)
+{
+ if (m_fOverlayMode)
+ return;
+ setImageOverCursor(pEvent->globalPos());
+ QIWithRetranslateUI<QTextBrowser>::mouseMoveEvent(pEvent);
+}
+
+void UIHelpViewer::mouseDoubleClickEvent(QMouseEvent *pEvent)
+{
+ clearOverlay();
+ QIWithRetranslateUI<QTextBrowser>::mouseDoubleClickEvent(pEvent);
+}
+
+void UIHelpViewer::paintEvent(QPaintEvent *pEvent)
+{
+ QIWithRetranslateUI<QTextBrowser>::paintEvent(pEvent);
+ QPainter painter(viewport());
+ foreach(const DocumentImage &image, m_imageMap)
+ {
+ QRect rect = cursorRect(image.m_textCursor);
+ QPixmap newPixmap = image.m_pixmap.scaledToWidth(image.m_fScaledWidth, Qt::SmoothTransformation);
+ QRectF imageRect(rect.x() - newPixmap.width(), rect.y(), newPixmap.width(), newPixmap.height());
+
+ int iMargin = 3;
+ QRectF fillRect(imageRect.x() - iMargin, imageRect.y() - iMargin,
+ imageRect.width() + 2 * iMargin, imageRect.height() + 2 * iMargin);
+ /** @todo I need to find the default color somehow and replace hard coded Qt::white. */
+ painter.fillRect(fillRect, Qt::white);
+ painter.drawPixmap(imageRect, newPixmap, newPixmap.rect());
+ }
+}
+
+bool UIHelpViewer::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ if (pObject == m_pOverlayLabel)
+ {
+ if (pEvent->type() == QEvent::MouseButtonPress ||
+ pEvent->type() == QEvent::MouseButtonDblClick)
+ clearOverlay();
+ }
+ return QIWithRetranslateUI<QTextBrowser>::eventFilter(pObject, pEvent);
+}
+
+void UIHelpViewer::keyPressEvent(QKeyEvent *pEvent)
+{
+ if (pEvent && pEvent->key() == Qt::Key_Escape)
+ clearOverlay();
+ if (pEvent && pEvent->modifiers() &Qt::ControlModifier)
+ {
+ switch (pEvent->key())
+ {
+ case Qt::Key_Equal:
+ emit sigZoomRequest(ZoomOperation_In);
+ break;
+ case Qt::Key_Minus:
+ emit sigZoomRequest(ZoomOperation_Out);
+ break;
+ case Qt::Key_0:
+ emit sigZoomRequest(ZoomOperation_Reset);
+ break;
+ default:
+ break;
+ }
+ }
+ QIWithRetranslateUI<QTextBrowser>::keyPressEvent(pEvent);
+}
+
+void UIHelpViewer::retranslateUi()
+{
+}
+
+void UIHelpViewer::moveFindWidgetIn(int iMargin)
+{
+ if (!m_pFindInPageWidget)
+ return;
+
+ QRect rect = m_pFindInPageWidget->geometry();
+ if (rect.left() < iMargin)
+ rect.translate(-rect.left() + iMargin, 0);
+ if (rect.right() > width() - iMargin)
+ rect.translate((width() - iMargin - rect.right()), 0);
+ if (rect.top() < iMargin)
+ rect.translate(0, -rect.top() + iMargin);
+
+ if (rect.bottom() > height() - iMargin)
+ rect.translate(0, (height() - iMargin - rect.bottom()));
+ m_pFindInPageWidget->setGeometry(rect);
+ m_pFindInPageWidget->update();
+}
+
+bool UIHelpViewer::isRectInside(const QRect &rect, int iMargin) const
+{
+ if (rect.left() < iMargin || rect.top() < iMargin)
+ return false;
+ if (rect.right() > width() - iMargin || rect.bottom() > height() - iMargin)
+ return false;
+ return true;
+}
+
+void UIHelpViewer::findAllMatches(const QString &searchString)
+{
+ QTextDocument *pDocument = document();
+ AssertReturnVoid(pDocument);
+
+ m_matchedCursorPosition.clear();
+ if (searchString.isEmpty())
+ return;
+ QTextCursor cursor(pDocument);
+ QTextDocument::FindFlags flags;
+ int iMatchCount = 0;
+ while (!cursor.isNull() && !cursor.atEnd())
+ {
+ cursor = pDocument->find(searchString, cursor, flags);
+ if (!cursor.isNull())
+ {
+ m_matchedCursorPosition << cursor.position() - searchString.length();
+ ++iMatchCount;
+ }
+ }
+}
+
+void UIHelpViewer::highlightFinds(int iSearchTermLength)
+{
+ QList<QTextEdit::ExtraSelection> extraSelections;
+ for (int i = 0; i < m_matchedCursorPosition.size(); ++i)
+ {
+ QTextEdit::ExtraSelection selection;
+ QTextCursor cursor = textCursor();
+ cursor.setPosition(m_matchedCursorPosition[i]);
+ cursor.setPosition(m_matchedCursorPosition[i] + iSearchTermLength, QTextCursor::KeepAnchor);
+ QTextCharFormat format = cursor.charFormat();
+ format.setBackground(Qt::yellow);
+
+ selection.cursor = cursor;
+ selection.format = format;
+ extraSelections.append(selection);
+ }
+ setExtraSelections(extraSelections);
+}
+
+void UIHelpViewer::selectMatch(int iMatchIndex, int iSearchStringLength)
+{
+ QTextCursor cursor = textCursor();
+ /* Move the cursor to the beginning of the matched string: */
+ cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex), QTextCursor::MoveAnchor);
+ /* Move the cursor to the end of the matched string while keeping the anchor at the begining thus selecting the text: */
+ cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex) + iSearchStringLength, QTextCursor::KeepAnchor);
+ ensureCursorVisible();
+ setTextCursor(cursor);
+}
+
+void UIHelpViewer::sltOpenLinkInNewTab()
+{
+ QAction *pSender = qobject_cast<QAction*>(sender());
+ if (!pSender)
+ return;
+ QUrl url = pSender->data().toUrl();
+ if (url.isValid())
+ emit sigOpenLinkInNewTab(url, false);
+}
+
+void UIHelpViewer::sltOpenLink()
+{
+ QAction *pSender = qobject_cast<QAction*>(sender());
+ if (!pSender)
+ return;
+ QUrl url = pSender->data().toUrl();
+ if (url.isValid())
+ setSource(url);
+}
+
+void UIHelpViewer::sltCopyLink()
+{
+ QAction *pSender = qobject_cast<QAction*>(sender());
+ if (!pSender)
+ return;
+ QUrl url = pSender->data().toUrl();
+ if (url.isValid())
+ {
+ QClipboard *pClipboard = QApplication::clipboard();
+ if (pClipboard)
+ pClipboard->setText(url.toString());
+ }
+}
+
+void UIHelpViewer::sltFindWidgetDrag(const QPoint &delta)
+{
+ if (!m_pFindInPageWidget)
+ return;
+ QRect geo = m_pFindInPageWidget->geometry();
+ geo.translate(delta);
+
+ /* Allow the move if m_pFindInPageWidget stays inside after the move: */
+ if (isRectInside(geo, m_iMarginForFindWidget))
+ m_pFindInPageWidget->move(m_pFindInPageWidget->pos() + delta);
+ m_fFindWidgetDragged = true;
+ update();
+}
+
+void UIHelpViewer::sltFindInPageSearchTextChange(const QString &strSearchText)
+{
+ m_iSearchTermLength = strSearchText.length();
+ findAllMatches(strSearchText);
+ highlightFinds(m_iSearchTermLength);
+ selectMatch(0, m_iSearchTermLength);
+ if (m_pFindInPageWidget)
+ m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), 0);
+}
+
+void UIHelpViewer::sltSelectPreviousMatch()
+{
+ m_iSelectedMatchIndex = m_iSelectedMatchIndex <= 0 ? m_matchedCursorPosition.size() - 1 : (m_iSelectedMatchIndex - 1);
+ selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
+ if (m_pFindInPageWidget)
+ m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
+}
+
+void UIHelpViewer::sltSelectNextMatch()
+{
+ m_iSelectedMatchIndex = m_iSelectedMatchIndex >= m_matchedCursorPosition.size() - 1 ? 0 : (m_iSelectedMatchIndex + 1);
+ selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
+ if (m_pFindInPageWidget)
+ m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
+}
+
+void UIHelpViewer::iterateDocumentImages()
+{
+ m_imageMap.clear();
+ QTextCursor cursor = textCursor();
+ cursor.movePosition(QTextCursor::Start);
+ while (!cursor.atEnd())
+ {
+ cursor.movePosition(QTextCursor::NextCharacter);
+ if (cursor.charFormat().isImageFormat())
+ {
+ QTextImageFormat imageFormat = cursor.charFormat().toImageFormat();
+ /* There seems to be two cursors per image. Use the first one: */
+ if (m_imageMap.contains(imageFormat.name()))
+ continue;
+ QHash<QString, DocumentImage>::iterator iterator = m_imageMap.insert(imageFormat.name(), DocumentImage());
+ DocumentImage &image = iterator.value();
+ image.m_fInitialWidth = imageFormat.width();
+ image.m_strName = imageFormat.name();
+ image.m_textCursor = cursor;
+ QUrl imageFileUrl;
+ foreach (const QUrl &fileUrl, m_helpFileList)
+ {
+ if (fileUrl.toString().contains(imageFormat.name(), Qt::CaseInsensitive))
+ {
+ imageFileUrl = fileUrl;
+ break;
+ }
+ }
+ if (imageFileUrl.isValid())
+ {
+ QByteArray fileData = m_pHelpEngine->fileData(imageFileUrl);
+ if (!fileData.isEmpty())
+ image.m_pixmap.loadFromData(fileData,"PNG");
+ }
+ }
+ }
+}
+
+void UIHelpViewer::scaleFont()
+{
+ QFont mFont = font();
+ mFont.setPointSize(m_iInitialFontPointSize * m_iZoomPercentage / 100.);
+ setFont(mFont);
+}
+
+void UIHelpViewer::scaleImages()
+{
+ for (QHash<QString, DocumentImage>::iterator iterator = m_imageMap.begin();
+ iterator != m_imageMap.end(); ++iterator)
+ {
+ DocumentImage &image = *iterator;
+ QTextCursor cursor = image.m_textCursor;
+ QTextCharFormat format = cursor.charFormat();
+ if (!format.isImageFormat())
+ continue;
+ QTextImageFormat imageFormat = format.toImageFormat();
+ image.m_fScaledWidth = image.m_fInitialWidth * m_iZoomPercentage / 100.;
+ imageFormat.setWidth(image.m_fScaledWidth);
+ cursor.deletePreviousChar();
+ cursor.deleteChar();
+ cursor.insertImage(imageFormat);
+ }
+}
+
+void UIHelpViewer::clearOverlay()
+{
+ AssertReturnVoid(m_pOverlayLabel);
+ setImageOverCursor(cursor().pos());
+
+ if (!m_fOverlayMode)
+ return;
+ m_overlayPixmap = QPixmap();
+ m_fOverlayMode = false;
+ if (m_pOverlayBlurEffect)
+ m_pOverlayBlurEffect->setEnabled(false);
+ m_pOverlayLabel->hide();
+}
+
+void UIHelpViewer::enableOverlay()
+{
+ AssertReturnVoid(m_pOverlayLabel);
+ m_fOverlayMode = true;
+ if (m_pOverlayBlurEffect)
+ m_pOverlayBlurEffect->setEnabled(true);
+ UICursor::setCursor(viewport(), m_defaultCursor);
+ m_fCursorChanged = false;
+ toggleFindInPageWidget(false);
+
+ /* Scale the image to 1:1 as long as it fits into avaible space (minus some margins and scrollbar sizes): */
+ int vWidth = 0;
+ if (verticalScrollBar() && verticalScrollBar()->isVisible())
+ vWidth = verticalScrollBar()->width();
+ int hMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) +
+ qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) + vWidth;
+
+ int hHeight = 0;
+ if (horizontalScrollBar() && horizontalScrollBar()->isVisible())
+ hHeight = horizontalScrollBar()->height();
+ int vMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) +
+ qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) + hHeight;
+
+ QSize size(qMin(width() - hMargin, m_overlayPixmap.width()),
+ qMin(height() - vMargin, m_overlayPixmap.height()));
+ m_pOverlayLabel->setPixmap(m_overlayPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
+ m_pOverlayLabel->show();
+
+ /* Center the label: */
+ int x = 0.5 * (width() - vWidth - m_pOverlayLabel->width());
+ int y = 0.5 * (height() - hHeight - m_pOverlayLabel->height());
+ m_pOverlayLabel->move(x, y);
+}
+
+void UIHelpViewer::loadImageAtPosition(const QPoint &globalPosition)
+{
+ clearOverlay();
+ QPoint viewportCoordinates = viewport()->mapFromGlobal(globalPosition);
+ QTextCursor cursor = cursorForPosition(viewportCoordinates);
+ if (!cursor.charFormat().isImageFormat())
+ return;
+ /* Dont zoom into image if mouse button released after a mouse drag: */
+ if (textCursor().hasSelection())
+ return;
+
+ QTextImageFormat imageFormat = cursor.charFormat().toImageFormat();
+ QUrl imageFileUrl;
+ foreach (const QUrl &fileUrl, m_helpFileList)
+ {
+ if (fileUrl.toString().contains(imageFormat.name(), Qt::CaseInsensitive))
+ {
+ imageFileUrl = fileUrl;
+ break;
+ }
+ }
+
+ if (!imageFileUrl.isValid())
+ return;
+ QByteArray fileData = m_pHelpEngine->fileData(imageFileUrl);
+ if (!fileData.isEmpty())
+ {
+ m_overlayPixmap.loadFromData(fileData,"PNG");
+ if (!m_overlayPixmap.isNull())
+ enableOverlay();
+ }
+}
+
+
+#include "UIHelpViewer.moc"
+
+#endif /* #ifdef VBOX_WITH_QHELP_VIEWER */
diff --git a/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpViewer.h b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpViewer.h
new file mode 100644
index 00000000..56a1179e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpViewer.h
@@ -0,0 +1,181 @@
+/* $Id: UIHelpViewer.h $ */
+/** @file
+ * VBox Qt GUI - UIHelpViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_helpbrowser_UIHelpViewer_h
+#define FEQT_INCLUDED_SRC_helpbrowser_UIHelpViewer_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTextBrowser>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QHelpEngine;
+class QGraphicsBlurEffect;
+class QLabel;
+class UIFindInPageWidget;
+
+#ifdef VBOX_WITH_QHELP_VIEWER
+
+/** A QTextBrowser extension used as poor man's html viewer. Since we were not happy with the quality of QTextBrowser's image
+ * rendering and didn't want to use WebKit module, this extension redraws the document images as overlays with improved QPainter
+ * parameters. There is also a small hack to render clicked image 1:1 (and the rest of the document blurred)
+ * for a zoom-in-image functionality. This extension can also scale the images while scaling the document. In contrast
+ * QTextBrowser scales only fonts. */
+class UIHelpViewer : public QIWithRetranslateUI<QTextBrowser>
+{
+
+ Q_OBJECT;
+
+public:
+
+ enum ZoomOperation
+ {
+ ZoomOperation_In = 0,
+ ZoomOperation_Out,
+ ZoomOperation_Reset,
+ ZoomOperation_Max
+ };
+
+signals:
+
+ void sigOpenLinkInNewTab(const QUrl &url, bool fBackground);
+ void sigFindInPageWidgetToogle(bool fVisible);
+ void sigFontPointSizeChanged(int iFontPointSize);
+ void sigGoBackward();
+ void sigGoForward();
+ void sigGoHome();
+ void sigAddBookmark();
+ void sigMouseOverImage(const QString &strImageName);
+ void sigZoomRequest(ZoomOperation enmZoomOperation);
+
+public:
+
+ UIHelpViewer(const QHelpEngine *pHelpEngine, QWidget *pParent = 0);
+ virtual QVariant loadResource(int type, const QUrl &name) RT_OVERRIDE;
+ void emitHistoryChangedSignal();
+#ifndef VBOX_IS_QT6_OR_LATER /* must override doSetSource since 6.0 */
+ virtual void setSource(const QUrl &url) RT_OVERRIDE;
+#endif
+ void setFont(const QFont &);
+ bool isFindInPageWidgetVisible() const;
+ void setZoomPercentage(int iZoomPercentage);
+ void setHelpFileList(const QList<QUrl> &helpFileList);
+ bool hasSelectedText() const;
+ static const QPair<int, int> zoomPercentageMinMax;
+ void toggleFindInPageWidget(bool fVisible);
+
+public slots:
+
+ void sltSelectPreviousMatch();
+ void sltSelectNextMatch();
+ virtual void reload() /* overload */;
+
+protected:
+
+ virtual void contextMenuEvent(QContextMenuEvent *event) RT_OVERRIDE;
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+ virtual void wheelEvent(QWheelEvent *pEvent) RT_OVERRIDE;
+ virtual void mouseReleaseEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ virtual void mouseMoveEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ virtual void mouseDoubleClickEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+#ifdef VBOX_IS_QT6_OR_LATER /* it was setSource before 6.0 */
+ virtual void doSetSource(const QUrl &url, QTextDocument::ResourceType type = QTextDocument::UnknownResource) RT_OVERRIDE;
+#endif
+
+private slots:
+
+ void sltOpenLinkInNewTab();
+ void sltOpenLink();
+ void sltCopyLink();
+ void sltFindWidgetDrag(const QPoint &delta);
+ void sltFindInPageSearchTextChange(const QString &strSearchText);
+ void sltToggleFindInPageWidget(bool fVisible);
+ void sltCloseFindInPageWidget();
+
+private:
+
+ struct DocumentImage
+ {
+ qreal m_fInitialWidth;
+ qreal m_fScaledWidth;
+ QTextCursor m_textCursor;
+ QPixmap m_pixmap;
+ QString m_strName;
+ };
+
+ void retranslateUi();
+ bool isRectInside(const QRect &rect, int iMargin) const;
+ void moveFindWidgetIn(int iMargin);
+ void findAllMatches(const QString &searchString);
+ void highlightFinds(int iSearchTermLength);
+ void selectMatch(int iMatchIndex, int iSearchStringLength);
+ /** Scans the document and finds all the images, whose pixmap data is retrieved from QHelp system to be used in overlay draw. */
+ void iterateDocumentImages();
+ void scaleFont();
+ void scaleImages();
+ /** If there is image at @p globalPosition then its data is loaded to m_overlayPixmap. */
+ void loadImageAtPosition(const QPoint &globalPosition);
+ void clearOverlay();
+ void enableOverlay();
+ void setImageOverCursor(QPoint globalPosition);
+
+ const QHelpEngine* m_pHelpEngine;
+ UIFindInPageWidget *m_pFindInPageWidget;
+ /** Initilized as false and set to true once the user drag moves the find widget. */
+ bool m_fFindWidgetDragged;
+ int m_iMarginForFindWidget;
+ /** Document positions of the cursors within the document for all matches. */
+ QVector<int> m_matchedCursorPosition;
+ int m_iSelectedMatchIndex;
+ int m_iSearchTermLength;
+ int m_iInitialFontPointSize;
+ /** A container to store the original image sizes/positions in the document. key is image name value is DocumentImage. */
+ QHash<QString, DocumentImage> m_imageMap;
+ /** Used to change th document cursor back from m_handCursor. */
+ QCursor m_defaultCursor;
+ QCursor m_handCursor;
+ /** We need this list from th QHelp system to obtain information of images. */
+ QList<QUrl> m_helpFileList;
+ QPixmap m_overlayPixmap;
+ bool m_fOverlayMode;
+ bool m_fCursorChanged;
+ QLabel *m_pOverlayLabel;
+ QGraphicsBlurEffect *m_pOverlayBlurEffect;
+ int m_iZoomPercentage;
+};
+
+#endif /* #ifdef VBOX_WITH_QHELP_VIEWER */
+#endif /* !FEQT_INCLUDED_SRC_helpbrowser_UIHelpViewer_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/logviewer/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogBookmark.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogBookmark.h
new file mode 100644
index 00000000..e0220a2b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogBookmark.h
@@ -0,0 +1,55 @@
+/* $Id: UIVMLogBookmark.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogBookmark_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogBookmark_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+
+struct UIVMLogBookmark
+{
+ UIVMLogBookmark(){}
+ UIVMLogBookmark(int iLineNumber, int iCursorPosition, const QString &strBlockText)
+ : m_iLineNumber(iLineNumber)
+ , m_iCursorPosition(iCursorPosition)
+ , m_strBlockText(strBlockText){}
+
+ bool operator==(const UIVMLogBookmark& otherBookmark) const
+ {
+ return m_iLineNumber == otherBookmark.m_iLineNumber;
+ }
+
+ int m_iLineNumber;
+ int m_iCursorPosition;
+ QString m_strBlockText;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogBookmark_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogPage.cpp b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogPage.cpp
new file mode 100644
index 00000000..9f3fc78f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogPage.cpp
@@ -0,0 +1,344 @@
+/* $Id: UIVMLogPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDateTime>
+#include <QDir>
+#include <QVBoxLayout>
+#if defined(RT_OS_SOLARIS)
+# include <QFontDatabase>
+#endif
+#include <QPainter>
+#include <QScrollBar>
+#include <QTextBlock>
+
+/* GUI includes: */
+#include "UIVMLogPage.h"
+#include "UIVMLogViewerTextEdit.h"
+
+
+/*********************************************************************************************************************************
+* UIVMLogTab implementation. *
+*********************************************************************************************************************************/
+
+UIVMLogTab::UIVMLogTab(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_uMachineId(uMachineId)
+ , m_strMachineName(strMachineName)
+{
+}
+const QUuid &UIVMLogTab::machineId() const
+{
+ return m_uMachineId;
+}
+
+const QString UIVMLogTab::machineName() const
+{
+ return m_strMachineName;
+}
+
+
+/*********************************************************************************************************************************
+* UIVMLogPage implementation. *
+*********************************************************************************************************************************/
+
+UIVMLogPage::UIVMLogPage(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName)
+ : UIVMLogTab(pParent, uMachineId, strMachineName)
+ , m_pMainLayout(0)
+ , m_pTextEdit(0)
+ , m_iSelectedBookmarkIndex(-1)
+ , m_bFiltered(false)
+ , m_iLogFileId(-1)
+{
+ prepare();
+}
+
+UIVMLogPage::~UIVMLogPage()
+{
+ cleanup();
+}
+
+int UIVMLogPage::defaultLogPageWidth() const
+{
+ if (!m_pTextEdit)
+ return 0;
+
+ /* Compute a width for 132 characters plus scrollbar and frame width: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iDefaultWidth = m_pTextEdit->fontMetrics().horizontalAdvance(QChar('x')) * 132 +
+#else
+ int iDefaultWidth = m_pTextEdit->fontMetrics().width(QChar('x')) * 132 +
+#endif
+ m_pTextEdit->verticalScrollBar()->width() +
+ m_pTextEdit->frameWidth() * 2;
+
+ return iDefaultWidth;
+}
+
+
+void UIVMLogPage::prepare()
+{
+ prepareWidgets();
+ retranslateUi();
+}
+
+void UIVMLogPage::prepareWidgets()
+{
+ m_pMainLayout = new QHBoxLayout();
+ setLayout(m_pMainLayout);
+ m_pMainLayout->setSpacing(0);
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pTextEdit = new UIVMLogViewerTextEdit(this);
+ m_pMainLayout->addWidget(m_pTextEdit);
+
+ connect(m_pTextEdit, &UIVMLogViewerTextEdit::sigAddBookmark, this, &UIVMLogPage::sltAddBookmark);
+ connect(m_pTextEdit, &UIVMLogViewerTextEdit::sigDeleteBookmark, this, &UIVMLogPage::sltDeleteBookmark);
+}
+
+QPlainTextEdit *UIVMLogPage::textEdit()
+{
+ return m_pTextEdit;
+}
+
+QTextDocument* UIVMLogPage::document()
+{
+ if (!m_pTextEdit)
+ return 0;
+ return m_pTextEdit->document();
+}
+
+void UIVMLogPage::retranslateUi()
+{
+}
+
+void UIVMLogPage::cleanup()
+{
+}
+
+void UIVMLogPage::setLogContent(const QString &strLogContent, bool fError)
+{
+ if (!fError)
+ {
+ m_strLog = strLogContent;
+ setTextEditText(strLogContent);
+ }
+ else
+ {
+ markForError();
+ setTextEditTextAsHtml(strLogContent);
+ }
+}
+
+const QString& UIVMLogPage::logString() const
+{
+ return m_strLog;
+}
+
+void UIVMLogPage::setLogFileName(const QString &strLogFileName)
+{
+ m_strLogFileName = strLogFileName;
+}
+
+const QString& UIVMLogPage::logFileName() const
+{
+ return m_strLogFileName;
+}
+
+void UIVMLogPage::setTextEditText(const QString &strText)
+{
+ if (!m_pTextEdit)
+ return;
+
+ m_pTextEdit->setPlainText(strText);
+ /* Move the cursor position to end: */
+ QTextCursor cursor = m_pTextEdit->textCursor();
+ cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
+ m_pTextEdit->setTextCursor(cursor);
+ update();
+}
+
+void UIVMLogPage::setTextEditTextAsHtml(const QString &strText)
+{
+ if (!m_pTextEdit)
+ return;
+ if (document())
+ document()->setHtml(strText);
+ update();
+}
+
+void UIVMLogPage::markForError()
+{
+ if (!m_pTextEdit)
+ return;
+ m_pTextEdit->setWrapLines(true);
+}
+
+void UIVMLogPage::setScrollBarMarkingsVector(const QVector<float> &vector)
+{
+ if (!m_pTextEdit)
+ return;
+ m_pTextEdit->setScrollBarMarkingsVector(vector);
+ update();
+}
+
+void UIVMLogPage::clearScrollBarMarkingsVector()
+{
+ if (!m_pTextEdit)
+ return;
+ m_pTextEdit->clearScrollBarMarkingsVector();
+ update();
+}
+
+void UIVMLogPage::documentUndo()
+{
+ if (!m_pTextEdit)
+ return;
+ if (m_pTextEdit->document())
+ m_pTextEdit->document()->undo();
+}
+
+
+
+void UIVMLogPage::deleteAllBookmarks()
+{
+ m_bookmarkManager.deleteAllBookmarks();
+ updateTextEditBookmarkLineSet();
+}
+
+void UIVMLogPage::scrollToBookmark(int bookmarkIndex)
+{
+ if (!m_pTextEdit)
+ return;
+ m_pTextEdit->setCursorPosition(m_bookmarkManager.cursorPosition(bookmarkIndex));
+}
+
+const QVector<UIVMLogBookmark>& UIVMLogPage::bookmarkList() const
+{
+ return m_bookmarkManager.bookmarkList();
+}
+
+void UIVMLogPage::sltAddBookmark(const UIVMLogBookmark& bookmark)
+{
+ m_bookmarkManager.addBookmark(bookmark);
+ updateTextEditBookmarkLineSet();
+ emit sigBookmarksUpdated();
+}
+
+void UIVMLogPage::sltDeleteBookmark(const UIVMLogBookmark& bookmark)
+{
+ m_bookmarkManager.deleteBookmark(bookmark);
+ updateTextEditBookmarkLineSet();
+ emit sigBookmarksUpdated();
+}
+
+void UIVMLogPage::deleteBookmarkByIndex(int iIndex)
+{
+ m_bookmarkManager.deleteBookmarkByIndex(iIndex);
+ updateTextEditBookmarkLineSet();
+ emit sigBookmarksUpdated();
+}
+
+void UIVMLogPage::updateTextEditBookmarkLineSet()
+{
+ if (!m_pTextEdit)
+ return;
+ m_pTextEdit->setBookmarkLineSet(m_bookmarkManager.lineSet());
+}
+
+bool UIVMLogPage::isFiltered() const
+{
+ return m_bFiltered;
+}
+
+void UIVMLogPage::setFiltered(bool filtered)
+{
+ if (m_bFiltered == filtered)
+ return;
+ m_bFiltered = filtered;
+ if (m_pTextEdit)
+ {
+ m_pTextEdit->setShownTextIsFiltered(m_bFiltered);
+ m_pTextEdit->update();
+ }
+ emit sigLogPageFilteredChanged(m_bFiltered);
+}
+
+void UIVMLogPage::setShowLineNumbers(bool bShowLineNumbers)
+{
+ if (!m_pTextEdit)
+ return;
+ m_pTextEdit->setShowLineNumbers(bShowLineNumbers);
+}
+
+void UIVMLogPage::setWrapLines(bool bWrapLines)
+{
+ if (!m_pTextEdit)
+ return;
+ m_pTextEdit->setWrapLines(bWrapLines);
+}
+
+QFont UIVMLogPage::currentFont() const
+{
+ if (!m_pTextEdit)
+ return QFont();
+ return m_pTextEdit->font();
+}
+
+void UIVMLogPage::setCurrentFont(QFont font)
+{
+ if (m_pTextEdit)
+ m_pTextEdit->setCurrentFont(font);
+}
+
+void UIVMLogPage::setLogFileId(int iLogFileId)
+{
+ m_iLogFileId = iLogFileId;
+}
+
+int UIVMLogPage::logFileId() const
+{
+ return m_iLogFileId;
+}
+
+void UIVMLogPage::scrollToEnd()
+{
+ if (m_pTextEdit)
+ m_pTextEdit->scrollToEnd();
+}
+
+void UIVMLogPage::saveScrollBarPosition()
+{
+ if (m_pTextEdit)
+ m_pTextEdit->saveScrollBarPosition();
+}
+
+void UIVMLogPage::restoreScrollBarPosition()
+{
+ if (m_pTextEdit)
+ m_pTextEdit->restoreScrollBarPosition();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogPage.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogPage.h
new file mode 100644
index 00000000..5c7590ba
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogPage.h
@@ -0,0 +1,239 @@
+/* $Id: UIVMLogPage.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogPage_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+#include <QUuid>
+#include <QPair>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIVMLogBookmark.h"
+
+/* Forward declarations: */
+class QITabWidget;
+class QHBoxLayout;
+class QPlainTextEdit;
+class UIVMLogViewerTextEdit;
+
+
+class UIVMLogBookmarkManager
+{
+public:
+ void addBookmark(const UIVMLogBookmark& newBookmark)
+ {
+ foreach (const UIVMLogBookmark& bookmark, m_bookmarks)
+ if (bookmark == newBookmark)
+ return;
+ m_bookmarks << newBookmark;
+ }
+
+ void addBookmark(int iCursorPosition, int iLineNumber, QString strBlockText)
+ {
+ foreach (const UIVMLogBookmark& bookmark, m_bookmarks)
+ if (bookmark.m_iLineNumber == iLineNumber)
+ return;
+ m_bookmarks << UIVMLogBookmark(iCursorPosition, iLineNumber, strBlockText);
+ }
+
+ void deleteBookmark(const UIVMLogBookmark& bookmark)
+ {
+ int index = -1;
+ for (int i = 0; i < m_bookmarks.size() && index == -1; ++i)
+ {
+ if (bookmark == m_bookmarks[i])
+ index = i;
+ }
+ deleteBookmarkByIndex(index);
+ }
+
+ void deleteBookmarkByIndex(int iIndex)
+ {
+ if (iIndex >= m_bookmarks.size() || iIndex < 0)
+ return;
+ m_bookmarks.removeAt(iIndex);
+ }
+
+ void deleteAllBookmarks()
+ {
+ m_bookmarks.clear();
+ }
+
+ int cursorPosition(int bookmarkIndex)
+ {
+ if (bookmarkIndex >= m_bookmarks.size())
+ return 0;
+ return m_bookmarks[bookmarkIndex].m_iCursorPosition;
+ }
+
+ QSet<int> lineSet() const
+ {
+ QSet<int> lines;
+ foreach (const UIVMLogBookmark& bookmark, m_bookmarks)
+ lines << bookmark.m_iLineNumber;
+ return lines;
+ }
+
+ const QVector<UIVMLogBookmark>& bookmarkList() const
+ {
+ return m_bookmarks;
+ }
+
+private:
+
+ QVector<UIVMLogBookmark> m_bookmarks;
+};
+
+
+class UIVMLogTab : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIVMLogTab(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName);
+ const QUuid &machineId() const;
+ const QString machineName() const;
+
+private:
+
+ QUuid m_uMachineId;
+ QString m_strMachineName;
+};
+
+/** UIVMLogPage defines data and functionalities of the each tab page of a UIVMLogViewerWidget.
+ * It stores the original log file content , a list of bookmarks, etc */
+class UIVMLogPage : public UIVMLogTab
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigBookmarksUpdated();
+ void sigLogPageFilteredChanged(bool isFiltered);
+
+public:
+
+ UIVMLogPage(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName);
+ ~UIVMLogPage();
+
+ /** Returns the width of the current log page. return 0 if there is no current log page: */
+ int defaultLogPageWidth() const;
+
+ QPlainTextEdit *textEdit();
+ QTextDocument *document();
+
+ void setLogContent(const QString &strLogContent, bool fError);
+ const QString& logString() const;
+
+ void setLogFileName(const QString &strFileName);
+ const QString& logFileName() const;
+
+ /** Marks the plain text edit When we dont have a log content. */
+ void markForError();
+
+ void setScrollBarMarkingsVector(const QVector<float> &vector);
+ void clearScrollBarMarkingsVector();
+
+ /** Undos the changes done to textDocument */
+ void documentUndo();
+
+ const QVector<UIVMLogBookmark>& bookmarkList() const;
+
+ void deleteAllBookmarks();
+ /** Scrolls the plain text edit to the bookmark with index @a bookmarkIndex. */
+ void scrollToBookmark(int bookmarkIndex);
+
+ bool isFiltered() const;
+ void setFiltered(bool filtered);
+
+ void setShowLineNumbers(bool bShowLineNumbers);
+ void setWrapLines(bool bWrapLines);
+
+ QFont currentFont() const;
+ void setCurrentFont(QFont font);
+
+ void setLogFileId(int iLogFileId);
+ int logFileId() const;
+
+ void scrollToEnd();
+
+ void saveScrollBarPosition();
+ void restoreScrollBarPosition();
+
+ void deleteBookmarkByIndex(int iIndex);
+
+private slots:
+
+ void sltAddBookmark(const UIVMLogBookmark& bookmark);
+ void sltDeleteBookmark(const UIVMLogBookmark& bookmark);
+
+private:
+
+ void prepare();
+ void prepareWidgets();
+ void cleanup();
+ void retranslateUi();
+ void updateTextEditBookmarkLineSet();
+
+ /** Set plaintextEdit's text. Note that the text we
+ * show currently might be different than
+ * m_strLog. For example during filtering. */
+ void setTextEditText(const QString &strText);
+ void setTextEditTextAsHtml(const QString &strText);
+
+ QHBoxLayout *m_pMainLayout;
+ UIVMLogViewerTextEdit *m_pTextEdit;
+ /** Stores the log file (unmodified by filtering etc) content. */
+ QString m_strLog;
+ /** Stores full path and name of the log file. */
+ QString m_strLogFileName;
+ /** Stores the bookmarks of the logpage. All other bookmark related containers are updated wrt. this one. */
+ UIVMLogBookmarkManager m_bookmarkManager;
+
+ /** Keeps the index of the selected bookmark. Used especially when moving from one tab to another. */
+ int m_iSelectedBookmarkIndex;
+
+ /** @name Filtering related state variables
+ * @{ */
+ /** Designates whether currently displayed text is log text or a filtered version of it. That is
+ if m_bFiltered is false than (m_strLog == m_pTextEdit->text()). */
+ bool m_bFiltered;
+ /** @} */
+ /** The id we pass to CMachine::ReadLog. Used while refreshing and saving page content. */
+ int m_iLogFileId;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerBookmarksPanel.cpp b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerBookmarksPanel.cpp
new file mode 100644
index 00000000..27e360f8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerBookmarksPanel.cpp
@@ -0,0 +1,271 @@
+/* $Id: UIVMLogViewerBookmarksPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QStyle>
+#ifdef RT_OS_SOLARIS
+# include <QFontDatabase>
+#endif
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIVMLogViewerBookmarksPanel.h"
+#include "UIVMLogViewerWidget.h"
+
+
+UIVMLogViewerBookmarksPanel::UIVMLogViewerBookmarksPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer)
+ : UIVMLogViewerPanel(pParent, pViewer)
+ , m_iMaxBookmarkTextLength(60)
+ , m_pBookmarksComboBox(0)
+ , m_pGotoSelectedBookmark(0)
+ , m_pDeleteAllButton(0)
+ , m_pDeleteCurrentButton(0)
+ , m_pNextButton(0)
+ , m_pPreviousButton(0)
+{
+ prepare();
+}
+
+void UIVMLogViewerBookmarksPanel::updateBookmarkList(const QVector<UIVMLogBookmark>& bookmarkList)
+{
+ if (!m_pBookmarksComboBox || !viewer())
+ return;
+
+ m_pBookmarksComboBox->clear();
+ QStringList bList;
+ bList << "";
+ for (int i = 0; i < bookmarkList.size(); ++i)
+ {
+ QString strItem = QString("BookMark %1 at Line %2: %3").arg(QString::number(i)).
+ arg(QString::number(bookmarkList[i].m_iLineNumber)).arg(bookmarkList[i].m_strBlockText);
+
+ if (strItem.length() > m_iMaxBookmarkTextLength)
+ {
+ strItem.resize(m_iMaxBookmarkTextLength);
+ strItem.replace(m_iMaxBookmarkTextLength, 3, QString("..."));
+ }
+ bList << strItem;
+ }
+ m_pBookmarksComboBox->addItems(bList);
+ /* Goto last item of the combobox. Avoid emitting sigBookmarkSelected since we dont want text edit to scroll to there: */
+ m_pBookmarksComboBox->blockSignals(true);
+ m_pBookmarksComboBox->setCurrentIndex(m_pBookmarksComboBox->count()-1);
+ m_pBookmarksComboBox->blockSignals(false);
+}
+
+void UIVMLogViewerBookmarksPanel::disableEnableBookmarking(bool flag)
+{
+ m_pBookmarksComboBox->setEnabled(flag);
+ m_pGotoSelectedBookmark->setEnabled(flag);
+ m_pDeleteAllButton->setEnabled(flag);
+ m_pDeleteCurrentButton->setEnabled(flag);
+ m_pNextButton->setEnabled(flag);
+ m_pPreviousButton->setEnabled(flag);
+}
+
+QString UIVMLogViewerBookmarksPanel::panelName() const
+{
+ return "BookmarksPanel";
+}
+
+void UIVMLogViewerBookmarksPanel::setBookmarkIndex(int index)
+{
+ if (!m_pBookmarksComboBox)
+ return;
+ /* If there is only Title of the combo, then goto that item: */
+ if (m_pBookmarksComboBox->count() == 1 || index >= m_pBookmarksComboBox->count())
+ {
+ m_pBookmarksComboBox->setCurrentIndex(0);
+ return;
+ }
+ /* index+1 since we always have a 0th item in our combo-box. */
+ m_pBookmarksComboBox->setCurrentIndex(index+1);
+}
+
+void UIVMLogViewerBookmarksPanel::prepareWidgets()
+{
+ if (!mainLayout())
+ return;
+
+ /* Create bookmark combo/button layout: */
+ QHBoxLayout *pComboButtonLayout = new QHBoxLayout;
+ if (pComboButtonLayout)
+ {
+ pComboButtonLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ pComboButtonLayout->setSpacing(5);
+#else
+ pComboButtonLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2);
+#endif
+
+ /* Create bookmark combo-box: */
+ m_pBookmarksComboBox = new QComboBox;
+ if (m_pBookmarksComboBox)
+ {
+ m_pBookmarksComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ /* Make sure we have 0th item in our combo-box. */
+ m_pBookmarksComboBox->insertItem(0, "");
+ pComboButtonLayout->addWidget(m_pBookmarksComboBox);
+ }
+
+ /* Create bookmark button layout 1: */
+ QHBoxLayout *pButtonLayout1 = new QHBoxLayout;
+ if (pButtonLayout1)
+ {
+ pButtonLayout1->setContentsMargins(0, 0, 0, 0);
+ pButtonLayout1->setSpacing(0);
+
+ /* Create goto selected bookmark button: */
+ m_pGotoSelectedBookmark = new QIToolButton;
+ if (m_pGotoSelectedBookmark)
+ {
+ m_pGotoSelectedBookmark->setIcon(UIIconPool::iconSet(":/log_viewer_goto_selected_bookmark_16px.png"));
+ pButtonLayout1->addWidget(m_pGotoSelectedBookmark);
+ }
+
+ /* Create goto previous bookmark button: */
+ m_pPreviousButton = new QIToolButton;
+ if (m_pPreviousButton)
+ {
+ m_pPreviousButton->setIcon(UIIconPool::iconSet(":/log_viewer_goto_previous_bookmark_16px.png"));
+ pButtonLayout1->addWidget(m_pPreviousButton);
+ }
+
+ /* Create goto next bookmark button: */
+ m_pNextButton = new QIToolButton;
+ if (m_pNextButton)
+ {
+ m_pNextButton->setIcon(UIIconPool::iconSet(":/log_viewer_goto_next_bookmark_16px.png"));
+ pButtonLayout1->addWidget(m_pNextButton);
+ }
+
+ pComboButtonLayout->addLayout(pButtonLayout1);
+ }
+
+ /* Create bookmark button layout 2: */
+ QHBoxLayout *pButtonLayout2 = new QHBoxLayout;
+ if (pButtonLayout2)
+ {
+ pButtonLayout2->setContentsMargins(0, 0, 0, 0);
+ pButtonLayout2->setSpacing(0);
+
+ /* Create delete current bookmark button: */
+ m_pDeleteCurrentButton = new QIToolButton;
+ if (m_pDeleteCurrentButton)
+ {
+ m_pDeleteCurrentButton->setIcon(UIIconPool::iconSet(":/log_viewer_delete_current_bookmark_16px.png"));
+ pButtonLayout2->addWidget(m_pDeleteCurrentButton);
+ }
+
+ /* Create delete all bookmarks button: */
+ m_pDeleteAllButton = new QIToolButton;
+ if (m_pDeleteAllButton)
+ {
+ m_pDeleteAllButton->setIcon(UIIconPool::iconSet(":/log_viewer_delete_all_bookmarks_16px.png"));
+ pButtonLayout2->addWidget(m_pDeleteAllButton);
+ }
+
+ pComboButtonLayout->addLayout(pButtonLayout2);
+ }
+
+ mainLayout()->addLayout(pComboButtonLayout);
+ }
+}
+
+void UIVMLogViewerBookmarksPanel::prepareConnections()
+{
+ connect(m_pBookmarksComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIVMLogViewerBookmarksPanel::sltBookmarkSelected);
+
+ connect(m_pGotoSelectedBookmark, &QIToolButton::clicked, this, &UIVMLogViewerBookmarksPanel::sltGotoSelectedBookmark);
+ connect(m_pNextButton, &QIToolButton::clicked, this, &UIVMLogViewerBookmarksPanel::sltGotoNextBookmark);
+ connect(m_pPreviousButton, &QIToolButton::clicked, this, &UIVMLogViewerBookmarksPanel::sltGotoPreviousBookmark);
+
+ connect(m_pDeleteAllButton, &QIToolButton::clicked, this, &UIVMLogViewerBookmarksPanel::sigDeleteAllBookmarks);
+ connect(m_pDeleteCurrentButton, &QIToolButton::clicked, this, &UIVMLogViewerBookmarksPanel::sltDeleteCurrentBookmark);
+}
+
+
+void UIVMLogViewerBookmarksPanel::retranslateUi()
+{
+ UIVMLogViewerPanel::retranslateUi();
+
+ m_pDeleteCurrentButton->setToolTip(UIVMLogViewerWidget::tr("Delete the current bookmark"));
+ m_pDeleteAllButton->setToolTip(UIVMLogViewerWidget::tr("Delete all bookmarks"));
+ m_pNextButton->setToolTip(UIVMLogViewerWidget::tr("Go to the next bookmark"));
+ m_pPreviousButton->setToolTip(UIVMLogViewerWidget::tr("Go to the previous bookmark"));
+ m_pGotoSelectedBookmark->setToolTip(UIVMLogViewerWidget::tr("Go to selected bookmark"));
+}
+
+void UIVMLogViewerBookmarksPanel::sltDeleteCurrentBookmark()
+{
+ if (!m_pBookmarksComboBox)
+ return;
+
+ if (m_pBookmarksComboBox->currentIndex() == 0)
+ return;
+ emit sigDeleteBookmarkByIndex(m_pBookmarksComboBox->currentIndex() - 1);
+}
+
+void UIVMLogViewerBookmarksPanel::sltBookmarkSelected(int index)
+{
+ /* Do nothing if the index is 0, that is combo-box title item: */
+ if (index <= 0)
+ return;
+ emit sigBookmarkSelected(index - 1);
+}
+
+
+void UIVMLogViewerBookmarksPanel::sltGotoNextBookmark()
+{
+ /* go to next bookmark or wrap around to the beginning of the list: */
+ if (m_pBookmarksComboBox->currentIndex() == m_pBookmarksComboBox->count()-1)
+ m_pBookmarksComboBox->setCurrentIndex(1);
+ else
+ m_pBookmarksComboBox->setCurrentIndex(m_pBookmarksComboBox->currentIndex() + 1);
+}
+
+
+void UIVMLogViewerBookmarksPanel::sltGotoPreviousBookmark()
+{
+ /* go to previous bookmark or wrap around to the end of the list: */
+ if (m_pBookmarksComboBox->currentIndex() <= 1)
+ m_pBookmarksComboBox->setCurrentIndex(m_pBookmarksComboBox->count() - 1);
+ else
+ m_pBookmarksComboBox->setCurrentIndex(m_pBookmarksComboBox->currentIndex() - 1);
+}
+
+void UIVMLogViewerBookmarksPanel::sltGotoSelectedBookmark()
+{
+ if (!m_pBookmarksComboBox || m_pBookmarksComboBox->count() <= 1)
+ return;
+ emit sigBookmarkSelected(m_pBookmarksComboBox->currentIndex() - 1);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerBookmarksPanel.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerBookmarksPanel.h
new file mode 100644
index 00000000..4c6a46bd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerBookmarksPanel.h
@@ -0,0 +1,100 @@
+/* $Id: UIVMLogViewerBookmarksPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerBookmarksPanel_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerBookmarksPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIVMLogBookmark.h"
+#include "UIVMLogViewerPanel.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QWidget;
+class QIToolButton;
+
+/** UIVMLogViewerPanel extension providing GUI for bookmark management. Show a list of bookmarks currently set
+ * for displayed log page. It has controls to navigate and clear bookmarks. */
+class UIVMLogViewerBookmarksPanel : public UIVMLogViewerPanel
+{
+ Q_OBJECT;
+
+public:
+
+ UIVMLogViewerBookmarksPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer);
+
+ /** Adds a single bookmark to an existing list of bookmarks. Possibly called
+ * by UIVMLogViewerWidget when user adds a bookmark thru context menu etc. */
+ void addBookmark(const QPair<int, QString> &newBookmark);
+ /** Clear the bookmark list and show this list instead. Probably done after
+ * user switches to another log page tab etc. */
+ void setBookmarksList(const QVector<QPair<int, QString> > &bookmarkList);
+ void updateBookmarkList(const QVector<UIVMLogBookmark>& bookmarkList);
+ /** Disable/enable all the widget except the close button */
+ void disableEnableBookmarking(bool flag);
+ virtual QString panelName() const RT_OVERRIDE;
+
+signals:
+
+ void sigDeleteBookmarkByIndex(int bookmarkIndex);
+ void sigDeleteAllBookmarks();
+ void sigBookmarkSelected(int index);
+
+protected:
+
+ virtual void prepareWidgets() RT_OVERRIDE;
+ virtual void prepareConnections() RT_OVERRIDE;
+
+ /** Handles the translation event. */
+ void retranslateUi();
+
+private slots:
+
+ void sltDeleteCurrentBookmark();
+ void sltBookmarkSelected(int index);
+ void sltGotoNextBookmark();
+ void sltGotoPreviousBookmark();
+ void sltGotoSelectedBookmark();
+
+private:
+
+ /** @a index is the index of the curent bookmark. */
+ void setBookmarkIndex(int index);
+
+ const int m_iMaxBookmarkTextLength;
+ QComboBox *m_pBookmarksComboBox;
+ QIToolButton *m_pGotoSelectedBookmark;
+ QIToolButton *m_pDeleteAllButton;
+ QIToolButton *m_pDeleteCurrentButton;
+ QIToolButton *m_pNextButton;
+ QIToolButton *m_pPreviousButton;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerBookmarksPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerDialog.cpp b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerDialog.cpp
new file mode 100644
index 00000000..514787e7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerDialog.cpp
@@ -0,0 +1,239 @@
+/* $Id: UIVMLogViewerDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewerDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#if defined(RT_OS_SOLARIS)
+# include <QFontDatabase>
+#endif
+#include <QDialogButtonBox>
+#include <QKeyEvent>
+#include <QLabel>
+#include <QPlainTextEdit>
+#include <QPushButton>
+#include <QScrollBar>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIVMLogViewerDialog.h"
+#include "UIVMLogViewerWidget.h"
+#include "UICommon.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Class UIVMLogViewerDialogFactory implementation. *
+*********************************************************************************************************************************/
+
+UIVMLogViewerDialogFactory::UIVMLogViewerDialogFactory(UIActionPool *pActionPool /* = 0 */,
+ const QUuid &uMachineId /* = QUuid()*/,
+ const QString &strMachineName /* = QString() */)
+ : m_pActionPool(pActionPool)
+ , m_uMachineId(uMachineId)
+ , m_strMachineName(strMachineName)
+{
+}
+
+void UIVMLogViewerDialogFactory::create(QIManagerDialog *&pDialog, QWidget *pCenterWidget)
+{
+ pDialog = new UIVMLogViewerDialog(pCenterWidget, m_pActionPool, m_uMachineId, m_strMachineName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVMLogViewerDialog implementation. *
+*********************************************************************************************************************************/
+
+UIVMLogViewerDialog::UIVMLogViewerDialog(QWidget *pCenterWidget, UIActionPool *pActionPool,
+ const QUuid &uMachineId /* = QUuid()*/,
+ const QString &strMachineName /* = QString() */)
+ : QIWithRetranslateUI<QIManagerDialog>(pCenterWidget)
+ , m_pActionPool(pActionPool)
+ , m_uMachineId(uMachineId)
+ , m_iGeometrySaveTimerId(-1)
+ , m_strMachineName(strMachineName)
+{
+}
+
+UIVMLogViewerDialog::~UIVMLogViewerDialog()
+{
+}
+
+void UIVMLogViewerDialog::setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items)
+{
+ Q_UNUSED(items);
+ UIVMLogViewerWidget *pLogViewerWidget = qobject_cast<UIVMLogViewerWidget*>(widget());
+ if (pLogViewerWidget)
+ pLogViewerWidget->setSelectedVMListItems(items);
+}
+
+void UIVMLogViewerDialog::addSelectedVMListItems(const QList<UIVirtualMachineItem*> &items)
+{
+ Q_UNUSED(items);
+ UIVMLogViewerWidget *pLogViewerWidget = qobject_cast<UIVMLogViewerWidget*>(widget());
+ if (pLogViewerWidget)
+ pLogViewerWidget->addSelectedVMListItems(items);
+}
+
+void UIVMLogViewerDialog::retranslateUi()
+{
+ /* Translate window title: */
+ if (!m_strMachineName.isEmpty())
+ setWindowTitle(UIVMLogViewerWidget::tr("%1 - Log Viewer").arg(m_strMachineName));
+ else
+ setWindowTitle(UIVMLogViewerWidget::tr("Log Viewer"));
+
+ /* Translate buttons: */
+ button(ButtonType_Close)->setText(UIVMLogViewerWidget::tr("Close"));
+ button(ButtonType_Help)->setText(UIVMLogViewerWidget::tr("Help"));
+ button(ButtonType_Close)->setStatusTip(UIVMLogViewerWidget::tr("Close dialog"));
+ button(ButtonType_Help)->setStatusTip(UIVMLogViewerWidget::tr("Show dialog help"));
+ button(ButtonType_Close)->setShortcut(Qt::Key_Escape);
+ button(ButtonType_Help)->setShortcut(QKeySequence::HelpContents);
+ button(ButtonType_Close)->setToolTip(UIVMLogViewerWidget::tr("Close Window (%1)").arg(button(ButtonType_Close)->shortcut().toString()));
+ button(ButtonType_Help)->setToolTip(UIVMLogViewerWidget::tr("Show Help (%1)").arg(button(ButtonType_Help)->shortcut().toString()));
+}
+
+bool UIVMLogViewerDialog::event(QEvent *pEvent)
+{
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ case QEvent::Move:
+ {
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+ break;
+ }
+ case QEvent::Timer:
+ {
+ QTimerEvent *pTimerEvent = static_cast<QTimerEvent*>(pEvent);
+ if (pTimerEvent->timerId() == m_iGeometrySaveTimerId)
+ {
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = -1;
+ saveDialogGeometry();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return QIWithRetranslateUI<QIManagerDialog>::event(pEvent);
+}
+
+void UIVMLogViewerDialog::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/vm_show_logs_32px.png", ":/vm_show_logs_16px.png"));
+#endif
+}
+
+void UIVMLogViewerDialog::configureCentralWidget()
+{
+ /* Create widget: */
+ UIVMLogViewerWidget *pWidget = new UIVMLogViewerWidget(EmbedTo_Dialog, m_pActionPool, true /* show toolbar */, m_uMachineId, this);
+ if (pWidget)
+ {
+ /* Configure widget: */
+ setWidget(pWidget);
+ setWidgetMenu(pWidget->menu());
+#ifdef VBOX_WS_MAC
+ setWidgetToolbar(pWidget->toolbar());
+#endif
+ connect(pWidget, &UIVMLogViewerWidget::sigSetCloseButtonShortCut,
+ this, &UIVMLogViewerDialog::sltSetCloseButtonShortCut);
+
+ /* Add into layout: */
+ centralWidget()->layout()->addWidget(pWidget);
+ }
+}
+
+void UIVMLogViewerDialog::finalize()
+{
+ /* Apply language settings: */
+ retranslateUi();
+ manageEscapeShortCut();
+ loadDialogGeometry();
+}
+
+void UIVMLogViewerDialog::loadDialogGeometry()
+{
+
+ const QRect availableGeo = gpDesktop->availableGeometry(this);
+ int iDefaultWidth = availableGeo.width() / 2;
+ int iDefaultHeight = availableGeo.height() * 3 / 4;
+ /* Try obtain the default width of the current logviewer: */
+ const UIVMLogViewerWidget *pWidget = qobject_cast<const UIVMLogViewerWidget*>(widget());
+ if (pWidget)
+ {
+ const int iWidth = pWidget->defaultLogPageWidth();
+ if (iWidth != 0)
+ iDefaultWidth = iWidth;
+ }
+ QRect defaultGeo(0, 0, iDefaultWidth, iDefaultHeight);
+
+ /* Load geometry from extradata: */
+ const QRect geo = gEDataManager->logWindowGeometry(this, centerWidget(), defaultGeo);
+ LogRel2(("GUI: UIVMLogViewerDialog: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ restoreGeometry(geo);
+}
+
+void UIVMLogViewerDialog::saveDialogGeometry()
+{
+ /* Save geometry to extradata: */
+ const QRect geo = currentGeometry();
+ LogRel2(("GUI: UIVMLogViewerDialog: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ gEDataManager->setLogWindowGeometry(geo, isCurrentlyMaximized());
+}
+
+bool UIVMLogViewerDialog::shouldBeMaximized() const
+{
+ return gEDataManager->logWindowShouldBeMaximized();
+}
+
+void UIVMLogViewerDialog::sltSetCloseButtonShortCut(QKeySequence shortcut)
+{
+ if (!closeEmitted() && button(ButtonType_Close))
+ button(ButtonType_Close)->setShortcut(shortcut);
+}
+
+void UIVMLogViewerDialog::manageEscapeShortCut()
+{
+ UIVMLogViewerWidget *pWidget = qobject_cast<UIVMLogViewerWidget*>(widget());
+ if (!pWidget)
+ return;
+ pWidget->manageEscapeShortCut();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerDialog.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerDialog.h
new file mode 100644
index 00000000..2c21c124
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerDialog.h
@@ -0,0 +1,145 @@
+/* $Id: UIVMLogViewerDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewerDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerDialog_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QString>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+
+/* Forward declarations: */
+class QDialogButtonBox;
+class QVBoxLayout;
+class UIActionPool;
+class UIVMLogViewerDialog;
+class UIVirtualMachineItem;
+class CMachine;
+
+
+/** QIManagerDialogFactory extension used as a factory for Log Viewer dialog. */
+class SHARED_LIBRARY_STUFF UIVMLogViewerDialogFactory : public QIManagerDialogFactory
+{
+public:
+
+ /** Constructs Log Viewer factory acquiring additional arguments.
+ * @param pActionPool Brings the action-pool reference.
+ * @param uMachineId Brings the machine id for which VM Log-Viewer is requested. */
+ UIVMLogViewerDialogFactory(UIActionPool *pActionPool = 0, const QUuid &uMachineId = QUuid(),
+ const QString &strMachineName = QString());
+
+protected:
+
+ /** Creates derived @a pDialog instance.
+ * @param pCenterWidget Brings the widget to center wrt. pCenterWidget. */
+ virtual void create(QIManagerDialog *&pDialog, QWidget *pCenterWidget) RT_OVERRIDE;
+
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** Holds the machine id. */
+ QUuid m_uMachineId;
+ QString m_strMachineName;
+};
+
+
+/** QIManagerDialog extension providing GUI with the dialog displaying machine logs. */
+class SHARED_LIBRARY_STUFF UIVMLogViewerDialog : public QIWithRetranslateUI<QIManagerDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Log Viewer dialog.
+ * @param pCenterWidget Brings the widget reference to center according to.
+ * @param pActionPool Brings the action-pool reference.
+ * @param machine id Brings the machine id. */
+ UIVMLogViewerDialog(QWidget *pCenterWidget, UIActionPool *pActionPool,
+ const QUuid &uMachineId = QUuid(), const QString &strMachineName = QString());
+ ~UIVMLogViewerDialog();
+ void setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items);
+ void addSelectedVMListItems(const QList<UIVirtualMachineItem*> &items);
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ virtual void configure() RT_OVERRIDE;
+ /** Configures central-widget. */
+ virtual void configureCentralWidget() RT_OVERRIDE;
+ /** Perform final preparations. */
+ virtual void finalize() RT_OVERRIDE;
+ /** Loads dialog geometry from extradata. */
+ virtual void loadDialogGeometry();
+
+ /** Saves dialog geometry into extradata. */
+ virtual void saveDialogGeometry();
+ /** @} */
+
+ /** @name Functions related to geometry restoration.
+ * @{ */
+ /** Returns whether the window should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** Must be handles soemthing related to close @a shortcut. */
+ void sltSetCloseButtonShortCut(QKeySequence shortcut);
+
+private:
+
+ void manageEscapeShortCut();
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** Holds the machine id. */
+ QUuid m_uMachineId;
+ int m_iGeometrySaveTimerId;
+ QString m_strMachineName;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerFilterPanel.cpp b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerFilterPanel.cpp
new file mode 100644
index 00000000..0d890ea5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerFilterPanel.cpp
@@ -0,0 +1,641 @@
+/* $Id: UIVMLogViewerFilterPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QComboBox>
+#include <QFrame>
+#include <QHBoxLayout>
+#if defined(RT_OS_SOLARIS)
+# include <QFontDatabase>
+#endif
+#include <QLabel>
+#include <QLineEdit>
+#include <QPlainTextEdit>
+#include <QRegularExpression>
+#include <QTextCursor>
+#include <QRadioButton>
+#include <QScrollArea>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIVMLogPage.h"
+#include "UIVMLogViewerFilterPanel.h"
+#include "UIVMLogViewerWidget.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* UIVMFilterLineEdit definition. *
+*********************************************************************************************************************************/
+
+/** UIVMFilterLineEdit class is used to display and modify the list of filter terms.
+ * the terms are displayed as words with spaces in between and it is possible to
+ * remove these terms one by one by selecting them or completely by the clearAll button
+ * located on the right side of the line edit: */
+class UIVMFilterLineEdit : public QLineEdit
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigFilterTermRemoved(QString removedString);
+ void sigClearAll();
+
+public:
+
+ UIVMFilterLineEdit(QWidget *parent = 0);
+ void addFilterTerm(const QString& filterTermString);
+ void clearAll();
+
+protected:
+
+ /* Delete mouseDoubleClick and mouseMoveEvent implementations of the base class */
+ virtual void mouseDoubleClickEvent(QMouseEvent *) RT_OVERRIDE {}
+ virtual void mouseMoveEvent(QMouseEvent *) RT_OVERRIDE {}
+ /* Override the mousePressEvent to control how selection is done: */
+ virtual void mousePressEvent(QMouseEvent * event) RT_OVERRIDE;
+ virtual void mouseReleaseEvent(QMouseEvent *){}
+ virtual void paintEvent(QPaintEvent *event) RT_OVERRIDE;
+
+private slots:
+
+ /* Nofifies the listeners that selected word (filter term) has been removed: */
+ void sltRemoveFilterTerm();
+ /* The whole content is removed. Listeners are notified: */
+ void sltClearAll();
+
+private:
+
+ void createButtons();
+ QToolButton *m_pRemoveTermButton;
+ QToolButton *m_pClearAllButton;
+ const int m_iRemoveTermButtonSize;
+ int m_iTrailingSpaceCount;
+};
+
+
+/*********************************************************************************************************************************
+* UIVMFilterLineEdit implementation. *
+*********************************************************************************************************************************/
+
+UIVMFilterLineEdit::UIVMFilterLineEdit(QWidget *parent /*= 0*/)
+ :QLineEdit(parent)
+ , m_pRemoveTermButton(0)
+ , m_pClearAllButton(0)
+ , m_iRemoveTermButtonSize(16)
+ , m_iTrailingSpaceCount(1)
+{
+ setReadOnly(true);
+ home(false);
+ setContextMenuPolicy(Qt::NoContextMenu);
+ createButtons();
+ /** Try to guess the width of the space between filter terms so that remove button
+ we display when a term is selected does not hide the next/previous word: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int spaceWidth = fontMetrics().horizontalAdvance(' ');
+#else
+ int spaceWidth = fontMetrics().width(' ');
+#endif
+ if (spaceWidth != 0)
+ m_iTrailingSpaceCount = (m_iRemoveTermButtonSize / spaceWidth) + 1;
+}
+
+void UIVMFilterLineEdit::addFilterTerm(const QString& filterTermString)
+{
+ if (text().isEmpty())
+ insert(filterTermString);
+ else
+ {
+ QString newString(filterTermString);
+ QString space(m_iTrailingSpaceCount, QChar(' '));
+ insert(newString.prepend(space));
+ }
+}
+
+void UIVMFilterLineEdit::clearAll()
+{
+ if (text().isEmpty())
+ return;
+ sltClearAll();
+}
+
+void UIVMFilterLineEdit::mousePressEvent(QMouseEvent * event)
+{
+ /* Simulate double mouse click to select a word with a single click: */
+ QLineEdit::mouseDoubleClickEvent(event);
+}
+
+void UIVMFilterLineEdit::paintEvent(QPaintEvent *event)
+{
+ /* Call to base-class: */
+ QLineEdit::paintEvent(event);
+
+ if (!m_pClearAllButton || !m_pRemoveTermButton)
+ createButtons();
+ int clearButtonSize = height();
+
+ int deltaHeight = 0.5 * (height() - m_pClearAllButton->height());
+#ifdef VBOX_WS_MAC
+ m_pClearAllButton->setGeometry(width() - clearButtonSize - 2, deltaHeight, clearButtonSize, clearButtonSize);
+#else
+ m_pClearAllButton->setGeometry(width() - clearButtonSize - 1, deltaHeight, clearButtonSize, clearButtonSize);
+#endif
+
+ /* If we have a selected term move the m_pRemoveTermButton to the end of the
+ or start of the word (depending on the location of the word within line edit itself: */
+ if (hasSelectedText())
+ {
+ //int deltaHeight = 0.5 * (height() - m_pClearAllButton->height());
+ m_pRemoveTermButton->show();
+ int buttonSize = m_iRemoveTermButtonSize;
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int charWidth = fontMetrics().horizontalAdvance('x');
+#else
+ int charWidth = fontMetrics().width('x');
+#endif
+#ifdef VBOX_WS_MAC
+ int buttonLeft = cursorRect().left() + 1;
+#else
+ int buttonLeft = cursorRect().right() - 0.9 * charWidth;
+#endif
+ /* If buttonLeft is in far right of the line edit, move the
+ button to left side of the selected word: */
+ if (buttonLeft + buttonSize >= width() - clearButtonSize)
+ {
+ int selectionWidth = charWidth * selectedText().length();
+ buttonLeft -= (selectionWidth + buttonSize);
+ }
+ m_pRemoveTermButton->setGeometry(buttonLeft, deltaHeight, buttonSize, buttonSize);
+ }
+ else
+ m_pRemoveTermButton->hide();
+}
+
+void UIVMFilterLineEdit::sltRemoveFilterTerm()
+{
+ if (!hasSelectedText())
+ return;
+ emit sigFilterTermRemoved(selectedText());
+ /* Remove the string from text() including the trailing space: */
+ setText(text().remove(selectionStart(), selectedText().length() + m_iTrailingSpaceCount));
+}
+
+void UIVMFilterLineEdit::sltClearAll()
+{
+ /* Check if we have some text to avoid recursive calls: */
+ if (text().isEmpty())
+ return;
+
+ clear();
+ emit sigClearAll();
+}
+
+void UIVMFilterLineEdit::createButtons()
+{
+ if (!m_pRemoveTermButton)
+ {
+ m_pRemoveTermButton = new QToolButton(this);
+ if (m_pRemoveTermButton)
+ {
+ m_pRemoveTermButton->setIcon(UIIconPool::iconSet(":/log_viewer_delete_filter_16px.png"));
+ m_pRemoveTermButton->hide();
+ connect(m_pRemoveTermButton, &QIToolButton::clicked, this, &UIVMFilterLineEdit::sltRemoveFilterTerm);
+ const QSize sh = m_pRemoveTermButton->sizeHint();
+ m_pRemoveTermButton->setStyleSheet("QToolButton { border: 0px none black; margin: 0px 0px 0px 0px; } QToolButton::menu-indicator {image: none;}");
+ m_pRemoveTermButton->setFixedSize(sh);
+ }
+ }
+
+ if (!m_pClearAllButton)
+ {
+ m_pClearAllButton = new QToolButton(this);
+ if (m_pClearAllButton)
+ {
+ m_pClearAllButton->setIcon(UIIconPool::iconSet(":/log_viewer_delete_all_filters_16px.png"));
+ connect(m_pClearAllButton, &QIToolButton::clicked, this, &UIVMFilterLineEdit::sltClearAll);
+ const QSize sh = m_pClearAllButton->sizeHint();
+ m_pClearAllButton->setStyleSheet("QToolButton { border: 0px none black; margin: 0px 0px 0px 0px; } QToolButton::menu-indicator {image: none;}");
+ m_pClearAllButton->setFixedSize(sh);
+ }
+ }
+ if (m_pRemoveTermButton && m_pClearAllButton)
+ setMinimumHeight(qMax(m_pRemoveTermButton->minimumHeight(), m_pClearAllButton->minimumHeight()));
+ else if (m_pRemoveTermButton)
+ setMinimumHeight(m_pRemoveTermButton->minimumHeight());
+ else if (m_pClearAllButton)
+ setMinimumHeight(m_pClearAllButton->minimumHeight());
+}
+
+
+/*********************************************************************************************************************************
+* UIVMLogViewerFilterPanel implementation. *
+*********************************************************************************************************************************/
+
+UIVMLogViewerFilterPanel::UIVMLogViewerFilterPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer)
+ : UIVMLogViewerPanel(pParent, pViewer)
+ , m_pFilterLabel(0)
+ , m_pFilterComboBox(0)
+ , m_pButtonGroup(0)
+ , m_pAndRadioButton(0)
+ , m_pOrRadioButton(0)
+ , m_pRadioButtonContainer(0)
+ , m_pAddFilterTermButton(0)
+ , m_eFilterOperatorButton(AndButton)
+ , m_pFilterTermsLineEdit(0)
+ , m_pResultLabel(0)
+ , m_iUnfilteredLineCount(0)
+ , m_iFilteredLineCount(0)
+{
+ prepare();
+}
+
+QString UIVMLogViewerFilterPanel::panelName() const
+{
+ return "FilterPanel";
+}
+
+void UIVMLogViewerFilterPanel::applyFilter()
+{
+ if (isVisible())
+ filter();
+ else
+ resetFiltering();
+ retranslateUi();
+ emit sigFilterApplied();
+}
+
+void UIVMLogViewerFilterPanel::filter()
+{
+ if (!viewer())
+ return;
+ QPlainTextEdit *pCurrentTextEdit = textEdit();
+ if (!pCurrentTextEdit)
+ return;
+
+ UIVMLogPage *logPage = viewer()->currentLogPage();
+ if (!logPage)
+ return;
+
+ const QString* originalLogString = logString();
+ m_iUnfilteredLineCount = 0;
+ m_iFilteredLineCount = 0;
+ if (!originalLogString || originalLogString->isNull())
+ return;
+ QTextDocument *document = textDocument();
+ if (!document)
+ return;
+ QStringList stringLines = originalLogString->split("\n");
+ m_iUnfilteredLineCount = stringLines.size();
+
+ if (m_filterTermSet.empty())
+ resetFiltering();
+
+ /* Prepare filter-data: */
+ QString strFilteredText;
+ int count = 0;
+ for (int lineIdx = 0; lineIdx < stringLines.size(); ++lineIdx)
+ {
+ const QString& currentLineString = stringLines[lineIdx];
+ if (currentLineString.isEmpty())
+ continue;
+ if (applyFilterTermsToString(currentLineString))
+ {
+ strFilteredText.append(currentLineString).append("\n");
+ ++count;
+ }
+ }
+
+ document->setPlainText(strFilteredText);
+ m_iFilteredLineCount = document->lineCount();
+
+ /* Move the cursor position to end: */
+ QTextCursor cursor = pCurrentTextEdit->textCursor();
+ cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
+ pCurrentTextEdit->setTextCursor(cursor);
+ logPage->scrollToEnd();
+}
+
+void UIVMLogViewerFilterPanel::resetFiltering()
+{
+ UIVMLogPage *logPage = viewer()->currentLogPage();
+ QTextDocument *document = textDocument();
+ if (!logPage || !document)
+ return;
+
+ document->setPlainText(logPage->logString());
+ m_iFilteredLineCount = document->lineCount();
+ m_iUnfilteredLineCount = document->lineCount();
+ logPage->scrollToEnd();
+}
+
+bool UIVMLogViewerFilterPanel::applyFilterTermsToString(const QString& string)
+{
+ /* Number of the filter terms contained with the @p string: */
+ int hitCount = 0;
+
+ for (QSet<QString>::const_iterator iterator = m_filterTermSet.begin();
+ iterator != m_filterTermSet.end(); ++iterator)
+ {
+ /* Disregard empty and invalid filter terms: */
+ const QString& filterTerm = *iterator;
+ if (filterTerm.isEmpty())
+ continue;
+ const QRegularExpression rxFilterExp(filterTerm, QRegularExpression::CaseInsensitiveOption);
+ if (!rxFilterExp.isValid())
+ continue;
+
+ if (string.contains(rxFilterExp))
+ {
+ ++hitCount;
+ /* Early return */
+ if (m_eFilterOperatorButton == OrButton)
+ return true;
+ }
+
+ /* Early return */
+ if (!string.contains(rxFilterExp) && m_eFilterOperatorButton == AndButton )
+ return false;
+ }
+ /* All the terms are found within the @p string. To catch AND case: */
+ if (hitCount == m_filterTermSet.size())
+ return true;
+ return false;
+}
+
+
+void UIVMLogViewerFilterPanel::sltAddFilterTerm()
+{
+ if (!m_pFilterComboBox)
+ return;
+ if (m_pFilterComboBox->currentText().isEmpty())
+ return;
+
+ /* Continue only if the term is new. */
+ if (m_filterTermSet.contains(m_pFilterComboBox->currentText()))
+ return;
+ m_filterTermSet.insert(m_pFilterComboBox->currentText());
+
+ /* Add the new filter term to line edit: */
+ if (m_pFilterTermsLineEdit)
+ m_pFilterTermsLineEdit->addFilterTerm(m_pFilterComboBox->currentText());
+
+ /* Clear the content of the combo box: */
+ m_pFilterComboBox->setCurrentText(QString());
+ applyFilter();
+}
+
+void UIVMLogViewerFilterPanel::sltClearFilterTerms()
+{
+ if (m_filterTermSet.empty())
+ return;
+ m_filterTermSet.clear();
+ applyFilter();
+ if (m_pFilterTermsLineEdit)
+ m_pFilterTermsLineEdit->clearAll();
+}
+
+void UIVMLogViewerFilterPanel::sltOperatorButtonChanged(QAbstractButton *pButton)
+{
+ int buttonId = m_pButtonGroup->id(pButton);
+ if (buttonId < 0 || buttonId >= ButtonEnd)
+ return;
+ m_eFilterOperatorButton = static_cast<FilterOperatorButton>(buttonId);
+ applyFilter();
+}
+
+void UIVMLogViewerFilterPanel::sltRemoveFilterTerm(const QString &termString)
+{
+ m_filterTermSet.remove(termString);
+ applyFilter();
+}
+
+void UIVMLogViewerFilterPanel::prepareWidgets()
+{
+ if (!mainLayout())
+ return;
+
+ prepareRadioButtonGroup();
+
+ /* Create combo/button layout: */
+ QHBoxLayout *pComboButtonLayout = new QHBoxLayout;
+ if (pComboButtonLayout)
+ {
+ pComboButtonLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ pComboButtonLayout->setSpacing(5);
+#else
+ pComboButtonLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2);
+#endif
+
+ /* Create filter combo-box: */
+ m_pFilterComboBox = new QComboBox;
+ if (m_pFilterComboBox)
+ {
+ m_pFilterComboBox->setEditable(true);
+ QStringList strFilterPresets;
+ strFilterPresets << "" << "GUI" << "NAT" << "AHCI" << "VD"
+ << "Audio" << "VUSB" << "SUP" << "PGM" << "HDA"
+ << "HM" << "VMM" << "GIM" << "CPUM";
+ strFilterPresets.sort();
+ m_pFilterComboBox->addItems(strFilterPresets);
+ pComboButtonLayout->addWidget(m_pFilterComboBox);
+ }
+
+ /* Create add filter-term button: */
+ m_pAddFilterTermButton = new QIToolButton;
+ if (m_pAddFilterTermButton)
+ {
+ m_pAddFilterTermButton->setIcon(UIIconPool::iconSet(":/log_viewer_filter_add_16px.png"));
+ pComboButtonLayout->addWidget(m_pAddFilterTermButton);
+ }
+
+ mainLayout()->addLayout(pComboButtonLayout, 1);
+ }
+
+ /* Create filter-term line-edit: */
+ m_pFilterTermsLineEdit = new UIVMFilterLineEdit;
+ if (m_pFilterTermsLineEdit)
+ {
+ m_pFilterTermsLineEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ mainLayout()->addWidget(m_pFilterTermsLineEdit, 3);
+ }
+
+ /* Create result label: */
+ m_pResultLabel = new QLabel;
+ if (m_pResultLabel)
+ {
+ m_pResultLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ mainLayout()->addWidget(m_pResultLabel, 0);
+ }
+}
+
+void UIVMLogViewerFilterPanel::prepareRadioButtonGroup()
+{
+ /* Create radio-button container: */
+ m_pRadioButtonContainer = new QFrame;
+ if (m_pRadioButtonContainer)
+ {
+ /* Configure container: */
+ m_pRadioButtonContainer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ m_pRadioButtonContainer->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+
+ /* Create container layout: */
+ QHBoxLayout *pContainerLayout = new QHBoxLayout(m_pRadioButtonContainer);
+ if (pContainerLayout)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ pContainerLayout->setContentsMargins(5, 0, 0, 7);
+ pContainerLayout->setSpacing(5);
+#else
+ pContainerLayout->setContentsMargins(qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 2, 0,
+ qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) / 2, 0);
+ pContainerLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2);
+#endif
+
+ /* Create button-group: */
+ m_pButtonGroup = new QButtonGroup(this);
+ if (m_pButtonGroup)
+ {
+ /* Create 'Or' radio-button: */
+ m_pOrRadioButton = new QRadioButton;
+ if (m_pOrRadioButton)
+ {
+ /* Configure radio-button: */
+ m_pButtonGroup->addButton(m_pOrRadioButton, static_cast<int>(OrButton));
+ m_pOrRadioButton->setChecked(true);
+ m_pOrRadioButton->setText("Or");
+
+ /* Add into layout: */
+ pContainerLayout->addWidget(m_pOrRadioButton);
+ }
+
+ /* Create 'And' radio-button: */
+ m_pAndRadioButton = new QRadioButton;
+ if (m_pAndRadioButton)
+ {
+ /* Configure radio-button: */
+ m_pButtonGroup->addButton(m_pAndRadioButton, static_cast<int>(AndButton));
+ m_pAndRadioButton->setText("And");
+
+ /* Add into layout: */
+ pContainerLayout->addWidget(m_pAndRadioButton);
+ }
+ }
+ }
+
+ /* Add into layout: */
+ mainLayout()->addWidget(m_pRadioButtonContainer);
+ }
+
+ /* Initialize other related stuff: */
+ m_eFilterOperatorButton = OrButton;
+}
+
+void UIVMLogViewerFilterPanel::prepareConnections()
+{
+ connect(m_pAddFilterTermButton, &QIToolButton::clicked, this, &UIVMLogViewerFilterPanel::sltAddFilterTerm);
+ connect(m_pButtonGroup, static_cast<void (QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked),
+ this, &UIVMLogViewerFilterPanel::sltOperatorButtonChanged);
+ connect(m_pFilterComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIVMLogViewerFilterPanel::sltAddFilterTerm);
+ connect(m_pFilterTermsLineEdit, &UIVMFilterLineEdit::sigFilterTermRemoved,
+ this, &UIVMLogViewerFilterPanel::sltRemoveFilterTerm);
+ connect(m_pFilterTermsLineEdit, &UIVMFilterLineEdit::sigClearAll,
+ this, &UIVMLogViewerFilterPanel::sltClearFilterTerms);
+}
+
+
+void UIVMLogViewerFilterPanel::retranslateUi()
+{
+ UIVMLogViewerPanel::retranslateUi();
+
+ m_pFilterComboBox->setToolTip(UIVMLogViewerWidget::tr("Select or enter a term which will be used in filtering the log text"));
+ m_pAddFilterTermButton->setToolTip(UIVMLogViewerWidget::tr("Add the filter term to the set of filter terms"));
+ m_pResultLabel->setText(UIVMLogViewerWidget::tr("Showing %1/%2").arg(m_iFilteredLineCount).arg(m_iUnfilteredLineCount));
+ m_pFilterTermsLineEdit->setToolTip(UIVMLogViewerWidget::tr("The filter terms list, select one to remove or click "
+ "the button on the right side to remove them all"));
+ m_pRadioButtonContainer->setToolTip(UIVMLogViewerWidget::tr("The type of boolean operator for filter operation"));
+}
+
+bool UIVMLogViewerFilterPanel::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Handle only events sent to viewer(): */
+ if (pObject != viewer())
+ return UIVMLogViewerPanel::eventFilter(pObject, pEvent);
+
+ /* Depending on event-type: */
+ switch (pEvent->type())
+ {
+ /* Process key press only: */
+ case QEvent::KeyPress:
+ {
+ /* Cast to corresponding key press event: */
+ QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
+
+ /* Handle Ctrl+T key combination as a shortcut to focus search field: */
+ if (pKeyEvent->QInputEvent::modifiers() == Qt::ControlModifier &&
+ pKeyEvent->key() == Qt::Key_T)
+ {
+ if (isHidden())
+ show();
+ m_pFilterComboBox->setFocus();
+ return true;
+ }
+ else if (pKeyEvent->key() == Qt::Key_Return && m_pFilterComboBox && m_pFilterComboBox->hasFocus())
+ sltAddFilterTerm();
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return UIVMLogViewerPanel::eventFilter(pObject, pEvent);
+}
+
+/** Handles the Qt show @a pEvent. */
+void UIVMLogViewerFilterPanel::showEvent(QShowEvent *pEvent)
+{
+ UIVMLogViewerPanel::showEvent(pEvent);
+ /* Set focus to combo-box: */
+ m_pFilterComboBox->setFocus();
+ applyFilter();
+}
+
+void UIVMLogViewerFilterPanel::hideEvent(QHideEvent *pEvent)
+{
+ UIVMLogViewerPanel::hideEvent(pEvent);
+ applyFilter();
+}
+
+#include "UIVMLogViewerFilterPanel.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerFilterPanel.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerFilterPanel.h
new file mode 100644
index 00000000..b097cd0d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerFilterPanel.h
@@ -0,0 +1,125 @@
+/* $Id: UIVMLogViewerFilterPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerFilterPanel_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerFilterPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+# include <QSet>
+
+/* GUI includes: */
+#include "UIVMLogViewerPanel.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QButtonGroup;
+class QComboBox;
+class QFrame;
+class QLabel;
+class QLineEdit;
+class QIToolButton;
+class QRadioButton;
+class UIVMFilterLineEdit;
+
+
+/** QWidget extension
+ * providing GUI for filter panel in VM Log Viewer. */
+class UIVMLogViewerFilterPanel : public UIVMLogViewerPanel
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigFilterApplied();
+
+public:
+
+ /** Constructs the filter-panel by passing @a pParent to the QWidget base-class constructor.
+ * @param pViewer Specifies reference to the VM Log-Viewer this filter-panel belongs to. */
+ UIVMLogViewerFilterPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer);
+ virtual QString panelName() const RT_OVERRIDE;
+
+public slots:
+
+ /** Applies filter settings and filters the current log-page. */
+ void applyFilter();
+
+protected:
+
+ virtual void prepareWidgets() RT_OVERRIDE;
+ virtual void prepareConnections() RT_OVERRIDE;
+
+ void retranslateUi() RT_OVERRIDE;
+ /** Handles Qt @a pEvent, used for keyboard processing. */
+ bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+ void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ void hideEvent(QHideEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Adds the new filter term and reapplies the filter. */
+ void sltAddFilterTerm();
+ /** Clear all the filter terms and reset the filtering. */
+ void sltClearFilterTerms();
+ /** Executes the necessary code to handle filter's boolean operator change ('And', 'Or'). */
+ void sltOperatorButtonChanged(QAbstractButton *pButton);
+ void sltRemoveFilterTerm(const QString &termString);
+
+private:
+
+ enum FilterOperatorButton{
+ AndButton = 0,/* Don't change this value */
+ OrButton,
+ ButtonEnd
+ };
+
+ void prepareRadioButtonGroup();
+
+ bool applyFilterTermsToString(const QString& string);
+ void filter();
+ /** Revert the document to original. */
+ void resetFiltering();
+
+ QLabel *m_pFilterLabel;
+ QComboBox *m_pFilterComboBox;
+ QButtonGroup *m_pButtonGroup;
+ QRadioButton *m_pAndRadioButton;
+ QRadioButton *m_pOrRadioButton;
+ QFrame *m_pRadioButtonContainer;
+ QIToolButton *m_pAddFilterTermButton;
+ QSet<QString> m_filterTermSet;
+ FilterOperatorButton m_eFilterOperatorButton;
+ UIVMFilterLineEdit *m_pFilterTermsLineEdit;
+ QLabel *m_pResultLabel;
+ int m_iUnfilteredLineCount;
+ int m_iFilteredLineCount;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerFilterPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerOptionsPanel.cpp b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerOptionsPanel.cpp
new file mode 100644
index 00000000..316f01a7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerOptionsPanel.cpp
@@ -0,0 +1,204 @@
+/* $Id: UIVMLogViewerOptionsPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QHBoxLayout>
+#include <QFontDatabase>
+#include <QFontDialog>
+#include <QCheckBox>
+#include <QLabel>
+#include <QSpinBox>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIVMLogViewerOptionsPanel.h"
+#include "UIVMLogViewerWidget.h"
+
+
+UIVMLogViewerOptionsPanel::UIVMLogViewerOptionsPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer)
+ : UIVMLogViewerPanel(pParent, pViewer)
+ , m_pLineNumberCheckBox(0)
+ , m_pWrapLinesCheckBox(0)
+ , m_pFontSizeSpinBox(0)
+ , m_pFontSizeLabel(0)
+ , m_pOpenFontDialogButton(0)
+ , m_pResetToDefaultsButton(0)
+ , m_iDefaultFontSize(9)
+{
+ prepare();
+}
+
+void UIVMLogViewerOptionsPanel::setShowLineNumbers(bool bShowLineNumbers)
+{
+ if (!m_pLineNumberCheckBox)
+ return;
+ if (m_pLineNumberCheckBox->isChecked() == bShowLineNumbers)
+ return;
+ m_pLineNumberCheckBox->setChecked(bShowLineNumbers);
+}
+
+void UIVMLogViewerOptionsPanel::setWrapLines(bool bWrapLines)
+{
+ if (!m_pWrapLinesCheckBox)
+ return;
+ if (m_pWrapLinesCheckBox->isChecked() == bWrapLines)
+ return;
+ m_pWrapLinesCheckBox->setChecked(bWrapLines);
+}
+
+void UIVMLogViewerOptionsPanel::setFontSizeInPoints(int fontSizeInPoints)
+{
+ if (!m_pFontSizeSpinBox)
+ return;
+ if (m_pFontSizeSpinBox->value() == fontSizeInPoints)
+ return;
+ m_pFontSizeSpinBox->setValue(fontSizeInPoints);
+}
+
+QString UIVMLogViewerOptionsPanel::panelName() const
+{
+ return "OptionsPanel";
+}
+
+void UIVMLogViewerOptionsPanel::prepareWidgets()
+{
+ if (!mainLayout())
+ return;
+
+ /* Create line-number check-box: */
+ m_pLineNumberCheckBox = new QCheckBox;
+ if (m_pLineNumberCheckBox)
+ {
+ m_pLineNumberCheckBox->setChecked(true);
+ mainLayout()->addWidget(m_pLineNumberCheckBox, 0, Qt::AlignLeft);
+ }
+
+ /* Create wrap-lines check-box: */
+ m_pWrapLinesCheckBox = new QCheckBox;
+ if (m_pWrapLinesCheckBox)
+ {
+ m_pWrapLinesCheckBox->setChecked(false);
+ mainLayout()->addWidget(m_pWrapLinesCheckBox, 0, Qt::AlignLeft);
+ }
+
+ /* Create font-size spin-box: */
+ m_pFontSizeSpinBox = new QSpinBox;
+ if (m_pFontSizeSpinBox)
+ {
+ mainLayout()->addWidget(m_pFontSizeSpinBox, 0, Qt::AlignLeft);
+ m_pFontSizeSpinBox->setValue(m_iDefaultFontSize);
+ m_pFontSizeSpinBox->setMaximum(44);
+ m_pFontSizeSpinBox->setMinimum(6);
+ }
+
+ /* Create font-size label: */
+ m_pFontSizeLabel = new QLabel;
+ if (m_pFontSizeLabel)
+ {
+ mainLayout()->addWidget(m_pFontSizeLabel, 0, Qt::AlignLeft);
+ if (m_pFontSizeSpinBox)
+ m_pFontSizeLabel->setBuddy(m_pFontSizeSpinBox);
+ }
+
+ /* Create combo/button layout: */
+ QHBoxLayout *pButtonLayout = new QHBoxLayout;
+ if (pButtonLayout)
+ {
+ pButtonLayout->setContentsMargins(0, 0, 0, 0);
+ pButtonLayout->setSpacing(0);
+
+ /* Create open font dialog button: */
+ m_pOpenFontDialogButton = new QIToolButton;
+ if (m_pOpenFontDialogButton)
+ {
+ pButtonLayout->addWidget(m_pOpenFontDialogButton, 0);
+ m_pOpenFontDialogButton->setIcon(UIIconPool::iconSet(":/log_viewer_choose_font_16px.png"));
+ }
+
+ /* Create reset font to default button: */
+ m_pResetToDefaultsButton = new QIToolButton;
+ if (m_pResetToDefaultsButton)
+ {
+ pButtonLayout->addWidget(m_pResetToDefaultsButton, 0);
+ m_pResetToDefaultsButton->setIcon(UIIconPool::iconSet(":/log_viewer_reset_font_16px.png"));
+ }
+
+ mainLayout()->addLayout(pButtonLayout);
+ }
+
+ mainLayout()->addStretch(2);
+}
+
+void UIVMLogViewerOptionsPanel::prepareConnections()
+{
+ if (m_pLineNumberCheckBox)
+ connect(m_pLineNumberCheckBox, &QCheckBox::toggled, this, &UIVMLogViewerOptionsPanel::sigShowLineNumbers);
+ if (m_pWrapLinesCheckBox)
+ connect(m_pWrapLinesCheckBox, &QCheckBox::toggled, this, &UIVMLogViewerOptionsPanel::sigWrapLines);
+ if (m_pFontSizeSpinBox)
+ connect(m_pFontSizeSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIVMLogViewerOptionsPanel::sigChangeFontSizeInPoints);
+ if (m_pOpenFontDialogButton)
+ connect(m_pOpenFontDialogButton, &QIToolButton::clicked, this, &UIVMLogViewerOptionsPanel::sltOpenFontDialog);
+ if (m_pResetToDefaultsButton)
+ connect(m_pResetToDefaultsButton, &QIToolButton::clicked, this, &UIVMLogViewerOptionsPanel::sigResetToDefaults);
+}
+
+void UIVMLogViewerOptionsPanel::retranslateUi()
+{
+ UIVMLogViewerPanel::retranslateUi();
+
+ m_pLineNumberCheckBox->setText(UIVMLogViewerWidget::tr("Show Line Numbers"));
+ m_pLineNumberCheckBox->setToolTip(UIVMLogViewerWidget::tr("When checked, show line numbers"));
+
+ m_pWrapLinesCheckBox->setText(UIVMLogViewerWidget::tr("Wrap Lines"));
+ m_pWrapLinesCheckBox->setToolTip(UIVMLogViewerWidget::tr("When checked, wrap lines"));
+
+ m_pFontSizeLabel->setText(UIVMLogViewerWidget::tr("Font Size"));
+ m_pFontSizeSpinBox->setToolTip(UIVMLogViewerWidget::tr("Log viewer font size"));
+
+ m_pOpenFontDialogButton->setToolTip(UIVMLogViewerWidget::tr("Open a font dialog to select font face for the logviewer"));
+ m_pResetToDefaultsButton->setToolTip(UIVMLogViewerWidget::tr("Reset options to application defaults"));
+}
+
+void UIVMLogViewerOptionsPanel::sltOpenFontDialog()
+{
+ QFont currentFont;
+ UIVMLogViewerWidget* parentWidget = qobject_cast<UIVMLogViewerWidget*>(parent());
+ if (!parentWidget)
+ return;
+
+ currentFont = parentWidget->currentFont();
+ bool ok;
+ QFont font =
+ QFontDialog::getFont(&ok, currentFont, this, "Logviewer font");
+
+ if (ok)
+ emit sigChangeFont(font);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerOptionsPanel.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerOptionsPanel.h
new file mode 100644
index 00000000..8f7a4789
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerOptionsPanel.h
@@ -0,0 +1,95 @@
+/* $Id: UIVMLogViewerOptionsPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerOptionsPanel_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerOptionsPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIVMLogViewerPanel.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QSpinBox;
+class QLabel;
+class QIToolButton;
+class UIVMLogViewerWidget;
+
+/** UIVMLogViewerPanel extension providing GUI to manage logviewer options. */
+class UIVMLogViewerOptionsPanel : public UIVMLogViewerPanel
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigShowLineNumbers(bool show);
+ void sigWrapLines(bool show);
+ void sigChangeFontSizeInPoints(int size);
+ void sigChangeFont(QFont font);
+ void sigResetToDefaults();
+
+public:
+
+ UIVMLogViewerOptionsPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer);
+
+ void setShowLineNumbers(bool bShowLineNumbers);
+ void setWrapLines(bool bWrapLines);
+ void setFontSizeInPoints(int fontSizeInPoints);
+ virtual QString panelName() const RT_OVERRIDE;
+
+public slots:
+
+
+protected:
+
+ virtual void prepareWidgets() RT_OVERRIDE;
+ virtual void prepareConnections() RT_OVERRIDE;
+
+ /** Handles the translation event. */
+ void retranslateUi();
+
+private slots:
+
+ void sltOpenFontDialog();
+
+private:
+
+ QCheckBox *m_pLineNumberCheckBox;
+ QCheckBox *m_pWrapLinesCheckBox;
+ QSpinBox *m_pFontSizeSpinBox;
+ QLabel *m_pFontSizeLabel;
+ QIToolButton *m_pOpenFontDialogButton;
+ QIToolButton *m_pResetToDefaultsButton;
+
+ /** Default font size in points. */
+ const int m_iDefaultFontSize;
+
+};
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerOptionsPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerPanel.cpp b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerPanel.cpp
new file mode 100644
index 00000000..d613e1fe
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerPanel.cpp
@@ -0,0 +1,93 @@
+/* $Id: UIVMLogViewerPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPlainTextEdit>
+#include <QTextCursor>
+#include <QToolButton>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIVMLogPage.h"
+#include "UIVMLogViewerPanel.h"
+#include "UIVMLogViewerWidget.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+
+UIVMLogViewerPanel::UIVMLogViewerPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer)
+ : UIDialogPanel(pParent)
+ , m_pViewer(pViewer)
+{
+}
+
+void UIVMLogViewerPanel::retranslateUi()
+{
+}
+
+UIVMLogViewerWidget* UIVMLogViewerPanel::viewer()
+{
+ return m_pViewer;
+}
+
+const UIVMLogViewerWidget* UIVMLogViewerPanel::viewer() const
+{
+ return m_pViewer;
+}
+
+QTextDocument *UIVMLogViewerPanel::textDocument()
+{
+ QPlainTextEdit *pEdit = textEdit();
+ if (!pEdit)
+ return 0;
+ return textEdit()->document();
+}
+
+QPlainTextEdit *UIVMLogViewerPanel::textEdit()
+{
+ if (!viewer())
+ return 0;
+ UIVMLogPage *logPage = viewer()->currentLogPage();
+ if (!logPage)
+ return 0;
+ return logPage->textEdit();
+}
+
+const QString* UIVMLogViewerPanel::logString() const
+{
+ if (!viewer())
+ return 0;
+ const UIVMLogPage* const page = qobject_cast<const UIVMLogPage* const>(viewer()->currentLogPage());
+ if (!page)
+ return 0;
+ return &(page->logString());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerPanel.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerPanel.h
new file mode 100644
index 00000000..0319a5d1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerPanel.h
@@ -0,0 +1,73 @@
+/* $Id: UIVMLogViewerPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerPanel_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QKeySequence>
+
+/* GUI includes: */
+#include "UIDialogPanel.h"
+
+/* Forward declarations: */
+class QPlainTextEdit;
+class QTextDocument;
+class UIVMLogViewerWidget;
+
+
+/** UIDialonPanel extension acting as the base class for UIVMLogViewerXXXPanel widgets. */
+class UIVMLogViewerPanel : public UIDialogPanel
+{
+ Q_OBJECT;
+
+public:
+
+ UIVMLogViewerPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer);
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /* Access functions for children classes. */
+ UIVMLogViewerWidget *viewer();
+ const UIVMLogViewerWidget *viewer() const;
+ QTextDocument *textDocument();
+ QPlainTextEdit *textEdit();
+ /* Return the unmodified log. */
+ const QString *logString() const;
+
+private:
+
+ /** Holds the reference to VM Log-Viewer this panel belongs to. */
+ UIVMLogViewerWidget *m_pViewer;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerSearchPanel.cpp b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerSearchPanel.cpp
new file mode 100644
index 00000000..0954fbbe
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerSearchPanel.cpp
@@ -0,0 +1,550 @@
+/* $Id: UIVMLogViewerSearchPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QCheckBox>
+#include <QComboBox>
+#if defined(RT_OS_SOLARIS)
+# include <QFontDatabase>
+#endif
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPlainTextEdit>
+#include <QScrollBar>
+#include <QTextBlock>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UISearchLineEdit.h"
+#include "UIVMLogPage.h"
+#include "UIVMLogViewerSearchPanel.h"
+#include "UIVMLogViewerWidget.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+
+UIVMLogViewerSearchPanel::UIVMLogViewerSearchPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer)
+ : UIVMLogViewerPanel(pParent, pViewer)
+ , m_pSearchEditor(0)
+ , m_pNextButton(0)
+ , m_pPreviousButton(0)
+ , m_pCaseSensitiveCheckBox(0)
+ , m_pMatchWholeWordCheckBox(0)
+ , m_pHighlightAllCheckBox(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIVMLogViewerSearchPanel::refresh()
+{
+ /* We start the search from the end of the doc. assuming log's end is more interesting: */
+ if (isVisible())
+ performSearch(BackwardSearch, true);
+ else
+ reset();
+
+ emit sigHighlightingUpdated();
+}
+
+void UIVMLogViewerSearchPanel::reset()
+{
+ m_iSelectedMatchIndex = 0;
+ m_matchLocationVector.clear();
+ m_matchedCursorPosition.clear();
+ if (m_pSearchEditor)
+ m_pSearchEditor->reset();
+ emit sigHighlightingUpdated();
+}
+
+const QVector<float> &UIVMLogViewerSearchPanel::matchLocationVector() const
+{
+ return m_matchLocationVector;
+}
+
+QString UIVMLogViewerSearchPanel::panelName() const
+{
+ return "SearchPanel";
+}
+
+int UIVMLogViewerSearchPanel::matchCount() const
+{
+ return m_matchedCursorPosition.size();
+}
+
+void UIVMLogViewerSearchPanel::hideEvent(QHideEvent *pEvent)
+{
+ /* Get focus-widget: */
+ QWidget *pFocus = QApplication::focusWidget();
+ /* If focus-widget is valid and child-widget of search-panel,
+ * focus next child-widget in line: */
+ if (pFocus && pFocus->parent() == this)
+ focusNextPrevChild(true);
+ /* Call to base-class: */
+ UIVMLogViewerPanel::hideEvent(pEvent);
+ reset();
+}
+
+void UIVMLogViewerSearchPanel::sltSearchTextChanged(const QString &strSearchString)
+{
+ /* Enable/disable Next-Previous buttons as per search-string validity: */
+ m_pNextButton->setEnabled(!strSearchString.isEmpty());
+ m_pPreviousButton->setEnabled(!strSearchString.isEmpty());
+
+ /* If search-string is not empty: */
+ if (!strSearchString.isEmpty())
+ {
+ /* Reset the position to force the search restart from the document's end: */
+ performSearch(BackwardSearch, true);
+ emit sigHighlightingUpdated();
+ return;
+ }
+
+ /* If search-string is empty, reset cursor position: */
+ if (!viewer())
+ return;
+
+ QPlainTextEdit *pBrowser = textEdit();
+ if (!pBrowser)
+ return;
+ /* If cursor has selection: */
+ if (pBrowser->textCursor().hasSelection())
+ {
+ /* Get cursor and reset position: */
+ QTextCursor cursor = pBrowser->textCursor();
+ cursor.setPosition(cursor.anchor());
+ pBrowser->setTextCursor(cursor);
+ }
+ m_matchedCursorPosition.clear();
+ m_matchLocationVector.clear();
+ clearHighlighting();
+ emit sigSearchUpdated();
+}
+
+void UIVMLogViewerSearchPanel::sltHighlightAllCheckBox()
+{
+ if (!viewer())
+ return;
+
+ QTextDocument *pDocument = textDocument();
+ if (!pDocument)
+ return;
+
+ if (m_pHighlightAllCheckBox->isChecked())
+ {
+ const QString &searchString = m_pSearchEditor->text();
+ if (searchString.isEmpty())
+ return;
+ highlightAll(searchString);
+ }
+ else
+ clearHighlighting();
+
+ emit sigHighlightingUpdated();
+}
+
+void UIVMLogViewerSearchPanel::sltCaseSentitiveCheckBox()
+{
+ refresh();
+}
+
+void UIVMLogViewerSearchPanel::sltMatchWholeWordCheckBox()
+{
+ refresh();
+}
+
+void UIVMLogViewerSearchPanel::sltSelectNextPreviousMatch()
+{
+ moveSelection(sender() == m_pNextButton);
+}
+
+void UIVMLogViewerSearchPanel::prepareWidgets()
+{
+ if (!mainLayout())
+ return;
+
+ /* Create search field layout: */
+ QHBoxLayout *pSearchFieldLayout = new QHBoxLayout;
+ if (pSearchFieldLayout)
+ {
+ pSearchFieldLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ pSearchFieldLayout->setSpacing(5);
+#else
+ pSearchFieldLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2);
+#endif
+
+ /* Create search-editor: */
+ m_pSearchEditor = new UISearchLineEdit(0 /* parent */);
+ if (m_pSearchEditor)
+ {
+ m_pSearchEditor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ pSearchFieldLayout->addWidget(m_pSearchEditor);
+ }
+
+ /* Create search button layout: */
+ QHBoxLayout *pSearchButtonsLayout = new QHBoxLayout;
+ if (pSearchButtonsLayout)
+ {
+ pSearchButtonsLayout->setContentsMargins(0, 0, 0, 0);
+ pSearchButtonsLayout->setSpacing(0);
+
+ /* Create Previous button: */
+ m_pPreviousButton = new QIToolButton;
+ if (m_pPreviousButton)
+ {
+ m_pPreviousButton->setIcon(UIIconPool::iconSet(":/log_viewer_search_backward_16px.png"));
+ pSearchButtonsLayout->addWidget(m_pPreviousButton);
+ }
+
+ /* Create Next button: */
+ m_pNextButton = new QIToolButton;
+ if (m_pNextButton)
+ {
+ m_pNextButton->setIcon(UIIconPool::iconSet(":/log_viewer_search_forward_16px.png"));
+ pSearchButtonsLayout->addWidget(m_pNextButton);
+ }
+
+ pSearchFieldLayout->addLayout(pSearchButtonsLayout);
+ }
+
+ mainLayout()->addLayout(pSearchFieldLayout);
+ }
+
+ /* Create case-sensitive check-box: */
+ m_pCaseSensitiveCheckBox = new QCheckBox;
+ if (m_pCaseSensitiveCheckBox)
+ {
+ mainLayout()->addWidget(m_pCaseSensitiveCheckBox);
+ }
+
+ /* Create whole-word check-box: */
+ m_pMatchWholeWordCheckBox = new QCheckBox;
+ if (m_pMatchWholeWordCheckBox)
+ {
+ setFocusProxy(m_pMatchWholeWordCheckBox);
+ mainLayout()->addWidget(m_pMatchWholeWordCheckBox);
+ }
+
+ /* Create highlight-all check-box: */
+ m_pHighlightAllCheckBox = new QCheckBox;
+ if (m_pHighlightAllCheckBox)
+ {
+ mainLayout()->addWidget(m_pHighlightAllCheckBox);
+ }
+}
+
+void UIVMLogViewerSearchPanel::prepareConnections()
+{
+ connect(m_pSearchEditor, &UISearchLineEdit::textChanged, this, &UIVMLogViewerSearchPanel::sltSearchTextChanged);
+ connect(m_pNextButton, &QIToolButton::clicked, this, &UIVMLogViewerSearchPanel::sltSelectNextPreviousMatch);
+ connect(m_pPreviousButton, &QIToolButton::clicked, this, &UIVMLogViewerSearchPanel::sltSelectNextPreviousMatch);
+
+ connect(m_pHighlightAllCheckBox, &QCheckBox::stateChanged,
+ this, &UIVMLogViewerSearchPanel::sltHighlightAllCheckBox);
+ connect(m_pCaseSensitiveCheckBox, &QCheckBox::stateChanged,
+ this, &UIVMLogViewerSearchPanel::sltCaseSentitiveCheckBox);
+ connect(m_pMatchWholeWordCheckBox, &QCheckBox::stateChanged,
+ this, &UIVMLogViewerSearchPanel::sltMatchWholeWordCheckBox);
+}
+
+void UIVMLogViewerSearchPanel::retranslateUi()
+{
+ UIVMLogViewerPanel::retranslateUi();
+
+ m_pSearchEditor->setToolTip(UIVMLogViewerWidget::tr("Enter a search string here"));
+ m_pNextButton->setToolTip(UIVMLogViewerWidget::tr("Search for the next occurrence of the string (F3)"));
+ m_pPreviousButton->setToolTip(UIVMLogViewerWidget::tr("Search for the previous occurrence of the string (Shift+F3)"));
+
+ m_pCaseSensitiveCheckBox->setText(UIVMLogViewerWidget::tr("C&ase Sensitive"));
+ m_pCaseSensitiveCheckBox->setToolTip(UIVMLogViewerWidget::tr("When checked, perform case sensitive search"));
+
+ m_pMatchWholeWordCheckBox->setText(UIVMLogViewerWidget::tr("Ma&tch Whole Word"));
+ m_pMatchWholeWordCheckBox->setToolTip(UIVMLogViewerWidget::tr("When checked, search matches only complete words"));
+
+ m_pHighlightAllCheckBox->setText(UIVMLogViewerWidget::tr("&Highlight All"));
+ m_pHighlightAllCheckBox->setToolTip(UIVMLogViewerWidget::tr("When checked, all occurence of the search text are highlighted"));
+}
+
+void UIVMLogViewerSearchPanel::keyPressEvent(QKeyEvent *pEvent)
+{
+ switch (pEvent->key())
+ {
+ /* Process Enter press as 'search-next',
+ * performed for any search panel widget: */
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ {
+ if (pEvent->modifiers() == 0 ||
+ pEvent->modifiers() & Qt::KeypadModifier)
+ {
+ /* Animate click on 'Next' button: */
+ m_pNextButton->animateClick();
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ /* Call to base-class: */
+ UIVMLogViewerPanel::keyPressEvent(pEvent);
+}
+
+bool UIVMLogViewerSearchPanel::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Handle only events sent to viewer(): */
+ if (pObject != viewer())
+ return UIVMLogViewerPanel::eventFilter(pObject, pEvent);
+
+ /* Depending on event-type: */
+ switch (pEvent->type())
+ {
+ /* Process key press only: */
+ case QEvent::KeyPress:
+ {
+ /* Cast to corresponding key press event: */
+ QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
+
+ /* Handle F3/Shift+F3 as search next/previous shortcuts: */
+ if (pKeyEvent->key() == Qt::Key_F3)
+ {
+ /* If there is no modifier 'Key-F3' is pressed: */
+ if (pKeyEvent->QInputEvent::modifiers() == 0)
+ {
+ /* Animate click on 'Next' button: */
+ m_pNextButton->animateClick();
+ return true;
+ }
+ /* If there is 'ShiftModifier' 'Shift + Key-F3' is pressed: */
+ else if (pKeyEvent->QInputEvent::modifiers() == Qt::ShiftModifier)
+ {
+ /* Animate click on 'Prev' button: */
+ m_pPreviousButton->animateClick();
+ return true;
+ }
+ }
+ /* Handle Ctrl+F key combination as a shortcut to focus search field: */
+ else if (pKeyEvent->QInputEvent::modifiers() == Qt::ControlModifier &&
+ pKeyEvent->key() == Qt::Key_F)
+ {
+ /* Make sure current log-page is visible: */
+ emit sigShowPanel(this);
+ /* Set focus on search-editor: */
+ m_pSearchEditor->setFocus();
+ return true;
+ }
+ /* Handle alpha-numeric keys to implement the "find as you type" feature: */
+ else if ((pKeyEvent->QInputEvent::modifiers() & ~Qt::ShiftModifier) == 0 &&
+ pKeyEvent->key() >= Qt::Key_Exclam && pKeyEvent->key() <= Qt::Key_AsciiTilde)
+ {
+ /* Make sure current log-page is visible: */
+ emit sigShowPanel(this);
+ /* Set focus on search-editor: */
+ m_pSearchEditor->setFocus();
+ /* Insert the text to search-editor, which triggers the search-operation for new text: */
+ m_pSearchEditor->insert(pKeyEvent->text());
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return UIVMLogViewerPanel::eventFilter(pObject, pEvent);
+}
+
+void UIVMLogViewerSearchPanel::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIVMLogViewerPanel::showEvent(pEvent);
+ if (m_pSearchEditor)
+ {
+ /* Set focus on search-editor: */
+ m_pSearchEditor->setFocus();
+ /* Select all the text: */
+ m_pSearchEditor->selectAll();
+ m_pSearchEditor->setMatchCount(m_matchedCursorPosition.size());
+ }
+}
+
+void UIVMLogViewerSearchPanel::performSearch(SearchDirection , bool )
+{
+ QPlainTextEdit *pTextEdit = textEdit();
+ if (!pTextEdit)
+ return;
+ QTextDocument *pDocument = textDocument();
+ if (!pDocument)
+ return;
+ if (!m_pSearchEditor)
+ return;
+
+ const QString &searchString = m_pSearchEditor->text();
+ emit sigSearchUpdated();
+
+ if (searchString.isEmpty())
+ return;
+
+ findAll(pDocument, searchString);
+ m_iSelectedMatchIndex = 0;
+ selectMatch(m_iSelectedMatchIndex, searchString);
+ if (m_pSearchEditor)
+ {
+ m_pSearchEditor->setMatchCount(m_matchedCursorPosition.size());
+ m_pSearchEditor->setScrollToIndex(m_matchedCursorPosition.empty() ? -1 : 0);
+ }
+ if (m_pHighlightAllCheckBox->isChecked())
+ highlightAll(searchString);
+}
+
+void UIVMLogViewerSearchPanel::clearHighlighting()
+{
+ QPlainTextEdit *pTextEdit = textEdit();
+ if (pTextEdit)
+ pTextEdit->setExtraSelections(QList<QTextEdit::ExtraSelection>());
+ emit sigHighlightingUpdated();
+}
+
+void UIVMLogViewerSearchPanel::highlightAll(const QString &searchString)
+{
+ clearHighlighting();
+ QPlainTextEdit *pTextEdit = textEdit();
+
+ if (!pTextEdit)
+ return;
+
+ QList<QTextEdit::ExtraSelection> extraSelections;
+ for (int i = 0; i < m_matchedCursorPosition.size(); ++i)
+ {
+ QTextEdit::ExtraSelection selection;
+ QTextCursor cursor = pTextEdit->textCursor();
+ cursor.setPosition(m_matchedCursorPosition[i]);
+ cursor.setPosition(m_matchedCursorPosition[i] + searchString.length(), QTextCursor::KeepAnchor);
+ QTextCharFormat format = cursor.charFormat();
+ format.setBackground(Qt::yellow);
+
+ selection.cursor = cursor;
+ selection.format = format;
+ extraSelections.append(selection);
+ }
+ pTextEdit->setExtraSelections(extraSelections);
+
+}
+
+void UIVMLogViewerSearchPanel::findAll(QTextDocument *pDocument, const QString &searchString)
+{
+ if (!pDocument)
+ return;
+ m_matchedCursorPosition.clear();
+ m_matchLocationVector.clear();
+ if (searchString.isEmpty())
+ return;
+ QTextCursor cursor(pDocument);
+ QTextDocument::FindFlags flags = constructFindFlags(ForwardSearch);
+ int blockCount = pDocument->blockCount();
+ while (!cursor.isNull() && !cursor.atEnd())
+ {
+ cursor = pDocument->find(searchString, cursor, flags);
+
+ if (!cursor.isNull())
+ {
+ m_matchedCursorPosition << cursor.position() - searchString.length();
+ /* The following assumes we have single line blocks only: */
+ int cursorLine = pDocument->findBlock(cursor.position()).blockNumber();
+ if (blockCount != 0)
+ m_matchLocationVector.push_back(cursorLine / static_cast<float>(blockCount));
+ }
+ }
+}
+
+void UIVMLogViewerSearchPanel::selectMatch(int iMatchIndex, const QString &searchString)
+{
+ if (!textEdit())
+ return;
+ if (searchString.isEmpty())
+ return;
+ if (iMatchIndex < 0 || iMatchIndex >= m_matchedCursorPosition.size())
+ return;
+
+ QTextCursor cursor = textEdit()->textCursor();
+ /* Move the cursor to the beginning of the matched string: */
+ cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex), QTextCursor::MoveAnchor);
+ /* Move the cursor to the end of the matched string while keeping the anchor at the begining thus selecting the text: */
+ cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex) + searchString.length(), QTextCursor::KeepAnchor);
+ textEdit()->ensureCursorVisible();
+ textEdit()->setTextCursor(cursor);
+}
+
+void UIVMLogViewerSearchPanel::moveSelection(bool fForward)
+{
+ if (matchCount() == 0)
+ return;
+ if (fForward)
+ m_iSelectedMatchIndex = m_iSelectedMatchIndex >= m_matchedCursorPosition.size() - 1 ? 0 : (m_iSelectedMatchIndex + 1);
+ else
+ m_iSelectedMatchIndex = m_iSelectedMatchIndex <= 0 ? m_matchedCursorPosition.size() - 1 : (m_iSelectedMatchIndex - 1);
+ selectMatch(m_iSelectedMatchIndex, m_pSearchEditor->text());
+ if (m_pSearchEditor)
+ m_pSearchEditor->setScrollToIndex(m_iSelectedMatchIndex);
+}
+
+int UIVMLogViewerSearchPanel::countMatches(QTextDocument *pDocument, const QString &searchString) const
+{
+ if (!pDocument)
+ return 0;
+ if (searchString.isEmpty())
+ return 0;
+ int count = 0;
+ QTextCursor cursor(pDocument);
+ QTextDocument::FindFlags flags = constructFindFlags(ForwardSearch);
+ while (!cursor.isNull() && !cursor.atEnd())
+ {
+ cursor = pDocument->find(searchString, cursor, flags);
+
+ if (!cursor.isNull())
+ ++count;
+ }
+ return count;
+}
+
+QTextDocument::FindFlags UIVMLogViewerSearchPanel::constructFindFlags(SearchDirection eDirection) const
+{
+ QTextDocument::FindFlags findFlags;
+ if (eDirection == BackwardSearch)
+ findFlags = findFlags | QTextDocument::FindBackward;
+ if (m_pCaseSensitiveCheckBox->isChecked())
+ findFlags = findFlags | QTextDocument::FindCaseSensitively;
+ if (m_pMatchWholeWordCheckBox->isChecked())
+ findFlags = findFlags | QTextDocument::FindWholeWords;
+ return findFlags;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerSearchPanel.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerSearchPanel.h
new file mode 100644
index 00000000..e3207077
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerSearchPanel.h
@@ -0,0 +1,136 @@
+/* $Id: UIVMLogViewerSearchPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerSearchPanel_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerSearchPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTextDocument>
+
+/* GUI includes: */
+#include "UIVMLogViewerPanel.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QHBoxLayout;
+class QLabel;
+class QWidget;
+class QIToolButton;
+class UISearchLineEdit;
+class UIVMLogViewerWidget;
+
+/** UIVMLogViewerPanel extension
+ * providing GUI for search-panel in VM Log-Viewer. */
+class UIVMLogViewerSearchPanel : public UIVMLogViewerPanel
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigHighlightingUpdated();
+ void sigSearchUpdated();
+
+public:
+
+ /** Constructs search-panel by passing @a pParent to the QWidget base-class constructor.
+ * @param pViewer Specifies instance of VM Log-Viewer. */
+ UIVMLogViewerSearchPanel(QWidget *pParent, UIVMLogViewerWidget *pViewer);
+ /** Resets the search position and starts a new search. */
+ void refresh();
+ const QVector<float> &matchLocationVector() const;
+ virtual QString panelName() const RT_OVERRIDE;
+ /** Returns the number of the matches to the current search. */
+ int matchCount() const;
+
+protected:
+
+ virtual void prepareWidgets() RT_OVERRIDE;
+ virtual void prepareConnections() RT_OVERRIDE;
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** Handles Qt key-press @a pEevent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+ /** Handles Qt @a pEvent, used for keyboard processing. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ virtual void hideEvent(QHideEvent* pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles textchanged signal from search-editor.
+ * @param strSearchString Specifies search-string. */
+ void sltSearchTextChanged(const QString &strSearchString);
+ void sltHighlightAllCheckBox();
+ void sltCaseSentitiveCheckBox();
+ void sltMatchWholeWordCheckBox();
+ void sltSelectNextPreviousMatch();
+
+private:
+
+ enum SearchDirection { ForwardSearch, BackwardSearch };
+
+ /** Clear the highlighting */
+ void clearHighlighting();
+
+ /** Search routine.
+ * @param eDirection Specifies the seach direction
+ * @param highlight if false highlight function is not called
+ thus we avoid calling highlighting for the same string repeatedly. */
+ void performSearch(SearchDirection eDirection, bool highlight);
+ void highlightAll(const QString &searchString);
+ void findAll(QTextDocument *pDocument, const QString &searchString);
+ void selectMatch(int iMatchIndex, const QString &searchString);
+ void moveSelection(bool fForward);
+
+ /** Constructs the find flags for QTextDocument::find function. */
+ QTextDocument::FindFlags constructFindFlags(SearchDirection eDirection) const;
+ /** Searches the whole document and return the number of matches to the current search term. */
+ int countMatches(QTextDocument *pDocument, const QString &searchString) const;
+ void reset();
+
+ /** Holds the instance of search-editor we create. */
+ UISearchLineEdit *m_pSearchEditor;
+
+ QIToolButton *m_pNextButton;
+ QIToolButton *m_pPreviousButton;
+ /** Holds the instance of case-sensitive checkbox we create. */
+ QCheckBox *m_pCaseSensitiveCheckBox;
+ QCheckBox *m_pMatchWholeWordCheckBox;
+ QCheckBox *m_pHighlightAllCheckBox;
+ /** Stores relative positions of the lines of the matches wrt. total # of lines. The values are in [0,1]
+ 0 being the first line 1 being the last. */
+ QVector<float> m_matchLocationVector;
+ /** Document positions of the cursors within th document for all matches. */
+ QVector<int> m_matchedCursorPosition;
+ /** The index of the curently selected item within m_matchedCursorPosition. */
+ int m_iSelectedMatchIndex;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerSearchPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerTextEdit.cpp b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerTextEdit.cpp
new file mode 100644
index 00000000..61b72c51
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerTextEdit.cpp
@@ -0,0 +1,574 @@
+/* $Id: UIVMLogViewerTextEdit.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#if defined(RT_OS_SOLARIS)
+# include <QFontDatabase>
+#endif
+#include <QMenu>
+#include <QPainter>
+#include <QPlainTextEdit>
+#include <QScrollBar>
+#include <QStyle>
+#include <QTextBlock>
+
+/* GUI includes: */
+#include "UIIconPool.h"
+#include "UIVMLogViewerTextEdit.h"
+#include "UIVMLogViewerWidget.h"
+
+/** We use a modified scrollbar style for our QPlainTextEdits to get the
+ markings on the scrollbars correctly. The default scrollbarstyle does not
+ reveal the height of the pushbuttons on the scrollbar (on either side of it, with arrow on them)
+ to compute the marking locations correctly. Thus we turn these push buttons off: */
+const QString verticalScrollBarStyle("QScrollBar:vertical {"
+ "border: 1px ridge grey; "
+ "margin: 0px 0px 0 0px;}"
+ "QScrollBar::handle:vertical {"
+ "min-height: 10px;"
+ "background: grey;}"
+ "QScrollBar::add-line:vertical {"
+ "width: 0px;}"
+ "QScrollBar::sub-line:vertical {"
+ "width: 0px;}");
+
+const QString horizontalScrollBarStyle("QScrollBar:horizontal {"
+ "border: 1px ridge grey; "
+ "margin: 0px 0px 0 0px;}"
+ "QScrollBar::handle:horizontal {"
+ "min-height: 10px;"
+ "background: grey;}"
+ "QScrollBar::add-line:horizontal {"
+ "height: 0px;}"
+ "QScrollBar::sub-line:horizontal {"
+ "height: 0px;}");
+
+
+/*********************************************************************************************************************************
+* UIIndicatorScrollBar definition. *
+*********************************************************************************************************************************/
+
+class UIIndicatorScrollBar : public QScrollBar
+{
+ Q_OBJECT;
+
+public:
+
+ UIIndicatorScrollBar(QWidget *parent = 0);
+ void setMarkingsVector(const QVector<float> &vector);
+ void clearMarkingsVector();
+
+protected:
+
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /* Stores the relative (to scrollbar's height) positions of markings,
+ where we draw a horizontal line. Values are in [0.0, 1.0]*/
+ QVector<float> m_markingsVector;
+};
+
+
+/*********************************************************************************************************************************
+* UIIndicatorScrollBar implemetation. *
+*********************************************************************************************************************************/
+
+UIIndicatorScrollBar::UIIndicatorScrollBar(QWidget *parent /*= 0 */)
+ :QScrollBar(parent)
+{
+ setStyleSheet(verticalScrollBarStyle);
+}
+
+void UIIndicatorScrollBar::setMarkingsVector(const QVector<float> &vector)
+{
+ m_markingsVector = vector;
+}
+
+void UIIndicatorScrollBar::clearMarkingsVector()
+{
+ m_markingsVector.clear();
+}
+
+void UIIndicatorScrollBar::paintEvent(QPaintEvent *pEvent) /* override */
+{
+ QScrollBar::paintEvent(pEvent);
+ /* Put a red line to mark the bookmark positions: */
+ for (int i = 0; i < m_markingsVector.size(); ++i)
+ {
+ QPointF p1 = QPointF(0, m_markingsVector[i] * height());
+ QPointF p2 = QPointF(width(), m_markingsVector[i] * height());
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setPen(QPen(QColor(255, 0, 0, 75), 1.1f));
+ painter.drawLine(p1, p2);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* UILineNumberArea definition. *
+*********************************************************************************************************************************/
+
+class UILineNumberArea : public QWidget
+{
+public:
+ UILineNumberArea(UIVMLogViewerTextEdit *textEdit);
+ QSize sizeHint() const;
+
+protected:
+
+ void paintEvent(QPaintEvent *event);
+ void mouseMoveEvent(QMouseEvent *pEvent);
+ void mousePressEvent(QMouseEvent *pEvent);
+
+private:
+ UIVMLogViewerTextEdit *m_pTextEdit;
+};
+
+
+/*********************************************************************************************************************************
+* UILineNumberArea implemetation. *
+*********************************************************************************************************************************/
+
+UILineNumberArea::UILineNumberArea(UIVMLogViewerTextEdit *textEdit)
+ :QWidget(textEdit)
+ , m_pTextEdit(textEdit)
+{
+ setMouseTracking(true);
+}
+
+QSize UILineNumberArea::sizeHint() const
+{
+ if (!m_pTextEdit)
+ return QSize();
+ return QSize(m_pTextEdit->lineNumberAreaWidth(), 0);
+}
+
+void UILineNumberArea::paintEvent(QPaintEvent *event)
+{
+ if (m_pTextEdit)
+ m_pTextEdit->lineNumberAreaPaintEvent(event);
+}
+
+void UILineNumberArea::mouseMoveEvent(QMouseEvent *pEvent)
+{
+ if (m_pTextEdit)
+ m_pTextEdit->setMouseCursorLine(m_pTextEdit->lineNumberForPos(pEvent->pos()));
+ update();
+}
+
+void UILineNumberArea::mousePressEvent(QMouseEvent *pEvent)
+{
+ if (m_pTextEdit)
+ m_pTextEdit->toggleBookmark(m_pTextEdit->bookmarkForPos(pEvent->pos()));
+}
+
+
+/*********************************************************************************************************************************
+* UIVMLogViewerTextEdit implemetation. *
+*********************************************************************************************************************************/
+
+UIVMLogViewerTextEdit::UIVMLogViewerTextEdit(QWidget* parent /* = 0 */)
+ : QIWithRetranslateUI<QPlainTextEdit>(parent)
+ , m_pLineNumberArea(0)
+ , m_mouseCursorLine(-1)
+ , m_bShownTextIsFiltered(false)
+ , m_bShowLineNumbers(true)
+ , m_bWrapLines(true)
+ , m_bHasContextMenu(false)
+ , m_iVerticalScrollBarValue(0)
+{
+ configure();
+ prepare();
+}
+
+void UIVMLogViewerTextEdit::configure()
+{
+ setMouseTracking(true);
+
+ /* Prepare modified standard palette: */
+ QPalette pal = QApplication::palette();
+ pal.setColor(QPalette::Inactive, QPalette::Highlight, pal.color(QPalette::Active, QPalette::Highlight));
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText, pal.color(QPalette::Active, QPalette::HighlightedText));
+ setPalette(pal);
+
+ /* Configure this' wrap mode: */
+ setWrapLines(false);
+ setReadOnly(true);
+}
+
+void UIVMLogViewerTextEdit::prepare()
+{
+ prepareWidgets();
+ retranslateUi();
+}
+
+void UIVMLogViewerTextEdit::prepareWidgets()
+{
+ m_pLineNumberArea = new UILineNumberArea(this);
+
+ connect(this, &UIVMLogViewerTextEdit::blockCountChanged, this, &UIVMLogViewerTextEdit::sltUpdateLineNumberAreaWidth);
+ connect(this, &UIVMLogViewerTextEdit::updateRequest, this, &UIVMLogViewerTextEdit::sltHandleUpdateRequest);
+ sltUpdateLineNumberAreaWidth(0);
+
+ setVerticalScrollBar(new UIIndicatorScrollBar());
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ QScrollBar *pHorizontalScrollBar = horizontalScrollBar();
+ if (pHorizontalScrollBar)
+ pHorizontalScrollBar->setStyleSheet(horizontalScrollBarStyle);
+}
+
+void UIVMLogViewerTextEdit::setCurrentFont(QFont font)
+{
+ setFont(font);
+ if (m_pLineNumberArea)
+ m_pLineNumberArea->setFont(font);
+}
+
+void UIVMLogViewerTextEdit::saveScrollBarPosition()
+{
+ if (verticalScrollBar())
+ m_iVerticalScrollBarValue = verticalScrollBar()->value();
+}
+
+void UIVMLogViewerTextEdit::restoreScrollBarPosition()
+{
+ QScrollBar *pBar = verticalScrollBar();
+ if (pBar && pBar->maximum() >= m_iVerticalScrollBarValue && pBar->minimum() <= m_iVerticalScrollBarValue)
+ pBar->setValue(m_iVerticalScrollBarValue);
+}
+
+void UIVMLogViewerTextEdit::setCursorPosition(int iPosition)
+{
+ QTextCursor cursor = textCursor();
+ cursor.setPosition(iPosition);
+ setTextCursor(cursor);
+ centerCursor();
+}
+
+int UIVMLogViewerTextEdit::lineNumberAreaWidth()
+{
+ if (!m_bShowLineNumbers)
+ return 0;
+
+ int digits = 1;
+ int max = qMax(1, blockCount());
+ while (max >= 10) {
+ max /= 10;
+ ++digits;
+ }
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
+#else
+ int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
+#endif
+
+ return space;
+}
+
+void UIVMLogViewerTextEdit::lineNumberAreaPaintEvent(QPaintEvent *event)
+{
+ if (!m_bShowLineNumbers)
+ return;
+ QPainter painter(m_pLineNumberArea);
+ painter.fillRect(event->rect(), Qt::lightGray);
+ QTextBlock block = firstVisibleBlock();
+ int blockNumber = block.blockNumber();
+ int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
+ int bottom = top + (int) blockBoundingRect(block).height();
+ while (block.isValid() && top <= event->rect().bottom()) {
+ if (block.isVisible() && bottom >= event->rect().top()) {
+ QString number = QString::number(blockNumber + 1);
+ /* Mark this line if it is bookmarked, but only if the text is not filtered. */
+ if (m_bookmarkLineSet.contains(blockNumber + 1) && !m_bShownTextIsFiltered)
+ {
+ QPainterPath path;
+ path.addRect(0, top, m_pLineNumberArea->width(), m_pLineNumberArea->fontMetrics().lineSpacing());
+ painter.fillPath(path, QColor(204, 255, 51, 125));
+ painter.drawPath(path);
+ }
+ /* Draw a unfilled red rectangled around the line number to indicate line the mouse cursor is currently
+ hovering on. Do this only if mouse is over the ext edit or the context menu is around: */
+ if ((blockNumber + 1) == m_mouseCursorLine && (underMouse() || m_bHasContextMenu))
+ {
+ painter.setPen(Qt::red);
+ painter.drawRect(0, top, m_pLineNumberArea->width(), m_pLineNumberArea->fontMetrics().lineSpacing());
+ }
+
+ painter.setPen(Qt::black);
+ painter.drawText(0, top, m_pLineNumberArea->width(), m_pLineNumberArea->fontMetrics().lineSpacing(),
+ Qt::AlignRight, number);
+ }
+ block = block.next();
+ top = bottom;
+ bottom = top + (int) blockBoundingRect(block).height();
+ ++blockNumber;
+ }
+}
+
+void UIVMLogViewerTextEdit::retranslateUi()
+{
+ m_strBackgroungText = QString(UIVMLogViewerWidget::tr("Filtered"));
+}
+
+void UIVMLogViewerTextEdit::contextMenuEvent(QContextMenuEvent *pEvent)
+{
+ /* If shown text is filtered, do not create Bookmark action since
+ we disable all bookmarking related functionalities in this case. */
+ if (m_bShownTextIsFiltered)
+ {
+ QPlainTextEdit::contextMenuEvent(pEvent);
+ return;
+ }
+ m_bHasContextMenu = true;
+ QMenu *menu = createStandardContextMenu();
+
+
+ QAction *pAction = menu->addAction(UIVMLogViewerWidget::tr("Bookmark"));
+ if (pAction)
+ {
+ pAction->setCheckable(true);
+ UIVMLogBookmark menuBookmark = bookmarkForPos(pEvent->pos());
+ pAction->setChecked(m_bookmarkLineSet.contains(menuBookmark.m_iLineNumber));
+ if (pAction->isChecked())
+ pAction->setIcon(UIIconPool::iconSet(":/log_viewer_bookmark_on_16px.png"));
+ else
+ pAction->setIcon(UIIconPool::iconSet(":/log_viewer_bookmark_off_16px.png"));
+
+ m_iContextMenuBookmark = menuBookmark;
+ connect(pAction, &QAction::triggered, this, &UIVMLogViewerTextEdit::sltBookmark);
+
+ }
+ menu->exec(pEvent->globalPos());
+
+ if (pAction)
+ disconnect(pAction, &QAction::triggered, this, &UIVMLogViewerTextEdit::sltBookmark);
+
+ delete menu;
+ m_bHasContextMenu = false;
+}
+
+void UIVMLogViewerTextEdit::resizeEvent(QResizeEvent *pEvent)
+{
+ QPlainTextEdit::resizeEvent(pEvent);
+ if (m_pLineNumberArea)
+ {
+ QRect cr = contentsRect();
+ m_pLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
+ }
+}
+
+void UIVMLogViewerTextEdit::mouseMoveEvent(QMouseEvent *pEvent)
+{
+ setMouseCursorLine(lineNumberForPos(pEvent->pos()));
+ if (m_pLineNumberArea)
+ m_pLineNumberArea->update();
+ QPlainTextEdit::mouseMoveEvent(pEvent);
+}
+
+void UIVMLogViewerTextEdit::leaveEvent(QEvent * pEvent)
+{
+ QPlainTextEdit::leaveEvent(pEvent);
+ /* Force a redraw as mouse leaves this to remove the mouse
+ cursor track rectangle (the red rectangle we draw on the line number area). */
+ update();
+}
+
+void UIVMLogViewerTextEdit::sltUpdateLineNumberAreaWidth(int /* newBlockCount */)
+{
+ setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
+}
+
+void UIVMLogViewerTextEdit::sltHandleUpdateRequest(const QRect &rect, int dy)
+{
+ if (dy)
+ m_pLineNumberArea->scroll(0, dy);
+ else
+ m_pLineNumberArea->update(0, rect.y(), m_pLineNumberArea->width(), rect.height());
+
+ if (rect.contains(viewport()->rect()))
+ sltUpdateLineNumberAreaWidth(0);
+
+ if (viewport())
+ viewport()->update();
+}
+
+void UIVMLogViewerTextEdit::sltBookmark()
+{
+ toggleBookmark(m_iContextMenuBookmark);
+}
+
+void UIVMLogViewerTextEdit::setScrollBarMarkingsVector(const QVector<float> &vector)
+{
+ UIIndicatorScrollBar* vScrollBar = qobject_cast<UIIndicatorScrollBar*>(verticalScrollBar());
+ if (vScrollBar)
+ vScrollBar->setMarkingsVector(vector);
+}
+
+void UIVMLogViewerTextEdit::clearScrollBarMarkingsVector()
+{
+ UIIndicatorScrollBar* vScrollBar = qobject_cast<UIIndicatorScrollBar*>(verticalScrollBar());
+ if (vScrollBar)
+ vScrollBar->clearMarkingsVector();
+}
+
+void UIVMLogViewerTextEdit::scrollToLine(int lineNumber)
+{
+ QTextDocument* pDocument = document();
+ if (!pDocument)
+ return;
+
+ moveCursor(QTextCursor::End);
+ int halfPageLineCount = 0.5 * visibleLineCount() ;
+ QTextCursor cursor(pDocument->findBlockByLineNumber(qMax(lineNumber - halfPageLineCount, 0)));
+ setTextCursor(cursor);
+}
+
+void UIVMLogViewerTextEdit::scrollToEnd()
+{
+ moveCursor(QTextCursor::End);
+ ensureCursorVisible();
+}
+
+int UIVMLogViewerTextEdit::visibleLineCount()
+{
+ int height = 0;
+ if (viewport())
+ height = viewport()->height();
+ if (verticalScrollBar() && verticalScrollBar()->isVisible())
+ height -= horizontalScrollBar()->height();
+ int singleLineHeight = fontMetrics().lineSpacing();
+ if (singleLineHeight == 0)
+ return 0;
+ return height / singleLineHeight;
+}
+
+void UIVMLogViewerTextEdit::setBookmarkLineSet(const QSet<int>& lineSet)
+{
+ m_bookmarkLineSet = lineSet;
+ update();
+}
+
+int UIVMLogViewerTextEdit::lineNumberForPos(const QPoint &position)
+{
+ QTextCursor cursor = cursorForPosition(position);
+ QTextBlock block = cursor.block();
+ return block.blockNumber() + 1;
+}
+
+UIVMLogBookmark UIVMLogViewerTextEdit::bookmarkForPos(const QPoint &position)
+{
+ QTextCursor cursor = cursorForPosition(position);
+ QTextBlock block = cursor.block();
+ return UIVMLogBookmark(block.blockNumber() + 1, cursor.position(), block.text());
+}
+
+void UIVMLogViewerTextEdit::setMouseCursorLine(int lineNumber)
+{
+ m_mouseCursorLine = lineNumber;
+}
+
+void UIVMLogViewerTextEdit::toggleBookmark(const UIVMLogBookmark& bookmark)
+{
+ if (m_bShownTextIsFiltered)
+ return;
+
+ if (m_bookmarkLineSet.contains(bookmark.m_iLineNumber))
+ emit sigDeleteBookmark(bookmark);
+ else
+ emit sigAddBookmark(bookmark);
+}
+
+void UIVMLogViewerTextEdit::setShownTextIsFiltered(bool warning)
+{
+ if (m_bShownTextIsFiltered == warning)
+ return;
+ m_bShownTextIsFiltered = warning;
+ if (viewport())
+ viewport()->update();
+}
+
+void UIVMLogViewerTextEdit::setShowLineNumbers(bool bShowLineNumbers)
+{
+ if (m_bShowLineNumbers == bShowLineNumbers)
+ return;
+ m_bShowLineNumbers = bShowLineNumbers;
+ emit updateRequest(viewport()->rect(), 0);
+}
+
+bool UIVMLogViewerTextEdit::showLineNumbers() const
+{
+ return m_bShowLineNumbers;
+}
+
+void UIVMLogViewerTextEdit::setWrapLines(bool bWrapLines)
+{
+ if (m_bWrapLines == bWrapLines)
+ return;
+ m_bWrapLines = bWrapLines;
+ if (m_bWrapLines)
+ {
+ setLineWrapMode(QPlainTextEdit::WidgetWidth);
+ setWordWrapMode(QTextOption::WordWrap);
+ }
+ else
+ {
+ setWordWrapMode(QTextOption::NoWrap);
+ setWordWrapMode(QTextOption::NoWrap);
+ }
+ update();
+}
+
+bool UIVMLogViewerTextEdit::wrapLines() const
+{
+ return m_bWrapLines;
+}
+
+int UIVMLogViewerTextEdit::currentVerticalScrollBarValue() const
+{
+ if (!verticalScrollBar())
+ return -1;
+ return verticalScrollBar()->value();
+}
+
+void UIVMLogViewerTextEdit::setCurrentVerticalScrollBarValue(int value)
+{
+ if (!verticalScrollBar())
+ return;
+
+ setCenterOnScroll(true);
+
+ verticalScrollBar()->setValue(value);
+ verticalScrollBar()->setSliderPosition(value);
+ viewport()->update();
+ update();
+}
+
+#include "UIVMLogViewerTextEdit.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerTextEdit.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerTextEdit.h
new file mode 100644
index 00000000..9e5f1444
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerTextEdit.h
@@ -0,0 +1,136 @@
+/* $Id: UIVMLogViewerTextEdit.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerTextEdit_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerTextEdit_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIVMLogBookmark.h"
+
+/* Qt includes: */
+#include <QPlainTextEdit>
+#include <QPair>
+
+
+/* QPlainTextEdit extension with some addtional context menu items,
+ a special scrollbar, line number area, bookmarking support,
+ background watermarking etc.: */
+class UIVMLogViewerTextEdit : public QIWithRetranslateUI<QPlainTextEdit>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigAddBookmark(const UIVMLogBookmark& bookmark);
+ void sigDeleteBookmark(const UIVMLogBookmark& bookmark);
+
+public:
+
+ UIVMLogViewerTextEdit(QWidget* parent = 0);
+
+ int lineNumberAreaWidth();
+ void lineNumberAreaPaintEvent(QPaintEvent *event);
+ /** Forwards the call to scroll bar class */
+ void setScrollBarMarkingsVector(const QVector<float> &vector);
+ /** Forwards the call to scroll bar class */
+ void clearScrollBarMarkingsVector();
+
+ void scrollToLine(int lineNumber);
+ void scrollToEnd();
+ void setBookmarkLineSet(const QSet<int>& lineSet);
+ void setShownTextIsFiltered(bool warning);
+
+ void setShowLineNumbers(bool bShowLineNumbers);
+ bool showLineNumbers() const;
+
+ void setWrapLines(bool bWrapLines);
+ bool wrapLines() const;
+
+ /** currentVerticalScrollBarValue is used by UIVMLogPage to store and restore scrolled
+ plain text position as we switch from a tab to another */
+ int currentVerticalScrollBarValue() const;
+ void setCurrentVerticalScrollBarValue(int value);
+ void setCurrentFont(QFont font);
+ void saveScrollBarPosition();
+ void restoreScrollBarPosition();
+
+ void setCursorPosition(int iPosition);
+
+protected:
+
+ virtual void contextMenuEvent(QContextMenuEvent *pEvent) RT_OVERRIDE;
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+ virtual void mouseMoveEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ virtual void leaveEvent(QEvent * pEvent) RT_OVERRIDE;
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ void sltBookmark();
+ void sltUpdateLineNumberAreaWidth(int newBlockCount);
+ void sltHandleUpdateRequest(const QRect &, int);
+ int visibleLineCount();
+
+private:
+
+ /** Configures this (such as palette etc.) */
+ void configure();
+ void prepare();
+ void prepareWidgets();
+ UIVMLogBookmark bookmarkForPos(const QPoint &position);
+ int lineNumberForPos(const QPoint &position);
+ void setMouseCursorLine(int lineNumber);
+ /** If bookmark exists this function removes it, if not it adds the bookmark. */
+ void toggleBookmark(const UIVMLogBookmark& bookmark);
+
+ UIVMLogBookmark m_iContextMenuBookmark;
+ QWidget *m_pLineNumberArea;
+ /** Set of bookmarked lines. This set is updated from UIVMLogPage. This set is
+ used only for lookup in this class. */
+ QSet<int> m_bookmarkLineSet;
+ /** Number of the line under the mouse cursor. */
+ int m_mouseCursorLine;
+ /** If true the we draw a text near the top right corner of the text edit to warn
+ the user the text edit's content is filtered (as oppesed to whole log file content.
+ And we dont display bookmarks and adding/deleting bookmarks are disabled. */
+ bool m_bShownTextIsFiltered;
+ bool m_bShowLineNumbers;
+ bool m_bWrapLines;
+ QString m_strBackgroungText;
+ friend class UILineNumberArea;
+ bool m_bHasContextMenu;
+ int m_iVerticalScrollBarValue;
+ };
+
+
+
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerTextEdit_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerWidget.cpp b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerWidget.cpp
new file mode 100644
index 00000000..9e511230
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerWidget.cpp
@@ -0,0 +1,1250 @@
+/* $Id: UIVMLogViewerWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewerWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QDateTime>
+#include <QDir>
+#include <QFont>
+#include <QMenu>
+#include <QPainter>
+#include <QPlainTextEdit>
+#include <QScrollBar>
+#include <QStyle>
+#include <QStyleFactory>
+#include <QStylePainter>
+#include <QStyleOptionTab>
+#include <QTabBar>
+#include <QTextBlock>
+#include <QVBoxLayout>
+#ifdef RT_OS_SOLARIS
+# include <QFontDatabase>
+#endif
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "QITabWidget.h"
+#include "UIActionPool.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIVirtualMachineItem.h"
+#include "UIVMLogPage.h"
+#include "UIVMLogViewerWidget.h"
+#include "UIVMLogViewerBookmarksPanel.h"
+#include "UIVMLogViewerFilterPanel.h"
+#include "UIVMLogViewerSearchPanel.h"
+#include "UIVMLogViewerOptionsPanel.h"
+#include "QIToolBar.h"
+#include "QIToolButton.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+/** Limit the read string size to avoid bloated log viewer pages. */
+const ULONG uAllowedLogSize = _256M;
+
+class UILogTabCloseButton : public QIToolButton
+{
+ Q_OBJECT;
+
+public:
+
+ //UILogTabCloseButton(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName);
+ UILogTabCloseButton(QWidget *pParent, const QUuid &uMachineId)
+ : QIToolButton(pParent)
+ , m_uMachineId(uMachineId)
+ {
+ setAutoRaise(true);
+ setIcon(UIIconPool::iconSet(":/close_16px.png"));
+ }
+
+ const QUuid &machineId() const
+ {
+ return m_uMachineId;
+ }
+
+protected:
+
+ QUuid m_uMachineId;
+};
+/*********************************************************************************************************************************
+* UILabelTab definition. *
+*********************************************************************************************************************************/
+
+class UILabelTab : public UIVMLogTab
+{
+
+ Q_OBJECT;
+
+public:
+
+ UILabelTab(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName);
+
+protected:
+
+ void retranslateUi();
+};
+
+/*********************************************************************************************************************************
+* UITabBar definition. *
+*********************************************************************************************************************************/
+/** A QTabBar extention to be able to override paintEvent for custom tab coloring. */
+class UITabBar : public QTabBar
+{
+
+ Q_OBJECT;
+
+public:
+
+ UITabBar(QWidget *pParent = 0);
+
+protected:
+
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+};
+
+/*********************************************************************************************************************************
+* UITabWidget definition. *
+*********************************************************************************************************************************/
+
+/** A QITabWidget used only for setTabBar since it is protected. */
+class UITabWidget : public QITabWidget
+{
+
+ Q_OBJECT;
+
+public:
+
+ UITabWidget(QWidget *pParent = 0);
+};
+
+/*********************************************************************************************************************************
+* UILabelTab implementation. *
+*********************************************************************************************************************************/
+
+UILabelTab::UILabelTab(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName)
+ : UIVMLogTab(pParent, uMachineId, strMachineName)
+{
+}
+
+void UILabelTab::retranslateUi()
+{
+}
+
+/*********************************************************************************************************************************
+* UITabBar implementation. *
+*********************************************************************************************************************************/
+
+UITabBar::UITabBar(QWidget *pParent /* = 0 */)
+ :QTabBar(pParent)
+{
+}
+
+void UITabBar::paintEvent(QPaintEvent *pEvent)
+{
+ Q_UNUSED(pEvent);
+ QStylePainter painter(this);
+ for (int i = 0; i < count(); i++)
+ {
+ QStyleOptionTab opt;
+ initStyleOption(&opt, i);
+ bool fLabelTab = tabData(i).toBool();
+
+ if (!fLabelTab)
+ painter.drawControl(QStyle::CE_TabBarTabShape, opt);
+ painter.drawControl(QStyle::CE_TabBarTabLabel, opt);
+ }
+}
+
+/*********************************************************************************************************************************
+* UITabWidget implementation. *
+*********************************************************************************************************************************/
+
+UITabWidget::UITabWidget(QWidget *pParent /* = 0 */)
+ :QITabWidget(pParent)
+{
+ setTabBar(new UITabBar(this));
+}
+
+
+/*********************************************************************************************************************************
+* UIVMLogViewerWidget implementation. *
+*********************************************************************************************************************************/
+
+UIVMLogViewerWidget::UIVMLogViewerWidget(EmbedTo enmEmbedding,
+ UIActionPool *pActionPool,
+ bool fShowToolbar /* = true */,
+ const QUuid &uMachineId /* = QUuid() */,
+ QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_fIsPolished(false)
+ , m_pTabWidget(0)
+ , m_pSearchPanel(0)
+ , m_pFilterPanel(0)
+ , m_pBookmarksPanel(0)
+ , m_pOptionsPanel(0)
+ , m_pMainLayout(0)
+ , m_pToolBar(0)
+ , m_bShowLineNumbers(true)
+ , m_bWrapLines(false)
+ , m_font(QFontDatabase::systemFont(QFontDatabase::FixedFont))
+ , m_pCornerButton(0)
+ , m_pMachineSelectionMenu(0)
+ , m_fCommitDataSignalReceived(false)
+ , m_pPreviousLogPage(0)
+{
+ /* Prepare VM Log-Viewer: */
+ prepare();
+ restorePanelVisibility();
+ if (!uMachineId.isNull())
+ setMachines(QVector<QUuid>() << uMachineId);
+ connect(&uiCommon(), &UICommon::sigAskToCommitData,
+ this, &UIVMLogViewerWidget::sltCommitDataSignalReceived);
+}
+
+UIVMLogViewerWidget::~UIVMLogViewerWidget()
+{
+}
+
+int UIVMLogViewerWidget::defaultLogPageWidth() const
+{
+ if (!m_pTabWidget)
+ return 0;
+
+ QWidget *pContainer = m_pTabWidget->currentWidget();
+ if (!pContainer)
+ return 0;
+
+ QPlainTextEdit *pBrowser = pContainer->findChild<QPlainTextEdit*>();
+ if (!pBrowser)
+ return 0;
+ /* Compute a width for 132 characters plus scrollbar and frame width: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iDefaultWidth = pBrowser->fontMetrics().horizontalAdvance(QChar('x')) * 132 +
+#else
+ int iDefaultWidth = pBrowser->fontMetrics().width(QChar('x')) * 132 +
+#endif
+ pBrowser->verticalScrollBar()->width() +
+ pBrowser->frameWidth() * 2;
+
+ return iDefaultWidth;
+}
+
+QMenu *UIVMLogViewerWidget::menu() const
+{
+ return m_pActionPool->action(UIActionIndex_M_LogWindow)->menu();
+}
+
+
+void UIVMLogViewerWidget::setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items)
+{
+ QVector<QUuid> selectedMachines;
+
+ foreach (const UIVirtualMachineItem *item, items)
+ {
+ if (!item)
+ continue;
+ selectedMachines << item->id();
+ }
+ setMachines(selectedMachines);
+}
+
+void UIVMLogViewerWidget::addSelectedVMListItems(const QList<UIVirtualMachineItem*> &items)
+{
+ QVector<QUuid> selectedMachines(m_machines);
+
+ foreach (const UIVirtualMachineItem *item, items)
+ {
+ if (!item)
+ continue;
+ selectedMachines << item->id();
+ }
+ setMachines(selectedMachines);
+}
+
+void UIVMLogViewerWidget::setMachines(const QVector<QUuid> &machineIDs)
+{
+ /* List of machines that are newly added to selected machine list: */
+ QVector<QUuid> newSelections;
+ QVector<QUuid> unselectedMachines(m_machines);
+
+ foreach (const QUuid &id, machineIDs)
+ {
+ unselectedMachines.removeAll(id);
+ if (!m_machines.contains(id))
+ newSelections << id;
+ }
+ m_machines = machineIDs;
+
+ m_pTabWidget->hide();
+ /* Read logs and create pages/tabs for newly selected machines: */
+ createLogViewerPages(newSelections);
+ /* Remove the log pages/tabs of unselected machines from the tab widget: */
+ removeLogViewerPages(unselectedMachines);
+ /* Assign color indexes to tabs based on machines. We use two alternating colors to indicate different machine logs. */
+ markLabelTabs();
+ labelTabHandler();
+ m_pTabWidget->show();
+}
+
+void UIVMLogViewerWidget::markLabelTabs()
+{
+ if (!m_pTabWidget || !m_pTabWidget->tabBar() || m_pTabWidget->tabBar()->count() == 0)
+ return;
+ QTabBar *pTabBar = m_pTabWidget->tabBar();
+
+ for (int i = 0; i < pTabBar->count(); ++i)
+ {
+ if (qobject_cast<UILabelTab*>(m_pTabWidget->widget(i)))
+ {
+ pTabBar->setTabData(i, true);
+ /* Add close button only for dialog mode in manager UI. */
+ if (uiCommon().uiType() == UICommon::UIType_SelectorUI && m_enmEmbedding == EmbedTo_Dialog)
+ {
+ UIVMLogTab *pTab = logTab(i);
+ if (pTab)
+ {
+ UILogTabCloseButton *pCloseButton = new UILogTabCloseButton(0, pTab->machineId());
+ pCloseButton->setIcon(UIIconPool::iconSet(":/close_16px.png"));
+ pTabBar->setTabButton(i, QTabBar::RightSide, pCloseButton);
+ pCloseButton->setToolTip(tr("Close this machine's logs"));
+ connect(pCloseButton, &UILogTabCloseButton::clicked, this, &UIVMLogViewerWidget::sltTabCloseButtonClick);
+ }
+ }
+ }
+ else
+ {
+ pTabBar->setTabData(i, false);
+ }
+
+ }
+}
+
+QString UIVMLogViewerWidget::readLogFile(CMachine &comMachine, int iLogFileId)
+{
+ QString strLogFileContent;
+ ULONG uOffset = 0;
+
+ while (true)
+ {
+ QVector<BYTE> data = comMachine.ReadLog(iLogFileId, uOffset, _1M);
+ if (data.size() == 0)
+ break;
+ strLogFileContent.append(QString::fromUtf8((char*)data.data(), data.size()));
+ uOffset += data.size();
+ /* Don't read futher if we have reached the allowed size limit: */
+ if (uOffset >= uAllowedLogSize)
+ {
+ strLogFileContent.append("\n=========Log file has been truncated as it is too large.======");
+ break;
+ }
+ }
+ return strLogFileContent;
+}
+
+QFont UIVMLogViewerWidget::currentFont() const
+{
+ const UIVMLogPage* logPage = currentLogPage();
+ if (!logPage)
+ return QFont();
+ return logPage->currentFont();
+}
+
+bool UIVMLogViewerWidget::shouldBeMaximized() const
+{
+ return gEDataManager->logWindowShouldBeMaximized();
+}
+
+void UIVMLogViewerWidget::saveOptions()
+{
+ if (!m_fCommitDataSignalReceived)
+ gEDataManager->setLogViweverOptions(m_font, m_bWrapLines, m_bShowLineNumbers);
+}
+
+void UIVMLogViewerWidget::savePanelVisibility()
+{
+ if (m_fCommitDataSignalReceived)
+ return;
+ /* Save a list of currently visible panels: */
+ QStringList strNameList;
+ foreach(UIDialogPanel* pPanel, m_visiblePanelsList)
+ strNameList.append(pPanel->panelName());
+ gEDataManager->setLogViewerVisiblePanels(strNameList);
+}
+
+void UIVMLogViewerWidget::sltRefresh()
+{
+ if (!m_pTabWidget)
+ return;
+
+ UIVMLogPage *pCurrentPage = currentLogPage();
+ if (!pCurrentPage || pCurrentPage->logFileId() == -1)
+ return;
+
+ CMachine comMachine = uiCommon().virtualBox().FindMachine(pCurrentPage->machineId().toString());
+ if (comMachine.isNull())
+ return;
+
+ QString strLogContent = readLogFile(comMachine, pCurrentPage->logFileId());
+ pCurrentPage->setLogContent(strLogContent, false);
+
+ if (m_pSearchPanel && m_pSearchPanel->isVisible())
+ m_pSearchPanel->refresh();
+
+ /* Re-Apply the filter settings: */
+ if (m_pFilterPanel)
+ m_pFilterPanel->applyFilter();
+}
+
+void UIVMLogViewerWidget::sltReload()
+{
+ if (!m_pTabWidget)
+ return;
+
+ m_pTabWidget->blockSignals(true);
+ m_pTabWidget->hide();
+
+ removeAllLogPages();
+ createLogViewerPages(m_machines);
+
+ /* re-Apply the filter settings: */
+ if (m_pFilterPanel)
+ m_pFilterPanel->applyFilter();
+
+ m_pTabWidget->blockSignals(false);
+ markLabelTabs();
+ m_pTabWidget->show();
+}
+
+void UIVMLogViewerWidget::sltSave()
+{
+ UIVMLogPage *pLogPage = currentLogPage();
+ if (!pLogPage)
+ return;
+
+ CMachine comMachine = uiCommon().virtualBox().FindMachine(pLogPage->machineId().toString());
+ if (comMachine.isNull())
+ return;
+
+ const QString& fileName = pLogPage->logFileName();
+ if (fileName.isEmpty())
+ return;
+ /* Prepare "save as" dialog: */
+ const QFileInfo fileInfo(fileName);
+ /* Prepare default filename: */
+ const QDateTime dtInfo = fileInfo.lastModified();
+ const QString strDtString = dtInfo.toString("yyyy-MM-dd-hh-mm-ss");
+ const QString strDefaultFileName = QString("%1-%2.log").arg(comMachine.GetName()).arg(strDtString);
+ const QString strDefaultFullName = QDir::toNativeSeparators(QDir::home().absolutePath() + "/" + strDefaultFileName);
+
+ const QString strNewFileName = QIFileDialog::getSaveFileName(strDefaultFullName,
+ "",
+ this,
+ tr("Save VirtualBox Log As"),
+ 0 /* selected filter */,
+ true /* resolve symlinks */,
+ true /* confirm overwrite */);
+ /* Make sure file-name is not empty: */
+ if (!strNewFileName.isEmpty())
+ {
+ /* Delete the previous file if already exists as user already confirmed: */
+ if (QFile::exists(strNewFileName))
+ QFile::remove(strNewFileName);
+ /* Copy log into the file: */
+ QFile::copy(fileName, strNewFileName);
+ }
+}
+
+void UIVMLogViewerWidget::sltDeleteBookmarkByIndex(int index)
+{
+ UIVMLogPage* pLogPage = currentLogPage();
+ if (!pLogPage)
+ return;
+ pLogPage->deleteBookmarkByIndex(index);
+ if (m_pBookmarksPanel)
+ m_pBookmarksPanel->updateBookmarkList(pLogPage->bookmarkList());
+}
+
+void UIVMLogViewerWidget::sltDeleteAllBookmarks()
+{
+ UIVMLogPage* pLogPage = currentLogPage();
+ if (!pLogPage)
+ return;
+ pLogPage->deleteAllBookmarks();
+
+ if (m_pBookmarksPanel)
+ m_pBookmarksPanel->updateBookmarkList(pLogPage->bookmarkList());
+}
+
+void UIVMLogViewerWidget::sltUpdateBookmarkPanel()
+{
+ if (!currentLogPage() || !m_pBookmarksPanel)
+ return;
+ m_pBookmarksPanel->updateBookmarkList(currentLogPage()->bookmarkList());
+}
+
+void UIVMLogViewerWidget::gotoBookmark(int bookmarkIndex)
+{
+ if (!currentLogPage())
+ return;
+ currentLogPage()->scrollToBookmark(bookmarkIndex);
+}
+
+void UIVMLogViewerWidget::sltPanelActionToggled(bool fChecked)
+{
+ QAction *pSenderAction = qobject_cast<QAction*>(sender());
+ if (!pSenderAction)
+ return;
+ UIDialogPanel* pPanel = 0;
+ /* Look for the sender() within the m_panelActionMap's values: */
+ for (QMap<UIDialogPanel*, QAction*>::const_iterator iterator = m_panelActionMap.begin();
+ iterator != m_panelActionMap.end(); ++iterator)
+ {
+ if (iterator.value() == pSenderAction)
+ pPanel = iterator.key();
+ }
+ if (!pPanel)
+ return;
+ if (fChecked)
+ showPanel(pPanel);
+ else
+ hidePanel(pPanel);
+}
+
+void UIVMLogViewerWidget::sltSearchResultHighLigting()
+{
+ if (!m_pSearchPanel || !currentLogPage())
+ return;
+ currentLogPage()->setScrollBarMarkingsVector(m_pSearchPanel->matchLocationVector());
+}
+
+void UIVMLogViewerWidget::sltHandleSearchUpdated()
+{
+ if (!m_pSearchPanel || !currentLogPage())
+ return;
+}
+
+void UIVMLogViewerWidget::sltCurrentTabChanged(int tabIndex)
+{
+ Q_UNUSED(tabIndex);
+
+ if (m_pPreviousLogPage)
+ m_pPreviousLogPage->saveScrollBarPosition();
+
+ if (labelTabHandler())
+ return;
+ /* Dont refresh the search here as it is refreshed by the filtering mechanism
+ which is updated as tab current index changes (see sltFilterApplied): */
+ if (m_pFilterPanel)
+ m_pFilterPanel->applyFilter();
+
+ /* We keep a separate QVector<LogBookmark> for each log page: */
+ if (m_pBookmarksPanel && currentLogPage())
+ m_pBookmarksPanel->updateBookmarkList(currentLogPage()->bookmarkList());
+
+ m_pPreviousLogPage = currentLogPage();
+ if (m_pPreviousLogPage)
+ m_pPreviousLogPage->restoreScrollBarPosition();
+}
+
+void UIVMLogViewerWidget::sltFilterApplied()
+{
+ /* Reapply the search to get highlighting etc. correctly */
+ if (m_pSearchPanel)
+ m_pSearchPanel->refresh();
+}
+
+void UIVMLogViewerWidget::sltLogPageFilteredChanged(bool isFiltered)
+{
+ /* Disable bookmark panel since bookmarks are stored as line numbers within
+ the original log text and does not mean much in a reduced/filtered one. */
+ if (m_pBookmarksPanel)
+ m_pBookmarksPanel->disableEnableBookmarking(!isFiltered);
+}
+
+void UIVMLogViewerWidget::sltHandleHidePanel(UIDialogPanel *pPanel)
+{
+ hidePanel(pPanel);
+}
+
+void UIVMLogViewerWidget::sltHandleShowPanel(UIDialogPanel *pPanel)
+{
+ showPanel(pPanel);
+}
+
+void UIVMLogViewerWidget::sltShowLineNumbers(bool bShowLineNumbers)
+{
+ if (m_bShowLineNumbers == bShowLineNumbers)
+ return;
+
+ m_bShowLineNumbers = bShowLineNumbers;
+ /* Set all log page instances. */
+ for (int i = 0; m_pTabWidget && (i < m_pTabWidget->count()); ++i)
+ {
+ UIVMLogPage* pLogPage = logPage(i);
+ if (pLogPage)
+ pLogPage->setShowLineNumbers(m_bShowLineNumbers);
+ }
+ saveOptions();
+}
+
+void UIVMLogViewerWidget::sltWrapLines(bool bWrapLines)
+{
+ if (m_bWrapLines == bWrapLines)
+ return;
+
+ m_bWrapLines = bWrapLines;
+ /* Set all log page instances. */
+ for (int i = 0; m_pTabWidget && (i < m_pTabWidget->count()); ++i)
+ {
+ UIVMLogPage* pLogPage = logPage(i);
+ if (pLogPage)
+ pLogPage->setWrapLines(m_bWrapLines);
+ }
+ saveOptions();
+}
+
+void UIVMLogViewerWidget::sltFontSizeChanged(int fontSize)
+{
+ if (m_font.pointSize() == fontSize)
+ return;
+ m_font.setPointSize(fontSize);
+ for (int i = 0; m_pTabWidget && (i < m_pTabWidget->count()); ++i)
+ {
+ UIVMLogPage* pLogPage = logPage(i);
+ if (pLogPage)
+ pLogPage->setCurrentFont(m_font);
+ }
+ saveOptions();
+}
+
+void UIVMLogViewerWidget::sltChangeFont(QFont font)
+{
+ if (m_font == font)
+ return;
+ m_font = font;
+ for (int i = 0; m_pTabWidget && (i < m_pTabWidget->count()); ++i)
+ {
+ UIVMLogPage* pLogPage = logPage(i);
+ if (pLogPage)
+ pLogPage->setCurrentFont(m_font);
+ }
+ saveOptions();
+}
+
+void UIVMLogViewerWidget::sltResetOptionsToDefault()
+{
+ sltShowLineNumbers(true);
+ sltWrapLines(false);
+ sltChangeFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
+
+ if (m_pOptionsPanel)
+ {
+ m_pOptionsPanel->setShowLineNumbers(true);
+ m_pOptionsPanel->setWrapLines(false);
+ m_pOptionsPanel->setFontSizeInPoints(m_font.pointSize());
+ }
+ saveOptions();
+}
+
+void UIVMLogViewerWidget::sltCloseMachineLogs()
+{
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ if (!pAction)
+ return;
+ QUuid machineId = pAction->data().toUuid();
+ if (machineId.isNull())
+ return;
+ QVector<QUuid> machineList;
+ machineList << machineId;
+ removeLogViewerPages(machineList);
+}
+
+void UIVMLogViewerWidget::sltTabCloseButtonClick()
+{
+ UILogTabCloseButton *pButton = qobject_cast<UILogTabCloseButton*>(sender());
+ if (!pButton)
+ return;
+ if (pButton->machineId().isNull())
+ return;
+ QVector<QUuid> list;
+ list << pButton->machineId();
+ removeLogViewerPages(list);
+}
+
+void UIVMLogViewerWidget::sltCommitDataSignalReceived()
+{
+ m_fCommitDataSignalReceived = true;
+}
+
+void UIVMLogViewerWidget::prepare()
+{
+ /* Load options: */
+ loadOptions();
+
+ /* Prepare stuff: */
+ prepareActions();
+ /* Prepare widgets: */
+ prepareWidgets();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Setup escape shortcut: */
+ manageEscapeShortCut();
+ uiCommon().setHelpKeyword(this, "log-viewer");
+}
+
+void UIVMLogViewerWidget::prepareActions()
+{
+ /* First of all, add actions which has smaller shortcut scope: */
+ addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Find));
+ addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Filter));
+ addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark));
+ addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Options));
+ addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Refresh));
+ addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Save));
+
+ /* Connect actions: */
+ connect(m_pActionPool->action(UIActionIndex_M_Log_T_Find), &QAction::toggled,
+ this, &UIVMLogViewerWidget::sltPanelActionToggled);
+ connect(m_pActionPool->action(UIActionIndex_M_Log_T_Filter), &QAction::toggled,
+ this, &UIVMLogViewerWidget::sltPanelActionToggled);
+ connect(m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark), &QAction::toggled,
+ this, &UIVMLogViewerWidget::sltPanelActionToggled);
+ connect(m_pActionPool->action(UIActionIndex_M_Log_T_Options), &QAction::toggled,
+ this, &UIVMLogViewerWidget::sltPanelActionToggled);
+ connect(m_pActionPool->action(UIActionIndex_M_Log_S_Refresh), &QAction::triggered,
+ this, &UIVMLogViewerWidget::sltRefresh);
+ connect(m_pActionPool->action(UIActionIndex_M_Log_S_Reload), &QAction::triggered,
+ this, &UIVMLogViewerWidget::sltReload);
+ connect(m_pActionPool->action(UIActionIndex_M_Log_S_Save), &QAction::triggered,
+ this, &UIVMLogViewerWidget::sltSave);
+}
+
+void UIVMLogViewerWidget::prepareWidgets()
+{
+ /* Create main layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (m_pMainLayout)
+ {
+ /* Configure layout: */
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setSpacing(10);
+#else
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ /* Prepare toolbar, if requested: */
+ if (m_fShowToolbar)
+ prepareToolBar();
+
+ /* Create VM Log-Viewer container: */
+ m_pTabWidget = new UITabWidget;
+ if (m_pTabWidget)
+ {
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pTabWidget);
+ connect(m_pTabWidget, &QITabWidget::currentChanged, this, &UIVMLogViewerWidget::sltCurrentTabChanged);
+ }
+
+ /* Create VM Log-Viewer search-panel: */
+ m_pSearchPanel = new UIVMLogViewerSearchPanel(0, this);
+ if (m_pSearchPanel)
+ {
+ /* Configure panel: */
+ installEventFilter(m_pSearchPanel);
+ m_pSearchPanel->hide();
+ connect(m_pSearchPanel, &UIVMLogViewerSearchPanel::sigHighlightingUpdated,
+ this, &UIVMLogViewerWidget::sltSearchResultHighLigting);
+ connect(m_pSearchPanel, &UIVMLogViewerSearchPanel::sigSearchUpdated,
+ this, &UIVMLogViewerWidget::sltHandleSearchUpdated);
+ connect(m_pSearchPanel, &UIVMLogViewerSearchPanel::sigHidePanel,
+ this, &UIVMLogViewerWidget::sltHandleHidePanel);
+ connect(m_pSearchPanel, &UIVMLogViewerSearchPanel::sigShowPanel,
+ this, &UIVMLogViewerWidget::sltHandleShowPanel);
+ m_panelActionMap.insert(m_pSearchPanel, m_pActionPool->action(UIActionIndex_M_Log_T_Find));
+
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pSearchPanel);
+ }
+
+ /* Create VM Log-Viewer filter-panel: */
+ m_pFilterPanel = new UIVMLogViewerFilterPanel(0, this);
+ if (m_pFilterPanel)
+ {
+ /* Configure panel: */
+ installEventFilter(m_pFilterPanel);
+ m_pFilterPanel->hide();
+ connect(m_pFilterPanel, &UIVMLogViewerFilterPanel::sigFilterApplied,
+ this, &UIVMLogViewerWidget::sltFilterApplied);
+ connect(m_pFilterPanel, &UIVMLogViewerFilterPanel::sigHidePanel,
+ this, &UIVMLogViewerWidget::sltHandleHidePanel);
+ connect(m_pFilterPanel, &UIVMLogViewerFilterPanel::sigShowPanel,
+ this, &UIVMLogViewerWidget::sltHandleShowPanel);
+ m_panelActionMap.insert(m_pFilterPanel, m_pActionPool->action(UIActionIndex_M_Log_T_Filter));
+
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pFilterPanel);
+ }
+
+ /* Create VM Log-Viewer bookmarks-panel: */
+ m_pBookmarksPanel = new UIVMLogViewerBookmarksPanel(0, this);
+ if (m_pBookmarksPanel)
+ {
+ /* Configure panel: */
+ m_pBookmarksPanel->hide();
+ connect(m_pBookmarksPanel, &UIVMLogViewerBookmarksPanel::sigDeleteBookmarkByIndex,
+ this, &UIVMLogViewerWidget::sltDeleteBookmarkByIndex);
+ connect(m_pBookmarksPanel, &UIVMLogViewerBookmarksPanel::sigDeleteAllBookmarks,
+ this, &UIVMLogViewerWidget::sltDeleteAllBookmarks);
+ connect(m_pBookmarksPanel, &UIVMLogViewerBookmarksPanel::sigBookmarkSelected,
+ this, &UIVMLogViewerWidget::gotoBookmark);
+ m_panelActionMap.insert(m_pBookmarksPanel, m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark));
+ connect(m_pBookmarksPanel, &UIVMLogViewerBookmarksPanel::sigHidePanel,
+ this, &UIVMLogViewerWidget::sltHandleHidePanel);
+ connect(m_pBookmarksPanel, &UIVMLogViewerBookmarksPanel::sigShowPanel,
+ this, &UIVMLogViewerWidget::sltHandleShowPanel);
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pBookmarksPanel);
+ }
+
+ /* Create VM Log-Viewer options-panel: */
+ m_pOptionsPanel = new UIVMLogViewerOptionsPanel(0, this);
+ if (m_pOptionsPanel)
+ {
+ /* Configure panel: */
+ m_pOptionsPanel->hide();
+ m_pOptionsPanel->setShowLineNumbers(m_bShowLineNumbers);
+ m_pOptionsPanel->setWrapLines(m_bWrapLines);
+ m_pOptionsPanel->setFontSizeInPoints(m_font.pointSize());
+ connect(m_pOptionsPanel, &UIVMLogViewerOptionsPanel::sigShowLineNumbers, this, &UIVMLogViewerWidget::sltShowLineNumbers);
+ connect(m_pOptionsPanel, &UIVMLogViewerOptionsPanel::sigWrapLines, this, &UIVMLogViewerWidget::sltWrapLines);
+ connect(m_pOptionsPanel, &UIVMLogViewerOptionsPanel::sigChangeFontSizeInPoints, this, &UIVMLogViewerWidget::sltFontSizeChanged);
+ connect(m_pOptionsPanel, &UIVMLogViewerOptionsPanel::sigChangeFont, this, &UIVMLogViewerWidget::sltChangeFont);
+ connect(m_pOptionsPanel, &UIVMLogViewerOptionsPanel::sigResetToDefaults, this, &UIVMLogViewerWidget::sltResetOptionsToDefault);
+ connect(m_pOptionsPanel, &UIVMLogViewerOptionsPanel::sigHidePanel, this, &UIVMLogViewerWidget::sltHandleHidePanel);
+ connect(m_pOptionsPanel, &UIVMLogViewerOptionsPanel::sigShowPanel, this, &UIVMLogViewerWidget::sltHandleShowPanel);
+
+ m_panelActionMap.insert(m_pOptionsPanel, m_pActionPool->action(UIActionIndex_M_Log_T_Options));
+
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pOptionsPanel);
+ }
+ }
+}
+
+void UIVMLogViewerWidget::prepareToolBar()
+{
+ /* Create toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ if (m_pToolBar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+
+ /* Add toolbar actions: */
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Save));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Find));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Filter));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Options));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Refresh));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Reload));
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pToolBar);
+ }
+#else
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pToolBar);
+#endif
+ }
+}
+
+void UIVMLogViewerWidget::loadOptions()
+{
+ m_bWrapLines = gEDataManager->logViewerWrapLines();
+ m_bShowLineNumbers = gEDataManager->logViewerShowLineNumbers();
+ QFont loadedFont = gEDataManager->logViewerFont();
+ if (loadedFont != QFont())
+ m_font = loadedFont;
+}
+
+void UIVMLogViewerWidget::restorePanelVisibility()
+{
+ /** Reset the action states first: */
+ foreach(QAction* pAction, m_panelActionMap.values())
+ {
+ pAction->blockSignals(true);
+ pAction->setChecked(false);
+ pAction->blockSignals(false);
+ }
+
+ /* Load the visible panel list and show them: */
+ QStringList strNameList = gEDataManager->logViewerVisiblePanels();
+ foreach(const QString strName, strNameList)
+ {
+ foreach(UIDialogPanel* pPanel, m_panelActionMap.keys())
+ {
+ if (strName == pPanel->panelName())
+ {
+ showPanel(pPanel);
+ break;
+ }
+ }
+ }
+}
+
+void UIVMLogViewerWidget::retranslateUi()
+{
+ /* Translate toolbar: */
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text. */
+ if (m_pToolBar)
+ m_pToolBar->updateLayout();
+#endif
+ if (m_pCornerButton)
+ m_pCornerButton->setToolTip(tr("Select machines to show their log"));
+}
+
+void UIVMLogViewerWidget::showEvent(QShowEvent *pEvent)
+{
+ QWidget::showEvent(pEvent);
+
+ /* One may think that QWidget::polish() is the right place to do things
+ * below, but apparently, by the time when QWidget::polish() is called,
+ * the widget style & layout are not fully done, at least the minimum
+ * size hint is not properly calculated. Since this is sometimes necessary,
+ * we provide our own "polish" implementation: */
+
+ if (m_fIsPolished)
+ return;
+
+ m_fIsPolished = true;
+}
+
+void UIVMLogViewerWidget::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Depending on key pressed: */
+ switch (pEvent->key())
+ {
+ /* Process Back key as switch to previous tab: */
+ case Qt::Key_Back:
+ {
+ if (m_pTabWidget->currentIndex() > 0)
+ {
+ m_pTabWidget->setCurrentIndex(m_pTabWidget->currentIndex() - 1);
+ return;
+ }
+ break;
+ }
+ /* Process Forward key as switch to next tab: */
+ case Qt::Key_Forward:
+ {
+ if (m_pTabWidget->currentIndex() < m_pTabWidget->count())
+ {
+ m_pTabWidget->setCurrentIndex(m_pTabWidget->currentIndex() + 1);
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ QWidget::keyPressEvent(pEvent);
+}
+
+QVector<UIVMLogTab*> UIVMLogViewerWidget::logTabs()
+{
+ QVector<UIVMLogTab*> tabs;
+ if (m_pTabWidget)
+ return tabs;
+ for (int i = 0; i < m_pTabWidget->count(); ++i)
+ {
+ UIVMLogTab *pPage = logTab(i);
+ if (pPage)
+ tabs << pPage;
+ }
+ return tabs;
+}
+
+void UIVMLogViewerWidget::createLogPage(const QString &strFileName,
+ const QString &strMachineName,
+ const QUuid &machineId, int iLogFileId,
+ const QString &strLogContent, bool noLogsToShow)
+{
+ if (!m_pTabWidget)
+ return;
+
+ /* Create page-container: */
+ UIVMLogPage* pLogPage = new UIVMLogPage(this, machineId, strMachineName);
+ if (pLogPage)
+ {
+ connect(pLogPage, &UIVMLogPage::sigBookmarksUpdated, this, &UIVMLogViewerWidget::sltUpdateBookmarkPanel);
+ connect(pLogPage, &UIVMLogPage::sigLogPageFilteredChanged, this, &UIVMLogViewerWidget::sltLogPageFilteredChanged);
+ /* Initialize setting for this log page */
+ pLogPage->setShowLineNumbers(m_bShowLineNumbers);
+ pLogPage->setWrapLines(m_bWrapLines);
+ pLogPage->setCurrentFont(m_font);
+ pLogPage->setLogFileId(iLogFileId);
+ /* Set the file name only if we really have log file to read. */
+ if (!noLogsToShow)
+ pLogPage->setLogFileName(strFileName);
+
+ int iIndex = m_pTabWidget->addTab(pLogPage, QFileInfo(strFileName).fileName());
+ /* !!Hack alert. Setting html to text edit while th tab is not current ends up in an empty text edit: */
+ if (noLogsToShow)
+ m_pTabWidget->setCurrentIndex(iIndex);
+
+ pLogPage->setLogContent(strLogContent, noLogsToShow);
+ pLogPage->setScrollBarMarkingsVector(m_pSearchPanel->matchLocationVector());
+ }
+}
+
+const UIVMLogPage *UIVMLogViewerWidget::currentLogPage() const
+{
+ if (!m_pTabWidget)
+ return 0;
+ return qobject_cast<const UIVMLogPage*>(m_pTabWidget->currentWidget());
+}
+
+UIVMLogPage *UIVMLogViewerWidget::currentLogPage()
+{
+ if (!m_pTabWidget)
+ return 0;
+ return qobject_cast<UIVMLogPage*>(m_pTabWidget->currentWidget());
+}
+
+UIVMLogTab *UIVMLogViewerWidget::logTab(int iIndex)
+{
+ if (!m_pTabWidget)
+ return 0;
+ return qobject_cast<UIVMLogTab*>(m_pTabWidget->widget(iIndex));
+}
+
+UIVMLogPage *UIVMLogViewerWidget::logPage(int iIndex)
+{
+ if (!m_pTabWidget)
+ return 0;
+ return qobject_cast<UIVMLogPage*>(m_pTabWidget->widget(iIndex));
+}
+
+void UIVMLogViewerWidget::createLogViewerPages(const QVector<QUuid> &machineList)
+{
+ if (!m_pTabWidget)
+ return;
+ m_pTabWidget->blockSignals(true);
+
+ const CSystemProperties &sys = uiCommon().virtualBox().GetSystemProperties();
+ unsigned cMaxLogs = sys.GetLogHistoryCount() + 1 /*VBox.log*/ + 1 /*VBoxHardening.log*/; /** @todo Add api for getting total possible log count! */
+ foreach (const QUuid &machineId, machineList)
+ {
+ CMachine comMachine = uiCommon().virtualBox().FindMachine(machineId.toString());
+ if (comMachine.isNull())
+ continue;
+
+ QUuid uMachineId = comMachine.GetId();
+ QString strMachineName = comMachine.GetName();
+
+ /* Add a label tab with machine name on it. Used only in manager UI: */
+ if (uiCommon().uiType() == UICommon::UIType_SelectorUI)
+ m_pTabWidget->addTab(new UILabelTab(this, uMachineId, strMachineName), strMachineName);
+
+ bool fNoLogFileForMachine = true;
+ for (unsigned iLogFileId = 0; iLogFileId < cMaxLogs; ++iLogFileId)
+ {
+ QString strLogContent = readLogFile(comMachine, iLogFileId);
+ if (!strLogContent.isEmpty())
+ {
+ fNoLogFileForMachine = false;
+ createLogPage(comMachine.QueryLogFilename(iLogFileId),
+ strMachineName,
+ uMachineId, iLogFileId,
+ strLogContent, false);
+ }
+ }
+ if (fNoLogFileForMachine)
+ {
+ QString strDummyTabText = QString(tr("<p>No log files for the machine %1 found. Press the "
+ "<b>Reload</b> button to reload the log folder "
+ "<nobr><b>%2</b></nobr>.</p>")
+ .arg(strMachineName).arg(comMachine.GetLogFolder()));
+ createLogPage(QString("NoLogFile"), strMachineName, uMachineId, -1 /* iLogFileId */, strDummyTabText, true);
+ }
+ }
+ m_pTabWidget->blockSignals(false);
+ labelTabHandler();
+}
+
+void UIVMLogViewerWidget::removeLogViewerPages(const QVector<QUuid> &machineList)
+{
+ /* Nothing to do: */
+ if (machineList.isEmpty() || !m_pTabWidget)
+ return;
+
+ QVector<QUuid> currentMachineList(m_machines);
+ /* Make sure that we remove the machine(s) from our machine list: */
+ foreach (const QUuid &id, machineList)
+ currentMachineList.removeAll(id);
+ if (currentMachineList.isEmpty())
+ return;
+ m_machines = currentMachineList;
+
+ m_pTabWidget->blockSignals(true);
+ /* Cache log page pointers and tab titles: */
+ QVector<QPair<UIVMLogTab*, QString> > logTabs;
+ for (int i = 0; i < m_pTabWidget->count(); ++i)
+ {
+ UIVMLogTab *pTab = logTab(i);
+ if (pTab)
+ logTabs << QPair<UIVMLogTab*, QString>(pTab, m_pTabWidget->tabText(i));
+ }
+ /* Remove all the tabs from tab widget, note that this does not delete tab widgets: */
+ m_pTabWidget->clear();
+ QVector<UIVMLogTab*> pagesToRemove;
+ /* Add tab widgets (log pages) back as long as machine id is not in machineList: */
+ for (int i = 0; i < logTabs.size(); ++i)
+ {
+ if (!logTabs[i].first)
+ continue;
+ const QUuid &id = logTabs[i].first->machineId();
+
+ if (machineList.contains(id))
+ pagesToRemove << logTabs[i].first;
+ else
+ m_pTabWidget->addTab(logTabs[i].first, logTabs[i].second);
+ }
+ /* Delete all the other pages: */
+ qDeleteAll(pagesToRemove.begin(), pagesToRemove.end());
+ m_pTabWidget->blockSignals(false);
+ labelTabHandler();
+ markLabelTabs();
+}
+
+void UIVMLogViewerWidget::removeAllLogPages()
+{
+ if (!m_pTabWidget)
+ return;
+
+ QVector<QWidget*> pagesToRemove;
+ for (int i = 0; i < m_pTabWidget->count(); ++i)
+ pagesToRemove << m_pTabWidget->widget(i);
+ m_pTabWidget->clear();
+ qDeleteAll(pagesToRemove.begin(), pagesToRemove.end());
+}
+
+void UIVMLogViewerWidget::resetHighlighthing()
+{
+ /* Undo the document changes to remove highlighting: */
+ UIVMLogPage* logPage = currentLogPage();
+ if (!logPage)
+ return;
+ logPage->documentUndo();
+ logPage->clearScrollBarMarkingsVector();
+}
+
+void UIVMLogViewerWidget::hidePanel(UIDialogPanel* panel)
+{
+ if (!panel || !m_pActionPool)
+ return;
+ if (panel->isVisible())
+ panel->setVisible(false);
+ QMap<UIDialogPanel*, QAction*>::iterator iterator = m_panelActionMap.find(panel);
+ if (iterator != m_panelActionMap.end())
+ {
+ if (iterator.value() && iterator.value()->isChecked())
+ iterator.value()->setChecked(false);
+ }
+ m_visiblePanelsList.removeOne(panel);
+ manageEscapeShortCut();
+ savePanelVisibility();
+}
+
+void UIVMLogViewerWidget::showPanel(UIDialogPanel* panel)
+{
+ if (panel && panel->isHidden())
+ panel->setVisible(true);
+ QMap<UIDialogPanel*, QAction*>::iterator iterator = m_panelActionMap.find(panel);
+ if (iterator != m_panelActionMap.end())
+ {
+ if (!iterator.value()->isChecked())
+ iterator.value()->setChecked(true);
+ }
+ if (!m_visiblePanelsList.contains(panel))
+ m_visiblePanelsList.push_back(panel);
+ manageEscapeShortCut();
+ savePanelVisibility();
+}
+
+void UIVMLogViewerWidget::manageEscapeShortCut()
+{
+ /* if there is no visible panels give the escape shortcut to parent dialog: */
+ if (m_visiblePanelsList.isEmpty())
+ {
+ emit sigSetCloseButtonShortCut(QKeySequence(Qt::Key_Escape));
+ return;
+ }
+ /* Take the escape shortcut from the dialog: */
+ emit sigSetCloseButtonShortCut(QKeySequence());
+ /* Just loop thru the visible panel list and set the esc key to the
+ panel which made visible latest */
+ for (int i = 0; i < m_visiblePanelsList.size() - 1; ++i)
+ {
+ m_visiblePanelsList[i]->setCloseButtonShortCut(QKeySequence());
+ }
+ m_visiblePanelsList.back()->setCloseButtonShortCut(QKeySequence(Qt::Key_Escape));
+}
+
+bool UIVMLogViewerWidget::labelTabHandler()
+{
+ if (!m_pTabWidget || !qobject_cast<UILabelTab*>(m_pTabWidget->currentWidget()))
+ return false;
+ if (m_pTabWidget->currentIndex() < m_pTabWidget->count() - 1)
+ m_pTabWidget->setCurrentIndex(m_pTabWidget->currentIndex() + 1);
+ return true;
+}
+
+#include "UIVMLogViewerWidget.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerWidget.h b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerWidget.h
new file mode 100644
index 00000000..0385ca6c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerWidget.h
@@ -0,0 +1,272 @@
+/* $Id: UIVMLogViewerWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIVMLogViewerWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerWidget_h
+#define FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QKeySequence>
+#include <QPair>
+#include <QPointer>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+
+/* Forward declarations: */
+class QITabWidget;
+class UITabWidget;
+class QPlainTextEdit;
+class QVBoxLayout;
+class UIActionPool;
+class UIDialogPanel;
+class QIToolBar;
+class QIToolButton;
+class UIMachineListMenu;
+class UIVirtualMachineItem;
+class UIVMLogPage;
+class UIVMLogTab;
+class UIVMLogViewerBookmarksPanel;
+class UIVMLogViewerFilterPanel;
+class UIVMLogViewerPanel;
+class UIVMLogViewerSearchPanel;
+class UIVMLogViewerOptionsPanel;
+
+/** QWidget extension providing GUI for VirtualBox LogViewer. It
+ * encapsulates log pages, toolbar, a tab widget and manages
+ * interaction between these classes. */
+class SHARED_LIBRARY_STUFF UIVMLogViewerWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigSetCloseButtonShortCut(QKeySequence);
+
+public:
+
+ /** Constructs the VM Log-Viewer by passing @a pParent to QWidget base-class constructor.
+ * @param enmEmbedding Brings the type of widget embedding.
+ * @param pActionPool Brings the action-pool reference.
+ * @param fShowToolbar Brings whether we should create/show toolbar.
+ * @param uMachineId Brings the machine id for which VM Log-Viewer is requested. */
+ UIVMLogViewerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar = true, const QUuid &uMachineId = QUuid(), QWidget *pParent = 0);
+ ~UIVMLogViewerWidget();
+ /** Returns the width of the current log page. return 0 if there is no current log page: */
+ int defaultLogPageWidth() const;
+
+ /** Returns the menu. */
+ QMenu *menu() const;
+
+#ifdef VBOX_WS_MAC
+ /** Returns the toolbar. */
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+ void setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items);
+ void addSelectedVMListItems(const QList<UIVirtualMachineItem*> &items);
+ QFont currentFont() const;
+
+protected:
+
+ /** Returns whether the window should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const;
+
+private slots:
+
+ /** Rereads the log file shown in the current tab. */
+ void sltRefresh();
+ /** Rereads all the log files . */
+ void sltReload();
+ /** Handles save action triggering. */
+ void sltSave();
+
+ /** @name Bookmark related slots
+ * @{ */
+ /** Deletes the bookmark with @p index from the current logs bookmark list. */
+ void sltDeleteBookmarkByIndex(int index);
+ /** Receives delete all signal from the bookmark panel and notifies UIVMLogPage. */
+ void sltDeleteAllBookmarks();
+ /** Manages bookmark panel update when bookmark vector is updated. */
+ void sltUpdateBookmarkPanel();
+ /** Makes the current UIVMLogPage to goto (scroll) its bookmark with index @a index. */
+ void gotoBookmark(int bookmarkIndex);
+ /** @} */
+
+ void sltPanelActionToggled(bool fChecked);
+ /** Handles the search result highlight changes. */
+ void sltSearchResultHighLigting();
+ void sltHandleSearchUpdated();
+ /** Handles the tab change of the logviewer. */
+ void sltCurrentTabChanged(int tabIndex);
+ void sltFilterApplied();
+ /* Handles the UIVMLogPage signal which is emitted when isFiltered property
+ of UIVMLogPage is changed. */
+ void sltLogPageFilteredChanged(bool isFiltered);
+ void sltHandleHidePanel(UIDialogPanel *pPanel);
+ void sltHandleShowPanel(UIDialogPanel *pPanel);
+
+ /** @name Slots to handle signals from settings panel
+ * @{ */
+ void sltShowLineNumbers(bool bShowLineNumbers);
+ void sltWrapLines(bool bWrapLine);
+ void sltFontSizeChanged(int fontSize);
+ void sltChangeFont(QFont font);
+ void sltResetOptionsToDefault();
+ /** @} */
+ void sltCloseMachineLogs();
+ void sltTabCloseButtonClick();
+ void sltCommitDataSignalReceived();
+
+private:
+
+ /** @name Prepare/Cleanup
+ * @{ */
+ /** Prepares VM Log-Viewer. */
+ void prepare();
+ /** Prepares actions. */
+ void prepareActions();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares toolbar. */
+ void prepareToolBar();
+ void saveOptions();
+ /** Loads options. */
+ void loadOptions();
+ void savePanelVisibility();
+ /** Shows the panels that have been visible the last time logviewer is closed. */
+ void restorePanelVisibility();
+ /** @} */
+
+ /** @name Event handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles Qt show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles Qt key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+ /** Returns the newly created log-page using @a strPage filename. */
+ void createLogPage(const QString &strFileName,
+ const QString &strMachineName,
+ const QUuid &machineId, int iLogFileId,
+ const QString &strLogContent, bool noLogsToShow);
+
+ const UIVMLogPage *currentLogPage() const;
+ UIVMLogPage *currentLogPage();
+ /** Returns the log tab at tab with iIndex if it contains a log page. Return 0 otherwise. */
+ UIVMLogTab *logTab(int iIndex);
+ UIVMLogPage *logPage(int iIndex);
+ /** Returns a vector of all the log pages of the tab widget. */
+ QVector<UIVMLogTab*> logTabs();
+
+ void createLogViewerPages(const QVector<QUuid> &machineList);
+ /** Removes the log pages/tabs that shows logs of the machines from @p machineList. */
+ void removeLogViewerPages(const QVector<QUuid> &machineList);
+ void removeAllLogPages();
+ void markLabelTabs();
+
+ /** Resets document (of the current tab) and scrollbar highligthing */
+ void resetHighlighthing();
+ void hidePanel(UIDialogPanel* panel);
+ void showPanel(UIDialogPanel* panel);
+ /** Make sure escape key is assigned to only a single widget. This is done by checking
+ several things in the following order:
+ - when there are no more panels visible assign it to the parent dialog
+ - grab it from the dialog as soon as a panel becomes visible again
+ - assigned it to the most recently "unhidden" panel */
+ void manageEscapeShortCut();
+ void setMachines(const QVector<QUuid> &machineIDs);
+ /** Returns the content of the ith log file of @comMachine or possibly an empty string */
+ QString readLogFile(CMachine &comMachine, int iLogFileId);
+ /** If the current tab is a label tab then switch to the next tab and return true. Returns false otherwise. */
+ bool labelTabHandler();
+
+ /** Holds the widget's embedding type. */
+ const EmbedTo m_enmEmbedding;
+ /** Holds the action-pool reference. Wrapped around with QPointer to avoid use-after-delete case during vm window close.*/
+ QPointer<UIActionPool> m_pActionPool;
+ /** Holds whether we should create/show toolbar. */
+ const bool m_fShowToolbar;
+ QVector<QUuid> m_machines;
+
+ /** Holds whether the dialog is polished. */
+ bool m_fIsPolished;
+
+ /** Holds container for log-pages. */
+ UITabWidget *m_pTabWidget;
+
+ /** @name Panel instances and a QMap for mapping panel instances to related actions.
+ * @{ */
+ UIVMLogViewerSearchPanel *m_pSearchPanel;
+ UIVMLogViewerFilterPanel *m_pFilterPanel;
+ UIVMLogViewerBookmarksPanel *m_pBookmarksPanel;
+ UIVMLogViewerOptionsPanel *m_pOptionsPanel;
+ QMap<UIDialogPanel*, QAction*> m_panelActionMap;
+ QList<UIDialogPanel*> m_visiblePanelsList;
+ /** @} */
+ QVBoxLayout *m_pMainLayout;
+
+ /** @name Toolbar and menu variables.
+ * @{ */
+ QIToolBar *m_pToolBar;
+ /** @} */
+
+ /** @name Toolbar and menu variables. Cache these to restore them after refresh.
+ * @{ */
+ /** Showing/hiding line numbers and line wraping options are set per
+ UIVMLogViewerWidget and applies to all log pages (all tabs) */
+ bool m_bShowLineNumbers;
+ bool m_bWrapLines;
+ QFont m_font;
+ /** @} */
+ QIToolButton *m_pCornerButton;
+ UIMachineListMenu *m_pMachineSelectionMenu;
+ /** All extra data saves are done dynamically (as an option changes etc.). The this flag is true
+ * we should not try to save anything to extra data anymore. */
+ bool m_fCommitDataSignalReceived;
+ QPointer<UIVMLogPage> m_pPreviousLogPage;
+
+ friend class UIVMLogViewerFilterPanel;
+ friend class UIVMLogViewerPanel;
+ friend class UIVMLogViewerDialog;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_logviewer_UIVMLogViewerWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/main.cpp b/src/VBox/Frontends/VirtualBox/src/main.cpp
new file mode 100644
index 00000000..02b1de55
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/main.cpp
@@ -0,0 +1,878 @@
+/* $Id: main.cpp $ */
+/** @file
+ * VBox Qt GUI - The main() function.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QMessageBox>
+#ifdef VBOX_WS_X11
+# ifndef Q_OS_SOLARIS
+# include <QFontDatabase>
+# endif
+#endif
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIStarter.h"
+#include "UIModalWindowManager.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils.h"
+# include "UICocoaApplication.h"
+#endif /* VBOX_WS_MAC */
+
+/* Other VBox includes: */
+#include <iprt/buildconfig.h>
+#include <iprt/stream.h>
+#include <VBox/err.h>
+#include <VBox/version.h>
+#include <VBox/sup.h>
+#if !defined(VBOX_WITH_HARDENING) || !defined(VBOX_RUNTIME_UI)
+# include <iprt/initterm.h>
+# ifdef VBOX_WS_MAC
+# include <iprt/asm.h>
+# endif
+#endif
+#ifdef VBOX_WS_X11
+# include <iprt/env.h>
+#endif
+#ifdef VBOX_WITH_HARDENING
+# include <iprt/ctype.h>
+#endif
+#if defined(VBOX_RUNTIME_UI) && defined(VBOX_WS_MAC)
+# include <iprt/path.h>
+#endif
+
+/* Other includes: */
+#ifdef VBOX_WS_MAC
+# include <dlfcn.h>
+# include <sys/mman.h>
+#endif /* VBOX_WS_MAC */
+#ifdef VBOX_WS_X11
+# include <dlfcn.h>
+# include <unistd.h>
+# include <X11/Xlib.h>
+# if defined(RT_OS_LINUX) && defined(DEBUG)
+# include <signal.h>
+# include <execinfo.h>
+# ifndef __USE_GNU
+# define __USE_GNU
+# endif /* !__USE_GNU */
+# include <ucontext.h>
+# ifdef RT_ARCH_AMD64
+# define REG_PC REG_RIP
+# else /* !RT_ARCH_AMD64 */
+# define REG_PC REG_EIP
+# endif /* !RT_ARCH_AMD64 */
+# endif /* RT_OS_LINUX && DEBUG */
+#endif /* VBOX_WS_X11 */
+
+
+/* XXX Temporarily. Don't rely on the user to hack the Makefile himself! */
+QString g_QStrHintLinuxNoMemory = QApplication::tr(
+ "This error means that the kernel driver was either not able to "
+ "allocate enough memory or that some mapping operation failed."
+ );
+
+QString g_QStrHintLinuxNoDriver = QApplication::tr(
+ "The VirtualBox Linux kernel driver is either not loaded or not set "
+ "up correctly. Please try setting it up again by executing<br/><br/>"
+ " <font color=blue>'/sbin/vboxconfig'</font><br/><br/>"
+ "as root.<br/><br/>"
+ "If your system has EFI Secure Boot enabled you may also need to sign "
+ "the kernel modules (vboxdrv, vboxnetflt, vboxnetadp, vboxpci) before "
+ "you can load them. Please see your Linux system's documentation for "
+ "more information."
+ );
+
+QString g_QStrHintOtherWrongDriverVersion = QApplication::tr(
+ "The VirtualBox kernel modules do not match this version of "
+ "VirtualBox. The installation of VirtualBox was apparently not "
+ "successful. Please try completely uninstalling and reinstalling "
+ "VirtualBox."
+ );
+
+QString g_QStrHintLinuxWrongDriverVersion = QApplication::tr(
+ "The VirtualBox kernel modules do not match this version of "
+ "VirtualBox. The installation of VirtualBox was apparently not "
+ "successful. Executing<br/><br/>"
+ " <font color=blue>'/sbin/vboxconfig'</font><br/><br/>"
+ "may correct this. Make sure that you are not mixing builds "
+ "of VirtualBox from different sources."
+ );
+
+QString g_QStrHintOtherNoDriver = QApplication::tr(
+ "Make sure the kernel module has been loaded successfully."
+ );
+
+/* I hope this isn't (C), (TM) or (R) Microsoft support ;-) */
+QString g_QStrHintReinstall = QApplication::tr(
+ "Please try reinstalling VirtualBox."
+ );
+
+
+#ifdef VBOX_WS_X11
+/** X11: For versions of Xlib which are aware of multi-threaded environments this function
+ * calls for XInitThreads() which initializes Xlib support for concurrent threads.
+ * @returns @c non-zero unless it is unsafe to make multi-threaded calls to Xlib.
+ * @remarks This is a workaround for a bug on old Xlib versions, fixed in commit
+ * 941f02e and released in Xlib version 1.1. We check for the symbol
+ * "xcb_connect" which was introduced in that version. */
+static Status MakeSureMultiThreadingIsSafe()
+{
+ /* Success by default: */
+ Status rc = 1;
+ /* Get a global handle to process symbols: */
+ void *pvProcess = dlopen(0, RTLD_GLOBAL | RTLD_LAZY);
+ /* Initialize multi-thread environment only if we can obtain
+ * an address of xcb_connect symbol in this process: */
+ if (pvProcess && dlsym(pvProcess, "xcb_connect"))
+ rc = XInitThreads();
+ /* Close the handle: */
+ if (pvProcess)
+ dlclose(pvProcess);
+ /* Return result: */
+ return rc;
+}
+
+# if defined(RT_OS_LINUX) && defined(DEBUG)
+/** X11, Linux, Debug: The signal handler that prints out a backtrace of the call stack.
+ * @remarks The code is taken from http://www.linuxjournal.com/article/6391. */
+static void BackTraceSignalHandler(int sig, siginfo_t *pInfo, void *pSecret)
+{
+ void *trace[16];
+ char **messages = (char **)0;
+ int i, iTraceSize = 0;
+ ucontext_t *uc = (ucontext_t *)pSecret;
+
+ /* Do something useful with siginfo_t: */
+ if (sig == SIGSEGV)
+ Log(("GUI: Got signal %d, faulty address is %p, from %p\n",
+ sig, pInfo->si_addr, uc->uc_mcontext.gregs[REG_PC]));
+ /* Or do nothing by default: */
+ else
+ Log(("GUI: Got signal %d\n", sig));
+
+ /* Acquire backtrace of 16 lvls depth: */
+ iTraceSize = backtrace(trace, 16);
+
+ /* Overwrite sigaction with caller's address: */
+ trace[1] = (void *)uc->uc_mcontext.gregs[REG_PC];
+
+ /* Translate the addresses into an array of messages: */
+ messages = backtrace_symbols(trace, iTraceSize);
+
+ /* Skip first stack frame (points here): */
+ Log(("GUI: [bt] Execution path:\n"));
+ for (i = 1; i < iTraceSize; ++i)
+ Log(("GUI: [bt] %s\n", messages[i]));
+
+ exit(0);
+}
+
+/** X11, Linux, Debug: Installs signal handler printing backtrace of the call stack. */
+static void InstallSignalHandler()
+{
+ struct sigaction sa;
+ sa.sa_sigaction = BackTraceSignalHandler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ sigaction(SIGSEGV, &sa, 0);
+ sigaction(SIGBUS, &sa, 0);
+ sigaction(SIGUSR1, &sa, 0);
+}
+# endif /* RT_OS_LINUX && DEBUG */
+#endif /* VBOX_WS_X11 */
+
+/** Qt5 message handler, function that prints out
+ * debug, warning, critical, fatal and system error messages.
+ * @param type Holds the type of the message.
+ * @param context Holds the message context.
+ * @param strMessage Holds the message body. */
+static void QtMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &strMessage)
+{
+ NOREF(context);
+# ifndef VBOX_WS_X11
+ NOREF(strMessage);
+# endif
+ switch (type)
+ {
+ case QtDebugMsg:
+ Log(("Qt DEBUG: %s\n", strMessage.toUtf8().constData()));
+ break;
+ case QtWarningMsg:
+ Log(("Qt WARNING: %s\n", strMessage.toUtf8().constData()));
+# ifdef VBOX_WS_X11
+ /* Needed for instance for the message ``cannot connect to X server'': */
+ RTStrmPrintf(g_pStdErr, "Qt WARNING: %s\n", strMessage.toUtf8().constData());
+# endif
+ break;
+ case QtCriticalMsg:
+ Log(("Qt CRITICAL: %s\n", strMessage.toUtf8().constData()));
+# ifdef VBOX_WS_X11
+ /* Needed for instance for the message ``cannot connect to X server'': */
+ RTStrmPrintf(g_pStdErr, "Qt CRITICAL: %s\n", strMessage.toUtf8().constData());
+# endif
+ break;
+ case QtFatalMsg:
+ Log(("Qt FATAL: %s\n", strMessage.toUtf8().constData()));
+# ifdef VBOX_WS_X11
+ /* Needed for instance for the message ``cannot connect to X server'': */
+ RTStrmPrintf(g_pStdErr, "Qt FATAL: %s\n", strMessage.toUtf8().constData());
+# endif
+# if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
+ case QtInfoMsg:
+ /** @todo ignore? */
+ break;
+# endif
+ }
+}
+
+/** Shows all available command line parameters. */
+static void ShowHelp()
+{
+#ifndef VBOX_RUNTIME_UI
+ static const char s_szTitle[] = VBOX_PRODUCT " VM Selector";
+#else
+ static const char s_szTitle[] = VBOX_PRODUCT " VM Runner";
+#endif
+ static const char s_szUsage[] =
+#ifdef VBOX_RUNTIME_UI
+ "Options:\n"
+ " --startvm <vmname|UUID> start a VM by specifying its UUID or name\n"
+ " --separate start a separate VM process\n"
+ " --normal keep normal (windowed) mode during startup\n"
+ " --fullscreen switch to fullscreen mode during startup\n"
+ " --seamless switch to seamless mode during startup\n"
+ " --scale switch to scale mode during startup\n"
+ " --no-startvm-errormsgbox do not show a message box for VM start errors\n"
+ " --restore-current restore the current snapshot before starting\n"
+ " --no-aggressive-caching delays caching media info in VM processes\n"
+ " --fda <image|none> Mount the specified floppy image\n"
+ " --dvd <image|none> Mount the specified DVD image\n"
+# ifdef VBOX_GUI_WITH_PIDFILE
+ " --pidfile <file> create a pidfile file when a VM is up and running\n"
+# endif /* VBOX_GUI_WITH_PIDFILE */
+# ifdef VBOX_WITH_DEBUGGER_GUI
+ " --dbg enable the GUI debug menu\n"
+ " --debug like --dbg and show debug windows at VM startup\n"
+ " --debug-command-line like --dbg and show command line window at VM startup\n"
+ " --debug-statistics like --dbg and show statistics window at VM startup\n"
+ " --statistics-expand <pat> expand the matching statistics (can be repeated)\n"
+ " --statistics-filter <pat> statistics filter\n"
+ " --no-debug disable the GUI debug menu and debug windows\n"
+ " --start-paused start the VM in the paused state\n"
+ " --start-running start the VM running (for overriding --debug*)\n"
+# endif /* VBOX_WITH_DEBUGGER_GUI */
+ "\n"
+ "Expert options:\n"
+ " --execute-all-in-iem For debugging the interpreted execution mode.\n"
+ " --driverless Do not open the support driver (NEM or IEM mode).\n"
+ " --warp-pct <pct> time warp factor, 100%% (= 1.0) = normal speed\n"
+ "\n"
+# ifdef VBOX_WITH_DEBUGGER_GUI
+ "The following environment (and extra data) variables are evaluated:\n"
+ " VBOX_GUI_DBG_ENABLED (GUI/Dbg/Enabled)\n"
+ " enable the GUI debug menu if set\n"
+ " VBOX_GUI_DBG_AUTO_SHOW (GUI/Dbg/AutoShow)\n"
+ " show debug windows at VM startup\n"
+ " VBOX_GUI_NO_DEBUGGER\n"
+ " disable the GUI debug menu and debug windows\n"
+# endif /* VBOX_WITH_DEBUGGER_GUI */
+#else
+ "No special options.\n"
+ "\n"
+ "If you are looking for --startvm and related options, you need to use VirtualBoxVM.\n"
+#endif
+ ;
+
+ RTPrintf("%s v%s\n"
+ "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
+ "\n"
+ "%s",
+ s_szTitle, RTBldCfgVersion(), s_szUsage);
+
+#ifdef RT_OS_WINDOWS
+ /* Show message box. Modify the option list a little
+ * to better make it fit in the upcoming dialog. */
+ char szTitleWithVersion[sizeof(s_szTitle) + 128];
+ char szMsg[sizeof(s_szUsage) + 128];
+ char *pszDst = szMsg;
+ size_t offSrc = 0;
+ while (offSrc < sizeof(s_szUsage) - 1U)
+ {
+ char ch;
+ if ( s_szUsage[offSrc] == ' '
+ && s_szUsage[offSrc + 1] == ' '
+ && ( (ch = s_szUsage[offSrc + 2]) == '-' /* option line */
+ || ch == 'V' /* env.var. line */))
+ {
+ /* Split option lines: */
+ if (ch == '-')
+ {
+ offSrc += 2;
+ size_t cchOption = 0;
+ while ( s_szUsage[offSrc + cchOption] != ' '
+ || s_szUsage[offSrc + cchOption + 1] != ' ')
+ ++cchOption;
+
+ memcpy(pszDst, &s_szUsage[offSrc], cchOption);
+ offSrc += cchOption + 2;
+ pszDst += cchOption;
+ }
+ /* Change environment variable indentation: */
+ else
+ {
+ offSrc += 2;
+ size_t cchLine = 0;
+ while ((ch = s_szUsage[offSrc + cchLine]) != '\n' && ch != '\0')
+ ++cchLine;
+
+ memcpy(pszDst, &s_szUsage[offSrc], cchLine);
+ offSrc += cchLine + 1;
+ pszDst += cchLine;
+ }
+ *pszDst++ = '\n';
+ *pszDst++ = '\t';
+
+ while (s_szUsage[offSrc] == ' ')
+ ++offSrc;
+ }
+
+ /* Copy up to and including end of line: */
+ while ((ch = s_szUsage[offSrc++]) != '\n' && ch != '\0')
+ *pszDst++ = ch;
+ *pszDst++ = ch;
+ }
+ *pszDst = '\0';
+
+ RTStrPrintf(szTitleWithVersion, sizeof(szTitleWithVersion), "%s v%s - Command Line Options", s_szTitle, RTBldCfgVersion());
+ MessageBoxExA(NULL /*hwndOwner*/, szMsg, szTitleWithVersion, MB_OK | MB_ICONINFORMATION,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
+#endif
+}
+
+extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char ** /*envp*/)
+{
+#ifdef RT_OS_WINDOWS
+ ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
+#endif
+
+ /* Failed result initially: */
+ int iResultCode = 1;
+
+ /* Start logging: */
+ LogFlowFuncEnter();
+
+ /* Simulate try-catch block: */
+ do
+ {
+#ifdef VBOX_WS_X11
+ /* Make sure multi-threaded environment is safe: */
+ if (!MakeSureMultiThreadingIsSafe())
+ break;
+ /* Force using Qt platform module 'xcb', we have X11 specific code: */
+ RTEnvSet("QT_QPA_PLATFORM", "xcb");
+#endif /* VBOX_WS_X11 */
+
+ /* Console help preprocessing: */
+ bool fHelpShown = false;
+ for (int i = 0; i < argc; ++i)
+ {
+ if ( !strcmp(argv[i], "-h")
+ || !strcmp(argv[i], "-?")
+ || !strcmp(argv[i], "-help")
+ || !strcmp(argv[i], "--help"))
+ {
+ fHelpShown = true;
+ ShowHelp();
+ break;
+ }
+ }
+ if (fHelpShown)
+ {
+ iResultCode = 0;
+ break;
+ }
+
+#ifdef VBOX_WITH_HARDENING
+ /* Make sure the image verification code works: */
+ SUPR3HardenedVerifyInit();
+#endif /* VBOX_WITH_HARDENING */
+
+#ifdef VBOX_WS_MAC
+ /* Instantiate own NSApplication before QApplication do it for us: */
+ UICocoaApplication::instance();
+
+# ifdef VBOX_RUNTIME_UI
+ /* If we're a helper app inside Resources in the main application bundle,
+ we need to amend the library path so the platform plugin can be found.
+ Note! This builds on the initIprtForDarwinHelperApp() hack. */
+ {
+ char szExecDir[RTPATH_MAX];
+ int vrc = RTPathExecDir(szExecDir, sizeof(szExecDir));
+ AssertRC(vrc);
+ RTPathStripTrailingSlash(szExecDir); /* .../Contents/MacOS */
+ RTPathStripFilename(szExecDir); /* .../Contents */
+ RTPathAppend(szExecDir, sizeof(szExecDir), "plugins"); /* .../Contents/plugins */
+ QCoreApplication::addLibraryPath(QString::fromUtf8(szExecDir));
+ }
+# endif /* VBOX_RUNTIME_UI */
+#endif /* VBOX_WS_MAC */
+
+#ifdef VBOX_WS_X11
+# if defined(RT_OS_LINUX) && defined(DEBUG)
+ /* Install signal handler to backtrace the call stack: */
+ InstallSignalHandler();
+# endif /* RT_OS_LINUX && DEBUG */
+#endif /* VBOX_WS_X11 */
+
+ /* Install Qt console message handler: */
+ qInstallMessageHandler(QtMessageOutput);
+
+ /* Enable HiDPI support: */
+ QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
+#if (!defined(DEBUG_bird) || defined(RT_OS_DARWIN))
+# ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ /* This shouldn't be enabled for customer WM, since Qt has conflicts in that case. */
+ QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+# endif
+#endif
+
+ /* Create application: */
+ QApplication a(argc, argv);
+
+#ifdef VBOX_WS_WIN
+ /* Drag in the sound drivers and DLLs early to get rid of the delay taking
+ * place when the main menu bar (or any action from that menu bar) is
+ * activated for the first time. This delay is especially annoying if it
+ * happens when the VM is executing in real mode (which gives 100% CPU
+ * load and slows down the load process that happens on the main GUI
+ * thread to several seconds). */
+ PlaySound(NULL, NULL, 0);
+
+#endif /* VBOX_WS_WIN */
+
+#ifdef VBOX_WS_MAC
+ /* Disable menu icons on MacOS X host: */
+ ::darwinDisableIconsInMenus();
+
+#endif /* VBOX_WS_MAC */
+
+#ifdef VBOX_WS_X11
+ /* Make all widget native.
+ * We did it to avoid various Qt crashes while testing widget attributes or acquiring winIds.
+ * Yes, we aware of note that alien widgets faster to draw but the only widget we need to be fast
+ * is viewport of VM which was always native since we are using his id for 3D service needs. */
+ a.setAttribute(Qt::AA_NativeWindows);
+
+# ifdef Q_OS_SOLARIS
+ a.setStyle("fusion");
+# endif /* Q_OS_SOLARIS */
+
+# ifndef Q_OS_SOLARIS
+ /* Apply font fixes (after QApplication get created and instantiated font-family): */
+ QFontDatabase fontDataBase;
+ QString currentFamily(QApplication::font().family());
+ bool isCurrentScaleable = fontDataBase.isScalable(currentFamily);
+ QString subFamily(QFont::substitute(currentFamily));
+ bool isSubScaleable = fontDataBase.isScalable(subFamily);
+ if (isCurrentScaleable && !isSubScaleable)
+ QFont::removeSubstitutions(currentFamily);
+# endif /* !Q_OS_SOLARIS */
+
+ /* Qt version check (major.minor are sensitive, fix number is ignored): */
+ if (UICommon::qtRTVersion() < (UICommon::qtCTVersion() & 0xFFFF00))
+ {
+ QString strMsg = QApplication::tr("Executable <b>%1</b> requires Qt %2.x, found Qt %3.")
+ .arg(qAppName())
+ .arg(UICommon::qtCTVersionString().section('.', 0, 1))
+ .arg(UICommon::qtRTVersionString());
+ QMessageBox::critical(0, QApplication::tr("Incompatible Qt Library Error"),
+ strMsg, QMessageBox::Abort, 0);
+ qFatal("%s", strMsg.toUtf8().constData());
+ break;
+ }
+#endif /* VBOX_WS_X11 */
+
+ /* Create modal-window manager: */
+ UIModalWindowManager::create();
+
+ /* Create UI starter: */
+ UIStarter::create();
+#ifndef VBOX_RUNTIME_UI
+ /* Create global app instance for Selector UI: */
+ UICommon::create(UICommon::UIType_SelectorUI);
+#else
+ /* Create global app instance for Runtime UI: */
+ UICommon::create(UICommon::UIType_RuntimeUI);
+#endif
+
+ /* Simulate try-catch block: */
+ do
+ {
+ /* Exit if UICommon is not valid: */
+ if (!uiCommon().isValid())
+ break;
+
+ /* Init link between UI starter and global app instance: */
+ gStarter->init();
+
+ /* Exit if UICommon pre-processed arguments: */
+ if (uiCommon().processArgs())
+ break;
+
+ // WORKAROUND:
+ // Initially we wanted to make that workaround for Runtime UI only,
+ // because only there we had a strict handling for proper application quit
+ // procedure. But it appeared on X11 (as usually due to an async nature) there
+ // can happen situations that Qt application is checking whether at least one
+ // window is already shown and if not - exits prematurely _before_ it is actually
+ // shown. That can happen for example if window is not yet shown because blocked
+ // by startup error message-box which is not treated as real window by some
+ // reason. So we are making application exit manual everywhere.
+ qApp->setQuitOnLastWindowClosed(false);
+
+ /* Request to Start UI _after_ QApplication executed: */
+ QMetaObject::invokeMethod(gStarter, "sltStartUI", Qt::QueuedConnection);
+
+ /* Start application: */
+ iResultCode = a.exec();
+
+ /* Break link between UI starter and global app instance: */
+ gStarter->deinit();
+ }
+ while (0);
+
+ /* Destroy global app instance: */
+ UICommon::destroy();
+ /* Destroy UI starter: */
+ UIStarter::destroy();
+
+ /* Destroy modal-window manager: */
+ UIModalWindowManager::destroy();
+ }
+ while (0);
+
+ /* Finish logging: */
+ LogFlowFunc(("rc=%d\n", iResultCode));
+ LogFlowFuncLeave();
+
+ /* Return result: */
+ return iResultCode;
+}
+
+#if !defined(VBOX_WITH_HARDENING) || !defined(VBOX_RUNTIME_UI)
+
+# if defined(RT_OS_DARWIN) && defined(VBOX_RUNTIME_UI)
+
+extern "C" const char *_dyld_get_image_name(uint32_t);
+
+/** Init runtime with the executable path pointing into the
+ * VirtualBox.app/Contents/MacOS/ rather than
+ * VirtualBox.app/Contents/Resource/VirtualBoxVM.app/Contents/MacOS/.
+ *
+ * This is a HACK to make codesign and friends happy on OS X. The idea is to
+ * improve and eliminate this over time.
+ */
+DECL_NO_INLINE(static, int) initIprtForDarwinHelperApp(int cArgs, char ***ppapszArgs, uint32_t fFlags)
+{
+ const char *pszImageName = _dyld_get_image_name(0);
+ AssertReturn(pszImageName, VERR_INTERNAL_ERROR);
+
+ char szTmpPath[PATH_MAX + 1];
+ const char *psz = realpath(pszImageName, szTmpPath);
+ int rc;
+ if (psz)
+ {
+ char *pszFilename = RTPathFilename(szTmpPath);
+ if (pszFilename)
+ {
+ char const chSavedFilename0 = *pszFilename;
+ *pszFilename = '\0';
+ RTPathStripTrailingSlash(szTmpPath); /* VirtualBox.app/Contents/Resources/VirtualBoxVM.app/Contents/MacOS */
+ RTPathStripFilename(szTmpPath); /* VirtualBox.app/Contents/Resources/VirtualBoxVM.app/Contents/ */
+ RTPathStripFilename(szTmpPath); /* VirtualBox.app/Contents/Resources/VirtualBoxVM.app */
+ RTPathStripFilename(szTmpPath); /* VirtualBox.app/Contents/Resources */
+ RTPathStripFilename(szTmpPath); /* VirtualBox.app/Contents */
+ char *pszDst = strchr(szTmpPath, '\0');
+ pszDst = (char *)memcpy(pszDst, RT_STR_TUPLE("/MacOS/")) + sizeof("/MacOS/") - 1; /** @todo where is mempcpy? */
+ *pszFilename = chSavedFilename0;
+ memmove(pszDst, pszFilename, strlen(pszFilename) + 1);
+
+ return RTR3InitEx(RTR3INIT_VER_CUR, fFlags, cArgs, ppapszArgs, szTmpPath);
+ }
+ rc = VERR_INVALID_NAME;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszImageName, strlen(pszImageName), pszImageName), rc);
+ return rc;
+}
+# endif
+
+
+int main(int argc, char **argv, char **envp)
+{
+# ifdef VBOX_WS_X11
+ /* Make sure multi-threaded environment is safe: */
+ if (!MakeSureMultiThreadingIsSafe())
+ return 1;
+# endif /* VBOX_WS_X11 */
+
+ /*
+ * Determin the IPRT/SUPLib initialization flags if runtime UI process.
+ * Only initialize SUPLib if about to start a VM in this process.
+ *
+ * Note! This must must match the corresponding parsing in hardenedmain.cpp
+ * and UICommon.cpp exactly, otherwise there will be weird error messages.
+ */
+ /** @todo r=bird: We should consider just postponing this stuff till VM
+ * creation, it shouldn't make too much of a difference GIP-wise. */
+ uint32_t fFlags = 0;
+# ifdef VBOX_RUNTIME_UI
+ unsigned cOptionsLeft = 4;
+ bool fStartVM = false;
+ bool fSeparateProcess = false;
+ bool fExecuteAllInIem = false;
+ bool fDriverless = false;
+ for (int i = 1; i < argc && cOptionsLeft > 0; ++i)
+ {
+ if ( !strcmp(argv[i], "--startvm")
+ || !strcmp(argv[i], "-startvm"))
+ {
+ cOptionsLeft -= fStartVM == false;
+ fStartVM = true;
+ i++;
+ }
+ else if ( !strcmp(argv[i], "--separate")
+ || !strcmp(argv[i], "-separate"))
+ {
+ cOptionsLeft -= fSeparateProcess == false;
+ fSeparateProcess = true;
+ }
+ else if (!strcmp(argv[i], "--execute-all-in-iem"))
+ {
+ cOptionsLeft -= fExecuteAllInIem == false;
+ fExecuteAllInIem = true;
+ }
+ else if (!strcmp(argv[i], "--driverless"))
+ {
+ cOptionsLeft -= fDriverless == false;
+ fDriverless = true;
+ }
+ }
+ if (fStartVM && !fSeparateProcess)
+ {
+ fFlags |= RTR3INIT_FLAGS_TRY_SUPLIB;
+ if (fExecuteAllInIem)
+ fFlags |= SUPR3INIT_F_DRIVERLESS_IEM_ALLOWED << RTR3INIT_FLAGS_SUPLIB_SHIFT;
+ if (fDriverless)
+ fFlags |= SUPR3INIT_F_DRIVERLESS << RTR3INIT_FLAGS_SUPLIB_SHIFT;
+ }
+# endif
+
+ /* Initialize VBox Runtime: */
+# if defined(RT_OS_DARWIN) && defined(VBOX_RUNTIME_UI)
+ int rc = initIprtForDarwinHelperApp(argc, &argv, fFlags);
+# else
+ int rc = RTR3InitExe(argc, &argv, fFlags);
+# endif
+ if (RT_FAILURE(rc))
+ {
+ /* Initialization failed: */
+
+ /* We have to create QApplication anyway
+ * just to show the only one error-message: */
+ QApplication a(argc, &argv[0]);
+ Q_UNUSED(a);
+
+# ifdef Q_OS_SOLARIS
+ a.setStyle("fusion");
+# endif
+
+ /* Prepare the error-message: */
+ QString strTitle = QApplication::tr("VirtualBox - Runtime Error");
+ QString strText = "<html>";
+ switch (rc)
+ {
+ case VERR_VM_DRIVER_NOT_INSTALLED:
+ case VERR_VM_DRIVER_LOAD_ERROR:
+ strText += QApplication::tr("<b>Cannot access the kernel driver!</b><br/><br/>");
+# ifdef RT_OS_LINUX
+ strText += g_QStrHintLinuxNoDriver;
+# else
+ strText += g_QStrHintOtherNoDriver;
+# endif
+ break;
+# ifdef RT_OS_LINUX
+ case VERR_NO_MEMORY:
+ strText += g_QStrHintLinuxNoMemory;
+ break;
+# endif
+ case VERR_VM_DRIVER_NOT_ACCESSIBLE:
+ strText += QApplication::tr("Kernel driver not accessible");
+ break;
+ case VERR_VM_DRIVER_VERSION_MISMATCH:
+# ifdef RT_OS_LINUX
+ strText += g_QStrHintLinuxWrongDriverVersion;
+# else
+ strText += g_QStrHintOtherWrongDriverVersion;
+# endif
+ break;
+ default:
+ strText += QApplication::tr("Unknown error %2 during initialization of the Runtime").arg(rc);
+ break;
+ }
+ strText += "</html>";
+
+ /* Show the error-message: */
+ QMessageBox::critical(0 /* parent */, strTitle, strText,
+ QMessageBox::Abort /* 1st button */, 0 /* 2nd button */);
+
+ /* Default error-result: */
+ return 1;
+ }
+
+ /* Call to actual main function: */
+ return TrustedMain(argc, argv, envp);
+}
+
+#endif /* !VBOX_WITH_HARDENING || !VBOX_RUNTIME_UI */
+
+#ifdef VBOX_WITH_HARDENING
+
+/**
+ * Special entrypoint used by the hardening code when something goes south.
+ *
+ * Display an error dialog to the user.
+ *
+ * @param pszWhere Indicates where the error occured.
+ * @param enmWhat Indicates what init operation was going on at the time.
+ * @param rc The VBox status code corresponding to the error.
+ * @param pszMsgFmt The message format string.
+ * @param va Format arguments.
+ */
+extern "C" DECLEXPORT(void) TrustedError(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, va_list va)
+{
+ char szMsgBuf[_16K];
+
+ /*
+ * We have to create QApplication anyway just to show the only one error-message.
+ * This is a bit hackish as we don't have the argument vector handy.
+ */
+ int argc = 0;
+ char *argv[2] = { NULL, NULL };
+ QApplication a(argc, &argv[0]);
+
+ /*
+ * The details starts off a properly formatted rc and where/what, we use
+ * the szMsgBuf for this, thus this have to come before the actual message
+ * formatting.
+ */
+ RTStrPrintf(szMsgBuf, sizeof(szMsgBuf),
+ "<!--EOM-->"
+ "where: %s\n"
+ "what: %d\n"
+ "%Rra\n",
+ pszWhere, enmWhat, rc);
+ QString strDetails = szMsgBuf;
+
+ /*
+ * Format the error message. Take whatever comes after a double new line as
+ * something better off in the details section.
+ */
+ RTStrPrintfV(szMsgBuf, sizeof(szMsgBuf), pszMsgFmt, va);
+
+ char *pszDetails = strstr(szMsgBuf, "\n\n");
+ if (pszDetails)
+ {
+ while (RT_C_IS_SPACE(*pszDetails))
+ *pszDetails++ = '\0';
+ if (*pszDetails)
+ {
+ strDetails += "\n";
+ strDetails += pszDetails;
+ }
+ RTStrStripR(szMsgBuf);
+ }
+
+ QString strText = QApplication::tr("<html><b>%1 (rc=%2)</b><br/><br/>").arg(szMsgBuf).arg(rc);
+ strText.replace(QString("\n"), QString("<br>"));
+
+ /*
+ * Append possibly helpful hints to the error message.
+ */
+ switch (enmWhat)
+ {
+ case kSupInitOp_Driver:
+# ifdef RT_OS_LINUX
+ strText += g_QStrHintLinuxNoDriver;
+# else /* RT_OS_LINUX */
+ strText += g_QStrHintOtherNoDriver;
+# endif /* !RT_OS_LINUX */
+ break;
+ case kSupInitOp_IPRT:
+ case kSupInitOp_Misc:
+ if (rc == VERR_VM_DRIVER_VERSION_MISMATCH)
+# ifndef RT_OS_LINUX
+ strText += g_QStrHintOtherWrongDriverVersion;
+# else
+ strText += g_QStrHintLinuxWrongDriverVersion;
+ else if (rc == VERR_NO_MEMORY)
+ strText += g_QStrHintLinuxNoMemory;
+# endif
+ else
+ strText += g_QStrHintReinstall;
+ break;
+ case kSupInitOp_Integrity:
+ case kSupInitOp_RootCheck:
+ strText += g_QStrHintReinstall;
+ break;
+ default:
+ /* no hints here */
+ break;
+ }
+
+# ifdef VBOX_WS_X11
+ /* We have to to make sure that we display the error-message
+ * after the parent displayed its own message. */
+ sleep(2);
+# endif /* VBOX_WS_X11 */
+
+ /* Update strText with strDetails: */
+ if (!strDetails.isEmpty())
+ strText += QString("<br><br>%1").arg(strDetails);
+
+ /* Close the <html> scope: */
+ strText += "</html>";
+
+ /* Create and show the error message-box: */
+ QMessageBox::critical(0, QApplication::tr("VirtualBox - Error In %1").arg(pszWhere), strText);
+
+ qFatal("%s", strText.toUtf8().constData());
+}
+
+#endif /* VBOX_WITH_HARDENING */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/manager/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UICloudEntityKey.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UICloudEntityKey.cpp
new file mode 100644
index 00000000..476e527a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UICloudEntityKey.cpp
@@ -0,0 +1,75 @@
+/* $Id: UICloudEntityKey.cpp $ */
+/** @file
+ * VBox Qt GUI - UICloudEntityKey class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICloudEntityKey.h"
+
+
+UICloudEntityKey::UICloudEntityKey(const QString &strProviderShortName /* = QString() */,
+ const QString &strProfileName /* = QString() */,
+ const QUuid &uMachineId /* = QUuid() */)
+ : m_strProviderShortName(strProviderShortName)
+ , m_strProfileName(strProfileName)
+ , m_uMachineId(uMachineId)
+{
+}
+
+UICloudEntityKey::UICloudEntityKey(const UICloudEntityKey &another)
+ : m_strProviderShortName(another.m_strProviderShortName)
+ , m_strProfileName(another.m_strProfileName)
+ , m_uMachineId(another.m_uMachineId)
+{
+}
+
+bool UICloudEntityKey::operator==(const UICloudEntityKey &another) const
+{
+ return true
+ && toString() == another.toString()
+ ;
+}
+
+bool UICloudEntityKey::operator<(const UICloudEntityKey &another) const
+{
+ return true
+ && toString() < another.toString()
+ ;
+}
+
+QString UICloudEntityKey::toString() const
+{
+ QString strResult;
+ if (m_strProviderShortName.isEmpty())
+ return strResult;
+ strResult += QString("/%1").arg(m_strProviderShortName);
+ if (m_strProfileName.isEmpty())
+ return strResult;
+ strResult += QString("/%1").arg(m_strProfileName);
+ if (m_uMachineId.isNull())
+ return strResult;
+ strResult += QString("/%1").arg(m_uMachineId.toString());
+ return strResult;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UICloudEntityKey.h b/src/VBox/Frontends/VirtualBox/src/manager/UICloudEntityKey.h
new file mode 100644
index 00000000..e41c926e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UICloudEntityKey.h
@@ -0,0 +1,79 @@
+/* $Id: UICloudEntityKey.h $ */
+/** @file
+ * VBox Qt GUI - UICloudEntityKey class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UICloudEntityKey_h
+#define FEQT_INCLUDED_SRC_manager_UICloudEntityKey_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QHash>
+#include <QString>
+#include <QUuid>
+
+/** Cloud entity key definition.
+ * This is a key for various indexed containers,
+ * allowing to distinguish one cloud entity from another. */
+struct UICloudEntityKey
+{
+ /** Constructs cloud entity key on the basis of passed parameters.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name.
+ * @param uMachineId Brings machine id. */
+ UICloudEntityKey(const QString &strProviderShortName = QString(),
+ const QString &strProfileName = QString(),
+ const QUuid &uMachineId = QUuid());
+ /** Constructs cloud entity key on the basis of @a another one. */
+ UICloudEntityKey(const UICloudEntityKey &another);
+
+ /** Returns whether this one key equals to @a another one. */
+ bool operator==(const UICloudEntityKey &another) const;
+ /** Returns whether this one key is less than @a another one. */
+ bool operator<(const UICloudEntityKey &another) const;
+
+ /** Returns string key representation. */
+ QString toString() const;
+
+ /** Holds provider short name. */
+ QString m_strProviderShortName;
+ /** Holds profile name. */
+ QString m_strProfileName;
+ /** Holds machine id. */
+ QUuid m_uMachineId;
+};
+
+#ifdef VBOX_IS_QT6_OR_LATER /* uint replaced with size_t since 6.0 */
+inline size_t qHash(const UICloudEntityKey &key, size_t uSeed)
+#else
+inline uint qHash(const UICloudEntityKey &key, uint uSeed)
+#endif
+{
+ return qHash(key.toString(), uSeed);
+}
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UICloudEntityKey_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIErrorPane.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIErrorPane.cpp
new file mode 100644
index 00000000..76012538
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIErrorPane.cpp
@@ -0,0 +1,68 @@
+/* $Id: UIErrorPane.cpp $ */
+/** @file
+ * VBox Qt GUI - UIErrorPane class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QTextBrowser>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIErrorPane.h"
+
+
+UIErrorPane::UIErrorPane(QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_pBrowserDetails(0)
+{
+ prepare();
+}
+
+void UIErrorPane::setErrorDetails(const QString &strDetails)
+{
+ /* Redirect to details browser: */
+ m_pBrowserDetails->setText(strDetails);
+}
+
+void UIErrorPane::prepare()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare details browser: */
+ m_pBrowserDetails = new QTextBrowser;
+ if (m_pBrowserDetails)
+ {
+ m_pBrowserDetails->setFocusPolicy(Qt::StrongFocus);
+ m_pBrowserDetails->document()->setDefaultStyleSheet("a { text-decoration: none; }");
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pBrowserDetails);
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIErrorPane.h b/src/VBox/Frontends/VirtualBox/src/manager/UIErrorPane.h
new file mode 100644
index 00000000..17fe7ce8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIErrorPane.h
@@ -0,0 +1,63 @@
+/* $Id: UIErrorPane.h $ */
+/** @file
+ * VBox Qt GUI - UIErrorPane class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIErrorPane_h
+#define FEQT_INCLUDED_SRC_manager_UIErrorPane_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* Forward declarations: */
+class QTextBrowser;
+
+/** QWidget subclass representing error pane reflecting
+ * information about currently chosen inaccessible VM. */
+class UIErrorPane : public QWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs error pane passing @a pParent to the base-class. */
+ UIErrorPane(QWidget *pParent = 0);
+
+ /** Defines error @a strDetails. */
+ void setErrorDetails(const QString &strDetails);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the text-browser instance. */
+ QTextBrowser *m_pBrowserDetails;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIErrorPane_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIManagerDefs.h b/src/VBox/Frontends/VirtualBox/src/manager/UIManagerDefs.h
new file mode 100644
index 00000000..8281b872
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIManagerDefs.h
@@ -0,0 +1,54 @@
+/* $Id: UIManagerDefs.h $ */
+/** @file
+ * VBox Qt GUI - UIVirtualBoxManager definitions.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIManagerDefs_h
+#define FEQT_INCLUDED_SRC_manager_UIManagerDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/** Virtual machine item types. */
+enum UIVirtualMachineItemType
+{
+ UIVirtualMachineItemType_Invalid,
+ UIVirtualMachineItemType_Local,
+ UIVirtualMachineItemType_CloudFake,
+ UIVirtualMachineItemType_CloudReal
+};
+
+
+/** Fake cloud virtual machine item states. */
+enum UIFakeCloudVirtualMachineItemState
+{
+ UIFakeCloudVirtualMachineItemState_NotApplicable,
+ UIFakeCloudVirtualMachineItemState_Loading,
+ UIFakeCloudVirtualMachineItemState_Done
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIManagerDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIProgressTaskReadCloudMachineList.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIProgressTaskReadCloudMachineList.cpp
new file mode 100644
index 00000000..2e25c976
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIProgressTaskReadCloudMachineList.cpp
@@ -0,0 +1,100 @@
+/* $Id: UIProgressTaskReadCloudMachineList.cpp $ */
+/** @file
+ * VBox Qt GUI - UIProgressTaskReadCloudMachineList class implementation.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICloudNetworkingStuff.h"
+#include "UIErrorString.h"
+#include "UIProgressTaskReadCloudMachineList.h"
+
+
+UIProgressTaskReadCloudMachineList::UIProgressTaskReadCloudMachineList(QObject *pParent,
+ const UICloudEntityKey &guiCloudProfileKey,
+ bool fWithRefresh)
+ : UIProgressTask(pParent)
+ , m_guiCloudProfileKey(guiCloudProfileKey)
+ , m_fWithRefresh(fWithRefresh)
+{
+}
+
+UICloudEntityKey UIProgressTaskReadCloudMachineList::cloudProfileKey() const
+{
+ return m_guiCloudProfileKey;
+}
+
+QVector<CCloudMachine> UIProgressTaskReadCloudMachineList::machines() const
+{
+ return m_machines;
+}
+
+QString UIProgressTaskReadCloudMachineList::errorMessage() const
+{
+ return m_strErrorMessage;
+}
+
+CProgress UIProgressTaskReadCloudMachineList::createProgress()
+{
+ /* Create cloud client: */
+ m_comCloudClient = cloudClientByName(m_guiCloudProfileKey.m_strProviderShortName,
+ m_guiCloudProfileKey.m_strProfileName,
+ m_strErrorMessage);
+ if (m_comCloudClient.isNull())
+ return CProgress();
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_fWithRefresh
+ ? m_comCloudClient.ReadCloudMachineList()
+ : m_comCloudClient.ReadCloudMachineStubList();
+ if (!m_comCloudClient.isOk())
+ {
+ m_strErrorMessage = UIErrorString::formatErrorInfo(m_comCloudClient);
+ return CProgress();
+ }
+
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UIProgressTaskReadCloudMachineList::handleProgressFinished(CProgress &comProgress)
+{
+ /* Check if we already have error-mesage: */
+ if (!m_strErrorMessage.isEmpty())
+ return;
+
+ /* Handle progress-wrapper errors: */
+ if (comProgress.isNotNull() && !comProgress.GetCanceled() && (!comProgress.isOk() || comProgress.GetResultCode() != 0))
+ {
+ m_strErrorMessage = UIErrorString::formatErrorInfo(comProgress);
+ return;
+ }
+
+ /* Fill the result: */
+ m_machines = m_fWithRefresh
+ ? m_comCloudClient.GetCloudMachineList()
+ : m_comCloudClient.GetCloudMachineStubList();
+ if (!m_comCloudClient.isOk())
+ m_strErrorMessage = UIErrorString::formatErrorInfo(m_comCloudClient);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIProgressTaskReadCloudMachineList.h b/src/VBox/Frontends/VirtualBox/src/manager/UIProgressTaskReadCloudMachineList.h
new file mode 100644
index 00000000..45f4794e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIProgressTaskReadCloudMachineList.h
@@ -0,0 +1,87 @@
+/* $Id: UIProgressTaskReadCloudMachineList.h $ */
+/** @file
+ * VBox Qt GUI - UIProgressTaskReadCloudMachineList class declaration.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIProgressTaskReadCloudMachineList_h
+#define FEQT_INCLUDED_SRC_manager_UIProgressTaskReadCloudMachineList_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UICloudEntityKey.h"
+#include "UIProgressTask.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudClient.h"
+#include "CCloudMachine.h"
+
+/** UIProgressTask extension performing read cloud machine list task. */
+class UIProgressTaskReadCloudMachineList : public UIProgressTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs read cloud machine list task passing @a pParent to the base-class.
+ * @param guiCloudProfileKey Brings cloud profile description key.
+ * @param fWithRefresh Brings whether cloud machine should be refreshed as well. */
+ UIProgressTaskReadCloudMachineList(QObject *pParent, const UICloudEntityKey &guiCloudProfileKey, bool fWithRefresh);
+
+ /** Returns cloud profile description key. */
+ UICloudEntityKey cloudProfileKey() const;
+
+ /** Returns resulting cloud machine-wrapper vector. */
+ QVector<CCloudMachine> machines() const;
+
+ /** Returns error message. */
+ QString errorMessage() const;
+
+protected:
+
+ /** Creates and returns started progress-wrapper required to init UIProgressObject. */
+ virtual CProgress createProgress() RT_OVERRIDE;
+ /** Handles finished @a comProgress wrapper. */
+ virtual void handleProgressFinished(CProgress &comProgress) RT_OVERRIDE;
+
+private:
+
+ /** Holds the cloud profile description key. */
+ UICloudEntityKey m_guiCloudProfileKey;
+ /** Holds whether cloud machine should be refreshed as well. */
+ bool m_fWithRefresh;
+
+ /** Holds the cloud client-wrapper. */
+ CCloudClient m_comCloudClient;
+ /** Holds the resulting cloud machine-wrapper vector. */
+ QVector<CCloudMachine> m_machines;
+
+ /** Holds the error message. */
+ QString m_strErrorMessage;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIProgressTaskReadCloudMachineList_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UITaskCloudGetSettingsForm.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UITaskCloudGetSettingsForm.cpp
new file mode 100644
index 00000000..cabed7ad
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UITaskCloudGetSettingsForm.cpp
@@ -0,0 +1,103 @@
+/* $Id: UITaskCloudGetSettingsForm.cpp $ */
+/** @file
+ * VBox Qt GUI - UITaskCloudGetSettingsForm class implementation.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UICloudNetworkingStuff.h"
+#include "UINotificationCenter.h"
+#include "UITaskCloudGetSettingsForm.h"
+#include "UIThreadPool.h"
+
+
+/*********************************************************************************************************************************
+* Class UITaskCloudGetSettingsForm implementation. *
+*********************************************************************************************************************************/
+
+UITaskCloudGetSettingsForm::UITaskCloudGetSettingsForm(const CCloudMachine &comCloudMachine)
+ : UITask(Type_CloudGetSettingsForm)
+ , m_comCloudMachine(comCloudMachine)
+{
+}
+
+CForm UITaskCloudGetSettingsForm::result() const
+{
+ m_mutex.lock();
+ const CForm comResult = m_comResult;
+ m_mutex.unlock();
+ return comResult;
+}
+
+QString UITaskCloudGetSettingsForm::errorInfo() const
+{
+ m_mutex.lock();
+ QString strErrorInfo = m_strErrorInfo;
+ m_mutex.unlock();
+ return strErrorInfo;
+}
+
+void UITaskCloudGetSettingsForm::run()
+{
+ m_mutex.lock();
+ cloudMachineSettingsForm(m_comCloudMachine, m_comResult, m_strErrorInfo);
+ m_mutex.unlock();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIReceiverCloudGetSettingsForm implementation. *
+*********************************************************************************************************************************/
+
+UIReceiverCloudGetSettingsForm::UIReceiverCloudGetSettingsForm(QWidget *pParent)
+ : QObject(pParent)
+ , m_pParent(pParent)
+{
+ /* Connect receiver: */
+ connect(uiCommon().threadPoolCloud(), &UIThreadPool::sigTaskComplete,
+ this, &UIReceiverCloudGetSettingsForm::sltHandleTaskComplete);
+}
+
+void UIReceiverCloudGetSettingsForm::sltHandleTaskComplete(UITask *pTask)
+{
+ /* Skip unrelated tasks: */
+ if (!pTask || pTask->type() != UITask::Type_CloudGetSettingsForm)
+ return;
+
+ /* Cast task to corresponding sub-class: */
+ UITaskCloudGetSettingsForm *pSettingsTask = static_cast<UITaskCloudGetSettingsForm*>(pTask);
+
+ /* Redirect to another listeners: */
+ if (pSettingsTask->errorInfo().isNull())
+ emit sigTaskComplete(pSettingsTask->result());
+ else
+ {
+ UINotificationMessage::cannotAcquireCloudMachineSettings(pSettingsTask->errorInfo());
+ emit sigTaskFailed(pSettingsTask->errorInfo());
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UITaskCloudGetSettingsForm.h b/src/VBox/Frontends/VirtualBox/src/manager/UITaskCloudGetSettingsForm.h
new file mode 100644
index 00000000..d1ad143e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UITaskCloudGetSettingsForm.h
@@ -0,0 +1,114 @@
+/* $Id: UITaskCloudGetSettingsForm.h $ */
+/** @file
+ * VBox Qt GUI - UITaskCloudGetSettingsForm class declaration.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UITaskCloudGetSettingsForm_h
+#define FEQT_INCLUDED_SRC_manager_UITaskCloudGetSettingsForm_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMutex>
+
+/* GUI includes: */
+#include "UITask.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudMachine.h"
+#include "CForm.h"
+
+/** UITask extension used to get cloud machine settings form. */
+class UITaskCloudGetSettingsForm : public UITask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task taking @a comCloudMachine as data.
+ * @param comCloudMachine Brings the cloud machine object. */
+ UITaskCloudGetSettingsForm(const CCloudMachine &comCloudMachine);
+
+ /** Returns cloud machine object. */
+ CCloudMachine cloudMachine() const { return m_comCloudMachine; }
+
+ /** Returns error info. */
+ QString errorInfo() const;
+
+ /** Returns the task result. */
+ CForm result() const;
+
+protected:
+
+ /** Contains the task body. */
+ virtual void run() RT_OVERRIDE;
+
+private:
+
+ /** Holds the mutex to access result. */
+ mutable QMutex m_mutex;
+
+ /** Holds the cloud machine object. */
+ CCloudMachine m_comCloudMachine;
+
+ /** Holds the error info. */
+ QString m_strErrorInfo;
+
+ /** Holds the task result. */
+ CForm m_comResult;
+};
+
+/** QObject extension used to receive and redirect result
+ * of get cloud machine settings form task described above. */
+class UIReceiverCloudGetSettingsForm : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about task is complete with certain comResult. */
+ void sigTaskComplete(const CForm &comResult);
+ /** Notifies about task is failed with certain strErrorMessage. */
+ void sigTaskFailed(const QString &strErrorMessage);
+
+public:
+
+ /** Constructs receiver passing @a pParent to the base-class. */
+ UIReceiverCloudGetSettingsForm(QWidget *pParent);
+
+public slots:
+
+ /** Handles thread-pool signal about @a pTask is complete. */
+ void sltHandleTaskComplete(UITask *pTask);
+
+private:
+
+ /** Holds the parent widget reference. */
+ QWidget *m_pParent;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UITaskCloudGetSettingsForm_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneGlobal.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneGlobal.cpp
new file mode 100644
index 00000000..37d6ecef
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneGlobal.cpp
@@ -0,0 +1,333 @@
+/* $Id: UIToolPaneGlobal.cpp $ */
+/** @file
+ * VBox Qt GUI - UIToolPaneGlobal class implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStackedLayout>
+#ifndef VBOX_WS_MAC
+# include <QStyle>
+#endif
+#include <QUuid>
+
+/* GUI includes */
+#include "UIActionPoolManager.h"
+#include "UICommon.h"
+#include "UICloudProfileManager.h"
+#include "UIExtensionPackManager.h"
+#include "UIMediumManager.h"
+#include "UINetworkManager.h"
+#include "UIToolPaneGlobal.h"
+#include "UIVMActivityOverviewWidget.h"
+#include "UIWelcomePane.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+UIToolPaneGlobal::UIToolPaneGlobal(UIActionPool *pActionPool, QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_pActionPool(pActionPool)
+ , m_pLayout(0)
+ , m_pPaneWelcome(0)
+ , m_pPaneExtensions(0)
+ , m_pPaneMedia(0)
+ , m_pPaneNetwork(0)
+ , m_pPaneCloud(0)
+ , m_pPaneVMActivityOverview(0)
+ , m_fActive(false)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIToolPaneGlobal::~UIToolPaneGlobal()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIToolPaneGlobal::setActive(bool fActive)
+{
+ /* Save activity: */
+ if (m_fActive != fActive)
+ {
+ m_fActive = fActive;
+
+ /* Handle token change: */
+ handleTokenChange();
+ }
+}
+
+UIToolType UIToolPaneGlobal::currentTool() const
+{
+ return m_pLayout && m_pLayout->currentWidget()
+ ? m_pLayout->currentWidget()->property("ToolType").value<UIToolType>()
+ : UIToolType_Invalid;
+}
+
+bool UIToolPaneGlobal::isToolOpened(UIToolType enmType) const
+{
+ /* Search through the stacked widgets: */
+ for (int iIndex = 0; iIndex < m_pLayout->count(); ++iIndex)
+ if (m_pLayout->widget(iIndex)->property("ToolType").value<UIToolType>() == enmType)
+ return true;
+ return false;
+}
+
+void UIToolPaneGlobal::openTool(UIToolType enmType)
+{
+ /* Search through the stacked widgets: */
+ int iActualIndex = -1;
+ for (int iIndex = 0; iIndex < m_pLayout->count(); ++iIndex)
+ if (m_pLayout->widget(iIndex)->property("ToolType").value<UIToolType>() == enmType)
+ iActualIndex = iIndex;
+
+ /* If widget with such type exists: */
+ if (iActualIndex != -1)
+ {
+ /* Activate corresponding index: */
+ m_pLayout->setCurrentIndex(iActualIndex);
+ }
+ /* Otherwise: */
+ else
+ {
+ /* Create, remember, append corresponding stacked widget: */
+ switch (enmType)
+ {
+ case UIToolType_Welcome:
+ {
+ /* Create Desktop pane: */
+ m_pPaneWelcome = new UIWelcomePane;
+ if (m_pPaneWelcome)
+ {
+ /* Configure pane: */
+ m_pPaneWelcome->setProperty("ToolType", QVariant::fromValue(UIToolType_Welcome));
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneWelcome);
+ m_pLayout->setCurrentWidget(m_pPaneWelcome);
+ }
+ break;
+ }
+ case UIToolType_Extensions:
+ {
+ /* Create Extension Pack Manager: */
+ m_pPaneExtensions = new UIExtensionPackManagerWidget(EmbedTo_Stack, m_pActionPool, false /* show toolbar */);
+ AssertPtrReturnVoid(m_pPaneExtensions);
+ {
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneExtensions->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+
+ /* Configure pane: */
+ m_pPaneExtensions->setProperty("ToolType", QVariant::fromValue(UIToolType_Extensions));
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneExtensions);
+ m_pLayout->setCurrentWidget(m_pPaneExtensions);
+ }
+ break;
+ }
+ case UIToolType_Media:
+ {
+ /* Create Virtual Media Manager: */
+ m_pPaneMedia = new UIMediumManagerWidget(EmbedTo_Stack, m_pActionPool, false /* show toolbar */);
+ AssertPtrReturnVoid(m_pPaneMedia);
+ {
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneMedia->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+
+ /* Configure pane: */
+ m_pPaneMedia->setProperty("ToolType", QVariant::fromValue(UIToolType_Media));
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneMedia);
+ m_pLayout->setCurrentWidget(m_pPaneMedia);
+ }
+ break;
+ }
+ case UIToolType_Network:
+ {
+ /* Create Network Manager: */
+ m_pPaneNetwork = new UINetworkManagerWidget(EmbedTo_Stack, m_pActionPool, false /* show toolbar */);
+ AssertPtrReturnVoid(m_pPaneNetwork);
+ {
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneNetwork->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+
+ /* Configure pane: */
+ m_pPaneNetwork->setProperty("ToolType", QVariant::fromValue(UIToolType_Network));
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneNetwork);
+ m_pLayout->setCurrentWidget(m_pPaneNetwork);
+ }
+ break;
+ }
+ case UIToolType_Cloud:
+ {
+ /* Create Cloud Profile Manager: */
+ m_pPaneCloud = new UICloudProfileManagerWidget(EmbedTo_Stack, m_pActionPool, false /* show toolbar */);
+ AssertPtrReturnVoid(m_pPaneCloud);
+ {
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneCloud->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+
+ /* Configure pane: */
+ m_pPaneCloud->setProperty("ToolType", QVariant::fromValue(UIToolType_Cloud));
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneCloud);
+ m_pLayout->setCurrentWidget(m_pPaneCloud);
+ }
+ break;
+ }
+ case UIToolType_VMActivityOverview:
+ {
+ /* Create VM Activity Overview: */
+ m_pPaneVMActivityOverview = new UIVMActivityOverviewWidget(EmbedTo_Stack, m_pActionPool, false /* show toolbar */);
+ AssertPtrReturnVoid(m_pPaneVMActivityOverview);
+ {
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneVMActivityOverview->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+
+ /* Configure pane: */
+ m_pPaneVMActivityOverview->setProperty("ToolType", QVariant::fromValue(UIToolType_VMActivityOverview));
+ connect(m_pPaneVMActivityOverview, &UIVMActivityOverviewWidget::sigSwitchToMachineActivityPane,
+ this, &UIToolPaneGlobal::sigSwitchToMachineActivityPane);
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneVMActivityOverview);
+ m_pLayout->setCurrentWidget(m_pPaneVMActivityOverview);
+ }
+
+ break;
+ }
+ default:
+ AssertFailedReturnVoid();
+ }
+ }
+
+ /* Handle token change: */
+ handleTokenChange();
+}
+
+void UIToolPaneGlobal::closeTool(UIToolType enmType)
+{
+ /* Search through the stacked widgets: */
+ int iActualIndex = -1;
+ for (int iIndex = 0; iIndex < m_pLayout->count(); ++iIndex)
+ if (m_pLayout->widget(iIndex)->property("ToolType").value<UIToolType>() == enmType)
+ iActualIndex = iIndex;
+
+ /* If widget with such type doesn't exist: */
+ if (iActualIndex != -1)
+ {
+ /* Forget corresponding widget: */
+ switch (enmType)
+ {
+ case UIToolType_Welcome: m_pPaneWelcome = 0; break;
+ case UIToolType_Extensions: m_pPaneExtensions = 0; break;
+ case UIToolType_Media: m_pPaneMedia = 0; break;
+ case UIToolType_Network: m_pPaneNetwork = 0; break;
+ case UIToolType_Cloud: m_pPaneCloud = 0; break;
+ default: break;
+ }
+ /* Delete corresponding widget: */
+ QWidget *pWidget = m_pLayout->widget(iActualIndex);
+ m_pLayout->removeWidget(pWidget);
+ delete pWidget;
+ }
+
+ /* Handle token change: */
+ handleTokenChange();
+}
+
+QString UIToolPaneGlobal::currentHelpKeyword() const
+{
+ QWidget *pCurrentToolWidget = 0;
+ //UIToolType currentTool() const;
+ switch (currentTool())
+ {
+ case UIToolType_Welcome:
+ pCurrentToolWidget = m_pPaneWelcome;
+ break;
+ case UIToolType_Extensions:
+ pCurrentToolWidget = m_pPaneExtensions;
+ break;
+ case UIToolType_Media:
+ pCurrentToolWidget = m_pPaneMedia;
+ break;
+ case UIToolType_Network:
+ pCurrentToolWidget = m_pPaneNetwork;
+ break;
+ case UIToolType_Cloud:
+ pCurrentToolWidget = m_pPaneCloud;
+ break;
+ case UIToolType_VMActivityOverview:
+ pCurrentToolWidget = m_pPaneVMActivityOverview;
+ break;
+ default:
+ break;
+ }
+ return uiCommon().helpKeyword(pCurrentToolWidget);
+}
+
+void UIToolPaneGlobal::prepare()
+{
+ /* Create stacked-layout: */
+ m_pLayout = new QStackedLayout(this);
+
+ /* Create desktop pane: */
+ openTool(UIToolType_Welcome);
+}
+
+void UIToolPaneGlobal::cleanup()
+{
+ /* Remove all widgets prematurelly: */
+ while (m_pLayout->count())
+ {
+ QWidget *pWidget = m_pLayout->widget(0);
+ m_pLayout->removeWidget(pWidget);
+ delete pWidget;
+ }
+}
+
+void UIToolPaneGlobal::handleTokenChange()
+{
+ /* Determine whether resource monitor is currently active tool: */
+ if (m_pPaneVMActivityOverview)
+ m_pPaneVMActivityOverview->setIsCurrentTool(m_fActive && currentTool() == UIToolType_VMActivityOverview);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneGlobal.h b/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneGlobal.h
new file mode 100644
index 00000000..02186ea8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneGlobal.h
@@ -0,0 +1,122 @@
+/* $Id: UIToolPaneGlobal.h $ */
+/** @file
+ * VBox Qt GUI - UIToolPaneGlobal class declaration.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIToolPaneGlobal_h
+#define FEQT_INCLUDED_SRC_manager_UIToolPaneGlobal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QStackedLayout;
+class QVBoxLayout;
+class UIActionPool;
+class UICloudProfileManagerWidget;
+class UIExtensionPackManagerWidget;
+class UIMediumManagerWidget;
+class UINetworkManagerWidget;
+class UIVMActivityOverviewWidget;
+class UIVirtualMachineItem;
+class UIWelcomePane;
+class CMachine;
+
+
+/** QWidget subclass representing container for tool panes. */
+class UIToolPaneGlobal : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about request to switch to Activity pane of machine with @a uMachineId. */
+ void sigSwitchToMachineActivityPane(const QUuid &uMachineId);
+
+public:
+
+ /** Constructs tools pane passing @a pParent to the base-class. */
+ UIToolPaneGlobal(UIActionPool *pActionPool, QWidget *pParent = 0);
+ /** Destructs tools pane. */
+ virtual ~UIToolPaneGlobal() RT_OVERRIDE;
+
+ /** Defines whether this pane is @a fActive. */
+ void setActive(bool fActive);
+ /** Returns whether this pane is active. */
+ bool active() const { return m_fActive; }
+
+ /** Returns type of tool currently opened. */
+ UIToolType currentTool() const;
+ /** Returns whether tool of particular @a enmType is opened. */
+ bool isToolOpened(UIToolType enmType) const;
+ /** Activates tool of passed @a enmType, creates new one if necessary. */
+ void openTool(UIToolType enmType);
+ /** Closes tool of passed @a enmType, deletes one if exists. */
+ void closeTool(UIToolType enmType);
+ /** Returns the help keyword of the current tool's widget. */
+ QString currentHelpKeyword() const;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares stacked-layout. */
+ void prepareStackedLayout();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Handles token change. */
+ void handleTokenChange();
+
+ /** Holds the action pool reference. */
+ UIActionPool *m_pActionPool;
+
+ /** Holds the stacked-layout instance. */
+ QStackedLayout *m_pLayout;
+ /** Holds the Welcome pane instance. */
+ UIWelcomePane *m_pPaneWelcome;
+ /** Holds the Extension Pack Manager instance. */
+ UIExtensionPackManagerWidget *m_pPaneExtensions;
+ /** Holds the Virtual Media Manager instance. */
+ UIMediumManagerWidget *m_pPaneMedia;
+ /** Holds the Network Manager instance. */
+ UINetworkManagerWidget *m_pPaneNetwork;
+ /** Holds the Cloud Profile Manager instance. */
+ UICloudProfileManagerWidget *m_pPaneCloud;
+ /** Holds the VM Activity Overview instance. */
+ UIVMActivityOverviewWidget *m_pPaneVMActivityOverview;
+
+ /** Holds whether this pane is active. */
+ bool m_fActive;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIToolPaneGlobal_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneMachine.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneMachine.cpp
new file mode 100644
index 00000000..fb9a5111
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneMachine.cpp
@@ -0,0 +1,398 @@
+/* $Id: UIToolPaneMachine.cpp $ */
+/** @file
+ * VBox Qt GUI - UIToolPaneMachine class implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStackedLayout>
+#ifndef VBOX_WS_MAC
+# include <QStyle>
+#endif
+#include <QUuid>
+
+/* GUI includes */
+#include "UIActionPoolManager.h"
+#include "UIDetails.h"
+#include "UIErrorPane.h"
+#include "UIFileManager.h"
+#include "UIIconPool.h"
+#include "UISnapshotPane.h"
+#include "UIToolPaneMachine.h"
+#include "UIVirtualMachineItem.h"
+#include "UIVMActivityToolWidget.h"
+#include "UIVMLogViewerWidget.h"
+
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+UIToolPaneMachine::UIToolPaneMachine(UIActionPool *pActionPool, QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_pActionPool(pActionPool)
+ , m_pItem(0)
+ , m_pLayout(0)
+ , m_pPaneError(0)
+ , m_pPaneDetails(0)
+ , m_pPaneSnapshots(0)
+ , m_pPaneLogViewer(0)
+ , m_pPaneVMActivityMonitor(0)
+ , m_pPaneFileManager(0)
+ , m_fActive(false)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIToolPaneMachine::~UIToolPaneMachine()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIToolPaneMachine::setActive(bool fActive)
+{
+ /* Save activity: */
+ if (m_fActive != fActive)
+ {
+ m_fActive = fActive;
+
+ /* Handle token change: */
+ handleTokenChange();
+ }
+}
+
+UIToolType UIToolPaneMachine::currentTool() const
+{
+ return m_pLayout && m_pLayout->currentWidget()
+ ? m_pLayout->currentWidget()->property("ToolType").value<UIToolType>()
+ : UIToolType_Invalid;
+}
+
+bool UIToolPaneMachine::isToolOpened(UIToolType enmType) const
+{
+ /* Search through the stacked widgets: */
+ for (int iIndex = 0; iIndex < m_pLayout->count(); ++iIndex)
+ if (m_pLayout->widget(iIndex)->property("ToolType").value<UIToolType>() == enmType)
+ return true;
+ return false;
+}
+
+void UIToolPaneMachine::openTool(UIToolType enmType)
+{
+ /* Search through the stacked widgets: */
+ int iActualIndex = -1;
+ for (int iIndex = 0; iIndex < m_pLayout->count(); ++iIndex)
+ if (m_pLayout->widget(iIndex)->property("ToolType").value<UIToolType>() == enmType)
+ iActualIndex = iIndex;
+
+ /* If widget with such type exists: */
+ if (iActualIndex != -1)
+ {
+ /* Activate corresponding index: */
+ m_pLayout->setCurrentIndex(iActualIndex);
+ }
+ /* Otherwise: */
+ else
+ {
+ /* Create, remember, append corresponding stacked widget: */
+ switch (enmType)
+ {
+ case UIToolType_Error:
+ {
+ /* Create Error pane: */
+ m_pPaneError = new UIErrorPane;
+ if (m_pPaneError)
+ {
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneError->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+
+ /* Configure pane: */
+ m_pPaneError->setProperty("ToolType", QVariant::fromValue(UIToolType_Error));
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneError);
+ m_pLayout->setCurrentWidget(m_pPaneError);
+ }
+ break;
+ }
+ case UIToolType_Details:
+ {
+ /* Create Details pane: */
+ m_pPaneDetails = new UIDetails;
+ AssertPtrReturnVoid(m_pPaneDetails);
+ {
+ /* Configure pane: */
+ m_pPaneDetails->setProperty("ToolType", QVariant::fromValue(UIToolType_Details));
+ connect(this, &UIToolPaneMachine::sigToggleStarted, m_pPaneDetails, &UIDetails::sigToggleStarted);
+ connect(this, &UIToolPaneMachine::sigToggleFinished, m_pPaneDetails, &UIDetails::sigToggleFinished);
+ connect(m_pPaneDetails, &UIDetails::sigLinkClicked, this, &UIToolPaneMachine::sigLinkClicked);
+ m_pPaneDetails->setItems(m_items);
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneDetails);
+ m_pLayout->setCurrentWidget(m_pPaneDetails);
+ }
+ break;
+ }
+ case UIToolType_Snapshots:
+ {
+ /* Create Snapshots pane: */
+ m_pPaneSnapshots = new UISnapshotPane(m_pActionPool, false /* show toolbar? */);
+ AssertPtrReturnVoid(m_pPaneSnapshots);
+ {
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneSnapshots->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+
+ /* Configure pane: */
+ m_pPaneSnapshots->setProperty("ToolType", QVariant::fromValue(UIToolType_Snapshots));
+ connect(m_pPaneSnapshots, &UISnapshotPane::sigCurrentItemChange,
+ this, &UIToolPaneMachine::sigCurrentSnapshotItemChange);
+ m_pPaneSnapshots->setMachineItems(m_items);
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneSnapshots);
+ m_pLayout->setCurrentWidget(m_pPaneSnapshots);
+ }
+ break;
+ }
+ case UIToolType_Logs:
+ {
+ /* Create the Logviewer pane: */
+ m_pPaneLogViewer = new UIVMLogViewerWidget(EmbedTo_Stack, m_pActionPool, false /* show toolbar */);
+ AssertPtrReturnVoid(m_pPaneLogViewer);
+ {
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneLogViewer->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+
+ /* Configure pane: */
+ m_pPaneLogViewer->setProperty("ToolType", QVariant::fromValue(UIToolType_Logs));
+ m_pPaneLogViewer->setSelectedVMListItems(m_items);
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneLogViewer);
+ m_pLayout->setCurrentWidget(m_pPaneLogViewer);
+ }
+ break;
+ }
+ case UIToolType_VMActivity:
+ {
+ m_pPaneVMActivityMonitor = new UIVMActivityToolWidget(EmbedTo_Stack, m_pActionPool,
+ false /* Show toolbar */, 0 /* Parent */);
+ AssertPtrReturnVoid(m_pPaneVMActivityMonitor);
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneVMActivityMonitor->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+
+ /* Configure pane: */
+ m_pPaneVMActivityMonitor->setProperty("ToolType", QVariant::fromValue(UIToolType_VMActivity));
+ m_pPaneVMActivityMonitor->setSelectedVMListItems(m_items);
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneVMActivityMonitor);
+ m_pLayout->setCurrentWidget(m_pPaneVMActivityMonitor);
+
+ connect(m_pPaneVMActivityMonitor, &UIVMActivityToolWidget::sigSwitchToActivityOverviewPane,
+ this, &UIToolPaneMachine::sigSwitchToActivityOverviewPane);
+ break;
+ }
+ case UIToolType_FileManager:
+ {
+ if (!m_items.isEmpty())
+ m_pPaneFileManager = new UIFileManager(EmbedTo_Stack, m_pActionPool,
+ uiCommon().virtualBox().FindMachine(m_items[0]->id().toString()),
+ 0, false /* fShowToolbar */);
+ else
+ m_pPaneFileManager = new UIFileManager(EmbedTo_Stack, m_pActionPool, CMachine(),
+ 0, false /* fShowToolbar */);
+ AssertPtrReturnVoid(m_pPaneFileManager);
+#ifndef VBOX_WS_MAC
+ const int iMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 4;
+ m_pPaneFileManager->setContentsMargins(iMargin, 0, iMargin, 0);
+#endif
+ /* Configure pane: */
+ m_pPaneFileManager->setProperty("ToolType", QVariant::fromValue(UIToolType_FileManager));
+ m_pPaneFileManager->setSelectedVMListItems(m_items);
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pPaneFileManager);
+ m_pLayout->setCurrentWidget(m_pPaneFileManager);
+ break;
+ }
+ default:
+ AssertFailedReturnVoid();
+ }
+ }
+
+ /* Handle token change: */
+ handleTokenChange();
+}
+
+void UIToolPaneMachine::closeTool(UIToolType enmType)
+{
+ /* Search through the stacked widgets: */
+ int iActualIndex = -1;
+ for (int iIndex = 0; iIndex < m_pLayout->count(); ++iIndex)
+ if (m_pLayout->widget(iIndex)->property("ToolType").value<UIToolType>() == enmType)
+ iActualIndex = iIndex;
+
+ /* If widget with such type doesn't exist: */
+ if (iActualIndex != -1)
+ {
+ /* Forget corresponding widget: */
+ switch (enmType)
+ {
+ case UIToolType_Error: m_pPaneError = 0; break;
+ case UIToolType_Details: m_pPaneDetails = 0; break;
+ case UIToolType_Snapshots: m_pPaneSnapshots = 0; break;
+ case UIToolType_Logs: m_pPaneLogViewer = 0; break;
+ case UIToolType_VMActivity: m_pPaneVMActivityMonitor = 0; break;
+ default: break;
+ }
+ /* Delete corresponding widget: */
+ QWidget *pWidget = m_pLayout->widget(iActualIndex);
+ m_pLayout->removeWidget(pWidget);
+ delete pWidget;
+ }
+
+ /* Handle token change: */
+ handleTokenChange();
+}
+
+void UIToolPaneMachine::setErrorDetails(const QString &strDetails)
+{
+ /* Update Error pane: */
+ if (m_pPaneError)
+ m_pPaneError->setErrorDetails(strDetails);
+}
+
+void UIToolPaneMachine::setCurrentItem(UIVirtualMachineItem *pItem)
+{
+ if (m_pItem == pItem)
+ return;
+
+ /* Remember new item: */
+ m_pItem = pItem;
+}
+
+void UIToolPaneMachine::setItems(const QList<UIVirtualMachineItem*> &items)
+{
+ /* Cache passed value: */
+ m_items = items;
+
+ /* Update details pane is open: */
+ if (isToolOpened(UIToolType_Details))
+ {
+ AssertPtrReturnVoid(m_pPaneDetails);
+ m_pPaneDetails->setItems(m_items);
+ }
+ /* Update snapshots pane if it is open: */
+ if (isToolOpened(UIToolType_Snapshots))
+ {
+ AssertPtrReturnVoid(m_pPaneSnapshots);
+ m_pPaneSnapshots->setMachineItems(m_items);
+ }
+ /* Update logs pane if it is open: */
+ if (isToolOpened(UIToolType_Logs))
+ {
+ AssertPtrReturnVoid(m_pPaneLogViewer);
+ m_pPaneLogViewer->setSelectedVMListItems(m_items);
+ }
+ /* Update performance monitor pane if it is open: */
+ if (isToolOpened(UIToolType_VMActivity))
+ {
+ AssertPtrReturnVoid(m_pPaneVMActivityMonitor);
+ m_pPaneVMActivityMonitor->setSelectedVMListItems(m_items);
+ }
+ if (isToolOpened(UIToolType_FileManager))
+ {
+ AssertPtrReturnVoid(m_pPaneFileManager);
+ if (!m_items.isEmpty() && m_items[0])
+ m_pPaneFileManager->setSelectedVMListItems(m_items);
+ }
+}
+
+bool UIToolPaneMachine::isCurrentStateItemSelected() const
+{
+ if (!m_pPaneSnapshots)
+ return false;
+ return m_pPaneSnapshots->isCurrentStateItemSelected();
+}
+
+QString UIToolPaneMachine::currentHelpKeyword() const
+{
+ QWidget *pCurrentToolWidget = 0;
+ switch (currentTool())
+ {
+ case UIToolType_Error:
+ pCurrentToolWidget = m_pPaneError;
+ break;
+ case UIToolType_Details:
+ pCurrentToolWidget = m_pPaneDetails;
+ break;
+ case UIToolType_Snapshots:
+ pCurrentToolWidget = m_pPaneSnapshots;
+ break;
+ case UIToolType_Logs:
+ pCurrentToolWidget = m_pPaneLogViewer;
+ break;
+ case UIToolType_VMActivity:
+ pCurrentToolWidget = m_pPaneVMActivityMonitor;
+ break;
+ default:
+ break;
+ }
+ return uiCommon().helpKeyword(pCurrentToolWidget);
+}
+
+void UIToolPaneMachine::prepare()
+{
+ /* Create stacked-layout: */
+ m_pLayout = new QStackedLayout(this);
+
+ /* Create Details pane: */
+ openTool(UIToolType_Details);
+}
+
+void UIToolPaneMachine::cleanup()
+{
+ /* Remove all widgets prematurelly: */
+ while (m_pLayout->count())
+ {
+ QWidget *pWidget = m_pLayout->widget(0);
+ m_pLayout->removeWidget(pWidget);
+ delete pWidget;
+ }
+}
+
+void UIToolPaneMachine::handleTokenChange()
+{
+ // printf("UIToolPaneMachine::handleTokenChange: Active = %d, current tool = %d\n", m_fActive, currentTool());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneMachine.h b/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneMachine.h
new file mode 100644
index 00000000..94810501
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIToolPaneMachine.h
@@ -0,0 +1,153 @@
+/* $Id: UIToolPaneMachine.h $ */
+/** @file
+ * VBox Qt GUI - UIToolPaneMachine class declaration.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIToolPaneMachine_h
+#define FEQT_INCLUDED_SRC_manager_UIToolPaneMachine_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QStackedLayout;
+class QVBoxLayout;
+class UIActionPool;
+class UIDetails;
+class UIErrorPane;
+class UIVMActivityToolWidget;
+class UISnapshotPane;
+class UIVirtualMachineItem;
+class UIVMLogViewerWidget;
+class UIFileManager;
+
+/** QWidget subclass representing container for tool panes. */
+class UIToolPaneMachine : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Redirects signal from UIVirtualBoxManager to UIDetails. */
+ void sigToggleStarted();
+ /** Redirects signal from UIVirtualBoxManager to UIDetails. */
+ void sigToggleFinished();
+ /** Redirects signal from UIDetails to UIVirtualBoxManager. */
+ void sigLinkClicked(const QString &strCategory, const QString &strControl, const QUuid &uId);
+
+ /** Notifies listeners about current Snapshot pane item change. */
+ void sigCurrentSnapshotItemChange();
+
+ /** Notifies listeners about request to switch to Activity Overview pane. */
+ void sigSwitchToActivityOverviewPane();
+
+public:
+
+ /** Constructs tools pane passing @a pParent to the base-class. */
+ UIToolPaneMachine(UIActionPool *pActionPool, QWidget *pParent = 0);
+ /** Destructs tools pane. */
+ virtual ~UIToolPaneMachine() RT_OVERRIDE;
+
+ /** Defines whether this pane is @a fActive. */
+ void setActive(bool fActive);
+ /** Returns whether this pane is active. */
+ bool active() const { return m_fActive; }
+
+ /** Returns type of tool currently opened. */
+ UIToolType currentTool() const;
+ /** Returns whether tool of particular @a enmType is opened. */
+ bool isToolOpened(UIToolType enmType) const;
+ /** Activates tool of passed @a enmType, creates new one if necessary. */
+ void openTool(UIToolType enmType);
+ /** Closes tool of passed @a enmType, deletes one if exists. */
+ void closeTool(UIToolType enmType);
+
+ /** Defines error @a strDetails and switches to Error pane. */
+ void setErrorDetails(const QString &strDetails);
+
+ /** Defines current machine @a pItem. */
+ void setCurrentItem(UIVirtualMachineItem *pItem);
+
+ /** Defines the machine @a items. */
+ void setItems(const QList<UIVirtualMachineItem*> &items);
+
+ /** Returns whether current-state item of Snapshot pane is selected. */
+ bool isCurrentStateItemSelected() const;
+
+ /** Returns the help keyword of the current tool's widget. */
+ QString currentHelpKeyword() const;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares stacked-layout. */
+ void prepareStackedLayout();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Handles token change. */
+ void handleTokenChange();
+
+ /** Holds the action pool reference. */
+ UIActionPool *m_pActionPool;
+
+ /** Holds current machine item reference. */
+ UIVirtualMachineItem *m_pItem;
+
+ /** Holds the stacked-layout instance. */
+ QStackedLayout *m_pLayout;
+ /** Holds the Error pane instance. */
+ UIErrorPane *m_pPaneError;
+ /** Holds the Details pane instance. */
+ UIDetails *m_pPaneDetails;
+ /** Holds the Snapshots pane instance. */
+ UISnapshotPane *m_pPaneSnapshots;
+ /** Holds the Logviewer pane instance. */
+ UIVMLogViewerWidget *m_pPaneLogViewer;
+ /** Holds the Performance Monitor pane instance. */
+ UIVMActivityToolWidget *m_pPaneVMActivityMonitor;
+ /** Holds the File Manager pane instance. */
+ UIFileManager *m_pPaneFileManager;
+
+ /** Holds whether this pane is active. */
+ bool m_fActive;
+
+ /** Holds the cache of passed items. */
+ QList<UIVirtualMachineItem*> m_items;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIToolPaneMachine_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManager.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManager.cpp
new file mode 100644
index 00000000..ea6548a2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManager.cpp
@@ -0,0 +1,3841 @@
+/* $Id: UIVirtualBoxManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVirtualBoxManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QActionGroup>
+#include <QClipboard>
+#include <QFile>
+#include <QFontDatabase>
+#include <QGuiApplication>
+#include <QMenuBar>
+#include <QProcess>
+#include <QPushButton>
+#include <QStandardPaths>
+#include <QStatusBar>
+#include <QStyle>
+#include <QTextEdit>
+#ifndef VBOX_WS_WIN
+# include <QRegExp>
+#endif
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIFileDialog.h"
+#include "QIRichTextLabel.h"
+#include "UIActionPoolManager.h"
+#include "UICloudConsoleManager.h"
+#include "UICloudNetworkingStuff.h"
+#include "UICloudProfileManager.h"
+#include "UIDesktopServices.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIErrorString.h"
+#include "UIExtension.h"
+#include "UIExtensionPackManager.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMedium.h"
+#include "UIMediumManager.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINetworkManager.h"
+#include "UINotificationCenter.h"
+#include "UIQObjectStuff.h"
+#include "UISettingsDialogSpecific.h"
+#include "UIVirtualBoxManager.h"
+#include "UIVirtualBoxManagerWidget.h"
+#include "UIVirtualMachineItemCloud.h"
+#include "UIVirtualMachineItemLocal.h"
+#include "UIVMLogViewerDialog.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIWizardAddCloudVM.h"
+#include "UIWizardCloneVM.h"
+#include "UIWizardExportApp.h"
+#include "UIWizardImportApp.h"
+#include "UIWizardNewCloudVM.h"
+#include "UIWizardNewVM.h"
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+# include "UIUpdateManager.h"
+#endif
+#ifdef VBOX_WS_MAC
+# include "UIImageTools.h"
+# include "UIWindowMenuManager.h"
+# include "VBoxUtils.h"
+#else
+# include "UIMenuBar.h"
+#endif
+#ifdef VBOX_WS_X11
+# include "UIDesktopWidgetWatchdog.h"
+#endif
+
+/* COM includes: */
+#include "CHostUSBDevice.h"
+#include "CSystemProperties.h"
+#include "CUnattended.h"
+#include "CVirtualBoxErrorInfo.h"
+#ifdef VBOX_WS_MAC
+# include "CVirtualBox.h"
+#endif
+
+/* Other VBox stuff: */
+#include <iprt/buildconfig.h>
+#include <VBox/version.h>
+
+/** QDialog extension used to ask for a public key for console connection needs. */
+class UIAcquirePublicKeyDialog : public QIWithRetranslateUI<QDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs dialog passing @a pParent to the base-class. */
+ UIAcquirePublicKeyDialog(QWidget *pParent = 0);
+
+ /** Return public key. */
+ QString publicKey() const;
+
+private slots:
+
+ /** Handles help-viewer @a link click. */
+ void sltHandleHelpViewerLinkClick(const QUrl &link);
+
+ /** Handles abstract @a pButton click. */
+ void sltHandleButtonClicked(QAbstractButton *pButton);
+ /** Handles Open button click. */
+ void sltHandleOpenButtonClick();
+
+ /** Performs revalidation. */
+ void sltRevalidate();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepare editor contents. */
+ void prepareEditorContents();
+
+ /** Returns a list of default key folders. */
+ QStringList defaultKeyFolders() const;
+ /** Returns a list of key generation tools. */
+ QStringList keyGenerationTools() const;
+
+ /** Loads file contents.
+ * @returns Whether file was really loaded. */
+ bool loadFileContents(const QString &strPath, bool fIgnoreErrors = false);
+
+ /** Holds the help-viewer instance. */
+ QIRichTextLabel *m_pHelpViewer;
+ /** Holds the text-editor instance. */
+ QTextEdit *m_pTextEditor;
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIAcquirePublicKeyDialog implementation. *
+*********************************************************************************************************************************/
+
+UIAcquirePublicKeyDialog::UIAcquirePublicKeyDialog(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QDialog>(pParent)
+ , m_pHelpViewer(0)
+ , m_pTextEditor(0)
+ , m_pButtonBox(0)
+{
+ prepare();
+ sltRevalidate();
+}
+
+QString UIAcquirePublicKeyDialog::publicKey() const
+{
+ return m_pTextEditor->toPlainText();
+}
+
+void UIAcquirePublicKeyDialog::sltHandleHelpViewerLinkClick(const QUrl &link)
+{
+ /* Parse the link meta and use it to get tool path to copy to clipboard: */
+ bool fOk = false;
+ const uint uToolNumber = link.toString().section('#', 1, 1).toUInt(&fOk);
+ if (fOk)
+ QApplication::clipboard()->setText(keyGenerationTools().value(uToolNumber), QClipboard::Clipboard);
+}
+
+void UIAcquirePublicKeyDialog::sltHandleButtonClicked(QAbstractButton *pButton)
+{
+ const QDialogButtonBox::StandardButton enmStandardButton = m_pButtonBox->standardButton(pButton);
+ switch (enmStandardButton)
+ {
+ case QDialogButtonBox::Ok: return accept();
+ case QDialogButtonBox::Cancel: return reject();
+ case QDialogButtonBox::Open: return sltHandleOpenButtonClick();
+ default: break;
+ }
+}
+
+void UIAcquirePublicKeyDialog::sltHandleOpenButtonClick()
+{
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ const QString strFileName = QIFileDialog::getOpenFileName(comVBox.GetHomeFolder(), QString(),
+ this, tr("Choose a public key file"));
+ if (!strFileName.isEmpty())
+ {
+ gEDataManager->setCloudConsolePublicKeyPath(strFileName);
+ loadFileContents(strFileName);
+ }
+}
+
+void UIAcquirePublicKeyDialog::sltRevalidate()
+{
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(!m_pTextEditor->toPlainText().isEmpty());
+}
+
+void UIAcquirePublicKeyDialog::retranslateUi()
+{
+ setWindowTitle(tr("Public key"));
+
+ /* Generating help-viewer text: */
+ QStringList folders;
+ foreach (const QString &strFolder, defaultKeyFolders())
+ folders << QString("&nbsp;%1").arg(strFolder);
+ const QStringList initialTools = keyGenerationTools();
+ QStringList tools;
+ foreach (const QString &strTool, initialTools)
+ tools << QString("&nbsp;<a href=#%1><img src='manager://copy'/></a>&nbsp;&nbsp;%2")
+ .arg(initialTools.indexOf(strTool))
+ .arg(strTool);
+#ifdef VBOX_WS_WIN
+ m_pHelpViewer->setText(tr("We haven't found public key id_rsa[.pub] in suitable locations. "
+ "If you have one, please put it under one of those folders OR copy "
+ "content to the edit box below:<br><br>"
+ "%1<br><br>"
+ "If you don't have one, please consider using one of the following "
+ "tools to generate it:<br><br>"
+ "%2")
+ .arg(folders.join("<br>"))
+ .arg(tools.join("<br>")));
+#else
+ m_pHelpViewer->setText(tr("We haven't found public key id_rsa[.pub] in suitable location. "
+ "If you have one, please put it under specified folder OR copy "
+ "content to the edit box below:<br><br>"
+ "%1<br><br>"
+ "If you don't have one, please consider using the following "
+ "tool to generate it:<br><br>"
+ "%2")
+ .arg(folders.join("<br>"))
+ .arg(tools.join("<br>")));
+#endif
+
+ m_pTextEditor->setPlaceholderText(tr("Paste public key"));
+ m_pButtonBox->button(QDialogButtonBox::Open)->setText(tr("Browse"));
+}
+
+void UIAcquirePublicKeyDialog::prepare()
+{
+ /* Prepare widgets: */
+ prepareWidgets();
+ /* Prepare editor contents: */
+ prepareEditorContents();
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Resize to suitable size: */
+ const int iMinimumHeightHint = minimumSizeHint().height();
+ resize(iMinimumHeightHint * 1.618, iMinimumHeightHint);
+}
+
+void UIAcquirePublicKeyDialog::prepareWidgets()
+{
+ /* Prepare layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Create help-viewer: */
+ m_pHelpViewer = new QIRichTextLabel(this);
+ if (m_pHelpViewer)
+ {
+ /* Prepare icon and size as well: */
+ const QIcon icon = UIIconPool::iconSet(":/file_manager_copy_16px.png");
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) * 2 / 3;
+
+ /* Configure help-viewer: */
+ m_pHelpViewer->setHidden(true);
+ m_pHelpViewer->setMinimumTextWidth(gpDesktop->screenGeometry(window()).width() / 5);
+ m_pHelpViewer->registerPixmap(icon.pixmap(window()->windowHandle(), QSize(iMetric, iMetric)), "manager://copy");
+ connect(m_pHelpViewer, &QIRichTextLabel::sigLinkClicked, this, &UIAcquirePublicKeyDialog::sltHandleHelpViewerLinkClick);
+ pLayout->addWidget(m_pHelpViewer, 2);
+ }
+
+ /* Prepare text-editor: */
+ m_pTextEditor = new QTextEdit(this);
+ if (m_pTextEditor)
+ {
+ connect(m_pTextEditor, &QTextEdit::textChanged, this, &UIAcquirePublicKeyDialog::sltRevalidate);
+ pLayout->addWidget(m_pTextEditor, 1);
+ }
+
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox(this);
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Open);
+ connect(m_pButtonBox, &QIDialogButtonBox::clicked, this, &UIAcquirePublicKeyDialog::sltHandleButtonClicked);
+ pLayout->addWidget(m_pButtonBox);
+ }
+ }
+}
+
+void UIAcquirePublicKeyDialog::prepareEditorContents()
+{
+ /* Check whether we were able to load key file: */
+ bool fFileLoaded = false;
+
+ /* Try to load last remembered file contents: */
+ fFileLoaded = loadFileContents(gEDataManager->cloudConsolePublicKeyPath(), true /* ignore errors */);
+ if (!fFileLoaded)
+ {
+ /* We have failed to load file mentioned in extra-data, now we have
+ * to check whether file present in one of default paths: */
+ QString strAbsoluteFilePathWeNeed;
+ foreach (const QString &strPath, defaultKeyFolders())
+ {
+ /* Gather possible file names, there can be few of them: */
+ const QStringList fileNames = QStringList() << "id_rsa.pub" << "id_rsa";
+ /* For each file name we have to: */
+ foreach (const QString &strFileName, fileNames)
+ {
+ /* Compose absolute file path: */
+ const QString strAbsoluteFilePath = QDir(strPath).absoluteFilePath(strFileName);
+ /* If that file exists, we are referring it: */
+ if (QFile::exists(strAbsoluteFilePath))
+ {
+ strAbsoluteFilePathWeNeed = strAbsoluteFilePath;
+ break;
+ }
+ }
+ /* Break early if we have found something: */
+ if (!strAbsoluteFilePathWeNeed.isEmpty())
+ break;
+ }
+
+ /* Try to open file if it was really found: */
+ if (!strAbsoluteFilePathWeNeed.isEmpty())
+ fFileLoaded = loadFileContents(strAbsoluteFilePathWeNeed, true /* ignore errors */);
+ }
+
+ /* Show/hide help-viewer depending on
+ * whether we were able to load the file: */
+ m_pHelpViewer->setHidden(fFileLoaded);
+}
+
+QStringList UIAcquirePublicKeyDialog::defaultKeyFolders() const
+{
+ QStringList folders;
+#ifdef VBOX_WS_WIN
+ // WORKAROUND:
+ // There is additional default folder on Windows:
+ folders << QDir::toNativeSeparators(QDir(QDir::homePath()).absoluteFilePath("oci"));
+#endif
+ folders << QDir::toNativeSeparators(QDir(QDir::homePath()).absoluteFilePath(".ssh"));
+ return folders;
+}
+
+QStringList UIAcquirePublicKeyDialog::keyGenerationTools() const
+{
+ QStringList tools;
+#ifdef VBOX_WS_WIN
+ // WORKAROUND:
+ // There is additional key generation tool on Windows:
+ tools << "puttygen.exe";
+ tools << "ssh-keygen.exe -m PEM -t rsa -b 4096";
+#else
+ tools << "ssh-keygen -m PEM -t rsa -b 4096";
+#endif
+ return tools;
+}
+
+bool UIAcquirePublicKeyDialog::loadFileContents(const QString &strPath, bool fIgnoreErrors /* = false */)
+{
+ /* Make sure file path isn't empty: */
+ if (strPath.isEmpty())
+ {
+ if (!fIgnoreErrors)
+ UINotificationMessage::warnAboutPublicKeyFilePathIsEmpty();
+ return false;
+ }
+
+ /* Make sure file exists and is of suitable size: */
+ QFileInfo fi(strPath);
+ if (!fi.exists())
+ {
+ if (!fIgnoreErrors)
+ UINotificationMessage::warnAboutPublicKeyFileDoesntExist(strPath);
+ return false;
+ }
+ if (fi.size() > 10 * _1K)
+ {
+ if (!fIgnoreErrors)
+ UINotificationMessage::warnAboutPublicKeyFileIsOfTooLargeSize(strPath);
+ return false;
+ }
+
+ /* Make sure file can be opened: */
+ QFile file(strPath);
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ if (!fIgnoreErrors)
+ UINotificationMessage::warnAboutPublicKeyFileIsntReadable(strPath);
+ return false;
+ }
+
+ /* File opened and read, filling editor: */
+ m_pTextEditor->setPlainText(file.readAll());
+ return true;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualBoxManager implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIVirtualBoxManager *UIVirtualBoxManager::s_pInstance = 0;
+
+/* static */
+void UIVirtualBoxManager::create()
+{
+ /* Make sure VirtualBox Manager isn't created: */
+ AssertReturnVoid(s_pInstance == 0);
+
+ /* Create VirtualBox Manager: */
+ new UIVirtualBoxManager;
+ /* Prepare VirtualBox Manager: */
+ s_pInstance->prepare();
+ /* Show VirtualBox Manager: */
+ s_pInstance->show();
+ /* Register in the modal window manager: */
+ windowManager().setMainWindowShown(s_pInstance);
+}
+
+/* static */
+void UIVirtualBoxManager::destroy()
+{
+ /* Make sure VirtualBox Manager is created: */
+ AssertPtrReturnVoid(s_pInstance);
+
+ /* Unregister in the modal window manager: */
+ windowManager().setMainWindowShown(0);
+ /* Cleanup VirtualBox Manager: */
+ s_pInstance->cleanup();
+ /* Destroy machine UI: */
+ delete s_pInstance;
+}
+
+UIVirtualBoxManager::UIVirtualBoxManager()
+ : m_fPolished(false)
+ , m_fFirstMediumEnumerationHandled(false)
+ , m_pActionPool(0)
+ , m_pLogViewerDialog(0)
+ , m_pWidget(0)
+ , m_iGeometrySaveTimerId(-1)
+{
+ s_pInstance = this;
+ setAcceptDrops(true);
+}
+
+UIVirtualBoxManager::~UIVirtualBoxManager()
+{
+ s_pInstance = 0;
+}
+
+bool UIVirtualBoxManager::shouldBeMaximized() const
+{
+ return gEDataManager->selectorWindowShouldBeMaximized();
+}
+
+#ifdef VBOX_WS_MAC
+bool UIVirtualBoxManager::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Ignore for non-active window except for FileOpen event which should be always processed: */
+ if (!isActiveWindow() && pEvent->type() != QEvent::FileOpen)
+ return QMainWindowWithRestorableGeometryAndRetranslateUi::eventFilter(pObject, pEvent);
+
+ /* Ignore for other objects: */
+ if (qobject_cast<QWidget*>(pObject) &&
+ qobject_cast<QWidget*>(pObject)->window() != this)
+ return QMainWindowWithRestorableGeometryAndRetranslateUi::eventFilter(pObject, pEvent);
+
+ /* Which event do we have? */
+ switch (pEvent->type())
+ {
+ case QEvent::FileOpen:
+ {
+ sltHandleOpenUrlCall(QList<QUrl>() << static_cast<QFileOpenEvent*>(pEvent)->url());
+ pEvent->accept();
+ return true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QMainWindowWithRestorableGeometryAndRetranslateUi::eventFilter(pObject, pEvent);
+}
+#endif /* VBOX_WS_MAC */
+
+void UIVirtualBoxManager::retranslateUi()
+{
+ /* Set window title: */
+ QString strTitle(VBOX_PRODUCT);
+ strTitle += " " + tr("Manager", "Note: main window title which is prepended by the product name.");
+#ifdef VBOX_BLEEDING_EDGE
+ strTitle += QString(" EXPERIMENTAL build ")
+ + QString(RTBldCfgVersion())
+ + QString(" r")
+ + QString(RTBldCfgRevisionStr())
+ + QString(" - " VBOX_BLEEDING_EDGE);
+#endif /* VBOX_BLEEDING_EDGE */
+ setWindowTitle(strTitle);
+}
+
+bool UIVirtualBoxManager::event(QEvent *pEvent)
+{
+ /* Which event do we have? */
+ switch (pEvent->type())
+ {
+ /* Handle every ScreenChangeInternal event to notify listeners: */
+ case QEvent::ScreenChangeInternal:
+ {
+ emit sigWindowRemapped();
+ break;
+ }
+ /* Handle move/resize geometry changes: */
+ case QEvent::Move:
+ case QEvent::Resize:
+ {
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+ break;
+ }
+ /* Handle timer event started above: */
+ case QEvent::Timer:
+ {
+ QTimerEvent *pTimerEvent = static_cast<QTimerEvent*>(pEvent);
+ if (pTimerEvent->timerId() == m_iGeometrySaveTimerId)
+ {
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = -1;
+ const QRect geo = currentGeometry();
+ LogRel2(("GUI: UIVirtualBoxManager: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ gEDataManager->setSelectorWindowGeometry(geo, isCurrentlyMaximized());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ /* Call to base-class: */
+ return QMainWindowWithRestorableGeometryAndRetranslateUi::event(pEvent);
+}
+
+void UIVirtualBoxManager::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QMainWindowWithRestorableGeometryAndRetranslateUi::showEvent(pEvent);
+
+ /* Is polishing required? */
+ if (!m_fPolished)
+ {
+ /* Pass the show-event to polish-event: */
+ polishEvent(pEvent);
+ /* Mark as polished: */
+ m_fPolished = true;
+ }
+}
+
+void UIVirtualBoxManager::polishEvent(QShowEvent *)
+{
+ /* Make sure user warned about inaccessible media: */
+ QMetaObject::invokeMethod(this, "sltHandleMediumEnumerationFinish", Qt::QueuedConnection);
+}
+
+void UIVirtualBoxManager::closeEvent(QCloseEvent *pEvent)
+{
+ /* Call to base-class: */
+ QMainWindowWithRestorableGeometryAndRetranslateUi::closeEvent(pEvent);
+
+ /* Quit application: */
+ QApplication::quit();
+}
+
+void UIVirtualBoxManager::dragEnterEvent(QDragEnterEvent *pEvent)
+{
+ if (pEvent->mimeData()->hasUrls())
+ pEvent->acceptProposedAction();
+}
+
+void UIVirtualBoxManager::dropEvent(QDropEvent *pEvent)
+{
+ if (!pEvent->mimeData()->hasUrls())
+ return;
+ sltHandleOpenUrlCall(pEvent->mimeData()->urls());
+ pEvent->acceptProposedAction();
+}
+
+#ifdef VBOX_WS_X11
+void UIVirtualBoxManager::sltHandleHostScreenAvailableAreaChange()
+{
+ /* Prevent handling if fake screen detected: */
+ if (UIDesktopWidgetWatchdog::isFakeScreenDetected())
+ return;
+
+ /* Restore the geometry cached by the window: */
+ const QRect geo = currentGeometry();
+ resize(geo.size());
+ move(geo.topLeft());
+}
+#endif /* VBOX_WS_X11 */
+
+void UIVirtualBoxManager::sltHandleCommitData()
+{
+ /* Close the sub-dialogs first: */
+ sltCloseManagerWindow(UIToolType_Extensions);
+ sltCloseManagerWindow(UIToolType_Media);
+ sltCloseManagerWindow(UIToolType_Network);
+ sltCloseManagerWindow(UIToolType_Cloud);
+ sltCloseManagerWindow(UIToolType_CloudConsole);
+ sltCloseSettingsDialog();
+ sltClosePreferencesDialog();
+}
+
+void UIVirtualBoxManager::sltHandleMediumEnumerationFinish()
+{
+#if 0 // ohh, come on!
+ /* To avoid annoying the user, we check for inaccessible media just once, after
+ * the first media emumeration [started from main() at startup] is complete. */
+ if (m_fFirstMediumEnumerationHandled)
+ return;
+ m_fFirstMediumEnumerationHandled = true;
+
+ /* Make sure MM window/tool is not opened,
+ * otherwise user sees everything himself: */
+ if ( m_pManagerVirtualMedia
+ || m_pWidget->isGlobalToolOpened(UIToolType_Media))
+ return;
+
+ /* Look for at least one inaccessible medium: */
+ bool fIsThereAnyInaccessibleMedium = false;
+ foreach (const QUuid &uMediumID, uiCommon().mediumIDs())
+ {
+ if (uiCommon().medium(uMediumID).state() == KMediumState_Inaccessible)
+ {
+ fIsThereAnyInaccessibleMedium = true;
+ break;
+ }
+ }
+ /* Warn the user about inaccessible medium, propose to open MM window/tool: */
+ if (fIsThereAnyInaccessibleMedium && msgCenter().warnAboutInaccessibleMedia())
+ {
+ /* Open the MM window: */
+ sltOpenVirtualMediumManagerWindow();
+ }
+#endif
+}
+
+void UIVirtualBoxManager::sltHandleOpenUrlCall(QList<QUrl> list /* = QList<QUrl>() */)
+{
+ /* If passed list is empty, we take the one from UICommon: */
+ if (list.isEmpty())
+ list = uiCommon().takeArgumentUrls();
+
+ /* Check if we are can handle the dropped urls: */
+ for (int i = 0; i < list.size(); ++i)
+ {
+#ifdef VBOX_WS_MAC
+ const QString strFile = ::darwinResolveAlias(list.at(i).toLocalFile());
+#else
+ const QString strFile = list.at(i).toLocalFile();
+#endif
+ const QStringList isoExtensionList = QStringList() << "iso";
+ /* If there is such file exists: */
+ if (!strFile.isEmpty() && QFile::exists(strFile))
+ {
+ /* And has allowed VBox config file extension: */
+ if (UICommon::hasAllowedExtension(strFile, VBoxFileExts))
+ {
+ /* Handle VBox config file: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CMachine comMachine = comVBox.FindMachine(strFile);
+ if (comVBox.isOk() && comMachine.isNotNull())
+ launchMachine(comMachine);
+ else
+ openAddMachineDialog(strFile);
+ }
+ /* And has allowed VBox OVF file extension: */
+ else if (UICommon::hasAllowedExtension(strFile, OVFFileExts))
+ {
+ /* Allow only one file at the time: */
+ sltOpenImportApplianceWizard(strFile);
+ break;
+ }
+ /* And has allowed VBox extension pack file extension: */
+ else if (UICommon::hasAllowedExtension(strFile, VBoxExtPackFileExts))
+ {
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* Prevent update manager from proposing us to update EP: */
+ gUpdateManager->setEPInstallationRequested(true);
+#endif
+ /* Propose the user to install EP described by the arguments @a list. */
+ UIExtension::install(strFile, QString(), this, NULL);
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* Allow update manager to propose us to update EP: */
+ gUpdateManager->setEPInstallationRequested(false);
+#endif
+ }
+ else if (UICommon::hasAllowedExtension(strFile, isoExtensionList))
+ {
+ openNewMachineWizard(strFile);
+ }
+ }
+ }
+}
+
+void UIVirtualBoxManager::sltCheckUSBAccesibility()
+{
+ CHost comHost = uiCommon().host();
+ if (!comHost.isOk())
+ return;
+ if (comHost.GetUSBDevices().isEmpty() && comHost.isWarning())
+ UINotificationMessage::cannotEnumerateHostUSBDevices(comHost);
+}
+
+void UIVirtualBoxManager::sltHandleChooserPaneIndexChange()
+{
+ // WORKAROUND:
+ // These menus are dynamical since local and cloud VMs have different menu contents.
+ // Yet .. we have to prepare Machine/Group menus beforehand, they contains shortcuts.
+ updateMenuGroup(actionPool()->action(UIActionIndexMN_M_Group)->menu());
+ updateMenuMachine(actionPool()->action(UIActionIndexMN_M_Machine)->menu());
+
+ updateActionsVisibility();
+ updateActionsAppearance();
+
+ /* Special handling for opened settings dialog: */
+ if ( m_pWidget->isLocalMachineItemSelected()
+ && m_settings.contains(UISettingsDialog::DialogType_Machine))
+ {
+ /* Cast dialog to required type: */
+ UISettingsDialogMachine *pDialog =
+ qobject_cast<UISettingsDialogMachine*>(m_settings.value(UISettingsDialog::DialogType_Machine));
+ AssertPtrReturnVoid(pDialog);
+
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertPtrReturnVoid(pItem);
+
+ /* Update machine stuff: */
+ pDialog->setNewMachineId(pItem->id());
+ }
+ else if ( m_pWidget->isCloudMachineItemSelected()
+ && m_pCloudSettings)
+ {
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertPtrReturnVoid(pItem);
+ UIVirtualMachineItemCloud *pItemCloud = pItem->toCloud();
+ AssertPtrReturnVoid(pItemCloud);
+
+ /* Update machine stuff: */
+ m_pCloudSettings->setCloudMachine(pItemCloud->machine());
+ }
+}
+
+void UIVirtualBoxManager::sltHandleGroupSavingProgressChange()
+{
+ updateActionsAppearance();
+}
+
+void UIVirtualBoxManager::sltHandleCloudUpdateProgressChange()
+{
+ updateActionsAppearance();
+}
+
+void UIVirtualBoxManager::sltHandleToolTypeChange()
+{
+ /* Update actions stuff: */
+ updateActionsVisibility();
+ updateActionsAppearance();
+
+ /* Make sure separate dialog closed when corresponding tool opened: */
+ switch (m_pWidget->toolsType())
+ {
+ case UIToolType_Extensions:
+ case UIToolType_Media:
+ case UIToolType_Network:
+ case UIToolType_Cloud:
+ case UIToolType_CloudConsole:
+ sltCloseManagerWindow(m_pWidget->toolsType());
+ break;
+ case UIToolType_Logs:
+ sltCloseLogViewerWindow();
+ break;
+ case UIToolType_VMActivity:
+ case UIToolType_FileManager:
+ default:
+ break;
+ }
+}
+
+void UIVirtualBoxManager::sltCurrentSnapshotItemChange()
+{
+ updateActionsAppearance();
+}
+
+void UIVirtualBoxManager::sltHandleCloudMachineStateChange(const QUuid & /* uId */)
+{
+ updateActionsAppearance();
+}
+
+void UIVirtualBoxManager::sltHandleStateChange(const QUuid &)
+{
+ updateActionsAppearance();
+}
+
+void UIVirtualBoxManager::sltHandleMenuPrepare(int iIndex, QMenu *pMenu)
+{
+ /* Update if there is update-handler: */
+ if (m_menuUpdateHandlers.contains(iIndex))
+ (this->*(m_menuUpdateHandlers.value(iIndex)))(pMenu);
+}
+
+void UIVirtualBoxManager::sltOpenManagerWindow(UIToolType enmType /* = UIToolType_Invalid */)
+{
+ /* Determine actual tool type if possible: */
+ if (enmType == UIToolType_Invalid)
+ {
+ if ( sender()
+ && sender()->inherits("UIAction"))
+ {
+ UIAction *pAction = qobject_cast<UIAction*>(sender());
+ AssertPtrReturnVoid(pAction);
+ enmType = pAction->property("UIToolType").value<UIToolType>();
+ }
+ }
+
+ /* Make sure type is valid: */
+ AssertReturnVoid(enmType != UIToolType_Invalid);
+
+ /* First check if instance of widget opened the embedded way: */
+ if (m_pWidget->isGlobalToolOpened(enmType))
+ {
+ m_pWidget->setToolsType(UIToolType_Welcome);
+ m_pWidget->closeGlobalTool(enmType);
+ }
+
+ /* Create instance if not yet created: */
+ if (!m_managers.contains(enmType))
+ {
+ switch (enmType)
+ {
+ case UIToolType_Extensions: UIExtensionPackManagerFactory(m_pActionPool).prepare(m_managers[enmType], this); break;
+ case UIToolType_Media: UIMediumManagerFactory(m_pActionPool).prepare(m_managers[enmType], this); break;
+ case UIToolType_Network: UINetworkManagerFactory(m_pActionPool).prepare(m_managers[enmType], this); break;
+ case UIToolType_Cloud: UICloudProfileManagerFactory(m_pActionPool).prepare(m_managers[enmType], this); break;
+ case UIToolType_CloudConsole: UICloudConsoleManagerFactory(m_pActionPool).prepare(m_managers[enmType], this); break;
+ default: break;
+ }
+
+ connect(m_managers[enmType], &QIManagerDialog::sigClose,
+ this, &UIVirtualBoxManager::sltCloseManagerWindowDefault);
+ }
+
+ /* Show instance: */
+ m_managers.value(enmType)->show();
+ m_managers.value(enmType)->setWindowState(m_managers.value(enmType)->windowState() & ~Qt::WindowMinimized);
+ m_managers.value(enmType)->activateWindow();
+}
+
+void UIVirtualBoxManager::sltCloseManagerWindow(UIToolType enmType /* = UIToolType_Invalid */)
+{
+ /* Determine actual tool type if possible: */
+ if (enmType == UIToolType_Invalid)
+ {
+ if ( sender()
+ && sender()->inherits("QIManagerDialog"))
+ {
+ QIManagerDialog *pManager = qobject_cast<QIManagerDialog*>(sender());
+ AssertPtrReturnVoid(pManager);
+ enmType = m_managers.key(pManager);
+ }
+ }
+
+ /* Make sure type is valid: */
+ AssertReturnVoid(enmType != UIToolType_Invalid);
+
+ /* Destroy instance if still exists: */
+ if (m_managers.contains(enmType))
+ {
+ switch (enmType)
+ {
+ case UIToolType_Extensions: UIExtensionPackManagerFactory().cleanup(m_managers[enmType]); break;
+ case UIToolType_Media: UIMediumManagerFactory().cleanup(m_managers[enmType]); break;
+ case UIToolType_Network: UINetworkManagerFactory().cleanup(m_managers[enmType]); break;
+ case UIToolType_Cloud: UICloudProfileManagerFactory().cleanup(m_managers[enmType]); break;
+ case UIToolType_CloudConsole: UICloudConsoleManagerFactory().cleanup(m_managers[enmType]); break;
+ default: break;
+ }
+
+ m_managers.remove(enmType);
+ }
+}
+
+void UIVirtualBoxManager::sltOpenImportApplianceWizard(const QString &strFileName /* = QString() */)
+{
+ /* Initialize variables: */
+#ifdef VBOX_WS_MAC
+ QString strTmpFile = ::darwinResolveAlias(strFileName);
+#else
+ QString strTmpFile = strFileName;
+#endif
+
+ /* If there is no file-name passed,
+ * check if cloud stuff focused currently: */
+ bool fOCIByDefault = false;
+ if ( strTmpFile.isEmpty()
+ && ( m_pWidget->isSingleCloudProviderGroupSelected()
+ || m_pWidget->isSingleCloudProfileGroupSelected()
+ || m_pWidget->isCloudMachineItemSelected()))
+ {
+ /* We can generate cloud hints as well: */
+ fOCIByDefault = true;
+ strTmpFile = m_pWidget->fullGroupName();
+ }
+
+ /* Lock the action preventing cascade calls: */
+ UIQObjectPropertySetter guardBlock(actionPool()->action(UIActionIndexMN_M_File_S_ImportAppliance), "opened", true);
+ connect(&guardBlock, &UIQObjectPropertySetter::sigAboutToBeDestroyed,
+ this, &UIVirtualBoxManager::sltHandleUpdateActionAppearanceRequest);
+ updateActionsAppearance();
+
+ /* Use the "safe way" to open stack of Mac OS X Sheets: */
+ QWidget *pWizardParent = windowManager().realParentWindow(this);
+ UINativeWizardPointer pWizard = new UIWizardImportApp(pWizardParent, fOCIByDefault, strTmpFile);
+ windowManager().registerNewParent(pWizard, pWizardParent);
+ pWizard->exec();
+ delete pWizard;
+}
+
+void UIVirtualBoxManager::sltOpenExportApplianceWizard()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+
+ /* Populate the list of VM names: */
+ QStringList names;
+ for (int i = 0; i < items.size(); ++i)
+ names << items.at(i)->name();
+
+ /* Lock the actions preventing cascade calls: */
+ UIQObjectPropertySetter guardBlock(QList<QObject*>() << actionPool()->action(UIActionIndexMN_M_File_S_ExportAppliance)
+ << actionPool()->action(UIActionIndexMN_M_Machine_S_ExportToOCI),
+ "opened", true);
+ connect(&guardBlock, &UIQObjectPropertySetter::sigAboutToBeDestroyed,
+ this, &UIVirtualBoxManager::sltHandleUpdateActionAppearanceRequest);
+ updateActionsAppearance();
+
+ /* Check what was the action invoked us: */
+ UIAction *pAction = qobject_cast<UIAction*>(sender());
+
+ /* Use the "safe way" to open stack of Mac OS X Sheets: */
+ QWidget *pWizardParent = windowManager().realParentWindow(this);
+ UINativeWizardPointer pWizard = new UIWizardExportApp(pWizardParent,
+ names,
+ pAction &&
+ pAction == actionPool()->action(UIActionIndexMN_M_Machine_S_ExportToOCI));
+ windowManager().registerNewParent(pWizard, pWizardParent);
+ pWizard->exec();
+ delete pWizard;
+}
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+void UIVirtualBoxManager::sltOpenExtraDataManagerWindow()
+{
+ gEDataManager->openWindow(this);
+}
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+
+void UIVirtualBoxManager::sltOpenPreferencesDialog()
+{
+ /* Don't show the inaccessible warning
+ * if the user tries to open global settings: */
+ m_fFirstMediumEnumerationHandled = true;
+
+ /* Create instance if not yet created: */
+ if (!m_settings.contains(UISettingsDialog::DialogType_Global))
+ {
+ m_settings[UISettingsDialog::DialogType_Global] = new UISettingsDialogGlobal(this);
+ connect(m_settings[UISettingsDialog::DialogType_Global], &UISettingsDialogGlobal::sigClose,
+ this, &UIVirtualBoxManager::sltClosePreferencesDialog);
+ m_settings.value(UISettingsDialog::DialogType_Global)->load();
+ }
+
+ /* Expose instance: */
+ UIDesktopWidgetWatchdog::restoreWidget(m_settings.value(UISettingsDialog::DialogType_Global));
+}
+
+void UIVirtualBoxManager::sltClosePreferencesDialog()
+{
+ /* Remove instance if exist: */
+ delete m_settings.take(UISettingsDialog::DialogType_Global);
+}
+
+void UIVirtualBoxManager::sltPerformExit()
+{
+ close();
+}
+
+void UIVirtualBoxManager::sltOpenNewMachineWizard()
+{
+ openNewMachineWizard();
+}
+
+void UIVirtualBoxManager::sltOpenAddMachineDialog()
+{
+ /* Lock the actions preventing cascade calls: */
+ UIQObjectPropertySetter guardBlock(QList<QObject*>() << actionPool()->action(UIActionIndexMN_M_Welcome_S_Add)
+ << actionPool()->action(UIActionIndexMN_M_Machine_S_Add)
+ << actionPool()->action(UIActionIndexMN_M_Group_S_Add),
+ "opened", true);
+ connect(&guardBlock, &UIQObjectPropertySetter::sigAboutToBeDestroyed,
+ this, &UIVirtualBoxManager::sltHandleUpdateActionAppearanceRequest);
+ updateActionsAppearance();
+
+ /* Get first selected item: */
+ UIVirtualMachineItem *pItem = currentItem();
+
+ /* For global item or local machine: */
+ if ( !pItem
+ || pItem->itemType() == UIVirtualMachineItemType_Local)
+ {
+ /* Open add machine dialog: */
+ openAddMachineDialog();
+ }
+ /* For cloud machine: */
+ else
+ {
+ /* Use the "safe way" to open stack of Mac OS X Sheets: */
+ QWidget *pWizardParent = windowManager().realParentWindow(this);
+ UISafePointerWizardAddCloudVM pWizard = new UIWizardAddCloudVM(pWizardParent, m_pWidget->fullGroupName());
+ windowManager().registerNewParent(pWizard, pWizardParent);
+
+ /* Execute wizard: */
+ pWizard->exec();
+ delete pWizard;
+ }
+}
+
+void UIVirtualBoxManager::sltOpenGroupNameEditor()
+{
+ m_pWidget->openGroupNameEditor();
+}
+
+void UIVirtualBoxManager::sltDisbandGroup()
+{
+ m_pWidget->disbandGroup();
+}
+
+void UIVirtualBoxManager::sltOpenSettingsDialog(QString strCategory /* = QString() */,
+ QString strControl /* = QString() */,
+ const QUuid &uID /* = QString() */)
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+
+ /* For local machine: */
+ if (pItem->itemType() == UIVirtualMachineItemType_Local)
+ {
+ /* Process href from VM details / description: */
+ if (!strCategory.isEmpty() && strCategory[0] != '#')
+ {
+ uiCommon().openURL(strCategory);
+ }
+ else
+ {
+ /* Check if control is coded into the URL by %%: */
+ if (strControl.isEmpty())
+ {
+ QStringList parts = strCategory.split("%%");
+ if (parts.size() == 2)
+ {
+ strCategory = parts.at(0);
+ strControl = parts.at(1);
+ }
+ }
+
+ /* Don't show the inaccessible warning
+ * if the user tries to open VM settings: */
+ m_fFirstMediumEnumerationHandled = true;
+
+ /* Create instance if not yet created: */
+ if (!m_settings.contains(UISettingsDialog::DialogType_Machine))
+ {
+ m_settings[UISettingsDialog::DialogType_Machine] = new UISettingsDialogMachine(this,
+ uID.isNull() ? pItem->id() : uID,
+ actionPool(),
+ strCategory,
+ strControl);
+ connect(m_settings[UISettingsDialog::DialogType_Machine], &UISettingsDialogMachine::sigClose,
+ this, &UIVirtualBoxManager::sltCloseSettingsDialog);
+ m_settings.value(UISettingsDialog::DialogType_Machine)->load();
+ }
+
+ /* Expose instance: */
+ UIDesktopWidgetWatchdog::restoreWidget(m_settings.value(UISettingsDialog::DialogType_Machine));
+ }
+ }
+ /* For cloud machine: */
+ else
+ {
+ /* Create instance if not yet created: */
+ if (m_pCloudSettings.isNull())
+ {
+ m_pCloudSettings = new UICloudMachineSettingsDialog(this,
+ pItem->toCloud()->machine());
+ connect(m_pCloudSettings, &UICloudMachineSettingsDialog::sigClose,
+ this, &UIVirtualBoxManager::sltCloseSettingsDialog);
+ }
+
+ /* Expose instance: */
+ UIDesktopWidgetWatchdog::restoreWidget(m_pCloudSettings);
+ }
+}
+
+void UIVirtualBoxManager::sltCloseSettingsDialog()
+{
+ /* What type of dialog should we delete? */
+ enum DelType { None, Local, Cloud, All } enmType = None;
+ if (qobject_cast<UISettingsDialog*>(sender()))
+ enmType = (DelType)(enmType | Local);
+ else if (qobject_cast<UICloudMachineSettingsDialog*>(sender()))
+ enmType = (DelType)(enmType | Cloud);
+
+ /* It's all if nothing: */
+ if (enmType == None)
+ enmType = All;
+
+ /* Remove requested instances: */
+ if (enmType & Local)
+ delete m_settings.take(UISettingsDialog::DialogType_Machine);
+ if (enmType & Cloud)
+ delete m_pCloudSettings;
+}
+
+void UIVirtualBoxManager::sltOpenCloneMachineWizard()
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+ /* Make sure current item is local one: */
+ UIVirtualMachineItemLocal *pItemLocal = pItem->toLocal();
+ AssertMsgReturnVoid(pItemLocal, ("Current item should be local one!\n"));
+
+ /* Use the "safe way" to open stack of Mac OS X Sheets: */
+ QWidget *pWizardParent = windowManager().realParentWindow(this);
+ const QStringList &machineGroupNames = pItemLocal->groups();
+ const QString strGroup = !machineGroupNames.isEmpty() ? machineGroupNames.at(0) : QString();
+ QPointer<UINativeWizard> pWizard = new UIWizardCloneVM(pWizardParent, pItemLocal->machine(), strGroup, CSnapshot());
+ windowManager().registerNewParent(pWizard, pWizardParent);
+ pWizard->exec();
+ delete pWizard;
+}
+
+void UIVirtualBoxManager::sltPerformMachineMove()
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+
+ /* Open a file dialog for the user to select a destination folder. Start with the default machine folder: */
+ const QString strBaseFolder = uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder();
+ const QString strTitle = tr("Select a destination folder to move the selected virtual machine");
+ const QString strDestinationFolder = QIFileDialog::getExistingDirectory(strBaseFolder, this, strTitle);
+ if (!strDestinationFolder.isEmpty())
+ {
+ /* Move machine: */
+ UINotificationProgressMachineMove *pNotification = new UINotificationProgressMachineMove(pItem->id(),
+ strDestinationFolder,
+ "basic");
+ gpNotificationCenter->append(pNotification);
+ }
+}
+
+void UIVirtualBoxManager::sltPerformMachineRemove()
+{
+ m_pWidget->removeMachine();
+}
+
+void UIVirtualBoxManager::sltPerformMachineMoveToNewGroup()
+{
+ m_pWidget->moveMachineToGroup();
+}
+
+void UIVirtualBoxManager::sltPerformMachineMoveToSpecificGroup()
+{
+ AssertPtrReturnVoid(sender());
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertPtrReturnVoid(pAction);
+ m_pWidget->moveMachineToGroup(pAction->property("actual_group_name").toString());
+}
+
+void UIVirtualBoxManager::sltPerformStartOrShowMachine()
+{
+ /* Start selected VMs in corresponding mode: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+ performStartOrShowVirtualMachines(items, UILaunchMode_Invalid);
+}
+
+void UIVirtualBoxManager::sltPerformStartMachineNormal()
+{
+ /* Start selected VMs in corresponding mode: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+ performStartOrShowVirtualMachines(items, UILaunchMode_Default);
+}
+
+void UIVirtualBoxManager::sltPerformStartMachineHeadless()
+{
+ /* Start selected VMs in corresponding mode: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+ performStartOrShowVirtualMachines(items, UILaunchMode_Headless);
+}
+
+void UIVirtualBoxManager::sltPerformStartMachineDetachable()
+{
+ /* Start selected VMs in corresponding mode: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+ performStartOrShowVirtualMachines(items, UILaunchMode_Separate);
+}
+
+void UIVirtualBoxManager::sltPerformCreateConsoleConnectionForGroup()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* Create input dialog to pass public key to newly created console connection: */
+ QPointer<UIAcquirePublicKeyDialog> pDialog = new UIAcquirePublicKeyDialog(this);
+ if (pDialog)
+ {
+ if (pDialog->exec() == QDialog::Accepted)
+ {
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* Make sure the item exists: */
+ AssertPtr(pItem);
+ if (pItem)
+ {
+ /* Make sure the item is of cloud type: */
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ if (pCloudItem)
+ {
+ /* Acquire current machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+
+ /* Acquire machine console connection fingerprint: */
+ QString strConsoleConnectionFingerprint;
+ if (cloudMachineConsoleConnectionFingerprint(comMachine, strConsoleConnectionFingerprint))
+ {
+ /* Only if no fingerprint exist: */
+ if (strConsoleConnectionFingerprint.isEmpty())
+ {
+ /* Create cloud console connection: */
+ UINotificationProgressCloudConsoleConnectionCreate *pNotification =
+ new UINotificationProgressCloudConsoleConnectionCreate(comMachine,
+ pDialog->publicKey());
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+ }
+ }
+ }
+ }
+ delete pDialog;
+ }
+}
+
+void UIVirtualBoxManager::sltPerformCreateConsoleConnectionForMachine()
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+
+ /* Create input dialog to pass public key to newly created console connection: */
+ QPointer<UIAcquirePublicKeyDialog> pDialog = new UIAcquirePublicKeyDialog(this);
+ if (pDialog)
+ {
+ if (pDialog->exec() == QDialog::Accepted)
+ {
+ /* Make sure the item is of cloud type: */
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ AssertPtr(pCloudItem);
+ if (pCloudItem)
+ {
+ /* Acquire current machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+
+ /* Acquire machine console connection fingerprint: */
+ QString strConsoleConnectionFingerprint;
+ if (cloudMachineConsoleConnectionFingerprint(comMachine, strConsoleConnectionFingerprint))
+ {
+ /* Only if no fingerprint exist: */
+ if (strConsoleConnectionFingerprint.isEmpty())
+ {
+ /* Create cloud console connection: */
+ UINotificationProgressCloudConsoleConnectionCreate *pNotification =
+ new UINotificationProgressCloudConsoleConnectionCreate(comMachine,
+ pDialog->publicKey());
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+ }
+ }
+ delete pDialog;
+ }
+}
+
+void UIVirtualBoxManager::sltPerformDeleteConsoleConnectionForGroup()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* Make sure the item exists: */
+ AssertPtr(pItem);
+ if (pItem)
+ {
+ /* Make sure the item is of cloud type: */
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ if (pCloudItem)
+ {
+ /* Acquire current machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+
+ /* Acquire machine console connection fingerprint: */
+ QString strConsoleConnectionFingerprint;
+ if (cloudMachineConsoleConnectionFingerprint(comMachine, strConsoleConnectionFingerprint))
+ {
+ /* Only if fingerprint exists: */
+ if (!strConsoleConnectionFingerprint.isEmpty())
+ {
+ /* Delete cloud console connection: */
+ UINotificationProgressCloudConsoleConnectionDelete *pNotification =
+ new UINotificationProgressCloudConsoleConnectionDelete(comMachine);
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+ }
+ }
+ }
+}
+
+void UIVirtualBoxManager::sltPerformDeleteConsoleConnectionForMachine()
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+
+ /* Make sure the item is of cloud type: */
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ AssertPtr(pCloudItem);
+ if (pCloudItem)
+ {
+ /* Acquire current machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+
+ /* Acquire machine console connection fingerprint: */
+ QString strConsoleConnectionFingerprint;
+ if (cloudMachineConsoleConnectionFingerprint(comMachine, strConsoleConnectionFingerprint))
+ {
+ /* Only if fingerprint exists: */
+ if (!strConsoleConnectionFingerprint.isEmpty())
+ {
+ /* Delete cloud console connection: */
+ UINotificationProgressCloudConsoleConnectionDelete *pNotification =
+ new UINotificationProgressCloudConsoleConnectionDelete(comMachine);
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+ }
+}
+
+void UIVirtualBoxManager::sltCopyConsoleConnectionFingerprint()
+{
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertPtrReturnVoid(pAction);
+ QClipboard *pClipboard = QGuiApplication::clipboard();
+ AssertPtrReturnVoid(pClipboard);
+ pClipboard->setText(pAction->property("fingerprint").toString());
+}
+
+void UIVirtualBoxManager::sltExecuteExternalApplication()
+{
+ /* Acquire passed path and argument strings: */
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertMsgReturnVoid(pAction, ("This slot should be called by action only!\n"));
+ const QString strPath = pAction->property("path").toString();
+ const QString strArguments = pAction->property("arguments").toString();
+
+ /* Get current-item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ AssertPtrReturnVoid(pCloudItem);
+
+ /* Get cloud machine to acquire serial command: */
+ const CCloudMachine comMachine = pCloudItem->machine();
+
+#if defined(VBOX_WS_MAC)
+ /* Gather arguments: */
+ QStringList arguments;
+ arguments << parseShellArguments(strArguments);
+
+ /* Make sure that isn't a request to start Open command: */
+ if (strPath != "open" && strPath != "/usr/bin/open")
+ {
+ /* In that case just add the command we have as simple argument: */
+ arguments << comMachine.GetSerialConsoleCommand();
+ }
+ else
+ {
+ /* Otherwise upload command to external file which can be opened with Open command: */
+ QDir uiHomeFolder(uiCommon().virtualBox().GetHomeFolder());
+ const QString strAbsoluteCommandName = uiHomeFolder.absoluteFilePath("last.command");
+ QFile file(strAbsoluteCommandName);
+ file.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
+ if (!file.open(QIODevice::WriteOnly))
+ AssertFailedReturnVoid();
+ file.write(comMachine.GetSerialConsoleCommand().toUtf8());
+ file.close();
+ arguments << strAbsoluteCommandName;
+ }
+
+ /* Execute console application finally: */
+ QProcess::startDetached(strPath, arguments);
+#elif defined(VBOX_WS_WIN)
+ /* Gather arguments: */
+ QStringList arguments;
+ arguments << strArguments;
+ arguments << comMachine.GetSerialConsoleCommandWindows();
+
+ /* Execute console application finally: */
+ QProcess::startDetached(QString("%1 %2").arg(strPath, arguments.join(' ')));
+#elif defined(VBOX_WS_X11)
+ /* Gather arguments: */
+ QStringList arguments;
+ arguments << parseShellArguments(strArguments);
+ arguments << comMachine.GetSerialConsoleCommand();
+
+ /* Execute console application finally: */
+ QProcess::startDetached(strPath, arguments);
+#endif /* VBOX_WS_X11 */
+}
+
+void UIVirtualBoxManager::sltPerformCopyCommandSerialUnix()
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ AssertPtrReturnVoid(pCloudItem);
+
+ /* Acquire cloud machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+
+ /* Put copied serial command to clipboard: */
+ QClipboard *pClipboard = QGuiApplication::clipboard();
+ AssertPtrReturnVoid(pClipboard);
+ pClipboard->setText(comMachine.GetSerialConsoleCommand());
+}
+
+void UIVirtualBoxManager::sltPerformCopyCommandSerialWindows()
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ AssertPtrReturnVoid(pCloudItem);
+
+ /* Acquire cloud machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+
+ /* Put copied serial command to clipboard: */
+ QClipboard *pClipboard = QGuiApplication::clipboard();
+ AssertPtrReturnVoid(pClipboard);
+ pClipboard->setText(comMachine.GetSerialConsoleCommandWindows());
+}
+
+void UIVirtualBoxManager::sltPerformCopyCommandVNCUnix()
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ AssertPtrReturnVoid(pCloudItem);
+
+ /* Acquire cloud machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+
+ /* Put copied VNC command to clipboard: */
+ QClipboard *pClipboard = QGuiApplication::clipboard();
+ AssertPtrReturnVoid(pClipboard);
+ pClipboard->setText(comMachine.GetVNCConsoleCommand());
+}
+
+void UIVirtualBoxManager::sltPerformCopyCommandVNCWindows()
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ AssertPtrReturnVoid(pCloudItem);
+
+ /* Acquire cloud machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+
+ /* Put copied VNC command to clipboard: */
+ QClipboard *pClipboard = QGuiApplication::clipboard();
+ AssertPtrReturnVoid(pClipboard);
+ pClipboard->setText(comMachine.GetVNCConsoleCommandWindows());
+}
+
+void UIVirtualBoxManager::sltPerformShowLog()
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ AssertPtrReturnVoid(pCloudItem);
+
+ /* Acquire cloud machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+
+ /* Requesting cloud console log: */
+ UINotificationProgressCloudConsoleLogAcquire *pNotification = new UINotificationProgressCloudConsoleLogAcquire(comMachine);
+ connect(pNotification, &UINotificationProgressCloudConsoleLogAcquire::sigLogRead,
+ this, &UIVirtualBoxManager::sltHandleConsoleLogRead);
+ gpNotificationCenter->append(pNotification);
+}
+
+void UIVirtualBoxManager::sltHandleConsoleLogRead(const QString &strName, const QString &strLog)
+{
+ /* Prepare dialog: */
+ QWidget *pWindow = new QWidget(this, Qt::Window);
+ if (pWindow)
+ {
+ pWindow->setAttribute(Qt::WA_DeleteOnClose);
+ pWindow->setWindowTitle(QString("%1 - Console Log").arg(strName));
+
+ QVBoxLayout *pLayout = new QVBoxLayout(pWindow);
+ if (pLayout)
+ {
+ QTextEdit *pTextEdit = new QTextEdit(pWindow);
+ if (pTextEdit)
+ {
+ pTextEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
+ pTextEdit->setReadOnly(true);
+ pTextEdit->setText(strLog);
+ pLayout->addWidget(pTextEdit);
+ }
+ }
+ }
+
+ /* Show dialog: */
+ pWindow->show();
+}
+
+void UIVirtualBoxManager::sltPerformDiscardMachineState()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* Prepare the list of the machines to be discarded/terminated: */
+ QStringList machinesToDiscard;
+ QList<UIVirtualMachineItem*> itemsToDiscard;
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if (isActionEnabled(UIActionIndexMN_M_Group_S_Discard, QList<UIVirtualMachineItem*>() << pItem))
+ {
+ machinesToDiscard << pItem->name();
+ itemsToDiscard << pItem;
+ }
+ }
+ AssertMsg(!machinesToDiscard.isEmpty(), ("This action should not be allowed!"));
+
+ /* Confirm discarding: */
+ if ( machinesToDiscard.isEmpty()
+ || !msgCenter().confirmDiscardSavedState(machinesToDiscard.join(", ")))
+ return;
+
+ /* For every confirmed item to discard: */
+ foreach (UIVirtualMachineItem *pItem, itemsToDiscard)
+ {
+ /* Open a session to modify VM: */
+ AssertPtrReturnVoid(pItem);
+ CSession comSession = uiCommon().openSession(pItem->id());
+ if (comSession.isNull())
+ return;
+
+ /* Get session machine: */
+ CMachine comMachine = comSession.GetMachine();
+ comMachine.DiscardSavedState(true);
+ if (!comMachine.isOk())
+ UINotificationMessage::cannotDiscardSavedState(comMachine);
+
+ /* Unlock machine finally: */
+ comSession.UnlockMachine();
+ }
+}
+
+void UIVirtualBoxManager::sltPerformPauseOrResumeMachine(bool fPause)
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* For every selected item: */
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* But for local machine items only: */
+ AssertPtrReturnVoid(pItem);
+ if (pItem->itemType() != UIVirtualMachineItemType_Local)
+ continue;
+
+ /* Get local machine item state: */
+ UIVirtualMachineItemLocal *pLocalItem = pItem->toLocal();
+ AssertPtrReturnVoid(pLocalItem);
+ const KMachineState enmState = pLocalItem->machineState();
+
+ /* Check if current item could be paused/resumed: */
+ if (!isActionEnabled(UIActionIndexMN_M_Group_T_Pause, QList<UIVirtualMachineItem*>() << pItem))
+ continue;
+
+ /* Check if current item already paused: */
+ if (fPause &&
+ (enmState == KMachineState_Paused ||
+ enmState == KMachineState_TeleportingPausedVM))
+ continue;
+
+ /* Check if current item already resumed: */
+ if (!fPause &&
+ (enmState == KMachineState_Running ||
+ enmState == KMachineState_Teleporting ||
+ enmState == KMachineState_LiveSnapshotting))
+ continue;
+
+ /* Open a session to modify VM state: */
+ CSession comSession = uiCommon().openExistingSession(pItem->id());
+ if (comSession.isNull())
+ return;
+
+ /* Get session console: */
+ CConsole comConsole = comSession.GetConsole();
+ /* Pause/resume VM: */
+ if (fPause)
+ comConsole.Pause();
+ else
+ comConsole.Resume();
+ if (!comConsole.isOk())
+ {
+ if (fPause)
+ UINotificationMessage::cannotPauseMachine(comConsole);
+ else
+ UINotificationMessage::cannotResumeMachine(comConsole);
+ }
+
+ /* Unlock machine finally: */
+ comSession.UnlockMachine();
+ }
+}
+
+void UIVirtualBoxManager::sltPerformResetMachine()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* Prepare the list of the machines to be reseted: */
+ QStringList machineNames;
+ QList<UIVirtualMachineItem*> itemsToReset;
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if (isActionEnabled(UIActionIndexMN_M_Group_S_Reset, QList<UIVirtualMachineItem*>() << pItem))
+ {
+ machineNames << pItem->name();
+ itemsToReset << pItem;
+ }
+ }
+ AssertMsg(!machineNames.isEmpty(), ("This action should not be allowed!"));
+
+ /* Confirm reseting VM: */
+ if (!msgCenter().confirmResetMachine(machineNames.join(", ")))
+ return;
+
+ /* For each selected item: */
+ foreach (UIVirtualMachineItem *pItem, itemsToReset)
+ {
+ /* Open a session to modify VM state: */
+ CSession comSession = uiCommon().openExistingSession(pItem->id());
+ if (comSession.isNull())
+ return;
+
+ /* Get session console: */
+ CConsole comConsole = comSession.GetConsole();
+ /* Reset VM: */
+ comConsole.Reset();
+
+ /* Unlock machine finally: */
+ comSession.UnlockMachine();
+ }
+}
+
+void UIVirtualBoxManager::sltPerformDetachMachineUI()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* For each selected item: */
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* Check if current item could be detached: */
+ if (!isActionEnabled(UIActionIndexMN_M_Machine_S_Detach, QList<UIVirtualMachineItem*>() << pItem))
+ continue;
+
+ /// @todo Detach separate UI process..
+ AssertFailed();
+ }
+}
+
+void UIVirtualBoxManager::sltPerformSaveMachineState()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* For each selected item: */
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* Sanity check: */
+ AssertPtrReturnVoid(pItem);
+ AssertPtrReturnVoid(pItem->toLocal());
+
+ /* Check if current item could be saved: */
+ if (!isActionEnabled(UIActionIndexMN_M_Machine_M_Stop_S_SaveState, QList<UIVirtualMachineItem*>() << pItem))
+ continue;
+
+ /* Saving VM state: */
+ UINotificationProgressMachineSaveState *pNotification = new UINotificationProgressMachineSaveState(pItem->toLocal()->machine());
+ gpNotificationCenter->append(pNotification);
+ }
+}
+
+void UIVirtualBoxManager::sltPerformTerminateMachine()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* Prepare the list of the machines to be terminated: */
+ QStringList machinesToTerminate;
+ QList<UIVirtualMachineItem*> itemsToTerminate;
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if (isActionEnabled(UIActionIndexMN_M_Group_M_Stop_S_Terminate, QList<UIVirtualMachineItem*>() << pItem))
+ {
+ machinesToTerminate << pItem->name();
+ itemsToTerminate << pItem;
+ }
+ }
+ AssertMsg(!machinesToTerminate.isEmpty(), ("This action should not be allowed!"));
+
+ /* Confirm terminating: */
+ if ( machinesToTerminate.isEmpty()
+ || !msgCenter().confirmTerminateCloudInstance(machinesToTerminate.join(", ")))
+ return;
+
+ /* For every confirmed item to terminate: */
+ foreach (UIVirtualMachineItem *pItem, itemsToTerminate)
+ {
+ /* Sanity check: */
+ AssertPtrReturnVoid(pItem);
+
+ /* Terminating cloud VM: */
+ UINotificationProgressCloudMachineTerminate *pNotification =
+ new UINotificationProgressCloudMachineTerminate(pItem->toCloud()->machine());
+ gpNotificationCenter->append(pNotification);
+ }
+}
+
+void UIVirtualBoxManager::sltPerformShutdownMachine()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* Prepare the list of the machines to be shutdowned: */
+ QStringList machineNames;
+ QList<UIVirtualMachineItem*> itemsToShutdown;
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if (isActionEnabled(UIActionIndexMN_M_Machine_M_Stop_S_Shutdown, QList<UIVirtualMachineItem*>() << pItem))
+ {
+ machineNames << pItem->name();
+ itemsToShutdown << pItem;
+ }
+ }
+ AssertMsg(!machineNames.isEmpty(), ("This action should not be allowed!"));
+
+ /* Confirm ACPI shutdown current VM: */
+ if (!msgCenter().confirmACPIShutdownMachine(machineNames.join(", ")))
+ return;
+
+ /* For each selected item: */
+ foreach (UIVirtualMachineItem *pItem, itemsToShutdown)
+ {
+ /* Sanity check: */
+ AssertPtrReturnVoid(pItem);
+
+ /* For local machine: */
+ if (pItem->itemType() == UIVirtualMachineItemType_Local)
+ {
+ /* Open a session to modify VM state: */
+ CSession comSession = uiCommon().openExistingSession(pItem->id());
+ if (comSession.isNull())
+ return;
+
+ /* Get session console: */
+ CConsole comConsole = comSession.GetConsole();
+ /* ACPI Shutdown: */
+ comConsole.PowerButton();
+ if (!comConsole.isOk())
+ UINotificationMessage::cannotACPIShutdownMachine(comConsole);
+
+ /* Unlock machine finally: */
+ comSession.UnlockMachine();
+ }
+ /* For real cloud machine: */
+ else if (pItem->itemType() == UIVirtualMachineItemType_CloudReal)
+ {
+ /* Shutting cloud VM down: */
+ UINotificationProgressCloudMachineShutdown *pNotification =
+ new UINotificationProgressCloudMachineShutdown(pItem->toCloud()->machine());
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+}
+
+void UIVirtualBoxManager::sltPerformPowerOffMachine()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* Prepare the list of the machines to be powered off: */
+ QStringList machineNames;
+ QList<UIVirtualMachineItem*> itemsToPowerOff;
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if (isActionEnabled(UIActionIndexMN_M_Machine_M_Stop_S_PowerOff, QList<UIVirtualMachineItem*>() << pItem))
+ {
+ machineNames << pItem->name();
+ itemsToPowerOff << pItem;
+ }
+ }
+ AssertMsg(!machineNames.isEmpty(), ("This action should not be allowed!"));
+
+ /* Confirm Power Off current VM: */
+ if (!msgCenter().confirmPowerOffMachine(machineNames.join(", ")))
+ return;
+
+ /* For each selected item: */
+ foreach (UIVirtualMachineItem *pItem, itemsToPowerOff)
+ {
+ /* Sanity check: */
+ AssertPtrReturnVoid(pItem);
+
+ /* For local machine: */
+ if (pItem->itemType() == UIVirtualMachineItemType_Local)
+ {
+ /* Powering VM off: */
+ UINotificationProgressMachinePowerOff *pNotification =
+ new UINotificationProgressMachinePowerOff(pItem->toLocal()->machine(),
+ CConsole() /* dummy */,
+ gEDataManager->discardStateOnPowerOff(pItem->id()));
+ pNotification->setProperty("machine_id", pItem->id());
+ connect(pNotification, &UINotificationProgressMachinePowerOff::sigMachinePoweredOff,
+ this, &UIVirtualBoxManager::sltHandlePoweredOffMachine);
+ gpNotificationCenter->append(pNotification);
+ }
+ /* For real cloud machine: */
+ else if (pItem->itemType() == UIVirtualMachineItemType_CloudReal)
+ {
+ /* Powering cloud VM off: */
+ UINotificationProgressCloudMachinePowerOff *pNotification =
+ new UINotificationProgressCloudMachinePowerOff(pItem->toCloud()->machine());
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+}
+
+void UIVirtualBoxManager::sltHandlePoweredOffMachine(bool fSuccess, bool fIncludingDiscard)
+{
+ /* Was previous step successful? */
+ if (fSuccess)
+ {
+ /* Do we have other tasks? */
+ if (fIncludingDiscard)
+ {
+ /* Discard state if requested: */
+ AssertPtrReturnVoid(sender());
+ UINotificationProgressSnapshotRestore *pNotification =
+ new UINotificationProgressSnapshotRestore(sender()->property("machine_id").toUuid());
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+}
+
+void UIVirtualBoxManager::sltPerformShowGlobalTool(QAction *pAction)
+{
+ AssertPtrReturnVoid(pAction);
+ AssertPtrReturnVoid(m_pWidget);
+ m_pWidget->switchToGlobalItem();
+ m_pWidget->setToolsType(pAction->property("UIToolType").value<UIToolType>());
+}
+
+void UIVirtualBoxManager::sltPerformShowMachineTool(QAction *pAction)
+{
+ AssertPtrReturnVoid(pAction);
+ AssertPtrReturnVoid(m_pWidget);
+ m_pWidget->setToolsType(pAction->property("UIToolType").value<UIToolType>());
+}
+
+void UIVirtualBoxManager::sltOpenLogViewerWindow()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* First check if instance of widget opened the embedded way: */
+ if (m_pWidget->isMachineToolOpened(UIToolType_Logs))
+ {
+ m_pWidget->setToolsType(UIToolType_Details);
+ m_pWidget->closeMachineTool(UIToolType_Logs);
+ }
+
+ QList<UIVirtualMachineItem*> itemsToShowLogs;
+
+ /* For each selected item: */
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* Make sure current item is local one: */
+ UIVirtualMachineItemLocal *pItemLocal = pItem->toLocal();
+ if (!pItemLocal)
+ continue;
+
+ /* Check if log could be show for the current item: */
+ if (!isActionEnabled(UIActionIndexMN_M_Group_S_ShowLogDialog, QList<UIVirtualMachineItem*>() << pItem))
+ continue;
+ itemsToShowLogs << pItem;
+ }
+
+ if (itemsToShowLogs.isEmpty())
+ return;
+ if (!m_pLogViewerDialog)
+ {
+ UIVMLogViewerDialogFactory dialogFactory(actionPool(), QUuid());
+ dialogFactory.prepare(m_pLogViewerDialog, this);
+ if (m_pLogViewerDialog)
+ connect(m_pLogViewerDialog, &QIManagerDialog::sigClose,
+ this, &UIVirtualBoxManager::sltCloseLogViewerWindow);
+ }
+ AssertPtrReturnVoid(m_pLogViewerDialog);
+ UIVMLogViewerDialog *pDialog = qobject_cast<UIVMLogViewerDialog*>(m_pLogViewerDialog);
+ if (pDialog)
+ pDialog->addSelectedVMListItems(itemsToShowLogs);
+ m_pLogViewerDialog->show();
+ m_pLogViewerDialog->setWindowState(m_pLogViewerDialog->windowState() & ~Qt::WindowMinimized);
+ m_pLogViewerDialog->activateWindow();
+}
+
+void UIVirtualBoxManager::sltCloseLogViewerWindow()
+{
+ if (!m_pLogViewerDialog)
+ return;
+
+ QIManagerDialog* pDialog = m_pLogViewerDialog;
+ m_pLogViewerDialog = 0;
+ pDialog->close();
+ UIVMLogViewerDialogFactory().cleanup(pDialog);
+}
+
+void UIVirtualBoxManager::sltPerformRefreshMachine()
+{
+ m_pWidget->refreshMachine();
+}
+
+void UIVirtualBoxManager::sltShowMachineInFileManager()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* For each selected item: */
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* Make sure current item is local one: */
+ UIVirtualMachineItemLocal *pItemLocal = pItem->toLocal();
+ if (!pItemLocal)
+ continue;
+
+ /* Check if that item could be shown in file-browser: */
+ if (!isActionEnabled(UIActionIndexMN_M_Group_S_ShowInFileManager, QList<UIVirtualMachineItem*>() << pItem))
+ continue;
+
+ /* Show VM in filebrowser: */
+ UIDesktopServices::openInFileManager(pItemLocal->machine().GetSettingsFilePath());
+ }
+}
+
+void UIVirtualBoxManager::sltPerformCreateMachineShortcut()
+{
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* For each selected item: */
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* Make sure current item is local one: */
+ UIVirtualMachineItemLocal *pItemLocal = pItem->toLocal();
+ if (!pItemLocal)
+ continue;
+
+ /* Check if shortcuts could be created for this item: */
+ if (!isActionEnabled(UIActionIndexMN_M_Group_S_CreateShortcut, QList<UIVirtualMachineItem*>() << pItem))
+ continue;
+
+ /* Create shortcut for this VM: */
+ const CMachine &comMachine = pItemLocal->machine();
+ UIDesktopServices::createMachineShortcut(comMachine.GetSettingsFilePath(),
+ QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
+ comMachine.GetName(), comMachine.GetId());
+ }
+}
+
+void UIVirtualBoxManager::sltPerformGroupSorting()
+{
+ m_pWidget->sortGroup();
+}
+
+void UIVirtualBoxManager::sltPerformMachineSearchWidgetVisibilityToggling(bool fVisible)
+{
+ m_pWidget->setMachineSearchWidgetVisibility(fVisible);
+}
+
+void UIVirtualBoxManager::sltPerformShowHelpBrowser()
+{
+ m_pWidget->showHelpBrowser();
+}
+
+void UIVirtualBoxManager::prepare()
+{
+#ifdef VBOX_WS_X11
+ /* Assign same name to both WM_CLASS name & class for now: */
+ NativeWindowSubsystem::X11SetWMClass(this, "VirtualBox Manager", "VirtualBox Manager");
+#endif
+
+#ifdef VBOX_WS_MAC
+ /* We have to make sure that we are getting the front most process: */
+ ::darwinSetFrontMostProcess();
+ /* Install global event-filter, since vmstarter.app can send us FileOpen events,
+ * see UIVirtualBoxManager::eventFilter for handler implementation. */
+ qApp->installEventFilter(this);
+#endif
+
+ /* Cache media data early if necessary: */
+ if (uiCommon().agressiveCaching())
+ uiCommon().enumerateMedia();
+
+ /* Prepare: */
+ prepareIcon();
+ prepareMenuBar();
+ prepareStatusBar();
+ prepareWidgets();
+ prepareConnections();
+
+ /* Update actions initially: */
+ sltHandleChooserPaneIndexChange();
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Translate UI: */
+ retranslateUi();
+
+#ifdef VBOX_WS_MAC
+ /* Beta label? */
+ if (uiCommon().showBetaLabel())
+ {
+ QPixmap betaLabel = ::betaLabel(QSize(74, darwinWindowTitleHeight(this) - 1));
+ ::darwinLabelWindow(this, &betaLabel);
+ }
+#endif /* VBOX_WS_MAC */
+
+ /* If there are unhandled URLs we should handle them after manager is shown: */
+ if (uiCommon().argumentUrlsPresent())
+ QMetaObject::invokeMethod(this, "sltHandleOpenUrlCall", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "sltCheckUSBAccesibility", Qt::QueuedConnection);
+}
+
+void UIVirtualBoxManager::prepareIcon()
+{
+ /* Prepare application icon.
+ * On Win host it's built-in to the executable.
+ * On Mac OS X the icon referenced in info.plist is used.
+ * On X11 we will provide as much icons as we can. */
+#if !defined(VBOX_WS_WIN) && !defined(VBOX_WS_MAC)
+ QIcon icon(":/VirtualBox.svg");
+ icon.addFile(":/VirtualBox_48px.png");
+ icon.addFile(":/VirtualBox_64px.png");
+ setWindowIcon(icon);
+#endif /* !VBOX_WS_WIN && !VBOX_WS_MAC */
+}
+
+void UIVirtualBoxManager::prepareMenuBar()
+{
+#ifndef VBOX_WS_MAC
+ /* Create menu-bar: */
+ setMenuBar(new UIMenuBar);
+ if (menuBar())
+ {
+ /* Make sure menu-bar fills own solid background: */
+ menuBar()->setAutoFillBackground(true);
+# ifdef VBOX_WS_WIN
+ // WORKAROUND:
+ // On Windows we have to override Windows Vista style with style-sheet:
+ menuBar()->setStyleSheet(QString("QMenuBar { background-color: %1; }")
+ .arg(QApplication::palette().color(QPalette::Active, QPalette::Window).name(QColor::HexRgb)));
+# endif
+ }
+#endif
+
+ /* Create action-pool: */
+ m_pActionPool = UIActionPool::create(UIActionPoolType_Manager);
+
+ /* Prepare menu update-handlers: */
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group] = &UIVirtualBoxManager::updateMenuGroup;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine] = &UIVirtualBoxManager::updateMenuMachine;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group_M_MoveToGroup] = &UIVirtualBoxManager::updateMenuGroupMoveToGroup;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group_M_Console] = &UIVirtualBoxManager::updateMenuGroupConsole;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Group_M_Stop] = &UIVirtualBoxManager::updateMenuGroupClose;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine_M_MoveToGroup] = &UIVirtualBoxManager::updateMenuMachineMoveToGroup;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine_M_Console] = &UIVirtualBoxManager::updateMenuMachineConsole;
+ m_menuUpdateHandlers[UIActionIndexMN_M_Machine_M_Stop] = &UIVirtualBoxManager::updateMenuMachineClose;
+
+ /* Build menu-bar: */
+ foreach (QMenu *pMenu, actionPool()->menus())
+ {
+#ifdef VBOX_WS_MAC
+ /* Before 'Help' menu we should: */
+ if (pMenu == actionPool()->action(UIActionIndex_Menu_Help)->menu())
+ {
+ /* Insert 'Window' menu: */
+ UIWindowMenuManager::create();
+ menuBar()->addMenu(gpWindowMenuManager->createMenu(this));
+ gpWindowMenuManager->addWindow(this);
+ }
+#endif
+ menuBar()->addMenu(pMenu);
+ }
+
+ /* Setup menu-bar policy: */
+ menuBar()->setContextMenuPolicy(Qt::CustomContextMenu);
+}
+
+void UIVirtualBoxManager::prepareStatusBar()
+{
+ /* We are not using status-bar anymore: */
+ statusBar()->setHidden(true);
+}
+
+void UIVirtualBoxManager::prepareWidgets()
+{
+ /* Prepare central-widget: */
+ m_pWidget = new UIVirtualBoxManagerWidget(this);
+ if (m_pWidget)
+ setCentralWidget(m_pWidget);
+}
+
+void UIVirtualBoxManager::prepareConnections()
+{
+#ifdef VBOX_WS_X11
+ /* Desktop event handlers: */
+ connect(gpDesktop, &UIDesktopWidgetWatchdog::sigHostScreenWorkAreaResized,
+ this, &UIVirtualBoxManager::sltHandleHostScreenAvailableAreaChange);
+#endif
+
+ /* UICommon connections: */
+ connect(&uiCommon(), &UICommon::sigAskToCommitData,
+ this, &UIVirtualBoxManager::sltHandleCommitData);
+ connect(&uiCommon(), &UICommon::sigMediumEnumerationFinished,
+ this, &UIVirtualBoxManager::sltHandleMediumEnumerationFinish);
+
+ /* Widget connections: */
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigChooserPaneIndexChange,
+ this, &UIVirtualBoxManager::sltHandleChooserPaneIndexChange);
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigGroupSavingStateChanged,
+ this, &UIVirtualBoxManager::sltHandleGroupSavingProgressChange);
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigCloudUpdateStateChanged,
+ this, &UIVirtualBoxManager::sltHandleCloudUpdateProgressChange);
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigStartOrShowRequest,
+ this, &UIVirtualBoxManager::sltPerformStartOrShowMachine);
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigCloudMachineStateChange,
+ this, &UIVirtualBoxManager::sltHandleCloudMachineStateChange);
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigToolTypeChange,
+ this, &UIVirtualBoxManager::sltHandleToolTypeChange);
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigMachineSettingsLinkClicked,
+ this, &UIVirtualBoxManager::sltOpenSettingsDialog);
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigCurrentSnapshotItemChange,
+ this, &UIVirtualBoxManager::sltCurrentSnapshotItemChange);
+ connect(menuBar(), &QMenuBar::customContextMenuRequested,
+ m_pWidget, &UIVirtualBoxManagerWidget::sltHandleToolBarContextMenuRequest);
+
+ /* Global VBox event handlers: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UIVirtualBoxManager::sltHandleStateChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSessionStateChange,
+ this, &UIVirtualBoxManager::sltHandleStateChange);
+
+ /* General action-pool connections: */
+ connect(actionPool(), &UIActionPool::sigNotifyAboutMenuPrepare, this, &UIVirtualBoxManager::sltHandleMenuPrepare);
+
+ /* 'File' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_File_S_ImportAppliance), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenImportApplianceWizardDefault);
+ connect(actionPool()->action(UIActionIndexMN_M_File_S_ExportAppliance), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenExportApplianceWizard);
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ connect(actionPool()->action(UIActionIndexMN_M_File_S_ShowExtraDataManager), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenExtraDataManagerWindow);
+#endif /* VBOX_GUI_WITH_EXTRADATA_MANAGER_UI */
+ connect(actionPool()->action(UIActionIndex_M_Application_S_Preferences), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenPreferencesDialog);
+ connect(actionPool()->action(UIActionIndexMN_M_File_S_Close), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformExit);
+ connect(actionPool()->actionGroup(UIActionIndexMN_M_File_M_Tools), &QActionGroup::triggered,
+ this, &UIVirtualBoxManager::sltPerformShowGlobalTool);
+
+ /* 'Welcome' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_Welcome_S_New), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenNewMachineWizard);
+ connect(actionPool()->action(UIActionIndexMN_M_Welcome_S_Add), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenAddMachineDialog);
+
+ /* 'Group' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_New), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenNewMachineWizard);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_Add), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenAddMachineDialog);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_Rename), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenGroupNameEditor);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_Remove), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltDisbandGroup);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformStartOrShowMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_T_Pause), &UIAction::toggled,
+ this, &UIVirtualBoxManager::sltPerformPauseOrResumeMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_Reset), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformResetMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_Detach), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformDetachMachineUI);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_Discard), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformDiscardMachineState);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_ShowLogDialog), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenLogViewerWindow);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_Refresh), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformRefreshMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_ShowInFileManager), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltShowMachineInFileManager);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_CreateShortcut), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformCreateMachineShortcut);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_S_Sort), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformGroupSorting);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_T_Search), &UIAction::toggled,
+ this, &UIVirtualBoxManager::sltPerformMachineSearchWidgetVisibilityToggling);
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigMachineSearchWidgetVisibilityChanged,
+ actionPool()->action(UIActionIndexMN_M_Group_T_Search), &QAction::setChecked);
+
+ /* 'Machine' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_New), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenNewMachineWizard);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Add), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenAddMachineDialog);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenSettingsDialogDefault);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Clone), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenCloneMachineWizard);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Move), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformMachineMove);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_ExportToOCI), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenExportApplianceWizard);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Remove), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformMachineRemove);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_MoveToGroup_S_New), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformMachineMoveToNewGroup);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformStartOrShowMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_T_Pause), &UIAction::toggled,
+ this, &UIVirtualBoxManager::sltPerformPauseOrResumeMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Reset), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformResetMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Detach), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformDetachMachineUI);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformDiscardMachineState);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_ShowLogDialog), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenLogViewerWindow);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Refresh), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformRefreshMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_ShowInFileManager), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltShowMachineInFileManager);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_CreateShortcut), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformCreateMachineShortcut);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_SortParent), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformGroupSorting);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_T_Search), &UIAction::toggled,
+ this, &UIVirtualBoxManager::sltPerformMachineSearchWidgetVisibilityToggling);
+ connect(m_pWidget, &UIVirtualBoxManagerWidget::sigMachineSearchWidgetVisibilityChanged,
+ actionPool()->action(UIActionIndexMN_M_Machine_T_Search), &QAction::setChecked);
+
+ /* 'Group/Start or Show' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartNormal), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformStartMachineNormal);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartHeadless), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformStartMachineHeadless);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartDetachable), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformStartMachineDetachable);
+
+ /* 'Machine/Start or Show' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartNormal), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformStartMachineNormal);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartHeadless), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformStartMachineHeadless);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartDetachable), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformStartMachineDetachable);
+
+ /* 'Group/Console' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_Console_S_CreateConnection), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformCreateConsoleConnectionForGroup);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_Console_S_DeleteConnection), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformDeleteConsoleConnectionForGroup);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_Console_S_ConfigureApplications), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenManagerWindowDefault);
+
+ /* 'Machine/Console' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CreateConnection), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformCreateConsoleConnectionForMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_DeleteConnection), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformDeleteConsoleConnectionForMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialUnix), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformCopyCommandSerialUnix);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialWindows), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformCopyCommandSerialWindows);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCUnix), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformCopyCommandVNCUnix);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCWindows), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformCopyCommandVNCWindows);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_ConfigureApplications), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltOpenManagerWindowDefault);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_ShowLog), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformShowLog);
+
+ /* 'Group/Stop' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_SaveState), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformSaveMachineState);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_Terminate), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformTerminateMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_Shutdown), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformShutdownMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_PowerOff), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformPowerOffMachine);
+
+ /* 'Machine/Stop' menu connections: */
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_SaveState), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformSaveMachineState);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_Terminate), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformTerminateMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_Shutdown), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformShutdownMachine);
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_PowerOff), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformPowerOffMachine);
+
+ /* 'Group/Tools' menu connections: */
+ connect(actionPool()->actionGroup(UIActionIndexMN_M_Group_M_Tools), &QActionGroup::triggered,
+ this, &UIVirtualBoxManager::sltPerformShowMachineTool);
+
+ /* 'Machine/Tools' menu connections: */
+ connect(actionPool()->actionGroup(UIActionIndexMN_M_Machine_M_Tools), &QActionGroup::triggered,
+ this, &UIVirtualBoxManager::sltPerformShowMachineTool);
+
+ /* 'Help' menu contents action connection. It is done here since we need different behaviour in
+ * the manager and runtime UIs: */
+ connect(actionPool()->action(UIActionIndex_Simple_Contents), &UIAction::triggered,
+ this, &UIVirtualBoxManager::sltPerformShowHelpBrowser);
+}
+
+void UIVirtualBoxManager::loadSettings()
+{
+ /* Load window geometry: */
+ {
+ const QRect geo = gEDataManager->selectorWindowGeometry(this);
+ LogRel2(("GUI: UIVirtualBoxManager: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ restoreGeometry(geo);
+ }
+}
+
+void UIVirtualBoxManager::cleanupConnections()
+{
+ /* Honestly we should disconnect everything here,
+ * but for now it's enough to disconnect the most critical. */
+ m_pWidget->disconnect(this);
+}
+
+void UIVirtualBoxManager::cleanupWidgets()
+{
+ /* Deconfigure central-widget: */
+ setCentralWidget(0);
+ /* Destroy central-widget: */
+ delete m_pWidget;
+ m_pWidget = 0;
+}
+
+void UIVirtualBoxManager::cleanupMenuBar()
+{
+#ifdef VBOX_WS_MAC
+ /* Cleanup 'Window' menu: */
+ UIWindowMenuManager::destroy();
+#endif
+
+ /* Destroy action-pool: */
+ UIActionPool::destroy(m_pActionPool);
+ m_pActionPool = 0;
+}
+
+void UIVirtualBoxManager::cleanup()
+{
+ /* Ask sub-dialogs to commit data: */
+ sltHandleCommitData();
+
+ /* Cleanup: */
+ cleanupConnections();
+ cleanupWidgets();
+ cleanupMenuBar();
+}
+
+UIVirtualMachineItem *UIVirtualBoxManager::currentItem() const
+{
+ return m_pWidget->currentItem();
+}
+
+QList<UIVirtualMachineItem*> UIVirtualBoxManager::currentItems() const
+{
+ return m_pWidget->currentItems();
+}
+
+bool UIVirtualBoxManager::isGroupSavingInProgress() const
+{
+ return m_pWidget->isGroupSavingInProgress();
+}
+
+bool UIVirtualBoxManager::isAllItemsOfOneGroupSelected() const
+{
+ return m_pWidget->isAllItemsOfOneGroupSelected();
+}
+
+bool UIVirtualBoxManager::isSingleGroupSelected() const
+{
+ return m_pWidget->isSingleGroupSelected();
+}
+
+bool UIVirtualBoxManager::isSingleLocalGroupSelected() const
+{
+ return m_pWidget->isSingleLocalGroupSelected();
+}
+
+bool UIVirtualBoxManager::isSingleCloudProviderGroupSelected() const
+{
+ return m_pWidget->isSingleCloudProviderGroupSelected();
+}
+
+bool UIVirtualBoxManager::isSingleCloudProfileGroupSelected() const
+{
+ return m_pWidget->isSingleCloudProfileGroupSelected();
+}
+
+bool UIVirtualBoxManager::isCloudProfileUpdateInProgress() const
+{
+ return m_pWidget->isCloudProfileUpdateInProgress();
+}
+
+bool UIVirtualBoxManager::checkUnattendedInstallError(const CUnattended &comUnattended) const
+{
+ if (!comUnattended.isOk())
+ {
+ UINotificationMessage::cannotRunUnattendedGuestInstall(comUnattended);
+ return false;
+ }
+ return true;
+}
+
+void UIVirtualBoxManager::openAddMachineDialog(const QString &strFileName /* = QString() */)
+{
+ /* Initialize variables: */
+#ifdef VBOX_WS_MAC
+ QString strTmpFile = ::darwinResolveAlias(strFileName);
+#else
+ QString strTmpFile = strFileName;
+#endif
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* No file specified: */
+ if (strTmpFile.isEmpty())
+ {
+ QString strBaseFolder;
+ if (currentItem() && currentItem()->toLocal())
+ {
+ QDir folder = QFileInfo(currentItem()->toLocal()->settingsFile()).absoluteDir();
+ folder.cdUp();
+ strBaseFolder = folder.absolutePath();
+ }
+ if (strBaseFolder.isEmpty())
+ strBaseFolder = comVBox.GetSystemProperties().GetDefaultMachineFolder();
+ QString strTitle = tr("Select a virtual machine file");
+ QStringList extensions;
+ for (int i = 0; i < VBoxFileExts.size(); ++i)
+ extensions << QString("*.%1").arg(VBoxFileExts[i]);
+ QString strFilter = tr("Virtual machine files (%1)").arg(extensions.join(" "));
+ /* Create open file dialog: */
+ QStringList fileNames = QIFileDialog::getOpenFileNames(strBaseFolder, strFilter, this, strTitle, 0, true, true);
+ if (!fileNames.isEmpty())
+ strTmpFile = fileNames.at(0);
+ }
+
+ /* Nothing was chosen? */
+ if (strTmpFile.isEmpty())
+ return;
+
+ /* Make sure this machine can be opened: */
+ CMachine comMachineNew = comVBox.OpenMachine(strTmpFile, QString());
+ if (!comVBox.isOk())
+ {
+ UINotificationMessage::cannotOpenMachine(comVBox, strTmpFile);
+ return;
+ }
+
+ /* Make sure this machine was NOT registered already: */
+ CMachine comMachineOld = comVBox.FindMachine(comMachineNew.GetId().toString());
+ if (!comMachineOld.isNull())
+ {
+ UINotificationMessage::cannotReregisterExistingMachine(comMachineOld.GetName(), strTmpFile);
+ return;
+ }
+
+ /* Register that machine: */
+ comVBox.RegisterMachine(comMachineNew);
+}
+
+void UIVirtualBoxManager::openNewMachineWizard(const QString &strISOFilePath /* = QString() */)
+{
+ /* Lock the actions preventing cascade calls: */
+ UIQObjectPropertySetter guardBlock(QList<QObject*>() << actionPool()->action(UIActionIndexMN_M_Welcome_S_New)
+ << actionPool()->action(UIActionIndexMN_M_Machine_S_New)
+ << actionPool()->action(UIActionIndexMN_M_Group_S_New),
+ "opened", true);
+ connect(&guardBlock, &UIQObjectPropertySetter::sigAboutToBeDestroyed,
+ this, &UIVirtualBoxManager::sltHandleUpdateActionAppearanceRequest);
+ updateActionsAppearance();
+
+ /* Get first selected item: */
+ UIVirtualMachineItem *pItem = currentItem();
+
+ /* For global item or local machine: */
+ if ( !pItem
+ || pItem->itemType() == UIVirtualMachineItemType_Local)
+ {
+ CUnattended comUnattendedInstaller = uiCommon().virtualBox().CreateUnattendedInstaller();
+ AssertMsg(!comUnattendedInstaller.isNull(), ("Could not create unattended installer!\n"));
+
+ /* Use the "safe way" to open stack of Mac OS X Sheets: */
+ QWidget *pWizardParent = windowManager().realParentWindow(this);
+ UISafePointerWizardNewVM pWizard = new UIWizardNewVM(pWizardParent, actionPool(),
+ m_pWidget->fullGroupName(),
+ comUnattendedInstaller, strISOFilePath);
+ windowManager().registerNewParent(pWizard, pWizardParent);
+
+ /* Execute wizard: */
+ pWizard->exec();
+
+ bool fStartHeadless = pWizard->startHeadless();
+ bool fUnattendedEnabled = pWizard->isUnattendedEnabled();
+ QString strMachineId = pWizard->createdMachineId().toString();
+ delete pWizard;
+ /* Handle unattended install stuff: */
+ if (fUnattendedEnabled)
+ startUnattendedInstall(comUnattendedInstaller, fStartHeadless, strMachineId);
+ }
+ /* For cloud machine: */
+ else
+ {
+ /* Use the "safe way" to open stack of Mac OS X Sheets: */
+ QWidget *pWizardParent = windowManager().realParentWindow(this);
+ UISafePointerWizardNewCloudVM pWizard = new UIWizardNewCloudVM(pWizardParent, m_pWidget->fullGroupName());
+ windowManager().registerNewParent(pWizard, pWizardParent);
+
+ /* Execute wizard: */
+ pWizard->exec();
+ delete pWizard;
+ }
+}
+
+/* static */
+void UIVirtualBoxManager::launchMachine(CMachine &comMachine,
+ UILaunchMode enmLaunchMode /* = UILaunchMode_Default */)
+{
+ /* Switch to machine window(s) if possible: */
+ if ( comMachine.GetSessionState() == KSessionState_Locked // precondition for CanShowConsoleWindow()
+ && comMachine.CanShowConsoleWindow())
+ {
+ UICommon::switchToMachine(comMachine);
+ return;
+ }
+
+ /* Not for separate UI (which can connect to machine in any state): */
+ if (enmLaunchMode != UILaunchMode_Separate)
+ {
+ /* Make sure machine-state is one of required: */
+ const KMachineState enmState = comMachine.GetState(); Q_UNUSED(enmState);
+ AssertMsg( enmState == KMachineState_PoweredOff
+ || enmState == KMachineState_Saved
+ || enmState == KMachineState_Teleported
+ || enmState == KMachineState_Aborted
+ || enmState == KMachineState_AbortedSaved
+ , ("Machine must be PoweredOff/Saved/Teleported/Aborted (%d)", enmState));
+ }
+
+ /* Powering VM up: */
+ UINotificationProgressMachinePowerUp *pNotification =
+ new UINotificationProgressMachinePowerUp(comMachine, enmLaunchMode);
+ gpNotificationCenter->append(pNotification);
+}
+
+/* static */
+void UIVirtualBoxManager::launchMachine(CCloudMachine &comMachine)
+{
+ /* Powering cloud VM up: */
+ UINotificationProgressCloudMachinePowerUp *pNotification =
+ new UINotificationProgressCloudMachinePowerUp(comMachine);
+ gpNotificationCenter->append(pNotification);
+}
+
+void UIVirtualBoxManager::startUnattendedInstall(CUnattended &comUnattendedInstaller,
+ bool fStartHeadless, const QString &strMachineId)
+{
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CMachine comMachine = comVBox.FindMachine(strMachineId);
+ if (comMachine.isNull())
+ return;
+
+ comUnattendedInstaller.Prepare();
+ AssertReturnVoid(checkUnattendedInstallError(comUnattendedInstaller));
+ comUnattendedInstaller.ConstructMedia();
+ AssertReturnVoid(checkUnattendedInstallError(comUnattendedInstaller));
+ comUnattendedInstaller.ReconfigureVM();
+ AssertReturnVoid(checkUnattendedInstallError(comUnattendedInstaller));
+
+ launchMachine(comMachine, fStartHeadless ? UILaunchMode_Headless : UILaunchMode_Default);
+}
+
+void UIVirtualBoxManager::performStartOrShowVirtualMachines(const QList<UIVirtualMachineItem*> &items, UILaunchMode enmLaunchMode)
+{
+ /* Do nothing while group saving is in progress: */
+ if (isGroupSavingInProgress())
+ return;
+
+ /* Compose the list of startable items: */
+ QStringList startableMachineNames;
+ QList<UIVirtualMachineItem*> startableItems;
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if (isAtLeastOneItemCanBeStarted(QList<UIVirtualMachineItem*>() << pItem))
+ {
+ startableItems << pItem;
+ startableMachineNames << pItem->name();
+ }
+ }
+
+ /* Initially we have start auto-confirmed: */
+ bool fStartConfirmed = true;
+ /* But if we have more than one item to start =>
+ * We should still ask user for a confirmation: */
+ if (startableItems.size() > 1)
+ fStartConfirmed = msgCenter().confirmStartMultipleMachines(startableMachineNames.join(", "));
+
+ /* For every item => check if it could be launched: */
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if ( isAtLeastOneItemCanBeShown(QList<UIVirtualMachineItem*>() << pItem)
+ || ( isAtLeastOneItemCanBeStarted(QList<UIVirtualMachineItem*>() << pItem)
+ && fStartConfirmed))
+ {
+ /* For local machine: */
+ if (pItem->itemType() == UIVirtualMachineItemType_Local)
+ {
+ /* Fetch item launch mode: */
+ UILaunchMode enmItemLaunchMode = enmLaunchMode;
+ if (enmItemLaunchMode == UILaunchMode_Invalid)
+ enmItemLaunchMode = pItem->isItemRunningHeadless()
+ ? UILaunchMode_Separate
+ : qApp->keyboardModifiers() == Qt::ShiftModifier
+ ? UILaunchMode_Headless
+ : UILaunchMode_Default;
+ /* Acquire local machine: */
+ CMachine machine = pItem->toLocal()->machine();
+ /* Launch current VM: */
+ launchMachine(machine, enmItemLaunchMode);
+ }
+ /* For real cloud machine: */
+ else if (pItem->itemType() == UIVirtualMachineItemType_CloudReal)
+ {
+ /* Acquire cloud machine: */
+ CCloudMachine comCloudMachine = pItem->toCloud()->machine();
+ /* Launch current VM: */
+ launchMachine(comCloudMachine);
+ }
+ }
+ }
+}
+
+#ifndef VBOX_WS_WIN
+QStringList UIVirtualBoxManager::parseShellArguments(const QString &strArguments)
+{
+ //printf("start processing arguments\n");
+
+ /* Parse argument string: */
+ QStringList arguments;
+ QRegExp re("(\"[^\"]+\")|('[^']+')|([^\\s\"']+)");
+ int iPosition = 0;
+ int iIndex = re.indexIn(strArguments, iPosition);
+ while (iIndex != -1)
+ {
+ /* Get what's the sequence we have: */
+ const QString strCap0 = re.cap(0);
+ /* Get what's the double-quoted sequence we have: */
+ const QString strCap1 = re.cap(1);
+ /* Get what's the single-quoted sequence we have: */
+ const QString strCap2 = re.cap(2);
+ /* Get what's the unquoted sequence we have: */
+ const QString strCap3 = re.cap(3);
+
+ /* If new sequence starts where previous ended
+ * we are appending new value to previous one, otherwise
+ * we are appending new value to argument list itself.. */
+
+ /* Do we have double-quoted sequence? */
+ if (!strCap1.isEmpty())
+ {
+ //printf(" [D] double-quoted sequence starting at: %d\n", iIndex);
+ /* Unquote the value and add it to the list: */
+ const QString strValue = strCap1.mid(1, strCap1.size() - 2);
+ if (!arguments.isEmpty() && iIndex == iPosition)
+ arguments.last() += strValue;
+ else
+ arguments << strValue;
+ }
+ /* Do we have single-quoted sequence? */
+ else if (!strCap2.isEmpty())
+ {
+ //printf(" [S] single-quoted sequence starting at: %d\n", iIndex);
+ /* Unquote the value and add it to the list: */
+ const QString strValue = strCap2.mid(1, strCap2.size() - 2);
+ if (!arguments.isEmpty() && iIndex == iPosition)
+ arguments.last() += strValue;
+ else
+ arguments << strValue;
+ }
+ /* Do we have unquoted sequence? */
+ else if (!strCap3.isEmpty())
+ {
+ //printf(" [U] unquoted sequence starting at: %d\n", iIndex);
+ /* Value wasn't unquoted, add it to the list: */
+ if (!arguments.isEmpty() && iIndex == iPosition)
+ arguments.last() += strCap3;
+ else
+ arguments << strCap3;
+ }
+
+ /* Advance position: */
+ iPosition = iIndex + strCap0.size();
+ /* Search for a next sequence: */
+ iIndex = re.indexIn(strArguments, iPosition);
+ }
+
+ //printf("arguments processed:\n");
+ //foreach (const QString &strArgument, arguments)
+ // printf(" %s\n", strArgument.toUtf8().constData());
+
+ /* Return parsed arguments: */
+ return arguments;
+}
+#endif /* !VBOX_WS_WIN */
+
+void UIVirtualBoxManager::updateMenuGroup(QMenu *pMenu)
+{
+ /* For single cloud provider/profile: */
+ if ( isSingleCloudProviderGroupSelected()
+ || isSingleCloudProfileGroupSelected())
+ {
+ /* Populate Group-menu: */
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_New));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Add));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow));
+ pMenu->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Console)->menu());
+ pMenu->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Stop)->menu());
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Refresh));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Sort));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_T_Search));
+ }
+ /* For other cases, like local group or no group at all: */
+ else
+ {
+ /* Populate Group-menu: */
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_New));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Add));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Rename));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Remove));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_MoveToGroup));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_T_Pause));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Reset));
+ // pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Detach));
+ pMenu->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Stop)->menu());
+ pMenu->addSeparator();
+ pMenu->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Tools)->menu());
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Discard));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_ShowLogDialog));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Refresh));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_ShowInFileManager));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_CreateShortcut));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Sort));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_T_Search));
+ }
+}
+
+void UIVirtualBoxManager::updateMenuMachine(QMenu *pMenu)
+{
+ /* Get first selected item: */
+ UIVirtualMachineItem *pItem = currentItem();
+
+ /* For cloud machine(s): */
+ if ( pItem
+ && ( pItem->itemType() == UIVirtualMachineItemType_CloudFake
+ || pItem->itemType() == UIVirtualMachineItemType_CloudReal))
+ {
+ /* Populate Machine-menu: */
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_New));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Add));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Remove));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
+ pMenu->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Console)->menu());
+ pMenu->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop)->menu());
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Refresh));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_SortParent));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_T_Search));
+ }
+ /* For other cases, like local machine(s) or no machine at all: */
+ else
+ {
+ /* Populate Machine-menu: */
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_New));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Add));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Clone));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Move));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_ExportToOCI));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Remove));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_MoveToGroup));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_T_Pause));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Reset));
+ // pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Detach));
+ pMenu->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop)->menu());
+ pMenu->addSeparator();
+ pMenu->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Tools)->menu());
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_ShowLogDialog));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Refresh));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_ShowInFileManager));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_CreateShortcut));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_SortParent));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_T_Search));
+ }
+}
+
+void UIVirtualBoxManager::updateMenuGroupMoveToGroup(QMenu *pMenu)
+{
+ const QStringList groups = m_pWidget->possibleGroupsForGroupToMove(m_pWidget->fullGroupName());
+ if (!groups.isEmpty())
+ pMenu->addSeparator();
+ foreach (const QString &strGroupName, groups)
+ {
+ QString strVisibleGroupName = strGroupName;
+ if (strVisibleGroupName.startsWith('/'))
+ strVisibleGroupName.remove(0, 1);
+ if (strVisibleGroupName.isEmpty())
+ strVisibleGroupName = QApplication::translate("UIActionPool", "[Root]", "group");
+ QAction *pAction = pMenu->addAction(strVisibleGroupName, this, &UIVirtualBoxManager::sltPerformMachineMoveToSpecificGroup);
+ pAction->setProperty("actual_group_name", strGroupName);
+ }
+}
+
+void UIVirtualBoxManager::updateMenuGroupConsole(QMenu *pMenu)
+{
+ /* Populate 'Group' / 'Console' menu: */
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Console_S_CreateConnection));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Console_S_DeleteConnection));
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Console_S_ConfigureApplications));
+}
+
+void UIVirtualBoxManager::updateMenuGroupClose(QMenu *pMenu)
+{
+ /* Get first selected item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertPtrReturnVoid(pItem);
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* For local machine: */
+ if (pItem->itemType() == UIVirtualMachineItemType_Local)
+ {
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_SaveState));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_Shutdown));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_PowerOff));
+ }
+ else
+ {
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_Terminate));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_Shutdown));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_PowerOff));
+ }
+
+ /* Configure 'Group' / 'Stop' menu: */
+ actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_Shutdown)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Stop_S_Shutdown, items));
+}
+
+void UIVirtualBoxManager::updateMenuMachineMoveToGroup(QMenu *pMenu)
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+
+ const QStringList groups = m_pWidget->possibleGroupsForMachineToMove(pItem->id());
+ if (!groups.isEmpty())
+ pMenu->addSeparator();
+ foreach (const QString &strGroupName, groups)
+ {
+ QString strVisibleGroupName = strGroupName;
+ if (strVisibleGroupName.startsWith('/'))
+ strVisibleGroupName.remove(0, 1);
+ if (strVisibleGroupName.isEmpty())
+ strVisibleGroupName = QApplication::translate("UIActionPool", "[Root]", "group");
+ QAction *pAction = pMenu->addAction(strVisibleGroupName, this, &UIVirtualBoxManager::sltPerformMachineMoveToSpecificGroup);
+ pAction->setProperty("actual_group_name", strGroupName);
+ }
+}
+
+void UIVirtualBoxManager::updateMenuMachineConsole(QMenu *pMenu)
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertMsgReturnVoid(pItem, ("Current item should be selected!\n"));
+ UIVirtualMachineItemCloud *pCloudItem = pItem->toCloud();
+ AssertPtrReturnVoid(pCloudItem);
+
+ /* Acquire current cloud machine: */
+ CCloudMachine comMachine = pCloudItem->machine();
+ const QString strFingerprint = comMachine.GetConsoleConnectionFingerprint();
+
+ /* Populate 'Group' / 'Console' menu: */
+ if (strFingerprint.isEmpty())
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CreateConnection));
+ else
+ {
+ /* Copy fingerprint to clipboard action: */
+ const QString strFingerprintCompressed = strFingerprint.size() <= 12
+ ? strFingerprint
+ : QString("%1...%2").arg(strFingerprint.left(6), strFingerprint.right(6));
+ QAction *pAction = pMenu->addAction(UIIconPool::iconSet(":/cloud_machine_console_copy_connection_fingerprint_16px.png",
+ ":/cloud_machine_console_copy_connection_fingerprint_disabled_16px.png"),
+ QApplication::translate("UIActionPool", "Copy Key Fingerprint (%1)").arg(strFingerprintCompressed),
+ this, &UIVirtualBoxManager::sltCopyConsoleConnectionFingerprint);
+ pAction->setProperty("fingerprint", strFingerprint);
+
+ /* Copy command to clipboard actions: */
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialUnix));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialWindows));
+// pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCUnix));
+// pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCWindows));
+ pMenu->addSeparator();
+
+ /* Default Connect action: */
+ QAction *pDefaultAction = pMenu->addAction(QApplication::translate("UIActionPool", "Connect", "to cloud VM"),
+ this, &UIVirtualBoxManager::sltExecuteExternalApplication);
+#if defined(VBOX_WS_MAC)
+ pDefaultAction->setProperty("path", "open");
+#elif defined(VBOX_WS_WIN)
+ pDefaultAction->setProperty("path", "powershell");
+#elif defined(VBOX_WS_X11)
+ const QPair<QString, QString> terminalData = defaultTerminalData();
+ pDefaultAction->setProperty("path", terminalData.first);
+ pDefaultAction->setProperty("arguments", QString("%1 sh -c").arg(terminalData.second));
+#endif
+
+ /* Terminal application/profile action list: */
+ const QStringList restrictions = gEDataManager->cloudConsoleManagerRestrictions();
+ foreach (const QString strApplicationId, gEDataManager->cloudConsoleManagerApplications())
+ {
+ const QString strApplicationDefinition = QString("/%1").arg(strApplicationId);
+ if (restrictions.contains(strApplicationDefinition))
+ continue;
+ const QString strApplicationOptions = gEDataManager->cloudConsoleManagerApplication(strApplicationId);
+ const QStringList applicationValues = strApplicationOptions.split(',');
+ bool fAtLeastOneProfileListed = false;
+ foreach (const QString strProfileId, gEDataManager->cloudConsoleManagerProfiles(strApplicationId))
+ {
+ const QString strProfileDefinition = QString("/%1/%2").arg(strApplicationId, strProfileId);
+ if (restrictions.contains(strProfileDefinition))
+ continue;
+ const QString strProfileOptions = gEDataManager->cloudConsoleManagerProfile(strApplicationId, strProfileId);
+ const QStringList profileValues = strProfileOptions.split(',');
+ QAction *pAction = pMenu->addAction(QApplication::translate("UIActionPool",
+ "Connect with %1 (%2)",
+ "with terminal application (profile)")
+ .arg(applicationValues.value(0), profileValues.value(0)),
+ this, &UIVirtualBoxManager::sltExecuteExternalApplication);
+ pAction->setProperty("path", applicationValues.value(1));
+ pAction->setProperty("arguments", profileValues.value(1));
+ fAtLeastOneProfileListed = true;
+ }
+ if (!fAtLeastOneProfileListed)
+ {
+ QAction *pAction = pMenu->addAction(QApplication::translate("UIActionPool",
+ "Connect with %1",
+ "with terminal application")
+ .arg(applicationValues.value(0)),
+ this, &UIVirtualBoxManager::sltExecuteExternalApplication);
+ pAction->setProperty("path", applicationValues.value(1));
+ pAction->setProperty("arguments", applicationValues.value(2));
+ }
+ }
+ /* Terminal application configuration tool: */
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_ConfigureApplications));
+ pMenu->addSeparator();
+
+ /* Delete connection action finally: */
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_DeleteConnection));
+ }
+
+ /* Show console log action: */
+ pMenu->addSeparator();
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_ShowLog));
+}
+
+void UIVirtualBoxManager::updateMenuMachineClose(QMenu *pMenu)
+{
+ /* Get first selected item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ AssertPtrReturnVoid(pItem);
+ /* Get selected items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+ AssertMsgReturnVoid(!items.isEmpty(), ("At least one item should be selected!\n"));
+
+ /* For local machine: */
+ if (pItem->itemType() == UIVirtualMachineItemType_Local)
+ {
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_SaveState));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_Shutdown));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_PowerOff));
+ }
+ else
+ {
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_Terminate));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_Shutdown));
+ pMenu->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_PowerOff));
+ }
+
+ /* Configure 'Machine' / 'Stop' menu: */
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_Shutdown)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Stop_S_Shutdown, items));
+}
+
+void UIVirtualBoxManager::updateActionsVisibility()
+{
+ /* Determine whether Machine or Group menu should be shown at all: */
+ const bool fGlobalMenuShown = m_pWidget->isGlobalItemSelected();
+ const bool fGroupMenuShown = m_pWidget->isGroupItemSelected() && isSingleGroupSelected();
+ const bool fMachineMenuShown = m_pWidget->isMachineItemSelected() && !isSingleGroupSelected();
+ actionPool()->action(UIActionIndexMN_M_Welcome)->setVisible(fGlobalMenuShown);
+ actionPool()->action(UIActionIndexMN_M_Group)->setVisible(fGroupMenuShown);
+ actionPool()->action(UIActionIndexMN_M_Machine)->setVisible(fMachineMenuShown);
+
+ /* Determine whether Extensions menu should be visible: */
+ const bool fExtensionsMenuShown = fGlobalMenuShown && m_pWidget->currentGlobalTool() == UIToolType_Extensions;
+ actionPool()->action(UIActionIndexMN_M_Extension)->setVisible(fExtensionsMenuShown);
+ /* Determine whether Media menu should be visible: */
+ const bool fMediumMenuShown = fGlobalMenuShown && m_pWidget->currentGlobalTool() == UIToolType_Media;
+ actionPool()->action(UIActionIndexMN_M_Medium)->setVisible(fMediumMenuShown);
+ /* Determine whether Network menu should be visible: */
+ const bool fNetworkMenuShown = fGlobalMenuShown && m_pWidget->currentGlobalTool() == UIToolType_Network;
+ actionPool()->action(UIActionIndexMN_M_Network)->setVisible(fNetworkMenuShown);
+ /* Determine whether Cloud menu should be visible: */
+ const bool fCloudMenuShown = fGlobalMenuShown && m_pWidget->currentGlobalTool() == UIToolType_Cloud;
+ actionPool()->action(UIActionIndexMN_M_Cloud)->setVisible(fCloudMenuShown);
+ /* Determine whether Resources menu should be visible: */
+ const bool fResourcesMenuShown = fGlobalMenuShown && m_pWidget->currentGlobalTool() == UIToolType_VMActivityOverview;
+ actionPool()->action(UIActionIndexMN_M_VMActivityOverview)->setVisible(fResourcesMenuShown);
+
+ /* Determine whether Snapshots menu should be visible: */
+ const bool fSnapshotMenuShown = (fMachineMenuShown || fGroupMenuShown) &&
+ m_pWidget->currentMachineTool() == UIToolType_Snapshots;
+ actionPool()->action(UIActionIndexMN_M_Snapshot)->setVisible(fSnapshotMenuShown);
+ /* Determine whether Logs menu should be visible: */
+ const bool fLogViewerMenuShown = (fMachineMenuShown || fGroupMenuShown) &&
+ m_pWidget->currentMachineTool() == UIToolType_Logs;
+ actionPool()->action(UIActionIndex_M_Log)->setVisible(fLogViewerMenuShown);
+ /* Determine whether Performance menu should be visible: */
+ const bool fPerformanceMenuShown = (fMachineMenuShown || fGroupMenuShown) &&
+ m_pWidget->currentMachineTool() == UIToolType_VMActivity;
+ actionPool()->action(UIActionIndex_M_Activity)->setVisible(fPerformanceMenuShown);
+ /* Determine whether File Manager menu item should be visible: */
+ const bool fFileManagerMenuShown = (fMachineMenuShown || fGroupMenuShown) &&
+ m_pWidget->currentMachineTool() == UIToolType_FileManager;
+ actionPool()->action(UIActionIndex_M_FileManager)->setVisible(fFileManagerMenuShown);
+
+ /* Hide action shortcuts: */
+ if (!fGlobalMenuShown)
+ actionPool()->setShortcutsVisible(UIActionIndexMN_M_Welcome, false);
+ if (!fGroupMenuShown)
+ actionPool()->setShortcutsVisible(UIActionIndexMN_M_Group, false);
+ if (!fMachineMenuShown)
+ actionPool()->setShortcutsVisible(UIActionIndexMN_M_Machine, false);
+
+ /* Show action shortcuts: */
+ if (fGlobalMenuShown)
+ actionPool()->setShortcutsVisible(UIActionIndexMN_M_Welcome, true);
+ if (fGroupMenuShown)
+ actionPool()->setShortcutsVisible(UIActionIndexMN_M_Group, true);
+ if (fMachineMenuShown)
+ actionPool()->setShortcutsVisible(UIActionIndexMN_M_Machine, true);
+}
+
+void UIVirtualBoxManager::updateActionsAppearance()
+{
+ /* Get current items: */
+ QList<UIVirtualMachineItem*> items = currentItems();
+
+ /* Enable/disable File/Application actions: */
+ actionPool()->action(UIActionIndex_M_Application_S_Preferences)->setEnabled(isActionEnabled(UIActionIndex_M_Application_S_Preferences, items));
+ actionPool()->action(UIActionIndexMN_M_File_S_ExportAppliance)->setEnabled(isActionEnabled(UIActionIndexMN_M_File_S_ExportAppliance, items));
+ actionPool()->action(UIActionIndexMN_M_File_S_ImportAppliance)->setEnabled(isActionEnabled(UIActionIndexMN_M_File_S_ImportAppliance, items));
+
+ /* Enable/disable welcome actions: */
+ actionPool()->action(UIActionIndexMN_M_Welcome_S_New)->setEnabled(isActionEnabled(UIActionIndexMN_M_Welcome_S_New, items));
+ actionPool()->action(UIActionIndexMN_M_Welcome_S_Add)->setEnabled(isActionEnabled(UIActionIndexMN_M_Welcome_S_Add, items));
+
+ /* Enable/disable group actions: */
+ actionPool()->action(UIActionIndexMN_M_Group_S_New)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_New, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_Add)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_Add, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_Rename)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_Rename, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_Remove)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_Remove, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_MoveToGroup)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_MoveToGroup, items));
+ actionPool()->action(UIActionIndexMN_M_Group_T_Pause)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_T_Pause, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_Reset)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_Reset, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_Detach)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_Detach, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_Discard)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_Discard, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_ShowLogDialog)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_ShowLogDialog, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_Refresh)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_Refresh, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_ShowInFileManager)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_ShowInFileManager, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_CreateShortcut)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_CreateShortcut, items));
+ actionPool()->action(UIActionIndexMN_M_Group_S_Sort)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_S_Sort, items));
+
+ /* Enable/disable machine actions: */
+ actionPool()->action(UIActionIndexMN_M_Machine_S_New)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_New, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_Add)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_Add, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_Settings)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_Settings, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_Clone)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_Clone, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_Move)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_Move, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_ExportToOCI)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_ExportToOCI, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_Remove)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_Remove, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_MoveToGroup)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_MoveToGroup, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_MoveToGroup_S_New)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_MoveToGroup_S_New, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_T_Pause)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_T_Pause, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_Reset)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_Reset, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_Detach)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_Detach, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_Discard)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_Discard, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_ShowLogDialog)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_ShowLogDialog, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_Refresh)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_Refresh, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_ShowInFileManager)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_ShowInFileManager, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_CreateShortcut)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_CreateShortcut, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_S_SortParent)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_S_SortParent, items));
+
+ /* Enable/disable group-start-or-show actions: */
+ actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_StartOrShow, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartNormal)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_StartOrShow_S_StartNormal, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartHeadless)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_StartOrShow_S_StartHeadless, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow_S_StartDetachable)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_StartOrShow_S_StartDetachable, items));
+
+ /* Enable/disable machine-start-or-show actions: */
+ actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_StartOrShow, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartNormal)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartNormal, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartHeadless)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartHeadless, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartDetachable)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_StartOrShow_S_StartDetachable, items));
+
+ /* Enable/disable group-console actions: */
+ actionPool()->action(UIActionIndexMN_M_Group_M_Console)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Console, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_Console_S_CreateConnection)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Console_S_CreateConnection, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_Console_S_DeleteConnection)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Console_S_DeleteConnection, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_Console_S_ConfigureApplications)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Console_S_ConfigureApplications, items));
+
+ /* Enable/disable machine-console actions: */
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Console)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Console, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CreateConnection)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Console_S_CreateConnection, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_DeleteConnection)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Console_S_DeleteConnection, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialUnix)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialUnix, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialWindows)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialWindows, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCUnix)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCUnix, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCWindows)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCWindows, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_ConfigureApplications)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Console_S_ConfigureApplications, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Console_S_ShowLog)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Console_S_ShowLog, items));
+
+ /* Enable/disable group-stop actions: */
+ actionPool()->action(UIActionIndexMN_M_Group_M_Stop)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Stop, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_SaveState)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Stop_S_SaveState, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_Terminate)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Stop_S_Terminate, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_Shutdown)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Stop_S_Shutdown, items));
+ actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_PowerOff)->setEnabled(isActionEnabled(UIActionIndexMN_M_Group_M_Stop_S_PowerOff, items));
+
+ /* Enable/disable machine-stop actions: */
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Stop)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Stop, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_SaveState)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Stop_S_SaveState, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_Terminate)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Stop_S_Terminate, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_Shutdown)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Stop_S_Shutdown, items));
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_PowerOff)->setEnabled(isActionEnabled(UIActionIndexMN_M_Machine_M_Stop_S_PowerOff, items));
+
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+
+ /* Start/Show action is deremined by 1st item: */
+ if (pItem && pItem->accessible())
+ {
+ actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow)->setState(pItem->isItemPoweredOff() ? 0 : 1);
+ actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow)->setState(pItem->isItemPoweredOff() ? 0 : 1);
+ m_pWidget->updateToolBarMenuButtons(pItem->isItemPoweredOff());
+ }
+ else
+ {
+ actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow)->setState(0);
+ actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow)->setState(0);
+ m_pWidget->updateToolBarMenuButtons(true /* separate menu section? */);
+ }
+
+ /* Pause/Resume action is deremined by 1st started item: */
+ UIVirtualMachineItem *pFirstStartedAction = 0;
+ foreach (UIVirtualMachineItem *pSelectedItem, items)
+ {
+ if (pSelectedItem->isItemStarted())
+ {
+ pFirstStartedAction = pSelectedItem;
+ break;
+ }
+ }
+ /* Update the group Pause/Resume action appearance: */
+ actionPool()->action(UIActionIndexMN_M_Group_T_Pause)->blockSignals(true);
+ actionPool()->action(UIActionIndexMN_M_Group_T_Pause)->setChecked(pFirstStartedAction && pFirstStartedAction->isItemPaused());
+ actionPool()->action(UIActionIndexMN_M_Group_T_Pause)->retranslateUi();
+ actionPool()->action(UIActionIndexMN_M_Group_T_Pause)->blockSignals(false);
+ /* Update the machine Pause/Resume action appearance: */
+ actionPool()->action(UIActionIndexMN_M_Machine_T_Pause)->blockSignals(true);
+ actionPool()->action(UIActionIndexMN_M_Machine_T_Pause)->setChecked(pFirstStartedAction && pFirstStartedAction->isItemPaused());
+ actionPool()->action(UIActionIndexMN_M_Machine_T_Pause)->retranslateUi();
+ actionPool()->action(UIActionIndexMN_M_Machine_T_Pause)->blockSignals(false);
+
+ /* Update action toggle states: */
+ if (m_pWidget)
+ {
+ switch (m_pWidget->currentMachineTool())
+ {
+ case UIToolType_Details:
+ {
+ actionPool()->action(UIActionIndexMN_M_Group_M_Tools_T_Details)->setChecked(true);
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Tools_T_Details)->setChecked(true);
+ break;
+ }
+ case UIToolType_Snapshots:
+ {
+ actionPool()->action(UIActionIndexMN_M_Group_M_Tools_T_Snapshots)->setChecked(true);
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Tools_T_Snapshots)->setChecked(true);
+ break;
+ }
+ case UIToolType_Logs:
+ {
+ actionPool()->action(UIActionIndexMN_M_Group_M_Tools_T_Logs)->setChecked(true);
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Tools_T_Logs)->setChecked(true);
+ break;
+ }
+ case UIToolType_VMActivity:
+ {
+ actionPool()->action(UIActionIndexMN_M_Group_M_Tools_T_Activity)->setChecked(true);
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Tools_T_Activity)->setChecked(true);
+ break;
+ }
+ case UIToolType_FileManager:
+ {
+ actionPool()->action(UIActionIndexMN_M_Group_M_Tools_T_FileManager)->setChecked(true);
+ actionPool()->action(UIActionIndexMN_M_Machine_M_Tools_T_FileManager)->setChecked(true);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+bool UIVirtualBoxManager::isActionEnabled(int iActionIndex, const QList<UIVirtualMachineItem*> &items)
+{
+ /* Make sure action pool exists: */
+ AssertPtrReturn(actionPool(), false);
+
+ /* Any "opened" action is by definition disabled: */
+ if ( actionPool()->action(iActionIndex)
+ && actionPool()->action(iActionIndex)->property("opened").toBool())
+ return false;
+
+ /* For known *global* action types: */
+ switch (iActionIndex)
+ {
+ case UIActionIndex_M_Application_S_Preferences:
+ case UIActionIndexMN_M_File_S_ExportAppliance:
+ case UIActionIndexMN_M_File_S_ImportAppliance:
+ case UIActionIndexMN_M_Welcome_S_New:
+ case UIActionIndexMN_M_Welcome_S_Add:
+ return true;
+ default:
+ break;
+ }
+
+ /* No *machine* actions enabled for empty item list: */
+ if (items.isEmpty())
+ return false;
+
+ /* Get first item: */
+ UIVirtualMachineItem *pItem = items.first();
+
+ /* For known *machine* action types: */
+ switch (iActionIndex)
+ {
+ case UIActionIndexMN_M_Group_S_New:
+ case UIActionIndexMN_M_Group_S_Add:
+ {
+ return !isGroupSavingInProgress();
+ }
+ case UIActionIndexMN_M_Group_S_Sort:
+ {
+ return !isGroupSavingInProgress() &&
+ isSingleGroupSelected() &&
+ isItemsLocal(items);
+ }
+ case UIActionIndexMN_M_Group_S_Rename:
+ case UIActionIndexMN_M_Group_S_Remove:
+ {
+ return !isGroupSavingInProgress() &&
+ isSingleGroupSelected() &&
+ isItemsLocal(items) &&
+ isItemsPoweredOff(items);
+ }
+ case UIActionIndexMN_M_Machine_S_New:
+ case UIActionIndexMN_M_Machine_S_Add:
+ {
+ return !isGroupSavingInProgress();
+ }
+ case UIActionIndexMN_M_Machine_S_Settings:
+ {
+ return !isGroupSavingInProgress() &&
+ items.size() == 1 &&
+ pItem->configurationAccessLevel() != ConfigurationAccessLevel_Null &&
+ (m_pWidget->currentMachineTool() != UIToolType_Snapshots ||
+ m_pWidget->isCurrentStateItemSelected());
+ }
+ case UIActionIndexMN_M_Machine_S_Clone:
+ case UIActionIndexMN_M_Machine_S_Move:
+ {
+ return !isGroupSavingInProgress() &&
+ items.size() == 1 &&
+ pItem->toLocal() &&
+ pItem->isItemEditable();
+ }
+ case UIActionIndexMN_M_Machine_S_ExportToOCI:
+ {
+ return items.size() == 1 &&
+ pItem->toLocal();
+ }
+ case UIActionIndexMN_M_Machine_S_Remove:
+ {
+ return !isGroupSavingInProgress() &&
+ (isItemsLocal(items) || !isCloudProfileUpdateInProgress()) &&
+ isAtLeastOneItemRemovable(items);
+ }
+ case UIActionIndexMN_M_Group_M_MoveToGroup:
+ case UIActionIndexMN_M_Machine_M_MoveToGroup:
+ case UIActionIndexMN_M_Machine_M_MoveToGroup_S_New:
+ {
+ return !isGroupSavingInProgress() &&
+ isItemsLocal(items) &&
+ isItemsPoweredOff(items);
+ }
+ case UIActionIndexMN_M_Group_M_StartOrShow:
+ case UIActionIndexMN_M_Group_M_StartOrShow_S_StartNormal:
+ case UIActionIndexMN_M_Machine_M_StartOrShow:
+ case UIActionIndexMN_M_Machine_M_StartOrShow_S_StartNormal:
+ {
+ return !isGroupSavingInProgress() &&
+ isAtLeastOneItemCanBeStartedOrShown(items) &&
+ (m_pWidget->currentMachineTool() != UIToolType_Snapshots ||
+ m_pWidget->isCurrentStateItemSelected());
+ }
+ case UIActionIndexMN_M_Group_M_StartOrShow_S_StartHeadless:
+ case UIActionIndexMN_M_Group_M_StartOrShow_S_StartDetachable:
+ case UIActionIndexMN_M_Machine_M_StartOrShow_S_StartHeadless:
+ case UIActionIndexMN_M_Machine_M_StartOrShow_S_StartDetachable:
+ {
+ return !isGroupSavingInProgress() &&
+ isItemsLocal(items) &&
+ isAtLeastOneItemCanBeStartedOrShown(items) &&
+ (m_pWidget->currentMachineTool() != UIToolType_Snapshots ||
+ m_pWidget->isCurrentStateItemSelected());
+ }
+ case UIActionIndexMN_M_Group_S_Discard:
+ case UIActionIndexMN_M_Machine_S_Discard:
+ {
+ return !isGroupSavingInProgress() &&
+ isItemsLocal(items) &&
+ isAtLeastOneItemDiscardable(items) &&
+ (m_pWidget->currentMachineTool() != UIToolType_Snapshots ||
+ m_pWidget->isCurrentStateItemSelected());
+ }
+ case UIActionIndexMN_M_Group_S_ShowLogDialog:
+ case UIActionIndexMN_M_Machine_S_ShowLogDialog:
+ {
+ return isItemsLocal(items) &&
+ isAtLeastOneItemAccessible(items);
+ }
+ case UIActionIndexMN_M_Group_T_Pause:
+ case UIActionIndexMN_M_Machine_T_Pause:
+ {
+ return isItemsLocal(items) &&
+ isAtLeastOneItemStarted(items);
+ }
+ case UIActionIndexMN_M_Group_S_Reset:
+ case UIActionIndexMN_M_Machine_S_Reset:
+ {
+ return isItemsLocal(items) &&
+ isAtLeastOneItemRunning(items);
+ }
+ case UIActionIndexMN_M_Group_S_Detach:
+ case UIActionIndexMN_M_Machine_S_Detach:
+ {
+ return isItemsLocal(items) &&
+ isAtLeastOneItemRunning(items) &&
+ isAtLeastOneItemDetachable(items);
+ }
+ case UIActionIndexMN_M_Group_S_Refresh:
+ case UIActionIndexMN_M_Machine_S_Refresh:
+ {
+ return isAtLeastOneItemInaccessible(items);
+ }
+ case UIActionIndexMN_M_Group_S_ShowInFileManager:
+ case UIActionIndexMN_M_Machine_S_ShowInFileManager:
+ {
+ return isItemsLocal(items) &&
+ isAtLeastOneItemAccessible(items);
+ }
+ case UIActionIndexMN_M_Machine_S_SortParent:
+ {
+ return !isGroupSavingInProgress() &&
+ isItemsLocal(items);
+ }
+ case UIActionIndexMN_M_Group_S_CreateShortcut:
+ case UIActionIndexMN_M_Machine_S_CreateShortcut:
+ {
+ return isAtLeastOneItemSupportsShortcuts(items);
+ }
+ case UIActionIndexMN_M_Group_M_Console:
+ case UIActionIndexMN_M_Group_M_Console_S_CreateConnection:
+ case UIActionIndexMN_M_Group_M_Console_S_DeleteConnection:
+ case UIActionIndexMN_M_Group_M_Console_S_ConfigureApplications:
+ case UIActionIndexMN_M_Machine_M_Console:
+ case UIActionIndexMN_M_Machine_M_Console_S_CreateConnection:
+ case UIActionIndexMN_M_Machine_M_Console_S_DeleteConnection:
+ case UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialUnix:
+ case UIActionIndexMN_M_Machine_M_Console_S_CopyCommandSerialWindows:
+ case UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCUnix:
+ case UIActionIndexMN_M_Machine_M_Console_S_CopyCommandVNCWindows:
+ case UIActionIndexMN_M_Machine_M_Console_S_ConfigureApplications:
+ case UIActionIndexMN_M_Machine_M_Console_S_ShowLog:
+ {
+ return isAtLeastOneItemStarted(items);
+ }
+ case UIActionIndexMN_M_Group_M_Stop:
+ case UIActionIndexMN_M_Machine_M_Stop:
+ {
+ return (isItemsLocal(items) && isAtLeastOneItemStarted(items))
+ || (isItemsCloud(items) && isAtLeastOneItemDiscardable(items));
+ }
+ case UIActionIndexMN_M_Group_M_Stop_S_SaveState:
+ case UIActionIndexMN_M_Machine_M_Stop_S_SaveState:
+ {
+ return isActionEnabled(UIActionIndexMN_M_Machine_M_Stop, items)
+ && isItemsLocal(items);
+ }
+ case UIActionIndexMN_M_Group_M_Stop_S_Terminate:
+ case UIActionIndexMN_M_Machine_M_Stop_S_Terminate:
+ {
+ return isActionEnabled(UIActionIndexMN_M_Machine_M_Stop, items)
+ && isAtLeastOneItemDiscardable(items);
+ }
+ case UIActionIndexMN_M_Group_M_Stop_S_Shutdown:
+ case UIActionIndexMN_M_Machine_M_Stop_S_Shutdown:
+ {
+ return isActionEnabled(UIActionIndexMN_M_Machine_M_Stop, items)
+ && isAtLeastOneItemAbleToShutdown(items);
+ }
+ case UIActionIndexMN_M_Group_M_Stop_S_PowerOff:
+ case UIActionIndexMN_M_Machine_M_Stop_S_PowerOff:
+ {
+ return isActionEnabled(UIActionIndexMN_M_Machine_M_Stop, items)
+ && isAtLeastOneItemStarted(items);
+ }
+ default:
+ break;
+ }
+
+ /* Unknown actions are disabled: */
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isItemsLocal(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if (!pItem->toLocal())
+ return false;
+ return true;
+}
+
+/* static */
+bool UIVirtualBoxManager::isItemsCloud(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if (!pItem->toCloud())
+ return false;
+ return true;
+}
+
+/* static */
+bool UIVirtualBoxManager::isItemsPoweredOff(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if (!pItem->isItemPoweredOff())
+ return false;
+ return true;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemAbleToShutdown(const QList<UIVirtualMachineItem*> &items)
+{
+ /* Enumerate all the passed items: */
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* Skip non-running machines: */
+ if (!pItem->isItemRunning())
+ continue;
+
+ /* For local machine: */
+ if (pItem->itemType() == UIVirtualMachineItemType_Local)
+ {
+ /* Skip session failures: */
+ CSession session = uiCommon().openExistingSession(pItem->id());
+ if (session.isNull())
+ continue;
+ /* Skip console failures: */
+ CConsole console = session.GetConsole();
+ if (console.isNull())
+ {
+ /* Do not forget to release machine: */
+ session.UnlockMachine();
+ continue;
+ }
+ /* Is the guest entered ACPI mode? */
+ bool fGuestEnteredACPIMode = console.GetGuestEnteredACPIMode();
+ /* Do not forget to release machine: */
+ session.UnlockMachine();
+ /* True if the guest entered ACPI mode: */
+ if (fGuestEnteredACPIMode)
+ return true;
+ }
+ /* For real cloud machine: */
+ else if (pItem->itemType() == UIVirtualMachineItemType_CloudReal)
+ {
+ /* Running cloud VM has it by definition: */
+ return true;
+ }
+ }
+ /* False by default: */
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemSupportsShortcuts(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if ( pItem->accessible()
+ && pItem->toLocal()
+#ifdef VBOX_WS_MAC
+ /* On Mac OS X this are real alias files, which don't work with the old legacy xml files. */
+ && pItem->toLocal()->settingsFile().endsWith(".vbox", Qt::CaseInsensitive)
+#endif
+ )
+ return true;
+ }
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemAccessible(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if (pItem->accessible())
+ return true;
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemInaccessible(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if (!pItem->accessible())
+ return true;
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemRemovable(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if (pItem->isItemRemovable())
+ return true;
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemCanBeStarted(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if (pItem->isItemPoweredOff() && pItem->isItemEditable())
+ return true;
+ }
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemCanBeShown(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if ( pItem->isItemStarted()
+ && pItem->isItemCanBeSwitchedTo())
+ return true;
+ }
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemCanBeStartedOrShown(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ if ( ( pItem->isItemPoweredOff()
+ && pItem->isItemEditable())
+ || ( pItem->isItemStarted()
+ && pItem->isItemCanBeSwitchedTo()))
+ return true;
+ }
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemDiscardable(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if ( pItem->isItemSaved()
+ && pItem->isItemEditable())
+ return true;
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemStarted(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if (pItem->isItemStarted())
+ return true;
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemRunning(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if (pItem->isItemRunning())
+ return true;
+ return false;
+}
+
+/* static */
+bool UIVirtualBoxManager::isAtLeastOneItemDetachable(const QList<UIVirtualMachineItem*> &items)
+{
+ foreach (UIVirtualMachineItem *pItem, items)
+ if (pItem->isItemRunningHeadless())
+ return true;
+ return false;
+}
+
+#ifdef VBOX_WS_X11
+/* static */
+QPair<QString, QString> UIVirtualBoxManager::defaultTerminalData()
+{
+ /* List known terminals: */
+ QStringList knownTerminalNames;
+ knownTerminalNames << "gnome-terminal"
+ << "terminator"
+ << "konsole"
+ << "xfce4-terminal"
+ << "mate-terminal"
+ << "lxterminal"
+ << "tilda"
+ << "xterm"
+ << "aterm"
+ << "rxvt-unicode"
+ << "rxvt";
+
+ /* Fill map of known terminal --execute argument exceptions,
+ * keep in mind, terminals doesn't mentioned here will be
+ * used with default `-e` argument: */
+ QMap<QString, QString> knownTerminalArguments;
+ knownTerminalArguments["gnome-terminal"] = "--";
+ knownTerminalArguments["terminator"] = "-x";
+ knownTerminalArguments["xfce4-terminal"] = "-x";
+ knownTerminalArguments["mate-terminal"] = "-x";
+ knownTerminalArguments["tilda"] = "-c";
+
+ /* Search for a first one suitable through shell command -v test: */
+ foreach (const QString &strTerminalName, knownTerminalNames)
+ {
+ const QString strPath = "sh";
+ const QStringList arguments = QStringList() << "-c" << QString("command -v '%1'").arg(strTerminalName);
+ QProcess process;
+ process.start(strPath, arguments, QIODevice::ReadOnly);
+ process.waitForFinished(3000);
+ if (process.exitCode() == 0)
+ {
+ const QString strResult = process.readAllStandardOutput();
+ if (strResult.startsWith('/'))
+ return qMakePair(strResult.trimmed(), knownTerminalArguments.value(strTerminalName, "-e"));
+ }
+ }
+ return QPair<QString, QString>();
+}
+#endif
+
+
+#include "UIVirtualBoxManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManager.h b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManager.h
new file mode 100644
index 00000000..b5c633ef
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManager.h
@@ -0,0 +1,516 @@
+/* $Id: UIVirtualBoxManager.h $ */
+/** @file
+ * VBox Qt GUI - UIVirtualBoxManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIVirtualBoxManager_h
+#define FEQT_INCLUDED_SRC_manager_UIVirtualBoxManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMainWindow>
+#include <QUrl>
+
+/* GUI includes: */
+#include "QIWithRestorableGeometry.h"
+#include "QIWithRetranslateUI.h"
+#include "UICloudMachineSettingsDialog.h"
+#include "UICommon.h"
+#include "UIExtraDataDefs.h"
+#include "UISettingsDialog.h"
+
+/* Forward declarations: */
+class QMenu;
+class QIManagerDialog;
+class UIAction;
+class UIActionPool;
+struct UIUnattendedInstallData;
+class UIVirtualBoxManagerWidget;
+class UIVirtualMachineItem;
+class CCloudMachine;
+class CUnattended;
+
+/* Type definitions: */
+typedef QIWithRestorableGeometry<QMainWindow> QMainWindowWithRestorableGeometry;
+typedef QIWithRetranslateUI<QMainWindowWithRestorableGeometry> QMainWindowWithRestorableGeometryAndRetranslateUi;
+
+/** Singleton QMainWindow extension used as VirtualBox Manager instance. */
+class UIVirtualBoxManager : public QMainWindowWithRestorableGeometryAndRetranslateUi
+{
+ Q_OBJECT;
+
+ /** Pointer to menu update-handler for this class: */
+ typedef void (UIVirtualBoxManager::*MenuUpdateHandler)(QMenu *pMenu);
+
+signals:
+
+ /** Notifies listeners about this window remapped to another screen. */
+ void sigWindowRemapped();
+
+public:
+
+ /** Singleton constructor. */
+ static void create();
+ /** Singleton destructor. */
+ static void destroy();
+ /** Singleton instance provider. */
+ static UIVirtualBoxManager *instance() { return s_pInstance; }
+
+ /** Returns the action-pool instance. */
+ UIActionPool *actionPool() const { return m_pActionPool; }
+
+ /** Opens Cloud Profile Manager. */
+ void openCloudProfileManager() { sltOpenManagerWindow(UIToolType_Cloud); }
+
+protected:
+
+ /** Constructs VirtualBox Manager. */
+ UIVirtualBoxManager();
+ /** Destructs VirtualBox Manager. */
+ virtual ~UIVirtualBoxManager() RT_OVERRIDE;
+
+ /** Returns whether the window should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const RT_OVERRIDE;
+
+ /** @name Event handling stuff.
+ * @{ */
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Preprocesses any @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+#endif
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles first show @a pEvent. */
+ virtual void polishEvent(QShowEvent *pEvent);
+ /** Handles close @a pEvent. */
+ virtual void closeEvent(QCloseEvent *pEvent) RT_OVERRIDE;
+ /** Handles drag enter @a pEvent. */
+ virtual void dragEnterEvent(QDragEnterEvent *event) RT_OVERRIDE;
+ /** Handles drop @a pEvent. */
+ virtual void dropEvent(QDropEvent *event) RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Common stuff.
+ * @{ */
+#ifdef VBOX_WS_X11
+ /** Handles host-screen available-area change. */
+ void sltHandleHostScreenAvailableAreaChange();
+#endif
+
+ /** Handles request to update actions. */
+ void sltHandleUpdateActionAppearanceRequest() { updateActionsAppearance(); }
+
+ /** Handles request to commit data. */
+ void sltHandleCommitData();
+
+ /** Handles signal about medium-enumeration finished. */
+ void sltHandleMediumEnumerationFinish();
+
+ /** Handles call to open a @a list of URLs. */
+ void sltHandleOpenUrlCall(QList<QUrl> list = QList<QUrl>());
+
+ /** Checks if USB device list can be enumerated and host produces any warning during enumeration. */
+ void sltCheckUSBAccesibility();
+
+ /** Hnadles singal about Chooser-pane index change. */
+ void sltHandleChooserPaneIndexChange();
+ /** Handles signal about group saving progress change. */
+ void sltHandleGroupSavingProgressChange();
+ /** Handles signal about cloud update progress change. */
+ void sltHandleCloudUpdateProgressChange();
+
+ /** Handles singal about Tool type change. */
+ void sltHandleToolTypeChange();
+
+ /** Handles current snapshot item change. */
+ void sltCurrentSnapshotItemChange();
+
+ /** Handles state change for cloud machine with certain @a uId. */
+ void sltHandleCloudMachineStateChange(const QUuid &uId);
+ /** @} */
+
+ /** @name CVirtualBox event handling stuff.
+ * @{ */
+ /** Handles CVirtualBox event about state change for machine with @a uID. */
+ void sltHandleStateChange(const QUuid &uID);
+ /** @} */
+
+ /** @name Action-pool stuff.
+ * @{ */
+ /** Handle menu prepare. */
+ void sltHandleMenuPrepare(int iIndex, QMenu *pMenu);
+ /** @} */
+
+ /** @name File menu stuff.
+ * @{ */
+ /** Handles call to open Manager window of certain @a enmType. */
+ void sltOpenManagerWindow(UIToolType enmType = UIToolType_Invalid);
+ /** Handles call to open Manager window by default. */
+ void sltOpenManagerWindowDefault() { sltOpenManagerWindow(); }
+ /** Handles call to close Manager window of certain @a enmType. */
+ void sltCloseManagerWindow(UIToolType enmType = UIToolType_Invalid);
+ /** Handles call to close Manager window by default. */
+ void sltCloseManagerWindowDefault() { sltCloseManagerWindow(); }
+
+ /** Handles call to open Import Appliance wizard.
+ * @param strFileName can bring the name of file to import appliance from. */
+ void sltOpenImportApplianceWizard(const QString &strFileName = QString());
+ /** Handles call to open Import Appliance wizard the default way. */
+ void sltOpenImportApplianceWizardDefault() { sltOpenImportApplianceWizard(); }
+ /** Handles call to open Export Appliance wizard. */
+ void sltOpenExportApplianceWizard();
+
+#ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ /** Handles call to open Extra-data Manager window. */
+ void sltOpenExtraDataManagerWindow();
+#endif
+
+ /** Handles call to open Preferences dialog. */
+ void sltOpenPreferencesDialog();
+ /** Handles call to close Preferences dialog. */
+ void sltClosePreferencesDialog();
+
+ /** Handles call to exit application. */
+ void sltPerformExit();
+ /** @} */
+
+ /** @name Machine menu stuff.
+ * @{ */
+ /** Handles call to open new machine wizard. */
+ void sltOpenNewMachineWizard();
+ /** Handles call to open add machine dialog. */
+ void sltOpenAddMachineDialog();
+
+ /** Handles call to open group name editor. */
+ void sltOpenGroupNameEditor();
+ /** Handles call to disband group. */
+ void sltDisbandGroup();
+
+ /** Handles call to open Settings dialog.
+ * @param strCategory can bring the settings category to start from.
+ * @param strControl can bring the widget of the page to focus.
+ * @param uID can bring the ID of machine to manage. */
+ void sltOpenSettingsDialog(QString strCategory = QString(),
+ QString strControl = QString(),
+ const QUuid &uID = QUuid());
+ /** Handles call to open Settings dialog the default way. */
+ void sltOpenSettingsDialogDefault() { sltOpenSettingsDialog(); }
+ /** Handles call to close Settings dialog. */
+ void sltCloseSettingsDialog();
+
+ /** Handles call to open Clone Machine wizard. */
+ void sltOpenCloneMachineWizard();
+
+ /** Handles call to move machine. */
+ void sltPerformMachineMove();
+
+ /** Handles call to remove machine. */
+ void sltPerformMachineRemove();
+
+ /** Handles call to move machine to a new group. */
+ void sltPerformMachineMoveToNewGroup();
+ /** Handles call to move machine to a specific group. */
+ void sltPerformMachineMoveToSpecificGroup();
+
+ /** Handles call to start or show machine. */
+ void sltPerformStartOrShowMachine();
+ /** Handles call to start machine in normal mode. */
+ void sltPerformStartMachineNormal();
+ /** Handles call to start machine in headless mode. */
+ void sltPerformStartMachineHeadless();
+ /** Handles call to start machine in detachable mode. */
+ void sltPerformStartMachineDetachable();
+
+ /** Handles call to create console connection for group. */
+ void sltPerformCreateConsoleConnectionForGroup();
+ /** Handles call to create console connection for machine. */
+ void sltPerformCreateConsoleConnectionForMachine();
+ /** Handles call to delete console connection for group. */
+ void sltPerformDeleteConsoleConnectionForGroup();
+ /** Handles call to delete console connection for machine. */
+ void sltPerformDeleteConsoleConnectionForMachine();
+ /** Handles call to copy console connection key fingerprint. */
+ void sltCopyConsoleConnectionFingerprint();
+ /** Handles call to copy serial console command for Unix. */
+ void sltPerformCopyCommandSerialUnix();
+ /** Handles call to copy serial console command for Windows. */
+ void sltPerformCopyCommandSerialWindows();
+ /** Handles call to copy VNC console command for Unix. */
+ void sltPerformCopyCommandVNCUnix();
+ /** Handles call to copy VNC console command for Windows. */
+ void sltPerformCopyCommandVNCWindows();
+ /** Handles call to show console log. */
+ void sltPerformShowLog();
+ /** Handles call about console @a strLog for cloud VM with @a strName read. */
+ void sltHandleConsoleLogRead(const QString &strName, const QString &strLog);
+ /** Handles call to execute external application. */
+ void sltExecuteExternalApplication();
+
+ /** Handles call to discard machine state. */
+ void sltPerformDiscardMachineState();
+
+ /** Handles call to @a fPause or resume machine otherwise. */
+ void sltPerformPauseOrResumeMachine(bool fPause);
+
+ /** Handles call to reset machine. */
+ void sltPerformResetMachine();
+
+ /** Handles call to detach machine UI. */
+ void sltPerformDetachMachineUI();
+ /** Handles call to save machine state. */
+ void sltPerformSaveMachineState();
+ /** Handles call to terminate machine. */
+ void sltPerformTerminateMachine();
+ /** Handles call to ask machine for shutdown. */
+ void sltPerformShutdownMachine();
+ /** Handles call to power machine off. */
+ void sltPerformPowerOffMachine();
+ /** Handles signal about machine powered off.
+ * @param fSuccess Brings whether machine was powered off successfully.
+ * @param fIncludingDiscard Brings whether machine state should be discarded. */
+ void sltHandlePoweredOffMachine(bool fSuccess, bool fIncludingDiscard);
+
+ /** Handles call to show global tool corresponding to passed @a pAction. */
+ void sltPerformShowGlobalTool(QAction *pAction);
+ /** Handles call to show machine tool corresponding to passed @a pAction. */
+ void sltPerformShowMachineTool(QAction *pAction);
+
+ /** Handles call to open machine Log Viewer window. */
+ void sltOpenLogViewerWindow();
+ /** Handles call to close machine Log Viewer window. */
+ void sltCloseLogViewerWindow();
+
+ /** Handles call to refresh machine. */
+ void sltPerformRefreshMachine();
+
+ /** Handles call to show machine in File Manager. */
+ void sltShowMachineInFileManager();
+
+ /** Handles call to create machine shortcut. */
+ void sltPerformCreateMachineShortcut();
+
+ /** Handles call to sort group. */
+ void sltPerformGroupSorting();
+
+ /** Handles call to toggle machine search widget visibility to be @a fVisible. */
+ void sltPerformMachineSearchWidgetVisibilityToggling(bool fVisible);
+
+ /** Handles call to show help viewer. */
+ void sltPerformShowHelpBrowser();
+ /** @} */
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares window. */
+ void prepare();
+ /** Prepares icon. */
+ void prepareIcon();
+ /** Prepares menu-bar. */
+ void prepareMenuBar();
+ /** Prepares status-bar. */
+ void prepareStatusBar();
+ /** Prepares toolbar. */
+ void prepareToolbar();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Loads settings. */
+ void loadSettings();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups widgets. */
+ void cleanupWidgets();
+ /** Cleanups menu-bar. */
+ void cleanupMenuBar();
+ /** Cleanups window. */
+ void cleanup();
+ /** @} */
+
+ /** @name Common stuff.
+ * @{ */
+ /** Returns current-item. */
+ UIVirtualMachineItem *currentItem() const;
+ /** Returns a list of current-items. */
+ QList<UIVirtualMachineItem*> currentItems() const;
+
+ /** Returns whether group saving is in progress. */
+ bool isGroupSavingInProgress() const;
+ /** Returns whether all items of one group is selected. */
+ bool isAllItemsOfOneGroupSelected() const;
+ /** Returns whether single group is selected. */
+ bool isSingleGroupSelected() const;
+ /** Returns whether single local group is selected. */
+ bool isSingleLocalGroupSelected() const;
+ /** Returns whether single cloud provider group is selected. */
+ bool isSingleCloudProviderGroupSelected() const;
+ /** Returns whether single cloud profile group is selected. */
+ bool isSingleCloudProfileGroupSelected() const;
+
+ /** Returns whether at least one cloud profile currently being updated. */
+ bool isCloudProfileUpdateInProgress() const;
+
+ /** Checks if @a comUnattended has any errors.
+ * If so shows an error notification and returns false, else returns true. */
+ bool checkUnattendedInstallError(const CUnattended &comUnattended) const;
+ /** @} */
+
+ /** @name Various VM helpers.
+ * @{ */
+ /** Opens add machine dialog specifying initial name with @a strFileName. */
+ void openAddMachineDialog(const QString &strFileName = QString());
+ /** Opens new machine dialog specifying initial name with @a strFileName. */
+ void openNewMachineWizard(const QString &strISOFilePath = QString());
+ /** Launches certain @a comMachine in specified @a enmLaunchMode. */
+ static void launchMachine(CMachine &comMachine, UILaunchMode enmLaunchMode = UILaunchMode_Default);
+ /** Launches certain @a comMachine. */
+ static void launchMachine(CCloudMachine &comMachine);
+
+ /** Creates an unattended installer and uses it to install guest os to newly created vm. */
+ void startUnattendedInstall(CUnattended &comUnattendedInstaller, bool fStartHeadless, const QString &strMachineId);
+
+ /** Launches or shows virtual machines represented by passed @a items in corresponding @a enmLaunchMode (for launch). */
+ void performStartOrShowVirtualMachines(const QList<UIVirtualMachineItem*> &items, UILaunchMode enmLaunchMode);
+
+#ifndef VBOX_WS_WIN
+ /** Parses serialized @a strArguments string according to shell rules. */
+ QStringList parseShellArguments(const QString &strArguments);
+#endif
+ /** @} */
+
+ /** @name Action update stuff.
+ * @{ */
+ /** Updates 'Group' menu. */
+ void updateMenuGroup(QMenu *pMenu);
+ /** Updates 'Machine' menu. */
+ void updateMenuMachine(QMenu *pMenu);
+ /** Updates 'Group' : 'Move to Group' menu. */
+ void updateMenuGroupMoveToGroup(QMenu *pMenu);
+ /** Updates 'Group' : 'Console' menu. */
+ void updateMenuGroupConsole(QMenu *pMenu);
+ /** Updates 'Group' : 'Close' menu. */
+ void updateMenuGroupClose(QMenu *pMenu);
+ /** Updates 'Machine' : 'Move to Group' menu. */
+ void updateMenuMachineMoveToGroup(QMenu *pMenu);
+ /** Updates 'Machine' : 'Console' menu. */
+ void updateMenuMachineConsole(QMenu *pMenu);
+ /** Updates 'Machine' : 'Close' menu. */
+ void updateMenuMachineClose(QMenu *pMenu);
+
+ /** Performs update of actions visibility. */
+ void updateActionsVisibility();
+ /** Performs update of actions appearance. */
+ void updateActionsAppearance();
+
+ /** Returns whether the action with @a iActionIndex is enabled.
+ * @param items used to calculate verdict about should the action be enabled. */
+ bool isActionEnabled(int iActionIndex, const QList<UIVirtualMachineItem*> &items);
+
+ /** Returns whether all passed @a items are local. */
+ static bool isItemsLocal(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether all passed @a items are cloud. */
+ static bool isItemsCloud(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether all passed @a items are powered off. */
+ static bool isItemsPoweredOff(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items is able to shutdown. */
+ static bool isAtLeastOneItemAbleToShutdown(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items supports shortcut creation. */
+ static bool isAtLeastOneItemSupportsShortcuts(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items is accessible. */
+ static bool isAtLeastOneItemAccessible(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items is inaccessible. */
+ static bool isAtLeastOneItemInaccessible(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items is removable. */
+ static bool isAtLeastOneItemRemovable(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items can be started. */
+ static bool isAtLeastOneItemCanBeStarted(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items can be shown. */
+ static bool isAtLeastOneItemCanBeShown(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items can be started or shown. */
+ static bool isAtLeastOneItemCanBeStartedOrShown(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items can be discarded. */
+ static bool isAtLeastOneItemDiscardable(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items is started. */
+ static bool isAtLeastOneItemStarted(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items is running. */
+ static bool isAtLeastOneItemRunning(const QList<UIVirtualMachineItem*> &items);
+ /** Returns whether at least one of passed @a items is detachable. */
+ static bool isAtLeastOneItemDetachable(const QList<UIVirtualMachineItem*> &items);
+
+#ifdef VBOX_WS_X11
+ /** Tries to guess default X11 terminal emulator.
+ * @returns Data packed into Qt pair of QString(s),
+ * which is `name` and `--execute argument`. */
+ static QPair<QString, QString> defaultTerminalData();
+#endif
+ /** @} */
+
+ /** Holds the static instance. */
+ static UIVirtualBoxManager *s_pInstance;
+
+ /** Holds whether the dialog is polished. */
+ bool m_fPolished : 1;
+ /** Holds whether first medium-enumeration handled. */
+ bool m_fFirstMediumEnumerationHandled : 1;
+
+ /** Holds the action-pool instance. */
+ UIActionPool *m_pActionPool;
+ /** Holds the map of menu update-handlers. */
+ QMap<int, MenuUpdateHandler> m_menuUpdateHandlers;
+
+ /** Holds the map of various global managers. */
+ QMap<UIToolType, QIManagerDialog*> m_managers;
+
+ /** Holds the map of various settings dialogs. */
+ QMap<UISettingsDialog::DialogType, UISettingsDialog*> m_settings;
+ /** Holds the cloud settings dialog instance. */
+ UISafePointerCloudMachineSettingsDialog m_pCloudSettings;
+
+ /** Holds the instance of UIVMLogViewerDialog. */
+ QIManagerDialog *m_pLogViewerDialog;
+
+ /** Holds the central-widget instance. */
+ UIVirtualBoxManagerWidget *m_pWidget;
+
+ /** Holds the geometry save timer ID. */
+ int m_iGeometrySaveTimerId;
+};
+
+#define gpManager UIVirtualBoxManager::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIVirtualBoxManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManagerWidget.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManagerWidget.cpp
new file mode 100644
index 00000000..9c50e0f2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManagerWidget.cpp
@@ -0,0 +1,1144 @@
+/* $Id: UIVirtualBoxManagerWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVirtualBoxManagerWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QStackedWidget>
+#include <QStyle>
+#include <QTimer>
+#include <QToolButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QISplitter.h"
+#include "UIActionPoolManager.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIChooser.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+#include "UIVirtualBoxManager.h"
+#include "UIVirtualBoxManagerWidget.h"
+#include "UITabBar.h"
+#include "QIToolBar.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualMachineItemCloud.h"
+#include "UIVirtualMachineItemLocal.h"
+#include "UITools.h"
+#ifndef VBOX_WS_MAC
+# include "UIMenuBar.h"
+#endif
+#ifdef VBOX_WS_MAC
+# ifdef VBOX_IS_QT6_OR_LATER /* we are branding as Dev Preview since 6.0 */
+# include "UIIconPool.h"
+# endif
+#endif
+
+
+UIVirtualBoxManagerWidget::UIVirtualBoxManagerWidget(UIVirtualBoxManager *pParent)
+ : m_pActionPool(pParent->actionPool())
+ , m_pSplitter(0)
+ , m_pToolBar(0)
+ , m_pPaneChooser(0)
+ , m_pStackedWidget(0)
+ , m_pPaneToolsGlobal(0)
+ , m_pPaneToolsMachine(0)
+ , m_pSlidingAnimation(0)
+ , m_pPaneTools(0)
+ , m_enmSelectionType(SelectionType_Invalid)
+ , m_fSelectedMachineItemAccessible(false)
+ , m_pSplitterSettingsSaveTimer(0)
+{
+ prepare();
+}
+
+UIVirtualBoxManagerWidget::~UIVirtualBoxManagerWidget()
+{
+ cleanup();
+}
+
+UIVirtualMachineItem *UIVirtualBoxManagerWidget::currentItem() const
+{
+ return m_pPaneChooser->currentItem();
+}
+
+QList<UIVirtualMachineItem*> UIVirtualBoxManagerWidget::currentItems() const
+{
+ return m_pPaneChooser->currentItems();
+}
+
+bool UIVirtualBoxManagerWidget::isGroupItemSelected() const
+{
+ return m_pPaneChooser->isGroupItemSelected();
+}
+
+bool UIVirtualBoxManagerWidget::isGlobalItemSelected() const
+{
+ return m_pPaneChooser->isGlobalItemSelected();
+}
+
+bool UIVirtualBoxManagerWidget::isMachineItemSelected() const
+{
+ return m_pPaneChooser->isMachineItemSelected();
+}
+
+bool UIVirtualBoxManagerWidget::isLocalMachineItemSelected() const
+{
+ return m_pPaneChooser->isLocalMachineItemSelected();
+}
+
+bool UIVirtualBoxManagerWidget::isCloudMachineItemSelected() const
+{
+ return m_pPaneChooser->isCloudMachineItemSelected();
+}
+
+bool UIVirtualBoxManagerWidget::isSingleGroupSelected() const
+{
+ return m_pPaneChooser->isSingleGroupSelected();
+}
+
+bool UIVirtualBoxManagerWidget::isSingleLocalGroupSelected() const
+{
+ return m_pPaneChooser->isSingleLocalGroupSelected();
+}
+
+bool UIVirtualBoxManagerWidget::isSingleCloudProviderGroupSelected() const
+{
+ return m_pPaneChooser->isSingleCloudProviderGroupSelected();
+}
+
+bool UIVirtualBoxManagerWidget::isSingleCloudProfileGroupSelected() const
+{
+ return m_pPaneChooser->isSingleCloudProfileGroupSelected();
+}
+
+bool UIVirtualBoxManagerWidget::isAllItemsOfOneGroupSelected() const
+{
+ return m_pPaneChooser->isAllItemsOfOneGroupSelected();
+}
+
+QString UIVirtualBoxManagerWidget::fullGroupName() const
+{
+ return m_pPaneChooser->fullGroupName();
+}
+
+bool UIVirtualBoxManagerWidget::isGroupSavingInProgress() const
+{
+ return m_pPaneChooser->isGroupSavingInProgress();
+}
+
+bool UIVirtualBoxManagerWidget::isCloudProfileUpdateInProgress() const
+{
+ return m_pPaneChooser->isCloudProfileUpdateInProgress();
+}
+
+void UIVirtualBoxManagerWidget::switchToGlobalItem()
+{
+ AssertPtrReturnVoid(m_pPaneChooser);
+ m_pPaneChooser->setCurrentGlobal();
+}
+
+void UIVirtualBoxManagerWidget::openGroupNameEditor()
+{
+ m_pPaneChooser->openGroupNameEditor();
+}
+
+void UIVirtualBoxManagerWidget::disbandGroup()
+{
+ m_pPaneChooser->disbandGroup();
+}
+
+void UIVirtualBoxManagerWidget::removeMachine()
+{
+ m_pPaneChooser->removeMachine();
+}
+
+void UIVirtualBoxManagerWidget::moveMachineToGroup(const QString &strName /* = QString() */)
+{
+ m_pPaneChooser->moveMachineToGroup(strName);
+}
+
+QStringList UIVirtualBoxManagerWidget::possibleGroupsForMachineToMove(const QUuid &uId)
+{
+ return m_pPaneChooser->possibleGroupsForMachineToMove(uId);
+}
+
+QStringList UIVirtualBoxManagerWidget::possibleGroupsForGroupToMove(const QString &strFullName)
+{
+ return m_pPaneChooser->possibleGroupsForGroupToMove(strFullName);
+}
+
+void UIVirtualBoxManagerWidget::refreshMachine()
+{
+ m_pPaneChooser->refreshMachine();
+}
+
+void UIVirtualBoxManagerWidget::sortGroup()
+{
+ m_pPaneChooser->sortGroup();
+}
+
+void UIVirtualBoxManagerWidget::setMachineSearchWidgetVisibility(bool fVisible)
+{
+ m_pPaneChooser->setMachineSearchWidgetVisibility(fVisible);
+}
+
+void UIVirtualBoxManagerWidget::setToolsType(UIToolType enmType)
+{
+ m_pPaneTools->setToolsType(enmType);
+}
+
+UIToolType UIVirtualBoxManagerWidget::toolsType() const
+{
+ return m_pPaneTools ? m_pPaneTools->toolsType() : UIToolType_Invalid;
+}
+
+UIToolType UIVirtualBoxManagerWidget::currentGlobalTool() const
+{
+ return m_pPaneToolsGlobal ? m_pPaneToolsGlobal->currentTool() : UIToolType_Invalid;
+}
+
+UIToolType UIVirtualBoxManagerWidget::currentMachineTool() const
+{
+ return m_pPaneToolsMachine ? m_pPaneToolsMachine->currentTool() : UIToolType_Invalid;
+}
+
+bool UIVirtualBoxManagerWidget::isGlobalToolOpened(UIToolType enmType) const
+{
+ return m_pPaneToolsGlobal ? m_pPaneToolsGlobal->isToolOpened(enmType) : false;
+}
+
+bool UIVirtualBoxManagerWidget::isMachineToolOpened(UIToolType enmType) const
+{
+ return m_pPaneToolsMachine ? m_pPaneToolsMachine->isToolOpened(enmType) : false;
+}
+
+void UIVirtualBoxManagerWidget::switchToGlobalTool(UIToolType enmType)
+{
+ /* Open corresponding tool: */
+ m_pPaneToolsGlobal->openTool(enmType);
+
+ /* Let the parent know: */
+ emit sigToolTypeChange();
+
+ /* Update toolbar: */
+ updateToolbar();
+}
+
+void UIVirtualBoxManagerWidget::switchToMachineTool(UIToolType enmType)
+{
+ /* Open corresponding tool: */
+ m_pPaneToolsMachine->openTool(enmType);
+
+ /* Let the parent know: */
+ emit sigToolTypeChange();
+
+ /* Update toolbar: */
+ updateToolbar();
+}
+
+void UIVirtualBoxManagerWidget::closeGlobalTool(UIToolType enmType)
+{
+ m_pPaneToolsGlobal->closeTool(enmType);
+}
+
+void UIVirtualBoxManagerWidget::closeMachineTool(UIToolType enmType)
+{
+ m_pPaneToolsMachine->closeTool(enmType);
+}
+
+bool UIVirtualBoxManagerWidget::isCurrentStateItemSelected() const
+{
+ return m_pPaneToolsMachine->isCurrentStateItemSelected();
+}
+
+void UIVirtualBoxManagerWidget::updateToolBarMenuButtons(bool fSeparateMenuSection)
+{
+ QToolButton *pButton = qobject_cast<QToolButton*>(m_pToolBar->widgetForAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow)));
+ if (pButton)
+ pButton->setPopupMode(fSeparateMenuSection ? QToolButton::MenuButtonPopup : QToolButton::DelayedPopup);
+}
+
+void UIVirtualBoxManagerWidget::showHelpBrowser()
+{
+ QString strHelpKeyword;
+ if (isGlobalItemSelected())
+ strHelpKeyword = m_pPaneToolsGlobal->currentHelpKeyword();
+ else if (isMachineItemSelected())
+ strHelpKeyword = m_pPaneToolsMachine->currentHelpKeyword();
+
+ msgCenter().sltHandleHelpRequestWithKeyword(strHelpKeyword);
+}
+
+void UIVirtualBoxManagerWidget::sltHandleToolBarContextMenuRequest(const QPoint &position)
+{
+ /* Populate toolbar actions: */
+ QList<QAction*> actions;
+ /* Add 'Show Toolbar Text' action: */
+ QAction *pShowToolBarText = new QAction(UIVirtualBoxManager::tr("Show Toolbar Text"), 0);
+ if (pShowToolBarText)
+ {
+ pShowToolBarText->setCheckable(true);
+ pShowToolBarText->setChecked(m_pToolBar->toolButtonStyle() == Qt::ToolButtonTextUnderIcon);
+ actions << pShowToolBarText;
+ }
+
+ /* Prepare the menu position: */
+ QPoint globalPosition = position;
+ QWidget *pSender = qobject_cast<QWidget*>(sender());
+ if (pSender)
+ globalPosition = pSender->mapToGlobal(position);
+
+ /* Execute the menu: */
+ QAction *pResult = QMenu::exec(actions, globalPosition);
+
+ /* Handle the menu execution result: */
+ if (pResult == pShowToolBarText)
+ {
+ m_pToolBar->setUseTextLabels(pResult->isChecked());
+ gEDataManager->setSelectorWindowToolBarTextVisible(pResult->isChecked());
+ }
+}
+
+void UIVirtualBoxManagerWidget::retranslateUi()
+{
+ /* Make sure chosen item fetched: */
+ sltHandleChooserPaneIndexChange();
+
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text.
+ m_pToolBar->updateLayout();
+#endif
+}
+
+void UIVirtualBoxManagerWidget::sltHandleStateChange(const QUuid &uId)
+{
+ // WORKAROUND:
+ // In certain intermediate states VM info can be NULL which
+ // causing annoying assertions, such updates can be ignored?
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CMachine comMachine = comVBox.FindMachine(uId.toString());
+ if (comVBox.isOk() && comMachine.isNotNull())
+ {
+ switch (comMachine.GetState())
+ {
+ case KMachineState_DeletingSnapshot:
+ return;
+ default:
+ break;
+ }
+ }
+
+ /* Recache current item info if machine or group item selected: */
+ if (isMachineItemSelected() || isGroupItemSelected())
+ recacheCurrentItemInformation();
+}
+
+void UIVirtualBoxManagerWidget::sltHandleSplitterMove()
+{
+ /* Create timer if isn't exist already: */
+ if (!m_pSplitterSettingsSaveTimer)
+ {
+ m_pSplitterSettingsSaveTimer = new QTimer(this);
+ if (m_pSplitterSettingsSaveTimer)
+ {
+ m_pSplitterSettingsSaveTimer->setInterval(300);
+ m_pSplitterSettingsSaveTimer->setSingleShot(true);
+ connect(m_pSplitterSettingsSaveTimer, &QTimer::timeout,
+ this, &UIVirtualBoxManagerWidget::sltSaveSplitterSettings);
+ }
+ }
+ /* [Re]start timer finally: */
+ m_pSplitterSettingsSaveTimer->start();
+}
+
+void UIVirtualBoxManagerWidget::sltSaveSplitterSettings()
+{
+ const QList<int> splitterSizes = m_pSplitter->sizes();
+ LogRel2(("GUI: UIVirtualBoxManagerWidget: Saving splitter as: Size=%d,%d\n",
+ splitterSizes.at(0), splitterSizes.at(1)));
+ gEDataManager->setSelectorWindowSplitterHints(splitterSizes);
+}
+
+void UIVirtualBoxManagerWidget::sltHandleToolBarResize(const QSize &newSize)
+{
+ emit sigToolBarHeightChange(newSize.height());
+}
+
+void UIVirtualBoxManagerWidget::sltHandleChooserPaneIndexChange()
+{
+ /* Let the parent know: */
+ emit sigChooserPaneIndexChange();
+
+ /* If global item is selected and we are on machine tools pane => switch to global tools pane: */
+ if ( isGlobalItemSelected()
+ && m_pStackedWidget->currentWidget() != m_pPaneToolsGlobal)
+ {
+ /* Just start animation and return, do nothing else.. */
+ m_pStackedWidget->setCurrentWidget(m_pPaneToolsGlobal); // rendering w/a
+ m_pStackedWidget->setCurrentWidget(m_pSlidingAnimation);
+ m_pSlidingAnimation->animate(SlidingDirection_Reverse);
+ return;
+ }
+
+ else
+
+ /* If machine or group item is selected and we are on global tools pane => switch to machine tools pane: */
+ if ( (isMachineItemSelected() || isGroupItemSelected())
+ && m_pStackedWidget->currentWidget() != m_pPaneToolsMachine)
+ {
+ /* Just start animation and return, do nothing else.. */
+ m_pStackedWidget->setCurrentWidget(m_pPaneToolsMachine); // rendering w/a
+ m_pStackedWidget->setCurrentWidget(m_pSlidingAnimation);
+ m_pSlidingAnimation->animate(SlidingDirection_Forward);
+ return;
+ }
+
+ /* Recache current item info if machine or group item selected: */
+ if (isMachineItemSelected() || isGroupItemSelected())
+ recacheCurrentItemInformation();
+
+ /* Calculate selection type: */
+ const SelectionType enmSelectedItemType = isSingleLocalGroupSelected()
+ ? SelectionType_SingleLocalGroupItem
+ : isSingleCloudProviderGroupSelected() || isSingleCloudProfileGroupSelected()
+ ? SelectionType_SingleCloudGroupItem
+ : isGlobalItemSelected()
+ ? SelectionType_FirstIsGlobalItem
+ : isLocalMachineItemSelected()
+ ? SelectionType_FirstIsLocalMachineItem
+ : isCloudMachineItemSelected()
+ ? SelectionType_FirstIsCloudMachineItem
+ : SelectionType_Invalid;
+ /* Acquire current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ const bool fCurrentItemIsOk = pItem && pItem->accessible();
+
+ /* Update toolbar if selection type or item accessibility got changed: */
+ if ( m_enmSelectionType != enmSelectedItemType
+ || m_fSelectedMachineItemAccessible != fCurrentItemIsOk)
+ updateToolbar();
+
+ /* Remember the last selection type: */
+ m_enmSelectionType = enmSelectedItemType;
+ /* Remember whether the last selected item was accessible: */
+ m_fSelectedMachineItemAccessible = fCurrentItemIsOk;
+}
+
+void UIVirtualBoxManagerWidget::sltHandleSlidingAnimationComplete(SlidingDirection enmDirection)
+{
+ /* First switch the panes: */
+ switch (enmDirection)
+ {
+ case SlidingDirection_Forward:
+ {
+ m_pPaneTools->setToolsClass(UIToolClass_Machine);
+ m_pStackedWidget->setCurrentWidget(m_pPaneToolsMachine);
+ m_pPaneToolsGlobal->setActive(false);
+ m_pPaneToolsMachine->setActive(true);
+ break;
+ }
+ case SlidingDirection_Reverse:
+ {
+ m_pPaneTools->setToolsClass(UIToolClass_Global);
+ m_pStackedWidget->setCurrentWidget(m_pPaneToolsGlobal);
+ m_pPaneToolsMachine->setActive(false);
+ m_pPaneToolsGlobal->setActive(true);
+ break;
+ }
+ }
+ /* Then handle current item change (again!): */
+ sltHandleChooserPaneIndexChange();
+}
+
+void UIVirtualBoxManagerWidget::sltHandleCloudMachineStateChange(const QUuid &uId)
+{
+ /* Not for global items: */
+ if (!isGlobalItemSelected())
+ {
+ /* Acquire current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ const bool fCurrentItemIsOk = pItem && pItem->accessible();
+
+ /* If current item is Ok: */
+ if (fCurrentItemIsOk)
+ {
+ /* If Error-pane is chosen currently => open tool currently chosen in Tools-pane: */
+ if (m_pPaneToolsMachine->currentTool() == UIToolType_Error)
+ sltHandleToolsPaneIndexChange();
+
+ /* If we still have same item selected: */
+ if (pItem && pItem->id() == uId)
+ {
+ /* Propagate current items to update the Details-pane: */
+ m_pPaneToolsMachine->setItems(currentItems());
+ }
+ }
+ else
+ {
+ /* Make sure Error pane raised: */
+ if (m_pPaneToolsMachine->currentTool() != UIToolType_Error)
+ m_pPaneToolsMachine->openTool(UIToolType_Error);
+
+ /* If we still have same item selected: */
+ if (pItem && pItem->id() == uId)
+ {
+ /* Propagate current items to update the Details-pane (in any case): */
+ m_pPaneToolsMachine->setItems(currentItems());
+ /* Propagate last access error to update the Error-pane (if machine selected but inaccessible): */
+ m_pPaneToolsMachine->setErrorDetails(pItem->accessError());
+ }
+ }
+
+ /* Pass the signal further: */
+ emit sigCloudMachineStateChange(uId);
+ }
+}
+
+void UIVirtualBoxManagerWidget::sltHandleToolMenuRequested(UIToolClass enmClass, const QPoint &position)
+{
+ /* Define current tools class: */
+ m_pPaneTools->setToolsClass(enmClass);
+
+ /* Compose popup-menu geometry first of all: */
+ QRect ourGeo = QRect(position, m_pPaneTools->minimumSizeHint());
+ /* Adjust location only to properly fit into available geometry space: */
+ const QRect availableGeo = gpDesktop->availableGeometry(position);
+ ourGeo = gpDesktop->normalizeGeometry(ourGeo, availableGeo, false /* resize? */);
+
+ /* Move, resize and show: */
+ m_pPaneTools->move(ourGeo.topLeft());
+ m_pPaneTools->show();
+ // WORKAROUND:
+ // Don't want even to think why, but for Qt::Popup resize to
+ // smaller size often being ignored until it is actually shown.
+ m_pPaneTools->resize(ourGeo.size());
+}
+
+void UIVirtualBoxManagerWidget::sltHandleToolsPaneIndexChange()
+{
+ /* Acquire current class/type: */
+ const UIToolClass enmCurrentClass = m_pPaneTools->toolsClass();
+ const UIToolType enmCurrentType = m_pPaneTools->toolsType();
+
+ /* Invent default for fallback case: */
+ const UIToolType enmDefaultType = enmCurrentClass == UIToolClass_Global ? UIToolType_Welcome
+ : enmCurrentClass == UIToolClass_Machine ? UIToolType_Details
+ : UIToolType_Invalid;
+ AssertReturnVoid(enmDefaultType != UIToolType_Invalid);
+
+ /* Calculate new type to choose: */
+ const UIToolType enmNewType = UIToolStuff::isTypeOfClass(enmCurrentType, enmCurrentClass)
+ ? enmCurrentType : enmDefaultType;
+
+ /* Choose new type: */
+ switch (m_pPaneTools->toolsClass())
+ {
+ case UIToolClass_Global: switchToGlobalTool(enmNewType); break;
+ case UIToolClass_Machine: switchToMachineTool(enmNewType); break;
+ default: break;
+ }
+}
+
+void UIVirtualBoxManagerWidget::sltSwitchToMachineActivityPane(const QUuid &uMachineId)
+{
+ AssertPtrReturnVoid(m_pPaneChooser);
+ AssertPtrReturnVoid(m_pPaneTools);
+ m_pPaneChooser->setCurrentMachine(uMachineId);
+ m_pPaneTools->setToolsType(UIToolType_VMActivity);
+}
+
+void UIVirtualBoxManagerWidget::sltSwitchToActivityOverviewPane()
+{
+ AssertPtrReturnVoid(m_pPaneChooser);
+ AssertPtrReturnVoid(m_pPaneTools);
+ m_pPaneTools->setToolsType(UIToolType_VMActivityOverview);
+ m_pPaneChooser->setCurrentGlobal();
+}
+
+void UIVirtualBoxManagerWidget::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Translate UI: */
+ retranslateUi();
+
+ /* Make sure current Chooser-pane index fetched: */
+ sltHandleChooserPaneIndexChange();
+}
+
+void UIVirtualBoxManagerWidget::prepareWidgets()
+{
+ /* Create main-layout: */
+ QHBoxLayout *pLayoutMain = new QHBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Configure layout: */
+ pLayoutMain->setSpacing(0);
+ pLayoutMain->setContentsMargins(0, 0, 0, 0);
+
+ /* Create splitter: */
+ m_pSplitter = new QISplitter(Qt::Horizontal, QISplitter::Flat);
+ if (m_pSplitter)
+ {
+ /* Configure splitter: */
+ m_pSplitter->setHandleWidth(1);
+
+ /* Create Chooser-pane: */
+ m_pPaneChooser = new UIChooser(this, actionPool());
+ if (m_pPaneChooser)
+ {
+ /* Add into splitter: */
+ m_pSplitter->addWidget(m_pPaneChooser);
+ }
+
+ /* Create right widget: */
+ QWidget *pWidgetRight = new QWidget;
+ if (pWidgetRight)
+ {
+ /* Create right-layout: */
+ QVBoxLayout *pLayoutRight = new QVBoxLayout(pWidgetRight);
+ if(pLayoutRight)
+ {
+ /* Configure layout: */
+ pLayoutRight->setSpacing(0);
+ pLayoutRight->setContentsMargins(0, 0, 0, 0);
+
+ /* Create Main toolbar: */
+ m_pToolBar = new QIToolBar;
+ if (m_pToolBar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_pToolBar->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pToolBar->setUseTextLabels(true);
+#ifdef VBOX_WS_MAC
+ m_pToolBar->emulateMacToolbar();
+# ifdef VBOX_IS_QT6_OR_LATER /* we are branding as Dev Preview since 6.0 */
+ /* Branding stuff for Qt6 beta: */
+ if (uiCommon().showBetaLabel())
+ m_pToolBar->enableBranding(UIIconPool::iconSet(":/explosion_hazard_32px.png"),
+ "Dev Preview", // do we need to make it NLS?
+ QColor(246, 179, 0),
+ 74 /* width of BETA label */);
+# endif
+#endif /* VBOX_WS_MAC */
+
+ /* Add toolbar into layout: */
+ pLayoutRight->addWidget(m_pToolBar);
+ }
+
+ /* Create stacked-widget: */
+ m_pStackedWidget = new QStackedWidget;
+ if (m_pStackedWidget)
+ {
+ /* Create Global Tools-pane: */
+ m_pPaneToolsGlobal = new UIToolPaneGlobal(actionPool());
+ if (m_pPaneToolsGlobal)
+ {
+ if (m_pPaneChooser->isGlobalItemSelected())
+ m_pPaneToolsGlobal->setActive(true);
+ connect(m_pPaneToolsGlobal, &UIToolPaneGlobal::sigSwitchToMachineActivityPane,
+ this, &UIVirtualBoxManagerWidget::sltSwitchToMachineActivityPane);
+
+ /* Add into stack: */
+ m_pStackedWidget->addWidget(m_pPaneToolsGlobal);
+ }
+
+ /* Create Machine Tools-pane: */
+ m_pPaneToolsMachine = new UIToolPaneMachine(actionPool());
+ if (m_pPaneToolsMachine)
+ {
+ if (!m_pPaneChooser->isGlobalItemSelected())
+ m_pPaneToolsMachine->setActive(true);
+ connect(m_pPaneToolsMachine, &UIToolPaneMachine::sigCurrentSnapshotItemChange,
+ this, &UIVirtualBoxManagerWidget::sigCurrentSnapshotItemChange);
+ connect(m_pPaneToolsMachine, &UIToolPaneMachine::sigSwitchToActivityOverviewPane,
+ this, &UIVirtualBoxManagerWidget::sltSwitchToActivityOverviewPane);
+
+ /* Add into stack: */
+ m_pStackedWidget->addWidget(m_pPaneToolsMachine);
+ }
+
+ /* Create sliding-widget: */
+ // Reverse initial animation direction if group or machine selected!
+ const bool fReverse = !m_pPaneChooser->isGlobalItemSelected();
+ m_pSlidingAnimation = new UISlidingAnimation(Qt::Vertical, fReverse);
+ if (m_pSlidingAnimation)
+ {
+ /* Add first/second widgets into sliding animation: */
+ m_pSlidingAnimation->setWidgets(m_pPaneToolsGlobal, m_pPaneToolsMachine);
+ connect(m_pSlidingAnimation, &UISlidingAnimation::sigAnimationComplete,
+ this, &UIVirtualBoxManagerWidget::sltHandleSlidingAnimationComplete);
+
+ /* Add into stack: */
+ m_pStackedWidget->addWidget(m_pSlidingAnimation);
+ }
+
+ /* Choose which pane should be active initially: */
+ if (m_pPaneChooser->isGlobalItemSelected())
+ m_pStackedWidget->setCurrentWidget(m_pPaneToolsGlobal);
+ else
+ m_pStackedWidget->setCurrentWidget(m_pPaneToolsMachine);
+
+ /* Add into layout: */
+ pLayoutRight->addWidget(m_pStackedWidget, 1);
+ }
+ }
+
+ /* Add into splitter: */
+ m_pSplitter->addWidget(pWidgetRight);
+ }
+
+ /* Adjust splitter colors according to main widgets it splits: */
+ m_pSplitter->configureColor(QApplication::palette().color(QPalette::Active, QPalette::Window).darker(130));
+ /* Set the initial distribution. The right site is bigger. */
+ m_pSplitter->setStretchFactor(0, 2);
+ m_pSplitter->setStretchFactor(1, 3);
+
+ /* Add into layout: */
+ pLayoutMain->addWidget(m_pSplitter);
+ }
+
+ /* Create Tools-pane: */
+ m_pPaneTools = new UITools(this);
+ if (m_pPaneTools)
+ {
+ /* Choose which pane should be active initially: */
+ if (m_pPaneChooser->isGlobalItemSelected())
+ m_pPaneTools->setToolsClass(UIToolClass_Global);
+ else
+ m_pPaneTools->setToolsClass(UIToolClass_Machine);
+ }
+ }
+
+ /* Create notification-center: */
+ UINotificationCenter::create(this);
+
+ /* Update toolbar finally: */
+ updateToolbar();
+
+ /* Bring the VM list to the focus: */
+ m_pPaneChooser->setFocus();
+}
+
+void UIVirtualBoxManagerWidget::prepareConnections()
+{
+ /* Global VBox event handlers: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UIVirtualBoxManagerWidget::sltHandleStateChange);
+
+ /* Splitter connections: */
+ connect(m_pSplitter, &QISplitter::splitterMoved,
+ this, &UIVirtualBoxManagerWidget::sltHandleSplitterMove);
+
+ /* Tool-bar connections: */
+ connect(m_pToolBar, &QIToolBar::customContextMenuRequested,
+ this, &UIVirtualBoxManagerWidget::sltHandleToolBarContextMenuRequest);
+ connect(m_pToolBar, &QIToolBar::sigResized,
+ this, &UIVirtualBoxManagerWidget::sltHandleToolBarResize);
+
+ /* Chooser-pane connections: */
+ connect(this, &UIVirtualBoxManagerWidget::sigToolBarHeightChange,
+ m_pPaneChooser, &UIChooser::setGlobalItemHeightHint);
+ connect(m_pPaneChooser, &UIChooser::sigSelectionChanged,
+ this, &UIVirtualBoxManagerWidget::sltHandleChooserPaneIndexChange);
+ connect(m_pPaneChooser, &UIChooser::sigSelectionInvalidated,
+ this, &UIVirtualBoxManagerWidget::sltHandleChooserPaneSelectionInvalidated);
+ connect(m_pPaneChooser, &UIChooser::sigToggleStarted,
+ m_pPaneToolsMachine, &UIToolPaneMachine::sigToggleStarted);
+ connect(m_pPaneChooser, &UIChooser::sigToggleFinished,
+ m_pPaneToolsMachine, &UIToolPaneMachine::sigToggleFinished);
+ connect(m_pPaneChooser, &UIChooser::sigGroupSavingStateChanged,
+ this, &UIVirtualBoxManagerWidget::sigGroupSavingStateChanged);
+ connect(m_pPaneChooser, &UIChooser::sigCloudUpdateStateChanged,
+ this, &UIVirtualBoxManagerWidget::sigCloudUpdateStateChanged);
+ connect(m_pPaneChooser, &UIChooser::sigToolMenuRequested,
+ this, &UIVirtualBoxManagerWidget::sltHandleToolMenuRequested);
+ connect(m_pPaneChooser, &UIChooser::sigCloudMachineStateChange,
+ this, &UIVirtualBoxManagerWidget::sltHandleCloudMachineStateChange);
+ connect(m_pPaneChooser, &UIChooser::sigStartOrShowRequest,
+ this, &UIVirtualBoxManagerWidget::sigStartOrShowRequest);
+ connect(m_pPaneChooser, &UIChooser::sigMachineSearchWidgetVisibilityChanged,
+ this, &UIVirtualBoxManagerWidget::sigMachineSearchWidgetVisibilityChanged);
+
+ /* Details-pane connections: */
+ connect(m_pPaneToolsMachine, &UIToolPaneMachine::sigLinkClicked,
+ this, &UIVirtualBoxManagerWidget::sigMachineSettingsLinkClicked);
+
+ /* Tools-pane connections: */
+ connect(m_pPaneTools, &UITools::sigSelectionChanged,
+ this, &UIVirtualBoxManagerWidget::sltHandleToolsPaneIndexChange);
+}
+
+void UIVirtualBoxManagerWidget::loadSettings()
+{
+ /* Restore splitter handle position: */
+ {
+ QList<int> sizes = gEDataManager->selectorWindowSplitterHints();
+ /* If both hints are zero, we have the 'default' case: */
+ if (sizes.at(0) == 0 && sizes.at(1) == 0)
+ {
+ sizes[0] = (int)(width() * .9 * (1.0 / 3));
+ sizes[1] = (int)(width() * .9 * (2.0 / 3));
+ }
+ LogRel2(("GUI: UIVirtualBoxManagerWidget: Restoring splitter to: Size=%d,%d\n",
+ sizes.at(0), sizes.at(1)));
+ m_pSplitter->setSizes(sizes);
+ }
+
+ /* Restore toolbar settings: */
+ {
+ m_pToolBar->setUseTextLabels(gEDataManager->selectorWindowToolBarTextVisible());
+ }
+
+ /* Open tools last chosen in Tools-pane: */
+ switchToGlobalTool(m_pPaneTools->lastSelectedToolGlobal());
+ switchToMachineTool(m_pPaneTools->lastSelectedToolMachine());
+}
+
+void UIVirtualBoxManagerWidget::updateToolbar()
+{
+ /* Make sure toolbar exists: */
+ AssertPtrReturnVoid(m_pToolBar);
+
+ /* Clear initially: */
+ m_pToolBar->clear();
+
+ /* Basic action set: */
+ switch (m_pPaneTools->toolsClass())
+ {
+ /* Global toolbar: */
+ case UIToolClass_Global:
+ {
+ switch (currentGlobalTool())
+ {
+ case UIToolType_Welcome:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Application_S_Preferences));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ImportAppliance));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ExportAppliance));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Welcome_S_New));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Welcome_S_Add));
+ break;
+ }
+ case UIToolType_Extensions:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Extension_S_Install));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Extension_S_Uninstall));
+ break;
+ }
+ case UIToolType_Media:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_S_Add));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_S_Create));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_S_Copy));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_S_Move));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_S_Remove));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_S_Release));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_S_Clear));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_T_Search));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_T_Details));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Medium_S_Refresh));
+ break;
+ }
+ case UIToolType_Network:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Network_S_Create));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Network_S_Remove));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Network_T_Details));
+ //m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Network_S_Refresh));
+ break;
+ }
+ case UIToolType_Cloud:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Cloud_S_Add));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Cloud_S_Import));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Cloud_S_Remove));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Cloud_T_Details));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Cloud_S_TryPage));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Cloud_S_Help));
+ break;
+ }
+ case UIToolType_VMActivityOverview:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_VMActivityOverview_M_Columns));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_VMActivityOverview_S_SwitchToMachineActivity));
+ QToolButton *pButton =
+ qobject_cast<QToolButton*>(m_pToolBar->widgetForAction(actionPool()->action(UIActionIndexMN_M_VMActivityOverview_M_Columns)));
+ if (pButton)
+ {
+ pButton->setPopupMode(QToolButton::InstantPopup);
+ pButton->setAutoRaise(true);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
+ /* Machine toolbar: */
+ case UIToolClass_Machine:
+ {
+ switch (currentMachineTool())
+ {
+ case UIToolType_Details:
+ {
+ if (isSingleGroupSelected())
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_New));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Add));
+ m_pToolBar->addSeparator();
+ if (isSingleLocalGroupSelected())
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Discard));
+ else if ( isSingleCloudProviderGroupSelected()
+ || isSingleCloudProfileGroupSelected())
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_Stop_S_Terminate));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow));
+ }
+ else
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_New));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Add));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
+ if (isLocalMachineItemSelected())
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard));
+ else if (isCloudMachineItemSelected())
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop_S_Terminate));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
+ }
+ break;
+ }
+ case UIToolType_Snapshots:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Snapshot_S_Take));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Snapshot_S_Delete));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Snapshot_S_Restore));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Snapshot_T_Properties));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Snapshot_S_Clone));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
+ break;
+ }
+ case UIToolType_Logs:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Log_S_Save));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Log_T_Find));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Log_T_Filter));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Log_T_Bookmark));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Log_T_Options));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Log_S_Refresh));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Log_S_Reload));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
+ break;
+ }
+ case UIToolType_VMActivity:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Activity_S_Export));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndex_M_Activity_S_ToVMActivityOverview));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
+ break;
+ }
+ case UIToolType_FileManager:
+ {
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_Options));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_Operations));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_FileManager_T_Log));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
+ break;
+ }
+ case UIToolType_Error:
+ {
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_New));
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Add));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Refresh));
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // Actually Qt should do that itself but by some unknown reason it sometimes
+ // forget to update toolbar after changing its actions on Cocoa platform.
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_New), &UIAction::changed,
+ m_pToolBar, static_cast<void(QIToolBar::*)(void)>(&QIToolBar::update));
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings), &UIAction::changed,
+ m_pToolBar, static_cast<void(QIToolBar::*)(void)>(&QIToolBar::update));
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard), &UIAction::changed,
+ m_pToolBar, static_cast<void(QIToolBar::*)(void)>(&QIToolBar::update));
+ connect(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow), &UIAction::changed,
+ m_pToolBar, static_cast<void(QIToolBar::*)(void)>(&QIToolBar::update));
+
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text.
+ m_pToolBar->updateLayout();
+#endif /* VBOX_WS_MAC */
+}
+
+void UIVirtualBoxManagerWidget::cleanupConnections()
+{
+ /* Tool-bar connections: */
+ disconnect(m_pToolBar, &QIToolBar::customContextMenuRequested,
+ this, &UIVirtualBoxManagerWidget::sltHandleToolBarContextMenuRequest);
+ disconnect(m_pToolBar, &QIToolBar::sigResized,
+ this, &UIVirtualBoxManagerWidget::sltHandleToolBarResize);
+
+ /* Chooser-pane connections: */
+ disconnect(this, &UIVirtualBoxManagerWidget::sigToolBarHeightChange,
+ m_pPaneChooser, &UIChooser::setGlobalItemHeightHint);
+ disconnect(m_pPaneChooser, &UIChooser::sigSelectionChanged,
+ this, &UIVirtualBoxManagerWidget::sltHandleChooserPaneIndexChange);
+ disconnect(m_pPaneChooser, &UIChooser::sigSelectionInvalidated,
+ this, &UIVirtualBoxManagerWidget::sltHandleChooserPaneSelectionInvalidated);
+ disconnect(m_pPaneChooser, &UIChooser::sigToggleStarted,
+ m_pPaneToolsMachine, &UIToolPaneMachine::sigToggleStarted);
+ disconnect(m_pPaneChooser, &UIChooser::sigToggleFinished,
+ m_pPaneToolsMachine, &UIToolPaneMachine::sigToggleFinished);
+ disconnect(m_pPaneChooser, &UIChooser::sigGroupSavingStateChanged,
+ this, &UIVirtualBoxManagerWidget::sigGroupSavingStateChanged);
+ disconnect(m_pPaneChooser, &UIChooser::sigCloudUpdateStateChanged,
+ this, &UIVirtualBoxManagerWidget::sigCloudUpdateStateChanged);
+ disconnect(m_pPaneChooser, &UIChooser::sigToolMenuRequested,
+ this, &UIVirtualBoxManagerWidget::sltHandleToolMenuRequested);
+ disconnect(m_pPaneChooser, &UIChooser::sigCloudMachineStateChange,
+ this, &UIVirtualBoxManagerWidget::sltHandleCloudMachineStateChange);
+ disconnect(m_pPaneChooser, &UIChooser::sigStartOrShowRequest,
+ this, &UIVirtualBoxManagerWidget::sigStartOrShowRequest);
+ disconnect(m_pPaneChooser, &UIChooser::sigMachineSearchWidgetVisibilityChanged,
+ this, &UIVirtualBoxManagerWidget::sigMachineSearchWidgetVisibilityChanged);
+
+ /* Details-pane connections: */
+ disconnect(m_pPaneToolsMachine, &UIToolPaneMachine::sigLinkClicked,
+ this, &UIVirtualBoxManagerWidget::sigMachineSettingsLinkClicked);
+
+ /* Tools-pane connections: */
+ disconnect(m_pPaneTools, &UITools::sigSelectionChanged,
+ this, &UIVirtualBoxManagerWidget::sltHandleToolsPaneIndexChange);
+}
+
+void UIVirtualBoxManagerWidget::cleanupWidgets()
+{
+ UINotificationCenter::destroy();
+}
+
+void UIVirtualBoxManagerWidget::cleanup()
+{
+ /* Cleanup everything: */
+ cleanupConnections();
+ cleanupWidgets();
+}
+
+void UIVirtualBoxManagerWidget::recacheCurrentItemInformation(bool fDontRaiseErrorPane /* = false */)
+{
+ /* Get current item: */
+ UIVirtualMachineItem *pItem = currentItem();
+ const bool fCurrentItemIsOk = pItem && pItem->accessible();
+
+ /* Update machine tools restrictions: */
+ QList<UIToolType> retrictedTypes;
+ if (pItem && pItem->itemType() != UIVirtualMachineItemType_Local)
+ retrictedTypes << UIToolType_Snapshots << UIToolType_Logs << UIToolType_VMActivity;
+ if (retrictedTypes.contains(m_pPaneTools->toolsType()))
+ m_pPaneTools->setToolsType(UIToolType_Details);
+ m_pPaneTools->setRestrictedToolTypes(retrictedTypes);
+ /* Update machine tools availability: */
+ m_pPaneTools->setToolClassEnabled(UIToolClass_Machine, fCurrentItemIsOk);
+
+ /* Take restrictions into account, closing all restricted tools: */
+ foreach (const UIToolType &enmRestrictedType, retrictedTypes)
+ m_pPaneToolsMachine->closeTool(enmRestrictedType);
+
+ /* Propagate current item anyway: */
+ m_pPaneToolsMachine->setCurrentItem(pItem);
+
+ /* If current item is Ok: */
+ if (fCurrentItemIsOk)
+ {
+ /* If Error-pane is chosen currently => open tool currently chosen in Tools-pane: */
+ if (m_pPaneToolsMachine->currentTool() == UIToolType_Error)
+ sltHandleToolsPaneIndexChange();
+ }
+ else
+ {
+ /* If we were not asked separately: */
+ if (!fDontRaiseErrorPane)
+ {
+ /* Make sure Error pane raised: */
+ m_pPaneToolsMachine->openTool(UIToolType_Error);
+
+ /* Propagate last access error to update the Error-pane (if machine selected but inaccessible): */
+ if (pItem)
+ m_pPaneToolsMachine->setErrorDetails(pItem->accessError());
+ }
+ }
+
+ /* Propagate current items to update the Details-pane: */
+ m_pPaneToolsMachine->setItems(currentItems());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManagerWidget.h b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManagerWidget.h
new file mode 100644
index 00000000..74b5ec49
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualBoxManagerWidget.h
@@ -0,0 +1,360 @@
+/* $Id: UIVirtualBoxManagerWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIVirtualBoxManagerWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIVirtualBoxManagerWidget_h
+#define FEQT_INCLUDED_SRC_manager_UIVirtualBoxManagerWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UISlidingAnimation.h"
+#include "UIToolPaneGlobal.h"
+#include "UIToolPaneMachine.h"
+
+/* Forward declarations: */
+class QStackedWidget;
+class QTimer;
+class QISplitter;
+class QIToolBar;
+class UIActionPool;
+class UIChooser;
+class UITabBar;
+class UITools;
+class UIVirtualBoxManager;
+class UIVirtualMachineItem;
+
+/** QWidget extension used as VirtualBox Manager Widget instance. */
+class UIVirtualBoxManagerWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+ /** Possible selection types. */
+ enum SelectionType
+ {
+ SelectionType_Invalid,
+ SelectionType_SingleLocalGroupItem,
+ SelectionType_SingleCloudGroupItem,
+ SelectionType_FirstIsGlobalItem,
+ SelectionType_FirstIsLocalMachineItem,
+ SelectionType_FirstIsCloudMachineItem
+ };
+
+signals:
+
+ /** @name Tool-bar stuff.
+ * @{ */
+ /* Notifies listeners about tool-bar height change. */
+ void sigToolBarHeightChange(int iHeight);
+ /** @} */
+
+ /** @name Chooser pane stuff.
+ * @{ */
+ /** Notifies about Chooser-pane index change. */
+ void sigChooserPaneIndexChange();
+ /** Notifies about Chooser-pane group saving change. */
+ void sigGroupSavingStateChanged();
+ /** Notifies about Chooser-pane cloud update change. */
+ void sigCloudUpdateStateChanged();
+
+ /** Notifies about state change for cloud machine with certain @a uId. */
+ void sigCloudMachineStateChange(const QUuid &uId);
+
+ /** Notify listeners about start or show request. */
+ void sigStartOrShowRequest();
+ /** Notifies listeners about machine search widget visibility changed to @a fVisible. */
+ void sigMachineSearchWidgetVisibilityChanged(bool fVisible);
+ /** @} */
+
+ /** @name Tools pane stuff.
+ * @{ */
+ /** Notifies about Tool type change. */
+ void sigToolTypeChange();
+ /** @} */
+
+ /** @name Tools / Details pane stuff.
+ * @{ */
+ /** Notifies aboud Details-pane link clicked. */
+ void sigMachineSettingsLinkClicked(const QString &strCategory, const QString &strControl, const QUuid &uId);
+ /** @} */
+
+ /** @name Tools / Snapshots pane stuff.
+ * @{ */
+ /** Notifies listeners about current Snapshots pane item change. */
+ void sigCurrentSnapshotItemChange();
+ /** @} */
+
+public:
+
+ /** Constructs VirtualBox Manager widget. */
+ UIVirtualBoxManagerWidget(UIVirtualBoxManager *pParent);
+ /** Destructs VirtualBox Manager widget. */
+ virtual ~UIVirtualBoxManagerWidget() RT_OVERRIDE;
+
+ /** @name Common stuff.
+ * @{ */
+ /** Returns the action-pool instance. */
+ UIActionPool *actionPool() const { return m_pActionPool; }
+ /** @} */
+
+ /** @name Chooser pane stuff.
+ * @{ */
+ /** Returns current-item. */
+ UIVirtualMachineItem *currentItem() const;
+ /** Returns a list of current-items. */
+ QList<UIVirtualMachineItem*> currentItems() const;
+
+ /** Returns whether group item is selected. */
+ bool isGroupItemSelected() const;
+ /** Returns whether global item is selected. */
+ bool isGlobalItemSelected() const;
+ /** Returns whether machine item is selected. */
+ bool isMachineItemSelected() const;
+ /** Returns whether local machine item is selected. */
+ bool isLocalMachineItemSelected() const;
+ /** Returns whether cloud machine item is selected. */
+ bool isCloudMachineItemSelected() const;
+
+ /** Returns whether single group is selected. */
+ bool isSingleGroupSelected() const;
+ /** Returns whether single local group is selected. */
+ bool isSingleLocalGroupSelected() const;
+ /** Returns whether single cloud provider group is selected. */
+ bool isSingleCloudProviderGroupSelected() const;
+ /** Returns whether single cloud profile group is selected. */
+ bool isSingleCloudProfileGroupSelected() const;
+ /** Returns whether all items of one group are selected. */
+ bool isAllItemsOfOneGroupSelected() const;
+
+ /** Returns full name of currently selected group. */
+ QString fullGroupName() const;
+
+ /** Returns whether group saving is in progress. */
+ bool isGroupSavingInProgress() const;
+ /** Returns whether at least one cloud profile currently being updated. */
+ bool isCloudProfileUpdateInProgress() const;
+
+ /** Switches to global item. */
+ void switchToGlobalItem();
+ /** Opens group name editor. */
+ void openGroupNameEditor();
+ /** Disbands group. */
+ void disbandGroup();
+ /** Removes machine. */
+ void removeMachine();
+ /** Moves machine to a group with certain @a strName. */
+ void moveMachineToGroup(const QString &strName = QString());
+ /** Returns possible groups for machine with passed @a uId to move to. */
+ QStringList possibleGroupsForMachineToMove(const QUuid &uId);
+ /** Returns possible groups for group with passed @a strFullName to move to. */
+ QStringList possibleGroupsForGroupToMove(const QString &strFullName);
+ /** Refreshes machine. */
+ void refreshMachine();
+ /** Sorts group. */
+ void sortGroup();
+ /** Toggle machine search widget to be @a fVisible. */
+ void setMachineSearchWidgetVisibility(bool fVisible);
+ /** @} */
+
+ /** @name Tools pane stuff.
+ * @{ */
+ /** Defines tools @a enmType. */
+ void setToolsType(UIToolType enmType);
+ /** Returns tools type. */
+ UIToolType toolsType() const;
+
+ /** Returns a type of curent Global tool. */
+ UIToolType currentGlobalTool() const;
+ /** Returns a type of curent Machine tool. */
+ UIToolType currentMachineTool() const;
+ /** Returns whether Global tool of passed @a enmType is opened. */
+ bool isGlobalToolOpened(UIToolType enmType) const;
+ /** Returns whether Machine tool of passed @a enmType is opened. */
+ bool isMachineToolOpened(UIToolType enmType) const;
+ /** Switches to Global tool of passed @a enmType. */
+ void switchToGlobalTool(UIToolType enmType);
+ /** Switches to Machine tool of passed @a enmType. */
+ void switchToMachineTool(UIToolType enmType);
+ /** Closes Global tool of passed @a enmType. */
+ void closeGlobalTool(UIToolType enmType);
+ /** Closes Machine tool of passed @a enmType. */
+ void closeMachineTool(UIToolType enmType);
+ /** @} */
+
+ /** @name Tools / Snapshot pane stuff.
+ * @{ */
+ /** Returns whether current-state item of Snapshot pane is selected. */
+ bool isCurrentStateItemSelected() const;
+ /** @} */
+
+ /** @name Tool-bar stuff.
+ * @{ */
+ /** Updates tool-bar menu buttons. */
+ void updateToolBarMenuButtons(bool fSeparateMenuSection);
+ /** @} */
+
+ /** @name Help browser stuff.
+ * @{ */
+ /** Shpws the help browser. */
+ void showHelpBrowser();
+ /** @} */
+
+public slots:
+
+ /** @name Tool-bar stuff.
+ * @{ */
+ /** Handles tool-bar context-menu request for passed @a position. */
+ void sltHandleToolBarContextMenuRequest(const QPoint &position);
+ /** @} */
+
+protected:
+
+ /** @name Event handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name CVirtualBox event handling stuff.
+ * @{ */
+ /** Handles CVirtualBox event about state change for machine with @a uId. */
+ void sltHandleStateChange(const QUuid &uId);
+ /** @} */
+
+ /** @name Splitter stuff.
+ * @{ */
+ /** Handles signal about splitter move. */
+ void sltHandleSplitterMove();
+ /** Handles request to save splitter settings. */
+ void sltSaveSplitterSettings();
+ /** @} */
+
+ /** @name Tool-bar stuff.
+ * @{ */
+ /** Handles signal about tool-bar resize to @a newSize. */
+ void sltHandleToolBarResize(const QSize &newSize);
+ /** @} */
+
+ /** @name Chooser pane stuff.
+ * @{ */
+ /** Handles signal about Chooser-pane index change. */
+ void sltHandleChooserPaneIndexChange();
+
+ /** Handles signal about Chooser-pane selection invalidated. */
+ void sltHandleChooserPaneSelectionInvalidated() { recacheCurrentItemInformation(true /* fDontRaiseErrorPane */); }
+
+ /** Handles sliding animation complete signal.
+ * @param enmDirection Brings which direction was animation finished for. */
+ void sltHandleSlidingAnimationComplete(SlidingDirection enmDirection);
+
+ /** Handles state change for cloud machine with certain @a uId. */
+ void sltHandleCloudMachineStateChange(const QUuid &uId);
+ /** @} */
+
+ /** @name Tools pane stuff.
+ * @{ */
+ /** Handles tool menu request. */
+ void sltHandleToolMenuRequested(UIToolClass enmClass, const QPoint &position);
+
+ /** Handles signal about Tools-pane index change. */
+ void sltHandleToolsPaneIndexChange();
+
+ /** Handles signal requesting switch to Activity pane of machine with @a uMachineId. */
+ void sltSwitchToMachineActivityPane(const QUuid &uMachineId);
+ /** Handles signal requesting switch to Resources pane. */
+ void sltSwitchToActivityOverviewPane();
+ /** @} */
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Loads settings. */
+ void loadSettings();
+
+ /** Updates toolbar. */
+ void updateToolbar();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups widgets. */
+ void cleanupWidgets();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name Tools / Common stuff.
+ * @{ */
+ /** Recaches current item information.
+ * @param fDontRaiseErrorPane Brings whether we should not raise error-pane. */
+ void recacheCurrentItemInformation(bool fDontRaiseErrorPane = false);
+ /** @} */
+
+ /** Holds the action-pool instance. */
+ UIActionPool *m_pActionPool;
+
+ /** Holds the central splitter instance. */
+ QISplitter *m_pSplitter;
+
+ /** Holds the main toolbar instance. */
+ QIToolBar *m_pToolBar;
+
+ /** Holds the Chooser-pane instance. */
+ UIChooser *m_pPaneChooser;
+ /** Holds the stacked-widget. */
+ QStackedWidget *m_pStackedWidget;
+ /** Holds the Global Tools-pane instance. */
+ UIToolPaneGlobal *m_pPaneToolsGlobal;
+ /** Holds the Machine Tools-pane instance. */
+ UIToolPaneMachine *m_pPaneToolsMachine;
+ /** Holds the sliding-animation widget instance. */
+ UISlidingAnimation *m_pSlidingAnimation;
+ /** Holds the Tools-pane instance. */
+ UITools *m_pPaneTools;
+
+ /** Holds the last selection type. */
+ SelectionType m_enmSelectionType;
+ /** Holds whether the last selected item was accessible. */
+ bool m_fSelectedMachineItemAccessible;
+
+ /** Holds the splitter settings save timer. */
+ QTimer *m_pSplitterSettingsSaveTimer;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIVirtualBoxManagerWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItem.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItem.cpp
new file mode 100644
index 00000000..c728f5c6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItem.cpp
@@ -0,0 +1,88 @@
+/* $Id: UIVirtualMachineItem.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVirtualMachineItem class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIVirtualMachineItemCloud.h"
+#include "UIVirtualMachineItemLocal.h"
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualMachineItem implementation. *
+*********************************************************************************************************************************/
+
+UIVirtualMachineItem::UIVirtualMachineItem(UIVirtualMachineItemType enmType)
+ : m_enmType(enmType)
+ , m_fAccessible(false)
+ , m_enmConfigurationAccessLevel(ConfigurationAccessLevel_Null)
+ , m_fHasDetails(false)
+{
+}
+
+UIVirtualMachineItem::~UIVirtualMachineItem()
+{
+}
+
+UIVirtualMachineItemLocal *UIVirtualMachineItem::toLocal()
+{
+ return itemType() == UIVirtualMachineItemType_Local
+ ? static_cast<UIVirtualMachineItemLocal*>(this)
+ : 0;
+}
+
+UIVirtualMachineItemCloud *UIVirtualMachineItem::toCloud()
+{
+ return ( itemType() == UIVirtualMachineItemType_CloudFake
+ || itemType() == UIVirtualMachineItemType_CloudReal)
+ ? static_cast<UIVirtualMachineItemCloud*>(this)
+ : 0;
+}
+
+QPixmap UIVirtualMachineItem::osPixmap(QSize *pLogicalSize /* = 0 */) const
+{
+ if (pLogicalSize)
+ *pLogicalSize = m_logicalPixmapSize;
+ return m_pixmap;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualMachineItemMimeData implementation. *
+*********************************************************************************************************************************/
+
+QString UIVirtualMachineItemMimeData::m_type = "application/org.virtualbox.gui.vmselector.UIVirtualMachineItem";
+
+UIVirtualMachineItemMimeData::UIVirtualMachineItemMimeData(UIVirtualMachineItem *pItem)
+ : m_pItem(pItem)
+{
+}
+
+QStringList UIVirtualMachineItemMimeData::formats() const
+{
+ QStringList types;
+ types << type();
+ return types;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItem.h b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItem.h
new file mode 100644
index 00000000..482474a0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItem.h
@@ -0,0 +1,235 @@
+/* $Id: UIVirtualMachineItem.h $ */
+/** @file
+ * VBox Qt GUI - UIVirtualMachineItem class declarations.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIVirtualMachineItem_h
+#define FEQT_INCLUDED_SRC_manager_UIVirtualMachineItem_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QMimeData>
+#include <QPixmap>
+#include <QUuid>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIManagerDefs.h"
+#include "UISettingsDefs.h"
+
+/* Forward declarations: */
+class UIVirtualMachineItemCloud;
+class UIVirtualMachineItemLocal;
+
+/* Using declarations: */
+using namespace UISettingsDefs;
+
+/** Virtual Machine item interface. A wrapper caching VM data. */
+class UIVirtualMachineItem : public QIWithRetranslateUI3<QObject>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs VM item on the basis of taken @a enmType. */
+ UIVirtualMachineItem(UIVirtualMachineItemType enmType);
+ /** Destructs VM item. */
+ virtual ~UIVirtualMachineItem();
+
+ /** @name RTTI stuff.
+ * @{ */
+ /** Returns item type. */
+ UIVirtualMachineItemType itemType() const { return m_enmType; }
+ /** Returns item casted to local type. */
+ UIVirtualMachineItemLocal *toLocal();
+ /** Returns item casted to cloud type. */
+ UIVirtualMachineItemCloud *toCloud();
+ /** @} */
+
+ /** @name VM access attributes.
+ * @{ */
+ /** Returns whether VM was accessible. */
+ bool accessible() const { return m_fAccessible; }
+ /** Returns the last cached access error. */
+ QString accessError() const { return m_strAccessError; }
+ /** @} */
+
+ /** @name Basic attributes.
+ * @{ */
+ /** Returns cached machine id. */
+ QUuid id() const { return m_uId; }
+ /** Returns cached machine name. */
+ QString name() const { return m_strName; }
+ /** Returns cached machine OS type id. */
+ QString osTypeId() const { return m_strOSTypeId; }
+ /** Returns cached machine OS type pixmap.
+ * @param pLogicalSize Argument to assign actual pixmap size to. */
+ QPixmap osPixmap(QSize *pLogicalSize = 0) const;
+ /** @} */
+
+ /** @name State attributes.
+ * @{ */
+ /** Returns cached machine state name. */
+ QString machineStateName() const { return m_strMachineStateName; }
+ /** Returns cached machine state icon. */
+ QIcon machineStateIcon() const { return m_machineStateIcon; }
+
+ /** Returns cached configuration access level. */
+ ConfigurationAccessLevel configurationAccessLevel() const { return m_enmConfigurationAccessLevel; }
+ /** @} */
+
+ /** @name Visual attributes.
+ * @{ */
+ /** Returns cached machine tool-tip. */
+ QString toolTipText() const { return m_strToolTipText; }
+ /** @} */
+
+ /** @name Extra-data options.
+ * @{ */
+ /** Returns whether we should show machine details. */
+ bool hasDetails() const { return m_fHasDetails; }
+ /** @} */
+
+ /** @name Update stuff.
+ * @{ */
+ /** Recaches machine data. */
+ virtual void recache() = 0;
+ /** Recaches machine item pixmap. */
+ virtual void recachePixmap() = 0;
+ /** @} */
+
+ /** @name Validation stuff.
+ * @{ */
+ /** Returns whether this item is editable. */
+ virtual bool isItemEditable() const = 0;
+ /** Returns whether this item is removable. */
+ virtual bool isItemRemovable() const = 0;
+ /** Returns whether this item is saved. */
+ virtual bool isItemSaved() const = 0;
+ /** Returns whether this item is powered off. */
+ virtual bool isItemPoweredOff() const = 0;
+ /** Returns whether this item is started. */
+ virtual bool isItemStarted() const = 0;
+ /** Returns whether this item is running. */
+ virtual bool isItemRunning() const = 0;
+ /** Returns whether this item is running headless. */
+ virtual bool isItemRunningHeadless() const = 0;
+ /** Returns whether this item is paused. */
+ virtual bool isItemPaused() const = 0;
+ /** Returns whether this item is stuck. */
+ virtual bool isItemStuck() const = 0;
+ /** Returns whether this item can be switched to. */
+ virtual bool isItemCanBeSwitchedTo() const = 0;
+ /** @} */
+
+protected:
+
+ /** @name RTTI stuff.
+ * @{ */
+ /** Holds item type. */
+ UIVirtualMachineItemType m_enmType;
+ /** @} */
+
+ /** @name VM access attributes.
+ * @{ */
+ /** Holds whether VM was accessible. */
+ bool m_fAccessible;
+ /** Holds the last cached access error. */
+ QString m_strAccessError;
+ /** @} */
+
+ /** @name Basic attributes.
+ * @{ */
+ /** Holds cached machine id. */
+ QUuid m_uId;
+ /** Holds cached machine name. */
+ QString m_strName;
+ /** Holds cached machine OS type id. */
+ QString m_strOSTypeId;
+ /** Holds cached machine OS type pixmap. */
+ QPixmap m_pixmap;
+ /** Holds cached machine OS type pixmap size. */
+ QSize m_logicalPixmapSize;
+ /** @} */
+
+ /** @name State attributes.
+ * @{ */
+ /** Holds cached machine state name. */
+ QString m_strMachineStateName;
+ /** Holds cached machine state name. */
+ QIcon m_machineStateIcon;
+
+ /** Holds configuration access level. */
+ ConfigurationAccessLevel m_enmConfigurationAccessLevel;
+ /** @} */
+
+ /** @name Visual attributes.
+ * @{ */
+ /** Holds cached machine tool-tip. */
+ QString m_strToolTipText;
+ /** @} */
+
+ /** @name Extra-data options.
+ * @{ */
+ /** Holds whether we should show machine details. */
+ bool m_fHasDetails;
+ /** @} */
+};
+
+/* Make the pointer of this class public to the QVariant framework */
+Q_DECLARE_METATYPE(UIVirtualMachineItem *);
+
+/** QMimeData subclass for handling UIVirtualMachineItem mime data. */
+class UIVirtualMachineItemMimeData : public QMimeData
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs mime data for passed VM @a pItem. */
+ UIVirtualMachineItemMimeData(UIVirtualMachineItem *pItem);
+
+ /** Returns cached VM item. */
+ UIVirtualMachineItem *item() const { return m_pItem; }
+
+ /** Returns supported format list. */
+ virtual QStringList formats() const RT_OVERRIDE;
+
+ /** Returns UIVirtualMachineItem mime data type. */
+ static QString type() { return m_type; }
+
+private:
+
+ /** Holds cached VM item. */
+ UIVirtualMachineItem *m_pItem;
+
+ /** Holds UIVirtualMachineItem mime data type. */
+ static QString m_type;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIVirtualMachineItem_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemCloud.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemCloud.cpp
new file mode 100644
index 00000000..1255e2db
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemCloud.cpp
@@ -0,0 +1,433 @@
+/* $Id: UIVirtualMachineItemCloud.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVirtualMachineItemCloud class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPointer>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UICloudNetworkingStuff.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIErrorString.h"
+#include "UIIconPool.h"
+#include "UINotificationCenter.h"
+#include "UIProgressTask.h"
+#include "UIThreadPool.h"
+#include "UIVirtualMachineItemCloud.h"
+
+/* COM includes: */
+#include "CProgress.h"
+#include "CVirtualBoxErrorInfo.h"
+
+
+/** UIProgressTask extension performing cloud machine refresh task.
+ * @todo rework this task to be a part of notification-center. */
+class UIProgressTaskRefreshCloudMachine : public UIProgressTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs @a comCloudMachine refresh task passing @a pParent to the base-class. */
+ UIProgressTaskRefreshCloudMachine(QObject *pParent, const CCloudMachine &comCloudMachine);
+
+protected:
+
+ /** Creates and returns started progress-wrapper required to init UIProgressObject. */
+ virtual CProgress createProgress() RT_OVERRIDE;
+ /** Handles finished @a comProgress wrapper. */
+ virtual void handleProgressFinished(CProgress &comProgress) RT_OVERRIDE;
+
+private:
+
+ /** Holds the cloud machine wrapper. */
+ CCloudMachine m_comCloudMachine;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIProgressTaskRefreshCloudMachine implementation. *
+*********************************************************************************************************************************/
+
+UIProgressTaskRefreshCloudMachine::UIProgressTaskRefreshCloudMachine(QObject *pParent, const CCloudMachine &comCloudMachine)
+ : UIProgressTask(pParent)
+ , m_comCloudMachine(comCloudMachine)
+{
+}
+
+CProgress UIProgressTaskRefreshCloudMachine::createProgress()
+{
+ /* Prepare resulting progress-wrapper: */
+ CProgress comResult;
+
+ /* Initialize actual progress-wrapper: */
+ CProgress comProgress = m_comCloudMachine.Refresh();
+ if (!m_comCloudMachine.isOk())
+ UINotificationMessage::cannotRefreshCloudMachine(m_comCloudMachine);
+ else
+ comResult = comProgress;
+
+ /* Return progress-wrapper in any case: */
+ return comResult;
+}
+
+void UIProgressTaskRefreshCloudMachine::handleProgressFinished(CProgress &comProgress)
+{
+ /* Handle progress-wrapper errors: */
+ if (comProgress.isNotNull() && !comProgress.GetCanceled() && (!comProgress.isOk() || comProgress.GetResultCode() != 0))
+ UINotificationMessage::cannotRefreshCloudMachine(comProgress);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualMachineItemCloud implementation. *
+*********************************************************************************************************************************/
+
+UIVirtualMachineItemCloud::UIVirtualMachineItemCloud(UIFakeCloudVirtualMachineItemState enmState)
+ : UIVirtualMachineItem(UIVirtualMachineItemType_CloudFake)
+ , m_enmMachineState(KCloudMachineState_Invalid)
+ , m_enmFakeCloudItemState(enmState)
+ , m_fRefreshScheduled(false)
+ , m_pProgressTaskRefresh(0)
+{
+ prepare();
+}
+
+UIVirtualMachineItemCloud::UIVirtualMachineItemCloud(const CCloudMachine &comCloudMachine)
+ : UIVirtualMachineItem(UIVirtualMachineItemType_CloudReal)
+ , m_comCloudMachine(comCloudMachine)
+ , m_enmMachineState(KCloudMachineState_Invalid)
+ , m_enmFakeCloudItemState(UIFakeCloudVirtualMachineItemState_NotApplicable)
+ , m_fRefreshScheduled(false)
+ , m_pProgressTaskRefresh(0)
+{
+ prepare();
+}
+
+UIVirtualMachineItemCloud::~UIVirtualMachineItemCloud()
+{
+ cleanup();
+}
+
+void UIVirtualMachineItemCloud::setFakeCloudItemState(UIFakeCloudVirtualMachineItemState enmState)
+{
+ m_enmFakeCloudItemState = enmState;
+ recache();
+}
+
+void UIVirtualMachineItemCloud::setFakeCloudItemErrorMessage(const QString &strErrorMessage)
+{
+ m_strFakeCloudItemErrorMessage = strErrorMessage;
+ recache();
+}
+
+void UIVirtualMachineItemCloud::updateInfoAsync(bool fDelayed, bool fSubscribe /* = false */)
+{
+ /* Ignore refresh request if progress-task is absent: */
+ if (!m_pProgressTaskRefresh)
+ return;
+
+ /* Mark update scheduled if requested: */
+ if (fSubscribe)
+ m_fRefreshScheduled = true;
+
+ /* Schedule refresh request in a 10 or 0 seconds
+ * if progress-task isn't already scheduled or running: */
+ if ( !m_pProgressTaskRefresh->isScheduled()
+ && !m_pProgressTaskRefresh->isRunning())
+ m_pProgressTaskRefresh->schedule(fDelayed ? 10000 : 0);
+}
+
+void UIVirtualMachineItemCloud::stopAsyncUpdates()
+{
+ /* Ignore cancel request if progress-task is absent: */
+ if (!m_pProgressTaskRefresh)
+ return;
+
+ /* Mark update canceled in any case: */
+ m_fRefreshScheduled = false;
+}
+
+void UIVirtualMachineItemCloud::waitForAsyncInfoUpdateFinished()
+{
+ /* Ignore cancel request if progress-task is absent: */
+ if (!m_pProgressTaskRefresh)
+ return;
+
+ /* Mark update canceled in any case: */
+ m_fRefreshScheduled = false;
+
+ /* Cancel refresh request
+ * if progress-task already running: */
+ if (m_pProgressTaskRefresh->isRunning())
+ m_pProgressTaskRefresh->cancel();
+}
+
+void UIVirtualMachineItemCloud::recache()
+{
+ switch (itemType())
+ {
+ case UIVirtualMachineItemType_CloudFake:
+ {
+ /* Make sure cloud VM is NOT set: */
+ AssertReturnVoid(m_comCloudMachine.isNull());
+
+ /* Determine ID/name: */
+ m_uId = QUuid();
+ m_strName = QString();
+
+ /* Determine whether VM is accessible: */
+ m_fAccessible = m_strFakeCloudItemErrorMessage.isNull();
+ m_strAccessError = m_strFakeCloudItemErrorMessage;
+
+ /* Determine VM OS type: */
+ m_strOSTypeId = "Other";
+
+ /* Determine VM states: */
+ m_enmMachineState = KCloudMachineState_Stopped;
+ switch (m_enmFakeCloudItemState)
+ {
+ case UIFakeCloudVirtualMachineItemState_Loading:
+ m_machineStateIcon = UIIconPool::iconSet(":/state_loading_16px.png");
+ break;
+ case UIFakeCloudVirtualMachineItemState_Done:
+ m_machineStateIcon = UIIconPool::iconSet(":/vm_new_16px.png");
+ break;
+ default:
+ break;
+ }
+
+ /* Determine configuration access level: */
+ m_enmConfigurationAccessLevel = ConfigurationAccessLevel_Null;
+
+ /* Determine whether we should show this VM details: */
+ m_fHasDetails = true;
+
+ break;
+ }
+ case UIVirtualMachineItemType_CloudReal:
+ {
+ /* Make sure cloud VM is set: */
+ AssertReturnVoid(m_comCloudMachine.isNotNull());
+
+ /* Determine ID/name: */
+ m_uId = m_comCloudMachine.GetId();
+ m_strName = m_comCloudMachine.GetName();
+
+ /* Determine whether VM is accessible: */
+ m_fAccessible = m_comCloudMachine.GetAccessible();
+ m_strAccessError = !m_fAccessible ? UIErrorString::formatErrorInfo(m_comCloudMachine.GetAccessError()) : QString();
+
+ /* Determine VM OS type: */
+ m_strOSTypeId = m_fAccessible ? m_comCloudMachine.GetOSTypeId() : "Other";
+
+ /* Determine VM states: */
+ m_enmMachineState = m_fAccessible ? m_comCloudMachine.GetState() : KCloudMachineState_Stopped;
+ m_machineStateIcon = gpConverter->toIcon(m_enmMachineState);
+
+ /* Determine configuration access level: */
+ m_enmConfigurationAccessLevel = m_fAccessible ? ConfigurationAccessLevel_Full : ConfigurationAccessLevel_Null;
+
+ /* Determine whether we should show this VM details: */
+ m_fHasDetails = true;
+
+ break;
+ }
+ default:
+ {
+ AssertFailed();
+ break;
+ }
+ }
+
+ /* Recache item pixmap: */
+ recachePixmap();
+
+ /* Retranslate finally: */
+ retranslateUi();
+}
+
+void UIVirtualMachineItemCloud::recachePixmap()
+{
+ /* We are using icon corresponding to cached guest OS type: */
+ if ( itemType() == UIVirtualMachineItemType_CloudFake
+ && fakeCloudItemState() == UIFakeCloudVirtualMachineItemState_Loading)
+ m_pixmap = generalIconPool().guestOSTypePixmapDefault("Cloud", &m_logicalPixmapSize);
+ else
+ m_pixmap = generalIconPool().guestOSTypePixmapDefault(m_strOSTypeId, &m_logicalPixmapSize);
+}
+
+bool UIVirtualMachineItemCloud::isItemEditable() const
+{
+ return accessible()
+ && itemType() == UIVirtualMachineItemType_CloudReal;
+}
+
+bool UIVirtualMachineItemCloud::isItemRemovable() const
+{
+ return accessible()
+ && itemType() == UIVirtualMachineItemType_CloudReal;
+}
+
+bool UIVirtualMachineItemCloud::isItemSaved() const
+{
+ return accessible()
+ && itemType() == UIVirtualMachineItemType_CloudReal
+ && ( machineState() == KCloudMachineState_Stopped
+ || machineState() == KCloudMachineState_Running);
+}
+
+bool UIVirtualMachineItemCloud::isItemPoweredOff() const
+{
+ return accessible()
+ && ( machineState() == KCloudMachineState_Stopped
+ || machineState() == KCloudMachineState_Terminated);
+}
+
+bool UIVirtualMachineItemCloud::isItemStarted() const
+{
+ return isItemRunning()
+ || isItemPaused();
+}
+
+bool UIVirtualMachineItemCloud::isItemRunning() const
+{
+ return accessible()
+ && machineState() == KCloudMachineState_Running;
+}
+
+bool UIVirtualMachineItemCloud::isItemRunningHeadless() const
+{
+ return isItemRunning();
+}
+
+bool UIVirtualMachineItemCloud::isItemPaused() const
+{
+ return false;
+}
+
+bool UIVirtualMachineItemCloud::isItemStuck() const
+{
+ return false;
+}
+
+bool UIVirtualMachineItemCloud::isItemCanBeSwitchedTo() const
+{
+ return false;
+}
+
+void UIVirtualMachineItemCloud::retranslateUi()
+{
+ /* If machine is accessible: */
+ if (accessible())
+ {
+ if (itemType() == UIVirtualMachineItemType_CloudFake)
+ {
+ /* Update fake machine state name: */
+ switch (m_enmFakeCloudItemState)
+ {
+ case UIFakeCloudVirtualMachineItemState_Loading:
+ m_strMachineStateName = tr("Loading ...");
+ break;
+ case UIFakeCloudVirtualMachineItemState_Done:
+ m_strMachineStateName = tr("Empty");
+ break;
+ default:
+ break;
+ }
+
+ /* Update tool-tip: */
+ m_strToolTipText = m_strMachineStateName;
+ }
+ else
+ {
+ /* Update real machine state name: */
+ m_strMachineStateName = gpConverter->toString(m_enmMachineState);
+
+ /* Update tool-tip: */
+ m_strToolTipText = QString("<nobr><b>%1</b></nobr><br>"
+ "<nobr>%2</nobr>")
+ .arg(m_strName)
+ .arg(gpConverter->toString(m_enmMachineState));
+ }
+ }
+ /* Otherwise: */
+ else
+ {
+ /* We have our own translation for Null states: */
+ m_strMachineStateName = tr("Inaccessible", "VM");
+
+ /* Update tool-tip: */
+ m_strToolTipText = tr("<nobr><b>%1</b></nobr><br>"
+ "<nobr>Inaccessible</nobr>",
+ "Inaccessible VM tooltip (name)")
+ .arg(m_strName);
+ }
+}
+
+void UIVirtualMachineItemCloud::sltHandleRefreshCloudMachineInfoDone()
+{
+ /* Recache: */
+ recache();
+
+ /* Notify listeners: */
+ emit sigRefreshFinished();
+
+ /* Refresh again if scheduled: */
+ if (m_fRefreshScheduled)
+ updateInfoAsync(true /* async? */);
+}
+
+void UIVirtualMachineItemCloud::prepare()
+{
+ /* Prepare progress-task if necessary: */
+ if (itemType() == UIVirtualMachineItemType_CloudReal)
+ {
+ m_pProgressTaskRefresh = new UIProgressTaskRefreshCloudMachine(this, machine());
+ if (m_pProgressTaskRefresh)
+ {
+ connect(m_pProgressTaskRefresh, &UIProgressTaskRefreshCloudMachine::sigProgressStarted,
+ this, &UIVirtualMachineItemCloud::sigRefreshStarted);
+ connect(m_pProgressTaskRefresh, &UIProgressTaskRefreshCloudMachine::sigProgressFinished,
+ this, &UIVirtualMachineItemCloud::sltHandleRefreshCloudMachineInfoDone);
+ }
+ }
+
+ /* Recache finally: */
+ recache();
+}
+
+void UIVirtualMachineItemCloud::cleanup()
+{
+ /* Cleanup progress-task: */
+ delete m_pProgressTaskRefresh;
+ m_pProgressTaskRefresh = 0;
+}
+
+
+#include "UIVirtualMachineItemCloud.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemCloud.h b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemCloud.h
new file mode 100644
index 00000000..34b54016
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemCloud.h
@@ -0,0 +1,175 @@
+/* $Id: UIVirtualMachineItemCloud.h $ */
+/** @file
+ * VBox Qt GUI - UIVirtualMachineItemCloud class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIVirtualMachineItemCloud_h
+#define FEQT_INCLUDED_SRC_manager_UIVirtualMachineItemCloud_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIVirtualMachineItem.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudMachine.h"
+
+/* Forward declarations: */
+class UIProgressTask;
+
+/** UIVirtualMachineItem sub-class used as cloud Virtual Machine item interface. */
+class UIVirtualMachineItemCloud : public UIVirtualMachineItem
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about refresh started. */
+ void sigRefreshStarted();
+ /** Notifies listeners about refresh finished. */
+ void sigRefreshFinished();
+
+public:
+
+ /** Constructs fake cloud VM item of certain @a enmState. */
+ UIVirtualMachineItemCloud(UIFakeCloudVirtualMachineItemState enmState);
+ /** Constructs real cloud VM item on the basis of taken @a comCloudMachine. */
+ UIVirtualMachineItemCloud(const CCloudMachine &comCloudMachine);
+ /** Destructs cloud VM item. */
+ virtual ~UIVirtualMachineItemCloud() RT_OVERRIDE;
+
+ /** @name Arguments.
+ * @{ */
+ /** Returns cached cloud machine object. */
+ CCloudMachine machine() const { return m_comCloudMachine; }
+ /** @} */
+
+ /** @name Data attributes.
+ * @{ */
+ /** Returns cached machine state. */
+ KCloudMachineState machineState() const { return m_enmMachineState; }
+
+ /** Defines fake cloud item @a enmState. */
+ void setFakeCloudItemState(UIFakeCloudVirtualMachineItemState enmState);
+ /** Returns fake cloud item state. */
+ UIFakeCloudVirtualMachineItemState fakeCloudItemState() const { return m_enmFakeCloudItemState; }
+
+ /** Defines fake cloud item @a strErrorMessage. */
+ void setFakeCloudItemErrorMessage(const QString &strErrorMessage);
+ /** Returns fake cloud item error message. */
+ QString fakeCloudItemErrorMessage() const { return m_strFakeCloudItemErrorMessage; }
+
+ /** Updates cloud VM info async way, @a fDelayed if requested or instant otherwise.
+ * @param fSubscribe Brings whether this update should be performed periodically. */
+ void updateInfoAsync(bool fDelayed, bool fSubscribe = false);
+ /** Stop periodical updates previously requested. */
+ void stopAsyncUpdates();
+ /** Makes sure async info update is finished.
+ * @note This method creates own event-loop to avoid blocking calling thread event processing,
+ * so it's safe to call it from the GUI thread, ofc the method itself will be blocked. */
+ void waitForAsyncInfoUpdateFinished();
+ /** @} */
+
+ /** @name Update stuff.
+ * @{ */
+ /** Recaches machine data. */
+ virtual void recache() RT_OVERRIDE;
+ /** Recaches machine item pixmap. */
+ virtual void recachePixmap() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Validation stuff.
+ * @{ */
+ /** Returns whether this item is editable. */
+ virtual bool isItemEditable() const RT_OVERRIDE;
+ /** Returns whether this item is removable. */
+ virtual bool isItemRemovable() const RT_OVERRIDE;
+ /** Returns whether this item is saved. */
+ virtual bool isItemSaved() const RT_OVERRIDE;
+ /** Returns whether this item is powered off. */
+ virtual bool isItemPoweredOff() const RT_OVERRIDE;
+ /** Returns whether this item is started. */
+ virtual bool isItemStarted() const RT_OVERRIDE;
+ /** Returns whether this item is running. */
+ virtual bool isItemRunning() const RT_OVERRIDE;
+ /** Returns whether this item is running headless. */
+ virtual bool isItemRunningHeadless() const RT_OVERRIDE;
+ /** Returns whether this item is paused. */
+ virtual bool isItemPaused() const RT_OVERRIDE;
+ /** Returns whether this item is stuck. */
+ virtual bool isItemStuck() const RT_OVERRIDE;
+ /** Returns whether this item can be switched to. */
+ virtual bool isItemCanBeSwitchedTo() const RT_OVERRIDE;
+ /** @} */
+
+protected:
+
+ /** @name Event handling.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** Handles signal about cloud VM info refresh progress is done. */
+ void sltHandleRefreshCloudMachineInfoDone();
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name Arguments.
+ * @{ */
+ /** Holds cached cloud machine object. */
+ CCloudMachine m_comCloudMachine;
+ /** @} */
+
+ /** @name Data attributes.
+ * @{ */
+ /** Holds cached machine state. */
+ KCloudMachineState m_enmMachineState;
+
+ /** Holds fake cloud item state. */
+ UIFakeCloudVirtualMachineItemState m_enmFakeCloudItemState;
+ /** Holds fake cloud item error message. */
+ QString m_strFakeCloudItemErrorMessage;
+
+ /** Holds whether we plan to refresh info. */
+ bool m_fRefreshScheduled;
+ /** Holds the refresh progress-task instance. */
+ UIProgressTask *m_pProgressTaskRefresh;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIVirtualMachineItemCloud_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemLocal.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemLocal.cpp
new file mode 100644
index 00000000..bd5cb8d2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemLocal.cpp
@@ -0,0 +1,312 @@
+/* $Id: UIVirtualMachineItemLocal.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVirtualMachineItem class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QFileInfo>
+#include <QIcon>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIErrorString.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIVirtualMachineItemLocal.h"
+#ifdef VBOX_WS_MAC
+# include <ApplicationServices/ApplicationServices.h>
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "CSnapshot.h"
+#include "CVirtualBoxErrorInfo.h"
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualMachineItemLocal implementation. *
+*********************************************************************************************************************************/
+
+UIVirtualMachineItemLocal::UIVirtualMachineItemLocal(const CMachine &comMachine)
+ : UIVirtualMachineItem(UIVirtualMachineItemType_Local)
+ , m_comMachine(comMachine)
+ , m_cSnaphot(0)
+ , m_enmMachineState(KMachineState_Null)
+ , m_enmSessionState(KSessionState_Null)
+{
+ recache();
+}
+
+UIVirtualMachineItemLocal::~UIVirtualMachineItemLocal()
+{
+}
+
+void UIVirtualMachineItemLocal::recache()
+{
+ /* Determine attributes which are always available: */
+ m_uId = m_comMachine.GetId();
+ m_strSettingsFile = m_comMachine.GetSettingsFilePath();
+
+ /* Now determine whether VM is accessible: */
+ m_fAccessible = m_comMachine.GetAccessible();
+ if (m_fAccessible)
+ {
+ /* Reset last access error information: */
+ m_strAccessError.clear();
+
+ /* Determine own VM attributes: */
+ m_strName = m_comMachine.GetName();
+ m_strOSTypeId = m_comMachine.GetOSTypeId();
+ m_groups = m_comMachine.GetGroups().toList();
+
+ /* Determine snapshot attributes: */
+ CSnapshot comSnapshot = m_comMachine.GetCurrentSnapshot();
+ m_strSnapshotName = comSnapshot.isNull() ? QString() : comSnapshot.GetName();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
+ m_lastStateChange.setSecsSinceEpoch(m_comMachine.GetLastStateChange() / 1000);
+#else
+ m_lastStateChange.setTime_t(m_comMachine.GetLastStateChange() / 1000);
+#endif
+ m_cSnaphot = m_comMachine.GetSnapshotCount();
+
+ /* Determine VM states: */
+ m_enmMachineState = m_comMachine.GetState();
+ m_machineStateIcon = gpConverter->toIcon(m_enmMachineState);
+ m_enmSessionState = m_comMachine.GetSessionState();
+
+ /* Determine configuration access level: */
+ m_enmConfigurationAccessLevel = ::configurationAccessLevel(m_enmSessionState, m_enmMachineState);
+ /* Also take restrictions into account: */
+ if ( m_enmConfigurationAccessLevel != ConfigurationAccessLevel_Null
+ && !gEDataManager->machineReconfigurationEnabled(m_uId))
+ m_enmConfigurationAccessLevel = ConfigurationAccessLevel_Null;
+
+ /* Determine PID finally: */
+ if ( m_enmMachineState == KMachineState_PoweredOff
+ || m_enmMachineState == KMachineState_Saved
+ || m_enmMachineState == KMachineState_Teleported
+ || m_enmMachineState == KMachineState_Aborted
+ || m_enmMachineState == KMachineState_AbortedSaved
+ )
+ {
+ m_pid = (ULONG) ~0;
+ }
+ else
+ {
+ m_pid = m_comMachine.GetSessionPID();
+ }
+
+ /* Determine whether we should show this VM details: */
+ m_fHasDetails = gEDataManager->showMachineInVirtualBoxManagerDetails(m_uId);
+ }
+ else
+ {
+ /* Update last access error information: */
+ m_strAccessError = UIErrorString::formatErrorInfo(m_comMachine.GetAccessError());
+
+ /* Determine machine name on the basis of settings file only: */
+ QFileInfo fi(m_strSettingsFile);
+ m_strName = UICommon::hasAllowedExtension(fi.completeSuffix(), VBoxFileExts)
+ ? fi.completeBaseName()
+ : fi.fileName();
+ /* Reset other VM attributes: */
+ m_strOSTypeId = QString();
+ m_groups.clear();
+
+ /* Reset snapshot attributes: */
+ m_strSnapshotName = QString();
+ m_lastStateChange = QDateTime::currentDateTime();
+ m_cSnaphot = 0;
+
+ /* Reset VM states: */
+ m_enmMachineState = KMachineState_Null;
+ m_machineStateIcon = gpConverter->toIcon(KMachineState_Aborted);
+ m_enmSessionState = KSessionState_Null;
+
+ /* Reset configuration access level: */
+ m_enmConfigurationAccessLevel = ConfigurationAccessLevel_Null;
+
+ /* Reset PID finally: */
+ m_pid = (ULONG) ~0;
+
+ /* Reset whether we should show this VM details: */
+ m_fHasDetails = true;
+ }
+
+ /* Recache item pixmap: */
+ recachePixmap();
+
+ /* Retranslate finally: */
+ retranslateUi();
+}
+
+void UIVirtualMachineItemLocal::recachePixmap()
+{
+ /* If machine is accessible: */
+ if (m_fAccessible)
+ {
+ /* First, we are trying to acquire personal machine guest OS type icon: */
+ m_pixmap = generalIconPool().userMachinePixmapDefault(m_comMachine, &m_logicalPixmapSize);
+ /* If there is nothing, we are using icon corresponding to cached guest OS type: */
+ if (m_pixmap.isNull())
+ m_pixmap = generalIconPool().guestOSTypePixmapDefault(m_strOSTypeId, &m_logicalPixmapSize);
+ }
+ /* Otherwise: */
+ else
+ {
+ /* We are using "Other" guest OS type icon: */
+ m_pixmap = generalIconPool().guestOSTypePixmapDefault("Other", &m_logicalPixmapSize);
+ }
+}
+
+bool UIVirtualMachineItemLocal::isItemEditable() const
+{
+ return accessible()
+ && sessionState() == KSessionState_Unlocked;
+}
+
+bool UIVirtualMachineItemLocal::isItemRemovable() const
+{
+ return !accessible()
+ || sessionState() == KSessionState_Unlocked;
+}
+
+bool UIVirtualMachineItemLocal::isItemSaved() const
+{
+ return accessible()
+ && ( machineState() == KMachineState_Saved
+ || machineState() == KMachineState_AbortedSaved);
+}
+
+bool UIVirtualMachineItemLocal::isItemPoweredOff() const
+{
+ return accessible()
+ && ( machineState() == KMachineState_PoweredOff
+ || machineState() == KMachineState_Saved
+ || machineState() == KMachineState_Teleported
+ || machineState() == KMachineState_Aborted
+ || machineState() == KMachineState_AbortedSaved);
+}
+
+bool UIVirtualMachineItemLocal::isItemStarted() const
+{
+ return isItemRunning()
+ || isItemPaused();
+}
+
+bool UIVirtualMachineItemLocal::isItemRunning() const
+{
+ return accessible()
+ && ( machineState() == KMachineState_Running
+ || machineState() == KMachineState_Teleporting
+ || machineState() == KMachineState_LiveSnapshotting);
+}
+
+bool UIVirtualMachineItemLocal::isItemRunningHeadless() const
+{
+ if (isItemRunning())
+ {
+ /* Open session to determine which frontend VM is started with: */
+ CSession comSession = uiCommon().openExistingSession(id());
+ if (!comSession.isNull())
+ {
+ /* Acquire the session name: */
+ const QString strSessionName = comSession.GetMachine().GetSessionName();
+ /* Close the session early: */
+ comSession.UnlockMachine();
+ /* Check whether we are in 'headless' session: */
+ return strSessionName == "headless";
+ }
+ }
+ return false;
+}
+
+bool UIVirtualMachineItemLocal::isItemPaused() const
+{
+ return accessible()
+ && ( machineState() == KMachineState_Paused
+ || machineState() == KMachineState_TeleportingPausedVM);
+}
+
+bool UIVirtualMachineItemLocal::isItemStuck() const
+{
+ return accessible()
+ && machineState() == KMachineState_Stuck;
+}
+
+bool UIVirtualMachineItemLocal::isItemCanBeSwitchedTo() const
+{
+ return const_cast<CMachine&>(m_comMachine).CanShowConsoleWindow()
+ || isItemRunningHeadless();
+}
+
+void UIVirtualMachineItemLocal::retranslateUi()
+{
+ /* This is used in tool-tip generation: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const QString strDateTime = m_lastStateChange.date() == QDate::currentDate()
+ ? QLocale::system().toString(m_lastStateChange.time(), QLocale::ShortFormat)
+ : QLocale::system().toString(m_lastStateChange, QLocale::ShortFormat);
+#else
+ const QString strDateTime = (m_lastStateChange.date() == QDate::currentDate())
+ ? m_lastStateChange.time().toString(Qt::LocalDate)
+ : m_lastStateChange.toString(Qt::LocalDate);
+#endif
+
+ /* If machine is accessible: */
+ if (m_fAccessible)
+ {
+ /* Just use the usual translation for valid states: */
+ m_strMachineStateName = gpConverter->toString(m_enmMachineState);
+ m_strSessionStateName = gpConverter->toString(m_enmSessionState);
+
+ /* Update tool-tip: */
+ m_strToolTipText = QString("<b>%1</b>").arg(m_strName);
+ if (!m_strSnapshotName.isNull())
+ m_strToolTipText += QString(" (%1)").arg(m_strSnapshotName);
+ m_strToolTipText = tr("<nobr>%1<br></nobr>"
+ "<nobr>%2 since %3</nobr><br>"
+ "<nobr>Session %4</nobr>",
+ "VM tooltip (name, last state change, session state)")
+ .arg(m_strToolTipText)
+ .arg(gpConverter->toString(m_enmMachineState))
+ .arg(strDateTime)
+ .arg(gpConverter->toString(m_enmSessionState).toLower());
+ }
+ /* Otherwise: */
+ else
+ {
+ /* We have our own translation for Null states: */
+ m_strMachineStateName = tr("Inaccessible");
+ m_strSessionStateName = tr("Inaccessible");
+
+ /* Update tool-tip: */
+ m_strToolTipText = tr("<nobr><b>%1</b><br></nobr>"
+ "<nobr>Inaccessible since %2</nobr>",
+ "Inaccessible VM tooltip (name, last state change)")
+ .arg(m_strSettingsFile)
+ .arg(strDateTime);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemLocal.h b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemLocal.h
new file mode 100644
index 00000000..9c9c3164
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIVirtualMachineItemLocal.h
@@ -0,0 +1,170 @@
+/* $Id: UIVirtualMachineItemLocal.h $ */
+/** @file
+ * VBox Qt GUI - UIVirtualMachineItemLocal class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIVirtualMachineItemLocal_h
+#define FEQT_INCLUDED_SRC_manager_UIVirtualMachineItemLocal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDateTime>
+
+/* GUI includes: */
+#include "UIVirtualMachineItem.h"
+
+/* COM includes: */
+#include "CMachine.h"
+
+/** UIVirtualMachineItem sub-class used as local Virtual Machine item interface. */
+class UIVirtualMachineItemLocal : public UIVirtualMachineItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs local VM item on the basis of taken @a comMachine. */
+ UIVirtualMachineItemLocal(const CMachine &comMachine);
+ /** Destructs local VM item. */
+ virtual ~UIVirtualMachineItemLocal();
+
+ /** @name Arguments.
+ * @{ */
+ /** Returns cached virtual machine object. */
+ CMachine machine() const { return m_comMachine; }
+ /** @} */
+
+ /** @name Basic attributes.
+ * @{ */
+ /** Returns cached machine settings file name. */
+ QString settingsFile() const { return m_strSettingsFile; }
+ /** Returns cached machine group list. */
+ const QStringList &groups() { return m_groups; }
+ /** @} */
+
+ /** @name Snapshot attributes.
+ * @{ */
+ /** Returns cached snapshot name. */
+ QString snapshotName() const { return m_strSnapshotName; }
+ /** Returns cached snapshot children count. */
+ ULONG snapshotCount() const { return m_cSnaphot; }
+ /** @} */
+
+ /** @name State attributes.
+ * @{ */
+ /** Returns cached machine state. */
+ KMachineState machineState() const { return m_enmMachineState; }
+ /** Returns cached session state. */
+ KSessionState sessionState() const { return m_enmSessionState; }
+ /** Returns cached session state name. */
+ QString sessionStateName() const { return m_strSessionStateName; }
+ /** @} */
+
+ /** @name Update stuff.
+ * @{ */
+ /** Recaches machine data. */
+ virtual void recache() RT_OVERRIDE;
+ /** Recaches machine item pixmap. */
+ virtual void recachePixmap() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Validation stuff.
+ * @{ */
+ /** Returns whether this item is editable. */
+ virtual bool isItemEditable() const RT_OVERRIDE;
+ /** Returns whether this item is removable. */
+ virtual bool isItemRemovable() const RT_OVERRIDE;
+ /** Returns whether this item is saved. */
+ virtual bool isItemSaved() const RT_OVERRIDE;
+ /** Returns whether this item is powered off. */
+ virtual bool isItemPoweredOff() const RT_OVERRIDE;
+ /** Returns whether this item is started. */
+ virtual bool isItemStarted() const RT_OVERRIDE;
+ /** Returns whether this item is running. */
+ virtual bool isItemRunning() const RT_OVERRIDE;
+ /** Returns whether this item is running headless. */
+ virtual bool isItemRunningHeadless() const RT_OVERRIDE;
+ /** Returns whether this item is paused. */
+ virtual bool isItemPaused() const RT_OVERRIDE;
+ /** Returns whether this item is stuck. */
+ virtual bool isItemStuck() const RT_OVERRIDE;
+ /** Returns whether this item can be switched to. */
+ virtual bool isItemCanBeSwitchedTo() const RT_OVERRIDE;
+ /** @} */
+
+protected:
+
+ /** @name Event handling.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+private:
+
+ /** @name Arguments.
+ * @{ */
+ /** Holds cached machine object reference. */
+ CMachine m_comMachine;
+ /** @} */
+
+ /** @name Basic attributes.
+ * @{ */
+ /** Holds cached machine settings file name. */
+ QString m_strSettingsFile;
+ /** Holds cached machine group list. */
+ QStringList m_groups;
+ /** @} */
+
+ /** @name Snapshot attributes.
+ * @{ */
+ /** Holds cached snapshot name. */
+ QString m_strSnapshotName;
+ /** Holds cached last state change date/time. */
+ QDateTime m_lastStateChange;
+ /** Holds cached snapshot children count. */
+ ULONG m_cSnaphot;
+ /** @} */
+
+ /** @name State attributes.
+ * @{ */
+ /** Holds cached machine state. */
+ KMachineState m_enmMachineState;
+ /** Holds cached session state. */
+ KSessionState m_enmSessionState;
+ /** Holds cached session state name. */
+ QString m_strSessionStateName;
+ /** @} */
+
+ /** @name Console attributes.
+ * @{ */
+ /** Holds machine PID. */
+ ULONG m_pid;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIVirtualMachineItemLocal_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIWelcomePane.cpp b/src/VBox/Frontends/VirtualBox/src/manager/UIWelcomePane.cpp
new file mode 100644
index 00000000..82764059
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIWelcomePane.cpp
@@ -0,0 +1,255 @@
+/* $Id: UIWelcomePane.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWelcomePane class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes */
+#include "QIWithRetranslateUI.h"
+#include "UICommon.h"
+#include "UIIconPool.h"
+#include "UIWelcomePane.h"
+
+/* Forward declarations: */
+class QEvent;
+class QHBoxLayout;
+class QString;
+class QResizeEvent;
+class QVBoxLayout;
+
+
+/** Wrappable QLabel extension for tools pane of the desktop widget.
+ * The main idea behind this stuff is to allow dynamically calculate
+ * [minimum] size hint for changeable one-the-fly widget width.
+ * That's a "white unicorn" task for QLabel which never worked since
+ * the beginning, because out-of-the-box version just uses static
+ * hints calculation which is very stupid taking into account
+ * QLayout "eats it raw" and tries to be dynamical on it's basis. */
+class UIWrappableLabel : public QLabel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs wrappable label passing @a pParent to the base-class. */
+ UIWrappableLabel(QWidget *pParent = 0);
+
+protected:
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Returns whether the widget's preferred height depends on its width. */
+ virtual bool hasHeightForWidth() const RT_OVERRIDE;
+
+ /** Holds the minimum widget size. */
+ virtual QSize minimumSizeHint() const RT_OVERRIDE;
+
+ /** Holds the preferred widget size. */
+ virtual QSize sizeHint() const RT_OVERRIDE;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIWrappableLabel implementation. *
+*********************************************************************************************************************************/
+
+UIWrappableLabel::UIWrappableLabel(QWidget *pParent /* = 0 */)
+ : QLabel(pParent)
+{
+}
+
+void UIWrappableLabel::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QLabel::resizeEvent(pEvent);
+
+ // WORKAROUND:
+ // That's not cheap procedure but we need it to
+ // make sure geometry is updated after width changed.
+ if (minimumWidth() > 0)
+ updateGeometry();
+}
+
+bool UIWrappableLabel::hasHeightForWidth() const
+{
+ // WORKAROUND:
+ // No need to panic, we do it ourselves in resizeEvent() and
+ // this 'false' here to prevent automatic layout fighting for it.
+ return minimumWidth() > 0
+ ? false
+ : QLabel::hasHeightForWidth();
+}
+
+QSize UIWrappableLabel::minimumSizeHint() const
+{
+ // WORKAROUND:
+ // We should calculate hint height on the basis of width,
+ // keeping the hint width equal to minimum we have set.
+ return minimumWidth() > 0
+ ? QSize(minimumWidth(), heightForWidth(width()))
+ : QLabel::minimumSizeHint();
+}
+
+QSize UIWrappableLabel::sizeHint() const
+{
+ // WORKAROUND:
+ // Keep widget always minimal.
+ return minimumSizeHint();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWelcomePane implementation. *
+*********************************************************************************************************************************/
+
+UIWelcomePane::UIWelcomePane(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLabelText(0)
+ , m_pLabelIcon(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+bool UIWelcomePane::event(QEvent *pEvent)
+{
+ /* Handle known event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Show:
+ case QEvent::ScreenChangeInternal:
+ {
+ /* Update pixmap: */
+ updatePixmap();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::event(pEvent);
+}
+
+void UIWelcomePane::retranslateUi()
+{
+ /* Translate welcome text: */
+ m_pLabelText->setText(tr("<h3>Welcome to VirtualBox!</h3>"
+ "<p>The left part of application window contains global tools and "
+ "lists all virtual machines and virtual machine groups on your computer. "
+ "You can import, add and create new VMs using corresponding toolbar buttons. "
+ "You can popup a tools of currently selected element using corresponding element button.</p>"
+ "<p>You can press the <b>%1</b> key to get instant help, or visit "
+ "<a href=https://www.virtualbox.org>www.virtualbox.org</a> "
+ "for more information and latest news.</p>")
+ .arg(QKeySequence(QKeySequence::HelpContents).toString(QKeySequence::NativeText)));
+}
+
+void UIWelcomePane::sltHandleLinkActivated(const QString &strLink)
+{
+ uiCommon().openURL(strLink);
+}
+
+void UIWelcomePane::prepare()
+{
+ /* Prepare default welcome icon: */
+ m_icon = UIIconPool::iconSet(":/tools_banner_global_200px.png");
+
+ /* Create main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Create welcome layout: */
+ QHBoxLayout *pLayoutWelcome = new QHBoxLayout;
+ if (pLayoutWelcome)
+ {
+ /* Configure layout: */
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 2;
+ pLayoutWelcome->setContentsMargins(iL, 0, 0, 0);
+
+ /* Create welcome text label: */
+ m_pLabelText = new UIWrappableLabel;
+ if (m_pLabelText)
+ {
+ /* Configure label: */
+ m_pLabelText->setWordWrap(true);
+ m_pLabelText->setMinimumWidth(160); /// @todo make dynamic
+ m_pLabelText->setAlignment(Qt::AlignLeading | Qt::AlignTop);
+ m_pLabelText->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ connect(m_pLabelText, &QLabel::linkActivated, this, &UIWelcomePane::sltHandleLinkActivated);
+
+ /* Add into layout: */
+ pLayoutWelcome->addWidget(m_pLabelText);
+ }
+
+ /* Create welcome picture label: */
+ m_pLabelIcon = new QLabel;
+ if (m_pLabelIcon)
+ {
+ /* Configure label: */
+ m_pLabelIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ /* Add into layout: */
+ pLayoutWelcome->addWidget(m_pLabelIcon);
+ pLayoutWelcome->setAlignment(m_pLabelIcon, Qt::AlignHCenter | Qt::AlignTop);
+ }
+
+ /* Add into layout: */
+ pMainLayout->addLayout(pLayoutWelcome);
+ }
+
+ /* Add stretch: */
+ pMainLayout->addStretch();
+ }
+
+ uiCommon().setHelpKeyword(this, "intro-starting");
+
+ /* Translate finally: */
+ retranslateUi();
+ /* Update pixmap: */
+ updatePixmap();
+}
+
+void UIWelcomePane::updatePixmap()
+{
+ /* Assign corresponding icon: */
+ if (!m_icon.isNull())
+ {
+ const QList<QSize> sizes = m_icon.availableSizes();
+ const QSize firstOne = sizes.isEmpty() ? QSize(200, 200) : sizes.first();
+ m_pLabelIcon->setPixmap(m_icon.pixmap(window()->windowHandle(),
+ QSize(firstOne.width(),
+ firstOne.height())));
+ }
+}
+
+
+#include "UIWelcomePane.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/UIWelcomePane.h b/src/VBox/Frontends/VirtualBox/src/manager/UIWelcomePane.h
new file mode 100644
index 00000000..29eda449
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/UIWelcomePane.h
@@ -0,0 +1,84 @@
+/* $Id: UIWelcomePane.h $ */
+/** @file
+ * VBox Qt GUI - UIWelcomePane class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_UIWelcomePane_h
+#define FEQT_INCLUDED_SRC_manager_UIWelcomePane_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QLabel;
+
+/** QWidget subclass holding Welcome information about VirtualBox. */
+class UIWelcomePane : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Welcome pane passing @a pParent to the base-class. */
+ UIWelcomePane(QWidget *pParent = 0);
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles activated @a strLink. */
+ void sltHandleLinkActivated(const QString &strLink);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates pixmap. */
+ void updatePixmap();
+
+ /** Holds the icon instance. */
+ QIcon m_icon;
+
+ /** Holds the text label instance. */
+ QLabel *m_pLabelText;
+ /** Holds the icon label instance. */
+ QLabel *m_pLabelIcon;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_UIWelcomePane_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/manager/chooser/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooser.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooser.cpp
new file mode 100644
index 00000000..01181923
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooser.cpp
@@ -0,0 +1,354 @@
+/* $Id: UIChooser.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooser class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIChooser.h"
+#include "UIChooserModel.h"
+#include "UIChooserView.h"
+
+
+UIChooser::UIChooser(QWidget *pParent, UIActionPool *pActionPool)
+ : QWidget(pParent)
+ , m_pActionPool(pActionPool)
+ , m_pChooserModel(0)
+ , m_pChooserView(0)
+{
+ prepare();
+}
+
+UIChooser::~UIChooser()
+{
+ cleanup();
+}
+
+bool UIChooser::isGroupSavingInProgress() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isGroupSavingInProgress();
+}
+
+bool UIChooser::isCloudProfileUpdateInProgress() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isCloudProfileUpdateInProgress();
+}
+
+UIVirtualMachineItem *UIChooser::currentItem() const
+{
+ AssertPtrReturn(model(), 0);
+ return model()->firstSelectedMachineItem();
+}
+
+QList<UIVirtualMachineItem*> UIChooser::currentItems() const
+{
+ AssertPtrReturn(model(), QList<UIVirtualMachineItem*>());
+ return model()->selectedMachineItems();
+}
+
+bool UIChooser::isGroupItemSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isGroupItemSelected();
+}
+
+bool UIChooser::isGlobalItemSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isGlobalItemSelected();
+}
+
+bool UIChooser::isMachineItemSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isMachineItemSelected();
+}
+
+bool UIChooser::isLocalMachineItemSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isLocalMachineItemSelected();
+}
+
+bool UIChooser::isCloudMachineItemSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isCloudMachineItemSelected();
+}
+
+bool UIChooser::isSingleGroupSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isSingleGroupSelected();
+}
+
+bool UIChooser::isSingleLocalGroupSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isSingleLocalGroupSelected();
+}
+
+bool UIChooser::isSingleCloudProviderGroupSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isSingleCloudProviderGroupSelected();
+}
+
+bool UIChooser::isSingleCloudProfileGroupSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isSingleCloudProfileGroupSelected();
+}
+
+bool UIChooser::isAllItemsOfOneGroupSelected() const
+{
+ AssertPtrReturn(model(), false);
+ return model()->isAllItemsOfOneGroupSelected();
+}
+
+QString UIChooser::fullGroupName() const
+{
+ AssertPtrReturn(model(), QString());
+ return model()->fullGroupName();
+}
+
+void UIChooser::openGroupNameEditor()
+{
+ AssertPtrReturnVoid(model());
+ model()->startEditingSelectedGroupItemName();
+}
+
+void UIChooser::disbandGroup()
+{
+ AssertPtrReturnVoid(model());
+ model()->disbandSelectedGroupItem();
+}
+
+void UIChooser::removeMachine()
+{
+ AssertPtrReturnVoid(model());
+ model()->removeSelectedMachineItems();
+}
+
+void UIChooser::moveMachineToGroup(const QString &strName)
+{
+ AssertPtrReturnVoid(model());
+ model()->moveSelectedMachineItemsToGroupItem(strName);
+}
+
+QStringList UIChooser::possibleGroupsForMachineToMove(const QUuid &uId)
+{
+ AssertPtrReturn(model(), QStringList());
+ return model()->possibleGroupNodeNamesForMachineNodeToMove(uId);
+}
+
+QStringList UIChooser::possibleGroupsForGroupToMove(const QString &strFullName)
+{
+ AssertPtrReturn(model(), QStringList());
+ return model()->possibleGroupNodeNamesForGroupNodeToMove(strFullName);
+}
+
+void UIChooser::refreshMachine()
+{
+ AssertPtrReturnVoid(model());
+ model()->refreshSelectedMachineItems();
+}
+
+void UIChooser::sortGroup()
+{
+ AssertPtrReturnVoid(model());
+ model()->sortSelectedGroupItem();
+}
+
+void UIChooser::setMachineSearchWidgetVisibility(bool fVisible)
+{
+ AssertPtrReturnVoid(view());
+ view()->setSearchWidgetVisible(fVisible);
+}
+
+void UIChooser::setCurrentMachine(const QUuid &uId)
+{
+ AssertPtrReturnVoid(model());
+ model()->setCurrentMachineItem(uId);
+}
+
+void UIChooser::setCurrentGlobal()
+{
+ AssertPtrReturnVoid(model());
+ model()->setCurrentGlobalItem();
+}
+
+void UIChooser::setGlobalItemHeightHint(int iHeight)
+{
+ AssertPtrReturnVoid(model());
+ model()->setGlobalItemHeightHint(iHeight);
+}
+
+void UIChooser::sltToolMenuRequested(UIToolClass enmClass, const QPoint &position)
+{
+ /* Translate scene coordinates to global one: */
+ AssertPtrReturnVoid(view());
+ emit sigToolMenuRequested(enmClass, mapToGlobal(view()->mapFromScene(position)));
+}
+
+void UIChooser::prepare()
+{
+ /* Prepare everything: */
+ prepareModel();
+ prepareWidgets();
+ prepareConnections();
+
+ /* Init model: */
+ initModel();
+}
+
+void UIChooser::prepareModel()
+{
+ /* Prepare Chooser-model: */
+ m_pChooserModel = new UIChooserModel(this, actionPool());
+}
+
+void UIChooser::prepareWidgets()
+{
+ /* Prepare main-layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+ pMainLayout->setSpacing(0);
+
+ /* Prepare Chooser-view: */
+ m_pChooserView = new UIChooserView(this);
+ if (m_pChooserView)
+ {
+ AssertPtrReturnVoid(model());
+ m_pChooserView->setModel(model());
+ m_pChooserView->setScene(model()->scene());
+ m_pChooserView->show();
+ setFocusProxy(m_pChooserView);
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pChooserView);
+ }
+ }
+}
+
+void UIChooser::prepareConnections()
+{
+ AssertPtrReturnVoid(model());
+ AssertPtrReturnVoid(view());
+
+ /* Abstract Chooser-model connections: */
+ connect(model(), &UIChooserModel::sigCloudMachineStateChange,
+ this, &UIChooser::sigCloudMachineStateChange);
+ connect(model(), &UIChooserModel::sigGroupSavingStateChanged,
+ this, &UIChooser::sigGroupSavingStateChanged);
+ connect(model(), &UIChooserModel::sigCloudUpdateStateChanged,
+ this, &UIChooser::sigCloudUpdateStateChanged);
+
+ /* Chooser-model connections: */
+ connect(model(), &UIChooserModel::sigToolMenuRequested,
+ this, &UIChooser::sltToolMenuRequested);
+ connect(model(), &UIChooserModel::sigSelectionChanged,
+ this, &UIChooser::sigSelectionChanged);
+ connect(model(), &UIChooserModel::sigSelectionInvalidated,
+ this, &UIChooser::sigSelectionInvalidated);
+ connect(model(), &UIChooserModel::sigToggleStarted,
+ this, &UIChooser::sigToggleStarted);
+ connect(model(), &UIChooserModel::sigToggleFinished,
+ this, &UIChooser::sigToggleFinished);
+ connect(model(), &UIChooserModel::sigRootItemMinimumWidthHintChanged,
+ view(), &UIChooserView::sltMinimumWidthHintChanged);
+ connect(model(), &UIChooserModel::sigStartOrShowRequest,
+ this, &UIChooser::sigStartOrShowRequest);
+
+ /* Chooser-view connections: */
+ connect(view(), &UIChooserView::sigResized,
+ model(), &UIChooserModel::sltHandleViewResized);
+ connect(view(), &UIChooserView::sigSearchWidgetVisibilityChanged,
+ this, &UIChooser::sigMachineSearchWidgetVisibilityChanged);
+}
+
+void UIChooser::initModel()
+{
+ AssertPtrReturnVoid(model());
+ model()->init();
+}
+
+void UIChooser::deinitModel()
+{
+ AssertPtrReturnVoid(model());
+ model()->deinit();
+}
+
+void UIChooser::cleanupConnections()
+{
+ AssertPtrReturnVoid(model());
+ AssertPtrReturnVoid(view());
+
+ /* Abstract Chooser-model connections: */
+ disconnect(model(), &UIChooserModel::sigCloudMachineStateChange,
+ this, &UIChooser::sigCloudMachineStateChange);
+ disconnect(model(), &UIChooserModel::sigGroupSavingStateChanged,
+ this, &UIChooser::sigGroupSavingStateChanged);
+ disconnect(model(), &UIChooserModel::sigCloudUpdateStateChanged,
+ this, &UIChooser::sigCloudUpdateStateChanged);
+
+ /* Chooser-model connections: */
+ disconnect(model(), &UIChooserModel::sigToolMenuRequested,
+ this, &UIChooser::sltToolMenuRequested);
+ disconnect(model(), &UIChooserModel::sigSelectionChanged,
+ this, &UIChooser::sigSelectionChanged);
+ disconnect(model(), &UIChooserModel::sigSelectionInvalidated,
+ this, &UIChooser::sigSelectionInvalidated);
+ disconnect(model(), &UIChooserModel::sigToggleStarted,
+ this, &UIChooser::sigToggleStarted);
+ disconnect(model(), &UIChooserModel::sigToggleFinished,
+ this, &UIChooser::sigToggleFinished);
+ disconnect(model(), &UIChooserModel::sigRootItemMinimumWidthHintChanged,
+ view(), &UIChooserView::sltMinimumWidthHintChanged);
+ disconnect(model(), &UIChooserModel::sigStartOrShowRequest,
+ this, &UIChooser::sigStartOrShowRequest);
+
+ /* Chooser-view connections: */
+ disconnect(view(), &UIChooserView::sigResized,
+ model(), &UIChooserModel::sltHandleViewResized);
+ disconnect(view(), &UIChooserView::sigSearchWidgetVisibilityChanged,
+ this, &UIChooser::sigMachineSearchWidgetVisibilityChanged);
+}
+
+void UIChooser::cleanup()
+{
+ /* Deinit model: */
+ deinitModel();
+
+ /* Cleanup everything: */
+ cleanupConnections();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooser.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooser.h
new file mode 100644
index 00000000..8d8c2fdb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooser.h
@@ -0,0 +1,239 @@
+/* $Id: UIChooser.h $ */
+/** @file
+ * VBox Qt GUI - UIChooser class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooser_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooser_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+/* Forward declarations: */
+class UIActionPool;
+class UIChooserModel;
+class UIChooserView;
+class UIVirtualMachineItem;
+
+/** QWidget extension used as VM Chooser-pane. */
+class UIChooser : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name Cloud machine stuff.
+ * @{ */
+ /** Notifies listeners about state change for cloud machine with certain @a uId. */
+ void sigCloudMachineStateChange(const QUuid &uId);
+ /** @} */
+
+ /** @name Group saving stuff.
+ * @{ */
+ /** Notifies listeners about group saving state change. */
+ void sigGroupSavingStateChanged();
+ /** @} */
+
+ /** @name Cloud update stuff.
+ * @{ */
+ /** Notifies listeners about cloud update state change. */
+ void sigCloudUpdateStateChanged();
+ /** @} */
+
+ /** @name Tool stuff.
+ * @{ */
+ /** Notifies listeners about tool popup-menu request for certain @a enmClass and @a position. */
+ void sigToolMenuRequested(UIToolClass enmClass, const QPoint &position);
+ /** @} */
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Notifies listeners about selection changed. */
+ void sigSelectionChanged();
+ /** Notifies listeners about selection invalidated. */
+ void sigSelectionInvalidated();
+
+ /** Notifies listeners about group toggling started. */
+ void sigToggleStarted();
+ /** Notifies listeners about group toggling finished. */
+ void sigToggleFinished();
+ /** @} */
+
+ /** @name Action stuff.
+ * @{ */
+ /** Notifies listeners about start or show request. */
+ void sigStartOrShowRequest();
+ /** Notifies listeners about machine search widget visibility changed to @a fVisible. */
+ void sigMachineSearchWidgetVisibilityChanged(bool fVisible);
+ /** @} */
+
+public:
+
+ /** Constructs Chooser-pane passing @a pParent to the base-class.
+ * @param pActionPool Brings the action-pool reference. */
+ UIChooser(QWidget *pParent, UIActionPool *pActionPool);
+ /** Destructs Chooser-pane. */
+ virtual ~UIChooser() RT_OVERRIDE;
+
+ /** @name General stuff.
+ * @{ */
+ /** Returns the action-pool reference. */
+ UIActionPool *actionPool() const { return m_pActionPool; }
+
+ /** Return the Chooser-model instance. */
+ UIChooserModel *model() const { return m_pChooserModel; }
+ /** Return the Chooser-view instance. */
+ UIChooserView *view() const { return m_pChooserView; }
+ /** @} */
+
+ /** @name Group saving stuff.
+ * @{ */
+ /** Returns whether group saving is in progress. */
+ bool isGroupSavingInProgress() const;
+ /** @} */
+
+ /** @name Cloud update stuff.
+ * @{ */
+ /** Returns whether at least one cloud profile currently being updated. */
+ bool isCloudProfileUpdateInProgress() const;
+ /** @} */
+
+ /** @name Current-item stuff.
+ * @{ */
+ /** Returns current-item. */
+ UIVirtualMachineItem *currentItem() const;
+ /** Returns a list of current-items. */
+ QList<UIVirtualMachineItem*> currentItems() const;
+
+ /** Returns whether group item is selected. */
+ bool isGroupItemSelected() const;
+ /** Returns whether global item is selected. */
+ bool isGlobalItemSelected() const;
+ /** Returns whether machine item is selected. */
+ bool isMachineItemSelected() const;
+ /** Returns whether local machine item is selected. */
+ bool isLocalMachineItemSelected() const;
+ /** Returns whether cloud machine item is selected. */
+ bool isCloudMachineItemSelected() const;
+
+ /** Returns whether single group is selected. */
+ bool isSingleGroupSelected() const;
+ /** Returns whether single local group is selected. */
+ bool isSingleLocalGroupSelected() const;
+ /** Returns whether single cloud provider group is selected. */
+ bool isSingleCloudProviderGroupSelected() const;
+ /** Returns whether single cloud profile group is selected. */
+ bool isSingleCloudProfileGroupSelected() const;
+ /** Returns whether all items of one group are selected. */
+ bool isAllItemsOfOneGroupSelected() const;
+
+ /** Returns full name of currently selected group. */
+ QString fullGroupName() const;
+ /** @} */
+
+ /** @name Action handling stuff.
+ * @{ */
+ /** Opens group name editor. */
+ void openGroupNameEditor();
+ /** Disbands group. */
+ void disbandGroup();
+ /** Removes machine. */
+ void removeMachine();
+ /** Moves machine to a group with certain @a strName. */
+ void moveMachineToGroup(const QString &strName);
+ /** Returns possible groups for machine with passed @a uId to move to. */
+ QStringList possibleGroupsForMachineToMove(const QUuid &uId);
+ /** Returns possible groups for group with passed @a strFullName to move to. */
+ QStringList possibleGroupsForGroupToMove(const QString &strFullName);
+ /** Refreshes machine. */
+ void refreshMachine();
+ /** Sorts group. */
+ void sortGroup();
+ /** Toggle machine search widget to be @a fVisible. */
+ void setMachineSearchWidgetVisibility(bool fVisible);
+ /** Changes current machine to the one with certain @a uId. */
+ void setCurrentMachine(const QUuid &uId);
+ /** Set global tools to be the current item. */
+ void setCurrentGlobal();
+ /** @} */
+
+public slots:
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Defines global item @a iHeight. */
+ void setGlobalItemHeightHint(int iHeight);
+ /** @} */
+
+private slots:
+
+ /** @name General stuff.
+ * @{ */
+ /** Handles signal about tool popup-menu request for certain tool @a enmClass and in specified @a position. */
+ void sltToolMenuRequested(UIToolClass enmClass, const QPoint &position);
+ /** @} */
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares model. */
+ void prepareModel();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Init model. */
+ void initModel();
+
+ /** Deinit model. */
+ void deinitModel();
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+
+ /** Holds the Chooser-model instane. */
+ UIChooserModel *m_pChooserModel;
+ /** Holds the Chooser-view instane. */
+ UIChooserView *m_pChooserView;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooser_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.cpp
new file mode 100644
index 00000000..c7f73cdf
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.cpp
@@ -0,0 +1,1942 @@
+/* $Id: UIChooserAbstractModel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserAbstractModel class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QRegExp>
+#include <QRegularExpression>
+#include <QThread>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIChooser.h"
+#include "UIChooserAbstractModel.h"
+#include "UIChooserNode.h"
+#include "UIChooserNodeGroup.h"
+#include "UIChooserNodeGlobal.h"
+#include "UIChooserNodeMachine.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+#include "UIProgressTaskReadCloudMachineList.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualMachineItemCloud.h"
+
+/* COM includes: */
+#include "CCloudMachine.h"
+#include "CCloudProfile.h"
+#include "CCloudProvider.h"
+#include "CMachine.h"
+
+/* Type defs: */
+typedef QSet<QString> UIStringSet;
+
+
+/** QThread subclass allowing to save group settings asynchronously. */
+class UIThreadGroupSettingsSave : public QThread
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about machine with certain @a uMachineId to be reloaded. */
+ void sigReload(const QUuid &uMachineId);
+
+ /** Notifies about task is complete. */
+ void sigComplete();
+
+public:
+
+ /** Returns group settings saving thread instance. */
+ static UIThreadGroupSettingsSave *instance();
+ /** Prepares group settings saving thread instance. */
+ static void prepare();
+ /** Cleanups group settings saving thread instance. */
+ static void cleanup();
+
+ /** Configures @a group settings saving thread with corresponding @a pListener.
+ * @param oldLists Brings the old settings list to be compared.
+ * @param newLists Brings the new settings list to be saved. */
+ void configure(QObject *pParent,
+ const QMap<QString, QStringList> &oldLists,
+ const QMap<QString, QStringList> &newLists);
+
+protected:
+
+ /** Constructs group settings saving thread. */
+ UIThreadGroupSettingsSave();
+ /** Destructs group settings saving thread. */
+ virtual ~UIThreadGroupSettingsSave() RT_OVERRIDE;
+
+ /** Contains a thread task to be executed. */
+ virtual void run() RT_OVERRIDE;
+
+ /** Holds the singleton instance. */
+ static UIThreadGroupSettingsSave *s_pInstance;
+
+ /** Holds the map of group settings to be compared. */
+ QMap<QString, QStringList> m_oldLists;
+ /** Holds the map of group settings to be saved. */
+ QMap<QString, QStringList> m_newLists;
+};
+
+
+/** QThread subclass allowing to save group definitions asynchronously. */
+class UIThreadGroupDefinitionsSave : public QThread
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about task is complete. */
+ void sigComplete();
+
+public:
+
+ /** Returns group definitions saving thread instance. */
+ static UIThreadGroupDefinitionsSave *instance();
+ /** Prepares group definitions saving thread instance. */
+ static void prepare();
+ /** Cleanups group definitions saving thread instance. */
+ static void cleanup();
+
+ /** Configures group definitions saving thread with corresponding @a pListener.
+ * @param lists Brings definitions lists to be saved. */
+ void configure(QObject *pListener,
+ const QMap<QString, QStringList> &lists);
+
+protected:
+
+ /** Constructs group definitions saving thread. */
+ UIThreadGroupDefinitionsSave();
+ /** Destructs group definitions saving thread. */
+ virtual ~UIThreadGroupDefinitionsSave() RT_OVERRIDE;
+
+ /** Contains a thread task to be executed. */
+ virtual void run() RT_OVERRIDE;
+
+ /** Holds the singleton instance. */
+ static UIThreadGroupDefinitionsSave *s_pInstance;
+
+ /** Holds the map of group definitions to be saved. */
+ QMap<QString, QStringList> m_lists;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIThreadGroupSettingsSave implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIThreadGroupSettingsSave *UIThreadGroupSettingsSave::s_pInstance = 0;
+
+/* static */
+UIThreadGroupSettingsSave *UIThreadGroupSettingsSave::instance()
+{
+ return s_pInstance;
+}
+
+/* static */
+void UIThreadGroupSettingsSave::prepare()
+{
+ /* Make sure instance is not prepared: */
+ if (s_pInstance)
+ return;
+
+ /* Crate instance: */
+ new UIThreadGroupSettingsSave;
+}
+
+/* static */
+void UIThreadGroupSettingsSave::cleanup()
+{
+ /* Make sure instance is prepared: */
+ if (!s_pInstance)
+ return;
+
+ /* Delete instance: */
+ delete s_pInstance;
+}
+
+void UIThreadGroupSettingsSave::configure(QObject *pParent,
+ const QMap<QString, QStringList> &oldLists,
+ const QMap<QString, QStringList> &newLists)
+{
+ m_oldLists = oldLists;
+ m_newLists = newLists;
+ UIChooserAbstractModel *pChooserAbstractModel = qobject_cast<UIChooserAbstractModel*>(pParent);
+ AssertPtrReturnVoid(pChooserAbstractModel);
+ {
+ connect(this, &UIThreadGroupSettingsSave::sigComplete,
+ pChooserAbstractModel, &UIChooserAbstractModel::sltGroupSettingsSaveComplete);
+ }
+}
+
+UIThreadGroupSettingsSave::UIThreadGroupSettingsSave()
+{
+ /* Assign instance: */
+ s_pInstance = this;
+}
+
+UIThreadGroupSettingsSave::~UIThreadGroupSettingsSave()
+{
+ /* Make sure thread work is complete: */
+ wait();
+
+ /* Erase instance: */
+ s_pInstance = 0;
+}
+
+void UIThreadGroupSettingsSave::run()
+{
+ /* COM prepare: */
+ COMBase::InitializeCOM(false);
+
+ /* For every particular machine ID: */
+ foreach (const QString &strId, m_newLists.keys())
+ {
+ /* Get new group list/set: */
+ const QStringList &newGroupList = m_newLists.value(strId);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ const UIStringSet newGroupSet(newGroupList.begin(), newGroupList.end());
+#else
+ const UIStringSet &newGroupSet = UIStringSet::fromList(newGroupList);
+#endif
+ /* Get old group list/set: */
+ const QStringList &oldGroupList = m_oldLists.value(strId);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ const UIStringSet oldGroupSet(oldGroupList.begin(), oldGroupList.end());
+#else
+ const UIStringSet &oldGroupSet = UIStringSet::fromList(oldGroupList);
+#endif
+ /* Make sure group set changed: */
+ if (newGroupSet == oldGroupSet)
+ continue;
+
+ /* The next steps are subsequent.
+ * Every of them is mandatory in order to continue
+ * with common cleanup in case of failure.
+ * We have to simulate a try-catch block. */
+ CSession comSession;
+ CMachine comMachine;
+ do
+ {
+ /* 1. Open session: */
+ comSession = uiCommon().openSession(QUuid(strId));
+ if (comSession.isNull())
+ break;
+
+ /* 2. Get session machine: */
+ comMachine = comSession.GetMachine();
+ if (comMachine.isNull())
+ break;
+
+ /* 3. Set new groups: */
+ comMachine.SetGroups(newGroupList.toVector());
+ if (!comMachine.isOk())
+ {
+ msgCenter().cannotSetGroups(comMachine);
+ break;
+ }
+
+ /* 4. Save settings: */
+ comMachine.SaveSettings();
+ if (!comMachine.isOk())
+ {
+ msgCenter().cannotSaveMachineSettings(comMachine);
+ break;
+ }
+ } while (0);
+
+ /* Cleanup if necessary: */
+ if (comMachine.isNull() || !comMachine.isOk())
+ emit sigReload(QUuid(strId));
+ if (!comSession.isNull())
+ comSession.UnlockMachine();
+ }
+
+ /* Notify listeners about completeness: */
+ emit sigComplete();
+
+ /* COM cleanup: */
+ COMBase::CleanupCOM();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIThreadGroupDefinitionsSave implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIThreadGroupDefinitionsSave *UIThreadGroupDefinitionsSave::s_pInstance = 0;
+
+/* static */
+UIThreadGroupDefinitionsSave *UIThreadGroupDefinitionsSave::instance()
+{
+ return s_pInstance;
+}
+
+/* static */
+void UIThreadGroupDefinitionsSave::prepare()
+{
+ /* Make sure instance is not prepared: */
+ if (s_pInstance)
+ return;
+
+ /* Crate instance: */
+ new UIThreadGroupDefinitionsSave;
+}
+
+/* static */
+void UIThreadGroupDefinitionsSave::cleanup()
+{
+ /* Make sure instance is prepared: */
+ if (!s_pInstance)
+ return;
+
+ /* Delete instance: */
+ delete s_pInstance;
+}
+
+void UIThreadGroupDefinitionsSave::configure(QObject *pParent,
+ const QMap<QString, QStringList> &groups)
+{
+ m_lists = groups;
+ UIChooserAbstractModel *pChooserAbstractModel = qobject_cast<UIChooserAbstractModel*>(pParent);
+ AssertPtrReturnVoid(pChooserAbstractModel);
+ {
+ connect(this, &UIThreadGroupDefinitionsSave::sigComplete,
+ pChooserAbstractModel, &UIChooserAbstractModel::sltGroupDefinitionsSaveComplete);
+ }
+}
+
+UIThreadGroupDefinitionsSave::UIThreadGroupDefinitionsSave()
+{
+ /* Assign instance: */
+ s_pInstance = this;
+}
+
+UIThreadGroupDefinitionsSave::~UIThreadGroupDefinitionsSave()
+{
+ /* Make sure thread work is complete: */
+ wait();
+
+ /* Erase instance: */
+ s_pInstance = 0;
+}
+
+void UIThreadGroupDefinitionsSave::run()
+{
+ /* COM prepare: */
+ COMBase::InitializeCOM(false);
+
+ /* Acquire a list of known group definition keys: */
+ QStringList knownKeys = gEDataManager->knownMachineGroupDefinitionKeys();
+ /* For every group definition to be saved: */
+ foreach (const QString &strId, m_lists.keys())
+ {
+ /* Save definition only if there is a change: */
+ if (gEDataManager->machineGroupDefinitions(strId) != m_lists.value(strId))
+ gEDataManager->setMachineGroupDefinitions(strId, m_lists.value(strId));
+ /* Remove it from known keys: */
+ knownKeys.removeAll(strId);
+ }
+ /* Wipe out rest of known group definitions: */
+ foreach (const QString strId, knownKeys)
+ gEDataManager->setMachineGroupDefinitions(strId, QStringList());
+
+ /* Notify listeners about completeness: */
+ emit sigComplete();
+
+ /* COM cleanup: */
+ COMBase::CleanupCOM();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIChooserAbstractModel implementation. *
+*********************************************************************************************************************************/
+
+UIChooserAbstractModel::UIChooserAbstractModel(UIChooser *pParent)
+ : QObject(pParent)
+ , m_pParent(pParent)
+ , m_pInvisibleRootNode(0)
+{
+ prepare();
+}
+
+UIChooserAbstractModel::~UIChooserAbstractModel()
+{
+ cleanup();
+}
+
+void UIChooserAbstractModel::init()
+{
+ /* Create invisible root group node: */
+ m_pInvisibleRootNode = new UIChooserNodeGroup(0 /* parent */,
+ 0 /* position */,
+ QUuid() /* id */,
+ QString() /* name */,
+ UIChooserNodeGroupType_Local,
+ true /* opened */);
+ if (invisibleRoot())
+ {
+ /* Link root to this model: */
+ invisibleRoot()->setModel(this);
+
+ /* Create global node: */
+ new UIChooserNodeGlobal(invisibleRoot() /* parent */,
+ 0 /* position */,
+ shouldGlobalNodeBeFavorite(invisibleRoot()),
+ QString() /* tip */);
+
+ /* Reload local tree: */
+ reloadLocalTree();
+ /* Reload cloud tree: */
+ reloadCloudTree();
+ }
+}
+
+void UIChooserAbstractModel::deinit()
+{
+ /* Make sure all saving steps complete: */
+ makeSureGroupSettingsSaveIsFinished();
+ makeSureGroupDefinitionsSaveIsFinished();
+}
+
+void UIChooserAbstractModel::wipeOutEmptyGroups()
+{
+ wipeOutEmptyGroupsStartingFrom(invisibleRoot());
+}
+
+QStringList UIChooserAbstractModel::possibleGroupNodeNamesForMachineNodeToMove(const QUuid &uId)
+{
+ /* Search for all the machine nodes with passed ID: */
+ QList<UIChooserNode*> machineNodes;
+ invisibleRoot()->searchForNodes(uId.toString(),
+ UIChooserItemSearchFlag_Machine | UIChooserItemSearchFlag_ExactId,
+ machineNodes);
+
+ /* Return group nodes starting from root one: */
+ return gatherPossibleGroupNodeNames(invisibleRoot(), machineNodes);
+}
+
+QStringList UIChooserAbstractModel::possibleGroupNodeNamesForGroupNodeToMove(const QString &strFullName)
+{
+ /* Search for all the group nodes with passed full-name: */
+ QList<UIChooserNode*> groupNodes;
+ invisibleRoot()->searchForNodes(strFullName,
+ UIChooserItemSearchFlag_LocalGroup | UIChooserItemSearchFlag_FullName,
+ groupNodes);
+
+ /* Return group nodes starting from root one: */
+ return gatherPossibleGroupNodeNames(invisibleRoot(), groupNodes);
+}
+
+/* static */
+QString UIChooserAbstractModel::uniqueGroupName(UIChooserNode *pRoot)
+{
+ /* Enumerate all the group names: */
+ QStringList groupNames;
+ foreach (UIChooserNode *pNode, pRoot->nodes(UIChooserNodeType_Group))
+ groupNames << pNode->name();
+
+ /* Prepare reg-exp: */
+ const QString strMinimumName = tr("New group");
+ const QString strShortTemplate = strMinimumName;
+ const QString strFullTemplate = strShortTemplate + QString(" (\\d+)");
+ const QRegExp shortRegExp(strShortTemplate);
+ const QRegExp fullRegExp(strFullTemplate);
+
+ /* Search for the maximum index: */
+ int iMinimumPossibleNumber = 0;
+ foreach (const QString &strName, groupNames)
+ {
+ if (shortRegExp.exactMatch(strName))
+ iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, 2);
+ else if (fullRegExp.exactMatch(strName))
+ iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, fullRegExp.cap(1).toInt() + 1);
+ }
+
+ /* Prepare/return result: */
+ QString strResult = strMinimumName;
+ if (iMinimumPossibleNumber)
+ strResult += " " + QString::number(iMinimumPossibleNumber);
+ return strResult;
+}
+
+void UIChooserAbstractModel::performSearch(const QString &strSearchTerm, int iSearchFlags)
+{
+ /* Make sure invisible root exists: */
+ AssertPtrReturnVoid(invisibleRoot());
+
+ /* Currently we perform the search only for machines, when this to be changed make
+ * sure the disabled flags of the other item types are also managed correctly. */
+
+ /* Reset the search first to erase the disabled flag,
+ * this also returns a full list of all machine nodes: */
+ const QList<UIChooserNode*> nodes = resetSearch();
+
+ /* Stop here if no search conditions specified: */
+ if (strSearchTerm.isEmpty())
+ return;
+
+ /* Search for all the nodes matching required condition: */
+ invisibleRoot()->searchForNodes(strSearchTerm, iSearchFlags, m_searchResults);
+
+ /* Assign/reset the disabled flag for required nodes: */
+ foreach (UIChooserNode *pNode, nodes)
+ {
+ AssertPtrReturnVoid(pNode);
+ pNode->setDisabled(!m_searchResults.contains(pNode));
+ }
+}
+
+QList<UIChooserNode*> UIChooserAbstractModel::resetSearch()
+{
+ /* Prepare resulting nodes: */
+ QList<UIChooserNode*> nodes;
+
+ /* Make sure invisible root exists: */
+ AssertPtrReturn(invisibleRoot(), nodes);
+
+ /* Calling UIChooserNode::searchForNodes with an empty search term
+ * returns a list all nodes (of the whole tree) of the required type: */
+ invisibleRoot()->searchForNodes(QString(), UIChooserItemSearchFlag_Machine, nodes);
+
+ /* Reset the disabled flag of the nodes first: */
+ foreach (UIChooserNode *pNode, nodes)
+ {
+ AssertPtrReturn(pNode, nodes);
+ pNode->setDisabled(false);
+ }
+
+ /* Reset the search result related data: */
+ m_searchResults.clear();
+
+ /* Return nodes: */
+ return nodes;
+}
+
+QList<UIChooserNode*> UIChooserAbstractModel::searchResult() const
+{
+ return m_searchResults;
+}
+
+void UIChooserAbstractModel::saveGroups()
+{
+ emit sigSaveSettings();
+}
+
+bool UIChooserAbstractModel::isGroupSavingInProgress() const
+{
+ return UIThreadGroupSettingsSave::instance()
+ || UIThreadGroupDefinitionsSave::instance();
+}
+
+/* static */
+QString UIChooserAbstractModel::toOldStyleUuid(const QUuid &uId)
+{
+ return uId.toString().remove(QRegularExpression("[{}]"));
+}
+
+/* static */
+QString UIChooserAbstractModel::prefixToString(UIChooserNodeDataPrefixType enmType)
+{
+ switch (enmType)
+ {
+ /* Global nodes: */
+ case UIChooserNodeDataPrefixType_Global: return "n";
+ /* Machine nodes: */
+ case UIChooserNodeDataPrefixType_Machine: return "m";
+ /* Group nodes: */
+ case UIChooserNodeDataPrefixType_Local: return "g";
+ case UIChooserNodeDataPrefixType_Provider: return "p";
+ case UIChooserNodeDataPrefixType_Profile: return "a";
+ }
+ return QString();
+}
+
+/* static */
+QString UIChooserAbstractModel::optionToString(UIChooserNodeDataOptionType enmType)
+{
+ switch (enmType)
+ {
+ /* Global nodes: */
+ case UIChooserNodeDataOptionType_GlobalFavorite: return "f";
+ /* Group nodes: */
+ case UIChooserNodeDataOptionType_GroupOpened: return "o";
+ }
+ return QString();
+}
+
+/* static */
+QString UIChooserAbstractModel::valueToString(UIChooserNodeDataValueType enmType)
+{
+ switch (enmType)
+ {
+ /* Global nodes: */
+ case UIChooserNodeDataValueType_GlobalDefault: return "GLOBAL";
+ }
+ return QString();
+}
+
+void UIChooserAbstractModel::insertCloudEntityKey(const UICloudEntityKey &key)
+{
+// printf("Cloud entity with key %s being updated..\n", key.toString().toUtf8().constData());
+ m_cloudEntityKeysBeingUpdated.insert(key);
+ emit sigCloudUpdateStateChanged();
+}
+
+void UIChooserAbstractModel::removeCloudEntityKey(const UICloudEntityKey &key)
+{
+// printf("Cloud entity with key %s is updated!\n", key.toString().toUtf8().constData());
+ m_cloudEntityKeysBeingUpdated.remove(key);
+ emit sigCloudUpdateStateChanged();
+}
+
+bool UIChooserAbstractModel::containsCloudEntityKey(const UICloudEntityKey &key) const
+{
+ return m_cloudEntityKeysBeingUpdated.contains(key);
+}
+
+bool UIChooserAbstractModel::isCloudProfileUpdateInProgress() const
+{
+ /* Compose RE for profile: */
+ QRegExp re("^/[^/]+/[^/]+$");
+ /* Check whether keys match profile RE: */
+ foreach (const UICloudEntityKey &key, m_cloudEntityKeysBeingUpdated)
+ {
+ const int iIndex = re.indexIn(key.toString());
+ if (iIndex != -1)
+ return true;
+ }
+ /* False by default: */
+ return false;
+}
+
+void UIChooserAbstractModel::sltHandleCloudMachineRefreshStarted()
+{
+ /* Acquire sender: */
+ UIVirtualMachineItem *pCache = qobject_cast<UIVirtualMachineItem*>(sender());
+ AssertPtrReturnVoid(pCache);
+
+ /* Acquire sender's ID: */
+ const QUuid uId = pCache->id();
+
+ /* Search for a first machine node with passed ID: */
+ UIChooserNode *pMachineNode = searchMachineNode(invisibleRoot(), uId);
+
+ /* Insert cloud machine key into a list of keys currently being updated: */
+ const UICloudEntityKey guiCloudMachineKey = UICloudEntityKey(pMachineNode->parentNode()->parentNode()->name(),
+ pMachineNode->parentNode()->name(),
+ pMachineNode->toMachineNode()->id());
+ insertCloudEntityKey(guiCloudMachineKey);
+}
+
+void UIChooserAbstractModel::sltHandleCloudMachineRefreshFinished()
+{
+ /* Acquire sender: */
+ UIVirtualMachineItem *pCache = qobject_cast<UIVirtualMachineItem*>(sender());
+ AssertPtrReturnVoid(pCache);
+
+ /* Acquire sender's ID: */
+ const QUuid uId = pCache->id();
+
+ /* Search for a first machine node with passed ID: */
+ UIChooserNode *pMachineNode = searchMachineNode(invisibleRoot(), uId);
+
+ /* Remove cloud machine key from the list of keys currently being updated: */
+ const UICloudEntityKey guiCloudMachineKey = UICloudEntityKey(pMachineNode->parentNode()->parentNode()->name(),
+ pMachineNode->parentNode()->name(),
+ pMachineNode->toMachineNode()->id());
+ removeCloudEntityKey(guiCloudMachineKey);
+
+ /* Notify listeners: */
+ emit sigCloudMachineStateChange(uId);
+}
+
+void UIChooserAbstractModel::sltGroupSettingsSaveComplete()
+{
+ makeSureGroupSettingsSaveIsFinished();
+ emit sigGroupSavingStateChanged();
+}
+
+void UIChooserAbstractModel::sltGroupDefinitionsSaveComplete()
+{
+ makeSureGroupDefinitionsSaveIsFinished();
+ emit sigGroupSavingStateChanged();
+}
+
+void UIChooserAbstractModel::sltLocalMachineStateChanged(const QUuid &uMachineId, const KMachineState)
+{
+ /* Update machine-nodes with passed id: */
+ invisibleRoot()->updateAllNodes(uMachineId);
+}
+
+void UIChooserAbstractModel::sltLocalMachineDataChanged(const QUuid &uMachineId)
+{
+ /* Update machine-nodes with passed id: */
+ invisibleRoot()->updateAllNodes(uMachineId);
+}
+
+void UIChooserAbstractModel::sltLocalMachineRegistrationChanged(const QUuid &uMachineId, const bool fRegistered)
+{
+ /* Existing VM unregistered? */
+ if (!fRegistered)
+ {
+ /* Remove machine-items with passed id: */
+ invisibleRoot()->removeAllNodes(uMachineId);
+ /* Wipe out empty groups: */
+ wipeOutEmptyGroups();
+ }
+ /* New VM registered? */
+ else
+ {
+ /* Should we show this VM? */
+ if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
+ {
+ /* Add new machine-item: */
+ const CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
+ if (comMachine.isNotNull())
+ addLocalMachineIntoTheTree(comMachine, true /* make it visible */);
+ }
+ }
+}
+
+void UIChooserAbstractModel::sltLocalMachineGroupsChanged(const QUuid &uMachineId)
+{
+ /* Skip VM if restricted: */
+ if (!gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
+ return;
+
+ /* Search for cached group list: */
+ const QStringList oldGroupList = m_groups.value(toOldStyleUuid(uMachineId));
+ //printf("Old groups for VM with ID=%s: %s\n",
+ // uMachineId.toString().toUtf8().constData(),
+ // oldGroupList.join(", ").toUtf8().constData());
+
+ /* Search for existing registered machine: */
+ const CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
+ if (comMachine.isNull())
+ return;
+ /* Look for a new group list: */
+ const QStringList newGroupList = comMachine.GetGroups().toList();
+ //printf("New groups for VM with ID=%s: %s\n",
+ // uMachineId.toString().toUtf8().constData(),
+ // newGroupList.join(", ").toUtf8().constData());
+
+ /* Re-register VM if required: */
+#ifdef VBOX_IS_QT6_OR_LATER /* we have to use range constructors since 6.0 */
+ QSet<QString> newGroupSet(newGroupList.begin(), newGroupList.end());
+ QSet<QString> oldGroupSet(oldGroupList.begin(), oldGroupList.end());
+ if (newGroupSet != oldGroupSet)
+#else
+ if (newGroupList.toSet() != oldGroupList.toSet())
+#endif
+ {
+ sltLocalMachineRegistrationChanged(uMachineId, false);
+ sltLocalMachineRegistrationChanged(uMachineId, true);
+ }
+}
+
+void UIChooserAbstractModel::sltSessionStateChanged(const QUuid &uMachineId, const KSessionState)
+{
+ /* Update machine-nodes with passed id: */
+ invisibleRoot()->updateAllNodes(uMachineId);
+}
+
+void UIChooserAbstractModel::sltSnapshotChanged(const QUuid &uMachineId, const QUuid &)
+{
+ /* Update machine-nodes with passed id: */
+ invisibleRoot()->updateAllNodes(uMachineId);
+}
+
+void UIChooserAbstractModel::sltHandleCloudProviderUninstall(const QUuid &uProviderId)
+{
+ /* First of all, stop all cloud updates: */
+ stopCloudUpdates();
+
+ /* Search and delete corresponding cloud provider node if present: */
+ delete searchProviderNode(uProviderId);
+}
+
+void UIChooserAbstractModel::sltReloadMachine(const QUuid &uMachineId)
+{
+ /* Remove machine-items with passed id: */
+ invisibleRoot()->removeAllNodes(uMachineId);
+ /* Wipe out empty groups: */
+ wipeOutEmptyGroups();
+
+ /* Should we show this VM? */
+ if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
+ {
+ /* Add new machine-item: */
+ const CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
+ addLocalMachineIntoTheTree(comMachine, true /* make it visible */);
+ }
+}
+
+void UIChooserAbstractModel::sltCommitData()
+{
+ /* Finally, stop all cloud updates: */
+ stopCloudUpdates(true /* forced? */);
+}
+
+void UIChooserAbstractModel::sltDetachCOM()
+{
+ /* Delete tree: */
+ delete m_pInvisibleRootNode;
+ m_pInvisibleRootNode = 0;
+}
+
+void UIChooserAbstractModel::sltCloudMachineUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QUuid &uId)
+{
+ /* Search for profile node: */
+ UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
+ if (!pProfileNode)
+ return;
+
+ /* Remove machine-item with passed uId: */
+ pProfileNode->removeAllNodes(uId);
+
+ /* If there are no items left => add fake cloud VM node: */
+ if (pProfileNode->nodes(UIChooserNodeType_Machine).isEmpty())
+ createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Done);
+}
+
+void UIChooserAbstractModel::sltCloudMachinesUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QList<QUuid> &ids)
+{
+ /* Search for profile node: */
+ UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
+ if (!pProfileNode)
+ return;
+
+ /* Remove machine-items with passed id: */
+ foreach (const QUuid &uId, ids)
+ pProfileNode->removeAllNodes(uId);
+
+ /* If there are no items left => add fake cloud VM node: */
+ if (pProfileNode->nodes(UIChooserNodeType_Machine).isEmpty())
+ createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Done);
+}
+
+void UIChooserAbstractModel::sltCloudMachineRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine)
+{
+ /* Search for profile node: */
+ UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
+ if (!pProfileNode)
+ return;
+
+ /* Compose corresponding group path: */
+ const QString strGroup = QString("/%1/%2").arg(strProviderShortName, strProfileName);
+ /* Make sure there is no VM with such ID already: */
+ QUuid uId;
+ if (!cloudMachineId(comMachine, uId))
+ return;
+ if (checkIfNodeContainChildWithId(pProfileNode, uId))
+ return;
+ /* Add new machine-item: */
+ addCloudMachineIntoTheTree(strGroup, comMachine, true /* make it visible? */);
+
+ /* Delete fake node if present: */
+ delete searchFakeNode(pProfileNode);
+}
+
+void UIChooserAbstractModel::sltCloudMachinesRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QVector<CCloudMachine> &machines)
+{
+ /* Search for profile node: */
+ UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
+ if (!pProfileNode)
+ return;
+
+ /* Compose corresponding group path: */
+ const QString strGroup = QString("/%1/%2").arg(strProviderShortName, strProfileName);
+ foreach (const CCloudMachine &comMachine, machines)
+ {
+ /* Make sure there is no VM with such ID already: */
+ QUuid uId;
+ if (!cloudMachineId(comMachine, uId))
+ continue;
+ if (checkIfNodeContainChildWithId(pProfileNode, uId))
+ continue;
+ /* Add new machine-item: */
+ addCloudMachineIntoTheTree(strGroup, comMachine, false /* make it visible? */);
+ }
+
+ /* Delete fake node if present: */
+ delete searchFakeNode(pProfileNode);
+}
+
+void UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete()
+{
+ /* Parse task result: */
+ UIProgressTaskReadCloudMachineList *pSender = qobject_cast<UIProgressTaskReadCloudMachineList*>(sender());
+ AssertPtrReturnVoid(pSender);
+ const UICloudEntityKey guiCloudProfileKey = pSender->cloudProfileKey();
+ const QVector<CCloudMachine> machines = pSender->machines();
+ const QString strErrorMessage = pSender->errorMessage();
+
+ /* Delete task: */
+ delete pSender;
+
+ /* Check whether this task was expected: */
+ if (!containsCloudEntityKey(guiCloudProfileKey))
+ return;
+
+ /* Search for provider node separately, it can be removed already: */
+ UIChooserNode *pProviderNode = searchProviderNode(guiCloudProfileKey.m_strProviderShortName);
+ if (pProviderNode)
+ {
+ /* Search for profile node separately, it can be hidden at all: */
+ UIChooserNode *pProfileNode = searchProfileNode(pProviderNode, guiCloudProfileKey.m_strProfileName);
+ if (pProfileNode)
+ {
+ /* Compose old set of machine IDs: */
+ QSet<QUuid> oldIDs;
+ foreach (UIChooserNode *pNode, pProfileNode->nodes(UIChooserNodeType_Machine))
+ {
+ AssertPtrReturnVoid(pNode);
+ UIChooserNodeMachine *pNodeMachine = pNode->toMachineNode();
+ AssertPtrReturnVoid(pNodeMachine);
+ if (pNodeMachine->cacheType() != UIVirtualMachineItemType_CloudReal)
+ continue;
+ oldIDs << pNodeMachine->id();
+ }
+ /* Compose new set of machine IDs and map of machines: */
+ QSet<QUuid> newIDs;
+ QMap<QUuid, CCloudMachine> newMachines;
+ foreach (const CCloudMachine &comMachine, machines)
+ {
+ QUuid uId;
+ AssertReturnVoid(cloudMachineId(comMachine, uId));
+ newMachines[uId] = comMachine;
+ newIDs << uId;
+ }
+
+ /* Calculate set of unregistered/registered IDs: */
+ const QSet<QUuid> unregisteredIDs = oldIDs - newIDs;
+ const QSet<QUuid> registeredIDs = newIDs - oldIDs;
+ QVector<CCloudMachine> registeredMachines;
+ foreach (const QUuid &uId, registeredIDs)
+ registeredMachines << newMachines.value(uId);
+
+ /* Remove unregistered cloud VM nodes: */
+ if (!unregisteredIDs.isEmpty())
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QList<QUuid> listUnregisteredIDs(unregisteredIDs.begin(), unregisteredIDs.end());
+#else
+ QList<QUuid> listUnregisteredIDs = unregisteredIDs.toList();
+#endif
+ sltCloudMachinesUnregistered(guiCloudProfileKey.m_strProviderShortName,
+ guiCloudProfileKey.m_strProfileName,
+ listUnregisteredIDs);
+ }
+ /* Add registered cloud VM nodes: */
+ if (!registeredMachines.isEmpty())
+ sltCloudMachinesRegistered(guiCloudProfileKey.m_strProviderShortName,
+ guiCloudProfileKey.m_strProfileName,
+ registeredMachines);
+ /* If we changed nothing and have nothing currently: */
+ if (unregisteredIDs.isEmpty() && newIDs.isEmpty())
+ {
+ /* We should update at least fake cloud machine node: */
+ UIChooserNode *pFakeNode = searchFakeNode(pProfileNode);
+ AssertPtrReturnVoid(pFakeNode);
+ UIVirtualMachineItemCloud *pFakeMachineItem = pFakeNode->toMachineNode()->cache()->toCloud();
+ AssertPtrReturnVoid(pFakeMachineItem);
+ pFakeMachineItem->setFakeCloudItemState(UIFakeCloudVirtualMachineItemState_Done);
+ pFakeMachineItem->setFakeCloudItemErrorMessage(strErrorMessage);
+ if (pFakeNode->item())
+ pFakeNode->item()->updateItem();
+ }
+ }
+ }
+
+ /* Remove cloud entity key from the list of keys currently being updated: */
+ removeCloudEntityKey(guiCloudProfileKey);
+}
+
+void UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange()
+{
+ /* Reload cloud tree: */
+ reloadCloudTree();
+}
+
+void UIChooserAbstractModel::createReadCloudMachineListTask(const UICloudEntityKey &guiCloudProfileKey, bool fWithRefresh)
+{
+ /* Do not create task if already registered: */
+ if (containsCloudEntityKey(guiCloudProfileKey))
+ return;
+
+ /* Create task: */
+ UIProgressTaskReadCloudMachineList *pTask = new UIProgressTaskReadCloudMachineList(this,
+ guiCloudProfileKey,
+ fWithRefresh);
+ if (pTask)
+ {
+ /* It's easy to find child by name later: */
+ pTask->setObjectName(guiCloudProfileKey.toString());
+
+ /* Insert cloud profile key into a list of keys currently being updated: */
+ insertCloudEntityKey(guiCloudProfileKey);
+
+ /* Connect and start it finally: */
+ connect(pTask, &UIProgressTaskReadCloudMachineList::sigProgressFinished,
+ this, &UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete);
+ pTask->start();
+ }
+}
+
+void UIChooserAbstractModel::sltSaveSettings()
+{
+ saveGroupSettings();
+ saveGroupDefinitions();
+}
+
+void UIChooserAbstractModel::prepare()
+{
+ prepareConnections();
+}
+
+void UIChooserAbstractModel::prepareConnections()
+{
+ /* UICommon connections: */
+ connect(&uiCommon(), &UICommon::sigAskToCommitData,
+ this, &UIChooserAbstractModel::sltCommitData);
+ connect(&uiCommon(), &UICommon::sigAskToDetachCOM,
+ this, &UIChooserAbstractModel::sltDetachCOM);
+ connect(&uiCommon(), &UICommon::sigCloudMachineUnregistered,
+ this, &UIChooserAbstractModel::sltCloudMachineUnregistered);
+ connect(&uiCommon(), &UICommon::sigCloudMachineRegistered,
+ this, &UIChooserAbstractModel::sltCloudMachineRegistered);
+
+ /* Global connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UIChooserAbstractModel::sltLocalMachineStateChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange,
+ this, &UIChooserAbstractModel::sltLocalMachineDataChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineRegistered,
+ this, &UIChooserAbstractModel::sltLocalMachineRegistrationChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineGroupsChange,
+ this, &UIChooserAbstractModel::sltLocalMachineGroupsChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSessionStateChange,
+ this, &UIChooserAbstractModel::sltSessionStateChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotTake,
+ this, &UIChooserAbstractModel::sltSnapshotChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotDelete,
+ this, &UIChooserAbstractModel::sltSnapshotChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotChange,
+ this, &UIChooserAbstractModel::sltSnapshotChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotRestore,
+ this, &UIChooserAbstractModel::sltSnapshotChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProviderListChanged,
+ this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProviderUninstall,
+ this, &UIChooserAbstractModel::sltHandleCloudProviderUninstall);
+
+ /* Settings saving connections: */
+ connect(this, &UIChooserAbstractModel::sigSaveSettings,
+ this, &UIChooserAbstractModel::sltSaveSettings,
+ Qt::QueuedConnection);
+
+ /* Extra-data connections: */
+ connect(gEDataManager, &UIExtraDataManager::sigCloudProfileManagerRestrictionChange,
+ this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
+}
+
+void UIChooserAbstractModel::cleanupConnections()
+{
+ /* Group saving connections: */
+ disconnect(this, &UIChooserAbstractModel::sigSaveSettings,
+ this, &UIChooserAbstractModel::sltSaveSettings);
+}
+
+void UIChooserAbstractModel::cleanup()
+{
+ cleanupConnections();
+}
+
+void UIChooserAbstractModel::reloadLocalTree()
+{
+ LogRelFlow(("UIChooserAbstractModel: Loading local VMs...\n"));
+
+ /* Acquire VBox: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Acquire existing local machines: */
+ const QVector<CMachine> machines = comVBox.GetMachines();
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotAcquireVirtualBoxParameter(comVBox);
+ else
+ {
+ /* Iterate through existing machines: */
+ foreach (const CMachine &comMachine, machines)
+ {
+ /* Skip if we have nothing to populate (wtf happened?): */
+ if (comMachine.isNull())
+ continue;
+
+ /* Get machine ID: */
+ const QUuid uMachineID = comMachine.GetId();
+ /* Show error message if necessary: */
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotAcquireMachineParameter(comMachine);
+ continue;
+ }
+
+ /* Skip if we have nothing to show (wtf happened?): */
+ if (uMachineID.isNull())
+ continue;
+
+ /* Skip if machine is restricted from being shown: */
+ if (!gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineID))
+ continue;
+
+ /* Add machine into tree: */
+ addLocalMachineIntoTheTree(comMachine);
+ }
+ }
+
+ LogRelFlow(("UIChooserAbstractModel: Local VMs loaded.\n"));
+}
+
+void UIChooserAbstractModel::reloadCloudTree()
+{
+ LogRelFlow(("UIChooserAbstractModel: Loading cloud providers/profiles...\n"));
+
+ /* Wipe out existing cloud providers first.
+ * This is quite rude, in future we need to reimplement it more wise.. */
+ foreach (UIChooserNode *pNode, invisibleRoot()->nodes(UIChooserNodeType_Group))
+ {
+ AssertPtrReturnVoid(pNode);
+ UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
+ AssertPtrReturnVoid(pGroupNode);
+ if (pGroupNode->groupType() == UIChooserNodeGroupType_Provider)
+ delete pNode;
+ }
+
+ /* Acquire Cloud Profile Manager restrictions: */
+ const QStringList restrictions = gEDataManager->cloudProfileManagerRestrictions();
+
+ /* Iterate through existing providers: */
+ foreach (CCloudProvider comCloudProvider, listCloudProviders())
+ {
+ /* Skip if we have nothing to populate: */
+ if (comCloudProvider.isNull())
+ continue;
+
+ /* Acquire provider id: */
+ QUuid uProviderId;
+ if (!cloudProviderId(comCloudProvider, uProviderId))
+ continue;
+
+ /* Acquire provider short name: */
+ QString strProviderShortName;
+ if (!cloudProviderShortName(comCloudProvider, strProviderShortName))
+ continue;
+
+ /* Make sure this provider isn't restricted: */
+ const QString strProviderPath = QString("/%1").arg(strProviderShortName);
+ if (restrictions.contains(strProviderPath))
+ continue;
+
+ /* Acquire list of profiles: */
+ const QVector<CCloudProfile> profiles = listCloudProfiles(comCloudProvider);
+ if (profiles.isEmpty())
+ continue;
+
+ /* Add provider group node: */
+ UIChooserNodeGroup *pProviderNode =
+ new UIChooserNodeGroup(invisibleRoot() /* parent */,
+ getDesiredNodePosition(invisibleRoot(),
+ UIChooserNodeDataPrefixType_Provider,
+ strProviderShortName),
+ uProviderId,
+ strProviderShortName,
+ UIChooserNodeGroupType_Provider,
+ shouldGroupNodeBeOpened(invisibleRoot(),
+ UIChooserNodeDataPrefixType_Provider,
+ strProviderShortName));
+
+ /* Iterate through provider's profiles: */
+ foreach (CCloudProfile comCloudProfile, profiles)
+ {
+ /* Skip if we have nothing to populate: */
+ if (comCloudProfile.isNull())
+ continue;
+
+ /* Acquire profile name: */
+ QString strProfileName;
+ if (!cloudProfileName(comCloudProfile, strProfileName))
+ continue;
+
+ /* Make sure this profile isn't restricted: */
+ const QString strProfilePath = QString("/%1/%2").arg(strProviderShortName, strProfileName);
+ if (restrictions.contains(strProfilePath))
+ continue;
+
+ /* Add profile sub-group node: */
+ UIChooserNodeGroup *pProfileNode =
+ new UIChooserNodeGroup(pProviderNode /* parent */,
+ getDesiredNodePosition(pProviderNode,
+ UIChooserNodeDataPrefixType_Profile,
+ strProfileName),
+ QUuid() /* id */,
+ strProfileName,
+ UIChooserNodeGroupType_Profile,
+ shouldGroupNodeBeOpened(pProviderNode,
+ UIChooserNodeDataPrefixType_Profile,
+ strProfileName));
+
+ /* Add fake cloud VM item: */
+ createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Loading);
+
+ /* Create read cloud machine list task: */
+ const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
+ createReadCloudMachineListTask(guiCloudProfileKey, true /* with refresh? */);
+ }
+ }
+
+ LogRelFlow(("UIChooserAbstractModel: Cloud providers/profiles loaded.\n"));
+}
+
+void UIChooserAbstractModel::addLocalMachineIntoTheTree(const CMachine &comMachine,
+ bool fMakeItVisible /* = false */)
+{
+ /* Make sure passed VM is not NULL: */
+ if (comMachine.isNull())
+ LogRelFlow(("UIChooserModel: ERROR: Passed local VM is NULL!\n"));
+ AssertReturnVoid(!comMachine.isNull());
+
+ /* Which VM we are loading: */
+ const QUuid uId = comMachine.GetId();
+ LogRelFlow(("UIChooserModel: Loading local VM with ID={%s}...\n",
+ toOldStyleUuid(uId).toUtf8().constData()));
+
+ /* Is that machine accessible? */
+ if (comMachine.GetAccessible())
+ {
+ /* Acquire VM name: */
+ const QString strName = comMachine.GetName();
+ LogRelFlow(("UIChooserModel: Local VM {%s} is accessible.\n", strName.toUtf8().constData()));
+ /* Which groups passed machine attached to? */
+ const QVector<QString> groups = comMachine.GetGroups();
+ const QStringList groupList = groups.toList();
+ const QString strGroups = groupList.join(", ");
+ LogRelFlow(("UIChooserModel: Local VM {%s} has groups: {%s}.\n",
+ strName.toUtf8().constData(), strGroups.toUtf8().constData()));
+ foreach (QString strGroup, groups)
+ {
+ /* Remove last '/' if any: */
+ if (strGroup.right(1) == "/")
+ strGroup.truncate(strGroup.size() - 1);
+ /* Create machine-item with found group-item as parent: */
+ LogRelFlow(("UIChooserModel: Creating node for local VM {%s} in group {%s}.\n",
+ strName.toUtf8().constData(), strGroup.toUtf8().constData()));
+ createLocalMachineNode(getLocalGroupNode(strGroup, invisibleRoot(), fMakeItVisible), comMachine);
+ }
+ /* Update group settings: */
+ m_groups[toOldStyleUuid(uId)] = groupList;
+ }
+ /* Inaccessible machine: */
+ else
+ {
+ /* VM is accessible: */
+ LogRelFlow(("UIChooserModel: Local VM {%s} is inaccessible.\n",
+ toOldStyleUuid(uId).toUtf8().constData()));
+ /* Create machine-item with main-root group-item as parent: */
+ createLocalMachineNode(invisibleRoot(), comMachine);
+ }
+}
+
+void UIChooserAbstractModel::addCloudMachineIntoTheTree(const QString &strGroup,
+ const CCloudMachine &comMachine,
+ bool fMakeItVisible /* = false */)
+{
+ /* Make sure passed VM is not NULL: */
+ if (comMachine.isNull())
+ LogRelFlow(("UIChooserModel: ERROR: Passed cloud VM is NULL!\n"));
+ AssertReturnVoid(!comMachine.isNull());
+
+ /* Which VM we are loading: */
+ const QUuid uId = comMachine.GetId();
+ LogRelFlow(("UIChooserModel: Loading cloud VM with ID={%s}...\n",
+ toOldStyleUuid(uId).toUtf8().constData()));
+
+ /* Acquire VM name: */
+ QString strName = comMachine.GetName();
+ if (strName.isEmpty())
+ strName = uId.toString();
+ LogRelFlow(("UIChooserModel: Creating node for cloud VM {%s} in group {%s}.\n",
+ strName.toUtf8().constData(), strGroup.toUtf8().constData()));
+ /* Create machine-item with found group-item as parent: */
+ createCloudMachineNode(getCloudGroupNode(strGroup, invisibleRoot(), fMakeItVisible), comMachine);
+ /* Update group settings: */
+ const QStringList groupList(strGroup);
+ m_groups[toOldStyleUuid(uId)] = groupList;
+}
+
+UIChooserNode *UIChooserAbstractModel::getLocalGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened)
+{
+ /* Check passed stuff: */
+ if (pParentNode->name() == strName)
+ return pParentNode;
+
+ /* Prepare variables: */
+ const QString strFirstSubName = strName.section('/', 0, 0);
+ const QString strFirstSuffix = strName.section('/', 1, -1);
+ const QString strSecondSubName = strFirstSuffix.section('/', 0, 0);
+ const QString strSecondSuffix = strFirstSuffix.section('/', 1, -1);
+
+ /* Passed group name equal to first sub-name: */
+ if (pParentNode->name() == strFirstSubName)
+ {
+ /* Make sure first-suffix is NOT empty: */
+ AssertMsg(!strFirstSuffix.isEmpty(), ("Invalid group name!"));
+ /* Trying to get group node among our children: */
+ foreach (UIChooserNode *pNode, pParentNode->nodes(UIChooserNodeType_Group))
+ {
+ AssertPtrReturn(pNode, 0);
+ UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
+ AssertPtrReturn(pGroupNode, 0);
+ if ( pGroupNode->groupType() == UIChooserNodeGroupType_Local
+ && pNode->name() == strSecondSubName)
+ {
+ UIChooserNode *pFoundNode = getLocalGroupNode(strFirstSuffix, pNode, fAllGroupsOpened);
+ if (UIChooserNodeGroup *pFoundGroupNode = pFoundNode->toGroupNode())
+ if (fAllGroupsOpened && pFoundGroupNode->isClosed())
+ pFoundGroupNode->open();
+ return pFoundNode;
+ }
+ }
+ }
+
+ /* Found nothing? Creating: */
+ UIChooserNodeGroup *pNewGroupNode =
+ new UIChooserNodeGroup(pParentNode,
+ getDesiredNodePosition(pParentNode,
+ UIChooserNodeDataPrefixType_Local,
+ strSecondSubName),
+ QUuid() /* id */,
+ strSecondSubName,
+ UIChooserNodeGroupType_Local,
+ fAllGroupsOpened || shouldGroupNodeBeOpened(pParentNode,
+ UIChooserNodeDataPrefixType_Local,
+ strSecondSubName));
+ return strSecondSuffix.isEmpty() ? pNewGroupNode : getLocalGroupNode(strFirstSuffix, pNewGroupNode, fAllGroupsOpened);
+}
+
+UIChooserNode *UIChooserAbstractModel::getCloudGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened)
+{
+ /* Check passed stuff: */
+ if (pParentNode->name() == strName)
+ return pParentNode;
+
+ /* Prepare variables: */
+ const QString strFirstSubName = strName.section('/', 0, 0);
+ const QString strFirstSuffix = strName.section('/', 1, -1);
+ const QString strSecondSubName = strFirstSuffix.section('/', 0, 0);
+
+ /* Passed group name equal to first sub-name: */
+ if (pParentNode->name() == strFirstSubName)
+ {
+ /* Make sure first-suffix is NOT empty: */
+ AssertMsg(!strFirstSuffix.isEmpty(), ("Invalid group name!"));
+ /* Trying to get group node among our children: */
+ foreach (UIChooserNode *pNode, pParentNode->nodes(UIChooserNodeType_Group))
+ {
+ AssertPtrReturn(pNode, 0);
+ UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
+ AssertPtrReturn(pGroupNode, 0);
+ if ( ( pGroupNode->groupType() == UIChooserNodeGroupType_Provider
+ || pGroupNode->groupType() == UIChooserNodeGroupType_Profile)
+ && pNode->name() == strSecondSubName)
+ {
+ UIChooserNode *pFoundNode = getCloudGroupNode(strFirstSuffix, pNode, fAllGroupsOpened);
+ if (UIChooserNodeGroup *pFoundGroupNode = pFoundNode->toGroupNode())
+ if (fAllGroupsOpened && pFoundGroupNode->isClosed())
+ pFoundGroupNode->open();
+ return pFoundNode;
+ }
+ }
+ }
+
+ /* Found nothing? Returning parent: */
+ AssertFailedReturn(pParentNode);
+}
+
+bool UIChooserAbstractModel::shouldGroupNodeBeOpened(UIChooserNode *pParentNode,
+ UIChooserNodeDataPrefixType enmDataType,
+ const QString &strName) const
+{
+ /* Read group definitions: */
+ const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
+ /* Return 'false' if no definitions found: */
+ if (definitions.isEmpty())
+ return false;
+
+ /* Prepare required group definition reg-exp: */
+ const QString strNodePrefix = prefixToString(enmDataType);
+ const QString strNodeOptionOpened = optionToString(UIChooserNodeDataOptionType_GroupOpened);
+ const QString strDefinitionTemplate = QString("%1(\\S)*=%2").arg(strNodePrefix, strName);
+ const QRegExp definitionRegExp(strDefinitionTemplate);
+ /* For each the group definition: */
+ foreach (const QString &strDefinition, definitions)
+ {
+ /* Check if this is required definition: */
+ if (definitionRegExp.indexIn(strDefinition) == 0)
+ {
+ /* Get group descriptor: */
+ const QString strDescriptor(definitionRegExp.cap(1));
+ if (strDescriptor.contains(strNodeOptionOpened))
+ return true;
+ }
+ }
+
+ /* Return 'false' by default: */
+ return false;
+}
+
+bool UIChooserAbstractModel::shouldGlobalNodeBeFavorite(UIChooserNode *pParentNode) const
+{
+ /* Read group definitions: */
+ const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
+ /* Return 'false' if no definitions found: */
+ if (definitions.isEmpty())
+ return false;
+
+ /* Prepare required group definition reg-exp: */
+ const QString strNodePrefix = prefixToString(UIChooserNodeDataPrefixType_Global);
+ const QString strNodeOptionFavorite = optionToString(UIChooserNodeDataOptionType_GlobalFavorite);
+ const QString strNodeValueDefault = valueToString(UIChooserNodeDataValueType_GlobalDefault);
+ const QString strDefinitionTemplate = QString("%1(\\S)*=%2").arg(strNodePrefix, strNodeValueDefault);
+ const QRegExp definitionRegExp(strDefinitionTemplate);
+ /* For each the group definition: */
+ foreach (const QString &strDefinition, definitions)
+ {
+ /* Check if this is required definition: */
+ if (definitionRegExp.indexIn(strDefinition) == 0)
+ {
+ /* Get group descriptor: */
+ const QString strDescriptor(definitionRegExp.cap(1));
+ if (strDescriptor.contains(strNodeOptionFavorite))
+ return true;
+ }
+ }
+
+ /* Return 'false' by default: */
+ return false;
+}
+
+void UIChooserAbstractModel::wipeOutEmptyGroupsStartingFrom(UIChooserNode *pParent)
+{
+ /* Cleanup all the group children recursively first: */
+ foreach (UIChooserNode *pNode, pParent->nodes(UIChooserNodeType_Group))
+ wipeOutEmptyGroupsStartingFrom(pNode);
+ /* If parent isn't root and has no nodes: */
+ if (!pParent->isRoot() && !pParent->hasNodes())
+ {
+ /* Delete parent node and item: */
+ delete pParent;
+ }
+}
+
+int UIChooserAbstractModel::getDesiredNodePosition(UIChooserNode *pParentNode,
+ UIChooserNodeDataPrefixType enmDataType,
+ const QString &strName)
+{
+ /* End of list (by default)? */
+ int iNewNodeDesiredPosition = -1;
+ /* Which position should be new node placed by definitions: */
+ const int iNewNodeDefinitionPosition = getDefinedNodePosition(pParentNode, enmDataType, strName);
+
+ /* If some position defined: */
+ if (iNewNodeDefinitionPosition != -1)
+ {
+ /* Start of list if some definition present: */
+ iNewNodeDesiredPosition = 0;
+ /* We have to check all the existing node positions: */
+ UIChooserNodeType enmType = UIChooserNodeType_Any;
+ switch (enmDataType)
+ {
+ case UIChooserNodeDataPrefixType_Global: enmType = UIChooserNodeType_Global; break;
+ case UIChooserNodeDataPrefixType_Machine: enmType = UIChooserNodeType_Machine; break;
+ case UIChooserNodeDataPrefixType_Local:
+ case UIChooserNodeDataPrefixType_Provider:
+ case UIChooserNodeDataPrefixType_Profile: enmType = UIChooserNodeType_Group; break;
+ }
+ const QList<UIChooserNode*> nodes = pParentNode->nodes(enmType);
+ for (int i = nodes.size() - 1; i >= 0; --i)
+ {
+ /* Get current node: */
+ UIChooserNode *pNode = nodes.at(i);
+ AssertPtrReturn(pNode, iNewNodeDesiredPosition);
+ /* Which position should be current node placed by definitions? */
+ UIChooserNodeDataPrefixType enmNodeDataType = UIChooserNodeDataPrefixType_Global;
+ QString strDefinitionName;
+ switch (pNode->type())
+ {
+ case UIChooserNodeType_Machine:
+ {
+ enmNodeDataType = UIChooserNodeDataPrefixType_Machine;
+ strDefinitionName = toOldStyleUuid(pNode->toMachineNode()->id());
+ break;
+ }
+ case UIChooserNodeType_Group:
+ {
+ /* Cast to group node: */
+ UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
+ AssertPtrReturn(pGroupNode, iNewNodeDesiredPosition);
+ switch (pGroupNode->groupType())
+ {
+ case UIChooserNodeGroupType_Local: enmNodeDataType = UIChooserNodeDataPrefixType_Local; break;
+ case UIChooserNodeGroupType_Provider: enmNodeDataType = UIChooserNodeDataPrefixType_Provider; break;
+ case UIChooserNodeGroupType_Profile: enmNodeDataType = UIChooserNodeDataPrefixType_Profile; break;
+ default: break;
+ }
+ strDefinitionName = pNode->name();
+ break;
+ }
+ default:
+ break;
+ }
+ /* If some position defined: */
+ const int iNodeDefinitionPosition = getDefinedNodePosition(pParentNode, enmNodeDataType, strDefinitionName);
+ if (iNodeDefinitionPosition != -1)
+ {
+ AssertReturn(iNodeDefinitionPosition != iNewNodeDefinitionPosition, iNewNodeDesiredPosition);
+ if (iNodeDefinitionPosition < iNewNodeDefinitionPosition)
+ {
+ iNewNodeDesiredPosition = i + 1;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Return desired node position: */
+ return iNewNodeDesiredPosition;
+}
+
+int UIChooserAbstractModel::getDefinedNodePosition(UIChooserNode *pParentNode, UIChooserNodeDataPrefixType enmDataType, const QString &strName)
+{
+ /* Read group definitions: */
+ const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
+ /* Return 'false' if no definitions found: */
+ if (definitions.isEmpty())
+ return -1;
+
+ /* Prepare definition reg-exp: */
+ QString strDefinitionTemplateShort;
+ QString strDefinitionTemplateFull;
+ const QString strNodePrefixLocal = prefixToString(UIChooserNodeDataPrefixType_Local);
+ const QString strNodePrefixProvider = prefixToString(UIChooserNodeDataPrefixType_Provider);
+ const QString strNodePrefixProfile = prefixToString(UIChooserNodeDataPrefixType_Profile);
+ const QString strNodePrefixMachine = prefixToString(UIChooserNodeDataPrefixType_Machine);
+ switch (enmDataType)
+ {
+ case UIChooserNodeDataPrefixType_Local:
+ {
+ strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
+ strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixLocal, strName);
+ break;
+ }
+ case UIChooserNodeDataPrefixType_Provider:
+ {
+ strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
+ strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixProvider, strName);
+ break;
+ }
+ case UIChooserNodeDataPrefixType_Profile:
+ {
+ strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
+ strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixProfile, strName);
+ break;
+ }
+ case UIChooserNodeDataPrefixType_Machine:
+ {
+ strDefinitionTemplateShort = QString("^%1=").arg(strNodePrefixMachine);
+ strDefinitionTemplateFull = QString("^%1=%2$").arg(strNodePrefixMachine, strName);
+ break;
+ }
+ default:
+ return -1;
+ }
+ QRegExp definitionRegExpShort(strDefinitionTemplateShort);
+ QRegExp definitionRegExpFull(strDefinitionTemplateFull);
+
+ /* For each the definition: */
+ int iDefinitionIndex = -1;
+ foreach (const QString &strDefinition, definitions)
+ {
+ /* Check if this definition is of required type: */
+ if (definitionRegExpShort.indexIn(strDefinition) == 0)
+ {
+ ++iDefinitionIndex;
+ /* Check if this definition is exactly what we need: */
+ if (definitionRegExpFull.indexIn(strDefinition) == 0)
+ return iDefinitionIndex;
+ }
+ }
+
+ /* Return result: */
+ return -1;
+}
+
+void UIChooserAbstractModel::createLocalMachineNode(UIChooserNode *pParentNode, const CMachine &comMachine)
+{
+ new UIChooserNodeMachine(pParentNode,
+ getDesiredNodePosition(pParentNode,
+ UIChooserNodeDataPrefixType_Machine,
+ toOldStyleUuid(comMachine.GetId())),
+ comMachine);
+}
+
+void UIChooserAbstractModel::createCloudMachineNode(UIChooserNode *pParentNode, UIFakeCloudVirtualMachineItemState enmState)
+{
+ new UIChooserNodeMachine(pParentNode,
+ 0 /* position */,
+ enmState);
+}
+
+void UIChooserAbstractModel::createCloudMachineNode(UIChooserNode *pParentNode, const CCloudMachine &comMachine)
+{
+ UIChooserNodeMachine *pNode = new UIChooserNodeMachine(pParentNode,
+ getDesiredNodePosition(pParentNode,
+ UIChooserNodeDataPrefixType_Machine,
+ toOldStyleUuid(comMachine.GetId())),
+ comMachine);
+ /* Request for async node update if necessary: */
+ if (!comMachine.GetAccessible())
+ {
+ AssertReturnVoid(pNode && pNode->cacheType() == UIVirtualMachineItemType_CloudReal);
+ pNode->cache()->toCloud()->updateInfoAsync(false /* delayed? */);
+ }
+}
+
+QStringList UIChooserAbstractModel::gatherPossibleGroupNodeNames(UIChooserNode *pCurrentNode, QList<UIChooserNode*> exceptions) const
+{
+ /* Prepare result: */
+ QStringList result;
+
+ /* Walk through all the children and make sure there are no exceptions: */
+ bool fAddCurrent = true;
+ foreach (UIChooserNode *pChild, pCurrentNode->nodes(UIChooserNodeType_Any))
+ {
+ AssertPtrReturn(pChild, result);
+ if (exceptions.contains(pChild))
+ fAddCurrent = false;
+ else
+ {
+ if (pChild->type() == UIChooserNodeType_Group)
+ {
+ UIChooserNodeGroup *pChildGroup = pChild->toGroupNode();
+ AssertPtrReturn(pChildGroup, result);
+ if (pChildGroup->groupType() == UIChooserNodeGroupType_Local)
+ result << gatherPossibleGroupNodeNames(pChild, exceptions);
+ }
+ }
+ }
+
+ /* Add current item if not overridden: */
+ if (fAddCurrent)
+ result.prepend(pCurrentNode->fullName());
+
+ /* Return result: */
+ return result;
+}
+
+bool UIChooserAbstractModel::checkIfNodeContainChildWithId(UIChooserNode *pParentNode, const QUuid &uId) const
+{
+ /* Check parent-node type: */
+ AssertPtrReturn(pParentNode, false);
+ switch (pParentNode->type())
+ {
+ case UIChooserNodeType_Machine:
+ {
+ /* Check if pParentNode has the passed uId itself: */
+ UIChooserNodeMachine *pMachineNode = pParentNode->toMachineNode();
+ AssertPtrReturn(pMachineNode, false);
+ if (pMachineNode->id() == uId)
+ return true;
+ break;
+ }
+ case UIChooserNodeType_Group:
+ {
+ /* Recursively iterate through children: */
+ foreach (UIChooserNode *pChildNode, pParentNode->nodes())
+ if (checkIfNodeContainChildWithId(pChildNode, uId))
+ return true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* False by default: */
+ return false;
+}
+
+void UIChooserAbstractModel::saveGroupSettings()
+{
+ /* Make sure there is no group settings saving activity: */
+ if (UIThreadGroupSettingsSave::instance())
+ return;
+
+ /* Prepare full group map: */
+ QMap<QString, QStringList> groups;
+ gatherGroupSettings(groups, invisibleRoot());
+
+ /* Save information in other thread: */
+ UIThreadGroupSettingsSave::prepare();
+ emit sigGroupSavingStateChanged();
+ connect(UIThreadGroupSettingsSave::instance(), &UIThreadGroupSettingsSave::sigReload,
+ this, &UIChooserAbstractModel::sltReloadMachine);
+ UIThreadGroupSettingsSave::instance()->configure(this, m_groups, groups);
+ UIThreadGroupSettingsSave::instance()->start();
+ m_groups = groups;
+}
+
+void UIChooserAbstractModel::saveGroupDefinitions()
+{
+ /* Make sure there is no group definitions save activity: */
+ if (UIThreadGroupDefinitionsSave::instance())
+ return;
+
+ /* Prepare full group map: */
+ QMap<QString, QStringList> groups;
+ gatherGroupDefinitions(groups, invisibleRoot());
+
+ /* Save information in other thread: */
+ UIThreadGroupDefinitionsSave::prepare();
+ emit sigGroupSavingStateChanged();
+ UIThreadGroupDefinitionsSave::instance()->configure(this, groups);
+ UIThreadGroupDefinitionsSave::instance()->start();
+}
+
+void UIChooserAbstractModel::gatherGroupSettings(QMap<QString, QStringList> &settings,
+ UIChooserNode *pParentGroup)
+{
+ /* Iterate over all the machine-nodes: */
+ foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Machine))
+ {
+ /* Make sure it's really machine node: */
+ AssertPtrReturnVoid(pNode);
+ UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
+ AssertPtrReturnVoid(pMachineNode);
+ /* Make sure it's local machine node exactly and it's accessible: */
+ if ( pMachineNode->cacheType() == UIVirtualMachineItemType_Local
+ && pMachineNode->accessible())
+ settings[toOldStyleUuid(pMachineNode->id())] << pParentGroup->fullName();
+ }
+ /* Iterate over all the group-nodes: */
+ foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Group))
+ gatherGroupSettings(settings, pNode);
+}
+
+void UIChooserAbstractModel::gatherGroupDefinitions(QMap<QString, QStringList> &definitions,
+ UIChooserNode *pParentGroup)
+{
+ /* Prepare extra-data key for current group: */
+ const QString strExtraDataKey = pParentGroup->fullName();
+ /* Iterate over all the global-nodes: */
+ foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Global))
+ {
+ /* Append node definition: */
+ AssertPtrReturnVoid(pNode);
+ definitions[strExtraDataKey] << pNode->definition(true /* full */);
+ }
+ /* Iterate over all the group-nodes: */
+ foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Group))
+ {
+ /* Append node definition: */
+ AssertPtrReturnVoid(pNode);
+ definitions[strExtraDataKey] << pNode->definition(true /* full */);
+ /* Go recursively through children: */
+ gatherGroupDefinitions(definitions, pNode);
+ }
+ /* Iterate over all the machine-nodes: */
+ foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Machine))
+ {
+ /* Make sure it's really machine node: */
+ AssertPtrReturnVoid(pNode);
+ UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
+ AssertPtrReturnVoid(pMachineNode);
+ /* Append node definition, make sure it's local or real cloud machine node only: */
+ if ( pMachineNode->cacheType() == UIVirtualMachineItemType_Local
+ || pMachineNode->cacheType() == UIVirtualMachineItemType_CloudReal)
+ definitions[strExtraDataKey] << pNode->definition(true /* full */);
+ }
+}
+
+void UIChooserAbstractModel::makeSureGroupSettingsSaveIsFinished()
+{
+ /* Cleanup if necessary: */
+ if (UIThreadGroupSettingsSave::instance())
+ UIThreadGroupSettingsSave::cleanup();
+}
+
+void UIChooserAbstractModel::makeSureGroupDefinitionsSaveIsFinished()
+{
+ /* Cleanup if necessary: */
+ if (UIThreadGroupDefinitionsSave::instance())
+ UIThreadGroupDefinitionsSave::cleanup();
+}
+
+UIChooserNode *UIChooserAbstractModel::searchProviderNode(const QUuid &uProviderId)
+{
+ /* Search for a list of nodes matching passed name: */
+ QList<UIChooserNode*> providerNodes;
+ invisibleRoot()->searchForNodes(uProviderId.toString(),
+ UIChooserItemSearchFlag_CloudProvider | UIChooserItemSearchFlag_ExactId,
+ providerNodes);
+
+ /* Return 1st node if any: */
+ return providerNodes.value(0);
+}
+
+UIChooserNode *UIChooserAbstractModel::searchProviderNode(const QString &strProviderShortName)
+{
+ /* Search for a list of nodes matching passed name: */
+ QList<UIChooserNode*> providerNodes;
+ invisibleRoot()->searchForNodes(strProviderShortName,
+ UIChooserItemSearchFlag_CloudProvider | UIChooserItemSearchFlag_ExactName,
+ providerNodes);
+
+ /* Return 1st node if any: */
+ return providerNodes.value(0);
+}
+
+UIChooserNode *UIChooserAbstractModel::searchProfileNode(UIChooserNode *pProviderNode, const QString &strProfileName)
+{
+ AssertPtrReturn(pProviderNode, 0);
+
+ /* Search for a list of nodes matching passed name: */
+ QList<UIChooserNode*> profileNodes;
+ pProviderNode->searchForNodes(strProfileName,
+ UIChooserItemSearchFlag_CloudProfile | UIChooserItemSearchFlag_ExactName,
+ profileNodes);
+
+ /* Return 1st node if any: */
+ return profileNodes.value(0);
+}
+
+UIChooserNode *UIChooserAbstractModel::searchProfileNode(const QString &strProviderShortName, const QString &strProfileName)
+{
+ /* Wrap method above: */
+ return searchProfileNode(searchProviderNode(strProviderShortName), strProfileName);
+}
+
+UIChooserNode *UIChooserAbstractModel::searchMachineNode(UIChooserNode *pProfileNode, const QUuid &uMachineId)
+{
+ AssertPtrReturn(pProfileNode, 0);
+
+ /* Search for a list of nodes matching passed ID: */
+ QList<UIChooserNode*> machineNodes;
+ pProfileNode->searchForNodes(uMachineId.toString(),
+ UIChooserItemSearchFlag_Machine | UIChooserItemSearchFlag_ExactId,
+ machineNodes);
+
+ /* Return 1st node if any: */
+ return machineNodes.value(0);
+}
+
+UIChooserNode *UIChooserAbstractModel::searchMachineNode(const QString &strProviderShortName, const QString &strProfileName, const QUuid &uMachineId)
+{
+ /* Wrap method above: */
+ return searchMachineNode(searchProfileNode(strProviderShortName, strProfileName), uMachineId);
+}
+
+UIChooserNode *UIChooserAbstractModel::searchFakeNode(UIChooserNode *pProfileNode)
+{
+ /* Wrap method above: */
+ return searchMachineNode(pProfileNode, QUuid());
+}
+
+UIChooserNode *UIChooserAbstractModel::searchFakeNode(const QString &strProviderShortName, const QString &strProfileName)
+{
+ /* Wrap method above: */
+ return searchMachineNode(strProviderShortName, strProfileName, QUuid());
+}
+
+void UIChooserAbstractModel::stopCloudUpdates(bool fForced /* = false */)
+{
+ /* Stop all cloud entity updates currently being performed: */
+ foreach (const UICloudEntityKey &key, m_cloudEntityKeysBeingUpdated)
+ {
+ /* For profiles: */
+ if (key.m_uMachineId.isNull())
+ {
+ /* Search task child by key: */
+ UIProgressTaskReadCloudMachineList *pTask = findChild<UIProgressTaskReadCloudMachineList*>(key.toString());
+ AssertPtrReturnVoid(pTask);
+
+ /* Wait for cloud profile refresh task to complete,
+ * then delete the task itself manually: */
+ if (!fForced)
+ pTask->cancel();
+ delete pTask;
+ }
+ /* For machines: */
+ else
+ {
+ /* Search machine node: */
+ UIChooserNode *pNode = searchMachineNode(key.m_strProviderShortName, key.m_strProfileName, key.m_uMachineId);
+ AssertPtrReturnVoid(pNode);
+ /* Acquire cloud machine item: */
+ UIVirtualMachineItemCloud *pCloudMachineItem = pNode->toMachineNode()->cache()->toCloud();
+ AssertPtrReturnVoid(pCloudMachineItem);
+
+ /* Wait for cloud machine refresh task to complete,
+ * task itself will be deleted with the machine-node: */
+ pCloudMachineItem->waitForAsyncInfoUpdateFinished();
+ }
+ }
+
+ /* We haven't let tasks to unregister themselves
+ * so we have to cleanup task set ourselves: */
+ m_cloudEntityKeysBeingUpdated.clear();
+}
+
+
+#include "UIChooserAbstractModel.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.h
new file mode 100644
index 00000000..72336489
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.h
@@ -0,0 +1,407 @@
+/* $Id: UIChooserAbstractModel.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserAbstractModel class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserAbstractModel_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserAbstractModel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSet>
+#include <QUuid>
+
+/* GUI includes: */
+#include "UIChooserDefs.h"
+#include "UICloudEntityKey.h"
+#include "UIManagerDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudMachine.h" /* required for Qt6 / c++17 */
+
+/* Forward declaration: */
+class UIChooser;
+class UIChooserNode;
+class CMachine;
+
+/** QObject extension used as VM Chooser-pane abstract model.
+ * This class is used to load/save a tree of abstract invisible
+ * nodes representing VMs and their groups from/to extra-data. */
+class UIChooserAbstractModel : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name Cloud machine stuff.
+ * @{ */
+ /** Notifies listeners about state change for cloud machine with certain @a uId. */
+ void sigCloudMachineStateChange(const QUuid &uId);
+ /** @} */
+
+ /** @name Group saving stuff.
+ * @{ */
+ /** Issues request to save settings. */
+ void sigSaveSettings();
+ /** Notifies listeners about group saving state changed. */
+ void sigGroupSavingStateChanged();
+ /** @} */
+
+ /** @name Cloud update stuff.
+ * @{ */
+ /** Notifies listeners about cloud update state changed. */
+ void sigCloudUpdateStateChanged();
+ /** @} */
+
+public:
+
+ /** Constructs abstract Chooser-model passing @a pParent to the base-class. */
+ UIChooserAbstractModel(UIChooser *pParent);
+ /** Destructs abstract Chooser-model. */
+ virtual ~UIChooserAbstractModel() RT_OVERRIDE;
+
+ /** @name General stuff.
+ * @{ */
+ /** Inits model. */
+ virtual void init();
+ /** Deinits model. */
+ virtual void deinit();
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Returns invisible root node instance. */
+ UIChooserNode *invisibleRoot() const { return m_pInvisibleRootNode; }
+
+ /** Wipes out empty groups. */
+ void wipeOutEmptyGroups();
+
+ /** Returns possible group node names for machine node with passed @a uId to move to. */
+ QStringList possibleGroupNodeNamesForMachineNodeToMove(const QUuid &uId);
+ /** Returns possible group node names for group node with passed @a strFullName to move to. */
+ QStringList possibleGroupNodeNamesForGroupNodeToMove(const QString &strFullName);
+
+ /** Generates unique group name traversing recursively starting from @a pRoot. */
+ static QString uniqueGroupName(UIChooserNode *pRoot);
+ /** @} */
+
+ /** @name Search stuff.
+ * @{ */
+ /** Performs a search using @a strSearchTerm and @a iSearchFlags specified. */
+ virtual void performSearch(const QString &strSearchTerm, int iSearchFlags);
+ /** Resets the search result data members and disables item's visual effects.
+ * Also returns a list of all nodes which may be utilized by the calling code. */
+ virtual QList<UIChooserNode*> resetSearch();
+ /** Returns search result. */
+ QList<UIChooserNode*> searchResult() const;
+ /** @} */
+
+ /** @name Group saving stuff.
+ * @{ */
+ /** Commands to save groups. */
+ void saveGroups();
+ /** Returns whether group saving is in progress. */
+ bool isGroupSavingInProgress() const;
+
+ /** Returns QString representation for passed @a uId, wiping out {} symbols.
+ * @note Required for backward compatibility after QString=>QUuid change. */
+ static QString toOldStyleUuid(const QUuid &uId);
+
+ /** Returns node extra-data prefix of certain @a enmType. */
+ static QString prefixToString(UIChooserNodeDataPrefixType enmType);
+ /** Returns node extra-data option of certain @a enmType. */
+ static QString optionToString(UIChooserNodeDataOptionType enmType);
+ /** Returns node extra-data value of certain @a enmType. */
+ static QString valueToString(UIChooserNodeDataValueType enmType);
+ /** @} */
+
+ /** @name Cloud update stuff.
+ * @{ */
+ /** Inserts cloud entity @a key into a set of keys currently being updated. */
+ void insertCloudEntityKey(const UICloudEntityKey &key);
+ /** Removes cloud entity @a key from a set of keys currently being updated. */
+ void removeCloudEntityKey(const UICloudEntityKey &key);
+ /** Returns whether cloud entity @a key is a part of key set currently being updated. */
+ bool containsCloudEntityKey(const UICloudEntityKey &key) const;
+
+ /** Returns whether at least one cloud profile currently being updated. */
+ bool isCloudProfileUpdateInProgress() const;
+ /** @} */
+
+public slots:
+
+ /** @name Cloud machine stuff.
+ * @{ */
+ /** Handles cloud machine refresh started. */
+ void sltHandleCloudMachineRefreshStarted();
+ /** Handles cloud machine refresh finished. */
+ void sltHandleCloudMachineRefreshFinished();
+ /** @} */
+
+ /** @name Group saving stuff.
+ * @{ */
+ /** Handles group settings saving complete. */
+ void sltGroupSettingsSaveComplete();
+ /** Handles group definitions saving complete. */
+ void sltGroupDefinitionsSaveComplete();
+ /** @} */
+
+protected slots:
+
+ /** @name Main event handling stuff.
+ * @{ */
+ /** Handles local machine @a enmState change for machine with certain @a uMachineId. */
+ virtual void sltLocalMachineStateChanged(const QUuid &uMachineId, const KMachineState enmState);
+ /** Handles local machine data change for machine with certain @a uMachineId. */
+ virtual void sltLocalMachineDataChanged(const QUuid &uMachineId);
+ /** Handles local machine registering/unregistering for machine with certain @a uMachineId. */
+ virtual void sltLocalMachineRegistrationChanged(const QUuid &uMachineId, const bool fRegistered);
+ /** Handles local machine registering/unregistering for machine with certain @a uMachineId. */
+ virtual void sltLocalMachineGroupsChanged(const QUuid &uMachineId);
+
+ /** Handles session @a enmState change for machine with certain @a uMachineId. */
+ virtual void sltSessionStateChanged(const QUuid &uMachineId, const KSessionState enmState);
+
+ /** Handles snapshot change for machine/snapshot with certain @a uMachineId / @a uSnapshotId. */
+ virtual void sltSnapshotChanged(const QUuid &uMachineId, const QUuid &uSnapshotId);
+
+ /** Handles event about cloud provider with @a uProviderId being uninstalled. */
+ virtual void sltHandleCloudProviderUninstall(const QUuid &uProviderId);
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Handles reload machine with certain @a uMachineId request. */
+ virtual void sltReloadMachine(const QUuid &uMachineId);
+
+ /** Handles command to commit data. */
+ virtual void sltCommitData();
+ /** Handles command to detach COM. */
+ virtual void sltDetachCOM();
+ /** @} */
+
+ /** @name Cloud stuff.
+ * @{ */
+ /** Handles cloud machine unregistering for @a uId.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name. */
+ virtual void sltCloudMachineUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QUuid &uId);
+ /** Handles cloud machine unregistering for a list of @a ids.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name. */
+ virtual void sltCloudMachinesUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QList<QUuid> &ids);
+ /** Handles cloud machine registering for @a comMachine.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name. */
+ virtual void sltCloudMachineRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine);
+ /** Handles cloud machine registering for a list of @a machines.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name. */
+ virtual void sltCloudMachinesRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QVector<CCloudMachine> &machines);
+
+ /** Handles read cloud machine list task complete signal. */
+ virtual void sltHandleReadCloudMachineListTaskComplete();
+
+ /** Handles Cloud Profile Manager cumulative change. */
+ virtual void sltHandleCloudProfileManagerCumulativeChange();
+ /** @} */
+
+protected:
+
+ /** @name Children stuff.
+ * @{ */
+ /** Creates and registers read cloud machine list task with @a guiCloudProfileKey.
+ * @param fWithRefresh Brings whether machines should be refreshed as well. */
+ void createReadCloudMachineListTask(const UICloudEntityKey &guiCloudProfileKey, bool fWithRefresh);
+ /** @} */
+
+private slots:
+
+ /** @name Group saving stuff.
+ * @{ */
+ /** Handles request to save settings. */
+ void sltSaveSettings();
+ /** @} */
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Reloads local tree. */
+ void reloadLocalTree();
+ /** Reloads cloud tree. */
+ void reloadCloudTree();
+
+ /** Adds local machine item based on certain @a comMachine and optionally @a fMakeItVisible. */
+ void addLocalMachineIntoTheTree(const CMachine &comMachine, bool fMakeItVisible = false);
+ /** Adds cloud machine item based on certain @a comMachine and optionally @a fMakeItVisible, into @a strGroup. */
+ void addCloudMachineIntoTheTree(const QString &strGroup, const CCloudMachine &comMachine, bool fMakeItVisible = false);
+
+ /** Acquires local group node, creates one if necessary.
+ * @param strName Brings the name of group we looking for.
+ * @param pParentNode Brings the parent we starting to look for a group from.
+ * @param fAllGroupsOpened Brings whether we should open all the groups till the required one. */
+ UIChooserNode *getLocalGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened);
+ /** Acquires cloud group node, never create new, returns root if nothing found.
+ * @param strName Brings the name of group we looking for.
+ * @param pParentNode Brings the parent we starting to look for a group from.
+ * @param fAllGroupsOpened Brings whether we should open all the groups till the required one. */
+ UIChooserNode *getCloudGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened);
+
+ /** Returns whether group node * with specified @a enmDataType and @a strName should be opened,
+ * searching starting from the passed @a pParentNode. */
+ bool shouldGroupNodeBeOpened(UIChooserNode *pParentNode,
+ UIChooserNodeDataPrefixType enmDataType,
+ const QString &strName) const;
+ /** Returns whether global node should be favorite,
+ * searching starting from the passed @a pParentNode. */
+ bool shouldGlobalNodeBeFavorite(UIChooserNode *pParentNode) const;
+
+ /** Wipes out empty groups starting from @a pParentItem. */
+ void wipeOutEmptyGroupsStartingFrom(UIChooserNode *pParentNode);
+
+ /** Acquires desired position for a child of @a pParentNode with specified @a enmDataType and @a strName. */
+ int getDesiredNodePosition(UIChooserNode *pParentNode, UIChooserNodeDataPrefixType enmDataType, const QString &strName);
+ /** Acquires defined position for a child of @a pParentNode with specified @a enmDataType and @a strName. */
+ int getDefinedNodePosition(UIChooserNode *pParentNode, UIChooserNodeDataPrefixType enmDataType, const QString &strName);
+
+ /** Creates local machine node based on certain @a comMachine as a child of specified @a pParentNode. */
+ void createLocalMachineNode(UIChooserNode *pParentNode, const CMachine &comMachine);
+ /** Creates fake cloud machine node in passed @a enmState as a child of specified @a pParentNode. */
+ void createCloudMachineNode(UIChooserNode *pParentNode, UIFakeCloudVirtualMachineItemState enmState);
+ /** Creates real cloud machine node based on certain @a comMachine as a child of specified @a pParentNode. */
+ void createCloudMachineNode(UIChooserNode *pParentNode, const CCloudMachine &comMachine);
+
+ /** Gathers a list of possible group node names for machine nodes listed in @a exceptions, starting from @a pCurrentNode. */
+ QStringList gatherPossibleGroupNodeNames(UIChooserNode *pCurrentNode, QList<UIChooserNode*> exceptions) const;
+
+ /** Returns whether passed @a pParentNode contains child node with passed @a uId. */
+ bool checkIfNodeContainChildWithId(UIChooserNode *pParentNode, const QUuid &uId) const;
+ /** @} */
+
+ /** @name Group saving stuff.
+ * @{ */
+ /** Saves group settings. */
+ void saveGroupSettings();
+ /** Saves group definitions. */
+ void saveGroupDefinitions();
+
+ /** Gathers group @a settings of @a pParentGroup. */
+ void gatherGroupSettings(QMap<QString, QStringList> &settings, UIChooserNode *pParentGroup);
+ /** Gathers group @a definitions of @a pParentGroup. */
+ void gatherGroupDefinitions(QMap<QString, QStringList> &definitions, UIChooserNode *pParentGroup);
+
+ /** Makes sure group settings saving is finished. */
+ void makeSureGroupSettingsSaveIsFinished();
+ /** Makes sure group definitions saving is finished. */
+ void makeSureGroupDefinitionsSaveIsFinished();
+ /** @} */
+
+ /** @name Cloud stuff.
+ * @{ */
+ /** Searches for provider node with passed @a uProviderId. */
+ UIChooserNode *searchProviderNode(const QUuid &uProviderId);
+ /** Searches for provider node with passed @a strProviderShortName. */
+ UIChooserNode *searchProviderNode(const QString &strProviderShortName);
+
+ /** Searches for profile node with passed @a strProfileName under passed @a pProviderNode. */
+ UIChooserNode *searchProfileNode(UIChooserNode *pProviderNode, const QString &strProfileName);
+ /** Searches for profile node with passed @a strProviderShortName and @a strProfileName. */
+ UIChooserNode *searchProfileNode(const QString &strProviderShortName, const QString &strProfileName);
+
+ /** Searches for machine node with passed @a uMachineId under passed @a pProfileNode. */
+ UIChooserNode *searchMachineNode(UIChooserNode *pProfileNode, const QUuid &uMachineId);
+ /** Searches for machine with passed @a strProviderShortName, @a strProfileName and @a uMachineId. */
+ UIChooserNode *searchMachineNode(const QString &strProviderShortName, const QString &strProfileName, const QUuid &uMachineId);
+
+ /** Searches for fake node under passed @a pProfileNode. */
+ UIChooserNode *searchFakeNode(UIChooserNode *pProfileNode);
+ /** Searches for fake with passed @a strProviderShortName and @a strProfileName. */
+ UIChooserNode *searchFakeNode(const QString &strProviderShortName, const QString &strProfileName);
+ /** @} */
+
+ /** @name Cloud update stuff.
+ * @{ */
+ /** Stops all cloud updates.
+ * @param fForced Brings whether cloud updates should be killed. */
+ void stopCloudUpdates(bool fForced = false);
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the parent widget reference. */
+ UIChooser *m_pParent;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Holds the invisible root node instance. */
+ UIChooserNode *m_pInvisibleRootNode;
+ /** @} */
+
+ /** @name Search stuff.
+ * @{ */
+ /** Stores the results of the current search. */
+ QList<UIChooserNode*> m_searchResults;
+ /** @} */
+
+ /** @name Group saving stuff.
+ * @{ */
+ /** Holds the consolidated map of group settings/definitions. */
+ QMap<QString, QStringList> m_groups;
+ /** @} */
+
+ /** @name Cloud update stuff.
+ * @{ */
+ /** Holds the set of cloud entity keys currently being updated. */
+ QSet<UICloudEntityKey> m_cloudEntityKeysBeingUpdated;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserAbstractModel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserDefs.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserDefs.h
new file mode 100644
index 00000000..1b042a01
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserDefs.h
@@ -0,0 +1,118 @@
+/* $Id: UIChooserDefs.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserDefs class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserDefs_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QGraphicsItem>
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+
+/** UIChooserNode types. */
+enum UIChooserNodeType
+{
+ UIChooserNodeType_Any = QGraphicsItem::UserType,
+ UIChooserNodeType_Group,
+ UIChooserNodeType_Global,
+ UIChooserNodeType_Machine
+};
+
+
+/** UIChooserNodeGroup types. */
+enum UIChooserNodeGroupType
+{
+ UIChooserNodeGroupType_Invalid,
+ UIChooserNodeGroupType_Local,
+ UIChooserNodeGroupType_Provider,
+ UIChooserNodeGroupType_Profile
+};
+
+
+/** UIChooserNode extra-data prefix types. */
+enum UIChooserNodeDataPrefixType
+{
+ UIChooserNodeDataPrefixType_Global,
+ UIChooserNodeDataPrefixType_Machine,
+ UIChooserNodeDataPrefixType_Local,
+ UIChooserNodeDataPrefixType_Provider,
+ UIChooserNodeDataPrefixType_Profile
+};
+
+
+/** UIChooserNode extra-data option types. */
+enum UIChooserNodeDataOptionType
+{
+ UIChooserNodeDataOptionType_GlobalFavorite,
+ UIChooserNodeDataOptionType_GroupOpened
+};
+
+
+/** UIChooserNode extra-data value types. */
+enum UIChooserNodeDataValueType
+{
+ UIChooserNodeDataValueType_GlobalDefault
+};
+
+
+/** UIChooserItem search flags. */
+enum UIChooserItemSearchFlag
+{
+ UIChooserItemSearchFlag_Global = RT_BIT(0),
+ UIChooserItemSearchFlag_Machine = RT_BIT(1),
+ UIChooserItemSearchFlag_LocalGroup = RT_BIT(2),
+ UIChooserItemSearchFlag_CloudProvider = RT_BIT(3),
+ UIChooserItemSearchFlag_CloudProfile = RT_BIT(4),
+ UIChooserItemSearchFlag_ExactId = RT_BIT(5),
+ UIChooserItemSearchFlag_ExactName = RT_BIT(6),
+ UIChooserItemSearchFlag_FullName = RT_BIT(7),
+};
+
+
+/** UIChooserItem drag token types. */
+enum UIChooserItemDragToken
+{
+ UIChooserItemDragToken_Off,
+ UIChooserItemDragToken_Up,
+ UIChooserItemDragToken_Down
+};
+
+
+/** UIChooserItemMachine enumeration flags. */
+enum UIChooserItemMachineEnumerationFlag
+{
+ UIChooserItemMachineEnumerationFlag_Unique = RT_BIT(0),
+ UIChooserItemMachineEnumerationFlag_Inaccessible = RT_BIT(1)
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerKeyboard.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerKeyboard.cpp
new file mode 100644
index 00000000..1ca98f83
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerKeyboard.cpp
@@ -0,0 +1,428 @@
+/* $Id: UIChooserHandlerKeyboard.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserHandlerKeyboard class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QKeyEvent>
+
+/* GUI incluedes: */
+#include "UIChooserHandlerKeyboard.h"
+#include "UIChooserModel.h"
+#include "UIChooserItemGroup.h"
+#include "UIChooserItemMachine.h"
+#include "UIChooserNodeGroup.h"
+#include "UIChooserNodeMachine.h"
+
+
+UIChooserHandlerKeyboard::UIChooserHandlerKeyboard(UIChooserModel *pParent)
+ : QObject(pParent)
+ , m_pModel(pParent)
+{
+ /* Setup shift map: */
+ m_shiftMap[Qt::Key_Up] = UIItemShiftSize_Item;
+ m_shiftMap[Qt::Key_Down] = UIItemShiftSize_Item;
+ m_shiftMap[Qt::Key_Home] = UIItemShiftSize_Full;
+ m_shiftMap[Qt::Key_End] = UIItemShiftSize_Full;
+}
+
+bool UIChooserHandlerKeyboard::handle(QKeyEvent *pEvent, UIKeyboardEventType type) const
+{
+ /* Process passed event: */
+ switch (type)
+ {
+ case UIKeyboardEventType_Press: return handleKeyPress(pEvent);
+ case UIKeyboardEventType_Release: return handleKeyRelease(pEvent);
+ }
+ /* Pass event if unknown: */
+ return false;
+}
+
+UIChooserModel* UIChooserHandlerKeyboard::model() const
+{
+ return m_pModel;
+}
+
+bool UIChooserHandlerKeyboard::handleKeyPress(QKeyEvent *pEvent) const
+{
+ /* Which key it was? */
+ switch (pEvent->key())
+ {
+ /* Key UP? */
+ case Qt::Key_Up:
+ /* Key HOME? */
+ case Qt::Key_Home:
+ {
+ /* Was control modifier pressed? */
+#ifdef VBOX_WS_MAC
+ if (pEvent->modifiers() & Qt::ControlModifier &&
+ pEvent->modifiers() & Qt::KeypadModifier)
+#else /* VBOX_WS_MAC */
+ if (pEvent->modifiers() == Qt::ControlModifier)
+#endif /* !VBOX_WS_MAC */
+ {
+ /* Shift item up: */
+ shift(UIItemShiftDirection_Up, m_shiftMap[pEvent->key()]);
+ return true;
+ }
+
+ /* Was shift modifier pressed? */
+#ifdef VBOX_WS_MAC
+ else if (pEvent->modifiers() & Qt::ShiftModifier &&
+ pEvent->modifiers() & Qt::KeypadModifier)
+#else /* VBOX_WS_MAC */
+ else if (pEvent->modifiers() == Qt::ShiftModifier)
+#endif /* !VBOX_WS_MAC */
+ {
+ /* Determine current-item position: */
+ UIChooserItem *pCurrentItem = model()->currentItem();
+ int iPosition = model()->navigationItems().indexOf(pCurrentItem);
+ /* Determine 'previous' item: */
+ UIChooserItem *pPreviousItem = 0;
+ if (iPosition > 0)
+ {
+ if ( pEvent->key() == Qt::Key_Up
+ || pEvent->key() == Qt::Key_Home)
+ {
+ UIChooserItem *pPossiblePreviousItem = 0;
+ const int iLimit = pEvent->key() == Qt::Key_Up ? iPosition - 1 : 0;
+ for (int i = iPosition - 1; i >= iLimit; --i)
+ {
+ pPossiblePreviousItem = model()->navigationItems().at(i);
+ if (( pCurrentItem->type() == UIChooserNodeType_Global
+ && pPossiblePreviousItem->type() == UIChooserNodeType_Global)
+ || ( pCurrentItem->type() != UIChooserNodeType_Global
+ && pPossiblePreviousItem->type() != UIChooserNodeType_Global))
+ pPreviousItem = pPossiblePreviousItem;
+ }
+ }
+ }
+ if (pPreviousItem)
+ {
+ /* Make sure 'previous' item is visible: */
+ pPreviousItem->makeSureItsVisible();
+ /* Calculate positions: */
+ UIChooserItem *pFirstItem = model()->firstSelectedItem();
+ int iFirstPosition = model()->navigationItems().indexOf(pFirstItem);
+ int iPreviousPosition = model()->navigationItems().indexOf(pPreviousItem);
+ /* Populate list of items from 'first' to 'previous': */
+ QList<UIChooserItem*> items;
+ if (iFirstPosition <= iPreviousPosition)
+ for (int i = iFirstPosition; i <= iPreviousPosition; ++i)
+ items << model()->navigationItems().at(i);
+ else
+ for (int i = iFirstPosition; i >= iPreviousPosition; --i)
+ items << model()->navigationItems().at(i);
+ /* Set that list as selected: */
+ model()->setSelectedItems(items);
+ /* Make 'previous' item current one: */
+ model()->setCurrentItem(pPreviousItem);
+ /* Filter-out this event: */
+ return true;
+ }
+ }
+
+ /* There is no modifiers pressed? */
+#ifdef VBOX_WS_MAC
+ else if (pEvent->modifiers() == Qt::KeypadModifier)
+#else /* VBOX_WS_MAC */
+ else if (pEvent->modifiers() == Qt::NoModifier)
+#endif /* !VBOX_WS_MAC */
+ {
+ /* Determine current-item position: */
+ int iPosition = model()->navigationItems().indexOf(model()->currentItem());
+ /* Determine 'previous' item: */
+ UIChooserItem *pPreviousItem = 0;
+ if (iPosition > 0)
+ {
+ if (pEvent->key() == Qt::Key_Up)
+ pPreviousItem = model()->navigationItems().at(iPosition - 1);
+ else if (pEvent->key() == Qt::Key_Home)
+ pPreviousItem = model()->navigationItems().first();
+ }
+ if (pPreviousItem)
+ {
+ /* Make sure 'previous' item is visible: */
+ pPreviousItem->makeSureItsVisible();
+ /* Make 'previous' item the only selected: */
+ model()->setSelectedItem(pPreviousItem);
+ /* Filter-out this event: */
+ return true;
+ }
+ }
+ /* Pass this event: */
+ return false;
+ }
+ /* Key DOWN? */
+ case Qt::Key_Down:
+ /* Key END? */
+ case Qt::Key_End:
+ {
+ /* Was control modifier pressed? */
+#ifdef VBOX_WS_MAC
+ if (pEvent->modifiers() & Qt::ControlModifier &&
+ pEvent->modifiers() & Qt::KeypadModifier)
+#else /* VBOX_WS_MAC */
+ if (pEvent->modifiers() == Qt::ControlModifier)
+#endif /* !VBOX_WS_MAC */
+ {
+ /* Shift item down: */
+ shift(UIItemShiftDirection_Down, m_shiftMap[pEvent->key()]);
+ return true;
+ }
+
+ /* Was shift modifier pressed? */
+#ifdef VBOX_WS_MAC
+ else if (pEvent->modifiers() & Qt::ShiftModifier &&
+ pEvent->modifiers() & Qt::KeypadModifier)
+#else /* VBOX_WS_MAC */
+ else if (pEvent->modifiers() == Qt::ShiftModifier)
+#endif /* !VBOX_WS_MAC */
+ {
+ /* Determine current-item position: */
+ UIChooserItem *pCurrentItem = model()->currentItem();
+ int iPosition = model()->navigationItems().indexOf(pCurrentItem);
+ /* Determine 'next' item: */
+ UIChooserItem *pNextItem = 0;
+ if (iPosition < model()->navigationItems().size() - 1)
+ {
+ if ( pEvent->key() == Qt::Key_Down
+ || pEvent->key() == Qt::Key_End)
+ {
+ UIChooserItem *pPossibleNextItem = 0;
+ const int iLimit = pEvent->key() == Qt::Key_Down ? iPosition + 1 : 0;
+ for (int i = iPosition + 1; i <= iLimit; ++i)
+ {
+ pPossibleNextItem = model()->navigationItems().at(i);
+ if (( pCurrentItem->type() == UIChooserNodeType_Global
+ && pPossibleNextItem->type() == UIChooserNodeType_Global)
+ || ( pCurrentItem->type() != UIChooserNodeType_Global
+ && pPossibleNextItem->type() != UIChooserNodeType_Global))
+ pNextItem = pPossibleNextItem;
+ }
+ }
+ }
+ if (pNextItem)
+ {
+ /* Make sure 'next' item is visible: */
+ pNextItem->makeSureItsVisible();
+ /* Calculate positions: */
+ UIChooserItem *pFirstItem = model()->firstSelectedItem();
+ int iFirstPosition = model()->navigationItems().indexOf(pFirstItem);
+ int iNextPosition = model()->navigationItems().indexOf(pNextItem);
+ /* Populate list of items from 'first' to 'next': */
+ QList<UIChooserItem*> items;
+ if (iFirstPosition <= iNextPosition)
+ for (int i = iFirstPosition; i <= iNextPosition; ++i)
+ items << model()->navigationItems().at(i);
+ else
+ for (int i = iFirstPosition; i >= iNextPosition; --i)
+ items << model()->navigationItems().at(i);
+ /* Set that list as selected: */
+ model()->setSelectedItems(items);
+ /* Make 'next' item current one: */
+ model()->setCurrentItem(pNextItem);
+ /* Filter-out this event: */
+ return true;
+ }
+ }
+
+ /* There is no modifiers pressed? */
+#ifdef VBOX_WS_MAC
+ else if (pEvent->modifiers() == Qt::KeypadModifier)
+#else /* VBOX_WS_MAC */
+ else if (pEvent->modifiers() == Qt::NoModifier)
+#endif /* !VBOX_WS_MAC */
+ {
+ /* Determine current-item position: */
+ int iPosition = model()->navigationItems().indexOf(model()->currentItem());
+ /* Determine 'next' item: */
+ UIChooserItem *pNextItem = 0;
+ if (iPosition < model()->navigationItems().size() - 1)
+ {
+ if (pEvent->key() == Qt::Key_Down)
+ pNextItem = model()->navigationItems().at(iPosition + 1);
+ else if (pEvent->key() == Qt::Key_End)
+ pNextItem = model()->navigationItems().last();
+ }
+ if (pNextItem)
+ {
+ /* Make sure 'next' item is visible: */
+ pNextItem->makeSureItsVisible();
+ /* Make 'next' item the only selected one: */
+ model()->setSelectedItem(pNextItem);
+ /* Filter-out this event: */
+ return true;
+ }
+ }
+ /* Pass this event: */
+ return false;
+ }
+ /* Key F2? */
+ case Qt::Key_F2:
+ {
+ /* If this item is of group type: */
+ if (model()->currentItem()->type() == UIChooserNodeType_Group)
+ {
+ /* Start editing selected group item name: */
+ model()->startEditingSelectedGroupItemName();
+ /* Filter that event out: */
+ return true;
+ }
+ /* Pass event to other items: */
+ return false;
+ }
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ {
+ /* If this item is of group or machine type: */
+ if ( model()->currentItem()->type() == UIChooserNodeType_Group
+ || model()->currentItem()->type() == UIChooserNodeType_Machine)
+ {
+ /* Start or show selected items: */
+ model()->startOrShowSelectedItems();
+ /* And filter out that event: */
+ return true;
+ }
+ /* Pass event to other items: */
+ return false;
+ }
+ case Qt::Key_Space:
+ {
+ /* If there is a current-item: */
+ if (UIChooserItem *pCurrentItem = model()->currentItem())
+ {
+ /* Of the group type: */
+ if (pCurrentItem->type() == UIChooserNodeType_Group)
+ {
+ /* Toggle that group: */
+ UIChooserItemGroup *pGroupItem = pCurrentItem->toGroupItem();
+ if (pGroupItem->isClosed())
+ pGroupItem->open();
+ else if (pGroupItem->isOpened())
+ pGroupItem->close();
+ /* Filter that event out: */
+ return true;
+ }
+ }
+ /* Pass event to other items: */
+ return false;
+ }
+ case Qt::Key_Escape:
+ {
+ /* Make sure that vm search widget is hidden: */
+ model()->setSearchWidgetVisible(false);
+ break;
+ }
+ default:
+ {
+ /* Start lookup only for non-empty and printable strings: */
+ const QString strText = pEvent->text();
+ if (!strText.isEmpty() && pEvent->modifiers() == Qt::NoModifier && pEvent->text().at(0).isPrint())
+ model()->lookFor(strText);
+ break;
+ }
+ }
+ /* Pass all other events: */
+ return false;
+}
+
+bool UIChooserHandlerKeyboard::handleKeyRelease(QKeyEvent*) const
+{
+ /* Pass all events: */
+ return false;
+}
+
+void UIChooserHandlerKeyboard::shift(UIItemShiftDirection enmDirection, UIItemShiftType enmShiftType) const
+{
+ /* Get current-node and its parent: */
+ UIChooserNode *pCurrentNode = model()->currentItem()->node();
+ UIChooserNode *pParentNode = pCurrentNode->parentNode();
+ /* Get current-node position: */
+ const int iCurrentNodePosition = pCurrentNode->position();
+
+ /* Calculate new position: */
+ int iNewCurrentNodePosition = -1;
+ switch (enmDirection)
+ {
+ case UIItemShiftDirection_Up:
+ {
+ if (iCurrentNodePosition > 0)
+ switch (enmShiftType)
+ {
+ case UIItemShiftSize_Item: iNewCurrentNodePosition = iCurrentNodePosition - 1; break;
+ case UIItemShiftSize_Full: iNewCurrentNodePosition = 0; break;
+ default: break;
+ }
+ break;
+ }
+ case UIItemShiftDirection_Down:
+ {
+ if (iCurrentNodePosition < pParentNode->nodes(pCurrentNode->type()).size() - 1)
+ switch (enmShiftType)
+ {
+ case UIItemShiftSize_Item: iNewCurrentNodePosition = iCurrentNodePosition + 2; break;
+ case UIItemShiftSize_Full: iNewCurrentNodePosition = pParentNode->nodes(pCurrentNode->type()).size(); break;
+ default: break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ /* Filter out invalid requests: */
+ if (iNewCurrentNodePosition == -1)
+ return;
+
+ /* Create shifted node/item: */
+ UIChooserItem *pShiftedItem = 0;
+ switch (pCurrentNode->type())
+ {
+ case UIChooserNodeType_Group:
+ {
+ UIChooserNodeGroup *pNewNode = new UIChooserNodeGroup(pParentNode, iNewCurrentNodePosition, pCurrentNode->toGroupNode());
+ pShiftedItem = new UIChooserItemGroup(pParentNode->item(), pNewNode);
+ break;
+ }
+ case UIChooserNodeType_Machine:
+ {
+ UIChooserNodeMachine *pNewNode = new UIChooserNodeMachine(pParentNode, iNewCurrentNodePosition, pCurrentNode->toMachineNode());
+ pShiftedItem = new UIChooserItemMachine(pParentNode->item(), pNewNode);
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Delete old node/item: */
+ delete pCurrentNode;
+
+ /* Update model: */
+ model()->wipeOutEmptyGroups();
+ model()->updateNavigationItemList();
+ model()->updateLayout();
+ model()->setSelectedItem(pShiftedItem);
+ model()->saveGroups();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerKeyboard.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerKeyboard.h
new file mode 100644
index 00000000..f116916a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerKeyboard.h
@@ -0,0 +1,93 @@
+/* $Id: UIChooserHandlerKeyboard.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserHandlerKeyboard class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserHandlerKeyboard_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserHandlerKeyboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QMap>
+
+/* Forward declarations: */
+class UIChooserModel;
+class QKeyEvent;
+
+/** Keyboard event type. */
+enum UIKeyboardEventType
+{
+ UIKeyboardEventType_Press,
+ UIKeyboardEventType_Release
+};
+
+/** Item shift direction. */
+enum UIItemShiftDirection
+{
+ UIItemShiftDirection_Up,
+ UIItemShiftDirection_Down
+};
+
+/** Item shift types. */
+enum UIItemShiftType
+{
+ UIItemShiftSize_Item,
+ UIItemShiftSize_Full
+};
+
+/** Keyboard handler for graphics selector. */
+class UIChooserHandlerKeyboard : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor. */
+ UIChooserHandlerKeyboard(UIChooserModel *pParent);
+
+ /** API: Model keyboard-event handler delegate. */
+ bool handle(QKeyEvent *pEvent, UIKeyboardEventType type) const;
+
+private:
+
+ /** API: Model wrapper. */
+ UIChooserModel* model() const;
+
+ /** Helpers: Model keyboard-event handler delegates. */
+ bool handleKeyPress(QKeyEvent *pEvent) const;
+ bool handleKeyRelease(QKeyEvent *pEvent) const;
+
+ /** Helper: Item shift delegate. */
+ void shift(UIItemShiftDirection enmDirection, UIItemShiftType enmShiftType) const;
+
+ /** Variables. */
+ UIChooserModel *m_pModel;
+ QMap<int, UIItemShiftType> m_shiftMap;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserHandlerKeyboard_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerMouse.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerMouse.cpp
new file mode 100644
index 00000000..e0cd629a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerMouse.cpp
@@ -0,0 +1,263 @@
+/* $Id: UIChooserHandlerMouse.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserHandlerMouse class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGraphicsSceneMouseEvent>
+
+/* GUI incluedes: */
+#include "UIChooserHandlerMouse.h"
+#include "UIChooserModel.h"
+#include "UIChooserItemGroup.h"
+#include "UIChooserItemGlobal.h"
+#include "UIChooserItemMachine.h"
+
+
+UIChooserHandlerMouse::UIChooserHandlerMouse(UIChooserModel *pParent)
+ : QObject(pParent)
+ , m_pModel(pParent)
+{
+}
+
+bool UIChooserHandlerMouse::handle(QGraphicsSceneMouseEvent *pEvent, UIMouseEventType type) const
+{
+ /* Process passed event: */
+ switch (type)
+ {
+ case UIMouseEventType_Press: return handleMousePress(pEvent);
+ case UIMouseEventType_Release: return handleMouseRelease(pEvent);
+ case UIMouseEventType_DoubleClick: return handleMouseDoubleClick(pEvent);
+ }
+ /* Pass event if unknown: */
+ return false;
+}
+
+UIChooserModel* UIChooserHandlerMouse::model() const
+{
+ return m_pModel;
+}
+
+bool UIChooserHandlerMouse::handleMousePress(QGraphicsSceneMouseEvent *pEvent) const
+{
+ /* Get item under mouse cursor: */
+ QPointF scenePos = pEvent->scenePos();
+ if (QGraphicsItem *pItemUnderMouse = model()->itemAt(scenePos))
+ {
+ /* Which button it was? */
+ switch (pEvent->button())
+ {
+ /* Left one? */
+ case Qt::LeftButton:
+ {
+ /* Which item we just clicked? */
+ UIChooserItem *pClickedItem = 0;
+ /* Was that a group item? */
+ if (UIChooserItemGroup *pGroupItem = qgraphicsitem_cast<UIChooserItemGroup*>(pItemUnderMouse))
+ pClickedItem = pGroupItem;
+ /* Or a global one? */
+ else if (UIChooserItemGlobal *pGlobalItem = qgraphicsitem_cast<UIChooserItemGlobal*>(pItemUnderMouse))
+ {
+ const QPoint itemCursorPos = pGlobalItem->mapFromScene(scenePos).toPoint();
+ if ( pGlobalItem->isToolButtonArea(itemCursorPos)
+ && ( model()->firstSelectedItem() == pGlobalItem
+ || pGlobalItem->isHovered()))
+ {
+ model()->handleToolButtonClick(pGlobalItem);
+ if (model()->firstSelectedItem() != pGlobalItem)
+ pClickedItem = pGlobalItem;
+ }
+ else
+ if ( pGlobalItem->isPinButtonArea(itemCursorPos)
+ && ( model()->firstSelectedItem() == pGlobalItem
+ || pGlobalItem->isHovered()))
+ model()->handlePinButtonClick(pGlobalItem);
+ else
+ pClickedItem = pGlobalItem;
+ }
+ /* Or a machine one? */
+ else if (UIChooserItemMachine *pMachineItem = qgraphicsitem_cast<UIChooserItemMachine*>(pItemUnderMouse))
+ {
+ const QPoint itemCursorPos = pMachineItem->mapFromScene(scenePos).toPoint();
+ if ( pMachineItem->isToolButtonArea(itemCursorPos)
+ && ( model()->firstSelectedItem() == pMachineItem
+ || pMachineItem->isHovered()))
+ {
+ model()->handleToolButtonClick(pMachineItem);
+ if (model()->firstSelectedItem() != pMachineItem)
+ pClickedItem = pMachineItem;
+ }
+ else
+ pClickedItem = pMachineItem;
+ }
+ /* If we had clicked one of required item types: */
+ if (pClickedItem && !pClickedItem->isRoot())
+ {
+ /* Was 'shift' modifier pressed? */
+ if (pEvent->modifiers() == Qt::ShiftModifier)
+ {
+ /* Calculate positions: */
+ UIChooserItem *pFirstItem = model()->firstSelectedItem();
+ AssertPtrReturn(pFirstItem, false); // is failure possible?
+ const int iFirstPosition = model()->navigationItems().indexOf(pFirstItem);
+ const int iClickedPosition = model()->navigationItems().indexOf(pClickedItem);
+ /* Populate list of items from 'first' to 'clicked': */
+ QList<UIChooserItem*> items;
+ if (iFirstPosition <= iClickedPosition)
+ for (int i = iFirstPosition; i <= iClickedPosition; ++i)
+ items << model()->navigationItems().at(i);
+ else
+ for (int i = iFirstPosition; i >= iClickedPosition; --i)
+ items << model()->navigationItems().at(i);
+ /* Wipe out items of inconsistent types: */
+ QList<UIChooserItem*> filteredItems;
+ foreach (UIChooserItem *pIteratedItem, items)
+ {
+ /* So, the logic is to add intermediate item if
+ * - first and intermediate selected items are global or
+ * - first and intermediate selected items are NOT global. */
+ if ( ( pFirstItem->type() == UIChooserNodeType_Global
+ && pIteratedItem->type() == UIChooserNodeType_Global)
+ || ( pFirstItem->type() != UIChooserNodeType_Global
+ && pIteratedItem->type() != UIChooserNodeType_Global))
+ filteredItems << pIteratedItem;
+ }
+ /* Make that list selected: */
+ model()->setSelectedItems(filteredItems);
+ /* Make item closest to clicked the current one: */
+ if (!filteredItems.isEmpty())
+ model()->setCurrentItem(filteredItems.last());
+ }
+ /* Was 'control' modifier pressed? */
+ else if (pEvent->modifiers() == Qt::ControlModifier)
+ {
+ /* Invert selection state for clicked item: */
+ if (model()->selectedItems().contains(pClickedItem))
+ model()->removeFromSelectedItems(pClickedItem);
+ else
+ {
+ /* So, the logic is to add newly clicked item if
+ * - previously and newly selected items are global or
+ * - previously and newly selected items are NOT global. */
+ UIChooserItem *pFirstItem = model()->firstSelectedItem();
+ AssertPtrReturn(pFirstItem, false); // is failure possible?
+ if ( ( pFirstItem->type() == UIChooserNodeType_Global
+ && pClickedItem->type() == UIChooserNodeType_Global)
+ || ( pFirstItem->type() != UIChooserNodeType_Global
+ && pClickedItem->type() != UIChooserNodeType_Global))
+ model()->addToSelectedItems(pClickedItem);
+ }
+ /* Make clicked item current one: */
+ model()->setCurrentItem(pClickedItem);
+ }
+ /* Was no modifiers pressed? */
+ else if (pEvent->modifiers() == Qt::NoModifier)
+ {
+ /* Make clicked item the only selected one: */
+ model()->setSelectedItem(pClickedItem);
+ }
+ }
+ break;
+ }
+ /* Right one? */
+ case Qt::RightButton:
+ {
+ /* Which item we just clicked? */
+ UIChooserItem *pClickedItem = 0;
+ /* Was that a group item? */
+ if (UIChooserItemGroup *pGroupItem = qgraphicsitem_cast<UIChooserItemGroup*>(pItemUnderMouse))
+ pClickedItem = pGroupItem;
+ /* Or a global one? */
+ else if (UIChooserItemGlobal *pGlobalItem = qgraphicsitem_cast<UIChooserItemGlobal*>(pItemUnderMouse))
+ pClickedItem = pGlobalItem;
+ /* Or a machine one? */
+ else if (UIChooserItemMachine *pMachineItem = qgraphicsitem_cast<UIChooserItemMachine*>(pItemUnderMouse))
+ pClickedItem = pMachineItem;
+ /* If we had clicked one of required item types: */
+ if (pClickedItem && !pClickedItem->isRoot())
+ {
+ /* Select clicked item if not selected yet: */
+ if (!model()->selectedItems().contains(pClickedItem))
+ model()->setSelectedItem(pClickedItem);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ /* Pass all other events: */
+ return false;
+}
+
+bool UIChooserHandlerMouse::handleMouseRelease(QGraphicsSceneMouseEvent*) const
+{
+ /* Pass all events: */
+ return false;
+}
+
+bool UIChooserHandlerMouse::handleMouseDoubleClick(QGraphicsSceneMouseEvent *pEvent) const
+{
+ /* Get item under mouse cursor: */
+ QPointF scenePos = pEvent->scenePos();
+ if (QGraphicsItem *pItemUnderMouse = model()->itemAt(scenePos))
+ {
+ /* Which button it was? */
+ switch (pEvent->button())
+ {
+ /* Left one? */
+ case Qt::LeftButton:
+ {
+ /* Was that a group item? */
+ if (UIChooserItemGroup *pGroupItem = qgraphicsitem_cast<UIChooserItemGroup*>(pItemUnderMouse))
+ {
+ /* If it was not root: */
+ if (!pGroupItem->isRoot())
+ {
+ /* Toggle it: */
+ if (pGroupItem->isClosed())
+ pGroupItem->open();
+ else if (pGroupItem->isOpened())
+ pGroupItem->close();
+ }
+ /* Filter that event out: */
+ return true;
+ }
+ /* Or a machine one? */
+ else if (pItemUnderMouse->type() == UIChooserNodeType_Machine)
+ {
+ /* Start or show selected items: */
+ model()->startOrShowSelectedItems();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ /* Pass all other events: */
+ return false;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerMouse.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerMouse.h
new file mode 100644
index 00000000..2bd4ffa3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerMouse.h
@@ -0,0 +1,78 @@
+/* $Id: UIChooserHandlerMouse.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserHandlerMouse class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserHandlerMouse_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserHandlerMouse_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* Forward declarations: */
+class UIChooserModel;
+class QGraphicsSceneMouseEvent;
+class UIChooserItem;
+
+/* Mouse event type: */
+enum UIMouseEventType
+{
+ UIMouseEventType_Press,
+ UIMouseEventType_Release,
+ UIMouseEventType_DoubleClick
+};
+
+/* Mouse handler for graphics selector: */
+class UIChooserHandlerMouse : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /* Constructor: */
+ UIChooserHandlerMouse(UIChooserModel *pParent);
+
+ /* API: Model mouse-event handler delegate: */
+ bool handle(QGraphicsSceneMouseEvent *pEvent, UIMouseEventType type) const;
+
+private:
+
+ /* API: Model wrapper: */
+ UIChooserModel* model() const;
+
+ /* Helpers: Model mouse-event handler delegates: */
+ bool handleMousePress(QGraphicsSceneMouseEvent *pEvent) const;
+ bool handleMouseRelease(QGraphicsSceneMouseEvent *pEvent) const;
+ bool handleMouseDoubleClick(QGraphicsSceneMouseEvent *pEvent) const;
+
+ /* Variables: */
+ UIChooserModel *m_pModel;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserHandlerMouse_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItem.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItem.cpp
new file mode 100644
index 00000000..c002438b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItem.cpp
@@ -0,0 +1,729 @@
+/* $Id: UIChooserItem.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserItem class definition.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleObject>
+#include <QApplication>
+#include <QStyle>
+#include <QPainter>
+#include <QGraphicsScene>
+#include <QStyleOptionFocusRect>
+#include <QGraphicsSceneMouseEvent>
+#include <QStateMachine>
+#include <QPropertyAnimation>
+#include <QSignalTransition>
+#include <QDrag>
+
+/* GUI includes: */
+#include "UIChooserItem.h"
+#include "UIChooserItemGroup.h"
+#include "UIChooserItemGlobal.h"
+#include "UIChooserItemMachine.h"
+#include "UIChooserView.h"
+#include "UIChooserModel.h"
+#include "UIChooserNode.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIImageTools.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QAccessibleObject extension used as an accessibility interface for Chooser-view items. */
+class UIAccessibilityInterfaceForUIChooserItem : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating Chooser-view accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UIChooserItem"))
+ return new UIAccessibilityInterfaceForUIChooserItem(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ UIAccessibilityInterfaceForUIChooserItem(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Return the parent: */
+ return QAccessible::queryAccessibleInterface(item()->model()->view());
+ }
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Return the number of group children: */
+ if (item()->type() == UIChooserNodeType_Group)
+ return item()->items().size();
+
+ /* Zero by default: */
+ return 0;
+ }
+
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Return the child with the passed iIndex: */
+ return QAccessible::queryAccessibleInterface(item()->items().at(iIndex));
+ }
+
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE
+ {
+ /* Search for corresponding child: */
+ for (int i = 0; i < childCount(); ++i)
+ if (child(i) == pChild)
+ return i;
+
+ /* -1 by default: */
+ return -1;
+ }
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE
+ {
+ /* Now goes the mapping: */
+ const QSize itemSize = item()->size().toSize();
+ const QPointF itemPosInScene = item()->mapToScene(QPointF(0, 0));
+ const QPoint itemPosInView = item()->model()->view()->mapFromScene(itemPosInScene);
+ const QPoint itemPosInScreen = item()->model()->view()->mapToGlobal(itemPosInView);
+ const QRect itemRectInScreen = QRect(itemPosInScreen, itemSize);
+ return itemRectInScreen;
+ }
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QString());
+
+ switch (enmTextRole)
+ {
+ case QAccessible::Name: return item()->name();
+ case QAccessible::Description: return item()->description();
+ default: break;
+ }
+
+ /* Null-string by default: */
+ return QString();
+ }
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QAccessible::NoRole);
+
+ /* Return the role of group: */
+ if (item()->type() == UIChooserNodeType_Group)
+ return QAccessible::List;
+
+ /* ListItem by default: */
+ return QAccessible::ListItem;
+ }
+
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QAccessible::State());
+
+ /* Compose the state: */
+ QAccessible::State state;
+ state.focusable = true;
+ state.selectable = true;
+
+ /* Compose the state of first selected-item: */
+ if (item() && item() == item()->model()->firstSelectedItem())
+ {
+ state.active = true;
+ state.focused = true;
+ state.selected = true;
+ }
+
+ /* Compose the state of group: */
+ if (item()->type() == UIChooserNodeType_Group)
+ {
+ state.expandable = true;
+ if (!item()->toGroupItem()->isClosed())
+ state.expanded = true;
+ }
+
+ /* Return the state: */
+ return state;
+ }
+
+private:
+
+ /** Returns corresponding Chooser-view item. */
+ UIChooserItem *item() const { return qobject_cast<UIChooserItem*>(object()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIChooserDisabledItemEffect implementation. *
+*********************************************************************************************************************************/
+
+UIChooserDisabledItemEffect::UIChooserDisabledItemEffect(int iBlurRadius, QObject *pParent /* = 0 */)
+ : QGraphicsEffect(pParent)
+ , m_iBlurRadius(iBlurRadius)
+{
+}
+
+void UIChooserDisabledItemEffect::draw(QPainter *pPainter)
+{
+ QPoint offset;
+ QPixmap pixmap;
+ /* Get the original pixmap: */
+ pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset);
+ QImage resultImage;
+ /* Apply our blur and grayscale filters to the original pixmap: */
+ UIImageTools::blurImage(pixmap.toImage(), resultImage, m_iBlurRadius);
+ pixmap.convertFromImage(UIImageTools::toGray(resultImage));
+ QWidget *pParentWidget = qobject_cast<QWidget*>(parent());
+ pixmap.setDevicePixelRatio( pParentWidget
+ ? UIDesktopWidgetWatchdog::devicePixelRatioActual(pParentWidget)
+ : UIDesktopWidgetWatchdog::devicePixelRatioActual());
+ /* Use the filtered pixmap: */
+ pPainter->drawPixmap(offset, pixmap);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIChooserItem implementation. *
+*********************************************************************************************************************************/
+
+UIChooserItem::UIChooserItem(UIChooserItem *pParent, UIChooserNode *pNode,
+ int iDefaultValue /* = 0 */, int iHoveredValue /* = 100 */)
+ : QIWithRetranslateUI4<QIGraphicsWidget>(pParent)
+ , m_pParent(pParent)
+ , m_pNode(pNode)
+ , m_fHovered(false)
+ , m_fSelected(false)
+ , m_pHoveringMachine(0)
+ , m_pHoveringAnimationForward(0)
+ , m_pHoveringAnimationBackward(0)
+ , m_iAnimationDuration(400)
+ , m_iDefaultValue(iDefaultValue)
+ , m_iHoveredValue(iHoveredValue)
+ , m_iAnimatedValue(m_iDefaultValue)
+ , m_pDisabledEffect(0)
+ , m_enmDragTokenPlace(UIChooserItemDragToken_Off)
+ , m_iDragTokenDarkness(110)
+{
+ /* Install Chooser-view item accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUIChooserItem::pFactory);
+
+ /* Assign item for passed node: */
+ node()->setItem(this);
+
+ /* Basic item setup: */
+ setOwnedByLayout(false);
+ setAcceptDrops(true);
+ setFocusPolicy(Qt::NoFocus);
+ setFlag(QGraphicsItem::ItemIsSelectable, false);
+ setAcceptHoverEvents(!isRoot());
+
+ /* Non-root item? */
+ if (!isRoot())
+ {
+ /* Create hovering animation machine: */
+ m_pHoveringMachine = new QStateMachine(this);
+ if (m_pHoveringMachine)
+ {
+ /* Create 'default' state: */
+ QState *pStateDefault = new QState(m_pHoveringMachine);
+ /* Create 'hovered' state: */
+ QState *pStateHovered = new QState(m_pHoveringMachine);
+
+ /* Configure 'default' state: */
+ if (pStateDefault)
+ {
+ /* When we entering default state => we assigning animatedValue to m_iDefaultValue: */
+ pStateDefault->assignProperty(this, "animatedValue", m_iDefaultValue);
+
+ /* Add state transitions: */
+ QSignalTransition *pDefaultToHovered = pStateDefault->addTransition(this, SIGNAL(sigHoverEnter()), pStateHovered);
+ if (pDefaultToHovered)
+ {
+ /* Create forward animation: */
+ m_pHoveringAnimationForward = new QPropertyAnimation(this, "animatedValue", this);
+ if (m_pHoveringAnimationForward)
+ {
+ m_pHoveringAnimationForward->setDuration(m_iAnimationDuration);
+ m_pHoveringAnimationForward->setStartValue(m_iDefaultValue);
+ m_pHoveringAnimationForward->setEndValue(m_iHoveredValue);
+
+ /* Add to transition: */
+ pDefaultToHovered->addAnimation(m_pHoveringAnimationForward);
+ }
+ }
+ }
+
+ /* Configure 'hovered' state: */
+ if (pStateHovered)
+ {
+ /* When we entering hovered state => we assigning animatedValue to m_iHoveredValue: */
+ pStateHovered->assignProperty(this, "animatedValue", m_iHoveredValue);
+
+ /* Add state transitions: */
+ QSignalTransition *pHoveredToDefault = pStateHovered->addTransition(this, SIGNAL(sigHoverLeave()), pStateDefault);
+ if (pHoveredToDefault)
+ {
+ /* Create backward animation: */
+ m_pHoveringAnimationBackward = new QPropertyAnimation(this, "animatedValue", this);
+ if (m_pHoveringAnimationBackward)
+ {
+ m_pHoveringAnimationBackward->setDuration(m_iAnimationDuration);
+ m_pHoveringAnimationBackward->setStartValue(m_iHoveredValue);
+ m_pHoveringAnimationBackward->setEndValue(m_iDefaultValue);
+
+ /* Add to transition: */
+ pHoveredToDefault->addAnimation(m_pHoveringAnimationBackward);
+ }
+ }
+ }
+
+ /* Initial state is 'default': */
+ m_pHoveringMachine->setInitialState(pStateDefault);
+ /* Start state-machine: */
+ m_pHoveringMachine->start();
+ }
+
+ /* Allocate the effect instance which we use when the item is marked as disabled: */
+ m_pDisabledEffect = new UIChooserDisabledItemEffect(1 /* Blur Radius */, model()->view());
+ if (m_pDisabledEffect)
+ {
+ setGraphicsEffect(m_pDisabledEffect);
+ m_pDisabledEffect->setEnabled(node()->isDisabled());
+ }
+ }
+}
+
+UIChooserItemGroup *UIChooserItem::toGroupItem()
+{
+ UIChooserItemGroup *pItem = qgraphicsitem_cast<UIChooserItemGroup*>(this);
+ AssertMsg(pItem, ("Trying to cast invalid item type to UIChooserItemGroup!"));
+ return pItem;
+}
+
+UIChooserItemGlobal *UIChooserItem::toGlobalItem()
+{
+ UIChooserItemGlobal *pItem = qgraphicsitem_cast<UIChooserItemGlobal*>(this);
+ AssertMsg(pItem, ("Trying to cast invalid item type to UIChooserItemGlobal!"));
+ return pItem;
+}
+
+UIChooserItemMachine *UIChooserItem::toMachineItem()
+{
+ UIChooserItemMachine *pItem = qgraphicsitem_cast<UIChooserItemMachine*>(this);
+ AssertMsg(pItem, ("Trying to cast invalid item type to UIChooserItemMachine!"));
+ return pItem;
+}
+
+UIChooserModel *UIChooserItem::model() const
+{
+ UIChooserModel *pModel = qobject_cast<UIChooserModel*>(QIGraphicsWidget::scene()->parent());
+ AssertMsg(pModel, ("Incorrect graphics scene parent set!"));
+ return pModel;
+}
+
+bool UIChooserItem::isRoot() const
+{
+ return node()->isRoot();
+}
+
+QString UIChooserItem::name() const
+{
+ return node()->name();
+}
+
+QString UIChooserItem::fullName() const
+{
+ return node()->fullName();
+}
+
+QString UIChooserItem::description() const
+{
+ return node()->description();
+}
+
+QString UIChooserItem::definition() const
+{
+ return node()->definition();
+}
+
+bool UIChooserItem::isFavorite() const
+{
+ return node()->isFavorite();
+}
+
+void UIChooserItem::setFavorite(bool fFavorite)
+{
+ node()->setFavorite(fFavorite);
+ if (m_pParent)
+ m_pParent->toGroupItem()->updateFavorites();
+}
+
+int UIChooserItem::position() const
+{
+ return node()->position();
+}
+
+bool UIChooserItem::isHovered() const
+{
+ return m_fHovered;
+}
+
+bool UIChooserItem::isSelected() const
+{
+ return m_fSelected;
+}
+
+void UIChooserItem::setSelected(bool fSelected)
+{
+ m_fSelected = fSelected;
+}
+
+void UIChooserItem::setDisabledEffect(bool fOn)
+{
+ if (m_pDisabledEffect)
+ m_pDisabledEffect->setEnabled(fOn);
+}
+
+void UIChooserItem::updateGeometry()
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::updateGeometry();
+
+ /* Update parent's geometry: */
+ if (parentItem())
+ parentItem()->updateGeometry();
+}
+
+void UIChooserItem::makeSureItsVisible()
+{
+ /* Get parrent item: */
+ UIChooserItemGroup *pParentItem = parentItem()->toGroupItem();
+ if (!pParentItem)
+ return;
+ /* If item is not visible. That is all the parent group(s) are opened (expanded): */
+ if (!isVisible())
+ {
+ /* We should make parent visible: */
+ pParentItem->makeSureItsVisible();
+ /* And make sure its opened: */
+ if (pParentItem->isClosed())
+ pParentItem->open(false);
+ }
+}
+
+UIChooserItemDragToken UIChooserItem::dragTokenPlace() const
+{
+ return m_enmDragTokenPlace;
+}
+
+void UIChooserItem::setDragTokenPlace(UIChooserItemDragToken enmPlace)
+{
+ /* Something changed? */
+ if (m_enmDragTokenPlace != enmPlace)
+ {
+ m_enmDragTokenPlace = enmPlace;
+ update();
+ }
+}
+
+void UIChooserItem::hoverMoveEvent(QGraphicsSceneHoverEvent *)
+{
+ if (!m_fHovered)
+ {
+ m_fHovered = true;
+ emit sigHoverEnter();
+ }
+ update();
+}
+
+void UIChooserItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
+{
+ if (m_fHovered)
+ {
+ m_fHovered = false;
+ emit sigHoverLeave();
+ update();
+ }
+}
+
+void UIChooserItem::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* By default, non-moveable and non-selectable items
+ * can't grab mouse-press events which is required
+ * to grab further mouse-move events which we wants... */
+ if (isRoot())
+ pEvent->ignore();
+ else
+ pEvent->accept();
+}
+
+void UIChooserItem::mouseMoveEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Make sure item is really dragged: */
+ if (QLineF(pEvent->screenPos(),
+ pEvent->buttonDownScreenPos(Qt::LeftButton)).length() <
+ QApplication::startDragDistance())
+ return;
+
+ /* Initialize dragging: */
+ QDrag *pDrag = new QDrag(pEvent->widget());
+ model()->setCurrentDragObject(pDrag);
+ pDrag->setPixmap(toPixmap());
+ pDrag->setMimeData(createMimeData());
+ pDrag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction);
+}
+
+void UIChooserItem::dragMoveEvent(QGraphicsSceneDragDropEvent *pEvent)
+{
+ /* Make sure we are non-root: */
+ if (!isRoot())
+ {
+ /* Allow drag tokens only for the same item type as current: */
+ bool fAllowDragToken = false;
+ if ((type() == UIChooserNodeType_Group &&
+ pEvent->mimeData()->hasFormat(UIChooserItemGroup::className())) ||
+ (type() == UIChooserNodeType_Machine &&
+ pEvent->mimeData()->hasFormat(UIChooserItemMachine::className())))
+ fAllowDragToken = true;
+ /* Do we need a drag-token? */
+ if (fAllowDragToken)
+ {
+ QPoint p = pEvent->pos().toPoint();
+ if (p.y() < 10)
+ setDragTokenPlace(UIChooserItemDragToken_Up);
+ else if (p.y() > minimumSizeHint().toSize().height() - 10)
+ setDragTokenPlace(UIChooserItemDragToken_Down);
+ else
+ setDragTokenPlace(UIChooserItemDragToken_Off);
+ }
+ }
+ /* Check if drop is allowed: */
+ pEvent->setAccepted(isDropAllowed(pEvent, dragTokenPlace()));
+}
+
+void UIChooserItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *)
+{
+ resetDragToken();
+}
+
+void UIChooserItem::dropEvent(QGraphicsSceneDragDropEvent *pEvent)
+{
+ /* Do we have token active? */
+ switch (dragTokenPlace())
+ {
+ case UIChooserItemDragToken_Off:
+ {
+ /* Its our drop, processing: */
+ processDrop(pEvent);
+ break;
+ }
+ default:
+ {
+ /* Its parent drop, passing: */
+ parentItem()->processDrop(pEvent, this, dragTokenPlace());
+ break;
+ }
+ }
+}
+
+/* static */
+QSize UIChooserItem::textSize(const QFont &font, QPaintDevice *pPaintDevice, const QString &strText)
+{
+ /* Make sure text is not empty: */
+ if (strText.isEmpty())
+ return QSize(0, 0);
+
+ /* Return text size, based on font-metrics: */
+ QFontMetrics fm(font, pPaintDevice);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ return QSize(fm.horizontalAdvance(strText), fm.height());
+#else
+ return QSize(fm.width(strText), fm.height());
+#endif
+}
+
+/* static */
+int UIChooserItem::textWidth(const QFont &font, QPaintDevice *pPaintDevice, int iCount)
+{
+ /* Return text width: */
+ QFontMetrics fm(font, pPaintDevice);
+ QString strString;
+ strString.fill('_', iCount);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ return fm.horizontalAdvance(strString);
+#else
+ return fm.width(strString);
+#endif
+}
+
+/* static */
+QString UIChooserItem::compressText(const QFont &font, QPaintDevice *pPaintDevice, QString strText, int iWidth)
+{
+ /* Check if passed text is empty: */
+ if (strText.isEmpty())
+ return strText;
+
+ /* Check if passed text fits maximum width: */
+ QFontMetrics fm(font, pPaintDevice);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ if (fm.horizontalAdvance(strText) <= iWidth)
+#else
+ if (fm.width(strText) <= iWidth)
+#endif
+ return strText;
+
+ /* Truncate otherwise: */
+ QString strEllipsis = QString("...");
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iEllipsisWidth = fm.horizontalAdvance(strEllipsis + " ");
+ while (!strText.isEmpty() && fm.horizontalAdvance(strText) + iEllipsisWidth > iWidth)
+ strText.truncate(strText.size() - 1);
+#else
+ int iEllipsisWidth = fm.width(strEllipsis + " ");
+ while (!strText.isEmpty() && fm.width(strText) + iEllipsisWidth > iWidth)
+ strText.truncate(strText.size() - 1);
+#endif
+ return strText + strEllipsis;
+}
+
+/* static */
+void UIChooserItem::paintFrameRect(QPainter *pPainter, bool fIsSelected, int iRadius,
+ const QRect &rectangle)
+{
+ pPainter->save();
+ QPalette pal = QApplication::palette();
+ QColor base = pal.color(QPalette::Active, fIsSelected ? QPalette::Highlight : QPalette::Window);
+ pPainter->setPen(base.darker(160));
+ if (iRadius)
+ pPainter->drawRoundedRect(rectangle, iRadius, iRadius);
+ else
+ pPainter->drawRect(rectangle);
+ pPainter->restore();
+}
+
+/* static */
+void UIChooserItem::paintPixmap(QPainter *pPainter, const QPoint &point,
+ const QPixmap &pixmap)
+{
+ pPainter->drawPixmap(point, pixmap);
+}
+
+/* static */
+void UIChooserItem::paintText(QPainter *pPainter, QPoint point,
+ const QFont &font, QPaintDevice *pPaintDevice,
+ const QString &strText)
+{
+ /* Prepare variables: */
+ QFontMetrics fm(font, pPaintDevice);
+ point += QPoint(0, fm.ascent());
+
+ /* Draw text: */
+ pPainter->save();
+ pPainter->setFont(font);
+ pPainter->drawText(point, strText);
+ pPainter->restore();
+}
+
+/* static */
+void UIChooserItem::paintFlatButton(QPainter *pPainter, const QRect &rectangle, const QPoint &cursorPosition)
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare colors: */
+ const QColor color = QApplication::palette().color(QPalette::Active, QPalette::Button);
+
+ /* Prepare pen: */
+ QPen pen;
+ pen.setColor(color);
+ pen.setWidth(0);
+ pPainter->setPen(pen);
+
+ /* Apply clipping path: */
+ QPainterPath path;
+ path.addRect(rectangle);
+ pPainter->setClipPath(path);
+
+ /* Paint active background: */
+ QRadialGradient grad(rectangle.center(), rectangle.width(), cursorPosition);
+ QColor color1 = color;
+ color1.setAlpha(50);
+ QColor color2 = color;
+ color2.setAlpha(250);
+ grad.setColorAt(0, color1);
+ grad.setColorAt(1, color2);
+ pPainter->fillRect(rectangle.adjusted(0, 0, -1, -1), grad);
+
+ /* Paint frame: */
+ pPainter->drawRect(rectangle.adjusted(0, 0, -1, -1));
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIChooserItemMimeData implementation. *
+*********************************************************************************************************************************/
+
+UIChooserItemMimeData::UIChooserItemMimeData(UIChooserItem *pItem)
+ : m_pItem(pItem)
+{
+}
+
+bool UIChooserItemMimeData::hasFormat(const QString &strMimeType) const
+{
+ if (strMimeType == QString(m_pItem->metaObject()->className()))
+ return true;
+ return false;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItem.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItem.h
new file mode 100644
index 00000000..cf8ba17c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItem.h
@@ -0,0 +1,387 @@
+/* $Id: UIChooserItem.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserItem class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserItem_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserItem_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QGraphicsEffect>
+#include <QMimeData>
+#include <QRectF>
+#include <QString>
+#include <QUuid>
+
+/* GUI includes: */
+#include "QIGraphicsWidget.h"
+#include "QIWithRetranslateUI.h"
+#include "UIChooserDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+/* Forward declaration: */
+class QGraphicsSceneHoverEvent;
+class QGraphicsSceneMouseEvent;
+class QGraphicsSceneDragDropEvent;
+class QPropertyAnimation;
+class QStateMachine;
+class UIActionPool;
+class UIChooserItemGroup;
+class UIChooserItemGlobal;
+class UIChooserItemMachine;
+class UIChooserModel;
+class UIChooserNode;
+
+
+/** A simple QGraphicsEffect extension to mark disabled UIChooserItem.
+ * @note Applies blur and gray scale filters. */
+class UIChooserDisabledItemEffect : public QGraphicsEffect
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs blur effect passing @a pParent to the base-class.
+ * @param iBlurRadius Brings the blur effect radius. */
+ UIChooserDisabledItemEffect(int iBlurRadius, QObject *pParent = 0);
+
+protected:
+
+ /** Draws effect with passed @a pPainter. */
+ virtual void draw(QPainter *pPainter);
+
+private:
+
+ /** Holds the blur effect radius. */
+ int m_iBlurRadius;
+};
+
+
+/** QIGraphicsWidget extension used as interface
+ * for graphics chooser model/view architecture. */
+class UIChooserItem : public QIWithRetranslateUI4<QIGraphicsWidget>
+{
+ Q_OBJECT;
+ Q_PROPERTY(int animatedValue READ animatedValue WRITE setAnimatedValue);
+
+signals:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Notifies listeners about hover enter. */
+ void sigHoverEnter();
+ /** Notifies listeners about hover leave. */
+ void sigHoverLeave();
+ /** @} */
+
+public:
+
+ /** Constructs item passing @a pParent to the base-class.
+ * @param pNode Brings the node this item is built for.
+ * @param iDefaultValue Brings default value for hovering animation.
+ * @param iHoveredValue Brings hovered value for hovering animation. */
+ UIChooserItem(UIChooserItem *pParent, UIChooserNode *pNode,
+ int iDefaultValue = 0, int iHoveredValue = 100);
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns parent reference. */
+ UIChooserItem *parentItem() const { return m_pParent; }
+ /** Returns node reference. */
+ UIChooserNode *node() const { return m_pNode; }
+
+ /** Casts item to group one. */
+ UIChooserItemGroup *toGroupItem();
+ /** Casts item to global one. */
+ UIChooserItemGlobal *toGlobalItem();
+ /** Casts item to machine one. */
+ UIChooserItemMachine *toMachineItem();
+
+ /** Returns model reference. */
+ UIChooserModel *model() const;
+
+ /** Returns whether item is root. */
+ bool isRoot() const;
+
+ /** Returns item name. */
+ QString name() const;
+ /** Returns item full-name. */
+ QString fullName() const;
+ /** Returns item description. */
+ QString description() const;
+ /** Returns item definition. */
+ QString definition() const;
+
+ /** Returns whether item is favorite. */
+ bool isFavorite() const;
+ /** Defines whether item is @a fFavorite. */
+ virtual void setFavorite(bool fFavorite);
+
+ /** Returns item position. */
+ int position() const;
+
+ /** Returns whether item is hovered. */
+ bool isHovered() const;
+
+ /** Returns whether item is selected.
+ * @note Sometimes it's useful to know whether item is selected in model above. */
+ virtual bool isSelected() const;
+ /** Defines item as @a fSelected.
+ * @note Don't forget to call for base-class method when reimplementing it. */
+ virtual void setSelected(bool fSelected);
+
+ /** Starts item editing. */
+ virtual void startEditing() = 0;
+
+ /** Updates item. */
+ virtual void updateItem() = 0;
+ /** Updates item tool-tip. */
+ virtual void updateToolTip() = 0;
+
+ /** Installs event-filter for @a pSource object.
+ * @note Base-class implementation does nothing. */
+ virtual void installEventFilterHelper(QObject *pSource) { Q_UNUSED(pSource); }
+ /** Defines whether visual effect for disabled item is @a fOn. */
+ void setDisabledEffect(bool fOn);
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Returns children items of certain @a enmType. */
+ virtual QList<UIChooserItem*> items(UIChooserNodeType enmType = UIChooserNodeType_Any) const = 0;
+
+ /** Adds possible @a fFavorite child @a pItem to certain @a iPosition. */
+ virtual void addItem(UIChooserItem *pItem, bool fFavorite, int iPosition) = 0;
+ /** Removes child @a pItem. */
+ virtual void removeItem(UIChooserItem *pItem) = 0;
+
+ /** Searches for a first child item answering to specified @a strSearchTag and @a iSearchFlags. */
+ virtual UIChooserItem *searchForItem(const QString &strSearchTag, int iSearchFlags) = 0;
+
+ /** Searches for a first machine child item. */
+ virtual UIChooserItem *firstMachineItem() = 0;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates geometry. */
+ virtual void updateGeometry() RT_OVERRIDE;
+
+ /** Updates layout. */
+ virtual void updateLayout() = 0;
+
+ /** Returns minimum width-hint. */
+ virtual int minimumWidthHint() const = 0;
+ /** Returns minimum height-hint. */
+ virtual int minimumHeightHint() const = 0;
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Makes sure item is visible. */
+ virtual void makeSureItsVisible();
+
+ /** Returns pixmap item representation. */
+ virtual QPixmap toPixmap() = 0;
+
+ /** Returns whether item drop is allowed.
+ * @param pEvent Brings information about drop event.
+ * @param enmPlace Brings the place of drag token to the drop moment. */
+ virtual bool isDropAllowed(QGraphicsSceneDragDropEvent *pEvent, UIChooserItemDragToken enmPlace = UIChooserItemDragToken_Off) const = 0;
+ /** Processes item drop.
+ * @param pEvent Brings information about drop event.
+ * @param pFromWho Brings the item according to which we choose drop position.
+ * @param enmPlace Brings the place of drag token to the drop moment (according to item mentioned above). */
+ virtual void processDrop(QGraphicsSceneDragDropEvent *pEvent, UIChooserItem *pFromWho = 0, UIChooserItemDragToken enmPlace = UIChooserItemDragToken_Off) = 0;
+ /** Reset drag token. */
+ virtual void resetDragToken() = 0;
+
+ /** Returns drag token place. */
+ UIChooserItemDragToken dragTokenPlace() const;
+ /** Defines drag token @a enmPlace. */
+ void setDragTokenPlace(UIChooserItemDragToken enmPlace);
+ /** @} */
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles hover enter @a event. */
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+ /** Handles hover leave @a event. */
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse press @a event. */
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse move @a event. */
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles drag move @a event. */
+ virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *pEvent) RT_OVERRIDE;
+ /** Handles drag leave @a event. */
+ virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent *pEvent) RT_OVERRIDE;
+ /** Handles drop @a event. */
+ virtual void dropEvent(QGraphicsSceneDragDropEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns item's default animation value. */
+ int defaultValue() const { return m_iDefaultValue; }
+ /** Defines item's default animation @a iValue. */
+ void setDefaultValue(int iValue) { m_iDefaultValue = iValue; update(); }
+
+ /** Returns item's hovered animation value. */
+ int hoveredValue() const { return m_iHoveredValue; }
+ /** Defines item's hovered animation @a iValue. */
+ void setHoveredValue(int iValue) { m_iHoveredValue = iValue; update(); }
+
+ /** Returns item's animated value. */
+ int animatedValue() const { return m_iAnimatedValue; }
+ /** Defines item's animated @a iValue. */
+ void setAnimatedValue(int iValue) { m_iAnimatedValue = iValue; update(); }
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Returns previous geometry. */
+ QRectF previousGeometry() const { return m_previousGeometry; }
+ /** Defines previous @a geometry. */
+ void setPreviousGeometry(const QRectF &geometry) { m_previousGeometry = geometry; }
+
+ /** Returns @a strText size calculated on the basis of certain @a font and @a pPaintDevice. */
+ static QSize textSize(const QFont &font, QPaintDevice *pPaintDevice, const QString &strText);
+ /** Returns a width of line containing @a iCount of chars calculated on the basis of certain @a font and @a pPaintDevice. */
+ static int textWidth(const QFont &font, QPaintDevice *pPaintDevice, int iCount);
+ /** Compresses @a strText to @a iWidth on the basis of certain @a font and @a pPaintDevice. */
+ static QString compressText(const QFont &font, QPaintDevice *pPaintDevice, QString strText, int iWidth);
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Returns D&D mime data. */
+ virtual QMimeData *createMimeData() = 0;
+
+ /** Returns drag token darkness. */
+ int dragTokenDarkness() const { return m_iDragTokenDarkness; }
+ /** @} */
+
+ /** @name Painting stuff.
+ * @{ */
+ /** Paints frame @a rectangle using passed @a pPainter.
+ * @param fIsSelected Brings whether this rectangle should be filled.
+ * @param iRadius Brings the radius of rounded corners. */
+ static void paintFrameRect(QPainter *pPainter, bool fIsSelected, int iRadius,
+ const QRect &rectangle);
+ /** Paints @a pixmap using passed @a pPainter putting its upper-left corner to specified @a point. */
+ static void paintPixmap(QPainter *pPainter, const QPoint &point,
+ const QPixmap &pixmap);
+ /** Paints @a strText using passed @a pPainter putting its upper-left corner to specified @a point.
+ * @param font Brings the text font.
+ * @param pPaintDevice Brings the paint-device reference to initilize painting from. */
+ static void paintText(QPainter *pPainter, QPoint point,
+ const QFont &font, QPaintDevice *pPaintDevice,
+ const QString &strText);
+ /** Paints flat button @a rectangle using passed @a pPainter moving light focus according to passed @a cursorPosition. */
+ static void paintFlatButton(QPainter *pPainter, const QRect &rectangle, const QPoint &cursorPosition);
+ /** @} */
+
+private:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Holds the item's parent item. */
+ UIChooserItem *m_pParent;
+ /** Holds the node this item is built for. */
+ UIChooserNode *m_pNode;
+
+ /** Holds whether item is hovered. */
+ bool m_fHovered;
+ /** Holds whether item is selected. */
+ bool m_fSelected;
+ /** Holds the hovering animation machine instance. */
+ QStateMachine *m_pHoveringMachine;
+ /** Holds the forward hovering animation instance. */
+ QPropertyAnimation *m_pHoveringAnimationForward;
+ /** Holds the backward hovering animation instance. */
+ QPropertyAnimation *m_pHoveringAnimationBackward;
+ /** Holds the animation duration. */
+ int m_iAnimationDuration;
+ /** Holds the default animation value. */
+ int m_iDefaultValue;
+ /** Holds the hovered animation value. */
+ int m_iHoveredValue;
+ /** Holds the animated value. */
+ int m_iAnimatedValue;
+ /** Holds the blur effect instance. */
+ UIChooserDisabledItemEffect *m_pDisabledEffect;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds previous geometry. */
+ QRectF m_previousGeometry;
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Holds drag token place. */
+ UIChooserItemDragToken m_enmDragTokenPlace;
+
+ /** Holds drag token darkness. */
+ int m_iDragTokenDarkness;
+ /** @} */
+};
+
+
+/** QMimeData for graphics item interface. */
+class UIChooserItemMimeData : public QMimeData
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs mime-data on the basis of passed @a pItem. */
+ UIChooserItemMimeData(UIChooserItem *pItem);
+
+ /** Returns cached item. */
+ UIChooserItem *item() const { return m_pItem; }
+
+ /** Constructs mime-data on the basis of passed @a pItem. */
+ virtual bool hasFormat(const QString &strMimeType) const RT_OVERRIDE;
+
+private:
+
+ /** Holds the cached item. */
+ UIChooserItem *m_pItem;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserItem_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGlobal.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGlobal.cpp
new file mode 100644
index 00000000..7885da16
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGlobal.cpp
@@ -0,0 +1,861 @@
+/* $Id: UIChooserItemGlobal.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserItemGlobal class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsView>
+#include <QPainter>
+#include <QStyleOptionGraphicsItem>
+
+/* GUI includes: */
+#include "UIChooserItemGlobal.h"
+#include "UIChooserModel.h"
+#include "UIChooserNodeGlobal.h"
+#include "UIIconPool.h"
+#include "UIVirtualBoxManager.h"
+
+
+UIChooserItemGlobal::UIChooserItemGlobal(UIChooserItem *pParent, UIChooserNodeGlobal *pNode)
+ : UIChooserItem(pParent, pNode)
+#ifdef VBOX_WS_MAC
+ , m_iDefaultDarknessStart(0)
+ , m_iDefaultDarknessFinal(0)
+#endif
+ , m_iHoverLightnessStart(0)
+ , m_iHoverLightnessFinal(0)
+ , m_iHighlightLightnessStart(0)
+ , m_iHighlightLightnessFinal(0)
+ , m_iMinimumNameWidth(0)
+ , m_iMaximumNameWidth(0)
+ , m_iHeightHint(0)
+{
+ prepare();
+}
+
+UIChooserItemGlobal::~UIChooserItemGlobal()
+{
+ cleanup();
+}
+
+UIChooserNodeGlobal *UIChooserItemGlobal::nodeToGlobalType() const
+{
+ return node() ? node()->toGlobalNode() : 0;
+}
+
+bool UIChooserItemGlobal::isToolButtonArea(const QPoint &position, int iMarginMultiplier /* = 1 */) const
+{
+ const int iFullWidth = geometry().width();
+ const int iFullHeight = geometry().height();
+ const int iMarginHR = data(GlobalItemData_MarginHR).toInt();
+ const int iButtonMargin = data(GlobalItemData_ButtonMargin).toInt();
+ const int iToolPixmapX = iFullWidth - iMarginHR - 1
+ - m_toolPixmap.width() / m_toolPixmap.devicePixelRatio();
+ const int iToolPixmapY = (iFullHeight - m_toolPixmap.height() / m_toolPixmap.devicePixelRatio()) / 2;
+ QRect rect = QRect(iToolPixmapX,
+ iToolPixmapY,
+ m_toolPixmap.width() / m_toolPixmap.devicePixelRatio(),
+ m_toolPixmap.height() / m_toolPixmap.devicePixelRatio());
+ rect.adjust(-iMarginMultiplier * iButtonMargin, -iMarginMultiplier * iButtonMargin,
+ iMarginMultiplier * iButtonMargin, iMarginMultiplier * iButtonMargin);
+ return rect.contains(position);
+}
+
+bool UIChooserItemGlobal::isPinButtonArea(const QPoint &position, int iMarginMultiplier /* = 1 */) const
+{
+ const int iFullWidth = geometry().width();
+ const int iFullHeight = geometry().height();
+ const int iMarginHR = data(GlobalItemData_MarginHR).toInt();
+ const int iSpacing = data(GlobalItemData_Spacing).toInt();
+ const int iButtonMargin = data(GlobalItemData_ButtonMargin).toInt();
+ const int iPinPixmapX = iFullWidth - iMarginHR - 1
+ - m_toolPixmap.width() / m_toolPixmap.devicePixelRatio()
+ - iSpacing
+ - m_pinPixmap.width() / m_pinPixmap.devicePixelRatio();
+ const int iPinPixmapY = (iFullHeight - m_pinPixmap.height() / m_pinPixmap.devicePixelRatio()) / 2;
+ QRect rect = QRect(iPinPixmapX,
+ iPinPixmapY,
+ m_pinPixmap.width() / m_pinPixmap.devicePixelRatio(),
+ m_pinPixmap.height() / m_pinPixmap.devicePixelRatio());
+ rect.adjust(-iMarginMultiplier * iButtonMargin, -iMarginMultiplier * iButtonMargin,
+ iMarginMultiplier * iButtonMargin, iMarginMultiplier * iButtonMargin);
+ return rect.contains(position);
+}
+
+int UIChooserItemGlobal::heightHint() const
+{
+ return m_iHeightHint;
+}
+
+void UIChooserItemGlobal::setHeightHint(int iHint)
+{
+ /* Remember a new hint: */
+ m_iHeightHint = iHint;
+
+ /* Update geometry and the model layout: */
+ updateGeometry();
+ model()->updateLayout();
+}
+
+void UIChooserItemGlobal::retranslateUi()
+{
+}
+
+void UIChooserItemGlobal::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIChooserItem::showEvent(pEvent);
+
+ /* Update pixmaps: */
+ updatePixmaps();
+}
+
+void UIChooserItemGlobal::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIChooserItem::resizeEvent(pEvent);
+
+ /* What is the new geometry? */
+ const QRectF newGeometry = geometry();
+
+ /* Should we update visible name? */
+ if (previousGeometry().width() != newGeometry.width())
+ updateMaximumNameWidth();
+
+ /* Remember the new geometry: */
+ setPreviousGeometry(newGeometry);
+}
+
+void UIChooserItemGlobal::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIChooserItem::mousePressEvent(pEvent);
+ /* No drag at all: */
+ pEvent->ignore();
+}
+
+void UIChooserItemGlobal::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget * /* pWidget = 0 */)
+{
+ /* Acquire rectangle: */
+ const QRect rectangle = pOptions->rect;
+
+ /* Paint background: */
+ paintBackground(pPainter, rectangle);
+ /* Paint frame: */
+ paintFrame(pPainter, rectangle);
+ /* Paint global info: */
+ paintGlobalInfo(pPainter, rectangle);
+}
+
+void UIChooserItemGlobal::setFavorite(bool fFavorite)
+{
+ /* Call to base-class: */
+ UIChooserItem::setFavorite(fFavorite);
+
+ /* Update pin-pixmap: */
+ updatePinPixmap();
+}
+
+void UIChooserItemGlobal::startEditing()
+{
+ AssertMsgFailed(("Global graphics item do NOT support editing yet!"));
+}
+
+void UIChooserItemGlobal::updateItem()
+{
+ /* Update this global-item: */
+ updatePixmaps();
+ updateMinimumNameWidth();
+ updateVisibleName();
+ updateToolTip();
+ update();
+
+ /* Update parent group-item: */
+ parentItem()->updateToolTip();
+ parentItem()->update();
+}
+
+void UIChooserItemGlobal::updateToolTip()
+{
+ // Nothing for now..
+}
+
+QList<UIChooserItem*> UIChooserItemGlobal::items(UIChooserNodeType) const
+{
+ AssertMsgFailedReturn(("Global graphics item do NOT support children!"), QList<UIChooserItem*>());
+}
+
+void UIChooserItemGlobal::addItem(UIChooserItem *, bool, int)
+{
+ AssertMsgFailed(("Global graphics item do NOT support children!"));
+}
+
+void UIChooserItemGlobal::removeItem(UIChooserItem *)
+{
+ AssertMsgFailed(("Global graphics item do NOT support children!"));
+}
+
+UIChooserItem *UIChooserItemGlobal::searchForItem(const QString &, int iSearchFlags)
+{
+ /* Ignore if we are not searching for the global-item: */
+ if (!(iSearchFlags & UIChooserItemSearchFlag_Global))
+ return 0;
+
+ /* Returning this: */
+ return this;
+}
+
+UIChooserItem *UIChooserItemGlobal::firstMachineItem()
+{
+ return 0;
+}
+
+void UIChooserItemGlobal::updateLayout()
+{
+ // Just do nothing ..
+}
+
+int UIChooserItemGlobal::minimumWidthHint() const
+{
+ /* Prepare variables: */
+ const int iMarginHL = data(GlobalItemData_MarginHL).toInt();
+ const int iMarginHR = data(GlobalItemData_MarginHR).toInt();
+ const int iSpacing = data(GlobalItemData_Spacing).toInt();
+
+ /* Calculating proposed width: */
+ int iProposedWidth = 0;
+
+ /* Two margins: */
+ iProposedWidth += iMarginHL + iMarginHR;
+ /* And global-item content width: */
+ iProposedWidth += (m_pixmapSize.width() +
+ iSpacing +
+ m_iMinimumNameWidth +
+ iSpacing +
+ m_toolPixmapSize.width() +
+ iSpacing +
+ m_pinPixmapSize.width());
+
+ /* Return result: */
+ return iProposedWidth;
+}
+
+int UIChooserItemGlobal::minimumHeightHint() const
+{
+ /* Prepare variables: */
+ const int iMarginV = data(GlobalItemData_MarginV).toInt();
+
+ /* Calculating proposed height: */
+ int iProposedHeight = 0;
+
+ /* Global-item content height: */
+ int iContentHeight = qMax(m_pixmapSize.height(), m_visibleNameSize.height());
+ iContentHeight = qMax(iContentHeight, m_toolPixmapSize.height());
+ iContentHeight = qMax(iContentHeight, m_pinPixmapSize.height());
+
+ /* If we have height hint: */
+ if (m_iHeightHint)
+ {
+ /* Take the largest value between height hint and content height: */
+ iProposedHeight += qMax(m_iHeightHint, iContentHeight);
+ }
+ /* Otherwise: */
+ else
+ {
+ /* Two margins: */
+ iProposedHeight += 2 * iMarginV;
+ /* And content height: */
+ iProposedHeight += iContentHeight;
+ }
+
+ /* Return result: */
+ return iProposedHeight;
+}
+
+QSizeF UIChooserItemGlobal::sizeHint(Qt::SizeHint which, const QSizeF &constraint /* = QSizeF() */) const
+{
+ /* If Qt::MinimumSize requested: */
+ if (which == Qt::MinimumSize)
+ return QSizeF(minimumWidthHint(), minimumHeightHint());
+ /* Else call to base-class: */
+ return UIChooserItem::sizeHint(which, constraint);
+}
+
+QPixmap UIChooserItemGlobal::toPixmap()
+{
+ AssertFailedReturn(QPixmap());
+}
+
+bool UIChooserItemGlobal::isDropAllowed(QGraphicsSceneDragDropEvent *, UIChooserItemDragToken) const
+{
+ /* No drops at all: */
+ return false;
+}
+
+void UIChooserItemGlobal::processDrop(QGraphicsSceneDragDropEvent *, UIChooserItem *, UIChooserItemDragToken)
+{
+ /* Nothing to process: */
+}
+
+void UIChooserItemGlobal::resetDragToken()
+{
+ /* Nothing to process: */
+}
+
+QMimeData *UIChooserItemGlobal::createMimeData()
+{
+ /* Nothing to return: */
+ return 0;
+}
+
+void UIChooserItemGlobal::sltHandleWindowRemapped()
+{
+ updatePixmaps();
+}
+
+void UIChooserItemGlobal::prepare()
+{
+ /* Color tones: */
+#if defined(VBOX_WS_MAC)
+ m_iDefaultDarknessStart = 105;
+ m_iDefaultDarknessFinal = 115;
+ m_iHoverLightnessStart = 125;
+ m_iHoverLightnessFinal = 115;
+ m_iHighlightLightnessStart = 115;
+ m_iHighlightLightnessFinal = 105;
+#elif defined(VBOX_WS_WIN)
+ m_iHoverLightnessStart = 220;
+ m_iHoverLightnessFinal = 210;
+ m_iHighlightLightnessStart = 190;
+ m_iHighlightLightnessFinal = 180;
+#else /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+ m_iHoverLightnessStart = 125;
+ m_iHoverLightnessFinal = 115;
+ m_iHighlightLightnessStart = 110;
+ m_iHighlightLightnessFinal = 100;
+#endif /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+
+ /* Fonts: */
+ m_nameFont = font();
+ m_nameFont.setWeight(QFont::Bold);
+
+ /* Sizes: */
+ m_iMinimumNameWidth = 0;
+ m_iMaximumNameWidth = 0;
+
+ /* Add item to the parent: */
+ AssertPtrReturnVoid(parentItem());
+ parentItem()->addItem(this, isFavorite(), position());
+
+ /* Configure connections: */
+ connect(gpManager, &UIVirtualBoxManager::sigWindowRemapped,
+ this, &UIChooserItemGlobal::sltHandleWindowRemapped);
+
+ /* Init: */
+ updatePixmaps();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIChooserItemGlobal::cleanup()
+{
+ /* If that item is current: */
+ if (model()->currentItem() == this)
+ {
+ /* Unset current-item: */
+ model()->setCurrentItem(0);
+ }
+ /* If that item is in selection list: */
+ if (model()->selectedItems().contains(this))
+ {
+ /* Remove item from the selection list: */
+ model()->removeFromSelectedItems(this);
+ }
+ /* If that item is in navigation list: */
+ if (model()->navigationItems().contains(this))
+ {
+ /* Remove item from the navigation list: */
+ model()->removeFromNavigationItems(this);
+ }
+
+ /* Remove item from the parent: */
+ AssertPtrReturnVoid(parentItem());
+ parentItem()->removeItem(this);
+}
+
+QVariant UIChooserItemGlobal::data(int iKey) const
+{
+ /* Provide other members with required data: */
+ switch (iKey)
+ {
+ /* Layout hints: */
+ case GlobalItemData_MarginHL: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ case GlobalItemData_MarginHR: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4 * 5;
+ case GlobalItemData_MarginV: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4 * 3;
+ case GlobalItemData_Spacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+ case GlobalItemData_ButtonMargin: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Default: */
+ default: break;
+ }
+ return QVariant();
+}
+
+void UIChooserItemGlobal::updatePixmaps()
+{
+ /* Update pixmap: */
+ updatePixmap();
+ /* Update tool-pixmap: */
+ updateToolPixmap();
+ /* Update pin-pixmap: */
+ updatePinPixmap();
+}
+
+void UIChooserItemGlobal::updatePixmap()
+{
+ /* Acquire new metric, then compose pixmap-size: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
+ const QSize pixmapSize = QSize(iMetric, iMetric);
+
+ /* Create new icon, then acquire pixmap: */
+ const QIcon icon = UIIconPool::iconSet(":/tools_global_32px.png");
+ const QPixmap pixmap = icon.pixmap(gpManager->windowHandle(), pixmapSize);
+
+ /* Update linked values: */
+ if (m_pixmapSize != pixmapSize)
+ {
+ m_pixmapSize = pixmapSize;
+ updateMaximumNameWidth();
+ updateGeometry();
+ }
+ if (m_pixmap.toImage() != pixmap.toImage())
+ {
+ m_pixmap = pixmap;
+ update();
+ }
+}
+
+void UIChooserItemGlobal::updateToolPixmap()
+{
+ /* Determine icon metric: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize) * .75;
+ /* Create new tool-pixmap and tool-pixmap size: */
+ const QIcon toolIcon = UIIconPool::iconSet(":/tools_menu_24px.png");
+ AssertReturnVoid(!toolIcon.isNull());
+ const QSize toolPixmapSize = QSize(iIconMetric, iIconMetric);
+ const QPixmap toolPixmap = toolIcon.pixmap(gpManager->windowHandle(), toolPixmapSize);
+ /* Update linked values: */
+ if (m_toolPixmapSize != toolPixmapSize)
+ {
+ m_toolPixmapSize = toolPixmapSize;
+ updateGeometry();
+ }
+ if (m_toolPixmap.toImage() != toolPixmap.toImage())
+ {
+ m_toolPixmap = toolPixmap;
+ update();
+ }
+}
+
+void UIChooserItemGlobal::updatePinPixmap()
+{
+ /* Determine icon metric: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize) * .75;
+ /* Create new tool-pixmap and tool-pixmap size: */
+ const QIcon pinIcon = UIIconPool::iconSet(isFavorite() ? ":/favorite_pressed_24px.png" : ":/favorite_24px.png");
+ AssertReturnVoid(!pinIcon.isNull());
+ const QSize pinPixmapSize = QSize(iIconMetric, iIconMetric);
+ const QPixmap pinPixmap = pinIcon.pixmap(gpManager->windowHandle(), pinPixmapSize);
+ /* Update linked values: */
+ if (m_pinPixmapSize != pinPixmapSize)
+ {
+ m_pinPixmapSize = pinPixmapSize;
+ updateGeometry();
+ }
+ if (m_pinPixmap.toImage() != pinPixmap.toImage())
+ {
+ m_pinPixmap = pinPixmap;
+ update();
+ }
+}
+
+void UIChooserItemGlobal::updateMinimumNameWidth()
+{
+ /* Calculate new minimum name width: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+ const QFontMetrics fm(m_nameFont, pPaintDevice);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const int iMinimumNameWidth = fm.horizontalAdvance(compressText(m_nameFont, pPaintDevice, name(),
+ textWidth(m_nameFont, pPaintDevice, 15)));
+#else
+ const int iMinimumNameWidth = fm.width(compressText(m_nameFont, pPaintDevice, name(), textWidth(m_nameFont, pPaintDevice, 15)));
+#endif
+
+ /* Is there something changed? */
+ if (m_iMinimumNameWidth == iMinimumNameWidth)
+ return;
+
+ /* Update linked values: */
+ m_iMinimumNameWidth = iMinimumNameWidth;
+ updateGeometry();
+}
+
+void UIChooserItemGlobal::updateMaximumNameWidth()
+{
+ /* Prepare variables: */
+ const int iMarginHL = data(GlobalItemData_MarginHL).toInt();
+ const int iMarginHR = data(GlobalItemData_MarginHR).toInt();
+ const int iSpacing = data(GlobalItemData_Spacing).toInt();
+
+ /* Calculate new maximum name width: */
+ int iMaximumNameWidth = (int)geometry().width();
+ iMaximumNameWidth -= iMarginHL; /* left margin */
+ iMaximumNameWidth -= m_pixmapSize.width(); /* pixmap width */
+ iMaximumNameWidth -= iSpacing; /* spacing between pixmap and name */
+ iMaximumNameWidth -= iMarginHR; /* right margin */
+
+ /* Is there something changed? */
+ if (m_iMaximumNameWidth == iMaximumNameWidth)
+ return;
+
+ /* Update linked values: */
+ m_iMaximumNameWidth = iMaximumNameWidth;
+ updateVisibleName();
+}
+
+void UIChooserItemGlobal::updateVisibleName()
+{
+ /* Prepare variables: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+
+ /* Calculate new visible name and name-size: */
+ const QString strVisibleName = compressText(m_nameFont, pPaintDevice, name(), m_iMaximumNameWidth);
+ const QSize visibleNameSize = textSize(m_nameFont, pPaintDevice, strVisibleName);
+
+ /* Update linked values: */
+ if (m_visibleNameSize != visibleNameSize)
+ {
+ m_visibleNameSize = visibleNameSize;
+ updateGeometry();
+ }
+ if (m_strVisibleName != strVisibleName)
+ {
+ m_strVisibleName = strVisibleName;
+ update();
+ }
+}
+
+void UIChooserItemGlobal::paintBackground(QPainter *pPainter, const QRect &rectangle)
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare color: */
+ const QPalette pal = QApplication::palette();
+
+ /* Selected-item background: */
+ if (model()->selectedItems().contains(this))
+ {
+ /* Prepare color: */
+ const QColor backgroundColor = pal.color(QPalette::Active, QPalette::Highlight);
+ /* Draw gradient: */
+ QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
+ bgGrad.setColorAt(0, backgroundColor.lighter(m_iHighlightLightnessStart));
+ bgGrad.setColorAt(1, backgroundColor.lighter(m_iHighlightLightnessFinal));
+ pPainter->fillRect(rectangle, bgGrad);
+
+ if (isHovered())
+ {
+ /* Prepare color: */
+ QColor animationColor1 = QColor(Qt::white);
+ QColor animationColor2 = QColor(Qt::white);
+#ifdef VBOX_WS_MAC
+ animationColor1.setAlpha(90);
+#else
+ animationColor1.setAlpha(30);
+#endif
+ animationColor2.setAlpha(0);
+ /* Draw hovered-item animated gradient: */
+ QRect animatedRect = rectangle;
+ animatedRect.setWidth(animatedRect.height());
+ const int iLength = 2 * animatedRect.width() + rectangle.width();
+ const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
+ animatedRect.moveLeft(iShift);
+ QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
+ bgAnimatedGrad.setColorAt(0, animationColor2);
+ bgAnimatedGrad.setColorAt(0.1, animationColor2);
+ bgAnimatedGrad.setColorAt(0.5, animationColor1);
+ bgAnimatedGrad.setColorAt(0.9, animationColor2);
+ bgAnimatedGrad.setColorAt(1, animationColor2);
+ pPainter->fillRect(rectangle, bgAnimatedGrad);
+ }
+ }
+ /* Hovered-item background: */
+ else if (isHovered())
+ {
+ /* Prepare color: */
+ const QColor backgroundColor = pal.color(QPalette::Active, QPalette::Highlight);
+ /* Draw gradient: */
+ QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
+ bgGrad.setColorAt(0, backgroundColor.lighter(m_iHoverLightnessStart));
+ bgGrad.setColorAt(1, backgroundColor.lighter(m_iHoverLightnessFinal));
+ pPainter->fillRect(rectangle, bgGrad);
+
+ /* Prepare color: */
+ QColor animationColor1 = QColor(Qt::white);
+ QColor animationColor2 = QColor(Qt::white);
+#ifdef VBOX_WS_MAC
+ animationColor1.setAlpha(120);
+#else
+ animationColor1.setAlpha(50);
+#endif
+ animationColor2.setAlpha(0);
+ /* Draw hovered-item animated gradient: */
+ QRect animatedRect = rectangle;
+ animatedRect.setWidth(animatedRect.height());
+ const int iLength = 2 * animatedRect.width() + rectangle.width();
+ const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
+ animatedRect.moveLeft(iShift);
+ QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
+ bgAnimatedGrad.setColorAt(0, animationColor2);
+ bgAnimatedGrad.setColorAt(0.1, animationColor2);
+ bgAnimatedGrad.setColorAt(0.5, animationColor1);
+ bgAnimatedGrad.setColorAt(0.9, animationColor2);
+ bgAnimatedGrad.setColorAt(1, animationColor2);
+ pPainter->fillRect(rectangle, bgAnimatedGrad);
+ }
+ /* Default background: */
+ else
+ {
+#ifdef VBOX_WS_MAC
+ /* Prepare color: */
+ const QColor backgroundColor = pal.color(QPalette::Active, QPalette::Window);
+ /* Draw gradient: */
+ QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
+ bgGrad.setColorAt(0, backgroundColor.darker(m_iDefaultDarknessStart));
+ bgGrad.setColorAt(1, backgroundColor.darker(m_iDefaultDarknessFinal));
+ pPainter->fillRect(rectangle, bgGrad);
+#else
+ /* Draw simple background: */
+ pPainter->fillRect(rectangle, pal.color(QPalette::Active, QPalette::Window));
+#endif
+ }
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIChooserItemGlobal::paintFrame(QPainter *pPainter, const QRect &rectangle)
+{
+ /* Only selected and/or hovered item should have a frame: */
+ if (!model()->selectedItems().contains(this) && !isHovered())
+ return;
+
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare color: */
+ const QPalette pal = QApplication::palette();
+ QColor strokeColor;
+
+ /* Selected-item frame: */
+ if (model()->selectedItems().contains(this))
+ strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightnessStart - 40);
+ /* Hovered-item frame: */
+ else if (isHovered())
+ strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHoverLightnessStart - 40);
+
+ /* Create/assign pen: */
+ QPen pen(strokeColor);
+ pen.setWidth(0);
+ pPainter->setPen(pen);
+
+ /* Draw borders: */
+ pPainter->drawLine(rectangle.topLeft(), rectangle.topRight() + QPoint(1, 0));
+ pPainter->drawLine(rectangle.bottomLeft(), rectangle.bottomRight() + QPoint(1, 0));
+ pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIChooserItemGlobal::paintGlobalInfo(QPainter *pPainter, const QRect &rectangle)
+{
+ /* Prepare variables: */
+ const int iFullWidth = rectangle.width();
+ const int iFullHeight = rectangle.height();
+ const int iMarginHL = data(GlobalItemData_MarginHL).toInt();
+ const int iMarginHR = data(GlobalItemData_MarginHR).toInt();
+ const int iSpacing = data(GlobalItemData_Spacing).toInt();
+ const int iButtonMargin = data(GlobalItemData_ButtonMargin).toInt();
+
+ /* Selected or hovered item foreground: */
+ if (model()->selectedItems().contains(this) || isHovered())
+ {
+ /* Prepare palette: */
+ const QPalette pal = QApplication::palette();
+
+ /* Get background color: */
+ const QColor highlight = pal.color(QPalette::Active, QPalette::Highlight);
+ const QColor background = model()->selectedItems().contains(this)
+ ? highlight.lighter(m_iHighlightLightnessStart)
+ : highlight.lighter(m_iHoverLightnessStart);
+
+ /* Get foreground color: */
+ const QColor simpleText = pal.color(QPalette::Active, QPalette::Text);
+ const QColor highlightText = pal.color(QPalette::Active, QPalette::HighlightedText);
+ QColor lightText = simpleText.black() < highlightText.black() ? simpleText : highlightText;
+ QColor darkText = simpleText.black() > highlightText.black() ? simpleText : highlightText;
+ if (lightText.black() > 128)
+ lightText = QColor(Qt::white);
+ if (darkText.black() < 128)
+ darkText = QColor(Qt::black);
+
+ /* Gather foreground color for background one: */
+ double dLuminance = (0.299 * background.red() + 0.587 * background.green() + 0.114 * background.blue()) / 255;
+ //printf("luminance = %f\n", dLuminance);
+ if (dLuminance > 0.5)
+ pPainter->setPen(darkText);
+ else
+ pPainter->setPen(lightText);
+ }
+
+ /* Calculate indents: */
+ int iLeftColumnIndent = iMarginHL;
+
+ /* Paint left column: */
+ {
+ /* Prepare variables: */
+ const int iGlobalPixmapX = iLeftColumnIndent;
+ const int iGlobalPixmapY = (iFullHeight - m_pixmap.height() / m_pixmap.devicePixelRatio()) / 2;
+
+ /* Paint pixmap: */
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iGlobalPixmapX, iGlobalPixmapY),
+ /* Pixmap to paint: */
+ m_pixmap);
+ }
+
+ /* Calculate indents: */
+ const int iMiddleColumnIndent = iLeftColumnIndent +
+ m_pixmapSize.width() +
+ iSpacing;
+
+ /* Paint middle column: */
+ {
+ /* Prepare variables: */
+ const int iNameX = iMiddleColumnIndent;
+ const int iNameY = (iFullHeight - m_visibleNameSize.height()) / 2;
+
+ /* Paint name: */
+ paintText(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iNameX, iNameY),
+ /* Font to paint text: */
+ m_nameFont,
+ /* Paint device: */
+ model()->paintDevice(),
+ /* Text to paint: */
+ m_strVisibleName);
+ }
+
+ /* Calculate indents: */
+ QGraphicsView *pView = model()->scene()->views().first();
+ const QPointF sceneCursorPosition = pView->mapToScene(pView->mapFromGlobal(QCursor::pos()));
+ const QPoint itemCursorPosition = mapFromScene(sceneCursorPosition).toPoint();
+ int iRightColumnIndent = iFullWidth - iMarginHR - 1 - m_toolPixmap.width() / m_toolPixmap.devicePixelRatio();
+
+ /* Paint right column: */
+ if ( model()->firstSelectedItem() == this
+ || isHovered())
+ {
+ /* Prepare variables: */
+ const int iToolPixmapX = iRightColumnIndent;
+ const int iToolPixmapY = (iFullHeight - m_toolPixmap.height() / m_toolPixmap.devicePixelRatio()) / 2;
+ QRect toolButtonRectangle = QRect(iToolPixmapX,
+ iToolPixmapY,
+ m_toolPixmap.width() / m_toolPixmap.devicePixelRatio(),
+ m_toolPixmap.height() / m_toolPixmap.devicePixelRatio());
+ toolButtonRectangle.adjust(- iButtonMargin, -iButtonMargin, iButtonMargin, iButtonMargin);
+
+ /* Paint tool button: */
+ if ( isHovered()
+ && isToolButtonArea(itemCursorPosition, 4))
+ paintFlatButton(/* Painter: */
+ pPainter,
+ /* Button rectangle: */
+ toolButtonRectangle,
+ /* Cursor position: */
+ itemCursorPosition);
+
+ /* Paint pixmap: */
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iToolPixmapX, iToolPixmapY),
+ /* Pixmap to paint: */
+ m_toolPixmap);
+ }
+
+ /* Calculate indents: */
+ iRightColumnIndent = iRightColumnIndent - m_toolPixmap.width() / m_toolPixmap.devicePixelRatio() - iSpacing;
+
+ /* Paint right column: */
+ if ( model()->firstSelectedItem() == this
+ || isHovered())
+ {
+ /* Prepare variables: */
+ const int iPinPixmapX = iRightColumnIndent;
+ const int iPinPixmapY = (iFullHeight - m_pinPixmap.height() / m_pinPixmap.devicePixelRatio()) / 2;
+ QRect pinButtonRectangle = QRect(iPinPixmapX,
+ iPinPixmapY,
+ m_pinPixmap.width() / m_pinPixmap.devicePixelRatio(),
+ m_pinPixmap.height() / m_pinPixmap.devicePixelRatio());
+ pinButtonRectangle.adjust(- iButtonMargin, -iButtonMargin, iButtonMargin, iButtonMargin);
+
+ /* Paint pin button: */
+ if ( isHovered()
+ && isPinButtonArea(itemCursorPosition, 4))
+ paintFlatButton(/* Painter: */
+ pPainter,
+ /* Button rectangle: */
+ pinButtonRectangle,
+ /* Cursor position: */
+ itemCursorPosition);
+
+ /* Paint pixmap: */
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iPinPixmapX, iPinPixmapY),
+ /* Pixmap to paint: */
+ m_pinPixmap);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGlobal.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGlobal.h
new file mode 100644
index 00000000..96e9e921
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGlobal.h
@@ -0,0 +1,282 @@
+/* $Id: UIChooserItemGlobal.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserItemGlobal class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserItemGlobal_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserItemGlobal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIChooserItem.h"
+
+/* Forward declarations: */
+class UIChooserNodeGlobal;
+
+
+/** UIChooserItem extension implementing global item. */
+class UIChooserItemGlobal : public UIChooserItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** RTTI required for qgraphicsitem_cast. */
+ enum { Type = UIChooserNodeType_Global };
+
+ /** Build item for certain @a pNode, passing @a pParent to the base-class. */
+ UIChooserItemGlobal(UIChooserItem *pParent, UIChooserNodeGlobal *pNode);
+ /** Destructs global item. */
+ virtual ~UIChooserItemGlobal() RT_OVERRIDE;
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns global node reference. */
+ UIChooserNodeGlobal *nodeToGlobalType() const;
+
+ /** Returns whether passed @a position belongs to tool button area. */
+ bool isToolButtonArea(const QPoint &position, int iMarginMultiplier = 1) const;
+ /** Returns whether passed @a position belongs to pin button area. */
+ bool isPinButtonArea(const QPoint &position, int iMarginMultiplier = 1) const;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Returns height hint. */
+ int heightHint() const;
+ /** Defines height @a iHint. */
+ void setHeightHint(int iHint);
+ /** @} */
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse press @a pEvent. */
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns RTTI item type. */
+ virtual int type() const RT_OVERRIDE { return Type; }
+
+ /** Defines whether item is @a fFavorite. */
+ virtual void setFavorite(bool fFavorite) RT_OVERRIDE;
+
+ /** Starts item editing. */
+ virtual void startEditing() RT_OVERRIDE;
+
+ /** Updates item. */
+ virtual void updateItem() RT_OVERRIDE;
+ /** Updates item tool-tip. */
+ virtual void updateToolTip() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Returns children items of certain @a enmType. */
+ virtual QList<UIChooserItem*> items(UIChooserNodeType enmType = UIChooserNodeType_Any) const RT_OVERRIDE;
+
+ /** Adds possible @a fFavorite child @a pItem to certain @a iPosition. */
+ virtual void addItem(UIChooserItem *pItem, bool fFavorite, int iPosition) RT_OVERRIDE;
+ /** Removes child @a pItem. */
+ virtual void removeItem(UIChooserItem *pItem) RT_OVERRIDE;
+
+ /** Searches for a first child item answering to specified @a strSearchTag and @a iSearchFlags. */
+ virtual UIChooserItem *searchForItem(const QString &strSearchTag, int iSearchFlags) RT_OVERRIDE;
+
+ /** Searches for a first machine child item. */
+ virtual UIChooserItem *firstMachineItem() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates layout. */
+ virtual void updateLayout() RT_OVERRIDE;
+
+ /** Returns minimum width-hint. */
+ virtual int minimumWidthHint() const RT_OVERRIDE;
+ /** Returns minimum height-hint. */
+ virtual int minimumHeightHint() const RT_OVERRIDE;
+
+ /** Returns size-hint.
+ * @param enmWhich Brings size-hint type.
+ * @param constraint Brings size constraint. */
+ virtual QSizeF sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint = QSizeF()) const RT_OVERRIDE;
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Returns pixmap item representation. */
+ virtual QPixmap toPixmap() RT_OVERRIDE;
+
+ /** Returns whether item drop is allowed.
+ * @param pEvent Brings information about drop event.
+ * @param enmPlace Brings the place of drag token to the drop moment. */
+ virtual bool isDropAllowed(QGraphicsSceneDragDropEvent *pEvent, UIChooserItemDragToken where) const RT_OVERRIDE;
+ /** Processes item drop.
+ * @param pEvent Brings information about drop event.
+ * @param pFromWho Brings the item according to which we choose drop position.
+ * @param enmPlace Brings the place of drag token to the drop moment (according to item mentioned above). */
+ virtual void processDrop(QGraphicsSceneDragDropEvent *pEvent, UIChooserItem *pFromWho, UIChooserItemDragToken where) RT_OVERRIDE;
+ /** Reset drag token. */
+ virtual void resetDragToken() RT_OVERRIDE;
+
+ /** Returns D&D mime data. */
+ virtual QMimeData *createMimeData() RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles top-level window remaps. */
+ void sltHandleWindowRemapped();
+ /** @} */
+
+private:
+
+ /** Data field types. */
+ enum GlobalItemData
+ {
+ /* Layout hints: */
+ GlobalItemData_MarginHL,
+ GlobalItemData_MarginHR,
+ GlobalItemData_MarginV,
+ GlobalItemData_Spacing,
+ GlobalItemData_ButtonMargin,
+ };
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns abstractly stored data value for certain @a iKey. */
+ QVariant data(int iKey) const;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates pixmaps. */
+ void updatePixmaps();
+ /** Updates pixmap. */
+ void updatePixmap();
+ /** Updates tool pixmap. */
+ void updateToolPixmap();
+ /** Updates pin pixmap. */
+ void updatePinPixmap();
+ /** Updates minimum name width. */
+ void updateMinimumNameWidth();
+ /** Updates maximum name width. */
+ void updateMaximumNameWidth();
+ /** Updates visible name. */
+ void updateVisibleName();
+ /** @} */
+
+ /** @name Painting stuff.
+ * @{ */
+ /** Paints background using specified @a pPainter and certain @a rectangle. */
+ void paintBackground(QPainter *pPainter, const QRect &rectangle);
+ /** Paints frame using specified @a pPainter and certain @a rect. */
+ void paintFrame(QPainter *pPainter, const QRect &rectangle);
+ /** Paints global info using specified @a pPainter and certain @a pOptions. */
+ void paintGlobalInfo(QPainter *pPainter, const QRect &rectangle);
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+#ifdef VBOX_WS_MAC
+ /** Holds item start default darkness. */
+ int m_iDefaultDarknessStart;
+ /** Holds item final default darkness. */
+ int m_iDefaultDarknessFinal;
+#endif
+ /** Holds item start hover lightness. */
+ int m_iHoverLightnessStart;
+ /** Holds item final hover lightness. */
+ int m_iHoverLightnessFinal;
+ /** Holds item start highlight lightness. */
+ int m_iHighlightLightnessStart;
+ /** Holds item final highlight lightness. */
+ int m_iHighlightLightnessFinal;
+
+ /** Holds item pixmap. */
+ QPixmap m_pixmap;
+ /** Holds item tool pixmap. */
+ QPixmap m_toolPixmap;
+ /** Holds item pin pixmap. */
+ QPixmap m_pinPixmap;
+
+ /** Holds item visible name. */
+ QString m_strVisibleName;
+
+ /** Holds item name font. */
+ QFont m_nameFont;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds pixmap size. */
+ QSize m_pixmapSize;
+ /** Holds tool pixmap size. */
+ QSize m_toolPixmapSize;
+ /** Holds pin pixmap size. */
+ QSize m_pinPixmapSize;
+ /** Holds visible name size. */
+ QSize m_visibleNameSize;
+
+ /** Holds minimum name width. */
+ int m_iMinimumNameWidth;
+ /** Holds maximum name width. */
+ int m_iMaximumNameWidth;
+
+ /** Holds the height hint. */
+ int m_iHeightHint;
+ /** @} */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserItemGlobal_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGroup.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGroup.cpp
new file mode 100644
index 00000000..815eac89
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGroup.cpp
@@ -0,0 +1,1921 @@
+/* $Id: UIChooserItemGroup.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserItemGroup class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGraphicsLinearLayout>
+#include <QGraphicsScene>
+#include <QGraphicsSceneDragDropEvent>
+#include <QGraphicsView>
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QPainter>
+#include <QRegularExpression>
+#include <QStyleOptionGraphicsItem>
+#include <QWindow>
+
+/* GUI includes: */
+#include "UIChooserItemGlobal.h"
+#include "UIChooserItemGroup.h"
+#include "UIChooserItemMachine.h"
+#include "UIChooserModel.h"
+#include "UIChooserNodeGroup.h"
+#include "UIChooserNodeMachine.h"
+#include "UIGraphicsRotatorButton.h"
+#include "UIGraphicsScrollArea.h"
+#include "UIIconPool.h"
+#include "UIVirtualBoxManager.h"
+#include "UIVirtualMachineItem.h"
+
+
+/*********************************************************************************************************************************
+* Class UIChooserItemGroup implementation. *
+*********************************************************************************************************************************/
+
+UIChooserItemGroup::UIChooserItemGroup(QGraphicsScene *pScene, UIChooserNodeGroup *pNode)
+ : UIChooserItem(0, pNode)
+ , m_pScene(pScene)
+ , m_iRootBackgroundDarknessStart(0)
+ , m_iRootBackgroundDarknessFinal(0)
+ , m_iItemBackgroundDarknessStart(0)
+ , m_iItemBackgroundDarknessFinal(0)
+ , m_iHighlightLightness(0)
+ , m_iAdditionalHeight(0)
+ , m_pToggleButton(0)
+ , m_pNameEditorWidget(0)
+ , m_pContainerFavorite(0)
+ , m_pLayoutFavorite(0)
+ , m_pScrollArea(0)
+ , m_pContainer(0)
+ , m_pLayout(0)
+ , m_pLayoutGlobal(0)
+ , m_pLayoutGroup(0)
+ , m_pLayoutMachine(0)
+ , m_iPreviousMinimumWidthHint(0)
+{
+ prepare();
+}
+
+UIChooserItemGroup::UIChooserItemGroup(UIChooserItem *pParent, UIChooserNodeGroup *pNode)
+ : UIChooserItem(pParent, pNode)
+ , m_pScene(0)
+ , m_iRootBackgroundDarknessStart(0)
+ , m_iRootBackgroundDarknessFinal(0)
+ , m_iItemBackgroundDarknessStart(0)
+ , m_iItemBackgroundDarknessFinal(0)
+ , m_iHighlightLightness(0)
+ , m_iAdditionalHeight(0)
+ , m_pToggleButton(0)
+ , m_pNameEditorWidget(0)
+ , m_pContainerFavorite(0)
+ , m_pLayoutFavorite(0)
+ , m_pScrollArea(0)
+ , m_pContainer(0)
+ , m_pLayout(0)
+ , m_pLayoutGlobal(0)
+ , m_pLayoutGroup(0)
+ , m_pLayoutMachine(0)
+ , m_iPreviousMinimumWidthHint(0)
+{
+ prepare();
+}
+
+UIChooserItemGroup::~UIChooserItemGroup()
+{
+ cleanup();
+}
+
+UIChooserNodeGroup *UIChooserItemGroup::nodeToGroupType() const
+{
+ return node() ? node()->toGroupNode() : 0;
+}
+
+QUuid UIChooserItemGroup::id() const
+{
+ return nodeToGroupType() ? nodeToGroupType()->id() : QUuid();
+}
+
+UIChooserNodeGroupType UIChooserItemGroup::groupType() const
+{
+ return nodeToGroupType() ? nodeToGroupType()->groupType() : UIChooserNodeGroupType_Invalid;
+}
+
+bool UIChooserItemGroup::isClosed() const
+{
+ return nodeToGroupType()->isClosed() && !isRoot();
+}
+
+void UIChooserItemGroup::close(bool fAnimated /* = true */)
+{
+ AssertMsg(!isRoot(), ("Can't close root-item!"));
+ m_pToggleButton->setToggled(false, fAnimated);
+}
+
+bool UIChooserItemGroup::isOpened() const
+{
+ return nodeToGroupType()->isOpened() || isRoot();
+}
+
+void UIChooserItemGroup::open(bool fAnimated /* = true */)
+{
+ AssertMsg(!isRoot(), ("Can't open root-item!"));
+ m_pToggleButton->setToggled(true, fAnimated);
+}
+
+void UIChooserItemGroup::updateFavorites()
+{
+ /* Global items only for now, move items to corresponding layout: */
+ foreach (UIChooserItem *pItem, items(UIChooserNodeType_Global))
+ if (pItem->isFavorite())
+ {
+ for (int iIndex = 0; iIndex < m_pLayoutGlobal->count(); ++iIndex)
+ if (m_pLayoutGlobal->itemAt(iIndex) == pItem)
+ m_pLayoutFavorite->addItem(pItem);
+ }
+ else
+ {
+ for (int iIndex = 0; iIndex < m_pLayoutFavorite->count(); ++iIndex)
+ if (m_pLayoutFavorite->itemAt(iIndex) == pItem)
+ m_pLayoutGlobal->addItem(pItem);
+ }
+
+ /* Update/activate children layout: */
+ m_pLayout->updateGeometry();
+ m_pLayout->activate();
+
+ /* Relayout model: */
+ model()->updateLayout();
+}
+
+int UIChooserItemGroup::scrollingValue() const
+{
+ return m_pScrollArea->scrollingValue();
+}
+
+void UIChooserItemGroup::setScrollingValue(int iValue)
+{
+ m_pScrollArea->setScrollingValue(iValue);
+}
+
+void UIChooserItemGroup::scrollBy(int iDelta)
+{
+ m_pScrollArea->scrollBy(iDelta);
+}
+
+void UIChooserItemGroup::makeSureItemIsVisible(UIChooserItem *pItem)
+{
+ /* Make sure item exists: */
+ AssertPtrReturnVoid(pItem);
+
+ /* Convert child rectangle to local coordinates for this group. This also
+ * works for a child at any sub-level, doesn't necessary of this group. */
+ const QPointF positionInScene = pItem->mapToScene(QPointF(0, 0));
+ const QPointF positionInGroup = mapFromScene(positionInScene);
+ const QRectF itemRectInGroup = QRectF(positionInGroup, pItem->size());
+ m_pScrollArea->makeSureRectIsVisible(itemRectInGroup);
+}
+
+/* static */
+QString UIChooserItemGroup::className()
+{
+ return "UIChooserItemGroup";
+}
+
+void UIChooserItemGroup::retranslateUi()
+{
+ updateToggleButtonToolTip();
+}
+
+void UIChooserItemGroup::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIChooserItem::showEvent(pEvent);
+
+ /* Update pixmaps: */
+ updatePixmaps();
+}
+
+void UIChooserItemGroup::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIChooserItem::resizeEvent(pEvent);
+
+ /* What is the new geometry? */
+ const QRectF newGeometry = geometry();
+
+ /* Should we update visible name? */
+ if (previousGeometry().width() != newGeometry.width())
+ updateVisibleName();
+
+ /* Remember the new geometry: */
+ setPreviousGeometry(newGeometry);
+}
+
+void UIChooserItemGroup::hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent)
+{
+ /* Skip if hovered: */
+ if (isHovered())
+ return;
+
+ /* Prepare variables: */
+ const QPoint pos = pEvent->pos().toPoint();
+ const int iMarginV = data(GroupItemData_MarginV).toInt();
+ const int iHeaderHeight = m_minimumHeaderSize.height();
+ const int iFullHeaderHeight = 2 * iMarginV + iHeaderHeight;
+ /* Skip if hovered part out of the header: */
+ if (pos.y() >= iFullHeaderHeight)
+ return;
+
+ /* Call to base-class: */
+ UIChooserItem::hoverMoveEvent(pEvent);
+
+ /* Update linked values: */
+ updateVisibleName();
+}
+
+void UIChooserItemGroup::hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent)
+{
+ /* Skip if not hovered: */
+ if (!isHovered())
+ return;
+
+ /* Call to base-class: */
+ UIChooserItem::hoverLeaveEvent(pEvent);
+
+ /* Update linked values: */
+ updateVisibleName();
+}
+
+void UIChooserItemGroup::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget* /* pWidget = 0 */)
+{
+ /* Acquire rectangle: */
+ const QRect rectangle = pOptions->rect;
+
+ /* Paint background: */
+ paintBackground(pPainter, rectangle);
+ /* Paint frame: */
+ paintFrame(pPainter, rectangle);
+ /* Paint header: */
+ paintHeader(pPainter, rectangle);
+}
+
+void UIChooserItemGroup::startEditing()
+{
+ /* Not for root: */
+ if (isRoot())
+ return;
+
+ /* Not while saving groups: */
+ if (model()->isGroupSavingInProgress())
+ return;
+
+ /* Make sure item visible: */
+ if (model()->root())
+ model()->root()->toGroupItem() ->makeSureItemIsVisible(this);
+
+ /* Assign name-editor text: */
+ m_pNameEditorWidget->setText(name());
+
+ /* Layout name-editor: */
+ const int iMarginV = data(GroupItemData_MarginV).toInt();
+ const int iHeaderHeight = 2 * iMarginV + m_minimumHeaderSize.height();
+ const QSize headerSize = QSize(geometry().width(), iHeaderHeight);
+ const QGraphicsView *pView = model()->scene()->views().first();
+ const QPointF viewPoint = pView->mapFromScene(mapToScene(QPointF(0, 0)));
+ const QPoint globalPoint = pView->parentWidget()->mapToGlobal(viewPoint.toPoint());
+ m_pNameEditorWidget->move(globalPoint);
+ m_pNameEditorWidget->resize(headerSize);
+
+ /* Show name-editor: */
+ m_pNameEditorWidget->show();
+ m_pNameEditorWidget->setFocus();
+}
+
+void UIChooserItemGroup::updateItem()
+{
+ /* Update this group-item: */
+ updateVisibleName();
+ updateMinimumHeaderSize();
+ updateToolTip();
+ update();
+
+ /* Update parent group-item: */
+ if (parentItem())
+ {
+ parentItem()->updateToolTip();
+ parentItem()->update();
+ }
+}
+
+void UIChooserItemGroup::updateToolTip()
+{
+ /* Not for root item: */
+ if (isRoot())
+ return;
+
+ /* Prepare variables: */
+ QStringList toolTipInfo;
+
+ /* Should we add name? */
+ if (!name().isEmpty())
+ {
+ /* Template: */
+ QString strTemplateForName = tr("<b>%1</b>", "Group item tool-tip / Group name");
+
+ /* Append value: */
+ toolTipInfo << strTemplateForName.arg(name());
+ }
+
+ /* Should we add group info? */
+ if (!items(UIChooserNodeType_Group).isEmpty())
+ {
+ /* Template: */
+ QString strGroupCount = tr("%n group(s)", "Group item tool-tip / Group info", items(UIChooserNodeType_Group).size());
+
+ /* Append value: */
+ QString strValue = tr("<nobr>%1</nobr>", "Group item tool-tip / Group info wrapper").arg(strGroupCount);
+ toolTipInfo << strValue;
+ }
+
+ /* Should we add machine info? */
+ if (!items(UIChooserNodeType_Machine).isEmpty())
+ {
+ /* Check if 'this' group contains started VMs: */
+ int iCountOfStartedMachineItems = 0;
+ foreach (UIChooserItem *pItem, items(UIChooserNodeType_Machine))
+ {
+ AssertPtrReturnVoid(pItem);
+ UIChooserItemMachine *pMachineItem = pItem->toMachineItem();
+ AssertPtrReturnVoid(pMachineItem);
+ AssertPtrReturnVoid(pMachineItem->cache());
+ if (pMachineItem->cache()->isItemStarted())
+ ++iCountOfStartedMachineItems;
+ }
+ /* Template: */
+ QString strMachineCount = tr("%n machine(s)", "Group item tool-tip / Machine info", items(UIChooserNodeType_Machine).size());
+ QString strStartedMachineCount = tr("(%n running)", "Group item tool-tip / Running machine info", iCountOfStartedMachineItems);
+
+ /* Append value: */
+ QString strValue = !iCountOfStartedMachineItems ?
+ tr("<nobr>%1</nobr>", "Group item tool-tip / Machine info wrapper").arg(strMachineCount) :
+ tr("<nobr>%1 %2</nobr>", "Group item tool-tip / Machine info wrapper, including running").arg(strMachineCount).arg(strStartedMachineCount);
+ toolTipInfo << strValue;
+ }
+
+ /* Set tool-tip: */
+ setToolTip(toolTipInfo.join("<br>"));
+}
+
+void UIChooserItemGroup::installEventFilterHelper(QObject *pSource)
+{
+ /* The only object which need's that filter for now is scroll-area: */
+ pSource->installEventFilter(m_pScrollArea);
+}
+
+QList<UIChooserItem*> UIChooserItemGroup::items(UIChooserNodeType type /* = UIChooserNodeType_Any */) const
+{
+ switch (type)
+ {
+ case UIChooserNodeType_Any: return items(UIChooserNodeType_Global) + items(UIChooserNodeType_Group) + items(UIChooserNodeType_Machine);
+ case UIChooserNodeType_Global: return m_globalItems;
+ case UIChooserNodeType_Group: return m_groupItems;
+ case UIChooserNodeType_Machine: return m_machineItems;
+ default: break;
+ }
+ return QList<UIChooserItem*>();
+}
+
+void UIChooserItemGroup::addItem(UIChooserItem *pItem, bool fFavorite, int iPosition)
+{
+ /* Check item type: */
+ switch (pItem->type())
+ {
+ case UIChooserNodeType_Global:
+ {
+ AssertMsg(!m_globalItems.contains(pItem), ("Global-item already added!"));
+ if (iPosition < 0 || iPosition >= m_globalItems.size())
+ {
+ if (fFavorite)
+ m_pLayoutFavorite->addItem(pItem);
+ else
+ m_pLayoutGlobal->addItem(pItem);
+ m_globalItems.append(pItem);
+ }
+ else
+ {
+ if (fFavorite)
+ m_pLayoutFavorite->insertItem(iPosition, pItem);
+ else
+ m_pLayoutGlobal->insertItem(iPosition, pItem);
+ m_globalItems.insert(iPosition, pItem);
+ }
+ break;
+ }
+ case UIChooserNodeType_Group:
+ {
+ AssertMsg(!m_groupItems.contains(pItem), ("Group-item already added!"));
+ if (iPosition < 0 || iPosition >= m_groupItems.size())
+ {
+ m_pLayoutGroup->addItem(pItem);
+ m_groupItems.append(pItem);
+ }
+ else
+ {
+ m_pLayoutGroup->insertItem(iPosition, pItem);
+ m_groupItems.insert(iPosition, pItem);
+ }
+ break;
+ }
+ case UIChooserNodeType_Machine:
+ {
+ AssertMsg(!m_machineItems.contains(pItem), ("Machine-item already added!"));
+ if (iPosition < 0 || iPosition >= m_machineItems.size())
+ {
+ m_pLayoutMachine->addItem(pItem);
+ m_machineItems.append(pItem);
+ }
+ else
+ {
+ m_pLayoutMachine->insertItem(iPosition, pItem);
+ m_machineItems.insert(iPosition, pItem);
+ }
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid item type!"));
+ break;
+ }
+ }
+
+ /* Update linked values: */
+ updateLayoutSpacings();
+ updateItemCountInfo();
+ updateToolTip();
+ updateGeometry();
+}
+
+void UIChooserItemGroup::removeItem(UIChooserItem *pItem)
+{
+ /* Check item type: */
+ switch (pItem->type())
+ {
+ case UIChooserNodeType_Global:
+ {
+ AssertMsg(m_globalItems.contains(pItem), ("Global-item was not found!"));
+ m_globalItems.removeAt(m_globalItems.indexOf(pItem));
+ if (pItem->isFavorite())
+ m_pLayoutFavorite->removeItem(pItem);
+ else
+ m_pLayoutGlobal->removeItem(pItem);
+ break;
+ }
+ case UIChooserNodeType_Group:
+ {
+ AssertMsg(m_groupItems.contains(pItem), ("Group-item was not found!"));
+ m_groupItems.removeAt(m_groupItems.indexOf(pItem));
+ if (pItem->isFavorite())
+ m_pLayoutFavorite->removeItem(pItem);
+ else
+ m_pLayoutGroup->removeItem(pItem);
+ break;
+ }
+ case UIChooserNodeType_Machine:
+ {
+ AssertMsg(m_machineItems.contains(pItem), ("Machine-item was not found!"));
+ m_machineItems.removeAt(m_machineItems.indexOf(pItem));
+ if (pItem->isFavorite())
+ m_pLayoutFavorite->removeItem(pItem);
+ else
+ m_pLayoutMachine->removeItem(pItem);
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid item type!"));
+ break;
+ }
+ }
+
+ /* Update linked values: */
+ updateLayoutSpacings();
+ updateItemCountInfo();
+ updateToolTip();
+ updateGeometry();
+}
+
+UIChooserItem* UIChooserItemGroup::searchForItem(const QString &strSearchTag, int iSearchFlags)
+{
+ /* Are we searching among group-items? */
+ if ( ( iSearchFlags & UIChooserItemSearchFlag_LocalGroup
+ && groupType() == UIChooserNodeGroupType_Local)
+ || ( iSearchFlags & UIChooserItemSearchFlag_CloudProvider
+ && groupType() == UIChooserNodeGroupType_Provider)
+ || ( iSearchFlags & UIChooserItemSearchFlag_CloudProfile
+ && groupType() == UIChooserNodeGroupType_Profile))
+ {
+ /* Are we searching by the exact ID? */
+ if (iSearchFlags & UIChooserItemSearchFlag_ExactId)
+ {
+ /* Exact ID matches? */
+ if (id().toString() == strSearchTag)
+ return this;
+ }
+ /* Are we searching by the exact name? */
+ else if (iSearchFlags & UIChooserItemSearchFlag_ExactName)
+ {
+ /* Exact name matches? */
+ if (name() == strSearchTag)
+ return this;
+ }
+ /* Are we searching by the full name? */
+ else if (iSearchFlags & UIChooserItemSearchFlag_FullName)
+ {
+ /* Full name matches? */
+ if (fullName() == strSearchTag)
+ return this;
+ }
+ /* Are we searching by the few first symbols? */
+ else
+ {
+ /* Name starts with passed symbols? */
+ if (name().startsWith(strSearchTag, Qt::CaseInsensitive))
+ return this;
+ }
+ }
+
+ /* Search among all the children, but machines first: */
+ foreach (UIChooserItem *pItem, items(UIChooserNodeType_Machine))
+ if (UIChooserItem *pFoundItem = pItem->searchForItem(strSearchTag, iSearchFlags))
+ return pFoundItem;
+ foreach (UIChooserItem *pItem, items(UIChooserNodeType_Global))
+ if (UIChooserItem *pFoundItem = pItem->searchForItem(strSearchTag, iSearchFlags))
+ return pFoundItem;
+ foreach (UIChooserItem *pItem, items(UIChooserNodeType_Group))
+ if (UIChooserItem *pFoundItem = pItem->searchForItem(strSearchTag, iSearchFlags))
+ return pFoundItem;
+
+ /* Found nothing? */
+ return 0;
+}
+
+UIChooserItem *UIChooserItemGroup::firstMachineItem()
+{
+ /* If this group-item have at least one machine-item: */
+ if (node()->hasNodes(UIChooserNodeType_Machine))
+ /* Return the first machine-item: */
+ return items(UIChooserNodeType_Machine).first()->firstMachineItem();
+ /* If this group-item have at least one group-item: */
+ else if (node()->hasNodes(UIChooserNodeType_Group))
+ /* Return the first machine-item of the first group-item: */
+ return items(UIChooserNodeType_Group).first()->firstMachineItem();
+ /* Found nothing? */
+ return 0;
+}
+
+void UIChooserItemGroup::updateGeometry()
+{
+ /* Update/activate children layout: */
+ m_pLayout->updateGeometry();
+ m_pLayout->activate();
+
+ /* Call to base-class: */
+ UIChooserItem::updateGeometry();
+
+ /* Special handling for root-groups: */
+ if (isRoot())
+ {
+ /* Root-group should notify Chooser-view if minimum-width-hint was changed: */
+ const int iMinimumWidthHint = minimumWidthHint();
+ if (m_iPreviousMinimumWidthHint != iMinimumWidthHint)
+ {
+ /* Save new minimum-width-hint, notify listener: */
+ m_iPreviousMinimumWidthHint = iMinimumWidthHint;
+ emit sigMinimumWidthHintChanged(m_iPreviousMinimumWidthHint);
+ }
+ }
+}
+
+void UIChooserItemGroup::updateLayout()
+{
+ /* Prepare variables: */
+ const int iMarginHL = data(GroupItemData_MarginHL).toInt();
+ const int iMarginV = data(GroupItemData_MarginV).toInt();
+ const int iParentIndent = data(GroupItemData_ParentIndent).toInt();
+ const int iFullHeaderHeight = m_minimumHeaderSize.height();
+ int iPreviousVerticalIndent = 0;
+
+ /* Header (root-item): */
+ if (isRoot())
+ {
+ /* Acquire view: */
+ const QGraphicsView *pView = model()->scene()->views().first();
+
+ /* Adjust scroll-view geometry: */
+ QSize viewSize = pView->size();
+ viewSize.setHeight(viewSize.height() - iPreviousVerticalIndent);
+ /* Adjust favorite children container: */
+ m_pContainerFavorite->resize(viewSize.width(), m_pContainerFavorite->minimumSizeHint().height());
+ m_pContainerFavorite->setPos(0, iPreviousVerticalIndent);
+ iPreviousVerticalIndent += m_pContainerFavorite->minimumSizeHint().height();
+ /* Adjust other children scroll-area: */
+ m_pScrollArea->resize(viewSize.width(), viewSize.height() - m_pContainerFavorite->minimumSizeHint().height());
+ m_pScrollArea->setPos(0, iPreviousVerticalIndent);
+ }
+ /* Header (non-root-item): */
+ else
+ {
+ /* Toggle-button: */
+ if (m_pToggleButton)
+ {
+ /* Prepare variables: */
+ int iToggleButtonHeight = m_toggleButtonSize.height();
+ /* Layout toggle-button: */
+ int iToggleButtonX = iMarginHL;
+ int iToggleButtonY = iToggleButtonHeight == iFullHeaderHeight ? iMarginV :
+ iMarginV + (iFullHeaderHeight - iToggleButtonHeight) / 2;
+ m_pToggleButton->setPos(iToggleButtonX, iToggleButtonY);
+ /* Show toggle-button: */
+ m_pToggleButton->show();
+ }
+
+ /* Prepare body indent: */
+ iPreviousVerticalIndent = 2 * iMarginV + iFullHeaderHeight;
+
+ /* Adjust scroll-view geometry: */
+ QSize itemSize = size().toSize();
+ itemSize.setHeight(itemSize.height() - iPreviousVerticalIndent);
+ /* Adjust favorite children container: */
+ m_pContainerFavorite->resize(itemSize.width() - iParentIndent, m_pContainerFavorite->minimumSizeHint().height());
+ m_pContainerFavorite->setPos(iParentIndent, iPreviousVerticalIndent);
+ iPreviousVerticalIndent += m_pContainerFavorite->minimumSizeHint().height();
+ /* Adjust other children scroll-area: */
+ m_pScrollArea->resize(itemSize.width() - iParentIndent, itemSize.height() - m_pContainerFavorite->minimumSizeHint().height());
+ m_pScrollArea->setPos(iParentIndent, iPreviousVerticalIndent);
+ }
+
+ /* No body for closed group: */
+ if (isClosed())
+ {
+ m_pContainerFavorite->hide();
+ m_pScrollArea->hide();
+ }
+ /* Body for opened group: */
+ else
+ {
+ m_pContainerFavorite->show();
+ m_pScrollArea->show();
+ foreach (UIChooserItem *pItem, items())
+ pItem->updateLayout();
+ }
+}
+
+int UIChooserItemGroup::minimumWidthHint() const
+{
+ return minimumWidthHintForGroup(isOpened());
+}
+
+int UIChooserItemGroup::minimumHeightHint() const
+{
+ return minimumHeightHintForGroup(isOpened());
+}
+
+QSizeF UIChooserItemGroup::sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint /* = QSizeF() */) const
+{
+ /* If Qt::MinimumSize requested: */
+ if (enmWhich == Qt::MinimumSize)
+ return minimumSizeHintForGroup(isOpened());
+ /* Else call to base-class: */
+ return UIChooserItem::sizeHint(enmWhich, constraint);
+}
+
+QPixmap UIChooserItemGroup::toPixmap()
+{
+ /* Ask item to paint itself into pixmap: */
+ qreal dDpr = gpManager->windowHandle()->devicePixelRatio();
+ QSize actualSize = size().toSize();
+ QPixmap pixmap(actualSize * dDpr);
+ pixmap.setDevicePixelRatio(dDpr);
+ QPainter painter(&pixmap);
+ QStyleOptionGraphicsItem options;
+ options.rect = QRect(QPoint(0, 0), actualSize);
+ paint(&painter, &options);
+ return pixmap;
+}
+
+bool UIChooserItemGroup::isDropAllowed(QGraphicsSceneDragDropEvent *pEvent, UIChooserItemDragToken where) const
+{
+ /* No drops while saving groups: */
+ if (model()->isGroupSavingInProgress())
+ return false;
+ /* If drag token is shown, its up to parent to decide: */
+ if (where != UIChooserItemDragToken_Off)
+ return parentItem()->isDropAllowed(pEvent);
+
+ /* Else we should check mime format: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ if (pMimeData->hasFormat(UIChooserItemGroup::className()))
+ {
+ /* Get passed group-item: */
+ const UIChooserItemMimeData *pCastedMimeData = qobject_cast<const UIChooserItemMimeData*>(pMimeData);
+ AssertPtrReturn(pCastedMimeData, false);
+ UIChooserItem *pItem = pCastedMimeData->item();
+ AssertPtrReturn(pItem, false);
+ UIChooserItemGroup *pGroupItem = pItem->toGroupItem();
+ AssertPtrReturn(pGroupItem, false);
+
+ /* For local items: */
+ if ( groupType() == UIChooserNodeGroupType_Local
+ && pGroupItem->groupType() == UIChooserNodeGroupType_Local)
+ {
+ /* Make sure passed machine isn't immutable within own group: */
+ if ( pGroupItem->isContainsLockedMachine()
+ && !m_groupItems.contains(pItem))
+ return false;
+ /* Make sure passed group is not 'this': */
+ if (pItem == this)
+ return false;
+ /* Make sure passed group is not among our parents: */
+ const UIChooserItem *pTestedItem = this;
+ while (UIChooserItem *pParentOfTestedWidget = pTestedItem->parentItem())
+ {
+ if (pItem == pParentOfTestedWidget)
+ return false;
+ pTestedItem = pParentOfTestedWidget;
+ }
+
+ /* Allow finally: */
+ return true;
+ }
+ /* For profiles inside provider and providers inside root group: */
+ else
+ if ( ( groupType() == UIChooserNodeGroupType_Provider
+ && pGroupItem->groupType() == UIChooserNodeGroupType_Profile)
+ || ( groupType() == UIChooserNodeGroupType_Local
+ && pGroupItem->groupType() == UIChooserNodeGroupType_Provider))
+ {
+ /* Make sure passed item is ours: */
+ return m_groupItems.contains(pItem);
+ }
+ }
+ else if (pMimeData->hasFormat(UIChooserItemMachine::className()))
+ {
+ /* Get passed machine-item: */
+ const UIChooserItemMimeData *pCastedMimeData = qobject_cast<const UIChooserItemMimeData*>(pMimeData);
+ AssertPtrReturn(pCastedMimeData, false);
+ UIChooserItem *pItem = pCastedMimeData->item();
+ AssertPtrReturn(pItem, false);
+ UIChooserItemMachine *pMachineItem = pItem->toMachineItem();
+ AssertPtrReturn(pMachineItem, false);
+
+ /* For local items: */
+ if ( groupType() == UIChooserNodeGroupType_Local
+ && pMachineItem->cacheType() == UIVirtualMachineItemType_Local)
+ {
+ /* Make sure passed machine isn't immutable within own group: */
+ if ( pMachineItem->isLockedMachine()
+ && !m_machineItems.contains(pItem))
+ return false;
+ switch (pEvent->proposedAction())
+ {
+ case Qt::MoveAction:
+ {
+ /* Make sure passed item is ours or there is no other item with such id: */
+ return m_machineItems.contains(pItem) || !isContainsMachine(pMachineItem->id());
+ }
+ case Qt::CopyAction:
+ {
+ /* Make sure there is no other item with such id: */
+ return !isContainsMachine(pMachineItem->id());
+ }
+ default:
+ break;
+ }
+ }
+ /* For cloud items: */
+ else
+ if ( groupType() == UIChooserNodeGroupType_Profile
+ && pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
+ {
+ /* Make sure passed item is ours: */
+ return m_machineItems.contains(pItem);
+ }
+ }
+ /* That was invalid mime: */
+ return false;
+}
+
+void UIChooserItemGroup::processDrop(QGraphicsSceneDragDropEvent *pEvent, UIChooserItem *pFromWho, UIChooserItemDragToken where)
+{
+ /* Get mime: */
+ const QMimeData *pMime = pEvent->mimeData();
+ /* Check mime format: */
+ if (pMime->hasFormat(UIChooserItemGroup::className()))
+ {
+ switch (pEvent->proposedAction())
+ {
+ case Qt::MoveAction:
+ case Qt::CopyAction:
+ {
+ /* Remember scene: */
+ UIChooserModel *pModel = model();
+
+ /* Get passed group-item: */
+ const UIChooserItemMimeData *pCastedMime = qobject_cast<const UIChooserItemMimeData*>(pMime);
+ AssertMsg(pCastedMime, ("Can't cast passed mime-data to UIChooserItemMimeData!"));
+ UIChooserNode *pNode = pCastedMime->item()->node();
+
+ /* Check if we have position information: */
+ int iPosition = m_groupItems.size();
+ if (pFromWho && where != UIChooserItemDragToken_Off)
+ {
+ /* Make sure sender item if our child: */
+ AssertMsg(m_groupItems.contains(pFromWho), ("Sender item is NOT our child!"));
+ if (m_groupItems.contains(pFromWho))
+ {
+ iPosition = m_groupItems.indexOf(pFromWho);
+ if (where == UIChooserItemDragToken_Down)
+ ++iPosition;
+ }
+ }
+
+ /* Copy passed group-item into this group: */
+ UIChooserNodeGroup *pNewGroupNode = new UIChooserNodeGroup(node(), iPosition, pNode->toGroupNode());
+ UIChooserItemGroup *pNewGroupItem = new UIChooserItemGroup(this, pNewGroupNode);
+ if (isClosed())
+ open(false);
+
+ /* If proposed action is 'move': */
+ if (pEvent->proposedAction() == Qt::MoveAction)
+ {
+ /* Delete passed item: */
+ delete pNode;
+ }
+
+ /* Update model: */
+ pModel->wipeOutEmptyGroups();
+ pModel->updateNavigationItemList();
+ pModel->updateLayout();
+ pModel->setSelectedItem(pNewGroupItem);
+ pModel->saveGroups();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else if (pMime->hasFormat(UIChooserItemMachine::className()))
+ {
+ switch (pEvent->proposedAction())
+ {
+ case Qt::MoveAction:
+ case Qt::CopyAction:
+ {
+ /* Remember scene: */
+ UIChooserModel *pModel = model();
+
+ /* Get passed machine-item: */
+ const UIChooserItemMimeData *pCastedMime = qobject_cast<const UIChooserItemMimeData*>(pMime);
+ AssertMsg(pCastedMime, ("Can't cast passed mime-data to UIChooserItemMimeData!"));
+ UIChooserNode *pNode = pCastedMime->item()->node();
+
+ /* Check if we have position information: */
+ int iPosition = m_machineItems.size();
+ if (pFromWho && where != UIChooserItemDragToken_Off)
+ {
+ /* Make sure sender item if our child: */
+ AssertMsg(m_machineItems.contains(pFromWho), ("Sender item is NOT our child!"));
+ if (m_machineItems.contains(pFromWho))
+ {
+ iPosition = m_machineItems.indexOf(pFromWho);
+ if (where == UIChooserItemDragToken_Down)
+ ++iPosition;
+ }
+ }
+
+ /* Copy passed machine-item into this group: */
+ UIChooserNodeMachine *pNewMachineNode = new UIChooserNodeMachine(node(), iPosition, pNode->toMachineNode());
+ UIChooserItemMachine *pNewMachineItem = new UIChooserItemMachine(this, pNewMachineNode);
+ if (isClosed())
+ open(false);
+
+ /* If proposed action is 'move': */
+ if (pEvent->proposedAction() == Qt::MoveAction)
+ {
+ /* Delete passed item: */
+ delete pNode;
+ }
+
+ /* Update model: */
+ pModel->wipeOutEmptyGroups();
+ pModel->updateNavigationItemList();
+ pModel->updateLayout();
+ pModel->setSelectedItem(pNewMachineItem);
+ pModel->saveGroups();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void UIChooserItemGroup::resetDragToken()
+{
+ /* Reset drag token for this item: */
+ if (dragTokenPlace() != UIChooserItemDragToken_Off)
+ {
+ setDragTokenPlace(UIChooserItemDragToken_Off);
+ update();
+ }
+ /* Reset drag tokens for all the items: */
+ foreach (UIChooserItem *pItem, items())
+ pItem->resetDragToken();
+}
+
+QMimeData* UIChooserItemGroup::createMimeData()
+{
+ return new UIChooserItemMimeData(this);
+}
+
+void UIChooserItemGroup::sltHandleWindowRemapped()
+{
+ /* Update pixmaps: */
+ updatePixmaps();
+}
+
+void UIChooserItemGroup::sltNameEditingFinished()
+{
+ /* Not for root: */
+ if (isRoot())
+ return;
+
+ /* Close name-editor: */
+ m_pNameEditorWidget->close();
+
+ /* Enumerate all the used machine and group names: */
+ QStringList usedNames;
+ foreach (UIChooserItem *pItem, parentItem()->items())
+ {
+ AssertPtrReturnVoid(pItem);
+ if ( pItem->type() == UIChooserNodeType_Machine
+ || ( pItem->type() == UIChooserNodeType_Group
+ && pItem->toGroupItem()->groupType() == UIChooserNodeGroupType_Local))
+ usedNames << pItem->name();
+ }
+ /* If proposed name is empty or not unique, reject it: */
+ QString strNewName = m_pNameEditorWidget->text().trimmed();
+ if (strNewName.isEmpty() || usedNames.contains(strNewName))
+ return;
+
+ /* We should replace forbidden symbols
+ * with ... well, probably underscores: */
+ strNewName.replace(QRegularExpression("[\\\\/:*?\"<>]"), "_");
+
+ /* Set new name, save settings: */
+ nodeToGroupType()->setName(strNewName);
+ model()->saveGroups();
+}
+
+void UIChooserItemGroup::sltGroupToggleStart()
+{
+ /* Not for root: */
+ if (isRoot())
+ return;
+
+ /* Toggle started: */
+ emit sigToggleStarted();
+
+ /* Setup animation: */
+ updateAnimationParameters();
+
+ /* Group closed, we are opening it: */
+ if (nodeToGroupType()->isClosed())
+ {
+ /* Toggle-state and navigation will be
+ * updated on toggle-finish signal! */
+ }
+ /* Group opened, we are closing it: */
+ else
+ {
+ /* Update toggle-state: */
+ nodeToGroupType()->close();
+ /* Update geometry: */
+ updateGeometry();
+ /* Update navigation: */
+ model()->updateNavigationItemList();
+ /* Relayout model: */
+ model()->updateLayout();
+ }
+}
+
+void UIChooserItemGroup::sltGroupToggleFinish(bool fToggled)
+{
+ /* Not for root: */
+ if (isRoot())
+ return;
+
+ /* Update toggle-state: */
+ fToggled ? nodeToGroupType()->open() : nodeToGroupType()->close();
+ /* Update geometry: */
+ updateGeometry();
+ /* Update navigation: */
+ model()->updateNavigationItemList();
+ /* Relayout model: */
+ model()->updateLayout();
+ /* Update toggle-button tool-tip: */
+ updateToggleButtonToolTip();
+ /* Repaint finally: */
+ update();
+ /* Save changes: */
+ model()->saveGroups();
+
+ /* Toggle finished: */
+ emit sigToggleFinished();
+}
+
+void UIChooserItemGroup::prepare()
+{
+ /* Color tones: */
+ m_iRootBackgroundDarknessStart = 115;
+ m_iRootBackgroundDarknessFinal = 150;
+ m_iItemBackgroundDarknessStart = 100;
+ m_iItemBackgroundDarknessFinal = 105;
+#if defined(VBOX_WS_MAC)
+ m_iHighlightLightness = 105;
+#elif defined(VBOX_WS_WIN)
+ m_iHighlightLightness = 190;
+#else /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+ m_iHighlightLightness = 105;
+#endif /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+
+ /* Prepare self: */
+ m_nameFont = font();
+ m_nameFont.setWeight(QFont::Bold);
+ m_infoFont = font();
+ m_minimumHeaderSize = QSize(0, 0);
+
+ /* Prepare header widgets of non-root item: */
+ if (!isRoot())
+ {
+ /* Setup toggle-button: */
+ m_pToggleButton = new UIGraphicsRotatorButton(this, "additionalHeight", isOpened());
+ if (m_pToggleButton)
+ {
+ m_pToggleButton->hide();
+ connect(m_pToggleButton, &UIGraphicsRotatorButton::sigRotationStart,
+ this, &UIChooserItemGroup::sltGroupToggleStart);
+ connect(m_pToggleButton, &UIGraphicsRotatorButton::sigRotationFinish,
+ this, &UIChooserItemGroup::sltGroupToggleFinish);
+ }
+ m_toggleButtonSize = m_pToggleButton ? m_pToggleButton->minimumSizeHint().toSize() : QSize(0, 0);
+
+ /* Setup name-editor: */
+ m_pNameEditorWidget = new UIEditorGroupRename(name());
+ if (m_pNameEditorWidget)
+ {
+ m_pNameEditorWidget->setFont(m_nameFont);
+ connect(m_pNameEditorWidget, &UIEditorGroupRename::sigEditingFinished,
+ this, &UIChooserItemGroup::sltNameEditingFinished);
+ }
+ }
+
+ /* Prepare favorite children container: */
+ m_pContainerFavorite = new QIGraphicsWidget(this);
+ if (m_pContainerFavorite)
+ {
+ /* Make it always above other children scroll-area: */
+ m_pContainerFavorite->setZValue(1);
+
+ /* Prepare favorite children layout: */
+ m_pLayoutFavorite = new QGraphicsLinearLayout(Qt::Vertical, m_pContainerFavorite);
+ if (m_pLayoutFavorite)
+ {
+ m_pLayoutFavorite->setContentsMargins(0, 0, 0, 0);
+ m_pLayoutFavorite->setSpacing(0);
+ }
+ }
+
+ /* Prepare scroll-area: */
+ m_pScrollArea = new UIGraphicsScrollArea(Qt::Vertical, this);
+ if (m_pScrollArea)
+ {
+ /* Prepare container: */
+ m_pContainer = new QIGraphicsWidget;
+ if (m_pContainer)
+ {
+ /* Prepare layout: */
+ m_pLayout = new QGraphicsLinearLayout(Qt::Vertical, m_pContainer);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setSpacing(0);
+
+ /* Prepare global layout: */
+ m_pLayoutGlobal = new QGraphicsLinearLayout(Qt::Vertical);
+ if (m_pLayoutGlobal)
+ {
+ m_pLayoutGlobal->setContentsMargins(0, 0, 0, 0);
+ m_pLayoutGlobal->setSpacing(1);
+ m_pLayout->addItem(m_pLayoutGlobal);
+ }
+
+ /* Prepare group layout: */
+ m_pLayoutGroup = new QGraphicsLinearLayout(Qt::Vertical);
+ if (m_pLayoutGroup)
+ {
+ m_pLayoutGroup->setContentsMargins(0, 0, 0, 0);
+ m_pLayoutGroup->setSpacing(1);
+ m_pLayout->addItem(m_pLayoutGroup);
+ }
+
+ /* Prepare machine layout: */
+ m_pLayoutMachine = new QGraphicsLinearLayout(Qt::Vertical);
+ if (m_pLayoutMachine)
+ {
+ m_pLayoutMachine->setContentsMargins(0, 0, 0, 0);
+ m_pLayoutMachine->setSpacing(1);
+ m_pLayout->addItem(m_pLayoutMachine);
+ }
+ }
+
+ /* Assign to scroll-area: */
+ m_pScrollArea->setViewport(m_pContainer);
+ }
+ }
+
+ /* Add item directly to the scene (if passed): */
+ if (m_pScene)
+ m_pScene->addItem(this);
+ /* Add item to the parent instead (if passed),
+ * it will be added to the scene indirectly: */
+ else if (parentItem())
+ parentItem()->addItem(this, isFavorite(), position());
+ /* Otherwise sombody forgot to pass scene or parent. */
+ else
+ AssertFailedReturnVoid();
+
+ /* Copy contents: */
+ copyContents(nodeToGroupType());
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Initialize non-root items: */
+ if (!isRoot())
+ {
+ updatePixmaps();
+ updateItemCountInfo();
+ updateVisibleName();
+ updateToolTip();
+ }
+
+ /* Configure connections: */
+ connect(this, &UIChooserItemGroup::sigMinimumWidthHintChanged,
+ model(), &UIChooserModel::sigRootItemMinimumWidthHintChanged);
+ if (!isRoot())
+ {
+ /* Non-root items can be toggled: */
+ connect(this, &UIChooserItemGroup::sigToggleStarted,
+ model(), &UIChooserModel::sigToggleStarted);
+ connect(this, &UIChooserItemGroup::sigToggleFinished,
+ model(), &UIChooserModel::sigToggleFinished,
+ Qt::QueuedConnection);
+ /* Non-root items requires pixmap updates: */
+ connect(gpManager, &UIVirtualBoxManager::sigWindowRemapped,
+ this, &UIChooserItemGroup::sltHandleWindowRemapped);
+ }
+
+ /* Invalidate minimum width hint
+ * after we isntalled listener: */
+ m_iPreviousMinimumWidthHint = 0;
+ /* Update geometry finally: */
+ updateGeometry();
+}
+
+void UIChooserItemGroup::cleanup()
+{
+ /* Delete group name editor: */
+ delete m_pNameEditorWidget;
+ m_pNameEditorWidget = 0;
+
+ /* Delete all the items: */
+ while (!m_groupItems.isEmpty()) { delete m_groupItems.last(); }
+ while (!m_globalItems.isEmpty()) { delete m_globalItems.last(); }
+ while (!m_machineItems.isEmpty()) { delete m_machineItems.last(); }
+
+ /* If that item is current: */
+ if (model()->currentItem() == this)
+ {
+ /* Unset current-item: */
+ model()->setCurrentItem(0);
+ }
+ /* If that item is in selection list: */
+ if (model()->selectedItems().contains(this))
+ {
+ /* Remove item from the selection list: */
+ model()->removeFromSelectedItems(this);
+ }
+ /* If that item is in navigation list: */
+ if (model()->navigationItems().contains(this))
+ {
+ /* Remove item from the navigation list: */
+ model()->removeFromNavigationItems(this);
+ }
+
+ /* Remove item from the parent: */
+ if (parentItem())
+ parentItem()->removeItem(this);
+}
+
+QVariant UIChooserItemGroup::data(int iKey) const
+{
+ /* Provide other members with required data: */
+ switch (iKey)
+ {
+ /* Layout hints: */
+ case GroupItemData_MarginHL: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+ case GroupItemData_MarginHR: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4 * 5;
+ case GroupItemData_MarginV: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+ case GroupItemData_HeaderSpacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+ case GroupItemData_ChildrenSpacing: return 1;
+ case GroupItemData_ParentIndent: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+
+ /* Default: */
+ default: break;
+ }
+ return QVariant();
+}
+
+int UIChooserItemGroup::additionalHeight() const
+{
+ return m_iAdditionalHeight;
+}
+
+void UIChooserItemGroup::setAdditionalHeight(int iAdditionalHeight)
+{
+ m_iAdditionalHeight = iAdditionalHeight;
+ updateGeometry();
+ model()->updateLayout();
+}
+
+void UIChooserItemGroup::updateAnimationParameters()
+{
+ /* Only for item with button: */
+ if (!m_pToggleButton)
+ return;
+
+ /* Recalculate animation parameters: */
+ QSizeF openedSize = minimumSizeHintForGroup(true);
+ QSizeF closedSize = minimumSizeHintForGroup(false);
+ int iAdditionalHeight = (int)(openedSize.height() - closedSize.height());
+ m_pToggleButton->setAnimationRange(0, iAdditionalHeight);
+}
+
+void UIChooserItemGroup::updateToggleButtonToolTip()
+{
+ /* Only for item with button: */
+ if (!m_pToggleButton)
+ return;
+
+ /* Update toggle-button tool-tip: */
+ m_pToggleButton->setToolTip(isOpened() ? tr("Collapse group") : tr("Expand group"));
+}
+
+void UIChooserItemGroup::copyContents(UIChooserNodeGroup *pCopyFrom)
+{
+ foreach (UIChooserNode *pNode, pCopyFrom->nodes(UIChooserNodeType_Group))
+ new UIChooserItemGroup(this, pNode->toGroupNode());
+ foreach (UIChooserNode *pNode, pCopyFrom->nodes(UIChooserNodeType_Global))
+ new UIChooserItemGlobal(this, pNode->toGlobalNode());
+ foreach (UIChooserNode *pNode, pCopyFrom->nodes(UIChooserNodeType_Machine))
+ new UIChooserItemMachine(this, pNode->toMachineNode());
+}
+
+bool UIChooserItemGroup::isContainsMachine(const QUuid &uId) const
+{
+ /* Check each machine-item: */
+ foreach (UIChooserItem *pItem, m_machineItems)
+ {
+ /* Make sure it's really machine node: */
+ AssertPtrReturn(pItem, false);
+ UIChooserItemMachine *pMachineItem = pItem->toMachineItem();
+ AssertPtrReturn(pMachineItem, false);
+ if (pMachineItem->id() == uId)
+ return true;
+ }
+ /* Found nothing? */
+ return false;
+}
+
+bool UIChooserItemGroup::isContainsLockedMachine()
+{
+ /* Check each machine-item: */
+ foreach (UIChooserItem *pItem, items(UIChooserNodeType_Machine))
+ if (pItem->toMachineItem()->isLockedMachine())
+ return true;
+ /* Check each group-item: */
+ foreach (UIChooserItem *pItem, items(UIChooserNodeType_Group))
+ if (pItem->toGroupItem()->isContainsLockedMachine())
+ return true;
+ /* Found nothing? */
+ return false;
+}
+
+void UIChooserItemGroup::updateItemCountInfo()
+{
+ /* Not for root item: */
+ if (isRoot())
+ return;
+
+ /* Update item info attributes: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+ QString strInfoGroups = m_groupItems.isEmpty() ? QString() : QString::number(m_groupItems.size());
+ QString strInfoMachines = m_machineItems.isEmpty() ? QString() : QString::number(m_machineItems.size());
+ QSize infoSizeGroups = textSize(m_infoFont, pPaintDevice, strInfoGroups);
+ QSize infoSizeMachines = textSize(m_infoFont, pPaintDevice, strInfoMachines);
+
+ /* Update linked values: */
+ bool fSomethingChanged = false;
+ if (m_strInfoGroups != strInfoGroups)
+ {
+ m_strInfoGroups = strInfoGroups;
+ fSomethingChanged = true;
+ }
+ if (m_strInfoMachines != strInfoMachines)
+ {
+ m_strInfoMachines = strInfoMachines;
+ fSomethingChanged = true;
+ }
+ if (m_infoSizeGroups != infoSizeGroups)
+ {
+ m_infoSizeGroups = infoSizeGroups;
+ fSomethingChanged = true;
+ }
+ if (m_infoSizeMachines != infoSizeMachines)
+ {
+ m_infoSizeMachines = infoSizeMachines;
+ fSomethingChanged = true;
+ }
+ if (fSomethingChanged)
+ {
+ updateVisibleName();
+ updateMinimumHeaderSize();
+ }
+}
+
+int UIChooserItemGroup::minimumWidthHintForGroup(bool fGroupOpened) const
+{
+ /* Calculating proposed width: */
+ int iProposedWidth = 0;
+
+ /* For root item: */
+ if (isRoot())
+ {
+ /* Main root-item always takes body into account: */
+ if (node()->hasNodes())
+ {
+ /* We have to take maximum children width into account: */
+ iProposedWidth = qMax(m_pContainerFavorite->minimumSizeHint().width(),
+ m_pContainer->minimumSizeHint().width());
+ }
+ }
+ /* For other items: */
+ else
+ {
+ /* Prepare variables: */
+ const int iMarginHL = data(GroupItemData_MarginHL).toInt();
+ const int iMarginHR = data(GroupItemData_MarginHR).toInt();
+
+ /* Basically we have to take header width into account: */
+ iProposedWidth += m_minimumHeaderSize.width();
+
+ /* But if group-item is opened: */
+ if (fGroupOpened)
+ {
+ /* We have to take maximum children width into account: */
+ iProposedWidth = qMax(m_pContainerFavorite->minimumSizeHint().width(),
+ m_pContainer->minimumSizeHint().width());
+ }
+
+ /* And 2 margins at last - left and right: */
+ iProposedWidth += iMarginHL + iMarginHR;
+ }
+
+ /* Return result: */
+ return iProposedWidth;
+}
+
+int UIChooserItemGroup::minimumHeightHintForGroup(bool fGroupOpened) const
+{
+ /* Calculating proposed height: */
+ int iProposedHeight = 0;
+
+ /* For root item: */
+ if (isRoot())
+ {
+ /* Main root-item always takes body into account: */
+ if (node()->hasNodes())
+ {
+ /* Prepare variables: */
+ const int iSpacingV = data(GroupItemData_ChildrenSpacing).toInt();
+
+ /* We have to take maximum children height into account: */
+ iProposedHeight += m_pContainerFavorite->minimumSizeHint().height();
+ iProposedHeight += m_pContainer->minimumSizeHint().height();
+ iProposedHeight += iSpacingV;
+ }
+ }
+ /* For other items: */
+ else
+ {
+ /* Prepare variables: */
+ const int iMarginV = data(GroupItemData_MarginV).toInt();
+
+ /* Group-item header have 2 margins - top and bottom: */
+ iProposedHeight += 2 * iMarginV;
+ /* And header content height to take into account: */
+ iProposedHeight += m_minimumHeaderSize.height();
+
+ /* But if group-item is opened: */
+ if (fGroupOpened)
+ {
+ /* We have to take maximum children height into account: */
+ iProposedHeight += m_pContainerFavorite->minimumSizeHint().height();
+ iProposedHeight += m_pContainer->minimumSizeHint().height();
+ }
+
+ /* Finally, additional height during animation: */
+ if (!fGroupOpened && m_pToggleButton && m_pToggleButton->isAnimationRunning())
+ iProposedHeight += m_iAdditionalHeight;
+ }
+
+ /* Return result: */
+ return iProposedHeight;
+}
+
+QSizeF UIChooserItemGroup::minimumSizeHintForGroup(bool fGroupOpened) const
+{
+ return QSizeF(minimumWidthHintForGroup(fGroupOpened), minimumHeightHintForGroup(fGroupOpened));
+}
+
+void UIChooserItemGroup::updateVisibleName()
+{
+ /* Not for root item: */
+ if (isRoot())
+ return;
+
+ /* Prepare variables: */
+ int iMarginHL = data(GroupItemData_MarginHL).toInt();
+ int iMarginHR = data(GroupItemData_MarginHR).toInt();
+ int iHeaderSpacing = data(GroupItemData_HeaderSpacing).toInt();
+ int iToggleButtonWidth = m_toggleButtonSize.width();
+ int iGroupPixmapWidth = m_pixmapSizeGroups.width();
+ int iMachinePixmapWidth = m_pixmapSizeMachines.width();
+ int iGroupCountTextWidth = m_infoSizeGroups.width();
+ int iMachineCountTextWidth = m_infoSizeMachines.width();
+ int iMaximumWidth = (int)geometry().width();
+
+ /* Left margin: */
+ iMaximumWidth -= iMarginHL;
+ /* Button width: */
+ if (!isRoot())
+ iMaximumWidth -= iToggleButtonWidth;
+ /* Spacing between button and name: */
+ iMaximumWidth -= iHeaderSpacing;
+ if (isHovered())
+ {
+ /* Spacing between name and info: */
+ iMaximumWidth -= iHeaderSpacing;
+ /* Group info width: */
+ if (!m_groupItems.isEmpty())
+ iMaximumWidth -= (iGroupPixmapWidth + iGroupCountTextWidth);
+ /* Machine info width: */
+ if (!m_machineItems.isEmpty())
+ iMaximumWidth -= (iMachinePixmapWidth + iMachineCountTextWidth);
+ }
+ /* Right margin: */
+ iMaximumWidth -= iMarginHR;
+ /* Calculate new visible name and name-size: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+ QString strVisibleName = compressText(m_nameFont, pPaintDevice, name(), iMaximumWidth);
+ QSize visibleNameSize = textSize(m_nameFont, pPaintDevice, strVisibleName);
+
+ /* Update linked values: */
+ if (m_visibleNameSize != visibleNameSize)
+ {
+ m_visibleNameSize = visibleNameSize;
+ updateGeometry();
+ }
+ if (m_strVisibleName != strVisibleName)
+ {
+ m_strVisibleName = strVisibleName;
+ update();
+ }
+}
+
+void UIChooserItemGroup::updatePixmaps()
+{
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_groupsPixmap = UIIconPool::iconSet(":/group_abstract_16px.png").pixmap(gpManager->windowHandle(),
+ QSize(iIconMetric, iIconMetric));
+ m_machinesPixmap = UIIconPool::iconSet(":/machine_abstract_16px.png").pixmap(gpManager->windowHandle(),
+ QSize(iIconMetric, iIconMetric));
+ m_pixmapSizeGroups = m_groupsPixmap.size() / m_groupsPixmap.devicePixelRatio();
+ m_pixmapSizeMachines = m_machinesPixmap.size() / m_machinesPixmap.devicePixelRatio();
+}
+
+void UIChooserItemGroup::updateMinimumHeaderSize()
+{
+ /* Not for root item: */
+ if (isRoot())
+ return;
+
+ /* Prepare variables: */
+ int iHeaderSpacing = data(GroupItemData_HeaderSpacing).toInt();
+
+ /* Calculate minimum visible name size: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+ QFontMetrics fm(m_nameFont, pPaintDevice);
+ int iMaximumNameWidth = textWidth(m_nameFont, pPaintDevice, 20);
+ QString strCompressedName = compressText(m_nameFont, pPaintDevice, name(), iMaximumNameWidth);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iMinimumNameWidth = fm.horizontalAdvance(strCompressedName);
+#else
+ int iMinimumNameWidth = fm.width(strCompressedName);
+#endif
+ int iMinimumNameHeight = fm.height();
+
+ /* Calculate minimum width: */
+ int iHeaderWidth = 0;
+ /* Button width: */
+ if (!isRoot())
+ iHeaderWidth += m_toggleButtonSize.width();
+ iHeaderWidth += /* Spacing between button and name: */
+ iHeaderSpacing +
+ /* Minimum name width: */
+ iMinimumNameWidth +
+ /* Spacing between name and info: */
+ iHeaderSpacing;
+ /* Group info width: */
+ if (!m_groupItems.isEmpty())
+ iHeaderWidth += (m_pixmapSizeGroups.width() + m_infoSizeGroups.width());
+ /* Machine info width: */
+ if (!m_machineItems.isEmpty())
+ iHeaderWidth += (m_pixmapSizeMachines.width() + m_infoSizeMachines.width());
+
+ /* Calculate maximum height: */
+ QList<int> heights;
+ /* Button height: */
+ if (!isRoot())
+ heights << m_toggleButtonSize.height();
+ heights /* Minimum name height: */
+ << iMinimumNameHeight
+ /* Group info heights: */
+ << m_pixmapSizeGroups.height() << m_infoSizeGroups.height()
+ /* Machine info heights: */
+ << m_pixmapSizeMachines.height() << m_infoSizeMachines.height();
+ /* Button height: */
+ int iHeaderHeight = 0;
+ foreach (int iHeight, heights)
+ iHeaderHeight = qMax(iHeaderHeight, iHeight);
+
+ /* Calculate new minimum header size: */
+ QSize minimumHeaderSize = QSize(iHeaderWidth, iHeaderHeight);
+
+ /* Is there something changed? */
+ if (m_minimumHeaderSize == minimumHeaderSize)
+ return;
+
+ /* Update linked values: */
+ m_minimumHeaderSize = minimumHeaderSize;
+ updateGeometry();
+}
+
+void UIChooserItemGroup::updateLayoutSpacings()
+{
+ m_pLayout->setItemSpacing(0, m_globalItems.isEmpty() ? 0 : 1);
+ m_pLayout->setItemSpacing(1, m_groupItems.isEmpty() ? 0 : 1);
+ m_pLayout->setItemSpacing(2, m_machineItems.isEmpty() ? 0 : 1);
+}
+
+void UIChooserItemGroup::paintBackground(QPainter *pPainter, const QRect &rect)
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Root-item: */
+ if (isRoot())
+ {
+ /* Acquire background color: */
+ const QColor backgroundColor = QApplication::palette().color(QPalette::Active, QPalette::Window);
+
+ /* Paint default background: */
+ QLinearGradient gradientDefault(rect.topRight(), rect.bottomLeft());
+ gradientDefault.setColorAt(0, backgroundColor.darker(m_iRootBackgroundDarknessStart));
+ gradientDefault.setColorAt(1, backgroundColor.darker(m_iRootBackgroundDarknessFinal));
+ pPainter->fillRect(rect, gradientDefault);
+ }
+ else
+ {
+ /* Acquire background color: */
+ const QColor backgroundColor = model()->selectedItems().contains(this)
+ ? QApplication::palette().color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightness)
+ : QApplication::palette().color(QPalette::Active, QPalette::Window);
+
+ /* Paint default background: */
+ QLinearGradient gradientDefault(rect.topRight(), rect.bottomLeft());
+ gradientDefault.setColorAt(0, backgroundColor.darker(m_iItemBackgroundDarknessStart));
+ gradientDefault.setColorAt(1, backgroundColor.darker(m_iItemBackgroundDarknessFinal));
+ pPainter->fillRect(rect, gradientDefault);
+
+ /* If element is hovered: */
+ if (animatedValue())
+ {
+ /* Calculate header rectangle: */
+ const int iMarginV = data(GroupItemData_MarginV).toInt();
+ const int iFullHeaderHeight = 2 * iMarginV + m_minimumHeaderSize.height();
+ QRect headRect = rect;
+ headRect.setHeight(iFullHeaderHeight);
+
+ /* Acquire header color: */
+ QColor headColor = backgroundColor.lighter(130);
+
+ /* Paint hovered background: */
+ QColor hcTone1 = headColor;
+ QColor hcTone2 = headColor;
+ hcTone1.setAlpha(255 * animatedValue() / 100);
+ hcTone2.setAlpha(0);
+ QLinearGradient gradientHovered(headRect.topLeft(), headRect.bottomLeft());
+ gradientHovered.setColorAt(0, hcTone1);
+ gradientHovered.setColorAt(1, hcTone2);
+ pPainter->fillRect(headRect, gradientHovered);
+ }
+
+ /* Paint drag token UP? */
+ if (dragTokenPlace() != UIChooserItemDragToken_Off)
+ {
+ QLinearGradient dragTokenGradient;
+ QRect dragTokenRect = rect;
+ if (dragTokenPlace() == UIChooserItemDragToken_Up)
+ {
+ dragTokenRect.setHeight(5);
+ dragTokenGradient.setStart(dragTokenRect.bottomLeft());
+ dragTokenGradient.setFinalStop(dragTokenRect.topLeft());
+ }
+ else if (dragTokenPlace() == UIChooserItemDragToken_Down)
+ {
+ dragTokenRect.setTopLeft(dragTokenRect.bottomLeft() - QPoint(0, 5));
+ dragTokenGradient.setStart(dragTokenRect.topLeft());
+ dragTokenGradient.setFinalStop(dragTokenRect.bottomLeft());
+ }
+ dragTokenGradient.setColorAt(0, backgroundColor.darker(dragTokenDarkness()));
+ dragTokenGradient.setColorAt(1, backgroundColor.darker(dragTokenDarkness() + 40));
+ pPainter->fillRect(dragTokenRect, dragTokenGradient);
+ }
+ }
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIChooserItemGroup::paintFrame(QPainter *pPainter, const QRect &rectangle)
+{
+ /* Not for roots: */
+ if (isRoot())
+ return;
+
+ /* Only selected item should have a frame: */
+ if (!model()->selectedItems().contains(this))
+ return;
+
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare variables: */
+ const int iMarginV = data(GroupItemData_MarginV).toInt();
+ const int iParentIndent = data(GroupItemData_ParentIndent).toInt();
+ const int iFullHeaderHeight = 2 * iMarginV + m_minimumHeaderSize.height();
+
+ /* Prepare color: */
+ const QColor frameColor = QApplication::palette().color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightness - 40);
+
+ /* Create/assign pen: */
+ QPen pen(frameColor);
+ pen.setWidth(0);
+ pPainter->setPen(pen);
+
+ /* Calculate top rectangle: */
+ QRect topRect = rectangle;
+ if (nodeToGroupType()->isOpened())
+ topRect.setBottom(topRect.top() + iFullHeaderHeight - 1);
+
+ /* Draw borders: */
+ pPainter->drawLine(rectangle.topLeft(), rectangle.topRight());
+ if (node()->hasNodes() && nodeToGroupType()->isOpened())
+ pPainter->drawLine(topRect.bottomLeft() + QPoint(iParentIndent, 0), topRect.bottomRight() + QPoint(1, 0));
+ else
+ pPainter->drawLine(topRect.bottomLeft(), topRect.bottomRight() + QPoint(1, 0));
+ pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIChooserItemGroup::paintHeader(QPainter *pPainter, const QRect &rect)
+{
+ /* Not for root item: */
+ if (isRoot())
+ return;
+
+ /* Prepare variables: */
+ const int iMarginHL = data(GroupItemData_MarginHL).toInt();
+ const int iMarginHR = data(GroupItemData_MarginHR).toInt();
+ const int iMarginV = data(GroupItemData_MarginV).toInt();
+ const int iHeaderSpacing = data(GroupItemData_HeaderSpacing).toInt();
+ const int iFullHeaderHeight = m_minimumHeaderSize.height();
+
+ /* Selected item foreground: */
+ if (model()->selectedItems().contains(this))
+ {
+ /* Prepare palette: */
+ const QPalette pal = QApplication::palette();
+
+ /* Get background color: */
+ const QColor background = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightness);
+
+ /* Get foreground color: */
+ const QColor simpleText = pal.color(QPalette::Active, QPalette::Text);
+ const QColor highlightText = pal.color(QPalette::Active, QPalette::HighlightedText);
+ QColor lightText = simpleText.black() < highlightText.black() ? simpleText : highlightText;
+ QColor darkText = simpleText.black() > highlightText.black() ? simpleText : highlightText;
+ if (lightText.black() > 128)
+ lightText = QColor(Qt::white);
+ if (darkText.black() < 128)
+ darkText = QColor(Qt::black);
+
+ /* Gather foreground color for background one: */
+ double dLuminance = (0.299 * background.red() + 0.587 * background.green() + 0.114 * background.blue()) / 255;
+ //printf("luminance = %f\n", dLuminance);
+ if (dLuminance > 0.5)
+ pPainter->setPen(darkText);
+ else
+ pPainter->setPen(lightText);
+ }
+
+ /* Paint name: */
+ int iNameX = iMarginHL;
+ if (!isRoot())
+ iNameX += m_toggleButtonSize.width();
+ iNameX += iHeaderSpacing;
+ int iNameY = m_visibleNameSize.height() == iFullHeaderHeight ? iMarginV :
+ iMarginV + (iFullHeaderHeight - m_visibleNameSize.height()) / 2;
+ paintText(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iNameX, iNameY),
+ /* Font to paint text: */
+ m_nameFont,
+ /* Paint device: */
+ model()->paintDevice(),
+ /* Text to paint: */
+ m_strVisibleName);
+
+ /* Should we add more info? */
+ if (isHovered())
+ {
+ /* Indent: */
+ int iHorizontalIndent = rect.right() - iMarginHR;
+
+ /* Should we draw machine count info? */
+ if (!m_strInfoMachines.isEmpty())
+ {
+ iHorizontalIndent -= m_infoSizeMachines.width();
+ int iMachineCountTextX = iHorizontalIndent;
+ int iMachineCountTextY = m_infoSizeMachines.height() == iFullHeaderHeight ?
+ iMarginV : iMarginV + (iFullHeaderHeight - m_infoSizeMachines.height()) / 2;
+ paintText(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iMachineCountTextX, iMachineCountTextY),
+ /* Font to paint text: */
+ m_infoFont,
+ /* Paint device: */
+ model()->paintDevice(),
+ /* Text to paint: */
+ m_strInfoMachines);
+
+ iHorizontalIndent -= m_pixmapSizeMachines.width();
+ int iMachinePixmapX = iHorizontalIndent;
+ int iMachinePixmapY = m_pixmapSizeMachines.height() == iFullHeaderHeight ?
+ iMarginV : iMarginV + (iFullHeaderHeight - m_pixmapSizeMachines.height()) / 2;
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iMachinePixmapX, iMachinePixmapY),
+ /* Pixmap to paint: */
+ m_machinesPixmap);
+ }
+
+ /* Should we draw group count info? */
+ if (!m_strInfoGroups.isEmpty())
+ {
+ iHorizontalIndent -= m_infoSizeGroups.width();
+ int iGroupCountTextX = iHorizontalIndent;
+ int iGroupCountTextY = m_infoSizeGroups.height() == iFullHeaderHeight ?
+ iMarginV : iMarginV + (iFullHeaderHeight - m_infoSizeGroups.height()) / 2;
+ paintText(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iGroupCountTextX, iGroupCountTextY),
+ /* Font to paint text: */
+ m_infoFont,
+ /* Paint device: */
+ model()->paintDevice(),
+ /* Text to paint: */
+ m_strInfoGroups);
+
+ iHorizontalIndent -= m_pixmapSizeGroups.width();
+ int iGroupPixmapX = iHorizontalIndent;
+ int iGroupPixmapY = m_pixmapSizeGroups.height() == iFullHeaderHeight ?
+ iMarginV : iMarginV + (iFullHeaderHeight - m_pixmapSizeGroups.height()) / 2;
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iGroupPixmapX, iGroupPixmapY),
+ /* Pixmap to paint: */
+ m_groupsPixmap);
+ }
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIEditorGroupRename implementation. *
+*********************************************************************************************************************************/
+
+UIEditorGroupRename::UIEditorGroupRename(const QString &strName)
+ : QWidget(0, Qt::Popup)
+ , m_pLineEdit(0)
+{
+ /* Create layout: */
+ QHBoxLayout *pLayout = new QHBoxLayout(this);
+ if (pLayout)
+ {
+ /* Configure layout: */
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create line-edit: */
+ m_pLineEdit = new QLineEdit(strName);
+ if (m_pLineEdit)
+ {
+ setFocusProxy(m_pLineEdit);
+ m_pLineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ m_pLineEdit->setTextMargins(0, 0, 0, 0);
+ connect(m_pLineEdit, &QLineEdit::returnPressed,
+ this, &UIEditorGroupRename::sigEditingFinished);
+ }
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pLineEdit);
+ }
+}
+
+QString UIEditorGroupRename::text() const
+{
+ return m_pLineEdit->text();
+}
+
+void UIEditorGroupRename::setText(const QString &strText)
+{
+ m_pLineEdit->setText(strText);
+}
+
+void UIEditorGroupRename::setFont(const QFont &font)
+{
+ QWidget::setFont(font);
+ m_pLineEdit->setFont(font);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGroup.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGroup.h
new file mode 100644
index 00000000..fa2a2517
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGroup.h
@@ -0,0 +1,446 @@
+/* $Id: UIChooserItemGroup.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserItemGroup class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserItemGroup_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserItemGroup_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIChooserItem.h"
+
+/* Forward declarations: */
+class QGraphicsLinearLayout;
+class QLineEdit;
+class UIChooserNodeGroup;
+class UIEditorGroupRename;
+class UIGraphicsButton;
+class UIGraphicsRotatorButton;
+class UIGraphicsScrollArea;
+
+
+/** UIChooserItem extension implementing group item. */
+class UIChooserItemGroup : public UIChooserItem
+{
+ Q_OBJECT;
+ Q_PROPERTY(int additionalHeight READ additionalHeight WRITE setAdditionalHeight);
+
+signals:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Notifies listeners about toggle start. */
+ void sigToggleStarted();
+ /** Notifies listeners about toggle finish. */
+ void sigToggleFinished();
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Notifies listeners about @a iMinimumWidthHint change. */
+ void sigMinimumWidthHintChanged(int iMinimumWidthHint);
+ /** @} */
+
+public:
+
+ /** RTTI required for qgraphicsitem_cast. */
+ enum { Type = UIChooserNodeType_Group };
+
+ /** Build item for certain @a pNode, adding it directly to the @a pScene. */
+ UIChooserItemGroup(QGraphicsScene *pScene, UIChooserNodeGroup *pNode);
+ /** Build item for certain @a pNode, passing @a pParent to the base-class. */
+ UIChooserItemGroup(UIChooserItem *pParent, UIChooserNodeGroup *pNode);
+ /** Destructs group item. */
+ virtual ~UIChooserItemGroup() RT_OVERRIDE;
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns group node reference. */
+ UIChooserNodeGroup *nodeToGroupType() const;
+ /** Returns item machine id. */
+ QUuid id() const;
+ /** Returns group node type. */
+ UIChooserNodeGroupType groupType() const;
+
+ /** Returns whether group is closed. */
+ bool isClosed() const;
+ /** Closes group in @a fAnimated way if requested. */
+ void close(bool fAnimated = true);
+
+ /** Returns whether group is opened. */
+ bool isOpened() const;
+ /** Opens group in @a fAnimated way if requested. */
+ void open(bool fAnimated = true);
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Updates positions of favorite items. */
+ void updateFavorites();
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Returns scrolling location value in pixels. */
+ int scrollingValue() const;
+ /** Defines scrolling location @a iValue in pixels. */
+ void setScrollingValue(int iValue);
+ /** Performs scrolling by @a iDelta pixels. */
+ void scrollBy(int iDelta);
+
+ /** Makes sure passed @a pItem is visible within the current root item.
+ * @note Please keep in mind that any group item can be a root, but there
+ * is just one model root item at the same time, accessible via model's
+ * root() getter, and this API can be called for current root item only,
+ * because this is root item who performs actual scrolling, while
+ * @a pItem itself can be on any level of embedding. */
+ void makeSureItemIsVisible(UIChooserItem *pItem);
+
+ /** Class-name used for drag&drop mime-data format. */
+ static QString className();
+ /** @} */
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles hover enter @a event. */
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+ /** Handles hover leave @a event. */
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns RTTI item type. */
+ virtual int type() const RT_OVERRIDE { return Type; }
+
+ /** Starts item editing. */
+ virtual void startEditing() RT_OVERRIDE;
+
+ /** Updates item. */
+ virtual void updateItem() RT_OVERRIDE;
+ /** Updates item tool-tip. */
+ virtual void updateToolTip() RT_OVERRIDE;
+
+ /** Installs event-filter for @a pSource object. */
+ virtual void installEventFilterHelper(QObject *pSource) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Returns children items of certain @a enmType. */
+ virtual QList<UIChooserItem*> items(UIChooserNodeType enmType = UIChooserNodeType_Any) const RT_OVERRIDE;
+
+ /** Adds possible @a fFavorite child @a pItem to certain @a iPosition. */
+ virtual void addItem(UIChooserItem *pItem, bool fFavorite, int iPosition) RT_OVERRIDE;
+ /** Removes child @a pItem. */
+ virtual void removeItem(UIChooserItem *pItem) RT_OVERRIDE;
+
+ /** Searches for a first child item answering to specified @a strSearchTag and @a iSearchFlags. */
+ virtual UIChooserItem *searchForItem(const QString &strSearchTag, int iSearchFlags) RT_OVERRIDE;
+
+ /** Searches for a first machine child item. */
+ virtual UIChooserItem *firstMachineItem() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates geometry. */
+ virtual void updateGeometry() RT_OVERRIDE;
+
+ /** Updates layout. */
+ virtual void updateLayout() RT_OVERRIDE;
+
+ /** Returns minimum width-hint. */
+ virtual int minimumWidthHint() const RT_OVERRIDE;
+ /** Returns minimum height-hint. */
+ virtual int minimumHeightHint() const RT_OVERRIDE;
+
+ /** Returns size-hint.
+ * @param enmWhich Brings size-hint type.
+ * @param constraint Brings size constraint. */
+ virtual QSizeF sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint = QSizeF()) const RT_OVERRIDE;
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Returns pixmap item representation. */
+ virtual QPixmap toPixmap() RT_OVERRIDE;
+
+ /** Returns whether item drop is allowed.
+ * @param pEvent Brings information about drop event.
+ * @param enmPlace Brings the place of drag token to the drop moment. */
+ virtual bool isDropAllowed(QGraphicsSceneDragDropEvent *pEvent, UIChooserItemDragToken where) const RT_OVERRIDE;
+ /** Processes item drop.
+ * @param pEvent Brings information about drop event.
+ * @param pFromWho Brings the item according to which we choose drop position.
+ * @param enmPlace Brings the place of drag token to the drop moment (according to item mentioned above). */
+ virtual void processDrop(QGraphicsSceneDragDropEvent *pEvent, UIChooserItem *pFromWho, UIChooserItemDragToken where) RT_OVERRIDE;
+ /** Reset drag token. */
+ virtual void resetDragToken() RT_OVERRIDE;
+
+ /** Returns D&D mime data. */
+ virtual QMimeData *createMimeData() RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles top-level window remaps. */
+ void sltHandleWindowRemapped();
+
+ /** Handles name editing trigger. */
+ void sltNameEditingFinished();
+
+ /** Handles group toggle start. */
+ void sltGroupToggleStart();
+ /** Handles group toggle finish for group finally @a fToggled. */
+ void sltGroupToggleFinish(bool fToggled);
+ /** @} */
+
+private:
+
+ /** Data field types. */
+ enum GroupItemData
+ {
+ /* Layout hints: */
+ GroupItemData_MarginHL,
+ GroupItemData_MarginHR,
+ GroupItemData_MarginV,
+ GroupItemData_HeaderSpacing,
+ GroupItemData_ChildrenSpacing,
+ GroupItemData_ParentIndent,
+ };
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns abstractly stored data value for certain @a iKey. */
+ QVariant data(int iKey) const;
+
+ /** Returns additional height. */
+ int additionalHeight() const;
+ /** Defines @a iAdditionalHeight. */
+ void setAdditionalHeight(int iAdditionalHeight);
+
+ /** Updates animation parameters. */
+ void updateAnimationParameters();
+ /** Updates toggle-button tool-tip. */
+ void updateToggleButtonToolTip();
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Copies group contents from @a pCopyFrom node recursively. */
+ void copyContents(UIChooserNodeGroup *pCopyFrom);
+
+ /** Returns whether group contains machine with @a uId. */
+ bool isContainsMachine(const QUuid &uId) const;
+ /** Returns whether group contains locked machine. */
+ bool isContainsLockedMachine();
+
+ /** Updates user count info. */
+ void updateItemCountInfo();
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Returns minimum width-hint depending on whether @a fGroupOpened. */
+ int minimumWidthHintForGroup(bool fGroupOpened) const;
+ /** Returns minimum height-hint depending on whether @a fGroupOpened. */
+ int minimumHeightHintForGroup(bool fGroupOpened) const;
+ /** Returns minimum size-hint depending on whether @a fGroupOpened. */
+ QSizeF minimumSizeHintForGroup(bool fGroupOpened) const;
+
+ /** Updates visible name. */
+ void updateVisibleName();
+ /** Updates pixmaps. */
+ void updatePixmaps();
+ /** Updates minimum header size. */
+ void updateMinimumHeaderSize();
+ /** Updates layout spacings. */
+ void updateLayoutSpacings();
+ /** @} */
+
+ /** @name Painting stuff.
+ * @{ */
+ /** Paints background using specified @a pPainter and certain @a rect. */
+ void paintBackground(QPainter *pPainter, const QRect &rect);
+ /** Paints frame rectangle using specified @a pPainter and certain @a rect. */
+ void paintFrame(QPainter *pPainter, const QRect &rect);
+ /** Paints header using specified @a pPainter and certain @a rect. */
+ void paintHeader(QPainter *pPainter, const QRect &rect);
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Holds the graphics scene reference. */
+ QGraphicsScene *m_pScene;
+
+ /** Holds the cached visible name. */
+ QString m_strVisibleName;
+ /** Holds the cached group children info. */
+ QString m_strInfoGroups;
+ /** Holds the cached machine children info. */
+ QString m_strInfoMachines;
+
+ /** Holds root start background darkness. */
+ int m_iRootBackgroundDarknessStart;
+ /** Holds root final background darkness. */
+ int m_iRootBackgroundDarknessFinal;
+ /** Holds item start background darkness. */
+ int m_iItemBackgroundDarknessStart;
+ /** Holds item final background darkness. */
+ int m_iItemBackgroundDarknessFinal;
+ /** Holds item header highlight lightness. */
+ int m_iHighlightLightness;
+
+ /** Holds aditional height. */
+ int m_iAdditionalHeight;
+
+ /** Holds group children pixmap. */
+ QPixmap m_groupsPixmap;
+ /** Holds machine children pixmap. */
+ QPixmap m_machinesPixmap;
+
+ /** Holds the name font. */
+ QFont m_nameFont;
+ /** Holds the info font. */
+ QFont m_infoFont;
+
+ /** Holds the group toggle button instance. */
+ UIGraphicsRotatorButton *m_pToggleButton;
+
+ /** Holds the group name editor instance. */
+ UIEditorGroupRename *m_pNameEditorWidget;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Holds the favorite children container instance. */
+ QIGraphicsWidget *m_pContainerFavorite;
+ /** Holds the favorite children layout instance. */
+ QGraphicsLinearLayout *m_pLayoutFavorite;
+
+ /** Holds the children scroll-area instance. */
+ UIGraphicsScrollArea *m_pScrollArea;
+ /** Holds the children container instance. */
+ QIGraphicsWidget *m_pContainer;
+
+ /** Holds the main layout instance. */
+ QGraphicsLinearLayout *m_pLayout;
+ /** Holds the global layout instance. */
+ QGraphicsLinearLayout *m_pLayoutGlobal;
+ /** Holds the group layout instance. */
+ QGraphicsLinearLayout *m_pLayoutGroup;
+ /** Holds the machine layout instance. */
+ QGraphicsLinearLayout *m_pLayoutMachine;
+
+ /** Holds the global children list. */
+ QList<UIChooserItem*> m_globalItems;
+ /** Holds the group children list. */
+ QList<UIChooserItem*> m_groupItems;
+ /** Holds the machine children list. */
+ QList<UIChooserItem*> m_machineItems;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds previous minimum width hint. */
+ int m_iPreviousMinimumWidthHint;
+
+ /** Holds cached visible name size. */
+ QSize m_visibleNameSize;
+ /** Holds cached group children pixmap size. */
+ QSize m_pixmapSizeGroups;
+ /** Holds cached machine children pixmap size. */
+ QSize m_pixmapSizeMachines;
+ /** Holds cached group children info size. */
+ QSize m_infoSizeGroups;
+ /** Holds cached machine children info size. */
+ QSize m_infoSizeMachines;
+ /** Holds cached minimum header size. */
+ QSize m_minimumHeaderSize;
+ /** Holds cached toggle button size. */
+ QSize m_toggleButtonSize;
+ /** @} */
+};
+
+
+/** QWidget extension to use as group name editor. */
+class UIEditorGroupRename : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about group editing finished. */
+ void sigEditingFinished();
+
+public:
+
+ /** Constructs group editor with initial @a strName. */
+ UIEditorGroupRename(const QString &strName);
+
+ /** Returns editor text. */
+ QString text() const;
+ /** Defines editor @a strText. */
+ void setText(const QString &strText);
+
+ /** Defines editor @a font. */
+ void setFont(const QFont &font);
+
+private:
+
+ /** Holds the line-edit instance. */
+ QLineEdit *m_pLineEdit;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserItemGroup_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemMachine.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemMachine.cpp
new file mode 100644
index 00000000..f617c86d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemMachine.cpp
@@ -0,0 +1,1315 @@
+/* $Id: UIChooserItemMachine.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserItemMachine class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGraphicsSceneDragDropEvent>
+#include <QGraphicsView>
+#include <QPainter>
+#include <QStyleOptionGraphicsItem>
+#include <QWindow>
+
+/* GUI includes: */
+#include "UIChooserItemGroup.h"
+#include "UIChooserItemMachine.h"
+#include "UIChooserModel.h"
+#include "UIChooserNodeGroup.h"
+#include "UIChooserNodeMachine.h"
+#include "UIIconPool.h"
+#include "UIVirtualBoxManager.h"
+#include "UIVirtualMachineItemCloud.h"
+#include "UIVirtualMachineItemLocal.h"
+
+
+UIChooserItemMachine::UIChooserItemMachine(UIChooserItem *pParent, UIChooserNodeMachine *pNode)
+ : UIChooserItem(pParent, pNode)
+ , m_iDefaultLightnessStart(0)
+ , m_iDefaultLightnessFinal(0)
+ , m_iHoverLightnessStart(0)
+ , m_iHoverLightnessFinal(0)
+ , m_iHighlightLightnessStart(0)
+ , m_iHighlightLightnessFinal(0)
+ , m_iFirstRowMaximumWidth(0)
+ , m_iMinimumNameWidth(0)
+ , m_iMaximumNameWidth(0)
+ , m_iMinimumSnapshotNameWidth(0)
+ , m_iMaximumSnapshotNameWidth(0)
+{
+ prepare();
+}
+
+UIChooserItemMachine::~UIChooserItemMachine()
+{
+ cleanup();
+}
+
+UIChooserNodeMachine *UIChooserItemMachine::nodeToMachineType() const
+{
+ return node() ? node()->toMachineNode() : 0;
+}
+
+QUuid UIChooserItemMachine::id() const
+{
+ return nodeToMachineType() ? nodeToMachineType()->id() : QUuid();
+}
+
+bool UIChooserItemMachine::accessible() const
+{
+ return nodeToMachineType() ? nodeToMachineType()->accessible() : false;
+}
+
+UIVirtualMachineItem *UIChooserItemMachine::cache() const
+{
+ return nodeToMachineType() ? nodeToMachineType()->cache() : 0;
+}
+
+UIVirtualMachineItemType UIChooserItemMachine::cacheType() const
+{
+ return cache() ? cache()->itemType() : UIVirtualMachineItemType_Invalid;
+}
+
+void UIChooserItemMachine::recache()
+{
+ if (cache())
+ cache()->recache();
+}
+
+bool UIChooserItemMachine::isLockedMachine() const
+{
+ /* For local machines only, others always unlocked: */
+ if (cacheType() != UIVirtualMachineItemType_Local)
+ return false;
+
+ /* Acquire local machine state: */
+ AssertPtrReturn(cache()->toLocal(), true);
+ const KMachineState enmState = cache()->toLocal()->machineState();
+ return enmState != KMachineState_PoweredOff
+ && enmState != KMachineState_Saved
+ && enmState != KMachineState_Teleported
+ && enmState != KMachineState_Aborted
+ && enmState != KMachineState_AbortedSaved;
+}
+
+bool UIChooserItemMachine::isToolButtonArea(const QPoint &position, int iMarginMultiplier /* = 1 */) const
+{
+ const int iFullWidth = geometry().width();
+ const int iFullHeight = geometry().height();
+ const int iMarginHR = data(MachineItemData_MarginHR).toInt();
+ const int iButtonMargin = data(MachineItemData_ButtonMargin).toInt();
+ const int iToolPixmapX = iFullWidth - iMarginHR - 1 - m_toolPixmap.width() / m_toolPixmap.devicePixelRatio();
+ const int iToolPixmapY = (iFullHeight - m_toolPixmap.height() / m_toolPixmap.devicePixelRatio()) / 2;
+ QRect rect = QRect(iToolPixmapX,
+ iToolPixmapY,
+ m_toolPixmap.width() / m_toolPixmap.devicePixelRatio(),
+ m_toolPixmap.height() / m_toolPixmap.devicePixelRatio());
+ rect.adjust(-iMarginMultiplier * iButtonMargin, -iMarginMultiplier * iButtonMargin,
+ iMarginMultiplier * iButtonMargin, iMarginMultiplier * iButtonMargin);
+ return rect.contains(position);
+}
+
+/* static */
+QString UIChooserItemMachine::className()
+{
+ return "UIChooserItemMachine";
+}
+
+/* static */
+void UIChooserItemMachine::enumerateMachineItems(const QList<UIChooserItem*> &il,
+ QList<UIChooserItemMachine*> &ol,
+ int iEnumerationFlags /* = 0 */)
+{
+ /* Enumerate all the passed items: */
+ foreach (UIChooserItem *pItem, il)
+ {
+ /* If that is machine-item: */
+ AssertPtrReturnVoid(pItem);
+ if (pItem->type() == UIChooserNodeType_Machine)
+ {
+ /* Get the iterated machine-item: */
+ UIChooserItemMachine *pMachineItem = pItem->toMachineItem();
+ AssertPtrReturnVoid(pMachineItem);
+ /* Skip if exactly this item is already enumerated: */
+ if (ol.contains(pMachineItem))
+ continue;
+ /* Skip if item with same ID is already enumerated but we need unique: */
+ if ((iEnumerationFlags & UIChooserItemMachineEnumerationFlag_Unique) &&
+ checkIfContains(ol, pMachineItem))
+ continue;
+ /* Skip if this item is accessible and we no need it: */
+ if ((iEnumerationFlags & UIChooserItemMachineEnumerationFlag_Inaccessible) &&
+ pMachineItem->accessible())
+ continue;
+ /* Add it: */
+ ol << pMachineItem;
+ }
+ /* If that is group-item: */
+ else if (pItem->type() == UIChooserNodeType_Group)
+ {
+ /* Enumerate all the machine-items recursively: */
+ enumerateMachineItems(pItem->items(UIChooserNodeType_Machine), ol, iEnumerationFlags);
+ /* Enumerate all the group-items recursively: */
+ enumerateMachineItems(pItem->items(UIChooserNodeType_Group), ol, iEnumerationFlags);
+ }
+ }
+}
+
+void UIChooserItemMachine::retranslateUi()
+{
+}
+
+void UIChooserItemMachine::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIChooserItem::showEvent(pEvent);
+
+ /* Recache and update pixmaps: */
+ AssertPtrReturnVoid(cache());
+ cache()->recachePixmap();
+ updatePixmaps();
+}
+
+void UIChooserItemMachine::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIChooserItem::resizeEvent(pEvent);
+
+ /* What is the new geometry? */
+ const QRectF newGeometry = geometry();
+
+ /* Should we update visible name? */
+ if (previousGeometry().width() != newGeometry.width())
+ updateFirstRowMaximumWidth();
+
+ /* Remember the new geometry: */
+ setPreviousGeometry(newGeometry);
+}
+
+void UIChooserItemMachine::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIChooserItem::mousePressEvent(pEvent);
+ /* No drag for inaccessible: */
+ if (!accessible())
+ pEvent->ignore();
+}
+
+void UIChooserItemMachine::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget* /* pWidget = 0 */)
+{
+ /* Acquire rectangle: */
+ const QRect rectangle = pOptions->rect;
+
+ /* Paint background: */
+ paintBackground(pPainter, rectangle);
+ /* Paint frame: */
+ paintFrame(pPainter, rectangle);
+ /* Paint machine info: */
+ paintMachineInfo(pPainter, rectangle);
+}
+
+void UIChooserItemMachine::setSelected(bool fSelected)
+{
+ /* Call to base-class: */
+ UIChooserItem::setSelected(fSelected);
+
+ /* Special treatment for real cloud items: */
+ if (cacheType() == UIVirtualMachineItemType_CloudReal)
+ {
+ UIVirtualMachineItemCloud *pCloudMachineItem = cache()->toCloud();
+ AssertPtrReturnVoid(pCloudMachineItem);
+ if (fSelected && pCloudMachineItem->accessible())
+ pCloudMachineItem->updateInfoAsync(false /* delayed? */, true /* subscribe */);
+ else
+ pCloudMachineItem->stopAsyncUpdates();
+ }
+}
+
+void UIChooserItemMachine::startEditing()
+{
+ AssertMsgFailed(("Machine graphics item do NOT support editing yet!"));
+}
+
+void UIChooserItemMachine::updateItem()
+{
+ /* Update this machine-item: */
+ updatePixmaps();
+ updateMinimumNameWidth();
+ updateVisibleName();
+ updateMinimumSnapshotNameWidth();
+ updateVisibleSnapshotName();
+ updateStateTextSize();
+ updateToolTip();
+ update();
+
+ /* Update parent group-item: */
+ parentItem()->updateToolTip();
+ parentItem()->update();
+}
+
+void UIChooserItemMachine::updateToolTip()
+{
+ AssertPtrReturnVoid(cache());
+ setToolTip(cache()->toolTipText());
+}
+
+QList<UIChooserItem*> UIChooserItemMachine::items(UIChooserNodeType) const
+{
+ AssertMsgFailed(("Machine graphics item do NOT support children!"));
+ return QList<UIChooserItem*>();
+}
+
+void UIChooserItemMachine::addItem(UIChooserItem*, bool, int)
+{
+ AssertMsgFailed(("Machine graphics item do NOT support children!"));
+}
+
+void UIChooserItemMachine::removeItem(UIChooserItem*)
+{
+ AssertMsgFailed(("Machine graphics item do NOT support children!"));
+}
+
+UIChooserItem* UIChooserItemMachine::searchForItem(const QString &strSearchTag, int iSearchFlags)
+{
+ /* Ignore if we are not searching for the machine-item: */
+ if (!(iSearchFlags & UIChooserItemSearchFlag_Machine))
+ return 0;
+
+ /* Are we searching by the exact ID? */
+ if (iSearchFlags & UIChooserItemSearchFlag_ExactId)
+ {
+ /* Exact ID doesn't match? */
+ if (id() != QUuid(strSearchTag))
+ return 0;
+ }
+ /* Are we searching by the exact name? */
+ else if (iSearchFlags & UIChooserItemSearchFlag_ExactName)
+ {
+ /* Exact name doesn't match? */
+ if (name() != strSearchTag)
+ return 0;
+ }
+ /* Are we searching by the few first symbols? */
+ else
+ {
+ /* Name doesn't start with passed symbols? */
+ if (!name().startsWith(strSearchTag, Qt::CaseInsensitive))
+ return 0;
+ }
+
+ /* Returning this: */
+ return this;
+}
+
+UIChooserItem *UIChooserItemMachine::firstMachineItem()
+{
+ return this;
+}
+
+void UIChooserItemMachine::updateLayout()
+{
+ // Just do nothing ..
+}
+
+int UIChooserItemMachine::minimumWidthHint() const
+{
+ /* Prepare variables: */
+ const int iMarginHL = data(MachineItemData_MarginHL).toInt();
+ const int iMarginHR = data(MachineItemData_MarginHR).toInt();
+ const int iMajorSpacing = data(MachineItemData_MajorSpacing).toInt();
+ const int iMinorSpacing = data(MachineItemData_MinorSpacing).toInt();
+ const int iButtonMargin = data(MachineItemData_ButtonMargin).toInt();
+
+ /* Calculating proposed width: */
+ int iProposedWidth = 0;
+
+ /* Two margins: */
+ iProposedWidth += iMarginHL + iMarginHR;
+ /* And machine-item content to take into account: */
+ int iTopLineWidth = m_iMinimumNameWidth;
+ /* Only local items can have snapshots: */
+ if ( cacheType() == UIVirtualMachineItemType_Local
+ && !cache()->toLocal()->snapshotName().isEmpty())
+ iTopLineWidth += (iMinorSpacing +
+ m_iMinimumSnapshotNameWidth);
+ int iBottomLineWidth = m_statePixmapSize.width() +
+ iMinorSpacing +
+ m_stateTextSize.width();
+ int iMiddleColumnWidth = qMax(iTopLineWidth, iBottomLineWidth);
+ int iMachineItemWidth = m_pixmapSize.width() +
+ iMajorSpacing +
+ iMiddleColumnWidth +
+ iMajorSpacing +
+ m_toolPixmapSize.width() + 2 * iButtonMargin;
+ iProposedWidth += iMachineItemWidth;
+
+ /* Return result: */
+ return iProposedWidth;
+}
+
+int UIChooserItemMachine::minimumHeightHint() const
+{
+ /* Prepare variables: */
+ const int iMarginV = data(MachineItemData_MarginV).toInt();
+ const int iMachineItemTextSpacing = data(MachineItemData_TextSpacing).toInt();
+ const int iButtonMargin = data(MachineItemData_ButtonMargin).toInt();
+
+ /* Calculating proposed height: */
+ int iProposedHeight = 0;
+
+ /* Two margins: */
+ iProposedHeight += 2 * iMarginV;
+ /* And machine-item content to take into account: */
+ int iTopLineHeight = qMax(m_visibleNameSize.height(), m_visibleSnapshotNameSize.height());
+ int iBottomLineHeight = qMax(m_statePixmapSize.height(), m_stateTextSize.height());
+ int iMiddleColumnHeight = iTopLineHeight +
+ iMachineItemTextSpacing +
+ iBottomLineHeight;
+ QList<int> heights;
+ heights << m_pixmapSize.height() << iMiddleColumnHeight << m_toolPixmapSize.height() + 2 * iButtonMargin;
+ int iMaxHeight = 0;
+ foreach (int iHeight, heights)
+ iMaxHeight = qMax(iMaxHeight, iHeight);
+ iProposedHeight += iMaxHeight;
+
+ /* Return result: */
+ return iProposedHeight;
+}
+
+QSizeF UIChooserItemMachine::sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint /* = QSizeF() */) const
+{
+ /* If Qt::MinimumSize requested: */
+ if (enmWhich == Qt::MinimumSize)
+ return QSizeF(minimumWidthHint(), minimumHeightHint());
+ /* Else call to base-class: */
+ return UIChooserItem::sizeHint(enmWhich, constraint);
+}
+
+QPixmap UIChooserItemMachine::toPixmap()
+{
+ /* Ask item to paint itself into pixmap: */
+ qreal dDpr = gpManager->windowHandle()->devicePixelRatio();
+ QSize actualSize = size().toSize();
+ QPixmap pixmap(actualSize * dDpr);
+ pixmap.setDevicePixelRatio(dDpr);
+ QPainter painter(&pixmap);
+ QStyleOptionGraphicsItem options;
+ options.rect = QRect(QPoint(0, 0), actualSize);
+ paint(&painter, &options);
+ return pixmap;
+}
+
+bool UIChooserItemMachine::isDropAllowed(QGraphicsSceneDragDropEvent *pEvent, UIChooserItemDragToken where) const
+{
+ /* No drops while saving groups: */
+ if (model()->isGroupSavingInProgress())
+ return false;
+ /* If drag token is shown, its up to parent to decide: */
+ if (where != UIChooserItemDragToken_Off)
+ return parentItem()->isDropAllowed(pEvent);
+
+ /* No drops for immutable item: */
+ if (isLockedMachine())
+ return false;
+ /* No drops for inaccessible item: */
+ if (!accessible())
+ return false;
+
+ /* Else we should try to cast mime to known classes: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ if (pMimeData->hasFormat(UIChooserItemMachine::className()))
+ {
+ /* Get passed machine-item: */
+ const UIChooserItemMimeData *pCastedMimeData = qobject_cast<const UIChooserItemMimeData*>(pMimeData);
+ AssertPtrReturn(pCastedMimeData, false);
+ UIChooserItem *pItem = pCastedMimeData->item();
+ AssertPtrReturn(pItem, false);
+ UIChooserItemMachine *pMachineItem = pItem->toMachineItem();
+ AssertPtrReturn(pMachineItem, false);
+
+ /* No drops for cloud items: */
+ if ( cacheType() != UIVirtualMachineItemType_Local
+ || pMachineItem->cacheType() != UIVirtualMachineItemType_Local)
+ return false;
+ /* No drops for immutable item: */
+ if (pMachineItem->isLockedMachine())
+ return false;
+ /* No drops for the same item: */
+ if (pMachineItem->id() == id())
+ return false;
+
+ /* Allow finally: */
+ return true;
+ }
+ /* That was invalid mime: */
+ return false;
+}
+
+void UIChooserItemMachine::processDrop(QGraphicsSceneDragDropEvent *pEvent, UIChooserItem *pFromWho, UIChooserItemDragToken where)
+{
+ /* Get mime: */
+ const QMimeData *pMime = pEvent->mimeData();
+ /* Make sure this handler called by this item (not by children): */
+ AssertMsg(!pFromWho && where == UIChooserItemDragToken_Off, ("Machine graphics item do NOT support children!"));
+ Q_UNUSED(pFromWho);
+ Q_UNUSED(where);
+ if (pMime->hasFormat(UIChooserItemMachine::className()))
+ {
+ switch (pEvent->proposedAction())
+ {
+ case Qt::MoveAction:
+ case Qt::CopyAction:
+ {
+ /* Remember scene: */
+ UIChooserModel *pModel = model();
+
+ /* Get passed item: */
+ const UIChooserItemMimeData *pCastedMime = qobject_cast<const UIChooserItemMimeData*>(pMime);
+ AssertMsg(pCastedMime, ("Can't cast passed mime-data to UIChooserItemMimeData!"));
+ UIChooserNode *pNode = pCastedMime->item()->node();
+
+ /* Group passed item with current-item into the new group: */
+ UIChooserNodeGroup *pNewGroupNode = new UIChooserNodeGroup(parentItem()->node(),
+ parentItem()->node()->nodes().size(),
+ QUuid() /* id */,
+ UIChooserModel::uniqueGroupName(parentItem()->node()),
+ parentItem()->node()->toGroupNode()->groupType(),
+ true /* opened */);
+ UIChooserItemGroup *pNewGroupItem = new UIChooserItemGroup(parentItem(), pNewGroupNode);
+ UIChooserNodeMachine *pNewMachineNode1 = new UIChooserNodeMachine(pNewGroupNode,
+ pNewGroupNode->nodes().size(),
+ nodeToMachineType());
+ new UIChooserItemMachine(pNewGroupItem, pNewMachineNode1);
+ UIChooserNodeMachine *pNewMachineNode2 = new UIChooserNodeMachine(pNewGroupNode,
+ pNewGroupNode->nodes().size(),
+ pNode->toMachineNode());
+ new UIChooserItemMachine(pNewGroupItem, pNewMachineNode2);
+
+ /* If proposed action is 'move': */
+ if (pEvent->proposedAction() == Qt::MoveAction)
+ {
+ /* Delete passed node: */
+ delete pNode;
+ }
+ /* Delete this node: */
+ delete node();
+
+ /* Update model: */
+ pModel->wipeOutEmptyGroups();
+ pModel->updateNavigationItemList();
+ pModel->updateLayout();
+ pModel->setSelectedItem(pNewGroupItem);
+ pModel->saveGroups();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void UIChooserItemMachine::resetDragToken()
+{
+ /* Reset drag token for this item: */
+ if (dragTokenPlace() != UIChooserItemDragToken_Off)
+ {
+ setDragTokenPlace(UIChooserItemDragToken_Off);
+ update();
+ }
+}
+
+QMimeData* UIChooserItemMachine::createMimeData()
+{
+ return new UIChooserItemMimeData(this);
+}
+
+void UIChooserItemMachine::sltHandleWindowRemapped()
+{
+ /* Recache and update pixmaps: */
+ AssertPtrReturnVoid(cache());
+ cache()->recachePixmap();
+ updatePixmaps();
+}
+
+void UIChooserItemMachine::prepare()
+{
+ /* Color tones: */
+#if defined(VBOX_WS_MAC)
+ m_iDefaultLightnessStart = 120;
+ m_iDefaultLightnessFinal = 110;
+ m_iHoverLightnessStart = 125;
+ m_iHoverLightnessFinal = 115;
+ m_iHighlightLightnessStart = 115;
+ m_iHighlightLightnessFinal = 105;
+#elif defined(VBOX_WS_WIN)
+ m_iDefaultLightnessStart = 120;
+ m_iDefaultLightnessFinal = 110;
+ m_iHoverLightnessStart = 220;
+ m_iHoverLightnessFinal = 210;
+ m_iHighlightLightnessStart = 190;
+ m_iHighlightLightnessFinal = 180;
+#else /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+ m_iDefaultLightnessStart = 110;
+ m_iDefaultLightnessFinal = 100;
+ m_iHoverLightnessStart = 125;
+ m_iHoverLightnessFinal = 115;
+ m_iHighlightLightnessStart = 110;
+ m_iHighlightLightnessFinal = 100;
+#endif /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+
+ /* Fonts: */
+ m_nameFont = font();
+ m_nameFont.setWeight(QFont::Bold);
+ m_snapshotNameFont = font();
+ m_stateTextFont = font();
+
+ /* Sizes: */
+ m_iFirstRowMaximumWidth = 0;
+ m_iMinimumNameWidth = 0;
+ m_iMaximumNameWidth = 0;
+ m_iMinimumSnapshotNameWidth = 0;
+ m_iMaximumSnapshotNameWidth = 0;
+
+ /* Add item to the parent: */
+ AssertPtrReturnVoid(parentItem());
+ parentItem()->addItem(this, isFavorite(), position());
+
+ /* Configure connections: */
+ connect(gpManager, &UIVirtualBoxManager::sigWindowRemapped,
+ this, &UIChooserItemMachine::sltHandleWindowRemapped);
+ connect(model(), &UIChooserModel::sigSelectionChanged,
+ this, &UIChooserItemMachine::sltUpdateFirstRowMaximumWidth);
+ connect(this, &UIChooserItemMachine::sigHoverEnter,
+ this, &UIChooserItemMachine::sltUpdateFirstRowMaximumWidth);
+ connect(this, &UIChooserItemMachine::sigHoverLeave,
+ this, &UIChooserItemMachine::sltUpdateFirstRowMaximumWidth);
+
+ /* Init: */
+ updateItem();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIChooserItemMachine::cleanup()
+{
+ /* If that item is current: */
+ if (model()->currentItem() == this)
+ {
+ /* Unset current-item: */
+ model()->setCurrentItem(0);
+ }
+ /* If that item is in selection list: */
+ if (model()->selectedItems().contains(this))
+ {
+ /* Remove item from the selection list: */
+ model()->removeFromSelectedItems(this);
+ }
+ /* If that item is in navigation list: */
+ if (model()->navigationItems().contains(this))
+ {
+ /* Remove item from the navigation list: */
+ model()->removeFromNavigationItems(this);
+ }
+
+ /* Remove item from the parent: */
+ AssertPtrReturnVoid(parentItem());
+ parentItem()->removeItem(this);
+}
+
+QVariant UIChooserItemMachine::data(int iKey) const
+{
+ /* Provide other members with required data: */
+ switch (iKey)
+ {
+ /* Layout hints: */
+ case MachineItemData_MarginHL: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ case MachineItemData_MarginHR: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4 * 5;
+ case MachineItemData_MarginV: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4 * 3;
+ case MachineItemData_MajorSpacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+ case MachineItemData_MinorSpacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+ case MachineItemData_TextSpacing: return 0;
+ case MachineItemData_ButtonMargin: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Default: */
+ default: break;
+ }
+ return QVariant();
+}
+
+void UIChooserItemMachine::updatePixmaps()
+{
+ /* Update pixmap: */
+ updatePixmap();
+ /* Update state-pixmap: */
+ updateStatePixmap();
+ /* Update tool-pixmap: */
+ updateToolPixmap();
+}
+
+void UIChooserItemMachine::updatePixmap()
+{
+ /* Get new pixmap and pixmap-size: */
+ AssertPtrReturnVoid(cache());
+ QSize pixmapSize;
+ QPixmap pixmap = cache()->osPixmap(&pixmapSize);
+ /* Update linked values: */
+ if (m_pixmapSize != pixmapSize)
+ {
+ m_pixmapSize = pixmapSize;
+ updateFirstRowMaximumWidth();
+ updateGeometry();
+ }
+ if (m_pixmap.toImage() != pixmap.toImage())
+ {
+ m_pixmap = pixmap;
+ update();
+ }
+}
+
+void UIChooserItemMachine::updateStatePixmap()
+{
+ /* Determine icon metric: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ /* Get new state-pixmap and state-pixmap size: */
+ AssertPtrReturnVoid(cache());
+ const QIcon stateIcon = cache()->machineStateIcon();
+ AssertReturnVoid(!stateIcon.isNull());
+ const QSize statePixmapSize = QSize(iIconMetric, iIconMetric);
+ const QPixmap statePixmap = stateIcon.pixmap(gpManager->windowHandle(), statePixmapSize);
+ /* Update linked values: */
+ if (m_statePixmapSize != statePixmapSize)
+ {
+ m_statePixmapSize = statePixmapSize;
+ updateGeometry();
+ }
+ if (m_statePixmap.toImage() != statePixmap.toImage())
+ {
+ m_statePixmap = statePixmap;
+ update();
+ }
+}
+
+void UIChooserItemMachine::updateToolPixmap()
+{
+ /* Determine icon metric: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize) * .75;
+ /* Create new tool-pixmap and tool-pixmap size: */
+ const QIcon toolIcon = UIIconPool::iconSet(":/tools_menu_24px.png");
+ AssertReturnVoid(!toolIcon.isNull());
+ const QSize toolPixmapSize = QSize(iIconMetric, iIconMetric);
+ const QPixmap toolPixmap = toolIcon.pixmap(gpManager->windowHandle(), toolPixmapSize);
+ /* Update linked values: */
+ if (m_toolPixmapSize != toolPixmapSize)
+ {
+ m_toolPixmapSize = toolPixmapSize;
+ updateGeometry();
+ }
+ if (m_toolPixmap.toImage() != toolPixmap.toImage())
+ {
+ m_toolPixmap = toolPixmap;
+ update();
+ }
+}
+
+void UIChooserItemMachine::updateFirstRowMaximumWidth()
+{
+ /* Prepare variables: */
+ const int iMarginHL = data(MachineItemData_MarginHL).toInt();
+ const int iMarginHR = data(MachineItemData_MarginHR).toInt();
+ const int iMajorSpacing = data(MachineItemData_MajorSpacing).toInt();
+ const int iButtonMargin = data(MachineItemData_ButtonMargin).toInt();
+
+ /* Calculate new maximum width for the first row: */
+ int iFirstRowMaximumWidth = (int)geometry().width();
+ iFirstRowMaximumWidth -= iMarginHL; /* left margin */
+ iFirstRowMaximumWidth -= m_pixmapSize.width(); /* left pixmap width */
+ iFirstRowMaximumWidth -= iMajorSpacing; /* spacing between left pixmap and name(s) */
+ if ( model()->firstSelectedItem() == this
+ || isHovered())
+ {
+ iFirstRowMaximumWidth -= iMajorSpacing; /* spacing between name(s) and right pixmap */
+ iFirstRowMaximumWidth -= m_toolPixmapSize.width() + 2 * iButtonMargin; /* right pixmap width */
+ }
+ iFirstRowMaximumWidth -= iMarginHR; /* right margin */
+
+ /* Is there something changed? */
+ if (m_iFirstRowMaximumWidth == iFirstRowMaximumWidth)
+ return;
+
+ /* Update linked values: */
+ m_iFirstRowMaximumWidth = iFirstRowMaximumWidth;
+ updateMaximumNameWidth();
+ updateMaximumSnapshotNameWidth();
+}
+
+void UIChooserItemMachine::updateMinimumNameWidth()
+{
+ /* Calculate new minimum name width: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+ QFontMetrics fm(m_nameFont, pPaintDevice);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iMinimumNameWidth = fm.horizontalAdvance(compressText(m_nameFont, pPaintDevice, name(),
+ textWidth(m_nameFont, pPaintDevice, 15)));
+#else
+ int iMinimumNameWidth = fm.width(compressText(m_nameFont, pPaintDevice, name(), textWidth(m_nameFont, pPaintDevice, 15)));
+#endif
+
+ /* Is there something changed? */
+ if (m_iMinimumNameWidth == iMinimumNameWidth)
+ return;
+
+ /* Update linked values: */
+ m_iMinimumNameWidth = iMinimumNameWidth;
+ updateGeometry();
+}
+
+void UIChooserItemMachine::updateMinimumSnapshotNameWidth()
+{
+ /* Calculate new minimum snapshot-name width: */
+ int iMinimumSnapshotNameWidth = 0;
+ /* Is there any snapshot exists? */
+ if ( cacheType() == UIVirtualMachineItemType_Local
+ && !cache()->toLocal()->snapshotName().isEmpty())
+ {
+ QFontMetrics fm(m_snapshotNameFont, model()->paintDevice());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iBracketWidth = fm.horizontalAdvance("()"); /* bracket width */
+ int iActualTextWidth = fm.horizontalAdvance(cache()->toLocal()->snapshotName()); /* snapshot-name width */
+ int iMinimumTextWidth = fm.horizontalAdvance("..."); /* ellipsis width */
+#else
+ int iBracketWidth = fm.width("()"); /* bracket width */
+ int iActualTextWidth = fm.width(cache()->toLocal()->snapshotName()); /* snapshot-name width */
+ int iMinimumTextWidth = fm.width("..."); /* ellipsis width */
+#endif
+ iMinimumSnapshotNameWidth = iBracketWidth + qMin(iActualTextWidth, iMinimumTextWidth);
+ }
+
+ /* Is there something changed? */
+ if (m_iMinimumSnapshotNameWidth == iMinimumSnapshotNameWidth)
+ return;
+
+ /* Update linked values: */
+ m_iMinimumSnapshotNameWidth = iMinimumSnapshotNameWidth;
+ updateMaximumNameWidth();
+ updateGeometry();
+}
+
+void UIChooserItemMachine::updateMaximumNameWidth()
+{
+ /* Calculate new maximum name width: */
+ int iMaximumNameWidth = m_iFirstRowMaximumWidth;
+ /* Do we have a minimum snapshot-name width? */
+ if (m_iMinimumSnapshotNameWidth != 0)
+ {
+ /* Prepare variables: */
+ int iMinorSpacing = data(MachineItemData_MinorSpacing).toInt();
+ /* Take spacing and snapshot-name into account: */
+ iMaximumNameWidth -= (iMinorSpacing + m_iMinimumSnapshotNameWidth);
+ }
+
+ /* Is there something changed? */
+ if (m_iMaximumNameWidth == iMaximumNameWidth)
+ return;
+
+ /* Update linked values: */
+ m_iMaximumNameWidth = iMaximumNameWidth;
+ updateVisibleName();
+}
+
+void UIChooserItemMachine::updateMaximumSnapshotNameWidth()
+{
+ /* Prepare variables: */
+ int iMinorSpacing = data(MachineItemData_MinorSpacing).toInt();
+
+ /* Calculate new maximum snapshot-name width: */
+ int iMaximumSnapshotNameWidth = m_iFirstRowMaximumWidth;
+ iMaximumSnapshotNameWidth -= (iMinorSpacing + m_visibleNameSize.width());
+
+ /* Is there something changed? */
+ if (m_iMaximumSnapshotNameWidth == iMaximumSnapshotNameWidth)
+ return;
+
+ /* Update linked values: */
+ m_iMaximumSnapshotNameWidth = iMaximumSnapshotNameWidth;
+ updateVisibleSnapshotName();
+}
+
+void UIChooserItemMachine::updateVisibleName()
+{
+ /* Prepare variables: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+
+ /* Calculate new visible name and name-size: */
+ QString strVisibleName = compressText(m_nameFont, pPaintDevice, name(), m_iMaximumNameWidth);
+ QSize visibleNameSize = textSize(m_nameFont, pPaintDevice, strVisibleName);
+
+ /* Update linked values: */
+ if (m_visibleNameSize != visibleNameSize)
+ {
+ m_visibleNameSize = visibleNameSize;
+ updateMaximumSnapshotNameWidth();
+ updateGeometry();
+ }
+ if (m_strVisibleName != strVisibleName)
+ {
+ m_strVisibleName = strVisibleName;
+ update();
+ }
+}
+
+void UIChooserItemMachine::updateVisibleSnapshotName()
+{
+ /* Make sure this is local machine item: */
+ if (cacheType() != UIVirtualMachineItemType_Local)
+ return;
+
+ /* Prepare variables: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+
+ /* Calculate new visible snapshot-name: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iBracketWidth = QFontMetrics(m_snapshotNameFont, pPaintDevice).horizontalAdvance("()");
+#else
+ int iBracketWidth = QFontMetrics(m_snapshotNameFont, pPaintDevice).width("()");
+#endif
+ QString strVisibleSnapshotName = compressText(m_snapshotNameFont, pPaintDevice, cache()->toLocal()->snapshotName(),
+ m_iMaximumSnapshotNameWidth - iBracketWidth);
+ strVisibleSnapshotName = QString("(%1)").arg(strVisibleSnapshotName);
+ QSize visibleSnapshotNameSize = textSize(m_snapshotNameFont, pPaintDevice, strVisibleSnapshotName);
+
+ /* Update linked values: */
+ if (m_visibleSnapshotNameSize != visibleSnapshotNameSize)
+ {
+ m_visibleSnapshotNameSize = visibleSnapshotNameSize;
+ updateGeometry();
+ }
+ if (m_strVisibleSnapshotName != strVisibleSnapshotName)
+ {
+ m_strVisibleSnapshotName = strVisibleSnapshotName;
+ update();
+ }
+}
+
+void UIChooserItemMachine::updateStateTextSize()
+{
+ /* Get new state-text and state-text size: */
+ AssertPtrReturnVoid(cache());
+ const QSize stateTextSize = textSize(m_stateTextFont, model()->paintDevice(), cache()->machineStateName());
+
+ /* Update linked values: */
+ if (m_stateTextSize != stateTextSize)
+ {
+ m_stateTextSize = stateTextSize;
+ updateGeometry();
+ }
+}
+
+void UIChooserItemMachine::paintBackground(QPainter *pPainter, const QRect &rectangle)
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare color: */
+ const QPalette pal = QApplication::palette();
+
+ /* Selected-item background: */
+ if (model()->selectedItems().contains(this))
+ {
+ /* Prepare color: */
+ QColor backgroundColor = pal.color(QPalette::Active, QPalette::Highlight);
+ /* Draw gradient: */
+ QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
+ bgGrad.setColorAt(0, backgroundColor.lighter(m_iHighlightLightnessStart));
+ bgGrad.setColorAt(1, backgroundColor.lighter(m_iHighlightLightnessFinal));
+ pPainter->fillRect(rectangle, bgGrad);
+
+ if (isHovered())
+ {
+ /* Prepare color: */
+ QColor animationColor1 = QColor(Qt::white);
+ QColor animationColor2 = QColor(Qt::white);
+#ifdef VBOX_WS_MAC
+ animationColor1.setAlpha(90);
+#else
+ animationColor1.setAlpha(30);
+#endif
+ animationColor2.setAlpha(0);
+ /* Draw hovered-item animated gradient: */
+ QRect animatedRect = rectangle;
+ animatedRect.setWidth(animatedRect.height());
+ const int iLength = 2 * animatedRect.width() + rectangle.width();
+ const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
+ animatedRect.moveLeft(iShift);
+ QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
+ bgAnimatedGrad.setColorAt(0, animationColor2);
+ bgAnimatedGrad.setColorAt(0.1, animationColor2);
+ bgAnimatedGrad.setColorAt(0.5, animationColor1);
+ bgAnimatedGrad.setColorAt(0.9, animationColor2);
+ bgAnimatedGrad.setColorAt(1, animationColor2);
+ pPainter->fillRect(rectangle, bgAnimatedGrad);
+ }
+ }
+ /* Hovered-item background: */
+ else if (isHovered())
+ {
+ /* Prepare color: */
+ QColor backgroundColor = pal.color(QPalette::Active, QPalette::Highlight);
+ /* Draw gradient: */
+ QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
+ bgGrad.setColorAt(0, backgroundColor.lighter(m_iHoverLightnessStart));
+ bgGrad.setColorAt(1, backgroundColor.lighter(m_iHoverLightnessFinal));
+ pPainter->fillRect(rectangle, bgGrad);
+
+ /* Prepare color: */
+ QColor animationColor1 = QColor(Qt::white);
+ QColor animationColor2 = QColor(Qt::white);
+#ifdef VBOX_WS_MAC
+ animationColor1.setAlpha(120);
+#else
+ animationColor1.setAlpha(50);
+#endif
+ animationColor2.setAlpha(0);
+ /* Draw hovered-item animated gradient: */
+ QRect animatedRect = rectangle;
+ animatedRect.setWidth(animatedRect.height());
+ const int iLength = 2 * animatedRect.width() + rectangle.width();
+ const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
+ animatedRect.moveLeft(iShift);
+ QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
+ bgAnimatedGrad.setColorAt(0, animationColor2);
+ bgAnimatedGrad.setColorAt(0.1, animationColor2);
+ bgAnimatedGrad.setColorAt(0.5, animationColor1);
+ bgAnimatedGrad.setColorAt(0.9, animationColor2);
+ bgAnimatedGrad.setColorAt(1, animationColor2);
+ pPainter->fillRect(rectangle, bgAnimatedGrad);
+ }
+ /* Default background: */
+ else
+ {
+ /* Prepare color: */
+ QColor backgroundColor = pal.color(QPalette::Active, QPalette::Window);
+ /* Draw gradient: */
+ QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
+ bgGrad.setColorAt(0, backgroundColor.lighter(m_iDefaultLightnessStart));
+ bgGrad.setColorAt(1, backgroundColor.lighter(m_iDefaultLightnessFinal));
+ pPainter->fillRect(rectangle, bgGrad);
+ }
+
+ /* Paint drag token UP? */
+ if (dragTokenPlace() != UIChooserItemDragToken_Off)
+ {
+ /* Window color: */
+ QColor backgroundColor;
+
+ QLinearGradient dragTokenGradient;
+ QRect dragTokenRect = rectangle;
+ if (dragTokenPlace() == UIChooserItemDragToken_Up)
+ {
+ /* Selected-item background: */
+ if (model()->selectedItems().contains(this))
+ backgroundColor = pal.color(QPalette::Active, QPalette::Highlight);
+ /* Default background: */
+ else
+ backgroundColor = pal.color(QPalette::Active, QPalette::Window);
+
+ dragTokenRect.setHeight(5);
+ dragTokenGradient.setStart(dragTokenRect.bottomLeft());
+ dragTokenGradient.setFinalStop(dragTokenRect.topLeft());
+ }
+ else if (dragTokenPlace() == UIChooserItemDragToken_Down)
+ {
+ /* Selected-item background: */
+ if (model()->selectedItems().contains(this))
+ backgroundColor = pal.color(QPalette::Active, QPalette::Highlight);
+ /* Default background: */
+ else
+ backgroundColor = pal.color(QPalette::Active, QPalette::Window);
+
+ dragTokenRect.setTopLeft(dragTokenRect.bottomLeft() - QPoint(0, 4));
+ dragTokenGradient.setStart(dragTokenRect.topLeft());
+ dragTokenGradient.setFinalStop(dragTokenRect.bottomLeft());
+ }
+ QColor color1 = backgroundColor;
+ QColor color2 = backgroundColor;
+ color1.setAlpha(64);
+ color2.setAlpha(255);
+ dragTokenGradient.setColorAt(0, color1);
+ dragTokenGradient.setColorAt(1, color2);
+ pPainter->fillRect(dragTokenRect, dragTokenGradient);
+ }
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIChooserItemMachine::paintFrame(QPainter *pPainter, const QRect &rectangle)
+{
+ /* Only selected and/or hovered item should have a frame: */
+ if (!model()->selectedItems().contains(this) && !isHovered())
+ return;
+
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare color: */
+ const QPalette pal = QApplication::palette();
+ QColor strokeColor;
+
+ /* Selected-item frame: */
+ if (model()->selectedItems().contains(this))
+ strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightnessStart - 40);
+ /* Hovered-item frame: */
+ else if (isHovered())
+ strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHoverLightnessStart - 40);
+
+ /* Create/assign pen: */
+ QPen pen(strokeColor);
+ pen.setWidth(0);
+ pPainter->setPen(pen);
+
+ /* Draw borders: */
+ if (dragTokenPlace() != UIChooserItemDragToken_Up)
+ pPainter->drawLine(rectangle.topLeft(), rectangle.topRight() + QPoint(1, 0));
+ if (dragTokenPlace() != UIChooserItemDragToken_Down)
+ pPainter->drawLine(rectangle.bottomLeft(), rectangle.bottomRight() + QPoint(1, 0));
+ pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIChooserItemMachine::paintMachineInfo(QPainter *pPainter, const QRect &rectangle)
+{
+ /* Prepare variables: */
+ const int iFullWidth = rectangle.width();
+ const int iFullHeight = rectangle.height();
+ const int iMarginHL = data(MachineItemData_MarginHL).toInt();
+ const int iMarginHR = data(MachineItemData_MarginHR).toInt();
+ const int iMajorSpacing = data(MachineItemData_MajorSpacing).toInt();
+ const int iMinorSpacing = data(MachineItemData_MinorSpacing).toInt();
+ const int iMachineItemTextSpacing = data(MachineItemData_TextSpacing).toInt();
+ const int iButtonMargin = data(MachineItemData_ButtonMargin).toInt();
+
+ /* Selected or hovered item foreground: */
+ if (model()->selectedItems().contains(this) || isHovered())
+ {
+ /* Prepare palette: */
+ const QPalette pal = QApplication::palette();
+
+ /* Get background color: */
+ const QColor highlight = pal.color(QPalette::Active, QPalette::Highlight);
+ const QColor background = model()->selectedItems().contains(this)
+ ? highlight.lighter(m_iHighlightLightnessStart)
+ : highlight.lighter(m_iHoverLightnessStart);
+
+ /* Get foreground color: */
+ const QColor simpleText = pal.color(QPalette::Active, QPalette::Text);
+ const QColor highlightText = pal.color(QPalette::Active, QPalette::HighlightedText);
+ QColor lightText = simpleText.black() < highlightText.black() ? simpleText : highlightText;
+ QColor darkText = simpleText.black() > highlightText.black() ? simpleText : highlightText;
+ if (lightText.black() > 128)
+ lightText = QColor(Qt::white);
+ if (darkText.black() < 128)
+ darkText = QColor(Qt::black);
+
+ /* Gather foreground color for background one: */
+ double dLuminance = (0.299 * background.red() + 0.587 * background.green() + 0.114 * background.blue()) / 255;
+ //printf("luminance = %f\n", dLuminance);
+ if (dLuminance > 0.5)
+ pPainter->setPen(darkText);
+ else
+ pPainter->setPen(lightText);
+ }
+
+ /* Calculate indents: */
+ int iLeftColumnIndent = iMarginHL;
+
+ /* Paint left column: */
+ {
+ /* Prepare variables: */
+ int iMachinePixmapX = iLeftColumnIndent;
+ int iMachinePixmapY = (iFullHeight - m_pixmap.height() / m_pixmap.devicePixelRatio()) / 2;
+ /* Paint pixmap: */
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iMachinePixmapX, iMachinePixmapY),
+ /* Pixmap to paint: */
+ m_pixmap);
+ }
+
+ /* Calculate indents: */
+ int iMiddleColumnIndent = iLeftColumnIndent +
+ m_pixmapSize.width() +
+ iMajorSpacing;
+
+ /* Paint middle column: */
+ {
+ /* Calculate indents: */
+ int iTopLineHeight = qMax(m_visibleNameSize.height(), m_visibleSnapshotNameSize.height());
+ int iBottomLineHeight = qMax(m_statePixmapSize.height(), m_stateTextSize.height());
+ int iRightColumnHeight = iTopLineHeight + iMachineItemTextSpacing + iBottomLineHeight;
+ int iTopLineIndent = (iFullHeight - iRightColumnHeight) / 2 - 1;
+
+ /* Paint top line: */
+ {
+ /* Paint left element: */
+ {
+ /* Prepare variables: */
+ int iNameX = iMiddleColumnIndent;
+ int iNameY = iTopLineIndent;
+ /* Paint name: */
+ paintText(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iNameX, iNameY),
+ /* Font to paint text: */
+ m_nameFont,
+ /* Paint device: */
+ model()->paintDevice(),
+ /* Text to paint: */
+ m_strVisibleName);
+ }
+
+ /* Calculate indents: */
+ int iSnapshotNameIndent = iMiddleColumnIndent +
+ m_visibleNameSize.width() +
+ iMinorSpacing;
+
+ /* Paint middle element: */
+ if ( cacheType() == UIVirtualMachineItemType_Local
+ && !cache()->toLocal()->snapshotName().isEmpty())
+ {
+ /* Prepare variables: */
+ int iSnapshotNameX = iSnapshotNameIndent;
+ int iSnapshotNameY = iTopLineIndent;
+ /* Paint snapshot-name: */
+ paintText(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iSnapshotNameX, iSnapshotNameY),
+ /* Font to paint text: */
+ m_snapshotNameFont,
+ /* Paint device: */
+ model()->paintDevice(),
+ /* Text to paint: */
+ m_strVisibleSnapshotName);
+ }
+ }
+
+ /* Calculate indents: */
+ int iBottomLineIndent = iTopLineIndent + iTopLineHeight + 1;
+
+ /* Paint bottom line: */
+ {
+ /* Paint left element: */
+ {
+ /* Prepare variables: */
+ int iMachineStatePixmapX = iMiddleColumnIndent;
+ int iMachineStatePixmapY = iBottomLineIndent;
+ /* Paint state pixmap: */
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iMachineStatePixmapX, iMachineStatePixmapY),
+ /* Pixmap to paint: */
+ m_statePixmap);
+ }
+
+ /* Calculate indents: */
+ int iMachineStateTextIndent = iMiddleColumnIndent +
+ m_statePixmapSize.width() +
+ iMinorSpacing;
+
+ /* Paint right element: */
+ {
+ /* Prepare variables: */
+ int iMachineStateTextX = iMachineStateTextIndent;
+ int iMachineStateTextY = iBottomLineIndent + 1;
+ /* Paint state text: */
+ AssertPtrReturnVoid(cache());
+ paintText(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iMachineStateTextX, iMachineStateTextY),
+ /* Font to paint text: */
+ m_stateTextFont,
+ /* Paint device: */
+ model()->paintDevice(),
+ /* Text to paint: */
+ cache()->machineStateName());
+ }
+ }
+ }
+
+ /* Calculate indents: */
+ QGraphicsView *pView = model()->scene()->views().first();
+ const QPointF sceneCursorPosition = pView->mapToScene(pView->mapFromGlobal(QCursor::pos()));
+ const QPoint itemCursorPosition = mapFromScene(sceneCursorPosition).toPoint();
+ int iRightColumnIndent = iFullWidth - iMarginHR - 1 - m_toolPixmap.width() / m_toolPixmap.devicePixelRatio();
+
+ /* Paint right column: */
+ if ( model()->firstSelectedItem() == this
+ || isHovered())
+ {
+ /* Prepare variables: */
+ const int iToolPixmapX = iRightColumnIndent;
+ const int iToolPixmapY = (iFullHeight - m_toolPixmap.height() / m_toolPixmap.devicePixelRatio()) / 2;
+ QRect toolButtonRectangle = QRect(iToolPixmapX,
+ iToolPixmapY,
+ m_toolPixmap.width() / m_toolPixmap.devicePixelRatio(),
+ m_toolPixmap.height() / m_toolPixmap.devicePixelRatio());
+ toolButtonRectangle.adjust(- iButtonMargin, -iButtonMargin, iButtonMargin, iButtonMargin);
+
+ /* Paint tool button: */
+ if ( isHovered()
+ && isToolButtonArea(itemCursorPosition, 4))
+ paintFlatButton(/* Painter: */
+ pPainter,
+ /* Button rectangle: */
+ toolButtonRectangle,
+ /* Cursor position: */
+ itemCursorPosition);
+
+ /* Paint pixmap: */
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iToolPixmapX, iToolPixmapY),
+ /* Pixmap to paint: */
+ m_toolPixmap);
+ }
+}
+
+/* static */
+bool UIChooserItemMachine::checkIfContains(const QList<UIChooserItemMachine*> &list, UIChooserItemMachine *pItem)
+{
+ /* Check if passed list contains passed machine-item id: */
+ foreach (UIChooserItemMachine *pIteratedItem, list)
+ if (pIteratedItem->id() == pItem->id())
+ return true;
+ /* Found nothing? */
+ return false;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemMachine.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemMachine.h
new file mode 100644
index 00000000..ade8dd01
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemMachine.h
@@ -0,0 +1,333 @@
+/* $Id: UIChooserItemMachine.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserItemMachine class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserItemMachine_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserItemMachine_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIChooserItem.h"
+#include "UIManagerDefs.h"
+
+/* Forward declarations: */
+class UIChooserNodeMachine;
+class UIVirtualMachineItem;
+
+
+/** UIChooserItem extension implementing machine item. */
+class UIChooserItemMachine : public UIChooserItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** RTTI required for qgraphicsitem_cast. */
+ enum { Type = UIChooserNodeType_Machine };
+
+ /** Build item for certain @a pNode, passing @a pParent to the base-class. */
+ UIChooserItemMachine(UIChooserItem *pParent, UIChooserNodeMachine *pNode);
+ /** Destructs machine item. */
+ virtual ~UIChooserItemMachine() RT_OVERRIDE;
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns machine node reference. */
+ UIChooserNodeMachine *nodeToMachineType() const;
+ /** Returns item machine id. */
+ QUuid id() const;
+ /** Returns whether item accessible. */
+ bool accessible() const;
+
+ /** Returns virtual machine cache instance. */
+ UIVirtualMachineItem *cache() const;
+ /** Returns virtual machine cache type. */
+ UIVirtualMachineItemType cacheType() const;
+
+ /** Recaches item contents. */
+ void recache();
+
+ /** Returns whether VM is locked. */
+ bool isLockedMachine() const;
+
+ /** Returns whether passed @a position belongs to tool button area. */
+ bool isToolButtonArea(const QPoint &position, int iMarginMultiplier = 1) const;
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Class-name used for drag&drop mime-data format. */
+ static QString className();
+
+ /** Enumerates machine items from @a il to @a ol using @a iEnumerationFlags. */
+ static void enumerateMachineItems(const QList<UIChooserItem*> &il,
+ QList<UIChooserItemMachine*> &ol,
+ int iEnumerationFlags = 0);
+ /** @} */
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse press @a pEvent. */
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns RTTI item type. */
+ virtual int type() const RT_OVERRIDE { return Type; }
+
+ /** Defines item as @a fSelected. */
+ virtual void setSelected(bool fSelected) RT_OVERRIDE;
+
+ /** Starts item editing. */
+ virtual void startEditing() RT_OVERRIDE;
+
+ /** Updates item. */
+ virtual void updateItem() RT_OVERRIDE;
+ /** Updates item tool-tip. */
+ virtual void updateToolTip() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Returns children items of certain @a enmType. */
+ virtual QList<UIChooserItem*> items(UIChooserNodeType enmType = UIChooserNodeType_Any) const RT_OVERRIDE;
+
+ /** Adds possible @a fFavorite child @a pItem to certain @a iPosition. */
+ virtual void addItem(UIChooserItem *pItem, bool fFavorite, int iPosition) RT_OVERRIDE;
+ /** Removes child @a pItem. */
+ virtual void removeItem(UIChooserItem *pItem) RT_OVERRIDE;
+
+ /** Searches for a first child item answering to specified @a strSearchTag and @a iSearchFlags. */
+ virtual UIChooserItem *searchForItem(const QString &strSearchTag, int iSearchFlags) RT_OVERRIDE;
+
+ /** Searches for a first machine child item. */
+ virtual UIChooserItem *firstMachineItem() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates layout. */
+ virtual void updateLayout() RT_OVERRIDE;
+
+ /** Returns minimum width-hint. */
+ virtual int minimumWidthHint() const RT_OVERRIDE;
+ /** Returns minimum height-hint. */
+ virtual int minimumHeightHint() const RT_OVERRIDE;
+
+ /** Returns size-hint.
+ * @param enmWhich Brings size-hint type.
+ * @param constraint Brings size constraint. */
+ virtual QSizeF sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint = QSizeF()) const RT_OVERRIDE;
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Returns pixmap item representation. */
+ virtual QPixmap toPixmap() RT_OVERRIDE;
+
+ /** Returns whether item drop is allowed.
+ * @param pEvent Brings information about drop event.
+ * @param enmPlace Brings the place of drag token to the drop moment. */
+ virtual bool isDropAllowed(QGraphicsSceneDragDropEvent *pEvent, UIChooserItemDragToken where) const RT_OVERRIDE;
+ /** Processes item drop.
+ * @param pEvent Brings information about drop event.
+ * @param pFromWho Brings the item according to which we choose drop position.
+ * @param enmPlace Brings the place of drag token to the drop moment (according to item mentioned above). */
+ virtual void processDrop(QGraphicsSceneDragDropEvent *pEvent, UIChooserItem *pFromWho, UIChooserItemDragToken where) RT_OVERRIDE;
+ /** Reset drag token. */
+ virtual void resetDragToken() RT_OVERRIDE;
+
+ /** Returns D&D mime data. */
+ virtual QMimeData *createMimeData() RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles top-level window remaps. */
+ void sltHandleWindowRemapped();
+
+ /** Updates first row maximum width. */
+ void sltUpdateFirstRowMaximumWidth() { updateFirstRowMaximumWidth(); }
+ /** @} */
+
+private:
+
+ /** Data field types. */
+ enum MachineItemData
+ {
+ /* Layout hints: */
+ MachineItemData_MarginHL,
+ MachineItemData_MarginHR,
+ MachineItemData_MarginV,
+ MachineItemData_MajorSpacing,
+ MachineItemData_MinorSpacing,
+ MachineItemData_TextSpacing,
+ MachineItemData_ButtonMargin,
+ };
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns abstractly stored data value for certain @a iKey. */
+ QVariant data(int iKey) const;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates pixmaps. */
+ void updatePixmaps();
+ /** Updates pixmap. */
+ void updatePixmap();
+ /** Updates state pixmap. */
+ void updateStatePixmap();
+ /** Updates tool pixmap. */
+ void updateToolPixmap();
+ /** Updates first row maximum width. */
+ void updateFirstRowMaximumWidth();
+ /** Updates minimum name width. */
+ void updateMinimumNameWidth();
+ /** Updates minimum snapshot name width. */
+ void updateMinimumSnapshotNameWidth();
+ /** Updates maximum name width. */
+ void updateMaximumNameWidth();
+ /** Updates maximum snapshot name width. */
+ void updateMaximumSnapshotNameWidth();
+ /** Updates visible name. */
+ void updateVisibleName();
+ /** Updates visible snapshot name. */
+ void updateVisibleSnapshotName();
+ /** Updates state text size. */
+ void updateStateTextSize();
+ /** @} */
+
+ /** @name Painting stuff.
+ * @{ */
+ /** Paints background using specified @a pPainter and certain @a rectangle. */
+ void paintBackground(QPainter *pPainter, const QRect &rectangle);
+ /** Paints frame using specified @a pPainter and certain @a rectangle. */
+ void paintFrame(QPainter *pPainter, const QRect &rectangle);
+ /** Paints machine info using specified @a pPainter and certain @a rectangle. */
+ void paintMachineInfo(QPainter *pPainter, const QRect &rectangle);
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Returns whether machine items @a list contains passed @a pItem. */
+ static bool checkIfContains(const QList<UIChooserItemMachine*> &list,
+ UIChooserItemMachine *pItem);
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Holds item start default lightness. */
+ int m_iDefaultLightnessStart;
+ /** Holds item final default lightness. */
+ int m_iDefaultLightnessFinal;
+ /** Holds item start hover lightness. */
+ int m_iHoverLightnessStart;
+ /** Holds item final hover lightness. */
+ int m_iHoverLightnessFinal;
+ /** Holds item start highlight lightness. */
+ int m_iHighlightLightnessStart;
+ /** Holds item final highlight lightness. */
+ int m_iHighlightLightnessFinal;
+
+ /** Holds item pixmap. */
+ QPixmap m_pixmap;
+ /** Holds item state pixmap. */
+ QPixmap m_statePixmap;
+ /** Holds item tool pixmap. */
+ QPixmap m_toolPixmap;
+
+ /** Holds item visible name. */
+ QString m_strVisibleName;
+ /** Holds item visible snapshot name. */
+ QString m_strVisibleSnapshotName;
+
+ /** Holds item name font. */
+ QFont m_nameFont;
+ /** Holds item snapshot name font. */
+ QFont m_snapshotNameFont;
+ /** Holds item state text font. */
+ QFont m_stateTextFont;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds pixmap size. */
+ QSize m_pixmapSize;
+ /** Holds state pixmap size. */
+ QSize m_statePixmapSize;
+ /** Holds tool pixmap size. */
+ QSize m_toolPixmapSize;
+ /** Holds visible name size. */
+ QSize m_visibleNameSize;
+ /** Holds visible snapshot name size. */
+ QSize m_visibleSnapshotNameSize;
+ /** Holds state text size. */
+ QSize m_stateTextSize;
+
+ /** Holds first row maximum width. */
+ int m_iFirstRowMaximumWidth;
+ /** Holds minimum name width. */
+ int m_iMinimumNameWidth;
+ /** Holds maximum name width. */
+ int m_iMaximumNameWidth;
+ /** Holds minimum snapshot name width. */
+ int m_iMinimumSnapshotNameWidth;
+ /** Holds maximum snapshot name width. */
+ int m_iMaximumSnapshotNameWidth;
+ /** @} */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserItemMachine_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.cpp
new file mode 100644
index 00000000..1565e35a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.cpp
@@ -0,0 +1,2042 @@
+/* $Id: UIChooserModel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserModel class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDrag>
+#include <QGraphicsScene>
+#include <QGraphicsSceneContextMenuEvent>
+#include <QGraphicsView>
+#include <QScrollBar>
+#include <QTimer>
+
+/* GUI includes: */
+#include "QIMessageBox.h"
+#include "UICommon.h"
+#include "UIActionPoolManager.h"
+#include "UIChooser.h"
+#include "UIChooserHandlerMouse.h"
+#include "UIChooserHandlerKeyboard.h"
+#include "UIChooserItemGroup.h"
+#include "UIChooserItemGlobal.h"
+#include "UIChooserItemMachine.h"
+#include "UIChooserModel.h"
+#include "UIChooserNode.h"
+#include "UIChooserNodeGroup.h"
+#include "UIChooserNodeGlobal.h"
+#include "UIChooserNodeMachine.h"
+#include "UIChooserView.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINotificationCenter.h"
+#include "UIVirtualBoxManagerWidget.h"
+#include "UIVirtualMachineItemCloud.h"
+#include "UIVirtualMachineItemLocal.h"
+
+/* COM includes: */
+#include "CExtPack.h"
+#include "CExtPackManager.h"
+
+/* Type defs: */
+typedef QSet<QString> UIStringSet;
+
+
+UIChooserModel::UIChooserModel(UIChooser *pParent, UIActionPool *pActionPool)
+ : UIChooserAbstractModel(pParent)
+ , m_pActionPool(pActionPool)
+ , m_pScene(0)
+ , m_pMouseHandler(0)
+ , m_pKeyboardHandler(0)
+ , m_fSelectionSaveAllowed(false)
+ , m_iCurrentSearchResultIndex(-1)
+ , m_iScrollingTokenSize(30)
+ , m_fIsScrollingInProgress(false)
+ , m_iGlobalItemHeightHint(0)
+ , m_pTimerCloudProfileUpdate(0)
+{
+ prepare();
+}
+
+UIChooserModel::~UIChooserModel()
+{
+ cleanup();
+}
+
+void UIChooserModel::init()
+{
+ /* Call to base-class: */
+ UIChooserAbstractModel::init();
+
+ /* Build tree for main root: */
+ buildTreeForMainRoot();
+ /* Load settings: */
+ loadSettings();
+}
+
+UIActionPool *UIChooserModel::actionPool() const
+{
+ return m_pActionPool;
+}
+
+QGraphicsScene *UIChooserModel::scene() const
+{
+ return m_pScene;
+}
+
+UIChooserView *UIChooserModel::view() const
+{
+ return scene() && !scene()->views().isEmpty() ? qobject_cast<UIChooserView*>(scene()->views().first()) : 0;
+}
+
+QPaintDevice *UIChooserModel::paintDevice() const
+{
+ return scene() && !scene()->views().isEmpty() ? scene()->views().first() : 0;
+}
+
+QGraphicsItem *UIChooserModel::itemAt(const QPointF &position, const QTransform &deviceTransform /* = QTransform() */) const
+{
+ return scene() ? scene()->itemAt(position, deviceTransform) : 0;
+}
+
+void UIChooserModel::handleToolButtonClick(UIChooserItem *pItem)
+{
+ switch (pItem->type())
+ {
+ case UIChooserNodeType_Global:
+ emit sigToolMenuRequested(UIToolClass_Global, pItem->mapToScene(QPointF(pItem->size().width(), 0)).toPoint());
+ break;
+ case UIChooserNodeType_Machine:
+ emit sigToolMenuRequested(UIToolClass_Machine, pItem->mapToScene(QPointF(pItem->size().width(), 0)).toPoint());
+ break;
+ default:
+ break;
+ }
+}
+
+void UIChooserModel::handlePinButtonClick(UIChooserItem *pItem)
+{
+ switch (pItem->type())
+ {
+ case UIChooserNodeType_Global:
+ pItem->setFavorite(!pItem->isFavorite());
+ break;
+ default:
+ break;
+ }
+}
+
+void UIChooserModel::setSelectedItems(const QList<UIChooserItem*> &items)
+{
+ /* Is there something changed? */
+ if (m_selectedItems == items)
+ return;
+
+ /* Remember old selected-item list: */
+ const QList<UIChooserItem*> oldCurrentItems = m_selectedItems;
+
+ /* Clear current selected-item list: */
+ m_selectedItems.clear();
+
+ /* Iterate over all the passed items: */
+ foreach (UIChooserItem *pItem, items)
+ {
+ /* Add item to current selected-item list if navigation list contains it: */
+ if (pItem && navigationItems().contains(pItem))
+ m_selectedItems << pItem;
+ else
+ AssertMsgFailed(("Passed item is not in navigation list!"));
+ }
+
+ /* Make sure selection list is never empty if current-item present: */
+ if (m_selectedItems.isEmpty() && currentItem() && navigationItems().contains(currentItem()))
+ m_selectedItems << currentItem();
+
+ /* Is there something really changed? */
+ if (oldCurrentItems == m_selectedItems)
+ return;
+
+ /* Update all the old items (they are no longer selected): */
+ foreach (UIChooserItem *pItem, oldCurrentItems)
+ {
+ pItem->setSelected(false);
+ pItem->update();
+ }
+ /* Update all the new items (they are selected now): */
+ foreach (UIChooserItem *pItem, m_selectedItems)
+ {
+ pItem->setSelected(true);
+ pItem->update();
+ }
+
+ /* Should the selection changes be saved? */
+ if (m_fSelectionSaveAllowed)
+ {
+ /* Acquire first selected item: */
+ UIChooserItem *pFirstSelectedItem = m_selectedItems.value(0);
+ /* If this item is of machine type: */
+ if ( pFirstSelectedItem
+ && pFirstSelectedItem->type() == UIChooserNodeType_Machine)
+ {
+ /* Cast to machine item: */
+ UIChooserItemMachine *pMachineItem = pFirstSelectedItem->toMachineItem();
+ /* If this machine item is of cloud type =>
+ * Choose the parent (profile) group item as the last one selected: */
+ if ( pMachineItem
+ && ( pMachineItem->cacheType() == UIVirtualMachineItemType_CloudFake
+ || pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal))
+ pFirstSelectedItem = pMachineItem->parentItem();
+ }
+ /* Save last selected-item: */
+ gEDataManager->setSelectorWindowLastItemChosen(pFirstSelectedItem ? pFirstSelectedItem->definition() : QString());
+ }
+
+ /* Notify about selection changes: */
+ emit sigSelectionChanged();
+}
+
+void UIChooserModel::setSelectedItem(UIChooserItem *pItem)
+{
+ /* Call for wrapper above: */
+ QList<UIChooserItem*> items;
+ if (pItem)
+ items << pItem;
+ setSelectedItems(items);
+
+ /* Make selected-item current one as well: */
+ setCurrentItem(firstSelectedItem());
+}
+
+void UIChooserModel::setSelectedItem(const QString &strDefinition)
+{
+ /* Search an item by definition: */
+ UIChooserItem *pItem = searchItemByDefinition(strDefinition);
+
+ /* Make sure found item is in navigation list: */
+ if (!pItem || !navigationItems().contains(pItem))
+ return;
+
+ /* Call for wrapper above: */
+ setSelectedItem(pItem);
+}
+
+void UIChooserModel::clearSelectedItems()
+{
+ /* Call for wrapper above: */
+ setSelectedItem(0);
+}
+
+const QList<UIChooserItem*> &UIChooserModel::selectedItems() const
+{
+ return m_selectedItems;
+}
+
+void UIChooserModel::addToSelectedItems(UIChooserItem *pItem)
+{
+ /* Prepare updated list: */
+ QList<UIChooserItem*> list(selectedItems());
+ list << pItem;
+ /* Call for wrapper above: */
+ setSelectedItems(list);
+}
+
+void UIChooserModel::removeFromSelectedItems(UIChooserItem *pItem)
+{
+ /* Prepare updated list: */
+ QList<UIChooserItem*> list(selectedItems());
+ list.removeAll(pItem);
+ /* Call for wrapper above: */
+ setSelectedItems(list);
+}
+
+UIChooserItem *UIChooserModel::firstSelectedItem() const
+{
+ /* Return first of selected-items, if any: */
+ return selectedItems().value(0);
+}
+
+UIVirtualMachineItem *UIChooserModel::firstSelectedMachineItem() const
+{
+ /* Return first machine-item of the selected-item: */
+ return firstSelectedItem()
+ && firstSelectedItem()->firstMachineItem()
+ && firstSelectedItem()->firstMachineItem()->toMachineItem()
+ ? firstSelectedItem()->firstMachineItem()->toMachineItem()->cache()
+ : 0;
+}
+
+QList<UIVirtualMachineItem*> UIChooserModel::selectedMachineItems() const
+{
+ /* Gather list of selected unique machine-items: */
+ QList<UIChooserItemMachine*> currentMachineItemList;
+ UIChooserItemMachine::enumerateMachineItems(selectedItems(), currentMachineItemList,
+ UIChooserItemMachineEnumerationFlag_Unique);
+
+ /* Reintegrate machine-items into valid format: */
+ QList<UIVirtualMachineItem*> currentMachineList;
+ foreach (UIChooserItemMachine *pItem, currentMachineItemList)
+ currentMachineList << pItem->cache();
+ return currentMachineList;
+}
+
+bool UIChooserModel::isGroupItemSelected() const
+{
+ return firstSelectedItem() && firstSelectedItem()->type() == UIChooserNodeType_Group;
+}
+
+bool UIChooserModel::isGlobalItemSelected() const
+{
+ return firstSelectedItem() && firstSelectedItem()->type() == UIChooserNodeType_Global;
+}
+
+bool UIChooserModel::isMachineItemSelected() const
+{
+ return firstSelectedItem() && firstSelectedItem()->type() == UIChooserNodeType_Machine;
+}
+
+bool UIChooserModel::isLocalMachineItemSelected() const
+{
+ return isMachineItemSelected()
+ && firstSelectedItem()->toMachineItem()->cacheType() == UIVirtualMachineItemType_Local;
+}
+
+bool UIChooserModel::isCloudMachineItemSelected() const
+{
+ return isMachineItemSelected()
+ && firstSelectedItem()->toMachineItem()->cacheType() == UIVirtualMachineItemType_CloudReal;
+}
+
+bool UIChooserModel::isSingleGroupSelected() const
+{
+ return selectedItems().size() == 1
+ && firstSelectedItem()->type() == UIChooserNodeType_Group;
+}
+
+bool UIChooserModel::isSingleLocalGroupSelected() const
+{
+ return isSingleGroupSelected()
+ && firstSelectedItem()->toGroupItem()->groupType() == UIChooserNodeGroupType_Local;
+}
+
+bool UIChooserModel::isSingleCloudProviderGroupSelected() const
+{
+ return isSingleGroupSelected()
+ && firstSelectedItem()->toGroupItem()->groupType() == UIChooserNodeGroupType_Provider;
+}
+
+bool UIChooserModel::isSingleCloudProfileGroupSelected() const
+{
+ return isSingleGroupSelected()
+ && firstSelectedItem()->toGroupItem()->groupType() == UIChooserNodeGroupType_Profile;
+}
+
+bool UIChooserModel::isAllItemsOfOneGroupSelected() const
+{
+ /* Make sure at least one item selected: */
+ if (selectedItems().isEmpty())
+ return false;
+
+ /* Determine the parent group of the first item: */
+ UIChooserItem *pFirstParent = firstSelectedItem()->parentItem();
+
+ /* Make sure this parent is not main root-item: */
+ if (pFirstParent == root())
+ return false;
+
+ /* Enumerate selected-item set: */
+ QSet<UIChooserItem*> currentItemSet;
+ foreach (UIChooserItem *pCurrentItem, selectedItems())
+ currentItemSet << pCurrentItem;
+
+ /* Enumerate first parent children set: */
+ QSet<UIChooserItem*> firstParentItemSet;
+ foreach (UIChooserItem *pFirstParentItem, pFirstParent->items())
+ firstParentItemSet << pFirstParentItem;
+
+ /* Check if both sets contains the same: */
+ return currentItemSet == firstParentItemSet;
+}
+
+QString UIChooserModel::fullGroupName() const
+{
+ return isSingleGroupSelected() ? firstSelectedItem()->fullName() : firstSelectedItem()->parentItem()->fullName();
+}
+
+UIChooserItem *UIChooserModel::findClosestUnselectedItem() const
+{
+ /* Take the current-item (if any) as a starting point
+ * and find the closest non-selected-item. */
+ UIChooserItem *pItem = currentItem();
+ if (!pItem)
+ pItem = firstSelectedItem();
+ if (pItem)
+ {
+ int idxBefore = navigationItems().indexOf(pItem) - 1;
+ int idxAfter = idxBefore + 2;
+ while (idxBefore >= 0 || idxAfter < navigationItems().size())
+ {
+ if (idxAfter < navigationItems().size())
+ {
+ pItem = navigationItems().at(idxAfter);
+ if ( !selectedItems().contains(pItem)
+ && ( pItem->type() == UIChooserNodeType_Machine
+ || pItem->type() == UIChooserNodeType_Global))
+ return pItem;
+ ++idxAfter;
+ }
+ if (idxBefore >= 0)
+ {
+ pItem = navigationItems().at(idxBefore);
+ if ( !selectedItems().contains(pItem)
+ && ( pItem->type() == UIChooserNodeType_Machine
+ || pItem->type() == UIChooserNodeType_Global))
+ return pItem;
+ --idxBefore;
+ }
+ }
+ }
+ return 0;
+}
+
+void UIChooserModel::makeSureNoItemWithCertainIdSelected(const QUuid &uId)
+{
+ /* Look for all nodes with passed uId: */
+ QList<UIChooserNode*> matchedNodes;
+ invisibleRoot()->searchForNodes(uId.toString(),
+ UIChooserItemSearchFlag_Machine |
+ UIChooserItemSearchFlag_ExactId,
+ matchedNodes);
+
+ /* Compose a set of items with passed uId: */
+ QSet<UIChooserItem*> matchedItems;
+ foreach (UIChooserNode *pNode, matchedNodes)
+ if (pNode && pNode->item())
+ matchedItems << pNode->item();
+
+ /* If we have at least one of those items currently selected: */
+#ifdef VBOX_IS_QT6_OR_LATER /* we have to use range constructors since 6.0 */
+ {
+ QList<UIChooserItem *> selectedItemsList = selectedItems();
+ QSet<UIChooserItem *> selectedItemsSet(selectedItemsList.begin(), selectedItemsList.end());
+ if (selectedItemsSet.intersects(matchedItems))
+ setSelectedItem(findClosestUnselectedItem());
+ }
+#else
+ if (selectedItems().toSet().intersects(matchedItems))
+ setSelectedItem(findClosestUnselectedItem());
+#endif
+
+ /* If global item is currently chosen, selection should be invalidated: */
+ if (firstSelectedItem() && firstSelectedItem()->type() == UIChooserNodeType_Global)
+ emit sigSelectionInvalidated();
+}
+
+void UIChooserModel::makeSureAtLeastOneItemSelected()
+{
+ /* If we have no item selected but
+ * at least one in the navigation list (global item): */
+ if (!firstSelectedItem() && !navigationItems().isEmpty())
+ {
+ /* We are choosing it, selection should be invalidated: */
+ setSelectedItem(navigationItems().first());
+ emit sigSelectionInvalidated();
+ }
+}
+
+void UIChooserModel::setCurrentItem(UIChooserItem *pItem)
+{
+ /* Make sure real focus unset: */
+ clearRealFocus();
+
+ /* Is there something changed? */
+ if (m_pCurrentItem == pItem)
+ return;
+
+ /* Remember old current-item: */
+ UIChooserItem *pOldCurrentItem = m_pCurrentItem;
+
+ /* Set new current-item: */
+ m_pCurrentItem = pItem;
+
+ /* Disconnect old current-item (if any): */
+ if (pOldCurrentItem)
+ disconnect(pOldCurrentItem, &UIChooserItem::destroyed, this, &UIChooserModel::sltCurrentItemDestroyed);
+ /* Connect new current-item (if any): */
+ if (m_pCurrentItem)
+ connect(m_pCurrentItem.data(), &UIChooserItem::destroyed, this, &UIChooserModel::sltCurrentItemDestroyed);
+
+ /* If dialog is visible and item exists => make it visible as well: */
+ if (view() && view()->window() && root())
+ if (view()->window()->isVisible() && pItem)
+ root()->toGroupItem()->makeSureItemIsVisible(pItem);
+
+ /* Make sure selection list is never empty if current-item present: */
+ if (!firstSelectedItem() && m_pCurrentItem)
+ setSelectedItem(m_pCurrentItem);
+}
+
+UIChooserItem *UIChooserModel::currentItem() const
+{
+ return m_pCurrentItem;
+}
+
+const QList<UIChooserItem*> &UIChooserModel::navigationItems() const
+{
+ return m_navigationItems;
+}
+
+void UIChooserModel::removeFromNavigationItems(UIChooserItem *pItem)
+{
+ AssertMsg(pItem, ("Passed item is invalid!"));
+ m_navigationItems.removeAll(pItem);
+}
+
+void UIChooserModel::updateNavigationItemList()
+{
+ m_navigationItems.clear();
+ m_navigationItems = createNavigationItemList(root());
+}
+
+UIChooserItem *UIChooserModel::searchItemByDefinition(const QString &strDefinition) const
+{
+ /* Null if empty definition passed: */
+ if (strDefinition.isEmpty())
+ return 0;
+
+ /* Parse definition: */
+ UIChooserItem *pItem = 0;
+ const QString strItemType = strDefinition.section('=', 0, 0);
+ const QString strItemDescriptor = strDefinition.section('=', 1, -1);
+ /* Its a local group-item definition? */
+ if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Local))
+ {
+ /* Search for group-item with passed descriptor (name): */
+ pItem = root()->searchForItem(strItemDescriptor,
+ UIChooserItemSearchFlag_LocalGroup |
+ UIChooserItemSearchFlag_FullName);
+ }
+ /* Its a provider group-item definition? */
+ else if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Provider))
+ {
+ /* Search for group-item with passed descriptor (name): */
+ pItem = root()->searchForItem(strItemDescriptor,
+ UIChooserItemSearchFlag_CloudProvider |
+ UIChooserItemSearchFlag_FullName);
+ }
+ /* Its a profile group-item definition? */
+ else if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Profile))
+ {
+ /* Search for group-item with passed descriptor (name): */
+ pItem = root()->searchForItem(strItemDescriptor,
+ UIChooserItemSearchFlag_CloudProfile |
+ UIChooserItemSearchFlag_FullName);
+ }
+ /* Its a global-item definition? */
+ else if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Global))
+ {
+ /* Search for global-item with required name: */
+ pItem = root()->searchForItem(strItemDescriptor,
+ UIChooserItemSearchFlag_Global |
+ UIChooserItemSearchFlag_ExactName);
+ }
+ /* Its a machine-item definition? */
+ else if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Machine))
+ {
+ /* Search for machine-item with required ID: */
+ pItem = root()->searchForItem(strItemDescriptor,
+ UIChooserItemSearchFlag_Machine |
+ UIChooserItemSearchFlag_ExactId);
+ }
+
+ /* Return result: */
+ return pItem;
+}
+
+void UIChooserModel::performSearch(const QString &strSearchTerm, int iSearchFlags)
+{
+ /* Call to base-class: */
+ UIChooserAbstractModel::performSearch(strSearchTerm, iSearchFlags);
+
+ /* Select 1st found item: */
+ selectSearchResult(true);
+}
+
+QList<UIChooserNode*> UIChooserModel::resetSearch()
+{
+ /* Reset search result index: */
+ m_iCurrentSearchResultIndex = -1;
+
+ /* Call to base-class: */
+ return UIChooserAbstractModel::resetSearch();
+}
+
+void UIChooserModel::selectSearchResult(bool fIsNext)
+{
+ /* If nothing was found: */
+ if (searchResult().isEmpty())
+ {
+ /* Reset search result index: */
+ m_iCurrentSearchResultIndex = -1;
+ }
+ /* If something was found: */
+ else
+ {
+ /* Advance index forward: */
+ if (fIsNext)
+ {
+ if (++m_iCurrentSearchResultIndex >= searchResult().size())
+ m_iCurrentSearchResultIndex = 0;
+ }
+ /* Advance index backward: */
+ else
+ {
+ if (--m_iCurrentSearchResultIndex < 0)
+ m_iCurrentSearchResultIndex = searchResult().size() - 1;
+ }
+
+ /* If found item exists: */
+ if (searchResult().at(m_iCurrentSearchResultIndex))
+ {
+ /* Select curresponding found item, make sure it's visible, scroll if necessary: */
+ UIChooserItem *pItem = searchResult().at(m_iCurrentSearchResultIndex)->item();
+ if (pItem)
+ {
+ pItem->makeSureItsVisible();
+ setSelectedItem(pItem);
+ }
+ }
+ }
+
+ /* Update the search widget's match count(s): */
+ if (view())
+ view()->setSearchResultsCount(searchResult().size(), m_iCurrentSearchResultIndex);
+}
+
+void UIChooserModel::setSearchWidgetVisible(bool fVisible)
+{
+ if (view())
+ view()->setSearchWidgetVisible(fVisible);
+}
+
+UIChooserItem *UIChooserModel::root() const
+{
+ return m_pRoot.data();
+}
+
+void UIChooserModel::startEditingSelectedGroupItemName()
+{
+ /* Only for single selected local group: */
+ if (!isSingleLocalGroupSelected())
+ return;
+
+ /* Start editing first selected item name: */
+ firstSelectedItem()->startEditing();
+}
+
+void UIChooserModel::disbandSelectedGroupItem()
+{
+ /* Only for single selected local group: */
+ if (!isSingleLocalGroupSelected())
+ return;
+
+ /* Check if we have collisions between disbandable group children and their potential siblings: */
+ UIChooserItem *pCurrentItem = currentItem();
+ UIChooserNode *pCurrentNode = pCurrentItem->node();
+ UIChooserItem *pParentItem = pCurrentItem->parentItem();
+ UIChooserNode *pParentNode = pParentItem->node();
+ QList<UIChooserNode*> childrenToBeRenamed;
+ foreach (UIChooserNode *pChildNode, pCurrentNode->nodes())
+ {
+ /* Acquire disbandable group child name to check for collision with group siblings: */
+ const QString strChildName = pChildNode->name();
+ UIChooserNode *pCollisionSibling = 0;
+ /* And then compare this child name with all the sibling names: */
+ foreach (UIChooserNode *pSiblingNode, pParentNode->nodes())
+ {
+ /* There can't be a collision between local child and cloud provider sibling: */
+ if ( pSiblingNode->type() == UIChooserNodeType_Group
+ && pSiblingNode->toGroupNode()->groupType() == UIChooserNodeGroupType_Provider)
+ continue;
+ /* If sibling isn't disbandable group itself and has name similar to one of group children: */
+ if (pSiblingNode != pCurrentNode && pSiblingNode->name() == strChildName)
+ {
+ /* We have a collision sibling: */
+ pCollisionSibling = pSiblingNode;
+ break;
+ }
+ }
+ /* If there is a collision sibling: */
+ if (pCollisionSibling)
+ {
+ switch (pChildNode->type())
+ {
+ /* We can't resolve collision automatically for VMs: */
+ case UIChooserNodeType_Machine:
+ {
+ UINotificationMessage::cannotResolveCollisionAutomatically(strChildName, pParentNode->name());
+ return;
+ }
+ /* But we can do it for VM groups: */
+ case UIChooserNodeType_Group:
+ {
+ if (!msgCenter().confirmAutomaticCollisionResolve(strChildName, pParentNode->name()))
+ return;
+ childrenToBeRenamed << pChildNode;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Copy all the children into our parent: */
+ QList<UIChooserItem*> ungroupedItems;
+ foreach (UIChooserNode *pNode, pCurrentNode->nodes())
+ {
+ switch (pNode->type())
+ {
+ case UIChooserNodeType_Group:
+ {
+ UIChooserNodeGroup *pGroupNode = new UIChooserNodeGroup(pParentNode,
+ pParentNode->nodes().size(),
+ pNode->toGroupNode());
+ UIChooserItemGroup *pGroupItem = new UIChooserItemGroup(pParentItem, pGroupNode);
+ if (childrenToBeRenamed.contains(pNode))
+ pGroupNode->setName(uniqueGroupName(pParentNode));
+ ungroupedItems << pGroupItem;
+ break;
+ }
+ case UIChooserNodeType_Machine:
+ {
+ UIChooserNodeMachine *pMachineNode = new UIChooserNodeMachine(pParentNode,
+ pParentNode->nodes().size(),
+ pNode->toMachineNode());
+ UIChooserItemMachine *pMachineItem = new UIChooserItemMachine(pParentItem, pMachineNode);
+ ungroupedItems << pMachineItem;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Delete current group: */
+ delete pCurrentNode;
+
+ /* And update model: */
+ updateTreeForMainRoot();
+
+ /* Choose ungrouped items if present: */
+ if (!ungroupedItems.isEmpty())
+ {
+ setSelectedItems(ungroupedItems);
+ setCurrentItem(firstSelectedItem());
+ }
+ makeSureAtLeastOneItemSelected();
+
+ /* Save groups finally: */
+ saveGroups();
+}
+
+void UIChooserModel::removeSelectedMachineItems()
+{
+ /* Enumerate all the selected machine-items: */
+ QList<UIChooserItemMachine*> selectedMachineItemList;
+ UIChooserItemMachine::enumerateMachineItems(selectedItems(), selectedMachineItemList);
+ /* Enumerate all the existing machine-items: */
+ QList<UIChooserItemMachine*> existingMachineItemList;
+ UIChooserItemMachine::enumerateMachineItems(root()->items(), existingMachineItemList);
+
+ /* Prepare arrays: */
+ QMap<QUuid, bool> verdicts;
+ QList<UIChooserItemMachine*> localMachineItemsToRemove;
+ QList<CMachine> localMachinesToUnregister;
+ QList<UIChooserItemMachine*> cloudMachineItemsToUnregister;
+
+ /* For each selected machine-item: */
+ foreach (UIChooserItemMachine *pMachineItem, selectedMachineItemList)
+ {
+ /* Get machine-item id: */
+ AssertPtrReturnVoid(pMachineItem);
+ const QUuid uId = pMachineItem->id();
+
+ /* We already decided for that machine? */
+ if (verdicts.contains(uId))
+ {
+ /* To remove similar machine items? */
+ if (!verdicts.value(uId))
+ localMachineItemsToRemove << pMachineItem;
+ continue;
+ }
+
+ /* Selected copy count: */
+ int iSelectedCopyCount = 0;
+ foreach (UIChooserItemMachine *pSelectedItem, selectedMachineItemList)
+ {
+ AssertPtrReturnVoid(pSelectedItem);
+ if (pSelectedItem->id() == uId)
+ ++iSelectedCopyCount;
+ }
+ /* Existing copy count: */
+ int iExistingCopyCount = 0;
+ foreach (UIChooserItemMachine *pExistingItem, existingMachineItemList)
+ {
+ AssertPtrReturnVoid(pExistingItem);
+ if (pExistingItem->id() == uId)
+ ++iExistingCopyCount;
+ }
+ /* If selected copy count equal to existing copy count,
+ * we will propose ro unregister machine fully else
+ * we will just propose to remove selected-items: */
+ const bool fVerdict = iSelectedCopyCount == iExistingCopyCount;
+ verdicts.insert(uId, fVerdict);
+ if (fVerdict)
+ {
+ if (pMachineItem->cacheType() == UIVirtualMachineItemType_Local)
+ localMachinesToUnregister.append(pMachineItem->cache()->toLocal()->machine());
+ else if (pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
+ cloudMachineItemsToUnregister.append(pMachineItem);
+ }
+ else
+ localMachineItemsToRemove << pMachineItem;
+ }
+
+ /* If we have something to remove: */
+ if (!localMachineItemsToRemove.isEmpty())
+ removeLocalMachineItems(localMachineItemsToRemove);
+ /* If we have something local to unregister: */
+ if (!localMachinesToUnregister.isEmpty())
+ unregisterLocalMachines(localMachinesToUnregister);
+ /* If we have something cloud to unregister: */
+ if (!cloudMachineItemsToUnregister.isEmpty())
+ unregisterCloudMachineItems(cloudMachineItemsToUnregister);
+}
+
+void UIChooserModel::moveSelectedMachineItemsToGroupItem(const QString &strName /* = QString() */)
+{
+ /* Prepare target group pointers: */
+ UIChooserNodeGroup *pTargetGroupNode = 0;
+ UIChooserItemGroup *pTargetGroupItem = 0;
+ if (strName.isNull())
+ {
+ /* Create new group node in the current root: */
+ pTargetGroupNode = new UIChooserNodeGroup(invisibleRoot(),
+ invisibleRoot()->nodes().size() /* position */,
+ QUuid() /* id */,
+ uniqueGroupName(invisibleRoot()),
+ UIChooserNodeGroupType_Local,
+ true /* opened */);
+ pTargetGroupItem = new UIChooserItemGroup(root(), pTargetGroupNode);
+ }
+ else
+ {
+ /* Search for existing group with certain name: */
+ UIChooserItem *pTargetItem = root()->searchForItem(strName,
+ UIChooserItemSearchFlag_LocalGroup |
+ UIChooserItemSearchFlag_FullName);
+ AssertPtrReturnVoid(pTargetItem);
+ pTargetGroupItem = pTargetItem->toGroupItem();
+ UIChooserNode *pTargetNode = pTargetItem->node();
+ AssertPtrReturnVoid(pTargetNode);
+ pTargetGroupNode = pTargetNode->toGroupNode();
+ }
+ AssertPtrReturnVoid(pTargetGroupNode);
+ AssertPtrReturnVoid(pTargetGroupItem);
+
+ /* For each of currently selected-items: */
+ QStringList busyGroupNames;
+ QStringList busyMachineNames;
+ QList<UIChooserItem*> copiedItems;
+ foreach (UIChooserItem *pItem, selectedItems())
+ {
+ /* For each of known types: */
+ switch (pItem->type())
+ {
+ case UIChooserNodeType_Group:
+ {
+ /* Avoid name collisions: */
+ if (busyGroupNames.contains(pItem->name()))
+ break;
+ /* Add name to busy: */
+ busyGroupNames << pItem->name();
+ /* Copy or move group-item: */
+ UIChooserNodeGroup *pNewGroupSubNode = new UIChooserNodeGroup(pTargetGroupNode,
+ pTargetGroupNode->nodes().size(),
+ pItem->node()->toGroupNode());
+ copiedItems << new UIChooserItemGroup(pTargetGroupItem, pNewGroupSubNode);
+ delete pItem->node();
+ break;
+ }
+ case UIChooserNodeType_Machine:
+ {
+ /* Avoid name collisions: */
+ if (busyMachineNames.contains(pItem->name()))
+ break;
+ /* Add name to busy: */
+ busyMachineNames << pItem->name();
+ /* Copy or move machine-item: */
+ UIChooserNodeMachine *pNewMachineSubNode = new UIChooserNodeMachine(pTargetGroupNode,
+ pTargetGroupNode->nodes().size(),
+ pItem->node()->toMachineNode());
+ copiedItems << new UIChooserItemMachine(pTargetGroupItem, pNewMachineSubNode);
+ delete pItem->node();
+ break;
+ }
+ }
+ }
+
+ /* Update model: */
+ wipeOutEmptyGroups();
+ updateTreeForMainRoot();
+
+ /* Check if we can select copied items: */
+ QList<UIChooserItem*> itemsToSelect;
+ foreach (UIChooserItem *pCopiedItem, copiedItems)
+ if (navigationItems().contains(pCopiedItem))
+ itemsToSelect << pCopiedItem;
+ if (!itemsToSelect.isEmpty())
+ {
+ setSelectedItems(itemsToSelect);
+ setCurrentItem(firstSelectedItem());
+ }
+ else
+ {
+ /* Otherwise check if we can select one of our parents: */
+ UIChooserItem *pItemToSelect = pTargetGroupItem;
+ while ( !navigationItems().contains(pItemToSelect)
+ && pItemToSelect->parentItem() != root())
+ pItemToSelect = pItemToSelect->parentItem();
+ if (navigationItems().contains(pItemToSelect))
+ setSelectedItem(pItemToSelect);
+ }
+
+ /* Save groups finally: */
+ saveGroups();
+}
+
+void UIChooserModel::startOrShowSelectedItems()
+{
+ emit sigStartOrShowRequest();
+}
+
+void UIChooserModel::refreshSelectedMachineItems()
+{
+ /* Gather list of current unique inaccessible machine-items: */
+ QList<UIChooserItemMachine*> inaccessibleMachineItemList;
+ UIChooserItemMachine::enumerateMachineItems(selectedItems(), inaccessibleMachineItemList,
+ UIChooserItemMachineEnumerationFlag_Unique |
+ UIChooserItemMachineEnumerationFlag_Inaccessible);
+
+ /* Prepare item to be selected: */
+ UIChooserItem *pSelectedItem = 0;
+
+ /* For each machine-item: */
+ foreach (UIChooserItemMachine *pItem, inaccessibleMachineItemList)
+ {
+ AssertPtrReturnVoid(pItem);
+ switch (pItem->cacheType())
+ {
+ case UIVirtualMachineItemType_Local:
+ {
+ /* Recache: */
+ pItem->recache();
+
+ /* Became accessible? */
+ if (pItem->accessible())
+ {
+ /* Acquire machine ID: */
+ const QUuid uId = pItem->id();
+ /* Reload this machine: */
+ sltReloadMachine(uId);
+ /* Select first of reloaded items: */
+ if (!pSelectedItem)
+ pSelectedItem = root()->searchForItem(uId.toString(),
+ UIChooserItemSearchFlag_Machine |
+ UIChooserItemSearchFlag_ExactId);
+ }
+
+ break;
+ }
+ case UIVirtualMachineItemType_CloudFake:
+ {
+ /* Compose cloud entity key: */
+ UIChooserItem *pParent = pItem->parentItem();
+ AssertPtrReturnVoid(pParent);
+ UIChooserItem *pParentOfParent = pParent->parentItem();
+ AssertPtrReturnVoid(pParentOfParent);
+
+ /* Create read cloud machine list task: */
+ const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(pParentOfParent->name(), pParent->name());
+ createReadCloudMachineListTask(guiCloudProfileKey, true /* with refresh? */);
+
+ break;
+ }
+ case UIVirtualMachineItemType_CloudReal:
+ {
+ /* Much more simple than for local items, we are not reloading them, just refreshing: */
+ pItem->cache()->toCloud()->updateInfoAsync(false /* delayed */);
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Some item to be selected? */
+ if (pSelectedItem)
+ {
+ pSelectedItem->makeSureItsVisible();
+ setSelectedItem(pSelectedItem);
+ }
+}
+
+void UIChooserModel::sortSelectedGroupItem()
+{
+ /* For single selected group, sort first selected item children: */
+ if (isSingleGroupSelected())
+ firstSelectedItem()->node()->sortNodes();
+ /* Otherwise, sort first selected item neighbors: */
+ else
+ firstSelectedItem()->parentItem()->node()->sortNodes();
+
+ /* Rebuild tree for main root: */
+ buildTreeForMainRoot(true /* preserve selection */);
+}
+
+void UIChooserModel::setCurrentMachineItem(const QUuid &uId)
+{
+ /* Look whether we have such item at all: */
+ UIChooserItem *pItem = root()->searchForItem(uId.toString(),
+ UIChooserItemSearchFlag_Machine |
+ UIChooserItemSearchFlag_ExactId);
+
+ /* Select item if exists: */
+ if (pItem)
+ setSelectedItem(pItem);
+}
+
+void UIChooserModel::setCurrentGlobalItem()
+{
+ /* Look whether we have such item at all: */
+ UIChooserItem *pItem = root()->searchForItem(QString(),
+ UIChooserItemSearchFlag_Global);
+
+ /* Select item if exists: */
+ if (pItem)
+ setSelectedItem(pItem);
+}
+
+void UIChooserModel::setCurrentDragObject(QDrag *pDragObject)
+{
+ /* Make sure real focus unset: */
+ clearRealFocus();
+
+ /* Remember new drag-object: */
+ m_pCurrentDragObject = pDragObject;
+ connect(m_pCurrentDragObject.data(), &QDrag::destroyed,
+ this, &UIChooserModel::sltCurrentDragObjectDestroyed);
+}
+
+void UIChooserModel::lookFor(const QString &strLookupText)
+{
+ if (view())
+ {
+ view()->setSearchWidgetVisible(true);
+ view()->appendToSearchString(strLookupText);
+ }
+}
+
+void UIChooserModel::updateLayout()
+{
+ /* Sanity check. This method can be called when invisible root is
+ * temporary deleted. We should ignore request in such case. */
+ if (!view() || !root())
+ return;
+
+ /* Initialize variables: */
+ const QSize viewportSize = view()->size();
+ const int iViewportWidth = viewportSize.width();
+ const int iViewportHeight = root()->minimumSizeHint().toSize().height();
+
+ /* Move root: */
+ root()->setPos(0, 0);
+ /* Resize root: */
+ root()->resize(iViewportWidth, iViewportHeight);
+ /* Layout root content: */
+ root()->updateLayout();
+}
+
+void UIChooserModel::setGlobalItemHeightHint(int iHint)
+{
+ /* Save and apply global item height hint: */
+ m_iGlobalItemHeightHint = iHint;
+ applyGlobalItemHeightHint();
+}
+
+void UIChooserModel::sltHandleViewResized()
+{
+ /* Relayout: */
+ updateLayout();
+
+ /* Make current item visible asynchronously: */
+ QMetaObject::invokeMethod(this, "sltMakeSureCurrentItemVisible", Qt::QueuedConnection);
+}
+
+bool UIChooserModel::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* Process only scene events: */
+ if (pWatched != scene())
+ return QObject::eventFilter(pWatched, pEvent);
+
+ /* Process only item focused by model: */
+ if (scene()->focusItem())
+ return QObject::eventFilter(pWatched, pEvent);
+
+ /* Checking event-type: */
+ switch (pEvent->type())
+ {
+ /* Keyboard handler: */
+ case QEvent::KeyPress:
+ return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Press);
+ case QEvent::KeyRelease:
+ return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Release);
+ /* Mouse handler: */
+ case QEvent::GraphicsSceneMousePress:
+ return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Press);
+ case QEvent::GraphicsSceneMouseRelease:
+ return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Release);
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_DoubleClick);
+ /* Context-menu handler: */
+ case QEvent::GraphicsSceneContextMenu:
+ return processContextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent*>(pEvent));
+ /* Drag&drop scroll-event (drag-move) handler: */
+ case QEvent::GraphicsSceneDragMove:
+ return processDragMoveEvent(static_cast<QGraphicsSceneDragDropEvent*>(pEvent));
+ /* Drag&drop scroll-event (drag-leave) handler: */
+ case QEvent::GraphicsSceneDragLeave:
+ return processDragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent*>(pEvent));
+ default: break; /* Shut up MSC */
+ }
+
+ /* Call to base-class: */
+ return QObject::eventFilter(pWatched, pEvent);
+}
+
+void UIChooserModel::sltLocalMachineRegistrationChanged(const QUuid &uMachineId, const bool fRegistered)
+{
+ /* Existing VM unregistered => make sure no item with passed uMachineId is selected: */
+ if (!fRegistered)
+ makeSureNoItemWithCertainIdSelected(uMachineId);
+
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltLocalMachineRegistrationChanged(uMachineId, fRegistered);
+
+ /* Existing VM unregistered? */
+ if (!fRegistered)
+ {
+ /* Update tree for main root: */
+ updateTreeForMainRoot();
+ }
+ /* New VM registered? */
+ else
+ {
+ /* Should we show this VM? */
+ if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
+ {
+ /* Rebuild tree for main root: */
+ buildTreeForMainRoot(true /* preserve selection */);
+ /* Search for newly added item: */
+ UIChooserItem *pNewItem = root()->searchForItem(uMachineId.toString(),
+ UIChooserItemSearchFlag_Machine |
+ UIChooserItemSearchFlag_ExactId);
+ /* Select newly added item if any: */
+ if (pNewItem)
+ setSelectedItem(pNewItem);
+ }
+ }
+}
+
+void UIChooserModel::sltHandleCloudProviderUninstall(const QUuid &uProviderId)
+{
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltHandleCloudProviderUninstall(uProviderId);
+
+ /* Notify about selection invalidated: */
+ emit sigSelectionInvalidated();
+}
+
+void UIChooserModel::sltReloadMachine(const QUuid &uMachineId)
+{
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltReloadMachine(uMachineId);
+
+ /* Should we show this VM? */
+ if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
+ {
+ /* Rebuild tree for main root: */
+ buildTreeForMainRoot(false /* preserve selection */);
+ /* Select newly added item: */
+ setSelectedItem(root()->searchForItem(uMachineId.toString(),
+ UIChooserItemSearchFlag_Machine |
+ UIChooserItemSearchFlag_ExactId));
+ }
+ makeSureAtLeastOneItemSelected();
+
+ /* Notify listeners about selection change: */
+ emit sigSelectionChanged();
+}
+
+void UIChooserModel::sltDetachCOM()
+{
+ /* Clean tree for main root: */
+ clearTreeForMainRoot();
+ emit sigSelectionInvalidated();
+
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltDetachCOM();
+}
+
+void UIChooserModel::sltCloudMachineUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QUuid &uId)
+{
+ /* Make sure no item with passed uId is selected: */
+ makeSureNoItemWithCertainIdSelected(uId);
+
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltCloudMachineUnregistered(strProviderShortName, strProfileName, uId);
+
+ /* Rebuild tree for main root: */
+ buildTreeForMainRoot(true /* preserve selection */);
+}
+
+void UIChooserModel::sltCloudMachinesUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QList<QUuid> &ids)
+{
+ /* Make sure no item with one of passed ids is selected: */
+ foreach (const QUuid &uId, ids)
+ makeSureNoItemWithCertainIdSelected(uId);
+
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltCloudMachinesUnregistered(strProviderShortName, strProfileName, ids);
+
+ /* Rebuild tree for main root: */
+ buildTreeForMainRoot(true /* preserve selection */);
+}
+
+void UIChooserModel::sltCloudMachineRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine)
+{
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltCloudMachineRegistered(strProviderShortName, strProfileName, comMachine);
+
+ /* Rebuild tree for main root: */
+ buildTreeForMainRoot(false /* preserve selection */);
+
+ /* Select newly added item: */
+ QUuid uMachineId;
+ if (cloudMachineId(comMachine, uMachineId))
+ setSelectedItem(root()->searchForItem(uMachineId.toString(),
+ UIChooserItemSearchFlag_Machine |
+ UIChooserItemSearchFlag_ExactId));
+}
+
+void UIChooserModel::sltCloudMachinesRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QVector<CCloudMachine> &machines)
+{
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltCloudMachinesRegistered(strProviderShortName, strProfileName, machines);
+
+ /* Rebuild tree for main root: */
+ buildTreeForMainRoot(true /* preserve selection */);
+}
+
+void UIChooserModel::sltHandleReadCloudMachineListTaskComplete()
+{
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete();
+
+ /* Restart cloud profile update timer: */
+ m_pTimerCloudProfileUpdate->start(10000);
+}
+
+void UIChooserModel::sltHandleCloudProfileManagerCumulativeChange()
+{
+ /* Call to base-class: */
+ UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange();
+
+ /* Build tree for main root: */
+ buildTreeForMainRoot(true /* preserve selection */);
+}
+
+void UIChooserModel::sltMakeSureCurrentItemVisible()
+{
+ root()->toGroupItem()->makeSureItemIsVisible(currentItem());
+}
+
+void UIChooserModel::sltCurrentItemDestroyed()
+{
+ AssertMsgFailed(("Current-item destroyed!"));
+}
+
+void UIChooserModel::sltStartScrolling()
+{
+ /* Make sure view exists: */
+ AssertPtrReturnVoid(view());
+
+ /* Should we scroll? */
+ if (!m_fIsScrollingInProgress)
+ return;
+
+ /* Reset scrolling progress: */
+ m_fIsScrollingInProgress = false;
+
+ /* Convert mouse position to view co-ordinates: */
+ const QPoint mousePos = view()->mapFromGlobal(QCursor::pos());
+ /* Mouse position is at the top of view? */
+ if (mousePos.y() < m_iScrollingTokenSize && mousePos.y() > 0)
+ {
+ int iValue = mousePos.y();
+ if (!iValue)
+ iValue = 1;
+ const int iDelta = m_iScrollingTokenSize / iValue;
+ /* Backward scrolling: */
+ root()->toGroupItem()->scrollBy(- 2 * iDelta);
+ m_fIsScrollingInProgress = true;
+ QTimer::singleShot(10, this, SLOT(sltStartScrolling()));
+ }
+ /* Mouse position is at the bottom of view? */
+ else if (mousePos.y() > view()->height() - m_iScrollingTokenSize && mousePos.y() < view()->height())
+ {
+ int iValue = view()->height() - mousePos.y();
+ if (!iValue)
+ iValue = 1;
+ const int iDelta = m_iScrollingTokenSize / iValue;
+ /* Forward scrolling: */
+ root()->toGroupItem()->scrollBy(2 * iDelta);
+ m_fIsScrollingInProgress = true;
+ QTimer::singleShot(10, this, SLOT(sltStartScrolling()));
+ }
+}
+
+void UIChooserModel::sltCurrentDragObjectDestroyed()
+{
+ root()->resetDragToken();
+}
+
+void UIChooserModel::sltHandleCloudMachineRemoved(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QString &strName)
+{
+ Q_UNUSED(strName);
+
+ /* Update profile to make sure it has no stale instances: */
+ const UICloudEntityKey cloudEntityKeyForProfile = UICloudEntityKey(strProviderShortName, strProfileName);
+ createReadCloudMachineListTask(cloudEntityKeyForProfile, false /* with refresh? */);
+}
+
+void UIChooserModel::sltUpdateSelectedCloudProfiles()
+{
+ /* For every selected item: */
+ QSet<UICloudEntityKey> selectedCloudProfileKeys;
+ foreach (UIChooserItem *pSelectedItem, selectedItems())
+ {
+ /* Enumerate cloud profile keys to update: */
+ switch (pSelectedItem->type())
+ {
+ case UIChooserNodeType_Group:
+ {
+ UIChooserItemGroup *pGroupItem = pSelectedItem->toGroupItem();
+ AssertPtrReturnVoid(pGroupItem);
+ switch (pGroupItem->groupType())
+ {
+ case UIChooserNodeGroupType_Provider:
+ {
+ const QString strProviderShortName = pSelectedItem->name();
+ foreach (UIChooserItem *pChildItem, pSelectedItem->items(UIChooserNodeType_Group))
+ {
+ const QString strProfileName = pChildItem->name();
+ const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
+ if (!selectedCloudProfileKeys.contains(guiCloudProfileKey))
+ selectedCloudProfileKeys.insert(guiCloudProfileKey);
+ }
+ break;
+ }
+ case UIChooserNodeGroupType_Profile:
+ {
+ const QString strProviderShortName = pSelectedItem->parentItem()->name();
+ const QString strProfileName = pSelectedItem->name();
+ const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
+ if (!selectedCloudProfileKeys.contains(guiCloudProfileKey))
+ selectedCloudProfileKeys.insert(guiCloudProfileKey);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case UIChooserNodeType_Machine:
+ {
+ UIChooserItemMachine *pMachineItem = pSelectedItem->toMachineItem();
+ AssertPtrReturnVoid(pMachineItem);
+ if ( pMachineItem->cacheType() == UIVirtualMachineItemType_CloudFake
+ || pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
+ {
+ const QString strProviderShortName = pMachineItem->parentItem()->parentItem()->name();
+ const QString strProfileName = pMachineItem->parentItem()->name();
+ const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
+ if (!selectedCloudProfileKeys.contains(guiCloudProfileKey))
+ selectedCloudProfileKeys.insert(guiCloudProfileKey);
+ }
+ break;
+ }
+ }
+ }
+
+ /* Restart List Cloud Machines task for selected profile keys: */
+ foreach (const UICloudEntityKey &guiCloudProfileKey, selectedCloudProfileKeys)
+ createReadCloudMachineListTask(guiCloudProfileKey, false /* with refresh? */);
+}
+
+void UIChooserModel::prepare()
+{
+ prepareScene();
+ prepareContextMenu();
+ prepareHandlers();
+ prepareCloudUpdateTimer();
+ prepareConnections();
+}
+
+void UIChooserModel::prepareScene()
+{
+ m_pScene = new QGraphicsScene(this);
+ if (m_pScene)
+ m_pScene->installEventFilter(this);
+}
+
+void UIChooserModel::prepareContextMenu()
+{
+ /* Context menu for global(s): */
+ m_localMenus[UIChooserNodeType_Global] = new QMenu;
+ if (QMenu *pMenuGlobal = m_localMenus.value(UIChooserNodeType_Global))
+ {
+#ifdef VBOX_WS_MAC
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_About));
+ pMenuGlobal->addSeparator();
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_Preferences));
+ pMenuGlobal->addSeparator();
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ImportAppliance));
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ExportAppliance));
+# ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ShowExtraDataManager));
+ pMenuGlobal->addSeparator();
+# endif
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_M_Tools));
+
+#else /* !VBOX_WS_MAC */
+
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_Preferences));
+ pMenuGlobal->addSeparator();
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ImportAppliance));
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ExportAppliance));
+ pMenuGlobal->addSeparator();
+# ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ShowExtraDataManager));
+ pMenuGlobal->addSeparator();
+# endif
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_M_Tools));
+ pMenuGlobal->addSeparator();
+# ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ if (gEDataManager->applicationUpdateEnabled())
+ pMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_CheckForUpdates));
+# endif
+#endif /* !VBOX_WS_MAC */
+ }
+
+ /* Context menu for local group(s): */
+ m_localMenus[UIChooserNodeType_Group] = new QMenu;
+ if (QMenu *pMenuGroup = m_localMenus.value(UIChooserNodeType_Group))
+ {
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_New));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Add));
+ pMenuGroup->addSeparator();
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Rename));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Remove));
+ pMenuGroup->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_MoveToGroup)->menu());
+ pMenuGroup->addSeparator();
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_T_Pause));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Reset));
+ // pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Detach));
+ pMenuGroup->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Stop)->menu());
+ pMenuGroup->addSeparator();
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Discard));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_ShowLogDialog));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Refresh));
+ pMenuGroup->addSeparator();
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_ShowInFileManager));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_CreateShortcut));
+ pMenuGroup->addSeparator();
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Sort));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_T_Search));
+ }
+
+ /* Context menu for local machine(s): */
+ m_localMenus[UIChooserNodeType_Machine] = new QMenu;
+ if (QMenu *pMenuMachine = m_localMenus.value(UIChooserNodeType_Machine))
+ {
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Clone));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Move));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_ExportToOCI));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Remove));
+ pMenuMachine->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_MoveToGroup)->menu());
+ pMenuMachine->addSeparator();
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_T_Pause));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Reset));
+ // pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Detach));
+ pMenuMachine->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop)->menu());
+ pMenuMachine->addSeparator();
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_ShowLogDialog));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Refresh));
+ pMenuMachine->addSeparator();
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_ShowInFileManager));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_CreateShortcut));
+ pMenuMachine->addSeparator();
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_SortParent));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_T_Search));
+ }
+
+ /* Context menu for cloud group(s): */
+ m_cloudMenus[UIChooserNodeType_Group] = new QMenu;
+ if (QMenu *pMenuGroup = m_cloudMenus.value(UIChooserNodeType_Group))
+ {
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_New));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Add));
+ pMenuGroup->addSeparator();
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow));
+ pMenuGroup->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Console)->menu());
+ pMenuGroup->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Stop)->menu());
+ pMenuGroup->addSeparator();
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Refresh));
+ pMenuGroup->addSeparator();
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Sort));
+ pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_T_Search));
+ }
+
+ /* Context menu for cloud machine(s): */
+ m_cloudMenus[UIChooserNodeType_Machine] = new QMenu;
+ if (QMenu *pMenuMachine = m_cloudMenus.value(UIChooserNodeType_Machine))
+ {
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Remove));
+ pMenuMachine->addSeparator();
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
+ pMenuMachine->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Console)->menu());
+ pMenuMachine->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop)->menu());
+ pMenuMachine->addSeparator();
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Refresh));
+ pMenuMachine->addSeparator();
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_SortParent));
+ pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_T_Search));
+ }
+}
+
+void UIChooserModel::prepareHandlers()
+{
+ m_pMouseHandler = new UIChooserHandlerMouse(this);
+ m_pKeyboardHandler = new UIChooserHandlerKeyboard(this);
+}
+
+void UIChooserModel::prepareCloudUpdateTimer()
+{
+ m_pTimerCloudProfileUpdate = new QTimer;
+ if (m_pTimerCloudProfileUpdate)
+ m_pTimerCloudProfileUpdate->setSingleShot(true);
+}
+
+void UIChooserModel::prepareConnections()
+{
+ connect(this, &UIChooserModel::sigSelectionChanged,
+ this, &UIChooserModel::sltUpdateSelectedCloudProfiles);
+ connect(m_pTimerCloudProfileUpdate, &QTimer::timeout,
+ this, &UIChooserModel::sltUpdateSelectedCloudProfiles);
+}
+
+void UIChooserModel::loadSettings()
+{
+ /* Load last selected-item (choose first if unable to load): */
+ setSelectedItem(gEDataManager->selectorWindowLastItemChosen());
+ makeSureAtLeastOneItemSelected();
+}
+
+void UIChooserModel::cleanupConnections()
+{
+ disconnect(this, &UIChooserModel::sigSelectionChanged,
+ this, &UIChooserModel::sltUpdateSelectedCloudProfiles);
+ disconnect(m_pTimerCloudProfileUpdate, &QTimer::timeout,
+ this, &UIChooserModel::sltUpdateSelectedCloudProfiles);
+}
+
+void UIChooserModel::cleanupCloudUpdateTimer()
+{
+ delete m_pTimerCloudProfileUpdate;
+ m_pTimerCloudProfileUpdate = 0;
+}
+
+void UIChooserModel::cleanupHandlers()
+{
+ delete m_pKeyboardHandler;
+ m_pKeyboardHandler = 0;
+ delete m_pMouseHandler;
+ m_pMouseHandler = 0;
+}
+
+void UIChooserModel::cleanupContextMenu()
+{
+ qDeleteAll(m_localMenus);
+ m_localMenus.clear();
+ qDeleteAll(m_cloudMenus);
+ m_cloudMenus.clear();
+}
+
+void UIChooserModel::cleanupScene()
+{
+ delete m_pScene;
+ m_pScene = 0;
+}
+
+void UIChooserModel::cleanup()
+{
+ cleanupConnections();
+ cleanupCloudUpdateTimer();
+ cleanupHandlers();
+ cleanupContextMenu();
+ cleanupScene();
+}
+
+bool UIChooserModel::processContextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent)
+{
+ /* Whats the reason? */
+ switch (pEvent->reason())
+ {
+ case QGraphicsSceneContextMenuEvent::Mouse:
+ {
+ /* Look for an item under cursor: */
+ if (QGraphicsItem *pItem = itemAt(pEvent->scenePos()))
+ {
+ switch (pItem->type())
+ {
+ case UIChooserNodeType_Global:
+ {
+ /* Global context menu for all global item cases: */
+ m_localMenus.value(UIChooserNodeType_Global)->exec(pEvent->screenPos());
+ break;
+ }
+ case UIChooserNodeType_Group:
+ {
+ /* Get group-item: */
+ UIChooserItemGroup *pGroupItem = qgraphicsitem_cast<UIChooserItemGroup*>(pItem);
+ /* Don't show context menu for root-item: */
+ if (pGroupItem->isRoot())
+ break;
+ /* Make sure we have group-item selected exclusively: */
+ if (selectedItems().contains(pGroupItem) && selectedItems().size() == 1)
+ {
+ /* Group context menu in that case: */
+ if (pGroupItem->groupType() == UIChooserNodeGroupType_Local)
+ m_localMenus.value(UIChooserNodeType_Group)->exec(pEvent->screenPos());
+ else if ( pGroupItem->groupType() == UIChooserNodeGroupType_Provider
+ || pGroupItem->groupType() == UIChooserNodeGroupType_Profile)
+ m_cloudMenus.value(UIChooserNodeType_Group)->exec(pEvent->screenPos());
+ break;
+ }
+ /* Otherwise we have to find a first child machine-item: */
+ else
+ pItem = qobject_cast<UIChooserItem*>(pGroupItem)->firstMachineItem();
+ }
+ RT_FALL_THRU();
+ case UIChooserNodeType_Machine:
+ {
+ /* Get machine-item: */
+ UIChooserItemMachine *pMachineItem = qgraphicsitem_cast<UIChooserItemMachine*>(pItem);
+ /* Machine context menu for other Group/Machine cases: */
+ if (pMachineItem->cacheType() == UIVirtualMachineItemType_Local)
+ m_localMenus.value(UIChooserNodeType_Machine)->exec(pEvent->screenPos());
+ else if (pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
+ m_cloudMenus.value(UIChooserNodeType_Machine)->exec(pEvent->screenPos());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ /* Filter out by default: */
+ return true;
+ }
+ case QGraphicsSceneContextMenuEvent::Keyboard:
+ {
+ /* Get first selected-item: */
+ if (UIChooserItem *pItem = firstSelectedItem())
+ {
+ switch (pItem->type())
+ {
+ case UIChooserNodeType_Global:
+ {
+ /* Global context menu for all global item cases: */
+ m_localMenus.value(UIChooserNodeType_Global)->exec(pEvent->screenPos());
+ break;
+ }
+ case UIChooserNodeType_Group:
+ {
+ /* Get group-item: */
+ UIChooserItemGroup *pGroupItem = qgraphicsitem_cast<UIChooserItemGroup*>(pItem);
+ /* Make sure we have group-item selected exclusively: */
+ if (selectedItems().contains(pGroupItem) && selectedItems().size() == 1)
+ {
+ /* Group context menu in that case: */
+ if (pGroupItem->groupType() == UIChooserNodeGroupType_Local)
+ m_localMenus.value(UIChooserNodeType_Group)->exec(pEvent->screenPos());
+ else if ( pGroupItem->groupType() == UIChooserNodeGroupType_Provider
+ || pGroupItem->groupType() == UIChooserNodeGroupType_Profile)
+ m_cloudMenus.value(UIChooserNodeType_Group)->exec(pEvent->screenPos());
+ break;
+ }
+ /* Otherwise we have to find a first child machine-item: */
+ else
+ pItem = qobject_cast<UIChooserItem*>(pGroupItem)->firstMachineItem();
+ }
+ RT_FALL_THRU();
+ case UIChooserNodeType_Machine:
+ {
+ /* Get machine-item: */
+ UIChooserItemMachine *pMachineItem = qgraphicsitem_cast<UIChooserItemMachine*>(pItem);
+ /* Machine context menu for other Group/Machine cases: */
+ if (pMachineItem->cacheType() == UIVirtualMachineItemType_Local)
+ m_localMenus.value(UIChooserNodeType_Machine)->exec(pEvent->screenPos());
+ else if (pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
+ m_cloudMenus.value(UIChooserNodeType_Machine)->exec(pEvent->screenPos());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ /* Filter out by default: */
+ return true;
+ }
+ default:
+ break;
+ }
+ /* Pass others context menu events: */
+ return false;
+}
+
+void UIChooserModel::clearRealFocus()
+{
+ /* Set the real focus to null: */
+ scene()->setFocusItem(0);
+}
+
+QList<UIChooserItem*> UIChooserModel::createNavigationItemList(UIChooserItem *pItem)
+{
+ /* Prepare navigation list: */
+ QList<UIChooserItem*> navigationItems;
+
+ /* Iterate over all the global-items: */
+ foreach (UIChooserItem *pGlobalItem, pItem->items(UIChooserNodeType_Global))
+ navigationItems << pGlobalItem;
+ /* Iterate over all the group-items: */
+ foreach (UIChooserItem *pGroupItem, pItem->items(UIChooserNodeType_Group))
+ {
+ navigationItems << pGroupItem;
+ if (pGroupItem->toGroupItem()->isOpened())
+ navigationItems << createNavigationItemList(pGroupItem);
+ }
+ /* Iterate over all the machine-items: */
+ foreach (UIChooserItem *pMachineItem, pItem->items(UIChooserNodeType_Machine))
+ navigationItems << pMachineItem;
+
+ /* Return navigation list: */
+ return navigationItems;
+}
+
+void UIChooserModel::clearTreeForMainRoot()
+{
+ /* Forbid to save selection changes: */
+ m_fSelectionSaveAllowed = false;
+
+ /* Cleanup tree if exists: */
+ delete m_pRoot;
+ m_pRoot = 0;
+}
+
+void UIChooserModel::buildTreeForMainRoot(bool fPreserveSelection /* = false */)
+{
+ /* This isn't safe if dragging is started and needs to be fixed properly,
+ * but for now we will just ignore build request: */
+ /// @todo Make sure D&D is safe on tree rebuild
+ if (m_pCurrentDragObject)
+ return;
+
+ /* Remember scrolling location: */
+ const int iScrollLocation = m_pRoot ? m_pRoot->toGroupItem()->scrollingValue() : 0;
+
+ /* Remember all selected items if requested: */
+ QStringList selectedItemDefinitions;
+ if (fPreserveSelection && !selectedItems().isEmpty())
+ {
+ foreach (UIChooserItem *pSelectedItem, selectedItems())
+ selectedItemDefinitions << pSelectedItem->definition();
+ }
+
+ /* Clean tree for main root: */
+ clearTreeForMainRoot();
+
+ /* Build whole tree for invisible root item: */
+ m_pRoot = new UIChooserItemGroup(scene(), invisibleRoot()->toGroupNode());
+
+ /* Install root as event-filter for scene view,
+ * we need QEvent::Scroll events from it: */
+ root()->installEventFilterHelper(view());
+
+ /* Update tree for main root: */
+ updateTreeForMainRoot();
+
+ /* Apply current global item height hint: */
+ applyGlobalItemHeightHint();
+
+ /* Restore all selected items if requested: */
+ if (fPreserveSelection)
+ {
+ QList<UIChooserItem*> selectedItems;
+ foreach (const QString &strSelectedItemDefinition, selectedItemDefinitions)
+ {
+ UIChooserItem *pSelectedItem = searchItemByDefinition(strSelectedItemDefinition);
+ if (pSelectedItem)
+ selectedItems << pSelectedItem;
+ }
+ setSelectedItems(selectedItems);
+ setCurrentItem(firstSelectedItem());
+ makeSureAtLeastOneItemSelected();
+ }
+
+ /* Restore scrolling location: */
+ m_pRoot->toGroupItem()->setScrollingValue(iScrollLocation);
+
+ /* Repeat search if search widget is visible: */
+ if (view() && view()->isSearchWidgetVisible())
+ view()->redoSearch();
+
+ /* Allow to save selection changes: */
+ m_fSelectionSaveAllowed = true;
+}
+
+void UIChooserModel::updateTreeForMainRoot()
+{
+ updateNavigationItemList();
+ updateLayout();
+}
+
+void UIChooserModel::removeLocalMachineItems(const QList<UIChooserItemMachine*> &machineItems)
+{
+ /* Confirm machine-items removal: */
+ QStringList names;
+ foreach (UIChooserItemMachine *pItem, machineItems)
+ names << pItem->name();
+ if (!msgCenter().confirmMachineItemRemoval(names))
+ return;
+
+ /* Find and select closest unselected item: */
+ setSelectedItem(findClosestUnselectedItem());
+
+ /* Remove nodes of all the passed items: */
+ foreach (UIChooserItemMachine *pItem, machineItems)
+ delete pItem->node();
+
+ /* And update model: */
+ wipeOutEmptyGroups();
+ updateTreeForMainRoot();
+
+ /* Save groups finally: */
+ saveGroups();
+}
+
+void UIChooserModel::unregisterLocalMachines(const QList<CMachine> &machines)
+{
+ /* Confirm machine removal: */
+ const int iResultCode = msgCenter().confirmMachineRemoval(machines);
+ if (iResultCode == AlertButton_Cancel)
+ return;
+
+ /* For every selected machine: */
+ foreach (CMachine comMachine, machines)
+ {
+ if (iResultCode == AlertButton_Choice1)
+ {
+ /* Unregister machine first: */
+ CMediumVector media = comMachine.Unregister(KCleanupMode_DetachAllReturnHardDisksOnly);
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotRemoveMachine(comMachine);
+ continue;
+ }
+ /* Removing machine: */
+ UINotificationProgressMachineMediaRemove *pNotification = new UINotificationProgressMachineMediaRemove(comMachine, media);
+ gpNotificationCenter->append(pNotification);
+ }
+ else if (iResultCode == AlertButton_Choice2 || iResultCode == AlertButton_Ok)
+ {
+ /* Unregister machine first: */
+ CMediumVector media = comMachine.Unregister(KCleanupMode_DetachAllReturnHardDisksOnly);
+ if (!comMachine.isOk())
+ {
+ UINotificationMessage::cannotRemoveMachine(comMachine);
+ continue;
+ }
+ /* Finally close all media, deliberately ignoring errors: */
+ foreach (CMedium comMedium, media)
+ {
+ if (!comMedium.isNull())
+ comMedium.Close();
+ }
+ }
+ }
+}
+
+void UIChooserModel::unregisterCloudMachineItems(const QList<UIChooserItemMachine*> &machineItems)
+{
+ /* Compose a list of machines: */
+ QList<CCloudMachine> machines;
+ foreach (UIChooserItemMachine *pMachineItem, machineItems)
+ machines << pMachineItem->cache()->toCloud()->machine();
+
+ /* Stop cloud profile update prematurely: */
+ m_pTimerCloudProfileUpdate->stop();
+
+ /* Confirm machine removal: */
+ const int iResultCode = msgCenter().confirmCloudMachineRemoval(machines);
+ if (iResultCode == AlertButton_Cancel)
+ {
+ /* Resume cloud profile update if cancelled: */
+ m_pTimerCloudProfileUpdate->start(10000);
+ return;
+ }
+
+ /* For every selected machine-item: */
+ foreach (UIChooserItemMachine *pMachineItem, machineItems)
+ {
+ /* Compose cloud entity keys for profile and machine: */
+ const QString strProviderShortName = pMachineItem->parentItem()->parentItem()->name();
+ const QString strProfileName = pMachineItem->parentItem()->name();
+ const QUuid uMachineId = pMachineItem->id();
+ const UICloudEntityKey cloudEntityKeyForMachine = UICloudEntityKey(strProviderShortName, strProfileName, uMachineId);
+
+ /* Stop refreshing machine being deleted: */
+ if (containsCloudEntityKey(cloudEntityKeyForMachine))
+ pMachineItem->cache()->toCloud()->waitForAsyncInfoUpdateFinished();
+
+ /* Acquire cloud machine: */
+ CCloudMachine comMachine = pMachineItem->cache()->toCloud()->machine();
+
+ /* Removing cloud machine: */
+ UINotificationProgressCloudMachineRemove *pNotification =
+ new UINotificationProgressCloudMachineRemove(comMachine,
+ iResultCode == AlertButton_Choice1,
+ strProviderShortName,
+ strProfileName);
+ connect(pNotification, &UINotificationProgressCloudMachineRemove::sigCloudMachineRemoved,
+ this, &UIChooserModel::sltHandleCloudMachineRemoved);
+ gpNotificationCenter->append(pNotification);
+ }
+}
+
+bool UIChooserModel::processDragMoveEvent(QGraphicsSceneDragDropEvent *pEvent)
+{
+ /* Make sure view exists: */
+ AssertPtrReturn(view(), false);
+
+ /* Do we scrolling already? */
+ if (m_fIsScrollingInProgress)
+ return false;
+
+ /* Check scroll-area: */
+ const QPoint eventPoint = view()->mapFromGlobal(pEvent->screenPos());
+ if ( (eventPoint.y() < m_iScrollingTokenSize)
+ || (eventPoint.y() > view()->height() - m_iScrollingTokenSize))
+ {
+ /* Set scrolling in progress: */
+ m_fIsScrollingInProgress = true;
+ /* Start scrolling: */
+ QTimer::singleShot(200, this, SLOT(sltStartScrolling()));
+ }
+
+ /* Pass event: */
+ return false;
+}
+
+bool UIChooserModel::processDragLeaveEvent(QGraphicsSceneDragDropEvent *pEvent)
+{
+ /* Event object is not required here: */
+ Q_UNUSED(pEvent);
+
+ /* Make sure to stop scrolling as drag-leave event happened: */
+ if (m_fIsScrollingInProgress)
+ m_fIsScrollingInProgress = false;
+
+ /* Pass event: */
+ return false;
+}
+
+void UIChooserModel::applyGlobalItemHeightHint()
+{
+ /* Make sure there is something to apply: */
+ if (m_iGlobalItemHeightHint == 0)
+ return;
+
+ /* Walk thrugh all the items of navigation list: */
+ foreach (UIChooserItem *pItem, navigationItems())
+ {
+ /* And for each global item: */
+ if (pItem->type() == UIChooserNodeType_Global)
+ {
+ /* Apply the height hint we have: */
+ UIChooserItemGlobal *pGlobalItem = pItem->toGlobalItem();
+ if (pGlobalItem)
+ pGlobalItem->setHeightHint(m_iGlobalItemHeightHint);
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.h
new file mode 100644
index 00000000..5acf5ede
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.h
@@ -0,0 +1,503 @@
+/* $Id: UIChooserModel.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserModel class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserModel_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserModel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPointer>
+
+/* GUI includes: */
+#include "UIChooserAbstractModel.h"
+#include "UIExtraDataDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudMachine.h"
+#include "CMachine.h"
+
+/* Forward declaration: */
+class QDrag;
+class UIActionPool;
+class UIChooser;
+class UIChooserHandlerMouse;
+class UIChooserHandlerKeyboard;
+class UIChooserItem;
+class UIChooserItemMachine;
+class UIChooserNode;
+class UIChooserView;
+class UIVirtualMachineItem;
+
+/** UIChooserAbstractModel extension used as VM Chooser-pane model.
+ * This class is used to operate on tree of visible tree items
+ * representing VMs and their groups. */
+class UIChooserModel : public UIChooserAbstractModel
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name Tool stuff.
+ * @{ */
+ /** Notifies listeners about tool popup-menu request for certain @a enmClass and @a position. */
+ void sigToolMenuRequested(UIToolClass enmClass, const QPoint &position);
+ /** @} */
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Notifies listeners about selection changed. */
+ void sigSelectionChanged();
+ /** Notifies listeners about selection invalidated. */
+ void sigSelectionInvalidated();
+
+ /** Notifies listeners about group toggling started. */
+ void sigToggleStarted();
+ /** Notifies listeners about group toggling finished. */
+ void sigToggleFinished();
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Notifies listeners about root item minimum width @a iHint changed. */
+ void sigRootItemMinimumWidthHintChanged(int iHint);
+ /** @} */
+
+ /** @name Action stuff.
+ * @{ */
+ /** Notifies listeners about start or show request. */
+ void sigStartOrShowRequest();
+ /** @} */
+
+public:
+
+ /** Constructs Chooser-model passing @a pParent to the base-class. */
+ UIChooserModel(UIChooser *pParent, UIActionPool *pActionPool);
+ /** Destructs Chooser-model. */
+ virtual ~UIChooserModel() RT_OVERRIDE;
+
+ /** @name General stuff.
+ * @{ */
+ /** Inits model. */
+ virtual void init() RT_OVERRIDE;
+
+ /** Returns the action-pool reference. */
+ UIActionPool *actionPool() const;
+ /** Returns the scene reference. */
+ QGraphicsScene *scene() const;
+ /** Returns the reference of the first view of the scene(). */
+ UIChooserView *view() const;
+ /** Returns the paint device reference. */
+ QPaintDevice *paintDevice() const;
+
+ /** Returns item at @a position, taking into account possible @a deviceTransform. */
+ QGraphicsItem *itemAt(const QPointF &position, const QTransform &deviceTransform = QTransform()) const;
+
+ /** Handles tool button click for certain @a pItem. */
+ void handleToolButtonClick(UIChooserItem *pItem);
+ /** Handles pin button click for certain @a pItem. */
+ void handlePinButtonClick(UIChooserItem *pItem);
+ /** @} */
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Sets a list of selected @a items. */
+ void setSelectedItems(const QList<UIChooserItem*> &items);
+ /** Defines selected @a pItem. */
+ void setSelectedItem(UIChooserItem *pItem);
+ /** Defines selected-item by @a definition. */
+ void setSelectedItem(const QString &strDefinition);
+ /** Clear selected-items list. */
+ void clearSelectedItems();
+
+ /** Returns a list of selected-items. */
+ const QList<UIChooserItem*> &selectedItems() const;
+
+ /** Adds @a pItem to list of selected. */
+ void addToSelectedItems(UIChooserItem *pItem);
+ /** Removes @a pItem from list of selected. */
+ void removeFromSelectedItems(UIChooserItem *pItem);
+
+ /** Returns first selected-item. */
+ UIChooserItem *firstSelectedItem() const;
+ /** Returns first selected machine item. */
+ UIVirtualMachineItem *firstSelectedMachineItem() const;
+ /** Returns a list of selected machine items. */
+ QList<UIVirtualMachineItem*> selectedMachineItems() const;
+
+ /** Returns whether group item is selected. */
+ bool isGroupItemSelected() const;
+ /** Returns whether global item is selected. */
+ bool isGlobalItemSelected() const;
+ /** Returns whether machine item is selected. */
+ bool isMachineItemSelected() const;
+ /** Returns whether local machine item is selected. */
+ bool isLocalMachineItemSelected() const;
+ /** Returns whether cloud machine item is selected. */
+ bool isCloudMachineItemSelected() const;
+
+ /** Returns whether single group is selected. */
+ bool isSingleGroupSelected() const;
+ /** Returns whether single local group is selected. */
+ bool isSingleLocalGroupSelected() const;
+ /** Returns whether single cloud provider group is selected. */
+ bool isSingleCloudProviderGroupSelected() const;
+ /** Returns whether single cloud profile group is selected. */
+ bool isSingleCloudProfileGroupSelected() const;
+ /** Returns whether all machine items of one group is selected. */
+ bool isAllItemsOfOneGroupSelected() const;
+
+ /** Returns full name of currently selected group. */
+ QString fullGroupName() const;
+
+ /** Finds closest non-selected-item. */
+ UIChooserItem *findClosestUnselectedItem() const;
+ /** Makes sure selection doesn't contain item with certain @a uId. */
+ void makeSureNoItemWithCertainIdSelected(const QUuid &uId);
+ /** Makes sure at least one item selected. */
+ void makeSureAtLeastOneItemSelected();
+
+ /** Defines current @a pItem. */
+ void setCurrentItem(UIChooserItem *pItem);
+ /** Returns current-item. */
+ UIChooserItem *currentItem() const;
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Returns a list of navigation-items. */
+ const QList<UIChooserItem*> &navigationItems() const;
+ /** Removes @a pItem from navigation list. */
+ void removeFromNavigationItems(UIChooserItem *pItem);
+ /** Updates navigation list. */
+ void updateNavigationItemList();
+ /** @} */
+
+ /** @name Search stuff.
+ * @{ */
+ /** Performs a search for an item matching @a strDefinition. */
+ UIChooserItem *searchItemByDefinition(const QString &strDefinition) const;
+
+ /** Performs a search using @a strSearchTerm and @a iSearchFlags specified. */
+ virtual void performSearch(const QString &strSearchTerm, int iSearchFlags) RT_OVERRIDE;
+ /** Resets the search result data members and disables item's visual effects.
+ * Also returns a list of all nodes which may be utilized by the calling code. */
+ virtual QList<UIChooserNode*> resetSearch() RT_OVERRIDE;
+
+ /** Selects next/prev (wrt. @a fIsNext) search result. */
+ void selectSearchResult(bool fIsNext);
+ /** Shows/hides machine search widget. */
+ void setSearchWidgetVisible(bool fVisible);
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Returns the root instance. */
+ UIChooserItem *root() const;
+
+ /** Starts editing selected group item name. */
+ void startEditingSelectedGroupItemName();
+ /** Disbands selected group item. */
+ void disbandSelectedGroupItem();
+ /** Removes selected machine items. */
+ void removeSelectedMachineItems();
+ /** Moves selected machine items to group item.
+ * @param strName Holds the group item name to move items to, if
+ * that name isn't specified, new top-level group
+ * item will be created. */
+ void moveSelectedMachineItemsToGroupItem(const QString &strName);
+ /** Starts or shows selected items. */
+ void startOrShowSelectedItems();
+ /** Refreshes selected machine items. */
+ void refreshSelectedMachineItems();
+ /** Sorts selected [parent] group item. */
+ void sortSelectedGroupItem();
+ /** Changes current machine item to the one with certain @a uId. */
+ void setCurrentMachineItem(const QUuid &uId);
+ /** Sets global tools item to be the current one. */
+ void setCurrentGlobalItem();
+
+ /** Defines current @a pDragObject. */
+ void setCurrentDragObject(QDrag *pDragObject);
+
+ /** Looks for item with certain @a strLookupText. */
+ void lookFor(const QString &strLookupText);
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates layout. */
+ void updateLayout();
+
+ /** Defines global item height @a iHint. */
+ void setGlobalItemHeightHint(int iHint);
+ /** @} */
+
+public slots:
+
+ /** @name General stuff.
+ * @{ */
+ /** Handles Chooser-view resize. */
+ void sltHandleViewResized();
+ /** @} */
+
+protected:
+
+ /** @name Event handling stuff.
+ * @{ */
+ /** Preprocesses Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+protected slots:
+
+ /** @name Main event handling stuff.
+ * @{ */
+ /** Handles local machine registering/unregistering for machine with certain @a uMachineId. */
+ virtual void sltLocalMachineRegistrationChanged(const QUuid &uMachineId, const bool fRegistered) RT_OVERRIDE;
+
+ /** Handles event about cloud provider with @a uProviderId being uninstalled. */
+ virtual void sltHandleCloudProviderUninstall(const QUuid &uProviderId) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Handles reload machine with certain @a uMachineId request. */
+ virtual void sltReloadMachine(const QUuid &uMachineId) RT_OVERRIDE;
+
+ /** Handles command to detach COM. */
+ virtual void sltDetachCOM() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Cloud stuff.
+ * @{ */
+ /** Handles cloud machine unregistering for @a uId.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name. */
+ virtual void sltCloudMachineUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QUuid &uId) RT_OVERRIDE;
+ /** Handles cloud machine unregistering for a list of @a ids.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name. */
+ virtual void sltCloudMachinesUnregistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QList<QUuid> &ids) RT_OVERRIDE;
+ /** Handles cloud machine registering for @a comMachine.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name. */
+ virtual void sltCloudMachineRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine) RT_OVERRIDE;
+ /** Handles cloud machine registering for a list of @a machines.
+ * @param strProviderShortName Brings provider short name.
+ * @param strProfileName Brings profile name. */
+ virtual void sltCloudMachinesRegistered(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QVector<CCloudMachine> &machines) RT_OVERRIDE;
+
+ /** Handles read cloud machine list task complete signal. */
+ virtual void sltHandleReadCloudMachineListTaskComplete() RT_OVERRIDE;
+
+ /** Handles Cloud Profile Manager cumulative changes. */
+ virtual void sltHandleCloudProfileManagerCumulativeChange() RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Makes sure current item is visible. */
+ void sltMakeSureCurrentItemVisible();
+
+ /** Handles current-item destruction. */
+ void sltCurrentItemDestroyed();
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Handles D&D scrolling. */
+ void sltStartScrolling();
+ /** Handles D&D object destruction. */
+ void sltCurrentDragObjectDestroyed();
+ /** @} */
+
+ /** @name Cloud stuff.
+ * @{ */
+ /** Handles cloud machine removal.
+ * @param strProviderShortName Brings the provider short name.
+ * @param strProfileName Brings the profile name.
+ * @param strName Brings the machine name. */
+ void sltHandleCloudMachineRemoved(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QString &strName);
+
+ /** Updates selected cloud profiles. */
+ void sltUpdateSelectedCloudProfiles();
+ /** @} */
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares scene. */
+ void prepareScene();
+ /** Prepares context-menu. */
+ void prepareContextMenu();
+ /** Prepares handlers. */
+ void prepareHandlers();
+ /** Prepares cloud update timer. */
+ void prepareCloudUpdateTimer();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Loads settings. */
+ void loadSettings();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups cloud update timer.*/
+ void cleanupCloudUpdateTimer();
+ /** Cleanups handlers. */
+ void cleanupHandlers();
+ /** Cleanups context-menu. */
+ void cleanupContextMenu();
+ /** Cleanups scene. */
+ void cleanupScene();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Handles context-menu @a pEvent. */
+ bool processContextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent);
+ /** @} */
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Clears real focus. */
+ void clearRealFocus();
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Creates navigation list for passed root @a pItem. */
+ QList<UIChooserItem*> createNavigationItemList(UIChooserItem *pItem);
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Clears tree for main root. */
+ void clearTreeForMainRoot();
+ /** [Re]builds tree for main root, preserves selection if requested. */
+ void buildTreeForMainRoot(bool fPreserveSelection = false);
+ /** Update tree for main root. */
+ void updateTreeForMainRoot();
+
+ /** Removes a list of local virtual @a machineItems. */
+ void removeLocalMachineItems(const QList<UIChooserItemMachine*> &machineItems);
+ /** Unregisters a list of local virtual @a machines. */
+ void unregisterLocalMachines(const QList<CMachine> &machines);
+ /** Unregisters a list of cloud virtual @a machineItems. */
+ void unregisterCloudMachineItems(const QList<UIChooserItemMachine*> &machineItems);
+
+ /** Processes drag move @a pEvent. */
+ bool processDragMoveEvent(QGraphicsSceneDragDropEvent *pEvent);
+ /** Processes drag leave @a pEvent. */
+ bool processDragLeaveEvent(QGraphicsSceneDragDropEvent *pEvent);
+
+ /** Applies the global item height hint. */
+ void applyGlobalItemHeightHint();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+
+ /** Holds the scene reference. */
+ QGraphicsScene *m_pScene;
+
+ /** Holds the mouse handler instance. */
+ UIChooserHandlerMouse *m_pMouseHandler;
+ /** Holds the keyboard handler instance. */
+ UIChooserHandlerKeyboard *m_pKeyboardHandler;
+
+ /** Holds the map of local context-menu instances. */
+ QMap<UIChooserNodeType, QMenu*> m_localMenus;
+ /** Holds the map of cloud context-menu instances. */
+ QMap<UIChooserNodeType, QMenu*> m_cloudMenus;
+ /** @} */
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Holds the current-item reference. */
+ QPointer<UIChooserItem> m_pCurrentItem;
+
+ /** Holds whether selection save allowed. */
+ bool m_fSelectionSaveAllowed;
+ /** @} */
+
+ /** @name Search stuff.
+ * @{ */
+ /** Stores the index (within the m_searchResults) of the currently selected found item. */
+ int m_iCurrentSearchResultIndex;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Holds the root instance. */
+ QPointer<UIChooserItem> m_pRoot;
+
+ /** Holds the navigation-items. */
+ QList<UIChooserItem*> m_navigationItems;
+ /** Holds the selected-items. */
+ QList<UIChooserItem*> m_selectedItems;
+
+ /** Holds the current drag object instance. */
+ QPointer<QDrag> m_pCurrentDragObject;
+ /** Holds the drag scrolling token size. */
+ int m_iScrollingTokenSize;
+ /** Holds whether drag scrolling is in progress. */
+ bool m_fIsScrollingInProgress;
+
+ /** Holds the global item height hint. */
+ int m_iGlobalItemHeightHint;
+ /** @} */
+
+ /** @name Cloud stuff.
+ * @{ */
+ /** Holds cloud profile update timer instance. */
+ QTimer *m_pTimerCloudProfileUpdate;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserModel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNode.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNode.cpp
new file mode 100644
index 00000000..83133fb5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNode.cpp
@@ -0,0 +1,95 @@
+/* $Id: UIChooserNode.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserNode class definition.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIChooserNode.h"
+#include "UIChooserNodeGroup.h"
+#include "UIChooserNodeGlobal.h"
+#include "UIChooserNodeMachine.h"
+
+/* Other VBox includes: */
+#include "iprt/cpp/utils.h"
+
+
+UIChooserNode::UIChooserNode(UIChooserNode *pParent /* = 0 */, bool fFavorite /* = false */)
+ : QIWithRetranslateUI3<QObject>(pParent)
+ , m_pParent(pParent)
+ , m_fFavorite(fFavorite)
+ , m_pModel(0)
+ , m_fDisabled(false)
+{
+}
+
+UIChooserNode::~UIChooserNode()
+{
+ if (!m_pItem.isNull())
+ delete m_pItem.data();
+}
+
+UIChooserNodeGroup *UIChooserNode::toGroupNode()
+{
+ return static_cast<UIChooserNodeGroup*>(this);
+}
+
+UIChooserNodeGlobal *UIChooserNode::toGlobalNode()
+{
+ return static_cast<UIChooserNodeGlobal*>(this);
+}
+
+UIChooserNodeMachine *UIChooserNode::toMachineNode()
+{
+ return static_cast<UIChooserNodeMachine*>(this);
+}
+
+UIChooserNode *UIChooserNode::rootNode() const
+{
+ return isRoot() ? unconst(this) : parentNode()->rootNode();
+}
+
+UIChooserAbstractModel *UIChooserNode::model() const
+{
+ return m_pModel ? m_pModel : rootNode()->model();
+}
+
+int UIChooserNode::position()
+{
+ return parentNode() ? parentNode()->positionOf(this) : 0;
+}
+
+bool UIChooserNode::isDisabled() const
+{
+ return m_fDisabled;
+}
+
+void UIChooserNode::setDisabled(bool fDisabled)
+{
+ if (fDisabled == m_fDisabled)
+ return;
+ m_fDisabled = fDisabled;
+ if (m_pItem)
+ m_pItem->setDisabledEffect(m_fDisabled);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNode.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNode.h
new file mode 100644
index 00000000..04229629
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNode.h
@@ -0,0 +1,165 @@
+/* $Id: UIChooserNode.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserNode class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserNode_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserNode_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QPointer>
+#include <QString>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIChooserDefs.h"
+#include "UIChooserItem.h"
+
+/* Forward declaration: */
+class UIChooserAbstractModel;
+class UIChooserNodeGroup;
+class UIChooserNodeGlobal;
+class UIChooserNodeMachine;
+
+
+/** QObject subclass used as interface for invisible tree-view nodes.
+ * These nodes can be of three types (group, global and machine node).
+ * They can be used to compose a tree of nodes loaded from VBox setting. */
+class UIChooserNode : public QIWithRetranslateUI3<QObject>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs chooser node passing @a pParent to the base-class.
+ * @param fFavorite Brings whether the node is favorite. */
+ UIChooserNode(UIChooserNode *pParent = 0, bool fFavorite = false);
+ /** Destructs chooser node. */
+ virtual ~UIChooserNode() RT_OVERRIDE;
+
+ /** Returns RTTI node type. */
+ virtual UIChooserNodeType type() const = 0;
+
+ /** Casts node to group one. */
+ UIChooserNodeGroup *toGroupNode();
+ /** Casts node to global one. */
+ UIChooserNodeGlobal *toGlobalNode();
+ /** Casts node to machine one. */
+ UIChooserNodeMachine *toMachineNode();
+
+ /** Returns parent node reference. */
+ UIChooserNode *parentNode() const { return m_pParent; }
+ /** Returns whether node is of root kind. */
+ bool isRoot() const { return !m_pParent; }
+ /** Returns root node reference. */
+ UIChooserNode *rootNode() const;
+
+ /** Returns whether the node is favorite. */
+ bool isFavorite() const { return m_fFavorite; }
+ /** Defines whether the node is @a fFavorite. */
+ void setFavorite(bool fFavorite) { m_fFavorite = fFavorite; }
+
+ /** Defines the @a pModel reference. */
+ void setModel(UIChooserAbstractModel *pModel) { m_pModel = pModel; }
+ /** Returns the model reference. */
+ UIChooserAbstractModel *model() const;
+
+ /** Returns node name. */
+ virtual QString name() const = 0;
+ /** Returns full node name. */
+ virtual QString fullName() const = 0;
+ /** Returns item description. */
+ virtual QString description() const = 0;
+ /** Returns item definition.
+ * @param fFull Brings whether full definition is required
+ * which is used while saving group definitions,
+ * otherwise short definition will be returned,
+ * which is used while saving last chosen node. */
+ virtual QString definition(bool fFull = false) const = 0;
+
+ /** Returns whether there are children of certain @a enmType. */
+ virtual bool hasNodes(UIChooserNodeType enmType = UIChooserNodeType_Any) const = 0;
+ /** Returns a list of nodes of certain @a enmType. */
+ virtual QList<UIChooserNode*> nodes(UIChooserNodeType enmType = UIChooserNodeType_Any) const = 0;
+
+ /** Adds passed @a pNode to specified @a iPosition. */
+ virtual void addNode(UIChooserNode *pNode, int iPosition) = 0;
+ /** Removes passed @a pNode. */
+ virtual void removeNode(UIChooserNode *pNode) = 0;
+
+ /** Removes all children with specified @a uId recursively. */
+ virtual void removeAllNodes(const QUuid &uId) = 0;
+ /** Updates all children with specified @a uId recursively. */
+ virtual void updateAllNodes(const QUuid &uId) = 0;
+
+ /** Returns node position. */
+ int position();
+ /** Returns position of specified node inside this one. */
+ virtual int positionOf(UIChooserNode *pNode) = 0;
+
+ /** Defines linked @a pItem. */
+ void setItem(UIChooserItem *pItem) { m_pItem = pItem; }
+ /** Returns linked item. */
+ UIChooserItem *item() const { return m_pItem.data(); }
+
+ /** Performs search wrt. @a strSearchTerm and @a iSearchFlags and updates @a matchedItems. For an empty
+ * @a strSearchTerm all items are added wrt. node type from @a iSearchFlags. */
+ virtual void searchForNodes(const QString &strSearchTerm, int iSearchFlags, QList<UIChooserNode*> &matchedItems) = 0;
+
+ /** Performs sorting of children nodes. */
+ virtual void sortNodes() = 0;
+
+ /** Returns if node is disabled. */
+ bool isDisabled() const;
+ /** Sets the disabled flag. */
+ void setDisabled(bool fDisabled);
+
+protected:
+
+ /** Holds the parent node reference. */
+ UIChooserNode *m_pParent;
+ /** Holds whether the node is favorite. */
+ bool m_fFavorite;
+
+ /** Holds the model reference. */
+ UIChooserAbstractModel *m_pModel;
+
+ /** Holds the linked item reference. */
+ QPointer<UIChooserItem> m_pItem;
+
+ /** Holds item description. */
+ QString m_strDescription;
+
+ /** Holds the flag to indicate whether the node is disabled or not. */
+ bool m_fDisabled;
+
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserNode_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGlobal.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGlobal.cpp
new file mode 100644
index 00000000..52694b78
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGlobal.cpp
@@ -0,0 +1,182 @@
+/* $Id: UIChooserNodeGlobal.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserNodeGlobal class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIChooserAbstractModel.h"
+#include "UIChooserNodeGlobal.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+UIChooserNodeGlobal::UIChooserNodeGlobal(UIChooserNode *pParent,
+ int iPosition,
+ bool fFavorite,
+ const QString &)
+ : UIChooserNode(pParent, fFavorite)
+{
+ /* Add to parent: */
+ if (parentNode())
+ parentNode()->addNode(this, iPosition);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIChooserNodeGlobal::UIChooserNodeGlobal(UIChooserNode *pParent,
+ int iPosition,
+ UIChooserNodeGlobal *pCopyFrom)
+ : UIChooserNode(pParent, pCopyFrom->isFavorite())
+{
+ /* Add to parent: */
+ if (parentNode())
+ parentNode()->addNode(this, iPosition);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIChooserNodeGlobal::~UIChooserNodeGlobal()
+{
+ /* Delete item: */
+ delete item();
+
+ /* Remove from parent: */
+ if (parentNode())
+ parentNode()->removeNode(this);
+}
+
+QString UIChooserNodeGlobal::name() const
+{
+ return m_strName;
+}
+
+QString UIChooserNodeGlobal::fullName() const
+{
+ return name();
+}
+
+QString UIChooserNodeGlobal::description() const
+{
+ return m_strDescription;
+}
+
+QString UIChooserNodeGlobal::definition(bool fFull /* = false */) const
+{
+ const QString strNodePrefix = UIChooserAbstractModel::prefixToString(UIChooserNodeDataPrefixType_Global);
+ const QString strNodeOptionFavorite = UIChooserAbstractModel::optionToString(UIChooserNodeDataOptionType_GlobalFavorite);
+ const QString strNodeValueDefault = UIChooserAbstractModel::valueToString(UIChooserNodeDataValueType_GlobalDefault);
+ return fFull
+ ? QString("%1%2=%3").arg(strNodePrefix).arg(isFavorite() ? strNodeOptionFavorite : "").arg(strNodeValueDefault)
+ : QString("%1=%2").arg(strNodePrefix).arg(strNodeValueDefault);
+}
+
+bool UIChooserNodeGlobal::hasNodes(UIChooserNodeType enmType /* = UIChooserNodeType_Any */) const
+{
+ Q_UNUSED(enmType);
+ AssertFailedReturn(false);
+}
+
+QList<UIChooserNode*> UIChooserNodeGlobal::nodes(UIChooserNodeType enmType /* = UIChooserNodeType_Any */) const
+{
+ Q_UNUSED(enmType);
+ AssertFailedReturn(QList<UIChooserNode*>());
+}
+
+void UIChooserNodeGlobal::addNode(UIChooserNode *pNode, int iPosition)
+{
+ Q_UNUSED(pNode);
+ Q_UNUSED(iPosition);
+ AssertFailedReturnVoid();
+}
+
+void UIChooserNodeGlobal::removeNode(UIChooserNode *pNode)
+{
+ Q_UNUSED(pNode);
+ AssertFailedReturnVoid();
+}
+
+void UIChooserNodeGlobal::removeAllNodes(const QUuid &)
+{
+ // Nothing to remove for global-node..
+}
+
+void UIChooserNodeGlobal::updateAllNodes(const QUuid &)
+{
+ // Nothing to update for global-node..
+
+ /* Update global-item: */
+ item()->updateItem();
+}
+
+int UIChooserNodeGlobal::positionOf(UIChooserNode *pNode)
+{
+ Q_UNUSED(pNode);
+ AssertFailedReturn(0);
+}
+
+void UIChooserNodeGlobal::searchForNodes(const QString &strSearchTerm, int iSearchFlags, QList<UIChooserNode*> &matchedItems)
+{
+ /* Ignore if we are not searching for the global-node: */
+ if (!(iSearchFlags & UIChooserItemSearchFlag_Global))
+ return;
+
+ /* If the search term is empty we just add the node to the matched list: */
+ if (strSearchTerm.isEmpty())
+ matchedItems << this;
+ else
+ {
+ /* If exact name flag specified => check full node name: */
+ if (iSearchFlags & UIChooserItemSearchFlag_ExactName)
+ {
+ if (name() == strSearchTerm)
+ matchedItems << this;
+ }
+ /* Otherwise check if name contains search term: */
+ else
+ {
+ if (name().contains(strSearchTerm, Qt::CaseInsensitive))
+ matchedItems << this;
+ }
+ }
+}
+
+void UIChooserNodeGlobal::sortNodes()
+{
+ AssertFailedReturnVoid();
+}
+
+void UIChooserNodeGlobal::retranslateUi()
+{
+ /* Translate name & description: */
+ m_strName = tr("Tools");
+ m_strDescription = tr("Item");
+
+ /* Update global-item: */
+ if (item())
+ item()->updateItem();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGlobal.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGlobal.h
new file mode 100644
index 00000000..a3fc2df5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGlobal.h
@@ -0,0 +1,114 @@
+/* $Id: UIChooserNodeGlobal.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserNodeGlobal class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserNodeGlobal_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserNodeGlobal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIChooserNode.h"
+
+
+/** UIChooserNode subclass used as interface for invisible tree-view global nodes. */
+class UIChooserNodeGlobal : public UIChooserNode
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs chooser node passing @a pParent to the base-class.
+ * @param iPosition Brings the initial node position.
+ * @param fFavorite Brings whether the node is favorite.
+ * @param strTip Brings the dummy tip. */
+ UIChooserNodeGlobal(UIChooserNode *pParent,
+ int iPosition,
+ bool fFavorite,
+ const QString &strTip);
+ /** Constructs chooser node passing @a pParent to the base-class.
+ * @param iPosition Brings the initial node position.
+ * @param pCopyFrom Brings the node to copy data from. */
+ UIChooserNodeGlobal(UIChooserNode *pParent,
+ int iPosition,
+ UIChooserNodeGlobal *pCopyFrom);
+ /** Destructs chooser node. */
+ virtual ~UIChooserNodeGlobal() RT_OVERRIDE;
+
+ /** Returns RTTI node type. */
+ virtual UIChooserNodeType type() const RT_OVERRIDE { return UIChooserNodeType_Global; }
+
+ /** Returns node name. */
+ virtual QString name() const RT_OVERRIDE;
+ /** Returns full node name. */
+ virtual QString fullName() const RT_OVERRIDE;
+ /** Returns item description. */
+ virtual QString description() const RT_OVERRIDE;
+ /** Returns item definition.
+ * @param fFull Brings whether full definition is required
+ * which is used while saving group definitions,
+ * otherwise short definition will be returned,
+ * which is used while saving last chosen node. */
+ virtual QString definition(bool fFull = false) const RT_OVERRIDE;
+
+ /** Returns whether there are children of certain @a enmType. */
+ virtual bool hasNodes(UIChooserNodeType enmType = UIChooserNodeType_Any) const RT_OVERRIDE;
+ /** Returns a list of nodes of certain @a enmType. */
+ virtual QList<UIChooserNode*> nodes(UIChooserNodeType enmType = UIChooserNodeType_Any) const RT_OVERRIDE;
+
+ /** Adds passed @a pNode to specified @a iPosition. */
+ virtual void addNode(UIChooserNode *pNode, int iPosition) RT_OVERRIDE;
+ /** Removes passed @a pNode. */
+ virtual void removeNode(UIChooserNode *pNode) RT_OVERRIDE;
+
+ /** Removes all children with specified @a uId recursively. */
+ virtual void removeAllNodes(const QUuid &uId) RT_OVERRIDE;
+ /** Updates all children with specified @a uId recursively. */
+ virtual void updateAllNodes(const QUuid &uId) RT_OVERRIDE;
+
+ /** Returns position of specified node inside this one. */
+ virtual int positionOf(UIChooserNode *pNode) RT_OVERRIDE;
+
+ /** Updates the @a matchedItems wrt. @strSearchTerm and @a iSearchFlags. */
+ virtual void searchForNodes(const QString &strSearchTerm, int iSearchFlags, QList<UIChooserNode*> &matchedItems) RT_OVERRIDE;
+
+ /** Performs sorting of children nodes. */
+ virtual void sortNodes() RT_OVERRIDE;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Holds the node name. */
+ QString m_strName;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserNodeGlobal_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGroup.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGroup.cpp
new file mode 100644
index 00000000..57633969
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGroup.cpp
@@ -0,0 +1,328 @@
+/* $Id: UIChooserNodeGroup.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserNodeGroup class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIChooserAbstractModel.h"
+#include "UIChooserNodeGroup.h"
+#include "UIChooserNodeGlobal.h"
+#include "UIChooserNodeMachine.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+UIChooserNodeGroup::UIChooserNodeGroup(UIChooserNode *pParent,
+ int iPosition,
+ const QUuid &uId,
+ const QString &strName,
+ UIChooserNodeGroupType enmGroupType,
+ bool fOpened)
+ : UIChooserNode(pParent, false /* favorite */)
+ , m_uId(uId)
+ , m_strName(strName)
+ , m_enmGroupType(enmGroupType)
+ , m_fOpened(fOpened)
+{
+ /* Add to parent: */
+ if (parentNode())
+ parentNode()->addNode(this, iPosition);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIChooserNodeGroup::UIChooserNodeGroup(UIChooserNode *pParent,
+ int iPosition,
+ UIChooserNodeGroup *pCopyFrom)
+ : UIChooserNode(pParent, false /* favorite */)
+ , m_uId(pCopyFrom->id())
+ , m_strName(pCopyFrom->name())
+ , m_enmGroupType(pCopyFrom->groupType())
+ , m_fOpened(pCopyFrom->isOpened())
+{
+ /* Add to parent: */
+ if (parentNode())
+ parentNode()->addNode(this, iPosition);
+
+ /* Copy internal stuff: */
+ copyContents(pCopyFrom);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIChooserNodeGroup::~UIChooserNodeGroup()
+{
+ /* Cleanup groups first, that
+ * gives us proper recursion: */
+ while (!m_nodesGroup.isEmpty())
+ delete m_nodesGroup.last();
+ while (!m_nodesGlobal.isEmpty())
+ delete m_nodesGlobal.last();
+ while (!m_nodesMachine.isEmpty())
+ delete m_nodesMachine.last();
+
+ /* Delete item: */
+ delete item();
+
+ /* Remove from parent: */
+ if (parentNode())
+ parentNode()->removeNode(this);
+}
+
+QString UIChooserNodeGroup::name() const
+{
+ return m_strName;
+}
+
+QString UIChooserNodeGroup::fullName() const
+{
+ /* Return "/" for root item: */
+ if (isRoot())
+ return "/";
+ /* Get full parent name, append with '/' if not yet appended: */
+ QString strFullParentName = parentNode()->fullName();
+ if (!strFullParentName.endsWith('/'))
+ strFullParentName.append('/');
+ /* Return full item name based on parent prefix: */
+ return strFullParentName + name();
+}
+
+QString UIChooserNodeGroup::description() const
+{
+ return name();
+}
+
+QString UIChooserNodeGroup::definition(bool fFull /* = false */) const
+{
+ QString strNodePrefix;
+ switch (groupType())
+ {
+ case UIChooserNodeGroupType_Local:
+ strNodePrefix = UIChooserAbstractModel::prefixToString(UIChooserNodeDataPrefixType_Local);
+ break;
+ case UIChooserNodeGroupType_Provider:
+ strNodePrefix = UIChooserAbstractModel::prefixToString(UIChooserNodeDataPrefixType_Provider);
+ break;
+ case UIChooserNodeGroupType_Profile:
+ strNodePrefix = UIChooserAbstractModel::prefixToString(UIChooserNodeDataPrefixType_Profile);
+ break;
+ default:
+ AssertFailedReturn(QString());
+ }
+ const QString strNodeOptionOpened = UIChooserAbstractModel::optionToString(UIChooserNodeDataOptionType_GroupOpened);
+ return fFull
+ ? QString("%1%2=%3").arg(strNodePrefix).arg(isOpened() ? strNodeOptionOpened : "").arg(name())
+ : QString("%1=%2").arg(strNodePrefix).arg(fullName());
+}
+
+bool UIChooserNodeGroup::hasNodes(UIChooserNodeType enmType /* = UIChooserNodeType_Any */) const
+{
+ switch (enmType)
+ {
+ case UIChooserNodeType_Any:
+ return hasNodes(UIChooserNodeType_Group) || hasNodes(UIChooserNodeType_Global) || hasNodes(UIChooserNodeType_Machine);
+ case UIChooserNodeType_Group:
+ return !m_nodesGroup.isEmpty();
+ case UIChooserNodeType_Global:
+ return !m_nodesGlobal.isEmpty();
+ case UIChooserNodeType_Machine:
+ return !m_nodesMachine.isEmpty();
+ }
+ return false;
+}
+
+QList<UIChooserNode*> UIChooserNodeGroup::nodes(UIChooserNodeType enmType /* = UIChooserNodeType_Any */) const
+{
+ switch (enmType)
+ {
+ case UIChooserNodeType_Any: return m_nodesGlobal + m_nodesGroup + m_nodesMachine;
+ case UIChooserNodeType_Group: return m_nodesGroup;
+ case UIChooserNodeType_Global: return m_nodesGlobal;
+ case UIChooserNodeType_Machine: return m_nodesMachine;
+ }
+ AssertFailedReturn(QList<UIChooserNode*>());
+}
+
+void UIChooserNodeGroup::addNode(UIChooserNode *pNode, int iPosition)
+{
+ switch (pNode->type())
+ {
+ case UIChooserNodeType_Group: m_nodesGroup.insert(iPosition == -1 ? m_nodesGroup.size() : iPosition, pNode); return;
+ case UIChooserNodeType_Global: m_nodesGlobal.insert(iPosition == -1 ? m_nodesGlobal.size() : iPosition, pNode); return;
+ case UIChooserNodeType_Machine: m_nodesMachine.insert(iPosition == -1 ? m_nodesMachine.size() : iPosition, pNode); return;
+ default: break;
+ }
+ AssertFailedReturnVoid();
+}
+
+void UIChooserNodeGroup::removeNode(UIChooserNode *pNode)
+{
+ switch (pNode->type())
+ {
+ case UIChooserNodeType_Group: m_nodesGroup.removeAll(pNode); return;
+ case UIChooserNodeType_Global: m_nodesGlobal.removeAll(pNode); return;
+ case UIChooserNodeType_Machine: m_nodesMachine.removeAll(pNode); return;
+ default: break;
+ }
+ AssertFailedReturnVoid();
+}
+
+void UIChooserNodeGroup::removeAllNodes(const QUuid &uId)
+{
+ foreach (UIChooserNode *pNode, nodes())
+ pNode->removeAllNodes(uId);
+}
+
+void UIChooserNodeGroup::updateAllNodes(const QUuid &uId)
+{
+ // Nothing to update for group-node..
+
+ /* Update group-item: */
+ item()->updateItem();
+
+ /* Update all the children recursively: */
+ foreach (UIChooserNode *pNode, nodes())
+ pNode->updateAllNodes(uId);
+}
+
+int UIChooserNodeGroup::positionOf(UIChooserNode *pNode)
+{
+ switch (pNode->type())
+ {
+ case UIChooserNodeType_Group: return m_nodesGroup.indexOf(pNode->toGroupNode());
+ case UIChooserNodeType_Global: return m_nodesGlobal.indexOf(pNode->toGlobalNode());
+ case UIChooserNodeType_Machine: return m_nodesMachine.indexOf(pNode->toMachineNode());
+ default: break;
+ }
+ AssertFailedReturn(0);
+}
+
+void UIChooserNodeGroup::setName(const QString &strName)
+{
+ /* Make sure something changed: */
+ if (m_strName == strName)
+ return;
+
+ /* Save name: */
+ m_strName = strName;
+
+ /* Update group-item: */
+ if (item())
+ item()->updateItem();
+}
+
+void UIChooserNodeGroup::searchForNodes(const QString &strSearchTerm, int iSearchFlags, QList<UIChooserNode*> &matchedItems)
+{
+ /* If we are searching for the group-node: */
+ if ( ( iSearchFlags & UIChooserItemSearchFlag_LocalGroup
+ && groupType() == UIChooserNodeGroupType_Local)
+ || ( iSearchFlags & UIChooserItemSearchFlag_CloudProvider
+ && groupType() == UIChooserNodeGroupType_Provider)
+ || ( iSearchFlags & UIChooserItemSearchFlag_CloudProfile
+ && groupType() == UIChooserNodeGroupType_Profile))
+ {
+ /* If the search term is empty we just add the node to the matched list: */
+ if (strSearchTerm.isEmpty())
+ matchedItems << this;
+ else
+ {
+ /* If exact ID flag specified => check node ID: */
+ if (iSearchFlags & UIChooserItemSearchFlag_ExactId)
+ {
+ if (id().toString() == strSearchTerm)
+ matchedItems << this;
+ }
+ /* If exact name flag specified => check node name: */
+ else if (iSearchFlags & UIChooserItemSearchFlag_ExactName)
+ {
+ if (name() == strSearchTerm)
+ matchedItems << this;
+ }
+ /* If full name flag specified => check full node name: */
+ else if (iSearchFlags & UIChooserItemSearchFlag_FullName)
+ {
+ if (fullName() == strSearchTerm)
+ matchedItems << this;
+ }
+ /* Otherwise check if name contains search term: */
+ else
+ {
+ if (name().contains(strSearchTerm, Qt::CaseInsensitive))
+ matchedItems << this;
+ }
+ }
+ }
+
+ /* Search among all the children: */
+ foreach (UIChooserNode *pNode, m_nodesGroup)
+ pNode->searchForNodes(strSearchTerm, iSearchFlags, matchedItems);
+ foreach (UIChooserNode *pNode, m_nodesGlobal)
+ pNode->searchForNodes(strSearchTerm, iSearchFlags, matchedItems);
+ foreach (UIChooserNode *pNode, m_nodesMachine)
+ pNode->searchForNodes(strSearchTerm, iSearchFlags, matchedItems);
+}
+
+void UIChooserNodeGroup::sortNodes()
+{
+ QMap<QString, UIChooserNode*> mapGroup;
+ foreach (UIChooserNode *pNode, m_nodesGroup)
+ mapGroup[pNode->name()] = pNode;
+ m_nodesGroup = mapGroup.values();
+
+ QMap<QString, UIChooserNode*> mapGlobal;
+ foreach (UIChooserNode *pNode, m_nodesGlobal)
+ mapGlobal[pNode->name()] = pNode;
+ m_nodesGlobal = mapGlobal.values();
+
+ QMap<QString, UIChooserNode*> mapMachine;
+ foreach (UIChooserNode *pNode, m_nodesMachine)
+ mapMachine[pNode->name()] = pNode;
+ m_nodesMachine = mapMachine.values();
+}
+
+QUuid UIChooserNodeGroup::id() const
+{
+ return m_uId;
+}
+
+void UIChooserNodeGroup::retranslateUi()
+{
+ /* Update group-item: */
+ if (item())
+ item()->updateItem();
+}
+
+void UIChooserNodeGroup::copyContents(UIChooserNodeGroup *pCopyFrom)
+{
+ foreach (UIChooserNode *pNode, pCopyFrom->nodes(UIChooserNodeType_Group))
+ new UIChooserNodeGroup(this, m_nodesGroup.size(), pNode->toGroupNode());
+ foreach (UIChooserNode *pNode, pCopyFrom->nodes(UIChooserNodeType_Global))
+ new UIChooserNodeGlobal(this, m_nodesGlobal.size(), pNode->toGlobalNode());
+ foreach (UIChooserNode *pNode, pCopyFrom->nodes(UIChooserNodeType_Machine))
+ new UIChooserNodeMachine(this, m_nodesMachine.size(), pNode->toMachineNode());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGroup.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGroup.h
new file mode 100644
index 00000000..e110e7c7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeGroup.h
@@ -0,0 +1,153 @@
+/* $Id: UIChooserNodeGroup.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserNodeGroup class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserNodeGroup_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserNodeGroup_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIChooserNode.h"
+
+
+/** UIChooserNode subclass used as interface for invisible tree-view group nodes. */
+class UIChooserNodeGroup : public UIChooserNode
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs chooser node passing @a pParent to the base-class.
+ * @param iPosition Brings the initial node position.
+ * @param uId Brings current node id.
+ * @param strName Brings current node name.
+ * @param enmGroupType Brings group node type.
+ * @param fOpened Brings whether this group node is opened. */
+ UIChooserNodeGroup(UIChooserNode *pParent,
+ int iPosition,
+ const QUuid &uId,
+ const QString &strName,
+ UIChooserNodeGroupType enmGroupType,
+ bool fOpened);
+ /** Constructs chooser node passing @a pParent to the base-class.
+ * @param iPosition Brings the initial node position.
+ * @param pCopyFrom Brings the node to copy data from. */
+ UIChooserNodeGroup(UIChooserNode *pParent,
+ int iPosition,
+ UIChooserNodeGroup *pCopyFrom);
+ /** Destructs chooser node removing it's children. */
+ virtual ~UIChooserNodeGroup() RT_OVERRIDE;
+
+ /** Returns RTTI node type. */
+ virtual UIChooserNodeType type() const RT_OVERRIDE { return UIChooserNodeType_Group; }
+
+ /** Returns item name. */
+ virtual QString name() const RT_OVERRIDE;
+ /** Returns item full-name. */
+ virtual QString fullName() const RT_OVERRIDE;
+ /** Returns item description. */
+ virtual QString description() const RT_OVERRIDE;
+ /** Returns item definition.
+ * @param fFull Brings whether full definition is required
+ * which is used while saving group definitions,
+ * otherwise short definition will be returned,
+ * which is used while saving last chosen node. */
+ virtual QString definition(bool fFull = false) const RT_OVERRIDE;
+
+ /** Returns whether there are children of certain @a enmType. */
+ virtual bool hasNodes(UIChooserNodeType enmType = UIChooserNodeType_Any) const RT_OVERRIDE;
+ /** Returns a list of nodes of certain @a enmType. */
+ virtual QList<UIChooserNode*> nodes(UIChooserNodeType enmType = UIChooserNodeType_Any) const RT_OVERRIDE;
+
+ /** Adds passed @a pNode to specified @a iPosition. */
+ virtual void addNode(UIChooserNode *pNode, int iPosition) RT_OVERRIDE;
+ /** Removes passed @a pNode. */
+ virtual void removeNode(UIChooserNode *pNode) RT_OVERRIDE;
+
+ /** Removes all children with specified @a uId recursively. */
+ virtual void removeAllNodes(const QUuid &uId) RT_OVERRIDE;
+ /** Updates all children with specified @a uId recursively. */
+ virtual void updateAllNodes(const QUuid &uId) RT_OVERRIDE;
+
+ /** Returns position of specified node inside this one. */
+ virtual int positionOf(UIChooserNode *pNode) RT_OVERRIDE;
+
+ /** Defines node @a strName. */
+ void setName(const QString &strName);
+
+ /** Returns group node type. */
+ UIChooserNodeGroupType groupType() const { return m_enmGroupType; }
+
+ /** Returns whether this group node is opened. */
+ bool isOpened() const { return m_fOpened; }
+ /** Returns whether this group node is closed. */
+ bool isClosed() const { return !m_fOpened; }
+
+ /** Opens this group node. */
+ void open() { m_fOpened = true; }
+ /** Closes this group node. */
+ void close() { m_fOpened = false; }
+
+ /** Recursively searches for a children wrt. @a strSearchTerm and @a iSearchFlags and updates the @a matchedItems. */
+ virtual void searchForNodes(const QString &strSearchTerm, int iSearchFlags, QList<UIChooserNode*> &matchedItems) RT_OVERRIDE;
+
+ /** Performs sorting of children nodes. */
+ virtual void sortNodes() RT_OVERRIDE;
+
+ /** Returns node group id. */
+ QUuid id() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Copies children contents from @a pCopyFrom item. */
+ void copyContents(UIChooserNodeGroup *pCopyFrom);
+
+ /** Holds the node id. */
+ QUuid m_uId;
+ /** Holds the node name. */
+ QString m_strName;
+ /** Holds the group node type. */
+ UIChooserNodeGroupType m_enmGroupType;
+ /** Holds whether node is opened. */
+ bool m_fOpened;
+
+ /** Holds group children. */
+ QList<UIChooserNode*> m_nodesGroup;
+ /** Holds global children. */
+ QList<UIChooserNode*> m_nodesGlobal;
+ /** Holds machine children. */
+ QList<UIChooserNode*> m_nodesMachine;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserNodeGroup_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeMachine.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeMachine.cpp
new file mode 100644
index 00000000..a5ca0e50
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeMachine.cpp
@@ -0,0 +1,294 @@
+/* $Id: UIChooserNodeMachine.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserNodeMachine class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <qglobal.h>
+#ifdef VBOX_IS_QT6_OR_LATER /* fromWildcard is available since 6.0 */
+# include <QRegularExpression>
+#else
+# include <QRegExp>
+#endif
+
+/* GUI includes: */
+#include "UIChooserAbstractModel.h"
+#include "UIChooserNodeMachine.h"
+#include "UIVirtualMachineItemCloud.h"
+#include "UIVirtualMachineItemLocal.h"
+
+
+UIChooserNodeMachine::UIChooserNodeMachine(UIChooserNode *pParent,
+ int iPosition,
+ const CMachine &comMachine)
+ : UIChooserNode(pParent, false /* favorite */)
+ , m_pCache(new UIVirtualMachineItemLocal(comMachine))
+{
+ /* Add to parent: */
+ if (parentNode())
+ parentNode()->addNode(this, iPosition);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIChooserNodeMachine::UIChooserNodeMachine(UIChooserNode *pParent,
+ int iPosition,
+ const CCloudMachine &comCloudMachine)
+ : UIChooserNode(pParent, false /* favorite */)
+ , m_pCache(new UIVirtualMachineItemCloud(comCloudMachine))
+{
+ /* Add to parent: */
+ if (parentNode())
+ parentNode()->addNode(this, iPosition);
+
+ /* Cloud VM item can notify machine node only directly (no console), we have to setup listener: */
+ connect(static_cast<UIVirtualMachineItemCloud*>(m_pCache), &UIVirtualMachineItemCloud::sigRefreshFinished,
+ this, &UIChooserNodeMachine::sltHandleStateChange);
+ connect(static_cast<UIVirtualMachineItemCloud*>(m_pCache), &UIVirtualMachineItemCloud::sigRefreshStarted,
+ static_cast<UIChooserAbstractModel*>(model()), &UIChooserAbstractModel::sltHandleCloudMachineRefreshStarted);
+ connect(static_cast<UIVirtualMachineItemCloud*>(m_pCache), &UIVirtualMachineItemCloud::sigRefreshFinished,
+ static_cast<UIChooserAbstractModel*>(model()), &UIChooserAbstractModel::sltHandleCloudMachineRefreshFinished);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIChooserNodeMachine::UIChooserNodeMachine(UIChooserNode *pParent,
+ int iPosition,
+ UIFakeCloudVirtualMachineItemState enmState)
+ : UIChooserNode(pParent, false /* favorite */)
+ , m_pCache(new UIVirtualMachineItemCloud(enmState))
+{
+ /* Add to parent: */
+ if (parentNode())
+ parentNode()->addNode(this, iPosition);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIChooserNodeMachine::UIChooserNodeMachine(UIChooserNode *pParent,
+ int iPosition,
+ UIChooserNodeMachine *pCopyFrom)
+ : UIChooserNode(pParent, pCopyFrom->isFavorite())
+{
+ /* Prepare cache of corresponding type: */
+ switch (pCopyFrom->cacheType())
+ {
+ case UIVirtualMachineItemType_Local:
+ m_pCache = new UIVirtualMachineItemLocal(pCopyFrom->cache()->toLocal()->machine());
+ break;
+ case UIVirtualMachineItemType_CloudFake:
+ m_pCache = new UIVirtualMachineItemCloud(pCopyFrom->cache()->toCloud()->fakeCloudItemState());
+ break;
+ case UIVirtualMachineItemType_CloudReal:
+ m_pCache = new UIVirtualMachineItemCloud(pCopyFrom->cache()->toCloud()->machine());
+ break;
+ default:
+ break;
+ }
+
+ /* Add to parent: */
+ if (parentNode())
+ parentNode()->addNode(this, iPosition);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIChooserNodeMachine::~UIChooserNodeMachine()
+{
+ /* Delete item: */
+ delete item();
+
+ /* Remove from parent: */
+ if (parentNode())
+ parentNode()->removeNode(this);
+
+ /* Cleanup cache: */
+ delete m_pCache;
+}
+
+QString UIChooserNodeMachine::name() const
+{
+ return m_pCache->name();
+}
+
+QString UIChooserNodeMachine::fullName() const
+{
+ /* Get full parent name, append with '/' if not yet appended: */
+ AssertReturn(parentNode(), name());
+ QString strFullParentName = parentNode()->fullName();
+ if (!strFullParentName.endsWith('/'))
+ strFullParentName.append('/');
+ /* Return full item name based on parent prefix: */
+ return strFullParentName + name();
+}
+
+QString UIChooserNodeMachine::description() const
+{
+ return m_strDescription;
+}
+
+QString UIChooserNodeMachine::definition(bool) const
+{
+ const QString strNodePrefix = UIChooserAbstractModel::prefixToString(UIChooserNodeDataPrefixType_Machine);
+ return QString("%1=%2").arg(strNodePrefix).arg(UIChooserAbstractModel::toOldStyleUuid(id()));
+}
+
+bool UIChooserNodeMachine::hasNodes(UIChooserNodeType enmType /* = UIChooserNodeType_Any */) const
+{
+ Q_UNUSED(enmType);
+ AssertFailedReturn(false);
+}
+
+QList<UIChooserNode*> UIChooserNodeMachine::nodes(UIChooserNodeType enmType /* = UIChooserNodeType_Any */) const
+{
+ Q_UNUSED(enmType);
+ AssertFailedReturn(QList<UIChooserNode*>());
+}
+
+void UIChooserNodeMachine::addNode(UIChooserNode *pNode, int iPosition)
+{
+ Q_UNUSED(pNode);
+ Q_UNUSED(iPosition);
+ AssertFailedReturnVoid();
+}
+
+void UIChooserNodeMachine::removeNode(UIChooserNode *pNode)
+{
+ Q_UNUSED(pNode);
+ AssertFailedReturnVoid();
+}
+
+void UIChooserNodeMachine::removeAllNodes(const QUuid &uId)
+{
+ /* Skip other ids: */
+ if (id() != uId)
+ return;
+
+ /* Remove this node: */
+ delete this;
+}
+
+void UIChooserNodeMachine::updateAllNodes(const QUuid &uId)
+{
+ /* Skip other ids: */
+ if (id() != uId)
+ return;
+
+ /* Update cache: */
+ m_pCache->recache();
+
+ /* Update machine-item: */
+ if (item())
+ item()->updateItem();
+}
+
+int UIChooserNodeMachine::positionOf(UIChooserNode *pNode)
+{
+ Q_UNUSED(pNode);
+ AssertFailedReturn(0);
+}
+
+void UIChooserNodeMachine::searchForNodes(const QString &strSearchTerm, int iSearchFlags, QList<UIChooserNode*> &matchedItems)
+{
+ /* Ignore if we are not searching for the machine-node: */
+ if (!(iSearchFlags & UIChooserItemSearchFlag_Machine))
+ return;
+
+ /* If the search term is empty we just add the node to the matched list: */
+ if (strSearchTerm.isEmpty())
+ matchedItems << this;
+ else
+ {
+ /* If exact ID flag specified => check node ID: */
+ if (iSearchFlags & UIChooserItemSearchFlag_ExactId)
+ {
+ if (id() == QUuid(strSearchTerm))
+ matchedItems << this;
+ }
+ /* If exact name flag specified => check full node name: */
+ else if (iSearchFlags & UIChooserItemSearchFlag_ExactName)
+ {
+ if (name() == strSearchTerm)
+ matchedItems << this;
+ }
+ /* Otherwise check if name contains search term, including wildcards: */
+ else
+ {
+#ifdef VBOX_IS_QT6_OR_LATER /* fromWildcard is available since 6.0 */
+ QRegularExpression searchRegEx = QRegularExpression::fromWildcard(strSearchTerm, Qt::CaseInsensitive);
+#else
+ QRegExp searchRegEx(strSearchTerm, Qt::CaseInsensitive, QRegExp::WildcardUnix);
+#endif
+ if (name().contains(searchRegEx))
+ matchedItems << this;
+ }
+ }
+}
+
+void UIChooserNodeMachine::sortNodes()
+{
+ AssertFailedReturnVoid();
+}
+
+UIVirtualMachineItem *UIChooserNodeMachine::cache() const
+{
+ return m_pCache;
+}
+
+UIVirtualMachineItemType UIChooserNodeMachine::cacheType() const
+{
+ return cache() ? cache()->itemType() : UIVirtualMachineItemType_Invalid;
+}
+
+QUuid UIChooserNodeMachine::id() const
+{
+ return cache() ? cache()->id() : QUuid();
+}
+
+bool UIChooserNodeMachine::accessible() const
+{
+ return cache() ? cache()->accessible() : false;
+}
+
+void UIChooserNodeMachine::retranslateUi()
+{
+ /* Update internal stuff: */
+ m_strDescription = tr("Virtual Machine");
+
+ /* Update machine-item: */
+ if (item())
+ item()->updateItem();
+}
+
+void UIChooserNodeMachine::sltHandleStateChange()
+{
+ /* Update machine-item: */
+ if (item())
+ item()->updateItem();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeMachine.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeMachine.h
new file mode 100644
index 00000000..03537c13
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserNodeMachine.h
@@ -0,0 +1,145 @@
+/* $Id: UIChooserNodeMachine.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserNodeMachine class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserNodeMachine_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserNodeMachine_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIChooserNode.h"
+#include "UIManagerDefs.h"
+
+/* Forward declarations: */
+class UIVirtualMachineItem;
+class CCloudMachine;
+class CMachine;
+
+
+/** UIChooserNode subclass used as interface for invisible tree-view machine nodes. */
+class UIChooserNodeMachine : public UIChooserNode
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs chooser node for local VM passing @a pParent to the base-class.
+ * @param iPosition Brings initial node position.
+ * @param comMachine Brings COM machine object. */
+ UIChooserNodeMachine(UIChooserNode *pParent,
+ int iPosition,
+ const CMachine &comMachine);
+ /** Constructs chooser node for real cloud VM passing @a pParent to the base-class.
+ * @param iPosition Brings initial node position.
+ * @param comCloudMachine Brings COM cloud machine object. */
+ UIChooserNodeMachine(UIChooserNode *pParent,
+ int iPosition,
+ const CCloudMachine &comCloudMachine);
+ /** Constructs chooser node for fake cloud VM passing @a pParent to the base-class.
+ * @param iPosition Brings the initial node position.
+ * @param enmState Brings fake item type. */
+ UIChooserNodeMachine(UIChooserNode *pParent,
+ int iPosition,
+ UIFakeCloudVirtualMachineItemState enmState);
+ /** Constructs chooser node passing @a pParent to the base-class.
+ * @param iPosition Brings the initial node position.
+ * @param pCopyFrom Brings the node to copy data from. */
+ UIChooserNodeMachine(UIChooserNode *pParent,
+ int iPosition,
+ UIChooserNodeMachine *pCopyFrom);
+ /** Destructs chooser node. */
+ virtual ~UIChooserNodeMachine() RT_OVERRIDE;
+
+ /** Returns RTTI node type. */
+ virtual UIChooserNodeType type() const RT_OVERRIDE { return UIChooserNodeType_Machine; }
+
+ /** Returns item name. */
+ virtual QString name() const RT_OVERRIDE;
+ /** Returns item full-name. */
+ virtual QString fullName() const RT_OVERRIDE;
+ /** Returns item description. */
+ virtual QString description() const RT_OVERRIDE;
+ /** Returns item definition.
+ * @param fFull Brings whether full definition is required
+ * which is used while saving group definitions,
+ * otherwise short definition will be returned,
+ * which is used while saving last chosen node. */
+ virtual QString definition(bool fFull = false) const RT_OVERRIDE;
+
+ /** Returns whether there are children of certain @a enmType. */
+ virtual bool hasNodes(UIChooserNodeType enmType = UIChooserNodeType_Any) const RT_OVERRIDE;
+ /** Returns a list of nodes of certain @a enmType. */
+ virtual QList<UIChooserNode*> nodes(UIChooserNodeType enmType = UIChooserNodeType_Any) const RT_OVERRIDE;
+
+ /** Adds passed @a pNode to specified @a iPosition. */
+ virtual void addNode(UIChooserNode *pNode, int iPosition) RT_OVERRIDE;
+ /** Removes passed @a pNode. */
+ virtual void removeNode(UIChooserNode *pNode) RT_OVERRIDE;
+
+ /** Removes all children with specified @a uId recursively. */
+ virtual void removeAllNodes(const QUuid &uId) RT_OVERRIDE;
+ /** Updates all children with specified @a uId recursively. */
+ virtual void updateAllNodes(const QUuid &uId) RT_OVERRIDE;
+
+ /** Returns position of specified node inside this one. */
+ virtual int positionOf(UIChooserNode *pNode) RT_OVERRIDE;
+
+ /** Checks if this instance matches to search wrt. @a strSearchTerm and @a iSearchFlags and updates @a matchedItems. */
+ virtual void searchForNodes(const QString &strSearchTerm, int iSearchFlags, QList<UIChooserNode*> &matchedItems) RT_OVERRIDE;
+
+ /** Performs sorting of children nodes. */
+ virtual void sortNodes() RT_OVERRIDE;
+
+ /** Returns virtual machine cache instance. */
+ UIVirtualMachineItem *cache() const;
+ /** Returns virtual machine cache instance. */
+ UIVirtualMachineItemType cacheType() const;
+
+ /** Returns node machine id. */
+ QUuid id() const;
+ /** Returns whether node accessible. */
+ bool accessible() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles machine state change. */
+ void sltHandleStateChange();
+
+private:
+
+ /** Holds virtual machine cache instance. */
+ UIVirtualMachineItem *m_pCache;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserNodeMachine_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserSearchWidget.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserSearchWidget.cpp
new file mode 100644
index 00000000..a93a0f21
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserSearchWidget.cpp
@@ -0,0 +1,218 @@
+/* $Id: UIChooserSearchWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserSearchWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "QIToolButton.h"
+#include "UIChooserDefs.h"
+#include "UIChooserSearchWidget.h"
+#include "UIIconPool.h"
+#include "UISearchLineEdit.h"
+
+UIChooserSearchWidget::UIChooserSearchWidget(QWidget *pParent)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLineEdit(0)
+ , m_pMainLayout(0)
+ , m_pScrollToNextMatchButton(0)
+ , m_pScrollToPreviousMatchButton(0)
+ , m_pCloseButton(0)
+{
+ /* Have a background. In some cases having no background causes strange artefacts in Cinnamon themes: */
+ setAutoFillBackground(true);
+ prepareWidgets();
+ prepareConnections();
+ retranslateUi();
+}
+
+void UIChooserSearchWidget::setMatchCount(int iMatchCount)
+{
+ if (!m_pLineEdit)
+ return;
+ m_pLineEdit->setMatchCount(iMatchCount);
+}
+
+void UIChooserSearchWidget::setScroolToIndex(int iScrollToIndex)
+{
+ if (!m_pLineEdit)
+ return;
+ m_pLineEdit->setScrollToIndex(iScrollToIndex);
+}
+
+void UIChooserSearchWidget::appendToSearchString(const QString &strSearchText)
+{
+ if (!m_pLineEdit)
+ return;
+ m_pLineEdit->setText(m_pLineEdit->text().append(strSearchText));
+}
+
+void UIChooserSearchWidget::redoSearch()
+{
+ if (!m_pLineEdit)
+ return;
+ sltHandleSearchTermChange(m_pLineEdit->text());
+}
+
+void UIChooserSearchWidget::prepareWidgets()
+{
+ m_pMainLayout = new QHBoxLayout;
+ if (!m_pMainLayout)
+ return;
+
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setContentsMargins(0, 5, 0, 5);
+ m_pMainLayout->setSpacing(2);
+#else
+ m_pMainLayout->setContentsMargins(qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 2,
+ qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) / 4,
+ qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) / 2,
+ qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) / 4);
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2);
+#endif
+
+ m_pCloseButton = new QIToolButton;
+ if (m_pCloseButton)
+ {
+ m_pCloseButton->setIcon(UIIconPool::iconSet(":/close_16px.png"));
+ m_pMainLayout->addWidget(m_pCloseButton, 0, Qt::AlignLeft);
+ }
+
+ m_pLineEdit = new UISearchLineEdit;
+ if (m_pLineEdit)
+ {
+ m_pMainLayout->addWidget(m_pLineEdit);
+ m_pLineEdit->installEventFilter(this);
+ setFocusProxy(m_pLineEdit);
+ }
+
+ m_pScrollToPreviousMatchButton = new QIToolButton;
+ if (m_pScrollToPreviousMatchButton)
+ {
+ m_pScrollToPreviousMatchButton->setIcon(UIIconPool::iconSet(":/log_viewer_search_backward_16px.png",
+ ":/log_viewer_search_backward_disabled_16px.png"));
+ m_pMainLayout->addWidget(m_pScrollToPreviousMatchButton);
+ }
+ m_pScrollToNextMatchButton = new QIToolButton;
+ if (m_pScrollToNextMatchButton)
+ {
+ m_pScrollToNextMatchButton->setIcon(UIIconPool::iconSet(":/log_viewer_search_forward_16px.png",
+ ":/log_viewer_search_forward_disabled_16px.png"));
+ m_pMainLayout->addWidget(m_pScrollToNextMatchButton);
+ }
+
+ setLayout(m_pMainLayout);
+}
+
+void UIChooserSearchWidget::prepareConnections()
+{
+ if (m_pLineEdit)
+ connect(m_pLineEdit, &QILineEdit::textChanged, this, &UIChooserSearchWidget::sltHandleSearchTermChange);
+ if (m_pCloseButton)
+ connect(m_pCloseButton, &QIToolButton::clicked, this, &UIChooserSearchWidget::sltHandleCloseButtonClick);
+ if (m_pScrollToPreviousMatchButton)
+ connect(m_pScrollToPreviousMatchButton, &QIToolButton::clicked, this, &UIChooserSearchWidget::sltHandleScroolToButtonClick);
+ if (m_pScrollToNextMatchButton)
+ connect(m_pScrollToNextMatchButton, &QIToolButton::clicked, this, &UIChooserSearchWidget::sltHandleScroolToButtonClick);
+}
+
+void UIChooserSearchWidget::showEvent(QShowEvent *pEvent)
+{
+ Q_UNUSED(pEvent);
+ if (m_pLineEdit)
+ m_pLineEdit->setFocus();
+}
+
+void UIChooserSearchWidget::hideEvent(QHideEvent *pEvent)
+{
+ Q_UNUSED(pEvent);
+ if (m_pLineEdit)
+ m_pLineEdit->clear();
+}
+
+void UIChooserSearchWidget::retranslateUi()
+{
+ if (m_pScrollToNextMatchButton)
+ m_pScrollToNextMatchButton->setToolTip(tr("Navigate to the next item among the search results"));
+ if (m_pScrollToPreviousMatchButton)
+ m_pScrollToPreviousMatchButton->setToolTip(tr("Navigate to the previous item among the search results"));
+ if (m_pLineEdit)
+ m_pLineEdit->setToolTip(tr("Enter a search term to be used during virtual machine search"));
+ if (m_pCloseButton)
+ m_pCloseButton->setToolTip(tr("Close the search widget"));
+}
+
+bool UIChooserSearchWidget::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* Handle KeyPress events for m_pLineEdit only: */
+ if ( pWatched == m_pLineEdit
+ && pEvent->type() == QEvent::KeyPress)
+ {
+ QKeyEvent *pKeyEvent = dynamic_cast<QKeyEvent*>(pEvent);
+ if (pKeyEvent)
+ {
+ if (pKeyEvent->key() == Qt::Key_Escape)
+ {
+ emit sigToggleVisibility(false);
+ return true;
+ }
+ else if (pKeyEvent->key() == Qt::Key_Up || pKeyEvent->key() == Qt::Key_Down)
+ {
+ emit sigScrollToMatch(pKeyEvent->key() == Qt::Key_Down);
+ return true;
+ }
+ }
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::eventFilter(pWatched, pEvent);
+}
+
+void UIChooserSearchWidget::sltHandleSearchTermChange(const QString &strSearchTerm)
+{
+ if (strSearchTerm.isEmpty())
+ {
+ emit sigToggleVisibility(false);
+ return;
+ }
+ emit sigRedoSearch(strSearchTerm, UIChooserItemSearchFlag_Machine);
+}
+
+void UIChooserSearchWidget::sltHandleScroolToButtonClick()
+{
+ if (sender() == m_pScrollToNextMatchButton)
+ emit sigScrollToMatch(true);
+ else if (sender() == m_pScrollToPreviousMatchButton)
+ emit sigScrollToMatch(false);
+}
+
+void UIChooserSearchWidget::sltHandleCloseButtonClick()
+{
+ emit sigToggleVisibility(false);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserSearchWidget.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserSearchWidget.h
new file mode 100644
index 00000000..e10d9dcc
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserSearchWidget.h
@@ -0,0 +1,104 @@
+/* $Id: UIChooserSearchWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserSearchWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserSearchWidget_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserSearchWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QILineEdit;
+class QIToolButton;
+class UISearchLineEdit;
+
+/** QWidget extension used as virtual machine search widget in the VM Chooser-pane. */
+class UIChooserSearchWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigRedoSearch(const QString &strSearchTerm, int iSearchFlags);
+ /** Is being signalled as next/prev tool buttons are pressed. @a fIsNext is true
+ * for the next and false for the previous case. */
+ void sigScrollToMatch(bool fIsNext);
+ /** Is used for signalling show/hide event from this to parent. */
+ void sigToggleVisibility(bool fIsVisible);
+
+public:
+
+ UIChooserSearchWidget(QWidget *pParent);
+ /** Forward @a iMatchCount to UISearchLineEdit. */
+ void setMatchCount(int iMatchCount);
+ /** Forward @a iScrollToIndex to UISearchLineEdit. */
+ void setScroolToIndex(int iScrollToIndex);
+ /** Appends the @a strSearchText to the current (if any) search text. */
+ void appendToSearchString(const QString &strSearchText);
+ /** Repeats the last search again. */
+ void redoSearch();
+
+protected:
+
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ virtual void hideEvent(QHideEvent *pEvent) RT_OVERRIDE;
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual bool eventFilter(QObject *pWatched, QEvent *pEvent) RT_OVERRIDE;
+
+public slots:
+
+private slots:
+
+ /** Emits sigRedoSearch thuse causes a re-search. */
+ void sltHandleSearchTermChange(const QString &strSearchTerm);
+ void sltHandleScroolToButtonClick();
+ /** Emits sigToggleVisibility, */
+ void sltHandleCloseButtonClick();
+
+private:
+
+ void prepareWidgets();
+ void prepareConnections();
+
+ /** @name Member widgets.
+ * @{ */
+ UISearchLineEdit *m_pLineEdit;
+ QHBoxLayout *m_pMainLayout;
+ QIToolButton *m_pScrollToNextMatchButton;
+ QIToolButton *m_pScrollToPreviousMatchButton;
+ QIToolButton *m_pCloseButton;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserSearchWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserView.cpp b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserView.cpp
new file mode 100644
index 00000000..027e9fcb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserView.cpp
@@ -0,0 +1,333 @@
+/* $Id: UIChooserView.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChooserView class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QScrollBar>
+
+/* GUI includes: */
+#include "UIChooserItem.h"
+#include "UIChooserModel.h"
+#include "UIChooserSearchWidget.h"
+#include "UIChooserView.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/** QAccessibleWidget extension used as an accessibility interface for Chooser-view. */
+class UIAccessibilityInterfaceForUIChooserView : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating Chooser-view accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UIChooserView"))
+ return new UIAccessibilityInterfaceForUIChooserView(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ UIAccessibilityInterfaceForUIChooserView(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::List)
+ {}
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), 0);
+
+ /* Return the number of model children if model really assigned: */
+ return view()->model() ? view()->model()->root()->items().size() : 0;
+ }
+
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Return the model child with the passed iIndex if model really assigned: */
+ return QAccessible::queryAccessibleInterface(view()->model() ? view()->model()->root()->items().at(iIndex) : 0);
+ }
+
+ /** Returns the index of passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), -1);
+ /* Make sure child is valid: */
+ AssertReturn(pChild, -1);
+
+ /* Acquire item itself: */
+ UIChooserItem *pChildItem = qobject_cast<UIChooserItem*>(pChild->object());
+
+ /* Return the index of item in it's parent: */
+ return pChildItem && pChildItem->parentItem()
+ ? pChildItem->parentItem()->items().indexOf(pChildItem)
+ : -1;;
+ }
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), QString());
+
+ /* Return view tool-tip: */
+ Q_UNUSED(enmTextRole);
+ return view()->whatsThis();
+ }
+
+private:
+
+ /** Returns corresponding Chooser-view. */
+ UIChooserView *view() const { return qobject_cast<UIChooserView*>(widget()); }
+};
+
+
+UIChooserView::UIChooserView(QWidget *pParent)
+ : QIWithRetranslateUI<QIGraphicsView>(pParent)
+ , m_pChooserModel(0)
+ , m_pSearchWidget(0)
+ , m_iMinimumWidthHint(0)
+{
+ prepare();
+}
+
+void UIChooserView::setModel(UIChooserModel *pChooserModel)
+{
+ m_pChooserModel = pChooserModel;
+}
+
+UIChooserModel *UIChooserView::model() const
+{
+ return m_pChooserModel;
+}
+
+bool UIChooserView::isSearchWidgetVisible() const
+{
+ /* Make sure search widget exists: */
+ AssertPtrReturn(m_pSearchWidget, false);
+
+ /* Return widget visibility state: */
+ return m_pSearchWidget->isVisible();
+}
+
+void UIChooserView::setSearchWidgetVisible(bool fVisible)
+{
+ /* Make sure search widget exists: */
+ AssertPtrReturnVoid(m_pSearchWidget);
+
+ /* Make sure keyboard focus is managed correctly: */
+ if (fVisible)
+ m_pSearchWidget->setFocus();
+ else
+ setFocus();
+
+ /* Make sure visibility state is really changed: */
+ if (m_pSearchWidget->isVisible() == fVisible)
+ return;
+
+ /* Set widget visibility state: */
+ m_pSearchWidget->setVisible(fVisible);
+
+ /* Notify listeners: */
+ emit sigSearchWidgetVisibilityChanged(fVisible);
+
+ /* Update geometry if widget is visible: */
+ if (m_pSearchWidget->isVisible())
+ updateSearchWidgetGeometry();
+
+ /* Reset search each time widget visibility changed,
+ * Model can be undefined.. */
+ if (model())
+ model()->resetSearch();
+}
+
+void UIChooserView::setSearchResultsCount(int iTotalMatchCount, int iCurrentlyScrolledItemIndex)
+{
+ /* Make sure search widget exists: */
+ AssertPtrReturnVoid(m_pSearchWidget);
+
+ /* Update count of search results and scroll to certain result: */
+ m_pSearchWidget->setMatchCount(iTotalMatchCount);
+ m_pSearchWidget->setScroolToIndex(iCurrentlyScrolledItemIndex);
+}
+
+void UIChooserView::appendToSearchString(const QString &strSearchText)
+{
+ /* Make sure search widget exists: */
+ AssertPtrReturnVoid(m_pSearchWidget);
+
+ /* Update search string with passed text: */
+ m_pSearchWidget->appendToSearchString(strSearchText);
+}
+
+void UIChooserView::redoSearch()
+{
+ /* Make sure search widget exists: */
+ AssertPtrReturnVoid(m_pSearchWidget);
+
+ /* Pass request to search widget: */
+ m_pSearchWidget->redoSearch();
+}
+
+void UIChooserView::sltMinimumWidthHintChanged(int iHint)
+{
+ /* Is there something changed? */
+ if (m_iMinimumWidthHint == iHint)
+ return;
+
+ /* Remember new value: */
+ m_iMinimumWidthHint = iHint;
+
+ /* Set minimum view width according passed width-hint: */
+ setMinimumWidth(2 * frameWidth() + m_iMinimumWidthHint + verticalScrollBar()->sizeHint().width());
+
+ /* Update scene rectangle: */
+ updateSceneRect();
+}
+
+void UIChooserView::sltRedoSearch(const QString &strSearchTerm, int iSearchFlags)
+{
+ /* Model can be undefined: */
+ if (!model())
+ return;
+
+ /* Perform search: */
+ model()->performSearch(strSearchTerm, iSearchFlags);
+}
+
+void UIChooserView::sltHandleScrollToSearchResult(bool fNext)
+{
+ /* Model can be undefined: */
+ if (!model())
+ return;
+
+ /* Move to requested search result: */
+ model()->selectSearchResult(fNext);
+}
+
+void UIChooserView::sltHandleSearchWidgetVisibilityToggle(bool fVisible)
+{
+ setSearchWidgetVisible(fVisible);
+}
+
+void UIChooserView::retranslateUi()
+{
+ /* Translate this: */
+ setWhatsThis(tr("Contains a tree of Virtual Machines and their groups"));
+}
+
+void UIChooserView::prepare()
+{
+ /* Install Chooser-view accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUIChooserView::pFactory);
+
+ /* Prepare everything: */
+ prepareThis();
+ prepareWidget();
+
+ /* Update everything: */
+ updateSceneRect();
+ updateSearchWidgetGeometry();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIChooserView::prepareThis()
+{
+ /* Prepare palette: */
+ QPalette pal = QApplication::palette();
+ pal.setColor(QPalette::Active, QPalette::Base, pal.color(QPalette::Active, QPalette::Window));
+ pal.setColor(QPalette::Inactive, QPalette::Base, pal.color(QPalette::Inactive, QPalette::Window));
+ setPalette(pal);
+
+ /* Prepare frame: */
+ setFrameShape(QFrame::NoFrame);
+ setFrameShadow(QFrame::Plain);
+ setAlignment(Qt::AlignLeft | Qt::AlignTop);
+
+ /* Prepare scroll-bars policy: */
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+}
+
+void UIChooserView::prepareWidget()
+{
+ /* Create the search widget (initially hidden): */
+ m_pSearchWidget = new UIChooserSearchWidget(this);
+ if (m_pSearchWidget)
+ {
+ m_pSearchWidget->hide();
+ connect(m_pSearchWidget, &UIChooserSearchWidget::sigRedoSearch,
+ this, &UIChooserView::sltRedoSearch);
+ connect(m_pSearchWidget, &UIChooserSearchWidget::sigScrollToMatch,
+ this, &UIChooserView::sltHandleScrollToSearchResult);
+ connect(m_pSearchWidget, &UIChooserSearchWidget::sigToggleVisibility,
+ this, &UIChooserView::sltHandleSearchWidgetVisibilityToggle);
+ }
+}
+
+void UIChooserView::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI<QIGraphicsView>::resizeEvent(pEvent);
+ /* Notify listeners: */
+ emit sigResized();
+
+ /* Update everything: */
+ updateSceneRect();
+ updateSearchWidgetGeometry();
+}
+
+void UIChooserView::updateSceneRect()
+{
+ setSceneRect(0, 0, m_iMinimumWidthHint, height());
+}
+
+void UIChooserView::updateSearchWidgetGeometry()
+{
+ /* Make sure search widget exists: */
+ AssertPtrReturnVoid(m_pSearchWidget);
+
+ /* Update visible widget only: */
+ if (m_pSearchWidget->isVisible())
+ {
+ const int iHeight = m_pSearchWidget->height();
+ m_pSearchWidget->setGeometry(QRect(0, height() - iHeight, width(), iHeight));
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserView.h b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserView.h
new file mode 100644
index 00000000..92dbbfb2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserView.h
@@ -0,0 +1,162 @@
+/* $Id: UIChooserView.h $ */
+/** @file
+ * VBox Qt GUI - UIChooserView class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserView_h
+#define FEQT_INCLUDED_SRC_manager_chooser_UIChooserView_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIGraphicsView.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class UIChooserModel;
+class UIChooserSearchWidget;
+
+/** QIGraphicsView extension used as VM chooser pane view. */
+class UIChooserView : public QIWithRetranslateUI<QIGraphicsView>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about resize. */
+ void sigResized();
+
+ /** Notifies listeners about search widget visibility changed to @a fVisible. */
+ void sigSearchWidgetVisibilityChanged(bool fVisible);
+
+public:
+
+ /** Constructs a Chooser-view passing @a pParent to the base-class. */
+ UIChooserView(QWidget *pParent);
+
+ /** @name General stuff.
+ * @{ */
+ /** Defines @a pChooserModel reference. */
+ void setModel(UIChooserModel *pChooserModel);
+ /** Returns Chooser-model reference. */
+ UIChooserModel *model() const;
+ /** @} */
+
+ /** @name Search stuff.
+ * @{ */
+ /** Returns whether search widget visible. */
+ bool isSearchWidgetVisible() const;
+ /** Makes search widget @a fVisible. */
+ void setSearchWidgetVisible(bool fVisible);
+
+ /** Updates search widget's results count.
+ * @param iTotalMatchCount Brings total search results count.
+ * @param iCurrentlyScrolledItemIndex Brings the item index search currently scrolled to. */
+ void setSearchResultsCount(int iTotalMatchCount, int iCurrentlyScrolledItemIndex);
+ /** Forwards @a strSearchText to the search widget which in
+ * turn appends it to the current (if any) search term. */
+ void appendToSearchString(const QString &strSearchText);
+ /** Repeats the last search again. */
+ void redoSearch();
+ /** @} */
+
+public slots:
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Handles minimum width @a iHint change. */
+ void sltMinimumWidthHintChanged(int iHint);
+ /** @} */
+
+protected:
+
+ /** @name Event handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Search stuff.
+ * @{ */
+ /** Handles request for a new search.
+ * @param strSearchTerm Brings the search term.
+ * @param iSearchFlags Brings the item search flags. */
+ void sltRedoSearch(const QString &strSearchTerm, int iSearchFlags);
+ /** Handles request to scroll to @a fNext search result. */
+ void sltHandleScrollToSearchResult(bool fNext);
+ /** Handles request to scroll to make search widget @a fVisible. */
+ void sltHandleSearchWidgetVisibilityToggle(bool fVisible);
+ /** @} */
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares this. */
+ void prepareThis();
+ /** Prepares widgets. */
+ void prepareWidget();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Updates scene rectangle. */
+ void updateSceneRect();
+ /** @} */
+
+ /** @name Search stuff.
+ * @{ */
+ /** Updates search widget's geometry. */
+ void updateSearchWidgetGeometry();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the Chooser-model reference. */
+ UIChooserModel *m_pChooserModel;
+ /** @} */
+
+ /** @name Search stuff.
+ * @{ */
+ /** Holds the search widget instance. */
+ UIChooserSearchWidget *m_pSearchWidget;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds the minimum width hint. */
+ int m_iMinimumWidthHint;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserView_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/manager/details/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetails.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetails.cpp
new file mode 100644
index 00000000..74a00164
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetails.cpp
@@ -0,0 +1,133 @@
+/* $Id: UIDetails.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetails class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDetails.h"
+#include "UIDetailsModel.h"
+#include "UIDetailsView.h"
+#include "UIExtraDataManager.h"
+
+
+UIDetails::UIDetails(QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_pMainLayout(0)
+ , m_pDetailsModel(0)
+ , m_pDetailsView(0)
+{
+ prepare();
+}
+
+void UIDetails::setItems(const QList<UIVirtualMachineItem*> &items)
+{
+ /* Propagate to details-model: */
+ m_pDetailsModel->setItems(items);
+}
+
+void UIDetails::prepare()
+{
+ /* Prepare everything: */
+ prepareContents();
+ prepareConnections();
+
+ /* Configure context-sensitive help: */
+ uiCommon().setHelpKeyword(this, "vm-details-tool");
+
+ /* Init model finally: */
+ initModel();
+}
+
+void UIDetails::prepareContents()
+{
+ /* Prepare main-layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (m_pMainLayout)
+ {
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+ m_pMainLayout->setSpacing(0);
+
+ /* Prepare model: */
+ prepareModel();
+ }
+}
+
+void UIDetails::prepareModel()
+{
+ /* Prepare model: */
+ m_pDetailsModel = new UIDetailsModel(this);
+ if (m_pDetailsModel)
+ prepareView();
+}
+
+void UIDetails::prepareView()
+{
+ AssertPtrReturnVoid(m_pDetailsModel);
+ AssertPtrReturnVoid(m_pMainLayout);
+
+ /* Prepare view: */
+ m_pDetailsView = new UIDetailsView(this);
+ if (m_pDetailsView)
+ {
+ m_pDetailsView->setScene(m_pDetailsModel->scene());
+ m_pDetailsView->show();
+ setFocusProxy(m_pDetailsView);
+
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pDetailsView);
+ }
+}
+
+void UIDetails::prepareConnections()
+{
+ /* Extra-data events connections: */
+ connect(gEDataManager, &UIExtraDataManager::sigDetailsCategoriesChange,
+ m_pDetailsModel, &UIDetailsModel::sltHandleExtraDataCategoriesChange);
+ connect(gEDataManager, &UIExtraDataManager::sigDetailsOptionsChange,
+ m_pDetailsModel, &UIDetailsModel::sltHandleExtraDataOptionsChange);
+
+ /* Model connections: */
+ connect(m_pDetailsModel, &UIDetailsModel::sigRootItemMinimumWidthHintChanged,
+ m_pDetailsView, &UIDetailsView::sltMinimumWidthHintChanged);
+ connect(m_pDetailsModel, &UIDetailsModel::sigLinkClicked,
+ this, &UIDetails::sigLinkClicked);
+ connect(this, &UIDetails::sigToggleStarted,
+ m_pDetailsModel, &UIDetailsModel::sltHandleToggleStarted);
+ connect(this, &UIDetails::sigToggleFinished,
+ m_pDetailsModel, &UIDetailsModel::sltHandleToggleFinished);
+
+ /* View connections: */
+ connect(m_pDetailsView, &UIDetailsView::sigResized,
+ m_pDetailsModel, &UIDetailsModel::sltHandleViewResize);
+}
+
+void UIDetails::initModel()
+{
+ m_pDetailsModel->init();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetails.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetails.h
new file mode 100644
index 00000000..7a6dfb4f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetails.h
@@ -0,0 +1,112 @@
+/* $Id: UIDetails.h $ */
+/** @file
+ * VBox Qt GUI - UIDetails class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIDetails_h
+#define FEQT_INCLUDED_SRC_manager_details_UIDetails_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* Forward declartions: */
+class QString;
+class QVBoxLayout;
+class UIDetailsModel;
+class UIDetailsView;
+class UIVirtualMachineItem;
+
+/** QWidget-based Details pane container. */
+class UIDetails : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name General stuff.
+ * @{ */
+ /** Notifies listeners about link click.
+ * @param strCategory Brings link category.
+ * @param strControl Brings control name.
+ * @param uId Brings machine ID. */
+ void sigLinkClicked(const QString &strCategory,
+ const QString &strControl,
+ const QUuid &uId);
+
+ /** Notifies listeners about toggling started. */
+ void sigToggleStarted();
+ /** Notifies listeners about toggling finished. */
+ void sigToggleFinished();
+ /** @} */
+
+public:
+
+ /** Constructs Details pane passing @a pParent to the base-class. */
+ UIDetails(QWidget *pParent = 0);
+
+ /** @name General stuff.
+ * @{ */
+ /** Return the Details-model instance. */
+ UIDetailsModel *model() const { return m_pDetailsModel; }
+ /** Return the Details-view instance. */
+ UIDetailsView *view() const { return m_pDetailsView; }
+
+ /** Replaces current model @a items. */
+ void setItems(const QList<UIVirtualMachineItem*> &items);
+ /** @} */
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares contents. */
+ void prepareContents();
+ /** Prepares model. */
+ void prepareModel();
+ /** Prepares view. */
+ void prepareView();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Inits model. */
+ void initModel();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the main layout instance. */
+ QVBoxLayout *m_pMainLayout;
+ /** Holds the details model instance. */
+ UIDetailsModel *m_pDetailsModel;
+ /** Holds the details view instance. */
+ UIDetailsView *m_pDetailsView;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIDetails_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsContextMenu.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsContextMenu.cpp
new file mode 100644
index 00000000..9c56a8e3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsContextMenu.cpp
@@ -0,0 +1,1006 @@
+/* $Id: UIDetailsContextMenu.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsContextMenu class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QListWidget>
+#include <QMetaEnum>
+
+/* GUI includes: */
+#include "UIConverter.h"
+#include "UIDetailsContextMenu.h"
+#include "UIDetailsModel.h"
+
+
+UIDetailsContextMenu::UIDetailsContextMenu(UIDetailsModel *pModel)
+ : QIWithRetranslateUI2<QWidget>(0, Qt::Popup)
+ , m_pModel(pModel)
+ , m_pListWidgetCategories(0)
+ , m_pListWidgetOptions(0)
+{
+ prepare();
+}
+
+void UIDetailsContextMenu::updateCategoryStates()
+{
+ /* Enumerate all the category items: */
+ for (int i = 0; i < m_pListWidgetCategories->count(); ++i)
+ {
+ QListWidgetItem *pCategoryItem = m_pListWidgetCategories->item(i);
+ if (pCategoryItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const DetailsElementType enmCategoryType = pCategoryItem->data(DataField_Type).value<DetailsElementType>();
+ pCategoryItem->setCheckState(m_pModel->categories().contains(enmCategoryType) ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+}
+
+void UIDetailsContextMenu::updateOptionStates(DetailsElementType enmRequiredCategoryType /* = DetailsElementType_Invalid */)
+{
+ /* First make sure we really have category item selected: */
+ QListWidgetItem *pCategoryItem = m_pListWidgetCategories->currentItem();
+ if (!pCategoryItem)
+ return;
+
+ /* Then acquire category type and check if it is suitable: */
+ const DetailsElementType enmCategoryType = pCategoryItem->data(DataField_Type).value<DetailsElementType>();
+ if (enmRequiredCategoryType == DetailsElementType_Invalid)
+ enmRequiredCategoryType = enmCategoryType;
+ if (enmCategoryType != enmRequiredCategoryType)
+ return;
+
+ /* Handle known category types: */
+ switch (enmRequiredCategoryType)
+ {
+ case DetailsElementType_General:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>();
+ pOptionItem->setCheckState(m_pModel->optionsGeneral() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_System:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>();
+ pOptionItem->setCheckState(m_pModel->optionsSystem() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Display:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>();
+ pOptionItem->setCheckState(m_pModel->optionsDisplay() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Storage:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>();
+ pOptionItem->setCheckState(m_pModel->optionsStorage() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Audio:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>();
+ pOptionItem->setCheckState(m_pModel->optionsAudio() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Network:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>();
+ pOptionItem->setCheckState(m_pModel->optionsNetwork() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Serial:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>();
+ pOptionItem->setCheckState(m_pModel->optionsSerial() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_USB:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>();
+ pOptionItem->setCheckState(m_pModel->optionsUsb() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_SF:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>();
+ pOptionItem->setCheckState(m_pModel->optionsSharedFolders() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_UI:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>();
+ pOptionItem->setCheckState(m_pModel->optionsUserInterface() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Description:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* Apply check-state on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>();
+ pOptionItem->setCheckState(m_pModel->optionsDescription() & enmOptionType ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void UIDetailsContextMenu::retranslateUi()
+{
+ retranslateCategories();
+ retranslateOptions();
+ adjustListWidgets();
+}
+
+void UIDetailsContextMenu::retranslateCategories()
+{
+ /* Enumerate all the category items: */
+ for (int i = 0; i < m_pListWidgetCategories->count(); ++i)
+ {
+ QListWidgetItem *pCategoryItem = m_pListWidgetCategories->item(i);
+ if (pCategoryItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const DetailsElementType enmCategoryType = pCategoryItem->data(DataField_Type).value<DetailsElementType>();
+ pCategoryItem->setText(gpConverter->toString(enmCategoryType));
+ }
+ }
+}
+
+void UIDetailsContextMenu::retranslateOptions()
+{
+ /* Acquire currently selected category item: */
+ QListWidgetItem *pCategoryItem = m_pListWidgetCategories->currentItem();
+ if (!pCategoryItem)
+ return;
+
+ /* Populate currently selected category options: */
+ const DetailsElementType enmCategoryType = pCategoryItem->data(DataField_Type).value<DetailsElementType>();
+ switch (enmCategoryType)
+ {
+ case DetailsElementType_General:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_System:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Display:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Storage:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Audio:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Network:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Serial:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_USB:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_SF:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_UI:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Description:
+ {
+ /* Enumerate all the option items: */
+ for (int i = 0; i < m_pListWidgetOptions->count(); ++i)
+ {
+ QListWidgetItem *pOptionItem = m_pListWidgetOptions->item(i);
+ if (pOptionItem)
+ {
+ /* We can translate this thing on per-enum basis: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription enmOptionType =
+ pOptionItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>();
+ pOptionItem->setText(gpConverter->toString(enmOptionType));
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void UIDetailsContextMenu::sltCategoryItemEntered(QListWidgetItem *pItem)
+{
+ /* Choose hovered item as current one: */
+ m_pListWidgetCategories->setCurrentItem(pItem);
+}
+
+void UIDetailsContextMenu::sltCategoryItemClicked(QListWidgetItem *pItem)
+{
+ /* Notify listeners: */
+ const DetailsElementType enmCategoryType = pItem->data(DataField_Type).value<DetailsElementType>();
+
+ /* Toggle element visibility status: */
+ QMap<DetailsElementType, bool> categories = m_pModel->categories();
+ if (categories.contains(enmCategoryType))
+ categories.remove(enmCategoryType);
+ else
+ categories[enmCategoryType] = true;
+ m_pModel->setCategories(categories);
+}
+
+void UIDetailsContextMenu::sltCategoryItemChanged(QListWidgetItem *, QListWidgetItem *)
+{
+ /* Update options list: */
+ populateOptions();
+ updateOptionStates();
+ retranslateOptions();
+}
+
+void UIDetailsContextMenu::sltOptionItemEntered(QListWidgetItem *pItem)
+{
+ /* Choose hovered item as current one: */
+ m_pListWidgetOptions->setCurrentItem(pItem);
+}
+
+void UIDetailsContextMenu::sltOptionItemClicked(QListWidgetItem *pItem)
+{
+ /* First make sure we really have category item selected: */
+ QListWidgetItem *pCategoryItem = m_pListWidgetCategories->currentItem();
+ if (!pCategoryItem)
+ return;
+
+ /* Then acquire category type: */
+ const DetailsElementType enmCategoryType = pCategoryItem->data(DataField_Type).value<DetailsElementType>();
+
+ /* Handle known category types: */
+ switch (enmCategoryType)
+ {
+ case DetailsElementType_General:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>();
+ m_pModel->setOptionsGeneral(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>(m_pModel->optionsGeneral() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_System:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>();
+ m_pModel->setOptionsSystem(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>(m_pModel->optionsSystem() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_Display:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>();
+ m_pModel->setOptionsDisplay(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>(m_pModel->optionsDisplay() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_Storage:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>();
+ m_pModel->setOptionsStorage(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>(m_pModel->optionsStorage() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_Audio:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>();
+ m_pModel->setOptionsAudio(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>(m_pModel->optionsAudio() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_Network:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>();
+ m_pModel->setOptionsNetwork(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(m_pModel->optionsNetwork() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_Serial:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>();
+ m_pModel->setOptionsSerial(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>(m_pModel->optionsSerial() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_USB:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>();
+ m_pModel->setOptionsUsb(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>(m_pModel->optionsUsb() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_SF:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>();
+ m_pModel->setOptionsSharedFolders(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>(m_pModel->optionsSharedFolders() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_UI:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>();
+ m_pModel->setOptionsUserInterface(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>(m_pModel->optionsUserInterface() ^ enmOptionType));
+ break;
+ }
+ case DetailsElementType_Description:
+ {
+ /* Toggle element visibility status: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription enmOptionType =
+ pItem->data(DataField_Type).value<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>();
+ m_pModel->setOptionsDescription(static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>(m_pModel->optionsDescription() ^ enmOptionType));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void UIDetailsContextMenu::prepare()
+{
+ /* Create main layout: */
+ QHBoxLayout *pMainLayout = new QHBoxLayout(this);
+ if (pMainLayout)
+ {
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+ pMainLayout->setSpacing(1);
+
+ /* Create list of categories: */
+ m_pListWidgetCategories = new QListWidget(this);
+ if (m_pListWidgetCategories)
+ {
+ m_pListWidgetCategories->setMouseTracking(true);
+ m_pListWidgetCategories->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ connect(m_pListWidgetCategories, &QListWidget::itemEntered,
+ this, &UIDetailsContextMenu::sltCategoryItemEntered);
+ connect(m_pListWidgetCategories, &QListWidget::itemClicked,
+ this, &UIDetailsContextMenu::sltCategoryItemClicked);
+ connect(m_pListWidgetCategories, &QListWidget::currentItemChanged,
+ this, &UIDetailsContextMenu::sltCategoryItemChanged);
+ pMainLayout->addWidget(m_pListWidgetCategories);
+ }
+
+ /* Create list of options: */
+ m_pListWidgetOptions = new QListWidget(this);
+ if (m_pListWidgetOptions)
+ {
+ m_pListWidgetOptions->setMouseTracking(true);
+ m_pListWidgetOptions->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ connect(m_pListWidgetOptions, &QListWidget::itemEntered,
+ this, &UIDetailsContextMenu::sltOptionItemEntered);
+ connect(m_pListWidgetOptions, &QListWidget::itemClicked,
+ this, &UIDetailsContextMenu::sltOptionItemClicked);
+ pMainLayout->addWidget(m_pListWidgetOptions);
+ }
+ }
+
+ /* Prepare lists: */
+ populateCategories();
+ populateOptions();
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIDetailsContextMenu::populateCategories()
+{
+ /* Clear category list initially: */
+ m_pListWidgetCategories->clear();
+
+ /* Enumerate all the known categories: */
+ for (int i = DetailsElementType_Invalid + 1; i < DetailsElementType_Max; ++i)
+ {
+ /* Prepare current category type: */
+ const DetailsElementType enmCategoryType = static_cast<DetailsElementType>(i);
+ /* And create list-widget item of it: */
+ QListWidgetItem *pCategoryItem = createCategoryItem(gpConverter->toIcon(enmCategoryType));
+ if (pCategoryItem)
+ {
+ pCategoryItem->setData(DataField_Type, QVariant::fromValue(enmCategoryType));
+ pCategoryItem->setCheckState(Qt::Unchecked);
+ }
+ }
+}
+
+void UIDetailsContextMenu::populateOptions()
+{
+ /* Clear option list initially: */
+ m_pListWidgetOptions->clear();
+
+ /* Acquire currently selected category item: */
+ QListWidgetItem *pCategoryItem = m_pListWidgetCategories->currentItem();
+ if (!pCategoryItem)
+ return;
+
+ /* We will use that one for all the options fetching: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* Populate currently selected category options: */
+ const DetailsElementType enmCategoryType = pCategoryItem->data(DataField_Type).value<DetailsElementType>();
+ switch (enmCategoryType)
+ {
+ case DetailsElementType_General:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeGeneral");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_System:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeSystem");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Display:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeDisplay");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Storage:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeStorage");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Audio:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeAudio");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Network:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeNetwork");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Serial:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeSerial");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_USB:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeUsb");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_SF:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeSharedFolders");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_UI:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeUserInterface");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ case DetailsElementType_Description:
+ {
+ /* Enumerate all the known options: */
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeDescription");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Default)
+ continue;
+ /* And create list-widget item of it: */
+ QListWidgetItem *pOptionItem = createOptionItem();
+ if (pOptionItem)
+ {
+ pOptionItem->setData(DataField_Type, QVariant::fromValue(enmOptionType));
+ pOptionItem->setData(DataField_Name, gpConverter->toInternalString(enmOptionType));
+ pOptionItem->setCheckState(Qt::Unchecked);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void UIDetailsContextMenu::adjustListWidgets()
+{
+ /* Include frame width: */
+ int iW = 2 * m_pListWidgetCategories->frameWidth();
+ int iH = iW;
+
+ /* Include size hints: */
+ iW += m_pListWidgetCategories->sizeHintForColumn(0);
+ iH += m_pListWidgetCategories->sizeHintForRow(0) * m_pListWidgetCategories->count();
+
+ /* Category list size is constant, options list size is vague: */
+ m_pListWidgetCategories->setFixedSize(QSize(iW * 1.3, iH));
+ m_pListWidgetOptions->setFixedSize(QSize(iW * 1.3, iH));
+}
+
+QListWidgetItem *UIDetailsContextMenu::createCategoryItem(const QIcon &icon)
+{
+ QListWidgetItem *pItem = new QListWidgetItem(icon, QString(), m_pListWidgetCategories);
+ if (pItem)
+ m_pListWidgetCategories->addItem(pItem);
+ return pItem;
+}
+
+QListWidgetItem *UIDetailsContextMenu::createOptionItem()
+{
+ QListWidgetItem *pItem = new QListWidgetItem(QString(), m_pListWidgetOptions);
+ if (pItem)
+ m_pListWidgetOptions->addItem(pItem);
+ return pItem;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsContextMenu.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsContextMenu.h
new file mode 100644
index 00000000..8b53c33f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsContextMenu.h
@@ -0,0 +1,117 @@
+/* $Id: UIDetailsContextMenu.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsContextMenu class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIDetailsContextMenu_h
+#define FEQT_INCLUDED_SRC_manager_details_UIDetailsContextMenu_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+
+/* Forward declaration: */
+class QListWidget;
+class QListWidgetItem;
+class UIDetailsModel;
+
+/** QWidget subclass used as Details pane context menu. */
+class UIDetailsContextMenu : public QIWithRetranslateUI2<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Context menu data fields. */
+ enum DataField
+ {
+ DataField_Type = Qt::UserRole + 1,
+ DataField_Name = Qt::UserRole + 2,
+ };
+
+ /** Constructs context-menu.
+ * @param pModel Brings model object reference. */
+ UIDetailsContextMenu(UIDetailsModel *pModel);
+
+ /** Updates category check-states. */
+ void updateCategoryStates();
+ /** Updates option check-states for certain @a enmRequiredCategoryType. */
+ void updateOptionStates(DetailsElementType enmRequiredCategoryType = DetailsElementType_Invalid);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles translation event for categories list. */
+ void retranslateCategories();
+ /** Handles translation event for options list. */
+ void retranslateOptions();
+
+private slots:
+
+ /** Handles signal about category list-widget @a pItem hovered. */
+ void sltCategoryItemEntered(QListWidgetItem *pItem);
+ /** Handles signal about category list-widget @a pItem clicked. */
+ void sltCategoryItemClicked(QListWidgetItem *pItem);
+ /** Handles signal about current category list-widget @a pItem hovered. */
+ void sltCategoryItemChanged(QListWidgetItem *pCurrent, QListWidgetItem *pPrevious);
+
+ /** Handles signal about option list-widget @a pItem hovered. */
+ void sltOptionItemEntered(QListWidgetItem *pItem);
+ /** Handles signal about option list-widget @a pItem clicked. */
+ void sltOptionItemClicked(QListWidgetItem *pItem);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** (Re)populates categories. */
+ void populateCategories();
+ /** (Re)populates options. */
+ void populateOptions();
+
+ /** Adjusts both list widgets. */
+ void adjustListWidgets();
+
+ /** Creates category list item with specified @a icon. */
+ QListWidgetItem *createCategoryItem(const QIcon &icon);
+ /** Creates option list item. */
+ QListWidgetItem *createOptionItem();
+
+ /** Holds the model reference. */
+ UIDetailsModel *m_pModel;
+
+ /** Holds the categories list instance. */
+ QListWidget *m_pListWidgetCategories;
+ /** Holds the options list instance. */
+ QListWidget *m_pListWidgetOptions;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIDetailsContextMenu_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElement.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElement.cpp
new file mode 100644
index 00000000..6c302320
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElement.cpp
@@ -0,0 +1,1571 @@
+/* $Id: UIDetailsElement.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsElement class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QActionGroup>
+#include <QClipboard>
+#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsView>
+#include <QPropertyAnimation>
+#include <QSignalTransition>
+#include <QStateMachine>
+#include <QStyleOptionGraphicsItem>
+
+/* GUI includes: */
+#include "QIDialogContainer.h"
+#include "UIActionPool.h"
+#include "UIAudioControllerEditor.h"
+#include "UIAudioHostDriverEditor.h"
+#include "UIBaseMemoryEditor.h"
+#include "UIBootOrderEditor.h"
+#include "UICloudMachineSettingsDialogPage.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIConverter.h"
+#include "UICursor.h"
+#include "UIDetailsElement.h"
+#include "UIDetailsGenerator.h"
+#include "UIDetailsSet.h"
+#include "UIDetailsModel.h"
+#include "UIExtraDataManager.h"
+#include "UIGraphicsControllerEditor.h"
+#include "UIGraphicsRotatorButton.h"
+#include "UIGraphicsTextPane.h"
+#include "UIIconPool.h"
+#include "UIMachineAttributeSetter.h"
+#include "UINameAndSystemEditor.h"
+#include "UINetworkAttachmentEditor.h"
+#include "UITaskCloudGetSettingsForm.h"
+#include "UIThreadPool.h"
+#include "UIVideoMemoryEditor.h"
+#include "UIVirtualBoxManager.h"
+#include "UIVisualStateEditor.h"
+
+
+/** Known anchor roles. */
+enum AnchorRole
+{
+ AnchorRole_Invalid,
+ AnchorRole_MachineName,
+ AnchorRole_MachineLocation,
+ AnchorRole_OSType,
+ AnchorRole_BaseMemory,
+ AnchorRole_BootOrder,
+ AnchorRole_VideoMemory,
+ AnchorRole_GraphicsControllerType,
+ AnchorRole_Storage,
+ AnchorRole_AudioHostDriverType,
+ AnchorRole_AudioControllerType,
+ AnchorRole_NetworkAttachmentType,
+ AnchorRole_USBControllerType,
+ AnchorRole_VisualStateType,
+#ifndef VBOX_WS_MAC
+ AnchorRole_MenuBar,
+#endif
+ AnchorRole_StatusBar,
+#ifndef VBOX_WS_MAC
+ AnchorRole_MiniToolbar,
+#endif
+ AnchorRole_Cloud,
+};
+
+
+UIDetailsElement::UIDetailsElement(UIDetailsSet *pParent, DetailsElementType enmType, bool fOpened)
+ : UIDetailsItem(pParent)
+ , m_pSet(pParent)
+ , m_enmType(enmType)
+ , m_iDefaultDarknessStart(100)
+ , m_iDefaultDarknessFinal(105)
+ , m_fHovered(false)
+ , m_fNameHovered(false)
+ , m_pHoveringMachine(0)
+ , m_pHoveringAnimationForward(0)
+ , m_pHoveringAnimationBackward(0)
+ , m_iAnimationDuration(300)
+ , m_iDefaultValue(0)
+ , m_iHoveredValue(100)
+ , m_iAnimatedValue(m_iDefaultValue)
+ , m_pButton(0)
+ , m_fClosed(!fOpened)
+ , m_fAnimationRunning(false)
+ , m_iAdditionalHeight(0)
+ , m_pTextPane(0)
+ , m_iMinimumHeaderWidth(0)
+ , m_iMinimumHeaderHeight(0)
+{
+ /* Prepare element: */
+ prepareElement();
+ /* Prepare button: */
+ prepareButton();
+ /* Prepare text-pane: */
+ prepareTextPane();
+
+ /* Setup size-policy: */
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ /* Add item to the parent: */
+ AssertMsg(parentItem(), ("No parent set for details element!"));
+ parentItem()->addItem(this);
+}
+
+UIDetailsElement::~UIDetailsElement()
+{
+ /* Remove item from the parent: */
+ AssertMsg(parentItem(), ("No parent set for details element!"));
+ parentItem()->removeItem(this);
+}
+
+void UIDetailsElement::setText(const UITextTable &text)
+{
+ /* Pass text to text-pane: */
+ m_pTextPane->setText(text);
+}
+
+UITextTable &UIDetailsElement::text() const
+{
+ /* Retrieve text from text-pane: */
+ return m_pTextPane->text();
+}
+
+void UIDetailsElement::close(bool fAnimated /* = true */)
+{
+ m_pButton->setToggled(false, fAnimated);
+}
+
+void UIDetailsElement::open(bool fAnimated /* = true */)
+{
+ m_pButton->setToggled(true, fAnimated);
+}
+
+void UIDetailsElement::markAnimationFinished()
+{
+ /* Mark animation as non-running: */
+ m_fAnimationRunning = false;
+
+ /* Recursively update size-hint: */
+ updateGeometry();
+ /* Repaint: */
+ update();
+}
+
+void UIDetailsElement::updateAppearance()
+{
+ /* Reset name hover state: */
+ m_fNameHovered = false;
+ updateNameHoverLink();
+
+ /* Update anchor role restrictions: */
+ const ConfigurationAccessLevel enmCal = m_pSet->configurationAccessLevel();
+ m_pTextPane->setAnchorRoleRestricted("#machine_name", enmCal != ConfigurationAccessLevel_Full
+ && enmCal != ConfigurationAccessLevel_Partial_Saved);
+ m_pTextPane->setAnchorRoleRestricted("#machine_location", enmCal != ConfigurationAccessLevel_Full);
+ m_pTextPane->setAnchorRoleRestricted("#os_type", enmCal != ConfigurationAccessLevel_Full);
+ m_pTextPane->setAnchorRoleRestricted("#base_memory", enmCal != ConfigurationAccessLevel_Full);
+ m_pTextPane->setAnchorRoleRestricted("#boot_order", enmCal != ConfigurationAccessLevel_Full);
+ m_pTextPane->setAnchorRoleRestricted("#video_memory", enmCal != ConfigurationAccessLevel_Full);
+ m_pTextPane->setAnchorRoleRestricted("#graphics_controller_type", enmCal != ConfigurationAccessLevel_Full);
+ m_pTextPane->setAnchorRoleRestricted("#mount", enmCal == ConfigurationAccessLevel_Null);
+ m_pTextPane->setAnchorRoleRestricted("#attach", enmCal != ConfigurationAccessLevel_Full);
+ m_pTextPane->setAnchorRoleRestricted("#audio_host_driver_type", enmCal != ConfigurationAccessLevel_Full
+ && enmCal != ConfigurationAccessLevel_Partial_Saved);
+ m_pTextPane->setAnchorRoleRestricted("#audio_controller_type", enmCal != ConfigurationAccessLevel_Full);
+ m_pTextPane->setAnchorRoleRestricted("#network_attachment_type", enmCal == ConfigurationAccessLevel_Null);
+ m_pTextPane->setAnchorRoleRestricted("#usb_controller_type", enmCal != ConfigurationAccessLevel_Full);
+ m_pTextPane->setAnchorRoleRestricted("#visual_state", enmCal == ConfigurationAccessLevel_Null);
+#ifndef VBOX_WS_MAC
+ m_pTextPane->setAnchorRoleRestricted("#menu_bar", enmCal == ConfigurationAccessLevel_Null);
+#endif
+ m_pTextPane->setAnchorRoleRestricted("#status_bar", enmCal == ConfigurationAccessLevel_Null);
+#ifndef VBOX_WS_MAC
+ m_pTextPane->setAnchorRoleRestricted("#mini_toolbar", enmCal == ConfigurationAccessLevel_Null);
+#endif
+}
+
+void UIDetailsElement::updateLayout()
+{
+ /* Prepare variables: */
+ QSize size = geometry().size().toSize();
+ int iMargin = data(ElementData_Margin).toInt();
+
+ /* Layout button: */
+ int iButtonWidth = m_buttonSize.width();
+ int iButtonHeight = m_buttonSize.height();
+ int iButtonX = size.width() - 2 * iMargin - iButtonWidth;
+ int iButtonY = iButtonHeight == m_iMinimumHeaderHeight ? iMargin :
+ iMargin + (m_iMinimumHeaderHeight - iButtonHeight) / 2;
+ m_pButton->setPos(iButtonX, iButtonY);
+
+ /* If closed or animation running => hide: */
+ if ((isClosed() || isAnimationRunning()) && m_pTextPane->isVisible())
+ m_pTextPane->hide();
+ /* If opened and animation isn't running => show: */
+ else if (!isClosed() && !isAnimationRunning() && !m_pTextPane->isVisible())
+ m_pTextPane->show();
+
+ /* Layout text-pane: */
+ int iTextPaneX = 2 * iMargin;
+ int iTextPaneY = iMargin + m_iMinimumHeaderHeight + 2 * iMargin;
+ m_pTextPane->setPos(iTextPaneX, iTextPaneY);
+ m_pTextPane->resize(size.width() - 4 * iMargin,
+ size.height() - 4 * iMargin - m_iMinimumHeaderHeight);
+}
+
+int UIDetailsElement::minimumWidthHint() const
+{
+ /* Prepare variables: */
+ int iMargin = data(ElementData_Margin).toInt();
+ int iMinimumWidthHint = 0;
+
+ /* Maximum width: */
+ iMinimumWidthHint = qMax(m_iMinimumHeaderWidth, (int)m_pTextPane->minimumSizeHint().width());
+
+ /* And 4 margins: 2 left and 2 right: */
+ iMinimumWidthHint += 4 * iMargin;
+
+ /* Return result: */
+ return iMinimumWidthHint;
+}
+
+int UIDetailsElement::minimumHeightHint() const
+{
+ return minimumHeightHintForElement(m_fClosed);
+}
+
+void UIDetailsElement::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ UIDetailsItem::showEvent(pEvent);
+
+ /* Update icon: */
+ updateIcon();
+}
+
+void UIDetailsElement::resizeEvent(QGraphicsSceneResizeEvent*)
+{
+ /* Update layout: */
+ updateLayout();
+}
+
+void UIDetailsElement::hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent)
+{
+ /* Update hover state: */
+ if (!m_fHovered)
+ {
+ m_fHovered = true;
+ emit sigHoverEnter();
+ }
+
+ /* Update name-hover state: */
+ handleHoverEvent(pEvent);
+}
+
+void UIDetailsElement::hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent)
+{
+ /* Update hover state: */
+ if (m_fHovered)
+ {
+ m_fHovered = false;
+ emit sigHoverLeave();
+ }
+
+ /* Update name-hover state: */
+ handleHoverEvent(pEvent);
+}
+
+void UIDetailsElement::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Only for hovered header: */
+ if (!m_fNameHovered)
+ return;
+
+ /* Process link click: */
+ pEvent->accept();
+ QString strCategory;
+ if (m_enmType >= DetailsElementType_General &&
+ m_enmType < DetailsElementType_Description)
+ strCategory = QString("#%1").arg(gpConverter->toInternalString(m_enmType));
+ else if (m_enmType == DetailsElementType_Description)
+ strCategory = QString("#%1%%m_pEditorDescription").arg(gpConverter->toInternalString(m_enmType));
+ emit sigLinkClicked(strCategory, QString(), machine().GetId());
+}
+
+void UIDetailsElement::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Only for left-button: */
+ if (pEvent->button() != Qt::LeftButton)
+ return;
+
+ /* Process left-button double-click: */
+ emit sigToggleElement(m_enmType, isClosed());
+}
+
+void UIDetailsElement::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *)
+{
+ /* Update button visibility: */
+ updateButtonVisibility();
+
+ /* Paint background: */
+ paintBackground(pPainter, pOptions);
+ /* Paint element info: */
+ paintElementInfo(pPainter, pOptions);
+}
+
+QString UIDetailsElement::description() const
+{
+ return tr("%1 details", "like 'General details' or 'Storage details'").arg(m_strName);
+}
+
+const CMachine &UIDetailsElement::machine()
+{
+ return m_pSet->machine();
+}
+
+const CCloudMachine &UIDetailsElement::cloudMachine()
+{
+ return m_pSet->cloudMachine();
+}
+
+bool UIDetailsElement::isLocal() const
+{
+ return m_pSet->isLocal();
+}
+
+void UIDetailsElement::setName(const QString &strName)
+{
+ /* Cache name: */
+ m_strName = strName;
+ QFontMetrics fm(m_nameFont, model()->paintDevice());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ m_nameSize = QSize(fm.horizontalAdvance(m_strName), fm.height());
+#else
+ m_nameSize = QSize(fm.width(m_strName), fm.height());
+#endif
+
+ /* Update linked values: */
+ updateMinimumHeaderWidth();
+ updateMinimumHeaderHeight();
+}
+
+void UIDetailsElement::setAdditionalHeight(int iAdditionalHeight)
+{
+ /* Cache new value: */
+ m_iAdditionalHeight = iAdditionalHeight;
+ /* Update layout: */
+ updateLayout();
+ /* Repaint: */
+ update();
+}
+
+QVariant UIDetailsElement::data(int iKey) const
+{
+ /* Provide other members with required data: */
+ switch (iKey)
+ {
+ /* Hints: */
+ case ElementData_Margin: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+ case ElementData_Spacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+ /* Default: */
+ default: break;
+ }
+ return QVariant();
+}
+
+void UIDetailsElement::addItem(UIDetailsItem*)
+{
+ AssertMsgFailed(("Details element do NOT support children!"));
+}
+
+void UIDetailsElement::removeItem(UIDetailsItem*)
+{
+ AssertMsgFailed(("Details element do NOT support children!"));
+}
+
+QList<UIDetailsItem*> UIDetailsElement::items(UIDetailsItemType) const
+{
+ AssertMsgFailed(("Details element do NOT support children!"));
+ return QList<UIDetailsItem*>();
+}
+
+bool UIDetailsElement::hasItems(UIDetailsItemType) const
+{
+ AssertMsgFailed(("Details element do NOT support children!"));
+ return false;
+}
+
+void UIDetailsElement::clearItems(UIDetailsItemType)
+{
+ AssertMsgFailed(("Details element do NOT support children!"));
+}
+
+int UIDetailsElement::minimumHeightHintForElement(bool fClosed) const
+{
+ /* Prepare variables: */
+ int iMargin = data(ElementData_Margin).toInt();
+ int iMinimumHeightHint = 0;
+
+ /* Two margins: */
+ iMinimumHeightHint += 2 * iMargin;
+
+ /* Header height: */
+ iMinimumHeightHint += m_iMinimumHeaderHeight;
+
+ /* Element is opened? */
+ if (!fClosed)
+ {
+ /* Add text height: */
+ if (!m_pTextPane->isEmpty())
+ iMinimumHeightHint += 2 * iMargin + (int)m_pTextPane->minimumSizeHint().height();
+ }
+
+ /* Additional height during animation: */
+ if (m_fAnimationRunning && isClosed())
+ iMinimumHeightHint += m_iAdditionalHeight;
+
+ /* Return value: */
+ return iMinimumHeightHint;
+}
+
+void UIDetailsElement::sltHandleWindowRemapped()
+{
+ /* Update icon: */
+ updateIcon();
+}
+
+void UIDetailsElement::sltToggleButtonClicked()
+{
+ emit sigToggleElement(m_enmType, isClosed());
+}
+
+void UIDetailsElement::sltElementToggleStart()
+{
+ /* Mark animation running: */
+ m_fAnimationRunning = true;
+
+ /* Setup animation: */
+ updateAnimationParameters();
+
+ /* Invert toggle-state instantly only for closed elements.
+ * Opened element being closed should remain opened
+ * until animation is fully finished. */
+ if (m_fClosed)
+ m_fClosed = !m_fClosed;
+}
+
+void UIDetailsElement::sltElementToggleFinish(bool fToggled)
+{
+ /* Update toggle-state: */
+ m_fClosed = !fToggled;
+
+ /* Notify about finishing: */
+ emit sigToggleElementFinished();
+}
+
+void UIDetailsElement::sltHandleAnchorClicked(const QString &strAnchor)
+{
+ /* Compose a map of known anchor roles: */
+ QMap<QString, AnchorRole> roles;
+ roles["#machine_name"] = AnchorRole_MachineName;
+ roles["#machine_location"] = AnchorRole_MachineLocation;
+ roles["#os_type"] = AnchorRole_OSType;
+ roles["#base_memory"] = AnchorRole_BaseMemory;
+ roles["#boot_order"] = AnchorRole_BootOrder;
+ roles["#video_memory"] = AnchorRole_VideoMemory;
+ roles["#graphics_controller_type"] = AnchorRole_GraphicsControllerType;
+ roles["#mount"] = AnchorRole_Storage;
+ roles["#attach"] = AnchorRole_Storage;
+ roles["#audio_host_driver_type"] = AnchorRole_AudioHostDriverType;
+ roles["#audio_controller_type"] = AnchorRole_AudioControllerType;
+ roles["#network_attachment_type"] = AnchorRole_NetworkAttachmentType;
+ roles["#usb_controller_type"] = AnchorRole_USBControllerType;
+ roles["#visual_state"] = AnchorRole_VisualStateType;
+#ifndef VBOX_WS_MAC
+ roles["#menu_bar"] = AnchorRole_MenuBar;
+#endif
+ roles["#status_bar"] = AnchorRole_StatusBar;
+#ifndef VBOX_WS_MAC
+ roles["#mini_toolbar"] = AnchorRole_MiniToolbar;
+#endif
+ roles["#cloud"] = AnchorRole_Cloud;
+
+ /* Current anchor role: */
+ const QString strRole = strAnchor.section(',', 0, 0);
+ const QString strData = strAnchor.section(',', 1);
+
+ /* Handle known anchor roles: */
+ const AnchorRole enmRole = roles.value(strRole, AnchorRole_Invalid);
+ switch (enmRole)
+ {
+ case AnchorRole_MachineName:
+ case AnchorRole_MachineLocation:
+ case AnchorRole_OSType:
+ {
+ popupNameAndSystemEditor(enmRole == AnchorRole_MachineName /* choose name? */,
+ enmRole == AnchorRole_MachineLocation /* choose path? */,
+ enmRole == AnchorRole_OSType /* choose type? */,
+ strData.section(',', 0, 0) /* value */);
+ break;
+ }
+ case AnchorRole_BaseMemory:
+ {
+ popupBaseMemoryEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+ case AnchorRole_BootOrder:
+ {
+ popupBootOrderEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+ case AnchorRole_VideoMemory:
+ {
+ popupVideoMemoryEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+ case AnchorRole_GraphicsControllerType:
+ {
+ popupGraphicsControllerTypeEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+ case AnchorRole_Storage:
+ {
+ popupStorageEditor(strData /* complex value */);
+ break;
+ }
+ case AnchorRole_AudioHostDriverType:
+ {
+ popupAudioHostDriverTypeEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+ case AnchorRole_AudioControllerType:
+ {
+ popupAudioControllerTypeEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+ case AnchorRole_NetworkAttachmentType:
+ {
+ popupNetworkAttachmentTypeEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+ case AnchorRole_USBControllerType:
+ {
+ popupUSBControllerTypeEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+ case AnchorRole_VisualStateType:
+ {
+ popupVisualStateTypeEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+#ifndef VBOX_WS_MAC
+ case AnchorRole_MenuBar:
+ {
+ popupMenuBarEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+#endif
+ case AnchorRole_StatusBar:
+ {
+ popupStatusBarEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+#ifndef VBOX_WS_MAC
+ case AnchorRole_MiniToolbar:
+ {
+ popupMiniToolbarEditor(strData.section(',', 0, 0) /* value */);
+ break;
+ }
+#endif
+ case AnchorRole_Cloud:
+ {
+ popupCloudEditor(strData /* complex value */);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void UIDetailsElement::sltHandleCopyRequest()
+{
+ /* Acquire sender: */
+ QObject *pSender = sender();
+ AssertPtrReturnVoid(pSender);
+
+ /* Acquire clipboard: */
+ QClipboard *pClipboard = QGuiApplication::clipboard();
+ AssertPtrReturnVoid(pClipboard);
+ pClipboard->setText(pSender->property("contents").toString());
+}
+
+void UIDetailsElement::sltHandleEditRequest()
+{
+ /* Acquire sender: */
+ QObject *pSender = sender();
+ AssertPtrReturnVoid(pSender);
+
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Acquire cloud machine: */
+ CCloudMachine comCloudMachine = cloudMachine();
+
+ /* Prepare editor: */
+ UISafePointerCloudMachineSettingsDialogPage pEditor = new UICloudMachineSettingsDialogPage(pPopup,
+ false /* full-scale? */);
+ if (pEditor)
+ {
+ /* Configure editor: */
+ connect(pEditor.data(), &UICloudMachineSettingsDialogPage::sigValidChanged,
+ pPopup.data(), &QIDialogContainer::setProgressBarHidden);
+ connect(pEditor.data(), &UICloudMachineSettingsDialogPage::sigValidChanged,
+ pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
+ pEditor->setFilter(pSender->property("filter").toString());
+ /* Create get settings form task: */
+ UITaskCloudGetSettingsForm *pTask = new UITaskCloudGetSettingsForm(comCloudMachine);
+ /* Create get settings form receiver: */
+ UIReceiverCloudGetSettingsForm *pReceiver = new UIReceiverCloudGetSettingsForm(pEditor);
+ if (pReceiver)
+ {
+ connect(pReceiver, &UIReceiverCloudGetSettingsForm::sigTaskComplete,
+ pEditor.data(), &UICloudMachineSettingsDialogPage::setForm);
+ connect(pReceiver, &UIReceiverCloudGetSettingsForm::sigTaskFailed,
+ pPopup.data(), &QIDialogContainer::close);
+ }
+ /* Start task: */
+ if (pTask && pReceiver)
+ uiCommon().threadPoolCloud()->enqueueTask(pTask);
+ /* Embed editor: */
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->resize(pPopup->minimumSizeHint());
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ {
+ /* Makes sure page data committed: */
+ if (pEditor)
+ pEditor->makeSureDataCommitted();
+
+ /* Apply form: */
+ CForm comForm = pEditor->form();
+ applyCloudMachineSettingsForm(comCloudMachine, comForm, gpNotificationCenter);
+ }
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+void UIDetailsElement::sltMountStorageMedium()
+{
+ /* Sender action: */
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertMsgReturnVoid(pAction, ("This slot should only be called by menu action!\n"));
+
+ /* Current mount-target: */
+ const UIMediumTarget target = pAction->data().value<UIMediumTarget>();
+
+ /* Update current machine mount-target: */
+ uiCommon().updateMachineStorage(machine(), target, gpManager->actionPool());
+}
+
+void UIDetailsElement::prepareElement()
+{
+ /* Initialization: */
+ m_nameFont = font();
+ m_nameFont.setWeight(QFont::Bold);
+ m_textFont = font();
+
+ /* Update icon: */
+ updateIcon();
+
+ /* Create hovering animation machine: */
+ m_pHoveringMachine = new QStateMachine(this);
+ if (m_pHoveringMachine)
+ {
+ /* Create 'default' state: */
+ QState *pStateDefault = new QState(m_pHoveringMachine);
+ /* Create 'hovered' state: */
+ QState *pStateHovered = new QState(m_pHoveringMachine);
+
+ /* Configure 'default' state: */
+ if (pStateDefault)
+ {
+ /* When we entering default state => we assigning animatedValue to m_iDefaultValue: */
+ pStateDefault->assignProperty(this, "animatedValue", m_iDefaultValue);
+
+ /* Add state transition: */
+ QSignalTransition *pDefaultToHovered = pStateDefault->addTransition(this, SIGNAL(sigHoverEnter()), pStateHovered);
+ if (pDefaultToHovered)
+ {
+ /* Create forward animation: */
+ m_pHoveringAnimationForward = new QPropertyAnimation(this, "animatedValue", this);
+ if (m_pHoveringAnimationForward)
+ {
+ m_pHoveringAnimationForward->setDuration(m_iAnimationDuration);
+ m_pHoveringAnimationForward->setStartValue(m_iDefaultValue);
+ m_pHoveringAnimationForward->setEndValue(m_iHoveredValue);
+
+ /* Add to transition: */
+ pDefaultToHovered->addAnimation(m_pHoveringAnimationForward);
+ }
+ }
+ }
+
+ /* Configure 'hovered' state: */
+ if (pStateHovered)
+ {
+ /* When we entering hovered state => we assigning animatedValue to m_iHoveredValue: */
+ pStateHovered->assignProperty(this, "animatedValue", m_iHoveredValue);
+
+ /* Add state transition: */
+ QSignalTransition *pHoveredToDefault = pStateHovered->addTransition(this, SIGNAL(sigHoverLeave()), pStateDefault);
+ if (pHoveredToDefault)
+ {
+ /* Create backward animation: */
+ m_pHoveringAnimationBackward = new QPropertyAnimation(this, "animatedValue", this);
+ if (m_pHoveringAnimationBackward)
+ {
+ m_pHoveringAnimationBackward->setDuration(m_iAnimationDuration);
+ m_pHoveringAnimationBackward->setStartValue(m_iHoveredValue);
+ m_pHoveringAnimationBackward->setEndValue(m_iDefaultValue);
+
+ /* Add to transition: */
+ pHoveredToDefault->addAnimation(m_pHoveringAnimationBackward);
+ }
+ }
+ }
+
+ /* Initial state is 'default': */
+ m_pHoveringMachine->setInitialState(pStateDefault);
+ /* Start state-machine: */
+ m_pHoveringMachine->start();
+ }
+
+ /* Configure connections: */
+ connect(gpManager, &UIVirtualBoxManager::sigWindowRemapped,
+ this, &UIDetailsElement::sltHandleWindowRemapped);
+ connect(this, &UIDetailsElement::sigToggleElement,
+ model(), &UIDetailsModel::sltToggleElements);
+ connect(this, &UIDetailsElement::sigLinkClicked,
+ model(), &UIDetailsModel::sigLinkClicked);
+}
+
+void UIDetailsElement::prepareButton()
+{
+ /* Setup toggle-button: */
+ m_pButton = new UIGraphicsRotatorButton(this, "additionalHeight", !m_fClosed, true /* reflected */);
+ m_pButton->setAutoHandleButtonClick(false);
+ connect(m_pButton, &UIGraphicsRotatorButton::sigButtonClicked, this, &UIDetailsElement::sltToggleButtonClicked);
+ connect(m_pButton, &UIGraphicsRotatorButton::sigRotationStart, this, &UIDetailsElement::sltElementToggleStart);
+ connect(m_pButton, &UIGraphicsRotatorButton::sigRotationFinish, this, &UIDetailsElement::sltElementToggleFinish);
+ m_buttonSize = m_pButton->minimumSizeHint().toSize();
+}
+
+void UIDetailsElement::prepareTextPane()
+{
+ /* Create text-pane: */
+ m_pTextPane = new UIGraphicsTextPane(this, model()->paintDevice());
+ connect(m_pTextPane, &UIGraphicsTextPane::sigGeometryChanged, this, &UIDetailsElement::sltUpdateGeometry);
+ connect(m_pTextPane, &UIGraphicsTextPane::sigAnchorClicked, this, &UIDetailsElement::sltHandleAnchorClicked);
+}
+
+void UIDetailsElement::updateIcon()
+{
+ /* Prepare whole icon first of all: */
+ const QIcon icon = gpConverter->toIcon(elementType());
+
+ /* Cache icon: */
+ if (icon.isNull())
+ {
+ /* No icon provided: */
+ m_pixmapSize = QSize();
+ m_pixmap = QPixmap();
+ }
+ else
+ {
+ /* Determine default icon size: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pixmapSize = QSize(iIconMetric, iIconMetric);
+ /* Acquire the icon of corresponding size (taking top-level widget DPI into account): */
+ m_pixmap = icon.pixmap(gpManager->windowHandle(), m_pixmapSize);
+ }
+
+ /* Update linked values: */
+ updateMinimumHeaderWidth();
+ updateMinimumHeaderHeight();
+}
+
+void UIDetailsElement::handleHoverEvent(QGraphicsSceneHoverEvent *pEvent)
+{
+ /* Not for 'preview' element type: */
+ if (m_enmType == DetailsElementType_Preview)
+ return;
+
+ /* Prepare variables: */
+ int iMargin = data(ElementData_Margin).toInt();
+ int iSpacing = data(ElementData_Spacing).toInt();
+ int iNameHeight = m_nameSize.height();
+ int iElementNameX = 2 * iMargin + m_pixmapSize.width() + iSpacing;
+ int iElementNameY = iNameHeight == m_iMinimumHeaderHeight ?
+ iMargin : iMargin + (m_iMinimumHeaderHeight - iNameHeight) / 2;
+
+ /* Simulate hyperlink hovering: */
+ QPoint point = pEvent->pos().toPoint();
+ bool fNameHovered = QRect(QPoint(iElementNameX, iElementNameY), m_nameSize).contains(point);
+ if ( m_pSet->configurationAccessLevel() != ConfigurationAccessLevel_Null
+ && m_fNameHovered != fNameHovered)
+ {
+ m_fNameHovered = fNameHovered;
+ updateNameHoverLink();
+ }
+}
+
+void UIDetailsElement::updateNameHoverLink()
+{
+ if (m_fNameHovered)
+ UICursor::setCursor(this, Qt::PointingHandCursor);
+ else
+ UICursor::unsetCursor(this);
+ update();
+}
+
+void UIDetailsElement::updateAnimationParameters()
+{
+ /* Recalculate animation parameters: */
+ int iOpenedHeight = minimumHeightHintForElement(false);
+ int iClosedHeight = minimumHeightHintForElement(true);
+ int iAdditionalHeight = iOpenedHeight - iClosedHeight;
+ if (m_fClosed)
+ m_iAdditionalHeight = 0;
+ else
+ m_iAdditionalHeight = iAdditionalHeight;
+ m_pButton->setAnimationRange(0, iAdditionalHeight);
+}
+
+void UIDetailsElement::updateButtonVisibility()
+{
+ if (m_fHovered && !m_pButton->isVisible())
+ m_pButton->show();
+ else if (!m_fHovered && m_pButton->isVisible())
+ m_pButton->hide();
+}
+
+void UIDetailsElement::popupNameAndSystemEditor(bool fChooseName, bool fChoosePath, bool fChooseType, const QString &strValue)
+{
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Prepare editor: */
+ UINameAndSystemEditor *pEditor = new UINameAndSystemEditor(pPopup,
+ fChooseName,
+ fChoosePath,
+ false /* edition? */,
+ false /* image? */,
+ fChooseType);
+ if (pEditor)
+ {
+ if (fChooseName)
+ pEditor->setName(strValue);
+ else if (fChoosePath)
+ pEditor->setPath(strValue);
+ else if (fChooseType)
+ pEditor->setTypeId(strValue);
+
+ /* Add to popup: */
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->adjustSize();
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ {
+ if (fChooseName)
+ setMachineAttribute(machine(), MachineAttribute_Name, QVariant::fromValue(pEditor->name()));
+ else if (fChooseType)
+ setMachineAttribute(machine(), MachineAttribute_OSType, QVariant::fromValue(pEditor->typeId()));
+ else if (fChoosePath)
+ setMachineLocation(machine().GetId(), pEditor->path());
+ }
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+void UIDetailsElement::popupBaseMemoryEditor(const QString &strValue)
+{
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Prepare editor: */
+ UIBaseMemoryEditor *pEditor = new UIBaseMemoryEditor(pPopup);
+ if (pEditor)
+ {
+ pEditor->setValue(strValue.toInt());
+ connect(pEditor, &UIBaseMemoryEditor::sigValidChanged,
+ pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->adjustSize();
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ setMachineAttribute(machine(), MachineAttribute_BaseMemory, QVariant::fromValue(pEditor->value()));
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+void UIDetailsElement::popupBootOrderEditor(const QString &strValue)
+{
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Prepare editor: */
+ UIBootOrderEditor *pEditor = new UIBootOrderEditor(pPopup);
+ if (pEditor)
+ {
+ pEditor->setValue(bootItemsFromSerializedString(strValue));
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->adjustSize();
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ setMachineAttribute(machine(), MachineAttribute_BootOrder, QVariant::fromValue(pEditor->value()));
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+void UIDetailsElement::popupVideoMemoryEditor(const QString &strValue)
+{
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Prepare editor: */
+ UIVideoMemoryEditor *pEditor = new UIVideoMemoryEditor(pPopup);
+ if (pEditor)
+ {
+ pEditor->setValue(strValue.toInt());
+ connect(pEditor, &UIVideoMemoryEditor::sigValidChanged,
+ pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->adjustSize();
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ setMachineAttribute(machine(), MachineAttribute_VideoMemory, QVariant::fromValue(pEditor->value()));
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+void UIDetailsElement::popupGraphicsControllerTypeEditor(const QString &strValue)
+{
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Prepare editor: */
+ UIGraphicsControllerEditor *pEditor = new UIGraphicsControllerEditor(pPopup);
+ if (pEditor)
+ {
+ pEditor->setValue(static_cast<KGraphicsControllerType>(strValue.toInt()));
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->adjustSize();
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ setMachineAttribute(machine(), MachineAttribute_GraphicsControllerType, QVariant::fromValue(pEditor->value()));
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+void UIDetailsElement::popupStorageEditor(const QString &strValue)
+{
+ /* Prepare storage-menu: */
+ UIMenu menu;
+ menu.setShowToolTip(true);
+
+ /* Storage-controller name: */
+ QString strControllerName = strValue.section(',', 0, 0);
+ /* Storage-slot: */
+ StorageSlot storageSlot = gpConverter->fromString<StorageSlot>(strValue.section(',', 1));
+
+ /* Fill storage-menu: */
+ uiCommon().prepareStorageMenu(menu, this, SLOT(sltMountStorageMedium()),
+ machine(), strControllerName, storageSlot);
+
+ /* Exec menu: */
+ menu.exec(QCursor::pos());
+}
+
+void UIDetailsElement::popupAudioHostDriverTypeEditor(const QString &strValue)
+{
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Prepare editor: */
+ UIAudioHostDriverEditor *pEditor = new UIAudioHostDriverEditor(pPopup);
+ if (pEditor)
+ {
+ pEditor->setValue(static_cast<KAudioDriverType>(strValue.toInt()));
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->adjustSize();
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ setMachineAttribute(machine(), MachineAttribute_AudioHostDriverType, QVariant::fromValue(pEditor->value()));
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+void UIDetailsElement::popupAudioControllerTypeEditor(const QString &strValue)
+{
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Prepare editor: */
+ UIAudioControllerEditor *pEditor = new UIAudioControllerEditor(pPopup);
+ if (pEditor)
+ {
+ pEditor->setValue(static_cast<KAudioControllerType>(strValue.toInt()));
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->adjustSize();
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ setMachineAttribute(machine(), MachineAttribute_AudioControllerType, QVariant::fromValue(pEditor->value()));
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+void UIDetailsElement::popupNetworkAttachmentTypeEditor(const QString &strValue)
+{
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Prepare editor: */
+ UINetworkAttachmentEditor *pEditor = new UINetworkAttachmentEditor(pPopup);
+ if (pEditor)
+ {
+ pEditor->setValueNames(KNetworkAttachmentType_Bridged, UINetworkAttachmentEditor::bridgedAdapters());
+ pEditor->setValueNames(KNetworkAttachmentType_Internal, UINetworkAttachmentEditor::internalNetworks());
+ pEditor->setValueNames(KNetworkAttachmentType_HostOnly, UINetworkAttachmentEditor::hostInterfaces());
+ pEditor->setValueNames(KNetworkAttachmentType_Generic, UINetworkAttachmentEditor::genericDrivers());
+ pEditor->setValueNames(KNetworkAttachmentType_NATNetwork, UINetworkAttachmentEditor::natNetworks());
+ pEditor->setValueType(static_cast<KNetworkAttachmentType>(strValue.section(';', 1, 1).toInt()));
+ pEditor->setValueName(pEditor->valueType(), strValue.section(';', 2, 2));
+ connect(pEditor, &UINetworkAttachmentEditor::sigValidChanged,
+ pPopup.data(), &QIDialogContainer::setOkButtonEnabled);
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->adjustSize();
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ {
+ UINetworkAdapterDescriptor nad(strValue.section(';', 0, 0).toInt(),
+ pEditor->valueType(), pEditor->valueName(pEditor->valueType()));
+ setMachineAttribute(machine(), MachineAttribute_NetworkAttachmentType, QVariant::fromValue(nad));
+ }
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+void UIDetailsElement::popupUSBControllerTypeEditor(const QString &strValue)
+{
+ /* Parse controller type list: */
+ UIUSBControllerTypeSet controllerSet;
+ const QStringList controllerInternals = strValue.split(';');
+ foreach (const QString &strControllerType, controllerInternals)
+ {
+ /* Parse each internal controller description: */
+ bool fParsed = false;
+ KUSBControllerType enmType = static_cast<KUSBControllerType>(strControllerType.toInt(&fParsed));
+ if (!fParsed)
+ enmType = KUSBControllerType_Null;
+ controllerSet << enmType;
+ }
+
+ /* Prepare existing controller sets: */
+ QMap<int, UIUSBControllerTypeSet> controllerSets;
+ controllerSets[0] = UIUSBControllerTypeSet();
+ controllerSets[1] = UIUSBControllerTypeSet() << KUSBControllerType_OHCI;
+ controllerSets[2] = UIUSBControllerTypeSet() << KUSBControllerType_OHCI << KUSBControllerType_EHCI;
+ controllerSets[3] = UIUSBControllerTypeSet() << KUSBControllerType_XHCI;
+
+ /* Fill menu with actions: */
+ UIMenu menu;
+ QActionGroup group(&menu);
+ QMap<int, QAction*> actions;
+ actions[0] = menu.addAction(QApplication::translate("UIDetails", "Disabled", "details (usb)"));
+ group.addAction(actions.value(0));
+ actions.value(0)->setCheckable(true);
+ actions[1] = menu.addAction(QApplication::translate("UIDetails", "USB 1.1 (OHCI) Controller", "details (usb)"));
+ group.addAction(actions.value(1));
+ actions.value(1)->setCheckable(true);
+ actions[2] = menu.addAction(QApplication::translate("UIDetails", "USB 2.0 (OHCI + EHCI) Controller", "details (usb)"));
+ group.addAction(actions.value(2));
+ actions.value(2)->setCheckable(true);
+ actions[3] = menu.addAction(QApplication::translate("UIDetails", "USB 3.0 (xHCI) Controller", "details (usb)"));
+ group.addAction(actions.value(3));
+ actions.value(3)->setCheckable(true);
+
+ /* Mark current one: */
+ for (int i = 0; i <= 3; ++i)
+ actions.value(i)->setChecked(controllerSets.key(controllerSet) == i);
+
+ /* Execute menu, look for result: */
+ QAction *pTriggeredAction = menu.exec(QCursor::pos());
+ if (pTriggeredAction)
+ {
+ const int iTriggeredIndex = actions.key(pTriggeredAction);
+ if (controllerSets.key(controllerSet) != iTriggeredIndex)
+ setMachineAttribute(machine(), MachineAttribute_USBControllerType, QVariant::fromValue(controllerSets.value(iTriggeredIndex)));
+ }
+}
+
+void UIDetailsElement::popupVisualStateTypeEditor(const QString &strValue)
+{
+ /* Prepare popup: */
+ QPointer<QIDialogContainer> pPopup = new QIDialogContainer(0, Qt::Tool);
+ if (pPopup)
+ {
+ /* Prepare editor: */
+ UIVisualStateEditor *pEditor = new UIVisualStateEditor(pPopup);
+ if (pEditor)
+ {
+ pEditor->setMachineId(machine().GetId());
+ pEditor->setValue(static_cast<UIVisualStateType>(strValue.toInt()));
+ pPopup->setWidget(pEditor);
+ }
+
+ /* Adjust popup geometry: */
+ pPopup->move(QCursor::pos());
+ pPopup->adjustSize();
+
+ // WORKAROUND:
+ // On Windows, Tool dialogs aren't activated by default by some reason.
+ // So we have created sltActivateWindow wrapping actual activateWindow
+ // to fix that annoying issue.
+ QMetaObject::invokeMethod(pPopup, "sltActivateWindow", Qt::QueuedConnection);
+ /* Execute popup, change machine name if confirmed: */
+ if (pPopup->exec() == QDialog::Accepted)
+ gEDataManager->setRequestedVisualState(pEditor->value(), machine().GetId());
+
+ /* Delete popup: */
+ delete pPopup;
+ }
+}
+
+#ifndef VBOX_WS_MAC
+void UIDetailsElement::popupMenuBarEditor(const QString &strValue)
+{
+ /* Parse whether we have it enabled, true if unable to parse: */
+ bool fParsed = false;
+ bool fEnabled = strValue.toInt(&fParsed);
+ if (!fParsed)
+ fEnabled = true;
+
+ /* Fill menu with actions: */
+ UIMenu menu;
+ QActionGroup group(&menu);
+ QAction *pActionDisable = menu.addAction(QApplication::translate("UIDetails", "Disabled", "details (user interface/menu-bar)"));
+ group.addAction(pActionDisable);
+ pActionDisable->setCheckable(true);
+ pActionDisable->setChecked(!fEnabled);
+ QAction *pActionEnable = menu.addAction(QApplication::translate("UIDetails", "Enabled", "details (user interface/menu-bar)"));
+ group.addAction(pActionEnable);
+ pActionEnable->setCheckable(true);
+ pActionEnable->setChecked(fEnabled);
+
+ /* Execute menu, look for result: */
+ QAction *pTriggeredAction = menu.exec(QCursor::pos());
+ if ( pTriggeredAction
+ && ( (fEnabled && pTriggeredAction == pActionDisable)
+ || (!fEnabled && pTriggeredAction == pActionEnable)))
+ {
+ gEDataManager->setMenuBarEnabled(!fEnabled, machine().GetId());
+ }
+}
+#endif
+
+void UIDetailsElement::popupStatusBarEditor(const QString &strValue)
+{
+ /* Parse whether we have it enabled, true if unable to parse: */
+ bool fParsed = false;
+ bool fEnabled = strValue.toInt(&fParsed);
+ if (!fParsed)
+ fEnabled = true;
+
+ /* Fill menu with actions: */
+ UIMenu menu;
+ QActionGroup group(&menu);
+ QAction *pActionDisable = menu.addAction(QApplication::translate("UIDetails", "Disabled", "details (user interface/status-bar)"));
+ group.addAction(pActionDisable);
+ pActionDisable->setCheckable(true);
+ pActionDisable->setChecked(!fEnabled);
+ QAction *pActionEnable = menu.addAction(QApplication::translate("UIDetails", "Enabled", "details (user interface/status-bar)"));
+ group.addAction(pActionEnable);
+ pActionEnable->setCheckable(true);
+ pActionEnable->setChecked(fEnabled);
+
+ /* Execute menu, look for result: */
+ QAction *pTriggeredAction = menu.exec(QCursor::pos());
+ if ( pTriggeredAction
+ && ( (fEnabled && pTriggeredAction == pActionDisable)
+ || (!fEnabled && pTriggeredAction == pActionEnable)))
+ {
+ gEDataManager->setStatusBarEnabled(!fEnabled, machine().GetId());
+ }
+}
+
+#ifndef VBOX_WS_MAC
+void UIDetailsElement::popupMiniToolbarEditor(const QString &strValue)
+{
+ /* Parse whether we have it enabled: */
+ bool fParsed = false;
+ MiniToolbarAlignment enmAlignment = static_cast<MiniToolbarAlignment>(strValue.toInt(&fParsed));
+ if (!fParsed)
+ enmAlignment = MiniToolbarAlignment_Disabled;
+
+ /* Fill menu with actions: */
+ UIMenu menu;
+ QActionGroup group(&menu);
+ QAction *pActionDisabled = menu.addAction(QApplication::translate("UIDetails", "Disabled", "details (user interface/mini-toolbar)"));
+ group.addAction(pActionDisabled);
+ pActionDisabled->setCheckable(true);
+ pActionDisabled->setChecked(enmAlignment == MiniToolbarAlignment_Disabled);
+ QAction *pActionTop = menu.addAction(QApplication::translate("UIDetails", "Top", "details (user interface/mini-toolbar position)"));
+ group.addAction(pActionTop);
+ pActionTop->setCheckable(true);
+ pActionTop->setChecked(enmAlignment == MiniToolbarAlignment_Top);
+ QAction *pActionBottom = menu.addAction(QApplication::translate("UIDetails", "Bottom", "details (user interface/mini-toolbar position)"));
+ group.addAction(pActionBottom);
+ pActionBottom->setCheckable(true);
+ pActionBottom->setChecked(enmAlignment == MiniToolbarAlignment_Bottom);
+
+ /* Execute menu, look for result: */
+ QAction *pTriggeredAction = menu.exec(QCursor::pos());
+ if (pTriggeredAction)
+ {
+ const QUuid uMachineId = machine().GetId();
+ if (pTriggeredAction == pActionDisabled)
+ gEDataManager->setMiniToolbarEnabled(false, uMachineId);
+ else if (pTriggeredAction == pActionTop)
+ {
+ gEDataManager->setMiniToolbarEnabled(true, uMachineId);
+ gEDataManager->setMiniToolbarAlignment(Qt::AlignTop, uMachineId);
+ }
+ else if (pTriggeredAction == pActionBottom)
+ {
+ gEDataManager->setMiniToolbarEnabled(true, uMachineId);
+ gEDataManager->setMiniToolbarAlignment(Qt::AlignBottom, uMachineId);
+ }
+ }
+}
+#endif
+
+void UIDetailsElement::popupCloudEditor(const QString &strValue)
+{
+ /* Prepare cloud-menu: */
+ UIMenu menu;
+ menu.setShowToolTip(true);
+
+ /* Acquire cloud machine: */
+ CCloudMachine comCloudMachine = cloudMachine();
+ /* Acquire details form: */
+ CForm comForm = comCloudMachine.GetDetailsForm();
+ /* Ignore cloud machine errors: */
+ if (comCloudMachine.isOk())
+ {
+ /* For each form value: */
+ foreach (const CFormValue &comIteratedValue, comForm.GetValues())
+ {
+ /* Acquire label: */
+ const QString &strIteratedLabel = comIteratedValue.GetLabel();
+ if (strIteratedLabel != strValue)
+ continue;
+
+ /* Acquire resulting value in short and full form: */
+ const QString strIteratedResultShort = UIDetailsGenerator::generateFormValueInformation(comIteratedValue);
+ const QString strIteratedResultFull = UIDetailsGenerator::generateFormValueInformation(comIteratedValue, true /* full */);
+
+ /* Add 'Copy' action: */
+ QAction *pAction = menu.addAction(tr("Copy value (%1)").arg(strIteratedResultShort),
+ this, &UIDetailsElement::sltHandleCopyRequest);
+ if (pAction)
+ {
+ pAction->setToolTip(strIteratedResultFull);
+ pAction->setProperty("contents", strIteratedResultFull);
+ }
+
+ /* Add 'Edit' action: */
+ if (comIteratedValue.GetEnabled())
+ {
+ QAction *pAction = menu.addAction(tr("Edit value..."),
+ this, &UIDetailsElement::sltHandleEditRequest);
+ if (pAction)
+ pAction->setProperty("filter", strIteratedLabel);
+ }
+
+ /* Quit prematurely: */
+ break;
+ }
+ }
+
+ /* Exec menu: */
+ menu.exec(QCursor::pos());
+}
+
+void UIDetailsElement::updateMinimumHeaderWidth()
+{
+ /* Prepare variables: */
+ int iSpacing = data(ElementData_Spacing).toInt();
+
+ /* Update minimum-header-width: */
+ m_iMinimumHeaderWidth = m_pixmapSize.width() +
+ iSpacing + m_nameSize.width() +
+ iSpacing + m_buttonSize.width();
+}
+
+void UIDetailsElement::updateMinimumHeaderHeight()
+{
+ /* Update minimum-header-height: */
+ m_iMinimumHeaderHeight = qMax(m_pixmapSize.height(), m_nameSize.height());
+ m_iMinimumHeaderHeight = qMax(m_iMinimumHeaderHeight, m_buttonSize.height());
+}
+
+void UIDetailsElement::paintBackground(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions) const
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare variables: */
+ const int iMargin = data(ElementData_Margin).toInt();
+ const int iHeadHeight = 2 * iMargin + m_iMinimumHeaderHeight;
+ const QRect optionRect = pOptions->rect;
+ const QRect headRect = QRect(optionRect.topLeft(), QSize(optionRect.width(), iHeadHeight));
+ const QRect fullRect = m_fAnimationRunning
+ ? QRect(optionRect.topLeft(), QSize(optionRect.width(), iHeadHeight + m_iAdditionalHeight))
+ : optionRect;
+
+ /* Acquire background color: */
+ QColor backgroundColor = QApplication::palette().color(QPalette::Active, QPalette::Window);
+
+ /* Paint default background: */
+ QLinearGradient gradientDefault(fullRect.topLeft(), fullRect.bottomRight());
+ gradientDefault.setColorAt(0, backgroundColor.darker(m_iDefaultDarknessStart));
+ gradientDefault.setColorAt(1, backgroundColor.darker(m_iDefaultDarknessFinal));
+ pPainter->fillRect(fullRect, gradientDefault);
+
+ /* If element is hovered: */
+ if (animatedValue())
+ {
+ /* Acquire header color: */
+ QColor headColor = backgroundColor.lighter(130);
+
+ /* Paint hovered background: */
+ QColor hcTone1 = headColor;
+ QColor hcTone2 = headColor;
+ hcTone1.setAlpha(255 * animatedValue() / 100);
+ hcTone2.setAlpha(0);
+ QLinearGradient gradientHovered(headRect.topLeft(), headRect.bottomLeft());
+ gradientHovered.setColorAt(0, hcTone1);
+ gradientHovered.setColorAt(1, hcTone2);
+ pPainter->fillRect(headRect, gradientHovered);
+ }
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIDetailsElement::paintElementInfo(QPainter *pPainter, const QStyleOptionGraphicsItem *) const
+{
+ /* Initialize some necessary variables: */
+ const int iMargin = data(ElementData_Margin).toInt();
+ const int iSpacing = data(ElementData_Spacing).toInt();
+
+ /* Calculate attributes: */
+ const int iPixmapHeight = m_pixmapSize.height();
+ const int iNameHeight = m_nameSize.height();
+ const int iMaximumHeight = qMax(iPixmapHeight, iNameHeight);
+
+ /* Prepare color: */
+ const QPalette pal = QApplication::palette();
+ const QColor buttonTextColor = pal.color(QPalette::Active, QPalette::Text);
+ const QColor linkTextColor = pal.color(QPalette::Active, QPalette::Link);
+
+ /* Paint pixmap: */
+ int iElementPixmapX = 2 * iMargin;
+ int iElementPixmapY = iPixmapHeight == iMaximumHeight ?
+ iMargin : iMargin + (iMaximumHeight - iPixmapHeight) / 2;
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Rectangle to paint in: */
+ QRect(QPoint(iElementPixmapX, iElementPixmapY), m_pixmapSize),
+ /* Pixmap to paint: */
+ m_pixmap);
+
+ /* Paint name: */
+ int iMachineNameX = iElementPixmapX +
+ m_pixmapSize.width() +
+ iSpacing;
+ int iMachineNameY = iNameHeight == iMaximumHeight ?
+ iMargin : iMargin + (iMaximumHeight - iNameHeight) / 2;
+ paintText(/* Painter: */
+ pPainter,
+ /* Rectangle to paint in: */
+ QPoint(iMachineNameX, iMachineNameY),
+ /* Font to paint text: */
+ m_nameFont,
+ /* Paint device: */
+ model()->paintDevice(),
+ /* Text to paint: */
+ m_strName,
+ /* Name hovered? */
+ m_fNameHovered ? linkTextColor : buttonTextColor);
+}
+
+/* static */
+void UIDetailsElement::paintPixmap(QPainter *pPainter, const QRect &rect, const QPixmap &pixmap)
+{
+ pPainter->drawPixmap(rect, pixmap);
+}
+
+/* static */
+void UIDetailsElement::paintText(QPainter *pPainter, QPoint point,
+ const QFont &font, QPaintDevice *pPaintDevice,
+ const QString &strText, const QColor &color)
+{
+ /* Prepare variables: */
+ QFontMetrics fm(font, pPaintDevice);
+ point += QPoint(0, fm.ascent());
+
+ /* Draw text: */
+ pPainter->save();
+ pPainter->setFont(font);
+ pPainter->setPen(color);
+ pPainter->drawText(point, strText);
+ pPainter->restore();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElement.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElement.h
new file mode 100644
index 00000000..0c5230d2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElement.h
@@ -0,0 +1,416 @@
+/* $Id: UIDetailsElement.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsElement class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIDetailsElement_h
+#define FEQT_INCLUDED_SRC_manager_details_UIDetailsElement_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+
+/* GUI includes: */
+#include "UIDetailsItem.h"
+#include "UIExtraDataDefs.h"
+#include "UITextTable.h"
+
+/* Forward declarations: */
+class QPropertyAnimation;
+class QStateMachine;
+class QTextLayout;
+class UIDetailsSet;
+class UIGraphicsRotatorButton;
+class UIGraphicsTextPane;
+class CCloudMachine;
+class CMachine;
+
+
+/** UIDetailsItem extension implementing element item. */
+class UIDetailsElement : public UIDetailsItem
+{
+ Q_OBJECT;
+ Q_PROPERTY(int animatedValue READ animatedValue WRITE setAnimatedValue);
+ Q_PROPERTY(int additionalHeight READ additionalHeight WRITE setAdditionalHeight);
+
+signals:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Notifies about hover enter. */
+ void sigHoverEnter();
+ /** Notifies about hover leave. */
+ void sigHoverLeave();
+
+ /** Notifies about @a enmType element @a fToggled. */
+ void sigToggleElement(DetailsElementType enmType, bool fToggled);
+ /** Notifies about element toggle finished. */
+ void sigToggleElementFinished();
+
+ /** Notifies about element link clicked.
+ * @param strCategory Brings the link category.
+ * @param strControl Brings the wanted settings control.
+ * @param uId Brings the ID. */
+ void sigLinkClicked(const QString &strCategory, const QString &strControl, const QUuid &uId);
+ /** @} */
+
+public:
+
+ /** RTTI item type. */
+ enum { Type = UIDetailsItemType_Element };
+
+ /** Constructs element item, passing pParent to the base-class.
+ * @param enmType Brings element type.
+ * @param fOpened Brings whether element is opened. */
+ UIDetailsElement(UIDetailsSet *pParent, DetailsElementType enmType, bool fOpened);
+ /** Destructs element item. */
+ virtual ~UIDetailsElement() RT_OVERRIDE;
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns element type. */
+ DetailsElementType elementType() const { return m_enmType; }
+
+ /** Defines the @a text table as the passed one. */
+ void setText(const UITextTable &text);
+ /** Returns the reference to the text table. */
+ UITextTable &text() const;
+
+ /** Closes group in @a fAnimated way if requested. */
+ void close(bool fAnimated = true);
+ /** Returns whether group is closed. */
+ bool isClosed() const { return m_fClosed; }
+
+ /** Opens group in @a fAnimated way if requested. */
+ void open(bool fAnimated = true);
+ /** Returns whether group is opened. */
+ bool isOpened() const { return !m_fClosed; }
+
+ /** Returns whether toggle animation is running. */
+ bool isAnimationRunning() const { return m_fAnimationRunning; }
+ /** Marks animation finished. */
+ void markAnimationFinished();
+
+ /** Updates element appearance. */
+ virtual void updateAppearance();
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates layout. */
+ virtual void updateLayout() RT_OVERRIDE;
+
+ /** Returns minimum width-hint. */
+ virtual int minimumWidthHint() const RT_OVERRIDE;
+ /** Returns minimum height-hint. */
+ virtual int minimumHeightHint() const RT_OVERRIDE;
+ /** @} */
+
+protected:
+
+ /** Data field types. */
+ enum ElementData
+ {
+ /* Hints: */
+ ElementData_Margin,
+ ElementData_Spacing
+ };
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** This event handler is delivered after the widget has been resized. */
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles hover enter @a event. */
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+ /** Handles hover leave @a event. */
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse press @a event. */
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse double-click @a event. */
+ virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns RTTI item type. */
+ virtual int type() const RT_OVERRIDE { return Type; }
+
+ /** Returns the description of the item. */
+ virtual QString description() const RT_OVERRIDE;
+
+ /** Returns cached machine reference. */
+ const CMachine &machine();
+ /** Returns cached cloud machine reference. */
+ const CCloudMachine &cloudMachine();
+
+ /** Returns whether element is of local type. */
+ bool isLocal() const;
+
+ /** Defines element @a strName. */
+ void setName(const QString &strName);
+
+ /** Defines @a iAdditionalHeight during toggle animation. */
+ void setAdditionalHeight(int iAdditionalHeight);
+ /** Returns additional height during toggle animation. */
+ int additionalHeight() const { return m_iAdditionalHeight; }
+ /** Returns toggle button instance. */
+ UIGraphicsRotatorButton *button() const { return m_pButton; }
+
+ /** Returns abstractly stored data value for certain @a iKey. */
+ QVariant data(int iKey) const;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Adds child @a pItem. */
+ virtual void addItem(UIDetailsItem *pItem) RT_OVERRIDE;
+ /** Removes child @a pItem. */
+ virtual void removeItem(UIDetailsItem *pItem) RT_OVERRIDE;
+
+ /** Returns children items of certain @a enmType. */
+ virtual QList<UIDetailsItem*> items(UIDetailsItemType enmType) const RT_OVERRIDE;
+ /** Returns whether there are children items of certain @a enmType. */
+ virtual bool hasItems(UIDetailsItemType enmType) const RT_OVERRIDE;
+ /** Clears children items of certain @a enmType. */
+ virtual void clearItems(UIDetailsItemType enmType) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Returns minimum width-hint for @a fClosed element. */
+ virtual int minimumHeightHintForElement(bool fClosed) const;
+
+ /** Returns minimum header width. */
+ int minimumHeaderWidth() const { return m_iMinimumHeaderWidth; }
+ /** Returns minimum header height. */
+ int minimumHeaderHeight() const { return m_iMinimumHeaderHeight; }
+ /** @} */
+
+private slots:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles top-level window remaps. */
+ void sltHandleWindowRemapped();
+
+ /** Handles toggle button click. */
+ void sltToggleButtonClicked();
+ /** Handles toggle start. */
+ void sltElementToggleStart();
+ /** Handles toggle finish. */
+ void sltElementToggleFinish(bool fToggled);
+
+ /** Handles child anchor clicks. */
+ void sltHandleAnchorClicked(const QString &strAnchor);
+ /** Handles child copy request. */
+ void sltHandleCopyRequest();
+ /** Handles child edit request. */
+ void sltHandleEditRequest();
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Handles children geometry changes. */
+ void sltUpdateGeometry() { updateGeometry(); }
+ /** @} */
+
+ /** @name Move to sub-class.
+ * @{ */
+ /** Handles mount storage medium requests. */
+ void sltMountStorageMedium();
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares element. */
+ void prepareElement();
+ /** Prepares toggle button. */
+ void prepareButton();
+ /** Prepares text pane. */
+ void prepareTextPane();
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Updates icon. */
+ void updateIcon();
+
+ /** Defines animated @a iValue. */
+ void setAnimatedValue(int iValue) { m_iAnimatedValue = iValue; update(); }
+ /** Returns animated value. */
+ int animatedValue() const { return m_iAnimatedValue; }
+
+ /** Handles any kind of hover @a pEvent. */
+ void handleHoverEvent(QGraphicsSceneHoverEvent *pEvent);
+ /** Updates hovered link. */
+ void updateNameHoverLink();
+
+ /** Updates animation parameters. */
+ void updateAnimationParameters();
+ /** Updates toggle button visibility. */
+ void updateButtonVisibility();
+
+ /** Popups name & system editor. */
+ void popupNameAndSystemEditor(bool fChooseName, bool fChoosePath, bool fChooseType, const QString &strValue);
+ /** Popups base-memory editor. */
+ void popupBaseMemoryEditor(const QString &strValue);
+ /** Popups boot-order editor. */
+ void popupBootOrderEditor(const QString &strValue);
+ /** Popups video-memory editor. */
+ void popupVideoMemoryEditor(const QString &strValue);
+ /** Popups graphics controller type editor. */
+ void popupGraphicsControllerTypeEditor(const QString &strValue);
+ /** Popups storage editor. */
+ void popupStorageEditor(const QString &strValue);
+ /** Popups audio host-driver type editor. */
+ void popupAudioHostDriverTypeEditor(const QString &strValue);
+ /** Popups audio controller type editor. */
+ void popupAudioControllerTypeEditor(const QString &strValue);
+ /** Popups network attachment type editor. */
+ void popupNetworkAttachmentTypeEditor(const QString &strValue);
+ /** Popups USB controller type editor. */
+ void popupUSBControllerTypeEditor(const QString &strValue);
+ /** Popups visual-state type editor. */
+ void popupVisualStateTypeEditor(const QString &strValue);
+#ifndef VBOX_WS_MAC
+ /** Popups menu-bar editor. */
+ void popupMenuBarEditor(const QString &strValue);
+#endif
+ /** Popups status-bar editor. */
+ void popupStatusBarEditor(const QString &strValue);
+#ifndef VBOX_WS_MAC
+ /** Popups mini-toolbar editor. */
+ void popupMiniToolbarEditor(const QString &strValue);
+#endif
+ /** Popups cloud editor. */
+ void popupCloudEditor(const QString &strValue);
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates minimum header width. */
+ void updateMinimumHeaderWidth();
+ /** Updates minimum header height. */
+ void updateMinimumHeaderHeight();
+ /** @} */
+
+ /** @name Painting stuff.
+ * @{ */
+ /** Paints background using specified @a pPainter and certain @a pOptions. */
+ void paintBackground(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions) const;
+ /** Paints element info using specified @a pPainter and certain @a pOptions. */
+ void paintElementInfo(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions) const;
+
+ /** Paints @a pixmap using passed @a pPainter and spified @a rect. */
+ static void paintPixmap(QPainter *pPainter, const QRect &rect, const QPixmap &pixmap);
+ /** Paints @a strText using passed @a pPainter, @a font, @a color, @a pPaintDevice and spified @a point. */
+ static void paintText(QPainter *pPainter, QPoint point,
+ const QFont &font, QPaintDevice *pPaintDevice,
+ const QString &strText, const QColor &color);
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Holds the parent reference. */
+ UIDetailsSet *m_pSet;
+ /** Holds the element type. */
+ DetailsElementType m_enmType;
+
+ /** Holds the element pixmap. */
+ QPixmap m_pixmap;
+ /** Holds the element name. */
+ QString m_strName;
+
+ /** Holds the name font. */
+ QFont m_nameFont;
+ /** Holds the text font. */
+ QFont m_textFont;
+
+ /** Holds the start default darkness. */
+ int m_iDefaultDarknessStart;
+ /** Holds the final default darkness. */
+ int m_iDefaultDarknessFinal;
+
+ /** Holds whether element is hovered. */
+ bool m_fHovered;
+ /** Holds whether element name is hovered. */
+ bool m_fNameHovered;
+ /** Holds the hovering animation machine instance. */
+ QStateMachine *m_pHoveringMachine;
+ /** Holds the forward hovering animation instance. */
+ QPropertyAnimation *m_pHoveringAnimationForward;
+ /** Holds the backward hovering animation instance. */
+ QPropertyAnimation *m_pHoveringAnimationBackward;
+ /** Holds the animation duration. */
+ int m_iAnimationDuration;
+ /** Holds the default animation value. */
+ int m_iDefaultValue;
+ /** Holds the hovered animation value. */
+ int m_iHoveredValue;
+ /** Holds the animated value. */
+ int m_iAnimatedValue;
+
+ /** Holds the toggle button instance. */
+ UIGraphicsRotatorButton *m_pButton;
+ /** Holds whether element is closed. */
+ bool m_fClosed;
+ /** Holds whether animation is running. */
+ bool m_fAnimationRunning;
+ /** Holds the additional height. */
+ int m_iAdditionalHeight;
+
+ /** Holds the graphics text pane instance. */
+ UIGraphicsTextPane *m_pTextPane;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds the pixmap size. */
+ QSize m_pixmapSize;
+ /** Holds the name size. */
+ QSize m_nameSize;
+ /** Holds the button size. */
+ QSize m_buttonSize;
+
+ /** Holds minimum header width. */
+ int m_iMinimumHeaderWidth;
+ /** Holds minimum header height. */
+ int m_iMinimumHeaderHeight;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIDetailsElement_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElements.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElements.cpp
new file mode 100644
index 00000000..a7eeffa0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElements.cpp
@@ -0,0 +1,467 @@
+/* $Id: UIDetailsElements.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsElement[Name] classes implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QGraphicsLinearLayout>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIDetailsElements.h"
+#include "UIDetailsGenerator.h"
+#include "UIDetailsModel.h"
+#include "UIErrorString.h"
+#include "UIGraphicsRotatorButton.h"
+#include "UIGraphicsTextPane.h"
+#include "UIIconPool.h"
+#include "UIMachinePreview.h"
+#include "UIThreadPool.h"
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CMedium.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CRecordingScreenSettings.h"
+#include "CRecordingSettings.h"
+#include "CSerialPort.h"
+#include "CSharedFolder.h"
+#include "CStorageController.h"
+#include "CSystemProperties.h"
+#include "CUSBController.h"
+#include "CUSBDeviceFilter.h"
+#include "CUSBDeviceFilters.h"
+#include "CVRDEServer.h"
+
+UIDetailsUpdateTask::UIDetailsUpdateTask(const CMachine &comMachine)
+ : UITask(UITask::Type_DetailsPopulation)
+ , m_comMachine(comMachine)
+{
+}
+
+UIDetailsUpdateTask::UIDetailsUpdateTask(const CCloudMachine &comCloudMachine)
+ : UITask(UITask::Type_DetailsPopulation)
+ , m_comCloudMachine(comCloudMachine)
+{
+}
+
+CMachine UIDetailsUpdateTask::machine() const
+{
+ /* Acquire copy under a proper lock: */
+ m_machineMutex.lock();
+ const CMachine comMachine = m_comMachine;
+ m_machineMutex.unlock();
+ return comMachine;
+}
+
+CCloudMachine UIDetailsUpdateTask::cloudMachine() const
+{
+ /* Acquire copy under a proper lock: */
+ m_machineMutex.lock();
+ const CCloudMachine comCloudMachine = m_comCloudMachine;
+ m_machineMutex.unlock();
+ return comCloudMachine;
+}
+
+UITextTable UIDetailsUpdateTask::table() const
+{
+ /* Acquire copy under a proper lock: */
+ m_tableMutex.lock();
+ const UITextTable guiTable = m_guiTable;
+ m_tableMutex.unlock();
+ return guiTable;
+}
+
+void UIDetailsUpdateTask::setTable(const UITextTable &guiTable)
+{
+ /* Assign under a proper lock: */
+ m_tableMutex.lock();
+ m_guiTable = guiTable;
+ m_tableMutex.unlock();
+}
+
+UIDetailsElementInterface::UIDetailsElementInterface(UIDetailsSet *pParent, DetailsElementType type, bool fOpened)
+ : UIDetailsElement(pParent, type, fOpened)
+ , m_pTask(0)
+{
+ /* Listen for the global thread-pool: */
+ connect(uiCommon().threadPool(), &UIThreadPool::sigTaskComplete,
+ this, &UIDetailsElementInterface::sltUpdateAppearanceFinished);
+
+ /* Translate finally: */
+ retranslateUi();
+}
+
+void UIDetailsElementInterface::retranslateUi()
+{
+ /* Assign corresponding name: */
+ setName(gpConverter->toString(elementType()));
+}
+
+void UIDetailsElementInterface::updateAppearance()
+{
+ /* Call to base-class: */
+ UIDetailsElement::updateAppearance();
+
+ /* Prepare/start update task: */
+ if (!m_pTask)
+ {
+ /* Prepare update task: */
+ m_pTask = createUpdateTask();
+ /* Post task into global thread-pool: */
+ uiCommon().threadPool()->enqueueTask(m_pTask);
+ }
+}
+
+void UIDetailsElementInterface::sltUpdateAppearanceFinished(UITask *pTask)
+{
+ /* Make sure that is one of our tasks: */
+ if (pTask->type() != UITask::Type_DetailsPopulation)
+ return;
+
+ /* Skip unrelated tasks: */
+ if (m_pTask != pTask)
+ return;
+
+ /* Assign new text if changed: */
+ const UITextTable newText = qobject_cast<UIDetailsUpdateTask*>(pTask)->table();
+ if (text() != newText)
+ setText(newText);
+
+ /* Mark task processed: */
+ m_pTask = 0;
+
+ /* Notify listeners about update task complete: */
+ emit sigBuildDone();
+}
+
+
+UIDetailsElementPreview::UIDetailsElementPreview(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElement(pParent, DetailsElementType_Preview, fOpened)
+{
+ /* Create preview: */
+ m_pPreview = new UIMachinePreview(this);
+ AssertPtr(m_pPreview);
+ {
+ /* Configure preview: */
+ connect(m_pPreview, &UIMachinePreview::sigSizeHintChanged,
+ this, &UIDetailsElementPreview::sltPreviewSizeHintChanged);
+ }
+
+ /* Translate finally: */
+ retranslateUi();
+}
+
+void UIDetailsElementPreview::updateLayout()
+{
+ /* Call to base-class: */
+ UIDetailsElement::updateLayout();
+
+ /* Show/hide preview: */
+ if ((isClosed() || isAnimationRunning()) && m_pPreview->isVisible())
+ m_pPreview->hide();
+ if (!isClosed() && !isAnimationRunning() && !m_pPreview->isVisible())
+ m_pPreview->show();
+
+ /* Layout Preview: */
+ const int iMargin = data(ElementData_Margin).toInt();
+ m_pPreview->setPos(iMargin, 2 * iMargin + minimumHeaderHeight());
+ m_pPreview->resize(m_pPreview->minimumSizeHint());
+}
+
+void UIDetailsElementPreview::sltPreviewSizeHintChanged()
+{
+ /* Recursively update size-hints: */
+ updateGeometry();
+ /* Update whole model layout: */
+ model()->updateLayout();
+}
+
+void UIDetailsElementPreview::retranslateUi()
+{
+ /* Assign corresponding name: */
+ setName(gpConverter->toString(elementType()));
+}
+
+int UIDetailsElementPreview::minimumWidthHint() const
+{
+ /* Prepare variables: */
+ int iMargin = data(ElementData_Margin).toInt();
+
+ /* Calculating proposed width: */
+ int iProposedWidth = 0;
+
+ /* Maximum between header width and preview width: */
+ iProposedWidth += qMax(minimumHeaderWidth(), m_pPreview->minimumSizeHint().toSize().width());
+
+ /* Two margins: */
+ iProposedWidth += 2 * iMargin;
+
+ /* Return result: */
+ return iProposedWidth;
+}
+
+int UIDetailsElementPreview::minimumHeightHintForElement(bool fClosed) const
+{
+ /* Prepare variables: */
+ int iMargin = data(ElementData_Margin).toInt();
+
+ /* Calculating proposed height: */
+ int iProposedHeight = 0;
+
+ /* Two margins: */
+ iProposedHeight += 2 * iMargin;
+
+ /* Header height: */
+ iProposedHeight += minimumHeaderHeight();
+
+ /* Element is opened? */
+ if (!fClosed)
+ {
+ iProposedHeight += iMargin;
+ iProposedHeight += m_pPreview->minimumSizeHint().toSize().height();
+ }
+ else
+ {
+ /* Additional height during animation: */
+ if (button()->isAnimationRunning())
+ iProposedHeight += additionalHeight();
+ }
+
+ /* Return result: */
+ return iProposedHeight;
+}
+
+void UIDetailsElementPreview::updateAppearance()
+{
+ /* Call to base-class: */
+ UIDetailsElement::updateAppearance();
+
+ /* Set new machine attribute directly: */
+ m_pPreview->setMachine(machine());
+ m_pPreview->resize(m_pPreview->minimumSizeHint());
+ emit sigBuildDone();
+}
+
+
+void UIDetailsUpdateTaskGeneral::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationGeneral(comMachine, m_fOptions));
+}
+
+void UIDetailsUpdateTaskGeneralCloud::run()
+{
+ /* Acquire corresponding machine: */
+ CCloudMachine comCloudMachine = cloudMachine();
+ if (comCloudMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationGeneral(comCloudMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementGeneral::createUpdateTask()
+{
+ return isLocal()
+ ? static_cast<UITask*>(new UIDetailsUpdateTaskGeneral(machine(), model()->optionsGeneral()))
+ : static_cast<UITask*>(new UIDetailsUpdateTaskGeneralCloud(cloudMachine(), model()->optionsGeneral()));
+}
+
+
+void UIDetailsUpdateTaskSystem::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationSystem(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementSystem::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskSystem(machine(), model()->optionsSystem());
+}
+
+
+void UIDetailsUpdateTaskDisplay::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationDisplay(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementDisplay::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskDisplay(machine(), model()->optionsDisplay());
+}
+
+
+void UIDetailsUpdateTaskStorage::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationStorage(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementStorage::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskStorage(machine(), model()->optionsStorage());
+}
+
+
+void UIDetailsUpdateTaskAudio::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationAudio(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementAudio::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskAudio(machine(), model()->optionsAudio());
+}
+
+void UIDetailsUpdateTaskNetwork::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationNetwork(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementNetwork::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskNetwork(machine(), model()->optionsNetwork());
+}
+
+void UIDetailsUpdateTaskSerial::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationSerial(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementSerial::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskSerial(machine(), model()->optionsSerial());
+}
+
+void UIDetailsUpdateTaskUSB::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationUSB(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementUSB::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskUSB(machine(), model()->optionsUsb());
+}
+
+
+void UIDetailsUpdateTaskSF::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationSharedFolders(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementSF::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskSF(machine(), model()->optionsSharedFolders());
+}
+
+
+void UIDetailsUpdateTaskUI::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationUI(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementUI::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskUI(machine(), model()->optionsUserInterface());
+}
+
+
+void UIDetailsUpdateTaskDescription::run()
+{
+ /* Acquire corresponding machine: */
+ CMachine comMachine = machine();
+ if (comMachine.isNull())
+ return;
+
+ /* Generate details table: */
+ setTable(UIDetailsGenerator::generateMachineInformationDescription(comMachine, m_fOptions));
+}
+
+UITask *UIDetailsElementDescription::createUpdateTask()
+{
+ return new UIDetailsUpdateTaskDescription(machine(), model()->optionsDescription());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElements.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElements.h
new file mode 100644
index 00000000..4748e39b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsElements.h
@@ -0,0 +1,615 @@
+/* $Id: UIDetailsElements.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsElement[Name] classes declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIDetailsElements_h
+#define FEQT_INCLUDED_SRC_manager_details_UIDetailsElements_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMutex>
+
+/* GUI includes: */
+#include "UIDetailsElement.h"
+#include "UITask.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudMachine.h"
+#include "CMachine.h"
+
+/* Forward declarations: */
+class UIMachinePreview;
+class CNetworkAdapter;
+
+
+/** UITask extension used as update task for the details-element. */
+class UIDetailsUpdateTask : public UITask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task taking @a comMachine as data. */
+ UIDetailsUpdateTask(const CMachine &comMachine);
+ /** Constructs update task taking @a comCloudMachine as data. */
+ UIDetailsUpdateTask(const CCloudMachine &comCloudMachine);
+
+ /** Returns the machine. */
+ CMachine machine() const;
+ /** Returns the cloud machine. */
+ CCloudMachine cloudMachine() const;
+
+ /** Returns the table. */
+ UITextTable table() const;
+ /** Defines the @a guiTable. */
+ void setTable(const UITextTable &guiTable);
+
+private:
+
+ /** Holds the mutex to access m_comMachine and m_comCloudMachine members. */
+ mutable QMutex m_machineMutex;
+ /** Holds the machine being processed. */
+ CMachine m_comMachine;
+ /** Holds the cloud machine being processed. */
+ CCloudMachine m_comCloudMachine;
+
+ /** Holds the mutex to access m_guiTable member. */
+ mutable QMutex m_tableMutex;
+ /** Holds the machine being filled. */
+ UITextTable m_guiTable;
+};
+
+/** UIDetailsElement extension used as a wrapping interface to
+ * extend base-class with async functionality performed by the COM worker-threads. */
+class UIDetailsElementInterface : public UIDetailsElement
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element interface for passed @a pParent set.
+ * @param type brings the details-element type this element belongs to.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementInterface(UIDetailsSet *pParent, DetailsElementType type, bool fOpened);
+
+protected:
+
+ /** Performs translation. */
+ virtual void retranslateUi();
+
+ /** Updates appearance. */
+ virtual void updateAppearance();
+
+ /** Creates update task. */
+ virtual UITask *createUpdateTask() = 0;
+
+private slots:
+
+ /** Handles the signal about update @a pTask is finished. */
+ virtual void sltUpdateAppearanceFinished(UITask *pTask);
+
+private:
+
+ /** Holds the instance of the update task. */
+ UITask *m_pTask;
+};
+
+
+/** UIDetailsElementInterface extension for the details-element type 'Preview'. */
+class UIDetailsElementPreview : public UIDetailsElement
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element interface for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be opened. */
+ UIDetailsElementPreview(UIDetailsSet *pParent, bool fOpened);
+
+ /** Updates layout. */
+ virtual void updateLayout() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles preview size-hint changes. */
+ void sltPreviewSizeHintChanged();
+
+private:
+
+ /** Performs translation. */
+ virtual void retranslateUi();
+
+ /** Returns minimum width hint. */
+ int minimumWidthHint() const;
+ /** Returns minimum height hint.
+ * @param fClosed allows to specify whether the hint should
+ * be calculated for the closed element. */
+ int minimumHeightHintForElement(bool fClosed) const;
+
+ /** Updates appearance. */
+ void updateAppearance();
+
+ /** Holds the instance of VM preview. */
+ UIMachinePreview *m_pPreview;
+};
+
+
+/** UITask extension used as update task for the details-element type 'General'. */
+class UIDetailsUpdateTaskGeneral : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskGeneral(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral m_fOptions;
+};
+
+/** UITask extension used as update task for the details-element type 'General' of cloud VM. */
+class UIDetailsUpdateTaskGeneralCloud : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comCloudMachine to the base-class. */
+ UIDetailsUpdateTaskGeneralCloud(const CCloudMachine &comCloudMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral fOptions)
+ : UIDetailsUpdateTask(comCloudMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'General'. */
+class UIDetailsElementGeneral : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementGeneral(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_General, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'System'. */
+class UIDetailsUpdateTaskSystem : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskSystem(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeSystem fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSystem m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'System'. */
+class UIDetailsElementSystem : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementSystem(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_System, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'Display'. */
+class UIDetailsUpdateTaskDisplay : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskDisplay(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'Display'. */
+class UIDetailsElementDisplay : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementDisplay(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_Display, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'Storage'. */
+class UIDetailsUpdateTaskStorage : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskStorage(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeStorage fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeStorage m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'Storage'. */
+class UIDetailsElementStorage : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementStorage(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_Storage, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'Audio'. */
+class UIDetailsUpdateTaskAudio : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskAudio(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeAudio fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeAudio m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'Audio'. */
+class UIDetailsElementAudio : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementAudio(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_Audio, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'Network'. */
+class UIDetailsUpdateTaskNetwork : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskNetwork(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Summarizes generic properties. */
+ static QString summarizeGenericProperties(const CNetworkAdapter &adapter);
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'Network'. */
+class UIDetailsElementNetwork : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementNetwork(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_Network, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'Serial'. */
+class UIDetailsUpdateTaskSerial : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskSerial(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeSerial fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSerial m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'Serial'. */
+class UIDetailsElementSerial : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementSerial(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_Serial, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'USB'. */
+class UIDetailsUpdateTaskUSB : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskUSB(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeUsb fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeUsb m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'USB'. */
+class UIDetailsElementUSB : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementUSB(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_USB, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'SF'. */
+class UIDetailsUpdateTaskSF : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskSF(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'SF'. */
+class UIDetailsElementSF : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementSF(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_SF, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'UI'. */
+class UIDetailsUpdateTaskUI : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskUI(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'UI'. */
+class UIDetailsElementUI : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementUI(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_UI, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+
+/** UITask extension used as update task for the details-element type 'Description'. */
+class UIDetailsUpdateTaskDescription : public UIDetailsUpdateTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs update task passing @a comMachine to the base-class. */
+ UIDetailsUpdateTaskDescription(const CMachine &comMachine, UIExtraDataMetaDefs::DetailsElementOptionTypeDescription fOptions)
+ : UIDetailsUpdateTask(comMachine), m_fOptions(fOptions) {}
+
+private:
+
+ /** Contains update task body. */
+ void run();
+
+ /** Holds the options. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeDescription m_fOptions;
+};
+
+/** UIDetailsElementInterface extension for the details-element type 'Description'. */
+class UIDetailsElementDescription : public UIDetailsElementInterface
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs details-element object for passed @a pParent set.
+ * @param fOpened brings whether the details-element should be visually opened. */
+ UIDetailsElementDescription(UIDetailsSet *pParent, bool fOpened)
+ : UIDetailsElementInterface(pParent, DetailsElementType_Description, fOpened) {}
+
+private:
+
+ /** Creates update task for this element. */
+ virtual UITask *createUpdateTask() RT_OVERRIDE;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIDetailsElements_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsGroup.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsGroup.cpp
new file mode 100644
index 00000000..d838dd5f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsGroup.cpp
@@ -0,0 +1,279 @@
+/* $Id: UIDetailsGroup.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsGroup class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt include: */
+#include <QGraphicsLinearLayout>
+#include <QGraphicsScene>
+#include <QPainter>
+#include <QStyle>
+#include <QStyleOptionGraphicsItem>
+
+/* GUI includes: */
+#include "UIGraphicsScrollArea.h"
+#include "UIDetailsGroup.h"
+#include "UIDetailsModel.h"
+#include "UIDetailsSet.h"
+#include "UIDetailsView.h"
+#include "UIExtraDataManager.h"
+#include "UIVirtualMachineItem.h"
+#include "UICommon.h"
+
+
+UIDetailsGroup::UIDetailsGroup(QGraphicsScene *pParent)
+ : UIDetailsItem(0)
+ , m_pBuildStep(0)
+ , m_pScrollArea(0)
+ , m_pContainer(0)
+ , m_pLayout(0)
+ , m_iPreviousMinimumWidthHint(0)
+{
+ /* Prepare scroll-area: */
+ m_pScrollArea = new UIGraphicsScrollArea(Qt::Vertical, this);
+ if (m_pScrollArea)
+ {
+ /* Prepare container: */
+ m_pContainer = new QIGraphicsWidget;
+ if (m_pContainer)
+ {
+ /* Prepare layout: */
+ m_pLayout = new QGraphicsLinearLayout(Qt::Vertical, m_pContainer);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setSpacing(0);
+ }
+
+ /* Assign to scroll-area: */
+ m_pScrollArea->setViewport(m_pContainer);
+ }
+ }
+
+ /* Add group to the parent scene: */
+ pParent->addItem(this);
+
+ /* Prepare connections: */
+ prepareConnections();
+}
+
+UIDetailsGroup::~UIDetailsGroup()
+{
+ /* Cleanup items: */
+ clearItems();
+}
+
+void UIDetailsGroup::buildGroup(const QList<UIVirtualMachineItem*> &machineItems)
+{
+ /* Filter out cloud VM items for now: */
+ QList<UIVirtualMachineItem*> filteredItems;
+ foreach (UIVirtualMachineItem *pItem, machineItems)
+ if ( pItem->itemType() == UIVirtualMachineItemType_Local
+ || pItem->itemType() == UIVirtualMachineItemType_CloudReal)
+ filteredItems << pItem;
+
+ /* Remember passed machine-items: */
+ m_machineItems = filteredItems;
+
+ /* Cleanup superflous items: */
+ const bool fCleanupPerformed = m_items.size() > m_machineItems.size();
+ while (m_items.size() > m_machineItems.size())
+ delete m_items.last();
+ foreach (UIDetailsItem *pItem, m_items)
+ pItem->toSet()->clearSet();
+ if (fCleanupPerformed)
+ updateGeometry();
+
+ /* Start building group: */
+ rebuildGroup();
+}
+
+void UIDetailsGroup::rebuildGroup()
+{
+ /* Cleanup build-step: */
+ delete m_pBuildStep;
+ m_pBuildStep = 0;
+
+ /* Generate new group-id: */
+ m_uGroupId = QUuid::createUuid();
+
+ /* Request to build first step: */
+ emit sigBuildStep(m_uGroupId, 0);
+}
+
+void UIDetailsGroup::stopBuildingGroup()
+{
+ /* Generate new group-id: */
+ m_uGroupId = QUuid::createUuid();
+}
+
+void UIDetailsGroup::installEventFilterHelper(QObject *pSource)
+{
+ /* The only object which need's that filter for now is scroll-area: */
+ pSource->installEventFilter(m_pScrollArea);
+}
+
+QList<UIDetailsItem*> UIDetailsGroup::items(UIDetailsItemType enmType /* = UIDetailsItemType_Set */) const
+{
+ switch (enmType)
+ {
+ case UIDetailsItemType_Set: return m_items;
+ case UIDetailsItemType_Any: return items(UIDetailsItemType_Set);
+ default: AssertMsgFailed(("Invalid item type!")); break;
+ }
+ return QList<UIDetailsItem*>();
+}
+
+void UIDetailsGroup::updateLayout()
+{
+ /* Acquire view: */
+ UIDetailsView *pView = model()->view();
+
+ /* Adjust children scroll-area: */
+ m_pScrollArea->resize(pView->size());
+ m_pScrollArea->setPos(0, 0);
+
+ /* Layout all the sets: */
+ foreach (UIDetailsItem *pItem, items())
+ pItem->updateLayout();
+}
+
+int UIDetailsGroup::minimumWidthHint() const
+{
+ return m_pContainer->minimumSizeHint().width();
+}
+
+int UIDetailsGroup::minimumHeightHint() const
+{
+ return m_pContainer->minimumSizeHint().height();
+}
+
+void UIDetailsGroup::sltBuildStep(const QUuid &uStepId, int iStepNumber)
+{
+ /* Cleanup build-step: */
+ delete m_pBuildStep;
+ m_pBuildStep = 0;
+
+ /* Is step id valid? */
+ if (uStepId != m_uGroupId)
+ return;
+
+ /* Step number feats the bounds: */
+ if (iStepNumber >= 0 && iStepNumber < m_machineItems.size())
+ {
+ /* Should we create a new set for this step? */
+ UIDetailsSet *pSet = 0;
+ if (iStepNumber > m_items.size() - 1)
+ pSet = new UIDetailsSet(this);
+ /* Or use existing? */
+ else
+ pSet = m_items.at(iStepNumber)->toSet();
+
+ /* Create next build-step: */
+ m_pBuildStep = new UIPrepareStep(this, pSet, uStepId, iStepNumber + 1);
+
+ /* Build set: */
+ pSet->buildSet(m_machineItems[iStepNumber], m_machineItems.size() == 1, model()->categories());
+ }
+ else
+ {
+ /* Notify listener about build done: */
+ emit sigBuildDone();
+ }
+}
+
+void UIDetailsGroup::addItem(UIDetailsItem *pItem)
+{
+ switch (pItem->type())
+ {
+ case UIDetailsItemType_Set:
+ {
+ m_pLayout->addItem(pItem);
+ m_items.append(pItem);
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid item type!"));
+ break;
+ }
+ }
+}
+
+void UIDetailsGroup::removeItem(UIDetailsItem *pItem)
+{
+ switch (pItem->type())
+ {
+ case UIDetailsItemType_Set: m_items.removeAt(m_items.indexOf(pItem)); break;
+ default: AssertMsgFailed(("Invalid item type!")); break;
+ }
+}
+
+bool UIDetailsGroup::hasItems(UIDetailsItemType enmType /* = UIDetailsItemType_Set */) const
+{
+ switch (enmType)
+ {
+ case UIDetailsItemType_Set: return !m_items.isEmpty();
+ case UIDetailsItemType_Any: return hasItems(UIDetailsItemType_Set);
+ default: AssertMsgFailed(("Invalid item type!")); break;
+ }
+ return false;
+}
+
+void UIDetailsGroup::clearItems(UIDetailsItemType enmType /* = UIDetailsItemType_Set */)
+{
+ switch (enmType)
+ {
+ case UIDetailsItemType_Set: while (!m_items.isEmpty()) { delete m_items.last(); } break;
+ case UIDetailsItemType_Any: clearItems(UIDetailsItemType_Set); break;
+ default: AssertMsgFailed(("Invalid item type!")); break;
+ }
+}
+
+void UIDetailsGroup::updateGeometry()
+{
+ /* Update/activate children layout: */
+ m_pLayout->updateGeometry();
+ m_pLayout->activate();
+
+ /* Call to base class: */
+ UIDetailsItem::updateGeometry();
+
+ /* Group-item should notify details-view if minimum-width-hint was changed: */
+ int iMinimumWidthHint = minimumWidthHint();
+ if (m_iPreviousMinimumWidthHint != iMinimumWidthHint)
+ {
+ /* Save new minimum-width-hint, notify listener: */
+ m_iPreviousMinimumWidthHint = iMinimumWidthHint;
+ emit sigMinimumWidthHintChanged(m_iPreviousMinimumWidthHint);
+ }
+}
+
+void UIDetailsGroup::prepareConnections()
+{
+ /* Prepare group-item connections: */
+ connect(this, &UIDetailsGroup::sigMinimumWidthHintChanged,
+ model(), &UIDetailsModel::sigRootItemMinimumWidthHintChanged);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsGroup.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsGroup.h
new file mode 100644
index 00000000..32e2b791
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsGroup.h
@@ -0,0 +1,175 @@
+/* $Id: UIDetailsGroup.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsGroup class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIDetailsGroup_h
+#define FEQT_INCLUDED_SRC_manager_details_UIDetailsGroup_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIDetailsItem.h"
+
+/* Forward declarations: */
+class QGraphicsLinearLayout;
+class QGraphicsScene;
+class UIGraphicsScrollArea;
+class UIVirtualMachineItem;
+
+/** UIDetailsItem extension implementing group item. */
+class UIDetailsGroup : public UIDetailsItem
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Notifies listeners about @a iMinimumWidthHint changed. */
+ void sigMinimumWidthHintChanged(int iMinimumWidthHint);
+ /** @} */
+
+public:
+
+ /** RTTI item type. */
+ enum { Type = UIDetailsItemType_Group };
+
+ /** Constructs group item, passing pScene to the base-class. */
+ UIDetailsGroup(QGraphicsScene *pScene);
+ /** Destructs group item. */
+ virtual ~UIDetailsGroup() RT_OVERRIDE;
+
+ /** @name Item stuff.
+ * @{ */
+ /** Builds group based on passed @a machineItems. */
+ void buildGroup(const QList<UIVirtualMachineItem*> &machineItems);
+ /** Builds group based on cached machine items. */
+ void rebuildGroup();
+ /** Stops currently building group. */
+ void stopBuildingGroup();
+
+ /** Installs event-filter for @a pSource object. */
+ virtual void installEventFilterHelper(QObject *pSource) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Returns children items of certain @a enmType. */
+ virtual QList<UIDetailsItem*> items(UIDetailsItemType enmType = UIDetailsItemType_Set) const RT_OVERRIDE;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates layout. */
+ virtual void updateLayout() RT_OVERRIDE;
+
+ /** Returns minimum width-hint. */
+ virtual int minimumWidthHint() const RT_OVERRIDE;
+ /** Returns minimum height-hint. */
+ virtual int minimumHeightHint() const RT_OVERRIDE;
+ /** @} */
+
+protected slots:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles request about starting step build.
+ * @param uStepId Brings the step ID.
+ * @param iStepNumber Brings the step number. */
+ /** @} */
+ virtual void sltBuildStep(const QUuid &uStepId, int iStepNumber) RT_OVERRIDE;
+
+protected:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns RTTI item type. */
+ virtual int type() const RT_OVERRIDE { return Type; }
+
+ /** Returns the description of the item. */
+ virtual QString description() const RT_OVERRIDE { return QString(); }
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Adds child @a pItem. */
+ virtual void addItem(UIDetailsItem *pItem) RT_OVERRIDE;
+ /** Removes child @a pItem. */
+ virtual void removeItem(UIDetailsItem *pItem) RT_OVERRIDE;
+
+ /** Returns whether there are children items of certain @a enmType. */
+ virtual bool hasItems(UIDetailsItemType enmType = UIDetailsItemType_Set) const RT_OVERRIDE;
+ /** Clears children items of certain @a enmType. */
+ virtual void clearItems(UIDetailsItemType enmType = UIDetailsItemType_Set) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates geometry. */
+ virtual void updateGeometry() RT_OVERRIDE;
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares connections. */
+ void prepareConnections();
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Holds the build step instance. */
+ UIPrepareStep *m_pBuildStep;
+ /** Holds the generated group ID. */
+ QUuid m_uGroupId;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Holds the children scroll-area instance. */
+ UIGraphicsScrollArea *m_pScrollArea;
+ /** Holds the children container instance. */
+ QIGraphicsWidget *m_pContainer;
+ /** Holds the children layout instance. */
+ QGraphicsLinearLayout *m_pLayout;
+
+ /** Holds the cached machine item list. */
+ QList<UIVirtualMachineItem*> m_machineItems;
+
+ /** Holds the child list (a list of sets). */
+ QList<UIDetailsItem*> m_items;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds previous minimum width hint. */
+ int m_iPreviousMinimumWidthHint;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIDetailsGroup_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsItem.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsItem.cpp
new file mode 100644
index 00000000..96f98a59
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsItem.cpp
@@ -0,0 +1,308 @@
+/* $Id: UIDetailsItem.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsItem class definition.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleObject>
+#include <QApplication>
+#include <QGraphicsScene>
+#include <QPainter>
+#include <QStyleOptionGraphicsItem>
+
+/* GUI includes: */
+#include "UIGraphicsTextPane.h"
+#include "UIDetails.h"
+#include "UIDetailsElement.h"
+#include "UIDetailsGroup.h"
+#include "UIDetailsModel.h"
+#include "UIDetailsSet.h"
+#include "UIDetailsView.h"
+
+
+/** QAccessibleObject extension used as an accessibility interface for Details-view items. */
+class UIAccessibilityInterfaceForUIDetailsItem : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating Details-view accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UIDetailsItem"))
+ return new UIAccessibilityInterfaceForUIDetailsItem(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ UIAccessibilityInterfaceForUIDetailsItem(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Return the parent: */
+ switch (item()->type())
+ {
+ /* For a set: */
+ case UIDetailsItemType_Set:
+ {
+ /* Always return parent view: */
+ return QAccessible::queryAccessibleInterface(item()->model()->details()->view());
+ }
+ /* For an element: */
+ case UIDetailsItemType_Element:
+ {
+ /* What amount of children root has? */
+ const int cChildCount = item()->model()->root()->items().size();
+
+ /* Return our parent (if root has many of children): */
+ if (cChildCount > 1)
+ return QAccessible::queryAccessibleInterface(item()->parentItem());
+
+ /* Return parent view (otherwise): */
+ return QAccessible::queryAccessibleInterface(item()->model()->details()->view());
+ }
+ default:
+ break;
+ }
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Return the number of children: */
+ switch (item()->type())
+ {
+ case UIDetailsItemType_Set: return item()->items().size();
+ case UIDetailsItemType_Element: return item()->toElement()->text().size();
+ default: break;
+ }
+
+ /* Zero by default: */
+ return 0;
+ }
+
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Return the child with the iIndex: */
+ switch (item()->type())
+ {
+ case UIDetailsItemType_Set: return QAccessible::queryAccessibleInterface(item()->items().at(iIndex));
+ case UIDetailsItemType_Element: return QAccessible::queryAccessibleInterface(&item()->toElement()->text()[iIndex]);
+ default: break;
+ }
+
+ /* Null be default: */
+ return 0;
+ }
+
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE
+ {
+ /* Search for corresponding child: */
+ for (int i = 0; i < childCount(); ++i)
+ if (child(i) == pChild)
+ return i;
+
+ /* -1 by default: */
+ return -1;
+ }
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE
+ {
+ /* Now goes the mapping: */
+ const QSize itemSize = item()->size().toSize();
+ const QPointF itemPosInScene = item()->mapToScene(QPointF(0, 0));
+ const QPoint itemPosInView = item()->model()->details()->view()->mapFromScene(itemPosInScene);
+ const QPoint itemPosInScreen = item()->model()->details()->view()->mapToGlobal(itemPosInView);
+ const QRect itemRectInScreen = QRect(itemPosInScreen, itemSize);
+ return itemRectInScreen;
+ }
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QString());
+
+ /* Return the description: */
+ if (enmTextRole == QAccessible::Description)
+ return item()->description();
+
+ /* Null-string by default: */
+ return QString();
+ }
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE
+ {
+ /* Return the role: */
+ return QAccessible::List;
+ }
+
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE
+ {
+ /* Return the state: */
+ return QAccessible::State();
+ }
+
+private:
+
+ /** Returns corresponding Details-view item. */
+ UIDetailsItem *item() const { return qobject_cast<UIDetailsItem*>(object()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIDetailsItem implementation. *
+*********************************************************************************************************************************/
+
+UIDetailsItem::UIDetailsItem(UIDetailsItem *pParent)
+ : QIWithRetranslateUI4<QIGraphicsWidget>(pParent)
+ , m_pParent(pParent)
+{
+ /* Install Details-view item accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUIDetailsItem::pFactory);
+
+ /* Basic item setup: */
+ setOwnedByLayout(false);
+ setFocusPolicy(Qt::NoFocus);
+ setFlag(QGraphicsItem::ItemIsSelectable, false);
+
+ /* Non-root item? */
+ if (parentItem())
+ {
+ /* Non-root item setup: */
+ setAcceptHoverEvents(true);
+ }
+
+ /* Setup connections: */
+ connect(this, &UIDetailsItem::sigBuildStep,
+ this, &UIDetailsItem::sltBuildStep,
+ Qt::QueuedConnection);
+}
+
+UIDetailsGroup *UIDetailsItem::toGroup()
+{
+ UIDetailsGroup *pItem = qgraphicsitem_cast<UIDetailsGroup*>(this);
+ AssertMsg(pItem, ("Trying to cast invalid item type to UIDetailsGroup!"));
+ return pItem;
+}
+
+UIDetailsSet *UIDetailsItem::toSet()
+{
+ UIDetailsSet *pItem = qgraphicsitem_cast<UIDetailsSet*>(this);
+ AssertMsg(pItem, ("Trying to cast invalid item type to UIDetailsSet!"));
+ return pItem;
+}
+
+UIDetailsElement *UIDetailsItem::toElement()
+{
+ UIDetailsElement *pItem = qgraphicsitem_cast<UIDetailsElement*>(this);
+ AssertMsg(pItem, ("Trying to cast invalid item type to UIDetailsElement!"));
+ return pItem;
+}
+
+UIDetailsModel *UIDetailsItem::model() const
+{
+ UIDetailsModel *pModel = qobject_cast<UIDetailsModel*>(QIGraphicsWidget::scene()->parent());
+ AssertMsg(pModel, ("Incorrect graphics scene parent set!"));
+ return pModel;
+}
+
+void UIDetailsItem::updateGeometry()
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::updateGeometry();
+
+ /* Do the same for the parent: */
+ if (parentItem())
+ parentItem()->updateGeometry();
+}
+
+QSizeF UIDetailsItem::sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint /* = QSizeF() */) const
+{
+ /* If Qt::MinimumSize or Qt::PreferredSize requested: */
+ if (enmWhich == Qt::MinimumSize || enmWhich == Qt::PreferredSize)
+ /* Return wrappers: */
+ return QSizeF(minimumWidthHint(), minimumHeightHint());
+ /* Call to base-class: */
+ return QIGraphicsWidget::sizeHint(enmWhich, constraint);
+}
+
+void UIDetailsItem::sltBuildStep(const QUuid &, int)
+{
+ AssertMsgFailed(("This item doesn't support building!"));
+}
+
+
+/*********************************************************************************************************************************
+* Class UIPrepareStep implementation. *
+*********************************************************************************************************************************/
+
+UIPrepareStep::UIPrepareStep(QObject *pParent, QObject *pBuildObject, const QUuid &uStepId, int iStepNumber)
+ : QObject(pParent)
+ , m_uStepId(uStepId)
+ , m_iStepNumber(iStepNumber)
+{
+ /* Prepare connections: */
+ connect(qobject_cast<UIDetailsItem*>(pBuildObject), &UIDetailsItem::sigBuildDone,
+ this, &UIPrepareStep::sltStepDone,
+ Qt::QueuedConnection);
+
+ UIDetailsItem *pDetailsItem = qobject_cast<UIDetailsItem*>(pParent);
+ AssertPtrReturnVoid(pDetailsItem);
+ {
+ connect(this, &UIPrepareStep::sigStepDone,
+ pDetailsItem, &UIDetailsItem::sltBuildStep,
+ Qt::QueuedConnection);
+ }
+}
+
+void UIPrepareStep::sltStepDone()
+{
+ emit sigStepDone(m_uStepId, m_iStepNumber);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsItem.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsItem.h
new file mode 100644
index 00000000..a125315b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsItem.h
@@ -0,0 +1,200 @@
+/* $Id: UIDetailsItem.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsItem class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIDetailsItem_h
+#define FEQT_INCLUDED_SRC_manager_details_UIDetailsItem_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes */
+#include <QUuid>
+
+/* GUI includes: */
+#include "QIGraphicsWidget.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declaration: */
+class QGraphicsSceneHoverEvent;
+class QGraphicsSceneMouseEvent;
+class UIDetailsElement;
+class UIDetailsGroup;
+class UIDetailsModel;
+class UIDetailsSet;
+
+
+/** UIDetailsItem types. */
+enum UIDetailsItemType
+{
+ UIDetailsItemType_Any = QGraphicsItem::UserType,
+ UIDetailsItemType_Group,
+ UIDetailsItemType_Set,
+ UIDetailsItemType_Element,
+ UIDetailsItemType_Preview
+};
+
+
+/** QIGraphicsWidget extension used as interface
+ * for graphics details model/view architecture. */
+class UIDetailsItem : public QIWithRetranslateUI4<QIGraphicsWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Notifies listeners about step build should be started.
+ * @param uStepId Brings the step ID.
+ * @param iStepNumber Brings the step number. */
+ void sigBuildStep(const QUuid &uStepId, int iStepNumber);
+ /** Notifies listeners about step build complete. */
+ void sigBuildDone();
+ /** @} */
+
+public:
+
+ /** Constructs item passing @a pParent to the base-class. */
+ UIDetailsItem(UIDetailsItem *pParent);
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns parent reference. */
+ UIDetailsItem *parentItem() const { return m_pParent; }
+
+ /** Casts item to group one. */
+ UIDetailsGroup *toGroup();
+ /** Casts item to set one. */
+ UIDetailsSet *toSet();
+ /** Casts item to element one. */
+ UIDetailsElement *toElement();
+
+ /** Returns model reference. */
+ UIDetailsModel *model() const;
+
+ /** Returns the description of the item. */
+ virtual QString description() const = 0;
+
+ /** Installs event-filter for @a pSource object.
+ * @note Base-class implementation does nothing. */
+ virtual void installEventFilterHelper(QObject *pSource) { Q_UNUSED(pSource); }
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Adds child @a pItem. */
+ virtual void addItem(UIDetailsItem *pItem) = 0;
+ /** Removes child @a pItem. */
+ virtual void removeItem(UIDetailsItem *pItem) = 0;
+
+ /** Returns children items of certain @a enmType. */
+ virtual QList<UIDetailsItem*> items(UIDetailsItemType enmType = UIDetailsItemType_Any) const = 0;
+ /** Returns whether there are children items of certain @a enmType. */
+ virtual bool hasItems(UIDetailsItemType enmType = UIDetailsItemType_Any) const = 0;
+ /** Clears children items of certain @a enmType. */
+ virtual void clearItems(UIDetailsItemType enmType = UIDetailsItemType_Any) = 0;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates geometry. */
+ virtual void updateGeometry() RT_OVERRIDE;
+
+ /** Updates layout. */
+ virtual void updateLayout() = 0;
+
+ /** Returns minimum width-hint. */
+ virtual int minimumWidthHint() const = 0;
+ /** Returns minimum height-hint. */
+ virtual int minimumHeightHint() const = 0;
+
+ /** Returns size-hint.
+ * @param enmWhich Brings size-hint type.
+ * @param constraint Brings size constraint. */
+ virtual QSizeF sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint = QSizeF()) const RT_OVERRIDE;
+ /** @} */
+
+public slots:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles request about starting step build.
+ * @param uStepId Brings the step ID.
+ * @param iStepNumber Brings the step number. */
+ /** @} */
+ virtual void sltBuildStep(const QUuid &uStepId, int iStepNumber);
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE {}
+ /** @} */
+
+private:
+
+ /** Holds the parent item reference. */
+ UIDetailsItem *m_pParent;
+};
+
+
+/** QObject extension used to prepare details steps. */
+class UIPrepareStep : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about step preparing is complete.
+ * @param strStepId Brings the step ID.
+ * @param iStepNumber Brings the step number. */
+ void sigStepDone(const QUuid& aStepId, int iStepNumber);
+
+public:
+
+ /** Constructs step preparing object passing @a pParent to the base-class.
+ * @param pBuildObject Brings the build object reference.
+ * @param uStepId Brings the step ID.
+ * @param iStepNumber Brings the step number.*/
+ UIPrepareStep(QObject *pParent, QObject *pBuildObject, const QUuid &uStepId, int iStepNumber);
+
+private slots:
+
+ /** Handles step prepare completion. */
+ void sltStepDone();
+
+private:
+
+ /** Holds the step ID. */
+ QUuid m_uStepId;
+ /** Holds the step number. */
+ int m_iStepNumber;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIDetailsItem_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsModel.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsModel.cpp
new file mode 100644
index 00000000..7281c2cf
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsModel.cpp
@@ -0,0 +1,1047 @@
+/* $Id: UIDetailsModel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsModel class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QGraphicsScene>
+#include <QGraphicsSceneContextMenuEvent>
+#include <QGraphicsView>
+#include <QMenu>
+#include <QMetaEnum>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIDetails.h"
+#include "UIDetailsContextMenu.h"
+#include "UIDetailsElement.h"
+#include "UIDetailsGroup.h"
+#include "UIDetailsModel.h"
+#include "UIDetailsView.h"
+#include "UIExtraDataManager.h"
+
+
+/*********************************************************************************************************************************
+* Class UIDetailsModel implementation. *
+*********************************************************************************************************************************/
+
+UIDetailsModel::UIDetailsModel(UIDetails *pParent)
+ : QObject(pParent)
+ , m_pDetails(pParent)
+ , m_pScene(0)
+ , m_pRoot(0)
+ , m_pAnimationCallback(0)
+ , m_pContextMenu(0)
+{
+ prepare();
+}
+
+UIDetailsModel::~UIDetailsModel()
+{
+ cleanup();
+}
+
+void UIDetailsModel::init()
+{
+ /* Install root as event-filter for scene view,
+ * we need QEvent::Scroll events from it: */
+ root()->installEventFilterHelper(view());
+}
+
+QGraphicsScene *UIDetailsModel::scene() const
+{
+ return m_pScene;
+}
+
+UIDetailsView *UIDetailsModel::view() const
+{
+ return scene() && !scene()->views().isEmpty() ? qobject_cast<UIDetailsView*>(scene()->views().first()) : 0;
+}
+
+QGraphicsView *UIDetailsModel::paintDevice() const
+{
+ return scene() && !scene()->views().isEmpty() ? scene()->views().first() : 0;
+}
+
+QGraphicsItem *UIDetailsModel::itemAt(const QPointF &position) const
+{
+ return scene()->itemAt(position, QTransform());
+}
+
+UIDetailsItem *UIDetailsModel::root() const
+{
+ return m_pRoot;
+}
+
+void UIDetailsModel::updateLayout()
+{
+ /* Initialize variables: */
+ AssertPtrReturnVoid(view());
+ AssertPtrReturnVoid(root());
+ const QSize viewportSize = view()->size();
+ const int iViewportWidth = viewportSize.width();
+ const int iViewportHeight = root()->minimumSizeHint().toSize().height();
+
+ /* Move root: */
+ root()->setPos(0, 0);
+ /* Resize root: */
+ root()->resize(iViewportWidth, iViewportHeight);
+ /* Layout root content: */
+ root()->updateLayout();
+}
+
+void UIDetailsModel::setItems(const QList<UIVirtualMachineItem*> &items)
+{
+ m_pRoot->buildGroup(items);
+}
+
+void UIDetailsModel::setCategories(const QMap<DetailsElementType, bool> &categories)
+{
+ m_categories = categories;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateCategoryStates();
+
+ /* Save categories: */
+ gEDataManager->setSelectorWindowDetailsElements(m_categories);
+}
+
+void UIDetailsModel::setOptionsGeneral(UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral fOptionsGeneral)
+{
+ m_fOptionsGeneral = fOptionsGeneral;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_General);
+
+ /* Save general options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeGeneral");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsGeneral & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_General, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_General, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsSystem(UIExtraDataMetaDefs::DetailsElementOptionTypeSystem fOptionsSystem)
+{
+ m_fOptionsSystem = fOptionsSystem;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_System);
+
+ /* Save system options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeSystem");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsSystem & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_System, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_System, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsDisplay(UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay fOptionsDisplay)
+{
+ m_fOptionsDisplay = fOptionsDisplay;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_Display);
+
+ /* Save display options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeDisplay");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsDisplay & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Display, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Display, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsStorage(UIExtraDataMetaDefs::DetailsElementOptionTypeStorage fOptionsStorage)
+{
+ m_fOptionsStorage = fOptionsStorage;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_Storage);
+
+ /* Save storage options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeStorage");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsStorage & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Storage, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Storage, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsAudio(UIExtraDataMetaDefs::DetailsElementOptionTypeAudio fOptionsAudio)
+{
+ m_fOptionsAudio = fOptionsAudio;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_Audio);
+
+ /* Save audio options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeAudio");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsAudio & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Audio, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Audio, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsNetwork(UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork fOptionsNetwork)
+{
+ m_fOptionsNetwork = fOptionsNetwork;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_Network);
+
+ /* Save network options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeNetwork");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsNetwork & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Network, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Network, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsSerial(UIExtraDataMetaDefs::DetailsElementOptionTypeSerial fOptionsSerial)
+{
+ m_fOptionsSerial = fOptionsSerial;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_Serial);
+
+ /* Save serial options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeSerial");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsSerial & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Serial, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Serial, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsUsb(UIExtraDataMetaDefs::DetailsElementOptionTypeUsb fOptionsUsb)
+{
+ m_fOptionsUsb = fOptionsUsb;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_USB);
+
+ /* Save USB options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeUsb");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsUsb & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_USB, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_USB, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsSharedFolders(UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders fOptionsSharedFolders)
+{
+ m_fOptionsSharedFolders = fOptionsSharedFolders;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_SF);
+
+ /* Save shared folders options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeSharedFolders");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsSharedFolders & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_SF, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_SF, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsUserInterface(UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface fOptionsUserInterface)
+{
+ m_fOptionsUserInterface = fOptionsUserInterface;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_UI);
+
+ /* Save user interface options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeUserInterface");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsUserInterface & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_UI, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_UI, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::setOptionsDescription(UIExtraDataMetaDefs::DetailsElementOptionTypeDescription fOptionsDescription)
+{
+ m_fOptionsDescription = fOptionsDescription;
+ m_pRoot->rebuildGroup();
+ m_pContextMenu->updateOptionStates(DetailsElementType_Description);
+
+ /* Save description options: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+ const int iEnumIndex = smo.indexOfEnumerator("DetailsElementOptionTypeDescription");
+ if (iEnumIndex != -1)
+ {
+ bool fDefault = true;
+ QStringList options;
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Prepare current option type: */
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription enmOptionType =
+ static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip invalid and default types: */
+ if ( enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Invalid
+ || enmOptionType == UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Default)
+ continue;
+ /* If option type enabled: */
+ if (m_fOptionsDescription & enmOptionType)
+ {
+ /* Add it to the list: */
+ options << gpConverter->toInternalString(enmOptionType);
+ /* Make sure item is included by default: */
+ if (!(UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Default & enmOptionType))
+ fDefault = false;
+ }
+ /* If option type disabled: */
+ else
+ {
+ /* Make sure item is excluded by default: */
+ if (UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Default & enmOptionType)
+ fDefault = false;
+ }
+ /* Save options: */
+ if (!fDefault)
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Description, options);
+ else
+ gEDataManager->setVBoxManagerDetailsPaneElementOptions(DetailsElementType_Description, QStringList());
+ }
+ }
+}
+
+void UIDetailsModel::sltHandleViewResize()
+{
+ updateLayout();
+}
+
+void UIDetailsModel::sltHandleToggleStarted()
+{
+ m_pRoot->stopBuildingGroup();
+}
+
+void UIDetailsModel::sltHandleToggleFinished()
+{
+ m_pRoot->rebuildGroup();
+}
+
+void UIDetailsModel::sltHandleExtraDataCategoriesChange()
+{
+ loadDetailsCategories();
+ m_pContextMenu->updateCategoryStates();
+ m_pRoot->rebuildGroup();
+}
+
+void UIDetailsModel::sltHandleExtraDataOptionsChange(DetailsElementType enmType)
+{
+ loadDetailsOptions(enmType);
+ m_pContextMenu->updateOptionStates(enmType);
+ m_pRoot->rebuildGroup();
+}
+
+bool UIDetailsModel::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Handle allowed context-menu events: */
+ if (pObject == scene() && pEvent->type() == QEvent::GraphicsSceneContextMenu)
+ return processContextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent*>(pEvent));
+
+ /* Call to base-class: */
+ return QObject::eventFilter(pObject, pEvent);
+}
+
+void UIDetailsModel::sltToggleElements(DetailsElementType type, bool fToggled)
+{
+ /* Make sure it is not started yet: */
+ if (m_pAnimationCallback)
+ return;
+
+ /* Prepare/configure animation callback: */
+ m_pAnimationCallback = new UIDetailsElementAnimationCallback(this, type, fToggled);
+ connect(m_pAnimationCallback, &UIDetailsElementAnimationCallback::sigAllAnimationFinished,
+ this, &UIDetailsModel::sltToggleAnimationFinished, Qt::QueuedConnection);
+ /* For each the set of the group: */
+ foreach (UIDetailsItem *pSetItem, m_pRoot->items())
+ {
+ /* For each the element of the set: */
+ foreach (UIDetailsItem *pElementItem, pSetItem->items())
+ {
+ /* Get each element: */
+ UIDetailsElement *pElement = pElementItem->toElement();
+ /* Check if this element is of required type: */
+ if (pElement->elementType() == type)
+ {
+ if (fToggled && pElement->isClosed())
+ {
+ m_pAnimationCallback->addNotifier(pElement);
+ pElement->open();
+ }
+ else if (!fToggled && pElement->isOpened())
+ {
+ m_pAnimationCallback->addNotifier(pElement);
+ pElement->close();
+ }
+ }
+ }
+ }
+ /* Update layout: */
+ updateLayout();
+}
+
+void UIDetailsModel::sltToggleAnimationFinished(DetailsElementType enmType, bool fToggled)
+{
+ /* Cleanup animation callback: */
+ delete m_pAnimationCallback;
+ m_pAnimationCallback = 0;
+
+ /* Mark animation finished: */
+ foreach (UIDetailsItem *pSetItem, m_pRoot->items())
+ {
+ foreach (UIDetailsItem *pElementItem, pSetItem->items())
+ {
+ UIDetailsElement *pElement = pElementItem->toElement();
+ if (pElement->elementType() == enmType)
+ pElement->markAnimationFinished();
+ }
+ }
+ /* Update layout: */
+ updateLayout();
+
+ /* Update element open/close status: */
+ if (m_categories.contains(enmType))
+ {
+ m_categories[enmType] = fToggled;
+ gEDataManager->setSelectorWindowDetailsElements(m_categories);
+ }
+}
+
+void UIDetailsModel::prepare()
+{
+ /* Prepare things: */
+ prepareScene();
+ prepareRoot();
+ prepareContextMenu();
+ loadSettings();
+}
+
+void UIDetailsModel::prepareScene()
+{
+ m_pScene = new QGraphicsScene(this);
+ if (m_pScene)
+ m_pScene->installEventFilter(this);
+}
+
+void UIDetailsModel::prepareRoot()
+{
+ m_pRoot = new UIDetailsGroup(scene());
+}
+
+void UIDetailsModel::prepareContextMenu()
+{
+ m_pContextMenu = new UIDetailsContextMenu(this);
+}
+
+void UIDetailsModel::loadSettings()
+{
+ loadDetailsCategories();
+ loadDetailsOptions();
+}
+
+void UIDetailsModel::loadDetailsCategories()
+{
+ m_categories = gEDataManager->selectorWindowDetailsElements();
+ m_pContextMenu->updateCategoryStates();
+}
+
+void UIDetailsModel::loadDetailsOptions(DetailsElementType enmType /* = DetailsElementType_Invalid */)
+{
+ /* We will handle DetailsElementType_Invalid as a request to load everything. */
+
+ if (enmType == DetailsElementType_General || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsGeneral = UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_General))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Invalid)
+ m_fOptionsGeneral = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral>(m_fOptionsGeneral | enmOption);
+ }
+ if (m_fOptionsGeneral == UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Invalid)
+ m_fOptionsGeneral = UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Default;
+ }
+
+ if (enmType == DetailsElementType_System || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsSystem = UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_System))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSystem enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Invalid)
+ m_fOptionsSystem = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSystem>(m_fOptionsSystem | enmOption);
+ }
+ if (m_fOptionsSystem == UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Invalid)
+ m_fOptionsSystem = UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Default;
+ }
+
+ if (enmType == DetailsElementType_Display || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsDisplay = UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_Display))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Invalid)
+ m_fOptionsDisplay = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay>(m_fOptionsDisplay | enmOption);
+ }
+ if (m_fOptionsDisplay == UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Invalid)
+ m_fOptionsDisplay = UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Default;
+ }
+
+ if (enmType == DetailsElementType_Storage || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsStorage = UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_Storage))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeStorage enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Invalid)
+ m_fOptionsStorage = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeStorage>(m_fOptionsStorage | enmOption);
+ }
+ if (m_fOptionsStorage == UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Invalid)
+ m_fOptionsStorage = UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Default;
+ }
+
+ if (enmType == DetailsElementType_Audio || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsAudio = UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_Audio))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeAudio enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Invalid)
+ m_fOptionsAudio = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeAudio>(m_fOptionsAudio | enmOption);
+ }
+ if (m_fOptionsAudio == UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Invalid)
+ m_fOptionsAudio = UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Default;
+ }
+
+ if (enmType == DetailsElementType_Network || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsNetwork = UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_Network))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid)
+ m_fOptionsNetwork = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork>(m_fOptionsNetwork | enmOption);
+ }
+ if (m_fOptionsNetwork == UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid)
+ m_fOptionsNetwork = UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Default;
+ }
+
+ if (enmType == DetailsElementType_Serial || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsSerial = UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_Serial))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSerial enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Invalid)
+ m_fOptionsSerial = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSerial>(m_fOptionsSerial | enmOption);
+ }
+ if (m_fOptionsSerial == UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Invalid)
+ m_fOptionsSerial = UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Default;
+ }
+
+ if (enmType == DetailsElementType_USB || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsUsb = UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_USB))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUsb enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Invalid)
+ m_fOptionsUsb = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeUsb>(m_fOptionsUsb | enmOption);
+ }
+ if (m_fOptionsUsb == UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Invalid)
+ m_fOptionsUsb = UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Default;
+ }
+
+ if (enmType == DetailsElementType_SF || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsSharedFolders = UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_SF))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Invalid)
+ m_fOptionsSharedFolders = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders>(m_fOptionsSharedFolders | enmOption);
+ }
+ if (m_fOptionsSharedFolders == UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Invalid)
+ m_fOptionsSharedFolders = UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Default;
+ }
+
+ if (enmType == DetailsElementType_UI || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsUserInterface = UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_UI))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Invalid)
+ m_fOptionsUserInterface = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface>(m_fOptionsUserInterface | enmOption);
+ }
+ if (m_fOptionsUserInterface == UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Invalid)
+ m_fOptionsUserInterface = UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface_Default;
+ }
+
+ if (enmType == DetailsElementType_Description || enmType == DetailsElementType_Invalid)
+ {
+ m_fOptionsDescription = UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Invalid;
+ foreach (const QString &strOption, gEDataManager->vboxManagerDetailsPaneElementOptions(DetailsElementType_Description))
+ {
+ const UIExtraDataMetaDefs::DetailsElementOptionTypeDescription enmOption =
+ gpConverter->fromInternalString<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>(strOption);
+ if (enmOption != UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Invalid)
+ m_fOptionsDescription = static_cast<UIExtraDataMetaDefs::DetailsElementOptionTypeDescription>(m_fOptionsDescription | enmOption);
+ }
+ if (m_fOptionsDescription == UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Invalid)
+ m_fOptionsDescription = UIExtraDataMetaDefs::DetailsElementOptionTypeDescription_Default;
+ }
+
+ m_pContextMenu->updateOptionStates();
+}
+
+void UIDetailsModel::cleanupContextMenu()
+{
+ delete m_pContextMenu;
+ m_pContextMenu = 0;
+}
+
+void UIDetailsModel::cleanupRoot()
+{
+ delete m_pRoot;
+ m_pRoot = 0;
+}
+
+void UIDetailsModel::cleanupScene()
+{
+ delete m_pScene;
+ m_pScene = 0;
+}
+
+void UIDetailsModel::cleanup()
+{
+ /* Cleanup things: */
+ cleanupContextMenu();
+ cleanupRoot();
+ cleanupScene();
+}
+
+bool UIDetailsModel::processContextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent)
+{
+ /* Pass preview context menu instead: */
+ if (QGraphicsItem *pItem = itemAt(pEvent->scenePos()))
+ if (pItem->type() == UIDetailsItemType_Preview)
+ return false;
+
+ /* Adjust the menu then show it: */
+ const QRect availableGeo = gpDesktop->availableGeometry(pEvent->screenPos());
+ QRect geo(pEvent->screenPos(), m_pContextMenu->minimumSizeHint());
+ if (geo.topRight().x() > availableGeo.topRight().x())
+ geo.adjust(availableGeo.topRight().x() - geo.topRight().x(), 0,
+ availableGeo.topRight().x() - geo.topRight().x(), 0);
+ if (geo.bottomLeft().y() > availableGeo.bottomLeft().y())
+ geo.adjust(0, availableGeo.bottomLeft().y() - geo.bottomLeft().y(),
+ 0, availableGeo.bottomLeft().y() - geo.bottomLeft().y());
+ m_pContextMenu->resize(geo.size());
+ m_pContextMenu->move(geo.topLeft());
+ m_pContextMenu->show();
+
+ /* Filter: */
+ return true;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIDetailsElementAnimationCallback implementation. *
+*********************************************************************************************************************************/
+
+UIDetailsElementAnimationCallback::UIDetailsElementAnimationCallback(QObject *pParent, DetailsElementType enmType, bool fToggled)
+ : QObject(pParent)
+ , m_enmType(enmType)
+ , m_fToggled(fToggled)
+{
+}
+
+void UIDetailsElementAnimationCallback::addNotifier(UIDetailsElement *pItem)
+{
+ /* Connect notifier: */
+ connect(pItem, &UIDetailsElement::sigToggleElementFinished,
+ this, &UIDetailsElementAnimationCallback::sltAnimationFinished);
+ /* Remember notifier: */
+ m_notifiers << pItem;
+}
+
+void UIDetailsElementAnimationCallback::sltAnimationFinished()
+{
+ /* Determine notifier: */
+ UIDetailsElement *pItem = qobject_cast<UIDetailsElement*>(sender());
+ /* Disconnect notifier: */
+ disconnect(pItem, &UIDetailsElement::sigToggleElementFinished,
+ this, &UIDetailsElementAnimationCallback::sltAnimationFinished);
+ /* Remove notifier: */
+ m_notifiers.removeAll(pItem);
+ /* Check if we finished: */
+ if (m_notifiers.isEmpty())
+ emit sigAllAnimationFinished(m_enmType, m_fToggled);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsModel.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsModel.h
new file mode 100644
index 00000000..1f47468e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsModel.h
@@ -0,0 +1,310 @@
+/* $Id: UIDetailsModel.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsModel class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIDetailsModel_h
+#define FEQT_INCLUDED_SRC_manager_details_UIDetailsModel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QObject>
+#include <QPointer>
+#include <QSet>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declaration: */
+class QGraphicsItem;
+class QGraphicsScene;
+class QGraphicsSceneContextMenuEvent;
+class QGraphicsView;
+class UIVirtualMachineItem;
+class UIDetails;
+class UIDetailsContextMenu;
+class UIDetailsElement;
+class UIDetailsElementAnimationCallback;
+class UIDetailsGroup;
+class UIDetailsItem;
+class UIDetailsView;
+
+
+/** QObject sub-class used as graphics details model. */
+class UIDetailsModel : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about model root item @a iMinimumWidthHint changed. */
+ void sigRootItemMinimumWidthHintChanged(int iMinimumWidthHint);
+
+ /** Notifies listeners about element link clicked.
+ * @param strCategory Brings details element category.
+ * @param strControl Brings settings control to select.
+ * @param uId Brings ID of the machine details referring. */
+ void sigLinkClicked(const QString &strCategory, const QString &strControl, const QUuid &uId);
+
+public:
+
+ /** Constructs a details model passing @a pParent to the base-class.
+ * @param pParent Brings the details container to embed into. */
+ UIDetailsModel(UIDetails *pParent);
+ /** Destructs a details model. */
+ virtual ~UIDetailsModel() RT_OVERRIDE;
+
+ /** Inits model. */
+ void init();
+
+ /** Returns graphics scene this model belongs to. */
+ QGraphicsScene *scene() const;
+ /** Returns the reference of the first view of the scene(). */
+ UIDetailsView *view() const;
+ /** Returns paint device this model belongs to. */
+ QGraphicsView *paintDevice() const;
+
+ /** Returns graphics item as certain @a position. */
+ QGraphicsItem *itemAt(const QPointF &position) const;
+
+ /** Returns the details pane reference. */
+ UIDetails *details() const { return m_pDetails; }
+
+ /** Returns the root item instance. */
+ UIDetailsItem *root() const;
+
+ /** Updates layout by positioning items manually. */
+ void updateLayout();
+
+ /** Defines virtual machine @a items for this model to reflect. */
+ void setItems(const QList<UIVirtualMachineItem*> &items);
+
+ /** Returns the details categories. */
+ const QMap<DetailsElementType, bool> &categories() const { return m_categories; }
+ /** Defines the details @a categories. */
+ void setCategories(const QMap<DetailsElementType, bool> &categories);
+
+ /** Returns options for General element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral optionsGeneral() const { return m_fOptionsGeneral; }
+ /** Defines @a fOptions for General element. */
+ void setOptionsGeneral(UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral fOptions);
+
+ /** Returns options for System element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSystem optionsSystem() const { return m_fOptionsSystem; }
+ /** Defines @a fOptions for System element. */
+ void setOptionsSystem(UIExtraDataMetaDefs::DetailsElementOptionTypeSystem fOptions);
+
+ /** Returns options for Display element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay optionsDisplay() const { return m_fOptionsDisplay; }
+ /** Defines @a fOptions for Display element. */
+ void setOptionsDisplay(UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay fOptions);
+
+ /** Returns options for Storage element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeStorage optionsStorage() const { return m_fOptionsStorage; }
+ /** Defines @a fOptions for Storage element. */
+ void setOptionsStorage(UIExtraDataMetaDefs::DetailsElementOptionTypeStorage fOptions);
+
+ /** Returns options for Audio element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeAudio optionsAudio() const { return m_fOptionsAudio; }
+ /** Defines @a fOptions for Audio element. */
+ void setOptionsAudio(UIExtraDataMetaDefs::DetailsElementOptionTypeAudio fOptions);
+
+ /** Returns options for Network element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork optionsNetwork() const { return m_fOptionsNetwork; }
+ /** Defines @a fOptions for Network element. */
+ void setOptionsNetwork(UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork fOptions);
+
+ /** Returns options for Serial element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSerial optionsSerial() const { return m_fOptionsSerial; }
+ /** Defines @a fOptions for Serial element. */
+ void setOptionsSerial(UIExtraDataMetaDefs::DetailsElementOptionTypeSerial fOptions);
+
+ /** Returns options for Usb element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeUsb optionsUsb() const { return m_fOptionsUsb; }
+ /** Defines @a fOptions for Usb element. */
+ void setOptionsUsb(UIExtraDataMetaDefs::DetailsElementOptionTypeUsb fOptions);
+
+ /** Returns options for Shared Folders element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders optionsSharedFolders() const { return m_fOptionsSharedFolders; }
+ /** Defines @a fOptions for Shared Folders element. */
+ void setOptionsSharedFolders(UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders fOptions);
+
+ /** Returns options for User Interface element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface optionsUserInterface() const { return m_fOptionsUserInterface; }
+ /** Defines @a fOptions for User Interface element. */
+ void setOptionsUserInterface(UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface fOptions);
+
+ /** Returns options for Description element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeDescription optionsDescription() const { return m_fOptionsDescription; }
+ /** Defines @a fOptions for Description element. */
+ void setOptionsDescription(UIExtraDataMetaDefs::DetailsElementOptionTypeDescription fOptions);
+
+public slots:
+
+ /** Handle details view resize. */
+ void sltHandleViewResize();
+
+ /** Handles chooser pane signal about group toggle started. */
+ void sltHandleToggleStarted();
+ /** Handles chooser pane signal about group toggle finished. */
+ void sltHandleToggleFinished();
+
+ /** Handle extra-data categories change. */
+ void sltHandleExtraDataCategoriesChange();
+ /** Handle extra-data options change for category of certain @a enmType. */
+ void sltHandleExtraDataOptionsChange(DetailsElementType enmType);
+
+ /** Handles request to start toggle details element of certain @a enmType, making element @a fToggled. */
+ void sltToggleElements(DetailsElementType type, bool fToggled);
+
+protected:
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles sigal about details element of certain @a enmType toggling finished, making element @a fToggled. */
+ void sltToggleAnimationFinished(DetailsElementType type, bool fToggled);
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares scene. */
+ void prepareScene();
+ /** Prepares root. */
+ void prepareRoot();
+ /** Prepares context-menu. */
+ void prepareContextMenu();
+ /** Loads settings. */
+ void loadSettings();
+ /** Loads details categories. */
+ void loadDetailsCategories();
+ /** Loads details options for certain category @a enmType.
+ * @note enmType equal to DetailsElementType_Invalid means load everything. */
+ void loadDetailsOptions(DetailsElementType enmType = DetailsElementType_Invalid);
+
+ /** Cleanups context-menu. */
+ void cleanupContextMenu();
+ /** Cleanups root. */
+ void cleanupRoot();
+ /** Cleanups scene. */
+ void cleanupScene();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** Performs handling for allowed context menu @a pEvent. */
+ bool processContextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent);
+
+ /** Holds the details reference. */
+ UIDetails *m_pDetails;
+
+ /** Holds the graphics scene reference. */
+ QGraphicsScene *m_pScene;
+ /** Holds the root element instance. */
+ UIDetailsGroup *m_pRoot;
+ /** Holds the element animation callback instance. */
+ UIDetailsElementAnimationCallback *m_pAnimationCallback;
+
+ /** Holds the details categories. */
+ QMap<DetailsElementType, bool> m_categories;
+
+ /** Holds the options for General element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral m_fOptionsGeneral;
+ /** Holds the options for System element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSystem m_fOptionsSystem;
+ /** Holds the options for Display element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay m_fOptionsDisplay;
+ /** Holds the options for Storage element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeStorage m_fOptionsStorage;
+ /** Holds the options for Audio element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeAudio m_fOptionsAudio;
+ /** Holds the options for Network element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork m_fOptionsNetwork;
+ /** Holds the options for Serial element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSerial m_fOptionsSerial;
+ /** Holds the options for Usb element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeUsb m_fOptionsUsb;
+ /** Holds the options for Shared Folders element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders m_fOptionsSharedFolders;
+ /** Holds the options for User Interface element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeUserInterface m_fOptionsUserInterface;
+ /** Holds the options for Description element. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeDescription m_fOptionsDescription;
+
+ /** Holds the context-menu instance. */
+ UIDetailsContextMenu *m_pContextMenu;
+};
+
+
+/** QObject sub-class used as details element animation callback. */
+class UIDetailsElementAnimationCallback : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about all animations finished.
+ * @param enmType Brings the type of element item which was animated.
+ * @param fToggled Brings whether elements being toggled to be closed or opened. */
+ void sigAllAnimationFinished(DetailsElementType enmType, bool fToggled);
+
+public:
+
+ /** Constructors details element animation callback passing @a pParent to the base-class.
+ * @param enmType Brings the type of element item which was animated.
+ * @param fToggled Brings whether elements being toggled to be closed or opened. */
+ UIDetailsElementAnimationCallback(QObject *pParent, DetailsElementType enmType, bool fToggled);
+
+ /** Adds notifier for a certain details @a pItem. */
+ void addNotifier(UIDetailsElement *pItem);
+
+private slots:
+
+ /** Handles a signal about animation finnished. */
+ void sltAnimationFinished();
+
+private:
+
+ /** Holds the list of elements which notifies this callback about completion. */
+ QList<UIDetailsElement*> m_notifiers;
+ /** Holds the type of element item which was animated. */
+ DetailsElementType m_enmType;
+ /** Holds whether elements being toggled to be closed or opened. */
+ bool m_fToggled;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIDetailsModel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsSet.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsSet.cpp
new file mode 100644
index 00000000..4dc5db91
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsSet.cpp
@@ -0,0 +1,771 @@
+/* $Id: UIDetailsSet.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsSet class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPainter>
+#include <QStyle>
+#include <QStyleOptionGraphicsItem>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDetailsElements.h"
+#include "UIDetailsModel.h"
+#include "UIDetailsSet.h"
+#include "UIMedium.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualMachineItemCloud.h"
+#include "UIVirtualMachineItemLocal.h"
+
+/* COM includes: */
+#include "CUSBController.h"
+#include "CUSBDeviceFilters.h"
+
+
+UIDetailsSet::UIDetailsSet(UIDetailsItem *pParent)
+ : UIDetailsItem(pParent)
+ , m_pMachineItem(0)
+ , m_fFullSet(true)
+ , m_fIsLocal(true)
+ , m_fHasDetails(false)
+ , m_configurationAccessLevel(ConfigurationAccessLevel_Null)
+ , m_pBuildStep(0)
+ , m_iBackgroundDarknessStart(115)
+ , m_iBackgroundDarknessFinal(150)
+{
+ /* Add set to the parent group: */
+ parentItem()->addItem(this);
+
+ /* Prepare set: */
+ prepareSet();
+
+ /* Prepare connections: */
+ prepareConnections();
+}
+
+UIDetailsSet::~UIDetailsSet()
+{
+ /* Cleanup items: */
+ clearItems();
+
+ /* Remove set from the parent group: */
+ parentItem()->removeItem(this);
+}
+
+void UIDetailsSet::clearSet()
+{
+ /* Clear passed arguments: */
+ m_pMachineItem = 0;
+ m_comMachine = CMachine();
+ m_comCloudMachine = CCloudMachine();
+}
+
+void UIDetailsSet::buildSet(UIVirtualMachineItem *pMachineItem, bool fFullSet, const QMap<DetailsElementType, bool> &settings)
+{
+ /* Remember passed arguments: */
+ m_pMachineItem = pMachineItem;
+ m_fIsLocal = m_pMachineItem->itemType() == UIVirtualMachineItemType_Local;
+ m_fHasDetails = m_pMachineItem->hasDetails();
+ m_fFullSet = fFullSet;
+ m_settings = settings;
+
+ /* Prepare a list of types to build: */
+ QList<DetailsElementType> types;
+
+ /* Make sure we have details: */
+ if (m_fHasDetails)
+ {
+ /* Special handling wrt item type: */
+ switch (m_pMachineItem->itemType())
+ {
+ case UIVirtualMachineItemType_Local:
+ {
+ /* Get local machine: */
+ m_comMachine = m_pMachineItem->toLocal()->machine();
+
+ /* Compose a list of types to build: */
+ if (m_fFullSet)
+ types << DetailsElementType_General << DetailsElementType_System << DetailsElementType_Preview
+ << DetailsElementType_Display << DetailsElementType_Storage << DetailsElementType_Audio
+ << DetailsElementType_Network << DetailsElementType_Serial << DetailsElementType_USB
+ << DetailsElementType_SF << DetailsElementType_UI << DetailsElementType_Description;
+ else
+ types << DetailsElementType_General << DetailsElementType_System << DetailsElementType_Preview;
+
+ /* Take into account USB controller restrictions: */
+ const CUSBDeviceFilters &filters = m_comMachine.GetUSBDeviceFilters();
+ if (filters.isNull() || !m_comMachine.GetUSBProxyAvailable())
+ m_settings.remove(DetailsElementType_USB);
+
+ break;
+ }
+ case UIVirtualMachineItemType_CloudReal:
+ {
+ /* Get cloud machine: */
+ m_comCloudMachine = m_pMachineItem->toCloud()->machine();
+
+ /* Compose a list of types to build: */
+ types << DetailsElementType_General;
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Cleanup if new types differs from old: */
+ if (m_types != types)
+ {
+ clearItems();
+ m_elements.clear();
+ updateGeometry();
+ }
+
+ /* Remember new types: */
+ m_types = types;
+
+ /* Build or emit fake signal: */
+ if (m_fHasDetails)
+ rebuildSet();
+ else
+ emit sigBuildDone();
+}
+
+void UIDetailsSet::updateLayout()
+{
+ /* Prepare variables: */
+ const int iMargin = data(SetData_Margin).toInt();
+ const int iSpacing = data(SetData_Spacing).toInt();
+ const int iMaximumWidth = geometry().width();
+ UIDetailsElement *pPreviewElement = element(DetailsElementType_Preview);
+ const bool fPreviewVisible = pPreviewElement && pPreviewElement->isVisible();
+ const int iPreviewWidth = fPreviewVisible ? pPreviewElement->minimumWidthHint() : 0;
+ const int iPreviewHeight = fPreviewVisible ? pPreviewElement->minimumHeightHint() : 0;
+ int iVerticalIndent = iMargin;
+ int iPreviewGroupHeight = 0;
+ bool fPreviewGroupUnfinished = fPreviewVisible;
+ QList<UIDetailsElement*> listPreviewGroup;
+ m_listPreviewGroup.clear();
+ m_listOutsideGroup.clear();
+
+ /* Layout all the items but Preview: */
+ foreach (UIDetailsItem *pItem, items())
+ {
+ /* Make sure item exists: */
+ AssertPtrReturnVoid(pItem);
+ /* Skip item if hidden: */
+ if (!pItem->isVisible())
+ continue;
+
+ /* Acquire element type: */
+ UIDetailsElement *pElement = pItem->toElement();
+ AssertPtrReturnVoid(pElement);
+ const DetailsElementType enmElementType = pElement->elementType();
+ /* Skip Preview element: */
+ if (enmElementType == DetailsElementType_Preview)
+ continue;
+
+ /* Calculate element size: */
+ QSizeF elementSize;
+
+ /* If we haven't finished filling Preview group: */
+ if (fPreviewGroupUnfinished)
+ {
+ /* For Preview group we have limited element width: */
+ elementSize.setWidth(iMaximumWidth - (iSpacing + iPreviewWidth));
+ /* Resize element to width to get corresponding height: */
+ pElement->resize(elementSize.width(), pElement->geometry().height());
+ /* Now we can get element height based on width above: */
+ elementSize.setHeight(pElement->minimumHeightHint());
+ /* Resize element to height based on width above: */
+ pElement->resize(pElement->geometry().width(), elementSize.height());
+
+ /* Calculate remaining vertical space: */
+ const int iRemainingSpace = (iPreviewHeight + iSpacing) - iPreviewGroupHeight;
+
+ /* If last element height is at least two times taller than the remaining space: */
+ if (elementSize.height() / 2 > iRemainingSpace)
+ {
+ /* We should stop filling Preview group now: */
+ fPreviewGroupUnfinished = false;
+
+ /* Advance indent only if there is remaining space at all: */
+ if (iRemainingSpace > 0)
+ iVerticalIndent += iRemainingSpace;
+ }
+ /* Otherwise last element can still be inserted to Preview group: */
+ else
+ {
+ /* Advance Preview group height: */
+ iPreviewGroupHeight += (elementSize.height() + iSpacing);
+ /* Append last Preview group element: */
+ listPreviewGroup << pElement;
+ m_listPreviewGroup << enmElementType;
+ }
+ }
+
+ /* If we have finished filling Preview group: */
+ if (!fPreviewGroupUnfinished)
+ {
+ /* Calculate element width: */
+ elementSize.setWidth(iMaximumWidth);
+ /* Resize element to width to get corresponding height: */
+ pElement->resize(elementSize.width(), pElement->geometry().height());
+ /* Now we can get element height based on width above: */
+ elementSize.setHeight(pElement->minimumHeightHint());
+ /* Resize element to height based on width above: */
+ pElement->resize(pElement->geometry().width(), elementSize.height());
+ /* Append last Outside group element: */
+ m_listOutsideGroup << enmElementType;
+ }
+
+ /* Move element: */
+ pElement->setPos(0, iVerticalIndent);
+ /* Layout element content: */
+ pElement->updateLayout();
+
+ /* Advance indent: */
+ iVerticalIndent += (elementSize.height() + iSpacing);
+ }
+
+ /* Make sure last opened Preview group item, if exists, consumes rest of vertical space: */
+ if (!listPreviewGroup.isEmpty())
+ {
+ /* Calculate remaining vertical space: */
+ const int iRemainingSpace = (iPreviewHeight + iSpacing) - iPreviewGroupHeight;
+ if (iRemainingSpace > 0)
+ {
+ /* Look for last opened element: */
+ int iLastOpenedElement = -1;
+ foreach (UIDetailsElement *pElement, listPreviewGroup)
+ if (pElement->isOpened())
+ iLastOpenedElement = listPreviewGroup.indexOf(pElement);
+
+ /* If at least one is opened: */
+ if (iLastOpenedElement != -1)
+ {
+ /* Resize element to width to get corresponding height: */
+ UIDetailsElement *pFoundOne = listPreviewGroup.at(iLastOpenedElement);
+ pFoundOne->resize(pFoundOne->geometry().width(), pFoundOne->geometry().height() + iRemainingSpace);
+
+ /* Adjust subsequent element positions: */
+ for (int i = iLastOpenedElement + 1; i < listPreviewGroup.size(); ++i)
+ {
+ UIDetailsElement *pIteratedOne = listPreviewGroup.at(i);
+ pIteratedOne->setPos(pIteratedOne->geometry().x(), pIteratedOne->geometry().y() + iRemainingSpace);
+ }
+
+ /* Layout element content: */
+ pFoundOne->updateLayout();
+ }
+ }
+ }
+
+ /* If Preview exists: */
+ if (fPreviewVisible)
+ {
+ /* Align it to the right corner if there is at least one element in the Preview group.
+ * Otherwise we can put it to the left corner to be able to take whole the space. */
+ if (!listPreviewGroup.isEmpty())
+ pPreviewElement->setPos(iMaximumWidth - iPreviewWidth, iMargin);
+ else
+ pPreviewElement->setPos(0, iMargin);
+
+ /* Resize it to it's size if there is at least one element in the Preview group.
+ * Otherwise we can take whole the horizontal space we have. */
+ int iWidth = iPreviewWidth;
+ int iHeight = iPreviewHeight;
+ if (listPreviewGroup.isEmpty())
+ iWidth = iMaximumWidth;
+ if (!pPreviewElement->isAnimationRunning() && !pPreviewElement->isClosed())
+ iHeight += iPreviewGroupHeight - (iPreviewHeight + iSpacing);
+ pPreviewElement->resize(iWidth, iHeight);
+
+ /* Layout element content: */
+ pPreviewElement->updateLayout();
+ }
+
+ /* Set layout update procedure cause hints to be invalidated,
+ * so we have to update geometry to recalculate them: */
+ updateGeometry();
+}
+
+void UIDetailsSet::sltBuildStep(const QUuid &uStepId, int iStepNumber)
+{
+ /* Cleanup build-step: */
+ delete m_pBuildStep;
+ m_pBuildStep = 0;
+
+ /* Is step id valid? */
+ if (uStepId != m_uSetId)
+ return;
+
+ /* Step number feats the bounds: */
+ if (iStepNumber >= 0 && iStepNumber < m_types.size())
+ {
+ /* Load details settings: */
+ const DetailsElementType enmElementType = m_types.at(iStepNumber);
+ /* Should the element be visible? */
+ bool fVisible = m_settings.contains(enmElementType);
+ /* Should the element be opened? */
+ bool fOpen = fVisible && m_settings[enmElementType];
+
+ /* Check if element is present already: */
+ UIDetailsElement *pElement = element(enmElementType);
+ if (pElement && fOpen)
+ pElement->open(false);
+ /* Create element if necessary: */
+ bool fJustCreated = false;
+ if (!pElement)
+ {
+ fJustCreated = true;
+ pElement = createElement(enmElementType, fOpen);
+ }
+
+ /* Show element if necessary: */
+ if (fVisible && !pElement->isVisible())
+ {
+ /* Show the element: */
+ pElement->show();
+ /* Recursively update size-hint: */
+ pElement->updateGeometry();
+ /* Update layout: */
+ model()->updateLayout();
+ }
+ /* Hide element if necessary: */
+ else if (!fVisible && pElement->isVisible())
+ {
+ /* Hide the element: */
+ pElement->hide();
+ /* Recursively update size-hint: */
+ updateGeometry();
+ /* Update layout: */
+ model()->updateLayout();
+ }
+ /* Update model if necessary: */
+ else if (fJustCreated)
+ model()->updateLayout();
+
+ /* For visible element: */
+ if (pElement->isVisible())
+ {
+ /* Create next build-step: */
+ m_pBuildStep = new UIPrepareStep(this, pElement, uStepId, iStepNumber + 1);
+
+ /* Build element: */
+ pElement->updateAppearance();
+ }
+ /* For invisible element: */
+ else
+ {
+ /* Just build next step: */
+ sltBuildStep(uStepId, iStepNumber + 1);
+ }
+ }
+ /* Step number out of bounds: */
+ else
+ {
+ /* Update model: */
+ model()->updateLayout();
+ /* Repaint all the items: */
+ foreach (UIDetailsItem *pItem, items())
+ pItem->update();
+ /* Notify listener about build done: */
+ emit sigBuildDone();
+ }
+}
+
+void UIDetailsSet::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *)
+{
+ /* Paint background: */
+ paintBackground(pPainter, pOptions);
+}
+
+QString UIDetailsSet::description() const
+{
+ return tr("Contains the details of virtual machine '%1'").arg(m_pMachineItem->name());
+}
+
+void UIDetailsSet::addItem(UIDetailsItem *pItem)
+{
+ switch (pItem->type())
+ {
+ case UIDetailsItemType_Element:
+ {
+ UIDetailsElement *pElement = pItem->toElement();
+ DetailsElementType type = pElement->elementType();
+ AssertMsg(!m_elements.contains(type), ("Element already added!"));
+ m_elements.insert(type, pItem);
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid item type!"));
+ break;
+ }
+ }
+}
+
+void UIDetailsSet::removeItem(UIDetailsItem *pItem)
+{
+ switch (pItem->type())
+ {
+ case UIDetailsItemType_Element:
+ {
+ UIDetailsElement *pElement = pItem->toElement();
+ DetailsElementType type = pElement->elementType();
+ AssertMsg(m_elements.contains(type), ("Element do not present (type = %d)!", (int)type));
+ m_elements.remove(type);
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid item type!"));
+ break;
+ }
+ }
+}
+
+QList<UIDetailsItem*> UIDetailsSet::items(UIDetailsItemType enmType /* = UIDetailsItemType_Element */) const
+{
+ switch (enmType)
+ {
+ case UIDetailsItemType_Element: return m_elements.values();
+ case UIDetailsItemType_Any: return items(UIDetailsItemType_Element);
+ default: AssertMsgFailed(("Invalid item type!")); break;
+ }
+ return QList<UIDetailsItem*>();
+}
+
+bool UIDetailsSet::hasItems(UIDetailsItemType enmType /* = UIDetailsItemType_Element */) const
+{
+ switch (enmType)
+ {
+ case UIDetailsItemType_Element: return !m_elements.isEmpty();
+ case UIDetailsItemType_Any: return hasItems(UIDetailsItemType_Element);
+ default: AssertMsgFailed(("Invalid item type!")); break;
+ }
+ return false;
+}
+
+void UIDetailsSet::clearItems(UIDetailsItemType enmType /* = UIDetailsItemType_Element */)
+{
+ switch (enmType)
+ {
+ case UIDetailsItemType_Element:
+ {
+ foreach (int iKey, m_elements.keys())
+ delete m_elements[iKey];
+ AssertMsg(m_elements.isEmpty(), ("Set items cleanup failed!"));
+ break;
+ }
+ case UIDetailsItemType_Any:
+ {
+ clearItems(UIDetailsItemType_Element);
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid item type!"));
+ break;
+ }
+ }
+}
+
+UIDetailsElement *UIDetailsSet::element(DetailsElementType enmElementType) const
+{
+ UIDetailsItem *pItem = m_elements.value(enmElementType, 0);
+ if (pItem)
+ return pItem->toElement();
+ return 0;
+}
+
+int UIDetailsSet::minimumWidthHint() const
+{
+ /* Zero if has no details: */
+ if (!hasDetails())
+ return 0;
+
+ /* Prepare variables: */
+ const int iSpacing = data(SetData_Spacing).toInt();
+ int iMinimumWidthHintPreview = 0;
+ int iMinimumWidthHintInGroup = 0;
+ int iMinimumWidthHintOutGroup = 0;
+
+ /* Take into account all the elements: */
+ foreach (UIDetailsItem *pItem, items())
+ {
+ /* Make sure item exists: */
+ AssertPtrReturn(pItem, 0);
+ /* Skip item if hidden: */
+ if (!pItem->isVisible())
+ continue;
+
+ /* Acquire element type: */
+ UIDetailsElement *pElement = pItem->toElement();
+ AssertPtrReturn(pElement, 0);
+ DetailsElementType enmElementType = pElement->elementType();
+
+ /* Calculate corresponding hints: */
+ if (enmElementType == DetailsElementType_Preview)
+ iMinimumWidthHintPreview = pItem->minimumWidthHint();
+ else
+ {
+ if (m_listPreviewGroup.contains(enmElementType))
+ iMinimumWidthHintInGroup = qMax(iMinimumWidthHintInGroup, pItem->minimumWidthHint());
+ else if (m_listOutsideGroup.contains(enmElementType))
+ iMinimumWidthHintOutGroup = qMax(iMinimumWidthHintOutGroup, pItem->minimumWidthHint());
+ }
+ }
+
+ /* Append minimum width of Preview and Preview group: */
+ int iMinimumWidthHint = 0;
+ if (iMinimumWidthHintPreview)
+ iMinimumWidthHint += iMinimumWidthHintPreview;
+ if (iMinimumWidthHintInGroup)
+ iMinimumWidthHint += iMinimumWidthHintInGroup;
+ if (iMinimumWidthHintPreview && iMinimumWidthHintInGroup)
+ iMinimumWidthHint += iSpacing;
+ /* Compare with minimum width of Outside group: */
+ iMinimumWidthHint = qMax(iMinimumWidthHint, iMinimumWidthHintOutGroup);
+ /* Return result: */
+ return iMinimumWidthHint;
+}
+
+int UIDetailsSet::minimumHeightHint() const
+{
+ /* Zero if has no details: */
+ if (!hasDetails())
+ return 0;
+
+ /* Prepare variables: */
+ const int iMargin = data(SetData_Margin).toInt();
+ const int iSpacing = data(SetData_Spacing).toInt();
+ int iMinimumHeightHintPreview = 0;
+ int iMinimumHeightHintInGroup = 0;
+ int iMinimumHeightHintOutGroup = 0;
+
+ /* Take into account all the elements: */
+ foreach (UIDetailsItem *pItem, items())
+ {
+ /* Make sure item exists: */
+ AssertPtrReturn(pItem, 0);
+ /* Skip item if hidden: */
+ if (!pItem->isVisible())
+ continue;
+
+ /* Acquire element type: */
+ UIDetailsElement *pElement = pItem->toElement();
+ AssertPtrReturn(pElement, 0);
+ DetailsElementType enmElementType = pElement->elementType();
+
+ /* Calculate corresponding hints: */
+ if (enmElementType == DetailsElementType_Preview)
+ iMinimumHeightHintPreview += pItem->minimumHeightHint();
+ else
+ {
+ if (m_listPreviewGroup.contains(enmElementType))
+ iMinimumHeightHintInGroup += (pItem->minimumHeightHint() + iSpacing);
+ else if (m_listOutsideGroup.contains(enmElementType))
+ iMinimumHeightHintOutGroup += (pItem->minimumHeightHint() + iSpacing);
+ }
+ }
+ /* Minus last spacing: */
+ if (iMinimumHeightHintInGroup > 0)
+ iMinimumHeightHintInGroup -= iSpacing;
+ if (iMinimumHeightHintOutGroup > 0)
+ iMinimumHeightHintOutGroup -= iSpacing;
+
+ /* Append minimum height of Preview and Preview group: */
+ int iMinimumHeightHint = qMax(iMinimumHeightHintPreview, iMinimumHeightHintInGroup);
+ /* Add spacing if necessary: */
+ if (!m_listPreviewGroup.isEmpty() && !m_listOutsideGroup.isEmpty())
+ iMinimumHeightHint += iSpacing;
+ /* Add Outside group height if necessary: */
+ if (!m_listOutsideGroup.isEmpty())
+ iMinimumHeightHint += iMinimumHeightHintOutGroup;
+ /* And two margins finally: */
+ iMinimumHeightHint += 2 * iMargin;
+ /* Return result: */
+ return iMinimumHeightHint;
+}
+
+void UIDetailsSet::sltMachineStateChange(const QUuid &uId)
+{
+ /* For local VMs only: */
+ if (!m_fIsLocal)
+ return;
+
+ /* Make sure VM is set: */
+ if (m_comMachine.isNull())
+ return;
+
+ /* Is this our VM changed? */
+ if (m_comMachine.GetId() != uId)
+ return;
+
+ /* Update appearance: */
+ rebuildSet();
+}
+
+void UIDetailsSet::sltMachineAttributesChange(const QUuid &uId)
+{
+ /* For local VMs only: */
+ if (!m_fIsLocal)
+ return;
+
+ /* Make sure VM is set: */
+ if (m_comMachine.isNull())
+ return;
+
+ /* Is this our VM changed? */
+ if (m_comMachine.GetId() != uId)
+ return;
+
+ /* Update appearance: */
+ rebuildSet();
+}
+
+void UIDetailsSet::sltMediumEnumerated(const QUuid &uId)
+{
+ /* For local VMs only: */
+ if (!m_fIsLocal)
+ return;
+
+ /* Make sure VM is set: */
+ if (m_comMachine.isNull())
+ return;
+
+ /* Is this our medium changed? */
+ const UIMedium guiMedium = uiCommon().medium(uId);
+ if ( guiMedium.isNull()
+ || !guiMedium.machineIds().contains(m_comMachine.GetId()))
+ return;
+
+ /* Update appearance: */
+ rebuildSet();
+}
+
+void UIDetailsSet::prepareSet()
+{
+ /* Setup size-policy: */
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+}
+
+void UIDetailsSet::prepareConnections()
+{
+ /* Global-events connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange, this, &UIDetailsSet::sltMachineStateChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange, this, &UIDetailsSet::sltMachineAttributesChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSessionStateChange, this, &UIDetailsSet::sltMachineAttributesChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotTake, this, &UIDetailsSet::sltMachineAttributesChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotDelete, this, &UIDetailsSet::sltMachineAttributesChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotChange, this, &UIDetailsSet::sltMachineAttributesChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotRestore, this, &UIDetailsSet::sltMachineAttributesChange);
+
+ /* Meidum-enumeration connections: */
+ connect(&uiCommon(), &UICommon::sigMediumEnumerated, this, &UIDetailsSet::sltMediumEnumerated);
+}
+
+QVariant UIDetailsSet::data(int iKey) const
+{
+ /* Provide other members with required data: */
+ switch (iKey)
+ {
+ /* Layout hints: */
+ case SetData_Margin: return 1;
+ case SetData_Spacing: return 1;
+ /* Default: */
+ default: break;
+ }
+ return QVariant();
+}
+
+void UIDetailsSet::rebuildSet()
+{
+ /* Make sure we have details: */
+ if (!m_fHasDetails)
+ return;
+
+ /* Recache properties: */
+ m_configurationAccessLevel = m_pMachineItem->configurationAccessLevel();
+
+ /* Cleanup build-step: */
+ delete m_pBuildStep;
+ m_pBuildStep = 0;
+
+ /* Generate new set-id: */
+ m_uSetId = QUuid::createUuid();
+
+ /* Request to build first step: */
+ emit sigBuildStep(m_uSetId, 0);
+}
+
+UIDetailsElement *UIDetailsSet::createElement(DetailsElementType enmElementType, bool fOpen)
+{
+ /* Element factory: */
+ switch (enmElementType)
+ {
+ case DetailsElementType_General: return new UIDetailsElementGeneral(this, fOpen);
+ case DetailsElementType_System: return new UIDetailsElementSystem(this, fOpen);
+ case DetailsElementType_Preview: return new UIDetailsElementPreview(this, fOpen);
+ case DetailsElementType_Display: return new UIDetailsElementDisplay(this, fOpen);
+ case DetailsElementType_Storage: return new UIDetailsElementStorage(this, fOpen);
+ case DetailsElementType_Audio: return new UIDetailsElementAudio(this, fOpen);
+ case DetailsElementType_Network: return new UIDetailsElementNetwork(this, fOpen);
+ case DetailsElementType_Serial: return new UIDetailsElementSerial(this, fOpen);
+ case DetailsElementType_USB: return new UIDetailsElementUSB(this, fOpen);
+ case DetailsElementType_SF: return new UIDetailsElementSF(this, fOpen);
+ case DetailsElementType_UI: return new UIDetailsElementUI(this, fOpen);
+ case DetailsElementType_Description: return new UIDetailsElementDescription(this, fOpen);
+ default: AssertFailed(); break; /* Shut up, MSC! */
+ }
+ return 0;
+}
+
+void UIDetailsSet::paintBackground(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions) const
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare variables: */
+ const QRect optionRect = pOptions->rect;
+
+ /* Acquire background color: */
+ const QColor backgroundColor = QApplication::palette().color(QPalette::Active, QPalette::Window);
+
+ /* Paint default background: */
+ QColor bcTone1 = backgroundColor.darker(m_iBackgroundDarknessStart);
+ QColor bcTone2 = backgroundColor.darker(m_iBackgroundDarknessFinal);
+ QLinearGradient gradientDefault(optionRect.topLeft(), optionRect.bottomRight());
+ gradientDefault.setColorAt(0, bcTone1);
+ gradientDefault.setColorAt(1, bcTone2);
+ pPainter->fillRect(optionRect, gradientDefault);
+
+ /* Restore painter: */
+ pPainter->restore();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsSet.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsSet.h
new file mode 100644
index 00000000..78340864
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsSet.h
@@ -0,0 +1,247 @@
+/* $Id: UIDetailsSet.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsSet class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIDetailsSet_h
+#define FEQT_INCLUDED_SRC_manager_details_UIDetailsSet_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIDetailsItem.h"
+#include "UIExtraDataDefs.h"
+#include "UISettingsDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudMachine.h"
+#include "CMachine.h"
+
+/* Forward declarations: */
+class UIVirtualMachineItem;
+
+/* Using declarations: */
+using namespace UISettingsDefs;
+
+/** UIDetailsItem extension implementing set item. */
+class UIDetailsSet : public UIDetailsItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** RTTI item type. */
+ enum { Type = UIDetailsItemType_Set };
+
+ /** Constructs set item, passing pParent to the base-class. */
+ UIDetailsSet(UIDetailsItem *pParent);
+ /** Destructs set item. */
+ virtual ~UIDetailsSet() RT_OVERRIDE;
+
+ /** @name Item stuff.
+ * @{ */
+ /** Cleanups set, wiping out machine-item and [cloud]machine information for good. */
+ void clearSet();
+ /** Builds set based on passed @a pMachineItem.
+ * @param fFullSet Brings whether full set should be built.
+ * @param settings Brings details related settings. */
+ void buildSet(UIVirtualMachineItem *pMachineItem, bool fFullSet, const QMap<DetailsElementType, bool> &settings);
+
+ /** Returns cached machine. */
+ const CMachine &machine() const { return m_comMachine; }
+ /** Returns cached cloud machine. */
+ const CCloudMachine &cloudMachine() const { return m_comCloudMachine; }
+
+ /** Returns whether set is of local type. */
+ bool isLocal() const { return m_fIsLocal; }
+ /** Returns whether set has cached details. */
+ bool hasDetails() const { return m_fHasDetails; }
+ /** Returns configuration access level. */
+ ConfigurationAccessLevel configurationAccessLevel() const { return m_configurationAccessLevel; }
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates layout. */
+ virtual void updateLayout() RT_OVERRIDE;
+ /** @} */
+
+protected slots:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles request about starting step build.
+ * @param uStepId Brings the step ID.
+ * @param iStepNumber Brings the step number. */
+ virtual void sltBuildStep(const QUuid &uStepId, int iStepNumber) RT_OVERRIDE;
+ /** @} */
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns RTTI item type. */
+ virtual int type() const RT_OVERRIDE { return Type; }
+
+ /** Returns the description of the item. */
+ virtual QString description() const RT_OVERRIDE;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Adds child @a pItem. */
+ virtual void addItem(UIDetailsItem *pItem) RT_OVERRIDE;
+ /** Removes child @a pItem. */
+ virtual void removeItem(UIDetailsItem *pItem) RT_OVERRIDE;
+
+ /** Returns children items of certain @a enmType. */
+ virtual QList<UIDetailsItem*> items(UIDetailsItemType type = UIDetailsItemType_Element) const RT_OVERRIDE;
+ /** Returns whether there are children items of certain @a enmType. */
+ virtual bool hasItems(UIDetailsItemType type = UIDetailsItemType_Element) const RT_OVERRIDE;
+ /** Clears children items of certain @a enmType. */
+ virtual void clearItems(UIDetailsItemType type = UIDetailsItemType_Element) RT_OVERRIDE;
+
+ /** Returns details element of certain @a enmElementType. */
+ UIDetailsElement *element(DetailsElementType enmElementType) const;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Returns minimum width-hint. */
+ virtual int minimumWidthHint() const RT_OVERRIDE;
+ /** Returns minimum height-hint. */
+ virtual int minimumHeightHint() const RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles machine-state change for item with @a uId. */
+ void sltMachineStateChange(const QUuid &uId);
+ /** Handles machine-attribute change for item with @a uId. */
+ void sltMachineAttributesChange(const QUuid &uId);
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles enumerated signal for medium with @a uId. */
+ void sltMediumEnumerated(const QUuid &uId);
+ /** @} */
+
+private:
+
+ /** Data field types. */
+ enum SetItemData
+ {
+ /* Layout hints: */
+ SetData_Margin,
+ SetData_Spacing
+ };
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares set. */
+ void prepareSet();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns abstractly stored data value for certain @a iKey. */
+ QVariant data(int iKey) const;
+
+ /** Rebuilds set based on cached machine item. */
+ void rebuildSet();
+
+ /** Creates element of specified @a enmElementType in @a fOpen state. */
+ UIDetailsElement *createElement(DetailsElementType enmElementType, bool fOpen);
+ /** @} */
+
+ /** @name Painting stuff.
+ * @{ */
+ /** Paints background using specified @a pPainter and certain @a pOptions. */
+ void paintBackground(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions) const;
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Holds the machine-item this set is built for. */
+ UIVirtualMachineItem *m_pMachineItem;
+ /** Holds whether whether full set should be built. */
+ bool m_fFullSet;
+ /** Holds the details related settings. */
+ QMap<DetailsElementType, bool> m_settings;
+
+ /** Holds the machine reference. */
+ CMachine m_comMachine;
+ /** Holds the cloud machine reference. */
+ CCloudMachine m_comCloudMachine;
+
+ /** Holds whether set is of local type. */
+ bool m_fIsLocal;
+ /** Holds whether set has details. */
+ bool m_fHasDetails;
+ /** Holds configuration access level. */
+ ConfigurationAccessLevel m_configurationAccessLevel;
+
+ /** Holds the build step instance. */
+ UIPrepareStep *m_pBuildStep;
+ /** Holds the list of types to build steps for. */
+ QList<DetailsElementType> m_types;
+ /** Holds the generated set ID. */
+ QUuid m_uSetId;
+
+ /** Holds the start background darkness. */
+ int m_iBackgroundDarknessStart;
+ /** Holds the final background darkness. */
+ int m_iBackgroundDarknessFinal;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Holds the map of generated detail elements. */
+ QMap<int, UIDetailsItem*> m_elements;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds the list of elements in the Preview group. */
+ QList<DetailsElementType> m_listPreviewGroup;
+ /** Holds the list of elements in the Outside group. */
+ QList<DetailsElementType> m_listOutsideGroup;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIDetailsSet_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsView.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsView.cpp
new file mode 100644
index 00000000..250a3ae5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsView.cpp
@@ -0,0 +1,207 @@
+/* $Id: UIDetailsView.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsView class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QScrollBar>
+
+/* GUI includes: */
+#include "UIDetails.h"
+#include "UIDetailsItem.h"
+#include "UIDetailsModel.h"
+#include "UIDetailsView.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/** QAccessibleWidget extension used as an accessibility interface for Details-view. */
+class UIAccessibilityInterfaceForUIDetailsView : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating Details-view accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UIDetailsView"))
+ return new UIAccessibilityInterfaceForUIDetailsView(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ UIAccessibilityInterfaceForUIDetailsView(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::List)
+ {}
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), 0);
+
+ /* What amount of children root has? */
+ const int cChildCount = view()->details()->model()->root()->items().size();
+
+ /* Return amount of children root has (if there are many of children): */
+ if (cChildCount > 1)
+ return cChildCount;
+
+ /* Return the number of children lone root child has (otherwise): */
+ return view()->details()->model()->root()->items().first()->items().size();
+ }
+
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* What amount of children root has? */
+ const int cChildCount = view()->details()->model()->root()->items().size();
+
+ /* Return the root child with the passed iIndex (if there are many of children): */
+ if (cChildCount > 1)
+ return QAccessible::queryAccessibleInterface(view()->details()->model()->root()->items().at(iIndex));
+
+ /* Return the lone root child's child with the passed iIndex (otherwise): */
+ return QAccessible::queryAccessibleInterface(view()->details()->model()->root()->items().first()->items().at(iIndex));
+ }
+
+ /** Returns the index of passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), -1);
+ /* Make sure child is valid: */
+ AssertReturn(pChild, -1);
+
+ /* Acquire item itself: */
+ UIDetailsItem *pChildItem = qobject_cast<UIDetailsItem*>(pChild->object());
+
+ /* Return the index of item in it's parent: */
+ return pChildItem && pChildItem->parentItem()
+ ? pChildItem->parentItem()->items().indexOf(pChildItem)
+ : -1;
+ }
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), QString());
+
+ /* Return view tool-tip: */
+ Q_UNUSED(enmTextRole);
+ return view()->whatsThis();
+ }
+
+private:
+
+ /** Returns corresponding Details-view. */
+ UIDetailsView *view() const { return qobject_cast<UIDetailsView*>(widget()); }
+};
+
+
+UIDetailsView::UIDetailsView(UIDetails *pParent)
+ : QIWithRetranslateUI<QIGraphicsView>(pParent)
+ , m_pDetails(pParent)
+ , m_iMinimumWidthHint(0)
+{
+ prepare();
+}
+
+void UIDetailsView::sltMinimumWidthHintChanged(int iHint)
+{
+ /* Is there something changed? */
+ if (m_iMinimumWidthHint == iHint)
+ return;
+
+ /* Remember new value: */
+ m_iMinimumWidthHint = iHint;
+ if (m_iMinimumWidthHint <= 0)
+ m_iMinimumWidthHint = 1;
+
+ /* Set minimum view width according passed width-hint: */
+ setMinimumWidth(2 * frameWidth() + m_iMinimumWidthHint + verticalScrollBar()->sizeHint().width());
+
+ /* Update scene-rect: */
+ updateSceneRect();
+}
+
+void UIDetailsView::retranslateUi()
+{
+ /* Translate this: */
+ setWhatsThis(tr("Contains a list of Virtual Machine details."));
+}
+
+void UIDetailsView::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI<QIGraphicsView>::resizeEvent(pEvent);
+ /* Notify listeners: */
+ emit sigResized();
+
+ /* Update scene-rect: */
+ updateSceneRect();
+}
+
+void UIDetailsView::prepare()
+{
+ /* Install Details-view accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUIDetailsView::pFactory);
+
+ /* Prepare palette: */
+ QPalette pal = QApplication::palette();
+ pal.setColor(QPalette::Active, QPalette::Base, pal.color(QPalette::Active, QPalette::Window));
+ pal.setColor(QPalette::Inactive, QPalette::Base, pal.color(QPalette::Inactive, QPalette::Window));
+ setPalette(pal);
+
+ /* Setup frame: */
+ setFrameShape(QFrame::NoFrame);
+ setFrameShadow(QFrame::Plain);
+ setAlignment(Qt::AlignLeft | Qt::AlignTop);
+
+ /* Setup scroll-bars policy: */
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ /* Update scene-rect: */
+ updateSceneRect();
+
+ /* Translate finally: */
+ retranslateUi();
+}
+
+void UIDetailsView::updateSceneRect()
+{
+ setSceneRect(0, 0, m_iMinimumWidthHint, height());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsView.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsView.h
new file mode 100644
index 00000000..bec9a644
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIDetailsView.h
@@ -0,0 +1,88 @@
+/* $Id: UIDetailsView.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsView class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIDetailsView_h
+#define FEQT_INCLUDED_SRC_manager_details_UIDetailsView_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIGraphicsView.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class UIDetails;
+
+/* Graphics details-view: */
+class UIDetailsView : public QIWithRetranslateUI<QIGraphicsView>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about resize. */
+ void sigResized();
+
+public:
+
+ /** Constructs a details-view passing @a pParent to the base-class.
+ * @param pParent Brings the details container to embed into. */
+ UIDetailsView(UIDetails *pParent);
+
+ /** Returns the details reference. */
+ UIDetails *details() const { return m_pDetails; }
+
+public slots:
+
+ /** Handles minimum width @a iHint change. */
+ void sltMinimumWidthHintChanged(int iHint);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates scene rectangle. */
+ void updateSceneRect();
+
+ /** Holds the details reference. */
+ UIDetails *m_pDetails;
+
+ /** Updates scene rectangle. */
+ int m_iMinimumWidthHint;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIDetailsView_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIMachinePreview.cpp b/src/VBox/Frontends/VirtualBox/src/manager/details/UIMachinePreview.cpp
new file mode 100644
index 00000000..d0742b30
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIMachinePreview.cpp
@@ -0,0 +1,600 @@
+/* $Id: UIMachinePreview.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachinePreview class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QActionGroup>
+#include <QGraphicsSceneContextMenuEvent>
+#include <QMenu>
+#include <QPainter>
+#include <QStyle>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UIMachinePreview.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIExtraDataManager.h"
+#include "UIImageTools.h"
+#include "UIConverter.h"
+#include "UIIconPool.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CDisplay.h"
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+
+UIMachinePreview::UIMachinePreview(QIGraphicsWidget *pParent)
+ : QIWithRetranslateUI4<QIGraphicsWidget>(pParent)
+ , m_pUpdateTimer(new QTimer(this))
+ , m_pUpdateTimerMenu(0)
+ , m_dRatio((double)QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 16)
+ , m_iMargin(0)
+ , m_enmPreset(AspectRatioPreset_16x9)
+ , m_pPreviewImg(0)
+{
+ prepare();
+}
+
+UIMachinePreview::~UIMachinePreview()
+{
+ cleanup();
+}
+
+void UIMachinePreview::setMachine(const CMachine& comMachine)
+{
+ /* Pause: */
+ stop();
+
+ /* Assign new machine: */
+ m_comMachine = comMachine;
+
+ /* Fetch machine data: */
+ m_strPreviewName = tr("No preview");
+ if (!m_comMachine.isNull())
+ m_strPreviewName = m_comMachine.GetAccessible() ? m_comMachine.GetName() :
+ tr("Inaccessible");
+
+ /* Resume: */
+ restart();
+}
+
+CMachine UIMachinePreview::machine() const
+{
+ return m_comMachine;
+}
+
+void UIMachinePreview::retranslateUi()
+{
+ /* Translate actions: */
+ m_actions.value(PreviewUpdateIntervalType_Disabled)->setText(tr("Update disabled"));
+ m_actions.value(PreviewUpdateIntervalType_500ms)->setText(tr("Every 0.5 s"));
+ m_actions.value(PreviewUpdateIntervalType_1000ms)->setText(tr("Every 1 s"));
+ m_actions.value(PreviewUpdateIntervalType_2000ms)->setText(tr("Every 2 s"));
+ m_actions.value(PreviewUpdateIntervalType_5000ms)->setText(tr("Every 5 s"));
+ m_actions.value(PreviewUpdateIntervalType_10000ms)->setText(tr("Every 10 s"));
+}
+
+void UIMachinePreview::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
+{
+ recalculatePreviewRectangle();
+ sltRecreatePreview();
+
+ /* Call to base-class: */
+ QIGraphicsWidget::resizeEvent(pEvent);
+}
+
+void UIMachinePreview::showEvent(QShowEvent *pEvent)
+{
+ restart();
+
+ /* Call to base-class: */
+ QIGraphicsWidget::showEvent(pEvent);
+}
+
+void UIMachinePreview::hideEvent(QHideEvent *pEvent)
+{
+ stop();
+
+ /* Call to base-class: */
+ QIGraphicsWidget::hideEvent(pEvent);
+}
+
+void UIMachinePreview::contextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent)
+{
+ QAction *pReturn = m_pUpdateTimerMenu->exec(pEvent->screenPos(), 0);
+ if (pReturn)
+ {
+ PreviewUpdateIntervalType enmInterval = static_cast<PreviewUpdateIntervalType>(pReturn->data().toInt());
+ setUpdateInterval(enmInterval, true);
+ restart();
+ }
+}
+
+void UIMachinePreview::paint(QPainter *pPainter, const QStyleOptionGraphicsItem*, QWidget*)
+{
+ /* Where should the content go: */
+ QRect cr = contentsRect().toRect();
+ if (!cr.isValid())
+ return;
+
+ /* If there is a preview image available: */
+ if (m_pPreviewImg)
+ {
+ /* Draw empty monitor frame: */
+ pPainter->drawPixmap(cr.x() + m_iMargin, cr.y() + m_iMargin, *m_emptyPixmaps.value(m_enmPreset));
+
+ /* Move image to viewport center: */
+ QRect imageRect(QPoint(0, 0), m_pPreviewImg->size());
+ imageRect.moveCenter(m_vRect.center());
+
+#ifdef VBOX_WS_MAC
+ /* Set composition-mode to opaque: */
+ pPainter->setCompositionMode(QPainter::CompositionMode_Source);
+ /* Replace translucent background with black one: */
+ pPainter->fillRect(imageRect, QColor(Qt::black));
+ /* Return default composition-mode back: */
+ pPainter->setCompositionMode(QPainter::CompositionMode_SourceAtop);
+#endif /* VBOX_WS_MAC */
+
+ /* Draw preview image: */
+ pPainter->drawImage(imageRect.topLeft(), *m_pPreviewImg);
+ }
+ else
+ {
+ /* Draw full monitor frame: */
+ pPainter->drawPixmap(cr.x() + m_iMargin, cr.y() + m_iMargin, *m_fullPixmaps.value(m_enmPreset));
+
+ /* Paint preview name: */
+ QFont font = pPainter->font();
+ font.setBold(true);
+ int fFlags = Qt::AlignCenter | Qt::TextWordWrap;
+ float h = m_vRect.size().height() * .2;
+ QRect r;
+ /* Make a little magic to find out if the given text fits into our rectangle.
+ * Decrease the font pixel size as long as it doesn't fit. */
+ int cMax = 30;
+ do
+ {
+ h = h * .8;
+ font.setPixelSize((int)h);
+ pPainter->setFont(font);
+ r = pPainter->boundingRect(m_vRect, fFlags, m_strPreviewName);
+ }
+ while ((r.height() > m_vRect.height() || r.width() > m_vRect.width()) && cMax-- != 0);
+ pPainter->setPen(Qt::white);
+ pPainter->drawText(m_vRect, fFlags, m_strPreviewName);
+ }
+}
+
+QSizeF UIMachinePreview::sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint /* = QSizeF() */) const
+{
+ if (enmWhich == Qt::MinimumSize)
+ {
+ AssertReturn(m_emptyPixmaps.contains(m_enmPreset),
+ QIGraphicsWidget::sizeHint(enmWhich, constraint));
+ QSize size = m_sizes.value(m_enmPreset);
+ if (m_iMargin != 0)
+ {
+ size.setWidth(size.width() - 2 * m_iMargin);
+ size.setHeight(size.height() - 2 * m_iMargin);
+ }
+ return size;
+ }
+
+ /* Call to base-class: */
+ return QIGraphicsWidget::sizeHint(enmWhich, constraint);
+}
+
+void UIMachinePreview::sltMachineStateChange(const QUuid &uId)
+{
+ /* Make sure its the event for our machine: */
+ if (m_comMachine.isNull() || m_comMachine.GetId() != uId)
+ return;
+
+ /* Restart the preview: */
+ restart();
+}
+
+void UIMachinePreview::sltRecreatePreview()
+{
+ /* Skip invisible preview: */
+ if (!isVisible())
+ return;
+
+ /* Cleanup previous image: */
+ if (m_pPreviewImg)
+ {
+ delete m_pPreviewImg;
+ m_pPreviewImg = 0;
+ }
+
+ /* Fetch actual machine-state: */
+ const KMachineState enmMachineState = m_comMachine.isNull() ? KMachineState_Null : m_comMachine.GetState();
+
+ /* We are creating preview only for assigned and accessible VMs: */
+ if (!m_comMachine.isNull() && enmMachineState != KMachineState_Null &&
+ m_vRect.width() > 0 && m_vRect.height() > 0)
+ {
+ /* Prepare image: */
+ QImage image;
+
+ /* Use 10x9 as the aspect-ratio preset by default: */
+ AspectRatioPreset enmPreset = AspectRatioPreset_16x9;
+
+ /* Preview update enabled? */
+ if (m_pUpdateTimer->interval() > 0)
+ {
+ /* Depending on machine state: */
+ switch (enmMachineState)
+ {
+ /* If machine is in SAVED/RESTORING state: */
+ case KMachineState_Saved:
+ case KMachineState_AbortedSaved:
+ case KMachineState_Restoring:
+ {
+ /* Use the screenshot from saved-state if possible: */
+ ULONG uGuestWidth = 0, uGuestHeight = 0;
+ QVector<BYTE> screenData = m_comMachine.ReadSavedScreenshotToArray(0, KBitmapFormat_PNG, uGuestWidth, uGuestHeight);
+
+ /* Make sure screen-data is OK: */
+ if (!m_comMachine.isOk() || screenData.isEmpty())
+ break;
+
+ if (uGuestWidth > 0 && uGuestHeight > 0)
+ {
+ /* Calculate aspect-ratio: */
+ const double dAspectRatio = (double)uGuestWidth / uGuestHeight;
+ /* Look for the best aspect-ratio preset: */
+ enmPreset = bestAspectRatioPreset(dAspectRatio, m_ratios);
+ }
+
+ /* Create image based on shallow copy or screenshot data,
+ * scale image down if necessary to the size possible to reflect: */
+ image = QImage::fromData(screenData.data(), screenData.size(), "PNG")
+ .scaled(imageAspectRatioSize(m_vRect.size(), QSize(uGuestWidth, uGuestHeight)),
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ /* And detach that copy to make it deep: */
+ image.detach();
+ /* Dim image to give it required look: */
+ dimImage(image);
+ break;
+ }
+ /* If machine is in RUNNING/PAUSED state: */
+ case KMachineState_Running:
+ case KMachineState_Paused:
+ {
+ /* Make sure session state is Locked: */
+ if (m_comSession.GetState() != KSessionState_Locked)
+ break;
+
+ /* Make sure console is OK: */
+ CConsole console = m_comSession.GetConsole();
+ if (!m_comSession.isOk() || console.isNull())
+ break;
+ /* Make sure display is OK: */
+ CDisplay display = console.GetDisplay();
+ if (!console.isOk() || display.isNull())
+ break;
+
+ /* Acquire guest-screen attributes: */
+ LONG iOriginX = 0, iOriginY = 0;
+ ULONG uGuestWidth = 0, uGuestHeight = 0, uBpp = 0;
+ KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
+ display.GetScreenResolution(0, uGuestWidth, uGuestHeight, uBpp, iOriginX, iOriginY, monitorStatus);
+ if (uGuestWidth > 0 && uGuestHeight > 0)
+ {
+ /* Calculate aspect-ratio: */
+ const double dAspectRatio = (double)uGuestWidth / uGuestHeight;
+ /* Look for the best aspect-ratio preset: */
+ enmPreset = bestAspectRatioPreset(dAspectRatio, m_ratios);
+ }
+
+ /* Calculate size corresponding to aspect-ratio: */
+ const QSize size = imageAspectRatioSize(m_vRect.size(), QSize(uGuestWidth, uGuestHeight));
+
+ /* Use direct VM content: */
+ QVector<BYTE> screenData = display.TakeScreenShotToArray(0, size.width(), size.height(), KBitmapFormat_BGR0);
+
+ /* Make sure screen-data is OK: */
+ if (!display.isOk() || screenData.isEmpty())
+ break;
+
+ /* Make sure screen-data size is valid: */
+ const int iExpectedSize = size.width() * size.height() * 4;
+ const int iActualSize = screenData.size();
+ if (iActualSize != iExpectedSize)
+ {
+ AssertMsgFailed(("Invalid screen-data size '%d', should be '%d'!\n", iActualSize, iExpectedSize));
+ break;
+ }
+
+ /* Create image based on shallow copy of acquired data: */
+ QImage tempImage(screenData.data(), size.width(), size.height(), QImage::Format_RGB32);
+ image = tempImage;
+ /* And detach that copy to make it deep: */
+ image.detach();
+ /* Dim image to give it required look for PAUSED state: */
+ if (enmMachineState == KMachineState_Paused)
+ dimImage(image);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* If image initialized: */
+ if (!image.isNull())
+ {
+ /* Shallow copy that image: */
+ m_pPreviewImg = new QImage(image);
+ /* And detach that copy to make it deep: */
+ m_pPreviewImg->detach();
+ }
+
+ /* If preset changed: */
+ if (m_enmPreset != enmPreset)
+ {
+ /* Save new preset: */
+ m_enmPreset = enmPreset;
+ /* And update geometry: */
+ updateGeometry();
+ emit sigSizeHintChanged();
+ }
+ }
+
+ /* Redraw preview in any case: */
+ update();
+}
+
+void UIMachinePreview::prepare()
+{
+ /* Create session instance: */
+ m_comSession.createInstance(CLSID_Session);
+
+ /* Cache aspect-ratio preset settings: */
+ const QIcon empty16x10 = UIIconPool::iconSet(":/preview_empty_16to10_242x167px.png");
+ const QIcon empty16x9 = UIIconPool::iconSet(":/preview_empty_16to9_242x155px.png");
+ const QIcon empty4x3 = UIIconPool::iconSet(":/preview_empty_4to3_242x192px.png");
+ const QIcon full16x10 = UIIconPool::iconSet(":/preview_full_16to10_242x167px.png");
+ const QIcon full16x9 = UIIconPool::iconSet(":/preview_full_16to9_242x155px.png");
+ const QIcon full4x3 = UIIconPool::iconSet(":/preview_full_4to3_242x192px.png");
+
+ // WORKAROUND:
+ // Since we don't have x3 and x4 HiDPI icons yet,
+ // and we hadn't enabled automatic up-scaling for now,
+ // we have to make sure m_dRatio is within possible bounds.
+ const QList<QSize> sizes = empty16x10.availableSizes();
+ if (sizes.size() >= 2)
+ m_dRatio = qMin(m_dRatio, (double)sizes.last().width() / sizes.first().width());
+
+ m_sizes.insert(AspectRatioPreset_16x10, QSize(242 * m_dRatio, 167 * m_dRatio));
+ m_sizes.insert(AspectRatioPreset_16x9, QSize(242 * m_dRatio, 155 * m_dRatio));
+ m_sizes.insert(AspectRatioPreset_4x3, QSize(242 * m_dRatio, 192 * m_dRatio));
+ m_ratios.insert(AspectRatioPreset_16x10, (double)16/10);
+ m_ratios.insert(AspectRatioPreset_16x9, (double)16/9);
+ m_ratios.insert(AspectRatioPreset_4x3, (double)4/3);
+ m_emptyPixmaps.insert(AspectRatioPreset_16x10, new QPixmap(empty16x10.pixmap(m_sizes.value(AspectRatioPreset_16x10))));
+ m_emptyPixmaps.insert(AspectRatioPreset_16x9, new QPixmap(empty16x9.pixmap(m_sizes.value(AspectRatioPreset_16x9))));
+ m_emptyPixmaps.insert(AspectRatioPreset_4x3, new QPixmap(empty4x3.pixmap(m_sizes.value(AspectRatioPreset_4x3))));
+ m_fullPixmaps.insert(AspectRatioPreset_16x10, new QPixmap(full16x10.pixmap(m_sizes.value(AspectRatioPreset_16x10))));
+ m_fullPixmaps.insert(AspectRatioPreset_16x9, new QPixmap(full16x9.pixmap(m_sizes.value(AspectRatioPreset_16x9))));
+ m_fullPixmaps.insert(AspectRatioPreset_4x3, new QPixmap(full4x3.pixmap(m_sizes.value(AspectRatioPreset_4x3))));
+
+ /* Setup contents (depends on presets above!): */
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ /* Create the context menu: */
+ m_pUpdateTimerMenu = new QMenu;
+ QActionGroup *pUpdateTimeG = new QActionGroup(this);
+ pUpdateTimeG->setExclusive(true);
+ for(int i = 0; i < PreviewUpdateIntervalType_Max; ++i)
+ {
+ QAction *pUpdateTime = new QAction(pUpdateTimeG);
+ pUpdateTime->setData(i);
+ pUpdateTime->setCheckable(true);
+ pUpdateTimeG->addAction(pUpdateTime);
+ m_pUpdateTimerMenu->addAction(pUpdateTime);
+ m_actions[static_cast<PreviewUpdateIntervalType>(i)] = pUpdateTime;
+ }
+ m_pUpdateTimerMenu->insertSeparator(m_actions[static_cast<PreviewUpdateIntervalType>(PreviewUpdateIntervalType_500ms)]);
+
+ /* Initialize with the new update interval: */
+ setUpdateInterval(gEDataManager->selectorWindowPreviewUpdateInterval(), false);
+
+ /* Setup connections: */
+ connect(m_pUpdateTimer, &QTimer::timeout, this, &UIMachinePreview::sltRecreatePreview);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UIMachinePreview::sltMachineStateChange);
+
+ /* Retranslate the UI */
+ retranslateUi();
+}
+
+void UIMachinePreview::cleanup()
+{
+ /* Close any open session: */
+ if (m_comSession.GetState() == KSessionState_Locked)
+ m_comSession.UnlockMachine();
+
+ /* Destroy background images: */
+ foreach (const AspectRatioPreset &enmPreset, m_emptyPixmaps.keys())
+ {
+ delete m_emptyPixmaps.value(enmPreset);
+ m_emptyPixmaps.remove(enmPreset);
+ }
+ foreach (const AspectRatioPreset &enmPreset, m_fullPixmaps.keys())
+ {
+ delete m_fullPixmaps.value(enmPreset);
+ m_fullPixmaps.remove(enmPreset);
+ }
+
+ /* Destroy preview image: */
+ if (m_pPreviewImg)
+ delete m_pPreviewImg;
+
+ /* Destroy update timer: */
+ if (m_pUpdateTimerMenu)
+ delete m_pUpdateTimerMenu;
+}
+
+void UIMachinePreview::setUpdateInterval(PreviewUpdateIntervalType interval, bool fSave)
+{
+ switch (interval)
+ {
+ case PreviewUpdateIntervalType_Disabled:
+ {
+ /* Stop the timer: */
+ m_pUpdateTimer->stop();
+ /* And continue with other cases: */
+ }
+ RT_FALL_THRU();
+ case PreviewUpdateIntervalType_500ms:
+ case PreviewUpdateIntervalType_1000ms:
+ case PreviewUpdateIntervalType_2000ms:
+ case PreviewUpdateIntervalType_5000ms:
+ case PreviewUpdateIntervalType_10000ms:
+ {
+ /* Set the timer interval: */
+ m_pUpdateTimer->setInterval(gpConverter->toInternalInteger(interval));
+ /* Check corresponding action: */
+ m_actions[interval]->setChecked(true);
+ break;
+ }
+ case PreviewUpdateIntervalType_Max:
+ break;
+ }
+ if (fSave)
+ gEDataManager->setSelectorWindowPreviewUpdateInterval(interval);
+}
+
+void UIMachinePreview::recalculatePreviewRectangle()
+{
+ /* Contents rectangle: */
+ QRect cr = contentsRect().toRect();
+ m_vRect = cr.adjusted( 21 * m_dRatio + m_iMargin, 21 * m_dRatio + m_iMargin,
+ -21 * m_dRatio - m_iMargin, -21 * m_dRatio - m_iMargin);
+}
+
+void UIMachinePreview::restart()
+{
+ /* Fetch the latest machine-state: */
+ KMachineState enmMachineState = m_comMachine.isNull() ? KMachineState_Null : m_comMachine.GetState();
+
+ /* Reopen session if necessary: */
+ if (m_comSession.GetState() == KSessionState_Locked)
+ m_comSession.UnlockMachine();
+ if (!m_comMachine.isNull())
+ {
+ /* Lock the session for the current machine: */
+ if (enmMachineState == KMachineState_Running || enmMachineState == KMachineState_Paused)
+ m_comMachine.LockMachine(m_comSession, KLockType_Shared);
+ }
+
+ /* Recreate the preview image: */
+ sltRecreatePreview();
+
+ /* Start the timer if necessary: */
+ if (!m_comMachine.isNull())
+ {
+ if (m_pUpdateTimer->interval() > 0 && enmMachineState == KMachineState_Running)
+ m_pUpdateTimer->start();
+ }
+}
+
+void UIMachinePreview::stop()
+{
+ /* Stop the timer: */
+ m_pUpdateTimer->stop();
+}
+
+/* static */
+UIMachinePreview::AspectRatioPreset UIMachinePreview::bestAspectRatioPreset(const double dAspectRatio,
+ const QMap<AspectRatioPreset, double> &ratios)
+{
+ /* Use 16x9 preset as the 'best' by 'default': */
+ AspectRatioPreset bestPreset = AspectRatioPreset_16x9;
+ /* Calculate minimum diff based on 'default' preset: */
+ double dMinimumDiff = qAbs(dAspectRatio - ratios.value(bestPreset));
+ /* Now look for the 'best' aspect-ratio preset among existing: */
+ for (AspectRatioPreset currentPreset = AspectRatioPreset_16x10;
+ currentPreset <= AspectRatioPreset_4x3;
+ currentPreset = (AspectRatioPreset)(currentPreset + 1))
+ {
+ /* Calculate current diff based on 'current' preset: */
+ const double dDiff = qAbs(dAspectRatio - ratios.value(currentPreset));
+ /* If new 'best' preset found: */
+ if (dDiff < dMinimumDiff)
+ {
+ /* Remember new diff: */
+ dMinimumDiff = dDiff;
+ /* And new preset: */
+ bestPreset = currentPreset;
+ }
+ }
+ /* Return 'best' preset: */
+ return bestPreset;
+}
+
+/* static */
+QSize UIMachinePreview::imageAspectRatioSize(const QSize &hostSize, const QSize &guestSize)
+{
+ /* Make sure host-size and guest-size are valid: */
+ AssertReturn(!hostSize.isNull(), QSize());
+ if (guestSize.isNull())
+ return hostSize;
+
+ /* Calculate host/guest aspect-ratio: */
+ const double dHostAspectRatio = (double)hostSize.width() / hostSize.height();
+ const double dGuestAspectRatio = (double)guestSize.width() / guestSize.height();
+ int iWidth = 0, iHeight = 0;
+ /* Guest-screen more thin by vertical than host-screen: */
+ if (dGuestAspectRatio >= dHostAspectRatio)
+ {
+ /* Get host width: */
+ iWidth = hostSize.width();
+ /* And calculate height based on guest aspect ratio: */
+ iHeight = (int)((double)iWidth / dGuestAspectRatio);
+ /* But no more than host height: */
+ iHeight = qMin(iHeight, hostSize.height());
+ }
+ /* Host-screen more thin by vertical than guest-screen: */
+ else
+ {
+ /* Get host height: */
+ iHeight = hostSize.height();
+ /* And calculate width based on guest aspect ratio: */
+ iWidth = (int)((double)iHeight * dGuestAspectRatio);
+ /* But no more than host width: */
+ iWidth = qMin(iWidth, hostSize.width());
+ }
+ /* Return actual size: */
+ return QSize(iWidth, iHeight);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/details/UIMachinePreview.h b/src/VBox/Frontends/VirtualBox/src/manager/details/UIMachinePreview.h
new file mode 100644
index 00000000..c3a30734
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/details/UIMachinePreview.h
@@ -0,0 +1,218 @@
+/* $Id: UIMachinePreview.h $ */
+/** @file
+ * VBox Qt GUI - UIMachinePreview class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_details_UIMachinePreview_h
+#define FEQT_INCLUDED_SRC_manager_details_UIMachinePreview_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QHash>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIDetailsItem.h"
+#include "UIExtraDataDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+#include "CSession.h"
+
+/* Forward declarations: */
+class QAction;
+class QImage;
+class QPixmap;
+class QMenu;
+class QTimer;
+
+/** QIGraphicsWidget sub-class used as VM Preview widget inside Details pane. */
+class UIMachinePreview : public QIWithRetranslateUI4<QIGraphicsWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Notifies about size-hint changes. */
+ void sigSizeHintChanged();
+ /** @} */
+
+public:
+
+ /** RTTI item type. */
+ enum { Type = UIDetailsItemType_Preview };
+
+ /** Constructs preview element, passing pParent to the base-class. */
+ UIMachinePreview(QIGraphicsWidget *pParent);
+ /** Destructs preview element. */
+ virtual ~UIMachinePreview() RT_OVERRIDE;
+
+ /** @name Item stuff.
+ * @{ */
+ /** Defines @a comMachine to make preview for. */
+ void setMachine(const CMachine &comMachine);
+ /** Retuirns machine we do preview for. */
+ CMachine machine() const;
+ /** @} */
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles hide @a pEvent. */
+ virtual void hideEvent(QHideEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles context-menu @a pEvent. */
+ virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent) RT_OVERRIDE;
+
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns RTTI item type. */
+ virtual int type() const RT_OVERRIDE { return Type; }
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Returns size-hint.
+ * @param enmWhich Brings size-hint type.
+ * @param constraint Brings size constraint. */
+ virtual QSizeF sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint = QSizeF()) const RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles machine-state change for item with @a uId. */
+ void sltMachineStateChange(const QUuid &uId);
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles request to recreate preview. */
+ void sltRecreatePreview();
+ /** @} */
+
+private:
+
+ /** Aspect ratio presets. */
+ enum AspectRatioPreset
+ {
+ AspectRatioPreset_16x10,
+ AspectRatioPreset_16x9,
+ AspectRatioPreset_4x3,
+ };
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Define update @a enmInterval, @a fSave it if requested. */
+ void setUpdateInterval(PreviewUpdateIntervalType enmInterval, bool fSave);
+
+ /** Recalculates preview rectangle. */
+ void recalculatePreviewRectangle();
+
+ /** Restarts preview uppdate routine. */
+ void restart();
+ /** Stops preview uppdate routine. */
+ void stop();
+ /** @} */
+
+ /** Looks for the best aspect-ratio preset for the passed @a dAspectRatio among all the passed @a ratios. */
+ static AspectRatioPreset bestAspectRatioPreset(const double dAspectRatio, const QMap<AspectRatioPreset, double> &ratios);
+ /** Calculates image size suitable to passed @a hostSize and @a guestSize. */
+ static QSize imageAspectRatioSize(const QSize &hostSize, const QSize &guestSize);
+
+ /** @name Item stuff.
+ * @{ */
+ /** Holds the session reference. */
+ CSession m_comSession;
+ /** Holds the machine reference. */
+ CMachine m_comMachine;
+
+ /** Holds the update timer instance. */
+ QTimer *m_pUpdateTimer;
+ /** Holds the update timer menu instance. */
+ QMenu *m_pUpdateTimerMenu;
+ /** Holds the update timer menu action list. */
+ QHash<PreviewUpdateIntervalType, QAction*> m_actions;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds the aspect-ratio of the preview. */
+ double m_dRatio;
+ /** Holds the layout margin. */
+ const int m_iMargin;
+ /** Holds the viewport rectangle. */
+ QRect m_vRect;
+
+ /** Holds the current aspect-ratio preset. */
+ AspectRatioPreset m_enmPreset;
+ /** Holds the aspect-ratio preset sizes. */
+ QMap<AspectRatioPreset, QSize> m_sizes;
+ /** Holds the aspect-ratio preset ratios. */
+ QMap<AspectRatioPreset, double> m_ratios;
+ /** Holds the aspect-ratio preset empty pixmaps. */
+ QMap<AspectRatioPreset, QPixmap*> m_emptyPixmaps;
+ /** Holds the aspect-ratio preset filled pixmaps. */
+ QMap<AspectRatioPreset, QPixmap*> m_fullPixmaps;
+ /** @} */
+
+ /** @name Painting stuff.
+ * @{ */
+ /** Holds the preview image instance. */
+ QImage *m_pPreviewImg;
+ /** Holds the preview name. */
+ QString m_strPreviewName;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_details_UIMachinePreview_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/manager/tools/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UITools.cpp b/src/VBox/Frontends/VirtualBox/src/manager/tools/UITools.cpp
new file mode 100644
index 00000000..b15e122f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UITools.cpp
@@ -0,0 +1,180 @@
+/* $Id: UITools.cpp $ */
+/** @file
+ * VBox Qt GUI - UITools class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UITools.h"
+#include "UIToolsModel.h"
+#include "UIToolsView.h"
+#include "UIVirtualBoxManagerWidget.h"
+
+
+UITools::UITools(UIVirtualBoxManagerWidget *pParent /* = 0 */)
+ : QWidget(pParent, Qt::Popup)
+ , m_pManagerWidget(pParent)
+ , m_pMainLayout(0)
+ , m_pToolsModel(0)
+ , m_pToolsView(0)
+{
+ prepare();
+}
+
+UIActionPool *UITools::actionPool() const
+{
+ return managerWidget()->actionPool();
+}
+
+void UITools::setToolsClass(UIToolClass enmClass)
+{
+ m_pToolsModel->setToolsClass(enmClass);
+}
+
+UIToolClass UITools::toolsClass() const
+{
+ return m_pToolsModel->toolsClass();
+}
+
+void UITools::setToolsType(UIToolType enmType)
+{
+ m_pToolsModel->setToolsType(enmType);
+}
+
+UIToolType UITools::toolsType() const
+{
+ return m_pToolsModel->toolsType();
+}
+
+UIToolType UITools::lastSelectedToolGlobal() const
+{
+ return m_pToolsModel->lastSelectedToolGlobal();
+}
+
+UIToolType UITools::lastSelectedToolMachine() const
+{
+ return m_pToolsModel->lastSelectedToolMachine();
+}
+
+void UITools::setToolClassEnabled(UIToolClass enmClass, bool fEnabled)
+{
+ m_pToolsModel->setToolClassEnabled(enmClass, fEnabled);
+}
+
+bool UITools::toolClassEnabled(UIToolClass enmClass) const
+{
+ return m_pToolsModel->toolClassEnabled(enmClass);
+}
+
+void UITools::setRestrictedToolTypes(const QList<UIToolType> &types)
+{
+ m_pToolsModel->setRestrictedToolTypes(types);
+}
+
+QList<UIToolType> UITools::restrictedToolTypes() const
+{
+ return m_pToolsModel->restrictedToolTypes();
+}
+
+UIToolsItem *UITools::currentItem() const
+{
+ return m_pToolsModel->currentItem();
+}
+
+void UITools::prepare()
+{
+ /* Prepare everything: */
+ prepareContents();
+ prepareConnections();
+
+ /* Init model finally: */
+ initModel();
+}
+
+void UITools::prepareContents()
+{
+ /* Setup own layout rules: */
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);
+
+ /* Prepare main-layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (m_pMainLayout)
+ {
+ m_pMainLayout->setContentsMargins(1, 1, 1, 1);
+ m_pMainLayout->setSpacing(0);
+
+ /* Prepare model: */
+ prepareModel();
+ }
+}
+
+void UITools::prepareModel()
+{
+ /* Prepare model: */
+ m_pToolsModel = new UIToolsModel(this);
+ if (m_pToolsModel)
+ prepareView();
+}
+
+void UITools::prepareView()
+{
+ AssertPtrReturnVoid(m_pToolsModel);
+ AssertPtrReturnVoid(m_pMainLayout);
+
+ /* Prepare view: */
+ m_pToolsView = new UIToolsView(this);
+ if (m_pToolsView)
+ {
+ m_pToolsView->setScene(m_pToolsModel->scene());
+ m_pToolsView->show();
+ setFocusProxy(m_pToolsView);
+
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pToolsView);
+ }
+}
+
+void UITools::prepareConnections()
+{
+ /* Model connections: */
+ connect(m_pToolsModel, &UIToolsModel::sigItemMinimumWidthHintChanged,
+ m_pToolsView, &UIToolsView::sltMinimumWidthHintChanged);
+ connect(m_pToolsModel, &UIToolsModel::sigItemMinimumHeightHintChanged,
+ m_pToolsView, &UIToolsView::sltMinimumHeightHintChanged);
+ connect(m_pToolsModel, &UIToolsModel::sigFocusChanged,
+ m_pToolsView, &UIToolsView::sltFocusChanged);
+
+ /* View connections: */
+ connect(m_pToolsView, &UIToolsView::sigResized,
+ m_pToolsModel, &UIToolsModel::sltHandleViewResized);
+}
+
+void UITools::initModel()
+{
+ m_pToolsModel->init();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UITools.h b/src/VBox/Frontends/VirtualBox/src/manager/tools/UITools.h
new file mode 100644
index 00000000..48c8581c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UITools.h
@@ -0,0 +1,148 @@
+/* $Id: UITools.h $ */
+/** @file
+ * VBox Qt GUI - UITools class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_tools_UITools_h
+#define FEQT_INCLUDED_SRC_manager_tools_UITools_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI icludes: */
+#include "UIExtraDataDefs.h"
+
+/* Forward declarations: */
+class QVBoxLayout;
+class UIActionPool;
+class UIToolsItem;
+class UIToolsModel;
+class UIToolsView;
+class UIVirtualBoxManagerWidget;
+
+/** QWidget extension used as VM Tools-pane. */
+class UITools : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name General stuff.
+ * @{ */
+ /** Notifies listeners about selection changed. */
+ void sigSelectionChanged();
+
+ /** Notifies listeners about expanding started. */
+ void sigExpandingStarted();
+ /** Notifies listeners about expanding finished. */
+ void sigExpandingFinished();
+ /** @} */
+
+public:
+
+ /** Constructs Tools-pane passing @a pParent to the base-class. */
+ UITools(UIVirtualBoxManagerWidget *pParent = 0);
+
+ /** @name General stuff.
+ * @{ */
+ /** Returns the manager-widget reference. */
+ UIVirtualBoxManagerWidget *managerWidget() const { return m_pManagerWidget; }
+
+ /** Returns the action-pool reference. */
+ UIActionPool *actionPool() const;
+
+ /** Return the Tools-model instance. */
+ UIToolsModel *model() const { return m_pToolsModel; }
+ /** Return the Tools-view instance. */
+ UIToolsView *view() const { return m_pToolsView; }
+
+ /** Defines current tools @a enmClass. */
+ void setToolsClass(UIToolClass enmClass);
+ /** Returns current tools class. */
+ UIToolClass toolsClass() const;
+
+ /** Defines current tools @a enmType. */
+ void setToolsType(UIToolType enmType);
+ /** Returns current tools type. */
+ UIToolType toolsType() const;
+
+ /** Returns last selected global tool. */
+ UIToolType lastSelectedToolGlobal() const;
+ /** Returns last selected machine tool. */
+ UIToolType lastSelectedToolMachine() const;
+
+ /** Defines whether certain @a enmClass of tools is @a fEnabled.*/
+ void setToolClassEnabled(UIToolClass enmClass, bool fEnabled);
+ /** Returns whether certain class of tools is enabled.*/
+ bool toolClassEnabled(UIToolClass enmClass) const;
+
+ /** Defines restructed tool @a types. */
+ void setRestrictedToolTypes(const QList<UIToolType> &types);
+ /** Returns restricted tool types. */
+ QList<UIToolType> restrictedToolTypes() const;
+ /** @} */
+
+ /** @name Current item stuff.
+ * @{ */
+ /** Returns current item. */
+ UIToolsItem *currentItem() const;
+ /** @} */
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares contents. */
+ void prepareContents();
+ /** Prepares model. */
+ void prepareModel();
+ /** Prepares view. */
+ void prepareView();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Inits model. */
+ void initModel();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the manager-widget reference. */
+ UIVirtualBoxManagerWidget *m_pManagerWidget;
+
+ /** Holds the main layout instane. */
+ QVBoxLayout *m_pMainLayout;
+ /** Holds the Tools-model instane. */
+ UIToolsModel *m_pToolsModel;
+ /** Holds the Tools-view instane. */
+ UIToolsView *m_pToolsView;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_tools_UITools_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerKeyboard.cpp b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerKeyboard.cpp
new file mode 100644
index 00000000..6c9b37b6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerKeyboard.cpp
@@ -0,0 +1,144 @@
+/* $Id: UIToolsHandlerKeyboard.cpp $ */
+/** @file
+ * VBox Qt GUI - UIToolsHandlerKeyboard class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QKeyEvent>
+
+/* GUI incluedes: */
+#include "UIToolsHandlerKeyboard.h"
+#include "UIToolsModel.h"
+#include "UIToolsItem.h"
+
+
+UIToolsHandlerKeyboard::UIToolsHandlerKeyboard(UIToolsModel *pParent)
+ : QObject(pParent)
+ , m_pModel(pParent)
+{
+}
+
+bool UIToolsHandlerKeyboard::handle(QKeyEvent *pEvent, UIKeyboardEventType enmType) const
+{
+ /* Process passed event: */
+ switch (enmType)
+ {
+ case UIKeyboardEventType_Press: return handleKeyPress(pEvent);
+ case UIKeyboardEventType_Release: return handleKeyRelease(pEvent);
+ }
+ /* Pass event if unknown: */
+ return false;
+}
+
+UIToolsModel *UIToolsHandlerKeyboard::model() const
+{
+ return m_pModel;
+}
+
+bool UIToolsHandlerKeyboard::handleKeyPress(QKeyEvent *pEvent) const
+{
+ /* Which key it was? */
+ switch (pEvent->key())
+ {
+ /* Key UP? */
+ case Qt::Key_Up:
+ /* Key HOME? */
+ case Qt::Key_Home:
+ {
+ /* Determine focus item position: */
+ const int iPosition = model()->navigationList().indexOf(model()->focusItem());
+ /* Determine 'previous' item: */
+ UIToolsItem *pPreviousItem = 0;
+ if (iPosition > 0)
+ {
+ if (pEvent->key() == Qt::Key_Up)
+ for (int i = iPosition - 1; i >= 0; --i)
+ {
+ UIToolsItem *pIteratedItem = model()->navigationList().at(i);
+ if (pIteratedItem->isEnabled())
+ {
+ pPreviousItem = pIteratedItem;
+ break;
+ }
+ }
+ else if (pEvent->key() == Qt::Key_Home)
+ pPreviousItem = model()->navigationList().first();
+ }
+ if (pPreviousItem)
+ {
+ /* Make 'previous' item the current one: */
+ model()->setCurrentItem(pPreviousItem);
+ /* Filter-out this event: */
+ return true;
+ }
+ /* Pass this event: */
+ return false;
+ }
+ /* Key DOWN? */
+ case Qt::Key_Down:
+ /* Key END? */
+ case Qt::Key_End:
+ {
+ /* Determine focus item position: */
+ int iPosition = model()->navigationList().indexOf(model()->focusItem());
+ /* Determine 'next' item: */
+ UIToolsItem *pNextItem = 0;
+ if (iPosition < model()->navigationList().size() - 1)
+ {
+ if (pEvent->key() == Qt::Key_Down)
+ for (int i = iPosition + 1; i < model()->navigationList().size(); ++i)
+ {
+ UIToolsItem *pIteratedItem = model()->navigationList().at(i);
+ if (pIteratedItem->isEnabled())
+ {
+ pNextItem = pIteratedItem;
+ break;
+ }
+ }
+ else if (pEvent->key() == Qt::Key_End)
+ pNextItem = model()->navigationList().last();
+ }
+ if (pNextItem)
+ {
+ /* Make 'next' item the current one: */
+ model()->setCurrentItem(pNextItem);
+ /* Filter-out this event: */
+ return true;
+ }
+ /* Pass this event: */
+ return false;
+ }
+ default:
+ break;
+ }
+ /* Pass all other events: */
+ return false;
+}
+
+bool UIToolsHandlerKeyboard::handleKeyRelease(QKeyEvent *) const
+{
+ /* Pass all events: */
+ return false;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerKeyboard.h b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerKeyboard.h
new file mode 100644
index 00000000..8563bb4d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerKeyboard.h
@@ -0,0 +1,79 @@
+/* $Id: UIToolsHandlerKeyboard.h $ */
+/** @file
+ * VBox Qt GUI - UIToolsHandlerKeyboard class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_tools_UIToolsHandlerKeyboard_h
+#define FEQT_INCLUDED_SRC_manager_tools_UIToolsHandlerKeyboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QObject>
+
+/* Forward declarations: */
+class QKeyEvent;
+class UIToolsModel;
+
+
+/** Keyboard event types. */
+enum UIKeyboardEventType
+{
+ UIKeyboardEventType_Press,
+ UIKeyboardEventType_Release
+};
+
+
+/** QObject extension used as keyboard handler for graphics tools selector. */
+class UIToolsHandlerKeyboard : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs keyboard handler passing @a pParent to the base-class. */
+ UIToolsHandlerKeyboard(UIToolsModel *pParent);
+
+ /** Handles keyboard @a pEvent of certain @a enmType. */
+ bool handle(QKeyEvent *pEvent, UIKeyboardEventType enmType) const;
+
+private:
+
+ /** Returns the parent model reference. */
+ UIToolsModel *model() const;
+
+ /** Handles keyboard press @a pEvent. */
+ bool handleKeyPress(QKeyEvent *pEvent) const;
+ /** Handles keyboard release @a pEvent. */
+ bool handleKeyRelease(QKeyEvent *pEvent) const;
+
+ /** Holds the parent model reference. */
+ UIToolsModel *m_pModel;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_tools_UIToolsHandlerKeyboard_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerMouse.cpp b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerMouse.cpp
new file mode 100644
index 00000000..799dcca4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerMouse.cpp
@@ -0,0 +1,94 @@
+/* $Id: UIToolsHandlerMouse.cpp $ */
+/** @file
+ * VBox Qt GUI - UIToolsHandlerMouse class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGraphicsSceneMouseEvent>
+
+/* GUI incluedes: */
+#include "UIToolsHandlerMouse.h"
+#include "UIToolsModel.h"
+
+
+UIToolsHandlerMouse::UIToolsHandlerMouse(UIToolsModel *pParent)
+ : QObject(pParent)
+ , m_pModel(pParent)
+{
+}
+
+bool UIToolsHandlerMouse::handle(QGraphicsSceneMouseEvent *pEvent, UIMouseEventType enmType) const
+{
+ /* Process passed event: */
+ switch (enmType)
+ {
+ case UIMouseEventType_Press: return handleMousePress(pEvent);
+ case UIMouseEventType_Release: return handleMouseRelease(pEvent);
+ }
+ /* Pass event if unknown: */
+ return false;
+}
+
+UIToolsModel *UIToolsHandlerMouse::model() const
+{
+ return m_pModel;
+}
+
+bool UIToolsHandlerMouse::handleMousePress(QGraphicsSceneMouseEvent *pEvent) const
+{
+ /* Get item under mouse cursor: */
+ QPointF scenePos = pEvent->scenePos();
+ if (QGraphicsItem *pItemUnderMouse = model()->itemAt(scenePos))
+ {
+ /* Which button it was? */
+ switch (pEvent->button())
+ {
+ /* Both buttons: */
+ case Qt::LeftButton:
+ case Qt::RightButton:
+ {
+ /* Which item we just clicked? */
+ UIToolsItem *pClickedItem = qgraphicsitem_cast<UIToolsItem*>(pItemUnderMouse);
+ /* Make clicked item the current one: */
+ if (pClickedItem && pClickedItem->isEnabled())
+ {
+ model()->setCurrentItem(pClickedItem);
+ model()->closeParent();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ /* Pass all other events: */
+ return false;
+}
+
+bool UIToolsHandlerMouse::handleMouseRelease(QGraphicsSceneMouseEvent *) const
+{
+ /* Pass all events: */
+ return false;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerMouse.h b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerMouse.h
new file mode 100644
index 00000000..7214f866
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsHandlerMouse.h
@@ -0,0 +1,79 @@
+/* $Id: UIToolsHandlerMouse.h $ */
+/** @file
+ * VBox Qt GUI - UIToolsHandlerMouse class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_tools_UIToolsHandlerMouse_h
+#define FEQT_INCLUDED_SRC_manager_tools_UIToolsHandlerMouse_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* Forward declarations: */
+class QGraphicsSceneMouseEvent;
+class UIToolsModel;
+class UIToolsItem;
+
+
+/** Mouse event type. */
+enum UIMouseEventType
+{
+ UIMouseEventType_Press,
+ UIMouseEventType_Release
+};
+
+
+/** QObject extension used as mouse handler for graphics tools selector. */
+class UIToolsHandlerMouse : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs mouse handler passing @a pParent to the base-class. */
+ UIToolsHandlerMouse(UIToolsModel *pParent);
+
+ /** Handles mouse @a pEvent of certain @a enmType. */
+ bool handle(QGraphicsSceneMouseEvent *pEvent, UIMouseEventType enmType) const;
+
+private:
+
+ /** Returns the parent model reference. */
+ UIToolsModel *model() const;
+
+ /** Handles mouse press @a pEvent. */
+ bool handleMousePress(QGraphicsSceneMouseEvent *pEvent) const;
+ /** Handles mouse release @a pEvent. */
+ bool handleMouseRelease(QGraphicsSceneMouseEvent *pEvent) const;
+
+ /** Holds the parent model reference. */
+ UIToolsModel *m_pModel;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_manager_tools_UIToolsHandlerMouse_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsItem.cpp b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsItem.cpp
new file mode 100644
index 00000000..8a1e8cc9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsItem.cpp
@@ -0,0 +1,990 @@
+/* $Id: UIToolsItem.cpp $ */
+/** @file
+ * VBox Qt GUI - UIToolsItem class definition.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleObject>
+#include <QApplication>
+#include <QGraphicsScene>
+#include <QPainter>
+#include <QPropertyAnimation>
+#include <QSignalTransition>
+#include <QStateMachine>
+#include <QStyle>
+#include <QStyleOptionGraphicsItem>
+
+/* GUI includes: */
+#include "UIImageTools.h"
+#include "UITools.h"
+#include "UIToolsItem.h"
+#include "UIToolsModel.h"
+#include "UIToolsView.h"
+#include "UIVirtualBoxManager.h"
+
+
+/** QAccessibleObject extension used as an accessibility interface for Tools-view items. */
+class UIAccessibilityInterfaceForUIToolsItem : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating Tools-view accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UIToolsItem"))
+ return new UIAccessibilityInterfaceForUIToolsItem(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ UIAccessibilityInterfaceForUIToolsItem(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Return the parent: */
+ return QAccessible::queryAccessibleInterface(item()->model()->tools()->view());
+ }
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Zero: */
+ return 0;
+ }
+
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int) const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), 0);
+
+ /* Null: */
+ return 0;
+ }
+
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE
+ {
+ /* Search for corresponding child: */
+ for (int i = 0; i < childCount(); ++i)
+ if (child(i) == pChild)
+ return i;
+
+ /* -1 by default: */
+ return -1;
+ }
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE
+ {
+ /* Now goes the mapping: */
+ const QSize itemSize = item()->size().toSize();
+ const QPointF itemPosInScene = item()->mapToScene(QPointF(0, 0));
+ const QPoint itemPosInView = item()->model()->tools()->view()->mapFromScene(itemPosInScene);
+ const QPoint itemPosInScreen = item()->model()->tools()->view()->mapToGlobal(itemPosInView);
+ const QRect itemRectInScreen = QRect(itemPosInScreen, itemSize);
+ return itemRectInScreen;
+ }
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QString());
+
+ switch (enmTextRole)
+ {
+ case QAccessible::Name: return item()->name();
+ /// @todo handle!
+ //case QAccessible::Description: return item()->description();
+ default: break;
+ }
+
+ /* Null-string by default: */
+ return QString();
+ }
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QAccessible::NoRole);
+
+ /* ListItem by default: */
+ return QAccessible::ListItem;
+ }
+
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE
+ {
+ /* Make sure item still alive: */
+ AssertPtrReturn(item(), QAccessible::State());
+
+ /* Compose the state: */
+ QAccessible::State state;
+ state.focusable = true;
+ state.selectable = true;
+
+ /* Compose the state of current item: */
+ if (item() && item() == item()->model()->currentItem())
+ {
+ state.active = true;
+ state.focused = true;
+ state.selected = true;
+ }
+
+ /* Return the state: */
+ return state;
+ }
+
+private:
+
+ /** Returns corresponding Tools-view item. */
+ UIToolsItem *item() const { return qobject_cast<UIToolsItem*>(object()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIToolsItem implementation. *
+*********************************************************************************************************************************/
+
+UIToolsItem::UIToolsItem(QGraphicsScene *pScene,
+ UIToolClass enmClass, UIToolType enmType,
+ const QString &strName, const QIcon &icon)
+ : m_pScene(pScene)
+ , m_enmClass(enmClass)
+ , m_enmType(enmType)
+ , m_icon(icon)
+ , m_strName(strName)
+ , m_fHovered(false)
+ , m_pHoveringMachine(0)
+ , m_pHoveringAnimationForward(0)
+ , m_pHoveringAnimationBackward(0)
+ , m_iAnimationDuration(400)
+ , m_iDefaultValue(0)
+ , m_iHoveredValue(100)
+ , m_iAnimatedValue(m_iDefaultValue)
+ , m_iDefaultLightnessStart(0)
+ , m_iDefaultLightnessFinal(0)
+ , m_iHoverLightnessStart(0)
+ , m_iHoverLightnessFinal(0)
+ , m_iHighlightLightnessStart(0)
+ , m_iHighlightLightnessFinal(0)
+ , m_iPreviousMinimumWidthHint(0)
+ , m_iPreviousMinimumHeightHint(0)
+ , m_iMaximumNameWidth(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIToolsItem::~UIToolsItem()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+UIToolsModel *UIToolsItem::model() const
+{
+ UIToolsModel *pModel = qobject_cast<UIToolsModel*>(QIGraphicsWidget::scene()->parent());
+ AssertMsg(pModel, ("Incorrect graphics scene parent set!"));
+ return pModel;
+}
+
+void UIToolsItem::reconfigure(UIToolClass enmClass, UIToolType enmType,
+ const QIcon &icon, const QString &strName)
+{
+ /* If class is changed: */
+ if (m_enmClass != enmClass)
+ {
+ /* Update linked values: */
+ m_enmClass = enmClass;
+ }
+
+ /* If type is changed: */
+ if (m_enmType != enmType)
+ {
+ /* Update linked values: */
+ m_enmType = enmType;
+ }
+
+ /* Update linked values: */
+ m_icon = icon;
+ updatePixmap();
+
+ /* Update name finally: */
+ reconfigure(strName);
+}
+
+void UIToolsItem::reconfigure(const QString &strName)
+{
+ /* If name is changed: */
+ if (m_strName != strName)
+ {
+ /* Update linked values: */
+ m_strName = strName;
+ updateMinimumNameSize();
+ updateVisibleName();
+ }
+}
+
+UIToolClass UIToolsItem::itemClass() const
+{
+ return m_enmClass;
+}
+
+UIToolType UIToolsItem::itemType() const
+{
+ return m_enmType;
+}
+
+const QIcon &UIToolsItem::icon() const
+{
+ return m_icon;
+}
+
+const QString &UIToolsItem::name() const
+{
+ return m_strName;
+}
+
+void UIToolsItem::setEnabled(bool fEnabled)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::setEnabled(fEnabled);
+
+ /* Update linked values: */
+ updatePixmap();
+}
+
+void UIToolsItem::setHovered(bool fHovered)
+{
+ m_fHovered = fHovered;
+ if (m_fHovered)
+ emit sigHoverEnter();
+ else
+ emit sigHoverLeave();
+}
+
+bool UIToolsItem::isHovered() const
+{
+ return m_fHovered;
+}
+
+void UIToolsItem::updateGeometry()
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::updateGeometry();
+
+ /* We should notify Tools-model if minimum-width-hint was changed: */
+ const int iMinimumWidthHint = minimumWidthHint();
+ if (m_iPreviousMinimumWidthHint != iMinimumWidthHint)
+ {
+ /* Save new minimum-width-hint, notify listener: */
+ m_iPreviousMinimumWidthHint = iMinimumWidthHint;
+ emit sigMinimumWidthHintChanged(m_iPreviousMinimumWidthHint);
+ }
+ /* We should notify Tools-model if minimum-height-hint was changed: */
+ const int iMinimumHeightHint = minimumHeightHint();
+ if (m_iPreviousMinimumHeightHint != iMinimumHeightHint)
+ {
+ /* Save new minimum-height-hint, notify listener: */
+ m_iPreviousMinimumHeightHint = iMinimumHeightHint;
+ emit sigMinimumHeightHintChanged(m_iPreviousMinimumHeightHint);
+ }
+}
+
+int UIToolsItem::minimumWidthHint() const
+{
+ /* Prepare variables: */
+ const int iMargin = data(ToolsItemData_Margin).toInt();
+ const int iSpacing = data(ToolsItemData_Spacing).toInt();
+
+ /* Calculating proposed width: */
+ int iProposedWidth = 0;
+
+ /* Two margins: */
+ iProposedWidth += 2 * iMargin;
+ /* And Tools-item content to take into account: */
+ int iToolsItemWidth = m_pixmapSize.width() +
+ iSpacing +
+ m_minimumNameSize.width();
+ iProposedWidth += iToolsItemWidth;
+
+ /* Return result: */
+ return iProposedWidth;
+}
+
+int UIToolsItem::minimumHeightHint() const
+{
+ /* Prepare variables: */
+ const int iMargin = data(ToolsItemData_Margin).toInt();
+
+ /* Calculating proposed height: */
+ int iProposedHeight = 0;
+
+ /* Two margins: */
+ iProposedHeight += 2 * iMargin;
+ /* And Tools-item content to take into account: */
+ int iToolsItemHeight = qMax(m_pixmapSize.height(),
+ m_minimumNameSize.height());
+ iProposedHeight += iToolsItemHeight;
+
+ /* Return result: */
+ return iProposedHeight;
+}
+
+QSizeF UIToolsItem::sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint /* = QSizeF() */) const
+{
+ /* If Qt::MinimumSize requested: */
+ if (enmWhich == Qt::MinimumSize)
+ return QSizeF(minimumWidthHint(), minimumHeightHint());
+ /* Else call to base-class: */
+ return QIGraphicsWidget::sizeHint(enmWhich, constraint);
+}
+
+void UIToolsItem::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::showEvent(pEvent);
+
+ /* Update pixmap: */
+ updatePixmap();
+}
+
+void UIToolsItem::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::resizeEvent(pEvent);
+
+ /* What is the new geometry? */
+ const QRectF newGeometry = geometry();
+
+ /* Should we update visible name? */
+ if (previousGeometry().width() != newGeometry.width())
+ updateMaximumNameWidth();
+
+ /* Remember the new geometry: */
+ setPreviousGeometry(newGeometry);
+}
+
+void UIToolsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *)
+{
+ if (!m_fHovered)
+ {
+ m_fHovered = true;
+ emit sigHoverEnter();
+ update();
+ }
+}
+
+void UIToolsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
+{
+ if (m_fHovered)
+ {
+ m_fHovered = false;
+ emit sigHoverLeave();
+ update();
+ }
+}
+
+void UIToolsItem::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget * /* pWidget = 0 */)
+{
+ /* Acquire rectangle: */
+ const QRect rectangle = pOptions->rect;
+
+ /* Paint background: */
+ paintBackground(pPainter, rectangle);
+ /* Paint frame: */
+ paintFrame(pPainter, rectangle);
+ /* Paint tool info: */
+ paintToolInfo(pPainter, rectangle);
+}
+
+void UIToolsItem::sltHandleWindowRemapped()
+{
+ /* Update pixmap: */
+ updatePixmap();
+}
+
+void UIToolsItem::prepare()
+{
+ /* Add item to the scene: */
+ AssertMsg(m_pScene, ("Incorrect scene passed!"));
+ m_pScene->addItem(this);
+
+ /* Install Tools-view item accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUIToolsItem::pFactory);
+
+ /* Prepare color tones: */
+#if defined(VBOX_WS_MAC)
+ m_iDefaultLightnessStart = 120;
+ m_iDefaultLightnessFinal = 110;
+ m_iHoverLightnessStart = 125;
+ m_iHoverLightnessFinal = 115;
+ m_iHighlightLightnessStart = 115;
+ m_iHighlightLightnessFinal = 105;
+#elif defined(VBOX_WS_WIN)
+ m_iDefaultLightnessStart = 120;
+ m_iDefaultLightnessFinal = 110;
+ m_iHoverLightnessStart = 220;
+ m_iHoverLightnessFinal = 210;
+ m_iHighlightLightnessStart = 190;
+ m_iHighlightLightnessFinal = 180;
+#else /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+ m_iDefaultLightnessStart = 110;
+ m_iDefaultLightnessFinal = 100;
+ m_iHoverLightnessStart = 125;
+ m_iHoverLightnessFinal = 115;
+ m_iHighlightLightnessStart = 110;
+ m_iHighlightLightnessFinal = 100;
+#endif /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+
+ /* Prepare fonts: */
+ m_nameFont = font();
+ m_nameFont.setWeight(QFont::Bold);
+
+ /* Configure item options: */
+ setOwnedByLayout(false);
+ setAcceptHoverEvents(true);
+ setFocusPolicy(Qt::NoFocus);
+ setFlag(QGraphicsItem::ItemIsSelectable, false);
+
+ /* Prepare hover animation: */
+ prepareHoverAnimation();
+ /* Prepare connections: */
+ prepareConnections();
+
+ /* Init: */
+ updatePixmap();
+ updateMinimumNameSize();
+ updateVisibleName();
+}
+
+void UIToolsItem::prepareHoverAnimation()
+{
+ /* Create hovering animation machine: */
+ m_pHoveringMachine = new QStateMachine(this);
+ if (m_pHoveringMachine)
+ {
+ /* Create 'default' state: */
+ QState *pStateDefault = new QState(m_pHoveringMachine);
+ /* Create 'hovered' state: */
+ QState *pStateHovered = new QState(m_pHoveringMachine);
+
+ /* Configure 'default' state: */
+ if (pStateDefault)
+ {
+ /* When we entering default state => we assigning animatedValue to m_iDefaultValue: */
+ pStateDefault->assignProperty(this, "animatedValue", m_iDefaultValue);
+
+ /* Add state transitions: */
+ QSignalTransition *pDefaultToHovered = pStateDefault->addTransition(this, SIGNAL(sigHoverEnter()), pStateHovered);
+ if (pDefaultToHovered)
+ {
+ /* Create forward animation: */
+ m_pHoveringAnimationForward = new QPropertyAnimation(this, "animatedValue", this);
+ if (m_pHoveringAnimationForward)
+ {
+ m_pHoveringAnimationForward->setDuration(m_iAnimationDuration);
+ m_pHoveringAnimationForward->setStartValue(m_iDefaultValue);
+ m_pHoveringAnimationForward->setEndValue(m_iHoveredValue);
+
+ /* Add to transition: */
+ pDefaultToHovered->addAnimation(m_pHoveringAnimationForward);
+ }
+ }
+ }
+
+ /* Configure 'hovered' state: */
+ if (pStateHovered)
+ {
+ /* When we entering hovered state => we assigning animatedValue to m_iHoveredValue: */
+ pStateHovered->assignProperty(this, "animatedValue", m_iHoveredValue);
+
+ /* Add state transitions: */
+ QSignalTransition *pHoveredToDefault = pStateHovered->addTransition(this, SIGNAL(sigHoverLeave()), pStateDefault);
+ if (pHoveredToDefault)
+ {
+ /* Create backward animation: */
+ m_pHoveringAnimationBackward = new QPropertyAnimation(this, "animatedValue", this);
+ if (m_pHoveringAnimationBackward)
+ {
+ m_pHoveringAnimationBackward->setDuration(m_iAnimationDuration);
+ m_pHoveringAnimationBackward->setStartValue(m_iHoveredValue);
+ m_pHoveringAnimationBackward->setEndValue(m_iDefaultValue);
+
+ /* Add to transition: */
+ pHoveredToDefault->addAnimation(m_pHoveringAnimationBackward);
+ }
+ }
+ }
+
+ /* Initial state is 'default': */
+ m_pHoveringMachine->setInitialState(pStateDefault);
+ /* Start state-machine: */
+ m_pHoveringMachine->start();
+ }
+}
+
+void UIToolsItem::prepareConnections()
+{
+ /* This => model connections: */
+ connect(this, &UIToolsItem::sigMinimumWidthHintChanged,
+ model(), &UIToolsModel::sltItemMinimumWidthHintChanged);
+ connect(this, &UIToolsItem::sigMinimumHeightHintChanged,
+ model(), &UIToolsModel::sltItemMinimumHeightHintChanged);
+
+ /* Manager => this connections: */
+ connect(gpManager, &UIVirtualBoxManager::sigWindowRemapped,
+ this, &UIToolsItem::sltHandleWindowRemapped);
+}
+
+void UIToolsItem::cleanup()
+{
+ /* If that item is focused: */
+ if (model()->focusItem() == this)
+ {
+ /* Unset the focus item: */
+ model()->setFocusItem(0);
+ }
+ /* If that item is current: */
+ if (model()->currentItem() == this)
+ {
+ /* Unset the current item: */
+ model()->setCurrentItem(0);
+ }
+ /* If that item is in navigation list: */
+ if (model()->navigationList().contains(this))
+ {
+ /* Remove item from the navigation list: */
+ model()->removeFromNavigationList(this);
+ }
+}
+
+QVariant UIToolsItem::data(int iKey) const
+{
+ /* Provide other members with required data: */
+ switch (iKey)
+ {
+ /* Layout hints: */
+ case ToolsItemData_Margin: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 3 * 2;
+ case ToolsItemData_Spacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+
+ /* Default: */
+ default: break;
+ }
+ return QVariant();
+}
+
+void UIToolsItem::updatePixmap()
+{
+ /* Prepare variables: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) * 1.5;
+
+ /* Prepare new pixmap size: */
+ const QSize pixmapSize = QSize(iIconMetric, iIconMetric);
+ const QPixmap pixmap = m_icon.pixmap(gpManager->windowHandle(), pixmapSize, isEnabled() ? QIcon::Normal : QIcon::Disabled);
+ /* Update linked values: */
+ if (m_pixmapSize != pixmapSize)
+ {
+ m_pixmapSize = pixmapSize;
+ updateMaximumNameWidth();
+ updateGeometry();
+ }
+ if (m_pixmap.toImage() != pixmap.toImage())
+ {
+ m_pixmap = pixmap;
+ update();
+ }
+}
+
+void UIToolsItem::updateMinimumNameSize()
+{
+ /* Prepare variables: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+
+ /* Calculate new minimum name size: */
+ const QFontMetrics fm(m_nameFont, pPaintDevice);
+ const int iWidthOf15Letters = textWidthMonospace(m_nameFont, pPaintDevice, 15);
+ const QString strNameCompressedTo15Letters = compressText(m_nameFont, pPaintDevice, m_strName, iWidthOf15Letters);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const QSize minimumNameSize = QSize(fm.horizontalAdvance(strNameCompressedTo15Letters), fm.height());
+#else
+ const QSize minimumNameSize = QSize(fm.width(strNameCompressedTo15Letters), fm.height());
+#endif
+
+ /* Update linked values: */
+ if (m_minimumNameSize != minimumNameSize)
+ {
+ m_minimumNameSize = minimumNameSize;
+ updateGeometry();
+ }
+}
+
+void UIToolsItem::updateMaximumNameWidth()
+{
+ /* Prepare variables: */
+ const int iMargin = data(ToolsItemData_Margin).toInt();
+ const int iSpacing = data(ToolsItemData_Spacing).toInt();
+
+ /* Calculate new maximum name width: */
+ int iMaximumNameWidth = (int)geometry().width();
+ iMaximumNameWidth -= iMargin; /* left margin */
+ iMaximumNameWidth -= m_pixmapSize.width(); /* pixmap width */
+ iMaximumNameWidth -= iSpacing; /* spacing between pixmap and name(s) */
+ iMaximumNameWidth -= iMargin; /* right margin */
+
+ /* Update linked values: */
+ if (m_iMaximumNameWidth != iMaximumNameWidth)
+ {
+ m_iMaximumNameWidth = iMaximumNameWidth;
+ updateVisibleName();
+ }
+}
+
+void UIToolsItem::updateVisibleName()
+{
+ /* Prepare variables: */
+ QPaintDevice *pPaintDevice = model()->paintDevice();
+
+ /* Calculate new visible name: */
+ const QString strVisibleName = compressText(m_nameFont, pPaintDevice, m_strName, m_iMaximumNameWidth);
+
+ /* Update linked values: */
+ if (m_strVisibleName != strVisibleName)
+ {
+ m_strVisibleName = strVisibleName;
+ update();
+ }
+}
+
+/* static */
+int UIToolsItem::textWidthMonospace(const QFont &font, QPaintDevice *pPaintDevice, int iCount)
+{
+ /* Return text width, based on font-metrics: */
+ QFontMetrics fm(font, pPaintDevice);
+ QString strString;
+ strString.fill('_', iCount);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ return fm.horizontalAdvance(strString);
+#else
+ return fm.width(strString);
+#endif
+}
+
+/* static */
+QString UIToolsItem::compressText(const QFont &font, QPaintDevice *pPaintDevice, QString strText, int iWidth)
+{
+ /* Check if passed text is empty: */
+ if (strText.isEmpty())
+ return strText;
+
+ /* Check if passed text already fits maximum width: */
+ QFontMetrics fm(font, pPaintDevice);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ if (fm.horizontalAdvance(strText) <= iWidth)
+#else
+ if (fm.width(strText) <= iWidth)
+#endif
+ return strText;
+
+ /* Truncate otherwise: */
+ QString strEllipsis = QString("...");
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iEllipsisWidth = fm.horizontalAdvance(strEllipsis + " ");
+ while (!strText.isEmpty() && fm.horizontalAdvance(strText) + iEllipsisWidth > iWidth)
+ strText.truncate(strText.size() - 1);
+#else
+ int iEllipsisWidth = fm.width(strEllipsis + " ");
+ while (!strText.isEmpty() && fm.width(strText) + iEllipsisWidth > iWidth)
+ strText.truncate(strText.size() - 1);
+#endif
+ return strText + strEllipsis;
+}
+
+void UIToolsItem::paintBackground(QPainter *pPainter, const QRect &rectangle) const
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare variables: */
+ const QPalette pal = QApplication::palette();
+
+ /* Selection background: */
+ if (model()->currentItem() == this)
+ {
+ /* Prepare color: */
+ const QColor backgroundColor = isEnabled()
+ ? pal.color(QPalette::Active, QPalette::Highlight)
+ : pal.color(QPalette::Disabled, QPalette::Window);
+ /* Draw gradient: */
+ QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
+ bgGrad.setColorAt(0, backgroundColor.lighter(m_iHighlightLightnessStart));
+ bgGrad.setColorAt(1, backgroundColor.lighter(m_iHighlightLightnessFinal));
+ pPainter->fillRect(rectangle, bgGrad);
+
+ if (isEnabled() && isHovered())
+ {
+ /* Prepare color: */
+ QColor animationColor1 = QColor(Qt::white);
+ QColor animationColor2 = QColor(Qt::white);
+#ifdef VBOX_WS_MAC
+ animationColor1.setAlpha(90);
+#else
+ animationColor1.setAlpha(30);
+#endif
+ animationColor2.setAlpha(0);
+ /* Draw hovering animated gradient: */
+ QRect animatedRect = rectangle;
+ animatedRect.setWidth(animatedRect.height());
+ const int iLength = 2 * animatedRect.width() + rectangle.width();
+ const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
+ animatedRect.moveLeft(iShift);
+ QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
+ bgAnimatedGrad.setColorAt(0, animationColor2);
+ bgAnimatedGrad.setColorAt(0.1, animationColor2);
+ bgAnimatedGrad.setColorAt(0.5, animationColor1);
+ bgAnimatedGrad.setColorAt(0.9, animationColor2);
+ bgAnimatedGrad.setColorAt(1, animationColor2);
+ pPainter->fillRect(rectangle, bgAnimatedGrad);
+ }
+ }
+ /* Hovering background: */
+ else if (isHovered())
+ {
+ /* Prepare color: */
+ const QColor backgroundColor = isEnabled()
+ ? pal.color(QPalette::Active, QPalette::Highlight)
+ : pal.color(QPalette::Disabled, QPalette::Window);
+ /* Draw gradient: */
+ QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
+ bgGrad.setColorAt(0, backgroundColor.lighter(m_iHoverLightnessStart));
+ bgGrad.setColorAt(1, backgroundColor.lighter(m_iHoverLightnessFinal));
+ pPainter->fillRect(rectangle, bgGrad);
+
+ if (isEnabled())
+ {
+ /* Prepare color: */
+ QColor animationColor1 = QColor(Qt::white);
+ QColor animationColor2 = QColor(Qt::white);
+#ifdef VBOX_WS_MAC
+ animationColor1.setAlpha(120);
+#else
+ animationColor1.setAlpha(50);
+#endif
+ animationColor2.setAlpha(0);
+ /* Draw hovering animated gradient: */
+ QRect animatedRect = rectangle;
+ animatedRect.setWidth(animatedRect.height());
+ const int iLength = 2 * animatedRect.width() + rectangle.width();
+ const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
+ animatedRect.moveLeft(iShift);
+ QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
+ bgAnimatedGrad.setColorAt(0, animationColor2);
+ bgAnimatedGrad.setColorAt(0.1, animationColor2);
+ bgAnimatedGrad.setColorAt(0.5, animationColor1);
+ bgAnimatedGrad.setColorAt(0.9, animationColor2);
+ bgAnimatedGrad.setColorAt(1, animationColor2);
+ pPainter->fillRect(rectangle, bgAnimatedGrad);
+ }
+ }
+ /* Default background: */
+ else
+ {
+ /* Prepare color: */
+ const QColor backgroundColor = isEnabled()
+ ? pal.color(QPalette::Active, QPalette::Window)
+ : pal.color(QPalette::Disabled, QPalette::Window);
+ /* Draw gradient: */
+ QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
+ bgGrad.setColorAt(0, backgroundColor.lighter(m_iDefaultLightnessStart));
+ bgGrad.setColorAt(1, backgroundColor.lighter(m_iDefaultLightnessFinal));
+ pPainter->fillRect(rectangle, bgGrad);
+ }
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIToolsItem::paintFrame(QPainter *pPainter, const QRect &rectangle) const
+{
+ /* Don't paint frame for disabled items: */
+ if (!isEnabled())
+ return;
+
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare colors: */
+ const QPalette pal = QApplication::palette();
+ QColor strokeColor;
+
+ /* Selection frame: */
+ if (model()->currentItem() == this)
+ strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightnessStart - 40);
+ /* Hovering frame: */
+ else if (isHovered())
+ strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHoverLightnessStart - 40);
+ /* Default frame: */
+ else
+ strokeColor = pal.color(QPalette::Active, QPalette::Window).lighter(m_iDefaultLightnessStart);
+
+ /* Create/assign pen: */
+ QPen pen(strokeColor);
+ pen.setWidth(0);
+ pPainter->setPen(pen);
+
+ /* Draw borders: */
+ pPainter->drawLine(rectangle.topLeft(), rectangle.topRight());
+ pPainter->drawLine(rectangle.bottomLeft(), rectangle.bottomRight());
+ pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
+ pPainter->drawLine(rectangle.topRight(), rectangle.bottomRight());
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIToolsItem::paintToolInfo(QPainter *pPainter, const QRect &rectangle) const
+{
+ /* Prepare variables: */
+ const int iFullHeight = rectangle.height();
+ const int iMargin = data(ToolsItemData_Margin).toInt();
+ const int iSpacing = data(ToolsItemData_Spacing).toInt();
+ const QPalette pal = QApplication::palette();
+
+ /* Selected or hovered item foreground: */
+ if (model()->currentItem() == this || isHovered())
+ {
+ /* Prepare palette: */
+ const QPalette pal = QApplication::palette();
+
+ /* Get background color: */
+ const QColor highlight = pal.color(QPalette::Active, QPalette::Highlight);
+ const QColor background = model()->currentItem() == this
+ ? highlight.lighter(m_iHighlightLightnessStart)
+ : highlight.lighter(m_iHoverLightnessStart);
+
+ /* Get foreground color: */
+ const QColor simpleText = pal.color(QPalette::Active, QPalette::Text);
+ const QColor highlightText = pal.color(QPalette::Active, QPalette::HighlightedText);
+ QColor lightText = simpleText.black() < highlightText.black() ? simpleText : highlightText;
+ QColor darkText = simpleText.black() > highlightText.black() ? simpleText : highlightText;
+ if (lightText.black() > 128)
+ lightText = QColor(Qt::white);
+ if (darkText.black() < 128)
+ darkText = QColor(Qt::black);
+
+ /* Gather foreground color for background one: */
+ double dLuminance = (0.299 * background.red() + 0.587 * background.green() + 0.114 * background.blue()) / 255;
+ //printf("luminance = %f\n", dLuminance);
+ if (dLuminance > 0.5)
+ pPainter->setPen(darkText);
+ else
+ pPainter->setPen(lightText);
+ }
+ /* Default item foreground: */
+ else
+ {
+ const QColor textColor = isEnabled()
+ ? pal.color(QPalette::Active, QPalette::Text)
+ : pal.color(QPalette::Disabled, QPalette::Text);
+ pPainter->setPen(textColor);
+ }
+
+ /* Paint left column: */
+ {
+ /* Prepare variables: */
+ int iPixmapX = iMargin;
+ int iPixmapY = (iFullHeight - m_pixmap.height() / m_pixmap.devicePixelRatio()) / 2;
+ /* Paint pixmap: */
+ paintPixmap(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iPixmapX, iPixmapY),
+ /* Pixmap to paint: */
+ m_pixmap);
+ }
+
+ /* Paint right column: */
+ {
+ /* Prepare variables: */
+ int iNameX = iMargin + m_pixmapSize.width() + iSpacing;
+ int iNameY = (iFullHeight - m_minimumNameSize.height()) / 2;
+ /* Paint name: */
+ paintText(/* Painter: */
+ pPainter,
+ /* Point to paint in: */
+ QPoint(iNameX, iNameY),
+ /* Font to paint text: */
+ m_nameFont,
+ /* Paint device: */
+ model()->paintDevice(),
+ /* Text to paint: */
+ m_strVisibleName);
+ }
+}
+
+/* static */
+void UIToolsItem::paintPixmap(QPainter *pPainter, const QPoint &point,
+ const QPixmap &pixmap)
+{
+ /* Draw pixmap: */
+ pPainter->drawPixmap(point, pixmap);
+}
+
+/* static */
+void UIToolsItem::paintText(QPainter *pPainter, QPoint point,
+ const QFont &font, QPaintDevice *pPaintDevice,
+ const QString &strText)
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Assign font: */
+ pPainter->setFont(font);
+
+ /* Calculate ascent: */
+ QFontMetrics fm(font, pPaintDevice);
+ point += QPoint(0, fm.ascent());
+
+ /* Draw text: */
+ pPainter->drawText(point, strText);
+
+ /* Restore painter: */
+ pPainter->restore();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsItem.h b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsItem.h
new file mode 100644
index 00000000..eb37b5d2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsItem.h
@@ -0,0 +1,335 @@
+/* $Id: UIToolsItem.h $ */
+/** @file
+ * VBox Qt GUI - UIToolsItem class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_tools_UIToolsItem_h
+#define FEQT_INCLUDED_SRC_manager_tools_UIToolsItem_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QMimeData>
+#include <QPixmap>
+#include <QRectF>
+#include <QString>
+
+/* GUI includes: */
+#include "QIGraphicsWidget.h"
+#include "UITools.h"
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+/* Forward declaration: */
+class QPropertyAnimation;
+class QGraphicsScene;
+class QGraphicsSceneDragDropEvent;
+class QGraphicsSceneHoverEvent;
+class QGraphicsSceneMouseEvent;
+class QStateMachine;
+class UIActionPool;
+class UIToolsItemGroup;
+class UIToolsItemGlobal;
+class UIToolsItemMachine;
+class UIToolsModel;
+
+/** QIGraphicsWidget extension used as interface
+ * for graphics Tools-model/view architecture. */
+class UIToolsItem : public QIGraphicsWidget
+{
+ Q_OBJECT;
+ Q_PROPERTY(int animatedValue READ animatedValue WRITE setAnimatedValue);
+
+signals:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Notifies listeners about hover enter. */
+ void sigHoverEnter();
+ /** Notifies listeners about hover leave. */
+ void sigHoverLeave();
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Notifies listeners about minimum width @a iHint change. */
+ void sigMinimumWidthHintChanged(int iHint);
+ /** Notifies listeners about minimum height @a iHint change. */
+ void sigMinimumHeightHintChanged(int iHint);
+ /** @} */
+
+public:
+
+ /** Constructs item on the basis of passed arguments.
+ * @param pScene Brings the scene reference to add item to.
+ * @param enmClass Brings the item class.
+ * @param enmType Brings the item type.
+ * @param strName Brings the item name.
+ * @param icon Brings the item icon. */
+ UIToolsItem(QGraphicsScene *pScene,
+ UIToolClass enmClass, UIToolType enmType,
+ const QString &strName, const QIcon &icon);
+ /** Destructs item. */
+ virtual ~UIToolsItem() RT_OVERRIDE;
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns model reference. */
+ UIToolsModel *model() const;
+
+ /** Reconfigures item with new @a enmClass, @a enmType, @a icon and @a strName. */
+ void reconfigure(UIToolClass enmClass, UIToolType enmType,
+ const QIcon &icon, const QString &strName);
+ /** Reconfigures item with @a strName. */
+ void reconfigure(const QString &strName);
+
+ /** Returns item class. */
+ UIToolClass itemClass() const;
+ /** Returns item type. */
+ UIToolType itemType() const;
+ /** Returns item icon. */
+ const QIcon &icon() const;
+ /** Returns item name. */
+ const QString &name() const;
+
+ /** Defines whether item is @a fEnabled. */
+ void setEnabled(bool fEnabled);
+
+ /** Defines whether item is @a fHovered. */
+ void setHovered(bool fHovered);
+ /** Returns whether item is hovered. */
+ bool isHovered() const;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates geometry. */
+ virtual void updateGeometry() RT_OVERRIDE;
+
+ /** Returns minimum width-hint. */
+ int minimumWidthHint() const;
+ /** Returns minimum height-hint. */
+ int minimumHeightHint() const;
+
+ /** Returns size-hint.
+ * @param enmWhich Brings size-hint type.
+ * @param constraint Brings size constraint. */
+ virtual QSizeF sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint = QSizeF()) const RT_OVERRIDE;
+ /** @} */
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles hover enter @a event. */
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+ /** Handles hover leave @a event. */
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Item stuff.
+ * @{ */
+ /** Handles top-level window remaps. */
+ void sltHandleWindowRemapped();
+ /** @} */
+
+private:
+
+ /** Data field types. */
+ enum ToolsItemData
+ {
+ /* Layout hints: */
+ ToolsItemData_Margin,
+ ToolsItemData_Spacing,
+ };
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares hover animation. */
+ void prepareHoverAnimation();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Returns abstractly stored data value for certain @a iKey. */
+ QVariant data(int iKey) const;
+
+ /** Defines item's default animation @a iValue. */
+ void setDefaultValue(int iValue) { m_iDefaultValue = iValue; update(); }
+ /** Returns item's default animation value. */
+ int defaultValue() const { return m_iDefaultValue; }
+
+ /** Defines item's hovered animation @a iValue. */
+ void setHoveredValue(int iValue) { m_iHoveredValue = iValue; update(); }
+ /** Returns item's hovered animation value. */
+ int hoveredValue() const { return m_iHoveredValue; }
+
+ /** Defines item's animated @a iValue. */
+ void setAnimatedValue(int iValue) { m_iAnimatedValue = iValue; update(); }
+ /** Returns item's animated value. */
+ int animatedValue() const { return m_iAnimatedValue; }
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Defines previous @a geometry. */
+ void setPreviousGeometry(const QRectF &geometry) { m_previousGeometry = geometry; }
+ /** Returns previous geometry. */
+ const QRectF &previousGeometry() const { return m_previousGeometry; }
+
+ /** Updates pixmap. */
+ void updatePixmap();
+ /** Updates minimum name size. */
+ void updateMinimumNameSize();
+ /** Updates maximum name width. */
+ void updateMaximumNameWidth();
+ /** Updates visible name. */
+ void updateVisibleName();
+
+ /** Returns monospace text width of line containing @a iCount of chars calculated on the basis of certain @a font and @a pPaintDevice. */
+ static int textWidthMonospace(const QFont &font, QPaintDevice *pPaintDevice, int iCount);
+ /** Compresses @a strText to @a iWidth on the basis of certain @a font and @a pPaintDevice. */
+ static QString compressText(const QFont &font, QPaintDevice *pPaintDevice, QString strText, int iWidth);
+ /** @} */
+
+ /** @name Painting stuff.
+ * @{ */
+ /** Paints background using specified @a pPainter.
+ * @param rectangle Brings the rectangle to fill with background. */
+ void paintBackground(QPainter *pPainter, const QRect &rectangle) const;
+ /** Paints frame using using passed @a pPainter.
+ * @param rectangle Brings the rectangle to stroke with frame. */
+ void paintFrame(QPainter *pPainter, const QRect &rectangle) const;
+ /** Paints tool info using using passed @a pPainter.
+ * @param rectangle Brings the rectangle to limit painting with. */
+ void paintToolInfo(QPainter *pPainter, const QRect &rectangle) const;
+
+ /** Paints @a pixmap using passed @a pPainter.
+ * @param pOptions Brings the options set with painting data. */
+ static void paintPixmap(QPainter *pPainter, const QPoint &point, const QPixmap &pixmap);
+
+ /** Paints @a strText using passed @a pPainter.
+ * @param point Brings upper-left corner pixmap should be mapped to.
+ * @param font Brings the text font.
+ * @param pPaintDevice Brings the paint-device reference to initilize painting from. */
+ static void paintText(QPainter *pPainter, QPoint point,
+ const QFont &font, QPaintDevice *pPaintDevice,
+ const QString &strText);
+ /** @} */
+
+ /** @name Item stuff.
+ * @{ */
+ /** Holds the item parent. */
+ QGraphicsScene *m_pScene;
+ /** Holds the item class. */
+ UIToolClass m_enmClass;
+ /** Holds the item type. */
+ UIToolType m_enmType;
+ /** Holds the item icon. */
+ QIcon m_icon;
+ /** Holds the item name. */
+ QString m_strName;
+
+ /** Holds the item pixmap. */
+ QPixmap m_pixmap;
+ /** Holds the item visible name. */
+ QString m_strVisibleName;
+
+ /** Holds name font. */
+ QFont m_nameFont;
+
+ /** Holds whether item is hovered. */
+ bool m_fHovered;
+ /** Holds the hovering animation machine instance. */
+ QStateMachine *m_pHoveringMachine;
+ /** Holds the forward hovering animation instance. */
+ QPropertyAnimation *m_pHoveringAnimationForward;
+ /** Holds the backward hovering animation instance. */
+ QPropertyAnimation *m_pHoveringAnimationBackward;
+ /** Holds the animation duration. */
+ int m_iAnimationDuration;
+ /** Holds the default animation value. */
+ int m_iDefaultValue;
+ /** Holds the hovered animation value. */
+ int m_iHoveredValue;
+ /** Holds the animated value. */
+ int m_iAnimatedValue;
+
+ /** Holds start default lightness tone. */
+ int m_iDefaultLightnessStart;
+ /** Holds final default lightness tone. */
+ int m_iDefaultLightnessFinal;
+ /** Holds start hover lightness tone. */
+ int m_iHoverLightnessStart;
+ /** Holds final hover lightness tone. */
+ int m_iHoverLightnessFinal;
+ /** Holds start highlight lightness tone. */
+ int m_iHighlightLightnessStart;
+ /** Holds final highlight lightness tone. */
+ int m_iHighlightLightnessFinal;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds previous geometry. */
+ QRectF m_previousGeometry;
+
+ /** Holds previous minimum width hint. */
+ int m_iPreviousMinimumWidthHint;
+ /** Holds previous minimum height hint. */
+ int m_iPreviousMinimumHeightHint;
+
+ /** Holds the pixmap size. */
+ QSize m_pixmapSize;
+ /** Holds minimum name size. */
+ QSize m_minimumNameSize;
+
+ /** Holds maximum name width. */
+ int m_iMaximumNameWidth;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_tools_UIToolsItem_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsModel.cpp b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsModel.cpp
new file mode 100644
index 00000000..da954616
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsModel.cpp
@@ -0,0 +1,660 @@
+/* $Id: UIToolsModel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIToolsModel class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGraphicsScene>
+#include <QGraphicsSceneContextMenuEvent>
+#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsView>
+#include <QPropertyAnimation>
+#include <QScrollBar>
+#include <QTimer>
+
+/* GUI includes: */
+#include "QIMessageBox.h"
+#include "UICommon.h"
+#include "UIActionPoolManager.h"
+#include "UIIconPool.h"
+#include "UITools.h"
+#include "UIToolsHandlerMouse.h"
+#include "UIToolsHandlerKeyboard.h"
+#include "UIToolsModel.h"
+#include "UIExtraDataDefs.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UIVirtualBoxManagerWidget.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIWizardNewVM.h"
+
+/* COM includes: */
+#include "CExtPack.h"
+#include "CExtPackManager.h"
+
+/* Qt includes: */
+#include <QParallelAnimationGroup>
+
+/* Type defs: */
+typedef QSet<QString> UIStringSet;
+
+
+UIToolsModel::UIToolsModel(UITools *pParent)
+ : QIWithRetranslateUI3<QObject>(pParent)
+ , m_pTools(pParent)
+ , m_pScene(0)
+ , m_pMouseHandler(0)
+ , m_pKeyboardHandler(0)
+ , m_enmCurrentClass(UIToolClass_Global)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIToolsModel::~UIToolsModel()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIToolsModel::init()
+{
+ /* Load settings: */
+ loadSettings();
+
+ /* Update linked values: */
+ updateLayout();
+ updateNavigation();
+ sltItemMinimumWidthHintChanged();
+ sltItemMinimumHeightHintChanged();
+}
+
+UITools *UIToolsModel::tools() const
+{
+ return m_pTools;
+}
+
+UIActionPool *UIToolsModel::actionPool() const
+{
+ return tools()->actionPool();
+}
+
+QGraphicsScene *UIToolsModel::scene() const
+{
+ return m_pScene;
+}
+
+QPaintDevice *UIToolsModel::paintDevice() const
+{
+ if (scene() && !scene()->views().isEmpty())
+ return scene()->views().first();
+ return 0;
+}
+
+QGraphicsItem *UIToolsModel::itemAt(const QPointF &position, const QTransform &deviceTransform /* = QTransform() */) const
+{
+ return scene()->itemAt(position, deviceTransform);
+}
+
+void UIToolsModel::setToolsClass(UIToolClass enmClass)
+{
+ /* Update linked values: */
+ if (m_enmCurrentClass != enmClass)
+ {
+ m_enmCurrentClass = enmClass;
+ updateLayout();
+ updateNavigation();
+ sltItemMinimumHeightHintChanged();
+ }
+}
+
+UIToolClass UIToolsModel::toolsClass() const
+{
+ return m_enmCurrentClass;
+}
+
+void UIToolsModel::setToolsType(UIToolType enmType)
+{
+ /* Update linked values: */
+ if (currentItem()->itemType() != enmType)
+ {
+ foreach (UIToolsItem *pItem, items())
+ if (pItem->itemType() == enmType)
+ {
+ setCurrentItem(pItem);
+ break;
+ }
+ }
+}
+
+UIToolType UIToolsModel::toolsType() const
+{
+ return currentItem()->itemType();
+}
+
+UIToolType UIToolsModel::lastSelectedToolGlobal() const
+{
+ return m_pLastItemGlobal->itemType();
+}
+
+UIToolType UIToolsModel::lastSelectedToolMachine() const
+{
+ return m_pLastItemMachine->itemType();
+}
+
+void UIToolsModel::setToolClassEnabled(UIToolClass enmClass, bool fEnabled)
+{
+ /* Update linked values: */
+ if (m_enabledToolClasses.value(enmClass) != fEnabled)
+ {
+ m_enabledToolClasses[enmClass] = fEnabled;
+ foreach (UIToolsItem *pItem, items())
+ pItem->setEnabled( m_enabledToolClasses.value(pItem->itemClass())
+ && !m_restrictedToolTypes.contains(pItem->itemType()));
+ }
+}
+
+bool UIToolsModel::toolClassEnabled(UIToolClass enmClass) const
+{
+ return m_enabledToolClasses.value(enmClass);
+}
+
+void UIToolsModel::setRestrictedToolTypes(const QList<UIToolType> &types)
+{
+ /* Update linked values: */
+ if (m_restrictedToolTypes != types)
+ {
+ m_restrictedToolTypes = types;
+ foreach (UIToolsItem *pItem, items())
+ pItem->setEnabled( m_enabledToolClasses.value(pItem->itemClass())
+ && !m_restrictedToolTypes.contains(pItem->itemType()));
+ }
+}
+
+QList<UIToolType> UIToolsModel::restrictedToolTypes() const
+{
+ return m_restrictedToolTypes;
+}
+
+void UIToolsModel::closeParent()
+{
+ m_pTools->close();
+}
+
+void UIToolsModel::setCurrentItem(UIToolsItem *pItem)
+{
+ /* Is there something changed? */
+ if (m_pCurrentItem == pItem)
+ return;
+
+ /* Remember old current-item: */
+ UIToolsItem *pOldCurrentItem = m_pCurrentItem;
+
+ /* If there is item: */
+ if (pItem)
+ {
+ /* Set this item to current if navigation list contains it: */
+ if (navigationList().contains(pItem))
+ m_pCurrentItem = pItem;
+ /* Update last item in any case: */
+ switch (pItem->itemClass())
+ {
+ case UIToolClass_Global: m_pLastItemGlobal = pItem; break;
+ case UIToolClass_Machine: m_pLastItemMachine = pItem; break;
+ default: break;
+ }
+
+ /* Save selected items data: */
+ const QList<UIToolType> set = QList<UIToolType>() << m_pLastItemGlobal->itemType() << m_pLastItemMachine->itemType();
+ LogRel2(("GUI: UIToolsModel: Saving tool items as: Global=%d, Machine=%d\n",
+ (int)m_pLastItemGlobal->itemType(), (int)m_pLastItemMachine->itemType()));
+ gEDataManager->setToolsPaneLastItemsChosen(set);
+ }
+ /* Otherwise reset current item: */
+ else
+ m_pCurrentItem = 0;
+
+ /* Update old item (if any): */
+ if (pOldCurrentItem)
+ pOldCurrentItem->update();
+ /* Update new item (if any): */
+ if (m_pCurrentItem)
+ m_pCurrentItem->update();
+
+ /* Notify about selection change: */
+ emit sigSelectionChanged();
+
+ /* Move focus to current-item: */
+ setFocusItem(currentItem());
+
+ /* Adjust corrresponding actions finally: */
+ const UIToolType enmType = currentItem() ? currentItem()->itemType() : UIToolType_Welcome;
+ QMap<UIToolType, UIAction*> actions;
+ actions[UIToolType_Welcome] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_WelcomeScreen);
+ actions[UIToolType_Extensions] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_ExtensionPackManager);
+ actions[UIToolType_Media] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_VirtualMediaManager);
+ actions[UIToolType_Network] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_NetworkManager);
+ actions[UIToolType_Cloud] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_CloudProfileManager);
+ actions[UIToolType_VMActivityOverview] = actionPool()->action(UIActionIndexMN_M_File_M_Tools_T_VMActivityOverview);
+ if (actions.contains(enmType))
+ actions.value(enmType)->setChecked(true);
+}
+
+UIToolsItem *UIToolsModel::currentItem() const
+{
+ return m_pCurrentItem;
+}
+
+void UIToolsModel::setFocusItem(UIToolsItem *pItem)
+{
+ /* Always make sure real focus unset: */
+ scene()->setFocusItem(0);
+
+ /* Is there something changed? */
+ if (m_pFocusItem == pItem)
+ return;
+
+ /* Remember old focus-item: */
+ UIToolsItem *pOldFocusItem = m_pFocusItem;
+
+ /* If there is item: */
+ if (pItem)
+ {
+ /* Set this item to focus if navigation list contains it: */
+ if (navigationList().contains(pItem))
+ m_pFocusItem = pItem;
+ /* Otherwise it's error: */
+ else
+ AssertMsgFailed(("Passed item is not in navigation list!"));
+ }
+ /* Otherwise reset focus item: */
+ else
+ m_pFocusItem = 0;
+
+ /* Disconnect old focus-item (if any): */
+ if (pOldFocusItem)
+ disconnect(pOldFocusItem, &UIToolsItem::destroyed, this, &UIToolsModel::sltFocusItemDestroyed);
+ /* Connect new focus-item (if any): */
+ if (m_pFocusItem)
+ connect(m_pFocusItem.data(), &UIToolsItem::destroyed, this, &UIToolsModel::sltFocusItemDestroyed);
+
+ /* Notify about focus change: */
+ emit sigFocusChanged();
+}
+
+UIToolsItem *UIToolsModel::focusItem() const
+{
+ return m_pFocusItem;
+}
+
+const QList<UIToolsItem*> &UIToolsModel::navigationList() const
+{
+ return m_navigationList;
+}
+
+void UIToolsModel::removeFromNavigationList(UIToolsItem *pItem)
+{
+ AssertMsg(pItem, ("Passed item is invalid!"));
+ m_navigationList.removeAll(pItem);
+}
+
+void UIToolsModel::updateNavigation()
+{
+ /* Clear list initially: */
+ m_navigationList.clear();
+
+ /* Enumerate the children: */
+ foreach (UIToolsItem *pItem, items())
+ if (pItem->isVisible())
+ m_navigationList << pItem;
+
+ /* Choose last selected item of current class: */
+ UIToolsItem *pLastSelectedItem = m_enmCurrentClass == UIToolClass_Global
+ ? m_pLastItemGlobal : m_pLastItemMachine;
+ if (navigationList().contains(pLastSelectedItem))
+ setCurrentItem(pLastSelectedItem);
+}
+
+QList<UIToolsItem*> UIToolsModel::items() const
+{
+ return m_items;
+}
+
+UIToolsItem *UIToolsModel::item(UIToolType enmType) const
+{
+ foreach (UIToolsItem *pItem, items())
+ if (pItem->itemType() == enmType)
+ return pItem;
+ return 0;
+}
+
+void UIToolsModel::updateLayout()
+{
+ /* Prepare variables: */
+ const int iMargin = data(ToolsModelData_Margin).toInt();
+ const int iSpacing = data(ToolsModelData_Spacing).toInt();
+ const QSize viewportSize = scene()->views()[0]->viewport()->size();
+ const int iViewportWidth = viewportSize.width();
+ int iVerticalIndent = iMargin;
+
+ /* Layout the children: */
+ foreach (UIToolsItem *pItem, items())
+ {
+ /* Hide/skip unrelated items: */
+ if (pItem->itemClass() != m_enmCurrentClass)
+ {
+ pItem->hide();
+ continue;
+ }
+
+ /* Set item position: */
+ pItem->setPos(iMargin, iVerticalIndent);
+ /* Set root-item size: */
+ pItem->resize(iViewportWidth, pItem->minimumHeightHint());
+ /* Make sure item is shown: */
+ pItem->show();
+ /* Advance vertical indent: */
+ iVerticalIndent += (pItem->minimumHeightHint() + iSpacing);
+ }
+}
+
+void UIToolsModel::sltHandleViewResized()
+{
+ /* Relayout: */
+ updateLayout();
+}
+
+void UIToolsModel::sltItemMinimumWidthHintChanged()
+{
+ /* Prepare variables: */
+ const int iMargin = data(ToolsModelData_Margin).toInt();
+
+ /* Calculate maximum horizontal width: */
+ int iMinimumWidthHint = 0;
+ iMinimumWidthHint += 2 * iMargin;
+ foreach (UIToolsItem *pItem, items())
+ iMinimumWidthHint = qMax(iMinimumWidthHint, pItem->minimumWidthHint());
+
+ /* Notify listeners: */
+ emit sigItemMinimumWidthHintChanged(iMinimumWidthHint);
+}
+
+void UIToolsModel::sltItemMinimumHeightHintChanged()
+{
+ /* Prepare variables: */
+ const int iMargin = data(ToolsModelData_Margin).toInt();
+ const int iSpacing = data(ToolsModelData_Spacing).toInt();
+
+ /* Calculate summary vertical height: */
+ int iMinimumHeightHint = 0;
+ iMinimumHeightHint += 2 * iMargin;
+ foreach (UIToolsItem *pItem, items())
+ if (pItem->isVisible())
+ iMinimumHeightHint += (pItem->minimumHeightHint() + iSpacing);
+ iMinimumHeightHint -= iSpacing;
+
+ /* Notify listeners: */
+ emit sigItemMinimumHeightHintChanged(iMinimumHeightHint);
+}
+
+bool UIToolsModel::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* Process only scene events: */
+ if (pWatched != scene())
+ return QIWithRetranslateUI3<QObject>::eventFilter(pWatched, pEvent);
+
+ /* Process only item focused by model: */
+ if (scene()->focusItem())
+ return QIWithRetranslateUI3<QObject>::eventFilter(pWatched, pEvent);
+
+ /* Do not handle disabled items: */
+ if (!currentItem()->isEnabled())
+ return QIWithRetranslateUI3<QObject>::eventFilter(pWatched, pEvent);
+
+ /* Checking event-type: */
+ switch (pEvent->type())
+ {
+ /* Keyboard handler: */
+ case QEvent::KeyPress:
+ return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Press);
+ case QEvent::KeyRelease:
+ return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Release);
+ /* Mouse handler: */
+ case QEvent::GraphicsSceneMousePress:
+ return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Press);
+ case QEvent::GraphicsSceneMouseRelease:
+ return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Release);
+ /* Shut up MSC: */
+ default: break;
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI3<QObject>::eventFilter(pWatched, pEvent);
+}
+
+void UIToolsModel::retranslateUi()
+{
+ foreach (UIToolsItem *pItem, m_items)
+ {
+ switch (pItem->itemType())
+ {
+ case UIToolType_Welcome: pItem->reconfigure(tr("Welcome")); break;
+ case UIToolType_Extensions: pItem->reconfigure(tr("Extensions")); break;
+ case UIToolType_Media: pItem->reconfigure(tr("Media")); break;
+ case UIToolType_Network: pItem->reconfigure(tr("Network")); break;
+ case UIToolType_Cloud: pItem->reconfigure(tr("Cloud")); break;
+ case UIToolType_VMActivityOverview: pItem->reconfigure(tr("Activities")); break;
+ case UIToolType_Details: pItem->reconfigure(tr("Details")); break;
+ case UIToolType_Snapshots: pItem->reconfigure(tr("Snapshots")); break;
+ case UIToolType_Logs: pItem->reconfigure(tr("Logs")); break;
+ case UIToolType_VMActivity: pItem->reconfigure(tr("Activity")); break;
+ case UIToolType_FileManager: pItem->reconfigure(tr("File Manager")); break;
+ default: break;
+ }
+ }
+}
+
+void UIToolsModel::sltFocusItemDestroyed()
+{
+ AssertMsgFailed(("Focus item destroyed!"));
+}
+
+void UIToolsModel::prepare()
+{
+ /* Prepare scene: */
+ prepareScene();
+ /* Prepare items: */
+ prepareItems();
+ /* Prepare handlers: */
+ prepareHandlers();
+ /* Prepare connections: */
+ prepareConnections();
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIToolsModel::prepareScene()
+{
+ m_pScene = new QGraphicsScene(this);
+ if (m_pScene)
+ m_pScene->installEventFilter(this);
+}
+
+void UIToolsModel::prepareItems()
+{
+ /* Enable both classes of tools initially: */
+ m_enabledToolClasses[UIToolClass_Global] = true;
+ m_enabledToolClasses[UIToolClass_Machine] = true;
+
+ /* Welcome: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Welcome, QString(),
+ UIIconPool::iconSet(":/welcome_screen_24px.png", ":/welcome_screen_24px.png"));
+
+ /* Extensions: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Extensions, QString(),
+ UIIconPool::iconSet(":/extension_pack_manager_24px.png", ":/extension_pack_manager_disabled_24px.png"));
+
+ /* Media: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Media, QString(),
+ UIIconPool::iconSet(":/media_manager_24px.png", ":/media_manager_disabled_24px.png"));
+
+ /* Network: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Network, QString(),
+ UIIconPool::iconSet(":/host_iface_manager_24px.png", ":/host_iface_manager_disabled_24px.png"));
+
+ /* Cloud: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_Cloud, QString(),
+ UIIconPool::iconSet(":/cloud_profile_manager_24px.png", ":/cloud_profile_manager_disabled_24px.png"));
+
+ /* Activities: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Global, UIToolType_VMActivityOverview, QString(),
+ UIIconPool::iconSet(":/resources_monitor_24px.png", ":/resources_monitor_disabled_24px.png"));
+
+ /* Details: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_Details, QString(),
+ UIIconPool::iconSet(":/machine_details_manager_24px.png", ":/machine_details_manager_disabled_24px.png"));
+
+ /* Snapshots: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_Snapshots, QString(),
+ UIIconPool::iconSet(":/snapshot_manager_24px.png", ":/snapshot_manager_disabled_24px.png"));
+
+ /* Logs: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_Logs, QString(),
+ UIIconPool::iconSet(":/vm_show_logs_24px.png", ":/vm_show_logs_disabled_24px.png"));
+
+ /* Activity: */
+ m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_VMActivity, QString(),
+ UIIconPool::iconSet(":/performance_monitor_24px.png", ":/performance_monitor_disabled_24px.png"));
+
+ m_items << new UIToolsItem(scene(), UIToolClass_Machine, UIToolType_FileManager, QString(),
+ UIIconPool::iconSet(":/file_manager_24px.png", ":/file_manager_disabled_24px.png"));
+}
+
+void UIToolsModel::prepareHandlers()
+{
+ m_pMouseHandler = new UIToolsHandlerMouse(this);
+ m_pKeyboardHandler = new UIToolsHandlerKeyboard(this);
+}
+
+void UIToolsModel::prepareConnections()
+{
+ UITools* pTools = qobject_cast<UITools*>(parent());
+ AssertPtrReturnVoid(pTools);
+ {
+ /* Setup parent connections: */
+ connect(this, &UIToolsModel::sigSelectionChanged,
+ pTools, &UITools::sigSelectionChanged);
+ connect(this, &UIToolsModel::sigExpandingStarted,
+ pTools, &UITools::sigExpandingStarted);
+ connect(this, &UIToolsModel::sigExpandingFinished,
+ pTools, &UITools::sigExpandingFinished);
+ }
+}
+
+void UIToolsModel::loadSettings()
+{
+ /* Load selected items data: */
+ const QList<UIToolType> data = gEDataManager->toolsPaneLastItemsChosen();
+ UIToolType enmTypeGlobal = data.value(0);
+ if (!UIToolStuff::isTypeOfClass(enmTypeGlobal, UIToolClass_Global))
+ enmTypeGlobal = UIToolType_Welcome;
+ UIToolType enmTypeMachine = data.value(1);
+ if (!UIToolStuff::isTypeOfClass(enmTypeMachine, UIToolClass_Machine))
+ enmTypeMachine = UIToolType_Details;
+ LogRel2(("GUI: UIToolsModel: Restoring tool items as: Global=%d, Machine=%d\n",
+ (int)enmTypeGlobal, (int)enmTypeMachine));
+
+ /* First of them is current global class item definition: */
+ foreach (UIToolsItem *pItem, items())
+ if (pItem->itemType() == enmTypeGlobal)
+ m_pLastItemGlobal = pItem;
+ if (m_pLastItemGlobal.isNull())
+ m_pLastItemGlobal = item(UIToolType_Welcome);
+
+ /* Second of them is current machine class item definition: */
+ foreach (UIToolsItem *pItem, items())
+ if (pItem->itemType() == enmTypeMachine)
+ m_pLastItemMachine = pItem;
+ if (m_pLastItemMachine.isNull())
+ m_pLastItemMachine = item(UIToolType_Details);
+}
+
+void UIToolsModel::cleanupConnections()
+{
+ /* Disconnect selection-changed signal prematurelly.
+ * Keep in mind, we are using static_cast instead of qobject_cast here to be
+ * sure connection is disconnected even if parent is self-destroyed. */
+ disconnect(this, &UIToolsModel::sigSelectionChanged,
+ static_cast<UITools*>(parent()), &UITools::sigSelectionChanged);
+}
+
+void UIToolsModel::cleanupHandlers()
+{
+ delete m_pKeyboardHandler;
+ m_pKeyboardHandler = 0;
+ delete m_pMouseHandler;
+ m_pMouseHandler = 0;
+}
+
+void UIToolsModel::cleanupItems()
+{
+ foreach (UIToolsItem *pItem, m_items)
+ delete pItem;
+ m_items.clear();
+}
+
+void UIToolsModel::cleanupScene()
+{
+ delete m_pScene;
+ m_pScene = 0;
+}
+
+void UIToolsModel::cleanup()
+{
+ /* Cleanup connections: */
+ cleanupConnections();
+ /* Cleanup handlers: */
+ cleanupHandlers();
+ /* Cleanup items: */
+ cleanupItems();
+ /* Cleanup scene: */
+ cleanupScene();
+}
+
+QVariant UIToolsModel::data(int iKey) const
+{
+ /* Provide other members with required data: */
+ switch (iKey)
+ {
+ /* Layout hints: */
+ case ToolsModelData_Margin: return 0;
+ case ToolsModelData_Spacing: return 1;
+
+ /* Default: */
+ default: break;
+ }
+ return QVariant();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsModel.h b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsModel.h
new file mode 100644
index 00000000..54515951
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsModel.h
@@ -0,0 +1,305 @@
+/* $Id: UIToolsModel.h $ */
+/** @file
+ * VBox Qt GUI - UIToolsModel class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_tools_UIToolsModel_h
+#define FEQT_INCLUDED_SRC_manager_tools_UIToolsModel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QObject>
+#include <QPointer>
+#include <QTransform>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIToolsItem.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declaration: */
+class QGraphicsItem;
+class QGraphicsScene;
+class QGraphicsSceneContextMenuEvent;
+class QMenu;
+class QPaintDevice;
+class QTimer;
+class UIActionPool;
+class UITools;
+class UIToolsHandlerMouse;
+class UIToolsHandlerKeyboard;
+
+/** QObject extension used as VM Tools-pane model: */
+class UIToolsModel : public QIWithRetranslateUI3<QObject>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Notifies about selection changed. */
+ void sigSelectionChanged();
+ /** Notifies about focus changed. */
+ void sigFocusChanged();
+
+ /** Notifies about group expanding started. */
+ void sigExpandingStarted();
+ /** Notifies about group expanding finished. */
+ void sigExpandingFinished();
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Notifies about item minimum width @a iHint changed. */
+ void sigItemMinimumWidthHintChanged(int iHint);
+ /** Notifies about item minimum height @a iHint changed. */
+ void sigItemMinimumHeightHintChanged(int iHint);
+ /** @} */
+
+public:
+
+ /** Constructs Tools-model passing @a pParent to the base-class. */
+ UIToolsModel(UITools *pParent);
+ /** Destructs Tools-model. */
+ virtual ~UIToolsModel() RT_OVERRIDE;
+
+ /** @name General stuff.
+ * @{ */
+ /** Inits model. */
+ void init();
+
+ /** Returns the Tools reference. */
+ UITools *tools() const;
+ /** Returns the action-pool reference. */
+ UIActionPool *actionPool() const;
+ /** Returns the scene reference. */
+ QGraphicsScene *scene() const;
+ /** Returns the paint device reference. */
+ QPaintDevice *paintDevice() const;
+
+ /** Returns item at @a position, taking into account possible @a deviceTransform. */
+ QGraphicsItem *itemAt(const QPointF &position, const QTransform &deviceTransform = QTransform()) const;
+
+ /** Defines current tools @a enmClass. */
+ void setToolsClass(UIToolClass enmClass);
+ /** Returns current tools class. */
+ UIToolClass toolsClass() const;
+
+ /** Defines current tools @a enmType. */
+ void setToolsType(UIToolType enmType);
+ /** Returns current tools type. */
+ UIToolType toolsType() const;
+
+ /** Returns last selected global tool. */
+ UIToolType lastSelectedToolGlobal() const;
+ /** Returns last selected machine tool. */
+ UIToolType lastSelectedToolMachine() const;
+
+ /** Defines whether certain @a enmClass of tools is @a fEnabled.*/
+ void setToolClassEnabled(UIToolClass enmClass, bool fEnabled);
+ /** Returns whether certain class of tools is enabled.*/
+ bool toolClassEnabled(UIToolClass enmClass) const;
+
+ /** Defines restructed tool @a types. */
+ void setRestrictedToolTypes(const QList<UIToolType> &types);
+ /** Returns restricted tool types. */
+ QList<UIToolType> restrictedToolTypes() const;
+
+ /** Closes parent. */
+ void closeParent();
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Returns the item list. */
+ QList<UIToolsItem*> items() const;
+
+ /** Returns the item of passed @a enmType. */
+ UIToolsItem *item(UIToolType enmType) const;
+ /** @} */
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Defines current @a pItem. */
+ void setCurrentItem(UIToolsItem *pItem);
+ /** Returns current item. */
+ UIToolsItem *currentItem() const;
+
+ /** Defines focus @a pItem. */
+ void setFocusItem(UIToolsItem *pItem);
+ /** Returns focus item. */
+ UIToolsItem *focusItem() const;
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Returns navigation item list. */
+ const QList<UIToolsItem*> &navigationList() const;
+ /** Removes @a pItem from navigation list. */
+ void removeFromNavigationList(UIToolsItem *pItem);
+ /** Updates navigation list. */
+ void updateNavigation();
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Updates layout. */
+ void updateLayout();
+ /** @} */
+
+public slots:
+
+ /** @name General stuff.
+ * @{ */
+ /** Handles Tools-view resize. */
+ void sltHandleViewResized();
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Handles minimum width hint change. */
+ void sltItemMinimumWidthHintChanged();
+ /** Handles minimum height hint change. */
+ void sltItemMinimumHeightHintChanged();
+ /** @} */
+
+protected:
+
+ /** @name Event handling stuff.
+ * @{ */
+ /** Preprocesses Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Handles focus item destruction. */
+ void sltFocusItemDestroyed();
+ /** @} */
+
+private:
+
+ /** Data field types. */
+ enum ToolsModelData
+ {
+ /* Layout hints: */
+ ToolsModelData_Margin,
+ ToolsModelData_Spacing,
+ };
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares scene. */
+ void prepareScene();
+ /** Prepares items. */
+ void prepareItems();
+ /** Prepares handlers. */
+ void prepareHandlers();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Loads settings. */
+ void loadSettings();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups connections. */
+ void cleanupHandlers();
+ /** Cleanups items. */
+ void cleanupItems();
+ /** Cleanups scene. */
+ void cleanupScene();
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Returns abstractly stored data value for certain @a iKey. */
+ QVariant data(int iKey) const;
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the Tools reference. */
+ UITools *m_pTools;
+
+ /** Holds the scene reference. */
+ QGraphicsScene *m_pScene;
+
+ /** Holds the mouse handler instance. */
+ UIToolsHandlerMouse *m_pMouseHandler;
+ /** Holds the keyboard handler instance. */
+ UIToolsHandlerKeyboard *m_pKeyboardHandler;
+
+ /** Holds current tools class. */
+ UIToolClass m_enmCurrentClass;
+
+ /** Holds whether tools of particular class are enabled. */
+ QMap<UIToolClass, bool> m_enabledToolClasses;
+
+ /** Holds a list of restricted tool types. */
+ QList<UIToolType> m_restrictedToolTypes;
+ /** @} */
+
+ /** @name Children stuff.
+ * @{ */
+ /** Holds the root stack. */
+ QList<UIToolsItem*> m_items;
+ /** @} */
+
+ /** @name Selection stuff.
+ * @{ */
+ /** Holds the selected item reference. */
+ QPointer<UIToolsItem> m_pCurrentItem;
+ /** Holds the focus item reference. */
+ QPointer<UIToolsItem> m_pFocusItem;
+ /** @} */
+
+ /** @name Navigation stuff.
+ * @{ */
+ /** Holds the navigation list. */
+ QList<UIToolsItem*> m_navigationList;
+
+ /** Holds the last chosen navigation item of global class. */
+ QPointer<UIToolsItem> m_pLastItemGlobal;
+ /** Holds the last chosen navigation item of machine class. */
+ QPointer<UIToolsItem> m_pLastItemMachine;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_tools_UIToolsModel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsView.cpp b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsView.cpp
new file mode 100644
index 00000000..5414eb04
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsView.cpp
@@ -0,0 +1,221 @@
+/* $Id: UIToolsView.cpp $ */
+/** @file
+ * VBox Qt GUI - UIToolsView class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QApplication>
+#include <QScrollBar>
+
+/* GUI includes: */
+#include "UITools.h"
+#include "UIToolsItem.h"
+#include "UIToolsModel.h"
+#include "UIToolsView.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/** QAccessibleWidget extension used as an accessibility interface for Tools-view. */
+class UIAccessibilityInterfaceForUIToolsView : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating Tools-view accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UIToolsView"))
+ return new UIAccessibilityInterfaceForUIToolsView(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ UIAccessibilityInterfaceForUIToolsView(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::List)
+ {}
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), 0);
+
+ /* Return the number of children: */
+ return view()->tools()->model()->items().size();
+ }
+
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), 0);
+ /* Make sure index is valid: */
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Return the child with the passed iIndex: */
+ return QAccessible::queryAccessibleInterface(view()->tools()->model()->items().at(iIndex));
+ }
+
+ /** Returns the index of passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), -1);
+ /* Make sure child is valid: */
+ AssertReturn(pChild, -1);
+
+ /* Return the index of passed model child: */
+ return view()->tools()->model()->items().indexOf(qobject_cast<UIToolsItem*>(pChild->object()));
+ }
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE
+ {
+ /* Make sure view still alive: */
+ AssertPtrReturn(view(), QString());
+
+ /* Return view tool-tip: */
+ Q_UNUSED(enmTextRole);
+ return view()->toolTip();
+ }
+
+private:
+
+ /** Returns corresponding Tools-view. */
+ UIToolsView *view() const { return qobject_cast<UIToolsView*>(widget()); }
+};
+
+
+UIToolsView::UIToolsView(UITools *pParent)
+ : QIWithRetranslateUI<QIGraphicsView>(pParent)
+ , m_pTools(pParent)
+ , m_iMinimumWidthHint(0)
+ , m_iMinimumHeightHint(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIToolsView::sltFocusChanged()
+{
+ /* Make sure focus-item set: */
+ const UIToolsItem *pFocusItem = tools() && tools()->model()
+ ? tools()->model()->focusItem()
+ : 0;
+ if (!pFocusItem)
+ return;
+
+ const QSize viewSize = viewport()->size();
+ QRectF geo = pFocusItem->geometry();
+ geo &= QRectF(geo.topLeft(), viewSize);
+ ensureVisible(geo, 0, 0);
+}
+
+void UIToolsView::sltMinimumWidthHintChanged(int iHint)
+{
+ /* Is there something changed? */
+ if (m_iMinimumWidthHint == iHint)
+ return;
+
+ /* Remember new value: */
+ m_iMinimumWidthHint = iHint;
+
+ /* Set minimum view width according passed width-hint: */
+ setMinimumWidth(2 * frameWidth() + m_iMinimumWidthHint);
+
+ /* Update scene-rect: */
+ updateSceneRect();
+}
+
+void UIToolsView::sltMinimumHeightHintChanged(int iHint)
+{
+ /* Is there something changed? */
+ if (m_iMinimumHeightHint == iHint)
+ return;
+
+ /* Remember new value: */
+ m_iMinimumHeightHint = iHint;
+
+ /* Set minimum view height according passed height-hint: */
+ setMinimumHeight(2 * frameWidth() + m_iMinimumHeightHint);
+
+ /* Update scene-rect: */
+ updateSceneRect();
+}
+
+void UIToolsView::retranslateUi()
+{
+ /* Translate this: */
+ setWhatsThis(tr("Contains a list of VirtualBox tools."));
+}
+
+void UIToolsView::prepare()
+{
+ /* Install Tools-view accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUIToolsView::pFactory);
+
+ /* Prepare palette: */
+ preparePalette();
+
+ /* Setup frame: */
+ setFrameShape(QFrame::NoFrame);
+ setFrameShadow(QFrame::Plain);
+ setAlignment(Qt::AlignLeft | Qt::AlignTop);
+
+ /* Setup scroll-bars policy: */
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ /* Update scene-rect: */
+ updateSceneRect();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIToolsView::preparePalette()
+{
+ /* Setup palette: */
+ QPalette pal = qApp->palette();
+ pal.setColor(QPalette::Active, QPalette::Base, pal.color(QPalette::Active, QPalette::Window));
+ setPalette(pal);
+}
+
+void UIToolsView::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI<QIGraphicsView>::resizeEvent(pEvent);
+ /* Notify listeners: */
+ emit sigResized();
+}
+
+void UIToolsView::updateSceneRect()
+{
+ setSceneRect(0, 0, m_iMinimumWidthHint, m_iMinimumHeightHint);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsView.h b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsView.h
new file mode 100644
index 00000000..255b22c8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsView.h
@@ -0,0 +1,121 @@
+/* $Id: UIToolsView.h $ */
+/** @file
+ * VBox Qt GUI - UIToolsView class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_manager_tools_UIToolsView_h
+#define FEQT_INCLUDED_SRC_manager_tools_UIToolsView_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIGraphicsView.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class UITools;
+
+/** QIGraphicsView extension used as VM Tools-pane view. */
+class UIToolsView : public QIWithRetranslateUI<QIGraphicsView>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about resize. */
+ void sigResized();
+
+public:
+
+ /** Constructs a Tools-view passing @a pParent to the base-class.
+ * @param pParent Brings the Tools-container to embed into. */
+ UIToolsView(UITools *pParent);
+
+ /** @name General stuff.
+ * @{ */
+ /** Returns the Tools reference. */
+ UITools *tools() const { return m_pTools; }
+ /** @} */
+
+public slots:
+
+ /** @name General stuff.
+ * @{ */
+ /** Handles focus change to @a pFocusItem. */
+ void sltFocusChanged();
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Handles minimum width @a iHint change. */
+ void sltMinimumWidthHintChanged(int iHint);
+ /** Handles minimum height @a iHint change. */
+ void sltMinimumHeightHintChanged(int iHint);
+ /** @} */
+
+protected:
+
+ /** @name Event handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+private:
+
+ /** @name Prepare/Cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares palette. */
+ void preparePalette();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Updates scene rectangle. */
+ void updateSceneRect();
+ /** @} */
+
+ /** @name General stuff.
+ * @{ */
+ /** Holds the Tools-pane reference. */
+ UITools *m_pTools;
+ /** @} */
+
+ /** @name Layout stuff.
+ * @{ */
+ /** Holds the minimum width hint. */
+ int m_iMinimumWidthHint;
+ /** Holds the minimum height hint. */
+ int m_iMinimumHeightHint;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_manager_tools_UIToolsView_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/medium/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIFDCreationDialog.cpp b/src/VBox/Frontends/VirtualBox/src/medium/UIFDCreationDialog.cpp
new file mode 100644
index 00000000..587c70c7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIFDCreationDialog.cpp
@@ -0,0 +1,313 @@
+/* $Id: UIFDCreationDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFDCreationDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes */
+#include <QCheckBox>
+#include <QDir>
+#include <QGridLayout>
+#include <QLabel>
+#include <QPushButton>
+
+/* GUI includes */
+#include "QIDialogButtonBox.h"
+#include "UICommon.h"
+#include "UIFDCreationDialog.h"
+#include "UIFilePathSelector.h"
+#include "UIIconPool.h"
+#include "UIMedium.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+#include "UIModalWindowManager.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+#include "CMedium.h"
+#include "CMediumFormat.h"
+
+
+UIFDCreationDialog::UIFDCreationDialog(QWidget *pParent,
+ const QString &strDefaultFolder,
+ const QString &strMachineName /* = QString() */)
+ : QIWithRetranslateUI<QDialog>(pParent)
+ , m_strDefaultFolder(strDefaultFolder)
+ , m_strMachineName(strMachineName)
+ , m_pPathLabel(0)
+ , m_pFilePathSelector(0)
+ , m_pSizeLabel(0)
+ , m_pSizeCombo(0)
+ , m_pFormatCheckBox(0)
+ , m_pButtonBox(0)
+{
+ prepare();
+}
+
+QUuid UIFDCreationDialog::mediumID() const
+{
+ return m_uMediumID;
+}
+
+/* static */
+QUuid UIFDCreationDialog::createFloppyDisk(QWidget *pParent, const QString &strDefaultFolder /* QString() */,
+ const QString &strMachineName /* = QString() */ )
+{
+ QString strStartPath(strDefaultFolder);
+
+ if (strStartPath.isEmpty())
+ strStartPath = uiCommon().defaultFolderPathForType(UIMediumDeviceType_Floppy);
+
+ QWidget *pDialogParent = windowManager().realParentWindow(pParent);
+
+ UIFDCreationDialog *pDialog = new UIFDCreationDialog(pParent, strStartPath, strMachineName);
+ if (!pDialog)
+ return QUuid();
+ windowManager().registerNewParent(pDialog, pDialogParent);
+
+ if (pDialog->exec())
+ {
+ QUuid uMediumID = pDialog->mediumID();
+ delete pDialog;
+ return uMediumID;
+ }
+ delete pDialog;
+ return QUuid();
+}
+
+void UIFDCreationDialog::accept()
+{
+ /* Make Ok button disabled first of all: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ /* Acquire medium path & formats: */
+ const QString strMediumLocation = m_pFilePathSelector->path();
+ const QVector<CMediumFormat> mediumFormats = UIMediumDefs::getFormatsForDeviceType(KDeviceType_Floppy);
+ /* Make sure we have both path and formats selected: */
+ if (strMediumLocation.isEmpty() || mediumFormats.isEmpty())
+ return;
+
+ /* Get VBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Create medium: */
+ CMedium comMedium = comVBox.CreateMedium(mediumFormats[0].GetName(), strMediumLocation,
+ KAccessMode_ReadWrite, KDeviceType_Floppy);
+ if (!comVBox.isOk())
+ {
+ msgCenter().cannotCreateMediumStorage(comVBox, strMediumLocation, this);
+ return;
+ }
+
+ /* Compose medium storage variants: */
+ QVector<KMediumVariant> variants(1, KMediumVariant_Fixed);
+ /* Decide if disk formatting is required: */
+ if (m_pFormatCheckBox && m_pFormatCheckBox->checkState() == Qt::Checked)
+ variants.push_back(KMediumVariant_Formatted);
+
+ /* Create medium storage, asynchronously: */
+ UINotificationProgressMediumCreate *pNotification =
+ new UINotificationProgressMediumCreate(comMedium, m_pSizeCombo->currentData().toLongLong(), variants);
+ connect(pNotification, &UINotificationProgressMediumCreate::sigMediumCreated,
+ &uiCommon(), &UICommon::sltHandleMediumCreated);
+ connect(pNotification, &UINotificationProgressMediumCreate::sigMediumCreated,
+ this, &UIFDCreationDialog::sltHandleMediumCreated);
+ gpNotificationCenter->append(pNotification);
+}
+
+void UIFDCreationDialog::retranslateUi()
+{
+ if (m_strMachineName.isEmpty())
+ setWindowTitle(QString("%1").arg(tr("Floppy Disk Creator")));
+ else
+ setWindowTitle(QString("%1 - %2").arg(m_strMachineName).arg(tr("Floppy Disk Creator")));
+ if (m_pPathLabel)
+ m_pPathLabel->setText(tr("File &Path:"));
+ if (m_pSizeLabel)
+ {
+ m_pSizeLabel->setText(tr("&Size:"));
+ m_pSizeLabel->setToolTip(tr("Sets the size of the floppy disk."));
+ }
+ if (m_pButtonBox)
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText("C&reate");
+ if (m_pFormatCheckBox)
+ {
+ m_pFormatCheckBox->setText(tr("&Format disk as FAT12"));
+ m_pFormatCheckBox->setToolTip(tr("Formats the floppy disk as FAT12."));
+ }
+ if (m_pSizeCombo)
+ {
+ m_pSizeCombo->setItemText(FDSize_2_88M, tr("2.88M"));
+ m_pSizeCombo->setItemText(FDSize_1_44M, tr("1.44M"));
+ m_pSizeCombo->setItemText(FDSize_1_2M, tr("1.2M"));
+ m_pSizeCombo->setItemText(FDSize_720K, tr("720K"));
+ m_pSizeCombo->setItemText(FDSize_360K, tr("360K"));
+ }
+
+ if (m_pButtonBox && m_pButtonBox->button(QDialogButtonBox::Ok))
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setToolTip(tr("Create the disk and close this dialog."));
+
+ if (m_pButtonBox && m_pButtonBox->button(QDialogButtonBox::Cancel))
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setToolTip(tr("Cancel"));
+}
+
+void UIFDCreationDialog::sltPathChanged(const QString &strPath)
+{
+ bool fIsFileUnique = checkFilePath(strPath);
+ m_pFilePathSelector->mark(!fIsFileUnique, tr("File already exists"));
+
+ if (m_pButtonBox && m_pButtonBox->button(QDialogButtonBox::Ok))
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(fIsFileUnique);
+}
+
+bool UIFDCreationDialog::checkFilePath(const QString &strPath) const
+{
+ return !QFileInfo(strPath).exists();
+}
+
+void UIFDCreationDialog::sltHandleMediumCreated(const CMedium &comMedium)
+{
+ /* Store the ID of the newly created medium: */
+ m_uMediumID = comMedium.GetId();
+
+ /* Close the dialog now: */
+ QDialog::accept();
+}
+
+void UIFDCreationDialog::prepare()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/fd_add_32px.png", ":/fd_add_16px.png"));
+#endif
+
+ setWindowModality(Qt::WindowModal);
+ setSizeGripEnabled(false);
+
+ /* Prepare main layout: */
+ QGridLayout *pLayoutMain = new QGridLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare path label: */
+ m_pPathLabel = new QLabel(this);
+ if (m_pPathLabel)
+ {
+ m_pPathLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutMain->addWidget(m_pPathLabel, 0, 0);
+ }
+ /* Prepare file path selector: */
+ m_pFilePathSelector = new UIFilePathSelector(this);
+ if (m_pFilePathSelector)
+ {
+ m_pFilePathSelector->setMode(UIFilePathSelector::Mode_File_Save);
+ const QString strFilePath = getDefaultFilePath();
+ m_pFilePathSelector->setDefaultPath(strFilePath);
+ m_pFilePathSelector->setPath(strFilePath);
+
+ pLayoutMain->addWidget(m_pFilePathSelector, 0, 1, 1, 3);
+ connect(m_pFilePathSelector, &UIFilePathSelector::pathChanged,
+ this, &UIFDCreationDialog::sltPathChanged);
+ if (m_pPathLabel)
+ m_pPathLabel->setBuddy(m_pFilePathSelector);
+ }
+
+ /* Prepare size label: */
+ m_pSizeLabel = new QLabel(this);
+ if (m_pSizeLabel)
+ {
+ m_pSizeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutMain->addWidget(m_pSizeLabel, 1, 0);
+ }
+ /* Prepare size combo: */
+ m_pSizeCombo = new QComboBox(this);
+ if (m_pSizeCombo)
+ {
+ m_pSizeCombo->insertItem(FDSize_2_88M, "2.88M", 2949120);
+ m_pSizeCombo->insertItem(FDSize_1_44M, "1.44M", 1474560);
+ m_pSizeCombo->insertItem(FDSize_1_2M, "1.2M", 1228800);
+ m_pSizeCombo->insertItem(FDSize_720K, "720K", 737280);
+ m_pSizeCombo->insertItem(FDSize_360K, "360K", 368640);
+ m_pSizeCombo->setCurrentIndex(FDSize_1_44M);
+
+ pLayoutMain->addWidget(m_pSizeCombo, 1, 1);
+
+ if (m_pSizeLabel)
+ m_pSizeLabel->setBuddy(m_pSizeCombo);
+ }
+
+ /* Prepare format check-box: */
+ m_pFormatCheckBox = new QCheckBox;
+ if (m_pFormatCheckBox)
+ {
+ m_pFormatCheckBox->setCheckState(Qt::Checked);
+ pLayoutMain->addWidget(m_pFormatCheckBox, 2, 1, 1, 2);
+ }
+
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
+ if (m_pButtonBox)
+ {
+ uiCommon().setHelpKeyword(m_pButtonBox->button(QDialogButtonBox::Help), "create-floppy-disk-image");
+ connect(m_pButtonBox, &QDialogButtonBox::accepted, this, &UIFDCreationDialog::accept);
+ connect(m_pButtonBox, &QDialogButtonBox::rejected, this, &UIFDCreationDialog::reject);
+ connect(m_pButtonBox->button(QDialogButtonBox::Help), &QPushButton::pressed,
+ &(msgCenter()), &UIMessageCenter::sltHandleHelpRequest);
+ pLayoutMain->addWidget(m_pButtonBox, 3, 0, 1, 3);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+
+#ifdef VBOX_WS_MAC
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ setFixedSize(minimumSize());
+#endif /* VBOX_WS_MAC */
+
+ /* Adjust dialog size: */
+ adjustSize();
+}
+
+QString UIFDCreationDialog::getDefaultFilePath() const
+{
+ /* Prepare default file-path on the basis of passerd default folder: */
+ QString strDefaultFilePath = m_strDefaultFolder;
+
+ /* Make sure it's not empty if possible: */
+ if (strDefaultFilePath.isEmpty())
+ strDefaultFilePath = uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder();
+ if (strDefaultFilePath.isEmpty())
+ return strDefaultFilePath;
+
+ /* Append file-path with disc name, generate unique file-name if necessary: */
+ QString strDiskname = !m_strMachineName.isEmpty() ? m_strMachineName : "NewFloppyDisk";
+ strDiskname = UICommon::findUniqueFileName(m_strDefaultFolder, strDiskname);
+
+ /* Append file-path with preferred extension finally: */
+ const QString strPreferredExtension = UIMediumDefs::getPreferredExtensionForMedium(KDeviceType_Floppy);
+ strDefaultFilePath = QDir(strDefaultFilePath).absoluteFilePath(strDiskname + "." + strPreferredExtension);
+
+ /* Return default file-path: */
+ return strDefaultFilePath;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIFDCreationDialog.h b/src/VBox/Frontends/VirtualBox/src/medium/UIFDCreationDialog.h
new file mode 100644
index 00000000..b44c71d4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIFDCreationDialog.h
@@ -0,0 +1,133 @@
+/* $Id: UIFDCreationDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIFDCreationDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_UIFDCreationDialog_h
+#define FEQT_INCLUDED_SRC_medium_UIFDCreationDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDialog>
+#include <QUuid>
+
+/* GUI Includes */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QComboBox;
+class QIDialogButtonBox;
+class QLabel;
+class UIFilePathSelector;
+class CMedium;
+
+/* A QDialog extension to get necessary setting from the user for floppy disk creation. */
+class SHARED_LIBRARY_STUFF UIFDCreationDialog : public QIWithRetranslateUI<QDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs the floppy disc creation dialog passing @a pParent to the base-class.
+ * @param strDefaultFolder Brings the default folder.
+ * @param strMachineName Brings the machine name. */
+ UIFDCreationDialog(QWidget *pParent,
+ const QString &strDefaultFolder,
+ const QString &strMachineName = QString());
+
+ /** Return the medium ID. */
+ QUuid mediumID() const;
+
+ /** Creates and shows a dialog thru which user can create a new floppy disk a VISO using the file-open dialog.
+ * @param parent Passes the parent of the dialog,
+ * @param strDefaultFolder Passes the default folder,
+ * @param strMachineName Passes the name of the machine,
+ * returns the UUID of the newly created medium if successful, a null QUuid otherwise.*/
+ static QUuid createFloppyDisk(QWidget *pParent, const QString &strDefaultFolder = QString(),
+ const QString &strMachineName = QString());
+
+
+public slots:
+
+ /** Creates the floppy disc image, asynchronously. */
+ virtual void accept() /* override final */;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+private slots:
+
+ /** Handles signal about @a comMedium was created. */
+ void sltHandleMediumCreated(const CMedium &comMedium);
+ void sltPathChanged(const QString &strPath);
+
+private:
+
+ /** Floppy disc sizes. */
+ enum FDSize
+ {
+ FDSize_2_88M,
+ FDSize_1_44M,
+ FDSize_1_2M,
+ FDSize_720K,
+ FDSize_360K
+ };
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Returns default file-path. */
+ QString getDefaultFilePath() const;
+ /** Returns false if the file is already exists. */
+ bool checkFilePath(const QString &strPath) const;
+
+ /** Holds the default folder. */
+ QString m_strDefaultFolder;
+ /** Holds the machine name. */
+ QString m_strMachineName;
+
+ /** Holds the path label instance. */
+ QLabel *m_pPathLabel;
+ /** Holds the file path selector instance. */
+ UIFilePathSelector *m_pFilePathSelector;
+ /** Holds the size label instance. */
+ QLabel *m_pSizeLabel;
+ /** Holds the size combo instance. */
+ QComboBox *m_pSizeCombo;
+ /** Holds the format check-box instance. */
+ QCheckBox *m_pFormatCheckBox;
+ /** holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+
+ /** Holds the created medium ID. */
+ QUuid m_uMediumID;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_UIFDCreationDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMedium.cpp b/src/VBox/Frontends/VirtualBox/src/medium/UIMedium.cpp
new file mode 100644
index 00000000..27cb066f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMedium.cpp
@@ -0,0 +1,669 @@
+/* $Id: UIMedium.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMedium class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QDir>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIErrorString.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMedium.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "CMachine.h"
+#include "CSnapshot.h"
+
+QUuid UIMedium::m_uNullID;
+QString UIMedium::m_sstrTable = QString("<table>%1</table>");
+QString UIMedium::m_sstrRow = QString("<tr><td>%1</td></tr>");
+
+UIMedium::UIMedium()
+ : m_type(UIMediumDeviceType_Invalid)
+ , m_medium(CMedium())
+ , m_state(KMediumState_NotCreated)
+ , m_enmMediumType(KMediumType_Max)
+ , m_enmMediumVariant(KMediumVariant_Max)
+{
+ refresh();
+}
+
+UIMedium::UIMedium(const CMedium &medium, UIMediumDeviceType type)
+ : m_type(type)
+ , m_medium(medium)
+ , m_state(KMediumState_NotCreated)
+ , m_enmMediumType(KMediumType_Max)
+ , m_enmMediumVariant(KMediumVariant_Max)
+{
+ refresh();
+}
+
+UIMedium::UIMedium(const CMedium &medium, UIMediumDeviceType type, KMediumState state)
+ : m_type(type)
+ , m_medium(medium)
+ , m_state(state)
+ , m_enmMediumType(KMediumType_Max)
+ , m_enmMediumVariant(KMediumVariant_Max)
+{
+ refresh();
+}
+
+UIMedium::UIMedium(const UIMedium &other)
+{
+ *this = other;
+}
+
+UIMedium& UIMedium::operator=(const UIMedium &other)
+{
+ m_type = other.type();
+
+ m_medium = other.medium();
+
+ m_state = other.state();
+ m_result = other.result();
+ m_strLastAccessError = other.lastAccessError();
+
+ m_uId = other.id();
+ m_uRootId = other.rootID();
+ m_uParentId = other.parentID();
+
+ m_uKey = other.key();
+
+ m_strName = other.name();
+ m_strLocation = other.location();
+ m_strDescription = other.description();
+
+ m_uSize = other.sizeInBytes();
+ m_uLogicalSize = other.logicalSizeInBytes();
+ m_strSize = other.size();
+ m_strLogicalSize = other.logicalSize();
+
+ m_enmMediumType = other.mediumType();
+ m_enmMediumVariant = other.mediumVariant();
+
+ m_strHardDiskType = other.hardDiskType();
+ m_strHardDiskFormat = other.hardDiskFormat();
+ m_fHasChildren = other.hasChildren();
+ m_strStorageDetails = other.storageDetails();
+ m_strEncryptionPasswordID = other.encryptionPasswordID();
+
+ m_strUsage = other.usage();
+ m_strToolTip = other.tip();
+ m_machineIds = other.machineIds();
+ m_curStateMachineIds = other.curStateMachineIds();
+
+ m_noDiffs = other.cache();
+
+ m_fHidden = other.m_fHidden;
+ m_fUsedByHiddenMachinesOnly = other.m_fUsedByHiddenMachinesOnly;
+ m_fReadOnly = other.isReadOnly();
+ m_fUsedInSnapshots = other.isUsedInSnapshots();
+ m_fHostDrive = other.isHostDrive();
+ m_fEncrypted = other.isEncrypted();
+
+ return *this;
+}
+
+void UIMedium::blockAndQueryState()
+{
+ /* Ignore for NULL medium: */
+ if (m_medium.isNull())
+ return;
+
+ /* Acquire actual medium state: */
+ m_state = m_medium.RefreshState();
+
+ /* Save the result to distinguish between
+ * inaccessible and e.g. uninitialized objects: */
+ m_result = COMResult(m_medium);
+ if (!m_result.isOk())
+ {
+ m_state = KMediumState_Inaccessible;
+ m_strLastAccessError = QString();
+ }
+ else
+ m_strLastAccessError = m_medium.GetLastAccessError();
+
+ /* Refresh finally: */
+ refresh();
+}
+
+void UIMedium::refresh()
+{
+ /* Reset ID parameters: */
+ m_uId = nullID();
+ m_uRootId = nullID();
+ m_uParentId = nullID();
+
+ /* Reset cache parameters: */
+ //m_strKey = nullID();
+
+ /* Reset name/location/description/size parameters: */
+ m_strName = QApplication::translate("UICommon", "Empty", "medium");
+ m_strLocation = m_strSize = m_strLogicalSize = QString("--");
+ m_strDescription = QString();
+ m_uSize = m_uLogicalSize = 0;
+
+ /* Reset medium type & variant parameter: */
+ m_enmMediumType = KMediumType_Max;
+ m_enmMediumVariant = KMediumVariant_Max;
+
+ /* Reset hard drive related parameters: */
+ m_strHardDiskType = QString();
+ m_strHardDiskFormat = QString();
+ m_fHasChildren = false;
+ m_strStorageDetails = QString();
+ m_strEncryptionPasswordID = QString();
+
+ /* Reset data parameters: */
+ m_strUsage = QString();
+ m_strToolTip = QString();
+ m_machineIds.clear();
+ m_curStateMachineIds.clear();
+
+ /* Reset m_noDiffs: */
+ m_noDiffs.isSet = false;
+
+ /* Reset flags: */
+ m_fHidden = false;
+ m_fUsedByHiddenMachinesOnly = false;
+ m_fReadOnly = false;
+ m_fUsedInSnapshots = false;
+ m_fHostDrive = false;
+ m_fEncrypted = false;
+
+ /* For non NULL medium: */
+ if (!m_medium.isNull())
+ {
+ /* Refresh medium ID: */
+ m_uId = normalizedID(m_medium.GetId());
+ /* Refresh root medium ID: */
+ m_uRootId = m_uId;
+
+ /* Init medium key if necessary: */
+ if (m_uKey.isNull())
+ m_uKey = m_uId;
+
+ /* Check whether this is host-drive medium: */
+ m_fHostDrive = m_medium.GetHostDrive();
+
+ /* Refresh medium description: */
+ m_strDescription = m_medium.GetDescription();
+
+ /* Refresh medium name: */
+ if (!m_fHostDrive)
+ m_strName = m_medium.GetName();
+ else if (m_strDescription.isEmpty())
+ m_strName = QApplication::translate("UICommon", "Host Drive '%1'", "medium").arg(QDir::toNativeSeparators(m_medium.GetLocation()));
+ else
+ m_strName = QApplication::translate("UICommon", "Host Drive %1 (%2)", "medium").arg(m_strDescription, m_medium.GetName());
+ /* Refresh medium location: */
+ if (!m_fHostDrive)
+ m_strLocation = QDir::toNativeSeparators(m_medium.GetLocation());
+
+ /* Refresh medium size and logical size: */
+ if (!m_fHostDrive)
+ {
+ /* Only for created and accessible media: */
+ if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated)
+ {
+ m_uSize = m_medium.GetSize();
+ m_strSize = UITranslator::formatSize(m_uSize);
+ if (m_type == UIMediumDeviceType_HardDisk)
+ {
+ m_uLogicalSize = m_medium.GetLogicalSize();
+ m_strLogicalSize = UITranslator::formatSize(m_uLogicalSize);
+ }
+ else
+ {
+ m_uLogicalSize = m_uSize;
+ m_strLogicalSize = m_strSize;
+ }
+ }
+ }
+
+ /* Refresh medium type & variant: */
+ m_enmMediumType = m_medium.GetType();
+ qlonglong iMediumVariant = 0;
+ foreach (const KMediumVariant &enmVariant, m_medium.GetVariant())
+ iMediumVariant |= enmVariant;
+ m_enmMediumVariant = (KMediumVariant)iMediumVariant;
+
+ /* For hard drive medium: */
+ if (m_type == UIMediumDeviceType_HardDisk)
+ {
+ /* Refresh hard drive disk type: */
+ m_strHardDiskType = mediumTypeToString(m_medium);
+ /* Refresh hard drive format: */
+ m_strHardDiskFormat = m_medium.GetFormat();
+
+ /* Refresh hard drive parental status: */
+ m_fHasChildren = m_medium.GetChildren().size();
+
+ /* Refresh hard drive storage details: */
+ m_strStorageDetails = gpConverter->toString(m_enmMediumVariant);
+
+ /* Check whether this is read-only hard drive: */
+ m_fReadOnly = m_medium.GetReadOnly();
+
+ /* Refresh parent hard drive ID: */
+ CMedium parentMedium = m_medium.GetParent();
+ if (!parentMedium.isNull())
+ m_uParentId = normalizedID(parentMedium.GetId());
+
+ /* Only for created and accessible media: */
+ if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated)
+ {
+ /* Refresh root hard drive ID: */
+ while (!parentMedium.isNull())
+ {
+ m_uRootId = normalizedID(parentMedium.GetId());
+ parentMedium = parentMedium.GetParent();
+ }
+
+ /* Refresh encryption attributes: */
+ if (m_uRootId != m_uId)
+ {
+ m_strEncryptionPasswordID = root().encryptionPasswordID();
+ m_fEncrypted = root().isEncrypted();
+ }
+ else
+ {
+ QString strCipher;
+ CMedium medium(m_medium);
+ const QString strEncryptionPasswordID = medium.GetEncryptionSettings(strCipher);
+ if (medium.isOk())
+ {
+ m_strEncryptionPasswordID = strEncryptionPasswordID;
+ m_fEncrypted = true;
+ }
+ }
+ }
+ }
+
+ /* Check whether this is hidden medium: */
+ QString strHints = m_medium.GetProperty("Special/GUI/Hints");
+ if (!strHints.isEmpty())
+ {
+ QStringList hints(strHints.split(','));
+ if (hints.contains("Hide", Qt::CaseInsensitive))
+ m_fHidden = true;
+ }
+
+ /* Refresh usage data: */
+ m_curStateMachineIds.clear();
+ m_machineIds = m_medium.GetMachineIds().toList();
+ if (m_machineIds.size() > 0)
+ {
+ /* Get CVirtualBox object: */
+ CVirtualBox vbox = uiCommon().virtualBox();
+
+ /* By default we assuming that this medium is attached
+ * to 'hidden' machines only, if at least one machine present: */
+ m_fUsedByHiddenMachinesOnly = true;
+
+ /* Prepare machine usage: */
+ QString strMachineUsage;
+ /* Walk through all the machines this medium attached to: */
+ foreach (const QUuid &uMachineID, m_machineIds)
+ {
+ /* Look for the corresponding machine: */
+ CMachine machine = vbox.FindMachine(uMachineID.toString());
+
+ /* UIMedium object can wrap newly created CMedium object
+ * which belongs to not yet registered machine, like while creating VM clone.
+ * We can skip such a machines in usage string. */
+ if (machine.isNull())
+ {
+ /* Since we can't precisely check 'hidden' status for that machine in such case,
+ * we have to assume that medium attached not only to 'hidden' machines: */
+ m_fUsedByHiddenMachinesOnly = false;
+ continue;
+ }
+
+ /* Finally we can precisely check if current machine is 'hidden': */
+ if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineID))
+ m_fUsedByHiddenMachinesOnly = false;
+
+ /* Prepare snapshot usage: */
+ QString strSnapshotUsage;
+ /* Walk through all the snapshots this medium attached to: */
+ foreach (const QUuid &uSnapshotID, m_medium.GetSnapshotIds(uMachineID))
+ {
+ if (uSnapshotID == uMachineID)
+ {
+ /* The medium is attached to the machine in the current
+ * state, we don't distinguish this for now by always
+ * giving the VM name in front of snapshot names. */
+ m_curStateMachineIds.push_back(uSnapshotID);
+ continue;
+ }
+
+ /* Look for the corresponding snapshot: */
+ CSnapshot snapshot = machine.FindSnapshot(uSnapshotID.toString());
+
+ /* Snapshot can be NULL while takeSnaphot is in progress: */
+ if (snapshot.isNull())
+ continue;
+
+ /* Refresh snapshot usage flag: */
+ m_fUsedInSnapshots = true;
+
+ /* Append snapshot usage: */
+ if (!strSnapshotUsage.isNull())
+ strSnapshotUsage += ", ";
+ strSnapshotUsage += snapshot.GetName();
+ }
+
+ /* Append machine usage: */
+ if (!strMachineUsage.isNull())
+ strMachineUsage += ", ";
+ strMachineUsage += machine.GetName();
+
+ /* Append snapshot usage: */
+ if (!strSnapshotUsage.isNull())
+ strMachineUsage += QString(" (%2)").arg(strSnapshotUsage);
+ }
+
+ /* Append machine usage: */
+ if (!strMachineUsage.isEmpty())
+ m_strUsage += strMachineUsage;
+ }
+
+ /* Refresh tool-tip: */
+ m_strToolTip = m_sstrRow.arg(QString("<p style=white-space:pre><b>%1</b></p>").arg(m_fHostDrive ? m_strName : m_strLocation));
+ if (m_type == UIMediumDeviceType_HardDisk)
+ {
+ m_strToolTip += m_sstrRow.arg(QApplication::translate("UICommon", "<p style=white-space:pre>Type (Format): %1 (%2)</p>", "medium")
+ .arg(m_strHardDiskType).arg(m_strHardDiskFormat));
+ }
+ m_strToolTip += m_sstrRow.arg(QApplication::translate("UICommon", "<p>Attached to: %1</p>", "image")
+ .arg(m_strUsage.isNull() ? QApplication::translate("UICommon", "<i>Not Attached</i>", "image") : m_strUsage));
+ switch (m_state)
+ {
+ case KMediumState_NotCreated:
+ {
+ m_strToolTip += m_sstrRow.arg(QApplication::translate("UICommon", "<i>Checking accessibility...</i>", "medium"));
+ break;
+ }
+ case KMediumState_Inaccessible:
+ {
+ if (m_result.isOk())
+ {
+ /* Not Accessible: */
+ m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(UITranslator::highlight(m_strLastAccessError, true /* aToolTip */));
+ }
+ else
+ {
+ /* Accessibility check (eg GetState()) itself failed: */
+ m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(QApplication::translate("UICommon", "Failed to check accessibility of disk image files.", "medium")) +
+ m_sstrRow.arg(UIErrorString::formatErrorInfo(m_result) + ".");
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void UIMedium::updateParentID()
+{
+ m_uParentId = nullID();
+ if (m_type == UIMediumDeviceType_HardDisk)
+ {
+ CMedium parentMedium = m_medium.GetParent();
+ if (!parentMedium.isNull())
+ m_uParentId = normalizedID(parentMedium.GetId());
+ }
+}
+
+QString UIMedium::toolTip(bool fNoDiffs /* = false */, bool fCheckRO /* = false */, bool fNullAllowed /* = false */) const
+{
+ QString strTip;
+
+ if (m_medium.isNull())
+ {
+ strTip = fNullAllowed ? m_sstrRow.arg(QApplication::translate("UICommon", "<b>No disk image file selected</b>", "medium")) +
+ m_sstrRow.arg(QApplication::translate("UICommon", "You can also change this while the machine is running.")) :
+ m_sstrRow.arg(QApplication::translate("UICommon", "<b>No disk image files available</b>", "medium")) +
+ m_sstrRow.arg(QApplication::translate("UICommon", "You can create or add disk image files in the virtual machine settings."));
+ }
+ else
+ {
+ unconst(this)->checkNoDiffs(fNoDiffs);
+
+ strTip = fNoDiffs ? m_noDiffs.toolTip : m_strToolTip;
+
+ if (fCheckRO && m_fReadOnly)
+ strTip += m_sstrRow.arg("<hr>") +
+ m_sstrRow.arg(QApplication::translate("UICommon",
+ "Attaching this hard disk will be performed indirectly using "
+ "a newly created differencing hard disk.", "medium"));
+ }
+
+ return m_sstrTable.arg(strTip);
+}
+
+QPixmap UIMedium::icon(bool fNoDiffs /* = false */, bool fCheckRO /* = false */) const
+{
+ QPixmap pixmap;
+
+ if (state(fNoDiffs) == KMediumState_Inaccessible)
+ pixmap = result(fNoDiffs).isOk() ? generalIconPool().warningIcon() : generalIconPool().errorIcon();
+
+ if (fCheckRO && m_fReadOnly)
+ {
+ QIcon icon = UIIconPool::iconSet(":/hd_create_16px.png");
+ pixmap = UIIconPool::joinPixmaps(pixmap, icon.pixmap(icon.availableSizes().value(0, QSize(16, 16))));
+ }
+
+ return pixmap;
+}
+
+QString UIMedium::details(bool fNoDiffs /* = false */,
+ bool fPredictDiff /* = false */,
+ bool fUseHTML /* = false */) const
+{
+ /// @todo the below check is rough; if m_medium becomes uninitialized, any
+ // of getters called afterwards will also fail. The same relates to the
+ // root hard drive object (that will be the hard drive itself in case of
+ // non-differencing disks). However, this check was added to fix a
+ // particular use case: when the hard drive is a differencing hard drive and
+ // it happens to be discarded (and uninitialized) after this method is
+ // called but before we read all its properties (yes, it's possible!), the
+ // root object will be null and calling methods on it will assert in the
+ // debug builds. This check seems to be enough as a quick solution (fresh
+ // hard drive attachments will be re-read by a machine state change signal
+ // after the discard operation is finished, so the user will eventually see
+ // correct data), but in order to solve the problem properly we need to use
+ // exceptions everywhere (or check the result after every method call). See
+ // @bugref{2149}.
+
+ if (m_medium.isNull() || m_fHostDrive)
+ return m_strName;
+
+ if (!m_medium.isOk())
+ return QString();
+
+ QString strDetails, strText;
+
+ /* Note: root accessible only if medium enumerated: */
+ UIMedium rootMedium = root();
+ KMediumState eState = m_state;
+
+ if (m_type == UIMediumDeviceType_HardDisk)
+ {
+ if (fNoDiffs)
+ {
+ bool isDiff = (!fPredictDiff && parentID() != nullID()) || (fPredictDiff && m_fReadOnly);
+
+ strDetails = isDiff && fUseHTML ?
+ QString("<i>%1</i>, ").arg(rootMedium.m_strHardDiskType) :
+ QString("%1, ").arg(rootMedium.m_strHardDiskType);
+
+ eState = this->state(true /* fNoDiffs */);
+
+ if (rootMedium.m_state == KMediumState_NotCreated)
+ eState = KMediumState_NotCreated;
+ }
+ else
+ {
+ strDetails = QString("%1, ").arg(rootMedium.m_strHardDiskType);
+ }
+
+ /* Add encryption status: */
+ if (m_fEncrypted)
+ strDetails += QString("%1, ").arg(QApplication::translate("UICommon", "Encrypted", "medium"));
+ }
+
+ /// @todo prepend the details with the warning/error icon when not accessible
+
+ switch (eState)
+ {
+ case KMediumState_NotCreated:
+ strText = QApplication::translate("UICommon", "Checking...", "medium");
+ strDetails += fUseHTML ? QString("<i>%1</i>").arg(strText) : strText;
+ break;
+ case KMediumState_Inaccessible:
+ strText = QApplication::translate("UICommon", "Inaccessible", "medium");
+ strDetails += fUseHTML ? QString("<b>%1</b>").arg(strText) : strText;
+ break;
+ default:
+ strDetails += m_type == UIMediumDeviceType_HardDisk ? rootMedium.m_strLogicalSize : rootMedium.m_strSize;
+ break;
+ }
+
+ strDetails = fUseHTML ?
+ QString("%1 (<nobr>%2</nobr>)").arg(QFileInfo(rootMedium.m_strName).fileName(), strDetails) :
+ QString("%1 (%2)").arg(QFileInfo(rootMedium.m_strName).fileName(), strDetails);
+
+ return strDetails;
+}
+
+/* static */
+QUuid UIMedium::nullID()
+{
+ return m_uNullID;
+}
+
+/* static */
+QUuid UIMedium::normalizedID(const QUuid &uID)
+{
+ /// @todo wipe out!
+ return uID;
+}
+
+/* static */
+bool UIMedium::isMediumAttachedToHiddenMachinesOnly(const UIMedium &medium)
+{
+ /* Iterate till the root: */
+ UIMedium mediumIterator = medium;
+ do
+ {
+ /* Ignore medium if its hidden
+ * or attached to hidden machines only: */
+ if (mediumIterator.isHidden())
+ return true;
+ /* Move iterator to parent: */
+ mediumIterator = mediumIterator.parent();
+ }
+ while (!mediumIterator.isNull());
+ /* False by default: */
+ return false;
+}
+
+UIMedium UIMedium::root() const
+{
+ /* Redirect call to UICommon: */
+ return uiCommon().medium(m_uRootId);
+}
+
+UIMedium UIMedium::parent() const
+{
+ /* Redirect call to UICommon: */
+ return uiCommon().medium(m_uParentId);
+}
+
+void UIMedium::checkNoDiffs(bool fNoDiffs)
+{
+ if (!fNoDiffs || m_noDiffs.isSet)
+ return;
+
+ m_noDiffs.toolTip = QString();
+
+ m_noDiffs.state = m_state;
+ for (UIMedium parentMedium = parent(); !parentMedium.isNull(); parentMedium = parentMedium.parent())
+ {
+ if (parentMedium.m_state == KMediumState_Inaccessible)
+ {
+ m_noDiffs.state = parentMedium.m_state;
+
+ if (m_noDiffs.toolTip.isNull())
+ m_noDiffs.toolTip = m_sstrRow.arg(QApplication::translate("UICommon",
+ "Some of the files in this hard disk chain "
+ "are inaccessible. Please use the Virtual Medium "
+ "Manager to inspect these files.", "medium"));
+
+ if (!parentMedium.m_result.isOk())
+ {
+ m_noDiffs.result = parentMedium.m_result;
+ break;
+ }
+ }
+ }
+
+ if (parentID() != nullID() && !m_fReadOnly)
+ {
+ m_noDiffs.toolTip = root().tip() +
+ m_sstrRow.arg("<hr>") +
+ m_sstrRow.arg(QApplication::translate("UICommon",
+ "This base hard disk is indirectly attached using "
+ "the following differencing hard disk:", "medium")) +
+ m_strToolTip + m_noDiffs.toolTip;
+ }
+
+ if (m_noDiffs.toolTip.isNull())
+ m_noDiffs.toolTip = m_strToolTip;
+
+ m_noDiffs.isSet = true;
+}
+
+/* static */
+QString UIMedium::mediumTypeToString(const CMedium &comMedium)
+{
+ if (!comMedium.GetParent().isNull())
+ {
+ Assert(comMedium.GetType() == KMediumType_Normal);
+ return QApplication::translate("UICommon", "Differencing", "MediumType");
+ }
+ return gpConverter->toString(comMedium.GetType());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMedium.h b/src/VBox/Frontends/VirtualBox/src/medium/UIMedium.h
new file mode 100644
index 00000000..950e94e9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMedium.h
@@ -0,0 +1,444 @@
+/* $Id: UIMedium.h $ */
+/** @file
+ * VBox Qt GUI - UIMedium class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_UIMedium_h
+#define FEQT_INCLUDED_SRC_medium_UIMedium_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QPixmap>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UIMediumDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMedium.h"
+
+/* Other VBox includes: */
+#include "iprt/cpp/utils.h"
+
+/** Storage medium cache used to
+ * override some UIMedium attributes in the
+ * user-friendly "don't show diffs" mode. */
+struct NoDiffsCache
+{
+ /** Constructor. */
+ NoDiffsCache() : isSet(false), state(KMediumState_NotCreated) {}
+
+ /** Operator= reimplementation. */
+ NoDiffsCache& operator=(const NoDiffsCache &other)
+ {
+ isSet = other.isSet;
+ state = other.state;
+ result = other.result;
+ toolTip = other.toolTip;
+ return *this;
+ }
+
+ /** Holds whether the cache is set. */
+ bool isSet : 1;
+
+ /** Holds overriden medium state. */
+ KMediumState state;
+ /** Holds overriden medium acquiring result. */
+ COMResult result;
+ /** Holds overriden medium tool-tip. */
+ QString toolTip;
+};
+
+/** Storage medium descriptor wrapping CMedium wrapper for IMedium interface.
+ *
+ * Maintains the results of the last CMedium state (accessibility) check and precomposes
+ * string parameters such as name, location and size which can be used for various GUI tasks.
+ *
+ * Many getter methods take the boolean @a fNoDiffs argument.
+ * Unless explicitly stated otherwise, this argument, when set to @c true,
+ * will cause the corresponding property of this object's root medium to be returned instead
+ * of its own one. This is useful when hard drive medium is reflected in the user-friendly
+ * "don't show diffs" mode. For non-hard drive media, the value of this argument is irrelevant
+ * because the root object for such medium is the medium itself.
+ *
+ * Note that this class "abuses" the KMediumState_NotCreated state value to indicate that the
+ * accessibility check of the given medium (see #blockAndQueryState()) has not been done yet
+ * and therefore some parameters such as #size() are meaningless because they can be read only
+ * from the accessible medium. The real KMediumState_NotCreated state is not necessary because
+ * this class is only used with created (existing) media. */
+class SHARED_LIBRARY_STUFF UIMedium
+{
+public:
+
+ /** Default constructor.
+ * Creates NULL UIMedium which is not associated with any CMedium. */
+ UIMedium();
+
+ /** Lazy wrapping constructor.
+ * Creates the UIMedium associated with the given @a medium of the given @a type. */
+ UIMedium(const CMedium &medium, UIMediumDeviceType type);
+
+ /** Wrapping constructor with known medium state.
+ * Similarly to the previous one it creates the UIMedium associated with the
+ * given @a medium of the given @a type but sets the UIMedium @a state to passed one.
+ * Suitable when the medium state is known such as right after the medium creation. */
+ UIMedium(const CMedium &medium, UIMediumDeviceType type, KMediumState state);
+
+ /** Copy constructor.
+ * Creates the UIMedium on the basis of the passed @a other one. */
+ UIMedium(const UIMedium &other);
+
+ /** Operator= reimplementation. */
+ UIMedium& operator=(const UIMedium &other);
+
+ /** Queries the actual medium state.
+ * @note This method blocks for the duration of the state check.
+ * Since this check may take quite a while,
+ * the calling thread must not be the UI thread. */
+ void blockAndQueryState();
+
+ /** Refreshes the precomposed user-readable strings.
+ * @note Note that some string such as #size() are meaningless if the medium state is
+ * KMediumState_NotCreated (i.e. the medium has not yet been checked for accessibility). */
+ void refresh();
+
+ /** Returns the type of UIMedium object. */
+ UIMediumDeviceType type() const { return m_type; }
+
+ /** Returns the CMedium wrapped by this UIMedium object. */
+ const CMedium& medium() const { return m_medium; }
+
+ /** Returns @c true if CMedium wrapped by this UIMedium object has ID == #nullID().
+ * @note Also make sure wrapped CMedium is NULL object if his ID == #nullID(). */
+ bool isNull() const
+ {
+ AssertReturn(m_uId != nullID() || m_medium.isNull(), true);
+ return m_uId == nullID();
+ }
+
+ /** Returns the medium state.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the worst state
+ * (in terms of inaccessibility) detected on the given hard drive chain. */
+ KMediumState state(bool fNoDiffs = false) const
+ {
+ unconst(this)->checkNoDiffs(fNoDiffs);
+ return fNoDiffs ? m_noDiffs.state : m_state;
+ }
+
+ /** Returns the result of the last blockAndQueryState() call.
+ * Indicates an error and contain a proper error info if the last state check fails.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the worst result
+ * (in terms of inaccessibility) detected on the given hard drive chain. */
+ const COMResult& result(bool fNoDiffs = false) const
+ {
+ unconst(this)->checkNoDiffs(fNoDiffs);
+ return fNoDiffs ? m_noDiffs.result : m_result;
+ }
+
+ /** Returns the error result of the last blockAndQueryState() call. */
+ QString lastAccessError() const { return m_strLastAccessError; }
+
+ /** Returns the medium ID. */
+ QUuid id() const { return m_uId; }
+
+ /** Returns the medium root ID. */
+ QUuid rootID() const { return m_uRootId; }
+ /** Returns the medium parent ID. */
+ QUuid parentID() const { return m_uParentId; }
+
+ /** Updates medium parent. */
+ void updateParentID();
+
+ /** Returns the medium cache key. */
+ QUuid key() const { return m_uKey; }
+ /** Defines the medium cache @a uKey. */
+ void setKey(const QUuid &uKey) { m_uKey = uKey; }
+
+ /** Returns the medium name.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the name of root in the given hard drive chain. */
+ QString name(bool fNoDiffs = false) const { return fNoDiffs ? root().m_strName : m_strName; }
+ /** Returns the medium location.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the location of root in the given hard drive chain. */
+ QString location(bool fNoDiffs = false) const { return fNoDiffs ? root().m_strLocation : m_strLocation; }
+ /** Returns the medium description.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the description of root in the given hard drive chain. */
+ QString description(bool fNoDiffs = false) const { return fNoDiffs ? root().m_strDescription : m_strDescription; }
+
+ /** Returns the medium size in bytes.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the size of root in the given hard drive chain. */
+ qulonglong sizeInBytes(bool fNoDiffs = false) const { return fNoDiffs ? root().m_uSize : m_uSize; }
+ /** Returns the logical medium size in bytes.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the size of root in the given hard drive chain. */
+ qulonglong logicalSizeInBytes(bool fNoDiffs = false) const { return fNoDiffs ? root().m_uLogicalSize : m_uLogicalSize; }
+ /** Returns the medium size.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the size of root in the given hard drive chain. */
+ QString size(bool fNoDiffs = false) const { return fNoDiffs ? root().m_strSize : m_strSize; }
+ /** Returns the logical medium size.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the logical size of root in the given hard drive chain. */
+ QString logicalSize(bool fNoDiffs = false) const { return fNoDiffs ? root().m_strLogicalSize : m_strLogicalSize; }
+
+ /** Returns the medium disk type.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the disk type of root in the given hard drive chain. */
+ KMediumType mediumType(bool fNoDiffs = false) const { return fNoDiffs ? root().m_enmMediumType : m_enmMediumType; }
+ /** Returns the medium disk variant.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the disk variant of root in the given hard drive chain. */
+ KMediumVariant mediumVariant(bool fNoDiffs = false) const { return fNoDiffs ? root().m_enmMediumVariant : m_enmMediumVariant; }
+
+ /** Returns the hard drive medium disk type.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the disk type of root in the given hard drive chain. */
+ QString hardDiskType(bool fNoDiffs = false) const { return fNoDiffs ? root().m_strHardDiskType : m_strHardDiskType; }
+ /** Returns the hard drive medium disk format.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the disk format of root in the given hard drive chain. */
+ QString hardDiskFormat(bool fNoDiffs = false) const { return fNoDiffs ? root().m_strHardDiskFormat : m_strHardDiskFormat; }
+
+ /** Returns whether the hard drive medium disk has childred.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the disk format of root in the given hard drive chain. */
+ bool hasChildren(bool fNoDiffs = false) const { return fNoDiffs ? root().m_fHasChildren : m_fHasChildren; }
+
+ /** Returns the hard drive medium storage details. */
+ QString storageDetails() const { return m_strStorageDetails; }
+ /** Returns the hard drive medium encryption password ID. */
+ QString encryptionPasswordID() const { return m_strEncryptionPasswordID; }
+
+ /** Returns the medium usage data.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @note In "don't show diffs" mode, this method returns the usage data of root in the given hard drive chain. */
+ QString usage(bool fNoDiffs = false) const { return fNoDiffs ? root().m_strUsage : m_strUsage; }
+
+ /** Returns the short version of medium tool-tip. */
+ QString tip() const { return m_strToolTip; }
+
+ /** Returns the full version of medium tool-tip.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @param fCheckRO @c true to perform the #readOnly() check and add a notice accordingly.
+ * @param fNullAllowed @c true to allow NULL medium description to be mentioned in the tool-tip.
+ * @note In "don't show diffs" mode (where the attributes of the base hard drive are shown instead
+ * of the attributes of the differencing hard drive), extra information will be added to the
+ * tooltip to give the user a hint that the medium is actually a differencing hard drive. */
+ QString toolTip(bool fNoDiffs = false, bool fCheckRO = false, bool fNullAllowed = false) const;
+
+ /** Shortcut to <tt>#toolTip(fNoDiffs, true, fNullAllowed)</tt>. */
+ QString toolTipCheckRO(bool fNoDiffs = false, bool fNullAllowed = false) const { return toolTip(fNoDiffs, true, fNullAllowed); }
+
+ /** Returns an icon corresponding to the medium state.
+ * Distinguishes between the Inaccessible state and the situation when querying the state itself failed.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @param fCheckRO @c true to perform the #readOnly() check and change the icon accordingly.
+ * @note In "don't show diffs" mode (where the attributes of the base hard drive are shown instead
+ * of the attributes of the differencing hard drive), the most worst medium state on the given
+ * hard drive chain will be used to select the medium icon. */
+ QPixmap icon(bool fNoDiffs = false, bool fCheckRO = false) const;
+
+ /** Shortcut to <tt>#icon(fNoDiffs, true)</tt>. */
+ QPixmap iconCheckRO(bool fNoDiffs = false) const { return icon(fNoDiffs, true); }
+
+ /** Returns the details of this medium as a single-line string.
+ * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
+ * @param fPredictDiff @c true to mark the hard drive as differencing if attaching
+ * it would create a differencing hard drive.
+ * @param fUseHTML @c true to allow for emphasizing using bold and italics.
+ * @note For hard drives, the details include the location, type and the logical size of the hard drive.
+ * Note that if @a fNoDiffs is @c true, these properties are queried on the root hard drive of the
+ * given hard drive because the primary purpose of the returned string is to be human readable
+ * (so that seeing a complex diff hard drive name is usually not desirable).
+ * @note For other medium types, the location and the actual size are returned.
+ * Arguments @a fPredictDiff and @a fNoDiffs are ignored in this case.
+ * @note Use #detailsHTML() instead of passing @c true for @a fUseHTML.
+ * @note The medium object may become uninitialized by a third party while this method is reading its properties.
+ * In this case, the method will return an empty string. */
+ QString details(bool fNoDiffs = false, bool fPredictDiff = false, bool fUseHTML = false) const;
+
+ /** Shortcut to <tt>#details(fNoDiffs, fPredictDiff, true)</tt>. */
+ QString detailsHTML(bool fNoDiffs = false, bool fPredictDiff = false) const { return details(fNoDiffs, fPredictDiff, true); }
+
+ /** Returns the medium cache for "don't show diffs" mode. */
+ const NoDiffsCache& cache() const { return m_noDiffs; }
+
+ /** Returns whether this medium is hidden.
+ * @note The medium is considered 'hidden' if it has corresponding
+ * medium property or is connected to 'hidden' VMs only. */
+ bool isHidden() const { return m_fHidden || m_fUsedByHiddenMachinesOnly; }
+
+ /** Returns whether this medium is read-only
+ * (either because it is Immutable or because it has child hard drives).
+ * @note Read-only medium can only be attached indirectly. */
+ bool isReadOnly() const { return m_fReadOnly; }
+
+ /** Returns whether this medium is attached to any VM in any snapshot. */
+ bool isUsedInSnapshots() const { return m_fUsedInSnapshots; }
+
+ /** Returns whether this medium corresponds to real host drive. */
+ bool isHostDrive() const { return m_fHostDrive; }
+
+ /** Returns whether this medium is encrypted. */
+ bool isEncrypted() const { return m_fEncrypted; }
+
+ /** Returns whether this medium is attached to any VM (in the current state or in a snapshot) in which case
+ * #usage() will contain a string with comma-separated VM names (with snapshot names, if any, in parenthesis). */
+ bool isUsed() const { return !m_strUsage.isNull(); }
+
+ /** Returns whether this medium is attached to the given machine in the current state. */
+ bool isAttachedInCurStateTo(const QUuid &uMachineId) const { return m_curStateMachineIds.indexOf(uMachineId) >= 0; }
+
+ /** Returns a vector of IDs of all machines this medium is attached to. */
+ const QList<QUuid>& machineIds() const { return m_machineIds; }
+ /** Returns a vector of IDs of all machines this medium is attached to
+ * in their current state (i.e. excluding snapshots). */
+ const QList<QUuid>& curStateMachineIds() const { return m_curStateMachineIds; }
+
+ /** Returns NULL medium ID. */
+ static QUuid nullID();
+
+ /** Returns passed @a uID if it's valid or #nullID() overwise. */
+ static QUuid normalizedID(const QUuid &uID);
+
+ /** Determines if passed @a medium is attached to hidden machines only. */
+ static bool isMediumAttachedToHiddenMachinesOnly(const UIMedium &medium);
+
+private:
+
+ /** Returns medium root. */
+ UIMedium root() const;
+ /** Returns medium parent. */
+ UIMedium parent() const;
+
+ /** Checks if m_noDiffs is filled in and does it if not.
+ * @param fNoDiffs @if false, this method immediately returns. */
+ void checkNoDiffs(bool fNoDiffs);
+
+ /** Returns string representation for passed @a comMedium type. */
+ static QString mediumTypeToString(const CMedium &comMedium);
+
+ /** Holds the type of UIMedium object. */
+ UIMediumDeviceType m_type;
+
+ /** Holds the CMedium wrapped by this UIMedium object. */
+ CMedium m_medium;
+
+ /** Holds the medium state. */
+ KMediumState m_state;
+ /** Holds the result of the last blockAndQueryState() call. */
+ COMResult m_result;
+ /** Holds the error result of the last blockAndQueryState() call. */
+ QString m_strLastAccessError;
+
+ /** Holds the medium ID. */
+ QUuid m_uId;
+ /** Holds the medium root ID. */
+ QUuid m_uRootId;
+ /** Holds the medium parent ID. */
+ QUuid m_uParentId;
+
+ /** Holds the medium cache key. */
+ QUuid m_uKey;
+
+ /** Holds the medium name. */
+ QString m_strName;
+ /** Holds the medium location. */
+ QString m_strLocation;
+ /** Holds the medium description. */
+ QString m_strDescription;
+
+ /** Holds the medium size in bytes. */
+ qulonglong m_uSize;
+ /** Holds the logical medium size in bytes. */
+ qulonglong m_uLogicalSize;
+ /** Holds the medium size. */
+ QString m_strSize;
+ /** Holds the logical medium size. */
+ QString m_strLogicalSize;
+
+ /** Holds the medium disk type. */
+ KMediumType m_enmMediumType;
+ /** Holds the medium disk variant. */
+ KMediumVariant m_enmMediumVariant;
+
+ /** Holds the hard drive medium disk type. */
+ QString m_strHardDiskType;
+ /** Holds the hard drive medium disk format. */
+ QString m_strHardDiskFormat;
+ /** Holds whether the hard drive medium disk has children. */
+ bool m_fHasChildren;
+ /** Holds the hard drive medium storage details. */
+ QString m_strStorageDetails;
+ /** Holds the hard drive medium encryption password ID. */
+ QString m_strEncryptionPasswordID;
+
+ /** Holds the medium usage. */
+ QString m_strUsage;
+ /** Holds the medium tool-tip. */
+ QString m_strToolTip;
+ /** Holds the vector of IDs of all machines this medium is attached to. */
+ QList<QUuid> m_machineIds;
+ /** Hodls the vector of IDs of all machines this medium is attached to
+ * in their current state (i.e. excluding snapshots). */
+ QList<QUuid> m_curStateMachineIds;
+
+ /** Holds the medium cache for "don't show diffs" mode. */
+ NoDiffsCache m_noDiffs;
+
+ /** Holds whether this medium is 'hidden' by the corresponding medium property. */
+ bool m_fHidden : 1;
+ /** Holds whether this medium is 'hidden' because it's used by 'hidden' VMs only. */
+ bool m_fUsedByHiddenMachinesOnly : 1;
+ /** Holds whether this medium is read-only. */
+ bool m_fReadOnly : 1;
+ /** Holds whether this medium is attached to any VM in any snapshot. */
+ bool m_fUsedInSnapshots : 1;
+ /** Holds whether this medium corresponds to real host drive. */
+ bool m_fHostDrive : 1;
+ /** Holds whether this medium is encrypted. */
+ bool m_fEncrypted : 1;
+
+ /** Holds the NULL medium ID. */
+ static QUuid m_uNullID;
+ /** Holds the medium tool-tip table template. */
+ static QString m_sstrTable;
+ /** Holds the medium tool-tip table row template. */
+ static QString m_sstrRow;
+};
+Q_DECLARE_METATYPE(UIMedium);
+
+typedef QMap<QUuid, UIMedium> UIMediumMap;
+
+#endif /* !FEQT_INCLUDED_SRC_medium_UIMedium_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDefs.cpp b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDefs.cpp
new file mode 100644
index 00000000..739cb789
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDefs.cpp
@@ -0,0 +1,154 @@
+/* $Id: UIMediumDefs.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMedium related implementations.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIMediumDefs.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CMediumFormat.h"
+#include "CSystemProperties.h"
+
+/* COM includes: */
+#include "CMediumFormat.h"
+#include "CSystemProperties.h"
+#include "CVirtualBox.h"
+
+
+/* Convert global medium type (KDeviceType) to local (UIMediumDeviceType): */
+UIMediumDeviceType UIMediumDefs::mediumTypeToLocal(KDeviceType globalType)
+{
+ switch (globalType)
+ {
+ case KDeviceType_HardDisk:
+ return UIMediumDeviceType_HardDisk;
+ case KDeviceType_DVD:
+ return UIMediumDeviceType_DVD;
+ case KDeviceType_Floppy:
+ return UIMediumDeviceType_Floppy;
+ default:
+ break;
+ }
+ return UIMediumDeviceType_Invalid;
+}
+
+/* Convert local medium type (UIMediumDeviceType) to global (KDeviceType): */
+KDeviceType UIMediumDefs::mediumTypeToGlobal(UIMediumDeviceType localType)
+{
+ switch (localType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ return KDeviceType_HardDisk;
+ case UIMediumDeviceType_DVD:
+ return KDeviceType_DVD;
+ case UIMediumDeviceType_Floppy:
+ return KDeviceType_Floppy;
+ default:
+ break;
+ }
+ return KDeviceType_Null;
+}
+
+QList<QPair<QString, QString> > UIMediumDefs::MediumBackends(const CVirtualBox &comVBox, KDeviceType enmType)
+{
+ /* Prepare a list of pairs with the form <tt>{"Backend Name", "*.suffix1 .suffix2 ..."}</tt>. */
+ const CSystemProperties comSystemProperties = comVBox.GetSystemProperties();
+ QVector<CMediumFormat> mediumFormats = comSystemProperties.GetMediumFormats();
+ QList<QPair<QString, QString> > backendPropList;
+ for (int i = 0; i < mediumFormats.size(); ++i)
+ {
+ /* Acquire file extensions & device types: */
+ QVector<QString> fileExtensions;
+ QVector<KDeviceType> deviceTypes;
+ mediumFormats[i].DescribeFileExtensions(fileExtensions, deviceTypes);
+
+ /* Compose filters list: */
+ QStringList filters;
+ for (int iExtensionIndex = 0; iExtensionIndex < fileExtensions.size(); ++iExtensionIndex)
+ if (deviceTypes[iExtensionIndex] == enmType)
+ filters << QString("*.%1").arg(fileExtensions[iExtensionIndex]);
+ /* Create a pair out of the backend description and all suffix's. */
+ if (!filters.isEmpty())
+ backendPropList << QPair<QString, QString>(mediumFormats[i].GetName(), filters.join(" "));
+ }
+ return backendPropList;
+}
+
+QList<QPair<QString, QString> > UIMediumDefs::HDDBackends(const CVirtualBox &comVBox)
+{
+ return MediumBackends(comVBox, KDeviceType_HardDisk);
+}
+
+QList<QPair<QString, QString> > UIMediumDefs::DVDBackends(const CVirtualBox &comVBox)
+{
+ return MediumBackends(comVBox, KDeviceType_DVD);
+}
+
+QList<QPair<QString, QString> > UIMediumDefs::FloppyBackends(const CVirtualBox &comVBox)
+{
+ return MediumBackends(comVBox, KDeviceType_Floppy);
+}
+
+QString UIMediumDefs::getPreferredExtensionForMedium(KDeviceType enmDeviceType)
+{
+ CSystemProperties comSystemProperties = uiCommon().virtualBox().GetSystemProperties();
+ QVector<CMediumFormat> mediumFormats = comSystemProperties.GetMediumFormats();
+ for (int i = 0; i < mediumFormats.size(); ++i)
+ {
+ /* File extensions */
+ QVector <QString> fileExtensions;
+ QVector <KDeviceType> deviceTypes;
+
+ mediumFormats[i].DescribeFileExtensions(fileExtensions, deviceTypes);
+ if (fileExtensions.size() != deviceTypes.size())
+ continue;
+ for (int a = 0; a < fileExtensions.size(); ++a)
+ {
+ if (deviceTypes[a] == enmDeviceType)
+ return fileExtensions[a];
+ }
+ }
+ return QString();
+}
+
+QVector<CMediumFormat> UIMediumDefs::getFormatsForDeviceType(KDeviceType enmDeviceType)
+{
+ CSystemProperties comSystemProperties = uiCommon().virtualBox().GetSystemProperties();
+ QVector<CMediumFormat> mediumFormats = comSystemProperties.GetMediumFormats();
+ QVector<CMediumFormat> formatList;
+ for (int i = 0; i < mediumFormats.size(); ++i)
+ {
+ /* File extensions */
+ QVector <QString> fileExtensions;
+ QVector <KDeviceType> deviceTypes;
+
+ mediumFormats[i].DescribeFileExtensions(fileExtensions, deviceTypes);
+ if (deviceTypes.contains(enmDeviceType))
+ formatList.push_back(mediumFormats[i]);
+ }
+ return formatList;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDefs.h b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDefs.h
new file mode 100644
index 00000000..0c6c9c0c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDefs.h
@@ -0,0 +1,138 @@
+/* $Id: UIMediumDefs.h $ */
+/** @file
+ * VBox Qt GUI - UIMedium related declarations.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_UIMediumDefs_h
+#define FEQT_INCLUDED_SRC_medium_UIMediumDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CVirtualBox.h"
+
+/* Other VBox includes: */
+#include <VBox/com/defs.h>
+
+/* Forward declarations: */
+class CVirtualBox;
+
+
+/** Medium formats. */
+enum UIMediumFormat
+{
+ UIMediumFormat_VDI,
+ UIMediumFormat_VMDK,
+ UIMediumFormat_VHD,
+ UIMediumFormat_Parallels,
+ UIMediumFormat_QED,
+ UIMediumFormat_QCOW,
+};
+
+/** UIMediumDefs namespace. */
+namespace UIMediumDefs
+{
+ /** UIMedium types. */
+ enum UIMediumDeviceType
+ {
+ UIMediumDeviceType_HardDisk,
+ UIMediumDeviceType_DVD,
+ UIMediumDeviceType_Floppy,
+ UIMediumDeviceType_All,
+ UIMediumDeviceType_Invalid
+ };
+
+ /** Converts global medium type (KDeviceType) to local (UIMediumDeviceType). */
+ SHARED_LIBRARY_STUFF UIMediumDeviceType mediumTypeToLocal(KDeviceType globalType);
+ /** Convert local medium type (UIMediumDeviceType) to global (KDeviceType). */
+ SHARED_LIBRARY_STUFF KDeviceType mediumTypeToGlobal(UIMediumDeviceType localType);
+
+ /** Returns medium formats which are currently supported by @a comVBox for the given @a enmDeviceType. */
+ QList<QPair<QString, QString> > MediumBackends(const CVirtualBox &comVBox, KDeviceType enmDeviceType);
+ /** Returns which hard disk formats are currently supported by @a comVBox. */
+ QList<QPair<QString, QString> > HDDBackends(const CVirtualBox &comVBox);
+ /** Returns which optical disk formats are currently supported by @a comVBox. */
+ QList<QPair<QString, QString> > DVDBackends(const CVirtualBox &comVBox);
+ /** Returns which floppy disk formats are currently supported by @a comVBox. */
+ QList<QPair<QString, QString> > FloppyBackends(const CVirtualBox &comVBox);
+
+ /** Returns the first file extension of the list of file extension support for the @a enmDeviceType. */
+ QString getPreferredExtensionForMedium(KDeviceType enmDeviceType);
+ QVector<CMediumFormat> getFormatsForDeviceType(KDeviceType enmDeviceType);
+}
+/* Using this namespace globally: */
+using namespace UIMediumDefs;
+
+/** Medium-target. */
+struct UIMediumTarget
+{
+ /** Medium-target types. */
+ enum UIMediumTargetType
+ {
+ UIMediumTargetType_WithID,
+ UIMediumTargetType_WithLocation,
+ UIMediumTargetType_WithFileDialog,
+ UIMediumTargetType_CreateAdHocVISO,
+ UIMediumTargetType_CreateFloppyDisk
+ };
+
+ /** Medium-target constructor. */
+ UIMediumTarget(const QString &strName = QString(), LONG iPort = 0, LONG iDevice = 0,
+ UIMediumDeviceType aMediumType = UIMediumDeviceType_Invalid,
+ UIMediumTargetType aType = UIMediumTargetType_WithID, const QString &strData = QString())
+ : name(strName), port(iPort), device(iDevice)
+ , mediumType(aMediumType)
+ , type(aType), data(strData)
+ {}
+
+ /** Determines controller name. */
+ QString name;
+ /** Determines controller port. */
+ LONG port;
+ /** Determines controller device. */
+ LONG device;
+
+ /** Determines medium-target medium-type. */
+ UIMediumDeviceType mediumType;
+
+ /** Determines medium-target type. */
+ UIMediumTargetType type;
+ /** Depending on medium-target type holds <i>ID</i> or <i>location</i>. */
+ QString data;
+};
+
+/* Let QMetaType know about our types: */
+Q_DECLARE_METATYPE(UIMediumDeviceType);
+Q_DECLARE_METATYPE(UIMediumTarget);
+
+#endif /* !FEQT_INCLUDED_SRC_medium_UIMediumDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDetailsWidget.cpp b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDetailsWidget.cpp
new file mode 100644
index 00000000..4bcc5e19
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDetailsWidget.cpp
@@ -0,0 +1,874 @@
+/* $Id: UIMediumDetailsWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMediumDetailsWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QSlider>
+#include <QStackedLayout>
+#include <QStyle>
+#include <QTextEdit>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIFileDialog.h"
+#include "QILabel.h"
+#include "QILineEdit.h"
+#include "QITabWidget.h"
+#include "QIToolButton.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIIconPool.h"
+#include "UIMediumDetailsWidget.h"
+#include "UIMediumManager.h"
+#include "UIMediumSizeEditor.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIMediumDetailsWidget::UIMediumDetailsWidget(UIMediumManagerWidget *pParent, EmbedTo enmEmbedding)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pParent(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_oldData(UIDataMedium())
+ , m_newData(UIDataMedium())
+ , m_pTabWidget(0)
+ , m_pLabelType(0), m_pComboBoxType(0), m_pErrorPaneType(0)
+ , m_pLabelLocation(0), m_pEditorLocation(0), m_pErrorPaneLocation(0), m_pButtonLocation(0)
+ , m_pLabelDescription(0), m_pEditorDescription(0), m_pErrorPaneDescription(0)
+ , m_pLabelSize(0), m_pEditorSize(0), m_pErrorPaneSize(0)
+ , m_pButtonBox(0)
+ , m_pProgressBar(0)
+ , m_fValid(true)
+ , m_pLayoutDetails(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIMediumDetailsWidget::setCurrentType(UIMediumDeviceType enmType)
+{
+ /* If known type was requested => raise corresponding container: */
+ if (m_aContainers.contains(enmType))
+ m_pLayoutDetails->setCurrentWidget(infoContainer(enmType));
+}
+
+void UIMediumDetailsWidget::setData(const UIDataMedium &data)
+{
+ /* Cache old/new data: */
+ m_oldData = data;
+ m_newData = m_oldData;
+
+ /* Load options data: */
+ loadDataForOptions();
+ /* Load details data: */
+ loadDataForDetails();
+}
+
+void UIMediumDetailsWidget::enableDisableMediumModificationWidgets(bool fMediumIsModifiable)
+{
+ if (m_pComboBoxType)
+ m_pComboBoxType->setEnabled(fMediumIsModifiable);
+ if (m_pEditorLocation)
+ m_pEditorLocation->setEnabled(fMediumIsModifiable);
+ if (m_pEditorSize)
+ m_pEditorSize->setEnabled(fMediumIsModifiable);
+ if (m_pEditorDescription)
+ m_pEditorDescription->setEnabled(fMediumIsModifiable);
+}
+
+void UIMediumDetailsWidget::setOptionsEnabled(bool fEnabled)
+{
+ m_pTabWidget->widget(0)->setEnabled(fEnabled);
+}
+
+void UIMediumDetailsWidget::retranslateUi()
+{
+ /* Translate tab-widget: */
+ m_pTabWidget->setTabText(0, UIMediumManager::tr("&Attributes"));
+ m_pTabWidget->setTabText(1, UIMediumManager::tr("&Information"));
+
+ /* Translate 'Options' tab content. */
+
+ /* Translate labels: */
+ m_pLabelType->setText(UIMediumManager::tr("&Type:"));
+ m_pLabelLocation->setText(UIMediumManager::tr("&Location:"));
+ m_pLabelDescription->setText(UIMediumManager::tr("&Description:"));
+ m_pLabelSize->setText(UIMediumManager::tr("&Size:"));
+
+ /* Translate fields: */
+ m_pComboBoxType->setToolTip(UIMediumManager::tr("Holds the type of this medium."));
+ for (int i = 0; i < m_pComboBoxType->count(); ++i)
+ m_pComboBoxType->setItemText(i, gpConverter->toString(m_pComboBoxType->itemData(i).value<KMediumType>()));
+ m_pEditorLocation->setToolTip(UIMediumManager::tr("Holds the location of this medium."));
+ m_pButtonLocation->setToolTip(UIMediumManager::tr("Choose Medium Location"));
+ m_pEditorDescription->setToolTip(UIMediumManager::tr("Holds the description of this medium."));
+ m_pEditorSize->setToolTip(UIMediumManager::tr("Holds the size of this medium."));
+
+ /* Translate button-box: */
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setText(UIMediumManager::tr("Reset"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText(UIMediumManager::tr("Apply"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setStatusTip(UIMediumManager::tr("Reset changes in current medium details"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(UIMediumManager::tr("Apply changes in current medium details"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->
+ setToolTip(UIMediumManager::tr("Reset Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->
+ setToolTip(UIMediumManager::tr("Apply Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+
+ /* Translate 'Details' tab content. */
+
+ /* Retranslate validation: */
+ retranslateValidation();
+}
+
+void UIMediumDetailsWidget::sltTypeIndexChanged(int iIndex)
+{
+ m_newData.m_options.m_enmMediumType = m_pComboBoxType->itemData(iIndex).value<KMediumType>();
+ revalidate(m_pErrorPaneType);
+ updateButtonStates();
+}
+
+void UIMediumDetailsWidget::sltLocationPathChanged(const QString &strPath)
+{
+ m_newData.m_options.m_strLocation = strPath;
+ revalidate(m_pErrorPaneLocation);
+ updateButtonStates();
+}
+
+void UIMediumDetailsWidget::sltChooseLocationPath()
+{
+ /* Open file-save dialog to choose location for current medium: */
+ const QString strFileName = QIFileDialog::getSaveFileName(m_pEditorLocation->text(),
+ QApplication::translate("UIMediumManager", "Current extension (*.%1)")
+ .arg(QFileInfo(m_oldData.m_options.m_strLocation).suffix()),
+ this,
+ QApplication::translate("UIMediumManager", "Choose the location of this medium"),
+ 0, true, true);
+ if (!strFileName.isNull())
+ m_pEditorLocation->setText(QDir::toNativeSeparators(strFileName));
+}
+
+void UIMediumDetailsWidget::sltDescriptionTextChanged()
+{
+ m_newData.m_options.m_strDescription = m_pEditorDescription->toPlainText();
+ revalidate(m_pErrorPaneDescription);
+ updateButtonStates();
+}
+
+void UIMediumDetailsWidget::sltSizeValueChanged(qulonglong uSize)
+{
+ m_newData.m_options.m_uLogicalSize = uSize;
+ revalidate(m_pErrorPaneSize);
+ updateButtonStates();
+}
+
+void UIMediumDetailsWidget::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+ /* Make sure button-box exists: */
+ AssertPtrReturnVoid(m_pButtonBox);
+
+ /* Disable buttons first of all: */
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if (pButton == m_pButtonBox->button(QDialogButtonBox::Cancel))
+ emit sigDataChangeRejected();
+ else
+ if (pButton == m_pButtonBox->button(QDialogButtonBox::Ok))
+ emit sigDataChangeAccepted();
+}
+
+void UIMediumDetailsWidget::prepare()
+{
+ /* Prepare this: */
+ prepareThis();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Update button states finally: */
+ updateButtonStates();
+}
+
+void UIMediumDetailsWidget::prepareThis()
+{
+ /* Create layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ AssertPtrReturnVoid(pLayout);
+ {
+ /* Configure layout: */
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare tab-widget: */
+ prepareTabWidget();
+ }
+}
+
+void UIMediumDetailsWidget::prepareTabWidget()
+{
+ /* Create tab-widget: */
+ m_pTabWidget = new QITabWidget;
+ AssertPtrReturnVoid(m_pTabWidget);
+ {
+ /* Prepare 'Options' tab: */
+ prepareTabOptions();
+ /* Prepare 'Details' tab: */
+ prepareTabDetails();
+
+ /* Add into layout: */
+ layout()->addWidget(m_pTabWidget);
+ }
+}
+
+void UIMediumDetailsWidget::prepareTabOptions()
+{
+ /* Create 'Options' tab: */
+ QWidget *pTabOptions = new QWidget;
+ AssertPtrReturnVoid(pTabOptions);
+ {
+ /* Create 'Options' layout: */
+ QGridLayout *pLayoutOptions = new QGridLayout(pTabOptions);
+ AssertPtrReturnVoid(pLayoutOptions);
+ {
+#ifdef VBOX_WS_MAC
+ /* Configure layout: */
+ pLayoutOptions->setSpacing(10);
+ pLayoutOptions->setContentsMargins(10, 10, 10, 10);
+ // WORKAROUND:
+ // Using adjusted vertical spacing because there are special widgets which
+ // requires more care and attention, UIFilePathSelector and UIMediumSizeEditor.
+ pLayoutOptions->setVerticalSpacing(6);
+#endif
+
+ /* Get the required icon metric: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+
+ /* Create type label: */
+ m_pLabelType = new QLabel;
+ AssertPtrReturnVoid(m_pLabelType);
+ {
+ /* Configure label: */
+ m_pLabelType->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+
+ /* Add into layout: */
+ pLayoutOptions->addWidget(m_pLabelType, 0, 0);
+ }
+
+ /* Create type layout: */
+ QHBoxLayout *pLayoutType = new QHBoxLayout;
+ AssertPtrReturnVoid(pLayoutType);
+ {
+ /* Configure layout: */
+ pLayoutType->setContentsMargins(0, 0, 0, 0);
+
+ /* Create type editor: */
+ m_pComboBoxType = new QComboBox;
+ AssertPtrReturnVoid(m_pComboBoxType);
+ {
+ /* Configure editor: */
+ m_pLabelType->setBuddy(m_pComboBoxType);
+ m_pComboBoxType->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ m_pComboBoxType->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ connect(m_pComboBoxType, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
+ this, &UIMediumDetailsWidget::sltTypeIndexChanged);
+
+ /* Add into layout: */
+ pLayoutType->addWidget(m_pComboBoxType);
+ }
+
+ /* Add stretch: */
+ pLayoutType->addStretch();
+
+ /* Create type error pane: */
+ m_pErrorPaneType = new QLabel;
+ AssertPtrReturnVoid(m_pErrorPaneType);
+ {
+ /* Configure label: */
+ m_pErrorPaneType->setAlignment(Qt::AlignCenter);
+ m_pErrorPaneType->setPixmap(UIIconPool::iconSet(":/status_error_16px.png")
+ .pixmap(QSize(iIconMetric, iIconMetric)));
+
+ /* Add into layout: */
+ pLayoutType->addWidget(m_pErrorPaneType);
+ }
+
+ /* Add into layout: */
+ pLayoutOptions->addLayout(pLayoutType, 0, 1);
+ }
+
+ /* Create location label: */
+ m_pLabelLocation = new QLabel;
+ AssertPtrReturnVoid(m_pLabelLocation);
+ {
+ /* Configure label: */
+ m_pLabelLocation->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+
+ /* Add into layout: */
+ pLayoutOptions->addWidget(m_pLabelLocation, 1, 0);
+ }
+
+ /* Create location layout: */
+ QHBoxLayout *pLayoutLocation = new QHBoxLayout;
+ AssertPtrReturnVoid(pLayoutLocation);
+ {
+ /* Configure layout: */
+ pLayoutLocation->setContentsMargins(0, 0, 0, 0);
+
+ /* Create location editor: */
+ m_pEditorLocation = new QLineEdit;
+ AssertPtrReturnVoid(m_pEditorLocation);
+ {
+ /* Configure editor: */
+ m_pLabelLocation->setBuddy(m_pEditorLocation);
+ m_pEditorLocation->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ connect(m_pEditorLocation, &QLineEdit::textChanged,
+ this, &UIMediumDetailsWidget::sltLocationPathChanged);
+
+ /* Add into layout: */
+ pLayoutLocation->addWidget(m_pEditorLocation);
+ }
+
+ /* Create location error pane: */
+ m_pErrorPaneLocation = new QLabel;
+ AssertPtrReturnVoid(m_pErrorPaneLocation);
+ {
+ /* Configure label: */
+ m_pErrorPaneLocation->setAlignment(Qt::AlignCenter);
+ m_pErrorPaneLocation->setPixmap(UIIconPool::iconSet(":/status_error_16px.png")
+ .pixmap(QSize(iIconMetric, iIconMetric)));
+ /* Add into layout: */
+ pLayoutLocation->addWidget(m_pErrorPaneLocation);
+ }
+
+ /* Create location button: */
+ m_pButtonLocation = new QIToolButton;
+ AssertPtrReturnVoid(m_pButtonLocation);
+ {
+ /* Configure editor: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pButtonLocation->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pButtonLocation->setIcon(UIIconPool::iconSet(":/select_file_16px.png"));
+ m_pButtonLocation->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ connect(m_pButtonLocation, &QIToolButton::clicked,
+ this, &UIMediumDetailsWidget::sltChooseLocationPath);
+
+ /* Add into layout: */
+ pLayoutLocation->addWidget(m_pButtonLocation);
+ }
+
+ /* Add into layout: */
+ pLayoutOptions->addLayout(pLayoutLocation, 1, 1);
+ }
+
+ /* Create description label: */
+ m_pLabelDescription = new QLabel;
+ AssertPtrReturnVoid(m_pLabelDescription);
+ {
+ /* Configure label: */
+ m_pLabelDescription->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+
+ /* Add into layout: */
+ pLayoutOptions->addWidget(m_pLabelDescription, 2, 0);
+ }
+
+ /* Create description layout: */
+ QGridLayout *pLayoutDescription = new QGridLayout;
+ AssertPtrReturnVoid(pLayoutDescription);
+ {
+ /* Configure layout: */
+ pLayoutDescription->setContentsMargins(0, 0, 0, 0);
+
+ /* Create description editor: */
+ m_pEditorDescription = new QTextEdit;
+ AssertPtrReturnVoid(m_pEditorDescription);
+ {
+ /* Configure editor: */
+ m_pLabelDescription->setBuddy(m_pEditorDescription);
+ QFontMetrics fontMetrics = m_pEditorDescription->fontMetrics();
+ QTextDocument *pTextDocument = m_pEditorDescription->document();
+ const int iMinimumHeight = fontMetrics.lineSpacing() * 3
+ + pTextDocument->documentMargin() * 2
+ + m_pEditorDescription->frameWidth() * 2;
+ m_pEditorDescription->setMaximumHeight(iMinimumHeight);
+ connect(m_pEditorDescription, &QTextEdit::textChanged,
+ this, &UIMediumDetailsWidget::sltDescriptionTextChanged);
+
+ /* Add into layout: */
+ pLayoutDescription->addWidget(m_pEditorDescription, 0, 0, 2, 1);
+ }
+
+ /* Create description error pane: */
+ m_pErrorPaneDescription = new QLabel;
+ AssertPtrReturnVoid(m_pErrorPaneDescription);
+ {
+ /* Configure label: */
+ m_pErrorPaneDescription->setAlignment(Qt::AlignCenter);
+ m_pErrorPaneDescription->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ m_pErrorPaneDescription->setPixmap(UIIconPool::iconSet(":/status_error_16px.png")
+ .pixmap(QSize(iIconMetric, iIconMetric)));
+
+ /* Add into layout: */
+ pLayoutDescription->addWidget(m_pErrorPaneDescription, 0, 1, Qt::AlignCenter);
+ }
+
+ /* Add into layout: */
+ pLayoutOptions->addLayout(pLayoutDescription, 2, 1, 2, 1);
+ }
+
+ /* Create size label: */
+ m_pLabelSize = new QLabel;
+ AssertPtrReturnVoid(m_pLabelSize);
+ {
+ /* Configure label: */
+ m_pLabelSize->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+
+ /* Add into layout: */
+ pLayoutOptions->addWidget(m_pLabelSize, 4, 0);
+ }
+
+ /* Create size layout: */
+ QGridLayout *pLayoutSize = new QGridLayout;
+ AssertPtrReturnVoid(pLayoutSize);
+ {
+ /* Configure layout: */
+ pLayoutSize->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // Using adjusted vertical stretch because there is special widget
+ // which requires more care and attention, UIMediumSizeEditor.
+ pLayoutSize->setRowStretch(0, 3);
+ pLayoutSize->setRowStretch(1, 2);
+#endif
+
+ /* Create size editor: */
+ m_pEditorSize = new UIMediumSizeEditor(0 /* parent */);
+ AssertPtrReturnVoid(m_pEditorSize);
+ {
+ /* Configure editor: */
+ m_pLabelSize->setBuddy(m_pEditorSize);
+ m_pEditorSize->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ connect(m_pEditorSize, &UIMediumSizeEditor::sigSizeChanged,
+ this, &UIMediumDetailsWidget::sltSizeValueChanged);
+
+ /* Add into layout: */
+ pLayoutSize->addWidget(m_pEditorSize, 0, 0, 2, 1);
+ }
+
+ /* Create size error pane: */
+ m_pErrorPaneSize = new QLabel;
+ AssertPtrReturnVoid(m_pErrorPaneSize);
+ {
+ /* Configure label: */
+ m_pErrorPaneSize->setAlignment(Qt::AlignCenter);
+ m_pErrorPaneSize->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ m_pErrorPaneSize->setPixmap(UIIconPool::iconSet(":/status_error_16px.png")
+ .pixmap(QSize(iIconMetric, iIconMetric)));
+
+ /* Add into layout: */
+ pLayoutSize->addWidget(m_pErrorPaneSize, 0, 1, Qt::AlignCenter);
+ }
+
+ /* Add into layout: */
+ pLayoutOptions->addLayout(pLayoutSize, 4, 1, 2, 1);
+ }
+
+ /* Create stretch: */
+ QSpacerItem *pSpacer2 = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
+ AssertPtrReturnVoid(pSpacer2);
+ {
+ /* Add into layout: */
+ pLayoutOptions->addItem(pSpacer2, 6, 0, 1, 2);
+ }
+
+ /* If parent embedded into stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ AssertPtrReturnVoid(m_pButtonBox);
+ {
+ /* Configure button-box: */
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBox, &QIDialogButtonBox::clicked, this, &UIMediumDetailsWidget::sltHandleButtonBoxClick);
+
+ /* Create progress-bar: */
+ m_pProgressBar = new UIEnumerationProgressBar;
+ AssertPtrReturnVoid(m_pProgressBar);
+ {
+ /* Configure progress-bar: */
+ m_pProgressBar->hide();
+ /* Add progress-bar into button-box layout: */
+ m_pButtonBox->addExtraWidget(m_pProgressBar);
+ /* Notify parent it has progress-bar: */
+ m_pParent->setProgressBar(m_pProgressBar);
+ }
+ }
+
+ /* Add into layout: */
+ pLayoutOptions->addWidget(m_pButtonBox, 7, 0, 1, 2);
+ }
+ }
+
+ /* Add to tab-widget: */
+ m_pTabWidget->addTab(pTabOptions, QString());
+ }
+}
+
+void UIMediumDetailsWidget::prepareTabDetails()
+{
+ /* Create 'Details' tab: */
+ QWidget *pTabDetails = new QWidget;
+ AssertPtrReturnVoid(pTabDetails);
+ {
+ /* Create stacked layout: */
+ m_pLayoutDetails = new QStackedLayout(pTabDetails);
+ AssertPtrReturnVoid(m_pLayoutDetails);
+ {
+ /* Create information-containers: */
+ for (int i = (int)UIMediumDeviceType_HardDisk; i < (int)UIMediumDeviceType_All; ++i)
+ {
+ const UIMediumDeviceType enmType = (UIMediumDeviceType)i;
+ prepareInformationContainer(enmType, enmType == UIMediumDeviceType_HardDisk ? 5 : 2); /// @todo Remove hard-coded values.
+ }
+ }
+
+ /* Add to tab-widget: */
+ m_pTabWidget->addTab(pTabDetails, QString());
+ }
+}
+
+void UIMediumDetailsWidget::prepareInformationContainer(UIMediumDeviceType enmType, int cFields)
+{
+ /* Create information-container: */
+ m_aContainers[enmType] = new QWidget;
+ QWidget *pContainer = infoContainer(enmType);
+ AssertPtrReturnVoid(pContainer);
+ {
+ /* Create layout: */
+ new QGridLayout(pContainer);
+ QGridLayout *pLayout = qobject_cast<QGridLayout*>(pContainer->layout());
+ AssertPtrReturnVoid(pLayout);
+ {
+ /* Configure layout: */
+ pLayout->setVerticalSpacing(0);
+ pLayout->setColumnStretch(1, 1);
+
+ /* Create labels & fields: */
+ int i = 0;
+ for (; i < cFields; ++i)
+ {
+ /* Create label: */
+ m_aLabels[enmType] << new QLabel;
+ QLabel *pLabel = infoLabel(enmType, i);
+ AssertPtrReturnVoid(pLabel);
+ {
+ /* Configure label: */
+ pLabel->setMargin(2);
+ pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+
+ /* Add into layout: */
+ pLayout->addWidget(pLabel, i, 0);
+ }
+
+ /* Create field: */
+ m_aFields[enmType] << new QILabel;
+ QILabel *pField = infoField(enmType, i);
+ AssertPtrReturnVoid(pField);
+ {
+ /* Configure field: */
+ pField->setMargin(2);
+ pField->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
+ pField->setFullSizeSelection(true);
+
+ /* Add into layout: */
+ pLayout->addWidget(pField, i, 1);
+ }
+ }
+
+ /* Create stretch: */
+ QSpacerItem *pSpacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
+ AssertPtrReturnVoid(pSpacer);
+ {
+ /* Add into layout: */
+ pLayout->addItem(pSpacer, i, 0, 1, 2);
+ }
+ }
+
+ /* Add into layout: */
+ m_pLayoutDetails->addWidget(pContainer);
+ }
+}
+
+void UIMediumDetailsWidget::loadDataForOptions()
+{
+ /* Clear type combo-box: */
+ m_pLabelType->setEnabled(m_newData.m_fValid);
+ m_pComboBoxType->setEnabled(m_newData.m_fValid);
+ m_pComboBoxType->clear();
+ if (m_newData.m_fValid)
+ {
+ /* Populate type combo-box: */
+ switch (m_newData.m_enmDeviceType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ {
+ /* No type changes for differencing disks: */
+ if (m_oldData.m_enmVariant & KMediumVariant_Diff)
+ m_pComboBoxType->addItem(QString(), m_oldData.m_options.m_enmMediumType);
+ else
+ {
+ m_pComboBoxType->addItem(QString(), QVariant::fromValue(KMediumType_Normal));
+ m_pComboBoxType->addItem(QString(), QVariant::fromValue(KMediumType_Immutable));
+ if (!m_newData.m_fHasChildren)
+ {
+ m_pComboBoxType->addItem(QString(), QVariant::fromValue(KMediumType_Writethrough));
+ m_pComboBoxType->addItem(QString(), QVariant::fromValue(KMediumType_Shareable));
+ }
+ m_pComboBoxType->addItem(QString(), QVariant::fromValue(KMediumType_MultiAttach));
+ }
+ break;
+ }
+ case UIMediumDeviceType_DVD:
+ {
+ m_pComboBoxType->addItem(QString(), QVariant::fromValue(KMediumType_Readonly));
+ break;
+ }
+ case UIMediumDeviceType_Floppy:
+ {
+ m_pComboBoxType->addItem(QString(), QVariant::fromValue(KMediumType_Writethrough));
+ m_pComboBoxType->addItem(QString(), QVariant::fromValue(KMediumType_Readonly));
+ break;
+ }
+ default:
+ break;
+ }
+ /* Translate type combo-box: */
+ for (int i = 0; i < m_pComboBoxType->count(); ++i)
+ {
+ m_pComboBoxType->setItemText(i, gpConverter->toString(m_pComboBoxType->itemData(i).value<KMediumType>()));
+ m_pComboBoxType->setItemData(i, mediumTypeTip(m_pComboBoxType->itemData(i).value<KMediumType>()), Qt::ToolTipRole);
+ }
+ }
+
+ /* Choose the item with required type to be the current one: */
+ for (int i = 0; i < m_pComboBoxType->count(); ++i)
+ if (m_pComboBoxType->itemData(i).value<KMediumType>() == m_newData.m_options.m_enmMediumType)
+ m_pComboBoxType->setCurrentIndex(i);
+ sltTypeIndexChanged(m_pComboBoxType->currentIndex());
+
+ /* Load location: */
+ m_pLabelLocation->setEnabled(m_newData.m_fValid);
+ m_pEditorLocation->setEnabled(m_newData.m_fValid);
+ m_pButtonLocation->setEnabled(m_newData.m_fValid);
+ m_pEditorLocation->setText(m_newData.m_options.m_strLocation);
+
+ /* Load description: */
+ m_pLabelDescription->setEnabled(m_newData.m_fValid);
+ m_pEditorDescription->setEnabled(m_newData.m_fValid);
+ m_pEditorDescription->setPlainText(m_newData.m_options.m_strDescription);
+
+ /* Load size: */
+ const bool fEnableResize = m_newData.m_fValid
+ && m_newData.m_enmDeviceType == UIMediumDeviceType_HardDisk
+ && !(m_newData.m_enmVariant & KMediumVariant_Fixed);
+ m_pLabelSize->setEnabled(fEnableResize);
+ m_pEditorSize->setEnabled(fEnableResize);
+ m_pEditorSize->setMediumSize(m_newData.m_options.m_uLogicalSize);
+ sltSizeValueChanged(m_pEditorSize->mediumSize());
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMediumDetailsWidget::loadDataForDetails()
+{
+ /* Get information-labels just to acquire their number: */
+ const QList<QLabel*> aLabels = m_aLabels.value(m_newData.m_enmDeviceType, QList<QLabel*>());
+ /* Get information-fields just to acquire their number: */
+ const QList<QILabel*> aFields = m_aFields.value(m_newData.m_enmDeviceType, QList<QILabel*>());
+ /* For each the label => update contents: */
+ for (int i = 0; i < aLabels.size(); ++i)
+ infoLabel(m_newData.m_enmDeviceType, i)->setText(m_newData.m_details.m_aLabels.value(i, QString()));
+ /* For each the field => update contents: */
+ for (int i = 0; i < aFields.size(); ++i)
+ {
+ infoField(m_newData.m_enmDeviceType, i)->setText(m_newData.m_details.m_aFields.value(i, QString()));
+ infoField(m_newData.m_enmDeviceType, i)->setEnabled(!infoField(m_newData.m_enmDeviceType, i)->text().trimmed().isEmpty());
+ }
+}
+
+void UIMediumDetailsWidget::revalidate(QWidget *pWidget /* = 0 */)
+{
+ /* Reset the result: */
+ m_fValid = true;
+
+ /* Validate 'Options' tab content: */
+ if (!pWidget || pWidget == m_pErrorPaneType)
+ {
+ /* Always valid for now: */
+ const bool fError = false;
+ m_pErrorPaneType->setVisible(fError);
+ if (fError)
+ m_fValid = false;
+ }
+ if (!pWidget || pWidget == m_pErrorPaneLocation)
+ {
+ /* If medium is valid itself, details are valid only is location is set: */
+ const bool fError = m_newData.m_fValid && m_newData.m_options.m_strLocation.isEmpty();
+ m_pErrorPaneLocation->setVisible(fError);
+ if (fError)
+ m_fValid = false;
+ }
+ if (!pWidget || pWidget == m_pErrorPaneDescription)
+ {
+ /* Always valid for now: */
+ const bool fError = false;
+ m_pErrorPaneDescription->setVisible(fError);
+ if (fError)
+ m_fValid = false;
+ }
+ if (!pWidget || pWidget == m_pErrorPaneSize)
+ {
+ /* Always valid for now: */
+ const bool fError = m_newData.m_options.m_uLogicalSize < m_oldData.m_options.m_uLogicalSize;
+ m_pErrorPaneSize->setVisible(fError);
+ if (fError)
+ m_fValid = false;
+ }
+
+ /* Retranslate validation: */
+ retranslateValidation(pWidget);
+}
+
+void UIMediumDetailsWidget::retranslateValidation(QWidget *pWidget /* = 0 */)
+{
+ /* Translate 'Interface' tab content: */
+// if (!pWidget || pWidget == m_pErrorPaneType)
+// m_pErrorPaneType->setToolTip(UIMediumManager::tr("Cannot change from type <b>%1</b> to <b>%2</b>.")
+// .arg(m_oldData.m_options.m_enmType).arg(m_newData.m_options.m_enmType));
+ if (!pWidget || pWidget == m_pErrorPaneLocation)
+ m_pErrorPaneLocation->setToolTip(UIMediumManager::tr("Location cannot be empty."));
+// if (!pWidget || pWidget == m_pErrorPaneDescription)
+// m_pErrorPaneDescription->setToolTip(UIMediumManager::tr("Cannot change medium description from <b>%1</b> to <b>%2</b>.")
+// .arg(m_oldData.m_options.m_strDescription)
+// .arg(m_newData.m_options.m_strDescription));
+ if (!pWidget || pWidget == m_pErrorPaneSize)
+ m_pErrorPaneSize->setToolTip(UIMediumManager::tr("Cannot change medium size from <b>%1</b> to <b>%2</b> as storage "
+ "shrinking is currently not implemented.")
+ .arg(UITranslator::formatSize(m_oldData.m_options.m_uLogicalSize))
+ .arg(UITranslator::formatSize(m_newData.m_options.m_uLogicalSize)));
+}
+
+void UIMediumDetailsWidget::updateButtonStates()
+{
+// if (m_newData != m_oldData)
+// {
+// if (m_newData.m_options != m_oldData.m_options)
+// {
+// if (m_newData.m_options.m_enmType != m_oldData.m_options.m_enmType)
+// printf("Type: %d\n", (int)m_newData.m_options.m_enmType);
+// if (m_newData.m_options.m_uLogicalSize != m_oldData.m_options.m_uLogicalSize)
+// printf("Size: %llu vs %llu\n", m_newData.m_options.m_uLogicalSize, m_oldData.m_options.m_uLogicalSize);
+// if (m_newData.m_options.m_strLocation != m_oldData.m_options.m_strLocation)
+// printf("Location: %s\n", m_newData.m_options.m_strLocation.toUtf8().constData());
+// if (m_newData.m_options.m_strDescription != m_oldData.m_options.m_strDescription)
+// printf("Description: %s\n", m_newData.m_options.m_strDescription.toUtf8().constData());
+// }
+// }
+
+ /* Update 'Apply' / 'Reset' button states: */
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled((m_oldData != m_newData) && m_fValid);
+ }
+
+ /* Notify listeners as well: */
+ emit sigRejectAllowed(m_oldData != m_newData);
+ emit sigAcceptAllowed((m_oldData != m_newData) && m_fValid);
+}
+
+/* static */
+QString UIMediumDetailsWidget::mediumTypeTip(KMediumType enmType)
+{
+ switch (enmType)
+ {
+ case KMediumType_Normal:
+ return UIMediumManager::tr("This type of medium is attached directly or indirectly, preserved when taking "
+ "snapshots.");
+ case KMediumType_Immutable:
+ return UIMediumManager::tr("This type of medium is attached indirectly, changes are wiped out the next time the "
+ "virtual machine is started.");
+ case KMediumType_Writethrough:
+ return UIMediumManager::tr("This type of medium is attached directly, ignored when taking snapshots.");
+ case KMediumType_Shareable:
+ return UIMediumManager::tr("This type of medium is attached directly, allowed to be used concurrently by several "
+ "machines.");
+ case KMediumType_Readonly:
+ return UIMediumManager::tr("This type of medium is attached directly, and can be used by several machines.");
+ case KMediumType_MultiAttach:
+ return UIMediumManager::tr("This type of medium is attached indirectly, so that one base medium can be used for "
+ "several VMs which have their own differencing medium to store their modifications.");
+ default:
+ break;
+ }
+ AssertFailedReturn(QString());
+}
+
+QWidget *UIMediumDetailsWidget::infoContainer(UIMediumDeviceType enmType) const
+{
+ /* Return information-container for known medium type: */
+ return m_aContainers.value(enmType, 0);
+}
+
+QLabel *UIMediumDetailsWidget::infoLabel(UIMediumDeviceType enmType, int iIndex) const
+{
+ /* Acquire list of labels: */
+ const QList<QLabel*> aLabels = m_aLabels.value(enmType, QList<QLabel*>());
+
+ /* Return label for known index: */
+ return aLabels.value(iIndex, 0);
+}
+
+QILabel *UIMediumDetailsWidget::infoField(UIMediumDeviceType enmType, int iIndex) const
+{
+ /* Acquire list of fields: */
+ const QList<QILabel*> aFields = m_aFields.value(enmType, QList<QILabel*>());
+
+ /* Return label for known index: */
+ return aFields.value(iIndex, 0);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDetailsWidget.h b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDetailsWidget.h
new file mode 100644
index 00000000..26c00392
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumDetailsWidget.h
@@ -0,0 +1,368 @@
+/* $Id: UIMediumDetailsWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIMediumDetailsWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_UIMediumDetailsWidget_h
+#define FEQT_INCLUDED_SRC_medium_UIMediumDetailsWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIMediumDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QComboBox;
+class QLabel;
+class QLineEdit;
+class QStackedLayout;
+class QTextEdit;
+class QWidget;
+class QILabel;
+class QITabWidget;
+class QIToolButton;
+class UIEnumerationProgressBar;
+class UIMediumManagerWidget;
+class UIMediumSizeEditor;
+
+
+/** Virtual Media Manager: Medium options data structure. */
+struct UIDataMediumOptions
+{
+ /** Constructs data. */
+ UIDataMediumOptions()
+ : m_enmMediumType(KMediumType_Normal)
+ , m_strLocation(QString())
+ , m_strDescription(QString())
+ , m_uLogicalSize(0)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataMediumOptions &other) const
+ {
+ return true
+ && (m_enmMediumType == other.m_enmMediumType)
+ && (m_strLocation == other.m_strLocation)
+ && (m_strDescription == other.m_strDescription)
+ && (m_uLogicalSize == other.m_uLogicalSize)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataMediumOptions &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataMediumOptions &other) const { return !equal(other); }
+
+ /** Holds the medium type. */
+ KMediumType m_enmMediumType;
+ /** Holds the location. */
+ QString m_strLocation;
+ /** Holds the description. */
+ QString m_strDescription;
+ /** Holds the logical size. */
+ qulonglong m_uLogicalSize;
+};
+
+
+/** Virtual Media Manager: Medium details data structure. */
+struct UIDataMediumDetails
+{
+ /** Constructs data. */
+ UIDataMediumDetails()
+ : m_aLabels(QStringList())
+ , m_aFields(QStringList())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataMediumDetails &other) const
+ {
+ return true
+ && (m_aLabels == other.m_aLabels)
+ && (m_aFields == other.m_aFields)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataMediumDetails &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataMediumDetails &other) const { return !equal(other); }
+
+ /** Holds the labels list. */
+ QStringList m_aLabels;
+ /** Holds the fields list. */
+ QStringList m_aFields;
+};
+
+
+/** Virtual Media Manager: Medium data structure. */
+struct UIDataMedium
+{
+ /** Constructs data. */
+ UIDataMedium()
+ : m_fValid(false)
+ , m_enmDeviceType(UIMediumDeviceType_Invalid)
+ , m_enmVariant(KMediumVariant_Max)
+ , m_fHasChildren(false)
+ , m_options(UIDataMediumOptions())
+ , m_details(UIDataMediumDetails())
+ {}
+
+ /** Constructs data with passed @enmType. */
+ UIDataMedium(UIMediumDeviceType enmType)
+ : m_fValid(false)
+ , m_enmDeviceType(enmType)
+ , m_enmVariant(KMediumVariant_Max)
+ , m_fHasChildren(false)
+ , m_options(UIDataMediumOptions())
+ , m_details(UIDataMediumDetails())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataMedium &other) const
+ {
+ return true
+ && (m_fValid == other.m_fValid)
+ && (m_enmDeviceType == other.m_enmDeviceType)
+ && (m_enmVariant == other.m_enmVariant)
+ && (m_fHasChildren == other.m_fHasChildren)
+ && (m_options == other.m_options)
+ && (m_details == other.m_details)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataMedium &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataMedium &other) const { return !equal(other); }
+
+ /** Holds whether data is valid. */
+ bool m_fValid;
+ /** Holds the medium type. */
+ UIMediumDeviceType m_enmDeviceType;
+ /** Holds the medium variant. */
+ KMediumVariant m_enmVariant;
+ /** Holds whether medium has children. */
+ bool m_fHasChildren;
+
+ /** Holds the medium options. */
+ UIDataMediumOptions m_options;
+ /** Holds the details data. */
+ UIDataMediumDetails m_details;
+};
+
+
+/** Virtual Media Manager: Virtual Media Manager details-widget. */
+class UIMediumDetailsWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about accept is allowed. */
+ void sigAcceptAllowed(bool fAllowed);
+ /** Notifies listeners about reject is allowed. */
+ void sigRejectAllowed(bool fAllowed);
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+public:
+
+ /** Constructs medium details dialog passing @a pParent to the base-class.
+ * @param enmEmbedding Brings embedding type. */
+ UIMediumDetailsWidget(UIMediumManagerWidget *pParent, EmbedTo enmEmbedding);
+
+ /** Defines the raised details @a enmType. */
+ void setCurrentType(UIMediumDeviceType enmType);
+
+ /** Returns the medium data. */
+ const UIDataMedium &data() const { return m_newData; }
+ /** Defines the @a data for passed @a enmType. */
+ void setData(const UIDataMedium &data);
+ /** Enables/disables some of the medium editing widgets of the details tab. */
+ void enableDisableMediumModificationWidgets(bool fMediumIsModifiable);
+
+public slots:
+
+ /** Defines whether the options tab is @a fEnabled. */
+ void setOptionsEnabled(bool fEnabled);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** @name Options stuff.
+ * @{ */
+ /** Handles type change. */
+ void sltTypeIndexChanged(int iIndex);
+ /** Handles location change. */
+ void sltLocationPathChanged(const QString &strPath);
+ /** Handles request to choose location. */
+ void sltChooseLocationPath();
+ /** Handles description text change. */
+ void sltDescriptionTextChanged();
+ /** Handles size editor change. */
+ void sltSizeValueChanged(qulonglong uSize);
+
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares this. */
+ void prepareThis();
+ /** Prepares tab-widget. */
+ void prepareTabWidget();
+ /** Prepares 'Options' tab. */
+ void prepareTabOptions();
+ /** Prepares 'Details' tab. */
+ void prepareTabDetails();
+ /** Prepares information-container. */
+ void prepareInformationContainer(UIMediumDeviceType enmType, int cFields);
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+ /** Load options data. */
+ void loadDataForOptions();
+ /** Load details data. */
+ void loadDataForDetails();
+ /** @} */
+
+ /** @name Options stuff.
+ * @{ */
+ /** Revalidates changes for passed @a pWidget. */
+ void revalidate(QWidget *pWidget = 0);
+ /** Retranslates validation for passed @a pWidget. */
+ void retranslateValidation(QWidget *pWidget = 0);
+ /** Updates button states. */
+ void updateButtonStates();
+
+ /** Returns tool-tip for passed medium @a enmType. */
+ static QString mediumTypeTip(KMediumType enmType);
+ /** @} */
+
+ /** @name Details stuff.
+ * @{ */
+ /** Returns information-container for passed medium @a enmType. */
+ QWidget *infoContainer(UIMediumDeviceType enmType) const;
+ /** Returns information-label for passed medium @a enmType and @a iIndex. */
+ QLabel *infoLabel(UIMediumDeviceType enmType, int iIndex) const;
+ /** Returns information-field for passed medium @a enmType and @a iIndex. */
+ QILabel *infoField(UIMediumDeviceType enmType, int iIndex) const;
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the parent reference. */
+ UIMediumManagerWidget *m_pParent;
+ /** Holds the parent widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+
+ /** Holds the old data copy. */
+ UIDataMedium m_oldData;
+ /** Holds the new data copy. */
+ UIDataMedium m_newData;
+
+ /** Holds the tab-widget. */
+ QITabWidget *m_pTabWidget;
+ /** @} */
+
+ /** @name Options variables.
+ * @{ */
+ /** Holds the type label. */
+ QLabel *m_pLabelType;
+ /** Holds the type combo-box. */
+ QComboBox *m_pComboBoxType;
+ /** Holds the type error pane. */
+ QLabel *m_pErrorPaneType;
+
+ /** Holds the location label. */
+ QLabel *m_pLabelLocation;
+ /** Holds the location editor. */
+ QLineEdit *m_pEditorLocation;
+ /** Holds the location error pane. */
+ QLabel *m_pErrorPaneLocation;
+ /** Holds the location button. */
+ QIToolButton *m_pButtonLocation;
+
+ /** Holds the description label. */
+ QLabel *m_pLabelDescription;
+ /** Holds the description editor. */
+ QTextEdit *m_pEditorDescription;
+ /** Holds the description error pane. */
+ QLabel *m_pErrorPaneDescription;
+
+ /** Holds the size label. */
+ QLabel *m_pLabelSize;
+ /** Holds the size editor. */
+ UIMediumSizeEditor *m_pEditorSize;
+ /** Holds the size error pane. */
+ QLabel *m_pErrorPaneSize;
+
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+ /** Holds the progress-bar widget instance. */
+ UIEnumerationProgressBar *m_pProgressBar;
+
+ /** Holds whether options are valid. */
+ bool m_fValid;
+ /** @} */
+
+ /** @name Details variables.
+ * @{ */
+ /** Holds the details layout: */
+ QStackedLayout *m_pLayoutDetails;
+
+ /** Holds the map of information-container instances. */
+ QMap<UIMediumDeviceType, QWidget*> m_aContainers;
+ /** Holds the map of information-container label instances. */
+ QMap<UIMediumDeviceType, QList<QLabel*> > m_aLabels;
+ /** Holds the information-container field instances. */
+ QMap<UIMediumDeviceType, QList<QILabel*> > m_aFields;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_UIMediumDetailsWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumEnumerator.cpp b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumEnumerator.cpp
new file mode 100644
index 00000000..73968baa
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumEnumerator.cpp
@@ -0,0 +1,662 @@
+/* $Id: UIMediumEnumerator.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMediumEnumerator class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QSet>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIErrorString.h"
+#include "UIMediumEnumerator.h"
+#include "UINotificationCenter.h"
+#include "UITask.h"
+#include "UIThreadPool.h"
+#include "UIVirtualBoxEventHandler.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+#include "CMediumAttachment.h"
+#include "CSnapshot.h"
+
+
+/** Template function to convert a list of
+ * abstract objects to a human readable string list.
+ * @note T should have .toString() member implemented. */
+template<class T> static QStringList toStringList(const QList<T> &list)
+{
+ QStringList l;
+ foreach(const T &t, list)
+ l << t.toString();
+ return l;
+}
+
+
+/** UITask extension used for medium-enumeration purposes.
+ * @note We made setting/getting medium a thread-safe stuff. But this wasn't
+ * dangerous for us before since setter/getter calls are splitted in time
+ * by enumeration logic. Previously we were even using
+ * property/setProperty API for that but latest Qt versions prohibits
+ * property/setProperty API usage from other than the GUI thread so we
+ * had to rework that stuff to be thread-safe for Qt >= 5.11. */
+class UITaskMediumEnumeration : public UITask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs @a guiMedium enumeration task. */
+ UITaskMediumEnumeration(const UIMedium &guiMedium)
+ : UITask(UITask::Type_MediumEnumeration)
+ , m_guiMedium(guiMedium)
+ {}
+
+ /** Returns GUI medium. */
+ UIMedium medium() const
+ {
+ /* Acquire under a proper lock: */
+ m_mutex.lock();
+ const UIMedium guiMedium = m_guiMedium;
+ m_mutex.unlock();
+ return guiMedium;
+ }
+
+private:
+
+ /** Contains medium-enumeration task body. */
+ virtual void run() RT_OVERRIDE
+ {
+ /* Enumerate under a proper lock: */
+ m_mutex.lock();
+ m_guiMedium.blockAndQueryState();
+ m_mutex.unlock();
+ }
+
+ /** Holds the mutex to access m_guiMedium member. */
+ mutable QMutex m_mutex;
+ /** Holds the medium being enumerated. */
+ UIMedium m_guiMedium;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMediumEnumerator implementation. *
+*********************************************************************************************************************************/
+
+UIMediumEnumerator::UIMediumEnumerator()
+ : m_fFullMediumEnumerationRequested(false)
+ , m_fMediumEnumerationInProgress(false)
+{
+ /* Allow UIMedium to be used in inter-thread signals: */
+ qRegisterMetaType<UIMedium>();
+
+ /* Prepare Main event handlers: */
+ /* Machine related events: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange,
+ this, &UIMediumEnumerator::sltHandleMachineDataChange);
+ /* Medium related events: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigStorageControllerChange,
+ this, &UIMediumEnumerator::sltHandleStorageControllerChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigStorageDeviceChange,
+ this, &UIMediumEnumerator::sltHandleStorageDeviceChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMediumChange,
+ this, &UIMediumEnumerator::sltHandleMediumChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMediumConfigChange,
+ this, &UIMediumEnumerator::sltHandleMediumConfigChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMediumRegistered,
+ this, &UIMediumEnumerator::sltHandleMediumRegistered);
+
+ /* Prepare global thread-pool listener: */
+ connect(uiCommon().threadPool(), &UIThreadPool::sigTaskComplete,
+ this, &UIMediumEnumerator::sltHandleMediumEnumerationTaskComplete);
+
+ /* We should make sure media map contains at least NULL medium object: */
+ addNullMediumToMap(m_media);
+ /* Notify listener about initial enumeration started/finished instantly: */
+ LogRel(("GUI: UIMediumEnumerator: Initial medium-enumeration finished!\n"));
+ emit sigMediumEnumerationStarted();
+ emit sigMediumEnumerationFinished();
+}
+
+QList<QUuid> UIMediumEnumerator::mediumIDs() const
+{
+ /* Return keys of current media map: */
+ return m_media.keys();
+}
+
+UIMedium UIMediumEnumerator::medium(const QUuid &uMediumID) const
+{
+ /* Search through current media map
+ * for the UIMedium with passed ID: */
+ if (m_media.contains(uMediumID))
+ return m_media.value(uMediumID);
+ /* Return NULL UIMedium otherwise: */
+ return UIMedium();
+}
+
+void UIMediumEnumerator::createMedium(const UIMedium &guiMedium)
+{
+ /* Get UIMedium ID: */
+ const QUuid uMediumID = guiMedium.id();
+
+ /* Do not create UIMedium(s) with incorrect ID: */
+ AssertReturnVoid(!uMediumID.isNull());
+ /* Make sure UIMedium doesn't exist already: */
+ if (m_media.contains(uMediumID))
+ return;
+
+ /* Insert UIMedium: */
+ m_media[uMediumID] = guiMedium;
+ LogRel(("GUI: UIMediumEnumerator: Medium with key={%s} created\n", uMediumID.toString().toUtf8().constData()));
+
+ /* Notify listener: */
+ emit sigMediumCreated(uMediumID);
+}
+
+void UIMediumEnumerator::enumerateMedia(const CMediumVector &comMedia /* = CMediumVector() */)
+{
+ /* Compose new map of currently cached media & their children.
+ * While composing we are using data from already cached media. */
+ UIMediumMap guiMedia;
+ addNullMediumToMap(guiMedia);
+ if (comMedia.isEmpty())
+ {
+ /* Compose new map of all known media & their children: */
+ addMediaToMap(uiCommon().virtualBox().GetHardDisks(), guiMedia);
+ addMediaToMap(uiCommon().host().GetDVDDrives(), guiMedia);
+ addMediaToMap(uiCommon().virtualBox().GetDVDImages(), guiMedia);
+ addMediaToMap(uiCommon().host().GetFloppyDrives(), guiMedia);
+ addMediaToMap(uiCommon().virtualBox().GetFloppyImages(), guiMedia);
+ }
+ else
+ {
+ /* Compose new map of passed media & their children: */
+ addMediaToMap(comMedia, guiMedia);
+ }
+
+ /* UICommon is cleaning up, abort immediately: */
+ if (uiCommon().isCleaningUp())
+ return;
+
+ if (comMedia.isEmpty())
+ {
+ /* Replace existing media map since
+ * we have full medium enumeration: */
+ m_fFullMediumEnumerationRequested = true;
+ m_media = guiMedia;
+ }
+ else
+ {
+ /* Throw the media to existing map: */
+ foreach (const QUuid &uMediumId, guiMedia.keys())
+ m_media[uMediumId] = guiMedia.value(uMediumId);
+ }
+
+ /* If enumeration hasn't yet started: */
+ if (!m_fMediumEnumerationInProgress)
+ {
+ /* Notify listener about enumeration started: */
+ LogRel(("GUI: UIMediumEnumerator: Medium-enumeration started...\n"));
+ m_fMediumEnumerationInProgress = true;
+ emit sigMediumEnumerationStarted();
+
+ /* Make sure we really have more than one UIMedium (which is NULL): */
+ if ( guiMedia.size() == 1
+ && guiMedia.first().id() == UIMedium::nullID())
+ {
+ /* Notify listener about enumeration finished instantly: */
+ LogRel(("GUI: UIMediumEnumerator: Medium-enumeration finished!\n"));
+ m_fMediumEnumerationInProgress = false;
+ emit sigMediumEnumerationFinished();
+ }
+ }
+
+ /* Start enumeration for media with non-NULL ID: */
+ foreach (const QUuid &uMediumID, guiMedia.keys())
+ if (!uMediumID.isNull())
+ createMediumEnumerationTask(guiMedia[uMediumID]);
+}
+
+void UIMediumEnumerator::refreshMedia()
+{
+ /* Make sure we are not already in progress: */
+ AssertReturnVoid(!m_fMediumEnumerationInProgress);
+
+ /* Refresh all cached media we have: */
+ foreach (const QUuid &uMediumID, m_media.keys())
+ m_media[uMediumID].refresh();
+}
+
+void UIMediumEnumerator::retranslateUi()
+{
+ /* Translating NULL UIMedium by recreating it: */
+ if (m_media.contains(UIMedium::nullID()))
+ m_media[UIMedium::nullID()] = UIMedium();
+}
+
+void UIMediumEnumerator::sltHandleMachineDataChange(const QUuid &uMachineId)
+{
+ //printf("MachineDataChange: machine-id=%s\n",
+ // uMachineId.toString().toUtf8().constData());
+ LogRel2(("GUI: UIMediumEnumerator: MachineDataChange event received, Machine ID = {%s}\n",
+ uMachineId.toString().toUtf8().constData()));
+
+ /* Enumerate all the media of machine with this ID: */
+ QList<QUuid> result;
+ enumerateAllMediaOfMachineWithId(uMachineId, result);
+}
+
+void UIMediumEnumerator::sltHandleStorageControllerChange(const QUuid &uMachineId, const QString &strControllerName)
+{
+ //printf("StorageControllerChanged: machine-id=%s, controller-name=%s\n",
+ // uMachineId.toString().toUtf8().constData(), strControllerName.toUtf8().constData());
+ LogRel2(("GUI: UIMediumEnumerator: StorageControllerChanged event received, Medium ID = {%s}, Controller Name = {%s}\n",
+ uMachineId.toString().toUtf8().constData(), strControllerName.toUtf8().constData()));
+}
+
+void UIMediumEnumerator::sltHandleStorageDeviceChange(CMediumAttachment comAttachment, bool fRemoved, bool fSilent)
+{
+ //printf("StorageDeviceChanged: removed=%d, silent=%d\n",
+ // fRemoved, fSilent);
+ LogRel2(("GUI: UIMediumEnumerator: StorageDeviceChanged event received, Removed = {%d}, Silent = {%d}\n",
+ fRemoved, fSilent));
+
+ /* Parse attachment: */
+ QList<QUuid> result;
+ parseAttachment(comAttachment, result);
+}
+
+void UIMediumEnumerator::sltHandleMediumChange(CMediumAttachment comAttachment)
+{
+ //printf("MediumChanged\n");
+ LogRel2(("GUI: UIMediumEnumerator: MediumChanged event received\n"));
+
+ /* Parse attachment: */
+ QList<QUuid> result;
+ parseAttachment(comAttachment, result);
+}
+
+void UIMediumEnumerator::sltHandleMediumConfigChange(CMedium comMedium)
+{
+ //printf("MediumConfigChanged\n");
+ LogRel2(("GUI: UIMediumEnumerator: MediumConfigChanged event received\n"));
+
+ /* Parse medium: */
+ QList<QUuid> result;
+ parseMedium(comMedium, result);
+}
+
+void UIMediumEnumerator::sltHandleMediumRegistered(const QUuid &uMediumId, KDeviceType enmMediumType, bool fRegistered)
+{
+ //printf("MediumRegistered: medium-id=%s, medium-type=%d, registered=%d\n",
+ // uMediumId.toString().toUtf8().constData(), enmMediumType, fRegistered);
+ //printf(" Medium to recache: %s\n",
+ // uMediumId.toString().toUtf8().constData());
+ LogRel2(("GUI: UIMediumEnumerator: MediumRegistered event received, Medium ID = {%s}, Medium type = {%d}, Registered = {%d}\n",
+ uMediumId.toString().toUtf8().constData(), enmMediumType, fRegistered));
+
+ /* New medium registered: */
+ if (fRegistered)
+ {
+ /* Make sure this medium isn't already cached: */
+ if (!medium(uMediumId).isNull())
+ {
+ /* This medium can be known because of async event nature. Currently medium registration event comes
+ * very late and other even unrelated events can come before it and request for this particular medium
+ * enumeration, so we just ignore repetitive events but enumerate this UIMedium at least once if it
+ * wasn't registered before. */
+ if (!m_registeredMediaIds.contains(uMediumId))
+ {
+ LogRel2(("GUI: UIMediumEnumerator: Medium {%s} is cached but not registered already, so will be enumerated..\n",
+ uMediumId.toString().toUtf8().constData()));
+ createMediumEnumerationTask(m_media.value(uMediumId));
+
+ /* Mark medium registered: */
+ m_registeredMediaIds << uMediumId;
+ }
+ }
+ else
+ {
+ /* Get VBox for temporary usage, it will cache the error info: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ /* Open existing medium, this API can be used to open known medium as well, using ID as location for that: */
+ CMedium comMedium = comVBox.OpenMedium(uMediumId.toString(), enmMediumType, KAccessMode_ReadWrite, false);
+ if (!comVBox.isOk())
+ LogRel(("GUI: UIMediumEnumerator: Unable to open registered medium! %s\n",
+ UIErrorString::simplifiedErrorInfo(comVBox).toUtf8().constData()));
+ else
+ {
+ /* Create new UIMedium: */
+ const UIMedium guiMedium(comMedium, UIMediumDefs::mediumTypeToLocal(comMedium.GetDeviceType()));
+ const QUuid &uUIMediumKey = guiMedium.key();
+
+ /* Cache corresponding UIMedium: */
+ m_media.insert(uUIMediumKey, guiMedium);
+ LogRel2(("GUI: UIMediumEnumerator: Medium {%s} is now cached and will be enumerated..\n",
+ uUIMediumKey.toString().toUtf8().constData()));
+
+ /* And notify listeners: */
+ emit sigMediumCreated(uUIMediumKey);
+
+ /* Enumerate corresponding UIMedium: */
+ createMediumEnumerationTask(m_media.value(uMediumId));
+
+ /* Mark medium registered: */
+ m_registeredMediaIds << uMediumId;
+ }
+ }
+ }
+ /* Old medium unregistered: */
+ else
+ {
+ /* Make sure this medium is still cached: */
+ if (medium(uMediumId).isNull())
+ {
+ /* This medium can be wiped out already because of async event nature. Currently
+ * medium unregistration event comes very late and other even unrealted events
+ * can come before it and request for this particular medium enumeration. If medium
+ * enumeration is performed fast enough (before medium unregistration event comes),
+ * medium will be wiped out already, so we just ignore it. */
+ LogRel2(("GUI: UIMediumEnumerator: Medium {%s} was not currently cached!\n",
+ uMediumId.toString().toUtf8().constData()));
+ }
+ else
+ {
+ /* Forget corresponding UIMedium: */
+ m_media.remove(uMediumId);
+ LogRel2(("GUI: UIMediumEnumerator: Medium {%s} is no more cached!\n",
+ uMediumId.toString().toUtf8().constData()));
+
+ /* And notify listeners: */
+ emit sigMediumDeleted(uMediumId);
+
+ /* Besides that we should enumerate all the
+ * 1st level children of deleted medium: */
+ QList<QUuid> result;
+ enumerateAllMediaOfMediumWithId(uMediumId, result);
+ }
+
+ /* Mark medium unregistered: */
+ m_registeredMediaIds.remove(uMediumId);
+ }
+}
+
+void UIMediumEnumerator::sltHandleMediumEnumerationTaskComplete(UITask *pTask)
+{
+ /* Make sure that is one of our tasks: */
+ if (pTask->type() != UITask::Type_MediumEnumeration)
+ return;
+ AssertReturnVoid(m_tasks.contains(pTask));
+
+ /* Get enumerated UIMedium: */
+ const UIMedium guiMedium = qobject_cast<UITaskMediumEnumeration*>(pTask)->medium();
+ const QUuid uMediumKey = guiMedium.key();
+ LogRel2(("GUI: UIMediumEnumerator: Medium with key={%s} enumerated\n",
+ uMediumKey.toString().toUtf8().constData()));
+
+ /* Remove task from internal set: */
+ m_tasks.remove(pTask);
+
+ /* Make sure such UIMedium still exists: */
+ if (!m_media.contains(uMediumKey))
+ {
+ LogRel2(("GUI: UIMediumEnumerator: Medium with key={%s} already deleted by a third party\n",
+ uMediumKey.toString().toUtf8().constData()));
+ return;
+ }
+
+ /* Check if UIMedium ID was changed: */
+ const QUuid uMediumID = guiMedium.id();
+ /* UIMedium ID was changed to nullID: */
+ if (uMediumID == UIMedium::nullID())
+ {
+ /* Delete this UIMedium: */
+ m_media.remove(uMediumKey);
+ LogRel2(("GUI: UIMediumEnumerator: Medium with key={%s} closed and deleted (after enumeration)\n",
+ uMediumKey.toString().toUtf8().constData()));
+
+ /* And notify listener about delete: */
+ emit sigMediumDeleted(uMediumKey);
+ }
+ /* UIMedium ID was changed to something proper: */
+ else if (uMediumID != uMediumKey)
+ {
+ /* We have to reinject enumerated UIMedium: */
+ m_media.remove(uMediumKey);
+ m_media[uMediumID] = guiMedium;
+ m_media[uMediumID].setKey(uMediumID);
+ LogRel2(("GUI: UIMediumEnumerator: Medium with key={%s} has it changed to {%s}\n",
+ uMediumKey.toString().toUtf8().constData(),
+ uMediumID.toString().toUtf8().constData()));
+
+ /* And notify listener about delete/create: */
+ emit sigMediumDeleted(uMediumKey);
+ emit sigMediumCreated(uMediumID);
+ }
+ /* UIMedium ID was not changed: */
+ else
+ {
+ /* Just update enumerated UIMedium: */
+ m_media[uMediumID] = guiMedium;
+ LogRel2(("GUI: UIMediumEnumerator: Medium with key={%s} updated\n",
+ uMediumID.toString().toUtf8().constData()));
+
+ /* And notify listener about update: */
+ emit sigMediumEnumerated(uMediumID);
+ }
+
+ /* If there are no more tasks we know about: */
+ if (m_tasks.isEmpty())
+ {
+ /* Notify listener: */
+ LogRel(("GUI: UIMediumEnumerator: Medium-enumeration finished!\n"));
+ m_fMediumEnumerationInProgress = false;
+ emit sigMediumEnumerationFinished();
+ }
+}
+
+void UIMediumEnumerator::createMediumEnumerationTask(const UIMedium &guiMedium)
+{
+ /* Prepare medium-enumeration task: */
+ UITask *pTask = new UITaskMediumEnumeration(guiMedium);
+ /* Append to internal set: */
+ m_tasks << pTask;
+ /* Post into global thread-pool: */
+ uiCommon().threadPool()->enqueueTask(pTask);
+}
+
+void UIMediumEnumerator::addNullMediumToMap(UIMediumMap &media)
+{
+ /* Insert NULL UIMedium to the passed media map.
+ * Get existing one from the previous map if any. */
+ const UIMedium guiMedium = m_media.contains(UIMedium::nullID())
+ ? m_media[UIMedium::nullID()]
+ : UIMedium();
+ media.insert(UIMedium::nullID(), guiMedium);
+}
+
+void UIMediumEnumerator::addMediaToMap(const CMediumVector &inputMedia, UIMediumMap &outputMedia)
+{
+ /* Iterate through passed inputMedia vector: */
+ foreach (const CMedium &comMedium, inputMedia)
+ {
+ /* If UICommon is cleaning up, abort immediately: */
+ if (uiCommon().isCleaningUp())
+ break;
+
+ /* Insert UIMedium to the passed media map.
+ * Get existing one from the previous map if any.
+ * Create on the basis of comMedium otherwise. */
+ const QUuid uMediumID = comMedium.GetId();
+ const UIMedium guiMedium = m_media.contains(uMediumID)
+ ? m_media.value(uMediumID)
+ : UIMedium(comMedium, UIMediumDefs::mediumTypeToLocal(comMedium.GetDeviceType()));
+ outputMedia.insert(guiMedium.id(), guiMedium);
+
+ /* Insert comMedium children into map as well: */
+ addMediaToMap(comMedium.GetChildren(), outputMedia);
+ }
+}
+
+void UIMediumEnumerator::parseAttachment(CMediumAttachment comAttachment, QList<QUuid> &result)
+{
+ /* Make sure attachment is valid: */
+ if (comAttachment.isNull())
+ {
+ LogRel2(("GUI: UIMediumEnumerator: Attachment is NULL!\n"));
+ /// @todo is this possible case?
+ AssertFailed();
+ }
+ else
+ {
+ /* Acquire attachment medium: */
+ CMedium comMedium = comAttachment.GetMedium();
+ if (!comAttachment.isOk())
+ LogRel(("GUI: UIMediumEnumerator: Unable to acquire attachment medium! %s\n",
+ UIErrorString::simplifiedErrorInfo(comAttachment).toUtf8().constData()));
+ else
+ {
+ /* Parse medium: */
+ parseMedium(comMedium, result);
+
+ // WORKAROUND:
+ // In current architecture there is no way to determine medium previously mounted
+ // to this attachment, so we will have to enumerate all other cached media which
+ // belongs to the same VM, since they may no longer belong to it.
+
+ /* Acquire parent VM: */
+ CMachine comMachine = comAttachment.GetMachine();
+ if (!comAttachment.isOk())
+ LogRel(("GUI: UIMediumEnumerator: Unable to acquire attachment parent machine! %s\n",
+ UIErrorString::simplifiedErrorInfo(comAttachment).toUtf8().constData()));
+ else
+ {
+ /* Acquire machine ID: */
+ const QUuid uMachineId = comMachine.GetId();
+ if (!comMachine.isOk())
+ LogRel(("GUI: UIMediumEnumerator: Unable to acquire machine ID! %s\n",
+ UIErrorString::simplifiedErrorInfo(comMachine).toUtf8().constData()));
+ else
+ {
+ /* Enumerate all the media of machine with this ID: */
+ enumerateAllMediaOfMachineWithId(uMachineId, result);
+ }
+ }
+ }
+ }
+}
+
+void UIMediumEnumerator::parseMedium(CMedium comMedium, QList<QUuid> &result)
+{
+ /* Make sure medium is valid: */
+ if (comMedium.isNull())
+ {
+ /* This medium is NULL by some reason, the obvious case when this
+ * can happen is when optical/floppy device is created empty. */
+ LogRel2(("GUI: UIMediumEnumerator: Medium is NULL!\n"));
+ }
+ else
+ {
+ /* Acquire medium ID: */
+ const QUuid uMediumId = comMedium.GetId();
+ if (!comMedium.isOk())
+ LogRel(("GUI: UIMediumEnumerator: Unable to acquire medium ID! %s\n",
+ UIErrorString::simplifiedErrorInfo(comMedium).toUtf8().constData()));
+ else
+ {
+ //printf(" Medium to recache: %s\n", uMediumId.toString().toUtf8().constData());
+
+ /* Make sure this medium is already cached: */
+ if (medium(uMediumId).isNull())
+ {
+ /* This medium isn't cached by some reason, which can be different.
+ * One of such reasons is when config-changed event comes earlier than
+ * corresponding registration event. For now we are ignoring that at all. */
+ LogRel2(("GUI: UIMediumEnumerator: Medium {%s} isn't cached yet!\n",
+ uMediumId.toString().toUtf8().constData()));
+ }
+ else
+ {
+ /* Enumerate corresponding UIMedium: */
+ LogRel2(("GUI: UIMediumEnumerator: Medium {%s} will be enumerated..\n",
+ uMediumId.toString().toUtf8().constData()));
+ createMediumEnumerationTask(m_media.value(uMediumId));
+ result << uMediumId;
+ }
+ }
+ }
+}
+
+void UIMediumEnumerator::enumerateAllMediaOfMachineWithId(const QUuid &uMachineId, QList<QUuid> &result)
+{
+ /* For each the cached UIMedium we have: */
+ foreach (const QUuid &uMediumId, mediumIDs())
+ {
+ /* Check if medium isn't NULL, used by our
+ * machine and wasn't already enumerated. */
+ const UIMedium guiMedium = medium(uMediumId);
+ if ( !guiMedium.isNull()
+ && guiMedium.machineIds().contains(uMachineId)
+ && !result.contains(uMediumId))
+ {
+ /* Enumerate corresponding UIMedium: */
+ //printf(" Medium to recache: %s\n",
+ // uMediumId.toString().toUtf8().constData());
+ LogRel2(("GUI: UIMediumEnumerator: Medium {%s} of machine {%s} will be enumerated..\n",
+ uMediumId.toString().toUtf8().constData(),
+ uMachineId.toString().toUtf8().constData()));
+ createMediumEnumerationTask(guiMedium);
+ result << uMediumId;
+ }
+ }
+}
+
+void UIMediumEnumerator::enumerateAllMediaOfMediumWithId(const QUuid &uParentMediumId, QList<QUuid> &result)
+{
+ /* For each the cached UIMedium we have: */
+ foreach (const QUuid &uMediumId, mediumIDs())
+ {
+ /* Check if medium isn't NULL, and is
+ * a child of specified parent medium. */
+ const UIMedium guiMedium = medium(uMediumId);
+ if ( !guiMedium.isNull()
+ && guiMedium.parentID() == uParentMediumId)
+ {
+ /* Enumerate corresponding UIMedium: */
+ //printf(" Medium to recache: %s\n",
+ // uMediumId.toString().toUtf8().constData());
+ LogRel2(("GUI: UIMediumEnumerator: Medium {%s} a child of medium {%s} will be enumerated..\n",
+ uMediumId.toString().toUtf8().constData(),
+ uParentMediumId.toString().toUtf8().constData()));
+ createMediumEnumerationTask(guiMedium);
+ result << uMediumId;
+ }
+ }
+}
+
+
+#include "UIMediumEnumerator.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumEnumerator.h b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumEnumerator.h
new file mode 100644
index 00000000..fda060d2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumEnumerator.h
@@ -0,0 +1,179 @@
+/* $Id: UIMediumEnumerator.h $ */
+/** @file
+ * VBox Qt GUI - UIMediumEnumerator class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_UIMediumEnumerator_h
+#define FEQT_INCLUDED_SRC_medium_UIMediumEnumerator_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QSet>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+#include "UIMedium.h"
+
+/* COM includes: */
+# include "CMedium.h"
+# include "CMediumAttachment.h"
+
+/* Forward declarations: */
+class UITask;
+class UIThreadPool;
+
+/** A map of CMedium objects ordered by their IDs. */
+typedef QMap<QUuid, CMedium> CMediumMap;
+
+/** QObject extension operating as medium-enumeration object.
+ * Manages access to cached UIMedium information via public API.
+ * Updates cache on corresponding Main events using thread-pool interface. */
+class SHARED_LIBRARY_STUFF UIMediumEnumerator : public QIWithRetranslateUI3<QObject>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about UIMedium with @a uMediumID created. */
+ void sigMediumCreated(const QUuid &uMediumID);
+ /** Notifies listeners about UIMedium with @a uMediumID deleted. */
+ void sigMediumDeleted(const QUuid &uMediumID);
+
+ /** Notifies listeners about consolidated medium-enumeration process has started. */
+ void sigMediumEnumerationStarted();
+ /** Notifies listeners about UIMedium with @a uMediumID updated. */
+ void sigMediumEnumerated(const QUuid &uMediumID);
+ /** Notifies listeners about consolidated medium-enumeration process has finished. */
+ void sigMediumEnumerationFinished();
+
+public:
+
+ /** Constructs medium-enumerator object. */
+ UIMediumEnumerator();
+
+ /** Returns cached UIMedium ID list. */
+ QList<QUuid> mediumIDs() const;
+ /** Returns a wrapper of cached UIMedium with specified @a uMediumID. */
+ UIMedium medium(const QUuid &uMediumID) const;
+
+ /** Creates UIMedium thus caching it internally on the basis of passed @a guiMedium information. */
+ void createMedium(const UIMedium &guiMedium);
+
+ /** Returns whether full consolidated medium-enumeration process is requested. */
+ bool isFullMediumEnumerationRequested() const { return m_fFullMediumEnumerationRequested; }
+ /** Returns whether any consolidated medium-enumeration process is in progress. */
+ bool isMediumEnumerationInProgress() const { return m_fMediumEnumerationInProgress; }
+ /** Makes a request to enumerate specified @a comMedia.
+ * @note Empty passed map means that full/overall medium-enumeration is requested.
+ * In that case previous map will be replaced with the new one, values
+ * present in both maps will be merged from the previous to new one.
+ * @note Non-empty passed map means that additional medium-enumeration is requested.
+ * In that case previous map will be extended with the new one, values
+ * present in both maps will be merged from the previous to new one. */
+ void enumerateMedia(const CMediumVector &comMedia = CMediumVector());
+ /** Refresh all the lightweight UIMedium information for all the cached UIMedium(s).
+ * @note Please note that this is a lightweight version, which doesn't perform
+ * heavy state/accessibility checks thus doesn't require to be performed
+ * by a worker COM-aware thread. */
+ void refreshMedia();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles machine-data-change event for a machine with specified @a uMachineId. */
+ void sltHandleMachineDataChange(const QUuid &uMachineId);
+
+ /** Handles signal about storage controller change.
+ * @param uMachineId Brings the ID of machine corresponding controller belongs to.
+ * @param strControllerName Brings the name of controller this event is related to. */
+ void sltHandleStorageControllerChange(const QUuid &uMachineId, const QString &strControllerName);
+ /** Handles signal about storage device change.
+ * @param comAttachment Brings corresponding attachment.
+ * @param fRemoved Brings whether medium is removed or added.
+ * @param fSilent Brings whether this change has gone silent for guest. */
+ void sltHandleStorageDeviceChange(CMediumAttachment comAttachment, bool fRemoved, bool fSilent);
+ /** Handles signal about storage medium @a comAttachment state change. */
+ void sltHandleMediumChange(CMediumAttachment comAttachment);
+ /** Handles signal about storage @a comMedium config change. */
+ void sltHandleMediumConfigChange(CMedium comMedium);
+ /** Handles signal about storage medium is (un)registered.
+ * @param uMediumId Brings corresponding medium ID.
+ * @param enmMediumType Brings corresponding medium type.
+ * @param fRegistered Brings whether medium is registered or unregistered. */
+ void sltHandleMediumRegistered(const QUuid &uMediumId, KDeviceType enmMediumType, bool fRegistered);
+
+ /** Handles medium-enumeration @a pTask complete signal. */
+ void sltHandleMediumEnumerationTaskComplete(UITask *pTask);
+
+private:
+
+ /** Creates medium-enumeration task for certain @a guiMedium. */
+ void createMediumEnumerationTask(const UIMedium &guiMedium);
+ /** Adds NULL UIMedium to specified @a outputMedia map. */
+ void addNullMediumToMap(UIMediumMap &outputMedia);
+ /** Adds @a inputMedia to specified @a outputMedia map. */
+ void addMediaToMap(const CMediumVector &inputMedia, UIMediumMap &outputMedia);
+
+ /** Parses incoming @a comAttachment, enumerating the media it has attached.
+ * @param result Brings the list of previously enumerated media
+ * IDs to be appended with newly enumerated. */
+ void parseAttachment(CMediumAttachment comAttachment, QList<QUuid> &result);
+ /** Parses incoming @a comMedium, enumerating the media it represents.
+ * @param result Brings the list of previously enumerated media
+ * IDs to be appended with newly enumerated. */
+ void parseMedium(CMedium comMedium, QList<QUuid> &result);
+
+ /** Enumerates all the known media attached to machine with certain @a uMachineId.
+ * @param result Brings the list of previously enumerated media
+ * IDs to be appended with newly enumerated. */
+ void enumerateAllMediaOfMachineWithId(const QUuid &uMachineId, QList<QUuid> &result);
+ /** Enumerates all the children media of medium with certain @a uMediumId.
+ * @param result Brings the list of previously enumerated media
+ * IDs to be appended with newly enumerated. */
+ void enumerateAllMediaOfMediumWithId(const QUuid &uMediumId, QList<QUuid> &result);
+
+ /** Holds whether full consolidated medium-enumeration process is requested. */
+ bool m_fFullMediumEnumerationRequested;
+ /** Holds whether any consolidated medium-enumeration process is in progress. */
+ bool m_fMediumEnumerationInProgress;
+
+ /** Holds a set of current medium-enumeration tasks. */
+ QSet<UITask*> m_tasks;
+
+ /** Holds a map of currently cached (enumerated) media. */
+ UIMediumMap m_media;
+ /** Holds a set of currently registered media IDs. */
+ QSet<QUuid> m_registeredMediaIds;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_UIMediumEnumerator_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumItem.cpp b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumItem.cpp
new file mode 100644
index 00000000..b6650570
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumItem.cpp
@@ -0,0 +1,628 @@
+/* $Id: UIMediumItem.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMediumItem class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QDir>
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "QIMessageBox.h"
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMediumItem.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "CMachine.h"
+#include "CMediumAttachment.h"
+#include "CMediumFormat.h"
+#include "CStorageController.h"
+
+
+/*********************************************************************************************************************************
+* Class UIMediumItem implementation. *
+*********************************************************************************************************************************/
+
+UIMediumItem::UIMediumItem(const UIMedium &guiMedium, QITreeWidget *pParent)
+ : QITreeWidgetItem(pParent)
+ , m_guiMedium(guiMedium)
+{
+ refresh();
+}
+
+UIMediumItem::UIMediumItem(const UIMedium &guiMedium, UIMediumItem *pParent)
+ : QITreeWidgetItem(pParent)
+ , m_guiMedium(guiMedium)
+{
+ refresh();
+}
+
+UIMediumItem::UIMediumItem(const UIMedium &guiMedium, QITreeWidgetItem *pParent)
+ : QITreeWidgetItem(pParent)
+ , m_guiMedium(guiMedium)
+{
+ refresh();
+}
+
+bool UIMediumItem::move()
+{
+ /* Open file-save dialog to choose location for current medium: */
+ const QString strFileName = QIFileDialog::getSaveFileName(location(),
+ tr("Current extension (*.%1)")
+ .arg(QFileInfo(location()).suffix()),
+ treeWidget(),
+ tr("Choose the location of this medium"),
+ 0, true, true);
+ if (strFileName.isNull() || strFileName == location())
+ return false;
+
+ /* Search for corresponding medium: */
+ CMedium comMedium = medium().medium();
+ if (comMedium.isNull() || !comMedium.isOk())
+ return false;
+
+ /* Assign new medium location: */
+ UINotificationProgressMediumMove *pNotification = new UINotificationProgressMediumMove(comMedium,
+ strFileName);
+ connect(pNotification, &UINotificationProgressMediumMove::sigProgressFinished,
+ this, &UIMediumItem::sltHandleMoveProgressFinished);
+ gpNotificationCenter->append(pNotification);
+
+ /* Positive: */
+ return true;
+}
+
+bool UIMediumItem::release(bool fShowMessageBox, bool fInduced)
+{
+ /* Refresh medium and item: */
+ m_guiMedium.refresh();
+ refresh();
+
+ /* Make sure medium was not released yet: */
+ if (medium().curStateMachineIds().isEmpty())
+ return true;
+
+ /* Confirm release: */
+ if (fShowMessageBox)
+ if (!msgCenter().confirmMediumRelease(medium(), fInduced, treeWidget()))
+ return false;
+
+ /* Release: */
+ foreach (const QUuid &uMachineId, medium().curStateMachineIds())
+ if (!releaseFrom(uMachineId))
+ return false;
+
+ /* True by default: */
+ return true;
+}
+
+void UIMediumItem::refreshAll()
+{
+ m_guiMedium.blockAndQueryState();
+ refresh();
+}
+
+void UIMediumItem::setMedium(const UIMedium &guiMedium)
+{
+ m_guiMedium = guiMedium;
+ refresh();
+}
+
+bool UIMediumItem::operator<(const QTreeWidgetItem &other) const
+{
+ int iColumn = treeWidget()->sortColumn();
+ ULONG64 uThisValue = UITranslator::parseSize( text(iColumn));
+ ULONG64 uThatValue = UITranslator::parseSize(other.text(iColumn));
+ return uThisValue && uThatValue ? uThisValue < uThatValue : QTreeWidgetItem::operator<(other);
+}
+
+bool UIMediumItem::isMediumModifiable() const
+{
+ if (medium().isNull())
+ return false;
+ if (m_enmDeviceType == UIMediumDeviceType_DVD || m_enmDeviceType == UIMediumDeviceType_Floppy)
+ return false;
+ foreach (const QUuid &uMachineId, medium().curStateMachineIds())
+ {
+ CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
+ if (comMachine.isNull())
+ continue;
+ if (comMachine.GetState() != KMachineState_PoweredOff &&
+ comMachine.GetState() != KMachineState_Aborted &&
+ comMachine.GetState() != KMachineState_AbortedSaved)
+ return false;
+ }
+ return true;
+}
+
+bool UIMediumItem::isMediumAttachedTo(QUuid uId)
+{
+ if (medium().isNull())
+ return false;
+ return medium().curStateMachineIds().contains(uId);
+}
+
+bool UIMediumItem::changeMediumType(KMediumType enmNewType)
+{
+ /* Cache the list of VMs the medium is attached to. We will need this for the re-attachment: */
+ QList<AttachmentCache> attachmentCacheList;
+ foreach (const QUuid &uMachineId, medium().curStateMachineIds())
+ {
+ const CMachine &comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
+ if (comMachine.isNull())
+ continue;
+ foreach (const CStorageController &comController, comMachine.GetStorageControllers())
+ {
+ if (comController.isNull())
+ continue;
+ const QString strControllerName = comController.GetName();
+ foreach (const CMediumAttachment &comAttachment, comMachine.GetMediumAttachmentsOfController(strControllerName))
+ {
+ if (comAttachment.isNull())
+ continue;
+ const CMedium &comMedium = comAttachment.GetMedium();
+ if (comMedium.isNull() || comMedium.GetId() != id())
+ continue;
+ AttachmentCache attachmentCache;
+ attachmentCache.m_uMachineId = uMachineId;
+ attachmentCache.m_strControllerName = strControllerName;
+ attachmentCache.m_enmControllerBus = comController.GetBus();
+ attachmentCache.m_iAttachmentPort = comAttachment.GetPort();
+ attachmentCache.m_iAttachmentDevice = comAttachment.GetDevice();
+ attachmentCacheList << attachmentCache;
+ }
+ }
+ }
+
+ /* Detach the medium from all the VMs it's attached to: */
+ if (!release(true, true))
+ return false;
+
+ /* Attempt to change medium type: */
+ CMedium comMedium = medium().medium();
+ comMedium.SetType(enmNewType);
+ if (!comMedium.isOk())
+ {
+ UINotificationMessage::cannotChangeMediumParameter(comMedium);
+ return false;
+ }
+
+ /* Reattach the medium to all the VMs it was previously attached: */
+ foreach (const AttachmentCache &attachmentCache, attachmentCacheList)
+ if (!attachTo(attachmentCache))
+ return false;
+
+ /* True finally: */
+ return true;
+}
+
+QString UIMediumItem::defaultText() const
+{
+ return tr("%1, %2: %3, %4: %5", "col.1 text, col.2 name: col.2 text, col.3 name: col.3 text")
+ .arg(text(0))
+ .arg(parentTree()->headerItem()->text(1)).arg(text(1))
+ .arg(parentTree()->headerItem()->text(2)).arg(text(2));
+}
+
+void UIMediumItem::sltHandleMoveProgressFinished()
+{
+ /* Recache item: */
+ refreshAll();
+}
+
+void UIMediumItem::sltHandleMediumRemoveRequest(CMedium comMedium)
+{
+ /* Close medium finally: */
+ comMedium.Close();
+ if (!comMedium.isOk())
+ UINotificationMessage::cannotCloseMedium(comMedium);
+}
+
+void UIMediumItem::refresh()
+{
+ /* Fill-in columns: */
+ setIcon(0, m_guiMedium.icon());
+ setText(0, m_guiMedium.name());
+ setText(1, m_guiMedium.logicalSize());
+ setText(2, m_guiMedium.size());
+ /* All columns get the same tooltip: */
+ QString strToolTip = m_guiMedium.toolTip();
+ for (int i = 0; i < treeWidget()->columnCount(); ++i)
+ setToolTip(i, strToolTip);
+
+ /* Gather medium data: */
+ m_fValid = !m_guiMedium.isNull()
+ && m_guiMedium.state() != KMediumState_Inaccessible;
+ m_enmDeviceType = m_guiMedium.type();
+ m_enmVariant = m_guiMedium.mediumVariant();
+ m_fHasChildren = m_guiMedium.hasChildren();
+ /* Gather medium options data: */
+ m_options.m_enmMediumType = m_guiMedium.mediumType();
+ m_options.m_strLocation = m_guiMedium.location();
+ m_options.m_uLogicalSize = m_guiMedium.logicalSizeInBytes();
+ m_options.m_strDescription = m_guiMedium.description();
+ /* Gather medium details data: */
+ m_details.m_aFields.clear();
+ switch (m_enmDeviceType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ {
+ m_details.m_aLabels << tr("Format:");
+ m_details.m_aLabels << tr("Storage details:");
+ m_details.m_aLabels << tr("Attached to:");
+ m_details.m_aLabels << tr("Encrypted with key:");
+ m_details.m_aLabels << tr("UUID:");
+
+ m_details.m_aFields << hardDiskFormat();
+ m_details.m_aFields << details();
+ m_details.m_aFields << (usage().isNull() ?
+ formatFieldText(tr("<i>Not&nbsp;Attached</i>"), false) :
+ formatFieldText(usage()));
+ m_details.m_aFields << (encryptionPasswordID().isNull() ?
+ formatFieldText(tr("<i>Not&nbsp;Encrypted</i>"), false) :
+ formatFieldText(encryptionPasswordID()));
+ m_details.m_aFields << id().toString();
+
+ break;
+ }
+ case UIMediumDeviceType_DVD:
+ case UIMediumDeviceType_Floppy:
+ {
+ m_details.m_aLabels << tr("Attached to:");
+ m_details.m_aLabels << tr("UUID:");
+
+ m_details.m_aFields << (usage().isNull() ?
+ formatFieldText(tr("<i>Not&nbsp;Attached</i>"), false) :
+ formatFieldText(usage()));
+ m_details.m_aFields << id().toString();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+bool UIMediumItem::releaseFrom(const QUuid &uMachineId)
+{
+ /* Open session: */
+ CSession session = uiCommon().openSession(uMachineId);
+ if (session.isNull())
+ return false;
+
+ /* Get machine: */
+ CMachine machine = session.GetMachine();
+
+ /* Prepare result: */
+ bool fSuccess = false;
+
+ /* Release medium from machine: */
+ if (releaseFrom(machine))
+ {
+ /* Save machine settings: */
+ machine.SaveSettings();
+ if (!machine.isOk())
+ msgCenter().cannotSaveMachineSettings(machine, treeWidget());
+ else
+ fSuccess = true;
+ }
+
+ /* Close session: */
+ session.UnlockMachine();
+
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMediumItem::attachTo(const AttachmentCache &attachmentCache)
+{
+ /* Open session: */
+ CSession comSession = uiCommon().openSession(attachmentCache.m_uMachineId);
+ if (comSession.isNull())
+ return false;
+
+ /* Attach device to machine: */
+ CMedium comMedium = medium().medium();
+ KDeviceType enmDeviceType = comMedium.GetDeviceType();
+ CMachine comMachine = comSession.GetMachine();
+ comMachine.AttachDevice(attachmentCache.m_strControllerName,
+ attachmentCache.m_iAttachmentPort,
+ attachmentCache.m_iAttachmentDevice,
+ enmDeviceType,
+ comMedium);
+ if (!comMachine.isOk())
+ msgCenter().cannotAttachDevice(comMachine,
+ mediumTypeToLocal(enmDeviceType),
+ comMedium.GetLocation(),
+ StorageSlot(attachmentCache.m_enmControllerBus,
+ attachmentCache.m_iAttachmentPort,
+ attachmentCache.m_iAttachmentDevice),
+ parentTree());
+ else
+ {
+ /* Save machine settings: */
+ comMachine.SaveSettings();
+ if (!comMachine.isOk())
+ msgCenter().cannotSaveMachineSettings(comMachine, parentTree());
+ }
+
+ /* Close session: */
+ comSession.UnlockMachine();
+
+ /* True finally: */
+ return true;
+}
+
+/* static */
+QString UIMediumItem::formatFieldText(const QString &strText, bool fCompact /* = true */,
+ const QString &strElipsis /* = "middle" */)
+{
+ QString strCompactString = QString("<compact elipsis=\"%1\">").arg(strElipsis);
+ QString strInfo = QString("<nobr>%1%2%3</nobr>")
+ .arg(fCompact ? strCompactString : "")
+ .arg(strText.isEmpty() ? tr("--", "no info") : strText)
+ .arg(fCompact ? "</compact>" : "");
+ return strInfo;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMediumItemHD implementation. *
+*********************************************************************************************************************************/
+
+UIMediumItemHD::UIMediumItemHD(const UIMedium &guiMedium, QITreeWidget *pParent)
+ : UIMediumItem(guiMedium, pParent)
+{
+}
+
+UIMediumItemHD::UIMediumItemHD(const UIMedium &guiMedium, UIMediumItem *pParent)
+ : UIMediumItem(guiMedium, pParent)
+{
+}
+
+UIMediumItemHD::UIMediumItemHD(const UIMedium &guiMedium, QITreeWidgetItem *pParent)
+ : UIMediumItem(guiMedium, pParent)
+{
+}
+
+bool UIMediumItemHD::remove(bool fShowMessageBox)
+{
+ /* Confirm medium removal: */
+ if (fShowMessageBox)
+ if (!msgCenter().confirmMediumRemoval(medium(), treeWidget()))
+ return false;
+
+ /* Propose to remove medium storage: */
+ if (!maybeRemoveStorage())
+ return false;
+
+ /* True by default: */
+ return true;
+}
+
+bool UIMediumItemHD::releaseFrom(CMachine comMachine)
+{
+ /* Was medium released from at least one attachment? */
+ bool fAtLeastOneRelease = false;
+
+ /* Enumerate attachments: */
+ CMediumAttachmentVector attachments = comMachine.GetMediumAttachments();
+ foreach (const CMediumAttachment &attachment, attachments)
+ {
+ /* Skip non-hard-disks: */
+ if (attachment.GetType() != KDeviceType_HardDisk)
+ continue;
+
+ /* Skip unrelated hard-disks: */
+ if (attachment.GetMedium().GetId() != id())
+ continue;
+
+ /* Remember controller: */
+ CStorageController controller = comMachine.GetStorageControllerByName(attachment.GetController());
+
+ /* Try to detach device: */
+ comMachine.DetachDevice(attachment.GetController(), attachment.GetPort(), attachment.GetDevice());
+ if (!comMachine.isOk())
+ {
+ /* Return failure: */
+ msgCenter().cannotDetachDevice(comMachine, UIMediumDeviceType_HardDisk, location(),
+ StorageSlot(controller.GetBus(), attachment.GetPort(), attachment.GetDevice()),
+ treeWidget());
+ return false;
+ }
+ else
+ fAtLeastOneRelease = true;
+ }
+
+ /* Return whether there was at least one release: */
+ return fAtLeastOneRelease;
+}
+
+bool UIMediumItemHD::maybeRemoveStorage()
+{
+ /* Acquire medium: */
+ CMedium comMedium = medium().medium();
+
+ /* We don't want to try to delete inaccessible storage as it will most likely fail.
+ * Note that UIMessageCenter::confirmMediumRemoval() is aware of that and
+ * will give a corresponding hint. Therefore, once the code is changed below,
+ * the hint should be re-checked for validity. */
+ bool fDeleteStorage = false;
+ qulonglong uCapability = 0;
+ foreach (KMediumFormatCapabilities capability, comMedium.GetMediumFormat().GetCapabilities())
+ uCapability |= capability;
+ if (state() != KMediumState_Inaccessible && uCapability & KMediumFormatCapabilities_File)
+ {
+ int rc = msgCenter().confirmDeleteHardDiskStorage(location(), treeWidget());
+ if (rc == AlertButton_Cancel)
+ return false;
+ fDeleteStorage = rc == AlertButton_Choice1;
+ }
+
+ /* If user wish to delete storage: */
+ if (fDeleteStorage)
+ {
+ /* Deleting medium storage first of all: */
+ UINotificationProgressMediumDeletingStorage *pNotification = new UINotificationProgressMediumDeletingStorage(comMedium);
+ connect(pNotification, &UINotificationProgressMediumDeletingStorage::sigMediumStorageDeleted,
+ this, &UIMediumItemHD::sltHandleMediumRemoveRequest);
+ gpNotificationCenter->append(pNotification);
+ }
+ /* Otherwise go to last step immediatelly: */
+ else
+ sltHandleMediumRemoveRequest(comMedium);
+
+ /* True by default: */
+ return true;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMediumItemCD implementation. *
+*********************************************************************************************************************************/
+
+UIMediumItemCD::UIMediumItemCD(const UIMedium &guiMedium, QITreeWidget *pParent)
+ : UIMediumItem(guiMedium, pParent)
+{
+}
+
+UIMediumItemCD::UIMediumItemCD(const UIMedium &guiMedium, QITreeWidgetItem *pParent)
+ : UIMediumItem(guiMedium, pParent)
+{
+}
+
+bool UIMediumItemCD::remove(bool fShowMessageBox)
+{
+ /* Confirm medium removal: */
+ if (fShowMessageBox)
+ if (!msgCenter().confirmMediumRemoval(medium(), treeWidget()))
+ return false;
+
+ /* Close optical-disk: */
+ sltHandleMediumRemoveRequest(medium().medium());
+
+ /* True by default: */
+ return true;
+}
+
+bool UIMediumItemCD::releaseFrom(CMachine comMachine)
+{
+ /* Was medium released from at least one attachment? */
+ bool fAtLeastOneRelease = false;
+
+ /* Enumerate attachments: */
+ CMediumAttachmentVector attachments = comMachine.GetMediumAttachments();
+ foreach (const CMediumAttachment &attachment, attachments)
+ {
+ /* Skip non-optical-disks: */
+ if (attachment.GetType() != KDeviceType_DVD)
+ continue;
+
+ /* Skip unrelated optical-disks: */
+ if (attachment.GetMedium().GetId() != id())
+ continue;
+
+ /* Try to unmount device: */
+ comMachine.MountMedium(attachment.GetController(), attachment.GetPort(), attachment.GetDevice(), CMedium(), false /* force */);
+ if (!comMachine.isOk())
+ {
+ /* Return failure: */
+ msgCenter().cannotRemountMedium(comMachine, medium(), false /* mount? */, false /* retry? */, treeWidget());
+ return false;
+ }
+ else
+ fAtLeastOneRelease = true;
+ }
+
+ /* Return whether there was at least one release: */
+ return fAtLeastOneRelease;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMediumItemFD implementation. *
+*********************************************************************************************************************************/
+
+UIMediumItemFD::UIMediumItemFD(const UIMedium &guiMedium, QITreeWidget *pParent)
+ : UIMediumItem(guiMedium, pParent)
+{
+}
+
+UIMediumItemFD::UIMediumItemFD(const UIMedium &guiMedium, QITreeWidgetItem *pParent)
+ : UIMediumItem(guiMedium, pParent)
+{
+}
+
+bool UIMediumItemFD::remove(bool fShowMessageBox)
+{
+ /* Confirm medium removal: */
+ if (fShowMessageBox)
+ if (!msgCenter().confirmMediumRemoval(medium(), treeWidget()))
+ return false;
+
+ /* Close floppy-disk: */
+ sltHandleMediumRemoveRequest(medium().medium());
+
+ /* True by default: */
+ return true;
+}
+
+bool UIMediumItemFD::releaseFrom(CMachine comMachine)
+{
+ /* Was medium released from at least one attachment? */
+ bool fAtLeastOneRelease = false;
+
+ /* Enumerate attachments: */
+ CMediumAttachmentVector attachments = comMachine.GetMediumAttachments();
+ foreach (const CMediumAttachment &attachment, attachments)
+ {
+ /* Skip non-floppy-disks: */
+ if (attachment.GetType() != KDeviceType_Floppy)
+ continue;
+
+ /* Skip unrelated floppy-disks: */
+ if (attachment.GetMedium().GetId() != id())
+ continue;
+
+ /* Try to unmount device: */
+ comMachine.MountMedium(attachment.GetController(), attachment.GetPort(), attachment.GetDevice(), CMedium(), false /* force */);
+ if (!comMachine.isOk())
+ {
+ /* Return failure: */
+ msgCenter().cannotRemountMedium(comMachine, medium(), false /* mount? */, false /* retry? */, treeWidget());
+ return false;
+ }
+ else
+ fAtLeastOneRelease = true;
+ }
+
+ /* Return whether there was at least one release: */
+ return fAtLeastOneRelease;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumItem.h b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumItem.h
new file mode 100644
index 00000000..afc0e1dd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumItem.h
@@ -0,0 +1,247 @@
+/* $Id: UIMediumItem.h $ */
+/** @file
+ * VBox Qt GUI - UIMediumItem class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_UIMediumItem_h
+#define FEQT_INCLUDED_SRC_medium_UIMediumItem_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QITreeWidget.h"
+#include "UIMedium.h"
+#include "UIMediumDetailsWidget.h"
+
+/** QITreeWidgetItem extension representing Media Manager item. */
+class SHARED_LIBRARY_STUFF UIMediumItem : public QITreeWidgetItem, public UIDataMedium
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs top-level item.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent tree reference. */
+ UIMediumItem(const UIMedium &guiMedium, QITreeWidget *pParent);
+ /** Constructs sub-level item.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent item reference. */
+ UIMediumItem(const UIMedium &guiMedium, UIMediumItem *pParent);
+ /** Constructs sub-level item under a QITreeWidgetItem.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent item reference. */
+ UIMediumItem(const UIMedium &guiMedium, QITreeWidgetItem *pParent);
+
+ /** Moves UIMedium wrapped by <i>this</i> item. */
+ virtual bool move();
+ /** Removes UIMedium wrapped by <i>this</i> item. */
+ virtual bool remove(bool fShowMessageBox) = 0;
+ /** Releases UIMedium wrapped by <i>this</i> item.
+ * @param fInduced Brings whether this action is caused by other user's action,
+ * not a direct order to release particularly selected medium. */
+ virtual bool release(bool fShowMessageBox, bool fInduced);
+
+ /** Refreshes item fully. */
+ void refreshAll();
+
+ /** Returns UIMedium wrapped by <i>this</i> item. */
+ const UIMedium &medium() const { return m_guiMedium; }
+ /** Defines UIMedium wrapped by <i>this</i> item. */
+ void setMedium(const UIMedium &guiMedium);
+
+ /** Returns UIMediumDeviceType of the wrapped UIMedium. */
+ UIMediumDeviceType mediumType() const { return m_guiMedium.type(); }
+
+ /** Returns KMediumState of the wrapped UIMedium. */
+ KMediumState state() const { return m_guiMedium.state(); }
+
+ /** Returns QUuid <i>ID</i> of the wrapped UIMedium. */
+ QUuid id() const { return m_guiMedium.id(); }
+ /** Returns QString <i>name</i> of the wrapped UIMedium. */
+ QString name() const { return m_guiMedium.name(); }
+
+ /** Returns QString <i>location</i> of the wrapped UIMedium. */
+ QString location() const { return m_guiMedium.location(); }
+
+ /** Returns QString <i>hard-disk format</i> of the wrapped UIMedium. */
+ QString hardDiskFormat() const { return m_guiMedium.hardDiskFormat(); }
+ /** Returns QString <i>hard-disk type</i> of the wrapped UIMedium. */
+ QString hardDiskType() const { return m_guiMedium.hardDiskType(); }
+
+ /** Returns QString <i>storage details</i> of the wrapped UIMedium. */
+ QString details() const { return m_guiMedium.storageDetails(); }
+ /** Returns QString <i>encryption password ID</i> of the wrapped UIMedium. */
+ QString encryptionPasswordID() const { return m_guiMedium.encryptionPasswordID(); }
+
+ /** Returns QString <i>tool-tip</i> of the wrapped UIMedium. */
+ QString toolTip() const { return m_guiMedium.toolTip(); }
+
+ /** Returns a vector of IDs of all machines wrapped UIMedium is attached to. */
+ const QList<QUuid> &machineIds() const { return m_guiMedium.machineIds(); }
+ /** Returns QString <i>usage</i> of the wrapped UIMedium. */
+ QString usage() const { return m_guiMedium.usage(); }
+ /** Returns whether wrapped UIMedium is used. */
+ bool isUsed() const { return m_guiMedium.isUsed(); }
+ /** Returns whether wrapped UIMedium is used in snapshots. */
+ bool isUsedInSnapshots() const { return m_guiMedium.isUsedInSnapshots(); }
+
+ /** Returns whether <i>this</i> item is less than @a other one. */
+ bool operator<(const QTreeWidgetItem &other) const;
+ /** Returns whether the medium can be modified. For
+ * simplicity's sake this returns false if one of the attached vms is not
+ * in PoweredOff or Aborted state. */
+ bool isMediumModifiable() const;
+ /** Returns true if the medium is attached to the vm with @p uId. */
+ bool isMediumAttachedTo(QUuid uId);
+ bool changeMediumType(KMediumType enmNewType);
+
+protected:
+
+ /** Release UIMedium wrapped by <i>this</i> item from virtual @a comMachine. */
+ virtual bool releaseFrom(CMachine comMachine) = 0;
+
+ /** Returns default text. */
+ virtual QString defaultText() const RT_OVERRIDE;
+
+protected slots:
+
+ /** Handles medium move progress result. */
+ void sltHandleMoveProgressFinished();
+
+ /** Handles @a comMedium remove request. */
+ void sltHandleMediumRemoveRequest(CMedium comMedium);
+
+private:
+
+ /** A simple struct used to save some parameters of machine device attachment.
+ * Used for re-attaching the medium to VMs after a medium type change. */
+ struct AttachmentCache
+ {
+ /** Holds the machine ID. */
+ QUuid m_uMachineId;
+ /** Holds the controller name. */
+ QString m_strControllerName;
+ /** Holds the controller bus. */
+ KStorageBus m_enmControllerBus;
+ /** Holds the attachment port. */
+ LONG m_iAttachmentPort;
+ /** Holds the attachment device. */
+ LONG m_iAttachmentDevice;
+ };
+
+ /** Refreshes item information such as icon, text and tool-tip. */
+ void refresh();
+
+ /** Releases UIMedium wrapped by <i>this</i> item from virtual machine with @a uMachineId. */
+ bool releaseFrom(const QUuid &uMachineId);
+ /** Is called by detaching the medium and modifiying it to restore the attachement. */
+ bool attachTo(const AttachmentCache &attachmentCache);
+
+ /** Formats field text. */
+ static QString formatFieldText(const QString &strText, bool fCompact = true, const QString &strElipsis = "middle");
+
+ /** Holds the UIMedium wrapped by <i>this</i> item. */
+ UIMedium m_guiMedium;
+};
+
+
+/** UIMediumItem extension representing hard-disk item. */
+class SHARED_LIBRARY_STUFF UIMediumItemHD : public UIMediumItem
+{
+public:
+
+ /** Constructs top-level item.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent tree reference. */
+ UIMediumItemHD(const UIMedium &guiMedium, QITreeWidget *pParent);
+ /** Constructs sub-level item.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent item reference. */
+ UIMediumItemHD(const UIMedium &guiMedium, UIMediumItem *pParent);
+ /** Constructs sub-level item under a QITreeWidgetItem.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent item reference. */
+ UIMediumItemHD(const UIMedium &guiMedium, QITreeWidgetItem *pParent);
+
+protected:
+
+ /** Removes UIMedium wrapped by <i>this</i> item. */
+ virtual bool remove(bool fShowMessageBox) RT_OVERRIDE;
+ /** Releases UIMedium wrapped by <i>this</i> item from virtual @a comMachine. */
+ virtual bool releaseFrom(CMachine comMachine) RT_OVERRIDE;
+
+private:
+
+ /** Proposes user to remove CMedium storage wrapped by <i>this</i> item. */
+ bool maybeRemoveStorage();
+};
+
+/** UIMediumItem extension representing optical-disk item. */
+class SHARED_LIBRARY_STUFF UIMediumItemCD : public UIMediumItem
+{
+public:
+
+ /** Constructs top-level item.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent tree reference. */
+ UIMediumItemCD(const UIMedium &guiMedium, QITreeWidget *pParent);
+ /** Constructs sub-level item under a QITreeWidgetItem.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent item reference. */
+ UIMediumItemCD(const UIMedium &guiMedium, QITreeWidgetItem *pParent);
+
+protected:
+
+ /** Removes UIMedium wrapped by <i>this</i> item. */
+ virtual bool remove(bool fShowMessageBox) RT_OVERRIDE;
+ /** Releases UIMedium wrapped by <i>this</i> item from virtual @a comMachine. */
+ virtual bool releaseFrom(CMachine comMachine) RT_OVERRIDE;
+};
+
+/** UIMediumItem extension representing floppy-disk item. */
+class SHARED_LIBRARY_STUFF UIMediumItemFD : public UIMediumItem
+{
+public:
+
+ /** Constructs top-level item.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent tree reference. */
+ UIMediumItemFD(const UIMedium &guiMedium, QITreeWidget *pParent);
+ /** Constructs sub-level item under a QITreeWidgetItem.
+ * @param guiMedium Brings the medium to wrap around.
+ * @param pParent Brings the parent item reference. */
+ UIMediumItemFD(const UIMedium &guiMedium, QITreeWidgetItem *pParent);
+
+protected:
+
+ /** Removes UIMedium wrapped by <i>this</i> item. */
+ virtual bool remove(bool fShowMessageBox) RT_OVERRIDE;
+ /** Releases UIMedium wrapped by <i>this</i> item from virtual @a comMachine. */
+ virtual bool releaseFrom(CMachine comMachine) RT_OVERRIDE;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_UIMediumItem_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumManager.cpp b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumManager.cpp
new file mode 100644
index 00000000..90d439fb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumManager.cpp
@@ -0,0 +1,1832 @@
+/* $Id: UIMediumManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMediumManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QFrame>
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <QLabel>
+#include <QMenuBar>
+#include <QProgressBar>
+#include <QPushButton>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIFileDialog.h"
+#include "QILabel.h"
+#include "QIMessageBox.h"
+#include "QITabWidget.h"
+#include "UIActionPoolManager.h"
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMediumDetailsWidget.h"
+#include "UIMediumItem.h"
+#include "UIMediumManager.h"
+#include "UIMediumSearchWidget.h"
+#include "UINotificationCenter.h"
+#include "UIWizardCloneVD.h"
+#include "UIMessageCenter.h"
+#include "QIToolBar.h"
+#include "UIIconPool.h"
+#include "UIMedium.h"
+#include "UIVirtualBoxEventHandler.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+#include "CMediumAttachment.h"
+#include "CMediumFormat.h"
+#include "CStorageController.h"
+#include "CSystemProperties.h"
+
+#ifdef VBOX_WS_MAC
+# include "UIWindowMenuManager.h"
+#endif /* VBOX_WS_MAC */
+
+
+
+/** Functor allowing to check if passed UIMediumItem is suitable by @a uID. */
+class CheckIfSuitableByID : public CheckIfSuitableBy
+{
+public:
+ /** Constructor accepting @a uID to compare with. */
+ CheckIfSuitableByID(const QUuid &uID) : m_uID(uID) {}
+
+private:
+ /** Determines whether passed UIMediumItem is suitable by @a uID. */
+ bool isItSuitable(UIMediumItem *pItem) const { return pItem->id() == m_uID; }
+ /** Holds the @a uID to compare to. */
+ QUuid m_uID;
+};
+
+/** Functor allowing to check if passed UIMediumItem is suitable by @a state. */
+class CheckIfSuitableByState : public CheckIfSuitableBy
+{
+public:
+ /** Constructor accepting @a state to compare with. */
+ CheckIfSuitableByState(KMediumState state) : m_state(state) {}
+
+private:
+ /** Determines whether passed UIMediumItem is suitable by @a state. */
+ bool isItSuitable(UIMediumItem *pItem) const { return pItem->state() == m_state; }
+ /** Holds the @a state to compare to. */
+ KMediumState m_state;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIEnumerationProgressBar implementation. *
+*********************************************************************************************************************************/
+
+UIEnumerationProgressBar::UIEnumerationProgressBar(QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIEnumerationProgressBar::setText(const QString &strText)
+{
+ m_pLabel->setText(strText);
+}
+
+int UIEnumerationProgressBar::value() const
+{
+ return m_pProgressBar->value();
+}
+
+void UIEnumerationProgressBar::setValue(int iValue)
+{
+ m_pProgressBar->setValue(iValue);
+}
+
+void UIEnumerationProgressBar::setMaximum(int iValue)
+{
+ m_pProgressBar->setMaximum(iValue);
+}
+
+void UIEnumerationProgressBar::prepare()
+{
+ /* Create layout: */
+ QHBoxLayout *pLayout = new QHBoxLayout(this);
+ {
+ /* Configure layout: */
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ /* Create label: */
+ m_pLabel = new QLabel;
+ /* Create progress-bar: */
+ m_pProgressBar = new QProgressBar;
+ {
+ /* Configure progress-bar: */
+ m_pProgressBar->setTextVisible(false);
+ }
+ /* Add widgets into layout: */
+ pLayout->addWidget(m_pLabel);
+ pLayout->addWidget(m_pProgressBar);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMediumManagerWidget implementation. *
+*********************************************************************************************************************************/
+
+UIMediumManagerWidget::UIMediumManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar /* = true */, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_fPreventChangeCurrentItem(false)
+ , m_pTabWidget(0)
+ , m_iTabCount(3)
+ , m_fInaccessibleHD(false)
+ , m_fInaccessibleCD(false)
+ , m_fInaccessibleFD(false)
+ , m_iconHD(UIIconPool::iconSet(":/hd_16px.png", ":/hd_disabled_16px.png"))
+ , m_iconCD(UIIconPool::iconSet(":/cd_16px.png", ":/cd_disabled_16px.png"))
+ , m_iconFD(UIIconPool::iconSet(":/fd_16px.png", ":/fd_disabled_16px.png"))
+ , m_pDetailsWidget(0)
+ , m_pToolBar(0)
+ , m_pProgressBar(0)
+ , m_pSearchWidget(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+QMenu *UIMediumManagerWidget::menu() const
+{
+ return m_pActionPool->action(UIActionIndexMN_M_MediumWindow)->menu();
+}
+
+void UIMediumManagerWidget::setProgressBar(UIEnumerationProgressBar *pProgressBar)
+{
+ /* Cache progress-bar reference:*/
+ m_pProgressBar = pProgressBar;
+
+ /* Update translation: */
+ retranslateUi();
+}
+
+void UIMediumManagerWidget::retranslateUi()
+{
+ /* Adjust toolbar: */
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text. */
+ if (m_pToolBar)
+ m_pToolBar->updateLayout();
+#endif
+
+ /* Translate tab-widget: */
+ if (m_pTabWidget)
+ {
+ m_pTabWidget->setTabText(tabIndex(UIMediumDeviceType_HardDisk), UIMediumManager::tr("&Hard disks"));
+ m_pTabWidget->setTabText(tabIndex(UIMediumDeviceType_DVD), UIMediumManager::tr("&Optical disks"));
+ m_pTabWidget->setTabText(tabIndex(UIMediumDeviceType_Floppy), UIMediumManager::tr("&Floppy disks"));
+ }
+
+ /* Translate HD tree-widget: */
+ QITreeWidget *pTreeWidgetHD = treeWidget(UIMediumDeviceType_HardDisk);
+ if (pTreeWidgetHD)
+ {
+ pTreeWidgetHD->setWhatsThis(UIMediumManager::tr("Registered hard drives"));
+ pTreeWidgetHD->headerItem()->setText(0, UIMediumManager::tr("Name"));
+ pTreeWidgetHD->headerItem()->setText(1, UIMediumManager::tr("Virtual Size"));
+ pTreeWidgetHD->headerItem()->setText(2, UIMediumManager::tr("Actual Size"));
+ }
+
+ /* Translate CD tree-widget: */
+ QITreeWidget *pTreeWidgetCD = treeWidget(UIMediumDeviceType_DVD);
+ if (pTreeWidgetCD)
+ {
+ pTreeWidgetCD->setWhatsThis(UIMediumManager::tr("Registered optical disks"));
+ pTreeWidgetCD->headerItem()->setText(0, UIMediumManager::tr("Name"));
+ pTreeWidgetCD->headerItem()->setText(1, UIMediumManager::tr("Size"));
+ }
+
+ /* Translate FD tree-widget: */
+ QITreeWidget *pTreeWidgetFD = treeWidget(UIMediumDeviceType_Floppy);
+ if (pTreeWidgetFD)
+ {
+ pTreeWidgetFD->setWhatsThis(UIMediumManager::tr("Registered floppy disks"));
+ pTreeWidgetFD->headerItem()->setText(0, UIMediumManager::tr("Name"));
+ pTreeWidgetFD->headerItem()->setText(1, UIMediumManager::tr("Size"));
+ }
+
+ /* Translate progress-bar: */
+ if (m_pProgressBar)
+ {
+ m_pProgressBar->setText(UIMediumManager::tr("Checking accessibility"));
+#ifdef VBOX_WS_MAC
+ /* Make sure that the widgets aren't jumping around
+ * while the progress-bar get visible. */
+ m_pProgressBar->adjustSize();
+ //int h = m_pProgressBar->height();
+ //if (m_pButtonBox)
+ // m_pButtonBox->setMinimumHeight(h + 12);
+#endif
+ }
+
+ /* Full refresh if there is at least one item present: */
+ if ( (pTreeWidgetHD && pTreeWidgetHD->topLevelItemCount())
+ || (pTreeWidgetCD && pTreeWidgetCD->topLevelItemCount())
+ || (pTreeWidgetFD && pTreeWidgetFD->topLevelItemCount()))
+ sltRefreshAll();
+}
+
+void UIMediumManagerWidget::sltResetMediumDetailsChanges()
+{
+ /* Push the current item data into details-widget: */
+ sltHandleCurrentTabChanged();
+}
+
+void UIMediumManagerWidget::sltApplyMediumDetailsChanges()
+{
+ /* Get current medium-item: */
+ UIMediumItem *pMediumItem = currentMediumItem();
+ AssertMsgReturnVoid(pMediumItem, ("Current item must not be null"));
+ AssertReturnVoid(!pMediumItem->id().isNull());
+
+ /* Get item data: */
+ UIDataMedium oldData = *pMediumItem;
+ UIDataMedium newData = m_pDetailsWidget->data();
+
+ /* Search for corresponding medium: */
+ CMedium comMedium = uiCommon().medium(pMediumItem->id()).medium();
+
+ /* Try to assign new medium type: */
+ if ( comMedium.isOk()
+ && newData.m_options.m_enmMediumType != oldData.m_options.m_enmMediumType)
+ pMediumItem->changeMediumType(newData.m_options.m_enmMediumType);
+
+ /* Try to assign new medium description: */
+ if ( comMedium.isOk()
+ && newData.m_options.m_strDescription != oldData.m_options.m_strDescription)
+ {
+ comMedium.SetDescription(newData.m_options.m_strDescription);
+
+ /* Show error message if necessary: */
+ if (!comMedium.isOk())
+ UINotificationMessage::cannotChangeMediumParameter(comMedium);
+ }
+
+ /* Try to assign new medium location: */
+ if ( comMedium.isOk()
+ && newData.m_options.m_strLocation != oldData.m_options.m_strLocation)
+ {
+ /* Assign new medium location: */
+ UINotificationProgressMediumMove *pNotification = new UINotificationProgressMediumMove(comMedium,
+ newData.m_options.m_strLocation);
+ connect(pNotification, &UINotificationProgressMediumMove::sigProgressFinished,
+ this, &UIMediumManagerWidget::sltHandleMoveProgressFinished);
+ gpNotificationCenter->append(pNotification);
+ }
+
+ /* Try to assign new medium size: */
+ if ( comMedium.isOk()
+ && newData.m_options.m_uLogicalSize != oldData.m_options.m_uLogicalSize)
+ {
+ /* Assign new medium size: */
+ UINotificationProgressMediumResize *pNotification = new UINotificationProgressMediumResize(comMedium,
+ newData.m_options.m_uLogicalSize);
+ connect(pNotification, &UINotificationProgressMediumResize::sigProgressFinished,
+ this, &UIMediumManagerWidget::sltHandleResizeProgressFinished);
+ gpNotificationCenter->append(pNotification);
+ }
+
+ /* Recache current item: */
+ pMediumItem->refreshAll();
+
+ /* Push the current item data into details-widget: */
+ sltHandleCurrentTabChanged();
+}
+
+void UIMediumManagerWidget::sltHandleMediumCreated(const QUuid &uMediumID)
+{
+ /* Search for corresponding medium: */
+ UIMedium medium = uiCommon().medium(uMediumID);
+
+ /* Ignore non-interesting media: */
+ if (medium.isNull() || medium.isHostDrive())
+ return;
+
+ /* Ignore media (and their children) which are
+ * marked as hidden or attached to hidden machines only: */
+ if (UIMedium::isMediumAttachedToHiddenMachinesOnly(medium))
+ return;
+
+ /* Create medium-item for corresponding medium: */
+ UIMediumItem *pMediumItem = createMediumItem(medium);
+
+ /* Make sure medium-item was created: */
+ if (!pMediumItem)
+ return;
+
+ /* If medium-item change allowed and
+ * 1. medium-enumeration is not currently in progress or
+ * 2. if there is no currently medium-item selected
+ * we have to choose newly added medium-item as current one: */
+ if ( !m_fPreventChangeCurrentItem
+ && ( !uiCommon().isMediumEnumerationInProgress()
+ || !mediumItem(medium.type())))
+ setCurrentItem(treeWidget(medium.type()), pMediumItem);
+}
+
+void UIMediumManagerWidget::sltHandleMediumDeleted(const QUuid &uMediumID)
+{
+ /* Make sure corresponding medium-item deleted: */
+ deleteMediumItem(uMediumID);
+}
+
+void UIMediumManagerWidget::sltHandleMediumEnumerationStart()
+{
+ /* Disable 'refresh' action: */
+ if (m_pActionPool->action(UIActionIndexMN_M_Medium_S_Refresh))
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Refresh)->setEnabled(false);
+
+ /* Disable details-widget: */
+ if (m_pDetailsWidget)
+ m_pDetailsWidget->setOptionsEnabled(false);
+
+ /* Reset and show progress-bar: */
+ if (m_pProgressBar)
+ {
+ m_pProgressBar->setMaximum(uiCommon().mediumIDs().size());
+ m_pProgressBar->setValue(0);
+ m_pProgressBar->show();
+ }
+
+ /* Reset inaccessibility flags: */
+ m_fInaccessibleHD =
+ m_fInaccessibleCD =
+ m_fInaccessibleFD = false;
+
+ /* Reset tab-widget icons: */
+ if (m_pTabWidget)
+ {
+ m_pTabWidget->setTabIcon(tabIndex(UIMediumDeviceType_HardDisk), m_iconHD);
+ m_pTabWidget->setTabIcon(tabIndex(UIMediumDeviceType_DVD), m_iconCD);
+ m_pTabWidget->setTabIcon(tabIndex(UIMediumDeviceType_Floppy), m_iconFD);
+ }
+
+ /* Repopulate tree-widgets content: */
+ repopulateTreeWidgets();
+
+ /* Re-fetch all current medium-items: */
+ refetchCurrentMediumItems();
+ refetchCurrentChosenMediumItem();
+}
+
+void UIMediumManagerWidget::sltHandleMediumEnumerated(const QUuid &uMediumID)
+{
+ /* Search for corresponding medium: */
+ UIMedium medium = uiCommon().medium(uMediumID);
+
+ /* Ignore non-interesting media: */
+ if (medium.isNull() || medium.isHostDrive())
+ return;
+
+ /* Ignore media (and their children) which are
+ * marked as hidden or attached to hidden machines only: */
+ if (UIMedium::isMediumAttachedToHiddenMachinesOnly(medium))
+ return;
+
+ /* Update medium-item for corresponding medium: */
+ updateMediumItem(medium);
+
+ /* Advance progress-bar: */
+ if (m_pProgressBar)
+ m_pProgressBar->setValue(m_pProgressBar->value() + 1);
+}
+
+void UIMediumManagerWidget::sltHandleMediumEnumerationFinish()
+{
+ /* Hide progress-bar: */
+ if (m_pProgressBar)
+ m_pProgressBar->hide();
+
+ /* Enable details-widget: */
+ if (m_pDetailsWidget)
+ m_pDetailsWidget->setOptionsEnabled(true);
+
+ /* Enable 'refresh' action: */
+ if (m_pActionPool->action(UIActionIndexMN_M_Medium_S_Refresh))
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Refresh)->setEnabled(true);
+
+ /* Re-fetch all current medium-items: */
+ refetchCurrentMediumItems();
+ refetchCurrentChosenMediumItem();
+}
+
+void UIMediumManagerWidget::sltHandleMachineStateChange(const QUuid &uId, const KMachineState state)
+{
+ UIMediumItem *pCurrentItem = currentMediumItem();
+ if (!pCurrentItem)
+ return;
+ /* If this machine is not using the current medium then we don't care about its state: */
+ if (!pCurrentItem->isMediumAttachedTo(uId))
+ return;
+ bool fMediumIsModifiable = true;
+ if (state != KMachineState_Aborted && state != KMachineState_PoweredOff && state != KMachineState_AbortedSaved)
+ fMediumIsModifiable = false;
+ m_pDetailsWidget->enableDisableMediumModificationWidgets(fMediumIsModifiable);
+}
+
+void UIMediumManagerWidget::sltAddMedium()
+{
+ QString strDefaultMachineFolder = uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder();
+ uiCommon().openMediumWithFileOpenDialog(currentMediumType(), this,
+ strDefaultMachineFolder, true /* use most recent medium folder */);
+}
+
+void UIMediumManagerWidget::sltCreateMedium()
+{
+ uiCommon().openMediumCreatorDialog(m_pActionPool, this, currentMediumType());
+}
+
+void UIMediumManagerWidget::sltCopyMedium()
+{
+ /* Get current medium-item: */
+ UIMediumItem *pMediumItem = currentMediumItem();
+ AssertMsgReturnVoid(pMediumItem, ("Current item must not be null"));
+ AssertReturnVoid(!pMediumItem->id().isNull());
+
+ /* Copy current medium-item: */
+ //pMediumItem->copy();
+
+ /* Show Clone VD wizard: */
+ UIMedium medium = pMediumItem->medium();
+ QPointer<UINativeWizard> pWizard = new UIWizardCloneVD(currentTreeWidget(), medium.medium());
+ pWizard->exec();
+
+ /* Delete if still exists: */
+ if (pWizard)
+ delete pWizard;
+}
+
+void UIMediumManagerWidget::sltMoveMedium()
+{
+ /* Get current medium-item: */
+ UIMediumItem *pMediumItem = currentMediumItem();
+ AssertMsgReturnVoid(pMediumItem, ("Current item must not be null"));
+ AssertReturnVoid(!pMediumItem->id().isNull());
+
+ /* Copy current medium-item: */
+ pMediumItem->move();
+
+ /* Push the current item data into details-widget: */
+ sltHandleCurrentTabChanged();
+}
+
+void UIMediumManagerWidget::sltRemoveMedium()
+{
+ /* Get current medium-item: */
+ UIMediumItem *pMediumItem = currentMediumItem();
+ AssertMsgReturnVoid(pMediumItem, ("Current item must not be null"));
+ AssertReturnVoid(!pMediumItem->id().isNull());
+
+ /* Remove current medium-item: */
+ pMediumItem->remove(true /* show message box */);
+}
+
+void UIMediumManagerWidget::sltReleaseMedium()
+{
+ /* Get current medium-item: */
+ UIMediumItem *pMediumItem = currentMediumItem();
+ AssertMsgReturnVoid(pMediumItem, ("Current item must not be null"));
+ AssertReturnVoid(!pMediumItem->id().isNull());
+
+ /* Remove current medium-item: */
+ bool fResult = pMediumItem->release(true /* show message box */, false /* induced */);
+
+ /* Refetch currently chosen medium-item: */
+ if (fResult)
+ refetchCurrentChosenMediumItem();
+}
+
+void UIMediumManagerWidget::sltClear()
+{
+ /* Currently we clear only DVD medium type items: */
+ if (currentMediumType() != UIMediumDeviceType_DVD && currentMediumType() != UIMediumDeviceType_Floppy)
+ return;
+ QITreeWidget* pTreeWidget = currentTreeWidget();
+ AssertReturnVoid(pTreeWidget);
+ /* Iterate over the tree items assuming medium items are immediate children of the root and they dont have children
+ * themselves which currently holds for DVD and floppy medium types: */
+ QList<UIMediumItem*> mediumsToRemove;
+ QStringList nameList;
+ for (int i = 0; i < pTreeWidget->childCount(); ++i)
+ {
+ UIMediumItem *pMediumItem = qobject_cast<UIMediumItem*>(pTreeWidget->childItem(i));
+ if (!pMediumItem)
+ continue;
+ if (pMediumItem->state() == KMediumState_Inaccessible)
+ {
+ mediumsToRemove << pMediumItem;
+ nameList << pMediumItem->name();
+ }
+ }
+ if (!msgCenter().confirmInaccesibleMediaClear(nameList, currentMediumType(), this))
+ return;
+
+ foreach (UIMediumItem *pMediumItem, mediumsToRemove)
+ {
+ pMediumItem->release(false /* no messag box */, false /* induced */);
+ pMediumItem->remove(false /* show no message box */);
+ }
+}
+
+void UIMediumManagerWidget::sltToggleMediumDetailsVisibility(bool fVisible)
+{
+ /* Save the setting: */
+ gEDataManager->setVirtualMediaManagerDetailsExpanded(fVisible);
+ /* Toggle medium details visibility: */
+ if (m_pDetailsWidget)
+ m_pDetailsWidget->setVisible(fVisible);
+ /* Notify external lsiteners: */
+ emit sigMediumDetailsVisibilityChanged(fVisible);
+}
+
+void UIMediumManagerWidget::sltToggleMediumSearchVisibility(bool fVisible)
+{
+ /* Save the setting: */
+ gEDataManager->setVirtualMediaManagerSearchWidgetExpanded(fVisible);
+ /* Toggle medium details visibility: */
+ if (m_pSearchWidget)
+ m_pSearchWidget->setVisible(fVisible);
+}
+
+void UIMediumManagerWidget::sltRefreshAll()
+{
+ /* Restart full medium-enumeration: */
+ uiCommon().enumerateMedia();
+}
+
+void UIMediumManagerWidget::sltHandleMoveProgressFinished()
+{
+ /* Get current medium-item: */
+ UIMediumItem *pMediumItem = currentMediumItem();
+ AssertMsgReturnVoid(pMediumItem, ("Current item must not be null"));
+ AssertReturnVoid(!pMediumItem->id().isNull());
+
+ /* Recache current item: */
+ pMediumItem->refreshAll();
+
+ /* Push the current item data into details-widget: */
+ sltHandleCurrentTabChanged();
+}
+
+void UIMediumManagerWidget::sltHandleResizeProgressFinished()
+{
+ /* Get current medium-item: */
+ UIMediumItem *pMediumItem = currentMediumItem();
+ AssertMsgReturnVoid(pMediumItem, ("Current item must not be null"));
+ AssertReturnVoid(!pMediumItem->id().isNull());
+
+ /* Recache current item: */
+ pMediumItem->refreshAll();
+
+ /* Push the current item data into details-widget: */
+ sltHandleCurrentTabChanged();
+}
+
+void UIMediumManagerWidget::sltHandleCurrentTabChanged()
+{
+ /* Get current tree-widget: */
+ QITreeWidget *pTreeWidget = currentTreeWidget();
+ if (pTreeWidget)
+ {
+ /* If another tree-widget was focused before,
+ * move focus to current tree-widget: */
+ if (qobject_cast<QITreeWidget*>(focusWidget()))
+ pTreeWidget->setFocus();
+ }
+
+ /* Update action icons: */
+ updateActionIcons();
+
+ /* Raise the required information-container: */
+ if (m_pDetailsWidget)
+ m_pDetailsWidget->setCurrentType(currentMediumType());
+
+ enableClearAction();
+
+ /* Re-fetch currently chosen medium-item: */
+ refetchCurrentChosenMediumItem();
+ sltHandlePerformSearch();
+}
+
+void UIMediumManagerWidget::sltHandleCurrentItemChanged()
+{
+ /* Get sender() tree-widget: */
+ QITreeWidget *pTreeWidget = qobject_cast<QITreeWidget*>(sender());
+ AssertMsgReturnVoid(pTreeWidget, ("This slot should be called by tree-widget only!\n"));
+
+ /* Re-fetch current medium-item of required type: */
+ refetchCurrentMediumItem(mediumType(pTreeWidget));
+}
+
+void UIMediumManagerWidget::sltHandleContextMenuRequest(const QPoint &position)
+{
+ /* Get current tree-widget: */
+ QITreeWidget *pTreeWidget = currentTreeWidget();
+ AssertPtrReturnVoid(pTreeWidget);
+
+ /* If underlaying item was found => make sure that item is current one: */
+ QTreeWidgetItem *pItem = pTreeWidget->itemAt(position);
+ if (pItem)
+ setCurrentItem(pTreeWidget, pItem);
+
+ /* Compose temporary context-menu: */
+ QMenu menu;
+ if (pTreeWidget->itemAt(position))
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Copy));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Move));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Remove));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Release));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Search));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Details));
+ }
+ else
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Add));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Create));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Search));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Refresh));
+ }
+ /* And show it: */
+ menu.exec(pTreeWidget->viewport()->mapToGlobal(position));
+}
+
+void UIMediumManagerWidget::sltPerformTablesAdjustment()
+{
+ /* Get all the tree-widgets: */
+ const QList<QITreeWidget*> trees = m_trees.values();
+
+ /* Calculate deduction for every header: */
+ QList<int> deductions;
+ foreach (QITreeWidget *pTreeWidget, trees)
+ {
+ int iDeduction = 0;
+ for (int iHeaderIndex = 1; iHeaderIndex < pTreeWidget->header()->count(); ++iHeaderIndex)
+ iDeduction += pTreeWidget->header()->sectionSize(iHeaderIndex);
+ deductions << iDeduction;
+ }
+
+ /* Adjust the table's first column: */
+ for (int iTreeIndex = 0; iTreeIndex < trees.size(); ++iTreeIndex)
+ {
+ QITreeWidget *pTreeWidget = trees[iTreeIndex];
+ int iSize0 = pTreeWidget->viewport()->width() - deductions[iTreeIndex];
+ if (pTreeWidget->header()->sectionSize(0) != iSize0)
+ pTreeWidget->header()->resizeSection(0, iSize0);
+ }
+}
+
+void UIMediumManagerWidget::sltHandlePerformSearch()
+{
+ performSearch(true);
+}
+
+void UIMediumManagerWidget::sltDetachCOM()
+{
+ /* Clear tree-widgets: */
+ QITreeWidget *pTreeWidgetHD = treeWidget(UIMediumDeviceType_HardDisk);
+ if (pTreeWidgetHD)
+ pTreeWidgetHD->clear();
+ QITreeWidget *pTreeWidgetCD = treeWidget(UIMediumDeviceType_DVD);
+ if (pTreeWidgetCD)
+ pTreeWidgetCD->clear();
+ QITreeWidget *pTreeWidgetFD = treeWidget(UIMediumDeviceType_Floppy);
+ if (pTreeWidgetFD)
+ pTreeWidgetFD->clear();
+}
+
+void UIMediumManagerWidget::prepare()
+{
+ /* Prepare connections: */
+ prepareConnections();
+ /* Prepare actions: */
+ prepareActions();
+ /* Prepare widgets: */
+ prepareWidgets();
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Start full medium-enumeration (if necessary): */
+ if (!uiCommon().isFullMediumEnumerationRequested())
+ uiCommon().enumerateMedia();
+ /* Emulate medium-enumeration otherwise: */
+ else
+ {
+ /* Emulate medium-enumeration start: */
+ sltHandleMediumEnumerationStart();
+
+ /* Emulate medium-enumeration finish (if necessary): */
+ if (!uiCommon().isMediumEnumerationInProgress())
+ sltHandleMediumEnumerationFinish();
+ }
+ uiCommon().setHelpKeyword(this,"virtual-media-manager");
+}
+
+void UIMediumManagerWidget::prepareConnections()
+{
+ /* Listen to vm state changed event so that we can disable/enable widgets related to the current medium if neds be: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UIMediumManagerWidget::sltHandleMachineStateChange);
+
+ /* Configure medium-processing connections: */
+ connect(&uiCommon(), &UICommon::sigMediumCreated,
+ this, &UIMediumManagerWidget::sltHandleMediumCreated);
+ connect(&uiCommon(), &UICommon::sigMediumDeleted,
+ this, &UIMediumManagerWidget::sltHandleMediumDeleted);
+
+ /* Configure medium-enumeration connections: */
+ connect(&uiCommon(), &UICommon::sigMediumEnumerationStarted,
+ this, &UIMediumManagerWidget::sltHandleMediumEnumerationStart);
+ connect(&uiCommon(), &UICommon::sigMediumEnumerated,
+ this, &UIMediumManagerWidget::sltHandleMediumEnumerated);
+ connect(&uiCommon(), &UICommon::sigMediumEnumerationFinished,
+ this, &UIMediumManagerWidget::sltHandleMediumEnumerationFinish);
+
+ /* Configure COM related connections: */
+ connect(&uiCommon(), &UICommon::sigAskToDetachCOM,
+ this, &UIMediumManagerWidget::sltDetachCOM);
+}
+
+void UIMediumManagerWidget::prepareActions()
+{
+ /* First of all, add actions which has smaller shortcut scope: */
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Add));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Create));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Copy));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Move));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Remove));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Release));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Clear));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Search));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Details));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Refresh));
+
+ /* Connect actions: */
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Add), &QAction::triggered,
+ this, &UIMediumManagerWidget::sltAddMedium);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Create), &QAction::triggered,
+ this, &UIMediumManagerWidget::sltCreateMedium);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Copy), &QAction::triggered,
+ this, &UIMediumManagerWidget::sltCopyMedium);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Move), &QAction::triggered,
+ this, &UIMediumManagerWidget::sltMoveMedium);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Remove), &QAction::triggered,
+ this, &UIMediumManagerWidget::sltRemoveMedium);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Release), &QAction::triggered,
+ this, &UIMediumManagerWidget::sltReleaseMedium);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Clear), &QAction::triggered,
+ this, &UIMediumManagerWidget::sltClear);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Details), &QAction::toggled,
+ this, &UIMediumManagerWidget::sltToggleMediumDetailsVisibility);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Search), &QAction::toggled,
+ this, &UIMediumManagerWidget::sltToggleMediumSearchVisibility);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Refresh), &QAction::triggered,
+ this, &UIMediumManagerWidget::sltRefreshAll);
+
+ /* Update action icons: */
+ updateActionIcons();
+}
+
+void UIMediumManagerWidget::prepareWidgets()
+{
+ /* Create main-layout: */
+ new QVBoxLayout(this);
+ AssertPtrReturnVoid(layout());
+ {
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ layout()->setSpacing(10);
+#else
+ layout()->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ /* Prepare toolbar, if requested: */
+ if (m_fShowToolbar)
+ prepareToolBar();
+ /* Prepare tab-widget: */
+ prepareTabWidget();
+ /* Prepare search-widget: */
+ prepareSearchWidget();
+ /* Prepare details-widget: */
+ prepareDetailsWidget();
+ }
+}
+
+void UIMediumManagerWidget::prepareToolBar()
+{
+ /* Create toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ AssertPtrReturnVoid(m_pToolBar);
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+
+ /* Add toolbar actions: */
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Add));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Create));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Copy));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Move));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Remove));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Release));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Clear));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Search));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Details));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Medium_S_Refresh));
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+ }
+#else
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+#endif
+ }
+}
+
+void UIMediumManagerWidget::prepareTabWidget()
+{
+ /* Create tab-widget: */
+ m_pTabWidget = new QITabWidget;
+ AssertPtrReturnVoid(m_pTabWidget);
+ {
+ /* Create tabs: */
+ for (int i = 0; i < m_iTabCount; ++i)
+ prepareTab((UIMediumDeviceType)i);
+ /* Configure tab-widget: */
+ m_pTabWidget->setFocusPolicy(Qt::TabFocus);
+ m_pTabWidget->setTabIcon(tabIndex(UIMediumDeviceType_HardDisk), m_iconHD);
+ m_pTabWidget->setTabIcon(tabIndex(UIMediumDeviceType_DVD), m_iconCD);
+ m_pTabWidget->setTabIcon(tabIndex(UIMediumDeviceType_Floppy), m_iconFD);
+ connect(m_pTabWidget, &QITabWidget::currentChanged, this, &UIMediumManagerWidget::sltHandleCurrentTabChanged);
+
+ /* Add tab-widget into central layout: */
+ layout()->addWidget(m_pTabWidget);
+
+ /* Update other widgets according chosen tab: */
+ sltHandleCurrentTabChanged();
+ }
+}
+
+void UIMediumManagerWidget::prepareTab(UIMediumDeviceType type)
+{
+ /* Create tab: */
+ m_pTabWidget->addTab(new QWidget, QString());
+ QWidget *pTab = tab(type);
+ AssertPtrReturnVoid(pTab);
+ {
+ /* Create tab layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(pTab);
+ AssertPtrReturnVoid(pLayout);
+ {
+#ifdef VBOX_WS_MAC
+ /* Configure layout: */
+ pLayout->setContentsMargins(10, 10, 10, 10);
+#endif
+
+ /* Prepare tree-widget: */
+ prepareTreeWidget(type, type == UIMediumDeviceType_HardDisk ? 3 : 2);
+ }
+ }
+}
+
+void UIMediumManagerWidget::prepareTreeWidget(UIMediumDeviceType type, int iColumns)
+{
+ /* Create tree-widget: */
+ m_trees.insert(tabIndex(type), new QITreeWidget);
+ QITreeWidget *pTreeWidget = treeWidget(type);
+ AssertPtrReturnVoid(pTreeWidget);
+ {
+ /* Configure tree-widget: */
+ pTreeWidget->setExpandsOnDoubleClick(false);
+ pTreeWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ pTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ pTreeWidget->setAlternatingRowColors(true);
+ pTreeWidget->setAllColumnsShowFocus(true);
+ pTreeWidget->setAcceptDrops(true);
+ pTreeWidget->setColumnCount(iColumns);
+ pTreeWidget->sortItems(0, Qt::AscendingOrder);
+ if (iColumns > 0)
+ pTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Fixed);
+ if (iColumns > 1)
+ pTreeWidget->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+ if (iColumns > 2)
+ pTreeWidget->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
+ pTreeWidget->header()->setStretchLastSection(false);
+ pTreeWidget->setSortingEnabled(true);
+ connect(pTreeWidget, &QITreeWidget::currentItemChanged,
+ this, &UIMediumManagerWidget::sltHandleCurrentItemChanged);
+ connect(pTreeWidget, &QITreeWidget::itemDoubleClicked,
+ m_pActionPool->action(UIActionIndexMN_M_Medium_T_Details), &QAction::setChecked);
+ connect(pTreeWidget, &QITreeWidget::customContextMenuRequested,
+ this, &UIMediumManagerWidget::sltHandleContextMenuRequest);
+ connect(pTreeWidget, &QITreeWidget::resized,
+ this, &UIMediumManagerWidget::sltPerformTablesAdjustment, Qt::QueuedConnection);
+ connect(pTreeWidget->header(), &QHeaderView::sectionResized,
+ this, &UIMediumManagerWidget::sltPerformTablesAdjustment, Qt::QueuedConnection);
+ /* Add tree-widget into tab layout: */
+ tab(type)->layout()->addWidget(pTreeWidget);
+ }
+}
+
+void UIMediumManagerWidget::prepareDetailsWidget()
+{
+ /* Create details-widget: */
+ m_pDetailsWidget = new UIMediumDetailsWidget(this, m_enmEmbedding);
+ AssertPtrReturnVoid(m_pDetailsWidget);
+ {
+ /* Configure details-widget: */
+ m_pDetailsWidget->setVisible(false);
+ m_pDetailsWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ connect(m_pDetailsWidget, &UIMediumDetailsWidget::sigAcceptAllowed,
+ this, &UIMediumManagerWidget::sigAcceptAllowed);
+ connect(m_pDetailsWidget, &UIMediumDetailsWidget::sigRejectAllowed,
+ this, &UIMediumManagerWidget::sigRejectAllowed);
+ connect(m_pDetailsWidget, &UIMediumDetailsWidget::sigDataChangeRejected,
+ this, &UIMediumManagerWidget::sltResetMediumDetailsChanges);
+ connect(m_pDetailsWidget, &UIMediumDetailsWidget::sigDataChangeAccepted,
+ this, &UIMediumManagerWidget::sltApplyMediumDetailsChanges);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pDetailsWidget);
+ }
+}
+
+void UIMediumManagerWidget::prepareSearchWidget()
+{
+ m_pSearchWidget = new UIMediumSearchWidget(this);
+ AssertPtrReturnVoid(m_pSearchWidget);
+ {
+ m_pSearchWidget->setVisible(false);
+ m_pSearchWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ connect(m_pSearchWidget, &UIMediumSearchWidget::sigPerformSearch,
+ this, &UIMediumManagerWidget::sltHandlePerformSearch);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pSearchWidget);
+ }
+
+}
+
+void UIMediumManagerWidget::loadSettings()
+{
+ /* Details action/widget: */
+ m_pActionPool->action(UIActionIndexMN_M_Medium_T_Details)->setChecked(gEDataManager->virtualMediaManagerDetailsExpanded());
+ sltToggleMediumDetailsVisibility(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Details)->isChecked());
+
+ /* Search action/widget: */
+ m_pActionPool->action(UIActionIndexMN_M_Medium_T_Search)->setChecked(gEDataManager->virtualMediaManagerSearchWidgetExpanded());
+ sltToggleMediumSearchVisibility(m_pActionPool->action(UIActionIndexMN_M_Medium_T_Search)->isChecked());
+}
+
+void UIMediumManagerWidget::repopulateTreeWidgets()
+{
+ /* Remember current medium-items: */
+ if (UIMediumItem *pMediumItem = mediumItem(UIMediumDeviceType_HardDisk))
+ m_uCurrentIdHD = pMediumItem->id();
+ if (UIMediumItem *pMediumItem = mediumItem(UIMediumDeviceType_DVD))
+ m_uCurrentIdCD = pMediumItem->id();
+ if (UIMediumItem *pMediumItem = mediumItem(UIMediumDeviceType_Floppy))
+ m_uCurrentIdFD = pMediumItem->id();
+
+ /* Clear tree-widgets: */
+ QITreeWidget *pTreeWidgetHD = treeWidget(UIMediumDeviceType_HardDisk);
+ if (pTreeWidgetHD)
+ {
+ setCurrentItem(pTreeWidgetHD, 0);
+ pTreeWidgetHD->clear();
+ }
+ QITreeWidget *pTreeWidgetCD = treeWidget(UIMediumDeviceType_DVD);
+ if (pTreeWidgetCD)
+ {
+ setCurrentItem(pTreeWidgetCD, 0);
+ pTreeWidgetCD->clear();
+ }
+ QITreeWidget *pTreeWidgetFD = treeWidget(UIMediumDeviceType_Floppy);
+ if (pTreeWidgetFD)
+ {
+ setCurrentItem(pTreeWidgetFD, 0);
+ pTreeWidgetFD->clear();
+ }
+
+ /* Create medium-items (do not change current one): */
+ m_fPreventChangeCurrentItem = true;
+ foreach (const QUuid &uMediumID, uiCommon().mediumIDs())
+ sltHandleMediumCreated(uMediumID);
+ m_fPreventChangeCurrentItem = false;
+
+ /* Select first item as current one if nothing selected: */
+ if (pTreeWidgetHD && !mediumItem(UIMediumDeviceType_HardDisk))
+ if (QTreeWidgetItem *pItem = pTreeWidgetHD->topLevelItem(0))
+ setCurrentItem(pTreeWidgetHD, pItem);
+ if (pTreeWidgetCD && !mediumItem(UIMediumDeviceType_DVD))
+ if (QTreeWidgetItem *pItem = pTreeWidgetCD->topLevelItem(0))
+ setCurrentItem(pTreeWidgetCD, pItem);
+ if (pTreeWidgetFD && !mediumItem(UIMediumDeviceType_Floppy))
+ if (QTreeWidgetItem *pItem = pTreeWidgetFD->topLevelItem(0))
+ setCurrentItem(pTreeWidgetFD, pItem);
+
+ sltHandlePerformSearch();
+}
+
+void UIMediumManagerWidget::refetchCurrentMediumItem(UIMediumDeviceType type)
+{
+ /* Get corresponding medium-item: */
+ UIMediumItem *pMediumItem = mediumItem(type);
+
+#ifdef VBOX_WS_MAC
+ /* Set the file for the proxy icon: */
+ if (pMediumItem == currentMediumItem())
+ setWindowFilePath(pMediumItem ? pMediumItem->location() : QString());
+#endif /* VBOX_WS_MAC */
+
+ /* Make sure current medium-item visible: */
+ if (pMediumItem)
+ treeWidget(type)->scrollToItem(pMediumItem, QAbstractItemView::EnsureVisible);
+
+ /* Update actions: */
+ updateActions();
+
+ /* Update details-widget: */
+ if (m_pDetailsWidget)
+ {
+ m_pDetailsWidget->setData(pMediumItem ? *pMediumItem : UIDataMedium(type));
+ if (pMediumItem && currentMediumItem())
+ m_pDetailsWidget->enableDisableMediumModificationWidgets(currentMediumItem()->isMediumModifiable());
+ }
+}
+
+void UIMediumManagerWidget::refetchCurrentChosenMediumItem()
+{
+ refetchCurrentMediumItem(currentMediumType());
+}
+
+void UIMediumManagerWidget::refetchCurrentMediumItems()
+{
+ refetchCurrentMediumItem(UIMediumDeviceType_HardDisk);
+ refetchCurrentMediumItem(UIMediumDeviceType_DVD);
+ refetchCurrentMediumItem(UIMediumDeviceType_Floppy);
+}
+
+void UIMediumManagerWidget::updateActions()
+{
+ /* Get current medium-item: */
+ UIMediumItem *pMediumItem = currentMediumItem();
+
+ /* Calculate actions accessibility: */
+ bool fNotInEnumeration = !uiCommon().isMediumEnumerationInProgress();
+
+ /* Apply actions accessibility: */
+ bool fActionEnabledCopy = fNotInEnumeration && pMediumItem && checkMediumFor(pMediumItem, Action_Copy);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Copy)->setEnabled(fActionEnabledCopy);
+ bool fActionEnabledMove = fNotInEnumeration && pMediumItem && checkMediumFor(pMediumItem, Action_Edit);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Move)->setEnabled(fActionEnabledMove);
+ bool fActionEnabledRemove = fNotInEnumeration && pMediumItem && checkMediumFor(pMediumItem, Action_Remove);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Remove)->setEnabled(fActionEnabledRemove);
+ bool fActionEnabledRelease = fNotInEnumeration && pMediumItem && checkMediumFor(pMediumItem, Action_Release);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Release)->setEnabled(fActionEnabledRelease);
+ bool fActionEnabledDetails = true;
+ m_pActionPool->action(UIActionIndexMN_M_Medium_T_Details)->setEnabled(fActionEnabledDetails);
+}
+
+void UIMediumManagerWidget::updateActionIcons()
+{
+ const UIMediumDeviceType enmCurrentMediumType = currentMediumType();
+ if (enmCurrentMediumType != UIMediumDeviceType_Invalid)
+ {
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Add)->setState((int)enmCurrentMediumType);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Create)->setState((int)enmCurrentMediumType);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Copy)->setState((int)enmCurrentMediumType);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Move)->setState((int)enmCurrentMediumType);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Remove)->setState((int)enmCurrentMediumType);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Release)->setState((int)enmCurrentMediumType);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Clear)->setState((int)enmCurrentMediumType);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_T_Search)->setState((int)enmCurrentMediumType);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_T_Details)->setState((int)enmCurrentMediumType);
+ }
+}
+
+void UIMediumManagerWidget::updateTabIcons(UIMediumItem *pMediumItem, Action action)
+{
+ /* Make sure medium-item is valid: */
+ AssertReturnVoid(pMediumItem);
+
+ /* Prepare data for tab: */
+ const QIcon *pIcon = 0;
+ bool *pfInaccessible = 0;
+ const UIMediumDeviceType mediumType = pMediumItem->mediumType();
+ switch (mediumType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ pIcon = &m_iconHD;
+ pfInaccessible = &m_fInaccessibleHD;
+ break;
+ case UIMediumDeviceType_DVD:
+ pIcon = &m_iconCD;
+ pfInaccessible = &m_fInaccessibleCD;
+ break;
+ case UIMediumDeviceType_Floppy:
+ pIcon = &m_iconFD;
+ pfInaccessible = &m_fInaccessibleFD;
+ break;
+ default:
+ AssertFailed();
+ }
+ AssertReturnVoid(pIcon && pfInaccessible);
+
+ switch (action)
+ {
+ case Action_Add:
+ {
+ /* Does it change the overall state? */
+ if (*pfInaccessible || pMediumItem->state() != KMediumState_Inaccessible)
+ break; /* no */
+
+ *pfInaccessible = true;
+
+ if (m_pTabWidget)
+ m_pTabWidget->setTabIcon(tabIndex(mediumType), generalIconPool().warningIcon());
+
+ break;
+ }
+ case Action_Edit:
+ case Action_Remove:
+ {
+ bool fCheckRest = false;
+
+ if (action == Action_Edit)
+ {
+ /* Does it change the overall state? */
+ if ((*pfInaccessible && pMediumItem->state() == KMediumState_Inaccessible) ||
+ (!*pfInaccessible && pMediumItem->state() != KMediumState_Inaccessible))
+ break; /* no */
+
+ /* Is the given item in charge? */
+ if (!*pfInaccessible && pMediumItem->state() == KMediumState_Inaccessible)
+ *pfInaccessible = true; /* yes */
+ else
+ fCheckRest = true; /* no */
+ }
+ else
+ fCheckRest = true;
+
+ if (fCheckRest)
+ {
+ /* Find the first KMediumState_Inaccessible item to be in charge: */
+ CheckIfSuitableByState lookForState(KMediumState_Inaccessible);
+ CheckIfSuitableByID ignoreID(pMediumItem->id());
+ UIMediumItem *pInaccessibleMediumItem = searchItem(pMediumItem->parentTree(), lookForState, &ignoreID);
+ *pfInaccessible = !!pInaccessibleMediumItem;
+ }
+
+ if (m_pTabWidget)
+ {
+ if (*pfInaccessible)
+ m_pTabWidget->setTabIcon(tabIndex(mediumType), generalIconPool().warningIcon());
+ else
+ m_pTabWidget->setTabIcon(tabIndex(mediumType), *pIcon);
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+UIMediumItem* UIMediumManagerWidget::createMediumItem(const UIMedium &medium)
+{
+ /* Get medium type: */
+ UIMediumDeviceType type = medium.type();
+
+ /* Create medium-item: */
+ UIMediumItem *pMediumItem = 0;
+ switch (type)
+ {
+ /* Of hard-drive type: */
+ case UIMediumDeviceType_HardDisk:
+ {
+ /* Make sure corresponding tree-widget exists: */
+ QITreeWidget *pTreeWidget = treeWidget(UIMediumDeviceType_HardDisk);
+ if (pTreeWidget)
+ {
+ /* Recursively create hard-drive item: */
+ pMediumItem = createHardDiskItem(medium);
+ /* Make sure item was created: */
+ if (!pMediumItem)
+ break;
+ if (pMediumItem->id() == m_uCurrentIdHD)
+ {
+ setCurrentItem(pTreeWidget, pMediumItem);
+ m_uCurrentIdHD = QUuid();
+ }
+ }
+ break;
+ }
+ /* Of optical-image type: */
+ case UIMediumDeviceType_DVD:
+ {
+ /* Make sure corresponding tree-widget exists: */
+ QITreeWidget *pTreeWidget = treeWidget(UIMediumDeviceType_DVD);
+ if (pTreeWidget)
+ {
+ /* Create optical-disk item: */
+ pMediumItem = new UIMediumItemCD(medium, pTreeWidget);
+ /* Make sure item was created: */
+ if (!pMediumItem)
+ break;
+ LogRel2(("UIMediumManager: Optical medium-item with ID={%s} created.\n", medium.id().toString().toUtf8().constData()));
+ if (pMediumItem->id() == m_uCurrentIdCD)
+ {
+ setCurrentItem(pTreeWidget, pMediumItem);
+ m_uCurrentIdCD = QUuid();
+ }
+ }
+ break;
+ }
+ /* Of floppy-image type: */
+ case UIMediumDeviceType_Floppy:
+ {
+ /* Make sure corresponding tree-widget exists: */
+ QITreeWidget *pTreeWidget = treeWidget(UIMediumDeviceType_Floppy);
+ if (pTreeWidget)
+ {
+ /* Create floppy-disk item: */
+ pMediumItem = new UIMediumItemFD(medium, pTreeWidget);
+ /* Make sure item was created: */
+ if (!pMediumItem)
+ break;
+ LogRel2(("UIMediumManager: Floppy medium-item with ID={%s} created.\n", medium.id().toString().toUtf8().constData()));
+ if (pMediumItem->id() == m_uCurrentIdFD)
+ {
+ setCurrentItem(pTreeWidget, pMediumItem);
+ m_uCurrentIdFD = QUuid();
+ }
+ }
+ break;
+ }
+ default: AssertMsgFailed(("Medium-type unknown: %d\n", type)); break;
+ }
+
+ /* Make sure item was created: */
+ if (!pMediumItem)
+ return 0;
+
+ /* Update tab-icons: */
+ updateTabIcons(pMediumItem, Action_Add);
+
+ /* Toogle enable/disable of clear action: */
+ enableClearAction();
+
+ /* Reperform the medium search (don't jump to the found element): */
+ performSearch(false);
+
+ /* Re-fetch medium-item if it is current one created: */
+ if (pMediumItem == mediumItem(type))
+ refetchCurrentMediumItem(type);
+
+ /* Return created medium-item: */
+ return pMediumItem;
+}
+
+UIMediumItem* UIMediumManagerWidget::createHardDiskItem(const UIMedium &medium)
+{
+ /* Make sure passed medium is valid: */
+ AssertReturn(!medium.medium().isNull(), 0);
+
+ /* Make sure corresponding tree-widget exists: */
+ QITreeWidget *pTreeWidget = treeWidget(UIMediumDeviceType_HardDisk);
+ if (pTreeWidget)
+ {
+ /* Search for existing medium-item: */
+ UIMediumItem *pMediumItem = searchItem(pTreeWidget, CheckIfSuitableByID(medium.id()));
+
+ /* If medium-item do not exists: */
+ if (!pMediumItem)
+ {
+ /* If medium have a parent: */
+ if (medium.parentID() != UIMedium::nullID())
+ {
+ /* Try to find parent medium-item: */
+ UIMediumItem *pParentMediumItem = searchItem(pTreeWidget, CheckIfSuitableByID(medium.parentID()));
+ /* If parent medium-item was not found: */
+ if (!pParentMediumItem)
+ {
+ /* Make sure corresponding parent medium is already cached! */
+ UIMedium parentMedium = uiCommon().medium(medium.parentID());
+ if (parentMedium.isNull())
+ AssertMsgFailed(("Parent medium with ID={%s} was not found!\n", medium.parentID().toString().toUtf8().constData()));
+ /* Try to create parent medium-item: */
+ else
+ pParentMediumItem = createHardDiskItem(parentMedium);
+ }
+ /* If parent medium-item was found: */
+ if (pParentMediumItem)
+ {
+ pMediumItem = new UIMediumItemHD(medium, pParentMediumItem);
+ LogRel2(("UIMediumManager: Child hard-disk medium-item with ID={%s} created.\n", medium.id().toString().toUtf8().constData()));
+ }
+ }
+ /* Else just create item as top-level one: */
+ if (!pMediumItem)
+ {
+ pMediumItem = new UIMediumItemHD(medium, pTreeWidget);
+ LogRel2(("UIMediumManager: Root hard-disk medium-item with ID={%s} created.\n", medium.id().toString().toUtf8().constData()));
+ }
+ }
+
+ /* Return created medium-item: */
+ return pMediumItem;
+ }
+
+ /* Return null by default: */
+ return 0;
+}
+
+void UIMediumManagerWidget::updateMediumItem(const UIMedium &medium)
+{
+ /* Get medium type: */
+ UIMediumDeviceType type = medium.type();
+
+ /* Search for existing medium-item: */
+ UIMediumItem *pMediumItem = searchItem(treeWidget(type), CheckIfSuitableByID(medium.id()));
+
+ /* Create item if doesn't exists: */
+ if (!pMediumItem)
+ pMediumItem = createMediumItem(medium);
+
+ /* Make sure item was created: */
+ if (!pMediumItem)
+ return;
+
+ /* Update medium-item: */
+ pMediumItem->setMedium(medium);
+ LogRel2(("UIMediumManager: Medium-item with ID={%s} updated.\n", medium.id().toString().toUtf8().constData()));
+
+ /* Update tab-icons: */
+ updateTabIcons(pMediumItem, Action_Edit);
+
+ /* Toogle enable/disable of clear action: */
+ enableClearAction();
+
+ /* Re-fetch medium-item if it is current one updated: */
+ if (pMediumItem == mediumItem(type))
+ refetchCurrentMediumItem(type);
+
+ /* Update all the children recursively as well: */
+ foreach(const QUuid &uMediumId, uiCommon().mediumIDs())
+ {
+ UIMedium guiMedium = uiCommon().medium(uMediumId);
+ if ( !guiMedium.isNull()
+ && guiMedium.parentID() == medium.id())
+ updateMediumItem(guiMedium);
+ }
+}
+
+void UIMediumManagerWidget::deleteMediumItem(const QUuid &uMediumID)
+{
+ /* Search for corresponding tree-widget: */
+ QList<UIMediumDeviceType> types;
+ types << UIMediumDeviceType_HardDisk << UIMediumDeviceType_DVD << UIMediumDeviceType_Floppy;
+ QITreeWidget *pTreeWidget = 0;
+ UIMediumItem *pMediumItem = 0;
+ foreach (UIMediumDeviceType type, types)
+ {
+ /* Get iterated tree-widget: */
+ pTreeWidget = treeWidget(type);
+ /* Search for existing medium-item: */
+ pMediumItem = searchItem(pTreeWidget, CheckIfSuitableByID(uMediumID));
+ if (pMediumItem)
+ break;
+ }
+
+ /* Make sure item was found: */
+ if (!pMediumItem)
+ return;
+
+ /* Update tab-icons: */
+ updateTabIcons(pMediumItem, Action_Remove);
+
+ /* Toogle enable/disable of clear action: */
+ enableClearAction();
+
+ /* Delete medium-item: */
+ delete pMediumItem;
+ LogRel2(("UIMediumManager: Medium-item with ID={%s} deleted.\n", uMediumID.toString().toUtf8().constData()));
+
+ /* Reperform the medium search (don't jump to the found element): */
+ performSearch(false);
+
+ /* If there is no current medium-item now selected
+ * we have to choose first-available medium-item as current one: */
+ if (!pTreeWidget->currentItem())
+ setCurrentItem(pTreeWidget, pTreeWidget->topLevelItem(0));
+}
+
+QWidget* UIMediumManagerWidget::tab(UIMediumDeviceType type) const
+{
+ /* Determine tab index for passed medium type: */
+ int iIndex = tabIndex(type);
+
+ /* Return tab for known tab index: */
+ if (iIndex >= 0 && iIndex < m_iTabCount)
+ return iIndex < m_pTabWidget->count() ? m_pTabWidget->widget(iIndex) : 0;
+
+ /* Null by default: */
+ return 0;
+}
+
+QITreeWidget* UIMediumManagerWidget::treeWidget(UIMediumDeviceType type) const
+{
+ /* Determine tab index for passed medium type: */
+ int iIndex = tabIndex(type);
+
+ /* Return tree-widget for known tab index: */
+ if (iIndex >= 0 && iIndex < m_iTabCount)
+ return m_trees.value(iIndex, 0);
+
+ /* Null by default: */
+ return 0;
+}
+
+UIMediumItem* UIMediumManagerWidget::mediumItem(UIMediumDeviceType type) const
+{
+ /* Get corresponding tree-widget: */
+ QITreeWidget *pTreeWidget = treeWidget(type);
+ /* Return corresponding medium-item: */
+ return pTreeWidget ? toMediumItem(pTreeWidget->currentItem()) : 0;
+}
+
+UIMediumDeviceType UIMediumManagerWidget::mediumType(QITreeWidget *pTreeWidget) const
+{
+ /* Determine tab index of passed tree-widget: */
+ int iIndex = m_trees.key(pTreeWidget, -1);
+
+ /* Return medium type for known tab index: */
+ if (iIndex >= 0 && iIndex < m_iTabCount)
+ return (UIMediumDeviceType)iIndex;
+
+ /* Invalid by default: */
+ AssertFailedReturn(UIMediumDeviceType_Invalid);
+}
+
+UIMediumDeviceType UIMediumManagerWidget::currentMediumType() const
+{
+ /* Invalid if tab-widget doesn't exists: */
+ if (!m_pTabWidget)
+ return UIMediumDeviceType_Invalid;
+
+ /* Return current medium type: */
+ return (UIMediumDeviceType)m_pTabWidget->currentIndex();
+}
+
+QITreeWidget* UIMediumManagerWidget::currentTreeWidget() const
+{
+ /* Return current tree-widget: */
+ return treeWidget(currentMediumType());
+}
+
+UIMediumItem* UIMediumManagerWidget::currentMediumItem() const
+{
+ /* Return current medium-item: */
+ return mediumItem(currentMediumType());
+}
+
+void UIMediumManagerWidget::setCurrentItem(QITreeWidget *pTreeWidget, QTreeWidgetItem *pItem)
+{
+ /* Make sure passed tree-widget is valid: */
+ AssertPtrReturnVoid(pTreeWidget);
+
+ /* Make passed item current for passed tree-widget: */
+ pTreeWidget->setCurrentItem(pItem);
+
+ /* If non NULL item was passed: */
+ if (pItem)
+ {
+ /* Make sure it's also selected, and visible: */
+ pItem->setSelected(true);
+ pTreeWidget->scrollToItem(pItem, QAbstractItemView::EnsureVisible);
+ }
+
+ /* Re-fetch currently chosen medium-item: */
+ refetchCurrentChosenMediumItem();
+}
+
+void UIMediumManagerWidget::enableClearAction()
+{
+ if (!m_pActionPool || !m_pActionPool->action(UIActionIndexMN_M_Medium_S_Clear))
+ return;
+
+ if (currentMediumType() == UIMediumDeviceType_HardDisk)
+ {
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Clear)->setVisible(false);
+ return;
+ }
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Clear)->setVisible(true);
+ bool fEnable = ((currentMediumType() == UIMediumDeviceType_DVD) && m_fInaccessibleCD) ||
+ ((currentMediumType() == UIMediumDeviceType_Floppy) && m_fInaccessibleFD);
+ m_pActionPool->action(UIActionIndexMN_M_Medium_S_Clear)->setEnabled(fEnable);
+}
+
+void UIMediumManagerWidget::performSearch(bool fSelectNext)
+{
+ if (!m_pSearchWidget || !m_pTabWidget)
+ return;
+
+ QITreeWidget *pTreeWidget = treeWidget(static_cast<UIMediumDeviceType>(m_pTabWidget->currentIndex()));
+ if (!pTreeWidget)
+ return;
+ m_pSearchWidget->search(pTreeWidget, fSelectNext);
+}
+
+/* static */
+int UIMediumManagerWidget::tabIndex(UIMediumDeviceType type)
+{
+ /* Return tab index corresponding to known medium type: */
+ switch (type)
+ {
+ case UIMediumDeviceType_HardDisk: return 0;
+ case UIMediumDeviceType_DVD: return 1;
+ case UIMediumDeviceType_Floppy: return 2;
+ default: break;
+ }
+
+ /* -1 by default: */
+ return -1;
+}
+
+/* static */
+UIMediumItem* UIMediumManagerWidget::searchItem(QITreeWidget *pTreeWidget, const CheckIfSuitableBy &condition, CheckIfSuitableBy *pException)
+{
+ /* Make sure argument is valid: */
+ if (!pTreeWidget)
+ return 0;
+
+ /* Return wrapper: */
+ return searchItem(pTreeWidget->invisibleRootItem(), condition, pException);
+}
+
+/* static */
+UIMediumItem* UIMediumManagerWidget::searchItem(QTreeWidgetItem *pParentItem, const CheckIfSuitableBy &condition, CheckIfSuitableBy *pException)
+{
+ /* Make sure argument is valid: */
+ if (!pParentItem)
+ return 0;
+
+ /* Verify passed item if it is of 'medium' type too: */
+ if (UIMediumItem *pMediumParentItem = toMediumItem(pParentItem))
+ if ( condition.isItSuitable(pMediumParentItem)
+ && (!pException || !pException->isItSuitable(pMediumParentItem)))
+ return pMediumParentItem;
+
+ /* Iterate other all the children: */
+ for (int iChildIndex = 0; iChildIndex < pParentItem->childCount(); ++iChildIndex)
+ if (UIMediumItem *pMediumChildItem = toMediumItem(pParentItem->child(iChildIndex)))
+ if (UIMediumItem *pRequiredMediumChildItem = searchItem(pMediumChildItem, condition, pException))
+ return pRequiredMediumChildItem;
+
+ /* Null by default: */
+ return 0;
+}
+
+/* static */
+bool UIMediumManagerWidget::checkMediumFor(UIMediumItem *pItem, Action action)
+{
+ /* Make sure passed ID is valid: */
+ AssertReturn(pItem, false);
+
+ switch (action)
+ {
+ case Action_Edit:
+ {
+ /* Edit means changing the description and alike; any media that is
+ * not being read to or written from can be altered in these terms. */
+ switch (pItem->state())
+ {
+ case KMediumState_NotCreated:
+ case KMediumState_Inaccessible:
+ case KMediumState_LockedRead:
+ case KMediumState_LockedWrite:
+ return false;
+ default:
+ break;
+ }
+ return true;
+ }
+ case Action_Copy:
+ {
+ return true;
+ }
+ case Action_Remove:
+ {
+ /* Removable if not attached to anything: */
+ return !pItem->isUsed();
+ }
+ case Action_Release:
+ {
+ /* Releasable if attached but not in snapshots: */
+ return pItem->isUsed() && !pItem->isUsedInSnapshots();
+ }
+
+ default:
+ break;
+ }
+
+ AssertFailedReturn(false);
+}
+
+/* static */
+UIMediumItem* UIMediumManagerWidget::toMediumItem(QTreeWidgetItem *pItem)
+{
+ /* Cast passed QTreeWidgetItem to UIMediumItem if possible: */
+ return pItem && pItem->type() == QITreeWidgetItem::ItemType ? static_cast<UIMediumItem*>(pItem) : 0;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMediumManagerFactory implementation. *
+*********************************************************************************************************************************/
+
+UIMediumManagerFactory::UIMediumManagerFactory(UIActionPool *pActionPool /* = 0 */)
+ : m_pActionPool(pActionPool)
+{
+}
+
+void UIMediumManagerFactory::create(QIManagerDialog *&pDialog, QWidget *pCenterWidget)
+{
+ pDialog = new UIMediumManager(pCenterWidget, m_pActionPool);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMediumManager implementation. *
+*********************************************************************************************************************************/
+
+UIMediumManager::UIMediumManager(QWidget *pCenterWidget, UIActionPool *pActionPool)
+ : QIWithRetranslateUI<QIManagerDialog>(pCenterWidget)
+ , m_pActionPool(pActionPool)
+ , m_pProgressBar(0)
+{
+}
+
+void UIMediumManager::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+ /* Disable buttons first of all: */
+ button(ButtonType_Reset)->setEnabled(false);
+ button(ButtonType_Apply)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if (pButton == button(ButtonType_Reset))
+ emit sigDataChangeRejected();
+ else
+ if (pButton == button(ButtonType_Apply))
+ emit sigDataChangeAccepted();
+}
+
+void UIMediumManager::retranslateUi()
+{
+ /* Translate window title: */
+ setWindowTitle(tr("Virtual Media Manager"));
+
+ /* Translate buttons: */
+ button(ButtonType_Reset)->setText(tr("Reset"));
+ button(ButtonType_Apply)->setText(tr("Apply"));
+ button(ButtonType_Close)->setText(tr("Close"));
+ button(ButtonType_Help)->setText(tr("Help"));
+ button(ButtonType_Reset)->setStatusTip(tr("Reset changes in current medium details"));
+ button(ButtonType_Apply)->setStatusTip(tr("Apply changes in current medium details"));
+ button(ButtonType_Close)->setStatusTip(tr("Close dialog without saving"));
+ button(ButtonType_Help)->setStatusTip(tr("Show dialog help"));
+ button(ButtonType_Reset)->setShortcut(QString("Ctrl+Backspace"));
+ button(ButtonType_Apply)->setShortcut(QString("Ctrl+Return"));
+ button(ButtonType_Close)->setShortcut(Qt::Key_Escape);
+ button(ButtonType_Help)->setShortcut(QKeySequence::HelpContents);
+ button(ButtonType_Reset)->setToolTip(tr("Reset Changes (%1)").arg(button(ButtonType_Reset)->shortcut().toString()));
+ button(ButtonType_Apply)->setToolTip(tr("Apply Changes (%1)").arg(button(ButtonType_Apply)->shortcut().toString()));
+ button(ButtonType_Close)->setToolTip(tr("Close Window (%1)").arg(button(ButtonType_Close)->shortcut().toString()));
+ button(ButtonType_Help)->setToolTip(tr("Show Help (%1)").arg(button(ButtonType_Help)->shortcut().toString()));
+}
+
+void UIMediumManager::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/media_manager_32px.png", ":/media_manager_16px.png"));
+#endif
+}
+
+void UIMediumManager::configureCentralWidget()
+{
+ /* Create widget: */
+ UIMediumManagerWidget *pWidget = new UIMediumManagerWidget(EmbedTo_Dialog, m_pActionPool, true, this);
+ AssertPtrReturnVoid(pWidget);
+ {
+ /* Configure widget: */
+ setWidget(pWidget);
+ setWidgetMenu(pWidget->menu());
+#ifdef VBOX_WS_MAC
+ setWidgetToolbar(pWidget->toolbar());
+#endif
+ connect(this, &UIMediumManager::sigDataChangeRejected,
+ pWidget, &UIMediumManagerWidget::sltResetMediumDetailsChanges);
+ connect(this, &UIMediumManager::sigDataChangeAccepted,
+ pWidget, &UIMediumManagerWidget::sltApplyMediumDetailsChanges);
+
+ /* Add into layout: */
+ centralWidget()->layout()->addWidget(pWidget);
+ }
+}
+
+void UIMediumManager::configureButtonBox()
+{
+ /* Configure button-box: */
+ connect(widget(), &UIMediumManagerWidget::sigMediumDetailsVisibilityChanged,
+ button(ButtonType_Apply), &QPushButton::setVisible);
+ connect(widget(), &UIMediumManagerWidget::sigMediumDetailsVisibilityChanged,
+ button(ButtonType_Reset), &QPushButton::setVisible);
+ connect(widget(), &UIMediumManagerWidget::sigAcceptAllowed,
+ button(ButtonType_Apply), &QPushButton::setEnabled);
+ connect(widget(), &UIMediumManagerWidget::sigRejectAllowed,
+ button(ButtonType_Reset), &QPushButton::setEnabled);
+ connect(buttonBox(), &QIDialogButtonBox::clicked,
+ this, &UIMediumManager::sltHandleButtonBoxClick);
+ // WORKAROUND:
+ // Since we connected signals later than extra-data loaded
+ // for signals above, we should handle that stuff here again:
+ button(ButtonType_Apply)->setVisible(gEDataManager->virtualMediaManagerDetailsExpanded());
+ button(ButtonType_Reset)->setVisible(gEDataManager->virtualMediaManagerDetailsExpanded());
+
+ /* Create progress-bar: */
+ m_pProgressBar = new UIEnumerationProgressBar;
+ AssertPtrReturnVoid(m_pProgressBar);
+ {
+ /* Configure progress-bar: */
+ m_pProgressBar->hide();
+ /* Add progress-bar into button-box layout: */
+ buttonBox()->addExtraWidget(m_pProgressBar);
+ /* Notify widget it has progress-bar: */
+ widget()->setProgressBar(m_pProgressBar);
+ }
+}
+
+void UIMediumManager::finalize()
+{
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIMediumManagerWidget *UIMediumManager::widget()
+{
+ return qobject_cast<UIMediumManagerWidget*>(QIManagerDialog::widget());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumManager.h b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumManager.h
new file mode 100644
index 00000000..8dd605ff
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumManager.h
@@ -0,0 +1,499 @@
+/* $Id: UIMediumManager.h $ */
+/** @file
+ * VBox Qt GUI - UIMediumManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_UIMediumManager_h
+#define FEQT_INCLUDED_SRC_medium_UIMediumManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIMediumDefs.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QLabel;
+class QProgressBar;
+class QTabWidget;
+class QTreeWidgetItem;
+class QIDialogButtonBox;
+class QILabel;
+class QITreeWidget;
+class UIActionPool;
+class UIMedium;
+class UIMediumDetailsWidget;
+class UIMediumItem;
+class UIMediumSearchWidget;
+class QIToolBar;
+
+
+/** Functor interface allowing to check if passed UIMediumItem is suitable. */
+class CheckIfSuitableBy
+{
+public:
+
+ /** Destructs functor. */
+ virtual ~CheckIfSuitableBy() { /* Makes MSC happy. */ }
+
+ /** Determines whether passed @a pItem is suitable. */
+ virtual bool isItSuitable(UIMediumItem *pItem) const = 0;
+};
+
+
+/** Medium manager progress-bar.
+ * Reflects medium-enumeration progress, stays hidden otherwise. */
+class UIEnumerationProgressBar : public QWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor on the basis of passed @a pParent. */
+ UIEnumerationProgressBar(QWidget *pParent = 0);
+
+ /** Defines progress-bar label-text. */
+ void setText(const QString &strText);
+
+ /** Returns progress-bar current-value. */
+ int value() const;
+ /** Defines progress-bar current-value. */
+ void setValue(int iValue);
+ /** Defines progress-bar maximum-value. */
+ void setMaximum(int iValue);
+
+private:
+
+ /** Prepares progress-bar content. */
+ void prepare();
+
+ /** Progress-bar label. */
+ QLabel *m_pLabel;
+ /** Progress-bar itself. */
+ QProgressBar *m_pProgressBar;
+};
+
+
+/** QWidget extension providing GUI with the pane to control media related functionality. */
+class UIMediumManagerWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+ /** Item action types. */
+ enum Action { Action_Add, Action_Edit, Action_Copy, Action_Remove, Action_Release };
+
+signals:
+
+ /** Notifies listeners about medium details-widget @a fVisible. */
+ void sigMediumDetailsVisibilityChanged(bool fVisible);
+ /** Notifies listeners about accept is @a fAllowed. */
+ void sigAcceptAllowed(bool fAllowed);
+ /** Notifies listeners about reject is @a fAllowed. */
+ void sigRejectAllowed(bool fAllowed);
+
+public:
+
+ /** Constructs Virtual Media Manager widget.
+ * @param enmEmbedding Brings the type of widget embedding.
+ * @param pActionPool Brings the action-pool reference.
+ * @param fShowToolbar Brings whether we should create/show toolbar. */
+ UIMediumManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool, bool fShowToolbar = true, QWidget *pParent = 0);
+
+ /** Returns the menu. */
+ QMenu *menu() const;
+
+#ifdef VBOX_WS_MAC
+ /** Returns the toolbar. */
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+ /** Defines @a pProgressBar reference. */
+ void setProgressBar(UIEnumerationProgressBar *pProgressBar);
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+public slots:
+
+ /** @name Details-widget stuff.
+ * @{ */
+ /** Handles command to reset medium details changes. */
+ void sltResetMediumDetailsChanges();
+ /** Handles command to apply medium details changes. */
+ void sltApplyMediumDetailsChanges();
+ /** @} */
+
+private slots:
+
+ /** @name Medium operation stuff.
+ * @{ */
+ /** Handles UICommon::sigMediumCreated signal. */
+ void sltHandleMediumCreated(const QUuid &uMediumID);
+ /** Handles UICommon::sigMediumDeleted signal. */
+ void sltHandleMediumDeleted(const QUuid &uMediumID);
+ /** @} */
+
+ /** @name Medium enumeration stuff.
+ * @{ */
+ /** Handles UICommon::sigMediumEnumerationStarted signal. */
+ void sltHandleMediumEnumerationStart();
+ /** Handles UICommon::sigMediumEnumerated signal. */
+ void sltHandleMediumEnumerated(const QUuid &uMediumID);
+ /** Handles UICommon::sigMediumEnumerationFinished signal. */
+ void sltHandleMediumEnumerationFinish();
+ void sltHandleMachineStateChange(const QUuid &uId, const KMachineState state);
+ /** @} */
+
+ /** @name Menu/action stuff.
+ * @{ */
+ /** Handles command to add medium. */
+ void sltAddMedium();
+ /** Handles command to create medium. */
+ void sltCreateMedium();
+ /** Handles command to copy medium. */
+ void sltCopyMedium();
+ /** Handles command to move medium. */
+ void sltMoveMedium();
+ /** Handles command to remove medium. */
+ void sltRemoveMedium();
+ /** Handles command to release medium. */
+ void sltReleaseMedium();
+ /** Removes all inaccessible media. */
+ void sltClear();
+ /** Handles command to make medium details @a fVisible. */
+ void sltToggleMediumDetailsVisibility(bool fVisible);
+ /** Handles command to make medium search pane @a fVisible. */
+ void sltToggleMediumSearchVisibility(bool fVisible);
+ /** Handles command to refresh medium. */
+ void sltRefreshAll();
+ /** @} */
+
+ /** @name Menu/action handler stuff.
+ * @{ */
+ /** Handles medium move progress finished signal. */
+ void sltHandleMoveProgressFinished();
+ /** Handles medium resize progress finished signal. */
+ void sltHandleResizeProgressFinished();
+ /** @} */
+
+ /** @name Tab-widget stuff.
+ * @{ */
+ /** Handles tab change case. */
+ void sltHandleCurrentTabChanged();
+ /** Handles item change case. */
+ void sltHandleCurrentItemChanged();
+ /** Handles item context-menu-call case. */
+ void sltHandleContextMenuRequest(const QPoint &position);
+ /** @} */
+
+ /** @name Tree-widget stuff.
+ * @{ */
+ /** Adjusts tree-widgets according content. */
+ void sltPerformTablesAdjustment();
+ /** @} */
+
+ /** @name Medium search stuff.
+ * @{ */
+ /** Adjusts tree-widgets according content. */
+ void sltHandlePerformSearch();
+ /** @} */
+
+ /** @name Medium search stuff.
+ * @{ */
+ /** Handles command to detach COM stuff. */
+ void sltDetachCOM();
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Prepares actions. */
+ void prepareActions();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares toolbar. */
+ void prepareToolBar();
+ /** Prepares tab-widget. */
+ void prepareTabWidget();
+ /** Prepares tab-widget's tab. */
+ void prepareTab(UIMediumDeviceType type);
+ /** Prepares tab-widget's tree-widget. */
+ void prepareTreeWidget(UIMediumDeviceType type, int iColumns);
+ /** Prepares details-widget. */
+ void prepareDetailsWidget();
+ /** Prepares search-widget. */
+ void prepareSearchWidget();
+ /** Load settings: */
+ void loadSettings();
+
+ /** Repopulates tree-widgets content. */
+ void repopulateTreeWidgets();
+
+ /** Updates details according latest changes in current item of passed @a type. */
+ void refetchCurrentMediumItem(UIMediumDeviceType type);
+ /** Updates details according latest changes in current item of chosen type. */
+ void refetchCurrentChosenMediumItem();
+ /** Updates details according latest changes in all current items. */
+ void refetchCurrentMediumItems();
+
+ /** Updates actions according currently chosen item. */
+ void updateActions();
+ /** Updates action icons according currently chosen tab. */
+ void updateActionIcons();
+ /** Updates tab icons according last @a action happened with @a pItem. */
+ void updateTabIcons(UIMediumItem *pItem, Action action);
+ /** @} */
+
+ /** @name Widget operation stuff.
+ * @{ */
+ /** Creates UIMediumItem for corresponding @a medium. */
+ UIMediumItem *createMediumItem(const UIMedium &medium);
+ /** Creates UIMediumItemHD for corresponding @a medium. */
+ UIMediumItem *createHardDiskItem(const UIMedium &medium);
+ /** Updates UIMediumItem for corresponding @a medium. */
+ void updateMediumItem(const UIMedium &medium);
+ /** Deletes UIMediumItem for corresponding @a uMediumID. */
+ void deleteMediumItem(const QUuid &uMediumID);
+
+ /** Returns tab for passed medium @a type. */
+ QWidget *tab(UIMediumDeviceType type) const;
+ /** Returns tree-widget for passed medium @a type. */
+ QITreeWidget *treeWidget(UIMediumDeviceType type) const;
+ /** Returns item for passed medium @a type. */
+ UIMediumItem *mediumItem(UIMediumDeviceType type) const;
+
+ /** Returns medium type for passed @a pTreeWidget. */
+ UIMediumDeviceType mediumType(QITreeWidget *pTreeWidget) const;
+
+ /** Returns current medium type. */
+ UIMediumDeviceType currentMediumType() const;
+ /** Returns current tree-widget. */
+ QITreeWidget *currentTreeWidget() const;
+ /** Returns current item. */
+ UIMediumItem *currentMediumItem() const;
+
+ /** Defines current item for passed @a pTreeWidget as @a pItem. */
+ void setCurrentItem(QITreeWidget *pTreeWidget, QTreeWidgetItem *pItem);
+
+ void enableClearAction();
+ /** @} */
+
+ /** @name Search stuff.
+ * @{ */
+ /** Calls the UIMediumSearchWidget::search(..). */
+ void performSearch(bool fSelectNext);
+ /** @} */
+
+ /** @name Helper stuff.
+ * @{ */
+ /** Returns tab index for passed UIMediumDeviceType. */
+ static int tabIndex(UIMediumDeviceType type);
+
+ /** Performs search for the @a pTree child which corresponds to the @a condition but not @a pException. */
+ static UIMediumItem *searchItem(QITreeWidget *pTree,
+ const CheckIfSuitableBy &condition,
+ CheckIfSuitableBy *pException = 0);
+ /** Performs search for the @a pParentItem child which corresponds to the @a condition but not @a pException. */
+ static UIMediumItem *searchItem(QTreeWidgetItem *pParentItem,
+ const CheckIfSuitableBy &condition,
+ CheckIfSuitableBy *pException = 0);
+
+
+ /** Checks if @a action can be used for @a pItem. */
+ static bool checkMediumFor(UIMediumItem *pItem, Action action);
+
+ /** Casts passed QTreeWidgetItem @a pItem to UIMediumItem if possible. */
+ static UIMediumItem *toMediumItem(QTreeWidgetItem *pItem);
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** Holds whether we should create/show toolbar. */
+ const bool m_fShowToolbar;
+
+ /** Holds whether Virtual Media Manager should preserve current item change. */
+ bool m_fPreventChangeCurrentItem;
+ /** @} */
+
+ /** @name Tab-widget variables.
+ * @{ */
+ /** Holds the tab-widget instance. */
+ QTabWidget *m_pTabWidget;
+ /** Holds the tab-widget tab-count. */
+ const int m_iTabCount;
+ /** Holds the map of tree-widget instances. */
+ QMap<int, QITreeWidget*> m_trees;
+ /** Holds whether hard-drive tab-widget have inaccessible item. */
+ bool m_fInaccessibleHD;
+ /** Holds whether optical-disk tab-widget have inaccessible item. */
+ bool m_fInaccessibleCD;
+ /** Holds whether floppy-disk tab-widget have inaccessible item. */
+ bool m_fInaccessibleFD;
+ /** Holds cached hard-drive tab-widget icon. */
+ const QIcon m_iconHD;
+ /** Holds cached optical-disk tab-widget icon. */
+ const QIcon m_iconCD;
+ /** Holds cached floppy-disk tab-widget icon. */
+ const QIcon m_iconFD;
+ /** Holds current hard-drive tree-view item ID. */
+ QUuid m_uCurrentIdHD;
+ /** Holds current optical-disk tree-view item ID. */
+ QUuid m_uCurrentIdCD;
+ /** Holds current floppy-disk tree-view item ID. */
+ QUuid m_uCurrentIdFD;
+ /** @} */
+
+ /** @name Details-widget variables.
+ * @{ */
+ /** Holds the medium details-widget instance. */
+ UIMediumDetailsWidget *m_pDetailsWidget;
+ /** @} */
+
+ /** @name Toolbar and menu variables.
+ * @{ */
+ /** Holds the toolbar widget instance. */
+ QIToolBar *m_pToolBar;
+ /** @} */
+
+ /** @name Progress-bar variables.
+ * @{ */
+ /** Holds the progress-bar widget reference. */
+ UIEnumerationProgressBar *m_pProgressBar;
+ /** @} */
+
+ /** @name Search-widget variables.
+ * @{ */
+ /** Holds the medium details-widget instance. */
+ UIMediumSearchWidget *m_pSearchWidget;
+ /** @} */
+
+};
+
+
+/** QIManagerDialogFactory extension used as a factory for Virtual Media Manager dialog. */
+class UIMediumManagerFactory : public QIManagerDialogFactory
+{
+public:
+
+ /** Constructs Media Manager factory acquiring additional arguments.
+ * @param pActionPool Brings the action-pool reference. */
+ UIMediumManagerFactory(UIActionPool *pActionPool = 0);
+
+protected:
+
+ /** Creates derived @a pDialog instance.
+ * @param pCenterWidget Brings the widget reference to center according to. */
+ virtual void create(QIManagerDialog *&pDialog, QWidget *pCenterWidget) RT_OVERRIDE;
+
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+};
+
+
+/** QIManagerDialog extension providing GUI with the dialog to control media related functionality. */
+class UIMediumManager : public QIWithRetranslateUI<QIManagerDialog>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+private slots:
+
+ /** @name Button-box stuff.
+ * @{ */
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** Constructs Medium Manager dialog.
+ * @param pCenterWidget Brings the widget reference to center according to.
+ * @param pActionPool Brings the action-pool reference. */
+ UIMediumManager(QWidget *pCenterWidget, UIActionPool *pActionPool);
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ virtual void configure() RT_OVERRIDE;
+ /** Configures central-widget. */
+ virtual void configureCentralWidget() RT_OVERRIDE;
+ /** Configures button-box. */
+ virtual void configureButtonBox() RT_OVERRIDE;
+ /** Perform final preparations. */
+ virtual void finalize() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Widget stuff.
+ * @{ */
+ /** Returns the widget. */
+ virtual UIMediumManagerWidget *widget() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Action related variables.
+ * @{ */
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** @} */
+
+ /** @name Progress-bar variables.
+ * @{ */
+ /** Holds the progress-bar widget instance. */
+ UIEnumerationProgressBar *m_pProgressBar;
+ /** @} */
+
+ /** Allow factory access to private/protected members: */
+ friend class UIMediumManagerFactory;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_UIMediumManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSearchWidget.cpp b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSearchWidget.cpp
new file mode 100644
index 00000000..191d9f16
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSearchWidget.cpp
@@ -0,0 +1,275 @@
+/* $Id: UIMediumSearchWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMediumSearchWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QPainter>
+#include <QHBoxLayout>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "QITreeWidget.h"
+#include "UIIconPool.h"
+#include "UIMediumItem.h"
+#include "UIMediumSearchWidget.h"
+#include "UISearchLineEdit.h"
+
+#ifdef VBOX_WS_MAC
+# include "UIWindowMenuManager.h"
+#endif /* VBOX_WS_MAC */
+
+
+/*********************************************************************************************************************************
+* FilterByNameUUID definition/implementation. *
+*********************************************************************************************************************************/
+
+class FilterByNameUUID : public QITreeWidgetItemFilter
+{
+
+public:
+
+ FilterByNameUUID(UIMediumSearchWidget::SearchType enmSearchType, const QString &strSearchTerm)
+ : m_enmSearchType(enmSearchType)
+ , m_strSearchTerm(strSearchTerm){}
+ virtual ~FilterByNameUUID(){}
+ virtual bool operator()(QTreeWidgetItem *pItem) const
+ {
+ if (!pItem || m_strSearchTerm.isEmpty())
+ return false;
+ if (pItem->type() != QITreeWidgetItem::ItemType)
+ return false;
+
+ UIMediumItem *pMediumItem = dynamic_cast<UIMediumItem*>(pItem);
+ if (!pMediumItem)
+ return false;
+ if (m_enmSearchType == UIMediumSearchWidget::SearchByUUID &&
+ !pMediumItem->id().toString().contains(m_strSearchTerm, Qt::CaseInsensitive))
+ return false;
+ if (m_enmSearchType == UIMediumSearchWidget::SearchByName &&
+ !pMediumItem->name().contains(m_strSearchTerm, Qt::CaseInsensitive))
+ return false;
+ return true;
+ }
+
+private:
+
+ UIMediumSearchWidget::SearchType m_enmSearchType;
+ QString m_strSearchTerm;
+};
+
+
+/*********************************************************************************************************************************
+* UIMediumSearchWidget implementation . *
+*********************************************************************************************************************************/
+
+UIMediumSearchWidget::UIMediumSearchWidget(QWidget *pParent)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_pSearchComboxBox(0)
+ , m_pSearchTermLineEdit(0)
+ , m_pShowNextMatchButton(0)
+ , m_pShowPreviousMatchButton(0)
+ , m_pTreeWidget(0)
+ , m_iScrollToIndex(-1)
+{
+ prepareWidgets();
+}
+
+void UIMediumSearchWidget::prepareWidgets()
+{
+ QHBoxLayout *pLayout = new QHBoxLayout;
+ setLayout(pLayout);
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ pLayout->setSpacing(0);
+
+ m_pSearchComboxBox = new QComboBox;
+ if (m_pSearchComboxBox)
+ {
+ m_pSearchComboxBox->setEditable(false);
+ m_pSearchComboxBox->insertItem(SearchByName, "Search By Name");
+ m_pSearchComboxBox->insertItem(SearchByUUID, "Search By UUID");
+ pLayout->addWidget(m_pSearchComboxBox);
+
+ connect(m_pSearchComboxBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIMediumSearchWidget::sigPerformSearch);
+
+ }
+
+ m_pSearchTermLineEdit = new UISearchLineEdit;
+ if (m_pSearchTermLineEdit)
+ {
+ m_pSearchTermLineEdit->setClearButtonEnabled(false);
+ pLayout->addWidget(m_pSearchTermLineEdit);
+ connect(m_pSearchTermLineEdit, &QLineEdit::textChanged,
+ this, &UIMediumSearchWidget::sigPerformSearch);
+ }
+
+ m_pShowPreviousMatchButton = new QIToolButton;
+ if (m_pShowPreviousMatchButton)
+ {
+ m_pShowPreviousMatchButton->setIcon(UIIconPool::iconSet(":/log_viewer_search_backward_16px.png", ":/log_viewer_search_backward_disabled_16px.png"));
+ connect(m_pShowPreviousMatchButton, &QIToolButton::clicked, this, &UIMediumSearchWidget::sltShowPreviousMatchingItem);
+ pLayout->addWidget(m_pShowPreviousMatchButton);
+ }
+ m_pShowNextMatchButton = new QIToolButton;
+ if (m_pShowNextMatchButton)
+ {
+ m_pShowNextMatchButton->setIcon(UIIconPool::iconSet(":/log_viewer_search_forward_16px.png", ":/log_viewer_search_forward_disabled_16px.png"));
+ connect(m_pShowNextMatchButton, &QIToolButton::clicked, this, &UIMediumSearchWidget:: sltShowNextMatchingItem);
+ pLayout->addWidget(m_pShowNextMatchButton);
+ }
+
+ retranslateUi();
+}
+
+UIMediumSearchWidget::SearchType UIMediumSearchWidget::searchType() const
+{
+ if (!m_pSearchComboxBox || m_pSearchComboxBox->currentIndex() >= static_cast<int>(SearchByMax))
+ return SearchByMax;
+ return static_cast<SearchType>(m_pSearchComboxBox->currentIndex());
+}
+
+QString UIMediumSearchWidget::searchTerm() const
+{
+ if (!m_pSearchTermLineEdit)
+ return QString();
+ return m_pSearchTermLineEdit->text();
+}
+
+void UIMediumSearchWidget::search(QITreeWidget* pTreeWidget, bool fGotoNext /* = true */)
+{
+ if (!pTreeWidget)
+ return;
+
+ m_pTreeWidget = pTreeWidget;
+ QList<QTreeWidgetItem*> allItems = pTreeWidget->filterItems(QITreeWidgetItemFilter());
+ markUnmarkItems(allItems, false);
+
+ m_matchedItemList = pTreeWidget->filterItems(FilterByNameUUID(searchType(), searchTerm()));
+ markUnmarkItems(m_matchedItemList, true);
+ if (!m_matchedItemList.isEmpty())
+ {
+ m_iScrollToIndex = -1;
+ if (fGotoNext)
+ goToNextPrevious(true);
+ }
+ else
+ m_iScrollToIndex = -1;
+ updateSearchLineEdit(m_matchedItemList.size(), m_iScrollToIndex);
+}
+
+void UIMediumSearchWidget::retranslateUi()
+{
+ if (m_pSearchComboxBox)
+ {
+ m_pSearchComboxBox->setItemText(SearchByName, tr("Search By Name"));
+ m_pSearchComboxBox->setItemText(SearchByUUID, tr("Search By UUID"));
+ m_pSearchComboxBox->setToolTip(tr("Select the search type"));
+ }
+ if (m_pSearchTermLineEdit)
+ m_pSearchTermLineEdit->setToolTip(tr("Enter the search term and press Enter/Return"));
+ if (m_pShowPreviousMatchButton)
+ m_pShowPreviousMatchButton->setToolTip(tr("Show the previous item matching the search term"));
+ if (m_pShowNextMatchButton)
+ m_pShowNextMatchButton->setToolTip(tr("Show the next item matching the search term"));
+}
+
+void UIMediumSearchWidget::showEvent(QShowEvent *pEvent)
+{
+ if (m_pSearchTermLineEdit)
+ m_pSearchTermLineEdit->setFocus();
+ QIWithRetranslateUI<QWidget>::showEvent(pEvent);
+}
+
+void UIMediumSearchWidget::markUnmarkItems(QList<QTreeWidgetItem*> &itemList, bool fMark)
+{
+ foreach (QTreeWidgetItem* pItem, itemList)
+ {
+ if (pItem->type() != QITreeWidgetItem::ItemType)
+ continue;
+ UIMediumItem *pMediumItem = static_cast<UIMediumItem*>(pItem);
+ if (!pMediumItem)
+ continue;
+ QFont font = pMediumItem->font(0);
+ font.setBold(fMark);
+ pMediumItem->setFont(0, font);
+
+ if (!fMark)
+ setUnderlineItemText(pMediumItem, false);
+ }
+}
+
+void UIMediumSearchWidget::setUnderlineItemText(QTreeWidgetItem* pItem, bool fUnderline)
+{
+ if (!pItem)
+ return;
+ QFont font = pItem->font(0);
+ font.setUnderline(fUnderline);
+ pItem->setFont(0, font);
+}
+
+void UIMediumSearchWidget::goToNextPrevious(bool fNext)
+{
+ if (!m_pTreeWidget || m_matchedItemList.isEmpty())
+ return;
+
+ if (m_iScrollToIndex >= 0 && m_iScrollToIndex < m_matchedItemList.size())
+ setUnderlineItemText(m_matchedItemList[m_iScrollToIndex], false);
+
+ if (fNext)
+ ++m_iScrollToIndex;
+ else
+ --m_iScrollToIndex;
+
+ if (m_iScrollToIndex >= m_matchedItemList.size())
+ m_iScrollToIndex = 0;
+ if (m_iScrollToIndex < 0)
+ m_iScrollToIndex = m_matchedItemList.size() - 1;
+
+ setUnderlineItemText(m_matchedItemList[m_iScrollToIndex], true);
+ m_pTreeWidget->scrollTo(m_pTreeWidget->itemIndex(m_matchedItemList[m_iScrollToIndex]), QAbstractItemView::PositionAtCenter);
+ updateSearchLineEdit(m_matchedItemList.size(), m_iScrollToIndex);
+}
+
+void UIMediumSearchWidget::sltShowNextMatchingItem()
+{
+ goToNextPrevious(true);
+}
+
+void UIMediumSearchWidget::sltShowPreviousMatchingItem()
+{
+ goToNextPrevious(false);
+}
+
+void UIMediumSearchWidget::updateSearchLineEdit(int iMatchCount, int iScrollToIndex)
+{
+ if (!m_pSearchTermLineEdit)
+ return;
+ m_pSearchTermLineEdit->setMatchCount(iMatchCount);
+ m_pSearchTermLineEdit->setScrollToIndex(iScrollToIndex);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSearchWidget.h b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSearchWidget.h
new file mode 100644
index 00000000..d6799f88
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSearchWidget.h
@@ -0,0 +1,105 @@
+/* $Id: UIMediumSearchWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIMediumSearchWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_UIMediumSearchWidget_h
+#define FEQT_INCLUDED_SRC_medium_UIMediumSearchWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QTreeWidgetItem;
+class QIToolButton;
+class QITreeWidget;
+class UISearchLineEdit;
+
+/** QWidget extension providing a simple way to enter a earch term and search type for medium searching
+ * in virtual media manager, medium selection dialog, etc. */
+class SHARED_LIBRARY_STUFF UIMediumSearchWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigPerformSearch();
+
+public:
+
+ enum SearchType
+ {
+ SearchByName,
+ SearchByUUID,
+ SearchByMax
+ };
+
+public:
+
+ UIMediumSearchWidget(QWidget *pParent = 0);
+ SearchType searchType() const;
+ QString searchTerm() const;
+ /** Performs the search on the items of the @p pTreeWidget. If @p is true
+ * then the next marched item is selected. */
+ void search(QITreeWidget* pTreeWidget, bool fGotoNext = true);
+
+protected:
+
+ void retranslateUi() RT_OVERRIDE;
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ void sltShowNextMatchingItem();
+ void sltShowPreviousMatchingItem();
+
+private:
+
+ void prepareWidgets();
+ /** Marks/unmarks the items of @p itemList depending on @p fMark. */
+ void markUnmarkItems(QList<QTreeWidgetItem*> &itemList, bool fMark);
+ void setUnderlineItemText(QTreeWidgetItem* pItem, bool fUnderline);
+ /** Increases (or decreases if @p fNext is false) the m_iScrollToIndex and
+ * takes care of the necessary decoration changes to mark the current item. */
+ void goToNextPrevious(bool fNext);
+ /** Updates the feedback text of th line edit that shows # of matches. */
+ void updateSearchLineEdit(int iMatchCount, int iScrollToIndex);
+
+ QComboBox *m_pSearchComboxBox;
+ UISearchLineEdit *m_pSearchTermLineEdit;
+ QIToolButton *m_pShowNextMatchButton;
+ QIToolButton *m_pShowPreviousMatchButton;
+
+ QList<QTreeWidgetItem*> m_matchedItemList;
+ QITreeWidget *m_pTreeWidget;
+ /** The index to the matched item (in m_matchedItemList) which is currently selected/scrolled to. */
+ int m_iScrollToIndex;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_UIMediumSearchWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSelector.cpp b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSelector.cpp
new file mode 100644
index 00000000..76e96893
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSelector.cpp
@@ -0,0 +1,811 @@
+/* $Id: UIMediumSelector.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMediumSelector class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QHeaderView>
+#include <QMenuBar>
+#include <QVBoxLayout>
+#include <QPushButton>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIFileDialog.h"
+#include "QIMessageBox.h"
+#include "QITabWidget.h"
+#include "QIToolButton.h"
+#include "UIActionPool.h"
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIMediumSearchWidget.h"
+#include "UIMediumSelector.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UIIconPool.h"
+#include "UIMedium.h"
+#include "UIMediumItem.h"
+#include "QIToolBar.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+#include "CMediumAttachment.h"
+#include "CMediumFormat.h"
+#include "CStorageController.h"
+#include "CSystemProperties.h"
+
+#ifdef VBOX_WS_MAC
+# include "UIWindowMenuManager.h"
+#endif /* VBOX_WS_MAC */
+
+
+UIMediumSelector::UIMediumSelector(const QUuid &uCurrentMediumId, UIMediumDeviceType enmMediumType, const QString &machineName,
+ const QString &machineSettingsFilePath, const QString &strMachineGuestOSTypeId,
+ const QUuid &uMachineID, QWidget *pParent, UIActionPool *pActionPool)
+ :QIWithRetranslateUI<QIWithRestorableGeometry<QIMainDialog> >(pParent)
+ , m_pCentralWidget(0)
+ , m_pMainLayout(0)
+ , m_pTreeWidget(0)
+ , m_enmMediumType(enmMediumType)
+ , m_pButtonBox(0)
+ , m_pCancelButton(0)
+ , m_pChooseButton(0)
+ , m_pLeaveEmptyButton(0)
+ , m_pMainMenu(0)
+ , m_pToolBar(0)
+ , m_pActionAdd(0)
+ , m_pActionCreate(0)
+ , m_pActionRefresh(0)
+ , m_pAttachedSubTreeRoot(0)
+ , m_pNotAttachedSubTreeRoot(0)
+ , m_pParent(pParent)
+ , m_pSearchWidget(0)
+ , m_iCurrentShownIndex(0)
+ , m_strMachineFolder(machineSettingsFilePath)
+ , m_strMachineName(machineName)
+ , m_strMachineGuestOSTypeId(strMachineGuestOSTypeId)
+ , m_uMachineID(uMachineID)
+ , m_pActionPool(pActionPool)
+ , m_iGeometrySaveTimerId(-1)
+{
+ /* Start full medium-enumeration (if necessary): */
+ if (!uiCommon().isFullMediumEnumerationRequested())
+ uiCommon().enumerateMedia();
+ configure();
+ finalize();
+ selectMedium(uCurrentMediumId);
+ loadSettings();
+}
+
+void UIMediumSelector::setEnableCreateAction(bool fEnable)
+{
+ if (!m_pActionCreate)
+ return;
+ m_pActionCreate->setEnabled(fEnable);
+ m_pActionCreate->setVisible(fEnable);
+}
+
+QList<QUuid> UIMediumSelector::selectedMediumIds() const
+{
+ QList<QUuid> selectedIds;
+ if (!m_pTreeWidget)
+ return selectedIds;
+ QList<QTreeWidgetItem*> selectedItems = m_pTreeWidget->selectedItems();
+ for (int i = 0; i < selectedItems.size(); ++i)
+ {
+ UIMediumItem *item = dynamic_cast<UIMediumItem*>(selectedItems.at(i));
+ if (item)
+ selectedIds.push_back(item->medium().id());
+ }
+ return selectedIds;
+}
+
+/* static */
+int UIMediumSelector::openMediumSelectorDialog(QWidget *pParent, UIMediumDeviceType enmMediumType, const QUuid &uCurrentMediumId,
+ QUuid &uSelectedMediumUuid, const QString &strMachineFolder, const QString &strMachineName,
+ const QString &strMachineGuestOSTypeId, bool fEnableCreate, const QUuid &uMachineID,
+ UIActionPool *pActionPool)
+{
+ QUuid uMachineOrGlobalId = uMachineID == QUuid() ? gEDataManager->GlobalID : uMachineID;
+
+ QWidget *pDialogParent = windowManager().realParentWindow(pParent);
+ QPointer<UIMediumSelector> pSelector = new UIMediumSelector(uCurrentMediumId, enmMediumType, strMachineName,
+ strMachineFolder, strMachineGuestOSTypeId,
+ uMachineOrGlobalId, pDialogParent, pActionPool);
+
+ if (!pSelector)
+ return static_cast<int>(UIMediumSelector::ReturnCode_Rejected);
+ pSelector->setEnableCreateAction(fEnableCreate);
+ windowManager().registerNewParent(pSelector, pDialogParent);
+
+ int iResult = pSelector->exec(false);
+ UIMediumSelector::ReturnCode returnCode;
+
+ if (iResult >= static_cast<int>(UIMediumSelector::ReturnCode_Max) || iResult < 0)
+ returnCode = UIMediumSelector::ReturnCode_Rejected;
+ else
+ returnCode = static_cast<UIMediumSelector::ReturnCode>(iResult);
+
+ if (returnCode == UIMediumSelector::ReturnCode_Accepted)
+ {
+ QList<QUuid> selectedMediumIds = pSelector->selectedMediumIds();
+
+ /* Currently we only care about the 0th since we support single selection by intention: */
+ if (selectedMediumIds.isEmpty())
+ returnCode = UIMediumSelector::ReturnCode_Rejected;
+ else
+ {
+ uSelectedMediumUuid = selectedMediumIds[0];
+ uiCommon().updateRecentlyUsedMediumListAndFolder(enmMediumType, uiCommon().medium(uSelectedMediumUuid).location());
+ }
+ }
+ delete pSelector;
+ return static_cast<int>(returnCode);
+}
+
+void UIMediumSelector::retranslateUi()
+{
+ if (m_pCancelButton)
+ {
+ m_pCancelButton->setText(tr("&Cancel"));
+ m_pCancelButton->setToolTip(tr("Cancel"));
+ }
+ if (m_pLeaveEmptyButton)
+ {
+ m_pLeaveEmptyButton->setText(tr("Leave &Empty"));
+ m_pLeaveEmptyButton->setToolTip(tr("Leave the drive empty"));
+ }
+
+ if (m_pChooseButton)
+ {
+ m_pChooseButton->setText(tr("C&hoose"));
+ m_pChooseButton->setToolTip(tr("Attach the selected medium to the drive"));
+ }
+
+ if (m_pTreeWidget)
+ {
+ m_pTreeWidget->headerItem()->setText(0, tr("Name"));
+ m_pTreeWidget->headerItem()->setText(1, tr("Virtual Size"));
+ m_pTreeWidget->headerItem()->setText(2, tr("Actual Size"));
+ }
+}
+
+bool UIMediumSelector::event(QEvent *pEvent)
+{
+ if (pEvent->type() == QEvent::Resize || pEvent->type() == QEvent::Move)
+ {
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+ }
+ else if (pEvent->type() == QEvent::Timer)
+ {
+ QTimerEvent *pTimerEvent = static_cast<QTimerEvent*>(pEvent);
+ if (pTimerEvent->timerId() == m_iGeometrySaveTimerId)
+ {
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = -1;
+ saveDialogGeometry();
+ }
+ }
+ return QIWithRetranslateUI<QIWithRestorableGeometry<QIMainDialog> >::event(pEvent);
+}
+
+void UIMediumSelector::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/media_manager_32px.png", ":/media_manager_16px.png"));
+#endif
+
+ setTitle();
+ prepareWidgets();
+ prepareActions();
+ prepareMenuAndToolBar();
+ prepareConnections();
+}
+
+void UIMediumSelector::prepareActions()
+{
+ if (!m_pActionPool)
+ return;
+
+ switch (m_enmMediumType)
+ {
+ case UIMediumDeviceType_DVD:
+ m_pActionAdd = m_pActionPool->action(UIActionIndex_M_MediumSelector_AddCD);
+ m_pActionCreate = m_pActionPool->action(UIActionIndex_M_MediumSelector_CreateCD);
+ break;
+ case UIMediumDeviceType_Floppy:
+ m_pActionAdd = m_pActionPool->action(UIActionIndex_M_MediumSelector_AddFD);
+ m_pActionCreate = m_pActionPool->action(UIActionIndex_M_MediumSelector_CreateFD);
+ break;
+ case UIMediumDeviceType_HardDisk:
+ case UIMediumDeviceType_All:
+ case UIMediumDeviceType_Invalid:
+ default:
+ m_pActionAdd = m_pActionPool->action(UIActionIndex_M_MediumSelector_AddHD);
+ m_pActionCreate = m_pActionPool->action(UIActionIndex_M_MediumSelector_CreateHD);
+ break;
+ }
+
+ m_pActionRefresh = m_pActionPool->action(UIActionIndex_M_MediumSelector_Refresh);
+}
+
+void UIMediumSelector::prepareMenuAndToolBar()
+{
+ if (!m_pMainMenu || !m_pToolBar)
+ return;
+
+ m_pMainMenu->addAction(m_pActionAdd);
+ m_pMainMenu->addAction(m_pActionCreate);
+ m_pMainMenu->addSeparator();
+ m_pMainMenu->addAction(m_pActionRefresh);
+
+ m_pToolBar->addAction(m_pActionAdd);
+ if (!(gEDataManager->restrictedDialogTypes(m_uMachineID) & UIExtraDataMetaDefs::DialogType_VISOCreator))
+ m_pToolBar->addAction(m_pActionCreate);
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionRefresh);
+}
+
+void UIMediumSelector::prepareConnections()
+{
+ /* Configure medium-enumeration connections: */
+ connect(&uiCommon(), &UICommon::sigMediumCreated,
+ this, &UIMediumSelector::sltHandleMediumCreated);
+ connect(&uiCommon(), &UICommon::sigMediumEnumerationStarted,
+ this, &UIMediumSelector::sltHandleMediumEnumerationStart);
+ connect(&uiCommon(), &UICommon::sigMediumEnumerated,
+ this, &UIMediumSelector::sltHandleMediumEnumerated);
+ connect(&uiCommon(), &UICommon::sigMediumEnumerationFinished,
+ this, &UIMediumSelector::sltHandleMediumEnumerationFinish);
+ if (m_pActionAdd)
+ connect(m_pActionAdd, &QAction::triggered, this, &UIMediumSelector::sltAddMedium);
+ if (m_pActionCreate)
+ connect(m_pActionCreate, &QAction::triggered, this, &UIMediumSelector::sltCreateMedium);
+ if (m_pActionRefresh)
+ connect(m_pActionRefresh, &QAction::triggered, this, &UIMediumSelector::sltHandleRefresh);
+
+ if (m_pTreeWidget)
+ {
+ connect(m_pTreeWidget, &QITreeWidget::itemSelectionChanged, this, &UIMediumSelector::sltHandleItemSelectionChanged);
+ connect(m_pTreeWidget, &QITreeWidget::itemDoubleClicked, this, &UIMediumSelector::sltHandleTreeWidgetDoubleClick);
+ connect(m_pTreeWidget, &QITreeWidget::customContextMenuRequested, this, &UIMediumSelector::sltHandleTreeContextMenuRequest);
+ }
+
+ if (m_pCancelButton)
+ connect(m_pCancelButton, &QPushButton::clicked, this, &UIMediumSelector::sltButtonCancel);
+ if (m_pChooseButton)
+ connect(m_pChooseButton, &QPushButton::clicked, this, &UIMediumSelector::sltButtonChoose);
+ if (m_pLeaveEmptyButton)
+ connect(m_pLeaveEmptyButton, &QPushButton::clicked, this, &UIMediumSelector::sltButtonLeaveEmpty);
+
+ if (m_pSearchWidget)
+ {
+ connect(m_pSearchWidget, &UIMediumSearchWidget::sigPerformSearch,
+ this, &UIMediumSelector::sltHandlePerformSearch);
+ }
+}
+
+UIMediumItem* UIMediumSelector::addTreeItem(const UIMedium &medium, QITreeWidgetItem *pParent)
+{
+ if (!pParent)
+ return 0;
+ switch (m_enmMediumType)
+ {
+ case UIMediumDeviceType_DVD:
+ return new UIMediumItemCD(medium, pParent);
+ break;
+ case UIMediumDeviceType_Floppy:
+ return new UIMediumItemFD(medium, pParent);
+ break;
+ case UIMediumDeviceType_HardDisk:
+ case UIMediumDeviceType_All:
+ case UIMediumDeviceType_Invalid:
+ default:
+ return createHardDiskItem(medium, pParent);
+ break;
+ }
+}
+
+UIMediumItem* UIMediumSelector::createHardDiskItem(const UIMedium &medium, QITreeWidgetItem *pParent)
+{
+ if (medium.medium().isNull())
+ return 0;
+ if (!m_pTreeWidget)
+ return 0;
+ /* Search the tree to see if we already have the item: */
+ UIMediumItem *pMediumItem = searchItem(0, medium.id());
+ if (pMediumItem)
+ return pMediumItem;
+ /* Check if the corresponding medium has a parent */
+ if (medium.parentID() != UIMedium::nullID())
+ {
+ UIMediumItem *pParentMediumItem = searchItem(0, medium.parentID());
+ /* If parent medium-item was not found we create it: */
+ if (!pParentMediumItem)
+ {
+ /* Make sure corresponding parent medium is already cached! */
+ UIMedium parentMedium = uiCommon().medium(medium.parentID());
+ if (parentMedium.isNull())
+ AssertMsgFailed(("Parent medium with ID={%s} was not found!\n", medium.parentID().toString().toUtf8().constData()));
+ /* Try to create parent medium-item: */
+ else
+ pParentMediumItem = createHardDiskItem(parentMedium, pParent);
+ }
+ if (pParentMediumItem)
+ {
+ pMediumItem = new UIMediumItemHD(medium, pParentMediumItem);
+ LogRel2(("UIMediumManager: Child hard-disk medium-item with ID={%s} created.\n", medium.id().toString().toUtf8().constData()));
+ }
+ else
+ AssertMsgFailed(("Parent medium with ID={%s} could not be created!\n", medium.parentID().toString().toUtf8().constData()));
+ }
+
+ /* No parents, thus just create item as top-level one: */
+ else
+ {
+ pMediumItem = new UIMediumItemHD(medium, pParent);
+ LogRel2(("UIMediumManager: Root hard-disk medium-item with ID={%s} created.\n", medium.id().toString().toUtf8().constData()));
+ }
+ return pMediumItem;
+}
+
+void UIMediumSelector::restoreSelection(const QList<QUuid> &selectedMediums, QVector<UIMediumItem*> &mediumList)
+{
+ if (!m_pTreeWidget)
+ return;
+ if (selectedMediums.isEmpty())
+ {
+ m_pTreeWidget->setCurrentItem(0);
+ return;
+ }
+ bool selected = false;
+ for (int i = 0; i < mediumList.size(); ++i)
+ {
+ if (!mediumList[i])
+ continue;
+ if (selectedMediums.contains(mediumList[i]->medium().id()))
+ {
+ mediumList[i]->setSelected(true);
+ selected = true;
+ }
+ }
+
+ if (!selected)
+ m_pTreeWidget->setCurrentItem(0);
+}
+
+void UIMediumSelector::prepareWidgets()
+{
+ m_pCentralWidget = new QWidget;
+ if (!m_pCentralWidget)
+ return;
+ setCentralWidget(m_pCentralWidget);
+
+ m_pMainLayout = new QVBoxLayout;
+ m_pCentralWidget->setLayout(m_pMainLayout);
+
+ if (!m_pMainLayout || !menuBar())
+ return;
+
+ if (m_pActionPool && m_pActionPool->action(UIActionIndex_M_MediumSelector))
+ {
+ m_pMainMenu = m_pActionPool->action(UIActionIndex_M_MediumSelector)->menu();
+ if (m_pMainMenu)
+ menuBar()->addMenu(m_pMainMenu);
+ }
+
+ m_pToolBar = new QIToolBar;
+ if (m_pToolBar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+ m_pMainLayout->addWidget(m_pToolBar);
+ }
+
+ m_pTreeWidget = new QITreeWidget;
+ if (m_pTreeWidget)
+ {
+ m_pTreeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_pMainLayout->addWidget(m_pTreeWidget);
+ m_pTreeWidget->setAlternatingRowColors(true);
+ int iColumnCount = (m_enmMediumType == UIMediumDeviceType_HardDisk) ? 3 : 2;
+ m_pTreeWidget->setColumnCount(iColumnCount);
+ m_pTreeWidget->setSortingEnabled(true);
+ m_pTreeWidget->sortItems(0, Qt::AscendingOrder);
+ m_pTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ }
+
+ m_pSearchWidget = new UIMediumSearchWidget;
+ if (m_pSearchWidget)
+ {
+ m_pMainLayout->addWidget(m_pSearchWidget);
+ }
+
+ m_pButtonBox = new QIDialogButtonBox;
+ if (m_pButtonBox)
+ {
+ /* Configure button-box: */
+ m_pCancelButton = m_pButtonBox->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
+
+ /* Only DVDs and Floppies can be left empty: */
+ if (m_enmMediumType == UIMediumDeviceType_DVD || m_enmMediumType == UIMediumDeviceType_Floppy)
+ m_pLeaveEmptyButton = m_pButtonBox->addButton(tr("Leave Empty"), QDialogButtonBox::ActionRole);
+
+ m_pChooseButton = m_pButtonBox->addButton(tr("Choose"), QDialogButtonBox::AcceptRole);
+ m_pCancelButton->setShortcut(Qt::Key_Escape);
+
+ /* Add button-box into main layout: */
+ m_pMainLayout->addWidget(m_pButtonBox);
+ }
+
+ repopulateTreeWidget();
+}
+
+void UIMediumSelector::sltButtonChoose()
+{
+ done(static_cast<int>(ReturnCode_Accepted));
+}
+
+void UIMediumSelector::sltButtonCancel()
+{
+ done(static_cast<int>(ReturnCode_Rejected));
+}
+
+void UIMediumSelector::sltButtonLeaveEmpty()
+{
+ done(static_cast<int>(ReturnCode_LeftEmpty));
+}
+
+void UIMediumSelector::sltAddMedium()
+{
+ QUuid uMediumID = uiCommon().openMediumWithFileOpenDialog(m_enmMediumType, this, m_strMachineFolder, true /* fUseLastFolder */);
+ if (uMediumID.isNull())
+ return;
+ repopulateTreeWidget();
+ selectMedium(uMediumID);
+}
+
+void UIMediumSelector::sltCreateMedium()
+{
+ QUuid uMediumId = uiCommon().openMediumCreatorDialog(m_pActionPool, this, m_enmMediumType, m_strMachineFolder,
+ m_strMachineName, m_strMachineGuestOSTypeId);
+ /* Make sure that the data structure is updated and newly created medium is selected and visible: */
+ sltHandleMediumCreated(uMediumId);
+}
+
+void UIMediumSelector::sltHandleItemSelectionChanged()
+{
+ updateChooseButton();
+}
+
+void UIMediumSelector::sltHandleTreeWidgetDoubleClick(QTreeWidgetItem * item, int column)
+{
+ Q_UNUSED(column);
+ if (!dynamic_cast<UIMediumItem*>(item))
+ return;
+ accept();
+}
+
+void UIMediumSelector::sltHandleMediumCreated(const QUuid &uMediumId)
+{
+ if (uMediumId.isNull())
+ return;
+ /* Update the tree widget making sure we show the new item: */
+ repopulateTreeWidget();
+ /* Select the new item: */
+ selectMedium(uMediumId);
+ /* Update the search: */
+ m_pSearchWidget->search(m_pTreeWidget);
+}
+
+void UIMediumSelector::sltHandleMediumEnumerationStart()
+{
+ /* Disable controls. Left Alone button box 'Ok' button. it is handle by tree population: */
+ if (m_pActionRefresh)
+ m_pActionRefresh->setEnabled(false);
+}
+
+void UIMediumSelector::sltHandleMediumEnumerated()
+{
+}
+
+void UIMediumSelector::sltHandleMediumEnumerationFinish()
+{
+ repopulateTreeWidget();
+ if (m_pActionRefresh)
+ m_pActionRefresh->setEnabled(true);
+}
+
+void UIMediumSelector::sltHandleRefresh()
+{
+ /* Restart full medium-enumeration: */
+ uiCommon().enumerateMedia();
+ /* Update the search: */
+ m_pSearchWidget->search(m_pTreeWidget);
+}
+
+void UIMediumSelector::sltHandlePerformSearch()
+{
+ if (!m_pSearchWidget)
+ return;
+ m_pSearchWidget->search(m_pTreeWidget);
+}
+
+void UIMediumSelector::sltHandleTreeContextMenuRequest(const QPoint &point)
+{
+ QWidget *pSender = qobject_cast<QWidget*>(sender());
+ if (!pSender)
+ return;
+
+ QMenu menu;
+ QAction *pExpandAll = menu.addAction(tr("Expand All"));
+ QAction *pCollapseAll = menu.addAction(tr("Collapse All"));
+ if (!pExpandAll || !pCollapseAll)
+ return;
+
+ pExpandAll->setIcon(UIIconPool::iconSet(":/expand_all_16px.png"));
+ pCollapseAll->setIcon(UIIconPool::iconSet(":/collapse_all_16px.png"));
+
+ connect(pExpandAll, &QAction::triggered, this, &UIMediumSelector::sltHandleTreeExpandAllSignal);
+ connect(pCollapseAll, &QAction::triggered, this, &UIMediumSelector::sltHandleTreeCollapseAllSignal);
+
+ menu.exec(pSender->mapToGlobal(point));
+}
+
+void UIMediumSelector::sltHandleTreeExpandAllSignal()
+{
+ if (m_pTreeWidget)
+ m_pTreeWidget->expandAll();
+}
+
+void UIMediumSelector::sltHandleTreeCollapseAllSignal()
+{
+ if (m_pTreeWidget)
+ m_pTreeWidget->collapseAll();
+
+ if (m_pAttachedSubTreeRoot)
+ m_pTreeWidget->setExpanded(m_pTreeWidget->itemIndex(m_pAttachedSubTreeRoot), true);
+ if (m_pNotAttachedSubTreeRoot)
+ m_pTreeWidget->setExpanded(m_pTreeWidget->itemIndex(m_pNotAttachedSubTreeRoot), true);
+}
+
+void UIMediumSelector::selectMedium(const QUuid &uMediumID)
+{
+ if (!m_pTreeWidget || uMediumID.isNull())
+ return;
+ UIMediumItem *pMediumItem = searchItem(0, uMediumID);
+ if (pMediumItem)
+ {
+ m_pTreeWidget->setCurrentItem(pMediumItem);
+ QModelIndex itemIndex = m_pTreeWidget->itemIndex(pMediumItem);
+ if (itemIndex.isValid())
+ m_pTreeWidget->scrollTo(itemIndex, QAbstractItemView::EnsureVisible);
+ }
+}
+
+void UIMediumSelector::updateChooseButton()
+{
+ if (!m_pTreeWidget || !m_pChooseButton)
+ return;
+ QList<QTreeWidgetItem*> selectedItems = m_pTreeWidget->selectedItems();
+ if (selectedItems.isEmpty())
+ {
+ m_pChooseButton->setEnabled(false);
+ return;
+ }
+
+ /* check if at least one of the selected items is a UIMediumItem */
+ bool mediumItemSelected = false;
+ for (int i = 0; i < selectedItems.size() && !mediumItemSelected; ++i)
+ {
+ if (dynamic_cast<UIMediumItem*>(selectedItems.at(i)))
+ mediumItemSelected = true;
+ }
+ if (mediumItemSelected)
+ m_pChooseButton->setEnabled(true);
+ else
+ m_pChooseButton->setEnabled(false);
+}
+
+void UIMediumSelector::finalize()
+{
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMediumSelector::showEvent(QShowEvent *pEvent)
+{
+ Q_UNUSED(pEvent);
+
+ if (m_pTreeWidget)
+ m_pTreeWidget->setFocus();
+}
+
+void UIMediumSelector::repopulateTreeWidget()
+{
+ if (!m_pTreeWidget)
+ return;
+ /* Cache the currently selected items: */
+ QList<QTreeWidgetItem*> selectedItems = m_pTreeWidget->selectedItems();
+ QList<QUuid> selectedMedia = selectedMediumIds();
+ /* uuid list of selected items: */
+ /* Reset the related data structure: */
+ m_mediumItemList.clear();
+ m_pTreeWidget->clear();
+ m_pAttachedSubTreeRoot = 0;
+ m_pNotAttachedSubTreeRoot = 0;
+ QVector<UIMediumItem*> menuItemVector;
+ foreach (const QUuid &uMediumID, uiCommon().mediumIDs())
+ {
+ UIMedium medium = uiCommon().medium(uMediumID);
+ if (medium.type() == m_enmMediumType)
+ {
+ bool isMediumAttached = !(medium.medium().GetMachineIds().isEmpty());
+ QITreeWidgetItem *pParent = 0;
+ if (isMediumAttached)
+ {
+ if (!m_pAttachedSubTreeRoot)
+ {
+ QStringList strList;
+ strList << "Attached";
+ m_pAttachedSubTreeRoot = new QITreeWidgetItem(m_pTreeWidget, strList);
+ }
+ pParent = m_pAttachedSubTreeRoot;
+
+ }
+ else
+ {
+ if (!m_pNotAttachedSubTreeRoot)
+ {
+ QStringList strList;
+ strList << "Not Attached";
+ m_pNotAttachedSubTreeRoot = new QITreeWidgetItem(m_pTreeWidget, strList);
+ }
+ pParent = m_pNotAttachedSubTreeRoot;
+ }
+ UIMediumItem *treeItem = addTreeItem(medium, pParent);
+ m_mediumItemList.append(treeItem);
+ menuItemVector.push_back(treeItem);
+ }
+ }
+ restoreSelection(selectedMedia, menuItemVector);
+ saveDefaultForeground();
+ updateChooseButton();
+ if (m_pAttachedSubTreeRoot)
+ m_pTreeWidget->expandItem(m_pAttachedSubTreeRoot);
+ if (m_pNotAttachedSubTreeRoot)
+ m_pTreeWidget->expandItem(m_pNotAttachedSubTreeRoot);
+ m_pTreeWidget->resizeColumnToContents(0);
+}
+
+void UIMediumSelector::saveDefaultForeground()
+{
+ if (!m_pTreeWidget)
+ return;
+ if (m_defaultItemForeground == QBrush() && m_pTreeWidget->topLevelItemCount() >= 1)
+ {
+ QTreeWidgetItem *item = m_pTreeWidget->topLevelItem(0);
+ if (item)
+ {
+ QVariant data = item->data(0, Qt::ForegroundRole);
+ if (data.canConvert<QBrush>())
+ {
+ m_defaultItemForeground = data.value<QBrush>();
+ }
+ }
+ }
+}
+
+UIMediumItem* UIMediumSelector::searchItem(const QTreeWidgetItem *pParent, const QUuid &mediumId)
+{
+ if (!m_pTreeWidget)
+ return 0;
+ if (!pParent)
+ pParent = m_pTreeWidget->invisibleRootItem();
+ if (!pParent)
+ return 0;
+
+ for (int i = 0; i < pParent->childCount(); ++i)
+ {
+ QTreeWidgetItem *pChild = pParent->child(i);
+ if (!pChild)
+ continue;
+ UIMediumItem *mediumItem = dynamic_cast<UIMediumItem*>(pChild);
+ if (mediumItem)
+ {
+ if (mediumItem->id() == mediumId)
+ return mediumItem;
+ }
+ UIMediumItem *pResult = searchItem(pChild, mediumId);
+ if (pResult)
+ return pResult;
+ }
+ return 0;
+}
+
+void UIMediumSelector::setTitle()
+{
+ switch (m_enmMediumType)
+ {
+ case UIMediumDeviceType_DVD:
+ if (!m_strMachineName.isEmpty())
+ setWindowTitle(QString("%1 - %2").arg(m_strMachineName).arg(tr("Optical Disk Selector")));
+ else
+ setWindowTitle(QString("%1").arg(tr("Optical Disk Selector")));
+ break;
+ case UIMediumDeviceType_Floppy:
+ if (!m_strMachineName.isEmpty())
+ setWindowTitle(QString("%1 - %2").arg(m_strMachineName).arg(tr("Floppy Disk Selector")));
+ else
+ setWindowTitle(QString("%1").arg(tr("Floppy Disk Selector")));
+ break;
+ case UIMediumDeviceType_HardDisk:
+ if (!m_strMachineName.isEmpty())
+ setWindowTitle(QString("%1 - %2").arg(m_strMachineName).arg(tr("Hard Disk Selector")));
+ else
+ setWindowTitle(QString("%1").arg(tr("Hard Disk Selector")));
+ break;
+ case UIMediumDeviceType_All:
+ case UIMediumDeviceType_Invalid:
+ default:
+ if (!m_strMachineName.isEmpty())
+ setWindowTitle(QString("%1 - %2").arg(m_strMachineName).arg(tr("Virtual Medium Selector")));
+ else
+ setWindowTitle(QString("%1").arg(tr("Virtual Medium Selector")));
+ break;
+ }
+}
+
+void UIMediumSelector::saveDialogGeometry()
+{
+ const QRect geo = currentGeometry();
+ LogRel2(("GUI: UIMediumSelector: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ gEDataManager->setMediumSelectorDialogGeometry(geo, isCurrentlyMaximized());
+}
+
+void UIMediumSelector::loadSettings()
+{
+ const QRect availableGeo = gpDesktop->availableGeometry(this);
+ int iDefaultWidth = availableGeo.width() / 2;
+ int iDefaultHeight = availableGeo.height() * 3 / 4;
+ QRect defaultGeo(0, 0, iDefaultWidth, iDefaultHeight);
+
+ QWidget *pParent = windowManager().realParentWindow(m_pParent ? m_pParent : windowManager().mainWindowShown());
+ /* Load geometry from extradata: */
+ const QRect geo = gEDataManager->mediumSelectorDialogGeometry(this, pParent, defaultGeo);
+ LogRel2(("GUI: UISoftKeyboard: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+
+ restoreGeometry(geo);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSelector.h b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSelector.h
new file mode 100644
index 00000000..7adc6c6e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/UIMediumSelector.h
@@ -0,0 +1,189 @@
+/* $Id: UIMediumSelector.h $ */
+/** @file
+ * VBox Qt GUI - UIMediumSelector class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_UIMediumSelector_h
+#define FEQT_INCLUDED_SRC_medium_UIMediumSelector_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIMainDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "QIWithRestorableGeometry.h"
+#include "UIMedium.h"
+#include "UIMediumDefs.h"
+
+
+/* Forward declarations: */
+class QAction;
+class QTreeWidgetItem;
+class QITreeWidget;
+class QITreeWidgetItem;
+class QVBoxLayout;
+class QIDialogButtonBox;
+class QIToolBar;
+class UIActionPool;
+class UIMediumItem;
+class UIMediumSearchWidget;
+
+/** QIDialog extension providing GUI with a dialog to select an existing medium. */
+class SHARED_LIBRARY_STUFF UIMediumSelector : public QIWithRetranslateUI<QIWithRestorableGeometry<QIMainDialog> >
+{
+
+ Q_OBJECT;
+
+signals:
+
+public:
+
+ UIMediumSelector(const QUuid &uCurrentMediumId, UIMediumDeviceType enmMediumType, const QString &machineName,
+ const QString &machineSettingsFilePath, const QString &strMachineGuestOSTypeId,
+ const QUuid &uMachineID, QWidget *pParent, UIActionPool *pActionPool);
+ /** Disables/enables the create action and controls its visibility. */
+ void setEnableCreateAction(bool fEnable);
+ QList<QUuid> selectedMediumIds() const;
+
+ enum ReturnCode
+ {
+ ReturnCode_Rejected = 0,
+ ReturnCode_Accepted,
+ ReturnCode_LeftEmpty,
+ ReturnCode_Max
+ };
+
+ /** Creates and shows a UIMediumSelector dialog.
+ * @param parent Passes the parent of the dialog,
+ * @param enmMediumType Passes the medium type,
+ * @param uCurrentMediumId Passes the id of the currently selected medium,
+ * @param uSelectedMediumUuid Gets the selected medium id from selection dialog,
+ * @param strMachineFolder Passes the machine folder,
+ * @param strMachineName Passes the name of the machine,
+ * @param strMachineGuestOSTypeId Passes the type ID of machine's guest os,
+ * @param fEnableCreate Passes whether to show/enable create action in the medium selector dialog,
+ * @param uMachineID Passes the machine UUID,
+ * @param pActionPool Passes the action pool instance pointer,
+ * returns the return code of the UIMediumSelector::ReturnCode as int. In case of a medium selection
+ * UUID of the selected medium is returned in @param uSelectedMediumUuid.*/
+ static int openMediumSelectorDialog(QWidget *pParent, UIMediumDeviceType enmMediumType, const QUuid &uCurrentMediumId,
+ QUuid &uSelectedMediumUuid, const QString &strMachineFolder, const QString &strMachineName,
+ const QString &strMachineGuestOSTypeId, bool fEnableCreate, const QUuid &uMachineID,
+ UIActionPool *pActionPool);
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() final override;
+ void showEvent(QShowEvent *pEvent) final override;
+ bool event(QEvent *pEvent) final override;
+ /** @} */
+
+private slots:
+
+ void sltButtonLeaveEmpty();
+ void sltButtonCancel();
+ void sltButtonChoose();
+ void sltAddMedium();
+ void sltCreateMedium();
+ void sltHandleItemSelectionChanged();
+ void sltHandleTreeWidgetDoubleClick(QTreeWidgetItem * item, int column);
+ void sltHandleMediumCreated(const QUuid &uMediumId);
+ void sltHandleMediumEnumerationStart();
+ void sltHandleMediumEnumerated();
+ void sltHandleMediumEnumerationFinish();
+ void sltHandleRefresh();
+ void sltHandlePerformSearch();
+ void sltHandleTreeContextMenuRequest(const QPoint &point);
+ void sltHandleTreeExpandAllSignal();
+ void sltHandleTreeCollapseAllSignal();
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ void configure();
+ void prepareWidgets();
+ void prepareActions();
+ void prepareMenuAndToolBar();
+ void prepareConnections();
+ /** Perform final preparations. */
+ void finalize();
+ /** @} */
+
+ void repopulateTreeWidget();
+ /** Disable/enable 'ok' button on the basis of having a selected item */
+ void updateChooseButton();
+ UIMediumItem* addTreeItem(const UIMedium &medium, QITreeWidgetItem *pParent);
+ void restoreSelection(const QList<QUuid> &selectedMediums, QVector<UIMediumItem*> &mediumList);
+ /** Recursively create the hard disk hierarchy under the tree widget */
+ UIMediumItem* createHardDiskItem(const UIMedium &medium, QITreeWidgetItem *pParent);
+ UIMediumItem* searchItem(const QTreeWidgetItem *pParent, const QUuid &mediumId);
+ /** Remember the default foreground brush of the tree so that we can reset tree items' foreground later */
+ void saveDefaultForeground();
+ void selectMedium(const QUuid &uMediumID);
+ void setTitle();
+ void saveDialogGeometry();
+ void loadSettings();
+ QWidget *m_pCentralWidget;
+ QVBoxLayout *m_pMainLayout;
+ QITreeWidget *m_pTreeWidget;
+ UIMediumDeviceType m_enmMediumType;
+ QIDialogButtonBox *m_pButtonBox;
+ QPushButton *m_pCancelButton;
+ QPushButton *m_pChooseButton;
+ QPushButton *m_pLeaveEmptyButton;
+ QMenu *m_pMainMenu;
+ QIToolBar *m_pToolBar;
+ QAction *m_pActionAdd;
+ QAction *m_pActionCreate;
+ QAction *m_pActionRefresh;
+ /** All the known media that are already attached to some vm are added under the following top level tree item */
+ QITreeWidgetItem *m_pAttachedSubTreeRoot;
+ /** All the known media that are not attached to any vm are added under the following top level tree item */
+ QITreeWidgetItem *m_pNotAttachedSubTreeRoot;
+ QWidget *m_pParent;
+ UIMediumSearchWidget *m_pSearchWidget;
+ /** The list all items added to tree. kept in sync. with tree to make searching easier (faster). */
+ QList<UIMediumItem*> m_mediumItemList;
+ /** List of items that are matching to the search. */
+ QList<UIMediumItem*> m_mathingItemList;
+ /** Index of the currently shown (scrolled) item in the m_mathingItemList. */
+ int m_iCurrentShownIndex;
+ QBrush m_defaultItemForeground;
+ QString m_strMachineFolder;
+ QString m_strMachineName;
+ QString m_strMachineGuestOSTypeId;
+ QUuid m_uMachineID;
+ QUuid m_uCurrentMediumId;
+ UIActionPool *m_pActionPool;
+ int m_iGeometrySaveTimerId;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_UIMediumSelector_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/medium/viso/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoBrowserBase.cpp b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoBrowserBase.cpp
new file mode 100644
index 00000000..0d5e5b28
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoBrowserBase.cpp
@@ -0,0 +1,349 @@
+/* $Id: UIVisoBrowserBase.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVisoBrowserBase class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QGridLayout>
+#include <QLineEdit>
+#include <QTreeView>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIVisoBrowserBase.h"
+
+/*********************************************************************************************************************************
+* UILocationSelector definition. *
+*********************************************************************************************************************************/
+/** A QWidget extension used to show/hide parents treeview (thru signals) and show the path of
+ * the current selected file item. */
+class UILocationSelector : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigExpandCollapseTreeView();
+public:
+
+ UILocationSelector(QWidget *pParent = 0);
+ int lineEditWidth() const;
+ void updateLineEditText(const QString &strText);
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+ virtual bool eventFilter(QObject *pObj, QEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ void prepareWidgets();
+ QLineEdit *m_pLineEdit;
+ QGridLayout *m_pMainLayout;
+ QIToolButton *m_pExpandButton;
+};
+
+/*********************************************************************************************************************************
+* UILocationSelector implementation. *
+*********************************************************************************************************************************/
+
+UILocationSelector::UILocationSelector(QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLineEdit(0)
+ , m_pMainLayout(0)
+ , m_pExpandButton(0)
+{
+ prepareWidgets();
+}
+
+int UILocationSelector::lineEditWidth() const
+{
+ if (!m_pLineEdit)
+ return 0;
+ return m_pLineEdit->width();
+}
+
+void UILocationSelector::updateLineEditText(const QString &strText)
+{
+ if (!m_pLineEdit)
+ return;
+ m_pLineEdit->setText(strText);
+}
+
+void UILocationSelector::paintEvent(QPaintEvent *pEvent)
+{
+ QIWithRetranslateUI<QWidget>::paintEvent(pEvent);
+}
+
+void UILocationSelector::retranslateUi()
+{
+ if (m_pExpandButton)
+ m_pExpandButton->setToolTip(QApplication::translate("UIVisoCreatorWidget", "Click to show/hide the tree view."));
+ if (m_pLineEdit)
+ m_pLineEdit->setToolTip(QApplication::translate("UIVisoCreatorWidget", "Shows the current location."));
+}
+
+bool UILocationSelector::eventFilter(QObject *pObj, QEvent *pEvent)
+{
+ /* Handle only events sent to m_pLineEdit only: */
+ if (pObj != m_pLineEdit)
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObj, pEvent);
+
+ if (pEvent->type() == QEvent::MouseButtonPress)
+ {
+ QMouseEvent *pMouseEvent = dynamic_cast<QMouseEvent*>(pEvent);
+ if (pMouseEvent && pMouseEvent->button() == Qt::LeftButton)
+ emit sigExpandCollapseTreeView();
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObj, pEvent);
+}
+
+void UILocationSelector::prepareWidgets()
+{
+ m_pMainLayout = new QGridLayout;
+ if (!m_pMainLayout)
+ return;
+ m_pMainLayout->setSpacing(0);
+ m_pMainLayout->setContentsMargins(0,0,0,0);
+
+ m_pLineEdit = new QLineEdit;
+ if (m_pLineEdit)
+ {
+ m_pMainLayout->addWidget(m_pLineEdit, 0, 0, 1, 4);
+ m_pLineEdit->setReadOnly(true);
+ m_pLineEdit->installEventFilter(this);
+ }
+
+ m_pExpandButton = new QIToolButton;
+ if (m_pExpandButton)
+ {
+ m_pMainLayout->addWidget(m_pExpandButton, 0, 4, 1, 1);
+ m_pExpandButton->setIcon(UIIconPool::iconSet(":/select_file_16px.png", ":/select_file_disabled_16px.png"));
+ connect(m_pExpandButton, &QIToolButton::clicked,
+ this, &UILocationSelector::sigExpandCollapseTreeView);
+
+ }
+ setLayout(m_pMainLayout);
+ retranslateUi();
+}
+
+/*********************************************************************************************************************************
+* UIVisoBrowserBase implementation. *
+*********************************************************************************************************************************/
+
+UIVisoBrowserBase::UIVisoBrowserBase(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QGroupBox>(pParent)
+ , m_pTreeView(0)
+ , m_pMainLayout(0)
+ , m_pLocationSelector(0)
+{
+}
+
+UIVisoBrowserBase::~UIVisoBrowserBase()
+{
+}
+
+bool UIVisoBrowserBase::isTreeViewVisible() const
+{
+ if (!m_pTreeView)
+ return false;
+ return m_pTreeView->isVisible();
+}
+
+void UIVisoBrowserBase::hideTreeView()
+{
+ if (isTreeViewVisible())
+ sltExpandCollapseTreeView();
+}
+
+void UIVisoBrowserBase::prepareObjects()
+{
+ m_pMainLayout = new QGridLayout;
+ setLayout(m_pMainLayout);
+
+ if (!m_pMainLayout)
+ return;
+
+ m_pMainLayout->setRowStretch(1, 2);
+ m_pLocationSelector = new UILocationSelector;
+ if (m_pLocationSelector)
+ {
+ m_pMainLayout->addWidget(m_pLocationSelector, 0, 0, 1, 4);
+ }
+
+ m_pTreeView = new QTreeView(this);
+ if (m_pTreeView)
+ {
+ m_pTreeView->hide();
+ m_pTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_pTreeView->header()->hide();
+ m_pTreeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
+ m_pTreeView->setFrameStyle(QFrame::Panel | QFrame::Plain);
+ m_pTreeView->installEventFilter(this);
+ m_pTreeView->setTabKeyNavigation(false);
+ }
+}
+
+void UIVisoBrowserBase::prepareConnections()
+{
+ if (m_pTreeView)
+ {
+ connect(m_pTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
+ this, &UIVisoBrowserBase::sltHandleTreeSelectionChanged);
+ connect(m_pTreeView, &QTreeView::clicked,
+ this, &UIVisoBrowserBase::sltHandleTreeItemClicked);
+ }
+ if (m_pLocationSelector)
+ connect(m_pLocationSelector, &UILocationSelector::sigExpandCollapseTreeView,
+ this, &UIVisoBrowserBase::sltExpandCollapseTreeView);
+}
+
+void UIVisoBrowserBase::updateLocationSelectorText(const QString &strText)
+{
+ if (!m_pLocationSelector)
+ return;
+ m_pLocationSelector->updateLineEditText(strText);
+}
+
+void UIVisoBrowserBase::resizeEvent(QResizeEvent *pEvent)
+{
+ QIWithRetranslateUI<QGroupBox>::resizeEvent(pEvent);
+ if (m_pTreeView)
+ updateTreeViewGeometry(m_pTreeView->isVisible());
+}
+
+/* Close the tree view when it recieves focus-out and enter key press event: */
+bool UIVisoBrowserBase::eventFilter(QObject *pObj, QEvent *pEvent)
+{
+ /* Handle only events sent to m_pTreeView only: */
+ if (pObj != m_pTreeView)
+ return QIWithRetranslateUI<QGroupBox>::eventFilter(pObj, pEvent);
+
+ if (pEvent->type() == QEvent::KeyPress)
+ {
+ QKeyEvent *pKeyEvent = dynamic_cast<QKeyEvent*>(pEvent);
+ if (pKeyEvent &&
+ (pKeyEvent->key() == Qt::Key_Return ||
+ pKeyEvent->key() == Qt::Key_Enter))
+ {
+ updateTreeViewGeometry(false);
+ }
+ }
+ else if (pEvent->type() == QEvent::FocusOut)
+ {
+ updateTreeViewGeometry(false);
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QGroupBox>::eventFilter(pObj, pEvent);
+}
+
+void UIVisoBrowserBase::keyPressEvent(QKeyEvent *pEvent)
+{
+ if (pEvent->key() == Qt::Key_Escape)
+ {
+ if (m_pTreeView->isVisible())
+ updateTreeViewGeometry(false);
+
+ }
+ QIWithRetranslateUI<QGroupBox>::keyPressEvent(pEvent);
+}
+
+void UIVisoBrowserBase::sltFileTableViewContextMenu(const QPoint &point)
+{
+ QWidget *pSender = qobject_cast<QWidget*>(sender());
+ if (!pSender)
+ return;
+ emit sigCreateFileTableViewContextMenu(pSender, point);
+}
+
+void UIVisoBrowserBase::sltHandleTableViewItemDoubleClick(const QModelIndex &index)
+{
+ tableViewItemDoubleClick(index);
+}
+
+void UIVisoBrowserBase::sltHandleTreeSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
+{
+ Q_UNUSED(deselected);
+ QModelIndexList indices = selected.indexes();
+ if (indices.empty())
+ return;
+ QModelIndex selectedIndex = indices[0];
+ treeSelectionChanged(selectedIndex);
+}
+
+void UIVisoBrowserBase::sltHandleTreeItemClicked(const QModelIndex &modelIndex)
+{
+ if (!m_pTreeView)
+ return;
+ m_pTreeView->setExpanded(modelIndex, true);
+ updateTreeViewGeometry(false);
+ emit sigTreeViewVisibilityChanged(m_pTreeView->isVisible());
+}
+
+void UIVisoBrowserBase::sltExpandCollapseTreeView()
+{
+ if (!m_pTreeView)
+ return;
+ updateTreeViewGeometry(!m_pTreeView->isVisible());
+}
+
+void UIVisoBrowserBase::updateTreeViewGeometry(bool fShow)
+{
+ if (!m_pTreeView)
+ return;
+
+ if (!fShow)
+ {
+ if (!m_pTreeView->isVisible())
+ return;
+ else
+ {
+ m_pTreeView->hide();
+ emit sigTreeViewVisibilityChanged(m_pTreeView->isVisible());
+ m_pTreeView->clearFocus();
+ return;
+ }
+ }
+ if (!m_pLocationSelector)
+ return;
+
+ int iy = m_pLocationSelector->y() + m_pLocationSelector->height();
+ int ix = m_pLocationSelector->x();
+ int iWidth = m_pLocationSelector->lineEditWidth();
+ m_pTreeView-> move(ix, iy);
+ m_pTreeView->raise();
+ m_pTreeView->resize(iWidth, 0.75 * height());
+ m_pTreeView->show();
+ m_pTreeView->setFocus();
+ emit sigTreeViewVisibilityChanged(m_pTreeView->isVisible());
+}
+
+#include "UIVisoBrowserBase.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoBrowserBase.h b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoBrowserBase.h
new file mode 100644
index 00000000..c167f99a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoBrowserBase.h
@@ -0,0 +1,105 @@
+/* $Id: UIVisoBrowserBase.h $ */
+/** @file
+ * VBox Qt GUI - UIVisoBrowserBase class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_viso_UIVisoBrowserBase_h
+#define FEQT_INCLUDED_SRC_medium_viso_UIVisoBrowserBase_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QModelIndex>
+#include <QGroupBox>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QItemSelection;
+class QGridLayout;
+class QTreeView;
+class UILocationSelector;
+
+/** An abstract QWidget extension hosting a tree and table view. */
+class UIVisoBrowserBase : public QIWithRetranslateUI<QGroupBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigTreeViewVisibilityChanged(bool fVisible);
+ void sigCreateFileTableViewContextMenu(QWidget *pMenuRequester, const QPoint &point);
+
+public:
+
+ UIVisoBrowserBase(QWidget *pParent = 0);
+ ~UIVisoBrowserBase();
+ virtual void showHideHiddenObjects(bool bShow) = 0;
+ /* Returns true if tree view is currently visible: */
+ bool isTreeViewVisible() const;
+ void hideTreeView();
+ virtual bool tableViewHasSelection() const = 0;
+
+public slots:
+
+ void sltHandleTableViewItemDoubleClick(const QModelIndex &index);
+
+protected:
+
+ void prepareObjects();
+ void prepareConnections();
+ void updateLocationSelectorText(const QString &strText);
+
+ virtual void tableViewItemDoubleClick(const QModelIndex &index) = 0;
+ virtual void treeSelectionChanged(const QModelIndex &selectedTreeIndex) = 0;
+ virtual void setTableRootIndex(QModelIndex index = QModelIndex()) = 0;
+ virtual void setTreeCurrentIndex(QModelIndex index = QModelIndex()) = 0;
+
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+ virtual bool eventFilter(QObject *pObj, QEvent *pEvent) RT_OVERRIDE;
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+ QTreeView *m_pTreeView;
+ QGridLayout *m_pMainLayout;
+
+protected slots:
+
+ void sltFileTableViewContextMenu(const QPoint &point);
+
+private slots:
+
+ void sltHandleTreeSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+ void sltHandleTreeItemClicked(const QModelIndex &modelIndex);
+ void sltExpandCollapseTreeView();
+
+private:
+
+ void updateTreeViewGeometry(bool fShow);
+ UILocationSelector *m_pLocationSelector;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_viso_UIVisoBrowserBase_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoConfigurationPanel.cpp b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoConfigurationPanel.cpp
new file mode 100644
index 00000000..aa9d3a5e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoConfigurationPanel.cpp
@@ -0,0 +1,170 @@
+/* $Id: UIVisoConfigurationPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVisoConfigurationPanel class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+
+/* GUI includes: */
+#include "UIIconPool.h"
+#include "QILabel.h"
+#include "QILineEdit.h"
+#include "QIToolButton.h"
+#include "UIVisoConfigurationPanel.h"
+
+UIVisoConfigurationPanel::UIVisoConfigurationPanel(QWidget *pParent /* =0 */)
+ : UIDialogPanel(pParent)
+ , m_pVisoNameLabel(0)
+ , m_pCustomOptionsLabel(0)
+ , m_pVisoNameLineEdit(0)
+ , m_pCustomOptionsComboBox(0)
+ , m_pDeleteButton(0)
+{
+ prepareObjects();
+ prepareConnections();
+}
+
+UIVisoConfigurationPanel::~UIVisoConfigurationPanel()
+{
+}
+
+QString UIVisoConfigurationPanel::panelName() const
+{
+ return "ConfigurationPanel";
+}
+
+void UIVisoConfigurationPanel::setVisoName(const QString& strVisoName)
+{
+ if (m_pVisoNameLineEdit)
+ m_pVisoNameLineEdit->setText(strVisoName);
+}
+
+void UIVisoConfigurationPanel::setVisoCustomOptions(const QStringList& visoCustomOptions)
+{
+ if (!m_pCustomOptionsComboBox)
+ return;
+ m_pCustomOptionsComboBox->clear();
+ foreach (const QString &strOption, visoCustomOptions)
+ m_pCustomOptionsComboBox->addItem(strOption);
+}
+
+void UIVisoConfigurationPanel::prepareObjects()
+{
+ if (!mainLayout())
+ return;
+
+ /* Name edit and and label: */
+ m_pVisoNameLabel = new QILabel(QApplication::translate("UIVisoCreatorWidget", "VISO Name:"));
+ m_pVisoNameLineEdit = new QILineEdit;
+ if (m_pVisoNameLabel && m_pVisoNameLineEdit)
+ {
+ m_pVisoNameLabel->setBuddy(m_pVisoNameLineEdit);
+ mainLayout()->addWidget(m_pVisoNameLabel, 0, Qt::AlignLeft);
+ mainLayout()->addWidget(m_pVisoNameLineEdit, 0, Qt::AlignLeft);
+ }
+
+ addVerticalSeparator();
+
+ /* Cutom Viso options stuff: */
+ m_pCustomOptionsLabel = new QILabel(QApplication::translate("UIVisoCreatorWidget", "Custom VISO options:"));
+ m_pCustomOptionsComboBox = new QComboBox;
+ m_pDeleteButton = new QIToolButton;
+
+ if (m_pCustomOptionsLabel && m_pCustomOptionsComboBox && m_pDeleteButton)
+ {
+ m_pDeleteButton->setIcon(UIIconPool::iconSet(":/log_viewer_delete_current_bookmark_16px.png"));
+
+ m_pCustomOptionsComboBox->setEditable(true);
+ m_pCustomOptionsLabel->setBuddy(m_pCustomOptionsComboBox);
+
+ mainLayout()->addWidget(m_pCustomOptionsLabel, 0, Qt::AlignLeft);
+ mainLayout()->addWidget(m_pCustomOptionsComboBox, Qt::AlignLeft);
+ mainLayout()->addWidget(m_pDeleteButton, 0, Qt::AlignLeft);
+ }
+ retranslateUi();
+}
+
+void UIVisoConfigurationPanel::prepareConnections()
+{
+ if (m_pVisoNameLineEdit)
+ connect(m_pVisoNameLineEdit, &QILineEdit::editingFinished, this, &UIVisoConfigurationPanel::sltHandleVisoNameChanged);
+ if (m_pDeleteButton)
+ connect(m_pDeleteButton, &QIToolButton::clicked, this, &UIVisoConfigurationPanel::sltHandleDeleteCurrentCustomOption);
+}
+
+void UIVisoConfigurationPanel::retranslateUi()
+{
+ if (m_pVisoNameLabel)
+ m_pVisoNameLabel->setText(QApplication::translate("UIVisoCreatorWidget", "VISO Name:"));
+ if (m_pCustomOptionsLabel)
+ m_pCustomOptionsLabel->setText(QApplication::translate("UIVisoCreatorWidget", "Custom VISO options:"));
+ if (m_pDeleteButton)
+ m_pDeleteButton->setToolTip(QApplication::translate("UIVisoCreatorWidget", "Remove current option."));
+ if (m_pVisoNameLineEdit)
+ m_pVisoNameLineEdit->setToolTip(QApplication::translate("UIVisoCreatorWidget", "Holds the name of the VISO medium."));
+ if (m_pCustomOptionsComboBox)
+ m_pCustomOptionsComboBox->setToolTip(QApplication::translate("UIVisoCreatorWidget", "Holds options for VISO creation."));
+}
+
+void UIVisoConfigurationPanel::addCustomVisoOption()
+{
+ if (!m_pCustomOptionsComboBox)
+ return;
+ if (m_pCustomOptionsComboBox->currentText().isEmpty())
+ return;
+
+ emitCustomVisoOptions();
+ m_pCustomOptionsComboBox->clearEditText();
+}
+
+void UIVisoConfigurationPanel::emitCustomVisoOptions()
+{
+ if (!m_pCustomOptionsComboBox)
+ return;
+ QStringList customVisoOptions;
+ for (int i = 0; i < m_pCustomOptionsComboBox->count(); ++i)
+ customVisoOptions << m_pCustomOptionsComboBox->itemText(i);
+
+ if (!customVisoOptions.isEmpty())
+ emit sigCustomVisoOptionsChanged(customVisoOptions);
+}
+
+void UIVisoConfigurationPanel::sltHandleVisoNameChanged()
+{
+ if (m_pVisoNameLineEdit)
+ emit sigVisoNameChanged(m_pVisoNameLineEdit->text());
+}
+
+void UIVisoConfigurationPanel::sltHandleDeleteCurrentCustomOption()
+{
+ if (!m_pCustomOptionsComboBox)
+ return;
+ if (m_pCustomOptionsComboBox->currentText().isEmpty())
+ return;
+ m_pCustomOptionsComboBox->removeItem(m_pCustomOptionsComboBox->currentIndex());
+ emitCustomVisoOptions();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoConfigurationPanel.h b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoConfigurationPanel.h
new file mode 100644
index 00000000..2333367e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoConfigurationPanel.h
@@ -0,0 +1,82 @@
+/* $Id: UIVisoConfigurationPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVisoConfigurationPanel class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_viso_UIVisoConfigurationPanel_h
+#define FEQT_INCLUDED_SRC_medium_viso_UIVisoConfigurationPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIDialogPanel.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QILabel;
+class QILineEdit;
+class QIToolButton;
+
+class UIVisoConfigurationPanel : public UIDialogPanel
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigVisoNameChanged(const QString &strVisoName);
+ void sigCustomVisoOptionsChanged(const QStringList &customVisoOptions);
+
+public:
+ UIVisoConfigurationPanel(QWidget *pParent = 0);
+ ~UIVisoConfigurationPanel();
+ virtual QString panelName() const RT_OVERRIDE;
+ void setVisoName(const QString& strVisoName);
+ void setVisoCustomOptions(const QStringList& visoCustomOptions);
+
+protected:
+
+ void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ void sltHandleVisoNameChanged();
+ void sltHandleDeleteCurrentCustomOption();
+
+private:
+
+ void prepareObjects();
+ void prepareConnections();
+ void addCustomVisoOption();
+ void emitCustomVisoOptions();
+
+ QILabel *m_pVisoNameLabel;
+ QILabel *m_pCustomOptionsLabel;
+ QILineEdit *m_pVisoNameLineEdit;
+ QComboBox *m_pCustomOptionsComboBox;
+ QIToolButton *m_pDeleteButton;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_viso_UIVisoConfigurationPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoContentBrowser.cpp b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoContentBrowser.cpp
new file mode 100644
index 00000000..76fbd6a4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoContentBrowser.cpp
@@ -0,0 +1,786 @@
+/* $Id: UIVisoContentBrowser.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVisoContentBrowser class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/* Qt includes: */
+#include <QDir>
+#include <QFileInfo>
+#include <QGridLayout>
+#include <QHeaderView>
+#include <QMimeData>
+#include <QTableView>
+#include <QTreeView>
+
+/* GUI includes: */
+#include "UICustomFileSystemModel.h"
+#include "UIPathOperations.h"
+#include "UIVisoContentBrowser.h"
+
+/*********************************************************************************************************************************
+* UIVisoContentTableView definition. *
+*********************************************************************************************************************************/
+
+/** An QTableView extension mainly used to handle dropeed file objects from the host browser. */
+class UIVisoContentTableView : public QTableView
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigNewItemsDropped(QStringList pathList);
+
+public:
+
+ UIVisoContentTableView(QWidget *pParent = 0);
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dropEvent(QDropEvent *event);
+ void dragMoveEvent(QDragMoveEvent *event);
+};
+
+
+/*********************************************************************************************************************************
+* UIVisoContentTreeProxyModel definition. *
+*********************************************************************************************************************************/
+
+class UIVisoContentTreeProxyModel : public UICustomFileSystemProxyModel
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIVisoContentTreeProxyModel(QObject *parent = 0);
+
+protected:
+
+ /** Used to filter-out files and show only directories. */
+ virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const RT_OVERRIDE;
+};
+
+
+/*********************************************************************************************************************************
+* UIVisoContentTableView implementation. *
+*********************************************************************************************************************************/
+UIVisoContentTableView::UIVisoContentTableView(QWidget *pParent /* = 0 */)
+ :QTableView(pParent)
+{
+}
+
+void UIVisoContentTableView::dragMoveEvent(QDragMoveEvent *event)
+{
+ event->acceptProposedAction();
+
+}
+
+void UIVisoContentTableView::dragEnterEvent(QDragEnterEvent *pEvent)
+{
+ if (pEvent->mimeData()->hasFormat("application/vnd.text.list"))
+ pEvent->accept();
+ else
+ pEvent->ignore();
+}
+
+void UIVisoContentTableView::dropEvent(QDropEvent *pEvent)
+{
+ if (pEvent->mimeData()->hasFormat("application/vnd.text.list"))
+ {
+ QByteArray itemData = pEvent->mimeData()->data("application/vnd.text.list");
+ QDataStream stream(&itemData, QIODevice::ReadOnly);
+ QStringList pathList;
+
+ while (!stream.atEnd()) {
+ QString text;
+ stream >> text;
+ pathList << text;
+ }
+ emit sigNewItemsDropped(pathList);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* UIVisoContentTreeProxyModel implementation. *
+*********************************************************************************************************************************/
+
+UIVisoContentTreeProxyModel::UIVisoContentTreeProxyModel(QObject *parent /* = 0 */)
+ :UICustomFileSystemProxyModel(parent)
+{
+}
+
+bool UIVisoContentTreeProxyModel::filterAcceptsRow(int iSourceRow, const QModelIndex &sourceParent) const /* override */
+{
+ QModelIndex itemIndex = sourceModel()->index(iSourceRow, 0, sourceParent);
+ if (!itemIndex.isValid())
+ return false;
+
+ UICustomFileSystemItem *item = static_cast<UICustomFileSystemItem*>(itemIndex.internalPointer());
+ if (!item)
+ return false;
+
+ if (item->isUpDirectory())
+ return false;
+ if (item->isDirectory() || item->isSymLinkToADirectory())
+ return true;
+
+ return false;
+}
+
+
+/*********************************************************************************************************************************
+* UIVisoContentBrowser implementation. *
+*********************************************************************************************************************************/
+
+UIVisoContentBrowser::UIVisoContentBrowser(QWidget *pParent)
+ : UIVisoBrowserBase(pParent)
+ , m_pTableView(0)
+ , m_pModel(0)
+ , m_pTableProxyModel(0)
+ , m_pTreeProxyModel(0)
+{
+ prepareObjects();
+ prepareConnections();
+
+ /* Assuming the root items only child is the one with the path '/', navigate into it. */
+ /* Hack alert. for some reason without invalidating proxy models mapFromSource return invalid index. */
+ if (m_pTableProxyModel)
+ m_pTableProxyModel->invalidate();
+ if (m_pTreeProxyModel)
+ m_pTreeProxyModel->setSourceModel(m_pModel);
+ if (rootItem() && rootItem()->childCount() > 0)
+ {
+ UICustomFileSystemItem *pStartItem = static_cast<UICustomFileSystemItem*>(rootItem()->children()[0]);
+ if (pStartItem)
+ {
+ QModelIndex iindex = m_pModel->index(pStartItem);
+ if (iindex.isValid())
+ tableViewItemDoubleClick(convertIndexToTableIndex(iindex));
+ }
+ }
+}
+
+UIVisoContentBrowser::~UIVisoContentBrowser()
+{
+}
+
+void UIVisoContentBrowser::addObjectsToViso(QStringList pathList)
+{
+ if (!m_pTableView)
+ return;
+
+ QModelIndex parentIndex = m_pTableProxyModel->mapToSource(m_pTableView->rootIndex());
+ if (!parentIndex.isValid())
+ return;
+
+ UICustomFileSystemItem *pParentItem = static_cast<UICustomFileSystemItem*>(parentIndex.internalPointer());
+ if (!pParentItem)
+ return;
+ foreach (const QString &strPath, pathList)
+ {
+ QFileInfo fileInfo(strPath);
+ if (!fileInfo.exists())
+ continue;
+ if (pParentItem->child(fileInfo.fileName()))
+ continue;
+
+ UICustomFileSystemItem* pAddedItem = new UICustomFileSystemItem(fileInfo.fileName(), pParentItem,
+ fileType(fileInfo));
+ pAddedItem->setData(strPath, UICustomFileSystemModelColumn_LocalPath);
+ pAddedItem->setData(UIPathOperations::mergePaths(pParentItem->path(), fileInfo.fileName()),
+ UICustomFileSystemModelColumn_Path);
+ pAddedItem->setIsOpened(false);
+ if (fileInfo.isSymLink())
+ {
+ pAddedItem->setTargetPath(fileInfo.symLinkTarget());
+ pAddedItem->setIsSymLinkToADirectory(QFileInfo(fileInfo.symLinkTarget()).isDir());
+ }
+ createAnIsoEntry(pAddedItem);
+ }
+ if (m_pTableProxyModel)
+ m_pTableProxyModel->invalidate();
+ if (m_pTreeProxyModel)
+ {
+ m_pTreeProxyModel->invalidate();
+ m_pTreeView->setExpanded(m_pTreeView->currentIndex(), true);
+ }
+}
+
+void UIVisoContentBrowser::createAnIsoEntry(UICustomFileSystemItem *pItem, bool bRemove /* = false */)
+{
+ if (!pItem)
+ return;
+ if (pItem->data(UICustomFileSystemModelColumn_Path).toString().isEmpty())
+ return;
+
+ if (!bRemove && pItem->data(UICustomFileSystemModelColumn_LocalPath).toString().isEmpty())
+ return;
+ if (!bRemove)
+ m_entryMap.insert(pItem->data(UICustomFileSystemModelColumn_Path).toString(),
+ pItem->data(UICustomFileSystemModelColumn_LocalPath).toString());
+ else
+ m_entryMap.insert(pItem->data(UICustomFileSystemModelColumn_Path).toString(),
+ ":remove:");
+}
+
+QStringList UIVisoContentBrowser::entryList()
+{
+ QStringList entryList;
+ for (QMap<QString, QString>::const_iterator iterator = m_entryMap.begin(); iterator != m_entryMap.end(); ++iterator)
+ {
+ QString strEntry = QString("%1=%2").arg(iterator.key()).arg(iterator.value());
+ entryList << strEntry;
+ }
+ return entryList;
+}
+
+void UIVisoContentBrowser::retranslateUi()
+{
+ UICustomFileSystemItem *pRootItem = rootItem();
+ if (pRootItem)
+ {
+ pRootItem->setData(QApplication::translate("UIVisoCreatorWidget", "Name"), UICustomFileSystemModelColumn_Name);
+ pRootItem->setData(QApplication::translate("UIVisoCreatorWidget", "Size"), UICustomFileSystemModelColumn_Size);
+ pRootItem->setData(QApplication::translate("UIVisoCreatorWidget", "Change Time"), UICustomFileSystemModelColumn_ChangeTime);
+ pRootItem->setData(QApplication::translate("UIVisoCreatorWidget", "Owner"), UICustomFileSystemModelColumn_Owner);
+ pRootItem->setData(QApplication::translate("UIVisoCreatorWidget", "Permissions"), UICustomFileSystemModelColumn_Permissions);
+ pRootItem->setData(QApplication::translate("UIVisoCreatorWidget", "Local Path"), UICustomFileSystemModelColumn_LocalPath);
+ pRootItem->setData(QApplication::translate("UIVisoCreatorWidget", "ISO Path"), UICustomFileSystemModelColumn_Path);
+ }
+}
+
+void UIVisoContentBrowser::tableViewItemDoubleClick(const QModelIndex &index)
+{
+ if (!index.isValid() || !m_pTableProxyModel)
+ return;
+ UICustomFileSystemItem *pClickedItem =
+ static_cast<UICustomFileSystemItem*>(m_pTableProxyModel->mapToSource(index).internalPointer());
+ if (pClickedItem->isUpDirectory())
+ {
+ QModelIndex currentRoot = m_pTableProxyModel->mapToSource(m_pTableView->rootIndex());
+ /* Go up if we are not already there: */
+ if (currentRoot != m_pModel->rootIndex())
+ {
+ setTableRootIndex(currentRoot.parent());
+ setTreeCurrentIndex(currentRoot.parent());
+ }
+ }
+ else
+ {
+ scanHostDirectory(pClickedItem);
+ setTableRootIndex(index);
+ setTreeCurrentIndex(index);
+ }
+}
+
+void UIVisoContentBrowser::sltHandleCreateNewDirectory()
+{
+ if (!m_pTableView)
+ return;
+ QString strNewDirectoryName("NewDirectory");
+
+ QModelIndex parentIndex = m_pTableProxyModel->mapToSource(m_pTableView->rootIndex());
+ if (!parentIndex.isValid())
+ return;
+
+ UICustomFileSystemItem *pParentItem = static_cast<UICustomFileSystemItem*>(parentIndex.internalPointer());
+ if (!pParentItem)
+ return;
+
+ /* Check to see if we already have a directory named strNewDirectoryName: */
+ const QList<UICustomFileSystemItem*> children = pParentItem->children();
+ foreach (const UICustomFileSystemItem *item, children)
+ {
+ if (item->name() == strNewDirectoryName)
+ return;
+ }
+
+ UICustomFileSystemItem* pAddedItem = new UICustomFileSystemItem(strNewDirectoryName, pParentItem,
+ KFsObjType_Directory);
+ pAddedItem->setData(UIPathOperations::mergePaths(pParentItem->path(), strNewDirectoryName), UICustomFileSystemModelColumn_Path);
+
+ pAddedItem->setIsOpened(false);
+ if (m_pTableProxyModel)
+ m_pTableProxyModel->invalidate();
+
+ renameFileObject(pAddedItem);
+}
+
+void UIVisoContentBrowser::sltHandleRemoveItems()
+{
+ removeItems(tableSelectedItems());
+}
+
+void UIVisoContentBrowser::removeItems(const QList<UICustomFileSystemItem*> itemList)
+{
+ foreach(UICustomFileSystemItem *pItem, itemList)
+ {
+ if (!pItem)
+ continue;
+ QString strIsoPath = pItem->data(UICustomFileSystemModelColumn_Path).toString();
+ if (strIsoPath.isEmpty())
+ continue;
+
+ bool bFoundInMap = false;
+ for (QMap<QString, QString>::iterator iterator = m_entryMap.begin(); iterator != m_entryMap.end(); )
+ {
+ if (iterator.key().startsWith(strIsoPath))
+ {
+ iterator = m_entryMap.erase(iterator);
+ bFoundInMap = true;
+ }
+ else
+ ++iterator;
+ }
+ if (!bFoundInMap)
+ createAnIsoEntry(pItem, true /* bool bRemove */);
+ }
+
+ foreach(UICustomFileSystemItem *pItem, itemList)
+ {
+ if (!pItem)
+ continue;
+ /* Remove the item from the m_pModel: */
+ if (m_pModel)
+ m_pModel->deleteItem(pItem);
+ }
+ if (m_pTreeProxyModel)
+ m_pTreeProxyModel->invalidate();
+ if (m_pTableProxyModel)
+ m_pTableProxyModel->invalidate();
+
+}
+
+void UIVisoContentBrowser::prepareObjects()
+{
+ UIVisoBrowserBase::prepareObjects();
+
+ m_pModel = new UICustomFileSystemModel(this);
+ m_pTableProxyModel = new UICustomFileSystemProxyModel(this);
+ if (m_pTableProxyModel)
+ {
+ m_pTableProxyModel->setSourceModel(m_pModel);
+ m_pTableProxyModel->setListDirectoriesOnTop(true);
+ }
+
+ m_pTreeProxyModel = new UIVisoContentTreeProxyModel(this);
+ if (m_pTreeProxyModel)
+ {
+ m_pTreeProxyModel->setSourceModel(m_pModel);
+ }
+
+ initializeModel();
+
+ if (m_pTreeView)
+ {
+ m_pTreeView->setModel(m_pTreeProxyModel);
+ m_pTreeView->setCurrentIndex(m_pTreeProxyModel->mapFromSource(m_pModel->rootIndex()));
+ m_pTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ /* Show only the 0th column that is "name': */
+ m_pTreeView->hideColumn(UICustomFileSystemModelColumn_Owner);
+ m_pTreeView->hideColumn(UICustomFileSystemModelColumn_Permissions);
+ m_pTreeView->hideColumn(UICustomFileSystemModelColumn_Size);
+ m_pTreeView->hideColumn(UICustomFileSystemModelColumn_ChangeTime);
+ m_pTreeView->hideColumn(UICustomFileSystemModelColumn_Path);
+ m_pTreeView->hideColumn(UICustomFileSystemModelColumn_LocalPath);
+ }
+
+ m_pTableView = new UIVisoContentTableView;
+ if (m_pTableView)
+ {
+ m_pMainLayout->addWidget(m_pTableView, 1, 0, 6, 4);
+ m_pTableView->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ m_pTableView->setShowGrid(false);
+ m_pTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ m_pTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ m_pTableView->setAlternatingRowColors(true);
+ m_pTableView->setTabKeyNavigation(false);
+ QHeaderView *pVerticalHeader = m_pTableView->verticalHeader();
+ if (pVerticalHeader)
+ {
+ m_pTableView->verticalHeader()->setVisible(false);
+ /* Minimize the row height: */
+ m_pTableView->verticalHeader()->setDefaultSectionSize(m_pTableView->verticalHeader()->minimumSectionSize());
+ }
+ QHeaderView *pHorizontalHeader = m_pTableView->horizontalHeader();
+ if (pHorizontalHeader)
+ {
+ pHorizontalHeader->setHighlightSections(false);
+ pHorizontalHeader->setSectionResizeMode(QHeaderView::Stretch);
+ }
+
+ m_pTableView->setModel(m_pTableProxyModel);
+ setTableRootIndex();
+ m_pTableView->hideColumn(UICustomFileSystemModelColumn_Owner);
+ m_pTableView->hideColumn(UICustomFileSystemModelColumn_Permissions);
+ m_pTableView->hideColumn(UICustomFileSystemModelColumn_Size);
+ m_pTableView->hideColumn(UICustomFileSystemModelColumn_ChangeTime);
+
+ m_pTableView->setSortingEnabled(true);
+ m_pTableView->sortByColumn(0, Qt::AscendingOrder);
+
+ m_pTableView->setDragEnabled(false);
+ m_pTableView->setAcceptDrops(true);
+ m_pTableView->setDropIndicatorShown(true);
+ m_pTableView->setDragDropMode(QAbstractItemView::DropOnly);
+ }
+ retranslateUi();
+}
+
+void UIVisoContentBrowser::prepareConnections()
+{
+ UIVisoBrowserBase::prepareConnections();
+
+ if (m_pTableView)
+ {
+ connect(m_pTableView, &UIVisoContentTableView::doubleClicked,
+ this, &UIVisoBrowserBase::sltHandleTableViewItemDoubleClick);
+ connect(m_pTableView, &UIVisoContentTableView::sigNewItemsDropped,
+ this, &UIVisoContentBrowser::sltHandleDroppedItems);
+ connect(m_pTableView, &QTableView::customContextMenuRequested,
+ this, &UIVisoContentBrowser::sltFileTableViewContextMenu);
+ }
+
+ if (m_pTableView->selectionModel())
+ connect(m_pTableView->selectionModel(), &QItemSelectionModel::selectionChanged,
+ this, &UIVisoContentBrowser::sltHandleTableSelectionChanged);
+ if (m_pModel)
+ connect(m_pModel, &UICustomFileSystemModel::sigItemRenamed,
+ this, &UIVisoContentBrowser::sltHandleItemRenameAttempt);
+}
+
+UICustomFileSystemItem* UIVisoContentBrowser::rootItem()
+{
+ if (!m_pModel)
+ return 0;
+ return m_pModel->rootItem();
+}
+
+void UIVisoContentBrowser::initializeModel()
+{
+ if (m_pModel)
+ m_pModel->reset();
+ if (!rootItem())
+ return;
+
+ const QString startPath = QString("/%1").arg(m_strVisoName);
+
+ UICustomFileSystemItem *pStartItem = new UICustomFileSystemItem(startPath, rootItem(), KFsObjType_Directory);
+ pStartItem->setPath("/");
+ pStartItem->setIsOpened(false);
+}
+
+void UIVisoContentBrowser::setTableRootIndex(QModelIndex index /* = QModelIndex */)
+{
+ if (!m_pTableView)
+ return;
+
+ QModelIndex tableIndex;
+ if (index.isValid())
+ {
+ tableIndex = convertIndexToTableIndex(index);
+ if (tableIndex.isValid())
+ m_pTableView->setRootIndex(tableIndex);
+ }
+ else
+ {
+ if (m_pTreeView && m_pTreeView->selectionModel())
+ {
+ QItemSelectionModel *selectionModel = m_pTreeView->selectionModel();
+ if (!selectionModel->selectedIndexes().isEmpty())
+ {
+ QModelIndex treeIndex = selectionModel->selectedIndexes().at(0);
+ tableIndex = convertIndexToTableIndex(treeIndex);
+ if (tableIndex.isValid())
+ m_pTableView->setRootIndex(tableIndex);
+ }
+ }
+ }
+ if (tableIndex.isValid())
+ {
+ UICustomFileSystemItem *pItem =
+ static_cast<UICustomFileSystemItem*>(m_pTableProxyModel->mapToSource(tableIndex).internalPointer());
+ if (pItem)
+ {
+ QString strPath = pItem->data(UICustomFileSystemModelColumn_Path).toString();
+ updateLocationSelectorText(strPath);
+ }
+ }
+}
+
+void UIVisoContentBrowser::setTreeCurrentIndex(QModelIndex index /* = QModelIndex() */)
+{
+ if (!m_pTreeView)
+ return;
+ QItemSelectionModel *pSelectionModel = m_pTreeView->selectionModel();
+ if (!pSelectionModel)
+ return;
+ m_pTreeView->blockSignals(true);
+ pSelectionModel->blockSignals(true);
+ QModelIndex treeIndex;
+ if (index.isValid())
+ {
+ treeIndex = convertIndexToTreeIndex(index);
+ }
+ else
+ {
+ QItemSelectionModel *selectionModel = m_pTableView->selectionModel();
+ if (selectionModel)
+ {
+ if (!selectionModel->selectedIndexes().isEmpty())
+ {
+ QModelIndex tableIndex = selectionModel->selectedIndexes().at(0);
+ treeIndex = convertIndexToTreeIndex(tableIndex);
+ }
+ }
+ }
+
+ if (treeIndex.isValid())
+ {
+ m_pTreeView->setCurrentIndex(treeIndex);
+ m_pTreeView->setExpanded(treeIndex, true);
+ m_pTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter);
+ m_pTreeProxyModel->invalidate();
+ }
+ pSelectionModel->blockSignals(false);
+ m_pTreeView->blockSignals(false);
+}
+
+void UIVisoContentBrowser::treeSelectionChanged(const QModelIndex &selectedTreeIndex)
+{
+ if (!m_pTableProxyModel || !m_pTreeProxyModel)
+ return;
+
+ /* Check if we need to scan the directory in the host system: */
+ UICustomFileSystemItem *pClickedItem =
+ static_cast<UICustomFileSystemItem*>(m_pTreeProxyModel->mapToSource(selectedTreeIndex).internalPointer());
+ scanHostDirectory(pClickedItem);
+ setTableRootIndex(selectedTreeIndex);
+ m_pTableProxyModel->invalidate();
+ m_pTreeProxyModel->invalidate();
+}
+
+void UIVisoContentBrowser::showHideHiddenObjects(bool bShow)
+{
+ Q_UNUSED(bShow);
+}
+
+void UIVisoContentBrowser::setVisoName(const QString &strName)
+{
+ if (m_strVisoName == strName)
+ return;
+ m_strVisoName = strName;
+ updateStartItemName();
+}
+
+bool UIVisoContentBrowser::tableViewHasSelection() const
+{
+ if (!m_pTableView)
+ return false;
+ QItemSelectionModel *pSelectionModel = m_pTableView->selectionModel();
+ if (!pSelectionModel)
+ return false;
+ return pSelectionModel->hasSelection();
+}
+
+QModelIndex UIVisoContentBrowser::convertIndexToTableIndex(const QModelIndex &index)
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ if (index.model() == m_pTableProxyModel)
+ return index;
+ else if (index.model() == m_pModel)
+ return m_pTableProxyModel->mapFromSource(index);
+ else if (index.model() == m_pTreeProxyModel)
+ return m_pTableProxyModel->mapFromSource(m_pTreeProxyModel->mapToSource(index));
+ return QModelIndex();
+}
+
+QModelIndex UIVisoContentBrowser::convertIndexToTreeIndex(const QModelIndex &index)
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ if (index.model() == m_pTreeProxyModel)
+ return index;
+ else if (index.model() == m_pModel)
+ return m_pTreeProxyModel->mapFromSource(index);
+ else if (index.model() == m_pTableProxyModel)
+ return m_pTreeProxyModel->mapFromSource(m_pTableProxyModel->mapToSource(index));
+ return QModelIndex();
+}
+
+void UIVisoContentBrowser::scanHostDirectory(UICustomFileSystemItem *directoryItem)
+{
+ if (!directoryItem)
+ return;
+ /* the clicked item can be a directory created with the VISO content. in that case local path data
+ should be empty: */
+ if (directoryItem->type() != KFsObjType_Directory ||
+ directoryItem->data(UICustomFileSystemModelColumn_LocalPath).toString().isEmpty())
+ return;
+ QDir directory(directoryItem->data(UICustomFileSystemModelColumn_LocalPath).toString());
+ if (directory.exists() && !directoryItem->isOpened())
+ {
+ QFileInfoList directoryContent = directory.entryInfoList();
+ for (int i = 0; i < directoryContent.size(); ++i)
+ {
+ const QFileInfo &fileInfo = directoryContent[i];
+ if (fileInfo.fileName() == ".")
+ continue;
+ UICustomFileSystemItem *newItem = new UICustomFileSystemItem(fileInfo.fileName(),
+ directoryItem,
+ fileType(fileInfo));
+ newItem->setData(fileInfo.filePath(), UICustomFileSystemModelColumn_LocalPath);
+
+ newItem->setData(UIPathOperations::mergePaths(directoryItem->path(), fileInfo.fileName()),
+ UICustomFileSystemModelColumn_Path);
+ if (fileInfo.isSymLink())
+ {
+ newItem->setTargetPath(fileInfo.symLinkTarget());
+ newItem->setIsSymLinkToADirectory(QFileInfo(fileInfo.symLinkTarget()).isDir());
+ }
+ }
+ directoryItem->setIsOpened(true);
+ }
+}
+
+/* static */ KFsObjType UIVisoContentBrowser::fileType(const QFileInfo &fsInfo)
+{
+ if (!fsInfo.exists())
+ return KFsObjType_Unknown;
+ /* first check if it is symlink becacuse for Qt
+ being smylin and directory/file is not mutually exclusive: */
+ if (fsInfo.isSymLink())
+ return KFsObjType_Symlink;
+ else if (fsInfo.isFile())
+ return KFsObjType_File;
+ else if (fsInfo.isDir())
+ return KFsObjType_Directory;
+
+ return KFsObjType_Unknown;
+}
+
+void UIVisoContentBrowser::updateStartItemName()
+{
+ if (!rootItem() || !rootItem()->child(0))
+ return;
+ const QString strName(QDir::toNativeSeparators("/"));
+
+ rootItem()->child(0)->setData(strName, UICustomFileSystemModelColumn_Name);
+ /* If the table root index is the start item then we have to update the location selector text here: */
+ if (m_pTableProxyModel->mapToSource(m_pTableView->rootIndex()).internalPointer() == rootItem()->child(0))
+ updateLocationSelectorText(strName);
+ m_pTreeProxyModel->invalidate();
+ m_pTableProxyModel->invalidate();
+}
+
+void UIVisoContentBrowser::renameFileObject(UICustomFileSystemItem *pItem)
+{
+ m_pTableView->edit(m_pTableProxyModel->mapFromSource(m_pModel->index(pItem)));
+}
+
+void UIVisoContentBrowser::sltHandleItemRenameAction()
+{
+ QList<UICustomFileSystemItem*> selectedItems = tableSelectedItems();
+ if (selectedItems.empty())
+ return;
+ /* This is not complete. we have to modify the entries in the m_entryMap as well: */
+ renameFileObject(selectedItems.at(0));
+}
+
+void UIVisoContentBrowser::sltHandleItemRenameAttempt(UICustomFileSystemItem *pItem, QString strOldName, QString strNewName)
+{
+ if (!pItem || !pItem->parentItem())
+ return;
+ QList<UICustomFileSystemItem*> children = pItem->parentItem()->children();
+ bool bDuplicate = false;
+ foreach (const UICustomFileSystemItem *item, children)
+ {
+ if (item->name() == strNewName && item != pItem)
+ bDuplicate = true;
+ }
+
+ if (bDuplicate)
+ {
+ /* Restore the previous name in case the @strNewName is a duplicate: */
+ pItem->setData(strOldName, static_cast<int>(UICustomFileSystemModelColumn_Name));
+ }
+
+ pItem->setData(UIPathOperations::mergePaths(pItem->parentItem()->path(), pItem->name()), UICustomFileSystemModelColumn_Path);
+ if (m_pTableProxyModel)
+ m_pTableProxyModel->invalidate();
+}
+
+void UIVisoContentBrowser::sltHandleTableSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
+{
+ Q_UNUSED(deselected);
+ emit sigTableSelectionChanged(selected.isEmpty());
+}
+
+void UIVisoContentBrowser::sltHandleResetAction()
+{
+ if (!rootItem() || !rootItem()->child(0))
+ return;
+ rootItem()->child(0)->removeChildren();
+ m_entryMap.clear();
+ if (m_pTableProxyModel)
+ m_pTableProxyModel->invalidate();
+ if (m_pTreeProxyModel)
+ m_pTreeProxyModel->invalidate();
+}
+
+void UIVisoContentBrowser::sltHandleDroppedItems(QStringList pathList)
+{
+ addObjectsToViso(pathList);
+}
+
+void UIVisoContentBrowser::reset()
+{
+ m_entryMap.clear();
+}
+
+QList<UICustomFileSystemItem*> UIVisoContentBrowser::tableSelectedItems()
+{
+ QList<UICustomFileSystemItem*> selectedItems;
+ if (!m_pTableProxyModel)
+ return selectedItems;
+ QItemSelectionModel *selectionModel = m_pTableView->selectionModel();
+ if (!selectionModel || selectionModel->selectedIndexes().isEmpty())
+ return selectedItems;
+ QModelIndexList list = selectionModel->selectedRows();
+ foreach (QModelIndex index, list)
+ {
+ UICustomFileSystemItem *pItem =
+ static_cast<UICustomFileSystemItem*>(m_pTableProxyModel->mapToSource(index).internalPointer());
+ if (pItem)
+ selectedItems << pItem;
+ }
+ return selectedItems;
+}
+
+#include "UIVisoContentBrowser.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoContentBrowser.h b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoContentBrowser.h
new file mode 100644
index 00000000..6a392633
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoContentBrowser.h
@@ -0,0 +1,141 @@
+/* $Id: UIVisoContentBrowser.h $ */
+/** @file
+ * VBox Qt GUI - UIVisoContentBrowser class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_viso_UIVisoContentBrowser_h
+#define FEQT_INCLUDED_SRC_medium_viso_UIVisoContentBrowser_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UIVisoBrowserBase.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QFileInfo;
+class UICustomFileSystemItem;
+class UICustomFileSystemModel;
+class UICustomFileSystemProxyModel;
+class UIVisoContentTreeProxyModel;
+class UIVisoContentTableView;
+
+/** A UIVisoBrowserBase extension to view content of a VISO as a file tree. */
+class UIVisoContentBrowser : public UIVisoBrowserBase
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigTableSelectionChanged(bool fIsSelectionEmpty);
+
+public:
+
+ UIVisoContentBrowser(QWidget *pParent = 0);
+ ~UIVisoContentBrowser();
+ /** Adds file objests from the host file system. @p pathList consists of list of paths to there objects. */
+ void addObjectsToViso(QStringList pathList);
+ /** Returns the content of the VISO as a string list. Each element of the list becomes a line in the
+ * .viso file. */
+ QStringList entryList();
+ virtual void showHideHiddenObjects(bool bShow) override final;
+ void setVisoName(const QString &strName);
+ virtual bool tableViewHasSelection() const final override;
+
+public slots:
+
+ void sltHandleCreateNewDirectory();
+ /** Handles the signal we get from the model during setData call. Restores the old name of the file object
+ * to @p strOldName if need be (if rename fails for some reason). */
+ void sltHandleItemRenameAttempt(UICustomFileSystemItem *pItem, QString strOldName, QString strNewName);
+ void sltHandleRemoveItems();
+ void sltHandleResetAction();
+ void sltHandleItemRenameAction();
+
+protected:
+
+ void retranslateUi() final override;
+ virtual void tableViewItemDoubleClick(const QModelIndex &index) final override;
+ /** @name Functions to set view root indices explicitly. They block the related signals. @p is converted
+ to the correct index before setting.
+ * @{ */
+ virtual void setTableRootIndex(QModelIndex index = QModelIndex()) final override;
+ virtual void setTreeCurrentIndex(QModelIndex index = QModelIndex()) final override;
+ /** @} */
+
+ virtual void treeSelectionChanged(const QModelIndex &selectedTreeIndex) final override;
+
+private slots:
+
+ void sltHandleTableSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+ /** Adds the dragged-dropped items to VISO. */
+ void sltHandleDroppedItems(QStringList pathList);
+
+private:
+
+ void prepareObjects();
+ void prepareConnections();
+ void initializeModel();
+ UICustomFileSystemItem *rootItem();
+
+ /** @name Index conversion functions. These are half-smart and tries to determine the source model before conversion.
+ * @{ */
+ QModelIndex convertIndexToTableIndex(const QModelIndex &index);
+ QModelIndex convertIndexToTreeIndex(const QModelIndex &index);
+ /** @} */
+ /** Lists the content of the host file system directory by using Qt file system API. */
+ void scanHostDirectory(UICustomFileSystemItem *directory);
+ KFsObjType fileType(const QFileInfo &fsInfo);
+ /** Renames the starts item's name as VISO name changes. */
+ void updateStartItemName();
+ void renameFileObject(UICustomFileSystemItem *pItem);
+ void removeItems(const QList<UICustomFileSystemItem*> itemList);
+ /** Creates and entry for pItem consisting of a map item (key is iso path and value is host file system path)
+ * if @p bRemove is true then the value is the string ":remove:" which effectively removes the file object
+ * from the iso image. */
+ void createAnIsoEntry(UICustomFileSystemItem *pItem, bool bRemove = false);
+ void reset();
+ /** Returns a list of items which are currecntly selected
+ * in the table view. */
+ QList<UICustomFileSystemItem*> tableSelectedItems();
+ UIVisoContentTableView *m_pTableView;
+ UICustomFileSystemModel *m_pModel;
+ UICustomFileSystemProxyModel *m_pTableProxyModel;
+ UIVisoContentTreeProxyModel *m_pTreeProxyModel;
+
+ QString m_strVisoName;
+ /** keys of m_entryMap are iso locations and values are
+ * local location of file objects. these keys and values are
+ * concatenated and passed to the client to create ad-hoc.viso entries. */
+ QMap<QString, QString> m_entryMap;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_viso_UIVisoContentBrowser_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreator.cpp b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreator.cpp
new file mode 100644
index 00000000..35a2e8ac
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreator.cpp
@@ -0,0 +1,779 @@
+/* $Id: UIVisoCreator.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVisoCreator classes implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QMenuBar>
+#include <QPushButton>
+#include <QStyle>
+
+/* GUI includes: */
+#include "UIActionPool.h"
+#include "QIDialogButtonBox.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "QIToolBar.h"
+#include "UIVisoHostBrowser.h"
+#include "UIVisoCreator.h"
+#include "UIVisoConfigurationPanel.h"
+#include "UIVisoCreatorOptionsPanel.h"
+#include "UIVisoContentBrowser.h"
+#include "UICommon.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+/* Other VBox includes: */
+#include <iprt/getopt.h>
+#include <iprt/path.h>
+
+
+/*********************************************************************************************************************************
+* UIVisoCreatorWidget implementation. *
+*********************************************************************************************************************************/
+
+UIVisoCreatorWidget::UIVisoCreatorWidget(UIActionPool *pActionPool, QWidget *pParent,
+ bool fShowToolBar,const QString& strMachineName /* = QString() */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pActionConfiguration(0)
+ , m_pActionOptions(0)
+ , m_pAddAction(0)
+ , m_pRemoveAction(0)
+ , m_pCreateNewDirectoryAction(0)
+ , m_pRenameAction(0)
+ , m_pResetAction(0)
+ , m_pMainLayout(0)
+ , m_pHostBrowser(0)
+ , m_pVISOContentBrowser(0)
+ , m_pToolBar(0)
+ , m_pVerticalToolBar(0)
+ , m_pMainMenu(0)
+ , m_strMachineName(strMachineName)
+ , m_pCreatorOptionsPanel(0)
+ , m_pConfigurationPanel(0)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolBar(fShowToolBar)
+{
+ m_visoOptions.m_strVisoName = !strMachineName.isEmpty() ? strMachineName : "ad-hoc";
+ prepareWidgets();
+ populateMenuMainToolbar();
+ prepareConnections();
+ manageEscapeShortCut();
+ retranslateUi();
+}
+
+QStringList UIVisoCreatorWidget::entryList() const
+{
+ if (!m_pVISOContentBrowser)
+ return QStringList();
+ return m_pVISOContentBrowser->entryList();
+}
+
+const QString &UIVisoCreatorWidget::visoName() const
+{
+ return m_visoOptions.m_strVisoName;
+}
+
+const QStringList &UIVisoCreatorWidget::customOptions() const
+{
+ return m_visoOptions.m_customOptions;
+}
+
+QString UIVisoCreatorWidget::currentPath() const
+{
+ if (!m_pHostBrowser)
+ return QString();
+ return m_pHostBrowser->currentPath();
+}
+
+void UIVisoCreatorWidget::setCurrentPath(const QString &strPath)
+{
+ if (!m_pHostBrowser)
+ return;
+ m_pHostBrowser->setCurrentPath(strPath);
+}
+
+QMenu *UIVisoCreatorWidget::menu() const
+{
+ return m_pMainMenu;
+}
+
+void UIVisoCreatorWidget::retranslateUi()
+{
+ if (m_pHostBrowser)
+ m_pHostBrowser->setTitle(tr("Host File System"));
+ if (m_pVISOContentBrowser)
+ m_pVISOContentBrowser->setTitle(tr("VISO Content"));
+}
+
+void UIVisoCreatorWidget::sltHandleAddObjectsToViso(QStringList pathList)
+{
+ if (m_pVISOContentBrowser)
+ m_pVISOContentBrowser->addObjectsToViso(pathList);
+}
+
+void UIVisoCreatorWidget::sltPanelActionToggled(bool fChecked)
+{
+ QAction *pSenderAction = qobject_cast<QAction*>(sender());
+ if (!pSenderAction)
+ return;
+ UIDialogPanel* pPanel = 0;
+ /* Look for the sender() within the m_panelActionMap's values: */
+ for (QMap<UIDialogPanel*, QAction*>::const_iterator iterator = m_panelActionMap.begin();
+ iterator != m_panelActionMap.end(); ++iterator)
+ {
+ if (iterator.value() == pSenderAction)
+ pPanel = iterator.key();
+ }
+ if (!pPanel)
+ return;
+ if (fChecked)
+ showPanel(pPanel);
+ else
+ hidePanel(pPanel);
+}
+
+void UIVisoCreatorWidget::sltHandleVisoNameChanged(const QString &strVisoName)
+{
+ if (m_visoOptions.m_strVisoName == strVisoName)
+ return;
+ m_visoOptions.m_strVisoName = strVisoName;
+ if(m_pVISOContentBrowser)
+ m_pVISOContentBrowser->setVisoName(m_visoOptions.m_strVisoName);
+ emit sigVisoNameChanged(strVisoName);
+}
+
+void UIVisoCreatorWidget::sltHandleCustomVisoOptionsChanged(const QStringList &customVisoOptions)
+{
+ if (m_visoOptions.m_customOptions == customVisoOptions)
+ return;
+ m_visoOptions.m_customOptions = customVisoOptions;
+}
+
+void UIVisoCreatorWidget::sltHandleShowHiddenObjectsChange(bool fShow)
+{
+ if (m_browserOptions.m_fShowHiddenObjects == fShow)
+ return;
+ m_browserOptions.m_fShowHiddenObjects = fShow;
+ m_pHostBrowser->showHideHiddenObjects(fShow);
+}
+
+void UIVisoCreatorWidget::sltHandleHidePanel(UIDialogPanel *pPanel)
+{
+ hidePanel(pPanel);
+}
+
+void UIVisoCreatorWidget::sltHandleBrowserTreeViewVisibilityChanged(bool fVisible)
+{
+ Q_UNUSED(fVisible);
+ manageEscapeShortCut();
+}
+
+void UIVisoCreatorWidget::sltHandleHostBrowserTableSelectionChanged(bool fIsSelectionEmpty)
+{
+ if (m_pAddAction)
+ m_pAddAction->setEnabled(!fIsSelectionEmpty);
+}
+
+void UIVisoCreatorWidget::sltHandleContentBrowserTableSelectionChanged(bool fIsSelectionEmpty)
+{
+ if (m_pRemoveAction)
+ m_pRemoveAction->setEnabled(!fIsSelectionEmpty);
+}
+
+void UIVisoCreatorWidget::sltHandleShowContextMenu(const QWidget *pContextMenuRequester, const QPoint &point)
+{
+ if (!pContextMenuRequester)
+ return;
+
+ QMenu menu;
+
+ if (sender() == m_pHostBrowser)
+ {
+ menu.addAction(m_pAddAction);
+ }
+ else if (sender() == m_pVISOContentBrowser)
+ {
+ menu.addAction(m_pRemoveAction);
+ menu.addAction(m_pCreateNewDirectoryAction);
+ menu.addAction(m_pResetAction);
+ }
+
+ menu.exec(pContextMenuRequester->mapToGlobal(point));
+}
+
+void UIVisoCreatorWidget::prepareWidgets()
+{
+ m_pMainLayout = new QGridLayout(this);
+ if (!m_pMainLayout)
+ return;
+
+ /* Configure layout: */
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 2;
+ const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) / 2;
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) / 2;
+ const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) / 2;
+ m_pMainLayout->setContentsMargins(iL, iT, iR, iB);
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setSpacing(10);
+#else
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2);
+#endif
+
+ if (m_pActionPool && m_pActionPool->action(UIActionIndex_M_VISOCreator))
+ m_pMainMenu = m_pActionPool->action(UIActionIndex_M_VISOCreator)->menu();
+ int iLayoutRow = 0;
+ if (m_fShowToolBar)
+ {
+ m_pToolBar = new QIToolBar(parentWidget());
+ if (m_pToolBar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+ m_pMainLayout->addWidget(m_pToolBar, iLayoutRow++, 0, 1, 5);
+ }
+ }
+
+ m_pHostBrowser = new UIVisoHostBrowser;
+ if (m_pHostBrowser)
+ {
+ m_pMainLayout->addWidget(m_pHostBrowser, iLayoutRow, 0, 1, 4);
+ //m_pMainLayout->setColumnStretch(m_pMainLayout->indexOf(m_pHostBrowser), 2);
+ }
+
+ prepareVerticalToolBar();
+ if (m_pVerticalToolBar)
+ {
+ m_pMainLayout->addWidget(m_pVerticalToolBar, iLayoutRow, 4, 1, 1);
+ //m_pMainLayout->setColumnStretch(m_pMainLayout->indexOf(m_pVerticalToolBar), 1);
+ }
+
+ m_pVISOContentBrowser = new UIVisoContentBrowser;
+ if (m_pVISOContentBrowser)
+ {
+ m_pMainLayout->addWidget(m_pVISOContentBrowser, iLayoutRow, 5, 1, 4);
+ m_pVISOContentBrowser->setVisoName(m_visoOptions.m_strVisoName);
+ //m_pMainLayout->setColumnStretch(m_pMainLayout->indexOf(m_pVISOContentBrowser), 2);
+ }
+ ++iLayoutRow;
+ m_pConfigurationPanel = new UIVisoConfigurationPanel(this);
+ if (m_pConfigurationPanel)
+ {
+ m_pMainLayout->addWidget(m_pConfigurationPanel, iLayoutRow++, 0, 1, 9);
+ m_pConfigurationPanel->hide();
+ m_pConfigurationPanel->setVisoName(m_visoOptions.m_strVisoName);
+ m_pConfigurationPanel->setVisoCustomOptions(m_visoOptions.m_customOptions);
+ }
+
+ m_pCreatorOptionsPanel = new UIVisoCreatorOptionsPanel;
+ if (m_pCreatorOptionsPanel)
+ {
+ m_pCreatorOptionsPanel->setShowHiddenbjects(m_browserOptions.m_fShowHiddenObjects);
+ m_pMainLayout->addWidget(m_pCreatorOptionsPanel, iLayoutRow++, 0, 1, 9);
+ m_pCreatorOptionsPanel->hide();
+ }
+}
+
+void UIVisoCreatorWidget::prepareConnections()
+{
+ if (m_pHostBrowser)
+ {
+ connect(m_pHostBrowser, &UIVisoHostBrowser::sigAddObjectsToViso,
+ this, &UIVisoCreatorWidget::sltHandleAddObjectsToViso);
+ connect(m_pHostBrowser, &UIVisoHostBrowser::sigTreeViewVisibilityChanged,
+ this, &UIVisoCreatorWidget::sltHandleBrowserTreeViewVisibilityChanged);
+ connect(m_pHostBrowser, &UIVisoHostBrowser::sigTableSelectionChanged,
+ this, &UIVisoCreatorWidget::sltHandleHostBrowserTableSelectionChanged);
+ connect(m_pHostBrowser, &UIVisoHostBrowser::sigCreateFileTableViewContextMenu,
+ this, &UIVisoCreatorWidget::sltHandleShowContextMenu);
+ }
+
+ if (m_pVISOContentBrowser)
+ {
+ connect(m_pVISOContentBrowser, &UIVisoContentBrowser::sigTableSelectionChanged,
+ this, &UIVisoCreatorWidget::sltHandleContentBrowserTableSelectionChanged);
+ connect(m_pVISOContentBrowser, &UIVisoContentBrowser::sigCreateFileTableViewContextMenu,
+ this, &UIVisoCreatorWidget::sltHandleShowContextMenu);
+ }
+
+ if (m_pActionConfiguration)
+ connect(m_pActionConfiguration, &QAction::triggered, this, &UIVisoCreatorWidget::sltPanelActionToggled);
+ if (m_pActionOptions)
+ connect(m_pActionOptions, &QAction::triggered, this, &UIVisoCreatorWidget::sltPanelActionToggled);
+
+ if (m_pConfigurationPanel)
+ {
+ connect(m_pConfigurationPanel, &UIVisoConfigurationPanel::sigVisoNameChanged,
+ this, &UIVisoCreatorWidget::sltHandleVisoNameChanged);
+ connect(m_pConfigurationPanel, &UIVisoConfigurationPanel::sigCustomVisoOptionsChanged,
+ this, &UIVisoCreatorWidget::sltHandleCustomVisoOptionsChanged);
+ connect(m_pConfigurationPanel, &UIVisoConfigurationPanel::sigHidePanel,
+ this, &UIVisoCreatorWidget::sltHandleHidePanel);
+ m_panelActionMap.insert(m_pConfigurationPanel, m_pActionConfiguration);
+ }
+
+ if (m_pCreatorOptionsPanel)
+ {
+ connect(m_pCreatorOptionsPanel, &UIVisoCreatorOptionsPanel::sigShowHiddenObjects,
+ this, &UIVisoCreatorWidget::sltHandleShowHiddenObjectsChange);
+ connect(m_pCreatorOptionsPanel, &UIVisoCreatorOptionsPanel::sigHidePanel,
+ this, &UIVisoCreatorWidget::sltHandleHidePanel);
+ m_panelActionMap.insert(m_pCreatorOptionsPanel, m_pActionOptions);
+ }
+
+ if (m_pAddAction)
+ connect(m_pAddAction, &QAction::triggered,
+ m_pHostBrowser, &UIVisoHostBrowser::sltHandleAddAction);
+
+ if (m_pCreateNewDirectoryAction)
+ connect(m_pCreateNewDirectoryAction, &QAction::triggered,
+ m_pVISOContentBrowser, &UIVisoContentBrowser::sltHandleCreateNewDirectory);
+ if (m_pRemoveAction)
+ connect(m_pRemoveAction, &QAction::triggered,
+ m_pVISOContentBrowser, &UIVisoContentBrowser::sltHandleRemoveItems);
+ if (m_pResetAction)
+ connect(m_pResetAction, &QAction::triggered,
+ m_pVISOContentBrowser, &UIVisoContentBrowser::sltHandleResetAction);
+ if (m_pRenameAction)
+ connect(m_pRenameAction, &QAction::triggered,
+ m_pVISOContentBrowser,&UIVisoContentBrowser::sltHandleItemRenameAction);
+}
+
+void UIVisoCreatorWidget::prepareActions()
+{
+ if (!m_pActionPool)
+ return;
+
+ m_pActionConfiguration = m_pActionPool->action(UIActionIndex_M_VISOCreator_ToggleConfigPanel);
+ m_pActionOptions = m_pActionPool->action(UIActionIndex_M_VISOCreator_ToggleOptionsPanel);
+
+ m_pAddAction = m_pActionPool->action(UIActionIndex_M_VISOCreator_Add);
+ if (m_pAddAction && m_pHostBrowser)
+ m_pAddAction->setEnabled(m_pHostBrowser->tableViewHasSelection());
+ m_pRemoveAction = m_pActionPool->action(UIActionIndex_M_VISOCreator_Remove);
+ if (m_pRemoveAction && m_pVISOContentBrowser)
+ m_pRemoveAction->setEnabled(m_pVISOContentBrowser->tableViewHasSelection());
+ m_pCreateNewDirectoryAction = m_pActionPool->action(UIActionIndex_M_VISOCreator_CreateNewDirectory);
+ m_pRenameAction = m_pActionPool->action(UIActionIndex_M_VISOCreator_Rename);
+ m_pResetAction = m_pActionPool->action(UIActionIndex_M_VISOCreator_Reset);
+}
+
+void UIVisoCreatorWidget::populateMenuMainToolbar()
+{
+ prepareActions();
+ if (m_pToolBar)
+ {
+ if (m_pActionConfiguration)
+ m_pToolBar->addAction(m_pActionConfiguration);
+ if (m_pActionOptions)
+ m_pToolBar->addAction(m_pActionOptions);
+ }
+ if (m_pMainMenu)
+ {
+ m_pMainMenu->addAction(m_pActionConfiguration);
+ m_pMainMenu->addAction(m_pActionOptions);
+ m_pMainMenu->addSeparator();
+ m_pMainMenu->addAction(m_pAddAction);
+ m_pMainMenu->addAction(m_pRemoveAction);
+ m_pMainMenu->addAction(m_pCreateNewDirectoryAction);
+ m_pMainMenu->addAction(m_pResetAction);
+ }
+
+ if (m_pVerticalToolBar)
+ {
+ /* Add to dummy QWidget to toolbar to center the action icons vertically: */
+ QWidget *topSpacerWidget = new QWidget(this);
+ topSpacerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+ topSpacerWidget->setVisible(true);
+ QWidget *bottomSpacerWidget = new QWidget(this);
+ bottomSpacerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+ bottomSpacerWidget->setVisible(true);
+
+ m_pVerticalToolBar->addWidget(topSpacerWidget);
+ if (m_pAddAction)
+ m_pVerticalToolBar->addAction(m_pAddAction);
+ if (m_pRemoveAction)
+ m_pVerticalToolBar->addAction(m_pRemoveAction);
+ if (m_pCreateNewDirectoryAction)
+ m_pVerticalToolBar->addAction(m_pCreateNewDirectoryAction);
+ if (m_pResetAction)
+ m_pVerticalToolBar->addAction(m_pResetAction);
+
+ m_pVerticalToolBar->addWidget(bottomSpacerWidget);
+ }
+}
+
+void UIVisoCreatorWidget::hidePanel(UIDialogPanel* panel)
+{
+ if (panel && panel->isVisible())
+ panel->setVisible(false);
+ QMap<UIDialogPanel*, QAction*>::iterator iterator = m_panelActionMap.find(panel);
+ if (iterator != m_panelActionMap.end())
+ {
+ if (iterator.value() && iterator.value()->isChecked())
+ iterator.value()->setChecked(false);
+ }
+ m_visiblePanelsList.removeAll(panel);
+ manageEscapeShortCut();
+}
+
+void UIVisoCreatorWidget::showPanel(UIDialogPanel* panel)
+{
+ if (panel && panel->isHidden())
+ panel->setVisible(true);
+ QMap<UIDialogPanel*, QAction*>::iterator iterator = m_panelActionMap.find(panel);
+ if (iterator != m_panelActionMap.end())
+ {
+ if (!iterator.value()->isChecked())
+ iterator.value()->setChecked(true);
+ }
+ if (!m_visiblePanelsList.contains(panel))
+ m_visiblePanelsList.push_back(panel);
+ manageEscapeShortCut();
+}
+
+void UIVisoCreatorWidget::manageEscapeShortCut()
+{
+ /* Take the escape key from m_pButtonBox and from the panels in case treeview(s) in
+ host and/or content browser is open. We use the escape key to close those first: */
+ if ((m_pHostBrowser && m_pHostBrowser->isTreeViewVisible()) ||
+ (m_pVISOContentBrowser && m_pVISOContentBrowser->isTreeViewVisible()))
+ {
+ emit sigSetCancelButtonShortCut(QKeySequence());
+ for (int i = 0; i < m_visiblePanelsList.size(); ++i)
+ m_visiblePanelsList[i]->setCloseButtonShortCut(QKeySequence());
+ return;
+ }
+
+ /* if there are no visible panels then assign esc. key to cancel button of the button box: */
+ if (m_visiblePanelsList.isEmpty())
+ {
+ emit sigSetCancelButtonShortCut(QKeySequence(Qt::Key_Escape));
+ return;
+ }
+ emit sigSetCancelButtonShortCut(QKeySequence());
+
+ /* Just loop thru the visible panel list and set the esc key to the
+ panel which made visible latest */
+ for (int i = 0; i < m_visiblePanelsList.size() - 1; ++i)
+ m_visiblePanelsList[i]->setCloseButtonShortCut(QKeySequence());
+ m_visiblePanelsList.back()->setCloseButtonShortCut(QKeySequence(Qt::Key_Escape));
+}
+
+void UIVisoCreatorWidget::prepareVerticalToolBar()
+{
+ m_pVerticalToolBar = new QIToolBar;
+ if (!m_pVerticalToolBar)
+ return;
+
+ m_pVerticalToolBar->setOrientation(Qt::Vertical);
+}
+
+/* static */
+int UIVisoCreatorWidget::visoWriteQuotedString(PRTSTREAM pStrmDst, const char *pszPrefix,
+ QString const &rStr, const char *pszPostFix)
+{
+ QByteArray const utf8Array = rStr.toUtf8();
+ const char *apszArgv[2] = { utf8Array.constData(), NULL };
+ char *pszQuoted;
+ int vrc = RTGetOptArgvToString(&pszQuoted, apszArgv, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
+ if (RT_SUCCESS(vrc))
+ {
+ if (pszPrefix)
+ vrc = RTStrmPutStr(pStrmDst, pszPrefix);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTStrmPutStr(pStrmDst, pszQuoted);
+ if (pszPostFix && RT_SUCCESS(vrc))
+ vrc = RTStrmPutStr(pStrmDst, pszPostFix);
+ }
+ RTStrFree(pszQuoted);
+ }
+ return vrc;
+}
+
+/* static */
+QUuid UIVisoCreatorWidget::createViso(UIActionPool *pActionPool, QWidget *pParent,
+ const QString &strDefaultFolder /* = QString() */,
+ const QString &strMachineName /* = QString() */)
+{
+ QWidget *pDialogParent = windowManager().realParentWindow(pParent);
+ UIVisoCreatorDialog *pVisoCreator = new UIVisoCreatorDialog(pActionPool, pDialogParent, strMachineName);
+
+ if (!pVisoCreator)
+ return QUuid();
+ windowManager().registerNewParent(pVisoCreator, pDialogParent);
+ pVisoCreator->setCurrentPath(gEDataManager->visoCreatorRecentFolder());
+
+ if (pVisoCreator->exec(false /* not application modal */))
+ {
+ QStringList files = pVisoCreator->entryList();
+ QString strVisoName = pVisoCreator->visoName();
+ if (strVisoName.isEmpty())
+ strVisoName = strMachineName;
+
+ if (files.empty() || files[0].isEmpty())
+ {
+ delete pVisoCreator;
+ return QUuid();
+ }
+
+ gEDataManager->setVISOCreatorRecentFolder(pVisoCreator->currentPath());
+
+ /* Produce the VISO. */
+ char szVisoPath[RTPATH_MAX];
+ QString strFileName = QString("%1%2").arg(strVisoName).arg(".viso");
+
+ QString strVisoSaveFolder(strDefaultFolder);
+ if (strVisoSaveFolder.isEmpty())
+ strVisoSaveFolder = uiCommon().defaultFolderPathForType(UIMediumDeviceType_DVD);
+
+ int vrc = RTPathJoin(szVisoPath, sizeof(szVisoPath), strVisoSaveFolder.toUtf8().constData(), strFileName.toUtf8().constData());
+ if (RT_SUCCESS(vrc))
+ {
+ PRTSTREAM pStrmViso;
+ vrc = RTStrmOpen(szVisoPath, "w", &pStrmViso);
+ if (RT_SUCCESS(vrc))
+ {
+ RTUUID Uuid;
+ vrc = RTUuidCreate(&Uuid);
+ if (RT_SUCCESS(vrc))
+ {
+ RTStrmPrintf(pStrmViso, "--iprt-iso-maker-file-marker-bourne-sh %RTuuid\n", &Uuid);
+ vrc = UIVisoCreatorWidget::visoWriteQuotedString(pStrmViso, "--volume-id=", strVisoName, "\n");
+
+ for (int iFile = 0; iFile < files.size() && RT_SUCCESS(vrc); iFile++)
+ vrc = UIVisoCreatorWidget::visoWriteQuotedString(pStrmViso, NULL, files[iFile], "\n");
+
+ /* Append custom options if any to the file: */
+ const QStringList &customOptions = pVisoCreator->customOptions();
+ foreach (QString strLine, customOptions)
+ RTStrmPrintf(pStrmViso, "%s\n", strLine.toUtf8().constData());
+
+ RTStrmFlush(pStrmViso);
+ if (RT_SUCCESS(vrc))
+ vrc = RTStrmError(pStrmViso);
+ }
+
+ RTStrmClose(pStrmViso);
+ }
+ }
+
+ /* Done. */
+ if (RT_SUCCESS(vrc))
+ {
+ delete pVisoCreator;
+ return uiCommon().openMedium(UIMediumDeviceType_DVD, QString(szVisoPath), pParent);
+ }
+ /** @todo error message. */
+ else
+ {
+ delete pVisoCreator;
+ return QUuid();
+ }
+ }
+ delete pVisoCreator;
+ return QUuid();
+}
+
+
+/*********************************************************************************************************************************
+* UIVisoCreatorDialog implementation. *
+*********************************************************************************************************************************/
+UIVisoCreatorDialog::UIVisoCreatorDialog(UIActionPool *pActionPool, QWidget *pParent, const QString& strMachineName /* = QString() */)
+ : QIWithRetranslateUI<QIWithRestorableGeometry<QIMainDialog> >(pParent)
+ , m_strMachineName(strMachineName)
+ , m_pVisoCreatorWidget(0)
+ , m_pButtonBox(0)
+ , m_pActionPool(pActionPool)
+ , m_iGeometrySaveTimerId(-1)
+{
+ /* Make sure that the base class does not close this dialog upon pressing escape.
+ we manage escape key here with special casing: */
+ setRejectByEscape(false);
+ prepareWidgets();
+ prepareConnections();
+ loadSettings();
+}
+
+QStringList UIVisoCreatorDialog::entryList() const
+{
+ if (m_pVisoCreatorWidget)
+ return m_pVisoCreatorWidget->entryList();
+ return QStringList();
+}
+
+QString UIVisoCreatorDialog::visoName() const
+{
+ if (m_pVisoCreatorWidget)
+ return m_pVisoCreatorWidget->visoName();
+ return QString();
+}
+
+QStringList UIVisoCreatorDialog::customOptions() const
+{
+ if (m_pVisoCreatorWidget)
+ return m_pVisoCreatorWidget->customOptions();
+ return QStringList();
+}
+
+QString UIVisoCreatorDialog::currentPath() const
+{
+ if (m_pVisoCreatorWidget)
+ return m_pVisoCreatorWidget->currentPath();
+ return QString();
+}
+
+void UIVisoCreatorDialog::setCurrentPath(const QString &strPath)
+{
+ if (m_pVisoCreatorWidget)
+ m_pVisoCreatorWidget->setCurrentPath(strPath);
+}
+
+void UIVisoCreatorDialog::prepareWidgets()
+{
+ QWidget *pCentralWidget = new QWidget;
+ setCentralWidget(pCentralWidget);
+ QVBoxLayout *pMainLayout = new QVBoxLayout;
+ pCentralWidget->setLayout(pMainLayout);
+
+
+ m_pVisoCreatorWidget = new UIVisoCreatorWidget(m_pActionPool, this, true /* show toolbar */, m_strMachineName);
+ if (m_pVisoCreatorWidget)
+ {
+ menuBar()->addMenu(m_pVisoCreatorWidget->menu());
+ pMainLayout->addWidget(m_pVisoCreatorWidget);
+ connect(m_pVisoCreatorWidget, &UIVisoCreatorWidget::sigSetCancelButtonShortCut,
+ this, &UIVisoCreatorDialog::sltSetCancelButtonShortCut);
+ connect(m_pVisoCreatorWidget, &UIVisoCreatorWidget::sigVisoNameChanged,
+ this, &UIVisoCreatorDialog::sltsigVisoNameChanged);
+ }
+
+ m_pButtonBox = new QIDialogButtonBox;
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setDoNotPickDefaultButton(true);
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(QKeySequence(Qt::Key_Escape));
+ pMainLayout->addWidget(m_pButtonBox);
+
+ connect(m_pButtonBox->button(QIDialogButtonBox::Help), &QPushButton::pressed,
+ &(msgCenter()), &UIMessageCenter::sltHandleHelpRequest);
+ m_pButtonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents);
+
+ uiCommon().setHelpKeyword(m_pButtonBox->button(QIDialogButtonBox::Help), "create-optical-disk-image");
+ }
+ retranslateUi();
+}
+
+void UIVisoCreatorDialog::prepareConnections()
+{
+ if (m_pButtonBox)
+ {
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UIVisoCreatorDialog::close);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UIVisoCreatorDialog::accept);
+ }
+}
+
+void UIVisoCreatorDialog::retranslateUi()
+{
+ updateWindowTitle();
+ if (m_pButtonBox && m_pButtonBox->button(QDialogButtonBox::Ok))
+ {
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText(UIVisoCreatorWidget::tr("C&reate"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setToolTip(UIVisoCreatorWidget::tr("Creates VISO file with the selected content"));
+ }
+ if (m_pButtonBox && m_pButtonBox->button(QDialogButtonBox::Help))
+ m_pButtonBox->button(QDialogButtonBox::Help)->setToolTip(UIVisoCreatorWidget::tr("Opens the help browser and navigates to the related section"));
+}
+
+bool UIVisoCreatorDialog::event(QEvent *pEvent)
+{
+ if (pEvent->type() == QEvent::Resize || pEvent->type() == QEvent::Move)
+ {
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+ }
+ else if (pEvent->type() == QEvent::Timer)
+ {
+ QTimerEvent *pTimerEvent = static_cast<QTimerEvent*>(pEvent);
+ if (pTimerEvent->timerId() == m_iGeometrySaveTimerId)
+ {
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = -1;
+ saveDialogGeometry();
+ }
+ }
+ return QIWithRetranslateUI<QIWithRestorableGeometry<QIMainDialog> >::event(pEvent);
+}
+
+void UIVisoCreatorDialog::sltSetCancelButtonShortCut(QKeySequence keySequence)
+{
+ if (m_pButtonBox && m_pButtonBox->button(QDialogButtonBox::Cancel))
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(keySequence);
+}
+
+void UIVisoCreatorDialog::sltsigVisoNameChanged(const QString &strName)
+{
+ Q_UNUSED(strName);
+ updateWindowTitle();
+}
+
+void UIVisoCreatorDialog::loadSettings()
+{
+ const QRect availableGeo = gpDesktop->availableGeometry(this);
+ int iDefaultWidth = availableGeo.width() / 2;
+ int iDefaultHeight = availableGeo.height() * 3 / 4;
+ QRect defaultGeo(0, 0, iDefaultWidth, iDefaultHeight);
+
+ QWidget *pParent = windowManager().realParentWindow(parentWidget() ? parentWidget() : windowManager().mainWindowShown());
+ /* Load geometry from extradata: */
+ const QRect geo = gEDataManager->visoCreatorDialogGeometry(this, pParent, defaultGeo);
+ LogRel2(("GUI: UISoftKeyboard: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+
+ restoreGeometry(geo);
+}
+
+void UIVisoCreatorDialog::saveDialogGeometry()
+{
+ const QRect geo = currentGeometry();
+ LogRel2(("GUI: UIMediumSelector: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ gEDataManager->setVisoCreatorDialogGeometry(geo, isCurrentlyMaximized());
+}
+
+void UIVisoCreatorDialog::updateWindowTitle()
+{
+ setWindowTitle(QString("%1 - %2.%3").arg(UIVisoCreatorWidget::tr("VISO Creator")).arg(visoName()).arg("viso"));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreator.h b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreator.h
new file mode 100644
index 00000000..a552bc0b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreator.h
@@ -0,0 +1,234 @@
+/* $Id: UIVisoCreator.h $ */
+/** @file
+ * VBox Qt GUI - UIVisoCreator classes declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_viso_UIVisoCreator_h
+#define FEQT_INCLUDED_SRC_medium_viso_UIVisoCreator_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QModelIndex>
+
+/* GUI includes: */
+#include "QIMainDialog.h"
+#include "QIWithRestorableGeometry.h"
+#include "QIWithRetranslateUI.h"
+
+#include <iprt/stream.h>
+
+/* Forward declarations: */
+class QMenu;
+class QGridLayout;
+class QIDialogButtonBox;
+class UIDialogPanel;
+class QIToolBar;
+class UIActionPool;
+class UIVisoHostBrowser;
+class UIVisoContentBrowser;
+class UIVisoCreatorOptionsPanel;
+class UIVisoConfigurationPanel;
+
+/** A QIMainDialog extension. It hosts two UIVisoBrowserBase extensions, one for host and one
+ * for VISO file system. It has the main menu, main toolbar, and a vertical toolbar and corresponding
+ * actions. */
+class SHARED_LIBRARY_STUFF UIVisoCreatorWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigSetCancelButtonShortCut(QKeySequence keySequence);
+ void sigVisoNameChanged(const QString &strVisoName);
+
+public:
+
+ UIVisoCreatorWidget(UIActionPool *pActionPool, QWidget *pParent,
+ bool fShowToolBar, const QString& strMachineName = QString());
+ /** Returns the content of the .viso file. Each element of the list corresponds to a line in the .viso file. */
+ QStringList entryList() const;
+ const QString &visoName() const;
+ /** Returns custom ISO options (if any). */
+ const QStringList &customOptions() const;
+ /** Returns the current path that the host browser is listing. */
+ QString currentPath() const;
+ void setCurrentPath(const QString &strPath);
+ QMenu *menu() const;
+
+ /** Creates a VISO by using the VISO creator dialog.
+ * @param pParent Passes the dialog parent.
+ * @param strDefaultFolder Passes the folder to save the VISO file.
+ * @param strMachineName Passes the name of the machine,
+ * returns the UUID of the created medium or a null QUuid. */
+ static QUuid createViso(UIActionPool *pActionPool, QWidget *pParent,
+ const QString &strDefaultFolder = QString(),
+ const QString &strMachineName = QString());
+
+#ifdef VBOX_WS_MAC
+ /** Returns the toolbar. */
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+ /**
+ * Helper for createVisoMediumWithVisoCreator.
+ * @returns IPRT status code.
+ * @param pStrmDst Where to write the quoted string.
+ * @param pszPrefix Stuff to put in front of it.
+ * @param rStr The string to quote and write out.
+ * @param pszPrefix Stuff to put after it.
+ */
+ static int visoWriteQuotedString(PRTSTREAM pStrmDst, const char *pszPrefix,
+ QString const &rStr, const char *pszPostFix);
+
+protected:
+
+ virtual void retranslateUi() final override;
+
+private slots:
+
+ void sltHandleAddObjectsToViso(QStringList pathList);
+ void sltPanelActionToggled(bool fChecked);
+ void sltHandleVisoNameChanged(const QString& strVisoName);
+ void sltHandleCustomVisoOptionsChanged(const QStringList &customVisoOptions);
+ void sltHandleShowHiddenObjectsChange(bool fShow);
+ void sltHandleHidePanel(UIDialogPanel *pPanel);
+ void sltHandleBrowserTreeViewVisibilityChanged(bool fVisible);
+ void sltHandleHostBrowserTableSelectionChanged(bool fIsSelectionEmpty);
+ void sltHandleContentBrowserTableSelectionChanged(bool fIsSelectionEmpty);
+ void sltHandleShowContextMenu(const QWidget *pContextMenuRequester, const QPoint &point);
+
+private:
+
+ struct VisoOptions
+ {
+ VisoOptions()
+ :m_strVisoName("ad-hoc-viso"){}
+ QString m_strVisoName;
+ /** Additions viso options to be inserted to the viso file as separate lines. */
+ QStringList m_customOptions;
+ };
+
+ struct BrowserOptions
+ {
+ BrowserOptions()
+ :m_fShowHiddenObjects(true){}
+ bool m_fShowHiddenObjects;
+ };
+
+ void prepareWidgets();
+ void prepareConnections();
+ void prepareActions();
+ /** Creates and configures the vertical toolbar. Should be called after prepareActions() */
+ void prepareVerticalToolBar();
+ /* Populates the main menu and toolbard with already created actions.
+ * Leave out the vertical toolbar which is handled in prepareVerticalToolBar. */
+ void populateMenuMainToolbar();
+ /** Set the root index of the m_pTableModel to the current index of m_pTreeModel. */
+ void setTableRootIndex(QModelIndex index = QModelIndex() );
+ void setTreeCurrentIndex(QModelIndex index = QModelIndex() );
+ void hidePanel(UIDialogPanel *panel);
+ void showPanel(UIDialogPanel *panel);
+ /** Makes sure escape key is assigned to only a single widget. This is done by checking
+ * several things in the following order:
+ * - when (drop-down) tree views of browser panes are visible esc. key used to close those. thus it is taken from the dialog and panels
+ * - when there are no more panels visible assign it to the parent dialog
+ * - grab it from the dialog as soon as a panel becomes visible again
+ * - assign it to the most recently "unhidden" panel */
+ void manageEscapeShortCut();
+
+ /** @name Main toolbar (and main menu) actions
+ * @{ */
+ QAction *m_pActionConfiguration;
+ QAction *m_pActionOptions;
+ /** @} */
+
+ /** @name These actions are addded to vertical toolbar, context menus, and the main menu.
+ * @{ */
+ QAction *m_pAddAction;
+ QAction *m_pRemoveAction;
+ QAction *m_pCreateNewDirectoryAction;
+ QAction *m_pRenameAction;
+ QAction *m_pResetAction;
+ /** @} */
+
+ QGridLayout *m_pMainLayout;
+ UIVisoHostBrowser *m_pHostBrowser;
+ UIVisoContentBrowser *m_pVISOContentBrowser;
+
+ QIToolBar *m_pToolBar;
+ QIToolBar *m_pVerticalToolBar;
+ VisoOptions m_visoOptions;
+ BrowserOptions m_browserOptions;
+ QMenu *m_pMainMenu;
+ QString m_strMachineName;
+ UIVisoCreatorOptionsPanel *m_pCreatorOptionsPanel;
+ UIVisoConfigurationPanel *m_pConfigurationPanel;
+ QMap<UIDialogPanel*, QAction*> m_panelActionMap;
+ QList<UIDialogPanel*> m_visiblePanelsList;
+ QPointer<UIActionPool> m_pActionPool;
+ bool m_fShowToolBar;
+};
+
+
+class SHARED_LIBRARY_STUFF UIVisoCreatorDialog : public QIWithRetranslateUI<QIWithRestorableGeometry<QIMainDialog> >
+{
+ Q_OBJECT;
+
+public:
+
+ UIVisoCreatorDialog(UIActionPool *pActionPool, QWidget *pParent, const QString& strMachineName = QString());
+
+ QStringList entryList() const;
+ QString visoName() const;
+ QStringList customOptions() const;
+ QString currentPath() const;
+ void setCurrentPath(const QString &strPath);
+
+protected:
+
+ virtual bool event(QEvent *pEvent) final override;
+
+private slots:
+
+ void sltSetCancelButtonShortCut(QKeySequence keySequence);
+ void sltsigVisoNameChanged(const QString &strName);
+
+private:
+ void prepareWidgets();
+ void prepareConnections();
+ virtual void retranslateUi() final override;
+ void loadSettings();
+ void saveDialogGeometry();
+ void updateWindowTitle();
+
+ QString m_strMachineName;
+ UIVisoCreatorWidget *m_pVisoCreatorWidget;
+ QIDialogButtonBox *m_pButtonBox;
+ QPointer<UIActionPool> m_pActionPool;
+ int m_iGeometrySaveTimerId;
+};
+#endif /* !FEQT_INCLUDED_SRC_medium_viso_UIVisoCreator_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreatorOptionsPanel.cpp b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreatorOptionsPanel.cpp
new file mode 100644
index 00000000..1e11e62c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreatorOptionsPanel.cpp
@@ -0,0 +1,94 @@
+/* $Id: UIVisoCreatorOptionsPanel.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVisoCreatorOptionsPanel class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QHBoxLayout>
+
+/* GUI includes: */
+#include "QILabel.h"
+#include "UIVisoCreatorOptionsPanel.h"
+
+UIVisoCreatorOptionsPanel::UIVisoCreatorOptionsPanel(QWidget *pParent /* =0 */)
+ : UIDialogPanel(pParent)
+ , m_pShowHiddenObjectsCheckBox(0)
+ , m_pShowHiddenObjectsLabel(0)
+{
+ prepareObjects();
+ prepareConnections();
+}
+
+UIVisoCreatorOptionsPanel::~UIVisoCreatorOptionsPanel()
+{
+}
+
+QString UIVisoCreatorOptionsPanel::panelName() const
+{
+ return "OptionsPanel";
+}
+
+void UIVisoCreatorOptionsPanel::setShowHiddenbjects(bool fShow)
+{
+ if (m_pShowHiddenObjectsCheckBox)
+ m_pShowHiddenObjectsCheckBox->setChecked(fShow);
+}
+
+void UIVisoCreatorOptionsPanel::retranslateUi()
+{
+ if (m_pShowHiddenObjectsLabel)
+ m_pShowHiddenObjectsLabel->setText(QApplication::translate("UIVisoCreatorWidget", "Show Hidden Objects"));
+ if (m_pShowHiddenObjectsCheckBox)
+ m_pShowHiddenObjectsCheckBox->setToolTip(QApplication::translate("UIVisoCreatorWidget", "When checked, "
+ "multiple hidden objects are shown in the file browser"));
+}
+
+void UIVisoCreatorOptionsPanel::sltHandlShowHiddenObjectsChange(int iState)
+{
+ if (iState == static_cast<int>(Qt::Checked))
+ sigShowHiddenObjects(true);
+ else
+ sigShowHiddenObjects(false);
+}
+
+void UIVisoCreatorOptionsPanel::prepareObjects()
+{
+ if (!mainLayout())
+ return;
+
+ m_pShowHiddenObjectsCheckBox = new QCheckBox;
+ m_pShowHiddenObjectsLabel = new QILabel(QApplication::translate("UIVisoCreatorWidget", "Show Hidden Objects"));
+ m_pShowHiddenObjectsLabel->setBuddy(m_pShowHiddenObjectsCheckBox);
+ mainLayout()->addWidget(m_pShowHiddenObjectsCheckBox, 0, Qt::AlignLeft);
+ mainLayout()->addWidget(m_pShowHiddenObjectsLabel, 0, Qt::AlignLeft);
+ mainLayout()->addStretch(6);
+ connect(m_pShowHiddenObjectsCheckBox, &QCheckBox::stateChanged,
+ this, &UIVisoCreatorOptionsPanel::sltHandlShowHiddenObjectsChange);
+}
+
+void UIVisoCreatorOptionsPanel::prepareConnections()
+{
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreatorOptionsPanel.h b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreatorOptionsPanel.h
new file mode 100644
index 00000000..f973ec3f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoCreatorOptionsPanel.h
@@ -0,0 +1,74 @@
+/* $Id: UIVisoCreatorOptionsPanel.h $ */
+/** @file
+ * VBox Qt GUI - UIVisoCreatorOptionsPanel class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_viso_UIVisoCreatorOptionsPanel_h
+#define FEQT_INCLUDED_SRC_medium_viso_UIVisoCreatorOptionsPanel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Forward declarations: */
+class QCheckBox;
+class QILabel;
+
+/* GUI includes: */
+#include "UIDialogPanel.h"
+
+class UIVisoCreatorOptionsPanel : public UIDialogPanel
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigShowHiddenObjects(bool fShow);
+
+public:
+
+ UIVisoCreatorOptionsPanel(QWidget *pParent = 0);
+ ~UIVisoCreatorOptionsPanel();
+ virtual QString panelName() const RT_OVERRIDE;
+ void setShowHiddenbjects(bool fShow);
+
+protected:
+
+ void retranslateUi() RT_OVERRIDE;
+
+
+private slots:
+
+ void sltHandlShowHiddenObjectsChange(int iState);
+
+private:
+
+ void prepareObjects();
+ void prepareConnections();
+
+ QCheckBox *m_pShowHiddenObjectsCheckBox;
+ QILabel *m_pShowHiddenObjectsLabel;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_medium_viso_UIVisoCreatorOptionsPanel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoHostBrowser.cpp b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoHostBrowser.cpp
new file mode 100644
index 00000000..beb40458
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoHostBrowser.cpp
@@ -0,0 +1,351 @@
+/* $Id: UIVisoHostBrowser.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVisoHostBrowser class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+/* Qt includes: */
+#include <QFileSystemModel>
+#include <QGridLayout>
+#include <QHeaderView>
+#include <QMimeData>
+#include <QTextEdit>
+#include <QTreeView>
+#include <QTableView>
+
+/* GUI includes: */
+#include "UIVisoHostBrowser.h"
+
+/*********************************************************************************************************************************
+* UIVisoHostBrowserModel definition. *
+*********************************************************************************************************************************/
+
+class UIVisoHostBrowserModel : public QFileSystemModel
+{
+ Q_OBJECT;
+
+public:
+
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const RT_OVERRIDE;
+ UIVisoHostBrowserModel(QObject *pParent);
+
+ virtual QStringList mimeTypes() const RT_OVERRIDE;
+ /** Prepares the mime data as a list of text consisting of dragged objects full file path. */
+ QMimeData *mimeData(const QModelIndexList &indexes) const RT_OVERRIDE;
+
+protected:
+
+private:
+
+};
+
+/*********************************************************************************************************************************
+* UIVisoHostBrowserModel implementation. *
+*********************************************************************************************************************************/
+
+UIVisoHostBrowserModel::UIVisoHostBrowserModel(QObject *pParent /* = 0 */)
+ :QFileSystemModel(pParent)
+{
+}
+
+QVariant UIVisoHostBrowserModel::data(const QModelIndex &index, int enmRole /* = Qt::DisplayRole */) const
+{
+ if (enmRole == Qt::DecorationRole && index.column() == 0)
+ {
+ QFileInfo info = fileInfo(index);
+
+ if(info.isFile())
+ {
+ if(info.isSymLink())
+ return QIcon(":/file_manager_file_symlink_16px.png");
+ else
+ return QIcon(":/file_manager_file_16px.png");
+ }
+ else if(info.isDir())
+ {
+ if (filePath(index).contains(".."))
+ return QIcon(":/arrow_up_10px_x2.png");
+ /** A bad hack to detect drive roots and use HD icon. On Windows 10 QFileInfo()::isRoot()
+ * and QDir()::isRoot() return true only for C:/ : */
+#ifdef VBOX_WS_WIN
+ else if (info.absoluteFilePath().length() <= 3)
+ return QIcon(":/hd_32px.png");
+#endif
+ else if(info.isSymLink())
+ return QIcon(":/file_manager_folder_symlink_16px.png");
+ else
+ return QIcon(":/file_manager_folder_16px.png");
+ }
+ }
+ return QFileSystemModel::data(index, enmRole);
+}
+
+QStringList UIVisoHostBrowserModel::mimeTypes() const
+{
+ QStringList types;
+ types << "application/vnd.text.list";
+ return types;
+}
+
+QMimeData *UIVisoHostBrowserModel::mimeData(const QModelIndexList &indexes) const
+{
+ QMimeData *mimeData = new QMimeData();
+ QByteArray encodedData;
+
+ QDataStream stream(&encodedData, QIODevice::WriteOnly);
+
+ foreach (const QModelIndex &index, indexes) {
+ if (index.isValid() && index.column() == 0)
+ {
+ QString strPath = fileInfo(index).filePath();
+ if (!strPath.contains(".."))
+ stream << fileInfo(index).filePath();
+ }
+ }
+
+ mimeData->setData("application/vnd.text.list", encodedData);
+ return mimeData;
+}
+
+/*********************************************************************************************************************************
+* UIVisoHostBrowser implementation. *
+*********************************************************************************************************************************/
+
+UIVisoHostBrowser::UIVisoHostBrowser(QWidget *pParent /* = 0 */)
+ : UIVisoBrowserBase(pParent)
+ , m_pTreeModel(0)
+ , m_pTableModel(0)
+ , m_pTableView(0)
+{
+ prepareObjects();
+ prepareConnections();
+}
+
+UIVisoHostBrowser::~UIVisoHostBrowser()
+{
+}
+
+void UIVisoHostBrowser::retranslateUi()
+{
+}
+
+void UIVisoHostBrowser::prepareObjects()
+{
+ UIVisoBrowserBase::prepareObjects();
+
+ m_pTreeModel = new UIVisoHostBrowserModel(this);
+ m_pTreeModel->setRootPath(QDir::rootPath());
+ m_pTreeModel->setReadOnly(true);
+ m_pTreeModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden);
+ m_pTableModel = new UIVisoHostBrowserModel(this);
+ m_pTableModel->setRootPath(QDir::rootPath());
+ m_pTableModel->setReadOnly(true);
+ m_pTableModel->setFilter(QDir::AllEntries | QDir::NoDot | QDir::Hidden | QDir::System);
+
+ if (m_pTreeView)
+ {
+ m_pTreeView->setModel(m_pTreeModel);
+ m_pTreeView->setRootIndex(m_pTreeModel->index(m_pTreeModel->rootPath()).parent());
+ m_pTreeView->setCurrentIndex(m_pTreeModel->index(QDir::homePath()));
+ /* Show only the 0th column that is "name': */
+ m_pTreeView->hideColumn(1);
+ m_pTreeView->hideColumn(2);
+ m_pTreeView->hideColumn(3);
+ }
+
+ m_pTableView = new QTableView;
+ if (m_pTableView)
+ {
+ m_pTableView->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pMainLayout->addWidget(m_pTableView, 1, 0, 8, 4);
+ m_pTableView->setShowGrid(false);
+ m_pTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ m_pTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ m_pTableView->setAlternatingRowColors(true);
+ m_pTableView->setTabKeyNavigation(false);
+ QHeaderView *pVerticalHeader = m_pTableView->verticalHeader();
+ if (pVerticalHeader)
+ {
+ m_pTableView->verticalHeader()->setVisible(false);
+ /* Minimize the row height: */
+ m_pTableView->verticalHeader()->setDefaultSectionSize(m_pTableView->verticalHeader()->minimumSectionSize());
+ }
+ QHeaderView *pHorizontalHeader = m_pTableView->horizontalHeader();
+ if (pHorizontalHeader)
+ {
+ pHorizontalHeader->setHighlightSections(false);
+ pHorizontalHeader->setSectionResizeMode(QHeaderView::Stretch);
+ }
+
+ m_pTableView->setModel(m_pTableModel);
+ setTableRootIndex();
+ /* Hide the "type" column: */
+ m_pTableView->hideColumn(2);
+
+ m_pTableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ m_pTableView->setDragDropMode(QAbstractItemView::DragOnly);
+ }
+
+ retranslateUi();
+}
+
+void UIVisoHostBrowser::prepareConnections()
+{
+ UIVisoBrowserBase::prepareConnections();
+ if (m_pTableView)
+ {
+ connect(m_pTableView, &QTableView::doubleClicked,
+ this, &UIVisoBrowserBase::sltHandleTableViewItemDoubleClick);
+ connect(m_pTableView, &QTableView::customContextMenuRequested,
+ this, &UIVisoHostBrowser::sltFileTableViewContextMenu);
+ }
+
+ if (m_pTableView->selectionModel())
+ connect(m_pTableView->selectionModel(), &QItemSelectionModel::selectionChanged,
+ this, &UIVisoHostBrowser::sltHandleTableSelectionChanged);
+}
+
+void UIVisoHostBrowser::sltHandleTableSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
+{
+ Q_UNUSED(deselected);
+ emit sigTableSelectionChanged(selected.isEmpty());
+}
+
+void UIVisoHostBrowser::tableViewItemDoubleClick(const QModelIndex &index)
+{
+ if (!index.isValid())
+ return;
+ QFileInfo fileInfo = m_pTableModel->fileInfo(index);
+ /* QFileInfo::isDir() returns true if QFileInfo is a folder or a symlink to folder: */
+ if (!fileInfo.isDir())
+ return;
+ setTableRootIndex(index);
+
+ m_pTreeView->blockSignals(true);
+ setTreeCurrentIndex(index);
+ m_pTreeView->blockSignals(false);
+
+ /* Check if we still have something selected after table root index change: */
+ if (m_pTableView && m_pTableView->selectionModel())
+ emit sigTableSelectionChanged(m_pTableView->selectionModel()->hasSelection());
+}
+
+void UIVisoHostBrowser::treeSelectionChanged(const QModelIndex &selectedTreeIndex)
+{
+ setTableRootIndex(selectedTreeIndex);
+}
+
+void UIVisoHostBrowser::showHideHiddenObjects(bool bShow)
+{
+ if (bShow)
+ {
+ m_pTreeModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden);
+ m_pTableModel->setFilter(QDir::AllEntries | QDir::NoDot | QDir::Hidden | QDir::System);
+ }
+ else
+ {
+ m_pTreeModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);
+ m_pTableModel->setFilter(QDir::AllEntries | QDir::NoDot);
+ }
+}
+
+QString UIVisoHostBrowser::currentPath() const
+{
+ if (!m_pTreeView || !m_pTreeModel)
+ return QString();
+ QModelIndex currentTreeIndex = m_pTreeView->selectionModel()->currentIndex();
+ return QDir::fromNativeSeparators(m_pTreeModel->filePath(currentTreeIndex));
+}
+
+void UIVisoHostBrowser::setCurrentPath(const QString &strPath)
+{
+ if (strPath.isEmpty() || !m_pTreeModel)
+ return;
+ QModelIndex index = m_pTreeModel->index(strPath);
+ setTreeCurrentIndex(index);
+}
+
+bool UIVisoHostBrowser::tableViewHasSelection() const
+{
+ if (!m_pTableView)
+ return false;
+ QItemSelectionModel *pSelectionModel = m_pTableView->selectionModel();
+ if (!pSelectionModel)
+ return false;
+ return pSelectionModel->hasSelection();
+}
+
+void UIVisoHostBrowser::sltHandleAddAction()
+{
+ if (!m_pTableView || !m_pTableModel)
+ return;
+ QItemSelectionModel *pSelectionModel = m_pTableView->selectionModel();
+ if (!pSelectionModel)
+ return;
+ QModelIndexList selectedIndices = pSelectionModel->selectedRows(0);
+ QStringList pathList;
+ for (int i = 0; i < selectedIndices.size(); ++i)
+ {
+ QString strPath = m_pTableModel->filePath(selectedIndices[i]);
+ if (strPath.contains(".."))
+ continue;
+ pathList << strPath;
+ }
+ emit sigAddObjectsToViso(pathList);
+}
+
+void UIVisoHostBrowser::setTableRootIndex(QModelIndex index /* = QModelIndex */)
+{
+ if (!m_pTreeView || !m_pTreeView->selectionModel() || !m_pTableView)
+ return;
+ QString strCurrentTreePath;
+ if (!index.isValid())
+ {
+ QModelIndex currentTreeIndex = m_pTreeView->selectionModel()->currentIndex();
+ strCurrentTreePath = m_pTreeModel->filePath(currentTreeIndex);
+ }
+ else
+ strCurrentTreePath = m_pTreeModel->filePath(index);
+ if (!strCurrentTreePath.isEmpty())
+ m_pTableView->setRootIndex(m_pTableModel->index(strCurrentTreePath));
+ updateLocationSelectorText(strCurrentTreePath);
+}
+
+void UIVisoHostBrowser::setTreeCurrentIndex(QModelIndex index /* = QModelIndex() */)
+{
+ QString strCurrentTablePath;
+ if (!index.isValid())
+ {
+ QModelIndex currentTableIndex = m_pTableView->selectionModel()->currentIndex();
+ strCurrentTablePath = m_pTableModel->filePath(currentTableIndex);
+ }
+ else
+ strCurrentTablePath = m_pTableModel->filePath(index);
+ QModelIndex treeIndex = m_pTreeModel->index(strCurrentTablePath);
+ m_pTreeView->setCurrentIndex(treeIndex);
+ m_pTreeView->setExpanded(treeIndex, true);
+ m_pTreeView->scrollTo(treeIndex, QAbstractItemView::PositionAtCenter);
+}
+
+
+#include "UIVisoHostBrowser.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoHostBrowser.h b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoHostBrowser.h
new file mode 100644
index 00000000..4ab3fa49
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/medium/viso/UIVisoHostBrowser.h
@@ -0,0 +1,89 @@
+/* $Id: UIVisoHostBrowser.h $ */
+/** @file
+ * VBox Qt GUI - UIVisoHostBrowser class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_medium_viso_UIVisoHostBrowser_h
+#define FEQT_INCLUDED_SRC_medium_viso_UIVisoHostBrowser_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIVisoBrowserBase.h"
+
+/* Forward declarations: */
+class QItemSelection;
+class QTableView;
+class UIVisoHostBrowserModel;
+
+/** A UIVisoBrowserBase extension to view host file system. Uses QFileSystemModel. */
+class UIVisoHostBrowser : public UIVisoBrowserBase
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigAddObjectsToViso(QStringList pathList);
+ void sigTableSelectionChanged(bool fIsSelectionEmpty);
+
+public:
+
+ UIVisoHostBrowser(QWidget *pParent = 0);
+ ~UIVisoHostBrowser();
+ virtual void showHideHiddenObjects(bool bShow) final override;
+ QString currentPath() const;
+ void setCurrentPath(const QString &strPath);
+ virtual bool tableViewHasSelection() const final override;
+
+public slots:
+
+ void sltHandleAddAction();
+
+protected:
+
+ virtual void retranslateUi() final override;
+ virtual void tableViewItemDoubleClick(const QModelIndex &index) final override;
+ virtual void setTableRootIndex(QModelIndex index = QModelIndex()) final override;
+ virtual void setTreeCurrentIndex(QModelIndex index = QModelIndex()) final override;
+ virtual void treeSelectionChanged(const QModelIndex &selectedTreeIndex) final override;
+
+private slots:
+
+ void sltHandleTableSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+
+private:
+
+ void prepareObjects();
+ void prepareConnections();
+
+ /** We have two file system models (one for each item view) since we set different filters on each of these models. */
+ UIVisoHostBrowserModel *m_pTreeModel;
+ UIVisoHostBrowserModel *m_pTableModel;
+ QTableView *m_pTableView;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_medium_viso_UIVisoHostBrowser_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/networking/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIDownloader.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloader.cpp
new file mode 100644
index 00000000..aee9e288
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloader.cpp
@@ -0,0 +1,172 @@
+/* $Id: UIDownloader.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDownloader class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "VBoxUtils.h"
+#include "UIDownloader.h"
+#include "UIMessageCenter.h"
+#include "UINetworkReply.h"
+
+
+UIDownloader::UIDownloader()
+ : m_state(UIDownloaderState_Null)
+{
+ /* Connect async listeners for our own commands: */
+ connect(this, &UIDownloader::sigToStartAcknowledging, this, &UIDownloader::sltStartAcknowledging, Qt::QueuedConnection);
+ connect(this, &UIDownloader::sigToStartDownloading, this, &UIDownloader::sltStartDownloading, Qt::QueuedConnection);
+ connect(this, &UIDownloader::sigToStartVerifying, this, &UIDownloader::sltStartVerifying, Qt::QueuedConnection);
+}
+
+void UIDownloader::sltStartAcknowledging()
+{
+ /* Set state to acknowledging: */
+ m_state = UIDownloaderState_Acknowledging;
+
+ /* Send HEAD requests: */
+ createNetworkRequest(UINetworkRequestType_HEAD, m_sources);
+}
+
+void UIDownloader::sltStartDownloading()
+{
+ /* Set state to downloading: */
+ m_state = UIDownloaderState_Downloading;
+
+ /* Send GET request: */
+ createNetworkRequest(UINetworkRequestType_GET, QList<QUrl>() << m_source, m_strTarget);
+}
+
+void UIDownloader::sltStartVerifying()
+{
+ /* Set state to verifying: */
+ m_state = UIDownloaderState_Verifying;
+
+ /* Send GET request: */
+ createNetworkRequest(UINetworkRequestType_GET, QList<QUrl>() << m_strPathSHA256SumsFile);
+}
+
+QString UIDownloader::description() const
+{
+ /* Look for known state: */
+ switch (m_state)
+ {
+ case UIDownloaderState_Acknowledging: return tr("Looking for %1...");
+ case UIDownloaderState_Downloading: return tr("Downloading %1...");
+ case UIDownloaderState_Verifying: return tr("Verifying %1...");
+ default: break;
+ }
+ /* Return null-string by default: */
+ return QString();
+}
+
+void UIDownloader::processNetworkReplyProgress(qint64 iReceived, qint64 iTotal)
+{
+ /* Notify listeners: */
+ emit sigProgressChange((double)iReceived / iTotal * 100);
+}
+
+void UIDownloader::processNetworkReplyFailed(const QString &strError)
+{
+ /* Notify listeners: */
+ emit sigProgressFailed(strError);
+}
+
+void UIDownloader::processNetworkReplyCanceled(UINetworkReply *)
+{
+ /* Notify listeners: */
+ emit sigProgressCanceled();
+}
+
+void UIDownloader::processNetworkReplyFinished(UINetworkReply *pNetworkReply)
+{
+ /* Process reply: */
+ switch (m_state)
+ {
+ case UIDownloaderState_Acknowledging:
+ {
+ handleAcknowledgingResult(pNetworkReply);
+ break;
+ }
+ case UIDownloaderState_Downloading:
+ {
+ handleDownloadingResult(pNetworkReply);
+ break;
+ }
+ case UIDownloaderState_Verifying:
+ {
+ handleVerifyingResult(pNetworkReply);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void UIDownloader::handleAcknowledgingResult(UINetworkReply *pNetworkReply)
+{
+ /* Get the final source: */
+ m_source = pNetworkReply->url();
+
+ /* Ask for downloading: */
+ if (askForDownloadingConfirmation(pNetworkReply))
+ {
+ /* Start downloading: */
+ startDelayedDownloading();
+ }
+ else
+ {
+ /* Notify listeners: */
+ emit sigProgressFinished();
+ }
+}
+
+void UIDownloader::handleDownloadingResult(UINetworkReply *pNetworkReply)
+{
+ /* Handle downloaded object: */
+ handleDownloadedObject(pNetworkReply);
+
+ /* Check whether we should do verification: */
+ if (!m_strPathSHA256SumsFile.isEmpty())
+ {
+ /* Start verifying: */
+ startDelayedVerifying();
+ }
+ else
+ {
+ /* Notify listeners: */
+ emit sigProgressFinished();
+ }
+}
+
+void UIDownloader::handleVerifyingResult(UINetworkReply *pNetworkReply)
+{
+ /* Handle verified object: */
+ handleVerifiedObject(pNetworkReply);
+
+ /* Notify listeners: */
+ emit sigProgressFinished();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIDownloader.h b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloader.h
new file mode 100644
index 00000000..bb8aa18b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloader.h
@@ -0,0 +1,173 @@
+/* $Id: UIDownloader.h $ */
+/** @file
+ * VBox Qt GUI - UIDownloader class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UIDownloader_h
+#define FEQT_INCLUDED_SRC_networking_UIDownloader_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QUrl>
+#include <QList>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UINetworkCustomer.h"
+#include "UINetworkDefs.h"
+
+/* Forward declarations: */
+class QString;
+class UINetworkReply;
+
+/** Downloader interface.
+ * UINetworkCustomer class extension which allows background http downloading. */
+class SHARED_LIBRARY_STUFF UIDownloader : public UINetworkCustomer
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Signals to start acknowledging. */
+ void sigToStartAcknowledging();
+ /** Signals to start downloading. */
+ void sigToStartDownloading();
+ /** Signals to start verifying. */
+ void sigToStartVerifying();
+
+ /** Notifies listeners about progress change to @a iPercent. */
+ void sigProgressChange(ulong uPercent);
+ /** Notifies listeners about progress failed with @a strError. */
+ void sigProgressFailed(const QString &strError);
+ /** Notifies listeners about progress canceled. */
+ void sigProgressCanceled();
+ /** Notifies listeners about progress finished. */
+ void sigProgressFinished();
+
+public:
+
+ /** Constructs downloader. */
+ UIDownloader();
+
+public slots:
+
+ /** Starts the sequence. */
+ void start() { startDelayedAcknowledging(); }
+ /** Cancels the sequence. */
+ void cancel() { cancelNetworkRequest(); }
+
+protected slots:
+
+ /** Performs acknowledging part. */
+ void sltStartAcknowledging();
+ /** Performs downloading part. */
+ void sltStartDownloading();
+ /** Performs verifying part. */
+ void sltStartVerifying();
+
+protected:
+
+ /** Appends subsequent source to try to download from. */
+ void addSource(const QString &strSource) { m_sources << QUrl(strSource); }
+ /** Defines the only one source to try to download from. */
+ void setSource(const QString &strSource) { m_sources.clear(); addSource(strSource); }
+ /** Returns a list of sources to try to download from. */
+ const QList<QUrl> &sources() const { return m_sources; }
+ /** Returns a current source to try to download from. */
+ const QUrl &source() const { return m_source; }
+
+ /** Defines the @a strTarget file-path used to save downloaded file to. */
+ void setTarget(const QString &strTarget) { m_strTarget = strTarget; }
+ /** Returns the target file-path used to save downloaded file to. */
+ const QString &target() const { return m_strTarget; }
+
+ /** Defines the @a strPathSHA256SumsFile. */
+ void setPathSHA256SumsFile(const QString &strPathSHA256SumsFile) { m_strPathSHA256SumsFile = strPathSHA256SumsFile; }
+ /** Returns the SHA-256 sums file-path. */
+ QString pathSHA256SumsFile() const { return m_strPathSHA256SumsFile; }
+
+ /** Returns description of the current network operation. */
+ virtual QString description() const;
+
+ /** Handles network-reply progress for @a iReceived bytes of @a iTotal. */
+ virtual void processNetworkReplyProgress(qint64 iReceived, qint64 iTotal) RT_OVERRIDE;
+ /** Handles network-reply failed with specified @a strError. */
+ virtual void processNetworkReplyFailed(const QString &strError) RT_OVERRIDE;
+ /** Handles network-reply cancel request for @a pReply. */
+ virtual void processNetworkReplyCanceled(UINetworkReply *pReply) RT_OVERRIDE;
+ /** Handles network-reply finish for @a pReply. */
+ virtual void processNetworkReplyFinished(UINetworkReply *pReply) RT_OVERRIDE;
+
+ /** Asks user for downloading confirmation for passed @a pReply. */
+ virtual bool askForDownloadingConfirmation(UINetworkReply *pReply) = 0;
+ /** Handles downloaded object for passed @a pReply. */
+ virtual void handleDownloadedObject(UINetworkReply *pReply) = 0;
+ /** Handles verified object for passed @a pReply. */
+ virtual void handleVerifiedObject(UINetworkReply *pReply) { Q_UNUSED(pReply); }
+
+private:
+
+ /** UIDownloader states. */
+ enum UIDownloaderState
+ {
+ UIDownloaderState_Null,
+ UIDownloaderState_Acknowledging,
+ UIDownloaderState_Downloading,
+ UIDownloaderState_Verifying
+ };
+
+ /** Starts delayed acknowledging. */
+ void startDelayedAcknowledging() { emit sigToStartAcknowledging(); }
+ /** Starts delayed downloading. */
+ void startDelayedDownloading() { emit sigToStartDownloading(); }
+ /** Starts delayed verifying. */
+ void startDelayedVerifying() { emit sigToStartVerifying(); }
+
+ /** Handles acknowledging result. */
+ void handleAcknowledgingResult(UINetworkReply *pReply);
+ /** Handles downloading result. */
+ void handleDownloadingResult(UINetworkReply *pReply);
+ /** Handles verifying result. */
+ void handleVerifyingResult(UINetworkReply *pReply);
+
+ /** Holds the downloader state. */
+ UIDownloaderState m_state;
+
+ /** Holds the downloading sources. */
+ QList<QUrl> m_sources;
+ /** Holds the current downloading source. */
+ QUrl m_source;
+
+ /** Holds the downloading target path. */
+ QString m_strTarget;
+
+ /** Holds the SHA-256 sums file path. */
+ QString m_strPathSHA256SumsFile;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UIDownloader_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderExtensionPack.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderExtensionPack.cpp
new file mode 100644
index 00000000..d49252ee
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderExtensionPack.cpp
@@ -0,0 +1,182 @@
+/* $Id: UIDownloaderExtensionPack.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDownloaderExtensionPack class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QFile>
+#include <QRegularExpression>
+#include <QVariant>
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "UICommon.h"
+#include "UIDownloaderExtensionPack.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINetworkReply.h"
+#include "UINotificationCenter.h"
+#include "UIVersion.h"
+
+/* Other VBox includes: */
+#include <iprt/sha.h>
+
+
+UIDownloaderExtensionPack::UIDownloaderExtensionPack()
+{
+ /* Get version number and adjust it for test and trunk builds. The server only has official releases. */
+ const QString strVersion = UIVersion(uiCommon().vboxVersionStringNormalized()).effectiveReleasedVersion().toString();
+
+ /* Prepare source/target: */
+ const QString strUnderscoredName = QString(GUI_ExtPackName).replace(' ', '_');
+ const QString strSourceName = QString("%1-%2.vbox-extpack").arg(strUnderscoredName, strVersion);
+ const QString strSourcePath = QString("https://download.virtualbox.org/virtualbox/%1/").arg(strVersion);
+ const QString strSource = strSourcePath + strSourceName;
+ const QString strPathSHA256SumsFile = QString("https://www.virtualbox.org/download/hashes/%1/SHA256SUMS").arg(strVersion);
+ const QString strTarget = QDir(uiCommon().homeFolder()).absoluteFilePath(strSourceName);
+
+ /* Set source/target: */
+ setSource(strSource);
+ setTarget(strTarget);
+ setPathSHA256SumsFile(strPathSHA256SumsFile);
+}
+
+QString UIDownloaderExtensionPack::description() const
+{
+ return UIDownloader::description().arg(tr("VirtualBox Extension Pack"));
+}
+
+bool UIDownloaderExtensionPack::askForDownloadingConfirmation(UINetworkReply *pReply)
+{
+ return msgCenter().confirmDownloadExtensionPack(GUI_ExtPackName, source().toString(), pReply->header(UINetworkReply::ContentLengthHeader).toInt());
+}
+
+void UIDownloaderExtensionPack::handleDownloadedObject(UINetworkReply *pReply)
+{
+ m_receivedData = pReply->readAll();
+}
+
+void UIDownloaderExtensionPack::handleVerifiedObject(UINetworkReply *pReply)
+{
+ /* Try to verify the SHA-256 checksum: */
+ QString strCalculatedSumm;
+ bool fSuccess = false;
+ do
+ {
+ /* Read received data into the buffer: */
+ const QByteArray receivedData(pReply->readAll());
+ /* Make sure it's not empty: */
+ if (receivedData.isEmpty())
+ break;
+
+ /* Parse buffer contents to dictionary: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ const QStringList dictionary(QString(receivedData).split("\n", Qt::SkipEmptyParts));
+#else
+ const QStringList dictionary(QString(receivedData).split("\n", QString::SkipEmptyParts));
+#endif
+ /* Make sure it's not empty: */
+ if (dictionary.isEmpty())
+ break;
+
+ /* Parse each record to tags, look for the required one: */
+ foreach (const QString &strRecord, dictionary)
+ {
+ QRegularExpression separator(" \\*| ");
+ const QString strFileName = strRecord.section(separator, 1);
+ const QString strDownloadedSumm = strRecord.section(separator, 0, 0);
+ if (strFileName == source().fileName())
+ {
+ /* Calc the SHA-256 on the bytes, creating a string: */
+ uint8_t abHash[RTSHA256_HASH_SIZE];
+ RTSha256(m_receivedData.constData(), m_receivedData.length(), abHash);
+ char szDigest[RTSHA256_DIGEST_LEN + 1];
+ int rc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
+ if (RT_FAILURE(rc))
+ {
+ AssertRC(rc);
+ szDigest[0] = '\0';
+ }
+ strCalculatedSumm = szDigest;
+ //printf("Downloaded SHA-256 summ: [%s]\n", strDownloadedSumm.toUtf8().constData());
+ //printf("Calculated SHA-256 summ: [%s]\n", strCalculatedSumm.toUtf8().constData());
+ /* Make sure checksum is valid: */
+ fSuccess = strDownloadedSumm == strCalculatedSumm;
+ break;
+ }
+ }
+ }
+ while (false);
+
+ /* If SHA-256 checksum verification failed: */
+ if (!fSuccess)
+ {
+ /* Warn the user about additions-image was downloaded and saved but checksum is invalid: */
+ UINotificationMessage::cannotValidateExtentionPackSHA256Sum(GUI_ExtPackName, source().toString(), QDir::toNativeSeparators(target()));
+ return;
+ }
+
+ /* Serialize that buffer into the file: */
+ while (true)
+ {
+ /* Make sure the file already exists. If we reached
+ * this place, it's already written and checked. */
+ QFile file(target());
+ bool fSuccess = false;
+ /* Check step. Try to open file for reading first. */
+ if (file.open(QIODevice::ReadOnly))
+ fSuccess = true;
+ /* Failsafe step. Try to open file for writing otherwise. */
+ if (!fSuccess && file.open(QIODevice::WriteOnly))
+ {
+ /* Write buffer into the file: */
+ file.write(m_receivedData);
+ file.close();
+ fSuccess = true;
+ }
+ /* If the file already exists or was just written: */
+ if (fSuccess)
+ {
+ /* Warn the listener about extension-pack was downloaded: */
+ emit sigDownloadFinished(source().toString(), target(), strCalculatedSumm);
+ break;
+ }
+
+ /* Warn the user about extension-pack was downloaded but was NOT saved: */
+ msgCenter().cannotSaveExtensionPack(GUI_ExtPackName, source().toString(), QDir::toNativeSeparators(target()));
+
+ /* Ask the user for another location for the extension-pack file: */
+ QString strTarget = QIFileDialog::getExistingDirectory(QFileInfo(target()).absolutePath(),
+ windowManager().mainWindowShown(),
+ tr("Select folder to save %1 to").arg(GUI_ExtPackName), true);
+
+ /* Check if user had really set a new target: */
+ if (!strTarget.isNull())
+ setTarget(QDir(strTarget).absoluteFilePath(QFileInfo(target()).fileName()));
+ else
+ break;
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderExtensionPack.h b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderExtensionPack.h
new file mode 100644
index 00000000..cd7beb2b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderExtensionPack.h
@@ -0,0 +1,74 @@
+/* $Id: UIDownloaderExtensionPack.h $ */
+/** @file
+ * VBox Qt GUI - UIDownloaderExtensionPack class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UIDownloaderExtensionPack_h
+#define FEQT_INCLUDED_SRC_networking_UIDownloaderExtensionPack_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIDownloader.h"
+
+/* Forward declarations: */
+class QByteArray;
+
+/** UIDownloader extension for background extension-pack downloading. */
+class SHARED_LIBRARY_STUFF UIDownloaderExtensionPack : public UIDownloader
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about downloading finished.
+ * @param strSource Brings the downloading source.
+ * @param strTarget Brings the downloading target.
+ * @param strHash Brings the downloaded file hash. */
+ void sigDownloadFinished(const QString &strSource, const QString &strTarget, const QString &strHash);
+
+public:
+
+ /** Constructs downloader. */
+ UIDownloaderExtensionPack();
+
+private:
+
+ /** Returns description of the current network operation. */
+ virtual QString description() const RT_OVERRIDE;
+
+ /** Asks user for downloading confirmation for passed @a pReply. */
+ virtual bool askForDownloadingConfirmation(UINetworkReply *pReply) RT_OVERRIDE;
+ /** Handles downloaded object for passed @a pReply. */
+ virtual void handleDownloadedObject(UINetworkReply *pReply) RT_OVERRIDE;
+ /** Handles verified object for passed @a pReply. */
+ virtual void handleVerifiedObject(UINetworkReply *pReply) RT_OVERRIDE;
+
+ /** Holds the cached received data awaiting for verification. */
+ QByteArray m_receivedData;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UIDownloaderExtensionPack_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderGuestAdditions.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderGuestAdditions.cpp
new file mode 100644
index 00000000..2078a025
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderGuestAdditions.cpp
@@ -0,0 +1,199 @@
+/* $Id: UIDownloaderGuestAdditions.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDownloaderGuestAdditions class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QFile>
+#include <QRegularExpression>
+#include <QVariant>
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "UICommon.h"
+#include "UIDownloaderGuestAdditions.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINetworkReply.h"
+#include "UINotificationCenter.h"
+#include "UIVersion.h"
+
+/* Other VBox includes: */
+#include <iprt/sha.h>
+
+
+UIDownloaderGuestAdditions::UIDownloaderGuestAdditions()
+{
+ /* Get version number and adjust it for test and trunk builds. The server only has official releases. */
+ const QString strVersion = UIVersion(uiCommon().vboxVersionStringNormalized()).effectiveReleasedVersion().toString();
+
+ /* Prepare source/target: */
+ const QString strSourceName = QString("%1_%2.iso").arg(GUI_GuestAdditionsName, strVersion);
+ const QString strSourcePath = QString("https://download.virtualbox.org/virtualbox/%1/").arg(strVersion);
+ const QString strSource = strSourcePath + strSourceName;
+ const QString strPathSHA256SumsFile = QString("https://www.virtualbox.org/download/hashes/%1/SHA256SUMS").arg(strVersion);
+ const QString strTarget = QDir(uiCommon().homeFolder()).absoluteFilePath(QString("%1.tmp").arg(strSourceName));
+
+ /* Set source/target: */
+ setSource(strSource);
+ setTarget(strTarget);
+ setPathSHA256SumsFile(strPathSHA256SumsFile);
+}
+
+QString UIDownloaderGuestAdditions::description() const
+{
+ return UIDownloader::description().arg(tr("VirtualBox Guest Additions"));
+}
+
+bool UIDownloaderGuestAdditions::askForDownloadingConfirmation(UINetworkReply *pReply)
+{
+ return msgCenter().confirmDownloadGuestAdditions(source().toString(), pReply->header(UINetworkReply::ContentLengthHeader).toInt());
+}
+
+void UIDownloaderGuestAdditions::handleDownloadedObject(UINetworkReply *pReply)
+{
+ m_receivedData = pReply->readAll();
+}
+
+void UIDownloaderGuestAdditions::handleVerifiedObject(UINetworkReply *pReply)
+{
+ /* Try to verify the SHA-256 checksum: */
+ QString strCalculatedSumm;
+ bool fSuccess = false;
+ do
+ {
+ /* Read received data into the buffer: */
+ const QByteArray receivedData(pReply->readAll());
+ /* Make sure it's not empty: */
+ if (receivedData.isEmpty())
+ break;
+
+ /* Parse buffer contents to dictionary: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ const QStringList dictionary(QString(receivedData).split("\n", Qt::SkipEmptyParts));
+#else
+ const QStringList dictionary(QString(receivedData).split("\n", QString::SkipEmptyParts));
+#endif
+ /* Make sure it's not empty: */
+ if (dictionary.isEmpty())
+ break;
+
+ /* Parse each record to tags, look for the required one: */
+ foreach (const QString &strRecord, dictionary)
+ {
+ const QString strFileName = strRecord.section(" *", 1);
+ const QString strDownloadedSumm = strRecord.section(" *", 0, 0);
+ if (strFileName == source().fileName())
+ {
+ /* Calc the SHA-256 on the bytes, creating a string: */
+ uint8_t abHash[RTSHA256_HASH_SIZE];
+ RTSha256(m_receivedData.constData(), m_receivedData.length(), abHash);
+ char szDigest[RTSHA256_DIGEST_LEN + 1];
+ int rc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
+ if (RT_FAILURE(rc))
+ {
+ AssertRC(rc);
+ szDigest[0] = '\0';
+ }
+ strCalculatedSumm = szDigest;
+ //printf("Downloaded SHA-256 summ: [%s]\n", strDownloadedSumm.toUtf8().constData());
+ //printf("Calculated SHA-256 summ: [%s]\n", strCalculatedSumm.toUtf8().constData());
+ /* Make sure checksum is valid: */
+ fSuccess = strDownloadedSumm == strCalculatedSumm;
+ break;
+ }
+ }
+ }
+ while (false);
+
+ /* If SHA-256 checksum verification failed: */
+ if (!fSuccess)
+ {
+ /* Warn the user about additions-image was downloaded and saved but checksum is invalid: */
+ UINotificationMessage::cannotValidateGuestAdditionsSHA256Sum(source().toString(), QDir::toNativeSeparators(target()));
+ return;
+ }
+
+ /* Make sure temporary file exists. If we have
+ * reached this place, it's already written and verified. */
+ const QString strTempFilename = target();
+ if (!QFile::exists(strTempFilename))
+ {
+ /* But still we are providing a failsafe.
+ * Since we can try to write it again. */
+ QFile file(strTempFilename);
+ if (!file.open(QIODevice::WriteOnly))
+ AssertFailedReturnVoid();
+ file.write(m_receivedData);
+ }
+
+ /* Rename temporary file to target one. This can require a number
+ * of tries to let user choose the place to save file to. */
+ QString strNetTarget = target();
+ strNetTarget.remove(QRegularExpression("\\.tmp$"));
+ setTarget(strNetTarget);
+ while (true)
+ {
+ /* Make sure target file doesn't exist: */
+ bool fTargetFileExists = QFile::exists(target());
+ if (fTargetFileExists)
+ {
+ /* We should ask user about file rewriting (or exit otherwise): */
+ if (!msgCenter().confirmOverridingFile(QDir::toNativeSeparators(target())))
+ break;
+ /* And remove file if rewriting confirmed: */
+ if (QFile::remove(target()))
+ fTargetFileExists = false;
+ }
+
+ /* Try to rename temporary file to target one (would fail if target file still exists): */
+ const bool fFileRenamed = !fTargetFileExists && QFile::rename(strTempFilename, target());
+
+ /* If file renamed: */
+ if (fFileRenamed)
+ {
+ /* Warn the user about additions-image downloaded and saved, propose to mount it (and/or exit in any case): */
+ if (msgCenter().proposeMountGuestAdditions(source().toString(), QDir::toNativeSeparators(target())))
+ emit sigDownloadFinished(target());
+ break;
+ }
+ else
+ {
+ /* Warn the user about additions-image was downloaded but was NOT saved: */
+ msgCenter().cannotSaveGuestAdditions(source().toString(), QDir::toNativeSeparators(target()));
+ /* Ask the user for another location for the additions-image file: */
+ const QString strTarget = QIFileDialog::getExistingDirectory(QFileInfo(target()).absolutePath(),
+ windowManager().mainWindowShown(),
+ tr("Select folder to save Guest Additions image to"), true);
+
+ /* Check if user had really set a new target (and exit in opposite case): */
+ if (!strTarget.isNull())
+ setTarget(QDir(strTarget).absoluteFilePath(QFileInfo(target()).fileName()));
+ else
+ break;
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderGuestAdditions.h b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderGuestAdditions.h
new file mode 100644
index 00000000..5c438c90
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderGuestAdditions.h
@@ -0,0 +1,72 @@
+/* $Id: UIDownloaderGuestAdditions.h $ */
+/** @file
+ * VBox Qt GUI - UIDownloaderGuestAdditions class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UIDownloaderGuestAdditions_h
+#define FEQT_INCLUDED_SRC_networking_UIDownloaderGuestAdditions_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIDownloader.h"
+
+/* Forward declarations: */
+class QByteArray;
+
+/** UIDownloader extension for background guest additions downloading. */
+class SHARED_LIBRARY_STUFF UIDownloaderGuestAdditions : public UIDownloader
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about downloading finished.
+ * @param strFile Brings the downloaded file-name. */
+ void sigDownloadFinished(const QString &strFile);
+
+public:
+
+ /** Constructs downloader. */
+ UIDownloaderGuestAdditions();
+
+private:
+
+ /** Returns description of the current network operation. */
+ virtual QString description() const RT_OVERRIDE;
+
+ /** Asks user for downloading confirmation for passed @a pReply. */
+ virtual bool askForDownloadingConfirmation(UINetworkReply *pReply) RT_OVERRIDE;
+ /** Handles downloaded object for passed @a pReply. */
+ virtual void handleDownloadedObject(UINetworkReply *pReply) RT_OVERRIDE;
+ /** Handles verified object for passed @a pReply. */
+ virtual void handleVerifiedObject(UINetworkReply *pReply) RT_OVERRIDE;
+
+ /** Holds the cached received data awaiting for verification. */
+ QByteArray m_receivedData;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UIDownloaderGuestAdditions_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderUserManual.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderUserManual.cpp
new file mode 100644
index 00000000..4d7f77ab
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderUserManual.cpp
@@ -0,0 +1,120 @@
+/* $Id: UIDownloaderUserManual.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDownloaderUserManual class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QFile>
+#include <QVariant>
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "UICommon.h"
+#include "UIDownloaderUserManual.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINetworkReply.h"
+#include "UINotificationCenter.h"
+#include "UIVersion.h"
+
+
+UIDownloaderUserManual::UIDownloaderUserManual()
+{
+ /* Get version number and adjust it for test and trunk builds. The server only has official releases. */
+ const QString strVersion = UIVersion(uiCommon().vboxVersionStringNormalized()).effectiveReleasedVersion().toString();
+
+ /* Compose User Manual filename: */
+ QString strUserManualFullFileName = uiCommon().helpFile();
+ QString strUserManualShortFileName = QFileInfo(strUserManualFullFileName).fileName();
+
+ /* Add sources: */
+ QString strSource1 = QString("https://download.virtualbox.org/virtualbox/%1/").arg(strVersion) + strUserManualShortFileName;
+ QString strSource2 = QString("https://download.virtualbox.org/virtualbox/") + strUserManualShortFileName;
+ addSource(strSource1);
+ addSource(strSource2);
+
+ /* Set target: */
+ QString strUserManualDestination = QDir(uiCommon().homeFolder()).absoluteFilePath(strUserManualShortFileName);
+ setTarget(strUserManualDestination);
+}
+
+QString UIDownloaderUserManual::description() const
+{
+ return UIDownloader::description().arg(tr("VirtualBox User Manual"));
+}
+
+bool UIDownloaderUserManual::askForDownloadingConfirmation(UINetworkReply *pReply)
+{
+ return msgCenter().confirmDownloadUserManual(source().toString(), pReply->header(UINetworkReply::ContentLengthHeader).toInt());
+}
+
+void UIDownloaderUserManual::handleDownloadedObject(UINetworkReply *pReply)
+{
+ /* Read received data into the buffer: */
+ QByteArray receivedData(pReply->readAll());
+ /* Serialize that buffer into the file: */
+ while (true)
+ {
+ /* Make sure the file already exists. If we reached
+ * this place, it's already written and checked. */
+ QFile file(target());
+ bool fSuccess = false;
+ /* Check step. Try to open file for reading first. */
+ if (file.open(QIODevice::ReadOnly))
+ fSuccess = true;
+ /* Failsafe step. Try to open file for writing otherwise. */
+ if (!fSuccess && file.open(QIODevice::WriteOnly))
+ {
+ /* Write buffer into the file: */
+ file.write(receivedData);
+ file.close();
+ fSuccess = true;
+ }
+ /* If the file already exists or was just written: */
+ if (fSuccess)
+ {
+ /* Warn the user about user-manual loaded and saved: */
+ UINotificationMessage::warnAboutUserManualDownloaded(source().toString(), QDir::toNativeSeparators(target()));
+ /* Warn the listener about user-manual was downloaded: */
+ emit sigDownloadFinished(target());
+ break;
+ }
+
+ /* Warn user about user-manual was downloaded but was NOT saved: */
+ msgCenter().cannotSaveUserManual(source().toString(), QDir::toNativeSeparators(target()));
+
+ /* Ask the user for another location for the user-manual file: */
+ QString strTarget = QIFileDialog::getExistingDirectory(QFileInfo(target()).absolutePath(),
+ windowManager().mainWindowShown(),
+ tr("Select folder to save User Manual to"), true);
+
+ /* Check if user had really set a new target: */
+ if (!strTarget.isNull())
+ setTarget(QDir(strTarget).absoluteFilePath(QFileInfo(target()).fileName()));
+ else
+ break;
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderUserManual.h b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderUserManual.h
new file mode 100644
index 00000000..bc57a686
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderUserManual.h
@@ -0,0 +1,64 @@
+/* $Id: UIDownloaderUserManual.h $ */
+/** @file
+ * VBox Qt GUI - UIDownloaderUserManual class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UIDownloaderUserManual_h
+#define FEQT_INCLUDED_SRC_networking_UIDownloaderUserManual_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIDownloader.h"
+
+/** UIDownloader extension for background user-manual downloading. */
+class SHARED_LIBRARY_STUFF UIDownloaderUserManual : public UIDownloader
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about downloading finished.
+ * @param strFile Brings the downloaded file-name. */
+ void sigDownloadFinished(const QString &strFile);
+
+public:
+
+ /** Constructs downloader. */
+ UIDownloaderUserManual();
+
+private:
+
+ /** Returns description of the current network operation. */
+ virtual QString description() const RT_OVERRIDE;
+
+ /** Asks user for downloading confirmation for passed @a pReply. */
+ virtual bool askForDownloadingConfirmation(UINetworkReply *pReply) RT_OVERRIDE;
+ /** Handles downloaded object for passed @a pReply. */
+ virtual void handleDownloadedObject(UINetworkReply *pReply) RT_OVERRIDE;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UIDownloaderUserManual_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINetworkCustomer.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkCustomer.cpp
new file mode 100644
index 00000000..f8bf4067
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkCustomer.cpp
@@ -0,0 +1,53 @@
+/* $Id: UINetworkCustomer.cpp $ */
+/** @file
+ * VBox Qt GUI - UINetworkCustomer class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UINetworkCustomer.h"
+#include "UINetworkRequestManager.h"
+
+
+UINetworkCustomer::UINetworkCustomer()
+{
+}
+
+UINetworkCustomer::~UINetworkCustomer()
+{
+ emit sigBeingDestroyed(this);
+}
+
+void UINetworkCustomer::createNetworkRequest(UINetworkRequestType enmType,
+ const QList<QUrl> urls,
+ const QString &strTarget /* = QString() */,
+ const UserDictionary requestHeaders /* = UserDictionary() */)
+{
+ m_uId = gNetworkManager->createNetworkRequest(enmType, urls, strTarget, requestHeaders, this);
+}
+
+void UINetworkCustomer::cancelNetworkRequest()
+{
+ gNetworkManager->cancelNetworkRequest(m_uId);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINetworkCustomer.h b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkCustomer.h
new file mode 100644
index 00000000..da117182
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkCustomer.h
@@ -0,0 +1,96 @@
+/* $Id: UINetworkCustomer.h $ */
+/** @file
+ * VBox Qt GUI - UINetworkCustomer class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UINetworkCustomer_h
+#define FEQT_INCLUDED_SRC_networking_UINetworkCustomer_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QUrl>
+#include <QUuid>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UINetworkDefs.h"
+
+/* Forward declarations: */
+class UINetworkReply;
+
+/** Interface to access UINetworkRequestManager protected functionality. */
+class SHARED_LIBRARY_STUFF UINetworkCustomer : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a pNetworkCustomer being destroyed. */
+ void sigBeingDestroyed(UINetworkCustomer *pNetworkCustomer);
+
+public:
+
+ /** Constructs network customer passing @a pParent to the base-class. */
+ UINetworkCustomer();
+ /** Destructs network customer. */
+ virtual ~UINetworkCustomer() RT_OVERRIDE;
+
+ /** Returns description of the current network operation. */
+ virtual QString description() const { return QString(); }
+
+ /** Handles network reply progress for @a iReceived amount of bytes among @a iTotal. */
+ virtual void processNetworkReplyProgress(qint64 iReceived, qint64 iTotal) = 0;
+ /** Handles network reply failed with specified @a strError. */
+ virtual void processNetworkReplyFailed(const QString &strError) = 0;
+ /** Handles network reply canceling for a passed @a pReply. */
+ virtual void processNetworkReplyCanceled(UINetworkReply *pReply) = 0;
+ /** Handles network reply finishing for a passed @a pReply. */
+ virtual void processNetworkReplyFinished(UINetworkReply *pReply) = 0;
+
+protected:
+
+ /** Creates network-request.
+ * @param enmType Brings request type.
+ * @param urls Brings request urls, there can be few of them.
+ * @param strTarget Brings request target path.
+ * @param requestHeaders Brings request headers in dictionary form. */
+ void createNetworkRequest(UINetworkRequestType enmType,
+ const QList<QUrl> urls,
+ const QString &strTarget = QString(),
+ const UserDictionary requestHeaders = UserDictionary());
+
+ /** Aborts network-request. */
+ void cancelNetworkRequest();
+
+private:
+
+ /** Holds the network-request ID. */
+ QUuid m_uId;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UINetworkCustomer_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINetworkDefs.h b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkDefs.h
new file mode 100644
index 00000000..0a309283
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkDefs.h
@@ -0,0 +1,48 @@
+/* $Id: UINetworkDefs.h $ */
+/** @file
+ * VBox Qt GUI - Network routine related declarations.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UINetworkDefs_h
+#define FEQT_INCLUDED_SRC_networking_UINetworkDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+
+/** Network request types. */
+enum UINetworkRequestType
+{
+ UINetworkRequestType_HEAD,
+ UINetworkRequestType_GET
+};
+
+/** User dictionary. */
+typedef QMap<QString, QString> UserDictionary;
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UINetworkDefs_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINetworkReply.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkReply.cpp
new file mode 100644
index 00000000..8e89e0fc
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkReply.cpp
@@ -0,0 +1,895 @@
+/* $Id: UINetworkReply.cpp $ */
+/** @file
+ * VBox Qt GUI - UINetworkReply stuff implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QFile>
+#include <QThread>
+#include <QVector>
+#include <QVariant>
+
+/* GUI includes: */
+#include "UINetworkReply.h"
+#include "UINetworkRequestManager.h"
+#include "UIExtraDataManager.h"
+#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
+# include "UICommon.h"
+# include "VBoxUtils.h"
+# include "CSystemProperties.h"
+#endif
+
+/* Other VBox includes: */
+#include <iprt/initterm.h>
+#include <iprt/crypto/pem.h>
+#include <iprt/crypto/store.h>
+#include <iprt/err.h>
+#include <iprt/http.h>
+#include <iprt/path.h>
+#include <iprt/sha.h>
+#include <iprt/string.h>
+#include <iprt/zip.h>
+#include <VBox/log.h>
+
+
+/** QThread extension
+ * used as network-reply private thread interface. */
+class UINetworkReplyPrivateThread : public QThread
+{
+#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about reply progress change.
+ * @param iBytesReceived Holds the current amount of bytes received.
+ * @param iBytesTotal Holds the total amount of bytes to be received. */
+ void sigDownloadProgress(qint64 iBytesReceived, qint64 iBytesTotal);
+#endif /* !VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
+
+public:
+
+ /** Constructs network-reply thread of the passed @a type for the passed @a url and @a requestHeaders. */
+ UINetworkReplyPrivateThread(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders);
+
+ /** @name APIs
+ * @{ */
+ /** Aborts reply. */
+ void abort();
+
+ /** Returns the URL of the reply which is the URL of the request for now. */
+ const QUrl& url() const { return m_url; }
+
+ /** Returns the last cached IPRT HTTP error of the reply. */
+ int error() const { return m_iError; }
+
+ /** Returns binary content of the reply. */
+ const QByteArray& readAll() const { return m_reply; }
+ /** Returns value for the cached reply header of the passed @a type. */
+ QString header(UINetworkReply::KnownHeader type) const;
+
+ /** Returns short descriptive context of thread's current operation. */
+ const QString context() const { return m_strContext; }
+ /** @} */
+
+private:
+
+ /** @name Helpers for HTTP and Certificates handling.
+ * @{ */
+ /** Applies configuration. */
+ int applyConfiguration();
+ /** Applies proxy rules. */
+ int applyProxyRules();
+ /** Applies security certificates. */
+ int applyHttpsCertificates();
+ /** Applies raw headers. */
+ int applyRawHeaders();
+ /** Performs main request. */
+ int performMainRequest();
+
+ /** Performs whole thread functionality. */
+ void run();
+
+ /** Handles download progress callback.
+ * @param cbDownloadTotal Brings the total amount of bytes to be received.
+ * @param cbDownloaded Brings the current amount of bytes received. */
+ void handleProgressChange(uint64_t cbDownloadTotal, uint64_t cbDownloaded);
+ /** @} */
+
+ /** @name Static helpers for HTTP and Certificates handling.
+ * @{ */
+ /** Returns full certificate file-name. */
+ static QString fullCertificateFileName();
+
+ /** Applies proxy rules.
+ * @remarks Implementation doesn't exists, to be removed? */
+ static int applyProxyRules(RTHTTP hHttp, const QString &strHostName, int iPort);
+
+ /** Applies raw headers.
+ * @param hHttp Brings the HTTP client instance.
+ * @param headers Brings the map of headers to be applied. */
+ static int applyRawHeaders(RTHTTP hHttp, const UserDictionary &headers);
+
+ /** Returns the number of certificates found in a search result array.
+ * @param pafFoundCerts Brings the array parallel to s_aCerts with the status of each wanted certificate. */
+ static unsigned countCertsFound(bool const *pafFoundCerts);
+
+ /** Returns whether we've found all the necessary certificates.
+ * @param pafFoundCerts Brings the array parallel to s_aCerts with the status of each wanted certificate. */
+ static bool areAllCertsFound(bool const *pafFoundCerts);
+
+ /** Refreshes the certificates.
+ * @param phStore On input, this holds the current store, so that we can fish out wanted
+ * certificates from it. On successful return, this is replaced with a new
+ * store reflecting the refrehsed content of @a pszCaCertFile.
+ * @param pafFoundCerts On input, this holds the certificates found in the current store.
+ * On return, this reflects what is current in the @a pszCaCertFile.
+ * The array runs parallel to s_aCerts.
+ * @param pszCaCertFile Where to write the refreshed certificates if we've managed to gather
+ * a collection that is at least as good as the old one. */
+ static int refreshCertificates(PRTCRSTORE phStore, bool *pafFoundCerts, const char *pszCaCertFile);
+
+ /** Redirects download progress callback to particular object which can handle it.
+ * @param hHttp Brings the HTTP client instance.
+ * @param pvUser Brings the convenience pointer for the
+ * user-agent object which should handle that callback.
+ * @param cbDownloadTotal Brings the total amount of bytes to be received.
+ * @param cbDownloaded Brings the current amount of bytes received. */
+ static DECLCALLBACK(void) handleProgressChange(RTHTTP hHttp, void *pvUser, uint64_t cbDownloadTotal, uint64_t cbDownloaded);
+ /** @} */
+
+ /** Holds the request type. */
+ const UINetworkRequestType m_type;
+ /** Holds the request url. */
+ const QUrl m_url;
+ /** Holds the request target. */
+ const QString m_strTarget;
+ /** Holds the request headers. */
+ const UserDictionary m_requestHeaders;
+
+ /** Holds the IPRT HTTP client instance handle. */
+ RTHTTP m_hHttp;
+ /** Holds the last cached IPRT HTTP error of the reply. */
+ int m_iError;
+ /** Holds short descriptive context of thread's current operation. */
+ QString m_strContext;
+ /** Holds the reply instance. */
+ QByteArray m_reply;
+ /** Holds the cached reply headers. */
+ UserDictionary m_headers;
+
+ /** Holds the details on the certificates we are after. */
+ static const RTCRCERTWANTED s_aCerts[];
+ /** Holds the certificate file name (no path). */
+ static const QString s_strCertificateFileName;
+
+#ifdef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
+public:
+ /** Starts the test routine. */
+ static void testIt(RTTEST hTest);
+#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
+};
+
+
+#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
+/** QObject extension
+ * used as network-reply private data interface. */
+class UINetworkReplyPrivate : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about reply progress change.
+ * @param iBytesReceived Holds the current amount of bytes received.
+ * @param iBytesTotal Holds the total amount of bytes to be received. */
+ void downloadProgress(qint64 iBytesReceived, qint64 iBytesTotal);
+
+ /** Notifies listeners about reply has finished processing. */
+ void finished();
+
+public:
+
+ /** Constructs network-reply private data of the passed @a type for the passed @a url and @a requestHeaders. */
+ UINetworkReplyPrivate(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders);
+ /** Destructs reply private data. */
+ ~UINetworkReplyPrivate();
+
+ /** Aborts reply. */
+ void abort() { m_pThread->abort(); }
+
+ /** Returns URL of the reply. */
+ QUrl url() const { return m_pThread->url(); }
+
+ /** Returns the last cached error of the reply. */
+ UINetworkReply::NetworkError error() const { return m_error; }
+ /** Returns the user-oriented string corresponding to the last cached error of the reply. */
+ QString errorString() const;
+
+ /** Returns binary content of the reply. */
+ QByteArray readAll() const { return m_pThread->readAll(); }
+ /** Returns value for the cached reply header of the passed @a type. */
+ QString header(UINetworkReply::KnownHeader type) const { return m_pThread->header(type); }
+
+private slots:
+
+ /** Handles signal about reply has finished processing. */
+ void sltFinished();
+
+private:
+
+ /** Holds full error template in "Context description: Error description" form. */
+ QString m_strErrorTemplate;
+
+ /** Holds the last cached error of the reply. */
+ UINetworkReply::NetworkError m_error;
+
+ /** Holds the reply thread instance. */
+ UINetworkReplyPrivateThread *m_pThread;
+};
+#endif /* !VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
+
+
+/*********************************************************************************************************************************
+* Class UINetworkReplyPrivateThread implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+const RTCRCERTWANTED UINetworkReplyPrivateThread::s_aCerts[] =
+{
+ /*[0] =*/
+ {
+ /*.pszSubject =*/
+ "C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, "
+ "CN=VeriSign Class 3 Public Primary Certification Authority - G5",
+ /*.cbEncoded =*/ 0x4d7,
+ /*.Sha1Fingerprint =*/ true,
+ /*.Sha512Fingerprint =*/ true,
+ /*.abSha1 =*/
+ {
+ 0x4e, 0xb6, 0xd5, 0x78, 0x49, 0x9b, 0x1c, 0xcf, 0x5f, 0x58,
+ 0x1e, 0xad, 0x56, 0xbe, 0x3d, 0x9b, 0x67, 0x44, 0xa5, 0xe5
+ },
+ /*.abSha512 =*/
+ {
+ 0xd4, 0xf8, 0x10, 0x54, 0x72, 0x77, 0x0a, 0x2d,
+ 0xe3, 0x17, 0xb3, 0xcf, 0xed, 0x61, 0xae, 0x5c,
+ 0x5d, 0x3e, 0xde, 0xa1, 0x41, 0x35, 0xb2, 0xdf,
+ 0x60, 0xe2, 0x61, 0xfe, 0x3a, 0xc1, 0x66, 0xa3,
+ 0x3c, 0x88, 0x54, 0x04, 0x4f, 0x1d, 0x13, 0x46,
+ 0xe3, 0x8c, 0x06, 0x92, 0x9d, 0x70, 0x54, 0xc3,
+ 0x44, 0xeb, 0x2c, 0x74, 0x25, 0x9e, 0x5d, 0xfb,
+ 0xd2, 0x6b, 0xa8, 0x9a, 0xf0, 0xb3, 0x6a, 0x01
+ },
+ /*.pvUser =*/ NULL,
+ },
+};
+
+/* static */
+const QString UINetworkReplyPrivateThread::s_strCertificateFileName = QString("vbox-ssl-cacertificate.crt");
+
+UINetworkReplyPrivateThread::UINetworkReplyPrivateThread(UINetworkRequestType type,
+ const QUrl &url,
+ const QString &strTarget,
+ const UserDictionary &requestHeaders)
+ : m_type(type)
+ , m_url(url)
+ , m_strTarget(strTarget)
+ , m_requestHeaders(requestHeaders)
+ , m_hHttp(NIL_RTHTTP)
+ , m_iError(VINF_SUCCESS)
+{
+}
+
+void UINetworkReplyPrivateThread::abort()
+{
+ /* Call for abort: */
+ if (m_hHttp != NIL_RTHTTP)
+ RTHttpAbort(m_hHttp);
+}
+
+QString UINetworkReplyPrivateThread::header(UINetworkReply::KnownHeader type) const
+{
+ /* Look for known header type: */
+ switch (type)
+ {
+ case UINetworkReply::ContentTypeHeader: return m_headers.value("Content-Type");
+ case UINetworkReply::ContentLengthHeader: return m_headers.value("Content-Length");
+ case UINetworkReply::LastModifiedHeader: return m_headers.value("Last-Modified");
+ case UINetworkReply::LocationHeader: return m_headers.value("Location");
+ default: break;
+ }
+ /* Return null-string by default: */
+ return QString();
+}
+
+int UINetworkReplyPrivateThread::applyConfiguration()
+{
+ /* Install downloading progress callback: */
+ return RTHttpSetDownloadProgressCallback(m_hHttp, &UINetworkReplyPrivateThread::handleProgressChange, this);
+}
+
+int UINetworkReplyPrivateThread::applyProxyRules()
+{
+ /* Set thread context: */
+ m_strContext = tr("During proxy configuration");
+
+#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
+ /* If the specific proxy settings are enabled, we'll use them
+ * unless user disabled that functionality manually. */
+ const CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ const KProxyMode enmProxyMode = comProperties.GetProxyMode();
+ AssertReturn(comProperties.isOk(), VERR_INTERNAL_ERROR_3);
+ switch (enmProxyMode)
+ {
+ case KProxyMode_Manual:
+ return RTHttpSetProxyByUrl(m_hHttp, comProperties.GetProxyURL().toUtf8().constData());
+ case KProxyMode_NoProxy:
+ return VINF_SUCCESS;
+ default:
+ break;
+ }
+#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
+
+ /* By default, use system proxy: */
+ return RTHttpUseSystemProxySettings(m_hHttp);
+}
+
+int UINetworkReplyPrivateThread::applyHttpsCertificates()
+{
+ /* Check if we really need SSL: */
+ if (!url().toString().startsWith("https:", Qt::CaseInsensitive))
+ return VINF_SUCCESS;
+
+ /* Set thread context: */
+ m_strContext = tr("During certificate downloading");
+
+ /*
+ * Calc the filename of the CA certificate file.
+ */
+ const QString strFullCertificateFileName(fullCertificateFileName());
+ QByteArray utf8FullCertificateFileName = strFullCertificateFileName.toUtf8();
+ const char *pszCaCertFile = utf8FullCertificateFileName.constData();
+
+ /*
+ * Check the state of our CA certificate file, it's one of the following:
+ * - Missing, recreate from scratch (= refresh).
+ * - Everything is there and it is less than 28 days old, do nothing.
+ * - Everything is there but it's older than 28 days, refresh.
+ * - Missing certificates and is older than 1 min, refresh.
+ *
+ * Start by creating a store for loading the current state into, as we'll
+ * be need that for the refresh.
+ */
+ RTCRSTORE hCurStore = NIL_RTCRSTORE;
+ int rc = RTCrStoreCreateInMem(&hCurStore, 256);
+ if (RT_SUCCESS(rc))
+ {
+ bool fRefresh = true;
+ bool afCertsFound[RT_ELEMENTS(s_aCerts)];
+ RT_ZERO(afCertsFound);
+
+ /*
+ * Load the file if it exists.
+ *
+ * To effect regular updates, we need the modification date of the file,
+ * so we use RTPathQueryInfoEx here and not RTFileExists.
+ */
+ RTFSOBJINFO Info;
+ rc = RTPathQueryInfoEx(pszCaCertFile, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ if ( RT_SUCCESS(rc)
+ && RTFS_IS_FILE(Info.Attr.fMode))
+ {
+ RTERRINFOSTATIC StaticErrInfo;
+ rc = RTCrStoreCertAddFromFile(hCurStore, RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR, pszCaCertFile,
+ RTErrInfoInitStatic(&StaticErrInfo));
+ if (RTErrInfoIsSet(&StaticErrInfo.Core))
+ LogRel(("checkCertificates: %s\n", StaticErrInfo.Core.pszMsg));
+ else
+ AssertRC(rc);
+
+ /*
+ * Scan the store the for certificates we need, then see what we
+ * need to do wrt file age.
+ */
+ rc = RTCrStoreCertCheckWanted(hCurStore, s_aCerts, RT_ELEMENTS(s_aCerts), afCertsFound);
+ AssertRC(rc);
+ RTTIMESPEC RefreshAge;
+ uint32_t cSecRefresh = rc == VINF_SUCCESS ? 28 * RT_SEC_1DAY /* all found */ : 60 /* stuff missing */;
+ fRefresh = RTTimeSpecCompare(&Info.ModificationTime, RTTimeSpecSubSeconds(RTTimeNow(&RefreshAge), cSecRefresh)) <= 0;
+ }
+
+ /*
+ * Refresh the file if necessary.
+ */
+ if (fRefresh)
+ refreshCertificates(&hCurStore, afCertsFound, pszCaCertFile);
+
+ RTCrStoreRelease(hCurStore);
+
+ /*
+ * Final verdict.
+ */
+ if (areAllCertsFound(afCertsFound))
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_NOT_FOUND; /** @todo r=bird: Why not try and let RTHttpGet* bitch if the necessary certs are missing? */
+
+ /*
+ * Set our custom CA file.
+ */
+ if (RT_SUCCESS(rc))
+ rc = RTHttpSetCAFile(m_hHttp, pszCaCertFile);
+ }
+ return rc;
+}
+
+int UINetworkReplyPrivateThread::applyRawHeaders()
+{
+ /* Set thread context: */
+ m_strContext = tr("During network request");
+
+ /* Make sure we have a raw headers at all: */
+ if (m_requestHeaders.isEmpty())
+ return VINF_SUCCESS;
+
+ /* Apply raw headers: */
+ return applyRawHeaders(m_hHttp, m_requestHeaders);
+}
+
+int UINetworkReplyPrivateThread::performMainRequest()
+{
+ /* Set thread context: */
+ m_strContext = tr("During network request");
+
+ /* Paranoia: */
+ m_reply.clear();
+
+ /* Prepare result: */
+ int rc = 0;
+
+ /* Depending on request type: */
+ switch (m_type)
+ {
+ case UINetworkRequestType_HEAD:
+ {
+ /* Perform blocking HTTP HEAD request: */
+ void *pvResponse = 0;
+ size_t cbResponse = 0;
+ rc = RTHttpGetHeaderBinary(m_hHttp, m_url.toString().toUtf8().constData(), &pvResponse, &cbResponse);
+ if (RT_SUCCESS(rc))
+ {
+ m_reply = QByteArray((char*)pvResponse, (int)cbResponse);
+ RTHttpFreeResponse(pvResponse);
+ }
+
+ /* Paranoia: */
+ m_headers.clear();
+
+ /* Parse header contents: */
+ const QString strHeaders = QString(m_reply);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ const QStringList headers = strHeaders.split("\n", Qt::SkipEmptyParts);
+#else
+ const QStringList headers = strHeaders.split("\n", QString::SkipEmptyParts);
+#endif
+ foreach (const QString &strHeader, headers)
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ const QStringList values = strHeader.split(": ", Qt::SkipEmptyParts);
+#else
+ const QStringList values = strHeader.split(": ", QString::SkipEmptyParts);
+#endif
+ if (values.size() > 1)
+ m_headers[values.at(0)] = values.at(1);
+ }
+
+ /* Special handling of redirection header: */
+ if (rc == VERR_HTTP_REDIRECTED)
+ {
+ char *pszBuf = 0;
+ const int rrc = RTHttpGetRedirLocation(m_hHttp, &pszBuf);
+ if (RT_SUCCESS(rrc))
+ m_headers["Location"] = QString(pszBuf);
+ if (pszBuf)
+ RTMemFree(pszBuf);
+ }
+
+ break;
+ }
+ case UINetworkRequestType_GET:
+ {
+ /* Perform blocking HTTP GET request.
+ * Keep in mind that if the target parameter is provided,
+ * we are trying to download contents to file directly,
+ * otherwise it will be downloaded to memory and it's
+ * customer responsibility to save it afterwards. */
+ if (m_strTarget.isEmpty())
+ {
+ void *pvResponse = 0;
+ size_t cbResponse = 0;
+ rc = RTHttpGetBinary(m_hHttp, m_url.toString().toUtf8().constData(), &pvResponse, &cbResponse);
+ if (RT_SUCCESS(rc))
+ {
+ m_reply = QByteArray((char*)pvResponse, (int)cbResponse);
+ RTHttpFreeResponse(pvResponse);
+ }
+ }
+ else
+ {
+ rc = RTHttpGetFile(m_hHttp, m_url.toString().toUtf8().constData(), m_strTarget.toUtf8().constData());
+ if (RT_SUCCESS(rc))
+ {
+ QFile file(m_strTarget);
+ if (file.open(QIODevice::ReadOnly))
+ m_reply = file.readAll();
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Return result: */
+ return rc;
+}
+
+void UINetworkReplyPrivateThread::run()
+{
+ /* Create HTTP client: */
+ m_iError = RTHttpCreate(&m_hHttp);
+ if (RT_SUCCESS(m_iError))
+ {
+ /* Apply configuration: */
+ if (RT_SUCCESS(m_iError))
+ m_iError = applyConfiguration();
+
+ /* Apply proxy-rules: */
+ if (RT_SUCCESS(m_iError))
+ m_iError = applyProxyRules();
+
+ /* Apply https-certificates: */
+ if (RT_SUCCESS(m_iError))
+ m_iError = applyHttpsCertificates();
+
+ /* Assign raw-headers: */
+ if (RT_SUCCESS(m_iError))
+ m_iError = applyRawHeaders();
+
+ /* Perform main request: */
+ if (RT_SUCCESS(m_iError))
+ m_iError = performMainRequest();
+
+ /* Destroy HTTP client: */
+ RTHTTP hHttp = m_hHttp;
+ if (hHttp != NIL_RTHTTP)
+ {
+ /** @todo r=bird: There is a race here between this and abort()! */
+ m_hHttp = NIL_RTHTTP;
+ RTHttpDestroy(hHttp);
+ }
+ }
+}
+
+void UINetworkReplyPrivateThread::handleProgressChange(uint64_t cbDownloadTotal, uint64_t cbDownloaded)
+{
+#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
+ /* Notify listeners about progress change: */
+ emit sigDownloadProgress(cbDownloaded, cbDownloadTotal);
+#else /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
+ Q_UNUSED(cbDownloaded);
+ Q_UNUSED(cbDownloadTotal);
+#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
+}
+
+/* static */
+QString UINetworkReplyPrivateThread::fullCertificateFileName()
+{
+#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
+ const QDir homeDir(QDir::toNativeSeparators(uiCommon().homeFolder()));
+ return QDir::toNativeSeparators(homeDir.absoluteFilePath(s_strCertificateFileName));
+#else /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
+ return QString("/not/such/agency/non-existing-file.cer");
+#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
+}
+
+/* static */
+int UINetworkReplyPrivateThread::applyRawHeaders(RTHTTP hHttp, const UserDictionary &headers)
+{
+ /* Make sure HTTP is created: */
+ if (hHttp == NIL_RTHTTP)
+ return VERR_INVALID_HANDLE;
+
+ /* We should format them first: */
+ QVector<QByteArray> formattedHeaders;
+ QVector<const char*> formattedHeaderPointers;
+ foreach (const QString &header, headers.keys())
+ {
+ /* Prepare formatted representation: */
+ QString strFormattedString = QString("%1: %2").arg(header, headers.value(header));
+ formattedHeaders << strFormattedString.toUtf8();
+ formattedHeaderPointers << formattedHeaders.last().constData();
+ }
+ const char **ppFormattedHeaders = formattedHeaderPointers.data();
+
+ /* Apply HTTP headers: */
+ return RTHttpSetHeaders(hHttp, formattedHeaderPointers.size(), ppFormattedHeaders);
+}
+
+/* static */
+unsigned UINetworkReplyPrivateThread::countCertsFound(bool const *pafFoundCerts)
+{
+ unsigned cFound = 0;
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aCerts); i++)
+ cFound += pafFoundCerts[i];
+ return cFound;
+}
+
+/* static */
+bool UINetworkReplyPrivateThread::areAllCertsFound(bool const *pafFoundCerts)
+{
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aCerts); i++)
+ if (!pafFoundCerts[i])
+ return false;
+ return true;
+}
+
+/* static */
+int UINetworkReplyPrivateThread::refreshCertificates(PRTCRSTORE phStore, bool *pafFoundCerts, const char *pszCaCertFile)
+{
+ /*
+ * Collect the standard assortment of SSL certificates.
+ */
+ uint32_t cHint = RTCrStoreCertCount(*phStore);
+ RTCRSTORE hNewStore;
+ int rc = RTCrStoreCreateInMem(&hNewStore, cHint > 32 && cHint < _32K ? cHint + 16 : 256);
+ if (RT_SUCCESS(rc))
+ {
+ RTERRINFOSTATIC StaticErrInfo;
+ rc = RTHttpGatherCaCertsInStore(hNewStore, 0 /*fFlags*/, RTErrInfoInitStatic(&StaticErrInfo));
+ if (RTErrInfoIsSet(&StaticErrInfo.Core))
+ LogRel(("refreshCertificates/#1: %s\n", StaticErrInfo.Core.pszMsg));
+ else if (rc == VERR_NOT_FOUND)
+ LogRel(("refreshCertificates/#1: No trusted SSL certs found on the system, will try download...\n"));
+ else
+ AssertLogRelRC(rc);
+ if (RT_SUCCESS(rc) || rc == VERR_NOT_FOUND)
+ {
+ /*
+ * Check and see what we've got. If we haven't got all we desire,
+ * try add it from the previous store.
+ */
+ bool afNewFoundCerts[RT_ELEMENTS(s_aCerts)];
+ RT_ZERO(afNewFoundCerts); /* paranoia */
+
+ rc = RTCrStoreCertCheckWanted(hNewStore, s_aCerts, RT_ELEMENTS(s_aCerts), afNewFoundCerts);
+ AssertLogRelRC(rc);
+ Assert(rc != VINF_SUCCESS || areAllCertsFound(afNewFoundCerts));
+ if (rc != VINF_SUCCESS)
+ {
+ rc = RTCrStoreCertAddWantedFromStore(hNewStore,
+ RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
+ *phStore, s_aCerts, RT_ELEMENTS(s_aCerts), afNewFoundCerts);
+ AssertLogRelRC(rc);
+ Assert(rc != VINF_SUCCESS || areAllCertsFound(afNewFoundCerts));
+ }
+
+ /*
+ * If that didn't help, seek out certificates in more obscure places,
+ * like java, mozilla and mutt.
+ */
+ if (rc != VINF_SUCCESS)
+ {
+ rc = RTCrStoreCertAddWantedFromFishingExpedition(hNewStore,
+ RTCRCERTCTX_F_ADD_IF_NOT_FOUND
+ | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
+ s_aCerts, RT_ELEMENTS(s_aCerts), afNewFoundCerts,
+ RTErrInfoInitStatic(&StaticErrInfo));
+ if (RTErrInfoIsSet(&StaticErrInfo.Core))
+ LogRel(("refreshCertificates/#2: %s\n", StaticErrInfo.Core.pszMsg));
+ Assert(rc != VINF_SUCCESS || areAllCertsFound(afNewFoundCerts));
+ }
+
+ /*
+ * If we've got the same or better hit rate than the old store,
+ * replace the CA certs file.
+ */
+ if ( areAllCertsFound(afNewFoundCerts)
+ || countCertsFound(afNewFoundCerts) >= countCertsFound(pafFoundCerts) )
+ {
+ rc = RTCrStoreCertExportAsPem(hNewStore, 0 /*fFlags*/, pszCaCertFile);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel(("refreshCertificates/#3: Found %u/%u SSL certs we/you trust (previously %u/%u).\n",
+ countCertsFound(afNewFoundCerts), RTCrStoreCertCount(hNewStore),
+ countCertsFound(pafFoundCerts), RTCrStoreCertCount(*phStore) ));
+
+ memcpy(pafFoundCerts, afNewFoundCerts, sizeof(afNewFoundCerts));
+ RTCrStoreRelease(*phStore);
+ *phStore = hNewStore;
+ hNewStore = NIL_RTCRSTORE;
+ }
+ else
+ {
+ RT_ZERO(pafFoundCerts);
+ LogRel(("refreshCertificates/#3: RTCrStoreCertExportAsPem unexpectedly failed with %Rrc\n", rc));
+ }
+ }
+ else
+ LogRel(("refreshCertificates/#3: Sticking with the old file, missing essential certs.\n"));
+ }
+ RTCrStoreRelease(hNewStore);
+ }
+ return rc;
+}
+
+/* static */
+DECLCALLBACK(void) UINetworkReplyPrivateThread::handleProgressChange(RTHTTP hHttp, void *pvUser, uint64_t cbDownloadTotal, uint64_t cbDownloaded)
+{
+ /* Redirect callback to particular object: */
+ Q_UNUSED(hHttp);
+ AssertPtrReturnVoid(pvUser);
+ static_cast<UINetworkReplyPrivateThread*>(pvUser)->handleProgressChange(cbDownloadTotal, cbDownloaded);
+}
+
+
+#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
+
+
+/*********************************************************************************************************************************
+* Class UINetworkReplyPrivate implementation. *
+*********************************************************************************************************************************/
+
+UINetworkReplyPrivate::UINetworkReplyPrivate(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders)
+ : m_error(UINetworkReply::NoError)
+ , m_pThread(0)
+{
+ /* Prepare full error template: */
+ m_strErrorTemplate = tr("%1: %2", "Context description: Error description");
+
+ /* Create and run reply thread: */
+ m_pThread = new UINetworkReplyPrivateThread(type, url, strTarget, requestHeaders);
+ connect(m_pThread, &UINetworkReplyPrivateThread::sigDownloadProgress,
+ this, &UINetworkReplyPrivate::downloadProgress, Qt::QueuedConnection);
+ connect(m_pThread, &UINetworkReplyPrivateThread::finished,
+ this, &UINetworkReplyPrivate::sltFinished);
+ m_pThread->start();
+}
+
+UINetworkReplyPrivate::~UINetworkReplyPrivate()
+{
+ /* Terminate network-reply thread: */
+ m_pThread->abort();
+ m_pThread->wait();
+ delete m_pThread;
+ m_pThread = 0;
+}
+
+QString UINetworkReplyPrivate::errorString() const
+{
+ /* Look for known error codes: */
+ switch (m_error)
+ {
+ case UINetworkReply::NoError: break;
+ case UINetworkReply::RemoteHostClosedError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Unable to initialize HTTP library"));
+ case UINetworkReply::UrlNotFoundError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Url not found on the server"));
+ case UINetworkReply::HostNotFoundError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Host not found"));
+ case UINetworkReply::ContentAccessDenied: return m_strErrorTemplate.arg(m_pThread->context(), tr("Content access denied"));
+ case UINetworkReply::ProtocolFailure: return m_strErrorTemplate.arg(m_pThread->context(), tr("Protocol failure"));
+ case UINetworkReply::ConnectionRefusedError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Connection refused"));
+ case UINetworkReply::SslHandshakeFailedError: return m_strErrorTemplate.arg(m_pThread->context(), tr("SSL authentication failed"));
+ case UINetworkReply::AuthenticationRequiredError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Wrong SSL certificate format"));
+ case UINetworkReply::ContentReSendError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Content moved"));
+ case UINetworkReply::ProxyNotFoundError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Proxy not found"));
+ default: return m_strErrorTemplate.arg(m_pThread->context(), tr("Unknown reason"));
+ }
+ /* Return null-string by default: */
+ return QString();
+}
+
+void UINetworkReplyPrivate::sltFinished()
+{
+ /* Look for known error codes: */
+ switch (m_pThread->error())
+ {
+ case VINF_SUCCESS: m_error = UINetworkReply::NoError; break;
+ case VERR_HTTP_INIT_FAILED: m_error = UINetworkReply::RemoteHostClosedError; break;
+ case VERR_HTTP_NOT_FOUND: m_error = UINetworkReply::UrlNotFoundError; break;
+ case VERR_HTTP_HOST_NOT_FOUND: m_error = UINetworkReply::HostNotFoundError; break;
+ case VERR_HTTP_ACCESS_DENIED: m_error = UINetworkReply::ContentAccessDenied; break;
+ case VERR_HTTP_BAD_REQUEST: m_error = UINetworkReply::ProtocolFailure; break;
+ case VERR_HTTP_COULDNT_CONNECT: m_error = UINetworkReply::ConnectionRefusedError; break;
+ case VERR_HTTP_SSL_CONNECT_ERROR: m_error = UINetworkReply::SslHandshakeFailedError; break;
+ case VERR_HTTP_CACERT_WRONG_FORMAT: m_error = UINetworkReply::AuthenticationRequiredError; break;
+ case VERR_HTTP_CACERT_CANNOT_AUTHENTICATE: m_error = UINetworkReply::AuthenticationRequiredError; break;
+ case VERR_HTTP_ABORTED: m_error = UINetworkReply::OperationCanceledError; break;
+ case VERR_HTTP_REDIRECTED: m_error = UINetworkReply::ContentReSendError; break;
+ case VERR_HTTP_PROXY_NOT_FOUND: m_error = UINetworkReply::ProxyNotFoundError; break;
+ default: m_error = UINetworkReply::UnknownNetworkError; break;
+ }
+ /* Redirect signal to external listeners: */
+ emit finished();
+}
+
+
+/*********************************************************************************************************************************
+* Class UINetworkReply implementation. *
+*********************************************************************************************************************************/
+
+UINetworkReply::UINetworkReply(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders)
+ : m_pReply(new UINetworkReplyPrivate(type, url, strTarget, requestHeaders))
+{
+ /* Prepare network-reply object connections: */
+ connect(m_pReply, &UINetworkReplyPrivate::downloadProgress, this, &UINetworkReply::downloadProgress);
+ connect(m_pReply, &UINetworkReplyPrivate::finished, this, &UINetworkReply::finished);
+}
+
+UINetworkReply::~UINetworkReply()
+{
+ /* Cleanup network-reply object: */
+ if (m_pReply)
+ {
+ delete m_pReply;
+ m_pReply = 0;
+ }
+}
+
+void UINetworkReply::abort()
+{
+ return m_pReply->abort();
+}
+
+QUrl UINetworkReply::url() const
+{
+ return m_pReply->url();
+}
+
+UINetworkReply::NetworkError UINetworkReply::error() const
+{
+ return m_pReply->error();
+}
+
+QString UINetworkReply::errorString() const
+{
+ return m_pReply->errorString();
+}
+
+QByteArray UINetworkReply::readAll() const
+{
+ return m_pReply->readAll();
+}
+
+QVariant UINetworkReply::header(UINetworkReply::KnownHeader header) const
+{
+ return m_pReply->header(header);
+}
+
+#include "UINetworkReply.moc"
+
+#endif /* !VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINetworkReply.h b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkReply.h
new file mode 100644
index 00000000..168b5c6f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkReply.h
@@ -0,0 +1,122 @@
+/* $Id: UINetworkReply.h $ */
+/** @file
+ * VBox Qt GUI - UINetworkReply stuff declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UINetworkReply_h
+#define FEQT_INCLUDED_SRC_networking_UINetworkReply_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPointer>
+#include <QUrl>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UINetworkDefs.h"
+
+/* Forward declarations: */
+class UINetworkReplyPrivate;
+
+/** QObject extension
+ * used as network-reply interface. */
+class SHARED_LIBRARY_STUFF UINetworkReply : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about reply progress change.
+ * @param iBytesReceived Holds the current amount of bytes received.
+ * @param iBytesTotal Holds the total amount of bytes to be received. */
+ void downloadProgress(qint64 iBytesReceived, qint64 iBytesTotal);
+
+ /** Notifies listeners about reply has finished processing. */
+ void finished();
+
+public:
+
+ /** Known error codes.
+ * Came from QtNetwork module.
+ * More to go on demand when necessary. */
+ enum NetworkError
+ {
+ NoError,
+ ConnectionRefusedError,
+ RemoteHostClosedError,
+ UrlNotFoundError,
+ HostNotFoundError,
+ OperationCanceledError,
+ SslHandshakeFailedError,
+ ProxyNotFoundError,
+ ContentAccessDenied,
+ AuthenticationRequiredError,
+ ContentReSendError,
+ UnknownNetworkError,
+ ProtocolFailure,
+ };
+
+ /** Known header types.
+ * Came from QtNetwork module.
+ * More to go on demand when necessary. */
+ enum KnownHeader
+ {
+ ContentTypeHeader,
+ ContentLengthHeader,
+ LastModifiedHeader,
+ LocationHeader,
+ };
+
+ /** Constructs network-reply of the passed @a type for the passed @a url, @a strTarget and @a requestHeaders. */
+ UINetworkReply(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders);
+ /** Destructs reply. */
+ ~UINetworkReply();
+
+ /** Aborts reply. */
+ void abort();
+
+ /** Returns the URL of the reply. */
+ QUrl url() const;
+
+ /** Returns the last cached error of the reply. */
+ NetworkError error() const;
+ /** Returns the user-oriented string corresponding to the last cached error of the reply. */
+ QString errorString() const;
+
+ /** Returns binary content of the reply. */
+ QByteArray readAll() const;
+ /** Returns value for the cached reply header of the passed @a type. */
+ QVariant header(UINetworkReply::KnownHeader header) const;
+
+private:
+
+ /** Holds the reply private data instance. */
+ UINetworkReplyPrivate *m_pReply;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UINetworkReply_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequest.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequest.cpp
new file mode 100644
index 00000000..ef67c386
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequest.cpp
@@ -0,0 +1,198 @@
+/* $Id: UINetworkRequest.cpp $ */
+/** @file
+ * VBox Qt GUI - UINetworkRequest class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVariant>
+
+/* GUI includes: */
+#include "UINetworkRequest.h"
+#include "UINetworkRequestManager.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+UINetworkRequest::UINetworkRequest(UINetworkRequestType enmType,
+ const QList<QUrl> &urls,
+ const QString &strTarget,
+ const UserDictionary &requestHeaders)
+ : m_enmType(enmType)
+ , m_urls(urls)
+ , m_strTarget(strTarget)
+ , m_requestHeaders(requestHeaders)
+ , m_iUrlIndex(-1)
+ , m_fRunning(false)
+{
+ prepare();
+}
+
+UINetworkRequest::~UINetworkRequest()
+{
+ cleanup();
+}
+
+void UINetworkRequest::sltHandleNetworkReplyProgress(qint64 iReceived, qint64 iTotal)
+{
+ /* Notify network-request listeners: */
+ emit sigProgress(iReceived, iTotal);
+}
+
+void UINetworkRequest::sltHandleNetworkReplyFinish()
+{
+ /* Mark network-reply as non-running: */
+ m_fRunning = false;
+
+ /* Make sure network-reply still valid: */
+ if (!m_pReply)
+ return;
+
+ /* If network-reply has no errors: */
+ if (m_pReply->error() == UINetworkReply::NoError)
+ {
+ /* Notify network-request listeners: */
+ emit sigFinished();
+ }
+ /* If network-request was canceled: */
+ else if (m_pReply->error() == UINetworkReply::OperationCanceledError)
+ {
+ /* Notify network-request listeners: */
+ emit sigCanceled();
+ }
+ /* If some other error occured: */
+ else
+ {
+ /* Check if we are able to handle error: */
+ bool fErrorHandled = false;
+
+ /* Handle redirection: */
+ switch (m_pReply->error())
+ {
+ case UINetworkReply::ContentReSendError:
+ {
+ /* Check whether redirection link was acquired: */
+ const QString strRedirect = m_pReply->header(UINetworkReply::LocationHeader).toString();
+ if (!strRedirect.isEmpty())
+ {
+ /* Cleanup current network-reply first: */
+ cleanupNetworkReply();
+
+ /* Choose redirect-source as current url: */
+ m_url = strRedirect;
+
+ /* Create new network-reply finally: */
+ prepareNetworkReply();
+
+ /* Mark this error handled: */
+ fErrorHandled = true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* If error still unhandled: */
+ if (!fErrorHandled)
+ {
+ /* Check if we have other urls in queue: */
+ if (m_iUrlIndex < m_urls.size() - 1)
+ {
+ /* Cleanup current network-reply first: */
+ cleanupNetworkReply();
+
+ /* Choose next url as current: */
+ ++m_iUrlIndex;
+ m_url = m_urls.at(m_iUrlIndex);
+
+ /* Create new network-reply finally: */
+ prepareNetworkReply();
+ }
+ else
+ {
+ /* Notify network-request listeners: */
+ emit sigFailed(m_pReply->errorString());
+ }
+ }
+ }
+}
+
+void UINetworkRequest::sltCancel()
+{
+ /* Abort network-reply if present: */
+ if (m_pReply)
+ {
+ if (m_fRunning)
+ m_pReply->abort();
+ else
+ emit sigCanceled();
+ }
+}
+
+void UINetworkRequest::prepare()
+{
+ /* Choose first url as current: */
+ m_iUrlIndex = 0;
+ m_url = m_urls.at(m_iUrlIndex);
+
+ /* Prepare network-reply: */
+ prepareNetworkReply();
+}
+
+void UINetworkRequest::prepareNetworkReply()
+{
+ /* Create network-reply: */
+ m_pReply = new UINetworkReply(m_enmType, m_url, m_strTarget, m_requestHeaders);
+ AssertPtrReturnVoid(m_pReply.data());
+ {
+ /* Prepare network-reply: */
+ connect(m_pReply.data(), &UINetworkReply::downloadProgress,
+ this, &UINetworkRequest::sltHandleNetworkReplyProgress);
+ connect(m_pReply.data(), &UINetworkReply::finished,
+ this, &UINetworkRequest::sltHandleNetworkReplyFinish);
+
+ /* Mark network-reply as running: */
+ m_fRunning = true;
+
+ /* Notify network-request listeners: */
+ emit sigStarted();
+ }
+}
+
+void UINetworkRequest::cleanupNetworkReply()
+{
+ /* Destroy network-reply: */
+ AssertPtrReturnVoid(m_pReply.data());
+ m_pReply->disconnect();
+ m_pReply->deleteLater();
+ m_pReply = 0;
+}
+
+void UINetworkRequest::cleanup()
+{
+ /* Cleanup network-reply: */
+ cleanupNetworkReply();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequest.h b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequest.h
new file mode 100644
index 00000000..962da070
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequest.h
@@ -0,0 +1,127 @@
+/* $Id: UINetworkRequest.h $ */
+/** @file
+ * VBox Qt GUI - UINetworkRequest class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UINetworkRequest_h
+#define FEQT_INCLUDED_SRC_networking_UINetworkRequest_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QPointer>
+
+/* GUI inludes: */
+#include "UILibraryDefs.h"
+#include "UINetworkDefs.h"
+#include "UINetworkReply.h"
+
+/** QObject extension used as network-request container. */
+class SHARED_LIBRARY_STUFF UINetworkRequest : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listener about progress started. */
+ void sigStarted();
+ /** Notifies listener about progress changed.
+ * @param iReceived Brings the amount of bytes received.
+ * @param iTotal Brings the amount of total bytes to receive. */
+ void sigProgress(qint64 iReceived, qint64 iTotal);
+ /** Notifies listener about progress failed.
+ * @param strError Brings the error progress failed with. */
+ void sigFailed(const QString &strError);
+ /** Notifies listener about progress canceled. */
+ void sigCanceled();
+ /** Notifies listener about progress finished. */
+ void sigFinished();
+
+public:
+
+ /** Constructs network-request.
+ * @param enmType Brings request type.
+ * @param urls Brings request urls, there can be few of them.
+ * @param strTarget Brings request target path.
+ * @param requestHeaders Brings request headers in dictionary form. */
+ UINetworkRequest(UINetworkRequestType enmType,
+ const QList<QUrl> &urls,
+ const QString &strTarget,
+ const UserDictionary &requestHeaders);
+ /** Destructs network-request. */
+ virtual ~UINetworkRequest() /* override final */;
+
+ /** Returns the request reply. */
+ UINetworkReply *reply() { return m_pReply; }
+
+public slots:
+
+ /** Initiates request cancelling. */
+ void sltCancel();
+
+private slots:
+
+ /** Handles reply about progress changed.
+ * @param iReceived Brings the amount of bytes received.
+ * @param iTotal Brings the amount of total bytes to receive. */
+ void sltHandleNetworkReplyProgress(qint64 iReceived, qint64 iTotal);
+ /** Handles reply about progress finished. */
+ void sltHandleNetworkReplyFinish();
+
+private:
+
+ /** Prepares request. */
+ void prepare();
+ /** Prepares request's reply. */
+ void prepareNetworkReply();
+
+ /** Cleanups request's reply. */
+ void cleanupNetworkReply();
+ /** Cleanups request. */
+ void cleanup();
+
+ /** Holds the request type. */
+ const UINetworkRequestType m_enmType;
+ /** Holds the request urls. */
+ const QList<QUrl> m_urls;
+ /** Holds the request target. */
+ const QString m_strTarget;
+ /** Holds the request headers. */
+ const UserDictionary m_requestHeaders;
+
+ /** Holds current request url. */
+ QUrl m_url;
+ /** Holds index of current request url. */
+ int m_iUrlIndex;
+ /** Holds whether current request url is in progress. */
+ bool m_fRunning;
+
+ /** Holds the request reply. */
+ QPointer<UINetworkReply> m_pReply;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UINetworkRequest_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequestManager.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequestManager.cpp
new file mode 100644
index 00000000..3b16cb94
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequestManager.cpp
@@ -0,0 +1,218 @@
+/* $Id: UINetworkRequestManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UINetworkRequestManager stuff implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QUrl>
+
+/* GUI includes: */
+#include "UINetworkCustomer.h"
+#include "UINetworkRequest.h"
+#include "UINetworkRequestManager.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/* static */
+UINetworkRequestManager *UINetworkRequestManager::s_pInstance = 0;
+
+/* static */
+void UINetworkRequestManager::create()
+{
+ AssertReturnVoid(!s_pInstance);
+ new UINetworkRequestManager;
+}
+
+/* static */
+void UINetworkRequestManager::destroy()
+{
+ AssertPtrReturnVoid(s_pInstance);
+ delete s_pInstance;
+}
+
+/* static */
+UINetworkRequestManager *UINetworkRequestManager::instance()
+{
+ return s_pInstance;
+}
+
+QUuid UINetworkRequestManager::createNetworkRequest(UINetworkRequestType enmType,
+ const QList<QUrl> &urls,
+ const QString &strTarget,
+ const UserDictionary &requestHeaders,
+ UINetworkCustomer *pCustomer)
+{
+ /* Create network-request: */
+ UINetworkRequest *pNetworkRequest = new UINetworkRequest(enmType, urls, strTarget, requestHeaders);
+ if (pNetworkRequest)
+ {
+ /* Configure request listeners: */
+ connect(pNetworkRequest, &UINetworkRequest::sigProgress,
+ this, &UINetworkRequestManager::sltHandleNetworkRequestProgress);
+ connect(pNetworkRequest, &UINetworkRequest::sigCanceled,
+ this, &UINetworkRequestManager::sltHandleNetworkRequestCancel);
+ connect(pNetworkRequest, &UINetworkRequest::sigFinished,
+ this, &UINetworkRequestManager::sltHandleNetworkRequestFinish);
+ connect(pNetworkRequest, &UINetworkRequest::sigFailed,
+ this, &UINetworkRequestManager::sltHandleNetworkRequestFailure);
+
+ /* [Re]generate ID until unique: */
+ QUuid uId = QUuid::createUuid();
+ while (m_requests.contains(uId))
+ uId = QUuid::createUuid();
+
+ /* Add request&customer to map: */
+ m_requests.insert(uId, pNetworkRequest);
+ m_customers.insert(uId, pCustomer);
+ connect(pCustomer, &UINetworkCustomer::sigBeingDestroyed,
+ this, &UINetworkRequestManager::sltHandleNetworkCustomerBeingDestroyed,
+ Qt::UniqueConnection);
+
+ /* Return ID: */
+ return uId;
+ }
+
+ /* Null ID by default: */
+ return QUuid();
+}
+
+void UINetworkRequestManager::cancelNetworkRequest(const QUuid &uId)
+{
+ /* Cleanup request: */
+ UINetworkRequest *pNetworkRequest = m_requests.value(uId);
+ if (pNetworkRequest)
+ pNetworkRequest->sltCancel();
+}
+
+UINetworkRequestManager::UINetworkRequestManager()
+{
+ s_pInstance = this;
+ prepare();
+}
+
+UINetworkRequestManager::~UINetworkRequestManager()
+{
+ cleanup();
+ s_pInstance = 0;
+}
+
+void UINetworkRequestManager::sltHandleNetworkRequestProgress(qint64 iReceived, qint64 iTotal)
+{
+ /* Make sure we have this request registered: */
+ UINetworkRequest *pNetworkRequest = qobject_cast<UINetworkRequest*>(sender());
+ AssertPtrReturnVoid(pNetworkRequest);
+ const QUuid uId = m_requests.key(pNetworkRequest);
+ AssertReturnVoid(!uId.isNull());
+
+ /* Delegate request to customer: */
+ UINetworkCustomer *pNetworkCustomer = m_customers.value(uId);
+ if (pNetworkCustomer)
+ pNetworkCustomer->processNetworkReplyProgress(iReceived, iTotal);
+}
+
+void UINetworkRequestManager::sltHandleNetworkRequestFailure(const QString &strError)
+{
+ /* Make sure we have this request registered: */
+ UINetworkRequest *pNetworkRequest = qobject_cast<UINetworkRequest*>(sender());
+ AssertPtrReturnVoid(pNetworkRequest);
+ const QUuid uId = m_requests.key(pNetworkRequest);
+ AssertReturnVoid(!uId.isNull());
+
+ /* Delegate request to customer: */
+ UINetworkCustomer *pNetworkCustomer = m_customers.value(uId);
+ if (pNetworkCustomer)
+ pNetworkCustomer->processNetworkReplyFailed(strError);
+
+ /* Cleanup request: */
+ cleanupNetworkRequest(uId);
+}
+
+void UINetworkRequestManager::sltHandleNetworkRequestCancel()
+{
+ /* Make sure we have this request registered: */
+ UINetworkRequest *pNetworkRequest = qobject_cast<UINetworkRequest*>(sender());
+ AssertPtrReturnVoid(pNetworkRequest);
+ const QUuid uId = m_requests.key(pNetworkRequest);
+ AssertReturnVoid(!uId.isNull());
+
+ /* Delegate request to customer: */
+ UINetworkCustomer *pNetworkCustomer = m_customers.value(uId);
+ if (pNetworkCustomer)
+ pNetworkCustomer->processNetworkReplyCanceled(pNetworkRequest->reply());
+
+ /* Cleanup request: */
+ cleanupNetworkRequest(uId);
+}
+
+void UINetworkRequestManager::sltHandleNetworkRequestFinish()
+{
+ /* Make sure we have this request registered: */
+ UINetworkRequest *pNetworkRequest = qobject_cast<UINetworkRequest*>(sender());
+ AssertPtrReturnVoid(pNetworkRequest);
+ const QUuid uId = m_requests.key(pNetworkRequest);
+ AssertReturnVoid(!uId.isNull());
+
+ /* Delegate request to customer: */
+ UINetworkCustomer *pNetworkCustomer = m_customers.value(uId);
+ if (pNetworkCustomer)
+ pNetworkCustomer->processNetworkReplyFinished(pNetworkRequest->reply());
+
+ /* Cleanup request: */
+ cleanupNetworkRequest(uId);
+}
+
+void UINetworkRequestManager::sltHandleNetworkCustomerBeingDestroyed(UINetworkCustomer *pNetworkCustomer)
+{
+ /* Make sure customer was and still registered: */
+ const QList<QUuid> ids = m_customers.keys(pNetworkCustomer);
+ AssertReturnVoid(!ids.isEmpty());
+ /* Unregister it: */
+ foreach (const QUuid &uId, ids)
+ m_customers.remove(uId);
+}
+
+void UINetworkRequestManager::prepare()
+{
+ // nothing for now
+}
+
+void UINetworkRequestManager::cleanupNetworkRequest(const QUuid &uId)
+{
+ delete m_requests.value(uId);
+ m_requests.remove(uId);
+}
+
+void UINetworkRequestManager::cleanupNetworkRequests()
+{
+ foreach (const QUuid &uId, m_requests.keys())
+ cleanupNetworkRequest(uId);
+}
+
+void UINetworkRequestManager::cleanup()
+{
+ cleanupNetworkRequests();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequestManager.h b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequestManager.h
new file mode 100644
index 00000000..f31f8241
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINetworkRequestManager.h
@@ -0,0 +1,121 @@
+/* $Id: UINetworkRequestManager.h $ */
+/** @file
+ * VBox Qt GUI - UINetworkRequestManager stuff declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UINetworkRequestManager_h
+#define FEQT_INCLUDED_SRC_networking_UINetworkRequestManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QUuid>
+
+/* GUI inludes: */
+#include "UILibraryDefs.h"
+#include "UINetworkDefs.h"
+
+/* Forward declarations: */
+class QUrl;
+class UINetworkCustomer;
+class UINetworkRequest;
+
+/** QObject class extension.
+ * Providing network access for VirtualBox application purposes. */
+class SHARED_LIBRARY_STUFF UINetworkRequestManager : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Creates singleton instance. */
+ static void create();
+ /** Destroys singleton instance. */
+ static void destroy();
+ /** Returns the singleton instance. */
+ static UINetworkRequestManager *instance();
+
+ /** Creates network-request returning request ID.
+ * @param enmType Brings request type.
+ * @param urls Brings request urls, there can be few of them.
+ * @param strTarget Brings request target path.
+ * @param requestHeaders Brings request headers in dictionary form.
+ * @param pCustomer Brings customer this request being ordered by. */
+ QUuid createNetworkRequest(UINetworkRequestType enmType,
+ const QList<QUrl> &urls,
+ const QString &strTarget,
+ const UserDictionary &requestHeaders,
+ UINetworkCustomer *pCustomer);
+
+ /** Aborts network-request. */
+ void cancelNetworkRequest(const QUuid &uId);
+
+protected:
+
+ /** Constructs network manager. */
+ UINetworkRequestManager();
+ /** Destructs network manager. */
+ virtual ~UINetworkRequestManager() /* override final */;
+
+private slots:
+
+ /** Handles progress for @a iReceived amount of bytes among @a iTotal. */
+ void sltHandleNetworkRequestProgress(qint64 iReceived, qint64 iTotal);
+ /** Handles request @a strError. */
+ void sltHandleNetworkRequestFailure(const QString &strError);
+ /** Handles request canceling. */
+ void sltHandleNetworkRequestCancel();
+ /** Handles request finishing. */
+ void sltHandleNetworkRequestFinish();
+
+ /** Handles signal about @a pNetworkCustomer being destroyed. */
+ void sltHandleNetworkCustomerBeingDestroyed(UINetworkCustomer *pNetworkCustomer);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups network-request with passed @a uId. */
+ void cleanupNetworkRequest(const QUuid &uId);
+ /** Cleanups all network-requests. */
+ void cleanupNetworkRequests();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the singleton instance. */
+ static UINetworkRequestManager *s_pInstance;
+
+ /** Holds the map of current requests. */
+ QMap<QUuid, UINetworkRequest*> m_requests;
+ /** Holds the map of current customers. */
+ QMap<QUuid, UINetworkCustomer*> m_customers;
+};
+
+/** Singleton Network Manager 'official' name. */
+#define gNetworkManager UINetworkRequestManager::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UINetworkRequestManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINewVersionChecker.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UINewVersionChecker.cpp
new file mode 100644
index 00000000..42f2057e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINewVersionChecker.cpp
@@ -0,0 +1,211 @@
+/* $Id: UINewVersionChecker.cpp $ */
+/** @file
+ * VBox Qt GUI - UINewVersionChecker class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QRegularExpression>
+#include <QUrlQuery>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UINetworkReply.h"
+#include "UINewVersionChecker.h"
+#include "UINotificationCenter.h"
+#include "UIUpdateDefs.h"
+#ifdef Q_OS_LINUX
+# include "QIProcess.h"
+#endif
+
+/* Other VBox includes: */
+#include <iprt/system.h>
+#ifdef Q_OS_LINUX
+# include <iprt/path.h>
+#endif
+
+
+UINewVersionChecker::UINewVersionChecker(bool fForcedCall)
+ : m_fForcedCall(fForcedCall)
+ , m_url("https://update.virtualbox.org/query.php")
+{
+}
+
+void UINewVersionChecker::start()
+{
+ /* Compose query: */
+ QUrlQuery url;
+ url.addQueryItem("platform", uiCommon().virtualBox().GetPackageType());
+ /* Check if branding is active: */
+ if (uiCommon().brandingIsActive())
+ {
+ /* Branding: Check whether we have a local branding file which tells us our version suffix "FOO"
+ (e.g. 3.06.54321_FOO) to identify this installation: */
+ url.addQueryItem("version", QString("%1_%2_%3").arg(uiCommon().virtualBox().GetVersion())
+ .arg(uiCommon().virtualBox().GetRevision())
+ .arg(uiCommon().brandingGetKey("VerSuffix")));
+ }
+ else
+ {
+ /* Use hard coded version set by VBOX_VERSION_STRING: */
+ url.addQueryItem("version", QString("%1_%2").arg(uiCommon().virtualBox().GetVersion())
+ .arg(uiCommon().virtualBox().GetRevision()));
+ }
+ url.addQueryItem("count", QString::number(gEDataManager->applicationUpdateCheckCounter()));
+ url.addQueryItem("branch", VBoxUpdateData(gEDataManager->applicationUpdateData()).updateChannelName());
+ const QString strUserAgent(QString("VirtualBox %1 <%2>").arg(uiCommon().virtualBox().GetVersion()).arg(platformInfo()));
+
+ /* Send GET request: */
+ UserDictionary headers;
+ headers["User-Agent"] = strUserAgent;
+ QUrl fullUrl(m_url);
+ fullUrl.setQuery(url);
+ createNetworkRequest(UINetworkRequestType_GET, QList<QUrl>() << fullUrl, QString(), headers);
+}
+
+void UINewVersionChecker::cancel()
+{
+ cancelNetworkRequest();
+}
+
+void UINewVersionChecker::processNetworkReplyProgress(qint64, qint64)
+{
+}
+
+void UINewVersionChecker::processNetworkReplyFailed(const QString &strError)
+{
+ emit sigProgressFailed(strError);
+}
+
+void UINewVersionChecker::processNetworkReplyCanceled(UINetworkReply *)
+{
+ emit sigProgressCanceled();
+}
+
+void UINewVersionChecker::processNetworkReplyFinished(UINetworkReply *pReply)
+{
+ /* Deserialize incoming data: */
+ const QString strResponseData(pReply->readAll());
+
+#ifdef VBOX_NEW_VERSION_TEST
+ strResponseData = VBOX_NEW_VERSION_TEST;
+#endif
+ /* Newer version of necessary package found: */
+ if (strResponseData.indexOf(QRegularExpression("^\\d+\\.\\d+\\.\\d+(_[0-9A-Z]+)? \\S+$")) == 0)
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ const QStringList response = strResponseData.split(" ", Qt::SkipEmptyParts);
+#else
+ const QStringList response = strResponseData.split(" ", QString::SkipEmptyParts);
+#endif
+ UINotificationMessage::showUpdateSuccess(response[0], response[1]);
+ }
+ /* No newer version of necessary package found: */
+ else
+ {
+ if (isItForcedCall())
+ UINotificationMessage::showUpdateNotFound();
+ }
+
+ /* Increment update check counter: */
+ gEDataManager->incrementApplicationUpdateCheckCounter();
+
+ /* Notify about completion: */
+ emit sigProgressFinished();
+}
+
+/* static */
+QString UINewVersionChecker::platformInfo()
+{
+ /* Prepare platform report: */
+ QString strPlatform;
+
+#if defined (Q_OS_WIN)
+ strPlatform = "win";
+#elif defined (Q_OS_LINUX)
+ strPlatform = "linux";
+#elif defined (Q_OS_MACX)
+ strPlatform = "macosx";
+#elif defined (Q_OS_OS2)
+ strPlatform = "os2";
+#elif defined (Q_OS_FREEBSD)
+ strPlatform = "freebsd";
+#elif defined (Q_OS_SOLARIS)
+ strPlatform = "solaris";
+#else
+ strPlatform = "unknown";
+#endif
+
+ /* The format is <system>.<bitness>: */
+ strPlatform += QString(".%1").arg(ARCH_BITS);
+
+ /* Add more system information: */
+ int vrc;
+#ifdef Q_OS_LINUX
+ // WORKAROUND:
+ // On Linux we try to generate information using script first of all..
+
+ /* Get script path: */
+ char szAppPrivPath[RTPATH_MAX];
+ vrc = RTPathAppPrivateNoArch(szAppPrivPath, sizeof(szAppPrivPath));
+ AssertRC(vrc);
+ if (RT_SUCCESS(vrc))
+ {
+ /* Run script: */
+ QByteArray result = QIProcess::singleShot(QString(szAppPrivPath) + "/VBoxSysInfo.sh");
+ if (!result.isNull())
+ strPlatform += QString(" [%1]").arg(QString(result).trimmed());
+ else
+ vrc = VERR_TRY_AGAIN; /* (take the fallback path) */
+ }
+ if (RT_FAILURE(vrc))
+#endif /* Q_OS_LINUX */
+ {
+ /* Use RTSystemQueryOSInfo: */
+ char szTmp[256];
+ QStringList components;
+
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
+ if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
+ components << QString("Product: %1").arg(szTmp);
+
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
+ if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
+ components << QString("Release: %1").arg(szTmp);
+
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
+ if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
+ components << QString("Version: %1").arg(szTmp);
+
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
+ if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
+ components << QString("SP: %1").arg(szTmp);
+
+ if (!components.isEmpty())
+ strPlatform += QString(" [%1]").arg(components.join(" | "));
+ }
+
+ return strPlatform;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UINewVersionChecker.h b/src/VBox/Frontends/VirtualBox/src/networking/UINewVersionChecker.h
new file mode 100644
index 00000000..ca6413e5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UINewVersionChecker.h
@@ -0,0 +1,92 @@
+/* $Id: UINewVersionChecker.h $ */
+/** @file
+ * VBox Qt GUI - UINewVersionChecker class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UINewVersionChecker_h
+#define FEQT_INCLUDED_SRC_networking_UINewVersionChecker_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UINetworkCustomer.h"
+
+/** UINetworkCustomer extension for new version check. */
+class SHARED_LIBRARY_STUFF UINewVersionChecker : public UINetworkCustomer
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about progress failed with @a strError. */
+ void sigProgressFailed(const QString &strError);
+ /** Notifies listeners about progress canceled. */
+ void sigProgressCanceled();
+ /** Notifies listeners about progress finished. */
+ void sigProgressFinished();
+
+public:
+
+ /** Constructs new version checker.
+ * @param fForcedCall Brings whether this customer has forced privelegies. */
+ UINewVersionChecker(bool fForcedCall);
+
+ /** Returns whether this customer has forced privelegies. */
+ bool isItForcedCall() const { return m_fForcedCall; }
+ /** Returns url. */
+ QUrl url() const { return m_url; }
+
+public slots:
+
+ /** Starts new version check. */
+ void start();
+ /** Cancels new version check. */
+ void cancel();
+
+protected:
+
+ /** Handles network reply progress for @a iReceived amount of bytes among @a iTotal. */
+ virtual void processNetworkReplyProgress(qint64 iReceived, qint64 iTotal);
+ /** Handles network reply failed with specified @a strError. */
+ virtual void processNetworkReplyFailed(const QString &strError);
+ /** Handles network reply canceling for a passed @a pReply. */
+ virtual void processNetworkReplyCanceled(UINetworkReply *pReply);
+ /** Handles network reply finishing for a passed @a pReply. */
+ virtual void processNetworkReplyFinished(UINetworkReply *pReply);
+
+private:
+
+ /** Generates platform information. */
+ static QString platformInfo();
+
+ /** Holds whether this customer has forced privelegies. */
+ bool m_fForcedCall;
+ /** Holds the new version checker URL. */
+ QUrl m_url;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UINewVersionChecker_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateDefs.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateDefs.cpp
new file mode 100644
index 00000000..ae098aa1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateDefs.cpp
@@ -0,0 +1,404 @@
+/* $Id: UIUpdateDefs.cpp $ */
+/** @file
+ * VBox Qt GUI - Update routine related implementations.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCoreApplication>
+#include <QLocale>
+#include <QStringList>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UINotificationCenter.h"
+#include "UIUpdateDefs.h"
+
+/* COM includes: */
+#include "CUpdateAgent.h"
+
+
+/* static: */
+VBoxUpdateDayList VBoxUpdateData::s_days = VBoxUpdateDayList();
+
+/* static */
+void VBoxUpdateData::populate()
+{
+ /* Clear list initially: */
+ s_days.clear();
+
+ // WORKAROUND:
+ // To avoid re-translation complexity
+ // all values will be retranslated separately.
+
+ /* Separately retranslate each day: */
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "1 day"), "1 d", 86400);
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "2 days"), "2 d", 172800);
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "3 days"), "3 d", 259200);
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "4 days"), "4 d", 345600);
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "5 days"), "5 d", 432000);
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "6 days"), "6 d", 518400);
+
+ /* Separately retranslate each week: */
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "1 week"), "1 w", 604800);
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "2 weeks"), "2 w", 1209600);
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "3 weeks"), "3 w", 1814400);
+
+ /* Separately retranslate each month: */
+ s_days << VBoxUpdateDay(QCoreApplication::translate("UIUpdateManager", "1 month"), "1 m", 2592000);
+}
+
+/* static */
+QStringList VBoxUpdateData::list()
+{
+ QStringList result;
+ foreach (const VBoxUpdateDay &day, s_days)
+ result << day.val;
+ return result;
+}
+
+VBoxUpdateData::VBoxUpdateData(const QString &strData)
+ : m_strData(strData)
+ , m_fCheckEnabled(false)
+ , m_fCheckRequired(false)
+ , m_enmUpdatePeriod(UpdatePeriodType_Never)
+ , m_enmUpdateChannel(KUpdateChannel_Invalid)
+{
+ /* Skip 'never' case: */
+ if (m_strData == "never")
+ return;
+
+ /* Check is enabled in all cases besides 'never': */
+ m_fCheckEnabled = true;
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ const QStringList parser = m_strData.split(", ", Qt::SkipEmptyParts);
+#else
+ const QStringList parser = m_strData.split(", ", QString::SkipEmptyParts);
+#endif
+
+ /* Parse 'period' value: */
+ if (parser.size() > 0)
+ {
+ if (s_days.isEmpty())
+ populate();
+ m_enmUpdatePeriod = (UpdatePeriodType)s_days.indexOf(VBoxUpdateDay(QString(), parser.at(0), 0));
+ if (m_enmUpdatePeriod == UpdatePeriodType_Never)
+ m_enmUpdatePeriod = UpdatePeriodType_1Day;
+ }
+
+ /* Parse 'date' value: */
+ if (parser.size() > 1)
+ {
+ QDate date = QDate::fromString(parser.at(1), Qt::ISODate);
+ m_date = date.isValid() ? date : QDate::currentDate();
+ }
+
+ /* Parse 'update channel' value: */
+ if (parser.size() > 2)
+ {
+ m_enmUpdateChannel = updateChannelFromInternalString(parser.at(2));
+ }
+
+ /* Parse 'version' value: */
+ if (parser.size() > 3)
+ {
+ m_version = UIVersion(parser.at(3));
+ }
+
+ /* Decide whether we need to check: */
+ m_fCheckRequired = (QDate::currentDate() >= date())
+ && ( !version().isValid()
+ || version() != UIVersion(uiCommon().vboxVersionStringNormalized()));
+}
+
+VBoxUpdateData::VBoxUpdateData(bool fCheckEnabled, UpdatePeriodType enmUpdatePeriod, KUpdateChannel enmUpdateChannel)
+ : m_strData("never")
+ , m_fCheckEnabled(fCheckEnabled)
+ , m_fCheckRequired(false)
+ , m_enmUpdatePeriod(enmUpdatePeriod)
+ , m_enmUpdateChannel(enmUpdateChannel)
+{
+ /* Skip 'check disabled' case: */
+ if (!m_fCheckEnabled)
+ return;
+
+ /* Encode 'period' value: */
+ if (s_days.isEmpty())
+ populate();
+ const QString strRemindPeriod = s_days.at(m_enmUpdatePeriod).key;
+
+ /* Encode 'date' value: */
+ m_date = QDate::currentDate();
+ QStringList parser(strRemindPeriod.split(' '));
+ if (parser[1] == "d")
+ m_date = m_date.addDays(parser[0].toInt());
+ else if (parser[1] == "w")
+ m_date = m_date.addDays(parser[0].toInt() * 7);
+ else if (parser[1] == "m")
+ m_date = m_date.addDays(parser[0].toInt() * 30);
+ const QString strRemindDate = m_date.toString(Qt::ISODate);
+
+ /* Encode 'update channel' value: */
+ const QString strUpdateChannel = updateChannelName();
+
+ /* Encode 'version' value: */
+ m_version = UIVersion(uiCommon().vboxVersionStringNormalized());
+ const QString strVersionValue = m_version.toString();
+
+ /* Compose m_strData: */
+ m_strData = QString("%1, %2, %3, %4").arg(strRemindPeriod, strRemindDate, strUpdateChannel, strVersionValue);
+
+ /* Decide whether we need to check: */
+ m_fCheckRequired = (QDate::currentDate() >= date())
+ && ( !version().isValid()
+ || version() != UIVersion(uiCommon().vboxVersionStringNormalized()));
+}
+
+bool VBoxUpdateData::load(const CHost &comHost)
+{
+ /* Acquire update agent: */
+ CUpdateAgent comAgent = comHost.GetUpdateHost();
+ if (!comHost.isOk())
+ {
+ UINotificationMessage::cannotAcquireHostParameter(comHost);
+ return false;
+ }
+
+ /* Fetch whether agent is enabled: */
+ const BOOL fEnabled = comAgent.GetEnabled();
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotAcquireUpdateAgentParameter(comAgent);
+ return false;
+ }
+ m_fCheckEnabled = fEnabled;
+
+ /* Fetch 'period' value: */
+ const ULONG uFrequency = comAgent.GetCheckFrequency();
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotAcquireUpdateAgentParameter(comAgent);
+ return false;
+ }
+ m_enmUpdatePeriod = gatherSuitablePeriod(uFrequency);
+
+ /* Fetch 'date' value: */
+ const QString strLastDate = comAgent.GetLastCheckDate();
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotAcquireUpdateAgentParameter(comAgent);
+ return false;
+ }
+ m_date = QDate::fromString(strLastDate, Qt::ISODate);
+ const ULONG uFrequencyInDays = (uFrequency / 86400) + 1;
+ m_date = m_date.addDays(uFrequencyInDays);
+
+ /* Fetch 'update channel' value: */
+ KUpdateChannel enmUpdateChannel = comAgent.GetChannel();
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotAcquireUpdateAgentParameter(comAgent);
+ return false;
+ }
+ m_enmUpdateChannel = enmUpdateChannel;
+
+ /* Fetch 'version' value: */
+ const QString strVersion = comAgent.GetVersion();
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotAcquireUpdateAgentParameter(comAgent);
+ return false;
+ }
+ m_version = strVersion;
+
+ /* Fetch whether we need to check: */
+ const BOOL fNeedToCheck = comAgent.GetIsCheckNeeded();
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotAcquireUpdateAgentParameter(comAgent);
+ return false;
+ }
+ m_fCheckRequired = fNeedToCheck;
+
+ /* Optional stuff goes last; Fetch supported update channels: */
+ const QVector<KUpdateChannel> supportedUpdateChannels = comAgent.GetSupportedChannels();
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotAcquireUpdateAgentParameter(comAgent);
+ return false;
+ }
+ m_supportedUpdateChannels = supportedUpdateChannels;
+
+ /* Success finally: */
+ return true;
+}
+
+bool VBoxUpdateData::save(const CHost &comHost) const
+{
+ /* Acquire update agent: */
+ CUpdateAgent comAgent = comHost.GetUpdateHost();
+ if (!comHost.isOk())
+ {
+ UINotificationMessage::cannotAcquireHostParameter(comHost);
+ return false;
+ }
+
+ /* Save whether agent is enabled: */
+ comAgent.SetEnabled(m_fCheckEnabled);
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotChangeUpdateAgentParameter(comAgent);
+ return false;
+ }
+
+ /* Save 'period' value: */
+ comAgent.SetCheckFrequency(s_days.at(m_enmUpdatePeriod).length);
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotChangeUpdateAgentParameter(comAgent);
+ return false;
+ }
+
+ /* Save 'update channel' value: */
+ comAgent.SetChannel(m_enmUpdateChannel);
+ if (!comAgent.isOk())
+ {
+ UINotificationMessage::cannotChangeUpdateAgentParameter(comAgent);
+ return false;
+ }
+
+ /* Success finally: */
+ return true;
+}
+
+bool VBoxUpdateData::isCheckEnabled() const
+{
+ return m_fCheckEnabled;
+}
+
+bool VBoxUpdateData::isCheckRequired() const
+{
+ return m_fCheckRequired;
+}
+
+QString VBoxUpdateData::data() const
+{
+ return m_strData;
+}
+
+UpdatePeriodType VBoxUpdateData::updatePeriod() const
+{
+ return m_enmUpdatePeriod;
+}
+
+QDate VBoxUpdateData::date() const
+{
+ return m_date;
+}
+
+QString VBoxUpdateData::dateToString() const
+{
+ return isCheckEnabled()
+ ? QLocale::system().toString(m_date, QLocale::ShortFormat)
+ : QCoreApplication::translate("UIUpdateManager", "Never");
+}
+
+KUpdateChannel VBoxUpdateData::updateChannel() const
+{
+ return m_enmUpdateChannel;
+}
+
+QString VBoxUpdateData::updateChannelName() const
+{
+ return updateChannelToInternalString(m_enmUpdateChannel);
+}
+
+UIVersion VBoxUpdateData::version() const
+{
+ return m_version;
+}
+
+QVector<KUpdateChannel> VBoxUpdateData::supportedUpdateChannels() const
+{
+ return m_supportedUpdateChannels;
+}
+
+bool VBoxUpdateData::isEqual(const VBoxUpdateData &another) const
+{
+ return true
+ && (m_fCheckEnabled == another.isCheckEnabled())
+ && (m_enmUpdatePeriod == another.updatePeriod())
+ && (m_enmUpdateChannel == another.updateChannel())
+ ;
+}
+
+bool VBoxUpdateData::operator==(const VBoxUpdateData &another) const
+{
+ return isEqual(another);
+}
+
+bool VBoxUpdateData::operator!=(const VBoxUpdateData &another) const
+{
+ return !isEqual(another);
+}
+
+/* static */
+QString VBoxUpdateData::updateChannelToInternalString(KUpdateChannel enmUpdateChannel)
+{
+ switch (enmUpdateChannel)
+ {
+ case KUpdateChannel_WithTesting: return "withtesting";
+ case KUpdateChannel_WithBetas: return "withbetas";
+ case KUpdateChannel_All: return "allrelease";
+ default: return "stable";
+ }
+}
+
+/* static */
+KUpdateChannel VBoxUpdateData::updateChannelFromInternalString(const QString &strUpdateChannel)
+{
+ QMap<QString, KUpdateChannel> pairs;
+ pairs["withtesting"] = KUpdateChannel_WithTesting;
+ pairs["withbetas"] = KUpdateChannel_WithBetas;
+ pairs["allrelease"] = KUpdateChannel_All;
+ return pairs.value(strUpdateChannel, KUpdateChannel_Stable);
+}
+
+/* static */
+UpdatePeriodType VBoxUpdateData::gatherSuitablePeriod(ULONG uFrequency)
+{
+ if (s_days.isEmpty())
+ populate();
+
+ UpdatePeriodType enmType = UpdatePeriodType_1Day;
+ foreach (const VBoxUpdateDay &day, s_days)
+ {
+ if (uFrequency <= day.length)
+ return enmType;
+ enmType = (UpdatePeriodType)(enmType + 1);
+ }
+
+ return UpdatePeriodType_1Month;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateDefs.h b/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateDefs.h
new file mode 100644
index 00000000..39d49703
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateDefs.h
@@ -0,0 +1,176 @@
+/* $Id: UIUpdateDefs.h $ */
+/** @file
+ * VBox Qt GUI - Update routine related declarations.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UIUpdateDefs_h
+#define FEQT_INCLUDED_SRC_networking_UIUpdateDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDate>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UIVersion.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CHost.h"
+
+
+/** Update period types. */
+enum UpdatePeriodType
+{
+ UpdatePeriodType_Never = -1,
+ UpdatePeriodType_1Day = 0,
+ UpdatePeriodType_2Days = 1,
+ UpdatePeriodType_3Days = 2,
+ UpdatePeriodType_4Days = 3,
+ UpdatePeriodType_5Days = 4,
+ UpdatePeriodType_6Days = 5,
+ UpdatePeriodType_1Week = 6,
+ UpdatePeriodType_2Weeks = 7,
+ UpdatePeriodType_3Weeks = 8,
+ UpdatePeriodType_1Month = 9
+};
+
+
+/** Structure to store retranslated period type values. */
+struct VBoxUpdateDay
+{
+ VBoxUpdateDay(const QString &strVal, const QString &strKey, ULONG uLength)
+ : val(strVal)
+ , key(strKey)
+ , length(uLength)
+ {}
+
+ bool operator==(const VBoxUpdateDay &other) const
+ {
+ return val == other.val
+ || key == other.key
+ || length == other.length;
+ }
+
+ QString val;
+ QString key;
+ ULONG length;
+};
+typedef QList<VBoxUpdateDay> VBoxUpdateDayList;
+
+
+/** Class used to encode/decode update data. */
+class SHARED_LIBRARY_STUFF VBoxUpdateData
+{
+public:
+
+ /** Populates a set of update options. */
+ static void populate();
+ /** Returns a list of update options. */
+ static QStringList list();
+
+ /** Constructs update description on the basis of passed @a strData. */
+ VBoxUpdateData(const QString &strData = QString());
+ /** Constructs update description on the basis of passed @a fCheckEnabled, @a enmUpdatePeriod and @a enmUpdateChannel. */
+ VBoxUpdateData(bool fCheckEnabled, UpdatePeriodType enmUpdatePeriod, KUpdateChannel enmUpdateChannel);
+
+ /** Loads data from IHost. */
+ bool load(const CHost &comHost);
+ /** Saves data to IHost. */
+ bool save(const CHost &comHost) const;
+
+ /** Returns whether check is enabled. */
+ bool isCheckEnabled() const;
+ /** Returns whether check is required. */
+ bool isCheckRequired() const;
+
+ /** Returns update data. */
+ QString data() const;
+
+ /** Returns update period. */
+ UpdatePeriodType updatePeriod() const;
+ /** Returns update date. */
+ QDate date() const;
+ /** Returns update date as string. */
+ QString dateToString() const;
+ /** Returns update channel. */
+ KUpdateChannel updateChannel() const;
+ /** Returns update channel name. */
+ QString updateChannelName() const;
+ /** Returns version. */
+ UIVersion version() const;
+
+ /** Returns supported update chennels. */
+ QVector<KUpdateChannel> supportedUpdateChannels() const;
+
+ /** Returns whether this item equals to @a another one. */
+ bool isEqual(const VBoxUpdateData &another) const;
+ /** Returns whether this item equals to @a another one. */
+ bool operator==(const VBoxUpdateData &another) const;
+ /** Returns whether this item isn't equal to @a another one. */
+ bool operator!=(const VBoxUpdateData &another) const;
+
+ /** Converts passed @a enmUpdateChannel to internal QString value.
+ * @note This isn't a member of UIConverter since it's used for
+ * legacy extra-data settings saving routine only. */
+ static QString updateChannelToInternalString(KUpdateChannel enmUpdateChannel);
+ /** Converts passed @a strUpdateChannel to KUpdateChannel value.
+ * @note This isn't a member of UIConverter since it's used for
+ * legacy extra-data settings saving routine only. */
+ static KUpdateChannel updateChannelFromInternalString(const QString &strUpdateChannel);
+
+private:
+
+ /** Gathers period suitable to passed @a uFrequency rounding up. */
+ static UpdatePeriodType gatherSuitablePeriod(ULONG uFrequency);
+
+ /** Holds the populated list of update period options. */
+ static VBoxUpdateDayList s_days;
+
+ /** Holds the update data. */
+ QString m_strData;
+
+ /** Holds whether check is enabled. */
+ bool m_fCheckEnabled;
+ /** Holds whether it's need to check for update. */
+ bool m_fCheckRequired;
+
+ /** Holds the update period. */
+ UpdatePeriodType m_enmUpdatePeriod;
+ /** Holds the update date. */
+ QDate m_date;
+ /** Holds the update channel. */
+ KUpdateChannel m_enmUpdateChannel;
+ /** Holds the update version. */
+ UIVersion m_version;
+
+ /** Holds the supported update chennels. */
+ QVector<KUpdateChannel> m_supportedUpdateChannels;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UIUpdateDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateManager.cpp b/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateManager.cpp
new file mode 100644
index 00000000..9d4c086d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateManager.cpp
@@ -0,0 +1,370 @@
+/* $Id: UIUpdateManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUpdateManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIExecutionQueue.h"
+#include "UIExtension.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINotificationCenter.h"
+#include "UIUpdateDefs.h"
+#include "UIUpdateManager.h"
+
+/* COM includes: */
+#include "CExtPack.h"
+#include "CExtPackManager.h"
+
+
+/** UIExecutionStep extension to check for the new VirtualBox version. */
+class UIUpdateStepVirtualBox : public UIExecutionStep
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs extension step.
+ * @param fForcedCall Brings whether this customer has forced privelegies. */
+ UIUpdateStepVirtualBox(bool fForcedCall);
+
+ /** Executes the step. */
+ virtual void exec() RT_OVERRIDE;
+
+private:
+
+ /** Holds whether this customer has forced privelegies. */
+ bool m_fForcedCall;
+
+};
+
+
+/** UIExecutionStep extension to check for the new VirtualBox Extension Pack version. */
+class UIUpdateStepVirtualBoxExtensionPack : public UIExecutionStep
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs extension step. */
+ UIUpdateStepVirtualBoxExtensionPack();
+
+ /** Executes the step. */
+ virtual void exec() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles downloaded Extension Pack.
+ * @param strSource Brings the EP source.
+ * @param strTarget Brings the EP target.
+ * @param strDigest Brings the EP digest. */
+ void sltHandleDownloadedExtensionPack(const QString &strSource,
+ const QString &strTarget,
+ const QString &strDigest);
+};
+
+
+/*********************************************************************************************************************************
+* Class UIUpdateStepVirtualBox implementation. *
+*********************************************************************************************************************************/
+
+UIUpdateStepVirtualBox::UIUpdateStepVirtualBox(bool fForcedCall)
+ : m_fForcedCall(fForcedCall)
+{
+ Q_UNUSED(fForcedCall);
+}
+
+void UIUpdateStepVirtualBox::exec()
+{
+ /* Check for new VirtualBox version: */
+ UINotificationProgressNewVersionChecker *pNotification =
+ new UINotificationProgressNewVersionChecker(m_fForcedCall);
+ connect(pNotification, &UINotificationProgressNewVersionChecker::sigProgressFinished,
+ this, &UIUpdateStepVirtualBox::sigStepFinished);
+ gpNotificationCenter->append(pNotification);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIUpdateStepVirtualBoxExtensionPack implementation. *
+*********************************************************************************************************************************/
+
+UIUpdateStepVirtualBoxExtensionPack::UIUpdateStepVirtualBoxExtensionPack()
+{
+}
+
+void UIUpdateStepVirtualBoxExtensionPack::exec()
+{
+ /* Return if VirtualBox Manager issued a direct request to install EP: */
+ if (gUpdateManager->isEPInstallationRequested())
+ {
+ emit sigStepFinished();
+ return;
+ }
+
+ /* Return if already downloading: */
+ if (UINotificationDownloaderExtensionPack::exists())
+ {
+ gpNotificationCenter->invoke();
+ emit sigStepFinished();
+ return;
+ }
+
+ /* Get extension pack manager: */
+ CExtPackManager extPackManager = uiCommon().virtualBox().GetExtensionPackManager();
+ /* Return if extension pack manager is NOT available: */
+ if (extPackManager.isNull())
+ {
+ emit sigStepFinished();
+ return;
+ }
+
+ /* Get extension pack: */
+ CExtPack extPack = extPackManager.Find(GUI_ExtPackName);
+ /* Return if extension pack is NOT installed: */
+ if (extPack.isNull())
+ {
+ emit sigStepFinished();
+ return;
+ }
+
+ /* Get VirtualBox version: */
+ UIVersion vboxVersion(uiCommon().vboxVersionStringNormalized());
+ /* Get extension pack version: */
+ QString strExtPackVersion(extPack.GetVersion());
+
+ /* If this version being developed: */
+ if (vboxVersion.z() % 2 == 1)
+ {
+ /* If this version being developed on release branch (we use released one): */
+ if (vboxVersion.z() < 97)
+ vboxVersion.setZ(vboxVersion.z() - 1);
+ /* If this version being developed on trunk (we skip check at all): */
+ else
+ {
+ emit sigStepFinished();
+ return;
+ }
+ }
+
+ /* Get updated VirtualBox version: */
+ const QString strVBoxVersion = vboxVersion.toString();
+
+ /* Skip the check if the extension pack is equal to or newer than VBox. */
+ if (UIVersion(strExtPackVersion) >= vboxVersion)
+ {
+ emit sigStepFinished();
+ return;
+ }
+
+ QString strExtPackEdition(extPack.GetEdition());
+ if (strExtPackEdition.contains("ENTERPRISE"))
+ {
+ /* Inform the user that he should update the extension pack: */
+ UINotificationMessage::askUserToDownloadExtensionPack(GUI_ExtPackName, strExtPackVersion, strVBoxVersion);
+ /* Never try to download for ENTERPRISE version: */
+ emit sigStepFinished();
+ return;
+ }
+
+ /* Ask the user about extension pack downloading: */
+ if (!msgCenter().confirmLookingForExtensionPack(GUI_ExtPackName, strExtPackVersion))
+ {
+ emit sigStepFinished();
+ return;
+ }
+
+ /* Download extension pack: */
+ UINotificationDownloaderExtensionPack *pNotification = UINotificationDownloaderExtensionPack::instance(GUI_ExtPackName);
+ /* After downloading finished => propose to install the Extension Pack: */
+ connect(pNotification, &UINotificationDownloaderExtensionPack::sigExtensionPackDownloaded,
+ this, &UIUpdateStepVirtualBoxExtensionPack::sltHandleDownloadedExtensionPack);
+ /* Handle any signal as step-finished: */
+ connect(pNotification, &UINotificationDownloaderExtensionPack::sigProgressFailed,
+ this, &UIUpdateStepVirtualBoxExtensionPack::sigStepFinished);
+ connect(pNotification, &UINotificationDownloaderExtensionPack::sigProgressCanceled,
+ this, &UIUpdateStepVirtualBoxExtensionPack::sigStepFinished);
+ connect(pNotification, &UINotificationDownloaderExtensionPack::sigProgressFinished,
+ this, &UIUpdateStepVirtualBoxExtensionPack::sigStepFinished);
+ /* Append and start notification: */
+ gpNotificationCenter->append(pNotification);
+}
+
+void UIUpdateStepVirtualBoxExtensionPack::sltHandleDownloadedExtensionPack(const QString &strSource,
+ const QString &strTarget,
+ const QString &strDigest)
+{
+ /* Warn the user about extension pack was downloaded and saved, propose to install it: */
+ if (msgCenter().proposeInstallExtentionPack(GUI_ExtPackName, strSource, QDir::toNativeSeparators(strTarget)))
+ UIExtension::install(strTarget, strDigest, windowManager().mainWindowShown(), NULL);
+ /* Propose to delete the downloaded extension pack: */
+ if (msgCenter().proposeDeleteExtentionPack(QDir::toNativeSeparators(strTarget)))
+ {
+ /* Delete the downloaded extension pack: */
+ QFile::remove(QDir::toNativeSeparators(strTarget));
+ /* Get the list of old extension pack files in VirtualBox homefolder: */
+ const QStringList oldExtPackFiles = QDir(uiCommon().homeFolder()).entryList(QStringList("*.vbox-extpack"),
+ QDir::Files);
+ /* Propose to delete old extension pack files if there are any: */
+ if (oldExtPackFiles.size())
+ {
+ if (msgCenter().proposeDeleteOldExtentionPacks(oldExtPackFiles))
+ {
+ foreach (const QString &strExtPackFile, oldExtPackFiles)
+ {
+ /* Delete the old extension pack file: */
+ QFile::remove(QDir::toNativeSeparators(QDir(uiCommon().homeFolder()).filePath(strExtPackFile)));
+ }
+ }
+ }
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIUpdateManager implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIUpdateManager* UIUpdateManager::s_pInstance = 0;
+
+UIUpdateManager::UIUpdateManager()
+ : m_pQueue(new UIExecutionQueue(this))
+ , m_fIsRunning(false)
+ , m_uTime(1 /* day */ * 24 /* hours */ * 60 /* minutes */ * 60 /* seconds */ * 1000 /* ms */)
+ , m_fEPInstallationRequested(false)
+{
+ /* Prepare instance: */
+ if (s_pInstance != this)
+ s_pInstance = this;
+
+ /* Configure queue: */
+ connect(m_pQueue, &UIExecutionQueue::sigQueueFinished, this, &UIUpdateManager::sltHandleUpdateFinishing);
+
+#ifdef VBOX_WITH_UPDATE_REQUEST
+ /* Ask updater to check for the first time, for Selector UI only: */
+ if (gEDataManager->applicationUpdateEnabled() && uiCommon().uiType() == UICommon::UIType_SelectorUI)
+ QTimer::singleShot(0, this, SLOT(sltCheckIfUpdateIsNecessary()));
+#endif /* VBOX_WITH_UPDATE_REQUEST */
+}
+
+UIUpdateManager::~UIUpdateManager()
+{
+ /* Cleanup instance: */
+ if (s_pInstance == this)
+ s_pInstance = 0;
+}
+
+/* static */
+void UIUpdateManager::schedule()
+{
+ /* Ensure instance is NOT created: */
+ if (s_pInstance)
+ return;
+
+ /* Create instance: */
+ new UIUpdateManager;
+}
+
+/* static */
+void UIUpdateManager::shutdown()
+{
+ /* Ensure instance is created: */
+ if (!s_pInstance)
+ return;
+
+ /* Delete instance: */
+ delete s_pInstance;
+}
+
+void UIUpdateManager::sltForceCheck()
+{
+ /* Force call for new version check: */
+ sltCheckIfUpdateIsNecessary(true /* force call */);
+}
+
+void UIUpdateManager::sltCheckIfUpdateIsNecessary(bool fForcedCall /* = false */)
+{
+ /* If already running: */
+ if (m_fIsRunning)
+ {
+ /* And we have a force-call: */
+ if (fForcedCall)
+ gpNotificationCenter->invoke();
+ return;
+ }
+
+ /* Set as running: */
+ m_fIsRunning = true;
+
+ /* Load/decode curent update data: */
+ VBoxUpdateData currentData;
+ CHost comHost = uiCommon().host();
+ currentData.load(comHost);
+
+ /* If update is really necessary: */
+ if (
+#ifdef VBOX_NEW_VERSION_TEST
+ true ||
+#endif
+ fForcedCall || currentData.isCheckRequired())
+ {
+ /* Prepare update queue: */
+ m_pQueue->enqueue(new UIUpdateStepVirtualBox(fForcedCall));
+ m_pQueue->enqueue(new UIUpdateStepVirtualBoxExtensionPack);
+ /* Start update queue: */
+ m_pQueue->start();
+ }
+ else
+ sltHandleUpdateFinishing();
+}
+
+void UIUpdateManager::sltHandleUpdateFinishing()
+{
+ /* Load/decode curent update data: */
+ VBoxUpdateData currentData;
+ CHost comHost = uiCommon().host();
+ currentData.load(comHost);
+ /* Encode/save new update data: */
+ VBoxUpdateData newData(currentData.isCheckEnabled(), currentData.updatePeriod(), currentData.updateChannel());
+ newData.save(comHost);
+
+#ifdef VBOX_WITH_UPDATE_REQUEST
+ /* Ask updater to check for the next time: */
+ QTimer::singleShot(m_uTime, this, SLOT(sltCheckIfUpdateIsNecessary()));
+#endif /* VBOX_WITH_UPDATE_REQUEST */
+
+ /* Set as not running: */
+ m_fIsRunning = false;
+}
+
+
+#include "UIUpdateManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateManager.h b/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateManager.h
new file mode 100644
index 00000000..768dccf5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networking/UIUpdateManager.h
@@ -0,0 +1,101 @@
+/* $Id: UIUpdateManager.h $ */
+/** @file
+ * VBox Qt GUI - UIUpdateManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networking_UIUpdateManager_h
+#define FEQT_INCLUDED_SRC_networking_UIUpdateManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class UIExecutionQueue;
+
+/** Singleton to perform new version checks
+ * and update of various VirtualBox parts. */
+class SHARED_LIBRARY_STUFF UIUpdateManager : public QObject
+{
+ Q_OBJECT;
+
+ /** Constructs Update Manager. */
+ UIUpdateManager();
+ /** Destructs Update Manager. */
+ ~UIUpdateManager();
+
+public:
+
+ /** Schedules manager. */
+ static void schedule();
+ /** Shutdowns manager. */
+ static void shutdown();
+ /** Returns manager instance. */
+ static UIUpdateManager *instance() { return s_pInstance; }
+
+ /** Returns whether the Extension Pack installation is requested. */
+ bool isEPInstallationRequested() const { return m_fEPInstallationRequested; }
+ /** Defines whether the Extension Pack installation is @a fRequested. */
+ void setEPInstallationRequested(bool fRequested) { m_fEPInstallationRequested = fRequested; }
+
+public slots:
+
+ /** Performs forced new version check. */
+ void sltForceCheck();
+
+private slots:
+
+ /** Checks whether update is necessary.
+ * @param fForcedCall Brings whether this customer has forced privelegies. */
+ void sltCheckIfUpdateIsNecessary(bool fForcedCall = false);
+
+ /** Handles update finishing. */
+ void sltHandleUpdateFinishing();
+
+private:
+
+ /** Holds the singleton instance. */
+ static UIUpdateManager *s_pInstance;
+
+ /** Holds the execution queue instance. */
+ UIExecutionQueue *m_pQueue;
+ /** Holds whether Update Manager is running. */
+ bool m_fIsRunning;
+ /** Holds the refresh period. */
+ quint64 m_uTime;
+
+ /** Holds whether the Extension Pack installation is requested. */
+ bool m_fEPInstallationRequested;
+};
+
+/** Singleton Update Manager 'official' name. */
+#define gUpdateManager UIUpdateManager::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_networking_UIUpdateManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/networkmanager/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetCloudNetwork.cpp b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetCloudNetwork.cpp
new file mode 100644
index 00000000..c1f8497b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetCloudNetwork.cpp
@@ -0,0 +1,621 @@
+/* $Id: UIDetailsWidgetCloudNetwork.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsWidgetCloudNetwork class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QComboBox>
+#include <QFontMetrics>
+#include <QGroupBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QStyleOption>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QILineEdit.h"
+#include "QITabWidget.h"
+#include "QIToolButton.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIDetailsWidgetCloudNetwork.h"
+#include "UIFormEditorWidget.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINetworkManager.h"
+#include "UINetworkManagerUtils.h"
+#include "UINotificationCenter.h"
+
+
+UISubnetSelectionDialog::UISubnetSelectionDialog(QWidget *pParent,
+ const QString &strShortProviderName,
+ const QString &strProfileName,
+ const QString &strSubnetId)
+ : QIWithRetranslateUI<QDialog>(pParent)
+ , m_strProviderShortName(strShortProviderName)
+ , m_strProfileName(strProfileName)
+ , m_strSubnetId(strSubnetId)
+ , m_pFormEditor(0)
+ , m_pButtonBox(0)
+ , m_pNotificationCenter(0)
+{
+ prepare();
+}
+
+UISubnetSelectionDialog::~UISubnetSelectionDialog()
+{
+ cleanup();
+}
+
+void UISubnetSelectionDialog::accept()
+{
+ /* Get altered description back: */
+ m_comForm.GetVirtualSystemDescription();
+ QVector<KVirtualSystemDescriptionType> aTypes;
+ QVector<QString> aRefs, aOVFValues, aVBoxValues, aExtraConfigValues;
+ m_comDescription.GetDescriptionByType(KVirtualSystemDescriptionType_CloudOCISubnet,
+ aTypes, aRefs, aOVFValues, aVBoxValues, aExtraConfigValues);
+ if (!m_comDescription.isOk())
+ {
+ UINotificationMessage::cannotAcquireVirtualSystemDescriptionParameter(m_comDescription,
+ m_pNotificationCenter);
+ return;
+ }
+ AssertReturnVoid(!aVBoxValues.isEmpty());
+ m_strSubnetId = aVBoxValues.first();
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QDialog>::accept();
+}
+
+int UISubnetSelectionDialog::exec()
+{
+ /* Request to init dialog _after_ being executed: */
+ QMetaObject::invokeMethod(this, "sltInit", Qt::QueuedConnection);
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QDialog>::exec();
+}
+
+void UISubnetSelectionDialog::retranslateUi()
+{
+ setWindowTitle(UINetworkManager::tr("Select Subnet"));
+}
+
+void UISubnetSelectionDialog::sltInit()
+{
+ /* Create description: */
+ m_comDescription = createVirtualSystemDescription(m_pNotificationCenter);
+ if (m_comDescription.isNull())
+ return;
+ /* Update it with current subnet value: */
+ m_comDescription.AddDescription(KVirtualSystemDescriptionType_CloudOCISubnet, m_strSubnetId, QString());
+
+ /* Create cloud client: */
+ CCloudClient comCloudClient = cloudClientByName(m_strProviderShortName, m_strProfileName, m_pNotificationCenter);
+ if (comCloudClient.isNull())
+ return;
+
+ /* Create subnet selection VSD form: */
+ UINotificationProgressSubnetSelectionVSDFormCreate *pNotification = new UINotificationProgressSubnetSelectionVSDFormCreate(comCloudClient,
+ m_comDescription,
+ m_strProviderShortName,
+ m_strProfileName);
+ connect(pNotification, &UINotificationProgressSubnetSelectionVSDFormCreate::sigVSDFormCreated,
+ this, &UISubnetSelectionDialog::sltHandleVSDFormCreated);
+ m_pNotificationCenter->append(pNotification);
+}
+
+void UISubnetSelectionDialog::sltHandleVSDFormCreated(const CVirtualSystemDescriptionForm &comForm)
+{
+ m_comForm = comForm;
+ m_pFormEditor->setVirtualSystemDescriptionForm(m_comForm);
+}
+
+void UISubnetSelectionDialog::prepare()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare form editor: */
+ m_pFormEditor = new UIFormEditorWidget(this);
+ if (m_pFormEditor)
+ pLayoutMain->addWidget(m_pFormEditor);
+
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox(this);
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &QDialog::reject);
+
+ pLayoutMain->addWidget(m_pButtonBox);
+ }
+ }
+
+ /* Prepare local notification-center: */
+ m_pNotificationCenter = new UINotificationCenter(this);
+ if (m_pNotificationCenter && m_pFormEditor)
+ m_pFormEditor->setNotificationCenter(m_pNotificationCenter);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UISubnetSelectionDialog::cleanup()
+{
+ /* Cleanup local notification-center: */
+ delete m_pNotificationCenter;
+ m_pNotificationCenter = 0;
+}
+
+
+UIDetailsWidgetCloudNetwork::UIDetailsWidgetCloudNetwork(EmbedTo enmEmbedding, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pLabelNetworkName(0)
+ , m_pEditorNetworkName(0)
+ , m_pLabelProviderName(0)
+ , m_pComboProviderName(0)
+ , m_pLabelProfileName(0)
+ , m_pComboProfileName(0)
+ , m_pLabelNetworkId(0)
+ , m_pEditorNetworkId(0)
+ , m_pButtonNetworkId(0)
+ , m_pButtonBoxOptions(0)
+{
+ prepare();
+}
+
+void UIDetailsWidgetCloudNetwork::setData(const UIDataCloudNetwork &data,
+ const QStringList &busyNames /* = QStringList() */)
+{
+ /* Cache old/new data: */
+ m_oldData = data;
+ m_newData = m_oldData;
+ m_busyNames = busyNames;
+
+ /* Load data: */
+ loadData();
+}
+
+bool UIDetailsWidgetCloudNetwork::revalidate() const
+{
+ /* Make sure network name isn't empty: */
+ if (m_newData.m_strName.isEmpty())
+ {
+ UINotificationMessage::warnAboutNoNameSpecified(m_oldData.m_strName);
+ return false;
+ }
+ else
+ {
+ /* Make sure item names are unique: */
+ if (m_busyNames.contains(m_newData.m_strName))
+ {
+ UINotificationMessage::warnAboutNameAlreadyBusy(m_newData.m_strName);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void UIDetailsWidgetCloudNetwork::updateButtonStates()
+{
+// if (m_oldData != m_newData)
+// printf("Network: %s, %s, %s, %d, %d, %d\n",
+// m_newData.m_strName.toUtf8().constData(),
+// m_newData.m_strPrefixIPv4.toUtf8().constData(),
+// m_newData.m_strPrefixIPv6.toUtf8().constData(),
+// m_newData.m_fSupportsDHCP,
+// m_newData.m_fSupportsIPv6,
+// m_newData.m_fAdvertiseDefaultIPv6Route);
+
+ /* Update 'Apply' / 'Reset' button states: */
+ if (m_pButtonBoxOptions)
+ {
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setEnabled(m_oldData != m_newData);
+ }
+
+ /* Notify listeners as well: */
+ emit sigDataChanged(m_oldData != m_newData);
+}
+
+void UIDetailsWidgetCloudNetwork::retranslateUi()
+{
+ if (m_pLabelNetworkName)
+ m_pLabelNetworkName->setText(UINetworkManager::tr("N&ame:"));
+ if (m_pEditorNetworkName)
+ m_pEditorNetworkName->setToolTip(UINetworkManager::tr("Holds the name for this network."));
+ if (m_pLabelProviderName)
+ m_pLabelProviderName->setText(UINetworkManager::tr("&Provider:"));
+ if (m_pComboProviderName)
+ m_pComboProviderName->setToolTip(UINetworkManager::tr("Holds the cloud provider for this network."));
+ if (m_pLabelProfileName)
+ m_pLabelProfileName->setText(UINetworkManager::tr("P&rofile:"));
+ if (m_pComboProfileName)
+ m_pComboProfileName->setToolTip(UINetworkManager::tr("Holds the cloud profile for this network."));
+ if (m_pLabelNetworkId)
+ m_pLabelNetworkId->setText(UINetworkManager::tr("&Id:"));
+ if (m_pEditorNetworkId)
+ m_pEditorNetworkId->setToolTip(UINetworkManager::tr("Holds the id for this network."));
+ if (m_pButtonNetworkId)
+ m_pButtonNetworkId->setToolTip(UINetworkManager::tr("Selects the id for this network."));
+ if (m_pButtonBoxOptions)
+ {
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setText(UINetworkManager::tr("Reset"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setText(UINetworkManager::tr("Apply"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setStatusTip(UINetworkManager::tr("Reset changes in current "
+ "interface details"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setStatusTip(UINetworkManager::tr("Apply changes in current "
+ "interface details"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->
+ setToolTip(UINetworkManager::tr("Reset Changes (%1)").arg(m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->
+ setToolTip(UINetworkManager::tr("Apply Changes (%1)").arg(m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+}
+
+void UIDetailsWidgetCloudNetwork::sltNetworkNameChanged(const QString &strText)
+{
+ m_newData.m_strName = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetCloudNetwork::sltCloudProviderNameChanged(int iIndex)
+{
+ /* Store provider: */
+ m_newData.m_strProvider = m_pComboProviderName->itemData(iIndex).toString();
+
+ /* Update profiles: */
+ prepareProfiles();
+ /* And store selected profile: */
+ sltCloudProfileNameChanged(m_pComboProfileName->currentIndex());
+
+ /* Update button states finally: */
+ updateButtonStates();
+}
+
+void UIDetailsWidgetCloudNetwork::sltCloudProfileNameChanged(int iIndex)
+{
+ /* Store profile: */
+ m_newData.m_strProfile = m_pComboProfileName->itemData(iIndex).toString();
+
+ /* Update button states finally: */
+ updateButtonStates();
+}
+
+void UIDetailsWidgetCloudNetwork::sltNetworkIdChanged(const QString &strText)
+{
+ m_newData.m_strId = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetCloudNetwork::sltNetworkIdListRequested()
+{
+ /* Create subnet selection dialog: */
+ QPointer<UISubnetSelectionDialog> pDialog = new UISubnetSelectionDialog(this,
+ m_pComboProviderName->currentData().toString(),
+ m_pComboProfileName->currentData().toString(),
+ m_pEditorNetworkId->text());
+
+ /* Execute dialog to ask user for subnet: */
+ if (pDialog->exec() == QDialog::Accepted)
+ m_pEditorNetworkId->setText(pDialog->subnetId());
+
+ /* Cleanup subnet dialog finally: */
+ delete pDialog;
+}
+
+void UIDetailsWidgetCloudNetwork::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+ /* Make sure button-box exist: */
+ if (!m_pButtonBoxOptions)
+ return;
+
+ /* Disable buttons first of all: */
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if (pButton == m_pButtonBoxOptions->button(QDialogButtonBox::Cancel))
+ emit sigDataChangeRejected();
+ else
+ if (pButton == m_pButtonBoxOptions->button(QDialogButtonBox::Ok))
+ emit sigDataChangeAccepted();
+}
+
+void UIDetailsWidgetCloudNetwork::prepare()
+{
+ /* Prepare everything: */
+ prepareThis();
+ prepareProviders();
+ prepareProfiles();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Update button states finally: */
+ updateButtonStates();
+}
+
+void UIDetailsWidgetCloudNetwork::prepareThis()
+{
+ /* Prepare options widget layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+#ifdef VBOX_WS_MAC
+ pLayout->setSpacing(10);
+ pLayout->setContentsMargins(10, 10, 10, 10);
+#endif
+
+ /* Prepare network name label: */
+ m_pLabelNetworkName = new QLabel(this);
+ if (m_pLabelNetworkName)
+ {
+ m_pLabelNetworkName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelNetworkName, 0, 0);
+ }
+ /* Prepare network name editor: */
+ m_pEditorNetworkName = new QLineEdit(this);
+ if (m_pEditorNetworkName)
+ {
+ if (m_pLabelNetworkName)
+ m_pLabelNetworkName->setBuddy(m_pEditorNetworkName);
+ connect(m_pEditorNetworkName, &QLineEdit::textEdited,
+ this, &UIDetailsWidgetCloudNetwork::sltNetworkNameChanged);
+
+ pLayout->addWidget(m_pEditorNetworkName, 0, 1, 1, 2);
+ }
+
+ /* Prepare cloud provider name label: */
+ m_pLabelProviderName = new QLabel(this);
+ if (m_pLabelProviderName)
+ {
+ m_pLabelProviderName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelProviderName, 1, 0);
+ }
+ /* Prepare cloud provider name combo: */
+ m_pComboProviderName = new QComboBox(this);
+ if (m_pComboProviderName)
+ {
+ if (m_pLabelProviderName)
+ m_pLabelProviderName->setBuddy(m_pComboProviderName);
+ connect(m_pComboProviderName, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIDetailsWidgetCloudNetwork::sltCloudProviderNameChanged);
+
+ pLayout->addWidget(m_pComboProviderName, 1, 1, 1, 2);
+ }
+
+ /* Prepare cloud profile name label: */
+ m_pLabelProfileName = new QLabel(this);
+ if (m_pLabelProfileName)
+ {
+ m_pLabelProfileName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelProfileName, 2, 0);
+ }
+ /* Prepare cloud profile name combo: */
+ m_pComboProfileName = new QComboBox(this);
+ if (m_pComboProfileName)
+ {
+ if (m_pLabelProfileName)
+ m_pLabelProfileName->setBuddy(m_pComboProfileName);
+ connect(m_pComboProfileName, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIDetailsWidgetCloudNetwork::sltCloudProfileNameChanged);
+
+ pLayout->addWidget(m_pComboProfileName, 2, 1, 1, 2);
+ }
+
+ /* Prepare network id label: */
+ m_pLabelNetworkId = new QLabel(this);
+ if (m_pLabelNetworkId)
+ {
+ m_pLabelNetworkId->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelNetworkId, 3, 0);
+ }
+ /* Prepare network id editor: */
+ m_pEditorNetworkId = new QLineEdit(this);
+ if (m_pEditorNetworkId)
+ {
+ if (m_pLabelNetworkId)
+ m_pLabelNetworkId->setBuddy(m_pEditorNetworkId);
+ connect(m_pEditorNetworkId, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetCloudNetwork::sltNetworkIdChanged);
+
+ pLayout->addWidget(m_pEditorNetworkId, 3, 1);
+ }
+ /* Prepare network id button: */
+ m_pButtonNetworkId = new QIToolButton(this);
+ if (m_pButtonNetworkId)
+ {
+ m_pButtonNetworkId->setIcon(UIIconPool::iconSet(":/subnet_16px.png"));
+ connect(m_pButtonNetworkId, &QIToolButton::clicked,
+ this, &UIDetailsWidgetCloudNetwork::sltNetworkIdListRequested);
+
+ pLayout->addWidget(m_pButtonNetworkId, 3, 2);
+ }
+
+ /* If parent embedded into stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Prepare button-box: */
+ m_pButtonBoxOptions = new QIDialogButtonBox(this);
+ if (m_pButtonBoxOptions)
+ {
+ m_pButtonBoxOptions->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBoxOptions, &QIDialogButtonBox::clicked, this, &UIDetailsWidgetCloudNetwork::sltHandleButtonBoxClick);
+
+ pLayout->addWidget(m_pButtonBoxOptions, 4, 0, 1, 2);
+ }
+ }
+ }
+}
+
+void UIDetailsWidgetCloudNetwork::prepareProviders()
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(m_pComboProviderName);
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (m_pComboProviderName->currentIndex() != -1)
+ strOldData = m_pComboProviderName->currentData().toString();
+
+ /* Block signals while updating: */
+ m_pComboProviderName->blockSignals(true);
+
+ /* Clear combo initially: */
+ m_pComboProviderName->clear();
+
+ /* Add empty item: */
+ m_pComboProviderName->addItem("--");
+
+ /* Iterate through existing providers: */
+ foreach (const CCloudProvider &comProvider, listCloudProviders())
+ {
+ /* Skip if we have nothing to populate (file missing?): */
+ if (comProvider.isNull())
+ continue;
+ /* Acquire provider name: */
+ QString strProviderName;
+ if (!cloudProviderName(comProvider, strProviderName))
+ continue;
+ /* Acquire provider short name: */
+ QString strProviderShortName;
+ if (!cloudProviderShortName(comProvider, strProviderShortName))
+ continue;
+
+ /* Compose empty item, fill the data: */
+ m_pComboProviderName->addItem(strProviderName);
+ m_pComboProviderName->setItemData(m_pComboProviderName->count() - 1, strProviderShortName);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = m_pComboProviderName->findData(strOldData);
+ if ( iNewIndex == -1
+ && m_pComboProviderName->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ m_pComboProviderName->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ m_pComboProviderName->blockSignals(false);
+}
+
+void UIDetailsWidgetCloudNetwork::prepareProfiles()
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(m_pComboProfileName);
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (m_pComboProfileName->currentIndex() != -1)
+ strOldData = m_pComboProfileName->currentData().toString();
+
+ /* Block signals while updating: */
+ m_pComboProfileName->blockSignals(true);
+
+ /* Clear combo initially: */
+ m_pComboProfileName->clear();
+
+ /* Add empty item: */
+ m_pComboProfileName->addItem("--");
+
+ /* Acquire provider short name: */
+ const QString strProviderShortName = m_pComboProviderName->currentData().toString();
+ if (!strProviderShortName.isEmpty())
+ {
+ /* Acquire provider: */
+ CCloudProvider comProvider = cloudProviderByShortName(strProviderShortName);
+ if (comProvider.isNotNull())
+ {
+ /* Iterate through existing profiles: */
+ foreach (const CCloudProfile &comProfile, listCloudProfiles(comProvider))
+ {
+ /* Skip if we have nothing to populate (wtf happened?): */
+ if (comProfile.isNull())
+ continue;
+ /* Acquire current profile name: */
+ QString strProfileName;
+ if (!cloudProfileName(comProfile, strProfileName))
+ continue;
+
+ /* Compose item, fill the data: */
+ m_pComboProfileName->addItem(strProfileName);
+ m_pComboProfileName->setItemData(m_pComboProfileName->count() - 1, strProfileName);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = m_pComboProfileName->findData(strOldData);
+ if ( iNewIndex == -1
+ && m_pComboProfileName->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ m_pComboProfileName->setCurrentIndex(iNewIndex);
+ }
+ }
+
+ /* Unblock signals after update: */
+ m_pComboProfileName->blockSignals(false);
+}
+
+void UIDetailsWidgetCloudNetwork::loadData()
+{
+ /* Check whether network exists and enabled: */
+ const bool fIsNetworkExists = m_newData.m_fExists;
+
+ /* Update field availability: */
+ m_pLabelNetworkName->setEnabled(fIsNetworkExists);
+ m_pEditorNetworkName->setEnabled(fIsNetworkExists);
+ m_pLabelProviderName->setEnabled(fIsNetworkExists);
+ m_pComboProviderName->setEnabled(fIsNetworkExists);
+ m_pLabelProfileName->setEnabled(fIsNetworkExists);
+ m_pComboProfileName->setEnabled(fIsNetworkExists);
+ m_pLabelNetworkId->setEnabled(fIsNetworkExists);
+ m_pEditorNetworkId->setEnabled(fIsNetworkExists);
+ m_pButtonNetworkId->setEnabled(fIsNetworkExists);
+
+ /* Load fields: */
+ m_pEditorNetworkName->setText(m_newData.m_strName);
+ const int iProviderIndex = m_pComboProviderName->findData(m_newData.m_strProvider);
+ m_pComboProviderName->setCurrentIndex(iProviderIndex == -1 ? 0 : iProviderIndex);
+ const int iProfileIndex = m_pComboProfileName->findData(m_newData.m_strProfile);
+ m_pComboProfileName->setCurrentIndex(iProfileIndex == -1 ? 0 : iProfileIndex);
+ m_pEditorNetworkId->setText(m_newData.m_strId);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetCloudNetwork.h b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetCloudNetwork.h
new file mode 100644
index 00000000..70890bbb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetCloudNetwork.h
@@ -0,0 +1,303 @@
+/* $Id: UIDetailsWidgetCloudNetwork.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsWidgetCloudNetwork class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networkmanager_UIDetailsWidgetCloudNetwork_h
+#define FEQT_INCLUDED_SRC_networkmanager_UIDetailsWidgetCloudNetwork_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIPortForwardingTable.h"
+
+/* COM includes: */
+#include "CVirtualSystemDescription.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QCheckBox;
+class QComboBox;
+class QGridLayout;
+class QGroupBox;
+class QLabel;
+class QLineEdit;
+class QRadioButton;
+class QIDialogButtonBox;
+class QILineEdit;
+class QITabWidget;
+class QIToolButton;
+class UIFormEditorWidget;
+class UINotificationCenter;
+
+
+/** QDialog subclass for subnet selection functionality. */
+class UISubnetSelectionDialog : public QIWithRetranslateUI<QDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs dialog passing @a pParent to the base-class.
+ * @param strProviderShortName Brings the short provider name for cloud client being created.
+ * @param strProfileName Brings the profile name for cloud client being created.
+ * @param strSubnetId Brings the initial subnet ID to be cached. */
+ UISubnetSelectionDialog(QWidget *pParent,
+ const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QString &strSubnetId);
+ /** Destructs dialog. */
+ virtual ~UISubnetSelectionDialog() override final;
+
+ /** Returns cached subnet ID. */
+ QString subnetId() const { return m_strSubnetId; }
+
+public slots:
+
+ /** Accepts dialog. */
+ virtual void accept() override final;
+
+ /** Executes dialog. */
+ virtual int exec() override final;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() override final;
+
+private slots:
+
+ /** Performs dialog initialization. */
+ void sltInit();
+
+ /** Handles notification about subnet selection @a comForm being created. */
+ void sltHandleVSDFormCreated(const CVirtualSystemDescriptionForm &comForm);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the short provider name for cloud client being created. */
+ QString m_strProviderShortName;
+ /** Holds the profile name for cloud client being created. */
+ QString m_strProfileName;
+ /** Holds the cached subnet ID. */
+ QString m_strSubnetId;
+
+ /** Holds the virtual system description container. */
+ CVirtualSystemDescription m_comDescription;
+ /** Holds the virtual system description form. */
+ CVirtualSystemDescriptionForm m_comForm;
+
+ /** Holds the form editor instance. */
+ UIFormEditorWidget *m_pFormEditor;
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+
+ /** Holds the notification-center instance. */
+ UINotificationCenter *m_pNotificationCenter;
+};
+
+
+/** Network Manager: Cloud network data structure. */
+struct UIDataCloudNetwork
+{
+ /** Constructs data. */
+ UIDataCloudNetwork()
+ : m_fExists(false)
+ , m_fEnabled(true)
+ , m_strName(QString())
+ , m_strProvider(QString())
+ , m_strProfile(QString())
+ , m_strId(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataCloudNetwork &other) const
+ {
+ return true
+ && (m_fExists == other.m_fExists)
+ && (m_fEnabled == other.m_fEnabled)
+ && (m_strName == other.m_strName)
+ && (m_strProvider == other.m_strProvider)
+ && (m_strProfile == other.m_strProfile)
+ && (m_strId == other.m_strId)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataCloudNetwork &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataCloudNetwork &other) const { return !equal(other); }
+
+ /** Holds whether this network is not NULL. */
+ bool m_fExists;
+ /** Holds whether network is enabled. */
+ bool m_fEnabled;
+ /** Holds network name. */
+ QString m_strName;
+ /** Holds cloud provider name. */
+ QString m_strProvider;
+ /** Holds cloud profile name. */
+ QString m_strProfile;
+ /** Holds network id. */
+ QString m_strId;
+};
+
+
+/** Network Manager: Cloud network details-widget. */
+class UIDetailsWidgetCloudNetwork : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data changed and whether it @a fDiffers. */
+ void sigDataChanged(bool fDiffers);
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+public:
+
+ /** Constructs medium details dialog passing @a pParent to the base-class.
+ * @param enmEmbedding Brings embedding type. */
+ UIDetailsWidgetCloudNetwork(EmbedTo enmEmbedding, QWidget *pParent = 0);
+
+ /** Returns the host network data. */
+ const UIDataCloudNetwork &data() const { return m_newData; }
+ /** Defines the host network @a data.
+ * @param busyNames Holds the list of names busy by other
+ * Cloud networks. */
+ void setData(const UIDataCloudNetwork &data,
+ const QStringList &busyNames = QStringList());
+
+ /** @name Change handling stuff.
+ * @{ */
+ /** Revalidates changes. */
+ bool revalidate() const;
+
+ /** Updates button states. */
+ void updateButtonStates();
+ /** @} */
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** @name Change handling stuff.
+ * @{ */
+ /** Handles network name text change. */
+ void sltNetworkNameChanged(const QString &strText);
+ /** Handles cloud provider name index change. */
+ void sltCloudProviderNameChanged(int iIndex);
+ /** Handles cloud profile name index change. */
+ void sltCloudProfileNameChanged(int iIndex);
+ /** Handles network id text change. */
+ void sltNetworkIdChanged(const QString &strText);
+ /** Handles request to list possible network ids. */
+ void sltNetworkIdListRequested();
+
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares this. */
+ void prepareThis();
+ /** Prepares providers. */
+ void prepareProviders();
+ /** Prepares profiles. */
+ void prepareProfiles();
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+ /** Loads data. */
+ void loadData();
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the parent widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+
+ /** Holds the old data copy. */
+ UIDataCloudNetwork m_oldData;
+ /** Holds the new data copy. */
+ UIDataCloudNetwork m_newData;
+ /** @} */
+
+ /** @name Network variables.
+ * @{ */
+ /** Holds the network name label instance. */
+ QLabel *m_pLabelNetworkName;
+ /** Holds the network name editor instance. */
+ QLineEdit *m_pEditorNetworkName;
+ /** Holds the cloud provider name label instance. */
+ QLabel *m_pLabelProviderName;
+ /** Holds the cloud provider name combo instance. */
+ QComboBox *m_pComboProviderName;
+ /** Holds the cloud profile name label instance. */
+ QLabel *m_pLabelProfileName;
+ /** Holds the cloud profile name combo instance. */
+ QComboBox *m_pComboProfileName;
+ /** Holds the network id label instance. */
+ QLabel *m_pLabelNetworkId;
+ /** Holds the network id editor instance. */
+ QLineEdit *m_pEditorNetworkId;
+ /** Holds the network id list button instance. */
+ QIToolButton *m_pButtonNetworkId;
+
+ /** Holds the 'Options' button-box instance. */
+ QIDialogButtonBox *m_pButtonBoxOptions;
+ /** Holds the list of names busy by other
+ * NAT networks. */
+ QStringList m_busyNames;
+ /** @} */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_networkmanager_UIDetailsWidgetCloudNetwork_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetHostNetwork.cpp b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetHostNetwork.cpp
new file mode 100644
index 00000000..7c091bcd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetHostNetwork.cpp
@@ -0,0 +1,1193 @@
+/* $Id: UIDetailsWidgetHostNetwork.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsWidgetHostNetwork class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QStyleOption>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QILineEdit.h"
+#include "QITabWidget.h"
+#include "UIIconPool.h"
+#include "UIDetailsWidgetHostNetwork.h"
+#include "UIMessageCenter.h"
+#include "UINetworkManager.h"
+#include "UINetworkManagerUtils.h"
+#include "UINotificationCenter.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+#include "iprt/cidr.h"
+
+
+UIDetailsWidgetHostNetwork::UIDetailsWidgetHostNetwork(EmbedTo enmEmbedding, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+#ifdef VBOX_WS_MAC
+ , m_pLabelName(0)
+ , m_pEditorName(0)
+ , m_pLabelMask(0)
+ , m_pEditorMask(0)
+ , m_pLabelLBnd(0)
+ , m_pEditorLBnd(0)
+ , m_pLabelUBnd(0)
+ , m_pEditorUBnd(0)
+ , m_pButtonBox(0)
+#else /* !VBOX_WS_MAC */
+ , m_pTabWidget(0)
+ , m_pButtonAutomatic(0)
+ , m_pButtonManual(0)
+ , m_pLabelIPv4(0), m_pEditorIPv4(0)
+ , m_pLabelNMv4(0), m_pEditorNMv4(0)
+ , m_pLabelIPv6(0), m_pEditorIPv6(0)
+ , m_pLabelNMv6(0), m_pEditorNMv6(0)
+ , m_pButtonBoxInterface(0)
+ , m_pCheckBoxDHCP(0)
+ , m_pLabelDHCPAddress(0), m_pEditorDHCPAddress(0)
+ , m_pLabelDHCPMask(0), m_pEditorDHCPMask(0)
+ , m_pLabelDHCPLowerAddress(0), m_pEditorDHCPLowerAddress(0)
+ , m_pLabelDHCPUpperAddress(0), m_pEditorDHCPUpperAddress(0)
+ , m_pButtonBoxServer(0)
+#endif /* !VBOX_WS_MAC */
+{
+ prepare();
+}
+
+#ifdef VBOX_WS_MAC
+void UIDetailsWidgetHostNetwork::setData(const UIDataHostNetwork &data,
+ const QStringList &busyNames /* = QStringList() */)
+#else /* !VBOX_WS_MAC */
+void UIDetailsWidgetHostNetwork::setData(const UIDataHostNetwork &data)
+#endif /* !VBOX_WS_MAC */
+{
+ /* Cache old/new data: */
+ m_oldData = data;
+ m_newData = m_oldData;
+#ifdef VBOX_WS_MAC
+ m_busyNames = busyNames;
+#endif /* VBOX_WS_MAC */
+
+#ifdef VBOX_WS_MAC
+ /* Load data: */
+ loadData();
+#else /* !VBOX_WS_MAC */
+ /* Load 'Interface' data: */
+ loadDataForInterface();
+ /* Load 'DHCP server' data: */
+ loadDataForDHCPServer();
+#endif /* !VBOX_WS_MAC */
+}
+
+bool UIDetailsWidgetHostNetwork::revalidate() const
+{
+#ifdef VBOX_WS_MAC
+ /* Make sure network name isn't empty: */
+ if (m_newData.m_strName.isEmpty())
+ {
+ UINotificationMessage::warnAboutNoNameSpecified(m_oldData.m_strName);
+ return false;
+ }
+ else
+ {
+ /* Make sure item names are unique: */
+ if (m_busyNames.contains(m_newData.m_strName))
+ {
+ UINotificationMessage::warnAboutNameAlreadyBusy(m_newData.m_strName);
+ return false;
+ }
+ }
+
+ /* Make sure mask isn't empty: */
+ if (m_newData.m_strMask.isEmpty())
+ {
+ UINotificationMessage::warnAboutInvalidIPv4Mask(m_newData.m_strMask);
+ return false;
+ }
+ /* Make sure lower bound isn't empty: */
+ if (m_newData.m_strLBnd.isEmpty())
+ {
+ UINotificationMessage::warnAboutInvalidDHCPServerLowerAddress(m_newData.m_strLBnd);
+ return false;
+ }
+ /* Make sure upper bound isn't empty: */
+ if (m_newData.m_strUBnd.isEmpty())
+ {
+ UINotificationMessage::warnAboutInvalidDHCPServerUpperAddress(m_newData.m_strUBnd);
+ return false;
+ }
+
+#else /* !VBOX_WS_MAC */
+
+ /* Validate 'Interface' tab content: */
+ if ( m_newData.m_interface.m_fDHCPEnabled
+ && !m_newData.m_dhcpserver.m_fEnabled)
+ {
+ UINotificationMessage::warnAboutDHCPServerIsNotEnabled(m_newData.m_interface.m_strName);
+ return false;
+ }
+ if ( !m_newData.m_interface.m_fDHCPEnabled
+ && !m_newData.m_interface.m_strAddress.trimmed().isEmpty()
+ && ( !RTNetIsIPv4AddrStr(m_newData.m_interface.m_strAddress.toUtf8().constData())
+ || RTNetStrIsIPv4AddrAny(m_newData.m_interface.m_strAddress.toUtf8().constData())))
+ {
+ UINotificationMessage::warnAboutInvalidIPv4Address(m_newData.m_interface.m_strName);
+ return false;
+ }
+ if ( !m_newData.m_interface.m_fDHCPEnabled
+ && !m_newData.m_interface.m_strMask.trimmed().isEmpty()
+ && ( !RTNetIsIPv4AddrStr(m_newData.m_interface.m_strMask.toUtf8().constData())
+ || RTNetStrIsIPv4AddrAny(m_newData.m_interface.m_strMask.toUtf8().constData())))
+ {
+ UINotificationMessage::warnAboutInvalidIPv4Mask(m_newData.m_interface.m_strName);
+ return false;
+ }
+ if ( !m_newData.m_interface.m_fDHCPEnabled
+ && m_newData.m_interface.m_fSupportedIPv6
+ && !m_newData.m_interface.m_strAddress6.trimmed().isEmpty()
+ && ( !RTNetIsIPv6AddrStr(m_newData.m_interface.m_strAddress6.toUtf8().constData())
+ || RTNetStrIsIPv6AddrAny(m_newData.m_interface.m_strAddress6.toUtf8().constData())))
+ {
+ UINotificationMessage::warnAboutInvalidIPv6Address(m_newData.m_interface.m_strName);
+ return false;
+ }
+ bool fIsMaskPrefixLengthNumber = false;
+ const int iMaskPrefixLength = m_newData.m_interface.m_strPrefixLength6.trimmed().toInt(&fIsMaskPrefixLengthNumber);
+ if ( !m_newData.m_interface.m_fDHCPEnabled
+ && m_newData.m_interface.m_fSupportedIPv6
+ && ( !fIsMaskPrefixLengthNumber
+ || iMaskPrefixLength < 0
+ || iMaskPrefixLength > 128))
+ {
+ UINotificationMessage::warnAboutInvalidIPv6PrefixLength(m_newData.m_interface.m_strName);
+ return false;
+ }
+
+ /* Validate 'DHCP server' tab content: */
+ if ( m_newData.m_dhcpserver.m_fEnabled
+ && ( !RTNetIsIPv4AddrStr(m_newData.m_dhcpserver.m_strAddress.toUtf8().constData())
+ || RTNetStrIsIPv4AddrAny(m_newData.m_dhcpserver.m_strAddress.toUtf8().constData())))
+ {
+ UINotificationMessage::warnAboutInvalidDHCPServerAddress(m_newData.m_interface.m_strName);
+ return false;
+ }
+ if ( m_newData.m_dhcpserver.m_fEnabled
+ && ( !RTNetIsIPv4AddrStr(m_newData.m_dhcpserver.m_strMask.toUtf8().constData())
+ || RTNetStrIsIPv4AddrAny(m_newData.m_dhcpserver.m_strMask.toUtf8().constData())))
+ {
+ UINotificationMessage::warnAboutInvalidDHCPServerMask(m_newData.m_interface.m_strName);
+ return false;
+ }
+ if ( m_newData.m_dhcpserver.m_fEnabled
+ && ( !RTNetIsIPv4AddrStr(m_newData.m_dhcpserver.m_strLowerAddress.toUtf8().constData())
+ || RTNetStrIsIPv4AddrAny(m_newData.m_dhcpserver.m_strLowerAddress.toUtf8().constData())))
+ {
+ UINotificationMessage::warnAboutInvalidDHCPServerLowerAddress(m_newData.m_interface.m_strName);
+ return false;
+ }
+ if ( m_newData.m_dhcpserver.m_fEnabled
+ && ( !RTNetIsIPv4AddrStr(m_newData.m_dhcpserver.m_strUpperAddress.toUtf8().constData())
+ || RTNetStrIsIPv4AddrAny(m_newData.m_dhcpserver.m_strUpperAddress.toUtf8().constData())))
+ {
+ UINotificationMessage::warnAboutInvalidDHCPServerUpperAddress(m_newData.m_interface.m_strName);
+ return false;
+ }
+#endif /* !VBOX_WS_MAC */
+
+ /* True by default: */
+ return true;
+}
+
+void UIDetailsWidgetHostNetwork::updateButtonStates()
+{
+// if (m_oldData != m_newData)
+// printf("Interface: %s, %s, %s, %s; DHCP server: %d, %s, %s, %s, %s\n",
+// m_newData.m_interface.m_strAddress.toUtf8().constData(),
+// m_newData.m_interface.m_strMask.toUtf8().constData(),
+// m_newData.m_interface.m_strAddress6.toUtf8().constData(),
+// m_newData.m_interface.m_strPrefixLength6.toUtf8().constData(),
+// (int)m_newData.m_dhcpserver.m_fEnabled,
+// m_newData.m_dhcpserver.m_strAddress.toUtf8().constData(),
+// m_newData.m_dhcpserver.m_strMask.toUtf8().constData(),
+// m_newData.m_dhcpserver.m_strLowerAddress.toUtf8().constData(),
+// m_newData.m_dhcpserver.m_strUpperAddress.toUtf8().constData());
+
+#ifdef VBOX_WS_MAC
+ /* Update 'Apply' / 'Reset' button states: */
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(m_oldData != m_newData);
+ }
+#else /* !VBOX_WS_MAC */
+ /* Update 'Apply' / 'Reset' button states: */
+ if (m_pButtonBoxInterface)
+ {
+ m_pButtonBoxInterface->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
+ m_pButtonBoxInterface->button(QDialogButtonBox::Ok)->setEnabled(m_oldData != m_newData);
+ }
+ if (m_pButtonBoxServer)
+ {
+ m_pButtonBoxServer->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
+ m_pButtonBoxServer->button(QDialogButtonBox::Ok)->setEnabled(m_oldData != m_newData);
+ }
+#endif /* !VBOX_WS_MAC */
+
+ /* Notify listeners as well: */
+ emit sigDataChanged(m_oldData != m_newData);
+}
+
+void UIDetailsWidgetHostNetwork::retranslateUi()
+{
+#ifdef VBOX_WS_MAC
+ if (m_pLabelName)
+ m_pLabelName->setText(UINetworkManager::tr("&Name:"));
+ if (m_pEditorName)
+ m_pEditorName->setToolTip(UINetworkManager::tr("Holds the name for this network."));
+ if (m_pLabelMask)
+ m_pLabelMask->setText(UINetworkManager::tr("&Mask:"));
+ if (m_pEditorMask)
+ m_pEditorMask->setToolTip(UINetworkManager::tr("Holds the mask for this network."));
+ if (m_pLabelLBnd)
+ m_pLabelLBnd->setText(UINetworkManager::tr("&Lower Bound:"));
+ if (m_pEditorLBnd)
+ m_pEditorLBnd->setToolTip(UINetworkManager::tr("Holds the lower address bound for this network."));
+ if (m_pLabelUBnd)
+ m_pLabelUBnd->setText(UINetworkManager::tr("&Upper Bound:"));
+ if (m_pEditorUBnd)
+ m_pEditorUBnd->setToolTip(UINetworkManager::tr("Holds the upper address bound for this network."));
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setText(UINetworkManager::tr("Reset"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText(UINetworkManager::tr("Apply"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setStatusTip(UINetworkManager::tr("Reset changes in current network details"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(UINetworkManager::tr("Apply changes in current network details"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->
+ setToolTip(UINetworkManager::tr("Reset Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->
+ setToolTip(UINetworkManager::tr("Apply Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+
+#else /* !VBOX_WS_MAC */
+
+ /* Translate tab-widget: */
+ if (m_pTabWidget)
+ {
+ m_pTabWidget->setTabText(0, UINetworkManager::tr("&Adapter"));
+ m_pTabWidget->setTabText(1, UINetworkManager::tr("&DHCP Server"));
+ }
+
+ /* Translate 'Interface' tab content: */
+ if (m_pButtonAutomatic)
+ m_pButtonAutomatic->setText(UINetworkManager::tr("Configure Adapter &Automatically"));
+ if (m_pButtonManual)
+ m_pButtonManual->setText(UINetworkManager::tr("Configure Adapter &Manually"));
+ if (m_pLabelIPv4)
+ m_pLabelIPv4->setText(UINetworkManager::tr("&IPv4 Address:"));
+ if (m_pEditorIPv4)
+ m_pEditorIPv4->setToolTip(UINetworkManager::tr("Holds the host IPv4 address for this adapter."));
+ if (m_pLabelNMv4)
+ m_pLabelNMv4->setText(UINetworkManager::tr("IPv4 Network &Mask:"));
+ if (m_pEditorNMv4)
+ m_pEditorNMv4->setToolTip(UINetworkManager::tr("Holds the host IPv4 network mask for this adapter."));
+ if (m_pLabelIPv6)
+ m_pLabelIPv6->setText(UINetworkManager::tr("I&Pv6 Address:"));
+ if (m_pEditorIPv6)
+ m_pEditorIPv6->setToolTip(UINetworkManager::tr("Holds the host IPv6 address for this adapter if IPv6 is supported."));
+ if (m_pLabelNMv6)
+ m_pLabelNMv6->setText(UINetworkManager::tr("IPv6 Prefix &Length:"));
+ if (m_pEditorNMv6)
+ m_pEditorNMv6->setToolTip(UINetworkManager::tr("Holds the host IPv6 prefix length for this adapter if IPv6 is supported."));
+ if (m_pButtonBoxInterface)
+ {
+ m_pButtonBoxInterface->button(QDialogButtonBox::Cancel)->setText(UINetworkManager::tr("Reset"));
+ m_pButtonBoxInterface->button(QDialogButtonBox::Ok)->setText(UINetworkManager::tr("Apply"));
+ m_pButtonBoxInterface->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ m_pButtonBoxInterface->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBoxInterface->button(QDialogButtonBox::Cancel)->setStatusTip(UINetworkManager::tr("Reset changes in current interface details"));
+ m_pButtonBoxInterface->button(QDialogButtonBox::Ok)->setStatusTip(UINetworkManager::tr("Apply changes in current interface details"));
+ m_pButtonBoxInterface->button(QDialogButtonBox::Cancel)->
+ setToolTip(UINetworkManager::tr("Reset Changes (%1)").arg(m_pButtonBoxInterface->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+ m_pButtonBoxInterface->button(QDialogButtonBox::Ok)->
+ setToolTip(UINetworkManager::tr("Apply Changes (%1)").arg(m_pButtonBoxInterface->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+
+ /* Translate 'DHCP server' tab content: */
+ if (m_pCheckBoxDHCP)
+ {
+ m_pCheckBoxDHCP->setText(UINetworkManager::tr("&Enable Server"));
+ m_pCheckBoxDHCP->setToolTip(UINetworkManager::tr("When checked, the DHCP Server will be enabled for this network on machine start-up."));
+ }
+ if (m_pLabelDHCPAddress)
+ m_pLabelDHCPAddress->setText(UINetworkManager::tr("Server Add&ress:"));
+ if (m_pEditorDHCPAddress)
+ m_pEditorDHCPAddress->setToolTip(UINetworkManager::tr("Holds the address of the DHCP server servicing the network associated with this host-only adapter."));
+ if (m_pLabelDHCPMask)
+ m_pLabelDHCPMask->setText(UINetworkManager::tr("Server &Mask:"));
+ if (m_pEditorDHCPMask)
+ m_pEditorDHCPMask->setToolTip(UINetworkManager::tr("Holds the network mask of the DHCP server servicing the network associated with this host-only adapter."));
+ if (m_pLabelDHCPLowerAddress)
+ m_pLabelDHCPLowerAddress->setText(UINetworkManager::tr("&Lower Address Bound:"));
+ if (m_pEditorDHCPLowerAddress)
+ m_pEditorDHCPLowerAddress->setToolTip(UINetworkManager::tr("Holds the lower address bound offered by the DHCP server servicing the network associated with this host-only adapter."));
+ if (m_pLabelDHCPUpperAddress)
+ m_pLabelDHCPUpperAddress->setText(UINetworkManager::tr("&Upper Address Bound:"));
+ if (m_pEditorDHCPUpperAddress)
+ m_pEditorDHCPUpperAddress->setToolTip(UINetworkManager::tr("Holds the upper address bound offered by the DHCP server servicing the network associated with this host-only adapter."));
+ if (m_pButtonBoxServer)
+ {
+ m_pButtonBoxServer->button(QDialogButtonBox::Cancel)->setText(UINetworkManager::tr("Reset"));
+ m_pButtonBoxServer->button(QDialogButtonBox::Ok)->setText(UINetworkManager::tr("Apply"));
+ m_pButtonBoxServer->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ m_pButtonBoxServer->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBoxServer->button(QDialogButtonBox::Cancel)->setStatusTip(UINetworkManager::tr("Reset changes in current DHCP server details"));
+ m_pButtonBoxServer->button(QDialogButtonBox::Ok)->setStatusTip(UINetworkManager::tr("Apply changes in current DHCP server details"));
+ m_pButtonBoxServer->button(QDialogButtonBox::Cancel)->
+ setToolTip(UINetworkManager::tr("Reset Changes (%1)").arg(m_pButtonBoxServer->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+ m_pButtonBoxServer->button(QDialogButtonBox::Ok)->
+ setToolTip(UINetworkManager::tr("Apply Changes (%1)").arg(m_pButtonBoxServer->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+#endif /* !VBOX_WS_MAC */
+}
+
+#ifdef VBOX_WS_MAC
+void UIDetailsWidgetHostNetwork::sltTextChangedName(const QString &strText)
+{
+ m_newData.m_strName = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedMask(const QString &strText)
+{
+ m_newData.m_strMask = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedLBnd(const QString &strText)
+{
+ m_newData.m_strLBnd = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedUBnd(const QString &strText)
+{
+ m_newData.m_strUBnd = strText;
+ updateButtonStates();
+}
+
+#else /* !VBOX_WS_MAC */
+
+void UIDetailsWidgetHostNetwork::sltToggledButtonAutomatic(bool fChecked)
+{
+ m_newData.m_interface.m_fDHCPEnabled = fChecked;
+ loadDataForInterface();
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltToggledButtonManual(bool fChecked)
+{
+ m_newData.m_interface.m_fDHCPEnabled = !fChecked;
+ loadDataForInterface();
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedIPv4(const QString &strText)
+{
+ m_newData.m_interface.m_strAddress = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedNMv4(const QString &strText)
+{
+ m_newData.m_interface.m_strMask = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedIPv6(const QString &strText)
+{
+ m_newData.m_interface.m_strAddress6 = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedNMv6(const QString &strText)
+{
+ m_newData.m_interface.m_strPrefixLength6 = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltStatusChangedServer(int iChecked)
+{
+ m_newData.m_dhcpserver.m_fEnabled = (bool)iChecked;
+ loadDataForDHCPServer();
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedAddress(const QString &strText)
+{
+ m_newData.m_dhcpserver.m_strAddress = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedMask(const QString &strText)
+{
+ m_newData.m_dhcpserver.m_strMask = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedLowerAddress(const QString &strText)
+{
+ m_newData.m_dhcpserver.m_strLowerAddress = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::sltTextChangedUpperAddress(const QString &strText)
+{
+ m_newData.m_dhcpserver.m_strUpperAddress = strText;
+ updateButtonStates();
+}
+#endif /* !VBOX_WS_MAC */
+
+void UIDetailsWidgetHostNetwork::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+#ifdef VBOX_WS_MAC
+ /* Make sure button-box exists: */
+ if (!m_pButtonBox)
+ return;
+
+ /* Disable buttons first of all: */
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if ( pButton == m_pButtonBox->button(QDialogButtonBox::Cancel))
+ emit sigDataChangeRejected();
+ else
+ if ( pButton == m_pButtonBox->button(QDialogButtonBox::Ok))
+ emit sigDataChangeAccepted();
+
+#else /* !VBOX_WS_MAC */
+
+ /* Make sure button-boxes exist: */
+ if (!m_pButtonBoxInterface || !m_pButtonBoxServer)
+ return;
+
+ /* Disable buttons first of all: */
+ m_pButtonBoxInterface->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_pButtonBoxInterface->button(QDialogButtonBox::Ok)->setEnabled(false);
+ m_pButtonBoxServer->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_pButtonBoxServer->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if ( pButton == m_pButtonBoxInterface->button(QDialogButtonBox::Cancel)
+ || pButton == m_pButtonBoxServer->button(QDialogButtonBox::Cancel))
+ emit sigDataChangeRejected();
+ else
+ if ( pButton == m_pButtonBoxInterface->button(QDialogButtonBox::Ok)
+ || pButton == m_pButtonBoxServer->button(QDialogButtonBox::Ok))
+ emit sigDataChangeAccepted();
+#endif /* !VBOX_WS_MAC */
+}
+
+void UIDetailsWidgetHostNetwork::prepare()
+{
+ /* Prepare this: */
+ prepareThis();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Update button states finally: */
+ updateButtonStates();
+}
+
+void UIDetailsWidgetHostNetwork::prepareThis()
+{
+#ifdef VBOX_WS_MAC
+ /* Create layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ // really macOS only:
+ pLayout->setSpacing(10);
+ pLayout->setContentsMargins(10, 10, 10, 10);
+
+ /* Prepare options: */
+ prepareOptions();
+ }
+
+#else /* !VBOX_WS_MAC */
+
+ /* Create layout: */
+ new QVBoxLayout(this);
+ if (layout())
+ {
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare tab-widget: */
+ prepareTabWidget();
+ }
+#endif /* !VBOX_WS_MAC */
+}
+
+#ifdef VBOX_WS_MAC
+void UIDetailsWidgetHostNetwork::prepareOptions()
+{
+ /* Acquire layout: */
+ QGridLayout *pLayout = static_cast<QGridLayout*>(layout());
+ if (pLayout)
+ {
+ /* Prepare name label: */
+ m_pLabelName = new QLabel(this);
+ if (m_pLabelName)
+ {
+ m_pLabelName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelName, 0, 0);
+ }
+ /* Prepare name editor: */
+ m_pEditorName = new QILineEdit(this);
+ if (m_pEditorName)
+ {
+ m_pLabelName->setBuddy(m_pEditorName);
+ connect(m_pEditorName, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedName);
+
+ pLayout->addWidget(m_pEditorName, 0, 1);
+ }
+
+ /* Prepare mask label: */
+ m_pLabelMask = new QLabel(this);
+ if (m_pLabelMask)
+ {
+ m_pLabelMask->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelMask, 1, 0);
+ }
+ /* Prepare mask editor: */
+ m_pEditorMask = new QILineEdit(this);
+ if (m_pEditorMask)
+ {
+ m_pLabelMask->setBuddy(m_pEditorMask);
+ connect(m_pEditorMask, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedMask);
+
+ pLayout->addWidget(m_pEditorMask, 1, 1);
+ }
+
+ /* Prepare lower bound label: */
+ m_pLabelLBnd = new QLabel(this);
+ if (m_pLabelLBnd)
+ {
+ m_pLabelLBnd->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelLBnd, 2, 0);
+ }
+ /* Prepare lower bound editor: */
+ m_pEditorLBnd = new QILineEdit(this);
+ if (m_pEditorLBnd)
+ {
+ m_pLabelLBnd->setBuddy(m_pEditorLBnd);
+ connect(m_pEditorLBnd, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedLBnd);
+
+ pLayout->addWidget(m_pEditorLBnd, 2, 1);
+ }
+
+ /* Prepare upper bound label: */
+ m_pLabelUBnd = new QLabel(this);
+ if (m_pLabelUBnd)
+ {
+ m_pLabelUBnd->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelUBnd, 3, 0);
+ }
+ /* Prepare upper bound editor: */
+ m_pEditorUBnd = new QILineEdit(this);
+ if (m_pEditorUBnd)
+ {
+ m_pLabelUBnd->setBuddy(m_pEditorUBnd);
+ connect(m_pEditorUBnd, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedUBnd);
+
+ pLayout->addWidget(m_pEditorUBnd, 3, 1);
+ }
+
+ /* If parent embedded into stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox(this);
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBox, &QIDialogButtonBox::clicked, this, &UIDetailsWidgetHostNetwork::sltHandleButtonBoxClick);
+
+ pLayout->addWidget(m_pButtonBox, 4, 0, 1, 2);
+ }
+ }
+ }
+}
+
+#else /* !VBOX_WS_MAC */
+
+void UIDetailsWidgetHostNetwork::prepareTabWidget()
+{
+ /* Create tab-widget: */
+ m_pTabWidget = new QITabWidget(this);
+ if (m_pTabWidget)
+ {
+ /* Prepare 'Interface' tab: */
+ prepareTabInterface();
+ /* Prepare 'DHCP server' tab: */
+ prepareTabDHCPServer();
+
+ /* Add into layout: */
+ layout()->addWidget(m_pTabWidget);
+ }
+}
+
+void UIDetailsWidgetHostNetwork::prepareTabInterface()
+{
+ /* Prepare 'Interface' tab: */
+ QWidget *pTabInterface = new QWidget(m_pTabWidget);
+ if (pTabInterface)
+ {
+ /* Prepare 'Interface' layout: */
+ QGridLayout *pLayoutInterface = new QGridLayout(pTabInterface);
+ if (pLayoutInterface)
+ {
+#ifdef VBOX_WS_MAC
+ pLayoutInterface->setSpacing(10);
+ pLayoutInterface->setContentsMargins(10, 10, 10, 10);
+#endif
+
+ /* Prepare automatic interface configuration layout: */
+ QHBoxLayout *pLayoutAutomatic = new QHBoxLayout;
+ if (pLayoutAutomatic)
+ {
+ pLayoutAutomatic->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare automatic interface configuration radio-button: */
+ m_pButtonAutomatic = new QRadioButton(pTabInterface);
+ if (m_pButtonAutomatic)
+ {
+ connect(m_pButtonAutomatic, &QRadioButton::toggled,
+ this, &UIDetailsWidgetHostNetwork::sltToggledButtonAutomatic);
+ pLayoutAutomatic->addWidget(m_pButtonAutomatic);
+ }
+
+ pLayoutInterface->addLayout(pLayoutAutomatic, 0, 0, 1, 3);
+#ifdef VBOX_WS_MAC
+ pLayoutInterface->setRowMinimumHeight(0, 22);
+#endif
+ }
+
+ /* Prepare manual interface configuration layout: */
+ QHBoxLayout *pLayoutManual = new QHBoxLayout;
+ if (pLayoutManual)
+ {
+ pLayoutManual->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare manual interface configuration radio-button: */
+ m_pButtonManual = new QRadioButton(pTabInterface);
+ if (m_pButtonManual)
+ {
+ connect(m_pButtonManual, &QRadioButton::toggled,
+ this, &UIDetailsWidgetHostNetwork::sltToggledButtonManual);
+ pLayoutManual->addWidget(m_pButtonManual);
+ }
+
+ pLayoutInterface->addLayout(pLayoutManual, 1, 0, 1, 3);
+#ifdef VBOX_WS_MAC
+ pLayoutInterface->setRowMinimumHeight(1, 22);
+#endif
+ }
+
+ /* Prepare IPv4 address label: */
+ m_pLabelIPv4 = new QLabel(pTabInterface);
+ if (m_pLabelIPv4)
+ {
+ m_pLabelIPv4->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutInterface->addWidget(m_pLabelIPv4, 2, 1);
+ }
+ /* Prepare IPv4 layout: */
+ QHBoxLayout *pLayoutIPv4 = new QHBoxLayout;
+ if (pLayoutIPv4)
+ {
+ pLayoutIPv4->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare IPv4 address editor: */
+ m_pEditorIPv4 = new QILineEdit(pTabInterface);
+ if (m_pEditorIPv4)
+ {
+ m_pLabelIPv4->setBuddy(m_pEditorIPv4);
+ connect(m_pEditorIPv4, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedIPv4);
+
+ pLayoutIPv4->addWidget(m_pEditorIPv4);
+ }
+
+ pLayoutInterface->addLayout(pLayoutIPv4, 2, 2);
+ }
+
+ /* Prepare NMv4 network mask label: */
+ m_pLabelNMv4 = new QLabel(pTabInterface);
+ if (m_pLabelNMv4)
+ {
+ m_pLabelNMv4->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutInterface->addWidget(m_pLabelNMv4, 3, 1);
+ }
+ /* Prepare NMv4 layout: */
+ QHBoxLayout *pLayoutNMv4 = new QHBoxLayout;
+ if (pLayoutNMv4)
+ {
+ pLayoutNMv4->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare NMv4 network mask editor: */
+ m_pEditorNMv4 = new QILineEdit(pTabInterface);
+ if (m_pEditorNMv4)
+ {
+ m_pLabelNMv4->setBuddy(m_pEditorNMv4);
+ connect(m_pEditorNMv4, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedNMv4);
+
+ pLayoutNMv4->addWidget(m_pEditorNMv4);
+ }
+
+ pLayoutInterface->addLayout(pLayoutNMv4, 3, 2);
+ }
+
+ /* Prepare IPv6 address label: */
+ m_pLabelIPv6 = new QLabel(pTabInterface);
+ if (m_pLabelIPv6)
+ {
+ m_pLabelIPv6->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutInterface->addWidget(m_pLabelIPv6, 4, 1);
+ }
+ /* Prepare IPv6 layout: */
+ QHBoxLayout *pLayoutIPv6 = new QHBoxLayout;
+ if (pLayoutIPv6)
+ {
+ pLayoutIPv6->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare IPv6 address editor: */
+ m_pEditorIPv6 = new QILineEdit(pTabInterface);
+ if (m_pEditorIPv6)
+ {
+ m_pLabelIPv6->setBuddy(m_pEditorIPv6);
+ connect(m_pEditorIPv6, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedIPv6);
+
+ pLayoutIPv6->addWidget(m_pEditorIPv6);
+ }
+
+ pLayoutInterface->addLayout(pLayoutIPv6, 4, 2);
+ }
+
+ /* Prepare NMv6 network mask label: */
+ m_pLabelNMv6 = new QLabel(pTabInterface);
+ if (m_pLabelNMv6)
+ {
+ m_pLabelNMv6->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutInterface->addWidget(m_pLabelNMv6, 5, 1);
+ }
+ /* Prepare NMv6 layout: */
+ QHBoxLayout *pLayoutNMv6 = new QHBoxLayout;
+ if (pLayoutNMv6)
+ {
+ pLayoutNMv6->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare NMv6 network mask editor: */
+ m_pEditorNMv6 = new QILineEdit(pTabInterface);
+ if (m_pEditorNMv6)
+ {
+ m_pLabelNMv6->setBuddy(m_pEditorNMv6);
+ connect(m_pEditorNMv6, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedNMv6);
+
+ pLayoutNMv6->addWidget(m_pEditorNMv6);
+ }
+
+ pLayoutInterface->addLayout(pLayoutNMv6, 5, 2);
+ }
+
+ /* Prepare indent: */
+ QStyleOption options;
+ options.initFrom(m_pButtonManual);
+ const int iWidth = m_pButtonManual->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, &options, m_pButtonManual) +
+ m_pButtonManual->style()->pixelMetric(QStyle::PM_RadioButtonLabelSpacing, &options, m_pButtonManual) -
+ pLayoutInterface->spacing() - 1;
+ QSpacerItem *pSpacer1 = new QSpacerItem(iWidth, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
+ if (pSpacer1)
+ pLayoutInterface->addItem(pSpacer1, 2, 0, 4);
+ /* Prepare stretch: */
+ QSpacerItem *pSpacer2 = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
+ if (pSpacer2)
+ pLayoutInterface->addItem(pSpacer2, 6, 0, 1, 3);
+
+ /* If parent embedded into stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Prepare button-box: */
+ m_pButtonBoxInterface = new QIDialogButtonBox(pTabInterface);
+ if (m_pButtonBoxInterface)
+ {
+ m_pButtonBoxInterface->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBoxInterface, &QIDialogButtonBox::clicked, this, &UIDetailsWidgetHostNetwork::sltHandleButtonBoxClick);
+
+ pLayoutInterface->addWidget(m_pButtonBoxInterface, 7, 0, 1, 3);
+ }
+ }
+ }
+
+ m_pTabWidget->addTab(pTabInterface, QString());
+ }
+}
+
+void UIDetailsWidgetHostNetwork::prepareTabDHCPServer()
+{
+ /* Prepare 'DHCP server' tab: */
+ QWidget *pTabDHCPServer = new QWidget(m_pTabWidget);
+ if (pTabDHCPServer)
+ {
+ /* Prepare 'DHCP server' layout: */
+ QGridLayout *pLayoutDHCPServer = new QGridLayout(pTabDHCPServer);
+ if (pLayoutDHCPServer)
+ {
+#ifdef VBOX_WS_MAC
+ pLayoutDHCPServer->setSpacing(10);
+ pLayoutDHCPServer->setContentsMargins(10, 10, 10, 10);
+#endif
+
+ /* Prepare DHCP server status check-box: */
+ m_pCheckBoxDHCP = new QCheckBox(pTabDHCPServer);
+ if (m_pCheckBoxDHCP)
+ {
+ connect(m_pCheckBoxDHCP, &QCheckBox::stateChanged,
+ this, &UIDetailsWidgetHostNetwork::sltStatusChangedServer);
+ pLayoutDHCPServer->addWidget(m_pCheckBoxDHCP, 0, 0, 1, 2);
+#ifdef VBOX_WS_MAC
+ pLayoutDHCPServer->setRowMinimumHeight(0, 22);
+#endif
+ }
+
+ /* Prepare DHCP address label: */
+ m_pLabelDHCPAddress = new QLabel(pTabDHCPServer);
+ if (m_pLabelDHCPAddress)
+ {
+ m_pLabelDHCPAddress->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutDHCPServer->addWidget(m_pLabelDHCPAddress, 1, 1);
+ }
+ /* Prepare DHCP address layout: */
+ QHBoxLayout *pLayoutDHCPAddress = new QHBoxLayout;
+ if (pLayoutDHCPAddress)
+ {
+ pLayoutDHCPAddress->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare DHCP address editor: */
+ m_pEditorDHCPAddress = new QILineEdit(pTabDHCPServer);
+ if (m_pEditorDHCPAddress)
+ {
+ m_pLabelDHCPAddress->setBuddy(m_pEditorDHCPAddress);
+ connect(m_pEditorDHCPAddress, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedAddress);
+
+ pLayoutDHCPAddress->addWidget(m_pEditorDHCPAddress);
+ }
+
+ pLayoutDHCPServer->addLayout(pLayoutDHCPAddress, 1, 2);
+ }
+
+ /* Prepare DHCP network mask label: */
+ m_pLabelDHCPMask = new QLabel(pTabDHCPServer);
+ if (m_pLabelDHCPMask)
+ {
+ m_pLabelDHCPMask->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutDHCPServer->addWidget(m_pLabelDHCPMask, 2, 1);
+ }
+ /* Prepare DHCP mask layout: */
+ QHBoxLayout *pLayoutDHCPMask = new QHBoxLayout;
+ if (pLayoutDHCPMask)
+ {
+ pLayoutDHCPMask->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare DHCP network mask editor: */
+ m_pEditorDHCPMask = new QILineEdit(pTabDHCPServer);
+ if (m_pEditorDHCPMask)
+ {
+ m_pLabelDHCPMask->setBuddy(m_pEditorDHCPMask);
+ connect(m_pEditorDHCPMask, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedMask);
+
+ pLayoutDHCPMask->addWidget(m_pEditorDHCPMask);
+ }
+
+ pLayoutDHCPServer->addLayout(pLayoutDHCPMask, 2, 2);
+ }
+
+ /* Prepare DHCP lower address label: */
+ m_pLabelDHCPLowerAddress = new QLabel(pTabDHCPServer);
+ if (m_pLabelDHCPLowerAddress)
+ {
+ m_pLabelDHCPLowerAddress->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutDHCPServer->addWidget(m_pLabelDHCPLowerAddress, 3, 1);
+ }
+ /* Prepare DHCP lower address layout: */
+ QHBoxLayout *pLayoutDHCPLowerAddress = new QHBoxLayout;
+ if (pLayoutDHCPLowerAddress)
+ {
+ pLayoutDHCPLowerAddress->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare DHCP lower address editor: */
+ m_pEditorDHCPLowerAddress = new QILineEdit(pTabDHCPServer);
+ if (m_pEditorDHCPLowerAddress)
+ {
+ m_pLabelDHCPLowerAddress->setBuddy(m_pEditorDHCPLowerAddress);
+ connect(m_pEditorDHCPLowerAddress, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedLowerAddress);
+
+ pLayoutDHCPLowerAddress->addWidget(m_pEditorDHCPLowerAddress);
+ }
+
+ pLayoutDHCPServer->addLayout(pLayoutDHCPLowerAddress, 3, 2);
+ }
+
+ /* Prepare DHCP upper address label: */
+ m_pLabelDHCPUpperAddress = new QLabel(pTabDHCPServer);
+ if (m_pLabelDHCPUpperAddress)
+ {
+ m_pLabelDHCPUpperAddress->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutDHCPServer->addWidget(m_pLabelDHCPUpperAddress, 4, 1);
+ }
+ /* Prepare DHCP upper address layout: */
+ QHBoxLayout *pLayoutDHCPUpperAddress = new QHBoxLayout;
+ if (pLayoutDHCPUpperAddress)
+ {
+ pLayoutDHCPUpperAddress->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare DHCP upper address editor: */
+ m_pEditorDHCPUpperAddress = new QILineEdit(pTabDHCPServer);
+ if (m_pEditorDHCPUpperAddress)
+ {
+ m_pLabelDHCPUpperAddress->setBuddy(m_pEditorDHCPUpperAddress);
+ connect(m_pEditorDHCPUpperAddress, &QLineEdit::textChanged,
+ this, &UIDetailsWidgetHostNetwork::sltTextChangedUpperAddress);
+
+ pLayoutDHCPUpperAddress->addWidget(m_pEditorDHCPUpperAddress);
+ }
+
+ pLayoutDHCPServer->addLayout(pLayoutDHCPUpperAddress, 4, 2);
+ }
+
+ /* Prepare indent: */
+ QStyleOption options;
+ options.initFrom(m_pCheckBoxDHCP);
+ const int iWidth = m_pCheckBoxDHCP->style()->pixelMetric(QStyle::PM_IndicatorWidth, &options, m_pCheckBoxDHCP) +
+ m_pCheckBoxDHCP->style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &options, m_pCheckBoxDHCP) -
+ pLayoutDHCPServer->spacing() - 1;
+ QSpacerItem *pSpacer1 = new QSpacerItem(iWidth, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
+ if (pSpacer1)
+ pLayoutDHCPServer->addItem(pSpacer1, 1, 0, 4);
+ /* Prepare stretch: */
+ QSpacerItem *pSpacer2 = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
+ if (pSpacer2)
+ pLayoutDHCPServer->addItem(pSpacer2, 5, 0, 1, 3);
+
+ /* If parent embedded into stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Prepare button-box: */
+ m_pButtonBoxServer = new QIDialogButtonBox(pTabDHCPServer);
+ if (m_pButtonBoxServer)
+ {
+ m_pButtonBoxServer->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBoxServer, &QIDialogButtonBox::clicked, this, &UIDetailsWidgetHostNetwork::sltHandleButtonBoxClick);
+
+ pLayoutDHCPServer->addWidget(m_pButtonBoxServer, 6, 0, 1, 3);
+ }
+ }
+ }
+
+ m_pTabWidget->addTab(pTabDHCPServer, QString());
+ }
+}
+#endif /* !VBOX_WS_MAC */
+
+#ifdef VBOX_WS_MAC
+void UIDetailsWidgetHostNetwork::loadData()
+{
+ /* Check whether network exists and configurable: */
+ const bool fIsNetworkExists = m_newData.m_fExists;
+
+ /* Toggle network fields availability: */
+ if (m_pLabelName)
+ m_pLabelName->setEnabled(fIsNetworkExists);
+ if (m_pEditorName)
+ m_pEditorName->setEnabled(fIsNetworkExists);
+ if (m_pLabelMask)
+ m_pLabelMask->setEnabled(fIsNetworkExists);
+ if (m_pEditorMask)
+ m_pEditorMask->setEnabled(fIsNetworkExists);
+ if (m_pLabelLBnd)
+ m_pLabelLBnd->setEnabled(fIsNetworkExists);
+ if (m_pEditorLBnd)
+ m_pEditorLBnd->setEnabled(fIsNetworkExists);
+ if (m_pLabelUBnd)
+ m_pLabelUBnd->setEnabled(fIsNetworkExists);
+ if (m_pEditorUBnd)
+ m_pEditorUBnd->setEnabled(fIsNetworkExists);
+
+ /* Load network fields: */
+ if (m_pEditorName)
+ m_pEditorName->setText(m_newData.m_strName);
+ if (m_pEditorMask)
+ m_pEditorMask->setText(m_newData.m_strMask);
+ if (m_pEditorLBnd)
+ m_pEditorLBnd->setText(m_newData.m_strLBnd);
+ if (m_pEditorUBnd)
+ m_pEditorUBnd->setText(m_newData.m_strUBnd);
+}
+
+#else /* !VBOX_WS_MAC */
+
+void UIDetailsWidgetHostNetwork::loadDataForInterface()
+{
+ /* Check whether interface exists and configurable: */
+ const bool fIsInterfaceExists = m_newData.m_interface.m_fExists;
+ const bool fIsInterfaceConfigurable = !m_newData.m_interface.m_fDHCPEnabled;
+
+ /* Toggle radio-buttons availability: */
+ if (m_pButtonAutomatic)
+ m_pButtonAutomatic->setEnabled(fIsInterfaceExists);
+ if (m_pButtonManual)
+ m_pButtonManual->setEnabled(fIsInterfaceExists);
+
+ /* Toggle IPv4 & IPv6 interface fields availability: */
+ if (m_pLabelIPv4)
+ m_pLabelIPv4->setEnabled(fIsInterfaceExists && fIsInterfaceConfigurable);
+ if (m_pLabelNMv4)
+ m_pLabelNMv4->setEnabled(fIsInterfaceExists && fIsInterfaceConfigurable);
+ if (m_pEditorIPv4)
+ m_pEditorIPv4->setEnabled(fIsInterfaceExists && fIsInterfaceConfigurable);
+ if (m_pEditorNMv4)
+ m_pEditorNMv4->setEnabled(fIsInterfaceExists && fIsInterfaceConfigurable);
+
+ /* Load IPv4 interface fields: */
+ if (m_pButtonAutomatic)
+ m_pButtonAutomatic->setChecked(!fIsInterfaceConfigurable);
+ if (m_pButtonManual)
+ m_pButtonManual->setChecked(fIsInterfaceConfigurable);
+ if (m_pEditorIPv4)
+ m_pEditorIPv4->setText(m_newData.m_interface.m_strAddress);
+ if (m_pEditorNMv4)
+ m_pEditorNMv4->setText(m_newData.m_interface.m_strMask);
+
+ /* Toggle IPv6 interface fields availability: */
+ const bool fIsIpv6Configurable = fIsInterfaceConfigurable && m_newData.m_interface.m_fSupportedIPv6;
+ if (m_pLabelIPv6)
+ m_pLabelIPv6->setEnabled(fIsInterfaceExists && fIsIpv6Configurable);
+ if (m_pLabelNMv6)
+ m_pLabelNMv6->setEnabled(fIsInterfaceExists && fIsIpv6Configurable);
+ if (m_pEditorIPv6)
+ m_pEditorIPv6->setEnabled(fIsInterfaceExists && fIsIpv6Configurable);
+ if (m_pEditorNMv6)
+ m_pEditorNMv6->setEnabled(fIsInterfaceExists && fIsIpv6Configurable);
+
+ /* Load IPv6 interface fields: */
+ if (m_pEditorIPv6)
+ m_pEditorIPv6->setText(m_newData.m_interface.m_strAddress6);
+ if (m_pEditorNMv6)
+ m_pEditorNMv6->setText(m_newData.m_interface.m_strPrefixLength6);
+}
+
+void UIDetailsWidgetHostNetwork::loadDataForDHCPServer()
+{
+ /* Check whether interface exists and DHCP server available: */
+ const bool fIsInterfaceExists = m_newData.m_interface.m_fExists;
+ const bool fIsDHCPServerEnabled = m_newData.m_dhcpserver.m_fEnabled;
+
+ /* Toggle check-box availability: */
+ m_pCheckBoxDHCP->setEnabled(fIsInterfaceExists);
+
+ /* Toggle DHCP server fields availability: */
+ if (m_pLabelDHCPAddress)
+ m_pLabelDHCPAddress->setEnabled(fIsInterfaceExists && fIsDHCPServerEnabled);
+ if (m_pLabelDHCPMask)
+ m_pLabelDHCPMask->setEnabled(fIsInterfaceExists && fIsDHCPServerEnabled);
+ if (m_pLabelDHCPLowerAddress)
+ m_pLabelDHCPLowerAddress->setEnabled(fIsInterfaceExists && fIsDHCPServerEnabled);
+ if (m_pLabelDHCPUpperAddress)
+ m_pLabelDHCPUpperAddress->setEnabled(fIsInterfaceExists && fIsDHCPServerEnabled);
+ if (m_pEditorDHCPAddress)
+ m_pEditorDHCPAddress->setEnabled(fIsInterfaceExists && fIsDHCPServerEnabled);
+ if (m_pEditorDHCPMask)
+ m_pEditorDHCPMask->setEnabled(fIsInterfaceExists && fIsDHCPServerEnabled);
+ if (m_pEditorDHCPLowerAddress)
+ m_pEditorDHCPLowerAddress->setEnabled(fIsInterfaceExists && fIsDHCPServerEnabled);
+ if (m_pEditorDHCPUpperAddress)
+ m_pEditorDHCPUpperAddress->setEnabled(fIsInterfaceExists && fIsDHCPServerEnabled);
+
+ /* Load DHCP server fields: */
+ if (m_pCheckBoxDHCP)
+ m_pCheckBoxDHCP->setChecked(fIsDHCPServerEnabled);
+ if (m_pEditorDHCPAddress)
+ m_pEditorDHCPAddress->setText(m_newData.m_dhcpserver.m_strAddress);
+ if (m_pEditorDHCPMask)
+ m_pEditorDHCPMask->setText(m_newData.m_dhcpserver.m_strMask);
+ if (m_pEditorDHCPLowerAddress)
+ m_pEditorDHCPLowerAddress->setText(m_newData.m_dhcpserver.m_strLowerAddress);
+ if (m_pEditorDHCPUpperAddress)
+ m_pEditorDHCPUpperAddress->setText(m_newData.m_dhcpserver.m_strUpperAddress);
+
+ /* Invent default values if server was enabled
+ * but at least one current value is invalid: */
+ if ( fIsDHCPServerEnabled
+ && m_pEditorDHCPAddress
+ && m_pEditorDHCPMask
+ && m_pEditorDHCPLowerAddress
+ && m_pEditorDHCPUpperAddress
+ && ( m_pEditorDHCPAddress->text().isEmpty()
+ || m_pEditorDHCPAddress->text() == "0.0.0.0"
+ || m_pEditorDHCPMask->text().isEmpty()
+ || m_pEditorDHCPMask->text() == "0.0.0.0"
+ || m_pEditorDHCPLowerAddress->text().isEmpty()
+ || m_pEditorDHCPLowerAddress->text() == "0.0.0.0"
+ || m_pEditorDHCPUpperAddress->text().isEmpty()
+ || m_pEditorDHCPUpperAddress->text() == "0.0.0.0"))
+ {
+ const QStringList &proposal = makeDhcpServerProposal(m_oldData.m_interface.m_strAddress,
+ m_oldData.m_interface.m_strMask);
+ m_pEditorDHCPAddress->setText(proposal.at(0));
+ m_pEditorDHCPMask->setText(proposal.at(1));
+ m_pEditorDHCPLowerAddress->setText(proposal.at(2));
+ m_pEditorDHCPUpperAddress->setText(proposal.at(3));
+ }
+}
+#endif /* !VBOX_WS_MAC */
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetHostNetwork.h b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetHostNetwork.h
new file mode 100644
index 00000000..bab9fa37
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetHostNetwork.h
@@ -0,0 +1,458 @@
+/* $Id: UIDetailsWidgetHostNetwork.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsWidgetHostNetwork class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networkmanager_UIDetailsWidgetHostNetwork_h
+#define FEQT_INCLUDED_SRC_networkmanager_UIDetailsWidgetHostNetwork_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QCheckBox;
+class QLabel;
+class QRadioButton;
+class QIDialogButtonBox;
+class QILineEdit;
+class QITabWidget;
+
+
+#ifdef VBOX_WS_MAC
+/** Network Manager: Host network data structure. */
+struct UIDataHostNetwork
+{
+ /** Constructs data. */
+ UIDataHostNetwork()
+ : m_fExists(false)
+ , m_strName(QString())
+ , m_strMask(QString())
+ , m_strLBnd(QString())
+ , m_strUBnd(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataHostNetwork &other) const
+ {
+ return true
+ && (m_fExists == other.m_fExists)
+ && (m_strName == other.m_strName)
+ && (m_strMask == other.m_strMask)
+ && (m_strLBnd == other.m_strLBnd)
+ && (m_strUBnd == other.m_strUBnd)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataHostNetwork &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataHostNetwork &other) const { return !equal(other); }
+
+ /** Holds this interface is not NULL. */
+ bool m_fExists;
+ /** Holds network name. */
+ QString m_strName;
+ /** Holds network mask. */
+ QString m_strMask;
+ /** Holds lower bound. */
+ QString m_strLBnd;
+ /** Holds upper bound. */
+ QString m_strUBnd;
+};
+
+#else /* !VBOX_WS_MAC */
+
+/** Network Manager: Host Network Interface data structure. */
+struct UIDataHostNetworkInterface
+{
+ /** Constructs data. */
+ UIDataHostNetworkInterface()
+ : m_fExists(false)
+ , m_strName(QString())
+ , m_fDHCPEnabled(false)
+ , m_strAddress(QString())
+ , m_strMask(QString())
+ , m_fSupportedIPv6(false)
+ , m_strAddress6(QString())
+ , m_strPrefixLength6(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataHostNetworkInterface &other) const
+ {
+ return true
+ && (m_fExists == other.m_fExists)
+ && (m_strName == other.m_strName)
+ && (m_fDHCPEnabled == other.m_fDHCPEnabled)
+ && (m_strAddress == other.m_strAddress)
+ && (m_strMask == other.m_strMask)
+ && (m_fSupportedIPv6 == other.m_fSupportedIPv6)
+ && (m_strAddress6 == other.m_strAddress6)
+ && (m_strPrefixLength6 == other.m_strPrefixLength6)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataHostNetworkInterface &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataHostNetworkInterface &other) const { return !equal(other); }
+
+ /** Holds this interface is not NULL. */
+ bool m_fExists;
+ /** Holds interface name. */
+ QString m_strName;
+ /** Holds whether DHCP is enabled for that interface. */
+ bool m_fDHCPEnabled;
+ /** Holds IPv4 interface address. */
+ QString m_strAddress;
+ /** Holds IPv4 interface mask. */
+ QString m_strMask;
+ /** Holds whether IPv6 protocol supported. */
+ bool m_fSupportedIPv6;
+ /** Holds IPv6 interface address. */
+ QString m_strAddress6;
+ /** Holds IPv6 interface prefix length. */
+ QString m_strPrefixLength6;
+};
+
+/** Network Manager: DHCP Server data structure. */
+struct UIDataDHCPServer
+{
+ /** Constructs data. */
+ UIDataDHCPServer()
+ : m_fEnabled(false)
+ , m_strAddress(QString())
+ , m_strMask(QString())
+ , m_strLowerAddress(QString())
+ , m_strUpperAddress(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataDHCPServer &other) const
+ {
+ return true
+ && (m_fEnabled == other.m_fEnabled)
+ && (m_strAddress == other.m_strAddress)
+ && (m_strMask == other.m_strMask)
+ && (m_strLowerAddress == other.m_strLowerAddress)
+ && (m_strUpperAddress == other.m_strUpperAddress)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataDHCPServer &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataDHCPServer &other) const { return !equal(other); }
+
+ /** Holds whether DHCP server enabled. */
+ bool m_fEnabled;
+ /** Holds DHCP server address. */
+ QString m_strAddress;
+ /** Holds DHCP server mask. */
+ QString m_strMask;
+ /** Holds DHCP server lower address. */
+ QString m_strLowerAddress;
+ /** Holds DHCP server upper address. */
+ QString m_strUpperAddress;
+};
+
+/** Network Manager: Host network data structure. */
+struct UIDataHostNetwork
+{
+ /** Constructs data. */
+ UIDataHostNetwork()
+ : m_interface(UIDataHostNetworkInterface())
+ , m_dhcpserver(UIDataDHCPServer())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataHostNetwork &other) const
+ {
+ return true
+ && (m_interface == other.m_interface)
+ && (m_dhcpserver == other.m_dhcpserver)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataHostNetwork &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataHostNetwork &other) const { return !equal(other); }
+
+ /** Holds the interface data. */
+ UIDataHostNetworkInterface m_interface;
+ /** Holds the DHCP server data. */
+ UIDataDHCPServer m_dhcpserver;
+};
+#endif /* !VBOX_WS_MAC */
+
+
+/** Network Manager: Host network details-widget. */
+class UIDetailsWidgetHostNetwork : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data changed and whether it @a fDiffers. */
+ void sigDataChanged(bool fDiffers);
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+public:
+
+ /** Constructs medium details dialog passing @a pParent to the base-class.
+ * @param enmEmbedding Brings embedding type. */
+ UIDetailsWidgetHostNetwork(EmbedTo enmEmbedding, QWidget *pParent = 0);
+
+ /** Returns the host network data. */
+ const UIDataHostNetwork &data() const { return m_newData; }
+#ifdef VBOX_WS_MAC
+ /** Defines the host network @a data.
+ * @param busyNames Holds the list of names busy by other networks. */
+ void setData(const UIDataHostNetwork &data,
+ const QStringList &busyNames = QStringList());
+#else /* !VBOX_WS_MAC */
+ /** Defines the host network @a data. */
+ void setData(const UIDataHostNetwork &data);
+#endif /* !VBOX_WS_MAC */
+
+ /** @name Change handling stuff.
+ * @{ */
+ /** Revalidates changes for passed @a pWidget. */
+ bool revalidate() const;
+
+ /** Updates button states. */
+ void updateButtonStates();
+ /** @} */
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** @name Change handling stuff.
+ * @{ */
+#ifdef VBOX_WS_MAC
+ /** Handles network name text change. */
+ void sltTextChangedName(const QString &strText);
+ /** Handles network mask text change. */
+ void sltTextChangedMask(const QString &strText);
+ /** Handles network lower bound text change. */
+ void sltTextChangedLBnd(const QString &strText);
+ /** Handles network upper bound text change. */
+ void sltTextChangedUBnd(const QString &strText);
+
+#else /* !VBOX_WS_MAC */
+
+ /** Handles interface automatic configuration choice change. */
+ void sltToggledButtonAutomatic(bool fChecked);
+ /** Handles interface manual configuration choice change. */
+ void sltToggledButtonManual(bool fChecked);
+ /** Handles interface IPv4 text change. */
+ void sltTextChangedIPv4(const QString &strText);
+ /** Handles interface NMv4 text change. */
+ void sltTextChangedNMv4(const QString &strText);
+ /** Handles interface IPv6 text change. */
+ void sltTextChangedIPv6(const QString &strText);
+ /** Handles interface NMv6 text change. */
+ void sltTextChangedNMv6(const QString &strText);
+
+ /** Handles DHCP server status change. */
+ void sltStatusChangedServer(int iChecked);
+ /** Handles DHCP server address text change. */
+ void sltTextChangedAddress(const QString &strText);
+ /** Handles DHCP server mask text change. */
+ void sltTextChangedMask(const QString &strText);
+ /** Handles DHCP server lower address text change. */
+ void sltTextChangedLowerAddress(const QString &strText);
+ /** Handles DHCP server upper address text change. */
+ void sltTextChangedUpperAddress(const QString &strText);
+#endif /* !VBOX_WS_MAC */
+
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares this. */
+ void prepareThis();
+#ifdef VBOX_WS_MAC
+ /** Prepares options. */
+ void prepareOptions();
+#else /* !VBOX_WS_MAC */
+ /** Prepares tab-widget. */
+ void prepareTabWidget();
+ /** Prepares 'Interface' tab. */
+ void prepareTabInterface();
+ /** Prepares 'DHCP server' tab. */
+ void prepareTabDHCPServer();
+#endif /* !VBOX_WS_MAC */
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+#ifdef VBOX_WS_MAC
+ /** Loads data. */
+ void loadData();
+#else /* !VBOX_WS_MAC */
+ /** Loads interface data. */
+ void loadDataForInterface();
+ /** Loads server data. */
+ void loadDataForDHCPServer();
+#endif /* !VBOX_WS_MAC */
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the parent widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+
+ /** Holds the old data copy. */
+ UIDataHostNetwork m_oldData;
+ /** Holds the new data copy. */
+ UIDataHostNetwork m_newData;
+
+#ifndef VBOX_WS_MAC
+ /** Holds the tab-widget. */
+ QITabWidget *m_pTabWidget;
+#endif /* !VBOX_WS_MAC */
+ /** @} */
+
+#ifdef VBOX_WS_MAC
+ /** @name Network variables.
+ * @{ */
+ /** Holds the name label. */
+ QLabel *m_pLabelName;
+ /** Holds the name editor. */
+ QILineEdit *m_pEditorName;
+
+ /** Holds the mask label. */
+ QLabel *m_pLabelMask;
+ /** Holds the mask editor. */
+ QILineEdit *m_pEditorMask;
+
+ /** Holds the lower bound label. */
+ QLabel *m_pLabelLBnd;
+ /** Holds the lower bound editor. */
+ QILineEdit *m_pEditorLBnd;
+
+ /** Holds the upper bound label. */
+ QLabel *m_pLabelUBnd;
+ /** Holds the upper bound editor. */
+ QILineEdit *m_pEditorUBnd;
+
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+
+ /** Holds the list of names busy by other networks. */
+ QStringList m_busyNames;
+ /** @} */
+
+#else /* !VBOX_WS_MAC */
+
+ /** @name Interface variables.
+ * @{ */
+ /** Holds the automatic interface configuration button. */
+ QRadioButton *m_pButtonAutomatic;
+
+ /** Holds the manual interface configuration button. */
+ QRadioButton *m_pButtonManual;
+
+ /** Holds the IPv4 address label. */
+ QLabel *m_pLabelIPv4;
+ /** Holds the IPv4 address editor. */
+ QILineEdit *m_pEditorIPv4;
+
+ /** Holds the IPv4 network mask label. */
+ QLabel *m_pLabelNMv4;
+ /** Holds the IPv4 network mask editor. */
+ QILineEdit *m_pEditorNMv4;
+
+ /** Holds the IPv6 address label. */
+ QLabel *m_pLabelIPv6;
+ /** Holds the IPv6 address editor. */
+ QILineEdit *m_pEditorIPv6;
+
+ /** Holds the IPv6 network mask label. */
+ QLabel *m_pLabelNMv6;
+ /** Holds the IPv6 network mask editor. */
+ QILineEdit *m_pEditorNMv6;
+
+ /** Holds the interface button-box instance. */
+ QIDialogButtonBox *m_pButtonBoxInterface;
+ /** @} */
+
+ /** @name DHCP server variables.
+ * @{ */
+ /** Holds the DHCP server status chack-box. */
+ QCheckBox *m_pCheckBoxDHCP;
+
+ /** Holds the DHCP address label. */
+ QLabel *m_pLabelDHCPAddress;
+ /** Holds the DHCP address editor. */
+ QILineEdit *m_pEditorDHCPAddress;
+
+ /** Holds the DHCP network mask label. */
+ QLabel *m_pLabelDHCPMask;
+ /** Holds the DHCP network mask editor. */
+ QILineEdit *m_pEditorDHCPMask;
+
+ /** Holds the DHCP lower address label. */
+ QLabel *m_pLabelDHCPLowerAddress;
+ /** Holds the DHCP lower address editor. */
+ QILineEdit *m_pEditorDHCPLowerAddress;
+
+ /** Holds the DHCP upper address label. */
+ QLabel *m_pLabelDHCPUpperAddress;
+ /** Holds the DHCP upper address editor. */
+ QILineEdit *m_pEditorDHCPUpperAddress;
+
+ /** Holds the server button-box instance. */
+ QIDialogButtonBox *m_pButtonBoxServer;
+ /** @} */
+#endif /* !VBOX_WS_MAC */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networkmanager_UIDetailsWidgetHostNetwork_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetNATNetwork.cpp b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetNATNetwork.cpp
new file mode 100644
index 00000000..32aea6f5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetNATNetwork.cpp
@@ -0,0 +1,577 @@
+/* $Id: UIDetailsWidgetNATNetwork.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDetailsWidgetNATNetwork class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QFontMetrics>
+#include <QLabel>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QStyleOption>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QILineEdit.h"
+#include "QITabWidget.h"
+#include "UIIconPool.h"
+#include "UIDetailsWidgetNATNetwork.h"
+#include "UIMessageCenter.h"
+#include "UINetworkManager.h"
+#include "UINetworkManagerUtils.h"
+#include "UINotificationCenter.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+#include "iprt/cidr.h"
+
+
+UIDetailsWidgetNATNetwork::UIDetailsWidgetNATNetwork(EmbedTo enmEmbedding, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pTabWidget(0)
+ , m_pLabelNetworkName(0)
+ , m_pEditorNetworkName(0)
+ , m_pLabelNetworkIPv4Prefix(0)
+ , m_pEditorNetworkIPv4Prefix(0)
+ , m_pCheckboxSupportsDHCP(0)
+ , m_pCheckboxIPv6(0)
+ , m_pLabelNetworkIPv6Prefix(0)
+ , m_pEditorNetworkIPv6Prefix(0)
+ , m_pCheckboxAdvertiseDefaultIPv6Route(0)
+ , m_pButtonBoxOptions(0)
+ , m_pTabWidgetForwarding(0)
+ , m_pForwardingTableIPv4(0)
+ , m_pForwardingTableIPv6(0)
+ , m_pButtonBoxForwarding(0)
+ , m_fHoldPosition(false)
+{
+ prepare();
+}
+
+void UIDetailsWidgetNATNetwork::setData(const UIDataNATNetwork &data,
+ const QStringList &busyNames /* = QStringList() */,
+ bool fHoldPosition /* = false */)
+{
+ /* Cache old/new data: */
+ m_oldData = data;
+ m_newData = m_oldData;
+ m_busyNames = busyNames;
+ m_fHoldPosition = fHoldPosition;
+
+ /* Load 'Options' data: */
+ loadDataForOptions();
+ /* Load 'Forwarding' data: */
+ loadDataForForwarding();
+}
+
+bool UIDetailsWidgetNATNetwork::revalidate() const
+{
+ /* Make sure network name isn't empty: */
+ if (m_newData.m_strName.isEmpty())
+ {
+ UINotificationMessage::warnAboutNoNameSpecified(m_oldData.m_strName);
+ return false;
+ }
+ else
+ {
+ /* Make sure item names are unique: */
+ if (m_busyNames.contains(m_newData.m_strName))
+ {
+ UINotificationMessage::warnAboutNameAlreadyBusy(m_newData.m_strName);
+ return false;
+ }
+ }
+
+ /* Make sure IPv4 prefix isn't empty: */
+ if (m_newData.m_strPrefixIPv4.isEmpty())
+ {
+ UINotificationMessage::warnAboutNoIPv4PrefixSpecified(m_newData.m_strName);
+ return false;
+ }
+ /* Make sure IPv6 prefix isn't empty if IPv6 is supported: */
+ if (m_newData.m_fSupportsIPv6 && m_newData.m_strPrefixIPv6.isEmpty())
+ {
+ UINotificationMessage::warnAboutNoIPv6PrefixSpecified(m_newData.m_strName);
+ return false;
+ }
+
+ /* Validate 'Forwarding' tab content: */
+ return m_pForwardingTableIPv4->validate() && m_pForwardingTableIPv6->validate();
+}
+
+void UIDetailsWidgetNATNetwork::updateButtonStates()
+{
+// if (m_oldData != m_newData)
+// printf("Network: %s, %s, %s, %d, %d, %d\n",
+// m_newData.m_strName.toUtf8().constData(),
+// m_newData.m_strPrefixIPv4.toUtf8().constData(),
+// m_newData.m_strPrefixIPv6.toUtf8().constData(),
+// m_newData.m_fSupportsDHCP,
+// m_newData.m_fSupportsIPv6,
+// m_newData.m_fAdvertiseDefaultIPv6Route);
+
+ /* Update 'Apply' / 'Reset' button states: */
+ if (m_pButtonBoxOptions)
+ {
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setEnabled(m_oldData != m_newData);
+ }
+ if (m_pButtonBoxForwarding)
+ {
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Ok)->setEnabled(m_oldData != m_newData);
+ }
+
+ /* Notify listeners as well: */
+ emit sigDataChanged(m_oldData != m_newData);
+}
+
+void UIDetailsWidgetNATNetwork::retranslateUi()
+{
+ /* Translate tab-widget: */
+ if (m_pTabWidget)
+ {
+ m_pTabWidget->setTabText(0, UINetworkManager::tr("&General Options"));
+ m_pTabWidget->setTabText(1, UINetworkManager::tr("&Port Forwarding"));
+ }
+
+ if (m_pLabelNetworkName)
+ m_pLabelNetworkName->setText(UINetworkManager::tr("N&ame:"));
+ if (m_pEditorNetworkName)
+ m_pEditorNetworkName->setToolTip(UINetworkManager::tr("Holds the name for this network."));
+ if (m_pLabelNetworkIPv4Prefix)
+ m_pLabelNetworkIPv4Prefix->setText(UINetworkManager::tr("IPv&4 Prefix:"));
+ if (m_pEditorNetworkIPv4Prefix)
+ m_pEditorNetworkIPv4Prefix->setToolTip(UINetworkManager::tr("Holds the IPv4 prefix for this network."));
+ if (m_pLabelNetworkIPv6Prefix)
+ m_pLabelNetworkIPv6Prefix->setText(UINetworkManager::tr("IPv&6 Prefix:"));
+ if (m_pEditorNetworkIPv6Prefix)
+ m_pEditorNetworkIPv6Prefix->setToolTip(UINetworkManager::tr("Holds the IPv6 prefix for this network."));
+ if (m_pCheckboxSupportsDHCP)
+ {
+ m_pCheckboxSupportsDHCP->setText(UINetworkManager::tr("Enable &DHCP"));
+ m_pCheckboxSupportsDHCP->setToolTip(UINetworkManager::tr("When checked, this network will support DHCP."));
+ }
+ if (m_pCheckboxIPv6)
+ {
+ m_pCheckboxIPv6->setText(UINetworkManager::tr("&Enable IPv6"));
+ m_pCheckboxIPv6->setToolTip(UINetworkManager::tr("When checked, this network will support IPv6."));
+ }
+ if (m_pCheckboxAdvertiseDefaultIPv6Route)
+ {
+ m_pCheckboxAdvertiseDefaultIPv6Route->setText(UINetworkManager::tr("Advertise Default IPv6 &Route"));
+ m_pCheckboxAdvertiseDefaultIPv6Route->setToolTip(UINetworkManager::tr("When checked, this network will be advertised "
+ "as the default IPv6 route."));
+ }
+ if (m_pButtonBoxOptions)
+ {
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setText(UINetworkManager::tr("Reset"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setText(UINetworkManager::tr("Apply"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setStatusTip(UINetworkManager::tr("Reset changes in current "
+ "interface details"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setStatusTip(UINetworkManager::tr("Apply changes in current "
+ "interface details"));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->
+ setToolTip(UINetworkManager::tr("Reset Changes (%1)").arg(m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->
+ setToolTip(UINetworkManager::tr("Apply Changes (%1)").arg(m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+
+ /* Translate 'Forwarding' tab content: */
+ if (m_pTabWidgetForwarding)
+ {
+ m_pTabWidgetForwarding->setTabText(0, UINetworkManager::tr("IPv&4"));
+ m_pTabWidgetForwarding->setTabText(1, UINetworkManager::tr("IPv&6"));
+ }
+ if (m_pButtonBoxForwarding)
+ {
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Cancel)->setText(UINetworkManager::tr("Reset"));
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Ok)->setText(UINetworkManager::tr("Apply"));
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Cancel)->setStatusTip(UINetworkManager::tr("Reset changes in current "
+ "interface details"));
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Ok)->setStatusTip(UINetworkManager::tr("Apply changes in current "
+ "interface details"));
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Cancel)->
+ setToolTip(UINetworkManager::tr("Reset Changes (%1)").arg(m_pButtonBoxForwarding->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Ok)->
+ setToolTip(UINetworkManager::tr("Apply Changes (%1)").arg(m_pButtonBoxForwarding->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+}
+
+void UIDetailsWidgetNATNetwork::sltNetworkNameChanged(const QString &strText)
+{
+ m_newData.m_strName = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetNATNetwork::sltNetworkIPv4PrefixChanged(const QString &strText)
+{
+ m_newData.m_strPrefixIPv4 = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetNATNetwork::sltNetworkIPv6PrefixChanged(const QString &strText)
+{
+ m_newData.m_strPrefixIPv6 = strText;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetNATNetwork::sltSupportsDHCPChanged(bool fChecked)
+{
+ m_newData.m_fSupportsDHCP = fChecked;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetNATNetwork::sltSupportsIPv6Changed(bool fChecked)
+{
+ m_newData.m_fSupportsIPv6 = fChecked;
+ loadDataForOptions();
+ updateButtonStates();
+}
+
+void UIDetailsWidgetNATNetwork::sltAdvertiseDefaultIPv6RouteChanged(bool fChecked)
+{
+ m_newData.m_fAdvertiseDefaultIPv6Route = fChecked;
+ updateButtonStates();
+}
+
+void UIDetailsWidgetNATNetwork::sltForwardingRulesIPv4Changed()
+{
+ m_newData.m_rules4 = m_pForwardingTableIPv4->rules();
+ updateButtonStates();
+}
+
+void UIDetailsWidgetNATNetwork::sltForwardingRulesIPv6Changed()
+{
+ m_newData.m_rules6 = m_pForwardingTableIPv6->rules();
+ updateButtonStates();
+}
+
+void UIDetailsWidgetNATNetwork::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+ /* Make sure button-boxes exist: */
+ if (!m_pButtonBoxOptions || !m_pButtonBoxForwarding)
+ return;
+
+ /* Disable buttons first of all: */
+ m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_pButtonBoxOptions->button(QDialogButtonBox::Ok)->setEnabled(false);
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_pButtonBoxForwarding->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if ( pButton == m_pButtonBoxOptions->button(QDialogButtonBox::Cancel)
+ || pButton == m_pButtonBoxForwarding->button(QDialogButtonBox::Cancel))
+ emit sigDataChangeRejected();
+ else
+ if ( pButton == m_pButtonBoxOptions->button(QDialogButtonBox::Ok)
+ || pButton == m_pButtonBoxForwarding->button(QDialogButtonBox::Ok))
+ emit sigDataChangeAccepted();
+}
+
+void UIDetailsWidgetNATNetwork::prepare()
+{
+ /* Prepare this: */
+ prepareThis();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Update button states finally: */
+ updateButtonStates();
+}
+
+void UIDetailsWidgetNATNetwork::prepareThis()
+{
+ /* Create layout: */
+ new QVBoxLayout(this);
+ if (layout())
+ {
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare tab-widget: */
+ prepareTabWidget();
+ }
+}
+
+void UIDetailsWidgetNATNetwork::prepareTabWidget()
+{
+ /* Create tab-widget: */
+ m_pTabWidget = new QITabWidget(this);
+ if (m_pTabWidget)
+ {
+ /* Prepare 'Options' tab: */
+ prepareTabOptions();
+ /* Prepare 'Forwarding' tab: */
+ prepareTabForwarding();
+
+ /* Add into layout: */
+ layout()->addWidget(m_pTabWidget);
+ }
+}
+
+void UIDetailsWidgetNATNetwork::prepareTabOptions()
+{
+ /* Prepare 'Options' tab: */
+ QWidget *pTabOptions = new QWidget(m_pTabWidget);
+ if (pTabOptions)
+ {
+ /* Prepare 'Options' layout: */
+ QGridLayout *pLayoutOptions = new QGridLayout(pTabOptions);
+ if (pLayoutOptions)
+ {
+ pLayoutOptions->setColumnStretch(0, 0);
+ pLayoutOptions->setColumnStretch(1, 0);
+ pLayoutOptions->setColumnStretch(2, 1);
+ pLayoutOptions->setColumnStretch(3, 1);
+#ifdef VBOX_WS_MAC
+ pLayoutOptions->setSpacing(10);
+ pLayoutOptions->setContentsMargins(10, 10, 10, 10);
+#endif
+
+ /* Prepare network name label: */
+ m_pLabelNetworkName = new QLabel(pTabOptions);
+ if (m_pLabelNetworkName)
+ {
+ m_pLabelNetworkName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutOptions->addWidget(m_pLabelNetworkName, 0, 0, 1, 2);
+ }
+ /* Prepare network name editor: */
+ m_pEditorNetworkName = new QLineEdit(pTabOptions);
+ if (m_pEditorNetworkName)
+ {
+ if (m_pLabelNetworkName)
+ m_pLabelNetworkName->setBuddy(m_pEditorNetworkName);
+ connect(m_pEditorNetworkName, &QLineEdit::textEdited,
+ this, &UIDetailsWidgetNATNetwork::sltNetworkNameChanged);
+
+ pLayoutOptions->addWidget(m_pEditorNetworkName, 0, 2, 1, 2);
+ }
+
+ /* Prepare network IPv4 prefix label: */
+ m_pLabelNetworkIPv4Prefix = new QLabel(pTabOptions);
+ if (m_pLabelNetworkIPv4Prefix)
+ {
+ m_pLabelNetworkIPv4Prefix->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutOptions->addWidget(m_pLabelNetworkIPv4Prefix, 1, 0, 1, 2);
+ }
+ /* Prepare network IPv4 prefix editor: */
+ m_pEditorNetworkIPv4Prefix = new QLineEdit(pTabOptions);
+ if (m_pEditorNetworkIPv4Prefix)
+ {
+ if (m_pLabelNetworkIPv4Prefix)
+ m_pLabelNetworkIPv4Prefix->setBuddy(m_pEditorNetworkIPv4Prefix);
+ connect(m_pEditorNetworkIPv4Prefix, &QLineEdit::textEdited,
+ this, &UIDetailsWidgetNATNetwork::sltNetworkIPv4PrefixChanged);
+
+ pLayoutOptions->addWidget(m_pEditorNetworkIPv4Prefix, 1, 2, 1, 2);
+ }
+ /* Prepare 'supports DHCP' check-box: */
+ m_pCheckboxSupportsDHCP = new QCheckBox(pTabOptions);
+ if (m_pCheckboxSupportsDHCP)
+ {
+ connect(m_pCheckboxSupportsDHCP, &QCheckBox::toggled,
+ this, &UIDetailsWidgetNATNetwork::sltSupportsDHCPChanged);
+ pLayoutOptions->addWidget(m_pCheckboxSupportsDHCP, 2, 2);
+ }
+
+ /* Prepare IPv6 check-box: */
+ m_pCheckboxIPv6 = new QCheckBox(pTabOptions);
+ if (m_pCheckboxIPv6)
+ {
+ connect(m_pCheckboxIPv6, &QCheckBox::toggled,
+ this, &UIDetailsWidgetNATNetwork::sltSupportsIPv6Changed);
+ pLayoutOptions->addWidget(m_pCheckboxIPv6, 3, 0, 1, 3);
+ }
+ /* Prepare shifting spacer: */
+ QSpacerItem *pSpacer = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacer)
+ pLayoutOptions->addItem(pSpacer, 4, 0);
+ /* Prepare network IPv6 prefix label: */
+ m_pLabelNetworkIPv6Prefix = new QLabel(pTabOptions);
+ if (m_pLabelNetworkIPv6Prefix)
+ {
+ m_pLabelNetworkIPv6Prefix->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutOptions->addWidget(m_pLabelNetworkIPv6Prefix, 4, 1);
+ }
+ /* Prepare network IPv6 prefix editor: */
+ m_pEditorNetworkIPv6Prefix = new QLineEdit(pTabOptions);
+ if (m_pEditorNetworkIPv6Prefix)
+ {
+ if (m_pLabelNetworkIPv6Prefix)
+ m_pLabelNetworkIPv6Prefix->setBuddy(m_pEditorNetworkIPv6Prefix);
+ connect(m_pEditorNetworkIPv6Prefix, &QLineEdit::textEdited,
+ this, &UIDetailsWidgetNATNetwork::sltNetworkIPv6PrefixChanged);
+
+ pLayoutOptions->addWidget(m_pEditorNetworkIPv6Prefix, 4, 2, 1, 2);
+ }
+ /* Prepare 'advertise default IPv6 route' check-box: */
+ m_pCheckboxAdvertiseDefaultIPv6Route = new QCheckBox(pTabOptions);
+ if (m_pCheckboxAdvertiseDefaultIPv6Route)
+ {
+ connect(m_pCheckboxAdvertiseDefaultIPv6Route, &QCheckBox::toggled,
+ this, &UIDetailsWidgetNATNetwork::sltAdvertiseDefaultIPv6RouteChanged);
+ pLayoutOptions->addWidget(m_pCheckboxAdvertiseDefaultIPv6Route, 5, 2);
+ }
+
+ /* If parent embedded into stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Prepare button-box: */
+ m_pButtonBoxOptions = new QIDialogButtonBox(pTabOptions);
+ if (m_pButtonBoxOptions)
+ {
+ m_pButtonBoxOptions->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBoxOptions, &QIDialogButtonBox::clicked, this, &UIDetailsWidgetNATNetwork::sltHandleButtonBoxClick);
+
+ pLayoutOptions->addWidget(m_pButtonBoxOptions, 7, 0, 1, 4);
+ }
+ }
+ }
+
+ m_pTabWidget->addTab(pTabOptions, QString());
+ }
+}
+
+void UIDetailsWidgetNATNetwork::prepareTabForwarding()
+{
+ /* Prepare 'Forwarding' tab: */
+ QWidget *pTabForwarding = new QWidget(m_pTabWidget);
+ if (pTabForwarding)
+ {
+ /* Prepare 'Forwarding' layout: */
+ QGridLayout *pLayoutForwarding = new QGridLayout(pTabForwarding);
+ if (pLayoutForwarding)
+ {
+#ifdef VBOX_WS_MAC
+ /* Configure layout: */
+ pLayoutForwarding->setSpacing(10);
+ pLayoutForwarding->setContentsMargins(10, 10, 10, 10);
+#endif
+
+ /* Prepare forwarding tab-widget: */
+ m_pTabWidgetForwarding = new QITabWidget(pTabForwarding);
+ if (m_pTabWidgetForwarding)
+ {
+ /* Prepare IPv4 forwarding table: */
+ m_pForwardingTableIPv4 = new UIPortForwardingTable(UIPortForwardingDataList(),
+ false /* ip IPv6 protocol */,
+ false /* allow empty guest IPs */);
+ if (m_pForwardingTableIPv4)
+ {
+ connect(m_pForwardingTableIPv4, &UIPortForwardingTable::sigDataChanged,
+ this, &UIDetailsWidgetNATNetwork::sltForwardingRulesIPv4Changed);
+ m_pTabWidgetForwarding->addTab(m_pForwardingTableIPv4, QString());
+ }
+ /* Prepare IPv6 forwarding table: */
+ m_pForwardingTableIPv6 = new UIPortForwardingTable(UIPortForwardingDataList(),
+ true /* ip IPv6 protocol */,
+ false /* allow empty guest IPs */);
+ if (m_pForwardingTableIPv6)
+ {
+ connect(m_pForwardingTableIPv4, &UIPortForwardingTable::sigDataChanged,
+ this, &UIDetailsWidgetNATNetwork::sltForwardingRulesIPv6Changed);
+ m_pTabWidgetForwarding->addTab(m_pForwardingTableIPv6, QString());
+ }
+
+ pLayoutForwarding->addWidget(m_pTabWidgetForwarding, 0, 0);
+ }
+
+ /* If parent embedded into stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Prepare button-box: */
+ m_pButtonBoxForwarding = new QIDialogButtonBox(pTabForwarding);
+ if (m_pButtonBoxForwarding)
+ {
+ m_pButtonBoxForwarding->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(m_pButtonBoxForwarding, &QIDialogButtonBox::clicked, this, &UIDetailsWidgetNATNetwork::sltHandleButtonBoxClick);
+
+ pLayoutForwarding->addWidget(m_pButtonBoxForwarding, 1, 0);
+ }
+ }
+ }
+
+ m_pTabWidget->addTab(pTabForwarding, QString());
+ }
+}
+
+void UIDetailsWidgetNATNetwork::loadDataForOptions()
+{
+ /* Check whether network exists and enabled: */
+ const bool fIsNetworkExists = m_newData.m_fExists;
+ const bool fIsIPv6Supported = m_newData.m_fSupportsIPv6;
+
+ /* Update 'Options' field availability: */
+ m_pLabelNetworkName->setEnabled(fIsNetworkExists);
+ m_pEditorNetworkName->setEnabled(fIsNetworkExists);
+ m_pLabelNetworkIPv4Prefix->setEnabled(fIsNetworkExists);
+ m_pEditorNetworkIPv4Prefix->setEnabled(fIsNetworkExists);
+ m_pCheckboxSupportsDHCP->setEnabled(fIsNetworkExists);
+ m_pCheckboxIPv6->setEnabled(fIsNetworkExists);
+ m_pLabelNetworkIPv6Prefix->setEnabled(fIsNetworkExists && fIsIPv6Supported);
+ m_pEditorNetworkIPv6Prefix->setEnabled(fIsNetworkExists && fIsIPv6Supported);
+ m_pCheckboxAdvertiseDefaultIPv6Route->setEnabled(fIsNetworkExists && fIsIPv6Supported);
+
+ /* Load 'Options' fields: */
+ m_pEditorNetworkName->setText(m_newData.m_strName);
+ m_pEditorNetworkIPv4Prefix->setText(m_newData.m_strPrefixIPv4);
+ m_pCheckboxSupportsDHCP->setChecked(m_newData.m_fSupportsDHCP);
+ m_pCheckboxIPv6->setChecked(m_newData.m_fSupportsIPv6);
+ m_pEditorNetworkIPv6Prefix->setText(m_newData.m_strPrefixIPv6);
+ m_pCheckboxAdvertiseDefaultIPv6Route->setChecked(m_newData.m_fAdvertiseDefaultIPv6Route);
+}
+
+void UIDetailsWidgetNATNetwork::loadDataForForwarding()
+{
+ /* Check whether network exists and enabled: */
+ const bool fIsNetworkExists = m_newData.m_fExists;
+
+ /* Update 'Forwarding' field availability: */
+ m_pForwardingTableIPv4->setEnabled(fIsNetworkExists);
+ m_pForwardingTableIPv6->setEnabled(fIsNetworkExists);
+
+ /* Calculate/load guest address hints: */
+ char szTmpIp[16];
+ RTNETADDRIPV4 rtNetwork4;
+ int iPrefix4;
+ const int rc = RTNetStrToIPv4Cidr(m_newData.m_strPrefixIPv4.toUtf8().constData(), &rtNetwork4, &iPrefix4);
+ RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", rtNetwork4);
+ if (RT_SUCCESS(rc))
+ m_pForwardingTableIPv4->setGuestAddressHint(QString(szTmpIp));
+
+ /* Load 'Forwarding' fields: */
+ m_pForwardingTableIPv4->setRules(m_newData.m_rules4, m_fHoldPosition);
+ m_pForwardingTableIPv6->setRules(m_newData.m_rules6, m_fHoldPosition);
+ m_fHoldPosition = false;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetNATNetwork.h b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetNATNetwork.h
new file mode 100644
index 00000000..c948b607
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UIDetailsWidgetNATNetwork.h
@@ -0,0 +1,264 @@
+/* $Id: UIDetailsWidgetNATNetwork.h $ */
+/** @file
+ * VBox Qt GUI - UIDetailsWidgetNATNetwork class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networkmanager_UIDetailsWidgetNATNetwork_h
+#define FEQT_INCLUDED_SRC_networkmanager_UIDetailsWidgetNATNetwork_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIPortForwardingTable.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QCheckBox;
+class QLabel;
+class QLineEdit;
+class QRadioButton;
+class QIDialogButtonBox;
+class QILineEdit;
+class QITabWidget;
+
+
+/** Network Manager: NAT network data structure. */
+struct UIDataNATNetwork
+{
+ /** Constructs data. */
+ UIDataNATNetwork()
+ : m_fExists(false)
+ , m_strName(QString())
+ , m_strPrefixIPv4(QString())
+ , m_strPrefixIPv6(QString())
+ , m_fSupportsDHCP(false)
+ , m_fSupportsIPv6(false)
+ , m_fAdvertiseDefaultIPv6Route(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataNATNetwork &other) const
+ {
+ return true
+ && (m_fExists == other.m_fExists)
+ && (m_strName == other.m_strName)
+ && (m_strPrefixIPv4 == other.m_strPrefixIPv4)
+ && (m_strPrefixIPv6 == other.m_strPrefixIPv6)
+ && (m_fSupportsDHCP == other.m_fSupportsDHCP)
+ && (m_fSupportsIPv6 == other.m_fSupportsIPv6)
+ && (m_fAdvertiseDefaultIPv6Route == other.m_fAdvertiseDefaultIPv6Route)
+ && (m_rules4 == other.m_rules4)
+ && (m_rules6 == other.m_rules6)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataNATNetwork &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataNATNetwork &other) const { return !equal(other); }
+
+ /** Holds whether this network is not NULL. */
+ bool m_fExists;
+ /** Holds network name. */
+ QString m_strName;
+ /** Holds network IPv4 prefix. */
+ QString m_strPrefixIPv4;
+ /** Holds network IPv6 prefix. */
+ QString m_strPrefixIPv6;
+ /** Holds whether this network supports DHCP. */
+ bool m_fSupportsDHCP;
+ /** Holds whether this network supports IPv6. */
+ bool m_fSupportsIPv6;
+ /** Holds whether this network advertised as default IPv6 route. */
+ bool m_fAdvertiseDefaultIPv6Route;
+ /** Holds IPv4 port forwarding rules. */
+ UIPortForwardingDataList m_rules4;
+ /** Holds IPv6 port forwarding rules. */
+ UIPortForwardingDataList m_rules6;
+};
+
+
+/** Network Manager: NAT network details-widget. */
+class UIDetailsWidgetNATNetwork : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data changed and whether it @a fDiffers. */
+ void sigDataChanged(bool fDiffers);
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+public:
+
+ /** Constructs medium details dialog passing @a pParent to the base-class.
+ * @param enmEmbedding Brings embedding type. */
+ UIDetailsWidgetNATNetwork(EmbedTo enmEmbedding, QWidget *pParent = 0);
+
+ /** Returns the host network data. */
+ const UIDataNATNetwork &data() const { return m_newData; }
+ /** Defines the host network @a data.
+ * @param busyNames Holds the list of names busy by other
+ * NAT networks.
+ * @param fHoldPosition Holds whether we should try to keep
+ * port forwarding rule position intact. */
+ void setData(const UIDataNATNetwork &data,
+ const QStringList &busyNames = QStringList(),
+ bool fHoldPosition = false);
+
+ /** @name Change handling stuff.
+ * @{ */
+ /** Revalidates changes. */
+ bool revalidate() const;
+
+ /** Updates button states. */
+ void updateButtonStates();
+ /** @} */
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** @name Change handling stuff.
+ * @{ */
+ /** Handles network name text change. */
+ void sltNetworkNameChanged(const QString &strText);
+ /** Handles network IPv4 prefix text change. */
+ void sltNetworkIPv4PrefixChanged(const QString &strText);
+ /** Handles network IPv6 prefix text change. */
+ void sltNetworkIPv6PrefixChanged(const QString &strText);
+ /** Handles network supports DHCP choice change. */
+ void sltSupportsDHCPChanged(bool fChecked);
+ /** Handles network supports IPv6 choice change. */
+ void sltSupportsIPv6Changed(bool fChecked);
+ /** Handles network advertised as default IPv6 route choice change. */
+ void sltAdvertiseDefaultIPv6RouteChanged(bool fChecked);
+
+ /** Handles IPv4 forwarding rules table change. */
+ void sltForwardingRulesIPv4Changed();
+ /** Handles IPv6 forwarding rules table change. */
+ void sltForwardingRulesIPv6Changed();
+
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares this. */
+ void prepareThis();
+ /** Prepares tab-widget. */
+ void prepareTabWidget();
+ /** Prepares 'Options' tab. */
+ void prepareTabOptions();
+ /** Prepares 'Forwarding' tab. */
+ void prepareTabForwarding();
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+ /** Loads 'Options' data. */
+ void loadDataForOptions();
+ /** Loads 'Forwarding' data. */
+ void loadDataForForwarding();
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the parent widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+
+ /** Holds the old data copy. */
+ UIDataNATNetwork m_oldData;
+ /** Holds the new data copy. */
+ UIDataNATNetwork m_newData;
+
+ /** Holds the tab-widget. */
+ QITabWidget *m_pTabWidget;
+ /** @} */
+
+ /** @name Network variables.
+ * @{ */
+ /** Holds the network name label instance. */
+ QLabel *m_pLabelNetworkName;
+ /** Holds the network name editor instance. */
+ QLineEdit *m_pEditorNetworkName;
+ /** Holds the network IPv4 prefix label instance. */
+ QLabel *m_pLabelNetworkIPv4Prefix;
+ /** Holds the network IPv4 prefix editor instance. */
+ QLineEdit *m_pEditorNetworkIPv4Prefix;
+ /** Holds the 'supports DHCP' check-box instance. */
+ QCheckBox *m_pCheckboxSupportsDHCP;
+ /** Holds the IPv4 group-box instance. */
+ QCheckBox *m_pCheckboxIPv6;
+ /** Holds the network IPv6 prefix label instance. */
+ QLabel *m_pLabelNetworkIPv6Prefix;
+ /** Holds the network IPv6 prefix editor instance. */
+ QLineEdit *m_pEditorNetworkIPv6Prefix;
+ /** Holds the 'advertise default IPv6 route' check-box instance. */
+ QCheckBox *m_pCheckboxAdvertiseDefaultIPv6Route;
+ /** Holds the 'Options' button-box instance. */
+ QIDialogButtonBox *m_pButtonBoxOptions;
+ /** Holds the list of names busy by other
+ * NAT networks. */
+ QStringList m_busyNames;
+ /** @} */
+
+ /** @name Forwarding variables.
+ * @{ */
+ /** */
+ QITabWidget *m_pTabWidgetForwarding;
+ /** */
+ UIPortForwardingTable *m_pForwardingTableIPv4;
+ /** */
+ UIPortForwardingTable *m_pForwardingTableIPv6;
+ /** Holds the 'Forwarding' button-box instance. */
+ QIDialogButtonBox *m_pButtonBoxForwarding;
+ /** Holds whether we should try to keep
+ * port forwarding rule position intact. */
+ bool m_fHoldPosition;
+ /** @} */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_networkmanager_UIDetailsWidgetNATNetwork_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManager.cpp b/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManager.cpp
new file mode 100644
index 00000000..1594c1fd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManager.cpp
@@ -0,0 +1,2655 @@
+/* $Id: UINetworkManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UINetworkManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QMenuBar>
+#include <QPushButton>
+#include <QRegExp>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QITabWidget.h"
+#include "QITreeWidget.h"
+#include "UIActionPoolManager.h"
+#include "UIConverter.h"
+#include "UIDetailsWidgetCloudNetwork.h"
+#include "UIDetailsWidgetHostNetwork.h"
+#include "UIDetailsWidgetNATNetwork.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINetworkManager.h"
+#include "UINetworkManagerUtils.h"
+#include "UINotificationCenter.h"
+#include "QIToolBar.h"
+#ifdef VBOX_WS_MAC
+# include "UIWindowMenuManager.h"
+#endif
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CCloudNetwork.h"
+#include "CDHCPServer.h"
+#include "CHost.h"
+#ifdef VBOX_WS_MAC
+# include "CHostOnlyNetwork.h"
+#else
+# include "CHostNetworkInterface.h"
+#endif
+#include "CNATNetwork.h"
+
+/* Other VBox includes: */
+#include <iprt/cidr.h>
+
+
+/** Tab-widget indexes. */
+enum TabWidgetIndex
+{
+ TabWidgetIndex_HostNetwork,
+ TabWidgetIndex_NATNetwork,
+ TabWidgetIndex_CloudNetwork,
+};
+
+
+#ifdef VBOX_WS_MAC
+/** Host network tree-widget column indexes. */
+enum HostNetworkColumn
+{
+ HostNetworkColumn_Name,
+ HostNetworkColumn_Mask,
+ HostNetworkColumn_LBnd,
+ HostNetworkColumn_UBnd,
+ HostNetworkColumn_Max,
+};
+
+#else /* !VBOX_WS_MAC */
+
+/** Host network tree-widget column indexes. */
+enum HostNetworkColumn
+{
+ HostNetworkColumn_Name,
+ HostNetworkColumn_IPv4,
+ HostNetworkColumn_IPv6,
+ HostNetworkColumn_DHCP,
+ HostNetworkColumn_Max,
+};
+#endif /* !VBOX_WS_MAC */
+
+
+/** NAT network tree-widget column indexes. */
+enum NATNetworkColumn
+{
+ NATNetworkColumn_Name,
+ NATNetworkColumn_IPv4,
+ NATNetworkColumn_IPv6,
+ NATNetworkColumn_DHCP,
+ NATNetworkColumn_Max,
+};
+
+
+/** Cloud network tree-widget column indexes. */
+enum CloudNetworkColumn
+{
+ CloudNetworkColumn_Name,
+ CloudNetworkColumn_Provider,
+ CloudNetworkColumn_Profile,
+ CloudNetworkColumn_Max,
+};
+
+
+/** Network Manager: Host Network tree-widget item. */
+class UIItemHostNetwork : public QITreeWidgetItem, public UIDataHostNetwork
+{
+ Q_OBJECT;
+
+public:
+
+ /** Updates item fields from data. */
+ void updateFields();
+
+#ifdef VBOX_WS_MAC
+ /** Returns item name. */
+ QString name() const { return m_strName; }
+#else
+ /** Returns item name. */
+ QString name() const { return m_interface.m_strName; }
+#endif
+
+private:
+
+#ifndef VBOX_WS_MAC
+ /** Returns CIDR for a passed @a strMask. */
+ static int maskToCidr(const QString &strMask);
+#endif
+};
+
+
+/** Network Manager: NAT Network tree-widget item. */
+class UIItemNATNetwork : public QITreeWidgetItem, public UIDataNATNetwork
+{
+ Q_OBJECT;
+
+public:
+
+ /** Updates item fields from data. */
+ void updateFields();
+
+ /** Returns item name. */
+ QString name() const { return m_strName; }
+};
+
+
+/** Network Manager: Cloud Network tree-widget item. */
+class UIItemCloudNetwork : public QITreeWidgetItem, public UIDataCloudNetwork
+{
+ Q_OBJECT;
+
+public:
+
+ /** Updates item fields from data. */
+ void updateFields();
+
+ /** Returns item name. */
+ QString name() const { return m_strName; }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIItemHostNetwork implementation. *
+*********************************************************************************************************************************/
+
+void UIItemHostNetwork::updateFields()
+{
+#ifdef VBOX_WS_MAC
+ /* Compose item fields: */
+ setText(HostNetworkColumn_Name, m_strName);
+ setText(HostNetworkColumn_Mask, m_strMask);
+ setText(HostNetworkColumn_LBnd, m_strLBnd);
+ setText(HostNetworkColumn_UBnd, m_strUBnd);
+
+ /* Compose item tool-tip: */
+ const QString strTable("<table cellspacing=5>%1</table>");
+ const QString strHeader("<tr><td><nobr>%1:&nbsp;</nobr></td><td><nobr>%2</nobr></td></tr>");
+ QString strToolTip;
+
+ /* Network information: */
+ strToolTip += strHeader.arg(UINetworkManager::tr("Name"), m_strName);
+ strToolTip += strHeader.arg(UINetworkManager::tr("Mask"), m_strMask);
+ strToolTip += strHeader.arg(UINetworkManager::tr("Lower Bound"), m_strLBnd);
+ strToolTip += strHeader.arg(UINetworkManager::tr("Upper Bound"), m_strUBnd);
+
+#else /* !VBOX_WS_MAC */
+
+ /* Compose item fields: */
+ setText(HostNetworkColumn_Name, m_interface.m_strName);
+ setText(HostNetworkColumn_IPv4, m_interface.m_strAddress.isEmpty() ? QString() :
+ QString("%1/%2").arg(m_interface.m_strAddress).arg(maskToCidr(m_interface.m_strMask)));
+ setText(HostNetworkColumn_IPv6, m_interface.m_strAddress6.isEmpty() || !m_interface.m_fSupportedIPv6 ? QString() :
+ QString("%1/%2").arg(m_interface.m_strAddress6).arg(m_interface.m_strPrefixLength6.toInt()));
+ setText(HostNetworkColumn_DHCP, m_dhcpserver.m_fEnabled ? UINetworkManager::tr("Enabled", "DHCP Server")
+ : UINetworkManager::tr("Disabled", "DHCP Server"));
+
+ /* Compose item tool-tip: */
+ const QString strTable("<table cellspacing=5>%1</table>");
+ const QString strHeader("<tr><td><nobr>%1:&nbsp;</nobr></td><td><nobr>%2</nobr></td></tr>");
+ const QString strSubHeader("<tr><td><nobr>&nbsp;&nbsp;%1:&nbsp;</nobr></td><td><nobr>%2</nobr></td></tr>");
+ QString strToolTip;
+
+ /* Interface information: */
+ strToolTip += strHeader.arg(UINetworkManager::tr("Adapter"))
+ .arg(m_interface.m_fDHCPEnabled ?
+ UINetworkManager::tr("Automatically configured", "interface") :
+ UINetworkManager::tr("Manually configured", "interface"));
+ strToolTip += strSubHeader.arg(UINetworkManager::tr("IPv4 Address"))
+ .arg(m_interface.m_strAddress.isEmpty() ?
+ UINetworkManager::tr ("Not set", "address") :
+ m_interface.m_strAddress) +
+ strSubHeader.arg(UINetworkManager::tr("IPv4 Network Mask"))
+ .arg(m_interface.m_strMask.isEmpty() ?
+ UINetworkManager::tr ("Not set", "mask") :
+ m_interface.m_strMask);
+ if (m_interface.m_fSupportedIPv6)
+ {
+ strToolTip += strSubHeader.arg(UINetworkManager::tr("IPv6 Address"))
+ .arg(m_interface.m_strAddress6.isEmpty() ?
+ UINetworkManager::tr("Not set", "address") :
+ m_interface.m_strAddress6) +
+ strSubHeader.arg(UINetworkManager::tr("IPv6 Prefix Length"))
+ .arg(m_interface.m_strPrefixLength6.isEmpty() ?
+ UINetworkManager::tr("Not set", "length") :
+ m_interface.m_strPrefixLength6);
+ }
+
+ /* DHCP server information: */
+ strToolTip += strHeader.arg(UINetworkManager::tr("DHCP Server"))
+ .arg(m_dhcpserver.m_fEnabled ?
+ UINetworkManager::tr("Enabled", "server") :
+ UINetworkManager::tr("Disabled", "server"));
+ if (m_dhcpserver.m_fEnabled)
+ {
+ strToolTip += strSubHeader.arg(UINetworkManager::tr("Address"))
+ .arg(m_dhcpserver.m_strAddress.isEmpty() ?
+ UINetworkManager::tr("Not set", "address") :
+ m_dhcpserver.m_strAddress) +
+ strSubHeader.arg(UINetworkManager::tr("Network Mask"))
+ .arg(m_dhcpserver.m_strMask.isEmpty() ?
+ UINetworkManager::tr("Not set", "mask") :
+ m_dhcpserver.m_strMask) +
+ strSubHeader.arg(UINetworkManager::tr("Lower Bound"))
+ .arg(m_dhcpserver.m_strLowerAddress.isEmpty() ?
+ UINetworkManager::tr("Not set", "bound") :
+ m_dhcpserver.m_strLowerAddress) +
+ strSubHeader.arg(UINetworkManager::tr("Upper Bound"))
+ .arg(m_dhcpserver.m_strUpperAddress.isEmpty() ?
+ UINetworkManager::tr("Not set", "bound") :
+ m_dhcpserver.m_strUpperAddress);
+ }
+#endif /* !VBOX_WS_MAC */
+
+ /* Assign tool-tip finally: */
+ setToolTip(HostNetworkColumn_Name, strTable.arg(strToolTip));
+}
+
+#ifndef VBOX_WS_MAC
+/* static */
+int UIItemHostNetwork::maskToCidr(const QString &strMask)
+{
+ /* Parse passed mask: */
+ QList<int> address;
+ foreach (const QString &strValue, strMask.split('.'))
+ address << strValue.toInt();
+
+ /* Calculate CIDR: */
+ int iCidr = 0;
+ for (int i = 0; i < 4 || i < address.size(); ++i)
+ {
+ switch(address.at(i))
+ {
+ case 0x80: iCidr += 1; break;
+ case 0xC0: iCidr += 2; break;
+ case 0xE0: iCidr += 3; break;
+ case 0xF0: iCidr += 4; break;
+ case 0xF8: iCidr += 5; break;
+ case 0xFC: iCidr += 6; break;
+ case 0xFE: iCidr += 7; break;
+ case 0xFF: iCidr += 8; break;
+ /* Return CIDR prematurelly: */
+ default: return iCidr;
+ }
+ }
+
+ /* Return CIDR: */
+ return iCidr;
+}
+#endif /* !VBOX_WS_MAC */
+
+
+/*********************************************************************************************************************************
+* Class UIItemNATNetwork implementation. *
+*********************************************************************************************************************************/
+
+void UIItemNATNetwork::updateFields()
+{
+ /* Compose item fields: */
+ setText(NATNetworkColumn_Name, m_strName);
+ setText(NATNetworkColumn_IPv4, m_strPrefixIPv4);
+ setText(NATNetworkColumn_IPv6, m_strPrefixIPv6);
+ setText(NATNetworkColumn_DHCP, m_fSupportsDHCP ? UINetworkManager::tr("Enabled", "DHCP Server")
+ : UINetworkManager::tr("Disabled", "DHCP Server"));
+
+ /* Compose item tool-tip: */
+ const QString strTable("<table cellspacing=5>%1</table>");
+ const QString strHeader("<tr><td><nobr>%1:&nbsp;</nobr></td><td><nobr>%2</nobr></td></tr>");
+ const QString strSubHeader("<tr><td><nobr>&nbsp;&nbsp;%1:&nbsp;</nobr></td><td><nobr>%2</nobr></td></tr>");
+ QString strToolTip;
+
+ /* Network information: */
+ strToolTip += strHeader.arg(UINetworkManager::tr("Network Name"), m_strName);
+ strToolTip += strHeader.arg(UINetworkManager::tr("Network IPv4 Prefix"), m_strPrefixIPv4);
+ strToolTip += strHeader.arg(UINetworkManager::tr("Network IPv6 Prefix"), m_strPrefixIPv6);
+ strToolTip += strHeader.arg(UINetworkManager::tr("Supports DHCP"), m_fSupportsDHCP ? UINetworkManager::tr("yes")
+ : UINetworkManager::tr("no"));
+ strToolTip += strHeader.arg(UINetworkManager::tr("Supports IPv6"), m_fSupportsIPv6 ? UINetworkManager::tr("yes")
+ : UINetworkManager::tr("no"));
+ if (m_fSupportsIPv6 && m_fAdvertiseDefaultIPv6Route)
+ strToolTip += strSubHeader.arg(UINetworkManager::tr("Default IPv6 route"), UINetworkManager::tr("yes"));
+
+ /* Assign tool-tip finally: */
+ setToolTip(NATNetworkColumn_Name, strTable.arg(strToolTip));
+}
+
+
+/*********************************************************************************************************************************
+* Class UIItemCloudNetwork implementation. *
+*********************************************************************************************************************************/
+
+void UIItemCloudNetwork::updateFields()
+{
+ /* Compose item fields: */
+ setText(CloudNetworkColumn_Name, m_strName);
+ setText(CloudNetworkColumn_Provider, m_strProvider);
+ setText(CloudNetworkColumn_Profile, m_strProfile);
+
+ /* Compose item tool-tip: */
+ const QString strTable("<table cellspacing=5>%1</table>");
+ const QString strHeader("<tr><td><nobr>%1:&nbsp;</nobr></td><td><nobr>%2</nobr></td></tr>");
+ QString strToolTip;
+
+ /* Network information: */
+ strToolTip += strHeader.arg(UINetworkManager::tr("Network Name"), m_strName);
+ strToolTip += strHeader.arg(UINetworkManager::tr("Provider"), m_strProvider);
+ strToolTip += strHeader.arg(UINetworkManager::tr("Profile"), m_strProfile);
+
+ /* Assign tool-tip finally: */
+ setToolTip(CloudNetworkColumn_Name, strTable.arg(strToolTip));
+}
+
+
+/*********************************************************************************************************************************
+* Class UINetworkManagerWidget implementation. *
+*********************************************************************************************************************************/
+
+UINetworkManagerWidget::UINetworkManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar /* = true */, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmEmbedding(enmEmbedding)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_pToolBar(0)
+ , m_pTabWidget(0)
+ , m_pTabHostNetwork(0)
+ , m_pLayoutHostNetwork(0)
+ , m_pTreeWidgetHostNetwork(0)
+ , m_pDetailsWidgetHostNetwork(0)
+ , m_pTabNATNetwork(0)
+ , m_pLayoutNATNetwork(0)
+ , m_pTreeWidgetNATNetwork(0)
+ , m_pDetailsWidgetNATNetwork(0)
+ , m_pTabCloudNetwork(0)
+ , m_pLayoutCloudNetwork(0)
+ , m_pTreeWidgetCloudNetwork(0)
+ , m_pDetailsWidgetCloudNetwork(0)
+{
+ prepare();
+}
+
+QMenu *UINetworkManagerWidget::menu() const
+{
+ AssertPtrReturn(m_pActionPool, 0);
+ return m_pActionPool->action(UIActionIndexMN_M_NetworkWindow)->menu();
+}
+
+void UINetworkManagerWidget::retranslateUi()
+{
+ /* Adjust toolbar: */
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text.
+ if (m_pToolBar)
+ m_pToolBar->updateLayout();
+#endif /* VBOX_WS_MAC */
+
+ /* Translate tab-widget: */
+ if (m_pTabWidget)
+ {
+ m_pTabWidget->setTabText(0, UINetworkManager::tr("Host-only Networks"));
+ m_pTabWidget->setTabText(1, UINetworkManager::tr("NAT Networks"));
+ m_pTabWidget->setTabText(2, UINetworkManager::tr("Cloud Networks"));
+ }
+
+ /* Translate host network tree-widget: */
+ if (m_pTreeWidgetHostNetwork)
+ {
+#ifdef VBOX_WS_MAC
+ const QStringList fields = QStringList()
+ << UINetworkManager::tr("Name")
+ << UINetworkManager::tr("Mask")
+ << UINetworkManager::tr("Lower Bound")
+ << UINetworkManager::tr("Upper Bound");
+#else /* !VBOX_WS_MAC */
+ const QStringList fields = QStringList()
+ << UINetworkManager::tr("Name")
+ << UINetworkManager::tr("IPv4 Prefix")
+ << UINetworkManager::tr("IPv6 Prefix")
+ << UINetworkManager::tr("DHCP Server");
+#endif /* !VBOX_WS_MAC */
+ m_pTreeWidgetHostNetwork->setHeaderLabels(fields);
+ m_pTreeWidgetHostNetwork->setWhatsThis(UINetworkManager::tr("Registered host-only networks"));
+ }
+
+ /* Translate NAT network tree-widget: */
+ if (m_pTreeWidgetNATNetwork)
+ {
+ const QStringList fields = QStringList()
+ << UINetworkManager::tr("Name")
+ << UINetworkManager::tr("IPv4 Prefix")
+ << UINetworkManager::tr("IPv6 Prefix")
+ << UINetworkManager::tr("DHCP Server");
+ m_pTreeWidgetNATNetwork->setHeaderLabels(fields);
+ m_pTreeWidgetNATNetwork->setWhatsThis(UINetworkManager::tr("Registered NAT networks"));
+ }
+
+ /* Translate cloud network tree-widget: */
+ if (m_pTreeWidgetCloudNetwork)
+ {
+ const QStringList fields = QStringList()
+ << UINetworkManager::tr("Name")
+ << UINetworkManager::tr("Provider")
+ << UINetworkManager::tr("Profile");
+ m_pTreeWidgetCloudNetwork->setHeaderLabels(fields);
+ m_pTreeWidgetCloudNetwork->setWhatsThis(UINetworkManager::tr("Registered cloud networks"));
+ }
+}
+
+void UINetworkManagerWidget::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI<QWidget>::resizeEvent(pEvent);
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+}
+
+void UINetworkManagerWidget::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI<QWidget>::showEvent(pEvent);
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+}
+
+void UINetworkManagerWidget::sltResetDetailsChanges()
+{
+ /* Check tab-widget: */
+ AssertMsgReturnVoid(m_pTabWidget, ("This action should not be allowed!\n"));
+
+ /* Just push current item data to details-widget again: */
+ switch (m_pTabWidget->currentIndex())
+ {
+ case TabWidgetIndex_HostNetwork: sltHandleCurrentItemChangeHostNetwork(); break;
+ case TabWidgetIndex_NATNetwork: sltHandleCurrentItemChangeNATNetwork(); break;
+ case TabWidgetIndex_CloudNetwork: sltHandleCurrentItemChangeCloudNetwork(); break;
+ default: break;
+ }
+}
+
+void UINetworkManagerWidget::sltApplyDetailsChanges()
+{
+ /* Check tab-widget: */
+ AssertMsgReturnVoid(m_pTabWidget, ("This action should not be allowed!\n"));
+
+ /* Apply details-widget data changes: */
+ switch (m_pTabWidget->currentIndex())
+ {
+ case TabWidgetIndex_HostNetwork: sltApplyDetailsChangesHostNetwork(); break;
+ case TabWidgetIndex_NATNetwork: sltApplyDetailsChangesNATNetwork(); break;
+ case TabWidgetIndex_CloudNetwork: sltApplyDetailsChangesCloudNetwork(); break;
+ default: break;
+ }
+}
+
+void UINetworkManagerWidget::sltCreateHostNetwork()
+{
+ /* For host networks only: */
+ if (m_pTabWidget->currentIndex() != TabWidgetIndex_HostNetwork)
+ return;
+
+ /* Check host network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetHostNetwork, ("Host network tree-widget isn't created!\n"));
+
+#ifdef VBOX_WS_MAC
+ /* Compose a set of busy names: */
+ QSet<QString> names;
+ for (int i = 0; i < m_pTreeWidgetHostNetwork->topLevelItemCount(); ++i)
+ names << qobject_cast<UIItemHostNetwork*>(m_pTreeWidgetHostNetwork->childItem(i))->name();
+ /* Compose a map of busy indexes: */
+ QMap<int, bool> presence;
+ const QString strNameTemplate("HostNetwork%1");
+ const QRegExp regExp(strNameTemplate.arg("([\\d]*)"));
+ foreach (const QString &strName, names)
+ if (regExp.indexIn(strName) != -1)
+ presence[regExp.cap(1).toInt()] = true;
+ /* Search for a minimum index: */
+ int iMinimumIndex = 0;
+ for (int i = 0; !presence.isEmpty() && i <= presence.lastKey() + 1; ++i)
+ if (!presence.contains(i))
+ {
+ iMinimumIndex = i;
+ break;
+ }
+ /* Compose resulting index and name: */
+ const QString strNetworkName = strNameTemplate.arg(iMinimumIndex == 0 ? QString() : QString::number(iMinimumIndex));
+
+ /* Compose new item data: */
+ UIDataHostNetwork oldData;
+ oldData.m_fExists = true;
+ oldData.m_strName = strNetworkName;
+
+ /* Get VirtualBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Create network: */
+ CHostOnlyNetwork comNetwork = comVBox.CreateHostOnlyNetwork(oldData.m_strName);
+ CHostOnlyNetwork comNetworkBase = comNetwork;
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotCreateHostOnlyNetwork(comVBox);
+ else
+ {
+ /* Save host network name: */
+ if (comNetwork.isOk())
+ comNetwork.SetNetworkName(oldData.m_strName);
+
+ /* Show error message if necessary: */
+ if (!comNetwork.isOk())
+ UINotificationMessage::cannotChangeHostOnlyNetworkParameter(comNetwork);
+
+ /* Add network to the tree: */
+ UIDataHostNetwork newData;
+ loadHostNetwork(comNetworkBase, newData);
+ createItemForHostNetwork(newData, true);
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+
+#else /* !VBOX_WS_MAC */
+
+ /* Get host for further activities: */
+ CHost comHost = uiCommon().host();
+ CHostNetworkInterface comInterface;
+
+ /* Create interface: */
+ UINotificationProgressHostOnlyNetworkInterfaceCreate *pNotification =
+ new UINotificationProgressHostOnlyNetworkInterfaceCreate(comHost, comInterface);
+ connect(pNotification, &UINotificationProgressHostOnlyNetworkInterfaceCreate::sigHostOnlyNetworkInterfaceCreated,
+ this, &UINetworkManagerWidget::sigHandleHostOnlyNetworkInterfaceCreated);
+ gpNotificationCenter->append(pNotification);
+#endif /* !VBOX_WS_MAC */
+}
+
+#ifndef VBOX_WS_MAC
+void UINetworkManagerWidget::sigHandleHostOnlyNetworkInterfaceCreated(const CHostNetworkInterface &comInterface)
+{
+ /* Get network name for further activities: */
+ const QString strNetworkName = comInterface.GetNetworkName();
+
+ /* Show error message if necessary: */
+ if (!comInterface.isOk())
+ UINotificationMessage::cannotAcquireHostNetworkInterfaceParameter(comInterface);
+ else
+ {
+ /* Get VBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding DHCP server (create if necessary): */
+ CDHCPServer comServer = comVBox.FindDHCPServerByNetworkName(strNetworkName);
+ if (!comVBox.isOk() || comServer.isNull())
+ comServer = comVBox.CreateDHCPServer(strNetworkName);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk() || comServer.isNull())
+ UINotificationMessage::cannotCreateDHCPServer(comVBox, strNetworkName);
+
+ /* Add interface to the tree: */
+ UIDataHostNetwork data;
+ loadHostNetwork(comInterface, data);
+ createItemForHostNetwork(data, true);
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+}
+#endif /* !VBOX_WS_MAC */
+
+void UINetworkManagerWidget::sltRemoveHostNetwork()
+{
+ /* For host networks only: */
+ if (m_pTabWidget->currentIndex() != TabWidgetIndex_HostNetwork)
+ return;
+
+ /* Check host network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetHostNetwork, ("Host network tree-widget isn't created!\n"));
+
+ /* Get network item: */
+ UIItemHostNetwork *pItem = static_cast<UIItemHostNetwork*>(m_pTreeWidgetHostNetwork->currentItem());
+ AssertMsgReturnVoid(pItem, ("Current item must not be null!\n"));
+
+#ifdef VBOX_WS_MAC
+
+ /* Get network name: */
+ const QString strNetworkName(pItem->name());
+
+ /* Confirm host network removal: */
+ if (!msgCenter().confirmHostOnlyNetworkRemoval(strNetworkName, this))
+ return;
+
+ /* Get VirtualBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding network: */
+ const CHostOnlyNetwork &comNetwork = comVBox.FindHostOnlyNetworkByName(strNetworkName);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk() || comNetwork.isNull())
+ UINotificationMessage::cannotFindHostOnlyNetwork(comVBox, strNetworkName);
+ else
+ {
+ /* Remove network finally: */
+ comVBox.RemoveHostOnlyNetwork(comNetwork);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotRemoveHostOnlyNetwork(comVBox, strNetworkName);
+ else
+ {
+ /* Move selection to somewhere else: */
+ if (m_pTreeWidgetHostNetwork->itemBelow(pItem))
+ m_pTreeWidgetHostNetwork->setCurrentItem(m_pTreeWidgetHostNetwork->itemBelow(pItem));
+ else if (m_pTreeWidgetHostNetwork->itemAbove(pItem))
+ m_pTreeWidgetHostNetwork->setCurrentItem(m_pTreeWidgetHostNetwork->itemAbove(pItem));
+ else
+ m_pTreeWidgetHostNetwork->setCurrentItem(0);
+
+ /* Remove interface from the tree: */
+ delete pItem;
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+ }
+
+#else /* !VBOX_WS_MAC */
+
+ /* Get interface name: */
+ const QString strInterfaceName(pItem->name());
+
+ /* Confirm host network removal: */
+ if (!msgCenter().confirmHostNetworkInterfaceRemoval(strInterfaceName, this))
+ return;
+
+ /* Get host for further activities: */
+ CHost comHost = uiCommon().host();
+
+ /* Find corresponding interface: */
+ const CHostNetworkInterface comInterface = comHost.FindHostNetworkInterfaceByName(strInterfaceName);
+
+ /* Show error message if necessary: */
+ if (!comHost.isOk() || comInterface.isNull())
+ UINotificationMessage::cannotFindHostNetworkInterface(comHost, strInterfaceName);
+ else
+ {
+ /* Get network name for further activities: */
+ QString strNetworkName;
+ if (comInterface.isOk())
+ strNetworkName = comInterface.GetNetworkName();
+ /* Get interface id for further activities: */
+ QUuid uInterfaceId;
+ if (comInterface.isOk())
+ uInterfaceId = comInterface.GetId();
+
+ /* Show error message if necessary: */
+ if (!comInterface.isOk())
+ UINotificationMessage::cannotAcquireHostNetworkInterfaceParameter(comInterface);
+ else
+ {
+ /* Get VBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding DHCP server: */
+ const CDHCPServer &comServer = comVBox.FindDHCPServerByNetworkName(strNetworkName);
+ if (comVBox.isOk() && comServer.isNotNull())
+ {
+ /* Remove server if any: */
+ comVBox.RemoveDHCPServer(comServer);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotRemoveDHCPServer(comVBox, strInterfaceName);
+ }
+
+ /* Create interface: */
+ UINotificationProgressHostOnlyNetworkInterfaceRemove *pNotification =
+ new UINotificationProgressHostOnlyNetworkInterfaceRemove(comHost, uInterfaceId);
+ connect(pNotification, &UINotificationProgressHostOnlyNetworkInterfaceRemove::sigHostOnlyNetworkInterfaceRemoved,
+ this, &UINetworkManagerWidget::sigHandleHostOnlyNetworkInterfaceRemoved);
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+#endif /* !VBOX_WS_MAC */
+}
+
+#ifndef VBOX_WS_MAC
+void UINetworkManagerWidget::sigHandleHostOnlyNetworkInterfaceRemoved(const QString &strInterfaceName)
+{
+ /* Check host network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetHostNetwork, ("Host network tree-widget isn't created!\n"));
+
+ /* Search for required item: */
+ QList<QTreeWidgetItem*> items = m_pTreeWidgetHostNetwork->findItems(strInterfaceName, Qt::MatchCaseSensitive);
+ AssertReturnVoid(!items.isEmpty());
+ QTreeWidgetItem *pItem = items.first();
+
+ /* Move selection to somewhere else: */
+ if (m_pTreeWidgetHostNetwork->itemBelow(pItem))
+ m_pTreeWidgetHostNetwork->setCurrentItem(m_pTreeWidgetHostNetwork->itemBelow(pItem));
+ else if (m_pTreeWidgetHostNetwork->itemAbove(pItem))
+ m_pTreeWidgetHostNetwork->setCurrentItem(m_pTreeWidgetHostNetwork->itemAbove(pItem));
+ else
+ m_pTreeWidgetHostNetwork->setCurrentItem(0);
+
+ /* Remove interface from the tree: */
+ delete pItem;
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+}
+#endif /* !VBOX_WS_MAC */
+
+void UINetworkManagerWidget::sltCreateNATNetwork()
+{
+ /* For NAT networks only: */
+ if (m_pTabWidget->currentIndex() != TabWidgetIndex_NATNetwork)
+ return;
+
+ /* Compose a set of busy names: */
+ QSet<QString> names;
+ for (int i = 0; i < m_pTreeWidgetNATNetwork->topLevelItemCount(); ++i)
+ names << qobject_cast<UIItemNATNetwork*>(m_pTreeWidgetNATNetwork->childItem(i))->name();
+ /* Compose a map of busy indexes: */
+ QMap<int, bool> presence;
+ const QString strNameTemplate("NatNetwork%1");
+ const QRegExp regExp(strNameTemplate.arg("([\\d]*)"));
+ foreach (const QString &strName, names)
+ if (regExp.indexIn(strName) != -1)
+ presence[regExp.cap(1).toInt()] = true;
+ /* Search for a minimum index: */
+ int iMinimumIndex = 0;
+ for (int i = 0; !presence.isEmpty() && i <= presence.lastKey() + 1; ++i)
+ if (!presence.contains(i))
+ {
+ iMinimumIndex = i;
+ break;
+ }
+ /* Compose resulting index and name: */
+ const QString strNetworkName = strNameTemplate.arg(iMinimumIndex == 0 ? QString() : QString::number(iMinimumIndex));
+
+ /* Compose new item data: */
+ UIDataNATNetwork oldData;
+ oldData.m_fExists = true;
+ oldData.m_strName = strNetworkName;
+ oldData.m_strPrefixIPv4 = "10.0.2.0/24";
+ oldData.m_strPrefixIPv6 = ""; // do we need something here?
+ oldData.m_fSupportsDHCP = true;
+ oldData.m_fSupportsIPv6 = false;
+ oldData.m_fAdvertiseDefaultIPv6Route = false;
+
+ /* Get VirtualBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Create network: */
+ CNATNetwork comNetwork = comVBox.CreateNATNetwork(oldData.m_strName);
+ CNATNetwork comNetworkBase = comNetwork;
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotCreateNATNetwork(comVBox);
+ else
+ {
+ /* Save NAT network name: */
+ if (comNetwork.isOk())
+ comNetwork.SetNetworkName(oldData.m_strName);
+ /* Save NAT network IPv4 prefix: */
+ if (comNetwork.isOk())
+ comNetwork.SetNetwork(oldData.m_strPrefixIPv4);
+ /* Save NAT network IPv6 prefix: */
+ if (comNetwork.isOk())
+ comNetwork.SetIPv6Prefix(oldData.m_strPrefixIPv6);
+ /* Save whether NAT network needs DHCP server: */
+ if (comNetwork.isOk())
+ comNetwork.SetNeedDhcpServer(oldData.m_fSupportsDHCP);
+ /* Save whether NAT network supports IPv6: */
+ if (comNetwork.isOk())
+ comNetwork.SetIPv6Enabled(oldData.m_fSupportsIPv6);
+ /* Save whether NAT network should advertise default IPv6 route: */
+ if (comNetwork.isOk())
+ comNetwork.SetAdvertiseDefaultIPv6RouteEnabled(oldData.m_fAdvertiseDefaultIPv6Route);
+
+ /* Show error message if necessary: */
+ if (!comNetwork.isOk())
+ UINotificationMessage::cannotChangeNATNetworkParameter(comNetwork);
+
+ /* Add network to the tree: */
+ UIDataNATNetwork newData;
+ loadNATNetwork(comNetworkBase, newData);
+ createItemForNATNetwork(newData, true);
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+}
+
+void UINetworkManagerWidget::sltRemoveNATNetwork()
+{
+ /* For NAT networks only: */
+ if (m_pTabWidget->currentIndex() != TabWidgetIndex_NATNetwork)
+ return;
+
+ /* Check NAT network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetNATNetwork, ("NAT network tree-widget isn't created!\n"));
+
+ /* Get network item: */
+ UIItemNATNetwork *pItem = static_cast<UIItemNATNetwork*>(m_pTreeWidgetNATNetwork->currentItem());
+ AssertMsgReturnVoid(pItem, ("Current item must not be null!\n"));
+
+ /* Get network name: */
+ const QString strNetworkName(pItem->name());
+
+ /* Confirm host network removal: */
+ if (!msgCenter().confirmNATNetworkRemoval(strNetworkName, this))
+ return;
+
+ /* Get VirtualBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding network: */
+ const CNATNetwork &comNetwork = comVBox.FindNATNetworkByName(strNetworkName);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk() || comNetwork.isNull())
+ UINotificationMessage::cannotFindNATNetwork(comVBox, strNetworkName);
+ else
+ {
+ /* Remove network finally: */
+ comVBox.RemoveNATNetwork(comNetwork);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotRemoveNATNetwork(comVBox, strNetworkName);
+ else
+ {
+ /* Move selection to somewhere else: */
+ if (m_pTreeWidgetNATNetwork->itemBelow(pItem))
+ m_pTreeWidgetNATNetwork->setCurrentItem(m_pTreeWidgetNATNetwork->itemBelow(pItem));
+ else if (m_pTreeWidgetNATNetwork->itemAbove(pItem))
+ m_pTreeWidgetNATNetwork->setCurrentItem(m_pTreeWidgetNATNetwork->itemAbove(pItem));
+ else
+ m_pTreeWidgetNATNetwork->setCurrentItem(0);
+
+ /* Remove interface from the tree: */
+ delete pItem;
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+ }
+}
+
+void UINetworkManagerWidget::sltCreateCloudNetwork()
+{
+ /* For cloud networks only: */
+ if (m_pTabWidget->currentIndex() != TabWidgetIndex_CloudNetwork)
+ return;
+
+ /* Compose a set of busy names: */
+ QSet<QString> names;
+ for (int i = 0; i < m_pTreeWidgetCloudNetwork->topLevelItemCount(); ++i)
+ names << qobject_cast<UIItemCloudNetwork*>(m_pTreeWidgetCloudNetwork->childItem(i))->name();
+ /* Compose a map of busy indexes: */
+ QMap<int, bool> presence;
+ const QString strNameTemplate("CloudNetwork%1");
+ const QRegExp regExp(strNameTemplate.arg("([\\d]*)"));
+ foreach (const QString &strName, names)
+ if (regExp.indexIn(strName) != -1)
+ presence[regExp.cap(1).toInt()] = true;
+ /* Search for a minimum index: */
+ int iMinimumIndex = 0;
+ for (int i = 0; !presence.isEmpty() && i <= presence.lastKey() + 1; ++i)
+ if (!presence.contains(i))
+ {
+ iMinimumIndex = i;
+ break;
+ }
+ /* Compose resulting index and name: */
+ const QString strNetworkName = strNameTemplate.arg(iMinimumIndex == 0 ? QString() : QString::number(iMinimumIndex));
+
+ /* Compose new item data: */
+ UIDataCloudNetwork oldData;
+ oldData.m_fEnabled = true;
+ oldData.m_strName = strNetworkName;
+
+ /* Get VirtualBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Create network: */
+ CCloudNetwork comNetwork = comVBox.CreateCloudNetwork(oldData.m_strName);
+ CCloudNetwork comNetworkBase = comNetwork;
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotCreateCloudNetwork(comVBox);
+ else
+ {
+ /* Save whether network enabled: */
+ if (comNetwork.isOk())
+ comNetwork.SetEnabled(oldData.m_fEnabled);
+ /* Save cloud network name: */
+ if (comNetwork.isOk())
+ comNetwork.SetNetworkName(oldData.m_strName);
+
+ /* Show error message if necessary: */
+ if (!comNetwork.isOk())
+ UINotificationMessage::cannotChangeCloudNetworkParameter(comNetwork);
+
+ /* Add network to the tree: */
+ UIDataCloudNetwork newData;
+ loadCloudNetwork(comNetworkBase, newData);
+ createItemForCloudNetwork(newData, true);
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+}
+
+void UINetworkManagerWidget::sltRemoveCloudNetwork()
+{
+ /* For cloud networks only: */
+ if (m_pTabWidget->currentIndex() != TabWidgetIndex_CloudNetwork)
+ return;
+
+ /* Check cloud network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetCloudNetwork, ("Cloud network tree-widget isn't created!\n"));
+
+ /* Get network item: */
+ UIItemCloudNetwork *pItem = static_cast<UIItemCloudNetwork*>(m_pTreeWidgetCloudNetwork->currentItem());
+ AssertMsgReturnVoid(pItem, ("Current item must not be null!\n"));
+
+ /* Get network name: */
+ const QString strNetworkName(pItem->name());
+
+ /* Confirm host network removal: */
+ if (!msgCenter().confirmCloudNetworkRemoval(strNetworkName, this))
+ return;
+
+ /* Get VirtualBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding network: */
+ const CCloudNetwork &comNetwork = comVBox.FindCloudNetworkByName(strNetworkName);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk() || comNetwork.isNull())
+ UINotificationMessage::cannotFindCloudNetwork(comVBox, strNetworkName);
+ else
+ {
+ /* Remove network finally: */
+ comVBox.RemoveCloudNetwork(comNetwork);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotRemoveCloudNetwork(comVBox, strNetworkName);
+ else
+ {
+ /* Move selection to somewhere else: */
+ if (m_pTreeWidgetCloudNetwork->itemBelow(pItem))
+ m_pTreeWidgetCloudNetwork->setCurrentItem(m_pTreeWidgetCloudNetwork->itemBelow(pItem));
+ else if (m_pTreeWidgetCloudNetwork->itemAbove(pItem))
+ m_pTreeWidgetCloudNetwork->setCurrentItem(m_pTreeWidgetCloudNetwork->itemAbove(pItem));
+ else
+ m_pTreeWidgetCloudNetwork->setCurrentItem(0);
+
+ /* Remove interface from the tree: */
+ delete pItem;
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+ }
+}
+
+void UINetworkManagerWidget::sltToggleDetailsVisibility(bool fVisible)
+{
+ /* Save the setting: */
+ gEDataManager->setHostNetworkManagerDetailsExpanded(fVisible);
+ /* Show/hide details area and Apply/Reset buttons: */
+ switch (m_pTabWidget->currentIndex())
+ {
+ case TabWidgetIndex_HostNetwork:
+ {
+ if (m_pDetailsWidgetNATNetwork)
+ m_pDetailsWidgetNATNetwork->setVisible(false);
+ if (m_pDetailsWidgetCloudNetwork)
+ m_pDetailsWidgetCloudNetwork->setVisible(false);
+ if (m_pDetailsWidgetHostNetwork)
+ m_pDetailsWidgetHostNetwork->setVisible(fVisible);
+ break;
+ }
+ case TabWidgetIndex_NATNetwork:
+ {
+ if (m_pDetailsWidgetHostNetwork)
+ m_pDetailsWidgetHostNetwork->setVisible(false);
+ if (m_pDetailsWidgetCloudNetwork)
+ m_pDetailsWidgetCloudNetwork->setVisible(false);
+ if (m_pDetailsWidgetNATNetwork)
+ m_pDetailsWidgetNATNetwork->setVisible(fVisible);
+ break;
+ }
+ case TabWidgetIndex_CloudNetwork:
+ {
+ if (m_pDetailsWidgetHostNetwork)
+ m_pDetailsWidgetHostNetwork->setVisible(false);
+ if (m_pDetailsWidgetNATNetwork)
+ m_pDetailsWidgetNATNetwork->setVisible(false);
+ if (m_pDetailsWidgetCloudNetwork)
+ m_pDetailsWidgetCloudNetwork->setVisible(fVisible);
+ break;
+ }
+ }
+ /* Notify external listeners: */
+ emit sigDetailsVisibilityChanged(fVisible);
+}
+
+void UINetworkManagerWidget::sltHandleCurrentTabWidgetIndexChange()
+{
+ /* Update actions: */
+ updateActionAvailability();
+
+ /* Adjust tree-widgets first of all: */
+ sltAdjustTreeWidgets();
+
+ /* Show/hide details area and Apply/Reset buttons: */
+ const bool fVisible = m_pActionPool->action(UIActionIndexMN_M_Network_T_Details)->isChecked();
+ switch (m_pTabWidget->currentIndex())
+ {
+ case TabWidgetIndex_HostNetwork:
+ {
+ if (m_pDetailsWidgetNATNetwork)
+ m_pDetailsWidgetNATNetwork->setVisible(false);
+ if (m_pDetailsWidgetCloudNetwork)
+ m_pDetailsWidgetCloudNetwork->setVisible(false);
+ if (m_pDetailsWidgetHostNetwork)
+ m_pDetailsWidgetHostNetwork->setVisible(fVisible);
+ break;
+ }
+ case TabWidgetIndex_NATNetwork:
+ {
+ if (m_pDetailsWidgetHostNetwork)
+ m_pDetailsWidgetHostNetwork->setVisible(false);
+ if (m_pDetailsWidgetCloudNetwork)
+ m_pDetailsWidgetCloudNetwork->setVisible(false);
+ if (m_pDetailsWidgetNATNetwork)
+ m_pDetailsWidgetNATNetwork->setVisible(fVisible);
+ break;
+ }
+ case TabWidgetIndex_CloudNetwork:
+ {
+ if (m_pDetailsWidgetHostNetwork)
+ m_pDetailsWidgetHostNetwork->setVisible(false);
+ if (m_pDetailsWidgetNATNetwork)
+ m_pDetailsWidgetNATNetwork->setVisible(false);
+ if (m_pDetailsWidgetCloudNetwork)
+ m_pDetailsWidgetCloudNetwork->setVisible(fVisible);
+ break;
+ }
+ }
+}
+
+void UINetworkManagerWidget::sltAdjustTreeWidgets()
+{
+ /* Check host network tree-widget: */
+ if (m_pTreeWidgetHostNetwork)
+ {
+ /* Get the tree-widget abstract interface: */
+ QAbstractItemView *pItemView = m_pTreeWidgetHostNetwork;
+ /* Get the tree-widget header-view: */
+ QHeaderView *pItemHeader = m_pTreeWidgetHostNetwork->header();
+
+ /* Calculate the total tree-widget width: */
+ const int iTotal = m_pTreeWidgetHostNetwork->viewport()->width();
+#ifdef VBOX_WS_MAC
+ /* Look for a minimum width hints for non-important columns: */
+ const int iMinWidth1 = qMax(pItemView->sizeHintForColumn(HostNetworkColumn_Mask), pItemHeader->sectionSizeHint(HostNetworkColumn_Mask));
+ const int iMinWidth2 = qMax(pItemView->sizeHintForColumn(HostNetworkColumn_LBnd), pItemHeader->sectionSizeHint(HostNetworkColumn_LBnd));
+ const int iMinWidth3 = qMax(pItemView->sizeHintForColumn(HostNetworkColumn_UBnd), pItemHeader->sectionSizeHint(HostNetworkColumn_UBnd));
+#else /* !VBOX_WS_MAC */
+ /* Look for a minimum width hints for non-important columns: */
+ const int iMinWidth1 = qMax(pItemView->sizeHintForColumn(HostNetworkColumn_IPv4), pItemHeader->sectionSizeHint(HostNetworkColumn_IPv4));
+ const int iMinWidth2 = qMax(pItemView->sizeHintForColumn(HostNetworkColumn_IPv6), pItemHeader->sectionSizeHint(HostNetworkColumn_IPv6));
+ const int iMinWidth3 = qMax(pItemView->sizeHintForColumn(HostNetworkColumn_DHCP), pItemHeader->sectionSizeHint(HostNetworkColumn_DHCP));
+#endif /* !VBOX_WS_MAC */
+ /* Propose suitable width hints for non-important columns: */
+ const int iWidth1 = iMinWidth1 < iTotal / HostNetworkColumn_Max ? iMinWidth1 : iTotal / HostNetworkColumn_Max;
+ const int iWidth2 = iMinWidth2 < iTotal / HostNetworkColumn_Max ? iMinWidth2 : iTotal / HostNetworkColumn_Max;
+ const int iWidth3 = iMinWidth3 < iTotal / HostNetworkColumn_Max ? iMinWidth3 : iTotal / HostNetworkColumn_Max;
+ /* Apply the proposal: */
+#ifdef VBOX_WS_MAC
+ m_pTreeWidgetHostNetwork->setColumnWidth(HostNetworkColumn_Mask, iWidth1);
+ m_pTreeWidgetHostNetwork->setColumnWidth(HostNetworkColumn_LBnd, iWidth2);
+ m_pTreeWidgetHostNetwork->setColumnWidth(HostNetworkColumn_UBnd, iWidth3);
+#else /* !VBOX_WS_MAC */
+ m_pTreeWidgetHostNetwork->setColumnWidth(HostNetworkColumn_IPv4, iWidth1);
+ m_pTreeWidgetHostNetwork->setColumnWidth(HostNetworkColumn_IPv6, iWidth2);
+ m_pTreeWidgetHostNetwork->setColumnWidth(HostNetworkColumn_DHCP, iWidth3);
+#endif /* !VBOX_WS_MAC */
+ m_pTreeWidgetHostNetwork->setColumnWidth(HostNetworkColumn_Name, iTotal - iWidth1 - iWidth2 - iWidth3);
+ }
+
+ /* Check NAT network tree-widget: */
+ if (m_pTreeWidgetNATNetwork)
+ {
+ /* Get the tree-widget abstract interface: */
+ QAbstractItemView *pItemView = m_pTreeWidgetNATNetwork;
+ /* Get the tree-widget header-view: */
+ QHeaderView *pItemHeader = m_pTreeWidgetNATNetwork->header();
+
+ /* Calculate the total tree-widget width: */
+ const int iTotal = m_pTreeWidgetNATNetwork->viewport()->width();
+ /* Look for a minimum width hints for non-important columns: */
+ const int iMinWidth1 = qMax(pItemView->sizeHintForColumn(NATNetworkColumn_IPv4), pItemHeader->sectionSizeHint(NATNetworkColumn_IPv4));
+ const int iMinWidth2 = qMax(pItemView->sizeHintForColumn(NATNetworkColumn_IPv6), pItemHeader->sectionSizeHint(NATNetworkColumn_IPv6));
+ const int iMinWidth3 = qMax(pItemView->sizeHintForColumn(NATNetworkColumn_DHCP), pItemHeader->sectionSizeHint(NATNetworkColumn_DHCP));
+ /* Propose suitable width hints for non-important columns: */
+ const int iWidth1 = iMinWidth1 < iTotal / NATNetworkColumn_Max ? iMinWidth1 : iTotal / NATNetworkColumn_Max;
+ const int iWidth2 = iMinWidth2 < iTotal / NATNetworkColumn_Max ? iMinWidth2 : iTotal / NATNetworkColumn_Max;
+ const int iWidth3 = iMinWidth3 < iTotal / NATNetworkColumn_Max ? iMinWidth3 : iTotal / NATNetworkColumn_Max;
+ /* Apply the proposal: */
+ m_pTreeWidgetNATNetwork->setColumnWidth(NATNetworkColumn_IPv4, iWidth1);
+ m_pTreeWidgetNATNetwork->setColumnWidth(NATNetworkColumn_IPv6, iWidth2);
+ m_pTreeWidgetNATNetwork->setColumnWidth(NATNetworkColumn_DHCP, iWidth3);
+ m_pTreeWidgetNATNetwork->setColumnWidth(NATNetworkColumn_Name, iTotal - iWidth1 - iWidth2 - iWidth3);
+ }
+
+ /* Check cloud network tree-widget: */
+ if (m_pTreeWidgetCloudNetwork)
+ {
+ /* Get the tree-widget abstract interface: */
+ QAbstractItemView *pItemView = m_pTreeWidgetCloudNetwork;
+ /* Get the tree-widget header-view: */
+ QHeaderView *pItemHeader = m_pTreeWidgetCloudNetwork->header();
+
+ /* Calculate the total tree-widget width: */
+ const int iTotal = m_pTreeWidgetCloudNetwork->viewport()->width();
+ /* Look for a minimum width hints for non-important columns: */
+ const int iMinWidth1 = qMax(pItemView->sizeHintForColumn(CloudNetworkColumn_Provider), pItemHeader->sectionSizeHint(CloudNetworkColumn_Provider));
+ const int iMinWidth2 = qMax(pItemView->sizeHintForColumn(CloudNetworkColumn_Profile), pItemHeader->sectionSizeHint(CloudNetworkColumn_Profile));
+ /* Propose suitable width hints for non-important columns: */
+ const int iWidth1 = iMinWidth1 < iTotal / CloudNetworkColumn_Max ? iMinWidth1 : iTotal / CloudNetworkColumn_Max;
+ const int iWidth2 = iMinWidth2 < iTotal / CloudNetworkColumn_Max ? iMinWidth2 : iTotal / CloudNetworkColumn_Max;
+ /* Apply the proposal: */
+ m_pTreeWidgetCloudNetwork->setColumnWidth(CloudNetworkColumn_Provider, iWidth1);
+ m_pTreeWidgetCloudNetwork->setColumnWidth(CloudNetworkColumn_Profile, iWidth2);
+ m_pTreeWidgetCloudNetwork->setColumnWidth(CloudNetworkColumn_Name, iTotal - iWidth1 - iWidth2);
+ }
+}
+
+void UINetworkManagerWidget::sltHandleCurrentItemChangeHostNetwork()
+{
+ /* Update actions: */
+ updateActionAvailability();
+
+ /* Check host network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetHostNetwork, ("Host network tree-widget isn't created!\n"));
+
+ /* Get network item: */
+ UIItemHostNetwork *pItem = static_cast<UIItemHostNetwork*>(m_pTreeWidgetHostNetwork->currentItem());
+
+ /* Check host network details-widget: */
+ AssertMsgReturnVoid(m_pDetailsWidgetHostNetwork, ("Host network details-widget isn't created!\n"));
+
+ /* If there is an item => update details data: */
+ if (pItem)
+ m_pDetailsWidgetHostNetwork->setData(*pItem);
+ /* Otherwise => clear details: */
+ else
+ m_pDetailsWidgetHostNetwork->setData(UIDataHostNetwork());
+}
+
+void UINetworkManagerWidget::sltHandleContextMenuRequestHostNetwork(const QPoint &position)
+{
+ /* Check host network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetHostNetwork, ("Host network tree-widget isn't created!\n"));
+
+ /* Compose temporary context-menu: */
+ QMenu menu;
+ if (m_pTreeWidgetHostNetwork->itemAt(position))
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_T_Details));
+ }
+ else
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Create));
+// menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Refresh));
+ }
+ /* And show it: */
+ menu.exec(m_pTreeWidgetHostNetwork->mapToGlobal(position));
+}
+
+void UINetworkManagerWidget::sltApplyDetailsChangesHostNetwork()
+{
+ /* Check host network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetHostNetwork, ("Host network tree-widget isn't created!\n"));
+
+ /* Get host network item: */
+ UIItemHostNetwork *pItem = static_cast<UIItemHostNetwork*>(m_pTreeWidgetHostNetwork->currentItem());
+ AssertMsgReturnVoid(pItem, ("Current item must not be null!\n"));
+
+ /* Check host network details-widget: */
+ AssertMsgReturnVoid(m_pDetailsWidgetHostNetwork, ("Host network details-widget isn't created!\n"));
+
+ /* Revalidate host network details: */
+ if (m_pDetailsWidgetHostNetwork->revalidate())
+ {
+ /* Get item data: */
+ UIDataHostNetwork oldData = *pItem;
+ UIDataHostNetwork newData = m_pDetailsWidgetHostNetwork->data();
+
+#ifdef VBOX_WS_MAC
+ /* Get VirtualBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding network: */
+ CHostOnlyNetwork comNetwork = comVBox.FindHostOnlyNetworkByName(oldData.m_strName);
+ CHostOnlyNetwork comNetworkBase = comNetwork;
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk() || comNetwork.isNull())
+ UINotificationMessage::cannotFindHostOnlyNetwork(comVBox, oldData.m_strName);
+ else
+ {
+ /* Save host network name: */
+ if (comNetwork.isOk() && newData.m_strName != oldData.m_strName)
+ comNetwork.SetNetworkName(newData.m_strName);
+ /* Save host network mask: */
+ if (comNetwork.isOk() && newData.m_strMask != oldData.m_strMask)
+ comNetwork.SetNetworkMask(newData.m_strMask);
+ /* Save host network lower bound: */
+ if (comNetwork.isOk() && newData.m_strLBnd != oldData.m_strLBnd)
+ comNetwork.SetLowerIP(newData.m_strLBnd);
+ /* Save host network upper bound: */
+ if (comNetwork.isOk() && newData.m_strUBnd != oldData.m_strUBnd)
+ comNetwork.SetUpperIP(newData.m_strUBnd);
+
+ /* Show error message if necessary: */
+ if (!comNetwork.isOk())
+ UINotificationMessage::cannotChangeHostOnlyNetworkParameter(comNetwork);
+
+ /* Update network in the tree: */
+ UIDataHostNetwork data;
+ loadHostNetwork(comNetworkBase, data);
+ updateItemForHostNetwork(data, true, pItem);
+
+ /* Make sure current item fetched: */
+ sltHandleCurrentItemChangeHostNetwork();
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+
+#else /* !VBOX_WS_MAC */
+
+ /* Get host for further activities: */
+ CHost comHost = uiCommon().host();
+
+ /* Find corresponding interface: */
+ CHostNetworkInterface comInterface = comHost.FindHostNetworkInterfaceByName(oldData.m_interface.m_strName);
+
+ /* Show error message if necessary: */
+ if (!comHost.isOk() || comInterface.isNull())
+ UINotificationMessage::cannotFindHostNetworkInterface(comHost, oldData.m_interface.m_strName);
+ else
+ {
+ /* Save automatic interface configuration: */
+ if (newData.m_interface.m_fDHCPEnabled)
+ {
+ if ( comInterface.isOk()
+ && !oldData.m_interface.m_fDHCPEnabled)
+ comInterface.EnableDynamicIPConfig();
+ }
+ /* Save manual interface configuration: */
+ else
+ {
+ /* Save IPv4 interface configuration: */
+ if ( comInterface.isOk()
+ && ( oldData.m_interface.m_fDHCPEnabled
+ || newData.m_interface.m_strAddress != oldData.m_interface.m_strAddress
+ || newData.m_interface.m_strMask != oldData.m_interface.m_strMask))
+ comInterface.EnableStaticIPConfig(newData.m_interface.m_strAddress, newData.m_interface.m_strMask);
+ /* Save IPv6 interface configuration: */
+ if ( comInterface.isOk()
+ && newData.m_interface.m_fSupportedIPv6
+ && ( oldData.m_interface.m_fDHCPEnabled
+ || newData.m_interface.m_strAddress6 != oldData.m_interface.m_strAddress6
+ || newData.m_interface.m_strPrefixLength6 != oldData.m_interface.m_strPrefixLength6))
+ comInterface.EnableStaticIPConfigV6(newData.m_interface.m_strAddress6, newData.m_interface.m_strPrefixLength6.toULong());
+ }
+
+ /* Show error message if necessary: */
+ if (!comInterface.isOk())
+ UINotificationMessage::cannotChangeHostNetworkInterfaceParameter(comInterface);
+ else
+ {
+ /* Get network name for further activities: */
+ const QString strNetworkName = comInterface.GetNetworkName();
+
+ /* Show error message if necessary: */
+ if (!comInterface.isOk())
+ UINotificationMessage::cannotAcquireHostNetworkInterfaceParameter(comInterface);
+ else
+ {
+ /* Get VBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding DHCP server (create if necessary): */
+ CDHCPServer comServer = comVBox.FindDHCPServerByNetworkName(strNetworkName);
+ if (!comVBox.isOk() || comServer.isNull())
+ comServer = comVBox.CreateDHCPServer(strNetworkName);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk() || comServer.isNull())
+ UINotificationMessage::cannotCreateDHCPServer(comVBox, strNetworkName);
+ else
+ {
+ /* Save whether DHCP server is enabled: */
+ if ( comServer.isOk()
+ && newData.m_dhcpserver.m_fEnabled != oldData.m_dhcpserver.m_fEnabled)
+ comServer.SetEnabled(newData.m_dhcpserver.m_fEnabled);
+ /* Save DHCP server configuration: */
+ if ( comServer.isOk()
+ && newData.m_dhcpserver.m_fEnabled
+ && ( newData.m_dhcpserver.m_strAddress != oldData.m_dhcpserver.m_strAddress
+ || newData.m_dhcpserver.m_strMask != oldData.m_dhcpserver.m_strMask
+ || newData.m_dhcpserver.m_strLowerAddress != oldData.m_dhcpserver.m_strLowerAddress
+ || newData.m_dhcpserver.m_strUpperAddress != oldData.m_dhcpserver.m_strUpperAddress))
+ comServer.SetConfiguration(newData.m_dhcpserver.m_strAddress, newData.m_dhcpserver.m_strMask,
+ newData.m_dhcpserver.m_strLowerAddress, newData.m_dhcpserver.m_strUpperAddress);
+
+ /* Show error message if necessary: */
+ if (!comServer.isOk())
+ UINotificationMessage::cannotChangeDHCPServerParameter(comServer);
+ }
+ }
+ }
+
+ /* Find corresponding interface again (if necessary): */
+ if (!comInterface.isOk())
+ {
+ comInterface = comHost.FindHostNetworkInterfaceByName(oldData.m_interface.m_strName);
+
+ /* Show error message if necessary: */
+ if (!comHost.isOk() || comInterface.isNull())
+ UINotificationMessage::cannotFindHostNetworkInterface(comHost, oldData.m_interface.m_strName);
+ }
+
+ /* If interface is Ok now: */
+ if (comInterface.isNotNull() && comInterface.isOk())
+ {
+ /* Update interface in the tree: */
+ UIDataHostNetwork data;
+ loadHostNetwork(comInterface, data);
+ updateItemForHostNetwork(data, true, pItem);
+
+ /* Make sure current item fetched: */
+ sltHandleCurrentItemChangeHostNetwork();
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+ }
+#endif /* !VBOX_WS_MAC */
+ }
+
+ /* Make sure button states updated: */
+ m_pDetailsWidgetHostNetwork->updateButtonStates();
+}
+
+void UINetworkManagerWidget::sltHandleCurrentItemChangeNATNetworkHoldingPosition(bool fHoldPosition)
+{
+ /* Update actions: */
+ updateActionAvailability();
+
+ /* Check NAT network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetNATNetwork, ("NAT network tree-widget isn't created!\n"));
+
+ /* Get network item: */
+ UIItemNATNetwork *pItem = static_cast<UIItemNATNetwork*>(m_pTreeWidgetNATNetwork->currentItem());
+
+ /* Check NAT network details-widget: */
+ AssertMsgReturnVoid(m_pDetailsWidgetNATNetwork, ("NAT network details-widget isn't created!\n"));
+
+ /* If there is an item => update details data: */
+ if (pItem)
+ {
+ QStringList busyNamesForItem = busyNamesNAT();
+ busyNamesForItem.removeAll(pItem->name());
+ m_pDetailsWidgetNATNetwork->setData(*pItem, busyNamesForItem, fHoldPosition);
+ }
+ /* Otherwise => clear details: */
+ else
+ m_pDetailsWidgetNATNetwork->setData(UIDataNATNetwork());
+}
+
+void UINetworkManagerWidget::sltHandleCurrentItemChangeNATNetwork()
+{
+ sltHandleCurrentItemChangeNATNetworkHoldingPosition(false /* hold position? */);
+}
+
+void UINetworkManagerWidget::sltHandleContextMenuRequestNATNetwork(const QPoint &position)
+{
+ /* Check NAT network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetNATNetwork, ("NAT network tree-widget isn't created!\n"));
+
+ /* Compose temporary context-menu: */
+ QMenu menu;
+ if (m_pTreeWidgetNATNetwork->itemAt(position))
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_T_Details));
+ }
+ else
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Create));
+// menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Refresh));
+ }
+ /* And show it: */
+ menu.exec(m_pTreeWidgetNATNetwork->mapToGlobal(position));
+}
+
+void UINetworkManagerWidget::sltApplyDetailsChangesNATNetwork()
+{
+ /* Check NAT network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetNATNetwork, ("NAT network tree-widget isn't created!\n"));
+
+ /* Get NAT network item: */
+ UIItemNATNetwork *pItem = static_cast<UIItemNATNetwork*>(m_pTreeWidgetNATNetwork->currentItem());
+ AssertMsgReturnVoid(pItem, ("Current item must not be null!\n"));
+
+ /* Check NAT network details-widget: */
+ AssertMsgReturnVoid(m_pDetailsWidgetNATNetwork, ("NAT network details-widget isn't created!\n"));
+
+ /* Revalidate NAT network details: */
+ if (m_pDetailsWidgetNATNetwork->revalidate())
+ {
+ /* Get item data: */
+ UIDataNATNetwork oldData = *pItem;
+ UIDataNATNetwork newData = m_pDetailsWidgetNATNetwork->data();
+
+ /* Get VirtualBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding network: */
+ CNATNetwork comNetwork = comVBox.FindNATNetworkByName(oldData.m_strName);
+ CNATNetwork comNetworkBase = comNetwork;
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk() || comNetwork.isNull())
+ UINotificationMessage::cannotFindNATNetwork(comVBox, oldData.m_strName);
+ else
+ {
+ /* Save NAT network name: */
+ if (comNetwork.isOk() && newData.m_strName != oldData.m_strName)
+ comNetwork.SetNetworkName(newData.m_strName);
+ /* Save NAT network IPv4: */
+ if (comNetwork.isOk() && newData.m_strPrefixIPv4 != oldData.m_strPrefixIPv4)
+ comNetwork.SetNetwork(newData.m_strPrefixIPv4);
+ /* Save NAT network IPv6: */
+ if (comNetwork.isOk() && newData.m_strPrefixIPv6 != oldData.m_strPrefixIPv6)
+ comNetwork.SetIPv6Prefix(newData.m_strPrefixIPv6);
+ /* Save whether NAT network needs DHCP server: */
+ if (comNetwork.isOk() && newData.m_fSupportsDHCP != oldData.m_fSupportsDHCP)
+ comNetwork.SetNeedDhcpServer(newData.m_fSupportsDHCP);
+ /* Save whether NAT network supports IPv6: */
+ if (comNetwork.isOk() && newData.m_fSupportsIPv6 != oldData.m_fSupportsIPv6)
+ comNetwork.SetIPv6Enabled(newData.m_fSupportsIPv6);
+ /* Save whether NAT network should advertise default IPv6 route: */
+ if (comNetwork.isOk() && newData.m_fAdvertiseDefaultIPv6Route != oldData.m_fAdvertiseDefaultIPv6Route)
+ comNetwork.SetAdvertiseDefaultIPv6RouteEnabled(newData.m_fAdvertiseDefaultIPv6Route);
+
+ /* Save IPv4 forwarding rules: */
+ if (comNetwork.isOk() && newData.m_rules4 != oldData.m_rules4)
+ {
+ UIPortForwardingDataList oldRules = oldData.m_rules4;
+
+ /* Remove rules to be removed: */
+ foreach (const UIDataPortForwardingRule &oldRule, oldData.m_rules4)
+ if (comNetwork.isOk() && !newData.m_rules4.contains(oldRule))
+ {
+ comNetwork.RemovePortForwardRule(false /* IPv6? */, oldRule.name);
+ oldRules.removeAll(oldRule);
+ }
+ /* Add rules to be added: */
+ foreach (const UIDataPortForwardingRule &newRule, newData.m_rules4)
+ if (comNetwork.isOk() && !oldRules.contains(newRule))
+ {
+ comNetwork.AddPortForwardRule(false /* IPv6? */, newRule.name, newRule.protocol,
+ newRule.hostIp, newRule.hostPort.value(),
+ newRule.guestIp, newRule.guestPort.value());
+ oldRules.append(newRule);
+ }
+ }
+ /* Save IPv6 forwarding rules: */
+ if (comNetwork.isOk() && newData.m_rules6 != oldData.m_rules6)
+ {
+ UIPortForwardingDataList oldRules = oldData.m_rules6;
+
+ /* Remove rules to be removed: */
+ foreach (const UIDataPortForwardingRule &oldRule, oldData.m_rules6)
+ if (comNetwork.isOk() && !newData.m_rules6.contains(oldRule))
+ {
+ comNetwork.RemovePortForwardRule(true /* IPv6? */, oldRule.name);
+ oldRules.removeAll(oldRule);
+ }
+ /* Add rules to be added: */
+ foreach (const UIDataPortForwardingRule &newRule, newData.m_rules6)
+ if (comNetwork.isOk() && !oldRules.contains(newRule))
+ {
+ comNetwork.AddPortForwardRule(true /* IPv6? */, newRule.name, newRule.protocol,
+ newRule.hostIp, newRule.hostPort.value(),
+ newRule.guestIp, newRule.guestPort.value());
+ oldRules.append(newRule);
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!comNetwork.isOk())
+ UINotificationMessage::cannotChangeNATNetworkParameter(comNetwork);
+
+ /* Update network in the tree: */
+ UIDataNATNetwork data;
+ loadNATNetwork(comNetworkBase, data);
+ updateItemForNATNetwork(data, true, pItem);
+
+ /* Make sure current item fetched, trying to hold chosen position: */
+ sltHandleCurrentItemChangeNATNetworkHoldingPosition(true /* hold position? */);
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+ }
+
+ /* Make sure button states updated: */
+ m_pDetailsWidgetNATNetwork->updateButtonStates();
+}
+
+void UINetworkManagerWidget::sltHandleCurrentItemChangeCloudNetwork()
+{
+ /* Update actions: */
+ updateActionAvailability();
+
+ /* Check cloud network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetCloudNetwork, ("Cloud network tree-widget isn't created!\n"));
+
+ /* Get network item: */
+ UIItemCloudNetwork *pItem = static_cast<UIItemCloudNetwork*>(m_pTreeWidgetCloudNetwork->currentItem());
+
+ /* Check Cloud network details-widget: */
+ AssertMsgReturnVoid(m_pDetailsWidgetCloudNetwork, ("Cloud network details-widget isn't created!\n"));
+
+ /* If there is an item => update details data: */
+ if (pItem)
+ {
+ QStringList busyNamesForItem = busyNamesCloud();
+ busyNamesForItem.removeAll(pItem->name());
+ m_pDetailsWidgetCloudNetwork->setData(*pItem, busyNamesForItem);
+ }
+ /* Otherwise => clear details: */
+ else
+ m_pDetailsWidgetCloudNetwork->setData(UIDataCloudNetwork());
+}
+
+void UINetworkManagerWidget::sltHandleContextMenuRequestCloudNetwork(const QPoint &position)
+{
+ /* Check cloud network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetCloudNetwork, ("Cloud network tree-widget isn't created!\n"));
+
+ /* Compose temporary context-menu: */
+ QMenu menu;
+ if (m_pTreeWidgetCloudNetwork->itemAt(position))
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_T_Details));
+ }
+ else
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Create));
+// menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Refresh));
+ }
+ /* And show it: */
+ menu.exec(m_pTreeWidgetCloudNetwork->mapToGlobal(position));
+}
+
+void UINetworkManagerWidget::sltApplyDetailsChangesCloudNetwork()
+{
+ /* Check cloud network tree-widget: */
+ AssertMsgReturnVoid(m_pTreeWidgetCloudNetwork, ("Cloud network tree-widget isn't created!\n"));
+
+ /* Get Cloud network item: */
+ UIItemCloudNetwork *pItem = static_cast<UIItemCloudNetwork*>(m_pTreeWidgetCloudNetwork->currentItem());
+ AssertMsgReturnVoid(pItem, ("Current item must not be null!\n"));
+
+ /* Check Cloud network details-widget: */
+ AssertMsgReturnVoid(m_pDetailsWidgetCloudNetwork, ("Cloud network details-widget isn't created!\n"));
+
+ /* Revalidate Cloud network details: */
+ if (m_pDetailsWidgetCloudNetwork->revalidate())
+ {
+ /* Get item data: */
+ UIDataCloudNetwork oldData = *pItem;
+ UIDataCloudNetwork newData = m_pDetailsWidgetCloudNetwork->data();
+
+ /* Get VirtualBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding network: */
+ CCloudNetwork comNetwork = comVBox.FindCloudNetworkByName(oldData.m_strName);
+ CCloudNetwork comNetworkBase = comNetwork;
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk() || comNetwork.isNull())
+ UINotificationMessage::cannotFindCloudNetwork(comVBox, oldData.m_strName);
+ else
+ {
+ /* Save whether cloud network enabled: */
+ if (comNetwork.isOk() && newData.m_fEnabled != oldData.m_fEnabled)
+ comNetwork.SetEnabled(newData.m_fEnabled);
+ /* Save cloud network name: */
+ if (comNetwork.isOk() && newData.m_strName != oldData.m_strName)
+ comNetwork.SetNetworkName(newData.m_strName);
+ /* Save cloud provider: */
+ if (comNetwork.isOk() && newData.m_strProvider != oldData.m_strProvider)
+ comNetwork.SetProvider(newData.m_strProvider);
+ /* Save cloud profile: */
+ if (comNetwork.isOk() && newData.m_strProfile != oldData.m_strProfile)
+ comNetwork.SetProfile(newData.m_strProfile);
+ /* Save cloud network id: */
+ if (comNetwork.isOk() && newData.m_strId != oldData.m_strId)
+ comNetwork.SetNetworkId(newData.m_strId);
+
+ /* Show error message if necessary: */
+ if (!comNetwork.isOk())
+ UINotificationMessage::cannotChangeCloudNetworkParameter(comNetwork);
+
+ /* Update network in the tree: */
+ UIDataCloudNetwork data;
+ loadCloudNetwork(comNetworkBase, data);
+ updateItemForCloudNetwork(data, true, pItem);
+
+ /* Make sure current item fetched: */
+ sltHandleCurrentItemChangeCloudNetwork();
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+ }
+
+ /* Make sure button states updated: */
+ m_pDetailsWidgetNATNetwork->updateButtonStates();
+}
+
+void UINetworkManagerWidget::prepare()
+{
+ /* Prepare self: */
+ uiCommon().setHelpKeyword(this, "network-manager");
+
+ /* Prepare stuff: */
+ prepareActions();
+ prepareWidgets();
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Load networks: */
+ loadHostNetworks();
+ loadNATNetworks();
+ loadCloudNetworks();
+}
+
+void UINetworkManagerWidget::prepareActions()
+{
+ /* First of all, add actions which has smaller shortcut scope: */
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Create));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Network_T_Details));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Refresh));
+
+ /* Connect actions: */
+ connect(m_pActionPool->action(UIActionIndexMN_M_Network_S_Create), &QAction::triggered,
+ this, &UINetworkManagerWidget::sltCreateHostNetwork);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Network_S_Create), &QAction::triggered,
+ this, &UINetworkManagerWidget::sltCreateNATNetwork);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Network_S_Create), &QAction::triggered,
+ this, &UINetworkManagerWidget::sltCreateCloudNetwork);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove), &QAction::triggered,
+ this, &UINetworkManagerWidget::sltRemoveHostNetwork);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove), &QAction::triggered,
+ this, &UINetworkManagerWidget::sltRemoveNATNetwork);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove), &QAction::triggered,
+ this, &UINetworkManagerWidget::sltRemoveCloudNetwork);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Network_T_Details), &QAction::toggled,
+ this, &UINetworkManagerWidget::sltToggleDetailsVisibility);
+}
+
+void UINetworkManagerWidget::prepareWidgets()
+{
+ /* Create main-layout: */
+ new QVBoxLayout(this);
+ if (layout())
+ {
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ layout()->setSpacing(10);
+#else
+ layout()->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ /* Prepare toolbar, if requested: */
+ if (m_fShowToolbar)
+ prepareToolBar();
+
+ /* Prepare tab-widget: */
+ prepareTabWidget();
+
+ /* Prepare details widgets: */
+ prepareDetailsWidgetHostNetwork();
+ prepareDetailsWidgetNATNetwork();
+ prepareDetailsWidgetCloudNetwork();
+ }
+}
+
+void UINetworkManagerWidget::prepareToolBar()
+{
+ /* Prepare toolbar: */
+ m_pToolBar = new QIToolBar(parentWidget());
+ if (m_pToolBar)
+ {
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Create));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Network_T_Details));
+
+#ifdef VBOX_WS_MAC
+ /* Check whether we are embedded into a stack: */
+ if (m_enmEmbedding == EmbedTo_Stack)
+ {
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+ }
+#else /* !VBOX_WS_MAC */
+ /* Add into layout: */
+ layout()->addWidget(m_pToolBar);
+#endif /* !VBOX_WS_MAC */
+ }
+}
+
+void UINetworkManagerWidget::prepareTabWidget()
+{
+ /* Create tab-widget: */
+ m_pTabWidget = new QITabWidget(this);
+ if (m_pTabWidget)
+ {
+ connect(m_pTabWidget, &QITabWidget::currentChanged,
+ this, &UINetworkManagerWidget::sltHandleCurrentTabWidgetIndexChange);
+
+ prepareTabHostNetwork();
+ prepareTabNATNetwork();
+ prepareTabCloudNetwork();
+
+ /* Add into layout: */
+ layout()->addWidget(m_pTabWidget);
+ }
+}
+
+void UINetworkManagerWidget::prepareTabHostNetwork()
+{
+ /* Prepare host network tab: */
+ m_pTabHostNetwork = new QWidget(m_pTabWidget);
+ if (m_pTabHostNetwork)
+ {
+ /* Prepare host network layout: */
+ m_pLayoutHostNetwork = new QVBoxLayout(m_pTabHostNetwork);
+ if (m_pLayoutHostNetwork)
+ prepareTreeWidgetHostNetwork();
+
+ /* Add into tab-widget: */
+ m_pTabWidget->insertTab(TabWidgetIndex_HostNetwork, m_pTabHostNetwork, QString());
+ }
+}
+
+void UINetworkManagerWidget::prepareTreeWidgetHostNetwork()
+{
+ /* Prepare host network tree-widget: */
+ m_pTreeWidgetHostNetwork = new QITreeWidget(m_pTabHostNetwork);
+ if (m_pTreeWidgetHostNetwork)
+ {
+ m_pTreeWidgetHostNetwork->setRootIsDecorated(false);
+ m_pTreeWidgetHostNetwork->setAlternatingRowColors(true);
+ m_pTreeWidgetHostNetwork->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTreeWidgetHostNetwork->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTreeWidgetHostNetwork->setColumnCount(HostNetworkColumn_Max);
+ m_pTreeWidgetHostNetwork->setSortingEnabled(true);
+ m_pTreeWidgetHostNetwork->sortByColumn(HostNetworkColumn_Name, Qt::AscendingOrder);
+ m_pTreeWidgetHostNetwork->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
+ connect(m_pTreeWidgetHostNetwork, &QITreeWidget::currentItemChanged,
+ this, &UINetworkManagerWidget::sltHandleCurrentItemChangeHostNetwork);
+ connect(m_pTreeWidgetHostNetwork, &QITreeWidget::customContextMenuRequested,
+ this, &UINetworkManagerWidget::sltHandleContextMenuRequestHostNetwork);
+ connect(m_pTreeWidgetHostNetwork, &QITreeWidget::itemDoubleClicked,
+ m_pActionPool->action(UIActionIndexMN_M_Network_T_Details), &QAction::setChecked);
+
+ /* Add into layout: */
+ m_pLayoutHostNetwork->addWidget(m_pTreeWidgetHostNetwork);
+ }
+}
+
+void UINetworkManagerWidget::prepareDetailsWidgetHostNetwork()
+{
+ /* Prepare host network details-widget: */
+ m_pDetailsWidgetHostNetwork = new UIDetailsWidgetHostNetwork(m_enmEmbedding, this);
+ if (m_pDetailsWidgetHostNetwork)
+ {
+ m_pDetailsWidgetHostNetwork->setVisible(false);
+ m_pDetailsWidgetHostNetwork->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ connect(m_pDetailsWidgetHostNetwork, &UIDetailsWidgetHostNetwork::sigDataChanged,
+ this, &UINetworkManagerWidget::sigDetailsDataChangedHostNetwork);
+ connect(m_pDetailsWidgetHostNetwork, &UIDetailsWidgetHostNetwork::sigDataChangeRejected,
+ this, &UINetworkManagerWidget::sltHandleCurrentItemChangeHostNetwork);
+ connect(m_pDetailsWidgetHostNetwork, &UIDetailsWidgetHostNetwork::sigDataChangeAccepted,
+ this, &UINetworkManagerWidget::sltApplyDetailsChangesHostNetwork);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pDetailsWidgetHostNetwork);
+ }
+}
+
+void UINetworkManagerWidget::prepareTabNATNetwork()
+{
+ /* Prepare NAT network tab: */
+ m_pTabNATNetwork = new QWidget(m_pTabWidget);
+ if (m_pTabNATNetwork)
+ {
+ /* Prepare NAT network layout: */
+ m_pLayoutNATNetwork = new QVBoxLayout(m_pTabNATNetwork);
+ if (m_pLayoutNATNetwork)
+ prepareTreeWidgetNATNetwork();
+
+ /* Add into tab-widget: */
+ m_pTabWidget->insertTab(TabWidgetIndex_NATNetwork, m_pTabNATNetwork, QString());
+ }
+}
+
+void UINetworkManagerWidget::prepareTreeWidgetNATNetwork()
+{
+ /* Prepare NAT network tree-widget: */
+ m_pTreeWidgetNATNetwork = new QITreeWidget(m_pTabNATNetwork);
+ if (m_pTreeWidgetNATNetwork)
+ {
+ m_pTreeWidgetNATNetwork->setRootIsDecorated(false);
+ m_pTreeWidgetNATNetwork->setAlternatingRowColors(true);
+ m_pTreeWidgetNATNetwork->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTreeWidgetNATNetwork->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTreeWidgetNATNetwork->setColumnCount(NATNetworkColumn_Max);
+ m_pTreeWidgetNATNetwork->setSortingEnabled(true);
+ m_pTreeWidgetNATNetwork->sortByColumn(NATNetworkColumn_Name, Qt::AscendingOrder);
+ m_pTreeWidgetNATNetwork->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
+ connect(m_pTreeWidgetNATNetwork, &QITreeWidget::currentItemChanged,
+ this, &UINetworkManagerWidget::sltHandleCurrentItemChangeNATNetwork);
+ connect(m_pTreeWidgetNATNetwork, &QITreeWidget::customContextMenuRequested,
+ this, &UINetworkManagerWidget::sltHandleContextMenuRequestNATNetwork);
+ connect(m_pTreeWidgetNATNetwork, &QITreeWidget::itemDoubleClicked,
+ m_pActionPool->action(UIActionIndexMN_M_Network_T_Details), &QAction::setChecked);
+
+ /* Add into layout: */
+ m_pLayoutNATNetwork->addWidget(m_pTreeWidgetNATNetwork);
+ }
+}
+
+void UINetworkManagerWidget::prepareDetailsWidgetNATNetwork()
+{
+ /* Prepare NAT network details-widget: */
+ m_pDetailsWidgetNATNetwork = new UIDetailsWidgetNATNetwork(m_enmEmbedding, this);
+ if (m_pDetailsWidgetNATNetwork)
+ {
+ m_pDetailsWidgetNATNetwork->setVisible(false);
+ m_pDetailsWidgetNATNetwork->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ connect(m_pDetailsWidgetNATNetwork, &UIDetailsWidgetNATNetwork::sigDataChanged,
+ this, &UINetworkManagerWidget::sigDetailsDataChangedNATNetwork);
+ connect(m_pDetailsWidgetNATNetwork, &UIDetailsWidgetNATNetwork::sigDataChangeRejected,
+ this, &UINetworkManagerWidget::sltHandleCurrentItemChangeNATNetwork);
+ connect(m_pDetailsWidgetNATNetwork, &UIDetailsWidgetNATNetwork::sigDataChangeAccepted,
+ this, &UINetworkManagerWidget::sltApplyDetailsChangesNATNetwork);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pDetailsWidgetNATNetwork);
+ }
+}
+
+void UINetworkManagerWidget::prepareTabCloudNetwork()
+{
+ /* Prepare cloud network tab: */
+ m_pTabCloudNetwork = new QWidget(m_pTabWidget);
+ if (m_pTabCloudNetwork)
+ {
+ /* Prepare cloud network layout: */
+ m_pLayoutCloudNetwork = new QVBoxLayout(m_pTabCloudNetwork);
+ if (m_pLayoutCloudNetwork)
+ prepareTreeWidgetCloudNetwork();
+
+ /* Add into tab-widget: */
+ m_pTabWidget->insertTab(TabWidgetIndex_CloudNetwork, m_pTabCloudNetwork, QString());
+ }
+}
+
+void UINetworkManagerWidget::prepareTreeWidgetCloudNetwork()
+{
+ /* Prepare cloud network tree-widget: */
+ m_pTreeWidgetCloudNetwork = new QITreeWidget(m_pTabCloudNetwork);
+ if (m_pTreeWidgetCloudNetwork)
+ {
+ m_pTreeWidgetCloudNetwork->setRootIsDecorated(false);
+ m_pTreeWidgetCloudNetwork->setAlternatingRowColors(true);
+ m_pTreeWidgetCloudNetwork->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTreeWidgetCloudNetwork->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTreeWidgetCloudNetwork->setColumnCount(CloudNetworkColumn_Max);
+ m_pTreeWidgetCloudNetwork->setSortingEnabled(true);
+ m_pTreeWidgetCloudNetwork->sortByColumn(CloudNetworkColumn_Name, Qt::AscendingOrder);
+ m_pTreeWidgetCloudNetwork->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
+ connect(m_pTreeWidgetCloudNetwork, &QITreeWidget::currentItemChanged,
+ this, &UINetworkManagerWidget::sltHandleCurrentItemChangeCloudNetwork);
+ connect(m_pTreeWidgetCloudNetwork, &QITreeWidget::customContextMenuRequested,
+ this, &UINetworkManagerWidget::sltHandleContextMenuRequestCloudNetwork);
+ connect(m_pTreeWidgetCloudNetwork, &QITreeWidget::itemDoubleClicked,
+ m_pActionPool->action(UIActionIndexMN_M_Network_T_Details), &QAction::setChecked);
+
+ /* Add into layout: */
+ m_pLayoutCloudNetwork->addWidget(m_pTreeWidgetCloudNetwork);
+ }
+}
+
+void UINetworkManagerWidget::prepareDetailsWidgetCloudNetwork()
+{
+ /* Prepare cloud network details-widget: */
+ m_pDetailsWidgetCloudNetwork = new UIDetailsWidgetCloudNetwork(m_enmEmbedding, this);
+ if (m_pDetailsWidgetCloudNetwork)
+ {
+ m_pDetailsWidgetCloudNetwork->setVisible(false);
+ m_pDetailsWidgetCloudNetwork->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ connect(m_pDetailsWidgetCloudNetwork, &UIDetailsWidgetCloudNetwork::sigDataChanged,
+ this, &UINetworkManagerWidget::sigDetailsDataChangedCloudNetwork);
+ connect(m_pDetailsWidgetCloudNetwork, &UIDetailsWidgetCloudNetwork::sigDataChangeRejected,
+ this, &UINetworkManagerWidget::sltHandleCurrentItemChangeCloudNetwork);
+ connect(m_pDetailsWidgetCloudNetwork, &UIDetailsWidgetCloudNetwork::sigDataChangeAccepted,
+ this, &UINetworkManagerWidget::sltApplyDetailsChangesCloudNetwork);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pDetailsWidgetCloudNetwork);
+ }
+}
+
+void UINetworkManagerWidget::loadSettings()
+{
+ /* Details action/widget: */
+ if (m_pActionPool)
+ {
+ m_pActionPool->action(UIActionIndexMN_M_Network_T_Details)->setChecked(gEDataManager->hostNetworkManagerDetailsExpanded());
+ sltToggleDetailsVisibility(m_pActionPool->action(UIActionIndexMN_M_Network_T_Details)->isChecked());
+ }
+}
+
+void UINetworkManagerWidget::loadHostNetworks()
+{
+ /* Check host network tree-widget: */
+ if (!m_pTreeWidgetHostNetwork)
+ return;
+
+ /* Clear tree first of all: */
+ m_pTreeWidgetHostNetwork->clear();
+
+#ifdef VBOX_WS_MAC
+ /* Get VirtualBox for further activities: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Get networks for further activities: */
+ const QVector<CHostOnlyNetwork> networks = comVBox.GetHostOnlyNetworks();
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotAcquireVirtualBoxParameter(comVBox);
+ else
+ {
+ /* For each host network => load it to the tree: */
+ foreach (const CHostOnlyNetwork &comNetwork, networks)
+ {
+ UIDataHostNetwork data;
+ loadHostNetwork(comNetwork, data);
+ createItemForHostNetwork(data, false);
+ }
+
+ /* Choose the 1st item as current initially: */
+ m_pTreeWidgetHostNetwork->setCurrentItem(m_pTreeWidgetHostNetwork->topLevelItem(0));
+ sltHandleCurrentItemChangeHostNetwork();
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+
+#else /* !VBOX_WS_MAC */
+
+ /* Get host for further activities: */
+ const CHost comHost = uiCommon().host();
+
+ /* Get interfaces for further activities: */
+ const QVector<CHostNetworkInterface> interfaces = comHost.GetNetworkInterfaces();
+
+ /* Show error message if necessary: */
+ if (!comHost.isOk())
+ UINotificationMessage::cannotAcquireHostParameter(comHost);
+ else
+ {
+ /* For each host-only interface => load it to the tree: */
+ foreach (const CHostNetworkInterface &comInterface, interfaces)
+ if (comInterface.GetInterfaceType() == KHostNetworkInterfaceType_HostOnly)
+ {
+ UIDataHostNetwork data;
+ loadHostNetwork(comInterface, data);
+ createItemForHostNetwork(data, false);
+ }
+
+ /* Choose the 1st item as current initially: */
+ m_pTreeWidgetHostNetwork->setCurrentItem(m_pTreeWidgetHostNetwork->topLevelItem(0));
+ sltHandleCurrentItemChangeHostNetwork();
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+#endif /* !VBOX_WS_MAC */
+}
+
+#ifdef VBOX_WS_MAC
+void UINetworkManagerWidget::loadHostNetwork(const CHostOnlyNetwork &comNetwork, UIDataHostNetwork &data)
+{
+ /* Gather network settings: */
+ if (comNetwork.isNotNull())
+ data.m_fExists = true;
+ if (comNetwork.isOk())
+ data.m_strName = comNetwork.GetNetworkName();
+ if (comNetwork.isOk())
+ data.m_strMask = comNetwork.GetNetworkMask();
+ if (comNetwork.isOk())
+ data.m_strLBnd = comNetwork.GetLowerIP();
+ if (comNetwork.isOk())
+ data.m_strUBnd = comNetwork.GetUpperIP();
+
+ /* Show error message if necessary: */
+ if (!comNetwork.isOk())
+ UINotificationMessage::cannotAcquireHostOnlyNetworkParameter(comNetwork);
+}
+
+#else /* !VBOX_WS_MAC */
+
+void UINetworkManagerWidget::loadHostNetwork(const CHostNetworkInterface &comInterface, UIDataHostNetwork &data)
+{
+ /* Gather interface settings: */
+ if (comInterface.isNotNull())
+ data.m_interface.m_fExists = true;
+ if (comInterface.isOk())
+ data.m_interface.m_strName = comInterface.GetName();
+ if (comInterface.isOk())
+ data.m_interface.m_fDHCPEnabled = comInterface.GetDHCPEnabled();
+ if (comInterface.isOk())
+ data.m_interface.m_strAddress = comInterface.GetIPAddress();
+ if (comInterface.isOk())
+ data.m_interface.m_strMask = comInterface.GetNetworkMask();
+ if (comInterface.isOk())
+ data.m_interface.m_fSupportedIPv6 = comInterface.GetIPV6Supported();
+ if (comInterface.isOk())
+ data.m_interface.m_strAddress6 = comInterface.GetIPV6Address();
+ if (comInterface.isOk())
+ data.m_interface.m_strPrefixLength6 = QString::number(comInterface.GetIPV6NetworkMaskPrefixLength());
+
+ /* Get host interface network name for further activities: */
+ QString strNetworkName;
+ if (comInterface.isOk())
+ strNetworkName = comInterface.GetNetworkName();
+
+ /* Show error message if necessary: */
+ if (!comInterface.isOk())
+ UINotificationMessage::cannotAcquireHostNetworkInterfaceParameter(comInterface);
+
+ /* Get VBox for further activities: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Find corresponding DHCP server (create if necessary): */
+ CDHCPServer comServer = comVBox.FindDHCPServerByNetworkName(strNetworkName);
+ if (!comVBox.isOk() || comServer.isNull())
+ comServer = comVBox.CreateDHCPServer(strNetworkName);
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk() || comServer.isNull())
+ UINotificationMessage::cannotCreateDHCPServer(comVBox, strNetworkName);
+ else
+ {
+ /* Gather DHCP server settings: */
+ if (comServer.isOk())
+ data.m_dhcpserver.m_fEnabled = comServer.GetEnabled();
+ if (comServer.isOk())
+ data.m_dhcpserver.m_strAddress = comServer.GetIPAddress();
+ if (comServer.isOk())
+ data.m_dhcpserver.m_strMask = comServer.GetNetworkMask();
+ if (comServer.isOk())
+ data.m_dhcpserver.m_strLowerAddress = comServer.GetLowerIP();
+ if (comServer.isOk())
+ data.m_dhcpserver.m_strUpperAddress = comServer.GetUpperIP();
+
+ /* Show error message if necessary: */
+ if (!comServer.isOk())
+ return UINotificationMessage::cannotAcquireDHCPServerParameter(comServer);
+ }
+}
+#endif /* !VBOX_WS_MAC */
+
+void UINetworkManagerWidget::loadNATNetworks()
+{
+ /* Check NAT network tree-widget: */
+ if (!m_pTreeWidgetNATNetwork)
+ return;
+
+ /* Clear tree first of all: */
+ m_pTreeWidgetNATNetwork->clear();
+
+ /* Get VirtualBox for further activities: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Get interfaces for further activities: */
+ const QVector<CNATNetwork> networks = comVBox.GetNATNetworks();
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotAcquireVirtualBoxParameter(comVBox);
+ else
+ {
+ /* For each NAT network => load it to the tree: */
+ foreach (const CNATNetwork &comNetwork, networks)
+ {
+ UIDataNATNetwork data;
+ loadNATNetwork(comNetwork, data);
+ createItemForNATNetwork(data, false);
+ }
+
+ /* Choose the 1st item as current initially: */
+ m_pTreeWidgetNATNetwork->setCurrentItem(m_pTreeWidgetNATNetwork->topLevelItem(0));
+ sltHandleCurrentItemChangeNATNetwork();
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+}
+
+void UINetworkManagerWidget::loadNATNetwork(const CNATNetwork &comNetwork, UIDataNATNetwork &data)
+{
+ /* Gather network settings: */
+ if (comNetwork.isNotNull())
+ data.m_fExists = true;
+ if (comNetwork.isOk())
+ data.m_strName = comNetwork.GetNetworkName();
+ if (comNetwork.isOk())
+ data.m_strPrefixIPv4 = comNetwork.GetNetwork();
+ if (comNetwork.isOk())
+ data.m_strPrefixIPv6 = comNetwork.GetIPv6Prefix();
+ if (comNetwork.isOk())
+ data.m_fSupportsDHCP = comNetwork.GetNeedDhcpServer();
+ if (comNetwork.isOk())
+ data.m_fSupportsIPv6 = comNetwork.GetIPv6Enabled();
+ if (comNetwork.isOk())
+ data.m_fAdvertiseDefaultIPv6Route = comNetwork.GetAdvertiseDefaultIPv6RouteEnabled();
+
+ /* Gather forwarding rules: */
+ if (comNetwork.isOk())
+ {
+ /* Load IPv4 rules: */
+ foreach (QString strIPv4Rule, comNetwork.GetPortForwardRules4())
+ {
+ /* Replace all ':' with ',' first: */
+ strIPv4Rule.replace(':', ',');
+ /* Parse rules: */
+ QStringList rules = strIPv4Rule.split(',');
+ Assert(rules.size() == 6);
+ if (rules.size() != 6)
+ continue;
+ data.m_rules4 << UIDataPortForwardingRule(rules.at(0),
+ gpConverter->fromInternalString<KNATProtocol>(rules.at(1)),
+ QString(rules.at(2)).remove('[').remove(']'),
+ rules.at(3).toUInt(),
+ QString(rules.at(4)).remove('[').remove(']'),
+ rules.at(5).toUInt());
+ }
+
+ /* Load IPv6 rules: */
+ foreach (QString strIPv6Rule, comNetwork.GetPortForwardRules6())
+ {
+ /* Replace all ':' with ',' first: */
+ strIPv6Rule.replace(':', ',');
+ /* But replace ',' back with ':' for addresses: */
+ QRegExp re("\\[[0-9a-fA-F,]*,[0-9a-fA-F,]*\\]");
+ re.setMinimal(true);
+ while (re.indexIn(strIPv6Rule) != -1)
+ {
+ QString strCapOld = re.cap(0);
+ QString strCapNew = strCapOld;
+ strCapNew.replace(',', ':');
+ strIPv6Rule.replace(strCapOld, strCapNew);
+ }
+ /* Parse rules: */
+ QStringList rules = strIPv6Rule.split(',');
+ Assert(rules.size() == 6);
+ if (rules.size() != 6)
+ continue;
+ data.m_rules6 << UIDataPortForwardingRule(rules.at(0),
+ gpConverter->fromInternalString<KNATProtocol>(rules.at(1)),
+ QString(rules.at(2)).remove('[').remove(']'),
+ rules.at(3).toUInt(),
+ QString(rules.at(4)).remove('[').remove(']'),
+ rules.at(5).toUInt());
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!comNetwork.isOk())
+ UINotificationMessage::cannotAcquireNATNetworkParameter(comNetwork);
+}
+
+void UINetworkManagerWidget::loadCloudNetworks()
+{
+ /* Check cloud network tree-widget: */
+ if (!m_pTreeWidgetCloudNetwork)
+ return;
+
+ /* Clear tree first of all: */
+ m_pTreeWidgetCloudNetwork->clear();
+
+ /* Get VirtualBox for further activities: */
+ const CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Get interfaces for further activities: */
+ const QVector<CCloudNetwork> networks = comVBox.GetCloudNetworks();
+
+ /* Show error message if necessary: */
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotAcquireVirtualBoxParameter(comVBox);
+ else
+ {
+ /* For each cloud network => load it to the tree: */
+ foreach (const CCloudNetwork &comNetwork, networks)
+ {
+ UIDataCloudNetwork data;
+ loadCloudNetwork(comNetwork, data);
+ createItemForCloudNetwork(data, false);
+ }
+
+ /* Choose the 1st item as current initially: */
+ m_pTreeWidgetCloudNetwork->setCurrentItem(m_pTreeWidgetCloudNetwork->topLevelItem(0));
+ sltHandleCurrentItemChangeCloudNetwork();
+
+ /* Adjust tree-widgets: */
+ sltAdjustTreeWidgets();
+ }
+}
+
+void UINetworkManagerWidget::loadCloudNetwork(const CCloudNetwork &comNetwork, UIDataCloudNetwork &data)
+{
+ /* Gather network settings: */
+ if (comNetwork.isNotNull())
+ data.m_fExists = true;
+ if (comNetwork.isNotNull())
+ data.m_fEnabled = comNetwork.GetEnabled();
+ if (comNetwork.isOk())
+ data.m_strName = comNetwork.GetNetworkName();
+ if (comNetwork.isOk())
+ data.m_strProvider = comNetwork.GetProvider();
+ if (comNetwork.isOk())
+ data.m_strProfile = comNetwork.GetProfile();
+ if (comNetwork.isOk())
+ data.m_strId = comNetwork.GetNetworkId();
+
+ /* Show error message if necessary: */
+ if (!comNetwork.isOk())
+ UINotificationMessage::cannotAcquireCloudNetworkParameter(comNetwork);
+}
+
+void UINetworkManagerWidget::updateActionAvailability()
+{
+ /* Check which tab we have currently: */
+ switch (m_pTabWidget->currentIndex())
+ {
+ case TabWidgetIndex_HostNetwork:
+ {
+ AssertMsgReturnVoid(m_pTreeWidgetHostNetwork, ("Host network tree-widget isn't created!\n"));
+ UIItemHostNetwork *pItem = static_cast<UIItemHostNetwork*>(m_pTreeWidgetHostNetwork->currentItem());
+ m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove)->setEnabled(pItem);
+ break;
+ }
+ case TabWidgetIndex_NATNetwork:
+ {
+ AssertMsgReturnVoid(m_pTreeWidgetNATNetwork, ("NAT network tree-widget isn't created!\n"));
+ UIItemNATNetwork *pItem = static_cast<UIItemNATNetwork*>(m_pTreeWidgetNATNetwork->currentItem());
+ m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove)->setEnabled(pItem);
+ break;
+ }
+ case TabWidgetIndex_CloudNetwork:
+ {
+ AssertMsgReturnVoid(m_pTreeWidgetCloudNetwork, ("Cloud network tree-widget isn't created!\n"));
+ UIItemCloudNetwork *pItem = static_cast<UIItemCloudNetwork*>(m_pTreeWidgetCloudNetwork->currentItem());
+ m_pActionPool->action(UIActionIndexMN_M_Network_S_Remove)->setEnabled(pItem);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void UINetworkManagerWidget::createItemForHostNetwork(const UIDataHostNetwork &data, bool fChooseItem)
+{
+ /* Prepare new item: */
+ UIItemHostNetwork *pItem = new UIItemHostNetwork;
+ if (pItem)
+ {
+ pItem->UIDataHostNetwork::operator=(data);
+ pItem->updateFields();
+
+ /* Add item to the tree: */
+ m_pTreeWidgetHostNetwork->addTopLevelItem(pItem);
+
+ /* And choose it as current if necessary: */
+ if (fChooseItem)
+ m_pTreeWidgetHostNetwork->setCurrentItem(pItem);
+ }
+}
+
+void UINetworkManagerWidget::updateItemForHostNetwork(const UIDataHostNetwork &data, bool fChooseItem, UIItemHostNetwork *pItem)
+{
+ /* Update passed item: */
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->UIDataHostNetwork::operator=(data);
+ pItem->updateFields();
+ /* And choose it as current if necessary: */
+ if (fChooseItem)
+ m_pTreeWidgetHostNetwork->setCurrentItem(pItem);
+ }
+}
+
+void UINetworkManagerWidget::createItemForNATNetwork(const UIDataNATNetwork &data, bool fChooseItem)
+{
+ /* Create new item: */
+ UIItemNATNetwork *pItem = new UIItemNATNetwork;
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->UIDataNATNetwork::operator=(data);
+ pItem->updateFields();
+ /* Add item to the tree: */
+ m_pTreeWidgetNATNetwork->addTopLevelItem(pItem);
+ /* And choose it as current if necessary: */
+ if (fChooseItem)
+ m_pTreeWidgetNATNetwork->setCurrentItem(pItem);
+ }
+}
+
+void UINetworkManagerWidget::updateItemForNATNetwork(const UIDataNATNetwork &data, bool fChooseItem, UIItemNATNetwork *pItem)
+{
+ /* Update passed item: */
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->UIDataNATNetwork::operator=(data);
+ pItem->updateFields();
+ /* And choose it as current if necessary: */
+ if (fChooseItem)
+ m_pTreeWidgetNATNetwork->setCurrentItem(pItem);
+ }
+}
+
+void UINetworkManagerWidget::createItemForCloudNetwork(const UIDataCloudNetwork &data, bool fChooseItem)
+{
+ /* Create new item: */
+ UIItemCloudNetwork *pItem = new UIItemCloudNetwork;
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->UIDataCloudNetwork::operator=(data);
+ pItem->updateFields();
+ /* Add item to the tree: */
+ m_pTreeWidgetCloudNetwork->addTopLevelItem(pItem);
+ /* And choose it as current if necessary: */
+ if (fChooseItem)
+ m_pTreeWidgetCloudNetwork->setCurrentItem(pItem);
+ }
+}
+
+void UINetworkManagerWidget::updateItemForCloudNetwork(const UIDataCloudNetwork &data, bool fChooseItem, UIItemCloudNetwork *pItem)
+{
+ /* Update passed item: */
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->UIDataCloudNetwork::operator=(data);
+ pItem->updateFields();
+ /* And choose it as current if necessary: */
+ if (fChooseItem)
+ m_pTreeWidgetCloudNetwork->setCurrentItem(pItem);
+ }
+}
+
+#ifdef VBOX_WS_MAC
+QStringList UINetworkManagerWidget::busyNamesHost() const
+{
+ QStringList names;
+ for (int i = 0; i < m_pTreeWidgetHostNetwork->topLevelItemCount(); ++i)
+ {
+ UIItemHostNetwork *pItem = qobject_cast<UIItemHostNetwork*>(m_pTreeWidgetHostNetwork->childItem(i));
+ const QString strItemName(pItem->name());
+ if (!strItemName.isEmpty() && !names.contains(strItemName))
+ names << strItemName;
+ }
+ return names;
+}
+#endif /* VBOX_WS_MAC */
+
+QStringList UINetworkManagerWidget::busyNamesNAT() const
+{
+ QStringList names;
+ for (int i = 0; i < m_pTreeWidgetNATNetwork->topLevelItemCount(); ++i)
+ {
+ UIItemNATNetwork *pItem = qobject_cast<UIItemNATNetwork*>(m_pTreeWidgetNATNetwork->childItem(i));
+ const QString strItemName(pItem->name());
+ if (!strItemName.isEmpty() && !names.contains(strItemName))
+ names << strItemName;
+ }
+ return names;
+}
+
+QStringList UINetworkManagerWidget::busyNamesCloud() const
+{
+ QStringList names;
+ for (int i = 0; i < m_pTreeWidgetCloudNetwork->topLevelItemCount(); ++i)
+ {
+ UIItemCloudNetwork *pItem = qobject_cast<UIItemCloudNetwork*>(m_pTreeWidgetCloudNetwork->childItem(i));
+ const QString strItemName(pItem->name());
+ if (!strItemName.isEmpty() && !names.contains(strItemName))
+ names << strItemName;
+ }
+ return names;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINetworkManagerFactory implementation. *
+*********************************************************************************************************************************/
+
+UINetworkManagerFactory::UINetworkManagerFactory(UIActionPool *pActionPool /* = 0 */)
+ : m_pActionPool(pActionPool)
+{
+}
+
+void UINetworkManagerFactory::create(QIManagerDialog *&pDialog, QWidget *pCenterWidget)
+{
+ pDialog = new UINetworkManager(pCenterWidget, m_pActionPool);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINetworkManager implementation. *
+*********************************************************************************************************************************/
+
+UINetworkManager::UINetworkManager(QWidget *pCenterWidget, UIActionPool *pActionPool)
+ : QIWithRetranslateUI<QIManagerDialog>(pCenterWidget)
+ , m_pActionPool(pActionPool)
+{
+}
+
+void UINetworkManager::sltHandleButtonBoxClick(QAbstractButton *pButton)
+{
+ /* Disable buttons first of all: */
+ button(ButtonType_Reset)->setEnabled(false);
+ button(ButtonType_Apply)->setEnabled(false);
+
+ /* Compare with known buttons: */
+ if (pButton == button(ButtonType_Reset))
+ emit sigDataChangeRejected();
+ else
+ if (pButton == button(ButtonType_Apply))
+ emit sigDataChangeAccepted();
+}
+
+void UINetworkManager::retranslateUi()
+{
+ /* Translate window title: */
+ setWindowTitle(tr("Network Manager"));
+
+ /* Translate buttons: */
+ button(ButtonType_Reset)->setText(tr("Reset"));
+ button(ButtonType_Apply)->setText(tr("Apply"));
+ button(ButtonType_Close)->setText(tr("Close"));
+ button(ButtonType_Help)->setText(tr("Help"));
+ button(ButtonType_Reset)->setStatusTip(tr("Reset changes in current network details"));
+ button(ButtonType_Apply)->setStatusTip(tr("Apply changes in current network details"));
+ button(ButtonType_Close)->setStatusTip(tr("Close dialog without saving"));
+ button(ButtonType_Help)->setStatusTip(tr("Show dialog help"));
+ button(ButtonType_Reset)->setShortcut(QString("Ctrl+Backspace"));
+ button(ButtonType_Apply)->setShortcut(QString("Ctrl+Return"));
+ button(ButtonType_Close)->setShortcut(Qt::Key_Escape);
+ button(ButtonType_Help)->setShortcut(QKeySequence::HelpContents);
+ button(ButtonType_Reset)->setToolTip(tr("Reset Changes (%1)").arg(button(ButtonType_Reset)->shortcut().toString()));
+ button(ButtonType_Apply)->setToolTip(tr("Apply Changes (%1)").arg(button(ButtonType_Apply)->shortcut().toString()));
+ button(ButtonType_Close)->setToolTip(tr("Close Window (%1)").arg(button(ButtonType_Close)->shortcut().toString()));
+ button(ButtonType_Help)->setToolTip(tr("Show Help (%1)").arg(button(ButtonType_Help)->shortcut().toString()));
+}
+
+void UINetworkManager::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/host_iface_manager_32px.png", ":/host_iface_manager_16px.png"));
+#endif
+}
+
+void UINetworkManager::configureCentralWidget()
+{
+ /* Prepare widget: */
+ UINetworkManagerWidget *pWidget = new UINetworkManagerWidget(EmbedTo_Dialog, m_pActionPool, true, this);
+ if (pWidget)
+ {
+ setWidget(pWidget);
+ setWidgetMenu(pWidget->menu());
+#ifdef VBOX_WS_MAC
+ setWidgetToolbar(pWidget->toolbar());
+#endif
+ connect(this, &UINetworkManager::sigDataChangeRejected,
+ pWidget, &UINetworkManagerWidget::sltResetDetailsChanges);
+ connect(this, &UINetworkManager::sigDataChangeAccepted,
+ pWidget, &UINetworkManagerWidget::sltApplyDetailsChanges);
+
+ /* Add into layout: */
+ centralWidget()->layout()->addWidget(pWidget);
+ }
+}
+
+void UINetworkManager::configureButtonBox()
+{
+ /* Configure button-box: */
+ connect(widget(), &UINetworkManagerWidget::sigDetailsVisibilityChanged,
+ button(ButtonType_Apply), &QPushButton::setVisible);
+ connect(widget(), &UINetworkManagerWidget::sigDetailsVisibilityChanged,
+ button(ButtonType_Reset), &QPushButton::setVisible);
+ connect(widget(), &UINetworkManagerWidget::sigDetailsDataChangedHostNetwork,
+ button(ButtonType_Apply), &QPushButton::setEnabled);
+ connect(widget(), &UINetworkManagerWidget::sigDetailsDataChangedHostNetwork,
+ button(ButtonType_Reset), &QPushButton::setEnabled);
+ connect(widget(), &UINetworkManagerWidget::sigDetailsDataChangedNATNetwork,
+ button(ButtonType_Apply), &QPushButton::setEnabled);
+ connect(widget(), &UINetworkManagerWidget::sigDetailsDataChangedNATNetwork,
+ button(ButtonType_Reset), &QPushButton::setEnabled);
+ connect(widget(), &UINetworkManagerWidget::sigDetailsDataChangedCloudNetwork,
+ button(ButtonType_Apply), &QPushButton::setEnabled);
+ connect(widget(), &UINetworkManagerWidget::sigDetailsDataChangedCloudNetwork,
+ button(ButtonType_Reset), &QPushButton::setEnabled);
+ connect(buttonBox(), &QIDialogButtonBox::clicked,
+ this, &UINetworkManager::sltHandleButtonBoxClick);
+ // WORKAROUND:
+ // Since we connected signals later than extra-data loaded
+ // for signals above, we should handle that stuff here again:
+ button(ButtonType_Apply)->setVisible(gEDataManager->hostNetworkManagerDetailsExpanded());
+ button(ButtonType_Reset)->setVisible(gEDataManager->hostNetworkManagerDetailsExpanded());
+}
+
+void UINetworkManager::finalize()
+{
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UINetworkManagerWidget *UINetworkManager::widget()
+{
+ return qobject_cast<UINetworkManagerWidget*>(QIManagerDialog::widget());
+}
+
+
+#include "UINetworkManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManager.h b/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManager.h
new file mode 100644
index 00000000..d89e3d32
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManager.h
@@ -0,0 +1,416 @@
+/* $Id: UINetworkManager.h $ */
+/** @file
+ * VBox Qt GUI - UINetworkManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networkmanager_UINetworkManager_h
+#define FEQT_INCLUDED_SRC_networkmanager_UINetworkManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMainWindow>
+
+/* GUI includes: */
+#include "QIManagerDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class CCloudNetwork;
+#ifdef VBOX_WS_MAC
+class CHostOnlyNetwork;
+#else
+class CHostNetworkInterface;
+#endif
+class CNATNetwork;
+class QAbstractButton;
+class QTreeWidgetItem;
+class QVBoxLayout;
+class QIDialogButtonBox;
+class QITabWidget;
+class QITreeWidget;
+class UIActionPool;
+class UIDetailsWidgetCloudNetwork;
+class UIDetailsWidgetHostNetwork;
+class UIDetailsWidgetNATNetwork;
+class UIItemCloudNetwork;
+class UIItemHostNetwork;
+class UIItemNATNetwork;
+class QIToolBar;
+struct UIDataCloudNetwork;
+struct UIDataHostNetwork;
+struct UIDataNATNetwork;
+
+
+/** QWidget extension providing GUI with the pane to control network related functionality. */
+class UINetworkManagerWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about details-widget @a fVisible. */
+ void sigDetailsVisibilityChanged(bool fVisible);
+
+ /** Notifies listeners about host network details data @a fDiffers. */
+ void sigDetailsDataChangedHostNetwork(bool fDiffers);
+ /** Notifies listeners about NAT network details data @a fDiffers. */
+ void sigDetailsDataChangedNATNetwork(bool fDiffers);
+ /** Notifies listeners about cloud network details data @a fDiffers. */
+ void sigDetailsDataChangedCloudNetwork(bool fDiffers);
+
+public:
+
+ /** Constructs Network Manager widget.
+ * @param enmEmbedding Brings the type of widget embedding.
+ * @param pActionPool Brings the action-pool reference.
+ * @param fShowToolbar Brings whether we should create/show toolbar. */
+ UINetworkManagerWidget(EmbedTo enmEmbedding, UIActionPool *pActionPool,
+ bool fShowToolbar = true, QWidget *pParent = 0);
+
+ /** Returns the menu. */
+ QMenu *menu() const;
+
+#ifdef VBOX_WS_MAC
+ /** Returns the toolbar. */
+ QIToolBar *toolbar() const { return m_pToolBar; }
+#endif
+
+protected:
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+public slots:
+
+ /** @name Details-widget stuff.
+ * @{ */
+ /** Handles command to reset details changes. */
+ void sltResetDetailsChanges();
+ /** Handles command to apply details changes. */
+ void sltApplyDetailsChanges();
+ /** @} */
+
+private slots:
+
+ /** @name Menu/action stuff.
+ * @{ */
+ /** Handles command to create host network. */
+ void sltCreateHostNetwork();
+#ifndef VBOX_WS_MAC
+ /** Handles signal about host network @a comInterface created. */
+ void sigHandleHostOnlyNetworkInterfaceCreated(const CHostNetworkInterface &comInterface);
+#endif
+ /** Handles command to remove host network. */
+ void sltRemoveHostNetwork();
+#ifndef VBOX_WS_MAC
+ /** Handles signal about host network interface by the name of @a strInterfaceName was removed. */
+ void sigHandleHostOnlyNetworkInterfaceRemoved(const QString &strInterfaceName);
+#endif
+
+ /** Handles command to create NAT network. */
+ void sltCreateNATNetwork();
+ /** Handles command to remove NAT network. */
+ void sltRemoveNATNetwork();
+
+ /** Handles command to create cloud network. */
+ void sltCreateCloudNetwork();
+ /** Handles command to remove cloud network. */
+ void sltRemoveCloudNetwork();
+
+ /** Handles command to make details @a fVisible. */
+ void sltToggleDetailsVisibility(bool fVisible);
+ /** @} */
+
+ /** @name Tree-widget stuff.
+ * @{ */
+ /** Handles current tab-widget's index change. */
+ void sltHandleCurrentTabWidgetIndexChange();
+
+ /** Handles command to adjust tree-widget. */
+ void sltAdjustTreeWidgets();
+
+ /** Handles host network tree-widget current item change. */
+ void sltHandleCurrentItemChangeHostNetwork();
+ /** Handles host network context-menu request for tree-widget @a position. */
+ void sltHandleContextMenuRequestHostNetwork(const QPoint &position);
+ /** Handles command to apply host network details changes. */
+ void sltApplyDetailsChangesHostNetwork();
+
+ /** Handles NAT network tree-widget current item change.
+ * @param fHoldPosition Holds whether we should try to keep
+ * port forwarding rule position intact. */
+ void sltHandleCurrentItemChangeNATNetworkHoldingPosition(bool fHoldPosition);
+ /** Handles NAT network tree-widget current item change. */
+ void sltHandleCurrentItemChangeNATNetwork();
+ /** Handles NAT network context-menu request for tree-widget @a position. */
+ void sltHandleContextMenuRequestNATNetwork(const QPoint &position);
+ /** Handles command to apply NAT network details changes. */
+ void sltApplyDetailsChangesNATNetwork();
+
+ /** Handles cloud network tree-widget current item change. */
+ void sltHandleCurrentItemChangeCloudNetwork();
+ /** Handles cloud network context-menu request for tree-widget @a position. */
+ void sltHandleContextMenuRequestCloudNetwork(const QPoint &position);
+ /** Handles command to apply cloud network details changes. */
+ void sltApplyDetailsChangesCloudNetwork();
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares actions. */
+ void prepareActions();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares toolbar. */
+ void prepareToolBar();
+ /** Prepares tab-widget. */
+ void prepareTabWidget();
+ /** Prepares host network tab. */
+ void prepareTabHostNetwork();
+ /** Prepares host network tree-widget. */
+ void prepareTreeWidgetHostNetwork();
+ /** Prepares host network details-widget. */
+ void prepareDetailsWidgetHostNetwork();
+ /** Prepares NAT network tab. */
+ void prepareTabNATNetwork();
+ /** Prepares NAT network tree-widget. */
+ void prepareTreeWidgetNATNetwork();
+ /** Prepares NAT network details-widget. */
+ void prepareDetailsWidgetNATNetwork();
+ /** Prepares cloud network tab. */
+ void prepareTabCloudNetwork();
+ /** Prepares cloud network tree-widget. */
+ void prepareTreeWidgetCloudNetwork();
+ /** Prepares cloud network details-widget. */
+ void prepareDetailsWidgetCloudNetwork();
+ /** Load settings: */
+ void loadSettings();
+ /** @} */
+
+ /** @name Loading stuff.
+ * @{ */
+ /** Loads host networks. */
+ void loadHostNetworks();
+#ifdef VBOX_WS_MAC
+ /** Loads host @a comNetwork data to passed @a data container. */
+ void loadHostNetwork(const CHostOnlyNetwork &comNetwork, UIDataHostNetwork &data);
+#else
+ /** Loads host @a comInterface data to passed @a data container. */
+ void loadHostNetwork(const CHostNetworkInterface &comInterface, UIDataHostNetwork &data);
+#endif
+
+ /** Loads NAT networks. */
+ void loadNATNetworks();
+ /** Loads NAT @a comNetwork data to passed @a data container. */
+ void loadNATNetwork(const CNATNetwork &comNetwork, UIDataNATNetwork &data);
+
+ /** Loads cloud networks. */
+ void loadCloudNetworks();
+ /** Loads cloud @a comNetwork data to passed @a data container. */
+ void loadCloudNetwork(const CCloudNetwork &comNetwork, UIDataCloudNetwork &data);
+ /** @} */
+
+ /** @name Actions stuff.
+ * @{ */
+ /** Updates action availability. */
+ void updateActionAvailability();
+ /** @} */
+
+ /** @name Tree-widget stuff.
+ * @{ */
+ /** Creates a new host network tree-widget item on the basis of passed @a data, @a fChooseItem if requested. */
+ void createItemForHostNetwork(const UIDataHostNetwork &data, bool fChooseItem);
+ /** Updates passed host network tree-widget item on the basis of passed @a data, @a fChooseItem if requested. */
+ void updateItemForHostNetwork(const UIDataHostNetwork &data, bool fChooseItem, UIItemHostNetwork *pItem);
+
+ /** Creates a new NAT network tree-widget item on the basis of passed @a data, @a fChooseItem if requested. */
+ void createItemForNATNetwork(const UIDataNATNetwork &data, bool fChooseItem);
+ /** Updates passed NAT network tree-widget item on the basis of passed @a data, @a fChooseItem if requested. */
+ void updateItemForNATNetwork(const UIDataNATNetwork &data, bool fChooseItem, UIItemNATNetwork *pItem);
+
+ /** Creates a new cloud network tree-widget item on the basis of passed @a data, @a fChooseItem if requested. */
+ void createItemForCloudNetwork(const UIDataCloudNetwork &data, bool fChooseItem);
+ /** Updates passed cloud network tree-widget item on the basis of passed @a data, @a fChooseItem if requested. */
+ void updateItemForCloudNetwork(const UIDataCloudNetwork &data, bool fChooseItem, UIItemCloudNetwork *pItem);
+
+#ifdef VBOX_WS_MAC
+ /** Returns a list of busy host network names. */
+ QStringList busyNamesHost() const;
+#endif
+ /** Returns a list of busy NAT network names. */
+ QStringList busyNamesNAT() const;
+ /** Returns a list of busy cloud network names. */
+ QStringList busyNamesCloud() const;
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the widget embedding type. */
+ const EmbedTo m_enmEmbedding;
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** Holds whether we should create/show toolbar. */
+ const bool m_fShowToolbar;
+ /** @} */
+
+ /** @name Toolbar and menu variables.
+ * @{ */
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolBar;
+ /** @} */
+
+ /** @name Splitter variables.
+ * @{ */
+ /** Holds the tab-widget instance. */
+ QITabWidget *m_pTabWidget;
+
+ /** Holds the host network tab. */
+ QWidget *m_pTabHostNetwork;
+ /** Holds the host network layout. */
+ QVBoxLayout *m_pLayoutHostNetwork;
+ /** Holds the host network tree-widget instance. */
+ QITreeWidget *m_pTreeWidgetHostNetwork;
+ /** Holds the host network details-widget instance. */
+ UIDetailsWidgetHostNetwork *m_pDetailsWidgetHostNetwork;
+
+ /** Holds the NAT network tab. */
+ QWidget *m_pTabNATNetwork;
+ /** Holds the NAT network tab layout. */
+ QVBoxLayout *m_pLayoutNATNetwork;
+ /** Holds the NAT network tree-widget instance. */
+ QITreeWidget *m_pTreeWidgetNATNetwork;
+ /** Holds the NAT network details-widget instance. */
+ UIDetailsWidgetNATNetwork *m_pDetailsWidgetNATNetwork;
+
+ /** Holds the cloud network tab. */
+ QWidget *m_pTabCloudNetwork;
+ /** Holds the cloud network tab layout. */
+ QVBoxLayout *m_pLayoutCloudNetwork;
+ /** Holds the cloud network tree-widget instance. */
+ QITreeWidget *m_pTreeWidgetCloudNetwork;
+ /** Holds the cloud network details-widget instance. */
+ UIDetailsWidgetCloudNetwork *m_pDetailsWidgetCloudNetwork;
+ /** @} */
+};
+
+
+/** QIManagerDialogFactory extension used as a factory for Network Manager dialog. */
+class UINetworkManagerFactory : public QIManagerDialogFactory
+{
+public:
+
+ /** Constructs Media Manager factory acquiring additional arguments.
+ * @param pActionPool Brings the action-pool reference. */
+ UINetworkManagerFactory(UIActionPool *pActionPool = 0);
+
+protected:
+
+ /** Creates derived @a pDialog instance.
+ * @param pCenterWidget Brings the widget reference to center according to. */
+ virtual void create(QIManagerDialog *&pDialog, QWidget *pCenterWidget) RT_OVERRIDE;
+
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+};
+
+
+/** QIManagerDialog extension providing GUI with the dialog to control network related functionality. */
+class UINetworkManager : public QIWithRetranslateUI<QIManagerDialog>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data change rejected and should be reseted. */
+ void sigDataChangeRejected();
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+private slots:
+
+ /** @name Button-box stuff.
+ * @{ */
+ /** Handles button-box button click. */
+ void sltHandleButtonBoxClick(QAbstractButton *pButton);
+ /** @} */
+
+private:
+
+ /** Constructs Network Manager dialog.
+ * @param pCenterWidget Brings the widget reference to center according to.
+ * @param pActionPool Brings the action-pool reference. */
+ UINetworkManager(QWidget *pCenterWidget, UIActionPool *pActionPool);
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ virtual void configure() RT_OVERRIDE;
+ /** Configures central-widget. */
+ virtual void configureCentralWidget() RT_OVERRIDE;
+ /** Configures button-box. */
+ virtual void configureButtonBox() RT_OVERRIDE;
+ /** Perform final preparations. */
+ virtual void finalize() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Widget stuff.
+ * @{ */
+ /** Returns the widget. */
+ virtual UINetworkManagerWidget *widget() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Action related variables.
+ * @{ */
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** @} */
+
+ /** Allow factory access to private/protected members: */
+ friend class UINetworkManagerFactory;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_networkmanager_UINetworkManager_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManagerUtils.cpp b/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManagerUtils.cpp
new file mode 100644
index 00000000..3f640dcc
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManagerUtils.cpp
@@ -0,0 +1,152 @@
+/* $Id: UINetworkManagerUtils.cpp $ */
+/** @file
+ * VBox Qt GUI - UINetworkManagerUtils namespace implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UINetworkManagerUtils.h"
+
+
+quint32 UINetworkManagerUtils::ipv4FromQStringToQuint32(const QString &strAddress)
+{
+ quint32 uAddress = 0;
+ foreach (const QString &strPart, strAddress.split('.'))
+ {
+ uAddress = uAddress << 8;
+ bool fOk = false;
+ uint uPart = strPart.toUInt(&fOk);
+ if (fOk)
+ uAddress += uPart;
+ }
+ return uAddress;
+}
+
+QString UINetworkManagerUtils::ipv4FromQuint32ToQString(quint32 uAddress)
+{
+ QStringList address;
+ while (uAddress)
+ {
+ uint uPart = uAddress & 0xFF;
+ address.prepend(QString::number(uPart));
+ uAddress = uAddress >> 8;
+ }
+ return address.join('.');
+}
+
+quint32 UINetworkManagerUtils::incrementNetworkAddress(quint32 uAddress)
+{
+ return advanceNetworkAddress(uAddress, true /* forward */);
+}
+
+quint32 UINetworkManagerUtils::decrementNetworkAddress(quint32 uAddress)
+{
+ return advanceNetworkAddress(uAddress, false /* forward */);
+}
+
+quint32 UINetworkManagerUtils::advanceNetworkAddress(quint32 uAddress, bool fForward)
+{
+ /* Success by default: */
+ bool fSuccess = true;
+ do
+ {
+ /* Just advance address: */
+ if (fForward)
+ ++uAddress;
+ else
+ --uAddress;
+ /* And treat it as success initially: */
+ fSuccess = true;
+ /* Iterate the resulting bytes: */
+ uint uByteIndex = 0;
+ quint32 uIterator = uAddress;
+ while (fSuccess && uIterator)
+ {
+ /* Get current byte: */
+ const quint32 uCurrentByte = uIterator & 0xFF;
+ /* Advance iterator early: */
+ uIterator = uIterator >> 8;
+ // We know that .0. and .255. are legal these days
+ // but still prefer to exclude them from
+ // being proposed to an end user.
+ /* If current byte equal to 255
+ * or first byte equal to 0,
+ * let's try again: */
+ if ( uCurrentByte == 0xFF
+ || (uCurrentByte == 0x00 && uByteIndex == 0))
+ fSuccess = false;
+ /* Advance byte index: */
+ ++uByteIndex;
+ }
+ }
+ while (!fSuccess);
+ return uAddress;
+}
+
+QStringList UINetworkManagerUtils::makeDhcpServerProposal(const QString &strInterfaceAddress, const QString &strInterfaceMask)
+{
+ /* Convert interface address/mask into digital form and calculate inverted interface mask: */
+ const quint32 uAddress = ipv4FromQStringToQuint32(strInterfaceAddress);
+ const quint32 uMaskDirect = ipv4FromQStringToQuint32(strInterfaceMask);
+ const quint32 uMaskInvert = ~uMaskDirect;
+ //printf("Direct mask: %s (%u)\n", ipv4FromQuint32ToQString(uMaskDirect).toUtf8().constData(), uMaskDirect);
+ //printf("Inverted mask: %s (%u)\n", ipv4FromQuint32ToQString(uMaskInvert).toUtf8().constData(), uMaskInvert);
+
+ /* Split the interface address into left and right parts: */
+ const quint32 uPartL = uAddress & uMaskDirect;
+ const quint32 uPartR = uAddress & uMaskInvert;
+ //printf("Left part: %s (%u)\n", ipv4FromQuint32ToQString(uPartL).toUtf8().constData(), uPartL);
+ //printf("Right part: %s (%u)\n", ipv4FromQuint32ToQString(uPartR).toUtf8().constData(), uPartR);
+
+ /* Prepare DHCP server proposal:" */
+ quint32 uServerProposedAddress = 0;
+ quint32 uServerProposedAddressL = 0;
+ quint32 uServerProposedAddressU = 0;
+ if (uPartR < uMaskInvert / 2)
+ {
+ /* Make DHCP server proposal from right scope: */
+ //printf("Make DHCP server proposal from right scope:\n");
+ uServerProposedAddress = uPartL + incrementNetworkAddress(uPartR);
+ uServerProposedAddressL = uPartL + incrementNetworkAddress(incrementNetworkAddress(uPartR));
+ uServerProposedAddressU = uPartL + (uMaskInvert & 0xFEFEFEFE) /* decrementNetworkAddress(uMaskInvert) */;
+ }
+ else
+ {
+ /* Make DHCP server proposal from left scope: */
+ //printf("Make DHCP server proposal from left scope:\n");
+ uServerProposedAddress = uPartL + 1 /* incrementNetworkAddress(0) */;
+ uServerProposedAddressL = uPartL + 2 /* incrementNetworkAddress(incrementNetworkAddress(0)) */;
+ uServerProposedAddressU = uPartL + decrementNetworkAddress(uPartR);
+ }
+ //printf("DHCP server address: %s (%u)\n", ipv4FromQuint32ToQString(uServerProposedAddress).toUtf8().constData(), uServerProposedAddress);
+ //printf("DHCP server lower address: %s (%u)\n", ipv4FromQuint32ToQString(uServerProposedAddressL).toUtf8().constData(), uServerProposedAddressL);
+ //printf("DHCP server upper address: %s (%u)\n", ipv4FromQuint32ToQString(uServerProposedAddressU).toUtf8().constData(), uServerProposedAddressU);
+
+ /* Pack and return result: */
+ return QStringList() << ipv4FromQuint32ToQString(uServerProposedAddress)
+ << ipv4FromQuint32ToQString(uMaskDirect)
+ << ipv4FromQuint32ToQString(uServerProposedAddressL)
+ << ipv4FromQuint32ToQString(uServerProposedAddressU);
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManagerUtils.h b/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManagerUtils.h
new file mode 100644
index 00000000..b822b2f3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/networkmanager/UINetworkManagerUtils.h
@@ -0,0 +1,63 @@
+/* $Id: UINetworkManagerUtils.h $ */
+/** @file
+ * VBox Qt GUI - UINetworkManagerUtils namespace declaration.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_networkmanager_UINetworkManagerUtils_h
+#define FEQT_INCLUDED_SRC_networkmanager_UINetworkManagerUtils_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QStringList>
+
+
+/** Network Manager: Host network utilities. */
+namespace UINetworkManagerUtils
+{
+ /** Converts IPv4 address from QString to quint32. */
+ quint32 ipv4FromQStringToQuint32(const QString &strAddress);
+ /** Converts IPv4 address from quint32 to QString. */
+ QString ipv4FromQuint32ToQString(quint32 uAddress);
+
+ /** Increments network @a uAddress by 1 avoiding 0/255 values. */
+ quint32 incrementNetworkAddress(quint32 uAddress);
+ /** Decrements network @a uAddress by 1 avoiding 0/255 values. */
+ quint32 decrementNetworkAddress(quint32 uAddress);
+ /** Advances network @a uAddress by 1 avoiding 0/255 values.
+ * @param fForward Brings whether advance should
+ * go forward or backward otherwise. */
+ quint32 advanceNetworkAddress(quint32 uAddress, bool fForward);
+
+ /** Calculates DHCP server proposal on the basis of the passed @a strInterfaceAddress and @a strInterfaceMask. */
+ QStringList makeDhcpServerProposal(const QString &strInterfaceAddress, const QString &strInterfaceMask);
+}
+
+/* Using this namespace where included: */
+using namespace UINetworkManagerUtils;
+
+#endif /* !FEQT_INCLUDED_SRC_networkmanager_UINetworkManagerUtils_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/notificationcenter/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationCenter.cpp b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationCenter.cpp
new file mode 100644
index 00000000..98f2e43b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationCenter.cpp
@@ -0,0 +1,891 @@
+/* $Id: UINotificationCenter.cpp $ */
+/** @file
+ * VBox Qt GUI - UINotificationCenter class implementation.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QHBoxLayout>
+#include <QMenu>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QPropertyAnimation>
+#include <QScrollArea>
+#include <QSignalTransition>
+#include <QState>
+#include <QStateMachine>
+#include <QStyle>
+#include <QTimer>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UINotificationCenter.h"
+#include "UINotificationObjectItem.h"
+#include "UINotificationModel.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QScrollArea extension to make notification scroll-area more versatile. */
+class UINotificationScrollArea : public QScrollArea
+{
+ Q_OBJECT;
+
+public:
+
+ /** Creates notification scroll-area passing @a pParent to the base-class. */
+ UINotificationScrollArea(QWidget *pParent = 0);
+
+ /** Returns minimum size-hint. */
+ virtual QSize minimumSizeHint() const /* override final */;
+
+ /** Assigns scrollable @a pWidget.
+ * @note Keep in mind that's an override, but NOT a virtual method. */
+ void setWidget(QWidget *pWidget);
+
+protected:
+
+ /** Preprocesses @a pEvent for registered @a pWatched object. */
+ virtual bool eventFilter(QObject *pWatched, QEvent *pEvent) /* override final */;
+};
+
+
+/*********************************************************************************************************************************
+* Class UINotificationScrollArea implementation. *
+*********************************************************************************************************************************/
+
+UINotificationScrollArea::UINotificationScrollArea(QWidget *pParent /* = 0 */)
+ : QScrollArea(pParent)
+{
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+}
+
+QSize UINotificationScrollArea::minimumSizeHint() const
+{
+ /* So, here is the logic,
+ * we are taking width from widget if it's present,
+ * while keeping height calculated by the base-class. */
+ const QSize msh = QScrollArea::minimumSizeHint();
+ return widget() ? QSize(widget()->minimumSizeHint().width(), msh.height()) : msh;
+}
+
+void UINotificationScrollArea::setWidget(QWidget *pWidget)
+{
+ /* We'd like to listen for a new widget's events: */
+ if (widget())
+ widget()->removeEventFilter(this);
+ pWidget->installEventFilter(this);
+
+ /* Call to base-class: */
+ QScrollArea::setWidget(pWidget);
+}
+
+bool UINotificationScrollArea::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* For listened widget: */
+ if (pWatched == widget())
+ {
+ switch (pEvent->type())
+ {
+ /* We'd like to handle layout-request events: */
+ case QEvent::LayoutRequest:
+ updateGeometry();
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Call to base-class: */
+ return QScrollArea::eventFilter(pWatched, pEvent);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationCenter implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UINotificationCenter *UINotificationCenter::s_pInstance = 0;
+
+/* static */
+void UINotificationCenter::create(QWidget *pParent /* = 0 */)
+{
+ AssertReturnVoid(!s_pInstance);
+ s_pInstance = new UINotificationCenter(pParent);
+}
+
+/* static */
+void UINotificationCenter::destroy()
+{
+ AssertPtrReturnVoid(s_pInstance);
+ delete s_pInstance;
+ s_pInstance = 0;
+}
+
+/* static */
+UINotificationCenter *UINotificationCenter::instance()
+{
+ return s_pInstance;
+}
+
+UINotificationCenter::UINotificationCenter(QWidget *pParent)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pModel(0)
+ , m_enmAlignment(Qt::AlignTop)
+ , m_enmOrder(Qt::AscendingOrder)
+ , m_pLayoutMain(0)
+ , m_pLayoutButtons(0)
+ , m_pButtonOpen(0)
+ , m_pButtonToggleSorting(0)
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+ , m_pButtonKeepFinished(0)
+#endif
+ , m_pButtonRemoveFinished(0)
+ , m_pLayoutItems(0)
+ , m_pStateMachineSliding(0)
+ , m_iAnimatedValue(0)
+ , m_pTimerOpen(0)
+ , m_fLastResult(false)
+{
+ prepare();
+}
+
+UINotificationCenter::~UINotificationCenter()
+{
+ cleanup();
+}
+
+void UINotificationCenter::setParent(QWidget *pParent)
+{
+ /* Additionally hide if parent unset: */
+ if (!pParent)
+ setHidden(true);
+
+ /* Uninstall filter from previous parent: */
+ if (parent())
+ parent()->removeEventFilter(this);
+
+ /* Reparent: */
+ QIWithRetranslateUI<QWidget>::setParent(pParent);
+
+ /* Install filter to new parent: */
+ if (parent())
+ parent()->installEventFilter(this);
+
+ /* Show only if there is something to show: */
+ if (parent())
+ setHidden(m_pModel->ids().isEmpty());
+}
+
+void UINotificationCenter::invoke()
+{
+ /* Open if center isn't opened yet: */
+ if (!m_pButtonOpen->isChecked())
+ m_pButtonOpen->animateClick();
+}
+
+QUuid UINotificationCenter::append(UINotificationObject *pObject)
+{
+ /* Sanity check: */
+ AssertPtrReturn(m_pModel, QUuid());
+ AssertPtrReturn(pObject, QUuid());
+
+ /* Is object critical? */
+ const bool fCritical = pObject->isCritical();
+ /* Is object progress? */
+ const bool fProgress = pObject->inherits("UINotificationProgress");
+
+ /* Handle object. Be aware it can be deleted during handling! */
+ const QUuid uId = m_pModel->appendObject(pObject);
+
+ /* If object is critical and center isn't opened yet: */
+ if (!m_pButtonOpen->isChecked() && fCritical)
+ {
+ /* We should delay progresses for a bit: */
+ const int iDelay = fProgress ? 2000 : 0;
+ /* We should issue an open request: */
+ AssertPtrReturn(m_pTimerOpen, uId);
+ m_uOpenObjectId = uId;
+ m_pTimerOpen->start(iDelay);
+ }
+
+ return uId;
+}
+
+void UINotificationCenter::revoke(const QUuid &uId)
+{
+ AssertReturnVoid(!uId.isNull());
+ return m_pModel->revokeObject(uId);
+}
+
+bool UINotificationCenter::handleNow(UINotificationProgress *pProgress)
+{
+ /* Check for the recursive run: */
+ AssertMsgReturn(!m_pEventLoop, ("UINotificationCenter::handleNow() is called recursively!\n"), false);
+
+ /* Reset the result: */
+ m_fLastResult = false;
+
+ /* Guard progress for the case
+ * it destroyed itself in his append call: */
+ QPointer<UINotificationProgress> guardProgress = pProgress;
+ connect(pProgress, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationCenter::sltHandleProgressFinished);
+ append(pProgress);
+
+ /* Is progress still valid? */
+ if (guardProgress.isNull())
+ return m_fLastResult;
+ /* Is progress still running? */
+ if (guardProgress->isDone())
+ return m_fLastResult;
+
+ /* Create a local event-loop: */
+ QEventLoop eventLoop;
+ m_pEventLoop = &eventLoop;
+
+ /* Guard ourself for the case
+ * we destroyed ourself in our event-loop: */
+ QPointer<UINotificationCenter> guardThis = this;
+
+ /* Start the blocking event-loop: */
+ eventLoop.exec();
+
+ /* Are we still valid? */
+ if (guardThis.isNull())
+ return false;
+
+ /* Cleanup event-loop: */
+ m_pEventLoop = 0;
+
+ /* Return actual result: */
+ return m_fLastResult;
+}
+
+void UINotificationCenter::retranslateUi()
+{
+ if (m_pButtonOpen)
+ m_pButtonOpen->setToolTip(tr("Open notification center"));
+ if (m_pButtonToggleSorting)
+ m_pButtonToggleSorting->setToolTip(tr("Toggle ascending/descending order"));
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+ if (m_pButtonKeepFinished)
+ m_pButtonKeepFinished->setToolTip(tr("Keep finished progresses"));
+#endif
+ if (m_pButtonRemoveFinished)
+ m_pButtonRemoveFinished->setToolTip(tr("Delete finished notifications"));
+}
+
+bool UINotificationCenter::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* For parent object only: */
+ if (pObject == parent())
+ {
+ /* Handle required event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ {
+ /* When parent being resized we want
+ * to adjust overlay accordingly. */
+ adjustGeometry();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
+}
+
+bool UINotificationCenter::event(QEvent *pEvent)
+{
+ /* Handle required event types: */
+ switch (pEvent->type())
+ {
+ /* When we are being asked to update layout
+ * we want to adjust overlay accordingly. */
+ case QEvent::LayoutRequest:
+ {
+ adjustGeometry();
+ break;
+ }
+ /* When we are being resized or moved we want
+ * to adjust transparency mask accordingly. */
+ case QEvent::Move:
+ case QEvent::Resize:
+ {
+ adjustMask();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::event(pEvent);
+}
+
+void UINotificationCenter::paintEvent(QPaintEvent *pEvent)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pEvent);
+
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Limit painting with incoming rectangle: */
+ painter.setClipRect(pEvent->rect());
+
+ /* Paint background: */
+ paintBackground(&painter);
+ paintFrame(&painter);
+}
+
+void UINotificationCenter::sltHandleAlignmentChange()
+{
+ /* Update alignment: */
+ m_enmAlignment = gEDataManager->notificationCenterAlignment();
+
+ /* Re-insert to layout: */
+ m_pLayoutMain->removeItem(m_pLayoutButtons);
+ m_pLayoutMain->insertLayout(m_enmAlignment == Qt::AlignTop ? 0 : -1, m_pLayoutButtons);
+
+ /* Adjust mask to make sure button visible, layout should be finalized already: */
+ QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
+ adjustMask();
+}
+
+void UINotificationCenter::sltIssueOrderChange()
+{
+ const Qt::SortOrder enmSortOrder = m_pButtonToggleSorting->isChecked()
+ ? Qt::AscendingOrder
+ : Qt::DescendingOrder;
+ gEDataManager->setNotificationCenterOrder(enmSortOrder);
+}
+
+void UINotificationCenter::sltHandleOrderChange()
+{
+ /* Save new order: */
+ m_enmOrder = gEDataManager->notificationCenterOrder();
+
+ /* Cleanup items first: */
+ qDeleteAll(m_items);
+ m_items.clear();
+
+ /* Populate model contents again: */
+ foreach (const QUuid &uId, m_pModel->ids())
+ {
+ UINotificationObjectItem *pItem = UINotificationItem::create(this, m_pModel->objectById(uId));
+ m_items[uId] = pItem;
+ m_pLayoutItems->insertWidget(m_enmOrder == Qt::AscendingOrder ? -1 : 0, pItem);
+ }
+
+ /* Hide and slide away if there are no notifications to show: */
+ setHidden(m_pModel->ids().isEmpty());
+ if (m_pModel->ids().isEmpty() && m_pButtonOpen->isChecked())
+ m_pButtonOpen->toggle();
+}
+
+void UINotificationCenter::sltHandleOpenButtonToggled(bool fToggled)
+{
+ if (fToggled)
+ emit sigOpen();
+ else
+ emit sigClose();
+}
+
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+void UINotificationCenter::sltHandleKeepButtonToggled(bool fToggled)
+{
+ gEDataManager->setKeepSuccessfullNotificationProgresses(fToggled);
+}
+#endif /* VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON */
+
+void UINotificationCenter::sltHandleRemoveFinishedButtonClicked()
+{
+ m_pModel->revokeFinishedObjects();
+}
+
+void UINotificationCenter::sltHandleOpenButtonContextMenuRequested(const QPoint &)
+{
+ /* Create menu: */
+ QMenu menu(m_pButtonOpen);
+
+ /* Create action: */
+ QAction action( m_enmAlignment == Qt::AlignTop
+ ? tr("Align Bottom")
+ : tr("Align Top"),
+ m_pButtonOpen);
+ menu.addAction(&action);
+
+ /* Execute menu, check if any (single) action is clicked: */
+ QAction *pAction = menu.exec(m_pButtonOpen->mapToGlobal(QPoint(m_pButtonOpen->width(), 0)));
+ if (pAction)
+ {
+ const Qt::Alignment enmAlignment = m_enmAlignment == Qt::AlignTop
+ ? Qt::AlignBottom
+ : Qt::AlignTop;
+ gEDataManager->setNotificationCenterAlignment(enmAlignment);
+ }
+}
+
+void UINotificationCenter::sltHandleOpenTimerTimeout()
+{
+ /* Make sure it's invoked by corresponding timer only: */
+ QTimer *pTimer = qobject_cast<QTimer*>(sender());
+ AssertPtrReturnVoid(pTimer);
+ AssertReturnVoid(pTimer == m_pTimerOpen);
+
+ /* Stop corresponding timer: */
+ m_pTimerOpen->stop();
+
+ /* Check whether we really closed: */
+ if (m_pButtonOpen->isChecked())
+ return;
+
+ /* Check whether message with particular ID exists: */
+ if (!m_pModel->hasObject(m_uOpenObjectId))
+ return;
+
+ /* Toggle open button: */
+ m_pButtonOpen->animateClick();
+}
+
+void UINotificationCenter::sltHandleModelItemAdded(const QUuid &uId)
+{
+ /* Add corresponding model item representation: */
+ AssertReturnVoid(!m_items.contains(uId));
+ UINotificationObjectItem *pItem = UINotificationItem::create(this, m_pModel->objectById(uId));
+ m_items[uId] = pItem;
+ m_pLayoutItems->insertWidget(m_enmOrder == Qt::AscendingOrder ? -1 : 0, pItem);
+
+ /* Show if there are notifications to show: */
+ setHidden(m_pModel->ids().isEmpty());
+}
+
+void UINotificationCenter::sltHandleModelItemRemoved(const QUuid &uId)
+{
+ /* Remove corresponding model item representation: */
+ AssertReturnVoid(m_items.contains(uId));
+ delete m_items.take(uId);
+
+ /* Hide and slide away if there are no notifications to show: */
+ setHidden(m_pModel->ids().isEmpty());
+ if (m_pModel->ids().isEmpty() && m_pButtonOpen->isChecked())
+ m_pButtonOpen->toggle();
+}
+
+void UINotificationCenter::sltHandleProgressFinished()
+{
+ /* Acquire the sender: */
+ UINotificationProgress *pProgress = qobject_cast<UINotificationProgress*>(sender());
+ AssertPtrReturnVoid(pProgress);
+
+ /* Set the result: */
+ m_fLastResult = pProgress->error().isNull();
+
+ /* Break the loop if exists: */
+ if (m_pEventLoop)
+ m_pEventLoop->exit();
+}
+
+void UINotificationCenter::prepare()
+{
+ /* Hide initially: */
+ setHidden(true);
+
+ /* Listen for parent events: */
+ if (parent())
+ parent()->installEventFilter(this);
+
+ /* Prepare alignment: */
+ m_enmAlignment = gEDataManager->notificationCenterAlignment();
+ connect(gEDataManager, &UIExtraDataManager::sigNotificationCenterAlignmentChange,
+ this, &UINotificationCenter::sltHandleAlignmentChange);
+ /* Prepare order: */
+ m_enmOrder = gEDataManager->notificationCenterOrder();
+ connect(gEDataManager, &UIExtraDataManager::sigNotificationCenterOrderChange,
+ this, &UINotificationCenter::sltHandleOrderChange);
+
+ /* Prepare the rest of stuff: */
+ prepareModel();
+ prepareWidgets();
+ prepareStateMachineSliding();
+ prepareOpenTimer();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UINotificationCenter::prepareModel()
+{
+ m_pModel = new UINotificationModel(this);
+ if (m_pModel)
+ {
+ connect(m_pModel, &UINotificationModel::sigItemAdded,
+ this, &UINotificationCenter::sltHandleModelItemAdded);
+ connect(m_pModel, &UINotificationModel::sigItemRemoved,
+ this, &UINotificationCenter::sltHandleModelItemRemoved);
+ }
+}
+
+void UINotificationCenter::prepareWidgets()
+{
+ /* Prepare main layout: */
+ m_pLayoutMain = new QVBoxLayout(this);
+ if (m_pLayoutMain)
+ {
+ /* Create container scroll-area: */
+ UINotificationScrollArea *pScrollAreaContainer = new UINotificationScrollArea(this);
+ if (pScrollAreaContainer)
+ {
+ /* Prepare container widget: */
+ QWidget *pWidgetContainer = new QWidget(pScrollAreaContainer);
+ if (pWidgetContainer)
+ {
+ /* Prepare container layout: */
+ QVBoxLayout *pLayoutContainer = new QVBoxLayout(pWidgetContainer);
+ if (pLayoutContainer)
+ {
+ pLayoutContainer->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare items layout: */
+ m_pLayoutItems = new QVBoxLayout;
+ if (m_pLayoutItems)
+ pLayoutContainer->addLayout(m_pLayoutItems);
+
+ pLayoutContainer->addStretch();
+ }
+
+ /* Add to scroll-area: */
+ pScrollAreaContainer->setWidget(pWidgetContainer);
+ }
+
+ /* Configure container scroll-area: */
+ pScrollAreaContainer->setWidgetResizable(true);
+ pScrollAreaContainer->setFrameShape(QFrame::NoFrame);
+ pScrollAreaContainer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ pScrollAreaContainer->viewport()->setAutoFillBackground(false);
+ pScrollAreaContainer->widget()->setAutoFillBackground(false);
+
+ /* Add to layout: */
+ m_pLayoutMain->addWidget(pScrollAreaContainer);
+ }
+
+ /* Prepare buttons layout: */
+ m_pLayoutButtons = new QHBoxLayout;
+ if (m_pLayoutButtons)
+ {
+ m_pLayoutButtons->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare open button: */
+ m_pButtonOpen = new QIToolButton(this);
+ if (m_pButtonOpen)
+ {
+ m_pButtonOpen->setIcon(UIIconPool::iconSet(":/notification_center_16px.png"));
+ m_pButtonOpen->setCheckable(true);
+ m_pButtonOpen->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_pButtonOpen, &QIToolButton::toggled,
+ this, &UINotificationCenter::sltHandleOpenButtonToggled);
+ connect(m_pButtonOpen, &QIToolButton::customContextMenuRequested,
+ this, &UINotificationCenter::sltHandleOpenButtonContextMenuRequested);
+ m_pLayoutButtons->addWidget(m_pButtonOpen);
+ }
+
+ /* Add stretch: */
+ m_pLayoutButtons->addStretch(1);
+
+ /* Prepare toggle-sorting button: */
+ m_pButtonToggleSorting = new QIToolButton(this);
+ if (m_pButtonToggleSorting)
+ {
+ m_pButtonToggleSorting->setIcon(UIIconPool::iconSet(":/notification_center_sort_16px.png"));
+ m_pButtonToggleSorting->setCheckable(true);
+ m_pButtonToggleSorting->setChecked(gEDataManager->notificationCenterOrder() == Qt::AscendingOrder);
+ connect(m_pButtonToggleSorting, &QIToolButton::toggled, this, &UINotificationCenter::sltIssueOrderChange);
+ m_pLayoutButtons->addWidget(m_pButtonToggleSorting);
+ }
+
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+ /* Prepare keep-finished button: */
+ m_pButtonKeepFinished = new QIToolButton(this);
+ if (m_pButtonKeepFinished)
+ {
+ m_pButtonKeepFinished->setIcon(UIIconPool::iconSet(":/notification_center_hold_progress_16px.png"));
+ m_pButtonKeepFinished->setCheckable(true);
+ m_pButtonKeepFinished->setChecked(gEDataManager->keepSuccessfullNotificationProgresses());
+ connect(m_pButtonKeepFinished, &QIToolButton::toggled, this, &UINotificationCenter::sltHandleKeepButtonToggled);
+ m_pLayoutButtons->addWidget(m_pButtonKeepFinished);
+ }
+#endif /* VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON */
+
+ /* Prepare remove-finished button: */
+ m_pButtonRemoveFinished = new QIToolButton(this);
+ if (m_pButtonRemoveFinished)
+ {
+ m_pButtonRemoveFinished->setIcon(UIIconPool::iconSet(":/notification_center_delete_progress_16px.png"));
+ connect(m_pButtonRemoveFinished, &QIToolButton::clicked, this, &UINotificationCenter::sltHandleRemoveFinishedButtonClicked);
+ m_pLayoutButtons->addWidget(m_pButtonRemoveFinished);
+ }
+
+ /* Add to layout: */
+ m_pLayoutMain->insertLayout(m_enmAlignment == Qt::AlignTop ? 0 : -1, m_pLayoutButtons);
+ }
+ }
+}
+
+void UINotificationCenter::prepareStateMachineSliding()
+{
+ /* Create sliding animation state-machine: */
+ m_pStateMachineSliding = new QStateMachine(this);
+ if (m_pStateMachineSliding)
+ {
+ /* Create 'closed' state: */
+ QState *pStateClosed = new QState(m_pStateMachineSliding);
+ /* Create 'opened' state: */
+ QState *pStateOpened = new QState(m_pStateMachineSliding);
+
+ /* Configure 'closed' state: */
+ if (pStateClosed)
+ {
+ /* When we entering closed state => we assigning animatedValue to 0: */
+ pStateClosed->assignProperty(this, "animatedValue", 0);
+
+ /* Add state transitions: */
+ QSignalTransition *pClosedToOpened = pStateClosed->addTransition(this, SIGNAL(sigOpen()), pStateOpened);
+ if (pClosedToOpened)
+ {
+ /* Create forward animation: */
+ QPropertyAnimation *pAnimationForward = new QPropertyAnimation(this, "animatedValue", this);
+ if (pAnimationForward)
+ {
+ pAnimationForward->setEasingCurve(QEasingCurve::InCubic);
+ pAnimationForward->setDuration(300);
+ pAnimationForward->setStartValue(0);
+ pAnimationForward->setEndValue(100);
+
+ /* Add to transition: */
+ pClosedToOpened->addAnimation(pAnimationForward);
+ }
+ }
+ }
+
+ /* Configure 'opened' state: */
+ if (pStateOpened)
+ {
+ /* When we entering opened state => we assigning animatedValue to 100: */
+ pStateOpened->assignProperty(this, "animatedValue", 100);
+
+ /* Add state transitions: */
+ QSignalTransition *pOpenedToClosed = pStateOpened->addTransition(this, SIGNAL(sigClose()), pStateClosed);
+ if (pOpenedToClosed)
+ {
+ /* Create backward animation: */
+ QPropertyAnimation *pAnimationBackward = new QPropertyAnimation(this, "animatedValue", this);
+ if (pAnimationBackward)
+ {
+ pAnimationBackward->setEasingCurve(QEasingCurve::InCubic);
+ pAnimationBackward->setDuration(300);
+ pAnimationBackward->setStartValue(100);
+ pAnimationBackward->setEndValue(0);
+
+ /* Add to transition: */
+ pOpenedToClosed->addAnimation(pAnimationBackward);
+ }
+ }
+ }
+
+ /* Initial state is 'closed': */
+ m_pStateMachineSliding->setInitialState(pStateClosed);
+ /* Start state-machine: */
+ m_pStateMachineSliding->start();
+ }
+}
+
+void UINotificationCenter::prepareOpenTimer()
+{
+ m_pTimerOpen = new QTimer(this);
+ if (m_pTimerOpen)
+ connect(m_pTimerOpen, &QTimer::timeout,
+ this, &UINotificationCenter::sltHandleOpenTimerTimeout);
+}
+
+void UINotificationCenter::cleanup()
+{
+ /* Cleanup items: */
+ qDeleteAll(m_items);
+ m_items.clear();
+}
+
+void UINotificationCenter::paintBackground(QPainter *pPainter)
+{
+ /* Acquire palette: */
+ const bool fActive = parentWidget() && parentWidget()->isActiveWindow();
+ const QPalette pal = QApplication::palette();
+
+ /* Gather suitable color: */
+ QColor backgroundColor = pal.color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).darker(120);
+ backgroundColor.setAlpha((double)animatedValue() / 100 * 220);
+
+ /* Acquire pixel metric: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Adjust rectangle: */
+ QRect rectAdjusted = rect();
+ rectAdjusted.adjust(iMetric, iMetric, 0, -iMetric);
+
+ /* Paint background: */
+ pPainter->fillRect(rectAdjusted, backgroundColor);
+}
+
+void UINotificationCenter::paintFrame(QPainter *pPainter)
+{
+ /* Acquire palette: */
+ const bool fActive = parentWidget() && parentWidget()->isActiveWindow();
+ QPalette pal = QApplication::palette();
+
+ /* Gather suitable colors: */
+ QColor color1 = pal.color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).lighter(110);
+ color1.setAlpha(0);
+ QColor color2 = pal.color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).darker(200);
+
+ /* Acquire pixel metric: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Top-left corner: */
+ QRadialGradient grad1(QPointF(iMetric, iMetric), iMetric);
+ {
+ grad1.setColorAt(0, color2);
+ grad1.setColorAt(1, color1);
+ }
+ /* Bottom-left corner: */
+ QRadialGradient grad2(QPointF(iMetric, height() - iMetric), iMetric);
+ {
+ grad2.setColorAt(0, color2);
+ grad2.setColorAt(1, color1);
+ }
+
+ /* Top line: */
+ QLinearGradient grad3(QPointF(iMetric, 0), QPointF(iMetric, iMetric));
+ {
+ grad3.setColorAt(0, color1);
+ grad3.setColorAt(1, color2);
+ }
+ /* Bottom line: */
+ QLinearGradient grad4(QPointF(iMetric, height()), QPointF(iMetric, height() - iMetric));
+ {
+ grad4.setColorAt(0, color1);
+ grad4.setColorAt(1, color2);
+ }
+ /* Left line: */
+ QLinearGradient grad5(QPointF(0, height() - iMetric), QPointF(iMetric, height() - iMetric));
+ {
+ grad5.setColorAt(0, color1);
+ grad5.setColorAt(1, color2);
+ }
+
+ /* Paint shape/shadow: */
+ pPainter->fillRect(QRect(0, 0, iMetric, iMetric), grad1);
+ pPainter->fillRect(QRect(0, height() - iMetric, iMetric, iMetric), grad2);
+ pPainter->fillRect(QRect(iMetric, 0, width() - iMetric, iMetric), grad3);
+ pPainter->fillRect(QRect(iMetric, height() - iMetric, width() - iMetric, iMetric), grad4);
+ pPainter->fillRect(QRect(0, iMetric, iMetric, height() - iMetric * 2), grad5);
+}
+
+void UINotificationCenter::setAnimatedValue(int iValue)
+{
+ /* Store recent value: */
+ m_iAnimatedValue = iValue;
+
+ // WORKAROUND:
+ // Hide items if they are masked anyway.
+ // This actually shouldn't be necessary but
+ // *is* required to avoid painting artifacts.
+ foreach (QWidget *pItem, m_items.values())
+ pItem->setVisible(animatedValue());
+
+ /* Adjust geometry: */
+ adjustGeometry();
+}
+
+int UINotificationCenter::animatedValue() const
+{
+ return m_iAnimatedValue;
+}
+
+void UINotificationCenter::adjustGeometry()
+{
+ /* Make sure parent exists: */
+ QWidget *pParent = parentWidget();
+ if (!pParent)
+ return;
+ /* Acquire parent width and height: */
+ const int iParentWidth = pParent->width();
+ const int iParentHeight = pParent->height();
+
+ /* Acquire minimum width (includes margins by default): */
+ int iMinimumWidth = minimumSizeHint().width();
+ /* Acquire minimum button width (including margins manually): */
+ int iL, iT, iR, iB;
+ m_pLayoutMain->getContentsMargins(&iL, &iT, &iR, &iB);
+ const int iMinimumButtonWidth = m_pButtonOpen->minimumSizeHint().width() + iL + iR;
+
+ /* Make sure we have some default width if there is no contents: */
+ iMinimumWidth = qMax(iMinimumWidth, 200);
+
+ /* Move and resize notification-center finally: */
+ move(iParentWidth - (iMinimumButtonWidth + (double)animatedValue() / 100 * (iMinimumWidth - iMinimumButtonWidth)), 0);
+ resize(iMinimumWidth, iParentHeight);
+}
+
+void UINotificationCenter::adjustMask()
+{
+ /* We do include open-button mask only if center is opened or animated to be: */
+ QRegion region;
+ if (!animatedValue())
+ region += QRect(m_pButtonOpen->mapToParent(QPoint(0, 0)), m_pButtonOpen->size());
+ setMask(region);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationReceiver implementation. *
+*********************************************************************************************************************************/
+
+void UINotificationReceiver::setReceiverProperty(const QVariant &value)
+{
+ setProperty("received_value", value);
+}
+
+
+#include "UINotificationCenter.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationCenter.h b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationCenter.h
new file mode 100644
index 00000000..f2548652
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationCenter.h
@@ -0,0 +1,238 @@
+/* $Id: UINotificationCenter.h $ */
+/** @file
+ * VBox Qt GUI - UINotificationCenter class declaration.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_notificationcenter_UINotificationCenter_h
+#define FEQT_INCLUDED_SRC_notificationcenter_UINotificationCenter_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QEventLoop>
+#include <QPointer>
+#include <QUuid>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+#include "UINotificationObjects.h"
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QPainter;
+class QStateMachine;
+class QTimer;
+class QVBoxLayout;
+class QIToolButton;
+class UINotificationModel;
+class UINotificationObject;
+
+/** QWidget-based notification-center overlay. */
+class SHARED_LIBRARY_STUFF UINotificationCenter : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+ Q_PROPERTY(int animatedValue READ animatedValue WRITE setAnimatedValue);
+
+signals:
+
+ /** Requests sliding state-machine to open overlay. */
+ void sigOpen();
+ /** Requests sliding state-machine to close overlay. */
+ void sigClose();
+
+public:
+
+ /** Creates notification-center for passed @a pParent. */
+ static void create(QWidget *pParent = 0);
+ /** Destroys notification-center. */
+ static void destroy();
+ /** Returns notification-center singleton instance. */
+ static UINotificationCenter *instance();
+
+ /** Constructs notification-center passing @a pParent to the base-class. */
+ UINotificationCenter(QWidget *pParent);
+ /** Destructs notification-center. */
+ virtual ~UINotificationCenter() /* override final */;
+
+ /** Defines notification-center @a pParent. */
+ void setParent(QWidget *pParent);
+
+ /** Invokes notification-center. */
+ void invoke();
+
+ /** Appends a notification @a pObject to intenal model. */
+ QUuid append(UINotificationObject *pObject);
+ /** Revokes a notification object referenced by @a uId from intenal model. */
+ void revoke(const QUuid &uId);
+
+ /** Immediately and synchronously handles passed notification @a pProgress.
+ * @note It's a blocking call finished by sltHandleProgressFinished(). */
+ bool handleNow(UINotificationProgress *pProgress);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) /* override final */;
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) /* override final */;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) /* override final */;
+
+private slots:
+
+ /** Handles alignment changes. */
+ void sltHandleAlignmentChange();
+
+ /** Issues order changes. */
+ void sltIssueOrderChange();
+ /** Handles order changes. */
+ void sltHandleOrderChange();
+
+ /** Issues request to make open button @a fToggled. */
+ void sltHandleOpenButtonToggled(bool fToggled);
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+ /** Toggles notification-progresses keep approach. */
+ void sltHandleKeepButtonToggled(bool fToggled);
+#endif
+ /** Removes finished notifications. */
+ void sltHandleRemoveFinishedButtonClicked();
+
+ /** Invokes open button context menu at specified @a position. */
+ void sltHandleOpenButtonContextMenuRequested(const QPoint &position);
+
+ /** Handles open-timer timeout. */
+ void sltHandleOpenTimerTimeout();
+
+ /** Handles signal about model item with specified @a uId was added. */
+ void sltHandleModelItemAdded(const QUuid &uId);
+ /** Handles signal about model item with specified @a uId was removed. */
+ void sltHandleModelItemRemoved(const QUuid &uId);
+
+ /** Handles immediate progress being finished.
+ * @note Breaks blocking handleNow() call. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Prepares everything. */
+ void prepare();
+ /** Prepares model. */
+ void prepareModel();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares sliding state-machine. */
+ void prepareStateMachineSliding();
+ /** Prepares open-timer. */
+ void prepareOpenTimer();
+ /** Cleanups everything. */
+ void cleanup();
+
+ /** Paints background using pre-configured @a pPainter. */
+ void paintBackground(QPainter *pPainter);
+ /** Paints frame using pre-configured @a pPainter. */
+ void paintFrame(QPainter *pPainter);
+
+ /** Defines animated @a iValue. */
+ void setAnimatedValue(int iValue);
+ /** Returns animated value. */
+ int animatedValue() const;
+
+ /** Adjusts geometry. */
+ void adjustGeometry();
+ /** Adjusts mask. */
+ void adjustMask();
+
+ /** Holds the notification-center singleton instance. */
+ static UINotificationCenter *s_pInstance;
+
+ /** Holds the model instance. */
+ UINotificationModel *m_pModel;
+
+ /** Holds the alignment. */
+ Qt::Alignment m_enmAlignment;
+ /** Holds the order. */
+ Qt::SortOrder m_enmOrder;
+
+ /** Holds the main layout instance. */
+ QVBoxLayout *m_pLayoutMain;
+ /** Holds the buttons layout instance. */
+ QHBoxLayout *m_pLayoutButtons;
+ /** Holds the open button instance. */
+ QIToolButton *m_pButtonOpen;
+ /** Holds the toggle-sorting button instance. */
+ QIToolButton *m_pButtonToggleSorting;
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+ /** Holds the keep-finished button instance. */
+ QIToolButton *m_pButtonKeepFinished;
+#endif
+ /** Holds the remove-finished button instance. */
+ QIToolButton *m_pButtonRemoveFinished;
+ /** Holds the items layout instance. */
+ QVBoxLayout *m_pLayoutItems;
+
+ /** Holds the map of item instances. */
+ QMap<QUuid, QWidget*> m_items;
+
+ /** Holds the sliding state-machine instance. */
+ QStateMachine *m_pStateMachineSliding;
+ /** Holds the sliding animation current value. */
+ int m_iAnimatedValue;
+
+ /** Holds the open-timer instance. */
+ QTimer *m_pTimerOpen;
+ /** Holds the open-object ID. */
+ QUuid m_uOpenObjectId;
+
+ /** Holds the separate event-loop instance.
+ * @note This event-loop is only used when the center
+ * handles progress directly via handleNow(). */
+ QPointer<QEventLoop> m_pEventLoop;
+ /** Holds the last handleNow() result. */
+ bool m_fLastResult;
+};
+
+/** Singleton notification-center 'official' name. */
+#define gpNotificationCenter UINotificationCenter::instance()
+
+/** QObject subclass receiving notification value and storing is as a property. */
+class SHARED_LIBRARY_STUFF UINotificationReceiver : public QObject
+{
+ Q_OBJECT;
+
+public slots:
+
+ /** Defines received property by @a value. */
+ void setReceiverProperty(const QVariant &value);
+};
+
+#endif /* !FEQT_INCLUDED_SRC_notificationcenter_UINotificationCenter_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationModel.cpp b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationModel.cpp
new file mode 100644
index 00000000..a8f0f188
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationModel.cpp
@@ -0,0 +1,150 @@
+/* $Id: UINotificationModel.cpp $ */
+/** @file
+ * VBox Qt GUI - UINotificationModel class implementation.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QSet>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UINotificationModel.h"
+#include "UINotificationObject.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+UINotificationModel::UINotificationModel(QObject *pParent)
+ : QObject(pParent)
+{
+ prepare();
+}
+
+UINotificationModel::~UINotificationModel()
+{
+ cleanup();
+}
+
+QUuid UINotificationModel::appendObject(UINotificationObject *pObject)
+{
+ /* [Re]generate ID until unique: */
+ QUuid uId = QUuid::createUuid();
+ while (m_ids.contains(uId))
+ uId = QUuid::createUuid();
+ /* Append ID and object: */
+ m_ids << uId;
+ m_objects[uId] = pObject;
+ /* Connect object close signal: */
+ connect(pObject, &UINotificationObject::sigAboutToClose,
+ this, &UINotificationModel::sltHandleAboutToClose);
+ /* Notify listeners: */
+ emit sigItemAdded(uId);
+ /* Handle object: */
+ pObject->handle();
+ /* Return ID: */
+ return uId;
+}
+
+void UINotificationModel::revokeObject(const QUuid &uId)
+{
+ /* Remove id first of all: */
+ m_ids.removeAll(uId);
+ /* Notify listeners before object is deleted: */
+ emit sigItemRemoved(uId);
+ /* Delete object itself finally: */
+ delete m_objects.take(uId);
+}
+
+bool UINotificationModel::hasObject(const QUuid &uId) const
+{
+ return m_objects.contains(uId);
+}
+
+void UINotificationModel::revokeFinishedObjects()
+{
+ /* Check whether there are done objects: */
+ foreach (const QUuid &uId, m_ids)
+ {
+ UINotificationObject *pObject = m_objects.value(uId);
+ AssertPtrReturnVoid(pObject);
+ if (pObject->isDone())
+ revokeObject(uId);
+ }
+}
+
+QList<QUuid> UINotificationModel::ids() const
+{
+ return m_ids;
+}
+
+UINotificationObject *UINotificationModel::objectById(const QUuid &uId)
+{
+ return m_objects.value(uId);
+}
+
+void UINotificationModel::sltHandleAboutToClose(bool fDismiss)
+{
+ /* Determine sender: */
+ UINotificationObject *pSender = qobject_cast<UINotificationObject*>(sender());
+ AssertPtrReturnVoid(pSender);
+
+ /* Dismiss message if requested: */
+ if (fDismiss && !pSender->internalName().isEmpty())
+ {
+ QStringList suppressedMessages = gEDataManager->suppressedMessages();
+ if (!suppressedMessages.contains(pSender->internalName()))
+ {
+ suppressedMessages.push_back(pSender->internalName());
+ gEDataManager->setSuppressedMessages(suppressedMessages);
+ }
+ }
+
+ /* Revoke it from internal storage: */
+ const QUuid uId = m_objects.key(pSender);
+ AssertReturnVoid(!uId.isNull());
+ revokeObject(uId);
+}
+
+void UINotificationModel::sltDetachCOM()
+{
+ cleanup();
+}
+
+void UINotificationModel::prepare()
+{
+ connect(&uiCommon(), &UICommon::sigAskToDetachCOM,
+ this, &UINotificationModel::sltDetachCOM);
+}
+
+void UINotificationModel::cleanup()
+{
+ /* Wipe out all the objects: */
+ foreach (const QUuid &uId, m_ids)
+ delete m_objects.value(uId);
+ m_objects.clear();
+ m_ids.clear();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationModel.h b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationModel.h
new file mode 100644
index 00000000..732e273c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationModel.h
@@ -0,0 +1,101 @@
+/* $Id: UINotificationModel.h $ */
+/** @file
+ * VBox Qt GUI - UINotificationModel class declaration.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_notificationcenter_UINotificationModel_h
+#define FEQT_INCLUDED_SRC_notificationcenter_UINotificationModel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QList>
+#include <QMap>
+#include <QObject>
+#include <QUuid>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class UINotificationObject;
+
+/** QObject-based notification-center model. */
+class SHARED_LIBRARY_STUFF UINotificationModel : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about item with specified @a uId was added. */
+ void sigItemAdded(const QUuid &uId);
+ /** Notifies listeners about item with specified @a uId was removed. */
+ void sigItemRemoved(const QUuid &uId);
+
+public:
+
+ /** Constructs notification-center model passing @a pParent to the base-class. */
+ UINotificationModel(QObject *pParent);
+ /** Destructs notification-center model. */
+ virtual ~UINotificationModel() /* override final */;
+
+ /** Appens a notification @a pObject to internal storage. */
+ QUuid appendObject(UINotificationObject *pObject);
+ /** Revokes a notification object referenced by @a uId from intenal storage. */
+ void revokeObject(const QUuid &uId);
+ /** Returns whether there is a notification object referenced by @a uId. */
+ bool hasObject(const QUuid &uId) const;
+ /** Revokes finished notification objects. */
+ void revokeFinishedObjects();
+
+ /** Returns a list of registered notification object IDs. */
+ QList<QUuid> ids() const;
+ /** Returns a notification object referenced by specified @a uId. */
+ UINotificationObject *objectById(const QUuid &uId);
+
+private slots:
+
+ /** Handles request about to close sender() notification object.
+ * @param fDismiss Brings whether message closed as dismissed. */
+ void sltHandleAboutToClose(bool fDismiss);
+
+ /** Handles broadcast request to detach COM stuff. */
+ void sltDetachCOM();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the list of registered notification object IDs. */
+ QList<QUuid> m_ids;
+ /** Holds the map of notification objects registered by ID. */
+ QMap<QUuid, UINotificationObject*> m_objects;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_notificationcenter_UINotificationModel_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObject.cpp b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObject.cpp
new file mode 100644
index 00000000..ef6d43ee
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObject.cpp
@@ -0,0 +1,341 @@
+/* $Id: UINotificationObject.cpp $ */
+/** @file
+ * VBox Qt GUI - UINotificationObject class implementation.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIExtraDataManager.h"
+#include "UINotificationObject.h"
+#include "UINotificationProgressTask.h"
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+# include "UIDownloader.h"
+# include "UINewVersionChecker.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Class UINotificationObject implementation. *
+*********************************************************************************************************************************/
+
+UINotificationObject::UINotificationObject()
+{
+}
+
+void UINotificationObject::dismiss()
+{
+ emit sigAboutToClose(true);
+}
+
+void UINotificationObject::close()
+{
+ emit sigAboutToClose(false);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationSimple implementation. *
+*********************************************************************************************************************************/
+
+UINotificationSimple::UINotificationSimple(const QString &strName,
+ const QString &strDetails,
+ const QString &strInternalName,
+ const QString &strHelpKeyword,
+ bool fCritical /* = true */)
+ : m_strName(strName)
+ , m_strDetails(strDetails)
+ , m_strInternalName(strInternalName)
+ , m_strHelpKeyword(strHelpKeyword)
+ , m_fCritical(fCritical)
+{
+}
+
+bool UINotificationSimple::isCritical() const
+{
+ return m_fCritical;
+}
+
+bool UINotificationSimple::isDone() const
+{
+ return true;
+}
+
+QString UINotificationSimple::name() const
+{
+ return m_strName;
+}
+
+QString UINotificationSimple::details() const
+{
+ return m_strDetails;
+}
+
+QString UINotificationSimple::internalName() const
+{
+ return m_strInternalName;
+}
+
+QString UINotificationSimple::helpKeyword() const
+{
+ return m_strHelpKeyword;
+}
+
+void UINotificationSimple::handle()
+{
+}
+
+/* static */
+bool UINotificationSimple::isSuppressed(const QString &strInternalName)
+{
+ /* Sanity check: */
+ if (strInternalName.isEmpty())
+ return false;
+
+ /* Acquire and check suppressed message names: */
+ const QStringList suppressedMessages = gEDataManager->suppressedMessages();
+ return suppressedMessages.contains(strInternalName)
+ || suppressedMessages.contains("all");
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgress implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgress::UINotificationProgress()
+ : m_pTask(0)
+ , m_uPercent(0)
+ , m_fDone(false)
+{
+}
+
+UINotificationProgress::~UINotificationProgress()
+{
+ delete m_pTask;
+ m_pTask = 0;
+}
+
+ulong UINotificationProgress::percent() const
+{
+ return m_uPercent;
+}
+
+bool UINotificationProgress::isCancelable() const
+{
+ return m_pTask ? m_pTask->isCancelable() : false;
+}
+
+QString UINotificationProgress::error() const
+{
+ return m_pTask ? m_pTask->errorMessage() : QString();
+}
+
+bool UINotificationProgress::isCritical() const
+{
+ return true;
+}
+
+bool UINotificationProgress::isDone() const
+{
+ return m_fDone;
+}
+
+QString UINotificationProgress::internalName() const
+{
+ return QString();
+}
+
+QString UINotificationProgress::helpKeyword() const
+{
+ return QString();
+}
+
+void UINotificationProgress::handle()
+{
+ /* Prepare task: */
+ m_pTask = new UINotificationProgressTask(this);
+ if (m_pTask)
+ {
+ connect(m_pTask, &UIProgressTask::sigProgressStarted,
+ this, &UINotificationProgress::sigProgressStarted);
+ connect(m_pTask, &UIProgressTask::sigProgressChange,
+ this, &UINotificationProgress::sltHandleProgressChange);
+ connect(m_pTask, &UIProgressTask::sigProgressCanceled,
+ this, &UINotificationProgress::sigProgressFinished);
+ connect(m_pTask, &UIProgressTask::sigProgressFinished,
+ this, &UINotificationProgress::sltHandleProgressFinished);
+
+ /* And start it finally: */
+ m_pTask->start();
+ }
+}
+
+void UINotificationProgress::close()
+{
+ /* Cancel task: */
+ if (m_pTask)
+ m_pTask->cancel();
+ /* Call to base-class: */
+ UINotificationObject::close();
+}
+
+void UINotificationProgress::sltHandleProgressChange(ulong uPercent)
+{
+ m_uPercent = uPercent;
+ emit sigProgressChange(uPercent);
+}
+
+void UINotificationProgress::sltHandleProgressFinished()
+{
+ m_uPercent = 100;
+ m_fDone = true;
+ emit sigProgressFinished();
+
+ /* If there was no error and no reason to keep progress alive, - finish him! */
+ if ( error().isEmpty()
+#ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
+ && !gEDataManager->keepSuccessfullNotificationProgresses()
+#endif
+ )
+ close();
+}
+
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+
+
+/*********************************************************************************************************************************
+* Class UINotificationDownloader implementation. *
+*********************************************************************************************************************************/
+
+UINotificationDownloader::UINotificationDownloader()
+ : m_pDownloader(0)
+ , m_uPercent(0)
+ , m_fDone(false)
+{
+}
+
+UINotificationDownloader::~UINotificationDownloader()
+{
+ delete m_pDownloader;
+ m_pDownloader = 0;
+}
+
+ulong UINotificationDownloader::percent() const
+{
+ return m_uPercent;
+}
+
+QString UINotificationDownloader::error() const
+{
+ return m_strError;
+}
+
+bool UINotificationDownloader::isCritical() const
+{
+ return true;
+}
+
+bool UINotificationDownloader::isDone() const
+{
+ return m_fDone;
+}
+
+QString UINotificationDownloader::internalName() const
+{
+ return QString();
+}
+
+QString UINotificationDownloader::helpKeyword() const
+{
+ return QString();
+}
+
+void UINotificationDownloader::handle()
+{
+ /* Prepare downloader: */
+ m_pDownloader = createDownloader();
+ if (m_pDownloader)
+ {
+ connect(m_pDownloader, &UIDownloader::sigToStartAcknowledging,
+ this, &UINotificationDownloader::sigProgressStarted);
+ connect(m_pDownloader, &UIDownloader::sigToStartDownloading,
+ this, &UINotificationDownloader::sigProgressStarted);
+ connect(m_pDownloader, &UIDownloader::sigToStartVerifying,
+ this, &UINotificationDownloader::sigProgressStarted);
+ connect(m_pDownloader, &UIDownloader::sigProgressChange,
+ this, &UINotificationDownloader::sltHandleProgressChange);
+ connect(m_pDownloader, &UIDownloader::sigProgressFailed,
+ this, &UINotificationDownloader::sltHandleProgressFailed);
+ connect(m_pDownloader, &UIDownloader::sigProgressCanceled,
+ this, &UINotificationDownloader::sltHandleProgressCanceled);
+ connect(m_pDownloader, &UIDownloader::sigProgressFinished,
+ this, &UINotificationDownloader::sltHandleProgressFinished);
+
+ /* And start it finally: */
+ m_pDownloader->start();
+ }
+}
+
+void UINotificationDownloader::close()
+{
+ /* Cancel downloader: */
+ if (m_pDownloader)
+ m_pDownloader->cancel();
+ /* Call to base-class: */
+ UINotificationObject::close();
+}
+
+void UINotificationDownloader::sltHandleProgressChange(ulong uPercent)
+{
+ m_uPercent = uPercent;
+ emit sigProgressChange(uPercent);
+}
+
+void UINotificationDownloader::sltHandleProgressFailed(const QString &strError)
+{
+ delete m_pDownloader;
+ m_pDownloader = 0;
+ m_strError = strError;
+ m_fDone = true;
+ emit sigProgressFailed();
+}
+
+void UINotificationDownloader::sltHandleProgressCanceled()
+{
+ delete m_pDownloader;
+ m_pDownloader = 0;
+ m_fDone = true;
+ emit sigProgressCanceled();
+}
+
+void UINotificationDownloader::sltHandleProgressFinished()
+{
+ delete m_pDownloader;
+ m_pDownloader = 0;
+ m_fDone = true;
+ emit sigProgressFinished();
+}
+
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObject.h b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObject.h
new file mode 100644
index 00000000..ae556cdd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObject.h
@@ -0,0 +1,285 @@
+/* $Id: UINotificationObject.h $ */
+/** @file
+ * VBox Qt GUI - UINotificationObject class declaration.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_notificationcenter_UINotificationObject_h
+#define FEQT_INCLUDED_SRC_notificationcenter_UINotificationObject_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "CProgress.h"
+
+/* Forward declarations: */
+class UINotificationProgressTask;
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+class UIDownloader;
+class UINewVersionChecker;
+#endif
+
+/** QObject-based notification-object. */
+class SHARED_LIBRARY_STUFF UINotificationObject : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies model about closing.
+ * @param fDismiss Brings whether message closed as dismissed. */
+ void sigAboutToClose(bool fDismiss);
+
+public:
+
+ /** Constructs notification-object. */
+ UINotificationObject();
+
+ /** Returns whether object is critical. */
+ virtual bool isCritical() const = 0;
+ /** Returns whether object is done. */
+ virtual bool isDone() const = 0;
+ /** Returns object name. */
+ virtual QString name() const = 0;
+ /** Returns object details. */
+ virtual QString details() const = 0;
+ /** Returns object internal name. */
+ virtual QString internalName() const = 0;
+ /** Returns object help keyword. */
+ virtual QString helpKeyword() const = 0;
+ /** Handles notification-object being added. */
+ virtual void handle() = 0;
+
+public slots:
+
+ /** Notifies model about dismissing. */
+ virtual void dismiss();
+ /** Notifies model about closing. */
+ virtual void close();
+};
+
+/** UINotificationObject extension for notification-simple. */
+class SHARED_LIBRARY_STUFF UINotificationSimple : public UINotificationObject
+{
+ Q_OBJECT;
+
+protected:
+
+ /** Constructs notification-simple.
+ * @param strName Brings the message name.
+ * @param strDetails Brings the message details.
+ * @param strInternalName Brings the message internal name.
+ * @param strHelpKeyword Brings the message help keyword.
+ * @param fCritical Brings whether message is critical. */
+ UINotificationSimple(const QString &strName,
+ const QString &strDetails,
+ const QString &strInternalName,
+ const QString &strHelpKeyword,
+ bool fCritical = true);
+
+ /** Returns whether object is critical. */
+ virtual bool isCritical() const RT_OVERRIDE;
+ /** Returns whether object is done. */
+ virtual bool isDone() const RT_OVERRIDE;
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Returns object internal name. */
+ virtual QString internalName() const /* override final */;
+ /** Returns object help keyword. */
+ virtual QString helpKeyword() const /* override final */;
+ /** Handles notification-object being added. */
+ virtual void handle() /* override final */;
+
+ /** Returns whether message with passed @a strInternalName is suppressed. */
+ static bool isSuppressed(const QString &strInternalName);
+
+private:
+
+ /** Holds the message name. */
+ QString m_strName;
+ /** Holds the message details. */
+ QString m_strDetails;
+ /** Holds the message internal name. */
+ QString m_strInternalName;
+ /** Holds the message help keyword. */
+ QString m_strHelpKeyword;
+ /** Holds whether message is critical. */
+ bool m_fCritical;
+};
+
+/** UINotificationObject extension for notification-progress. */
+class SHARED_LIBRARY_STUFF UINotificationProgress : public UINotificationObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about progress started. */
+ void sigProgressStarted();
+ /** Notifies listeners about progress changed.
+ * @param uPercent Brings new progress percentage value. */
+ void sigProgressChange(ulong uPercent);
+ /** Notifies listeners about progress finished. */
+ void sigProgressFinished();
+
+public:
+
+ /** Constructs notification-progress. */
+ UINotificationProgress();
+ /** Destructs notification-progress. */
+ virtual ~UINotificationProgress() /* override final */;
+
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) = 0;
+
+ /** Returns current progress percentage value. */
+ ulong percent() const;
+ /** Returns whether progress is cancelable. */
+ bool isCancelable() const;
+ /** Returns error-message if any. */
+ QString error() const;
+
+ /** Returns whether object is critical. */
+ virtual bool isCritical() const RT_OVERRIDE;
+ /** Returns whether object is done. */
+ virtual bool isDone() const RT_OVERRIDE;
+ /** Returns object internal name. */
+ virtual QString internalName() const /* override final */;
+ /** Returns object help keyword. */
+ virtual QString helpKeyword() const /* override final */;
+ /** Handles notification-object being added. */
+ virtual void handle() /* override final */;
+
+public slots:
+
+ /** Stops the progress and notifies model about closing. */
+ virtual void close() /* override final */;
+
+private slots:
+
+ /** Handles signal about progress changed.
+ * @param uPercent Brings new progress percentage value. */
+ void sltHandleProgressChange(ulong uPercent);
+ /** Handles signal about progress finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the instance of progress-task being wrapped by this notification-progress. */
+ UINotificationProgressTask *m_pTask;
+
+ /** Holds the last cached progress percentage value. */
+ ulong m_uPercent;
+ /** Holds whether current progress is done. */
+ bool m_fDone;
+};
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+/** UINotificationObject extension for notification-downloader. */
+class SHARED_LIBRARY_STUFF UINotificationDownloader : public UINotificationObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about progress started. */
+ void sigProgressStarted();
+ /** Notifies listeners about progress changed.
+ * @param uPercent Brings new progress percentage value. */
+ void sigProgressChange(ulong uPercent);
+ /** Notifies listeners about progress failed. */
+ void sigProgressFailed();
+ /** Notifies listeners about progress canceled. */
+ void sigProgressCanceled();
+ /** Notifies listeners about progress finished. */
+ void sigProgressFinished();
+
+public:
+
+ /** Constructs notification-downloader. */
+ UINotificationDownloader();
+ /** Destructs notification-downloader. */
+ virtual ~UINotificationDownloader() /* override final */;
+
+ /** Creates and returns started downloader-wrapper. */
+ virtual UIDownloader *createDownloader() = 0;
+
+ /** Returns current progress percentage value. */
+ ulong percent() const;
+ /** Returns error-message if any. */
+ QString error() const;
+
+ /** Returns whether object is critical. */
+ virtual bool isCritical() const RT_OVERRIDE;
+ /** Returns whether object is done. */
+ virtual bool isDone() const RT_OVERRIDE;
+ /** Returns object internal name. */
+ virtual QString internalName() const /* override final */;
+ /** Returns object help keyword. */
+ virtual QString helpKeyword() const /* override final */;
+ /** Handles notification-object being added. */
+ virtual void handle() /* override final */;
+
+public slots:
+
+ /** Stops the downloader and notifies model about closing. */
+ virtual void close() /* override final */;
+
+private slots:
+
+ /** Handles signal about progress changed.
+ * @param uPercent Brings new progress percentage value. */
+ void sltHandleProgressChange(ulong uPercent);
+ /** Handles signal about progress failed.
+ * @param strError Brings error message if any. */
+ void sltHandleProgressFailed(const QString &strError);
+ /** Handles signal about progress canceled. */
+ void sltHandleProgressCanceled();
+ /** Handles signal about progress finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the instance of downloader being wrapped by this notification-downloader. */
+ UIDownloader *m_pDownloader;
+
+ /** Holds the last cached progress percentage value. */
+ ulong m_uPercent;
+ /** Holds the error message is any. */
+ QString m_strError;
+ /** Holds whether current progress is done. */
+ bool m_fDone;
+};
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+#endif /* !FEQT_INCLUDED_SRC_notificationcenter_UINotificationObject_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjectItem.cpp b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjectItem.cpp
new file mode 100644
index 00000000..b99bf4a3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjectItem.cpp
@@ -0,0 +1,444 @@
+/* $Id: UINotificationObjectItem.cpp $ */
+/** @file
+ * VBox Qt GUI - UINotificationObjectItem class implementation.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QFont>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QProgressBar>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINotificationObject.h"
+#include "UINotificationObjectItem.h"
+
+
+/*********************************************************************************************************************************
+* Class UINotificationObjectItem implementation. *
+*********************************************************************************************************************************/
+
+UINotificationObjectItem::UINotificationObjectItem(QWidget *pParent, UINotificationObject *pObject /* = 0 */)
+ : QWidget(pParent)
+ , m_pObject(pObject)
+ , m_pLayoutMain(0)
+ , m_pLayoutUpper(0)
+ , m_pLabelName(0)
+ , m_pButtonHelp(0)
+ , m_pButtonForget(0)
+ , m_pButtonClose(0)
+ , m_pLabelDetails(0)
+ , m_fHovered(false)
+ , m_fToggled(false)
+{
+ /* Make sure item is opaque. */
+ setAutoFillBackground(true);
+
+ /* Prepare main layout: */
+ m_pLayoutMain = new QVBoxLayout(this);
+ if (m_pLayoutMain)
+ {
+ /* Prepare upper layout: */
+ m_pLayoutUpper = new QHBoxLayout;
+ if (m_pLayoutUpper)
+ {
+ /* Prepare name label: */
+ m_pLabelName = new QLabel(this);
+ if (m_pLabelName)
+ {
+ m_pLabelName->setText(m_pObject->name());
+ m_pLayoutUpper->addWidget(m_pLabelName);
+ }
+
+ /* Prepare help button: */
+ if (!m_pObject->helpKeyword().isEmpty())
+ m_pButtonHelp = new QIToolButton(this);
+ if (m_pButtonHelp)
+ {
+ m_pButtonHelp->setIcon(UIIconPool::iconSet(":/help_16px.png"));
+ m_pButtonHelp->setIconSize(QSize(10, 10));
+ m_pButtonHelp->setProperty("helpkeyword", m_pObject->helpKeyword());
+ connect(m_pButtonHelp, &QIToolButton::clicked,
+ &msgCenter(), &UIMessageCenter::sltHandleHelpRequest);
+
+ m_pLayoutUpper->addWidget(m_pButtonHelp);
+ }
+
+ /* Prepare forget button: */
+ if (!m_pObject->internalName().isEmpty())
+ m_pButtonForget = new QIToolButton(this);
+ if (m_pButtonForget)
+ {
+ m_pButtonForget->setIcon(UIIconPool::iconSet(":/close_popup_16px.png"));
+ m_pButtonForget->setIconSize(QSize(10, 10));
+ connect(m_pButtonForget, &QIToolButton::clicked,
+ m_pObject, &UINotificationObject::dismiss);
+
+ m_pLayoutUpper->addWidget(m_pButtonForget);
+ }
+
+ /* Prepare close button: */
+ m_pButtonClose = new QIToolButton(this);
+ if (m_pButtonClose)
+ {
+ m_pButtonClose->setIcon(UIIconPool::iconSet(":/close_16px.png"));
+ m_pButtonClose->setIconSize(QSize(10, 10));
+ connect(m_pButtonClose, &QIToolButton::clicked,
+ m_pObject, &UINotificationObject::close);
+
+ m_pLayoutUpper->addWidget(m_pButtonClose);
+ }
+
+ /* Add to layout: */
+ m_pLayoutMain->addLayout(m_pLayoutUpper);
+ }
+
+ /* Prepare details label: */
+ m_pLabelDetails = new QIRichTextLabel(this);
+ if (m_pLabelDetails)
+ {
+ QFont myFont = m_pLabelDetails->font();
+ myFont.setPointSize(myFont.pointSize() - 1);
+ m_pLabelDetails->setBrowserFont(myFont);
+ m_pLabelDetails->setVisible(false);
+ int iHint = m_pLabelName->minimumSizeHint().width();
+ if (m_pButtonHelp)
+ iHint += m_pLayoutUpper->spacing() + m_pButtonHelp->minimumSizeHint().width();
+ if (m_pButtonForget)
+ iHint += m_pLayoutUpper->spacing() + m_pButtonForget->minimumSizeHint().width();
+ if (m_pButtonClose)
+ iHint += m_pLayoutUpper->spacing() + m_pButtonClose->minimumSizeHint().width();
+ m_pLabelDetails->setMinimumTextWidth(iHint);
+ m_pLabelDetails->setText(m_pObject->details());
+
+ m_pLayoutMain->addWidget(m_pLabelDetails);
+ }
+ }
+}
+
+bool UINotificationObjectItem::event(QEvent *pEvent)
+{
+ /* Handle required event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Enter:
+ case QEvent::MouseMove:
+ {
+ m_fHovered = true;
+ update();
+ break;
+ }
+ case QEvent::Leave:
+ {
+ m_fHovered = false;
+ update();
+ break;
+ }
+ case QEvent::MouseButtonRelease:
+ {
+ m_fToggled = !m_fToggled;
+ m_pLabelDetails->setVisible(m_fToggled);
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QWidget::event(pEvent);
+}
+
+void UINotificationObjectItem::paintEvent(QPaintEvent *pPaintEvent)
+{
+ /* Prepare painter: */
+ QPainter painter(this);
+ painter.setClipRect(pPaintEvent->rect());
+ /* Acquire palette: */
+ const bool fActive = isActiveWindow();
+ QPalette pal = QApplication::palette();
+
+ /* Gather suitable colors: */
+ QColor color = pal.color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window);
+ QColor color1;
+ QColor color2;
+ if (color.black() > 128)
+ {
+ color1 = color.lighter(110);
+ color2 = color.lighter(105);
+ }
+ else
+ {
+ color1 = color.darker(105);
+ color2 = color.darker(110);
+ }
+ /* Prepare background gradient: */
+ QLinearGradient grad(QPointF(0, 0), QPointF(width(), height()));
+ {
+ grad.setColorAt(0, color1);
+ grad.setColorAt(1, color2);
+ }
+ /* Fill background: */
+ painter.fillRect(rect(), grad);
+
+ /* If item is hovered: */
+ if (m_fHovered)
+ {
+ /* Gather suitable color: */
+ QColor color3 = pal.color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Highlight);
+ /* Override painter pen: */
+ painter.setPen(color3);
+ /* Draw frame: */
+ painter.drawRect(rect());
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressItem implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressItem::UINotificationProgressItem(QWidget *pParent, UINotificationProgress *pProgress /* = 0 */)
+ : UINotificationObjectItem(pParent, pProgress)
+ , m_pProgressBar(0)
+{
+ /* Main layout was prepared in base-class: */
+ if (m_pLayoutMain)
+ {
+ /* Name label was prepared in base-class: */
+ if (m_pLabelName)
+ m_pLabelName->setText(progress()->name());
+ /* Details label was prepared in base-class: */
+ if (m_pLabelDetails)
+ {
+ const int iHint = m_pLabelName->minimumSizeHint().width()
+ + m_pLayoutUpper->spacing()
+ + m_pButtonClose->minimumSizeHint().width();
+ m_pLabelDetails->setMinimumTextWidth(iHint);
+ updateDetails();
+ }
+
+ /* Prepare progress-bar: */
+ m_pProgressBar = new QProgressBar(this);
+ if (m_pProgressBar)
+ {
+ m_pProgressBar->setMinimum(0);
+ m_pProgressBar->setMaximum(100);
+ m_pProgressBar->setValue(progress()->percent());
+
+ m_pLayoutMain->addWidget(m_pProgressBar);
+ }
+ }
+
+ /* Prepare progress connections: */
+ connect(progress(), &UINotificationProgress::sigProgressStarted,
+ this, &UINotificationProgressItem::sltHandleProgressStarted);
+ connect(progress(), &UINotificationProgress::sigProgressChange,
+ this, &UINotificationProgressItem::sltHandleProgressChange);
+ connect(progress(), &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressItem::sltHandleProgressFinished);
+}
+
+void UINotificationProgressItem::sltHandleProgressStarted()
+{
+ /* Init close-button and progress-bar states: */
+ if (m_pButtonClose)
+ m_pButtonClose->setEnabled(progress()->isCancelable());
+ if (m_pProgressBar)
+ m_pProgressBar->setValue(0);
+ /* Update details with fetched stuff if any: */
+ if (m_pLabelDetails)
+ updateDetails();
+}
+
+void UINotificationProgressItem::sltHandleProgressChange(ulong uPercent)
+{
+ /* Update close-button and progress-bar states: */
+ if (m_pButtonClose)
+ m_pButtonClose->setEnabled(progress()->isCancelable());
+ if (m_pProgressBar)
+ m_pProgressBar->setValue(uPercent);
+}
+
+void UINotificationProgressItem::sltHandleProgressFinished()
+{
+ /* Finalize close-button and progress-bar states: */
+ if (m_pButtonClose)
+ m_pButtonClose->setEnabled(true);
+ if (m_pProgressBar)
+ m_pProgressBar->setValue(100);
+ /* Update details with error text if any: */
+ if (m_pLabelDetails)
+ updateDetails();
+}
+
+UINotificationProgress *UINotificationProgressItem::progress() const
+{
+ return qobject_cast<UINotificationProgress*>(m_pObject);
+}
+
+void UINotificationProgressItem::updateDetails()
+{
+ AssertPtrReturnVoid(m_pLabelDetails);
+ const QString strDetails = progress()->details();
+ const QString strError = progress()->error();
+ const QString strFullDetails = strError.isNull()
+ ? strDetails
+ : QString("%1<br>%2").arg(strDetails, strError);
+ m_pLabelDetails->setText(strFullDetails);
+ if (!strError.isEmpty())
+ {
+ m_fToggled = true;
+ m_pLabelDetails->setVisible(m_fToggled);
+ }
+}
+
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+
+
+/*********************************************************************************************************************************
+* Class UINotificationDownloaderItem implementation. *
+*********************************************************************************************************************************/
+
+UINotificationDownloaderItem::UINotificationDownloaderItem(QWidget *pParent, UINotificationDownloader *pDownloader /* = 0 */)
+ : UINotificationObjectItem(pParent, pDownloader)
+ , m_pProgressBar(0)
+{
+ /* Main layout was prepared in base-class: */
+ if (m_pLayoutMain)
+ {
+ /* Name label was prepared in base-class: */
+ if (m_pLabelName)
+ m_pLabelName->setText(downloader()->name());
+ /* Details label was prepared in base-class: */
+ if (m_pLabelDetails)
+ {
+ const int iHint = m_pLabelName->minimumSizeHint().width()
+ + m_pLayoutUpper->spacing()
+ + m_pButtonClose->minimumSizeHint().width();
+ m_pLabelDetails->setMinimumTextWidth(iHint);
+ updateDetails();
+ }
+
+ /* Prepare progress-bar: */
+ m_pProgressBar = new QProgressBar(this);
+ if (m_pProgressBar)
+ {
+ m_pProgressBar->setMinimum(0);
+ m_pProgressBar->setMaximum(100);
+ m_pProgressBar->setValue(downloader()->percent());
+
+ m_pLayoutMain->addWidget(m_pProgressBar);
+ }
+ }
+
+ /* Prepare downloader connections: */
+ connect(downloader(), &UINotificationDownloader::sigProgressStarted,
+ this, &UINotificationDownloaderItem::sltHandleProgressStarted);
+ connect(downloader(), &UINotificationDownloader::sigProgressChange,
+ this, &UINotificationDownloaderItem::sltHandleProgressChange);
+ connect(downloader(), &UINotificationDownloader::sigProgressFailed,
+ this, &UINotificationDownloaderItem::sltHandleProgressFinished);
+ connect(downloader(), &UINotificationDownloader::sigProgressCanceled,
+ this, &UINotificationDownloaderItem::sltHandleProgressFinished);
+ connect(downloader(), &UINotificationDownloader::sigProgressFinished,
+ this, &UINotificationDownloaderItem::sltHandleProgressFinished);
+}
+
+void UINotificationDownloaderItem::sltHandleProgressStarted()
+{
+ /* Init progress-bar state: */
+ if (m_pProgressBar)
+ m_pProgressBar->setValue(0);
+ /* Update details with fetched stuff if any: */
+ if (m_pLabelDetails)
+ updateDetails();
+}
+
+void UINotificationDownloaderItem::sltHandleProgressChange(ulong uPercent)
+{
+ /* Update progress-bar state: */
+ if (m_pProgressBar)
+ m_pProgressBar->setValue(uPercent);
+}
+
+void UINotificationDownloaderItem::sltHandleProgressFinished()
+{
+ /* Finalize progress-bar state: */
+ if (m_pProgressBar)
+ m_pProgressBar->setValue(100);
+ /* Update details with error text if any: */
+ if (m_pLabelDetails)
+ updateDetails();
+}
+
+UINotificationDownloader *UINotificationDownloaderItem::downloader() const
+{
+ return qobject_cast<UINotificationDownloader*>(m_pObject);
+}
+
+void UINotificationDownloaderItem::updateDetails()
+{
+ AssertPtrReturnVoid(m_pLabelDetails);
+ const QString strDetails = downloader()->details();
+ const QString strError = downloader()->error();
+ const QString strFullDetails = strError.isNull()
+ ? strDetails
+ : QString("%1<br>%2").arg(strDetails, strError);
+ m_pLabelDetails->setText(strFullDetails);
+ if (!strError.isEmpty())
+ {
+ m_fToggled = true;
+ m_pLabelDetails->setVisible(m_fToggled);
+ }
+}
+
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+
+/*********************************************************************************************************************************
+* Namespace UINotificationProgressItem implementation. *
+*********************************************************************************************************************************/
+
+UINotificationObjectItem *UINotificationItem::create(QWidget *pParent, UINotificationObject *pObject)
+{
+ /* Handle known types: */
+ if (pObject->inherits("UINotificationProgress"))
+ return new UINotificationProgressItem(pParent, static_cast<UINotificationProgress*>(pObject));
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ else if (pObject->inherits("UINotificationDownloader"))
+ return new UINotificationDownloaderItem(pParent, static_cast<UINotificationDownloader*>(pObject));
+#endif
+ /* Handle defaults: */
+ return new UINotificationObjectItem(pParent, pObject);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjectItem.h b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjectItem.h
new file mode 100644
index 00000000..e76f90e7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjectItem.h
@@ -0,0 +1,173 @@
+/* $Id: UINotificationObjectItem.h $ */
+/** @file
+ * VBox Qt GUI - UINotificationObjectItem class declaration.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_notificationcenter_UINotificationObjectItem_h
+#define FEQT_INCLUDED_SRC_notificationcenter_UINotificationObjectItem_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* VBox includes: */
+#include <iprt/cdefs.h>
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QLabel;
+class QProgressBar;
+class QVBoxLayout;
+class QIRichTextLabel;
+class QIToolButton;
+class UINotificationObject;
+class UINotificationProgress;
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+class UINotificationDownloader;
+#endif
+
+/** QWidget-based notification-object item. */
+class UINotificationObjectItem : public QWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs notification-object item, passing @a pParent to the base-class.
+ * @param pObject Brings the notification-object this item created for. */
+ UINotificationObjectItem(QWidget *pParent, UINotificationObject *pObject = 0);
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Holds the notification-object this item created for. */
+ UINotificationObject *m_pObject;
+
+ /** Holds the main layout instance. */
+ QVBoxLayout *m_pLayoutMain;
+ /** Holds the upper layout instance. */
+ QHBoxLayout *m_pLayoutUpper;
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the help button instance. */
+ QIToolButton *m_pButtonHelp;
+ /** Holds the forget button instance. */
+ QIToolButton *m_pButtonForget;
+ /** Holds the close button instance. */
+ QIToolButton *m_pButtonClose;
+ /** Holds the details label instance. */
+ QIRichTextLabel *m_pLabelDetails;
+
+ /** Holds whether item is hovered. */
+ bool m_fHovered;
+ /** Holds whether item is toggled. */
+ bool m_fToggled;
+};
+
+/** UINotificationObjectItem extension for notification-progress. */
+class UINotificationProgressItem : public UINotificationObjectItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs notification-progress item, passing @a pParent to the base-class.
+ * @param pProgress Brings the notification-progress this item created for. */
+ UINotificationProgressItem(QWidget *pParent, UINotificationProgress *pProgress = 0);
+
+private slots:
+
+ /** Handles signal about progress started. */
+ void sltHandleProgressStarted();
+ /** Handles signal about progress changed.
+ * @param uPercent Brings new progress percentage value. */
+ void sltHandleProgressChange(ulong uPercent);
+ /** Handles signal about progress finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the notification-progress this item created for. */
+ UINotificationProgress *progress() const;
+
+ /** Updates details. */
+ void updateDetails();
+
+ /** Holds the progress-bar instance. */
+ QProgressBar *m_pProgressBar;
+};
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+/** UINotificationObjectItem extension for notification-downloader. */
+class UINotificationDownloaderItem : public UINotificationObjectItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs notification-downloader item, passing @a pParent to the base-class.
+ * @param pDownloader Brings the notification-downloader this item created for. */
+ UINotificationDownloaderItem(QWidget *pParent, UINotificationDownloader *pDownloader = 0);
+
+private slots:
+
+ /** Handles signal about progress started. */
+ void sltHandleProgressStarted();
+ /** Handles signal about progress changed.
+ * @param uPercent Brings new progress percentage value. */
+ void sltHandleProgressChange(ulong uPercent);
+ /** Handles signal about progress finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the notification-downloader this item created for. */
+ UINotificationDownloader *downloader() const;
+
+ /** Updates details. */
+ void updateDetails();
+
+ /** Holds the progress-bar instance. */
+ QProgressBar *m_pProgressBar;
+};
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+/** Notification-object factory. */
+namespace UINotificationItem
+{
+ /** Creates notification-object of required type.
+ * @param pParent Brings the parent constructed item being attached to.
+ * @param pObject Brings the notification-object item being constructed for. */
+ UINotificationObjectItem *create(QWidget *pParent, UINotificationObject *pObject);
+}
+
+#endif /* !FEQT_INCLUDED_SRC_notificationcenter_UINotificationObjectItem_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjects.cpp b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjects.cpp
new file mode 100644
index 00000000..bb280277
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjects.cpp
@@ -0,0 +1,4362 @@
+/* $Id: UINotificationObjects.cpp $ */
+/** @file
+ * VBox Qt GUI - Various UINotificationObjects implementations.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QFileInfo>
+
+/* GUI includes: */
+#include "UIConverter.h"
+#include "UIErrorString.h"
+#include "UIExtraDataManager.h"
+#include "UIHostComboEditor.h"
+#include "UINotificationCenter.h"
+#include "UINotificationObjects.h"
+#include "UITranslator.h"
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+# include "UIDownloaderExtensionPack.h"
+# include "UIDownloaderGuestAdditions.h"
+# include "UIDownloaderUserManual.h"
+# include "UINewVersionChecker.h"
+#endif
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CBooleanFormValue.h"
+#include "CChoiceFormValue.h"
+#include "CCloudNetwork.h"
+#include "CCloudProfile.h"
+#include "CCloudProvider.h"
+#include "CCloudProviderManager.h"
+#include "CConsole.h"
+#include "CDHCPServer.h"
+#include "CDisplay.h"
+#include "CEmulatedUSB.h"
+#include "CExtPack.h"
+#include "CGraphicsAdapter.h"
+#include "CHostNetworkInterface.h"
+#include "CHostOnlyNetwork.h"
+#include "CMediumAttachment.h"
+#include "CNATNetwork.h"
+#include "CNetworkAdapter.h"
+#include "CRangedIntegerFormValue.h"
+#include "CRecordingSettings.h"
+#include "CStringFormValue.h"
+#ifdef VBOX_WITH_UPDATE_AGENT
+# include "CSystemProperties.h"
+#endif
+#include "CUnattended.h"
+#include "CUpdateAgent.h"
+#include "CVRDEServer.h"
+
+/* Other VBox stuff: */
+#ifdef VBOX_WS_X11
+# include <iprt/env.h>
+#endif
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+
+/*********************************************************************************************************************************
+* Class UINotificationMessage implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+QMap<QString, QUuid> UINotificationMessage::m_messages = QMap<QString, QUuid>();
+
+/* static */
+void UINotificationMessage::cannotFindHelpFile(const QString &strLocation)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't find help file ..."),
+ QApplication::translate("UIMessageCenter", "Failed to find the following help file: <b>%1</b>")
+ .arg(strLocation));
+}
+
+/* static */
+void UINotificationMessage::cannotOpenURL(const QString &strUrl)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't open URL ..."),
+ QApplication::translate("UIMessageCenter", "Failed to open <tt>%1</tt>. "
+ "Make sure your desktop environment can properly handle URLs of this type.")
+ .arg(strUrl));
+}
+
+/* static */
+void UINotificationMessage::remindAboutBetaBuild()
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "BETA build warning!"),
+ QApplication::translate("UIMessageCenter", "You are running a prerelease version of VirtualBox. "
+ "This version is not suitable for production use."));
+}
+
+/* static */
+void UINotificationMessage::remindAboutExperimentalBuild()
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Experimental build warning!"),
+ QApplication::translate("UIMessageCenter", "You are running an EXPERIMENTAL build of VirtualBox. "
+ "This version is not suitable for production use."));
+}
+
+/* static */
+void UINotificationMessage::warnAboutInvalidEncryptionPassword(const QString &strPasswordId)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Invalid Password ..."),
+ QApplication::translate("UIMessageCenter", "Encryption password for <nobr>ID = '%1'</nobr> is invalid.")
+ .arg(strPasswordId));
+}
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+/* static */
+void UINotificationMessage::showUpdateNotFound()
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Nothing to update ..."),
+ QApplication::translate("UIMessageCenter", "You are already running the most recent version of VirtualBox."));
+}
+
+/* static */
+void UINotificationMessage::showUpdateSuccess(const QString &strVersion, const QString &strLink)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "New version found ..."),
+ QApplication::translate("UIMessageCenter", "<p>A new version of VirtualBox has been released! Version <b>%1</b> is available "
+ "at <a href=\"https://www.virtualbox.org/\">virtualbox.org</a>.</p>"
+ "<p>You can download this version using the link:</p>"
+ "<p><a href=%2>%3</a></p>").arg(strVersion, strLink, strLink));
+}
+
+/* static */
+void UINotificationMessage::askUserToDownloadExtensionPack(const QString &strExtPackName,
+ const QString &strExtPackVersion,
+ const QString &strVBoxVersion)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Update is required ..."),
+ QApplication::translate("UIMessageCenter", "<p>You have version %1 of the <b><nobr>%2</nobr></b> installed.</p>"
+ "<p>You should download and install version %3 of this extension pack from "
+ "Oracle!</p>").arg(strExtPackVersion, strExtPackName, strVBoxVersion));
+}
+
+/* static */
+void UINotificationMessage::cannotValidateGuestAdditionsSHA256Sum(const QString &strUrl,
+ const QString &strSrc)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Unable to validate guest additions image ..."),
+ QApplication::translate("UIMessageCenter", "<p>The <b>VirtualBox Guest Additions</b> disk image file has been "
+ "successfully downloaded from <nobr><a href=\"%1\">%1</a></nobr> and saved "
+ "locally as <nobr><b>%2</b>, </nobr>but the SHA-256 checksum verification "
+ "failed.</p><p>Please do the download, installation and verification "
+ "manually.</p>").arg(strUrl, strSrc));
+}
+
+/* static */
+void UINotificationMessage::warnAboutUserManualDownloaded(const QString &strUrl, const QString &strTarget)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "User manual downloaded ..."),
+ QApplication::translate("UIMessageCenter", "<p>The VirtualBox User Manual has been successfully downloaded from "
+ "<nobr><a href=\"%1\">%1</a></nobr> and saved locally as "
+ "<nobr><b>%2</b>.</nobr></p>").arg(strUrl, strTarget));
+}
+
+/* static */
+void UINotificationMessage::cannotValidateExtentionPackSHA256Sum(const QString &strExtPackName,
+ const QString &strFrom,
+ const QString &strTo)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Unable to validate extension pack ..."),
+ QApplication::translate("UIMessageCenter", "<p>The <b><nobr>%1</nobr></b> has been successfully downloaded "
+ "from <nobr><a href=\"%2\">%2</a></nobr> and saved locally as "
+ "<nobr><b>%3</b>, </nobr>but the SHA-256 checksum verification failed.</p>"
+ "<p>Please do the download, installation and verification manually.</p>")
+ .arg(strExtPackName, strFrom, strTo));
+}
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+/* static */
+void UINotificationMessage::cannotCreateMachineFolder(const QString &strPath,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create machine folder ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create machine folder at <nobr><b>%1</b></nobr>.")
+ .arg(strPath),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotOverwriteMachineFolder(const QString &strPath,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't overwrite machine folder ..."),
+ QApplication::translate("UIMessageCenter", "Failed to overwrite machine folder at <nobr><b>%1</b></nobr>.")
+ .arg(strPath),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotRemoveMachineFolder(const QString &strPath,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't remove machine folder ..."),
+ QApplication::translate("UIMessageCenter", "Failed to remove machine folder at <nobr><b>%1</b></nobr>.")
+ .arg(strPath),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotReregisterExistingMachine(const QString &strName, const QString &strLocation)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't add machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to add virtual machine <b>%1</b> located in <i>%2</i> because its "
+ "already present.")
+ .arg(strName, strLocation));
+}
+
+/* static */
+void UINotificationMessage::cannotResolveCollisionAutomatically(const QString &strCollisionName, const QString &strGroupName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't resolve collision ..."),
+ QApplication::translate("UIMessageCenter", "<p>You are trying to move machine <nobr><b>%1</b></nobr> to group "
+ "<nobr><b>%2</b></nobr> which already have another item with the same "
+ "name.</p><p>Please resolve this name conflict and try again.</p>")
+ .arg(strCollisionName, strGroupName));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireCloudMachineSettings(const QString &strErrorDetails)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Cloud failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire cloud machine settings.") +
+ strErrorDetails);
+}
+
+/* static */
+void UINotificationMessage::cannotCreateMediumStorageInFAT(const QString &strPath,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create medium ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create medium storage at <nobr><b>%1</b></nobr>.")
+ .arg(strPath),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotOverwriteMediumStorage(const QString &strPath,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't overwrite medium ..."),
+ QApplication::translate("UIMessageCenter", "Failed to overwrite medium storage at <nobr><b>%1</b></nobr>.")
+ .arg(strPath),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotOpenLicenseFile(const QString &strPath)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't open license file ..."),
+ QApplication::translate("UIMessageCenter", "Failed to open the license file <nobr><b>%1</b></nobr>. Check file "
+ "permissions.").arg(strPath));
+}
+
+/* static */
+void UINotificationMessage::warnAboutPublicKeyFilePathIsEmpty()
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Public key missing ..."),
+ QApplication::translate("UIMessageCenter", "Public key file path is empty."));
+}
+
+/* static */
+void UINotificationMessage::warnAboutPublicKeyFileDoesntExist(const QString &strPath)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Public key missing ..."),
+ QApplication::translate("UIMessageCenter", "Failed to open the public key file <nobr><b>%1</b></nobr>. "
+ "File doesn't exist.").arg(strPath));
+}
+
+/* static */
+void UINotificationMessage::warnAboutPublicKeyFileIsOfTooLargeSize(const QString &strPath)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Public key too large ..."),
+ QApplication::translate("UIMessageCenter", "Failed to open the public key file <nobr><b>%1</b></nobr>. File is too "
+ "large for the key.").arg(strPath));
+}
+
+/* static */
+void UINotificationMessage::warnAboutPublicKeyFileIsntReadable(const QString &strPath)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Public key isn't readable ..."),
+ QApplication::translate("UIMessageCenter", "Failed to open the public key file <nobr><b>%1</b></nobr>. Check file "
+ "permissions.").arg(strPath));
+}
+
+/* static */
+void UINotificationMessage::warnAboutDHCPServerIsNotEnabled(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "DHCP server isn't enabled ..."),
+ QApplication::translate("UIMessageCenter", "Network <nobr><b>%1</b></nobr> is set to obtain the address "
+ "automatically but the corresponding DHCP server is not enabled.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutInvalidIPv4Address(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Invalid IPv4 address ..."),
+ QApplication::translate("UIMessageCenter", "Network <nobr><b>%1</b></nobr> does not "
+ "currently have a valid IPv4 address.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutInvalidIPv4Mask(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Invalid IPv4 mask ..."),
+ QApplication::translate("UIMessageCenter", "Network <nobr><b>%1</b></nobr> does not "
+ "currently have a valid IPv4 mask.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutInvalidIPv6Address(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Invalid IPv6 address ..."),
+ QApplication::translate("UIMessageCenter", "Network <nobr><b>%1</b></nobr> does not "
+ "currently have a valid IPv6 address.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutInvalidIPv6PrefixLength(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Invalid IPv6 prefix length ..."),
+ QApplication::translate("UIMessageCenter", "Network <nobr><b>%1</b></nobr> does not "
+ "currently have a valid IPv6 prefix length.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutInvalidDHCPServerAddress(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Invalid DHCP server address ..."),
+ QApplication::translate("UIMessageCenter", "Network <nobr><b>%1</b></nobr> does not "
+ "currently have a valid DHCP server address.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutInvalidDHCPServerMask(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Invalid DHCP server mask ..."),
+ QApplication::translate("UIMessageCenter", "Network <nobr><b>%1</b></nobr> does not "
+ "currently have a valid DHCP server mask.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutInvalidDHCPServerLowerAddress(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Invalid DHCP lower address ..."),
+ QApplication::translate("UIMessageCenter", "Network <nobr><b>%1</b></nobr> does not "
+ "currently have a valid DHCP server lower address bound.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutInvalidDHCPServerUpperAddress(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Invalid DHCP upper address ..."),
+ QApplication::translate("UIMessageCenter", "Network <nobr><b>%1</b></nobr> does not "
+ "currently have a valid DHCP server upper address bound.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutNoNameSpecified(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "No name specified ..."),
+ QApplication::translate("UIMessageCenter", "No new name specified for the network previously called <b>%1</b>.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutNameAlreadyBusy(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Name already busy ..."),
+ QApplication::translate("UIMessageCenter", "The name <b>%1</b> is being used for several networks.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutNoIPv4PrefixSpecified(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "No IPv4 prefix specified ..."),
+ QApplication::translate("UIMessageCenter", "No IPv4 prefix specified for the NAT network <b>%1</b>.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::warnAboutNoIPv6PrefixSpecified(const QString &strName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "No IPv6 prefix specified ..."),
+ QApplication::translate("UIMessageCenter", "No IPv6 prefix specified for the NAT network <b>%1</b>.")
+ .arg(strName));
+}
+
+/* static */
+void UINotificationMessage::cannotMountImage(const QString &strMachineName, const QString &strMediumName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't mount image ..."),
+ QApplication::translate("UIMessageCenter", "<p>Could not insert the <b>%1</b> disk image file into the virtual machine "
+ "<b>%2</b>, as the machine has no optical drives. Please add a drive using "
+ "the storage page of the virtual machine settings window.</p>")
+ .arg(strMediumName, strMachineName));
+}
+
+/* static */
+void UINotificationMessage::cannotSendACPIToMachine()
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't send ACPI shutdown ..."),
+ QApplication::translate("UIMessageCenter", "You are trying to shut down the guest with the ACPI power button. "
+ "This is currently not possible because the guest does not support "
+ "software shutdown."));
+}
+
+/* static */
+void UINotificationMessage::remindAboutAutoCapture()
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Auto capture keyboard ..."),
+ QApplication::translate("UIMessageCenter", "<p>You have the <b>Auto capture keyboard</b> option turned on. "
+ "This will cause the Virtual Machine to automatically <b>capture</b> "
+ "the keyboard every time the VM window is activated and make it "
+ "unavailable to other applications running on your host machine: "
+ "when the keyboard is captured, all keystrokes (including system ones "
+ "like Alt-Tab) will be directed to the VM.</p>"
+ "<p>You can press the <b>host key</b> at any time to <b>uncapture</b> the "
+ "keyboard and mouse (if it is captured) and return them to normal "
+ "operation. The currently assigned host key is shown on the status bar "
+ "at the bottom of the Virtual Machine window. This icon, together "
+ "with the mouse icon placed nearby, indicate the current keyboard "
+ "and mouse capture state.</p>") +
+ QApplication::translate("UIMessageCenter", "<p>The host key is currently defined as <b>%1</b>.</p>",
+ "additional message box paragraph")
+ .arg(UIHostCombo::toReadableString(gEDataManager->hostKeyCombination())),
+ "remindAboutAutoCapture");
+}
+
+/* static */
+void UINotificationMessage::remindAboutGuestAdditionsAreNotActive()
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Guest additions inactive ..."),
+ QApplication::translate("UIMessageCenter", "<p>The VirtualBox Guest Additions do not appear to be available on this "
+ "virtual machine, and shared folders cannot be used without them. To use "
+ "shared folders inside the virtual machine, please install the Guest "
+ "Additions if they are not installed, or re-install them if they are not "
+ "working correctly, by selecting <b>Insert Guest Additions CD image</b> from "
+ "the <b>Devices</b> menu. If they are installed but the machine is not yet "
+ "fully started then shared folders will be available once it is.</p>"),
+ "remindAboutGuestAdditionsAreNotActive");
+}
+
+/* static */
+void UINotificationMessage::remindAboutMouseIntegration(bool fSupportsAbsolute)
+{
+ if (fSupportsAbsolute)
+ {
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Mouse integration ..."),
+ QApplication::translate("UIMessageCenter", "<p>The Virtual Machine reports that the guest OS supports <b>mouse "
+ "pointer integration</b>. This means that you do not need to "
+ "<i>capture</i> the mouse pointer to be able to use it in your guest "
+ "OS -- all mouse actions you perform when the mouse pointer is over the "
+ "Virtual Machine's display are directly sent to the guest OS. If the "
+ "mouse is currently captured, it will be automatically uncaptured.</p>"
+ "<p>The mouse icon on the status bar will look "
+ "like&nbsp;<img src=:/mouse_seamless_16px.png/>&nbsp;to inform you that "
+ "mouse pointer integration is supported by the guest OS and is currently "
+ "turned on.</p><p><b>Note</b>: Some applications may behave incorrectly "
+ "in mouse pointer integration mode. You can always disable it for the "
+ "current session (and enable it again) by selecting the corresponding "
+ "action from the menu bar.</p>"),
+ "remindAboutMouseIntegration");
+ }
+ else
+ {
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Mouse integration ..."),
+ QApplication::translate("UIMessageCenter", "<p>The Virtual Machine reports that the guest OS does not support "
+ "<b>mouse pointer integration</b> in the current video mode. You need to "
+ "capture the mouse (by clicking over the VM display or pressing the host "
+ "key) in order to use the mouse inside the guest OS.</p>"),
+ "remindAboutMouseIntegration");
+ }
+}
+
+/* static */
+void UINotificationMessage::remindAboutPausedVMInput()
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Paused VM input ..."),
+ QApplication::translate("UIMessageCenter", "<p>The Virtual Machine is currently in the <b>Paused</b> state and not able "
+ "to see any keyboard or mouse input. If you want to continue to work inside "
+ "the VM, you need to resume it by selecting the corresponding action from the "
+ "menu bar.</p>"),
+ "remindAboutPausedVMInput");
+}
+
+/* static */
+void UINotificationMessage::forgetAboutPausedVMInput()
+{
+ destroyMessage("remindAboutPausedVMInput");
+}
+
+/* static */
+void UINotificationMessage::remindAboutWrongColorDepth(ulong uRealBPP, ulong uWantedBPP)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Wrong color depth ..."),
+ QApplication::translate("UIMessageCenter", "<p>The virtual screen is currently set to a <b>%1&nbsp;bit</b> color mode. "
+ "For better performance please change this to <b>%2&nbsp;bit</b>. This can "
+ "usually be done from the <b>Display</b> section of the guest operating "
+ "system's Control Panel or System Settings.</p>")
+ .arg(uRealBPP).arg(uWantedBPP),
+ "remindAboutWrongColorDepth");
+}
+
+/* static */
+void UINotificationMessage::forgetAboutWrongColorDepth()
+{
+ destroyMessage("remindAboutWrongColorDepth");
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireVirtualBoxParameter(const CVirtualBox &comVBox,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "VirtualBox failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire VirtualBox parameter.") +
+ UIErrorString::formatErrorInfo(comVBox),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireApplianceParameter(const CAppliance &comAppliance,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Appliance failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire appliance parameter.") +
+ UIErrorString::formatErrorInfo(comAppliance),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireExtensionPackManagerParameter(const CExtPackManager &comEPManager)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Extension Pack failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire Extension Pack Manager parameter.") +
+ UIErrorString::formatErrorInfo(comEPManager));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireExtensionPackParameter(const CExtPack &comPackage)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Extension Pack failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire Extension Pack parameter.") +
+ UIErrorString::formatErrorInfo(comPackage));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireHostParameter(const CHost &comHost)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Host failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire host parameter.") +
+ UIErrorString::formatErrorInfo(comHost));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireMediumParameter(const CMedium &comMedium)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Medium failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire medium parameter.") +
+ UIErrorString::formatErrorInfo(comMedium));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireSessionParameter(const CSession &comSession)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Session failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire session parameter.") +
+ UIErrorString::formatErrorInfo(comSession));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireMachineParameter(const CMachine &comMachine)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Machine failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire machine parameter.") +
+ UIErrorString::formatErrorInfo(comMachine));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireSnapshotParameter(const CSnapshot &comSnapshot)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Snapshot failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire snapshot parameter.") +
+ UIErrorString::formatErrorInfo(comSnapshot));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireDHCPServerParameter(const CDHCPServer &comServer)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "DHCP server failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire DHCP server parameter.") +
+ UIErrorString::formatErrorInfo(comServer));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireCloudNetworkParameter(const CCloudNetwork &comNetwork)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Cloud failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire cloud network parameter.") +
+ UIErrorString::formatErrorInfo(comNetwork));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireHostNetworkInterfaceParameter(const CHostNetworkInterface &comInterface)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Host network interface failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire host network interface parameter.") +
+ UIErrorString::formatErrorInfo(comInterface));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireHostOnlyNetworkParameter(const CHostOnlyNetwork &comNetwork)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Host only network failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire host only network parameter.") +
+ UIErrorString::formatErrorInfo(comNetwork));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireNATNetworkParameter(const CNATNetwork &comNetwork)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "NAT network failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire NAT network parameter.") +
+ UIErrorString::formatErrorInfo(comNetwork));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireDispayParameter(const CDisplay &comDisplay)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Display failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire display parameter.") +
+ UIErrorString::formatErrorInfo(comDisplay));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireUpdateAgentParameter(const CUpdateAgent &comAgent)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Update failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire update agent parameter.") +
+ UIErrorString::formatErrorInfo(comAgent));
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireVirtualSystemDescriptionParameter(const CVirtualSystemDescription &comVsd,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "VSD failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire VSD parameter.") +
+ UIErrorString::formatErrorInfo(comVsd),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireVirtualSystemDescriptionFormParameter(const CVirtualSystemDescriptionForm &comVsdForm,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "VSD form failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire VSD form parameter.") +
+ UIErrorString::formatErrorInfo(comVsdForm),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireCloudProviderManagerParameter(const CCloudProviderManager &comCloudProviderManager,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Cloud failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire cloud provider manager parameter.") +
+ UIErrorString::formatErrorInfo(comCloudProviderManager),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireCloudProviderParameter(const CCloudProvider &comCloudProvider,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Cloud failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire cloud provider parameter.") +
+ UIErrorString::formatErrorInfo(comCloudProvider),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireCloudProfileParameter(const CCloudProfile &comCloudProfile,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Cloud failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire cloud profile parameter.") +
+ UIErrorString::formatErrorInfo(comCloudProfile),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotAcquireCloudMachineParameter(const CCloudMachine &comCloudMachine,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Cloud failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire cloud machine parameter.") +
+ UIErrorString::formatErrorInfo(comCloudMachine),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotChangeMediumParameter(const CMedium &comMedium)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Medium failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change the parameter of the medium <b>%1</b>.")
+ .arg(CMedium(comMedium).GetLocation()) +
+ UIErrorString::formatErrorInfo(comMedium));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeMachineParameter(const CMachine &comMachine)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Machine failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change the parameter of the virtual machine <b>%1</b>.")
+ .arg(CMachine(comMachine).GetName()) +
+ UIErrorString::formatErrorInfo(comMachine));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeGraphicsAdapterParameter(const CGraphicsAdapter &comAdapter)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Graphics adapter failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change graphics adapter parameter.") +
+ UIErrorString::formatErrorInfo(comAdapter));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeAudioAdapterParameter(const CAudioAdapter &comAdapter)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Audio adapter failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change audio adapter parameter.") +
+ UIErrorString::formatErrorInfo(comAdapter));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeNetworkAdapterParameter(const CNetworkAdapter &comAdapter)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Network adapter failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change network adapter parameter.") +
+ UIErrorString::formatErrorInfo(comAdapter));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeDHCPServerParameter(const CDHCPServer &comServer)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "DHCP server failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change DHCP server parameter.") +
+ UIErrorString::formatErrorInfo(comServer));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeCloudNetworkParameter(const CCloudNetwork &comNetwork)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Cloud failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change cloud network parameter.") +
+ UIErrorString::formatErrorInfo(comNetwork));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeHostNetworkInterfaceParameter(const CHostNetworkInterface &comInterface)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Host network interface failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change host network interface parameter.") +
+ UIErrorString::formatErrorInfo(comInterface));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeHostOnlyNetworkParameter(const CHostOnlyNetwork &comNetwork)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Host only network failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change host only network parameter.") +
+ UIErrorString::formatErrorInfo(comNetwork));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeNATNetworkParameter(const CNATNetwork &comNetwork)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "NAT network failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change NAT network parameter.") +
+ UIErrorString::formatErrorInfo(comNetwork));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeCloudProfileParameter(const CCloudProfile &comProfile)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Cloud failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to assign cloud profile parameter.") +
+ UIErrorString::formatErrorInfo(comProfile));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeUpdateAgentParameter(const CUpdateAgent &comAgent)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Update failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to assign update agent parameter.") +
+ UIErrorString::formatErrorInfo(comAgent));
+}
+
+/* static */
+void UINotificationMessage::cannotChangeVirtualSystemDescriptionParameter(const CVirtualSystemDescription &comVsd,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "VSD failure ..."),
+ QApplication::translate("UIMessageCenter", "Failed to assign VSD parameter.") +
+ UIErrorString::formatErrorInfo(comVsd),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotEnumerateHostUSBDevices(const CHost &comHost)
+{
+ /* Refer users to manual's trouble shooting section depending on the host platform: */
+ QString strHelpKeyword;
+#if defined(RT_OS_LINUX)
+ strHelpKeyword = "ts_usb-linux";
+#elif defined(RT_OS_WINDOWS)
+ strHelpKeyword = "ts_win-guests";
+#elif defined(RT_OS_SOLARIS)
+ strHelpKeyword = "ts_sol-guests";
+#elif defined(RT_OS_DARWIN)
+#endif
+
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't enumerate USB devices ..."),
+ QApplication::translate("UIMessageCenter", "Failed to enumerate host USB devices.") +
+ UIErrorString::formatErrorInfo(comHost),
+ "cannotEnumerateHostUSBDevices",
+ strHelpKeyword);
+}
+
+/* static */
+void UINotificationMessage::cannotOpenMedium(const CVirtualBox &comVBox,
+ const QString &strLocation,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't open medium ..."),
+ QApplication::translate("UIMessageCenter", "Failed to open the disk image file <nobr><b>%1</b></nobr>.")
+ .arg(strLocation) +
+ UIErrorString::formatErrorInfo(comVBox),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotPauseMachine(const CConsole &comConsole)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't pause machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to pause the execution of the virtual machine <b>%1</b>.")
+ .arg(CConsole(comConsole).GetMachine().GetName()) +
+ UIErrorString::formatErrorInfo(comConsole));
+}
+
+/* static */
+void UINotificationMessage::cannotResumeMachine(const CConsole &comConsole)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't resume machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to resume the execution of the virtual machine <b>%1</b>.")
+ .arg(CConsole(comConsole).GetMachine().GetName()) +
+ UIErrorString::formatErrorInfo(comConsole));
+}
+
+/* static */
+void UINotificationMessage::cannotACPIShutdownMachine(const CConsole &comConsole)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't shutdown machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to send the ACPI Power Button press event to the virtual machine "
+ "<b>%1</b>.").arg(CConsole(comConsole).GetMachine().GetName()) +
+ UIErrorString::formatErrorInfo(comConsole));
+}
+
+/* static */
+void UINotificationMessage::cannotCreateAppliance(const CVirtualBox &comVBox,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create appliance ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create appliance.") +
+ UIErrorString::formatErrorInfo(comVBox),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotRegisterMachine(const CVirtualBox &comVBox,
+ const QString &strName,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't register machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to register machine <b>%1</b>.")
+ .arg(strName) +
+ UIErrorString::formatErrorInfo(comVBox),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotCreateMachine(const CVirtualBox &comVBox,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create machine.") +
+ UIErrorString::formatErrorInfo(comVBox),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotFindMachineById(const CVirtualBox &comVBox,
+ const QUuid &uMachineId,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't find machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to find the machine with following ID: <nobr><b>%1</b></nobr>.")
+ .arg(uMachineId.toString()) +
+ UIErrorString::formatErrorInfo(comVBox),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotOpenMachine(const CVirtualBox &comVBox, const QString &strLocation)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't open machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to open virtual machine located in %1.")
+ .arg(strLocation) +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotCreateMediumStorage(const CVirtualBox &comVBox,
+ const QString &strPath,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create medium storage ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create medium storage at <nobr><b>%1</b></nobr>.")
+ .arg(strPath) +
+ UIErrorString::formatErrorInfo(comVBox),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotGetExtensionPackManager(const CVirtualBox &comVBox)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't get Extension Pack Manager ..."),
+ QApplication::translate("UIMessageCenter", "Failed to acquire Extension Pack Manager.") +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotCreateVfsExplorer(const CAppliance &comAppliance, UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create VFS explorer ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create VFS explorer to check files.") +
+ UIErrorString::formatErrorInfo(comAppliance),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotAddDiskEncryptionPassword(const CAppliance &comAppliance, UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Bad password ..."),
+ QApplication::translate("UIMessageCenter", "Bad password or authentication failure.") +
+ UIErrorString::formatErrorInfo(comAppliance),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotInterpretAppliance(const CAppliance &comAppliance, UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't interpret appliance ..."),
+ QApplication::translate("UIMessageCenter", "Failed to interpret appliance being imported.") +
+ UIErrorString::formatErrorInfo(comAppliance),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotCreateVirtualSystemDescription(const CAppliance &comAppliance, UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create VSD ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create VSD.") +
+ UIErrorString::formatErrorInfo(comAppliance),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotOpenExtPack(const CExtPackManager &comExtPackManager, const QString &strFilename)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't open extension pack ..."),
+ QApplication::translate("UIMessageCenter", "Failed to open the Extension Pack <b>%1</b>.")
+ .arg(strFilename) +
+ UIErrorString::formatErrorInfo(comExtPackManager));
+}
+
+/* static */
+void UINotificationMessage::cannotReadExtPack(const CExtPackFile &comExtPackFile, const QString &strFilename)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't read extension pack ..."),
+ QApplication::translate("UIMessageCenter", "Failed to read the Extension Pack <b>%1</b>.")
+ .arg(strFilename) +
+ comExtPackFile.GetWhyUnusable());
+}
+
+/* static */
+void UINotificationMessage::cannotFindCloudNetwork(const CVirtualBox &comVBox, const QString &strNetworkName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't find cloud network ..."),
+ QApplication::translate("UIMessageCenter", "Unable to find the cloud network <b>%1</b>.")
+ .arg(strNetworkName) +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotFindHostNetworkInterface(const CHost &comHost, const QString &strInterfaceName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't find host network interface ..."),
+ QApplication::translate("UIMessageCenter", "Unable to find the host network interface <b>%1</b>.")
+ .arg(strInterfaceName) +
+ UIErrorString::formatErrorInfo(comHost));
+}
+
+/* static */
+void UINotificationMessage::cannotFindHostOnlyNetwork(const CVirtualBox &comVBox, const QString &strNetworkName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't find host only network ..."),
+ QApplication::translate("UIMessageCenter", "Unable to find the host only network <b>%1</b>.")
+ .arg(strNetworkName) +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotFindNATNetwork(const CVirtualBox &comVBox, const QString &strNetworkName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't find NAT network ..."),
+ QApplication::translate("UIMessageCenter", "Unable to find the NAT network <b>%1</b>.")
+ .arg(strNetworkName) +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotCreateDHCPServer(const CVirtualBox &comVBox, const QString &strInterfaceName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create DHCP server ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create a DHCP server for the network interface <b>%1</b>.")
+ .arg(strInterfaceName) +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotRemoveDHCPServer(const CVirtualBox &comVBox, const QString &strInterfaceName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't remove DHCP server ..."),
+ QApplication::translate("UIMessageCenter", "Failed to remove the DHCP server for the network interface <b>%1</b>.")
+ .arg(strInterfaceName) +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotCreateCloudNetwork(const CVirtualBox &comVBox)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create cloud network ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create a cloud network.") +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotRemoveCloudNetwork(const CVirtualBox &comVBox, const QString &strNetworkName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't remove cloud network ..."),
+ QApplication::translate("UIMessageCenter", "Failed to remove the cloud network <b>%1</b>.")
+ .arg(strNetworkName) +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotCreateHostOnlyNetwork(const CVirtualBox &comVBox)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create host only network ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create a host only network.") +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotRemoveHostOnlyNetwork(const CVirtualBox &comVBox, const QString &strNetworkName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't remove host only network ..."),
+ QApplication::translate("UIMessageCenter", "Failed to remove the host only network <b>%1</b>.")
+ .arg(strNetworkName) +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotCreateNATNetwork(const CVirtualBox &comVBox)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create NAT network ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create a NAT network.") +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotRemoveNATNetwork(const CVirtualBox &comVBox, const QString &strNetworkName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't remove NAT network ..."),
+ QApplication::translate("UIMessageCenter", "Failed to remove the NAT network <b>%1</b>.")
+ .arg(strNetworkName) +
+ UIErrorString::formatErrorInfo(comVBox));
+}
+
+/* static */
+void UINotificationMessage::cannotCreateCloudProfile(const CCloudProvider &comProvider)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create cloud profile ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create cloud profile.") +
+ UIErrorString::formatErrorInfo(comProvider));
+}
+
+/* static */
+void UINotificationMessage::cannotRemoveCloudProfile(const CCloudProfile &comProfile)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't remove cloud profile ..."),
+ QApplication::translate("UIMessageCenter", "Failed to remove cloud profile.") +
+ UIErrorString::formatErrorInfo(comProfile));
+}
+
+/* static */
+void UINotificationMessage::cannotSaveCloudProfiles(const CCloudProvider &comProvider)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't save cloud profiles ..."),
+ QApplication::translate("UIMessageCenter", "Failed to save cloud profiles.") +
+ UIErrorString::formatErrorInfo(comProvider));
+}
+
+/* static */
+void UINotificationMessage::cannotImportCloudProfiles(const CCloudProvider &comProvider)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't import cloud profiles ..."),
+ QApplication::translate("UIMessageCenter", "Failed to import cloud profiles.") +
+ UIErrorString::formatErrorInfo(comProvider));
+}
+
+/* static */
+void UINotificationMessage::cannotRefreshCloudMachine(const CCloudMachine &comMachine)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't refresh cloud machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to refresh cloud machine.") +
+ UIErrorString::formatErrorInfo(comMachine));
+}
+
+/* static */
+void UINotificationMessage::cannotRefreshCloudMachine(const CProgress &comProgress)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't refresh cloud machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to refresh cloud machine.") +
+ UIErrorString::formatErrorInfo(comProgress));
+}
+
+/* static */
+void UINotificationMessage::cannotCreateCloudClient(const CCloudProfile &comProfile, UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't create cloud client ..."),
+ QApplication::translate("UIMessageCenter", "Failed to create cloud client.") +
+ UIErrorString::formatErrorInfo(comProfile),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotCloseMedium(const CMedium &comMedium)
+{
+ /* Show the error: */
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't close medium ..."),
+ QApplication::translate("UIMessageCenter", "Failed to close the disk image file <nobr><b>%1</b></nobr>.")
+ .arg(CMedium(comMedium).GetLocation()) +
+ UIErrorString::formatErrorInfo(comMedium));
+}
+
+/* static */
+void UINotificationMessage::cannotDiscardSavedState(const CMachine &comMachine)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't discard saved state ..."),
+ QApplication::translate("UIMessageCenter", "Failed to discard the saved state of the virtual machine <b>%1</b>.")
+ .arg(CMachine(comMachine).GetName()) +
+ UIErrorString::formatErrorInfo(comMachine));
+}
+
+/* static */
+void UINotificationMessage::cannotRemoveMachine(const CMachine &comMachine, UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't remove machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to remove the virtual machine <b>%1</b>.")
+ .arg(CMachine(comMachine).GetName()) +
+ UIErrorString::formatErrorInfo(comMachine),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotExportMachine(const CMachine &comMachine, UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't export machine ..."),
+ QApplication::translate("UIMessageCenter", "Failed to export virtual machine <b>%1</b>.")
+ .arg(CMachine(comMachine).GetName()) +
+ UIErrorString::formatErrorInfo(comMachine),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotAttachDevice(const CMachine &comMachine,
+ UIMediumDeviceType enmType,
+ const QString &strLocation,
+ const StorageSlot &storageSlot,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ QString strMessage;
+ switch (enmType)
+ {
+ case UIMediumDeviceType_HardDisk:
+ {
+ strMessage = QApplication::translate("UIMessageCenter", "Failed to attach the hard disk (<nobr><b>%1</b></nobr>) to "
+ "the slot <i>%2</i> of the machine <b>%3</b>.")
+ .arg(strLocation)
+ .arg(gpConverter->toString(storageSlot))
+ .arg(CMachine(comMachine).GetName());
+ break;
+ }
+ case UIMediumDeviceType_DVD:
+ {
+ strMessage = QApplication::translate("UIMessageCenter", "Failed to attach the optical drive (<nobr><b>%1</b></nobr>) "
+ "to the slot <i>%2</i> of the machine <b>%3</b>.")
+ .arg(strLocation)
+ .arg(gpConverter->toString(storageSlot))
+ .arg(CMachine(comMachine).GetName());
+ break;
+ }
+ case UIMediumDeviceType_Floppy:
+ {
+ strMessage = QApplication::translate("UIMessageCenter", "Failed to attach the floppy drive (<nobr><b>%1</b></nobr>) "
+ "to the slot <i>%2</i> of the machine <b>%3</b>.")
+ .arg(strLocation)
+ .arg(gpConverter->toString(storageSlot))
+ .arg(CMachine(comMachine).GetName());
+ break;
+ }
+ default:
+ break;
+ }
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't attach device ..."),
+ strMessage + UIErrorString::formatErrorInfo(comMachine),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotFindSnapshotById(const CMachine &comMachine, const QUuid &uId)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't find snapshot ..."),
+ QApplication::translate("UIMessageCenter", "Failed to find snapshot with ID=<b>%1</b>.")
+ .arg(uId.toString()) +
+ UIErrorString::formatErrorInfo(comMachine));
+}
+
+/* static */
+void UINotificationMessage::cannotFindSnapshotByName(const CMachine &comMachine,
+ const QString &strName,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't find snapshot ..."),
+ QApplication::translate("UIMessageCenter", "Failed to find snapshot with name=<b>%1</b>.")
+ .arg(strName) +
+ UIErrorString::formatErrorInfo(comMachine),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotChangeSnapshot(const CSnapshot &comSnapshot,
+ const QString &strSnapshotName,
+ const QString &strMachineName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't change snapshot ..."),
+ QApplication::translate("UIMessageCenter", "Failed to change the snapshot <b>%1</b> of the virtual machine <b>%2</b>.")
+ .arg(strSnapshotName, strMachineName) +
+ UIErrorString::formatErrorInfo(comSnapshot));
+}
+
+/* static */
+void UINotificationMessage::cannotRunUnattendedGuestInstall(const CUnattended &comUnattended)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't run guest install ..."),
+ QApplication::translate("UIMessageCenter", "Failed to run unattended guest installation.") +
+ UIErrorString::formatErrorInfo(comUnattended));
+}
+
+/* static */
+void UINotificationMessage::cannotAttachUSBDevice(const CConsole &comConsole, const QString &strDevice)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't attach USB device ..."),
+ QApplication::translate("UIMessageCenter", "Failed to attach the USB device <b>%1</b> to the virtual machine <b>%2</b>.")
+ .arg(strDevice, CConsole(comConsole).GetMachine().GetName()) +
+ UIErrorString::formatErrorInfo(comConsole));
+}
+
+/* static */
+void UINotificationMessage::cannotAttachUSBDevice(const CVirtualBoxErrorInfo &comErrorInfo,
+ const QString &strDevice, const QString &strMachineName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't attach USB device ..."),
+ QApplication::translate("UIMessageCenter", "Failed to attach the USB device <b>%1</b> to the virtual machine <b>%2</b>.")
+ .arg(strDevice, strMachineName) +
+ UIErrorString::formatErrorInfo(comErrorInfo));
+}
+
+/* static */
+void UINotificationMessage::cannotDetachUSBDevice(const CConsole &comConsole, const QString &strDevice)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't detach USB device ..."),
+ QApplication::translate("UIMessageCenter", "Failed to detach the USB device <b>%1</b> from the virtual machine <b>%2</b>.")
+ .arg(strDevice, CConsole(comConsole).GetMachine().GetName()) +
+ UIErrorString::formatErrorInfo(comConsole));
+}
+
+/* static */
+void UINotificationMessage::cannotDetachUSBDevice(const CVirtualBoxErrorInfo &comErrorInfo,
+ const QString &strDevice, const QString &strMachineName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't detach USB device ..."),
+ QApplication::translate("UIMessageCenter", "Failed to detach the USB device <b>%1</b> from the virtual machine <b>%2</b>.")
+ .arg(strDevice, strMachineName) +
+ UIErrorString::formatErrorInfo(comErrorInfo));
+}
+
+/* static */
+void UINotificationMessage::cannotAttachWebCam(const CEmulatedUSB &comDispatcher,
+ const QString &strWebCamName, const QString &strMachineName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't attach webcam ..."),
+ QApplication::translate("UIMessageCenter", "Failed to attach the webcam <b>%1</b> to the virtual machine <b>%2</b>.")
+ .arg(strWebCamName, strMachineName) +
+ UIErrorString::formatErrorInfo(comDispatcher));
+}
+
+/* static */
+void UINotificationMessage::cannotDetachWebCam(const CEmulatedUSB &comDispatcher,
+ const QString &strWebCamName, const QString &strMachineName)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't detach webcam ..."),
+ QApplication::translate("UIMessageCenter", "Failed to detach the webcam <b>%1</b> from the virtual machine <b>%2</b>.")
+ .arg(strWebCamName, strMachineName) +
+ UIErrorString::formatErrorInfo(comDispatcher));
+}
+
+/* static */
+void UINotificationMessage::cannotSaveMachineSettings(const CMachine &comMachine, UINotificationCenter *pParent /* = 0 */)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't save machine settings ..."),
+ QApplication::translate("UIMessageCenter", "Failed to save the settings of the virtual machine <b>%1</b> to "
+ "<b><nobr>%2</nobr></b>.")
+ .arg(CMachine(comMachine).GetName(),
+ CMachine(comMachine).GetSettingsFilePath()) +
+ UIErrorString::formatErrorInfo(comMachine),
+ QString(), QString(), pParent);
+}
+
+/* static */
+void UINotificationMessage::cannotToggleAudioInput(const CAudioAdapter &comAdapter,
+ const QString &strMachineName, bool fEnable)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't toggle audio input ..."),
+ ( fEnable
+ ? QApplication::translate("UIMessageCenter", "Failed to enable the audio adapter input for the virtual machine <b>%1</b>.")
+ .arg(strMachineName)
+ : QApplication::translate("UIMessageCenter", "Failed to disable the audio adapter input for the virtual machine <b>%1</b>.")
+ .arg(strMachineName)) +
+ UIErrorString::formatErrorInfo(comAdapter));
+}
+
+/* static */
+void UINotificationMessage::cannotToggleAudioOutput(const CAudioAdapter &comAdapter,
+ const QString &strMachineName, bool fEnable)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't toggle audio output ..."),
+ ( fEnable
+ ? QApplication::translate("UIMessageCenter", "Failed to enable the audio adapter output for the virtual machine <b>%1</b>.")
+ .arg(strMachineName)
+ : QApplication::translate("UIMessageCenter", "Failed to disable the audio adapter output for the virtual machine <b>%1</b>.")
+ .arg(strMachineName)) +
+ UIErrorString::formatErrorInfo(comAdapter));
+}
+
+/* static */
+void UINotificationMessage::cannotToggleNetworkCable(const CNetworkAdapter &comAdapter,
+ const QString &strMachineName, bool fConnect)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't toggle network cable ..."),
+ ( fConnect
+ ? QApplication::translate("UIMessageCenter", "Failed to connect the network adapter cable of the virtual machine <b>%1</b>.")
+ .arg(strMachineName)
+ : QApplication::translate("UIMessageCenter", "Failed to disconnect the network adapter cable of the virtual machine <b>%1</b>.")
+ .arg(strMachineName)) +
+ UIErrorString::formatErrorInfo(comAdapter));
+}
+
+/* static */
+void UINotificationMessage::cannotToggleRecording(const CRecordingSettings &comRecording, const QString &strMachineName, bool fEnable)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't toggle recording ..."),
+ ( fEnable
+ ? QApplication::translate("UIMessageCenter", "Failed to enable recording for the virtual machine <b>%1</b>.")
+ .arg(strMachineName)
+ : QApplication::translate("UIMessageCenter", "Failed to disable recording for the virtual machine <b>%1</b>.")
+ .arg(strMachineName)) +
+ UIErrorString::formatErrorInfo(comRecording));
+}
+
+/* static */
+void UINotificationMessage::cannotToggleVRDEServer(const CVRDEServer &comServer,
+ const QString &strMachineName, bool fEnable)
+{
+ createMessage(
+ QApplication::translate("UIMessageCenter", "Can't toggle VRDE server ..."),
+ ( fEnable
+ ? QApplication::translate("UIMessageCenter", "Failed to enable the remote desktop server for the virtual machine <b>%1</b>.")
+ .arg(strMachineName)
+ : QApplication::translate("UIMessageCenter", "Failed to disable the remote desktop server for the virtual machine <b>%1</b>.")
+ .arg(strMachineName)) +
+ UIErrorString::formatErrorInfo(comServer));
+}
+
+UINotificationMessage::UINotificationMessage(const QString &strName,
+ const QString &strDetails,
+ const QString &strInternalName,
+ const QString &strHelpKeyword)
+ : UINotificationSimple(strName,
+ strDetails,
+ strInternalName,
+ strHelpKeyword)
+{
+}
+
+UINotificationMessage::~UINotificationMessage()
+{
+ /* Remove message from known: */
+ m_messages.remove(m_strInternalName);
+}
+
+/* static */
+void UINotificationMessage::createMessage(const QString &strName,
+ const QString &strDetails,
+ const QString &strInternalName /* = QString() */,
+ const QString &strHelpKeyword /* = QString() */,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ /* Check if message suppressed: */
+ if (isSuppressed(strInternalName))
+ return;
+ /* Check if message already exists: */
+ if ( !strInternalName.isEmpty()
+ && m_messages.contains(strInternalName))
+ return;
+
+ /* Choose effective parent: */
+ UINotificationCenter *pEffectiveParent = pParent ? pParent : gpNotificationCenter;
+
+ /* Create message finally: */
+ const QUuid uId = pEffectiveParent->append(new UINotificationMessage(strName,
+ strDetails,
+ strInternalName,
+ strHelpKeyword));
+ if (!strInternalName.isEmpty())
+ m_messages[strInternalName] = uId;
+}
+
+/* static */
+void UINotificationMessage::destroyMessage(const QString &strInternalName,
+ UINotificationCenter *pParent /* = 0 */)
+{
+ /* Check if message really exists: */
+ if (!m_messages.contains(strInternalName))
+ return;
+
+ /* Choose effective parent: */
+ UINotificationCenter *pEffectiveParent = pParent ? pParent : gpNotificationCenter;
+
+ /* Destroy message finally: */
+ pEffectiveParent->revoke(m_messages.value(strInternalName));
+ m_messages.remove(strInternalName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMediumCreate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMediumCreate::UINotificationProgressMediumCreate(const CMedium &comTarget,
+ qulonglong uSize,
+ const QVector<KMediumVariant> &variants)
+ : m_comTarget(comTarget)
+ , m_uSize(uSize)
+ , m_variants(variants)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressMediumCreate::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressMediumCreate::name() const
+{
+ return UINotificationProgress::tr("Creating medium ...");
+}
+
+QString UINotificationProgressMediumCreate::details() const
+{
+ return UINotificationProgress::tr("<b>Location:</b> %1<br><b>Size:</b> %2").arg(m_strLocation, UITranslator::formatSize(m_uSize));
+}
+
+CProgress UINotificationProgressMediumCreate::createProgress(COMResult &comResult)
+{
+ /* Acquire location: */
+ m_strLocation = m_comTarget.GetLocation();
+ if (!m_comTarget.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comTarget;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comTarget.CreateBaseStorage(m_uSize, m_variants);
+ /* Store COM result: */
+ comResult = m_comTarget;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressMediumCreate::sltHandleProgressFinished()
+{
+ if (m_comTarget.isNotNull() && !m_comTarget.GetId().isNull())
+ emit sigMediumCreated(m_comTarget);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMediumCopy implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMediumCopy::UINotificationProgressMediumCopy(const CMedium &comSource,
+ const CMedium &comTarget,
+ const QVector<KMediumVariant> &variants)
+ : m_comSource(comSource)
+ , m_comTarget(comTarget)
+ , m_variants(variants)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressMediumCopy::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressMediumCopy::name() const
+{
+ return UINotificationProgress::tr("Copying medium ...");
+}
+
+QString UINotificationProgressMediumCopy::details() const
+{
+ return UINotificationProgress::tr("<b>From:</b> %1<br><b>To:</b> %2").arg(m_strSourceLocation, m_strTargetLocation);
+}
+
+CProgress UINotificationProgressMediumCopy::createProgress(COMResult &comResult)
+{
+ /* Acquire locations: */
+ m_strSourceLocation = m_comSource.GetLocation();
+ if (!m_comSource.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comSource;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+ m_strTargetLocation = m_comTarget.GetLocation();
+ if (!m_comTarget.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comTarget;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comSource.CloneTo(m_comTarget, m_variants, CMedium());
+ /* Store COM result: */
+ comResult = m_comSource;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressMediumCopy::sltHandleProgressFinished()
+{
+ if (m_comTarget.isNotNull() && !m_comTarget.GetId().isNull())
+ emit sigMediumCopied(m_comTarget);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMediumMove implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMediumMove::UINotificationProgressMediumMove(const CMedium &comMedium,
+ const QString &strLocation)
+ : m_comMedium(comMedium)
+ , m_strTo(strLocation)
+{
+}
+
+QString UINotificationProgressMediumMove::name() const
+{
+ return UINotificationProgress::tr("Moving medium ...");
+}
+
+QString UINotificationProgressMediumMove::details() const
+{
+ return UINotificationProgress::tr("<b>From:</b> %1<br><b>To:</b> %2").arg(m_strFrom, m_strTo);
+}
+
+CProgress UINotificationProgressMediumMove::createProgress(COMResult &comResult)
+{
+ /* Acquire location: */
+ m_strFrom = m_comMedium.GetLocation();
+ if (!m_comMedium.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comMedium;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMedium.MoveTo(m_strTo);
+ /* Store COM result: */
+ comResult = m_comMedium;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMediumResize implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMediumResize::UINotificationProgressMediumResize(const CMedium &comMedium,
+ qulonglong uSize)
+ : m_comMedium(comMedium)
+ , m_uTo(uSize)
+{
+}
+
+QString UINotificationProgressMediumResize::name() const
+{
+ return UINotificationProgress::tr("Resizing medium ...");
+}
+
+QString UINotificationProgressMediumResize::details() const
+{
+ return UINotificationProgress::tr("<b>From:</b> %1<br><b>To:</b> %2")
+ .arg(UITranslator::formatSize(m_uFrom),
+ UITranslator::formatSize(m_uTo));
+}
+
+CProgress UINotificationProgressMediumResize::createProgress(COMResult &comResult)
+{
+ /* Acquire size: */
+ m_uFrom = m_comMedium.GetLogicalSize();
+ if (!m_comMedium.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comMedium;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMedium.Resize(m_uTo);
+ /* Store COM result: */
+ comResult = m_comMedium;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMediumDeletingStorage implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMediumDeletingStorage::UINotificationProgressMediumDeletingStorage(const CMedium &comMedium)
+ : m_comMedium(comMedium)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressMediumDeletingStorage::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressMediumDeletingStorage::name() const
+{
+ return UINotificationProgress::tr("Deleting medium storage ...");
+}
+
+QString UINotificationProgressMediumDeletingStorage::details() const
+{
+ return UINotificationProgress::tr("<b>Location:</b> %1").arg(m_strLocation);
+}
+
+CProgress UINotificationProgressMediumDeletingStorage::createProgress(COMResult &comResult)
+{
+ /* Acquire location: */
+ m_strLocation = m_comMedium.GetLocation();
+ if (!m_comMedium.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comMedium;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMedium.DeleteStorage();
+ /* Store COM result: */
+ comResult = m_comMedium;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressMediumDeletingStorage::sltHandleProgressFinished()
+{
+ if (!error().isEmpty())
+ emit sigMediumStorageDeleted(m_comMedium);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMachineCopy implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMachineCopy::UINotificationProgressMachineCopy(const CMachine &comSource,
+ const CMachine &comTarget,
+ const KCloneMode &enmCloneMode,
+ const QVector<KCloneOptions> &options)
+ : m_comSource(comSource)
+ , m_comTarget(comTarget)
+ , m_enmCloneMode(enmCloneMode)
+ , m_options(options)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressMachineCopy::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressMachineCopy::name() const
+{
+ return UINotificationProgress::tr("Copying machine ...");
+}
+
+QString UINotificationProgressMachineCopy::details() const
+{
+ return UINotificationProgress::tr("<b>From:</b> %1<br><b>To:</b> %2").arg(m_strSourceName, m_strTargetName);
+}
+
+CProgress UINotificationProgressMachineCopy::createProgress(COMResult &comResult)
+{
+ /* Acquire names: */
+ m_strSourceName = m_comSource.GetName();
+ if (!m_comSource.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comSource;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+ m_strTargetName = m_comTarget.GetName();
+ if (!m_comTarget.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comTarget;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comSource.CloneTo(m_comTarget, m_enmCloneMode, m_options);
+ /* Store COM result: */
+ comResult = m_comSource;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressMachineCopy::sltHandleProgressFinished()
+{
+ if (m_comTarget.isNotNull() && !m_comTarget.GetId().isNull())
+ emit sigMachineCopied(m_comTarget);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMachinePowerUp implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMachinePowerUp::UINotificationProgressMachinePowerUp(const CMachine &comMachine, UILaunchMode enmLaunchMode)
+ : m_comMachine(comMachine)
+ , m_enmLaunchMode(enmLaunchMode)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressMachinePowerUp::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressMachinePowerUp::name() const
+{
+ return UINotificationProgress::tr("Powering VM up ...");
+}
+
+QString UINotificationProgressMachinePowerUp::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressMachinePowerUp::createProgress(COMResult &comResult)
+{
+ /* Acquire VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Open a session thru which we will modify the machine: */
+ m_comSession.createInstance(CLSID_Session);
+ if (m_comSession.isNull())
+ {
+ comResult = m_comSession;
+ return CProgress();
+ }
+
+ /* Configure environment: */
+ QVector<QString> astrEnv;
+#ifdef VBOX_WS_WIN
+ /* Allow started VM process to be foreground window: */
+ AllowSetForegroundWindow(ASFW_ANY);
+#endif
+#ifdef VBOX_WS_X11
+ /* Make sure VM process will start on the same
+ * display as the VirtualBox Manager: */
+ const char *pDisplay = RTEnvGet("DISPLAY");
+ if (pDisplay)
+ astrEnv.append(QString("DISPLAY=%1").arg(pDisplay));
+ const char *pXauth = RTEnvGet("XAUTHORITY");
+ if (pXauth)
+ astrEnv.append(QString("XAUTHORITY=%1").arg(pXauth));
+#endif
+ QString strType;
+ switch (m_enmLaunchMode)
+ {
+ case UILaunchMode_Default: strType = ""; break;
+ case UILaunchMode_Separate: strType = "separate"; break;
+ case UILaunchMode_Headless: strType = "headless"; break;
+ default: AssertFailedReturn(CProgress());
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.LaunchVMProcess(m_comSession, strType, astrEnv);
+// /* If the VM is started separately and the VM process is already running, then it is OK. */
+// if (m_enmLaunchMode == UILaunchMode_Separate)
+// {
+// const KMachineState enmState = comMachine.GetState();
+// if ( enmState >= KMachineState_FirstOnline
+// && enmState <= KMachineState_LastOnline)
+// {
+// /* Already running: */
+// return;
+// }
+// }
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressMachinePowerUp::sltHandleProgressFinished()
+{
+ /* Unlock session finally: */
+ m_comSession.UnlockMachine();
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMachineMove implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMachineMove::UINotificationProgressMachineMove(const QUuid &uId,
+ const QString &strDestination,
+ const QString &strType)
+ : m_uId(uId)
+ , m_strDestination(QDir::toNativeSeparators(strDestination))
+ , m_strType(strType)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressMachineMove::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressMachineMove::name() const
+{
+ return UINotificationProgress::tr("Moving machine ...");
+}
+
+QString UINotificationProgressMachineMove::details() const
+{
+ return UINotificationProgress::tr("<b>From:</b> %1<br><b>To:</b> %2").arg(m_strSource, m_strDestination);
+}
+
+CProgress UINotificationProgressMachineMove::createProgress(COMResult &comResult)
+{
+ /* Open a session thru which we will modify the machine: */
+ m_comSession = uiCommon().openSession(m_uId, KLockType_Write);
+ if (m_comSession.isNull())
+ return CProgress();
+
+ /* Get session machine: */
+ CMachine comMachine = m_comSession.GetMachine();
+ if (!m_comSession.isOk())
+ {
+ comResult = m_comSession;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ /* Acquire VM source: */
+ const QString strSettingFilePath = comMachine.GetSettingsFilePath();
+ if (!comMachine.isOk())
+ {
+ comResult = comMachine;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+ QDir parentDir = QFileInfo(strSettingFilePath).absoluteDir();
+ parentDir.cdUp();
+ m_strSource = QDir::toNativeSeparators(parentDir.absolutePath());
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = comMachine.MoveTo(m_strDestination, m_strType);
+ /* Store COM result: */
+ comResult = comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressMachineMove::sltHandleProgressFinished()
+{
+ /* Unlock session finally: */
+ m_comSession.UnlockMachine();
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMachineSaveState implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMachineSaveState::UINotificationProgressMachineSaveState(const CMachine &comMachine)
+ : m_comMachine(comMachine)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressMachineSaveState::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressMachineSaveState::name() const
+{
+ return UINotificationProgress::tr("Saving VM state ...");
+}
+
+QString UINotificationProgressMachineSaveState::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressMachineSaveState::createProgress(COMResult &comResult)
+{
+ /* Acquire VM id: */
+ const QUuid uId = m_comMachine.GetId();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Acquire VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Prepare machine to save: */
+ CMachine comMachine = m_comMachine;
+
+ /* For Manager UI: */
+ switch (uiCommon().uiType())
+ {
+ case UICommon::UIType_SelectorUI:
+ {
+ /* Open a session thru which we will modify the machine: */
+ m_comSession = uiCommon().openExistingSession(uId);
+ if (m_comSession.isNull())
+ return CProgress();
+
+ /* Get session machine: */
+ comMachine = m_comSession.GetMachine();
+ if (!m_comSession.isOk())
+ {
+ comResult = m_comSession;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ /* Get machine state: */
+ const KMachineState enmState = comMachine.GetState();
+ if (!comMachine.isOk())
+ {
+ comResult = comMachine;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ /* If VM isn't yet paused: */
+ if (enmState != KMachineState_Paused)
+ {
+ /* Get session console: */
+ CConsole comConsole = m_comSession.GetConsole();
+ if (!m_comSession.isOk())
+ {
+ comResult = m_comSession;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ /* Pause VM first: */
+ comConsole.Pause();
+ if (!comConsole.isOk())
+ {
+ comResult = comConsole;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = comMachine.SaveState();
+ /* Store COM result: */
+ comResult = comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressMachineSaveState::sltHandleProgressFinished()
+{
+ /* Unlock session finally: */
+ if (m_comSession.isNotNull())
+ m_comSession.UnlockMachine();
+
+ /* Notifies listeners: */
+ emit sigMachineStateSaved(error().isEmpty());
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMachinePowerOff implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMachinePowerOff::UINotificationProgressMachinePowerOff(const CMachine &comMachine,
+ const CConsole &comConsole /* = CConsole() */,
+ bool fIncludingDiscard /* = false */)
+ : m_comMachine(comMachine)
+ , m_comConsole(comConsole)
+ , m_fIncludingDiscard(fIncludingDiscard)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressMachinePowerOff::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressMachinePowerOff::name() const
+{
+ return UINotificationProgress::tr("Powering VM off ...");
+}
+
+QString UINotificationProgressMachinePowerOff::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressMachinePowerOff::createProgress(COMResult &comResult)
+{
+ /* Prepare machine to power off: */
+ CMachine comMachine = m_comMachine;
+ /* Prepare console to power off: */
+ CConsole comConsole = m_comConsole;
+
+ /* For Manager UI: */
+ switch (uiCommon().uiType())
+ {
+ case UICommon::UIType_SelectorUI:
+ {
+ /* Acquire VM id: */
+ const QUuid uId = comMachine.GetId();
+ if (!comMachine.isOk())
+ {
+ comResult = comMachine;
+ return CProgress();
+ }
+
+ /* Open a session thru which we will modify the machine: */
+ m_comSession = uiCommon().openExistingSession(uId);
+ if (m_comSession.isNull())
+ return CProgress();
+
+ /* Get session machine: */
+ comMachine = m_comSession.GetMachine();
+ if (!m_comSession.isOk())
+ {
+ comResult = m_comSession;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ /* Get session console: */
+ comConsole = m_comSession.GetConsole();
+ if (!m_comSession.isOk())
+ {
+ comResult = m_comSession;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = comConsole.PowerDown();
+
+ /* For Runtime UI: */
+ switch (uiCommon().uiType())
+ {
+ case UICommon::UIType_RuntimeUI:
+ {
+ /* Check the console state, it might be already gone: */
+ if (!comConsole.isNull())
+ {
+ /* This can happen if VBoxSVC is not running: */
+ COMResult res(comConsole);
+ if (FAILED_DEAD_INTERFACE(res.rc()))
+ return CProgress();
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Store COM result: */
+ comResult = comConsole;
+
+ /* Acquire VM name, no error checks, too late: */
+ m_strName = comMachine.GetName();
+
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressMachinePowerOff::sltHandleProgressFinished()
+{
+ /* Unlock session finally: */
+ if (m_comSession.isNotNull())
+ m_comSession.UnlockMachine();
+
+ /* Notifies listeners: */
+ emit sigMachinePoweredOff(error().isEmpty(), m_fIncludingDiscard);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressMachineMediaRemove implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressMachineMediaRemove::UINotificationProgressMachineMediaRemove(const CMachine &comMachine,
+ const CMediumVector &media)
+ : m_comMachine(comMachine)
+ , m_media(media)
+{
+}
+
+QString UINotificationProgressMachineMediaRemove::name() const
+{
+ return UINotificationProgress::tr("Removing machine media ...");
+}
+
+QString UINotificationProgressMachineMediaRemove::details() const
+{
+ return UINotificationProgress::tr("<b>Machine Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressMachineMediaRemove::createProgress(COMResult &comResult)
+{
+ /* Acquire names: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.DeleteConfig(m_media);
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressVFSExplorerUpdate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressVFSExplorerUpdate::UINotificationProgressVFSExplorerUpdate(const CVFSExplorer &comExplorer)
+ : m_comExplorer(comExplorer)
+{
+}
+
+QString UINotificationProgressVFSExplorerUpdate::name() const
+{
+ return UINotificationProgress::tr("Updating VFS explorer ...");
+}
+
+QString UINotificationProgressVFSExplorerUpdate::details() const
+{
+ return UINotificationProgress::tr("<b>Path:</b> %1").arg(m_strPath);
+}
+
+CProgress UINotificationProgressVFSExplorerUpdate::createProgress(COMResult &comResult)
+{
+ /* Acquire path: */
+ m_strPath = m_comExplorer.GetPath();
+ if (!m_comExplorer.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comExplorer;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comExplorer.Update();
+ /* Store COM result: */
+ comResult = m_comExplorer;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressVFSExplorerFilesRemove implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressVFSExplorerFilesRemove::UINotificationProgressVFSExplorerFilesRemove(const CVFSExplorer &comExplorer,
+ const QVector<QString> &files)
+ : m_comExplorer(comExplorer)
+ , m_files(files)
+{
+}
+
+QString UINotificationProgressVFSExplorerFilesRemove::name() const
+{
+ return UINotificationProgress::tr("Removing VFS explorer files ...");
+}
+
+QString UINotificationProgressVFSExplorerFilesRemove::details() const
+{
+ return UINotificationProgress::tr("<b>Path:</b> %1<br><b>Files:</b> %2")
+ .arg(m_strPath)
+ .arg(QStringList(m_files.toList()).join(", "));
+}
+
+CProgress UINotificationProgressVFSExplorerFilesRemove::createProgress(COMResult &comResult)
+{
+ /* Acquire path: */
+ m_strPath = m_comExplorer.GetPath();
+ if (!m_comExplorer.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comExplorer;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comExplorer.Remove(m_files);
+ /* Store COM result: */
+ comResult = m_comExplorer;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressSubnetSelectionVSDFormCreate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressSubnetSelectionVSDFormCreate::UINotificationProgressSubnetSelectionVSDFormCreate(const CCloudClient &comClient,
+ const CVirtualSystemDescription &comVSD,
+ const QString &strProviderShortName,
+ const QString &strProfileName)
+ : m_comClient(comClient)
+ , m_comVSD(comVSD)
+ , m_strProviderShortName(strProviderShortName)
+ , m_strProfileName(strProfileName)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressSubnetSelectionVSDFormCreate::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressSubnetSelectionVSDFormCreate::name() const
+{
+ return UINotificationProgress::tr("Creating subnet selection VSD form ...");
+}
+
+QString UINotificationProgressSubnetSelectionVSDFormCreate::details() const
+{
+ return UINotificationProgress::tr("<b>Provider:</b> %1<br><b>Profile:</b> %2")
+ .arg(m_strProviderShortName, m_strProfileName);
+}
+
+CProgress UINotificationProgressSubnetSelectionVSDFormCreate::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.GetSubnetSelectionForm(m_comVSD, m_comVSDForm);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressSubnetSelectionVSDFormCreate::sltHandleProgressFinished()
+{
+ if (m_comVSDForm.isNotNull())
+ emit sigVSDFormCreated(m_comVSDForm);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressLaunchVSDFormCreate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressLaunchVSDFormCreate::UINotificationProgressLaunchVSDFormCreate(const CCloudClient &comClient,
+ const CVirtualSystemDescription &comVSD,
+ const QString &strProviderShortName,
+ const QString &strProfileName)
+ : m_comClient(comClient)
+ , m_comVSD(comVSD)
+ , m_strProviderShortName(strProviderShortName)
+ , m_strProfileName(strProfileName)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressLaunchVSDFormCreate::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressLaunchVSDFormCreate::name() const
+{
+ return UINotificationProgress::tr("Creating launch VSD form ...");
+}
+
+QString UINotificationProgressLaunchVSDFormCreate::details() const
+{
+ return UINotificationProgress::tr("<b>Provider:</b> %1<br><b>Profile:</b> %2")
+ .arg(m_strProviderShortName, m_strProfileName);
+}
+
+CProgress UINotificationProgressLaunchVSDFormCreate::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.GetLaunchDescriptionForm(m_comVSD, m_comVSDForm);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressLaunchVSDFormCreate::sltHandleProgressFinished()
+{
+ if (m_comVSDForm.isNotNull())
+ emit sigVSDFormCreated(m_comVSDForm);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressExportVSDFormCreate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressExportVSDFormCreate::UINotificationProgressExportVSDFormCreate(const CCloudClient &comClient,
+ const CVirtualSystemDescription &comVSD)
+ : m_comClient(comClient)
+ , m_comVSD(comVSD)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressExportVSDFormCreate::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressExportVSDFormCreate::name() const
+{
+ return UINotificationProgress::tr("Creating export VSD form ...");
+}
+
+QString UINotificationProgressExportVSDFormCreate::details() const
+{
+ return QString();
+}
+
+CProgress UINotificationProgressExportVSDFormCreate::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.GetExportDescriptionForm(m_comVSD, m_comVSDForm);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressExportVSDFormCreate::sltHandleProgressFinished()
+{
+ if (m_comVSDForm.isNotNull())
+ emit sigVSDFormCreated(QVariant::fromValue(m_comVSDForm));
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressImportVSDFormCreate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressImportVSDFormCreate::UINotificationProgressImportVSDFormCreate(const CCloudClient &comClient,
+ const CVirtualSystemDescription &comVSD)
+ : m_comClient(comClient)
+ , m_comVSD(comVSD)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressImportVSDFormCreate::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressImportVSDFormCreate::name() const
+{
+ return UINotificationProgress::tr("Creating import VSD form ...");
+}
+
+QString UINotificationProgressImportVSDFormCreate::details() const
+{
+ return QString();
+}
+
+CProgress UINotificationProgressImportVSDFormCreate::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.GetImportDescriptionForm(m_comVSD, m_comVSDForm);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressImportVSDFormCreate::sltHandleProgressFinished()
+{
+ if (m_comVSDForm.isNotNull())
+ emit sigVSDFormCreated(QVariant::fromValue(m_comVSDForm));
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudImageList implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudImageList::UINotificationProgressCloudImageList(const CCloudClient &comClient,
+ const QVector<KCloudImageState> &cloudImageStates)
+ : m_comClient(comClient)
+ , m_cloudImageStates(cloudImageStates)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressCloudImageList::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressCloudImageList::name() const
+{
+ return UINotificationProgress::tr("Listing cloud images ...");
+}
+
+QString UINotificationProgressCloudImageList::details() const
+{
+ return QString();
+}
+
+CProgress UINotificationProgressCloudImageList::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.ListImages(m_cloudImageStates, m_comNames, m_comIds);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressCloudImageList::sltHandleProgressFinished()
+{
+ if (m_comNames.isNotNull() && m_comIds.isNotNull())
+ {
+ emit sigImageNamesReceived(QVariant::fromValue(m_comNames));
+ emit sigImageIdsReceived(QVariant::fromValue(m_comIds));
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudSourceBootVolumeList implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudSourceBootVolumeList::UINotificationProgressCloudSourceBootVolumeList(const CCloudClient &comClient)
+ : m_comClient(comClient)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressCloudSourceBootVolumeList::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressCloudSourceBootVolumeList::name() const
+{
+ return UINotificationProgress::tr("Listing cloud source boot volumes ...");
+}
+
+QString UINotificationProgressCloudSourceBootVolumeList::details() const
+{
+ return QString();
+}
+
+CProgress UINotificationProgressCloudSourceBootVolumeList::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.ListSourceBootVolumes(m_comNames, m_comIds);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressCloudSourceBootVolumeList::sltHandleProgressFinished()
+{
+ if (m_comNames.isNotNull() && m_comIds.isNotNull())
+ {
+ emit sigImageNamesReceived(QVariant::fromValue(m_comNames));
+ emit sigImageIdsReceived(QVariant::fromValue(m_comIds));
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudInstanceList implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudInstanceList::UINotificationProgressCloudInstanceList(const CCloudClient &comClient)
+ : m_comClient(comClient)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressCloudInstanceList::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressCloudInstanceList::name() const
+{
+ return UINotificationProgress::tr("Listing cloud instances ...");
+}
+
+QString UINotificationProgressCloudInstanceList::details() const
+{
+ return QString();
+}
+
+CProgress UINotificationProgressCloudInstanceList::createProgress(COMResult &comResult)
+{
+ /* Currently we are interested in Running and Stopped VMs only: */
+ const QVector<KCloudMachineState> cloudMachineStates = QVector<KCloudMachineState>()
+ << KCloudMachineState_Running
+ << KCloudMachineState_Stopped;
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.ListInstances(cloudMachineStates, m_comNames, m_comIds);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressCloudInstanceList::sltHandleProgressFinished()
+{
+ if (m_comNames.isNotNull() && m_comIds.isNotNull())
+ {
+ emit sigImageNamesReceived(QVariant::fromValue(m_comNames));
+ emit sigImageIdsReceived(QVariant::fromValue(m_comIds));
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudSourceInstanceList implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudSourceInstanceList::UINotificationProgressCloudSourceInstanceList(const CCloudClient &comClient)
+ : m_comClient(comClient)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressCloudSourceInstanceList::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressCloudSourceInstanceList::name() const
+{
+ return UINotificationProgress::tr("Listing cloud source instances ...");
+}
+
+QString UINotificationProgressCloudSourceInstanceList::details() const
+{
+ return QString();
+}
+
+CProgress UINotificationProgressCloudSourceInstanceList::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.ListSourceInstances(m_comNames, m_comIds);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressCloudSourceInstanceList::sltHandleProgressFinished()
+{
+ if (m_comNames.isNotNull() && m_comIds.isNotNull())
+ {
+ emit sigImageNamesReceived(QVariant::fromValue(m_comNames));
+ emit sigImageIdsReceived(QVariant::fromValue(m_comIds));
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudMachineAdd implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudMachineAdd::UINotificationProgressCloudMachineAdd(const CCloudClient &comClient,
+ const CCloudMachine &comMachine,
+ const QString &strInstanceName,
+ const QString &strProviderShortName,
+ const QString &strProfileName)
+ : m_comClient(comClient)
+ , m_comMachine(comMachine)
+ , m_strInstanceName(strInstanceName)
+ , m_strProviderShortName(strProviderShortName)
+ , m_strProfileName(strProfileName)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressCloudMachineAdd::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressCloudMachineAdd::name() const
+{
+ return UINotificationProgress::tr("Adding cloud VM ...");
+}
+
+QString UINotificationProgressCloudMachineAdd::details() const
+{
+ return UINotificationProgress::tr("<b>Provider:</b> %1<br><b>Profile:</b> %2<br><b>Instance Name:</b> %3")
+ .arg(m_strProviderShortName, m_strProfileName, m_strInstanceName);
+}
+
+CProgress UINotificationProgressCloudMachineAdd::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.AddCloudMachine(m_strInstanceName, m_comMachine);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressCloudMachineAdd::sltHandleProgressFinished()
+{
+ if (m_comMachine.isNotNull() && !m_comMachine.GetId().isNull())
+ emit sigCloudMachineAdded(m_strProviderShortName, m_strProfileName, m_comMachine);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudMachineCreate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudMachineCreate::UINotificationProgressCloudMachineCreate(const CCloudClient &comClient,
+ const CCloudMachine &comMachine,
+ const CVirtualSystemDescription &comVSD,
+ const QString &strProviderShortName,
+ const QString &strProfileName)
+ : m_comClient(comClient)
+ , m_comMachine(comMachine)
+ , m_comVSD(comVSD)
+ , m_strProviderShortName(strProviderShortName)
+ , m_strProfileName(strProfileName)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressCloudMachineCreate::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressCloudMachineCreate::name() const
+{
+ return UINotificationProgress::tr("Creating cloud VM ...");
+}
+
+QString UINotificationProgressCloudMachineCreate::details() const
+{
+ return UINotificationProgress::tr("<b>Provider:</b> %1<br><b>Profile:</b> %2<br><b>VM Name:</b> %3")
+ .arg(m_strProviderShortName, m_strProfileName, m_strName);
+}
+
+CProgress UINotificationProgressCloudMachineCreate::createProgress(COMResult &comResult)
+{
+ /* Parse cloud VM name: */
+ QVector<KVirtualSystemDescriptionType> types;
+ QVector<QString> refs, origValues, configValues, extraConfigValues;
+ m_comVSD.GetDescriptionByType(KVirtualSystemDescriptionType_Name, types,
+ refs, origValues, configValues, extraConfigValues);
+ if (!origValues.isEmpty())
+ m_strName = origValues.first();
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comClient.CreateCloudMachine(m_comVSD, m_comMachine);
+ /* Store COM result: */
+ comResult = m_comClient;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressCloudMachineCreate::sltHandleProgressFinished()
+{
+ if (m_comMachine.isNotNull() && !m_comMachine.GetId().isNull())
+ emit sigCloudMachineCreated(m_strProviderShortName, m_strProfileName, m_comMachine);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudMachineRemove implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudMachineRemove::UINotificationProgressCloudMachineRemove(const CCloudMachine &comMachine,
+ bool fFullRemoval,
+ const QString &strProviderShortName,
+ const QString &strProfileName)
+ : m_comMachine(comMachine)
+ , m_fFullRemoval(fFullRemoval)
+ , m_strProviderShortName(strProviderShortName)
+ , m_strProfileName(strProfileName)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressCloudMachineRemove::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressCloudMachineRemove::name() const
+{
+ return m_fFullRemoval
+ ? UINotificationProgress::tr("Deleting cloud VM files ...")
+ : UINotificationProgress::tr("Removing cloud VM ...");
+}
+
+QString UINotificationProgressCloudMachineRemove::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressCloudMachineRemove::createProgress(COMResult &comResult)
+{
+ /* Acquire cloud VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_fFullRemoval
+ ? m_comMachine.Remove()
+ : m_comMachine.Unregister();
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressCloudMachineRemove::sltHandleProgressFinished()
+{
+ if (error().isEmpty())
+ emit sigCloudMachineRemoved(m_strProviderShortName, m_strProfileName, m_strName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudMachinePowerUp implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudMachinePowerUp::UINotificationProgressCloudMachinePowerUp(const CCloudMachine &comMachine)
+ : m_comMachine(comMachine)
+{
+}
+
+QString UINotificationProgressCloudMachinePowerUp::name() const
+{
+ return UINotificationProgress::tr("Powering cloud VM up ...");
+}
+
+QString UINotificationProgressCloudMachinePowerUp::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressCloudMachinePowerUp::createProgress(COMResult &comResult)
+{
+ /* Acquire cloud VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.PowerUp();
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudMachinePowerOff implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudMachinePowerOff::UINotificationProgressCloudMachinePowerOff(const CCloudMachine &comMachine)
+ : m_comMachine(comMachine)
+{
+}
+
+QString UINotificationProgressCloudMachinePowerOff::name() const
+{
+ return UINotificationProgress::tr("Powering cloud VM off ...");
+}
+
+QString UINotificationProgressCloudMachinePowerOff::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressCloudMachinePowerOff::createProgress(COMResult &comResult)
+{
+ /* Acquire cloud VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.PowerDown();
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudMachineShutdown implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudMachineShutdown::UINotificationProgressCloudMachineShutdown(const CCloudMachine &comMachine)
+ : m_comMachine(comMachine)
+{
+}
+
+QString UINotificationProgressCloudMachineShutdown::name() const
+{
+ return UINotificationProgress::tr("Shutting cloud VM down ...");
+}
+
+QString UINotificationProgressCloudMachineShutdown::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressCloudMachineShutdown::createProgress(COMResult &comResult)
+{
+ /* Acquire cloud VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.Shutdown();
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudMachineTerminate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudMachineTerminate::UINotificationProgressCloudMachineTerminate(const CCloudMachine &comMachine)
+ : m_comMachine(comMachine)
+{
+}
+
+QString UINotificationProgressCloudMachineTerminate::name() const
+{
+ return UINotificationProgress::tr("Terminating cloud VM ...");
+}
+
+QString UINotificationProgressCloudMachineTerminate::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressCloudMachineTerminate::createProgress(COMResult &comResult)
+{
+ /* Acquire cloud VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.Terminate();
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudMachineSettingsFormCreate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudMachineSettingsFormCreate::UINotificationProgressCloudMachineSettingsFormCreate(const CCloudMachine &comMachine,
+ const QString &strMachineName)
+ : m_comMachine(comMachine)
+ , m_strMachineName(strMachineName)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressCloudMachineSettingsFormCreate::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressCloudMachineSettingsFormCreate::name() const
+{
+ return UINotificationProgress::tr("Creating cloud VM settings form ...");
+}
+
+QString UINotificationProgressCloudMachineSettingsFormCreate::details() const
+{
+ return UINotificationProgress::tr("<b>Cloud VM Name:</b> %1").arg(m_strMachineName);
+}
+
+CProgress UINotificationProgressCloudMachineSettingsFormCreate::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.GetSettingsForm(m_comForm);
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressCloudMachineSettingsFormCreate::sltHandleProgressFinished()
+{
+ if (m_comForm.isNotNull())
+ emit sigSettingsFormCreated(QVariant::fromValue(m_comForm));
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudMachineSettingsFormApply implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudMachineSettingsFormApply::UINotificationProgressCloudMachineSettingsFormApply(const CForm &comForm,
+ const QString &strMachineName)
+ : m_comForm(comForm)
+ , m_strMachineName(strMachineName)
+{
+}
+
+QString UINotificationProgressCloudMachineSettingsFormApply::name() const
+{
+ return UINotificationProgress::tr("Applying cloud VM settings form ...");
+}
+
+QString UINotificationProgressCloudMachineSettingsFormApply::details() const
+{
+ return UINotificationProgress::tr("<b>Cloud VM Name:</b> %1").arg(m_strMachineName);
+}
+
+CProgress UINotificationProgressCloudMachineSettingsFormApply::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comForm.Apply();
+ /* Store COM result: */
+ comResult = m_comForm;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudConsoleConnectionCreate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudConsoleConnectionCreate::UINotificationProgressCloudConsoleConnectionCreate(const CCloudMachine &comMachine,
+ const QString &strPublicKey)
+ : m_comMachine(comMachine)
+ , m_strPublicKey(strPublicKey)
+{
+}
+
+QString UINotificationProgressCloudConsoleConnectionCreate::name() const
+{
+ return UINotificationProgress::tr("Creating cloud console connection ...");
+}
+
+QString UINotificationProgressCloudConsoleConnectionCreate::details() const
+{
+ return UINotificationProgress::tr("<b>Cloud VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressCloudConsoleConnectionCreate::createProgress(COMResult &comResult)
+{
+ /* Acquire cloud VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.CreateConsoleConnection(m_strPublicKey);
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudConsoleConnectionDelete implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudConsoleConnectionDelete::UINotificationProgressCloudConsoleConnectionDelete(const CCloudMachine &comMachine)
+ : m_comMachine(comMachine)
+{
+}
+
+QString UINotificationProgressCloudConsoleConnectionDelete::name() const
+{
+ return UINotificationProgress::tr("Deleting cloud console connection ...");
+}
+
+QString UINotificationProgressCloudConsoleConnectionDelete::details() const
+{
+ return UINotificationProgress::tr("<b>Cloud VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressCloudConsoleConnectionDelete::createProgress(COMResult &comResult)
+{
+ /* Acquire cloud VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.DeleteConsoleConnection();
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressCloudConsoleLogAcquire implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressCloudConsoleLogAcquire::UINotificationProgressCloudConsoleLogAcquire(const CCloudMachine &comMachine)
+ : m_comMachine(comMachine)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressCloudConsoleLogAcquire::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressCloudConsoleLogAcquire::name() const
+{
+ return UINotificationProgress::tr("Acquire cloud console log ...");
+}
+
+QString UINotificationProgressCloudConsoleLogAcquire::details() const
+{
+ return UINotificationProgress::tr("<b>Cloud VM Name:</b> %1").arg(m_strName);
+}
+
+CProgress UINotificationProgressCloudConsoleLogAcquire::createProgress(COMResult &comResult)
+{
+ /* Acquire cloud VM name: */
+ m_strName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comMachine.GetConsoleHistory(m_comStream);
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressCloudConsoleLogAcquire::sltHandleProgressFinished()
+{
+ /* Read the byte array: */
+ QVector<BYTE> byteArray;
+ while (true)
+ {
+ const QVector<BYTE> byteChunk = m_comStream.Read(64 * _1K, 0);
+ if (byteChunk.size() == 0)
+ break;
+ byteArray += byteChunk;
+ }
+ if (byteArray.size() == 0)
+ return;
+
+ /* Convert it to string and send away: */
+ const QString strLog = QString::fromUtf8(reinterpret_cast<const char *>(byteArray.data()), byteArray.size());
+ emit sigLogRead(m_strName, strLog);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressSnapshotTake implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressSnapshotTake::UINotificationProgressSnapshotTake(const CMachine &comMachine,
+ const QString &strSnapshotName,
+ const QString &strSnapshotDescription)
+ : m_comMachine(comMachine)
+ , m_strSnapshotName(strSnapshotName)
+ , m_strSnapshotDescription(strSnapshotDescription)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressSnapshotTake::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressSnapshotTake::name() const
+{
+ return UINotificationProgress::tr("Taking snapshot ...");
+}
+
+QString UINotificationProgressSnapshotTake::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1<br><b>Snapshot Name:</b> %2").arg(m_strMachineName, m_strSnapshotName);
+}
+
+CProgress UINotificationProgressSnapshotTake::createProgress(COMResult &comResult)
+{
+ /* Acquire VM id: */
+ const QUuid uId = m_comMachine.GetId();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Acquire VM name: */
+ m_strMachineName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Get session machine: */
+ CMachine comMachine;
+
+ /* For Manager UI: */
+ switch (uiCommon().uiType())
+ {
+ case UICommon::UIType_SelectorUI:
+ {
+ /* Acquire session state: */
+ const KSessionState enmSessionState = m_comMachine.GetSessionState();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Open a session thru which we will modify the machine: */
+ if (enmSessionState != KSessionState_Unlocked)
+ m_comSession = uiCommon().openExistingSession(uId);
+ else
+ m_comSession = uiCommon().openSession(uId);
+ if (m_comSession.isNull())
+ return CProgress();
+
+ /* Get session machine: */
+ comMachine = m_comSession.GetMachine();
+ if (!m_comSession.isOk())
+ {
+ comResult = m_comSession;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ break;
+ }
+ case UICommon::UIType_RuntimeUI:
+ {
+ /* Get passed machine: */
+ comMachine = m_comMachine;
+
+ break;
+ }
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = comMachine.TakeSnapshot(m_strSnapshotName,
+ m_strSnapshotDescription,
+ true, m_uSnapshotId);
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressSnapshotTake::sltHandleProgressFinished()
+{
+ if (m_comSession.isNotNull())
+ m_comSession.UnlockMachine();
+
+ if (!m_uSnapshotId.isNull())
+ emit sigSnapshotTaken(QVariant::fromValue(m_uSnapshotId));
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressSnapshotRestore implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressSnapshotRestore::UINotificationProgressSnapshotRestore(const QUuid &uMachineId,
+ const CSnapshot &comSnapshot /* = CSnapshot() */)
+ : m_uMachineId(uMachineId)
+ , m_comSnapshot(comSnapshot)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressSnapshotRestore::sltHandleProgressFinished);
+}
+
+UINotificationProgressSnapshotRestore::UINotificationProgressSnapshotRestore(const CMachine &comMachine,
+ const CSnapshot &comSnapshot /* = CSnapshot() */)
+ : m_comMachine(comMachine)
+ , m_comSnapshot(comSnapshot)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressSnapshotRestore::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressSnapshotRestore::name() const
+{
+ return UINotificationProgress::tr("Restoring snapshot ...");
+}
+
+QString UINotificationProgressSnapshotRestore::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1<br><b>Snapshot Name:</b> %2").arg(m_strMachineName, m_strSnapshotName);
+}
+
+CProgress UINotificationProgressSnapshotRestore::createProgress(COMResult &comResult)
+{
+ /* Make sure machine ID defined: */
+ if (m_uMachineId.isNull())
+ {
+ /* Acquire VM id: */
+ AssertReturn(m_comMachine.isNotNull(), CProgress());
+ m_uMachineId = m_comMachine.GetId();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+ }
+
+ /* Make sure machine defined: */
+ if (m_comMachine.isNull())
+ {
+ /* Acquire VM: */
+ AssertReturn(!m_uMachineId.isNull(), CProgress());
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ m_comMachine = comVBox.FindMachine(m_uMachineId.toString());
+ if (!comVBox.isOk())
+ {
+ comResult = comVBox;
+ return CProgress();
+ }
+ }
+
+ /* Make sure snapshot is defined: */
+ if (m_comSnapshot.isNull())
+ m_comSnapshot = m_comMachine.GetCurrentSnapshot();
+
+ /* Acquire snapshot name: */
+ m_strSnapshotName = m_comSnapshot.GetName();
+ if (!m_comSnapshot.isOk())
+ {
+ comResult = m_comSnapshot;
+ return CProgress();
+ }
+
+ /* Acquire session state: */
+ const KSessionState enmSessionState = m_comMachine.GetSessionState();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Open a session thru which we will modify the machine: */
+ if (enmSessionState != KSessionState_Unlocked)
+ m_comSession = uiCommon().openExistingSession(m_uMachineId);
+ else
+ m_comSession = uiCommon().openSession(m_uMachineId);
+ if (m_comSession.isNull())
+ return CProgress();
+
+ /* Get session machine: */
+ CMachine comMachine = m_comSession.GetMachine();
+ if (!m_comSession.isOk())
+ {
+ comResult = m_comSession;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ /* Acquire VM name: */
+ m_strMachineName = comMachine.GetName();
+ if (!comMachine.isOk())
+ {
+ comResult = comMachine;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = comMachine.RestoreSnapshot(m_comSnapshot);
+ /* Store COM result: */
+ comResult = comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressSnapshotRestore::sltHandleProgressFinished()
+{
+ /* Unlock session finally: */
+ m_comSession.UnlockMachine();
+
+ /* Notifies listeners: */
+ emit sigSnapshotRestored(error().isEmpty());
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressSnapshotDelete implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressSnapshotDelete::UINotificationProgressSnapshotDelete(const CMachine &comMachine,
+ const QUuid &uSnapshotId)
+ : m_comMachine(comMachine)
+ , m_uSnapshotId(uSnapshotId)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressSnapshotDelete::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressSnapshotDelete::name() const
+{
+ return UINotificationProgress::tr("Deleting snapshot ...");
+}
+
+QString UINotificationProgressSnapshotDelete::details() const
+{
+ return UINotificationProgress::tr("<b>VM Name:</b> %1<br><b>Snapshot Name:</b> %2").arg(m_strMachineName, m_strSnapshotName);
+}
+
+CProgress UINotificationProgressSnapshotDelete::createProgress(COMResult &comResult)
+{
+ /* Acquire VM id: */
+ const QUuid uId = m_comMachine.GetId();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Acquire VM name: */
+ m_strMachineName = m_comMachine.GetName();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Acquire snapshot: */
+ CSnapshot comSnapshot = m_comMachine.FindSnapshot(m_uSnapshotId.toString());
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Acquire snapshot name: */
+ m_strSnapshotName = comSnapshot.GetName();
+ if (!comSnapshot.isOk())
+ {
+ comResult = comSnapshot;
+ return CProgress();
+ }
+
+ /* Acquire session state: */
+ const KSessionState enmSessionState = m_comMachine.GetSessionState();
+ if (!m_comMachine.isOk())
+ {
+ comResult = m_comMachine;
+ return CProgress();
+ }
+
+ /* Open a session thru which we will modify the machine: */
+ if (enmSessionState != KSessionState_Unlocked)
+ m_comSession = uiCommon().openExistingSession(uId);
+ else
+ m_comSession = uiCommon().openSession(uId);
+ if (m_comSession.isNull())
+ return CProgress();
+
+ /* Get session machine: */
+ CMachine comMachine = m_comSession.GetMachine();
+ if (!m_comSession.isOk())
+ {
+ comResult = m_comSession;
+ m_comSession.UnlockMachine();
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = comMachine.DeleteSnapshot(m_uSnapshotId);
+ /* Store COM result: */
+ comResult = m_comMachine;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressSnapshotDelete::sltHandleProgressFinished()
+{
+ m_comSession.UnlockMachine();
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressApplianceWrite implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressApplianceWrite::UINotificationProgressApplianceWrite(const CAppliance &comAppliance,
+ const QString &strFormat,
+ const QVector<KExportOptions> &options,
+ const QString &strPath)
+ : m_comAppliance(comAppliance)
+ , m_strFormat(strFormat)
+ , m_options(options)
+ , m_strPath(strPath)
+{
+}
+
+QString UINotificationProgressApplianceWrite::name() const
+{
+ return UINotificationProgress::tr("Writing appliance ...");
+}
+
+QString UINotificationProgressApplianceWrite::details() const
+{
+ return UINotificationProgress::tr("<b>To:</b> %1").arg(m_strPath);
+}
+
+CProgress UINotificationProgressApplianceWrite::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comAppliance.Write(m_strFormat, m_options, m_strPath);
+ /* Store COM result: */
+ comResult = m_comAppliance;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressApplianceRead implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressApplianceRead::UINotificationProgressApplianceRead(const CAppliance &comAppliance,
+ const QString &strPath)
+ : m_comAppliance(comAppliance)
+ , m_strPath(strPath)
+{
+}
+
+QString UINotificationProgressApplianceRead::name() const
+{
+ return UINotificationProgress::tr("Reading appliance ...");
+}
+
+QString UINotificationProgressApplianceRead::details() const
+{
+ return UINotificationProgress::tr("<b>From:</b> %1").arg(m_strPath);
+}
+
+CProgress UINotificationProgressApplianceRead::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comAppliance.Read(m_strPath);
+ /* Store COM result: */
+ comResult = m_comAppliance;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressApplianceImport implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressApplianceImport::UINotificationProgressApplianceImport(const CAppliance &comAppliance,
+ const QVector<KImportOptions> &options)
+ : m_comAppliance(comAppliance)
+ , m_options(options)
+{
+}
+
+QString UINotificationProgressApplianceImport::name() const
+{
+ return UINotificationProgress::tr("Importing appliance ...");
+}
+
+QString UINotificationProgressApplianceImport::details() const
+{
+ return UINotificationProgress::tr("<b>From:</b> %1").arg(m_comAppliance.GetPath());
+}
+
+CProgress UINotificationProgressApplianceImport::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comAppliance.ImportMachines(m_options);
+ /* Store COM result: */
+ comResult = m_comAppliance;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressExtensionPackInstall implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressExtensionPackInstall::UINotificationProgressExtensionPackInstall(const CExtPackFile &comExtPackFile,
+ bool fReplace,
+ const QString &strExtensionPackName,
+ const QString &strDisplayInfo)
+ : m_comExtPackFile(comExtPackFile)
+ , m_fReplace(fReplace)
+ , m_strExtensionPackName(strExtensionPackName)
+ , m_strDisplayInfo(strDisplayInfo)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressExtensionPackInstall::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressExtensionPackInstall::name() const
+{
+ return UINotificationProgress::tr("Installing package ...");
+}
+
+QString UINotificationProgressExtensionPackInstall::details() const
+{
+ return UINotificationProgress::tr("<b>Name:</b> %1").arg(m_strExtensionPackName);
+}
+
+CProgress UINotificationProgressExtensionPackInstall::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comExtPackFile.Install(m_fReplace, m_strDisplayInfo);
+ /* Store COM result: */
+ comResult = m_comExtPackFile;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressExtensionPackInstall::sltHandleProgressFinished()
+{
+ if (error().isEmpty())
+ emit sigExtensionPackInstalled(m_strExtensionPackName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressExtensionPackUninstall implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressExtensionPackUninstall::UINotificationProgressExtensionPackUninstall(const CExtPackManager &comExtPackManager,
+ const QString &strExtensionPackName,
+ const QString &strDisplayInfo)
+ : m_comExtPackManager(comExtPackManager)
+ , m_strExtensionPackName(strExtensionPackName)
+ , m_strDisplayInfo(strDisplayInfo)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressExtensionPackUninstall::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressExtensionPackUninstall::name() const
+{
+ return UINotificationProgress::tr("Uninstalling package ...");
+}
+
+QString UINotificationProgressExtensionPackUninstall::details() const
+{
+ return UINotificationProgress::tr("<b>Name:</b> %1").arg(m_strExtensionPackName);
+}
+
+CProgress UINotificationProgressExtensionPackUninstall::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comExtPackManager.Uninstall(m_strExtensionPackName,
+ false /* forced removal? */,
+ m_strDisplayInfo);
+ /* Store COM result: */
+ comResult = m_comExtPackManager;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressExtensionPackUninstall::sltHandleProgressFinished()
+{
+ if (error().isEmpty())
+ emit sigExtensionPackUninstalled(m_strExtensionPackName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressGuestAdditionsInstall implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressGuestAdditionsInstall::UINotificationProgressGuestAdditionsInstall(const CGuest &comGuest,
+ const QString &strSource)
+ : m_comGuest(comGuest)
+ , m_strSource(strSource)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressGuestAdditionsInstall::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressGuestAdditionsInstall::name() const
+{
+ return UINotificationProgress::tr("Installing image ...");
+}
+
+QString UINotificationProgressGuestAdditionsInstall::details() const
+{
+ return UINotificationProgress::tr("<b>Name:</b> %1").arg(m_strSource);
+}
+
+CProgress UINotificationProgressGuestAdditionsInstall::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ QVector<QString> args;
+ QVector<KAdditionsUpdateFlag> flags;
+ CProgress comProgress = m_comGuest.UpdateGuestAdditions(m_strSource, args, flags);
+ /* Store COM result: */
+ comResult = m_comGuest;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressGuestAdditionsInstall::sltHandleProgressFinished()
+{
+ if (!error().isEmpty())
+ emit sigGuestAdditionsInstallationFailed(m_strSource);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressHostOnlyNetworkInterfaceCreate implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressHostOnlyNetworkInterfaceCreate::UINotificationProgressHostOnlyNetworkInterfaceCreate(const CHost &comHost,
+ const CHostNetworkInterface &comInterface)
+ : m_comHost(comHost)
+ , m_comInterface(comInterface)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressHostOnlyNetworkInterfaceCreate::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressHostOnlyNetworkInterfaceCreate::name() const
+{
+ return UINotificationProgress::tr("Creating Host-only Network Interface ...");
+}
+
+QString UINotificationProgressHostOnlyNetworkInterfaceCreate::details() const
+{
+ return UINotificationProgress::tr("<b>Name:</b> %1").arg("TBD");
+}
+
+CProgress UINotificationProgressHostOnlyNetworkInterfaceCreate::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comHost.CreateHostOnlyNetworkInterface(m_comInterface);
+ /* Store COM result: */
+ comResult = m_comHost;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressHostOnlyNetworkInterfaceCreate::sltHandleProgressFinished()
+{
+ if (error().isEmpty())
+ emit sigHostOnlyNetworkInterfaceCreated(m_comInterface);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressHostOnlyNetworkInterfaceRemove implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressHostOnlyNetworkInterfaceRemove::UINotificationProgressHostOnlyNetworkInterfaceRemove(const CHost &comHost,
+ const QUuid &uInterfaceId)
+ : m_comHost(comHost)
+ , m_uInterfaceId(uInterfaceId)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressHostOnlyNetworkInterfaceRemove::sltHandleProgressFinished);
+}
+
+QString UINotificationProgressHostOnlyNetworkInterfaceRemove::name() const
+{
+ return UINotificationProgress::tr("Removing Host-only Network Interface ...");
+}
+
+QString UINotificationProgressHostOnlyNetworkInterfaceRemove::details() const
+{
+ return UINotificationProgress::tr("<b>Name:</b> %1").arg(m_strInterfaceName);
+}
+
+CProgress UINotificationProgressHostOnlyNetworkInterfaceRemove::createProgress(COMResult &comResult)
+{
+ /* Acquire interface: */
+ CHostNetworkInterface comInterface = m_comHost.FindHostNetworkInterfaceById(m_uInterfaceId);
+ if (!m_comHost.isOk())
+ {
+ comResult = m_comHost;
+ return CProgress();
+ }
+
+ /* Acquire interface name: */
+ m_strInterfaceName = comInterface.GetName();
+ if (!comInterface.isOk())
+ {
+ comResult = comInterface;
+ return CProgress();
+ }
+
+ /* Initialize progress-wrapper: */
+ CProgress comProgress = m_comHost.RemoveHostOnlyNetworkInterface(m_uInterfaceId);
+ /* Store COM result: */
+ comResult = m_comHost;
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressHostOnlyNetworkInterfaceRemove::sltHandleProgressFinished()
+{
+ if (error().isEmpty())
+ emit sigHostOnlyNetworkInterfaceRemoved(m_strInterfaceName);
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressVsdFormValueSet implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressVsdFormValueSet::UINotificationProgressVsdFormValueSet(const CBooleanFormValue &comValue,
+ bool fBool)
+ : m_enmType(KFormValueType_Boolean)
+ , m_comValue(comValue)
+ , m_fBool(fBool)
+{
+}
+
+UINotificationProgressVsdFormValueSet::UINotificationProgressVsdFormValueSet(const CStringFormValue &comValue,
+ const QString &strString)
+ : m_enmType(KFormValueType_String)
+ , m_comValue(comValue)
+ , m_strString(strString)
+{
+}
+
+UINotificationProgressVsdFormValueSet::UINotificationProgressVsdFormValueSet(const CChoiceFormValue &comValue,
+ int iChoice)
+ : m_enmType(KFormValueType_Choice)
+ , m_comValue(comValue)
+ , m_iChoice(iChoice)
+{
+}
+
+UINotificationProgressVsdFormValueSet::UINotificationProgressVsdFormValueSet(const CRangedIntegerFormValue &comValue,
+ int iInteger)
+ : m_enmType(KFormValueType_RangedInteger)
+ , m_comValue(comValue)
+ , m_iInteger(iInteger)
+{
+}
+
+QString UINotificationProgressVsdFormValueSet::name() const
+{
+ return UINotificationProgress::tr("Set VSD form value ...");
+}
+
+QString UINotificationProgressVsdFormValueSet::details() const
+{
+ /* Handle known types: */
+ switch (m_enmType)
+ {
+ case KFormValueType_Boolean: return UINotificationProgress::tr("<b>Value:</b> %1").arg(m_fBool);
+ case KFormValueType_String: return UINotificationProgress::tr("<b>Value:</b> %1").arg(m_strString);
+ case KFormValueType_Choice: return UINotificationProgress::tr("<b>Value:</b> %1").arg(m_iChoice);
+ case KFormValueType_RangedInteger: return UINotificationProgress::tr("<b>Value:</b> %1").arg(m_iInteger);
+ default: break;
+ }
+ /* Null-string by default: */
+ return QString();
+}
+
+CProgress UINotificationProgressVsdFormValueSet::createProgress(COMResult &comResult)
+{
+ /* Initialize progress-wrapper: */
+ CProgress comProgress;
+
+ /* Handle known types: */
+ switch (m_enmType)
+ {
+ case KFormValueType_Boolean:
+ {
+ /* Set value: */
+ CBooleanFormValue comValue(m_comValue);
+ comProgress = comValue.SetSelected(m_fBool);
+ /* Store COM result: */
+ comResult = comValue;
+ break;
+ }
+ case KFormValueType_String:
+ {
+ /* Set value: */
+ CStringFormValue comValue(m_comValue);
+ comProgress = comValue.SetString(m_strString);
+ /* Store COM result: */
+ comResult = comValue;
+ break;
+ }
+ case KFormValueType_Choice:
+ {
+ /* Set value: */
+ CChoiceFormValue comValue(m_comValue);
+ comProgress = comValue.SetSelectedIndex(m_iChoice);
+ /* Store COM result: */
+ comResult = comValue;
+ break;
+ }
+ case KFormValueType_RangedInteger:
+ {
+ /* Set value: */
+ CRangedIntegerFormValue comValue(m_comValue);
+ comProgress = comValue.SetInteger(m_iInteger);
+ /* Store COM result: */
+ comResult = comValue;
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+
+
+/*********************************************************************************************************************************
+* Class UINotificationDownloaderExtensionPack implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UINotificationDownloaderExtensionPack *UINotificationDownloaderExtensionPack::s_pInstance = 0;
+
+/* static */
+UINotificationDownloaderExtensionPack *UINotificationDownloaderExtensionPack::instance(const QString &strPackName)
+{
+ if (!s_pInstance)
+ new UINotificationDownloaderExtensionPack(strPackName);
+ return s_pInstance;
+}
+
+/* static */
+bool UINotificationDownloaderExtensionPack::exists()
+{
+ return !!s_pInstance;
+}
+
+UINotificationDownloaderExtensionPack::UINotificationDownloaderExtensionPack(const QString &strPackName)
+ : m_strPackName(strPackName)
+{
+ s_pInstance = this;
+}
+
+UINotificationDownloaderExtensionPack::~UINotificationDownloaderExtensionPack()
+{
+ s_pInstance = 0;
+}
+
+QString UINotificationDownloaderExtensionPack::name() const
+{
+ return UINotificationDownloader::tr("Downloading Extension Pack ...");
+}
+
+QString UINotificationDownloaderExtensionPack::details() const
+{
+ return UINotificationProgress::tr("<b>Name:</b> %1").arg(m_strPackName);
+}
+
+UIDownloader *UINotificationDownloaderExtensionPack::createDownloader()
+{
+ /* Create and configure the Extension Pack downloader: */
+ UIDownloaderExtensionPack *pDownloader = new UIDownloaderExtensionPack;
+ if (pDownloader)
+ {
+ connect(pDownloader, &UIDownloaderExtensionPack::sigDownloadFinished,
+ this, &UINotificationDownloaderExtensionPack::sigExtensionPackDownloaded);
+ return pDownloader;
+ }
+ return 0;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationDownloaderGuestAdditions implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UINotificationDownloaderGuestAdditions *UINotificationDownloaderGuestAdditions::s_pInstance = 0;
+
+/* static */
+UINotificationDownloaderGuestAdditions *UINotificationDownloaderGuestAdditions::instance(const QString &strFileName)
+{
+ if (!s_pInstance)
+ new UINotificationDownloaderGuestAdditions(strFileName);
+ return s_pInstance;
+}
+
+/* static */
+bool UINotificationDownloaderGuestAdditions::exists()
+{
+ return !!s_pInstance;
+}
+
+UINotificationDownloaderGuestAdditions::UINotificationDownloaderGuestAdditions(const QString &strFileName)
+ : m_strFileName(strFileName)
+{
+ s_pInstance = this;
+}
+
+UINotificationDownloaderGuestAdditions::~UINotificationDownloaderGuestAdditions()
+{
+ s_pInstance = 0;
+}
+
+QString UINotificationDownloaderGuestAdditions::name() const
+{
+ return UINotificationDownloader::tr("Downloading Guest Additions ...");
+}
+
+QString UINotificationDownloaderGuestAdditions::details() const
+{
+ return UINotificationProgress::tr("<b>Name:</b> %1").arg(m_strFileName);
+}
+
+UIDownloader *UINotificationDownloaderGuestAdditions::createDownloader()
+{
+ /* Create and configure the User Manual downloader: */
+ UIDownloaderGuestAdditions *pDownloader = new UIDownloaderGuestAdditions;
+ if (pDownloader)
+ {
+ connect(pDownloader, &UIDownloaderGuestAdditions::sigDownloadFinished,
+ this, &UINotificationDownloaderGuestAdditions::sigGuestAdditionsDownloaded);
+ return pDownloader;
+ }
+ return 0;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationDownloaderUserManual implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UINotificationDownloaderUserManual *UINotificationDownloaderUserManual::s_pInstance = 0;
+
+/* static */
+UINotificationDownloaderUserManual *UINotificationDownloaderUserManual::instance(const QString &strFileName)
+{
+ if (!s_pInstance)
+ new UINotificationDownloaderUserManual(strFileName);
+ return s_pInstance;
+}
+
+/* static */
+bool UINotificationDownloaderUserManual::exists()
+{
+ return !!s_pInstance;
+}
+
+UINotificationDownloaderUserManual::UINotificationDownloaderUserManual(const QString &strFileName)
+ : m_strFileName(strFileName)
+{
+ s_pInstance = this;
+}
+
+UINotificationDownloaderUserManual::~UINotificationDownloaderUserManual()
+{
+ s_pInstance = 0;
+}
+
+QString UINotificationDownloaderUserManual::name() const
+{
+ return UINotificationDownloader::tr("Downloading User Manual ...");
+}
+
+QString UINotificationDownloaderUserManual::details() const
+{
+ return UINotificationProgress::tr("<b>Name:</b> %1").arg(m_strFileName);
+}
+
+UIDownloader *UINotificationDownloaderUserManual::createDownloader()
+{
+ /* Create and configure the User Manual downloader: */
+ UIDownloaderUserManual *pDownloader = new UIDownloaderUserManual;
+ if (pDownloader)
+ {
+ connect(pDownloader, &UIDownloaderUserManual::sigDownloadFinished,
+ this, &UINotificationDownloaderUserManual::sigUserManualDownloaded);
+ return pDownloader;
+ }
+ return 0;
+}
+
+
+/*********************************************************************************************************************************
+* Class UINotificationProgressNewVersionChecker implementation. *
+*********************************************************************************************************************************/
+
+UINotificationProgressNewVersionChecker::UINotificationProgressNewVersionChecker(bool fForcedCall)
+ : m_fForcedCall(fForcedCall)
+{
+ connect(this, &UINotificationProgress::sigProgressFinished,
+ this, &UINotificationProgressNewVersionChecker::sltHandleProgressFinished);
+
+#ifdef VBOX_WITH_UPDATE_AGENT
+ CHost comHost = uiCommon().host();
+ if (!comHost.isNull())
+ m_comUpdateHost = comHost.GetUpdateHost();
+#endif /* VBOX_WITH_UPDATE_AGENT */
+}
+
+QString UINotificationProgressNewVersionChecker::name() const
+{
+#ifdef VBOX_WITH_UPDATE_AGENT
+ if (m_comUpdateHost.isOk())
+ return UINotificationProgress::tr("Checking for new version of %1 ...").arg(m_comUpdateHost.GetName().toLocal8Bit().data());
+#endif /* VBOX_WITH_UPDATE_AGENT */
+ return UINotificationProgress::tr("Checking for new version ...");
+}
+
+QString UINotificationProgressNewVersionChecker::details() const
+{
+ return QString();
+}
+
+CProgress UINotificationProgressNewVersionChecker::createProgress(COMResult &comResult)
+{
+#ifdef VBOX_WITH_UPDATE_AGENT
+ if (!m_comUpdateHost.isOk())
+ return CProgress();
+
+ CProgress comProgress = m_comUpdateHost.CheckFor();
+ comResult = m_comUpdateHost;
+
+ return comProgress;
+#else
+ return CProgress();
+#endif /* VBOX_WITH_UPDATE_AGENT */
+}
+
+void UINotificationProgressNewVersionChecker::sltHandleProgressFinished()
+{
+#ifdef VBOX_WITH_UPDATE_AGENT
+ if (m_comUpdateHost.isNull() && !m_comUpdateHost.isOk())
+ return;
+
+ bool const fUpdateAvailable = m_comUpdateHost.GetState() == KUpdateState_Available; /** @todo Handle other states. */
+ if (!m_comUpdateHost.isOk())
+ return;
+
+ if (fUpdateAvailable)
+ {
+ QString strVersion = m_comUpdateHost.GetVersion();
+ if (!m_comUpdateHost.isOk())
+ return;
+
+ QString strURL = m_comUpdateHost.GetDownloadUrl();
+ if (!m_comUpdateHost.isOk())
+ return;
+
+ UINotificationMessage::showUpdateSuccess(strVersion, strURL);
+ }
+ else
+ {
+ if (m_fForcedCall)
+ UINotificationMessage::showUpdateNotFound();
+ }
+#endif /* VBOX_WITH_UPDATE_AGENT */
+}
+
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjects.h b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjects.h
new file mode 100644
index 00000000..c221f216
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationObjects.h
@@ -0,0 +1,2700 @@
+/* $Id: UINotificationObjects.h $ */
+/** @file
+ * VBox Qt GUI - Various UINotificationObjects declarations.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_notificationcenter_UINotificationObjects_h
+#define FEQT_INCLUDED_SRC_notificationcenter_UINotificationObjects_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QUuid>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UINotificationObject.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CAppliance.h"
+#include "CCloudClient.h"
+#include "CCloudMachine.h"
+#include "CConsole.h"
+#include "CDataStream.h"
+#include "CExtPackFile.h"
+#include "CExtPackManager.h"
+#include "CForm.h"
+#include "CFormValue.h"
+#include "CGuest.h"
+#include "CHost.h"
+#include "CHostNetworkInterface.h"
+#include "CMachine.h"
+#include "CMedium.h"
+#include "CSession.h"
+#include "CSnapshot.h"
+#include "CStringArray.h"
+#ifdef VBOX_WITH_UPDATE_AGENT
+# include "CUpdateAgent.h"
+#endif
+#include "CVFSExplorer.h"
+#include "CVirtualSystemDescription.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/* Forward declarations: */
+class UINotificationCenter;
+class CAudioAdapter;
+class CCloudProviderManager;
+class CCloudProvider;
+class CCloudProfile;
+class CEmulatedUSB;
+class CNetworkAdapter;
+class CVirtualBox;
+class CVirtualBoxErrorInfo;
+class CVRDEServer;
+class CUnattended;
+
+/** UINotificationObject extension for message functionality. */
+class SHARED_LIBRARY_STUFF UINotificationMessage : public UINotificationSimple
+{
+ Q_OBJECT;
+
+public:
+
+ /** @name Simple general warnings.
+ * @{ */
+ /** Notifies about inability to find help file at certain @a strLocation. */
+ static void cannotFindHelpFile(const QString &strLocation);
+
+ /** Notifies about inability to open @a strUrl. */
+ static void cannotOpenURL(const QString &strUrl);
+
+ /** Reminds about BETA build. */
+ static void remindAboutBetaBuild();
+ /** Reminds about BETA build. */
+ static void remindAboutExperimentalBuild();
+ /** Notifies about invalid encryption password.
+ * @param strPasswordId Brings password ID. */
+ static void warnAboutInvalidEncryptionPassword(const QString &strPasswordId);
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /** Notifies about update not found. */
+ static void showUpdateNotFound();
+ /** Notifies about update successful.
+ * @param strVersion Brings the found version.
+ * @param strLink Brings the link to found package. */
+ static void showUpdateSuccess(const QString &strVersion, const QString &strLink);
+ /** Notifies about extension pack needs to be updated.
+ * @param strExtPackName Brings the package name.
+ * @param strExtPackVersion Brings the package version.
+ * @param strVBoxVersion Brings VBox version. */
+ static void askUserToDownloadExtensionPack(const QString &strExtPackName,
+ const QString &strExtPackVersion,
+ const QString &strVBoxVersion);
+
+ /** Notifies about inability to validate guest additions.
+ * @param strUrl Brings the GA URL.
+ * @param strSrc Brings the GA source. */
+ static void cannotValidateGuestAdditionsSHA256Sum(const QString &strUrl,
+ const QString &strSrc);
+
+ /** Notifies about user manual downloded.
+ * @param strUrl Brings the GA URL.
+ * @param strSrc Brings the GA source. */
+ static void warnAboutUserManualDownloaded(const QString &strUrl,
+ const QString &strTarget);
+
+ /** Notifies about inability to validate guest additions.
+ * @param strUrl Brings the GA URL.
+ * @param strSrc Brings the GA source. */
+ static void cannotValidateExtentionPackSHA256Sum(const QString &strExtPackName,
+ const QString &strFrom,
+ const QString &strTo);
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ /** @} */
+
+ /** @name Simple VirtualBox Manager warnings.
+ * @{ */
+ /** Notifies about inability to create machine folder.
+ * @param strPath Brings the machine folder path. */
+ static void cannotCreateMachineFolder(const QString &strPath,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to overwrite machine folder.
+ * @param strPath Brings the machine folder path. */
+ static void cannotOverwriteMachineFolder(const QString &strPath,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to remove machine folder.
+ * @param strPath Brings the machine folder path. */
+ static void cannotRemoveMachineFolder(const QString &strPath,
+ UINotificationCenter *pParent = 0);
+
+ /** Notifies about inability to register existing machine.
+ * @param streName Brings the machine name.
+ * @param strLocation Brings the machine location. */
+ static void cannotReregisterExistingMachine(const QString &strName, const QString &strLocation);
+
+ /** Notifies about inability to resolve collision automatically.
+ * @param strCollisionName Brings the collision VM name.
+ * @param strGroupName Brings the group name. */
+ static void cannotResolveCollisionAutomatically(const QString &strCollisionName, const QString &strGroupName);
+
+ /** Notifies about inability to acquire cloud machine settings.
+ * @param strErrorDetails Brings the error details. */
+ static void cannotAcquireCloudMachineSettings(const QString &strErrorDetails);
+
+ /** Notifies about inability to create medium storage in FAT.
+ * @param strPath Brings the medium path. */
+ static void cannotCreateMediumStorageInFAT(const QString &strPath,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to overwrite medium storage.
+ * @param strPath Brings the medium path. */
+ static void cannotOverwriteMediumStorage(const QString &strPath,
+ UINotificationCenter *pParent = 0);
+
+ /** Notifies about inability to open license file.
+ * @param strPath Brings the license file path. */
+ static void cannotOpenLicenseFile(const QString &strPath);
+
+ /** Notifies about public key path is empty. */
+ static void warnAboutPublicKeyFilePathIsEmpty();
+ /** Notifies about public key file doesn't exist.
+ * @param strPath Brings the path being checked. */
+ static void warnAboutPublicKeyFileDoesntExist(const QString &strPath);
+ /** Notifies about public key file is of too large size.
+ * @param strPath Brings the path being checked. */
+ static void warnAboutPublicKeyFileIsOfTooLargeSize(const QString &strPath);
+ /** Notifies about public key file isn't readable.
+ * @param strPath Brings the path being checked. */
+ static void warnAboutPublicKeyFileIsntReadable(const QString &strPath);
+
+ /** Notifies about DHCP server isn't enabled.
+ * @param strName Brings the interface name. */
+ static void warnAboutDHCPServerIsNotEnabled(const QString &strName);
+ /** Notifies about invalid IPv4 address.
+ * @param strName Brings the interface name. */
+ static void warnAboutInvalidIPv4Address(const QString &strName);
+ /** Notifies about invalid IPv4 mask.
+ * @param strName Brings the interface name. */
+ static void warnAboutInvalidIPv4Mask(const QString &strName);
+ /** Notifies about invalid IPv6 address.
+ * @param strName Brings the interface name. */
+ static void warnAboutInvalidIPv6Address(const QString &strName);
+ /** Notifies about invalid IPv6 prefix length.
+ * @param strName Brings the interface name. */
+ static void warnAboutInvalidIPv6PrefixLength(const QString &strName);
+ /** Notifies about invalid DHCP server address.
+ * @param strName Brings the interface name. */
+ static void warnAboutInvalidDHCPServerAddress(const QString &strName);
+ /** Notifies about invalid DHCP server mask.
+ * @param strName Brings the interface name. */
+ static void warnAboutInvalidDHCPServerMask(const QString &strName);
+ /** Notifies about invalid DHCP server lower address.
+ * @param strName Brings the interface name. */
+ static void warnAboutInvalidDHCPServerLowerAddress(const QString &strName);
+ /** Notifies about invalid DHCP server upper address.
+ * @param strName Brings the interface name. */
+ static void warnAboutInvalidDHCPServerUpperAddress(const QString &strName);
+ /** Notifies about no name specified.
+ * @param strName Brings the interface name. */
+ static void warnAboutNoNameSpecified(const QString &strName);
+ /** Notifies about name already busy.
+ * @param strName Brings the interface name. */
+ static void warnAboutNameAlreadyBusy(const QString &strName);
+ /** Notifies about no IPv4 prefix specified.
+ * @param strName Brings the interface name. */
+ static void warnAboutNoIPv4PrefixSpecified(const QString &strName);
+ /** Notifies about no IPv6 prefix specified.
+ * @param strName Brings the interface name. */
+ static void warnAboutNoIPv6PrefixSpecified(const QString &strName);
+ /** @} */
+
+ /** @name Simple Runtime UI warnings.
+ * @{ */
+ /** Notifies about inability to mount image.
+ * @param strMachineName Brings the machine name.
+ * @param strMediumName Brings the medium name. */
+ static void cannotMountImage(const QString &strMachineName, const QString &strMediumName);
+
+ /** Notifies about inability to send ACPI shutdown. */
+ static void cannotSendACPIToMachine();
+
+ /** Reminds about keyboard auto capturing. */
+ static void remindAboutAutoCapture();
+
+ /** Reminds about GA not affected. */
+ static void remindAboutGuestAdditionsAreNotActive();
+
+ /** Reminds about mouse integration.
+ * @param fSupportsAbsolute Brings whether mouse supports absolute pointing. */
+ static void remindAboutMouseIntegration(bool fSupportsAbsolute);
+
+ /** Reminds about paused VM input. */
+ static void remindAboutPausedVMInput();
+ /** Revokes message about paused VM input. */
+ static void forgetAboutPausedVMInput();
+
+ /** Reminds about wrong color depth.
+ * @param uRealBPP Brings real bit per pixel value.
+ * @param uWantedBPP Brings wanted bit per pixel value. */
+ static void remindAboutWrongColorDepth(ulong uRealBPP, ulong uWantedBPP);
+ /** Revokes message about wrong color depth. */
+ static void forgetAboutWrongColorDepth();
+ /** @} */
+
+ /** @name COM general warnings.
+ * @{ */
+ /** Notifies about inability to acquire IVirtualBox parameter.
+ * @param comVBox Brings the object parameter get acquired from. */
+ static void cannotAcquireVirtualBoxParameter(const CVirtualBox &comVBox,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to acquire IAppliance parameter.
+ * @param comVBox Brings the object parameter get acquired from. */
+ static void cannotAcquireApplianceParameter(const CAppliance &comAppliance,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to acquire IExtPackManager parameter.
+ * @param comVBox Brings the object parameter get acquired from. */
+ static void cannotAcquireExtensionPackManagerParameter(const CExtPackManager &comEPManager);
+ /** Notifies about inability to acquire IExtPack parameter.
+ * @param comPackage Brings the object parameter get acquired from. */
+ static void cannotAcquireExtensionPackParameter(const CExtPack &comPackage);
+ /** Notifies about inability to acquire IHost parameter.
+ * @param comHost Brings the object parameter get acquired from. */
+ static void cannotAcquireHostParameter(const CHost &comHost);
+ /** Notifies about inability to acquire IMedium parameter.
+ * @param comMedium Brings the object parameter get acquired from. */
+ static void cannotAcquireMediumParameter(const CMedium &comMedium);
+ /** Notifies about inability to acquire ISession parameter.
+ * @param comSession Brings the object parameter get acquired from. */
+ static void cannotAcquireSessionParameter(const CSession &comSession);
+ /** Notifies about inability to acquire IMachine parameter.
+ * @param comSession Brings the object parameter get acquired from. */
+ static void cannotAcquireMachineParameter(const CMachine &comMachine);
+ /** Notifies about inability to acquire ISnapshot parameter.
+ * @param comSnapshot Brings the object parameter get acquired from. */
+ static void cannotAcquireSnapshotParameter(const CSnapshot &comSnapshot);
+ /** Notifies about inability to acquire IDHCPServer parameter.
+ * @param comServer Brings the object parameter get acquired from. */
+ static void cannotAcquireDHCPServerParameter(const CDHCPServer &comServer);
+ /** Notifies about inability to acquire ICloudNetwork parameter.
+ * @param comNetwork Brings the object parameter get acquired from. */
+ static void cannotAcquireCloudNetworkParameter(const CCloudNetwork &comNetwork);
+ /** Notifies about inability to acquire IHostNetworkInterface parameter.
+ * @param comInterface Brings the object parameter get acquired from. */
+ static void cannotAcquireHostNetworkInterfaceParameter(const CHostNetworkInterface &comInterface);
+ /** Notifies about inability to acquire IHostOnlyNetwork parameter.
+ * @param comNetwork Brings the object parameter get acquired from. */
+ static void cannotAcquireHostOnlyNetworkParameter(const CHostOnlyNetwork &comNetwork);
+ /** Notifies about inability to acquire INATNetwork parameter.
+ * @param comNetwork Brings the object parameter get acquired from. */
+ static void cannotAcquireNATNetworkParameter(const CNATNetwork &comNetwork);
+ /** Notifies about inability to acquire INATNetwork parameter.
+ * @param comNetwork Brings the object parameter get acquired from. */
+ static void cannotAcquireDispayParameter(const CDisplay &comDisplay);
+ /** Notifies about inability to acquire IUpdateAgent parameter.
+ * @param comAgent Brings the object parameter get acquired from. */
+ static void cannotAcquireUpdateAgentParameter(const CUpdateAgent &comAgent);
+ /** Notifies about inability to acquire IVirtualSystemDescription parameter.
+ * @param comVsd Brings the object parameter get acquired from. */
+ static void cannotAcquireVirtualSystemDescriptionParameter(const CVirtualSystemDescription &comVsd,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to acquire IVirtualSystemDescriptionForm parameter.
+ * @param comVsdForm Brings the object parameter get acquired from. */
+ static void cannotAcquireVirtualSystemDescriptionFormParameter(const CVirtualSystemDescriptionForm &comVsdForm,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to acquire ICloudProviderManager parameter.
+ * @param comCloudProviderManager Brings the object parameter get acquired from. */
+ static void cannotAcquireCloudProviderManagerParameter(const CCloudProviderManager &comCloudProviderManager,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to acquire ICloudProvider parameter.
+ * @param comCloudProvider Brings the object parameter get acquired from. */
+ static void cannotAcquireCloudProviderParameter(const CCloudProvider &comCloudProvider,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to acquire ICloudProfile parameter.
+ * @param comCloudProfile Brings the object parameter get acquired from. */
+ static void cannotAcquireCloudProfileParameter(const CCloudProfile &comCloudProfile,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to acquire ICloudMachine parameter.
+ * @param comCloudMachine Brings the object parameter get acquired from. */
+ static void cannotAcquireCloudMachineParameter(const CCloudMachine &comCloudMachine,
+ UINotificationCenter *pParent = 0);
+
+ /** Notifies about inability to change IMedium parameter.
+ * @param comMedium Brings the object parameter being changed for. */
+ static void cannotChangeMediumParameter(const CMedium &comMedium);
+ /** Notifies about inability to change IMachine parameter.
+ * @param comMachine Brings the object parameter being changed for. */
+ static void cannotChangeMachineParameter(const CMachine &comMachine);
+ /** Notifies about inability to change IGraphicsAdapter parameter.
+ * @param comAdapter Brings the object parameter being changed for. */
+ static void cannotChangeGraphicsAdapterParameter(const CGraphicsAdapter &comAdapter);
+ /** Notifies about inability to change IAudioAdapter parameter.
+ * @param comAdapter Brings the object parameter being changed for. */
+ static void cannotChangeAudioAdapterParameter(const CAudioAdapter &comAdapter);
+ /** Notifies about inability to change INetworkAdapter parameter.
+ * @param comAdapter Brings the object parameter being changed for. */
+ static void cannotChangeNetworkAdapterParameter(const CNetworkAdapter &comAdapter);
+ /** Notifies about inability to change IDHCPServer parameter.
+ * @param comServer Brings the object parameter being changed for. */
+ static void cannotChangeDHCPServerParameter(const CDHCPServer &comServer);
+ /** Notifies about inability to change ICloudNetwork parameter.
+ * @param comNetwork Brings the object parameter being changed for. */
+ static void cannotChangeCloudNetworkParameter(const CCloudNetwork &comNetwork);
+ /** Notifies about inability to change IHostNetworkInterface parameter.
+ * @param comInterface Brings the object parameter being changed for. */
+ static void cannotChangeHostNetworkInterfaceParameter(const CHostNetworkInterface &comInterface);
+ /** Notifies about inability to change IHostOnlyNetwork parameter.
+ * @param comNetwork Brings the object parameter being changed for. */
+ static void cannotChangeHostOnlyNetworkParameter(const CHostOnlyNetwork &comNetwork);
+ /** Notifies about inability to change INATNetwork parameter.
+ * @param comNetwork Brings the object parameter being changed for. */
+ static void cannotChangeNATNetworkParameter(const CNATNetwork &comNetwork);
+ /** Notifies about inability to change ICloudProfile parameter.
+ * @param comProfile Brings the object parameter being changed for. */
+ static void cannotChangeCloudProfileParameter(const CCloudProfile &comProfile);
+ /** Notifies about inability to change IUpdateAgent parameter.
+ * @param comAgent Brings the object parameter being changed for. */
+ static void cannotChangeUpdateAgentParameter(const CUpdateAgent &comAgent);
+ /** Notifies about inability to change IVirtualSystemDescription parameter.
+ * @param comVsd Brings the object parameter being changed for. */
+ static void cannotChangeVirtualSystemDescriptionParameter(const CVirtualSystemDescription &comVsd,
+ UINotificationCenter *pParent = 0);
+
+ /** Notifies about inability to enumerate host USB devices.
+ * @param comHost Brings the host devices enumerated for. */
+ static void cannotEnumerateHostUSBDevices(const CHost &comHost);
+ /** Notifies about inability to open medium.
+ * @param comVBox Brings common VBox object trying to open medium.
+ * @param strLocation Brings the medium location. */
+ static void cannotOpenMedium(const CVirtualBox &comVBox, const QString &strLocation,
+ UINotificationCenter *pParent = 0);
+
+ /** Notifies about inability to pause machine.
+ * @param comConsole Brings console trying to pause machine. */
+ static void cannotPauseMachine(const CConsole &comConsole);
+ /** Notifies about inability to resume machine.
+ * @param comConsole Brings console trying to resume machine. */
+ static void cannotResumeMachine(const CConsole &comConsole);
+ /** Notifies about inability to ACPI shutdown machine.
+ * @param comConsole Brings console trying to shutdown machine. */
+ static void cannotACPIShutdownMachine(const CConsole &comConsole);
+ /** @} */
+
+ /** @name COM VirtualBox Manager warnings.
+ * @{ */
+ /** Notifies about inability to create appliance.
+ * @param comVBox Brings common VBox object trying to create appliance. */
+ static void cannotCreateAppliance(const CVirtualBox &comVBox, UINotificationCenter *pParent = 0);
+ /** Notifies about inability to register machine.
+ * @param comVBox Brings common VBox object trying to register machine.
+ * @param strName Brings the name of VM being registered. */
+ static void cannotRegisterMachine(const CVirtualBox &comVBox, const QString &strName, UINotificationCenter *pParent = 0);
+ /** Notifies about inability to create machine.
+ * @param comVBox Brings common VBox object trying to create machine. */
+ static void cannotCreateMachine(const CVirtualBox &comVBox, UINotificationCenter *pParent = 0);
+ /** Notifies about inability to find machine by ID.
+ * @param comVBox Brings common VBox object trying to find machine.
+ * @param uMachineId Brings the machine ID. */
+ static void cannotFindMachineById(const CVirtualBox &comVBox,
+ const QUuid &uMachineId,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to open machine.
+ * @param comVBox Brings common VBox object trying to open machine.
+ * @param strLocation Brings the machine location. */
+ static void cannotOpenMachine(const CVirtualBox &comVBox, const QString &strLocation);
+ /** Notifies about inability to create medium storage.
+ * @param comVBox Brings common VBox object trying to create medium storage.
+ * @param strPath Brings the medium path. */
+ static void cannotCreateMediumStorage(const CVirtualBox &comVBox,
+ const QString &strPath,
+ UINotificationCenter *pParent = 0);
+ /** Notifies about inability to get ext pack manager.
+ * @param comVBox Brings common VBox object trying to open machine. */
+ static void cannotGetExtensionPackManager(const CVirtualBox &comVBox);
+
+ /** Notifies about inability to create VFS explorer.
+ * @param comAppliance Brings appliance trying to create VFS explorer. */
+ static void cannotCreateVfsExplorer(const CAppliance &comAppliance, UINotificationCenter *pParent = 0);
+ /** Notifies about inability to add disk scryption password.
+ * @param comAppliance Brings appliance trying to add disk scryption password. */
+ static void cannotAddDiskEncryptionPassword(const CAppliance &comAppliance, UINotificationCenter *pParent = 0);
+ /** Notifies about inability to interpret appliance.
+ * @param comAppliance Brings appliance we are trying to interpret. */
+ static void cannotInterpretAppliance(const CAppliance &comAppliance, UINotificationCenter *pParent = 0);
+ /** Notifies about inability to create VSD.
+ * @param comAppliance Brings appliance trying to create VSD. */
+ static void cannotCreateVirtualSystemDescription(const CAppliance &comAppliance, UINotificationCenter *pParent = 0);
+
+ /** Notifies about inability to open extension pack.
+ * @param comExtPackManager Brings extension pack manager trying to open extension pack.
+ * @param strFilename Brings extension pack file name. */
+ static void cannotOpenExtPack(const CExtPackManager &comExtPackManager, const QString &strFilename);
+ /** Notifies about inability to read extpack file.
+ * @param comExtPackFile Brings extension pack manager trying to open extension pack.
+ * @param strFilename Brings extension pack file name. */
+ static void cannotReadExtPack(const CExtPackFile &comExtPackFile, const QString &strFilename);
+
+ /** Notifies about inability to find cloud network.
+ * @param comVBox Brings common VBox object being search through.
+ * @param strNetworkName Brings network name. */
+ static void cannotFindCloudNetwork(const CVirtualBox &comVBox, const QString &strNetworkName);
+ /** Notifies about inability to find host network interface.
+ * @param comHost Brings the host being search through.
+ * @param strInterfaceName Brings interface name. */
+ static void cannotFindHostNetworkInterface(const CHost &comHost, const QString &strInterfaceName);
+ /** Notifies about inability to find host only network.
+ * @param comVBox Brings the common VBox object being search through.
+ * @param strNetworkName Brings interface name. */
+ static void cannotFindHostOnlyNetwork(const CVirtualBox &comVBox, const QString &strNetworkName);
+ /** Notifies about inability to find NAT network.
+ * @param comVBox Brings common VBox object being search through.
+ * @param strNetworkName Brings network name. */
+ static void cannotFindNATNetwork(const CVirtualBox &comVBox, const QString &strNetworkName);
+ /** Notifies about inability to create DHCP server.
+ * @param comVBox Brings common VBox object trying to create DHCP server.
+ * @param strInterfaceName Brings the interface name. */
+ static void cannotCreateDHCPServer(const CVirtualBox &comVBox, const QString &strInterfaceName);
+ /** Notifies about inability to remove DHCP server.
+ * @param comVBox Brings common VBox object trying to remove DHCP server.
+ * @param strInterfaceName Brings the interface name. */
+ static void cannotRemoveDHCPServer(const CVirtualBox &comVBox, const QString &strInterfaceName);
+ /** Notifies about inability to create cloud network.
+ * @param comVBox Brings common VBox object trying to create cloud network. */
+ static void cannotCreateCloudNetwork(const CVirtualBox &comVBox);
+ /** Notifies about inability to remove cloud network.
+ * @param comVBox Brings common VBox object trying to remove cloud network.
+ * @param strNetworkName Brings the network name. */
+ static void cannotRemoveCloudNetwork(const CVirtualBox &comVBox, const QString &strNetworkName);
+ /** Notifies about inability to create host only network.
+ * @param comVBox Brings common VBox object trying to create host only network. */
+ static void cannotCreateHostOnlyNetwork(const CVirtualBox &comVBox);
+ /** Notifies about inability to remove host only network.
+ * @param comVBox Brings common VBox object trying to remove host only network.
+ * @param strNetworkName Brings the network name. */
+ static void cannotRemoveHostOnlyNetwork(const CVirtualBox &comVBox, const QString &strNetworkName);
+ /** Notifies about inability to create NAT network.
+ * @param comVBox Brings common VBox object trying to create NAT network. */
+ static void cannotCreateNATNetwork(const CVirtualBox &comVBox);
+ /** Notifies about inability to remove NAT network.
+ * @param comVBox Brings common VBox object trying to remove NAT network.
+ * @param strNetworkName Brings the network name. */
+ static void cannotRemoveNATNetwork(const CVirtualBox &comVBox, const QString &strNetworkName);
+
+ /** Notifies about inability to create cloud profile.
+ * @param comProvider Brings the provider profile being created for. */
+ static void cannotCreateCloudProfile(const CCloudProvider &comProvider);
+ /** Notifies about inability to remove cloud profile.
+ * @param comProvider Brings the provider profile being removed from. */
+ static void cannotRemoveCloudProfile(const CCloudProfile &comProfile);
+ /** Notifies about inability to save cloud profiles.
+ * @param comProvider Brings the provider profiles being saved for. */
+ static void cannotSaveCloudProfiles(const CCloudProvider &comProvider);
+ /** Notifies about inability to import cloud profiles.
+ * @param comProvider Brings the provider profiles being imported for. */
+ static void cannotImportCloudProfiles(const CCloudProvider &comProvider);
+ /** Notifies about inability to refresh cloud machine.
+ * @param comMachine Brings the machine being refreshed. */
+ static void cannotRefreshCloudMachine(const CCloudMachine &comMachine);
+ /** Notifies about inability to refresh cloud machine.
+ * @param comProgress Brings the progress of machine being refreshed. */
+ static void cannotRefreshCloudMachine(const CProgress &comProgress);
+ /** Notifies about inability to create cloud client.
+ * @param comProfile Brings the profile client being created for. */
+ static void cannotCreateCloudClient(const CCloudProfile &comProfile, UINotificationCenter *pParent = 0);
+
+ /** Notifies about inability to open machine.
+ * @param comMedium Brings the medium being closed. */
+ static void cannotCloseMedium(const CMedium &comMedium);
+
+ /** Notifies about inability to discard saved state.
+ * @param comMachine Brings the collision VM name. */
+ static void cannotDiscardSavedState(const CMachine &comMachine);
+ /** Notifies about inability to remove machine.
+ * @param comMachine Brings machine being removed. */
+ static void cannotRemoveMachine(const CMachine &comMachine, UINotificationCenter *pParent = 0);
+ /** Notifies about inability to export appliance.
+ * @param comMachine Brings machine trying to export appliance. */
+ static void cannotExportMachine(const CMachine &comMachine, UINotificationCenter *pParent = 0);
+ /** Notifies about inability to attach device.
+ * @param comMachine Brings machine trying to attach device. */
+ static void cannotAttachDevice(const CMachine &comMachine,
+ UIMediumDeviceType enmType,
+ const QString &strLocation,
+ const StorageSlot &storageSlot,
+ UINotificationCenter *pParent = 0);
+
+ /** Notifies about inability to find snapshot by ID.
+ * @param comMachine Brings the machine being searched for particular snapshot.
+ * @param uId Brings the required snapshot ID. */
+ static void cannotFindSnapshotById(const CMachine &comMachine, const QUuid &uId);
+ /** Notifies about inability to find snapshot by name.
+ * @param comMachine Brings the machine being searched for particular snapshot.
+ * @param strName Brings the required snapshot name. */
+ static void cannotFindSnapshotByName(const CMachine &comMachine, const QString &strName, UINotificationCenter *pParent = 0);
+ /** Notifies about inability to change snapshot.
+ * @param comSnapshot Brings the snapshot being changed.
+ * @param strSnapshotName Brings snapshot name.
+ * @param strMachineName Brings machine name. */
+ static void cannotChangeSnapshot(const CSnapshot &comSnapshot, const QString &strSnapshotName, const QString &strMachineName);
+
+ /** Notifies about inability to run unattended guest install.
+ * @param comUnattended Brings the unattended being running guest install. */
+ static void cannotRunUnattendedGuestInstall(const CUnattended &comUnattended);
+ /** @} */
+
+ /** @name COM Runtime UI warnings.
+ * @{ */
+ /** Notifies about inability to attach USB device.
+ * @param comConsole Brings console USB device belongs to.
+ * @param strDevice Brings the device name. */
+ static void cannotAttachUSBDevice(const CConsole &comConsole, const QString &strDevice);
+ /** Notifies about inability to attach USB device.
+ * @param comErrorInfo Brings info about error happened.
+ * @param strDevice Brings the device name.
+ * @param strMachineName Brings the machine name. */
+ static void cannotAttachUSBDevice(const CVirtualBoxErrorInfo &comErrorInfo,
+ const QString &strDevice, const QString &strMachineName);
+ /** Notifies about inability to detach USB device.
+ * @param comConsole Brings console USB device belongs to.
+ * @param strDevice Brings the device name. */
+ static void cannotDetachUSBDevice(const CConsole &comConsole, const QString &strDevice);
+ /** Notifies about inability to detach USB device.
+ * @param comErrorInfo Brings info about error happened.
+ * @param strDevice Brings the device name.
+ * @param strMachineName Brings the machine name. */
+ static void cannotDetachUSBDevice(const CVirtualBoxErrorInfo &comErrorInfo,
+ const QString &strDevice, const QString &strMachineName);
+
+ /** Notifies about inability to attach webcam.
+ * @param comDispatcher Brings emulated USB dispatcher webcam being attached to.
+ * @param strWebCamName Brings the webcam name.
+ * @param strMachineName Brings the machine name. */
+ static void cannotAttachWebCam(const CEmulatedUSB &comDispatcher,
+ const QString &strWebCamName, const QString &strMachineName);
+ /** Notifies about inability to detach webcam.
+ * @param comDispatcher Brings emulated USB dispatcher webcam being detached from.
+ * @param strWebCamName Brings the webcam name.
+ * @param strMachineName Brings the machine name. */
+ static void cannotDetachWebCam(const CEmulatedUSB &comDispatcher,
+ const QString &strWebCamName, const QString &strMachineName);
+
+ /** Notifies about inability to save machine settings.
+ * @param comMachine Brings the machine trying to save settings. */
+ static void cannotSaveMachineSettings(const CMachine &comMachine, UINotificationCenter *pParent = 0);
+
+ /** Notifies about inability to toggle audio input.
+ * @param comAdapter Brings the adapter input being toggled for.
+ * @param strMachineName Brings the machine name.
+ * @param fEnable Brings whether adapter input is enabled or not. */
+ static void cannotToggleAudioInput(const CAudioAdapter &comAdapter,
+ const QString &strMachineName, bool fEnable);
+ /** Notifies about inability to toggle audio output.
+ * @param comAdapter Brings the adapter output being toggled for.
+ * @param strMachineName Brings the machine name.
+ * @param fEnable Brings whether adapter output is enabled or not. */
+ static void cannotToggleAudioOutput(const CAudioAdapter &comAdapter,
+ const QString &strMachineName, bool fEnable);
+
+ /** Notifies about inability to toggle network cable.
+ * @param comAdapter Brings the adapter network cable being toggled for.
+ * @param strMachineName Brings the machine name.
+ * @param fConnect Brings whether network cable is connected or not. */
+ static void cannotToggleNetworkCable(const CNetworkAdapter &comAdapter,
+ const QString &strMachineName, bool fConnect);
+
+ /** Notifies about inability to toggle recording.
+ * @param comRecording Brings the recording settings being toggled for.
+ * @param strMachineName Brings the machine name.
+ * @param fEnable Brings whether recording is enabled or not. */
+ static void cannotToggleRecording(const CRecordingSettings &comRecording, const QString &strMachineName, bool fEnable);
+
+ /** Notifies about inability to toggle VRDE server.
+ * @param comServer Brings the server being toggled.
+ * @param strMachineName Brings the machine name.
+ * @param fEnable Brings whether server is enabled or not. */
+ static void cannotToggleVRDEServer(const CVRDEServer &comServer,
+ const QString &strMachineName, bool fEnable);
+ /** @} */
+
+protected:
+
+ /** Constructs message notification-object.
+ * @param strName Brings the message name.
+ * @param strDetails Brings the message details.
+ * @param strInternalName Brings the message internal name.
+ * @param strHelpKeyword Brings the message help keyword. */
+ UINotificationMessage(const QString &strName,
+ const QString &strDetails,
+ const QString &strInternalName,
+ const QString &strHelpKeyword);
+ /** Destructs message notification-object. */
+ virtual ~UINotificationMessage() /* override final */;
+
+private:
+
+ /** Creates message.
+ * @param strName Brings the message name.
+ * @param strDetails Brings the message details.
+ * @param strInternalName Brings the message internal name.
+ * @param strHelpKeyword Brings the message help keyword.
+ * @param pParent Brings the local notification-center reference. */
+ static void createMessage(const QString &strName,
+ const QString &strDetails,
+ const QString &strInternalName = QString(),
+ const QString &strHelpKeyword = QString(),
+ UINotificationCenter *pParent = 0);
+ /** Destroys message.
+ * @param strInternalName Brings the message internal name.
+ * @param pParent Brings the local notification-center reference. */
+ static void destroyMessage(const QString &strInternalName,
+ UINotificationCenter *pParent = 0);
+
+ /** Holds the IDs of messages registered. */
+ static QMap<QString, QUuid> m_messages;
+
+ /** Holds the message name. */
+ QString m_strName;
+ /** Holds the message details. */
+ QString m_strDetails;
+ /** Holds the message internal name. */
+ QString m_strInternalName;
+};
+
+/** UINotificationProgress extension for medium create functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMediumCreate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a comMedium was created. */
+ void sigMediumCreated(const CMedium &comMedium);
+
+public:
+
+ /** Constructs medium create notification-progress.
+ * @param comTarget Brings the medium being the target.
+ * @param uSize Brings the target medium size.
+ * @param variants Brings the target medium options. */
+ UINotificationProgressMediumCreate(const CMedium &comTarget,
+ qulonglong uSize,
+ const QVector<KMediumVariant> &variants);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the medium being the target. */
+ CMedium m_comTarget;
+ /** Holds the target location. */
+ QString m_strLocation;
+ /** Holds the target medium size. */
+ qulonglong m_uSize;
+ /** Holds the target medium options. */
+ QVector<KMediumVariant> m_variants;
+};
+
+/** UINotificationProgress extension for medium copy functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMediumCopy : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a comMedium was copied. */
+ void sigMediumCopied(const CMedium &comMedium);
+
+public:
+
+ /** Constructs medium copy notification-progress.
+ * @param comSource Brings the medium being copied.
+ * @param comTarget Brings the medium being the target.
+ * @param variants Brings the target medium options. */
+ UINotificationProgressMediumCopy(const CMedium &comSource,
+ const CMedium &comTarget,
+ const QVector<KMediumVariant> &variants);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the medium being copied. */
+ CMedium m_comSource;
+ /** Holds the medium being the target. */
+ CMedium m_comTarget;
+ /** Holds the source location. */
+ QString m_strSourceLocation;
+ /** Holds the target location. */
+ QString m_strTargetLocation;
+ /** Holds the target medium options. */
+ QVector<KMediumVariant> m_variants;
+};
+
+/** UINotificationProgress extension for medium move functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMediumMove : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs medium move notification-progress.
+ * @param comMedium Brings the medium being moved.
+ * @param strLocation Brings the desired location. */
+ UINotificationProgressMediumMove(const CMedium &comMedium,
+ const QString &strLocation);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the medium being moved. */
+ CMedium m_comMedium;
+ /** Holds the initial location. */
+ QString m_strFrom;
+ /** Holds the desired location. */
+ QString m_strTo;
+};
+
+/** UINotificationProgress extension for medium resize functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMediumResize : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs medium resize notification-progress.
+ * @param comMedium Brings the medium being resized.
+ * @param uSize Brings the desired size. */
+ UINotificationProgressMediumResize(const CMedium &comMedium,
+ qulonglong uSize);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the medium being resized. */
+ CMedium m_comMedium;
+ /** Holds the initial size. */
+ qulonglong m_uFrom;
+ /** Holds the desired size. */
+ qulonglong m_uTo;
+};
+
+/** UINotificationProgress extension for deleting medium storage functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMediumDeletingStorage : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a comMedium storage was deleted. */
+ void sigMediumStorageDeleted(const CMedium &comMedium);
+
+public:
+
+ /** Constructs deleting medium storage notification-progress.
+ * @param comMedium Brings the medium which storage being deleted. */
+ UINotificationProgressMediumDeletingStorage(const CMedium &comMedium);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the medium which storage being deleted. */
+ CMedium m_comMedium;
+ /** Holds the medium location. */
+ QString m_strLocation;
+};
+
+/** UINotificationProgress extension for machine copy functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMachineCopy : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a comMachine was copied. */
+ void sigMachineCopied(const CMachine &comMachine);
+
+public:
+
+ /** Constructs machine copy notification-progress.
+ * @param comSource Brings the machine being copied.
+ * @param comTarget Brings the machine being the target.
+ * @param enmCloneMode Brings the cloning mode.
+ * @param options Brings the cloning options. */
+ UINotificationProgressMachineCopy(const CMachine &comSource,
+ const CMachine &comTarget,
+ const KCloneMode &enmCloneMode,
+ const QVector<KCloneOptions> &options);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the machine being copied. */
+ CMachine m_comSource;
+ /** Holds the machine being the target. */
+ CMachine m_comTarget;
+ /** Holds the source name. */
+ QString m_strSourceName;
+ /** Holds the target name. */
+ QString m_strTargetName;
+ /** Holds the machine cloning mode. */
+ KCloneMode m_enmCloneMode;
+ /** Holds the target machine options. */
+ QVector<KCloneOptions> m_options;
+};
+
+/** UINotificationProgress extension for machine move functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMachineMove : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs machine move notification-progress.
+ * @param uId Brings the machine id.
+ * @param strDestination Brings the move destination.
+ * @param strType Brings the moving type. */
+ UINotificationProgressMachineMove(const QUuid &uId,
+ const QString &strDestination,
+ const QString &strType);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the machine id. */
+ QUuid m_uId;
+ /** Holds the session being opened. */
+ CSession m_comSession;
+ /** Holds the machine source. */
+ QString m_strSource;
+ /** Holds the machine destination. */
+ QString m_strDestination;
+ /** Holds the moving type. */
+ QString m_strType;
+};
+
+/** UINotificationProgress extension for machine power-up functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMachinePowerUp : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs machine power-up notification-progress.
+ * @param comMachine Brings the machine being powered-up. */
+ UINotificationProgressMachinePowerUp(const CMachine &comMachine, UILaunchMode enmLaunchMode);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the machine being powered-up. */
+ CMachine m_comMachine;
+ /** Holds the launch mode. */
+ UILaunchMode m_enmLaunchMode;
+ /** Holds the session being opened. */
+ CSession m_comSession;
+ /** Holds the machine name. */
+ QString m_strName;
+};
+
+/** UINotificationProgress extension for machine save-state functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMachineSaveState : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about machine state saved.
+ * @param fSuccess Brings whether state was saved successfully. */
+ void sigMachineStateSaved(bool fSuccess);
+
+public:
+
+ /** Constructs machine save-state notification-progress.
+ * @param comMachine Brings the machine being saved. */
+ UINotificationProgressMachineSaveState(const CMachine &comMachine);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the machine being saved. */
+ CMachine m_comMachine;
+ /** Holds the session being opened. */
+ CSession m_comSession;
+ /** Holds the machine name. */
+ QString m_strName;
+};
+
+/** UINotificationProgress extension for machine power-off functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMachinePowerOff : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about machine powered off.
+ * @param fSuccess Brings whether power off sequence successfully.
+ * @param fIncludingDiscard Brings whether machine state should be discarded. */
+ void sigMachinePoweredOff(bool fSuccess, bool fIncludingDiscard);
+
+public:
+
+ /** Constructs machine power-off notification-progress.
+ * @param comMachine Brings the machine being powered off.
+ * @param comConsole Brings the console of machine being powered off.
+ * @param fIncludingDiscard Brings whether machine state should be discarded. */
+ UINotificationProgressMachinePowerOff(const CMachine &comMachine,
+ const CConsole &comConsole = CConsole(),
+ bool fIncludingDiscard = false);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the machine being powered off. */
+ CMachine m_comMachine;
+ /** Holds the console of machine being powered off. */
+ CConsole m_comConsole;
+ /** Holds whether machine state should be discarded. */
+ bool m_fIncludingDiscard;
+ /** Holds the session being opened. */
+ CSession m_comSession;
+ /** Holds the machine name. */
+ QString m_strName;
+};
+
+/** UINotificationProgress extension for machine media remove functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressMachineMediaRemove : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs machine media remove notification-progress.
+ * @param comMachine Brings the machine being removed.
+ * @param media Brings the machine media being removed. */
+ UINotificationProgressMachineMediaRemove(const CMachine &comMachine,
+ const CMediumVector &media);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the machine being removed. */
+ CMachine m_comMachine;
+ /** Holds the machine name. */
+ QString m_strName;
+ /** Holds the machine media being removed. */
+ CMediumVector m_media;
+};
+
+/** UINotificationProgress extension for VFS explorer update functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressVFSExplorerUpdate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs VFS explorer update notification-progress.
+ * @param comExplorer Brings the VFS explorer being updated. */
+ UINotificationProgressVFSExplorerUpdate(const CVFSExplorer &comExplorer);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the VFS explorer being updated. */
+ CVFSExplorer m_comExplorer;
+ /** Holds the VFS explorer path. */
+ QString m_strPath;
+};
+
+/** UINotificationProgress extension for VFS explorer files remove functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressVFSExplorerFilesRemove : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs VFS explorer files remove notification-progress.
+ * @param comExplorer Brings the VFS explorer removing the files.
+ * @param files Brings a vector of files to be removed. */
+ UINotificationProgressVFSExplorerFilesRemove(const CVFSExplorer &comExplorer,
+ const QVector<QString> &files);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the VFS explorer removing the files. */
+ CVFSExplorer m_comExplorer;
+ /** Holds a vector of files to be removed. */
+ QVector<QString> m_files;
+ /** Holds the VFS explorer path. */
+ QString m_strPath;
+};
+
+/** UINotificationProgress extension for subnet selection VSD form create functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressSubnetSelectionVSDFormCreate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about VSD @a comForm created.
+ * @param comForm Brings created VSD form. */
+ void sigVSDFormCreated(const CVirtualSystemDescriptionForm &comForm);
+
+public:
+
+ /** Constructs subnet selection VSD form create notification-progress.
+ * @param comClient Brings the cloud client being creating VSD form.
+ * @param comVsd Brings the VSD, form being created for.
+ * @param strProviderShortName Brings the short provider name.
+ * @param strProfileName Brings the profile name. */
+ UINotificationProgressSubnetSelectionVSDFormCreate(const CCloudClient &comClient,
+ const CVirtualSystemDescription &comVSD,
+ const QString &strProviderShortName,
+ const QString &strProfileName);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being creating VSD form. */
+ CCloudClient m_comClient;
+ /** Holds the VSD, form being created for. */
+ CVirtualSystemDescription m_comVSD;
+ /** Holds the VSD form being created. */
+ CVirtualSystemDescriptionForm m_comVSDForm;
+ /** Holds the short provider name. */
+ QString m_strProviderShortName;
+ /** Holds the profile name. */
+ QString m_strProfileName;
+};
+
+/** UINotificationProgress extension for launch VSD form create functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressLaunchVSDFormCreate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about VSD @a comForm created.
+ * @param comForm Brings created VSD form. */
+ void sigVSDFormCreated(const CVirtualSystemDescriptionForm &comForm);
+
+public:
+
+ /** Constructs launch VSD form create notification-progress.
+ * @param comClient Brings the cloud client being creating VSD form.
+ * @param comVsd Brings the VSD, form being created for.
+ * @param strProviderShortName Brings the short provider name.
+ * @param strProfileName Brings the profile name. */
+ UINotificationProgressLaunchVSDFormCreate(const CCloudClient &comClient,
+ const CVirtualSystemDescription &comVSD,
+ const QString &strProviderShortName,
+ const QString &strProfileName);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being creating VSD form. */
+ CCloudClient m_comClient;
+ /** Holds the VSD, form being created for. */
+ CVirtualSystemDescription m_comVSD;
+ /** Holds the VSD form being created. */
+ CVirtualSystemDescriptionForm m_comVSDForm;
+ /** Holds the short provider name. */
+ QString m_strProviderShortName;
+ /** Holds the profile name. */
+ QString m_strProfileName;
+};
+
+/** UINotificationProgress extension for export VSD form create functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressExportVSDFormCreate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about VSD @a comForm created.
+ * @param form Brings created VSD form. */
+ void sigVSDFormCreated(const QVariant &form);
+
+public:
+
+ /** Constructs export VSD form create notification-progress.
+ * @param comClient Brings the cloud client being creating VSD form.
+ * @param comVsd Brings the VSD, form being created for. */
+ UINotificationProgressExportVSDFormCreate(const CCloudClient &comClient,
+ const CVirtualSystemDescription &comVSD);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being creating VSD form. */
+ CCloudClient m_comClient;
+ /** Holds the VSD, form being created for. */
+ CVirtualSystemDescription m_comVSD;
+ /** Holds the VSD form being created. */
+ CVirtualSystemDescriptionForm m_comVSDForm;
+};
+
+/** UINotificationProgress extension for import VSD form create functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressImportVSDFormCreate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about VSD @a comForm created.
+ * @param form Brings created VSD form. */
+ void sigVSDFormCreated(const QVariant &form);
+
+public:
+
+ /** Constructs import VSD form create notification-progress.
+ * @param comClient Brings the cloud client being creating VSD form.
+ * @param comVsd Brings the VSD, form being created for. */
+ UINotificationProgressImportVSDFormCreate(const CCloudClient &comClient,
+ const CVirtualSystemDescription &comVSD);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being creating VSD form. */
+ CCloudClient m_comClient;
+ /** Holds the VSD, form being created for. */
+ CVirtualSystemDescription m_comVSD;
+ /** Holds the VSD form being created. */
+ CVirtualSystemDescriptionForm m_comVSDForm;
+};
+
+/** UINotificationProgress extension for cloud image list functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudImageList : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about image @a names received. */
+ void sigImageNamesReceived(const QVariant &names);
+ /** Notifies listeners about image @a ids received. */
+ void sigImageIdsReceived(const QVariant &ids);
+
+public:
+
+ /** Constructs cloud images list notification-progress.
+ * @param comClient Brings the cloud client being listing images.
+ * @param cloudImageStates Brings the image states we are interested in. */
+ UINotificationProgressCloudImageList(const CCloudClient &comClient,
+ const QVector<KCloudImageState> &cloudImageStates);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being listing images. */
+ CCloudClient m_comClient;
+ /** Holds the image states we are interested in. */
+ QVector<KCloudImageState> m_cloudImageStates;
+ /** Holds the listed names. */
+ CStringArray m_comNames;
+ /** Holds the listed ids. */
+ CStringArray m_comIds;
+};
+
+/** UINotificationProgress extension for cloud source boot volume list functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudSourceBootVolumeList : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about source boot volume @a names received. */
+ void sigImageNamesReceived(const QVariant &names);
+ /** Notifies listeners about source boot volume @a ids received. */
+ void sigImageIdsReceived(const QVariant &ids);
+
+public:
+
+ /** Constructs cloud source boot volumes list notification-progress.
+ * @param comClient Brings the cloud client being listing source boot volumes. */
+ UINotificationProgressCloudSourceBootVolumeList(const CCloudClient &comClient);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being listing source boot volumes. */
+ CCloudClient m_comClient;
+ /** Holds the listed names. */
+ CStringArray m_comNames;
+ /** Holds the listed ids. */
+ CStringArray m_comIds;
+};
+
+/** UINotificationProgress extension for cloud instance list functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudInstanceList : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about instance @a names received. */
+ void sigImageNamesReceived(const QVariant &names);
+ /** Notifies listeners about instance @a ids received. */
+ void sigImageIdsReceived(const QVariant &ids);
+
+public:
+
+ /** Constructs cloud instances list notification-progress.
+ * @param comClient Brings the cloud client being listing instances. */
+ UINotificationProgressCloudInstanceList(const CCloudClient &comClient);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being listing instances. */
+ CCloudClient m_comClient;
+ /** Holds the listed names. */
+ CStringArray m_comNames;
+ /** Holds the listed ids. */
+ CStringArray m_comIds;
+};
+
+/** UINotificationProgress extension for cloud source instance list functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudSourceInstanceList : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about source instance @a names received. */
+ void sigImageNamesReceived(const QVariant &names);
+ /** Notifies listeners about source instance @a ids received. */
+ void sigImageIdsReceived(const QVariant &ids);
+
+public:
+
+ /** Constructs cloud source instances list notification-progress.
+ * @param comClient Brings the cloud client being listing source instances. */
+ UINotificationProgressCloudSourceInstanceList(const CCloudClient &comClient);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being listing source instances. */
+ CCloudClient m_comClient;
+ /** Holds the listed names. */
+ CStringArray m_comNames;
+ /** Holds the listed ids. */
+ CStringArray m_comIds;
+};
+
+/** UINotificationProgress extension for cloud machine add functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudMachineAdd : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about cloud @a comMachine was added.
+ * @param strProviderShortName Brings the short provider name.
+ * @param strProfileName Brings the profile name. */
+ void sigCloudMachineAdded(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine);
+
+public:
+
+ /** Constructs cloud machine add notification-progress.
+ * @param comClient Brings the cloud client being adding machine.
+ * @param comMachine Brings the cloud machine being added.
+ * @param strInstanceName Brings the instance name.
+ * @param strProviderShortName Brings the short provider name.
+ * @param strProfileName Brings the profile name. */
+ UINotificationProgressCloudMachineAdd(const CCloudClient &comClient,
+ const CCloudMachine &comMachine,
+ const QString &strInstanceName,
+ const QString &strProviderShortName,
+ const QString &strProfileName);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being adding machine. */
+ CCloudClient m_comClient;
+ /** Holds the cloud machine being added. */
+ CCloudMachine m_comMachine;
+ /** Holds the instance name. */
+ QString m_strInstanceName;
+ /** Holds the short provider name. */
+ QString m_strProviderShortName;
+ /** Holds the profile name. */
+ QString m_strProfileName;
+};
+
+/** UINotificationProgress extension for cloud machine create functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudMachineCreate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about cloud @a comMachine was created.
+ * @param strProviderShortName Brings the short provider name.
+ * @param strProfileName Brings the profile name. */
+ void sigCloudMachineCreated(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const CCloudMachine &comMachine);
+
+public:
+
+ /** Constructs cloud machine create notification-progress.
+ * @param comClient Brings the cloud client being adding machine.
+ * @param comMachine Brings the cloud machine being added.
+ * @param comVSD Brings the virtual system description.
+ * @param strProviderShortName Brings the short provider name.
+ * @param strProfileName Brings the profile name. */
+ UINotificationProgressCloudMachineCreate(const CCloudClient &comClient,
+ const CCloudMachine &comMachine,
+ const CVirtualSystemDescription &comVSD,
+ const QString &strProviderShortName,
+ const QString &strProfileName);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud client being adding machine. */
+ CCloudClient m_comClient;
+ /** Holds the cloud machine being added. */
+ CCloudMachine m_comMachine;
+ /** Holds the the virtual system description. */
+ CVirtualSystemDescription m_comVSD;
+ /** Holds the cloud machine name. */
+ QString m_strName;
+ /** Holds the short provider name. */
+ QString m_strProviderShortName;
+ /** Holds the profile name. */
+ QString m_strProfileName;
+};
+
+/** UINotificationProgress extension for cloud machine remove functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudMachineRemove : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about cloud machine was removed.
+ * @param strProviderShortName Brings the short provider name.
+ * @param strProfileName Brings the profile name.
+ * @param strName Brings the machine name. */
+ void sigCloudMachineRemoved(const QString &strProviderShortName,
+ const QString &strProfileName,
+ const QString &strName);
+
+public:
+
+ /** Constructs cloud machine remove notification-progress.
+ * @param comMachine Brings the cloud machine being removed.
+ * @param fFullRemoval Brings whether cloud machine should be removed fully. */
+ UINotificationProgressCloudMachineRemove(const CCloudMachine &comMachine,
+ bool fFullRemoval,
+ const QString &strProviderShortName,
+ const QString &strProfileName);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud machine being removed. */
+ CCloudMachine m_comMachine;
+ /** Holds the cloud machine name. */
+ QString m_strName;
+ /** Holds whether cloud machine should be removed fully. */
+ bool m_fFullRemoval;
+ /** Holds the short provider name. */
+ QString m_strProviderShortName;
+ /** Holds the profile name. */
+ QString m_strProfileName;
+};
+
+/** UINotificationProgress extension for cloud machine power-up functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudMachinePowerUp : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs cloud machine power-up notification-progress.
+ * @param comMachine Brings the machine being powered-up. */
+ UINotificationProgressCloudMachinePowerUp(const CCloudMachine &comMachine);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the machine being powered-up. */
+ CCloudMachine m_comMachine;
+ /** Holds the machine name. */
+ QString m_strName;
+};
+
+/** UINotificationProgress extension for cloud machine power-off functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudMachinePowerOff : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs cloud machine power-off notification-progress.
+ * @param comMachine Brings the machine being powered-off. */
+ UINotificationProgressCloudMachinePowerOff(const CCloudMachine &comMachine);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the machine being powered-off. */
+ CCloudMachine m_comMachine;
+ /** Holds the machine name. */
+ QString m_strName;
+};
+
+/** UINotificationProgress extension for cloud machine shutdown functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudMachineShutdown : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs cloud machine shutdown notification-progress.
+ * @param comMachine Brings the machine being shutdown. */
+ UINotificationProgressCloudMachineShutdown(const CCloudMachine &comMachine);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the machine being shutdown. */
+ CCloudMachine m_comMachine;
+ /** Holds the machine name. */
+ QString m_strName;
+};
+
+/** UINotificationProgress extension for cloud machine terminate functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudMachineTerminate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs cloud machine terminate notification-progress.
+ * @param comMachine Brings the machine being terminate. */
+ UINotificationProgressCloudMachineTerminate(const CCloudMachine &comMachine);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the machine being terminated. */
+ CCloudMachine m_comMachine;
+ /** Holds the machine name. */
+ QString m_strName;
+};
+
+/** UINotificationProgress extension for cloud machine settings form create functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudMachineSettingsFormCreate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about settings @a comForm created.
+ * @param form Brings created VSD form. */
+ void sigSettingsFormCreated(const QVariant &form);
+
+public:
+
+ /** Constructs cloud machine settings form create notification-progress.
+ * @param comMachine Brings the machine form being created for.
+ * @param strMachineName Brings the machine name. */
+ UINotificationProgressCloudMachineSettingsFormCreate(const CCloudMachine &comMachine,
+ const QString &strMachineName);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the machine form being created for. */
+ CCloudMachine m_comMachine;
+ /** Holds the machine name. */
+ QString m_strMachineName;
+ /** Holds the form being created. */
+ CForm m_comForm;
+};
+
+/** UINotificationProgress extension for cloud machine settings form apply functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudMachineSettingsFormApply : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs cloud machine settings form apply notification-progress.
+ * @param comForm Brings the form being applied.
+ * @param strMachineName Brings the machine name. */
+ UINotificationProgressCloudMachineSettingsFormApply(const CForm &comForm,
+ const QString &strMachineName);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the machine form being created for. */
+ CForm m_comForm;
+ /** Holds the machine name. */
+ QString m_strMachineName;
+};
+
+/** UINotificationProgress extension for cloud console connection create functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudConsoleConnectionCreate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs cloud console connection create notification-progress.
+ * @param comMachine Brings the cloud machine for which console connection being created.
+ * @param strPublicKey Brings the public key used for console connection being created. */
+ UINotificationProgressCloudConsoleConnectionCreate(const CCloudMachine &comMachine,
+ const QString &strPublicKey);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the cloud machine for which console connection being created. */
+ CCloudMachine m_comMachine;
+ /** Holds the cloud machine name. */
+ QString m_strName;
+ /** Holds the public key used for console connection being created. */
+ QString m_strPublicKey;
+};
+
+/** UINotificationProgress extension for cloud console connection delete functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudConsoleConnectionDelete : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs cloud console connection delete notification-progress.
+ * @param comMachine Brings the cloud machine for which console connection being deleted. */
+ UINotificationProgressCloudConsoleConnectionDelete(const CCloudMachine &comMachine);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the cloud machine for which console connection being deleted. */
+ CCloudMachine m_comMachine;
+ /** Holds the cloud machine name. */
+ QString m_strName;
+};
+
+/** UINotificationProgress extension for cloud console log acquire functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressCloudConsoleLogAcquire : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about console @a strLog for cloud VM with @a strName read. */
+ void sigLogRead(const QString &strName, const QString &strLog);
+
+public:
+
+ /** Constructs cloud console log acquire notification-progress.
+ * @param comMachine Brings the cloud machine for which console log being acquired. */
+ UINotificationProgressCloudConsoleLogAcquire(const CCloudMachine &comMachine);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the cloud machine for which console log being acquired. */
+ CCloudMachine m_comMachine;
+ /** Holds the cloud machine name. */
+ QString m_strName;
+ /** Holds the stream log being read to. */
+ CDataStream m_comStream;
+};
+
+/** UINotificationProgress extension for snapshot take functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressSnapshotTake : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about snapshot with @a id taken. */
+ void sigSnapshotTaken(const QVariant &id);
+
+public:
+
+ /** Constructs snapshot take notification-progress.
+ * @param comMachine Brings the machine we are taking snapshot for.
+ * @param strSnapshotName Brings the name of snapshot being taken.
+ * @param strSnapshotDescription Brings the description of snapshot being taken. */
+ UINotificationProgressSnapshotTake(const CMachine &comMachine,
+ const QString &strSnapshotName,
+ const QString &strSnapshotDescription);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the machine we are taking snapshot for. */
+ CMachine m_comMachine;
+ /** Holds the name of snapshot being taken. */
+ QString m_strSnapshotName;
+ /** Holds the description of snapshot being taken. */
+ QString m_strSnapshotDescription;
+ /** Holds the machine name. */
+ QString m_strMachineName;
+ /** Holds the session being opened. */
+ CSession m_comSession;
+ /** Holds the taken snapshot id. */
+ QUuid m_uSnapshotId;
+};
+
+/** UINotificationProgress extension for snapshot restore functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressSnapshotRestore : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about snapshot restored.
+ * @param fSuccess Brings whether snapshot restored successfully. */
+ void sigSnapshotRestored(bool fSuccess);
+
+public:
+
+ /** Constructs snapshot restore notification-progress.
+ * @param uMachineId Brings the ID of machine we are restoring snapshot for.
+ * @param comSnapshot Brings the snapshot being restored. */
+ UINotificationProgressSnapshotRestore(const QUuid &uMachineId,
+ const CSnapshot &comSnapshot = CSnapshot());
+ /** Constructs snapshot restore notification-progress.
+ * @param comMachine Brings the machine we are restoring snapshot for.
+ * @param comSnapshot Brings the snapshot being restored. */
+ UINotificationProgressSnapshotRestore(const CMachine &comMachine,
+ const CSnapshot &comSnapshot = CSnapshot());
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the ID of machine we are restoring snapshot for. */
+ QUuid m_uMachineId;
+ /** Holds the machine we are restoring snapshot for. */
+ CMachine m_comMachine;
+ /** Holds the snapshot being restored. */
+ CSnapshot m_comSnapshot;
+ /** Holds the machine name. */
+ QString m_strMachineName;
+ /** Holds the snapshot name. */
+ QString m_strSnapshotName;
+ /** Holds the session being opened. */
+ CSession m_comSession;
+};
+
+/** UINotificationProgress extension for snapshot delete functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressSnapshotDelete : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs snapshot delete notification-progress.
+ * @param comMachine Brings the machine we are deleting snapshot from.
+ * @param uSnapshotId Brings the ID of snapshot being deleted. */
+ UINotificationProgressSnapshotDelete(const CMachine &comMachine,
+ const QUuid &uSnapshotId);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the machine we are deleting snapshot from. */
+ CMachine m_comMachine;
+ /** Holds the ID of snapshot being deleted. */
+ QUuid m_uSnapshotId;
+ /** Holds the machine name. */
+ QString m_strMachineName;
+ /** Holds the snapshot name. */
+ QString m_strSnapshotName;
+ /** Holds the session being opened. */
+ CSession m_comSession;
+};
+
+/** UINotificationProgress extension for appliance write functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressApplianceWrite : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs appliance write notification-progress.
+ * @param comAppliance Brings the appliance being written.
+ * @param strFormat Brings the appliance format.
+ * @param options Brings the export options to be taken into account.
+ * @param strPath Brings the appliance path. */
+ UINotificationProgressApplianceWrite(const CAppliance &comAppliance,
+ const QString &strFormat,
+ const QVector<KExportOptions> &options,
+ const QString &strPath);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the appliance being written. */
+ CAppliance m_comAppliance;
+ /** Holds the appliance format. */
+ QString m_strFormat;
+ /** Holds the export options to be taken into account. */
+ QVector<KExportOptions> m_options;
+ /** Holds the appliance path. */
+ QString m_strPath;
+};
+
+/** UINotificationProgress extension for appliance read functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressApplianceRead : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs appliance read notification-progress.
+ * @param comAppliance Brings the appliance being read.
+ * @param strPath Brings the appliance path. */
+ UINotificationProgressApplianceRead(const CAppliance &comAppliance,
+ const QString &strPath);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the appliance being read. */
+ CAppliance m_comAppliance;
+ /** Holds the appliance path. */
+ QString m_strPath;
+};
+
+/** UINotificationProgress extension for import appliance functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressApplianceImport : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs import appliance notification-progress.
+ * @param comAppliance Brings the appliance being imported.
+ * @param options Brings the import options to be taken into account. */
+ UINotificationProgressApplianceImport(const CAppliance &comAppliance,
+ const QVector<KImportOptions> &options);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Holds the appliance being imported. */
+ CAppliance m_comAppliance;
+ /** Holds the import options to be taken into account. */
+ QVector<KImportOptions> m_options;
+};
+
+/** UINotificationProgress extension for extension pack install functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressExtensionPackInstall : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about extension pack installed.
+ * @param strExtensionPackName Brings extension pack name. */
+ void sigExtensionPackInstalled(const QString &strExtensionPackName);
+
+public:
+
+ /** Constructs extension pack install notification-progress.
+ * @param comExtPackFile Brings the extension pack file to install.
+ * @param fReplace Brings whether extension pack should be replaced.
+ * @param strExtensionPackName Brings the extension pack name.
+ * @param strDisplayInfo Brings the display info. */
+ UINotificationProgressExtensionPackInstall(const CExtPackFile &comExtPackFile,
+ bool fReplace,
+ const QString &strExtensionPackName,
+ const QString &strDisplayInfo);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the extension pack file to install. */
+ CExtPackFile m_comExtPackFile;
+ /** Holds whether extension pack should be replaced. */
+ bool m_fReplace;
+ /** Holds the extension pack name. */
+ QString m_strExtensionPackName;
+ /** Holds the display info. */
+ QString m_strDisplayInfo;
+};
+
+/** UINotificationProgress extension for extension pack uninstall functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressExtensionPackUninstall : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about extension pack uninstalled.
+ * @param strExtensionPackName Brings extension pack name. */
+ void sigExtensionPackUninstalled(const QString &strExtensionPackName);
+
+public:
+
+ /** Constructs extension pack uninstall notification-progress.
+ * @param comExtPackManager Brings the extension pack manager.
+ * @param strExtensionPackName Brings the extension pack name.
+ * @param strDisplayInfo Brings the display info. */
+ UINotificationProgressExtensionPackUninstall(const CExtPackManager &comExtPackManager,
+ const QString &strExtensionPackName,
+ const QString &strDisplayInfo);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the extension pack manager. */
+ CExtPackManager m_comExtPackManager;
+ /** Holds the extension pack name. */
+ QString m_strExtensionPackName;
+ /** Holds the display info. */
+ QString m_strDisplayInfo;
+};
+
+/** UINotificationProgress extension for guest additions install functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressGuestAdditionsInstall : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about guest additions installation failed.
+ * @param strSource Brings the guest additions file path. */
+ void sigGuestAdditionsInstallationFailed(const QString &strSource);
+
+public:
+
+ /** Constructs guest additions install notification-progress.
+ * @param comGuest Brings the guest additions being installed to.
+ * @param strSource Brings the guest additions file path. */
+ UINotificationProgressGuestAdditionsInstall(const CGuest &comGuest,
+ const QString &strSource);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the guest additions being installed to. */
+ CGuest m_comGuest;
+ /** Holds the guest additions file path. */
+ QString m_strSource;
+};
+
+/** UINotificationProgress extension for host-only network interface create functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressHostOnlyNetworkInterfaceCreate : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about host-only network interface created.
+ * @param comInterface Brings network interface created. */
+ void sigHostOnlyNetworkInterfaceCreated(const CHostNetworkInterface &comInterface);
+
+public:
+
+ /** Constructs host-only network interface create notification-progress.
+ * @param comHost Brings the host network interface being created for.
+ * @param comInterface Brings the network interface being created. */
+ UINotificationProgressHostOnlyNetworkInterfaceCreate(const CHost &comHost,
+ const CHostNetworkInterface &comInterface);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the host network interface being created for. */
+ CHost m_comHost;
+ /** Holds the network interface being created. */
+ CHostNetworkInterface m_comInterface;
+};
+
+/** UINotificationProgress extension for host-only network interface remove functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressHostOnlyNetworkInterfaceRemove : public UINotificationProgress
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about host-only network interface removed.
+ * @param strInterfaceName Brings the name of network interface removed. */
+ void sigHostOnlyNetworkInterfaceRemoved(const QString &strInterfaceName);
+
+public:
+
+ /** Constructs host-only network interface remove notification-progress.
+ * @param comHost Brings the host network interface being removed for.
+ * @param uInterfaceId Brings the ID of network interface being removed. */
+ UINotificationProgressHostOnlyNetworkInterfaceRemove(const CHost &comHost,
+ const QUuid &uInterfaceId);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds the host network interface being removed for. */
+ CHost m_comHost;
+ /** Holds the ID of network interface being removed. */
+ QUuid m_uInterfaceId;
+ /** Holds the network interface name. */
+ QString m_strInterfaceName;
+};
+
+/** UINotificationProgress extension for virtual system description form value set functionality. */
+class SHARED_LIBRARY_STUFF UINotificationProgressVsdFormValueSet : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs virtual system description form value set notification-progress.
+ * @param comValue Brings our value being set.
+ * @param fBool Brings the value our value being set to. */
+ UINotificationProgressVsdFormValueSet(const CBooleanFormValue &comValue, bool fBool);
+
+ /** Constructs virtual system description form value set notification-progress.
+ * @param comValue Brings our value being set.
+ * @param strString Brings the value our value being set to. */
+ UINotificationProgressVsdFormValueSet(const CStringFormValue &comValue, const QString &strString);
+
+ /** Constructs virtual system description form value set notification-progress.
+ * @param comValue Brings our value being set.
+ * @param iChoice Brings the value our value being set to. */
+ UINotificationProgressVsdFormValueSet(const CChoiceFormValue &comValue, int iChoice);
+
+ /** Constructs virtual system description form value set notification-progress.
+ * @param comValue Brings our value being set.
+ * @param iInteger Brings the value our value being set to. */
+ UINotificationProgressVsdFormValueSet(const CRangedIntegerFormValue &comValue, int iInteger);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private:
+
+ /** Value type. */
+ KFormValueType m_enmType;
+
+ /** Holds our value being set. */
+ CFormValue m_comValue;
+
+ /** Holds the bool value. */
+ bool m_fBool;
+ /** Holds the string value. */
+ QString m_strString;
+ /** Holds the choice value. */
+ int m_iChoice;
+ /** Holds the integer value. */
+ int m_iInteger;
+};
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+/** UINotificationDownloader extension for extension pack downloading functionality. */
+class SHARED_LIBRARY_STUFF UINotificationDownloaderExtensionPack : public UINotificationDownloader
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about extension pack downloaded.
+ * @param strSource Brings the EP source.
+ * @param strTarget Brings the EP target.
+ * @param strDigest Brings the EP digest. */
+ void sigExtensionPackDownloaded(const QString &strSource,
+ const QString &strTarget,
+ const QString &strDigest);
+
+public:
+
+ /** Returns singleton instance, creates if necessary.
+ * @param strPackName Brings the package name. */
+ static UINotificationDownloaderExtensionPack *instance(const QString &strPackName);
+ /** Returns whether singleton instance already created. */
+ static bool exists();
+
+ /** Destructs extension pack downloading notification-downloader.
+ * @note Notification-center can destroy us at any time. */
+ virtual ~UINotificationDownloaderExtensionPack() /* override final */;
+
+protected:
+
+ /** Constructs extension pack downloading notification-downloader.
+ * @param strPackName Brings the package name. */
+ UINotificationDownloaderExtensionPack(const QString &strPackName);
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started downloader. */
+ virtual UIDownloader *createDownloader() /* override final */;
+
+private:
+
+ /** Holds the singleton instance. */
+ static UINotificationDownloaderExtensionPack *s_pInstance;
+
+ /** Holds the name of pack being dowloaded. */
+ QString m_strPackName;
+};
+
+/** UINotificationDownloader extension for guest additions downloading functionality. */
+class SHARED_LIBRARY_STUFF UINotificationDownloaderGuestAdditions : public UINotificationDownloader
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about guest additions downloaded.
+ * @param strLocation Brings the UM location. */
+ void sigGuestAdditionsDownloaded(const QString &strLocation);
+
+public:
+
+ /** Returns singleton instance, creates if necessary.
+ * @param strFileName Brings the file name. */
+ static UINotificationDownloaderGuestAdditions *instance(const QString &strFileName);
+ /** Returns whether singleton instance already created. */
+ static bool exists();
+
+ /** Destructs guest additions downloading notification-downloader.
+ * @note Notification-center can destroy us at any time. */
+ virtual ~UINotificationDownloaderGuestAdditions() /* override final */;
+
+protected:
+
+ /** Constructs guest additions downloading notification-downloader.
+ * @param strFileName Brings the file name. */
+ UINotificationDownloaderGuestAdditions(const QString &strFileName);
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started downloader. */
+ virtual UIDownloader *createDownloader() /* override final */;
+
+private:
+
+ /** Holds the singleton instance. */
+ static UINotificationDownloaderGuestAdditions *s_pInstance;
+
+ /** Holds the name of file being dowloaded. */
+ QString m_strFileName;
+};
+
+/** UINotificationDownloader extension for user manual downloading functionality. */
+class SHARED_LIBRARY_STUFF UINotificationDownloaderUserManual : public UINotificationDownloader
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about user manual downloaded.
+ * @param strLocation Brings the UM location. */
+ void sigUserManualDownloaded(const QString &strLocation);
+
+public:
+
+ /** Returns singleton instance, creates if necessary.
+ * @param strFileName Brings the file name. */
+ static UINotificationDownloaderUserManual *instance(const QString &strFileName);
+ /** Returns whether singleton instance already created. */
+ static bool exists();
+
+ /** Destructs user manual downloading notification-downloader.
+ * @note Notification-center can destroy us at any time. */
+ virtual ~UINotificationDownloaderUserManual() /* override final */;
+
+protected:
+
+ /** Constructs user manual downloading notification-downloader.
+ * @param strFileName Brings the file name. */
+ UINotificationDownloaderUserManual(const QString &strFileName);
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started downloader. */
+ virtual UIDownloader *createDownloader() /* override final */;
+
+private:
+
+ /** Holds the singleton instance. */
+ static UINotificationDownloaderUserManual *s_pInstance;
+
+ /** Holds the name of file being dowloaded. */
+ QString m_strFileName;
+};
+
+/** UINotificationProgress extension for checking a new VirtualBox version. */
+class SHARED_LIBRARY_STUFF UINotificationProgressNewVersionChecker : public UINotificationProgress
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs new version check notification-progress.
+ * @param fForcedCall Brings whether even negative result should be reflected. */
+ UINotificationProgressNewVersionChecker(bool fForcedCall);
+
+protected:
+
+ /** Returns object name. */
+ virtual QString name() const /* override final */;
+ /** Returns object details. */
+ virtual QString details() const /* override final */;
+ /** Creates and returns started progress-wrapper. */
+ virtual CProgress createProgress(COMResult &comResult) /* override final */;
+
+private slots:
+
+ /** Handles signal about progress being finished. */
+ void sltHandleProgressFinished();
+
+private:
+
+ /** Holds whether this customer has forced privelegies. */
+ bool m_fForcedCall;
+# ifdef VBOX_WITH_UPDATE_AGENT
+ /** Holds the host update agent reference. */
+ CUpdateAgent m_comUpdateHost;
+# endif
+};
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+
+#endif /* !FEQT_INCLUDED_SRC_notificationcenter_UINotificationObjects_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationProgressTask.cpp b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationProgressTask.cpp
new file mode 100644
index 00000000..f76b304b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationProgressTask.cpp
@@ -0,0 +1,64 @@
+/* $Id: UINotificationProgressTask.cpp $ */
+/** @file
+ * VBox Qt GUI - UINotificationProgressTask class implementation.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIErrorString.h"
+#include "UINotificationObject.h"
+#include "UINotificationProgressTask.h"
+
+
+UINotificationProgressTask::UINotificationProgressTask(UINotificationProgress *pParent)
+ : UIProgressTask(pParent)
+ , m_pParent(pParent)
+{
+}
+
+QString UINotificationProgressTask::errorMessage() const
+{
+ return m_strErrorMessage;
+}
+
+CProgress UINotificationProgressTask::createProgress()
+{
+ /* Call to sub-class to create progress-wrapper: */
+ COMResult comResult;
+ CProgress comProgress = m_pParent->createProgress(comResult);
+ if (!comResult.isOk())
+ {
+ m_strErrorMessage = UIErrorString::formatErrorInfo(comResult);
+ return CProgress();
+ }
+ /* Return progress-wrapper: */
+ return comProgress;
+}
+
+void UINotificationProgressTask::handleProgressFinished(CProgress &comProgress)
+{
+ /* Handle progress-wrapper errors: */
+ if (comProgress.isNotNull() && !comProgress.GetCanceled() && (!comProgress.isOk() || comProgress.GetResultCode() != 0))
+ m_strErrorMessage = UIErrorString::formatErrorInfo(comProgress);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationProgressTask.h b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationProgressTask.h
new file mode 100644
index 00000000..0fbd9099
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/notificationcenter/UINotificationProgressTask.h
@@ -0,0 +1,76 @@
+/* $Id: UINotificationProgressTask.h $ */
+/** @file
+ * VBox Qt GUI - UINotificationProgressTask class declaration.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_notificationcenter_UINotificationProgressTask_h
+#define FEQT_INCLUDED_SRC_notificationcenter_UINotificationProgressTask_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIProgressTask.h"
+
+/* Forward declarations: */
+class UINotificationProgress;
+
+/** UIProgressTask extension for notification-center needs executed the silent way.
+ * That means no modal messages will arise, you'll be able to retreive error information via API
+ * provided. createProgress() and handleProgressFinished() being reloaded to handle everythig
+ * silently, you just need to implement UINotificationProgress::createProgress() instead. */
+class SHARED_LIBRARY_STUFF UINotificationProgressTask : public UIProgressTask
+{
+ Q_OBJECT;
+
+public:
+
+ /** Creates notification progress task passing @a pParent to the base-class.
+ * @param pParent Brings the notification progress this task belongs to. */
+ UINotificationProgressTask(UINotificationProgress *pParent);
+
+ /** Returns error message. */
+ QString errorMessage() const;
+
+protected:
+
+ /** Creates and returns started progress-wrapper required to init UIProgressObject.
+ * @note You don't need to reload it, it uses pParent's createProgress()
+ * which should be reloaded in your pParent sub-class. */
+ virtual CProgress createProgress() /* override final */;
+ /** Handles finished @a comProgress wrapper.
+ * @note You don't need to reload it. */
+ virtual void handleProgressFinished(CProgress &comProgress) /* override final */;
+
+private:
+
+ /** Holds the notification progress this task belongs to. */
+ UINotificationProgress *m_pParent;
+
+ /** Holds the error message. */
+ QString m_strErrorMessage;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_notificationcenter_UINotificationProgressTask_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/objects/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/objects/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/objects/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/objects/UIExecutionQueue.cpp b/src/VBox/Frontends/VirtualBox/src/objects/UIExecutionQueue.cpp
new file mode 100644
index 00000000..945a37a8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/objects/UIExecutionQueue.cpp
@@ -0,0 +1,89 @@
+/* $Id: UIExecutionQueue.cpp $ */
+/** @file
+ * VBox Qt GUI - UIExecutionQueue class implementation.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIExecutionQueue.h"
+
+
+/*********************************************************************************************************************************
+* Class UIExecutionStep implementation. *
+*********************************************************************************************************************************/
+
+UIExecutionStep::UIExecutionStep()
+{
+}
+
+
+/*********************************************************************************************************************************
+* Class UIExecutionQueue implementation. *
+*********************************************************************************************************************************/
+
+UIExecutionQueue::UIExecutionQueue(QObject *pParent /* = 0 */)
+ : QObject(pParent)
+ , m_pExecutedStep(0)
+{
+ /* Listen for the queue start signal: */
+ connect(this, &UIExecutionQueue::sigStartQueue,
+ this, &UIExecutionQueue::sltStartsSubsequentStep,
+ Qt::QueuedConnection);
+}
+
+UIExecutionQueue::~UIExecutionQueue()
+{
+ /* Cleanup current step: */
+ delete m_pExecutedStep;
+ m_pExecutedStep = 0;
+
+ /* Dequeue steps one-by-one: */
+ while (!m_queue.isEmpty())
+ delete m_queue.dequeue();
+}
+
+void UIExecutionQueue::enqueue(UIExecutionStep *pUpdateStep)
+{
+ m_queue.enqueue(pUpdateStep);
+}
+
+void UIExecutionQueue::sltStartsSubsequentStep()
+{
+ /* Cleanup current step: */
+ delete m_pExecutedStep;
+ m_pExecutedStep = 0;
+
+ /* If queue is empty, we are finished: */
+ if (m_queue.isEmpty())
+ emit sigQueueFinished();
+ else
+ {
+ /* Otherwise dequeue first step and start it: */
+ m_pExecutedStep = m_queue.dequeue();
+ connect(m_pExecutedStep, &UIExecutionStep::sigStepFinished,
+ this, &UIExecutionQueue::sltStartsSubsequentStep,
+ Qt::QueuedConnection);
+ m_pExecutedStep->exec();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/objects/UIExecutionQueue.h b/src/VBox/Frontends/VirtualBox/src/objects/UIExecutionQueue.h
new file mode 100644
index 00000000..288c925b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/objects/UIExecutionQueue.h
@@ -0,0 +1,98 @@
+/* $Id: UIExecutionQueue.h $ */
+/** @file
+ * VBox Qt GUI - UIExecutionQueue class declaration.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_objects_UIExecutionQueue_h
+#define FEQT_INCLUDED_SRC_objects_UIExecutionQueue_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QQueue>
+
+/** QObject subclass providing GUI with
+ * interface for an execution step. */
+class UIExecutionStep : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about step finished. */
+ void sigStepFinished();
+
+public:
+
+ /** Constructs execution step. */
+ UIExecutionStep();
+
+ /** Executes the step. */
+ virtual void exec() = 0;
+};
+
+/** QObject subclass providing GUI with
+ * an object to process queue of execution steps. */
+class UIExecutionQueue : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Starts the queue. */
+ void sigStartQueue();
+
+ /** Notifies about queue finished. */
+ void sigQueueFinished();
+
+public:
+
+ /** Constructs execution queue passing @a pParent to the base-class. */
+ UIExecutionQueue(QObject *pParent = 0);
+ /** Destructs execution queue. */
+ virtual ~UIExecutionQueue() /* override final */;
+
+ /** Enqueues pStep into queue. */
+ void enqueue(UIExecutionStep *pStep);
+
+ /** Starts the queue. */
+ void start() { emit sigStartQueue(); }
+
+private slots:
+
+ /** Starts subsequent step. */
+ void sltStartsSubsequentStep();
+
+private:
+
+ /** Holds the execution step queue. */
+ QQueue<UIExecutionStep*> m_queue;
+ /** Holds current step being executed. */
+ UIExecutionStep *m_pExecutedStep;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_objects_UIExecutionQueue_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/objects/UIRichTextString.cpp b/src/VBox/Frontends/VirtualBox/src/objects/UIRichTextString.cpp
new file mode 100644
index 00000000..a10376c8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/objects/UIRichTextString.cpp
@@ -0,0 +1,240 @@
+/* $Id: UIRichTextString.cpp $ */
+/** @file
+ * VBox Qt GUI - UIRichTextString class implementation.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QPalette>
+#include <QRegExp>
+
+/* GUI includes: */
+#include "UIRichTextString.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+const QString UIRichTextString::s_strAny = QString("[\\s\\S]*");
+const QMap<UIRichTextString::Type, QString> UIRichTextString::s_patterns = populatePatterns();
+const QMap<UIRichTextString::Type, bool> UIRichTextString::s_doPatternHasMeta = populatePatternHasMeta();
+
+UIRichTextString::UIRichTextString(Type enmType /* = Type_None */)
+ : m_enmType(enmType)
+ , m_strString(QString())
+ , m_strStringMeta(QString())
+{
+}
+
+UIRichTextString::UIRichTextString(const QString &strString, Type enmType /* = Type_None */, const QString &strStringMeta /* = QString() */)
+ : m_enmType(enmType)
+ , m_strString(strString)
+ , m_strStringMeta(strStringMeta)
+{
+ //printf("Creating new UIRichTextString with string=\"%s\" and string-meta=\"%s\"\n",
+ // m_strString.toUtf8().constData(), m_strStringMeta.toUtf8().constData());
+ parse();
+}
+
+UIRichTextString::~UIRichTextString()
+{
+ /* Erase the map: */
+ qDeleteAll(m_strings.begin(), m_strings.end());
+ m_strings.clear();
+}
+
+QString UIRichTextString::toString() const
+{
+ /* Add own string first: */
+ QString strString = m_strString;
+
+ /* Add all the strings of children finally: */
+ foreach (const int &iPosition, m_strings.keys())
+ strString.insert(iPosition, m_strings.value(iPosition)->toString());
+
+ /* Return result: */
+ return strString;
+}
+
+QVector<QTextLayout::FormatRange> UIRichTextString::formatRanges(int iShift /* = 0 */) const
+{
+ /* Prepare format range list: */
+ QVector<QTextLayout::FormatRange> ranges;
+
+ /* Add own format range first: */
+ QTextLayout::FormatRange range;
+ range.start = iShift;
+ range.length = toString().size();
+ range.format = textCharFormat(m_enmType);
+ /* Enable anchor if present: */
+ if (!m_strAnchor.isNull())
+ {
+ range.format.setAnchorHref(m_strAnchor);
+ /* Highlight anchor if hovered: */
+ if (range.format.anchorHref() == m_strHoveredAnchor)
+ range.format.setForeground(qApp->palette().color(QPalette::Link));
+ }
+ ranges.append(range);
+
+ /* Add all the format ranges of children finally: */
+ foreach (const int &iPosition, m_strings.keys())
+ ranges.append(m_strings.value(iPosition)->formatRanges(iShift + iPosition));
+
+ /* Return result: */
+ return ranges;
+}
+
+void UIRichTextString::setHoveredAnchor(const QString &strHoveredAnchor)
+{
+ /* Define own hovered anchor first: */
+ m_strHoveredAnchor = strHoveredAnchor;
+
+ /* Propagate hovered anchor to children finally: */
+ foreach (const int &iPosition, m_strings.keys())
+ m_strings.value(iPosition)->setHoveredAnchor(m_strHoveredAnchor);
+}
+
+void UIRichTextString::parse()
+{
+ /* Assign the meta to anchor directly for now,
+ * will do a separate parsing when there will
+ * be more than one type of meta: */
+ if (!m_strStringMeta.isNull())
+ m_strAnchor = m_strStringMeta;
+
+ /* Parse the passed QString with all the known patterns: */
+ foreach (const Type &enmPattern, s_patterns.keys())
+ {
+ /* Get the current pattern: */
+ const QString strPattern = s_patterns.value(enmPattern);
+
+ /* Recursively parse the string: */
+ int iMaxLevel = 0;
+ do
+ {
+ /* Search for the maximum level of the current pattern: */
+ iMaxLevel = searchForMaxLevel(m_strString, strPattern, strPattern);
+ //printf(" Maximum level for the pattern \"%s\" is %d.\n",
+ // strPattern.toUtf8().constData(), iMaxLevel);
+ /* If current pattern of at least level 1 is found: */
+ if (iMaxLevel > 0)
+ {
+ /* Compose full pattern of the corresponding level: */
+ const QString strFullPattern = composeFullPattern(strPattern, strPattern, iMaxLevel);
+ //printf(" Full pattern: %s\n", strFullPattern.toUtf8().constData());
+ QRegExp regExp(strFullPattern);
+ regExp.setMinimal(true);
+ const int iPosition = regExp.indexIn(m_strString);
+ AssertReturnVoid(iPosition != -1);
+ if (iPosition != -1)
+ {
+ /* Cut the found string: */
+ m_strString.remove(iPosition, regExp.cap(0).size());
+ /* And paste that string as our child: */
+ const bool fPatterHasMeta = s_doPatternHasMeta.value(enmPattern);
+ const QString strSubString = !fPatterHasMeta ? regExp.cap(1) : regExp.cap(2);
+ const QString strSubMeta = !fPatterHasMeta ? QString() : regExp.cap(1);
+ m_strings.insert(iPosition, new UIRichTextString(strSubString, enmPattern, strSubMeta));
+ }
+ }
+ }
+ while (iMaxLevel > 0);
+ }
+}
+
+/* static */
+QMap<UIRichTextString::Type, QString> UIRichTextString::populatePatterns()
+{
+ QMap<Type, QString> patterns;
+ patterns.insert(Type_Anchor, QString("<a href=([^>]+)>(%1)</a>"));
+ patterns.insert(Type_Bold, QString("<b>(%1)</b>"));
+ patterns.insert(Type_Italic, QString("<i>(%1)</i>"));
+ return patterns;
+}
+
+/* static */
+QMap<UIRichTextString::Type, bool> UIRichTextString::populatePatternHasMeta()
+{
+ QMap<Type, bool> patternHasMeta;
+ patternHasMeta.insert(Type_Anchor, true);
+ patternHasMeta.insert(Type_Bold, false);
+ patternHasMeta.insert(Type_Italic, false);
+ return patternHasMeta;
+}
+
+/* static */
+int UIRichTextString::searchForMaxLevel(const QString &strString, const QString &strPattern,
+ const QString &strCurrentPattern, int iCurrentLevel /* = 0 */)
+{
+ QRegExp regExp(strCurrentPattern.arg(s_strAny));
+ regExp.setMinimal(true);
+ if (regExp.indexIn(strString) != -1)
+ return searchForMaxLevel(strString, strPattern,
+ strCurrentPattern.arg(s_strAny + strPattern + s_strAny),
+ iCurrentLevel + 1);
+ return iCurrentLevel;
+}
+
+/* static */
+QString UIRichTextString::composeFullPattern(const QString &strPattern,
+ const QString &strCurrentPattern, int iCurrentLevel)
+{
+ if (iCurrentLevel > 1)
+ return composeFullPattern(strPattern,
+ strCurrentPattern.arg(s_strAny + strPattern + s_strAny),
+ iCurrentLevel - 1);
+ return strCurrentPattern.arg(s_strAny);
+}
+
+/* static */
+QTextCharFormat UIRichTextString::textCharFormat(Type enmType)
+{
+ QTextCharFormat format;
+ switch (enmType)
+ {
+ case Type_Anchor:
+ {
+ format.setAnchor(true);
+ break;
+ }
+ case Type_Bold:
+ {
+ QFont font = format.font();
+ font.setBold(true);
+ format.setFont(font);
+ break;
+ }
+ case Type_Italic:
+ {
+ QFont font = format.font();
+ font.setItalic(true);
+ format.setFont(font);
+ break;
+ }
+
+ case Type_None: break; /* Shut up MSC */
+ }
+ return format;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/objects/UIRichTextString.h b/src/VBox/Frontends/VirtualBox/src/objects/UIRichTextString.h
new file mode 100644
index 00000000..07621242
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/objects/UIRichTextString.h
@@ -0,0 +1,130 @@
+/* $Id: UIRichTextString.h $ */
+/** @file
+ * VBox Qt GUI - UIRichTextString class declaration.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_objects_UIRichTextString_h
+#define FEQT_INCLUDED_SRC_objects_UIRichTextString_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QTextLayout>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** Rich text string implementation which parses the passed QString
+ * and holds it as the tree of the formatted rich text blocks. */
+class SHARED_LIBRARY_STUFF UIRichTextString
+{
+public:
+
+ /** Rich text block types. */
+ enum Type
+ {
+ Type_None,
+ Type_Anchor,
+ Type_Bold,
+ Type_Italic,
+ };
+
+ /** Constructs empty rich text string.
+ * @param enmType Brings the type of <i>this</i> rich text block. */
+ UIRichTextString(Type enmType = Type_None);
+
+ /** Constructs rich text string.
+ * @param strString Brings the string being parsed and held as the tree of rich text blocks.
+ * @param enmType Brings the type of <i>this</i> rich text block.
+ * @param strStringMeta Brings the string containing meta data describing <i>this</i> rich text block. */
+ UIRichTextString(const QString &strString, Type enmType = Type_None, const QString &strStringMeta = QString());
+
+ /** Destructor rich text string. */
+ virtual ~UIRichTextString();
+
+ /** Returns the QString representation. */
+ QString toString() const;
+
+ /** Returns the list of existing format ranges appropriate for QTextLayout.
+ * @param iShift Brings the shift of <i>this</i> rich text block accordig to it's root. */
+ QVector<QTextLayout::FormatRange> formatRanges(int iShift = 0) const;
+
+ /** Defines the anchor to highlight in <i>this</i> rich text block and in it's children. */
+ void setHoveredAnchor(const QString &strHoveredAnchor);
+
+private:
+
+ /** Parses the string. */
+ void parse();
+
+ /** Used to populate const static map of known patterns.
+ * @note Keep it sync with the method below - #populatePatternHasMeta(). */
+ static QMap<Type, QString> populatePatterns();
+ /** Used to populate const static map of meta flags for the known patterns.
+ * @note Keep it sync with the method above - #populatePatterns(). */
+ static QMap<Type, bool> populatePatternHasMeta();
+
+ /** Recursively searching for the maximum level of the passed pattern.
+ * @param strString Brings the string to check for the current (recursively advanced) pattern in,
+ * @param strPattern Brings the etalon pattern to recursively advance the current pattern with,
+ * @param strCurrentPattern Brings the current (recursively advanced) pattern to check for the presence of,
+ * @param iCurrentLevel Brings the current level of the recursively advanced pattern. */
+ static int searchForMaxLevel(const QString &strString, const QString &strPattern,
+ const QString &strCurrentPattern, int iCurrentLevel = 0);
+
+ /** Recursively composing the pattern of the maximum level.
+ * @param strPattern Brings the etalon pattern to recursively update the current pattern with,
+ * @param strCurrentPattern Brings the current (recursively advanced) pattern,
+ * @param iCurrentLevel Brings the amount of the levels left to recursively advance current pattern. */
+ static QString composeFullPattern(const QString &strPattern,
+ const QString &strCurrentPattern, int iCurrentLevel);
+
+ /** Composes the QTextCharFormat correpoding to passed @a enmType. */
+ static QTextCharFormat textCharFormat(Type enmType);
+
+ /** Holds the type of <i>this</i> rich text block. */
+ Type m_enmType;
+ /** Holds the string of <i>this</i> rich text block. */
+ QString m_strString;
+ /** Holds the string meta data of <i>this</i> rich text block. */
+ QString m_strStringMeta;
+ /** Holds the children of <i>this</i> rich text block. */
+ QMap<int, UIRichTextString*> m_strings;
+
+ /** Holds the anchor of <i>this</i> rich text block. */
+ QString m_strAnchor;
+ /** Holds the anchor to highlight in <i>this</i> rich text block and in it's children. */
+ QString m_strHoveredAnchor;
+
+ /** Holds the <i>any</i> string pattern. */
+ static const QString s_strAny;
+ /** Holds the map of known patterns. */
+ static const QMap<Type, QString> s_patterns;
+ /** Holds the map of meta flags for the known patterns. */
+ static const QMap<Type, bool> s_doPatternHasMeta;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_objects_UIRichTextString_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/platform/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/UIDesktopServices.h b/src/VBox/Frontends/VirtualBox/src/platform/UIDesktopServices.h
new file mode 100644
index 00000000..95bfa6a0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/UIDesktopServices.h
@@ -0,0 +1,51 @@
+/* $Id: UIDesktopServices.h $ */
+/** @file
+ * VBox Qt GUI - Desktop Services..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_UIDesktopServices_h
+#define FEQT_INCLUDED_SRC_platform_UIDesktopServices_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes */
+#include <QUuid>
+
+/** Name of the executable (image) used to start VMs. */
+#define VBOX_GUI_VMRUNNER_IMAGE "VirtualBoxVM"
+
+/* Qt forward declarations */
+class QString;
+
+class UIDesktopServices
+{
+public:
+ static bool createMachineShortcut(const QString &strSrcFile, const QString &strDstPath, const QString &strName, const QUuid &uUuid);
+ static bool openInFileManager(const QString &strFile);
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_UIDesktopServices_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.h
new file mode 100644
index 00000000..bbf8abcb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.h
@@ -0,0 +1,65 @@
+/* $Id: CocoaEventHelper.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Darwin Cocoa specific event-handling tasks.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_CocoaEventHelper_h
+#define FEQT_INCLUDED_SRC_platform_darwin_CocoaEventHelper_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+#include <VBox/VBoxCocoa.h>
+
+/* Cocoa declarations: */
+ADD_COCOA_NATIVE_REF(NSEvent);
+
+
+RT_C_DECLS_BEGIN
+
+/** Calls the -(NSUInteger)modifierFlags method on @a pEvent object and converts the flags to carbon style. */
+uint32_t darwinEventModifierFlagsXlated(ConstNativeNSEventRef pEvent);
+
+/** Get the name for a Cocoa @a enmEventType. */
+const char *darwinEventTypeName(unsigned long enmEventType);
+
+/** Debug helper function for dumping a Cocoa event to stdout.
+ * @param pszPrefix Brings the message prefix.
+ * @param pEvent Brings the Cocoa event. */
+void darwinPrintEvent(const char *pszPrefix, ConstNativeNSEventRef pEvent);
+
+/** Posts stripped mouse event based on passed @a pEvent. */
+SHARED_LIBRARY_STUFF void darwinPostStrippedMouseEvent(ConstNativeNSEventRef pEvent);
+
+RT_C_DECLS_END
+
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_CocoaEventHelper_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.mm
new file mode 100644
index 00000000..09152b37
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.mm
@@ -0,0 +1,366 @@
+/* $Id: CocoaEventHelper.mm $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Darwin Cocoa specific event handling tasks.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "CocoaEventHelper.h"
+#include "DarwinKeyboard.h"
+
+/* External includes: */
+#import <Cocoa/Cocoa.h>
+#import <AppKit/NSEvent.h>
+#include <Carbon/Carbon.h>
+
+/* They just had to rename a whole load of constants in 10.12. Just wrap the carp up
+ in some defines for now as we need to keep building with both 10.9 and 10.13+: */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+# define VBOX_NSAlphaShiftKeyMask NSEventModifierFlagCapsLock
+# define VBOX_NSAlternateKeyMask NSEventModifierFlagOption
+# define VBOX_NSAppKitDefined NSEventTypeAppKitDefined
+# define VBOX_NSApplicationDefined NSEventTypeApplicationDefined
+# define VBOX_NSCommandKeyMask NSEventModifierFlagCommand
+# define VBOX_NSControlKeyMask NSEventModifierFlagControl
+# define VBOX_NSCursorUpdate NSEventTypeCursorUpdate
+# define VBOX_NSFlagsChanged NSEventTypeFlagsChanged
+# define VBOX_NSFunctionKeyMask NSEventModifierFlagFunction
+# define VBOX_NSHelpKeyMask NSEventModifierFlagHelp
+# define VBOX_NSKeyDown NSEventTypeKeyDown
+# define VBOX_NSKeyUp NSEventTypeKeyUp
+# define VBOX_NSLeftMouseDown NSEventTypeLeftMouseDown
+# define VBOX_NSLeftMouseDragged NSEventTypeLeftMouseDragged
+# define VBOX_NSLeftMouseUp NSEventTypeLeftMouseUp
+# define VBOX_NSMouseEntered NSEventTypeMouseEntered
+# define VBOX_NSMouseExited NSEventTypeMouseExited
+# define VBOX_NSMouseMoved NSEventTypeMouseMoved
+# define VBOX_NSNumericPadKeyMask NSEventModifierFlagNumericPad
+# define VBOX_NSOtherMouseDown NSEventTypeOtherMouseDown
+# define VBOX_NSOtherMouseDragged NSEventTypeOtherMouseDragged
+# define VBOX_NSOtherMouseUp NSEventTypeOtherMouseUp
+# define VBOX_NSPeriodic NSEventTypePeriodic
+# define VBOX_NSRightMouseDown NSEventTypeRightMouseDown
+# define VBOX_NSRightMouseDragged NSEventTypeRightMouseDragged
+# define VBOX_NSRightMouseUp NSEventTypeRightMouseUp
+# define VBOX_NSScrollWheel NSEventTypeScrollWheel
+# define VBOX_NSShiftKeyMask NSEventModifierFlagShift
+# define VBOX_NSSystemDefined NSEventTypeSystemDefined
+# define VBOX_NSTabletPoint NSEventTypeTabletPoint
+# define VBOX_NSTabletProximity NSEventTypeTabletProximity
+#else
+# define VBOX_NSAlphaShiftKeyMask NSAlphaShiftKeyMask
+# define VBOX_NSAlternateKeyMask NSAlternateKeyMask
+# define VBOX_NSAppKitDefined NSAppKitDefined
+# define VBOX_NSApplicationDefined NSApplicationDefined
+# define VBOX_NSCommandKeyMask NSCommandKeyMask
+# define VBOX_NSControlKeyMask NSControlKeyMask
+# define VBOX_NSCursorUpdate NSCursorUpdate
+# define VBOX_NSFlagsChanged NSFlagsChanged
+# define VBOX_NSFunctionKeyMask NSFunctionKeyMask
+# define VBOX_NSHelpKeyMask NSHelpKeyMask
+# define VBOX_NSKeyDown NSKeyDown
+# define VBOX_NSKeyUp NSKeyUp
+# define VBOX_NSLeftMouseDown NSLeftMouseDown
+# define VBOX_NSLeftMouseDragged NSLeftMouseDragged
+# define VBOX_NSLeftMouseUp NSLeftMouseUp
+# define VBOX_NSMouseEntered NSMouseEntered
+# define VBOX_NSMouseExited NSMouseExited
+# define VBOX_NSMouseMoved NSMouseMoved
+# define VBOX_NSNumericPadKeyMask NSNumericPadKeyMask
+# define VBOX_NSOtherMouseDown NSOtherMouseDown
+# define VBOX_NSOtherMouseDragged NSOtherMouseDragged
+# define VBOX_NSOtherMouseUp NSOtherMouseUp
+# define VBOX_NSPeriodic NSPeriodic
+# define VBOX_NSRightMouseDown NSRightMouseDown
+# define VBOX_NSRightMouseDragged NSRightMouseDragged
+# define VBOX_NSRightMouseUp NSRightMouseUp
+# define VBOX_NSScrollWheel NSScrollWheel
+# define VBOX_NSShiftKeyMask NSShiftKeyMask
+# define VBOX_NSSystemDefined NSSystemDefined
+# define VBOX_NSTabletPoint NSTabletPoint
+# define VBOX_NSTabletProximity NSTabletProximity
+#endif
+
+uint32_t darwinEventModifierFlagsXlated(ConstNativeNSEventRef pEvent)
+{
+ NSUInteger fCocoa = [pEvent modifierFlags];
+ uint32_t fCarbon = 0;
+ if (fCocoa)
+ {
+ if (fCocoa & VBOX_NSAlphaShiftKeyMask)
+ fCarbon |= alphaLock;
+ if (fCocoa & (VBOX_NSShiftKeyMask | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
+ {
+ if (fCocoa & (NX_DEVICERSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
+ {
+ if (fCocoa & NX_DEVICERSHIFTKEYMASK)
+ fCarbon |= rightShiftKey;
+ if (fCocoa & NX_DEVICELSHIFTKEYMASK)
+ fCarbon |= shiftKey;
+ }
+ else
+ fCarbon |= shiftKey;
+ }
+
+ if (fCocoa & (VBOX_NSControlKeyMask | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
+ {
+ if (fCocoa & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
+ {
+ if (fCocoa & NX_DEVICERCTLKEYMASK)
+ fCarbon |= rightControlKey;
+ if (fCocoa & NX_DEVICELCTLKEYMASK)
+ fCarbon |= controlKey;
+ }
+ else
+ fCarbon |= controlKey;
+ }
+
+ if (fCocoa & (VBOX_NSAlternateKeyMask | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
+ {
+ if (fCocoa & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
+ {
+ if (fCocoa & NX_DEVICERALTKEYMASK)
+ fCarbon |= rightOptionKey;
+ if (fCocoa & NX_DEVICELALTKEYMASK)
+ fCarbon |= optionKey;
+ }
+ else
+ fCarbon |= optionKey;
+ }
+
+ if (fCocoa & (VBOX_NSCommandKeyMask | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
+ {
+ if (fCocoa & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
+ {
+ if (fCocoa & NX_DEVICERCMDKEYMASK)
+ fCarbon |= kEventKeyModifierRightCmdKeyMask;
+ if (fCocoa & NX_DEVICELCMDKEYMASK)
+ fCarbon |= cmdKey;
+ }
+ else
+ fCarbon |= cmdKey;
+ }
+
+ /*
+ if (fCocoa & VBOX_NSNumericPadKeyMask)
+ fCarbon |= ???;
+
+ if (fCocoa & VBOX_NSHelpKeyMask)
+ fCarbon |= ???;
+
+ if (fCocoa & VBOX_NSFunctionKeyMask)
+ fCarbon |= ???;
+ */
+ }
+
+ return fCarbon;
+}
+
+const char *darwinEventTypeName(unsigned long enmEventType)
+{
+ switch (enmEventType)
+ {
+#define EVT_CASE(nm) case nm: return #nm
+ EVT_CASE(VBOX_NSLeftMouseDown);
+ EVT_CASE(VBOX_NSLeftMouseUp);
+ EVT_CASE(VBOX_NSRightMouseDown);
+ EVT_CASE(VBOX_NSRightMouseUp);
+ EVT_CASE(VBOX_NSMouseMoved);
+ EVT_CASE(VBOX_NSLeftMouseDragged);
+ EVT_CASE(VBOX_NSRightMouseDragged);
+ EVT_CASE(VBOX_NSMouseEntered);
+ EVT_CASE(VBOX_NSMouseExited);
+ EVT_CASE(VBOX_NSKeyDown);
+ EVT_CASE(VBOX_NSKeyUp);
+ EVT_CASE(VBOX_NSFlagsChanged);
+ EVT_CASE(VBOX_NSAppKitDefined);
+ EVT_CASE(VBOX_NSSystemDefined);
+ EVT_CASE(VBOX_NSApplicationDefined);
+ EVT_CASE(VBOX_NSPeriodic);
+ EVT_CASE(VBOX_NSCursorUpdate);
+ EVT_CASE(VBOX_NSScrollWheel);
+ EVT_CASE(VBOX_NSTabletPoint);
+ EVT_CASE(VBOX_NSTabletProximity);
+ EVT_CASE(VBOX_NSOtherMouseDown);
+ EVT_CASE(VBOX_NSOtherMouseUp);
+ EVT_CASE(VBOX_NSOtherMouseDragged);
+#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
+ EVT_CASE(NSEventTypeGesture);
+ EVT_CASE(NSEventTypeMagnify);
+ EVT_CASE(NSEventTypeSwipe);
+ EVT_CASE(NSEventTypeRotate);
+ EVT_CASE(NSEventTypeBeginGesture);
+ EVT_CASE(NSEventTypeEndGesture);
+#endif
+#undef EVT_CASE
+ default:
+ return "Unknown!";
+ }
+}
+
+void darwinPrintEvent(const char *pszPrefix, ConstNativeNSEventRef pEvent)
+{
+ NSEventType enmEventType = [pEvent type];
+ NSUInteger fEventMask = [pEvent modifierFlags];
+ NSWindow *pEventWindow = [pEvent window];
+ NSInteger iEventWindow = [pEvent windowNumber];
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ NSGraphicsContext *pEventGraphicsContext = nil; /* NSEvent::context is deprecated and said to always return nil. */
+#else
+ NSGraphicsContext *pEventGraphicsContext = [pEvent context];
+#endif
+
+ printf("%s%p: Type=%lu Modifiers=%08lx pWindow=%p #Wnd=%ld pGraphCtx=%p %s\n",
+ pszPrefix, (void*)pEvent, (unsigned long)enmEventType, (unsigned long)fEventMask, (void*)pEventWindow,
+ (long)iEventWindow, (void*)pEventGraphicsContext, darwinEventTypeName(enmEventType));
+
+ /* Dump type specific info: */
+ switch (enmEventType)
+ {
+ case VBOX_NSLeftMouseDown:
+ case VBOX_NSLeftMouseUp:
+ case VBOX_NSRightMouseDown:
+ case VBOX_NSRightMouseUp:
+ case VBOX_NSMouseMoved:
+
+ case VBOX_NSLeftMouseDragged:
+ case VBOX_NSRightMouseDragged:
+ case VBOX_NSMouseEntered:
+ case VBOX_NSMouseExited:
+ break;
+
+ case VBOX_NSKeyDown:
+ case VBOX_NSKeyUp:
+ {
+ NSUInteger i;
+ NSUInteger cch;
+ NSString *pChars = [pEvent characters];
+ NSString *pCharsIgnMod = [pEvent charactersIgnoringModifiers];
+ BOOL fIsARepeat = [pEvent isARepeat];
+ unsigned short KeyCode = [pEvent keyCode];
+
+ printf(" KeyCode=%04x isARepeat=%d", KeyCode, fIsARepeat);
+ if (pChars)
+ {
+ cch = [pChars length];
+ printf(" characters={");
+ for (i = 0; i < cch; i++)
+ printf(i == 0 ? "%02x" : ",%02x", [pChars characterAtIndex: i]);
+ printf("}");
+ }
+
+ if (pCharsIgnMod)
+ {
+ cch = [pCharsIgnMod length];
+ printf(" charactersIgnoringModifiers={");
+ for (i = 0; i < cch; i++)
+ printf(i == 0 ? "%02x" : ",%02x", [pCharsIgnMod characterAtIndex: i]);
+ printf("}");
+ }
+ printf("\n");
+ break;
+ }
+
+ case VBOX_NSFlagsChanged:
+ {
+ NSUInteger fOddBits = VBOX_NSAlphaShiftKeyMask | VBOX_NSShiftKeyMask | VBOX_NSControlKeyMask | VBOX_NSAlternateKeyMask
+ | VBOX_NSCommandKeyMask | VBOX_NSNumericPadKeyMask | VBOX_NSHelpKeyMask | VBOX_NSFunctionKeyMask
+ | NX_DEVICELCTLKEYMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK
+ | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK | NX_DEVICELALTKEYMASK
+ | NX_DEVICERALTKEYMASK | NX_DEVICERCTLKEYMASK;
+
+ printf(" KeyCode=%04x", (int)[pEvent keyCode]);
+#define PRINT_MOD(cnst, nm) do { if (fEventMask & (cnst)) printf(" %s", #nm); } while (0)
+ /* device-independent: */
+ PRINT_MOD(VBOX_NSAlphaShiftKeyMask, "AlphaShift");
+ PRINT_MOD(VBOX_NSShiftKeyMask, "Shift");
+ PRINT_MOD(VBOX_NSControlKeyMask, "Ctrl");
+ PRINT_MOD(VBOX_NSAlternateKeyMask, "Alt");
+ PRINT_MOD(VBOX_NSCommandKeyMask, "Cmd");
+ PRINT_MOD(VBOX_NSNumericPadKeyMask, "NumLock");
+ PRINT_MOD(VBOX_NSHelpKeyMask, "Help");
+ PRINT_MOD(VBOX_NSFunctionKeyMask, "Fn");
+ /* device-dependent (sort of): */
+ PRINT_MOD(NX_DEVICELCTLKEYMASK, "$L-Ctrl");
+ PRINT_MOD(NX_DEVICELSHIFTKEYMASK, "$L-Shift");
+ PRINT_MOD(NX_DEVICERSHIFTKEYMASK, "$R-Shift");
+ PRINT_MOD(NX_DEVICELCMDKEYMASK, "$L-Cmd");
+ PRINT_MOD(NX_DEVICERCMDKEYMASK, "$R-Cmd");
+ PRINT_MOD(NX_DEVICELALTKEYMASK, "$L-Alt");
+ PRINT_MOD(NX_DEVICERALTKEYMASK, "$R-Alt");
+ PRINT_MOD(NX_DEVICERCTLKEYMASK, "$R-Ctrl");
+#undef PRINT_MOD
+
+ fOddBits = fEventMask & ~fOddBits;
+ if (fOddBits)
+ printf(" fOddBits=%#08lx", (unsigned long)fOddBits);
+#undef KNOWN_BITS
+ printf("\n");
+ break;
+ }
+
+ case VBOX_NSAppKitDefined:
+ case VBOX_NSSystemDefined:
+ case VBOX_NSApplicationDefined:
+ case VBOX_NSPeriodic:
+ case VBOX_NSCursorUpdate:
+ case VBOX_NSScrollWheel:
+ case VBOX_NSTabletPoint:
+ case VBOX_NSTabletProximity:
+ case VBOX_NSOtherMouseDown:
+ case VBOX_NSOtherMouseUp:
+ case VBOX_NSOtherMouseDragged:
+#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
+ case NSEventTypeGesture:
+ case NSEventTypeMagnify:
+ case NSEventTypeSwipe:
+ case NSEventTypeRotate:
+ case NSEventTypeBeginGesture:
+ case NSEventTypeEndGesture:
+#endif
+ default:
+ printf(" Unknown!\n");
+ break;
+ }
+}
+
+void darwinPostStrippedMouseEvent(ConstNativeNSEventRef pEvent)
+{
+ /* Create and post new stripped event: */
+ NSEvent *pNewEvent = [NSEvent mouseEventWithType:[pEvent type]
+ location:[pEvent locationInWindow]
+ modifierFlags:0
+ timestamp:[pEvent timestamp] // [NSDate timeIntervalSinceReferenceDate] ?
+ windowNumber:[pEvent windowNumber]
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ context:nil /* NSEvent::context is deprecated and said to always return nil. */
+#else
+ context:[pEvent context]
+#endif
+ eventNumber:[pEvent eventNumber]
+ clickCount:[pEvent clickCount]
+ pressure:[pEvent pressure]];
+ [NSApp postEvent:pNewEvent atStart:YES];
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp
new file mode 100644
index 00000000..4f2e2971
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp
@@ -0,0 +1,2197 @@
+/* $Id: DarwinKeyboard.cpp $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Darwin Keyboard specific tasks.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Defines: */
+#define LOG_GROUP LOG_GROUP_GUI
+#define VBOX_WITH_KBD_LEDS_SYNC
+//#define VBOX_WITHOUT_KBD_LEDS_SYNC_FILTERING
+
+/* GUI includes: */
+#include "DarwinKeyboard.h"
+#ifndef USE_HID_FOR_MODIFIERS
+# include "CocoaEventHelper.h"
+#endif
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+#include <VBox/log.h>
+#ifdef DEBUG_PRINTF
+# include <iprt/stream.h>
+#endif
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+# include <iprt/errcore.h>
+# include <iprt/semaphore.h>
+# include <VBox/sup.h>
+#endif
+
+/* External includes: */
+#include <ApplicationServices/ApplicationServices.h>
+#include <Carbon/Carbon.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDLib.h>
+#include <IOKit/usb/USB.h>
+#ifdef USE_HID_FOR_MODIFIERS
+# include <CoreFoundation/CoreFoundation.h>
+# include <IOKit/hid/IOHIDUsageTables.h>
+# include <mach/mach.h>
+# include <mach/mach_error.h>
+#endif
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+# include <IOKit/IOMessage.h>
+# include <IOKit/usb/IOUSBLib.h>
+#endif
+
+
+RT_C_DECLS_BEGIN
+/* Private interface in 10.3 and later. */
+typedef int CGSConnection;
+typedef enum
+{
+ kCGSGlobalHotKeyEnable = 0,
+ kCGSGlobalHotKeyDisable,
+ kCGSGlobalHotKeyDisableExceptUniversalAccess,
+ kCGSGlobalHotKeyInvalid = -1 /* bird */
+} CGSGlobalHotKeyOperatingMode;
+extern CGSConnection _CGSDefaultConnection(void);
+extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode *enmMode);
+extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode enmMode);
+RT_C_DECLS_END
+
+
+/* Defined Constants And Macros: */
+#define QZ_RMETA 0x36
+#define QZ_LMETA 0x37
+#define QZ_LSHIFT 0x38
+#define QZ_CAPSLOCK 0x39
+#define QZ_LALT 0x3A
+#define QZ_LCTRL 0x3B
+#define QZ_RSHIFT 0x3C
+#define QZ_RALT 0x3D
+#define QZ_RCTRL 0x3E
+// Found the definition of the fn-key in:
+// http://stuff.mit.edu/afs/sipb/project/darwin/src/modules/IOHIDFamily/IOHIDSystem/IOHIKeyboardMapper.cpp &
+// http://stuff.mit.edu/afs/sipb/project/darwin/src/modules/AppleADBKeyboard/AppleADBKeyboard.cpp
+// Maybe we need this in the future.
+#define QZ_FN 0x3F
+#define QZ_NUMLOCK 0x47
+/** Short hand for an extended key. */
+#define K_EX VBOXKEY_EXTENDED
+/** Short hand for a modifier key. */
+#define K_MOD VBOXKEY_MODIFIER
+/** Short hand for a lock key. */
+#define K_LOCK VBOXKEY_LOCK
+#ifdef USE_HID_FOR_MODIFIERS
+/** An attempt at catching reference leaks. */
+# define MY_CHECK_CREFS(cRefs) do { AssertMsg(cRefs < 25, ("%ld\n", cRefs)); NOREF(cRefs); } while (0)
+#endif
+
+
+/** This is derived partially from SDL_QuartzKeys.h and partially from testing.
+ * (The funny thing about the virtual scan codes on the mac is that they aren't
+ * offically documented, which is rather silly to say the least. Thus, the need
+ * for looking at SDL and other odd places for docs.) */
+static const uint16_t g_aDarwinToSet1[] =
+{
+ /* set-1 SDL_QuartzKeys.h */
+ 0x1e, /* QZ_a 0x00 */
+ 0x1f, /* QZ_s 0x01 */
+ 0x20, /* QZ_d 0x02 */
+ 0x21, /* QZ_f 0x03 */
+ 0x23, /* QZ_h 0x04 */
+ 0x22, /* QZ_g 0x05 */
+ 0x2c, /* QZ_z 0x06 */
+ 0x2d, /* QZ_x 0x07 */
+ 0x2e, /* QZ_c 0x08 */
+ 0x2f, /* QZ_v 0x09 */
+ 0x56, /* between lshift and z. 'INT 1'? */
+ 0x30, /* QZ_b 0x0B */
+ 0x10, /* QZ_q 0x0C */
+ 0x11, /* QZ_w 0x0D */
+ 0x12, /* QZ_e 0x0E */
+ 0x13, /* QZ_r 0x0F */
+ 0x15, /* QZ_y 0x10 */
+ 0x14, /* QZ_t 0x11 */
+ 0x02, /* QZ_1 0x12 */
+ 0x03, /* QZ_2 0x13 */
+ 0x04, /* QZ_3 0x14 */
+ 0x05, /* QZ_4 0x15 */
+ 0x07, /* QZ_6 0x16 */
+ 0x06, /* QZ_5 0x17 */
+ 0x0d, /* QZ_EQUALS 0x18 */
+ 0x0a, /* QZ_9 0x19 */
+ 0x08, /* QZ_7 0x1A */
+ 0x0c, /* QZ_MINUS 0x1B */
+ 0x09, /* QZ_8 0x1C */
+ 0x0b, /* QZ_0 0x1D */
+ 0x1b, /* QZ_RIGHTBRACKET 0x1E */
+ 0x18, /* QZ_o 0x1F */
+ 0x16, /* QZ_u 0x20 */
+ 0x1a, /* QZ_LEFTBRACKET 0x21 */
+ 0x17, /* QZ_i 0x22 */
+ 0x19, /* QZ_p 0x23 */
+ 0x1c, /* QZ_RETURN 0x24 */
+ 0x26, /* QZ_l 0x25 */
+ 0x24, /* QZ_j 0x26 */
+ 0x28, /* QZ_QUOTE 0x27 */
+ 0x25, /* QZ_k 0x28 */
+ 0x27, /* QZ_SEMICOLON 0x29 */
+ 0x2b, /* QZ_BACKSLASH 0x2A */
+ 0x33, /* QZ_COMMA 0x2B */
+ 0x35, /* QZ_SLASH 0x2C */
+ 0x31, /* QZ_n 0x2D */
+ 0x32, /* QZ_m 0x2E */
+ 0x34, /* QZ_PERIOD 0x2F */
+ 0x0f, /* QZ_TAB 0x30 */
+ 0x39, /* QZ_SPACE 0x31 */
+ 0x29, /* QZ_BACKQUOTE 0x32 */
+ 0x0e, /* QZ_BACKSPACE 0x33 */
+ 0x9c, /* QZ_IBOOK_ENTER 0x34 */
+ 0x01, /* QZ_ESCAPE 0x35 */
+ 0x5c|K_EX|K_MOD, /* QZ_RMETA 0x36 */
+ 0x5b|K_EX|K_MOD, /* QZ_LMETA 0x37 */
+ 0x2a|K_MOD, /* QZ_LSHIFT 0x38 */
+ 0x3a|K_LOCK, /* QZ_CAPSLOCK 0x39 */
+ 0x38|K_MOD, /* QZ_LALT 0x3A */
+ 0x1d|K_MOD, /* QZ_LCTRL 0x3B */
+ 0x36|K_MOD, /* QZ_RSHIFT 0x3C */
+ 0x38|K_EX|K_MOD, /* QZ_RALT 0x3D */
+ 0x1d|K_EX|K_MOD, /* QZ_RCTRL 0x3E */
+ 0, /* */
+ 0, /* */
+ 0x53, /* QZ_KP_PERIOD 0x41 */
+ 0, /* */
+ 0x37, /* QZ_KP_MULTIPLY 0x43 */
+ 0, /* */
+ 0x4e, /* QZ_KP_PLUS 0x45 */
+ 0, /* */
+ 0x45|K_LOCK, /* QZ_NUMLOCK 0x47 */
+ 0, /* */
+ 0, /* */
+ 0, /* */
+ 0x35|K_EX, /* QZ_KP_DIVIDE 0x4B */
+ 0x1c|K_EX, /* QZ_KP_ENTER 0x4C */
+ 0, /* */
+ 0x4a, /* QZ_KP_MINUS 0x4E */
+ 0, /* */
+ 0, /* */
+ 0x0d/*?*/, /* QZ_KP_EQUALS 0x51 */
+ 0x52, /* QZ_KP0 0x52 */
+ 0x4f, /* QZ_KP1 0x53 */
+ 0x50, /* QZ_KP2 0x54 */
+ 0x51, /* QZ_KP3 0x55 */
+ 0x4b, /* QZ_KP4 0x56 */
+ 0x4c, /* QZ_KP5 0x57 */
+ 0x4d, /* QZ_KP6 0x58 */
+ 0x47, /* QZ_KP7 0x59 */
+ 0, /* */
+ 0x48, /* QZ_KP8 0x5B */
+ 0x49, /* QZ_KP9 0x5C */
+ 0x7d, /* yen, | (JIS) 0x5D */
+ 0x73, /* _, ro (JIS) 0x5E */
+ 0, /* */
+ 0x3f, /* QZ_F5 0x60 */
+ 0x40, /* QZ_F6 0x61 */
+ 0x41, /* QZ_F7 0x62 */
+ 0x3d, /* QZ_F3 0x63 */
+ 0x42, /* QZ_F8 0x64 */
+ 0x43, /* QZ_F9 0x65 */
+ 0x29, /* Zen/Han (JIS) 0x66 */
+ 0x57, /* QZ_F11 0x67 */
+ 0x29, /* Zen/Han (JIS) 0x68 */
+ 0x37|K_EX, /* QZ_PRINT / F13 0x69 */
+ 0x63, /* QZ_F16 0x6A */
+ 0x46|K_LOCK, /* QZ_SCROLLOCK 0x6B */
+ 0, /* */
+ 0x44, /* QZ_F10 0x6D */
+ 0x5d|K_EX, /* */
+ 0x58, /* QZ_F12 0x6F */
+ 0, /* */
+ 0/* 0xe1,0x1d,0x45*/, /* QZ_PAUSE 0x71 */
+ 0x52|K_EX, /* QZ_INSERT / HELP 0x72 */
+ 0x47|K_EX, /* QZ_HOME 0x73 */
+ 0x49|K_EX, /* QZ_PAGEUP 0x74 */
+ 0x53|K_EX, /* QZ_DELETE 0x75 */
+ 0x3e, /* QZ_F4 0x76 */
+ 0x4f|K_EX, /* QZ_END 0x77 */
+ 0x3c, /* QZ_F2 0x78 */
+ 0x51|K_EX, /* QZ_PAGEDOWN 0x79 */
+ 0x3b, /* QZ_F1 0x7A */
+ 0x4b|K_EX, /* QZ_LEFT 0x7B */
+ 0x4d|K_EX, /* QZ_RIGHT 0x7C */
+ 0x50|K_EX, /* QZ_DOWN 0x7D */
+ 0x48|K_EX, /* QZ_UP 0x7E */
+ 0,/*0x5e|K_EX*/ /* QZ_POWER 0x7F */ /* have different break key! */
+ /* do NEVER deliver the Power
+ * scancode as e.g. Windows will
+ * handle it, @bugref{7692}. */
+};
+
+
+/** Holds whether we've connected or not. */
+static bool g_fConnectedToCGS = false;
+/** Holds the cached connection. */
+static CGSConnection g_CGSConnection;
+
+
+#ifdef USE_HID_FOR_MODIFIERS
+
+/** Holds the IO Master Port. */
+static mach_port_t g_MasterPort = NULL;
+
+/** Holds the amount of keyboards in the cache. */
+static unsigned g_cKeyboards = 0;
+/** Array of cached keyboard data. */
+static struct KeyboardCacheData
+{
+ /** The device interface. */
+ IOHIDDeviceInterface **ppHidDeviceInterface;
+ /** The queue interface. */
+ IOHIDQueueInterface **ppHidQueueInterface;
+
+ /** Cookie translation array. */
+ struct KeyboardCacheCookie
+ {
+ /** The cookie. */
+ IOHIDElementCookie Cookie;
+ /** The corresponding modifier mask. */
+ uint32_t fMask;
+ } aCookies[64];
+ /** Number of cookies in the array. */
+ unsigned cCookies;
+} g_aKeyboards[128];
+/** Holds the keyboard cache creation timestamp. */
+static uint64_t g_u64KeyboardTS = 0;
+
+/** Holds the HID queue status. */
+static bool g_fHIDQueueEnabled;
+/** Holds the current modifier mask. */
+static uint32_t g_fHIDModifierMask;
+/** Holds the old modifier mask. */
+static uint32_t g_fOldHIDModifierMask;
+
+#endif /* USE_HID_FOR_MODIFIERS */
+
+
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+
+#define VBOX_BOOL_TO_STR_STATE(x) (x) ? "ON" : "OFF"
+/** HID LEDs synchronization data: LED states. */
+typedef struct VBoxLedState_t
+{
+ /** Holds the state of NUM LOCK. */
+ bool fNumLockOn;
+ /** Holds the state of CAPS LOCK. */
+ bool fCapsLockOn;
+ /** Holds the state of SCROLL LOCK. */
+ bool fScrollLockOn;
+} VBoxLedState_t;
+
+/** HID LEDs synchronization data: keyboard states. */
+typedef struct VBoxKbdState_t
+{
+ /** Holds the reference to IOKit HID device. */
+ IOHIDDeviceRef pDevice;
+ /** Holds the LED states. */
+ VBoxLedState_t LED;
+ /** Holds the pointer to a VBoxHidsState_t instance where VBoxKbdState_t instance is stored. */
+ void *pParentContainer;
+ /** Holds the position in global storage (used to simplify CFArray navigation when removing detached device). */
+ CFIndex idxPosition;
+ /** Holds the KBD CAPS LOCK key hold timeout (some Apple keyboards only). */
+ uint64_t cCapsLockTimeout;
+ /** Holds the HID Location ID: unique for an USB device registered in the system. */
+ uint32_t idLocation;
+} VBoxKbdState_t;
+
+/** A struct that used to pass input event info from IOKit callback to a Carbon one */
+typedef struct VBoxKbdEvent_t
+{
+ VBoxKbdState_t *pKbd;
+ uint32_t iKeyCode;
+ uint64_t tsKeyDown;
+} VBoxKbdEvent_t;
+
+/** HID LEDs synchronization data: IOKit specific data. */
+typedef struct VBoxHidsState_t
+{
+ /** Holds the IOKit HID manager reference. */
+ IOHIDManagerRef hidManagerRef;
+ /** Holds the array which consists of VBoxKbdState_t elements. */
+ CFMutableArrayRef pDeviceCollection;
+ /** Holds the LED states that were stored during last broadcast and reflect a guest LED states. */
+ VBoxLedState_t guestState;
+
+ /** Holds the queue which will be appended in IOKit input callback. Carbon input callback will extract data from it. */
+ CFMutableArrayRef pFifoEventQueue;
+ /** Holds the lock for pFifoEventQueue. */
+ RTSEMMUTEX fifoEventQueueLock;
+
+ /** Holds the IOService notification reference: USB HID device matching. */
+ io_iterator_t pUsbHidDeviceMatchNotify;
+ /** Holds the IOService notification reference: USB HID general interest notifications (IOService messages). */
+ io_iterator_t pUsbHidGeneralInterestNotify;
+ /** Holds the IOService notification port reference: device match and device general interest message. */
+ IONotificationPortRef pNotificationPrortRef;
+
+ CFMachPortRef pTapRef;
+ CFRunLoopSourceRef pLoopSourceRef;
+} VBoxHidsState_t;
+
+#endif /* VBOX_WITH_KBD_LEDS_SYNC */
+
+
+unsigned DarwinKeycodeToSet1Scancode(unsigned uKeyCode)
+{
+ if (uKeyCode >= RT_ELEMENTS(g_aDarwinToSet1))
+ return 0;
+ return g_aDarwinToSet1[uKeyCode];
+}
+
+UInt32 DarwinAdjustModifierMask(UInt32 fModifiers, const void *pvCocoaEvent)
+{
+ /* Check if there is anything to adjust and perform the adjustment. */
+ if (fModifiers & (shiftKey | rightShiftKey | controlKey | rightControlKey | optionKey | rightOptionKey | cmdKey | kEventKeyModifierRightCmdKeyMask))
+ {
+#ifndef USE_HID_FOR_MODIFIERS
+ // WORKAROUND:
+ // Convert the Cocoa modifiers to Carbon ones (the Cocoa modifier
+ // definitions are tucked away in Objective-C headers, unfortunately).
+ //
+ // Update: CGEventTypes.h includes what looks like the Cocoa modifiers
+ // and the NX_* defines should be available as well. We should look
+ // into ways to intercept the CG (core graphics) events in the Carbon
+ // based setup and get rid of all this HID mess. */
+ AssertPtr(pvCocoaEvent);
+ //::darwinPrintEvent("dbg-adjMods: ", pvCocoaEvent);
+ uint32_t fAltModifiers = ::darwinEventModifierFlagsXlated(pvCocoaEvent);
+#else /* USE_HID_FOR_MODIFIERS */
+ /* Update the keyboard cache. */
+ darwinHIDKeyboardCacheUpdate();
+ const UInt32 fAltModifiers = g_fHIDModifierMask;
+#endif /* USE_HID_FOR_MODIFIERS */
+
+#ifdef DEBUG_PRINTF
+ RTPrintf("dbg-fAltModifiers=%#x fModifiers=%#x", fAltModifiers, fModifiers);
+#endif
+ if ( (fModifiers & (rightShiftKey | shiftKey))
+ && (fAltModifiers & (rightShiftKey | shiftKey)))
+ {
+ fModifiers &= ~(rightShiftKey | shiftKey);
+ fModifiers |= fAltModifiers & (rightShiftKey | shiftKey);
+ }
+
+ if ( (fModifiers & (rightControlKey | controlKey))
+ && (fAltModifiers & (rightControlKey | controlKey)))
+ {
+ fModifiers &= ~(rightControlKey | controlKey);
+ fModifiers |= fAltModifiers & (rightControlKey | controlKey);
+ }
+
+ if ( (fModifiers & (optionKey | rightOptionKey))
+ && (fAltModifiers & (optionKey | rightOptionKey)))
+ {
+ fModifiers &= ~(optionKey | rightOptionKey);
+ fModifiers |= fAltModifiers & (optionKey | rightOptionKey);
+ }
+
+ if ( (fModifiers & (cmdKey | kEventKeyModifierRightCmdKeyMask))
+ && (fAltModifiers & (cmdKey | kEventKeyModifierRightCmdKeyMask)))
+ {
+ fModifiers &= ~(cmdKey | kEventKeyModifierRightCmdKeyMask);
+ fModifiers |= fAltModifiers & (cmdKey | kEventKeyModifierRightCmdKeyMask);
+ }
+#ifdef DEBUG_PRINTF
+ RTPrintf(" -> %#x\n", fModifiers);
+#endif
+ }
+ return fModifiers;
+}
+
+unsigned DarwinModifierMaskToSet1Scancode(UInt32 fModifiers)
+{
+ unsigned uScanCode = DarwinModifierMaskToDarwinKeycode(fModifiers);
+ if (uScanCode < RT_ELEMENTS(g_aDarwinToSet1))
+ uScanCode = g_aDarwinToSet1[uScanCode];
+ else
+ Assert(uScanCode == ~0U);
+ return uScanCode;
+}
+
+unsigned DarwinModifierMaskToDarwinKeycode(UInt32 fModifiers)
+{
+ unsigned uKeyCode;
+
+ /** @todo find symbols for these keycodes... */
+ fModifiers &= shiftKey | rightShiftKey | controlKey | rightControlKey | optionKey | rightOptionKey | cmdKey
+ | kEventKeyModifierRightCmdKeyMask | kEventKeyModifierNumLockMask | alphaLock | kEventKeyModifierFnMask;
+ if (fModifiers == shiftKey)
+ uKeyCode = QZ_LSHIFT;
+ else if (fModifiers == rightShiftKey)
+ uKeyCode = QZ_RSHIFT;
+ else if (fModifiers == controlKey)
+ uKeyCode = QZ_LCTRL;
+ else if (fModifiers == rightControlKey)
+ uKeyCode = QZ_RCTRL;
+ else if (fModifiers == optionKey)
+ uKeyCode = QZ_LALT;
+ else if (fModifiers == rightOptionKey)
+ uKeyCode = QZ_RALT;
+ else if (fModifiers == cmdKey)
+ uKeyCode = QZ_LMETA;
+ else if (fModifiers == kEventKeyModifierRightCmdKeyMask /* hack */)
+ uKeyCode = QZ_RMETA;
+ else if (fModifiers == alphaLock)
+ uKeyCode = QZ_CAPSLOCK;
+ else if (fModifiers == kEventKeyModifierNumLockMask)
+ uKeyCode = QZ_NUMLOCK;
+ else if (fModifiers == kEventKeyModifierFnMask)
+ uKeyCode = QZ_FN;
+ else if (fModifiers == 0)
+ uKeyCode = 0;
+ else
+ uKeyCode = ~0U; /* multiple */
+ return uKeyCode;
+}
+
+UInt32 DarwinKeyCodeToDarwinModifierMask(unsigned uKeyCode)
+{
+ UInt32 fModifiers;
+
+ /** @todo find symbols for these keycodes... */
+ if (uKeyCode == QZ_LSHIFT)
+ fModifiers = shiftKey;
+ else if (uKeyCode == QZ_RSHIFT)
+ fModifiers = rightShiftKey;
+ else if (uKeyCode == QZ_LCTRL)
+ fModifiers = controlKey;
+ else if (uKeyCode == QZ_RCTRL)
+ fModifiers = rightControlKey;
+ else if (uKeyCode == QZ_LALT)
+ fModifiers = optionKey;
+ else if (uKeyCode == QZ_RALT)
+ fModifiers = rightOptionKey;
+ else if (uKeyCode == QZ_LMETA)
+ fModifiers = cmdKey;
+ else if (uKeyCode == QZ_RMETA)
+ fModifiers = kEventKeyModifierRightCmdKeyMask; /* hack */
+ else if (uKeyCode == QZ_CAPSLOCK)
+ fModifiers = alphaLock;
+ else if (uKeyCode == QZ_NUMLOCK)
+ fModifiers = kEventKeyModifierNumLockMask;
+ else if (uKeyCode == QZ_FN)
+ fModifiers = kEventKeyModifierFnMask;
+ else
+ fModifiers = 0;
+ return fModifiers;
+}
+
+
+void DarwinDisableGlobalHotKeys(bool fDisable)
+{
+ static unsigned s_cComplaints = 0;
+
+ /* Lazy connect to the core graphics service. */
+ if (!g_fConnectedToCGS)
+ {
+ g_CGSConnection = _CGSDefaultConnection();
+ g_fConnectedToCGS = true;
+ }
+
+ /* Get the current mode. */
+ CGSGlobalHotKeyOperatingMode enmMode = kCGSGlobalHotKeyInvalid;
+ CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmMode);
+ if ( enmMode != kCGSGlobalHotKeyEnable
+ && enmMode != kCGSGlobalHotKeyDisable
+ && enmMode != kCGSGlobalHotKeyDisableExceptUniversalAccess)
+ {
+ AssertMsgFailed(("%d\n", enmMode));
+ if (s_cComplaints++ < 32)
+ LogRel(("DarwinDisableGlobalHotKeys: Unexpected enmMode=%d\n", enmMode));
+ return;
+ }
+
+ /* Calc the new mode. */
+ if (fDisable)
+ {
+ if (enmMode != kCGSGlobalHotKeyEnable)
+ return;
+ enmMode = kCGSGlobalHotKeyDisableExceptUniversalAccess;
+ }
+ else
+ {
+ if (enmMode != kCGSGlobalHotKeyDisableExceptUniversalAccess)
+ return;
+ enmMode = kCGSGlobalHotKeyEnable;
+ }
+
+ /* Try set it and check the actual result. */
+ CGSSetGlobalHotKeyOperatingMode(g_CGSConnection, enmMode);
+ CGSGlobalHotKeyOperatingMode enmNewMode = kCGSGlobalHotKeyInvalid;
+ CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmNewMode);
+ if (enmNewMode != enmMode)
+ {
+ /* If the screensaver kicks in we should ignore failure here. */
+ AssertMsg(enmMode == kCGSGlobalHotKeyEnable, ("enmNewMode=%d enmMode=%d\n", enmNewMode, enmMode));
+ if (s_cComplaints++ < 32)
+ LogRel(("DarwinDisableGlobalHotKeys: Failed to change mode; enmNewMode=%d enmMode=%d\n", enmNewMode, enmMode));
+ }
+}
+
+
+#ifdef USE_HID_FOR_MODIFIERS
+
+/** Callback function for consuming queued events.
+ * @param pvTarget Brings the queue?
+ * @param rcIn Brings what?
+ * @param pvRefcon Brings the pointer to the keyboard cache entry.
+ * @param pvSender Brings what? */
+static void darwinQueueCallback(void *pvTarget, IOReturn rcIn, void *pvRefcon, void *pvSender)
+{
+ struct KeyboardCacheData *pKeyboardEntry = (struct KeyboardCacheData *)pvRefcon;
+ if (!pKeyboardEntry->ppHidQueueInterface)
+ return;
+ NOREF(pvTarget);
+ NOREF(rcIn);
+ NOREF(pvSender);
+
+ /* Consume the events. */
+ g_fOldHIDModifierMask = g_fHIDModifierMask;
+ for (;;)
+ {
+#ifdef DEBUG_PRINTF
+ RTPrintf("dbg-ev: "); RTStrmFlush(g_pStdOut);
+#endif
+ IOHIDEventStruct Event;
+ AbsoluteTime ZeroTime = {0,0};
+ IOReturn rc = (*pKeyboardEntry->ppHidQueueInterface)->getNextEvent(pKeyboardEntry->ppHidQueueInterface,
+ &Event, ZeroTime, 0);
+ if (rc != kIOReturnSuccess)
+ break;
+
+ /* Translate the cookie value to a modifier mask. */
+ uint32_t fMask = 0;
+ unsigned i = pKeyboardEntry->cCookies;
+ while (i-- > 0)
+ {
+ if (pKeyboardEntry->aCookies[i].Cookie == Event.elementCookie)
+ {
+ fMask = pKeyboardEntry->aCookies[i].fMask;
+ break;
+ }
+ }
+
+ /* Adjust the modifier mask. */
+ if (Event.value)
+ g_fHIDModifierMask |= fMask;
+ else
+ g_fHIDModifierMask &= ~fMask;
+#ifdef DEBUG_PRINTF
+ RTPrintf("t=%d c=%#x v=%#x cblv=%d lv=%p m=%#X\n", Event.type, Event.elementCookie, Event.value, Event.longValueSize, Event.value, fMask); RTStrmFlush(g_pStdOut);
+#endif
+ }
+#ifdef DEBUG_PRINTF
+ RTPrintf("dbg-ev: done\n"); RTStrmFlush(g_pStdOut);
+#endif
+}
+
+/* Forward declaration for darwinBruteForcePropertySearch. */
+static void darwinBruteForcePropertySearch(CFDictionaryRef DictRef, struct KeyboardCacheData *pKeyboardEntry);
+
+/** Element enumeration callback. */
+static void darwinBruteForcePropertySearchApplier(const void *pvValue, void *pvCacheEntry)
+{
+ if (CFGetTypeID(pvValue) == CFDictionaryGetTypeID())
+ darwinBruteForcePropertySearch((CFMutableDictionaryRef)pvValue, (struct KeyboardCacheData *)pvCacheEntry);
+}
+
+/** Recurses through the keyboard properties looking for certain keys. */
+static void darwinBruteForcePropertySearch(CFDictionaryRef DictRef, struct KeyboardCacheData *pKeyboardEntry)
+{
+ CFTypeRef ObjRef;
+
+ /* Check for the usage page and usage key we want. */
+ long lUsage;
+ ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementUsageKey));
+ if ( ObjRef
+ && CFGetTypeID(ObjRef) == CFNumberGetTypeID()
+ && CFNumberGetValue((CFNumberRef)ObjRef, kCFNumberLongType, &lUsage))
+ {
+ switch (lUsage)
+ {
+ case kHIDUsage_KeyboardLeftControl:
+ case kHIDUsage_KeyboardLeftShift:
+ case kHIDUsage_KeyboardLeftAlt:
+ case kHIDUsage_KeyboardLeftGUI:
+ case kHIDUsage_KeyboardRightControl:
+ case kHIDUsage_KeyboardRightShift:
+ case kHIDUsage_KeyboardRightAlt:
+ case kHIDUsage_KeyboardRightGUI:
+ {
+ long lPage;
+ ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementUsagePageKey));
+ if ( !ObjRef
+ || CFGetTypeID(ObjRef) != CFNumberGetTypeID()
+ || !CFNumberGetValue((CFNumberRef)ObjRef, kCFNumberLongType, &lPage)
+ || lPage != kHIDPage_KeyboardOrKeypad)
+ break;
+
+ if (pKeyboardEntry->cCookies >= RT_ELEMENTS(pKeyboardEntry->aCookies))
+ {
+ AssertMsgFailed(("too many cookies!\n"));
+ break;
+ }
+
+ /* Get the cookie and modifier mask. */
+ long lCookie;
+ ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementCookieKey));
+ if ( !ObjRef
+ || CFGetTypeID(ObjRef) != CFNumberGetTypeID()
+ || !CFNumberGetValue((CFNumberRef)ObjRef, kCFNumberLongType, &lCookie))
+ break;
+
+ uint32_t fMask;
+ switch (lUsage)
+ {
+ case kHIDUsage_KeyboardLeftControl : fMask = controlKey; break;
+ case kHIDUsage_KeyboardLeftShift : fMask = shiftKey; break;
+ case kHIDUsage_KeyboardLeftAlt : fMask = optionKey; break;
+ case kHIDUsage_KeyboardLeftGUI : fMask = cmdKey; break;
+ case kHIDUsage_KeyboardRightControl: fMask = rightControlKey; break;
+ case kHIDUsage_KeyboardRightShift : fMask = rightShiftKey; break;
+ case kHIDUsage_KeyboardRightAlt : fMask = rightOptionKey; break;
+ case kHIDUsage_KeyboardRightGUI : fMask = kEventKeyModifierRightCmdKeyMask; break;
+ default: AssertMsgFailed(("%ld\n",lUsage)); fMask = 0; break;
+ }
+
+ /* If we've got a queue, add the cookie to the queue. */
+ if (pKeyboardEntry->ppHidQueueInterface)
+ {
+ IOReturn rc = (*pKeyboardEntry->ppHidQueueInterface)->addElement(pKeyboardEntry->ppHidQueueInterface, (IOHIDElementCookie)lCookie, 0);
+ AssertMsg(rc == kIOReturnSuccess, ("rc=%d\n", rc));
+#ifdef DEBUG_PRINTF
+ RTPrintf("dbg-add: u=%#lx c=%#lx\n", lUsage, lCookie);
+#endif
+ }
+
+ /* Add the cookie to the keyboard entry. */
+ pKeyboardEntry->aCookies[pKeyboardEntry->cCookies].Cookie = (IOHIDElementCookie)lCookie;
+ pKeyboardEntry->aCookies[pKeyboardEntry->cCookies].fMask = fMask;
+ ++pKeyboardEntry->cCookies;
+ break;
+ }
+ }
+ }
+
+
+ /* Get the elements key and recursively iterate the elements looking for they key cookies. */
+ ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementKey));
+ if ( ObjRef
+ && CFGetTypeID(ObjRef) == CFArrayGetTypeID())
+ {
+ CFArrayRef ArrayObjRef = (CFArrayRef)ObjRef;
+ CFRange Range = {0, CFArrayGetCount(ArrayObjRef)};
+ CFArrayApplyFunction(ArrayObjRef, Range, darwinBruteForcePropertySearchApplier, pKeyboardEntry);
+ }
+}
+
+/** Creates a keyboard cache entry.
+ * @param pKeyboardEntry Brings the pointer to the entry.
+ * @param KeyboardDevice Brings the keyboard device to create the entry for. */
+static bool darwinHIDKeyboardCacheCreateEntry(struct KeyboardCacheData *pKeyboardEntry, io_object_t KeyboardDevice)
+{
+ unsigned long cRefs = 0;
+ memset(pKeyboardEntry, 0, sizeof(*pKeyboardEntry));
+
+ /* Query the HIDDeviceInterface for this HID (keyboard) object. */
+ SInt32 Score = 0;
+ IOCFPlugInInterface **ppPlugInInterface = NULL;
+ IOReturn rc = IOCreatePlugInInterfaceForService(KeyboardDevice, kIOHIDDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
+ if (rc == kIOReturnSuccess)
+ {
+ IOHIDDeviceInterface **ppHidDeviceInterface = NULL;
+ HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
+ CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
+ (LPVOID *)&ppHidDeviceInterface);
+ cRefs = (*ppPlugInInterface)->Release(ppPlugInInterface); MY_CHECK_CREFS(cRefs);
+ ppPlugInInterface = NULL;
+ if (hrc == S_OK)
+ {
+ rc = (*ppHidDeviceInterface)->open(ppHidDeviceInterface, 0);
+ if (rc == kIOReturnSuccess)
+ {
+ /* Create a removal callback. */
+ /** @todo */
+
+ /* Create the queue so we can insert elements while searching the properties. */
+ IOHIDQueueInterface **ppHidQueueInterface = (*ppHidDeviceInterface)->allocQueue(ppHidDeviceInterface);
+ if (ppHidQueueInterface)
+ {
+ rc = (*ppHidQueueInterface)->create(ppHidQueueInterface, 0, 32);
+ if (rc != kIOReturnSuccess)
+ {
+ AssertMsgFailed(("rc=%d\n", rc));
+ cRefs = (*ppHidQueueInterface)->Release(ppHidQueueInterface); MY_CHECK_CREFS(cRefs);
+ ppHidQueueInterface = NULL;
+ }
+ }
+ else
+ AssertFailed();
+ pKeyboardEntry->ppHidQueueInterface = ppHidQueueInterface;
+
+ /* Brute force getting of attributes. */
+ /** @todo read up on how to do this in a less resource intensive way! Suggestions are welcome! */
+ CFMutableDictionaryRef PropertiesRef = 0;
+ kern_return_t krc = IORegistryEntryCreateCFProperties(KeyboardDevice, &PropertiesRef, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ darwinBruteForcePropertySearch(PropertiesRef, pKeyboardEntry);
+ CFRelease(PropertiesRef);
+ }
+ else
+ AssertMsgFailed(("krc=%#x\n", krc));
+
+ if (ppHidQueueInterface)
+ {
+ /* Now install our queue callback. */
+ CFRunLoopSourceRef RunLoopSrcRef = NULL;
+ rc = (*ppHidQueueInterface)->createAsyncEventSource(ppHidQueueInterface, &RunLoopSrcRef);
+ if (rc == kIOReturnSuccess)
+ {
+ CFRunLoopRef RunLoopRef = (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop());
+ CFRunLoopAddSource(RunLoopRef, RunLoopSrcRef, kCFRunLoopDefaultMode);
+ }
+
+ /* Now install our queue callback. */
+ rc = (*ppHidQueueInterface)->setEventCallout(ppHidQueueInterface, darwinQueueCallback, ppHidQueueInterface, pKeyboardEntry);
+ if (rc != kIOReturnSuccess)
+ AssertMsgFailed(("rc=%d\n", rc));
+ }
+
+ /* Complete the new keyboard cache entry. */
+ pKeyboardEntry->ppHidDeviceInterface = ppHidDeviceInterface;
+ pKeyboardEntry->ppHidQueueInterface = ppHidQueueInterface;
+ return true;
+ }
+
+ AssertMsgFailed(("rc=%d\n", rc));
+ cRefs = (*ppHidDeviceInterface)->Release(ppHidDeviceInterface); MY_CHECK_CREFS(cRefs);
+ }
+ else
+ AssertMsgFailed(("hrc=%#x\n", hrc));
+ }
+ else
+ AssertMsgFailed(("rc=%d\n", rc));
+
+ return false;
+}
+
+/** Destroys a keyboard cache entry. */
+static void darwinHIDKeyboardCacheDestroyEntry(struct KeyboardCacheData *pKeyboardEntry)
+{
+ unsigned long cRefs;
+
+ /* Destroy the queue. */
+ if (pKeyboardEntry->ppHidQueueInterface)
+ {
+ IOHIDQueueInterface **ppHidQueueInterface = pKeyboardEntry->ppHidQueueInterface;
+ pKeyboardEntry->ppHidQueueInterface = NULL;
+
+ /* Stop it just in case we haven't done so. doesn't really matter I think. */
+ (*ppHidQueueInterface)->stop(ppHidQueueInterface);
+
+ /* Deal with the run loop source. */
+ CFRunLoopSourceRef RunLoopSrcRef = (*ppHidQueueInterface)->getAsyncEventSource(ppHidQueueInterface);
+ if (RunLoopSrcRef)
+ {
+ CFRunLoopRef RunLoopRef = (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop());
+ CFRunLoopRemoveSource(RunLoopRef, RunLoopSrcRef, kCFRunLoopDefaultMode);
+
+ CFRelease(RunLoopSrcRef);
+ }
+
+ /* Dispose of and release the queue. */
+ (*ppHidQueueInterface)->dispose(ppHidQueueInterface);
+ cRefs = (*ppHidQueueInterface)->Release(ppHidQueueInterface); MY_CHECK_CREFS(cRefs);
+ }
+
+ /* Release the removal hook? */
+ /** @todo */
+
+ /* Close and release the device interface. */
+ if (pKeyboardEntry->ppHidDeviceInterface)
+ {
+ IOHIDDeviceInterface **ppHidDeviceInterface = pKeyboardEntry->ppHidDeviceInterface;
+ pKeyboardEntry->ppHidDeviceInterface = NULL;
+
+ (*ppHidDeviceInterface)->close(ppHidDeviceInterface);
+ cRefs = (*ppHidDeviceInterface)->Release(ppHidDeviceInterface); MY_CHECK_CREFS(cRefs);
+ }
+}
+
+/** Zap the keyboard cache. */
+static void darwinHIDKeyboardCacheZap(void)
+{
+ /* Release the old cache data first. */
+ while (g_cKeyboards > 0)
+ {
+ unsigned i = --g_cKeyboards;
+ darwinHIDKeyboardCacheDestroyEntry(&g_aKeyboards[i]);
+ }
+}
+
+/** Updates the cached keyboard data.
+ * @todo The current implementation is very brute force...
+ * Rewrite it so that it doesn't flush the cache completely but simply checks whether
+ * anything has changed in the HID config. With any luck, there might even be a callback
+ * or something we can poll for HID config changes...
+ * setRemovalCallback() is a start... */
+static void darwinHIDKeyboardCacheDoUpdate(void)
+{
+ g_u64KeyboardTS = RTTimeMilliTS();
+
+ /* Dispense with the old cache data. */
+ darwinHIDKeyboardCacheZap();
+
+ /* Open the master port on the first invocation. */
+ if (!g_MasterPort)
+ {
+ kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &g_MasterPort);
+ AssertReturnVoid(krc == KERN_SUCCESS);
+ }
+
+ /* Create a matching dictionary for searching for keyboards devices. */
+ static const UInt32 s_Page = kHIDPage_GenericDesktop;
+ static const UInt32 s_Usage = kHIDUsage_GD_Keyboard;
+ CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOHIDDeviceKey);
+ AssertReturnVoid(RefMatchingDict);
+ CFDictionarySetValue(RefMatchingDict, CFSTR(kIOHIDPrimaryUsagePageKey),
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &s_Page));
+ CFDictionarySetValue(RefMatchingDict, CFSTR(kIOHIDPrimaryUsageKey),
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &s_Usage));
+
+ /* Perform the search and get a collection of keyboard devices. */
+ io_iterator_t Keyboards = NULL;
+ IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &Keyboards);
+ AssertMsgReturnVoid(rc == kIOReturnSuccess, ("rc=%d\n", rc));
+ RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
+
+ /* Enumerate the keyboards and query the cache data. */
+ unsigned i = 0;
+ io_object_t KeyboardDevice;
+ while ( i < RT_ELEMENTS(g_aKeyboards)
+ && (KeyboardDevice = IOIteratorNext(Keyboards)) != 0)
+ {
+ if (darwinHIDKeyboardCacheCreateEntry(&g_aKeyboards[i], KeyboardDevice))
+ i++;
+ IOObjectRelease(KeyboardDevice);
+ }
+ g_cKeyboards = i;
+
+ IOObjectRelease(Keyboards);
+}
+
+/** Updates the keyboard cache if it's time to do it again. */
+static void darwinHIDKeyboardCacheUpdate(void)
+{
+ if ( !g_cKeyboards
+ /*|| g_u64KeyboardTS - RTTimeMilliTS() > 7500*/ /* 7.5sec */)
+ darwinHIDKeyboardCacheDoUpdate();
+}
+
+/** Queries the modifier keys from the (IOKit) HID Manager. */
+static UInt32 darwinQueryHIDModifiers(void)
+{
+ /* Iterate thru the keyboards collecting their modifier masks. */
+ UInt32 fHIDModifiers = 0;
+ unsigned i = g_cKeyboards;
+ while (i-- > 0)
+ {
+ IOHIDDeviceInterface **ppHidDeviceInterface = g_aKeyboards[i].ppHidDeviceInterface;
+ if (!ppHidDeviceInterface)
+ continue;
+
+ unsigned j = g_aKeyboards[i].cCookies;
+ while (j-- > 0)
+ {
+ IOHIDEventStruct HidEvent;
+ IOReturn rc = (*ppHidDeviceInterface)->getElementValue(ppHidDeviceInterface,
+ g_aKeyboards[i].aCookies[j].Cookie,
+ &HidEvent);
+ if (rc == kIOReturnSuccess)
+ {
+ if (HidEvent.value)
+ fHIDModifiers |= g_aKeyboards[i].aCookies[j].fMask;
+ }
+ else
+ AssertMsgFailed(("rc=%#x\n", rc));
+ }
+ }
+
+ return fHIDModifiers;
+}
+
+#endif /* USE_HID_FOR_MODIFIERS */
+
+
+void DarwinGrabKeyboard(bool fGlobalHotkeys)
+{
+ LogFlow(("DarwinGrabKeyboard: fGlobalHotkeys=%RTbool\n", fGlobalHotkeys));
+
+#ifdef USE_HID_FOR_MODIFIERS
+ /* Update the keyboard cache. */
+ darwinHIDKeyboardCacheUpdate();
+
+ /* Start the keyboard queues and query the current mask. */
+ g_fHIDQueueEnabled = true;
+
+ unsigned i = g_cKeyboards;
+ while (i-- > 0)
+ {
+ if (g_aKeyboards[i].ppHidQueueInterface)
+ (*g_aKeyboards[i].ppHidQueueInterface)->start(g_aKeyboards[i].ppHidQueueInterface);
+ }
+
+ g_fHIDModifierMask = darwinQueryHIDModifiers();
+#endif /* USE_HID_FOR_MODIFIERS */
+
+ /* Disable hotkeys if requested. */
+ if (fGlobalHotkeys)
+ DarwinDisableGlobalHotKeys(true);
+}
+
+void DarwinReleaseKeyboard()
+{
+ LogFlow(("DarwinReleaseKeyboard\n"));
+
+ /* Re-enable hotkeys. */
+ DarwinDisableGlobalHotKeys(false);
+
+#ifdef USE_HID_FOR_MODIFIERS
+ /* Stop and drain the keyboard queues. */
+ g_fHIDQueueEnabled = false;
+
+#if 0
+ unsigned i = g_cKeyboards;
+ while (i-- > 0)
+ {
+ if (g_aKeyboards[i].ppHidQueueInterface)
+ {
+
+ (*g_aKeyboards[i].ppHidQueueInterface)->stop(g_aKeyboards[i].ppHidQueueInterface);
+
+ /* drain it */
+ IOReturn rc;
+ unsigned c = 0;
+ do
+ {
+ IOHIDEventStruct Event;
+ AbsoluteTime MaxTime = {0,0};
+ rc = (*g_aKeyboards[i].ppHidQueueInterface)->getNextEvent(g_aKeyboards[i].ppHidQueueInterface,
+ &Event, MaxTime, 0);
+ } while ( rc == kIOReturnSuccess
+ && c++ < 32);
+ }
+ }
+#else
+ /* Kill the keyboard cache. */
+ darwinHIDKeyboardCacheZap();
+#endif
+
+ /* Clear the modifier mask. */
+ g_fHIDModifierMask = 0;
+#endif /* USE_HID_FOR_MODIFIERS */
+}
+
+
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+
+/** Prepares dictionary that will be used to match HID LED device(s) while discovering. */
+static CFDictionaryRef darwinQueryLedDeviceMatchingDictionary()
+{
+ CFDictionaryRef deviceMatchingDictRef;
+
+ // Use two (key, value) pairs:
+ // - (kIOHIDDeviceUsagePageKey, kHIDPage_GenericDesktop),
+ // - (kIOHIDDeviceUsageKey, kHIDUsage_GD_Keyboard). */
+
+ CFNumberRef usagePageKeyCFNumberRef; int usagePageKeyCFNumberValue = kHIDPage_GenericDesktop;
+ CFNumberRef usageKeyCFNumberRef; int usageKeyCFNumberValue = kHIDUsage_GD_Keyboard;
+
+ usagePageKeyCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePageKeyCFNumberValue);
+ if (usagePageKeyCFNumberRef)
+ {
+ usageKeyCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usageKeyCFNumberValue);
+ if (usageKeyCFNumberRef)
+ {
+ CFStringRef dictionaryKeys[2] = { CFSTR(kIOHIDDeviceUsagePageKey), CFSTR(kIOHIDDeviceUsageKey) };
+ CFNumberRef dictionaryVals[2] = { usagePageKeyCFNumberRef, usageKeyCFNumberRef };
+
+ deviceMatchingDictRef = CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **)dictionaryKeys,
+ (const void **)dictionaryVals,
+ 2, /** two (key, value) pairs */
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (deviceMatchingDictRef)
+ {
+ CFRelease(usageKeyCFNumberRef);
+ CFRelease(usagePageKeyCFNumberRef);
+
+ return deviceMatchingDictRef;
+ }
+
+ CFRelease(usageKeyCFNumberRef);
+ }
+
+ CFRelease(usagePageKeyCFNumberRef);
+ }
+
+ return NULL;
+}
+
+/** Prepare dictionary that will be used to match HID LED device element(s) while discovering. */
+static CFDictionaryRef darwinQueryLedElementMatchingDictionary()
+{
+ CFDictionaryRef elementMatchingDictRef;
+
+ // Use only one (key, value) pair to match LED device element:
+ // - (kIOHIDElementUsagePageKey, kHIDPage_LEDs). */
+
+ CFNumberRef usagePageKeyCFNumberRef; int usagePageKeyCFNumberValue = kHIDPage_LEDs;
+
+ usagePageKeyCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePageKeyCFNumberValue);
+ if (usagePageKeyCFNumberRef)
+ {
+ CFStringRef dictionaryKeys[1] = { CFSTR(kIOHIDElementUsagePageKey), };
+ CFNumberRef dictionaryVals[1] = { usagePageKeyCFNumberRef, };
+
+ elementMatchingDictRef = CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **)dictionaryKeys,
+ (const void **)dictionaryVals,
+ 1, /** one (key, value) pair */
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (elementMatchingDictRef)
+ {
+ CFRelease(usagePageKeyCFNumberRef);
+ return elementMatchingDictRef;
+ }
+
+ CFRelease(usagePageKeyCFNumberRef);
+ }
+
+ return NULL;
+}
+
+/** Turn ON or OFF a particular LED. */
+static int darwinLedElementSetValue(IOHIDDeviceRef hidDevice, IOHIDElementRef element, bool fEnabled)
+{
+ IOHIDValueRef valueRef;
+ IOReturn rc = kIOReturnError;
+
+ /* Try to resume suspended keyboard devices. Abort if failed in order to avoid GUI freezes. */
+ int rc1 = SUPR3ResumeSuspendedKeyboards();
+ if (RT_FAILURE(rc1))
+ return rc1;
+
+ valueRef = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, element, 0, (fEnabled) ? 1 : 0);
+ if (valueRef)
+ {
+ rc = IOHIDDeviceSetValue(hidDevice, element, valueRef);
+ if (rc != kIOReturnSuccess)
+ LogRel2(("Warning! Something went wrong in attempt to turn %s HID device led (error %d)!\n", ((fEnabled) ? "on" : "off"), rc));
+ else
+ LogRel2(("Led (%d) is turned %s\n", (int)IOHIDElementGetUsage(element), ((fEnabled) ? "on" : "off")));
+
+ CFRelease(valueRef);
+ }
+
+ return rc;
+}
+
+/** Get state of a particular led. */
+static int darwinLedElementGetValue(IOHIDDeviceRef hidDevice, IOHIDElementRef element, bool *fEnabled)
+{
+ /* Try to resume suspended keyboard devices. Abort if failed in order to avoid GUI freezes. */
+ int rc1 = SUPR3ResumeSuspendedKeyboards();
+ if (RT_FAILURE(rc1))
+ return rc1;
+
+ IOHIDValueRef valueRef;
+ IOReturn rc = IOHIDDeviceGetValue(hidDevice, element, &valueRef);
+ if (rc == kIOReturnSuccess)
+ {
+ CFIndex integerValue = IOHIDValueGetIntegerValue(valueRef);
+ switch (integerValue)
+ {
+ case 0:
+ *fEnabled = false;
+ break;
+ case 1:
+ *fEnabled = true;
+ break;
+ default:
+ rc = kIOReturnError;
+ }
+
+ /*CFRelease(valueRef); - IOHIDDeviceGetValue does not return a reference, so no need to release it. */
+ }
+
+ return rc;
+}
+
+/** Set corresponding states from NumLock, CapsLock and ScrollLock leds. */
+static int darwinSetDeviceLedsState(IOHIDDeviceRef hidDevice, CFDictionaryRef elementMatchingDict,
+ bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+ CFArrayRef matchingElementsArrayRef;
+ int rc2 = 0;
+
+ matchingElementsArrayRef = IOHIDDeviceCopyMatchingElements(hidDevice, elementMatchingDict, kIOHIDOptionsTypeNone);
+ if (matchingElementsArrayRef)
+ {
+ CFIndex cElements = CFArrayGetCount(matchingElementsArrayRef);
+
+ /* Cycle though all the elements we found */
+ for (CFIndex i = 0; i < cElements; i++)
+ {
+ IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(matchingElementsArrayRef, i);
+ uint32_t usage = IOHIDElementGetUsage(element);
+ int rc = 0;
+
+ switch (usage)
+ {
+ case kHIDUsage_LED_NumLock:
+ rc = darwinLedElementSetValue(hidDevice, element, fNumLockOn);
+ break;
+
+ case kHIDUsage_LED_CapsLock:
+ rc = darwinLedElementSetValue(hidDevice, element, fCapsLockOn);
+ break;
+ case kHIDUsage_LED_ScrollLock:
+ rc = darwinLedElementSetValue(hidDevice, element, fScrollLockOn);
+ break;
+ }
+ if (rc != 0)
+ {
+ LogRel2(("Failed to set led (%d) state\n", (int)IOHIDElementGetUsage(element)));
+ rc2 = kIOReturnError;
+ }
+ }
+
+ CFRelease(matchingElementsArrayRef);
+ }
+
+ return rc2;
+}
+
+/** Get corresponding states for NumLock, CapsLock and ScrollLock leds. */
+static int darwinGetDeviceLedsState(IOHIDDeviceRef hidDevice, CFDictionaryRef elementMatchingDict,
+ bool *fNumLockOn, bool *fCapsLockOn, bool *fScrollLockOn)
+{
+ CFArrayRef matchingElementsArrayRef;
+ int rc2 = 0;
+
+ matchingElementsArrayRef = IOHIDDeviceCopyMatchingElements(hidDevice, elementMatchingDict, kIOHIDOptionsTypeNone);
+ if (matchingElementsArrayRef)
+ {
+ CFIndex cElements = CFArrayGetCount(matchingElementsArrayRef);
+
+ /* Cycle though all the elements we found */
+ for (CFIndex i = 0; i < cElements; i++)
+ {
+ IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(matchingElementsArrayRef, i);
+ uint32_t usage = IOHIDElementGetUsage(element);
+ int rc = 0;
+
+ switch (usage)
+ {
+ case kHIDUsage_LED_NumLock:
+ rc = darwinLedElementGetValue(hidDevice, element, fNumLockOn);
+ break;
+
+ case kHIDUsage_LED_CapsLock:
+ rc = darwinLedElementGetValue(hidDevice, element, fCapsLockOn);
+ break;
+ case kHIDUsage_LED_ScrollLock:
+ rc = darwinLedElementGetValue(hidDevice, element, fScrollLockOn);
+ break;
+ }
+ if (rc != 0)
+ {
+ LogRel2(("Failed to get led (%d) state\n", (int)IOHIDElementGetUsage(element)));
+ rc2 = kIOReturnError;
+ }
+ }
+
+ CFRelease(matchingElementsArrayRef);
+ }
+
+ return rc2;
+}
+
+/** Get integer property of HID device */
+static uint32_t darwinQueryIntProperty(IOHIDDeviceRef pHidDeviceRef, CFStringRef pProperty)
+{
+ CFTypeRef pNumberRef;
+ uint32_t value = 0;
+
+ AssertReturn(pHidDeviceRef, 0);
+ AssertReturn(pProperty, 0);
+
+ pNumberRef = IOHIDDeviceGetProperty(pHidDeviceRef, pProperty);
+ if (pNumberRef)
+ {
+ if (CFGetTypeID(pNumberRef) == CFNumberGetTypeID())
+ {
+ if (CFNumberGetValue((CFNumberRef)pNumberRef, kCFNumberSInt32Type, &value))
+ return value;
+ }
+ }
+
+ return 0;
+}
+
+/** Get HID Vendor ID */
+static uint32_t darwinHidVendorId(IOHIDDeviceRef pHidDeviceRef)
+{
+ return darwinQueryIntProperty(pHidDeviceRef, CFSTR(kIOHIDVendorIDKey));
+}
+
+/** Get HID Product ID */
+static uint32_t darwinHidProductId(IOHIDDeviceRef pHidDeviceRef)
+{
+ return darwinQueryIntProperty(pHidDeviceRef, CFSTR(kIOHIDProductIDKey));
+}
+
+/** Get HID Location ID */
+static uint32_t darwinHidLocationId(IOHIDDeviceRef pHidDeviceRef)
+{
+ return darwinQueryIntProperty(pHidDeviceRef, CFSTR(kIOHIDLocationIDKey));
+}
+
+/** Some keyboard devices might freeze after LEDs manipulation. We filter out such devices here.
+ * In the list below, devices that known to have such issues. If you want to add new device,
+ * then add it here. Currently, we only filter devices by Vendor ID.
+ * In future it might make sense to take Product ID into account as well. */
+static bool darwinHidDeviceSupported(IOHIDDeviceRef pHidDeviceRef)
+{
+#ifndef VBOX_WITHOUT_KBD_LEDS_SYNC_FILTERING
+ bool fSupported = true;
+ uint32_t vendorId = darwinHidVendorId(pHidDeviceRef);
+ uint32_t productId = darwinHidProductId(pHidDeviceRef);
+
+ if (vendorId == 0x05D5) /* Genius */
+ {
+ if (productId == 0x8001) /* GK-04008/C keyboard */
+ fSupported = false;
+ }
+ if (vendorId == 0xE6A) /* Megawin Technology */
+ {
+ if (productId == 0x6001) /* Japanese flexible keyboard */
+ fSupported = false;
+ }
+
+ LogRel2(("HID device [VendorID=0x%X, ProductId=0x%X] %s in the list of supported devices.\n", vendorId, productId, (fSupported ? "is" : "is not")));
+
+ return fSupported;
+#else /* !VBOX_WITH_KBD_LEDS_SYNC_FILTERING */
+ return true;
+#endif
+}
+
+/** IOKit key press callback helper: take care about key-down event.
+ * This code should be executed within a critical section under pHidState->fifoEventQueueLock. */
+static void darwinHidInputCbKeyDown(VBoxKbdState_t *pKbd, uint32_t iKeyCode, VBoxHidsState_t *pHidState)
+{
+ VBoxKbdEvent_t *pEvent = (VBoxKbdEvent_t *)malloc(sizeof(VBoxKbdEvent_t));
+
+ if (pEvent)
+ {
+ /* Queue Key-Down event. */
+ pEvent->tsKeyDown = RTTimeSystemMilliTS();
+ pEvent->pKbd = pKbd;
+ pEvent->iKeyCode = iKeyCode;
+
+ CFArrayAppendValue(pHidState->pFifoEventQueue, (void *)pEvent);
+
+ LogRel2(("IOHID: KBD %d: Modifier Key-Down event\n", (int)pKbd->idxPosition));
+ }
+ else
+ LogRel2(("IOHID: Modifier Key-Up event. Unable to find memory for KBD %d event\n", (int)pKbd->idxPosition));
+}
+
+/** IOkit and Carbon key press callbacks helper: CapsLock timeout checker.
+ *
+ * Returns FALSE if CAPS LOCK timeout not occurred and its state still was not switched (Apple kbd).
+ * Returns TRUE if CAPS LOCK timeout occurred and its state was switched (Apple kbd).
+ * Returns TRUE for non-Apple kbd. */
+static bool darwinKbdCapsEventMatches(VBoxKbdEvent_t *pEvent, bool fCapsLed)
+{
+ // CapsLock timeout is only applicable if conditions
+ // below are satisfied:
+ //
+ // a) Key pressed on Apple keyboard
+ // b) CapsLed is OFF at the moment when CapsLock key is pressed
+
+ bool fAppleKeyboard = (pEvent->pKbd->cCapsLockTimeout > 0);
+
+ /* Apple keyboard */
+ if (fAppleKeyboard && !fCapsLed)
+ {
+ uint64_t tsDiff = RTTimeSystemMilliTS() - pEvent->tsKeyDown;
+ if (tsDiff < pEvent->pKbd->cCapsLockTimeout)
+ return false;
+ }
+
+ return true;
+}
+
+/** IOKit key press callback helper: take care about key-up event.
+ * This code should be executed within a critical section under pHidState->fifoEventQueueLock. */
+static void darwinHidInputCbKeyUp(VBoxKbdState_t *pKbd, uint32_t iKeyCode, VBoxHidsState_t *pHidState)
+{
+ CFIndex iQueue = 0;
+ VBoxKbdEvent_t *pEvent = NULL;
+
+ // Key-up event assumes that key-down event occured previously. If so, an event
+ // data should be in event queue. Attempt to find it.
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pFifoEventQueue); i++)
+ {
+ VBoxKbdEvent_t *pCachedEvent = (VBoxKbdEvent_t *)CFArrayGetValueAtIndex(pHidState->pFifoEventQueue, i);
+
+ if (pCachedEvent && pCachedEvent->pKbd == pKbd && pCachedEvent->iKeyCode == iKeyCode)
+ {
+ pEvent = pCachedEvent;
+ iQueue = i;
+ break;
+ }
+ }
+
+ /* Event found. */
+ if (pEvent)
+ {
+ // NUM LOCK should not have timeout and its press should immidiately trigger Carbon callback.
+ // Therefore, if it is still in queue this is a problem because it was not handled by Carbon callback.
+ // This mean that NUM LOCK is most likely out of sync.
+ if (iKeyCode == kHIDUsage_KeypadNumLock)
+ {
+ LogRel2(("IOHID: KBD %d: Modifier Key-Up event. Key-Down event was not habdled by Carbon callback. "
+ "NUM LOCK is most likely out of sync\n", (int)pKbd->idxPosition));
+ }
+ else if (iKeyCode == kHIDUsage_KeyboardCapsLock)
+ {
+ // If CAPS LOCK key-press event still not match CAPS LOCK timeout criteria, Carbon callback
+ // should not be triggered for this event at all. Threfore, event should be removed from queue.
+ if (!darwinKbdCapsEventMatches(pEvent, pHidState->guestState.fCapsLockOn))
+ {
+ CFArrayRemoveValueAtIndex(pHidState->pFifoEventQueue, iQueue);
+
+ LogRel2(("IOHID: KBD %d: Modifier Key-Up event on Apple keyboard. Key-Down event was triggered %llu ms "
+ "ago. Carbon event should not be triggered, removed from queue\n", (int)pKbd->idxPosition,
+ RTTimeSystemMilliTS() - pEvent->tsKeyDown));
+ free(pEvent);
+ }
+ else
+ {
+ // CAPS LOCK key-press event matches to CAPS LOCK timeout criteria and still present in queue.
+ // This might mean that Carbon callback was triggered for this event, but cached keyboard state was not updated.
+ // It also might mean that Carbon callback still was not triggered, but it will be soon.
+ // Threfore, CAPS LOCK might be out of sync.
+ LogRel2(("IOHID: KBD %d: Modifier Key-Up event. Key-Down event was triggered %llu ms "
+ "ago and still was not handled by Carbon callback. CAPS LOCK might out of sync if "
+ "Carbon will not handle this\n", (int)pKbd->idxPosition, RTTimeSystemMilliTS() - pEvent->tsKeyDown));
+ }
+ }
+ }
+ else
+ LogRel2(("IOHID: KBD %d: Modifier Key-Up event. Modifier state change was "
+ "successfully handled by Carbon callback\n", (int)pKbd->idxPosition));
+}
+
+/** IOKit key press callback. Triggered before Carbon callback. We remember which keyboard produced a keypress here. */
+static void darwinHidInputCallback(void *pData, IOReturn unused, void *unused1, IOHIDValueRef pValueRef)
+{
+ (void)unused;
+ (void)unused1;
+
+ AssertReturnVoid(pValueRef);
+
+ IOHIDElementRef pElementRef = IOHIDValueGetElement(pValueRef);
+ AssertReturnVoid(pElementRef);
+
+ uint32_t usage = IOHIDElementGetUsage(pElementRef);
+
+ if (IOHIDElementGetUsagePage(pElementRef) == kHIDPage_KeyboardOrKeypad) /* Keyboard or keypad event */
+ if (usage == kHIDUsage_KeyboardCapsLock || /* CapsLock key has been pressed */
+ usage == kHIDUsage_KeypadNumLock) /* ... or NumLock key has been pressed */
+ {
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)pData;
+
+ if (pKbd && pKbd->pParentContainer)
+ {
+ bool fKeyDown = (IOHIDValueGetIntegerValue(pValueRef) == 1);
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer;
+
+ AssertReturnVoid(pHidState);
+
+ if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
+ return ;
+
+ /* Handle corresponding event. */
+ if (fKeyDown)
+ darwinHidInputCbKeyDown(pKbd, usage, pHidState);
+ else
+ darwinHidInputCbKeyUp(pKbd, usage, pHidState);
+
+ RTSemMutexRelease(pHidState->fifoEventQueueLock);
+ }
+ else
+ LogRel2(("IOHID: No KBD: A modifier key has been pressed\n"));
+ }
+}
+
+/** Carbon key press callback helper: find last occured KBD event in queue
+ * (ignoring those events which do not match CAPS LOCK timeout criteria).
+ * Once event found, it is removed from queue. This code should be executed
+ * within a critical section under pHidState->fifoEventQueueLock. */
+static VBoxKbdEvent_t *darwinCarbonCbFindEvent(VBoxHidsState_t *pHidState)
+{
+ VBoxKbdEvent_t *pEvent = NULL;
+
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pFifoEventQueue); i++)
+ {
+ pEvent = (VBoxKbdEvent_t *)CFArrayGetValueAtIndex(pHidState->pFifoEventQueue, i);
+
+ /* Paranoia: skip potentially dangerous data items. */
+ if (!pEvent || !pEvent->pKbd) continue;
+
+ if ( pEvent->iKeyCode == kHIDUsage_KeypadNumLock
+ || (pEvent->iKeyCode == kHIDUsage_KeyboardCapsLock && darwinKbdCapsEventMatches(pEvent, pHidState->guestState.fCapsLockOn)))
+ {
+ /* Found one. Remove it from queue. */
+ CFArrayRemoveValueAtIndex(pHidState->pFifoEventQueue, i);
+
+ LogRel2(("CARBON: Found event in queue: %d (KBD %d, tsKeyDown=%llu, pressed %llu ms ago)\n", (int)i,
+ (int)pEvent->pKbd->idxPosition, pEvent->tsKeyDown, RTTimeSystemMilliTS() - pEvent->tsKeyDown));
+
+ break;
+ }
+ else
+ LogRel2(("CARBON: Skip keyboard event from KBD %d, key pressed %llu ms ago\n",
+ (int)pEvent->pKbd->idxPosition, RTTimeSystemMilliTS() - pEvent->tsKeyDown));
+
+ pEvent = NULL;
+ }
+
+ return pEvent;
+}
+
+/** Carbon key press callback. Triggered after IOKit callback. */
+static CGEventRef darwinCarbonCallback(CGEventTapProxy unused, CGEventType unused1, CGEventRef pEventRef, void *pData)
+{
+ (void)unused;
+ (void)unused1;
+
+ CGEventFlags fMask = CGEventGetFlags(pEventRef);
+ bool fCaps = (bool)(fMask & NX_ALPHASHIFTMASK);
+ bool fNum = (bool)(fMask & NX_NUMERICPADMASK);
+ CGKeyCode key = CGEventGetIntegerValueField(pEventRef, kCGKeyboardEventKeycode);
+
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pData;
+ AssertReturn(pHidState, pEventRef);
+
+ if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
+ return pEventRef;
+
+ if (key == kHIDUsage_KeyboardCapsLock ||
+ key == kHIDUsage_KeypadNumLock)
+ {
+ /* Attempt to find an event queued by IOKit callback. */
+ VBoxKbdEvent_t *pEvent = darwinCarbonCbFindEvent(pHidState);
+ if (pEvent)
+ {
+ VBoxKbdState_t *pKbd = pEvent->pKbd;
+
+ LogRel2(("CARBON: KBD %d: caps=%s, num=%s. tsKeyDown=%llu, tsKeyUp=%llu [tsDiff=%llu ms]. %d events in queue.\n",
+ (int)pKbd->idxPosition, VBOX_BOOL_TO_STR_STATE(fCaps), VBOX_BOOL_TO_STR_STATE(fNum),
+ pEvent->tsKeyDown, RTTimeSystemMilliTS(), RTTimeSystemMilliTS() - pEvent->tsKeyDown,
+ CFArrayGetCount(pHidState->pFifoEventQueue)));
+
+ pKbd->LED.fCapsLockOn = fCaps;
+ pKbd->LED.fNumLockOn = fNum;
+
+ /* Silently resync last touched KBD device */
+ if (pHidState)
+ {
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ (void)darwinSetDeviceLedsState(pKbd->pDevice,
+ elementMatchingDict,
+ pHidState->guestState.fNumLockOn,
+ pHidState->guestState.fCapsLockOn,
+ pHidState->guestState.fScrollLockOn);
+
+ CFRelease(elementMatchingDict);
+ }
+ }
+
+ free(pEvent);
+ }
+ else
+ LogRel2(("CARBON: No KBD to take care when modifier key has been pressed: caps=%s, num=%s (%d events in queue)\n",
+ VBOX_BOOL_TO_STR_STATE(fCaps), VBOX_BOOL_TO_STR_STATE(fNum), CFArrayGetCount(pHidState->pFifoEventQueue)));
+ }
+
+ RTSemMutexRelease(pHidState->fifoEventQueueLock);
+
+ return pEventRef;
+}
+
+/** Helper function to obtain interface for IOUSBInterface IOService. */
+static IOUSBDeviceInterface ** darwinQueryUsbHidInterfaceInterface(io_service_t service)
+{
+ kern_return_t rc;
+ IOCFPlugInInterface **ppPluginInterface = NULL;
+ SInt32 iScore;
+
+ rc = IOCreatePlugInInterfaceForService(service, kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &ppPluginInterface, &iScore);
+
+ if (rc == kIOReturnSuccess && ppPluginInterface != NULL)
+ {
+ IOUSBDeviceInterface **ppUsbDeviceInterface = NULL;
+
+ rc = (*ppPluginInterface)->QueryInterface(ppPluginInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+ (LPVOID *)&ppUsbDeviceInterface);
+ IODestroyPlugInInterface(ppPluginInterface);
+
+ if (rc == kIOReturnSuccess && ppUsbDeviceInterface != NULL)
+ return ppUsbDeviceInterface;
+ else
+ LogRel2(("Failed to query plugin interface for USB device\n"));
+
+ }
+ else
+ LogRel2(("Failed to create plugin interface for USB device\n"));
+
+ return NULL;
+}
+
+/** Helper function for IOUSBInterface IOService general interest notification callback: resync LEDs. */
+static void darwinUsbHidResyncLeds(VBoxKbdState_t *pKbd)
+{
+ AssertReturnVoid(pKbd);
+
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer;
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ LogRel2(("Do HID device resync at location 0x%X \n", pKbd->idLocation));
+ (void)darwinSetDeviceLedsState(pKbd->pDevice, elementMatchingDict,
+ pHidState->guestState.fNumLockOn, pHidState->guestState.fCapsLockOn, pHidState->guestState.fScrollLockOn);
+ CFRelease(elementMatchingDict);
+ }
+}
+
+/** IOUSBInterface IOService general interest notification callback. When we receive it, we do
+ * silently resync kbd which has just changed its state. */
+static void darwinUsbHidGeneralInterestCb(void *pData, io_service_t unused1, natural_t msg, void *unused2)
+{
+ NOREF(unused1);
+ NOREF(unused2);
+
+ AssertReturnVoid(pData);
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)pData;
+
+ switch (msg)
+ {
+ case kIOUSBMessagePortHasBeenSuspended:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessagePortHasBeenSuspended for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ break;
+ }
+
+ case kIOUSBMessagePortHasBeenResumed:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessagePortHasBeenResumed for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ break;
+ }
+
+ case kIOUSBMessagePortHasBeenReset:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessagePortHasBeenReset for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ darwinUsbHidResyncLeds(pKbd);
+ break;
+ }
+
+ case kIOUSBMessageCompositeDriverReconfigured:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessageCompositeDriverReconfigured for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ break;
+ }
+
+ case kIOMessageServiceWasClosed:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOMessageServiceWasClosed for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ break;
+ }
+
+ default:
+ LogRel2(("IOUSBInterface IOService general interest notification 0x%X for KBD %d (Location ID: 0x%X)\n",
+ msg, (int)(pKbd->idxPosition), pKbd->idLocation));
+ }
+}
+
+/** Get pre-cached KBD device by its Location ID. */
+static VBoxKbdState_t *darwinUsbHidQueryKbdByLocationId(uint32_t idLocation, VBoxHidsState_t *pHidState)
+{
+ AssertReturn(pHidState, NULL);
+
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pDeviceCollection); i++)
+ {
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)CFArrayGetValueAtIndex(pHidState->pDeviceCollection, i);
+ if (pKbd && pKbd->idLocation == idLocation)
+ {
+ LogRel2(("Lookup USB HID Device by location ID 0x%X: found match\n", idLocation));
+ return pKbd;
+ }
+ }
+
+ LogRel2(("Lookup USB HID Device by location ID 0x%X: no matches found:\n", idLocation));
+
+ return NULL;
+}
+
+/** IOUSBInterface IOService match notification callback: issued when IOService instantinates.
+ * We subscribe to general interest notifications for available IOServices here. */
+static void darwinUsbHidDeviceMatchCb(void *pData, io_iterator_t iter)
+{
+ AssertReturnVoid(pData);
+
+ io_service_t service;
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pData;
+
+ while ((service = IOIteratorNext(iter)))
+ {
+ kern_return_t rc;
+
+ IOUSBDeviceInterface **ppUsbDeviceInterface = darwinQueryUsbHidInterfaceInterface(service);
+
+ if (ppUsbDeviceInterface)
+ {
+ uint8_t idDeviceClass, idDeviceSubClass;
+ UInt32 idLocation;
+
+ rc = (*ppUsbDeviceInterface)->GetLocationID (ppUsbDeviceInterface, &idLocation); AssertMsg(rc == 0, ("Failed to get Location ID"));
+ rc = (*ppUsbDeviceInterface)->GetDeviceClass (ppUsbDeviceInterface, &idDeviceClass); AssertMsg(rc == 0, ("Failed to get Device Class"));
+ rc = (*ppUsbDeviceInterface)->GetDeviceSubClass(ppUsbDeviceInterface, &idDeviceSubClass); AssertMsg(rc == 0, ("Failed to get Device Subclass"));
+
+ if (idDeviceClass == kUSBHIDInterfaceClass && idDeviceSubClass == kUSBHIDBootInterfaceSubClass)
+ {
+ VBoxKbdState_t *pKbd = darwinUsbHidQueryKbdByLocationId((uint32_t)idLocation, pHidState);
+
+ if (pKbd)
+ {
+ rc = IOServiceAddInterestNotification(pHidState->pNotificationPrortRef, service, kIOGeneralInterest,
+ darwinUsbHidGeneralInterestCb, pKbd, &pHidState->pUsbHidGeneralInterestNotify);
+
+ AssertMsg(rc == 0, ("Failed to add general interest notification"));
+
+ LogRel2(("Found HID device at location 0x%X: class 0x%X, subclass 0x%X\n", idLocation, idDeviceClass, idDeviceSubClass));
+ }
+ }
+
+ rc = (*ppUsbDeviceInterface)->Release(ppUsbDeviceInterface); AssertMsg(rc == 0, ("Failed to release USB device interface"));
+ }
+
+ IOObjectRelease(service);
+ }
+}
+
+/** Register IOUSBInterface IOService match notification callback in order to recync KBD
+ * device when it reports state change. */
+static int darwinUsbHidSubscribeInterestNotifications(VBoxHidsState_t *pHidState)
+{
+ AssertReturn(pHidState, kIOReturnBadArgument);
+
+ int rc = kIOReturnNoMemory;
+ CFDictionaryRef pDictionary = IOServiceMatching(kIOUSBInterfaceClassName);
+
+ if (pDictionary)
+ {
+ pHidState->pNotificationPrortRef = IONotificationPortCreate(kIOMasterPortDefault);
+ if (pHidState->pNotificationPrortRef)
+ {
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(pHidState->pNotificationPrortRef), kCFRunLoopDefaultMode);
+
+ rc = IOServiceAddMatchingNotification(pHidState->pNotificationPrortRef, kIOMatchedNotification,
+ pDictionary, darwinUsbHidDeviceMatchCb, pHidState,
+ &pHidState->pUsbHidDeviceMatchNotify);
+
+ if (rc == kIOReturnSuccess && pHidState->pUsbHidDeviceMatchNotify != IO_OBJECT_NULL)
+ {
+ darwinUsbHidDeviceMatchCb(pHidState, pHidState->pUsbHidDeviceMatchNotify);
+ LogRel2(("Successfully subscribed to IOUSBInterface IOService match notifications\n"));
+ }
+ else
+ LogRel2(("Failed to subscribe to IOUSBInterface IOService match notifications: subscription error 0x%X\n", rc));
+ }
+ else
+ LogRel2(("Failed to subscribe to IOUSBInterface IOService match notifications: unable to create notification port\n"));
+ }
+ else
+ LogRel2(("Failed to subscribe to IOUSBInterface IOService match notifications: no memory\n"));
+
+ return rc;
+}
+
+/** Remove IOUSBInterface IOService match notification subscription. */
+static void darwinUsbHidUnsubscribeInterestNotifications(VBoxHidsState_t *pHidState)
+{
+ AssertReturnVoid(pHidState);
+
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(pHidState->pNotificationPrortRef), kCFRunLoopDefaultMode);
+ IONotificationPortDestroy(pHidState->pNotificationPrortRef);
+ pHidState->pNotificationPrortRef = NULL;
+
+ LogRel2(("Successfully un-subscribed from IOUSBInterface IOService match notifications\n"));
+}
+
+/** This callback is called when user physically removes HID device. We remove device from cache here. */
+static void darwinHidRemovalCallback(void *pData, IOReturn unused, void *unused1)
+{
+ (void)unused;
+ (void)unused1;
+
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)pData; AssertReturnVoid(pKbd);
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer; AssertReturnVoid(pHidState);
+
+ AssertReturnVoid(pHidState->pDeviceCollection);
+
+ LogRel2(("Forget KBD %d\n", (int)pKbd->idxPosition));
+
+ //if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
+ // return ;
+
+ CFArrayRemoveValueAtIndex(pHidState->pDeviceCollection, pKbd->idxPosition);
+ free(pKbd);
+
+ //RTSemMutexRelease(pHidState->fifoEventQueueLock);
+}
+
+/** Check if we already cached given device */
+static bool darwinIsDeviceInCache(VBoxHidsState_t *pState, IOHIDDeviceRef pDevice)
+{
+ AssertReturn(pState, false);
+ AssertReturn(pState->pDeviceCollection, false);
+
+ for (CFIndex i = 0; i < CFArrayGetCount(pState->pDeviceCollection); i++)
+ {
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)CFArrayGetValueAtIndex(pState->pDeviceCollection, i);
+ if (pKbd && pKbd->pDevice == pDevice)
+ return true;
+ }
+
+ return false;
+}
+
+/** Add device to cache. */
+static void darwinHidAddDevice(VBoxHidsState_t *pHidState, IOHIDDeviceRef pDevice, bool fApplyLedState)
+{
+ int rc;
+
+ if (!darwinIsDeviceInCache(pHidState, pDevice))
+ {
+ if (IOHIDDeviceConformsTo(pDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)
+ && darwinHidDeviceSupported(pDevice))
+ {
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)malloc(sizeof(VBoxKbdState_t));
+ if (pKbd)
+ {
+ pKbd->pDevice = pDevice;
+ pKbd->pParentContainer = (void *)pHidState;
+ pKbd->idxPosition = CFArrayGetCount(pHidState->pDeviceCollection);
+ pKbd->idLocation = darwinHidLocationId(pDevice);
+
+ // Some Apple keyboards have CAPS LOCK key timeout. According to corresponding
+ // kext plist files, it is equals to 75 ms. For such devices we only add info into our FIFO event
+ // queue if the time between Key-Down and Key-Up events >= 75 ms.
+ pKbd->cCapsLockTimeout = (darwinHidVendorId(pKbd->pDevice) == kIOUSBVendorIDAppleComputer) ? 75 : 0;
+
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ rc = darwinGetDeviceLedsState(pKbd->pDevice,
+ elementMatchingDict,
+ &pKbd->LED.fNumLockOn,
+ &pKbd->LED.fCapsLockOn,
+ &pKbd->LED.fScrollLockOn);
+
+ // This should never happen, but if happened -- mark all the leds of current
+ // device as turned OFF.
+ if (rc != 0)
+ {
+ LogRel2(("Unable to get leds state for device %d. Mark leds as turned off\n", (int)(pKbd->idxPosition)));
+ pKbd->LED.fNumLockOn =
+ pKbd->LED.fCapsLockOn =
+ pKbd->LED.fScrollLockOn = false;
+ }
+
+ /* Register per-device removal callback */
+ IOHIDDeviceRegisterRemovalCallback(pKbd->pDevice, darwinHidRemovalCallback, (void *)pKbd);
+
+ /* Register per-device input callback */
+ IOHIDDeviceRegisterInputValueCallback(pKbd->pDevice, darwinHidInputCallback, (void *)pKbd);
+ IOHIDDeviceScheduleWithRunLoop(pKbd->pDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+ CFArrayAppendValue(pHidState->pDeviceCollection, (void *)pKbd);
+
+ LogRel2(("Saved LEDs for KBD %d (%p): fNumLockOn=%s, fCapsLockOn=%s, fScrollLockOn=%s\n",
+ (int)pKbd->idxPosition, pKbd, VBOX_BOOL_TO_STR_STATE(pKbd->LED.fNumLockOn), VBOX_BOOL_TO_STR_STATE(pKbd->LED.fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(pKbd->LED.fScrollLockOn)));
+
+ if (fApplyLedState)
+ {
+ rc = darwinSetDeviceLedsState(pKbd->pDevice, elementMatchingDict, pHidState->guestState.fNumLockOn,
+ pHidState->guestState.fCapsLockOn, pHidState->guestState.fScrollLockOn);
+ if (rc != 0)
+ LogRel2(("Unable to apply guest state to newly attached device\n"));
+ }
+
+ CFRelease(elementMatchingDict);
+ return;
+ }
+
+ free(pKbd);
+ }
+ }
+ }
+}
+
+/** This callback is called when new HID device discovered by IOHIDManager. We add devices to cache here and only here! */
+static void darwinHidMatchingCallback(void *pData, IOReturn unused, void *unused1, IOHIDDeviceRef pDevice)
+{
+ (void)unused;
+ (void)unused1;
+
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pData;
+
+ AssertReturnVoid(pHidState);
+ AssertReturnVoid(pHidState->pDeviceCollection);
+ AssertReturnVoid(pDevice);
+
+ darwinHidAddDevice(pHidState, pDevice, true);
+}
+
+/** Register Carbon key press callback. */
+static int darwinAddCarbonHandler(VBoxHidsState_t *pHidState)
+{
+ CFMachPortRef pTapRef;
+ CGEventMask fMask = CGEventMaskBit(kCGEventFlagsChanged);
+
+ AssertReturn(pHidState, kIOReturnError);
+
+ /* Create FIFO event queue for keyboard events */
+ pHidState->pFifoEventQueue = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
+ AssertReturn(pHidState->pFifoEventQueue, kIOReturnError);
+
+ /* Create Lock for FIFO event queue */
+ if (RT_FAILURE(RTSemMutexCreate(&pHidState->fifoEventQueueLock)))
+ {
+ LogRel2(("Unable to create Lock for FIFO event queue\n"));
+ CFRelease(pHidState->pFifoEventQueue);
+ pHidState->pFifoEventQueue = NULL;
+ return kIOReturnError;
+ }
+
+ pTapRef = CGEventTapCreate(kCGSessionEventTap, kCGTailAppendEventTap, kCGEventTapOptionDefault, fMask,
+ darwinCarbonCallback, (void *)pHidState);
+ if (pTapRef)
+ {
+ CFRunLoopSourceRef pLoopSourceRef;
+ pLoopSourceRef = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, pTapRef, 0);
+ if (pLoopSourceRef)
+ {
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), pLoopSourceRef, kCFRunLoopDefaultMode);
+ CGEventTapEnable(pTapRef, true);
+
+ pHidState->pTapRef = pTapRef;
+ pHidState->pLoopSourceRef = pLoopSourceRef;
+
+ return 0;
+ }
+ else
+ LogRel2(("Unable to create a loop source\n"));
+
+ CFRelease(pTapRef);
+ }
+ else
+ LogRel2(("Unable to create an event tap\n"));
+
+ return kIOReturnError;
+}
+
+/** Remove Carbon key press callback. */
+static void darwinRemoveCarbonHandler(VBoxHidsState_t *pHidState)
+{
+ AssertReturnVoid(pHidState);
+ AssertReturnVoid(pHidState->pTapRef);
+ AssertReturnVoid(pHidState->pLoopSourceRef);
+ AssertReturnVoid(pHidState->pFifoEventQueue);
+
+ CGEventTapEnable(pHidState->pTapRef, false);
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pHidState->pLoopSourceRef, kCFRunLoopDefaultMode);
+ CFRelease(pHidState->pLoopSourceRef);
+ CFRelease(pHidState->pTapRef);
+
+ RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT);
+ CFRelease(pHidState->pFifoEventQueue);
+ pHidState->pFifoEventQueue = NULL;
+ RTSemMutexRelease(pHidState->fifoEventQueueLock);
+
+ RTSemMutexDestroy(pHidState->fifoEventQueueLock);
+}
+
+#endif /* !VBOX_WITH_KBD_LEDS_SYNC */
+
+
+void *DarwinHidDevicesKeepLedsState()
+{
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+ IOReturn rc;
+ VBoxHidsState_t *pHidState;
+
+ pHidState = (VBoxHidsState_t *)malloc(sizeof(VBoxHidsState_t));
+ AssertReturn(pHidState, NULL);
+
+ pHidState->hidManagerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+ if (pHidState->hidManagerRef)
+ {
+ CFDictionaryRef deviceMatchingDictRef = darwinQueryLedDeviceMatchingDictionary();
+ if (deviceMatchingDictRef)
+ {
+ IOHIDManagerScheduleWithRunLoop(pHidState->hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ IOHIDManagerSetDeviceMatching(pHidState->hidManagerRef, deviceMatchingDictRef);
+
+ rc = IOHIDManagerOpen(pHidState->hidManagerRef, kIOHIDOptionsTypeNone);
+ if (rc == kIOReturnSuccess)
+ {
+ pHidState->pDeviceCollection = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
+ if (pHidState->pDeviceCollection)
+ {
+ if (darwinAddCarbonHandler(pHidState) == 0)
+ {
+ /* Populate cache with HID devices */
+ CFSetRef pDevicesSet = IOHIDManagerCopyDevices(pHidState->hidManagerRef);
+ if (pDevicesSet)
+ {
+ CFIndex cDevices = CFSetGetCount(pDevicesSet);
+
+ IOHIDDeviceRef *ppDevices = (IOHIDDeviceRef *)malloc((size_t)cDevices * sizeof(IOHIDDeviceRef));
+ if (ppDevices)
+ {
+ CFSetGetValues(pDevicesSet, (const void **)ppDevices);
+ for (CFIndex i= 0; i < cDevices; i++)
+ darwinHidAddDevice(pHidState, (IOHIDDeviceRef)ppDevices[i], false);
+
+ free(ppDevices);
+ }
+
+ CFRelease(pDevicesSet);
+ }
+
+ IOHIDManagerRegisterDeviceMatchingCallback(pHidState->hidManagerRef, darwinHidMatchingCallback, (void *)pHidState);
+
+ CFRelease(deviceMatchingDictRef);
+
+ /* This states should be set on broadcast */
+ pHidState->guestState.fNumLockOn =
+ pHidState->guestState.fCapsLockOn =
+ pHidState->guestState.fScrollLockOn = false;
+
+ /* Finally, subscribe to USB HID notifications in order to prevent LED artifacts on
+ automatic power management */
+ if (darwinUsbHidSubscribeInterestNotifications(pHidState) == 0)
+ return pHidState;
+ }
+ }
+
+ rc = IOHIDManagerClose(pHidState->hidManagerRef, 0);
+ if (rc != kIOReturnSuccess)
+ LogRel2(("Warning! Something went wrong in attempt to close HID device manager!\n"));
+ }
+
+ CFRelease(deviceMatchingDictRef);
+ }
+
+ CFRelease(pHidState->hidManagerRef);
+ }
+
+ free(pHidState);
+
+ return NULL;
+#else /* !VBOX_WITH_KBD_LEDS_SYNC */
+ return NULL;
+#endif
+}
+
+
+int DarwinHidDevicesApplyAndReleaseLedsState(void *pState)
+{
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pState;
+ IOReturn rc, rc2 = 0;
+
+ AssertReturn(pHidState, kIOReturnError);
+
+ darwinUsbHidUnsubscribeInterestNotifications(pHidState);
+
+ /* Need to unregister Carbon stuff first: */
+ darwinRemoveCarbonHandler(pHidState);
+
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ /* Restore LEDs: */
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pDeviceCollection); i++)
+ {
+ /* Cycle through supported devices only. */
+ VBoxKbdState_t *pKbd;
+ pKbd = (VBoxKbdState_t *)CFArrayGetValueAtIndex(pHidState->pDeviceCollection, i);
+
+ if (pKbd)
+ {
+ rc = darwinSetDeviceLedsState(pKbd->pDevice,
+ elementMatchingDict,
+ pKbd->LED.fNumLockOn,
+ pKbd->LED.fCapsLockOn,
+ pKbd->LED.fScrollLockOn);
+ if (rc != 0)
+ {
+ LogRel2(("Unable to restore led states for device (%d)!\n", (int)i));
+ rc2 = kIOReturnError;
+ }
+
+ IOHIDDeviceUnscheduleFromRunLoop(pKbd->pDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+ LogRel2(("Restored LEDs for KBD %d (%p): fNumLockOn=%s, fCapsLockOn=%s, fScrollLockOn=%s\n",
+ (int)i, pKbd, VBOX_BOOL_TO_STR_STATE(pKbd->LED.fNumLockOn), VBOX_BOOL_TO_STR_STATE(pKbd->LED.fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(pKbd->LED.fScrollLockOn)));
+
+ free(pKbd);
+ }
+ }
+
+ CFRelease(elementMatchingDict);
+ }
+
+ /* Free resources: */
+ CFRelease(pHidState->pDeviceCollection);
+
+ rc = IOHIDManagerClose(pHidState->hidManagerRef, 0);
+ if (rc != kIOReturnSuccess)
+ {
+ LogRel2(("Warning! Something went wrong in attempt to close HID device manager!\n"));
+ rc2 = kIOReturnError;
+ }
+
+ IOHIDManagerUnscheduleFromRunLoop(pHidState->hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+ CFRelease(pHidState->hidManagerRef);
+
+ free(pHidState);
+
+ return rc2;
+#else /* !VBOX_WITH_KBD_LEDS_SYNC */
+ (void)pState;
+ return 0;
+#endif /* !VBOX_WITH_KBD_LEDS_SYNC */
+}
+
+void DarwinHidDevicesBroadcastLeds(void *pState, bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pState;
+ IOReturn rc;
+
+ AssertReturnVoid(pHidState);
+ AssertReturnVoid(pHidState->pDeviceCollection);
+
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ LogRel2(("Start LEDs broadcast: fNumLockOn=%s, fCapsLockOn=%s, fScrollLockOn=%s\n",
+ VBOX_BOOL_TO_STR_STATE(fNumLockOn), VBOX_BOOL_TO_STR_STATE(fCapsLockOn), VBOX_BOOL_TO_STR_STATE(fScrollLockOn)));
+
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pDeviceCollection); i++)
+ {
+ /* Cycle through supported devices only. */
+ VBoxKbdState_t *pKbd;
+ pKbd = (VBoxKbdState_t *)CFArrayGetValueAtIndex(pHidState->pDeviceCollection, i);
+
+ if (pKbd && darwinHidDeviceSupported(pKbd->pDevice))
+ {
+ rc = darwinSetDeviceLedsState(pKbd->pDevice,
+ elementMatchingDict,
+ fNumLockOn,
+ fCapsLockOn,
+ fScrollLockOn);
+ if (rc != 0)
+ LogRel2(("Unable to restore led states for device (%d)!\n", (int)i));
+ }
+ }
+
+ LogRel2(("LEDs broadcast completed\n"));
+
+ CFRelease(elementMatchingDict);
+ }
+
+ /* Dynamically attached device will use these states: */
+ pHidState->guestState.fNumLockOn = fNumLockOn;
+ pHidState->guestState.fCapsLockOn = fCapsLockOn;
+ pHidState->guestState.fScrollLockOn = fScrollLockOn;
+#else /* !VBOX_WITH_KBD_LEDS_SYNC */
+ (void)fNumLockOn;
+ (void)fCapsLockOn;
+ (void)fScrollLockOn;
+#endif /* !VBOX_WITH_KBD_LEDS_SYNC */
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.h
new file mode 100644
index 00000000..359b1622
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.h
@@ -0,0 +1,96 @@
+/* $Id: DarwinKeyboard.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Darwin Keyboard specific tasks.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_DarwinKeyboard_h
+#define FEQT_INCLUDED_SRC_platform_darwin_DarwinKeyboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+/* External includes: */
+#include <CoreFoundation/CFBase.h>
+
+
+RT_C_DECLS_BEGIN
+
+/** Private hack for missing rightCmdKey enum. */
+#define kEventKeyModifierRightCmdKeyMask (1<<27)
+
+/** The scancode mask. */
+#define VBOXKEY_SCANCODE_MASK 0x007f
+/** Extended key. */
+#define VBOXKEY_EXTENDED 0x0080
+/** Modifier key. */
+#define VBOXKEY_MODIFIER 0x0400
+/** Lock key (like num lock and caps lock). */
+#define VBOXKEY_LOCK 0x0800
+
+/** Converts a darwin (virtual) key code to a set 1 scan code. */
+SHARED_LIBRARY_STUFF unsigned DarwinKeycodeToSet1Scancode(unsigned uKeyCode);
+/** Adjusts the modifier mask left / right using the current keyboard state. */
+SHARED_LIBRARY_STUFF UInt32 DarwinAdjustModifierMask(UInt32 fModifiers, const void *pvCocoaEvent);
+/** Converts a single modifier to a set 1 scan code. */
+SHARED_LIBRARY_STUFF unsigned DarwinModifierMaskToSet1Scancode(UInt32 fModifiers);
+/** Converts a single modifier to a darwin keycode. */
+SHARED_LIBRARY_STUFF unsigned DarwinModifierMaskToDarwinKeycode(UInt32 fModifiers);
+/** Converts a darwin keycode to a modifier mask. */
+SHARED_LIBRARY_STUFF UInt32 DarwinKeyCodeToDarwinModifierMask(unsigned uKeyCode);
+
+/** Disables or enabled global hot keys. */
+SHARED_LIBRARY_STUFF void DarwinDisableGlobalHotKeys(bool fDisable);
+
+/** Start grabbing keyboard events.
+ * @param fGlobalHotkeys Brings whether to disable global hotkeys or not. */
+SHARED_LIBRARY_STUFF void DarwinGrabKeyboard(bool fGlobalHotkeys);
+/** Reverses the actions taken by DarwinGrabKeyboard. */
+SHARED_LIBRARY_STUFF void DarwinReleaseKeyboard();
+
+/** Saves the states of leds for all HID devices attached to the system and return it. */
+SHARED_LIBRARY_STUFF void *DarwinHidDevicesKeepLedsState();
+
+/** Applies LEDs @a pState release its resources afterwards. */
+SHARED_LIBRARY_STUFF int DarwinHidDevicesApplyAndReleaseLedsState(void *pState);
+/** Set states for host keyboard LEDs.
+ * @note This function will set led values for all
+ * keyboard devices attached to the system.
+ * @param pState Brings the pointer to saved LEDs state.
+ * @param fNumLockOn Turns on NumLock led if TRUE, off otherwise
+ * @param fCapsLockOn Turns on CapsLock led if TRUE, off otherwise
+ * @param fScrollLockOn Turns on ScrollLock led if TRUE, off otherwise */
+SHARED_LIBRARY_STUFF void DarwinHidDevicesBroadcastLeds(void *pState, bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn);
+
+RT_C_DECLS_END
+
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_DarwinKeyboard_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/DockIconPreview.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DockIconPreview.h
new file mode 100644
index 00000000..313676f0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DockIconPreview.h
@@ -0,0 +1,49 @@
+/* $Id: DockIconPreview.h $ */
+/** @file
+ * VBox Qt GUI - UIDockIconPreview class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_DockIconPreview_h
+#define FEQT_INCLUDED_SRC_platform_darwin_DockIconPreview_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UICocoaDockIconPreview.h"
+
+
+/** UICocoaDockIconPreview extension to be used for VM. */
+class UIDockIconPreview : public UICocoaDockIconPreview
+{
+public:
+
+ /** Constructor taking passed @a pSession and @a overlayImage. */
+ UIDockIconPreview(UISession *pSession, const QPixmap& overlayImage)
+ : UICocoaDockIconPreview(pSession, overlayImage) {}
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_DockIconPreview_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/Info.plist b/src/VBox/Frontends/VirtualBox/src/platform/darwin/Info.plist
new file mode 100644
index 00000000..44d2d00c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/Info.plist
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundlePackageType</key> <string>APPL</string>
+ <key>CFBundleSignature</key> <string>VBOX</string>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.app.VirtualBox</string>
+ <key>CFBundleName</key> <string>VirtualBox</string>
+ <key>CFBundleExecutable</key> <string>VirtualBox</string>
+ <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ Manager @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string>
+ <key>CFBundleIconFile</key> <string>virtualbox</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>LSCanProvideIMVideoDataSource</key> <false/>
+ <key>NSHighResolutionCapable</key> <true/>
+ <key>NSSupportsAutomaticGraphicsSwitching</key><true/>
+ <key>NSCameraUsageDescription</key> <string>VirtualBox needs camera access for emulated webcam passthrough</string>
+ <key>NSMicrophoneUsageDescription</key> <string>VirtualBox needs microphone access for guest audio input</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeName</key> <string>VirtualBox Extension Pack</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vbox-extpack</string></array>
+ <key>CFBundleTypeRole</key> <string>Viewer</string>
+ <key>LSHandlerRank</key> <string>Owner</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vbox-extpack</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Open Virtualization Format</string>
+ <key>CFBundleTypeExtensions</key> <array><string>ovf</string></array>
+ <key>CFBundleTypeRole</key> <string>Viewer</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-ovf</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Open Virtualization Format Archive</string>
+ <key>CFBundleTypeExtensions</key> <array><string>ova</string></array>
+ <key>CFBundleTypeRole</key> <string>Viewer</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-ova</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Virtual Disk Image</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vdi</string></array>
+ <key>CFBundleTypeRole</key> <string>None</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vdi</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Virtual Machine Disk Format</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vmdk</string></array>
+ <key>CFBundleTypeRole</key> <string>None</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vmdk</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Virtual Hard Disk</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vhd</string></array>
+ <key>CFBundleTypeRole</key> <string>None</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vhd</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Virtual Hard Disk</string>
+ <key>CFBundleTypeExtensions</key> <array><string>hdd</string></array>
+ <key>CFBundleTypeRole</key> <string>None</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-hdd</string>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/platform/darwin/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/PkgInfo b/src/VBox/Frontends/VirtualBox/src/platform/darwin/PkgInfo
new file mode 100644
index 00000000..30c80e6c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/PkgInfo
@@ -0,0 +1 @@
+APPLVBOX \ No newline at end of file
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.cpp
new file mode 100644
index 00000000..092d86a1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.cpp
@@ -0,0 +1,144 @@
+/* $Id: UIAbstractDockIconPreview.cpp $ */
+/** @file
+ * VBox Qt GUI - Realtime Dock Icon Preview
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStyle>
+
+/* GUI includes: */
+#include "UIAbstractDockIconPreview.h"
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIFrameBuffer.h"
+#include "UIMachineLogic.h"
+#include "UIMachineView.h"
+#include "UISession.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+
+UIAbstractDockIconPreview::UIAbstractDockIconPreview(UISession * /* pSession */, const QPixmap& /* overlayImage */)
+{
+}
+
+void UIAbstractDockIconPreview::updateDockPreview(UIFrameBuffer *pFrameBuffer)
+{
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ Assert(cs);
+ /* Create the image copy of the framebuffer */
+ CGDataProviderRef dp = CGDataProviderCreateWithData(pFrameBuffer, pFrameBuffer->address(), pFrameBuffer->bitsPerPixel() / 8 * pFrameBuffer->width() * pFrameBuffer->height(), NULL);
+ Assert(dp);
+ CGImageRef ir = CGImageCreate(pFrameBuffer->width(), pFrameBuffer->height(), 8, 32, pFrameBuffer->bytesPerLine(), cs,
+ kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, dp, 0, false,
+ kCGRenderingIntentDefault);
+ Assert(ir);
+
+ /* Update the dock preview icon */
+ updateDockPreview(ir);
+
+ /* Release the temp data and image */
+ CGImageRelease(ir);
+ CGDataProviderRelease(dp);
+ CGColorSpaceRelease(cs);
+}
+
+UIAbstractDockIconPreviewHelper::UIAbstractDockIconPreviewHelper(UISession *pSession, const QPixmap& overlayImage)
+ : m_pSession(pSession)
+ , m_dockIconRect(CGRectMake(0, 0, 128, 128))
+ , m_dockMonitor(NULL)
+ , m_dockMonitorGlossy(NULL)
+ , m_updateRect(CGRectMake(0, 0, 0, 0))
+ , m_monitorRect(CGRectMake(0, 0, 0, 0))
+{
+ m_overlayImage = ::darwinToCGImageRef(&overlayImage);
+ Assert(m_overlayImage);
+}
+
+void* UIAbstractDockIconPreviewHelper::currentPreviewWindowId() const
+{
+ /* Get the MachineView which is currently previewed and return the win id
+ of the viewport. */
+ UIMachineView* pView = m_pSession->machineLogic()->dockPreviewView();
+ if (pView)
+ return (void*)pView->viewport()->winId();
+ return 0;
+}
+
+UIAbstractDockIconPreviewHelper::~UIAbstractDockIconPreviewHelper()
+{
+ CGImageRelease(m_overlayImage);
+ if (m_dockMonitor)
+ CGImageRelease(m_dockMonitor);
+ if (m_dockMonitorGlossy)
+ CGImageRelease(m_dockMonitorGlossy);
+}
+
+void UIAbstractDockIconPreviewHelper::initPreviewImages()
+{
+ if (!m_dockMonitor)
+ {
+ m_dockMonitor = ::darwinToCGImageRef("monitor.png");
+ Assert(m_dockMonitor);
+ /* Center it on the dock icon context */
+ m_monitorRect = centerRect(CGRectMake(0, 0,
+ CGImageGetWidth(m_dockMonitor),
+ CGImageGetWidth(m_dockMonitor)));
+ }
+
+ if (!m_dockMonitorGlossy)
+ {
+ m_dockMonitorGlossy = ::darwinToCGImageRef("monitor_glossy.png");
+ Assert(m_dockMonitorGlossy);
+ /* This depends on the content of monitor.png */
+ m_updateRect = CGRectMake(m_monitorRect.origin.x + 8 /* left-frame */ + 1 /* indent-size */,
+ m_monitorRect.origin.y + 8 /* top-frame */ + 1 /* indent-size */,
+ 128 /* .png-width */ - 8 /* left-frame */ - 8 /* right-frame */ - 2 * 1 /* indent-size */,
+ 128 /* .png-height */ - 8 /* top-frame */ - 25 /* bottom-frame */ - 2 * 1 /* indent-size */);
+ }
+}
+
+void UIAbstractDockIconPreviewHelper::drawOverlayIcons(CGContextRef context)
+{
+ /* Determine whether dock icon overlay is not disabled: */
+ if (!gEDataManager->dockIconDisableOverlay(uiCommon().managedVMUuid()))
+ {
+ /* Initialize overlay rectangle: */
+ CGRect overlayRect = CGRectMake(0, 0, 0, 0);
+ /* Make sure overlay image is valid: */
+ if (m_overlayImage)
+ {
+ /* Draw overlay image at bottom-right of dock icon: */
+ overlayRect = CGRectMake(m_dockIconRect.size.width - CGImageGetWidth(m_overlayImage),
+ m_dockIconRect.size.height - CGImageGetHeight(m_overlayImage),
+ CGImageGetWidth(m_overlayImage),
+ CGImageGetHeight(m_overlayImage));
+ CGContextDrawImage(context, flipRect(overlayRect), m_overlayImage);
+ }
+ }
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.h
new file mode 100644
index 00000000..cb84b436
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.h
@@ -0,0 +1,86 @@
+/* $Id: UIAbstractDockIconPreview.h $ */
+/** @file
+ * VBox Qt GUI - Abstract class for the dock icon preview.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UIAbstractDockIconPreview_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UIAbstractDockIconPreview_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* System includes */
+#include <ApplicationServices/ApplicationServices.h>
+
+/* VBox includes */
+#include "VBoxUtils-darwin.h"
+
+class UIFrameBuffer;
+class UISession;
+
+class QPixmap;
+
+class UIAbstractDockIconPreview
+{
+public:
+ UIAbstractDockIconPreview(UISession *pSession, const QPixmap& overlayImage);
+ virtual ~UIAbstractDockIconPreview() {}
+
+ virtual void updateDockOverlay() = 0;
+ virtual void updateDockPreview(CGImageRef VMImage) = 0;
+ virtual void updateDockPreview(UIFrameBuffer *pFrameBuffer);
+
+ virtual void setOriginalSize(int /* aWidth */, int /* aHeight */) {}
+};
+
+class UIAbstractDockIconPreviewHelper
+{
+public:
+ UIAbstractDockIconPreviewHelper(UISession *pSession, const QPixmap& overlayImage);
+ virtual ~UIAbstractDockIconPreviewHelper();
+ void initPreviewImages();
+ void drawOverlayIcons(CGContextRef context);
+
+ void* currentPreviewWindowId() const;
+
+ /* Flipping is necessary cause the drawing context in Mac OS X is flipped by 180 degree */
+ inline CGRect flipRect(CGRect rect) const { return ::darwinFlipCGRect(rect, m_dockIconRect); }
+ inline CGRect centerRect(CGRect rect) const { return ::darwinCenterRectTo(rect, m_dockIconRect); }
+ inline CGRect centerRectTo(CGRect rect, const CGRect& toRect) const { return ::darwinCenterRectTo(rect, toRect); }
+
+ /* Private member vars */
+ UISession *m_pSession;
+ const CGRect m_dockIconRect;
+
+ CGImageRef m_overlayImage;
+ CGImageRef m_dockMonitor;
+ CGImageRef m_dockMonitorGlossy;
+
+ CGRect m_updateRect;
+ CGRect m_monitorRect;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UIAbstractDockIconPreview_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.h
new file mode 100644
index 00000000..5d17cb0d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.h
@@ -0,0 +1,135 @@
+/* $Id: UICocoaApplication.h $ */
+/** @file
+ * VBox Qt GUI - UICocoaApplication class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UICocoaApplication_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UICocoaApplication_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+
+/* GUI includes: */
+#include "VBoxCocoaHelper.h"
+#include "VBoxUtils-darwin.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QObject;
+class QWidget;
+
+/* Cocoa declarations: */
+ADD_COCOA_NATIVE_REF(UICocoaApplicationPrivate);
+ADD_COCOA_NATIVE_REF(NSAutoreleasePool);
+ADD_COCOA_NATIVE_REF(NSString);
+ADD_COCOA_NATIVE_REF(NSWindow);
+ADD_COCOA_NATIVE_REF(NSButton);
+
+
+/** Event handler callback. */
+typedef bool (*PFNVBOXCACALLBACK)(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser);
+
+/** Native notification callback type for QObject. */
+typedef void (*PfnNativeNotificationCallbackForQObject)(QObject *pObject, const QMap<QString, QString> &userInfo);
+/** Native notification callback type for QWidget. */
+typedef void (*PfnNativeNotificationCallbackForQWidget)(const QString &strNativeNotificationName, QWidget *pWidget);
+/** Standard window button callback type for QWidget. */
+typedef void (*PfnStandardWindowButtonCallbackForQWidget)(StandardWindowButtonType emnButtonType, bool fWithOptionKey, QWidget *pWidget);
+
+
+/** Singleton prototype for our private NSApplication object. */
+class SHARED_LIBRARY_STUFF UICocoaApplication
+{
+public:
+
+ /** Returns singleton instance. */
+ static UICocoaApplication *instance();
+
+ /** Destructs cocoa application. */
+ virtual ~UICocoaApplication();
+
+ /** Returns whether application is currently active. */
+ bool isActive() const;
+
+ /** Hides the application. */
+ void hide();
+
+ /** Hides user elements such as menu-bar and dock. */
+ void hideUserElements();
+
+ /** Register native @a pfnCallback of the @a pvUser taking event @a fMask into account. */
+ void registerForNativeEvents(uint32_t fMask, PFNVBOXCACALLBACK pfnCallback, void *pvUser);
+ /** Unregister native @a pfnCallback of the @a pvUser taking event @a fMask into account. */
+ void unregisterForNativeEvents(uint32_t fMask, PFNVBOXCACALLBACK pfnCallback, void *pvUser);
+
+ /** Register passed @a pObject to native notification @a strNativeNotificationName, using @a pCallback as handler. */
+ void registerToNotificationOfWorkspace(const QString &strNativeNotificationName, QObject *pObject, PfnNativeNotificationCallbackForQObject pCallback);
+ /** Unregister passed @a pWidget from native notification @a strNativeNotificationName. */
+ void unregisterFromNotificationOfWorkspace(const QString &strNativeNotificationName, QObject *pObject);
+
+ /** Register passed @a pWidget to native notification @a strNativeNotificationName, using @a pCallback as handler. */
+ void registerToNotificationOfWindow(const QString &strNativeNotificationName, QWidget *pWidget, PfnNativeNotificationCallbackForQWidget pCallback);
+ /** Unregister passed @a pWidget from native notification @a strNativeNotificationName. */
+ void unregisterFromNotificationOfWindow(const QString &strNativeNotificationName, QWidget *pWidget);
+
+ /** Redirects native notification @a pstrNativeNotificationName for application to registered listener. */
+ void nativeNotificationProxyForObject(NativeNSStringRef pstrNativeNotificationName, const QMap<QString, QString> &userInfo);
+ /** Redirects native notification @a pstrNativeNotificationName for window @a pWindow to registered listener. */
+ void nativeNotificationProxyForWidget(NativeNSStringRef pstrNativeNotificationName, NativeNSWindowRef pWindow);
+
+ /** Register callback for standard window @a buttonType of passed @a pWidget as @a pCallback. */
+ void registerCallbackForStandardWindowButton(QWidget *pWidget, StandardWindowButtonType enmButtonType, PfnStandardWindowButtonCallbackForQWidget pCallback);
+ /** Unregister callback for standard window @a buttonType of passed @a pWidget. */
+ void unregisterCallbackForStandardWindowButton(QWidget *pWidget, StandardWindowButtonType enmButtonType);
+ /** Redirects standard window button selector to registered callback. */
+ void nativeCallbackProxyForStandardWindowButton(NativeNSButtonRef pButton, bool fWithOptionKey);
+
+private:
+
+ /** Constructs cocoa application. */
+ UICocoaApplication();
+
+ /** Holds the singleton access instance. */
+ static UICocoaApplication *s_pInstance;
+
+ /** Holds the private NSApplication instance. */
+ NativeUICocoaApplicationPrivateRef m_pNative;
+ /** Holds the private NSAutoreleasePool instance. */
+ NativeNSAutoreleasePoolRef m_pPool;
+
+ /** Map of notification callbacks registered for corresponding QObject(s). */
+ QMap<QObject*, QMap<QString, PfnNativeNotificationCallbackForQObject> > m_objectCallbacks;
+ /** Map of notification callbacks registered for corresponding QWidget(s). */
+ QMap<QWidget*, QMap<QString, PfnNativeNotificationCallbackForQWidget> > m_widgetCallbacks;
+
+ /** Map of callbacks registered for standard window button(s) of corresponding QWidget(s). */
+ QMap<QWidget*, QMap<StandardWindowButtonType, PfnStandardWindowButtonCallbackForQWidget> > m_stdWindowButtonCallbacks;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UICocoaApplication_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.mm
new file mode 100644
index 00000000..42196b26
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.mm
@@ -0,0 +1,494 @@
+/* $Id: UICocoaApplication.mm $ */
+/** @file
+ * VBox Qt GUI - UICocoaApplication class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICocoaApplication.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+/* External includes: */
+#import <AppKit/NSApplication.h>
+#import <AppKit/NSButton.h>
+#import <AppKit/NSEvent.h>
+#import <AppKit/NSWindow.h>
+#import <Foundation/NSArray.h>
+
+
+/** Class for tracking a callback. */
+@interface CallbackData : NSObject
+{
+@public
+ /** Holds the mask of events to send to this callback. */
+ uint32_t fMask;
+ /** Holds the callback. */
+ PFNVBOXCACALLBACK pfnCallback;
+ /** Holds the user argument. */
+ void *pvUser;
+}
+- (id)initWithMask :(uint32)mask callback :(PFNVBOXCACALLBACK)callback user :(void *)user;
+@end /* @interface CallbackData */
+
+@implementation CallbackData
+/** Performs initialization. */
+- (id)initWithMask :(uint32)mask callback :(PFNVBOXCACALLBACK)callback user :(void *)user
+{
+ self = [super init];
+ if (self)
+ {
+ fMask = mask;
+ pfnCallback = callback;
+ pvUser = user;
+ }
+ return self;
+}
+@end /* @implementation CallbackData */
+
+
+/** Class for event handling. */
+@interface UICocoaApplicationPrivate : NSApplication
+{
+ /** The event mask for which there currently are callbacks. */
+ uint32_t m_fMask;
+ /** Array of callbacks. */
+ NSMutableArray *m_pCallbacks;
+}
+- (id)init;
+- (void)sendEvent :(NSEvent *)theEvent;
+- (void)setCallback :(uint32_t)fMask :(PFNVBOXCACALLBACK)pfnCallback :(void *)pvUser;
+- (void)unsetCallback :(uint32_t)fMask :(PFNVBOXCACALLBACK)pfnCallback :(void *)pvUser;
+
+- (void)registerToNotificationOfWorkspace :(NSString *)pstrNotificationName;
+- (void)unregisterFromNotificationOfWorkspace :(NSString *)pstrNotificationName;
+
+- (void)registerToNotificationOfWindow :(NSString *)pstrNotificationName :(NSWindow *)pWindow;
+- (void)unregisterFromNotificationOfWindow :(NSString *)pstrNotificationName :(NSWindow *)pWindow;
+
+- (void)notificationCallbackOfObject :(NSNotification *)notification;
+- (void)notificationCallbackOfWindow :(NSNotification *)notification;
+
+- (void)registerSelectorForStandardWindowButton :(NSWindow *)pWindow :(StandardWindowButtonType)enmButtonType;
+- (void)selectorForStandardWindowButton :(NSButton *)pButton;
+@end /* @interface UICocoaApplicationPrivate */
+
+@implementation UICocoaApplicationPrivate
+/** Performs initialization. */
+- (id) init
+{
+ self = [super init];
+ if (self)
+ m_pCallbacks = [[NSMutableArray alloc] init];
+
+ // WORKAROUND:
+ // Gently disable El Capitan tries to break everything with the Enter Full Screen action.
+ // S.a. https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/ for reference.
+ [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"];
+
+ return self;
+}
+
+/** Sends an event.
+ * @param pEvent Brings the event to be sent. */
+- (void) sendEvent :(NSEvent *)pEvent
+{
+ /* Check if the type matches any of the registered callbacks. */
+ uint32_t const fMask = m_fMask;
+#if 0 /* for debugging */
+ ::darwinPrintEvent("sendEvent: ", pEvent);
+#endif
+ if (fMask != 0)
+ {
+ NSEventType EvtType = [pEvent type];
+ uint32_t fEvtMask = RT_LIKELY(EvtType < 32) ? RT_BIT_32(EvtType) : 0;
+ if (fMask & fEvtMask)
+ {
+ /* Do the callouts in LIFO order. */
+ for (CallbackData *pData in [m_pCallbacks reverseObjectEnumerator])
+ {
+ if (pData->fMask & fEvtMask)
+ {
+ if (pData->pfnCallback(pEvent, [pEvent eventRef], pData->pvUser))
+ return;
+ }
+
+ }
+ }
+ }
+
+ /* Get on with it. */
+ [super sendEvent:pEvent];
+}
+
+/** Registers an event callback.
+ * @param fMask Brings the event mask for which the callback is to be invoked.
+ * @param pfnCallback Brings the callback function.
+ * @param pvUser Brings the user argument. */
+- (void) setCallback :(uint32_t)fMask :(PFNVBOXCACALLBACK)pfnCallback :(void *)pvUser
+{
+ /* Add the callback data to the array: */
+ CallbackData *pData = [[[CallbackData alloc] initWithMask:fMask callback:pfnCallback user:pvUser] autorelease];
+ [m_pCallbacks addObject:pData];
+
+ /* Update the global mask: */
+ m_fMask |= fMask;
+}
+
+/** Deregisters an event callback.
+ * @param fMask Brings the event mask for which the callback is to be invoked.
+ * @param pfnCallback Brings the callback function.
+ * @param pvUser Brings the user argument. */
+- (void) unsetCallback: (uint32_t)fMask :(PFNVBOXCACALLBACK)pfnCallback :(void *)pvUser
+{
+ /* Loop the event array LIFO fashion searching for a matching callback. */
+ for (CallbackData *pData in [m_pCallbacks reverseObjectEnumerator])
+ {
+ if ( pData->pfnCallback == pfnCallback
+ && pData->pvUser == pvUser
+ && pData->fMask == fMask)
+ {
+ [m_pCallbacks removeObject:pData];
+ break;
+ }
+ }
+ uint32_t fNewMask = 0;
+ for (CallbackData *pData in m_pCallbacks)
+ fNewMask |= pData->fMask;
+ m_fMask = fNewMask;
+}
+
+/** Registers to cocoa notification @a pstrNotificationName. */
+- (void) registerToNotificationOfWorkspace :(NSString *)pstrNotificationName
+{
+ /* Register notification observer: */
+ NSNotificationCenter *pNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
+ [pNotificationCenter addObserver:self
+ selector:@selector(notificationCallbackOfObject:)
+ name:pstrNotificationName
+ object:nil];
+}
+
+/** Unregister @a pWindow from cocoa notification @a pstrNotificationName. */
+- (void) unregisterFromNotificationOfWorkspace :(NSString *)pstrNotificationName
+{
+ /* Uninstall notification observer: */
+ NSNotificationCenter *pNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
+ [pNotificationCenter removeObserver:self
+ name:pstrNotificationName
+ object:nil];
+}
+
+/** Register @a pWindow to cocoa notification @a pstrNotificationName. */
+- (void) registerToNotificationOfWindow :(NSString *)pstrNotificationName :(NSWindow *)pWindow
+{
+ /* Register notification observer: */
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(notificationCallbackOfWindow:)
+ name:pstrNotificationName
+ object:pWindow];
+}
+
+/** Unregister @a pWindow from cocoa notification @a pstrNotificationName. */
+- (void) unregisterFromNotificationOfWindow :(NSString *)pstrNotificationName :(NSWindow *)pWindow
+{
+ /* Uninstall notification observer: */
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:pstrNotificationName
+ object:pWindow];
+}
+
+/** Redirects cocoa @a notification to UICocoaApplication instance. */
+- (void) notificationCallbackOfObject :(NSNotification *)notification
+{
+ /* Get current notification name: */
+ NSString *pstrName = [notification name];
+
+ /* Prepare user-info: */
+ QMap<QString, QString> userInfo;
+
+ /* Process known notifications: */
+ if ( [pstrName isEqualToString :@"NSWorkspaceDidActivateApplicationNotification"]
+ || [pstrName isEqualToString :@"NSWorkspaceDidDeactivateApplicationNotification"])
+ {
+ NSDictionary *pUserInfo = [notification userInfo];
+ NSRunningApplication *pApplication = [pUserInfo valueForKey :@"NSWorkspaceApplicationKey"];
+ NSString *pstrBundleIndentifier = [pApplication bundleIdentifier];
+ userInfo.insert("BundleIdentifier", darwinFromNativeString((NativeNSStringRef)pstrBundleIndentifier));
+ }
+
+ /* Redirect known notifications to objects: */
+ UICocoaApplication::instance()->nativeNotificationProxyForObject(pstrName, userInfo);
+}
+
+/** Redirects cocoa @a notification to UICocoaApplication instance. */
+- (void) notificationCallbackOfWindow :(NSNotification *)notification
+{
+ /* Get current notification name: */
+ NSString *pstrName = [notification name];
+
+ /* Redirect known notifications to widgets: */
+ UICocoaApplication::instance()->nativeNotificationProxyForWidget(pstrName, [notification object]);
+}
+
+/** Registers selector for standard window @a enmButtonType of the passed @a pWindow. */
+- (void)registerSelectorForStandardWindowButton :(NSWindow *)pWindow :(StandardWindowButtonType)enmButtonType
+{
+ /* Retrieve corresponding button: */
+ NSButton *pButton = Nil;
+ switch (enmButtonType)
+ {
+ case StandardWindowButtonType_Close: pButton = [pWindow standardWindowButton:NSWindowCloseButton]; break;
+ case StandardWindowButtonType_Miniaturize: pButton = [pWindow standardWindowButton:NSWindowMiniaturizeButton]; break;
+ case StandardWindowButtonType_Zoom: pButton = [pWindow standardWindowButton:NSWindowZoomButton]; break;
+ case StandardWindowButtonType_Toolbar: pButton = [pWindow standardWindowButton:NSWindowToolbarButton]; break;
+ case StandardWindowButtonType_DocumentIcon: pButton = [pWindow standardWindowButton:NSWindowDocumentIconButton]; break;
+ case StandardWindowButtonType_DocumentVersions: /*pButton = [pWindow standardWindowButton:NSWindowDocumentVersionsButton];*/ break;
+ case StandardWindowButtonType_FullScreen: /*pButton = [pWindow standardWindowButton:NSWindowFullScreenButton];*/ break;
+ }
+
+ /* Register selector if button exists: */
+ if (pButton != Nil)
+ {
+ [pButton setTarget:self];
+ [pButton setAction:@selector(selectorForStandardWindowButton:)];
+ }
+}
+
+/** Redirects selector of the standard window @a pButton to UICocoaApplication instance callback. */
+- (void)selectorForStandardWindowButton :(NSButton *)pButton
+{
+ /* Check if Option key is currently held: */
+ const bool fWithOptionKey = [NSEvent modifierFlags] & NSAlternateKeyMask;
+
+ /* Redirect selector to callback: */
+ UICocoaApplication::instance()->nativeCallbackProxyForStandardWindowButton(pButton, fWithOptionKey);
+}
+@end /* @implementation UICocoaApplicationPrivate */
+
+
+/*********************************************************************************************************************************
+* Class UICocoaApplication implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UICocoaApplication* UICocoaApplication::s_pInstance = 0;
+
+/* static */
+UICocoaApplication* UICocoaApplication::instance()
+{
+ if (!s_pInstance)
+ s_pInstance = new UICocoaApplication;
+
+ return s_pInstance;
+}
+
+UICocoaApplication::UICocoaApplication()
+{
+ /* Make sure our private NSApplication object is created: */
+ m_pNative = (UICocoaApplicationPrivate*)[UICocoaApplicationPrivate sharedApplication];
+ // WORKAROUND":
+ // Create one auto release pool which is in place for all the
+ // initialization and deinitialization stuff. That is when the
+ // NSApplication is not running the run loop (there is a separate
+ // auto release pool defined).
+ m_pPool = [[NSAutoreleasePool alloc] init];
+}
+
+UICocoaApplication::~UICocoaApplication()
+{
+ [m_pNative release];
+ [m_pPool release];
+}
+
+bool UICocoaApplication::isActive() const
+{
+ return [m_pNative isActive];
+}
+
+void UICocoaApplication::hide()
+{
+ [m_pNative hide:m_pNative];
+}
+
+void UICocoaApplication::hideUserElements()
+{
+ [m_pNative setPresentationOptions:NSApplicationPresentationHideMenuBar | NSApplicationPresentationHideDock];
+}
+
+void UICocoaApplication::registerForNativeEvents(uint32_t fMask, PFNVBOXCACALLBACK pfnCallback, void *pvUser)
+{
+ [m_pNative setCallback:fMask :pfnCallback :pvUser];
+}
+
+void UICocoaApplication::unregisterForNativeEvents(uint32_t fMask, PFNVBOXCACALLBACK pfnCallback, void *pvUser)
+{
+ [m_pNative unsetCallback:fMask :pfnCallback :pvUser];
+}
+
+void UICocoaApplication::registerToNotificationOfWorkspace(const QString &strNativeNotificationName, QObject *pObject,
+ PfnNativeNotificationCallbackForQObject pCallback)
+{
+ /* Make sure it is not registered yet: */
+ AssertReturnVoid(!m_objectCallbacks.contains(pObject) || !m_objectCallbacks[pObject].contains(strNativeNotificationName));
+
+ /* Remember callback: */
+ m_objectCallbacks[pObject][strNativeNotificationName] = pCallback;
+
+ /* Register observer: */
+ NativeNSStringRef pstrNativeNotificationName = darwinToNativeString(strNativeNotificationName.toLatin1().constData());
+ [m_pNative registerToNotificationOfWorkspace :pstrNativeNotificationName];
+}
+
+void UICocoaApplication::unregisterFromNotificationOfWorkspace(const QString &strNativeNotificationName, QObject *pObject)
+{
+ /* Make sure it is registered yet: */
+ AssertReturnVoid(m_objectCallbacks.contains(pObject) && m_objectCallbacks[pObject].contains(strNativeNotificationName));
+
+ /* Forget callback: */
+ m_objectCallbacks[pObject].remove(strNativeNotificationName);
+ if (m_objectCallbacks[pObject].isEmpty())
+ m_objectCallbacks.remove(pObject);
+
+ /* Unregister observer: */
+ NativeNSStringRef pstrNativeNotificationName = darwinToNativeString(strNativeNotificationName.toLatin1().constData());
+ [m_pNative unregisterFromNotificationOfWorkspace :pstrNativeNotificationName];
+}
+
+void UICocoaApplication::registerToNotificationOfWindow(const QString &strNativeNotificationName, QWidget *pWidget,
+ PfnNativeNotificationCallbackForQWidget pCallback)
+{
+ /* Make sure it is not registered yet: */
+ AssertReturnVoid(!m_widgetCallbacks.contains(pWidget) || !m_widgetCallbacks[pWidget].contains(strNativeNotificationName));
+
+ /* Remember callback: */
+ m_widgetCallbacks[pWidget][strNativeNotificationName] = pCallback;
+
+ /* Register observer: */
+ NativeNSStringRef pstrNativeNotificationName = darwinToNativeString(strNativeNotificationName.toLatin1().constData());
+ NativeNSWindowRef pWindow = darwinToNativeWindow(pWidget);
+ [m_pNative registerToNotificationOfWindow :pstrNativeNotificationName :pWindow];
+}
+
+void UICocoaApplication::unregisterFromNotificationOfWindow(const QString &strNativeNotificationName, QWidget *pWidget)
+{
+ /* Make sure it is registered yet: */
+ AssertReturnVoid(m_widgetCallbacks.contains(pWidget) && m_widgetCallbacks[pWidget].contains(strNativeNotificationName));
+
+ /* Forget callback: */
+ m_widgetCallbacks[pWidget].remove(strNativeNotificationName);
+ if (m_widgetCallbacks[pWidget].isEmpty())
+ m_widgetCallbacks.remove(pWidget);
+
+ /* Unregister observer: */
+ NativeNSStringRef pstrNativeNotificationName = darwinToNativeString(strNativeNotificationName.toLatin1().constData());
+ NativeNSWindowRef pWindow = darwinToNativeWindow(pWidget);
+ [m_pNative unregisterFromNotificationOfWindow :pstrNativeNotificationName :pWindow];
+}
+
+void UICocoaApplication::nativeNotificationProxyForObject(NativeNSStringRef pstrNotificationName,
+ const QMap<QString,
+ QString> &userInfo)
+{
+ /* Get notification name: */
+ QString strNotificationName = darwinFromNativeString(pstrNotificationName);
+
+ /* Check if existing object(s) have corresponding notification handler: */
+ foreach (QObject *pObject, m_objectCallbacks.keys())
+ {
+ const QMap<QString, PfnNativeNotificationCallbackForQObject> &callbacks = m_objectCallbacks[pObject];
+ if (callbacks.contains(strNotificationName))
+ callbacks[strNotificationName](pObject, userInfo);
+ }
+}
+
+void UICocoaApplication::nativeNotificationProxyForWidget(NativeNSStringRef pstrNotificationName, NativeNSWindowRef pWindow)
+{
+ /* Get notification name: */
+ QString strNotificationName = darwinFromNativeString(pstrNotificationName);
+
+ /* Check if existing widget(s) have corresponding notification handler: */
+ foreach (QWidget *pWidget, m_widgetCallbacks.keys())
+ {
+ if (darwinToNativeWindow(pWidget) == pWindow)
+ {
+ const QMap<QString, PfnNativeNotificationCallbackForQWidget> &callbacks = m_widgetCallbacks[pWidget];
+ if (callbacks.contains(strNotificationName))
+ callbacks[strNotificationName](strNotificationName, pWidget);
+ }
+ }
+}
+
+void UICocoaApplication::registerCallbackForStandardWindowButton(QWidget *pWidget, StandardWindowButtonType enmButtonType,
+ PfnStandardWindowButtonCallbackForQWidget pCallback)
+{
+ /* Make sure it is not registered yet: */
+ AssertReturnVoid( !m_stdWindowButtonCallbacks.contains(pWidget)
+ || !m_stdWindowButtonCallbacks.value(pWidget).contains(enmButtonType));
+
+ /* Remember callback: */
+ m_stdWindowButtonCallbacks[pWidget][enmButtonType] = pCallback;
+
+ /* Register selector: */
+ NativeNSWindowRef pWindow = darwinToNativeWindow(pWidget);
+ [m_pNative registerSelectorForStandardWindowButton :pWindow :enmButtonType];
+}
+
+void UICocoaApplication::unregisterCallbackForStandardWindowButton(QWidget *pWidget, StandardWindowButtonType enmButtonType)
+{
+ /* Make sure it is registered yet: */
+ AssertReturnVoid( m_stdWindowButtonCallbacks.contains(pWidget)
+ && m_stdWindowButtonCallbacks.value(pWidget).contains(enmButtonType));
+
+ /* Forget callback: */
+ m_stdWindowButtonCallbacks[pWidget].remove(enmButtonType);
+ if (m_stdWindowButtonCallbacks.value(pWidget).isEmpty())
+ m_stdWindowButtonCallbacks.remove(pWidget);
+}
+
+void UICocoaApplication::nativeCallbackProxyForStandardWindowButton(NativeNSButtonRef pButton, bool fWithOptionKey)
+{
+ // WORKAROUND:
+ // Why not using nested foreach, will you ask?
+ // It's because Qt 4.x has shadowing issue in Q_FOREACH macro.
+ // Bug record QTBUG-33585 opened for Qt 4.8.4 and closed as _won't fix_ by one of Qt devs.
+
+ /* Check if passed button is one of the buttons of the registered widget(s): */
+ const QList<QWidget*> widgets = m_stdWindowButtonCallbacks.keys();
+ for (int iWidgetIndex = 0; iWidgetIndex < widgets.size(); ++iWidgetIndex)
+ {
+ QWidget *pWidget = widgets.at(iWidgetIndex);
+ const QMap<StandardWindowButtonType, PfnStandardWindowButtonCallbackForQWidget> callbacks
+ = m_stdWindowButtonCallbacks.value(pWidget);
+ const QList<StandardWindowButtonType> buttonTypes = callbacks.keys();
+ for (int iButtonTypeIndex = 0; iButtonTypeIndex < buttonTypes.size(); ++iButtonTypeIndex)
+ {
+ StandardWindowButtonType enmButtonType = buttonTypes.at(iButtonTypeIndex);
+ if (darwinNativeButtonOfWindow(pWidget, enmButtonType) == pButton)
+ return callbacks.value(enmButtonType)(enmButtonType, fWithOptionKey, pWidget);
+ }
+ }
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.h
new file mode 100644
index 00000000..cd401f4a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.h
@@ -0,0 +1,56 @@
+/* $Id: UICocoaDockIconPreview.h $ */
+/** @file
+ * VBox Qt GUI - UICocoaDockIconPreview class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UICocoaDockIconPreview_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UICocoaDockIconPreview_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes */
+#include "UIAbstractDockIconPreview.h"
+
+class UICocoaDockIconPreviewPrivate;
+
+class UICocoaDockIconPreview: public UIAbstractDockIconPreview
+{
+public:
+ UICocoaDockIconPreview(UISession *pSession, const QPixmap& overlayImage);
+ ~UICocoaDockIconPreview();
+
+ virtual void updateDockOverlay();
+ virtual void updateDockPreview(CGImageRef VMImage);
+ virtual void updateDockPreview(UIFrameBuffer *pFrameBuffer);
+
+ virtual void setOriginalSize(int aWidth, int aHeight);
+
+private:
+ UICocoaDockIconPreviewPrivate *d;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UICocoaDockIconPreview_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.mm
new file mode 100644
index 00000000..baba2a4b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.mm
@@ -0,0 +1,342 @@
+/* $Id: UICocoaDockIconPreview.mm $ */
+/** @file
+ * VBox Qt GUI - Cocoa helper for the dock icon preview.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UICocoaDockIconPreview.h"
+#include "VBoxCocoaHelper.h"
+
+/* System includes */
+#import <Cocoa/Cocoa.h>
+
+@interface UIDockTileMonitor: NSView
+{
+ UICocoaDockIconPreviewPrivate *p;
+
+ NSImageView *mScreenContent;
+ NSImageView *mMonitorGlossy;
+}
+- (id)initWithFrame:(NSRect)frame parent:(UICocoaDockIconPreviewPrivate*)parent;
+- (NSImageView*)screenContent;
+- (void)resize:(NSSize)size;
+@end
+
+@interface UIDockTileOverlay: NSView
+{
+ UICocoaDockIconPreviewPrivate *p;
+}
+- (id)initWithFrame:(NSRect)frame parent:(UICocoaDockIconPreviewPrivate*)parent;
+@end
+
+@interface UIDockTile: NSView
+{
+ UICocoaDockIconPreviewPrivate *p;
+
+ UIDockTileMonitor *mMonitor;
+ NSImageView *mAppIcon;
+
+ UIDockTileOverlay *mOverlay;
+}
+- (id)initWithParent:(UICocoaDockIconPreviewPrivate*)parent;
+- (void)destroy;
+- (NSView*)screenContentWithParentView:(NSView*)parentView;
+- (void)cleanup;
+- (void)restoreAppIcon;
+- (void)updateAppIcon;
+- (void)restoreMonitor;
+- (void)updateMonitorWithImage:(CGImageRef)image;
+- (void)resizeMonitor:(NSSize)size;
+@end
+
+/*
+ * Helper class which allow us to access all members/methods of AbstractDockIconPreviewHelper
+ * from any Cocoa class.
+ */
+class UICocoaDockIconPreviewPrivate: public UIAbstractDockIconPreviewHelper
+{
+public:
+ inline UICocoaDockIconPreviewPrivate(UISession *pSession, const QPixmap& overlayImage)
+ :UIAbstractDockIconPreviewHelper(pSession, overlayImage)
+ {
+ mUIDockTile = [[UIDockTile alloc] initWithParent:this];
+ }
+
+ inline ~UICocoaDockIconPreviewPrivate()
+ {
+
+ [mUIDockTile destroy];
+ [mUIDockTile release];
+ }
+
+ UIDockTile *mUIDockTile;
+};
+
+/*
+ * Cocoa wrapper for the abstract dock icon preview class
+ */
+UICocoaDockIconPreview::UICocoaDockIconPreview(UISession *pSession, const QPixmap& overlayImage)
+ : UIAbstractDockIconPreview(pSession, overlayImage)
+{
+ CocoaAutoreleasePool pool;
+
+ d = new UICocoaDockIconPreviewPrivate(pSession, overlayImage);
+}
+
+UICocoaDockIconPreview::~UICocoaDockIconPreview()
+{
+ CocoaAutoreleasePool pool;
+
+ delete d;
+}
+
+void UICocoaDockIconPreview::updateDockOverlay()
+{
+ CocoaAutoreleasePool pool;
+
+ [d->mUIDockTile updateAppIcon];
+}
+
+void UICocoaDockIconPreview::updateDockPreview(CGImageRef VMImage)
+{
+ CocoaAutoreleasePool pool;
+
+ [d->mUIDockTile updateMonitorWithImage:VMImage];
+}
+
+void UICocoaDockIconPreview::updateDockPreview(UIFrameBuffer *pFrameBuffer)
+{
+ CocoaAutoreleasePool pool;
+
+ UIAbstractDockIconPreview::updateDockPreview(pFrameBuffer);
+}
+
+void UICocoaDockIconPreview::setOriginalSize(int width, int height)
+{
+ CocoaAutoreleasePool pool;
+
+ [d->mUIDockTile resizeMonitor:NSMakeSize(width, height)];
+}
+
+/*
+ * Class for arranging/updating the layers for the glossy monitor preview.
+ */
+@implementation UIDockTileMonitor
+- (id)initWithFrame:(NSRect)frame parent:(UICocoaDockIconPreviewPrivate*)parent
+{
+ self = [super initWithFrame:frame];
+
+ if (self != nil)
+ {
+ p = parent;
+ /* The screen content view */
+ mScreenContent = [[NSImageView alloc] initWithFrame:NSRectFromCGRect(p->flipRect(p->m_updateRect))];
+// [mScreenContent setImageAlignment: NSImageAlignCenter];
+ [mScreenContent setImageAlignment: NSImageAlignTopLeft];
+ [mScreenContent setImageScaling: NSImageScaleAxesIndependently];
+ [self addSubview: mScreenContent];
+ /* The state view */
+ mMonitorGlossy = [[NSImageView alloc] initWithFrame:NSRectFromCGRect(p->flipRect(p->m_monitorRect))];
+ [mMonitorGlossy setImage: ::darwinToNSImageRef(p->m_dockMonitorGlossy)];
+ [self addSubview: mMonitorGlossy];
+ }
+
+ return self;
+}
+
+- (void)drawRect:(NSRect)aRect
+{
+ NSImage *dockMonitor = ::darwinToNSImageRef(p->m_dockMonitor);
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ [dockMonitor drawInRect:NSRectFromCGRect(p->flipRect(p->m_monitorRect)) fromRect:aRect operation:NSCompositingOperationSourceOver fraction:1.0];
+#else
+ [dockMonitor drawInRect:NSRectFromCGRect(p->flipRect(p->m_monitorRect)) fromRect:aRect operation:NSCompositeSourceOver fraction:1.0];
+#endif
+ [dockMonitor release];
+}
+
+- (NSImageView*)screenContent
+{
+ return mScreenContent;
+}
+
+- (void)resize:(NSSize)size
+{
+ /* Calculate the new size based on the aspect ratio of the original screen
+ size */
+ float w, h;
+ if (size.width > size.height)
+ {
+ w = p->m_updateRect.size.width;
+ h = ((float)size.height / size.width * p->m_updateRect.size.height);
+ }
+ else
+ {
+ w = ((float)size.width / size.height * p->m_updateRect.size.width);
+ h = p->m_updateRect.size.height;
+ }
+ CGRect r = (p->flipRect (p->centerRectTo (CGRectMake (0, 0, (int)w, (int)h), p->m_updateRect)));
+ r.origin.x = (int)r.origin.x;
+ r.origin.y = (int)r.origin.y;
+ r.size.width = (int)r.size.width;
+ r.size.height = (int)r.size.height;
+// printf("gui %f %f %f %f\n", r.origin.x, r.origin.y, r.size.width, r.size.height);
+ /* Center within the update rect */
+ [mScreenContent setFrame:NSRectFromCGRect (r)];
+}
+@end
+
+/*
+ * Simple implementation for the overlay of the OS & the state icon. Is used both
+ * in the application icon & preview mode.
+ */
+@implementation UIDockTileOverlay
+- (id)initWithFrame:(NSRect)frame parent:(UICocoaDockIconPreviewPrivate*)parent
+{
+ self = [super initWithFrame:frame];
+
+ if (self != nil)
+ p = parent;
+
+ return self;
+}
+
+- (void)drawRect:(NSRect)aRect
+{
+ RT_NOREF(aRect);
+ NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
+ CGContextRef pCGContext = (CGContextRef)[nsContext graphicsPort];
+ p->drawOverlayIcons (pCGContext);
+}
+@end
+
+/*
+ * VirtualBox Dock Tile implementation. Manage the switching between the icon
+ * and preview mode & forwards all update request to the appropriate methods.
+ */
+@implementation UIDockTile
+- (id)initWithParent:(UICocoaDockIconPreviewPrivate*)parent
+{
+ self = [super init];
+
+ if (self != nil)
+ {
+ p = parent;
+ /* Add self as the content view of the dock tile */
+ NSDockTile *dock = [[NSApplication sharedApplication] dockTile];
+ [dock setContentView: self];
+ /* App icon is default */
+ [self restoreAppIcon];
+ /* The overlay */
+ mOverlay = [[UIDockTileOverlay alloc] initWithFrame:NSRectFromCGRect(p->flipRect (p->m_dockIconRect)) parent:p];
+ [self addSubview: mOverlay];
+ }
+
+ return self;
+}
+
+- (void)destroy
+{
+ /* Remove all content from the application dock tile. */
+ [mOverlay removeFromSuperview];
+ [mOverlay release];
+ mOverlay = nil;
+ NSDockTile *dock = [[NSApplication sharedApplication] dockTile];
+ [dock setContentView: nil];
+ /* Cleanup all other resources */
+ [self cleanup];
+}
+
+- (NSView*)screenContentWithParentView:(NSView*)parentView
+{
+ if (mMonitor != nil)
+ {
+ void *pId = p->currentPreviewWindowId();
+ if (parentView == pId)
+ return [mMonitor screenContent];
+ }
+ return nil;
+}
+
+- (void)cleanup
+{
+ if (mAppIcon != nil)
+ {
+ [mAppIcon removeFromSuperview];
+ [mAppIcon release];
+ mAppIcon = nil;
+ }
+ if (mMonitor != nil)
+ {
+ [mMonitor removeFromSuperview];
+ [mMonitor release];
+ mMonitor = nil;
+ }
+}
+
+- (void)restoreAppIcon
+{
+ if (mAppIcon == nil)
+ {
+ [self cleanup];
+ mAppIcon = [[NSImageView alloc] initWithFrame:NSRectFromCGRect (p->flipRect (p->m_dockIconRect))];
+ [mAppIcon setImage: [NSImage imageNamed:@"NSApplicationIcon"]];
+ [self addSubview: mAppIcon positioned:NSWindowBelow relativeTo:mOverlay];
+ }
+}
+
+- (void)updateAppIcon
+{
+ [self restoreAppIcon];
+ [[[NSApplication sharedApplication] dockTile] display];
+}
+
+- (void)restoreMonitor
+{
+ if (mMonitor == nil)
+ {
+ p->initPreviewImages();
+ [self cleanup];
+ mMonitor = [[UIDockTileMonitor alloc] initWithFrame:NSRectFromCGRect (p->flipRect (p->m_dockIconRect)) parent:p];
+ [self addSubview: mMonitor positioned:NSWindowBelow relativeTo:mOverlay];
+ }
+}
+
+- (void)updateMonitorWithImage:(CGImageRef)image
+{
+ [self restoreMonitor];
+ NSImage *nsimage = ::darwinToNSImageRef(image);
+ [[mMonitor screenContent] setImage: nsimage];
+ [nsimage release];
+ [[[NSApplication sharedApplication] dockTile] display];
+}
+
+- (void)resizeMonitor:(NSSize)size
+{
+ [self restoreMonitor];
+ [mMonitor resize:size];
+}
+@end
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.h
new file mode 100644
index 00000000..773ee8c9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.h
@@ -0,0 +1,99 @@
+/* $Id: UICocoaSpecialControls.h $ */
+/** @file
+ * VBox Qt GUI - UICocoaSpecialControls class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UICocoaSpecialControls_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UICocoaSpecialControls_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+#ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
+
+/* Qt includes: */
+#include <QWidget>
+#ifndef VBOX_IS_QT6_OR_LATER /** @todo qt6: ... */
+# include <QMacCocoaViewContainer>
+#endif
+
+/* GUI includes: */
+#include "VBoxCocoaHelper.h"
+#include "UILibraryDefs.h"
+
+/* Add typedefs for Cocoa types: */
+ADD_COCOA_NATIVE_REF(NSButton);
+
+/** QMacCocoaViewContainer extension,
+ * used as cocoa button container. */
+class SHARED_LIBRARY_STUFF UICocoaButton
+#ifdef VBOX_IS_QT6_OR_LATER /** @todo qt6: ... */
+ : public QWidget
+#else
+ : public QMacCocoaViewContainer
+#endif
+{
+ Q_OBJECT
+
+signals:
+
+ /** Notifies about button click and whether it's @a fChecked. */
+ void clicked(bool fChecked = false);
+
+public:
+
+ /** Cocoa button types. */
+ enum CocoaButtonType
+ {
+ HelpButton,
+ CancelButton,
+ ResetButton
+ };
+
+ /** Constructs cocoa button passing @a pParent to the base-class.
+ * @param enmType Brings the button type. */
+ UICocoaButton(QWidget *pParent, CocoaButtonType enmType);
+ /** Destructs cocoa button. */
+ ~UICocoaButton();
+
+ /** Returns size-hint. */
+ QSize sizeHint() const;
+
+ /** Defines button @a strText. */
+ void setText(const QString &strText);
+ /** Defines button @a strToolTip. */
+ void setToolTip(const QString &strToolTip);
+
+ /** Handles button click. */
+ void onClicked();
+
+private:
+
+ /** Returns native cocoa button reference. */
+ NativeNSButtonRef nativeRef() const { return static_cast<NativeNSButtonRef>(cocoaView()); }
+};
+
+#endif /* VBOX_DARWIN_USE_NATIVE_CONTROLS */
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UICocoaSpecialControls_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.mm
new file mode 100644
index 00000000..b83b2e15
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.mm
@@ -0,0 +1,179 @@
+/* $Id: UICocoaSpecialControls.mm $ */
+/** @file
+ * VBox Qt GUI - UICocoaSpecialControls implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
+
+/* Qt includes: */
+#include <QMacCocoaViewContainer>
+
+/* GUI includes: */
+#include "VBoxUtils-darwin.h"
+#include "UICocoaSpecialControls.h"
+
+/* System includes: */
+#import <AppKit/NSButton.h>
+
+
+/** Private button-target interface. */
+@interface UIButtonTargetPrivate : NSObject
+{
+ UICocoaButton *m_pRealTarget;
+}
+// WORKAROUND:
+// The next method used to be called initWithObject, but Xcode 4.1 preview 5
+// cannot cope with that for some reason. Hope this doesn't break anything.
+-(id)initWithObjectAndLionTrouble:(UICocoaButton*)pObject;
+-(IBAction)clicked:(id)pSender;
+@end
+
+
+/*********************************************************************************************************************************
+* Class UIButtonTargetPrivate implementation. *
+*********************************************************************************************************************************/
+
+@implementation UIButtonTargetPrivate
+-(id)initWithObjectAndLionTrouble:(UICocoaButton*)pObject
+{
+ self = [super init];
+
+ m_pRealTarget = pObject;
+
+ return self;
+}
+
+-(IBAction)clicked:(id)pSender
+{
+ Q_UNUSED(pSender);
+ m_pRealTarget->onClicked();
+}
+@end
+
+
+/*********************************************************************************************************************************
+* Class UICocoaButton implementation. *
+*********************************************************************************************************************************/
+
+UICocoaButton::UICocoaButton(QWidget *pParent, CocoaButtonType enmType)
+ : QMacCocoaViewContainer(0, pParent)
+{
+ /* Prepare auto-release pool: */
+ NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
+
+ /* Prepare native button reference: */
+ NativeNSButtonRef pNativeRef;
+ NSRect initFrame;
+
+ /* Configure button: */
+ switch (enmType)
+ {
+ case HelpButton:
+ {
+ pNativeRef = [[NSButton alloc] init];
+ [pNativeRef setTitle: @""];
+ [pNativeRef setBezelStyle: NSHelpButtonBezelStyle];
+ [pNativeRef setBordered: YES];
+ [pNativeRef setAlignment: NSCenterTextAlignment];
+ [pNativeRef sizeToFit];
+ initFrame = [pNativeRef frame];
+ initFrame.size.width += 12; /* Margin */
+ [pNativeRef setFrame:initFrame];
+ break;
+ };
+ case CancelButton:
+ {
+ pNativeRef = [[NSButton alloc] initWithFrame: NSMakeRect(0, 0, 13, 13)];
+ [pNativeRef setTitle: @""];
+ [pNativeRef setBezelStyle:NSShadowlessSquareBezelStyle];
+ [pNativeRef setButtonType:NSMomentaryChangeButton];
+ [pNativeRef setImage: [NSImage imageNamed: NSImageNameStopProgressFreestandingTemplate]];
+ [pNativeRef setBordered: NO];
+ [[pNativeRef cell] setImageScaling: NSImageScaleProportionallyDown];
+ initFrame = [pNativeRef frame];
+ break;
+ }
+ case ResetButton:
+ {
+ pNativeRef = [[NSButton alloc] initWithFrame: NSMakeRect(0, 0, 13, 13)];
+ [pNativeRef setTitle: @""];
+ [pNativeRef setBezelStyle:NSShadowlessSquareBezelStyle];
+ [pNativeRef setButtonType:NSMomentaryChangeButton];
+ [pNativeRef setImage: [NSImage imageNamed: NSImageNameRefreshFreestandingTemplate]];
+ [pNativeRef setBordered: NO];
+ [[pNativeRef cell] setImageScaling: NSImageScaleProportionallyDown];
+ initFrame = [pNativeRef frame];
+ break;
+ }
+ }
+
+ /* Install click listener: */
+ UIButtonTargetPrivate *bt = [[UIButtonTargetPrivate alloc] initWithObjectAndLionTrouble:this];
+ [pNativeRef setTarget:bt];
+ [pNativeRef setAction:@selector(clicked:)];
+
+ /* Put the button to the QCocoaViewContainer: */
+ setCocoaView(pNativeRef);
+ /* Release our reference, since our super class
+ * takes ownership and we don't need it anymore. */
+ [pNativeRef release];
+
+ /* Finally resize the widget: */
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ setFixedSize(NSWidth(initFrame), NSHeight(initFrame));
+
+ /* Cleanup auto-release pool: */
+ [pPool release];
+}
+
+UICocoaButton::~UICocoaButton()
+{
+}
+
+QSize UICocoaButton::sizeHint() const
+{
+ NSRect frame = [nativeRef() frame];
+ return QSize(frame.size.width, frame.size.height);
+}
+
+void UICocoaButton::setText(const QString &strText)
+{
+ QString s(strText);
+ /* Set it for accessibility reasons as alternative title: */
+ [nativeRef() setAlternateTitle: ::darwinQStringToNSString(s.remove('&'))];
+}
+
+void UICocoaButton::setToolTip(const QString &strToolTip)
+{
+ [nativeRef() setToolTip: ::darwinQStringToNSString(strToolTip)];
+}
+
+void UICocoaButton::onClicked()
+{
+ emit clicked(false);
+}
+
+#endif /* VBOX_DARWIN_USE_NATIVE_CONTROLS */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin.cpp
new file mode 100644
index 00000000..ca0ba697
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin.cpp
@@ -0,0 +1,48 @@
+/* $Id: UIDesktopServices_darwin.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to darwin..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UIDesktopServices.h"
+#include "UIDesktopServices_darwin_p.h"
+#include "VBoxUtils-darwin.h"
+
+/* Qt includes */
+#include <QString>
+
+bool UIDesktopServices::createMachineShortcut(const QString &strSrcFile, const QString &strDstPath, const QString &strName, const QUuid &uUuid)
+{
+ return ::darwinCreateMachineShortcut(::darwinToNativeString(strSrcFile.toUtf8().constData()),
+ ::darwinToNativeString(strDstPath.toUtf8().constData()),
+ ::darwinToNativeString(strName.toUtf8().constData()),
+ ::darwinToNativeString(uUuid.toString().toUtf8().constData()));
+}
+
+bool UIDesktopServices::openInFileManager(const QString &strFile)
+{
+ return ::darwinOpenInFileManager(::darwinToNativeString(strFile.toUtf8().constData()));
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_cocoa.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_cocoa.mm
new file mode 100644
index 00000000..95ea82d8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_cocoa.mm
@@ -0,0 +1,74 @@
+/* $Id: UIDesktopServices_darwin_cocoa.mm $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to darwin.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UIDesktopServices_darwin_p.h"
+
+/* System includes */
+#include <Carbon/Carbon.h>
+#import <AppKit/NSWorkspace.h>
+
+/* Create desktop alias using a bookmark stuff. */
+bool darwinCreateMachineShortcut(NativeNSStringRef pstrSrcFile, NativeNSStringRef pstrDstPath, NativeNSStringRef pstrName, NativeNSStringRef /* pstrUuid */)
+{
+ RT_NOREF(pstrName);
+ if (!pstrSrcFile || !pstrDstPath)
+ return false;
+
+ NSError *pErr = nil;
+ NSURL *pSrcUrl = [NSURL fileURLWithPath:pstrSrcFile];
+
+ NSString *pVmFileName = [pSrcUrl lastPathComponent];
+ NSString *pSrcPath = [NSString stringWithFormat:@"%@/%@", pstrDstPath, [pVmFileName stringByDeletingPathExtension]];
+ NSURL *pDstUrl = [NSURL fileURLWithPath:pSrcPath];
+
+ bool rc = false;
+
+ if (!pSrcUrl || !pDstUrl)
+ return false;
+
+ NSData *pBookmark = [pSrcUrl bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:&pErr];
+
+ if (pBookmark)
+ {
+ rc = [NSURL writeBookmarkData:pBookmark
+ toURL:pDstUrl
+ options:0
+ error:&pErr];
+ }
+
+ return rc;
+}
+
+bool darwinOpenInFileManager(NativeNSStringRef pstrFile)
+{
+ return [[NSWorkspace sharedWorkspace] selectFile:pstrFile inFileViewerRootedAtPath:@""];
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_p.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_p.h
new file mode 100644
index 00000000..a02e7d09
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_p.h
@@ -0,0 +1,47 @@
+/* $Id: UIDesktopServices_darwin_p.h $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to darwin..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UIDesktopServices_darwin_p_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UIDesktopServices_darwin_p_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/VBoxCocoa.h>
+#include <iprt/cdefs.h> /* for RT_C_DECLS_BEGIN/RT_C_DECLS_END & stuff */
+
+ADD_COCOA_NATIVE_REF(NSString);
+
+RT_C_DECLS_BEGIN
+
+bool darwinCreateMachineShortcut(NativeNSStringRef pstrSrcFile, NativeNSStringRef pstrDstPath, NativeNSStringRef pstrName, NativeNSStringRef pstrUuid);
+bool darwinOpenInFileManager(NativeNSStringRef pstrFile);
+
+RT_C_DECLS_END
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UIDesktopServices_darwin_p_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.cpp
new file mode 100644
index 00000000..01b4bcbe
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.cpp
@@ -0,0 +1,372 @@
+/* $Id: UIWindowMenuManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWindowMenuManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QActionGroup>
+#include <QApplication>
+#include <QMenu>
+
+/* GUI includes: */
+#include "UIWindowMenuManager.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/** QObject extension
+ * used as Mac OS X 'Window' menu helper. */
+class UIMenuHelper : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs menu-helper on the basis of passed @a windows. */
+ UIMenuHelper(const QList<QWidget*> &windows);
+ /** Destructs menu-helper. */
+ virtual ~UIMenuHelper() RT_OVERRIDE;
+
+ /** Returns 'Window' menu. */
+ QMenu *menu() const { return m_pWindowMenu; }
+
+ /** Adds @a pWindow into 'Window' menu. */
+ QAction *addWindow(QWidget *pWindow);
+ /** Removes @a pWindow from 'Window' menu. */
+ void removeWindow(QWidget *pWindow);
+
+ /** Handles translation event. */
+ void retranslateUi();
+
+ /** Updates toggle action states according to passed @a pActiveWindow. */
+ void updateStatus(QWidget *pActiveWindow);
+
+private slots:
+
+ /** Handles request to minimize active-window. */
+ void sltMinimizeActiveWindow();
+
+ /** Handles request to raise sender window. */
+ void sltRaiseSender();
+
+private:
+
+ /** Holds the 'Window' menu instance. */
+ QMenu *m_pWindowMenu;
+ /** Holds the action group instance. */
+ QActionGroup *m_pGroup;
+ /** Holds the 'Minimize' action instance. */
+ QAction *m_pMinimizeAction;
+ /** Holds the hash of the registered menu-helper instances. */
+ QHash<QWidget*, QAction*> m_windows;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMenuHelper implementation. *
+*********************************************************************************************************************************/
+
+UIMenuHelper::UIMenuHelper(const QList<QWidget*> &windows)
+{
+ /* Prepare 'Window' menu: */
+ m_pWindowMenu = new QMenu;
+
+ /* Prepare action group: */
+ m_pGroup = new QActionGroup(this);
+ m_pGroup->setExclusive(true);
+
+ /* Prepare 'Minimize' action: */
+ m_pMinimizeAction = new QAction(this);
+ m_pWindowMenu->addAction(m_pMinimizeAction);
+ connect(m_pMinimizeAction, SIGNAL(triggered(bool)),
+ this, SLOT(sltMinimizeActiveWindow()));
+
+ /* Make sure all already available windows are
+ * properly registered within this menu: */
+ for (int i = 0; i < windows.size(); ++i)
+ addWindow(windows.at(i));
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIMenuHelper::~UIMenuHelper()
+{
+ /* Cleanup 'Window' menu: */
+ delete m_pWindowMenu;
+
+ /* Cleanup actions: */
+ qDeleteAll(m_windows);
+}
+
+QAction *UIMenuHelper::addWindow(QWidget *pWindow)
+{
+ QAction *pAction = 0;
+ if ( pWindow
+ && !m_windows.contains(pWindow))
+ {
+ if (m_windows.size() < 2)
+ m_pWindowMenu->addSeparator();
+
+ /* The main window always first: */
+ pAction = new QAction(this);
+ pAction->setText(pWindow->windowTitle());
+ pAction->setMenuRole(QAction::NoRole);
+ pAction->setData(QVariant::fromValue(pWindow));
+ pAction->setCheckable(true);
+
+ /* The first registered one is always
+ * considered as the main window: */
+ if (m_windows.size() == 0)
+ pAction->setShortcut(QKeySequence("Ctrl+0"));
+ m_pGroup->addAction(pAction);
+ connect(pAction, SIGNAL(triggered(bool)),
+ this, SLOT(sltRaiseSender()));
+ m_pWindowMenu->addAction(pAction);
+ m_windows[pWindow] = pAction;
+ }
+ return pAction;
+}
+
+void UIMenuHelper::removeWindow(QWidget *pWindow)
+{
+ if (m_windows.contains(pWindow))
+ {
+ delete m_windows.value(pWindow);
+ m_windows.remove(pWindow);
+ }
+}
+
+void UIMenuHelper::retranslateUi()
+{
+ /* Translate menu: */
+ m_pWindowMenu->setTitle(QApplication::translate("UIActionPool", "&Window"));
+
+ /* Translate menu 'Minimize' action: */
+ m_pMinimizeAction->setText(QApplication::translate("UIActionPool", "&Minimize"));
+ m_pMinimizeAction->setShortcut(QKeySequence("Ctrl+M"));
+
+ /* Translate other menu-actions: */
+ foreach (QAction *pAction, m_windows.values())
+ {
+ /* Get corresponding window from action's data: */
+ QWidget *pWindow = pAction->data().value<QWidget*>();
+ /* Use the window's title as the action's text: */
+ pAction->setText(pWindow->windowTitle());
+ }
+}
+
+void UIMenuHelper::updateStatus(QWidget *pActiveWindow)
+{
+ /* 'Minimize' action is enabled if there is active-window: */
+ m_pMinimizeAction->setEnabled(pActiveWindow != 0);
+
+ /* If there is active-window: */
+ if (pActiveWindow)
+ {
+ /* Toggle corresponding action on: */
+ if (m_windows.contains(pActiveWindow))
+ m_windows.value(pActiveWindow)->setChecked(true);
+ }
+ /* If there is no active-window: */
+ else
+ {
+ /* Make sure corresponding action toggled off: */
+ if (QAction *pChecked = m_pGroup->checkedAction())
+ pChecked->setChecked(false);
+ }
+}
+
+void UIMenuHelper::sltMinimizeActiveWindow()
+{
+ if (QWidget *pActiveWindow = qApp->activeWindow())
+ pActiveWindow->showMinimized();
+}
+
+void UIMenuHelper::sltRaiseSender()
+{
+ AssertReturnVoid(sender());
+ if (QAction *pAction = qobject_cast<QAction*>(sender()))
+ {
+ if (QWidget *pWidget = pAction->data().value<QWidget*>())
+ {
+ pWidget->show();
+ pWidget->raise();
+ pWidget->activateWindow();
+ }
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWindowMenuManager implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIWindowMenuManager* UIWindowMenuManager::s_pInstance = 0;
+
+/* static */
+void UIWindowMenuManager::create()
+{
+ /* Make sure 'Window' menu Manager is not created: */
+ AssertReturnVoid(!s_pInstance);
+
+ /* Create 'Window' menu Manager: */
+ new UIWindowMenuManager;
+}
+
+/* static */
+void UIWindowMenuManager::destroy()
+{
+ /* Make sure 'Window' menu Manager is created: */
+ AssertPtrReturnVoid(s_pInstance);
+
+ /* Delete 'Window' menu Manager: */
+ delete s_pInstance;
+}
+
+QMenu *UIWindowMenuManager::createMenu(QWidget *pWindow)
+{
+ /* Create helper: */
+ UIMenuHelper *pHelper = new UIMenuHelper(m_windows);
+ /* Register it: */
+ m_helpers[pWindow] = pHelper;
+
+ /* Return menu of created helper: */
+ return pHelper->menu();
+}
+
+void UIWindowMenuManager::destroyMenu(QWidget *pWindow)
+{
+ /* If window is registered: */
+ if (m_helpers.contains(pWindow))
+ {
+ /* Delete helper: */
+ delete m_helpers.value(pWindow);
+ /* Unregister it: */
+ m_helpers.remove(pWindow);
+ }
+}
+
+void UIWindowMenuManager::addWindow(QWidget *pWindow)
+{
+ /* Register window: */
+ m_windows.append(pWindow);
+ /* Add window to all menus we have: */
+ foreach (UIMenuHelper *pHelper, m_helpers.values())
+ pHelper->addWindow(pWindow);
+}
+
+void UIWindowMenuManager::removeWindow(QWidget *pWindow)
+{
+ /* Remove window from all menus we have: */
+ foreach (UIMenuHelper *pHelper, m_helpers.values())
+ pHelper->removeWindow(pWindow);
+ /* Unregister window: */
+ m_windows.removeAll(pWindow);
+}
+
+void UIWindowMenuManager::retranslateUi()
+{
+ /* Translate all the helpers: */
+ foreach (UIMenuHelper *pHelper, m_helpers.values())
+ pHelper->retranslateUi();
+}
+
+UIWindowMenuManager::UIWindowMenuManager()
+{
+ /* Assign instance: */
+ s_pInstance = this;
+
+ /* Install global event-filter: */
+ qApp->installEventFilter(this);
+}
+
+UIWindowMenuManager::~UIWindowMenuManager()
+{
+ /* Cleanup all helpers: */
+ qDeleteAll(m_helpers);
+
+ /* Unassign instance: */
+ s_pInstance = 0;
+}
+
+bool UIWindowMenuManager::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Acquire event type: */
+ const QEvent::Type type = pEvent->type();
+
+#ifdef VBOX_OSE /// @todo Do we still need it?
+ // WORKAROUND:
+ // Stupid Qt: Qt doesn't check if a window is minimized when a command is
+ // executed. This leads to strange behaviour. The minimized window is
+ // partly restored, but not usable. As a workaround we raise the parent
+ // window before we let execute the command.
+ // Note: fixed in our local Qt build since 4.7.0.
+ if (pObject && type == QEvent::Show)
+ {
+ QWidget *pWidget = qobject_cast<QWidget*>(pObject);
+ if ( pWidget
+ && pWidget->parentWidget()
+ && pWidget->parentWidget()->isMinimized())
+ {
+ pWidget->parentWidget()->show();
+ pWidget->parentWidget()->raise();
+ pWidget->parentWidget()->activateWindow();
+ }
+ }
+#endif /* VBOX_OSE */
+
+ /* We need to track several events which leads to different window
+ * activation and change the menu items in that case. */
+ if ( type == QEvent::ActivationChange
+ || type == QEvent::WindowActivate
+ || type == QEvent::WindowDeactivate
+ || type == QEvent::WindowStateChange
+ || type == QEvent::Show
+ || type == QEvent::Close
+ || type == QEvent::Hide)
+ {
+ QWidget *pActiveWindow = qApp->activeWindow();
+ foreach (UIMenuHelper *pHelper, m_helpers.values())
+ pHelper->updateStatus(pActiveWindow);
+ }
+
+ /* Besides our own retranslation, we should also retranslate
+ * everything on any registered widget title change event: */
+ if (pObject && type == QEvent::WindowTitleChange)
+ {
+ QWidget *pWidget = qobject_cast<QWidget*>(pObject);
+ if (pWidget && m_helpers.contains(pWidget))
+ retranslateUi();
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI3<QObject>::eventFilter(pObject, pEvent);
+}
+
+
+#include "UIWindowMenuManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.h
new file mode 100644
index 00000000..33d10ab0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.h
@@ -0,0 +1,98 @@
+/* $Id: UIWindowMenuManager.h $ */
+/** @file
+ * VBox Qt GUI - UIWindowMenuManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UIWindowMenuManager_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UIWindowMenuManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QHash>
+#include <QObject>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QMenu;
+class UIMenuHelper;
+
+/** Singleton QObject extension
+ * used as Mac OS X 'Window' menu Manager. */
+class SHARED_LIBRARY_STUFF UIWindowMenuManager : public QIWithRetranslateUI3<QObject>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Creates instance. */
+ static void create();
+ /** Destroyes instance. */
+ static void destroy();
+ /** Returns current instance. */
+ static UIWindowMenuManager *instance() { return s_pInstance; }
+
+ /** Creates 'Window' menu for passed @a pWindow. */
+ QMenu *createMenu(QWidget *pWindow);
+ /** Destroys 'Window' menu for passed @a pWindow. */
+ void destroyMenu(QWidget *pWindow);
+
+ /** Adds @a pWindow to all 'Window' menus. */
+ void addWindow(QWidget *pWindow);
+ /** Removes @a pWindow from all 'Window' menus. */
+ void removeWindow(QWidget *pWindow);
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+protected:
+
+ /** Constructs 'Window' menu Manager. */
+ UIWindowMenuManager();
+ /** Destructs 'Window' menu Manager. */
+ ~UIWindowMenuManager();
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Holds the static instance. */
+ static UIWindowMenuManager *s_pInstance;
+
+ /** Holds the list of the registered window references. */
+ QList<QWidget*> m_windows;
+
+ /** Holds the hash of the registered menu-helper instances. */
+ QHash<QWidget*, UIMenuHelper*> m_helpers;
+};
+
+/** Singleton 'Window' menu Manager 'official' name. */
+#define gpWindowMenuManager UIWindowMenuManager::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UIWindowMenuManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxCocoaHelper.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxCocoaHelper.h
new file mode 100644
index 00000000..1156292c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxCocoaHelper.h
@@ -0,0 +1,73 @@
+/* $Id: VBoxCocoaHelper.h $ */
+/** @file
+ * VBox Qt GUI - VBoxCocoa Helper.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_VBoxCocoaHelper_h
+#define FEQT_INCLUDED_SRC_platform_darwin_VBoxCocoaHelper_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Global includes */
+#include <VBox/VBoxCocoa.h>
+
+#ifdef __OBJC__
+
+/* System includes */
+#import <AppKit/NSImage.h>
+#import <Foundation/NSAutoreleasePool.h>
+#import <CoreFoundation/CFString.h>
+
+/* Qt includes */
+#include <QString>
+#include <QVarLengthArray>
+
+inline NSString *darwinQStringToNSString(const QString &aString)
+{
+ const UniChar *chars = reinterpret_cast<const UniChar *>(aString.unicode());
+ CFStringRef str = CFStringCreateWithCharacters(0, chars, aString.length());
+ return [(NSString*)CFStringCreateMutableCopy(0, 0, str) autorelease];
+}
+
+inline QString darwinNSStringToQString(const NSString *aString)
+{
+ CFStringRef str = reinterpret_cast<const CFStringRef>(aString);
+ if(!str)
+ return QString();
+ CFIndex length = CFStringGetLength(str);
+ const UniChar *chars = CFStringGetCharactersPtr(str);
+ if (chars)
+ return QString(reinterpret_cast<const QChar *>(chars), length);
+
+ QVarLengthArray<UniChar> buffer(length);
+ CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
+ return QString(reinterpret_cast<const QChar *>(buffer.constData()), length);
+}
+
+#endif /* __OBJC__ */
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_VBoxCocoaHelper_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin-cocoa.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin-cocoa.mm
new file mode 100644
index 00000000..cd355442
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin-cocoa.mm
@@ -0,0 +1,730 @@
+/* $Id: VBoxUtils-darwin-cocoa.mm $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling Darwin Cocoa specific tasks.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "VBoxUtils-darwin.h"
+#include "VBoxCocoaHelper.h"
+
+#include <QMenu>
+
+#include <iprt/assert.h>
+
+#import <AppKit/NSEvent.h>
+#import <AppKit/NSColor.h>
+#import <AppKit/NSFont.h>
+#import <AppKit/NSScreen.h>
+#import <AppKit/NSScroller.h>
+#import <AppKit/NSWindow.h>
+#import <AppKit/NSImageView.h>
+
+#import <objc/objc-class.h>
+
+/* For the keyboard stuff */
+#include <Carbon/Carbon.h>
+#include "DarwinKeyboard.h"
+
+/** Easy way of dynamical call for 10.7 AppKit functionality we do not yet support. */
+#define NSWindowCollectionBehaviorFullScreenPrimary (1 << 7)
+#define NSFullScreenWindowMask (1 << 14)
+
+NativeNSWindowRef darwinToNativeWindowImpl(NativeNSViewRef pView)
+{
+ NativeNSWindowRef window = NULL;
+ if (pView)
+ window = [pView window];
+
+ return window;
+}
+
+NativeNSViewRef darwinToNativeViewImpl(NativeNSWindowRef pWindow)
+{
+ NativeNSViewRef view = NULL;
+ if (pWindow)
+ view = [pWindow contentView];
+
+ return view;
+}
+
+NativeNSButtonRef darwinNativeButtonOfWindowImpl(NativeNSWindowRef pWindow, StandardWindowButtonType enmButtonType)
+{
+ /* Return corresponding button: */
+ switch (enmButtonType)
+ {
+ case StandardWindowButtonType_Close: return [pWindow standardWindowButton:NSWindowCloseButton];
+ case StandardWindowButtonType_Miniaturize: return [pWindow standardWindowButton:NSWindowMiniaturizeButton];
+ case StandardWindowButtonType_Zoom: return [pWindow standardWindowButton:NSWindowZoomButton];
+ case StandardWindowButtonType_Toolbar: return [pWindow standardWindowButton:NSWindowToolbarButton];
+ case StandardWindowButtonType_DocumentIcon: return [pWindow standardWindowButton:NSWindowDocumentIconButton];
+ case StandardWindowButtonType_DocumentVersions: /*return [pWindow standardWindowButton:NSWindowDocumentVersionsButton];*/ break;
+ case StandardWindowButtonType_FullScreen: /*return [pWindow standardWindowButton:NSWindowFullScreenButton];*/ break;
+ }
+ /* Return Nul by default: */
+ return Nil;
+}
+
+NativeNSImageRef darwinToNSImageRef(const CGImageRef pImage)
+{
+ /* Create a bitmap rep from the image. */
+ NSBitmapImageRep *bitmapRep = [[[NSBitmapImageRep alloc] initWithCGImage:pImage] autorelease];
+ /* Create an NSImage and add the bitmap rep to it */
+ NSImage *image = [[NSImage alloc] init];
+ [image addRepresentation:bitmapRep];
+ return image;
+}
+
+NativeNSImageRef darwinToNSImageRef(const QImage *pImage)
+{
+ /* Create CGImage on the basis of passed QImage: */
+ CGImageRef pCGImage = ::darwinToCGImageRef(pImage);
+ NativeNSImageRef pNSImage = ::darwinToNSImageRef(pCGImage);
+ CGImageRelease(pCGImage);
+ /* Apply device pixel ratio: */
+ double dScaleFactor = pImage->devicePixelRatio();
+ NSSize imageSize = { (CGFloat)pImage->width() / dScaleFactor,
+ (CGFloat)pImage->height() / dScaleFactor };
+ [pNSImage setSize:imageSize];
+ /* Return result: */
+ return pNSImage;
+}
+
+NativeNSImageRef darwinToNSImageRef(const QPixmap *pPixmap)
+{
+ CGImageRef pCGImage = ::darwinToCGImageRef(pPixmap);
+ NativeNSImageRef pNSImage = ::darwinToNSImageRef(pCGImage);
+ CGImageRelease(pCGImage);
+ return pNSImage;
+}
+
+NativeNSImageRef darwinToNSImageRef(const char *pczSource)
+{
+ CGImageRef pCGImage = ::darwinToCGImageRef(pczSource);
+ NativeNSImageRef pNSImage = ::darwinToNSImageRef(pCGImage);
+ CGImageRelease(pCGImage);
+ return pNSImage;
+}
+
+NativeNSStringRef darwinToNativeString(const char* pcszString)
+{
+ return [NSString stringWithUTF8String: pcszString];
+}
+
+QString darwinFromNativeString(NativeNSStringRef pString)
+{
+ return [pString cStringUsingEncoding :NSASCIIStringEncoding];
+}
+
+void darwinSetShowsToolbarButtonImpl(NativeNSWindowRef pWindow, bool fEnabled)
+{
+ [pWindow setShowsToolbarButton:fEnabled];
+}
+
+void darwinLabelWindow(NativeNSWindowRef pWindow, NativeNSImageRef pImage, double dDpr)
+{
+ /* Get the parent view of the close button. */
+ NSView *wv = [[pWindow standardWindowButton:NSWindowCloseButton] superview];
+ if (wv)
+ {
+ /* We have to calculate the size of the title bar for the center case. */
+ NSSize s = [pImage size];
+ NSSize s1 = [wv frame].size;
+ /* Correctly position the label. */
+ NSImageView *iv = [[NSImageView alloc] initWithFrame:NSMakeRect(s1.width - s.width / dDpr,
+ s1.height - s.height / dDpr - 1,
+ s.width / dDpr, s.height / dDpr)];
+ /* Configure the NSImageView for auto moving. */
+ [iv setImage:pImage];
+ [iv setAutoresizesSubviews:true];
+ [iv setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
+ /* Add it to the parent of the close button. */
+ [wv addSubview:iv positioned:NSWindowBelow relativeTo:nil];
+ }
+}
+
+void darwinSetShowsResizeIndicatorImpl(NativeNSWindowRef pWindow, bool fEnabled)
+{
+ [pWindow setShowsResizeIndicator:fEnabled];
+}
+
+void darwinSetHidesAllTitleButtonsImpl(NativeNSWindowRef pWindow)
+{
+ /* Remove all title buttons by changing the style mask. This method is
+ available from 10.6 on only. */
+ if ([pWindow respondsToSelector: @selector(setStyleMask:)])
+ [pWindow performSelector: @selector(setStyleMask:) withObject: (id)NSTitledWindowMask];
+ else
+ {
+ /* On pre 10.6 disable all the buttons currently displayed. Don't use
+ setHidden cause this remove the buttons, but didn't release the
+ place used for the buttons. */
+ NSButton *pButton = [pWindow standardWindowButton:NSWindowCloseButton];
+ if (pButton != Nil)
+ [pButton setEnabled: NO];
+ pButton = [pWindow standardWindowButton:NSWindowMiniaturizeButton];
+ if (pButton != Nil)
+ [pButton setEnabled: NO];
+ pButton = [pWindow standardWindowButton:NSWindowZoomButton];
+ if (pButton != Nil)
+ [pButton setEnabled: NO];
+ pButton = [pWindow standardWindowButton:NSWindowDocumentIconButton];
+ if (pButton != Nil)
+ [pButton setEnabled: NO];
+ }
+}
+
+void darwinSetShowsWindowTransparentImpl(NativeNSWindowRef pWindow, bool fEnabled)
+{
+ if (fEnabled)
+ {
+ [pWindow setOpaque:NO];
+ [pWindow setBackgroundColor:[NSColor clearColor]];
+ [pWindow setHasShadow:NO];
+ }
+ else
+ {
+ [pWindow setOpaque:YES];
+ [pWindow setBackgroundColor:[NSColor windowBackgroundColor]];
+ [pWindow setHasShadow:YES];
+ }
+}
+
+void darwinSetWindowHasShadow(NativeNSWindowRef pWindow, bool fEnabled)
+{
+ if (fEnabled)
+ [pWindow setHasShadow :YES];
+ else
+ [pWindow setHasShadow :NO];
+}
+
+void darwinMinaturizeWindow(NativeNSWindowRef pWindow)
+{
+ RT_NOREF(pWindow);
+// [[NSApplication sharedApplication] miniaturizeAll];
+// printf("bla\n");
+// [pWindow miniaturize:pWindow];
+// [[NSApplication sharedApplication] deactivate];
+// [pWindow performMiniaturize:nil];
+}
+
+void darwinEnableFullscreenSupport(NativeNSWindowRef pWindow)
+{
+ [pWindow setCollectionBehavior :NSWindowCollectionBehaviorFullScreenPrimary];
+}
+
+void darwinEnableTransienceSupport(NativeNSWindowRef pWindow)
+{
+ [pWindow setCollectionBehavior :NSWindowCollectionBehaviorTransient];
+}
+
+void darwinToggleFullscreenMode(NativeNSWindowRef pWindow)
+{
+ /* Toggle native fullscreen mode for passed pWindow. This method is available since 10.7 only. */
+ if ([pWindow respondsToSelector: @selector(toggleFullScreen:)])
+ [pWindow performSelector: @selector(toggleFullScreen:) withObject: (id)nil];
+}
+
+void darwinToggleWindowZoom(NativeNSWindowRef pWindow)
+{
+ /* Toggle native window zoom for passed pWindow. This method is available since 10.0. */
+ if ([pWindow respondsToSelector: @selector(zoom:)])
+ [pWindow performSelector: @selector(zoom:)];
+}
+
+bool darwinIsInFullscreenMode(NativeNSWindowRef pWindow)
+{
+ /* Check whether passed pWindow is in native fullscreen mode. */
+ return [pWindow styleMask] & NSFullScreenWindowMask;
+}
+
+bool darwinIsOnActiveSpace(NativeNSWindowRef pWindow)
+{
+ /* Check whether passed pWindow is on active space. */
+ return [pWindow isOnActiveSpace];
+}
+
+bool darwinScreensHaveSeparateSpaces()
+{
+ /* Check whether screens have separate spaces.
+ * This method is available since 10.9 only. */
+ if ([NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)])
+ return [NSScreen performSelector: @selector(screensHaveSeparateSpaces)];
+ else
+ return false;
+}
+
+bool darwinIsScrollerStyleOverlay()
+{
+ /* Check whether scrollers by default have legacy style.
+ * This method is available since 10.7 only. */
+ if ([NSScroller respondsToSelector: @selector(preferredScrollerStyle)])
+ {
+ const int enmType = (int)(intptr_t)[NSScroller performSelector: @selector(preferredScrollerStyle)];
+ return enmType == NSScrollerStyleOverlay;
+ }
+ else
+ return false;
+}
+
+/**
+ * Calls the + (void)setMouseCoalescingEnabled:(BOOL)flag class method.
+ *
+ * @param fEnabled Whether to enable or disable coalescing.
+ */
+void darwinSetMouseCoalescingEnabled(bool fEnabled)
+{
+ [NSEvent setMouseCoalescingEnabled:fEnabled];
+}
+
+void darwinWindowAnimateResizeImpl(NativeNSWindowRef pWindow, int x, int y, int width, int height)
+{
+ RT_NOREF(x, y, width);
+
+ /* It seems that Qt doesn't return the height of the window with the
+ * toolbar height included. So add this size manually. Could easily be that
+ * the Trolls fix this in the final release. */
+ NSToolbar *toolbar = [pWindow toolbar];
+ NSRect windowFrame = [pWindow frame];
+ int toolbarHeight = 0;
+ if(toolbar && [toolbar isVisible])
+ toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]);
+ int h = height + toolbarHeight;
+ int h1 = h - NSHeight(windowFrame);
+ windowFrame.size.height = h;
+ windowFrame.origin.y -= h1;
+
+ [pWindow setFrame:windowFrame display:YES animate: YES];
+}
+
+void darwinWindowAnimateResizeNewImpl(NativeNSWindowRef pWindow, int height, bool fAnimate)
+{
+ /* It seems that Qt doesn't return the height of the window with the
+ * toolbar height included. So add this size manually. Could easily be that
+ * the Trolls fix this in the final release. */
+ NSToolbar *toolbar = [pWindow toolbar];
+ NSRect windowFrame = [pWindow frame];
+ int toolbarHeight = 0;
+ if(toolbar && [toolbar isVisible])
+ toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]);
+ int h = height + toolbarHeight;
+ int h1 = h - NSHeight(windowFrame);
+ windowFrame.size.height = h;
+ windowFrame.origin.y -= h1;
+
+ [pWindow setFrame:windowFrame display:YES animate: fAnimate ? YES : NO];
+}
+
+void darwinTest(NativeNSViewRef pViewOld, NativeNSViewRef pViewNew, int h)
+{
+ NSMutableDictionary *pDicts[3] = { nil, nil, nil };
+ int c = 0;
+
+ /* Scaling necessary? */
+ if (h != -1)
+ {
+ NSWindow *pWindow = [(pViewOld ? pViewOld : pViewNew) window];
+ NSToolbar *toolbar = [pWindow toolbar];
+ NSRect windowFrame = [pWindow frame];
+ /* Dictionary containing all animation parameters. */
+ pDicts[c] = [NSMutableDictionary dictionaryWithCapacity:2];
+ /* Specify the animation target. */
+ [pDicts[c] setObject:pWindow forKey:NSViewAnimationTargetKey];
+ /* Scaling effect. */
+ [pDicts[c] setObject:[NSValue valueWithRect:windowFrame] forKey:NSViewAnimationStartFrameKey];
+ int toolbarHeight = 0;
+ if(toolbar && [toolbar isVisible])
+ toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]);
+ int h1 = h + toolbarHeight;
+ int h2 = h1 - NSHeight(windowFrame);
+ windowFrame.size.height = h1;
+ windowFrame.origin.y -= h2;
+ [pDicts[c] setObject:[NSValue valueWithRect:windowFrame] forKey:NSViewAnimationEndFrameKey];
+ ++c;
+ }
+ /* Fade out effect. */
+ if (pViewOld)
+ {
+ /* Dictionary containing all animation parameters. */
+ pDicts[c] = [NSMutableDictionary dictionaryWithCapacity:2];
+ /* Specify the animation target. */
+ [pDicts[c] setObject:pViewOld forKey:NSViewAnimationTargetKey];
+ /* Fade out effect. */
+ [pDicts[c] setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
+ ++c;
+ }
+ /* Fade in effect. */
+ if (pViewNew)
+ {
+ /* Dictionary containing all animation parameters. */
+ pDicts[c] = [NSMutableDictionary dictionaryWithCapacity:2];
+ /* Specify the animation target. */
+ [pDicts[c] setObject:pViewNew forKey:NSViewAnimationTargetKey];
+ /* Fade in effect. */
+ [pDicts[c] setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
+ ++c;
+ }
+ /* Create our animation object. */
+ NSViewAnimation *pAni = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:pDicts count:c]];
+ [pAni setDuration:.15];
+ [pAni setAnimationCurve:NSAnimationEaseIn];
+ [pAni setAnimationBlockingMode:NSAnimationBlocking];
+// [pAni setAnimationBlockingMode:NSAnimationNonblockingThreaded];
+
+ /* Run the animation. */
+ [pAni startAnimation];
+ /* Cleanup */
+ [pAni release];
+}
+
+void darwinWindowInvalidateShadowImpl(NativeNSWindowRef pWindow)
+{
+ [pWindow invalidateShadow];
+}
+
+int darwinWindowToolBarHeight(NativeNSWindowRef pWindow)
+{
+ NSToolbar *toolbar = [pWindow toolbar];
+ NSRect windowFrame = [pWindow frame];
+ int toolbarHeight = 0;
+ int theight = (NSHeight([NSWindow contentRectForFrameRect:[pWindow frame] styleMask:[pWindow styleMask]]) - NSHeight([[pWindow contentView] frame]));
+ /* toolbar height: */
+ if(toolbar && [toolbar isVisible])
+ /* title bar height: */
+ toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]) - theight;
+
+ return toolbarHeight;
+}
+
+int darwinWindowTitleHeight(NativeNSWindowRef pWindow)
+{
+ NSView *pSuperview = [[pWindow standardWindowButton:NSWindowCloseButton] superview];
+ NSSize sz = [pSuperview frame].size;
+ return sz.height;
+}
+
+bool darwinIsToolbarVisible(NativeNSWindowRef pWindow)
+{
+ NSToolbar *pToolbar = [pWindow toolbar];
+
+ return [pToolbar isVisible] == YES;
+}
+
+bool darwinIsWindowMaximized(NativeNSWindowRef pWindow)
+{
+ /* Mac OS X API NSWindow isZoomed returns true even for almost maximized windows,
+ * So implementing this by ourseleves by comparing visible screen-frame & window-frame: */
+ NSRect windowFrame = [pWindow frame];
+ NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
+
+ return (windowFrame.origin.x == screenFrame.origin.x) &&
+ (windowFrame.origin.y == screenFrame.origin.y) &&
+ (windowFrame.size.width == screenFrame.size.width) &&
+ (windowFrame.size.height == screenFrame.size.height);
+}
+
+bool darwinOpenFile(NativeNSStringRef pstrFile)
+{
+ return [[NSWorkspace sharedWorkspace] openFile:pstrFile];
+}
+
+float darwinSmallFontSize()
+{
+ float size = [NSFont systemFontSizeForControlSize: NSSmallControlSize];
+
+ return size;
+}
+
+bool darwinMouseGrabEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
+{
+ NSEvent *pEvent = (NSEvent*)pvCocoaEvent;
+ NSEventType EvtType = [pEvent type];
+ NSWindow *pWin = ::darwinToNativeWindow((QWidget*)pvUser);
+ if ( pWin == [pEvent window]
+ && ( EvtType == NSLeftMouseDown
+ || EvtType == NSLeftMouseUp
+ || EvtType == NSRightMouseDown
+ || EvtType == NSRightMouseUp
+ || EvtType == NSOtherMouseDown
+ || EvtType == NSOtherMouseUp
+ || EvtType == NSLeftMouseDragged
+ || EvtType == NSRightMouseDragged
+ || EvtType == NSOtherMouseDragged
+ || EvtType == NSMouseMoved
+ || EvtType == NSScrollWheel))
+ {
+ /* When the mouse position is not associated to the mouse cursor, the x
+ and y values are reported as delta values. */
+ float x = [pEvent deltaX];
+ float y = [pEvent deltaY];
+ if (EvtType == NSScrollWheel)
+ {
+ /* In the scroll wheel case we have to do some magic, cause a
+ normal scroll wheel on a mouse behaves different to a trackpad.
+ The following is used within Qt. We use the same to get a
+ similar behavior. */
+ if ([pEvent respondsToSelector:@selector(deviceDeltaX:)])
+ x = (float)(intptr_t)[pEvent performSelector:@selector(deviceDeltaX)] * 2;
+ else
+ x = qBound(-120, (int)(x * 10000), 120);
+ if ([pEvent respondsToSelector:@selector(deviceDeltaY:)])
+ y = (float)(intptr_t)[pEvent performSelector:@selector(deviceDeltaY)] * 2;
+ else
+ y = qBound(-120, (int)(y * 10000), 120);
+ }
+ /* Get the buttons which where pressed when this event occurs. We have
+ to use Carbon here, cause the Cocoa method [NSEvent pressedMouseButtons]
+ is >= 10.6. */
+ uint32 buttonMask = 0;
+ GetEventParameter((EventRef)pvCarbonEvent, kEventParamMouseChord, typeUInt32, 0,
+ sizeof(buttonMask), 0, &buttonMask);
+ /* Produce a Qt event out of our info. */
+ ::darwinSendMouseGrabEvents((QWidget*)pvUser, EvtType, [pEvent buttonNumber], buttonMask, x, y);
+ return true;
+ }
+ return false;
+}
+
+/* Cocoa event handler which checks if the user right clicked at the unified
+ toolbar or the title area. */
+bool darwinUnifiedToolbarEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
+{
+ RT_NOREF(pvCarbonEvent);
+
+ NSEvent *pEvent = (NSEvent*)pvCocoaEvent;
+ NSEventType EvtType = [pEvent type];
+ NSWindow *pWin = ::darwinToNativeWindow((QWidget*)pvUser);
+ /* First check for the right event type and that we are processing events
+ from the window which was registered by the user. */
+ if ( EvtType == NSRightMouseDown
+ && pWin == [pEvent window])
+ {
+ /* Get the mouse position of the event (screen coordinates) */
+ NSPoint point = [NSEvent mouseLocation];
+ /* Get the frame rectangle of the window (screen coordinates) */
+ NSRect winFrame = [pWin frame];
+ /* Calculate the height of the title and the toolbar */
+ int i = NSHeight(winFrame) - NSHeight([[pWin contentView] frame]);
+ /* Based on that height create a rectangle of the unified toolbar + title */
+ winFrame.origin.y += winFrame.size.height - i;
+ winFrame.size.height = i;
+ /* Check if the mouse press event was on the unified toolbar or title */
+ if (NSMouseInRect(point, winFrame, NO))
+ /* Create a Qt context menu event, with flipped screen coordinates */
+ ::darwinCreateContextMenuEvent(pvUser, point.x, NSHeight([[pWin screen] frame]) - point.y);
+ }
+ return false;
+}
+
+/**
+ * Check for some default application key combinations a Mac user expect, like
+ * CMD+Q or CMD+H.
+ *
+ * @returns true if such a key combo was hit, false otherwise.
+ * @param pEvent The Cocoa event.
+ */
+bool darwinIsApplicationCommand(ConstNativeNSEventRef pEvent)
+{
+ NSEventType eEvtType = [pEvent type];
+ bool fGlobalHotkey = false;
+//
+// if ( (eEvtType == NSKeyDown || eEvtType == NSKeyUp)
+// && [[NSApp mainMenu] performKeyEquivalent:pEvent])
+// return true;
+// return false;
+// && [[[NSApp mainMenu] delegate] menuHasKeyEquivalent:[NSApp mainMenu] forEvent:pEvent target:b action:a])
+
+ switch (eEvtType)
+ {
+ case NSKeyDown:
+ case NSKeyUp:
+ {
+ NSUInteger fEvtMask = [pEvent modifierFlags];
+ unsigned short KeyCode = [pEvent keyCode];
+ if ( ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) /* L+CMD */
+ || ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK))) /* R+CMD */
+ {
+ if ( KeyCode == 0x0c /* CMD+Q (Quit) */
+ || KeyCode == 0x04) /* CMD+H (Hide) */
+ fGlobalHotkey = true;
+ }
+ else if ( ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) /* L+ALT+CMD */
+ || ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICERCMDKEYMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICERCMDKEYMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK))) /* R+ALT+CMD */
+ {
+ if (KeyCode == 0x04) /* ALT+CMD+H (Hide-Others) */
+ fGlobalHotkey = true;
+ }
+ break;
+ }
+ default: break;
+ }
+ return fGlobalHotkey;
+}
+
+void darwinRetranslateAppMenu()
+{
+ /* This is purely Qt internal. If the Trolls change something here, it will
+ not work anymore, but at least it will not be a burning man. */
+ if ([NSApp respondsToSelector:@selector(qt_qcocoamenuLoader)])
+ {
+ id loader = [NSApp performSelector:@selector(qt_qcocoamenuLoader)];
+ if ([loader respondsToSelector:@selector(qtTranslateApplicationMenu)])
+ [loader performSelector:@selector(qtTranslateApplicationMenu)];
+ }
+}
+
+/* Our resize proxy singleton. This class has two major tasks. First it is used
+ to proxy the windowWillResize selector of the Qt delegate. As this is class
+ global and therewith done for all Qt window instances, we have to track the
+ windows we are interested in. This is the second task. */
+@interface UIResizeProxy: NSObject
+{
+ NSMutableArray *m_pArray;
+ bool m_fInit;
+}
++(UIResizeProxy*)sharedResizeProxy;
+-(void)addWindow:(NSWindow*)pWindow;
+-(void)removeWindow:(NSWindow*)pWindow;
+-(BOOL)containsWindow:(NSWindow*)pWindow;
+@end
+
+static UIResizeProxy *gSharedResizeProxy = nil;
+
+@implementation UIResizeProxy
++(UIResizeProxy*)sharedResizeProxy
+{
+ if (gSharedResizeProxy == nil)
+ gSharedResizeProxy = [[super allocWithZone:NULL] init];
+ return gSharedResizeProxy;
+}
+-(id)init
+{
+ self = [super init];
+
+ m_fInit = false;
+
+ return self;
+}
+- (void)addWindow:(NSWindow*)pWindow
+{
+ if (!m_fInit)
+ {
+ /* Create an array which contains the registered windows. */
+ m_pArray = [[NSMutableArray alloc] init];
+ /* Swizzle the windowWillResize method. This means replacing the
+ original method with our own one and reroute the original one to
+ another name. */
+ Class oriClass = [[pWindow delegate] class];
+ Class myClass = [UIResizeProxy class];
+ SEL oriSel = @selector(windowWillResize:toSize:);
+ SEL qtSel = @selector(qtWindowWillResize:toSize:);
+ Method m1 = class_getInstanceMethod(oriClass, oriSel);
+ Method m2 = class_getInstanceMethod(myClass, oriSel);
+ Method m3 = class_getInstanceMethod(myClass, qtSel);
+ /* Overwrite the original implementation with our own one. old contains
+ the old implementation. */
+ IMP old = method_setImplementation(m1, method_getImplementation(m2));
+ /* Add a new method to our class with the old implementation. */
+ class_addMethod(oriClass, qtSel, old, method_getTypeEncoding(m3));
+ m_fInit = true;
+ }
+ [m_pArray addObject:pWindow];
+}
+- (void)removeWindow:(NSWindow*)pWindow
+{
+ [m_pArray removeObject:pWindow];
+}
+- (BOOL)containsWindow:(NSWindow*)pWindow
+{
+ return [m_pArray containsObject:pWindow];
+}
+- (NSSize)qtWindowWillResize:(NSWindow *)pWindow toSize:(NSSize)newFrameSize
+{
+ RT_NOREF(pWindow);
+ /* This is a fake implementation. It will be replaced by the original Qt
+ method. */
+ return newFrameSize;
+}
+- (NSSize)windowWillResize:(NSWindow *)pWindow toSize:(NSSize)newFrameSize
+{
+ /* Call the original implementation for newFrameSize. */
+ NSSize qtSize = [self qtWindowWillResize:pWindow toSize:newFrameSize];
+ /* Check if we are responsible for this window. */
+ if (![[UIResizeProxy sharedResizeProxy] containsWindow:pWindow])
+ return qtSize;
+ /* The static modifier method in NSEvent is >= 10.6. It allows us to check
+ the shift modifier state during the resize. If it is not available the
+ user have to press shift before he start to resize. */
+ if ([NSEvent respondsToSelector:@selector(modifierFlags)])
+ {
+ if (((int)(intptr_t)[NSEvent performSelector:@selector(modifierFlags)] & NSShiftKeyMask) == NSShiftKeyMask)
+ return qtSize;
+ }
+ else
+ {
+ /* Shift key pressed when this resize event was initiated? */
+ if (([pWindow resizeFlags] & NSShiftKeyMask) == NSShiftKeyMask)
+ return qtSize;
+ }
+ /* The default case is to calculate the aspect radio of the old size and
+ used it for the new size. */
+ NSSize s = [pWindow frame].size;
+ double a = (double)s.width / s.height;
+ NSSize newSize = NSMakeSize(newFrameSize.width, newFrameSize.width / a);
+ /* We have to make sure the new rectangle meets the minimum requirements. */
+ NSSize testSize = [self qtWindowWillResize:pWindow toSize:newSize];
+ if ( testSize.width > newSize.width
+ || testSize.height > newSize.height)
+ {
+ double w1 = testSize.width / newSize.width;
+ double h1 = testSize.height / newSize.height;
+ if ( w1 > 1
+ && w1 > h1)
+ {
+ newSize.width = testSize.width;
+ newSize.height = testSize.width * a;
+ }else if (h1 > 1)
+ {
+ newSize.width = testSize.height * a;
+ newSize.height = testSize.height;
+ }
+ }
+ return newSize;
+}
+@end
+
+void darwinInstallResizeDelegate(NativeNSWindowRef pWindow)
+{
+ [[UIResizeProxy sharedResizeProxy] addWindow:pWindow];
+}
+
+void darwinUninstallResizeDelegate(NativeNSWindowRef pWindow)
+{
+ [[UIResizeProxy sharedResizeProxy] removeWindow:pWindow];
+}
+
+void *darwinCocoaToCarbonEvent(void *pvCocoaEvent)
+{
+ NSEvent *pEvent = (NSEvent*)pvCocoaEvent;
+ return (void*)[pEvent eventRef];
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.cpp
new file mode 100644
index 00000000..e5dbfc13
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.cpp
@@ -0,0 +1,777 @@
+/* $Id: VBoxUtils-darwin.cpp $ */
+/** @file
+ * VBox Qt GUI - Utility Classes and Functions specific to Darwin.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QMainWindow>
+#include <QApplication>
+#include <QWidget>
+#include <QToolBar>
+#include <QPainter>
+#include <QPixmap>
+#include <QContextMenuEvent>
+
+/* GUI includes: */
+#include "VBoxUtils-darwin.h"
+#include "VBoxCocoaHelper.h"
+#include "UICocoaApplication.h"
+
+/* Other VBox includes: */
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+
+/* System includes: */
+#include <Carbon/Carbon.h>
+
+NativeNSViewRef darwinToNativeView(QWidget *pWidget)
+{
+ if (pWidget)
+ return reinterpret_cast<NativeNSViewRef>(pWidget->winId());
+ return nil;
+}
+
+NativeNSWindowRef darwinToNativeWindow(QWidget *pWidget)
+{
+ if (pWidget)
+ return ::darwinToNativeWindowImpl(::darwinToNativeView(pWidget));
+ return nil;
+}
+
+NativeNSWindowRef darwinToNativeWindow(NativeNSViewRef aView)
+{
+ return ::darwinToNativeWindowImpl(aView);
+}
+
+NativeNSViewRef darwinToNativeView(NativeNSWindowRef aWindow)
+{
+ return ::darwinToNativeViewImpl(aWindow);
+}
+
+NativeNSWindowRef darwinNativeButtonOfWindow(QWidget *pWidget, StandardWindowButtonType enmButtonType)
+{
+ return ::darwinNativeButtonOfWindowImpl(::darwinToNativeWindow(pWidget), enmButtonType);
+}
+
+void darwinSetShowsToolbarButton(QToolBar *aToolBar, bool fEnabled)
+{
+ QWidget *parent = aToolBar->parentWidget();
+ if (parent)
+ ::darwinSetShowsToolbarButtonImpl(::darwinToNativeWindow(parent), fEnabled);
+}
+
+void darwinLabelWindow(QWidget *pWidget, QPixmap *pPixmap)
+{
+ ::darwinLabelWindow(::darwinToNativeWindow(pWidget), ::darwinToNSImageRef(pPixmap), pPixmap->devicePixelRatio());
+}
+
+void darwinSetHidesAllTitleButtons(QWidget *pWidget)
+{
+ /* Currently only necessary in the Cocoa version */
+ ::darwinSetHidesAllTitleButtonsImpl(::darwinToNativeWindow(pWidget));
+}
+
+void darwinSetShowsWindowTransparent(QWidget *pWidget, bool fEnabled)
+{
+ ::darwinSetShowsWindowTransparentImpl(::darwinToNativeWindow(pWidget), fEnabled);
+}
+
+void darwinSetWindowHasShadow(QWidget *pWidget, bool fEnabled)
+{
+ ::darwinSetWindowHasShadow(::darwinToNativeWindow(pWidget), fEnabled);
+}
+
+void darwinWindowAnimateResize(QWidget *pWidget, const QRect &aTarget)
+{
+ ::darwinWindowAnimateResizeImpl(::darwinToNativeWindow(pWidget), aTarget.x(), aTarget.y(), aTarget.width(), aTarget.height());
+}
+
+void darwinWindowAnimateResizeNew(QWidget *pWidget, int h, bool fAnimate)
+{
+ ::darwinWindowAnimateResizeNewImpl(::darwinToNativeWindow(pWidget), h, fAnimate);
+}
+
+void darwinTest(QWidget *pWidget1, QWidget *pWidget2, int h)
+{
+ ::darwinTest(::darwinToNativeView(pWidget1), ::darwinToNativeView(pWidget2), h);
+}
+
+void darwinWindowInvalidateShape(QWidget *pWidget)
+{
+ /* Here a simple update is enough! */
+ pWidget->update();
+}
+
+void darwinWindowInvalidateShadow(QWidget *pWidget)
+{
+ ::darwinWindowInvalidateShadowImpl(::darwinToNativeWindow(pWidget));
+}
+
+void darwinSetShowsResizeIndicator(QWidget *pWidget, bool fEnabled)
+{
+ ::darwinSetShowsResizeIndicatorImpl(::darwinToNativeWindow(pWidget), fEnabled);
+}
+
+bool darwinIsWindowMaximized(QWidget *pWidget)
+{
+ /* Currently only necessary in the Cocoa version */
+ return ::darwinIsWindowMaximized(::darwinToNativeWindow(pWidget));
+}
+
+void darwinMinaturizeWindow(QWidget *pWidget)
+{
+ return ::darwinMinaturizeWindow(::darwinToNativeWindow(pWidget));
+}
+
+void darwinEnableFullscreenSupport(QWidget *pWidget)
+{
+ return ::darwinEnableFullscreenSupport(::darwinToNativeWindow(pWidget));
+}
+
+void darwinEnableTransienceSupport(QWidget *pWidget)
+{
+ return ::darwinEnableTransienceSupport(::darwinToNativeWindow(pWidget));
+}
+
+void darwinToggleFullscreenMode(QWidget *pWidget)
+{
+ return ::darwinToggleFullscreenMode(::darwinToNativeWindow(pWidget));
+}
+
+void darwinToggleWindowZoom(QWidget *pWidget)
+{
+ return ::darwinToggleWindowZoom(::darwinToNativeWindow(pWidget));
+}
+
+bool darwinIsInFullscreenMode(QWidget *pWidget)
+{
+ return ::darwinIsInFullscreenMode(::darwinToNativeWindow(pWidget));
+}
+
+bool darwinIsOnActiveSpace(QWidget *pWidget)
+{
+ return ::darwinIsOnActiveSpace(::darwinToNativeWindow(pWidget));
+}
+
+void darwinInstallResizeDelegate(QWidget *pWidget)
+{
+ ::darwinInstallResizeDelegate(::darwinToNativeWindow(pWidget));
+}
+
+void darwinUninstallResizeDelegate(QWidget *pWidget)
+{
+ ::darwinUninstallResizeDelegate(::darwinToNativeWindow(pWidget));
+}
+
+bool darwinOpenFile(const QString& strFile)
+{
+ return ::darwinOpenFile(darwinToNativeString(strFile.toUtf8().constData()));
+}
+
+QString darwinSystemLanguage(void)
+{
+ /* Get the locales supported by our bundle */
+ CFArrayRef supportedLocales = ::CFBundleCopyBundleLocalizations(::CFBundleGetMainBundle());
+ /* Check them against the languages currently selected by the user */
+ CFArrayRef preferredLocales = ::CFBundleCopyPreferredLocalizationsFromArray(supportedLocales);
+ /* Get the one which is on top */
+ CFStringRef localeId = (CFStringRef)::CFArrayGetValueAtIndex(preferredLocales, 0);
+ /* Convert them to a C-string */
+ char localeName[20];
+ ::CFStringGetCString(localeId, localeName, sizeof(localeName), kCFStringEncodingUTF8);
+ /* Some cleanup */
+ ::CFRelease(supportedLocales);
+ ::CFRelease(preferredLocales);
+ QString id(localeName);
+ /* Check for some misbehavior */
+ if (id.isEmpty() ||
+ id.toLower() == "english")
+ id = "en";
+ return id;
+}
+
+void darwinDisableIconsInMenus(void)
+{
+ /* No icons in the menu of a mac application. */
+ QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, true);
+}
+
+int darwinWindowToolBarHeight(QWidget *pWidget)
+{
+ NOREF(pWidget);
+ return 0;
+}
+
+int darwinWindowTitleHeight(QWidget *pWidget)
+{
+ return ::darwinWindowTitleHeight(::darwinToNativeWindow(pWidget));
+}
+
+bool darwinIsToolbarVisible(QToolBar *pToolBar)
+{
+ bool fResult = false;
+ QWidget *pParent = pToolBar->parentWidget();
+ if (pParent)
+ fResult = ::darwinIsToolbarVisible(::darwinToNativeWindow(pParent));
+ return fResult;
+}
+
+
+bool darwinSetFrontMostProcess()
+{
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ return ::SetFrontProcess(&psn) == 0;
+}
+
+uint64_t darwinGetCurrentProcessId()
+{
+ uint64_t processId = 0;
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ if (::GetCurrentProcess(&psn) == 0)
+ processId = RT_MAKE_U64(psn.lowLongOfPSN, psn.highLongOfPSN);
+ return processId;
+}
+
+/* Proxy icon creation */
+QPixmap darwinCreateDragPixmap(const QPixmap& aPixmap, const QString &aText)
+{
+ QFontMetrics fm(qApp->font());
+ QRect tbRect = fm.boundingRect(aText);
+ const int h = qMax(aPixmap.height(), fm.ascent() + 1);
+ const int m = 2;
+ QPixmap dragPixmap(aPixmap.width() + tbRect.width() + m, h);
+ dragPixmap.fill(Qt::transparent);
+ QPainter painter(&dragPixmap);
+ painter.drawPixmap(0, qAbs(h - aPixmap.height()) / 2.0, aPixmap);
+ painter.setPen(Qt::white);
+ painter.drawText(QRect(aPixmap.width() + m, 1, tbRect.width(), h - 1), Qt::AlignLeft | Qt::AlignVCenter, aText);
+ painter.setPen(Qt::black);
+ painter.drawText(QRect(aPixmap.width() + m, 0, tbRect.width(), h - 1), Qt::AlignLeft | Qt::AlignVCenter, aText);
+ painter.end();
+ return dragPixmap;
+}
+
+/**
+ * Callback for deleting the QImage object when CGImageCreate is done
+ * with it (which is probably not until the returned CFGImageRef is released).
+ *
+ * @param info Pointer to the QImage.
+ */
+static void darwinDataProviderReleaseQImage(void *info, const void *, size_t)
+{
+ QImage *qimg = (QImage *)info;
+ delete qimg;
+}
+
+/**
+ * Converts a QPixmap to a CGImage.
+ *
+ * @returns CGImageRef for the new image. (Remember to release it when finished with it.)
+ * @param aPixmap Pointer to the QPixmap instance to convert.
+ */
+CGImageRef darwinToCGImageRef(const QImage *pImage)
+{
+ QImage *imageCopy = new QImage(*pImage);
+ /** @todo this code assumes 32-bit image input, the lazy bird convert image to 32-bit method is anything but optimal... */
+ if (imageCopy->format() != QImage::Format_ARGB32)
+ *imageCopy = imageCopy->convertToFormat(QImage::Format_ARGB32);
+ Assert(!imageCopy->isNull());
+
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ CGDataProviderRef dp = CGDataProviderCreateWithData(imageCopy, pImage->bits(), pImage->sizeInBytes(),
+ darwinDataProviderReleaseQImage);
+
+ CGBitmapInfo bmpInfo = kCGImageAlphaFirst | kCGBitmapByteOrder32Host;
+ CGImageRef ir = CGImageCreate(imageCopy->width(), imageCopy->height(), 8, 32, imageCopy->bytesPerLine(), cs,
+ bmpInfo, dp, 0 /*decode */, 0 /* shouldInterpolate */,
+ kCGRenderingIntentDefault);
+ CGColorSpaceRelease(cs);
+ CGDataProviderRelease(dp);
+
+ Assert(ir);
+ return ir;
+}
+
+/**
+ * Converts a QPixmap to a CGImage.
+ *
+ * @returns CGImageRef for the new image. (Remember to release it when finished with it.)
+ * @param aPixmap Pointer to the QPixmap instance to convert.
+ */
+CGImageRef darwinToCGImageRef(const QPixmap *pPixmap)
+{
+ /* It seems Qt releases the memory to an returned CGImageRef when the
+ * associated QPixmap is destroyed. This shouldn't happen as long a
+ * CGImageRef has a retrain count. As a workaround we make a real copy. */
+ int bitmapBytesPerRow = pPixmap->width() * 4;
+ int bitmapByteCount = (bitmapBytesPerRow * pPixmap->height());
+ /* Create a memory block for the temporary image. It is initialized by zero
+ * which means black & zero alpha. */
+ void *pBitmapData = RTMemAllocZ(bitmapByteCount);
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ /* Create a context to paint on */
+ CGContextRef ctx = CGBitmapContextCreate(pBitmapData,
+ pPixmap->width(),
+ pPixmap->height(),
+ 8,
+ bitmapBytesPerRow,
+ cs,
+ kCGImageAlphaPremultipliedFirst);
+ /* Get the CGImageRef from Qt */
+ CGImageRef qtPixmap = pPixmap->toImage().toCGImage();
+ /* Draw the image from Qt & convert the context back to a new CGImageRef. */
+ CGContextDrawImage(ctx, CGRectMake(0, 0, pPixmap->width(), pPixmap->height()), qtPixmap);
+ CGImageRef newImage = CGBitmapContextCreateImage(ctx);
+ /* Now release all used resources */
+ CGImageRelease(qtPixmap);
+ CGContextRelease(ctx);
+ CGColorSpaceRelease(cs);
+ RTMemFree(pBitmapData);
+
+ /* Return the new CGImageRef */
+ return newImage;
+}
+
+/**
+ * Loads an image using Qt and converts it to a CGImage.
+ *
+ * @returns CGImageRef for the new image. (Remember to release it when finished with it.)
+ * @param aSource The source name.
+ */
+CGImageRef darwinToCGImageRef(const char *pczSource)
+{
+ QPixmap qpm(QString(":/") + pczSource);
+ Assert(!qpm.isNull());
+ return ::darwinToCGImageRef(&qpm);
+}
+
+void darwinRegisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow)
+{
+ UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(3) /* NSRightMouseDown */, ::darwinUnifiedToolbarEvents, pWindow);
+}
+
+void darwinUnregisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow)
+{
+ UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(3) /* NSRightMouseDown */, ::darwinUnifiedToolbarEvents, pWindow);
+}
+
+void darwinMouseGrab(QWidget *pWidget)
+{
+ CGAssociateMouseAndMouseCursorPosition(false);
+ UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(1) | /* NSLeftMouseDown */
+ RT_BIT_32(2) | /* NSLeftMouseUp */
+ RT_BIT_32(3) | /* NSRightMouseDown */
+ RT_BIT_32(4) | /* NSRightMouseUp */
+ RT_BIT_32(5) | /* NSMouseMoved */
+ RT_BIT_32(6) | /* NSLeftMouseDragged */
+ RT_BIT_32(7) | /* NSRightMouseDragged */
+ RT_BIT_32(25) | /* NSOtherMouseDown */
+ RT_BIT_32(26) | /* NSOtherMouseUp */
+ RT_BIT_32(27) | /* NSOtherMouseDragged */
+ RT_BIT_32(22), /* NSScrollWheel */
+ ::darwinMouseGrabEvents, pWidget);
+}
+
+void darwinMouseRelease(QWidget *pWidget)
+{
+ UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(1) | /* NSLeftMouseDown */
+ RT_BIT_32(2) | /* NSLeftMouseUp */
+ RT_BIT_32(3) | /* NSRightMouseDown */
+ RT_BIT_32(4) | /* NSRightMouseUp */
+ RT_BIT_32(5) | /* NSMouseMoved */
+ RT_BIT_32(6) | /* NSLeftMouseDragged */
+ RT_BIT_32(7) | /* NSRightMouseDragged */
+ RT_BIT_32(25) | /* NSOtherMouseDown */
+ RT_BIT_32(26) | /* NSOtherMouseUp */
+ RT_BIT_32(27) | /* NSOtherMouseDragged */
+ RT_BIT_32(22), /* NSScrollWheel */
+ ::darwinMouseGrabEvents, pWidget);
+ CGAssociateMouseAndMouseCursorPosition(true);
+}
+
+void darwinSendMouseGrabEvents(QWidget *pWidget, int type, int button, int buttons, int x, int y)
+{
+ QEvent::Type qtType = QEvent::None;
+ Qt::MouseButtons qtButtons = Qt::NoButton;
+ Qt::MouseButton qtButton = Qt::NoButton;
+ Qt::MouseButton qtExtraButton = Qt::NoButton;
+ Qt::Orientation qtOrientation = Qt::Horizontal;
+ int wheelDelta = 0;
+ /* Which button is used in the NSOtherMouse... cases? */
+ if (button == 0)
+ qtExtraButton = Qt::LeftButton;
+ else if (button == 1)
+ qtExtraButton = Qt::RightButton;
+ else if (button == 2)
+ qtExtraButton = Qt::MiddleButton;
+ else if (button == 3)
+ qtExtraButton = Qt::XButton1;
+ else if (button == 4)
+ qtExtraButton = Qt::XButton2;
+ /* Map the NSEvent to a QEvent and define the Qt::Buttons when necessary. */
+ switch(type)
+ {
+ case 1: /* NSLeftMouseDown */
+ {
+ qtType = QEvent::MouseButtonPress;
+ qtButton = Qt::LeftButton;
+ break;
+ }
+ case 2: /* NSLeftMouseUp */
+ {
+ qtType = QEvent::MouseButtonRelease;
+ qtButton = Qt::LeftButton;
+ break;
+ }
+ case 3: /* NSRightMouseDown */
+ {
+ qtType = QEvent::MouseButtonPress;
+ qtButton = Qt::RightButton;
+ break;
+ }
+ case 4: /* NSRightMouseUp */
+ {
+ qtType = QEvent::MouseButtonRelease;
+ qtButton = Qt::RightButton;
+ break;
+ }
+ case 5: /* NSMouseMoved */
+ {
+ qtType = QEvent::MouseMove;
+ break;
+ }
+ case 6: /* NSLeftMouseDragged */
+ {
+ qtType = QEvent::MouseMove;
+ qtButton = Qt::LeftButton;
+ break;
+ }
+ case 7: /* NSRightMouseDragged */
+ {
+ qtType = QEvent::MouseMove;
+ qtButton = Qt::RightButton;
+ break;
+ }
+ case 22: /* NSScrollWheel */
+ {
+ qtType = QEvent::Wheel;
+ if (y != 0)
+ {
+ wheelDelta = y;
+ qtOrientation = Qt::Vertical;
+ }
+ else if (x != 0)
+ {
+ wheelDelta = x;
+ qtOrientation = Qt::Horizontal;
+ }
+ x = y = 0;
+ break;
+ }
+ case 25: /* NSOtherMouseDown */
+ {
+ qtType = QEvent::MouseButtonPress;
+ qtButton = qtExtraButton;
+ break;
+ }
+ case 26: /* NSOtherMouseUp */
+ {
+ qtType = QEvent::MouseButtonRelease;
+ qtButton = qtExtraButton;
+ break;
+ }
+ case 27: /* NSOtherMouseDragged */
+ {
+ qtType = QEvent::MouseMove;
+ qtButton = qtExtraButton;
+ break;
+ }
+ default: return;
+ }
+ /* Create a Qt::MouseButtons Mask. */
+ if ((buttons & RT_BIT_32(0)) == RT_BIT_32(0))
+ qtButtons |= Qt::LeftButton;
+ if ((buttons & RT_BIT_32(1)) == RT_BIT_32(1))
+ qtButtons |= Qt::RightButton;
+ if ((buttons & RT_BIT_32(2)) == RT_BIT_32(2))
+ qtButtons |= Qt::MiddleButton;
+ if ((buttons & RT_BIT_32(3)) == RT_BIT_32(3))
+ qtButtons |= Qt::XButton1;
+ if ((buttons & RT_BIT_32(4)) == RT_BIT_32(4))
+ qtButtons |= Qt::XButton2;
+ /* Create a new mouse delta event and send it to the widget. */
+ UIGrabMouseEvent *pEvent = new UIGrabMouseEvent(qtType, qtButton, qtButtons, x, y, wheelDelta, qtOrientation);
+ qApp->sendEvent(pWidget, pEvent);
+}
+
+void darwinCreateContextMenuEvent(void *pvUser, int x, int y)
+{
+ QWidget *pWin = static_cast<QWidget*>(pvUser);
+ QPoint global(x, y);
+ QPoint local = pWin->mapFromGlobal(global);
+ qApp->postEvent(pWin, new QContextMenuEvent(QContextMenuEvent::Mouse, local, global));
+}
+
+QString darwinResolveAlias(const QString &strFile)
+{
+ OSErr err = noErr;
+ FSRef fileRef;
+ QString strTarget;
+ do
+ {
+ Boolean fDir;
+ if ((err = FSPathMakeRef((const UInt8*)strFile.toUtf8().constData(), &fileRef, &fDir)) != noErr)
+ break;
+ Boolean fAlias = FALSE;
+ if ((err = FSIsAliasFile(&fileRef, &fAlias, &fDir)) != noErr)
+ break;
+ if (fAlias == TRUE)
+ {
+ if ((err = FSResolveAliasFile(&fileRef, TRUE, &fAlias, &fDir)) != noErr)
+ break;
+ char pszPath[1024];
+ if ((err = FSRefMakePath(&fileRef, (UInt8*)pszPath, 1024)) != noErr)
+ break;
+ strTarget = QString::fromUtf8(pszPath);
+ }
+ else
+ strTarget = strFile;
+ }while(0);
+
+ return strTarget;
+}
+
+
+/********************************************************************************
+ *
+ * Old carbon stuff. Have to convert soon!
+ *
+ ********************************************************************************/
+
+/* Event debugging stuff. Borrowed from Knuts Qt patch. */
+#if defined (DEBUG)
+
+# define MY_CASE(a) case a: return #a
+const char * DarwinDebugEventName(UInt32 ekind)
+{
+ switch (ekind)
+ {
+# if !__LP64__
+ MY_CASE(kEventWindowUpdate );
+ MY_CASE(kEventWindowDrawContent );
+# endif
+ MY_CASE(kEventWindowActivated );
+ MY_CASE(kEventWindowDeactivated );
+ MY_CASE(kEventWindowHandleActivate );
+ MY_CASE(kEventWindowHandleDeactivate );
+ MY_CASE(kEventWindowGetClickActivation );
+ MY_CASE(kEventWindowGetClickModality );
+ MY_CASE(kEventWindowShowing );
+ MY_CASE(kEventWindowHiding );
+ MY_CASE(kEventWindowShown );
+ MY_CASE(kEventWindowHidden );
+ MY_CASE(kEventWindowCollapsing );
+ MY_CASE(kEventWindowCollapsed );
+ MY_CASE(kEventWindowExpanding );
+ MY_CASE(kEventWindowExpanded );
+ MY_CASE(kEventWindowZoomed );
+ MY_CASE(kEventWindowBoundsChanging );
+ MY_CASE(kEventWindowBoundsChanged );
+ MY_CASE(kEventWindowResizeStarted );
+ MY_CASE(kEventWindowResizeCompleted );
+ MY_CASE(kEventWindowDragStarted );
+ MY_CASE(kEventWindowDragCompleted );
+ MY_CASE(kEventWindowClosed );
+ MY_CASE(kEventWindowTransitionStarted );
+ MY_CASE(kEventWindowTransitionCompleted );
+# if !__LP64__
+ MY_CASE(kEventWindowClickDragRgn );
+ MY_CASE(kEventWindowClickResizeRgn );
+ MY_CASE(kEventWindowClickCollapseRgn );
+ MY_CASE(kEventWindowClickCloseRgn );
+ MY_CASE(kEventWindowClickZoomRgn );
+ MY_CASE(kEventWindowClickContentRgn );
+ MY_CASE(kEventWindowClickProxyIconRgn );
+ MY_CASE(kEventWindowClickToolbarButtonRgn );
+ MY_CASE(kEventWindowClickStructureRgn );
+# endif
+ MY_CASE(kEventWindowCursorChange );
+ MY_CASE(kEventWindowCollapse );
+ MY_CASE(kEventWindowCollapseAll );
+ MY_CASE(kEventWindowExpand );
+ MY_CASE(kEventWindowExpandAll );
+ MY_CASE(kEventWindowClose );
+ MY_CASE(kEventWindowCloseAll );
+ MY_CASE(kEventWindowZoom );
+ MY_CASE(kEventWindowZoomAll );
+ MY_CASE(kEventWindowContextualMenuSelect );
+ MY_CASE(kEventWindowPathSelect );
+ MY_CASE(kEventWindowGetIdealSize );
+ MY_CASE(kEventWindowGetMinimumSize );
+ MY_CASE(kEventWindowGetMaximumSize );
+ MY_CASE(kEventWindowConstrain );
+# if !__LP64__
+ MY_CASE(kEventWindowHandleContentClick );
+# endif
+ MY_CASE(kEventWindowGetDockTileMenu );
+ MY_CASE(kEventWindowProxyBeginDrag );
+ MY_CASE(kEventWindowProxyEndDrag );
+ MY_CASE(kEventWindowToolbarSwitchMode );
+ MY_CASE(kEventWindowFocusAcquired );
+ MY_CASE(kEventWindowFocusRelinquish );
+ MY_CASE(kEventWindowFocusContent );
+ MY_CASE(kEventWindowFocusToolbar );
+ MY_CASE(kEventWindowFocusDrawer );
+ MY_CASE(kEventWindowSheetOpening );
+ MY_CASE(kEventWindowSheetOpened );
+ MY_CASE(kEventWindowSheetClosing );
+ MY_CASE(kEventWindowSheetClosed );
+ MY_CASE(kEventWindowDrawerOpening );
+ MY_CASE(kEventWindowDrawerOpened );
+ MY_CASE(kEventWindowDrawerClosing );
+ MY_CASE(kEventWindowDrawerClosed );
+ MY_CASE(kEventWindowDrawFrame );
+ MY_CASE(kEventWindowDrawPart );
+ MY_CASE(kEventWindowGetRegion );
+ MY_CASE(kEventWindowHitTest );
+ MY_CASE(kEventWindowInit );
+ MY_CASE(kEventWindowDispose );
+ MY_CASE(kEventWindowDragHilite );
+ MY_CASE(kEventWindowModified );
+ MY_CASE(kEventWindowSetupProxyDragImage );
+ MY_CASE(kEventWindowStateChanged );
+ MY_CASE(kEventWindowMeasureTitle );
+ MY_CASE(kEventWindowDrawGrowBox );
+ MY_CASE(kEventWindowGetGrowImageRegion );
+ MY_CASE(kEventWindowPaint );
+ }
+ static char s_sz[64];
+ sprintf(s_sz, "kind=%u", (uint)ekind);
+ return s_sz;
+}
+# undef MY_CASE
+
+/* Convert a class into the 4 char code defined in
+ * 'Developer/Headers/CFMCarbon/CarbonEvents.h' to
+ * identify the event. */
+const char * darwinDebugClassName(UInt32 eclass)
+{
+ char *pclass = (char*)&eclass;
+ static char s_sz[11];
+ sprintf(s_sz, "class=%c%c%c%c", pclass[3],
+ pclass[2],
+ pclass[1],
+ pclass[0]);
+ return s_sz;
+}
+
+void darwinDebugPrintEvent(const char *psz, EventRef evtRef)
+{
+ if (!evtRef)
+ return;
+ UInt32 ekind = GetEventKind(evtRef), eclass = GetEventClass(evtRef);
+ if (eclass == kEventClassWindow)
+ {
+ switch (ekind)
+ {
+# if !__LP64__
+ case kEventWindowDrawContent:
+ case kEventWindowUpdate:
+# endif
+ case kEventWindowBoundsChanged:
+ break;
+ default:
+ {
+ WindowRef wid = NULL;
+ GetEventParameter(evtRef, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &wid);
+ QWidget *widget = QWidget::find((WId)wid);
+ printf("%d %s: (%s) %#x win=%p wid=%p (%s)\n", (int)time(NULL), psz, darwinDebugClassName(eclass), (uint)ekind, wid, widget, DarwinDebugEventName(ekind));
+ break;
+ }
+ }
+ }
+ else if (eclass == kEventClassCommand)
+ {
+ WindowRef wid = NULL;
+ GetEventParameter(evtRef, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &wid);
+ QWidget *widget = QWidget::find((WId)wid);
+ const char *name = "Unknown";
+ switch (ekind)
+ {
+ case kEventCommandProcess:
+ name = "kEventCommandProcess";
+ break;
+ case kEventCommandUpdateStatus:
+ name = "kEventCommandUpdateStatus";
+ break;
+ }
+ printf("%d %s: (%s) %#x win=%p wid=%p (%s)\n", (int)time(NULL), psz, darwinDebugClassName(eclass), (uint)ekind, wid, widget, name);
+ }
+ else if (eclass == kEventClassKeyboard)
+ {
+ printf("%d %s: %#x(%s) %#x (kEventClassKeyboard)", (int)time(NULL), psz, (uint)eclass, darwinDebugClassName(eclass), (uint)ekind);
+
+ UInt32 keyCode = 0;
+ ::GetEventParameter(evtRef, kEventParamKeyCode, typeUInt32, NULL,
+ sizeof(keyCode), NULL, &keyCode);
+ printf(" keyCode=%d (%#x) ", (int)keyCode, (unsigned)keyCode);
+
+ char macCharCodes[8] = {0,0,0,0, 0,0,0,0};
+ ::GetEventParameter(evtRef, kEventParamKeyCode, typeChar, NULL,
+ sizeof(macCharCodes), NULL, &macCharCodes[0]);
+ printf(" macCharCodes={");
+ for (unsigned i =0; i < 8 && macCharCodes[i]; i++)
+ printf( i == 0 ? "%02x" : ",%02x", macCharCodes[i]);
+ printf("}");
+
+ UInt32 modifierMask = 0;
+ ::GetEventParameter(evtRef, kEventParamKeyModifiers, typeUInt32, NULL,
+ sizeof(modifierMask), NULL, &modifierMask);
+ printf(" modifierMask=%08x", (unsigned)modifierMask);
+
+ UniChar keyUnicodes[8] = {0,0,0,0, 0,0,0,0};
+ ::GetEventParameter(evtRef, kEventParamKeyUnicodes, typeUnicodeText, NULL,
+ sizeof(keyUnicodes), NULL, &keyUnicodes[0]);
+ printf(" keyUnicodes={");
+ for (unsigned i =0; i < 8 && keyUnicodes[i]; i++)
+ printf( i == 0 ? "%02x" : ",%02x", keyUnicodes[i]);
+ printf("}");
+
+ UInt32 keyboardType = 0;
+ ::GetEventParameter(evtRef, kEventParamKeyboardType, typeUInt32, NULL,
+ sizeof(keyboardType), NULL, &keyboardType);
+ printf(" keyboardType=%08x", (unsigned)keyboardType);
+
+ EventHotKeyID evtHotKeyId = {0,0};
+ ::GetEventParameter(evtRef, typeEventHotKeyID, typeEventHotKeyID, NULL,
+ sizeof(evtHotKeyId), NULL, &evtHotKeyId);
+ printf(" evtHotKeyId={signature=%08x, .id=%08x}", (unsigned)evtHotKeyId.signature, (unsigned)evtHotKeyId.id);
+ printf("\n");
+ }
+ else
+ printf("%d %s: %#x(%s) %#x\n", (int)time(NULL), psz, (uint)eclass, darwinDebugClassName(eclass), (uint)ekind);
+}
+
+#endif /* DEBUG */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.h
new file mode 100644
index 00000000..f545cf11
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.h
@@ -0,0 +1,319 @@
+/* $Id: VBoxUtils-darwin.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling Darwin specific tasks.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_VBoxUtils_darwin_h
+#define FEQT_INCLUDED_SRC_platform_darwin_VBoxUtils_darwin_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QRect>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <VBox/VBoxCocoa.h>
+#include <ApplicationServices/ApplicationServices.h>
+#undef PVM // Stupid, stupid apple headers (sys/param.h)!!
+#include <iprt/cdefs.h>
+
+/* External includes: */
+#include <ApplicationServices/ApplicationServices.h>
+
+/* Forward declarations: */
+class QImage;
+class QMainWindow;
+class QMenu;
+class QPixmap;
+class QToolBar;
+class QWidget;
+
+/* Cocoa declarations: */
+ADD_COCOA_NATIVE_REF(NSButton);
+ADD_COCOA_NATIVE_REF(NSEvent);
+ADD_COCOA_NATIVE_REF(NSImage);
+ADD_COCOA_NATIVE_REF(NSString);
+ADD_COCOA_NATIVE_REF(NSView);
+ADD_COCOA_NATIVE_REF(NSWindow);
+
+
+/** Mac OS X: Standard window button types. */
+enum StandardWindowButtonType
+{
+ StandardWindowButtonType_Close, // Since OS X 10.2
+ StandardWindowButtonType_Miniaturize, // Since OS X 10.2
+ StandardWindowButtonType_Zoom, // Since OS X 10.2
+ StandardWindowButtonType_Toolbar, // Since OS X 10.2
+ StandardWindowButtonType_DocumentIcon, // Since OS X 10.2
+ StandardWindowButtonType_DocumentVersions, // Since OS X 10.7
+ StandardWindowButtonType_FullScreen // Since OS X 10.7
+};
+
+
+RT_C_DECLS_BEGIN
+
+/********************************************************************************
+ *
+ * Window/View management (OS System native)
+ *
+ ********************************************************************************/
+NativeNSWindowRef darwinToNativeWindowImpl(NativeNSViewRef pView);
+NativeNSViewRef darwinToNativeViewImpl(NativeNSWindowRef pWindow);
+NativeNSButtonRef darwinNativeButtonOfWindowImpl(NativeNSWindowRef pWindow, StandardWindowButtonType enmButtonType);
+SHARED_LIBRARY_STUFF NativeNSStringRef darwinToNativeString(const char* pcszString);
+QString darwinFromNativeString(NativeNSStringRef pString);
+
+/********************************************************************************
+ *
+ * Simple setter methods (OS System native)
+ *
+ ********************************************************************************/
+void darwinSetShowsToolbarButtonImpl(NativeNSWindowRef pWindow, bool fEnabled);
+void darwinSetShowsResizeIndicatorImpl(NativeNSWindowRef pWindow, bool fEnabled);
+void darwinSetHidesAllTitleButtonsImpl(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinLabelWindow(NativeNSWindowRef pWindow, NativeNSImageRef pImage, double dDpr);
+void darwinSetShowsWindowTransparentImpl(NativeNSWindowRef pWindow, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinSetWindowHasShadow(NativeNSWindowRef pWindow, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinSetMouseCoalescingEnabled(bool fEnabled);
+
+void darwintest(NativeNSWindowRef pWindow);
+/********************************************************************************
+ *
+ * Simple helper methods (OS System native)
+ *
+ ********************************************************************************/
+void darwinWindowAnimateResizeImpl(NativeNSWindowRef pWindow, int x, int y, int width, int height);
+void darwinWindowAnimateResizeNewImpl(NativeNSWindowRef pWindow, int height, bool fAnimate);
+void darwinTest(NativeNSViewRef pView, NativeNSViewRef pView1, int h);
+void darwinWindowInvalidateShapeImpl(NativeNSWindowRef pWindow);
+void darwinWindowInvalidateShadowImpl(NativeNSWindowRef pWindow);
+int darwinWindowToolBarHeight(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF int darwinWindowTitleHeight(NativeNSWindowRef pWindow);
+bool darwinIsToolbarVisible(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF bool darwinIsWindowMaximized(NativeNSWindowRef pWindow);
+void darwinMinaturizeWindow(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinEnableFullscreenSupport(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinEnableTransienceSupport(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinToggleFullscreenMode(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinToggleWindowZoom(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF bool darwinIsInFullscreenMode(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF bool darwinIsOnActiveSpace(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF bool darwinScreensHaveSeparateSpaces();
+SHARED_LIBRARY_STUFF bool darwinIsScrollerStyleOverlay();
+
+bool darwinOpenFile(NativeNSStringRef pstrFile);
+
+SHARED_LIBRARY_STUFF float darwinSmallFontSize();
+SHARED_LIBRARY_STUFF bool darwinSetFrontMostProcess();
+SHARED_LIBRARY_STUFF uint64_t darwinGetCurrentProcessId();
+
+void darwinInstallResizeDelegate(NativeNSWindowRef pWindow);
+void darwinUninstallResizeDelegate(NativeNSWindowRef pWindow);
+
+bool darwinUnifiedToolbarEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser);
+bool darwinMouseGrabEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser);
+void darwinCreateContextMenuEvent(void *pvWin, int x, int y);
+
+SHARED_LIBRARY_STUFF bool darwinIsApplicationCommand(ConstNativeNSEventRef pEvent);
+
+void darwinRetranslateAppMenu();
+
+void darwinSendMouseGrabEvents(QWidget *pWidget, int type, int button, int buttons, int x, int y);
+
+SHARED_LIBRARY_STUFF QString darwinResolveAlias(const QString &strFile);
+
+RT_C_DECLS_END
+
+DECLINLINE(CGRect) darwinToCGRect(const QRect& aRect) { return CGRectMake(aRect.x(), aRect.y(), aRect.width(), aRect.height()); }
+DECLINLINE(CGRect) darwinFlipCGRect(CGRect aRect, double aTargetHeight) { aRect.origin.y = aTargetHeight - aRect.origin.y - aRect.size.height; return aRect; }
+DECLINLINE(CGRect) darwinFlipCGRect(CGRect aRect, const CGRect &aTarget) { return darwinFlipCGRect(aRect, aTarget.size.height); }
+DECLINLINE(CGRect) darwinCenterRectTo(CGRect aRect, const CGRect& aToRect)
+{
+ aRect.origin.x = aToRect.origin.x + (aToRect.size.width - aRect.size.width) / 2.0;
+ aRect.origin.y = aToRect.origin.y + (aToRect.size.height - aRect.size.height) / 2.0;
+ return aRect;
+}
+
+/********************************************************************************
+ *
+ * Window/View management (Qt Wrapper)
+ *
+ ********************************************************************************/
+
+/**
+ * Returns a reference to the native View of the QWidget.
+ *
+ * @returns either HIViewRef or NSView* of the QWidget.
+ * @param pWidget Pointer to the QWidget
+ */
+NativeNSViewRef darwinToNativeView(QWidget *pWidget);
+
+/**
+ * Returns a reference to the native Window of the QWidget.
+ *
+ * @returns either WindowRef or NSWindow* of the QWidget.
+ * @param pWidget Pointer to the QWidget
+ */
+NativeNSWindowRef darwinToNativeWindow(QWidget *pWidget);
+
+/* This is necessary because of the C calling convention. Its a simple wrapper
+ for darwinToNativeWindowImpl to allow operator overloading which isn't
+ allowed in C. */
+/**
+ * Returns a reference to the native Window of the View..
+ *
+ * @returns either WindowRef or NSWindow* of the View.
+ * @param pWidget Pointer to the native View
+ */
+NativeNSWindowRef darwinToNativeWindow(NativeNSViewRef pView);
+
+/**
+ * Returns a reference to the native View of the Window.
+ *
+ * @returns either HIViewRef or NSView* of the Window.
+ * @param pWidget Pointer to the native Window
+ */
+NativeNSViewRef darwinToNativeView(NativeNSWindowRef pWindow);
+
+/**
+ * Returns a reference to the native button of QWidget.
+ *
+ * @returns corresponding NSButton* of the QWidget.
+ * @param pWidget Brings the pointer to the QWidget.
+ * @param enmButtonType Brings the type of the native button required.
+ */
+NativeNSButtonRef darwinNativeButtonOfWindow(QWidget *pWidget, StandardWindowButtonType enmButtonType);
+
+/********************************************************************************
+ *
+ * Graphics stuff (Qt Wrapper)
+ *
+ ********************************************************************************/
+/**
+ * Returns a reference to the CGContext of the QWidget.
+ *
+ * @returns CGContextRef of the QWidget.
+ * @param pWidget Pointer to the QWidget
+ */
+SHARED_LIBRARY_STUFF CGImageRef darwinToCGImageRef(const QImage *pImage);
+SHARED_LIBRARY_STUFF CGImageRef darwinToCGImageRef(const QPixmap *pPixmap);
+SHARED_LIBRARY_STUFF CGImageRef darwinToCGImageRef(const char *pczSource);
+
+SHARED_LIBRARY_STUFF NativeNSImageRef darwinToNSImageRef(const CGImageRef pImage);
+SHARED_LIBRARY_STUFF NativeNSImageRef darwinToNSImageRef(const QImage *pImage);
+SHARED_LIBRARY_STUFF NativeNSImageRef darwinToNSImageRef(const QPixmap *pPixmap);
+SHARED_LIBRARY_STUFF NativeNSImageRef darwinToNSImageRef(const char *pczSource);
+
+#include <QEvent>
+class UIGrabMouseEvent: public QEvent
+{
+public:
+ enum { GrabMouseEvent = QEvent::User + 200 };
+
+ UIGrabMouseEvent(QEvent::Type type, Qt::MouseButton button, Qt::MouseButtons buttons, int x, int y, int wheelDelta, Qt::Orientation o)
+ : QEvent((QEvent::Type)GrabMouseEvent)
+ , m_type(type)
+ , m_button(button)
+ , m_buttons(buttons)
+ , m_x(x)
+ , m_y(y)
+ , m_wheelDelta(wheelDelta)
+ , m_orientation(o)
+ {}
+ QEvent::Type mouseEventType() const { return m_type; }
+ Qt::MouseButton button() const { return m_button; }
+ Qt::MouseButtons buttons() const { return m_buttons; }
+ int xDelta() const { return m_x; }
+ int yDelta() const { return m_y; }
+ int wheelDelta() const { return m_wheelDelta; }
+ Qt::Orientation orientation() const { return m_orientation; }
+
+private:
+ /* Private members */
+ QEvent::Type m_type;
+ Qt::MouseButton m_button;
+ Qt::MouseButtons m_buttons;
+ int m_x;
+ int m_y;
+ int m_wheelDelta;
+ Qt::Orientation m_orientation;
+};
+
+/********************************************************************************
+ *
+ * Simple setter methods (Qt Wrapper)
+ *
+ ********************************************************************************/
+void darwinSetShowsToolbarButton(QToolBar *aToolBar, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinLabelWindow(QWidget *pWidget, QPixmap *pPixmap);
+void darwinSetShowsResizeIndicator(QWidget *pWidget, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinSetHidesAllTitleButtons(QWidget *pWidget);
+void darwinSetShowsWindowTransparent(QWidget *pWidget, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinSetWindowHasShadow(QWidget *pWidget, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinDisableIconsInMenus(void);
+
+void darwinTest(QWidget *pWidget1, QWidget *pWidget2, int h);
+
+/********************************************************************************
+ *
+ * Simple helper methods (Qt Wrapper)
+ *
+ ********************************************************************************/
+SHARED_LIBRARY_STUFF void darwinWindowAnimateResize(QWidget *pWidget, const QRect &aTarget);
+void darwinWindowAnimateResizeNew(QWidget *pWidget, int h, bool fAnimate);
+void darwinWindowInvalidateShape(QWidget *pWidget);
+void darwinWindowInvalidateShadow(QWidget *pWidget);
+int darwinWindowToolBarHeight(QWidget *pWidget);
+SHARED_LIBRARY_STUFF int darwinWindowTitleHeight(QWidget *pWidget);
+bool darwinIsToolbarVisible(QToolBar *pToolBar);
+SHARED_LIBRARY_STUFF bool darwinIsWindowMaximized(QWidget *pWidget);
+void darwinMinaturizeWindow(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinEnableFullscreenSupport(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinEnableTransienceSupport(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinToggleFullscreenMode(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinToggleWindowZoom(QWidget *pWidget);
+SHARED_LIBRARY_STUFF bool darwinIsInFullscreenMode(QWidget *pWidget);
+SHARED_LIBRARY_STUFF bool darwinIsOnActiveSpace(QWidget *pWidget);
+bool darwinOpenFile(const QString &strFile);
+
+QString darwinSystemLanguage(void);
+QPixmap darwinCreateDragPixmap(const QPixmap& aPixmap, const QString &aText);
+
+void darwinInstallResizeDelegate(QWidget *pWidget);
+void darwinUninstallResizeDelegate(QWidget *pWidget);
+
+SHARED_LIBRARY_STUFF void darwinRegisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow);
+SHARED_LIBRARY_STUFF void darwinUnregisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow);
+
+SHARED_LIBRARY_STUFF void darwinMouseGrab(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinMouseRelease(QWidget *pWidget);
+
+SHARED_LIBRARY_STUFF void *darwinCocoaToCarbonEvent(void *pvCocoaEvent);
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_VBoxUtils_darwin_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-Info.plist b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-Info.plist
new file mode 100644
index 00000000..db617644
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundlePackageType</key> <string>APPL</string>
+ <key>CFBundleSignature</key> <string>VBVM</string>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.app.VirtualBoxVM</string>
+ <key>CFBundleName</key> <string>VirtualBox VM</string>
+ <key>CFBundleExecutable</key> <string>VirtualBoxVM</string>
+ <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string>
+ <key>CFBundleIconFile</key> <string>virtualbox</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>LSCanProvideIMVideoDataSource</key> <true/>
+ <key>NSHighResolutionCapable</key> <true/>
+ <key>NSSupportsAutomaticGraphicsSwitching</key><true/>
+ <key>NSCameraUsageDescription</key> <string>VirtualBox needs camera access for emulated webcam passthrough</string>
+ <key>NSMicrophoneUsageDescription</key> <string>VirtualBox needs microphone access for guest audio input</string>
+</dict>
+</plist>
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-PkgInfo b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-PkgInfo
new file mode 100644
index 00000000..be1f395e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-PkgInfo
@@ -0,0 +1 @@
+APPLVBVM \ No newline at end of file
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/tstDarwinKeyboard.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/tstDarwinKeyboard.cpp
new file mode 100644
index 00000000..0c6955ae
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/tstDarwinKeyboard.cpp
@@ -0,0 +1,108 @@
+/* $Id: tstDarwinKeyboard.cpp $ */
+/** @file
+ * VBox Qt GUI Testcase - Common GUI Library - Darwin Keyboard routines.
+ *
+ * @todo Move this up somewhere so that the two SDL GUIs can use parts of this code too (-HID crap).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/assert.h>
+
+#include "DarwinKeyboard.h"
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ AssertReleaseRCReturn(rc, 1);
+
+ /*
+ * Warmup tests.
+ */
+ RTPrintf("tstDarwinKeyboard: Warmup...\n");
+
+ RTTimeNanoTS();
+ DarwinGrabKeyboard(true);
+ DarwinReleaseKeyboard();
+
+ RTTimeNanoTS();
+ DarwinGrabKeyboard(true);
+ DarwinReleaseKeyboard();
+
+/* Test these too:
+unsigned DarwinKeycodeToSet1Scancode(unsigned uKeyCode);
+UInt32 DarwinAdjustModifierMask(UInt32 fModifiers);
+unsigned DarwinModifierMaskToSet1Scancode(UInt32 fModifiers);
+unsigned DarwinModifierMaskToDarwinKeycode(UInt32 fModifiers);
+UInt32 DarwinKeyCodeToDarwinModifierMask(unsigned uKeyCode);
+unsigned DarwinEventToSet1Scancode(EventRef Event, UInt32 *pfCurKeyModifiers);
+void DarwinDisableGlobalHotKeys(bool fDisable);
+*/
+
+ /*
+ * Grab and release the keyboard a lot of times and time it.
+ * We're looking both at performance and for memory and reference leaks here.
+ */
+ RTPrintf("tstDarwinKeyboard: Profiling Grab and Release");
+ RTStrmFlush(g_pStdOut);
+ const uint64_t u64Start = RTTimeNanoTS();
+ uint64_t u64Grab = 0;
+ uint64_t u64Release = 0;
+ unsigned i;
+ for (i = 0; i < 20; i++)
+ {
+ uint64_t u64 = RTTimeNanoTS();
+ DarwinGrabKeyboard(argc != 1);
+ u64Grab += RTTimeNanoTS() - u64;
+
+ u64 = RTTimeNanoTS();
+ DarwinReleaseKeyboard();
+ u64Release += RTTimeNanoTS() - u64;
+
+ if ((i % 10) == 0)
+ {
+ RTPrintf(".");
+ RTStrmFlush(g_pStdOut);
+ }
+ }
+ const uint64_t u64Elapsed = RTTimeNanoTS() - u64Start;
+ RTPrintf("\n"
+ "tstDarwinKeyboard: %u times in %RU64 ms - %RU64 ms per call\n",
+ i, u64Elapsed / 1000000, (u64Elapsed / i) / 1000000);
+ RTPrintf("tstDarwinKeyboard: DarwinGrabKeyboard: %RU64 ms total - %RU64 ms per call\n",
+ u64Grab / 1000000, (u64Grab / i) / 1000000);
+ RTPrintf("tstDarwinKeyboard: DarwinReleaseKeyboard: %RU64 ms total - %RU64 ms per call\n",
+ u64Release / 1000000, (u64Release / i) / 1000000);
+
+ return 0;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-Info.plist b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-Info.plist
new file mode 100644
index 00000000..303e0d48
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundlePackageType</key> <string>APPL</string>
+ <key>CFBundleSignature</key> <string>VMST</string>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.app.vmstarter</string>
+ <key>CFBundleName</key> <string>vmstarter</string>
+ <key>CFBundleExecutable</key> <string>vmstarter</string>
+ <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ vmstarter @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string>
+ <key>CFBundleIconFile</key> <string>virtualbox</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>LSUIElement</key> <true/>
+ <key>NSHighResolutionCapable</key> <true/>
+ <key>NSSupportsAutomaticGraphicsSwitching</key><true/>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeName</key> <string>VirtualBox Machine Definition</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vbox</string></array>
+ <key>CFBundleTypeRole</key> <string>Editor</string>
+ <key>LSHandlerRank</key> <string>Owner</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vbox</string>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-PkgInfo b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-PkgInfo
new file mode 100644
index 00000000..c1388087
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-PkgInfo
@@ -0,0 +1 @@
+APPLVBST
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter.mm
new file mode 100644
index 00000000..1aac4bd6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter.mm
@@ -0,0 +1,140 @@
+/* $Id: vmstarter.mm $ */
+/** @file
+ * VBox Qt GUI - Helper application for starting vbox the right way when the user double clicks on a file type association.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <iprt/cdefs.h>
+
+@interface AppDelegate: NSObject
+{
+NSString *m_strVBoxPath;
+}
+@end
+
+@implementation AppDelegate
+-(id) init
+{
+ self = [super init];
+ if (self)
+ {
+ /* Get the path of VBox by looking where our bundle is located. */
+ m_strVBoxPath = [[[[NSBundle mainBundle] bundlePath]
+ stringByAppendingPathComponent:@"/../../../../VirtualBox.app"]
+ stringByStandardizingPath];
+ /* We kill ourself after 1 seconds */
+ [NSTimer scheduledTimerWithTimeInterval:1.0
+ target:NSApp
+ selector:@selector(terminate:)
+ userInfo:nil
+ repeats:NO];
+ }
+
+ return self;
+}
+
+- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
+{
+ RT_NOREF(sender);
+
+ BOOL fResult = FALSE;
+ NSWorkspace *pWS = [NSWorkspace sharedWorkspace];
+ /* We need to check if vbox is running already. If so we sent an open
+ event. If not we start a new process with the file as parameter. */
+ NSArray *pApps = [pWS runningApplications];
+ bool fVBoxRuns = false;
+ for (NSRunningApplication *pApp in pApps)
+ {
+ if ([pApp.bundleIdentifier isEqualToString:@"org.virtualbox.app.VirtualBox"])
+ {
+ fVBoxRuns = true;
+ break;
+ }
+ }
+ if (fVBoxRuns)
+ {
+ /* Send the open event.
+ * Todo: check for an method which take a list of files. */
+ for (NSString *filename in filenames)
+ fResult = [pWS openFile:filename withApplication:m_strVBoxPath andDeactivate:TRUE];
+ }
+ else
+ {
+ /* Fire up a new instance of VBox. We prefer LSOpenApplication over
+ NSTask, cause it makes sure that VBox will become the front most
+ process after starting up. */
+/** @todo should replace all this with -[NSWorkspace
+ * launchApplicationAtURL:options:configuration:error:] because LSOpenApplication is deprecated in
+ * 10.10 while, FSPathMakeRef is deprecated since 10.8. */
+ /* The state horror show starts right here: */
+ OSStatus err = noErr;
+ Boolean fDir;
+ void *asyncLaunchRefCon = NULL;
+ FSRef fileRef;
+ CFStringRef file = NULL;
+ CFArrayRef args = NULL;
+ void **list = (void**)malloc(sizeof(void*) * [filenames count]);
+ for (size_t i = 0; i < [filenames count]; ++i)
+ list[i] = [filenames objectAtIndex:i];
+ do
+ {
+ NSString *strVBoxExe = [m_strVBoxPath stringByAppendingPathComponent:@"Contents/MacOS/VirtualBox"];
+ err = FSPathMakeRef((const UInt8*)[strVBoxExe UTF8String], &fileRef, &fDir);
+ if (err != noErr)
+ break;
+ args = CFArrayCreate(NULL, (const void **)list, [filenames count], &kCFTypeArrayCallBacks);
+ if (args == NULL)
+ break;
+ LSApplicationParameters par = { 0, 0, &fileRef, asyncLaunchRefCon, 0, args, 0 };
+ err = LSOpenApplication(&par, NULL);
+ if (err != noErr)
+ break;
+ fResult = TRUE;
+ }while(0);
+ if (list) /* Why bother checking, because you've crashed already if it's NULL! */
+ free(list);
+ if (file)
+ CFRelease(file);
+ if (args)
+ CFRelease(args);
+ }
+}
+@end
+
+int main()
+{
+ /* Global auto release pool. */
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ /* Create our own delegate for the application. */
+ AppDelegate *pAppDelegate = [[AppDelegate alloc] init];
+ [[NSApplication sharedApplication] setDelegate: (id<NSApplicationDelegate>)pAppDelegate]; /** @todo check out ugly cast */
+ pAppDelegate = nil;
+ /* Start the event loop. */
+ [NSApp run];
+ /* Cleanup */
+ [pool release];
+ return 0;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.asm b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.asm
new file mode 100644
index 00000000..573c797a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.asm
@@ -0,0 +1,71 @@
+; $Id: VBoxHlp.asm $
+;; @file
+; VBox Qt GUI - Implementation of OS/2-specific helpers that require to reside in a DLL.
+;
+; This stub is used to avoid linking the helper DLL to the C runtime.
+;
+
+;
+; Copyright (C) 2008-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; SPDX-License-Identifier: GPL-3.0-only
+;
+
+;; @todo BEGINCODE gives us this:
+;
+; 02-03-2008 22:19:37 SYS3175 PID 4383 TID 0001 Slot 0076
+; D:\CODING\INNOTEK\VBOX\OUT\OS2.X86\RELEASE\BIN\VIRTUALBOX.EXE
+; c0000005
+; 17d40000
+; P1=00000008 P2=0000bea4 P3=XXXXXXXX P4=XXXXXXXX
+; EAX=00001489 EBX=00000000 ECX=00000000 EDX=00000000
+; ESI=00000000 EDI=00001489
+; DS=be7f DSACC=00f3 DSLIM=0000003f
+; ES=0053 ESACC=f0f3 ESLIM=ffffffff
+; FS=150b FSACC=00f3 FSLIM=00000030
+; GS=0000 GSACC=**** GSLIM=********
+; CS:EIP=bea7:00000000 CSACC=00f2 CSLIM=00000002
+; SS:ESP=01d7:0000ffe8 SSACC=00f3 SSLIM=0000ffff
+; EBP=00000000 FLG=00012202
+;
+; VBOXHLP.DLL 0003:00000000
+;
+; Looks like the previous 'segment TEXT32 ...' definition in asmdefs.mac
+; is ignored and the segment is redefined as if it had no 'CLASS=CODE...'
+; attributes...
+
+;%include "iprt/asmdefs.mac"
+;
+;BEGINCODE
+
+segment TEXT32 public CLASS=CODE align=16 use32 flat
+
+extern _DLL_InitTerm
+
+; Low-level DLL entry point - Forward to the C code.
+..start:
+ jmp _DLL_InitTerm
+
+
+; emxomfld may generate references to this for weak symbols. It is usually
+; found in in libend.lib.
+ABSOLUTE 0
+global WEAK$ZERO
+WEAK$ZERO:
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.cpp b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.cpp
new file mode 100644
index 00000000..b5dd9eda
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.cpp
@@ -0,0 +1,198 @@
+/* $Id: VBoxHlp.cpp $ */
+/** @file
+ * VBox Qt GUI - Implementation of OS/2-specific helpers that require to reside in a DLL
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define OS2EMX_PLAIN_CHAR
+
+#define INCL_BASE
+#define INCL_PM
+#define INCL_DOSINFOSEG
+#define INCL_DOSDEVIOCTL
+#include <os2.h>
+
+#include "VBoxHlp.h"
+
+/**
+ * Undocumented PM hook that is called before the pressed key is checked
+ * against the global accelerator table.
+ *
+ * Taken from the xWorkplace source code where it appears to come from the
+ * ProgramCommander/2 source code. Thanks to Ulrich Moeller and Roman Stangl.
+ */
+#define HK_PREACCEL 17
+
+/* NOTE: all global data is per-process (DATA32 is multiple, nonshared). */
+
+/* Module handle of this DLL */
+static HMODULE gThisModule = NULLHANDLE;
+
+static PGINFOSEG gGIS = NULL;
+static PLINFOSEG gLIS = NULL;
+
+/* Parameters for the keyboard hook (VBoxHlpInstallKbdHook()) */
+HAB gKbdHookHab = NULLHANDLE;
+HWND gKbdHookHwnd = NULLHANDLE;
+ULONG gKbdHookMsg = 0;
+
+/**
+ * Message Input hook used to monitor the system message queue.
+ *
+ * @param aHab Anchor handle.
+ * @param aHwnd Pointer to the QMSG structure.
+ * @param aFS Flags from WinPeekMsg(), either PM_NOREMOVE or
+ * PM_REMOVE.
+ *
+ * @return @c TRUE to steal the given message or @c FALSE to pass it to the
+ * rest of the hook chain.
+ */
+static
+BOOL EXPENTRY vboxInputHook (HAB aHab, PQMSG aMsg, ULONG aFS)
+{
+ if (aMsg->msg == WM_CHAR)
+ {
+ /* For foreign processes that didn't call VBoxHlpInstallKbdHook(),
+ * gKbdHookHwnd remains NULL. If it's the case while in this input
+ * hook, it means that the given foreign process is in foreground
+ * now. Since forwarding should work only for processes that
+ * called VBoxHlpInstallKbdHook(), we ignore the message. */
+ if (gKbdHookHwnd != NULLHANDLE)
+ {
+ MRESULT reply =
+ WinSendMsg (gKbdHookHwnd, gKbdHookMsg, aMsg->mp1, aMsg->mp2);
+ return (BOOL) reply;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * Installs a hook that will intercept all keyboard input (WM_CHAR) messages
+ * and forward them to the given window handle using the given message
+ * identifier. Messages are intercepted only when the given top-level window
+ * is the active desktop window (i.e. a window receiving keyboard input).
+ *
+ * When the WM_CHAR message is intercepted, it is forwarded as is (including
+ * all parameters) except that the message ID is changed to the given message
+ * ID. The result of the WinSendMsg() call is then converted to BOOL and if
+ * it results to TRUE then the message is considered to be processed,
+ * otherwise it is passed back to the system for normal processing.
+ *
+ * If the hook is already installed for the same or another window, this
+ * method will return @c false.
+ *
+ * @note This function is not thread-safe and must be called only on the main
+ * thread once per process.
+ *
+ * @param aHab Window anchor block.
+ * @param aHwnd Top-level window handle to forward WM_CHAR messages to.
+ * @param aMsg Message ID to use when forwarding.
+ *
+ * @return @c true on success and @c false otherwise. */
+VBOXHLPDECL(bool) VBoxHlpInstallKbdHook (HAB aHab, HWND aHwnd,
+ unsigned long aMsg)
+{
+ if (gKbdHookHwnd != NULLHANDLE ||
+ aHwnd == NULLHANDLE)
+ return false;
+
+ BOOL ok = WinSetHook (aHab, NULLHANDLE, HK_PREACCEL,
+ (PFN) vboxInputHook, gThisModule);
+
+ if (ok)
+ {
+ gKbdHookHab = aHab;
+ gKbdHookHwnd = aHwnd;
+ gKbdHookMsg = aMsg;
+ }
+
+ return (bool) ok;
+}
+
+/**
+ * Uninstalls the keyboard hook installed by VBoxHlpInstallKbdHook().
+ * All arguments must match arguments passed to VBoxHlpInstallKbdHook(),
+ * otherwise this method will do nothing and return @c false.
+ *
+ * @return @c true on success and @c false otherwise.
+ */
+VBOXHLPDECL(bool) VBoxHlpUninstallKbdHook (HAB aHab, HWND aHwnd,
+ unsigned long aMsg)
+{
+ if (gKbdHookHab != aHab ||
+ gKbdHookHwnd != aHwnd ||
+ gKbdHookMsg != aMsg)
+ return false;
+
+ BOOL ok = WinReleaseHook (aHab, NULLHANDLE, HK_PREACCEL,
+ (PFN) vboxInputHook, gThisModule);
+
+ if (ok)
+ {
+ gKbdHookHab = NULLHANDLE;
+ gKbdHookHwnd = NULLHANDLE;
+ gKbdHookMsg = 0;
+ }
+
+ return (bool) ok;
+}
+
+/**
+ * DLL entry point.
+ *
+ * @param aHandle DLL module handle.
+ * @param aFlag 0 on initialization or 1 on termination.
+ *
+ * @return Non-zero for success or 0 for failure.
+ */
+ULONG _System _DLL_InitTerm (HMODULE aHandle, ULONG aFlag)
+{
+ bool ok = true;
+
+ if (aFlag == 0)
+ {
+ /* DLL initialization */
+
+ gThisModule = aHandle;
+
+ gGIS = GETGINFOSEG();
+ gLIS = GETLINFOSEG();
+ }
+ else
+ {
+ /* DLL termination */
+
+ /* Make sure we release the hook if the user forgets to do so. */
+ if (gKbdHookHwnd != NULLHANDLE)
+ WinReleaseHook (gKbdHookHab, NULLHANDLE, HK_PREACCEL,
+ (PFN) vboxInputHook, gThisModule);
+
+ gThisModule = NULLHANDLE;
+ }
+
+ return (unsigned long) ok;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.h b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.h
new file mode 100644
index 00000000..0df97704
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.h
@@ -0,0 +1,49 @@
+/* $Id: VBoxHlp.h $ */
+/** @file
+ * VBox Qt GUI - Declaration of OS/2-specific helpers that require to reside in a DLL.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_os2_VBoxHlp_h
+#define FEQT_INCLUDED_SRC_platform_os2_VBoxHlp_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+
+#ifdef IN_VBOXHLP
+# define VBOXHLPDECL(type) DECLEXPORT(type) RTCALL
+#else
+# define VBOXHLPDECL(type) DECLIMPORT(type) RTCALL
+#endif
+
+VBOXHLPDECL(bool) VBoxHlpInstallKbdHook (HAB aHab, HWND aHwnd,
+ unsigned long aMsg);
+
+VBOXHLPDECL(bool) VBoxHlpUninstallKbdHook (HAB aHab, HWND aHwnd,
+ unsigned long aMsg);
+
+#endif /* !FEQT_INCLUDED_SRC_platform_os2_VBoxHlp_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/platform/win/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/UIDesktopServices_win.cpp b/src/VBox/Frontends/VirtualBox/src/platform/win/UIDesktopServices_win.cpp
new file mode 100644
index 00000000..df4deef6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/UIDesktopServices_win.cpp
@@ -0,0 +1,88 @@
+/* $Id: UIDesktopServices_win.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to Windows..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UIDesktopServices.h"
+
+/* Qt includes */
+#include <QDir>
+#include <QCoreApplication>
+#include <QUuid>
+
+/* System includes */
+#include <iprt/win/shlobj.h>
+
+
+bool UIDesktopServices::createMachineShortcut(const QString & /* strSrcFile */, const QString &strDstPath, const QString &strName, const QUuid &uUuid)
+{
+ IShellLink *pShl = NULL;
+ IPersistFile *pPPF = NULL;
+ const QString strVBox = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/" + VBOX_GUI_VMRUNNER_IMAGE);
+ QFileInfo fi(strVBox);
+ QString strVBoxDir = QDir::toNativeSeparators(fi.absolutePath());
+ HRESULT rc = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)(&pShl));
+ if (FAILED(rc))
+ return false;
+ do
+ {
+ rc = pShl->SetPath(strVBox.utf16());
+ if (FAILED(rc))
+ break;
+ rc = pShl->SetWorkingDirectory(strVBoxDir.utf16());
+ if (FAILED(rc))
+ break;
+ QString strArgs = QString("--comment \"%1\" --startvm \"%2\"").arg(strName).arg(uUuid.toString());
+ rc = pShl->SetArguments(strArgs.utf16());
+ if (FAILED(rc))
+ break;
+ QString strDesc = QString("Starts the VirtualBox machine %1").arg(strName);
+ rc = pShl->SetDescription(strDesc.utf16());
+ if (FAILED(rc))
+ break;
+ rc = pShl->QueryInterface(IID_IPersistFile, (void**)&pPPF);
+ if (FAILED(rc))
+ break;
+ QString strLink = QString("%1\\%2.lnk").arg(strDstPath).arg(strName);
+ rc = pPPF->Save(strLink.utf16(), TRUE);
+ } while(0);
+ if (pPPF)
+ pPPF->Release();
+ if (pShl)
+ pShl->Release();
+ return SUCCEEDED(rc);
+}
+
+bool UIDesktopServices::openInFileManager(const QString &strFile)
+{
+ QFileInfo fi(strFile);
+ QString strTmp = QDir::toNativeSeparators(fi.absolutePath());
+
+ intptr_t rc = (intptr_t)ShellExecute(NULL, L"explore", strTmp.utf16(), NULL, NULL, SW_SHOWNORMAL);
+
+ return rc > 32 ? true : false;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.cpp b/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.cpp
new file mode 100644
index 00000000..7bd7c440
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.cpp
@@ -0,0 +1,128 @@
+/* $Id: VBoxUtils-win.cpp $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling Windows specific tasks.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "VBoxUtils-win.h"
+
+
+/** Namespace for native window sub-system functions. */
+namespace NativeWindowSubsystem
+{
+ /** Enumerates visible always-on-top (top-most) windows. */
+ BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) RT_NOTHROW_PROTO;
+ /** Contains visible top-most-window rectangles. */
+ QList<QRect> topMostRects;
+}
+
+BOOL CALLBACK NativeWindowSubsystem::EnumWindowsProc(HWND hWnd, LPARAM) RT_NOTHROW_DEF
+{
+ /* Ignore NULL HWNDs: */
+ if (!hWnd)
+ return TRUE;
+
+ /* Ignore hidden windows: */
+ if (!IsWindowVisible(hWnd))
+ return TRUE;
+
+ /* Get window style: */
+ LONG uStyle = GetWindowLong(hWnd, GWL_STYLE);
+ /* Ignore minimized windows: */
+ if (uStyle & WS_MINIMIZE)
+ return TRUE;
+
+ /* Get extended window style: */
+ LONG uExtendedStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
+ /* Ignore non-top-most windows: */
+ if (!(uExtendedStyle & WS_EX_TOPMOST))
+ return TRUE;
+
+ /* Get that window rectangle: */
+ RECT rect;
+ GetWindowRect(hWnd, &rect);
+ topMostRects << QRect(QPoint(rect.left, rect.top), QPoint(rect.right - 1, rect.bottom - 1));
+
+ /* Proceed to the next window: */
+ return TRUE;
+}
+
+const QRegion NativeWindowSubsystem::areaCoveredByTopMostWindows()
+{
+ /* Prepare the top-most region: */
+ QRegion topMostRegion;
+ /* Initialize the list of the top-most rectangles: */
+ topMostRects.clear();
+ /* Populate the list of top-most rectangles: */
+ EnumWindows((WNDENUMPROC)EnumWindowsProc, 0);
+ /* Update the top-most region with top-most rectangles: */
+ for (int iRectIndex = 0; iRectIndex < topMostRects.size(); ++iRectIndex)
+ topMostRegion += topMostRects[iRectIndex];
+ /* Return top-most region: */
+ return topMostRegion;
+}
+
+const void NativeWindowSubsystem::setScreenSaverActive(BOOL fDisableScreenSaver)
+{
+ BOOL fIsActive;
+ SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fIsActive, 0);
+ if (fIsActive == !fDisableScreenSaver)
+ return;
+ //printf("before %d\n", fIsActive);
+
+ SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, !fDisableScreenSaver, NULL, 0);
+
+ SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fIsActive, 0);
+ /*if (fIsActive == !fDisableScreenSaver)
+ printf("success %d %d\n", fIsActive, fDisableScreenSaver);
+*/
+}
+
+BOOL NativeWindowSubsystem::ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason)
+{
+ BOOL fResult = FALSE;
+ typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONCREATE)(HWND hWnd, LPCWSTR pwszReason);
+
+ PFNSHUTDOWNBLOCKREASONCREATE pfn = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(
+ GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
+ _ASSERTE(pfn);
+ if (pfn)
+ fResult = pfn(hWnd, pwszReason);
+ return fResult;
+}
+
+bool NativeWindowSubsystem::WinActivateWindow(WId wId, bool)
+{
+ bool fResult = true;
+ HWND handle = (HWND)wId;
+
+ if (IsIconic(handle))
+ fResult &= !!ShowWindow(handle, SW_RESTORE);
+ else if (!IsWindowVisible(handle))
+ fResult &= !!ShowWindow(handle, SW_SHOW);
+
+ fResult &= !!SetForegroundWindow(handle);
+ return fResult;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.h b/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.h
new file mode 100644
index 00000000..6153684e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.h
@@ -0,0 +1,57 @@
+/* $Id: VBoxUtils-win.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling Windows specific tasks.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_win_VBoxUtils_win_h
+#define FEQT_INCLUDED_SRC_platform_win_VBoxUtils_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QRegion>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* External includes: */
+#include <iprt/win/windows.h>
+
+/* Namespace for native window sub-system functions: */
+namespace NativeWindowSubsystem
+{
+ /* Returns area covered by visible always-on-top (top-most) windows: */
+ SHARED_LIBRARY_STUFF const QRegion areaCoveredByTopMostWindows();
+ SHARED_LIBRARY_STUFF const void setScreenSaverActive(BOOL fDisableScreenSaver);
+
+ /** Wraps WinAPI ShutdownBlockReasonCreate function. */
+ SHARED_LIBRARY_STUFF BOOL ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason);
+
+ /** Activates window with certain @a wId, @a fSwitchDesktop if requested. */
+ bool WinActivateWindow(WId wId, bool fSwitchDesktop);
+}
+
+#endif /* !FEQT_INCLUDED_SRC_platform_win_VBoxUtils_win_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/VirtualBox.VisualElementsManifest.xml b/src/VBox/Frontends/VirtualBox/src/platform/win/VirtualBox.VisualElementsManifest.xml
new file mode 100644
index 00000000..2a511ebf
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/VirtualBox.VisualElementsManifest.xml
@@ -0,0 +1,9 @@
+<Application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <VisualElements
+ BackgroundColor="#447aaa"
+ ShowNameOnSquare150x150Logo="off"
+ ForegroundText="light"
+ Square150x150Logo="VirtualBox_150px.png"
+ Square70x70Logo="VirtualBox_70px.png"
+ />
+</Application>
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.cpp b/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.cpp
new file mode 100644
index 00000000..a50c1f79
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.cpp
@@ -0,0 +1,310 @@
+/* $Id: WinKeyboard.cpp $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Windows Keyboard specific tasks.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Defines: */
+#define LOG_GROUP LOG_GROUP_GUI
+
+/* GUI includes: */
+#include "WinKeyboard.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+/* External includes: */
+#include <stdio.h>
+
+
+/* Beautification of log output */
+#define VBOX_BOOL_TO_STR_STATE(x) (x) ? "ON" : "OFF"
+#define VBOX_CONTROL_TO_STR_NAME(x) ((x == VK_CAPITAL) ? "CAPS" : (x == VK_SCROLL ? "SCROLL" : ((x == VK_NUMLOCK) ? "NUM" : "UNKNOWN")))
+
+/* A structure that contains internal control state representation */
+typedef struct VBoxModifiersState_t {
+ bool fNumLockOn; /** A state of NUM LOCK */
+ bool fCapsLockOn; /** A state of CAPS LOCK */
+ bool fScrollLockOn; /** A state of SCROLL LOCK */
+} VBoxModifiersState_t;
+
+/**
+ * Get current state of a keyboard modifier.
+ *
+ * @param idModifier Modifier to examine (VK_CAPITAL, VK_SCROLL or VK_NUMLOCK)
+ *
+ * @returns modifier state or asserts if wrong modifier is specified.
+ */
+static bool winGetModifierState(int idModifier)
+{
+ AssertReturn((idModifier == VK_CAPITAL) || (idModifier == VK_SCROLL) || (idModifier == VK_NUMLOCK), false);
+ return (GetKeyState(idModifier) & 0x0001) != 0;
+}
+
+/**
+ * Set current state of a keyboard modifier.
+ *
+ * @param idModifier Modifier to set (VK_CAPITAL, VK_SCROLL or VK_NUMLOCK)
+ * @param fState State to set
+ */
+static void winSetModifierState(int idModifier, bool fState)
+{
+ AssertReturnVoid((idModifier == VK_CAPITAL) || (idModifier == VK_SCROLL) || (idModifier == VK_NUMLOCK));
+
+ /* If the modifier is already in desired state, just do nothing. Otherwise, toggle it. */
+ if (winGetModifierState(idModifier) != fState)
+ {
+ /* Simulate KeyUp+KeyDown keystroke */
+ keybd_event(idModifier, 0, KEYEVENTF_EXTENDEDKEY, 0);
+ keybd_event(idModifier, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+
+ /* Process posted above keyboard events immediately: */
+ MSG msg;
+ while (::PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
+ ::DispatchMessage(&msg);
+
+ LogRel2(("HID LEDs sync: setting %s state to %s (0x%X).\n",
+ VBOX_CONTROL_TO_STR_NAME(idModifier), VBOX_BOOL_TO_STR_STATE(fState), MapVirtualKey(idModifier, MAPVK_VK_TO_VSC)));
+ }
+ else
+ {
+ LogRel2(("HID LEDs sync: setting %s state: skipped: state is already %s (0x%X).\n",
+ VBOX_CONTROL_TO_STR_NAME(idModifier), VBOX_BOOL_TO_STR_STATE(fState), MapVirtualKey(idModifier, MAPVK_VK_TO_VSC)));
+ }
+}
+
+/** Set all HID LEDs at once. */
+static bool winSetHidLeds(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+ winSetModifierState(VK_NUMLOCK, fNumLockOn);
+ winSetModifierState(VK_CAPITAL, fCapsLockOn);
+ winSetModifierState(VK_SCROLL, fScrollLockOn);
+ return true;
+}
+
+/** Check if specified LED states correspond to the system modifier states. */
+bool winHidLedsInSync(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+ if (winGetModifierState(VK_NUMLOCK) != fNumLockOn)
+ return false;
+
+ if (winGetModifierState(VK_CAPITAL) != fCapsLockOn)
+ return false;
+
+ if (winGetModifierState(VK_SCROLL) != fScrollLockOn)
+ return false;
+
+ return true;
+}
+
+/**
+ * Allocate memory and store modifier states there.
+ *
+ * @returns allocated memory witch contains modifier states or NULL.
+ */
+void * WinHidDevicesKeepLedsState(void)
+{
+ VBoxModifiersState_t *pState;
+
+ pState = (VBoxModifiersState_t *)malloc(sizeof(VBoxModifiersState_t));
+ if (pState)
+ {
+ pState->fNumLockOn = winGetModifierState(VK_NUMLOCK);
+ pState->fCapsLockOn = winGetModifierState(VK_CAPITAL);
+ pState->fScrollLockOn = winGetModifierState(VK_SCROLL);
+
+ LogRel2(("HID LEDs sync: host state captured: NUM(%s) CAPS(%s) SCROLL(%s)\n",
+ VBOX_BOOL_TO_STR_STATE(pState->fNumLockOn),
+ VBOX_BOOL_TO_STR_STATE(pState->fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(pState->fScrollLockOn)));
+
+ return (void *)pState;
+ }
+
+ LogRel2(("HID LEDs sync: unable to allocate memory for HID LEDs synchronization data. LEDs sync will be disabled.\n"));
+
+ return NULL;
+}
+
+/**
+ * Restore host modifier states and free memory.
+ */
+void WinHidDevicesApplyAndReleaseLedsState(void *pData)
+{
+ VBoxModifiersState_t *pState = (VBoxModifiersState_t *)pData;
+
+ if (pState)
+ {
+ LogRel2(("HID LEDs sync: attempt to restore host state: NUM(%s) CAPS(%s) SCROLL(%s)\n",
+ VBOX_BOOL_TO_STR_STATE(pState->fNumLockOn),
+ VBOX_BOOL_TO_STR_STATE(pState->fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(pState->fScrollLockOn)));
+
+ if (winSetHidLeds(pState->fNumLockOn, pState->fCapsLockOn, pState->fScrollLockOn))
+ LogRel2(("HID LEDs sync: host state restored\n"));
+ else
+ LogRel2(("HID LEDs sync: host state restore failed\n"));
+
+ free(pState);
+ }
+}
+
+/**
+ * Broadcast HID modifier states.
+ *
+ * @param fNumLockOn NUM LOCK state
+ * @param fCapsLockOn CAPS LOCK state
+ * @param fScrollLockOn SCROLL LOCK state
+ */
+void WinHidDevicesBroadcastLeds(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+ LogRel2(("HID LEDs sync: start broadcast guest modifier states: NUM(%s) CAPS(%s) SCROLL(%s)\n",
+ VBOX_BOOL_TO_STR_STATE(fNumLockOn),
+ VBOX_BOOL_TO_STR_STATE(fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(fScrollLockOn)));
+
+ if (winSetHidLeds(fNumLockOn, fCapsLockOn, fScrollLockOn))
+ LogRel2(("HID LEDs sync: broadcast completed\n"));
+ else
+ LogRel2(("HID LEDs sync: broadcast failed\n"));
+}
+
+/** @brief doesCurrentLayoutHaveAltGr
+ *
+ * @return true if this keyboard layout has an AltGr key, false otherwise
+ * Check to see whether the current keyboard layout actually has an AltGr key
+ * by checking whether any of the keys which might do produce a symbol when
+ * AltGr (Control + Alt) is depressed. Generally this loop will exit pretty
+ * early (it exits on the first iteration for my German layout). If there is
+ * no AltGr key in the layout then it will run right through, but that should
+ * hopefully not happen very often.
+ *
+ * In theory we could do this once and cache the result, but that involves
+ * tracking layout switches to invalidate the cache, and I don't think that the
+ * added complexity is worth the price. */
+static bool doesCurrentLayoutHaveAltGr()
+{
+ /* Keyboard state array with VK_CONTROL and VK_MENU depressed. */
+ static const BYTE s_auKeyStates[256] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x80 };
+ WORD ach;
+ unsigned i;
+
+ for (i = '0'; i <= VK_OEM_102; ++i)
+ {
+ if (ToAscii(i, 0, s_auKeyStates, &ach, 0))
+ break;
+ /* Skip ranges of virtual keys which are undefined or not relevant. */
+ if (i == '9')
+ i = 'A' - 1;
+ if (i == 'Z')
+ i = VK_OEM_1 - 1;
+ if (i == VK_OEM_3)
+ i = VK_OEM_4 - 1;
+ if (i == VK_OEM_8)
+ i = VK_OEM_102 - 1;
+ }
+ if (i > VK_OEM_102)
+ return false;
+ return true;
+}
+
+void WinAltGrMonitor::updateStateFromKeyEvent(unsigned iDownScanCode,
+ bool fKeyDown, bool fExtendedKey)
+{
+ LONG messageTime = GetMessageTime();
+ /* We do not want the make/break: */
+ AssertRelease(~iDownScanCode & 0x80);
+ /* Depending on m_enmFakeControlDetectionState: */
+ switch (m_enmFakeControlDetectionState)
+ {
+ case NONE:
+ case FAKE_CONTROL_DOWN:
+ if ( iDownScanCode == 0x1D /* left control */
+ && fKeyDown
+ && !fExtendedKey)
+ m_enmFakeControlDetectionState = LAST_EVENT_WAS_LEFT_CONTROL_DOWN;
+ else
+ m_enmFakeControlDetectionState = NONE;
+ break;
+ case LAST_EVENT_WAS_LEFT_CONTROL_DOWN:
+ if ( iDownScanCode == 0x38 /* Alt */
+ && fKeyDown
+ && fExtendedKey
+ && m_timeOfLastKeyEvent == messageTime
+ && doesCurrentLayoutHaveAltGr())
+ {
+ m_enmFakeControlDetectionState = FAKE_CONTROL_DOWN;
+ break;
+ }
+ else
+ m_enmFakeControlDetectionState = LEFT_CONTROL_DOWN;
+ RT_FALL_THRU();
+ case LEFT_CONTROL_DOWN:
+ if ( iDownScanCode == 0x1D /* left control */
+ && !fKeyDown
+ && !fExtendedKey)
+ m_enmFakeControlDetectionState = NONE;
+ break;
+ default:
+ AssertReleaseMsgFailed(("Unknown AltGr detection state.\n"));
+ }
+ m_timeOfLastKeyEvent = messageTime;
+}
+
+bool WinAltGrMonitor::isLeftControlReleaseNeeded() const
+{
+ return m_enmFakeControlDetectionState == FAKE_CONTROL_DOWN;
+}
+
+bool WinAltGrMonitor::isCurrentEventDefinitelyFake(unsigned iDownScanCode,
+ bool fKeyDown,
+ bool fExtendedKey) const
+{
+ if (iDownScanCode != 0x1d /* scan code: Control */ || fExtendedKey)
+ return false;
+
+ LONG messageTime = GetMessageTime();
+ MSG peekMsg;
+ if (!PeekMessage(&peekMsg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE))
+ return false;
+ if (messageTime != (LONG)peekMsg.time)
+ return false;
+
+ if ( fKeyDown
+ && peekMsg.message != WM_KEYDOWN
+ && peekMsg.message != WM_SYSKEYDOWN)
+ return false;
+ if ( !fKeyDown
+ && peekMsg.message != WM_KEYUP
+ && peekMsg.message != WM_SYSKEYUP)
+ return false;
+ if ( (RT_HIWORD(peekMsg.lParam) & 0xFF) != 0x38 /* scan code: Alt */
+ || !(RT_HIWORD(peekMsg.lParam) & KF_EXTENDED))
+ return false;
+ if (!doesCurrentLayoutHaveAltGr())
+ return false;
+ return true;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.h b/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.h
new file mode 100644
index 00000000..c9eb5c6b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.h
@@ -0,0 +1,102 @@
+/* $Id: WinKeyboard.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Windows Keyboard specific tasks.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_win_WinKeyboard_h
+#define FEQT_INCLUDED_SRC_platform_win_WinKeyboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/win/windows.h>
+
+SHARED_LIBRARY_STUFF void * WinHidDevicesKeepLedsState(void);
+SHARED_LIBRARY_STUFF void WinHidDevicesApplyAndReleaseLedsState(void *pData);
+SHARED_LIBRARY_STUFF void WinHidDevicesBroadcastLeds(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn);
+
+SHARED_LIBRARY_STUFF bool winHidLedsInSync(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn);
+
+/** Helper class to deal with Windows AltGr handling.
+ *
+ * Background: Windows sends AltGr key down and up events as two events: a
+ * left control event and a right alt one. Since the left control event does
+ * not correspond to actually pressing or releasing the left control key we
+ * would like to detect it and handle it. This class monitors all key down and
+ * up events and if it detects that a left control down event has been sendt
+ * although left control should be up it tells us to insert a left control up
+ * event into the event stream. While this does not let us filter out the
+ * unwanted event at source, it should still make guest system keyboard handling
+ * work correctly. */
+class SHARED_LIBRARY_STUFF WinAltGrMonitor
+{
+public:
+
+ /** Constructor. */
+ WinAltGrMonitor() : m_enmFakeControlDetectionState(NONE), m_timeOfLastKeyEvent(0) {}
+
+ /** All key events should be fed to this method.
+ * @param iDownScanCode the scan code stripped of the make/break bit
+ * @param fKeyDown is this a key down event?
+ * @param fExtended is this an extended scan code? */
+ void updateStateFromKeyEvent(unsigned iDownScanCode, bool fKeyDown, bool fExtended);
+
+ /** Do we need to insert a left control up into the stream? */
+ bool isLeftControlReleaseNeeded() const;
+
+ /** Can we tell for sure at this point that the current message is a fake
+ * control event? This method might fail to recognise a fake event, but
+ * should never incorrectly flag a non-fake one.
+ * @note We deliberately do not call this from the host combination editor
+ * in an attempt to ensure that the other code path also gets enough
+ * test coverage.
+ */
+ bool isCurrentEventDefinitelyFake(unsigned iDownScanCode,
+ bool fKeyDown,
+ bool fExtendedKey) const;
+
+private:
+
+ /** State detection for fake control events which we may have missed. */
+ enum
+ {
+ /** No interesting state. */
+ NONE,
+ /** The last keypress might be a fake control. */
+ LAST_EVENT_WAS_LEFT_CONTROL_DOWN,
+ /** Left control is down, so we ignore fake control events. */
+ LEFT_CONTROL_DOWN,
+ /** A fake control down event and no up was passed to the guest. */
+ FAKE_CONTROL_DOWN
+ } m_enmFakeControlDetectionState;
+ LONG m_timeOfLastKeyEvent;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_win_WinKeyboard_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/platform/x11/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/UIDesktopServices_x11.cpp b/src/VBox/Frontends/VirtualBox/src/platform/x11/UIDesktopServices_x11.cpp
new file mode 100644
index 00000000..ca1a6c1e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/UIDesktopServices_x11.cpp
@@ -0,0 +1,86 @@
+/* $Id: UIDesktopServices_x11.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to X11..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UIDesktopServices.h"
+
+/* Qt includes */
+#include <QCoreApplication>
+#include <QDesktopServices>
+#include <QDir>
+#include <QFile>
+#include <QTextStream>
+#include <QUrl>
+
+
+bool UIDesktopServices::createMachineShortcut(const QString & /* strSrcFile */, const QString &strDstPath, const QString &strName, const QUuid &uUuid)
+{
+ QFile link(strDstPath + QDir::separator() + strName + ".desktop");
+ if (link.open(QFile::WriteOnly | QFile::Truncate))
+ {
+ const QString strVBox = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/" + VBOX_GUI_VMRUNNER_IMAGE);
+ QTextStream out(&link);
+#ifndef VBOX_IS_QT6_OR_LATER /* QTextStream defaults to UTF-8 only since qt6 */
+ out.setCodec("UTF-8");
+#endif
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+# define QT_ENDL Qt::endl
+#else
+# define QT_ENDL endl
+#endif
+ /* Create a link which starts VirtualBox with the machine uuid. */
+ out << "[Desktop Entry]" << QT_ENDL
+ << "Encoding=UTF-8" << QT_ENDL
+ << "Version=1.0" << QT_ENDL
+ << "Name=" << strName << QT_ENDL
+ << "Comment=Starts the VirtualBox machine " << strName << QT_ENDL
+ << "Type=Application" << QT_ENDL
+ << "Exec=" << strVBox << " --comment \"" << strName << "\" --startvm \"" << uUuid.toString() << "\"" << QT_ENDL
+ << "Icon=virtualbox-vbox.png" << QT_ENDL;
+ /* This would be a real file link entry, but then we could also simply
+ * use a soft link (on most UNIX fs):
+ out << "[Desktop Entry]" << QT_ENDL
+ << "Encoding=UTF-8" << QT_ENDL
+ << "Version=1.0" << QT_ENDL
+ << "Name=" << strName << QT_ENDL
+ << "Type=Link" << QT_ENDL
+ << "Icon=virtualbox-vbox.png" << QT_ENDL
+ */
+ link.setPermissions(link.permissions() | QFile::ExeOwner);
+ /** @todo r=bird: check status here perhaps, might've run out of disk space or
+ * some such thing... */
+ return true;
+ }
+ return false;
+}
+
+bool UIDesktopServices::openInFileManager(const QString &strFile)
+{
+ QFileInfo fi(strFile);
+ return QDesktopServices::openUrl(QUrl("file://" + QDir::toNativeSeparators(fi.absolutePath()), QUrl::TolerantMode));
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.cpp b/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.cpp
new file mode 100644
index 00000000..e8b8db43
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.cpp
@@ -0,0 +1,679 @@
+/* $Id: VBoxUtils-x11.cpp $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling X11 specific tasks.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusConnectionInterface>
+#include <QtDBus/QDBusInterface>
+#include <QtDBus/QDBusReply>
+#include <QtXml/QDomDocument>
+#include <QtXml/QDomElement>
+#endif
+#include <QWidget>
+#ifdef VBOX_IS_QT6_OR_LATER /** @todo qt6: ... */
+# include <QGuiApplication>
+#else
+# include <QX11Info>
+#endif
+
+/* GUI includes: */
+#include "VBoxUtils-x11.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+/* Other includes: */
+#undef BOOL /* Undefine the VBox/com/defs.h variant */
+#define BOOL X11BOOL /* Typedef'ed in Xmd.h via dpms.h, causing -Wpch-invalid to trigger. */
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/dpms.h>
+#undef BOOL /* Restore the VBox/com/defs.h variant */
+#define BOOL PRBool
+
+
+bool NativeWindowSubsystem::X11IsCompositingManagerRunning()
+{
+ /* For each screen it manage, compositing manager MUST acquire ownership
+ * of a selection named _NET_WM_CM_Sn, where n is the screen number. */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ Atom atom_property_name = XInternAtom(pDisplay, "_NET_WM_CM_S0", True);
+ return XGetSelectionOwner(pDisplay, atom_property_name);
+}
+
+X11WMType NativeWindowSubsystem::X11WindowManagerType()
+{
+ /* Ask if root-window supports check for WM name: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ Atom atom_property_name;
+ Atom atom_returned_type;
+ int iReturnedFormat;
+ unsigned long ulReturnedItemCount;
+ unsigned long ulDummy;
+ unsigned char *pcData = 0;
+ X11WMType wmType = X11WMType_Unknown;
+ atom_property_name = XInternAtom(pDisplay, "_NET_SUPPORTING_WM_CHECK", True);
+ if (XGetWindowProperty(pDisplay, NativeWindowSubsystem::X11GetAppRootWindow(), atom_property_name,
+ 0, 512, False, XA_WINDOW, &atom_returned_type,
+ &iReturnedFormat, &ulReturnedItemCount, &ulDummy, &pcData) == Success)
+ {
+ Window WMWindow = None;
+ if (atom_returned_type == XA_WINDOW && iReturnedFormat == 32)
+ WMWindow = *((Window*)pcData);
+ if (pcData)
+ XFree(pcData);
+ if (WMWindow != None)
+ {
+ /* Ask root-window for WM name: */
+ atom_property_name = XInternAtom(pDisplay, "_NET_WM_NAME", True);
+ Atom utf8Atom = XInternAtom(pDisplay, "UTF8_STRING", True);
+ if (XGetWindowProperty(pDisplay, WMWindow, atom_property_name,
+ 0, 512, False, utf8Atom, &atom_returned_type,
+ &iReturnedFormat, &ulReturnedItemCount, &ulDummy, &pcData) == Success)
+ {
+ /** @todo r=bird: 6 QString conversions cannot be very efficient. */
+ if (QString((const char*)pcData).contains("Compiz", Qt::CaseInsensitive))
+ wmType = X11WMType_Compiz;
+ else
+ if (QString((const char*)pcData).contains("GNOME Shell", Qt::CaseInsensitive))
+ wmType = X11WMType_GNOMEShell;
+ else
+ if (QString((const char*)pcData).contains("KWin", Qt::CaseInsensitive))
+ wmType = X11WMType_KWin;
+ else
+ if (QString((const char*)pcData).contains("Metacity", Qt::CaseInsensitive))
+ wmType = X11WMType_Metacity;
+ else
+ if (QString((const char*)pcData).contains("Mutter", Qt::CaseInsensitive))
+ wmType = X11WMType_Mutter;
+ else
+ if (QString((const char*)pcData).contains("Xfwm4", Qt::CaseInsensitive))
+ wmType = X11WMType_Xfwm4;
+ if (pcData)
+ XFree(pcData);
+ }
+ }
+ }
+ return wmType;
+}
+
+bool NativeWindowSubsystem::X11CheckExtension(const char *pExtensionName)
+{
+ /* Check extension: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ int major_opcode;
+ int first_event;
+ int first_error;
+ return XQueryExtension(pDisplay, pExtensionName, &major_opcode, &first_event, &first_error);
+}
+
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+bool X11CheckDBusConnection(const QDBusConnection &connection)
+{
+ if (!connection.isConnected())
+ {
+ const QDBusError lastError = connection.lastError();
+ if (lastError.isValid())
+ {
+ LogRel(("QDBus error. Could not connect to D-Bus server: %s: %s\n",
+ lastError.name().toUtf8().constData(),
+ lastError.message().toUtf8().constData()));
+ }
+ else
+ LogRel(("QDBus error. Could not connect to D-Bus server: Unable to load dbus libraries\n"));
+ return false;
+ }
+ return true;
+}
+
+QStringList X11FindDBusScreenSaverServices(const QDBusConnection &connection)
+{
+ QStringList serviceNames;
+
+ QDBusReply<QStringList> replyr = connection.interface()->registeredServiceNames();
+ if (!replyr.isValid())
+ {
+ const QDBusError replyError = replyr.error();
+ LogRel(("QDBus error. Could not query registered service names %s %s",
+ replyError.name().toUtf8().constData(), replyError.message().toUtf8().constData()));
+ return serviceNames;
+ }
+
+ for (int i = 0; i < replyr.value().size(); ++i)
+ {
+ const QString strServiceName = replyr.value()[i];
+ if (strServiceName.contains("screensaver", Qt::CaseInsensitive))
+ serviceNames << strServiceName;
+ }
+ if (serviceNames.isEmpty())
+ LogRel(("QDBus error. No screen saver service found among registered DBus services."));
+
+ return serviceNames;
+}
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+
+bool NativeWindowSubsystem::X11CheckDBusScreenSaverServices()
+{
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+ QDBusConnection connection = QDBusConnection::sessionBus();
+ if (!X11CheckDBusConnection(connection))
+ return false;
+
+ QDBusReply<QStringList> replyr = connection.interface()->registeredServiceNames();
+ if (!replyr.isValid())
+ {
+ const QDBusError replyError = replyr.error();
+ LogRel(("QDBus error. Could not query registered service names %s %s",
+ replyError.name().toUtf8().constData(), replyError.message().toUtf8().constData()));
+ return false;
+ }
+ for (int i = 0; i < replyr.value().size(); ++i)
+ {
+ const QString strServiceName = replyr.value()[i];
+ if (strServiceName.contains("screensaver", Qt::CaseInsensitive))
+ return true;
+ }
+ LogRel(("QDBus error. No screen saver service found among registered DBus services."));
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+ return false;
+}
+
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+void X11IntrospectInterfaceNode(const QDomElement &interface,
+ const QString &strServiceName,
+ QVector<X11ScreenSaverInhibitMethod*> &methods)
+{
+ QDomElement child = interface.firstChildElement();
+ while (!child.isNull())
+ {
+ if (child.tagName() == "method" && child.attribute("name") == "Inhibit")
+ {
+ X11ScreenSaverInhibitMethod *newMethod = new X11ScreenSaverInhibitMethod;
+ newMethod->m_iCookie = 0;
+ newMethod->m_strServiceName = strServiceName;
+ newMethod->m_strInterface = interface.attribute("name");
+ newMethod->m_strPath = "/";
+ newMethod->m_strPath.append(interface.attribute("name"));
+ newMethod->m_strPath.replace(".", "/");
+ methods.append(newMethod);
+ }
+ child = child.nextSiblingElement();
+ }
+}
+
+void X11IntrospectServices(const QDBusConnection &connection,
+ const QString &strService,
+ const QString &strPath,
+ QVector<X11ScreenSaverInhibitMethod*> &methods)
+{
+ QDBusMessage call = QDBusMessage::createMethodCall(strService, strPath.isEmpty() ? QLatin1String("/") : strPath,
+ QLatin1String("org.freedesktop.DBus.Introspectable"),
+ QLatin1String("Introspect"));
+ QDBusReply<QString> xmlReply = connection.call(call);
+
+ if (!xmlReply.isValid())
+ return;
+
+ QDomDocument doc;
+ doc.setContent(xmlReply);
+ QDomElement node = doc.documentElement();
+ QDomElement child = node.firstChildElement();
+ while (!child.isNull())
+ {
+ if (child.tagName() == QLatin1String("node"))
+ {
+ QString subPath = strPath + QLatin1Char('/') + child.attribute(QLatin1String("name"));
+ X11IntrospectServices(connection, strService, subPath, methods);
+ }
+ else if (child.tagName() == QLatin1String("interface"))
+ X11IntrospectInterfaceNode(child, strService, methods);
+ child = child.nextSiblingElement();
+ }
+}
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+
+QVector<X11ScreenSaverInhibitMethod*> NativeWindowSubsystem::X11FindDBusScrenSaverInhibitMethods()
+{
+ QVector<X11ScreenSaverInhibitMethod*> methods;
+
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+ QDBusConnection connection = QDBusConnection::sessionBus();
+ if (!X11CheckDBusConnection(connection))
+ return methods;
+
+ QStringList services = X11FindDBusScreenSaverServices(connection);
+ foreach(const QString &strServiceName, services)
+ X11IntrospectServices(connection, strServiceName, "", methods);
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+
+ return methods;
+}
+
+void NativeWindowSubsystem::X11InhibitUninhibitScrenSaver(bool fInhibit, QVector<X11ScreenSaverInhibitMethod*> &inOutInhibitMethods)
+{
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+ QDBusConnection connection = QDBusConnection::sessionBus();
+ if (!X11CheckDBusConnection(connection))
+ return;
+ for (int i = 0; i < inOutInhibitMethods.size(); ++i)
+ {
+ QDBusInterface screenSaverInterface(inOutInhibitMethods[i]->m_strServiceName, inOutInhibitMethods[i]->m_strPath,
+ inOutInhibitMethods[i]->m_strInterface, connection);
+ if (!screenSaverInterface.isValid())
+ {
+ QDBusError error = screenSaverInterface.lastError();
+ LogRel(("QDBus error for service %s: %s. %s\n",
+ inOutInhibitMethods[i]->m_strServiceName.toUtf8().constData(),
+ error.name().toUtf8().constData(),
+ error.message().toUtf8().constData()));
+ continue;
+ }
+ QDBusReply<uint> reply;
+ if (fInhibit)
+ {
+ reply = screenSaverInterface.call("Inhibit", "Oracle VirtualBox", "ScreenSaverInhibit");
+ if (reply.isValid())
+ inOutInhibitMethods[i]->m_iCookie = reply.value();
+ }
+ else
+ {
+ reply = screenSaverInterface.call("UnInhibit", inOutInhibitMethods[i]->m_iCookie);
+ }
+ if (!reply.isValid())
+ {
+ QDBusError error = reply.error();
+ LogRel(("QDBus inhibition call error for service %s: %s. %s\n",
+ inOutInhibitMethods[i]->m_strServiceName.toUtf8().constData(),
+ error.name().toUtf8().constData(),
+ error.message().toUtf8().constData()));
+ }
+ }
+#else
+ Q_UNUSED(fInhibit);
+ Q_UNUSED(inOutInhibitMethods);
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+}
+
+char *XXGetProperty(Display *pDpy, Window windowHandle, Atom propType, const char *pszPropName)
+{
+ Atom propNameAtom = XInternAtom(pDpy, pszPropName, True /* only_if_exists */);
+ if (propNameAtom == None)
+ return NULL;
+
+ Atom actTypeAtom = None;
+ int actFmt = 0;
+ unsigned long nItems = 0;
+ unsigned long nBytesAfter = 0;
+ unsigned char *propVal = NULL;
+ int rc = XGetWindowProperty(pDpy, windowHandle, propNameAtom,
+ 0, LONG_MAX, False /* delete */,
+ propType, &actTypeAtom, &actFmt,
+ &nItems, &nBytesAfter, &propVal);
+ if (rc != Success)
+ return NULL;
+
+ return reinterpret_cast<char*>(propVal);
+}
+
+bool XXSendClientMessage(Display *pDpy, Window windowHandle, const char *pszMsg,
+ unsigned long aData0 = 0, unsigned long aData1 = 0,
+ unsigned long aData2 = 0, unsigned long aData3 = 0,
+ unsigned long aData4 = 0)
+{
+ Atom msgAtom = XInternAtom(pDpy, pszMsg, True /* only_if_exists */);
+ if (msgAtom == None)
+ return false;
+
+ XEvent ev;
+
+ ev.xclient.type = ClientMessage;
+ ev.xclient.serial = 0;
+ ev.xclient.send_event = True;
+ ev.xclient.display = pDpy;
+ ev.xclient.window = windowHandle;
+ ev.xclient.message_type = msgAtom;
+
+ /* Always send as 32 bit for now: */
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = aData0;
+ ev.xclient.data.l[1] = aData1;
+ ev.xclient.data.l[2] = aData2;
+ ev.xclient.data.l[3] = aData3;
+ ev.xclient.data.l[4] = aData4;
+
+ return XSendEvent(pDpy, DefaultRootWindow(pDpy), False,
+ SubstructureRedirectMask, &ev) != 0;
+}
+
+bool NativeWindowSubsystem::X11ActivateWindow(WId wId, bool fSwitchDesktop)
+{
+ bool fResult = true;
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ if (fSwitchDesktop)
+ {
+ /* Try to find the desktop ID using the NetWM property: */
+ CARD32 *pDesktop = (CARD32*)XXGetProperty(pDisplay, wId, XA_CARDINAL, "_NET_WM_DESKTOP");
+ if (pDesktop == NULL)
+ // WORKAROUND:
+ // if the NetWM properly is not supported try to find
+ // the desktop ID using the GNOME WM property.
+ pDesktop = (CARD32*)XXGetProperty(pDisplay, wId, XA_CARDINAL, "_WIN_WORKSPACE");
+
+ if (pDesktop != NULL)
+ {
+ bool ok = XXSendClientMessage(pDisplay, DefaultRootWindow(pDisplay), "_NET_CURRENT_DESKTOP", *pDesktop);
+ if (!ok)
+ {
+ Log1WarningFunc(("Couldn't switch to pDesktop=%08X\n", pDesktop));
+ fResult = false;
+ }
+ XFree(pDesktop);
+ }
+ else
+ {
+ Log1WarningFunc(("Couldn't find a pDesktop ID for wId=%08X\n", wId));
+ fResult = false;
+ }
+ }
+
+ bool ok = XXSendClientMessage(pDisplay, wId, "_NET_ACTIVE_WINDOW");
+ fResult &= !!ok;
+
+ XRaiseWindow(pDisplay, wId);
+ return fResult;
+}
+
+bool NativeWindowSubsystem::X11SupportsFullScreenMonitorsProtocol()
+{
+ /* This method tests whether the current X11 window manager supports full-screen mode as we need it.
+ * Unfortunately the EWMH specification was not fully clear about whether we can expect to find
+ * all of these atoms on the _NET_SUPPORTED root window property, so we have to test with all
+ * interesting window managers. If this fails for a user when you think it should succeed
+ * they should try executing:
+ * xprop -root | egrep -w '_NET_WM_FULLSCREEN_MONITORS|_NET_WM_STATE|_NET_WM_STATE_FULLSCREEN'
+ * in an X11 terminal window.
+ * All three strings should be found under a property called "_NET_SUPPORTED(ATOM)". */
+
+ /* Using a global to get at the display does not feel right, but that is how it is done elsewhere in the code. */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ Atom atomSupported = XInternAtom(pDisplay, "_NET_SUPPORTED",
+ True /* only_if_exists */);
+ Atom atomWMFullScreenMonitors = XInternAtom(pDisplay,
+ "_NET_WM_FULLSCREEN_MONITORS",
+ True /* only_if_exists */);
+ Atom atomWMState = XInternAtom(pDisplay,
+ "_NET_WM_STATE",
+ True /* only_if_exists */);
+ Atom atomWMStateFullScreen = XInternAtom(pDisplay,
+ "_NET_WM_STATE_FULLSCREEN",
+ True /* only_if_exists */);
+ bool fFoundFullScreenMonitors = false;
+ bool fFoundState = false;
+ bool fFoundStateFullScreen = false;
+ Atom atomType;
+ int cFormat;
+ unsigned long cItems;
+ unsigned long cbLeft;
+ Atom *pAtomHints;
+ int rc;
+ unsigned i;
+
+ if ( atomSupported == None || atomWMFullScreenMonitors == None
+ || atomWMState == None || atomWMStateFullScreen == None)
+ return false;
+ /* Get atom value: */
+ rc = XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay),
+ atomSupported, 0, 0x7fffffff /*LONG_MAX*/,
+ False /* delete */, XA_ATOM, &atomType,
+ &cFormat, &cItems, &cbLeft,
+ (unsigned char **)&pAtomHints);
+ if (rc != Success)
+ return false;
+ if (pAtomHints == NULL)
+ return false;
+ if (atomType == XA_ATOM && cFormat == 32 && cbLeft == 0)
+ for (i = 0; i < cItems; ++i)
+ {
+ if (pAtomHints[i] == atomWMFullScreenMonitors)
+ fFoundFullScreenMonitors = true;
+ if (pAtomHints[i] == atomWMState)
+ fFoundState = true;
+ if (pAtomHints[i] == atomWMStateFullScreen)
+ fFoundStateFullScreen = true;
+ }
+ XFree(pAtomHints);
+ return fFoundFullScreenMonitors && fFoundState && fFoundStateFullScreen;
+}
+
+bool NativeWindowSubsystem::X11SetFullScreenMonitor(QWidget *pWidget, ulong uScreenId)
+{
+ return XXSendClientMessage(NativeWindowSubsystem::X11GetDisplay(),
+ pWidget->window()->winId(),
+ "_NET_WM_FULLSCREEN_MONITORS",
+ uScreenId, uScreenId, uScreenId, uScreenId,
+ 1 /* Source indication (1 = normal application) */);
+}
+
+QVector<Atom> flagsNetWmState(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ QVector<Atom> resultNetWmState;
+ Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
+
+ /* Get the size of the property data: */
+ Atom actual_type;
+ int iActualFormat;
+ ulong uPropertyLength;
+ ulong uBytesLeft;
+ uchar *pPropertyData = 0;
+ if (XGetWindowProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, 0, 0, False, XA_ATOM, &actual_type, &iActualFormat,
+ &uPropertyLength, &uBytesLeft, &pPropertyData) == Success &&
+ actual_type == XA_ATOM && iActualFormat == 32)
+ {
+ resultNetWmState.resize(uBytesLeft / 4);
+ XFree((char*)pPropertyData);
+ pPropertyData = 0;
+
+ /* Fetch all data: */
+ if (XGetWindowProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, 0, resultNetWmState.size(), False, XA_ATOM, &actual_type, &iActualFormat,
+ &uPropertyLength, &uBytesLeft, &pPropertyData) != Success)
+ resultNetWmState.clear();
+ else if (uPropertyLength != (ulong)resultNetWmState.size())
+ resultNetWmState.resize(uPropertyLength);
+
+ /* Put it into resultNetWmState: */
+ if (!resultNetWmState.isEmpty())
+ memcpy(resultNetWmState.data(), pPropertyData, resultNetWmState.size() * sizeof(Atom));
+ if (pPropertyData)
+ XFree((char*)pPropertyData);
+ }
+
+ /* Return result: */
+ return resultNetWmState;
+}
+
+#if 0 // unused for now?
+bool NativeWindowSubsystem::isFullScreenFlagSet(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ Atom net_wm_state_fullscreen = XInternAtom(pDisplay, "_NET_WM_STATE_FULLSCREEN", True /* only if exists */);
+
+ /* Check if flagsNetWmState(pWidget) contains full-screen flag: */
+ return flagsNetWmState(pWidget).contains(net_wm_state_fullscreen);
+}
+
+void NativeWindowSubsystem::setFullScreenFlag(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
+ Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
+ Atom net_wm_state_fullscreen = XInternAtom(pDisplay, "_NET_WM_STATE_FULLSCREEN", True /* only if exists */);
+
+ /* Append resultNetWmState with fullscreen flag if necessary: */
+ if (!resultNetWmState.contains(net_wm_state_fullscreen))
+ {
+ resultNetWmState.append(net_wm_state_fullscreen);
+ /* Apply property to widget again: */
+ XChangeProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
+ }
+}
+#endif // unused for now?
+
+void NativeWindowSubsystem::X11SetSkipTaskBarFlag(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
+ Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
+ Atom net_wm_state_skip_taskbar = XInternAtom(pDisplay, "_NET_WM_STATE_SKIP_TASKBAR", True /* only if exists */);
+
+ /* Append resultNetWmState with skip-taskbar flag if necessary: */
+ if (!resultNetWmState.contains(net_wm_state_skip_taskbar))
+ {
+ resultNetWmState.append(net_wm_state_skip_taskbar);
+ /* Apply property to widget again: */
+ XChangeProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
+ }
+}
+
+void NativeWindowSubsystem::X11SetSkipPagerFlag(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
+ Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
+ Atom net_wm_state_skip_pager = XInternAtom(pDisplay, "_NET_WM_STATE_SKIP_PAGER", True /* only if exists */);
+
+ /* Append resultNetWmState with skip-pager flag if necessary: */
+ if (!resultNetWmState.contains(net_wm_state_skip_pager))
+ {
+ resultNetWmState.append(net_wm_state_skip_pager);
+ /* Apply property to widget again: */
+ XChangeProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
+ }
+}
+
+void NativeWindowSubsystem::X11SetWMClass(QWidget *pWidget, const QString &strNameString, const QString &strClassString)
+{
+ /* Make sure all arguments set: */
+ AssertReturnVoid(pWidget && !strNameString.isNull() && !strClassString.isNull());
+
+ /* Define QByteArray objects to make sure data is alive within the scope: */
+ QByteArray nameByteArray;
+ /* Check the existence of RESOURCE_NAME env. variable and override name string if necessary: */
+ const char resourceName[] = "RESOURCE_NAME";
+ if (qEnvironmentVariableIsSet(resourceName))
+ nameByteArray = qgetenv(resourceName);
+ else
+ nameByteArray = strNameString.toLatin1();
+ QByteArray classByteArray = strClassString.toLatin1();
+
+ AssertReturnVoid(nameByteArray.data() && classByteArray.data());
+
+ XClassHint windowClass;
+ windowClass.res_name = nameByteArray.data();
+ windowClass.res_class = classByteArray.data();
+ /* Set WM_CLASS of the window to passed name and class strings: */
+ XSetClassHint(NativeWindowSubsystem::X11GetDisplay(), pWidget->window()->winId(), &windowClass);
+}
+
+void NativeWindowSubsystem::X11SetXwaylandMayGrabKeyboardFlag(QWidget *pWidget)
+{
+ XXSendClientMessage(NativeWindowSubsystem::X11GetDisplay(), pWidget->window()->winId(),
+ "_XWAYLAND_MAY_GRAB_KEYBOARD", 1);
+}
+
+Display *NativeWindowSubsystem::X11GetDisplay()
+{
+#ifdef VBOX_IS_QT6_OR_LATER /** QX11Info is replaced with QNativeInterface::QX11Application since qt6 */
+ Display *pDisplay = 0;
+ if (qApp)
+ {
+ QNativeInterface::QX11Application *pX11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
+ if (pX11App)
+ pDisplay = pX11App->display();
+ }
+#else
+ Display *pDisplay = QX11Info::display();
+#endif
+ Assert(pDisplay);
+ return pDisplay;
+}
+
+xcb_connection_t *NativeWindowSubsystem::X11GetConnection()
+{
+#ifdef VBOX_IS_QT6_OR_LATER /** QX11Info is replaced with QNativeInterface::QX11Application since qt6 */
+ xcb_connection_t *pConnection = 0;
+ if (qApp)
+ {
+ QNativeInterface::QX11Application *pX11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
+ if (pX11App)
+ pConnection = pX11App->connection();
+ }
+#else
+ xcb_connection_t *pConnection = QX11Info::connection();
+#endif
+ Assert(pConnection);
+ return pConnection;
+}
+
+uint32_t NativeWindowSubsystem::X11GetAppRootWindow()
+{
+#ifdef VBOX_IS_QT6_OR_LATER /** QX11Info is replaced with QNativeInterface::QX11Application since qt6 */
+ Window idWindow = 0;
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ if (pDisplay)
+ idWindow = DefaultRootWindow(pDisplay);
+ return idWindow;
+#else
+ return QX11Info::appRootWindow();
+#endif
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.h b/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.h
new file mode 100644
index 00000000..12b4a172
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.h
@@ -0,0 +1,140 @@
+/* $Id: VBoxUtils-x11.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling X11 specific tasks.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_x11_VBoxUtils_x11_h
+#define FEQT_INCLUDED_SRC_platform_x11_VBoxUtils_x11_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+#include <QVector>
+#include <QWindow>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** X11: Known Window Manager types. */
+enum X11WMType
+{
+ X11WMType_Unknown,
+ X11WMType_Compiz,
+ X11WMType_GNOMEShell,
+ X11WMType_KWin,
+ X11WMType_Metacity,
+ X11WMType_Mutter,
+ X11WMType_Xfwm4,
+};
+
+/** X11: Screen-saver inhibit methods. */
+struct SHARED_LIBRARY_STUFF X11ScreenSaverInhibitMethod
+{
+ QString m_strServiceName;
+ QString m_strInterface;
+ QString m_strPath;
+ uint m_iCookie;
+};
+
+/** X11: XCB size-hints. */
+typedef struct
+{
+ /** User specified flags */
+ uint32_t flags;
+ /** User-specified position */
+ int32_t x, y;
+ /** User-specified size */
+ int32_t width, height;
+ /** Program-specified minimum size */
+ int32_t min_width, min_height;
+ /** Program-specified maximum size */
+ int32_t max_width, max_height;
+ /** Program-specified resize increments */
+ int32_t width_inc, height_inc;
+ /** Program-specified minimum aspect ratios */
+ int32_t min_aspect_num, min_aspect_den;
+ /** Program-specified maximum aspect ratios */
+ int32_t max_aspect_num, max_aspect_den;
+ /** Program-specified base size */
+ int32_t base_width, base_height;
+ /** Program-specified window gravity */
+ uint32_t win_gravity;
+} xcb_size_hints_t;
+
+/* X11 structs to avoid dragging in unnecessary X headers: */
+struct xcb_connection_t;
+struct _XDisplay;
+
+/* Namespace for native window sub-system functions: */
+namespace NativeWindowSubsystem
+{
+ /** X11: Determines and returns whether the compositing manager is running. */
+ bool X11IsCompositingManagerRunning();
+ /** X11: Determines and returns current Window Manager type. */
+ X11WMType X11WindowManagerType();
+
+ /** X11: Returns true if XLib extension with name @p extensionName is avaible, false otherwise. */
+ bool X11CheckExtension(const char *extensionName);
+
+ /** X11: Returns whether there are any DBus services whose name contains the substring 'screensaver'. */
+ bool X11CheckDBusScreenSaverServices();
+ /** X11: Returns the list of Inhibit methods found by introspecting DBus services. */
+ SHARED_LIBRARY_STUFF QVector<X11ScreenSaverInhibitMethod*> X11FindDBusScrenSaverInhibitMethods();
+ /** X11: Disables/enables Screen Saver through QDBus. */
+ SHARED_LIBRARY_STUFF void X11InhibitUninhibitScrenSaver(bool fInhibit, QVector<X11ScreenSaverInhibitMethod*> &inOutInhibitMethods);
+
+ /** Activates window with certain @a wId, @a fSwitchDesktop if requested. */
+ bool X11ActivateWindow(WId wId, bool fSwitchDesktop);
+
+ /** X11: Test whether the current window manager supports full screen mode. */
+ SHARED_LIBRARY_STUFF bool X11SupportsFullScreenMonitorsProtocol();
+ /** X11: Performs mapping of the passed @a pWidget to host-screen with passed @a uScreenId. */
+ SHARED_LIBRARY_STUFF bool X11SetFullScreenMonitor(QWidget *pWidget, ulong uScreenId);
+
+ /** X11: Sets _NET_WM_STATE_SKIP_TASKBAR flag for passed @a pWidget. */
+ SHARED_LIBRARY_STUFF void X11SetSkipTaskBarFlag(QWidget *pWidget);
+ /** X11: Sets _NET_WM_STATE_SKIP_PAGER flag for passed @a pWidget. */
+ SHARED_LIBRARY_STUFF void X11SetSkipPagerFlag(QWidget *pWidget);
+
+ /** X11: Assigns WM_CLASS property for passed @a pWidget. */
+ SHARED_LIBRARY_STUFF void X11SetWMClass(QWidget *pWidget, const QString &strNameString, const QString &strClassString);
+
+ /** X11: Tell the WM we are well behaved wrt Xwayland keyboard-grabs. This will
+ * make the WM turn our grab into a Wayland shortcut inhibition request,
+ * so that e.g. alt+tab will get send to the VM instead of moving the
+ * focus away from the VM. */
+ SHARED_LIBRARY_STUFF void X11SetXwaylandMayGrabKeyboardFlag(QWidget *pWidget);
+
+ /** X11: Gets the X11 display pointer. */
+ SHARED_LIBRARY_STUFF struct _XDisplay *X11GetDisplay();
+ /** X11: Gets the X11 connection. */
+ SHARED_LIBRARY_STUFF struct xcb_connection_t *X11GetConnection();
+ /** X11: Gets the X11 root (desktop) window. */
+ SHARED_LIBRARY_STUFF uint32_t X11GetAppRootWindow();
+}
+
+#endif /* !FEQT_INCLUDED_SRC_platform_x11_VBoxUtils_x11_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard-new.cpp b/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard-new.cpp
new file mode 100644
index 00000000..59e183bf
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard-new.cpp
@@ -0,0 +1,289 @@
+/* $Id: XKeyboard-new.cpp $ */
+/** @file
+ * VBox Qt GUI - Implementation of Linux-specific keyboard functions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Define GUI log group.
+ * This define should go *before* VBox/log.h include: */
+#define LOG_GROUP LOG_GROUP_GUI
+
+/* Qt includes: */
+#include <QString>
+#include <QStringList>
+
+/* Other VBox includes: */
+#include <VBox/log.h>
+
+/* GUI includes: */
+#include "XKeyboard.h"
+
+/* Other VBox includes: */
+#include "VBox/VBoxKeyboard.h"
+
+/* External includes: */
+#include <X11/XKBlib.h>
+#include <X11/keysym.h>
+
+
+/* VBoxKeyboard uses the deprecated XKeycodeToKeysym(3) API, but uses it safely. */
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+static unsigned gfByLayoutOK = 1;
+static unsigned gfByTypeOK = 1;
+static unsigned gfByXkbOK = 1;
+
+/** Prints a key to the release log in the format needed for the Wine layout tables. */
+static void printKey(Display *pDisplay, int cKeys)
+{
+ bool fWasEscape = false;
+
+ for (int i = 0; i < 2; ++i)
+ {
+ int iKeySym = XKeycodeToKeysym(pDisplay, cKeys, i);
+
+ int iValue = iKeySym & 0xff;
+ if ('\\' == iValue)
+ {
+ LogRel(("\\\\"));
+ }
+ else if ('"' == iValue)
+ {
+ LogRel(("\\\""));
+ }
+ else if ((iValue > 32) && (iValue < 127))
+ {
+ if ( fWasEscape
+ && ( ((iValue >= '0') && (iValue <= '9'))
+ || ((iValue >= 'A') && (iValue <= 'F'))
+ || ((iValue >= 'a') && (iValue <= 'f'))))
+ {
+ LogRel(("\"\""));
+ }
+ LogRel(("%c", (char)iValue));
+ }
+ else
+ {
+ LogRel(("\\x%x", iValue));
+ fWasEscape = true;
+ }
+ }
+}
+
+/** Dumps the keyboard layout to the release log. */
+static void dumpLayout(Display *pDisplay)
+{
+ LogRel(("Your keyboard layout does not appear to be fully supported by\n"
+ "VirtualBox. If you are experiencing keyboard problems this.\n"
+ "information may help us to resolve them.\n"
+ "(Note: please tell us if you are using a custom layout.)\n\n"
+ "The correct table for your layout is:\n"));
+ /* First, build up a table of scan-to-key code mappings */
+ unsigned scanToKeycode[512] = { 0 };
+ int iMinKey, iMaxKey;
+ XDisplayKeycodes(pDisplay, &iMinKey, &iMaxKey);
+ for (int i = iMinKey; i < iMaxKey; ++i)
+ scanToKeycode[X11DRV_KeyEvent(pDisplay, i)] = i;
+ LogRel(("\""));
+ printKey(pDisplay, scanToKeycode[0x29]); /* `~ */
+ for (int i = 2; i <= 0xd; ++i) /* 1! - =+ */
+ {
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[i]);
+ }
+ LogRel(("\",\n"));
+ LogRel(("\""));
+ printKey(pDisplay, scanToKeycode[0x10]); /* qQ */
+ for (int i = 0x11; i <= 0x1b; ++i) /* wW - ]} */
+ {
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[i]);
+ }
+ LogRel(("\",\n"));
+ LogRel(("\""));
+ printKey(pDisplay, scanToKeycode[0x1e]); /* aA */
+ for (int i = 0x1f; i <= 0x28; ++i) /* sS - '" */
+ {
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[i]);
+ }
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[0x2b]); /* \| */
+ LogRel(("\",\n"));
+ LogRel(("\""));
+ printKey(pDisplay, scanToKeycode[0x2c]); /* zZ */
+ for (int i = 0x2d; i <= 0x35; ++i) /* xX - /? */
+ {
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[i]);
+ }
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[0x56]); /* The 102nd key */
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[0x73]); /* The Brazilian key */
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[0x7d]); /* The Yen key */
+ LogRel(("\"\n\n"));
+}
+
+/** Dumps the keyboard type tables to the release log. */
+static void dumpType(Display *pDisplay)
+{
+ LogRel(("Your keyboard type does not appear to be known to VirtualBox. If\n"
+ "you are experiencing keyboard problems this information may help us\n"
+ "to resolve them. Please also provide information about what type\n"
+ "of keyboard you have and whether you are using a remote X server or\n"
+ "something similar.\n\n"
+ "The tables for your keyboard are:\n"));
+ for (unsigned i = 0; i < 256; ++i)
+ {
+ LogRel(("0x%x", X11DRV_KeyEvent(pDisplay, i)));
+ if (i < 255)
+ LogRel((", "));
+ if (15 == (i % 16))
+ LogRel(("\n"));
+ }
+ LogRel(("and\n"));
+ LogRel(("NULL, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x,\n0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ XKeysymToKeycode(pDisplay, XK_Control_L),
+ XKeysymToKeycode(pDisplay, XK_Shift_L),
+ XKeysymToKeycode(pDisplay, XK_Caps_Lock),
+ XKeysymToKeycode(pDisplay, XK_Tab),
+ XKeysymToKeycode(pDisplay, XK_Escape),
+ XKeysymToKeycode(pDisplay, XK_Return),
+ XKeysymToKeycode(pDisplay, XK_Up),
+ XKeysymToKeycode(pDisplay, XK_Down),
+ XKeysymToKeycode(pDisplay, XK_Left),
+ XKeysymToKeycode(pDisplay, XK_Right),
+ XKeysymToKeycode(pDisplay, XK_F1),
+ XKeysymToKeycode(pDisplay, XK_F2),
+ XKeysymToKeycode(pDisplay, XK_F3),
+ XKeysymToKeycode(pDisplay, XK_F4),
+ XKeysymToKeycode(pDisplay, XK_F5),
+ XKeysymToKeycode(pDisplay, XK_F6),
+ XKeysymToKeycode(pDisplay, XK_F7),
+ XKeysymToKeycode(pDisplay, XK_F8)));
+}
+
+/** Builds a table mapping the X server's scan codes to PC keyboard scan codes.
+ * The logic of the function is that while the X server may be using a different
+ * set of scan codes (if for example it is running on a non-PC machine), the
+ * keyboard layout should be similar to a PC layout. So we look at the symbols
+ * attached to each key on the X server, find the PC layout which is closest to
+ * it and remember the mappings. */
+bool initXKeyboard(Display *pDisplay, int (*remapScancodes)[2])
+{
+ X11DRV_InitKeyboard(pDisplay, &gfByLayoutOK, &gfByTypeOK, &gfByXkbOK, remapScancodes);
+
+ /* It will almost always work to some extent.. */
+ return true;
+}
+
+void initMappedX11Keyboard(Display *pDisplay, const QString &remapScancodes)
+{
+ /* Initialize X11 keyboard including the remapping specified in the
+ * global property GUI/RemapScancodes. This property is a string of
+ * comma-separated x=y pairs, where x is the X11 keycode and y is the
+ * keyboard scancode that is emitted when the key attached to the X11
+ * keycode is pressed. */
+
+ int (*scancodes)[2] = NULL;
+ int (*scancodesTail)[2] = NULL;
+
+ if (remapScancodes != QString())
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList tuples = remapScancodes.split(",", Qt::SkipEmptyParts);
+#else
+ QStringList tuples = remapScancodes.split(",", QString::SkipEmptyParts);
+#endif
+ scancodes = scancodesTail = new int [tuples.size()+1][2];
+ for (int i = 0; i < tuples.size(); ++i)
+ {
+ QStringList keyc2scan = tuples.at(i).split("=");
+ (*scancodesTail)[0] = keyc2scan.at(0).toUInt();
+ (*scancodesTail)[1] = keyc2scan.at(1).toUInt();
+ /* Do not advance on (ignore) identity mappings as this
+ * is the stop signal to initXKeyboard and friends: */
+ if ((*scancodesTail)[0] != (*scancodesTail)[1])
+ ++scancodesTail;
+ }
+ (*scancodesTail)[0] = (*scancodesTail)[1] = 0;
+ }
+
+ /* Initialize the X keyboard subsystem: */
+ initXKeyboard(pDisplay, scancodes);
+
+ if (scancodes)
+ delete scancodes;
+}
+
+unsigned handleXKeyEvent(Display *pDisplay, unsigned int iDetail)
+{
+ /* Call the WINE event handler: */
+ unsigned iKey = X11DRV_KeyEvent(pDisplay, iDetail);
+ LogRel3(("VBoxKeyboard: converting keycode %d to scancode %s0x%x\n",
+ iDetail, iKey > 0x100 ? "0xe0 " : "", iKey & 0xff));
+ return iKey;
+}
+
+void doXKeyboardLogging(Display *pDisplay)
+{
+ if (((1 == gfByTypeOK) || (1 == gfByXkbOK)) && (gfByLayoutOK != 1))
+ dumpLayout(pDisplay);
+ if (((1 == gfByLayoutOK) || (1 == gfByXkbOK)) && (gfByTypeOK != 1))
+ dumpType(pDisplay);
+ if ((gfByLayoutOK != 1) && (gfByTypeOK != 1) && (gfByXkbOK != 1))
+ {
+ LogRel(("Failed to recognize the keyboard mapping or to guess it based on\n"
+ "the keyboard layout. It is very likely that some keys will not\n"
+ "work correctly in the guest. If this is the case, please submit\n"
+ "a bug report, giving us information about your keyboard type,\n"
+ "its layout and other relevant information such as whether you\n"
+ "are using a remote X server or something similar. \n"));
+ unsigned *keyc2scan = X11DRV_getKeyc2scan();
+
+ LogRel(("The keycode-to-scancode table is: %d=%d",0,keyc2scan[0]));
+ for (int i = 1; i < 256; i++)
+ LogRel((",%d=%d",i,keyc2scan[i]));
+ LogRel(("\n"));
+ }
+ LogRel(("X Server details: vendor: %s, release: %d, protocol version: %d.%d, display string: %s\n",
+ ServerVendor(pDisplay), VendorRelease(pDisplay), ProtocolVersion(pDisplay),
+ ProtocolRevision(pDisplay), DisplayString(pDisplay)));
+ LogRel(("Using %s for keycode to scan code conversion\n",
+ gfByXkbOK ? "XKB"
+ : gfByTypeOK ? "known keycode mapping"
+ : "host keyboard layout detection"));
+}
+
+unsigned long wrapXkbKeycodeToKeysym(Display *pDisplay, unsigned char cCode,
+ unsigned int cGroup, unsigned int cIndex)
+{
+ KeySym cSym = XkbKeycodeToKeysym(pDisplay, cCode, cGroup, cIndex);
+ if (cSym != NoSymbol)
+ return cSym;
+ return XKeycodeToKeysym(pDisplay, cCode, cGroup * 2 + cIndex % 2);
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard.h b/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard.h
new file mode 100644
index 00000000..9e394f17
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard.h
@@ -0,0 +1,55 @@
+/* $Id: XKeyboard.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of Linux-specific keyboard functions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_x11_XKeyboard_h
+#define FEQT_INCLUDED_SRC_platform_x11_XKeyboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QString;
+typedef struct _XDisplay Display;
+
+/** Initializes the X keyboard subsystem. */
+SHARED_LIBRARY_STUFF void initMappedX11Keyboard(Display *pDisplay, const QString &remapScancodes);
+
+/** Handles native XKey events. */
+SHARED_LIBRARY_STUFF unsigned handleXKeyEvent(Display *pDisplay, unsigned int iDetail);
+
+/** Handles log requests from initXKeyboard after release logging is started. */
+SHARED_LIBRARY_STUFF void doXKeyboardLogging(Display *pDisplay);
+
+/** Wraps for the XkbKeycodeToKeysym(3) API which falls back to the deprecated XKeycodeToKeysym(3) if it is unavailable. */
+SHARED_LIBRARY_STUFF unsigned long wrapXkbKeycodeToKeysym(Display *pDisplay, unsigned char cCode,
+ unsigned int cGroup, unsigned int cIndex);
+
+#endif /* !FEQT_INCLUDED_SRC_platform_x11_XKeyboard_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/precomp_gcc.h b/src/VBox/Frontends/VirtualBox/src/precomp_gcc.h
new file mode 100644
index 00000000..a5a615ef
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/precomp_gcc.h
@@ -0,0 +1,181 @@
+/* $Id: precomp_gcc.h $*/
+/** @file
+ * VBox Qt GUI - Precompiled header for Visual C++.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*
+ * General pickings
+ *
+ * Note! We do not include iprt/log.h or similar because we need to
+ * support selecting different log groups. So, for now we restrict
+ * ourselves to frequently used QT, compiler, and system headers.
+ */
+
+/* Some CRT stuff that's frequently used. */
+#include <new>
+#include <stdlib.h>
+
+/* The most frequently used qt headers on a linux hosts based on dependency files. */
+#include <QtCore/qobject.h> /* 1003 */
+#include <QtCore/qmetatype.h> /* 958 */
+#include <QtCore/qstring.h> /* 923 */
+#include <QtCore/qstringlist.h> /* 905 */
+#include <QtCore/qrect.h> /* 858 */
+#include <QtCore/qvector.h> /* 853 */
+#include <QtCore/qmap.h> /* 849 */
+#include <QtCore/qcoreevent.h> /* 838 */
+#include <QtWidgets/qwidget.h> /* 826 */
+#include <QtCore/qpair.h> /* 698 */
+#include <QtCore/qlist.h> /* 682 */
+#include <QtGui/qpixmap.h> /* 670 */
+#include <QtCore/qhash.h> /* 637 */
+#include <QtCore/qsize.h> /* 627 */
+#include <QtCore/qglobal.h> /* 591 */
+#include <QtCore/qvariant.h> /* 588 */
+#include <QtCore/qregexp.h> /* 587 */
+#include <QtCore/qversiontagging.h> /* 585 */
+#include <QtCore/qtypeinfo.h> /* 585 */
+#include <QtCore/qtcore-config.h> /* 585 */
+#include <QtCore/qsystemdetection.h> /* 585 */
+#include <QtCore/qsysinfo.h> /* 585 */
+#include <QtCore/qstringview.h> /* 585 */
+#include <QtCore/qstringliteral.h> /* 585 */
+#include <QtCore/qstringalgorithms.h> /* 585 */
+#include <QtCore/qrefcount.h> /* 585 */
+#include <QtCore/qprocessordetection.h> /* 585 */
+#include <QtCore/qnumeric.h> /* 585 */
+#include <QtCore/qnamespace.h> /* 585 */
+#include <QtCore/qlogging.h> /* 585 */
+#include <QtCore/qglobalstatic.h> /* 585 */
+#include <QtCore/qgenericatomic.h> /* 585 */
+#include <QtCore/qflags.h> /* 585 */
+#include <QtCore/qconfig.h> /* 585 */
+#include <QtCore/qcompilerdetection.h> /* 585 */
+#include <QtCore/qchar.h> /* 585 */
+#include <QtCore/qbytearray.h> /* 585 */
+#include <QtCore/qbasicatomic.h> /* 585 */
+#include <QtCore/qatomic_cxx11.h> /* 585 */
+#include <QtCore/qatomic.h> /* 585 */
+#include <QtCore/qarraydata.h> /* 585 */
+#include <QtCore/qstringmatcher.h> /* 584 */
+#include <QtCore/qiterator.h> /* 584 */
+#include <QtCore/qhashfunctions.h> /* 584 */
+#include <QtCore/qbytearraylist.h> /* 584 */
+#include <QtCore/qalgorithms.h> /* 584 */
+#include <QtCore/qvarlengtharray.h> /* 580 */
+#include <QtCore/qobjectdefs.h> /* 580 */
+#include <QtCore/qcontainerfwd.h> /* 580 */
+#include <QtCore/qscopedpointer.h> /* 577 */
+#include <QtGui/qcolor.h> /* 574 */
+#include <QtCore/qpoint.h> /* 549 */
+#include <QtGui/qtransform.h> /* 546 */
+#include <QtCore/qmargins.h> /* 544 */
+#include <QtCore/qshareddata.h> /* 536 */
+#include <QtGui/qimage.h> /* 532 */
+#include <QtCore/qsharedpointer.h> /* 532 */
+#include <QtGui/qkeysequence.h> /* 531 */
+#include <QtGui/qcursor.h> /* 530 */
+#include <QtCore/qiodevice.h> /* 530 */
+#include <QtGui/qtguiglobal.h> /* 528 */
+#include <QtGui/qtgui-config.h> /* 528 */
+#include <QtGui/qwindowdefs.h> /* 527 */
+#include <QtGui/qregion.h> /* 527 */
+#include <QtGui/qrgba64.h> /* 526 */
+#include <QtGui/qrgb.h> /* 526 */
+#include <QtGui/qpolygon.h> /* 526 */
+#include <QtGui/qpixelformat.h> /* 526 */
+#include <QtGui/qpainterpath.h> /* 526 */
+#include <QtGui/qpaintdevice.h> /* 526 */
+#include <QtGui/qmatrix.h> /* 526 */
+#include <QtCore/qline.h> /* 526 */
+#include <QtCore/qdatastream.h> /* 526 */
+#include <QtWidgets/qtwidgetsglobal.h> /* 520 */
+#include <QtWidgets/qtwidgets-config.h> /* 520 */
+#include <QtGui/qfont.h> /* 507 */
+#include <QtGui/qbrush.h> /* 507 */
+#include <QtGui/qpalette.h> /* 506 */
+#include <QtWidgets/qsizepolicy.h> /* 505 */
+#include <QtGui/qfontmetrics.h> /* 497 */
+#include <QtGui/qfontinfo.h> /* 496 */
+
+/* Toplevel headers for which we already include the sub-headers.
+ Note! Must exist in precomptricks so we can apply #pragma once to them (good for GCC). */
+#include <QVariant>
+#include <QVarLengthArray>
+#include <QMutex>
+#include <QSysInfo>
+#include <QString>
+#include <QStringList>
+#include <QChar>
+#include <QApplication>
+#include <QGroupBox>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QObject>
+#include <QVector>
+#include <QMap>
+#include <QMetaType>
+#include <QRect>
+#include <QWidget>
+#include <QPixmap>
+
+/* misc others that we include a bit. */
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QPainter>
+#include <QTimer>
+#include <QStyle>
+#include <QMenu>
+#include <QDir>
+#include <QUuid>
+#include <QLineEdit>
+
+/* cdefs.h is a little bit of a question since it defines RT_STRICT, which someone
+ may want to redefine locally, but it's required by all other IPRT VBox includes. */
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/stdarg.h>
+#include <iprt/stdint.h>
+#include <iprt/assert.h>
+#include <iprt/assertcompile.h>
+#include <iprt/errcore.h>
+#include <iprt/cpp/list.h>
+#include <iprt/cpp/meta.h>
+#include <VBox/com/array.h>
+#include <VBox/com/assert.h>
+#include <VBox/com/ptr.h>
+#include <VBox/com/com.h>
+#include <VBox/com/defs.h>
+
+/* These two are freuqently used internal headers. */
+#include "UILibraryDefs.h"
+/*#include "QIWithRestorableGeometry.h" - broken as it includes iprt/log.h thru UIDefs.h via UICommon.h. */
+/*#include "QIWithRetranslateUI.h" - broken as it includes iprt/log.h thru UIDefs.h via UITranslator.h. */
+
+#if defined(Log) || defined(LogIsEnabled)
+# error "Log() from iprt/log.h cannot be defined in the precompiled header!"
+#endif
+
diff --git a/src/VBox/Frontends/VirtualBox/src/precomp_vcc.h b/src/VBox/Frontends/VirtualBox/src/precomp_vcc.h
new file mode 100644
index 00000000..059eb73b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/precomp_vcc.h
@@ -0,0 +1,171 @@
+/* $Id: precomp_vcc.h $*/
+/** @file
+ * VBox Qt GUI - Precompiled header for Visual C++.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*
+ * General pickings
+ *
+ * Note! We do not include iprt/log.h or similar because we need to
+ * support selecting different log groups. So, for now we restrict
+ * ourselves to frequently used QT, compiler, and SDK headers.
+ */
+#include <QVariant>
+#include <QVarLengthArray>
+#include <QMutex>
+#include <QSysInfo>
+#include <QString>
+#include <QChar>
+
+#include <QApplication>
+
+#include <QGroupBox>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QVBoxLayout>
+
+/* The most frequently used qt headers on windows hosts based on dependency files. */
+#include <qalgorithms.h>
+#include <qarraydata.h>
+#include <qatomic.h>
+#if _MSC_VER < 1910 /* Conflicts with qatomic_cxx11.h which is dragged in above somewhere. */
+# include <qatomic_msvc.h>
+#endif
+#include <qbasicatomic.h>
+#include <qbytearray.h>
+#include <qchar.h>
+#include <qcompilerdetection.h>
+#include <qconfig.h>
+#include <qcontainerfwd.h>
+#if QT_VERSION < QT_VERSION_CHECK(5, 8, 0)
+# include <qfeatures.h>
+#endif
+#include <qflags.h>
+#include <qgenericatomic.h>
+#include <qglobalstatic.h>
+#include <qisenum.h>
+#include <qlogging.h>
+#include <qmutex.h>
+#include <qnamespace.h>
+#include <qnumeric.h>
+#include <qobjectdefs.h>
+#include <qprocessordetection.h>
+#include <qrefcount.h>
+#include <qstring.h>
+#include <qsysinfo.h>
+#include <qsystemdetection.h>
+#include <qtypeinfo.h>
+#include <qvarlengtharray.h>
+#include <qpair.h>
+#include <qmetatype.h>
+#include <qobject.h>
+#include <qscopedpointer.h>
+#include <qglobal.h>
+#include <qbytearraylist.h>
+#include <qiterator.h>
+#include <qlist.h>
+#include <qregexp.h>
+#include <qstringlist.h>
+#include <qstringmatcher.h>
+#include <qtypetraits.h>
+
+/* Less frequently included: */
+#include <QtWidgets/QGraphicsWidget>
+#include <QtWidgets/qgraphicsitem.h>
+#include <QtWidgets/qgraphicslayoutitem.h>
+#include <QtWidgets/qgraphicswidget.h>
+#include <QtCore/QMetaType>
+#include <QtGui/qevent.h>
+#include <QtGui/qtouchdevice.h>
+#include <QtGui/qvector2d.h>
+#include <QtCore/QEvent>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qinputmethod.h>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/qapplication.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qeventloop.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qset.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfiledevice.h>
+#include <QtCore/qlocale.h>
+#include <QtCore/qcoreevent.h>
+#include <QtCore/QObject>
+#include <QtWidgets/qwidget.h>
+#include <QtCore/qvariant.h>
+#include <QtGui/qfontinfo.h>
+#include <QtGui/qfontmetrics.h>
+#include <QtGui/qcursor.h>
+#include <QtWidgets/qsizepolicy.h>
+#include <QtGui/qkeysequence.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qfont.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qline.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qpaintdevice.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/qpixelformat.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qpolygon.h>
+#include <QtGui/qrgb.h>
+#include <QtGui/qtransform.h>
+#include <QtCore/qdatastream.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtGui/qwindowdefs_win.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qmargins.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qhash.h>
+
+/* cdefs.h is a little bit of a question since it defines RT_STRICT, which
+ someone may want to redefine locally. But we need it for windows.h. */
+#include <iprt/cdefs.h>
+#include <iprt/win/windows.h>
+#include <iprt/types.h>
+#include <iprt/cpp/list.h>
+#include <iprt/cpp/meta.h>
+#include <iprt/cpp/ministring.h>
+#include <VBox/com/microatl.h>
+#include <VBox/com/com.h>
+#include <VBox/com/array.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/string.h>
+
+#if defined(Log) || defined(LogIsEnabled)
+# error "Log() from iprt/log.h cannot be defined in the precompiled header!"
+#endif
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/runtime/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIBootFailureDialog.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIBootFailureDialog.cpp
new file mode 100644
index 00000000..d5b4872b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIBootFailureDialog.cpp
@@ -0,0 +1,268 @@
+/* $Id: UIBootFailureDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIBootTimeErrorDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QCheckBox>
+#include <QHeaderView>
+#include <QLabel>
+#include <QMenuBar>
+#include <QVBoxLayout>
+#include <QPushButton>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIToolButton.h"
+#include "QIRichTextLabel.h"
+#include "UIBootFailureDialog.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIFilePathSelector.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+
+/* COM includes: */
+#include "CMediumAttachment.h"
+#include "CStorageController.h"
+
+UIBootFailureDialog::UIBootFailureDialog(QWidget *pParent, const CMachine &comMachine)
+ :QIWithRetranslateUI<QIMainDialog>(pParent)
+ , m_pParent(pParent)
+ , m_pCentralWidget(0)
+ , m_pMainLayout(0)
+ , m_pButtonBox(0)
+ , m_pCloseButton(0)
+ , m_pResetButton(0)
+ , m_pLabel(0)
+ , m_pBootImageSelector(0)
+ , m_pBootImageLabel(0)
+ , m_pIconLabel(0)
+ , m_pSuppressDialogCheckBox(0)
+ , m_comMachine(comMachine)
+{
+ configure();
+}
+
+UIBootFailureDialog::~UIBootFailureDialog()
+{
+ if (m_pSuppressDialogCheckBox && m_pSuppressDialogCheckBox->isChecked())
+ {
+ QStringList suppressedMessageList = gEDataManager->suppressedMessages();
+ suppressedMessageList << gpConverter->toInternalString(UIExtraDataMetaDefs::DialogType_BootFailure);
+ gEDataManager->setSuppressedMessages(suppressedMessageList);
+ }
+}
+
+QString UIBootFailureDialog::bootMediumPath() const
+{
+ if (!m_pBootImageSelector)
+ return QString();
+ return m_pBootImageSelector->path();
+}
+
+void UIBootFailureDialog::retranslateUi()
+{
+ if (m_pCloseButton)
+ {
+ m_pCloseButton->setText(tr("&Cancel"));
+ m_pCloseButton->setToolTip(tr("Closes this dialog without resetting the guest or mounting a medium"));
+ }
+ if (m_pResetButton)
+ {
+ m_pResetButton->setText(tr("&Mount and Retry Boot"));
+ m_pResetButton->setToolTip(tr("Mounts the selected ISO if any and reboots the vm"));
+ }
+
+ if (m_pLabel)
+ m_pLabel->setText(tr("The virtual machine failed to boot. That might be caused by a missing operating system "
+ "or misconfigured boot order. Mounting an operating system install DVD might solve this problem. "
+ "Selecting an ISO file will attempt to mount it after the dialog is closed."));
+
+ if (m_pBootImageLabel)
+ m_pBootImageLabel->setText(tr("DVD:"));
+ if (m_pSuppressDialogCheckBox)
+ {
+ m_pSuppressDialogCheckBox->setText(tr("Do not show this dialog again"));
+ m_pSuppressDialogCheckBox->setToolTip(tr("When checked this dialog will not be shown again."));
+ }
+ if (m_pBootImageSelector)
+ m_pBootImageSelector->setToolTip(tr("Holds the path of the ISO to be attached to machine as boot medium."));
+
+}
+
+void UIBootFailureDialog::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/media_manager_32px.png", ":/media_manager_16px.png"));
+#endif
+
+ setTitle();
+ prepareWidgets();
+ prepareConnections();
+}
+
+void UIBootFailureDialog::prepareConnections()
+{
+ if (m_pCloseButton)
+ connect(m_pCloseButton, &QPushButton::clicked, this, &UIBootFailureDialog::sltCancel);
+ if (m_pResetButton)
+ connect(m_pResetButton, &QPushButton::clicked, this, &UIBootFailureDialog::sltReset);
+}
+
+void UIBootFailureDialog::prepareWidgets()
+{
+ m_pCentralWidget = new QWidget;
+ if (!m_pCentralWidget)
+ return;
+ setCentralWidget(m_pCentralWidget);
+
+ m_pMainLayout = new QVBoxLayout;
+ m_pCentralWidget->setLayout(m_pMainLayout);
+
+ if (!m_pMainLayout || !menuBar())
+ return;
+
+ QHBoxLayout *pTopLayout = new QHBoxLayout;
+ pTopLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pIconLabel = new QLabel;
+ if (m_pIconLabel)
+ {
+ m_pIconLabel->setPixmap(iconPixmap());
+ m_pIconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ pTopLayout->addWidget(m_pIconLabel, Qt::AlignTop | Qt::AlignCenter);
+ }
+
+ m_pLabel = new QIRichTextLabel;
+ if (m_pLabel)
+ pTopLayout->addWidget(m_pLabel);
+
+ QHBoxLayout *pSelectorLayout = new QHBoxLayout;
+ pSelectorLayout->setContentsMargins(0, 0, 0, 0);
+ m_pBootImageLabel = new QLabel;
+
+ if (m_pBootImageLabel)
+ {
+ pSelectorLayout->addWidget(m_pBootImageLabel);
+ m_pBootImageLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
+
+ }
+
+ m_pBootImageSelector = new UIFilePathSelector;
+ if (m_pBootImageSelector)
+ {
+ m_pBootImageSelector->setMode(UIFilePathSelector::Mode_File_Open);
+ m_pBootImageSelector->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ m_pBootImageSelector->setFileDialogFilters("ISO Images(*.iso *.ISO)");
+ m_pBootImageSelector->setResetEnabled(false);
+ m_pBootImageSelector->setInitialPath(uiCommon().defaultFolderPathForType(UIMediumDeviceType_DVD));
+ m_pBootImageSelector->setRecentMediaListType(UIMediumDeviceType_DVD);
+ if (m_pBootImageLabel)
+ m_pBootImageLabel->setBuddy(m_pBootImageSelector);
+ pSelectorLayout->addWidget(m_pBootImageSelector);
+ connect(m_pBootImageSelector, &UIFilePathSelector::pathChanged,
+ this, &UIBootFailureDialog::sltFileSelectorPathChanged);
+ }
+
+ m_pMainLayout->addLayout(pTopLayout);
+ m_pMainLayout->addLayout(pSelectorLayout);
+
+ m_pSuppressDialogCheckBox = new QCheckBox;
+ if (m_pSuppressDialogCheckBox)
+ m_pMainLayout->addWidget(m_pSuppressDialogCheckBox);
+
+ m_pButtonBox = new QIDialogButtonBox;
+ if (m_pButtonBox)
+ {
+ m_pCloseButton = m_pButtonBox->addButton(QString(), QDialogButtonBox::RejectRole);
+ m_pResetButton = m_pButtonBox->addButton(QString(), QDialogButtonBox::ActionRole);
+ m_pCloseButton->setShortcut(Qt::Key_Escape);
+
+ m_pMainLayout->addWidget(m_pButtonBox);
+ }
+
+ m_pMainLayout->addStretch();
+ retranslateUi();
+}
+
+void UIBootFailureDialog::sltCancel()
+{
+ done(static_cast<int>(ReturnCode_Close));
+}
+
+void UIBootFailureDialog::sltReset()
+{
+ done(static_cast<int>(ReturnCode_Reset));
+}
+
+void UIBootFailureDialog::showEvent(QShowEvent *pEvent)
+{
+ if (m_pParent)
+ gpDesktop->centerWidget(this, m_pParent, false);
+ QIWithRetranslateUI<QIMainDialog>::showEvent(pEvent);
+
+}
+
+void UIBootFailureDialog::setTitle()
+{
+}
+
+void UIBootFailureDialog::sltFileSelectorPathChanged(const QString &strPath)
+{
+ Q_UNUSED(strPath);
+ bool fISOValid = checkISOImage();
+ if (m_pBootImageSelector)
+ {
+ m_pBootImageSelector->mark(!fISOValid, tr("The selected path is invalid."));
+ }
+ if (m_pResetButton)
+ m_pResetButton->setEnabled(fISOValid);
+}
+
+QPixmap UIBootFailureDialog::iconPixmap()
+{
+ QIcon icon = UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_MessageBoxWarning);
+ if (icon.isNull())
+ return QPixmap();
+ int iSize = QApplication::style()->pixelMetric(QStyle::PM_MessageBoxIconSize, 0, 0);
+ return icon.pixmap(iSize, iSize);
+}
+
+bool UIBootFailureDialog::checkISOImage() const
+{
+ AssertReturn(m_pBootImageSelector, true);
+ if (m_pBootImageSelector->path().isEmpty())
+ return true;
+ QFileInfo fileInfo(m_pBootImageSelector->path());
+ if (!fileInfo.exists() || !fileInfo.isReadable())
+ return false;
+ return true;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIBootFailureDialog.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIBootFailureDialog.h
new file mode 100644
index 00000000..3627d6f5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIBootFailureDialog.h
@@ -0,0 +1,120 @@
+/* $Id: UIBootFailureDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIBootFailureDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIBootFailureDialog_h
+#define FEQT_INCLUDED_SRC_runtime_UIBootFailureDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIMainDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIMedium.h"
+#include "UIMediumDefs.h"
+
+
+/* Forward declarations: */
+class QCheckBox;
+class QLabel;
+class QVBoxLayout;
+class QIDialogButtonBox;
+class QIRichTextLabel;
+class UIFilePathSelector;
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+
+/** QIDialog extension providing GUI with a dialog to select an existing medium. */
+class UIBootFailureDialog : public QIWithRetranslateUI<QIMainDialog>
+{
+
+ Q_OBJECT;
+
+signals:
+
+public:
+
+ enum ReturnCode
+ {
+ ReturnCode_Close = 0,
+ ReturnCode_Reset,
+ ReturnCode_Max
+ };
+
+ UIBootFailureDialog(QWidget *pParent, const CMachine &comMachine);
+ ~UIBootFailureDialog();
+ QString bootMediumPath() const;
+
+protected:
+
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ void sltCancel();
+ void sltReset();
+ void sltFileSelectorPathChanged(const QString &strPath);
+
+private:
+
+ QPixmap iconPixmap();
+ /* Checks if selected iso exists and readable. Returns false if not. Returns true if nothing is selected. */
+ bool checkISOImage() const;
+
+ /** @name Event-handling stuff.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** @} */
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Configures all. */
+ void configure();
+ void prepareWidgets();
+ void prepareConnections();
+ /** @} */
+
+ void setTitle();
+ QWidget *m_pParent;
+
+ QWidget *m_pCentralWidget;
+ QVBoxLayout *m_pMainLayout;
+ QIDialogButtonBox *m_pButtonBox;
+ QPushButton *m_pCloseButton;
+ QPushButton *m_pResetButton;
+ QIRichTextLabel *m_pLabel;
+ UIFilePathSelector *m_pBootImageSelector;
+ QLabel *m_pBootImageLabel;
+ QLabel *m_pIconLabel;
+ QCheckBox *m_pSuppressDialogCheckBox;
+ CMachine m_comMachine;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIBootFailureDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIConsoleEventHandler.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIConsoleEventHandler.cpp
new file mode 100644
index 00000000..10571889
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIConsoleEventHandler.cpp
@@ -0,0 +1,449 @@
+/* $Id: UIConsoleEventHandler.cpp $ */
+/** @file
+ * VBox Qt GUI - UIConsoleEventHandler class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConsoleEventHandler.h"
+#include "UIExtraDataManager.h"
+#include "UIMainEventListener.h"
+#include "UIMousePointerShapeData.h"
+#include "UISession.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils.h"
+#endif
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CConsole.h"
+#include "CEventListener.h"
+#include "CEventSource.h"
+
+
+/** Private QObject extension
+ * providing UIConsoleEventHandler with the CConsole event-source. */
+class UIConsoleEventHandlerProxy : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about mouse pointer @a shapeData change. */
+ void sigMousePointerShapeChange(const UIMousePointerShapeData &shapeData);
+ /** Notifies about mouse capability change to @a fSupportsAbsolute, @a fSupportsRelative,
+ * @a fSupportsTouchScreen, @a fSupportsTouchPad, and @a fNeedsHostCursor. */
+ void sigMouseCapabilityChange(bool fSupportsAbsolute, bool fSupportsRelative,
+ bool fSupportsTouchScreen, bool fSupportsTouchPad,
+ bool fNeedsHostCursor);
+ /** Notifies about guest request to change the cursor position to @a uX * @a uY.
+ * @param fContainsData Brings whether the @a uX and @a uY values are valid and could be used by the GUI now. */
+ void sigCursorPositionChange(bool fContainsData, unsigned long uX, unsigned long uY);
+ /** Notifies about keyboard LEDs change for @a fNumLock, @a fCapsLock and @a fScrollLock. */
+ void sigKeyboardLedsChangeEvent(bool fNumLock, bool fCapsLock, bool fScrollLock);
+ /** Notifies about machine @a state change. */
+ void sigStateChange(KMachineState state);
+ /** Notifies about guest additions state change. */
+ void sigAdditionsChange();
+ /** Notifies about network @a adapter state change. */
+ void sigNetworkAdapterChange(CNetworkAdapter adapter);
+ /** Notifies about storage device change for @a attachment, which was @a fRemoved and it was @a fSilent for guest. */
+ void sigStorageDeviceChange(CMediumAttachment attachment, bool fRemoved, bool fSilent);
+ /** Notifies about storage medium @a attachment state change. */
+ void sigMediumChange(CMediumAttachment attachment);
+ /** Notifies about VRDE device state change. */
+ void sigVRDEChange();
+ /** Notifies about recording state change. */
+ void sigRecordingChange();
+ /** Notifies about USB controller state change. */
+ void sigUSBControllerChange();
+ /** Notifies about USB @a device state change to @a fAttached, holding additional @a error information. */
+ void sigUSBDeviceStateChange(CUSBDevice device, bool fAttached, CVirtualBoxErrorInfo error);
+ /** Notifies about shared folder state change. */
+ void sigSharedFolderChange();
+ /** Notifies about CPU execution-cap change. */
+ void sigCPUExecutionCapChange();
+ /** Notifies about guest-screen configuration change of @a type for @a uScreenId with @a screenGeo. */
+ void sigGuestMonitorChange(KGuestMonitorChangedEventType type, ulong uScreenId, QRect screenGeo);
+ /** Notifies about Runtime error with @a strErrorId which is @a fFatal and have @a strMessage. */
+ void sigRuntimeError(bool fFatal, QString strErrorId, QString strMessage);
+#ifdef RT_OS_DARWIN
+ /** Notifies about VM window should be shown. */
+ void sigShowWindow();
+#endif /* RT_OS_DARWIN */
+ /** Notifies about audio adapter state change. */
+ void sigAudioAdapterChange();
+ /** Notifies clipboard mode change. */
+ void sigClipboardModeChange(KClipboardMode enmMode);
+ /** Notifies drag and drop mode change. */
+ void sigDnDModeChange(KDnDMode enmMode);
+
+public:
+
+ /** Constructs event proxy object on the basis of passed @a pParent and @a pSession. */
+ UIConsoleEventHandlerProxy(QObject *pParent, UISession *pSession);
+ /** Destructs event proxy object. */
+ virtual ~UIConsoleEventHandlerProxy() RT_OVERRIDE;
+
+private slots:
+
+ /** Returns whether VM window can be shown. */
+ void sltCanShowWindow(bool &fVeto, QString &strReason);
+ /** Shows VM window if possible. */
+ void sltShowWindow(qint64 &winId);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares listener. */
+ void prepareListener();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups connections. */
+ void cleanupConnections();
+ /** Cleanups listener. */
+ void cleanupListener();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the UI session reference. */
+ UISession *m_pSession;
+
+ /** Holds the Qt event listener instance. */
+ ComObjPtr<UIMainEventListenerImpl> m_pQtListener;
+ /** Holds the COM event listener instance. */
+ CEventListener m_comEventListener;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIConsoleEventHandlerProxy implementation. *
+*********************************************************************************************************************************/
+
+UIConsoleEventHandlerProxy::UIConsoleEventHandlerProxy(QObject *pParent, UISession *pSession)
+ : QObject(pParent)
+ , m_pSession(pSession)
+{
+ prepare();
+}
+
+UIConsoleEventHandlerProxy::~UIConsoleEventHandlerProxy()
+{
+ cleanup();
+}
+
+void UIConsoleEventHandlerProxy::sltCanShowWindow(bool & /* fVeto */, QString & /* strReason */)
+{
+ /* Nothing for now. */
+}
+
+void UIConsoleEventHandlerProxy::sltShowWindow(qint64 &winId)
+{
+#ifdef VBOX_WS_MAC
+ /* First of all, just ask the GUI thread to show the machine-window: */
+ winId = 0;
+ if (::darwinSetFrontMostProcess())
+ emit sigShowWindow();
+ else
+ {
+ /* If it's failed for some reason, send the other process our PSN so it can try: */
+ winId = ::darwinGetCurrentProcessId();
+ }
+#else /* !VBOX_WS_MAC */
+ /* Return the ID of the top-level machine-window. */
+ winId = (ULONG64)m_pSession->mainMachineWindowId();
+#endif /* !VBOX_WS_MAC */
+}
+
+void UIConsoleEventHandlerProxy::prepare()
+{
+ prepareListener();
+ prepareConnections();
+}
+
+void UIConsoleEventHandlerProxy::prepareListener()
+{
+ /* Make sure session is passed: */
+ AssertPtrReturnVoid(m_pSession);
+
+ /* Create event listener instance: */
+ m_pQtListener.createObject();
+ m_pQtListener->init(new UIMainEventListener, this);
+ m_comEventListener = CEventListener(m_pQtListener);
+
+ /* Get console: */
+ const CConsole comConsole = m_pSession->session().GetConsole();
+ AssertReturnVoid(!comConsole.isNull() && comConsole.isOk());
+ /* Get console event source: */
+ CEventSource comEventSourceConsole = comConsole.GetEventSource();
+ AssertReturnVoid(!comEventSourceConsole.isNull() && comEventSourceConsole.isOk());
+
+ /* Enumerate all the required event-types: */
+ QVector<KVBoxEventType> eventTypes;
+ eventTypes
+ << KVBoxEventType_OnMousePointerShapeChanged
+ << KVBoxEventType_OnMouseCapabilityChanged
+ << KVBoxEventType_OnCursorPositionChanged
+ << KVBoxEventType_OnKeyboardLedsChanged
+ << KVBoxEventType_OnStateChanged
+ << KVBoxEventType_OnAdditionsStateChanged
+ << KVBoxEventType_OnNetworkAdapterChanged
+ << KVBoxEventType_OnStorageDeviceChanged
+ << KVBoxEventType_OnMediumChanged
+ << KVBoxEventType_OnVRDEServerChanged
+ << KVBoxEventType_OnVRDEServerInfoChanged
+ << KVBoxEventType_OnRecordingChanged
+ << KVBoxEventType_OnUSBControllerChanged
+ << KVBoxEventType_OnUSBDeviceStateChanged
+ << KVBoxEventType_OnSharedFolderChanged
+ << KVBoxEventType_OnCPUExecutionCapChanged
+ << KVBoxEventType_OnGuestMonitorChanged
+ << KVBoxEventType_OnRuntimeError
+ << KVBoxEventType_OnCanShowWindow
+ << KVBoxEventType_OnShowWindow
+ << KVBoxEventType_OnAudioAdapterChanged
+ << KVBoxEventType_OnClipboardModeChanged
+ << KVBoxEventType_OnDnDModeChanged
+ ;
+
+ /* Register event listener for console event source: */
+ comEventSourceConsole.RegisterListener(m_comEventListener, eventTypes, FALSE /* active? */);
+ AssertWrapperOk(comEventSourceConsole);
+
+ /* Register event sources in their listeners as well: */
+ m_pQtListener->getWrapped()->registerSource(comEventSourceConsole, m_comEventListener);
+}
+
+void UIConsoleEventHandlerProxy::prepareConnections()
+{
+ /* Create direct (sync) connections for signals of main listener: */
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigMousePointerShapeChange,
+ this, &UIConsoleEventHandlerProxy::sigMousePointerShapeChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigMouseCapabilityChange,
+ this, &UIConsoleEventHandlerProxy::sigMouseCapabilityChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigCursorPositionChange,
+ this, &UIConsoleEventHandlerProxy::sigCursorPositionChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigKeyboardLedsChangeEvent,
+ this, &UIConsoleEventHandlerProxy::sigKeyboardLedsChangeEvent,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigStateChange,
+ this, &UIConsoleEventHandlerProxy::sigStateChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigAdditionsChange,
+ this, &UIConsoleEventHandlerProxy::sigAdditionsChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigNetworkAdapterChange,
+ this, &UIConsoleEventHandlerProxy::sigNetworkAdapterChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigStorageDeviceChange,
+ this, &UIConsoleEventHandlerProxy::sigStorageDeviceChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigMediumChange,
+ this, &UIConsoleEventHandlerProxy::sigMediumChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigVRDEChange,
+ this, &UIConsoleEventHandlerProxy::sigVRDEChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigRecordingChange,
+ this, &UIConsoleEventHandlerProxy::sigRecordingChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigUSBControllerChange,
+ this, &UIConsoleEventHandlerProxy::sigUSBControllerChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigUSBDeviceStateChange,
+ this, &UIConsoleEventHandlerProxy::sigUSBDeviceStateChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigSharedFolderChange,
+ this, &UIConsoleEventHandlerProxy::sigSharedFolderChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigCPUExecutionCapChange,
+ this, &UIConsoleEventHandlerProxy::sigCPUExecutionCapChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigGuestMonitorChange,
+ this, &UIConsoleEventHandlerProxy::sigGuestMonitorChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigRuntimeError,
+ this, &UIConsoleEventHandlerProxy::sigRuntimeError,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigCanShowWindow,
+ this, &UIConsoleEventHandlerProxy::sltCanShowWindow,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigShowWindow,
+ this, &UIConsoleEventHandlerProxy::sltShowWindow,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigAudioAdapterChange,
+ this, &UIConsoleEventHandlerProxy::sigAudioAdapterChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigClipboardModeChange,
+ this, &UIConsoleEventHandlerProxy::sigClipboardModeChange,
+ Qt::DirectConnection);
+ connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigDnDModeChange,
+ this, &UIConsoleEventHandlerProxy::sigDnDModeChange,
+ Qt::DirectConnection);
+}
+
+void UIConsoleEventHandlerProxy::cleanupConnections()
+{
+ /* Nothing for now. */
+}
+
+void UIConsoleEventHandlerProxy::cleanupListener()
+{
+ /* Make sure session is passed: */
+ AssertPtrReturnVoid(m_pSession);
+
+ /* Unregister everything: */
+ m_pQtListener->getWrapped()->unregisterSources();
+
+ /* Get console: */
+ const CConsole comConsole = m_pSession->session().GetConsole();
+ if (comConsole.isNull() || !comConsole.isOk())
+ return;
+ /* Get console event source: */
+ CEventSource comEventSourceConsole = comConsole.GetEventSource();
+ AssertWrapperOk(comEventSourceConsole);
+
+ /* Unregister event listener for console event source: */
+ comEventSourceConsole.UnregisterListener(m_comEventListener);
+}
+
+void UIConsoleEventHandlerProxy::cleanup()
+{
+ cleanupConnections();
+ cleanupListener();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIConsoleEventHandler implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIConsoleEventHandler *UIConsoleEventHandler::s_pInstance = 0;
+
+/* static */
+void UIConsoleEventHandler::create(UISession *pSession)
+{
+ if (!s_pInstance)
+ s_pInstance = new UIConsoleEventHandler(pSession);
+}
+
+/* static */
+void UIConsoleEventHandler::destroy()
+{
+ if (s_pInstance)
+ {
+ delete s_pInstance;
+ s_pInstance = 0;
+ }
+}
+
+UIConsoleEventHandler::UIConsoleEventHandler(UISession *pSession)
+ : m_pProxy(new UIConsoleEventHandlerProxy(this, pSession))
+{
+ prepare();
+}
+
+void UIConsoleEventHandler::prepare()
+{
+ prepareConnections();
+}
+
+void UIConsoleEventHandler::prepareConnections()
+{
+ /* Create queued (async) connections for signals of event proxy object: */
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigMousePointerShapeChange,
+ this, &UIConsoleEventHandler::sigMousePointerShapeChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigMouseCapabilityChange,
+ this, &UIConsoleEventHandler::sigMouseCapabilityChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigCursorPositionChange,
+ this, &UIConsoleEventHandler::sigCursorPositionChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigKeyboardLedsChangeEvent,
+ this, &UIConsoleEventHandler::sigKeyboardLedsChangeEvent,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigStateChange,
+ this, &UIConsoleEventHandler::sigStateChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigAdditionsChange,
+ this, &UIConsoleEventHandler::sigAdditionsChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigNetworkAdapterChange,
+ this, &UIConsoleEventHandler::sigNetworkAdapterChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigStorageDeviceChange,
+ this, &UIConsoleEventHandler::sigStorageDeviceChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigMediumChange,
+ this, &UIConsoleEventHandler::sigMediumChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigVRDEChange,
+ this, &UIConsoleEventHandler::sigVRDEChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigRecordingChange,
+ this, &UIConsoleEventHandler::sigRecordingChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigUSBControllerChange,
+ this, &UIConsoleEventHandler::sigUSBControllerChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigUSBDeviceStateChange,
+ this, &UIConsoleEventHandler::sigUSBDeviceStateChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigSharedFolderChange,
+ this, &UIConsoleEventHandler::sigSharedFolderChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigCPUExecutionCapChange,
+ this, &UIConsoleEventHandler::sigCPUExecutionCapChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigGuestMonitorChange,
+ this, &UIConsoleEventHandler::sigGuestMonitorChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigRuntimeError,
+ this, &UIConsoleEventHandler::sigRuntimeError,
+ Qt::QueuedConnection);
+#ifdef RT_OS_DARWIN
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigShowWindow,
+ this, &UIConsoleEventHandler::sigShowWindow,
+ Qt::QueuedConnection);
+#endif /* RT_OS_DARWIN */
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigAudioAdapterChange,
+ this, &UIConsoleEventHandler::sigAudioAdapterChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigClipboardModeChange,
+ this, &UIConsoleEventHandler::sigClipboardModeChange,
+ Qt::QueuedConnection);
+ connect(m_pProxy, &UIConsoleEventHandlerProxy::sigDnDModeChange,
+ this, &UIConsoleEventHandler::sigDnDModeChange,
+ Qt::QueuedConnection);
+}
+
+#include "UIConsoleEventHandler.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIConsoleEventHandler.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIConsoleEventHandler.h
new file mode 100644
index 00000000..3a758e4a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIConsoleEventHandler.h
@@ -0,0 +1,139 @@
+/* $Id: UIConsoleEventHandler.h $ */
+/** @file
+ * VBox Qt GUI - UIConsoleEventHandler class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIConsoleEventHandler_h
+#define FEQT_INCLUDED_SRC_runtime_UIConsoleEventHandler_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QRect>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CUSBDevice.h"
+#include "CVirtualBoxErrorInfo.h"
+
+/* Forward declarations: */
+class UIConsoleEventHandlerProxy;
+class UIMousePointerShapeData;
+class UISession;
+
+
+/** Singleton QObject extension
+ * providing GUI with the CConsole event-source. */
+class UIConsoleEventHandler : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about mouse pointer @a shapeData change. */
+ void sigMousePointerShapeChange(const UIMousePointerShapeData &shapeData);
+ /** Notifies about mouse capability change to @a fSupportsAbsolute, @a fSupportsRelative,
+ * @a fSupportsTouchScreen, @a fSupportsTouchPad and @a fNeedsHostCursor. */
+ void sigMouseCapabilityChange(bool fSupportsAbsolute, bool fSupportsRelative,
+ bool fSupportsTouchScreen, bool fSupportsTouchPad,
+ bool fNeedsHostCursor);
+ /** Notifies about guest request to change the cursor position to @a uX * @a uY.
+ * @param fContainsData Brings whether the @a uX and @a uY values are valid and could be used by the GUI now. */
+ void sigCursorPositionChange(bool fContainsData, unsigned long uX, unsigned long uY);
+ /** Notifies about keyboard LEDs change for @a fNumLock, @a fCapsLock and @a fScrollLock. */
+ void sigKeyboardLedsChangeEvent(bool fNumLock, bool fCapsLock, bool fScrollLock);
+ /** Notifies about machine @a state change. */
+ void sigStateChange(KMachineState state);
+ /** Notifies about guest additions state change. */
+ void sigAdditionsChange();
+ /** Notifies about network @a adapter state change. */
+ void sigNetworkAdapterChange(CNetworkAdapter adapter);
+ /** Notifies about storage device change for @a attachment, which was @a fRemoved and it was @a fSilent for guest. */
+ void sigStorageDeviceChange(CMediumAttachment attachment, bool fRemoved, bool fSilent);
+ /** Notifies about storage medium @a attachment state change. */
+ void sigMediumChange(CMediumAttachment attachment);
+ /** Notifies about VRDE device state change. */
+ void sigVRDEChange();
+ /** Notifies about recording state change. */
+ void sigRecordingChange();
+ /** Notifies about USB controller state change. */
+ void sigUSBControllerChange();
+ /** Notifies about USB @a device state change to @a fAttached, holding additional @a error information. */
+ void sigUSBDeviceStateChange(CUSBDevice device, bool fAttached, CVirtualBoxErrorInfo error);
+ /** Notifies about shared folder state change. */
+ void sigSharedFolderChange();
+ /** Notifies about CPU execution-cap change. */
+ void sigCPUExecutionCapChange();
+ /** Notifies about guest-screen configuration change of @a type for @a uScreenId with @a screenGeo. */
+ void sigGuestMonitorChange(KGuestMonitorChangedEventType type, ulong uScreenId, QRect screenGeo);
+ /** Notifies about Runtime error with @a strErrorId which is @a fFatal and have @a strMessage. */
+ void sigRuntimeError(bool fFatal, QString strErrorId, QString strMessage);
+#ifdef RT_OS_DARWIN
+ /** Notifies about VM window should be shown. */
+ void sigShowWindow();
+#endif /* RT_OS_DARWIN */
+ /** Notifies about audio adapter state change. */
+ void sigAudioAdapterChange();
+ /** Notifies clipboard mode change. */
+ void sigClipboardModeChange(KClipboardMode enmMode);
+ /** Notifies drag and drop mode change. */
+ void sigDnDModeChange(KDnDMode enmMode);
+
+public:
+
+ /** Returns singleton instance created by the factory. */
+ static UIConsoleEventHandler *instance() { return s_pInstance; }
+ /** Creates singleton instance created by the factory. */
+ static void create(UISession *pSession);
+ /** Destroys singleton instance created by the factory. */
+ static void destroy();
+
+protected:
+
+ /** Constructs console event handler for passed @a pSession. */
+ UIConsoleEventHandler(UISession *pSession);
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares connections. */
+ void prepareConnections();
+
+private:
+
+ /** Holds the singleton static console event handler instance. */
+ static UIConsoleEventHandler *s_pInstance;
+
+ /** Holds the console event proxy instance. */
+ UIConsoleEventHandlerProxy *m_pProxy;
+};
+
+/** Defines the globally known name for the console event handler instance. */
+#define gConsoleEvents UIConsoleEventHandler::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIConsoleEventHandler_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.cpp
new file mode 100644
index 00000000..38adc389
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.cpp
@@ -0,0 +1,808 @@
+/* $Id: UIDnDDataObject_win.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDnDDrag class implementation (implements IDataObject).
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+#include <VBox/log.h>
+
+#include <iprt/win/windows.h>
+#include <new> /* For bad_alloc. */
+#include <iprt/win/shlobj.h>
+
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/uri.h>
+#include <iprt/utf16.h>
+
+#include <QStringList>
+
+#include "UIDnDHandler.h"
+#include "UIDnDDataObject_win.h"
+#include "UIDnDEnumFormat_win.h"
+
+
+UIDnDDataObject::UIDnDDataObject(UIDnDHandler *pDnDHandler, const QStringList &lstFormats)
+ : m_pDnDHandler(pDnDHandler)
+ , m_enmStatus(Status_Uninitialized)
+ , m_cRefs(1)
+ , m_cFormats(0)
+ , m_pFormatEtc(NULL)
+ , m_pStgMedium(NULL)
+ , m_SemEvent(NIL_RTSEMEVENT)
+ , m_fDataRetrieved(false)
+ , m_pvData(NULL)
+ , m_cbData(0)
+{
+ HRESULT hr;
+
+ int cMaxFormats = 16; /* Maximum number of registered formats. */
+ ULONG cRegisteredFormats = 0;
+
+ try
+ {
+ m_pFormatEtc = new FORMATETC[cMaxFormats];
+ RT_BZERO(m_pFormatEtc, sizeof(FORMATETC) * cMaxFormats);
+ m_pStgMedium = new STGMEDIUM[cMaxFormats];
+ RT_BZERO(m_pStgMedium, sizeof(STGMEDIUM) * cMaxFormats);
+
+ for (int i = 0; i < lstFormats.size() && i < cMaxFormats; i++)
+ {
+ const QString &strFormat = lstFormats.at(i);
+ if (m_lstFormats.contains(strFormat))
+ continue;
+
+ /* URI data ("text/uri-list"). */
+ if (strFormat.contains("text/uri-list", Qt::CaseInsensitive))
+ {
+ RegisterFormat(&m_pFormatEtc[cRegisteredFormats], CF_TEXT);
+ m_pStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
+ RegisterFormat(&m_pFormatEtc[cRegisteredFormats], CF_UNICODETEXT);
+ m_pStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
+ RegisterFormat(&m_pFormatEtc[cRegisteredFormats], CF_HDROP);
+ m_pStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
+
+ m_lstFormats << strFormat;
+ }
+ /* Plain text ("text/plain"). */
+ if (strFormat.contains("text/plain", Qt::CaseInsensitive))
+ {
+ RegisterFormat(&m_pFormatEtc[cRegisteredFormats], CF_TEXT);
+ m_pStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
+ RegisterFormat(&m_pFormatEtc[cRegisteredFormats], CF_UNICODETEXT);
+ m_pStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
+
+ m_lstFormats << strFormat;
+ }
+ }
+
+ LogRel3(("DnD: Total registered native formats: %RU32 (for %d formats from guest)\n",
+ cRegisteredFormats, lstFormats.size()));
+ hr = S_OK;
+ }
+ catch (std::bad_alloc &)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ int rc2 = RTSemEventCreate(&m_SemEvent);
+ AssertRC(rc2);
+
+ /*
+ * Other (not so common) formats.
+ */
+#if 0
+ /* IStream. */
+ RegisterFormat(&mpFormatEtc[cFormats++],
+ RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
+ RegisterFormat(&mpFormatEtc[cFormats++],
+ RegisterClipboardFormat(CFSTR_FILECONTENTS),
+ TYMED_ISTREAM, 0 /* lIndex */);
+
+ /* Required for e.g. Windows Media Player. */
+ RegisterFormat(&mpFormatEtc[cFormats++],
+ RegisterClipboardFormat(CFSTR_FILENAME));
+ RegisterFormat(&mpFormatEtc[cFormats++],
+ RegisterClipboardFormat(CFSTR_FILENAMEW));
+ RegisterFormat(&mpFormatEtc[cFormats++],
+ RegisterClipboardFormat(CFSTR_SHELLIDLIST));
+ RegisterFormat(&mpFormatEtc[cFormats++],
+ RegisterClipboardFormat(CFSTR_SHELLIDLISTOFFSET));
+#endif
+ m_cFormats = cRegisteredFormats;
+ m_enmStatus = Status_Dropping;
+ }
+
+ LogFlowFunc(("hr=%Rhrc\n", hr));
+}
+
+UIDnDDataObject::~UIDnDDataObject(void)
+{
+ if (m_pFormatEtc)
+ delete[] m_pFormatEtc;
+
+ if (m_pStgMedium)
+ delete[] m_pStgMedium;
+
+ if (m_pvData)
+ RTMemFree(m_pvData);
+
+ if (m_SemEvent != NIL_RTSEMEVENT)
+ RTSemEventDestroy(m_SemEvent);
+
+ LogFlowFunc(("mRefCount=%RI32\n", m_cRefs));
+}
+
+/*
+ * IUnknown methods.
+ */
+
+STDMETHODIMP_(ULONG) UIDnDDataObject::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRefs);
+}
+
+STDMETHODIMP_(ULONG) UIDnDDataObject::Release(void)
+{
+ LONG lCount = InterlockedDecrement(&m_cRefs);
+ if (lCount == 0)
+ {
+ delete this;
+ return 0;
+ }
+
+ return lCount;
+}
+
+STDMETHODIMP UIDnDDataObject::QueryInterface(REFIID iid, void **ppvObject)
+{
+ AssertPtrReturn(ppvObject, E_INVALIDARG);
+
+ if ( iid == IID_IDataObject
+ || iid == IID_IUnknown)
+ {
+ AddRef();
+ *ppvObject = this;
+ return S_OK;
+ }
+
+ *ppvObject = 0;
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP UIDnDDataObject::GetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
+{
+ AssertPtrReturn(pFormatEtc, DV_E_FORMATETC);
+ AssertPtrReturn(pMedium, DV_E_FORMATETC);
+
+ HRESULT hr = DV_E_FORMATETC;
+
+ LPFORMATETC pThisFormat = NULL;
+ LPSTGMEDIUM pThisMedium = NULL;
+
+ LogFlowThisFunc(("\n"));
+
+ /* Format supported? */
+ ULONG lIndex;
+ if ( LookupFormatEtc(pFormatEtc, &lIndex)
+ && lIndex < m_cFormats) /* Paranoia. */
+ {
+ pThisMedium = &m_pStgMedium[lIndex];
+ AssertPtr(pThisMedium);
+ pThisFormat = &m_pFormatEtc[lIndex];
+ AssertPtr(pThisFormat);
+
+ LogFlowThisFunc(("pThisMedium=%p, pThisFormat=%p\n", pThisMedium, pThisFormat));
+ LogFlowThisFunc(("mStatus=%RU32\n", m_enmStatus));
+ switch (m_enmStatus)
+ {
+ case Status_Dropping:
+ {
+#if 0
+ LogRel3(("DnD: Dropping\n"));
+ LogFlowFunc(("Waiting for event ...\n"));
+ int rc2 = RTSemEventWait(m_SemEvent, RT_INDEFINITE_WAIT);
+ LogFlowFunc(("rc=%Rrc, mStatus=%RU32\n", rc2, m_enmStatus));
+#endif
+ break;
+ }
+
+ case Status_Dropped:
+ {
+ LogRel3(("DnD: Dropped\n"));
+ LogRel3(("DnD: cfFormat=%RI16, sFormat=%s, tyMed=%RU32, dwAspect=%RU32\n",
+ pThisFormat->cfFormat, UIDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat),
+ pThisFormat->tymed, pThisFormat->dwAspect));
+ LogRel3(("DnD: Got strFormat=%s, pvData=%p, cbData=%RU32\n",
+ m_strFormat.toUtf8().constData(), m_pvData, m_cbData));
+
+ QVariant::Type vaType = QVariant::Invalid; /* MSC: Might be used uninitialized otherwise! */
+ QString strMIMEType;
+ if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
+ && pFormatEtc->dwAspect == DVASPECT_CONTENT
+ && ( pFormatEtc->cfFormat == CF_TEXT
+ || pFormatEtc->cfFormat == CF_UNICODETEXT)
+ )
+ {
+ /* Use UTF-8, always. */
+ strMIMEType = "text/plain;charset=utf-8";
+ vaType = QVariant::String;
+ }
+ else if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
+ && pFormatEtc->dwAspect == DVASPECT_CONTENT
+ && pFormatEtc->cfFormat == CF_HDROP)
+ {
+ strMIMEType = "text/uri-list";
+ vaType = QVariant::StringList;
+ }
+#if 0 /* More formats; not needed right now. */
+ else if ( (pFormatEtc->tymed & TYMED_ISTREAM)
+ && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
+ && (pFormatEtc->cfFormat == CF_FILECONTENTS))
+ {
+
+ }
+ else if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
+ && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
+ && (pFormatEtc->cfFormat == CF_FILEDESCRIPTOR))
+ {
+
+ }
+ else if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
+ && (pFormatEtc->cfFormat == CF_PREFERREDDROPEFFECT))
+ {
+ HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, sizeof(DWORD));
+ DWORD *pdwEffect = (DWORD *)GlobalLock(hData);
+ AssertPtr(pdwEffect);
+ *pdwEffect = DROPEFFECT_COPY;
+ GlobalUnlock(hData);
+
+ pMedium->hGlobal = hData;
+ pMedium->tymed = TYMED_HGLOBAL;
+ }
+#endif
+ LogRel3(("DnD: strMIMEType=%s\n", strMIMEType.toUtf8().constData()));
+
+ int rc;
+
+ if (!m_fDataRetrieved)
+ {
+ if (m_pDnDHandler)
+ rc = m_pDnDHandler->retrieveData(Qt::CopyAction, strMIMEType, vaType, m_vaData);
+ else
+ rc = VERR_NOT_FOUND;
+
+ m_fDataRetrieved = true;
+ LogFlowFunc(("Retrieving data ended with %Rrc\n", rc));
+ }
+ else /* Data already been retrieved. */
+ rc = VINF_SUCCESS;
+
+ if ( RT_SUCCESS(rc)
+ && m_vaData.isValid())
+ {
+ if ( strMIMEType.startsWith("text/uri-list")
+ /* One item. */
+ && ( m_vaData.canConvert(QVariant::String)
+ /* Multiple items. */
+ || m_vaData.canConvert(QVariant::StringList))
+ )
+ {
+ QStringList lstFilesURI = m_vaData.toStringList();
+ QStringList lstFiles;
+ for (int i = 0; i < lstFilesURI.size(); i++)
+ {
+ char *pszFilePath = RTUriFilePath(lstFilesURI.at(i).toUtf8().constData());
+ if (pszFilePath)
+ {
+ lstFiles.append(pszFilePath);
+ RTStrFree(pszFilePath);
+ }
+ else /* Unable to parse -- refuse entire request. */
+ {
+ lstFiles.clear();
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ int cFiles = lstFiles.size();
+ LogFlowThisFunc(("Files (%d)\n", cFiles));
+ if ( RT_SUCCESS(rc)
+ && cFiles)
+ {
+ size_t cchFiles = 0; /* Number of characters. */
+ for (int i = 0; i < cFiles; i++)
+ {
+ const char *pszFile = lstFiles.at(i).toUtf8().constData();
+ cchFiles += strlen(pszFile);
+ cchFiles += 1; /* Terminating '\0'. */
+ LogFlowThisFunc(("\tFile: %s (cchFiles=%zu)\n", pszFile, cchFiles));
+ }
+
+ /* List termination with '\0'. */
+ cchFiles++;
+
+ size_t cbBuf = sizeof(DROPFILES) + (cchFiles * sizeof(RTUTF16));
+ DROPFILES *pDropFiles = (DROPFILES *)RTMemAllocZ(cbBuf);
+ if (pDropFiles)
+ {
+ /* Put the files list right after our DROPFILES structure. */
+ pDropFiles->pFiles = sizeof(DROPFILES); /* Offset to file list. */
+ pDropFiles->fWide = 1; /* We use Unicode. Always. */
+
+ uint8_t *pCurFile = (uint8_t *)pDropFiles + pDropFiles->pFiles;
+ AssertPtr(pCurFile);
+
+ LogFlowThisFunc(("Encoded:\n"));
+ for (int i = 0; i < cFiles; i++)
+ {
+ const char *pszFile = lstFiles.at(i).toUtf8().constData();
+ Assert(strlen(pszFile));
+
+ size_t cchCurFile;
+ PRTUTF16 pwszFile;
+ rc = RTStrToUtf16(pszFile, &pwszFile);
+ if (RT_SUCCESS(rc))
+ {
+ cchCurFile = RTUtf16Len(pwszFile);
+ Assert(cchCurFile);
+ memcpy(pCurFile, pwszFile, cchCurFile * sizeof(RTUTF16));
+ RTUtf16Free(pwszFile);
+ }
+ else
+ break;
+
+ pCurFile += cchCurFile * sizeof(RTUTF16);
+
+ /* Terminate current file name. */
+ *pCurFile = L'\0';
+ pCurFile += sizeof(RTUTF16);
+
+ LogFlowThisFunc(("\t#%d: cchCurFile=%zu\n", i, cchCurFile));
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *pCurFile = L'\0'; /* Final list terminator. */
+
+ /*
+ * Fill out the medium structure we're going to report back.
+ */
+ pMedium->tymed = TYMED_HGLOBAL;
+ pMedium->pUnkForRelease = NULL;
+ pMedium->hGlobal = GlobalAlloc( GMEM_ZEROINIT
+ | GMEM_MOVEABLE
+ | GMEM_DDESHARE, cbBuf);
+ if (pMedium->hGlobal)
+ {
+ LPVOID pvMem = GlobalLock(pMedium->hGlobal);
+ if (pvMem)
+ {
+ memcpy(pvMem, pDropFiles, cbBuf);
+ GlobalUnlock(pMedium->hGlobal);
+
+ hr = S_OK;
+ }
+ else
+ rc = VERR_ACCESS_DENIED;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ LogFlowThisFunc(("Copying to TYMED_HGLOBAL (%zu bytes): %Rrc\n", cbBuf, rc));
+ }
+
+ RTMemFree(pDropFiles);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (RT_FAILURE(rc))
+ LogFlowThisFunc(("Failed with %Rrc\n", rc));
+ }
+ }
+ else if ( strMIMEType.startsWith("text/plain;charset=utf-8") /* Use UTF-8, always. */
+ && m_vaData.canConvert(QVariant::String))
+ {
+ const bool fUnicode = pFormatEtc->cfFormat == CF_UNICODETEXT;
+ const size_t cbCh = fUnicode
+ ? sizeof(WCHAR) : sizeof(char);
+
+ QString strText = m_vaData.toString();
+ size_t cbSrc = strText.length() * cbCh;
+ LPCVOID pvSrc = fUnicode
+ ? (void *)strText.unicode()
+ : (void *)strText.toUtf8().constData();
+
+ AssertMsg(cbSrc, ("pvSrc=0x%p, cbSrc=%zu, cbCh=%zu\n", pvSrc, cbSrc, cbCh));
+ AssertPtr(pvSrc);
+
+ LogFlowFunc(("pvSrc=0x%p, cbSrc=%zu, cbCh=%zu, fUnicode=%RTbool\n",
+ pvSrc, cbSrc, cbCh, fUnicode));
+
+ pMedium->tymed = TYMED_HGLOBAL;
+ pMedium->pUnkForRelease = NULL;
+ pMedium->hGlobal = GlobalAlloc(GHND | GMEM_SHARE, cbSrc);
+ if (pMedium->hGlobal)
+ {
+ LPVOID pvDst = GlobalLock(pMedium->hGlobal);
+ if (pvDst)
+ {
+ memcpy(pvDst, pvSrc, cbSrc);
+ GlobalUnlock(pMedium->hGlobal);
+ }
+ else
+ rc = VERR_ACCESS_DENIED;
+
+ hr = S_OK;
+ }
+ else
+ hr = VERR_NO_MEMORY;
+ }
+ else
+ LogRel2(("DnD: MIME type '%s' not supported\n", strMIMEType.toUtf8().constData()));
+
+ LogFlowThisFunc(("Handling formats ended with rc=%Rrc\n", rc));
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Fallback in error case.
+ */
+ if (FAILED(hr))
+ {
+ if (pThisMedium)
+ {
+ switch (pThisMedium->tymed)
+ {
+
+ case TYMED_HGLOBAL:
+ pMedium->hGlobal = (HGLOBAL)OleDuplicateData(pThisMedium->hGlobal,
+ pThisFormat->cfFormat,
+ 0 /* Flags */);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (pFormatEtc)
+ pMedium->tymed = pFormatEtc->tymed;
+
+ pMedium->pUnkForRelease = NULL;
+ }
+
+ LogFlowThisFunc(("Returning hr=%Rhrc\n", hr));
+ return hr;
+}
+
+STDMETHODIMP UIDnDDataObject::GetDataHere(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
+{
+ RT_NOREF(pFormatEtc, pMedium);
+ LogFlowFunc(("\n"));
+ return DATA_E_FORMATETC;
+}
+
+STDMETHODIMP UIDnDDataObject::QueryGetData(LPFORMATETC pFormatEtc)
+{
+ return LookupFormatEtc(pFormatEtc, NULL /* puIndex */) ? S_OK : DV_E_FORMATETC;
+}
+
+STDMETHODIMP UIDnDDataObject::GetCanonicalFormatEtc(LPFORMATETC pFormatEtc, LPFORMATETC pFormatEtcOut)
+{
+ RT_NOREF(pFormatEtc);
+ LogFlowFunc(("\n"));
+
+ /* Set this to NULL in any case. */
+ pFormatEtcOut->ptd = NULL;
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP UIDnDDataObject::SetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium, BOOL fRelease)
+{
+ RT_NOREF(pFormatEtc, pMedium, fRelease);
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP UIDnDDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc)
+{
+ LogFlowFunc(("dwDirection=%RI32, mcFormats=%RI32, mpFormatEtc=%p\n",
+ dwDirection, m_cFormats, m_pFormatEtc));
+
+ HRESULT hr;
+ if (dwDirection == DATADIR_GET)
+ {
+ hr = UIDnDEnumFormatEtc::CreateEnumFormatEtc(m_cFormats, m_pFormatEtc, ppEnumFormatEtc);
+ }
+ else
+ hr = E_NOTIMPL;
+
+ LogFlowFunc(("hr=%Rhrc\n", hr));
+ return hr;
+}
+
+STDMETHODIMP UIDnDDataObject::DAdvise(LPFORMATETC pFormatEtc, DWORD fAdvise, IAdviseSink *pAdvSink, DWORD *pdwConnection)
+{
+ RT_NOREF(pFormatEtc, fAdvise, pAdvSink, pdwConnection);
+ return OLE_E_ADVISENOTSUPPORTED;
+}
+
+STDMETHODIMP UIDnDDataObject::DUnadvise(DWORD dwConnection)
+{
+ RT_NOREF(dwConnection);
+ return OLE_E_ADVISENOTSUPPORTED;
+}
+
+STDMETHODIMP UIDnDDataObject::EnumDAdvise(IEnumSTATDATA **ppEnumAdvise)
+{
+ RT_NOREF(ppEnumAdvise);
+ return OLE_E_ADVISENOTSUPPORTED;
+}
+
+/*
+ * Own stuff.
+ */
+
+/**
+ * Aborts waiting for data being "dropped".
+ *
+ * @returns VBox status code.
+ */
+int UIDnDDataObject::Abort(void)
+{
+ LogFlowFunc(("Aborting ...\n"));
+ m_enmStatus = Status_Aborted;
+ return RTSemEventSignal(m_SemEvent);
+}
+
+/**
+ * Static helper function to convert a CLIPFORMAT to a string and return it.
+ *
+ * @returns Pointer to converted stringified CLIPFORMAT, or "unknown" if not found / invalid.
+ * @param fmt CLIPFORMAT to return string for.
+ */
+/* static */
+const char* UIDnDDataObject::ClipboardFormatToString(CLIPFORMAT fmt)
+{
+ WCHAR wszFormat[128];
+ if (GetClipboardFormatNameW(fmt, wszFormat, sizeof(wszFormat) / sizeof(WCHAR)))
+ LogFlowFunc(("wFormat=%RI16, szName=%ls\n", fmt, wszFormat));
+
+ switch (fmt)
+ {
+
+ case 1:
+ return "CF_TEXT";
+ case 2:
+ return "CF_BITMAP";
+ case 3:
+ return "CF_METAFILEPICT";
+ case 4:
+ return "CF_SYLK";
+ case 5:
+ return "CF_DIF";
+ case 6:
+ return "CF_TIFF";
+ case 7:
+ return "CF_OEMTEXT";
+ case 8:
+ return "CF_DIB";
+ case 9:
+ return "CF_PALETTE";
+ case 10:
+ return "CF_PENDATA";
+ case 11:
+ return "CF_RIFF";
+ case 12:
+ return "CF_WAVE";
+ case 13:
+ return "CF_UNICODETEXT";
+ case 14:
+ return "CF_ENHMETAFILE";
+ case 15:
+ return "CF_HDROP";
+ case 16:
+ return "CF_LOCALE";
+ case 17:
+ return "CF_DIBV5";
+ case 18:
+ return "CF_MAX";
+ case 49158:
+ return "FileName";
+ case 49159:
+ return "FileNameW";
+ case 49161:
+ return "DATAOBJECT";
+ case 49171:
+ return "Ole Private Data";
+ case 49314:
+ return "Shell Object Offsets";
+ case 49316:
+ return "File Contents";
+ case 49317:
+ return "File Group Descriptor";
+ case 49323:
+ return "Preferred Drop Effect";
+ case 49380:
+ return "Shell Object Offsets";
+ case 49382:
+ return "FileContents";
+ case 49383:
+ return "FileGroupDescriptor";
+ case 49389:
+ return "Preferred DropEffect";
+ case 49268:
+ return "Shell IDList Array";
+ case 49619:
+ return "RenPrivateFileAttachments";
+ default:
+ break;
+ }
+
+ return "unknown";
+}
+
+/**
+ * Checks whether a given FORMATETC is supported by this data object and returns its index.
+ *
+ * @returns \c true if format is supported, \c false if not.
+ * @param pFormatEtc Pointer to FORMATETC to check for.
+ * @param puIndex Where to store the index if format is supported.
+ */
+bool UIDnDDataObject::LookupFormatEtc(LPFORMATETC pFormatEtc, ULONG *puIndex)
+{
+ AssertReturn(pFormatEtc, false);
+ /* puIndex is optional. */
+
+ for (ULONG i = 0; i < m_cFormats; i++)
+ {
+ if( (pFormatEtc->tymed & m_pFormatEtc[i].tymed)
+ && pFormatEtc->cfFormat == m_pFormatEtc[i].cfFormat
+ && pFormatEtc->dwAspect == m_pFormatEtc[i].dwAspect)
+ {
+ LogRel3(("DnD: Format found: tyMed=%RI32, cfFormat=%RI16, sFormats=%s, dwAspect=%RI32, ulIndex=%RU32\n",
+ pFormatEtc->tymed, pFormatEtc->cfFormat, UIDnDDataObject::ClipboardFormatToString(m_pFormatEtc[i].cfFormat),
+ pFormatEtc->dwAspect, i));
+
+ if (puIndex)
+ *puIndex = i;
+ return true;
+ }
+ }
+
+#if 0
+ LogRel3(("DnD: Format NOT found: tyMed=%RI32, cfFormat=%RI16, sFormats=%s, dwAspect=%RI32\n",
+ pFormatEtc->tymed, pFormatEtc->cfFormat, UIDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat),
+ pFormatEtc->dwAspect));
+#endif
+
+ return false;
+}
+
+/**
+ * Registers a new format with this data object.
+ *
+ * @param pFormatEtc Where to store the new format into.
+ * @param clipFormat Clipboard format to register.
+ * @param tyMed Format medium type to register.
+ * @param lIndex Format index to register.
+ * @param dwAspect Format aspect to register.
+ * @param pTargetDevice Format target device to register.
+ */
+void UIDnDDataObject::RegisterFormat(LPFORMATETC pFormatEtc, CLIPFORMAT clipFormat,
+ TYMED tyMed, LONG lIndex, DWORD dwAspect,
+ DVTARGETDEVICE *pTargetDevice)
+{
+ AssertPtr(pFormatEtc);
+
+ pFormatEtc->cfFormat = clipFormat;
+ pFormatEtc->tymed = tyMed;
+ pFormatEtc->lindex = lIndex;
+ pFormatEtc->dwAspect = dwAspect;
+ pFormatEtc->ptd = pTargetDevice;
+
+ LogFlowFunc(("Registered format=%ld, sFormat=%s\n",
+ pFormatEtc->cfFormat, UIDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat)));
+}
+
+/**
+ * Sets the current status of this data object.
+ *
+ * @param enmStatus New status to set.
+ */
+void UIDnDDataObject::SetStatus(Status enmStatus)
+{
+ LogFlowFunc(("Setting status to %RU32\n", enmStatus));
+ m_enmStatus = enmStatus;
+}
+
+/**
+ * Signals that data has been "dropped".
+ */
+void UIDnDDataObject::Signal(void)
+{
+ SetStatus(Status_Dropped);
+}
+
+/**
+ * Signals that data has been "dropped".
+ *
+ * @returns VBox status code.
+ * @param strFormat Format of data (MIME string).
+ * @param pvData Pointer to data.
+ * @param cbData Size (in bytes) of data.
+ */
+int UIDnDDataObject::Signal(const QString &strFormat,
+ const void *pvData, uint32_t cbData)
+{
+ LogFlowFunc(("Signalling ...\n"));
+
+ int rc;
+
+ if (cbData)
+ {
+ m_pvData = RTMemAlloc(cbData);
+ if (m_pvData)
+ {
+ memcpy(m_pvData, pvData, cbData);
+ m_cbData = cbData;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc))
+ {
+ m_strFormat = strFormat;
+ SetStatus(Status_Dropped);
+ }
+ else
+ SetStatus(Status_Aborted);
+
+ /* Signal in any case. */
+ int rc2 = RTSemEventSignal(m_SemEvent);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ return rc;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.h
new file mode 100644
index 00000000..3f54a51b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.h
@@ -0,0 +1,131 @@
+/* $Id: UIDnDDataObject_win.h $ */
+/** @file
+ * VBox Qt GUI - UIDnDDataObject class declaration.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIDnDDataObject_win_h
+#define FEQT_INCLUDED_SRC_runtime_UIDnDDataObject_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/critsect.h>
+
+#include <QString>
+#include <QStringList>
+#include <QVariant>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CDndSource.h"
+#include "CSession.h"
+
+/* Forward declarations: */
+class UIDnDHandler;
+
+class UIDnDDataObject : public IDataObject
+{
+public:
+
+ enum Status
+ {
+ Status_Uninitialized = 0,
+ Status_Initialized,
+ Status_Dropping,
+ Status_Dropped,
+ Status_Aborted,
+ Status_32Bit_Hack = 0x7fffffff
+ };
+
+public:
+
+ UIDnDDataObject(UIDnDHandler *pDnDHandler, const QStringList &lstFormats);
+ virtual ~UIDnDDataObject(void);
+
+public: /* IUnknown methods. */
+
+ STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+
+public: /* IDataObject methods. */
+
+ STDMETHOD(GetData)(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium);
+ STDMETHOD(GetDataHere)(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium);
+ STDMETHOD(QueryGetData)(LPFORMATETC pFormatEtc);
+ STDMETHOD(GetCanonicalFormatEtc)(LPFORMATETC pFormatEtc, LPFORMATETC pFormatEtcOut);
+ STDMETHOD(SetData)(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium, BOOL fRelease);
+ STDMETHOD(EnumFormatEtc)(DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc);
+ STDMETHOD(DAdvise)(LPFORMATETC pFormatEtc, DWORD fAdvise, IAdviseSink *pAdvSink, DWORD *pdwConnection);
+ STDMETHOD(DUnadvise)(DWORD dwConnection);
+ STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppEnumAdvise);
+
+public:
+
+ static const char *ClipboardFormatToString(CLIPFORMAT fmt);
+
+ int Abort(void);
+ void Signal(void);
+ int Signal(const QString &strFormat, const void *pvData, uint32_t cbData);
+
+protected:
+
+ void SetStatus(Status enmStatus);
+
+ bool LookupFormatEtc(LPFORMATETC pFormatEtc, ULONG *puIndex);
+ void RegisterFormat(LPFORMATETC pFormatEtc, CLIPFORMAT clipFormat, TYMED tyMed = TYMED_HGLOBAL,
+ LONG lindex = -1, DWORD dwAspect = DVASPECT_CONTENT, DVTARGETDEVICE *pTargetDevice = NULL);
+
+ /** Pointe rto drag and drop handler. */
+ UIDnDHandler *m_pDnDHandler;
+ /** Current drag and drop status. */
+ Status m_enmStatus;
+ /** Internal reference count of this object. */
+ LONG m_cRefs;
+ /** Number of native formats registered. This can be a different number than supplied with m_lstFormats. */
+ ULONG m_cFormats;
+ /** Array of registered FORMATETC structs. Matches m_cFormats. */
+ FORMATETC *m_pFormatEtc;
+ /** Array of registered STGMEDIUM structs. Matches m_cFormats. */
+ STGMEDIUM *m_pStgMedium;
+ /** Event semaphore used for waiting on status changes. */
+ RTSEMEVENT m_SemEvent;
+ /** List of supported formats. */
+ QStringList m_lstFormats;
+ /** Format of currently retrieved data. */
+ QString m_strFormat;
+ /** The retrieved data as a QVariant. Needed for buffering in case a second format needs the same data,
+ * e.g. CF_TEXT and CF_UNICODETEXT. */
+ QVariant m_vaData;
+ /** Whether the data already was retrieved or not. */
+ bool m_fDataRetrieved;
+ /** The retrieved data as a raw buffer. */
+ void *m_pvData;
+ /** Raw buffer size (in bytes). */
+ uint32_t m_cbData;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIDnDDataObject_win_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDropSource_win.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDropSource_win.cpp
new file mode 100644
index 00000000..e573c0c9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDropSource_win.cpp
@@ -0,0 +1,176 @@
+/* $Id: UIDnDDropSource_win.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDnDDropSource class implementation for Windows. This implements
+ * the IDropSource interface.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#undef LOG_GROUP
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+#include <VBox/log.h>
+
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+
+/* Qt includes: */
+#include <QApplication>
+
+/* Windows includes: */
+#include <QApplication>
+#include <iprt/win/windows.h>
+#include <new> /* For bad_alloc. */
+
+#include "UIDnDDropSource_win.h"
+#include "UIDnDDataObject_win.h"
+
+UIDnDDropSource::UIDnDDropSource(QWidget *pParent, UIDnDDataObject *pDataObject)
+ : m_pParent(pParent)
+ , m_pDataObject(pDataObject)
+ , m_cRefCount(1)
+ , m_dwCurEffect(DROPEFFECT_NONE)
+ , m_uCurAction(Qt::IgnoreAction)
+{
+ LogFlowFunc(("pParent=0x%p\n", m_pParent));
+}
+
+UIDnDDropSource::~UIDnDDropSource(void)
+{
+ LogFlowFunc(("mRefCount=%RU32\n", m_cRefCount));
+}
+
+/*
+ * IUnknown methods.
+ */
+
+STDMETHODIMP_(ULONG) UIDnDDropSource::AddRef(void)
+{
+ LogFlowFunc(("mRefCount=%RU32\n", m_cRefCount + 1));
+ return (ULONG)InterlockedIncrement(&m_cRefCount);
+}
+
+STDMETHODIMP_(ULONG) UIDnDDropSource::Release(void)
+{
+ Assert(m_cRefCount > 0);
+ LogFlowFunc(("mRefCount=%RU32\n", m_cRefCount - 1));
+ LONG lCount = InterlockedDecrement(&m_cRefCount);
+ if (lCount <= 0)
+ {
+ delete this;
+ return 0;
+ }
+
+ return (ULONG)lCount;
+}
+
+STDMETHODIMP UIDnDDropSource::QueryInterface(REFIID iid, void **ppvObject)
+{
+ AssertPtrReturn(ppvObject, E_INVALIDARG);
+
+ if ( iid == IID_IDropSource
+ || iid == IID_IUnknown)
+ {
+ AddRef();
+ *ppvObject = this;
+ return S_OK;
+ }
+
+ *ppvObject = 0;
+ return E_NOINTERFACE;
+}
+
+/*
+ * IDropSource methods.
+ */
+
+/**
+ * The system informs us about whether we should continue the drag'n drop
+ * operation or not, depending on the sent key states.
+ *
+ * @return HRESULT
+ */
+STDMETHODIMP UIDnDDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD dwKeyState)
+{
+ LogFlowFunc(("fEscapePressed=%RTbool, dwKeyState=0x%x, m_dwCurEffect=%RI32, m_uCurAction=%RU32\n",
+ RT_BOOL(fEscapePressed), dwKeyState, m_dwCurEffect, m_uCurAction));
+
+ /* ESC pressed? Bail out. */
+ if (fEscapePressed)
+ {
+ m_dwCurEffect = DROPEFFECT_NONE;
+ m_uCurAction = Qt::IgnoreAction;
+
+ LogRel2(("DnD: User cancelled dropping data to the host\n"));
+ return DRAGDROP_S_CANCEL;
+ }
+
+ bool fDropContent = false;
+
+ /* Left mouse button released? Start "drop" action. */
+ if ((dwKeyState & MK_LBUTTON) == 0)
+ fDropContent = true;
+ /** @todo Make this configurable? */
+
+ if (fDropContent)
+ {
+ if (m_pDataObject)
+ m_pDataObject->Signal();
+
+ LogRel2(("DnD: User dropped data to the host\n"));
+ return DRAGDROP_S_DROP;
+ }
+
+ QApplication::processEvents();
+
+ /* No change, just continue. */
+ return S_OK;
+}
+
+/**
+ * The drop target gives our source feedback about whether
+ * it can handle our data or not.
+ *
+ * @return HRESULT
+ */
+STDMETHODIMP UIDnDDropSource::GiveFeedback(DWORD dwEffect)
+{
+ Qt::DropActions dropActions = Qt::IgnoreAction;
+
+ LogFlowFunc(("dwEffect=0x%x\n", dwEffect));
+ if (dwEffect)
+ {
+ if (dwEffect & DROPEFFECT_COPY)
+ dropActions |= Qt::CopyAction;
+ if (dwEffect & DROPEFFECT_MOVE)
+ dropActions |= Qt::MoveAction;
+ if (dwEffect & DROPEFFECT_LINK)
+ dropActions |= Qt::LinkAction;
+
+ m_dwCurEffect = dwEffect;
+ }
+
+ m_uCurAction = dropActions;
+
+ return DRAGDROP_S_USEDEFAULTCURSORS;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDropSource_win.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDropSource_win.h
new file mode 100644
index 00000000..be31bffb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDropSource_win.h
@@ -0,0 +1,79 @@
+/* $Id: UIDnDDropSource_win.h $ */
+/** @file
+ * VBox Qt GUI - UIDnDDropSource class declaration.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIDnDDropSource_win_h
+#define FEQT_INCLUDED_SRC_runtime_UIDnDDropSource_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* COM includes: */
+#include "COMEnums.h"
+
+class UIDnDDataObject;
+
+/**
+ * Implementation of IDropSource for drag and drop on the host.
+ */
+class UIDnDDropSource : public IDropSource
+{
+public:
+
+ UIDnDDropSource(QWidget *pParent, UIDnDDataObject *pDataObject);
+ virtual ~UIDnDDropSource(void);
+
+public:
+
+ uint32_t GetCurrentAction(void) const { return m_uCurAction; }
+
+public: /* IUnknown methods. */
+
+ STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+
+public: /* IDropSource methods. */
+
+ STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD dwKeyState);
+ STDMETHOD(GiveFeedback)(DWORD dwEffect);
+
+protected:
+
+ /** Pointer to parent widget. */
+ QWidget *m_pParent;
+ /** Pointer to current data object. */
+ UIDnDDataObject *m_pDataObject;
+ /** The current reference count. */
+ LONG m_cRefCount;
+ /** Current (last) drop effect issued. */
+ DWORD m_dwCurEffect;
+ /** Current drop action to perform in case of a successful drop. */
+ Qt::DropActions m_uCurAction;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIDnDDropSource_win_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDEnumFormat_win.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDEnumFormat_win.cpp
new file mode 100644
index 00000000..a8335e34
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDEnumFormat_win.cpp
@@ -0,0 +1,201 @@
+/* $Id: UIDnDEnumFormat_win.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDnDEnumFormat class implementation. This class implements the
+ * IEnumFORMATETC ("Format et cetera") interface.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+#include <VBox/log.h>
+
+#include <iprt/win/windows.h>
+#include <new> /* For bad_alloc. */
+
+#include "UIDnDEnumFormat_win.h"
+#include "UIDnDDataObject_win.h"
+
+
+UIDnDEnumFormatEtc::UIDnDEnumFormatEtc(FORMATETC *pFormatEtc, ULONG cFormats)
+ : m_lRefCount(1),
+ m_nIndex(0)
+{
+ HRESULT hr;
+
+ try
+ {
+ LogFlowFunc(("pFormatEtc=%p, cFormats=%RU32\n", pFormatEtc, cFormats));
+ if (cFormats)
+ m_pFormatEtc = new FORMATETC[cFormats];
+
+ for (ULONG i = 0; i < cFormats; i++)
+ {
+ LogFlowFunc(("Format %RU32: cfFormat=%RI16, sFormat=%s, tyMed=%RU32, dwAspect=%RU32\n",
+ i, pFormatEtc[i].cfFormat, UIDnDDataObject::ClipboardFormatToString(pFormatEtc[i].cfFormat),
+ pFormatEtc[i].tymed, pFormatEtc[i].dwAspect));
+ UIDnDEnumFormatEtc::CopyFormat(&m_pFormatEtc[i], &pFormatEtc[i]);
+ }
+
+ m_nNumFormats = cFormats;
+ hr = S_OK;
+ }
+ catch (std::bad_alloc &)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ LogFlowFunc(("hr=%Rhrc\n", hr));
+}
+
+UIDnDEnumFormatEtc::~UIDnDEnumFormatEtc(void)
+{
+ if (m_pFormatEtc)
+ {
+ for (ULONG i = 0; i < m_nNumFormats; i++)
+ {
+ if(m_pFormatEtc[i].ptd)
+ CoTaskMemFree(m_pFormatEtc[i].ptd);
+ }
+
+ delete[] m_pFormatEtc;
+ m_pFormatEtc = NULL;
+ }
+
+ LogFlowFunc(("m_lRefCount=%RI32\n", m_lRefCount));
+}
+
+/*
+ * IUnknown methods.
+ */
+
+STDMETHODIMP_(ULONG) UIDnDEnumFormatEtc::AddRef(void)
+{
+ return InterlockedIncrement(&m_lRefCount);
+}
+
+STDMETHODIMP_(ULONG) UIDnDEnumFormatEtc::Release(void)
+{
+ LONG lCount = InterlockedDecrement(&m_lRefCount);
+ if (lCount == 0)
+ {
+ delete this;
+ return 0;
+ }
+
+ return lCount;
+}
+
+STDMETHODIMP UIDnDEnumFormatEtc::QueryInterface(REFIID iid, void **ppvObject)
+{
+ if ( iid == IID_IEnumFORMATETC
+ || iid == IID_IUnknown)
+ {
+ AddRef();
+ *ppvObject = this;
+ return S_OK;
+ }
+
+ *ppvObject = 0;
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP UIDnDEnumFormatEtc::Next(ULONG cFormats, FORMATETC *pFormatEtc, ULONG *pcFetched)
+{
+ ULONG ulCopied = 0;
+
+ if(cFormats == 0 || pFormatEtc == 0)
+ return E_INVALIDARG;
+
+ while ( m_nIndex < m_nNumFormats
+ && ulCopied < cFormats)
+ {
+ UIDnDEnumFormatEtc::CopyFormat(&pFormatEtc[ulCopied],
+ &m_pFormatEtc[m_nIndex]);
+ ulCopied++;
+ m_nIndex++;
+ }
+
+ if (pcFetched)
+ *pcFetched = ulCopied;
+
+ return (ulCopied == cFormats) ? S_OK : S_FALSE;
+}
+
+STDMETHODIMP UIDnDEnumFormatEtc::Skip(ULONG cFormats)
+{
+ m_nIndex += cFormats;
+ return (m_nIndex <= m_nNumFormats) ? S_OK : S_FALSE;
+}
+
+STDMETHODIMP UIDnDEnumFormatEtc::Reset(void)
+{
+ m_nIndex = 0;
+ return S_OK;
+}
+
+STDMETHODIMP UIDnDEnumFormatEtc::Clone(IEnumFORMATETC **ppEnumFormatEtc)
+{
+ HRESULT hResult =
+ CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc, ppEnumFormatEtc);
+
+ if (hResult == S_OK)
+ ((UIDnDEnumFormatEtc *) *ppEnumFormatEtc)->m_nIndex = m_nIndex;
+
+ return hResult;
+}
+
+/* static */
+void UIDnDEnumFormatEtc::CopyFormat(FORMATETC *pDest, FORMATETC *pSource)
+{
+ AssertPtrReturnVoid(pDest);
+ AssertPtrReturnVoid(pSource);
+
+ *pDest = *pSource;
+
+ if (pSource->ptd)
+ {
+ pDest->ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(sizeof(DVTARGETDEVICE));
+ *(pDest->ptd) = *(pSource->ptd);
+ }
+}
+
+/* static */
+HRESULT UIDnDEnumFormatEtc::CreateEnumFormatEtc(UINT nNumFormats, FORMATETC *pFormatEtc, IEnumFORMATETC **ppEnumFormatEtc)
+{
+ AssertPtrReturn(pFormatEtc, E_INVALIDARG);
+ AssertPtrReturn(ppEnumFormatEtc, E_INVALIDARG);
+
+ HRESULT hr;
+ try
+ {
+ *ppEnumFormatEtc = new UIDnDEnumFormatEtc(pFormatEtc, nNumFormats);
+ hr = S_OK;
+ }
+ catch(std::bad_alloc &)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ return hr;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDEnumFormat_win.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDEnumFormat_win.h
new file mode 100644
index 00000000..a4316209
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDEnumFormat_win.h
@@ -0,0 +1,67 @@
+/* $Id: UIDnDEnumFormat_win.h $ */
+/** @file
+ * VBox Qt GUI - UIDnDEnumFormat class declaration.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIDnDEnumFormat_win_h
+#define FEQT_INCLUDED_SRC_runtime_UIDnDEnumFormat_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+class UIDnDEnumFormatEtc : public IEnumFORMATETC
+{
+public:
+
+ UIDnDEnumFormatEtc(FORMATETC *pFormatEtc, ULONG cFormats);
+ virtual ~UIDnDEnumFormatEtc(void);
+
+public:
+
+ STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+
+ STDMETHOD(Next)(ULONG cFormats, FORMATETC *pFormatEtc, ULONG *pcFetched);
+ STDMETHOD(Skip)(ULONG cFormats);
+ STDMETHOD(Reset)(void);
+ STDMETHOD(Clone)(IEnumFORMATETC ** ppEnumFormatEtc);
+
+public:
+
+ static void CopyFormat(FORMATETC *pFormatDest, FORMATETC *pFormatSource);
+ static HRESULT CreateEnumFormatEtc(UINT cFormats, FORMATETC *pFormatEtc, IEnumFORMATETC **ppEnumFormatEtc);
+
+private:
+
+ LONG m_lRefCount;
+ ULONG m_nIndex;
+ ULONG m_nNumFormats;
+ FORMATETC * m_pFormatEtc;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIDnDEnumFormat_win_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp
new file mode 100644
index 00000000..5896d8a3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp
@@ -0,0 +1,912 @@
+/* $Id: UIDnDHandler.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDnDHandler class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QDrag>
+#include <QKeyEvent>
+#include <QStringList>
+#include <QTimer>
+#include <QUrl>
+#include <QWidget>
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+/* GUI includes: */
+#include "UIDnDHandler.h"
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+#include "CDnDSource.h"
+#ifdef RT_OS_WINDOWS
+# include "UIDnDDataObject_win.h"
+# include "UIDnDDropSource_win.h"
+#endif
+#include "UIDnDMIMEData.h"
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+#include "UIMessageCenter.h"
+#include "UISession.h"
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CGuest.h"
+#include "CGuestDnDSource.h"
+#include "CGuestDnDTarget.h"
+#include "CSession.h"
+
+#ifdef LOG_GROUP
+ #undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+#include <VBox/log.h>
+#include <iprt/err.h>
+
+#if 0
+# ifdef DEBUG
+# include <QTextStream>
+# include <QFile>
+/** Enable this to log debug output of a Qt debug build to a file defined by DEBUG_DND_QT_LOGFILE. */
+# define DEBUG_DND_QT
+# ifdef RT_OS_WINDOWS
+# define DEBUG_DND_QT_LOGFILE "c:\\temp\\VBoxQt.log"
+# else
+# define DEBUG_DND_QT_LOGFILE "/var/tmp/vboxqt.log"
+# endif
+# endif /* DEBUG */
+#endif
+
+
+UIDnDHandler::UIDnDHandler(UISession *pSession, QWidget *pParent)
+ : m_pSession(pSession)
+ , m_pParent(pParent)
+ , m_fDataRetrieved(false)
+#ifdef RT_OS_WINDOWS
+ , m_dwIntegrityLevel(0)
+#else
+ , m_pMIMEData(NULL)
+#endif
+{
+ AssertPtr(pSession);
+ m_dndSource = static_cast<CDnDSource>(pSession->guest().GetDnDSource());
+ m_dndTarget = static_cast<CDnDTarget>(pSession->guest().GetDnDTarget());
+}
+
+UIDnDHandler::~UIDnDHandler(void)
+{
+}
+
+/*
+ * Frontend -> Target.
+ */
+
+Qt::DropAction UIDnDHandler::dragEnter(ulong screenID, int x, int y,
+ Qt::DropAction proposedAction, Qt::DropActions possibleActions,
+ const QMimeData *pMimeData)
+{
+ LogFlowFunc(("screenID=%RU32, x=%d, y=%d, action=%ld\n", screenID, x, y, toVBoxDnDAction(proposedAction)));
+
+ /* Ask the guest for starting a DnD event. */
+ KDnDAction result = m_dndTarget.Enter(screenID,
+ x,
+ y,
+ toVBoxDnDAction(proposedAction),
+ toVBoxDnDActions(possibleActions),
+ pMimeData->formats().toVector());
+ if (m_dndTarget.isOk())
+ {
+ return toQtDnDAction(result);
+ }
+
+ msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
+ return Qt::IgnoreAction;
+}
+
+Qt::DropAction UIDnDHandler::dragMove(ulong screenID, int x, int y,
+ Qt::DropAction proposedAction, Qt::DropActions possibleActions,
+ const QMimeData *pMimeData)
+{
+ LogFlowFunc(("screenID=%RU32, x=%d, y=%d, action=%ld\n", screenID, x, y, toVBoxDnDAction(proposedAction)));
+
+ if (!m_dndTarget.isOk()) /* Don't try any further if we got an error before. */
+ return Qt::IgnoreAction;
+
+ /* Notify the guest that the mouse has been moved while doing
+ * a drag'n drop operation. */
+ KDnDAction result = m_dndTarget.Move(screenID,
+ x,
+ y,
+ toVBoxDnDAction(proposedAction),
+ toVBoxDnDActions(possibleActions),
+ pMimeData->formats().toVector());
+ if (m_dndTarget.isOk())
+ return toQtDnDAction(result);
+
+ msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
+ return Qt::IgnoreAction;
+}
+
+Qt::DropAction UIDnDHandler::dragDrop(ulong screenID, int x, int y,
+ Qt::DropAction proposedAction, Qt::DropActions possibleActions,
+ const QMimeData *pMimeData)
+{
+ LogFlowFunc(("screenID=%RU32, x=%d, y=%d, action=%ld\n", screenID, x, y, toVBoxDnDAction(proposedAction)));
+
+ if (!m_dndTarget.isOk()) /* Don't try any further if we got an error before. */
+ return Qt::IgnoreAction;
+
+ /* The format the guest requests. */
+ QString strFormat;
+ /* Ask the guest for dropping data. */
+ KDnDAction enmResult = m_dndTarget.Drop(screenID,
+ x,
+ y,
+ toVBoxDnDAction(proposedAction),
+ toVBoxDnDActions(possibleActions),
+ pMimeData->formats().toVector(), strFormat);
+ if (!m_dndTarget.isOk())
+ {
+ msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
+ }
+ else if (enmResult != KDnDAction_Ignore) /* Has the guest accepted the drop event? */
+ {
+ LogRel2(("DnD: Guest requested format '%s'\n", strFormat.toUtf8().constData()));
+ LogRel2(("DnD: The host offered %d formats:\n", pMimeData->formats().size()));
+
+#if 0
+ QStringList::const_iterator itFmt = pMimeData->formats().constBegin();
+ while (itFmt != pMimeData->formats().constEnd())
+ {
+ LogRel2(("DnD:\t'%s'\n", (*itFmt).toUtf8().constData()));
+ itFmt++;
+ }
+#endif
+ QByteArray arrBytes;
+
+ /*
+ * Does the host support the format requested by the guest?
+ * Lookup the format in the MIME data object.
+ */
+ AssertPtr(pMimeData);
+ if (pMimeData->formats().indexOf(strFormat) >= 0)
+ {
+ arrBytes = pMimeData->data(strFormat);
+ Assert(!arrBytes.isEmpty());
+ }
+ /*
+ * The host does not support the format requested by the guest.
+ * This can happen if the host wants to send plan text, for example, but
+ * the guest requested something else, e.g. an URI list.
+ *
+ * In that case dictate the guest to use a fixed format from the host,
+ * so instead returning the requested URI list, return the original
+ * data format from the host. The guest has to try to deal with that then.
+ **/
+ else
+ {
+ if (pMimeData->hasText())
+ {
+ LogRel2(("DnD: Converting data to text ...\n"));
+ arrBytes = pMimeData->text().toUtf8();
+ strFormat = "text/plain;charset=utf-8";
+ }
+ else
+ {
+ LogRel(("DnD: Host formats did not offer a matching format for the guest, skipping\n"));
+ enmResult = KDnDAction_Ignore;
+ }
+ }
+
+ if (arrBytes.size()) /* Anything to send? */
+ {
+ /* Convert data to a vector. */
+ QVector<uint8_t> vecData(arrBytes.size()); /** @todo Can this throw or anything? */
+ AssertReleaseMsg(vecData.size() == arrBytes.size(), ("Drag and drop format buffer size does not match"));
+ memcpy(vecData.data(), arrBytes.constData(), arrBytes.size());
+
+ /* Send data to the guest. */
+ LogRel2(("DnD: Host is sending %d bytes of data as '%s'\n", vecData.size(), strFormat.toUtf8().constData()));
+ CProgress progress = m_dndTarget.SendData(screenID, strFormat, vecData);
+
+ if (m_dndTarget.isOk())
+ {
+ msgCenter().showModalProgressDialog(progress,
+ tr("Dropping data ..."), ":/progress_dnd_hg_90px.png",
+ m_pParent);
+
+ LogFlowFunc(("Transfer fCompleted=%RTbool, fCanceled=%RTbool, hr=%Rhrc\n",
+ progress.GetCompleted(), progress.GetCanceled(), progress.GetResultCode()));
+
+ BOOL fCanceled = progress.GetCanceled();
+ if ( !fCanceled
+ && ( !progress.isOk()
+ || progress.GetResultCode() != 0))
+ {
+ msgCenter().cannotDropDataToGuest(progress, m_pParent);
+ enmResult = KDnDAction_Ignore;
+ }
+ }
+ else
+ {
+ msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
+ enmResult = KDnDAction_Ignore;
+ }
+ }
+ else /* Error. */
+ enmResult = KDnDAction_Ignore;
+ }
+
+ return toQtDnDAction(enmResult);
+}
+
+void UIDnDHandler::dragLeave(ulong screenID)
+{
+ LogFlowFunc(("screenID=%RU32\n", screenID));
+
+ if (!m_dndTarget.isOk()) /* Don't try any further if we got an error before. */
+ return;
+
+ m_dndTarget.Leave(screenID);
+ if (m_dndTarget.isOk())
+ return;
+
+ msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
+ return;
+}
+
+#ifdef DEBUG_DND_QT
+QTextStream *g_pStrmLogQt = NULL; /* Output stream for Qt debug logging. */
+
+/* static */
+void UIDnDHandler::debugOutputQt(QtMsgType type, const QMessageLogContext &context, const QString &strMessage)
+{
+ QString strMsg;
+ switch (type)
+ {
+ case QtWarningMsg:
+ strMsg += "[W]";
+ break;
+ case QtCriticalMsg:
+ strMsg += "[C]";
+ break;
+ case QtFatalMsg:
+ strMsg += "[F]";
+ break;
+ case QtDebugMsg:
+ default:
+ strMsg += "[D]";
+ break;
+ }
+
+ if (g_pStrmLogQt)
+ (*g_pStrmLogQt) << strMsg << " " << strMessage << endl;
+}
+#endif /* DEBUG_DND_QT */
+
+/*
+ * Source -> Frontend.
+ */
+
+int UIDnDHandler::dragStartInternal(const QStringList &lstFormats,
+ Qt::DropAction defAction, Qt::DropActions actions)
+{
+ RT_NOREF(defAction);
+
+ int rc = VINF_SUCCESS;
+
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+
+ LogFlowFunc(("defAction=0x%x\n", defAction));
+ LogFlowFunc(("Number of formats: %d\n", lstFormats.size()));
+# ifdef DEBUG
+ for (int i = 0; i < lstFormats.size(); i++)
+ LogFlowFunc(("\tFormat %d: %s\n", i, lstFormats.at(i).toUtf8().constData()));
+# endif
+
+# ifdef DEBUG_DND_QT
+ QFile *pFileDebugQt = new QFile(DEBUG_DND_QT_LOGFILE);
+ if (pFileDebugQt->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
+ {
+ g_pStrmLogQt = new QTextStream(pFileDebugQt);
+
+ qInstallMessageHandler(UIDnDHandler::debugOutputQt);
+ qDebug("========================================================================");
+ }
+# endif
+
+# ifdef RT_OS_WINDOWS
+ UIDnDDataObject *pDataObject = new UIDnDDataObject(this, lstFormats);
+ if (!pDataObject)
+ return VERR_NO_MEMORY;
+ UIDnDDropSource *pDropSource = new UIDnDDropSource(m_pParent, pDataObject);
+ if (!pDropSource)
+ return VERR_NO_MEMORY;
+
+ DWORD dwOKEffects = DROPEFFECT_NONE;
+ if (actions)
+ {
+ if (actions & Qt::CopyAction)
+ dwOKEffects |= DROPEFFECT_COPY;
+ if (actions & Qt::MoveAction)
+ dwOKEffects |= DROPEFFECT_MOVE;
+ if (actions & Qt::LinkAction)
+ dwOKEffects |= DROPEFFECT_LINK;
+ }
+
+ DWORD dwEffect;
+ LogRel2(("DnD: Starting drag and drop operation\n", dwOKEffects));
+ LogRel3(("DnD: DoDragDrop dwOKEffects=0x%x\n", dwOKEffects));
+ HRESULT hr = ::DoDragDrop(pDataObject, pDropSource, dwOKEffects, &dwEffect);
+ LogRel3(("DnD: DoDragDrop ended with hr=%Rhrc, dwEffect=%RI32\n", hr, dwEffect));
+
+ if (pDropSource)
+ pDropSource->Release();
+ if (pDataObject)
+ pDataObject->Release();
+
+# else /* !RT_OS_WINDOWS */
+
+ QDrag *pDrag = new QDrag(m_pParent);
+ if (!pDrag)
+ return VERR_NO_MEMORY;
+
+ /* Note: pMData is transferred to the QDrag object, so no need for deletion. */
+ m_pMIMEData = new UIDnDMIMEData(this, lstFormats, defAction, actions);
+ if (!m_pMIMEData)
+ {
+ delete pDrag;
+ return VERR_NO_MEMORY;
+ }
+
+ /* Inform the MIME data object of any changes in the current action. */
+ connect(pDrag, &QDrag::actionChanged,
+ m_pMIMEData, &UIDnDMIMEData::sltDropActionChanged);
+
+ /* Invoke this handler as data needs to be retrieved by our derived QMimeData class. */
+ connect(m_pMIMEData, &UIDnDMIMEData::sigGetData,
+ this, &UIDnDHandler::sltGetData);
+
+ /*
+ * Set MIME data object and start the (modal) drag'n drop operation on the host.
+ * This does not block Qt's event loop, however (on Windows it would).
+ */
+ pDrag->setMimeData(m_pMIMEData);
+ LogFlowFunc(("Executing modal drag'n drop operation ...\n"));
+
+ Qt::DropAction dropAction;
+# ifdef RT_OS_DARWIN
+# ifdef VBOX_WITH_DRAG_AND_DROP_PROMISES
+ dropAction = pDrag->exec(actions, defAction);
+# else
+ /* Without having VBOX_WITH_DRAG_AND_DROP_PROMISES enabled drag and drop
+ * will not work on OS X! It also requires some handcrafted patches within Qt
+ * (which also needs VBOX_WITH_DRAG_AND_DROP_PROMISES set there). */
+ dropAction = Qt::IgnoreAction;
+ rc = VERR_NOT_SUPPORTED;
+# endif
+# else /* !RT_OS_DARWIN */
+ dropAction = pDrag->exec(actions, defAction);
+# endif /* RT_OS_DARWIN */
+ LogRel3(("DnD: Ended with dropAction=%ld\n", UIDnDHandler::toVBoxDnDAction(dropAction)));
+
+ /* Note: The UIDnDMimeData object will not be not accessible here anymore,
+ * since QDrag had its ownership and deleted it after the (blocking)
+ * QDrag::exec() call. */
+
+ /* pDrag will be cleaned up by Qt automatically. */
+
+# endif /* !RT_OS_WINDOWS */
+
+ reset();
+
+#ifdef DEBUG_DND_QT
+ if (g_pStrmLogQt)
+ {
+ delete g_pStrmLogQt;
+ g_pStrmLogQt = NULL;
+ }
+
+ if (pFileDebugQt)
+ {
+ pFileDebugQt->close();
+ delete pFileDebugQt;
+ }
+#endif /* DEBUG_DND_QT */
+
+#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
+ rc = VERR_NOT_SUPPORTED;
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Checks whether a G->H drag'n drop operation is pending.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NO_DATA if no operation is pending or an error has occurred.
+ * @retval VERR_NOT_SUPPORTED if G->H operations are not supported.
+ * @param screenID Screen ID to check pending status.
+ */
+int UIDnDHandler::dragCheckPending(ulong screenID)
+{
+ int rc;
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ LogFlowFunc(("screenID=%RU32\n", screenID));
+
+ /**
+ * How this works: Source is asking the target if there is any DnD
+ * operation pending, when the mouse leaves the guest window. On
+ * return there is some info about a running DnD operation
+ * (or defaultAction is KDnDAction_Ignore if not). With
+ * this information we create a Qt QDrag object with our own QMimeType
+ * implementation and call exec.
+ *
+ * Note: This function *blocks* until the actual drag'n drop operation
+ * has been finished (successfully or not)!
+ */
+ CGuest guest = m_pSession->guest();
+
+ /* Clear our current data set. */
+ m_dataSource.lstFormats.clear();
+ m_dataSource.vecActions.clear();
+
+ /* Ask the guest if there is a drag and drop operation pending (on the guest). */
+ QVector<QString> vecFormats;
+ m_dataSource.defaultAction = m_dndSource.DragIsPending(screenID, vecFormats, m_dataSource.vecActions);
+ if (!m_dndSource.isOk())
+ {
+ msgCenter().cannotDropDataToHost(m_dndSource, m_pParent);
+ return VERR_NO_DATA;
+ }
+
+ LogRelMax3(10, ("DnD: Default action is: 0x%x\n", m_dataSource.defaultAction));
+ LogRelMax3(10, ("DnD: Number of supported guest actions: %d\n", m_dataSource.vecActions.size()));
+ for (int i = 0; i < m_dataSource.vecActions.size(); i++)
+ LogRelMax3(10, ("DnD: \tAction %d: 0x%x\n", i, m_dataSource.vecActions.at(i)));
+
+ LogRelMax3(10, ("DnD: Number of supported guest formats: %d\n", vecFormats.size()));
+ for (int i = 0; i < vecFormats.size(); i++)
+ {
+ const QString &strFmtGuest = vecFormats.at(i);
+ LogRelMax3(10, ("DnD: \tFormat %d: %s\n", i, strFmtGuest.toUtf8().constData()));
+ }
+
+ LogFlowFunc(("defaultAction=0x%x, vecFormatsSize=%d, vecActionsSize=%d\n",
+ m_dataSource.defaultAction, vecFormats.size(), m_dataSource.vecActions.size()));
+
+ if ( m_dataSource.defaultAction != KDnDAction_Ignore
+ && vecFormats.size())
+ {
+ for (int i = 0; i < vecFormats.size(); i++)
+ {
+ const QString &strFormat = vecFormats.at(i);
+ m_dataSource.lstFormats << strFormat;
+ }
+
+ rc = VINF_SUCCESS; /* There's a valid pending drag and drop operation on the guest. */
+ }
+ else /* No format data from the guest arrived yet. */
+ rc = VERR_NO_DATA;
+
+#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
+ RT_NOREF(screenID);
+ rc = VERR_NOT_SUPPORTED;
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int UIDnDHandler::dragStart(ulong screenID)
+{
+ int rc;
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+
+ RT_NOREF(screenID);
+
+ LogFlowFuncEnter();
+
+ /* Sanity checks. */
+ if ( !m_dataSource.lstFormats.size()
+ || m_dataSource.defaultAction == KDnDAction_Ignore
+ || !m_dataSource.vecActions.size())
+ {
+ return VERR_INVALID_PARAMETER;
+ }
+
+ rc = dragStartInternal(m_dataSource.lstFormats,
+ toQtDnDAction(m_dataSource.defaultAction), toQtDnDActions(m_dataSource.vecActions));
+
+#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
+ RT_NOREF(screenID);
+ rc = VERR_NOT_SUPPORTED;
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int UIDnDHandler::dragStop(ulong screenID)
+{
+ RT_NOREF(screenID);
+ int rc;
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ reset();
+ rc = VINF_SUCCESS;
+#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
+ rc = VERR_NOT_SUPPORTED;
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Initializes the drag'n drop UI handler.
+ *
+ * @returns VBox status code.
+ */
+int UIDnDHandler::init(void)
+{
+ int vrc;
+#ifdef RT_OS_WINDOWS
+
+# define CASE_INTEGRITY_LEVEL(a_Level) \
+ case a_Level: \
+ LogRel(("DnD: User Interface Privilege Isolation (UIPI) is running with %s\n", #a_Level)); \
+ break;
+
+ /*
+ * Assign and log the current process integrity interity level, so that we have a better chance of diagnosing issues
+ * when it comes to drag'n drop and UIPI (User Interface Privilege Isolation) -- a lower integrity level process
+ * cannot drag'n drop stuff to a higher integrity level one.
+ */
+ vrc = getProcessIntegrityLevel(&m_dwIntegrityLevel);
+ if (RT_SUCCESS(vrc))
+ {
+ switch (m_dwIntegrityLevel)
+ {
+ CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_UNTRUSTED_RID);
+ CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_LOW_RID);
+ CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_MEDIUM_RID);
+ CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_HIGH_RID);
+ CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_SYSTEM_RID);
+ CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_PROTECTED_PROCESS_RID);
+ default:
+ break;
+ }
+
+ if (m_dwIntegrityLevel > SECURITY_MANDATORY_MEDIUM_RID)
+ LogRel(("DnD: Warning: The VM process' integrity level is higher than most regular processes on the system. "
+ "This means that drag'n drop most likely will not work with other applications!\n"));
+ }
+ else
+ LogRel(("DnD: Unable to retrieve process integrity level (%Rrc) -- please report this bug!\n", vrc));
+# undef CASE_INTEGRITY_LEVEL
+
+#else /* ! RT_OS_WINDOWS */
+ vrc = VINF_SUCCESS;
+#endif /* RT_OS_WINDOWS */
+
+ return vrc;
+}
+
+void UIDnDHandler::reset(void)
+{
+ LogFlowFuncEnter();
+
+ m_fDataRetrieved = false;
+
+#ifdef RT_OS_WINDOWS
+ m_dwIntegrityLevel = 0;
+#endif
+}
+
+#ifdef RT_OS_WINDOWS
+/**
+ * Returns the process' current integrity level.
+ *
+ * @returns VBox status code.
+ * @param pdwIntegrityLevel Where to return the detected process integrity level on success.
+ */
+/* static */
+int UIDnDHandler::getProcessIntegrityLevel(DWORD *pdwIntegrityLevel)
+{
+ AssertPtrReturn(pdwIntegrityLevel, VERR_INVALID_POINTER);
+
+ int vrc = VINF_SUCCESS;
+
+# define PRINT_AND_ASSIGN_LAST_ERROR(a_Msg) \
+ { \
+ dwLastErr = GetLastError(); \
+ vrc = RTErrConvertFromWin32(dwLastErr); \
+ LogRel(("DnD: %s: %Rrc (%#x)\n", a_Msg, vrc, dwLastErr)); \
+ }
+
+ DWORD dwLastErr = 0;
+
+ DWORD cb;
+ HANDLE hToken;
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
+ {
+ PRINT_AND_ASSIGN_LAST_ERROR("OpenProcessToken failed");
+ return vrc;
+ }
+
+ if ( !GetTokenInformation(hToken, TokenIntegrityLevel, NULL, 0, &cb)
+ && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ PSID_AND_ATTRIBUTES pSidAndAttr = (PSID_AND_ATTRIBUTES)RTMemAlloc(cb);
+ if (GetTokenInformation(hToken, TokenIntegrityLevel, pSidAndAttr, cb, &cb))
+ {
+ *pdwIntegrityLevel = *GetSidSubAuthority(pSidAndAttr->Sid, *GetSidSubAuthorityCount(pSidAndAttr->Sid) - 1U);
+ }
+ else
+ PRINT_AND_ASSIGN_LAST_ERROR("GetTokenInformation(2) failed");
+ RTMemFree(pSidAndAttr);
+ }
+ else if ( GetLastError() == ERROR_INVALID_PARAMETER
+ || GetLastError() == ERROR_NOT_SUPPORTED)
+ {
+ /* Should never show, as we at least require Windows 7 nowadays on Windows hosts. */
+ PRINT_AND_ASSIGN_LAST_ERROR("Querying process integrity level not supported");
+ }
+ else
+ PRINT_AND_ASSIGN_LAST_ERROR("GetTokenInformation(1) failed");
+
+# undef PRINT_AND_ASSIGN_LAST_ERROR
+
+ CloseHandle(hToken);
+ return vrc;
+}
+#endif /* RT_OS_WINDOWS */
+
+int UIDnDHandler::retrieveData(Qt::DropAction dropAction,
+ const QString &strMIMEType,
+ QVector<uint8_t> &vecData)
+{
+ /** @todo r=andy Locking required? */
+
+ if (!strMIMEType.compare("application/x-qt-mime-type-name", Qt::CaseInsensitive))
+ return VINF_SUCCESS;
+
+ int rc;
+ if (!m_fDataRetrieved)
+ {
+ /*
+ * Retrieve the actual data from the guest.
+ */
+ rc = retrieveDataInternal(dropAction, strMIMEType, m_vecData);
+ if (RT_FAILURE(rc))
+ LogRel3(("DnD: Receiving data failed: %Rrc\n", rc));
+ else
+ m_fDataRetrieved = true;
+ }
+ else /* Data already received, supply cached version. */
+ rc = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc))
+ vecData = m_vecData;
+
+ return rc;
+}
+
+int UIDnDHandler::retrieveData( Qt::DropAction dropAction,
+ const QString &strMIMEType,
+ QVariant::Type vaType,
+ QVariant &vaData)
+{
+ QVector<uint8_t> vecData;
+ int rc = retrieveData(dropAction, strMIMEType, vecData);
+ if (RT_SUCCESS(rc))
+ {
+ /* If no/an invalid variant is set, try to guess the variant type.
+ * This can happen on OS X. */
+ if (vaType == QVariant::Invalid)
+ vaType = UIDnDMIMEData::getVariantType(strMIMEType);
+
+ rc = UIDnDMIMEData::getDataAsVariant(vecData, strMIMEType, vaType, vaData);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int UIDnDHandler::retrieveDataInternal( Qt::DropAction dropAction,
+ const QString &strMIMEType,
+ QVector<uint8_t> &vecData)
+{
+ LogRel3(("DnD: Retrieving data from guest as '%s' (%d)\n", qPrintable(strMIMEType), dropAction));
+
+ int rc = VINF_SUCCESS;
+
+ /* Indicate to the guest that we have dropped the data on the host.
+ * The guest then will initiate the actual "drop" operation into our proxy on the guest. */
+ Assert(!m_dndSource.isNull());
+ CProgress progress = m_dndSource.Drop(strMIMEType,
+ UIDnDHandler::toVBoxDnDAction(dropAction));
+ LogFlowFunc(("Source: isOk=%RTbool\n", m_dndSource.isOk()));
+ if (m_dndSource.isOk())
+ {
+ /* Send a mouse event with released mouse buttons into the guest that triggers
+ * the "drop" event in our proxy window on the guest. */
+ AssertPtr(m_pSession);
+ m_pSession->mouse().PutMouseEvent(0, 0, 0, 0, 0);
+
+ msgCenter().showModalProgressDialog(progress,
+ tr("Retrieving data ..."), ":/progress_dnd_gh_90px.png",
+ m_pParent);
+
+ LogFlowFunc(("Progress: fCanceled=%RTbool, fCompleted=%RTbool, isOk=%RTbool, hrc=%Rhrc\n",
+ progress.GetCanceled(), progress.GetCompleted(), progress.isOk(), progress.GetResultCode()));
+
+ if (!progress.GetCanceled())
+ {
+ rc = ( progress.isOk()
+ && progress.GetResultCode() == 0)
+ ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
+
+ if (RT_SUCCESS(rc))
+ {
+ /* After we successfully retrieved data from the source we query it from Main. */
+ vecData = m_dndSource.ReceiveData(); /** @todo QVector.size() is "int" only!? */
+ if (m_dndSource.isOk())
+ {
+ if (vecData.isEmpty())
+ rc = VERR_NO_DATA;
+ }
+ else
+ {
+ msgCenter().cannotDropDataToHost(m_dndSource, m_pParent);
+ rc = VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
+ }
+ }
+ else
+ msgCenter().cannotDropDataToHost(progress, m_pParent);
+ }
+ else /* Don't pop up a message. */
+ rc = VERR_CANCELLED;
+ }
+ else
+ {
+ msgCenter().cannotDropDataToHost(m_dndSource, m_pParent);
+ rc = VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int UIDnDHandler::sltGetData( Qt::DropAction dropAction,
+ const QString &strMIMEType,
+ QVariant::Type vaType,
+ QVariant &vaData)
+{
+ int rc = retrieveData(dropAction, strMIMEType, vaType, vaData);
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/*
+ * Drag and Drop helper methods
+ */
+
+/**
+ * Static helper function to convert a Qt drop action to an internal DnD drop action.
+ *
+ * @returns Converted internal drop action.
+ * @param action Qt drop action to convert.
+ */
+/* static */
+KDnDAction UIDnDHandler::toVBoxDnDAction(Qt::DropAction action)
+{
+ if (action == Qt::CopyAction)
+ return KDnDAction_Copy;
+ if (action == Qt::MoveAction)
+ return KDnDAction_Move;
+ if (action == Qt::LinkAction)
+ return KDnDAction_Link;
+
+ return KDnDAction_Ignore;
+}
+
+/**
+ * Static helper function to convert Qt drop actions to internal DnD drop actions.
+ *
+ * @returns Vector of converted internal drop actions.
+ * @param actions Qt drop actions to convert.
+ */
+/* static */
+QVector<KDnDAction> UIDnDHandler::toVBoxDnDActions(Qt::DropActions actions)
+{
+ QVector<KDnDAction> vbActions;
+ if (actions.testFlag(Qt::IgnoreAction))
+ vbActions << KDnDAction_Ignore;
+ if (actions.testFlag(Qt::CopyAction))
+ vbActions << KDnDAction_Copy;
+ if (actions.testFlag(Qt::MoveAction))
+ vbActions << KDnDAction_Move;
+ if (actions.testFlag(Qt::LinkAction))
+ vbActions << KDnDAction_Link;
+
+ return vbActions;
+}
+
+/**
+ * Static helper function to convert an internal drop action to a Qt drop action.
+ *
+ * @returns Converted Qt drop action.
+ * @param actions Internal drop action to convert.
+ */
+/* static */
+Qt::DropAction UIDnDHandler::toQtDnDAction(KDnDAction action)
+{
+ Qt::DropAction dropAct = Qt::IgnoreAction;
+ if (action == KDnDAction_Copy)
+ dropAct = Qt::CopyAction;
+ if (action == KDnDAction_Move)
+ dropAct = Qt::MoveAction;
+ if (action == KDnDAction_Link)
+ dropAct = Qt::LinkAction;
+
+ LogFlowFunc(("dropAct=0x%x\n", dropAct));
+ return dropAct;
+}
+
+/**
+ * Static helper function to convert a vector of internal drop actions to Qt drop actions.
+ *
+ * @returns Converted Qt drop actions.
+ * @param vecActions Internal drop actions to convert.
+ */
+/* static */
+Qt::DropActions UIDnDHandler::toQtDnDActions(const QVector<KDnDAction> &vecActions)
+{
+ Qt::DropActions dropActs = Qt::IgnoreAction;
+ for (int i = 0; i < vecActions.size(); i++)
+ {
+ switch (vecActions.at(i))
+ {
+ case KDnDAction_Ignore:
+ dropActs |= Qt::IgnoreAction;
+ break;
+ case KDnDAction_Copy:
+ dropActs |= Qt::CopyAction;
+ break;
+ case KDnDAction_Move:
+ dropActs |= Qt::MoveAction;
+ break;
+ case KDnDAction_Link:
+ dropActs |= Qt::LinkAction;
+ break;
+ default:
+ break;
+ }
+ }
+
+ LogFlowFunc(("dropActions=0x%x\n", int(dropActs)));
+ return dropActs;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.h
new file mode 100644
index 00000000..f862a76f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.h
@@ -0,0 +1,166 @@
+/* $Id: UIDnDHandler.h $ */
+/** @file
+ * VBox Qt GUI - UIDnDHandler class declaration..
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIDnDHandler_h
+#define FEQT_INCLUDED_SRC_runtime_UIDnDHandler_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMimeData>
+#include <QMutex>
+#include <QStringList>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CDnDTarget.h"
+#include "CDnDSource.h"
+
+/* Forward declarations: */
+class QMimeData;
+
+class UIDnDMIMEData;
+class UISession;
+
+/**
+ * Main class for implementing Drag'n'Drop in the frontend.
+ */
+class UIDnDHandler: public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ UIDnDHandler(UISession *pSession, QWidget *pParent);
+ virtual ~UIDnDHandler(void);
+
+ /**
+ * Drag and drop data set from the source.
+ */
+ typedef struct UIDnDDataSource
+ {
+ /** List of formats supported by the source. */
+ QStringList lstFormats;
+ /** List of allowed drop actions from the source. */
+ QVector<KDnDAction> vecActions;
+ /** Default drop action from the source. */
+ KDnDAction defaultAction;
+
+ } UIDnDDataSource;
+
+ int init(void);
+ void reset(void);
+
+ /* Frontend -> Target. */
+ Qt::DropAction dragEnter(ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData);
+ Qt::DropAction dragMove (ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData);
+ Qt::DropAction dragDrop (ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData);
+ void dragLeave(ulong screenId);
+
+ /* Source -> Frontend. */
+ int dragCheckPending(ulong screenId);
+ int dragStart(ulong screenId);
+ int dragStop(ulong screenID);
+
+ /* Data access. */
+ int retrieveData(Qt::DropAction dropAction, const QString &strMIMEType, QVector<uint8_t> &vecData);
+ int retrieveData(Qt::DropAction dropAction, const QString &strMIMEType, QVariant::Type vaType, QVariant &vaData);
+
+public:
+
+ static KDnDAction toVBoxDnDAction(Qt::DropAction action);
+ static QVector<KDnDAction> toVBoxDnDActions(Qt::DropActions actions);
+ static Qt::DropAction toQtDnDAction(KDnDAction action);
+ static Qt::DropActions toQtDnDActions(const QVector<KDnDAction> &vecActions);
+
+public slots:
+
+ /**
+ * Called by UIDnDMIMEData (Linux, OS X, Solaris) to start retrieving the actual data
+ * from the guest. This function will block and show a modal progress dialog until
+ * the data transfer is complete.
+ *
+ * @return IPRT status code.
+ * @param dropAction Drop action to perform.
+ * @param strMIMEType MIME data type.
+ * @param vaType Qt's variant type of the MIME data.
+ * @param vaData Reference to QVariant where to store the retrieved data.
+ */
+ int sltGetData(Qt::DropAction dropAction, const QString &strMIMEType, QVariant::Type vaType, QVariant &vaData);
+
+protected:
+
+#ifdef DEBUG
+ static void debugOutputQt(QtMsgType type, const char *pszMsg);
+#endif
+
+protected:
+
+ int dragStartInternal(const QStringList &lstFormats, Qt::DropAction defAction, Qt::DropActions actions);
+ int retrieveDataInternal(Qt::DropAction dropAction, const QString &strMIMEType, QVector<uint8_t> &vecData);
+
+protected:
+
+#ifdef RT_OS_WINDOWS
+ static int getProcessIntegrityLevel(DWORD *pdwIntegrityLevel);
+#endif
+
+protected:
+
+ /** Pointer to UI session. */
+ UISession *m_pSession;
+ /** Pointer to parent widget. */
+ QWidget *m_pParent;
+ /** Drag and drop source instance. */
+ CDnDSource m_dndSource;
+ /** Drag and drop target instance. */
+ CDnDTarget m_dndTarget;
+ /** Current data from the source (if any).
+ * At the momenet we only support one source at a time. */
+ UIDnDDataSource m_dataSource;
+ /** Flag indicating whether data has been retrieved from
+ * the guest already or not. */
+ bool m_fDataRetrieved;
+ QMutex m_ReadLock;
+ QMutex m_WriteLock;
+ /** Data received from the guest. */
+ QVector<uint8_t> m_vecData;
+
+#ifdef RT_OS_WINDOWS
+ /** Process integrity level we're running with. Needed for UIPI detection + logging.
+ * Set to 0 if not set yet or unavailable. */
+ DWORD m_dwIntegrityLevel;
+#else /* !RT_OS_WINDOWS */
+ /** Pointer to MIMEData instance used for handling
+ * own MIME times on non-Windows host OSes. */
+ UIDnDMIMEData *m_pMIMEData;
+ friend class UIDnDMIMEData;
+#endif /* RT_OS_WINDOWS */
+};
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIDnDHandler_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDMIMEData.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDMIMEData.cpp
new file mode 100644
index 00000000..31455bf6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDMIMEData.cpp
@@ -0,0 +1,311 @@
+/* $Id: UIDnDMIMEData.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDnDMIMEData class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+
+/* Qt includes: */
+#include <QFileInfo>
+#include <QMimeData>
+#include <QStringList>
+#include <QUrl>
+
+/* GUI includes: */
+#include "UIDnDMIMEData.h"
+
+/* Other VBox includes: */
+#include <VBox/log.h>
+#include <iprt/errcore.h>
+
+#include <VBox/GuestHost/DragAndDrop.h>
+
+
+UIDnDMIMEData::UIDnDMIMEData(UIDnDHandler *pDnDHandler,
+ QStringList lstFormats, Qt::DropAction defAction, Qt::DropActions actions)
+ : m_pDnDHandler(pDnDHandler)
+ , m_lstFormats(lstFormats)
+ , m_defAction(defAction)
+ , m_curAction(Qt::IgnoreAction)
+ , m_actions(actions)
+ , m_enmState(Dragging)
+{
+ LogFlowThisFuncEnter();
+#ifdef DEBUG
+ LogFlowFunc(("Number of formats: %d\n", m_lstFormats.size()));
+ for (int i = 0; i < m_lstFormats.size(); i++)
+ LogFlowFunc(("\tFormat %d: %s\n", i, m_lstFormats.at(i).toUtf8().constData()));
+#endif
+}
+
+QStringList UIDnDMIMEData::formats(void) const
+{
+ LogFlowFuncEnter();
+#ifdef DEBUG
+ for (int i = 0; i < m_lstFormats.size(); i++)
+ LogFlowFunc(("\tFormat %d: %s\n", i, m_lstFormats.at(i).toUtf8().constData()));
+#endif
+ return m_lstFormats;
+}
+
+bool UIDnDMIMEData::hasFormat(const QString &strMIMEType) const
+{
+ RT_NOREF(strMIMEType);
+
+ bool fRc;
+#ifdef RT_OS_DARWIN
+ fRc = m_lstFormats.contains(strMIMEType);
+#else
+ fRc = m_curAction != Qt::IgnoreAction;
+#endif
+
+ LogFlowFunc(("%s: %RTbool (QtMimeData: %RTbool, curAction=0x%x)\n",
+ strMIMEType.toStdString().c_str(), fRc, QMimeData::hasFormat(strMIMEType), m_curAction));
+
+ return fRc;
+}
+
+/**
+ * Called by Qt's drag'n drop operation (QDrag) for retrieving the actual drag'n drop
+ * data in case of a successful drag'n drop operation.
+ *
+ * @param strMIMEType MIME type string.
+ * @param vaType Variant containing the actual data based on the MIME type.
+ *
+ * @return QVariant
+ */
+QVariant UIDnDMIMEData::retrieveData(const QString &strMIMEType, QVariant::Type vaType) const
+{
+ LogFlowFunc(("state=%RU32, curAction=0x%x, defAction=0x%x, mimeType=%s, type=%d (%s)\n",
+ m_enmState, m_curAction, m_defAction, strMIMEType.toStdString().c_str(), vaType, QVariant::typeToName(vaType)));
+
+ int rc = VINF_SUCCESS;
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * On Windows this function will be called several times by Qt's
+ * OLE-specific internals to figure out which data formats we have
+ * to offer. So just assume we can drop data here for a start.
+ */
+#elif defined(RT_OS_DARWIN)
+# ifndef VBOX_WITH_DRAG_AND_DROP_PROMISES
+ /*
+ * Without VBOX_WITH_DRAG_AND_DROP_PROMISES being set in VBox *and* in our (patched) Qt
+ * libraries there's no reliable way to get this working on OS X. So just deny any dropping.
+ */
+ rc = VERR_NOT_IMPLEMENTED;
+
+ /* Let the user know. */
+ LogRel(("DnD: Drag and drop support for OS X is not available in this version\n"));
+# endif /* VBOX_WITH_DRAG_AND_DROP_PROMISES */
+#else /* !RT_OS_DARWIN */
+ /*
+ * On Linux/Solaris our state gets updated if the drop target has been
+ * changed. So query the current status if we're at the moment are able
+ * to drop something on the current target.
+ */
+ if (m_curAction == Qt::IgnoreAction)
+ {
+ LogFlowFunc(("Current drop action is 0x%x, so can't drop yet\n", m_curAction));
+ rc = VERR_NOT_FOUND;
+ }
+#endif
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Silently ignore internal Qt types / converters. */
+ if (!strMIMEType.compare("application/x-qt-mime-type-name", Qt::CaseInsensitive))
+ {
+ rc = VERR_NOT_FOUND;
+ }
+ /* Do we support the requested MIME type? */
+ else if (!m_lstFormats.contains(strMIMEType))
+ {
+ LogRel(("DnD: Unsupported MIME type '%s'\n", strMIMEType.toStdString().c_str()));
+ rc = VERR_NOT_SUPPORTED;
+ }
+#ifndef RT_OS_DARWIN /* On OS X QVariant::Invalid can happen for drag and drop "promises" for "lazy requests". */
+ /* Check supported variant types. */
+ else if (!(
+ /* Plain text. */
+ vaType == QVariant::String
+ /* Binary data. */
+ || vaType == QVariant::ByteArray
+ /* URI list. */
+ || vaType == QVariant::List
+ || vaType == QVariant::StringList))
+ {
+ LogRel(("DnD: Unsupported data type '%s'\n", QVariant::typeToName(vaType)));
+ rc = VERR_NOT_SUPPORTED;
+ }
+#endif
+ }
+
+ LogRel3(("DnD: Retrieved data state is %ld (action=0x%x), rc=%Rrc\n", m_enmState, m_curAction, rc));
+
+ if (RT_SUCCESS(rc))
+ {
+ QVariant vaData;
+ rc = emit sigGetData(Qt::CopyAction, strMIMEType, vaType, vaData);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel3(("DnD: Returning data for MIME type=%s, variant type=%s, rc=%Rrc\n",
+ strMIMEType.toStdString().c_str(), QVariant::typeToName(vaData.type()), rc));
+
+ return vaData;
+ }
+ }
+ else if (rc == VERR_NOT_FOUND) /* Silently skip internal entries. */
+ rc = VINF_SUCCESS;
+
+ if (RT_FAILURE(rc))
+ LogRel2(("DnD: Retrieving data failed with %Rrc\n", rc));
+
+ return QVariant(QVariant::Invalid);
+}
+
+/* static */
+QVariant::Type UIDnDMIMEData::getVariantType(const QString &strMIMEType)
+{
+ QVariant::Type vaType;
+
+ if ( !strMIMEType.compare("text/html")
+ || !strMIMEType.compare("text/plain;charset=utf-8")
+ || !strMIMEType.compare("text/plain;charset=utf-16")
+ || !strMIMEType.compare("text/plain")
+ || !strMIMEType.compare("text/richtext")
+ || !strMIMEType.compare("UTF8_STRING")
+ || !strMIMEType.compare("TEXT")
+ || !strMIMEType.compare("STRING"))
+ {
+ vaType = QVariant::String;
+ }
+ else if (!strMIMEType.compare("text/uri-list", Qt::CaseInsensitive))
+ vaType = QVariant::List;
+ else
+ vaType = QVariant::Invalid;
+
+ LogFlowFunc(("strMIMEType=%s -> vaType=%s\n", qPrintable(strMIMEType), QVariant::typeToName(vaType)));
+ return vaType;
+}
+
+/* static */
+int UIDnDMIMEData::getDataAsVariant(const QVector<uint8_t> &vecData,
+ const QString &strMIMEType,
+ QVariant::Type vaType,
+ QVariant &vaData)
+{
+ RT_NOREF(strMIMEType);
+ LogFlowFunc(("vecDataSize=%d, strMIMEType=%s vaType=%s\n",
+ vecData.size(), qPrintable(strMIMEType), QVariant::typeToName(vaType)));
+
+ int rc = VINF_SUCCESS;
+
+ switch (vaType)
+ {
+ case QVariant::String:
+ {
+ vaData = QVariant::fromValue(QString(reinterpret_cast<const char *>(vecData.constData())));
+ Assert(vaData.type() == QVariant::String);
+ break;
+ }
+
+ case QVariant::ByteArray:
+ {
+ QByteArray ba(reinterpret_cast<const char*>(vecData.constData()), vecData.size());
+
+ vaData = QVariant::fromValue(ba);
+ Assert(vaData.type() == QVariant::ByteArray);
+ break;
+ }
+
+ /* See: https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html */
+ case QVariant::List: /* Used on OS X for representing URI lists. */
+ {
+ QString strData = QString(reinterpret_cast<const char*>(vecData.constData()));
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList lstString = strData.split(DND_PATH_SEPARATOR_STR, Qt::SkipEmptyParts);
+#else
+ QStringList lstString = strData.split(DND_PATH_SEPARATOR_STR, QString::SkipEmptyParts);
+#endif
+
+ QVariantList lstVariant;
+
+ Q_FOREACH(const QString& strCur, lstString)
+ {
+ QVariant vaURL = QVariant::fromValue(QUrl(strCur));
+ Assert(vaURL.type() == QVariant::Url);
+ lstVariant.append(vaURL);
+ }
+
+ vaData = QVariant::fromValue(lstVariant);
+ Assert(vaData.type() == QVariant::List);
+ break;
+ }
+
+ case QVariant::StringList:
+ {
+ QString strData = QString(reinterpret_cast<const char*>(vecData.constData()));
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList lstString = strData.split(DND_PATH_SEPARATOR_STR, Qt::SkipEmptyParts);
+#else
+ QStringList lstString = strData.split(DND_PATH_SEPARATOR_STR, QString::SkipEmptyParts);
+#endif
+
+ LogFlowFunc(("\tStringList has %d entries\n", lstString.size()));
+#ifdef DEBUG
+ Q_FOREACH(const QString& strCur, lstString)
+ LogFlowFunc(("\t\tString: %s\n", qPrintable(strCur)));
+#endif
+ vaData = QVariant::fromValue(lstString);
+ Assert(vaData.type() == QVariant::StringList);
+ break;
+ }
+
+ default:
+ {
+ LogRel2(("DnD: Converting data (%d bytes) from guest to variant type '%s' not supported\n",
+ vecData.size(), QVariant::typeToName(vaType) ? QVariant::typeToName(vaType) : "<Invalid>"));
+
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Issued by the QDrag object as soon as the current drop action has changed.
+ *
+ * @param dropAction New drop action to use.
+ */
+void UIDnDMIMEData::sltDropActionChanged(Qt::DropAction dropAction)
+{
+ LogFlowFunc(("dropAction=0x%x\n", dropAction));
+ m_curAction = dropAction;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDMIMEData.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDMIMEData.h
new file mode 100644
index 00000000..2f9fd5b2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDMIMEData.h
@@ -0,0 +1,153 @@
+/* $Id: UIDnDMIMEData.h $ */
+/** @file
+ * VBox Qt GUI - UIDnDMIMEData class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIDnDMIMEData_h
+#define FEQT_INCLUDED_SRC_runtime_UIDnDMIMEData_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMimeData>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CConsole.h"
+#include "CDnDSource.h"
+#include "CGuest.h"
+#include "CSession.h"
+
+#include "UIDnDHandler.h"
+
+/** @todo Subclass QWindowsMime / QMacPasteboardMime
+ * to register own/more MIME types. */
+
+/**
+ * Own implementation of QMimeData for starting and
+ * handling all guest-to-host transfers.
+ */
+class UIDnDMIMEData: public QMimeData
+{
+ Q_OBJECT;
+
+ enum State
+ {
+ /** Host is in dragging state, without
+ * having retrieved the metadata from the guest yet. */
+ Dragging = 0,
+ /** There has been a "dropped" action which indicates
+ * that the guest can continue sending more data (if any)
+ * over to the host, based on the (MIME) metadata. */
+ Dropped,
+ /** The operation has been canceled. */
+ Canceled,
+ /** An error occurred. */
+ Error,
+ /** The usual 32-bit type blow up. */
+ State_32BIT_Hack = 0x7fffffff
+ };
+
+public:
+
+ UIDnDMIMEData(UIDnDHandler *pDnDHandler, QStringList formats, Qt::DropAction defAction, Qt::DropActions actions);
+
+signals:
+
+ /**
+ * Signal which gets emitted if this object is ready to retrieve data
+ * in the specified MIME type.
+ *
+ * @returns IPRT status code.
+ * @param dropAction Drop action to perform.
+ * @param strMimeType MIME data type.
+ * @param vaType Qt's variant type of the MIME data.
+ * @param vaData Reference to QVariant where to store the retrieved data.
+ */
+ int sigGetData(Qt::DropAction dropAction, const QString &strMIMEType, QVariant::Type vaType, QVariant &vaData) const;
+
+public slots:
+
+ /**
+ * Slot indicating that the current drop target has been changed.
+ * @note Does not work on OS X.
+ */
+ void sltDropActionChanged(Qt::DropAction dropAction);
+
+protected:
+ /** @name Overridden functions of QMimeData.
+ * @{ */
+ virtual QStringList formats(void) const;
+
+ virtual bool hasFormat(const QString &mimeType) const;
+
+ virtual QVariant retrieveData(const QString &strMIMEType, QVariant::Type vaType) const;
+ /** @} */
+
+public:
+
+ /** @name Internal helper functions.
+ * @{ */
+
+ /**
+ * Returns the matching variant type of a given MIME type.
+ *
+ * @returns Variant type.
+ * @param strMIMEType MIME type to retrieve variant type for.
+ */
+ static QVariant::Type getVariantType(const QString &strMIMEType);
+
+ /**
+ * Fills a QVariant with data according to the given type and data.
+ *
+ * @returns IPRT status code.
+ * @param vecData Bytes data to set.
+ * @param strMIMEType MIME type to handle.
+ * @param vaType Variant type to set the variant to.
+ * @param vaData Variant holding the transformed result.
+ * Note: The variant's type might be different from the input vaType!
+ */
+ static int getDataAsVariant(const QVector<uint8_t> &vecData, const QString &strMIMEType, QVariant::Type vaType, QVariant &vaData);
+ /** @} */
+
+protected:
+
+ /** Pointer to the parent. */
+ UIDnDHandler *m_pDnDHandler;
+ /** Available formats. */
+ QStringList m_lstFormats;
+ /** Default action on successful drop operation. */
+ Qt::DropAction m_defAction;
+ /** Current action, based on QDrag's status. */
+ Qt::DropAction m_curAction;
+ /** Available actions. */
+ Qt::DropActions m_actions;
+ /** The current dragging state. */
+ mutable State m_enmState;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIDnDMIMEData_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIFrameBuffer.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIFrameBuffer.cpp
new file mode 100644
index 00000000..d3eb1e47
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIFrameBuffer.cpp
@@ -0,0 +1,2501 @@
+/* $Id: UIFrameBuffer.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFrameBuffer class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QImage>
+#include <QRegion>
+#include <QPainter>
+#include <QTransform>
+
+/* GUI includes: */
+#include "UIActionPool.h"
+#include "UIActionPoolRuntime.h"
+#include "UIFrameBuffer.h"
+#include "UISession.h"
+#include "UIMachineLogic.h"
+#include "UIMachineWindow.h"
+#include "UIMachineView.h"
+#include "UINotificationCenter.h"
+#include "UIExtraDataManager.h"
+#include "UICommon.h"
+#ifdef VBOX_WITH_MASKED_SEAMLESS
+# include "UIMachineWindow.h"
+#endif /* VBOX_WITH_MASKED_SEAMLESS */
+#ifdef VBOX_WS_X11
+# include "VBoxUtils-x11.h"
+#endif
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CDisplay.h"
+#include "CFramebuffer.h"
+#include "CDisplaySourceBitmap.h"
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+/* Other VBox includes: */
+#include <iprt/critsect.h>
+#include <VBoxVideo3D.h>
+
+/* Other includes: */
+#include <math.h>
+
+#ifdef VBOX_WS_X11
+/* X11 includes: */
+# include <X11/Xlib.h>
+# undef Bool // Qt5 vs Xlib gift..
+#endif /* VBOX_WS_X11 */
+
+
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+/* Experimental code. */
+
+/* Qt OpenGL includes: */
+/* On Windows host they require the following two include files, otherwise compilation will fail with warnings.
+ * The two files are already included, but they are needed if the Qt files are moved to the 'Qt includes:' section. */
+// #include <iprt/stdint.h>
+// #include <iprt/win/windows.h>
+#include <QOffscreenSurface>
+#include <QOpenGLFunctions>
+#include <QOpenGLTexture>
+#include <QOpenGLWidget>
+
+# ifdef RT_OS_LINUX
+/* GL/glx.h must be included after Qt GL headers (which define GL_GLEXT_LEGACY) to avoid GL_GLEXT_VERSION conflict. */
+#include <GL/glx.h>
+# endif
+
+class UIFrameBufferPrivate;
+class GLWidget;
+
+/* Handles the guest screen texture for the target GLWidget. */
+class GLWidgetSource
+{
+public:
+
+ GLWidgetSource(GLWidget *pTarget);
+ virtual ~GLWidgetSource();
+
+ GLWidget *Target() { return m_pTarget; }
+
+ virtual void initGuestScreenTexture(int w, int h) { RT_NOREF(w, h); };
+ virtual void uninitGuestScreenTexture() {};
+ virtual void updateGuestImage() {};
+ virtual void cleanup() {};
+ virtual bool IsHW() { return false; };
+
+private:
+
+ GLWidget *m_pTarget;
+};
+
+/* Update the guest texture from a QImage. */
+class GLWidgetSourceImage : public GLWidgetSource
+{
+public:
+
+ GLWidgetSourceImage(GLWidget *pTarget, QImage *pImage);
+ virtual ~GLWidgetSourceImage();
+
+ virtual void initGuestScreenTexture(int w, int h);
+ virtual void updateGuestImage();
+
+private:
+
+ QImage *m_pImage;
+};
+
+# ifdef RT_OS_LINUX
+/* The guest texture is a X pixmap. */
+class GLWidgetSourcePixmap : public GLWidgetSource
+{
+public:
+
+ GLWidgetSourcePixmap(GLWidget *pTarget, Pixmap pixmap, VisualID visualid);
+ virtual ~GLWidgetSourcePixmap();
+
+ virtual void initGuestScreenTexture(int w, int h);
+ virtual void uninitGuestScreenTexture();
+ virtual void cleanup();
+ virtual bool IsHW() { return true; };
+
+private:
+
+ /* HW accelerated graphics output from a pixmap. */
+ Pixmap m_Pixmap;
+ VisualID m_visualid;
+
+ GLXPixmap m_glxPixmap;
+
+ Display *m_display;
+ PFNGLXBINDTEXIMAGEEXTPROC m_pfnglXBindTexImageEXT;
+ PFNGLXRELEASETEXIMAGEEXTPROC m_pfnglXReleaseTexImageEXT;
+};
+# endif /* RT_OS_LINUX */
+
+/* This widget allows to use OpenGL. */
+class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
+{
+ Q_OBJECT
+
+public:
+
+ GLWidget(QWidget *parent, UIFrameBufferPrivate *pFramebuffer);
+ virtual ~GLWidget();
+
+ /* Whether OpenGL is supported at all. */
+ static bool isSupported();
+
+ void lock() { RTCritSectEnter(&m_critSect); }
+ void unlock() { RTCritSectLeave(&m_critSect); }
+
+ /* Notification about the guest screen size. */
+ void resizeGuestScreen(int w, int h);
+ /* Which guest area is visible in the VM window. */
+ void setGuestVisibleRect(int x, int y, int w, int h);
+ /* Update the guest texture before painting. */
+ void updateGuestImage();
+
+ /* The guest texture OpenGL target. */
+ static GLenum const kTextureTarget = GL_TEXTURE_2D;
+
+ /* The the guest screen source. */
+ void setSource(GLWidgetSource *pSource, bool fForce);
+
+public slots:
+
+ void cleanup();
+
+protected:
+
+ /* QOpenGLWidget methods, which must be reimplemented. */
+ void initializeGL() RT_OVERRIDE;
+ void paintGL() RT_OVERRIDE;
+ void resizeGL(int w, int h) RT_OVERRIDE;
+
+private:
+
+ /* Get and possibly initialize the guest source. */
+ GLWidgetSource *getSource();
+
+ /* Create the texture which contains the guest screen bitmap. */
+ void createGuestTexture();
+ /* Delete the texture which contains the guest screen bitmap. */
+ void deleteGuestTexture();
+
+ /* Backlink. */
+ UIFrameBufferPrivate *m_pFramebuffer;
+
+ /* Fallback when no guest screen is available. */
+ GLWidgetSource m_nullSource;
+ /* The current guest screen bitmap source. */
+ GLWidgetSource *m_pSource;
+
+ /* The guest screen resolution. */
+ QSize m_guestSize;
+ /* The visible area of the guest screen in guest pixels. */
+ QRect m_guestVisibleRect;
+
+ /** RTCRITSECT object to protect frame-buffer access. */
+ RTCRITSECT m_critSect;
+
+ /* A new guest screen source has been set and needs reinitialization. */
+ bool m_fReinitSource;
+
+ /* Texture which contains entire guest screen. Size is m_guestSize. */
+ GLuint m_guestTexture;
+};
+#endif /* VBOX_GUI_WITH_QTGLFRAMEBUFFER */
+
+/** IFramebuffer implementation used to maintain VM display video memory. */
+class ATL_NO_VTABLE UIFrameBufferPrivate : public QObject,
+ public ATL::CComObjectRootEx<ATL::CComMultiThreadModel>,
+ VBOX_SCRIPTABLE_IMPL(IFramebuffer)
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listener about guest-screen resolution changes. */
+ void sigNotifyChange(int iWidth, int iHeight);
+ /** Notifies listener about guest-screen updates. */
+ void sigNotifyUpdate(int iX, int iY, int iWidth, int iHeight);
+ /** Notifies listener about guest-screen visible-region changes. */
+ void sigSetVisibleRegion(QRegion region);
+
+public:
+
+ /** Frame-buffer constructor. */
+ UIFrameBufferPrivate();
+ /** Frame-buffer destructor. */
+ ~UIFrameBufferPrivate();
+
+ /** Frame-buffer initialization.
+ * @param pMachineView defines machine-view this frame-buffer is bounded to. */
+ virtual HRESULT init(UIMachineView *pMachineView);
+
+ /** Assigns machine-view frame-buffer will be bounded to.
+ * @param pMachineView defines machine-view this frame-buffer is bounded to. */
+ virtual void setView(UIMachineView *pMachineView);
+
+ /** Returns the copy of the IDisplay wrapper. */
+ CDisplay display() const { return m_display; }
+ /** Attach frame-buffer to IDisplay. */
+ void attach();
+ /** Detach frame-buffer from IDisplay. */
+ void detach();
+
+ /** Returns frame-buffer data address. */
+ uchar *address() { return m_image.bits(); }
+ /** Returns frame-buffer width. */
+ ulong width() const { return m_iWidth; }
+ /** Returns frame-buffer height. */
+ ulong height() const { return m_iHeight; }
+ /** Returns frame-buffer bits-per-pixel value. */
+ ulong bitsPerPixel() const { return m_image.depth(); }
+ /** Returns frame-buffer bytes-per-line value. */
+ ulong bytesPerLine() const { return m_image.bytesPerLine(); }
+ /** Returns default frame-buffer pixel-format. */
+ ulong pixelFormat() const { return KBitmapFormat_BGR; }
+ /** Returns the visual-state this frame-buffer is used for. */
+ UIVisualStateType visualState() const { return m_pMachineView ? m_pMachineView->visualStateType() : UIVisualStateType_Invalid; }
+
+ /** Defines whether frame-buffer is <b>unused</b>.
+ * @note Refer to m_fUnused for more information.
+ * @note Calls to this and any other EMT callback are synchronized (from GUI side). */
+ void setMarkAsUnused(bool fUnused);
+
+ /** Returns the frame-buffer's scaled-size. */
+ QSize scaledSize() const { return m_scaledSize; }
+ /** Defines host-to-guest scale ratio as @a size. */
+ void setScaledSize(const QSize &size = QSize()) { m_scaledSize = size; }
+ /** Returns x-origin of the host (scaled) content corresponding to x-origin of guest (actual) content. */
+ inline int convertGuestXTo(int x) const { return m_scaledSize.isValid() ? qRound((double)m_scaledSize.width() / m_iWidth * x) : x; }
+ /** Returns y-origin of the host (scaled) content corresponding to y-origin of guest (actual) content. */
+ inline int convertGuestYTo(int y) const { return m_scaledSize.isValid() ? qRound((double)m_scaledSize.height() / m_iHeight * y) : y; }
+ /** Returns x-origin of the guest (actual) content corresponding to x-origin of host (scaled) content. */
+ inline int convertHostXTo(int x) const { return m_scaledSize.isValid() ? qRound((double)m_iWidth / m_scaledSize.width() * x) : x; }
+ /** Returns y-origin of the guest (actual) content corresponding to y-origin of host (scaled) content. */
+ inline int convertHostYTo(int y) const { return m_scaledSize.isValid() ? qRound((double)m_iHeight / m_scaledSize.height() * y) : y; }
+
+ /** Returns the scale-factor used by the frame-buffer. */
+ double scaleFactor() const { return m_dScaleFactor; }
+ /** Define the scale-factor used by the frame-buffer. */
+ void setScaleFactor(double dScaleFactor) { m_dScaleFactor = dScaleFactor; }
+
+ /** Returns device-pixel-ratio set for HiDPI frame-buffer. */
+ double devicePixelRatio() const { return m_dDevicePixelRatio; }
+ /** Defines device-pixel-ratio set for HiDPI frame-buffer. */
+ void setDevicePixelRatio(double dDevicePixelRatio) { m_dDevicePixelRatio = dDevicePixelRatio; }
+ /** Returns actual device-pixel-ratio set for HiDPI frame-buffer. */
+ double devicePixelRatioActual() const { return m_dDevicePixelRatioActual; }
+ /** Defines actual device-pixel-ratio set for HiDPI frame-buffer. */
+ void setDevicePixelRatioActual(double dDevicePixelRatioActual) { m_dDevicePixelRatioActual = dDevicePixelRatioActual; }
+
+ /** Returns whether frame-buffer should use unscaled HiDPI output. */
+ bool useUnscaledHiDPIOutput() const { return m_fUseUnscaledHiDPIOutput; }
+ /** Defines whether frame-buffer should use unscaled HiDPI output. */
+ void setUseUnscaledHiDPIOutput(bool fUseUnscaledHiDPIOutput) { m_fUseUnscaledHiDPIOutput = fUseUnscaledHiDPIOutput; }
+
+ /** Returns frame-buffer scaling optimization type. */
+ ScalingOptimizationType scalingOptimizationType() const { return m_enmScalingOptimizationType; }
+ /** Defines frame-buffer scaling optimization type: */
+ void setScalingOptimizationType(ScalingOptimizationType type) { m_enmScalingOptimizationType = type; }
+
+ DECLARE_NOT_AGGREGATABLE(UIFrameBufferPrivate)
+
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ BEGIN_COM_MAP(UIFrameBufferPrivate)
+ COM_INTERFACE_ENTRY(IFramebuffer)
+ COM_INTERFACE_ENTRY2(IDispatch,IFramebuffer)
+ COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.m_p)
+ END_COM_MAP()
+
+ HRESULT FinalConstruct();
+ void FinalRelease();
+
+ STDMETHOD(COMGETTER(Width))(ULONG *puWidth);
+ STDMETHOD(COMGETTER(Height))(ULONG *puHeight);
+ STDMETHOD(COMGETTER(BitsPerPixel))(ULONG *puBitsPerPixel);
+ STDMETHOD(COMGETTER(BytesPerLine))(ULONG *puBytesPerLine);
+ STDMETHOD(COMGETTER(PixelFormat))(BitmapFormat_T *puPixelFormat);
+ STDMETHOD(COMGETTER(HeightReduction))(ULONG *puHeightReduction);
+ STDMETHOD(COMGETTER(Overlay))(IFramebufferOverlay **ppOverlay);
+ STDMETHOD(COMGETTER(WinId))(LONG64 *pWinId);
+ STDMETHOD(COMGETTER(Capabilities))(ComSafeArrayOut(FramebufferCapabilities_T, aCapabilities));
+
+ /** EMT callback: Notifies frame-buffer about guest-screen size change.
+ * @param uScreenId Guest screen number.
+ * @param uX Horizontal origin of the update rectangle, in pixels.
+ * @param uY Vertical origin of the update rectangle, in pixels.
+ * @param uWidth Width of the guest display, in pixels.
+ * @param uHeight Height of the guest display, in pixels.
+ * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
+ * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
+ STDMETHOD(NotifyChange)(ULONG uScreenId, ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight);
+
+ /** EMT callback: Notifies frame-buffer about guest-screen update.
+ * @param uX Horizontal origin of the update rectangle, in pixels.
+ * @param uY Vertical origin of the update rectangle, in pixels.
+ * @param uWidth Width of the update rectangle, in pixels.
+ * @param uHeight Height of the update rectangle, in pixels.
+ * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
+ * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
+ STDMETHOD(NotifyUpdate)(ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight);
+
+ /** EMT callback: Notifies frame-buffer about guest-screen update.
+ * @param uX Horizontal origin of the update rectangle, in pixels.
+ * @param uY Vertical origin of the update rectangle, in pixels.
+ * @param uWidth Width of the update rectangle, in pixels.
+ * @param uHeight Height of the update rectangle, in pixels.
+ * @param image Brings image container which can be used to copy data from.
+ * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
+ * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
+ STDMETHOD(NotifyUpdateImage)(ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight, ComSafeArrayIn(BYTE, image));
+
+ /** EMT callback: Returns whether the frame-buffer implementation is willing to support a given video-mode.
+ * @param uWidth Width of the guest display, in pixels.
+ * @param uHeight Height of the guest display, in pixels.
+ * @param uBPP Color depth, bits per pixel.
+ * @param pfSupported Is frame-buffer able/willing to render the video mode or not.
+ * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
+ * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
+ STDMETHOD(VideoModeSupported)(ULONG uWidth, ULONG uHeight, ULONG uBPP, BOOL *pbSupported);
+
+ /** EMT callback which is not used in current implementation. */
+ STDMETHOD(GetVisibleRegion)(BYTE *pRectangles, ULONG uCount, ULONG *puCountCopied);
+ /** EMT callback: Suggests new visible-region to this frame-buffer.
+ * @param pRectangles Pointer to the RTRECT array.
+ * @param uCount Number of RTRECT elements in the rectangles array.
+ * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
+ * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
+ STDMETHOD(SetVisibleRegion)(BYTE *pRectangles, ULONG uCount);
+
+ /** EMT callback which is not used in current implementation. */
+ STDMETHOD(ProcessVHWACommand)(BYTE *pCommand, LONG enmCmd, BOOL fGuestCmd);
+
+ /** EMT callback: Notifies frame-buffer about 3D backend event.
+ * @param uType Event type. VBOX3D_NOTIFY_TYPE_*.
+ * @param aData Event-specific data, depends on the supplied event type.
+ * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
+ * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
+ STDMETHOD(Notify3DEvent)(ULONG uType, ComSafeArrayIn(BYTE, data));
+
+ /** Locks frame-buffer access. */
+ void lock() const { RTCritSectEnter(&m_critSect); }
+ /** Unlocks frame-buffer access. */
+ void unlock() const { RTCritSectLeave(&m_critSect); }
+
+ /** Handles frame-buffer notify-change-event. */
+ virtual void handleNotifyChange(int iWidth, int iHeight);
+ /** Handles frame-buffer paint-event. */
+ virtual void handlePaintEvent(QPaintEvent *pEvent);
+ /** Handles frame-buffer set-visible-region-event. */
+ virtual void handleSetVisibleRegion(const QRegion &region);
+
+ /** Performs frame-buffer resizing. */
+ virtual void performResize(int iWidth, int iHeight);
+ /** Performs frame-buffer rescaling. */
+ virtual void performRescale();
+
+ /** Handles viewport resize-event. */
+ virtual void viewportResized(QResizeEvent*)
+ {
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+ /* Sync GL widget size with the MachineView widget: */
+ /** @todo This can be probably done in a more automated way. */
+ if (m_pGLWidget && m_pMachineView)
+ m_pGLWidget->resize(m_pMachineView->viewport()->size());
+#endif
+ }
+
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+ bool isGLWidgetSupported()
+ {
+ QString s = uiCommon().virtualBox().GetExtraData("GUI/GLWidget");
+ return s == "1" && GLWidget::isSupported();
+ }
+#endif
+
+protected slots:
+
+ /** Handles guest requests to change mouse pointer shape or position. */
+ void sltMousePointerShapeOrPositionChange();
+
+protected:
+
+ /** Prepare connections routine. */
+ void prepareConnections();
+ /** Cleanup connections routine. */
+ void cleanupConnections();
+
+ /** Updates coordinate-system: */
+ void updateCoordinateSystem();
+
+ /** Default paint routine. */
+ void paintDefault(QPaintEvent *pEvent);
+ /** Paint routine for seamless mode. */
+ void paintSeamless(QPaintEvent *pEvent);
+
+ /** Returns the transformation mode corresponding to the passed @a dScaleFactor and ScalingOptimizationType. */
+ static Qt::TransformationMode transformationMode(ScalingOptimizationType type, double dScaleFactor = 0);
+
+ /** Erases corresponding @a rect with @a painter. */
+ static void eraseImageRect(QPainter &painter, const QRect &rect,
+ double dDevicePixelRatio);
+ /** Draws corresponding @a rect of passed @a image with @a painter. */
+ static void drawImageRect(QPainter &painter, const QImage &image, const QRect &rect,
+ int iContentsShiftX, int iContentsShiftY,
+ double dDevicePixelRatio);
+
+ /** Holds the screen-id. */
+ ulong m_uScreenId;
+
+ /** Holds the QImage buffer. */
+ QImage m_image;
+ /** Holds frame-buffer width. */
+ int m_iWidth;
+ /** Holds frame-buffer height. */
+ int m_iHeight;
+
+ /** Holds the copy of the IDisplay wrapper. */
+ CDisplay m_display;
+ /** Source bitmap from IDisplay. */
+ CDisplaySourceBitmap m_sourceBitmap;
+ /** Source bitmap from IDisplay (acquired but not yet applied). */
+ CDisplaySourceBitmap m_pendingSourceBitmap;
+ /** Holds whether there is a pending source bitmap which must be applied. */
+ bool m_fPendingSourceBitmap;
+
+ /** Holds machine-view this frame-buffer is bounded to. */
+ UIMachineView *m_pMachineView;
+ /** Holds window ID this frame-buffer referring to. */
+ int64_t m_iWinId;
+
+ /** Reflects whether screen-updates are allowed. */
+ bool m_fUpdatesAllowed;
+
+ /** Defines whether frame-buffer is <b>unused</b>.
+ * <b>Unused</b> status determines whether frame-buffer should ignore EMT events or not. */
+ bool m_fUnused;
+
+ /** RTCRITSECT object to protect frame-buffer access. */
+ mutable RTCRITSECT m_critSect;
+
+ /** @name Scale-factor related variables.
+ * @{ */
+ /** Holds the scale-factor used by the scaled-size. */
+ double m_dScaleFactor;
+ /** Holds the scaling optimization type used by the scaling mechanism. */
+ ScalingOptimizationType m_enmScalingOptimizationType;
+ /** Holds the coordinate-system for the scale-factor above. */
+ QTransform m_transform;
+ /** Holds the frame-buffer's scaled-size. */
+ QSize m_scaledSize;
+ /** @} */
+
+ /** @name Seamless mode related variables.
+ * @{ */
+ /* To avoid a seamless flicker which caused by the latency between
+ * the initial visible-region arriving from EMT thread
+ * and actual visible-region appliance on GUI thread
+ * it was decided to use two visible-region instances: */
+ /** Sync visible-region which being updated synchronously by locking EMT thread.
+ * Used for immediate manual clipping of the painting operations. */
+ QRegion m_syncVisibleRegion;
+ /** Async visible-region which being updated asynchronously by posting async-event from EMT to GUI thread,
+ * Used to update viewport parts for visible-region changes,
+ * because NotifyUpdate doesn't take into account these changes. */
+ QRegion m_asyncVisibleRegion;
+ /** When the frame-buffer is being resized, visible region is saved here.
+ * The saved region is applied when updates are enabled again. */
+ QRegion m_pendingSyncVisibleRegion;
+ /** @} */
+
+ /** @name HiDPI screens related variables.
+ * @{ */
+ /** Holds device-pixel-ratio set for HiDPI frame-buffer. */
+ double m_dDevicePixelRatio;
+ /** Holds actual device-pixel-ratio set for HiDPI frame-buffer. */
+ double m_dDevicePixelRatioActual;
+ /** Holds whether frame-buffer should use unscaled HiDPI output. */
+ bool m_fUseUnscaledHiDPIOutput;
+ /** @} */
+
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+ GLWidget *m_pGLWidget;
+#endif
+
+private:
+
+#ifdef Q_OS_WIN
+ ComPtr<IUnknown> m_pUnkMarshaler;
+#endif /* Q_OS_WIN */
+ /** Identifier returned by AttachFramebuffer. Used in DetachFramebuffer. */
+ QUuid m_uFramebufferId;
+
+ /** Holds the last cursor rectangle. */
+ QRect m_cursorRectangle;
+};
+
+
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+#define GLCHECK() \
+do { \
+ int glErr = glGetError(); \
+ if (glErr != GL_NO_ERROR) LogRel4(("GUI GL 0x%x @%d\n", glErr, __LINE__)); \
+} while(0)
+
+GLWidgetSource::GLWidgetSource(GLWidget *pTarget)
+ : m_pTarget(pTarget)
+{
+}
+
+GLWidgetSource::~GLWidgetSource()
+{
+ cleanup();
+}
+
+GLWidgetSourceImage::GLWidgetSourceImage(GLWidget *pTarget, QImage *pImage)
+ : GLWidgetSource(pTarget)
+ , m_pImage(pImage)
+{
+}
+
+GLWidgetSourceImage::~GLWidgetSourceImage()
+{
+}
+
+void GLWidgetSourceImage::initGuestScreenTexture(int w, int h)
+{
+ glTexImage2D(GLWidget::kTextureTarget, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ GLCHECK();
+}
+
+void GLWidgetSourceImage::updateGuestImage()
+{
+ /* Copy the image content to the texture. */
+ glTexSubImage2D(GLWidget::kTextureTarget, 0, 0, 0, m_pImage->width(), m_pImage->height(),
+ GL_BGRA, GL_UNSIGNED_BYTE, m_pImage->bits());
+ GLCHECK();
+}
+
+# ifdef RT_OS_LINUX
+GLWidgetSourcePixmap::GLWidgetSourcePixmap(GLWidget *pTarget, Pixmap pixmap, VisualID visualid)
+ : GLWidgetSource(pTarget)
+ , m_Pixmap(pixmap)
+ , m_visualid(visualid)
+ , m_glxPixmap(0)
+ , m_display(0)
+ , m_pfnglXBindTexImageEXT(0)
+ , m_pfnglXReleaseTexImageEXT(0)
+{
+}
+
+GLWidgetSourcePixmap::~GLWidgetSourcePixmap()
+{
+}
+
+void GLWidgetSourcePixmap::cleanup()
+{
+ m_pfnglXBindTexImageEXT = 0;
+ m_pfnglXReleaseTexImageEXT = 0;
+ m_Pixmap = 0;
+ m_visualid = 0;
+
+ if (m_glxPixmap)
+ {
+ glXDestroyPixmap(m_display, m_glxPixmap);
+ m_glxPixmap = 0;
+ }
+
+ if (m_display)
+ {
+ XCloseDisplay(m_display);
+ m_display = 0;
+ }
+}
+
+void GLWidgetSourcePixmap::initGuestScreenTexture(int w, int h)
+{
+ RT_NOREF(w, h);
+
+ LogRel4(("GUI: GLWidgetSourcePixmap::initGuestScreenTexture: Search for vid = %lu\n", m_visualid));
+
+ if (m_display)
+ return; /* Already initialized. */
+
+ m_display = XOpenDisplay(0);
+ if (m_display)
+ {
+ const char *glXExt = glXQueryExtensionsString(m_display, 0);
+ if (glXExt && RTStrStr(glXExt, "GLX_EXT_texture_from_pixmap"))
+ {
+ m_pfnglXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((const GLubyte *)"glXBindTexImageEXT");
+ m_pfnglXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((const GLubyte *)"glXReleaseTexImageEXT");
+ if (m_pfnglXBindTexImageEXT && m_pfnglXReleaseTexImageEXT)
+ {
+ LogRelMax(1, ("GUI: GLX_EXT_texture_from_pixmap supported\n"));
+
+ /* FBConfig attributes. */
+ static int const aConfigAttribList[] =
+ {
+ // GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ // GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
+ // GLX_X_RENDERABLE, True, // Render to GLX pixmaps
+ GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, // Must support GLX pixmaps
+ GLX_BIND_TO_TEXTURE_RGBA_EXT, True, // Must support GLX_EXT_texture_from_pixmap
+ GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, // Must support GL_TEXTURE_2D because the device creates the pixmap as TEXTURE_2D
+ GLX_DOUBLEBUFFER, False, // No need for double buffering for a pixmap.
+ GLX_RED_SIZE, 8, // True color RGB with 8 bits per channel.
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_STENCIL_SIZE, 0, // No stencil buffer
+ GLX_DEPTH_SIZE, 0, // No depth buffer
+ None
+ };
+
+ /* Find a suitable FB config. */
+ int cConfigs = 0;
+ GLXFBConfig *paConfigs = glXChooseFBConfig(m_display, 0, aConfigAttribList, &cConfigs);
+ LogRel4(("GUI: GLWidgetSourcePixmap::initGuestScreenTexture: paConfigs %p cConfigs %d\n", (void *)paConfigs, cConfigs));
+ if (paConfigs)
+ {
+ XVisualInfo *vi = NULL;
+ int i = 0;
+ for (; i < cConfigs; ++i)
+ {
+ /* Use XFree to free the data returned in the previous iteration of this loop. */
+ if (vi)
+ XFree(vi);
+
+ vi = glXGetVisualFromFBConfig(m_display, paConfigs[i]);
+ if (!vi)
+ continue;
+
+ LogRel4(("GUI: GLWidgetSourcePixmap::initGuestScreenTexture: %p vid %lu screen %d depth %d r %lu g %lu b %lu clrmap %d bitsperrgb %d\n",
+ (void *)vi->visual, vi->visualid, vi->screen, vi->depth,
+ vi->red_mask, vi->green_mask, vi->blue_mask, vi->colormap_size, vi->bits_per_rgb));
+
+ if (vi->visualid != m_visualid)
+ continue;
+
+ /* This FB config can be used. */
+ break;
+ }
+
+ if (vi)
+ {
+ XFree(vi);
+ vi = 0;
+ }
+
+ if (i < cConfigs)
+ {
+ /* Found the requested config. */
+ static int const aPixmapAttribList[] =
+ {
+ GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
+ GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
+ None
+ };
+ m_glxPixmap = glXCreatePixmap(m_display, paConfigs[i], m_Pixmap, aPixmapAttribList);
+ LogRel4(("GUI: GLWidgetSourcePixmap::initGuestScreenTexture: m_glxPixmap %ld\n", m_glxPixmap));
+
+ m_pfnglXBindTexImageEXT(m_display, m_glxPixmap, GLX_FRONT_LEFT_EXT, NULL);
+
+ /* "Use XFree to free the memory returned by glXChooseFBConfig." */
+ XFree(paConfigs);
+
+ /* Success. */
+ return;
+ }
+
+ LogRel4(("GUI: GLWidgetSourcePixmap::initGuestScreenTexture: fbconfig not found\n"));
+ /* "Use XFree to free the memory returned by glXChooseFBConfig." */
+ XFree(paConfigs);
+ }
+ }
+
+ m_pfnglXBindTexImageEXT = 0;
+ m_pfnglXReleaseTexImageEXT = 0;
+ }
+
+ XCloseDisplay(m_display);
+ m_display = 0;
+ }
+ else
+ {
+ LogRel4(("GUI: GLWidgetSourcePixmap::initGuestScreenTexture: failed to open Display\n"));
+ }
+}
+
+void GLWidgetSourcePixmap::uninitGuestScreenTexture()
+{
+ if (!m_glxPixmap)
+ return;
+
+ AssertReturnVoid(m_display && m_pfnglXReleaseTexImageEXT);
+ m_pfnglXReleaseTexImageEXT(m_display, m_glxPixmap, GLX_FRONT_LEFT_EXT);
+}
+# endif /* RT_OS_LINUX */
+
+GLWidget::GLWidget(QWidget *parent, UIFrameBufferPrivate *pFramebuffer)
+ : QOpenGLWidget(parent)
+ , m_pFramebuffer(pFramebuffer)
+ , m_nullSource(this)
+ , m_pSource(0)
+ , m_fReinitSource(false)
+ , m_guestTexture(0)
+{
+ int rc = RTCritSectInit(&m_critSect);
+ AssertRC(rc);
+
+ setMouseTracking(true);
+
+#if 0
+ QSurfaceFormat format;
+ format.setVersion(3, 3);
+ //format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setRenderableType(QSurfaceFormat::OpenGL);
+ format.setRedBufferSize(8);
+ format.setGreenBufferSize(8);
+ format.setBlueBufferSize(8);
+ format.setAlphaBufferSize(8);
+ format.setDepthBufferSize(0);
+ format.setStencilBufferSize(0);
+ format.setSwapInterval(0);
+ format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
+ setFormat(format);
+#endif
+}
+
+GLWidget::~GLWidget()
+{
+ cleanup();
+
+ RTCritSectDelete(&m_critSect);
+ RT_ZERO(m_critSect);
+}
+
+/* Whether OpenGL is usable.
+ * OpenGL 2.0 required.
+ */
+/* static */ bool GLWidget::isSupported()
+{
+ /* Create an OpenGL conntext: */
+ QOpenGLContext contextGL;
+ contextGL.create();
+ if (!contextGL.isValid())
+ return false;
+
+ /* Create an offscreen surface: */
+ QOffscreenSurface surface;
+ surface.create();
+ if (!surface.isValid())
+ return false;
+
+ /* Make the OpenGL context current: */
+ contextGL.makeCurrent(&surface);
+
+ /* Get the OpenGL version: */
+ char const *pszVersion = (char const *)contextGL.functions()->glGetString(GL_VERSION);
+ size_t cchVersion = pszVersion ? strlen(pszVersion) : 0;
+
+ int const verMajor = cchVersion >= 1 && '0' <= pszVersion[0] && pszVersion[0] <= '9'? pszVersion[0] - '0' : 0;
+ int const verMinor = cchVersion >= 3 && '0' <= pszVersion[2] && pszVersion[2] <= '9'? pszVersion[2] - '0' : 0;
+ int const ver = verMajor * 10 + verMinor;
+
+ /* Check if GL_TEXTURE_RECTANGLE is supported: */
+ //bool const fTextureRectangle = contextGL.hasExtension("GL_ARB_texture_rectangle")
+ // || contextGL.hasExtension("GL_NV_texture_rectangle")
+ // || ver >= 31;
+
+ /* Reset the current OpenGL context: */
+ contextGL.doneCurrent();
+
+ /* Decide if OpenGL support is good enough: */
+ return ver >= 20 /* && fTextureRectangle */;
+}
+
+/** @todo fForce is a bit of a hack. It does not allow to change the HW source to the QImage source,
+ * when QImage source is automatically set during the guest screen resize. Think again!
+ */
+void GLWidget::setSource(GLWidgetSource *pSource, bool fForce)
+{
+ lock();
+ if ( !fForce
+ && m_pSource
+ && m_pSource->IsHW())
+ {
+ LogRel4(("GUI: GLWidgetSourcePixmap::setSource: keeping HW source\n"));
+ unlock();
+ return;
+ }
+
+ if (m_pSource)
+ delete m_pSource;
+
+ m_pSource = pSource;
+ m_fReinitSource = true;
+ unlock();
+}
+
+GLWidgetSource *GLWidget::getSource()
+{
+ Assert(RTCritSectIsOwner(&m_critSect));
+ if (m_pSource)
+ {
+ if (m_fReinitSource)
+ {
+ m_fReinitSource = false;
+ LogRel4(("GUI: GLWidgetSourcePixmap::getSource: recreate guest texture\n"));
+
+ /* If OpenGL context has been created: */
+ if (context())
+ {
+ /* Delete the current guest texture: */
+ deleteGuestTexture();
+
+ /* Create and bind the new guest texture: */
+ createGuestTexture();
+
+ glBindTexture(kTextureTarget, m_guestTexture); GLCHECK();
+ }
+ }
+ return m_pSource;
+ }
+ return &m_nullSource;
+}
+
+void GLWidget::resizeGuestScreen(int w, int h)
+{
+ /* The guest screen has been resized. Remember the size: */
+ m_guestSize = QSize(w, h);
+}
+
+void GLWidget::setGuestVisibleRect(int x, int y, int w, int h)
+{
+ /* Remember the area of the guest screen which must be displayed: */
+ m_guestVisibleRect.setRect(x, y, w, h);
+}
+
+void GLWidget::updateGuestImage()
+{
+ /* If OpenGL context has been created: */
+ if (!context())
+ return;
+
+ makeCurrent();
+
+ lock();
+ GLWidgetSource *pSource = getSource();
+ if (m_guestTexture)
+ {
+ /* Copy the image content to the texture. */
+ glBindTexture(kTextureTarget, m_guestTexture);
+ GLCHECK();
+
+ pSource->updateGuestImage();
+ }
+ unlock();
+
+ doneCurrent();
+}
+
+void GLWidget::cleanup()
+{
+ if (!RTCritSectIsInitialized(&m_critSect))
+ return;
+
+ /* If OpenGL context has been created: */
+ if (!context())
+ return;
+
+ makeCurrent();
+
+ lock();
+ getSource()->cleanup();
+ setSource(0, true);
+ unlock();
+
+ /* Delete all OpenGL resources which are used by this widget: */
+ deleteGuestTexture();
+
+ doneCurrent();
+}
+
+void GLWidget::initializeGL()
+{
+ /* QOpenGLWidget documentation recommends to connect to the context's aboutToBeDestroyed() signal.
+ * See https://doc.qt.io/qt-5/qopenglwidget.html#details
+ * Connect the signal: */
+ connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup);
+
+ /* Required initialization for QOpenGLFunctions: */
+ initializeOpenGLFunctions();
+
+ /* Create OpenGL resources: */
+ createGuestTexture();
+
+ /* Setup the OpenGL context state: */
+ glClearColor(0, 0, 0, 1); GLCHECK();
+ glDisable(GL_DEPTH_TEST); GLCHECK();
+ glDisable(GL_CULL_FACE); GLCHECK();
+}
+
+void GLWidget::paintGL()
+{
+ lock();
+ if (m_guestTexture)
+ {
+ /* Dimensions of the target window, i.e. the widget's dimensions. */
+ GLint const w = width();
+ GLint const h = height();
+
+ /* The guest coordinates of the visible guest screen area: */
+ float x1 = m_guestVisibleRect.x();
+ float y1 = m_guestVisibleRect.y();
+ float x2 = x1 + m_guestVisibleRect.width();
+ float y2 = y1 + m_guestVisibleRect.height();
+
+ x1 /= (float)m_guestSize.width();
+ y1 /= (float)m_guestSize.height();
+ x2 /= (float)m_guestSize.width();
+ y2 /= (float)m_guestSize.height();
+
+ glDisable(GL_DEPTH_TEST); GLCHECK();
+ glDisable(GL_CULL_FACE); GLCHECK();
+
+ glEnable(kTextureTarget); GLCHECK();
+
+ /* Bind the guest texture: */
+ glBindTexture(kTextureTarget, m_guestTexture); GLCHECK();
+
+ /* This will reinitialize the source if necessary. */
+ getSource();
+
+ /* Draw the texture (upside down, because QImage and OpenGL store the bitmap differently): */
+ glBegin(GL_QUADS);
+ glTexCoord2f(x1, y1); glVertex2i(0, h);
+ glTexCoord2f(x1, y2); glVertex2i(0, 0);
+ glTexCoord2f(x2, y2); glVertex2i(w, 0);
+ glTexCoord2f(x2, y1); glVertex2i(w, h);
+ glEnd(); GLCHECK();
+
+ glBindTexture(kTextureTarget, 0); GLCHECK();
+
+ glDisable(kTextureTarget); GLCHECK();
+
+ glFlush(); GLCHECK();
+ }
+ unlock();
+}
+
+void GLWidget::resizeGL(int w, int h)
+{
+ /* Setup ModelViewProjection to work in the window cordinates: */
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glOrtho(0, w, 0, h, -1, 1);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ GLCHECK();
+}
+
+void GLWidget::createGuestTexture()
+{
+ if (m_guestSize.isEmpty())
+ return;
+
+ /* Choose GL_NEAREST if no scaling or the scaling factor is an integer: */
+ double const scaleFactor = m_pFramebuffer->scaleFactor();
+ GLenum const filter = floor(scaleFactor) == scaleFactor ? GL_NEAREST : GL_LINEAR;
+
+ /* Create a new guest texture, which must be the same size as the guest screen: */
+ glGenTextures(1, &m_guestTexture);
+ glEnable(kTextureTarget); GLCHECK();
+ glBindTexture(kTextureTarget, m_guestTexture);
+ glTexParameteri(kTextureTarget, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(kTextureTarget, GL_TEXTURE_MIN_FILTER, filter);
+
+ lock();
+ getSource()->initGuestScreenTexture(m_guestSize.width(), m_guestSize.height());
+ unlock();
+
+ glBindTexture(kTextureTarget, 0);
+ GLCHECK();
+ glDisable(kTextureTarget); GLCHECK();
+}
+
+void GLWidget::deleteGuestTexture()
+{
+ if (m_guestTexture)
+ {
+ glBindTexture(kTextureTarget, m_guestTexture);
+
+ lock();
+ getSource()->uninitGuestScreenTexture();
+ unlock();
+
+ glBindTexture(kTextureTarget, 0); GLCHECK();
+ glDeleteTextures(1, &m_guestTexture); GLCHECK();
+ m_guestTexture = 0;
+ }
+}
+#endif /* VBOX_GUI_WITH_QTGLFRAMEBUFFER */
+
+
+#ifdef VBOX_WITH_XPCOM
+NS_DECL_CLASSINFO(UIFrameBufferPrivate)
+NS_IMPL_THREADSAFE_ISUPPORTS1_CI(UIFrameBufferPrivate, IFramebuffer)
+#endif /* VBOX_WITH_XPCOM */
+
+
+UIFrameBufferPrivate::UIFrameBufferPrivate()
+ : m_uScreenId(0)
+ , m_iWidth(0), m_iHeight(0)
+ , m_fPendingSourceBitmap(false)
+ , m_pMachineView(NULL)
+ , m_iWinId(0)
+ , m_fUpdatesAllowed(false)
+ , m_fUnused(false)
+ , m_dScaleFactor(1.0)
+ , m_enmScalingOptimizationType(ScalingOptimizationType_None)
+ , m_dDevicePixelRatio(1.0)
+ , m_dDevicePixelRatioActual(1.0)
+ , m_fUseUnscaledHiDPIOutput(false)
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+ , m_pGLWidget(0)
+#endif
+{
+ /* Update coordinate-system: */
+ updateCoordinateSystem();
+}
+
+HRESULT UIFrameBufferPrivate::init(UIMachineView *pMachineView)
+{
+ LogRel2(("GUI: UIFrameBufferPrivate::init %p\n", this));
+
+ /* Assign mahine-view: */
+ m_pMachineView = pMachineView;
+
+ /* Assign index: */
+ m_uScreenId = m_pMachineView->screenId();
+
+ /* Cache window ID: */
+ m_iWinId = (m_pMachineView && m_pMachineView->viewport()) ? (LONG64)m_pMachineView->viewport()->winId() : 0;
+
+#ifdef VBOX_WS_X11
+ /* Sync Qt and X11 Server (see xTracker #7547). */
+ XSync(NativeWindowSubsystem::X11GetDisplay(), false);
+#endif
+
+ /* Assign display: */
+ m_display = m_pMachineView->uisession()->display();
+
+ /* Initialize critical-section: */
+ int rc = RTCritSectInit(&m_critSect);
+ AssertRC(rc);
+
+ /* Connect handlers: */
+ if (m_pMachineView)
+ prepareConnections();
+
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+ /* Decide if we are going to use GL to draw the guest screen: */
+ if (isGLWidgetSupported())
+ m_pGLWidget = new GLWidget(m_pMachineView->viewport(), this);
+#endif
+
+ /* Resize/rescale frame-buffer to the default size: */
+ performResize(640, 480);
+ performRescale();
+
+#ifdef Q_OS_WIN
+ CoCreateFreeThreadedMarshaler(this, m_pUnkMarshaler.asOutParam());
+#endif /* Q_OS_WIN */
+ return S_OK;
+}
+
+UIFrameBufferPrivate::~UIFrameBufferPrivate()
+{
+ LogRel2(("GUI: UIFrameBufferPrivate::~UIFrameBufferPrivate %p\n", this));
+
+ /* Disconnect handlers: */
+ if (m_pMachineView)
+ cleanupConnections();
+
+ /* Deinitialize critical-section: */
+ RTCritSectDelete(&m_critSect);
+}
+
+void UIFrameBufferPrivate::setView(UIMachineView *pMachineView)
+{
+ /* Disconnect old handlers: */
+ if (m_pMachineView)
+ cleanupConnections();
+
+ /* Reassign machine-view: */
+ m_pMachineView = pMachineView;
+ /* Recache window ID: */
+ m_iWinId = (m_pMachineView && m_pMachineView->viewport()) ? (LONG64)m_pMachineView->viewport()->winId() : 0;
+
+#ifdef VBOX_WS_X11
+ /* Sync Qt and X11 Server (see xTracker #7547). */
+ XSync(NativeWindowSubsystem::X11GetDisplay(), false);
+#endif
+
+ /* Connect new handlers: */
+ if (m_pMachineView)
+ prepareConnections();
+
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+ /* Decide if we are going to use GL to draw the guest screen: */
+ m_pGLWidget = 0;
+ if (m_pMachineView && isGLWidgetSupported())
+ m_pGLWidget = new GLWidget(m_pMachineView->viewport(), this);
+#endif
+}
+
+void UIFrameBufferPrivate::attach()
+{
+ m_uFramebufferId = display().AttachFramebuffer(m_uScreenId, CFramebuffer(this));
+}
+
+void UIFrameBufferPrivate::detach()
+{
+ CFramebuffer frameBuffer = display().QueryFramebuffer(m_uScreenId);
+ if (!frameBuffer.isNull())
+ {
+ display().DetachFramebuffer(m_uScreenId, m_uFramebufferId);
+ m_uFramebufferId = QUuid();
+ }
+}
+
+void UIFrameBufferPrivate::setMarkAsUnused(bool fUnused)
+{
+ lock();
+ m_fUnused = fUnused;
+ unlock();
+}
+
+HRESULT UIFrameBufferPrivate::FinalConstruct()
+{
+ return 0;
+}
+
+void UIFrameBufferPrivate::FinalRelease()
+{
+ return;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::COMGETTER(Width)(ULONG *puWidth)
+{
+ if (!puWidth)
+ return E_POINTER;
+ *puWidth = (ULONG)width();
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::COMGETTER(Height)(ULONG *puHeight)
+{
+ if (!puHeight)
+ return E_POINTER;
+ *puHeight = (ULONG)height();
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::COMGETTER(BitsPerPixel)(ULONG *puBitsPerPixel)
+{
+ if (!puBitsPerPixel)
+ return E_POINTER;
+ *puBitsPerPixel = bitsPerPixel();
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::COMGETTER(BytesPerLine)(ULONG *puBytesPerLine)
+{
+ if (!puBytesPerLine)
+ return E_POINTER;
+ *puBytesPerLine = bytesPerLine();
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::COMGETTER(PixelFormat)(BitmapFormat_T *puPixelFormat)
+{
+ if (!puPixelFormat)
+ return E_POINTER;
+ *puPixelFormat = (BitmapFormat_T)pixelFormat();
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::COMGETTER(HeightReduction)(ULONG *puHeightReduction)
+{
+ if (!puHeightReduction)
+ return E_POINTER;
+ *puHeightReduction = 0;
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::COMGETTER(Overlay)(IFramebufferOverlay **ppOverlay)
+{
+ if (!ppOverlay)
+ return E_POINTER;
+ *ppOverlay = 0;
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::COMGETTER(WinId)(LONG64 *pWinId)
+{
+ if (!pWinId)
+ return E_POINTER;
+ *pWinId = m_iWinId;
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::COMGETTER(Capabilities)(ComSafeArrayOut(FramebufferCapabilities_T, enmCapabilities))
+{
+ if (ComSafeArrayOutIsNull(enmCapabilities))
+ return E_POINTER;
+
+ com::SafeArray<FramebufferCapabilities_T> caps;
+ if (uiCommon().isSeparateProcess())
+ {
+ caps.resize(2);
+ caps[0] = FramebufferCapabilities_UpdateImage;
+ caps[1] = FramebufferCapabilities_RenderCursor;
+ }
+ else
+ {
+ caps.resize(3);
+ caps[0] = FramebufferCapabilities_VHWA;
+ caps[1] = FramebufferCapabilities_VisibleRegion;
+ caps[2] = FramebufferCapabilities_RenderCursor;
+ }
+
+ caps.detachTo(ComSafeArrayOutArg(enmCapabilities));
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::NotifyChange(ULONG uScreenId, ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight)
+{
+ CDisplaySourceBitmap sourceBitmap;
+ if (!uiCommon().isSeparateProcess())
+ display().QuerySourceBitmap(uScreenId, sourceBitmap);
+
+ /* Lock access to frame-buffer: */
+ lock();
+
+ /* Make sure frame-buffer is used: */
+ if (m_fUnused)
+ {
+ LogRel(("GUI: UIFrameBufferPrivate::NotifyChange: Screen=%lu, Origin=%lux%lu, Size=%lux%lu, Ignored!\n",
+ (unsigned long)uScreenId,
+ (unsigned long)uX, (unsigned long)uY,
+ (unsigned long)uWidth, (unsigned long)uHeight));
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Ignore NotifyChange: */
+ return E_FAIL;
+ }
+
+ /* Disable screen updates: */
+ m_fUpdatesAllowed = false;
+
+ /* While updates are disabled, visible region will be saved: */
+ m_pendingSyncVisibleRegion = QRegion();
+
+ if (!uiCommon().isSeparateProcess())
+ {
+ /* Acquire new pending bitmap: */
+ m_pendingSourceBitmap = sourceBitmap;
+ m_fPendingSourceBitmap = true;
+ }
+
+ /* Widget resize is NOT thread-safe and *probably* never will be,
+ * We have to notify machine-view with the async-signal to perform resize operation. */
+ LogRel2(("GUI: UIFrameBufferPrivate::NotifyChange: Screen=%lu, Origin=%lux%lu, Size=%lux%lu, Sending to async-handler\n",
+ (unsigned long)uScreenId,
+ (unsigned long)uX, (unsigned long)uY,
+ (unsigned long)uWidth, (unsigned long)uHeight));
+ emit sigNotifyChange(uWidth, uHeight);
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Give up control token to other thread: */
+ RTThreadYield();
+
+ /* Confirm NotifyChange: */
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::NotifyUpdate(ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight)
+{
+ /* Lock access to frame-buffer: */
+ lock();
+
+ /* Make sure frame-buffer is used: */
+ if (m_fUnused)
+ {
+ LogRel3(("GUI: UIFrameBufferPrivate::NotifyUpdate: Origin=%lux%lu, Size=%lux%lu, Ignored!\n",
+ (unsigned long)uX, (unsigned long)uY,
+ (unsigned long)uWidth, (unsigned long)uHeight));
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Ignore NotifyUpdate: */
+ return E_FAIL;
+ }
+
+ /* Widget update is NOT thread-safe and *seems* never will be,
+ * We have to notify machine-view with the async-signal to perform update operation. */
+ LogRel3(("GUI: UIFrameBufferPrivate::NotifyUpdate: Origin=%lux%lu, Size=%lux%lu, Sending to async-handler\n",
+ (unsigned long)uX, (unsigned long)uY,
+ (unsigned long)uWidth, (unsigned long)uHeight));
+ emit sigNotifyUpdate(uX, uY, uWidth, uHeight);
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Confirm NotifyUpdate: */
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::NotifyUpdateImage(ULONG uX, ULONG uY,
+ ULONG uWidth, ULONG uHeight,
+ ComSafeArrayIn(BYTE, image))
+{
+ /* Wrapping received data: */
+ com::SafeArray<BYTE> imageData(ComSafeArrayInArg(image));
+
+ /* Lock access to frame-buffer: */
+ lock();
+
+ /* Make sure frame-buffer is used: */
+ if (m_fUnused)
+ {
+ LogRel3(("GUI: UIFrameBufferPrivate::NotifyUpdateImage: Origin=%lux%lu, Size=%lux%lu, Ignored!\n",
+ (unsigned long)uX, (unsigned long)uY,
+ (unsigned long)uWidth, (unsigned long)uHeight));
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Ignore NotifyUpdate: */
+ return E_FAIL;
+ }
+ /* Directly update m_image if update fits: */
+ if ( m_fUpdatesAllowed
+ && uX + uWidth <= (ULONG)m_image.width()
+ && uY + uHeight <= (ULONG)m_image.height())
+ {
+ /* Copy to m_image: */
+ uchar *pu8Dst = m_image.bits() + uY * m_image.bytesPerLine() + uX * 4;
+ uchar *pu8Src = imageData.raw();
+ ULONG h;
+ for (h = 0; h < uHeight; ++h)
+ {
+ memcpy(pu8Dst, pu8Src, uWidth * 4);
+ pu8Dst += m_image.bytesPerLine();
+ pu8Src += uWidth * 4;
+ }
+
+ /* Widget update is NOT thread-safe and *seems* never will be,
+ * We have to notify machine-view with the async-signal to perform update operation. */
+ LogRel3(("GUI: UIFrameBufferPrivate::NotifyUpdateImage: Origin=%lux%lu, Size=%lux%lu, Sending to async-handler\n",
+ (unsigned long)uX, (unsigned long)uY,
+ (unsigned long)uWidth, (unsigned long)uHeight));
+ emit sigNotifyUpdate(uX, uY, uWidth, uHeight);
+ }
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Confirm NotifyUpdateImage: */
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::VideoModeSupported(ULONG uWidth, ULONG uHeight, ULONG uBPP, BOOL *pfSupported)
+{
+ /* Make sure result pointer is valid: */
+ if (!pfSupported)
+ {
+ LogRel2(("GUI: UIFrameBufferPrivate::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu, Invalid pfSupported pointer!\n",
+ (unsigned long)uBPP, (unsigned long)uWidth, (unsigned long)uHeight));
+
+ return E_POINTER;
+ }
+
+ /* Lock access to frame-buffer: */
+ lock();
+
+ /* Make sure frame-buffer is used: */
+ if (m_fUnused)
+ {
+ LogRel2(("GUI: UIFrameBufferPrivate::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu, Ignored!\n",
+ (unsigned long)uBPP, (unsigned long)uWidth, (unsigned long)uHeight));
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Ignore VideoModeSupported: */
+ return E_FAIL;
+ }
+
+ /* Determine if supported: */
+ *pfSupported = TRUE;
+ const QSize screenSize = m_pMachineView->maximumGuestSize();
+ if ( (screenSize.width() != 0)
+ && (uWidth > (ULONG)screenSize.width())
+ && (uWidth > (ULONG)width()))
+ *pfSupported = FALSE;
+ if ( (screenSize.height() != 0)
+ && (uHeight > (ULONG)screenSize.height())
+ && (uHeight > (ULONG)height()))
+ *pfSupported = FALSE;
+ if (*pfSupported)
+ LogRel2(("GUI: UIFrameBufferPrivate::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu is supported\n",
+ (unsigned long)uBPP, (unsigned long)uWidth, (unsigned long)uHeight));
+ else
+ LogRel(("GUI: UIFrameBufferPrivate::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu is NOT supported\n",
+ (unsigned long)uBPP, (unsigned long)uWidth, (unsigned long)uHeight));
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Confirm VideoModeSupported: */
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::GetVisibleRegion(BYTE *pRectangles, ULONG uCount, ULONG *puCountCopied)
+{
+ PRTRECT rects = (PRTRECT)pRectangles;
+
+ if (!rects)
+ return E_POINTER;
+
+ Q_UNUSED(uCount);
+ Q_UNUSED(puCountCopied);
+
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::SetVisibleRegion(BYTE *pRectangles, ULONG uCount)
+{
+ /* Make sure rectangles were passed: */
+ if (!pRectangles)
+ {
+ LogRel2(("GUI: UIFrameBufferPrivate::SetVisibleRegion: Rectangle count=%lu, Invalid pRectangles pointer!\n",
+ (unsigned long)uCount));
+
+ return E_POINTER;
+ }
+
+ /* Lock access to frame-buffer: */
+ lock();
+
+ /* Make sure frame-buffer is used: */
+ if (m_fUnused)
+ {
+ LogRel2(("GUI: UIFrameBufferPrivate::SetVisibleRegion: Rectangle count=%lu, Ignored!\n",
+ (unsigned long)uCount));
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Ignore SetVisibleRegion: */
+ return E_FAIL;
+ }
+
+ /* Compose region: */
+ QRegion region;
+ PRTRECT rects = (PRTRECT)pRectangles;
+ for (ULONG uIndex = 0; uIndex < uCount; ++uIndex)
+ {
+ /* Get current rectangle: */
+ QRect rect;
+ rect.setLeft(rects->xLeft);
+ rect.setTop(rects->yTop);
+ /* Which is inclusive: */
+ rect.setRight(rects->xRight - 1);
+ rect.setBottom(rects->yBottom - 1);
+ /* Append region: */
+ region += rect;
+ ++rects;
+ }
+ /* Tune according scale-factor: */
+ if (scaleFactor() != 1.0 || devicePixelRatio() > 1.0)
+ region = m_transform.map(region);
+
+ if (m_fUpdatesAllowed)
+ {
+ /* We are directly updating synchronous visible-region: */
+ m_syncVisibleRegion = region;
+ /* And send async-signal to update asynchronous one: */
+ LogRel2(("GUI: UIFrameBufferPrivate::SetVisibleRegion: Rectangle count=%lu, Sending to async-handler\n",
+ (unsigned long)uCount));
+ emit sigSetVisibleRegion(region);
+ }
+ else
+ {
+ /* Save the region. */
+ m_pendingSyncVisibleRegion = region;
+ LogRel2(("GUI: UIFrameBufferPrivate::SetVisibleRegion: Rectangle count=%lu, Saved\n",
+ (unsigned long)uCount));
+ }
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Confirm SetVisibleRegion: */
+ return S_OK;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::ProcessVHWACommand(BYTE *pCommand, LONG enmCmd, BOOL fGuestCmd)
+{
+ RT_NOREF(pCommand, enmCmd, fGuestCmd);
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP UIFrameBufferPrivate::Notify3DEvent(ULONG uType, ComSafeArrayIn(BYTE, data))
+{
+ /* Lock access to frame-buffer: */
+ lock();
+
+ /* Make sure frame-buffer is used: */
+ if (m_fUnused)
+ {
+ LogRel2(("GUI: UIFrameBufferPrivate::Notify3DEvent: Ignored!\n"));
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Ignore Notify3DEvent: */
+ return E_FAIL;
+ }
+
+ Q_UNUSED(data);
+#ifdef VBOX_WITH_XPCOM
+ Q_UNUSED(dataSize);
+#endif /* VBOX_WITH_XPCOM */
+ // com::SafeArray<BYTE> eventData(ComSafeArrayInArg(data));
+ switch (uType)
+ {
+ case VBOX3D_NOTIFY_TYPE_3DDATA_VISIBLE:
+ case VBOX3D_NOTIFY_TYPE_3DDATA_HIDDEN:
+ {
+ /// @todo wipe out whole case when confirmed
+ // We are no more supporting this, am I right?
+ AssertFailed();
+ /* Confirm Notify3DEvent: */
+ return S_OK;
+ }
+
+ case VBOX3D_NOTIFY_TYPE_TEST_FUNCTIONAL:
+ {
+ HRESULT hr = m_fUnused ? E_FAIL : S_OK;
+ unlock();
+ return hr;
+ }
+
+#if defined(VBOX_GUI_WITH_QTGLFRAMEBUFFER) && defined(RT_OS_LINUX)
+ case VBOX3D_NOTIFY_TYPE_HW_SCREEN_CREATED:
+ case VBOX3D_NOTIFY_TYPE_HW_SCREEN_DESTROYED:
+ case VBOX3D_NOTIFY_TYPE_HW_SCREEN_UPDATE_BEGIN:
+ case VBOX3D_NOTIFY_TYPE_HW_SCREEN_UPDATE_END:
+ {
+ HRESULT hr = S_OK;
+ com::SafeArray<BYTE> notifyData(ComSafeArrayInArg(data));
+ if (m_pGLWidget)
+ {
+ if (uType == VBOX3D_NOTIFY_TYPE_HW_SCREEN_CREATED)
+ {
+ LogRel4(("GUI: Notify3DEvent VBOX3D_NOTIFY_TYPE_3D_SCREEN_CREATED\n"));
+
+ struct NotifyData
+ {
+ uint64_t u64NativeHandle;
+ VisualID visualid;
+ };
+ struct NotifyData *pData = (struct NotifyData *)notifyData.raw();
+
+ GLWidgetSource *p = new GLWidgetSourcePixmap(m_pGLWidget, (Pixmap)pData->u64NativeHandle, pData->visualid);
+ m_pGLWidget->setSource(p, true);
+
+ LogRelMax(1, ("GUI: Created a HW accelerated screen\n"));
+ }
+ else if (uType == VBOX3D_NOTIFY_TYPE_HW_SCREEN_DESTROYED)
+ {
+ LogRel4(("GUI: Notify3DEvent VBOX3D_NOTIFY_TYPE_3D_SCREEN_DESTROYED\n"));
+
+ GLWidgetSource *p = new GLWidgetSourceImage(m_pGLWidget, &m_image);
+ m_pGLWidget->setSource(p, true);
+ }
+ else if (uType == VBOX3D_NOTIFY_TYPE_HW_SCREEN_UPDATE_BEGIN)
+ {
+ /* Do nothing. */
+ }
+ else if (uType == VBOX3D_NOTIFY_TYPE_HW_SCREEN_UPDATE_END)
+ {
+ struct NotifyData
+ {
+ uint64_t u64NativeHandle;
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+ };
+ struct NotifyData *pData = (struct NotifyData *)notifyData.raw();
+
+ /* Send the screen update message. */
+ int iX = pData->left;
+ int iY = pData->top;
+ int iWidth = pData->right - pData->left;
+ int iHeight = pData->bottom - pData->top;
+ emit sigNotifyUpdate(iX, iY, iWidth, iHeight);
+ }
+ }
+ else
+ {
+ hr = E_FAIL; // Not supported
+ }
+ unlock();
+ return hr;
+ }
+#endif /* defined(VBOX_GUI_WITH_QTGLFRAMEBUFFER) && defined(RT_OS_LINUX) */
+
+ default:
+ break;
+ }
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Ignore Notify3DEvent: */
+ return E_INVALIDARG;
+}
+
+void UIFrameBufferPrivate::handleNotifyChange(int iWidth, int iHeight)
+{
+ LogRel2(("GUI: UIFrameBufferPrivate::handleNotifyChange: Size=%dx%d\n", iWidth, iHeight));
+
+ /* Make sure machine-view is assigned: */
+ AssertPtrReturnVoid(m_pMachineView);
+
+ /* Lock access to frame-buffer: */
+ lock();
+
+ /* If there is NO pending source-bitmap: */
+ if (!uiCommon().isSeparateProcess() && !m_fPendingSourceBitmap)
+ {
+ /* Do nothing, change-event already processed: */
+ LogRel2(("GUI: UIFrameBufferPrivate::handleNotifyChange: Already processed.\n"));
+ /* Unlock access to frame-buffer: */
+ unlock();
+ /* Return immediately: */
+ return;
+ }
+
+ /* Release the current bitmap and keep the pending one: */
+ m_sourceBitmap = m_pendingSourceBitmap;
+ m_pendingSourceBitmap = 0;
+ m_fPendingSourceBitmap = false;
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+
+ /* Perform frame-buffer resize: */
+ performResize(iWidth, iHeight);
+}
+
+void UIFrameBufferPrivate::handlePaintEvent(QPaintEvent *pEvent)
+{
+ LogRel3(("GUI: UIFrameBufferPrivate::handlePaintEvent: Origin=%lux%lu, Size=%dx%d\n",
+ pEvent->rect().x(), pEvent->rect().y(),
+ pEvent->rect().width(), pEvent->rect().height()));
+
+ /* On mode switch the enqueued paint-event may still come
+ * while the machine-view is already null (before the new machine-view set),
+ * ignore paint-event in that case. */
+ if (!m_pMachineView)
+ return;
+
+ /* Lock access to frame-buffer: */
+ lock();
+
+ /* But if updates disabled: */
+ if (!m_fUpdatesAllowed)
+ {
+ /* Unlock access to frame-buffer: */
+ unlock();
+ /* And return immediately: */
+ return;
+ }
+
+ /* Depending on visual-state type: */
+ switch (m_pMachineView->machineLogic()->visualStateType())
+ {
+ case UIVisualStateType_Seamless:
+ paintSeamless(pEvent);
+ break;
+ default:
+ paintDefault(pEvent);
+ break;
+ }
+
+ /* Unlock access to frame-buffer: */
+ unlock();
+}
+
+void UIFrameBufferPrivate::handleSetVisibleRegion(const QRegion &region)
+{
+ /* Make sure async visible-region has changed or wasn't yet applied: */
+ if ( m_asyncVisibleRegion == region
+#ifdef VBOX_WITH_MASKED_SEAMLESS
+ && m_asyncVisibleRegion == m_pMachineView->machineWindow()->mask()
+#endif /* VBOX_WITH_MASKED_SEAMLESS */
+ )
+ return;
+
+ /* We are accounting async visible-regions one-by-one
+ * to keep corresponding viewport area always updated! */
+ if (!m_asyncVisibleRegion.isEmpty())
+ m_pMachineView->viewport()->update(m_asyncVisibleRegion - region);
+
+ /* Remember last visible region: */
+ m_asyncVisibleRegion = region;
+
+#ifdef VBOX_WITH_MASKED_SEAMLESS
+ /* We have to use async visible-region to apply to [Q]Widget [set]Mask: */
+ m_pMachineView->machineWindow()->setMask(m_asyncVisibleRegion);
+#endif /* VBOX_WITH_MASKED_SEAMLESS */
+}
+
+void UIFrameBufferPrivate::performResize(int iWidth, int iHeight)
+{
+ /* Make sure machine-view is assigned: */
+ AssertReturnVoidStmt(m_pMachineView, LogRel(("GUI: UIFrameBufferPrivate::performResize: Size=%dx%d\n", iWidth, iHeight)));
+
+ /* Invalidate visible-region (if necessary): */
+ if (m_pMachineView->machineLogic()->visualStateType() == UIVisualStateType_Seamless &&
+ (m_iWidth != iWidth || m_iHeight != iHeight))
+ {
+ lock();
+ m_syncVisibleRegion = QRegion();
+ m_asyncVisibleRegion = QRegion();
+ unlock();
+ }
+
+ /* If source-bitmap invalid: */
+ if (m_sourceBitmap.isNull())
+ {
+ /* Remember new size came from hint: */
+ m_iWidth = iWidth;
+ m_iHeight = iHeight;
+ LogRel(("GUI: UIFrameBufferPrivate::performResize: Size=%dx%d, Using fallback buffer since no source bitmap is provided\n",
+ m_iWidth, m_iHeight));
+
+ /* And recreate fallback buffer: */
+ m_image = QImage(m_iWidth, m_iHeight, QImage::Format_RGB32);
+ m_image.fill(0);
+ }
+ /* If source-bitmap valid: */
+ else
+ {
+ /* Acquire source-bitmap attributes: */
+ BYTE *pAddress = NULL;
+ ULONG ulWidth = 0;
+ ULONG ulHeight = 0;
+ ULONG ulBitsPerPixel = 0;
+ ULONG ulBytesPerLine = 0;
+ KBitmapFormat bitmapFormat = KBitmapFormat_Opaque;
+ m_sourceBitmap.QueryBitmapInfo(pAddress,
+ ulWidth,
+ ulHeight,
+ ulBitsPerPixel,
+ ulBytesPerLine,
+ bitmapFormat);
+ Assert(ulBitsPerPixel == 32);
+
+ /* Remember new actual size: */
+ m_iWidth = (int)ulWidth;
+ m_iHeight = (int)ulHeight;
+ LogRel2(("GUI: UIFrameBufferPrivate::performResize: Size=%dx%d, Directly using source bitmap content\n",
+ m_iWidth, m_iHeight));
+
+ /* Recreate QImage on the basis of source-bitmap content: */
+ m_image = QImage(pAddress, m_iWidth, m_iHeight, ulBytesPerLine, QImage::Format_RGB32);
+
+ /* Check whether guest color depth differs from the bitmap color depth: */
+ ULONG ulGuestBitsPerPixel = 0;
+ LONG xOrigin = 0;
+ LONG yOrigin = 0;
+ KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
+ display().GetScreenResolution(m_uScreenId, ulWidth, ulHeight, ulGuestBitsPerPixel, xOrigin, yOrigin, monitorStatus);
+
+ /* Remind user if necessary, ignore text and VGA modes: */
+ /* This check (supports graphics) is not quite right due to past mistakes
+ * in the Guest Additions protocol, but in practice it should be fine. */
+ if ( ulGuestBitsPerPixel != ulBitsPerPixel
+ && ulGuestBitsPerPixel != 0
+ && m_pMachineView->uisession()->isGuestSupportsGraphics())
+ UINotificationMessage::remindAboutWrongColorDepth(ulGuestBitsPerPixel, ulBitsPerPixel);
+ else
+ UINotificationMessage::forgetAboutWrongColorDepth();
+ }
+
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+ if (m_pGLWidget)
+ {
+ m_pGLWidget->resizeGuestScreen(m_iWidth, m_iHeight);
+
+ GLWidgetSource *p = new GLWidgetSourceImage(m_pGLWidget, &m_image);
+ m_pGLWidget->setSource(p, false);
+ }
+#endif
+
+ lock();
+
+ /* Enable screen updates: */
+ m_fUpdatesAllowed = true;
+
+ if (!m_pendingSyncVisibleRegion.isEmpty())
+ {
+ /* Directly update synchronous visible-region: */
+ m_syncVisibleRegion = m_pendingSyncVisibleRegion;
+ m_pendingSyncVisibleRegion = QRegion();
+
+ /* And send async-signal to update asynchronous one: */
+ LogRel2(("GUI: UIFrameBufferPrivate::performResize: Rectangle count=%lu, Sending to async-handler\n",
+ (unsigned long)m_syncVisibleRegion.rectCount()));
+ emit sigSetVisibleRegion(m_syncVisibleRegion);
+ }
+
+ /* Make sure that the current screen image is immediately displayed: */
+ m_pMachineView->viewport()->update();
+
+ unlock();
+
+ /* Make sure action-pool knows frame-buffer size: */
+ m_pMachineView->uisession()->actionPool()->toRuntime()->setGuestScreenSize(m_pMachineView->screenId(),
+ QSize(m_iWidth, m_iHeight));
+}
+
+void UIFrameBufferPrivate::performRescale()
+{
+// printf("UIFrameBufferPrivate::performRescale\n");
+
+ /* Make sure machine-view is assigned: */
+ AssertPtrReturnVoid(m_pMachineView);
+
+ /* Depending on current visual state: */
+ switch (m_pMachineView->machineLogic()->visualStateType())
+ {
+ case UIVisualStateType_Scale:
+ m_scaledSize = scaledSize().width() == m_iWidth && scaledSize().height() == m_iHeight ? QSize() : scaledSize();
+ break;
+ default:
+ m_scaledSize = scaleFactor() == 1.0 ? QSize() : QSize((int)(m_iWidth * scaleFactor()), (int)(m_iHeight * scaleFactor()));
+ break;
+ }
+
+ /* Update coordinate-system: */
+ updateCoordinateSystem();
+
+// printf("UIFrameBufferPrivate::performRescale: Complete: Scale-factor=%f, Scaled-size=%dx%d\n",
+// scaleFactor(), scaledSize().width(), scaledSize().height());
+}
+
+void UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange()
+{
+ /* Do we have view and valid cursor position?
+ * Also, please take into account, we are not currently painting
+ * framebuffer cursor if mouse integration is supported and enabled. */
+ if ( m_pMachineView
+ && !m_pMachineView->uisession()->isHidingHostPointer()
+ && m_pMachineView->uisession()->isValidPointerShapePresent()
+ && m_pMachineView->uisession()->isValidCursorPositionPresent()
+ && ( !m_pMachineView->uisession()->isMouseIntegrated()
+ || !m_pMachineView->uisession()->isMouseSupportsAbsolute()))
+ {
+ /* Acquire cursor hotspot: */
+ QPoint cursorHotspot = m_pMachineView->uisession()->cursorHotspot();
+ /* Apply the scale-factor if necessary: */
+ cursorHotspot /= scaleFactor();
+ /* Take the device-pixel-ratio into account: */
+ if (!useUnscaledHiDPIOutput())
+ cursorHotspot /= devicePixelRatioActual();
+
+ /* Acquire cursor position and size: */
+ QPoint cursorPosition = m_pMachineView->uisession()->cursorPosition() - cursorHotspot;
+ QSize cursorSize = m_pMachineView->uisession()->cursorSize();
+ /* Apply the scale-factor if necessary: */
+ cursorPosition *= scaleFactor();
+ cursorSize *= scaleFactor();
+ /* Take the device-pixel-ratio into account: */
+ if (!useUnscaledHiDPIOutput())
+ {
+ cursorPosition *= devicePixelRatioActual();
+ cursorSize *= devicePixelRatioActual();
+ }
+ cursorPosition /= devicePixelRatio();
+ cursorSize /= devicePixelRatio();
+
+ /* Call for a viewport update, we need to update cumulative
+ * region containing previous and current cursor rectagles. */
+ const QRect cursorRectangle = QRect(cursorPosition, cursorSize);
+ m_pMachineView->viewport()->update(QRegion(m_cursorRectangle) + cursorRectangle);
+
+ /* Remember current cursor rectangle: */
+ m_cursorRectangle = cursorRectangle;
+ }
+ /* Don't forget to clear the rectangle in opposite case: */
+ else if ( m_pMachineView
+ && m_cursorRectangle.isValid())
+ {
+ /* Call for a cursor area update: */
+ m_pMachineView->viewport()->update(m_cursorRectangle);
+ }
+}
+
+void UIFrameBufferPrivate::prepareConnections()
+{
+ /* Attach EMT connections: */
+ connect(this, &UIFrameBufferPrivate::sigNotifyChange,
+ m_pMachineView, &UIMachineView::sltHandleNotifyChange,
+ Qt::QueuedConnection);
+ connect(this, &UIFrameBufferPrivate::sigNotifyUpdate,
+ m_pMachineView, &UIMachineView::sltHandleNotifyUpdate,
+ Qt::QueuedConnection);
+ connect(this, &UIFrameBufferPrivate::sigSetVisibleRegion,
+ m_pMachineView, &UIMachineView::sltHandleSetVisibleRegion,
+ Qt::QueuedConnection);
+
+ /* Attach GUI connections: */
+ connect(m_pMachineView->uisession(), &UISession::sigMousePointerShapeChange,
+ this, &UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange);
+ connect(m_pMachineView->uisession(), &UISession::sigCursorPositionChange,
+ this, &UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange);
+}
+
+void UIFrameBufferPrivate::cleanupConnections()
+{
+ /* Detach EMT connections: */
+ disconnect(this, &UIFrameBufferPrivate::sigNotifyChange,
+ m_pMachineView, &UIMachineView::sltHandleNotifyChange);
+ disconnect(this, &UIFrameBufferPrivate::sigNotifyUpdate,
+ m_pMachineView, &UIMachineView::sltHandleNotifyUpdate);
+ disconnect(this, &UIFrameBufferPrivate::sigSetVisibleRegion,
+ m_pMachineView, &UIMachineView::sltHandleSetVisibleRegion);
+
+ /* Detach GUI connections: */
+ disconnect(m_pMachineView->uisession(), &UISession::sigMousePointerShapeChange,
+ this, &UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange);
+ disconnect(m_pMachineView->uisession(), &UISession::sigCursorPositionChange,
+ this, &UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange);
+}
+
+void UIFrameBufferPrivate::updateCoordinateSystem()
+{
+ /* Reset to default: */
+ m_transform = QTransform();
+
+ /* Apply the scale-factor if necessary: */
+ if (scaleFactor() != 1.0)
+ m_transform = m_transform.scale(scaleFactor(), scaleFactor());
+
+ /* Take the device-pixel-ratio into account: */
+ if (!useUnscaledHiDPIOutput())
+ m_transform = m_transform.scale(devicePixelRatioActual(), devicePixelRatioActual());
+ m_transform = m_transform.scale(1.0 / devicePixelRatio(), 1.0 / devicePixelRatio());
+}
+
+void UIFrameBufferPrivate::paintDefault(QPaintEvent *pEvent)
+{
+ /* Make sure cached image is valid: */
+ if (m_image.isNull())
+ return;
+
+#ifdef VBOX_GUI_WITH_QTGLFRAMEBUFFER
+ if (m_pGLWidget)
+ {
+ /* Draw the actually visible guest rectangle on the entire GLWidget.
+ * This code covers non-HiDPI normal and scaled modes. Scrollbars work too. */
+
+ /** @todo HiDPI support. Possibly need to split the geometry calculations from the QImage handling below
+ * and share the geometry code with the OpenGL code path. */
+
+ /* Set the visible guest rectangle: */
+ m_pGLWidget->setGuestVisibleRect(m_pMachineView->contentsX(), m_pMachineView->contentsY(),
+ convertHostXTo(m_pGLWidget->width()), convertHostYTo(m_pGLWidget->height()));
+
+ /* Tell the GL Widget to update the guest screen content from the source: */
+ m_pGLWidget->updateGuestImage();
+
+ /* Redraw: */
+ m_pGLWidget->update();
+
+ /* Done: */
+ return;
+ }
+#endif /* VBOX_GUI_WITH_QTGLFRAMEBUFFER */
+
+ /* First we take the cached image as the source: */
+ QImage *pSourceImage = &m_image;
+
+ /* But if we should scale image by some reason: */
+ if ( scaledSize().isValid()
+ || (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0))
+ {
+ /* Calculate final scaled size: */
+ QSize effectiveSize = !scaledSize().isValid() ? pSourceImage->size() : scaledSize();
+ /* Take the device-pixel-ratio into account: */
+ if (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0)
+ effectiveSize *= devicePixelRatioActual();
+ /* We scale the image to requested size and retain it
+ * by making heap shallow copy of that temporary object: */
+ switch (m_pMachineView->visualStateType())
+ {
+ case UIVisualStateType_Scale:
+ pSourceImage = new QImage(pSourceImage->scaled(effectiveSize, Qt::IgnoreAspectRatio,
+ transformationMode(scalingOptimizationType())));
+ break;
+ default:
+ pSourceImage = new QImage(pSourceImage->scaled(effectiveSize, Qt::IgnoreAspectRatio,
+ transformationMode(scalingOptimizationType(), m_dScaleFactor)));
+ break;
+ }
+ }
+
+ /* Take the device-pixel-ratio into account: */
+ pSourceImage->setDevicePixelRatio(devicePixelRatio());
+
+ /* Prepare the base and hidpi paint rectangles: */
+ const QRect paintRect = pEvent->rect();
+ QRect paintRectHiDPI = paintRect;
+
+ /* Take the device-pixel-ratio into account: */
+ paintRectHiDPI.moveTo(paintRectHiDPI.topLeft() * devicePixelRatio());
+ paintRectHiDPI.setSize(paintRectHiDPI.size() * devicePixelRatio());
+
+ /* Make sure hidpi paint rectangle is within the image boundary: */
+ paintRectHiDPI = paintRectHiDPI.intersected(pSourceImage->rect());
+ if (paintRectHiDPI.isEmpty())
+ return;
+
+ /* Create painter: */
+ QPainter painter(m_pMachineView->viewport());
+
+#ifdef VBOX_WS_MAC
+ /* On OSX for Qt5 we need to fill the backing store first: */
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.fillRect(paintRect, QColor(Qt::black));
+ painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
+#endif /* VBOX_WS_MAC */
+
+ /* Draw hidpi image rectangle: */
+ drawImageRect(painter, *pSourceImage, paintRectHiDPI,
+ m_pMachineView->contentsX(), m_pMachineView->contentsY(),
+ devicePixelRatio());
+
+ /* If we had to scale image for some reason: */
+ if ( scaledSize().isValid()
+ || (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0))
+ {
+ /* Wipe out copied image: */
+ delete pSourceImage;
+ pSourceImage = 0;
+ }
+
+ /* Paint cursor if it has valid shape and position.
+ * Also, please take into account, we are not currently painting
+ * framebuffer cursor if mouse integration is supported and enabled. */
+ if ( !m_cursorRectangle.isNull()
+ && !m_pMachineView->uisession()->isHidingHostPointer()
+ && m_pMachineView->uisession()->isValidPointerShapePresent()
+ && m_pMachineView->uisession()->isValidCursorPositionPresent()
+ && ( !m_pMachineView->uisession()->isMouseIntegrated()
+ || !m_pMachineView->uisession()->isMouseSupportsAbsolute()))
+ {
+ /* Acquire session cursor shape pixmap: */
+ QPixmap cursorPixmap = m_pMachineView->uisession()->cursorShapePixmap();
+
+ /* Take the device-pixel-ratio into account: */
+ cursorPixmap.setDevicePixelRatio(devicePixelRatio());
+
+ /* Draw sub-pixmap: */
+ painter.drawPixmap(m_cursorRectangle.topLeft(), cursorPixmap);
+ }
+}
+
+void UIFrameBufferPrivate::paintSeamless(QPaintEvent *pEvent)
+{
+ /* Make sure cached image is valid: */
+ if (m_image.isNull())
+ return;
+
+ /* First we take the cached image as the source: */
+ QImage *pSourceImage = &m_image;
+
+ /* But if we should scale image by some reason: */
+ if ( scaledSize().isValid()
+ || (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0))
+ {
+ /* Calculate final scaled size: */
+ QSize effectiveSize = !scaledSize().isValid() ? pSourceImage->size() : scaledSize();
+ /* Take the device-pixel-ratio into account: */
+ if (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0)
+ effectiveSize *= devicePixelRatioActual();
+ /* We scale the image to requested size and retain it
+ * by making heap shallow copy of that temporary object: */
+ switch (m_pMachineView->visualStateType())
+ {
+ case UIVisualStateType_Scale:
+ pSourceImage = new QImage(pSourceImage->scaled(effectiveSize, Qt::IgnoreAspectRatio,
+ transformationMode(scalingOptimizationType())));
+ break;
+ default:
+ pSourceImage = new QImage(pSourceImage->scaled(effectiveSize, Qt::IgnoreAspectRatio,
+ transformationMode(scalingOptimizationType(), m_dScaleFactor)));
+ break;
+ }
+ }
+
+ /* Take the device-pixel-ratio into account: */
+ pSourceImage->setDevicePixelRatio(devicePixelRatio());
+
+ /* Prepare the base and hidpi paint rectangles: */
+ const QRect paintRect = pEvent->rect();
+ QRect paintRectHiDPI = paintRect;
+
+ /* Take the device-pixel-ratio into account: */
+ paintRectHiDPI.moveTo(paintRectHiDPI.topLeft() * devicePixelRatio());
+ paintRectHiDPI.setSize(paintRectHiDPI.size() * devicePixelRatio());
+
+ /* Make sure hidpi paint rectangle is within the image boundary: */
+ paintRectHiDPI = paintRectHiDPI.intersected(pSourceImage->rect());
+ if (paintRectHiDPI.isEmpty())
+ return;
+
+ /* Create painter: */
+ QPainter painter(m_pMachineView->viewport());
+
+ /* Adjust painter for erasing: */
+ lock();
+ painter.setClipRegion(QRegion(paintRect) - m_syncVisibleRegion);
+ painter.setCompositionMode(QPainter::CompositionMode_Clear);
+ unlock();
+
+ /* Erase hidpi rectangle: */
+ eraseImageRect(painter, paintRectHiDPI,
+ devicePixelRatio());
+
+ /* Adjust painter for painting: */
+ lock();
+ painter.setClipRegion(QRegion(paintRect) & m_syncVisibleRegion);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
+ unlock();
+
+#ifdef VBOX_WITH_TRANSLUCENT_SEAMLESS
+ /* In case of translucent seamless for Qt5 we need to fill the backing store first: */
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.fillRect(paintRect, QColor(Qt::black));
+ painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
+#endif /* VBOX_WITH_TRANSLUCENT_SEAMLESS */
+
+ /* Draw hidpi image rectangle: */
+ drawImageRect(painter, *pSourceImage, paintRectHiDPI,
+ m_pMachineView->contentsX(), m_pMachineView->contentsY(),
+ devicePixelRatio());
+
+ /* If we had to scale image for some reason: */
+ if ( scaledSize().isValid()
+ || (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0))
+ {
+ /* Wipe out copied image: */
+ delete pSourceImage;
+ pSourceImage = 0;
+ }
+
+ /* Paint cursor if it has valid shape and position.
+ * Also, please take into account, we are not currently painting
+ * framebuffer cursor if mouse integration is supported and enabled. */
+ if ( !m_cursorRectangle.isNull()
+ && !m_pMachineView->uisession()->isHidingHostPointer()
+ && m_pMachineView->uisession()->isValidPointerShapePresent()
+ && m_pMachineView->uisession()->isValidCursorPositionPresent()
+ && ( !m_pMachineView->uisession()->isMouseIntegrated()
+ || !m_pMachineView->uisession()->isMouseSupportsAbsolute()))
+ {
+ /* Acquire session cursor shape pixmap: */
+ QPixmap cursorPixmap = m_pMachineView->uisession()->cursorShapePixmap();
+
+ /* Take the device-pixel-ratio into account: */
+ cursorPixmap.setDevicePixelRatio(devicePixelRatio());
+
+ /* Draw sub-pixmap: */
+ painter.drawPixmap(m_cursorRectangle.topLeft(), cursorPixmap);
+ }
+}
+
+/* static */
+Qt::TransformationMode UIFrameBufferPrivate::transformationMode(ScalingOptimizationType type, double dScaleFactor /* = 0 */)
+{
+ switch (type)
+ {
+ /* Check if optimization type is forced to be 'Performance': */
+ case ScalingOptimizationType_Performance: return Qt::FastTransformation;
+ default: break;
+ }
+ /* For integer-scaling we are choosing the 'Performance' optimization type ourselves: */
+ return dScaleFactor && floor(dScaleFactor) == dScaleFactor ? Qt::FastTransformation : Qt::SmoothTransformation;;
+}
+
+/* static */
+void UIFrameBufferPrivate::eraseImageRect(QPainter &painter, const QRect &rect,
+ double dDevicePixelRatio)
+{
+ /* Prepare sub-pixmap: */
+ QPixmap subPixmap = QPixmap(rect.width(), rect.height());
+ /* Take the device-pixel-ratio into account: */
+ subPixmap.setDevicePixelRatio(dDevicePixelRatio);
+
+ /* Which point we should draw corresponding sub-pixmap? */
+ QPoint paintPoint = rect.topLeft();
+ /* Take the device-pixel-ratio into account: */
+ paintPoint /= dDevicePixelRatio;
+
+ /* Draw sub-pixmap: */
+ painter.drawPixmap(paintPoint, subPixmap);
+}
+
+/* static */
+void UIFrameBufferPrivate::drawImageRect(QPainter &painter, const QImage &image, const QRect &rect,
+ int iContentsShiftX, int iContentsShiftY,
+ double dDevicePixelRatio)
+{
+ /* Calculate offset: */
+ const size_t offset = (rect.x() + iContentsShiftX) * image.depth() / 8 +
+ (rect.y() + iContentsShiftY) * image.bytesPerLine();
+
+ /* Restrain boundaries: */
+ const int iSubImageWidth = qMin(rect.width(), image.width() - rect.x() - iContentsShiftX);
+ const int iSubImageHeight = qMin(rect.height(), image.height() - rect.y() - iContentsShiftY);
+
+ /* Create sub-image (no copy involved): */
+ QImage subImage = QImage(image.bits() + offset,
+ iSubImageWidth, iSubImageHeight,
+ image.bytesPerLine(), image.format());
+
+ /* Create sub-pixmap on the basis of sub-image above (1st copy involved): */
+ QPixmap subPixmap = QPixmap::fromImage(subImage);
+ /* Take the device-pixel-ratio into account: */
+ subPixmap.setDevicePixelRatio(dDevicePixelRatio);
+
+ /* Which point we should draw corresponding sub-pixmap? */
+ QPoint paintPoint = rect.topLeft();
+ /* Take the device-pixel-ratio into account: */
+ paintPoint /= dDevicePixelRatio;
+
+ /* Draw sub-pixmap: */
+ painter.drawPixmap(paintPoint, subPixmap);
+}
+
+
+UIFrameBuffer::UIFrameBuffer()
+{
+ m_pFrameBuffer.createObject();
+}
+
+UIFrameBuffer::~UIFrameBuffer()
+{
+ m_pFrameBuffer.setNull();
+}
+
+HRESULT UIFrameBuffer::init(UIMachineView *pMachineView)
+{
+ return m_pFrameBuffer->init(pMachineView);
+}
+
+void UIFrameBuffer::attach()
+{
+ m_pFrameBuffer->attach();
+}
+
+void UIFrameBuffer::detach()
+{
+ m_pFrameBuffer->detach();
+}
+
+uchar* UIFrameBuffer::address()
+{
+ return m_pFrameBuffer->address();
+}
+
+ulong UIFrameBuffer::width() const
+{
+ return m_pFrameBuffer->width();
+}
+
+ulong UIFrameBuffer::height() const
+{
+ return m_pFrameBuffer->height();
+}
+
+ulong UIFrameBuffer::bitsPerPixel() const
+{
+ return m_pFrameBuffer->bitsPerPixel();
+}
+
+ulong UIFrameBuffer::bytesPerLine() const
+{
+ return m_pFrameBuffer->bytesPerLine();
+}
+
+UIVisualStateType UIFrameBuffer::visualState() const
+{
+ return m_pFrameBuffer->visualState();
+}
+
+void UIFrameBuffer::setView(UIMachineView *pMachineView)
+{
+ m_pFrameBuffer->setView(pMachineView);
+}
+
+void UIFrameBuffer::setMarkAsUnused(bool fUnused)
+{
+ m_pFrameBuffer->setMarkAsUnused(fUnused);
+}
+
+QSize UIFrameBuffer::scaledSize() const
+{
+ return m_pFrameBuffer->scaledSize();
+}
+
+void UIFrameBuffer::setScaledSize(const QSize &size /* = QSize() */)
+{
+ m_pFrameBuffer->setScaledSize(size);
+}
+
+int UIFrameBuffer::convertHostXTo(int iX) const
+{
+ return m_pFrameBuffer->convertHostXTo(iX);
+}
+
+int UIFrameBuffer::convertHostYTo(int iY) const
+{
+ return m_pFrameBuffer->convertHostXTo(iY);
+}
+
+double UIFrameBuffer::scaleFactor() const
+{
+ return m_pFrameBuffer->scaleFactor();
+}
+
+void UIFrameBuffer::setScaleFactor(double dScaleFactor)
+{
+ m_pFrameBuffer->setScaleFactor(dScaleFactor);
+}
+
+double UIFrameBuffer::devicePixelRatio() const
+{
+ return m_pFrameBuffer->devicePixelRatio();
+}
+
+void UIFrameBuffer::setDevicePixelRatio(double dDevicePixelRatio)
+{
+ m_pFrameBuffer->setDevicePixelRatio(dDevicePixelRatio);
+}
+
+double UIFrameBuffer::devicePixelRatioActual() const
+{
+ return m_pFrameBuffer->devicePixelRatioActual();
+}
+
+void UIFrameBuffer::setDevicePixelRatioActual(double dDevicePixelRatioActual)
+{
+ m_pFrameBuffer->setDevicePixelRatioActual(dDevicePixelRatioActual);
+}
+
+bool UIFrameBuffer::useUnscaledHiDPIOutput() const
+{
+ return m_pFrameBuffer->useUnscaledHiDPIOutput();
+}
+
+void UIFrameBuffer::setUseUnscaledHiDPIOutput(bool fUseUnscaledHiDPIOutput)
+{
+ m_pFrameBuffer->setUseUnscaledHiDPIOutput(fUseUnscaledHiDPIOutput);
+}
+
+ScalingOptimizationType UIFrameBuffer::scalingOptimizationType() const
+{
+ return m_pFrameBuffer->scalingOptimizationType();
+}
+
+void UIFrameBuffer::setScalingOptimizationType(ScalingOptimizationType type)
+{
+ m_pFrameBuffer->setScalingOptimizationType(type);
+}
+
+void UIFrameBuffer::handleNotifyChange(int iWidth, int iHeight)
+{
+ m_pFrameBuffer->handleNotifyChange(iWidth, iHeight);
+}
+
+void UIFrameBuffer::handlePaintEvent(QPaintEvent *pEvent)
+{
+ m_pFrameBuffer->handlePaintEvent(pEvent);
+}
+
+void UIFrameBuffer::handleSetVisibleRegion(const QRegion &region)
+{
+ m_pFrameBuffer->handleSetVisibleRegion(region);
+}
+
+void UIFrameBuffer::performResize(int iWidth, int iHeight)
+{
+ m_pFrameBuffer->performResize(iWidth, iHeight);
+}
+
+void UIFrameBuffer::performRescale()
+{
+ m_pFrameBuffer->performRescale();
+}
+
+void UIFrameBuffer::viewportResized(QResizeEvent *pEvent)
+{
+ m_pFrameBuffer->viewportResized(pEvent);
+}
+
+#include "UIFrameBuffer.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIFrameBuffer.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIFrameBuffer.h
new file mode 100644
index 00000000..a6203fc2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIFrameBuffer.h
@@ -0,0 +1,147 @@
+/* $Id: UIFrameBuffer.h $ */
+/** @file
+ * VBox Qt GUI - UIFrameBuffer class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIFrameBuffer_h
+#define FEQT_INCLUDED_SRC_runtime_UIFrameBuffer_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSize>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+/* Other VBox includes: */
+#include <VBox/com/ptr.h>
+
+/* Forward declarations: */
+class UIFrameBufferPrivate;
+class UIMachineView;
+class QResizeEvent;
+class QPaintEvent;
+class QRegion;
+
+/** IFramebuffer implementation used to maintain VM display video memory. */
+class UIFrameBuffer : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Frame-buffer constructor. */
+ UIFrameBuffer();
+
+ /** Frame-buffer destructor. */
+ ~UIFrameBuffer();
+
+ /** Frame-buffer initialization.
+ * @param pMachineView defines machine-view this frame-buffer is bounded to. */
+ HRESULT init(UIMachineView *pMachineView);
+
+ /** Assigns machine-view frame-buffer will be bounded to.
+ * @param pMachineView defines machine-view this frame-buffer is bounded to. */
+ void setView(UIMachineView *pMachineView);
+
+ /** Attach frame-buffer to the Display. */
+ void attach();
+ /** Detach frame-buffer from the Display. */
+ void detach();
+
+ /** Returns frame-buffer data address. */
+ uchar* address();
+ /** Returns frame-buffer width. */
+ ulong width() const;
+ /** Returns frame-buffer height. */
+ ulong height() const;
+ /** Returns frame-buffer bits-per-pixel value. */
+ ulong bitsPerPixel() const;
+ /** Returns frame-buffer bytes-per-line value. */
+ ulong bytesPerLine() const;
+ /** Returns the visual-state this frame-buffer is used for. */
+ UIVisualStateType visualState() const;
+
+ /** Defines whether frame-buffer is <b>unused</b>.
+ * @note Calls to this and any other EMT callback are synchronized (from GUI side). */
+ void setMarkAsUnused(bool fUnused);
+
+ /** Returns the frame-buffer's scaled-size. */
+ QSize scaledSize() const;
+ /** Defines host-to-guest scale ratio as @a size. */
+ void setScaledSize(const QSize &size = QSize());
+ /** Returns x-origin of the guest (actual) content corresponding to x-origin of host (scaled) content. */
+ int convertHostXTo(int iX) const;
+ /** Returns y-origin of the guest (actual) content corresponding to y-origin of host (scaled) content. */
+ int convertHostYTo(int iY) const;
+
+ /** Returns the scale-factor used by the frame-buffer. */
+ double scaleFactor() const;
+ /** Define the scale-factor used by the frame-buffer. */
+ void setScaleFactor(double dScaleFactor);
+
+ /** Returns device-pixel-ratio set for HiDPI frame-buffer. */
+ double devicePixelRatio() const;
+ /** Defines device-pixel-ratio set for HiDPI frame-buffer. */
+ void setDevicePixelRatio(double dDevicePixelRatio);
+ /** Returns actual device-pixel-ratio set for HiDPI frame-buffer. */
+ double devicePixelRatioActual() const;
+ /** Defines actual device-pixel-ratio set for HiDPI frame-buffer. */
+ void setDevicePixelRatioActual(double dDevicePixelRatioActual);
+
+ /** Returns whether frame-buffer should use unscaled HiDPI output. */
+ bool useUnscaledHiDPIOutput() const;
+ /** Defines whether frame-buffer should use unscaled HiDPI output. */
+ void setUseUnscaledHiDPIOutput(bool fUseUnscaledHiDPIOutput);
+
+ /** Returns the frame-buffer scaling optimization type. */
+ ScalingOptimizationType scalingOptimizationType() const;
+ /** Defines the frame-buffer scaling optimization type. */
+ void setScalingOptimizationType(ScalingOptimizationType type);
+
+ /** Handles frame-buffer notify-change-event. */
+ void handleNotifyChange(int iWidth, int iHeight);
+ /** Handles frame-buffer paint-event. */
+ void handlePaintEvent(QPaintEvent *pEvent);
+ /** Handles frame-buffer set-visible-region-event. */
+ void handleSetVisibleRegion(const QRegion &region);
+
+ /** Performs frame-buffer resizing. */
+ void performResize(int iWidth, int iHeight);
+ /** Performs frame-buffer rescaling. */
+ void performRescale();
+
+ /** Handles viewport resize-event. */
+ void viewportResized(QResizeEvent *pEvent);
+
+private:
+
+ /** Holds the frame-buffer private instance. */
+ ComObjPtr<UIFrameBufferPrivate> m_pFrameBuffer;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIFrameBuffer_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIIndicatorsPool.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIIndicatorsPool.cpp
new file mode 100644
index 00000000..2de9c758
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIIndicatorsPool.cpp
@@ -0,0 +1,1675 @@
+/* $Id: UIIndicatorsPool.cpp $ */
+/** @file
+ * VBox Qt GUI - UIIndicatorsPool class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QHBoxLayout>
+#include <QPainter>
+#include <QStyle>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UIIndicatorsPool.h"
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataManager.h"
+#include "UIMachineDefs.h"
+#include "UIConverter.h"
+#include "UIAnimationFramework.h"
+#include "UISession.h"
+#include "UIMedium.h"
+#include "UIIconPool.h"
+#include "UIHostComboEditor.h"
+#include "QIStatusBarIndicator.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CAudioSettings.h"
+#include "CGraphicsAdapter.h"
+#include "CRecordingSettings.h"
+#include "CRecordingScreenSettings.h"
+#include "CConsole.h"
+#include "CMachine.h"
+#include "CSystemProperties.h"
+#include "CMachineDebugger.h"
+#include "CGuest.h"
+#include "CStorageController.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CUSBController.h"
+#include "CUSBDeviceFilters.h"
+#include "CUSBDevice.h"
+#include "CSharedFolder.h"
+#include "CVRDEServer.h"
+
+/* Other VBox includes: */
+#include <iprt/time.h>
+
+
+/** QIStateStatusBarIndicator extension for Runtime UI. */
+class UISessionStateStatusBarIndicator : public QIWithRetranslateUI<QIStateStatusBarIndicator>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor which remembers passed @a session object. */
+ UISessionStateStatusBarIndicator(IndicatorType enmType, UISession *pSession);
+
+ /** Returns the indicator type. */
+ IndicatorType type() const { return m_enmType; }
+
+ /** Returns the indicator description. */
+ virtual QString description() const { return m_strDescription; }
+
+ /** Abstract update routine. */
+ virtual void updateAppearance() = 0;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Holds the indicator type. */
+ const IndicatorType m_enmType;
+
+ /** Holds the session UI reference. */
+ UISession *m_pSession;
+
+ /** Holds the indicator description. */
+ QString m_strDescription;
+
+ /** Holds the table format. */
+ static const QString s_strTable;
+ /** Holds the table row format 1. */
+ static const QString s_strTableRow1;
+ /** Holds the table row format 2. */
+ static const QString s_strTableRow2;
+ /** Holds the table row format 3. */
+ static const QString s_strTableRow3;
+ /** Holds the table row format 4. */
+ static const QString s_strTableRow4;
+};
+
+
+/* static */
+const QString UISessionStateStatusBarIndicator::s_strTable = QString("<table cellspacing=5 style='white-space:pre'>%1</table>");
+/* static */
+const QString UISessionStateStatusBarIndicator::s_strTableRow1 = QString("<tr><td colspan='2'><nobr><b>%1</b></nobr></td></tr>");
+/* static */
+const QString UISessionStateStatusBarIndicator::s_strTableRow2 = QString("<tr><td><nobr>%1:</nobr></td><td><nobr>%2</nobr></td></tr>");
+/* static */
+const QString UISessionStateStatusBarIndicator::s_strTableRow3 = QString("<tr><td><nobr>%1</nobr></td><td><nobr>%2</nobr></td></tr>");
+/* static */
+const QString UISessionStateStatusBarIndicator::s_strTableRow4 = QString("<tr><td><nobr>&nbsp;%1:</nobr></td><td><nobr>%2</nobr></td></tr>");
+
+
+/** QAccessibleWidget extension used as an accessibility interface for UISessionStateStatusBarIndicator. */
+class QIAccessibilityInterfaceForUISessionStateStatusBarIndicator : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating UISessionStateStatusBarIndicator accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UISessionStateStatusBarIndicator"))
+ return new QIAccessibilityInterfaceForUISessionStateStatusBarIndicator(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ QIAccessibilityInterfaceForUISessionStateStatusBarIndicator(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::Button)
+ {}
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text /* enmTextRole */) const RT_OVERRIDE
+ {
+ /* Sanity check: */
+ AssertPtrReturn(indicator(), 0);
+
+ /* Return the indicator description: */
+ return indicator()->description();
+ }
+
+private:
+
+ /** Returns corresponding UISessionStateStatusBarIndicator. */
+ UISessionStateStatusBarIndicator *indicator() const { return qobject_cast<UISessionStateStatusBarIndicator*>(widget()); }
+};
+
+
+UISessionStateStatusBarIndicator::UISessionStateStatusBarIndicator(IndicatorType enmType, UISession *pSession)
+ : m_enmType(enmType)
+ , m_pSession(pSession)
+{
+ /* Install UISessionStateStatusBarIndicator accessibility interface factory: */
+ QAccessible::installFactory(QIAccessibilityInterfaceForUISessionStateStatusBarIndicator::pFactory);
+}
+
+void UISessionStateStatusBarIndicator::retranslateUi()
+{
+ /* Translate description: */
+ m_strDescription = tr("%1 status-bar indicator", "like 'hard-disk status-bar indicator'")
+ .arg(gpConverter->toString(type()));
+
+ /* Update appearance finally: */
+ updateAppearance();
+}
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Hard-drive indicator. */
+class UIIndicatorHardDrive : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorHardDrive(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_HardDisks, pSession)
+ {
+ /* Assign state-icons: */
+ setStateIcon(KDeviceActivity_Idle, UIIconPool::iconSet(":/hd_16px.png"));
+ setStateIcon(KDeviceActivity_Reading, UIIconPool::iconSet(":/hd_read_16px.png"));
+ setStateIcon(KDeviceActivity_Writing, UIIconPool::iconSet(":/hd_write_16px.png"));
+ setStateIcon(KDeviceActivity_Null, UIIconPool::iconSet(":/hd_disabled_16px.png"));
+ /* Configure connection: */
+ connect(pSession, &UISession::sigStorageDeviceChange,
+ this, &UIIndicatorHardDrive::sltStorageDeviceChange);
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private slots:
+
+ /** Refresh the tooltip if the device config changes at runtime (hotplugging,
+ * USB storage). */
+ void sltStorageDeviceChange(const CMediumAttachment &attachment, bool fRemoved, bool fSilent)
+ {
+ RT_NOREF(attachment, fRemoved, fSilent);
+ updateAppearance();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get machine: */
+ const CMachine machine = m_pSession->machine();
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+
+ /* Enumerate all the controllers: */
+ bool fAttachmentsPresent = false;
+ foreach (const CStorageController &controller, machine.GetStorageControllers())
+ {
+ QString strAttData;
+ /* Enumerate all the attachments: */
+ foreach (const CMediumAttachment &attachment, machine.GetMediumAttachmentsOfController(controller.GetName()))
+ {
+ /* Skip unrelated attachments: */
+ if (attachment.GetType() != KDeviceType_HardDisk)
+ continue;
+ /* Append attachment data: */
+ strAttData += s_strTableRow4
+ .arg(gpConverter->toString(StorageSlot(controller.GetBus(), attachment.GetPort(), attachment.GetDevice())))
+ .arg(UIMedium(attachment.GetMedium(), UIMediumDeviceType_HardDisk).location());
+ fAttachmentsPresent = true;
+ }
+ /* Append controller data: */
+ if (!strAttData.isNull())
+ strFullData += s_strTableRow1.arg(controller.GetName()) + strAttData;
+ }
+
+ /* Show/hide indicator if there are no attachments
+ * and parent is visible already: */
+ if ( parentWidget()
+ && parentWidget()->isVisible())
+ setVisible(fAttachmentsPresent);
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ /* Update indicator state: */
+ setState(fAttachmentsPresent ? KDeviceActivity_Idle : KDeviceActivity_Null);
+ }
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Optical-drive indicator. */
+class UIIndicatorOpticalDisks : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorOpticalDisks(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_OpticalDisks, pSession)
+ {
+ /* Assign state-icons: */
+ setStateIcon(KDeviceActivity_Idle, UIIconPool::iconSet(":/cd_16px.png"));
+ setStateIcon(KDeviceActivity_Reading, UIIconPool::iconSet(":/cd_read_16px.png"));
+ setStateIcon(KDeviceActivity_Writing, UIIconPool::iconSet(":/cd_write_16px.png"));
+ setStateIcon(KDeviceActivity_Null, UIIconPool::iconSet(":/cd_disabled_16px.png"));
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get machine: */
+ const CMachine machine = m_pSession->machine();
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+
+ /* Enumerate all the controllers: */
+ bool fAttachmentsPresent = false;
+ bool fAttachmentsMounted = false;
+ foreach (const CStorageController &controller, machine.GetStorageControllers())
+ {
+ QString strAttData;
+ /* Enumerate all the attachments: */
+ foreach (const CMediumAttachment &attachment, machine.GetMediumAttachmentsOfController(controller.GetName()))
+ {
+ /* Skip unrelated attachments: */
+ if (attachment.GetType() != KDeviceType_DVD)
+ continue;
+ /* Append attachment data: */
+ UIMedium vboxMedium(attachment.GetMedium(), UIMediumDeviceType_DVD);
+ strAttData += s_strTableRow4
+ .arg(gpConverter->toString(StorageSlot(controller.GetBus(), attachment.GetPort(), attachment.GetDevice())))
+ .arg(vboxMedium.isNull() || vboxMedium.isHostDrive() ? vboxMedium.name() : vboxMedium.location());
+ fAttachmentsPresent = true;
+ if (!vboxMedium.isNull())
+ fAttachmentsMounted = true;
+ }
+ /* Append controller data: */
+ if (!strAttData.isNull())
+ strFullData += s_strTableRow1.arg(controller.GetName()) + strAttData;
+ }
+
+ /* Hide indicator if there are no attachments: */
+ if (!fAttachmentsPresent)
+ hide();
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ /* Update indicator state: */
+ setState(fAttachmentsMounted ? KDeviceActivity_Idle : KDeviceActivity_Null);
+ }
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Floppy-drive indicator. */
+class UIIndicatorFloppyDisks : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorFloppyDisks(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_FloppyDisks, pSession)
+ {
+ /* Assign state-icons: */
+ setStateIcon(KDeviceActivity_Idle, UIIconPool::iconSet(":/fd_16px.png"));
+ setStateIcon(KDeviceActivity_Reading, UIIconPool::iconSet(":/fd_read_16px.png"));
+ setStateIcon(KDeviceActivity_Writing, UIIconPool::iconSet(":/fd_write_16px.png"));
+ setStateIcon(KDeviceActivity_Null, UIIconPool::iconSet(":/fd_disabled_16px.png"));
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get machine: */
+ const CMachine machine = m_pSession->machine();
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+
+ /* Enumerate all the controllers: */
+ bool fAttachmentsPresent = false;
+ bool fAttachmentsMounted = false;
+ foreach (const CStorageController &controller, machine.GetStorageControllers())
+ {
+ QString strAttData;
+ /* Enumerate all the attachments: */
+ foreach (const CMediumAttachment &attachment, machine.GetMediumAttachmentsOfController(controller.GetName()))
+ {
+ /* Skip unrelated attachments: */
+ if (attachment.GetType() != KDeviceType_Floppy)
+ continue;
+ /* Append attachment data: */
+ UIMedium vboxMedium(attachment.GetMedium(), UIMediumDeviceType_Floppy);
+ strAttData += s_strTableRow4
+ .arg(gpConverter->toString(StorageSlot(controller.GetBus(), attachment.GetPort(), attachment.GetDevice())))
+ .arg(vboxMedium.isNull() || vboxMedium.isHostDrive() ? vboxMedium.name() : vboxMedium.location());
+ fAttachmentsPresent = true;
+ if (!vboxMedium.isNull())
+ fAttachmentsMounted = true;
+ }
+ /* Append controller data: */
+ if (!strAttData.isNull())
+ strFullData += s_strTableRow1.arg(controller.GetName()) + strAttData;
+ }
+
+ /* Hide indicator if there are no attachments: */
+ if (!fAttachmentsPresent)
+ hide();
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ /* Update indicator state: */
+ setState(fAttachmentsMounted ? KDeviceActivity_Idle : KDeviceActivity_Null);
+ }
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Audio indicator. */
+class UIIndicatorAudio : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Audio states. */
+ enum AudioState
+ {
+ AudioState_AllOff = 0,
+ AudioState_OutputOn = RT_BIT(0),
+ AudioState_InputOn = RT_BIT(1),
+ AudioState_AllOn = AudioState_InputOn | AudioState_OutputOn
+ };
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorAudio(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_Audio, pSession)
+ {
+ /* Assign state-icons: */
+ setStateIcon(AudioState_AllOff, UIIconPool::iconSet(":/audio_all_off_16px.png"));
+ setStateIcon(AudioState_OutputOn, UIIconPool::iconSet(":/audio_input_off_16px.png"));
+ setStateIcon(AudioState_InputOn, UIIconPool::iconSet(":/audio_output_off_16px.png"));
+ setStateIcon(AudioState_AllOn, UIIconPool::iconSet(":/audio_16px.png"));
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get machine: */
+ const CMachine comMachine = m_pSession->machine();
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+
+ /* Get audio adapter: */
+ const CAudioSettings comAudioSettings = comMachine.GetAudioSettings();
+ const CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ const bool fAudioEnabled = comAdapter.GetEnabled();
+ if (fAudioEnabled)
+ {
+ const bool fEnabledOutput = comAdapter.GetEnabledOut();
+ const bool fEnabledInput = comAdapter.GetEnabledIn();
+ strFullData = QString(s_strTableRow2).arg(QApplication::translate("UIDetails", "Audio Output", "details (audio)"),
+ fEnabledOutput ?
+ QApplication::translate("UIDetails", "Enabled", "details (audio/output)") :
+ QApplication::translate("UIDetails", "Disabled", "details (audio/output)"))
+ + QString(s_strTableRow2).arg(QApplication::translate("UIDetails", "Audio Input", "details (audio)"),
+ fEnabledInput ?
+ QApplication::translate("UIDetails", "Enabled", "details (audio/input)") :
+ QApplication::translate("UIDetails", "Disabled", "details (audio/input)"));
+ AudioState enmState = AudioState_AllOff;
+ if (fEnabledOutput)
+ enmState = (AudioState)(enmState | AudioState_OutputOn);
+ if (fEnabledInput)
+ enmState = (AudioState)(enmState | AudioState_InputOn);
+ setState(enmState);
+ }
+
+ /* Hide indicator if adapter is disabled: */
+ if (!fAudioEnabled)
+ hide();
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ }
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Network indicator. */
+class UIIndicatorNetwork : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorNetwork(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_Network, pSession)
+ , m_pTimerAutoUpdate(0)
+ , m_cMaxNetworkAdapters(0)
+ {
+ /* Assign state-icons: */
+ setStateIcon(KDeviceActivity_Idle, UIIconPool::iconSet(":/nw_16px.png"));
+ setStateIcon(KDeviceActivity_Reading, UIIconPool::iconSet(":/nw_read_16px.png"));
+ setStateIcon(KDeviceActivity_Writing, UIIconPool::iconSet(":/nw_write_16px.png"));
+ setStateIcon(KDeviceActivity_Null, UIIconPool::iconSet(":/nw_disabled_16px.png"));
+ /* Configure machine state-change listener: */
+ connect(m_pSession, &UISession::sigMachineStateChange,
+ this, &UIIndicatorNetwork::sltHandleMachineStateChange);
+ /* Fetch maximum network adapters count: */
+ const CVirtualBox vbox = uiCommon().virtualBox();
+ const CMachine machine = m_pSession->machine();
+ m_cMaxNetworkAdapters = vbox.GetSystemProperties().GetMaxNetworkAdapters(machine.GetChipsetType());
+ /* Create auto-update timer: */
+ m_pTimerAutoUpdate = new QTimer(this);
+ if (m_pTimerAutoUpdate)
+ {
+ /* Configure auto-update timer: */
+ connect(m_pTimerAutoUpdate, &QTimer::timeout, this, &UIIndicatorNetwork::sltUpdateNetworkIPs);
+ /* Start timer immediately if machine is running: */
+ sltHandleMachineStateChange();
+ }
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private slots:
+
+ /** Updates auto-update timer depending on machine state. */
+ void sltHandleMachineStateChange()
+ {
+ if (m_pSession->machineState() == KMachineState_Running)
+ {
+ /* Start auto-update timer otherwise: */
+ m_pTimerAutoUpdate->start(5000);
+ return;
+ }
+ /* Stop auto-update timer otherwise: */
+ m_pTimerAutoUpdate->stop();
+ }
+
+ /** Updates network IP addresses. */
+ void sltUpdateNetworkIPs()
+ {
+ updateAppearance();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get machine: */
+ const CMachine machine = m_pSession->machine();
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+
+ /* Gather adapter properties: */
+ RTTIMESPEC time;
+ uint64_t u64Now = RTTimeSpecGetNano(RTTimeNow(&time));
+ QString strFlags, strCount;
+ LONG64 iTimestamp;
+ machine.GetGuestProperty("/VirtualBox/GuestInfo/Net/Count", strCount, iTimestamp, strFlags);
+ bool fPropsValid = (u64Now - iTimestamp < UINT64_C(60000000000)); /* timeout beacon */
+ QStringList ipList, macList;
+ if (fPropsValid)
+ {
+ const int cAdapters = RT_MIN(strCount.toInt(), (int)m_cMaxNetworkAdapters);
+ for (int i = 0; i < cAdapters; ++i)
+ {
+ ipList << machine.GetGuestPropertyValue(QString("/VirtualBox/GuestInfo/Net/%1/V4/IP").arg(i));
+ macList << machine.GetGuestPropertyValue(QString("/VirtualBox/GuestInfo/Net/%1/MAC").arg(i));
+ }
+ }
+
+ /* Enumerate up to m_cMaxNetworkAdapters adapters: */
+ bool fAdaptersPresent = false;
+ bool fCablesDisconnected = true;
+ for (ulong uSlot = 0; uSlot < m_cMaxNetworkAdapters; ++uSlot)
+ {
+ const CNetworkAdapter &adapter = machine.GetNetworkAdapter(uSlot);
+ if (machine.isOk() && !adapter.isNull() && adapter.GetEnabled())
+ {
+ fAdaptersPresent = true;
+ QString strGuestIp;
+ if (fPropsValid)
+ {
+ const QString strGuestMac = adapter.GetMACAddress();
+ int iIp = macList.indexOf(strGuestMac);
+ if (iIp >= 0)
+ strGuestIp = ipList[iIp];
+ }
+ /* Check if the adapter's cable is connected: */
+ const bool fCableConnected = adapter.GetCableConnected();
+ if (fCablesDisconnected && fCableConnected)
+ fCablesDisconnected = false;
+ /* Append adapter data: */
+ strFullData += s_strTableRow1
+ .arg(QApplication::translate("UIIndicatorsPool", "Adapter %1 (%2)", "Network tooltip")
+ .arg(uSlot + 1).arg(gpConverter->toString(adapter.GetAttachmentType())));
+ if (!strGuestIp.isEmpty())
+ strFullData += s_strTableRow4
+ .arg(QApplication::translate("UIIndicatorsPool", "IP", "Network tooltip"), strGuestIp);
+ strFullData += s_strTableRow4
+ .arg(QApplication::translate("UIIndicatorsPool", "Cable", "Network tooltip"))
+ .arg(fCableConnected ?
+ QApplication::translate("UIIndicatorsPool", "Connected", "cable (Network tooltip)") :
+ QApplication::translate("UIIndicatorsPool", "Disconnected", "cable (Network tooltip)"));
+ }
+ }
+
+ /* Hide indicator if there are no enabled adapters: */
+ if (!fAdaptersPresent)
+ hide();
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ /* Update indicator state: */
+ setState(fAdaptersPresent && !fCablesDisconnected ? KDeviceActivity_Idle : KDeviceActivity_Null);
+ }
+
+ /** Holds the auto-update timer instance. */
+ QTimer *m_pTimerAutoUpdate;
+ /** Holds the maximum amount of the network adapters. */
+ ulong m_cMaxNetworkAdapters;
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: USB indicator. */
+class UIIndicatorUSB : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorUSB(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_USB, pSession)
+ {
+ /* Assign state-icons: */
+ setStateIcon(KDeviceActivity_Idle, UIIconPool::iconSet(":/usb_16px.png"));
+ setStateIcon(KDeviceActivity_Reading, UIIconPool::iconSet(":/usb_read_16px.png"));
+ setStateIcon(KDeviceActivity_Writing, UIIconPool::iconSet(":/usb_write_16px.png"));
+ setStateIcon(KDeviceActivity_Null, UIIconPool::iconSet(":/usb_disabled_16px.png"));
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get machine: */
+ const CMachine machine = m_pSession->machine();
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+
+ /* Check whether there is at least one USB controller with an available proxy. */
+ bool fUSBEnabled = !machine.GetUSBDeviceFilters().isNull()
+ && !machine.GetUSBControllers().isEmpty()
+ && machine.GetUSBProxyAvailable();
+ if (fUSBEnabled)
+ {
+ /* Enumerate all the USB devices: */
+ const CConsole console = m_pSession->console();
+ foreach (const CUSBDevice &usbDevice, console.GetUSBDevices())
+ strFullData += s_strTableRow1.arg(uiCommon().usbDetails(usbDevice));
+ /* Handle 'no-usb-devices' case: */
+ if (strFullData.isNull())
+ strFullData = s_strTableRow1
+ .arg(QApplication::translate("UIIndicatorsPool", "No USB devices attached", "USB tooltip"));
+ }
+
+ /* Hide indicator if there are USB controllers: */
+ if (!fUSBEnabled)
+ hide();
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ /* Update indicator state: */
+ setState(fUSBEnabled ? KDeviceActivity_Idle : KDeviceActivity_Null);
+ }
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Shared-folders indicator. */
+class UIIndicatorSharedFolders : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorSharedFolders(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_SharedFolders, pSession)
+ {
+ /* Assign state-icons: */
+ setStateIcon(KDeviceActivity_Idle, UIIconPool::iconSet(":/sf_16px.png"));
+ setStateIcon(KDeviceActivity_Reading, UIIconPool::iconSet(":/sf_read_16px.png"));
+ setStateIcon(KDeviceActivity_Writing, UIIconPool::iconSet(":/sf_write_16px.png"));
+ setStateIcon(KDeviceActivity_Null, UIIconPool::iconSet(":/sf_disabled_16px.png"));
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get objects: */
+ const CMachine machine = m_pSession->machine();
+ const CConsole console = m_pSession->console();
+ const CGuest guest = m_pSession->guest();
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+
+ /* Enumerate all the folders: */
+ QMap<QString, QString> sfs;
+ foreach (const CSharedFolder &sf, machine.GetSharedFolders())
+ sfs.insert(sf.GetName(), sf.GetHostPath());
+ foreach (const CSharedFolder &sf, console.GetSharedFolders())
+ sfs.insert(sf.GetName(), sf.GetHostPath());
+
+ /* Append attachment data: */
+ for (QMap<QString, QString>::const_iterator it = sfs.constBegin(); it != sfs.constEnd(); ++it)
+ {
+ /* Select slashes depending on the OS type: */
+ if (UICommon::isDOSType(guest.GetOSTypeId()))
+ strFullData += s_strTableRow2.arg(QString("<b>\\\\vboxsvr\\%1</b>").arg(it.key()), it.value());
+ else
+ strFullData += s_strTableRow2.arg(QString("<b>%1</b>").arg(it.key()), it.value());
+ }
+ /* Handle 'no-folders' case: */
+ if (sfs.isEmpty())
+ strFullData = s_strTableRow1
+ .arg(QApplication::translate("UIIndicatorsPool", "No shared folders", "Shared folders tooltip"));
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ /* Update indicator state: */
+ setState(!sfs.isEmpty() ? KDeviceActivity_Idle : KDeviceActivity_Null);
+ }
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Display indicator. */
+class UIIndicatorDisplay : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorDisplay(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_Display, pSession)
+ {
+ /* Assign state-icons: */
+ setStateIcon(KDeviceActivity_Null, UIIconPool::iconSet(":/display_software_16px.png"));
+ setStateIcon(KDeviceActivity_Idle, UIIconPool::iconSet(":/display_hardware_16px.png"));
+ setStateIcon(KDeviceActivity_Writing, UIIconPool::iconSet(":/display_hardware_write_16px.png"));
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get machine: */
+ const CMachine machine = m_pSession->machine();
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+
+ /* Get graphics adapter: */
+ CGraphicsAdapter comGraphics = machine.GetGraphicsAdapter();
+
+ /* Video Memory: */
+ const ULONG uVRAMSize = comGraphics.GetVRAMSize();
+ const QString strVRAMSize = UICommon::tr("<nobr>%1 MB</nobr>", "details report").arg(uVRAMSize);
+ strFullData += s_strTableRow2
+ .arg(QApplication::translate("UIIndicatorsPool", "Video memory", "Display tooltip"), strVRAMSize);
+
+ /* Monitor Count: */
+ const ULONG uMonitorCount = comGraphics.GetMonitorCount();
+ if (uMonitorCount > 1)
+ {
+ const QString strMonitorCount = QString::number(uMonitorCount);
+ strFullData += s_strTableRow2
+ .arg(QApplication::translate("UIIndicatorsPool", "Screens", "Display tooltip"), strMonitorCount);
+ }
+
+ /* 3D acceleration: */
+ const bool fAcceleration3D = comGraphics.GetAccelerate3DEnabled();
+ if (fAcceleration3D)
+ {
+ const QString strAcceleration3D = fAcceleration3D ?
+ UICommon::tr("Enabled", "details report (3D Acceleration)") :
+ UICommon::tr("Disabled", "details report (3D Acceleration)");
+ strFullData += s_strTableRow2
+ .arg(QApplication::translate("UIIndicatorsPool", "3D acceleration", "Display tooltip"), strAcceleration3D);
+ }
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ /* Set initial indicator state: */
+ setState(fAcceleration3D ? KDeviceActivity_Idle : KDeviceActivity_Null);
+ }
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Recording indicator. */
+class UIIndicatorRecording : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+ Q_PROPERTY(double rotationAngleStart READ rotationAngleStart);
+ Q_PROPERTY(double rotationAngleFinal READ rotationAngleFinal);
+ Q_PROPERTY(double rotationAngle READ rotationAngle WRITE setRotationAngle);
+
+ /** Recording states. */
+ enum UIIndicatorStateRecording
+ {
+ UIIndicatorStateRecording_Disabled = 0,
+ UIIndicatorStateRecording_Enabled = 1,
+ UIIndicatorStateRecording_Paused = 2
+ };
+
+ /** Recording modes. */
+ enum UIIndicatorStateRecordingMode
+ {
+ UIIndicatorStateRecordingMode_None = RT_BIT(0),
+ UIIndicatorStateRecordingMode_Video = RT_BIT(1),
+ UIIndicatorStateRecordingMode_Audio = RT_BIT(2)
+ };
+
+public:
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorRecording(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_Recording, pSession)
+ , m_pAnimation(0)
+ , m_dRotationAngle(0)
+ , m_enmRecordingMode(UIIndicatorStateRecordingMode_None)
+ {
+ /* Assign state-icons: */
+ setStateIcon(UIIndicatorStateRecording_Disabled, UIIconPool::iconSet(":/video_capture_16px.png"));
+ setStateIcon(UIIndicatorStateRecording_Enabled, UIIconPool::iconSet(":/movie_reel_16px.png"));
+ setStateIcon(UIIndicatorStateRecording_Paused, UIIconPool::iconSet(":/movie_reel_16px.png"));
+ /* Create *enabled* state animation: */
+ m_pAnimation = UIAnimationLoop::installAnimationLoop(this, "rotationAngle",
+ "rotationAngleStart", "rotationAngleFinal",
+ 1000);
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private slots:
+
+ /** Handles state change. */
+ void setState(int iState)
+ {
+ /* Update animation state: */
+ switch (iState)
+ {
+ case UIIndicatorStateRecording_Disabled:
+ m_pAnimation->stop();
+ m_dRotationAngle = 0;
+ break;
+ case UIIndicatorStateRecording_Enabled:
+ m_pAnimation->start();
+ break;
+ case UIIndicatorStateRecording_Paused:
+ m_pAnimation->stop();
+ break;
+ default:
+ break;
+ }
+ /* Call to base-class: */
+ QIStateStatusBarIndicator::setState(iState);
+ }
+
+private:
+
+ /** Paint-event handler. */
+ void paintEvent(QPaintEvent*)
+ {
+ /* Create new painter: */
+ QPainter painter(this);
+ /* Configure painter for *enabled* state: */
+ if (state() == UIIndicatorStateRecording_Enabled)
+ {
+ /* Configure painter for smooth animation: */
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setRenderHint(QPainter::SmoothPixmapTransform);
+ /* Shift rotation origin according pixmap center: */
+ painter.translate(height() / 2, height() / 2);
+ /* Rotate painter: */
+ painter.rotate(rotationAngle());
+ /* Unshift rotation origin according pixmap center: */
+ painter.translate(- height() / 2, - height() / 2);
+ }
+ /* Draw contents: */
+ drawContents(&painter);
+ }
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get machine: */
+ const CMachine comMachine = m_pSession->machine();
+ const bool fMachinePaused = m_pSession->isPaused();
+
+ /* Update indicator state early: */
+ CRecordingSettings comRecordingSettings = comMachine.GetRecordingSettings();
+ Assert(comRecordingSettings.isOk());
+ if (!comRecordingSettings.GetEnabled())
+ setState(UIIndicatorStateRecording_Disabled);
+ else if (!fMachinePaused)
+ setState(UIIndicatorStateRecording_Enabled);
+ else
+ setState(UIIndicatorStateRecording_Paused);
+
+ updateRecordingMode();
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+ switch (state())
+ {
+ case UIIndicatorStateRecording_Disabled:
+ {
+ strFullData += s_strTableRow1
+ .arg(QApplication::translate("UIIndicatorsPool", "Recording disabled", "Recording tooltip"));
+ break;
+ }
+ case UIIndicatorStateRecording_Enabled:
+ case UIIndicatorStateRecording_Paused:
+ {
+ QString strToolTip;
+ if ( m_enmRecordingMode & UIIndicatorStateRecordingMode_Audio
+ && m_enmRecordingMode & UIIndicatorStateRecordingMode_Video)
+ strToolTip = QApplication::translate("UIIndicatorsPool", "Video/audio recording file", "Recording tooltip");
+ else if (m_enmRecordingMode & UIIndicatorStateRecordingMode_Audio)
+ strToolTip = QApplication::translate("UIIndicatorsPool", "Audio recording file", "Recording tooltip");
+ else if (m_enmRecordingMode & UIIndicatorStateRecordingMode_Video)
+ strToolTip = QApplication::translate("UIIndicatorsPool", "Video recording file", "Recording tooltip");
+
+ /* For now all screens have the same config: */
+ CRecordingScreenSettings comRecordingScreen0Settings = comRecordingSettings.GetScreenSettings(0);
+ Assert(comRecordingScreen0Settings.isOk());
+
+ strFullData += s_strTableRow2
+ .arg(strToolTip)
+ .arg(comRecordingScreen0Settings.GetFilename());
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ }
+
+ /** Returns rotation start angle. */
+ double rotationAngleStart() const { return 0; }
+ /** Returns rotation finish angle. */
+ double rotationAngleFinal() const { return 360; }
+ /** Returns current rotation angle. */
+ double rotationAngle() const { return m_dRotationAngle; }
+ /** Defines current rotation angle. */
+ void setRotationAngle(double dRotationAngle) { m_dRotationAngle = dRotationAngle; update(); }
+
+ /* Parses RecordScreenSettings::Options and updates m_enmRecordingMode accordingly. */
+ void updateRecordingMode()
+ {
+ m_enmRecordingMode = UIIndicatorStateRecordingMode_None;
+
+ /* Get machine: */
+ if (!m_pSession)
+ return;
+ const CMachine comMachine = m_pSession->machine();
+ if (comMachine.isNull())
+ return;
+
+ CRecordingSettings comRecordingSettings = comMachine.GetRecordingSettings();
+ /* For now all screens have the same config: */
+ CRecordingScreenSettings recordingScreen0Settings = comRecordingSettings.GetScreenSettings(0);
+ if (recordingScreen0Settings.IsFeatureEnabled(KRecordingFeature_Video))
+ m_enmRecordingMode = (UIIndicatorStateRecordingMode)((int)m_enmRecordingMode | (int)UIIndicatorStateRecordingMode_Video);
+
+ if (recordingScreen0Settings.IsFeatureEnabled(KRecordingFeature_Audio))
+ m_enmRecordingMode = (UIIndicatorStateRecordingMode)((int)m_enmRecordingMode | (int)UIIndicatorStateRecordingMode_Audio);
+ }
+
+ /** Holds the rotation animation instance. */
+ UIAnimationLoop *m_pAnimation;
+ /** Holds current rotation angle. */
+ double m_dRotationAngle;
+
+ /** Holds the recording mode. */
+ UIIndicatorStateRecordingMode m_enmRecordingMode;
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Features indicator. */
+class UIIndicatorFeatures : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pSession to the UISessionStateStatusBarIndicator constructor. */
+ UIIndicatorFeatures(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_Features, pSession)
+ , m_iCPULoadPercentage(0)
+ {
+ /* Assign state-icons: */
+/** @todo The vtx_amdv_disabled_16px.png icon isn't really approprate anymore (no raw-mode),
+ * might want to get something different for KVMExecutionEngine_Emulated or reuse the
+ * vm_execution_engine_native_api_16px.png one... @bugref{9898} */
+ setStateIcon(KVMExecutionEngine_NotSet, UIIconPool::iconSet(":/vtx_amdv_disabled_16px.png"));
+ setStateIcon(KVMExecutionEngine_Emulated, UIIconPool::iconSet(":/vtx_amdv_disabled_16px.png"));
+ setStateIcon(KVMExecutionEngine_HwVirt, UIIconPool::iconSet(":/vtx_amdv_16px.png"));
+ setStateIcon(KVMExecutionEngine_NativeApi, UIIconPool::iconSet(":/vm_execution_engine_native_api_16px.png"));
+
+ /* Configure machine state-change listener: */
+ connect(m_pSession, &UISession::sigMachineStateChange,
+ this, &UIIndicatorFeatures::sltHandleMachineStateChange);
+ m_pTimerAutoUpdate = new QTimer(this);
+ if (m_pTimerAutoUpdate)
+ {
+ connect(m_pTimerAutoUpdate, &QTimer::timeout, this, &UIIndicatorFeatures::sltTimeout);
+ /* Start the timer immediately if the machine is running: */
+ sltHandleMachineStateChange();
+ }
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+protected:
+
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE
+ {
+ UISessionStateStatusBarIndicator::paintEvent(pEvent);
+ QPainter painter(this);
+
+ /* Draw a thin bar on th right hand side of the icon indication CPU load: */
+ QLinearGradient gradient(0, 0, 0, height());
+ gradient.setColorAt(1.0, Qt::green);
+ gradient.setColorAt(0.5, Qt::yellow);
+ gradient.setColorAt(0.0, Qt::red);
+
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(gradient);
+ /* Use 20% of the icon width to draw the indicator bar: */
+ painter.drawRect(QRect(QPoint(0.8 * width(), (100 - m_iCPULoadPercentage) / 100.f * height()),
+ QPoint(width(), height())));
+ /* Draw an empty rect. around the CPU load bar: */
+ int iBorderThickness = 1;
+ QRect outRect(QPoint(0.8 * width(), 0),
+ QPoint(width() - 2 * iBorderThickness, height() - 2 * iBorderThickness));
+ painter.setPen(QPen(Qt::black, 1));
+ painter.setBrush(Qt::NoBrush);
+ painter.drawRect(outRect);
+ }
+
+private slots:
+
+ /** Updates auto-update timer depending on machine state. */
+ void sltHandleMachineStateChange()
+ {
+ if (m_pSession->machineState() == KMachineState_Running)
+ {
+ /* Start auto-update timer otherwise: */
+ m_pTimerAutoUpdate->start(1000);
+ return;
+ }
+ /* Stop auto-update timer otherwise: */
+ m_pTimerAutoUpdate->stop();
+ }
+
+ void sltTimeout()
+ {
+ if (!m_pSession)
+ return;
+ CMachineDebugger comMachineDebugger = m_pSession->debugger();
+ if (comMachineDebugger.isNull())
+ return;
+ ULONG aPctExecuting;
+ ULONG aPctHalted;
+ ULONG aPctOther;
+ comMachineDebugger.GetCPULoad(0x7fffffff, aPctExecuting, aPctHalted, aPctOther);
+ m_iCPULoadPercentage = aPctExecuting + aPctOther;
+ update();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ /* Get objects: */
+ const CMachine machine = m_pSession->machine();
+
+ /* VT-x/AMD-V feature: */
+ KVMExecutionEngine enmEngine = m_pSession->getVMExecutionEngine();
+ QString strExecutionEngine;
+ switch (enmEngine)
+ {
+ case KVMExecutionEngine_HwVirt:
+ strExecutionEngine = "VT-x/AMD-V"; /* no translation */
+ break;
+ case KVMExecutionEngine_Emulated:
+ strExecutionEngine = "IEM"; /* no translation */
+ break;
+ case KVMExecutionEngine_NativeApi:
+ strExecutionEngine = "native API"; /* no translation */
+ break;
+ default:
+ AssertFailed();
+ enmEngine = KVMExecutionEngine_NotSet;
+ RT_FALL_THRU();
+ case KVMExecutionEngine_NotSet:
+ strExecutionEngine = UICommon::tr("not set", "details report (execution engine)");
+ break;
+ }
+
+ /* Nested Paging feature: */
+ const QString strNestedPaging = m_pSession->isHWVirtExNestedPagingEnabled() ?
+ UICommon::tr("Active", "details report (Nested Paging)") :
+ UICommon::tr("Inactive", "details report (Nested Paging)");
+
+ /* Unrestricted Execution feature: */
+ const QString strUnrestrictExec = m_pSession->isHWVirtExUXEnabled() ?
+ UICommon::tr("Active", "details report (Unrestricted Execution)") :
+ UICommon::tr("Inactive", "details report (Unrestricted Execution)");
+
+ /* CPU Execution Cap feature: */
+ QString strCPUExecCap = QString::number(machine.GetCPUExecutionCap());
+
+ /* Paravirtualization feature: */
+ const QString strParavirt = gpConverter->toString(m_pSession->paraVirtProvider());
+
+ /* Prepare tool-tip: */
+ QString strFullData;
+ //strFullData += s_strTableRow2.arg(UICommon::tr("VT-x/AMD-V", "details report"), strVirtualization);
+ strFullData += s_strTableRow2.arg(UICommon::tr("Execution engine", "details report"), strExecutionEngine);
+ strFullData += s_strTableRow2.arg(UICommon::tr("Nested Paging"), strNestedPaging);
+ strFullData += s_strTableRow2.arg(UICommon::tr("Unrestricted Execution"), strUnrestrictExec);
+ strFullData += s_strTableRow2.arg(UICommon::tr("Execution Cap", "details report"), strCPUExecCap);
+ strFullData += s_strTableRow2.arg(UICommon::tr("Paravirtualization Interface", "details report"), strParavirt);
+ const int cpuCount = machine.GetCPUCount();
+ if (cpuCount > 1)
+ strFullData += s_strTableRow2.arg(UICommon::tr("Processors", "details report"), QString::number(cpuCount));
+
+ /* Update tool-tip: */
+ setToolTip(s_strTable.arg(strFullData));
+ /* Update indicator state: */
+ setState(enmEngine);
+ }
+
+ QTimer *m_pTimerAutoUpdate;
+ ULONG m_iCPULoadPercentage;
+};
+
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Mouse indicator. */
+class UIIndicatorMouse : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, using @a pSession for state-update routine. */
+ UIIndicatorMouse(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_Mouse, pSession)
+ {
+ /* Assign state-icons: */
+ setStateIcon(0, UIIconPool::iconSet(":/mouse_disabled_16px.png"));
+ setStateIcon(1, UIIconPool::iconSet(":/mouse_16px.png"));
+ setStateIcon(2, UIIconPool::iconSet(":/mouse_seamless_16px.png"));
+ setStateIcon(3, UIIconPool::iconSet(":/mouse_can_seamless_16px.png"));
+ setStateIcon(4, UIIconPool::iconSet(":/mouse_can_seamless_uncaptured_16px.png"));
+ /* Configure connection: */
+ connect(pSession, &UISession::sigMouseStateChange,
+ this, static_cast<void(UIIndicatorMouse::*)(int)>(&UIIndicatorMouse::setState));
+ setState(pSession->mouseState());
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private slots:
+
+ /** Handles state change. */
+ void setState(int iState)
+ {
+ if ((iState & UIMouseStateType_MouseAbsoluteDisabled) &&
+ (iState & UIMouseStateType_MouseAbsolute) &&
+ !(iState & UIMouseStateType_MouseCaptured))
+ {
+ QIStateStatusBarIndicator::setState(4);
+ }
+ else
+ {
+ QIStateStatusBarIndicator::setState(iState & (UIMouseStateType_MouseAbsolute | UIMouseStateType_MouseCaptured));
+ }
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ const QString strToolTip = QApplication::translate("UIIndicatorsPool",
+ "Indicates whether the host mouse pointer is "
+ "captured by the guest OS:%1", "Mouse tooltip");
+ QString strFullData;
+ strFullData += s_strTableRow3
+ .arg(QString("<img src=:/mouse_disabled_16px.png/>"))
+ .arg(QApplication::translate("UIIndicatorsPool", "pointer is not captured", "Mouse tooltip"));
+ strFullData += s_strTableRow3
+ .arg(QString("<img src=:/mouse_16px.png/>"))
+ .arg(QApplication::translate("UIIndicatorsPool", "pointer is captured", "Mouse tooltip"));
+ strFullData += s_strTableRow3
+ .arg(QString("<img src=:/mouse_seamless_16px.png/>"))
+ .arg(QApplication::translate("UIIndicatorsPool", "mouse integration (MI) is On", "Mouse tooltip"));
+ strFullData += s_strTableRow3
+ .arg(QString("<img src=:/mouse_can_seamless_16px.png/>"))
+ .arg(QApplication::translate("UIIndicatorsPool", "MI is Off, pointer is captured", "Mouse tooltip"));
+ strFullData += s_strTableRow3
+ .arg(QString("<img src=:/mouse_can_seamless_uncaptured_16px.png/>"))
+ .arg(QApplication::translate("UIIndicatorsPool", "MI is Off, pointer is not captured", "Mouse tooltip"));
+ strFullData = s_strTable.arg(strFullData);
+ strFullData += QApplication::translate("UIIndicatorsPool",
+ "Note that the mouse integration feature requires Guest "
+ "Additions to be installed in the guest OS.", "Mouse tooltip");
+
+ /* Update tool-tip: */
+ setToolTip(strToolTip.arg(strFullData));
+ }
+};
+
+/** UISessionStateStatusBarIndicator extension for Runtime UI: Keyboard indicator. */
+class UIIndicatorKeyboard : public UISessionStateStatusBarIndicator
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, using @a pSession for state-update routine. */
+ UIIndicatorKeyboard(UISession *pSession)
+ : UISessionStateStatusBarIndicator(IndicatorType_Keyboard, pSession)
+ {
+ /* Assign state-icons: */
+ setStateIcon(0, UIIconPool::iconSet(":/hostkey_16px.png"));
+ setStateIcon(1, UIIconPool::iconSet(":/hostkey_captured_16px.png"));
+ setStateIcon(2, UIIconPool::iconSet(":/hostkey_pressed_16px.png"));
+ setStateIcon(3, UIIconPool::iconSet(":/hostkey_captured_pressed_16px.png"));
+ setStateIcon(4, UIIconPool::iconSet(":/hostkey_checked_16px.png"));
+ setStateIcon(5, UIIconPool::iconSet(":/hostkey_captured_checked_16px.png"));
+ setStateIcon(6, UIIconPool::iconSet(":/hostkey_pressed_checked_16px.png"));
+ setStateIcon(7, UIIconPool::iconSet(":/hostkey_captured_pressed_checked_16px.png"));
+ /* Configure connection: */
+ connect(pSession, &UISession::sigKeyboardStateChange,
+ this, static_cast<void(UIIndicatorKeyboard::*)(int)>(&UIIndicatorKeyboard::setState));
+ setState(pSession->keyboardState());
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+private:
+
+ /** Update routine. */
+ void updateAppearance()
+ {
+ const QString strToolTip = QApplication::translate("UIIndicatorsPool",
+ "Indicates whether the host keyboard is "
+ "captured by the guest OS:%1", "Keyboard tooltip");
+ QString strFullData;
+ strFullData += s_strTableRow3
+ .arg(QString("<img src=:/hostkey_16px.png/>"))
+ .arg(QApplication::translate("UIIndicatorsPool", "keyboard is not captured", "Keyboard tooltip"));
+ strFullData += s_strTableRow3
+ .arg(QString("<img src=:/hostkey_captured_16px.png/>"))
+ .arg(QApplication::translate("UIIndicatorsPool", "keyboard is captured", "Keyboard tooltip"));
+ strFullData = s_strTable.arg(strFullData);
+
+ /* Update tool-tip: */
+ setToolTip(strToolTip.arg(strFullData));
+ }
+};
+
+/** QITextStatusBarIndicator extension for Runtime UI: Keyboard-extension indicator. */
+class UIIndicatorKeyboardExtension : public QIWithRetranslateUI<QITextStatusBarIndicator>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor. */
+ UIIndicatorKeyboardExtension()
+ {
+ /* Make sure host-combination label will be updated: */
+ connect(gEDataManager, &UIExtraDataManager::sigRuntimeUIHostKeyCombinationChange,
+ this, &UIIndicatorKeyboardExtension::sltUpdateAppearance);
+ /* Translate finally: */
+ retranslateUi();
+ }
+
+public slots:
+
+ /** Update routine. */
+ void sltUpdateAppearance()
+ {
+ setText(UIHostCombo::toReadableString(gEDataManager->hostKeyCombination()));
+ }
+
+private:
+
+ /** Retranslation routine. */
+ void retranslateUi()
+ {
+ sltUpdateAppearance();
+ setToolTip(QApplication::translate("UIMachineWindowNormal",
+ "Shows the currently assigned Host key.<br>"
+ "This key, when pressed alone, toggles the keyboard and mouse "
+ "capture state. It can also be used in combination with other keys "
+ "to quickly perform actions from the main menu."));
+ }
+};
+
+
+UIIndicatorsPool::UIIndicatorsPool(UISession *pSession, QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_pSession(pSession)
+ , m_fEnabled(false)
+ , m_pTimerAutoUpdate(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIIndicatorsPool::~UIIndicatorsPool()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIIndicatorsPool::updateAppearance(IndicatorType indicatorType)
+{
+ /* Skip missed indicators: */
+ if (!m_pool.contains(indicatorType))
+ return;
+
+ /* Get indicator: */
+ QIStatusBarIndicator *pIndicator = m_pool.value(indicatorType);
+
+ /* Assert indicators with NO appearance: */
+ UISessionStateStatusBarIndicator *pSessionStateIndicator =
+ qobject_cast<UISessionStateStatusBarIndicator*>(pIndicator);
+ AssertPtrReturnVoid(pSessionStateIndicator);
+
+ /* Update indicator appearance: */
+ pSessionStateIndicator->updateAppearance();
+}
+
+void UIIndicatorsPool::setAutoUpdateIndicatorStates(bool fEnabled)
+{
+ /* Make sure auto-update timer exists: */
+ AssertPtrReturnVoid(m_pTimerAutoUpdate);
+
+ /* Start/stop timer: */
+ if (fEnabled)
+ m_pTimerAutoUpdate->start(100);
+ else
+ m_pTimerAutoUpdate->stop();
+}
+
+QPoint UIIndicatorsPool::mapIndicatorPositionToGlobal(IndicatorType enmIndicatorType, const QPoint &indicatorPosition)
+{
+ if (m_pool.contains(enmIndicatorType))
+ return m_pool.value(enmIndicatorType)->mapToGlobal(indicatorPosition);
+ return QPoint(0, 0);
+}
+
+void UIIndicatorsPool::sltHandleConfigurationChange(const QUuid &uMachineID)
+{
+ /* Skip unrelated machine IDs: */
+ if (uiCommon().managedVMUuid() != uMachineID)
+ return;
+
+ /* Update pool: */
+ updatePool();
+}
+
+void UIIndicatorsPool::sltAutoUpdateIndicatorStates()
+{
+ /* We should update states for following indicators: */
+ QVector<KDeviceType> deviceTypes;
+ if (m_pool.contains(IndicatorType_HardDisks))
+ deviceTypes.append(KDeviceType_HardDisk);
+ if (m_pool.contains(IndicatorType_OpticalDisks))
+ deviceTypes.append(KDeviceType_DVD);
+ if (m_pool.contains(IndicatorType_FloppyDisks))
+ deviceTypes.append(KDeviceType_Floppy);
+ if (m_pool.contains(IndicatorType_USB))
+ deviceTypes.append(KDeviceType_USB);
+ if (m_pool.contains(IndicatorType_Network))
+ deviceTypes.append(KDeviceType_Network);
+ if (m_pool.contains(IndicatorType_SharedFolders))
+ deviceTypes.append(KDeviceType_SharedFolder);
+ if (m_pool.contains(IndicatorType_Display))
+ deviceTypes.append(KDeviceType_Graphics3D);
+
+ /* Acquire current states from the console: */
+ CConsole console = m_pSession->console();
+ if (console.isNull() || !console.isOk())
+ return;
+ const QVector<KDeviceActivity> states = console.GetDeviceActivity(deviceTypes);
+ AssertReturnVoid(console.isOk());
+
+ /* Update indicators with the acquired states: */
+ for (int iIndicator = 0; iIndicator < states.size(); ++iIndicator)
+ {
+ QIStatusBarIndicator *pIndicator = 0;
+ switch (deviceTypes[iIndicator])
+ {
+ case KDeviceType_HardDisk: pIndicator = m_pool.value(IndicatorType_HardDisks); break;
+ case KDeviceType_DVD: pIndicator = m_pool.value(IndicatorType_OpticalDisks); break;
+ case KDeviceType_Floppy: pIndicator = m_pool.value(IndicatorType_FloppyDisks); break;
+ case KDeviceType_USB: pIndicator = m_pool.value(IndicatorType_USB); break;
+ case KDeviceType_Network: pIndicator = m_pool.value(IndicatorType_Network); break;
+ case KDeviceType_SharedFolder: pIndicator = m_pool.value(IndicatorType_SharedFolders); break;
+ case KDeviceType_Graphics3D: pIndicator = m_pool.value(IndicatorType_Display); break;
+ default: AssertFailed(); break;
+ }
+ if (pIndicator)
+ updateIndicatorStateForDevice(pIndicator, states[iIndicator]);
+ }
+}
+
+void UIIndicatorsPool::sltContextMenuRequest(QIStatusBarIndicator *pIndicator, QContextMenuEvent *pEvent)
+{
+ /* If that is one of pool indicators: */
+ foreach (IndicatorType indicatorType, m_pool.keys())
+ if (m_pool[indicatorType] == pIndicator)
+ {
+ /* Notify listener: */
+ emit sigContextMenuRequest(indicatorType, pEvent->pos());
+ return;
+ }
+}
+
+void UIIndicatorsPool::prepare()
+{
+ /* Prepare connections: */
+ prepareConnections();
+ /* Prepare contents: */
+ prepareContents();
+ /* Prepare auto-update timer: */
+ prepareUpdateTimer();
+}
+
+void UIIndicatorsPool::prepareConnections()
+{
+ /* Listen for the status-bar configuration changes: */
+ connect(gEDataManager, &UIExtraDataManager::sigStatusBarConfigurationChange,
+ this, &UIIndicatorsPool::sltHandleConfigurationChange);
+}
+
+void UIIndicatorsPool::prepareContents()
+{
+ /* Create main-layout: */
+ m_pMainLayout = new QHBoxLayout(this);
+ AssertPtrReturnVoid(m_pMainLayout);
+ {
+ /* Configure main-layout: */
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setSpacing(5);
+#else
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2);
+#endif
+ /* Update pool: */
+ updatePool();
+ }
+}
+
+void UIIndicatorsPool::prepareUpdateTimer()
+{
+ /* Create auto-update timer: */
+ m_pTimerAutoUpdate = new QTimer(this);
+ AssertPtrReturnVoid(m_pTimerAutoUpdate);
+ {
+ /* Configure auto-update timer: */
+ connect(m_pTimerAutoUpdate, &QTimer::timeout,
+ this, &UIIndicatorsPool::sltAutoUpdateIndicatorStates);
+ setAutoUpdateIndicatorStates(true);
+ }
+}
+
+void UIIndicatorsPool::updatePool()
+{
+ /* Acquire status-bar availability: */
+ m_fEnabled = gEDataManager->statusBarEnabled(uiCommon().managedVMUuid());
+ /* If status-bar is not enabled: */
+ if (!m_fEnabled)
+ {
+ /* Remove all indicators: */
+ while (!m_pool.isEmpty())
+ {
+ const IndicatorType firstType = m_pool.keys().first();
+ delete m_pool.value(firstType);
+ m_pool.remove(firstType);
+ }
+ /* And return: */
+ return;
+ }
+
+ /* Acquire status-bar restrictions: */
+ m_restrictions = gEDataManager->restrictedStatusBarIndicators(uiCommon().managedVMUuid());
+ /* Make sure 'Recording' is restricted as well if no features supported: */
+ if ( !m_restrictions.contains(IndicatorType_Recording)
+ && !uiCommon().supportedRecordingFeatures())
+ m_restrictions << IndicatorType_Recording;
+
+ /* Remove restricted indicators: */
+ foreach (const IndicatorType &indicatorType, m_restrictions)
+ {
+ if (m_pool.contains(indicatorType))
+ {
+ delete m_pool.value(indicatorType);
+ m_pool.remove(indicatorType);
+ }
+ }
+
+ /* Acquire status-bar order: */
+ m_order = gEDataManager->statusBarIndicatorOrder(uiCommon().managedVMUuid());
+ /* Make sure the order is complete taking restrictions into account: */
+ for (int iType = IndicatorType_Invalid; iType < IndicatorType_Max; ++iType)
+ {
+ /* Get iterated type: */
+ IndicatorType type = (IndicatorType)iType;
+ /* Skip invalid type: */
+ if (type == IndicatorType_Invalid)
+ continue;
+ /* Take restriction/presence into account: */
+ bool fRestricted = m_restrictions.contains(type);
+ bool fPresent = m_order.contains(type);
+ if (fRestricted && fPresent)
+ m_order.removeAll(type);
+ else if (!fRestricted && !fPresent)
+ m_order << type;
+ }
+
+ /* Add/Update allowed indicators: */
+ foreach (const IndicatorType &indicatorType, m_order)
+ {
+ /* Indicator exists: */
+ if (m_pool.contains(indicatorType))
+ {
+ /* Get indicator: */
+ QIStatusBarIndicator *pIndicator = m_pool.value(indicatorType);
+ /* Make sure it have valid position: */
+ const int iWantedIndex = indicatorPosition(indicatorType);
+ const int iActualIndex = m_pMainLayout->indexOf(pIndicator);
+ if (iActualIndex != iWantedIndex)
+ {
+ /* Re-inject indicator into main-layout at proper position: */
+ m_pMainLayout->removeWidget(pIndicator);
+ m_pMainLayout->insertWidget(iWantedIndex, pIndicator);
+ }
+ }
+ /* Indicator missed: */
+ else
+ {
+ /* Create indicator: */
+ switch (indicatorType)
+ {
+ case IndicatorType_HardDisks: m_pool[indicatorType] = new UIIndicatorHardDrive(m_pSession); break;
+ case IndicatorType_OpticalDisks: m_pool[indicatorType] = new UIIndicatorOpticalDisks(m_pSession); break;
+ case IndicatorType_FloppyDisks: m_pool[indicatorType] = new UIIndicatorFloppyDisks(m_pSession); break;
+ case IndicatorType_Audio: m_pool[indicatorType] = new UIIndicatorAudio(m_pSession); break;
+ case IndicatorType_Network: m_pool[indicatorType] = new UIIndicatorNetwork(m_pSession); break;
+ case IndicatorType_USB: m_pool[indicatorType] = new UIIndicatorUSB(m_pSession); break;
+ case IndicatorType_SharedFolders: m_pool[indicatorType] = new UIIndicatorSharedFolders(m_pSession); break;
+ case IndicatorType_Display: m_pool[indicatorType] = new UIIndicatorDisplay(m_pSession); break;
+ case IndicatorType_Recording: m_pool[indicatorType] = new UIIndicatorRecording(m_pSession); break;
+ case IndicatorType_Features: m_pool[indicatorType] = new UIIndicatorFeatures(m_pSession); break;
+ case IndicatorType_Mouse: m_pool[indicatorType] = new UIIndicatorMouse(m_pSession); break;
+ case IndicatorType_Keyboard: m_pool[indicatorType] = new UIIndicatorKeyboard(m_pSession); break;
+ case IndicatorType_KeyboardExtension: m_pool[indicatorType] = new UIIndicatorKeyboardExtension; break;
+ default: break;
+ }
+ /* Configure indicator: */
+ connect(m_pool.value(indicatorType), &QIStatusBarIndicator::sigContextMenuRequest,
+ this, &UIIndicatorsPool::sltContextMenuRequest);
+ /* Insert indicator into main-layout at proper position: */
+ m_pMainLayout->insertWidget(indicatorPosition(indicatorType), m_pool.value(indicatorType));
+ }
+ }
+}
+
+void UIIndicatorsPool::cleanupUpdateTimer()
+{
+ /* Destroy auto-update timer: */
+ AssertPtrReturnVoid(m_pTimerAutoUpdate);
+ {
+ m_pTimerAutoUpdate->stop();
+ delete m_pTimerAutoUpdate;
+ m_pTimerAutoUpdate = 0;
+ }
+}
+
+void UIIndicatorsPool::cleanupContents()
+{
+ /* Cleanup indicators: */
+ while (!m_pool.isEmpty())
+ {
+ const IndicatorType firstType = m_pool.keys().first();
+ delete m_pool.value(firstType);
+ m_pool.remove(firstType);
+ }
+}
+
+void UIIndicatorsPool::cleanup()
+{
+ /* Cleanup auto-update timer: */
+ cleanupUpdateTimer();
+ /* Cleanup indicators: */
+ cleanupContents();
+}
+
+void UIIndicatorsPool::contextMenuEvent(QContextMenuEvent *pEvent)
+{
+ /* Do not pass-through context menu events,
+ * otherwise they will raise the underlying status-bar context-menu. */
+ pEvent->accept();
+}
+
+int UIIndicatorsPool::indicatorPosition(IndicatorType indicatorType) const
+{
+ int iPosition = 0;
+ foreach (const IndicatorType &iteratedIndicatorType, m_order)
+ if (iteratedIndicatorType == indicatorType)
+ return iPosition;
+ else
+ ++iPosition;
+ return iPosition;
+}
+
+void UIIndicatorsPool::updateIndicatorStateForDevice(QIStatusBarIndicator *pIndicator, KDeviceActivity state)
+{
+ /* Assert indicators with NO state: */
+ QIStateStatusBarIndicator *pStateIndicator = qobject_cast<QIStateStatusBarIndicator*>(pIndicator);
+ AssertPtrReturnVoid(pStateIndicator);
+
+ /* Skip indicators with NULL state: */
+ if (pStateIndicator->state() == KDeviceActivity_Null)
+ return;
+
+ /* Paused VM have all indicator states set to IDLE: */
+ if (m_pSession->isPaused())
+ {
+ /* If current state differs from IDLE => set the IDLE one: */
+ if (pStateIndicator->state() != KDeviceActivity_Idle)
+ pStateIndicator->setState(KDeviceActivity_Idle);
+ }
+ else
+ {
+ /* If current state differs from actual => set the actual one: */
+ const int iState = (int)state;
+ if (pStateIndicator->state() != iState)
+ pStateIndicator->setState(iState);
+ }
+}
+
+#include "UIIndicatorsPool.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIIndicatorsPool.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIIndicatorsPool.h
new file mode 100644
index 00000000..2efe0e8b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIIndicatorsPool.h
@@ -0,0 +1,140 @@
+/* $Id: UIIndicatorsPool.h $ */
+/** @file
+ * VBox Qt GUI - UIIndicatorsPool class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIIndicatorsPool_h
+#define FEQT_INCLUDED_SRC_runtime_UIIndicatorsPool_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+#include <QList>
+#include <QMap>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class UISession;
+class CSession;
+class QIStatusBarIndicator;
+class QHBoxLayout;
+class QTimer;
+
+/** QWidget extension
+ * providing Runtime UI with status-bar indicators. */
+class UIIndicatorsPool : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about context menu request.
+ * @param indicatorType reflects which type of indicator it is,
+ * @param position reflects contex-menu position. */
+ void sigContextMenuRequest(IndicatorType indicatorType, const QPoint &position);
+
+public:
+
+ /** Constructor, passes @a pParent to the QWidget constructor.
+ * @param pSession is used to retrieve appearance information. */
+ UIIndicatorsPool(UISession *pSession, QWidget *pParent = 0);
+ /** Destructor. */
+ ~UIIndicatorsPool();
+
+ /** Updates appearance for passed @a indicatorType. */
+ void updateAppearance(IndicatorType indicatorType);
+
+ /** Defines whether indicator-states auto-update is @a fEnabled. */
+ void setAutoUpdateIndicatorStates(bool fEnabled);
+
+ /** Returns global screen position corresponding to @a indicatorPosition inside indicator of @a enmIndicatorType. */
+ QPoint mapIndicatorPositionToGlobal(IndicatorType enmIndicatorType, const QPoint &indicatorPosition);
+
+private slots:
+
+ /** Handles configuration change. */
+ void sltHandleConfigurationChange(const QUuid &uMachineID);
+
+ /** Handles indicator-states auto-update. */
+ void sltAutoUpdateIndicatorStates();
+
+ /** Handles context-menu request. */
+ void sltContextMenuRequest(QIStatusBarIndicator *pIndicator, QContextMenuEvent *pEvent);
+
+private:
+
+ /** Prepare routine. */
+ void prepare();
+ /** Prepare connections routine: */
+ void prepareConnections();
+ /** Prepare contents routine. */
+ void prepareContents();
+ /** Prepare update-timer routine: */
+ void prepareUpdateTimer();
+
+ /** Update pool routine. */
+ void updatePool();
+
+ /** Cleanup update-timer routine: */
+ void cleanupUpdateTimer();
+ /** Cleanup contents routine. */
+ void cleanupContents();
+ /** Cleanup routine. */
+ void cleanup();
+
+ /** Context-menu event handler. */
+ void contextMenuEvent(QContextMenuEvent *pEvent);
+
+ /** Returns position for passed @a indicatorType. */
+ int indicatorPosition(IndicatorType indicatorType) const;
+
+ /** Updates passed @a pIndicator with current @a state value. */
+ void updateIndicatorStateForDevice(QIStatusBarIndicator *pIndicator, KDeviceActivity state);
+
+ /** Holds the UI session reference. */
+ UISession *m_pSession;
+ /** Holds whether status-bar is enabled. */
+ bool m_fEnabled;
+ /** Holds the cached restrictions. */
+ QList<IndicatorType> m_restrictions;
+ /** Holds the cached order. */
+ QList<IndicatorType> m_order;
+ /** Holds cached indicator instances. */
+ QMap<IndicatorType, QIStatusBarIndicator*> m_pool;
+ /** Holds the main-layout instance. */
+ QHBoxLayout *m_pMainLayout;
+ /** Holds the auto-update timer instance. */
+ QTimer *m_pTimerAutoUpdate;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIIndicatorsPool_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.cpp
new file mode 100644
index 00000000..a3621c71
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.cpp
@@ -0,0 +1,2021 @@
+/* $Id: UIKeyboardHandler.cpp $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandler class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * Things worth testing when changing this code:
+ * - That automatic keyboard capture works.
+ * - That the keyboard is captured when the mouse is.
+ * - That the host key releases the keyboard when
+ * the keyboard is captured but the mouse not, and both when both are.
+ * - That the host key captures both keyboard and mouse.
+ * - That the keyboard is captured when the mouse capture notification is
+ * displayed.
+ * - That keyboard capture works on X11 hosts when windows are dragged with
+ * various window managers.
+ * - That multiple machine windows do not fight for the focus on X11 hosts
+ * (noticeable through strange modifier key and capitals behaviour).
+ */
+
+/* Qt includes: */
+#include <QKeyEvent>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UIActionPool.h"
+#include "UISession.h"
+#include "UIMachineLogic.h"
+#include "UIMachineWindow.h"
+#include "UIMachineView.h"
+#include "UIHostComboEditor.h"
+#include "UIKeyboardHandlerNormal.h"
+#include "UIKeyboardHandlerFullscreen.h"
+#include "UIKeyboardHandlerSeamless.h"
+#include "UIKeyboardHandlerScale.h"
+#include "UIMouseHandler.h"
+#include "UINotificationCenter.h"
+#ifdef VBOX_WS_MAC
+# include "UICocoaApplication.h"
+# include "VBoxUtils-darwin.h"
+# include "DarwinKeyboard.h"
+#endif
+#ifdef VBOX_WS_WIN
+# include "WinKeyboard.h"
+#endif
+#ifdef VBOX_WS_X11
+# include "XKeyboard.h"
+# include "VBoxUtils-x11.h"
+#endif
+
+/* COM includes: */
+#include "CKeyboard.h"
+
+/* Other VBox includes: */
+#ifdef VBOX_WS_MAC
+# include "iprt/cpp/utils.h"
+#endif
+
+/* External includes: */
+#ifdef VBOX_WS_MAC
+# include <Carbon/Carbon.h>
+#endif
+#ifdef VBOX_WS_X11
+# include <X11/XKBlib.h>
+# include <X11/keysym.h>
+# ifdef KeyPress
+const int XFocusIn = FocusIn;
+const int XFocusOut = FocusOut;
+const int XKeyPress = KeyPress;
+const int XKeyRelease = KeyRelease;
+# undef KeyRelease
+# undef KeyPress
+# undef FocusOut
+# undef FocusIn
+# endif /* KeyPress */
+# include <xcb/xcb.h>
+#endif /* VBOX_WS_X11 */
+
+/* Enums representing different keyboard-states: */
+enum { KeyExtended = 0x01, KeyPressed = 0x02, KeyPause = 0x04, KeyPrint = 0x08 };
+enum { IsKeyPressed = 0x01, IsExtKeyPressed = 0x02, IsKbdCaptured = 0x80 };
+
+
+#ifdef VBOX_WS_WIN
+UIKeyboardHandler *UIKeyboardHandler::m_spKeyboardHandler = 0;
+#endif /* VBOX_WS_WIN */
+
+/* Factory function to create keyboard-handler: */
+UIKeyboardHandler* UIKeyboardHandler::create(UIMachineLogic *pMachineLogic,
+ UIVisualStateType visualStateType)
+{
+ /* Prepare keyboard-handler: */
+ UIKeyboardHandler *pKeyboardHandler = 0;
+
+ /* Depending on visual-state type: */
+ switch (visualStateType)
+ {
+ case UIVisualStateType_Normal:
+ pKeyboardHandler = new UIKeyboardHandlerNormal(pMachineLogic);
+ break;
+ case UIVisualStateType_Fullscreen:
+ pKeyboardHandler = new UIKeyboardHandlerFullscreen(pMachineLogic);
+ break;
+ case UIVisualStateType_Seamless:
+ pKeyboardHandler = new UIKeyboardHandlerSeamless(pMachineLogic);
+ break;
+ case UIVisualStateType_Scale:
+ pKeyboardHandler = new UIKeyboardHandlerScale(pMachineLogic);
+ break;
+ default:
+ break;
+ }
+
+#ifdef VBOX_WS_WIN
+ /* It is necessary to have static pointer to created handler
+ * because windows keyboard-hook works only with static members: */
+ m_spKeyboardHandler = pKeyboardHandler;
+#endif /* VBOX_WS_WIN */
+
+ /* Return prepared keyboard-handler: */
+ return pKeyboardHandler;
+}
+
+/* Factory function to destroy keyboard-handler: */
+void UIKeyboardHandler::destroy(UIKeyboardHandler *pKeyboardHandler)
+{
+#ifdef VBOX_WS_WIN
+ /* It was necessary to have static pointer to created handler
+ * because windows keyboard-hook works only with static members: */
+ m_spKeyboardHandler = 0;
+#endif /* VBOX_WS_WIN */
+
+ /* Delete keyboard-handler: */
+ delete pKeyboardHandler;
+}
+
+/* Prepare listened objects: */
+void UIKeyboardHandler::prepareListener(ulong uScreenId, UIMachineWindow *pMachineWindow)
+{
+ /* If that window is NOT registered yet: */
+ if (!m_windows.contains(uScreenId))
+ {
+ /* Add window: */
+ m_windows.insert(uScreenId, pMachineWindow);
+ /* Install event-filter for window: */
+ m_windows[uScreenId]->installEventFilter(this);
+ }
+
+ /* If that view is NOT registered yet: */
+ if (!m_views.contains(uScreenId))
+ {
+ /* Add view: */
+ m_views.insert(uScreenId, pMachineWindow->machineView());
+ /* Install event-filter for view: */
+ m_views[uScreenId]->installEventFilter(this);
+ }
+}
+
+/* Cleanup listened objects: */
+void UIKeyboardHandler::cleanupListener(ulong uScreenId)
+{
+ /* Check if we should release keyboard first: */
+ if ((int)uScreenId == m_iKeyboardCaptureViewIndex)
+ releaseKeyboard();
+
+ /* If window still registered: */
+ if (m_windows.contains(uScreenId))
+ {
+ /* Remove window: */
+ m_windows.remove(uScreenId);
+ }
+
+ /* If view still registered: */
+ if (m_views.contains(uScreenId))
+ {
+ /* Remove view: */
+ m_views.remove(uScreenId);
+ }
+}
+
+void UIKeyboardHandler::captureKeyboard(ulong uScreenId)
+{
+ /* Do NOT capture the keyboard if it is already captured: */
+ if (m_fIsKeyboardCaptured)
+ {
+ /* Make sure the right screen had captured the keyboard: */
+ Assert((int)uScreenId == m_iKeyboardCaptureViewIndex);
+ return;
+ }
+
+ /* If the view exists: */
+ if (m_views.contains(uScreenId))
+ {
+ /* Remember which screen wishes to capture the keyboard: */
+ m_iKeyboardCaptureViewIndex = uScreenId;
+
+ /* On X11, we do not grab the keyboard as soon as it is captured, but delay it
+ * for 300 milliseconds after the formal capture. We do this for several reasons:
+ * - First, when several windows are created they all try to capture the keyboard when
+ * they get the focus. Due to the asynchronous nature of X11 the first window may only
+ * gets notified after the last is created, and there is a dance if they respond to
+ * the notifications by grabbing the keyboard and trigger new focus changes in the process.
+ * - Second, grabbing the keyboard immediately on focus change upsets some window managers,
+ * they give us the focus then try to grab the keyboard themselves, and sulk if they fail
+ * by refusing to e.g. drag a window using its title bar.
+ *
+ * IMPORTANT! We do the same under all other hosts as well mainly to have the
+ * common behavior everywhere while X11 is forced to behave that way. */
+ QTimer::singleShot(300, this, SLOT(sltFinaliseCaptureKeyboard()));
+ }
+}
+
+bool UIKeyboardHandler::finaliseCaptureKeyboard()
+{
+ /* Do NOT capture the keyboard if it is already captured: */
+ if (m_fIsKeyboardCaptured)
+ return true;
+
+ /* Make sure capture was really requested: */
+ if (m_iKeyboardCaptureViewIndex == -1)
+ return true;
+
+ /* If the view exists: */
+ if (m_views.contains(m_iKeyboardCaptureViewIndex))
+ {
+#if defined(VBOX_WS_MAC)
+
+ /* On Mac, keyboard grabbing is ineffective, a low-level keyboard-hook is used instead.
+ * It is being installed on focus-in event and uninstalled on focus-out.
+ * S.a. UIKeyboardHandler::eventFilter for more information. */
+
+ /* Besides that, we are not just using the Qt stuff to grab the keyboard,
+ * we also disable global hot keys and enable watching
+ * modifiers (for right/left separation). */
+ /// @todo Is that really needed?
+ ::DarwinDisableGlobalHotKeys(true);
+ m_views[m_iKeyboardCaptureViewIndex]->grabKeyboard();
+
+#elif defined(VBOX_WS_WIN)
+
+ /* On Win, keyboard grabbing is ineffective, a low-level keyboard-hook is used instead.
+ * It is being installed on focus-in event and uninstalled on focus-out.
+ * S.a. UIKeyboardHandler::eventFilter for more information. */
+
+#elif defined(VBOX_WS_X11)
+
+ /* On X11, we are using XCB stuff to grab the keyboard.
+ * This stuff is a part of the active keyboard grabbing functionality.
+ * Active keyboard grabbing causes a problems on many window managers - a window cannot
+ * be moved using the mouse. So we additionally grab the mouse buttons as well to detect
+ * that the user is trying to click outside of the internal window geometry and release
+ * the keyboard before the target window sees the click. (GNOME Shell's hot corner has
+ * the same problem. At present we just let that problem be.) */
+
+# if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
+ /* Make sure we really do still have the focus. Qt as of version 5.13 started
+ * reporting it with delay, so ask the X server directly. We could remove the
+ * version check some time in the future. If we do, remove the comment above
+ * about the focus notification dance, as it will no longer be relevant. */
+ xcb_get_input_focus_cookie_t xcbFocusCookie = xcb_get_input_focus(NativeWindowSubsystem::X11GetConnection());
+ xcb_get_input_focus_reply_t *pFocusReply = xcb_get_input_focus_reply(NativeWindowSubsystem::X11GetConnection(),
+ xcbFocusCookie, 0);
+ xcb_window_t xcbFocusWindow = pFocusReply->focus;
+ free(pFocusReply);
+ if (xcbFocusWindow != m_windows[m_iKeyboardCaptureViewIndex]->winId())
+ return true;
+# endif
+
+ /* Grab the mouse button.
+ * We do not check for failure as we do not currently implement a back-up plan. */
+ /* If any previous grab is still in process, release it. */
+ if (m_hButtonGrabWindow != 0)
+ xcb_ungrab_button_checked(NativeWindowSubsystem::X11GetConnection(), XCB_BUTTON_INDEX_ANY,
+ m_hButtonGrabWindow, XCB_MOD_MASK_ANY);
+ m_hButtonGrabWindow = NativeWindowSubsystem::X11GetAppRootWindow();
+ xcb_grab_button_checked(NativeWindowSubsystem::X11GetConnection(), 0, m_hButtonGrabWindow,
+ XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
+ XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_ANY, XCB_MOD_MASK_ANY);
+ /* And grab the keyboard, using XCB directly, as Qt does not report failure. */
+ xcb_grab_keyboard_cookie_t xcbGrabCookie = xcb_grab_keyboard(NativeWindowSubsystem::X11GetConnection(), false,
+ m_views[m_iKeyboardCaptureViewIndex]->winId(),
+ XCB_TIME_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ xcb_grab_keyboard_reply_t *pGrabReply = xcb_grab_keyboard_reply(NativeWindowSubsystem::X11GetConnection(),
+ xcbGrabCookie, NULL);
+ if (pGrabReply == NULL || pGrabReply->status != XCB_GRAB_STATUS_SUCCESS)
+ {
+ /* Release the mouse button grab.
+ * We do not check for failure as we do not currently implement a back-up plan. */
+ xcb_ungrab_button_checked(NativeWindowSubsystem::X11GetConnection(), XCB_BUTTON_INDEX_ANY,
+ m_hButtonGrabWindow, XCB_MOD_MASK_ANY);
+ m_hButtonGrabWindow = 0;
+ /* Try again later: */
+ free(pGrabReply);
+ return false;
+ }
+ free(pGrabReply);
+
+#else
+
+ /* On other platforms we are just praying Qt method to work: */
+ m_views[m_iKeyboardCaptureViewIndex]->grabKeyboard();
+
+#endif
+
+ /* Store new keyboard-captured state value: */
+ m_fIsKeyboardCaptured = true;
+
+ /* Notify all the listeners: */
+ emit sigStateChange(state());
+
+ return true;
+ }
+
+ return false;
+}
+
+void UIKeyboardHandler::releaseKeyboard()
+{
+ /* Do NOT release the keyboard if it is already released: */
+ if (!m_fIsKeyboardCaptured)
+ {
+ /* If a delayed capture is scheduled then cancel it: */
+ m_iKeyboardCaptureViewIndex = -1;
+ return;
+ }
+
+ /* If the view exists: */
+ if (m_views.contains(m_iKeyboardCaptureViewIndex))
+ {
+#if defined(VBOX_WS_MAC)
+
+ /* On Mac, keyboard grabbing is ineffective, a low-level keyboard-hook is used instead.
+ * It is being installed on focus-in event and uninstalled on focus-out.
+ * S.a. UIKeyboardHandler::eventFilter for more information. */
+
+ /* Besides that, we are not just using the Qt stuff to ungrab the keyboard,
+ * we also enable global hot keys and disable watching
+ * modifiers (for right/left separation). */
+ /// @todo Is that really needed?
+ ::DarwinDisableGlobalHotKeys(false);
+ m_views[m_iKeyboardCaptureViewIndex]->releaseKeyboard();
+
+#elif defined(VBOX_WS_WIN)
+
+ /* On Win, keyboard grabbing is ineffective, a low-level keyboard-hook is used instead.
+ * It is being installed on focus-in event and uninstalled on focus-out.
+ * S.a. UIKeyboardHandler::eventFilter for more information. */
+
+#elif defined(VBOX_WS_X11)
+
+ /* On X11, we are using XCB stuff to grab the keyboard.
+ * This stuff is a part of the active keyboard grabbing functionality.
+ * Active keyboard grabbing causes a problems on many window managers - a window cannot
+ * be moved using the mouse. So we finally releasing additionally grabbed mouse as well
+ * to allow further user interactions. */
+
+ /* Ungrab using XCB: */
+ xcb_ungrab_keyboard(NativeWindowSubsystem::X11GetConnection(), XCB_TIME_CURRENT_TIME);
+ /* Release the mouse button grab.
+ * We do not check for failure as we do not currently implement a back-up plan. */
+ xcb_ungrab_button_checked(NativeWindowSubsystem::X11GetConnection(), XCB_BUTTON_INDEX_ANY,
+ m_hButtonGrabWindow, XCB_MOD_MASK_ANY);
+ m_hButtonGrabWindow = 0;
+
+#else
+
+ /* On other platforms we are just praying Qt method to work: */
+ m_views[m_iKeyboardCaptureViewIndex]->releaseKeyboard();
+
+#endif
+
+ /* Forget which screen had captured the keyboard: */
+ m_iKeyboardCaptureViewIndex = -1;
+
+ /* Store new keyboard-captured state value: */
+ m_fIsKeyboardCaptured = false;
+
+ /* Notify all the listeners: */
+ emit sigStateChange(state());
+ }
+}
+
+void UIKeyboardHandler::releaseAllPressedKeys(bool aReleaseHostKey /* = true */)
+{
+ bool fSentRESEND = false;
+
+ /* Send a dummy modifier sequence to prevent the guest OS from recognizing
+ * a single key click (for ex., Alt) and performing an unwanted action
+ * (for ex., activating the menu) when we release all pressed keys below.
+ * This is just a work-around and is likely to fail in some cases. We are
+ * not aware of any ideal solution. Historically we sent an 0xFE scan code,
+ * but this is a real key release code on Brazilian keyboards. Now we send
+ * a sequence of all modifier keys contained in the host sequence, hoping
+ * that the user will choose something which the guest does not interpret. */
+ for (uint i = 0; i < RT_ELEMENTS(m_pressedKeys); i++)
+ {
+ if ((m_pressedKeys[i] & IsKeyPressed) || (m_pressedKeys[i] & IsExtKeyPressed))
+ {
+ if (!fSentRESEND)
+ {
+ QList <unsigned> shortCodes = UIHostCombo::modifiersToScanCodes(gEDataManager->hostKeyCombination());
+ QVector <LONG> codes;
+ foreach (unsigned idxCode, shortCodes)
+ {
+ if (idxCode & 0x100)
+ codes << 0xE0;
+ codes << (idxCode & 0x7F);
+ m_pressedKeys[idxCode & 0x7F] &= idxCode & 0x100 ? ~IsExtKeyPressed : ~IsKeyPressed;
+ }
+ foreach (unsigned idxCode, shortCodes)
+ {
+ if (idxCode & 0x100)
+ codes << 0xE0;
+ codes << ((idxCode & 0x7F) | 0x80);
+ }
+ keyboard().PutScancodes(codes);
+ fSentRESEND = true;
+ }
+ if (m_pressedKeys[i] & IsKeyPressed)
+ keyboard().PutScancode(i | 0x80);
+ else
+ {
+ QVector <LONG> codes(2);
+ codes[0] = 0xE0;
+ codes[1] = i | 0x80;
+ keyboard().PutScancodes(codes);
+ }
+ }
+ m_pressedKeys[i] = 0;
+ }
+
+ if (aReleaseHostKey)
+ {
+ m_bIsHostComboPressed = false;
+ m_pressedHostComboKeys.clear();
+ }
+
+#ifdef VBOX_WS_MAC
+ unsigned int hostComboModifierMask = 0;
+ QList<int> hostCombo = UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination());
+ for (int i = 0; i < hostCombo.size(); ++i)
+ hostComboModifierMask |= ::DarwinKeyCodeToDarwinModifierMask(hostCombo.at(i));
+ /* Clear most of the modifiers: */
+ m_uDarwinKeyModifiers &=
+ alphaLock | kEventKeyModifierNumLockMask |
+ (aReleaseHostKey ? 0 : hostComboModifierMask);
+#endif /* VBOX_WS_MAC */
+
+ /* Notify all the listeners: */
+ emit sigStateChange(state());
+}
+
+/* Current keyboard state: */
+int UIKeyboardHandler::state() const
+{
+ return (m_fIsKeyboardCaptured ? UIKeyboardStateType_KeyboardCaptured : 0) |
+ (m_bIsHostComboPressed ? UIKeyboardStateType_HostKeyPressed : 0) |
+ (m_fHostKeyComboPressInserted ? UIKeyboardStateType_HostKeyPressedInsertion : 0);
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+void UIKeyboardHandler::setDebuggerActive(bool aActive /* = true*/)
+{
+ if (aActive)
+ {
+ m_fDebuggerActive = true;
+ releaseKeyboard();
+ }
+ else
+ m_fDebuggerActive = false;
+}
+
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_WIN
+void UIKeyboardHandler::winSkipKeyboardEvents(bool fSkip)
+{
+ m_fSkipKeyboardEvents = fSkip;
+}
+#endif /* VBOX_WS_WIN */
+
+bool UIKeyboardHandler::nativeEventFilter(void *pMessage, ulong uScreenId)
+{
+ /* Make sure view with passed index exists: */
+ if (!m_views.contains(uScreenId))
+ return false;
+
+ /* Check if some system event should be filtered out.
+ * Returning @c true means filtering-out,
+ * Returning @c false means passing event to Qt. */
+ bool fResult = false; /* Pass to Qt by default. */
+
+# if defined(VBOX_WS_MAC)
+
+ /* Acquire carbon event reference from the cocoa one: */
+ EventRef event = static_cast<EventRef>(darwinCocoaToCarbonEvent(pMessage));
+
+ /* Depending on event kind: */
+ const UInt32 uEventKind = ::GetEventKind(event);
+ switch (uEventKind)
+ {
+ /* Watch for simple key-events: */
+ case kEventRawKeyDown:
+ case kEventRawKeyRepeat:
+ case kEventRawKeyUp:
+ {
+ /* Acquire keycode: */
+ UInt32 uKeyCode = ~0U;
+ ::GetEventParameter(event, kEventParamKeyCode, typeUInt32,
+ NULL, sizeof(uKeyCode), NULL, &uKeyCode);
+
+ /* The usb keyboard driver translates these codes to different virtual
+ * keycodes depending of the keyboard type. There are ANSI, ISO, JIS
+ * and unknown. For European keyboards (ISO) the key 0xa and 0x32 have
+ * to be switched. Here we are doing this at runtime, cause the user
+ * can have more than one keyboard (of different type), where he may
+ * switch at will all the time. Default is the ANSI standard as defined
+ * in g_aDarwinToSet1. Please note that the "~" on some English ISO
+ * keyboards will be wrongly swapped. This can maybe fixed by
+ * using a Apple keyboard layout in the guest. */
+ if ( (uKeyCode == 0xa || uKeyCode == 0x32)
+ && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO)
+ uKeyCode = 0x3c - uKeyCode;
+
+ /* Translate keycode to set 1 scan code: */
+ unsigned uScanCode = ::DarwinKeycodeToSet1Scancode(uKeyCode);
+
+ /* If scan code is valid: */
+ if (uScanCode)
+ {
+ /* Calculate flags: */
+ int iFlags = 0;
+ if (uEventKind != kEventRawKeyUp)
+ iFlags |= KeyPressed;
+ if (uScanCode & VBOXKEY_EXTENDED)
+ iFlags |= KeyExtended;
+ /** @todo KeyPause, KeyPrint. */
+ uScanCode &= VBOXKEY_SCANCODE_MASK;
+
+ /* Get the unicode string (if present): */
+ AssertCompileSize(wchar_t, 2);
+ AssertCompileSize(UniChar, 2);
+ ByteCount cbWritten = 0;
+ wchar_t ucs[8];
+ if (::GetEventParameter(event, kEventParamKeyUnicodes, typeUnicodeText,
+ NULL, sizeof(ucs), &cbWritten, &ucs[0]) != 0)
+ cbWritten = 0;
+ ucs[cbWritten / sizeof(wchar_t)] = 0; /* The api doesn't terminate it. */
+
+ /* Finally, handle parsed key-event: */
+ fResult = keyEvent(uKeyCode, uScanCode, iFlags, uScreenId, ucs[0] ? ucs : NULL);
+ }
+
+ break;
+ }
+ /* Watch for modifier key-events: */
+ case kEventRawKeyModifiersChanged:
+ {
+ /* Acquire new modifier mask, it may contain
+ * multiple modifier changes, kind of annoying: */
+ UInt32 uNewMask = 0;
+ ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
+ NULL, sizeof(uNewMask), NULL, &uNewMask);
+
+ /* Adjust new modifier mask to distinguish left/right modifiers: */
+ uNewMask = ::DarwinAdjustModifierMask(uNewMask, pMessage);
+
+ /* Determine what is really changed: */
+ const UInt32 changed = uNewMask ^ m_uDarwinKeyModifiers;
+ if (changed)
+ {
+ for (UInt32 bit = 0; bit < 32; ++bit)
+ {
+ /* Skip unchanged bits: */
+ if (!(changed & (1 << bit)))
+ continue;
+ /* Acquire set 1 scan code from new mask: */
+ unsigned uScanCode = ::DarwinModifierMaskToSet1Scancode(1 << bit);
+ /* Skip invalid scan codes: */
+ if (!uScanCode)
+ continue;
+ /* Acquire darwin keycode from new mask: */
+ unsigned uKeyCode = ::DarwinModifierMaskToDarwinKeycode(1 << bit);
+ /* Assert invalid keycodes: */
+ Assert(uKeyCode);
+
+ /* For non-lockable modifier: */
+ if (!(uScanCode & VBOXKEY_LOCK))
+ {
+ /* Calculate flags: */
+ unsigned uFlags = (uNewMask & (1 << bit)) ? KeyPressed : 0;
+ if (uScanCode & VBOXKEY_EXTENDED)
+ uFlags |= KeyExtended;
+ uScanCode &= VBOXKEY_SCANCODE_MASK;
+
+ /* Finally, handle parsed key-event: */
+ keyEvent(uKeyCode, uScanCode & 0xff, uFlags, uScreenId);
+ }
+ /* For lockable modifier: */
+ else
+ {
+ /* Calculate flags: */
+ unsigned uFlags = 0;
+ if (uScanCode & VBOXKEY_EXTENDED)
+ uFlags |= KeyExtended;
+ uScanCode &= VBOXKEY_SCANCODE_MASK;
+
+ /* Finally, handle parsed press/release pair: */
+ keyEvent(uKeyCode, uScanCode, uFlags | KeyPressed, uScreenId);
+ keyEvent(uKeyCode, uScanCode, uFlags, uScreenId);
+ }
+ }
+ }
+
+ /* Remember new modifier mask: */
+ m_uDarwinKeyModifiers = uNewMask;
+
+ /* Always return true here because we'll otherwise getting a Qt event
+ * we don't want and that will only cause the Pause warning to pop up: */
+ fResult = true;
+
+ break;
+ }
+ default:
+ break;
+ }
+
+# elif defined(VBOX_WS_WIN)
+
+ /* Ignore this event if m_fSkipKeyboardEvents is set by winSkipKeyboardEvents(). */
+ if (m_fSkipKeyboardEvents)
+ return false;
+
+ /* Cast to MSG event: */
+ MSG *pMsg = static_cast<MSG*>(pMessage);
+
+ /* Depending on message type: */
+ switch (pMsg->message)
+ {
+ /* Watch for key-events: */
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ {
+ // WORKAROUND:
+ // Can't do COM inter-process calls from a SendMessage handler,
+ // see http://support.microsoft.com/kb/131056.
+ if (uiCommon().isSeparateProcess() && InSendMessage())
+ {
+ PostMessage(pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam);
+ fResult = true;
+ break;
+ }
+
+ /* Check for our own special flag to ignore this event.
+ * That flag could only be set later in this function
+ * so having it here means this event came here
+ * for the second time already. */
+ if (pMsg->lParam & (0x1 << 25))
+ {
+ /* Remove that flag as well: */
+ pMsg->lParam &= ~(0x1 << 25);
+ fResult = false;
+ break;
+ }
+
+ /* Scan codes 0x80 and 0x00 should be filtered out: */
+ unsigned uScan = (pMsg->lParam >> 16) & 0x7F;
+ if (!uScan)
+ {
+ fResult = true;
+ break;
+ }
+
+ /* Get the virtual key: */
+ int iVKey = pMsg->wParam;
+
+ /* Calculate flags: */
+ int iFlags = 0;
+ if (pMsg->lParam & 0x1000000)
+ iFlags |= KeyExtended;
+ if (!(pMsg->lParam & 0x80000000))
+ iFlags |= KeyPressed;
+
+ /* Make sure AltGr monitor exists: */
+ AssertPtrReturn(m_pAltGrMonitor, false);
+ {
+ /* Filter event out if we are sure that this is a fake left control event: */
+ if (m_pAltGrMonitor->isCurrentEventDefinitelyFake(uScan, iFlags & KeyPressed, iFlags & KeyExtended))
+ {
+ fResult = true;
+ break;
+ }
+ /* Update AltGR monitor state from key-event: */
+ m_pAltGrMonitor->updateStateFromKeyEvent(uScan, iFlags & KeyPressed, iFlags & KeyExtended);
+ /* And release left Ctrl key early (if required): */
+ if (m_pAltGrMonitor->isLeftControlReleaseNeeded())
+ keyboard().PutScancode(0x1D | 0x80);
+ }
+
+ /* Check for special Korean keys. Based on the keyboard layout selected
+ * on the host, the scan code in lParam might be 0x71/0x72 or 0xF1/0xF2.
+ * In either case, we must deliver 0xF1/0xF2 scan code to the guest when
+ * the key is pressed and nothing when it's released. */
+ if (uScan == 0x71 || uScan == 0x72)
+ {
+ uScan |= 0x80;
+ iFlags = KeyPressed; /* Because a release would be ignored. */
+ iVKey = VK_PROCESSKEY; /* In case it was 0xFF. */
+ }
+
+ /* When one of the SHIFT keys is held and one of the cursor movement
+ * keys is pressed, Windows duplicates SHIFT press/release messages,
+ * but with the virtual keycode set to 0xFF. These virtual keys are also
+ * sent in some other situations (Pause, PrtScn, etc.). Filter out such messages. */
+ if (iVKey == 0xFF)
+ {
+ fResult = true;
+ break;
+ }
+
+ /* Handle special virtual keys: */
+ switch (iVKey)
+ {
+ case VK_SHIFT:
+ case VK_CONTROL:
+ case VK_MENU:
+ {
+ /* Overcome Win32 modifier key generalization: */
+ int iKeyscan = uScan;
+ if (iFlags & KeyExtended)
+ iKeyscan |= 0xE000;
+ switch (iKeyscan)
+ {
+ case 0x002A: iVKey = VK_LSHIFT; break;
+ case 0x0036: iVKey = VK_RSHIFT; break;
+ case 0x001D: iVKey = VK_LCONTROL; break;
+ case 0xE01D: iVKey = VK_RCONTROL; break;
+ case 0x0038: iVKey = VK_LMENU; break;
+ case 0xE038: iVKey = VK_RMENU; break;
+ }
+ break;
+ }
+ case VK_NUMLOCK:
+ /* Win32 sets the extended bit for the NumLock key. Reset it: */
+ iFlags &= ~KeyExtended;
+ break;
+ case VK_SNAPSHOT:
+ iFlags |= KeyPrint;
+ break;
+ case VK_PAUSE:
+ iFlags |= KeyPause;
+ break;
+ }
+
+ /* Finally, handle parsed key-event: */
+ fResult = keyEvent(iVKey, uScan, iFlags, uScreenId);
+
+ /* Always let Windows see key releases to prevent stuck keys.
+ * Hopefully this won't cause any other issues. */
+ if (pMsg->message == WM_KEYUP || pMsg->message == WM_SYSKEYUP)
+ {
+ fResult = false;
+ break;
+ }
+
+ /* Above keyEvent() returned that it didn't processed the event, but since the
+ * keyboard is captured, we don't want to pass it to Windows. We just want
+ * to let Qt process the message (to handle non-alphanumeric <HOST>+key
+ * shortcuts for example). So send it directly to the window with the
+ * special flag in the reserved area of lParam (to avoid recursion). */
+ if (!fResult && m_fIsKeyboardCaptured)
+ {
+ ::SendMessage(pMsg->hwnd, pMsg->message,
+ pMsg->wParam, pMsg->lParam | (0x1 << 25));
+ fResult = true;
+ break;
+ }
+
+ /* These special keys have to be handled by Windows as well to update the
+ * internal modifier state and to enable/disable the keyboard LED: */
+ if (iVKey == VK_NUMLOCK || iVKey == VK_CAPITAL || iVKey == VK_LSHIFT || iVKey == VK_RSHIFT)
+ {
+ fResult = false;
+ break;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+# elif defined(VBOX_WS_X11)
+
+ /* Cast to XCB event: */
+ xcb_generic_event_t *pEvent = static_cast<xcb_generic_event_t*>(pMessage);
+
+ /* Depending on event type: */
+ switch (pEvent->response_type & ~0x80)
+ {
+ /* Watch for key-events: */
+ case XCB_KEY_PRESS:
+ case XCB_KEY_RELEASE:
+ {
+ /* Cast to XCB key-event: */
+ xcb_key_press_event_t *pKeyEvent = static_cast<xcb_key_press_event_t*>(pMessage);
+
+ /* Translate the keycode to a PC scan code: */
+ unsigned uScan = handleXKeyEvent(NativeWindowSubsystem::X11GetDisplay(), pKeyEvent->detail);
+
+ /* Scan codes 0x00 (no valid translation) and 0x80 (extended flag) are ignored: */
+ if (!(uScan & 0x7F))
+ {
+ fResult = true;
+ break;
+ }
+
+ /* Calculate flags: */
+ int iflags = 0;
+ if (uScan >> 8)
+ iflags |= KeyExtended;
+ if ((pEvent->response_type & ~0x80) == XCB_KEY_PRESS)
+ iflags |= KeyPressed;
+
+ /* Remove the extended flag: */
+ uScan &= 0x7F;
+
+ /* Special Korean keys must send scan code 0xF1/0xF2
+ * when pressed and nothing when released. */
+ if (uScan == 0x71 || uScan == 0x72)
+ {
+ if ((pEvent->response_type & ~0x80) == XCB_KEY_RELEASE)
+ {
+ fResult = true;
+ break;
+ }
+ /* Re-create the bizarre scan code: */
+ uScan |= 0x80;
+ }
+
+ /* Translate the keycode to a keysym: */
+ KeySym ks = ::wrapXkbKeycodeToKeysym(NativeWindowSubsystem::X11GetDisplay(), pKeyEvent->detail, 0, 0);
+
+ /* Update special flags: */
+ switch (ks)
+ {
+ case XK_Print:
+ iflags |= KeyPrint;
+ break;
+ case XK_Pause:
+ if (pKeyEvent->state & ControlMask) /* Break */
+ {
+ ks = XK_Break;
+ iflags |= KeyExtended;
+ uScan = 0x46;
+ }
+ else
+ iflags |= KeyPause;
+ break;
+ }
+
+ /* Finally, handle parsed key-event: */
+ fResult = keyEvent(ks, uScan, iflags, uScreenId);
+
+ break;
+ }
+ default:
+ break;
+ }
+
+# else
+
+# warning "port me!"
+
+# endif
+
+ /* Return result: */
+ return fResult;
+}
+
+/* Machine state-change handler: */
+void UIKeyboardHandler::sltMachineStateChanged()
+{
+ /* Get machine state: */
+ KMachineState state = uisession()->machineState();
+ /* Handle particular machine states: */
+ switch (state)
+ {
+ case KMachineState_Paused:
+ case KMachineState_TeleportingPausedVM:
+ case KMachineState_Stuck:
+ {
+ /* Release the keyboard: */
+ releaseKeyboard();
+ /* And all pressed keys except the host-one : */
+ releaseAllPressedKeys(false /* release host-key? */);
+ break;
+ }
+ case KMachineState_Running:
+ {
+ /* Capture the keyboard by the first focused view: */
+ QList<ulong> theListOfViewIds = m_views.keys();
+ for (int i = 0; i < theListOfViewIds.size(); ++i)
+ {
+ if (viewHasFocus(theListOfViewIds[i]))
+ {
+ /* Capture keyboard: */
+#ifdef VBOX_WS_WIN
+ if (!isAutoCaptureDisabled() && autoCaptureSetGlobally() &&
+ GetAncestor((HWND)m_views[theListOfViewIds[i]]->winId(), GA_ROOT) == GetForegroundWindow())
+#else /* !VBOX_WS_WIN */
+ if (!isAutoCaptureDisabled() && autoCaptureSetGlobally())
+#endif /* !VBOX_WS_WIN */
+ captureKeyboard(theListOfViewIds[i]);
+ /* Reset the single-time disable capture flag: */
+ if (isAutoCaptureDisabled())
+ setAutoCaptureDisabled(false);
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Recall reminder about paused VM input
+ * if we are not in paused VM state already: */
+ if (machineLogic()->activeMachineWindow() &&
+ state != KMachineState_Paused &&
+ state != KMachineState_TeleportingPausedVM)
+ UINotificationMessage::forgetAboutPausedVMInput();
+}
+
+void UIKeyboardHandler::sltFinaliseCaptureKeyboard()
+{
+ /* Try to finalise keyboard capture: */
+ if (!finaliseCaptureKeyboard())
+ {
+ /* Try again in another 300 milliseconds in case of failure: */
+ QTimer::singleShot(300, this, SLOT(sltFinaliseCaptureKeyboard()));
+ }
+}
+
+/* Keyboard-handler constructor: */
+UIKeyboardHandler::UIKeyboardHandler(UIMachineLogic *pMachineLogic)
+ : QObject(pMachineLogic)
+ , m_pMachineLogic(pMachineLogic)
+ , m_iKeyboardCaptureViewIndex(-1)
+ , m_fIsKeyboardCaptured(false)
+ , m_bIsHostComboPressed(false)
+ , m_bIsHostComboAlone(false)
+ , m_bIsHostComboProcessed(false)
+ , m_fPassCADtoGuest(false)
+ , m_fHostKeyComboPressInserted(false)
+ , m_fDebuggerActive(false)
+ , m_iKeyboardHookViewIndex(-1)
+#if defined(VBOX_WS_MAC)
+ , m_uDarwinKeyModifiers(0)
+#elif defined(VBOX_WS_WIN)
+ , m_fIsHostkeyInCapture(false)
+ , m_fSkipKeyboardEvents(false)
+ , m_keyboardHook(NULL)
+ , m_pAltGrMonitor(0)
+#elif defined(VBOX_WS_X11)
+ , m_hButtonGrabWindow(0)
+#endif /* VBOX_WS_X11 */
+{
+ /* Prepare: */
+ prepareCommon();
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Initialize: */
+ sltMachineStateChanged();
+}
+
+/* Keyboard-handler destructor: */
+UIKeyboardHandler::~UIKeyboardHandler()
+{
+ /* Cleanup: */
+ cleanupCommon();
+}
+
+void UIKeyboardHandler::prepareCommon()
+{
+#ifdef VBOX_WS_WIN
+ /* Prepare AltGR monitor: */
+ m_pAltGrMonitor = new WinAltGrMonitor;
+#endif /* VBOX_WS_WIN */
+
+ /* Machine state-change updater: */
+ connect(uisession(), &UISession::sigMachineStateChange, this, &UIKeyboardHandler::sltMachineStateChanged);
+
+ /* Pressed keys: */
+ ::memset(m_pressedKeys, 0, sizeof(m_pressedKeys));
+ ::memset(m_pressedKeysCopy, 0, sizeof(m_pressedKeysCopy));
+}
+
+void UIKeyboardHandler::loadSettings()
+{
+ /* Global settings: */
+#ifdef VBOX_WS_X11
+ /* Initialize the X keyboard subsystem: */
+ initMappedX11Keyboard(NativeWindowSubsystem::X11GetDisplay(), gEDataManager->remappedScanCodes());
+ /* Fix for http://www.virtualbox.org/ticket/1296:
+ * when X11 sends events for repeated keys, it always inserts an XKeyRelease
+ * before the XKeyPress. */
+ /* Disable key release events during key auto-repeat: */
+ XkbSetDetectableAutoRepeat(NativeWindowSubsystem::X11GetDisplay(), True, NULL);
+#endif /* VBOX_WS_X11 */
+
+ /* Extra data settings: */
+ {
+ /* CAD setting: */
+ m_fPassCADtoGuest = gEDataManager->passCADtoGuest(uiCommon().managedVMUuid());
+ }
+}
+
+void UIKeyboardHandler::cleanupCommon()
+{
+#if defined(VBOX_WS_MAC)
+
+ /* Cleanup keyboard-hook: */
+ if (m_iKeyboardHookViewIndex != -1)
+ {
+ /* Ungrab the keyboard and unregister the event callback/hook: */
+ ::DarwinReleaseKeyboard();
+ UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */,
+ UIKeyboardHandler::macKeyboardProc, this);
+ }
+
+#elif defined(VBOX_WS_WIN)
+
+ /* Cleanup AltGR monitor: */
+ delete m_pAltGrMonitor;
+ m_pAltGrMonitor = 0;
+
+ /* If keyboard-hook is installed: */
+ if (m_keyboardHook)
+ {
+ /* Uninstall existing keyboard-hook: */
+ UnhookWindowsHookEx(m_keyboardHook);
+ m_keyboardHook = 0;
+ }
+
+#endif /* VBOX_WS_WIN */
+
+ /* Update keyboard hook view index: */
+ m_iKeyboardHookViewIndex = -1;
+}
+
+/* Machine-logic getter: */
+UIMachineLogic* UIKeyboardHandler::machineLogic() const
+{
+ return m_pMachineLogic;
+}
+
+/* Action-pool getter: */
+UIActionPool* UIKeyboardHandler::actionPool() const
+{
+ return machineLogic()->actionPool();
+}
+
+/* UI Session getter: */
+UISession* UIKeyboardHandler::uisession() const
+{
+ return machineLogic()->uisession();
+}
+
+CKeyboard& UIKeyboardHandler::keyboard() const
+{
+ return uisession()->keyboard();
+}
+
+/* Event handler for prepared listener(s): */
+bool UIKeyboardHandler::eventFilter(QObject *pWatchedObject, QEvent *pEvent)
+{
+ /* Check if pWatchedObject object is view: */
+ if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject))
+ {
+ /* Get corresponding screen index: */
+ ulong uScreenId = m_views.key(pWatchedView);
+ NOREF(uScreenId);
+ /* Handle view events: */
+ switch (pEvent->type())
+ {
+ case QEvent::FocusIn:
+ {
+#if defined(VBOX_WS_MAC)
+
+ /* If keyboard-hook is NOT installed;
+ * Or installed but NOT for that view: */
+ if ((int)uScreenId != m_iKeyboardHookViewIndex)
+ {
+ /* If keyboard-hook is NOT installed: */
+ if (m_iKeyboardHookViewIndex == -1)
+ {
+ /* Disable mouse and keyboard event compression/delaying
+ * to make sure we *really* get all of the events: */
+ ::CGSetLocalEventsSuppressionInterval(0.0); /** @todo replace with CGEventSourceSetLocalEventsSuppressionInterval ? */
+ ::darwinSetMouseCoalescingEnabled(false);
+
+ /* Bring the caps lock state up to date,
+ * otherwise e.g. a later Shift key press will accidentally inject a CapsLock key press and release,
+ * see UIKeyboardHandler::macKeyboardEvent for the code handling modifier key state changes. */
+ m_uDarwinKeyModifiers ^= (m_uDarwinKeyModifiers ^ ::GetCurrentEventKeyModifiers()) & alphaLock;
+
+ /* Register the event callback/hook and grab the keyboard: */
+ UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */,
+ UIKeyboardHandler::macKeyboardProc, this);
+ ::DarwinGrabKeyboard(false);
+ }
+ }
+
+#elif defined(VBOX_WS_WIN)
+
+ /* If keyboard-hook is NOT installed;
+ * Or installed but NOT for that view: */
+ if (!m_keyboardHook || (int)uScreenId != m_iKeyboardHookViewIndex)
+ {
+ /* If keyboard-hook is installed: */
+ if (m_keyboardHook)
+ {
+ /* Uninstall existing keyboard-hook: */
+ UnhookWindowsHookEx(m_keyboardHook);
+ m_keyboardHook = 0;
+ }
+ /* Install new keyboard-hook: */
+ m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, UIKeyboardHandler::winKeyboardProc, GetModuleHandle(NULL), 0);
+ AssertMsg(m_keyboardHook, ("SetWindowsHookEx(): err=%d", GetLastError()));
+ }
+
+#endif /* VBOX_WS_WIN */
+
+ /* Update keyboard hook view index: */
+ m_iKeyboardHookViewIndex = uScreenId;
+
+ if (isSessionRunning())
+ {
+ /* Capture keyboard: */
+#ifdef VBOX_WS_WIN
+ if (!isAutoCaptureDisabled() && autoCaptureSetGlobally() &&
+ GetAncestor((HWND)pWatchedView->winId(), GA_ROOT) == GetForegroundWindow())
+#else /* !VBOX_WS_WIN */
+ if (!isAutoCaptureDisabled() && autoCaptureSetGlobally())
+#endif /* !VBOX_WS_WIN */
+ captureKeyboard(uScreenId);
+ /* Reset the single-time disable capture flag: */
+ if (isAutoCaptureDisabled())
+ setAutoCaptureDisabled(false);
+ }
+
+ break;
+ }
+ case QEvent::FocusOut:
+ {
+ /* If host key combo press has been inserted (with no release yet) insert a release now: */
+ if (m_fHostKeyComboPressInserted)
+ machineLogic()->typeHostKeyComboPressRelease(false);
+
+#if defined(VBOX_WS_MAC)
+
+ /* If keyboard-hook is installed: */
+ if ((int)uScreenId == m_iKeyboardHookViewIndex)
+ {
+ /* Ungrab the keyboard and unregister the event callback/hook: */
+ ::DarwinReleaseKeyboard();
+ UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */,
+ UIKeyboardHandler::macKeyboardProc, this);
+ }
+
+#elif defined(VBOX_WS_WIN)
+
+ /* If keyboard-hook is installed: */
+ if (m_keyboardHook)
+ {
+ /* Uninstall existing keyboard-hook: */
+ UnhookWindowsHookEx(m_keyboardHook);
+ m_keyboardHook = 0;
+ }
+
+#endif /* VBOX_WS_WIN */
+
+ /* Update keyboard hook view index: */
+ m_iKeyboardHookViewIndex = -1;
+
+ /* Release keyboard: */
+ if (isSessionRunning() || isSessionStuck())
+ releaseKeyboard();
+ /* And all pressed keys: */
+ releaseAllPressedKeys(true);
+
+ break;
+ }
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ {
+ QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
+
+ if (m_bIsHostComboPressed && pEvent->type() == QEvent::KeyPress)
+ {
+ /* Passing F1-F12 keys to the guest: */
+ if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F12)
+ {
+ QVector <LONG> combo(6);
+ combo[0] = 0x1d; /* Ctrl down */
+ combo[1] = 0x38; /* Alt down */
+ combo[4] = 0xb8; /* Alt up */
+ combo[5] = 0x9d; /* Ctrl up */
+ if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F10)
+ {
+ combo[2] = 0x3b + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 down */
+ combo[3] = 0xbb + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 up */
+ }
+ /* There is some scan slice between F10 and F11 keys, so its separated: */
+ else if (pKeyEvent->key() >= Qt::Key_F11 && pKeyEvent->key() <= Qt::Key_F12)
+ {
+ combo[2] = 0x57 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 down */
+ combo[3] = 0xd7 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 up */
+ }
+ keyboard().PutScancodes(combo);
+ }
+ /* Process hot keys not processed in keyEvent() (as in case of non-alphanumeric keys): */
+ actionPool()->processHotKey(QKeySequence(pKeyEvent->key()));
+ }
+ else if (!m_bIsHostComboPressed && pEvent->type() == QEvent::KeyRelease)
+ {
+ /* Show a possible warning on key release which seems to be more expected by the end user: */
+ if (uisession()->isPaused())
+ UINotificationMessage::remindAboutPausedVMInput();
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Else just propagate to base-class: */
+ return QObject::eventFilter(pWatchedObject, pEvent);
+}
+
+#if defined(VBOX_WS_MAC)
+
+/* static */
+bool UIKeyboardHandler::macKeyboardProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
+{
+ /* Determine the event class: */
+ EventRef event = (EventRef)pvCarbonEvent;
+ UInt32 uEventClass = ::GetEventClass(event);
+
+ /* Check if this is an application key combo. In that case we will not pass
+ * the event to the guest, but let the host process it. */
+ if (::darwinIsApplicationCommand(pvCocoaEvent))
+ return false;
+
+ /* Get the keyboard handler from the user's void data: */
+ UIKeyboardHandler *pKeyboardHandler = static_cast<UIKeyboardHandler*>(pvUser);
+
+ /* All keyboard class events needs to be handled: */
+ if (uEventClass == kEventClassKeyboard && pKeyboardHandler && pKeyboardHandler->macKeyboardEvent(pvCocoaEvent, event))
+ return true;
+
+ /* Pass the event along: */
+ return false;
+}
+
+bool UIKeyboardHandler::macKeyboardEvent(const void *pvCocoaEvent, EventRef event)
+{
+ /* Check what related machine-view was NOT unregistered yet: */
+ if (!m_views.contains(m_iKeyboardHookViewIndex))
+ return false;
+
+ /* Pass event to machine-view's event handler: */
+ Q_UNUSED(event);
+ return nativeEventFilter(unconst(pvCocoaEvent), m_iKeyboardHookViewIndex);
+}
+
+#elif defined(VBOX_WS_WIN)
+
+/* static */
+LRESULT CALLBACK UIKeyboardHandler::winKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
+{
+ /* All keyboard class events needs to be handled: */
+ if (nCode == HC_ACTION && m_spKeyboardHandler && m_spKeyboardHandler->winKeyboardEvent(wParam, *(KBDLLHOOKSTRUCT*)lParam))
+ return 1;
+
+ /* Pass the event along: */
+ return CallNextHookEx(NULL, nCode, wParam, lParam);
+}
+
+bool UIKeyboardHandler::winKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event)
+{
+ /* Check that related machine-view was NOT unregistered yet: */
+ if (!m_views.contains(m_iKeyboardHookViewIndex))
+ return false;
+
+ /* HACK ALERT! Check that we're not in cleanup, as we're using gEDataManger
+ to get host key combinations and it as probably been cleaned up already.
+ We don't want to cause it to re-instantiate, with all the COM traffic
+ that might involve. Sample assertion stack (IPRT not windbg, sorry):
+
+ !!Assertion Failed!!
+ Expression: mRC != RPC_E_CANTCALLOUT_ININPUTSYNCCALL
+ Location : E:\vbox\svn\trunk\out\win.amd64\debug\obj\UICommon\include\COMWrappers.cpp(3857) class QVector<class QString> __cdecl CVirtualBox::GetExtraDataKeys(void)
+ Stack :
+ 00007fff39aa6634 VBoxRT.dll!RTAssertMsg1+0x274 (rva:0x246634)
+ [E:\vbox\svn\trunk\src\VBox\Runtime\common\misc\assert.cpp:159]
+ 00007fff39aa542f VBoxRT.dll!RTAssertMsg1Weak+0x2f (rva:0x24542f)
+ [E:\vbox\svn\trunk\src\VBox\Runtime\common\misc\RTAssertMsg1Weak.cpp:40 (+0x0)]
+ 00007fff36e5c00f UICommon.dll!CVirtualBox::GetExtraDataKeys+0x23f (rva:0x3fc00f)
+ [E:\vbox\svn\trunk\out\win.amd64\debug\obj\UICommon\include\COMWrappers.cpp:3857]
+ 00007fff36ac2cc9 UICommon.dll!UIExtraDataManager::prepareGlobalExtraDataMap+0xb9 (rva:0x62cc9)
+ [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp:4845]
+ 00007fff36ac2bf8 UICommon.dll!UIExtraDataManager::prepare+0x28 (rva:0x62bf8)
+ [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp:4833 (+0x0)]
+ 00007fff36ab1896 UICommon.dll!UIExtraDataManager::instance+0x66 (rva:0x51896)
+ [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp:2011 (+0x0)]
+ 00007ff771b05b06 VirtualBoxVM.exe!UIKeyboardHandler::winKeyboardEvent+0xe6 (rva:0x35b06)
+ [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\runtime\UIKeyboardHandler.cpp:1324]
+ 00007ff771b059ec VirtualBoxVM.exe!UIKeyboardHandler::winKeyboardProc+0x4c (rva:0x359ec)
+ [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\runtime\UIKeyboardHandler.cpp:1304]
+ 00007fff763311f2 USER32.dll!GetDlgCtrlID+0x232 (rva:0x211f2)
+ 00007fff7639ac89 USER32.dll!CreateSystemThreads+0xa29 (rva:0x8ac89)
+ 00007fff76b30ba4 ntdll.dll!KiUserCallbackDispatcher+0x24 (rva:0xa0ba4)
+ 00007fff749b1064 win32u.dll!NtUserPeekMessage+0x14 (rva:0x1064)
+ 00007fff7631a553 USER32.dll!PeekMessageW+0x1e3 (rva:0xa553)
+ 00007fff7631a4b3 USER32.dll!PeekMessageW+0x143 (rva:0xa4b3)
+ 00007e11000270dc ConEmuHk64.dll!SetLoadLibraryCallback+0xc3ac (rva:0x270dc)
+ 00007fff759be71b combase.dll!CoRegisterPSClsid+0x82b (rva:0x6e71b)
+ 00007fff759be685 combase.dll!CoRegisterPSClsid+0x795 (rva:0x6e685)
+ 00007fff759bcec1 combase.dll!Ordinal87+0x2851 (rva:0x6cec1)
+ 00007fff759bcbbb combase.dll!Ordinal87+0x254b (rva:0x6cbbb)
+ 00007fff75994956 combase.dll!RoGetApartmentIdentifier+0x55f6 (rva:0x44956)
+ cccccccccccccccc */
+ if (UICommon::instance()->isCleaningUp())
+ return false;
+
+ /* It's possible that a key has been pressed while the keyboard was not
+ * captured, but is being released under the capture. Detect this situation
+ * and do not pass on the key press to the virtual machine. */
+/** @todo r=bird: Why do this complicated test before the simple m_fIsKeyboardCaptured one? */
+ uint8_t what_pressed = (event.flags & 0x01)
+ && (event.vkCode != VK_RSHIFT)
+ ? IsExtKeyPressed : IsKeyPressed;
+ if ( (event.flags & 0x80) /* released */
+ && ( ( UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination()).contains(event.vkCode)
+ && !m_fIsHostkeyInCapture)
+ || ( m_pressedKeys[event.scanCode & 0x7F]
+ & (IsKbdCaptured | what_pressed))
+ == what_pressed))
+ return false;
+
+ if (!m_fIsKeyboardCaptured)
+ return false;
+
+ /* For normal user applications, Windows defines AltGr to be the same as
+ * LControl + RAlt. Without a low-level hook it is hard to recognise the
+ * additional LControl event inserted, but in a hook we recognise it by
+ * its special 0x21D scan code. */
+ if ( m_views[m_iKeyboardHookViewIndex]->hasFocus()
+ && ((event.scanCode & ~0x80) == 0x21D))
+ return true;
+
+ /* Compose the MSG: */
+ MSG message;
+ message.hwnd = (HWND)m_views[m_iKeyboardHookViewIndex]->winId();
+ message.message = msg;
+ message.wParam = event.vkCode;
+ message.lParam = 1 | (event.scanCode & 0xFF) << 16 | (event.flags & 0xFF) << 24;
+
+ /* Windows sets here the extended bit when the Right Shift key is pressed,
+ * which is totally wrong. Undo it. */
+ if (event.vkCode == VK_RSHIFT)
+ message.lParam &= ~0x1000000;
+
+ /* Pass event to view's event handler: */
+ return nativeEventFilter(&message, m_iKeyboardHookViewIndex);
+}
+
+#endif /* VBOX_WS_WIN */
+
+/**
+ * If the user has just completed a control-alt-del combination then handle
+ * that.
+ * @returns true if handling should stop here, false otherwise
+ */
+bool UIKeyboardHandler::keyEventCADHandled(uint8_t uScan)
+{
+ /* Check if it's C-A-D and GUI/PassCAD is not set/allowed: */
+ if (!m_fPassCADtoGuest &&
+ uScan == 0x53 /* Del */ &&
+ ((m_pressedKeys[0x38] & IsKeyPressed) /* Alt */ ||
+ (m_pressedKeys[0x38] & IsExtKeyPressed)) &&
+ ((m_pressedKeys[0x1d] & IsKeyPressed) /* Ctrl */ ||
+ (m_pressedKeys[0x1d] & IsExtKeyPressed)))
+ {
+ /* Use the C-A-D combination as a last resort to get the keyboard and mouse back
+ * to the host when the user forgets the Host Key. Note that it's always possible
+ * to send C-A-D to the guest using the Host+Del combination: */
+ if (isSessionRunning() && m_fIsKeyboardCaptured)
+ {
+ releaseKeyboard();
+ if (!uisession()->isMouseSupportsAbsolute() || !uisession()->isMouseIntegrated())
+ machineLogic()->mouseHandler()->releaseMouse();
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Handle a non-special (C-A-D, pause, print) key press or release
+ * @returns true if handling should stop here, false otherwise
+ */
+bool UIKeyboardHandler::keyEventHandleNormal(int iKey, uint8_t uScan, int fFlags, LONG *pCodes, uint *puCodesCount)
+{
+ /* Get the type of key - simple or extended: */
+ uint8_t uWhatPressed = fFlags & KeyExtended ? IsExtKeyPressed : IsKeyPressed;
+
+ /* If some key was pressed or some previously pressed key was released =>
+ * we are updating the list of pressed keys and preparing scan codes: */
+ if ((fFlags & KeyPressed) || (m_pressedKeys[uScan] & uWhatPressed))
+ {
+ /* If HID LEDs sync is disabled or not supported, check if the guest has the
+ * same view on the modifier keys (NumLock, CapsLock, ScrollLock) as the host. */
+ if (!machineLogic()->isHidLedsSyncEnabled())
+ if (fFlags & KeyPressed)
+ fixModifierState(pCodes, puCodesCount);
+
+ /* Prepend 'extended' scan code if needed: */
+ if (fFlags & KeyExtended)
+ pCodes[(*puCodesCount)++] = 0xE0;
+
+ /* Process key-press: */
+ if (fFlags & KeyPressed)
+ {
+ /* Append scan code: */
+ pCodes[(*puCodesCount)++] = uScan;
+ m_pressedKeys[uScan] |= uWhatPressed;
+ }
+ /* Process key-release if that key was pressed before: */
+ else if (m_pressedKeys[uScan] & uWhatPressed)
+ {
+ /* Append scan code: */
+ pCodes[(*puCodesCount)++] = uScan | 0x80;
+ m_pressedKeys[uScan] &= ~uWhatPressed;
+ }
+
+ /* Update keyboard-captured flag: */
+ if (m_fIsKeyboardCaptured)
+ m_pressedKeys[uScan] |= IsKbdCaptured;
+ else
+ m_pressedKeys[uScan] &= ~IsKbdCaptured;
+ }
+ /* Ignore key-release if that key was NOT pressed before,
+ * but only if thats not one of the host-combination keys: */
+ else
+ {
+ /* Get host-combo key list: */
+ QList<int> lstAllHostComboKey = UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination());
+ if (!lstAllHostComboKey.contains(iKey))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Check whether the key pressed results in a host key combination being
+ * handled.
+ * @returns true if a combination was handled, false otherwise
+ * @param pfResult where to store the result of the handling
+ */
+bool UIKeyboardHandler::keyEventHostComboHandled(int iKey, wchar_t *pUniKey, bool isHostComboStateChanged, bool *pfResult)
+{
+ if (isHostComboStateChanged)
+ {
+ if (!m_bIsHostComboPressed)
+ {
+ m_bIsHostComboPressed = true;
+ m_bIsHostComboAlone = true;
+ m_bIsHostComboProcessed = false;
+ if (isSessionRunning())
+ saveKeyStates();
+ }
+ }
+ else
+ {
+ if (m_bIsHostComboPressed)
+ {
+ if (m_bIsHostComboAlone)
+ {
+ m_bIsHostComboAlone = false;
+ m_bIsHostComboProcessed = true;
+ /* Process Host+<key> shortcuts.
+ * Currently, <key> is limited to alphanumeric chars.
+ * Other Host+<key> combinations are handled in Qt event(): */
+ *pfResult = processHotKey(iKey, pUniKey);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * Handle a key event that releases the host key combination
+ */
+void UIKeyboardHandler::keyEventHandleHostComboRelease(ulong uScreenId)
+{
+ if (m_bIsHostComboPressed)
+ {
+ m_bIsHostComboPressed = false;
+ /* Capturing/releasing keyboard/mouse if necessary: */
+ if (m_bIsHostComboAlone && !m_bIsHostComboProcessed)
+ {
+ if (isSessionRunning())
+ {
+ bool ok = true;
+ if (!m_fIsKeyboardCaptured)
+ {
+ /* Temporarily disable auto-capture that will take place after
+ * this dialog is dismissed because the capture state is to be
+ * defined by the dialog result itself: */
+ setAutoCaptureDisabled(true);
+ bool fIsAutoConfirmed = false;
+ ok = msgCenter().confirmInputCapture(fIsAutoConfirmed);
+ if (fIsAutoConfirmed)
+ setAutoCaptureDisabled(false);
+ /* Otherwise, the disable flag will be reset in the next
+ * machine-view's focus-in event (since may happen asynchronously
+ * on some platforms, after we return from this code): */
+ }
+ if (ok)
+ {
+ /* Determine whether the mouse can be captured: */
+ bool fCaptureMouse = !uisession()->isMouseSupportsAbsolute()
+ || !uisession()->isMouseIntegrated();
+
+ if (m_fIsKeyboardCaptured)
+ {
+ releaseKeyboard();
+ if (fCaptureMouse)
+ machineLogic()->mouseHandler()->releaseMouse();
+ }
+ else
+ {
+ captureKeyboard(uScreenId);
+#ifdef VBOX_WS_X11
+ /* Make sure that pending FocusOut events from the
+ * previous message box are handled, otherwise the
+ * mouse is immediately ungrabbed: */
+ /// @todo Is that really needed?
+ qApp->processEvents();
+#endif /* VBOX_WS_X11 */
+ finaliseCaptureKeyboard();
+ if (fCaptureMouse)
+ {
+ const MouseCapturePolicy mcp = gEDataManager->mouseCapturePolicy(uiCommon().managedVMUuid());
+ if (mcp == MouseCapturePolicy_Default || mcp == MouseCapturePolicy_HostComboOnly)
+ machineLogic()->mouseHandler()->captureMouse(uScreenId);
+ }
+ }
+ }
+ }
+ }
+ if (isSessionRunning())
+ sendChangedKeyStates();
+ }
+}
+
+void UIKeyboardHandler::keyEventReleaseHostComboKeys(const CKeyboard &constKeyboard)
+{
+ /* Get keyboard: */
+ CKeyboard keyboard(constKeyboard);
+ /* We have to make guest to release pressed keys from the host-combination: */
+ QList<uint8_t> hostComboScans = m_pressedHostComboKeys.values();
+ for (int i = 0 ; i < hostComboScans.size(); ++i)
+ {
+ uint8_t uScan = hostComboScans[i];
+ if (m_pressedKeys[uScan] & IsKeyPressed)
+ {
+ keyboard.PutScancode(uScan | 0x80);
+ }
+ else if (m_pressedKeys[uScan] & IsExtKeyPressed)
+ {
+ QVector<LONG> scancodes(2);
+ scancodes[0] = 0xE0;
+ scancodes[1] = uScan | 0x80;
+ keyboard.PutScancodes(scancodes);
+ }
+ m_pressedKeys[uScan] = 0;
+ }
+}
+
+bool UIKeyboardHandler::keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey /* = 0 */)
+{
+ /* Get host-combo key list: */
+ QList<int> allHostComboKeys = UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination());
+
+ /* Update the map of pressed host-combo keys: */
+ if (allHostComboKeys.contains(iKey))
+ {
+ if (fFlags & KeyPressed)
+ {
+ if (!m_pressedHostComboKeys.contains(iKey))
+ m_pressedHostComboKeys.insert(iKey, uScan);
+ else if (m_bIsHostComboPressed)
+ return true;
+ }
+ else
+ {
+ if (m_pressedHostComboKeys.contains(iKey))
+ m_pressedHostComboKeys.remove(iKey);
+ }
+ }
+
+ /* Check if we are currently holding FULL host-combo: */
+ bool fIsFullHostComboPresent = false;
+ if (!allHostComboKeys.isEmpty())
+ {
+ const QList<int> &pressedKeyList = m_pressedHostComboKeys.keys();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ fIsFullHostComboPresent = QSet<int>(allHostComboKeys.begin(), allHostComboKeys.end())
+ == QSet<int>(pressedKeyList.begin(), pressedKeyList.end());
+#else
+ fIsFullHostComboPresent = allHostComboKeys.toSet() == pressedKeyList.toSet();
+#endif
+ }
+
+ /* Check if currently pressed/released key had changed host-combo state: */
+ const bool isHostComboStateChanged = (!m_bIsHostComboPressed && fIsFullHostComboPresent) ||
+ ( m_bIsHostComboPressed && !fIsFullHostComboPresent);
+
+#ifdef VBOX_WS_WIN
+ if (m_bIsHostComboPressed || isHostComboStateChanged)
+ {
+ /* Currently this is used in winKeyboardEvent() only: */
+ m_fIsHostkeyInCapture = m_fIsKeyboardCaptured;
+ }
+#endif /* VBOX_WS_WIN */
+
+ if (keyEventCADHandled(uScan))
+ return true;
+
+ /* Preparing the press/release scan-codes array for sending to the guest:
+ * 1. if host-combo is NOT pressed, taking into account currently pressed key too,
+ * 2. if currently released key releases host-combo too.
+ * Using that rule, we are NOT sending to the guest:
+ * 1. the last key-press of host-combo,
+ * 2. all keys pressed while the host-combo being held (but we still send releases). */
+ LONG aCodesBuffer[16];
+ LONG *pCodes = aCodesBuffer;
+ uint uCodesCount = 0;
+ uint8_t uWhatPressed = fFlags & KeyExtended ? IsExtKeyPressed : IsKeyPressed;
+ if ((!m_bIsHostComboPressed && !isHostComboStateChanged) ||
+ ( m_bIsHostComboPressed && isHostComboStateChanged) ||
+ (!(fFlags & KeyPressed) && (m_pressedKeys[uScan] & uWhatPressed)))
+ {
+ /* Special flags handling (KeyPrint): */
+ if (fFlags & KeyPrint)
+ {
+ if (fFlags & KeyPressed)
+ {
+ static LONG PrintMake[] = { 0xE0, 0x37 };
+ pCodes = PrintMake;
+ uCodesCount = RT_ELEMENTS(PrintMake);
+ }
+ else
+ {
+ static LONG PrintBreak[] = { 0xE0, 0xB7 };
+ pCodes = PrintBreak;
+ uCodesCount = RT_ELEMENTS(PrintBreak);
+ }
+ }
+ /* Special flags handling (KeyPause): */
+ else if (fFlags & KeyPause)
+ {
+ if (fFlags & KeyPressed)
+ {
+ static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
+ pCodes = Pause;
+ uCodesCount = RT_ELEMENTS(Pause);
+ }
+ else
+ {
+ /* Pause shall not produce a break code: */
+ return true;
+ }
+ }
+ /* Common flags handling: */
+ else
+ if (keyEventHandleNormal(iKey, uScan, fFlags, pCodes, &uCodesCount))
+ return true;
+ }
+
+ /* Process the host-combo funtionality: */
+ if (fFlags & KeyPressed)
+ {
+ bool fResult;
+ if (keyEventHostComboHandled(iKey, pUniKey, isHostComboStateChanged, &fResult))
+ return fResult;
+ }
+ else
+ {
+ if (isHostComboStateChanged)
+ keyEventHandleHostComboRelease(uScreenId);
+ else
+ {
+ if (m_bIsHostComboPressed)
+ m_bIsHostComboAlone = true;
+ }
+ }
+
+ /* Notify all the listeners: */
+ emit sigStateChange(state());
+
+ /* If the VM is NOT paused: */
+ if (!uisession()->isPaused())
+ {
+ /* If there are scan-codes to send: */
+ if (uCodesCount)
+ {
+ /* Send prepared scan-codes to the guest: */
+ QVector<LONG> scancodes;
+ for (uint i = 0; i < uCodesCount; ++i)
+ scancodes.append(pCodes[i]);
+ keyboard().PutScancodes(scancodes);
+ }
+
+ /* If full host-key sequence was just finalized: */
+ if (isHostComboStateChanged && m_bIsHostComboPressed)
+ {
+ keyEventReleaseHostComboKeys(keyboard());
+ }
+ }
+
+ /* Prevent the key from going to Qt: */
+ return true;
+}
+
+bool UIKeyboardHandler::processHotKey(int iHotKey, wchar_t *pHotKey)
+{
+ /* Prepare processing result: */
+ bool fWasProcessed = false;
+
+#if defined(VBOX_WS_MAC)
+
+ Q_UNUSED(iHotKey);
+ if (pHotKey && pHotKey[0] && !pHotKey[1])
+ fWasProcessed = actionPool()->processHotKey(QKeySequence(QChar(pHotKey[0]).toUpper().unicode()));
+
+#elif defined(VBOX_WS_WIN)
+
+ Q_UNUSED(pHotKey);
+ int iKeyboardLayout = GetKeyboardLayoutList(0, NULL);
+ Assert(iKeyboardLayout);
+ HKL *pList = new HKL[iKeyboardLayout];
+ GetKeyboardLayoutList(iKeyboardLayout, pList);
+ for (int i = 0; i < iKeyboardLayout && !fWasProcessed; ++i)
+ {
+ wchar_t symbol;
+ static BYTE keys[256] = {0};
+ if (!ToUnicodeEx(iHotKey, 0, keys, &symbol, 1, 0, pList[i]) == 1)
+ symbol = 0;
+ if (symbol)
+ fWasProcessed = actionPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + QChar(symbol).toUpper().unicode())));
+ }
+ delete[] pList;
+
+#elif defined(VBOX_WS_X11)
+
+ Q_UNUSED(pHotKey);
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ KeyCode keyCode = XKeysymToKeycode(pDisplay, iHotKey);
+ for (int i = 0; i < 4 && !fWasProcessed; ++i) /* Up to four groups. */
+ {
+ KeySym ks = wrapXkbKeycodeToKeysym(pDisplay, keyCode, i, 0);
+ char symbol = 0;
+ if (XkbTranslateKeySym(pDisplay, &ks, 0, &symbol, 1, NULL) == 0)
+ symbol = 0;
+ if (symbol)
+ {
+ QChar qtSymbol = QString::fromLocal8Bit(&symbol, 1)[0];
+ fWasProcessed = actionPool()->processHotKey(QKeySequence(qtSymbol.toUpper().unicode()));
+ }
+ }
+
+#else
+
+# warning "port me!"
+
+#endif
+
+ /* Grab the key from the Qt if it was processed, or pass it to the Qt otherwise
+ * in order to process non-alphanumeric keys in event(), after they are converted to Qt virtual keys: */
+ return fWasProcessed;
+}
+
+void UIKeyboardHandler::fixModifierState(LONG *piCodes, uint *puCount)
+{
+ /* Synchronize the views of the host and the guest to the modifier keys.
+ * This function will add up to 6 additional keycodes to codes. */
+
+#if defined(VBOX_WS_MAC)
+
+ /* if (uisession()->numLockAdaptionCnt()) ... - NumLock isn't implemented by Mac OS X so ignore it. */
+ if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(::GetCurrentEventKeyModifiers() & alphaLock)))
+ {
+ uisession()->setCapsLockAdaptionCnt(uisession()->capsLockAdaptionCnt() - 1);
+ piCodes[(*puCount)++] = 0x3a;
+ piCodes[(*puCount)++] = 0x3a | 0x80;
+ /* Some keyboard layouts require shift to be pressed to break
+ * capslock. For simplicity, only do this if shift is not
+ * already held down. */
+ if (uisession()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
+ {
+ piCodes[(*puCount)++] = 0x2a;
+ piCodes[(*puCount)++] = 0x2a | 0x80;
+ }
+ }
+
+#elif defined(VBOX_WS_WIN)
+
+ if (uisession()->numLockAdaptionCnt() && (uisession()->isNumLock() ^ !!(GetKeyState(VK_NUMLOCK))))
+ {
+ uisession()->setNumLockAdaptionCnt(uisession()->numLockAdaptionCnt() - 1);
+ piCodes[(*puCount)++] = 0x45;
+ piCodes[(*puCount)++] = 0x45 | 0x80;
+ }
+ if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(GetKeyState(VK_CAPITAL))))
+ {
+ uisession()->setCapsLockAdaptionCnt(uisession()->capsLockAdaptionCnt() - 1);
+ piCodes[(*puCount)++] = 0x3a;
+ piCodes[(*puCount)++] = 0x3a | 0x80;
+ /* Some keyboard layouts require shift to be pressed to break
+ * capslock. For simplicity, only do this if shift is not
+ * already held down. */
+ if (uisession()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
+ {
+ piCodes[(*puCount)++] = 0x2a;
+ piCodes[(*puCount)++] = 0x2a | 0x80;
+ }
+ }
+
+#elif defined(VBOX_WS_X11)
+
+ Window wDummy1, wDummy2;
+ int iDummy3, iDummy4, iDummy5, iDummy6;
+ unsigned uMask;
+ unsigned uKeyMaskNum = 0, uKeyMaskCaps = 0;
+ Display * const pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ uKeyMaskCaps = LockMask;
+ XModifierKeymap* map = XGetModifierMapping(pDisplay);
+ KeyCode keyCodeNum = XKeysymToKeycode(pDisplay, XK_Num_Lock);
+
+ for (int i = 0; i < 8; ++ i)
+ if (keyCodeNum != NoSymbol && map->modifiermap[map->max_keypermod * i] == keyCodeNum)
+ uKeyMaskNum = 1 << i;
+ XQueryPointer(pDisplay, DefaultRootWindow(pDisplay), &wDummy1, &wDummy2,
+ &iDummy3, &iDummy4, &iDummy5, &iDummy6, &uMask);
+ XFreeModifiermap(map);
+
+ if (uisession()->numLockAdaptionCnt() && (uisession()->isNumLock() ^ !!(uMask & uKeyMaskNum)))
+ {
+ uisession()->setNumLockAdaptionCnt(uisession()->numLockAdaptionCnt() - 1);
+ piCodes[(*puCount)++] = 0x45;
+ piCodes[(*puCount)++] = 0x45 | 0x80;
+ }
+ if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(uMask & uKeyMaskCaps)))
+ {
+ uisession()->setCapsLockAdaptionCnt(uisession()->capsLockAdaptionCnt() - 1);
+ piCodes[(*puCount)++] = 0x3a;
+ piCodes[(*puCount)++] = 0x3a | 0x80;
+ /* Some keyboard layouts require shift to be pressed to break
+ * capslock. For simplicity, only do this if shift is not
+ * already held down. */
+ if (uisession()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
+ {
+ piCodes[(*puCount)++] = 0x2a;
+ piCodes[(*puCount)++] = 0x2a | 0x80;
+ }
+ }
+
+#else
+
+# warning "port me!"
+
+#endif
+}
+
+void UIKeyboardHandler::saveKeyStates()
+{
+ ::memcpy(m_pressedKeysCopy, m_pressedKeys, sizeof(m_pressedKeys));
+}
+
+void UIKeyboardHandler::sendChangedKeyStates()
+{
+ QVector <LONG> codes(2);
+ for (uint i = 0; i < RT_ELEMENTS(m_pressedKeys); ++ i)
+ {
+ uint8_t os = m_pressedKeysCopy[i];
+ uint8_t ns = m_pressedKeys[i];
+ if ((os & IsKeyPressed) != (ns & IsKeyPressed))
+ {
+ codes[0] = i;
+ if (!(ns & IsKeyPressed))
+ codes[0] |= 0x80;
+ keyboard().PutScancode(codes[0]);
+ }
+ else if ((os & IsExtKeyPressed) != (ns & IsExtKeyPressed))
+ {
+ codes[0] = 0xE0;
+ codes[1] = i;
+ if (!(ns & IsExtKeyPressed))
+ codes[1] |= 0x80;
+ keyboard().PutScancodes(codes);
+ }
+ }
+}
+
+bool UIKeyboardHandler::isAutoCaptureDisabled()
+{
+ return uisession()->isAutoCaptureDisabled();
+}
+
+void UIKeyboardHandler::setAutoCaptureDisabled(bool fIsAutoCaptureDisabled)
+{
+ uisession()->setAutoCaptureDisabled(fIsAutoCaptureDisabled);
+}
+
+bool UIKeyboardHandler::autoCaptureSetGlobally()
+{
+ return gEDataManager->autoCaptureEnabled() && !m_fDebuggerActive;
+}
+
+bool UIKeyboardHandler::viewHasFocus(ulong uScreenId)
+{
+ return m_views[uScreenId]->hasFocus();
+}
+
+bool UIKeyboardHandler::isSessionRunning()
+{
+ return uisession()->isRunning();
+}
+
+bool UIKeyboardHandler::isSessionStuck()
+{
+ return uisession()->isStuck();
+}
+
+UIMachineWindow* UIKeyboardHandler::isItListenedWindow(QObject *pWatchedObject) const
+{
+ UIMachineWindow *pResultWindow = 0;
+ QMap<ulong, UIMachineWindow*>::const_iterator i = m_windows.constBegin();
+ while (!pResultWindow && i != m_windows.constEnd())
+ {
+ UIMachineWindow *pIteratedWindow = i.value();
+ if (pIteratedWindow == pWatchedObject)
+ {
+ pResultWindow = pIteratedWindow;
+ continue;
+ }
+ ++i;
+ }
+ return pResultWindow;
+}
+
+UIMachineView* UIKeyboardHandler::isItListenedView(QObject *pWatchedObject) const
+{
+ UIMachineView *pResultView = 0;
+ QMap<ulong, UIMachineView*>::const_iterator i = m_views.constBegin();
+ while (!pResultView && i != m_views.constEnd())
+ {
+ UIMachineView *pIteratedView = i.value();
+ if (pIteratedView == pWatchedObject)
+ {
+ pResultView = pIteratedView;
+ continue;
+ }
+ ++i;
+ }
+ return pResultView;
+}
+
+void UIKeyboardHandler::setHostKeyComboPressedFlag(bool bPressed)
+{
+ m_fHostKeyComboPressInserted = bPressed;
+ emit sigStateChange(state());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.h
new file mode 100644
index 00000000..44f9aab8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.h
@@ -0,0 +1,241 @@
+/* $Id: UIKeyboardHandler.h $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandler class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIKeyboardHandler_h
+#define FEQT_INCLUDED_SRC_runtime_UIKeyboardHandler_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QObject>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+/* Other VBox includes: */
+#include <VBox/com/defs.h>
+
+/* External includes: */
+#ifdef VBOX_WS_MAC
+# include <Carbon/Carbon.h>
+# include <CoreFoundation/CFBase.h>
+#endif /* VBOX_WS_MAC */
+
+/* Forward declarations: */
+class QWidget;
+class UIActionPool;
+class UISession;
+class UIMachineLogic;
+class UIMachineWindow;
+class UIMachineView;
+class CKeyboard;
+#ifdef VBOX_WS_WIN
+class WinAltGrMonitor;
+#endif
+#ifdef VBOX_WS_X11
+# include <xcb/xcb.h>
+#endif
+
+
+/* Delegate to control VM keyboard functionality: */
+class UIKeyboardHandler : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about state-change. */
+ void sigStateChange(int iState);
+
+public:
+
+ /* Factory functions to create/destroy keyboard-handler: */
+ static UIKeyboardHandler* create(UIMachineLogic *pMachineLogic, UIVisualStateType visualStateType);
+ static void destroy(UIKeyboardHandler *pKeyboardHandler);
+
+ /* Prepare/cleanup listeners: */
+ void prepareListener(ulong uScreenId, UIMachineWindow *pMachineWindow);
+ void cleanupListener(ulong uScreenId);
+
+ /** Captures the keyboard for @a uScreenId. */
+ void captureKeyboard(ulong uScreenId);
+ /** Finalises keyboard capture. */
+ bool finaliseCaptureKeyboard();
+ /** Releases the keyboard. */
+ void releaseKeyboard();
+
+ void releaseAllPressedKeys(bool aReleaseHostKey = true);
+
+ /* Current keyboard state: */
+ int state() const;
+
+ /* Some getters required by side-code: */
+ bool isHostKeyPressed() const { return m_bIsHostComboPressed; }
+#ifdef VBOX_WS_MAC
+ bool isHostKeyAlone() const { return m_bIsHostComboAlone; }
+ bool isKeyboardGrabbed() const { return m_iKeyboardHookViewIndex != -1; }
+#endif /* VBOX_WS_MAC */
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* For the debugger. */
+ void setDebuggerActive(bool aActive = true);
+#endif
+
+#ifdef VBOX_WS_WIN
+ /** Tells the keyboard event handler to skip host keyboard events.
+ * Used for HID LEDs sync when on Windows host a keyboard event
+ * is generated in order to change corresponding LED. */
+ void winSkipKeyboardEvents(bool fSkip);
+#endif /* VBOX_WS_WIN */
+
+ /** Qt5: Performs pre-processing of all the native events. */
+ bool nativeEventFilter(void *pMessage, ulong uScreenId);
+
+ /** Called whenever host key press/release scan codes are inserted to the guest.
+ * @a bPressed is true for press and false for release inserts. */
+ void setHostKeyComboPressedFlag(bool bPressed);
+
+protected slots:
+
+ /* Machine state-change handler: */
+ virtual void sltMachineStateChanged();
+
+ /** Finalises keyboard capture. */
+ void sltFinaliseCaptureKeyboard();
+
+protected:
+
+ /* Keyboard-handler constructor/destructor: */
+ UIKeyboardHandler(UIMachineLogic *pMachineLogic);
+ virtual ~UIKeyboardHandler();
+
+ /* Prepare helpers: */
+ virtual void prepareCommon();
+ virtual void loadSettings();
+
+ /* Cleanup helpers: */
+ //virtual void saveSettings() {}
+ virtual void cleanupCommon();
+
+ /* Common getters: */
+ UIMachineLogic* machineLogic() const;
+ UIActionPool* actionPool() const;
+ UISession* uisession() const;
+
+ /** Returns the console's keyboard reference. */
+ CKeyboard& keyboard() const;
+
+ /* Event handler for registered machine-view(s): */
+ bool eventFilter(QObject *pWatchedObject, QEvent *pEvent);
+
+#if defined(VBOX_WS_MAC)
+ /** Mac: Performs initial pre-processing of all the native keyboard events. */
+ static bool macKeyboardProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser);
+ /** Mac: Performs initial pre-processing of all the native keyboard events. */
+ bool macKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent);
+#elif defined(VBOX_WS_WIN)
+ /** Win: Performs initial pre-processing of all the native keyboard events. */
+ static LRESULT CALLBACK winKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) RT_NOTHROW_PROTO;
+ /** Win: Performs initial pre-processing of all the native keyboard events. */
+ bool winKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event);
+#endif /* VBOX_WS_WIN */
+
+ bool keyEventCADHandled(uint8_t uScan);
+ bool keyEventHandleNormal(int iKey, uint8_t uScan, int fFlags, LONG *pCodes, uint *puCodesCount);
+ bool keyEventHostComboHandled(int iKey, wchar_t *pUniKey, bool isHostComboStateChanged, bool *pfResult);
+ void keyEventHandleHostComboRelease(ulong uScreenId);
+ void keyEventReleaseHostComboKeys(const CKeyboard &keyboard);
+ /* Separate function to handle most of existing keyboard-events: */
+ bool keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey = 0);
+ bool processHotKey(int iHotKey, wchar_t *pUniKey);
+
+ /* Private helpers: */
+ void fixModifierState(LONG *piCodes, uint *puCount);
+ void saveKeyStates();
+ void sendChangedKeyStates();
+ bool isAutoCaptureDisabled();
+ void setAutoCaptureDisabled(bool fIsAutoCaptureDisabled);
+ bool autoCaptureSetGlobally();
+ bool viewHasFocus(ulong uScreenId);
+ bool isSessionRunning();
+ bool isSessionStuck();
+
+ UIMachineWindow* isItListenedWindow(QObject *pWatchedObject) const;
+ UIMachineView* isItListenedView(QObject *pWatchedObject) const;
+
+ /* Machine logic parent: */
+ UIMachineLogic *m_pMachineLogic;
+
+ /* Registered machine-window(s): */
+ QMap<ulong, UIMachineWindow*> m_windows;
+ /* Registered machine-view(s): */
+ QMap<ulong, UIMachineView*> m_views;
+
+ /* Other keyboard variables: */
+ int m_iKeyboardCaptureViewIndex;
+
+ uint8_t m_pressedKeys[128];
+ uint8_t m_pressedKeysCopy[128];
+
+ QMap<int, uint8_t> m_pressedHostComboKeys;
+
+ bool m_fIsKeyboardCaptured : 1;
+ bool m_bIsHostComboPressed : 1;
+ bool m_bIsHostComboAlone : 1;
+ bool m_bIsHostComboProcessed : 1;
+ bool m_fPassCADtoGuest : 1;
+ bool m_fHostKeyComboPressInserted : 1;
+ /** Whether the debugger is active.
+ * Currently only affects auto capturing. */
+ bool m_fDebuggerActive : 1;
+
+ /** Holds the keyboard hook view index. */
+ int m_iKeyboardHookViewIndex;
+
+#if defined(VBOX_WS_MAC)
+ /** Mac: Holds the current modifiers key mask. */
+ UInt32 m_uDarwinKeyModifiers;
+#elif defined(VBOX_WS_WIN)
+ /** Win: Currently this is used in winKeyboardEvent() only. */
+ bool m_fIsHostkeyInCapture;
+ /** Win: Holds whether the keyboard event filter should ignore keyboard events. */
+ bool m_fSkipKeyboardEvents;
+ /** Win: Holds the keyboard hook instance. */
+ HHOOK m_keyboardHook;
+ /** Win: Holds the object monitoring key event stream for problematic AltGr events. */
+ WinAltGrMonitor *m_pAltGrMonitor;
+ /** Win: Holds the keyboard handler reference to be accessible from the keyboard hook. */
+ static UIKeyboardHandler *m_spKeyboardHandler;
+#elif defined(VBOX_WS_X11)
+ /** The root window at the time we grab the mouse buttons. */
+ xcb_window_t m_hButtonGrabWindow;
+#endif /* VBOX_WS_X11 */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIKeyboardHandler_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMachine.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachine.cpp
new file mode 100644
index 00000000..ec9ddda1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachine.cpp
@@ -0,0 +1,330 @@
+/* $Id: UIMachine.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachine class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIMachine.h"
+#include "UISession.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogic.h"
+#include "UIMachineWindow.h"
+#include "UIMessageCenter.h"
+
+/* COM includes: */
+#include "CMachine.h"
+#include "CSession.h"
+#include "CConsole.h"
+#include "CSnapshot.h"
+#include "CProgress.h"
+
+
+/* static */
+UIMachine *UIMachine::m_spInstance = 0;
+
+/* static */
+bool UIMachine::startMachine(const QUuid &uID)
+{
+ /* Make sure machine is not created: */
+ AssertReturn(!m_spInstance, false);
+
+ /* Restore current snapshot if requested: */
+ if (uiCommon().shouldRestoreCurrentSnapshot())
+ {
+ /* Create temporary session: */
+ CSession session = uiCommon().openSession(uID, KLockType_VM);
+ if (session.isNull())
+ return false;
+
+ /* Which VM we operate on? */
+ CMachine machine = session.GetMachine();
+ /* Which snapshot we are restoring? */
+ CSnapshot snapshot = machine.GetCurrentSnapshot();
+
+ /* Prepare restore-snapshot progress: */
+ CProgress progress = machine.RestoreSnapshot(snapshot);
+ if (!machine.isOk())
+ return msgCenter().cannotRestoreSnapshot(machine, snapshot.GetName(), machine.GetName());
+
+ /* Show the snapshot-discarding progress: */
+ msgCenter().showModalProgressDialog(progress, machine.GetName(), ":/progress_snapshot_discard_90px.png");
+ if (progress.GetResultCode() != 0)
+ return msgCenter().cannotRestoreSnapshot(progress, snapshot.GetName(), machine.GetName());
+
+ /* Unlock session finally: */
+ session.UnlockMachine();
+
+ /* Clear snapshot-restoring request: */
+ uiCommon().setShouldRestoreCurrentSnapshot(false);
+ }
+
+ /* For separate process we should launch VM before UI: */
+ if (uiCommon().isSeparateProcess())
+ {
+ /* Get corresponding machine: */
+ CMachine machine = uiCommon().virtualBox().FindMachine(uiCommon().managedVMUuid().toString());
+ AssertMsgReturn(!machine.isNull(), ("UICommon::managedVMUuid() should have filter that case before!\n"), false);
+
+ /* Try to launch corresponding machine: */
+ if (!UICommon::launchMachine(machine, UILaunchMode_Separate))
+ return false;
+ }
+
+ /* Try to create machine UI: */
+ return create();
+}
+
+/* static */
+bool UIMachine::create()
+{
+ /* Make sure machine is not created: */
+ AssertReturn(!m_spInstance, false);
+
+ /* Create machine UI: */
+ new UIMachine;
+ /* Make sure it's prepared: */
+ if (!m_spInstance->prepare())
+ {
+ /* Destroy machine UI otherwise: */
+ destroy();
+ /* False in that case: */
+ return false;
+ }
+ /* True by default: */
+ return true;
+}
+
+/* static */
+void UIMachine::destroy()
+{
+ /* Make sure machine is created: */
+ if (!m_spInstance)
+ return;
+
+ /* Protect versus recursive call: */
+ UIMachine *pInstance = m_spInstance;
+ m_spInstance = 0;
+ /* Cleanup machine UI: */
+ pInstance->cleanup();
+ /* Destroy machine UI: */
+ delete pInstance;
+}
+
+QWidget* UIMachine::activeWindow() const
+{
+ return machineLogic() && machineLogic()->activeMachineWindow()
+ ? machineLogic()->activeMachineWindow()
+ : 0;
+}
+
+void UIMachine::asyncChangeVisualState(UIVisualStateType visualState)
+{
+ emit sigRequestAsyncVisualStateChange(visualState);
+}
+
+void UIMachine::setRequestedVisualState(UIVisualStateType visualStateType)
+{
+ /* Remember requested visual state: */
+ m_enmRequestedVisualState = visualStateType;
+
+ /* Save only if it's different from Invalid and from current one: */
+ if ( m_enmRequestedVisualState != UIVisualStateType_Invalid
+ && gEDataManager->requestedVisualState(uiCommon().managedVMUuid()) != m_enmRequestedVisualState)
+ gEDataManager->setRequestedVisualState(m_enmRequestedVisualState, uiCommon().managedVMUuid());
+}
+
+UIVisualStateType UIMachine::requestedVisualState() const
+{
+ return m_enmRequestedVisualState;
+}
+
+void UIMachine::closeRuntimeUI()
+{
+ /* Quit application: */
+ QApplication::quit();
+}
+
+void UIMachine::sltChangeVisualState(UIVisualStateType visualState)
+{
+ /* Create new machine-logic: */
+ UIMachineLogic *pMachineLogic = UIMachineLogic::create(this, m_pSession, visualState);
+
+ /* First we have to check if the selected machine-logic is available at all.
+ * Only then we delete the old machine-logic and switch to the new one. */
+ if (pMachineLogic->checkAvailability())
+ {
+ /* Delete previous machine-logic if exists: */
+ if (m_pMachineLogic)
+ {
+ m_pMachineLogic->cleanup();
+ UIMachineLogic::destroy(m_pMachineLogic);
+ m_pMachineLogic = 0;
+ }
+
+ /* Set the new machine-logic as current one: */
+ m_pMachineLogic = pMachineLogic;
+ m_pMachineLogic->prepare();
+
+ /* Remember new visual state: */
+ m_visualState = visualState;
+
+ /* Save requested visual state: */
+ gEDataManager->setRequestedVisualState(m_visualState, uiCommon().managedVMUuid());
+ }
+ else
+ {
+ /* Delete temporary created machine-logic: */
+ pMachineLogic->cleanup();
+ UIMachineLogic::destroy(pMachineLogic);
+ }
+
+ /* Make sure machine-logic exists: */
+ if (!m_pMachineLogic)
+ {
+ /* Reset initial visual state to normal: */
+ m_initialVisualState = UIVisualStateType_Normal;
+ /* Enter initial visual state again: */
+ enterInitialVisualState();
+ }
+}
+
+UIMachine::UIMachine()
+ : QObject(0)
+ , m_pSession(0)
+ , m_allowedVisualStates(UIVisualStateType_Invalid)
+ , m_initialVisualState(UIVisualStateType_Normal)
+ , m_visualState(UIVisualStateType_Invalid)
+ , m_enmRequestedVisualState(UIVisualStateType_Invalid)
+ , m_pMachineLogic(0)
+{
+ m_spInstance = this;
+}
+
+UIMachine::~UIMachine()
+{
+ m_spInstance = 0;
+}
+
+bool UIMachine::prepare()
+{
+ /* Try to prepare session UI: */
+ if (!prepareSession())
+ return false;
+
+ /* Cache media data early if necessary: */
+ if (uiCommon().agressiveCaching())
+ {
+ AssertReturn(m_pSession, false);
+ uiCommon().enumerateMedia(m_pSession->machineMedia());
+ }
+
+ /* Prepare machine-logic: */
+ prepareMachineLogic();
+
+ /* Try to initialize session UI: */
+ if (!uisession()->initialize())
+ return false;
+
+ /* True by default: */
+ return true;
+}
+
+bool UIMachine::prepareSession()
+{
+ /* Try to create session UI: */
+ if (!UISession::create(m_pSession, this))
+ return false;
+
+ /* True by default: */
+ return true;
+}
+
+void UIMachine::prepareMachineLogic()
+{
+ /* Prepare async visual state type change handler: */
+ qRegisterMetaType<UIVisualStateType>();
+ connect(this, &UIMachine::sigRequestAsyncVisualStateChange,
+ this, &UIMachine::sltChangeVisualState,
+ Qt::QueuedConnection);
+
+ /* Load restricted visual states: */
+ UIVisualStateType restrictedVisualStates = gEDataManager->restrictedVisualStates(uiCommon().managedVMUuid());
+ /* Acquire allowed visual states: */
+ m_allowedVisualStates = static_cast<UIVisualStateType>(UIVisualStateType_All ^ restrictedVisualStates);
+
+ /* Load requested visual state, it can override initial one: */
+ m_enmRequestedVisualState = gEDataManager->requestedVisualState(uiCommon().managedVMUuid());
+ /* Check if requested visual state is allowed: */
+ if (isVisualStateAllowed(m_enmRequestedVisualState))
+ {
+ switch (m_enmRequestedVisualState)
+ {
+ /* Direct transition allowed to scale/fullscreen modes only: */
+ case UIVisualStateType_Scale: m_initialVisualState = UIVisualStateType_Scale; break;
+ case UIVisualStateType_Fullscreen: m_initialVisualState = UIVisualStateType_Fullscreen; break;
+ default: break;
+ }
+ }
+
+ /* Enter initial visual state: */
+ enterInitialVisualState();
+}
+
+void UIMachine::cleanupMachineLogic()
+{
+ /* Destroy machine-logic if exists: */
+ if (m_pMachineLogic)
+ {
+ m_pMachineLogic->cleanup();
+ UIMachineLogic::destroy(m_pMachineLogic);
+ m_pMachineLogic = 0;
+ }
+}
+
+void UIMachine::cleanupSession()
+{
+ /* Destroy session UI if exists: */
+ if (uisession())
+ UISession::destroy(m_pSession);
+}
+
+void UIMachine::cleanup()
+{
+ /* Preprocess all the meta-events: */
+ QApplication::sendPostedEvents(0, QEvent::MetaCall);
+
+ /* Cleanup machine-logic: */
+ cleanupMachineLogic();
+
+ /* Cleanup session UI: */
+ cleanupSession();
+}
+
+void UIMachine::enterInitialVisualState()
+{
+ sltChangeVisualState(m_initialVisualState);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMachine.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachine.h
new file mode 100644
index 00000000..9620b85a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachine.h
@@ -0,0 +1,144 @@
+/* $Id: UIMachine.h $ */
+/** @file
+ * VBox Qt GUI - UIMachine class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIMachine_h
+#define FEQT_INCLUDED_SRC_runtime_UIMachine_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+#include "UIMachineDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QWidget;
+class UISession;
+class UIMachineLogic;
+
+/** Singleton QObject extension
+ * used as virtual machine (VM) singleton instance. */
+class UIMachine : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Requests async visual-state change. */
+ void sigRequestAsyncVisualStateChange(UIVisualStateType visualStateType);
+
+public:
+
+ /** Static factory to start machine with passed @a uID.
+ * @return true if machine was started, false otherwise. */
+ static bool startMachine(const QUuid &uID);
+ /** Static constructor. */
+ static bool create();
+ /** Static destructor. */
+ static void destroy();
+ /** Static instance. */
+ static UIMachine* instance() { return m_spInstance; }
+
+ /** Returns session UI instance. */
+ UISession *uisession() const { return m_pSession; }
+ /** Returns machine-logic instance. */
+ UIMachineLogic* machineLogic() const { return m_pMachineLogic; }
+ /** Returns active machine-window reference (if possible). */
+ QWidget* activeWindow() const;
+
+ /** Returns whether requested visual @a state allowed. */
+ bool isVisualStateAllowed(UIVisualStateType state) const { return m_allowedVisualStates & state; }
+
+ /** Requests async visual-state change. */
+ void asyncChangeVisualState(UIVisualStateType visualStateType);
+
+ /** Requests visual-state to be entered when possible. */
+ void setRequestedVisualState(UIVisualStateType visualStateType);
+ /** Returns requested visual-state to be entered when possible. */
+ UIVisualStateType requestedVisualState() const;
+
+public slots:
+
+ /** Closes Runtime UI. */
+ void closeRuntimeUI();
+
+private slots:
+
+ /** Visual state-change handler. */
+ void sltChangeVisualState(UIVisualStateType visualStateType);
+
+private:
+
+ /** Constructor. */
+ UIMachine();
+ /** Destructor. */
+ ~UIMachine();
+
+ /** Prepare routine. */
+ bool prepare();
+ /** Prepare routine: Session stuff. */
+ bool prepareSession();
+ /** Prepare routine: Machine-logic stuff. */
+ void prepareMachineLogic();
+
+ /** Cleanup routine: Machine-logic stuff. */
+ void cleanupMachineLogic();
+ /** Cleanup routine: Session stuff. */
+ void cleanupSession();
+ /** Cleanup routine. */
+ void cleanup();
+
+ /** Moves VM to initial state. */
+ void enterInitialVisualState();
+
+ /** Static instance. */
+ static UIMachine* m_spInstance;
+
+ /** Holds the session UI instance. */
+ UISession *m_pSession;
+
+ /** Holds allowed visual states. */
+ UIVisualStateType m_allowedVisualStates;
+ /** Holds initial visual state. */
+ UIVisualStateType m_initialVisualState;
+ /** Holds current visual state. */
+ UIVisualStateType m_visualState;
+ /** Holds visual state which should be entered when possible. */
+ UIVisualStateType m_enmRequestedVisualState;
+ /** Holds current machine-logic. */
+ UIMachineLogic *m_pMachineLogic;
+};
+
+#define gpMachine UIMachine::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIMachine_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineDefs.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineDefs.h
new file mode 100644
index 00000000..a3fdbb80
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineDefs.h
@@ -0,0 +1,76 @@
+/* $Id: UIMachineDefs.h $ */
+/** @file
+ * VBox Qt GUI - Defines for Virtual Machine classes.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIMachineDefs_h
+#define FEQT_INCLUDED_SRC_runtime_UIMachineDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+/** Machine window visual element types. */
+enum UIVisualElement
+{
+ UIVisualElement_WindowTitle = RT_BIT(0),
+ UIVisualElement_MouseIntegrationStuff = RT_BIT(1),
+ UIVisualElement_IndicatorPoolStuff = RT_BIT(2),
+ UIVisualElement_HDStuff = RT_BIT(3),
+ UIVisualElement_CDStuff = RT_BIT(4),
+ UIVisualElement_FDStuff = RT_BIT(5),
+ UIVisualElement_AudioStuff = RT_BIT(6),
+ UIVisualElement_NetworkStuff = RT_BIT(7),
+ UIVisualElement_USBStuff = RT_BIT(8),
+ UIVisualElement_SharedFolderStuff = RT_BIT(9),
+ UIVisualElement_Display = RT_BIT(10),
+ UIVisualElement_Recording = RT_BIT(11),
+ UIVisualElement_FeaturesStuff = RT_BIT(12),
+#ifndef VBOX_WS_MAC
+ UIVisualElement_MiniToolBar = RT_BIT(13),
+#endif
+ UIVisualElement_AllStuff = 0xFFFF
+};
+
+/** Mouse state types. */
+enum UIMouseStateType
+{
+ UIMouseStateType_MouseCaptured = RT_BIT(0),
+ UIMouseStateType_MouseAbsolute = RT_BIT(1),
+ UIMouseStateType_MouseAbsoluteDisabled = RT_BIT(2),
+ UIMouseStateType_MouseNeedsHostCursor = RT_BIT(3)
+};
+
+/** Keyboard state types. */
+enum UIKeyboardStateType
+{
+ UIKeyboardStateType_KeyboardCaptured = RT_BIT(0),
+ UIKeyboardStateType_HostKeyPressed = RT_BIT(1),
+ UIKeyboardStateType_HostKeyPressedInsertion = RT_BIT(2)
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIMachineDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.cpp
new file mode 100644
index 00000000..9e13c454
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.cpp
@@ -0,0 +1,3394 @@
+/* $Id: UIMachineLogic.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogic class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QActionGroup>
+#include <QDateTime>
+#include <QDir>
+#include <QFileInfo>
+#include <QImageWriter>
+#include <QPainter>
+#include <QRegExp>
+#include <QRegularExpression>
+#include <QTimer>
+#ifdef VBOX_WS_MAC
+# include <QMenuBar>
+#endif /* VBOX_WS_MAC */
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "UIActionPoolRuntime.h"
+#include "UIAddDiskEncryptionPasswordDialog.h"
+#include "UIBootFailureDialog.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIFileManagerDialog.h"
+#include "UIFrameBuffer.h"
+#include "UIGuestProcessControlDialog.h"
+#include "UIHostComboEditor.h"
+#include "UIIconPool.h"
+#include "UIKeyboardHandler.h"
+#include "UIMachineLogic.h"
+#include "UIMachineLogicFullscreen.h"
+#include "UIMachineLogicNormal.h"
+#include "UIMachineLogicSeamless.h"
+#include "UIMachineLogicScale.h"
+#include "UIMachineView.h"
+#include "UIMachineWindow.h"
+#include "UIMedium.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UIMouseHandler.h"
+#include "UINotificationCenter.h"
+#include "UISession.h"
+#include "UISettingsDialogSpecific.h"
+#include "UISoftKeyboard.h"
+#include "UITakeSnapshotDialog.h"
+#include "UIVMLogViewerDialog.h"
+#include "UIVMInformationDialog.h"
+#ifdef VBOX_WS_MAC
+# include "DockIconPreview.h"
+# include "UIExtraDataManager.h"
+#endif
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+# include "UINetworkRequestManager.h"
+#endif
+#ifdef VBOX_WS_X11
+# include "VBoxUtils-x11.h"
+#endif
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CAudioSettings.h"
+#include "CDisplay.h"
+#include "CEmulatedUSB.h"
+#include "CGraphicsAdapter.h"
+#include "CHostUSBDevice.h"
+#include "CHostVideoInputDevice.h"
+#include "CMachineDebugger.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CRecordingSettings.h"
+#include "CSnapshot.h"
+#include "CStorageController.h"
+#include "CSystemProperties.h"
+#include "CUSBDevice.h"
+#include "CVirtualBoxErrorInfo.h"
+#include "CVRDEServer.h"
+#ifdef VBOX_WS_MAC
+# include "CGuest.h"
+#endif
+
+/* Other VBox includes: */
+#include <iprt/path.h>
+#include <iprt/thread.h>
+#ifdef VBOX_WITH_DEBUGGER_GUI
+# include <VBox/dbggui.h>
+# include <iprt/ldr.h>
+#endif
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+/* External / Other VBox includes: */
+#ifdef VBOX_WS_MAC
+# include "DarwinKeyboard.h"
+#endif
+#ifdef VBOX_WS_WIN
+# include "WinKeyboard.h"
+# include "VBoxUtils-win.h"
+#endif
+#ifdef VBOX_WS_X11
+# include <XKeyboard.h>
+#endif
+
+#define VBOX_WITH_REWORKED_SESSION_INFORMATION /**< Define for reworked session-information window. @todo r-bird: What's this for? */
+
+struct USBTarget
+{
+ USBTarget() : attach(false), id(QUuid()) {}
+ USBTarget(bool fAttach, const QUuid &uId)
+ : attach(fAttach), id(uId) {}
+ bool attach;
+ QUuid id;
+};
+Q_DECLARE_METATYPE(USBTarget);
+
+/** Describes enumerated webcam item. */
+struct WebCamTarget
+{
+ WebCamTarget() : attach(false), name(QString()), path(QString()) {}
+ WebCamTarget(bool fAttach, const QString &strName, const QString &strPath)
+ : attach(fAttach), name(strName), path(strPath) {}
+ bool attach;
+ QString name;
+ QString path;
+};
+Q_DECLARE_METATYPE(WebCamTarget);
+
+/* static */
+UIMachineLogic* UIMachineLogic::create(QObject *pParent,
+ UISession *pSession,
+ UIVisualStateType visualStateType)
+{
+ UIMachineLogic *pLogic = 0;
+ switch (visualStateType)
+ {
+ case UIVisualStateType_Normal:
+ pLogic = new UIMachineLogicNormal(pParent, pSession);
+ break;
+ case UIVisualStateType_Fullscreen:
+ pLogic = new UIMachineLogicFullscreen(pParent, pSession);
+ break;
+ case UIVisualStateType_Seamless:
+ pLogic = new UIMachineLogicSeamless(pParent, pSession);
+ break;
+ case UIVisualStateType_Scale:
+ pLogic = new UIMachineLogicScale(pParent, pSession);
+ break;
+
+ case UIVisualStateType_Invalid: case UIVisualStateType_All: break; /* Shut up, MSC! */
+ }
+ return pLogic;
+}
+
+/* static */
+void UIMachineLogic::destroy(UIMachineLogic *pWhichLogic)
+{
+ delete pWhichLogic;
+}
+
+void UIMachineLogic::prepare()
+{
+ /* Prepare required features: */
+ prepareRequiredFeatures();
+
+ /* Prepare session connections: */
+ prepareSessionConnections();
+
+ /* Prepare action groups:
+ * Note: This has to be done before prepareActionConnections
+ * cause here actions/menus are recreated. */
+ prepareActionGroups();
+ /* Prepare action connections: */
+ prepareActionConnections();
+
+ /* Prepare other connections: */
+ prepareOtherConnections();
+
+ /* Prepare handlers: */
+ prepareHandlers();
+
+ /* Prepare menu: */
+ prepareMenu();
+
+ /* Prepare machine-window(s): */
+ prepareMachineWindows();
+
+#ifdef VBOX_WS_MAC
+ /* Prepare dock: */
+ prepareDock();
+#endif /* VBOX_WS_MAC */
+
+#if 0 /* To early! The debugger needs a VM handle to work. So, must be done after power on. Moved to initializePostPowerUp. */
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Prepare debugger: */
+ prepareDebugger();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#endif
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Retranslate logic part: */
+ retranslateUi();
+}
+
+void UIMachineLogic::cleanup()
+{
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Cleanup debugger: */
+ cleanupDebugger();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+ /* Cleanup dock: */
+ cleanupDock();
+#endif /* VBOX_WS_MAC */
+
+ /* Cleanup menu: */
+ cleanupMenu();
+
+ /* Cleanup machine-window(s): */
+ cleanupMachineWindows();
+
+ /* Cleanup handlers: */
+ cleanupHandlers();
+
+ /* Cleanup action connections: */
+ cleanupActionConnections();
+ /* Cleanup action groups: */
+ cleanupActionGroups();
+
+ /* Cleanup session connections: */
+ cleanupSessionConnections();
+}
+
+void UIMachineLogic::initializePostPowerUp()
+{
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ prepareDebugger();
+#endif
+ sltMachineStateChanged();
+ sltAdditionsStateChanged();
+ sltMouseCapabilityChanged();
+}
+
+UIActionPool* UIMachineLogic::actionPool() const
+{
+ return uisession()->actionPool();
+}
+
+CSession& UIMachineLogic::session() const
+{
+ return uisession()->session();
+}
+
+CMachine& UIMachineLogic::machine() const
+{
+ return uisession()->machine();
+}
+
+CConsole& UIMachineLogic::console() const
+{
+ return uisession()->console();
+}
+
+CDisplay& UIMachineLogic::display() const
+{
+ return uisession()->display();
+}
+
+CGuest& UIMachineLogic::guest() const
+{
+ return uisession()->guest();
+}
+
+CMouse& UIMachineLogic::mouse() const
+{
+ return uisession()->mouse();
+}
+
+CKeyboard& UIMachineLogic::keyboard() const
+{
+ return uisession()->keyboard();
+}
+
+CMachineDebugger& UIMachineLogic::debugger() const
+{
+ return uisession()->debugger();
+}
+
+const QString& UIMachineLogic::machineName() const
+{
+ return uisession()->machineName();
+}
+
+UIMachineWindow* UIMachineLogic::mainMachineWindow() const
+{
+ /* Null if machine-window(s) not yet created: */
+ if (!isMachineWindowsCreated())
+ return 0;
+
+ /* First machine-window by default: */
+ return machineWindows().value(0);
+}
+
+UIMachineWindow* UIMachineLogic::activeMachineWindow() const
+{
+ /* Null if machine-window(s) not yet created: */
+ if (!isMachineWindowsCreated())
+ return 0;
+
+ /* Check if there is an active window present: */
+ for (int i = 0; i < machineWindows().size(); ++i)
+ {
+ UIMachineWindow *pIteratedWindow = machineWindows()[i];
+ if (pIteratedWindow->isActiveWindow())
+ return pIteratedWindow;
+ }
+
+ /* Main machine-window by default: */
+ return mainMachineWindow();
+}
+
+void UIMachineLogic::adjustMachineWindowsGeometry()
+{
+ /* By default, the only thing we need is to
+ * adjust machine-view size(s) if necessary: */
+ foreach(UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->adjustMachineViewSize();
+}
+
+void UIMachineLogic::sendMachineWindowsSizeHints()
+{
+ /* By default, the only thing we need is to
+ * send machine-view(s) size-hint(s) to the guest: */
+ foreach(UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->sendMachineViewSizeHint();
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineLogic::updateDockIcon()
+{
+ if (!isMachineWindowsCreated())
+ return;
+
+ if ( m_fIsDockIconEnabled
+ && m_pDockIconPreview)
+ if(UIMachineView *pView = machineWindows().at(m_DockIconPreviewMonitor)->machineView())
+ if (CGImageRef image = pView->vmContentImage())
+ {
+ m_pDockIconPreview->updateDockPreview(image);
+ CGImageRelease(image);
+ }
+}
+
+void UIMachineLogic::updateDockIconSize(int screenId, int width, int height)
+{
+ if (!isMachineWindowsCreated())
+ return;
+
+ if ( m_fIsDockIconEnabled
+ && m_pDockIconPreview
+ && m_DockIconPreviewMonitor == screenId)
+ m_pDockIconPreview->setOriginalSize(width, height);
+}
+
+UIMachineView* UIMachineLogic::dockPreviewView() const
+{
+ if ( m_fIsDockIconEnabled
+ && m_pDockIconPreview)
+ return machineWindows().at(m_DockIconPreviewMonitor)->machineView();
+ return 0;
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineLogic::sltHandleVBoxSVCAvailabilityChange()
+{
+ /* Do nothing if VBoxSVC still availabile: */
+ if (uiCommon().isVBoxSVCAvailable())
+ return;
+
+ /* Warn user about that: */
+ msgCenter().warnAboutVBoxSVCUnavailable();
+
+ /* Power VM off: */
+ LogRel(("GUI: Request to power VM off due to VBoxSVC is unavailable.\n"));
+ uisession()->powerOff(false /* do NOT restore current snapshot */);
+}
+
+void UIMachineLogic::sltChangeVisualStateToNormal()
+{
+ uisession()->setRequestedVisualState(UIVisualStateType_Invalid);
+ uisession()->changeVisualState(UIVisualStateType_Normal);
+}
+
+void UIMachineLogic::sltChangeVisualStateToFullscreen()
+{
+ uisession()->setRequestedVisualState(UIVisualStateType_Invalid);
+ uisession()->changeVisualState(UIVisualStateType_Fullscreen);
+}
+
+void UIMachineLogic::sltChangeVisualStateToSeamless()
+{
+ uisession()->setRequestedVisualState(UIVisualStateType_Invalid);
+ uisession()->changeVisualState(UIVisualStateType_Seamless);
+}
+
+void UIMachineLogic::sltChangeVisualStateToScale()
+{
+ uisession()->setRequestedVisualState(UIVisualStateType_Invalid);
+ uisession()->changeVisualState(UIVisualStateType_Scale);
+}
+
+void UIMachineLogic::sltMachineStateChanged()
+{
+ /* Get machine state: */
+ KMachineState state = uisession()->machineState();
+
+ /* Update action groups: */
+ m_pRunningActions->setEnabled(uisession()->isRunning());
+ m_pRunningOrPausedActions->setEnabled(uisession()->isRunning() || uisession()->isPaused());
+ m_pRunningOrPausedOrStackedActions->setEnabled(uisession()->isRunning() || uisession()->isPaused() || uisession()->isStuck());
+
+ switch (state)
+ {
+ case KMachineState_Stuck:
+ {
+ /* Prevent machine-view from resizing: */
+ uisession()->setGuestResizeIgnored(true);
+ /* Get log-folder: */
+ QString strLogFolder = machine().GetLogFolder();
+ /* Take the screenshot for debugging purposes: */
+ takeScreenshot(strLogFolder + "/VBox.png", "png");
+ /* How should we handle Guru Meditation? */
+ switch (gEDataManager->guruMeditationHandlerType(uiCommon().managedVMUuid()))
+ {
+ /* Ask how to proceed; Power off VM if proposal accepted: */
+ case GuruMeditationHandlerType_Default:
+ {
+ if (msgCenter().warnAboutGuruMeditation(QDir::toNativeSeparators(strLogFolder)))
+ {
+ LogRel(("GUI: User requested to power VM off on Guru Meditation.\n"));
+ uisession()->powerOff(false /* do NOT restore current snapshot */);
+ }
+ break;
+ }
+ /* Power off VM silently: */
+ case GuruMeditationHandlerType_PowerOff:
+ {
+ LogRel(("GUI: Automatic request to power VM off on Guru Meditation.\n"));
+ uisession()->powerOff(false /* do NOT restore current snapshot */);
+ break;
+ }
+ /* Just ignore it: */
+ case GuruMeditationHandlerType_Ignore:
+ default:
+ break;
+ }
+ break;
+ }
+ case KMachineState_Paused:
+ case KMachineState_TeleportingPausedVM:
+ {
+ QAction *pPauseAction = actionPool()->action(UIActionIndexRT_M_Machine_T_Pause);
+ if (!pPauseAction->isChecked())
+ {
+ /* Was paused from CSession side: */
+ pPauseAction->blockSignals(true);
+ pPauseAction->setChecked(true);
+ pPauseAction->blockSignals(false);
+ }
+ break;
+ }
+ case KMachineState_Running:
+ case KMachineState_Teleporting:
+ case KMachineState_LiveSnapshotting:
+ {
+ QAction *pPauseAction = actionPool()->action(UIActionIndexRT_M_Machine_T_Pause);
+ if (pPauseAction->isChecked())
+ {
+ /* Was resumed from CSession side: */
+ pPauseAction->blockSignals(true);
+ pPauseAction->setChecked(false);
+ pPauseAction->blockSignals(false);
+ }
+ break;
+ }
+ case KMachineState_PoweredOff:
+ case KMachineState_Saved:
+ case KMachineState_Teleported:
+ case KMachineState_Aborted:
+ case KMachineState_AbortedSaved:
+ {
+ /* If not in 'manual-override' mode: */
+ if (!uisession()->isManualOverrideMode())
+ {
+ /* VM has been powered off, saved, teleported or aborted.
+ * We must close Runtime UI: */
+ if (uiCommon().isSeparateProcess())
+ {
+ /* Hack: The VM process is terminating, so wait a bit to make sure that
+ * the session is unlocked and the GUI process can save extradata
+ * in UIMachine::cleanupMachineLogic.
+ */
+ /** @todo Probably should wait for the session state change event. */
+ KSessionState sessionState = uisession()->session().GetState();
+ int c = 0;
+ while ( sessionState == KSessionState_Locked
+ || sessionState == KSessionState_Unlocking)
+ {
+ if (++c > 50) break;
+
+ RTThreadSleep(100);
+ sessionState = uisession()->session().GetState();
+ }
+ }
+
+ LogRel(("GUI: Request to close Runtime UI because VM is powered off already.\n"));
+ uisession()->closeRuntimeUI();
+ return;
+ }
+ break;
+ }
+ case KMachineState_Saving:
+ {
+ /* Insert a host combo release if press has been inserted: */
+ typeHostKeyComboPressRelease(false);
+ break;
+ }
+#ifdef VBOX_WS_X11
+ case KMachineState_Starting:
+ case KMachineState_Restoring:
+ case KMachineState_TeleportingIn:
+ {
+ /* The keyboard handler may wish to do some release logging on startup.
+ * Tell it that the logger is now active. */
+ doXKeyboardLogging(NativeWindowSubsystem::X11GetDisplay());
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+
+#ifdef VBOX_WS_MAC
+ /* Update Dock Overlay: */
+ updateDockOverlay();
+#endif /* VBOX_WS_MAC */
+}
+
+void UIMachineLogic::sltAdditionsStateChanged()
+{
+ /* Update action states: */
+ LogRel3(("GUI: UIMachineLogic::sltAdditionsStateChanged: Adjusting actions availability according to GA state.\n"));
+ actionPool()->action(UIActionIndexRT_M_View_T_Seamless)->setEnabled(uisession()->isVisualStateAllowed(UIVisualStateType_Seamless) &&
+ uisession()->isGuestSupportsSeamless());
+}
+
+void UIMachineLogic::sltMouseCapabilityChanged()
+{
+ /* Variable falgs: */
+ bool fIsMouseSupportsAbsolute = uisession()->isMouseSupportsAbsolute();
+ bool fIsMouseSupportsRelative = uisession()->isMouseSupportsRelative();
+ bool fIsMouseSupportsTouchScreen = uisession()->isMouseSupportsTouchScreen();
+ bool fIsMouseSupportsTouchPad = uisession()->isMouseSupportsTouchPad();
+ bool fIsMouseHostCursorNeeded = uisession()->isMouseHostCursorNeeded();
+
+ /* For now MT stuff is not important for MI action: */
+ Q_UNUSED(fIsMouseSupportsTouchScreen);
+ Q_UNUSED(fIsMouseSupportsTouchPad);
+
+ /* Update action state: */
+ QAction *pAction = actionPool()->action(UIActionIndexRT_M_Input_M_Mouse_T_Integration);
+ pAction->setEnabled(fIsMouseSupportsAbsolute && fIsMouseSupportsRelative && !fIsMouseHostCursorNeeded);
+ if (fIsMouseHostCursorNeeded)
+ pAction->setChecked(true);
+}
+
+void UIMachineLogic::sltHidLedsSyncStateChanged(bool fEnabled)
+{
+ m_fIsHidLedsSyncEnabled = fEnabled;
+}
+
+void UIMachineLogic::sltDisableHostScreenSaverStateChanged(bool fDisabled)
+{
+#if defined(VBOX_WS_X11)
+ /* Find the methods once and cache them: */
+ if (m_methods.isEmpty())
+ m_methods = NativeWindowSubsystem::X11FindDBusScrenSaverInhibitMethods();
+ NativeWindowSubsystem::X11InhibitUninhibitScrenSaver(fDisabled, m_methods);
+#elif defined(VBOX_WS_WIN)
+ NativeWindowSubsystem::setScreenSaverActive(fDisabled);
+#else
+ Q_UNUSED(fDisabled);
+#endif
+}
+
+void UIMachineLogic::sltKeyboardLedsChanged()
+{
+ /* Here we have to update host LED lock states using values provided by UISession:
+ * [bool] uisession() -> isNumLock(), isCapsLock(), isScrollLock() can be used for that. */
+
+ if (!isHidLedsSyncEnabled())
+ return;
+
+ /* Check if we accidentally trying to manipulate LEDs when host LEDs state was deallocated. */
+ if (!m_pHostLedsState)
+ return;
+
+#if defined(VBOX_WS_MAC)
+ DarwinHidDevicesBroadcastLeds(m_pHostLedsState, uisession()->isNumLock(), uisession()->isCapsLock(), uisession()->isScrollLock());
+#elif defined(VBOX_WS_WIN)
+ if (!winHidLedsInSync(uisession()->isNumLock(), uisession()->isCapsLock(), uisession()->isScrollLock()))
+ {
+ keyboardHandler()->winSkipKeyboardEvents(true);
+ WinHidDevicesBroadcastLeds(uisession()->isNumLock(), uisession()->isCapsLock(), uisession()->isScrollLock());
+ keyboardHandler()->winSkipKeyboardEvents(false);
+ }
+ else
+ LogRel2(("GUI: HID LEDs Sync: already in sync\n"));
+#else
+ LogRelFlow(("UIMachineLogic::sltKeyboardLedsChanged: Updating host LED lock states does not supported on this platform.\n"));
+#endif
+}
+
+void UIMachineLogic::sltUSBDeviceStateChange(const CUSBDevice &device, bool fIsAttached, const CVirtualBoxErrorInfo &error)
+{
+ /* Check if USB device have anything to tell us: */
+ if (!error.isNull())
+ {
+ if (fIsAttached)
+ UINotificationMessage::cannotAttachUSBDevice(error, uiCommon().usbDetails(device), machineName());
+ else
+ UINotificationMessage::cannotDetachUSBDevice(error, uiCommon().usbDetails(device), machineName());
+ }
+}
+
+void UIMachineLogic::sltRuntimeError(bool fIsFatal, const QString &strErrorId, const QString &strMessage)
+{
+ /* Preprocess known runtime error types: */
+ if (strErrorId == "DrvVD_DEKMISSING")
+ return askUserForTheDiskEncryptionPasswords();
+ else if (strErrorId == "VMBootFail")
+ {
+ if (!gEDataManager->suppressedMessages().contains(gpConverter->toInternalString(UIExtraDataMetaDefs::DialogType_BootFailure)))
+ return showBootFailureDialog();
+ else
+ return;
+ }
+
+ /* Show runtime error: */
+ msgCenter().showRuntimeError(console(), fIsFatal, strErrorId, strMessage);
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineLogic::sltShowWindows()
+{
+ for (int i=0; i < machineWindows().size(); ++i)
+ {
+ UIMachineWindow *pMachineWindow = machineWindows().at(i);
+ /* Dunno what Qt thinks a window that has minimized to the dock
+ * should be - it is not hidden, neither is it minimized. OTOH it is
+ * marked shown and visible, but not activated. This latter isn't of
+ * much help though, since at this point nothing is marked activated.
+ * I might have overlooked something, but I'm buggered what if I know
+ * what. So, I'll just always show & activate the stupid window to
+ * make it get out of the dock when the user wishes to show a VM. */
+ pMachineWindow->raise();
+ pMachineWindow->activateWindow();
+ }
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineLogic::sltGuestMonitorChange(KGuestMonitorChangedEventType, ulong, QRect)
+{
+ LogRel(("GUI: UIMachineLogic: Guest-screen count changed\n"));
+
+ /* Make sure all machine-window(s) have proper geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->showInNecessaryMode();
+
+#ifdef VBOX_WS_MAC
+ /* Update dock: */
+ updateDock();
+#endif
+}
+
+void UIMachineLogic::sltHostScreenCountChange()
+{
+#ifdef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ /* Customer request to skip host-screen count change: */
+ LogRel(("GUI: UIMachineLogic: Host-screen count change skipped\n"));
+#else /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+ LogRel(("GUI: UIMachineLogic: Host-screen count changed\n"));
+
+ /* Make sure all machine-window(s) have proper geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->showInNecessaryMode();
+#endif /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+}
+
+void UIMachineLogic::sltHostScreenGeometryChange()
+{
+#ifdef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ /* Customer request to skip host-screen geometry change: */
+ LogRel(("GUI: UIMachineLogic: Host-screen geometry change skipped\n"));
+#else /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+ LogRel(("GUI: UIMachineLogic: Host-screen geometry changed\n"));
+
+ /* Make sure all machine-window(s) have proper geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->showInNecessaryMode();
+#endif /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+}
+
+void UIMachineLogic::sltHostScreenAvailableAreaChange()
+{
+#ifdef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ /* Customer request to skip host-screen available-area change: */
+ LogRel(("GUI: UIMachineLogic: Host-screen available-area change skipped\n"));
+#else /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+ LogRel(("GUI: UIMachineLogic: Host-screen available-area changed\n"));
+
+ /* Make sure all machine-window(s) have proper geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->showInNecessaryMode();
+#endif /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+}
+
+UIMachineLogic::UIMachineLogic(QObject *pParent, UISession *pSession, UIVisualStateType visualStateType)
+ : QIWithRetranslateUI3<QObject>(pParent)
+ , m_pSession(pSession)
+ , m_visualStateType(visualStateType)
+ , m_pKeyboardHandler(0)
+ , m_pMouseHandler(0)
+ , m_pRunningActions(0)
+ , m_pRunningOrPausedActions(0)
+ , m_pRunningOrPausedOrStackedActions(0)
+ , m_pSharedClipboardActions(0)
+ , m_pDragAndDropActions(0)
+ , m_fIsWindowsCreated(false)
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ , m_pDbgGui(0)
+ , m_pDbgGuiVT(0)
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ , m_fIsDockIconEnabled(true)
+ , m_pDockIconPreview(0)
+ , m_pDockPreviewSelectMonitorGroup(0)
+ , m_pDockSettingsMenuSeparator(0)
+ , m_DockIconPreviewMonitor(0)
+ , m_pDockSettingMenuAction(0)
+#endif /* VBOX_WS_MAC */
+ , m_pHostLedsState(NULL)
+ , m_fIsHidLedsSyncEnabled(false)
+ , m_pLogViewerDialog(0)
+ , m_pFileManagerDialog(0)
+ , m_pProcessControlDialog(0)
+ , m_pSoftKeyboardDialog(0)
+ , m_pVMInformationDialog(0)
+{
+}
+
+UIMachineLogic::~UIMachineLogic()
+{
+#if defined(VBOX_WS_X11)
+ qDeleteAll(m_methods.begin(), m_methods.end());
+ m_methods.clear();
+#endif
+}
+
+void UIMachineLogic::addMachineWindow(UIMachineWindow *pMachineWindow)
+{
+ m_machineWindowsList << pMachineWindow;
+}
+
+void UIMachineLogic::setKeyboardHandler(UIKeyboardHandler *pKeyboardHandler)
+{
+ /* Set new handler: */
+ m_pKeyboardHandler = pKeyboardHandler;
+ /* Connect to session: */
+ connect(m_pKeyboardHandler, &UIKeyboardHandler::sigStateChange,
+ uisession(), &UISession::setKeyboardState);
+}
+
+void UIMachineLogic::setMouseHandler(UIMouseHandler *pMouseHandler)
+{
+ /* Set new handler: */
+ m_pMouseHandler = pMouseHandler;
+ /* Connect to session: */
+ connect(m_pMouseHandler, &UIMouseHandler::sigStateChange,
+ uisession(), &UISession::setMouseState);
+}
+
+void UIMachineLogic::retranslateUi()
+{
+#ifdef VBOX_WS_MAC
+ if (m_pDockPreviewSelectMonitorGroup)
+ {
+ const QList<QAction*> &actions = m_pDockPreviewSelectMonitorGroup->actions();
+ for (int i = 0; i < actions.size(); ++i)
+ {
+ QAction *pAction = actions.at(i);
+ pAction->setText(QApplication::translate("UIActionPool", "Preview Monitor %1").arg(pAction->data().toInt() + 1));
+ }
+ }
+#endif /* VBOX_WS_MAC */
+ /* Shared Clipboard actions: */
+ if (m_pSharedClipboardActions)
+ {
+ foreach (QAction *pAction, m_pSharedClipboardActions->actions())
+ pAction->setText(gpConverter->toString(pAction->data().value<KClipboardMode>()));
+ }
+ if (m_pDragAndDropActions)
+ {
+ foreach (QAction *pAction, m_pDragAndDropActions->actions())
+ pAction->setText(gpConverter->toString(pAction->data().value<KDnDMode>()));
+ }
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineLogic::updateDockOverlay()
+{
+ /* Only to an update to the realtime preview if this is enabled by the user
+ * & we are in an state where the framebuffer is likely valid. Otherwise to
+ * the overlay stuff only. */
+ KMachineState state = uisession()->machineState();
+ if (m_fIsDockIconEnabled &&
+ (state == KMachineState_Running ||
+ state == KMachineState_Paused ||
+ state == KMachineState_Teleporting ||
+ state == KMachineState_LiveSnapshotting ||
+ state == KMachineState_Restoring ||
+ state == KMachineState_TeleportingPausedVM ||
+ state == KMachineState_TeleportingIn ||
+ state == KMachineState_Saving ||
+ state == KMachineState_DeletingSnapshotOnline ||
+ state == KMachineState_DeletingSnapshotPaused))
+ updateDockIcon();
+ else if (m_pDockIconPreview)
+ m_pDockIconPreview->updateDockOverlay();
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineLogic::prepareSessionConnections()
+{
+ /* We should watch for VBoxSVC availability changes: */
+ connect(&uiCommon(), &UICommon::sigVBoxSVCAvailabilityChange,
+ this, &UIMachineLogic::sltHandleVBoxSVCAvailabilityChange);
+
+ /* We should watch for requested modes: */
+ connect(uisession(), &UISession::sigInitialized, this, &UIMachineLogic::sltCheckForRequestedVisualStateType, Qt::QueuedConnection);
+ connect(uisession(), &UISession::sigAdditionsStateChange, this, &UIMachineLogic::sltCheckForRequestedVisualStateType);
+
+ /* We should watch for console events: */
+ connect(uisession(), &UISession::sigMachineStateChange, this, &UIMachineLogic::sltMachineStateChanged);
+ connect(uisession(), &UISession::sigAdditionsStateActualChange, this, &UIMachineLogic::sltAdditionsStateChanged);
+ connect(uisession(), &UISession::sigMouseCapabilityChange, this, &UIMachineLogic::sltMouseCapabilityChanged);
+ connect(uisession(), &UISession::sigKeyboardLedsChange, this, &UIMachineLogic::sltKeyboardLedsChanged);
+ connect(uisession(), &UISession::sigUSBDeviceStateChange, this, &UIMachineLogic::sltUSBDeviceStateChange);
+ connect(uisession(), &UISession::sigRuntimeError, this, &UIMachineLogic::sltRuntimeError);
+#ifdef VBOX_WS_MAC
+ connect(uisession(), &UISession::sigShowWindows, this, &UIMachineLogic::sltShowWindows);
+#endif /* VBOX_WS_MAC */
+ connect(uisession(), &UISession::sigGuestMonitorChange, this, &UIMachineLogic::sltGuestMonitorChange);
+
+ /* We should watch for host-screen-change events: */
+ connect(uisession(), &UISession::sigHostScreenCountChange, this, &UIMachineLogic::sltHostScreenCountChange);
+ connect(uisession(), &UISession::sigHostScreenGeometryChange, this, &UIMachineLogic::sltHostScreenGeometryChange);
+ connect(uisession(), &UISession::sigHostScreenAvailableAreaChange, this, &UIMachineLogic::sltHostScreenAvailableAreaChange);
+
+ /* We should notify about frame-buffer events: */
+ connect(this, &UIMachineLogic::sigFrameBufferResize, uisession(), &UISession::sigFrameBufferResize);
+}
+
+void UIMachineLogic::prepareActionGroups()
+{
+ /* Create group for all actions that are enabled only when the VM is running.
+ * Note that only actions whose enabled state depends exclusively on the
+ * execution state of the VM are added to this group. */
+ m_pRunningActions = new QActionGroup(this);
+ m_pRunningActions->setExclusive(false);
+
+ /* Create group for all actions that are enabled when the VM is running or paused.
+ * Note that only actions whose enabled state depends exclusively on the
+ * execution state of the VM are added to this group. */
+ m_pRunningOrPausedActions = new QActionGroup(this);
+ m_pRunningOrPausedActions->setExclusive(false);
+
+ /* Create group for all actions that are enabled when the VM is running or paused or stucked.
+ * Note that only actions whose enabled state depends exclusively on the
+ * execution state of the VM are added to this group. */
+ m_pRunningOrPausedOrStackedActions = new QActionGroup(this);
+ m_pRunningOrPausedOrStackedActions->setExclusive(false);
+
+ /* Move actions into running actions group: */
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_Machine_S_Reset));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_Machine_S_Shutdown));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_View_T_Seamless));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_View_T_Scale));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_View_T_GuestAutoresize));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeCAD));
+#ifdef VBOX_WS_X11
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeCABS));
+#endif /* VBOX_WS_X11 */
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeCtrlBreak));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeInsert));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypePrintScreen));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeAltPrintScreen));
+ m_pRunningActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_T_TypeHostKeyCombo));
+
+ /* Move actions into running-n-paused actions group: */
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Machine_S_Detach));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Machine_S_SaveState));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Machine_S_Settings));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Machine_S_TakeSnapshot));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Machine_S_ShowInformation));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Machine_T_Pause));
+#ifndef VBOX_WS_MAC
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_S_MinimizeWindow));
+#endif /* !VBOX_WS_MAC */
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_S_AdjustWindow));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_S_TakeScreenshot));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_M_Recording));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_M_Recording_S_Settings));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_M_Recording_T_Start));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_T_VRDEServer));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_M_MenuBar));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_S_Settings));
+#ifndef VBOX_WS_MAC
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility));
+#endif /* !VBOX_WS_MAC */
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_M_StatusBar));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_S_Settings));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_Settings));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_SoftKeyboard));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Mouse));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Input_M_Mouse_T_Integration));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_HardDrives));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_HardDrives_S_Settings));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_OpticalDevices));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_FloppyDevices));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_Audio));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Output));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Output));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_Network));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_Network_S_Settings));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_USBDevices));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_USBDevices_S_Settings));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_WebCams));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_SharedClipboard));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_DragAndDrop));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_SharedFolders));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_M_SharedFolders_S_Settings));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_S_InsertGuestAdditionsDisk));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions));
+#ifdef VBOX_WS_MAC
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndex_M_Window));
+ m_pRunningOrPausedActions->addAction(actionPool()->action(UIActionIndex_M_Window_S_Minimize));
+#endif /* VBOX_WS_MAC */
+
+ /* Move actions into running-n-paused-n-stucked actions group: */
+ m_pRunningOrPausedOrStackedActions->addAction(actionPool()->action(UIActionIndexRT_M_Machine_S_PowerOff));
+}
+
+void UIMachineLogic::prepareActionConnections()
+{
+ /* 'Application' actions connection: */
+ connect(actionPool()->action(UIActionIndex_M_Application_S_Preferences), &UIAction::triggered,
+ this, &UIMachineLogic::sltOpenPreferencesDialogDefault, Qt::UniqueConnection);
+ connect(actionPool()->action(UIActionIndex_M_Application_S_Close), &UIAction::triggered,
+ this, &UIMachineLogic::sltClose, Qt::QueuedConnection);
+
+ /* 'Machine' actions connections: */
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogic::sltOpenSettingsDialogDefault);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_TakeSnapshot), &UIAction::triggered,
+ this, &UIMachineLogic::sltTakeSnapshot);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_ShowInformation), &UIAction::triggered,
+ this, &UIMachineLogic::sltShowInformationDialog);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_ShowFileManager), &UIAction::triggered,
+ this, &UIMachineLogic::sltShowFileManagerDialog);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_T_Pause), &UIAction::toggled,
+ this, &UIMachineLogic::sltPause);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_Reset), &UIAction::triggered,
+ this, &UIMachineLogic::sltReset);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_Detach), &UIAction::triggered,
+ this, &UIMachineLogic::sltDetach, Qt::QueuedConnection);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_SaveState), &UIAction::triggered,
+ this, &UIMachineLogic::sltSaveState, Qt::QueuedConnection);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_Shutdown), &UIAction::triggered,
+ this, &UIMachineLogic::sltShutdown);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_PowerOff), &UIAction::triggered,
+ this, &UIMachineLogic::sltPowerOff, Qt::QueuedConnection);
+ connect(actionPool()->action(UIActionIndexRT_M_Machine_S_ShowLogDialog), &UIAction::triggered,
+ this, &UIMachineLogic::sltShowLogDialog);
+
+ /* 'View' actions connections: */
+#ifndef VBOX_WS_MAC
+ connect(actionPool()->action(UIActionIndexRT_M_View_S_MinimizeWindow), &UIAction::triggered,
+ this, &UIMachineLogic::sltMinimizeActiveMachineWindow, Qt::QueuedConnection);
+#endif /* !VBOX_WS_MAC */
+ connect(actionPool()->action(UIActionIndexRT_M_View_S_AdjustWindow), &UIAction::triggered,
+ this, &UIMachineLogic::sltAdjustMachineWindows);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_GuestAutoresize), &UIAction::toggled,
+ this, &UIMachineLogic::sltToggleGuestAutoresize);
+ connect(actionPool()->action(UIActionIndexRT_M_View_S_TakeScreenshot), &UIAction::triggered,
+ this, &UIMachineLogic::sltTakeScreenshot);
+ connect(actionPool()->action(UIActionIndexRT_M_View_M_Recording_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogic::sltOpenRecordingOptions);
+ connect(actionPool()->action(UIActionIndexRT_M_View_M_Recording_T_Start), &UIAction::toggled,
+ this, &UIMachineLogic::sltToggleRecording);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_VRDEServer), &UIAction::toggled,
+ this, &UIMachineLogic::sltToggleVRDE);
+
+ /* 'Input' actions connections: */
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogic::sltShowKeyboardSettings);
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_SoftKeyboard), &UIAction::triggered,
+ this, &UIMachineLogic::sltShowSoftKeyboard);
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeCAD), &UIAction::triggered,
+ this, &UIMachineLogic::sltTypeCAD);
+#ifdef VBOX_WS_X11
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeCABS), &UIAction::triggered,
+ this, &UIMachineLogic::sltTypeCABS);
+#endif /* VBOX_WS_X11 */
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeCtrlBreak), &UIAction::triggered,
+ this, &UIMachineLogic::sltTypeCtrlBreak);
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeInsert), &UIAction::triggered,
+ this, &UIMachineLogic::sltTypeInsert);
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypePrintScreen), &UIAction::triggered,
+ this, &UIMachineLogic::sltTypePrintScreen);
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_S_TypeAltPrintScreen), &UIAction::triggered,
+ this, &UIMachineLogic::sltTypeAltPrintScreen);
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_T_TypeHostKeyCombo), &UIAction::toggled,
+ this, &UIMachineLogic::sltTypeHostKeyComboPressRelease);
+ connect(actionPool()->action(UIActionIndexRT_M_Input_M_Mouse_T_Integration), &UIAction::toggled,
+ this, &UIMachineLogic::sltToggleMouseIntegration);
+
+ /* 'Devices' actions connections: */
+ connect(actionPool(), &UIActionPool::sigNotifyAboutMenuPrepare, this, &UIMachineLogic::sltHandleMenuPrepare);
+ connect(actionPool()->action(UIActionIndexRT_M_Devices_M_HardDrives_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogic::sltOpenSettingsDialogStorage);
+ connect(actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Output), &UIAction::toggled,
+ this, &UIMachineLogic::sltToggleAudioOutput);
+ connect(actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Input), &UIAction::toggled,
+ this, &UIMachineLogic::sltToggleAudioInput);
+ connect(actionPool()->action(UIActionIndexRT_M_Devices_M_Network_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogic::sltOpenSettingsDialogNetwork);
+ connect(actionPool()->action(UIActionIndexRT_M_Devices_M_USBDevices_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogic::sltOpenSettingsDialogUSBDevices);
+ connect(actionPool()->action(UIActionIndexRT_M_Devices_M_SharedFolders_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogic::sltOpenSettingsDialogSharedFolders);
+ connect(actionPool()->action(UIActionIndexRT_M_Devices_S_InsertGuestAdditionsDisk), &UIAction::triggered,
+ this, &UIMachineLogic::sltInstallGuestAdditions);
+ connect(actionPool()->action(UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions), &UIAction::triggered,
+ this, &UIMachineLogic::sltInstallGuestAdditions);
+
+ /* 'Help' menu 'Contents' action. Done here since we react differently to this action
+ * in manager and runtime UI: */
+ connect(actionPool()->action(UIActionIndex_Simple_Contents), &UIAction::triggered,
+ &msgCenter(), &UIMessageCenter::sltShowHelpHelpDialog);
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* 'Debug' actions connections: */
+ connect(actionPool()->action(UIActionIndexRT_M_Debug_S_ShowStatistics), &UIAction::triggered,
+ this, &UIMachineLogic::sltShowDebugStatistics);
+ connect(actionPool()->action(UIActionIndexRT_M_Debug_S_ShowCommandLine), &UIAction::triggered,
+ this, &UIMachineLogic::sltShowDebugCommandLine);
+ connect(actionPool()->action(UIActionIndexRT_M_Debug_T_Logging), &UIAction::toggled,
+ this, &UIMachineLogic::sltLoggingToggled);
+ connect(actionPool()->action(UIActionIndexRT_M_Debug_S_GuestControlConsole), &UIAction::triggered,
+ this, &UIMachineLogic::sltShowGuestControlConsoleDialog);
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+ /* 'Window' action connections: */
+ connect(actionPool()->action(UIActionIndex_M_Window_S_Minimize), &UIAction::triggered,
+ this, &UIMachineLogic::sltMinimizeActiveMachineWindow, Qt::QueuedConnection);
+#endif /* VBOX_WS_MAC */
+}
+
+void UIMachineLogic::prepareOtherConnections()
+{
+ /* Extra-data connections: */
+ connect(gEDataManager, &UIExtraDataManager::sigVisualStateChange,
+ this, &UIMachineLogic::sltHandleVisualStateChange);
+
+ /* UICommon connections: */
+ connect(&uiCommon(), &UICommon::sigAskToCommitData,
+ this, &UIMachineLogic::sltHandleCommitData);
+}
+
+void UIMachineLogic::prepareHandlers()
+{
+ /* Prepare menu update-handlers: */
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_OpticalDevices] = &UIMachineLogic::updateMenuDevicesStorage;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_FloppyDevices] = &UIMachineLogic::updateMenuDevicesStorage;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_Network] = &UIMachineLogic::updateMenuDevicesNetwork;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_USBDevices] = &UIMachineLogic::updateMenuDevicesUSB;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_WebCams] = &UIMachineLogic::updateMenuDevicesWebCams;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_SharedClipboard] = &UIMachineLogic::updateMenuDevicesSharedClipboard;
+ m_menuUpdateHandlers[UIActionIndexRT_M_Devices_M_DragAndDrop] = &UIMachineLogic::updateMenuDevicesDragAndDrop;
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ m_menuUpdateHandlers[UIActionIndexRT_M_Debug] = &UIMachineLogic::updateMenuDebug;
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ m_menuUpdateHandlers[UIActionIndex_M_Window] = &UIMachineLogic::updateMenuWindow;
+#endif /* VBOX_WS_MAC */
+
+ /* Create keyboard/mouse handlers: */
+ setKeyboardHandler(UIKeyboardHandler::create(this, visualStateType()));
+ setMouseHandler(UIMouseHandler::create(this, visualStateType()));
+ /* Update UI session values with current: */
+ uisession()->setKeyboardState(keyboardHandler()->state());
+ uisession()->setMouseState(mouseHandler()->state());
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineLogic::prepareDock()
+{
+ QMenu *pDockMenu = actionPool()->action(UIActionIndexRT_M_Dock)->menu();
+ /* Clear the menu to get rid of any previously added actions and separators: */
+ pDockMenu->clear();
+
+ /* Add all the 'Machine' menu entries to the 'Dock' menu: */
+ QList<QAction*> actions = actionPool()->action(UIActionIndexRT_M_Machine)->menu()->actions();
+ m_dockMachineMenuActions.clear();
+ for (int i=0; i < actions.size(); ++i)
+ {
+ /* Check if we really have correct action: */
+ UIAction *pAction = qobject_cast<UIAction*>(actions.at(i));
+ /* Skip incorrect actions: */
+ if (!pAction)
+ continue;
+ /* Skip actions which have 'role' (to prevent consuming): */
+ if (pAction->menuRole() != QAction::NoRole)
+ continue;
+ /* Skip actions which have menu (to prevent consuming): */
+ if (qobject_cast<UIActionMenu*>(pAction))
+ continue;
+ if (!pAction->isAllowed())
+ continue;
+ pDockMenu->addAction(actions.at(i));
+ m_dockMachineMenuActions.push_back(actions.at(i));
+ }
+ if (!m_dockMachineMenuActions.empty())
+ {
+ m_dockMachineMenuActions.push_back(pDockMenu->addSeparator());
+ }
+
+ QMenu *pDockSettingsMenu = actionPool()->action(UIActionIndexRT_M_Dock_M_DockSettings)->menu();
+ /* Clear the menu to get rid of any previously added actions and separators: */
+ pDockSettingsMenu->clear();
+ QActionGroup *pDockPreviewModeGroup = new QActionGroup(this);
+ QAction *pDockDisablePreview = actionPool()->action(UIActionIndexRT_M_Dock_M_DockSettings_T_DisableMonitor);
+ pDockPreviewModeGroup->addAction(pDockDisablePreview);
+ QAction *pDockEnablePreviewMonitor = actionPool()->action(UIActionIndexRT_M_Dock_M_DockSettings_T_PreviewMonitor);
+ pDockPreviewModeGroup->addAction(pDockEnablePreviewMonitor);
+ pDockSettingsMenu->addActions(pDockPreviewModeGroup->actions());
+
+ connect(pDockPreviewModeGroup, &QActionGroup::triggered, this, &UIMachineLogic::sltDockPreviewModeChanged);
+ connect(gEDataManager, &UIExtraDataManager::sigDockIconAppearanceChange, this, &UIMachineLogic::sltChangeDockIconUpdate);
+
+ /* Get dock icon disable overlay action: */
+ QAction *pDockIconDisableOverlay = actionPool()->action(UIActionIndexRT_M_Dock_M_DockSettings_T_DisableOverlay);
+ /* Prepare dock icon disable overlay action with initial data: */
+ pDockIconDisableOverlay->setChecked(gEDataManager->dockIconDisableOverlay(uiCommon().managedVMUuid()));
+ /* Connect dock icon disable overlay related signals: */
+ connect(pDockIconDisableOverlay, &QAction::triggered, this, &UIMachineLogic::sltDockIconDisableOverlayChanged);
+ connect(gEDataManager, &UIExtraDataManager::sigDockIconOverlayAppearanceChange,
+ this, &UIMachineLogic::sltChangeDockIconOverlayAppearance);
+ /* Add dock icon disable overlay action to the dock settings menu: */
+ pDockSettingsMenu->addAction(pDockIconDisableOverlay);
+
+ /* If we have more than one visible window: */
+ const QList<int> visibleWindowsList = uisession()->listOfVisibleWindows();
+ const int cVisibleGuestScreens = visibleWindowsList.size();
+ if (cVisibleGuestScreens > 1)
+ {
+ /* Add separator: */
+ m_pDockSettingsMenuSeparator = pDockSettingsMenu->addSeparator();
+
+ int extraDataUpdateMonitor = gEDataManager->realtimeDockIconUpdateMonitor(uiCommon().managedVMUuid());
+ if (visibleWindowsList.contains(extraDataUpdateMonitor))
+ m_DockIconPreviewMonitor = extraDataUpdateMonitor;
+ else
+ m_DockIconPreviewMonitor = visibleWindowsList.at(cVisibleGuestScreens - 1);
+
+ m_pDockPreviewSelectMonitorGroup = new QActionGroup(this);
+
+ /* And dock preview actions: */
+ for (int i = 0; i < cVisibleGuestScreens; ++i)
+ {
+ QAction *pAction = new QAction(m_pDockPreviewSelectMonitorGroup);
+ pAction->setCheckable(true);
+ pAction->setData(visibleWindowsList.at(i));
+ if (m_DockIconPreviewMonitor == visibleWindowsList.at(i))
+ pAction->setChecked(true);
+ }
+ pDockSettingsMenu->addActions(m_pDockPreviewSelectMonitorGroup->actions());
+ connect(m_pDockPreviewSelectMonitorGroup, &QActionGroup::triggered,
+ this, &UIMachineLogic::sltDockPreviewMonitorChanged);
+ }
+
+ m_pDockSettingMenuAction = pDockMenu->addMenu(pDockSettingsMenu);
+
+ /* Add it to the dock: */
+ pDockMenu->setAsDockMenu();
+
+ /* Now the dock icon preview: */
+ QPixmap pixmap = generalIconPool().userMachinePixmap(machine(), QSize(42, 42));
+ if (pixmap.isNull())
+ pixmap = generalIconPool().guestOSTypePixmap(guest().GetOSTypeId(), QSize(42, 42));
+ m_pDockIconPreview = new UIDockIconPreview(uisession(), pixmap);
+
+ /* Should the dock-icon be updated at runtime? */
+ bool fEnabled = gEDataManager->realtimeDockIconUpdateEnabled(uiCommon().managedVMUuid());
+ if (fEnabled)
+ pDockEnablePreviewMonitor->setChecked(true);
+ else
+ {
+ pDockDisablePreview->setChecked(true);
+ if(m_pDockPreviewSelectMonitorGroup)
+ m_pDockPreviewSelectMonitorGroup->setEnabled(false);
+ }
+ setDockIconPreviewEnabled(fEnabled);
+ updateDockOverlay();
+}
+
+void UIMachineLogic::updateDock()
+{
+ QMenu *pDockSettingsMenu = actionPool()->action(UIActionIndexRT_M_Dock_M_DockSettings)->menu();
+ AssertReturnVoid(pDockSettingsMenu);
+
+ QMenu *pDockMenu = actionPool()->action(UIActionIndexRT_M_Dock)->menu();
+ AssertReturnVoid(pDockMenu);
+
+ /* Clean previous machine menu actions: */
+ for (int i=0; i < m_dockMachineMenuActions.size(); ++i)
+ {
+ pDockMenu->removeAction(m_dockMachineMenuActions.at(i));
+ if (m_dockMachineMenuActions.at(i)->isSeparator())
+ delete m_dockMachineMenuActions[i];
+ }
+ m_dockMachineMenuActions.clear();
+
+ /* Determine the list of actions to be inserted: */
+ QList<QAction*> actions = actionPool()->action(UIActionIndexRT_M_Machine)->menu()->actions();
+ QList<QAction*> allowedActions;
+ for (int i=0; i < actions.size(); ++i)
+ {
+ /* Check if we really have correct action: */
+ UIAction *pAction = qobject_cast<UIAction*>(actions.at(i));
+ /* Skip incorrect actions: */
+ if (!pAction)
+ continue;
+ /* Skip actions which have 'role' (to prevent consuming): */
+ if (pAction->menuRole() != QAction::NoRole)
+ continue;
+ /* Skip actions which have menu (to prevent consuming): */
+ if (qobject_cast<UIActionMenu*>(pAction))
+ continue;
+ if (!pAction->isAllowed())
+ continue;
+ allowedActions.push_back(actions.at(i));
+ }
+
+ if (!allowedActions.empty())
+ {
+ QAction *pSeparator = new QAction(pDockMenu);
+ pSeparator->setSeparator(true);
+ allowedActions.push_back(pSeparator);
+ pDockMenu->insertActions(m_pDockSettingMenuAction, allowedActions);
+ m_dockMachineMenuActions = allowedActions;
+ }
+
+ /* Clean the previous preview actions: */
+ if (m_pDockPreviewSelectMonitorGroup)
+ {
+ QList<QAction*> previewActions = m_pDockPreviewSelectMonitorGroup->actions();
+ foreach (QAction *pAction, previewActions)
+ {
+ pDockSettingsMenu->removeAction(pAction);
+ m_pDockPreviewSelectMonitorGroup->removeAction(pAction);
+ delete pAction;
+ }
+ }
+ const QList<int> visibleWindowsList = uisession()->listOfVisibleWindows();
+ const int cVisibleGuestScreens = visibleWindowsList.size();
+ if (cVisibleGuestScreens > 1)
+ {
+ if (!m_pDockPreviewSelectMonitorGroup)
+ m_pDockPreviewSelectMonitorGroup = new QActionGroup(this);
+ /* Only if currently selected monitor for icon preview is not enabled: */
+ if (!visibleWindowsList.contains(m_DockIconPreviewMonitor))
+ {
+ int iExtraDataUpdateMonitor = gEDataManager->realtimeDockIconUpdateMonitor(uiCommon().managedVMUuid());
+ if (visibleWindowsList.contains(iExtraDataUpdateMonitor))
+ m_DockIconPreviewMonitor = iExtraDataUpdateMonitor;
+ else
+ m_DockIconPreviewMonitor = visibleWindowsList.at(cVisibleGuestScreens - 1);
+ }
+ if (!m_pDockSettingsMenuSeparator)
+ m_pDockSettingsMenuSeparator = pDockSettingsMenu->addSeparator();
+ for (int i=0; i < cVisibleGuestScreens; ++i)
+ {
+ QAction *pAction = new QAction(m_pDockPreviewSelectMonitorGroup);
+ pAction->setCheckable(true);
+ pAction->setData(visibleWindowsList.at(i));
+ pAction->setText(QApplication::translate("UIActionPool", "Preview Monitor %1").arg(pAction->data().toInt() + 1));
+ if (m_DockIconPreviewMonitor == visibleWindowsList.at(i))
+ pAction->setChecked(true);
+ }
+ pDockSettingsMenu->addActions(m_pDockPreviewSelectMonitorGroup->actions());
+ connect(m_pDockPreviewSelectMonitorGroup, &QActionGroup::triggered,
+ this, &UIMachineLogic::sltDockPreviewMonitorChanged);
+ }
+ else
+ {
+ m_DockIconPreviewMonitor = 0;
+ /* Remove the seperator as well: */
+ if (m_pDockSettingsMenuSeparator)
+ {
+ pDockSettingsMenu->removeAction(m_pDockSettingsMenuSeparator);
+ delete m_pDockSettingsMenuSeparator;
+ m_pDockSettingsMenuSeparator = 0;
+ }
+ }
+}
+#endif /* VBOX_WS_MAC */
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+void UIMachineLogic::prepareDebugger()
+{
+ if (uiCommon().isDebuggerAutoShowEnabled())
+ {
+ if (uiCommon().isDebuggerAutoShowStatisticsEnabled())
+ sltShowDebugStatistics();
+ if (uiCommon().isDebuggerAutoShowCommandLineEnabled())
+ sltShowDebugCommandLine();
+ }
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+void UIMachineLogic::loadSettings()
+{
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+ /* Read cached extra-data value: */
+ m_fIsHidLedsSyncEnabled = gEDataManager->hidLedsSyncState(uiCommon().managedVMUuid());
+ /* Subscribe to extra-data changes to be able to enable/disable feature dynamically: */
+ connect(gEDataManager, &UIExtraDataManager::sigHidLedsSyncStateChange, this, &UIMachineLogic::sltHidLedsSyncStateChanged);
+#endif /* VBOX_WS_MAC || VBOX_WS_WIN */
+ /* HID LEDs sync initialization: */
+ sltSwitchKeyboardLedsToGuestLeds();
+ /* */
+#if defined(VBOX_WS_X11) || defined(VBOX_WS_WIN)
+ connect(gEDataManager, &UIExtraDataManager::sigDisableHostScreenSaverStateChange,
+ this, &UIMachineLogic::sltDisableHostScreenSaverStateChanged);
+ sltDisableHostScreenSaverStateChanged(gEDataManager->disableHostScreenSaver());
+#endif
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+void UIMachineLogic::cleanupDebugger()
+{
+ /* Close debugger: */
+ dbgDestroy();
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+void UIMachineLogic::cleanupDock()
+{
+ if (m_pDockIconPreview)
+ {
+ delete m_pDockIconPreview;
+ m_pDockIconPreview = 0;
+ }
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineLogic::cleanupHandlers()
+{
+ /* Cleanup mouse-handler: */
+ UIMouseHandler::destroy(mouseHandler());
+
+ /* Cleanup keyboard-handler: */
+ UIKeyboardHandler::destroy(keyboardHandler());
+}
+
+void UIMachineLogic::cleanupSessionConnections()
+{
+ /* We should stop watching for VBoxSVC availability changes: */
+ disconnect(&uiCommon(), &UICommon::sigVBoxSVCAvailabilityChange,
+ this, &UIMachineLogic::sltHandleVBoxSVCAvailabilityChange);
+
+ /* We should stop watching for requested modes: */
+ disconnect(uisession(), &UISession::sigInitialized, this, &UIMachineLogic::sltCheckForRequestedVisualStateType);
+ disconnect(uisession(), &UISession::sigAdditionsStateChange, this, &UIMachineLogic::sltCheckForRequestedVisualStateType);
+
+ /* We should stop watching for console events: */
+ disconnect(uisession(), &UISession::sigMachineStateChange, this, &UIMachineLogic::sltMachineStateChanged);
+ disconnect(uisession(), &UISession::sigAdditionsStateActualChange, this, &UIMachineLogic::sltAdditionsStateChanged);
+ disconnect(uisession(), &UISession::sigMouseCapabilityChange, this, &UIMachineLogic::sltMouseCapabilityChanged);
+ disconnect(uisession(), &UISession::sigKeyboardLedsChange, this, &UIMachineLogic::sltKeyboardLedsChanged);
+ disconnect(uisession(), &UISession::sigUSBDeviceStateChange, this, &UIMachineLogic::sltUSBDeviceStateChange);
+ disconnect(uisession(), &UISession::sigRuntimeError, this, &UIMachineLogic::sltRuntimeError);
+#ifdef VBOX_WS_MAC
+ disconnect(uisession(), &UISession::sigShowWindows, this, &UIMachineLogic::sltShowWindows);
+#endif /* VBOX_WS_MAC */
+ disconnect(uisession(), &UISession::sigGuestMonitorChange, this, &UIMachineLogic::sltGuestMonitorChange);
+
+ /* We should stop watching for host-screen-change events: */
+ disconnect(uisession(), &UISession::sigHostScreenCountChange, this, &UIMachineLogic::sltHostScreenCountChange);
+ disconnect(uisession(), &UISession::sigHostScreenGeometryChange, this, &UIMachineLogic::sltHostScreenGeometryChange);
+ disconnect(uisession(), &UISession::sigHostScreenAvailableAreaChange, this, &UIMachineLogic::sltHostScreenAvailableAreaChange);
+
+ /* We should stop notify about frame-buffer events: */
+ disconnect(this, &UIMachineLogic::sigFrameBufferResize, uisession(), &UISession::sigFrameBufferResize);
+}
+
+bool UIMachineLogic::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* Handle machine-window events: */
+ if (UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(pWatched))
+ {
+ /* Make sure this window still registered: */
+ if (isMachineWindowsCreated() && m_machineWindowsList.contains(pMachineWindow))
+ {
+ switch (pEvent->type())
+ {
+ /* Handle *window activated* event: */
+ case QEvent::WindowActivate:
+ {
+#ifdef VBOX_WS_WIN
+ /* We should save current lock states as *previous* and
+ * set current lock states to guest values we have,
+ * As we have no ipc between threads of different VMs
+ * we are using 100ms timer as lazy sync timout: */
+
+ /* On Windows host we should do that only in case if sync
+ * is enabled. Otherwise, keyboardHandler()->winSkipKeyboardEvents(false)
+ * won't be called in sltSwitchKeyboardLedsToGuestLeds() and guest
+ * will loose keyboard input forever. */
+ if (isHidLedsSyncEnabled())
+ {
+ keyboardHandler()->winSkipKeyboardEvents(true);
+ QTimer::singleShot(100, this, SLOT(sltSwitchKeyboardLedsToGuestLeds()));
+ }
+#else /* VBOX_WS_WIN */
+ /* Trigger callback synchronously for now! */
+ sltSwitchKeyboardLedsToGuestLeds();
+#endif /* !VBOX_WS_WIN */
+ break;
+ }
+ /* Handle *window deactivated* event: */
+ case QEvent::WindowDeactivate:
+ {
+ /* We should restore lock states to *previous* known: */
+ sltSwitchKeyboardLedsToPreviousLeds();
+ break;
+ }
+ /* Default: */
+ default: break;
+ }
+ }
+ }
+ /* Call to base-class: */
+ return QIWithRetranslateUI3<QObject>::eventFilter(pWatched, pEvent);
+}
+
+void UIMachineLogic::sltHandleMenuPrepare(int iIndex, QMenu *pMenu)
+{
+ /* Update if there is update-handler: */
+ if (m_menuUpdateHandlers.contains(iIndex))
+ (this->*(m_menuUpdateHandlers.value(iIndex)))(pMenu);
+}
+
+void UIMachineLogic::sltOpenPreferencesDialog(const QString &strCategory /* = QString() */,
+ const QString &strControl /* = QString() */)
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Create instance if not yet created: */
+ if (!m_settings.contains(UISettingsDialog::DialogType_Global))
+ {
+ m_settings[UISettingsDialog::DialogType_Global] = new UISettingsDialogGlobal(activeMachineWindow(),
+ strCategory,
+ strControl);
+ connect(m_settings[UISettingsDialog::DialogType_Global], &UISettingsDialogGlobal::sigClose,
+ this, &UIMachineLogic::sltClosePreferencesDialog);
+ m_settings.value(UISettingsDialog::DialogType_Global)->load();
+ }
+
+ /* Expose instance: */
+ UIDesktopWidgetWatchdog::restoreWidget(m_settings.value(UISettingsDialog::DialogType_Global));
+}
+
+void UIMachineLogic::sltClosePreferencesDialog()
+{
+ /* Remove instance if exist: */
+ delete m_settings.take(UISettingsDialog::DialogType_Global);
+}
+
+void UIMachineLogic::sltClose()
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Do not close machine-window in 'manual-override' mode: */
+ if (uisession()->isManualOverrideMode())
+ return;
+
+ /* First, we have to close/hide any opened modal & popup application widgets.
+ * We have to make sure such window is hidden even if close-event was rejected.
+ * We are re-throwing this slot if any widget present to test again.
+ * If all opened widgets are closed/hidden, we can try to close machine-window: */
+ QWidget *pWidget = QApplication::activeModalWidget() ? QApplication::activeModalWidget() :
+ QApplication::activePopupWidget() ? QApplication::activePopupWidget() : 0;
+ if (pWidget)
+ {
+ /* Closing/hiding all we found: */
+ pWidget->close();
+ if (!pWidget->isHidden())
+ pWidget->hide();
+ QTimer::singleShot(0, this, SLOT(sltClose()));
+ return;
+ }
+
+ /* Try to close active machine-window: */
+ LogRel(("GUI: Request to close active machine-window.\n"));
+ activeMachineWindow()->close();
+}
+
+void UIMachineLogic::sltOpenSettingsDialog(const QString &strCategory /* = QString() */,
+ const QString &strControl /* = QString()*/)
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Create instance if not yet created: */
+ if (!m_settings.contains(UISettingsDialog::DialogType_Machine))
+ {
+ m_settings[UISettingsDialog::DialogType_Machine] = new UISettingsDialogMachine(activeMachineWindow(),
+ machine().GetId(),
+ actionPool(),
+ strCategory,
+ strControl);
+ connect(m_settings[UISettingsDialog::DialogType_Machine], &UISettingsDialogGlobal::sigClose,
+ this, &UIMachineLogic::sltCloseSettingsDialog);
+ m_settings.value(UISettingsDialog::DialogType_Machine)->load();
+ }
+
+ /* Expose instance: */
+ UIDesktopWidgetWatchdog::restoreWidget(m_settings.value(UISettingsDialog::DialogType_Machine));
+}
+
+void UIMachineLogic::sltCloseSettingsDialog()
+{
+ /* Remove instance if exist: */
+ delete m_settings.take(UISettingsDialog::DialogType_Machine);
+
+ /* We can't rely on MediumChange events as they are not yet properly implemented within Main.
+ * We can't watch for MachineData change events as well as they are of broadcast type
+ * and console event-handler do not processing broadcast events.
+ * But we still want to be updated after possible medium changes at least if they were
+ * originated from our side. */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->updateAppearanceOf(UIVisualElement_HDStuff | UIVisualElement_CDStuff | UIVisualElement_FDStuff);
+}
+
+void UIMachineLogic::sltTakeSnapshot()
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Create take-snapshot dialog: */
+ QWidget *pDlgParent = windowManager().realParentWindow(activeMachineWindow());
+ QPointer<UITakeSnapshotDialog> pDlg = new UITakeSnapshotDialog(pDlgParent, machine());
+ windowManager().registerNewParent(pDlg, pDlgParent);
+
+ /* Assign corresponding icon: */
+ if (uisession() && uisession()->machineWindowIcon())
+ pDlg->setIcon(*uisession()->machineWindowIcon());
+
+ /* Search for the max available filter index: */
+ const QString strNameTemplate = UITakeSnapshotDialog::tr("Snapshot %1");
+ int iMaxSnapshotIndex = searchMaxSnapshotIndex(machine(), machine().FindSnapshot(QString()), strNameTemplate);
+ pDlg->setName(strNameTemplate.arg(++ iMaxSnapshotIndex));
+
+ /* Exec the dialog: */
+ const bool fDialogAccepted = pDlg->exec() == QDialog::Accepted;
+
+ /* Make sure dialog still valid: */
+ if (!pDlg)
+ return;
+
+ /* Acquire variables: */
+ const QString strSnapshotName = pDlg->name().trimmed();
+ const QString strSnapshotDescription = pDlg->description();
+
+ /* Destroy dialog early: */
+ delete pDlg;
+
+ /* Was the dialog accepted? */
+ if (!fDialogAccepted)
+ return;
+
+ /* Take snapshot: */
+ UINotificationProgressSnapshotTake *pNotification = new UINotificationProgressSnapshotTake(machine(),
+ strSnapshotName,
+ strSnapshotDescription);
+ gpNotificationCenter->append(pNotification);
+}
+
+void UIMachineLogic::sltShowInformationDialog()
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ if (!m_pVMInformationDialog)
+ m_pVMInformationDialog = new UIVMInformationDialog(activeMachineWindow());
+
+ if (m_pVMInformationDialog)
+ {
+ m_pVMInformationDialog->show();
+ m_pVMInformationDialog->raise();
+ m_pVMInformationDialog->setWindowState(m_pVMInformationDialog->windowState() & ~Qt::WindowMinimized);
+ m_pVMInformationDialog->activateWindow();
+ connect(m_pVMInformationDialog, &UIVMInformationDialog::sigClose, this, &UIMachineLogic::sltCloseInformationDialogDefault);
+ }
+}
+
+void UIMachineLogic::sltCloseInformationDialog(bool fAsync /* = false */)
+{
+ if (!m_pVMInformationDialog)
+ return;
+ if (fAsync)
+ m_pVMInformationDialog->deleteLater();
+ else
+ delete m_pVMInformationDialog;
+ m_pVMInformationDialog = 0;
+}
+
+void UIMachineLogic::sltShowFileManagerDialog()
+{
+ if (machine().isNull() || !activeMachineWindow())
+ return;
+
+ /* Create a file manager only if we don't have one already: */
+ if (m_pFileManagerDialog)
+ {
+ m_pFileManagerDialog->activateWindow();
+ m_pFileManagerDialog->raise();
+ return;
+ }
+
+ QIManagerDialog *pFileManagerDialog;
+ UIFileManagerDialogFactory dialogFactory(actionPool(), machine().GetId(), machine().GetName());
+ dialogFactory.prepare(pFileManagerDialog, activeMachineWindow());
+ if (pFileManagerDialog)
+ {
+ m_pFileManagerDialog = pFileManagerDialog;
+
+ /* Show instance: */
+ pFileManagerDialog->show();
+ pFileManagerDialog->setWindowState(pFileManagerDialog->windowState() & ~Qt::WindowMinimized);
+ pFileManagerDialog->activateWindow();
+ pFileManagerDialog->raise();
+ connect(pFileManagerDialog, &QIManagerDialog::sigClose,
+ this, &UIMachineLogic::sltCloseFileManagerDialog);
+ }
+}
+
+void UIMachineLogic::sltCloseFileManagerDialog()
+{
+ if (!m_pFileManagerDialog)
+ return;
+
+ QIManagerDialog* pDialog = m_pFileManagerDialog;
+ /* Set the m_pFileManagerDialog to NULL before closing the dialog. or we will have redundant deletes*/
+ m_pFileManagerDialog = 0;
+ pDialog->close();
+ UIFileManagerDialogFactory().cleanup(pDialog);
+}
+
+void UIMachineLogic::sltShowLogDialog()
+{
+ if (machine().isNull() || !activeMachineWindow())
+ return;
+
+ /* Create a logviewer only if we don't have one already */
+ if (m_pLogViewerDialog)
+ return;
+
+ QIManagerDialog *pLogViewerDialog;
+ UIVMLogViewerDialogFactory dialogFactory(actionPool(), machine().GetId(), machine().GetName());
+ dialogFactory.prepare(pLogViewerDialog, activeMachineWindow());
+ if (pLogViewerDialog)
+ {
+ m_pLogViewerDialog = pLogViewerDialog;
+
+ /* Show instance: */
+ pLogViewerDialog->show();
+ pLogViewerDialog->setWindowState(pLogViewerDialog->windowState() & ~Qt::WindowMinimized);
+ pLogViewerDialog->activateWindow();
+ connect(pLogViewerDialog, &QIManagerDialog::sigClose,
+ this, &UIMachineLogic::sltCloseLogDialog);
+ }
+}
+
+void UIMachineLogic::sltCloseLogDialog()
+{
+ if (!m_pLogViewerDialog)
+ return;
+
+ QIManagerDialog* pDialog = m_pLogViewerDialog;
+ /* Set the m_pLogViewerDialog to NULL before closing the dialog. or we will have redundant deletes*/
+ m_pLogViewerDialog = 0;
+ pDialog->close();
+ UIVMLogViewerDialogFactory().cleanup(pDialog);
+}
+
+void UIMachineLogic::sltPause(bool fOn)
+{
+ uisession()->setPause(fOn);
+}
+
+void UIMachineLogic::sltReset()
+{
+ reset(true);
+}
+
+void UIMachineLogic::sltDetach()
+{
+ /* Make sure machine is in one of the allowed states: */
+ if (!uisession()->isRunning() && !uisession()->isPaused())
+ {
+ AssertMsgFailed(("Invalid machine-state. Action should be prohibited!"));
+ return;
+ }
+
+ LogRel(("GUI: User requested to detach GUI.\n"));
+ uisession()->detachUi();
+}
+
+void UIMachineLogic::sltSaveState()
+{
+ /* Make sure machine is in one of the allowed states: */
+ if (!uisession()->isRunning() && !uisession()->isPaused())
+ {
+ AssertMsgFailed(("Invalid machine-state. Action should be prohibited!"));
+ return;
+ }
+
+ LogRel(("GUI: User requested to save VM state.\n"));
+ uisession()->saveState();
+}
+
+void UIMachineLogic::sltShutdown()
+{
+ /* Make sure machine is in one of the allowed states: */
+ if (!uisession()->isRunning())
+ {
+ AssertMsgFailed(("Invalid machine-state. Action should be prohibited!"));
+ return;
+ }
+
+ LogRel(("GUI: User requested to shutdown VM.\n"));
+ uisession()->shutdown();
+}
+
+void UIMachineLogic::sltPowerOff()
+{
+ /* Make sure machine is in one of the allowed states: */
+ if (!uisession()->isRunning() && !uisession()->isPaused() && !uisession()->isStuck())
+ {
+ AssertMsgFailed(("Invalid machine-state. Action should be prohibited!"));
+ return;
+ }
+
+ LogRel(("GUI: User requested to power VM off.\n"));
+ const bool fDiscardStateOnPowerOff = gEDataManager->discardStateOnPowerOff(uiCommon().managedVMUuid());
+ uisession()->powerOff(machine().GetSnapshotCount() > 0 && fDiscardStateOnPowerOff);
+}
+
+void UIMachineLogic::sltMinimizeActiveMachineWindow()
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Minimize active machine-window: */
+ AssertPtrReturnVoid(activeMachineWindow());
+ activeMachineWindow()->showMinimized();
+}
+
+void UIMachineLogic::sltAdjustMachineWindows()
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Adjust all window(s)! */
+ foreach(UIMachineWindow *pMachineWindow, machineWindows())
+ {
+ /* Exit maximized window state if actual: */
+ if (pMachineWindow->isMaximized())
+ pMachineWindow->showNormal();
+
+ /* Normalize window geometry: */
+ pMachineWindow->normalizeGeometry(true /* adjust position */, true /* resize window to guest display size */);
+ }
+}
+
+void UIMachineLogic::sltToggleGuestAutoresize(bool fEnabled)
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Toggle guest-autoresize feature for all view(s)! */
+ foreach(UIMachineWindow *pMachineWindow, machineWindows())
+ {
+ pMachineWindow->machineView()->setGuestAutoresizeEnabled(fEnabled);
+ /* Normalize machine windows if auto resize option is toggled to true. */
+ if (fEnabled)
+ {
+ /* Exit maximized window state if actual: */
+ if (pMachineWindow->isMaximized())
+ pMachineWindow->showNormal();
+
+ /* Normalize window geometry: */
+ pMachineWindow->normalizeGeometry(true /* adjust position */, true /* resize window to guest display size */);
+ }
+ }
+
+ /* Save value to extra-data finally: */
+ gEDataManager->setGuestScreenAutoResizeEnabled(fEnabled, uiCommon().managedVMUuid());
+}
+
+void UIMachineLogic::sltTakeScreenshot()
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Formatting default filename for screenshot. VM folder is the default directory to save: */
+ const QFileInfo fi(machine().GetSettingsFilePath());
+ const QString strCurrentTime = QDateTime::currentDateTime().toString("dd_MM_yyyy_hh_mm_ss");
+ const QString strFormatDefaultFileName = QString("VirtualBox").append("_").append(machine().GetName()).append("_").append(strCurrentTime);
+ const QString strDefaultFileName = QDir(fi.absolutePath()).absoluteFilePath(strFormatDefaultFileName);
+
+ /* Formatting temporary filename for screenshot. It is saved in system temporary directory if available, else in VM folder: */
+ QString strTempFile = QDir(fi.absolutePath()).absoluteFilePath("temp").append("_").append(strCurrentTime).append(".png");
+ if (QDir::temp().exists())
+ strTempFile = QDir::temp().absoluteFilePath("temp").append("_").append(strCurrentTime).append(".png");
+
+ /* Do the screenshot: */
+ takeScreenshot(strTempFile, "png");
+
+ /* Which image formats for writing does this Qt version know of? */
+ QList<QByteArray> formats = QImageWriter::supportedImageFormats();
+ QStringList filters;
+ /* Build a filters list out of it: */
+ for (int i = 0; i < formats.size(); ++i)
+ {
+ const QString &s = formats.at(i) + " (*." + formats.at(i).toLower() + ")";
+ /* Check there isn't an entry already (even if it just uses another capitalization) */
+ if (filters.indexOf(QRegularExpression(QRegularExpression::escape(s), QRegularExpression::CaseInsensitiveOption)) == -1)
+ filters << s;
+ }
+ /* Try to select some common defaults: */
+ QString strFilter;
+ int i = filters.indexOf(QRegularExpression(".*png.*", QRegularExpression::CaseInsensitiveOption));
+ if (i == -1)
+ {
+ i = filters.indexOf(QRegularExpression(".*jpe+g.*", QRegularExpression::CaseInsensitiveOption));
+ if (i == -1)
+ i = filters.indexOf(QRegularExpression(".*bmp.*", QRegularExpression::CaseInsensitiveOption));
+ }
+ if (i != -1)
+ {
+ filters.prepend(filters.takeAt(i));
+ strFilter = filters.first();
+ }
+
+#ifdef VBOX_WS_WIN
+ /* Due to Qt bug, modal QFileDialog appeared above the active machine-window
+ * does not retreive the focus from the currently focused machine-view,
+ * as the result guest keyboard remains captured, so we should
+ * clear the focus from this machine-view initially: */
+ if (activeMachineWindow())
+ activeMachineWindow()->machineView()->clearFocus();
+#endif /* VBOX_WS_WIN */
+
+ /* Request the filename from the user: */
+ const QString strFilename = QIFileDialog::getSaveFileName(strDefaultFileName,
+ filters.join(";;"),
+ activeMachineWindow(),
+ tr("Select a filename for the screenshot ..."),
+ &strFilter,
+ true /* resolve symlinks */,
+ true /* confirm overwrite */);
+
+#ifdef VBOX_WS_WIN
+ /* Due to Qt bug, modal QFileDialog appeared above the active machine-window
+ * does not retreive the focus from the currently focused machine-view,
+ * as the result guest keyboard remains captured, so we already
+ * cleared the focus from this machine-view and should return
+ * that focus finally: */
+ if (activeMachineWindow())
+ activeMachineWindow()->machineView()->setFocus();
+#endif /* VBOX_WS_WIN */
+
+ if (!strFilename.isEmpty())
+ {
+ const QString strFormat = strFilter.split(" ").value(0, "png");
+ const QImage tmpImage(strTempFile);
+
+ /* On X11 Qt Filedialog returns the filepath without the filetype suffix, so adding it ourselves: */
+#ifdef VBOX_WS_X11
+ /* Add filetype suffix only if user has not added it explicitly: */
+ if (!strFilename.endsWith(QString(".%1").arg(strFormat)))
+ tmpImage.save(QDir::toNativeSeparators(QFile::encodeName(QString("%1.%2").arg(strFilename, strFormat))),
+ strFormat.toUtf8().constData());
+ else
+ tmpImage.save(QDir::toNativeSeparators(QFile::encodeName(strFilename)),
+ strFormat.toUtf8().constData());
+#else /* !VBOX_WS_X11 */
+ QFile file(strFilename);
+ if (file.open(QIODevice::WriteOnly))
+ tmpImage.save(&file, strFormat.toUtf8().constData());
+#endif /* !VBOX_WS_X11 */
+ }
+ QFile::remove(strTempFile);
+}
+
+void UIMachineLogic::sltOpenRecordingOptions()
+{
+ /* Open VM settings : Display page : Recording tab: */
+ sltOpenSettingsDialog("#display", "m_pCheckboxVideoCapture");
+}
+
+void UIMachineLogic::sltToggleRecording(bool fEnabled)
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Make sure something had changed: */
+ CRecordingSettings comRecordingSettings = machine().GetRecordingSettings();
+ if (comRecordingSettings.GetEnabled() == static_cast<BOOL>(fEnabled))
+ return;
+
+ /* Update recording state: */
+ comRecordingSettings.SetEnabled(fEnabled);
+ if (!comRecordingSettings.isOk())
+ {
+ /* Make sure action is updated: */
+ uisession()->updateStatusRecording();
+ /* Notify about the error: */
+ return UINotificationMessage::cannotToggleRecording(comRecordingSettings, machine().GetName(), fEnabled);
+ }
+
+ /* Save machine-settings: */
+ machine().SaveSettings();
+ if (!machine().isOk())
+ {
+ /* Make sure action is updated: */
+ uisession()->updateStatusRecording();
+ /* Notify about the error: */
+ return UINotificationMessage::cannotSaveMachineSettings(machine());
+ }
+}
+
+void UIMachineLogic::sltToggleVRDE(bool fEnabled)
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Access VRDE server: */
+ CVRDEServer server = machine().GetVRDEServer();
+ AssertMsgReturnVoid(machine().isOk() && !server.isNull(),
+ ("VRDE server should NOT be null!\n"));
+
+ /* Make sure something had changed: */
+ if (server.GetEnabled() == static_cast<BOOL>(fEnabled))
+ return;
+
+ /* Update VRDE server state: */
+ server.SetEnabled(fEnabled);
+ if (!server.isOk())
+ {
+ /* Make sure action is updated: */
+ uisession()->updateStatusVRDE();
+ /* Notify about the error: */
+ return UINotificationMessage::cannotToggleVRDEServer(server, machineName(), fEnabled);
+ }
+
+ /* Save machine-settings: */
+ machine().SaveSettings();
+ if (!machine().isOk())
+ {
+ /* Make sure action is updated: */
+ uisession()->updateStatusVRDE();
+ /* Notify about the error: */
+ return UINotificationMessage::cannotSaveMachineSettings(machine());
+ }
+}
+
+void UIMachineLogic::sltShowKeyboardSettings()
+{
+ /* Global preferences: Input page: */
+ sltOpenPreferencesDialog("#input", "m_pMachineTable");
+}
+
+void UIMachineLogic::sltShowSoftKeyboard()
+{
+ if (machine().isNull() || !activeMachineWindow())
+ return;
+
+ if (!m_pSoftKeyboardDialog)
+ {
+ QWidget *pCenterWidget = windowManager().realParentWindow(activeMachineWindow());
+ m_pSoftKeyboardDialog = new UISoftKeyboard(0, uisession(), pCenterWidget, machine().GetName());
+ connect(m_pSoftKeyboardDialog, &UISoftKeyboard::sigClose, this, &UIMachineLogic::sltCloseSoftKeyboardDefault);
+ }
+
+ if (m_pSoftKeyboardDialog)
+ {
+ m_pSoftKeyboardDialog->show();
+ m_pSoftKeyboardDialog->raise();
+ m_pSoftKeyboardDialog->setWindowState(m_pSoftKeyboardDialog->windowState() & ~Qt::WindowMinimized);
+ m_pSoftKeyboardDialog->activateWindow();
+ }
+}
+
+void UIMachineLogic::sltCloseSoftKeyboard(bool fAsync /* = false */)
+{
+ if (!m_pSoftKeyboardDialog)
+ return;
+ if (fAsync)
+ m_pSoftKeyboardDialog->deleteLater();
+ else
+ delete m_pSoftKeyboardDialog;
+ m_pSoftKeyboardDialog = 0;
+}
+
+void UIMachineLogic::sltTypeCAD()
+{
+ keyboard().PutCAD();
+ AssertWrapperOk(keyboard());
+}
+
+#ifdef VBOX_WS_X11
+void UIMachineLogic::sltTypeCABS()
+{
+ static QVector<LONG> sequence(6);
+ sequence[0] = 0x1d; /* Ctrl down */
+ sequence[1] = 0x38; /* Alt down */
+ sequence[2] = 0x0E; /* Backspace down */
+ sequence[3] = 0x0E | 0x80; /* Backspace up */
+ sequence[4] = 0x38 | 0x80; /* Alt up */
+ sequence[5] = 0x1d | 0x80; /* Ctrl up */
+ keyboard().PutScancodes(sequence);
+ AssertWrapperOk(keyboard());
+}
+#endif /* VBOX_WS_X11 */
+
+void UIMachineLogic::sltTypeCtrlBreak()
+{
+ static QVector<LONG> sequence(6);
+ sequence[0] = 0x1d; /* Ctrl down */
+ sequence[1] = 0xe0; /* Extended flag */
+ sequence[2] = 0x46; /* Break down */
+ sequence[3] = 0xe0; /* Extended flag */
+ sequence[4] = 0x46 | 0x80; /* Break up */
+ sequence[5] = 0x1d | 0x80; /* Ctrl up */
+ keyboard().PutScancodes(sequence);
+ AssertWrapperOk(keyboard());
+}
+
+void UIMachineLogic::sltTypeInsert()
+{
+ static QVector<LONG> sequence(4);
+ sequence[0] = 0xE0; /* Extended flag */
+ sequence[1] = 0x52; /* Insert down */
+ sequence[2] = 0xE0; /* Extended flag */
+ sequence[3] = 0x52 | 0x80; /* Insert up */
+ keyboard().PutScancodes(sequence);
+ AssertWrapperOk(keyboard());
+}
+
+void UIMachineLogic::sltTypePrintScreen()
+{
+ static QVector<LONG> sequence(8);
+ sequence[0] = 0xE0; /* Extended flag */
+ sequence[1] = 0x2A; /* Print.. down */
+ sequence[2] = 0xE0; /* Extended flag */
+ sequence[3] = 0x37; /* ..Screen down */
+ sequence[4] = 0xE0; /* Extended flag */
+ sequence[5] = 0x37 | 0x80; /* ..Screen up */
+ sequence[6] = 0xE0; /* Extended flag */
+ sequence[7] = 0x2A | 0x80; /* Print.. up */
+ keyboard().PutScancodes(sequence);
+ AssertWrapperOk(keyboard());
+}
+
+void UIMachineLogic::sltTypeAltPrintScreen()
+{
+ static QVector<LONG> sequence(10);
+ sequence[0] = 0x38; /* Alt down */
+ sequence[1] = 0xE0; /* Extended flag */
+ sequence[2] = 0x2A; /* Print.. down */
+ sequence[3] = 0xE0; /* Extended flag */
+ sequence[4] = 0x37; /* ..Screen down */
+ sequence[5] = 0xE0; /* Extended flag */
+ sequence[6] = 0x37 | 0x80; /* ..Screen up */
+ sequence[7] = 0xE0; /* Extended flag */
+ sequence[8] = 0x2A | 0x80; /* Print.. up */
+ sequence[9] = 0x38 | 0x80; /* Alt up */
+ keyboard().PutScancodes(sequence);
+ AssertWrapperOk(keyboard());
+}
+
+void UIMachineLogic::sltTypeHostKeyComboPressRelease(bool fToggleSequence)
+{
+ if (keyboardHandler())
+ keyboardHandler()->setHostKeyComboPressedFlag(fToggleSequence);
+ QList<unsigned> shortCodes = UIHostCombo::modifiersToScanCodes(gEDataManager->hostKeyCombination());
+ QVector<LONG> codes;
+ foreach (unsigned idxCode, shortCodes)
+ {
+ /* Check if we need to include extended code for this key: */
+ if (idxCode & 0x100)
+ codes << 0xE0;
+ if (fToggleSequence)
+ {
+ /* Add the press code: */
+ codes << (idxCode & 0x7F);
+ }
+ else
+ {
+ /* Add the release code: */
+ codes << ((idxCode & 0x7F) | 0x80);
+ }
+ }
+
+ keyboard().PutScancodes(codes);
+ AssertWrapperOk(keyboard());
+}
+
+void UIMachineLogic::sltToggleMouseIntegration(bool fEnabled)
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Disable/Enable mouse-integration for all view(s): */
+ mouseHandler()->setMouseIntegrationEnabled(fEnabled);
+}
+
+void UIMachineLogic::sltOpenSettingsDialogStorage()
+{
+ /* Machine settings: Storage page: */
+ sltOpenSettingsDialog("#storage");
+}
+
+void UIMachineLogic::sltMountStorageMedium()
+{
+ /* Sender action: */
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertMsgReturnVoid(pAction, ("This slot should only be called by menu action!\n"));
+
+ /* Current mount-target: */
+ const UIMediumTarget target = pAction->data().value<UIMediumTarget>();
+
+ /* Update current machine mount-target: */
+ uiCommon().updateMachineStorage(machine(), target, actionPool());
+}
+
+void UIMachineLogic::sltToggleAudioOutput(bool fEnabled)
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Access audio adapter: */
+ CAudioSettings const comAudioSettings = machine().GetAudioSettings();
+ CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ AssertMsgReturnVoid(machine().isOk() && comAdapter.isNotNull(),
+ ("Audio adapter should NOT be null!\n"));
+
+ /* Make sure something had changed: */
+ if (comAdapter.GetEnabledOut() == static_cast<BOOL>(fEnabled))
+ return;
+
+ /* Update audio output state: */
+ comAdapter.SetEnabledOut(fEnabled);
+ if (!comAdapter.isOk())
+ {
+ /* Make sure action is updated: */
+ uisession()->updateAudioOutput();
+ /* Notify about the error: */
+ return UINotificationMessage::cannotToggleAudioOutput(comAdapter, machineName(), fEnabled);
+ }
+
+ /* Save machine-settings: */
+ machine().SaveSettings();
+ if (!machine().isOk())
+ {
+ /* Make sure action is updated: */
+ uisession()->updateAudioOutput();
+ /* Notify about the error: */
+ return UINotificationMessage::cannotSaveMachineSettings(machine());
+ }
+}
+
+void UIMachineLogic::sltToggleAudioInput(bool fEnabled)
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Access audio adapter: */
+ CAudioSettings const comAudioSettings = machine().GetAudioSettings();
+ CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ AssertMsgReturnVoid(machine().isOk() && comAdapter.isNotNull(),
+ ("Audio adapter should NOT be null!\n"));
+
+ /* Make sure something had changed: */
+ if (comAdapter.GetEnabledIn() == static_cast<BOOL>(fEnabled))
+ return;
+
+ /* Update audio input state: */
+ comAdapter.SetEnabledIn(fEnabled);
+ if (!comAdapter.isOk())
+ {
+ /* Make sure action is updated: */
+ uisession()->updateAudioInput();
+ /* Notify about the error: */
+ return UINotificationMessage::cannotToggleAudioInput(comAdapter, machineName(), fEnabled);
+ }
+
+ /* Save machine-settings: */
+ machine().SaveSettings();
+ if (!machine().isOk())
+ {
+ /* Make sure action is updated: */
+ uisession()->updateAudioInput();
+ /* Notify about the error: */
+ return UINotificationMessage::cannotSaveMachineSettings(machine());
+ }
+}
+
+void UIMachineLogic::sltOpenSettingsDialogNetwork()
+{
+ /* Open VM settings : Network page: */
+ sltOpenSettingsDialog("#network");
+}
+
+void UIMachineLogic::sltOpenSettingsDialogUSBDevices()
+{
+ /* Machine settings: Storage page: */
+ sltOpenSettingsDialog("#usb");
+}
+
+void UIMachineLogic::sltOpenSettingsDialogSharedFolders()
+{
+ /* Do not process if additions are not loaded! */
+ if (!uisession()->isGuestAdditionsActive())
+ UINotificationMessage::remindAboutGuestAdditionsAreNotActive();
+
+ /* Open VM settings : Shared folders page: */
+ sltOpenSettingsDialog("#sharedFolders");
+}
+
+void UIMachineLogic::sltAttachUSBDevice()
+{
+ /* Get and check sender action object: */
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertMsg(pAction, ("This slot should only be called on selecting USB menu item!\n"));
+
+ /* Get operation target: */
+ USBTarget target = pAction->data().value<USBTarget>();
+
+ /* Attach USB device: */
+ if (target.attach)
+ {
+ /* Try to attach corresponding device: */
+ console().AttachUSBDevice(target.id, QString(""));
+ /* Check if console is OK: */
+ if (!console().isOk())
+ {
+ /* Get current host: */
+ CHost host = uiCommon().host();
+ /* Search the host for the corresponding USB device: */
+ CHostUSBDevice hostDevice = host.FindUSBDeviceById(target.id);
+ /* Get USB device from host USB device: */
+ CUSBDevice device(hostDevice);
+ /* Show a message about procedure failure: */
+ UINotificationMessage::cannotAttachUSBDevice(console(), uiCommon().usbDetails(device));
+ }
+ }
+ /* Detach USB device: */
+ else
+ {
+ /* Search the console for the corresponding USB device: */
+ CUSBDevice device = console().FindUSBDeviceById(target.id);
+ /* Try to detach corresponding device: */
+ console().DetachUSBDevice(target.id);
+ /* Check if console is OK: */
+ if (!console().isOk())
+ {
+ /* Show a message about procedure failure: */
+ UINotificationMessage::cannotDetachUSBDevice(console(), uiCommon().usbDetails(device));
+ }
+ }
+}
+
+void UIMachineLogic::sltAttachWebCamDevice()
+{
+ /* Get and check sender action object: */
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertReturnVoid(pAction);
+
+ /* Get operation target: */
+ WebCamTarget target = pAction->data().value<WebCamTarget>();
+
+ /* Get current emulated USB: */
+ CEmulatedUSB dispatcher = console().GetEmulatedUSB();
+
+ /* Attach webcam device: */
+ if (target.attach)
+ {
+ /* Try to attach corresponding device: */
+ dispatcher.WebcamAttach(target.path, "");
+ /* Check if dispatcher is OK: */
+ if (!dispatcher.isOk())
+ UINotificationMessage::cannotAttachWebCam(dispatcher, target.name, machineName());
+ }
+ /* Detach webcam device: */
+ else
+ {
+ /* Try to detach corresponding device: */
+ dispatcher.WebcamDetach(target.path);
+ /* Check if dispatcher is OK: */
+ if (!dispatcher.isOk())
+ UINotificationMessage::cannotDetachWebCam(dispatcher, target.name, machineName());
+ }
+}
+
+void UIMachineLogic::sltChangeSharedClipboardType(QAction *pAction)
+{
+ /* Assign new mode (without save): */
+ KClipboardMode enmMode = pAction->data().value<KClipboardMode>();
+ machine().SetClipboardMode(enmMode);
+}
+
+void UIMachineLogic::sltToggleNetworkAdapterConnection()
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Get and check 'the sender' action object: */
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertMsgReturnVoid(pAction, ("Sender action should NOT be null!\n"));
+
+ /* Get operation target: */
+ CNetworkAdapter adapter = machine().GetNetworkAdapter((ULONG)pAction->property("slot").toInt());
+ AssertMsgReturnVoid(machine().isOk() && !adapter.isNull(),
+ ("Network adapter should NOT be null!\n"));
+
+ /* Connect/disconnect cable to/from target: */
+ const bool fConnect = !adapter.GetCableConnected();
+ adapter.SetCableConnected(fConnect);
+ if (!adapter.isOk())
+ return UINotificationMessage::cannotToggleNetworkCable(adapter, machineName(), fConnect);
+
+ /* Save machine-settings: */
+ machine().SaveSettings();
+ if (!machine().isOk())
+ return UINotificationMessage::cannotSaveMachineSettings(machine());
+}
+
+void UIMachineLogic::sltChangeDragAndDropType(QAction *pAction)
+{
+ /* Assign new mode (without save): */
+ KDnDMode enmMode = pAction->data().value<KDnDMode>();
+ machine().SetDnDMode(enmMode);
+}
+
+void UIMachineLogic::sltInstallGuestAdditions()
+{
+ /* Do not process if window(s) missed! */
+ if (!isMachineWindowsCreated())
+ return;
+
+ bool fOnlyMount = sender() == actionPool()->action(UIActionIndexRT_M_Devices_S_InsertGuestAdditionsDisk);
+
+ /* Try to acquire default additions ISO: */
+ CSystemProperties comSystemProperties = uiCommon().virtualBox().GetSystemProperties();
+ const QString strAdditions = comSystemProperties.GetDefaultAdditionsISO();
+ if (comSystemProperties.isOk() && !strAdditions.isEmpty())
+ {
+ if (fOnlyMount)
+ return uisession()->sltMountDVDAdHoc(strAdditions);
+ else
+ return uisession()->sltInstallGuestAdditionsFrom(strAdditions);
+ }
+
+ /* Check whether we have already registered image: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CMediumVector comMedia = comVBox.GetDVDImages();
+ if (!comVBox.isOk())
+ UINotificationMessage::cannotAcquireVirtualBoxParameter(comVBox);
+ else
+ {
+ const QString strName = QString("%1_%2.iso").arg(GUI_GuestAdditionsName, uiCommon().vboxVersionStringNormalized());
+ foreach (const CMedium &comMedium, comMedia)
+ {
+ /* Compare the name part ignoring the file case: */
+ const QString strPath = comMedium.GetLocation();
+ if (!comMedium.isOk())
+ UINotificationMessage::cannotAcquireMediumParameter(comMedium);
+ {
+ const QString strFileName = QFileInfo(strPath).fileName();
+ if (RTPathCompare(strName.toUtf8().constData(), strFileName.toUtf8().constData()) == 0)
+ {
+ if (fOnlyMount)
+ return uisession()->sltMountDVDAdHoc(strPath);
+ else
+ return uisession()->sltInstallGuestAdditionsFrom(strPath);
+ }
+ }
+ }
+ }
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* If downloader is running already: */
+ if (UINotificationDownloaderGuestAdditions::exists())
+ gpNotificationCenter->invoke();
+ /* Else propose to download additions: */
+ else if (msgCenter().confirmLookingForGuestAdditions())
+ {
+ /* Download guest additions: */
+ UINotificationDownloaderGuestAdditions *pNotification = UINotificationDownloaderGuestAdditions::instance(GUI_GuestAdditionsName);
+ /* After downloading finished => propose to install or just mount the guest additions: */
+ if (fOnlyMount)
+ connect(pNotification, &UINotificationDownloaderGuestAdditions::sigGuestAdditionsDownloaded,
+ uisession(), &UISession::sltMountDVDAdHoc);
+ else
+ connect(pNotification, &UINotificationDownloaderGuestAdditions::sigGuestAdditionsDownloaded,
+ uisession(), &UISession::sltInstallGuestAdditionsFrom);
+ /* Append and start notification: */
+ gpNotificationCenter->append(pNotification);
+ }
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+
+void UIMachineLogic::sltShowDebugStatistics()
+{
+ if (dbgCreated())
+ {
+ keyboardHandler()->setDebuggerActive();
+ const QByteArray &expandBytes = uiCommon().getDebuggerStatisticsExpand().toUtf8();
+ const QByteArray &filterBytes = uiCommon().getDebuggerStatisticsFilter().toUtf8();
+ m_pDbgGuiVT->pfnShowStatistics(m_pDbgGui, filterBytes.constData(), expandBytes.constData());
+ }
+}
+
+void UIMachineLogic::sltShowDebugCommandLine()
+{
+ if (dbgCreated())
+ {
+ keyboardHandler()->setDebuggerActive();
+ m_pDbgGuiVT->pfnShowCommandLine(m_pDbgGui);
+ }
+}
+
+void UIMachineLogic::sltLoggingToggled(bool fState)
+{
+ NOREF(fState);
+ if (!debugger().isNull() && debugger().isOk())
+ debugger().SetLogEnabled(fState);
+}
+
+void UIMachineLogic::sltShowGuestControlConsoleDialog()
+{
+ if (machine().isNull() || !activeMachineWindow())
+ return;
+
+ /* Create the dialog only if we don't have one already */
+ if (m_pProcessControlDialog)
+ return;
+
+ QIManagerDialog *pProcessControlDialog;
+ UIGuestProcessControlDialogFactory dialogFactory(actionPool(), console().GetGuest(), machine().GetName());
+ dialogFactory.prepare(pProcessControlDialog, activeMachineWindow());
+ if (pProcessControlDialog)
+ {
+ m_pProcessControlDialog = pProcessControlDialog;
+
+ /* Show instance: */
+ pProcessControlDialog->show();
+ pProcessControlDialog->setWindowState(pProcessControlDialog->windowState() & ~Qt::WindowMinimized);
+ pProcessControlDialog->activateWindow();
+ connect(pProcessControlDialog, &QIManagerDialog::sigClose,
+ this, &UIMachineLogic::sltCloseGuestControlConsoleDialog);
+ }
+}
+
+void UIMachineLogic::sltCloseGuestControlConsoleDialog()
+{
+ if (!m_pProcessControlDialog)
+ return;
+
+ QIManagerDialog* pDialog = m_pProcessControlDialog;
+ /* Set the m_pLogViewerDialog to NULL before closing the dialog. or we will have redundant deletes*/
+ m_pProcessControlDialog = 0;
+ pDialog->close();
+ UIGuestProcessControlDialogFactory().cleanup(pDialog);
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+void UIMachineLogic::sltSwitchToMachineWindow()
+{
+ /* Acquire appropriate sender action: */
+ const QAction *pSender = qobject_cast<QAction*>(sender());
+ AssertReturnVoid(pSender);
+ {
+ /* Determine sender action index: */
+ const int iIndex = pSender->data().toInt();
+ AssertReturnVoid(iIndex >= 0 && iIndex < machineWindows().size());
+ {
+ /* Raise appropriate machine-window: */
+ UIMachineWindow *pMachineWindow = machineWindows().at(iIndex);
+ AssertPtrReturnVoid(pMachineWindow);
+ {
+ pMachineWindow->show();
+ pMachineWindow->raise();
+ pMachineWindow->activateWindow();
+ }
+ }
+ }
+}
+
+void UIMachineLogic::sltDockPreviewModeChanged(QAction *pAction)
+{
+ bool fEnabled = pAction != actionPool()->action(UIActionIndexRT_M_Dock_M_DockSettings_T_DisableMonitor);
+ gEDataManager->setRealtimeDockIconUpdateEnabled(fEnabled, uiCommon().managedVMUuid());
+ updateDockOverlay();
+}
+
+void UIMachineLogic::sltDockPreviewMonitorChanged(QAction *pAction)
+{
+ gEDataManager->setRealtimeDockIconUpdateMonitor(pAction->data().toInt(), uiCommon().managedVMUuid());
+ updateDockOverlay();
+}
+
+void UIMachineLogic::sltChangeDockIconUpdate(bool fEnabled)
+{
+ if (isMachineWindowsCreated())
+ {
+ setDockIconPreviewEnabled(fEnabled);
+ if (m_pDockPreviewSelectMonitorGroup)
+ {
+ m_pDockPreviewSelectMonitorGroup->setEnabled(fEnabled);
+ m_DockIconPreviewMonitor = qMin(gEDataManager->realtimeDockIconUpdateMonitor(uiCommon().managedVMUuid()),
+ (int)machine().GetGraphicsAdapter().GetMonitorCount() - 1);
+ }
+ /* Resize the dock icon in the case the preview monitor has changed. */
+ QSize size = machineWindows().at(m_DockIconPreviewMonitor)->machineView()->size();
+ updateDockIconSize(m_DockIconPreviewMonitor, size.width(), size.height());
+ updateDockOverlay();
+ }
+}
+
+void UIMachineLogic::sltChangeDockIconOverlayAppearance(bool fDisabled)
+{
+ /* Update dock icon overlay: */
+ if (isMachineWindowsCreated())
+ updateDockOverlay();
+ /* Make sure to update dock icon disable overlay action state when 'GUI_DockIconDisableOverlay' changed from extra-data manager: */
+ QAction *pDockIconDisableOverlay = actionPool()->action(UIActionIndexRT_M_Dock_M_DockSettings_T_DisableOverlay);
+ if (fDisabled != pDockIconDisableOverlay->isChecked())
+ {
+ /* Block signals initially to avoid recursive loop: */
+ pDockIconDisableOverlay->blockSignals(true);
+ /* Update state: */
+ pDockIconDisableOverlay->setChecked(fDisabled);
+ /* Make sure to unblock signals again: */
+ pDockIconDisableOverlay->blockSignals(false);
+ }
+}
+
+void UIMachineLogic::sltDockIconDisableOverlayChanged(bool fDisabled)
+{
+ /* Write dock icon disable overlay flag to extra-data: */
+ gEDataManager->setDockIconDisableOverlay(fDisabled, uiCommon().managedVMUuid());
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineLogic::sltSwitchKeyboardLedsToGuestLeds()
+{
+ /* Due to async nature of that feature
+ * it can happen that this slot is called when machine-window is
+ * minimized or not active anymore, we should ignore those cases. */
+ QWidget *pActiveWindow = QApplication::activeWindow();
+ if ( !pActiveWindow // no window is active anymore
+ || !qobject_cast<UIMachineWindow*>(pActiveWindow) // window is not machine one
+ || pActiveWindow->isMinimized()) // window is minimized
+ {
+ LogRel2(("GUI: HID LEDs Sync: skipping sync because active window is lost or minimized!\n"));
+ return;
+ }
+
+// /* Log statement (printf): */
+// QString strDt = QDateTime::currentDateTime().toString("HH:mm:ss:zzz");
+// printf("%s: UIMachineLogic: sltSwitchKeyboardLedsToGuestLeds called, machine name is {%s}\n",
+// strDt.toUtf8().constData(),
+// machineName().toUtf8().constData());
+
+ /* Here we have to store host LED lock states. */
+
+ /* Here we have to update host LED lock states using values provided by UISession registry.
+ * [bool] uisession() -> isNumLock(), isCapsLock(), isScrollLock() can be used for that. */
+
+ if (!isHidLedsSyncEnabled())
+ return;
+
+#if defined(VBOX_WS_MAC)
+ if (m_pHostLedsState == NULL)
+ m_pHostLedsState = DarwinHidDevicesKeepLedsState();
+ if (m_pHostLedsState != NULL)
+ DarwinHidDevicesBroadcastLeds(m_pHostLedsState, uisession()->isNumLock(), uisession()->isCapsLock(), uisession()->isScrollLock());
+#elif defined(VBOX_WS_WIN)
+ if (m_pHostLedsState == NULL)
+ m_pHostLedsState = WinHidDevicesKeepLedsState();
+ keyboardHandler()->winSkipKeyboardEvents(true);
+ WinHidDevicesBroadcastLeds(uisession()->isNumLock(), uisession()->isCapsLock(), uisession()->isScrollLock());
+ keyboardHandler()->winSkipKeyboardEvents(false);
+#else
+ LogRelFlow(("UIMachineLogic::sltSwitchKeyboardLedsToGuestLeds: keep host LED lock states and broadcast guest's ones does not supported on this platform\n"));
+#endif
+}
+
+void UIMachineLogic::sltSwitchKeyboardLedsToPreviousLeds()
+{
+// /* Log statement (printf): */
+// QString strDt = QDateTime::currentDateTime().toString("HH:mm:ss:zzz");
+// printf("%s: UIMachineLogic: sltSwitchKeyboardLedsToPreviousLeds called, machine name is {%s}\n",
+// strDt.toUtf8().constData(),
+// machineName().toUtf8().constData());
+
+ if (!isHidLedsSyncEnabled())
+ return;
+
+ /* Here we have to restore host LED lock states. */
+ void *pvLedState = m_pHostLedsState;
+ if (pvLedState)
+ {
+ /* bird: I've observed recursive calls here when setting m_pHostLedsState to NULL after calling
+ WinHidDevicesApplyAndReleaseLedsState. The result is a double free(), which the CRT
+ usually detects and I could see this->m_pHostLedsState == NULL. The windows function
+ does dispatch loop fun, that's probably the reason for it. Hopefully not an issue on OS X. */
+ m_pHostLedsState = NULL;
+#if defined(VBOX_WS_MAC)
+ DarwinHidDevicesApplyAndReleaseLedsState(pvLedState);
+#elif defined(VBOX_WS_WIN)
+ keyboardHandler()->winSkipKeyboardEvents(true);
+ WinHidDevicesApplyAndReleaseLedsState(pvLedState);
+ keyboardHandler()->winSkipKeyboardEvents(false);
+#else
+ LogRelFlow(("UIMachineLogic::sltSwitchKeyboardLedsToPreviousLeds: restore host LED lock states does not supported on this platform\n"));
+#endif
+ }
+}
+
+void UIMachineLogic::sltHandleVisualStateChange()
+{
+ /* Check for new requested value stored in extra-data: */
+ const UIVisualStateType enmRequestedState = gEDataManager->requestedVisualState(uiCommon().managedVMUuid());
+ /* Check whether current value OR old requested value differs from new requested one.
+ * That way we will NOT enter seamless mode instantly if it is already planned
+ * but is not entered because we're waiting for a guest addition permission. */
+ if ( visualStateType() != enmRequestedState
+ && uisession()->requestedVisualState() != enmRequestedState)
+ {
+ switch (enmRequestedState)
+ {
+ case UIVisualStateType_Normal: return sltChangeVisualStateToNormal();
+ case UIVisualStateType_Fullscreen: return sltChangeVisualStateToFullscreen();
+ case UIVisualStateType_Seamless: return sltChangeVisualStateToSeamless();
+ case UIVisualStateType_Scale: return sltChangeVisualStateToScale();
+ default: break;
+ }
+ }
+}
+
+void UIMachineLogic::sltHandleCommitData()
+{
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Cleanup debugger before VBoxDbg module handle cleaned up: */
+ cleanupDebugger();
+ sltCloseLogDialog();
+ sltCloseGuestControlConsoleDialog();
+#endif
+ activateScreenSaver();
+ sltCloseFileManagerDialog();
+ sltCloseInformationDialog();
+ sltCloseSoftKeyboard();
+ sltSwitchKeyboardLedsToPreviousLeds();
+ sltCloseSettingsDialog();
+ sltClosePreferencesDialog();
+}
+
+void UIMachineLogic::typeHostKeyComboPressRelease(bool fToggleSequence)
+{
+ QAction *pHostKeyAction = actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard_T_TypeHostKeyCombo);
+ if (!pHostKeyAction)
+ return;
+ /* Do nothing if we try to insert host key combo press (release) and it is already in pressed (released) state: */
+ if (fToggleSequence == pHostKeyAction->isChecked())
+ return;
+ pHostKeyAction->toggle();
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+void UIMachineLogic::dbgAdjustRelativePos()
+{
+ if (m_pDbgGui)
+ {
+ const QRect rct = activeMachineWindow()->frameGeometry();
+ m_pDbgGuiVT->pfnAdjustRelativePos(m_pDbgGui, rct.x(), rct.y(), rct.width(), rct.height());
+ }
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+void UIMachineLogic::updateMenuDevicesStorage(QMenu *pMenu)
+{
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Determine device-type: */
+ const QMenu *pOpticalDevicesMenu = actionPool()->action(UIActionIndexRT_M_Devices_M_OpticalDevices)->menu();
+ const QMenu *pFloppyDevicesMenu = actionPool()->action(UIActionIndexRT_M_Devices_M_FloppyDevices)->menu();
+ const KDeviceType deviceType = pMenu == pOpticalDevicesMenu ? KDeviceType_DVD :
+ pMenu == pFloppyDevicesMenu ? KDeviceType_Floppy :
+ KDeviceType_Null;
+ AssertMsgReturnVoid(deviceType != KDeviceType_Null, ("Incorrect storage device-type!\n"));
+
+ /* Prepare/fill all storage menus: */
+ foreach (const CMediumAttachment &attachment, machine().GetMediumAttachments())
+ {
+ /* Current controller: */
+ const CStorageController controller = machine().GetStorageControllerByName(attachment.GetController());
+ /* If controller present and device-type correct: */
+ if (!controller.isNull() && attachment.GetType() == deviceType)
+ {
+ /* Current controller/attachment attributes: */
+ const QString strControllerName = controller.GetName();
+ const StorageSlot storageSlot(controller.GetBus(), attachment.GetPort(), attachment.GetDevice());
+
+ /* Prepare current storage menu: */
+ QMenu *pStorageMenu = 0;
+ /* If it will be more than one storage menu: */
+ if (pMenu->menuAction()->data().toInt() > 1)
+ {
+ /* We have to create sub-menu for each of them: */
+ pStorageMenu = new QMenu(QString("%1 (%2)").arg(strControllerName).arg(gpConverter->toString(storageSlot)), pMenu);
+ switch (controller.GetBus())
+ {
+ case KStorageBus_IDE: pStorageMenu->setIcon(QIcon(":/ide_16px.png")); break;
+ case KStorageBus_SATA: pStorageMenu->setIcon(QIcon(":/sata_16px.png")); break;
+ case KStorageBus_SCSI: pStorageMenu->setIcon(QIcon(":/scsi_16px.png")); break;
+ case KStorageBus_Floppy: pStorageMenu->setIcon(QIcon(":/floppy_16px.png")); break;
+ case KStorageBus_SAS: pStorageMenu->setIcon(QIcon(":/sas_16px.png")); break;
+ case KStorageBus_USB: pStorageMenu->setIcon(QIcon(":/usb_16px.png")); break;
+ case KStorageBus_PCIe: pStorageMenu->setIcon(QIcon(":/pcie_16px.png")); break;
+ case KStorageBus_VirtioSCSI: pStorageMenu->setIcon(QIcon(":/virtio_scsi_16px.png")); break;
+ default: break;
+ }
+ pMenu->addMenu(pStorageMenu);
+ }
+ /* Otherwise just use existing one: */
+ else pStorageMenu = pMenu;
+
+ /* Fill current storage menu: */
+ uiCommon().prepareStorageMenu(*pStorageMenu,
+ this, SLOT(sltMountStorageMedium()),
+ machine(), strControllerName, storageSlot);
+ }
+ }
+}
+
+void UIMachineLogic::updateMenuDevicesNetwork(QMenu *pMenu)
+{
+ /* Determine how many adapters we should display: */
+ const KChipsetType chipsetType = machine().GetChipsetType();
+ const ULONG uCount = qMin((ULONG)4, uiCommon().virtualBox().GetSystemProperties().GetMaxNetworkAdapters(chipsetType));
+
+ /* Enumerate existing network adapters: */
+ QMap<int, bool> adapterData;
+ for (ULONG uSlot = 0; uSlot < uCount; ++uSlot)
+ {
+ /* Get and check iterated adapter: */
+ const CNetworkAdapter adapter = machine().GetNetworkAdapter(uSlot);
+ AssertReturnVoid(machine().isOk() && !adapter.isNull());
+
+ /* Skip disabled adapters: */
+ if (!adapter.GetEnabled())
+ continue;
+
+ /* Remember adapter data: */
+ adapterData.insert((int)uSlot, (bool)adapter.GetCableConnected());
+ }
+
+ /* Make sure at least one adapter was enabled: */
+ if (adapterData.isEmpty())
+ return;
+
+ /* Add new actions: */
+ foreach (int iSlot, adapterData.keys())
+ {
+ QAction *pAction = pMenu->addAction(UIIconPool::iconSetOnOff(":/connect_on_16px.png", ":/connect_16px.png"),
+ adapterData.size() == 1 ? UIActionPool::tr("&Connect Network Adapter") :
+ UIActionPool::tr("Connect Network Adapter &%1").arg(iSlot + 1),
+ this, SLOT(sltToggleNetworkAdapterConnection()));
+ pAction->setProperty("slot", iSlot);
+ pAction->setCheckable(true);
+ pAction->setChecked(adapterData[iSlot]);
+ }
+}
+
+void UIMachineLogic::updateMenuDevicesUSB(QMenu *pMenu)
+{
+ /* Get current host: */
+ const CHost host = uiCommon().host();
+ /* Get host USB device list: */
+ const CHostUSBDeviceVector devices = host.GetUSBDevices();
+
+ /* If device list is empty: */
+ if (devices.isEmpty())
+ {
+ /* Add only one - "empty" action: */
+ QAction *pEmptyMenuAction = pMenu->addAction(UIIconPool::iconSet(":/usb_unavailable_16px.png",
+ ":/usb_unavailable_disabled_16px.png"),
+ UIActionPool::tr("No USB Devices Connected"));
+ pEmptyMenuAction->setToolTip(UIActionPool::tr("No supported devices connected to the host PC"));
+ pEmptyMenuAction->setEnabled(false);
+ }
+ /* If device list is NOT empty: */
+ else
+ {
+ /* Populate menu with host USB devices: */
+ foreach (const CHostUSBDevice& hostDevice, devices)
+ {
+ /* Get USB device from current host USB device: */
+ const CUSBDevice device(hostDevice);
+
+ /* Create USB device action: */
+ QAction *pAttachUSBAction = pMenu->addAction(uiCommon().usbDetails(device),
+ this, SLOT(sltAttachUSBDevice()));
+ pAttachUSBAction->setToolTip(uiCommon().usbToolTip(device));
+ pAttachUSBAction->setCheckable(true);
+
+ /* Check if that USB device was already attached to this session: */
+ const CUSBDevice attachedDevice = console().FindUSBDeviceById(device.GetId());
+ pAttachUSBAction->setChecked(!attachedDevice.isNull());
+ pAttachUSBAction->setEnabled(hostDevice.GetState() != KUSBDeviceState_Unavailable);
+
+ /* Set USB attach data: */
+ pAttachUSBAction->setData(QVariant::fromValue(USBTarget(!pAttachUSBAction->isChecked(), device.GetId())));
+ }
+ }
+}
+
+void UIMachineLogic::updateMenuDevicesWebCams(QMenu *pMenu)
+{
+ /* Clear contents: */
+ pMenu->clear();
+
+ /* Get current host: */
+ const CHost host = uiCommon().host();
+ /* Get host webcam list: */
+ const CHostVideoInputDeviceVector webcams = host.GetVideoInputDevices();
+
+ /* If webcam list is empty: */
+ if (webcams.isEmpty())
+ {
+ /* Add only one - "empty" action: */
+ QAction *pEmptyMenuAction = pMenu->addAction(UIIconPool::iconSet(":/web_camera_unavailable_16px.png",
+ ":/web_camera_unavailable_disabled_16px.png"),
+ UIActionPool::tr("No Webcams Connected"));
+ pEmptyMenuAction->setToolTip(UIActionPool::tr("No supported webcams connected to the host PC"));
+ pEmptyMenuAction->setEnabled(false);
+ }
+ /* If webcam list is NOT empty: */
+ else
+ {
+ /* Populate menu with host webcams: */
+ const QVector<QString> attachedWebcamPaths = console().GetEmulatedUSB().GetWebcams();
+ foreach (const CHostVideoInputDevice &webcam, webcams)
+ {
+ /* Get webcam data: */
+ const QString strWebcamName = webcam.GetName();
+ const QString strWebcamPath = webcam.GetPath();
+
+ /* Create/configure webcam action: */
+ QAction *pAttachWebcamAction = pMenu->addAction(strWebcamName,
+ this, SLOT(sltAttachWebCamDevice()));
+ pAttachWebcamAction->setToolTip(uiCommon().usbToolTip(webcam));
+ pAttachWebcamAction->setCheckable(true);
+
+ /* Check if that webcam was already attached to this session: */
+ pAttachWebcamAction->setChecked(attachedWebcamPaths.contains(strWebcamPath));
+
+ /* Set USB attach data: */
+ pAttachWebcamAction->setData(QVariant::fromValue(WebCamTarget(!pAttachWebcamAction->isChecked(), strWebcamName, strWebcamPath)));
+ }
+ }
+}
+
+void UIMachineLogic::updateMenuDevicesSharedClipboard(QMenu *pMenu)
+{
+ /* Acquire current clipboard mode: */
+ const KClipboardMode enmCurrentMode = machine().GetClipboardMode();
+
+ /* First run: */
+ if (!m_pSharedClipboardActions)
+ {
+ /* Prepare action-group: */
+ m_pSharedClipboardActions = new QActionGroup(this);
+ /* Load currently supported Clipboard modes: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ QVector<KClipboardMode> clipboardModes = comProperties.GetSupportedClipboardModes();
+ /* Take current clipboard mode into account: */
+ if (!clipboardModes.contains(enmCurrentMode))
+ clipboardModes.prepend(enmCurrentMode);
+ /* Create action for all clipboard modes: */
+ foreach (const KClipboardMode &enmMode, clipboardModes)
+ {
+ QAction *pAction = new QAction(gpConverter->toString(enmMode), m_pSharedClipboardActions);
+ pMenu->addAction(pAction);
+ pAction->setData(QVariant::fromValue(enmMode));
+ pAction->setCheckable(true);
+ pAction->setChecked(enmMode == enmCurrentMode);
+ }
+ /* Connect action-group trigger: */
+ connect(m_pSharedClipboardActions, &QActionGroup::triggered, this, &UIMachineLogic::sltChangeSharedClipboardType);
+ }
+ /* Subsequent runs: */
+ else
+ foreach (QAction *pAction, m_pSharedClipboardActions->actions())
+ if (pAction->data().value<KClipboardMode>() == enmCurrentMode)
+ pAction->setChecked(true);
+}
+
+void UIMachineLogic::updateMenuDevicesDragAndDrop(QMenu *pMenu)
+{
+ /* Acquire current DnD mode: */
+ const KDnDMode enmCurrentMode = machine().GetDnDMode();
+
+ /* First run: */
+ if (!m_pDragAndDropActions)
+ {
+ /* Prepare action-group: */
+ m_pDragAndDropActions = new QActionGroup(this);
+ /* Load currently supported DnD modes: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ QVector<KDnDMode> dndModes = comProperties.GetSupportedDnDModes();
+ /* Take current DnD mode into account: */
+ if (!dndModes.contains(enmCurrentMode))
+ dndModes.prepend(enmCurrentMode);
+ /* Create action for all clipboard modes: */
+ foreach (const KDnDMode &enmMode, dndModes)
+ {
+ QAction *pAction = new QAction(gpConverter->toString(enmMode), m_pDragAndDropActions);
+ pMenu->addAction(pAction);
+ pAction->setData(QVariant::fromValue(enmMode));
+ pAction->setCheckable(true);
+ pAction->setChecked(enmMode == enmCurrentMode);
+ }
+ /* Connect action-group trigger: */
+ connect(m_pDragAndDropActions, &QActionGroup::triggered, this, &UIMachineLogic::sltChangeDragAndDropType);
+ }
+ /* Subsequent runs: */
+ else
+ foreach (QAction *pAction, m_pDragAndDropActions->actions())
+ if (pAction->data().value<KDnDMode>() == enmCurrentMode)
+ pAction->setChecked(true);
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+void UIMachineLogic::updateMenuDebug(QMenu*)
+{
+ /* The "Logging" item. */
+ bool fEnabled = false;
+ bool fChecked = false;
+ if (!debugger().isNull() && debugger().isOk())
+ {
+ fEnabled = true;
+ fChecked = debugger().GetLogEnabled() != FALSE;
+ }
+ if (fEnabled != actionPool()->action(UIActionIndexRT_M_Debug_T_Logging)->isEnabled())
+ actionPool()->action(UIActionIndexRT_M_Debug_T_Logging)->setEnabled(fEnabled);
+ if (fChecked != actionPool()->action(UIActionIndexRT_M_Debug_T_Logging)->isChecked())
+ actionPool()->action(UIActionIndexRT_M_Debug_T_Logging)->setChecked(fChecked);
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+void UIMachineLogic::updateMenuWindow(QMenu *pMenu)
+{
+ /* Make sure 'Switch' action(s) are allowed: */
+ AssertPtrReturnVoid(actionPool());
+ if (actionPool()->isAllowedInMenuWindow(UIExtraDataMetaDefs::MenuWindowActionType_Switch))
+ {
+ /* Append menu with actions to switch to machine-window(s): */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ {
+ /* Create machine-window action: */
+ AssertPtrReturnVoid(pMachineWindow);
+ QAction *pMachineWindowAction = pMenu->addAction(pMachineWindow->windowTitle(),
+ this, SLOT(sltSwitchToMachineWindow()));
+ AssertPtrReturnVoid(pMachineWindowAction);
+ {
+ pMachineWindowAction->setCheckable(true);
+ pMachineWindowAction->setChecked(activeMachineWindow() == pMachineWindow);
+ pMachineWindowAction->setData((int)pMachineWindow->screenId());
+ }
+ }
+ }
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineLogic::askUserForTheDiskEncryptionPasswords()
+{
+ /* Prepare the map of the encrypted media: */
+ EncryptedMediumMap encryptedMedia;
+ foreach (const CMediumAttachment &attachment, machine().GetMediumAttachments())
+ {
+ /* Acquire hard-drive attachments only: */
+ if (attachment.GetType() == KDeviceType_HardDisk)
+ {
+ /* Get the attachment medium base: */
+ const CMedium medium = attachment.GetMedium();
+ /* Update the map with this medium if it's encrypted: */
+ QString strCipher;
+ const QString strPasswordId = medium.GetEncryptionSettings(strCipher);
+ if (medium.isOk())
+ encryptedMedia.insert(strPasswordId, medium.GetId());
+ }
+ }
+
+ /* Ask for the disk encryption passwords if necessary: */
+ EncryptionPasswordMap encryptionPasswords;
+ if (!encryptedMedia.isEmpty())
+ {
+ /* Create the dialog for acquiring encryption passwords: */
+ QWidget *pDlgParent = windowManager().realParentWindow(activeMachineWindow());
+ QPointer<UIAddDiskEncryptionPasswordDialog> pDlg =
+ new UIAddDiskEncryptionPasswordDialog(pDlgParent,
+ machineName(),
+ encryptedMedia);
+ /* Execute the dialog: */
+ if (pDlg->exec() == QDialog::Accepted)
+ {
+ /* Acquire the passwords provided: */
+ encryptionPasswords = pDlg->encryptionPasswords();
+
+ /* Delete the dialog: */
+ delete pDlg;
+
+ /* Make sure the passwords were really provided: */
+ AssertReturnVoid(!encryptionPasswords.isEmpty());
+
+ /* Apply the disk encryption passwords: */
+ foreach (const QString &strKey, encryptionPasswords.keys())
+ {
+ console().AddEncryptionPassword(strKey, encryptionPasswords.value(strKey), false);
+ if (!console().isOk())
+ msgCenter().cannotAddDiskEncryptionPassword(console());
+ }
+ }
+ else
+ {
+ /* Any modal dialog can be destroyed in own event-loop
+ * as a part of VM power-off procedure which closes GUI.
+ * So we have to check if the dialog still valid.. */
+
+ /* If dialog still valid: */
+ if (pDlg)
+ {
+ /* Delete the dialog: */
+ delete pDlg;
+
+ /* Propose the user to close VM: */
+ LogRel(("GUI: Request to close Runtime UI due to DEK was not provided.\n"));
+ QMetaObject::invokeMethod(this, "sltClose", Qt::QueuedConnection);
+ }
+ }
+ }
+}
+
+int UIMachineLogic::searchMaxSnapshotIndex(const CMachine &machine,
+ const CSnapshot &snapshot,
+ const QString &strNameTemplate)
+{
+ int iMaxIndex = 0;
+ QRegExp regExp(QString("^") + strNameTemplate.arg("([0-9]+)") + QString("$"));
+ if (!snapshot.isNull())
+ {
+ /* Check the current snapshot name */
+ QString strName = snapshot.GetName();
+ int iPos = regExp.indexIn(strName);
+ if (iPos != -1)
+ iMaxIndex = regExp.cap(1).toInt() > iMaxIndex ? regExp.cap(1).toInt() : iMaxIndex;
+ /* Traversing all the snapshot children */
+ foreach (const CSnapshot &child, snapshot.GetChildren())
+ {
+ int iMaxIndexOfChildren = searchMaxSnapshotIndex(machine, child, strNameTemplate);
+ iMaxIndex = iMaxIndexOfChildren > iMaxIndex ? iMaxIndexOfChildren : iMaxIndex;
+ }
+ }
+ return iMaxIndex;
+}
+
+void UIMachineLogic::takeScreenshot(const QString &strFile, const QString &strFormat /* = "png" */) const
+{
+ /* Get console: */
+ const int cGuestScreens = machine().GetGraphicsAdapter().GetMonitorCount();
+ QList<QImage> images;
+ ULONG uMaxWidth = 0;
+ ULONG uMaxHeight = 0;
+ /* First create screenshots of all guest screens and save them in a list.
+ * Also sum the width of all images and search for the biggest image height. */
+ for (int i = 0; i < cGuestScreens; ++i)
+ {
+ ULONG width = 0;
+ ULONG height = 0;
+ ULONG bpp = 0;
+ LONG xOrigin = 0;
+ LONG yOrigin = 0;
+ KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
+ display().GetScreenResolution(i, width, height, bpp, xOrigin, yOrigin, monitorStatus);
+ uMaxWidth += width;
+ uMaxHeight = RT_MAX(uMaxHeight, height);
+ QImage shot = QImage(width, height, QImage::Format_RGB32);
+ /* For separate process: */
+ if (uiCommon().isSeparateProcess())
+ {
+ /* Take screen-data to array first: */
+ const QVector<BYTE> screenData = display().TakeScreenShotToArray(i, shot.width(), shot.height(), KBitmapFormat_BGR0);
+ /* And copy that data to screen-shot if it is Ok: */
+ if (display().isOk() && !screenData.isEmpty())
+ memcpy(shot.bits(), screenData.data(), shot.width() * shot.height() * 4);
+ }
+ /* For the same process: */
+ else
+ {
+ /* Take the screen-shot directly: */
+ display().TakeScreenShot(i, shot.bits(), shot.width(), shot.height(), KBitmapFormat_BGR0);
+ }
+ images << shot;
+ }
+ /* Create a image which will hold all sub images vertically. */
+ QImage bigImg = QImage(uMaxWidth, uMaxHeight, QImage::Format_RGB32);
+ QPainter p(&bigImg);
+ ULONG w = 0;
+ /* Paint them. */
+ for (int i = 0; i < images.size(); ++i)
+ {
+ p.drawImage(w, 0, images.at(i));
+ w += images.at(i).width();
+ }
+ p.end();
+
+ /* Save the big image in the requested format: */
+ const QFileInfo fi(strFile);
+ const QString &strPathWithoutSuffix = QDir(fi.absolutePath()).absoluteFilePath(fi.baseName());
+ const QString &strSuffix = fi.suffix().isEmpty() ? strFormat : fi.suffix();
+ bigImg.save(QDir::toNativeSeparators(QFile::encodeName(QString("%1.%2").arg(strPathWithoutSuffix, strSuffix))),
+ strFormat.toUtf8().constData());
+}
+
+void UIMachineLogic::activateScreenSaver()
+{
+ /* Do nothing if we did not de-activated the host screen saver: */
+ if (!gEDataManager->disableHostScreenSaver())
+ return;
+
+ QVector<CMachine> machines = uiCommon().virtualBox().GetMachines();
+ bool fAnother = false;
+ for (int i = 0; i < machines.size(); ++i)
+ {
+ if (machines[i].GetState() == KMachineState_Running && machines[i].GetId() != machine().GetId())
+ {
+ fAnother = true;
+ break;
+ }
+ }
+
+ /* Do nothing if there are other vms running.*/
+ if (fAnother)
+ return;
+ sltDisableHostScreenSaverStateChanged(false);
+}
+
+void UIMachineLogic::showBootFailureDialog()
+{
+ UIBootFailureDialog *pBootFailureDialog = new UIBootFailureDialog(activeMachineWindow(), machine());
+ AssertPtrReturnVoid(pBootFailureDialog);
+
+ int iResult = pBootFailureDialog->exec(false);
+ QString strISOPath = pBootFailureDialog->bootMediumPath();
+
+ delete pBootFailureDialog;
+
+ QFileInfo bootMediumFileInfo(strISOPath);
+ if (bootMediumFileInfo.exists() && bootMediumFileInfo.isReadable())
+ mountBootMedium(uiCommon().openMedium(UIMediumDeviceType_DVD, strISOPath));
+
+ if (iResult == static_cast<int>(UIBootFailureDialog::ReturnCode_Reset))
+ reset(false);
+}
+
+bool UIMachineLogic::mountBootMedium(const QUuid &uMediumId)
+{
+ AssertReturn(!uMediumId.isNull(), false);
+
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CMachine &comMachine = machine();
+ const CGuestOSType &comOsType = comVBox.GetGuestOSType(comMachine.GetOSTypeId());
+ /* Get recommended controller bus & type: */
+ const KStorageBus enmRecommendedDvdBus = comOsType.GetRecommendedDVDStorageBus();
+ const KStorageControllerType enmRecommendedDvdType = comOsType.GetRecommendedDVDStorageController();
+
+ CMediumAttachment comAttachment;
+ /* Search for an attachment of required bus & type: */
+ foreach (const CMediumAttachment &comCurrentAttachment, comMachine.GetMediumAttachments())
+ {
+ /* Determine current attachment's controller: */
+ const CStorageController &comCurrentController = comMachine.GetStorageControllerByName(comCurrentAttachment.GetController());
+
+ if ( comCurrentController.GetBus() == enmRecommendedDvdBus
+ && comCurrentController.GetControllerType() == enmRecommendedDvdType
+ && comCurrentAttachment.GetType() == KDeviceType_DVD)
+ {
+ comAttachment = comCurrentAttachment;
+ break;
+ }
+ }
+ AssertMsgReturn(!comAttachment.isNull(), ("Storage Controller is NOT properly configured!\n"), false);
+
+ const UIMedium guiMedium = uiCommon().medium(uMediumId);
+ const CMedium comMedium = guiMedium.medium();
+
+ /* Mount medium to the predefined port/device: */
+ comMachine.MountMedium(comAttachment.GetController(), comAttachment.GetPort(), comAttachment.GetDevice(), comMedium, false /* force */);
+ bool fSuccess = comMachine.isOk();
+
+ QWidget *pParent = windowManager().realParentWindow(activeMachineWindow());
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ msgCenter().cannotRemountMedium(comMachine, guiMedium, true /* mount? */, false /* retry? */, pParent);
+ else
+ {
+ /* Save machine settings: */
+ comMachine.SaveSettings();
+ fSuccess = comMachine.isOk();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ UINotificationMessage::cannotSaveMachineSettings(machine());
+ }
+ return fSuccess;
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+
+bool UIMachineLogic::dbgCreated()
+{
+ if (m_pDbgGui)
+ return true;
+
+ RTLDRMOD hLdrMod = uiCommon().getDebuggerModule();
+ if (hLdrMod == NIL_RTLDRMOD)
+ return false;
+
+ PFNDBGGUICREATE pfnGuiCreate;
+ int rc = RTLdrGetSymbol(hLdrMod, "DBGGuiCreate", (void**)&pfnGuiCreate);
+ if (RT_SUCCESS(rc))
+ {
+ ISession *pISession = session().raw();
+ rc = pfnGuiCreate(pISession, &m_pDbgGui, &m_pDbgGuiVT);
+ if (RT_SUCCESS(rc))
+ {
+ if ( DBGGUIVT_ARE_VERSIONS_COMPATIBLE(m_pDbgGuiVT->u32Version, DBGGUIVT_VERSION)
+ || m_pDbgGuiVT->u32EndVersion == m_pDbgGuiVT->u32Version)
+ {
+ m_pDbgGuiVT->pfnSetParent(m_pDbgGui, activeMachineWindow());
+ m_pDbgGuiVT->pfnSetMenu(m_pDbgGui, actionPool()->action(UIActionIndexRT_M_Debug));
+ dbgAdjustRelativePos();
+ return true;
+ }
+
+ LogRel(("GUI: DBGGuiCreate failed, incompatible versions (loaded %#x/%#x, expected %#x)\n",
+ m_pDbgGuiVT->u32Version, m_pDbgGuiVT->u32EndVersion, DBGGUIVT_VERSION));
+ }
+ else
+ LogRel(("GUI: DBGGuiCreate failed, rc=%Rrc\n", rc));
+ }
+ else
+ LogRel(("GUI: RTLdrGetSymbol(,\"DBGGuiCreate\",) -> %Rrc\n", rc));
+
+ m_pDbgGui = 0;
+ m_pDbgGuiVT = 0;
+ return false;
+}
+
+void UIMachineLogic::dbgDestroy()
+{
+ if (m_pDbgGui)
+ {
+ m_pDbgGuiVT->pfnDestroy(m_pDbgGui);
+ m_pDbgGui = 0;
+ m_pDbgGuiVT = 0;
+ }
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+void UIMachineLogic::reset(bool fShowConfirmation)
+{
+ if (fShowConfirmation)
+ {
+ /* Confirm/Reset current console: */
+ if (msgCenter().confirmResetMachine(machineName()))
+ console().Reset();
+ }
+ else
+ console().Reset();
+
+ /* TODO_NEW_CORE: On reset the additional screens didn't get a display
+ update. Emulate this for now until it get fixed. */
+ ulong uMonitorCount = machine().GetGraphicsAdapter().GetMonitorCount();
+ for (ulong uScreenId = 1; uScreenId < uMonitorCount; ++uScreenId)
+ machineWindows().at(uScreenId)->update();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.h
new file mode 100644
index 00000000..5e1ac083
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.h
@@ -0,0 +1,467 @@
+/* $Id: UIMachineLogic.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogic class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIMachineLogic_h
+#define FEQT_INCLUDED_SRC_runtime_UIMachineLogic_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UISettingsDialog.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QAction;
+class QActionGroup;
+class QIManagerDialog;
+class UISession;
+class UIActionPool;
+class UIKeyboardHandler;
+class UIMouseHandler;
+class UIMachineWindow;
+class UIMachineView;
+class UIDockIconPreview;
+class UISoftKeyboard;
+class UIVMInformationDialog;
+class CSession;
+class CMachine;
+class CConsole;
+class CDisplay;
+class CGuest;
+class CMouse;
+class CKeyboard;
+class CMachineDebugger;
+class CSnapshot;
+class CUSBDevice;
+class CVirtualBoxErrorInfo;
+#if defined(VBOX_WS_X11)
+ struct X11ScreenSaverInhibitMethod;
+#endif
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+typedef struct DBGGUIVT const *PCDBGGUIVT;
+typedef struct DBGGUI *PDBGGUI;
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+/* Machine logic interface: */
+class UIMachineLogic : public QIWithRetranslateUI3<QObject>
+{
+ Q_OBJECT;
+
+ /** Pointer to menu update-handler for this class: */
+ typedef void (UIMachineLogic::*MenuUpdateHandler)(QMenu *pMenu);
+
+signals:
+
+ /** Notifies about frame-buffer resize. */
+ void sigFrameBufferResize();
+
+public:
+
+ /* Factory functions to create/destroy required logic sub-child: */
+ static UIMachineLogic* create(QObject *pParent, UISession *pSession, UIVisualStateType visualStateType);
+ static void destroy(UIMachineLogic *pWhichLogic);
+
+ /* Check if this logic is available: */
+ virtual bool checkAvailability() = 0;
+
+ /** Returns machine-window flags for current machine-logic and passed @a uScreenId. */
+ virtual Qt::WindowFlags windowFlags(ulong uScreenId) const = 0;
+
+ /* Prepare/cleanup the logic: */
+ virtual void prepare();
+ virtual void cleanup();
+
+ void initializePostPowerUp();
+
+ /* Main getters/setters: */
+ UISession* uisession() const { return m_pSession; }
+ UIActionPool* actionPool() const;
+
+ /** Returns the session reference. */
+ CSession& session() const;
+ /** Returns the session's machine reference. */
+ CMachine& machine() const;
+ /** Returns the session's console reference. */
+ CConsole& console() const;
+ /** Returns the console's display reference. */
+ CDisplay& display() const;
+ /** Returns the console's guest reference. */
+ CGuest& guest() const;
+ /** Returns the console's mouse reference. */
+ CMouse& mouse() const;
+ /** Returns the console's keyboard reference. */
+ CKeyboard& keyboard() const;
+ /** Returns the console's debugger reference. */
+ CMachineDebugger& debugger() const;
+
+ /** Returns the machine name. */
+ const QString& machineName() const;
+
+ UIVisualStateType visualStateType() const { return m_visualStateType; }
+ const QList<UIMachineWindow*>& machineWindows() const { return m_machineWindowsList; }
+ UIKeyboardHandler* keyboardHandler() const { return m_pKeyboardHandler; }
+ UIMouseHandler* mouseHandler() const { return m_pMouseHandler; }
+ UIMachineWindow* mainMachineWindow() const;
+ UIMachineWindow* activeMachineWindow() const;
+
+ /** Adjusts machine-window(s) geometry if necessary. */
+ virtual void adjustMachineWindowsGeometry();
+
+ /** Send machine-window(s) size-hint(s) to the guest. */
+ virtual void sendMachineWindowsSizeHints();
+
+ /* Wrapper to open Machine settings / Network page: */
+ void openNetworkSettingsDialog() { sltOpenSettingsDialogNetwork(); }
+
+#ifdef VBOX_WS_MAC
+ void updateDockIcon();
+ void updateDockIconSize(int screenId, int width, int height);
+ UIMachineView* dockPreviewView() const;
+ virtual void updateDock();
+#endif /* VBOX_WS_MAC */
+
+ /** Returns whether VM should perform HID LEDs synchronization. */
+ bool isHidLedsSyncEnabled() const { return m_fIsHidLedsSyncEnabled; }
+ /** An public interface to sltTypeHostKeyComboPressRelease. */
+ void typeHostKeyComboPressRelease(bool fToggleSequence);
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Adjusts relative position for debugger window. */
+ void dbgAdjustRelativePos();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+protected slots:
+
+ /** Handles the VBoxSVC availability change. */
+ void sltHandleVBoxSVCAvailabilityChange();
+
+ /** Checks if some visual-state type was requested. */
+ virtual void sltCheckForRequestedVisualStateType() {}
+
+ /** Requests visual-state change to 'normal' (window). */
+ virtual void sltChangeVisualStateToNormal();
+ /** Requests visual-state change to 'fullscreen'. */
+ virtual void sltChangeVisualStateToFullscreen();
+ /** Requests visual-state change to 'seamless'. */
+ virtual void sltChangeVisualStateToSeamless();
+ /** Requests visual-state change to 'scale'. */
+ virtual void sltChangeVisualStateToScale();
+
+ /* Console callback handlers: */
+ virtual void sltMachineStateChanged();
+ virtual void sltAdditionsStateChanged();
+ virtual void sltMouseCapabilityChanged();
+ virtual void sltKeyboardLedsChanged();
+ virtual void sltUSBDeviceStateChange(const CUSBDevice &device, bool fIsAttached, const CVirtualBoxErrorInfo &error);
+ virtual void sltRuntimeError(bool fIsFatal, const QString &strErrorId, const QString &strMessage);
+#ifdef RT_OS_DARWIN
+ virtual void sltShowWindows();
+#endif /* RT_OS_DARWIN */
+ /** Handles guest-screen count change. */
+ virtual void sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo);
+
+ /** Handles host-screen count change. */
+ virtual void sltHostScreenCountChange();
+ /** Handles host-screen geometry change. */
+ virtual void sltHostScreenGeometryChange();
+ /** Handles host-screen available-area change. */
+ virtual void sltHostScreenAvailableAreaChange();
+
+protected:
+
+ /* Constructor: */
+ UIMachineLogic(QObject *pParent, UISession *pSession, UIVisualStateType visualStateType);
+ /* Destructor: */
+ ~UIMachineLogic();
+
+ /* Protected getters/setters: */
+ bool isMachineWindowsCreated() const { return m_fIsWindowsCreated; }
+ void setMachineWindowsCreated(bool fIsWindowsCreated) { m_fIsWindowsCreated = fIsWindowsCreated; }
+
+ /* Protected members: */
+ void setKeyboardHandler(UIKeyboardHandler *pKeyboardHandler);
+ void setMouseHandler(UIMouseHandler *pMouseHandler);
+ void addMachineWindow(UIMachineWindow *pMachineWindow);
+ void retranslateUi();
+#ifdef VBOX_WS_MAC
+ bool isDockIconPreviewEnabled() const { return m_fIsDockIconEnabled; }
+ void setDockIconPreviewEnabled(bool fIsDockIconPreviewEnabled) { m_fIsDockIconEnabled = fIsDockIconPreviewEnabled; }
+ void updateDockOverlay();
+#endif /* VBOX_WS_MAC */
+
+ /* Prepare helpers: */
+ virtual void prepareRequiredFeatures() {}
+ virtual void prepareSessionConnections();
+ virtual void prepareActionGroups();
+ virtual void prepareActionConnections();
+ virtual void prepareOtherConnections();
+ virtual void prepareHandlers();
+ virtual void prepareMachineWindows() = 0;
+ virtual void prepareMenu() {}
+#ifdef VBOX_WS_MAC
+ virtual void prepareDock();
+#endif /* VBOX_WS_MAC */
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ virtual void prepareDebugger();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+ virtual void loadSettings();
+
+ /* Cleanup helpers: */
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ virtual void cleanupDebugger();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ virtual void cleanupDock();
+#endif /* VBOX_WS_MAC */
+ virtual void cleanupMenu() {}
+ virtual void cleanupMachineWindows() = 0;
+ virtual void cleanupHandlers();
+ //virtual void cleanupOtherConnections() {}
+ virtual void cleanupActionConnections() {}
+ virtual void cleanupActionGroups() {}
+ virtual void cleanupSessionConnections();
+ //virtual void cleanupRequiredFeatures() {}
+
+ /* Handler: Event-filter stuff: */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+
+private slots:
+
+ /** Handle menu prepare. */
+ void sltHandleMenuPrepare(int iIndex, QMenu *pMenu);
+
+ /* "Application" menu functionality: */
+ void sltOpenPreferencesDialog(const QString &strCategory = QString(), const QString &strControl = QString());
+ void sltOpenPreferencesDialogDefault() { sltOpenPreferencesDialog(); }
+ void sltClosePreferencesDialog();
+ void sltClose();
+
+ /* "Machine" menu functionality: */
+ void sltOpenSettingsDialog(const QString &strCategory = QString(), const QString &strControl = QString());
+ void sltOpenSettingsDialogDefault() { sltOpenSettingsDialog(); }
+ void sltCloseSettingsDialog();
+ void sltTakeSnapshot();
+ void sltShowInformationDialog();
+ void sltCloseInformationDialog(bool fAsync = false);
+ void sltCloseInformationDialogDefault() { sltCloseInformationDialog(true); }
+ void sltShowFileManagerDialog();
+ void sltCloseFileManagerDialog();
+ void sltShowLogDialog();
+ void sltCloseLogDialog();
+ void sltPause(bool fOn);
+ void sltReset();
+ void sltDetach();
+ void sltSaveState();
+ void sltShutdown();
+ void sltPowerOff();
+
+ /* "View" menu functionality: */
+ void sltMinimizeActiveMachineWindow();
+ void sltAdjustMachineWindows();
+ void sltToggleGuestAutoresize(bool fEnabled);
+ void sltTakeScreenshot();
+ void sltOpenRecordingOptions();
+ void sltToggleRecording(bool fEnabled);
+ void sltToggleVRDE(bool fEnabled);
+
+ /* "Input" menu functionality: */
+ void sltShowKeyboardSettings();
+ void sltShowSoftKeyboard();
+ void sltCloseSoftKeyboard(bool fAsync = false);
+ void sltCloseSoftKeyboardDefault() { sltCloseSoftKeyboard(true); }
+ void sltTypeCAD();
+#ifdef VBOX_WS_X11
+ void sltTypeCABS();
+#endif /* VBOX_WS_X11 */
+ void sltTypeCtrlBreak();
+ void sltTypeInsert();
+ void sltTypePrintScreen();
+ void sltTypeAltPrintScreen();
+ void sltTypeHostKeyComboPressRelease(bool fToggleSequence);
+ void sltToggleMouseIntegration(bool fEnabled);
+
+ /* "Device" menu functionality: */
+ void sltOpenSettingsDialogStorage();
+ void sltMountStorageMedium();
+ void sltToggleAudioOutput(bool fEnabled);
+ void sltToggleAudioInput(bool fEnabled);
+ void sltOpenSettingsDialogNetwork();
+ void sltOpenSettingsDialogUSBDevices();
+ void sltOpenSettingsDialogSharedFolders();
+ void sltAttachUSBDevice();
+ void sltAttachWebCamDevice();
+ void sltChangeSharedClipboardType(QAction *pAction);
+ void sltToggleNetworkAdapterConnection();
+ void sltChangeDragAndDropType(QAction *pAction);
+ void sltInstallGuestAdditions();
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* "Debug" menu functionality: */
+ void sltShowDebugStatistics();
+ void sltShowDebugCommandLine();
+ void sltLoggingToggled(bool);
+ void sltShowGuestControlConsoleDialog();
+ void sltCloseGuestControlConsoleDialog();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef RT_OS_DARWIN /* Something is *really* broken in regards of the moc here */
+ /* "Window" menu functionality: */
+ void sltSwitchToMachineWindow();
+
+ /* "Dock" menu functionality: */
+ void sltDockPreviewModeChanged(QAction *pAction);
+ void sltDockPreviewMonitorChanged(QAction *pAction);
+ void sltChangeDockIconUpdate(bool fEnabled);
+ /** Handles dock icon overlay change event. */
+ void sltChangeDockIconOverlayAppearance(bool fDisabled);
+ /** Handles dock icon overlay disable action triggering. */
+ void sltDockIconDisableOverlayChanged(bool fDisabled);
+#endif /* RT_OS_DARWIN */
+
+ /* Handlers: Keyboard LEDs sync logic: */
+ void sltHidLedsSyncStateChanged(bool fEnabled);
+ void sltSwitchKeyboardLedsToGuestLeds();
+ void sltSwitchKeyboardLedsToPreviousLeds();
+
+ /* Handle disabling/enabling host screen saver. */
+ void sltDisableHostScreenSaverStateChanged(bool fDisabled);
+
+ /** Handles request for visual state change. */
+ void sltHandleVisualStateChange();
+
+ /** Handles request to commit data. */
+ void sltHandleCommitData();
+
+private:
+
+ /** Update 'Devices' : 'Optical/Floppy Devices' menu routine. */
+ void updateMenuDevicesStorage(QMenu *pMenu);
+ /** Update 'Devices' : 'Network' menu routine. */
+ void updateMenuDevicesNetwork(QMenu *pMenu);
+ /** Update 'Devices' : 'USB Devices' menu routine. */
+ void updateMenuDevicesUSB(QMenu *pMenu);
+ /** Update 'Devices' : 'Web Cams' menu routine. */
+ void updateMenuDevicesWebCams(QMenu *pMenu);
+ /** Update 'Devices' : 'Shared Clipboard' menu routine. */
+ void updateMenuDevicesSharedClipboard(QMenu *pMenu);
+ /** Update 'Devices' : 'Drag and Drop' menu routine. */
+ void updateMenuDevicesDragAndDrop(QMenu *pMenu);
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Update 'Debug' menu routine. */
+ void updateMenuDebug(QMenu *pMenu);
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ /** Update 'Window' menu routine. */
+ void updateMenuWindow(QMenu *pMenu);
+#endif /* VBOX_WS_MAC */
+
+ /** Asks user for the disks encryption passwords. */
+ void askUserForTheDiskEncryptionPasswords();
+
+ /* Helpers: */
+ static int searchMaxSnapshotIndex(const CMachine &machine, const CSnapshot &snapshot, const QString &strNameTemplate);
+ void takeScreenshot(const QString &strFile, const QString &strFormat /* = "png" */) const;
+
+ /** Reactivates the screen saver. This is possbily called during vm window close and enables host screen
+ * if there are no other vms running at the moment. Note that this seems to be not needed on Linux since
+ * closing vm windows re-activates screen saver automatically. On Windows explicit re-activation is needed. */
+ void activateScreenSaver();
+ /* Shows the boot failure dialog through which user can mount a boot DVD and reset the vm. */
+ void showBootFailureDialog();
+ /** Attempts to mount medium with @p uMediumId to the machine if it can find an appropriate controller and port. */
+ bool mountBootMedium(const QUuid &uMediumId);
+ /** Resets the machine. If @p fShowConfirmation is true then a confirmation messag box is shown first. */
+ void reset(bool fShowConfirmation);
+
+ /* Private variables: */
+ UISession *m_pSession;
+ UIVisualStateType m_visualStateType;
+ UIKeyboardHandler *m_pKeyboardHandler;
+ UIMouseHandler *m_pMouseHandler;
+ QList<UIMachineWindow*> m_machineWindowsList;
+
+ QActionGroup *m_pRunningActions;
+ QActionGroup *m_pRunningOrPausedActions;
+ QActionGroup *m_pRunningOrPausedOrStackedActions;
+ QActionGroup *m_pSharedClipboardActions;
+ QActionGroup *m_pDragAndDropActions;
+
+ /** Holds the map of menu update-handlers. */
+ QMap<int, MenuUpdateHandler> m_menuUpdateHandlers;
+
+ bool m_fIsWindowsCreated : 1;
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Debugger functionality: */
+ bool dbgCreated();
+ void dbgDestroy();
+ /* The handle to the debugger GUI: */
+ PDBGGUI m_pDbgGui;
+ /* The virtual method table for the debugger GUI: */
+ PCDBGGUIVT m_pDbgGuiVT;
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+ bool m_fIsDockIconEnabled;
+ UIDockIconPreview *m_pDockIconPreview;
+ QActionGroup *m_pDockPreviewSelectMonitorGroup;
+ QAction *m_pDockSettingsMenuSeparator;
+ int m_DockIconPreviewMonitor;
+ QAction *m_pDockSettingMenuAction;
+ /* Keeps a list of machine menu actions that we add to dock menu. */
+ QList<QAction*> m_dockMachineMenuActions;
+#endif /* VBOX_WS_MAC */
+
+ void *m_pHostLedsState;
+
+ /** Holds whether VM should perform HID LEDs synchronization. */
+ bool m_fIsHidLedsSyncEnabled;
+
+ /** Holds the map of settings dialogs. */
+ QMap<UISettingsDialog::DialogType, UISettingsDialog*> m_settings;
+
+ /** Holds the log viewer dialog instance. */
+ QIManagerDialog *m_pLogViewerDialog;
+ QIManagerDialog *m_pFileManagerDialog;
+ QIManagerDialog *m_pProcessControlDialog;
+ UISoftKeyboard *m_pSoftKeyboardDialog;
+ UIVMInformationDialog *m_pVMInformationDialog;
+
+ /* Holds the cookies returnd by QDBus inhibition calls. Map keys are service name. These are required during uninhibition.*/
+ QMap<QString, uint> m_screenSaverInhibitionCookies;
+#if defined(VBOX_WS_X11)
+ QVector<X11ScreenSaverInhibitMethod*> m_methods;
+#endif
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIMachineLogic_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.cpp
new file mode 100644
index 00000000..2518bad7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.cpp
@@ -0,0 +1,2248 @@
+/* $Id: UIMachineView.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineView class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QBitmap>
+#include <QMainWindow>
+#include <QPainter>
+#include <QScrollBar>
+#include <QTimer>
+#include <QAbstractNativeEventFilter>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIActionPoolRuntime.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UISession.h"
+#include "UIMachineLogic.h"
+#include "UIMachineWindow.h"
+#include "UIMachineViewNormal.h"
+#include "UIMachineViewFullscreen.h"
+#include "UIMachineViewSeamless.h"
+#include "UIMachineViewScale.h"
+#include "UINotificationCenter.h"
+#include "UIKeyboardHandler.h"
+#include "UIMouseHandler.h"
+#include "UIFrameBuffer.h"
+#ifdef VBOX_WS_MAC
+# include "UICocoaApplication.h"
+# include "DarwinKeyboard.h"
+# include "DockIconPreview.h"
+#endif
+#ifdef VBOX_WITH_DRAG_AND_DROP
+# include "UIDnDHandler.h"
+#endif
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CDisplay.h"
+#include "CGraphicsAdapter.h"
+#include "CSession.h"
+#include "CFramebuffer.h"
+#ifdef VBOX_WITH_DRAG_AND_DROP
+# include "CDnDSource.h"
+# include "CDnDTarget.h"
+# include "CGuest.h"
+# include "CGuestDnDSource.h"
+# include "CGuestDnDTarget.h"
+#endif
+
+/* Other VBox includes: */
+#include <VBox/VBoxOGL.h>
+#include <VBoxVideo.h>
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+
+/* External includes: */
+#include <math.h>
+#ifdef VBOX_WITH_DRAG_AND_DROP
+# include <new> /* For bad_alloc. */
+#endif
+#ifdef VBOX_WS_MAC
+# include <Carbon/Carbon.h>
+#endif
+#ifdef VBOX_WS_X11
+# include <xcb/xcb.h>
+#endif
+
+#ifdef DEBUG_andy
+/* Macro for debugging drag and drop actions which usually would
+ * go to Main's logging group. */
+# define DNDDEBUG(x) LogFlowFunc(x)
+#else
+# define DNDDEBUG(x)
+#endif
+
+
+/** QAbstractNativeEventFilter extension
+ * allowing to pre-process native platform events. */
+class UINativeEventFilter : public QAbstractNativeEventFilter
+{
+public:
+
+ /** Constructs native event filter storing @a pParent to redirect events to. */
+ UINativeEventFilter(UIMachineView *pParent)
+ : m_pParent(pParent)
+ {}
+
+ /** Redirects all the native events to parent. */
+#ifdef VBOX_IS_QT6_OR_LATER /* long replaced with qintptr since 6.0 */
+ bool nativeEventFilter(const QByteArray &eventType, void *pMessage, qintptr*)
+#else
+ bool nativeEventFilter(const QByteArray &eventType, void *pMessage, long*)
+#endif
+ {
+ return m_pParent->nativeEventPreprocessor(eventType, pMessage);
+ }
+
+private:
+
+ /** Holds the passed parent reference. */
+ UIMachineView *m_pParent;
+};
+
+
+/* static */
+UIMachineView* UIMachineView::create(UIMachineWindow *pMachineWindow, ulong uScreenId, UIVisualStateType visualStateType)
+{
+ UIMachineView *pMachineView = 0;
+ switch (visualStateType)
+ {
+ case UIVisualStateType_Normal:
+ pMachineView = new UIMachineViewNormal(pMachineWindow, uScreenId);
+ break;
+ case UIVisualStateType_Fullscreen:
+ pMachineView = new UIMachineViewFullscreen(pMachineWindow, uScreenId);
+ break;
+ case UIVisualStateType_Seamless:
+ pMachineView = new UIMachineViewSeamless(pMachineWindow, uScreenId);
+ break;
+ case UIVisualStateType_Scale:
+ pMachineView = new UIMachineViewScale(pMachineWindow, uScreenId);
+ break;
+ default:
+ break;
+ }
+
+ /* Load machine-view settings: */
+ pMachineView->loadMachineViewSettings();
+
+ /* Prepare viewport: */
+ pMachineView->prepareViewport();
+
+ /* Prepare frame-buffer: */
+ pMachineView->prepareFrameBuffer();
+
+ /* Prepare common things: */
+ pMachineView->prepareCommon();
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+ /* Prepare DnD: */
+ /* rc ignored */ pMachineView->prepareDnd();
+#endif
+
+ /* Prepare event-filters: */
+ pMachineView->prepareFilters();
+
+ /* Prepare connections: */
+ pMachineView->prepareConnections();
+
+ /* Prepare console connections: */
+ pMachineView->prepareConsoleConnections();
+
+ /* Initialization: */
+ pMachineView->sltMachineStateChanged();
+ /** @todo Can we move the call to sltAdditionsStateChanged() from the
+ * subclass constructors here too? It is called for Normal and Seamless,
+ * but not for Fullscreen and Scale. However for Scale it is a no op.,
+ * so it would not hurt. Would it hurt for fullscreen? */
+
+ /* Set a preliminary maximum size: */
+ pMachineView->setMaximumGuestSize();
+
+ /* Resend the last resize hint finally: */
+ pMachineView->resendSizeHint();
+
+ /* Return the created view: */
+ return pMachineView;
+}
+
+/* static */
+void UIMachineView::destroy(UIMachineView *pMachineView)
+{
+ if (!pMachineView)
+ return;
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+ /* Cleanup DnD: */
+ pMachineView->cleanupDnd();
+#endif
+
+ /* Cleanup frame-buffer: */
+ pMachineView->cleanupFrameBuffer();
+
+ /* Cleanup native filters: */
+ pMachineView->cleanupNativeFilters();
+
+ delete pMachineView;
+}
+
+void UIMachineView::applyMachineViewScaleFactor()
+{
+ /* Sanity check: */
+ if (!frameBuffer())
+ return;
+
+ /* Acquire selected scale-factor: */
+ double dScaleFactor = gEDataManager->scaleFactor(uiCommon().managedVMUuid(), m_uScreenId);
+
+ /* Take the device-pixel-ratio into account: */
+ frameBuffer()->setDevicePixelRatio(UIDesktopWidgetWatchdog::devicePixelRatio(machineWindow()));
+ frameBuffer()->setDevicePixelRatioActual(UIDesktopWidgetWatchdog::devicePixelRatioActual(machineWindow()));
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ const bool fUseUnscaledHiDPIOutput = dScaleFactor != dDevicePixelRatioActual;
+ dScaleFactor = fUseUnscaledHiDPIOutput ? dScaleFactor : 1.0;
+
+ /* Assign frame-buffer with new values: */
+ frameBuffer()->setScaleFactor(dScaleFactor);
+ frameBuffer()->setUseUnscaledHiDPIOutput(fUseUnscaledHiDPIOutput);
+
+ /* Propagate the scale-factor related attributes to 3D service if necessary: */
+ if (machine().GetGraphicsAdapter().GetAccelerate3DEnabled())
+ {
+ double dScaleFactorFor3D = dScaleFactor;
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ // WORKAROUND:
+ // On Windows and Linux opposing to macOS it's only Qt which can auto scale up,
+ // not 3D overlay itself, so for auto scale-up mode we have to take that into account.
+ if (!fUseUnscaledHiDPIOutput)
+ dScaleFactorFor3D *= frameBuffer()->devicePixelRatioActual();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+ display().NotifyScaleFactorChange(m_uScreenId,
+ (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
+ (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER));
+ display().NotifyHiDPIOutputPolicyChange(fUseUnscaledHiDPIOutput);
+ }
+
+ /* Perform frame-buffer rescaling: */
+ frameBuffer()->performRescale();
+
+ /* Update console's display viewport and 3D overlay: */
+ updateViewport();
+}
+
+UISession *UIMachineView::uisession() const
+{
+ return machineWindow()->uisession();
+}
+
+UIMachineLogic *UIMachineView::machineLogic() const
+{
+ return machineWindow()->machineLogic();
+}
+
+int UIMachineView::contentsWidth() const
+{
+ return frameBuffer()->width();
+}
+
+int UIMachineView::contentsHeight() const
+{
+ return frameBuffer()->height();
+}
+
+int UIMachineView::contentsX() const
+{
+ return horizontalScrollBar()->value();
+}
+
+int UIMachineView::contentsY() const
+{
+ return verticalScrollBar()->value();
+}
+
+int UIMachineView::visibleWidth() const
+{
+ return horizontalScrollBar()->pageStep();
+}
+
+int UIMachineView::visibleHeight() const
+{
+ return verticalScrollBar()->pageStep();
+}
+
+QPoint UIMachineView::viewportToContents(const QPoint &viewportPoint) const
+{
+ /* Get physical contents shifts of scroll-bars: */
+ int iContentsX = contentsX();
+ int iContentsY = contentsY();
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ if (!frameBuffer()->useUnscaledHiDPIOutput())
+ {
+ iContentsX *= dDevicePixelRatioActual;
+ iContentsY *= dDevicePixelRatioActual;
+ }
+ iContentsX /= dDevicePixelRatioFormal;
+ iContentsY /= dDevicePixelRatioFormal;
+
+ /* Return point shifted according scroll-bars: */
+ return QPoint(viewportPoint.x() + iContentsX, viewportPoint.y() + iContentsY);
+}
+
+void UIMachineView::scrollBy(int iDx, int iDy)
+{
+ horizontalScrollBar()->setValue(horizontalScrollBar()->value() + iDx);
+ verticalScrollBar()->setValue(verticalScrollBar()->value() + iDy);
+}
+
+UIVisualStateType UIMachineView::visualStateType() const
+{
+ return machineLogic()->visualStateType();
+}
+
+double UIMachineView::aspectRatio() const
+{
+ return frameBuffer() ? (double)(frameBuffer()->width()) / frameBuffer()->height() : 0;
+}
+
+void UIMachineView::setMaximumGuestSize(const QSize &minimumSizeHint /* = QSize() */)
+{
+ QSize maxSize;
+ switch (m_enmMaximumGuestScreenSizePolicy)
+ {
+ case MaximumGuestScreenSizePolicy_Fixed:
+ maxSize = m_fixedMaxGuestSize;
+ break;
+ case MaximumGuestScreenSizePolicy_Automatic:
+ maxSize = calculateMaxGuestSize().expandedTo(minimumSizeHint);
+ break;
+ case MaximumGuestScreenSizePolicy_Any:
+ /* (0, 0) means any of course. */
+ maxSize = QSize(0, 0);
+ }
+ ASMAtomicWriteU64(&m_u64MaximumGuestSize,
+ RT_MAKE_U64(maxSize.height(), maxSize.width()));
+}
+
+QSize UIMachineView::maximumGuestSize()
+{
+ uint64_t u64Size = ASMAtomicReadU64(&m_u64MaximumGuestSize);
+ return QSize(int(RT_HI_U32(u64Size)), int(RT_LO_U32(u64Size)));
+}
+
+void UIMachineView::updateViewport()
+{
+ display().ViewportChanged(screenId(), contentsX(), contentsY(), visibleWidth(), visibleHeight());
+}
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+int UIMachineView::dragCheckPending()
+{
+ int rc;
+
+ if (!dragAndDropIsActive())
+ rc = VERR_ACCESS_DENIED;
+# ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ else if (!m_fIsDraggingFromGuest)
+ {
+ /// @todo Add guest->guest DnD functionality here by getting
+ // the source of guest B (when copying from B to A).
+ rc = m_pDnDHandler->dragCheckPending(screenId());
+ if (RT_SUCCESS(rc))
+ m_fIsDraggingFromGuest = true;
+ }
+ else /* Already dragging, so report success. */
+ rc = VINF_SUCCESS;
+# else
+ rc = VERR_NOT_SUPPORTED;
+# endif
+
+ DNDDEBUG(("DnD: dragCheckPending ended with rc=%Rrc\n", rc));
+ return rc;
+}
+
+int UIMachineView::dragStart()
+{
+ int rc;
+
+ if (!dragAndDropIsActive())
+ rc = VERR_ACCESS_DENIED;
+# ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ else if (!m_fIsDraggingFromGuest)
+ rc = VERR_WRONG_ORDER;
+ else
+ {
+ /// @todo Add guest->guest DnD functionality here by getting
+ // the source of guest B (when copying from B to A).
+ rc = m_pDnDHandler->dragStart(screenId());
+
+ m_fIsDraggingFromGuest = false;
+ }
+# else
+ rc = VERR_NOT_SUPPORTED;
+# endif
+
+ DNDDEBUG(("DnD: dragStart ended with rc=%Rrc\n", rc));
+ return rc;
+}
+
+int UIMachineView::dragStop()
+{
+ int rc;
+
+ if (!dragAndDropIsActive())
+ rc = VERR_ACCESS_DENIED;
+# ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ else if (!m_fIsDraggingFromGuest)
+ rc = VERR_WRONG_ORDER;
+ else
+ rc = m_pDnDHandler->dragStop(screenId());
+# else
+ rc = VERR_NOT_SUPPORTED;
+# endif
+
+ DNDDEBUG(("DnD: dragStop ended with rc=%Rrc\n", rc));
+ return rc;
+}
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+
+bool UIMachineView::nativeEventPreprocessor(const QByteArray &eventType, void *pMessage)
+{
+ /* Check if some event should be filtered out.
+ * Returning @c true means filtering-out,
+ * Returning @c false means passing event to Qt. */
+
+# if defined(VBOX_WS_MAC)
+
+ /* Make sure it's generic NSEvent: */
+ if (eventType != "mac_generic_NSEvent")
+ return false;
+ EventRef event = static_cast<EventRef>(darwinCocoaToCarbonEvent(pMessage));
+
+ switch (::GetEventClass(event))
+ {
+ // Keep in mind that this stuff should not be enabled while we are still using
+ // own native keyboard filter installed through cocoa API, to be reworked.
+ // S.a. registerForNativeEvents call in UIKeyboardHandler implementation.
+#if 0
+ /* Watch for keyboard-events: */
+ case kEventClassKeyboard:
+ {
+ switch (::GetEventKind(event))
+ {
+ /* Watch for key-events: */
+ case kEventRawKeyDown:
+ case kEventRawKeyRepeat:
+ case kEventRawKeyUp:
+ case kEventRawKeyModifiersChanged:
+ {
+ /* Delegate key-event handling to the keyboard-handler: */
+ return machineLogic()->keyboardHandler()->nativeEventFilter(pMessage, screenId());
+ }
+ default:
+ break;
+ }
+ break;
+ }
+#endif
+ /* Watch for mouse-events: */
+ case kEventClassMouse:
+ {
+ switch (::GetEventKind(event))
+ {
+ /* Watch for button-events: */
+ case kEventMouseDown:
+ case kEventMouseUp:
+ {
+ /* Delegate button-event handling to the mouse-handler: */
+ return machineLogic()->mouseHandler()->nativeEventFilter(pMessage, screenId());
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+# elif defined(VBOX_WS_WIN)
+
+ /* Make sure it's generic MSG event: */
+ if (eventType != "windows_generic_MSG")
+ return false;
+ MSG *pEvent = static_cast<MSG*>(pMessage);
+
+ switch (pEvent->message)
+ {
+ /* Watch for key-events: */
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ {
+ // WORKAROUND:
+ // There is an issue in the Windows Qt5 event processing sequence
+ // causing QAbstractNativeEventFilter to receive Windows native events
+ // coming not just to the top-level window but to actual target as well.
+ // They are calling one - "global event" and another one - "context event".
+ // That way native events are always duplicated with almost no possibility
+ // to distinguish copies except the fact that synthetic event always have
+ // time set to 0 (actually that field was not initialized at all, we had
+ // fixed that in our private Qt tool). We should skip such events instantly.
+ if (pEvent->time == 0)
+ return false;
+
+ /* Delegate key-event handling to the keyboard-handler: */
+ return machineLogic()->keyboardHandler()->nativeEventFilter(pMessage, screenId());
+ }
+ default:
+ break;
+ }
+
+# elif defined(VBOX_WS_X11)
+
+ /* Make sure it's generic XCB event: */
+ if (eventType != "xcb_generic_event_t")
+ return false;
+ xcb_generic_event_t *pEvent = static_cast<xcb_generic_event_t*>(pMessage);
+
+ switch (pEvent->response_type & ~0x80)
+ {
+ /* Watch for key-events: */
+ case XCB_KEY_PRESS:
+ case XCB_KEY_RELEASE:
+ {
+ /* Delegate key-event handling to the keyboard-handler: */
+ return machineLogic()->keyboardHandler()->nativeEventFilter(pMessage, screenId());
+ }
+ /* Watch for button-events: */
+ case XCB_BUTTON_PRESS:
+ case XCB_BUTTON_RELEASE:
+ {
+ /* Delegate button-event handling to the mouse-handler: */
+ return machineLogic()->mouseHandler()->nativeEventFilter(pMessage, screenId());
+ }
+ default:
+ break;
+ }
+
+# else
+
+# warning "port me!"
+
+# endif
+
+ /* Filter nothing by default: */
+ return false;
+}
+
+#ifdef VBOX_WS_MAC
+CGImageRef UIMachineView::vmContentImage()
+{
+ /* Use pause-image if exists: */
+ if (!pausePixmap().isNull())
+ return darwinToCGImageRef(&pausePixmap());
+
+ /* Create the image ref out of the frame-buffer: */
+ return frameBuffertoCGImageRef(frameBuffer());
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineView::sltHandleNotifyChange(int iWidth, int iHeight)
+{
+ /* Sanity check: */
+ if (!frameBuffer())
+ return;
+
+ LogRel2(("GUI: UIMachineView::sltHandleNotifyChange: Screen=%d, Size=%dx%d\n",
+ (unsigned long)m_uScreenId, iWidth, iHeight));
+
+ /* Some situations require frame-buffer resize-events to be ignored at all,
+ * leaving machine-window, machine-view and frame-buffer sizes preserved: */
+ if (uisession()->isGuestResizeIgnored())
+ return;
+
+ /* In some situations especially in some VM states, guest-screen is not drawable: */
+ if (uisession()->isGuestScreenUnDrawable())
+ return;
+
+ /* Get old frame-buffer size: */
+ const QSize frameBufferSizeOld = QSize(frameBuffer()->width(),
+ frameBuffer()->height());
+
+ /* Perform frame-buffer mode-change: */
+ frameBuffer()->handleNotifyChange(iWidth, iHeight);
+
+ /* Get new frame-buffer size: */
+ const QSize frameBufferSizeNew = QSize(frameBuffer()->width(),
+ frameBuffer()->height());
+
+ /* For 'scale' mode: */
+ if (visualStateType() == UIVisualStateType_Scale)
+ {
+ /* Assign new frame-buffer logical-size: */
+ QSize scaledSize = size();
+ const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ scaledSize *= dDevicePixelRatioFormal;
+ if (!frameBuffer()->useUnscaledHiDPIOutput())
+ scaledSize /= dDevicePixelRatioActual;
+ frameBuffer()->setScaledSize(scaledSize);
+
+ /* Forget the last full-screen size: */
+ uisession()->setLastFullScreenSize(screenId(), QSize(-1, -1));
+ }
+ /* For other than 'scale' mode: */
+ else
+ {
+ /* Adjust maximum-size restriction for machine-view: */
+ setMaximumSize(sizeHint());
+
+ /* Disable the resize hint override hack and forget the last full-screen size: */
+ m_sizeHintOverride = QSize(-1, -1);
+ if (visualStateType() == UIVisualStateType_Normal)
+ uisession()->setLastFullScreenSize(screenId(), QSize(-1, -1));
+
+ /* Force machine-window update own layout: */
+ QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
+
+ /* Update machine-view sliders: */
+ updateSliders();
+
+ /* By some reason Win host forgets to update machine-window central-widget
+ * after main-layout was updated, let's do it for all the hosts: */
+ machineWindow()->centralWidget()->update();
+
+ /* Normalize 'normal' machine-window geometry if necessary: */
+ if (visualStateType() == UIVisualStateType_Normal &&
+ frameBufferSizeNew != frameBufferSizeOld)
+ machineWindow()->normalizeGeometry(true /* adjust position */, machineWindow()->shouldResizeToGuestDisplay());
+ }
+
+ /* Perform frame-buffer rescaling: */
+ frameBuffer()->performRescale();
+
+#ifdef VBOX_WS_MAC
+ /* Update MacOS X dock icon size: */
+ machineLogic()->updateDockIconSize(screenId(), frameBufferSizeNew.width(), frameBufferSizeNew.height());
+#endif /* VBOX_WS_MAC */
+
+ /* Notify frame-buffer resize: */
+ emit sigFrameBufferResize();
+
+ /* Ask for just required guest display update (it will also update
+ * the viewport through IFramebuffer::NotifyUpdate): */
+ display().InvalidateAndUpdateScreen(m_uScreenId);
+
+ /* If we are in normal or scaled mode and if GA are active,
+ * remember the guest-screen size to be able to restore it when necessary: */
+ /* As machines with Linux/Solaris and VMSVGA are not able to tell us
+ * whether a resize was due to the system or user interaction we currently
+ * do not store hints for these systems except when we explicitly send them
+ * ourselves. Windows guests should use VBoxVGA controllers, not VMSVGA. */
+ if ( !isFullscreenOrSeamless()
+ && uisession()->isGuestSupportsGraphics()
+ && (machine().GetGraphicsAdapter().GetGraphicsControllerType() != KGraphicsControllerType_VMSVGA))
+ setStoredGuestScreenSizeHint(frameBufferSizeNew);
+
+ LogRel2(("GUI: UIMachineView::sltHandleNotifyChange: Complete for Screen=%d, Size=%dx%d\n",
+ (unsigned long)m_uScreenId, frameBufferSizeNew.width(), frameBufferSizeNew.height()));
+}
+
+void UIMachineView::sltHandleNotifyUpdate(int iX, int iY, int iWidth, int iHeight)
+{
+ /* Sanity check: */
+ if (!frameBuffer())
+ return;
+
+ /* Prepare corresponding viewport part: */
+ QRect rect(iX, iY, iWidth, iHeight);
+
+ /* Take the scaling into account: */
+ const double dScaleFactor = frameBuffer()->scaleFactor();
+ const QSize scaledSize = frameBuffer()->scaledSize();
+ if (scaledSize.isValid())
+ {
+ /* Calculate corresponding scale-factors: */
+ const double xScaleFactor = visualStateType() == UIVisualStateType_Scale ?
+ (double)scaledSize.width() / frameBuffer()->width() : dScaleFactor;
+ const double yScaleFactor = visualStateType() == UIVisualStateType_Scale ?
+ (double)scaledSize.height() / frameBuffer()->height() : dScaleFactor;
+ /* Adjust corresponding viewport part: */
+ rect.moveTo((int)floor((double)rect.x() * xScaleFactor) - 1,
+ (int)floor((double)rect.y() * yScaleFactor) - 1);
+ rect.setSize(QSize((int)ceil((double)rect.width() * xScaleFactor) + 2,
+ (int)ceil((double)rect.height() * yScaleFactor) + 2));
+ }
+
+ /* Shift has to be scaled by the device-pixel-ratio
+ * but not scaled by the scale-factor. */
+ rect.translate(-contentsX(), -contentsY());
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ if (!frameBuffer()->useUnscaledHiDPIOutput() && dDevicePixelRatioActual != 1.0)
+ {
+ rect.moveTo((int)floor((double)rect.x() * dDevicePixelRatioActual) - 1,
+ (int)floor((double)rect.y() * dDevicePixelRatioActual) - 1);
+ rect.setSize(QSize((int)ceil((double)rect.width() * dDevicePixelRatioActual) + 2,
+ (int)ceil((double)rect.height() * dDevicePixelRatioActual) + 2));
+ }
+ if (dDevicePixelRatioFormal != 1.0)
+ {
+ rect.moveTo((int)floor((double)rect.x() / dDevicePixelRatioFormal) - 1,
+ (int)floor((double)rect.y() / dDevicePixelRatioFormal) - 1);
+ rect.setSize(QSize((int)ceil((double)rect.width() / dDevicePixelRatioFormal) + 2,
+ (int)ceil((double)rect.height() / dDevicePixelRatioFormal) + 2));
+ }
+
+ /* Limit the resulting part by the viewport rectangle: */
+ rect &= viewport()->rect();
+
+ /* Update corresponding viewport part: */
+ viewport()->update(rect);
+}
+
+void UIMachineView::sltHandleSetVisibleRegion(QRegion region)
+{
+ /* Used only in seamless-mode. */
+ Q_UNUSED(region);
+}
+
+void UIMachineView::sltPerformGuestResize(const QSize &toSize)
+{
+ /* There is a couple of things to keep in mind:
+ *
+ * First of all, passed size can be invalid (or even not sane one, where one of values equal to zero). Usually that happens
+ * if this function being invoked with default argument for example by some slot. In such case we get the available size for
+ * the guest-screen we have. We assume here that centralWidget() contains this view only and gives it all available space.
+ * In all other cases we have a valid non-zero size which should be handled as usual.
+ *
+ * Besides that, passed size or size taken from centralWidget() is _not_ absolute one, it's in widget's coordinate system
+ * which can and will be be transformed by scale-factor when appropriate, so before passing this size to a guest it has to
+ * be scaled backward. This is the key aspect in which internal resize differs from resize initiated from the outside. */
+
+ /* Make sure we have valid size to work with: */
+ QSize size( toSize.isValid() && toSize.width() > 0 && toSize.height() > 0
+ ? toSize : machineWindow()->centralWidget()->size());
+ AssertMsgReturnVoid(size.isValid() && size.width() > 0 && size.height() > 0,
+ ("Size should be valid (%dx%d)!\n", size.width(), size.height()));
+
+ /* Take the scale-factor(s) into account: */
+ size = scaledBackward(size);
+
+ /* Update current window size limitations: */
+ setMaximumGuestSize(size);
+
+ /* Record the hint to extra data, needed for guests using VMSVGA:
+ * This should be done before the actual hint is sent in case the guest overrides it.
+ * Do not send a hint if nothing has changed to prevent the guest being notified about its own changes. */
+ if ( !isFullscreenOrSeamless()
+ && uisession()->isGuestSupportsGraphics()
+ && ( (int)frameBuffer()->width() != size.width()
+ || (int)frameBuffer()->height() != size.height()
+ || uisession()->isScreenVisible(screenId()) != uisession()->isScreenVisibleHostDesires(screenId())))
+ setStoredGuestScreenSizeHint(size);
+
+ /* If auto-mount of guest-screens (auto-pilot) enabled: */
+ if (gEDataManager->autoMountGuestScreensEnabled(uiCommon().managedVMUuid()))
+ {
+ /* If host and guest have same opinion about guest-screen visibility: */
+ if (uisession()->isScreenVisible(screenId()) == uisession()->isScreenVisibleHostDesires(screenId()))
+ {
+ /* Do not send a hint if nothing has changed to prevent the guest being notified about its own changes: */
+ if ((int)frameBuffer()->width() != size.width() || (int)frameBuffer()->height() != size.height())
+ {
+ LogRel(("GUI: UIMachineView::sltPerformGuestResize: Auto-pilot resizing screen %d as %dx%d\n",
+ (int)screenId(), size.width(), size.height()));
+ display().SetVideoModeHint(screenId(),
+ uisession()->isScreenVisible(screenId()),
+ false /* change origin? */,
+ 0 /* origin x */,
+ 0 /* origin y */,
+ size.width(),
+ size.height(),
+ 0 /* bits per pixel */,
+ true /* notify? */);
+ }
+ }
+ else
+ {
+ /* If host desires to have guest-screen enabled and guest-screen is disabled, retrying: */
+ if (uisession()->isScreenVisibleHostDesires(screenId()))
+ {
+ /* Send enabling size-hint to the guest: */
+ LogRel(("GUI: UIMachineView::sltPerformGuestResize: Auto-pilot enabling guest-screen %d\n", (int)screenId()));
+ display().SetVideoModeHint(screenId(),
+ true /* enabled? */,
+ false /* change origin? */,
+ 0 /* origin x */,
+ 0 /* origin y */,
+ size.width(),
+ size.height(),
+ 0 /* bits per pixel */,
+ true /* notify? */);
+ }
+ /* If host desires to have guest-screen disabled and guest-screen is enabled, retrying: */
+ else
+ {
+ /* Send disabling size-hint to the guest: */
+ LogRel(("GUI: UIMachineView::sltPerformGuestResize: Auto-pilot disabling guest-screen %d\n", (int)screenId()));
+ display().SetVideoModeHint(screenId(),
+ false /* enabled? */,
+ false /* change origin? */,
+ 0 /* origin x */,
+ 0 /* origin y */,
+ 0 /* width */,
+ 0 /* height */,
+ 0 /* bits per pixel */,
+ true /* notify? */);
+ }
+ }
+ }
+ /* If auto-mount of guest-screens (auto-pilot) disabled: */
+ else
+ {
+ /* Should we send a hint? */
+ bool fSendHint = true;
+ /* Do not send a hint if nothing has changed to prevent the guest being notified about its own changes: */
+ if (fSendHint && (int)frameBuffer()->width() == size.width() && (int)frameBuffer()->height() == size.height())
+ {
+ LogRel(("GUI: UIMachineView::sltPerformGuestResize: Omitting to send size-hint %dx%d to guest-screen %d "
+ "because frame-buffer is already of the same size.\n", size.width(), size.height(), (int)screenId()));
+ fSendHint = false;
+ }
+ /* Do not send a hint if GA supports graphics and we have sent that hint already: */
+ if (fSendHint && uisession()->isGuestSupportsGraphics() && m_lastSizeHint == size)
+ {
+ LogRel(("GUI: UIMachineView::sltPerformGuestResize: Omitting to send size-hint %dx%d to guest-screen %d "
+ "because this hint was previously sent.\n", size.width(), size.height(), (int)screenId()));
+ fSendHint = false;
+ }
+ if (fSendHint)
+ {
+ LogRel(("GUI: UIMachineView::sltPerformGuestResize: Sending guest size-hint to screen %d as %dx%d\n",
+ (int)screenId(), size.width(), size.height()));
+ display().SetVideoModeHint(screenId(),
+ uisession()->isScreenVisible(screenId()),
+ false /* change origin? */,
+ 0 /* origin x */,
+ 0 /* origin y */,
+ size.width(),
+ size.height(),
+ 0 /* bits per pixel */,
+ true /* notify? */);
+ m_lastSizeHint = size;
+ }
+ }
+}
+
+void UIMachineView::sltHandleActionTriggerViewScreenToggle(int iScreen, bool fEnabled)
+{
+ /* Skip unrelated guest-screen index: */
+ if (iScreen != (int)screenId())
+ return;
+
+ /* Acquire current resolution: */
+ ULONG uWidth, uHeight, uBitsPerPixel;
+ LONG uOriginX, uOriginY;
+ KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
+ display().GetScreenResolution(screenId(), uWidth, uHeight, uBitsPerPixel, uOriginX, uOriginY, monitorStatus);
+ if (!display().isOk())
+ {
+ UINotificationMessage::cannotAcquireDispayParameter(display());
+ return;
+ }
+
+ /* Update desirable screen status: */
+ uisession()->setScreenVisibleHostDesires(screenId(), fEnabled);
+
+ /* Send enabling size-hint: */
+ if (fEnabled)
+ {
+ /* Defaults: */
+ if (!uWidth)
+ uWidth = 800;
+ if (!uHeight)
+ uHeight = 600;
+
+ /* Update current window size limitations: */
+ setMaximumGuestSize(QSize(uWidth, uHeight));
+
+ /* Record the hint to extra data, needed for guests using VMSVGA:
+ * This should be done before the actual hint is sent in case the guest overrides it.
+ * Do not send a hint if nothing has changed to prevent the guest being notified about its own changes. */
+ if ( !isFullscreenOrSeamless()
+ && uisession()->isGuestSupportsGraphics()
+ && ( frameBuffer()->width() != uWidth
+ || frameBuffer()->height() != uHeight
+ || uisession()->isScreenVisible(screenId()) != uisession()->isScreenVisibleHostDesires(screenId())))
+ setStoredGuestScreenSizeHint(QSize(uWidth, uHeight));
+
+ /* Send enabling size-hint to the guest: */
+ LogRel(("GUI: UIMachineView::sltHandleActionTriggerViewScreenToggle: Enabling guest-screen %d\n", (int)screenId()));
+ display().SetVideoModeHint(screenId(),
+ true /* enabled? */,
+ false /* change origin? */,
+ 0 /* origin x */,
+ 0 /* origin y */,
+ uWidth,
+ uHeight,
+ 0 /* bits per pixel */,
+ true /* notify? */);
+ }
+ else
+ {
+ /* Send disabling size-hint to the guest: */
+ LogRel(("GUI: UIMachineView::sltHandleActionTriggerViewScreenToggle: Disabling guest-screen %d\n", (int)screenId()));
+ display().SetVideoModeHint(screenId(),
+ false /* enabled? */,
+ false /* change origin? */,
+ 0 /* origin x */,
+ 0 /* origin y */,
+ 0 /* width */,
+ 0 /* height */,
+ 0 /* bits per pixel */,
+ true /* notify? */);
+ }
+}
+
+void UIMachineView::sltHandleActionTriggerViewScreenResize(int iScreen, const QSize &size)
+{
+ /* Skip unrelated guest-screen index: */
+ if (iScreen != (int)m_uScreenId)
+ return;
+
+ /* Make sure we have valid size to work with: */
+ AssertMsgReturnVoid(size.isValid() && size.width() > 0 && size.height() > 0,
+ ("Size should be valid (%dx%d)!\n", size.width(), size.height()));
+
+ /* Update current window size limitations: */
+ setMaximumGuestSize(size);
+
+ /* Record the hint to extra data, needed for guests using VMSVGA:
+ * This should be done before the actual hint is sent in case the guest overrides it.
+ * Do not send a hint if nothing has changed to prevent the guest being notified about its own changes. */
+ if ( !isFullscreenOrSeamless()
+ && uisession()->isGuestSupportsGraphics()
+ && ( (int)frameBuffer()->width() != size.width()
+ || (int)frameBuffer()->height() != size.height()
+ || uisession()->isScreenVisible(screenId()) != uisession()->isScreenVisibleHostDesires(screenId())))
+ setStoredGuestScreenSizeHint(size);
+
+ /* Send enabling size-hint to the guest: */
+ LogRel(("GUI: UIMachineView::sltHandleActionTriggerViewScreenResize: Resizing guest-screen %d\n", (int)screenId()));
+ display().SetVideoModeHint(screenId(),
+ true /* enabled? */,
+ false /* change origin? */,
+ 0 /* origin x */,
+ 0 /* origin y */,
+ size.width(),
+ size.height(),
+ 0 /* bits per pixel */,
+ true /* notify? */);
+}
+
+void UIMachineView::sltDesktopResized()
+{
+ setMaximumGuestSize();
+}
+
+void UIMachineView::sltHandleScaleFactorChange(const QUuid &uMachineID)
+{
+ /* Skip unrelated machine IDs: */
+ if (uMachineID != uiCommon().managedVMUuid())
+ return;
+
+ /* Acquire selected scale-factor: */
+ double dScaleFactor = gEDataManager->scaleFactor(uiCommon().managedVMUuid(), m_uScreenId);
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ const bool fUseUnscaledHiDPIOutput = dScaleFactor != dDevicePixelRatioActual;
+ dScaleFactor = fUseUnscaledHiDPIOutput ? dScaleFactor : 1.0;
+
+ /* Assign frame-buffer with new values: */
+ frameBuffer()->setScaleFactor(dScaleFactor);
+ frameBuffer()->setUseUnscaledHiDPIOutput(fUseUnscaledHiDPIOutput);
+
+ /* Propagate the scale-factor related attributes to 3D service if necessary: */
+ if (machine().GetGraphicsAdapter().GetAccelerate3DEnabled())
+ {
+ double dScaleFactorFor3D = dScaleFactor;
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ // WORKAROUND:
+ // On Windows and Linux opposing to macOS it's only Qt which can auto scale up,
+ // not 3D overlay itself, so for auto scale-up mode we have to take that into account.
+ if (!fUseUnscaledHiDPIOutput)
+ dScaleFactorFor3D *= frameBuffer()->devicePixelRatioActual();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+ display().NotifyScaleFactorChange(m_uScreenId,
+ (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
+ (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER));
+ display().NotifyHiDPIOutputPolicyChange(fUseUnscaledHiDPIOutput);
+ }
+
+ /* Handle scale attributes change: */
+ handleScaleChange();
+ /* Adjust guest-screen size: */
+ adjustGuestScreenSize();
+
+ /* Update scaled pause pixmap, if necessary: */
+ updateScaledPausePixmap();
+ viewport()->update();
+
+ /* Update console's display viewport and 3D overlay: */
+ updateViewport();
+}
+
+void UIMachineView::sltHandleScalingOptimizationChange(const QUuid &uMachineID)
+{
+ /* Skip unrelated machine IDs: */
+ if (uMachineID != uiCommon().managedVMUuid())
+ return;
+
+ /* Take the scaling-optimization type into account: */
+ frameBuffer()->setScalingOptimizationType(gEDataManager->scalingOptimizationType(uiCommon().managedVMUuid()));
+
+ /* Update viewport: */
+ viewport()->update();
+}
+
+void UIMachineView::sltMachineStateChanged()
+{
+ /* Get machine state: */
+ KMachineState state = uisession()->machineState();
+ switch (state)
+ {
+ case KMachineState_Paused:
+ case KMachineState_TeleportingPausedVM:
+ {
+ if ( frameBuffer()
+ && ( state != KMachineState_TeleportingPausedVM
+ || m_previousState != KMachineState_Teleporting))
+ {
+ // WORKAROUND:
+ // We can't take pause pixmap if actual state is Saving, this produces
+ // a lock and GUI will be frozen until SaveState call is complete...
+ const KMachineState enmActualState = machine().GetState();
+ if (enmActualState != KMachineState_Saving)
+ {
+ /* Take live pause-pixmap: */
+ takePausePixmapLive();
+ /* Fully repaint to pick up pause-pixmap: */
+ viewport()->update();
+ }
+ }
+ break;
+ }
+ case KMachineState_Restoring:
+ {
+ /* Only works with the primary screen currently. */
+ if (screenId() == 0)
+ {
+ /* Take snapshot pause-pixmap: */
+ takePausePixmapSnapshot();
+ /* Fully repaint to pick up pause-pixmap: */
+ viewport()->update();
+ }
+ break;
+ }
+ case KMachineState_Running:
+ {
+ if (m_previousState == KMachineState_Paused ||
+ m_previousState == KMachineState_TeleportingPausedVM ||
+ m_previousState == KMachineState_Restoring)
+ {
+ if (frameBuffer())
+ {
+ /* Reset pause-pixmap: */
+ resetPausePixmap();
+ /* Ask for full guest display update (it will also update
+ * the viewport through IFramebuffer::NotifyUpdate): */
+ display().InvalidateAndUpdate();
+ }
+ }
+ /* Reapply machine-view scale-factor: */
+ applyMachineViewScaleFactor();
+ break;
+ }
+ default:
+ break;
+ }
+
+ m_previousState = state;
+}
+
+void UIMachineView::sltMousePointerShapeChange()
+{
+ /* Fetch the shape and the mask: */
+ QPixmap pixmapShape = uisession()->cursorShapePixmap();
+ QPixmap pixmapMask = uisession()->cursorMaskPixmap();
+ const QPoint hotspot = uisession()->cursorHotspot();
+ uint uXHot = hotspot.x();
+ uint uYHot = hotspot.y();
+
+ /* If there is no mask: */
+ if (pixmapMask.isNull())
+ {
+ /* Scale the shape pixmap and
+ * compose the cursor on the basis of shape only: */
+ updateMousePointerPixmapScaling(pixmapShape, uXHot, uYHot);
+ m_cursor = QCursor(pixmapShape, uXHot, uYHot);
+ }
+ /* Otherwise: */
+ else
+ {
+ /* Scale the shape and the mask pixmaps and
+ * compose the cursor on the basis of shape and mask both: */
+ updateMousePointerPixmapScaling(pixmapShape, uXHot, uYHot);
+ /// @todo updateMousePointerPixmapScaling(pixmapMask, uXHot, uYHot);
+#ifdef VBOX_IS_QT6_OR_LATER /* since qt6 explicit constructor is replaced with QBitmap::fromPixmap static method */
+ m_cursor = QCursor(QBitmap::fromPixmap(pixmapShape), QBitmap::fromPixmap(pixmapMask), uXHot, uYHot);
+#else
+ m_cursor = QCursor(pixmapShape, pixmapMask, uXHot, uYHot);
+#endif
+ }
+
+ /* Let the listeners know: */
+ emit sigMousePointerShapeChange();
+}
+
+void UIMachineView::sltDetachCOM()
+{
+#ifdef VBOX_WITH_DRAG_AND_DROP
+ /* Cleanup DnD: */
+ cleanupDnd();
+#endif
+}
+
+UIMachineView::UIMachineView(UIMachineWindow *pMachineWindow, ulong uScreenId)
+ : QAbstractScrollArea(pMachineWindow->centralWidget())
+ , m_pMachineWindow(pMachineWindow)
+ , m_uScreenId(uScreenId)
+ , m_pFrameBuffer(0)
+ , m_previousState(KMachineState_Null)
+ , m_iHostScreenNumber(0)
+ , m_enmMaximumGuestScreenSizePolicy(MaximumGuestScreenSizePolicy_Automatic)
+ , m_u64MaximumGuestSize(0)
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ , m_fIsDraggingFromGuest(false)
+#endif
+ , m_pNativeEventFilter(0)
+{
+}
+
+void UIMachineView::loadMachineViewSettings()
+{
+ /* Global settings: */
+ {
+ /* Remember the maximum guest size policy for
+ * telling the guest about video modes we like: */
+ m_enmMaximumGuestScreenSizePolicy = gEDataManager->maxGuestResolutionPolicy();
+ if (m_enmMaximumGuestScreenSizePolicy == MaximumGuestScreenSizePolicy_Fixed)
+ m_fixedMaxGuestSize = gEDataManager->maxGuestResolutionForPolicyFixed();
+ }
+}
+
+void UIMachineView::prepareViewport()
+{
+ /* Prepare viewport: */
+ AssertPtrReturnVoid(viewport());
+ {
+ /* Enable manual painting: */
+ viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
+ /* Enable multi-touch support: */
+ viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
+ }
+}
+
+void UIMachineView::prepareFrameBuffer()
+{
+ /* Check whether we already have corresponding frame-buffer: */
+ UIFrameBuffer *pFrameBuffer = uisession()->frameBuffer(screenId());
+
+ /* If we do: */
+ if (pFrameBuffer)
+ {
+ /* Assign it's view: */
+ pFrameBuffer->setView(this);
+ /* Mark frame-buffer as used again: */
+ LogRelFlow(("GUI: UIMachineView::prepareFrameBuffer: Start EMT callbacks accepting for screen: %d\n", screenId()));
+ pFrameBuffer->setMarkAsUnused(false);
+ /* And remember our choice: */
+ m_pFrameBuffer = pFrameBuffer;
+ }
+ /* If we do not: */
+ else
+ {
+ /* Create new frame-buffer: */
+ m_pFrameBuffer = new UIFrameBuffer;
+ frameBuffer()->init(this);
+
+ /* Take scaling optimization type into account: */
+ frameBuffer()->setScalingOptimizationType(gEDataManager->scalingOptimizationType(uiCommon().managedVMUuid()));
+
+ /* Acquire selected scale-factor: */
+ double dScaleFactor = gEDataManager->scaleFactor(uiCommon().managedVMUuid(), m_uScreenId);
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioFormal = UIDesktopWidgetWatchdog::devicePixelRatio(machineWindow());
+ const double dDevicePixelRatioActual = UIDesktopWidgetWatchdog::devicePixelRatioActual(machineWindow());
+ const bool fUseUnscaledHiDPIOutput = dScaleFactor != dDevicePixelRatioActual;
+ dScaleFactor = fUseUnscaledHiDPIOutput ? dScaleFactor : 1.0;
+
+ /* Assign frame-buffer with new values: */
+ frameBuffer()->setDevicePixelRatio(dDevicePixelRatioFormal);
+ frameBuffer()->setDevicePixelRatioActual(dDevicePixelRatioActual);
+ frameBuffer()->setScaleFactor(dScaleFactor);
+ frameBuffer()->setUseUnscaledHiDPIOutput(fUseUnscaledHiDPIOutput);
+
+ /* Propagate the scale-factor related attributes to 3D service if necessary: */
+ if (machine().GetGraphicsAdapter().GetAccelerate3DEnabled())
+ {
+ double dScaleFactorFor3D = dScaleFactor;
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ // WORKAROUND:
+ // On Windows and Linux opposing to macOS it's only Qt which can auto scale up,
+ // not 3D overlay itself, so for auto scale-up mode we have to take that into account.
+ if (!fUseUnscaledHiDPIOutput)
+ dScaleFactorFor3D *= dDevicePixelRatioActual;
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+ display().NotifyScaleFactorChange(m_uScreenId,
+ (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
+ (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER));
+ display().NotifyHiDPIOutputPolicyChange(fUseUnscaledHiDPIOutput);
+ }
+
+ /* Perform frame-buffer rescaling: */
+ frameBuffer()->performRescale();
+
+ /* Associate uisession with frame-buffer finally: */
+ uisession()->setFrameBuffer(screenId(), frameBuffer());
+ }
+
+ /* Make sure frame-buffer was prepared: */
+ AssertReturnVoid(frameBuffer());
+
+ /* Reattach to IDisplay: */
+ frameBuffer()->detach();
+ frameBuffer()->attach();
+
+ /* Calculate frame-buffer size: */
+ QSize size;
+ {
+#ifdef VBOX_WS_X11
+ /* Processing pseudo resize-event to synchronize frame-buffer with stored framebuffer size.
+ * On X11 this will be additional done when the machine state was 'saved'. */
+ if (machine().GetState() == KMachineState_Saved || machine().GetState() == KMachineState_AbortedSaved)
+ size = storedGuestScreenSizeHint();
+#endif /* VBOX_WS_X11 */
+
+ /* If there is a preview image saved,
+ * we will resize the framebuffer to the size of that image: */
+ ULONG uWidth = 0, uHeight = 0;
+ QVector<KBitmapFormat> formats = machine().QuerySavedScreenshotInfo(0, uWidth, uHeight);
+ if (formats.size() > 0)
+ {
+ /* Init with the screenshot size: */
+ size = QSize(uWidth, uHeight);
+ /* Try to get the real guest dimensions from the save-state: */
+ ULONG uGuestOriginX = 0, uGuestOriginY = 0, uGuestWidth = 0, uGuestHeight = 0;
+ BOOL fEnabled = true;
+ machine().QuerySavedGuestScreenInfo(m_uScreenId, uGuestOriginX, uGuestOriginY, uGuestWidth, uGuestHeight, fEnabled);
+ if (uGuestWidth > 0 && uGuestHeight > 0)
+ size = QSize(uGuestWidth, uGuestHeight);
+ }
+
+ /* If we have a valid size, resize/rescale the frame-buffer. */
+ if (size.width() > 0 && size.height() > 0)
+ {
+ frameBuffer()->performResize(size.width(), size.height());
+ frameBuffer()->performRescale();
+ }
+ }
+}
+
+void UIMachineView::prepareCommon()
+{
+ /* Prepare view frame: */
+ setFrameStyle(QFrame::NoFrame);
+
+ /* Setup palette: */
+ QPalette palette(viewport()->palette());
+ palette.setColor(viewport()->backgroundRole(), Qt::black);
+ viewport()->setPalette(palette);
+
+ /* Setup focus policy: */
+ setFocusPolicy(Qt::WheelFocus);
+}
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+int UIMachineView::prepareDnd(void)
+{
+ /* Enable drag & drop: */
+ setAcceptDrops(true);
+
+ int vrc;
+
+ /* Create the drag and drop handler instance: */
+ m_pDnDHandler = new UIDnDHandler(uisession(), this /* pParent */);
+ if (m_pDnDHandler)
+ {
+ vrc = m_pDnDHandler->init();
+ }
+ else
+ vrc = VERR_NO_MEMORY;
+
+ if (RT_FAILURE(vrc))
+ LogRel(("DnD: Initialization failed with %Rrc\n", vrc));
+ return vrc;
+}
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+
+void UIMachineView::prepareFilters()
+{
+ /* Enable MouseMove events: */
+ viewport()->setMouseTracking(true);
+
+ /* We have to watch for own events too: */
+ installEventFilter(this);
+
+ /* QScrollView does the below on its own, but let's
+ * do it anyway for the case it will not do it in the future: */
+ viewport()->installEventFilter(this);
+
+ /* We want to be notified on some parent's events: */
+ machineWindow()->installEventFilter(this);
+}
+
+void UIMachineView::prepareConnections()
+{
+ /* UICommon connections: */
+ connect(&uiCommon(), &UICommon::sigAskToDetachCOM, this, &UIMachineView::sltDetachCOM);
+ /* Desktop resolution change (e.g. monitor hotplug): */
+ connect(gpDesktop, &UIDesktopWidgetWatchdog::sigHostScreenResized,
+ this, &UIMachineView::sltDesktopResized);
+ /* Scale-factor change: */
+ connect(gEDataManager, &UIExtraDataManager::sigScaleFactorChange,
+ this, &UIMachineView::sltHandleScaleFactorChange);
+ /* Scaling-optimization change: */
+ connect(gEDataManager, &UIExtraDataManager::sigScalingOptimizationTypeChange,
+ this, &UIMachineView::sltHandleScalingOptimizationChange);
+ /* Action-pool connections: */
+ UIActionPoolRuntime *pActionPoolRuntime = qobject_cast<UIActionPoolRuntime*>(actionPool());
+ if (pActionPoolRuntime)
+ {
+ connect(pActionPoolRuntime, &UIActionPoolRuntime::sigNotifyAboutTriggeringViewScreenToggle,
+ this, &UIMachineView::sltHandleActionTriggerViewScreenToggle);
+ connect(pActionPoolRuntime, &UIActionPoolRuntime::sigNotifyAboutTriggeringViewScreenResize,
+ this, &UIMachineView::sltHandleActionTriggerViewScreenResize);
+ }
+}
+
+void UIMachineView::prepareConsoleConnections()
+{
+ /* Machine state-change updater: */
+ connect(uisession(), &UISession::sigMachineStateChange, this, &UIMachineView::sltMachineStateChanged);
+ /* Mouse pointer shape updater: */
+ connect(uisession(), &UISession::sigMousePointerShapeChange, this, &UIMachineView::sltMousePointerShapeChange);
+}
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+void UIMachineView::cleanupDnd()
+{
+ delete m_pDnDHandler;
+ m_pDnDHandler = 0;
+}
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+
+void UIMachineView::cleanupFrameBuffer()
+{
+ /* Make sure framebuffer assigned at all: */
+ if (!frameBuffer())
+ return;
+
+ /* Make sure proper framebuffer assigned: */
+ AssertReturnVoid(frameBuffer() == uisession()->frameBuffer(screenId()));
+
+ /* Mark framebuffer as unused: */
+ LogRelFlow(("GUI: UIMachineView::cleanupFrameBuffer: Stop EMT callbacks accepting for screen: %d\n", screenId()));
+ frameBuffer()->setMarkAsUnused(true);
+
+ /* Process pending framebuffer events: */
+ QApplication::sendPostedEvents(this, QEvent::MetaCall);
+
+ /* Temporarily detach the framebuffer from IDisplay before detaching
+ * from view in order to respect the thread synchonisation logic (see UIFrameBuffer.h).
+ * Note: VBOX_WITH_CROGL additionally requires us to call DetachFramebuffer
+ * to ensure 3D gets notified of view being destroyed... */
+ if (console().isOk() && !display().isNull())
+ frameBuffer()->detach();
+
+ /* Detach framebuffer from view: */
+ frameBuffer()->setView(NULL);
+}
+
+void UIMachineView::cleanupNativeFilters()
+{
+ /* If native event filter exists: */
+ if (m_pNativeEventFilter)
+ {
+ /* Uninstall/destroy existing native event filter: */
+ qApp->removeNativeEventFilter(m_pNativeEventFilter);
+ delete m_pNativeEventFilter;
+ m_pNativeEventFilter = 0;
+ }
+}
+
+CSession& UIMachineView::session() const
+{
+ return uisession()->session();
+}
+
+CMachine& UIMachineView::machine() const
+{
+ return uisession()->machine();
+}
+
+CConsole& UIMachineView::console() const
+{
+ return uisession()->console();
+}
+
+CDisplay& UIMachineView::display() const
+{
+ return uisession()->display();
+}
+
+CGuest& UIMachineView::guest() const
+{
+ return uisession()->guest();
+}
+
+UIActionPool* UIMachineView::actionPool() const
+{
+ return machineWindow()->actionPool();
+}
+
+QSize UIMachineView::sizeHint() const
+{
+ /* Temporarily restrict the size to prevent a brief resize to the
+ * frame-buffer dimensions when we exit full-screen. This is only
+ * applied if the frame-buffer is at full-screen dimensions and
+ * until the first machine view resize. */
+
+ /* Get the frame-buffer dimensions: */
+ QSize frameBufferSize(frameBuffer()->width(), frameBuffer()->height());
+ /* Take the scale-factor(s) into account: */
+ frameBufferSize = scaledForward(frameBufferSize);
+ /* Check against the last full-screen size. */
+ if (frameBufferSize == uisession()->lastFullScreenSize(screenId()) && m_sizeHintOverride.isValid())
+ return m_sizeHintOverride;
+
+ /* Get frame-buffer size-hint: */
+ QSize size(frameBuffer()->width(), frameBuffer()->height());
+
+ /* Take the scale-factor(s) into account: */
+ size = scaledForward(size);
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /// @todo Fix all DEBUGGER stuff!
+ /* HACK ALERT! Really ugly workaround for the resizing to 9x1 done by DevVGA if provoked before power on. */
+ if (size.width() < 16 || size.height() < 16)
+ if (uiCommon().shouldStartPaused() || uiCommon().isDebuggerAutoShowEnabled())
+ size = QSize(640, 480);
+#endif /* !VBOX_WITH_DEBUGGER_GUI */
+
+ /* Return the resulting size-hint: */
+ return QSize(size.width() + frameWidth() * 2, size.height() + frameWidth() * 2);
+}
+
+QSize UIMachineView::storedGuestScreenSizeHint() const
+{
+ /* Load guest-screen size-hint: */
+ QSize sizeHint = gEDataManager->lastGuestScreenSizeHint(m_uScreenId, uiCommon().managedVMUuid());
+
+ /* Invent the default if necessary: */
+ if (!sizeHint.isValid())
+ sizeHint = QSize(800, 600);
+
+ /* Take the scale-factor(s) into account: */
+ sizeHint = scaledForward(sizeHint);
+
+ /* Return size-hint: */
+ LogRel2(("GUI: UIMachineView::storedGuestScreenSizeHint: Acquired guest-screen size-hint for screen %d as %dx%d\n",
+ (int)screenId(), sizeHint.width(), sizeHint.height()));
+ return sizeHint;
+}
+
+void UIMachineView::setStoredGuestScreenSizeHint(const QSize &sizeHint)
+{
+ /* Save guest-screen size-hint: */
+ LogRel2(("GUI: UIMachineView::setStoredGuestScreenSizeHint: Storing guest-screen size-hint for screen %d as %dx%d\n",
+ (int)screenId(), sizeHint.width(), sizeHint.height()));
+ gEDataManager->setLastGuestScreenSizeHint(m_uScreenId, sizeHint, uiCommon().managedVMUuid());
+}
+
+QSize UIMachineView::requestedGuestScreenSizeHint() const
+{
+ /* Acquire last guest-screen size-hint set, if any: */
+ BOOL fEnabled, fChangeOrigin;
+ LONG iOriginX, iOriginY;
+ ULONG uWidth, uHeight, uBitsPerPixel;
+ display().GetVideoModeHint(screenId(), fEnabled, fChangeOrigin,
+ iOriginX, iOriginY, uWidth, uHeight, uBitsPerPixel);
+
+ /* Acquire effective frame-buffer size otherwise: */
+ if (uWidth == 0 || uHeight == 0)
+ {
+ uWidth = frameBuffer()->width();
+ uHeight = frameBuffer()->height();
+ }
+
+ /* Return result: */
+ return QSize((int)uWidth, (int)uHeight);
+}
+
+bool UIMachineView::guestScreenVisibilityStatus() const
+{
+ /* Always 'true' for primary guest-screen: */
+ if (m_uScreenId == 0)
+ return true;
+
+ /* Actual value for other guest-screens: */
+ return gEDataManager->lastGuestScreenVisibilityStatus(m_uScreenId, uiCommon().managedVMUuid());
+}
+
+void UIMachineView::handleScaleChange()
+{
+ LogRel(("GUI: UIMachineView::handleScaleChange: Screen=%d\n",
+ (unsigned long)m_uScreenId));
+
+ /* If machine-window is visible: */
+ if (uisession()->isScreenVisible(m_uScreenId))
+ {
+ /* For 'scale' mode: */
+ if (visualStateType() == UIVisualStateType_Scale)
+ {
+ /* Assign new frame-buffer logical-size: */
+ QSize scaledSize = size();
+ const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ scaledSize *= dDevicePixelRatioFormal;
+ if (!frameBuffer()->useUnscaledHiDPIOutput())
+ scaledSize /= dDevicePixelRatioActual;
+ frameBuffer()->setScaledSize(scaledSize);
+ }
+ /* For other than 'scale' mode: */
+ else
+ {
+ /* Adjust maximum-size restriction for machine-view: */
+ setMaximumSize(sizeHint());
+
+ /* Force machine-window update own layout: */
+ QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
+
+ /* Update machine-view sliders: */
+ updateSliders();
+
+ /* By some reason Win host forgets to update machine-window central-widget
+ * after main-layout was updated, let's do it for all the hosts: */
+ machineWindow()->centralWidget()->update();
+
+ /* Normalize 'normal' machine-window geometry: */
+ if (visualStateType() == UIVisualStateType_Normal)
+ machineWindow()->normalizeGeometry(true /* adjust position */, machineWindow()->shouldResizeToGuestDisplay());
+ }
+
+ /* Perform frame-buffer rescaling: */
+ frameBuffer()->performRescale();
+ }
+
+ LogRelFlow(("GUI: UIMachineView::handleScaleChange: Complete for Screen=%d\n",
+ (unsigned long)m_uScreenId));
+}
+
+void UIMachineView::resetPausePixmap()
+{
+ /* Reset pixmap(s): */
+ m_pausePixmap = QPixmap();
+ m_pausePixmapScaled = QPixmap();
+}
+
+void UIMachineView::takePausePixmapLive()
+{
+ /* Prepare a screen-shot: */
+ QImage screenShot = QImage(frameBuffer()->width(), frameBuffer()->height(), QImage::Format_RGB32);
+ /* Which will be a 'black image' by default. */
+ screenShot.fill(0);
+
+ /* For separate process: */
+ if (uiCommon().isSeparateProcess())
+ {
+ /* Take screen-data to array: */
+ const QVector<BYTE> screenData = display().TakeScreenShotToArray(screenId(), screenShot.width(), screenShot.height(), KBitmapFormat_BGR0);
+ /* And copy that data to screen-shot if it is Ok: */
+ if (display().isOk() && !screenData.isEmpty())
+ memcpy(screenShot.bits(), screenData.data(), screenShot.width() * screenShot.height() * 4);
+ }
+ /* For the same process: */
+ else
+ {
+ /* Take the screen-shot directly: */
+ display().TakeScreenShot(screenId(), screenShot.bits(), screenShot.width(), screenShot.height(), KBitmapFormat_BGR0);
+ }
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ if (!frameBuffer()->useUnscaledHiDPIOutput() && dDevicePixelRatioActual != 1.0)
+ screenShot = screenShot.scaled(screenShot.size() * dDevicePixelRatioActual,
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+ /* Dim screen-shot if it is Ok: */
+ if (display().isOk() && !screenShot.isNull())
+ dimImage(screenShot);
+
+ /* Finally copy the screen-shot to pause-pixmap: */
+ m_pausePixmap = QPixmap::fromImage(screenShot);
+
+ /* Take the device-pixel-ratio into account: */
+ m_pausePixmap.setDevicePixelRatio(frameBuffer()->devicePixelRatio());
+
+ /* Update scaled pause pixmap: */
+ updateScaledPausePixmap();
+}
+
+void UIMachineView::takePausePixmapSnapshot()
+{
+ /* Acquire the screen-data from the saved-state: */
+ ULONG uWidth = 0, uHeight = 0;
+ const QVector<BYTE> screenData = machine().ReadSavedScreenshotToArray(0, KBitmapFormat_PNG, uWidth, uHeight);
+
+ /* Make sure there is saved-state screen-data: */
+ if (screenData.isEmpty())
+ return;
+
+ /* Acquire the screen-data properties from the saved-state: */
+ ULONG uGuestOriginX = 0, uGuestOriginY = 0, uGuestWidth = 0, uGuestHeight = 0;
+ BOOL fEnabled = true;
+ machine().QuerySavedGuestScreenInfo(m_uScreenId, uGuestOriginX, uGuestOriginY, uGuestWidth, uGuestHeight, fEnabled);
+
+ /* Calculate effective size: */
+ QSize effectiveSize = uGuestWidth > 0 ? QSize(uGuestWidth, uGuestHeight) : storedGuestScreenSizeHint();
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ if (!frameBuffer()->useUnscaledHiDPIOutput() && dDevicePixelRatioActual != 1.0)
+ effectiveSize *= dDevicePixelRatioActual;
+
+ /* Create a screen-shot on the basis of the screen-data we have in saved-state: */
+ QImage screenShot = QImage::fromData(screenData.data(), screenData.size(), "PNG").scaled(effectiveSize);
+
+ /* Dim screen-shot if it is Ok: */
+ if (machine().isOk() && !screenShot.isNull())
+ dimImage(screenShot);
+
+ /* Finally copy the screen-shot to pause-pixmap: */
+ m_pausePixmap = QPixmap::fromImage(screenShot);
+
+ /* Take the device-pixel-ratio into account: */
+ m_pausePixmap.setDevicePixelRatio(frameBuffer()->devicePixelRatio());
+
+ /* Update scaled pause pixmap: */
+ updateScaledPausePixmap();
+}
+
+void UIMachineView::updateScaledPausePixmap()
+{
+ /* Make sure pause pixmap is not null: */
+ if (pausePixmap().isNull())
+ return;
+
+ /* Make sure scaled-size is not null: */
+ QSize scaledSize = frameBuffer()->scaledSize();
+ if (!scaledSize.isValid())
+ return;
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ if (!frameBuffer()->useUnscaledHiDPIOutput() && dDevicePixelRatioActual != 1.0)
+ scaledSize *= dDevicePixelRatioActual;
+
+ /* Update pause pixmap finally: */
+ m_pausePixmapScaled = pausePixmap().scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+ /* Take the device-pixel-ratio into account: */
+ m_pausePixmapScaled.setDevicePixelRatio(frameBuffer()->devicePixelRatio());
+}
+
+void UIMachineView::updateSliders()
+{
+ /* Get current viewport size: */
+ QSize curViewportSize = viewport()->size();
+ /* Get maximum viewport size: */
+ const QSize maxViewportSize = maximumViewportSize();
+ /* Get current frame-buffer size: */
+ QSize frameBufferSize = QSize(frameBuffer()->width(), frameBuffer()->height());
+
+ /* Take the scale-factor(s) into account: */
+ frameBufferSize = scaledForward(frameBufferSize);
+
+ /* If maximum viewport size can cover whole frame-buffer => no scroll-bars required: */
+ if (maxViewportSize.expandedTo(frameBufferSize) == maxViewportSize)
+ curViewportSize = maxViewportSize;
+
+ /* What length we want scroll-bars of? */
+ int xRange = frameBufferSize.width() - curViewportSize.width();
+ int yRange = frameBufferSize.height() - curViewportSize.height();
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ xRange *= dDevicePixelRatioFormal;
+ yRange *= dDevicePixelRatioFormal;
+ if (!frameBuffer()->useUnscaledHiDPIOutput())
+ {
+ xRange /= dDevicePixelRatioActual;
+ yRange /= dDevicePixelRatioActual;
+ }
+
+ /* Configure scroll-bars: */
+ horizontalScrollBar()->setRange(0, xRange);
+ verticalScrollBar()->setRange(0, yRange);
+ horizontalScrollBar()->setPageStep(curViewportSize.width());
+ verticalScrollBar()->setPageStep(curViewportSize.height());
+}
+
+void UIMachineView::dimImage(QImage &img)
+{
+ for (int y = 0; y < img.height(); ++ y)
+ {
+ if (y % 2)
+ {
+ if (img.depth() == 32)
+ {
+ for (int x = 0; x < img.width(); ++ x)
+ {
+ int gray = qGray(img.pixel (x, y)) / 2;
+ img.setPixel(x, y, qRgb (gray, gray, gray));
+ }
+ }
+ else
+ {
+ ::memset(img.scanLine (y), 0, img.bytesPerLine());
+ }
+ }
+ else
+ {
+ if (img.depth() == 32)
+ {
+ for (int x = 0; x < img.width(); ++ x)
+ {
+ int gray = (2 * qGray (img.pixel (x, y))) / 3;
+ img.setPixel(x, y, qRgb (gray, gray, gray));
+ }
+ }
+ }
+ }
+}
+
+void UIMachineView::scrollContentsBy(int dx, int dy)
+{
+ /* Call to base-class: */
+ QAbstractScrollArea::scrollContentsBy(dx, dy);
+
+ /* Update console's display viewport and 3D overlay: */
+ updateViewport();
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineView::updateDockIcon()
+{
+ machineLogic()->updateDockIcon();
+}
+
+CGImageRef UIMachineView::frameBuffertoCGImageRef(UIFrameBuffer *pFrameBuffer)
+{
+ CGImageRef ir = 0;
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ if (cs)
+ {
+ /* Create the image copy of the framebuffer */
+ CGDataProviderRef dp = CGDataProviderCreateWithData(pFrameBuffer, pFrameBuffer->address(), pFrameBuffer->bitsPerPixel() / 8 * pFrameBuffer->width() * pFrameBuffer->height(), NULL);
+ if (dp)
+ {
+ ir = CGImageCreate(pFrameBuffer->width(), pFrameBuffer->height(), 8, 32, pFrameBuffer->bytesPerLine(), cs,
+ kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, dp, 0, false,
+ kCGRenderingIntentDefault);
+ CGDataProviderRelease(dp);
+ }
+ CGColorSpaceRelease(cs);
+ }
+ return ir;
+}
+#endif /* VBOX_WS_MAC */
+
+bool UIMachineView::isFullscreenOrSeamless() const
+{
+ return visualStateType() == UIVisualStateType_Fullscreen
+ || visualStateType() == UIVisualStateType_Seamless;
+}
+
+bool UIMachineView::event(QEvent *pEvent)
+{
+ switch ((UIEventType)pEvent->type())
+ {
+#ifdef VBOX_WS_MAC
+ /* Event posted OnShowWindow: */
+ case ShowWindowEventType:
+ {
+ /* Dunno what Qt3 thinks a window that has minimized to the dock should be - it is not hidden,
+ * neither is it minimized. OTOH it is marked shown and visible, but not activated.
+ * This latter isn't of much help though, since at this point nothing is marked activated.
+ * I might have overlooked something, but I'm buggered what if I know what. So, I'll just always
+ * show & activate the stupid window to make it get out of the dock when the user wishes to show a VM: */
+ window()->show();
+ window()->activateWindow();
+ return true;
+ }
+#endif /* VBOX_WS_MAC */
+
+ default:
+ break;
+ }
+
+ return QAbstractScrollArea::event(pEvent);
+}
+
+bool UIMachineView::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ if (pWatched == viewport())
+ {
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ {
+ /* Notify framebuffer about viewport resize: */
+ QResizeEvent *pResizeEvent = static_cast<QResizeEvent*>(pEvent);
+ if (frameBuffer())
+ frameBuffer()->viewportResized(pResizeEvent);
+ /* Update console's display viewport and 3D overlay: */
+ updateViewport();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (pWatched == this)
+ {
+ switch (pEvent->type())
+ {
+ case QEvent::Move:
+ {
+ /* Update console's display viewport and 3D overlay: */
+ updateViewport();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (pWatched == machineWindow())
+ {
+ switch (pEvent->type())
+ {
+ case QEvent::WindowStateChange:
+ {
+ /* During minimizing and state restoring machineWindow() gets
+ * the focus which belongs to console view window, so returning it properly. */
+ QWindowStateChangeEvent *pWindowEvent = static_cast<QWindowStateChangeEvent*>(pEvent);
+ if (pWindowEvent->oldState() & Qt::WindowMinimized)
+ {
+ if (QApplication::focusWidget())
+ {
+ QApplication::focusWidget()->clearFocus();
+ qApp->processEvents();
+ }
+ QTimer::singleShot(0, this, SLOT(setFocus()));
+ }
+ break;
+ }
+ case QEvent::Move:
+ {
+ /* Get current host-screen number: */
+ const int iCurrentHostScreenNumber = UIDesktopWidgetWatchdog::screenNumber(this);
+ if (m_iHostScreenNumber != iCurrentHostScreenNumber)
+ {
+ /* Recache current host screen: */
+ m_iHostScreenNumber = iCurrentHostScreenNumber;
+ /* Reapply machine-view scale-factor if necessary: */
+ applyMachineViewScaleFactor();
+ /* For 'normal'/'scaled' visual state type: */
+ if ( visualStateType() == UIVisualStateType_Normal
+ || visualStateType() == UIVisualStateType_Scale)
+ {
+ /* Make sure action-pool is of 'runtime' type: */
+ UIActionPoolRuntime *pActionPool = actionPool() && actionPool()->toRuntime() ? actionPool()->toRuntime() : 0;
+ AssertPtr(pActionPool);
+ if (pActionPool)
+ {
+ /* Inform action-pool about current guest-to-host screen mapping: */
+ QMap<int, int> screenMap = pActionPool->hostScreenForGuestScreenMap();
+ screenMap[m_uScreenId] = m_iHostScreenNumber;
+ pActionPool->setHostScreenForGuestScreenMap(screenMap);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return QAbstractScrollArea::eventFilter(pWatched, pEvent);
+}
+
+void UIMachineView::resizeEvent(QResizeEvent *pEvent)
+{
+ updateSliders();
+ return QAbstractScrollArea::resizeEvent(pEvent);
+}
+
+void UIMachineView::moveEvent(QMoveEvent *pEvent)
+{
+ return QAbstractScrollArea::moveEvent(pEvent);
+}
+
+void UIMachineView::paintEvent(QPaintEvent *pPaintEvent)
+{
+ /* Use pause-image if exists: */
+ if (!pausePixmap().isNull())
+ {
+ /* Create viewport painter: */
+ QPainter painter(viewport());
+ /* Avoid painting more than necessary: */
+ painter.setClipRect(pPaintEvent->rect());
+ /* Can be NULL when the event arrive during COM cleanup: */
+ UIFrameBuffer *pFramebuffer = frameBuffer();
+ /* Take the scale-factor into account: */
+ if ( pFramebuffer
+ ? pFramebuffer->scaleFactor() == 1.0 && !pFramebuffer->scaledSize().isValid()
+ : pausePixmapScaled().isNull())
+ painter.drawPixmap(viewport()->rect().topLeft(), pausePixmap());
+ else
+ painter.drawPixmap(viewport()->rect().topLeft(), pausePixmapScaled());
+#ifdef VBOX_WS_MAC
+ /* Update the dock icon: */
+ updateDockIcon();
+#endif /* VBOX_WS_MAC */
+ return;
+ }
+
+ /* Delegate the paint function to the UIFrameBuffer interface: */
+ if (frameBuffer())
+ frameBuffer()->handlePaintEvent(pPaintEvent);
+#ifdef VBOX_WS_MAC
+ /* Update the dock icon if we are in the running state: */
+ if (uisession()->isRunning())
+ updateDockIcon();
+#endif /* VBOX_WS_MAC */
+}
+
+void UIMachineView::focusInEvent(QFocusEvent *pEvent)
+{
+ /* Call to base-class: */
+ QAbstractScrollArea::focusInEvent(pEvent);
+
+ /* If native event filter isn't exists: */
+ if (!m_pNativeEventFilter)
+ {
+ /* Create/install new native event filter: */
+ m_pNativeEventFilter = new UINativeEventFilter(this);
+ qApp->installNativeEventFilter(m_pNativeEventFilter);
+ }
+}
+
+void UIMachineView::focusOutEvent(QFocusEvent *pEvent)
+{
+ /* If native event filter exists: */
+ if (m_pNativeEventFilter)
+ {
+ /* Uninstall/destroy existing native event filter: */
+ qApp->removeNativeEventFilter(m_pNativeEventFilter);
+ delete m_pNativeEventFilter;
+ m_pNativeEventFilter = 0;
+ }
+
+ /* Call to base-class: */
+ QAbstractScrollArea::focusOutEvent(pEvent);
+}
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+
+bool UIMachineView::dragAndDropCanAccept(void) const
+{
+ bool fAccept = m_pDnDHandler
+# ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ && !m_fIsDraggingFromGuest
+# endif
+ && machine().GetDnDMode() != KDnDMode_Disabled;
+ return fAccept;
+}
+
+bool UIMachineView::dragAndDropIsActive(void) const
+{
+ return ( m_pDnDHandler
+ && machine().GetDnDMode() != KDnDMode_Disabled);
+}
+
+void UIMachineView::dragEnterEvent(QDragEnterEvent *pEvent)
+{
+ AssertPtrReturnVoid(pEvent);
+
+ int rc = dragAndDropCanAccept() ? VINF_SUCCESS : VERR_ACCESS_DENIED;
+ if (RT_SUCCESS(rc))
+ {
+ /* Get mouse-pointer location. */
+ const QPoint &cpnt = viewportToContents(pEvent->pos());
+
+ /* Ask the target for starting a DnD event. */
+ Qt::DropAction result = m_pDnDHandler->dragEnter(screenId(),
+ frameBuffer()->convertHostXTo(cpnt.x()),
+ frameBuffer()->convertHostYTo(cpnt.y()),
+ pEvent->proposedAction(),
+ pEvent->possibleActions(),
+ pEvent->mimeData());
+
+ /* Set the DnD action returned by the guest. */
+ pEvent->setDropAction(result);
+ pEvent->accept();
+ }
+
+ DNDDEBUG(("DnD: dragEnterEvent ended with rc=%Rrc\n", rc));
+}
+
+void UIMachineView::dragMoveEvent(QDragMoveEvent *pEvent)
+{
+ AssertPtrReturnVoid(pEvent);
+
+ int rc = dragAndDropCanAccept() ? VINF_SUCCESS : VERR_ACCESS_DENIED;
+ if (RT_SUCCESS(rc))
+ {
+ /* Get mouse-pointer location. */
+ const QPoint &cpnt = viewportToContents(pEvent->pos());
+
+ /* Ask the guest for moving the drop cursor. */
+ Qt::DropAction result = m_pDnDHandler->dragMove(screenId(),
+ frameBuffer()->convertHostXTo(cpnt.x()),
+ frameBuffer()->convertHostYTo(cpnt.y()),
+ pEvent->proposedAction(),
+ pEvent->possibleActions(),
+ pEvent->mimeData());
+
+ /* Set the DnD action returned by the guest. */
+ pEvent->setDropAction(result);
+ pEvent->accept();
+ }
+
+ DNDDEBUG(("DnD: dragMoveEvent ended with rc=%Rrc\n", rc));
+}
+
+void UIMachineView::dragLeaveEvent(QDragLeaveEvent *pEvent)
+{
+ AssertPtrReturnVoid(pEvent);
+
+ int rc = dragAndDropCanAccept() ? VINF_SUCCESS : VERR_ACCESS_DENIED;
+ if (RT_SUCCESS(rc))
+ {
+ m_pDnDHandler->dragLeave(screenId());
+
+ pEvent->accept();
+ }
+
+ DNDDEBUG(("DnD: dragLeaveEvent ended with rc=%Rrc\n", rc));
+}
+
+void UIMachineView::dropEvent(QDropEvent *pEvent)
+{
+ AssertPtrReturnVoid(pEvent);
+
+ int rc = dragAndDropCanAccept() ? VINF_SUCCESS : VERR_ACCESS_DENIED;
+ if (RT_SUCCESS(rc))
+ {
+ /* Get mouse-pointer location. */
+ const QPoint &cpnt = viewportToContents(pEvent->pos());
+
+ /* Ask the guest for dropping data. */
+ Qt::DropAction result = m_pDnDHandler->dragDrop(screenId(),
+ frameBuffer()->convertHostXTo(cpnt.x()),
+ frameBuffer()->convertHostYTo(cpnt.y()),
+ pEvent->proposedAction(),
+ pEvent->possibleActions(),
+ pEvent->mimeData());
+
+ /* Set the DnD action returned by the guest. */
+ pEvent->setDropAction(result);
+ pEvent->accept();
+ }
+
+ DNDDEBUG(("DnD: dropEvent ended with rc=%Rrc\n", rc));
+}
+
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+
+QSize UIMachineView::scaledForward(QSize size) const
+{
+ /* Take the scale-factor into account: */
+ const double dScaleFactor = frameBuffer()->scaleFactor();
+ if (dScaleFactor != 1.0)
+ size = QSize((int)(size.width() * dScaleFactor), (int)(size.height() * dScaleFactor));
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ if (!frameBuffer()->useUnscaledHiDPIOutput())
+ size = QSize(size.width() * dDevicePixelRatioActual, size.height() * dDevicePixelRatioActual);
+ size = QSize(size.width() / dDevicePixelRatioFormal, size.height() / dDevicePixelRatioFormal);
+
+ /* Return result: */
+ return size;
+}
+
+QSize UIMachineView::scaledBackward(QSize size) const
+{
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ size = QSize(size.width() * dDevicePixelRatioFormal, size.height() * dDevicePixelRatioFormal);
+ if (!frameBuffer()->useUnscaledHiDPIOutput())
+ size = QSize(size.width() / dDevicePixelRatioActual, size.height() / dDevicePixelRatioActual);
+
+ /* Take the scale-factor into account: */
+ const double dScaleFactor = frameBuffer()->scaleFactor();
+ if (dScaleFactor != 1.0)
+ size = QSize((int)(size.width() / dScaleFactor), (int)(size.height() / dScaleFactor));
+
+ /* Return result: */
+ return size;
+}
+
+void UIMachineView::updateMousePointerPixmapScaling(QPixmap &pixmap, uint &uXHot, uint &uYHot)
+{
+#if defined(VBOX_WS_MAC)
+
+ /* Take into account scale-factor if necessary: */
+ const double dScaleFactor = frameBuffer()->scaleFactor();
+ //printf("Scale-factor: %f\n", dScaleFactor);
+ if (dScaleFactor > 1.0)
+ {
+ /* Scale the pixmap up: */
+ pixmap = pixmap.scaled(pixmap.width() * dScaleFactor, pixmap.height() * dScaleFactor,
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ uXHot *= dScaleFactor;
+ uYHot *= dScaleFactor;
+ }
+
+ /* Take into account device-pixel-ratio if necessary: */
+ const double dDevicePixelRatio = frameBuffer()->devicePixelRatio();
+ const bool fUseUnscaledHiDPIOutput = frameBuffer()->useUnscaledHiDPIOutput();
+ //printf("Device-pixel-ratio: %f, Unscaled HiDPI Output: %d\n",
+ // dDevicePixelRatio, fUseUnscaledHiDPIOutput);
+ if (dDevicePixelRatio > 1.0 && fUseUnscaledHiDPIOutput)
+ {
+ /* Scale the pixmap down: */
+ pixmap.setDevicePixelRatio(dDevicePixelRatio);
+ uXHot /= dDevicePixelRatio;
+ uYHot /= dDevicePixelRatio;
+ }
+
+#elif defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+
+ /* We want to scale the pixmap just once, so let's prepare cumulative multiplier: */
+ double dScaleMultiplier = 1.0;
+
+ /* Take into account scale-factor if necessary: */
+ const double dScaleFactor = frameBuffer()->scaleFactor();
+ //printf("Scale-factor: %f\n", dScaleFactor);
+ if (dScaleFactor > 1.0)
+ dScaleMultiplier *= dScaleFactor;
+
+ /* Take into account device-pixel-ratio if necessary: */
+# ifdef VBOX_WS_WIN
+ const double dDevicePixelRatio = frameBuffer()->devicePixelRatio();
+# endif
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ const bool fUseUnscaledHiDPIOutput = frameBuffer()->useUnscaledHiDPIOutput();
+ //printf("Device-pixel-ratio/actual: %f/%f, Unscaled HiDPI Output: %d\n",
+ // dDevicePixelRatio, dDevicePixelRatioActual, fUseUnscaledHiDPIOutput);
+ if (dDevicePixelRatioActual > 1.0 && !fUseUnscaledHiDPIOutput)
+ dScaleMultiplier *= dDevicePixelRatioActual;
+
+ /* If scale multiplier was set: */
+ if (dScaleMultiplier > 1.0)
+ {
+ /* Scale the pixmap up: */
+ pixmap = pixmap.scaled(pixmap.width() * dScaleMultiplier, pixmap.height() * dScaleMultiplier,
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ uXHot *= dScaleMultiplier;
+ uYHot *= dScaleMultiplier;
+ }
+
+# ifdef VBOX_WS_WIN
+ /* If device pixel ratio was set: */
+ if (dDevicePixelRatio > 1.0)
+ {
+ /* Scale the pixmap down: */
+ pixmap.setDevicePixelRatio(dDevicePixelRatio);
+ uXHot /= dDevicePixelRatio;
+ uYHot /= dDevicePixelRatio;
+ }
+# endif
+
+#else
+
+ Q_UNUSED(pixmap);
+ Q_UNUSED(uXHot);
+ Q_UNUSED(uYHot);
+
+#endif
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.h
new file mode 100644
index 00000000..e6661ad4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.h
@@ -0,0 +1,492 @@
+/* $Id: UIMachineView.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineView class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIMachineView_h
+#define FEQT_INCLUDED_SRC_runtime_UIMachineView_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QAbstractScrollArea>
+#include <QEventLoop>
+#include <QPointer>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+#include "UIFrameBuffer.h"
+#include "UIMachineDefs.h"
+#ifdef VBOX_WITH_DRAG_AND_DROP
+# include "UIDnDHandler.h"
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Other VBox includes: */
+#include "VBox/com/ptr.h"
+#ifdef VBOX_WS_MAC
+# include <ApplicationServices/ApplicationServices.h>
+#endif /* VBOX_WS_MAC */
+
+/* External includes: */
+#ifdef VBOX_WS_MAC
+# include <CoreFoundation/CFBase.h>
+#endif /* VBOX_WS_MAC */
+
+/* Forward declarations: */
+class UIActionPool;
+class UISession;
+class UIMachineLogic;
+class UIMachineWindow;
+class UINativeEventFilter;
+class CConsole;
+class CDisplay;
+class CGuest;
+class CMachine;
+class CSession;
+#ifdef VBOX_WITH_DRAG_AND_DROP
+class CDnDTarget;
+#endif
+
+
+class UIMachineView : public QAbstractScrollArea
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about mouse pointer shape change. */
+ void sigMousePointerShapeChange();
+ /** Notifies about frame-buffer resize. */
+ void sigFrameBufferResize();
+
+public:
+
+ /* Factory function to create machine-view: */
+ static UIMachineView* create(UIMachineWindow *pMachineWindow, ulong uScreenId, UIVisualStateType visualStateType);
+ /* Factory function to destroy required machine-view: */
+ static void destroy(UIMachineView *pMachineView);
+
+ /** Returns whether the guest-screen auto-resize is enabled. */
+ virtual bool isGuestAutoresizeEnabled() const { return true; }
+ /** Defines whether the guest-screen auto-resize is @a fEnabled. */
+ virtual void setGuestAutoresizeEnabled(bool fEnabled) { Q_UNUSED(fEnabled); }
+
+ /** Send saved guest-screen size-hint to the guest.
+ * @note Reimplemented in sub-classes. Base implementation does nothing. */
+ virtual void resendSizeHint() {}
+
+ /** Adjusts guest-screen size to correspond current visual-style.
+ * @note Reimplemented in sub-classes. Base implementation does nothing. */
+ virtual void adjustGuestScreenSize() {}
+
+ /** Applies machine-view scale-factor. */
+ virtual void applyMachineViewScaleFactor();
+
+ /** Returns screen ID for this view. */
+ ulong screenId() const { return m_uScreenId; }
+
+ /** Returns the session UI reference. */
+ UISession *uisession() const;
+ /** Returns the machine-logic reference. */
+ UIMachineLogic *machineLogic() const;
+ /** Returns the machine-window reference. */
+ UIMachineWindow *machineWindow() const { return m_pMachineWindow; }
+ /** Returns view's frame-buffer reference. */
+ UIFrameBuffer *frameBuffer() const { return m_pFrameBuffer; }
+
+ /** Returns actual contents width. */
+ int contentsWidth() const;
+ /** Returns actual contents height. */
+ int contentsHeight() const;
+ /** Returns actual contents x origin. */
+ int contentsX() const;
+ /** Returns actual contents y origin. */
+ int contentsY() const;
+ /** Returns visible contents width. */
+ int visibleWidth() const;
+ /** Returns visible contents height. */
+ int visibleHeight() const;
+ /** Translates viewport point to contents point. */
+ QPoint viewportToContents(const QPoint &viewportPoint) const;
+ /** Scrolls contents by @a iDx x iDy pixels. */
+ void scrollBy(int iDx, int iDy);
+
+ /** What view mode (normal, fullscreen etc.) are we in? */
+ UIVisualStateType visualStateType() const;
+
+ /** Returns cached mouse cursor. */
+ QCursor cursor() const { return m_cursor; }
+
+ /* Framebuffer aspect ratio: */
+ double aspectRatio() const;
+
+ /** Atomically store the maximum guest resolution which we currently wish
+ * to handle for @a maximumGuestSize() to read. Should be called if anything
+ * happens (e.g. a screen hotplug) which might cause the value to change.
+ * @sa m_u64MaximumGuestSize. */
+ void setMaximumGuestSize(const QSize &minimumSizeHint = QSize());
+ /** Atomically read the maximum guest resolution which we currently wish to
+ * handle. This may safely be called from another thread (called by
+ * UIFramebuffer on EMT).
+ * @sa m_u64MaximumGuestSize. */
+ QSize maximumGuestSize();
+
+ /** Updates console's display viewport.
+ * @remarks Used to update 3D-service overlay viewport as well. */
+ void updateViewport();
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+ /** Checks for a pending drag and drop event within the guest and
+ * (optionally) starts a drag and drop operation on the host. */
+ int dragCheckPending();
+ /** Starts a drag and drop operation from guest to the host.
+ * This internally either uses Qt's abstract QDrag methods
+ * or some other OS-dependent implementation. */
+ int dragStart();
+ /** Aborts (and resets) the current (pending)
+ * guest to host drag and drop operation. */
+ int dragStop();
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+
+ /** Performs pre-processing of all the native events. */
+ virtual bool nativeEventPreprocessor(const QByteArray &eventType, void *pMessage);
+
+#ifdef VBOX_WS_MAC
+ /** Returns VM contents image. */
+ CGImageRef vmContentImage();
+#endif
+
+public slots:
+
+ /** Handles NotifyChange event received from frame-buffer.
+ * @todo To make it right, this have to be protected, but
+ * connection should be moved from frame-buffer to this class. */
+ virtual void sltHandleNotifyChange(int iWidth, int iHeight);
+
+ /** Handles NotifyUpdate event received from frame-buffer.
+ * @todo To make it right, this have to be protected, but
+ * connection should be moved from frame-buffer to this class. */
+ virtual void sltHandleNotifyUpdate(int iX, int iY, int iWidth, int iHeight);
+
+ /** Handles SetVisibleRegion event received from frame-buffer.
+ * @todo To make it right, this have to be protected, but
+ * connection should be moved from frame-buffer to this class. */
+ virtual void sltHandleSetVisibleRegion(QRegion region);
+
+protected slots:
+
+ /* Performs guest-screen resize to a size specified.
+ * @param toSize Brings the size guest-screen needs to be resized to.
+ * @note If toSize isn't valid or sane one, it will be replaced with actual
+ * size of centralWidget() containing this machine-view currently.
+ * @note Also, take into acount that since this method is also called to
+ * resize to centralWidget() size, the size passed is expected to be
+ * tranformed to internal coordinate system and thus to be restored to
+ * guest coordinate system (absolute one) before passing to guest. */
+ void sltPerformGuestResize(const QSize &toSize = QSize());
+
+ /** Handles guest-screen toggle request.
+ * @param iScreen Brings the number of screen being referred.
+ * @param fEnabled Brings whether this screen should be enabled. */
+ void sltHandleActionTriggerViewScreenToggle(int iScreen, bool fEnabled);
+ /** Handles guest-screen resize request.
+ * @param iScreen Brings the number of screen being referred.
+ * @param size Brings the size of screen to be applied. */
+ void sltHandleActionTriggerViewScreenResize(int iScreen, const QSize &size);
+
+ /* Watch dog for desktop resizes: */
+ void sltDesktopResized();
+
+ /** Handles the scale-factor change. */
+ void sltHandleScaleFactorChange(const QUuid &uMachineID);
+
+ /** Handles the scaling-optimization change. */
+ void sltHandleScalingOptimizationChange(const QUuid &uMachineID);
+
+ /* Console callback handlers: */
+ virtual void sltMachineStateChanged();
+ /** Handles guest request to change the mouse pointer shape. */
+ void sltMousePointerShapeChange();
+
+ /** Detaches COM. */
+ void sltDetachCOM();
+
+protected:
+
+ /* Machine-view constructor: */
+ UIMachineView(UIMachineWindow *pMachineWindow, ulong uScreenId);
+ /* Machine-view destructor: */
+ virtual ~UIMachineView() {}
+
+ /* Prepare routines: */
+ virtual void loadMachineViewSettings();
+ //virtual void prepareNativeFilters() {}
+ virtual void prepareViewport();
+ virtual void prepareFrameBuffer();
+ virtual void prepareCommon();
+#ifdef VBOX_WITH_DRAG_AND_DROP
+ virtual int prepareDnd();
+#endif
+ virtual void prepareFilters();
+ virtual void prepareConnections();
+ virtual void prepareConsoleConnections();
+
+ /* Cleanup routines: */
+ //virtual void cleanupConsoleConnections() {}
+ //virtual void cleanupConnections() {}
+ //virtual void cleanupFilters() {}
+#ifdef VBOX_WITH_DRAG_AND_DROP
+ virtual void cleanupDnd();
+#endif
+ //virtual void cleanupCommon() {}
+ virtual void cleanupFrameBuffer();
+ //virtual void cleanupViewport();
+ virtual void cleanupNativeFilters();
+ //virtual void saveMachineViewSettings() {}
+
+ /** Returns the session reference. */
+ CSession& session() const;
+ /** Returns the session's machine reference. */
+ CMachine& machine() const;
+ /** Returns the session's console reference. */
+ CConsole& console() const;
+ /** Returns the console's display reference. */
+ CDisplay& display() const;
+ /** Returns the console's guest reference. */
+ CGuest& guest() const;
+
+ /* Protected getters: */
+ UIActionPool* actionPool() const;
+ QSize sizeHint() const;
+
+ /** Retrieves the last guest-screen size-hint from extra-data. */
+ QSize storedGuestScreenSizeHint() const;
+ /** Stores a guest-screen @a sizeHint to extra-data. */
+ void setStoredGuestScreenSizeHint(const QSize &sizeHint);
+
+ /** Retrieves the sent guest-screen size-hint from display or frame-buffer. */
+ QSize requestedGuestScreenSizeHint() const;
+
+ /** Retrieves the last guest-screen visibility status from extra-data. */
+ bool guestScreenVisibilityStatus() const;
+
+ /** Handles machine-view scale changes. */
+ void handleScaleChange();
+
+ /** Returns the pause-pixmap: */
+ const QPixmap& pausePixmap() const { return m_pausePixmap; }
+ /** Returns the scaled pause-pixmap: */
+ const QPixmap& pausePixmapScaled() const { return m_pausePixmapScaled; }
+ /** Resets the pause-pixmap. */
+ void resetPausePixmap();
+ /** Acquires live pause-pixmap. */
+ void takePausePixmapLive();
+ /** Acquires snapshot pause-pixmap. */
+ void takePausePixmapSnapshot();
+ /** Updates the scaled pause-pixmap. */
+ void updateScaledPausePixmap();
+
+ /** The available area on the current screen for application windows. */
+ virtual QRect workingArea() const = 0;
+ /** Calculate how big the guest desktop can be while still fitting on one
+ * host screen. */
+ virtual QSize calculateMaxGuestSize() const = 0;
+ virtual void updateSliders();
+ static void dimImage(QImage &img);
+ void scrollContentsBy(int dx, int dy);
+#ifdef VBOX_WS_MAC
+ void updateDockIcon();
+ CGImageRef frameBuffertoCGImageRef(UIFrameBuffer *pFrameBuffer);
+#endif /* VBOX_WS_MAC */
+ /** Is this a fullscreen-type view? */
+ bool isFullscreenOrSeamless() const;
+
+ /* Cross-platforms event processors: */
+ bool event(QEvent *pEvent);
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+ void resizeEvent(QResizeEvent *pEvent);
+ void moveEvent(QMoveEvent *pEvent);
+ void paintEvent(QPaintEvent *pEvent);
+
+ /** Handles focus-in @a pEvent. */
+ void focusInEvent(QFocusEvent *pEvent);
+ /** Handles focus-out @a pEvent. */
+ void focusOutEvent(QFocusEvent *pEvent);
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+ /**
+ * Returns @true if the VM window can accept (start is, start) a drag and drop
+ * operation, @false if not.
+ */
+ bool dragAndDropCanAccept(void) const;
+
+ /**
+ * Returns @true if drag and drop for this machine is active
+ * (that is, host->guest, guest->host or bidirectional), @false if not.
+ */
+ bool dragAndDropIsActive(void) const;
+
+ /**
+ * Host -> Guest: Issued when the host cursor enters the guest (VM) window.
+ * The guest will receive the relative cursor coordinates of the
+ * appropriate screen ID.
+ *
+ * @param pEvent Related enter event.
+ */
+ void dragEnterEvent(QDragEnterEvent *pEvent);
+
+ /**
+ * Host -> Guest: Issued when the host cursor moves inside (over) the guest (VM) window.
+ * The guest will receive the relative cursor coordinates of the
+ * appropriate screen ID.
+ *
+ * @param pEvent Related move event.
+ */
+ void dragLeaveEvent(QDragLeaveEvent *pEvent);
+
+ /**
+ * Host -> Guest: Issued when the host cursor leaves the guest (VM) window again.
+ * This will ask the guest to stop any further drag'n drop operation.
+ *
+ * @param pEvent Related leave event.
+ */
+ void dragMoveEvent(QDragMoveEvent *pEvent);
+
+ /**
+ * Host -> Guest: Issued when the host drops data into the guest (VM) window.
+ *
+ * @param pEvent Related drop event.
+ */
+ void dropEvent(QDropEvent *pEvent);
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+
+ /** Scales passed size forward. */
+ QSize scaledForward(QSize size) const;
+ /** Scales passed size backward. */
+ QSize scaledBackward(QSize size) const;
+
+ /** Updates mouse pointer @a pixmap, @a uXHot and @a uYHot according to scaling attributes. */
+ void updateMousePointerPixmapScaling(QPixmap &pixmap, uint &uXHot, uint &uYHot);
+
+ /* Protected members: */
+ UIMachineWindow *m_pMachineWindow;
+ ulong m_uScreenId;
+ QPointer<UIFrameBuffer> m_pFrameBuffer;
+ KMachineState m_previousState;
+ /** HACK: when switching out of fullscreen or seamless we wish to override
+ * the default size hint to avoid short resizes back to fullscreen size.
+ * Not explicitly initialised (i.e. invalid by default). */
+ QSize m_sizeHintOverride;
+
+ /** Last size hint sent as a part of guest auto-resize feature.
+ * @note Useful to avoid spamming CDisplay with same hint before
+ * frame-buffer finally resized to requested size. */
+ QSize m_lastSizeHint;
+
+ /** Holds current host-screen number. */
+ int m_iHostScreenNumber;
+
+ /** Holds the maximum guest screen size policy. */
+ MaximumGuestScreenSizePolicy m_enmMaximumGuestScreenSizePolicy;
+ /** The maximum guest size for fixed size policy. */
+ QSize m_fixedMaxGuestSize;
+ /** Maximum guest resolution which we wish to handle. Must be accessed
+ * atomically.
+ * @note The background for this variable is that we need this value to be
+ * available to the EMT thread, but it can only be calculated by the
+ * GUI, and GUI code can only safely be called on the GUI thread due to
+ * (at least) X11 threading issues. So we calculate the value in advance,
+ * monitor things in case it changes and update it atomically when it does.
+ */
+ /** @todo This should be private. */
+ volatile uint64_t m_u64MaximumGuestSize;
+
+ /** Holds the pause-pixmap. */
+ QPixmap m_pausePixmap;
+ /** Holds the scaled pause-pixmap. */
+ QPixmap m_pausePixmapScaled;
+
+ /** Holds cached mouse cursor. */
+ QCursor m_cursor;
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+ /** Pointer to drag and drop handler instance. */
+ UIDnDHandler *m_pDnDHandler;
+# ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ /** Flag indicating whether a guest->host drag currently is in
+ * progress or not. */
+ bool m_fIsDraggingFromGuest;
+# endif
+#endif
+
+ /** Holds the native event filter instance. */
+ UINativeEventFilter *m_pNativeEventFilter;
+};
+
+/* This maintenance class is a part of future roll-back mechanism.
+ * It allows to block main GUI thread until specific event received.
+ * Later it will become more abstract but now its just used to help
+ * fullscreen & seamless modes to restore normal guest size hint. */
+/** @todo This class is now unused - can it be removed altogether? */
+class UIMachineViewBlocker : public QEventLoop
+{
+ Q_OBJECT;
+
+public:
+
+ UIMachineViewBlocker()
+ : QEventLoop(0)
+ , m_iTimerId(0)
+ {
+ /* Also start timer to unlock pool in case of
+ * required condition doesn't happens by some reason: */
+ m_iTimerId = startTimer(3000);
+ }
+
+ virtual ~UIMachineViewBlocker()
+ {
+ /* Kill the timer: */
+ killTimer(m_iTimerId);
+ }
+
+protected:
+
+ void timerEvent(QTimerEvent *pEvent)
+ {
+ /* If that timer event occurs => it seems
+ * guest resize event doesn't comes in time,
+ * shame on it, but we just unlocking 'this': */
+ QEventLoop::timerEvent(pEvent);
+ exit();
+ }
+
+ int m_iTimerId;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIMachineView_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.cpp
new file mode 100644
index 00000000..48b101f6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.cpp
@@ -0,0 +1,720 @@
+/* $Id: UIMachineWindow.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindow class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCloseEvent>
+#include <QGridLayout>
+#include <QProcess>
+#include <QStyle>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UIActionPoolRuntime.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIModalWindowManager.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UISession.h"
+#include "UIMachineLogic.h"
+#include "UIMachineWindow.h"
+#include "UIMachineWindowNormal.h"
+#include "UIMachineWindowFullscreen.h"
+#include "UIMachineWindowSeamless.h"
+#include "UIMachineWindowScale.h"
+#include "UIMachineView.h"
+#include "UIKeyboardHandler.h"
+#include "UIMouseHandler.h"
+#include "UIVMCloseDialog.h"
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CGraphicsAdapter.h"
+#include "CSnapshot.h"
+
+/* Other VBox includes: */
+#include <VBox/version.h>
+#ifdef VBOX_BLEEDING_EDGE
+# include <iprt/buildconfig.h>
+#endif /* VBOX_BLEEDING_EDGE */
+
+
+/* static */
+UIMachineWindow* UIMachineWindow::create(UIMachineLogic *pMachineLogic, ulong uScreenId)
+{
+ /* Create machine-window: */
+ UIMachineWindow *pMachineWindow = 0;
+ switch (pMachineLogic->visualStateType())
+ {
+ case UIVisualStateType_Normal:
+ pMachineWindow = new UIMachineWindowNormal(pMachineLogic, uScreenId);
+ break;
+ case UIVisualStateType_Fullscreen:
+ pMachineWindow = new UIMachineWindowFullscreen(pMachineLogic, uScreenId);
+ break;
+ case UIVisualStateType_Seamless:
+ pMachineWindow = new UIMachineWindowSeamless(pMachineLogic, uScreenId);
+ break;
+ case UIVisualStateType_Scale:
+ pMachineWindow = new UIMachineWindowScale(pMachineLogic, uScreenId);
+ break;
+ default:
+ AssertMsgFailed(("Incorrect visual state!"));
+ break;
+ }
+ /* Prepare machine-window: */
+ pMachineWindow->prepare();
+ /* Return machine-window: */
+ return pMachineWindow;
+}
+
+/* static */
+void UIMachineWindow::destroy(UIMachineWindow *pWhichWindow)
+{
+ /* Cleanup machine-window: */
+ pWhichWindow->cleanup();
+ /* Delete machine-window: */
+ delete pWhichWindow;
+}
+
+void UIMachineWindow::prepare()
+{
+ /* Prepare session-connections: */
+ prepareSessionConnections();
+
+ /* Prepare main-layout: */
+ prepareMainLayout();
+
+ /* Prepare menu: */
+ prepareMenu();
+
+ /* Prepare status-bar: */
+ prepareStatusBar();
+
+ /* Prepare visual-state: */
+ prepareVisualState();
+
+ /* Prepare machine-view: */
+ prepareMachineView();
+
+ /* Prepare notification-center: */
+ prepareNotificationCenter();
+
+ /* Prepare handlers: */
+ prepareHandlers();
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Retranslate window: */
+ retranslateUi();
+
+ /* Show (must be done before updating the appearance): */
+ showInNecessaryMode();
+
+ /* Update all the elements: */
+ updateAppearanceOf(UIVisualElement_AllStuff);
+
+#ifdef VBOX_WS_X11
+ /* Prepare default class/name values: */
+ const QString strWindowClass = QString("VirtualBox Machine");
+ QString strWindowName = strWindowClass;
+ /* Check if we want Window Manager to distinguish Virtual Machine windows: */
+ if (gEDataManager->distinguishMachineWindowGroups(uiCommon().managedVMUuid()))
+ strWindowName = QString("VirtualBox Machine UUID: %1").arg(uiCommon().managedVMUuid().toString());
+ /* Assign WM_CLASS property: */
+ NativeWindowSubsystem::X11SetWMClass(this, strWindowName, strWindowClass);
+ /* Tell the WM we are well behaved wrt Xwayland keyboard-grabs: */
+ NativeWindowSubsystem::X11SetXwaylandMayGrabKeyboardFlag(this);
+#endif
+}
+
+void UIMachineWindow::cleanup()
+{
+ /* Save window settings: */
+ saveSettings();
+
+ /* Cleanup handlers: */
+ cleanupHandlers();
+
+ /* Cleanup visual-state: */
+ cleanupVisualState();
+
+ /* Cleanup notification-center: */
+ cleanupNotificationCenter();
+
+ /* Cleanup machine-view: */
+ cleanupMachineView();
+
+ /* Cleanup status-bar: */
+ cleanupStatusBar();
+
+ /* Cleanup menu: */
+ cleanupMenu();
+
+ /* Cleanup main layout: */
+ cleanupMainLayout();
+
+ /* Cleanup session connections: */
+ cleanupSessionConnections();
+}
+
+void UIMachineWindow::sltMachineStateChanged()
+{
+ /* Update window-title: */
+ updateAppearanceOf(UIVisualElement_WindowTitle);
+}
+
+UIMachineWindow::UIMachineWindow(UIMachineLogic *pMachineLogic, ulong uScreenId)
+ : QIWithRetranslateUI2<QMainWindow>(0, pMachineLogic->windowFlags(uScreenId))
+ , m_pMachineLogic(pMachineLogic)
+ , m_pMachineView(0)
+ , m_uScreenId(uScreenId)
+ , m_pMainLayout(0)
+ , m_pTopSpacer(0)
+ , m_pBottomSpacer(0)
+ , m_pLeftSpacer(0)
+ , m_pRightSpacer(0)
+{
+#ifndef VBOX_WS_MAC
+ /* Set machine-window icon if any: */
+ // On macOS window icon is referenced in info.plist.
+ if (uisession() && uisession()->machineWindowIcon())
+ setWindowIcon(*uisession()->machineWindowIcon());
+#endif /* !VBOX_WS_MAC */
+}
+
+UIActionPool* UIMachineWindow::actionPool() const
+{
+ return machineLogic()->actionPool();
+}
+
+UISession* UIMachineWindow::uisession() const
+{
+ return machineLogic()->uisession();
+}
+
+CSession& UIMachineWindow::session() const
+{
+ return uisession()->session();
+}
+
+CMachine& UIMachineWindow::machine() const
+{
+ return uisession()->machine();
+}
+
+CConsole& UIMachineWindow::console() const
+{
+ return uisession()->console();
+}
+
+const QString& UIMachineWindow::machineName() const
+{
+ return uisession()->machineName();
+}
+
+bool UIMachineWindow::shouldResizeToGuestDisplay() const
+{
+ return actionPool()
+ && actionPool()->action(UIActionIndexRT_M_View_T_GuestAutoresize)
+ && actionPool()->action(UIActionIndexRT_M_View_T_GuestAutoresize)->isChecked();
+}
+
+void UIMachineWindow::adjustMachineViewSize()
+{
+ /* We need to adjust guest-screen size if necessary: */
+ machineView()->adjustGuestScreenSize();
+}
+
+void UIMachineWindow::sendMachineViewSizeHint()
+{
+ /* Send machine-view size-hint to the guest: */
+ machineView()->resendSizeHint();
+}
+
+#ifdef VBOX_WITH_MASKED_SEAMLESS
+void UIMachineWindow::setMask(const QRegion &region)
+{
+ /* Call to base-class: */
+ QMainWindow::setMask(region);
+}
+#endif /* VBOX_WITH_MASKED_SEAMLESS */
+
+void UIMachineWindow::updateAppearanceOf(int iElement)
+{
+ /* Update window title: */
+ if (iElement & UIVisualElement_WindowTitle)
+ {
+ /* Make sure machine state is one of valid: */
+ const KMachineState enmState = uisession()->machineState();
+ if (enmState == KMachineState_Null)
+ return;
+
+ /* Prepare full name: */
+ QString strMachineName = machineName();
+
+ /* Append snapshot name: */
+ if (machine().GetSnapshotCount() > 0)
+ {
+ const CSnapshot comSnapshot = machine().GetCurrentSnapshot();
+ strMachineName += " (" + comSnapshot.GetName() + ")";
+ }
+
+ /* Append state name: */
+ strMachineName += " [" + gpConverter->toString(enmState) + "]";
+
+#ifndef VBOX_WS_MAC
+ /* Append user product name (besides macOS): */
+ const QString strUserProductName = uisession()->machineWindowNamePostfix();
+ strMachineName += " - " + (strUserProductName.isEmpty() ? defaultWindowTitle() : strUserProductName);
+#endif /* !VBOX_WS_MAC */
+
+ /* Check if we can get graphics adapter: */
+ CGraphicsAdapter comAdapter = machine().GetGraphicsAdapter();
+ if (machine().isOk() && comAdapter.isNotNull())
+ {
+ /* Append screen number only if there are more than one present: */
+ if (comAdapter.GetMonitorCount() > 1)
+ strMachineName += QString(" : %1").arg(m_uScreenId + 1);
+ }
+
+ /* Assign title finally: */
+ setWindowTitle(strMachineName);
+ }
+}
+
+void UIMachineWindow::retranslateUi()
+{
+ /* Compose window-title prefix: */
+ m_strWindowTitlePrefix = VBOX_PRODUCT;
+#ifdef VBOX_BLEEDING_EDGE
+ m_strWindowTitlePrefix += UIMachineWindow::tr(" EXPERIMENTAL build %1r%2 - %3")
+ .arg(RTBldCfgVersion())
+ .arg(RTBldCfgRevisionStr())
+ .arg(VBOX_BLEEDING_EDGE);
+#endif /* VBOX_BLEEDING_EDGE */
+ /* Update appearance of the window-title: */
+ updateAppearanceOf(UIVisualElement_WindowTitle);
+}
+
+bool UIMachineWindow::event(QEvent *pEvent)
+{
+ /* Call to base-class: */
+ const bool fResult = QIWithRetranslateUI2<QMainWindow>::event(pEvent);
+
+ /* Handle particular events: */
+ switch (pEvent->type())
+ {
+ case QEvent::WindowActivate:
+ {
+ /* Initiate registration in the modal window manager: */
+ windowManager().setMainWindowShown(this);
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIMachineWindow::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QMainWindow::showEvent(pEvent);
+
+ /* Initiate registration in the modal window manager: */
+ windowManager().setMainWindowShown(this);
+
+ /* Update appearance for indicator-pool: */
+ updateAppearanceOf(UIVisualElement_IndicatorPoolStuff);
+}
+
+void UIMachineWindow::hideEvent(QHideEvent *pEvent)
+{
+ /* Update registration in the modal window manager: */
+ if (windowManager().mainWindowShown() == this)
+ {
+ if (machineLogic()->activeMachineWindow())
+ windowManager().setMainWindowShown(machineLogic()->activeMachineWindow());
+ else
+ windowManager().setMainWindowShown(machineLogic()->mainMachineWindow());
+ }
+
+ /* Call to base-class: */
+ QMainWindow::hideEvent(pEvent);
+}
+
+void UIMachineWindow::closeEvent(QCloseEvent *pCloseEvent)
+{
+ /* Always ignore close-event first: */
+ pCloseEvent->ignore();
+
+ /* Make sure machine is in one of the allowed states: */
+ if (!uisession()->isRunning() && !uisession()->isPaused() && !uisession()->isStuck())
+ {
+#if defined(VBOX_IS_QT6_OR_LATER) && defined(VBOX_WS_MAC) /** @todo qt6 ... */
+ /* If we want to close the application, we need to accept the close event.
+ If we don't the QEvent::Quit processing in QApplication::event fails and
+ [QCocoaApplicationDelegate applicationShouldTerminate] complains printing
+ "Qt DEBUG: Application termination canceled" in the debug log. */
+ /** @todo qt6: This could easily be caused by something else, but needs to be
+ * looked at by a proper GUI expert. */
+ if (uisession()->isTurnedOff()) /** @todo qt6: Better state check here? */
+ pCloseEvent->accept();
+#endif
+ return;
+ }
+
+ /* If there is a close hook script defined: */
+ const QString strScript = gEDataManager->machineCloseHookScript(uiCommon().managedVMUuid());
+ if (!strScript.isEmpty())
+ {
+ /* Execute asynchronously and leave: */
+ QProcess::startDetached(strScript, QStringList() << machine().GetId().toString());
+ return;
+ }
+
+ /* Choose the close action: */
+ MachineCloseAction closeAction = MachineCloseAction_Invalid;
+
+ /* If default close-action defined and not restricted: */
+ MachineCloseAction defaultCloseAction = uisession()->defaultCloseAction();
+ MachineCloseAction restrictedCloseActions = uisession()->restrictedCloseActions();
+ if ((defaultCloseAction != MachineCloseAction_Invalid) &&
+ !(restrictedCloseActions & defaultCloseAction))
+ {
+ switch (defaultCloseAction)
+ {
+ /* If VM is stuck, and the default close-action is 'detach', 'save-state' or 'shutdown',
+ * we should ask the user about what to do: */
+ case MachineCloseAction_Detach:
+ case MachineCloseAction_SaveState:
+ case MachineCloseAction_Shutdown:
+ closeAction = uisession()->isStuck() ? MachineCloseAction_Invalid : defaultCloseAction;
+ break;
+ /* Otherwise we just use what we have: */
+ default:
+ closeAction = defaultCloseAction;
+ break;
+ }
+ }
+
+ /* If the close-action still undefined: */
+ if (closeAction == MachineCloseAction_Invalid)
+ {
+ /* Prepare close-dialog: */
+ QWidget *pParentDlg = windowManager().realParentWindow(this);
+ QPointer<UIVMCloseDialog> pCloseDlg = new UIVMCloseDialog(pParentDlg, machine(),
+ console().GetGuestEnteredACPIMode(),
+ restrictedCloseActions);
+ /* Configure close-dialog: */
+ if (uisession() && uisession()->machineWindowIcon())
+ pCloseDlg->setIcon(*uisession()->machineWindowIcon());
+
+ /* Make sure close-dialog is valid: */
+ if (pCloseDlg->isValid())
+ {
+ /* We are going to show close-dialog: */
+ bool fShowCloseDialog = true;
+ /* Check if VM is paused or stuck: */
+ const bool fWasPaused = uisession()->isPaused();
+ const bool fIsStuck = uisession()->isStuck();
+ /* If VM is NOT paused and NOT stuck: */
+ if (!fWasPaused && !fIsStuck)
+ {
+ /* We should pause it first: */
+ const bool fIsPaused = uisession()->pause();
+ /* If we were unable to pause VM: */
+ if (!fIsPaused)
+ {
+ /* If that is NOT the separate VM process UI: */
+ if (!uiCommon().isSeparateProcess())
+ {
+ /* We are not going to show close-dialog: */
+ fShowCloseDialog = false;
+ }
+ /* If that is the separate VM process UI: */
+ else
+ {
+ /* We are going to show close-dialog only
+ * if headless frontend stopped/killed already: */
+ CMachine machine = uisession()->machine();
+ KMachineState machineState = machine.GetState();
+ fShowCloseDialog = !machine.isOk() || machineState == KMachineState_Null;
+ }
+ }
+ }
+ /* If we are going to show close-dialog: */
+ if (fShowCloseDialog)
+ {
+ /* Show close-dialog to let the user make the choice: */
+ windowManager().registerNewParent(pCloseDlg, pParentDlg);
+ closeAction = static_cast<MachineCloseAction>(pCloseDlg->exec());
+
+ /* Make sure the dialog still valid: */
+ if (!pCloseDlg)
+ return;
+
+ /* If VM was not paused before but paused now,
+ * we should resume it if user canceled dialog or chosen shutdown: */
+ if (!fWasPaused && uisession()->isPaused() &&
+ (closeAction == MachineCloseAction_Invalid ||
+ closeAction == MachineCloseAction_Detach ||
+ closeAction == MachineCloseAction_Shutdown))
+ {
+ /* If we unable to resume VM, cancel closing: */
+ if (!uisession()->unpause())
+ closeAction = MachineCloseAction_Invalid;
+ }
+ }
+ }
+ else
+ {
+ /* Else user misconfigured .vbox file, we will reject closing UI: */
+ closeAction = MachineCloseAction_Invalid;
+ }
+
+ /* Cleanup close-dialog: */
+ delete pCloseDlg;
+ }
+
+ /* Depending on chosen result: */
+ switch (closeAction)
+ {
+ case MachineCloseAction_Detach:
+ {
+ /* Detach GUI: */
+ LogRel(("GUI: Request for close-action to detach GUI.\n"));
+ uisession()->detachUi();
+ break;
+ }
+ case MachineCloseAction_SaveState:
+ {
+ /* Save VM state: */
+ LogRel(("GUI: Request for close-action to save VM state.\n"));
+ uisession()->saveState();
+ break;
+ }
+ case MachineCloseAction_Shutdown:
+ {
+ /* Shutdown VM: */
+ LogRel(("GUI: Request for close-action to shutdown VM.\n"));
+ uisession()->shutdown();
+ break;
+ }
+ case MachineCloseAction_PowerOff:
+ case MachineCloseAction_PowerOff_RestoringSnapshot:
+ {
+ /* Power VM off: */
+ LogRel(("GUI: Request for close-action to power VM off.\n"));
+ const bool fDiscardStateOnPowerOff = gEDataManager->discardStateOnPowerOff(uiCommon().managedVMUuid())
+ || closeAction == MachineCloseAction_PowerOff_RestoringSnapshot;
+ uisession()->powerOff(machine().GetSnapshotCount() > 0 && fDiscardStateOnPowerOff);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void UIMachineWindow::prepareSessionConnections()
+{
+ /* We should watch for console events: */
+ connect(uisession(), &UISession::sigMachineStateChange, this, &UIMachineWindow::sltMachineStateChanged);
+}
+
+void UIMachineWindow::prepareMainLayout()
+{
+ /* Create central-widget: */
+ setCentralWidget(new QWidget);
+
+ /* Create main-layout: */
+ m_pMainLayout = new QGridLayout(centralWidget());
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+ m_pMainLayout->setSpacing(0);
+
+ /* Create shifting-spacers: */
+ m_pTopSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
+ m_pBottomSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
+ m_pLeftSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed);
+ m_pRightSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed);
+
+ /* Add shifting-spacers into main-layout: */
+ m_pMainLayout->addItem(m_pTopSpacer, 0, 1);
+ m_pMainLayout->addItem(m_pBottomSpacer, 2, 1);
+ m_pMainLayout->addItem(m_pLeftSpacer, 1, 0);
+ m_pMainLayout->addItem(m_pRightSpacer, 1, 2);
+}
+
+void UIMachineWindow::prepareMachineView()
+{
+ /* Get visual-state type: */
+ UIVisualStateType visualStateType = machineLogic()->visualStateType();
+
+ /* Create machine-view: */
+ m_pMachineView = UIMachineView::create(this, m_uScreenId, visualStateType);
+
+ /* Listen for frame-buffer resize: */
+ connect(m_pMachineView, &UIMachineView::sigFrameBufferResize, this, &UIMachineWindow::sigFrameBufferResize);
+
+ /* Add machine-view into main-layout: */
+ m_pMainLayout->addWidget(m_pMachineView, 1, 1, viewAlignment(visualStateType));
+
+ /* Install focus-proxy: */
+ setFocusProxy(m_pMachineView);
+}
+
+void UIMachineWindow::prepareNotificationCenter()
+{
+ // for now it will be added from within particular visual mode windows ..
+}
+
+void UIMachineWindow::prepareHandlers()
+{
+ /* Register keyboard-handler: */
+ machineLogic()->keyboardHandler()->prepareListener(m_uScreenId, this);
+
+ /* Register mouse-handler: */
+ machineLogic()->mouseHandler()->prepareListener(m_uScreenId, this);
+}
+
+void UIMachineWindow::cleanupHandlers()
+{
+ /* Unregister mouse-handler: */
+ machineLogic()->mouseHandler()->cleanupListener(m_uScreenId);
+
+ /* Unregister keyboard-handler: */
+ machineLogic()->keyboardHandler()->cleanupListener(m_uScreenId);
+}
+
+void UIMachineWindow::cleanupNotificationCenter()
+{
+ // for now it will be removed from within particular visual mode windows ..
+}
+
+void UIMachineWindow::cleanupMachineView()
+{
+ /* Destroy machine-view: */
+ UIMachineView::destroy(m_pMachineView);
+ m_pMachineView = 0;
+}
+
+void UIMachineWindow::cleanupSessionConnections()
+{
+ /* We should stop watching for console events: */
+ disconnect(uisession(), &UISession::sigMachineStateChange, this, &UIMachineWindow::sltMachineStateChanged);
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+void UIMachineWindow::updateDbgWindows()
+{
+ /* The debugger windows are bind to the main VM window. */
+ if (m_uScreenId == 0)
+ machineLogic()->dbgAdjustRelativePos();
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+/* static */
+Qt::Alignment UIMachineWindow::viewAlignment(UIVisualStateType visualStateType)
+{
+ switch (visualStateType)
+ {
+ case UIVisualStateType_Normal: return Qt::Alignment();
+ case UIVisualStateType_Fullscreen: return Qt::AlignVCenter | Qt::AlignHCenter;
+ case UIVisualStateType_Seamless: return Qt::Alignment();
+ case UIVisualStateType_Scale: return Qt::Alignment();
+ case UIVisualStateType_Invalid: case UIVisualStateType_All: break; /* Shut up, MSC! */
+ }
+ AssertMsgFailed(("Incorrect visual state!"));
+ return Qt::Alignment();
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineWindow::handleStandardWindowButtonCallback(StandardWindowButtonType enmButtonType, bool fWithOptionKey)
+{
+ switch (enmButtonType)
+ {
+ case StandardWindowButtonType_Zoom:
+ {
+ /* Handle 'Zoom' button for 'Normal' and 'Scaled' modes: */
+ if ( machineLogic()->visualStateType() == UIVisualStateType_Normal
+ || machineLogic()->visualStateType() == UIVisualStateType_Scale)
+ {
+ if (fWithOptionKey)
+ {
+ /* Toggle window zoom: */
+ darwinToggleWindowZoom(this);
+ }
+ else
+ {
+ /* Enter 'full-screen' mode: */
+ uisession()->setRequestedVisualState(UIVisualStateType_Invalid);
+ uisession()->changeVisualState(UIVisualStateType_Fullscreen);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/* static */
+void UIMachineWindow::handleNativeNotification(const QString &strNativeNotificationName, QWidget *pWidget)
+{
+ /* Handle arrived notification: */
+ LogRel(("GUI: UIMachineWindow::handleNativeNotification: Notification '%s' received\n",
+ strNativeNotificationName.toLatin1().constData()));
+ AssertPtrReturnVoid(pWidget);
+ if (UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(pWidget))
+ {
+ /* Redirect arrived notification: */
+ LogRel2(("UIMachineWindow::handleNativeNotification: Redirecting '%s' notification to corresponding machine-window...\n",
+ strNativeNotificationName.toLatin1().constData()));
+ pMachineWindow->handleNativeNotification(strNativeNotificationName);
+ }
+}
+
+/* static */
+void UIMachineWindow::handleStandardWindowButtonCallback(StandardWindowButtonType enmButtonType, bool fWithOptionKey, QWidget *pWidget)
+{
+ /* Handle arrived callback: */
+ LogRel(("GUI: UIMachineWindow::handleStandardWindowButtonCallback: Callback for standard window button '%d' with option key '%d' received\n",
+ (int)enmButtonType, (int)fWithOptionKey));
+ AssertPtrReturnVoid(pWidget);
+ if (UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(pWidget))
+ {
+ /* Redirect arrived callback: */
+ LogRel2(("UIMachineWindow::handleStandardWindowButtonCallback: Redirecting callback for standard window button '%d' with option key '%d' to corresponding machine-window...\n",
+ (int)enmButtonType, (int)fWithOptionKey));
+ pMachineWindow->handleStandardWindowButtonCallback(enmButtonType, fWithOptionKey);
+ }
+}
+#endif /* VBOX_WS_MAC */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.h
new file mode 100644
index 00000000..b527ec5a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.h
@@ -0,0 +1,229 @@
+/* $Id: UIMachineWindow.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindow class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIMachineWindow_h
+#define FEQT_INCLUDED_SRC_runtime_UIMachineWindow_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMainWindow>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+
+/* Forward declarations: */
+class QCloseEvent;
+class QEvent;
+class QHideEvent;
+class QGridLayout;
+class QShowEvent;
+class QSpacerItem;
+class UIActionPool;
+class UISession;
+class UIMachineLogic;
+class UIMachineView;
+class CSession;
+
+
+/* Machine-window interface: */
+class UIMachineWindow : public QIWithRetranslateUI2<QMainWindow>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about frame-buffer resize. */
+ void sigFrameBufferResize();
+
+public:
+
+ /* Factory functions to create/destroy machine-window: */
+ static UIMachineWindow* create(UIMachineLogic *pMachineLogic, ulong uScreenId = 0);
+ static void destroy(UIMachineWindow *pWhichWindow);
+
+ /* Prepare/cleanup machine-window: */
+ void prepare();
+ void cleanup();
+
+ /* Public getters: */
+ ulong screenId() const { return m_uScreenId; }
+ UIMachineView* machineView() const { return m_pMachineView; }
+ UIMachineLogic* machineLogic() const { return m_pMachineLogic; }
+ UIActionPool* actionPool() const;
+ UISession* uisession() const;
+
+ /** Returns the session reference. */
+ CSession& session() const;
+ /** Returns the session's machine reference. */
+ CMachine& machine() const;
+ /** Returns the session's console reference. */
+ CConsole& console() const;
+
+ /** Returns the machine name. */
+ const QString& machineName() const;
+
+ /** Returns whether the machine-window should resize to fit to the guest display.
+ * @note Relevant only to normal (windowed) case. */
+ bool shouldResizeToGuestDisplay() const;
+
+ /** Restores cached window geometry.
+ * @note Reimplemented in sub-classes. Base implementation does nothing. */
+ virtual void restoreCachedGeometry() {}
+
+ /** Adjusts machine-window size to correspond current machine-view size.
+ * @param fAdjustPosition determines whether is it necessary to adjust position too.
+ * @param fResizeToGuestDisplay determines if is it necessary to resize the window to fit to guest display size.
+ * @note Reimplemented in sub-classes. Base implementation does nothing. */
+ virtual void normalizeGeometry(bool fAdjustPosition, bool fResizeToGuestDisplay) { Q_UNUSED(fAdjustPosition); Q_UNUSED(fResizeToGuestDisplay); }
+
+ /** Adjusts machine-view size to correspond current machine-window size. */
+ virtual void adjustMachineViewSize();
+
+ /** Sends machine-view size-hint to the guest. */
+ virtual void sendMachineViewSizeHint();
+
+#ifdef VBOX_WITH_MASKED_SEAMLESS
+ /* Virtual caller for base class setMask: */
+ virtual void setMask(const QRegion &region);
+#endif /* VBOX_WITH_MASKED_SEAMLESS */
+
+ /** Makes sure window is exposed in required mode/state. */
+ virtual void showInNecessaryMode() = 0;
+
+ /** Updates appearance for specified @a iElement. */
+ virtual void updateAppearanceOf(int iElement);
+
+protected slots:
+
+#ifdef VBOX_WS_X11
+ /** X11: Performs machine-window geometry normalization. */
+ void sltNormalizeGeometry() { normalizeGeometry(true /* adjust position */, shouldResizeToGuestDisplay()); }
+#endif /* VBOX_WS_X11 */
+
+ /** Performs machine-window activation. */
+ void sltActivateWindow() { activateWindow(); }
+
+ /* Session event-handlers: */
+ virtual void sltMachineStateChanged();
+
+protected:
+
+ /* Constructor: */
+ UIMachineWindow(UIMachineLogic *pMachineLogic, ulong uScreenId);
+
+ /* Translate stuff: */
+ void retranslateUi();
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles hide @a pEvent. */
+ virtual void hideEvent(QHideEvent *pEvent) RT_OVERRIDE;
+
+ /** Close event handler. */
+ void closeEvent(QCloseEvent *pCloseEvent);
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Handles native notifications.
+ * @param strNativeNotificationName Native notification name. */
+ virtual void handleNativeNotification(const QString & /* strNativeNotificationName */) {}
+
+ /** Mac OS X: Handles standard window button callbacks.
+ * @param enmButtonType Brings standard window button type.
+ * @param fWithOptionKey Brings whether the Option key was held. */
+ virtual void handleStandardWindowButtonCallback(StandardWindowButtonType enmButtonType, bool fWithOptionKey);
+#endif /* VBOX_WS_MAC */
+
+ /* Prepare helpers: */
+ virtual void prepareSessionConnections();
+ virtual void prepareMainLayout();
+ virtual void prepareMenu() {}
+ virtual void prepareStatusBar() {}
+ virtual void prepareMachineView();
+ virtual void prepareNotificationCenter();
+ virtual void prepareVisualState() {}
+ virtual void prepareHandlers();
+ virtual void loadSettings() {}
+
+ /* Cleanup helpers: */
+ virtual void saveSettings() {}
+ virtual void cleanupHandlers();
+ virtual void cleanupVisualState() {}
+ virtual void cleanupNotificationCenter();
+ virtual void cleanupMachineView();
+ virtual void cleanupStatusBar() {}
+ virtual void cleanupMenu() {}
+ virtual void cleanupMainLayout() {}
+ virtual void cleanupSessionConnections();
+
+ /* Update stuff: */
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ void updateDbgWindows();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+ /* Helpers: */
+ const QString& defaultWindowTitle() const { return m_strWindowTitlePrefix; }
+ static Qt::Alignment viewAlignment(UIVisualStateType visualStateType);
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Handles native notifications.
+ * @param strNativeNotificationName Native notification name.
+ * @param pWidget Widget, notification related to. */
+ static void handleNativeNotification(const QString &strNativeNotificationName, QWidget *pWidget);
+
+ /** Mac OS X: Handles standard window button callbacks.
+ * @param enmButtonType Brings standard window button type.
+ * @param fWithOptionKey Brings whether the Option key was held.
+ * @param pWidget Brings widget, callback related to. */
+ static void handleStandardWindowButtonCallback(StandardWindowButtonType enmButtonType, bool fWithOptionKey, QWidget *pWidget);
+#endif /* VBOX_WS_MAC */
+
+ /* Variables: */
+ UIMachineLogic *m_pMachineLogic;
+ UIMachineView *m_pMachineView;
+ QString m_strWindowTitlePrefix;
+ ulong m_uScreenId;
+ QGridLayout *m_pMainLayout;
+ QSpacerItem *m_pTopSpacer;
+ QSpacerItem *m_pBottomSpacer;
+ QSpacerItem *m_pLeftSpacer;
+ QSpacerItem *m_pRightSpacer;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIMachineWindow_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.cpp
new file mode 100644
index 00000000..346ce09b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.cpp
@@ -0,0 +1,1345 @@
+/* $Id: UIMouseHandler.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMouseHandler class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QtMath>
+#include <QMouseEvent>
+#include <QTimer>
+#include <QTouchEvent>
+
+/* GUI includes: */
+#include "UICursor.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+#include "UISession.h"
+#include "UIMachineLogic.h"
+#include "UIMachineWindow.h"
+#include "UIMachineView.h"
+#include "UIKeyboardHandler.h"
+#include "UIMouseHandler.h"
+#include "UIFrameBuffer.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+# include "CocoaEventHelper.h"
+#endif
+#ifdef VBOX_WS_WIN
+# include "VBoxUtils-win.h"
+#endif
+#ifdef VBOX_WS_X11
+# include "VBoxUtils-x11.h"
+#endif
+
+/* COM includes: */
+#include "CDisplay.h"
+#include "CMouse.h"
+
+/* Other VBox includes: */
+#include <iprt/time.h>
+
+
+/* Factory function to create mouse-handler: */
+UIMouseHandler* UIMouseHandler::create(UIMachineLogic *pMachineLogic,
+ UIVisualStateType visualStateType)
+{
+ /* Prepare mouse-handler: */
+ UIMouseHandler *pMouseHandler = 0;
+ /* Depending on visual-state type: */
+ switch (visualStateType)
+ {
+ /* For now all the states using common mouse-handler: */
+ case UIVisualStateType_Normal:
+ case UIVisualStateType_Fullscreen:
+ case UIVisualStateType_Seamless:
+ case UIVisualStateType_Scale:
+ pMouseHandler = new UIMouseHandler(pMachineLogic);
+ break;
+ default:
+ break;
+ }
+ /* Return prepared mouse-handler: */
+ return pMouseHandler;
+}
+
+/* Factory function to destroy mouse-handler: */
+void UIMouseHandler::destroy(UIMouseHandler *pMouseHandler)
+{
+ /* Delete mouse-handler: */
+ delete pMouseHandler;
+}
+
+/* Prepare listener for particular machine-window: */
+void UIMouseHandler::prepareListener(ulong uIndex, UIMachineWindow *pMachineWindow)
+{
+ /* If that window is NOT registered yet: */
+ if (!m_windows.contains(uIndex))
+ {
+ /* Register machine-window: */
+ m_windows.insert(uIndex, pMachineWindow);
+ /* Install event-filter for machine-window: */
+ m_windows[uIndex]->installEventFilter(this);
+ }
+
+ /* If that view is NOT registered yet: */
+ if (!m_views.contains(uIndex))
+ {
+ /* Register machine-view: */
+ m_views.insert(uIndex, pMachineWindow->machineView());
+ /* Install event-filter for machine-view: */
+ m_views[uIndex]->installEventFilter(this);
+ /* Make machine-view notify mouse-handler about mouse pointer shape change: */
+ connect(m_views[uIndex], &UIMachineView::sigMousePointerShapeChange, this, &UIMouseHandler::sltMousePointerShapeChanged);
+ /* Make machine-view notify mouse-handler about frame-buffer resize: */
+ connect(m_views[uIndex], &UIMachineView::sigFrameBufferResize, this, &UIMouseHandler::sltMousePointerShapeChanged);
+ }
+
+ /* If that viewport is NOT registered yet: */
+ if (!m_viewports.contains(uIndex))
+ {
+ /* Register machine-view-viewport: */
+ m_viewports.insert(uIndex, pMachineWindow->machineView()->viewport());
+ /* Install event-filter for machine-view-viewport: */
+ m_viewports[uIndex]->installEventFilter(this);
+ }
+}
+
+/* Cleanup listener for particular machine-window: */
+void UIMouseHandler::cleanupListener(ulong uIndex)
+{
+ /* Check if we should release mouse first: */
+ if ((int)uIndex == m_iMouseCaptureViewIndex)
+ releaseMouse();
+
+ /* If that window still registered: */
+ if (m_windows.contains(uIndex))
+ {
+ /* Unregister machine-window: */
+ m_windows.remove(uIndex);
+ }
+
+ /* If that view still registered: */
+ if (m_views.contains(uIndex))
+ {
+ /* Unregister machine-view: */
+ m_views.remove(uIndex);
+ }
+
+ /* If that viewport still registered: */
+ if (m_viewports.contains(uIndex))
+ {
+ /* Unregister machine-view-viewport: */
+ m_viewports.remove(uIndex);
+ }
+}
+
+void UIMouseHandler::captureMouse(ulong uScreenId)
+{
+ /* Do not try to capture mouse if its captured already: */
+ if (uisession()->isMouseCaptured())
+ return;
+
+ /* If such viewport exists: */
+ if (m_viewports.contains(uScreenId))
+ {
+ /* Store mouse-capturing state value: */
+ uisession()->setMouseCaptured(true);
+
+ /* Memorize the index of machine-view-viewport captured mouse: */
+ m_iMouseCaptureViewIndex = uScreenId;
+
+ /* Memorize the host position where the cursor was captured: */
+ m_capturedMousePos = QCursor::pos();
+ /* Determine geometry of screen cursor was captured at: */
+ m_capturedScreenGeo = gpDesktop->screenGeometry(m_capturedMousePos);
+
+ /* Acquiring visible viewport rectangle in global coodrinates: */
+ QRect visibleRectangle = m_viewports[m_iMouseCaptureViewIndex]->visibleRegion().boundingRect();
+ QPoint visibleRectanglePos = m_views[m_iMouseCaptureViewIndex]->mapToGlobal(m_viewports[m_iMouseCaptureViewIndex]->pos());
+ visibleRectangle.translate(visibleRectanglePos);
+ visibleRectangle = visibleRectangle.intersected(gpDesktop->availableGeometry(machineLogic()->machineWindows()[m_iMouseCaptureViewIndex]));
+
+#ifdef VBOX_WS_WIN
+ /* Move the mouse to the center of the visible area: */
+ m_lastMousePos = visibleRectangle.center();
+ QCursor::setPos(m_lastMousePos);
+ /* Update mouse clipping: */
+ updateMouseCursorClipping();
+#elif defined (VBOX_WS_MAC)
+ /* Grab all mouse events: */
+ ::darwinMouseGrab(m_viewports[m_iMouseCaptureViewIndex]);
+#else /* VBOX_WS_MAC */
+ /* Remember current mouse position: */
+ m_lastMousePos = QCursor::pos();
+ /* Grab all mouse events: */
+ m_viewports[m_iMouseCaptureViewIndex]->grabMouse();
+#endif /* !VBOX_WS_MAC */
+
+ /* Switch guest mouse to the relative mode: */
+ mouse().PutMouseEvent(0, 0, 0, 0, 0);
+
+ /* Notify all the listeners: */
+ emit sigStateChange(state());
+ }
+}
+
+void UIMouseHandler::releaseMouse()
+{
+ /* Do not try to release mouse if its released already: */
+ if (!uisession()->isMouseCaptured())
+ return;
+
+ /* If such viewport exists: */
+ if (m_viewports.contains(m_iMouseCaptureViewIndex))
+ {
+ /* Store mouse-capturing state value: */
+ uisession()->setMouseCaptured(false);
+
+ /* Return the cursor to where it was when we captured it: */
+ QCursor::setPos(m_capturedMousePos);
+#ifdef VBOX_WS_WIN
+ /* Update mouse clipping: */
+ updateMouseCursorClipping();
+#elif defined(VBOX_WS_MAC)
+ /* Releasing grabbed mouse from that view: */
+ ::darwinMouseRelease(m_viewports[m_iMouseCaptureViewIndex]);
+#else /* VBOX_WS_MAC */
+ /* Releasing grabbed mouse from that view: */
+ m_viewports[m_iMouseCaptureViewIndex]->releaseMouse();
+#endif /* !VBOX_WS_MAC */
+ /* Reset mouse-capture index: */
+ m_iMouseCaptureViewIndex = -1;
+
+ /* Notify all the listeners: */
+ emit sigStateChange(state());
+ }
+}
+
+/* Setter for mouse-integration feature: */
+void UIMouseHandler::setMouseIntegrationEnabled(bool fEnabled)
+{
+ /* Do not do anything if its already done: */
+ if (uisession()->isMouseIntegrated() == fEnabled)
+ return;
+
+ /* Store mouse-integration state value: */
+ uisession()->setMouseIntegrated(fEnabled);
+
+ /* Reuse sltMouseCapabilityChanged() to update mouse state: */
+ sltMouseCapabilityChanged();
+}
+
+/* Current mouse state: */
+int UIMouseHandler::state() const
+{
+ return (uisession()->isMouseCaptured() ? UIMouseStateType_MouseCaptured : 0) |
+ (uisession()->isMouseSupportsAbsolute() ? UIMouseStateType_MouseAbsolute : 0) |
+ (uisession()->isMouseIntegrated() ? 0 : UIMouseStateType_MouseAbsoluteDisabled);
+}
+
+bool UIMouseHandler::nativeEventFilter(void *pMessage, ulong uScreenId)
+{
+ /* Make sure view with passed index exists: */
+ if (!m_views.contains(uScreenId))
+ return false;
+
+ /* Check if some system event should be filtered out.
+ * Returning @c true means filtering-out,
+ * Returning @c false means passing event to Qt. */
+ bool fResult = false; /* Pass to Qt by default. */
+
+# if defined(VBOX_WS_MAC)
+
+ /* Acquire carbon event reference from the cocoa one: */
+ EventRef event = static_cast<EventRef>(darwinCocoaToCarbonEvent(pMessage));
+
+ /* Depending on event kind: */
+ const UInt32 uEventKind = ::GetEventKind(event);
+ switch (uEventKind)
+ {
+ /* Watch for button-events: */
+ case kEventMouseDown:
+ case kEventMouseUp:
+ {
+ /* Acquire button number: */
+ EventMouseButton enmButton = 0;
+ ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton,
+ NULL, sizeof(enmButton), NULL, &enmButton);
+ /* If the event comes for primary mouse button: */
+ if (enmButton == kEventMouseButtonPrimary)
+ {
+ /* Acquire modifiers: */
+ UInt32 uKeyModifiers = ~0U;
+ ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
+ NULL, sizeof(uKeyModifiers), NULL, &uKeyModifiers);
+ /* If the event comes with Control modifier: */
+ if (uKeyModifiers == controlKey)
+ {
+ /* Replacing it with the stripped one: */
+ darwinPostStrippedMouseEvent(pMessage);
+ /* And filter out initial one: */
+ return true;
+ }
+ }
+ }
+ }
+
+# elif defined(VBOX_WS_WIN)
+
+ /* Nothing for now. */
+ RT_NOREF(pMessage, uScreenId);
+
+# elif defined(VBOX_WS_X11)
+
+ /* Cast to XCB event: */
+ xcb_generic_event_t *pEvent = static_cast<xcb_generic_event_t*>(pMessage);
+
+ /* Depending on event type: */
+ switch (pEvent->response_type & ~0x80)
+ {
+ /* Watch for button-events: */
+ case XCB_BUTTON_PRESS:
+ {
+ /* Do nothing if mouse is actively grabbed: */
+ if (uisession()->isMouseCaptured())
+ break;
+
+ /* If we see a mouse press from a grab while the mouse is not captured,
+ * release the keyboard before letting the event owner see it. This is
+ * because some owners cannot deal with failures to grab the keyboard
+ * themselves (e.g. window managers dragging windows). */
+
+ /* Cast to XCB button-event: */
+ xcb_button_press_event_t *pButtonEvent = static_cast<xcb_button_press_event_t*>(pMessage);
+
+ /* If this event is from our button grab then it will be reported relative to the root
+ * window and not to ours. In that case release the keyboard capture, re-capture it
+ * delayed, which will fail if we have lost the input focus in the mean-time, replay
+ * the button event for normal delivery (possibly straight back to us, but not relative
+ * to root this time) and tell Qt not to further process this event: */
+ if (pButtonEvent->event == pButtonEvent->root)
+ {
+ machineLogic()->keyboardHandler()->releaseKeyboard();
+ /** @todo It would be nicer to do this in the normal Qt button event
+ * handler to avoid avoidable races if the event was not for us. */
+ machineLogic()->keyboardHandler()->captureKeyboard(uScreenId);
+ /* Re-send the event so that the window which it was meant for gets it: */
+ xcb_allow_events_checked(NativeWindowSubsystem::X11GetConnection(), XCB_ALLOW_REPLAY_POINTER, pButtonEvent->time);
+ /* Do not let Qt see the event: */
+ return true;
+ }
+ }
+ default:
+ break;
+ }
+
+# else
+
+# warning "port me!"
+
+# endif
+
+ /* Return result: */
+ return fResult;
+}
+
+/* Machine state-change handler: */
+void UIMouseHandler::sltMachineStateChanged()
+{
+ /* Get machine state: */
+ KMachineState machineState = uisession()->machineState();
+ /* Handle particular machine states: */
+ switch (machineState)
+ {
+ case KMachineState_Paused:
+ case KMachineState_TeleportingPausedVM:
+ case KMachineState_Stuck:
+ {
+ /* Release the mouse: */
+ releaseMouse();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Recall reminder about paused VM input
+ * if we are not in paused VM state already: */
+ if (machineLogic()->activeMachineWindow() &&
+ machineState != KMachineState_Paused &&
+ machineState != KMachineState_TeleportingPausedVM)
+ UINotificationMessage::forgetAboutPausedVMInput();
+
+ /* Notify all the listeners: */
+ emit sigStateChange(state());
+}
+
+/* Mouse capability-change handler: */
+void UIMouseHandler::sltMouseCapabilityChanged()
+{
+ /* If mouse supports absolute pointing and mouse-integration activated: */
+ if (uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated())
+ {
+ /* Release the mouse: */
+ releaseMouse();
+ /* Also we should switch guest mouse to the absolute mode: */
+ mouse().PutMouseEventAbsolute(-1, -1, 0, 0, 0);
+ }
+#if 0 /* current team's decision is NOT to capture mouse on mouse-absolute mode loosing! */
+ /* If mouse-integration deactivated or mouse doesn't supports absolute pointing: */
+ else
+ {
+ /* Search for the machine-view focused now: */
+ int iFocusedView = -1;
+ QList<ulong> screenIds = m_views.keys();
+ for (int i = 0; i < screenIds.size(); ++i)
+ {
+ if (m_views[screenIds[i]]->hasFocus())
+ {
+ iFocusedView = screenIds[i];
+ break;
+ }
+ }
+ /* If there is no focused view but views are present we will use the first one: */
+ if (iFocusedView == -1 && !screenIds.isEmpty())
+ iFocusedView = screenIds[0];
+ /* Capture mouse using that view: */
+ if (iFocusedView != -1)
+ captureMouse(iFocusedView);
+ }
+#else /* but just to switch the guest mouse into relative mode! */
+ /* If mouse-integration deactivated or mouse doesn't supports absolute pointing: */
+ else
+ {
+ /* Switch guest mouse to the relative mode: */
+ mouse().PutMouseEvent(0, 0, 0, 0, 0);
+ }
+#endif
+
+ /* Notify user whether mouse supports absolute pointing
+ * if that method was called by corresponding signal: */
+ if (sender())
+ {
+ /* Do not annoy user while restoring VM: */
+ if (uisession()->machineState() != KMachineState_Restoring)
+ UINotificationMessage::remindAboutMouseIntegration(uisession()->isMouseSupportsAbsolute());
+ }
+
+ /* Notify all the listeners: */
+ emit sigStateChange(state());
+}
+
+/* Mouse pointer-shape-change handler: */
+void UIMouseHandler::sltMousePointerShapeChanged()
+{
+ /* First of all, we should check if the host pointer should be visible.
+ * We should hide host pointer in case of:
+ * 1. mouse is 'captured' or
+ * 2. machine is NOT 'paused' and mouse is NOT 'captured' and 'integrated' and 'absolute' but host pointer is 'hidden' by the guest. */
+ if (uisession()->isMouseCaptured() ||
+ (!uisession()->isPaused() &&
+ uisession()->isMouseIntegrated() &&
+ uisession()->isMouseSupportsAbsolute() &&
+ uisession()->isHidingHostPointer()))
+ {
+ QList<ulong> screenIds = m_viewports.keys();
+ for (int i = 0; i < screenIds.size(); ++i)
+ UICursor::setCursor(m_viewports[screenIds[i]], Qt::BlankCursor);
+ }
+
+ else
+
+ /* Otherwise we should show host pointer with guest shape assigned to it if:
+ * machine is NOT 'paused', mouse is 'integrated' and 'absolute' and valid pointer shape is present. */
+ if (!uisession()->isPaused() &&
+ uisession()->isMouseIntegrated() &&
+ uisession()->isMouseSupportsAbsolute() &&
+ uisession()->isValidPointerShapePresent())
+ {
+ QList<ulong> screenIds = m_viewports.keys();
+ for (int i = 0; i < screenIds.size(); ++i)
+ UICursor::setCursor(m_viewports[screenIds[i]], m_views[screenIds[i]]->cursor());
+ }
+
+ else
+
+ /* There could be other states covering such situations as:
+ * 1. machine is 'paused' or
+ * 2. mouse is NOT 'captured' and 'integrated' but NOT 'absolute' or
+ * 3. mouse is NOT 'captured' and 'absolute' but NOT 'integrated'.
+ * We have nothing to do with that except just unset the cursor. */
+ {
+ QList<ulong> screenIds = m_viewports.keys();
+ for (int i = 0; i < screenIds.size(); ++i)
+ UICursor::unsetCursor(m_viewports[screenIds[i]]);
+ }
+}
+
+void UIMouseHandler::sltMaybeActivateHoveredWindow()
+{
+ /* Are we still have hovered window to activate? */
+ if (m_pHoveredWindow && !m_pHoveredWindow->isActiveWindow())
+ {
+ /* Activate it: */
+ m_pHoveredWindow->activateWindow();
+#ifdef VBOX_WS_X11
+ /* On X11 its not enough to just activate window if you
+ * want to raise it also, so we will make it separately: */
+ m_pHoveredWindow->raise();
+#endif /* VBOX_WS_X11 */
+ }
+}
+
+/* Mouse-handler constructor: */
+UIMouseHandler::UIMouseHandler(UIMachineLogic *pMachineLogic)
+ : QObject(pMachineLogic)
+ , m_pMachineLogic(pMachineLogic)
+ , m_iLastMouseWheelDelta(0)
+ , m_iMouseCaptureViewIndex(-1)
+#ifdef VBOX_WS_WIN
+ , m_fCursorPositionReseted(false)
+#endif
+{
+ /* Machine state-change updater: */
+ connect(uisession(), &UISession::sigMachineStateChange, this, &UIMouseHandler::sltMachineStateChanged);
+
+ /* Mouse capability state-change updater: */
+ connect(uisession(), &UISession::sigMouseCapabilityChange, this, &UIMouseHandler::sltMouseCapabilityChanged);
+
+ /* Mouse pointer shape state-change updater: */
+ connect(this, &UIMouseHandler::sigStateChange, this, &UIMouseHandler::sltMousePointerShapeChanged);
+
+ /* Mouse cursor position state-change updater: */
+ connect(uisession(), &UISession::sigCursorPositionChange, this, &UIMouseHandler::sltMousePointerShapeChanged);
+
+ /* Initialize: */
+ sltMachineStateChanged();
+ sltMousePointerShapeChanged();
+ sltMouseCapabilityChanged();
+}
+
+/* Mouse-handler destructor: */
+UIMouseHandler::~UIMouseHandler()
+{
+}
+
+/* Machine-logic getter: */
+UIMachineLogic* UIMouseHandler::machineLogic() const
+{
+ return m_pMachineLogic;
+}
+
+/* UI Session getter: */
+UISession* UIMouseHandler::uisession() const
+{
+ return machineLogic()->uisession();
+}
+
+CDisplay& UIMouseHandler::display() const
+{
+ return uisession()->display();
+}
+
+CMouse& UIMouseHandler::mouse() const
+{
+ return uisession()->mouse();
+}
+
+/* Event handler for registered machine-view(s): */
+bool UIMouseHandler::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* If that object is of QWidget type: */
+ if (QWidget *pWatchedWidget = qobject_cast<QWidget*>(pWatched))
+ {
+ /* Check if that widget is in windows list: */
+ if (m_windows.values().contains(pWatchedWidget))
+ {
+#ifdef VBOX_WS_WIN
+ /* Handle window events: */
+ switch (pEvent->type())
+ {
+ case QEvent::Move:
+ {
+ /* Update mouse clipping if window was moved
+ * by Operating System desktop manager: */
+ updateMouseCursorClipping();
+ break;
+ }
+ default:
+ break;
+ }
+#endif /* VBOX_WS_WIN */
+ }
+
+ else
+
+ /* Check if that widget is of UIMachineView type: */
+ if (UIMachineView *pWatchedMachineView = qobject_cast<UIMachineView*>(pWatchedWidget))
+ {
+ /* Check if that widget is in views list: */
+ if (m_views.values().contains(pWatchedMachineView))
+ {
+ /* Handle view events: */
+ switch (pEvent->type())
+ {
+ case QEvent::FocusOut:
+ {
+ /* Release the mouse: */
+ releaseMouse();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ else
+
+ /* Check if that widget is in viewports list: */
+ if (m_viewports.values().contains(pWatchedWidget))
+ {
+ /* Get current watched widget screen id: */
+ ulong uScreenId = m_viewports.key(pWatchedWidget);
+ /* Handle viewport events: */
+ switch (pEvent->type())
+ {
+#ifdef VBOX_WS_MAC
+ case UIGrabMouseEvent::GrabMouseEvent:
+ {
+ UIGrabMouseEvent *pDeltaEvent = static_cast<UIGrabMouseEvent*>(pEvent);
+ QPoint p = QPoint(pDeltaEvent->xDelta() + m_lastMousePos.x(),
+ pDeltaEvent->yDelta() + m_lastMousePos.y());
+ if (mouseEvent(pDeltaEvent->mouseEventType(), uScreenId,
+ m_viewports[uScreenId]->mapFromGlobal(p), p,
+ pDeltaEvent->buttons(),
+ pDeltaEvent->wheelDelta(), pDeltaEvent->orientation()))
+ return true;
+ break;
+ }
+#endif /* VBOX_WS_MAC */
+ case QEvent::MouseMove:
+ {
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // Since we are handling mouse move/drag events in the same thread
+ // where we are painting guest content changes corresponding to those
+ // events, we can have input event queue overloaded with the move/drag
+ // events, so we should discard current one if there is subsequent already.
+ EventTypeSpec list[2];
+ list[0].eventClass = kEventClassMouse;
+ list[0].eventKind = kEventMouseMoved;
+ list[1].eventClass = kEventClassMouse;
+ list[1].eventKind = kEventMouseDragged;
+ if (AcquireFirstMatchingEventInQueue(GetCurrentEventQueue(), 2, list,
+ kEventQueueOptionsNone) != NULL)
+ return true;
+#endif /* VBOX_WS_MAC */
+
+ /* This event should be also processed using next 'case': */
+ }
+ RT_FALL_THRU();
+ case QEvent::MouseButtonRelease:
+ {
+ /* Get mouse-event: */
+ QMouseEvent *pOldMouseEvent = static_cast<QMouseEvent*>(pEvent);
+
+ /* Check which viewport(s) we *probably* hover: */
+ QWidgetList probablyHoveredViewports;
+ foreach (QWidget *pViewport, m_viewports)
+ {
+ QPoint posInViewport = pViewport->mapFromGlobal(pOldMouseEvent->globalPos());
+ if (pViewport->geometry().adjusted(0, 0, 1, 1).contains(posInViewport))
+ probablyHoveredViewports << pViewport;
+ }
+ /* Determine actually hovered viewport: */
+ QWidget *pHoveredWidget = probablyHoveredViewports.isEmpty() ? 0 :
+ probablyHoveredViewports.contains(pWatchedWidget) ? pWatchedWidget :
+ probablyHoveredViewports.first();
+
+ /* Check if we should propagate this event to another window: */
+ if (pHoveredWidget && pHoveredWidget != pWatchedWidget && m_viewports.values().contains(pHoveredWidget))
+ {
+ /* Prepare redirected mouse-move event: */
+ QMouseEvent *pNewMouseEvent = new QMouseEvent(pOldMouseEvent->type(),
+ pHoveredWidget->mapFromGlobal(pOldMouseEvent->globalPos()),
+ pOldMouseEvent->globalPos(),
+ pOldMouseEvent->button(),
+ pOldMouseEvent->buttons(),
+ pOldMouseEvent->modifiers());
+
+ /* Send that event to real destination: */
+ QApplication::postEvent(pHoveredWidget, pNewMouseEvent);
+
+ /* Filter out that event: */
+ return true;
+ }
+
+#ifdef VBOX_WS_X11
+ /* Make sure that we are focused after a click. Rather
+ * ugly, but works around a problem with GNOME
+ * screensaver, which sometimes removes our input focus
+ * and gives us no way to get it back. */
+ if (pEvent->type() == QEvent::MouseButtonRelease)
+ pWatchedWidget->window()->activateWindow();
+#endif /* VBOX_WS_X11 */
+ /* Check if we should activate window under cursor: */
+ if (gEDataManager->activateHoveredMachineWindow() &&
+ !uisession()->isMouseCaptured() &&
+ QApplication::activeWindow() &&
+ m_windows.values().contains(QApplication::activeWindow()) &&
+ m_windows.values().contains(pWatchedWidget->window()) &&
+ QApplication::activeWindow() != pWatchedWidget->window())
+ {
+ /* Put request for hovered window activation in 300msec: */
+ m_pHoveredWindow = pWatchedWidget->window();
+ QTimer::singleShot(300, this, SLOT(sltMaybeActivateHoveredWindow()));
+ }
+ else
+ {
+ /* Revoke request for hovered window activation: */
+ m_pHoveredWindow = 0;
+ }
+
+ /* This event should be also processed using next 'case': */
+ }
+ RT_FALL_THRU();
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ {
+ QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
+#ifdef VBOX_WS_X11
+ /* When the keyboard is captured, we also capture mouse button
+ * events, and release the keyboard and re-capture it delayed
+ * on every mouse click. When the click is inside our window
+ * area though the delay is not needed or wanted. Calling
+ * finaliseCaptureKeyboard() skips the delay if a delayed
+ * capture is in progress and has no effect if not: */
+ if (pEvent->type() == QEvent::MouseButtonPress)
+ machineLogic()->keyboardHandler()->finaliseCaptureKeyboard();
+#endif /* VBOX_WS_X11 */
+
+ /* For various mouse click related events
+ * we also reset last mouse wheel delta: */
+ if (pEvent->type() != QEvent::MouseMove)
+ m_iLastMouseWheelDelta = 0;
+
+ if (mouseEvent(pMouseEvent->type(), uScreenId,
+ pMouseEvent->pos(), pMouseEvent->globalPos(),
+ pMouseEvent->buttons(), 0, Qt::Horizontal))
+ return true;
+ break;
+ }
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ {
+ if (uisession()->isMouseSupportsTouchScreen() || uisession()->isMouseSupportsTouchPad())
+ return multiTouchEvent(static_cast<QTouchEvent*>(pEvent), uScreenId);
+ break;
+ }
+ case QEvent::Wheel:
+ {
+ QWheelEvent *pWheelEvent = static_cast<QWheelEvent*>(pEvent);
+ /* There are pointing devices which send smaller values for the delta than 120.
+ * Here we sum them up until we are greater than 120. This allows to have finer control
+ * over the speed acceleration & enables such devices to send a valid wheel event to our
+ * guest mouse device at all: */
+ int iDelta = 0;
+ const Qt::Orientation enmOrientation = qFabs(pWheelEvent->angleDelta().x())
+ > qFabs(pWheelEvent->angleDelta().y())
+ ? Qt::Horizontal
+ : Qt::Vertical;
+ m_iLastMouseWheelDelta += enmOrientation == Qt::Horizontal
+ ? pWheelEvent->angleDelta().x()
+ : pWheelEvent->angleDelta().y();
+ if (qAbs(m_iLastMouseWheelDelta) >= 120)
+ {
+ /* Rounding iDelta to the nearest multiple of 120: */
+ iDelta = m_iLastMouseWheelDelta / 120;
+ iDelta *= 120;
+ m_iLastMouseWheelDelta = m_iLastMouseWheelDelta % 120;
+ }
+ if (mouseEvent(pWheelEvent->type(),
+ uScreenId,
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ pWheelEvent->position().toPoint(),
+ pWheelEvent->globalPosition().toPoint(),
+#else
+ pWheelEvent->pos(),
+ pWheelEvent->globalPos(),
+#endif
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // Qt Cocoa is buggy. It always reports a left button pressed when the
+ // mouse wheel event occurs. A workaround is to ask the application which
+ // buttons are pressed currently:
+ QApplication::mouseButtons(),
+#else /* !VBOX_WS_MAC */
+ pWheelEvent->buttons(),
+#endif /* !VBOX_WS_MAC */
+ iDelta,
+ enmOrientation)
+ )
+ return true;
+ break;
+ }
+#ifdef VBOX_WS_MAC
+ case QEvent::Leave:
+ {
+ /* Enable mouse event compression if we leave the VM view.
+ * This is necessary for having smooth resizing of the VM/other windows: */
+ ::darwinSetMouseCoalescingEnabled(true);
+ break;
+ }
+ case QEvent::Enter:
+ {
+ /* Disable mouse event compression if we enter the VM view.
+ * So all mouse events are registered in the VM.
+ * Only do this if the keyboard/mouse is grabbed
+ * (this is when we have a valid event handler): */
+ if (machineLogic()->keyboardHandler()->isKeyboardGrabbed())
+ darwinSetMouseCoalescingEnabled(false);
+ break;
+ }
+#endif /* VBOX_WS_MAC */
+#ifdef VBOX_WS_WIN
+ case QEvent::Resize:
+ {
+ /* Update mouse clipping: */
+ updateMouseCursorClipping();
+ break;
+ }
+#endif /* VBOX_WS_WIN */
+ default:
+ break;
+ }
+ }
+ }
+ return QObject::eventFilter(pWatched, pEvent);
+}
+
+/* Try to detect if the mouse event is fake and actually generated by a touch device. */
+#ifdef VBOX_WS_WIN
+#if (WINVER < 0x0601)
+typedef enum tagINPUT_MESSAGE_DEVICE_TYPE {
+ IMDT_UNAVAILABLE = 0, // 0x0
+ IMDT_KEYBOARD = 1, // 0x1
+ IMDT_MOUSE = 2, // 0x2
+ IMDT_TOUCH = 4, // 0x4
+ IMDT_PEN = 8 // 0x8
+} INPUT_MESSAGE_DEVICE_TYPE;
+
+typedef enum tagINPUT_MESSAGE_ORIGIN_ID {
+ IMO_UNAVAILABLE = 0x00000000,
+ IMO_HARDWARE = 0x00000001,
+ IMO_INJECTED = 0x00000002,
+ IMO_SYSTEM = 0x00000004
+} INPUT_MESSAGE_ORIGIN_ID;
+
+typedef struct tagINPUT_MESSAGE_SOURCE {
+ INPUT_MESSAGE_DEVICE_TYPE deviceType;
+ INPUT_MESSAGE_ORIGIN_ID originId;
+} INPUT_MESSAGE_SOURCE;
+#endif /* WINVER < 0x0601 */
+
+#define MOUSEEVENTF_FROMTOUCH 0xFF515700
+#define MOUSEEVENTF_MASK 0xFFFFFF00
+
+typedef BOOL WINAPI FNGetCurrentInputMessageSource(INPUT_MESSAGE_SOURCE *inputMessageSource);
+typedef FNGetCurrentInputMessageSource *PFNGetCurrentInputMessageSource;
+
+static bool mouseIsTouchSource(int iEventType, Qt::MouseButtons mouseButtons)
+{
+ NOREF(mouseButtons);
+
+ static PFNGetCurrentInputMessageSource pfnGetCurrentInputMessageSource = (PFNGetCurrentInputMessageSource)-1;
+ if (pfnGetCurrentInputMessageSource == (PFNGetCurrentInputMessageSource)-1)
+ {
+ HMODULE hUser = GetModuleHandle(L"user32.dll");
+ if (hUser)
+ pfnGetCurrentInputMessageSource =
+ (PFNGetCurrentInputMessageSource)GetProcAddress(hUser, "GetCurrentInputMessageSource");
+ }
+
+ int deviceType = -1;
+ if (pfnGetCurrentInputMessageSource)
+ {
+ INPUT_MESSAGE_SOURCE inputMessageSource;
+ BOOL fSuccess = pfnGetCurrentInputMessageSource(&inputMessageSource);
+ deviceType = fSuccess? inputMessageSource.deviceType: -2;
+ }
+ else
+ {
+ if ( iEventType == QEvent::MouseButtonPress
+ || iEventType == QEvent::MouseButtonRelease
+ || iEventType == QEvent::MouseMove)
+ deviceType = (GetMessageExtraInfo() & MOUSEEVENTF_MASK) == MOUSEEVENTF_FROMTOUCH? IMDT_TOUCH: -3;
+ }
+
+ LogRelFlow(("mouseIsTouchSource: deviceType %d\n", deviceType));
+ return deviceType == IMDT_TOUCH || deviceType == IMDT_PEN;
+}
+#else
+/* Apparently VBOX_WS_MAC does not generate fake mouse events.
+ * Other platforms, which have no known method to detect fake events are handled here too.
+ */
+static bool mouseIsTouchSource(int iEventType, Qt::MouseButtons mouseButtons)
+{
+ NOREF(iEventType);
+ NOREF(mouseButtons);
+ return false;
+}
+#endif
+
+/* Separate function to handle most of existing mouse-events: */
+bool UIMouseHandler::mouseEvent(int iEventType, ulong uScreenId,
+ const QPoint &relativePos, const QPoint &globalPos,
+ Qt::MouseButtons mouseButtons,
+ int wheelDelta, Qt::Orientation wheelDirection)
+{
+ /* Ignore fake mouse events. */
+ if ( (uisession()->isMouseSupportsTouchScreen() || uisession()->isMouseSupportsTouchPad())
+ && mouseIsTouchSource(iEventType, mouseButtons))
+ return true;
+
+ /* Check if machine is still running: */
+ if (!uisession()->isRunning())
+ return true;
+
+ /* Check if such view & viewport are registered: */
+ if (!m_views.contains(uScreenId) || !m_viewports.contains(uScreenId))
+ return true;
+
+ int iMouseButtonsState = 0;
+ if (mouseButtons & Qt::LeftButton)
+ iMouseButtonsState |= KMouseButtonState_LeftButton;
+ if (mouseButtons & Qt::RightButton)
+ iMouseButtonsState |= KMouseButtonState_RightButton;
+ if (mouseButtons & Qt::MiddleButton)
+ iMouseButtonsState |= KMouseButtonState_MiddleButton;
+ if (mouseButtons & Qt::XButton1)
+ iMouseButtonsState |= KMouseButtonState_XButton1;
+ if (mouseButtons & Qt::XButton2)
+ iMouseButtonsState |= KMouseButtonState_XButton2;
+
+#ifdef VBOX_WS_MAC
+ /* Simulate the right click on host-key + left-mouse-button: */
+ if (machineLogic()->keyboardHandler()->isHostKeyPressed() &&
+ machineLogic()->keyboardHandler()->isHostKeyAlone() &&
+ iMouseButtonsState == KMouseButtonState_LeftButton)
+ iMouseButtonsState = KMouseButtonState_RightButton;
+#endif /* VBOX_WS_MAC */
+
+ int iWheelVertical = 0;
+ int iWheelHorizontal = 0;
+ if (wheelDirection == Qt::Vertical)
+ {
+ /* The absolute value of wheel delta is 120 units per every wheel move;
+ * positive deltas correspond to counterclockwise rotations (usually up),
+ * negative deltas correspond to clockwise (usually down). */
+ iWheelVertical = - (wheelDelta / 120);
+ }
+ else if (wheelDirection == Qt::Horizontal)
+ iWheelHorizontal = wheelDelta / 120;
+
+ if (uisession()->isMouseCaptured())
+ {
+#ifdef VBOX_WS_WIN
+ /* Send pending WM_PAINT events: */
+ ::UpdateWindow((HWND)m_viewports[uScreenId]->winId());
+#endif
+
+#ifdef VBOX_WS_WIN
+ // WORKAROUND:
+ // There are situations at least on Windows host that we are receiving
+ // previously posted (but not yet handled) mouse event right after we
+ // have manually teleported mouse cursor to simulate infinite movement,
+ // this makes cursor blink for a large amount of space, so we should
+ // ignore such blinks .. well, at least once.
+ const QPoint shiftingSpace = globalPos - m_lastMousePos;
+ if (m_fCursorPositionReseted && shiftingSpace.manhattanLength() >= 10)
+ {
+ m_fCursorPositionReseted = false;
+ return true;
+ }
+#endif
+
+ /* Pass event to the guest: */
+ mouse().PutMouseEvent(globalPos.x() - m_lastMousePos.x(),
+ globalPos.y() - m_lastMousePos.y(),
+ iWheelVertical, iWheelHorizontal, iMouseButtonsState);
+
+#ifdef VBOX_WS_WIN
+ /* Compose viewport-rectangle in local coordinates: */
+ QRect viewportRectangle = m_mouseCursorClippingRect;
+ QPoint viewportRectangleGlobalPos = m_views[uScreenId]->mapToGlobal(m_viewports[uScreenId]->pos());
+ viewportRectangle.translate(-viewportRectangleGlobalPos);
+
+ /* Compose boundaries: */
+ const int iX1 = viewportRectangle.left() + 1;
+ const int iY1 = viewportRectangle.top() + 1;
+ const int iX2 = viewportRectangle.right() - 1;
+ const int iY2 = viewportRectangle.bottom() - 1;
+
+ /* Simulate infinite movement: */
+ QPoint p = relativePos;
+ if (relativePos.x() <= iX1)
+ p.setX(iX2 - 1);
+ else if (relativePos.x() >= iX2)
+ p.setX(iX1 + 1);
+ if (relativePos.y() <= iY1)
+ p.setY(iY2 - 1);
+ else if (relativePos.y() >= iY2)
+ p.setY(iY1 + 1);
+ if (p != relativePos)
+ {
+ // WORKAROUND:
+ // Underlying QCursor::setPos call requires coordinates, rescaled according to primary screen.
+ // For that we have to map logical coordinates to relative origin (to make logical=>physical conversion).
+ // Besides that we have to make sure m_lastMousePos still uses logical coordinates afterwards.
+ const double dDprPrimary = UIDesktopWidgetWatchdog::devicePixelRatio(UIDesktopWidgetWatchdog::primaryScreenNumber());
+ const double dDprCurrent = UIDesktopWidgetWatchdog::devicePixelRatio(m_windows.value(m_iMouseCaptureViewIndex));
+ const QRect screenGeometry = gpDesktop->screenGeometry(m_windows.value(m_iMouseCaptureViewIndex));
+ QPoint requiredMousePos = (m_viewports[uScreenId]->mapToGlobal(p) - screenGeometry.topLeft()) * dDprCurrent + screenGeometry.topLeft();
+ QCursor::setPos(requiredMousePos / dDprPrimary);
+ m_lastMousePos = requiredMousePos / dDprCurrent;
+ m_fCursorPositionReseted = true;
+ }
+ else
+ {
+ m_lastMousePos = globalPos;
+ m_fCursorPositionReseted = false;
+ }
+#else /* !VBOX_WS_WIN */
+ /* Compose boundaries: */
+ const int iX1 = m_capturedScreenGeo.left() + 1;
+ const int iY1 = m_capturedScreenGeo.top() + 1;
+ const int iX2 = m_capturedScreenGeo.right() - 1;
+ const int iY2 = m_capturedScreenGeo.bottom() - 1;
+
+ /* Simulate infinite movement: */
+ QPoint p = globalPos;
+ if (globalPos.x() <= iX1)
+ p.setX(iX2 - 1);
+ else if (globalPos.x() >= iX2)
+ p.setX(iX1 + 1);
+ if (globalPos.y() <= iY1)
+ p.setY(iY2 - 1);
+ else if (globalPos.y() >= iY2)
+ p.setY(iY1 + 1);
+
+ if (p != globalPos)
+ {
+ m_lastMousePos = p;
+ /* No need for cursor updating on the Mac, there is no one. */
+# ifndef VBOX_WS_MAC
+ QCursor::setPos(m_lastMousePos);
+# endif /* VBOX_WS_MAC */
+ }
+ else
+ m_lastMousePos = globalPos;
+#endif /* !VBOX_WS_WIN */
+ return true; /* stop further event handling */
+ }
+ else /* !uisession()->isMouseCaptured() */
+ {
+ if (uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated())
+ {
+ int iCw = m_views[uScreenId]->contentsWidth(), iCh = m_views[uScreenId]->contentsHeight();
+ int iVw = m_views[uScreenId]->visibleWidth(), iVh = m_views[uScreenId]->visibleHeight();
+
+ /* Try to automatically scroll the guest canvas if the
+ * mouse goes outside its visible part: */
+ int iDx = 0;
+ if (relativePos.x() > iVw) iDx = relativePos.x() - iVw;
+ else if (relativePos.x() < 0) iDx = relativePos.x();
+ int iDy = 0;
+ if (relativePos.y() > iVh) iDy = relativePos.y() - iVh;
+ else if (relativePos.y() < 0) iDy = relativePos.y();
+ if (iDx != 0 || iDy != 0) m_views[uScreenId]->scrollBy(iDx, iDy);
+
+ /* Get mouse-pointer location: */
+ QPoint cpnt = m_views[uScreenId]->viewportToContents(relativePos);
+
+ /* Take the scale-factor(s) into account: */
+ const UIFrameBuffer *pFrameBuffer = m_views[uScreenId]->frameBuffer();
+ if (pFrameBuffer)
+ {
+ const QSize scaledSize = pFrameBuffer->scaledSize();
+ if (scaledSize.isValid())
+ {
+ const double xScaleFactor = (double)scaledSize.width() / pFrameBuffer->width();
+ const double yScaleFactor = (double)scaledSize.height() / pFrameBuffer->height();
+ cpnt.setX((int)(cpnt.x() / xScaleFactor));
+ cpnt.setY((int)(cpnt.y() / yScaleFactor));
+ }
+ }
+
+ /* Take the device-pixel-ratio into account: */
+ const double dDevicePixelRatioFormal = pFrameBuffer->devicePixelRatio();
+ const double dDevicePixelRatioActual = pFrameBuffer->devicePixelRatioActual();
+ cpnt.setX(cpnt.x() * dDevicePixelRatioFormal);
+ cpnt.setY(cpnt.y() * dDevicePixelRatioFormal);
+ if (!pFrameBuffer->useUnscaledHiDPIOutput())
+ {
+ cpnt.setX(cpnt.x() / dDevicePixelRatioActual);
+ cpnt.setY(cpnt.y() / dDevicePixelRatioActual);
+ }
+
+#ifdef VBOX_WITH_DRAG_AND_DROP
+# ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ QPointer<UIMachineView> pView = m_views[uScreenId];
+ bool fHandleDnDPending = RT_BOOL(mouseButtons.testFlag(Qt::LeftButton));
+
+ /* Mouse pointer outside VM window? */
+ if ( cpnt.x() < 0
+ || cpnt.x() > iCw - 1
+ || cpnt.y() < 0
+ || cpnt.y() > iCh - 1)
+ {
+ if (fHandleDnDPending)
+ {
+ LogRel2(("DnD: Drag and drop operation from guest to host started\n"));
+
+ int rc = pView->dragCheckPending();
+ if (RT_SUCCESS(rc))
+ {
+ pView->dragStart();
+ return true; /* Bail out -- we're done here. */
+ }
+ }
+ }
+ else /* Inside VM window? */
+ {
+ if (fHandleDnDPending)
+ pView->dragStop();
+ }
+# endif
+#endif /* VBOX_WITH_DRAG_AND_DROP */
+
+ /* Bound coordinates: */
+ if (cpnt.x() < 0) cpnt.setX(0);
+ else if (cpnt.x() > iCw - 1) cpnt.setX(iCw - 1);
+ if (cpnt.y() < 0) cpnt.setY(0);
+ else if (cpnt.y() > iCh - 1) cpnt.setY(iCh - 1);
+
+ /* Determine shifting: */
+ LONG xShift = 0, yShift = 0;
+ ULONG dummy;
+ KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
+ display().GetScreenResolution(uScreenId, dummy, dummy, dummy, xShift, yShift, monitorStatus);
+ /* Set shifting: */
+ cpnt.setX(cpnt.x() + xShift);
+ cpnt.setY(cpnt.y() + yShift);
+
+ /* Post absolute mouse-event into guest: */
+ mouse().PutMouseEventAbsolute(cpnt.x() + 1, cpnt.y() + 1, iWheelVertical, iWheelHorizontal, iMouseButtonsState);
+ return true;
+ }
+ else
+ {
+ if (m_views[uScreenId]->hasFocus() && (iEventType == QEvent::MouseButtonRelease && mouseButtons == Qt::NoButton))
+ {
+ if (uisession()->isPaused())
+ UINotificationMessage::remindAboutPausedVMInput();
+ else if (uisession()->isRunning())
+ {
+ /* Temporarily disable auto capture that will take place after this dialog is dismissed because
+ * the capture state is to be defined by the dialog result itself: */
+ uisession()->setAutoCaptureDisabled(true);
+ bool fIsAutoConfirmed = false;
+ bool ok = msgCenter().confirmInputCapture(fIsAutoConfirmed);
+ if (fIsAutoConfirmed)
+ uisession()->setAutoCaptureDisabled(false);
+ /* Otherwise, the disable flag will be reset in the next console view's focus in event (since
+ * may happen asynchronously on some platforms, after we return from this code): */
+ if (ok)
+ {
+#ifdef VBOX_WS_X11
+ /* Make sure that pending FocusOut events from the previous message box are handled,
+ * otherwise the mouse is immediately ungrabbed again: */
+ qApp->processEvents();
+#endif /* VBOX_WS_X11 */
+ machineLogic()->keyboardHandler()->captureKeyboard(uScreenId);
+ const MouseCapturePolicy mcp = gEDataManager->mouseCapturePolicy(uiCommon().managedVMUuid());
+ if (mcp == MouseCapturePolicy_Default)
+ captureMouse(uScreenId);
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool UIMouseHandler::multiTouchEvent(QTouchEvent *pTouchEvent, ulong uScreenId)
+{
+ /* Eat if machine isn't running: */
+ if (!uisession()->isRunning())
+ return true;
+
+ /* Eat if such view & viewport aren't registered: */
+ if (!m_views.contains(uScreenId) || !m_viewports.contains(uScreenId))
+ return true;
+
+ QVector<LONG64> contacts(pTouchEvent->touchPoints().size());
+
+ LONG xShift = 0, yShift = 0;
+
+#ifdef VBOX_IS_QT6_OR_LATER /* QTouchDevice was consumed by QInputDevice in 6.0 */
+ bool fTouchScreen = (pTouchEvent->device()->type() == QInputDevice::DeviceType::TouchScreen);
+#else
+ bool fTouchScreen = (pTouchEvent->device()->type() == QTouchDevice::TouchScreen);
+#endif
+ /* Compatibility with previous behavior. If there is no touchpad configured
+ * then treat all multitouch events as touchscreen ones: */
+ fTouchScreen |= !uisession()->isMouseSupportsTouchPad();
+
+ if (fTouchScreen)
+ {
+ ULONG dummy;
+ KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
+ display().GetScreenResolution(uScreenId, dummy, dummy, dummy, xShift, yShift, monitorStatus);
+ }
+
+ /* Pass all multi-touch events into guest: */
+ int iTouchPointIndex = 0;
+ foreach (const QTouchEvent::TouchPoint &touchPoint, pTouchEvent->touchPoints())
+ {
+ /* Get touch-point state: */
+ LONG iTouchPointState = KTouchContactState_None;
+ switch (touchPoint.state())
+ {
+ case Qt::TouchPointPressed:
+ case Qt::TouchPointMoved:
+ case Qt::TouchPointStationary:
+ iTouchPointState = KTouchContactState_InContact;
+ if (fTouchScreen)
+ iTouchPointState |= KTouchContactState_InRange;
+ break;
+ default:
+ break;
+ }
+
+ if (fTouchScreen)
+ {
+ /* Get absolute touch-point origin: */
+ QPoint currentTouchPoint = touchPoint.pos().toPoint();
+
+ /* Pass absolute touch-point data: */
+ LogRelFlow(("UIMouseHandler::multiTouchEvent: TouchScreen, Origin: %dx%d, Id: %d, State: %d\n",
+ currentTouchPoint.x(), currentTouchPoint.y(), touchPoint.id(), iTouchPointState));
+
+ contacts[iTouchPointIndex] = RT_MAKE_U64_FROM_U16((uint16_t)currentTouchPoint.x() + 1 + xShift,
+ (uint16_t)currentTouchPoint.y() + 1 + yShift,
+ RT_MAKE_U16(touchPoint.id(), iTouchPointState),
+ 0);
+ } else {
+ /* Get relative touch-point normalized position: */
+ QPointF rawTouchPoint = touchPoint.normalizedPos();
+
+ /* Pass relative touch-point data as Normalized Integer: */
+ uint16_t xNorm = rawTouchPoint.x() * 0xffff;
+ uint16_t yNorm = rawTouchPoint.y() * 0xffff;
+ LogRelFlow(("UIMouseHandler::multiTouchEvent: TouchPad, Normalized Position: %ux%u, Id: %d, State: %d\n",
+ xNorm, yNorm, touchPoint.id(), iTouchPointState));
+
+ contacts[iTouchPointIndex] = RT_MAKE_U64_FROM_U16(xNorm, yNorm,
+ RT_MAKE_U16(touchPoint.id(), iTouchPointState),
+ 0);
+ }
+
+ LogRelFlow(("UIMouseHandler::multiTouchEvent: %RX64\n", contacts[iTouchPointIndex]));
+
+ ++iTouchPointIndex;
+ }
+
+ mouse().PutEventMultiTouch(pTouchEvent->touchPoints().size(),
+ contacts,
+ fTouchScreen,
+ (ULONG)RTTimeMilliTS());
+
+ /* Eat by default? */
+ return true;
+}
+
+#ifdef VBOX_WS_WIN
+/* This method is actually required only because under win-host
+ * we do not really grab the mouse in case of capturing it: */
+void UIMouseHandler::updateMouseCursorClipping()
+{
+ /* Check if such view && viewport are registered: */
+ if (!m_views.contains(m_iMouseCaptureViewIndex) || !m_viewports.contains(m_iMouseCaptureViewIndex))
+ return;
+
+ if (uisession()->isMouseCaptured())
+ {
+ /* Get full-viewport-rectangle in global coordinates: */
+ QRect viewportRectangle = m_viewports[m_iMouseCaptureViewIndex]->visibleRegion().boundingRect();
+ const QPoint viewportRectangleGlobalPos = m_views[m_iMouseCaptureViewIndex]->mapToGlobal(m_viewports[m_iMouseCaptureViewIndex]->pos());
+ viewportRectangle.translate(viewportRectangleGlobalPos);
+
+ /* Trim full-viewport-rectangle by available geometry: */
+ viewportRectangle = viewportRectangle.intersected(gpDesktop->availableGeometry(machineLogic()->machineWindows()[m_iMouseCaptureViewIndex]));
+
+ /* Trim partial-viewport-rectangle by top-most windows: */
+ QRegion viewportRegion = QRegion(viewportRectangle) - NativeWindowSubsystem::areaCoveredByTopMostWindows();
+ /* Check if partial-viewport-region consists of 1 rectangle: */
+ if (viewportRegion.rectCount() > 1)
+ {
+ /* Choose the largest rectangle: */
+ QRect largestRect;
+ foreach (const QRect &rect, viewportRegion.rects())
+ largestRect = largestRect.width() * largestRect.height() < rect.width() * rect.height() ? rect : largestRect;
+ /* Assign the partial-viewport-region to the largest rect: */
+ viewportRegion = largestRect;
+ }
+ /* Assign the partial-viewport-rectangle to the partial-viewport-region: */
+ viewportRectangle = viewportRegion.boundingRect();
+
+ /* Assign the visible-viewport-rectangle to the partial-viewport-rectangle: */
+ m_mouseCursorClippingRect = viewportRectangle;
+
+ /* Prepare clipping area: */
+ // WORKAROUND:
+ // Underlying ClipCursor call requires physical coordinates, not logical upscaled Qt stuff.
+ // But we will have to map to relative origin (to make logical=>physical conversion) first.
+ const double dDpr = UIDesktopWidgetWatchdog::devicePixelRatio(m_windows.value(m_iMouseCaptureViewIndex));
+ const QRect screenGeometry = gpDesktop->screenGeometry(m_windows.value(m_iMouseCaptureViewIndex));
+ viewportRectangle.moveTo((viewportRectangle.topLeft() - screenGeometry.topLeft()) * dDpr + screenGeometry.topLeft());
+ viewportRectangle.setSize(viewportRectangle.size() * dDpr);
+ RECT rect = { viewportRectangle.left() + 1, viewportRectangle.top() + 1, viewportRectangle.right(), viewportRectangle.bottom() };
+ ::ClipCursor(&rect);
+ }
+ else
+ {
+ ::ClipCursor(NULL);
+ }
+}
+#endif /* VBOX_WS_WIN */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.h
new file mode 100644
index 00000000..a63a0884
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.h
@@ -0,0 +1,164 @@
+/* $Id: UIMouseHandler.h $ */
+/** @file
+ * VBox Qt GUI - UIMouseHandler class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIMouseHandler_h
+#define FEQT_INCLUDED_SRC_runtime_UIMouseHandler_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QObject>
+#include <QPoint>
+#include <QPointer>
+#include <QRect>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+
+/* Forward declarations: */
+class QTouchEvent;
+class QWidget;
+class UISession;
+class UIMachineLogic;
+class UIMachineWindow;
+class UIMachineView;
+class CDisplay;
+class CMouse;
+
+
+/* Delegate to control VM mouse functionality: */
+class UIMouseHandler : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about state-change. */
+ void sigStateChange(int iState);
+
+public:
+
+ /* Factory functions to create/destroy mouse-handler: */
+ static UIMouseHandler* create(UIMachineLogic *pMachineLogic, UIVisualStateType visualStateType);
+ static void destroy(UIMouseHandler *pMouseHandler);
+
+ /* Prepare/cleanup listener for particular machine-window: */
+ void prepareListener(ulong uIndex, UIMachineWindow *pMachineWindow);
+ void cleanupListener(ulong uIndex);
+
+ /* Commands to capture/release mouse: */
+ void captureMouse(ulong uScreenId);
+ void releaseMouse();
+
+ /* Setter for mouse-integration feature: */
+ void setMouseIntegrationEnabled(bool fEnabled);
+
+ /* Current mouse state: */
+ int state() const;
+
+ /** Qt5: Performs pre-processing of all the native events. */
+ bool nativeEventFilter(void *pMessage, ulong uScreenId);
+
+protected slots:
+
+ /* Machine state-change handler: */
+ virtual void sltMachineStateChanged();
+
+ /* Mouse capability-change handler: */
+ virtual void sltMouseCapabilityChanged();
+
+ /* Mouse pointer-shape-change handler: */
+ virtual void sltMousePointerShapeChanged();
+
+ /** Activate hovered window if any. */
+ void sltMaybeActivateHoveredWindow();
+
+protected:
+
+ /* Mouse-handler constructor/destructor: */
+ UIMouseHandler(UIMachineLogic *pMachineLogic);
+ virtual ~UIMouseHandler();
+
+ /* Getters: */
+ UIMachineLogic* machineLogic() const;
+ UISession* uisession() const;
+
+ /** Returns the console's display reference. */
+ CDisplay& display() const;
+ /** Returns the console's mouse reference. */
+ CMouse& mouse() const;
+
+ /* Event handler for registered machine-view(s): */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+
+ /* Separate function to handle most of existing mouse-events: */
+ bool mouseEvent(int iEventType, ulong uScreenId,
+ const QPoint &relativePos, const QPoint &globalPos,
+ Qt::MouseButtons mouseButtons,
+ int wheelDelta, Qt::Orientation wheelDirection);
+
+ /* Separate function to handle incoming multi-touch events: */
+ bool multiTouchEvent(QTouchEvent *pTouchEvent, ulong uScreenId);
+
+#ifdef VBOX_WS_WIN
+ /* This method is actually required only because under win-host
+ * we do not really grab the mouse in case of capturing it: */
+ void updateMouseCursorClipping();
+ QRect m_mouseCursorClippingRect;
+#endif /* VBOX_WS_WIN */
+
+ /* Machine logic parent: */
+ UIMachineLogic *m_pMachineLogic;
+
+ /* Registered machine-windows(s): */
+ QMap<ulong, QWidget*> m_windows;
+ /* Registered machine-view(s): */
+ QMap<ulong, UIMachineView*> m_views;
+ /* Registered machine-view-viewport(s): */
+ QMap<ulong, QWidget*> m_viewports;
+
+ /** Hovered window to be activated. */
+ QPointer<QWidget> m_pHoveredWindow;
+
+ /* Other mouse variables: */
+ QPoint m_lastMousePos;
+ QPoint m_capturedMousePos;
+ QRect m_capturedScreenGeo;
+ int m_iLastMouseWheelDelta;
+ int m_iMouseCaptureViewIndex;
+
+#ifdef VBOX_WS_WIN
+ /** Holds whether cursor position was just
+ * reseted to simulate infinite mouse moving. */
+ bool m_fCursorPositionReseted;
+#endif
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIMouseHandler_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.cpp
new file mode 100644
index 00000000..d7a5909a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.cpp
@@ -0,0 +1,326 @@
+/* $Id: UIMultiScreenLayout.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMultiScreenLayout class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QMenu>
+
+/* GUI includes: */
+#include "UIDefs.h"
+#include "UIMultiScreenLayout.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogic.h"
+#include "UIFrameBuffer.h"
+#include "UISession.h"
+#include "UIMessageCenter.h"
+#include "UIExtraDataManager.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CSession.h"
+#include "CConsole.h"
+#include "CMachine.h"
+#include "CDisplay.h"
+#include "CGraphicsAdapter.h"
+
+
+UIMultiScreenLayout::UIMultiScreenLayout(UIMachineLogic *pMachineLogic)
+ : m_pMachineLogic(pMachineLogic)
+ , m_cGuestScreens(m_pMachineLogic->machine().GetGraphicsAdapter().GetMonitorCount())
+ , m_cHostScreens(0)
+{
+ /* Calculate host/guest screen count: */
+ calculateHostMonitorCount();
+ calculateGuestScreenCount();
+
+ /* Prpeare connections: */
+ prepareConnections();
+}
+
+void UIMultiScreenLayout::update()
+{
+ LogRelFlow(("UIMultiScreenLayout::update: Started...\n"));
+
+ /* Clear screen-map initially: */
+ m_screenMap.clear();
+
+ /* Make a pool of available host screens: */
+ QList<int> availableScreens;
+ for (int i = 0; i < m_cHostScreens; ++i)
+ availableScreens << i;
+
+ /* Load all combinations stored in the settings file.
+ * We have to make sure they are valid, which means there have to be unique combinations
+ * and all guests screens need there own host screen. */
+ bool fShouldWeAutoMountGuestScreens = gEDataManager->autoMountGuestScreensEnabled(uiCommon().managedVMUuid());
+ LogRel(("GUI: UIMultiScreenLayout::update: GUI/AutomountGuestScreens is %s\n", fShouldWeAutoMountGuestScreens ? "enabled" : "disabled"));
+ foreach (int iGuestScreen, m_guestScreens)
+ {
+ /* Initialize variables: */
+ bool fValid = false;
+ int iHostScreen = -1;
+
+ if (!fValid)
+ {
+ /* If the user ever selected a combination in the view menu, we have the following entry: */
+ iHostScreen = gEDataManager->hostScreenForPassedGuestScreen(iGuestScreen, uiCommon().managedVMUuid());
+ /* Revalidate: */
+ fValid = iHostScreen >= 0 && iHostScreen < m_cHostScreens /* In the host screen bounds? */
+ && m_screenMap.key(iHostScreen, -1) == -1; /* Not taken already? */
+ }
+
+ if (!fValid)
+ {
+ /* Check the position of the guest window in normal mode.
+ * This makes sure that on first use fullscreen/seamless window opens on the same host-screen as the normal window was before.
+ * This even works with multi-screen. The user just have to move all the normal windows to the target host-screens
+ * and they will magically open there in fullscreen/seamless also. */
+ QRect geo = gEDataManager->machineWindowGeometry(UIVisualStateType_Normal, iGuestScreen, uiCommon().managedVMUuid());
+ /* If geometry is valid: */
+ if (!geo.isNull())
+ {
+ /* Get top-left corner position: */
+ QPoint topLeftPosition(geo.topLeft());
+ /* Check which host-screen the position belongs to: */
+ iHostScreen = UIDesktopWidgetWatchdog::screenNumber(topLeftPosition);
+ /* Revalidate: */
+ fValid = iHostScreen >= 0 && iHostScreen < m_cHostScreens /* In the host screen bounds? */
+ && m_screenMap.key(iHostScreen, -1) == -1; /* Not taken already? */
+ }
+ }
+
+ if (!fValid)
+ {
+ /* If still not valid, pick the next one
+ * if there is still available host screen: */
+ if (!availableScreens.isEmpty())
+ {
+ iHostScreen = availableScreens.first();
+ fValid = true;
+ }
+ }
+
+ if (fValid)
+ {
+ /* Register host screen for the guest screen: */
+ m_screenMap.insert(iGuestScreen, iHostScreen);
+ /* Remove it from the list of available host screens: */
+ availableScreens.removeOne(iHostScreen);
+ }
+ /* Do we have opinion about what to do with excessive guest-screen? */
+ else if (fShouldWeAutoMountGuestScreens)
+ {
+ /* Then we have to disable excessive guest-screen: */
+ LogRel(("GUI: UIMultiScreenLayout::update: Disabling excessive guest-screen %d\n", iGuestScreen));
+ m_pMachineLogic->uisession()->setScreenVisibleHostDesires(iGuestScreen, false);
+ m_pMachineLogic->display().SetVideoModeHint(iGuestScreen, false, false, 0, 0, 0, 0, 0, true);
+ }
+ }
+
+ /* Are we still have available host-screens
+ * and have opinion about what to do with disabled guest-screens? */
+ if (!availableScreens.isEmpty() && fShouldWeAutoMountGuestScreens)
+ {
+ /* How many excessive host-screens do we have? */
+ int cExcessiveHostScreens = availableScreens.size();
+ /* How many disabled guest-screens do we have? */
+ int cDisabledGuestScreens = m_disabledGuestScreens.size();
+ /* We have to try to enable disabled guest-screens if any: */
+ int cGuestScreensToEnable = qMin(cExcessiveHostScreens, cDisabledGuestScreens);
+ UISession *pSession = m_pMachineLogic->uisession();
+ for (int iGuestScreenIndex = 0; iGuestScreenIndex < cGuestScreensToEnable; ++iGuestScreenIndex)
+ {
+ /* Defaults: */
+ ULONG uWidth = 800;
+ ULONG uHeight = 600;
+ /* Try to get previous guest-screen arguments: */
+ int iGuestScreen = m_disabledGuestScreens[iGuestScreenIndex];
+ if (UIFrameBuffer *pFrameBuffer = pSession->frameBuffer(iGuestScreen))
+ {
+ if (pFrameBuffer->width() > 0)
+ uWidth = pFrameBuffer->width();
+ if (pFrameBuffer->height() > 0)
+ uHeight = pFrameBuffer->height();
+ }
+ /* Re-enable guest-screen with proper resolution: */
+ LogRel(("GUI: UIMultiScreenLayout::update: Enabling guest-screen %d with following resolution: %dx%d\n",
+ iGuestScreen, uWidth, uHeight));
+ m_pMachineLogic->uisession()->setScreenVisibleHostDesires(iGuestScreen, true);
+ m_pMachineLogic->display().SetVideoModeHint(iGuestScreen, true, false, 0, 0, uWidth, uHeight, 32, true);
+ }
+ }
+
+ /* Make sure action-pool knows whether multi-screen layout has host-screen for guest-screen: */
+ m_pMachineLogic->actionPool()->toRuntime()->setHostScreenForGuestScreenMap(m_screenMap);
+
+ LogRelFlow(("UIMultiScreenLayout::update: Finished!\n"));
+}
+
+void UIMultiScreenLayout::rebuild()
+{
+ LogRelFlow(("UIMultiScreenLayout::rebuild: Started...\n"));
+
+ /* Recalculate host/guest screen count: */
+ calculateHostMonitorCount();
+ calculateGuestScreenCount();
+ /* Update layout: */
+ update();
+
+ LogRelFlow(("UIMultiScreenLayout::rebuild: Finished!\n"));
+}
+
+int UIMultiScreenLayout::hostScreenCount() const
+{
+ return m_cHostScreens;
+}
+
+int UIMultiScreenLayout::guestScreenCount() const
+{
+ return m_guestScreens.size();
+}
+
+int UIMultiScreenLayout::hostScreenForGuestScreen(int iScreenId) const
+{
+ return m_screenMap.value(iScreenId, 0);
+}
+
+bool UIMultiScreenLayout::hasHostScreenForGuestScreen(int iScreenId) const
+{
+ return m_screenMap.contains(iScreenId);
+}
+
+quint64 UIMultiScreenLayout::memoryRequirements() const
+{
+ return memoryRequirements(m_screenMap);
+}
+
+void UIMultiScreenLayout::sltHandleScreenLayoutChange(int iRequestedGuestScreen, int iRequestedHostScreen)
+{
+ /* Search for the virtual screen which is currently displayed on the
+ * requested host screen. When there is one found, we swap both. */
+ QMap<int,int> tmpMap(m_screenMap);
+ int iCurrentGuestScreen = tmpMap.key(iRequestedHostScreen, -1);
+ if (iCurrentGuestScreen != -1 && tmpMap.contains(iRequestedGuestScreen))
+ tmpMap.insert(iCurrentGuestScreen, tmpMap.value(iRequestedGuestScreen));
+ else
+ tmpMap.remove(iCurrentGuestScreen);
+ tmpMap.insert(iRequestedGuestScreen, iRequestedHostScreen);
+
+ /* Check the memory requirements first: */
+ bool fSuccess = true;
+ if (m_pMachineLogic->uisession()->isGuestSupportsGraphics())
+ {
+ quint64 availBits = m_pMachineLogic->machine().GetGraphicsAdapter().GetVRAMSize() * _1M * 8;
+ quint64 usedBits = memoryRequirements(tmpMap);
+ fSuccess = availBits >= usedBits;
+ if (!fSuccess)
+ {
+ /* We have too little video memory for the new layout, so say it to the user and revert all the changes: */
+ if (m_pMachineLogic->visualStateType() == UIVisualStateType_Seamless)
+ msgCenter().cannotSwitchScreenInSeamless((((usedBits + 7) / 8 + _1M - 1) / _1M) * _1M);
+ else
+ fSuccess = msgCenter().cannotSwitchScreenInFullscreen((((usedBits + 7) / 8 + _1M - 1) / _1M) * _1M);
+ }
+ }
+ /* Make sure memory requirements matched: */
+ if (!fSuccess)
+ return;
+
+ /* Swap the maps: */
+ m_screenMap = tmpMap;
+
+ /* Make sure action-pool knows whether multi-screen layout has host-screen for guest-screen: */
+ m_pMachineLogic->actionPool()->toRuntime()->setHostScreenForGuestScreenMap(m_screenMap);
+
+ /* Save guest-to-host mapping: */
+ saveScreenMapping();
+
+ /* Notifies about layout change: */
+ emit sigScreenLayoutChange();
+}
+
+void UIMultiScreenLayout::calculateHostMonitorCount()
+{
+ m_cHostScreens = UIDesktopWidgetWatchdog::screenCount();
+}
+
+void UIMultiScreenLayout::calculateGuestScreenCount()
+{
+ /* Enumerate all the guest screens: */
+ m_guestScreens.clear();
+ m_disabledGuestScreens.clear();
+ for (uint iGuestScreen = 0; iGuestScreen < m_cGuestScreens; ++iGuestScreen)
+ if (m_pMachineLogic->uisession()->isScreenVisible(iGuestScreen))
+ m_guestScreens << iGuestScreen;
+ else
+ m_disabledGuestScreens << iGuestScreen;
+}
+
+void UIMultiScreenLayout::prepareConnections()
+{
+ /* Connect action-pool: */
+ connect(m_pMachineLogic->actionPool()->toRuntime(), &UIActionPoolRuntime::sigNotifyAboutTriggeringViewScreenRemap,
+ this, &UIMultiScreenLayout::sltHandleScreenLayoutChange);
+}
+
+void UIMultiScreenLayout::saveScreenMapping()
+{
+ foreach (const int &iGuestScreen, m_guestScreens)
+ {
+ const int iHostScreen = m_screenMap.value(iGuestScreen, -1);
+ gEDataManager->setHostScreenForPassedGuestScreen(iGuestScreen, iHostScreen, uiCommon().managedVMUuid());
+ }
+}
+
+quint64 UIMultiScreenLayout::memoryRequirements(const QMap<int, int> &screenLayout) const
+{
+ ULONG width = 0;
+ ULONG height = 0;
+ ULONG guestBpp = 0;
+ LONG xOrigin = 0;
+ LONG yOrigin = 0;
+ quint64 usedBits = 0;
+ foreach (int iGuestScreen, m_guestScreens)
+ {
+ QRect screen;
+ if (m_pMachineLogic->visualStateType() == UIVisualStateType_Seamless)
+ screen = gpDesktop->availableGeometry(screenLayout.value(iGuestScreen, 0));
+ else
+ screen = gpDesktop->screenGeometry(screenLayout.value(iGuestScreen, 0));
+ KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
+ m_pMachineLogic->display().GetScreenResolution(iGuestScreen, width, height, guestBpp, xOrigin, yOrigin, monitorStatus);
+ usedBits += screen.width() * /* display width */
+ screen.height() * /* display height */
+ guestBpp + /* guest bits per pixel */
+ _1M * 8; /* current cache per screen - may be changed in future */
+ }
+ usedBits += 4096 * 8; /* adapter info */
+ return usedBits;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.h
new file mode 100644
index 00000000..68df12e9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.h
@@ -0,0 +1,96 @@
+/* $Id: UIMultiScreenLayout.h $ */
+/** @file
+ * VBox Qt GUI - UIMultiScreenLayout class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIMultiScreenLayout_h
+#define FEQT_INCLUDED_SRC_runtime_UIMultiScreenLayout_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QMap>
+
+/* Forward declarations: */
+class UIMachineLogic;
+class QMenu;
+class QAction;
+
+/* Multi-screen layout manager: */
+class UIMultiScreenLayout : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about layout change. */
+ void sigScreenLayoutChange();
+
+public:
+
+ /* Constructor/destructor: */
+ UIMultiScreenLayout(UIMachineLogic *pMachineLogic);
+
+ /* API: Update stuff: */
+ void update();
+ void rebuild();
+
+ /* API: Getters: */
+ int hostScreenCount() const;
+ int guestScreenCount() const;
+ int hostScreenForGuestScreen(int iScreenId) const;
+ bool hasHostScreenForGuestScreen(int iScreenId) const;
+ quint64 memoryRequirements() const;
+
+private slots:
+
+ /* Handler: Screen change stuff: */
+ void sltHandleScreenLayoutChange(int iRequestedGuestScreen, int iRequestedHostScreen);
+
+private:
+
+ /* Helpers: Prepare stuff: */
+ void calculateHostMonitorCount();
+ void calculateGuestScreenCount();
+ void prepareConnections();
+
+ /* Other helpers: */
+ void saveScreenMapping();
+ quint64 memoryRequirements(const QMap<int, int> &screenLayout) const;
+
+ /* Variables: */
+ UIMachineLogic *m_pMachineLogic;
+ QList<int> m_guestScreens;
+ QList<int> m_disabledGuestScreens;
+ const uint m_cGuestScreens;
+ int m_cHostScreens;
+ QMap<int, int> m_screenMap;
+ QList<QMenu*> m_screenMenuList;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIMultiScreenLayout_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UISession.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UISession.cpp
new file mode 100644
index 00000000..71355c50
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UISession.cpp
@@ -0,0 +1,2228 @@
+/* $Id: UISession.cpp $ */
+/** @file
+ * VBox Qt GUI - UISession class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QBitmap>
+#include <QMenuBar>
+#include <QWidget>
+#ifdef VBOX_WS_MAC
+# include <QTimer>
+#endif
+#ifdef VBOX_WS_WIN
+# include <iprt/win/windows.h> /* Workaround for compile errors if included directly by QtWin. */
+# include <QtWin>
+#endif
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UISession.h"
+#include "UIMachine.h"
+#include "UIMedium.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogic.h"
+#include "UIMachineView.h"
+#include "UIMachineWindow.h"
+#include "UIMessageCenter.h"
+#include "UIMousePointerShapeData.h"
+#include "UINotificationCenter.h"
+#include "UIConsoleEventHandler.h"
+#include "UIFrameBuffer.h"
+#include "UISettingsDialogSpecific.h"
+#ifdef VBOX_WS_MAC
+# include "UICocoaApplication.h"
+# include "VBoxUtils-darwin.h"
+#endif
+#ifdef VBOX_GUI_WITH_KEYS_RESET_HANDLER
+# include "UIKeyboardHandler.h"
+# include <signal.h>
+#endif
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CAudioSettings.h"
+#include "CGraphicsAdapter.h"
+#include "CHostUSBDevice.h"
+#include "CRecordingSettings.h"
+#include "CSystemProperties.h"
+#include "CStorageController.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CHostNetworkInterface.h"
+#include "CVRDEServer.h"
+#include "CUSBController.h"
+#include "CUSBDeviceFilter.h"
+#include "CUSBDeviceFilters.h"
+#include "CHostVideoInputDevice.h"
+#include "CSnapshot.h"
+#include "CMedium.h"
+
+/* External includes: */
+#ifdef VBOX_WS_X11
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+#endif
+
+
+#ifdef VBOX_GUI_WITH_KEYS_RESET_HANDLER
+static void signalHandlerSIGUSR1(int sig, siginfo_t *, void *);
+#endif
+
+#ifdef VBOX_WS_MAC
+/**
+ * MacOS X: Application Services: Core Graphics: Display reconfiguration callback.
+ *
+ * Notifies UISession about @a display configuration change.
+ * Corresponding change described by Core Graphics @a flags.
+ * Uses UISession @a pHandler to process this change.
+ *
+ * @note Last argument (@a pHandler) must always be valid pointer to UISession object.
+ * @note Calls for UISession::sltHandleHostDisplayAboutToChange() slot if display configuration changed.
+ */
+void cgDisplayReconfigurationCallback(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *pHandler)
+{
+ /* Which flags we are handling? */
+ int iHandledFlags = kCGDisplayAddFlag /* display added */
+ | kCGDisplayRemoveFlag /* display removed */
+ | kCGDisplaySetModeFlag /* display mode changed */;
+
+ /* Handle 'display-add' case: */
+ if (flags & kCGDisplayAddFlag)
+ LogRelFlow(("GUI: UISession::cgDisplayReconfigurationCallback: Display added.\n"));
+ /* Handle 'display-remove' case: */
+ else if (flags & kCGDisplayRemoveFlag)
+ LogRelFlow(("GUI: UISession::cgDisplayReconfigurationCallback: Display removed.\n"));
+ /* Handle 'mode-set' case: */
+ else if (flags & kCGDisplaySetModeFlag)
+ LogRelFlow(("GUI: UISession::cgDisplayReconfigurationCallback: Display mode changed.\n"));
+
+ /* Ask handler to process our callback: */
+ if (flags & iHandledFlags)
+ QTimer::singleShot(0, static_cast<UISession*>(pHandler),
+ SLOT(sltHandleHostDisplayAboutToChange()));
+
+ Q_UNUSED(display);
+}
+#endif /* VBOX_WS_MAC */
+
+/* static */
+bool UISession::create(UISession *&pSession, UIMachine *pMachine)
+{
+ /* Make sure null pointer passed: */
+ AssertReturn(pSession == 0, false);
+
+ /* Create session UI: */
+ pSession = new UISession(pMachine);
+ /* Make sure it's prepared: */
+ if (!pSession->prepare())
+ {
+ /* Destroy session UI otherwise: */
+ destroy(pSession);
+ /* False in that case: */
+ return false;
+ }
+ /* True by default: */
+ return true;
+}
+
+/* static */
+void UISession::destroy(UISession *&pSession)
+{
+ /* Make sure valid pointer passed: */
+ AssertReturnVoid(pSession != 0);
+
+ /* Cleanup session UI: */
+ pSession->cleanup();
+ /* Destroy session: */
+ delete pSession;
+ pSession = 0;
+}
+
+bool UISession::initialize()
+{
+ /* Preprocess initialization: */
+ if (!preprocessInitialization())
+ return false;
+
+ /* Notify user about mouse&keyboard auto-capturing: */
+ if (gEDataManager->autoCaptureEnabled())
+ UINotificationMessage::remindAboutAutoCapture();
+
+ m_machineState = machine().GetState();
+
+ /* Apply debug settings from the command line. */
+ if (!debugger().isNull() && debugger().isOk())
+ {
+ if (uiCommon().areWeToExecuteAllInIem())
+ debugger().SetExecuteAllInIEM(true);
+ if (!uiCommon().isDefaultWarpPct())
+ debugger().SetVirtualTimeRate(uiCommon().getWarpPct());
+ }
+
+ /* Apply ad-hoc reconfigurations from the command line: */
+ if (uiCommon().hasFloppyImageToMount())
+ mountAdHocImage(KDeviceType_Floppy, UIMediumDeviceType_Floppy, uiCommon().getFloppyImage().toString());
+ if (uiCommon().hasDvdImageToMount())
+ mountAdHocImage(KDeviceType_DVD, UIMediumDeviceType_DVD, uiCommon().getDvdImage().toString());
+
+ /* Power UP if this is NOT separate process: */
+ if (!uiCommon().isSeparateProcess())
+ if (!powerUp())
+ return false;
+
+ /* Make sure all the pending Console events converted to signals
+ * during the powerUp() progress above reached their destinations.
+ * That is necessary to make sure all the pending machine state change events processed.
+ * We can't just use the machine state directly acquired from IMachine because there
+ * will be few places which are using stale machine state, not just this one. */
+ QApplication::sendPostedEvents(0, QEvent::MetaCall);
+
+ /* Check if we missed a really quick termination after successful startup: */
+ if (isTurnedOff())
+ {
+ LogRel(("GUI: Aborting startup due to invalid machine state detected: %d\n", machineState()));
+ return false;
+ }
+
+ /* Postprocess initialization: */
+ if (!postprocessInitialization())
+ return false;
+
+ /* Fetch corresponding states: */
+ if (uiCommon().isSeparateProcess())
+ {
+ m_fIsMouseSupportsAbsolute = mouse().GetAbsoluteSupported();
+ m_fIsMouseSupportsRelative = mouse().GetRelativeSupported();
+ m_fIsMouseSupportsTouchScreen = mouse().GetTouchScreenSupported();
+ m_fIsMouseSupportsTouchPad = mouse().GetTouchPadSupported();
+ m_fIsMouseHostCursorNeeded = mouse().GetNeedsHostCursor();
+ sltAdditionsChange();
+ }
+ machineLogic()->initializePostPowerUp();
+
+ /* Load VM settings: */
+ loadVMSettings();
+
+/* Log whether HID LEDs sync is enabled: */
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+ LogRel(("GUI: HID LEDs sync is %s\n",
+ uimachine()->machineLogic()->isHidLedsSyncEnabled()
+ ? "enabled" : "disabled"));
+#else /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+ LogRel(("GUI: HID LEDs sync is not supported on this platform\n"));
+#endif /* !VBOX_WS_MAC && !VBOX_WS_WIN */
+
+#ifdef VBOX_GUI_WITH_PIDFILE
+ uiCommon().createPidfile();
+#endif /* VBOX_GUI_WITH_PIDFILE */
+
+ /* Warn listeners about we are initialized: */
+ m_fInitialized = true;
+ emit sigInitialized();
+
+ /* True by default: */
+ return true;
+}
+
+bool UISession::powerUp()
+{
+ /* Power UP machine: */
+ CProgress progress = uiCommon().shouldStartPaused() ? console().PowerUpPaused() : console().PowerUp();
+
+ /* Check for immediate failure: */
+ if (!console().isOk() || progress.isNull())
+ {
+ if (uiCommon().showStartVMErrors())
+ msgCenter().cannotStartMachine(console(), machineName());
+ LogRel(("GUI: Aborting startup due to power up issue detected...\n"));
+ return false;
+ }
+
+ /* Some logging right after we powered up: */
+ LogRel(("Qt version: %s\n", UICommon::qtRTVersionString().toUtf8().constData()));
+#ifdef VBOX_WS_X11
+ LogRel(("X11 Window Manager code: %d\n", (int)uiCommon().typeOfWindowManager()));
+#endif
+
+ /* Enable 'manual-override',
+ * preventing automatic Runtime UI closing
+ * and visual representation mode changes: */
+ setManualOverrideMode(true);
+
+ /* Show "Starting/Restoring" progress dialog: */
+ if (isSaved())
+ {
+ msgCenter().showModalProgressDialog(progress, machineName(), ":/progress_state_restore_90px.png", 0, 0);
+ /* After restoring from 'saved' state, machine-window(s) geometry should be adjusted: */
+ machineLogic()->adjustMachineWindowsGeometry();
+ }
+ else
+ {
+#ifdef VBOX_IS_QT6_OR_LATER /** @todo why is this any problem on qt6? */
+ msgCenter().showModalProgressDialog(progress, machineName(), ":/progress_start_90px.png", 0, 0);
+#else
+ msgCenter().showModalProgressDialog(progress, machineName(), ":/progress_start_90px.png");
+#endif
+ /* After VM start, machine-window(s) size-hint(s) should be sent: */
+ machineLogic()->sendMachineWindowsSizeHints();
+ }
+
+ /* Check for progress failure: */
+ if (!progress.isOk() || progress.GetResultCode() != 0)
+ {
+ if (uiCommon().showStartVMErrors())
+ msgCenter().cannotStartMachine(progress, machineName());
+ LogRel(("GUI: Aborting startup due to power up progress issue detected...\n"));
+ return false;
+ }
+
+ /* Disable 'manual-override' finally: */
+ setManualOverrideMode(false);
+
+ /* True by default: */
+ return true;
+}
+
+void UISession::detachUi()
+{
+ /* Enable 'manual-override',
+ * preventing automatic Runtime UI closing: */
+ setManualOverrideMode(true);
+
+ /* Manually close Runtime UI: */
+ LogRel(("GUI: Detaching UI..\n"));
+ closeRuntimeUI();
+}
+
+void UISession::saveState()
+{
+ /* Saving state? */
+ bool fSaveState = true;
+
+ /* If VM is not paused, we should pause it first: */
+ if (!isPaused())
+ fSaveState = pause();
+
+ /* Save state: */
+ if (fSaveState)
+ {
+ /* Enable 'manual-override',
+ * preventing automatic Runtime UI closing: */
+ setManualOverrideMode(true);
+
+ /* Now, do the magic: */
+ LogRel(("GUI: Saving VM state..\n"));
+ UINotificationProgressMachineSaveState *pNotification = new UINotificationProgressMachineSaveState(machine());
+ connect(pNotification, &UINotificationProgressMachineSaveState::sigMachineStateSaved,
+ this, &UISession::sltHandleMachineStateSaved);
+ gpNotificationCenter->append(pNotification);
+ }
+}
+
+void UISession::shutdown()
+{
+ /* Warn the user about ACPI is not available if so: */
+ if (!console().GetGuestEnteredACPIMode())
+ return UINotificationMessage::cannotSendACPIToMachine();
+
+ /* Send ACPI shutdown signal if possible: */
+ LogRel(("GUI: Sending ACPI shutdown signal..\n"));
+ console().PowerButton();
+ if (!console().isOk())
+ UINotificationMessage::cannotACPIShutdownMachine(console());
+}
+
+void UISession::powerOff(bool fIncludingDiscard)
+{
+ /* Enable 'manual-override',
+ * preventing automatic Runtime UI closing: */
+ setManualOverrideMode(true);
+
+ /* Now, do the magic: */
+ LogRel(("GUI: Powering VM off..\n"));
+ UINotificationProgressMachinePowerOff *pNotification =
+ new UINotificationProgressMachinePowerOff(machine(), console(), fIncludingDiscard);
+ connect(pNotification, &UINotificationProgressMachinePowerOff::sigMachinePoweredOff,
+ this, &UISession::sltHandleMachinePoweredOff);
+ gpNotificationCenter->append(pNotification);
+}
+
+UIMachineLogic* UISession::machineLogic() const
+{
+ return uimachine() ? uimachine()->machineLogic() : 0;
+}
+
+QWidget* UISession::mainMachineWindow() const
+{
+ return machineLogic() ? machineLogic()->mainMachineWindow() : 0;
+}
+
+WId UISession::mainMachineWindowId() const
+{
+ return mainMachineWindow()->winId();
+}
+
+UIMachineWindow *UISession::activeMachineWindow() const
+{
+ return machineLogic() ? machineLogic()->activeMachineWindow() : 0;
+}
+
+bool UISession::isVisualStateAllowed(UIVisualStateType state) const
+{
+ return m_pMachine->isVisualStateAllowed(state);
+}
+
+void UISession::changeVisualState(UIVisualStateType visualStateType)
+{
+ m_pMachine->asyncChangeVisualState(visualStateType);
+}
+
+void UISession::setRequestedVisualState(UIVisualStateType visualStateType)
+{
+ m_pMachine->setRequestedVisualState(visualStateType);
+}
+
+UIVisualStateType UISession::requestedVisualState() const
+{
+ return m_pMachine->requestedVisualState();
+}
+
+bool UISession::setPause(bool fOn)
+{
+ if (fOn)
+ console().Pause();
+ else
+ console().Resume();
+
+ bool ok = console().isOk();
+ if (!ok)
+ {
+ if (fOn)
+ UINotificationMessage::cannotPauseMachine(console());
+ else
+ UINotificationMessage::cannotResumeMachine(console());
+ }
+
+ return ok;
+}
+
+void UISession::sltInstallGuestAdditionsFrom(const QString &strSource)
+{
+ if (!guestAdditionsUpgradable())
+ return sltMountDVDAdHoc(strSource);
+
+ /* Update guest additions automatically: */
+ UINotificationProgressGuestAdditionsInstall *pNotification =
+ new UINotificationProgressGuestAdditionsInstall(guest(), strSource);
+ connect(pNotification, &UINotificationProgressGuestAdditionsInstall::sigGuestAdditionsInstallationFailed,
+ this, &UISession::sltMountDVDAdHoc);
+ gpNotificationCenter->append(pNotification);
+}
+
+void UISession::sltMountDVDAdHoc(const QString &strSource)
+{
+ mountAdHocImage(KDeviceType_DVD, UIMediumDeviceType_DVD, strSource);
+}
+
+void UISession::closeRuntimeUI()
+{
+ /* First, we have to hide any opened modal/popup widgets.
+ * They then should unlock their event-loops asynchronously.
+ * If all such loops are unlocked, we can close Runtime UI. */
+ QWidget *pWidget = QApplication::activeModalWidget()
+ ? QApplication::activeModalWidget()
+ : QApplication::activePopupWidget()
+ ? QApplication::activePopupWidget()
+ : 0;
+ if (pWidget)
+ {
+ /* First we should try to close this widget: */
+ pWidget->close();
+ /* If widget rejected the 'close-event' we can
+ * still hide it and hope it will behave correctly
+ * and unlock his event-loop if any: */
+ if (!pWidget->isHidden())
+ pWidget->hide();
+ /* Asynchronously restart this slot: */
+ QMetaObject::invokeMethod(this, "closeRuntimeUI", Qt::QueuedConnection);
+ return;
+ }
+
+ /* Asynchronously ask UIMachine to close Runtime UI: */
+ LogRel(("GUI: Passing request to close Runtime UI from UI session to UI machine.\n"));
+ QMetaObject::invokeMethod(uimachine(), "closeRuntimeUI", Qt::QueuedConnection);
+}
+
+void UISession::sltDetachCOM()
+{
+ /* Cleanup everything COM related: */
+ cleanupFramebuffers();
+ cleanupConsoleEventHandlers();
+ cleanupNotificationCenter();
+ cleanupSession();
+}
+
+#ifdef RT_OS_DARWIN
+void UISession::sltHandleMenuBarConfigurationChange(const QUuid &uMachineID)
+{
+ /* Skip unrelated machine IDs: */
+ if (uiCommon().managedVMUuid() != uMachineID)
+ return;
+
+ /* Update Mac OS X menu-bar: */
+ updateMenu();
+}
+#endif /* RT_OS_DARWIN */
+
+void UISession::sltMousePointerShapeChange(const UIMousePointerShapeData &shapeData)
+{
+ /* In case if shape itself is present: */
+ if (shapeData.shape().size() > 0)
+ {
+ /* We are ignoring visibility flag: */
+ m_fIsHidingHostPointer = false;
+
+ /* And updating current shape data: */
+ m_shapeData = shapeData;
+ updateMousePointerShape();
+ }
+ /* In case if shape itself is NOT present: */
+ else
+ {
+ /* Remember if we should hide the cursor: */
+ m_fIsHidingHostPointer = !shapeData.isVisible();
+ }
+
+ /* Notify listeners about mouse capability changed: */
+ emit sigMousePointerShapeChange();
+}
+
+void UISession::sltMouseCapabilityChange(bool fSupportsAbsolute, bool fSupportsRelative,
+ bool fSupportsTouchScreen, bool fSupportsTouchPad,
+ bool fNeedsHostCursor)
+{
+ LogRelFlow(("GUI: UISession::sltMouseCapabilityChange: "
+ "Supports absolute: %s, Supports relative: %s, "
+ "Supports touchscreen: %s, Supports touchpad: %s, "
+ "Needs host cursor: %s\n",
+ fSupportsAbsolute ? "TRUE" : "FALSE", fSupportsRelative ? "TRUE" : "FALSE",
+ fSupportsTouchScreen ? "TRUE" : "FALSE", fSupportsTouchPad ? "TRUE" : "FALSE",
+ fNeedsHostCursor ? "TRUE" : "FALSE"));
+
+ /* Check if something had changed: */
+ if ( m_fIsMouseSupportsAbsolute != fSupportsAbsolute
+ || m_fIsMouseSupportsRelative != fSupportsRelative
+ || m_fIsMouseSupportsTouchScreen != fSupportsTouchScreen
+ || m_fIsMouseSupportsTouchPad != fSupportsTouchPad
+ || m_fIsMouseHostCursorNeeded != fNeedsHostCursor)
+ {
+ /* Store new data: */
+ m_fIsMouseSupportsAbsolute = fSupportsAbsolute;
+ m_fIsMouseSupportsRelative = fSupportsRelative;
+ m_fIsMouseSupportsTouchScreen = fSupportsTouchScreen;
+ m_fIsMouseSupportsTouchPad = fSupportsTouchPad;
+ m_fIsMouseHostCursorNeeded = fNeedsHostCursor;
+
+ /* Notify listeners about mouse capability changed: */
+ emit sigMouseCapabilityChange();
+ }
+}
+
+void UISession::sltCursorPositionChange(bool fContainsData, unsigned long uX, unsigned long uY)
+{
+ LogRelFlow(("GUI: UISession::sltCursorPositionChange: "
+ "Cursor position valid: %d, Cursor position: %dx%d\n",
+ fContainsData ? "TRUE" : "FALSE", uX, uY));
+
+ /* Check if something had changed: */
+ if ( m_fIsValidCursorPositionPresent != fContainsData
+ || m_cursorPosition.x() != (int)uX
+ || m_cursorPosition.y() != (int)uY)
+ {
+ /* Store new data: */
+ m_fIsValidCursorPositionPresent = fContainsData;
+ m_cursorPosition = QPoint(uX, uY);
+
+ /* Notify listeners about cursor position changed: */
+ emit sigCursorPositionChange();
+ }
+}
+
+void UISession::sltKeyboardLedsChangeEvent(bool fNumLock, bool fCapsLock, bool fScrollLock)
+{
+ /* Check if something had changed: */
+ if ( m_fNumLock != fNumLock
+ || m_fCapsLock != fCapsLock
+ || m_fScrollLock != fScrollLock)
+ {
+ /* Store new num lock data: */
+ if (m_fNumLock != fNumLock)
+ {
+ m_fNumLock = fNumLock;
+ m_uNumLockAdaptionCnt = 2;
+ }
+
+ /* Store new caps lock data: */
+ if (m_fCapsLock != fCapsLock)
+ {
+ m_fCapsLock = fCapsLock;
+ m_uCapsLockAdaptionCnt = 2;
+ }
+
+ /* Store new scroll lock data: */
+ if (m_fScrollLock != fScrollLock)
+ {
+ m_fScrollLock = fScrollLock;
+ }
+
+ /* Notify listeners about mouse capability changed: */
+ emit sigKeyboardLedsChange();
+ }
+}
+
+void UISession::sltStateChange(KMachineState state)
+{
+ /* Check if something had changed: */
+ if (m_machineState != state)
+ {
+ /* Store new data: */
+ m_machineStatePrevious = m_machineState;
+ m_machineState = state;
+
+ /* Notify listeners about machine state changed: */
+ emit sigMachineStateChange();
+ }
+}
+
+void UISession::sltVRDEChange()
+{
+ /* Make sure VRDE server is present: */
+ const CVRDEServer server = machine().GetVRDEServer();
+ AssertMsgReturnVoid(machine().isOk() && !server.isNull(),
+ ("VRDE server should NOT be null!\n"));
+
+ /* Check/Uncheck VRDE Server action depending on feature status: */
+ actionPool()->action(UIActionIndexRT_M_View_T_VRDEServer)->blockSignals(true);
+ actionPool()->action(UIActionIndexRT_M_View_T_VRDEServer)->setChecked(server.GetEnabled());
+ actionPool()->action(UIActionIndexRT_M_View_T_VRDEServer)->blockSignals(false);
+
+ /* Notify listeners about VRDE change: */
+ emit sigVRDEChange();
+}
+
+void UISession::sltRecordingChange()
+{
+ CRecordingSettings comRecordingSettings = machine().GetRecordingSettings();
+
+ /* Check/Uncheck Capture action depending on feature status: */
+ actionPool()->action(UIActionIndexRT_M_View_M_Recording_T_Start)->blockSignals(true);
+ actionPool()->action(UIActionIndexRT_M_View_M_Recording_T_Start)->setChecked(comRecordingSettings.GetEnabled());
+ actionPool()->action(UIActionIndexRT_M_View_M_Recording_T_Start)->blockSignals(false);
+
+ /* Notify listeners about Recording change: */
+ emit sigRecordingChange();
+}
+
+void UISession::sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo)
+{
+ /* Ignore KGuestMonitorChangedEventType_NewOrigin change event: */
+ if (changeType == KGuestMonitorChangedEventType_NewOrigin)
+ return;
+ /* Ignore KGuestMonitorChangedEventType_Disabled event for primary screen: */
+ AssertMsg(countOfVisibleWindows() > 0, ("All machine windows are hidden!"));
+ if (changeType == KGuestMonitorChangedEventType_Disabled && uScreenId == 0)
+ return;
+
+ /* Process KGuestMonitorChangedEventType_Enabled change event: */
+ if ( !isScreenVisible(uScreenId)
+ && changeType == KGuestMonitorChangedEventType_Enabled)
+ setScreenVisible(uScreenId, true);
+ /* Process KGuestMonitorChangedEventType_Disabled change event: */
+ else if ( isScreenVisible(uScreenId)
+ && changeType == KGuestMonitorChangedEventType_Disabled)
+ setScreenVisible(uScreenId, false);
+
+ /* Notify listeners about the change: */
+ emit sigGuestMonitorChange(changeType, uScreenId, screenGeo);
+}
+
+void UISession::sltHandleStorageDeviceChange(const CMediumAttachment &attachment, bool fRemoved, bool fSilent)
+{
+ /* Update action restrictions: */
+ updateActionRestrictions();
+
+ /* Notify listeners about storage device change: */
+ emit sigStorageDeviceChange(attachment, fRemoved, fSilent);
+}
+
+void UISession::sltAudioAdapterChange()
+{
+ /* Make sure Audio adapter is present: */
+ const CAudioSettings comAudioSettings = machine().GetAudioSettings();
+ const CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ AssertMsgReturnVoid(machine().isOk() && comAdapter.isNotNull(),
+ ("Audio adapter should NOT be null!\n"));
+
+ /* Check/Uncheck Audio adapter output/input actions depending on features status: */
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Output)->blockSignals(true);
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Output)->setChecked(comAdapter.GetEnabledOut());
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Output)->blockSignals(false);
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Input)->blockSignals(true);
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Input)->setChecked(comAdapter.GetEnabledIn());
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Input)->blockSignals(false);
+
+ /* Notify listeners about Audio adapter change: */
+ emit sigAudioAdapterChange();
+
+}
+
+void UISession::sltClipboardModeChange(KClipboardMode enmMode)
+{
+ emit sigClipboardModeChange(enmMode);
+}
+
+void UISession::sltDnDModeChange(KDnDMode enmMode)
+{
+ emit sigDnDModeChange(enmMode);
+}
+
+#ifdef RT_OS_DARWIN
+/**
+ * MacOS X: Restarts display-reconfiguration watchdog timer from the beginning.
+ * @note Watchdog is trying to determine display reconfiguration in
+ * UISession::sltCheckIfHostDisplayChanged() slot every 500ms for 40 tries.
+ */
+void UISession::sltHandleHostDisplayAboutToChange()
+{
+ LogRelFlow(("GUI: UISession::sltHandleHostDisplayAboutToChange()\n"));
+
+ if (m_pWatchdogDisplayChange->isActive())
+ m_pWatchdogDisplayChange->stop();
+ m_pWatchdogDisplayChange->setProperty("tryNumber", 1);
+ m_pWatchdogDisplayChange->start();
+}
+
+/**
+ * MacOS X: Determines display reconfiguration.
+ * @note Calls for UISession::sltHandleHostScreenCountChange() if screen count changed.
+ * @note Calls for UISession::sltHandleHostScreenGeometryChange() if screen geometry changed.
+ */
+void UISession::sltCheckIfHostDisplayChanged()
+{
+ LogRelFlow(("GUI: UISession::sltCheckIfHostDisplayChanged()\n"));
+
+ /* Check if display count changed: */
+ if (UIDesktopWidgetWatchdog::screenCount() != m_hostScreens.size())
+ {
+ /* Reset watchdog: */
+ m_pWatchdogDisplayChange->setProperty("tryNumber", 0);
+ /* Notify listeners about screen-count changed: */
+ return sltHandleHostScreenCountChange();
+ }
+ else
+ {
+ /* Check if at least one display geometry changed: */
+ for (int iScreenIndex = 0; iScreenIndex < UIDesktopWidgetWatchdog::screenCount(); ++iScreenIndex)
+ {
+ if (gpDesktop->screenGeometry(iScreenIndex) != m_hostScreens.at(iScreenIndex))
+ {
+ /* Reset watchdog: */
+ m_pWatchdogDisplayChange->setProperty("tryNumber", 0);
+ /* Notify listeners about screen-geometry changed: */
+ return sltHandleHostScreenGeometryChange();
+ }
+ }
+ }
+
+ /* Check if watchdog expired, restart if not: */
+ int cTryNumber = m_pWatchdogDisplayChange->property("tryNumber").toInt();
+ if (cTryNumber > 0 && cTryNumber < 40)
+ {
+ /* Restart watchdog again: */
+ m_pWatchdogDisplayChange->setProperty("tryNumber", ++cTryNumber);
+ m_pWatchdogDisplayChange->start();
+ }
+ else
+ {
+ /* Reset watchdog: */
+ m_pWatchdogDisplayChange->setProperty("tryNumber", 0);
+ }
+}
+#endif /* RT_OS_DARWIN */
+
+void UISession::sltHandleHostScreenCountChange()
+{
+ LogRelFlow(("GUI: UISession: Host-screen count changed.\n"));
+
+ /* Recache display data: */
+ updateHostScreenData();
+
+ /* Notify current machine-logic: */
+ emit sigHostScreenCountChange();
+}
+
+void UISession::sltHandleHostScreenGeometryChange()
+{
+ LogRelFlow(("GUI: UISession: Host-screen geometry changed.\n"));
+
+ /* Recache display data: */
+ updateHostScreenData();
+
+ /* Notify current machine-logic: */
+ emit sigHostScreenGeometryChange();
+}
+
+void UISession::sltHandleHostScreenAvailableAreaChange()
+{
+ LogRelFlow(("GUI: UISession: Host-screen available-area changed.\n"));
+
+ /* Notify current machine-logic: */
+ emit sigHostScreenAvailableAreaChange();
+}
+
+void UISession::sltHandleMachineStateSaved(bool fSuccess)
+{
+ /* Disable 'manual-override' finally: */
+ setManualOverrideMode(false);
+
+ /* Close Runtime UI if state was saved: */
+ if (fSuccess)
+ closeRuntimeUI();
+}
+
+void UISession::sltHandleMachinePoweredOff(bool fSuccess, bool fIncludingDiscard)
+{
+ /* Disable 'manual-override' finally: */
+ setManualOverrideMode(false);
+
+ /* Do we have other tasks? */
+ if (fSuccess)
+ {
+ if (!fIncludingDiscard)
+ closeRuntimeUI();
+ else
+ {
+ /* Now, do more magic! */
+ UINotificationProgressSnapshotRestore *pNotification =
+ new UINotificationProgressSnapshotRestore(uiCommon().managedVMUuid());
+ connect(pNotification, &UINotificationProgressSnapshotRestore::sigSnapshotRestored,
+ this, &UISession::sltHandleSnapshotRestored);
+ gpNotificationCenter->append(pNotification);
+ }
+ }
+}
+
+void UISession::sltHandleSnapshotRestored(bool)
+{
+ /* Close Runtime UI independent of snapshot restoring state: */
+ closeRuntimeUI();
+}
+
+void UISession::sltAdditionsChange()
+{
+ /* Variable flags: */
+ ULONG ulGuestAdditionsRunLevel = guest().GetAdditionsRunLevel();
+ LONG64 lLastUpdatedIgnored;
+ bool fIsGuestSupportsGraphics = guest().GetFacilityStatus(KAdditionsFacilityType_Graphics, lLastUpdatedIgnored)
+ == KAdditionsFacilityStatus_Active;
+ bool fIsGuestSupportsSeamless = guest().GetFacilityStatus(KAdditionsFacilityType_Seamless, lLastUpdatedIgnored)
+ == KAdditionsFacilityStatus_Active;
+ /* Check if something had changed: */
+ if (m_ulGuestAdditionsRunLevel != ulGuestAdditionsRunLevel ||
+ m_fIsGuestSupportsGraphics != fIsGuestSupportsGraphics ||
+ m_fIsGuestSupportsSeamless != fIsGuestSupportsSeamless)
+ {
+ /* Store new data: */
+ m_ulGuestAdditionsRunLevel = ulGuestAdditionsRunLevel;
+ m_fIsGuestSupportsGraphics = fIsGuestSupportsGraphics;
+ m_fIsGuestSupportsSeamless = fIsGuestSupportsSeamless;
+
+ /* Make sure action-pool knows whether GA supports graphics: */
+ actionPool()->toRuntime()->setGuestSupportsGraphics(m_fIsGuestSupportsGraphics);
+
+ if (actionPool()->action(UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions))
+ actionPool()->action(UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions)->setEnabled(guestAdditionsUpgradable());
+
+ /* Notify listeners about GA state really changed: */
+ LogRel(("GUI: UISession::sltAdditionsChange: GA state really changed, notifying listeners\n"));
+ emit sigAdditionsStateActualChange();
+ }
+
+ /* Notify listeners about GA state change event came: */
+ LogRel(("GUI: UISession::sltAdditionsChange: GA state change event came, notifying listeners\n"));
+ emit sigAdditionsStateChange();
+}
+
+UISession::UISession(UIMachine *pMachine)
+ : QObject(pMachine)
+ /* Base variables: */
+ , m_pMachine(pMachine)
+ , m_pActionPool(0)
+#ifdef VBOX_WS_MAC
+ , m_pMenuBar(0)
+#endif /* VBOX_WS_MAC */
+ /* Common variables: */
+ , m_machineStatePrevious(KMachineState_Null)
+ , m_machineState(KMachineState_Null)
+ , m_pMachineWindowIcon(0)
+#ifdef VBOX_WS_MAC
+ , m_pWatchdogDisplayChange(0)
+#endif /* VBOX_WS_MAC */
+ , m_defaultCloseAction(MachineCloseAction_Invalid)
+ , m_restrictedCloseActions(MachineCloseAction_Invalid)
+ , m_fAllCloseActionsRestricted(false)
+ /* Common flags: */
+ , m_fInitialized(false)
+ , m_fIsGuestResizeIgnored(false)
+ , m_fIsAutoCaptureDisabled(false)
+ , m_fIsManualOverride(false)
+ /* Guest additions flags: */
+ , m_ulGuestAdditionsRunLevel(0)
+ , m_fIsGuestSupportsGraphics(false)
+ , m_fIsGuestSupportsSeamless(false)
+ /* Mouse flags: */
+ , m_fNumLock(false)
+ , m_fCapsLock(false)
+ , m_fScrollLock(false)
+ , m_uNumLockAdaptionCnt(2)
+ , m_uCapsLockAdaptionCnt(2)
+ /* Mouse flags: */
+ , m_fIsMouseSupportsAbsolute(false)
+ , m_fIsMouseSupportsRelative(false)
+ , m_fIsMouseSupportsTouchScreen(false)
+ , m_fIsMouseSupportsTouchPad(false)
+ , m_fIsMouseHostCursorNeeded(false)
+ , m_fIsMouseCaptured(false)
+ , m_fIsMouseIntegrated(true)
+ , m_fIsValidPointerShapePresent(false)
+ , m_fIsHidingHostPointer(true)
+ , m_fIsValidCursorPositionPresent(false)
+ , m_enmVMExecutionEngine(KVMExecutionEngine_NotSet)
+ /* CPU hardware virtualization features for VM: */
+ , m_fIsHWVirtExNestedPagingEnabled(false)
+ , m_fIsHWVirtExUXEnabled(false)
+ /* VM's effective paravirtualization provider: */
+ , m_paraVirtProvider(KParavirtProvider_None)
+{
+}
+
+UISession::~UISession()
+{
+}
+
+bool UISession::prepare()
+{
+ /* Prepare COM stuff: */
+ if (!prepareSession())
+ return false;
+ prepareNotificationCenter();
+ prepareConsoleEventHandlers();
+ prepareFramebuffers();
+
+ /* Prepare GUI stuff: */
+ prepareActions();
+ prepareConnections();
+ prepareMachineWindowIcon();
+ prepareScreens();
+ prepareSignalHandling();
+
+ /* Load settings: */
+ loadSessionSettings();
+
+ /* True by default: */
+ return true;
+}
+
+bool UISession::prepareSession()
+{
+ /* Open session: */
+ m_session = uiCommon().openSession(uiCommon().managedVMUuid(),
+ uiCommon().isSeparateProcess()
+ ? KLockType_Shared
+ : KLockType_VM);
+ if (m_session.isNull())
+ return false;
+
+ /* Get machine: */
+ m_machine = m_session.GetMachine();
+ if (m_machine.isNull())
+ return false;
+
+ /* Get console: */
+ m_console = m_session.GetConsole();
+ if (m_console.isNull())
+ return false;
+
+ /* Get display: */
+ m_display = m_console.GetDisplay();
+ if (m_display.isNull())
+ return false;
+
+ /* Get guest: */
+ m_guest = m_console.GetGuest();
+ if (m_guest.isNull())
+ return false;
+
+ /* Get mouse: */
+ m_mouse = m_console.GetMouse();
+ if (m_mouse.isNull())
+ return false;
+
+ /* Get keyboard: */
+ m_keyboard = m_console.GetKeyboard();
+ if (m_keyboard.isNull())
+ return false;
+
+ /* Get debugger: */
+ m_debugger = m_console.GetDebugger();
+ if (m_debugger.isNull())
+ return false;
+
+ /* Update machine-name: */
+ m_strMachineName = machine().GetName();
+
+ /* Update machine-state: */
+ m_machineState = machine().GetState();
+
+ /* True by default: */
+ return true;
+}
+
+void UISession::prepareNotificationCenter()
+{
+ UINotificationCenter::create();
+}
+
+void UISession::prepareConsoleEventHandlers()
+{
+ /* Create console event-handler: */
+ UIConsoleEventHandler::create(this);
+
+ /* Add console event connections: */
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigMousePointerShapeChange,
+ this, &UISession::sltMousePointerShapeChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigMouseCapabilityChange,
+ this, &UISession::sltMouseCapabilityChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigCursorPositionChange,
+ this, &UISession::sltCursorPositionChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigKeyboardLedsChangeEvent,
+ this, &UISession::sltKeyboardLedsChangeEvent);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigStateChange,
+ this, &UISession::sltStateChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigAdditionsChange,
+ this, &UISession::sltAdditionsChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigVRDEChange,
+ this, &UISession::sltVRDEChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigRecordingChange,
+ this, &UISession::sltRecordingChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigNetworkAdapterChange,
+ this, &UISession::sigNetworkAdapterChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigStorageDeviceChange,
+ this, &UISession::sltHandleStorageDeviceChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigMediumChange,
+ this, &UISession::sigMediumChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigUSBControllerChange,
+ this, &UISession::sigUSBControllerChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigUSBDeviceStateChange,
+ this, &UISession::sigUSBDeviceStateChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigSharedFolderChange,
+ this, &UISession::sigSharedFolderChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigRuntimeError,
+ this, &UISession::sigRuntimeError);
+#ifdef VBOX_WS_MAC
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigShowWindow,
+ this, &UISession::sigShowWindows, Qt::QueuedConnection);
+#endif /* VBOX_WS_MAC */
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigCPUExecutionCapChange,
+ this, &UISession::sigCPUExecutionCapChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigGuestMonitorChange,
+ this, &UISession::sltGuestMonitorChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigAudioAdapterChange,
+ this, &UISession::sltAudioAdapterChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigClipboardModeChange,
+ this, &UISession::sltClipboardModeChange);
+ connect(gConsoleEvents, &UIConsoleEventHandler::sigDnDModeChange,
+ this, &UISession::sltDnDModeChange);
+}
+
+void UISession::prepareFramebuffers()
+{
+ /* Each framebuffer will be really prepared on first UIMachineView creation: */
+ m_frameBufferVector.resize(machine().GetGraphicsAdapter().GetMonitorCount());
+}
+
+void UISession::prepareActions()
+{
+ /* Create action-pool: */
+ m_pActionPool = UIActionPool::create(UIActionPoolType_Runtime);
+ if (actionPool())
+ {
+ /* Make sure action-pool knows guest-screen count: */
+ actionPool()->toRuntime()->setGuestScreenCount(m_frameBufferVector.size());
+ /* Update action restrictions: */
+ updateActionRestrictions();
+
+#ifdef VBOX_WS_MAC
+ /* Create Mac OS X menu-bar: */
+ m_pMenuBar = new QMenuBar;
+ if (m_pMenuBar)
+ {
+ /* Configure Mac OS X menu-bar: */
+ connect(gEDataManager, &UIExtraDataManager::sigMenuBarConfigurationChange,
+ this, &UISession::sltHandleMenuBarConfigurationChange);
+ /* Update Mac OS X menu-bar: */
+ updateMenu();
+ }
+#endif /* VBOX_WS_MAC */
+ /* Postpone enabling the GA update action until GA's are loaded: */
+ if (actionPool()->action(UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions))
+ actionPool()->action(UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions)->setEnabled(false);
+ }
+}
+
+void UISession::prepareConnections()
+{
+ /* UICommon connections: */
+ connect(&uiCommon(), &UICommon::sigAskToDetachCOM, this, &UISession::sltDetachCOM);
+
+#ifdef VBOX_WS_MAC
+ /* Install native display reconfiguration callback: */
+ CGDisplayRegisterReconfigurationCallback(cgDisplayReconfigurationCallback, this);
+#else /* !VBOX_WS_MAC */
+ /* Install Qt display reconfiguration callbacks: */
+ connect(gpDesktop, &UIDesktopWidgetWatchdog::sigHostScreenCountChanged,
+ this, &UISession::sltHandleHostScreenCountChange);
+ connect(gpDesktop, &UIDesktopWidgetWatchdog::sigHostScreenResized,
+ this, &UISession::sltHandleHostScreenGeometryChange);
+# if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ connect(gpDesktop, &UIDesktopWidgetWatchdog::sigHostScreenWorkAreaRecalculated,
+ this, &UISession::sltHandleHostScreenAvailableAreaChange);
+# else /* !VBOX_WS_X11 || VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+ connect(gpDesktop, &UIDesktopWidgetWatchdog::sigHostScreenWorkAreaResized,
+ this, &UISession::sltHandleHostScreenAvailableAreaChange);
+# endif /* !VBOX_WS_X11 || VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+#endif /* !VBOX_WS_MAC */
+}
+
+void UISession::prepareMachineWindowIcon()
+{
+ /* Acquire user machine-window icon: */
+ QIcon icon = generalIconPool().userMachineIcon(machine());
+ /* Use the OS type icon if user one was not set: */
+ if (icon.isNull())
+ icon = generalIconPool().guestOSTypeIcon(machine().GetOSTypeId());
+ /* Use the default icon if nothing else works: */
+ if (icon.isNull())
+ icon = QIcon(":/VirtualBox_48px.png");
+ /* Store the icon dynamically: */
+ m_pMachineWindowIcon = new QIcon(icon);
+}
+
+void UISession::prepareScreens()
+{
+ /* Recache display data: */
+ updateHostScreenData();
+
+#ifdef VBOX_WS_MAC
+ /* Prepare display-change watchdog: */
+ m_pWatchdogDisplayChange = new QTimer(this);
+ {
+ m_pWatchdogDisplayChange->setInterval(500);
+ m_pWatchdogDisplayChange->setSingleShot(true);
+ connect(m_pWatchdogDisplayChange, &QTimer::timeout,
+ this, &UISession::sltCheckIfHostDisplayChanged);
+ }
+#endif /* VBOX_WS_MAC */
+
+ /* Prepare initial screen visibility status: */
+ m_monitorVisibilityVector.resize(machine().GetGraphicsAdapter().GetMonitorCount());
+ m_monitorVisibilityVector.fill(false);
+ m_monitorVisibilityVector[0] = true;
+
+ /* Prepare empty last full-screen size vector: */
+ m_monitorLastFullScreenSizeVector.resize(machine().GetGraphicsAdapter().GetMonitorCount());
+ m_monitorLastFullScreenSizeVector.fill(QSize(-1, -1));
+
+ /* If machine is in 'saved' state: */
+ if (isSaved())
+ {
+ /* Update screen visibility status from saved-state: */
+ for (int iScreenIndex = 0; iScreenIndex < m_monitorVisibilityVector.size(); ++iScreenIndex)
+ {
+ BOOL fEnabled = true;
+ ULONG uGuestOriginX = 0, uGuestOriginY = 0, uGuestWidth = 0, uGuestHeight = 0;
+ machine().QuerySavedGuestScreenInfo(iScreenIndex,
+ uGuestOriginX, uGuestOriginY,
+ uGuestWidth, uGuestHeight, fEnabled);
+ m_monitorVisibilityVector[iScreenIndex] = fEnabled;
+ }
+ /* And make sure at least one of them is visible (primary if others are hidden): */
+ if (countOfVisibleWindows() < 1)
+ m_monitorVisibilityVector[0] = true;
+ }
+ else if (uiCommon().isSeparateProcess())
+ {
+ /* Update screen visibility status from display directly: */
+ for (int iScreenIndex = 0; iScreenIndex < m_monitorVisibilityVector.size(); ++iScreenIndex)
+ {
+ KGuestMonitorStatus enmStatus = KGuestMonitorStatus_Disabled;
+ ULONG uGuestWidth = 0, uGuestHeight = 0, uBpp = 0;
+ LONG iGuestOriginX = 0, iGuestOriginY = 0;
+ display().GetScreenResolution(iScreenIndex,
+ uGuestWidth, uGuestHeight, uBpp,
+ iGuestOriginX, iGuestOriginY, enmStatus);
+ m_monitorVisibilityVector[iScreenIndex] = ( enmStatus == KGuestMonitorStatus_Enabled
+ || enmStatus == KGuestMonitorStatus_Blank);
+ }
+ /* And make sure at least one of them is visible (primary if others are hidden): */
+ if (countOfVisibleWindows() < 1)
+ m_monitorVisibilityVector[0] = true;
+ }
+
+ /* Prepare initial screen visibility status of host-desires (same as facts): */
+ m_monitorVisibilityVectorHostDesires.resize(machine().GetGraphicsAdapter().GetMonitorCount());
+ for (int iScreenIndex = 0; iScreenIndex < m_monitorVisibilityVector.size(); ++iScreenIndex)
+ m_monitorVisibilityVectorHostDesires[iScreenIndex] = m_monitorVisibilityVector[iScreenIndex];
+
+ /* Make sure action-pool knows guest-screen visibility status: */
+ for (int iScreenIndex = 0; iScreenIndex < m_monitorVisibilityVector.size(); ++iScreenIndex)
+ actionPool()->toRuntime()->setGuestScreenVisible(iScreenIndex, m_monitorVisibilityVector.at(iScreenIndex));
+}
+
+void UISession::prepareSignalHandling()
+{
+#ifdef VBOX_GUI_WITH_KEYS_RESET_HANDLER
+ struct sigaction sa;
+ sa.sa_sigaction = &signalHandlerSIGUSR1;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ sigaction(SIGUSR1, &sa, NULL);
+#endif /* VBOX_GUI_WITH_KEYS_RESET_HANDLER */
+}
+
+void UISession::loadSessionSettings()
+{
+ /* Load extra-data settings: */
+ {
+ /* Get machine ID: */
+ const QUuid uMachineID = uiCommon().managedVMUuid();
+
+#ifndef VBOX_WS_MAC
+ /* Load user's machine-window name postfix: */
+ m_strMachineWindowNamePostfix = gEDataManager->machineWindowNamePostfix(uMachineID);
+#endif
+
+ /* Should guest autoresize? */
+ QAction *pGuestAutoresizeSwitch = actionPool()->action(UIActionIndexRT_M_View_T_GuestAutoresize);
+ pGuestAutoresizeSwitch->setChecked(gEDataManager->guestScreenAutoResizeEnabled(uMachineID));
+
+#ifdef VBOX_WS_MAC
+ /* User-element (Menu-bar and Dock) options: */
+ {
+ const bool fDisabled = gEDataManager->guiFeatureEnabled(GUIFeatureType_NoUserElements);
+ if (fDisabled)
+ UICocoaApplication::instance()->hideUserElements();
+ }
+#else /* !VBOX_WS_MAC */
+ /* Menu-bar options: */
+ {
+ const bool fEnabledGlobally = !gEDataManager->guiFeatureEnabled(GUIFeatureType_NoMenuBar);
+ const bool fEnabledForMachine = gEDataManager->menuBarEnabled(uMachineID);
+ const bool fEnabled = fEnabledGlobally && fEnabledForMachine;
+ QAction *pActionMenuBarSettings = actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_S_Settings);
+ pActionMenuBarSettings->setEnabled(fEnabled);
+ QAction *pActionMenuBarSwitch = actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility);
+ pActionMenuBarSwitch->blockSignals(true);
+ pActionMenuBarSwitch->setChecked(fEnabled);
+ pActionMenuBarSwitch->blockSignals(false);
+ }
+#endif /* !VBOX_WS_MAC */
+
+ /* Status-bar options: */
+ {
+ const bool fEnabledGlobally = !gEDataManager->guiFeatureEnabled(GUIFeatureType_NoStatusBar);
+ const bool fEnabledForMachine = gEDataManager->statusBarEnabled(uMachineID);
+ const bool fEnabled = fEnabledGlobally && fEnabledForMachine;
+ QAction *pActionStatusBarSettings = actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_S_Settings);
+ pActionStatusBarSettings->setEnabled(fEnabled);
+ QAction *pActionStatusBarSwitch = actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility);
+ pActionStatusBarSwitch->blockSignals(true);
+ pActionStatusBarSwitch->setChecked(fEnabled);
+ pActionStatusBarSwitch->blockSignals(false);
+ }
+
+ /* Input options: */
+ actionPool()->action(UIActionIndexRT_M_Input_M_Mouse_T_Integration)->setChecked(isMouseIntegrated());
+
+ /* Devices options: */
+ {
+ const CAudioSettings comAudioSettings = m_machine.GetAudioSettings();
+ const CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Output)->blockSignals(true);
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Output)->setChecked(comAdapter.GetEnabledOut());
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Output)->blockSignals(false);
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Input)->blockSignals(true);
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Input)->setChecked(comAdapter.GetEnabledIn());
+ actionPool()->action(UIActionIndexRT_M_Devices_M_Audio_T_Input)->blockSignals(false);
+ }
+
+ /* What is the default close action and the restricted are? */
+ m_defaultCloseAction = gEDataManager->defaultMachineCloseAction(uMachineID);
+ m_restrictedCloseActions = gEDataManager->restrictedMachineCloseActions(uMachineID);
+ m_fAllCloseActionsRestricted = (!uiCommon().isSeparateProcess() || (m_restrictedCloseActions & MachineCloseAction_Detach))
+ && (m_restrictedCloseActions & MachineCloseAction_SaveState)
+ && (m_restrictedCloseActions & MachineCloseAction_Shutdown)
+ && (m_restrictedCloseActions & MachineCloseAction_PowerOff);
+ }
+}
+
+void UISession::cleanupMachineWindowIcon()
+{
+ /* Cleanup machine-window icon: */
+ delete m_pMachineWindowIcon;
+ m_pMachineWindowIcon = 0;
+}
+
+void UISession::cleanupConnections()
+{
+#ifdef VBOX_WS_MAC
+ /* Remove display reconfiguration callback: */
+ CGDisplayRemoveReconfigurationCallback(cgDisplayReconfigurationCallback, this);
+#endif /* VBOX_WS_MAC */
+}
+
+void UISession::cleanupActions()
+{
+#ifdef VBOX_WS_MAC
+ /* Destroy Mac OS X menu-bar: */
+ delete m_pMenuBar;
+ m_pMenuBar = 0;
+#endif /* VBOX_WS_MAC */
+
+ /* Destroy action-pool if necessary: */
+ if (actionPool())
+ UIActionPool::destroy(actionPool());
+}
+
+void UISession::cleanupFramebuffers()
+{
+ /* Cleanup framebuffers finally: */
+ for (int i = m_frameBufferVector.size() - 1; i >= 0; --i)
+ {
+ UIFrameBuffer *pFrameBuffer = m_frameBufferVector[i];
+ if (pFrameBuffer)
+ {
+ /* Mark framebuffer as unused: */
+ pFrameBuffer->setMarkAsUnused(true);
+ /* Detach framebuffer from Display: */
+ pFrameBuffer->detach();
+ /* Delete framebuffer reference: */
+ delete pFrameBuffer;
+ }
+ }
+ m_frameBufferVector.clear();
+
+ /* Make sure action-pool knows guest-screen count: */
+ if (actionPool())
+ actionPool()->toRuntime()->setGuestScreenCount(m_frameBufferVector.size());
+}
+
+void UISession::cleanupConsoleEventHandlers()
+{
+ /* Destroy console event-handler if necessary: */
+ if (gConsoleEvents)
+ UIConsoleEventHandler::destroy();
+}
+
+void UISession::cleanupNotificationCenter()
+{
+ UINotificationCenter::destroy();
+}
+
+void UISession::cleanupSession()
+{
+ /* Detach debugger: */
+ if (!m_debugger.isNull())
+ m_debugger.detach();
+
+ /* Detach keyboard: */
+ if (!m_keyboard.isNull())
+ m_keyboard.detach();
+
+ /* Detach mouse: */
+ if (!m_mouse.isNull())
+ m_mouse.detach();
+
+ /* Detach guest: */
+ if (!m_guest.isNull())
+ m_guest.detach();
+
+ /* Detach display: */
+ if (!m_display.isNull())
+ m_display.detach();
+
+ /* Detach console: */
+ if (!m_console.isNull())
+ m_console.detach();
+
+ /* Detach machine: */
+ if (!m_machine.isNull())
+ m_machine.detach();
+
+ /* Close session: */
+ if (!m_session.isNull() && uiCommon().isVBoxSVCAvailable())
+ {
+ m_session.UnlockMachine();
+ m_session.detach();
+ }
+}
+
+void UISession::cleanup()
+{
+ /* Cleanup GUI stuff: */
+ //cleanupSignalHandling();
+ //cleanupScreens();
+ cleanupMachineWindowIcon();
+ cleanupConnections();
+ cleanupActions();
+}
+
+#ifdef VBOX_WS_MAC
+void UISession::updateMenu()
+{
+ /* Rebuild Mac OS X menu-bar: */
+ m_pMenuBar->clear();
+ foreach (QMenu *pMenu, actionPool()->menus())
+ {
+ UIMenu *pMenuUI = qobject_cast<UIMenu*>(pMenu);
+ if (!pMenuUI->isConsumable() || !pMenuUI->isConsumed())
+ m_pMenuBar->addMenu(pMenuUI);
+ if (pMenuUI->isConsumable() && !pMenuUI->isConsumed())
+ pMenuUI->setConsumed(true);
+ }
+ /* Update the dock menu as well: */
+ if (machineLogic())
+ machineLogic()->updateDock();
+}
+#endif /* VBOX_WS_MAC */
+
+/** Generate a BGRA bitmap which approximates a XOR/AND mouse pointer.
+ *
+ * Pixels which has 1 in the AND mask and not 0 in the XOR mask are replaced by
+ * the inverted pixel and 8 surrounding pixels with the original color.
+ * Fort example a white pixel (W) is replaced with a black (B) pixel:
+ * WWW
+ * W -> WBW
+ * WWW
+ * The surrounding pixels are written only if the corresponding source pixel
+ * does not affect the screen, i.e. AND bit is 1 and XOR value is 0.
+ */
+static void renderCursorPixels(const uint32_t *pu32XOR, const uint8_t *pu8AND,
+ uint32_t u32Width, uint32_t u32Height,
+ uint32_t *pu32Pixels, uint32_t cbPixels)
+{
+ /* Output pixels set to 0 which allow to not write transparent pixels anymore. */
+ memset(pu32Pixels, 0, cbPixels);
+
+ const uint32_t *pu32XORSrc = pu32XOR; /* Iterator for source XOR pixels. */
+ const uint8_t *pu8ANDSrcLine = pu8AND; /* The current AND mask scanline. */
+ uint32_t *pu32Dst = pu32Pixels; /* Iterator for all destination BGRA pixels. */
+
+ /* Some useful constants. */
+ const int cbANDLine = ((int)u32Width + 7) / 8;
+
+ int y;
+ for (y = 0; y < (int)u32Height; ++y)
+ {
+ int x;
+ for (x = 0; x < (int)u32Width; ++x)
+ {
+ const uint32_t u32Pixel = *pu32XORSrc; /* Current pixel at (x,y) */
+ const uint8_t *pu8ANDSrc = pu8ANDSrcLine + x / 8; /* Byte which containt current AND bit. */
+
+ if ((*pu8ANDSrc << (x % 8)) & 0x80)
+ {
+ if (u32Pixel)
+ {
+ const uint32_t u32PixelInverted = ~u32Pixel;
+
+ /* Scan neighbor pixels and assign them if they are transparent. */
+ int dy;
+ for (dy = -1; dy <= 1; ++dy)
+ {
+ const int yn = y + dy;
+ if (yn < 0 || yn >= (int)u32Height)
+ continue; /* Do not cross the bounds. */
+
+ int dx;
+ for (dx = -1; dx <= 1; ++dx)
+ {
+ const int xn = x + dx;
+ if (xn < 0 || xn >= (int)u32Width)
+ continue; /* Do not cross the bounds. */
+
+ if (dx != 0 || dy != 0)
+ {
+ /* Check if the neighbor pixel is transparent. */
+ const uint32_t *pu32XORNeighborSrc = &pu32XORSrc[dy * (int)u32Width + dx];
+ const uint8_t *pu8ANDNeighborSrc = pu8ANDSrcLine + dy * cbANDLine + xn / 8;
+ if ( *pu32XORNeighborSrc == 0
+ && ((*pu8ANDNeighborSrc << (xn % 8)) & 0x80) != 0)
+ {
+ /* Transparent neighbor pixels are replaced with the source pixel value. */
+ uint32_t *pu32PixelNeighborDst = &pu32Dst[dy * (int)u32Width + dx];
+ *pu32PixelNeighborDst = u32Pixel | 0xFF000000;
+ }
+ }
+ else
+ {
+ /* The pixel itself is replaced with inverted value. */
+ *pu32Dst = u32PixelInverted | 0xFF000000;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* The pixel does not affect the screen.
+ * Do nothing. Do not touch destination which can already contain generated pixels.
+ */
+ }
+ }
+ else
+ {
+ /* AND bit is 0, the pixel will be just drawn. */
+ *pu32Dst = u32Pixel | 0xFF000000;
+ }
+
+ ++pu32XORSrc; /* Next source pixel. */
+ ++pu32Dst; /* Next destination pixel. */
+ }
+
+ /* Next AND scanline. */
+ pu8ANDSrcLine += cbANDLine;
+ }
+}
+
+#ifdef VBOX_WS_WIN
+static bool isPointer1bpp(const uint8_t *pu8XorMask,
+ uint uWidth,
+ uint uHeight)
+{
+ /* Check if the pointer has only 0 and 0xFFFFFF pixels, ignoring the alpha channel. */
+ const uint32_t *pu32Src = (uint32_t *)pu8XorMask;
+
+ uint y;
+ for (y = 0; y < uHeight ; ++y)
+ {
+ uint x;
+ for (x = 0; x < uWidth; ++x)
+ {
+ const uint32_t u32Pixel = pu32Src[x] & UINT32_C(0xFFFFFF);
+ if (u32Pixel != 0 && u32Pixel != UINT32_C(0xFFFFFF))
+ return false;
+ }
+
+ pu32Src += uWidth;
+ }
+
+ return true;
+}
+#endif /* VBOX_WS_WIN */
+
+void UISession::updateMousePointerShape()
+{
+ /* Fetch incoming shape data: */
+ const bool fHasAlpha = m_shapeData.hasAlpha();
+ const uint uWidth = m_shapeData.shapeSize().width();
+ const uint uHeight = m_shapeData.shapeSize().height();
+ const uchar *pShapeData = m_shapeData.shape().constData();
+ AssertMsgReturnVoid(pShapeData, ("Shape data must not be NULL!\n"));
+
+ /* Invalidate mouse pointer shape initially: */
+ m_fIsValidPointerShapePresent = false;
+ m_cursorShapePixmap = QPixmap();
+ m_cursorMaskPixmap = QPixmap();
+
+ /* Parse incoming shape data: */
+ const uchar *pSrcAndMaskPtr = pShapeData;
+ const uint uAndMaskSize = (uWidth + 7) / 8 * uHeight;
+ const uchar *pSrcShapePtr = pShapeData + ((uAndMaskSize + 3) & ~3);
+
+#if defined (VBOX_WS_WIN)
+
+ /* Create an ARGB image out of the shape data: */
+
+ // WORKAROUND:
+ // Qt5 QCursor recommends 32 x 32 cursor, therefore the original data is copied to
+ // a larger QImage if necessary. Cursors like 10x16 did not work correctly (Solaris 10 guest).
+ // Align the cursor dimensions to 32 bit pixels, because for example a 56x56 monochrome cursor
+ // did not work correctly on Windows host.
+ const uint uCursorWidth = RT_ALIGN_32(uWidth, 32);
+ const uint uCursorHeight = RT_ALIGN_32(uHeight, 32);
+
+ if (fHasAlpha)
+ {
+ QImage image(uCursorWidth, uCursorHeight, QImage::Format_ARGB32);
+ memset(image.bits(), 0, image.byteCount());
+
+ const uint32_t *pu32SrcShapeScanline = (uint32_t *)pSrcShapePtr;
+ for (uint y = 0; y < uHeight; ++y, pu32SrcShapeScanline += uWidth)
+ memcpy(image.scanLine(y), pu32SrcShapeScanline, uWidth * sizeof(uint32_t));
+
+ m_cursorShapePixmap = QPixmap::fromImage(image);
+ }
+ else
+ {
+ if (isPointer1bpp(pSrcShapePtr, uWidth, uHeight))
+ {
+ /* Incoming data consist of 32 bit BGR XOR mask and 1 bit AND mask.
+ * XOR pixels contain either 0x00000000 or 0x00FFFFFF.
+ *
+ * Originally intended result (F denotes 0x00FFFFFF):
+ * XOR AND
+ * 0 0 black
+ * F 0 white
+ * 0 1 transparent
+ * F 1 xor'd
+ *
+ * Actual Qt5 result for color table 0:0xFF000000, 1:0xFFFFFFFF
+ * (tested on Windows 7 and 10 64 bit hosts):
+ * Bitmap Mask
+ * 0 0 black
+ * 1 0 white
+ * 0 1 xor
+ * 1 1 transparent
+ *
+ */
+
+ QVector<QRgb> colors(2);
+ colors[0] = UINT32_C(0xFF000000);
+ colors[1] = UINT32_C(0xFFFFFFFF);
+
+ QImage bitmap(uCursorWidth, uCursorHeight, QImage::Format_Mono);
+ bitmap.setColorTable(colors);
+ memset(bitmap.bits(), 0xFF, bitmap.byteCount());
+
+ QImage mask(uCursorWidth, uCursorHeight, QImage::Format_Mono);
+ mask.setColorTable(colors);
+ memset(mask.bits(), 0xFF, mask.byteCount());
+
+ const uint8_t *pu8SrcAndScanline = pSrcAndMaskPtr;
+ const uint32_t *pu32SrcShapeScanline = (uint32_t *)pSrcShapePtr;
+ for (uint y = 0; y < uHeight; ++y)
+ {
+ for (uint x = 0; x < uWidth; ++x)
+ {
+ const uint8_t u8Bit = (uint8_t)(1 << (7 - x % 8));
+
+ const uint8_t u8SrcMaskByte = pu8SrcAndScanline[x / 8];
+ const uint8_t u8SrcMaskBit = u8SrcMaskByte & u8Bit;
+ const uint32_t u32SrcPixel = pu32SrcShapeScanline[x] & UINT32_C(0xFFFFFF);
+
+ uint8_t *pu8DstMaskByte = &mask.scanLine(y)[x / 8];
+ uint8_t *pu8DstBitmapByte = &bitmap.scanLine(y)[x / 8];
+
+ if (u8SrcMaskBit == 0)
+ {
+ if (u32SrcPixel == 0)
+ {
+ /* Black: Qt Bitmap = 0, Mask = 0 */
+ *pu8DstMaskByte &= ~u8Bit;
+ *pu8DstBitmapByte &= ~u8Bit;
+ }
+ else
+ {
+ /* White: Qt Bitmap = 1, Mask = 0 */
+ *pu8DstMaskByte &= ~u8Bit;
+ *pu8DstBitmapByte |= u8Bit;
+ }
+ }
+ else
+ {
+ if (u32SrcPixel == 0)
+ {
+ /* Transparent: Qt Bitmap = 1, Mask = 1 */
+ *pu8DstMaskByte |= u8Bit;
+ *pu8DstBitmapByte |= u8Bit;
+ }
+ else
+ {
+ /* Xor'ed: Qt Bitmap = 0, Mask = 1 */
+ *pu8DstMaskByte |= u8Bit;
+ *pu8DstBitmapByte &= ~u8Bit;
+ }
+ }
+ }
+
+ pu8SrcAndScanline += (uWidth + 7) / 8;
+ pu32SrcShapeScanline += uWidth;
+ }
+
+ m_cursorShapePixmap = QBitmap::fromImage(bitmap);
+ m_cursorMaskPixmap = QBitmap::fromImage(mask);
+ }
+ else
+ {
+ /* Assign alpha channel values according to the AND mask: 1 -> 0x00, 0 -> 0xFF: */
+ QImage image(uCursorWidth, uCursorHeight, QImage::Format_ARGB32);
+ memset(image.bits(), 0, image.byteCount());
+
+ const uint8_t *pu8SrcAndScanline = pSrcAndMaskPtr;
+ const uint32_t *pu32SrcShapeScanline = (uint32_t *)pSrcShapePtr;
+
+ for (uint y = 0; y < uHeight; ++y)
+ {
+ uint32_t *pu32DstPixel = (uint32_t *)image.scanLine(y);
+
+ for (uint x = 0; x < uWidth; ++x)
+ {
+ const uint8_t u8Bit = (uint8_t)(1 << (7 - x % 8));
+ const uint8_t u8SrcMaskByte = pu8SrcAndScanline[x / 8];
+
+ if (u8SrcMaskByte & u8Bit)
+ *pu32DstPixel++ = pu32SrcShapeScanline[x] & UINT32_C(0x00FFFFFF);
+ else
+ *pu32DstPixel++ = pu32SrcShapeScanline[x] | UINT32_C(0xFF000000);
+ }
+
+ pu32SrcShapeScanline += uWidth;
+ pu8SrcAndScanline += (uWidth + 7) / 8;
+ }
+
+ m_cursorShapePixmap = QPixmap::fromImage(image);
+ }
+ }
+
+ /* Mark mouse pointer shape valid: */
+ m_fIsValidPointerShapePresent = true;
+
+#elif defined(VBOX_WS_X11) || defined(VBOX_WS_MAC)
+
+ /* Create an ARGB image out of the shape data: */
+ QImage image(uWidth, uHeight, QImage::Format_ARGB32);
+
+ if (fHasAlpha)
+ {
+ memcpy(image.bits(), pSrcShapePtr, uHeight * uWidth * 4);
+ }
+ else
+ {
+ renderCursorPixels((uint32_t *)pSrcShapePtr, pSrcAndMaskPtr,
+ uWidth, uHeight,
+ (uint32_t *)image.bits(), uHeight * uWidth * 4);
+ }
+
+ /* Create cursor-pixmap from the image: */
+ m_cursorShapePixmap = QPixmap::fromImage(image);
+
+ /* Mark mouse pointer shape valid: */
+ m_fIsValidPointerShapePresent = true;
+
+#else
+
+# warning "port me"
+
+#endif
+
+ /* Cache cursor pixmap size and hotspot: */
+ m_cursorSize = m_cursorShapePixmap.size();
+ m_cursorHotspot = m_shapeData.hotSpot();
+}
+
+bool UISession::preprocessInitialization()
+{
+#ifdef VBOX_WITH_NETFLT
+ /* Skip further checks if VM in saved state */
+ if (isSaved())
+ return true;
+
+ /* Make sure all the attached and enabled network
+ * adapters are present on the host. This check makes sense
+ * in two cases only - when attachement type is Bridged Network
+ * or Host-only Interface. NOTE: Only currently enabled
+ * attachement type is checked (incorrect parameters check for
+ * currently disabled attachement types is skipped). */
+ QStringList failedInterfaceNames;
+ QStringList availableInterfaceNames;
+
+ /* Create host network interface names list */
+ foreach (const CHostNetworkInterface &iface, uiCommon().host().GetNetworkInterfaces())
+ {
+ availableInterfaceNames << iface.GetName();
+ availableInterfaceNames << iface.GetShortName();
+ }
+
+ ulong cCount = uiCommon().virtualBox().GetSystemProperties().GetMaxNetworkAdapters(machine().GetChipsetType());
+ for (ulong uAdapterIndex = 0; uAdapterIndex < cCount; ++uAdapterIndex)
+ {
+ CNetworkAdapter na = machine().GetNetworkAdapter(uAdapterIndex);
+
+ if (na.GetEnabled())
+ {
+ QString strIfName = QString();
+
+ /* Get physical network interface name for currently
+ * enabled network attachement type */
+ switch (na.GetAttachmentType())
+ {
+ case KNetworkAttachmentType_Bridged:
+ strIfName = na.GetBridgedInterface();
+ break;
+#ifndef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnly:
+ strIfName = na.GetHostOnlyInterface();
+ break;
+#endif /* !VBOX_WITH_VMNET */
+ default: break; /* Shut up, MSC! */
+ }
+
+ if (!strIfName.isEmpty() &&
+ !availableInterfaceNames.contains(strIfName))
+ {
+ LogFlow(("Found invalid network interface: %s\n", strIfName.toStdString().c_str()));
+ failedInterfaceNames << QString("%1 (adapter %2)").arg(strIfName).arg(uAdapterIndex + 1);
+ }
+ }
+ }
+
+ /* Check if non-existent interfaces found */
+ if (!failedInterfaceNames.isEmpty())
+ {
+ if (msgCenter().warnAboutNetworkInterfaceNotFound(machineName(), failedInterfaceNames.join(", ")))
+ machineLogic()->openNetworkSettingsDialog();
+ else
+ {
+ LogRel(("GUI: Aborting startup due to preprocess initialization issue detected...\n"));
+ return false;
+ }
+ }
+#endif /* VBOX_WITH_NETFLT */
+
+ /* Check for USB enumeration warning. Don't return false even if we have a warning: */
+ CHost comHost = uiCommon().host();
+ if (comHost.GetUSBDevices().isEmpty() && comHost.isWarning())
+ {
+ /* Do not bitch if USB disabled: */
+ if (!machine().GetUSBControllers().isEmpty())
+ {
+ /* Do not bitch if there are no filters (check if enabled too?): */
+ if (!machine().GetUSBDeviceFilters().GetDeviceFilters().isEmpty())
+ UINotificationMessage::cannotEnumerateHostUSBDevices(comHost);
+ }
+ }
+
+ /* True by default: */
+ return true;
+}
+
+bool UISession::mountAdHocImage(KDeviceType enmDeviceType, UIMediumDeviceType enmMediumType, const QString &strMediumName)
+{
+ /* Get VBox: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Prepare medium to mount: */
+ UIMedium guiMedium;
+
+ /* The 'none' medium name means ejecting what ever is in the drive,
+ * in that case => leave the guiMedium variable null. */
+ if (strMediumName != "none")
+ {
+ /* Open the medium: */
+ const CMedium comMedium = comVBox.OpenMedium(strMediumName, enmDeviceType, KAccessMode_ReadWrite, false /* fForceNewUuid */);
+ if (!comVBox.isOk() || comMedium.isNull())
+ {
+ UINotificationMessage::cannotOpenMedium(comVBox, strMediumName);
+ return false;
+ }
+
+ /* Make sure medium ID is valid: */
+ const QUuid uMediumId = comMedium.GetId();
+ AssertReturn(!uMediumId.isNull(), false);
+
+ /* Try to find UIMedium among cached: */
+ guiMedium = uiCommon().medium(uMediumId);
+ if (guiMedium.isNull())
+ {
+ /* Cache new one if necessary: */
+ guiMedium = UIMedium(comMedium, enmMediumType, KMediumState_Created);
+ uiCommon().createMedium(guiMedium);
+ }
+ }
+
+ /* Search for a suitable storage slots: */
+ QList<ExactStorageSlot> aFreeStorageSlots;
+ QList<ExactStorageSlot> aBusyStorageSlots;
+ foreach (const CStorageController &comController, machine().GetStorageControllers())
+ {
+ foreach (const CMediumAttachment &comAttachment, machine().GetMediumAttachmentsOfController(comController.GetName()))
+ {
+ /* Look for an optical devices only: */
+ if (comAttachment.GetType() == enmDeviceType)
+ {
+ /* Append storage slot to corresponding list: */
+ if (comAttachment.GetMedium().isNull())
+ aFreeStorageSlots << ExactStorageSlot(comController.GetName(), comController.GetBus(),
+ comAttachment.GetPort(), comAttachment.GetDevice());
+ else
+ aBusyStorageSlots << ExactStorageSlot(comController.GetName(), comController.GetBus(),
+ comAttachment.GetPort(), comAttachment.GetDevice());
+ }
+ }
+ }
+
+ /* Make sure at least one storage slot found: */
+ QList<ExactStorageSlot> sStorageSlots = aFreeStorageSlots + aBusyStorageSlots;
+ if (sStorageSlots.isEmpty())
+ {
+ UINotificationMessage::cannotMountImage(machineName(), strMediumName);
+ return false;
+ }
+
+ /* Try to mount medium into first available storage slot: */
+ while (!sStorageSlots.isEmpty())
+ {
+ const ExactStorageSlot storageSlot = sStorageSlots.takeFirst();
+ machine().MountMedium(storageSlot.controller, storageSlot.port, storageSlot.device, guiMedium.medium(), false /* force */);
+ if (machine().isOk())
+ break;
+ }
+
+ /* Show error message if necessary: */
+ if (!machine().isOk())
+ {
+ msgCenter().cannotRemountMedium(machine(), guiMedium, true /* mount? */, false /* retry? */, activeMachineWindow());
+ return false;
+ }
+
+ /* Save machine settings: */
+ machine().SaveSettings();
+
+ /* Show error message if necessary: */
+ if (!machine().isOk())
+ {
+ UINotificationMessage::cannotSaveMachineSettings(machine());
+ return false;
+ }
+
+ /* True by default: */
+ return true;
+}
+
+bool UISession::postprocessInitialization()
+{
+ /* There used to be some raw-mode warnings here for raw-mode incompatible
+ guests (64-bit ones and OS/2). Nothing to do at present. */
+ return true;
+}
+
+bool UISession::isScreenVisibleHostDesires(ulong uScreenId) const
+{
+ /* Make sure index feats the bounds: */
+ AssertReturn(uScreenId < (ulong)m_monitorVisibilityVectorHostDesires.size(), false);
+
+ /* Return 'actual' (host-desire) visibility status: */
+ return m_monitorVisibilityVectorHostDesires.value((int)uScreenId);
+}
+
+void UISession::setScreenVisibleHostDesires(ulong uScreenId, bool fIsMonitorVisible)
+{
+ /* Make sure index feats the bounds: */
+ AssertReturnVoid(uScreenId < (ulong)m_monitorVisibilityVectorHostDesires.size());
+
+ /* Remember 'actual' (host-desire) visibility status: */
+ m_monitorVisibilityVectorHostDesires[(int)uScreenId] = fIsMonitorVisible;
+
+ /* And remember the request in extra data for guests with VMSVGA: */
+ /* This should be done before the actual hint is sent in case the guest overrides it. */
+ gEDataManager->setLastGuestScreenVisibilityStatus(uScreenId, fIsMonitorVisible, uiCommon().managedVMUuid());
+}
+
+bool UISession::isScreenVisible(ulong uScreenId) const
+{
+ /* Make sure index feats the bounds: */
+ AssertReturn(uScreenId < (ulong)m_monitorVisibilityVector.size(), false);
+
+ /* Return 'actual' visibility status: */
+ return m_monitorVisibilityVector.value((int)uScreenId);
+}
+
+void UISession::setScreenVisible(ulong uScreenId, bool fIsMonitorVisible)
+{
+ /* Make sure index feats the bounds: */
+ AssertReturnVoid(uScreenId < (ulong)m_monitorVisibilityVector.size());
+
+ /* Remember 'actual' visibility status: */
+ m_monitorVisibilityVector[(int)uScreenId] = fIsMonitorVisible;
+ /* Remember 'desired' visibility status: */
+ /* See note in UIMachineView::sltHandleNotifyChange() regarding the graphics controller check. */
+ if (machine().GetGraphicsAdapter().GetGraphicsControllerType() != KGraphicsControllerType_VMSVGA)
+ gEDataManager->setLastGuestScreenVisibilityStatus(uScreenId, fIsMonitorVisible, uiCommon().managedVMUuid());
+
+ /* Make sure action-pool knows guest-screen visibility status: */
+ actionPool()->toRuntime()->setGuestScreenVisible(uScreenId, fIsMonitorVisible);
+}
+
+QSize UISession::lastFullScreenSize(ulong uScreenId) const
+{
+ /* Make sure index fits the bounds: */
+ AssertReturn(uScreenId < (ulong)m_monitorLastFullScreenSizeVector.size(), QSize(-1, -1));
+
+ /* Return last full-screen size: */
+ return m_monitorLastFullScreenSizeVector.value((int)uScreenId);
+}
+
+void UISession::setLastFullScreenSize(ulong uScreenId, QSize size)
+{
+ /* Make sure index fits the bounds: */
+ AssertReturnVoid(uScreenId < (ulong)m_monitorLastFullScreenSizeVector.size());
+
+ /* Remember last full-screen size: */
+ m_monitorLastFullScreenSizeVector[(int)uScreenId] = size;
+}
+
+int UISession::countOfVisibleWindows()
+{
+ int cCountOfVisibleWindows = 0;
+ for (int i = 0; i < m_monitorVisibilityVector.size(); ++i)
+ if (m_monitorVisibilityVector[i])
+ ++cCountOfVisibleWindows;
+ return cCountOfVisibleWindows;
+}
+
+QList<int> UISession::listOfVisibleWindows() const
+{
+ QList<int> visibleWindows;
+ for (int i = 0; i < m_monitorVisibilityVector.size(); ++i)
+ if (m_monitorVisibilityVector.at(i))
+ visibleWindows.push_back(i);
+ return visibleWindows;
+}
+
+CMediumVector UISession::machineMedia() const
+{
+ CMediumVector comMedia;
+ /* Enumerate all the controllers: */
+ foreach (const CStorageController &comController, m_machine.GetStorageControllers())
+ {
+ /* Enumerate all the attachments: */
+ foreach (const CMediumAttachment &comAttachment, m_machine.GetMediumAttachmentsOfController(comController.GetName()))
+ {
+ /* Skip unrelated device types: */
+ const KDeviceType enmDeviceType = comAttachment.GetType();
+ if ( enmDeviceType != KDeviceType_HardDisk
+ && enmDeviceType != KDeviceType_Floppy
+ && enmDeviceType != KDeviceType_DVD)
+ continue;
+ if ( comAttachment.GetIsEjected()
+ || comAttachment.GetMedium().isNull())
+ continue;
+ comMedia.append(comAttachment.GetMedium());
+ }
+ }
+ return comMedia;
+}
+
+void UISession::loadVMSettings()
+{
+ /* Cache IMachine::ExecutionEngine value. */
+ m_enmVMExecutionEngine = m_debugger.GetExecutionEngine();
+ /* Load nested-paging CPU hardware virtualization extension: */
+ m_fIsHWVirtExNestedPagingEnabled = m_debugger.GetHWVirtExNestedPagingEnabled();
+ /* Load whether the VM is currently making use of the unrestricted execution feature of VT-x: */
+ m_fIsHWVirtExUXEnabled = m_debugger.GetHWVirtExUXEnabled();
+ /* Load VM's effective paravirtualization provider: */
+ m_paraVirtProvider = m_machine.GetEffectiveParavirtProvider();
+}
+
+UIFrameBuffer* UISession::frameBuffer(ulong uScreenId) const
+{
+ Assert(uScreenId < (ulong)m_frameBufferVector.size());
+ return m_frameBufferVector.value((int)uScreenId, 0);
+}
+
+void UISession::setFrameBuffer(ulong uScreenId, UIFrameBuffer* pFrameBuffer)
+{
+ Assert(uScreenId < (ulong)m_frameBufferVector.size());
+ if (uScreenId < (ulong)m_frameBufferVector.size())
+ m_frameBufferVector[(int)uScreenId] = pFrameBuffer;
+}
+
+void UISession::updateHostScreenData()
+{
+ /* Rebuild host-screen data vector: */
+ m_hostScreens.clear();
+ for (int iScreenIndex = 0; iScreenIndex < UIDesktopWidgetWatchdog::screenCount(); ++iScreenIndex)
+ m_hostScreens << gpDesktop->screenGeometry(iScreenIndex);
+
+ /* Make sure action-pool knows host-screen count: */
+ actionPool()->toRuntime()->setHostScreenCount(m_hostScreens.size());
+}
+
+void UISession::updateActionRestrictions()
+{
+ /* Get host and prepare restrictions: */
+ const CHost host = uiCommon().host();
+ UIExtraDataMetaDefs::RuntimeMenuMachineActionType restrictionForMachine = UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Invalid;
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType restrictionForView = UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid;
+ UIExtraDataMetaDefs::RuntimeMenuDevicesActionType restrictionForDevices = UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Invalid;
+
+ /* Separate process stuff: */
+ {
+ /* Initialize 'Machine' menu: */
+ if (!uiCommon().isSeparateProcess())
+ restrictionForMachine = (UIExtraDataMetaDefs::RuntimeMenuMachineActionType)(restrictionForMachine | UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Detach);
+ }
+
+ /* VRDE server stuff: */
+ {
+ /* Initialize 'View' menu: */
+ const CVRDEServer server = machine().GetVRDEServer();
+ if (server.isNull())
+ restrictionForView = (UIExtraDataMetaDefs::RuntimeMenuViewActionType)(restrictionForView | UIExtraDataMetaDefs::RuntimeMenuViewActionType_VRDEServer);
+ }
+
+ /* Storage stuff: */
+ {
+ /* Initialize CD/FD menus: */
+ int iDevicesCountCD = 0;
+ int iDevicesCountFD = 0;
+ foreach (const CMediumAttachment &attachment, machine().GetMediumAttachments())
+ {
+ if (attachment.GetType() == KDeviceType_DVD)
+ ++iDevicesCountCD;
+ if (attachment.GetType() == KDeviceType_Floppy)
+ ++iDevicesCountFD;
+ }
+ QAction *pOpticalDevicesMenu = actionPool()->action(UIActionIndexRT_M_Devices_M_OpticalDevices);
+ QAction *pFloppyDevicesMenu = actionPool()->action(UIActionIndexRT_M_Devices_M_FloppyDevices);
+ pOpticalDevicesMenu->setData(iDevicesCountCD);
+ pFloppyDevicesMenu->setData(iDevicesCountFD);
+ if (!iDevicesCountCD)
+ restrictionForDevices = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)(restrictionForDevices | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_OpticalDevices);
+ if (!iDevicesCountFD)
+ restrictionForDevices = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)(restrictionForDevices | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_FloppyDevices);
+ }
+
+ /* Audio stuff: */
+ {
+ /* Check whether audio controller is enabled. */
+ const CAudioSettings comAudioSettings = machine().GetAudioSettings();
+ const CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ if (comAdapter.isNull() || !comAdapter.GetEnabled())
+ restrictionForDevices = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)(restrictionForDevices | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Audio);
+ }
+
+ /* Network stuff: */
+ {
+ /* Initialize Network menu: */
+ bool fAtLeastOneAdapterActive = false;
+ const KChipsetType chipsetType = machine().GetChipsetType();
+ ULONG uSlots = uiCommon().virtualBox().GetSystemProperties().GetMaxNetworkAdapters(chipsetType);
+ for (ULONG uSlot = 0; uSlot < uSlots; ++uSlot)
+ {
+ const CNetworkAdapter &adapter = machine().GetNetworkAdapter(uSlot);
+ if (adapter.GetEnabled())
+ {
+ fAtLeastOneAdapterActive = true;
+ break;
+ }
+ }
+ if (!fAtLeastOneAdapterActive)
+ restrictionForDevices = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)(restrictionForDevices | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Network);
+ }
+
+ /* USB stuff: */
+ {
+ /* Check whether there is at least one USB controller with an available proxy. */
+ const bool fUSBEnabled = !machine().GetUSBDeviceFilters().isNull()
+ && !machine().GetUSBControllers().isEmpty()
+ && machine().GetUSBProxyAvailable();
+ if (!fUSBEnabled)
+ restrictionForDevices = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)(restrictionForDevices | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_USBDevices);
+ }
+
+ /* WebCams stuff: */
+ {
+ /* Check whether there is an accessible video input devices pool: */
+ host.GetVideoInputDevices();
+ const bool fWebCamsEnabled = host.isOk() && !machine().GetUSBControllers().isEmpty();
+ if (!fWebCamsEnabled)
+ restrictionForDevices = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)(restrictionForDevices | UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_WebCams);
+ }
+
+ /* Apply cumulative restriction for 'Machine' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuMachine(UIActionRestrictionLevel_Session, restrictionForMachine);
+ /* Apply cumulative restriction for 'View' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Session, restrictionForView);
+ /* Apply cumulative restriction for 'Devices' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuDevices(UIActionRestrictionLevel_Session, restrictionForDevices);
+}
+
+bool UISession::guestAdditionsUpgradable()
+{
+ if (!machine().isOk())
+ return false;
+
+ /* Auto GA update is currently for Windows and Linux guests only */
+ const CGuestOSType osType = uiCommon().vmGuestOSType(machine().GetOSTypeId());
+ if (!osType.isOk())
+ return false;
+
+ const QString strGuestFamily = osType.GetFamilyId();
+ bool fIsWindowOrLinux = strGuestFamily.contains("windows", Qt::CaseInsensitive) || strGuestFamily.contains("linux", Qt::CaseInsensitive);
+
+ if (!fIsWindowOrLinux)
+ return false;
+
+ /* Also check whether we have something to update automatically: */
+ const ULONG ulGuestAdditionsRunLevel = guest().GetAdditionsRunLevel();
+ if (ulGuestAdditionsRunLevel < (ULONG)KAdditionsRunLevelType_Userland)
+ return false;
+
+ return true;
+}
+
+#ifdef VBOX_GUI_WITH_KEYS_RESET_HANDLER
+/**
+ * Custom signal handler. When switching VTs, we might not get release events
+ * for Ctrl-Alt and in case a savestate is performed on the new VT, the VM will
+ * be saved with modifier keys stuck. This is annoying enough for introducing
+ * this hack.
+ */
+/* static */
+static void signalHandlerSIGUSR1(int sig, siginfo_t * /* pInfo */, void * /*pSecret */)
+{
+ /* Only SIGUSR1 is interesting: */
+ if (sig == SIGUSR1)
+ if (gpMachine)
+ gpMachine->uisession()->machineLogic()->keyboardHandler()->releaseAllPressedKeys();
+}
+#endif /* VBOX_GUI_WITH_KEYS_RESET_HANDLER */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UISession.h b/src/VBox/Frontends/VirtualBox/src/runtime/UISession.h
new file mode 100644
index 00000000..65a00e2f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UISession.h
@@ -0,0 +1,632 @@
+/* $Id: UISession.h $ */
+/** @file
+ * VBox Qt GUI - UISession class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UISession_h
+#define FEQT_INCLUDED_SRC_runtime_UISession_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+#include <QCursor>
+#include <QEvent>
+#include <QMap>
+#include <QPixmap>
+
+/* GUI includes: */
+#include "UIExtraDataDefs.h"
+#include "UIMediumDefs.h"
+#include "UIMousePointerShapeData.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CSession.h"
+#include "CMachine.h"
+#include "CConsole.h"
+#include "CDisplay.h"
+#include "CGuest.h"
+#include "CMouse.h"
+#include "CKeyboard.h"
+#include "CMachineDebugger.h"
+#include "CMedium.h"
+
+/* Forward declarations: */
+class QMenu;
+class UIFrameBuffer;
+class UIMachine;
+class UIMachineLogic;
+class UIMachineWindow;
+class UIActionPool;
+class CUSBDevice;
+class CNetworkAdapter;
+class CMediumAttachment;
+#ifdef VBOX_WS_MAC
+class QMenuBar;
+#else /* !VBOX_WS_MAC */
+class QIcon;
+#endif /* !VBOX_WS_MAC */
+
+class UISession : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Factory constructor. */
+ static bool create(UISession *&pSession, UIMachine *pMachine);
+ /** Factory destructor. */
+ static void destroy(UISession *&pSession);
+
+ /* API: Runtime UI stuff: */
+ bool initialize();
+ /** Powers VM up. */
+ bool powerUp();
+ /** Detaches and closes Runtime UI. */
+ void detachUi();
+ /** Saves VM state, then closes Runtime UI. */
+ void saveState();
+ /** Calls for guest shutdown to close Runtime UI. */
+ void shutdown();
+ /** Powers VM off, then closes Runtime UI. */
+ void powerOff(bool fIncludingDiscard);
+
+ /** Returns the session instance. */
+ CSession& session() { return m_session; }
+ /** Returns the session's machine instance. */
+ CMachine& machine() { return m_machine; }
+ /** Returns the session's console instance. */
+ CConsole& console() { return m_console; }
+ /** Returns the console's display instance. */
+ CDisplay& display() { return m_display; }
+ /** Returns the console's guest instance. */
+ CGuest& guest() { return m_guest; }
+ /** Returns the console's mouse instance. */
+ CMouse& mouse() { return m_mouse; }
+ /** Returns the console's keyboard instance. */
+ CKeyboard& keyboard() { return m_keyboard; }
+ /** Returns the console's debugger instance. */
+ CMachineDebugger& debugger() { return m_debugger; }
+
+ /** Returns the machine name. */
+ const QString& machineName() const { return m_strMachineName; }
+
+ UIActionPool* actionPool() const { return m_pActionPool; }
+ KMachineState machineStatePrevious() const { return m_machineStatePrevious; }
+ KMachineState machineState() const { return m_machineState; }
+ UIMachineLogic* machineLogic() const;
+ QWidget* mainMachineWindow() const;
+ WId mainMachineWindowId() const;
+ UIMachineWindow *activeMachineWindow() const;
+
+ /** Returns currently cached mouse cursor shape pixmap. */
+ QPixmap cursorShapePixmap() const { return m_cursorShapePixmap; }
+ /** Returns currently cached mouse cursor mask pixmap. */
+ QPixmap cursorMaskPixmap() const { return m_cursorMaskPixmap; }
+ /** Returns currently cached mouse cursor size. */
+ QSize cursorSize() const { return m_cursorSize; }
+ /** Returns currently cached mouse cursor hotspot. */
+ QPoint cursorHotspot() const { return m_cursorHotspot; }
+ /** Returns currently cached mouse cursor position. */
+ QPoint cursorPosition() const { return m_cursorPosition; }
+
+ /** @name Branding stuff.
+ ** @{ */
+ /** Returns the cached machine-window icon. */
+ QIcon *machineWindowIcon() const { return m_pMachineWindowIcon; }
+#ifndef VBOX_WS_MAC
+ /** Returns redefined machine-window name postfix. */
+ QString machineWindowNamePostfix() const { return m_strMachineWindowNamePostfix; }
+#endif
+ /** @} */
+
+ /** @name Host-screen configuration variables.
+ ** @{ */
+ /** Returns the list of host-screen geometries we currently have. */
+ QList<QRect> hostScreens() const { return m_hostScreens; }
+ /** @} */
+
+ /** @name Application Close configuration stuff.
+ * @{ */
+ /** Defines @a defaultCloseAction. */
+ void setDefaultCloseAction(MachineCloseAction defaultCloseAction) { m_defaultCloseAction = defaultCloseAction; }
+ /** Returns default close action. */
+ MachineCloseAction defaultCloseAction() const { return m_defaultCloseAction; }
+ /** Returns merged restricted close actions. */
+ MachineCloseAction restrictedCloseActions() const { return m_restrictedCloseActions; }
+ /** Returns whether all the close actions are restricted. */
+ bool isAllCloseActionsRestricted() const { return m_fAllCloseActionsRestricted; }
+ /** @} */
+
+ /** Returns whether visual @a state is allowed. */
+ bool isVisualStateAllowed(UIVisualStateType state) const;
+ /** Requests visual-state change. */
+ void changeVisualState(UIVisualStateType visualStateType);
+ /** Requests visual-state to be entered when possible. */
+ void setRequestedVisualState(UIVisualStateType visualStateType);
+ /** Returns requested visual-state to be entered when possible. */
+ UIVisualStateType requestedVisualState() const;
+
+ bool isSaved() const { return machineState() == KMachineState_Saved ||
+ machineState() == KMachineState_AbortedSaved; }
+ bool isTurnedOff() const { return machineState() == KMachineState_PoweredOff ||
+ machineState() == KMachineState_Saved ||
+ machineState() == KMachineState_Teleported ||
+ machineState() == KMachineState_Aborted ||
+ machineState() == KMachineState_AbortedSaved; }
+ bool isPaused() const { return machineState() == KMachineState_Paused ||
+ machineState() == KMachineState_TeleportingPausedVM; }
+ bool isRunning() const { return machineState() == KMachineState_Running ||
+ machineState() == KMachineState_Teleporting ||
+ machineState() == KMachineState_LiveSnapshotting; }
+ bool isStuck() const { return machineState() == KMachineState_Stuck; }
+ bool wasPaused() const { return machineStatePrevious() == KMachineState_Paused ||
+ machineStatePrevious() == KMachineState_TeleportingPausedVM; }
+ bool isInitialized() const { return m_fInitialized; }
+ bool isGuestResizeIgnored() const { return m_fIsGuestResizeIgnored; }
+ bool isAutoCaptureDisabled() const { return m_fIsAutoCaptureDisabled; }
+
+ /** Returns whether VM is in 'manual-override' mode.
+ * @note S.a. #m_fIsManualOverride description for more information. */
+ bool isManualOverrideMode() const { return m_fIsManualOverride; }
+ /** Defines whether VM is in 'manual-override' mode.
+ * @note S.a. #m_fIsManualOverride description for more information. */
+ void setManualOverrideMode(bool fIsManualOverride) { m_fIsManualOverride = fIsManualOverride; }
+
+ /* Guest additions state getters: */
+ bool isGuestAdditionsActive() const { return (m_ulGuestAdditionsRunLevel > KAdditionsRunLevelType_None); }
+ bool isGuestSupportsGraphics() const { return m_fIsGuestSupportsGraphics; }
+ /* The double check below is correct, even though it is an implementation
+ * detail of the Additions which the GUI should not ideally have to know. */
+ bool isGuestSupportsSeamless() const { return isGuestSupportsGraphics() && m_fIsGuestSupportsSeamless; }
+
+ /* Keyboard getters: */
+ /** Returns keyboard-state. */
+ int keyboardState() const { return m_iKeyboardState; }
+ bool isNumLock() const { return m_fNumLock; }
+ bool isCapsLock() const { return m_fCapsLock; }
+ bool isScrollLock() const { return m_fScrollLock; }
+ uint numLockAdaptionCnt() const { return m_uNumLockAdaptionCnt; }
+ uint capsLockAdaptionCnt() const { return m_uCapsLockAdaptionCnt; }
+
+ /* Mouse getters: */
+ /** Returns mouse-state. */
+ int mouseState() const { return m_iMouseState; }
+ bool isMouseSupportsAbsolute() const { return m_fIsMouseSupportsAbsolute; }
+ bool isMouseSupportsRelative() const { return m_fIsMouseSupportsRelative; }
+ bool isMouseSupportsTouchScreen() const { return m_fIsMouseSupportsTouchScreen; }
+ bool isMouseSupportsTouchPad() const { return m_fIsMouseSupportsTouchPad; }
+ bool isMouseHostCursorNeeded() const { return m_fIsMouseHostCursorNeeded; }
+ bool isMouseCaptured() const { return m_fIsMouseCaptured; }
+ bool isMouseIntegrated() const { return m_fIsMouseIntegrated; }
+ bool isValidPointerShapePresent() const { return m_fIsValidPointerShapePresent; }
+ bool isHidingHostPointer() const { return m_fIsHidingHostPointer; }
+ /** Returns whether the @a cursorPosition() is valid and could be used by the GUI now. */
+ bool isValidCursorPositionPresent() const { return m_fIsValidCursorPositionPresent; }
+
+ /* Common setters: */
+ bool pause() { return setPause(true); }
+ bool unpause() { return setPause(false); }
+ bool setPause(bool fOn);
+ void setGuestResizeIgnored(bool fIsGuestResizeIgnored) { m_fIsGuestResizeIgnored = fIsGuestResizeIgnored; }
+ void setAutoCaptureDisabled(bool fIsAutoCaptureDisabled) { m_fIsAutoCaptureDisabled = fIsAutoCaptureDisabled; }
+ void forgetPreviousMachineState() { m_machineStatePrevious = m_machineState; }
+
+ /* Keyboard setters: */
+ void setNumLockAdaptionCnt(uint uNumLockAdaptionCnt) { m_uNumLockAdaptionCnt = uNumLockAdaptionCnt; }
+ void setCapsLockAdaptionCnt(uint uCapsLockAdaptionCnt) { m_uCapsLockAdaptionCnt = uCapsLockAdaptionCnt; }
+
+ /* Mouse setters: */
+ void setMouseCaptured(bool fIsMouseCaptured) { m_fIsMouseCaptured = fIsMouseCaptured; }
+ void setMouseIntegrated(bool fIsMouseIntegrated) { m_fIsMouseIntegrated = fIsMouseIntegrated; }
+
+ /* Screen visibility status for host-desires: */
+ bool isScreenVisibleHostDesires(ulong uScreenId) const;
+ void setScreenVisibleHostDesires(ulong uScreenId, bool fIsMonitorVisible);
+
+ /* Screen visibility status: */
+ bool isScreenVisible(ulong uScreenId) const;
+ void setScreenVisible(ulong uScreenId, bool fIsMonitorVisible);
+
+ /* Last screen full-screen size: */
+ QSize lastFullScreenSize(ulong uScreenId) const;
+ void setLastFullScreenSize(ulong uScreenId, QSize size);
+
+ /** Returns whether guest-screen is undrawable.
+ * @todo: extend this method to all the states when guest-screen is undrawable. */
+ bool isGuestScreenUnDrawable() const { return machineState() == KMachineState_Stopping ||
+ machineState() == KMachineState_Saving; }
+
+ /* Returns existing framebuffer for the given screen-number;
+ * Returns 0 (asserts) if screen-number attribute is out of bounds: */
+ UIFrameBuffer* frameBuffer(ulong uScreenId) const;
+ /* Sets framebuffer for the given screen-number;
+ * Ignores (asserts) if screen-number attribute is out of bounds: */
+ void setFrameBuffer(ulong uScreenId, UIFrameBuffer* pFrameBuffer);
+ /** Returns existing frame-buffer vector. */
+ const QVector<UIFrameBuffer*>& frameBuffers() const { return m_frameBufferVector; }
+
+ /** Updates VRDE Server action state. */
+ void updateStatusVRDE() { sltVRDEChange(); }
+ /** Updates Recording action state. */
+ void updateStatusRecording() { sltRecordingChange(); }
+ /** Updates Audio output action state. */
+ void updateAudioOutput() { sltAudioAdapterChange(); }
+ /** Updates Audio input action state. */
+ void updateAudioInput() { sltAudioAdapterChange(); }
+
+ /** @name CPU hardware virtualization features for VM.
+ ** @{ */
+ /** Returns whether CPU hardware virtualization extension is enabled. */
+ KVMExecutionEngine getVMExecutionEngine() const { return m_enmVMExecutionEngine; }
+ /** Returns whether nested-paging CPU hardware virtualization extension is enabled. */
+ bool isHWVirtExNestedPagingEnabled() const { return m_fIsHWVirtExNestedPagingEnabled; }
+ /** Returns whether the VM is currently making use of the unrestricted execution feature of VT-x. */
+ bool isHWVirtExUXEnabled() const { return m_fIsHWVirtExUXEnabled; }
+ /** @} */
+
+ /** Returns VM's effective paravirtualization provider. */
+ KParavirtProvider paraVirtProvider() const { return m_paraVirtProvider; }
+
+ /** Returns the list of visible guest windows. */
+ QList<int> listOfVisibleWindows() const;
+
+ /** Returns a vector of media attached to the machine. */
+ CMediumVector machineMedia() const;
+
+signals:
+
+ /** Notifies about frame-buffer resize. */
+ void sigFrameBufferResize();
+
+ /* Console callback signals: */
+ /** Notifies listeners about keyboard state-change. */
+ void sigKeyboardStateChange(int iState);
+ /** Notifies listeners about mouse state-change. */
+ void sigMouseStateChange(int iState);
+ /** Notifies listeners about mouse pointer shape change. */
+ void sigMousePointerShapeChange();
+ /** Notifies listeners about mouse capability change. */
+ void sigMouseCapabilityChange();
+ /** Notifies listeners about cursor position change. */
+ void sigCursorPositionChange();
+ void sigKeyboardLedsChange();
+ void sigMachineStateChange();
+ void sigAdditionsStateChange();
+ void sigAdditionsStateActualChange();
+ void sigNetworkAdapterChange(const CNetworkAdapter &networkAdapter);
+ /** Notifies about storage device change for @a attachment, which was @a fRemoved and it was @a fSilent for guest. */
+ void sigStorageDeviceChange(const CMediumAttachment &attachment, bool fRemoved, bool fSilent);
+ void sigMediumChange(const CMediumAttachment &mediumAttachment);
+ void sigVRDEChange();
+ void sigRecordingChange();
+ void sigUSBControllerChange();
+ void sigUSBDeviceStateChange(const CUSBDevice &device, bool bIsAttached, const CVirtualBoxErrorInfo &error);
+ void sigSharedFolderChange();
+ void sigRuntimeError(bool bIsFatal, const QString &strErrorId, const QString &strMessage);
+#ifdef RT_OS_DARWIN
+ void sigShowWindows();
+#endif /* RT_OS_DARWIN */
+ void sigCPUExecutionCapChange();
+ void sigGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo);
+ void sigAudioAdapterChange();
+ void sigClipboardModeChange(KClipboardMode enmMode);
+ void sigDnDModeChange(KDnDMode enmMode);
+
+ /** Notifies about host-screen count change. */
+ void sigHostScreenCountChange();
+ /** Notifies about host-screen geometry change. */
+ void sigHostScreenGeometryChange();
+ /** Notifies about host-screen available-area change. */
+ void sigHostScreenAvailableAreaChange();
+
+ /* Session signals: */
+ void sigInitialized();
+
+public slots:
+
+ /** Handles request to install guest additions image.
+ * @param strSource Brings the source of image being installed. */
+ void sltInstallGuestAdditionsFrom(const QString &strSource);
+ /** Mounts DVD adhoc.
+ * @param strSource Brings the source of image being mounted. */
+ void sltMountDVDAdHoc(const QString &strSource);
+
+ /** Defines @a iKeyboardState. */
+ void setKeyboardState(int iKeyboardState) { m_iKeyboardState = iKeyboardState; emit sigKeyboardStateChange(m_iKeyboardState); }
+
+ /** Defines @a iMouseState. */
+ void setMouseState(int iMouseState) { m_iMouseState = iMouseState; emit sigMouseStateChange(m_iMouseState); }
+
+ /** Closes Runtime UI. */
+ void closeRuntimeUI();
+
+private slots:
+
+ /** Detaches COM. */
+ void sltDetachCOM();
+
+#ifdef RT_OS_DARWIN
+ /** Mac OS X: Handles menu-bar configuration-change. */
+ void sltHandleMenuBarConfigurationChange(const QUuid &uMachineID);
+#endif /* RT_OS_DARWIN */
+
+ /* Console events slots */
+ /** Handles signal about mouse pointer @a shapeData change. */
+ void sltMousePointerShapeChange(const UIMousePointerShapeData &shapeData);
+ /** Handles signal about mouse capability change to @a fSupportsAbsolute, @a fSupportsRelative,
+ * @a fSupportsTouchScreen, @a fSupportsTouchPad and @a fNeedsHostCursor. */
+ void sltMouseCapabilityChange(bool fSupportsAbsolute, bool fSupportsRelative,
+ bool fSupportsTouchScreen, bool fSupportsTouchPad,
+ bool fNeedsHostCursor);
+ /** Handles signal about guest request to change the cursor position to @a uX * @a uY.
+ * @param fContainsData Brings whether the @a uX and @a uY values are valid and could be used by the GUI now. */
+ void sltCursorPositionChange(bool fContainsData, unsigned long uX, unsigned long uY);
+ void sltKeyboardLedsChangeEvent(bool fNumLock, bool fCapsLock, bool fScrollLock);
+ void sltStateChange(KMachineState state);
+ void sltAdditionsChange();
+ void sltVRDEChange();
+ void sltRecordingChange();
+ void sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo);
+ /** Handles storage device change for @a attachment, which was @a fRemoved and it was @a fSilent for guest. */
+ void sltHandleStorageDeviceChange(const CMediumAttachment &attachment, bool fRemoved, bool fSilent);
+ /** Handles audio adapter change. */
+ void sltAudioAdapterChange();
+ /** Handles clip board mode change. */
+ void sltClipboardModeChange(KClipboardMode enmMode);
+ /** Handles drag and drop mode change. */
+ void sltDnDModeChange(KDnDMode enmMode);
+
+ /* Handlers: Display reconfiguration stuff: */
+#ifdef RT_OS_DARWIN
+ void sltHandleHostDisplayAboutToChange();
+ void sltCheckIfHostDisplayChanged();
+#endif /* RT_OS_DARWIN */
+
+ /** Handles host-screen count change. */
+ void sltHandleHostScreenCountChange();
+ /** Handles host-screen geometry change. */
+ void sltHandleHostScreenGeometryChange();
+ /** Handles host-screen available-area change. */
+ void sltHandleHostScreenAvailableAreaChange();
+
+ /** Handles signal about machine state saved.
+ * @param fSuccess Brings whether state was saved successfully. */
+ void sltHandleMachineStateSaved(bool fSuccess);
+ /** Handles signal about machine powered off.
+ * @param fSuccess Brings whether machine was powered off successfully.
+ * @param fIncludingDiscard Brings whether machine state should be discarded. */
+ void sltHandleMachinePoweredOff(bool fSuccess, bool fIncludingDiscard);
+ /** Handles signal about snapshot restored.
+ * @param fSuccess Brings whether machine was powered off successfully. */
+ void sltHandleSnapshotRestored(bool fSuccess);
+
+private:
+
+ /** Constructor. */
+ UISession(UIMachine *pMachine);
+ /** Destructor. */
+ ~UISession();
+
+ /* Private getters: */
+ UIMachine* uimachine() const { return m_pMachine; }
+
+ /* Prepare helpers: */
+ bool prepare();
+ bool prepareSession();
+ void prepareNotificationCenter();
+ void prepareConsoleEventHandlers();
+ void prepareFramebuffers();
+ void prepareActions();
+ void prepareConnections();
+ void prepareMachineWindowIcon();
+ void prepareScreens();
+ void prepareSignalHandling();
+
+ /* Settings stuff: */
+ void loadSessionSettings();
+
+ /* Cleanup helpers: */
+ //void cleanupSignalHandling();
+ //void cleanupScreens() {}
+ void cleanupMachineWindowIcon();
+ void cleanupConnections();
+ void cleanupActions();
+ void cleanupFramebuffers();
+ void cleanupConsoleEventHandlers();
+ void cleanupNotificationCenter();
+ void cleanupSession();
+ void cleanup();
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Updates menu-bar content. */
+ void updateMenu();
+#endif /* VBOX_WS_MAC */
+
+ /** Updates mouse pointer shape. */
+ void updateMousePointerShape();
+
+ /* Common helpers: */
+ bool preprocessInitialization();
+ bool mountAdHocImage(KDeviceType enmDeviceType, UIMediumDeviceType enmMediumType, const QString &strMediumName);
+ bool postprocessInitialization();
+ int countOfVisibleWindows();
+ /** Loads VM settings. */
+ void loadVMSettings();
+
+ /** Update host-screen data. */
+ void updateHostScreenData();
+
+ /** Updates action restrictions. */
+ void updateActionRestrictions();
+
+ /* Check if GA can be upgraded. */
+ bool guestAdditionsUpgradable();
+ /* Private variables: */
+ UIMachine *m_pMachine;
+
+ /** Holds the session instance. */
+ CSession m_session;
+ /** Holds the session's machine instance. */
+ CMachine m_machine;
+ /** Holds the session's console instance. */
+ CConsole m_console;
+ /** Holds the console's display instance. */
+ CDisplay m_display;
+ /** Holds the console's guest instance. */
+ CGuest m_guest;
+ /** Holds the console's mouse instance. */
+ CMouse m_mouse;
+ /** Holds the console's keyboard instance. */
+ CKeyboard m_keyboard;
+ /** Holds the console's debugger instance. */
+ CMachineDebugger m_debugger;
+
+ /** Holds the machine name. */
+ QString m_strMachineName;
+
+ /** Holds the action-pool instance. */
+ UIActionPool *m_pActionPool;
+
+#ifdef VBOX_WS_MAC
+ /** Holds the menu-bar instance. */
+ QMenuBar *m_pMenuBar;
+#endif /* VBOX_WS_MAC */
+
+ /* Screen visibility vector: */
+ QVector<bool> m_monitorVisibilityVector;
+
+ /* Screen visibility vector for host-desires: */
+ QVector<bool> m_monitorVisibilityVectorHostDesires;
+
+ /* Screen last full-screen size vector: */
+ QVector<QSize> m_monitorLastFullScreenSizeVector;
+
+ /* Frame-buffers vector: */
+ QVector<UIFrameBuffer*> m_frameBufferVector;
+
+ /* Common variables: */
+ KMachineState m_machineStatePrevious;
+ KMachineState m_machineState;
+
+ /** Holds cached mouse cursor shape pixmap. */
+ QPixmap m_cursorShapePixmap;
+ /** Holds cached mouse cursor mask pixmap. */
+ QPixmap m_cursorMaskPixmap;
+ /** Holds cached mouse cursor size. */
+ QSize m_cursorSize;
+ /** Holds cached mouse cursor hotspot. */
+ QPoint m_cursorHotspot;
+ /** Holds cached mouse cursor position. */
+ QPoint m_cursorPosition;
+
+ /** @name Branding variables.
+ ** @{ */
+ /** Holds the cached machine-window icon. */
+ QIcon *m_pMachineWindowIcon;
+#ifndef VBOX_WS_MAC
+ /** Holds redefined machine-window name postfix. */
+ QString m_strMachineWindowNamePostfix;
+#endif
+ /** @} */
+
+ /** @name Host-screen configuration variables.
+ * @{ */
+ /** Holds the list of host-screen geometries we currently have. */
+ QList<QRect> m_hostScreens;
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Watchdog timer looking for display reconfiguration. */
+ QTimer *m_pWatchdogDisplayChange;
+#endif /* VBOX_WS_MAC */
+ /** @} */
+
+ /** @name Application Close configuration variables.
+ * @{ */
+ /** Default close action. */
+ MachineCloseAction m_defaultCloseAction;
+ /** Merged restricted close actions. */
+ MachineCloseAction m_restrictedCloseActions;
+ /** Determines whether all the close actions are restricted. */
+ bool m_fAllCloseActionsRestricted;
+ /** @} */
+
+ /* Common flags: */
+ bool m_fInitialized : 1;
+ bool m_fIsGuestResizeIgnored : 1;
+ bool m_fIsAutoCaptureDisabled : 1;
+ /** Holds whether VM is in 'manual-override' mode
+ * which means there will be no automatic UI shutdowns,
+ * visual representation mode changes and other stuff. */
+ bool m_fIsManualOverride : 1;
+
+ /* Guest additions flags: */
+ ULONG m_ulGuestAdditionsRunLevel;
+ bool m_fIsGuestSupportsGraphics : 1;
+ bool m_fIsGuestSupportsSeamless : 1;
+
+ /* Keyboard flags: */
+ /** Holds the keyboard-state. */
+ int m_iKeyboardState;
+ bool m_fNumLock : 1;
+ bool m_fCapsLock : 1;
+ bool m_fScrollLock : 1;
+ uint m_uNumLockAdaptionCnt;
+ uint m_uCapsLockAdaptionCnt;
+
+ /* Mouse flags: */
+ /** Holds the mouse-state. */
+ int m_iMouseState;
+ bool m_fIsMouseSupportsAbsolute : 1;
+ bool m_fIsMouseSupportsRelative : 1;
+ bool m_fIsMouseSupportsTouchScreen: 1;
+ bool m_fIsMouseSupportsTouchPad: 1;
+ bool m_fIsMouseHostCursorNeeded : 1;
+ bool m_fIsMouseCaptured : 1;
+ bool m_fIsMouseIntegrated : 1;
+ bool m_fIsValidPointerShapePresent : 1;
+ bool m_fIsHidingHostPointer : 1;
+ /** Holds whether the @a m_cursorPosition is valid and could be used by the GUI now. */
+ bool m_fIsValidCursorPositionPresent : 1;
+ /** Holds the mouse pointer shape data. */
+ UIMousePointerShapeData m_shapeData;
+
+ /** Copy of IMachineDebugger::ExecutionEngine */
+ KVMExecutionEngine m_enmVMExecutionEngine;
+
+ /** @name CPU hardware virtualization features for VM.
+ ** @{ */
+ /** Holds whether nested-paging CPU hardware virtualization extension is enabled. */
+ bool m_fIsHWVirtExNestedPagingEnabled;
+ /** Holds whether the VM is currently making use of the unrestricted execution feature of VT-x. */
+ bool m_fIsHWVirtExUXEnabled;
+ /** @} */
+
+ /** Holds VM's effective paravirtualization provider. */
+ KParavirtProvider m_paraVirtProvider;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UISession_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIVMCloseDialog.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/UIVMCloseDialog.cpp
new file mode 100644
index 00000000..b5268555
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIVMCloseDialog.cpp
@@ -0,0 +1,643 @@
+/* $Id: UIVMCloseDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMCloseDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIIconPool.h"
+#include "UIVMCloseDialog.h"
+#include "UIExtraDataManager.h"
+#include "UIMessageCenter.h"
+#include "UIConverter.h"
+#include "UICommon.h"
+#include "QIDialogButtonBox.h"
+
+/* COM includes: */
+#include "CMachine.h"
+#include "CSession.h"
+#include "CConsole.h"
+#include "CSnapshot.h"
+
+
+UIVMCloseDialog::UIVMCloseDialog(QWidget *pParent, CMachine &comMachine,
+ bool fIsACPIEnabled, MachineCloseAction restictedCloseActions)
+ : QIWithRetranslateUI<QIDialog>(pParent)
+ , m_comMachine(comMachine)
+ , m_fIsACPIEnabled(fIsACPIEnabled)
+ , m_restictedCloseActions(restictedCloseActions)
+ , m_fValid(false)
+ , m_pMainLayout(0)
+ , m_pTopLayout(0)
+ , m_pTopLeftLayout(0)
+ , m_pTopRightLayout(0)
+ , m_pChoiceLayout(0)
+ , m_pLabelIcon(0), m_pLabelText(0)
+ , m_pLabelIconDetach(0), m_pRadioButtonDetach(0)
+ , m_pLabelIconSave(0), m_pRadioButtonSave(0)
+ , m_pLabelIconShutdown(0), m_pRadioButtonShutdown(0)
+ , m_pLabelIconPowerOff(0), m_pRadioButtonPowerOff(0)
+ , m_pCheckBoxDiscard(0)
+ , m_enmLastCloseAction(MachineCloseAction_Invalid)
+{
+ prepare();
+}
+
+void UIVMCloseDialog::setIcon(const QIcon &icon)
+{
+ /* Make sure icon is valid: */
+ if (icon.isNull())
+ return;
+
+ /* Remember it: */
+ m_icon = icon;
+ /* Update pixmaps: */
+ updatePixmaps();
+}
+
+bool UIVMCloseDialog::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Handle events realted to our radio-buttons only: */
+ if ( pObject != m_pRadioButtonDetach
+ && pObject != m_pRadioButtonSave
+ && pObject != m_pRadioButtonShutdown
+ && pObject != m_pRadioButtonPowerOff)
+ return QIWithRetranslateUI<QIDialog>::eventFilter(pObject, pEvent);
+
+ /* For now we are interested in double-click events only: */
+ if (pEvent->type() == QEvent::MouseButtonDblClick)
+ {
+ /* Make sure it's one of the radio-buttons
+ * which has this event-filter installed: */
+ if (qobject_cast<QRadioButton*>(pObject))
+ {
+ /* Since on double-click the button will be also selected
+ * we are just calling for the *accept* slot: */
+ accept();
+ }
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QIDialog>::eventFilter(pObject, pEvent);
+}
+
+bool UIVMCloseDialog::event(QEvent *pEvent)
+{
+ /* Pre-process in base-class: */
+ const bool fResult = QIWithRetranslateUI<QIDialog>::event(pEvent);
+
+ /* Post-process know event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::ScreenChangeInternal:
+ {
+ /* Update pixmaps: */
+ updatePixmaps();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Return pre-processed result: */
+ return fResult;
+}
+
+void UIVMCloseDialog::showEvent(QShowEvent *pEvent)
+{
+ /* Update pixmaps: */
+ updatePixmaps();
+
+ /* Call to base-class: */
+ QIWithRetranslateUI<QIDialog>::showEvent(pEvent);
+}
+
+void UIVMCloseDialog::retranslateUi()
+{
+ /* Translate title: */
+ setWindowTitle(tr("Close Virtual Machine"));
+
+ /* Translate text label: */
+ m_pLabelText->setText(tr("You want to:"));
+
+ /* Translate radio-buttons: */
+ m_pRadioButtonDetach->setText(tr("&Continue running in the background"));
+ m_pRadioButtonDetach->setWhatsThis(tr("<p>Close the virtual machine windows but keep the virtual machine running.</p>"
+ "<p>You can use the VirtualBox Manager to return to running the virtual machine "
+ "in a window.</p>"));
+ m_pRadioButtonSave->setText(tr("&Save the machine state"));
+ m_pRadioButtonSave->setWhatsThis(tr("<p>Saves the current execution state of the virtual machine to the physical hard disk "
+ "of the host PC.</p>"
+ "<p>Next time this machine is started, it will be restored from the saved state and "
+ "continue execution from the same place you saved it at, which will let you continue "
+ "your work immediately.</p>"
+ "<p>Note that saving the machine state may take a long time, depending on the guest "
+ "operating system type and the amount of memory you assigned to the virtual "
+ "machine.</p>"));
+ m_pRadioButtonShutdown->setText(tr("S&end the shutdown signal"));
+ m_pRadioButtonShutdown->setWhatsThis(tr("<p>Sends the ACPI Power Button press event to the virtual machine.</p>"
+ "<p>Normally, the guest operating system running inside the virtual machine will "
+ "detect this event and perform a clean shutdown procedure. This is a recommended "
+ "way to turn off the virtual machine because all applications running inside it "
+ "will get a chance to save their data and state.</p>"
+ "<p>If the machine doesn't respond to this action then the guest operating system "
+ "may be misconfigured or doesn't understand ACPI Power Button events at all. In "
+ "this case you should select the <b>Power off the machine</b> action to stop "
+ "virtual machine execution.</p>"));
+ m_pRadioButtonPowerOff->setText(tr("&Power off the machine"));
+ m_pRadioButtonPowerOff->setWhatsThis(tr("<p>Turns off the virtual machine.</p>"
+ "<p>Note that this action will stop machine execution immediately so that the guest "
+ "operating system running inside it will not be able to perform a clean shutdown "
+ "procedure which may result in <i>data loss</i> inside the virtual machine. "
+ "Selecting this action is recommended only if the virtual machine does not respond "
+ "to the <b>Send the shutdown signal</b> action.</p>"));
+
+ /* Translate check-box: */
+ m_pCheckBoxDiscard->setText(tr("&Restore current snapshot '%1'").arg(m_strDiscardCheckBoxText));
+ m_pCheckBoxDiscard->setWhatsThis(tr("<p>When checked, the machine will be returned to the state stored in the current "
+ "snapshot after it is turned off. This is useful if you are sure that you want to "
+ "discard the results of your last sessions and start again at that snapshot.</p>"));
+}
+
+void UIVMCloseDialog::sltUpdateWidgetAvailability()
+{
+ /* Discard option should be enabled only on power-off action: */
+ m_pCheckBoxDiscard->setEnabled(m_pRadioButtonPowerOff->isChecked());
+}
+
+void UIVMCloseDialog::accept()
+{
+ /* Calculate result: */
+ if (m_pRadioButtonDetach->isChecked())
+ setResult(MachineCloseAction_Detach);
+ else if (m_pRadioButtonSave->isChecked())
+ setResult(MachineCloseAction_SaveState);
+ else if (m_pRadioButtonShutdown->isChecked())
+ setResult(MachineCloseAction_Shutdown);
+ else if (m_pRadioButtonPowerOff->isChecked())
+ {
+ if (!m_pCheckBoxDiscard->isChecked() || !m_pCheckBoxDiscard->isVisible())
+ setResult(MachineCloseAction_PowerOff);
+ else
+ setResult(MachineCloseAction_PowerOff_RestoringSnapshot);
+ }
+
+ /* Memorize the last user's choice for the given VM: */
+ MachineCloseAction newCloseAction = static_cast<MachineCloseAction>(result());
+ /* But make sure 'Shutdown' is preserved if temporary unavailable: */
+ if (newCloseAction == MachineCloseAction_PowerOff &&
+ m_enmLastCloseAction == MachineCloseAction_Shutdown && !m_fIsACPIEnabled)
+ newCloseAction = MachineCloseAction_Shutdown;
+ gEDataManager->setLastMachineCloseAction(newCloseAction, uiCommon().managedVMUuid());
+
+ /* Hide the dialog: */
+ hide();
+}
+
+void UIVMCloseDialog::setButtonEnabledDetach(bool fEnabled)
+{
+ m_pLabelIconDetach->setEnabled(fEnabled);
+ m_pRadioButtonDetach->setEnabled(fEnabled);
+}
+
+void UIVMCloseDialog::setButtonVisibleDetach(bool fVisible)
+{
+ m_pLabelIconDetach->setVisible(fVisible);
+ m_pRadioButtonDetach->setVisible(fVisible);
+}
+
+void UIVMCloseDialog::setButtonEnabledSave(bool fEnabled)
+{
+ m_pLabelIconSave->setEnabled(fEnabled);
+ m_pRadioButtonSave->setEnabled(fEnabled);
+}
+
+void UIVMCloseDialog::setButtonVisibleSave(bool fVisible)
+{
+ m_pLabelIconSave->setVisible(fVisible);
+ m_pRadioButtonSave->setVisible(fVisible);
+}
+
+void UIVMCloseDialog::setButtonEnabledShutdown(bool fEnabled)
+{
+ m_pLabelIconShutdown->setEnabled(fEnabled);
+ m_pRadioButtonShutdown->setEnabled(fEnabled);
+}
+
+void UIVMCloseDialog::setButtonVisibleShutdown(bool fVisible)
+{
+ m_pLabelIconShutdown->setVisible(fVisible);
+ m_pRadioButtonShutdown->setVisible(fVisible);
+}
+
+void UIVMCloseDialog::setButtonEnabledPowerOff(bool fEnabled)
+{
+ m_pLabelIconPowerOff->setEnabled(fEnabled);
+ m_pRadioButtonPowerOff->setEnabled(fEnabled);
+}
+
+void UIVMCloseDialog::setButtonVisiblePowerOff(bool fVisible)
+{
+ m_pLabelIconPowerOff->setVisible(fVisible);
+ m_pRadioButtonPowerOff->setVisible(fVisible);
+}
+
+void UIVMCloseDialog::setCheckBoxVisibleDiscard(bool fVisible)
+{
+ m_pCheckBoxDiscard->setVisible(fVisible);
+}
+
+void UIVMCloseDialog::prepare()
+{
+ /* Choose default dialog icon: */
+ m_icon = UIIconPool::iconSet(":/os_unknown.png");
+
+ /* Prepare size-grip token: */
+ setSizeGripEnabled(false);
+
+ /* Prepare main layout: */
+ prepareMainLayout();
+
+ /* Update pixmaps: */
+ updatePixmaps();
+
+ /* Configure: */
+ configure();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIVMCloseDialog::prepareMainLayout()
+{
+ /* Create main layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (m_pMainLayout)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setContentsMargins(40, 20, 40, 20);
+ m_pMainLayout->setSpacing(15);
+#else
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 2);
+#endif
+
+ /* Prepare top layout: */
+ prepareTopLayout();
+
+ /* Add stretch between top and bottom: */
+ m_pMainLayout->addStretch(1);
+
+ /* Prepare button-box: */
+ prepareButtonBox();
+ }
+}
+
+void UIVMCloseDialog::prepareTopLayout()
+{
+ /* Create top layout: */
+ m_pTopLayout = new QHBoxLayout;
+ if (m_pTopLayout)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ m_pTopLayout->setSpacing(20);
+#else
+ m_pTopLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) * 2);
+#endif
+
+ /* Prepare top-left layout: */
+ prepareTopLeftLayout();
+ /* Prepare top-right layout: */
+ prepareTopRightLayout();
+
+ /* Add into layout: */
+ m_pMainLayout->addLayout(m_pTopLayout);
+ }
+}
+
+void UIVMCloseDialog::prepareTopLeftLayout()
+{
+ /* Create top-left layout: */
+ m_pTopLeftLayout = new QVBoxLayout;
+ if (m_pTopLeftLayout)
+ {
+ /* Create icon label: */
+ m_pLabelIcon = new QLabel;
+ if (m_pLabelIcon)
+ {
+ /* Configure label: */
+ m_pLabelIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ /* Add into layout: */
+ m_pTopLeftLayout->addWidget(m_pLabelIcon);
+ }
+
+ /* Add vertical stretch under icon label: */
+ m_pTopLeftLayout->addStretch();
+
+ /* Add into layout: */
+ m_pTopLayout->addLayout(m_pTopLeftLayout);
+ }
+}
+
+void UIVMCloseDialog::prepareTopRightLayout()
+{
+ /* Create top-right layout: */
+ m_pTopRightLayout = new QVBoxLayout;
+ if (m_pTopRightLayout)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ m_pTopRightLayout->setSpacing(10);
+#else
+ m_pTopRightLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing));
+#endif
+
+ /* Create text label: */
+ m_pLabelText = new QLabel;
+ if (m_pLabelText)
+ {
+ /* Add into layout: */
+ m_pTopRightLayout->addWidget(m_pLabelText);
+ }
+
+ /* Prepare choice layout: */
+ prepareChoiceLayout();
+
+ /* Add into layout: */
+ m_pTopLayout->addLayout(m_pTopRightLayout);
+ }
+}
+
+void UIVMCloseDialog::prepareChoiceLayout()
+{
+ /* Create 'choice' layout: */
+ m_pChoiceLayout = new QGridLayout;
+ if (m_pChoiceLayout)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ m_pChoiceLayout->setSpacing(10);
+#else
+ m_pChoiceLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing));
+#endif
+
+ /* Create button-group: */
+ QButtonGroup *pButtonGroup = new QButtonGroup(this);
+ if (pButtonGroup)
+ {
+ connect(pButtonGroup, static_cast<void (QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked),
+ this, &UIVMCloseDialog::sltUpdateWidgetAvailability);
+ }
+
+ /* Create 'detach' icon label: */
+ m_pLabelIconDetach = new QLabel;
+ if (m_pLabelIconDetach)
+ {
+ /* Configure label: */
+ m_pLabelIconDetach->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ /* Add into layout: */
+ m_pChoiceLayout->addWidget(m_pLabelIconDetach, 0, 0);
+ }
+ /* Create 'detach' radio-button: */
+ m_pRadioButtonDetach = new QRadioButton;
+ if (m_pRadioButtonDetach)
+ {
+ /* Configure button: */
+ m_pRadioButtonDetach->installEventFilter(this);
+ /* Add into group/layout: */
+ pButtonGroup->addButton(m_pRadioButtonDetach);
+ m_pChoiceLayout->addWidget(m_pRadioButtonDetach, 0, 1);
+ }
+
+ /* Create 'save' icon label: */
+ m_pLabelIconSave = new QLabel;
+ if (m_pLabelIconSave)
+ {
+ /* Configure label: */
+ m_pLabelIconSave->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ /* Add into layout: */
+ m_pChoiceLayout->addWidget(m_pLabelIconSave, 1, 0);
+ }
+ /* Create 'save' radio-button: */
+ m_pRadioButtonSave = new QRadioButton;
+ if (m_pRadioButtonSave)
+ {
+ /* Configure button: */
+ m_pRadioButtonSave->installEventFilter(this);
+ /* Add into group/layout: */
+ pButtonGroup->addButton(m_pRadioButtonSave);
+ m_pChoiceLayout->addWidget(m_pRadioButtonSave, 1, 1);
+ }
+
+ /* Create 'shutdown' icon label: */
+ m_pLabelIconShutdown = new QLabel;
+ if (m_pLabelIconShutdown)
+ {
+ /* Configure label: */
+ m_pLabelIconShutdown->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ /* Add into layout: */
+ m_pChoiceLayout->addWidget(m_pLabelIconShutdown, 2, 0);
+ }
+ /* Create 'shutdown' radio-button: */
+ m_pRadioButtonShutdown = new QRadioButton;
+ if (m_pRadioButtonShutdown)
+ {
+ /* Configure button: */
+ m_pRadioButtonShutdown->installEventFilter(this);
+ /* Add into group/layout: */
+ pButtonGroup->addButton(m_pRadioButtonShutdown);
+ m_pChoiceLayout->addWidget(m_pRadioButtonShutdown, 2, 1);
+ }
+
+ /* Create 'power-off' icon label: */
+ m_pLabelIconPowerOff = new QLabel;
+ if (m_pLabelIconPowerOff)
+ {
+ /* Configure label: */
+ m_pLabelIconPowerOff->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ /* Add into layout: */
+ m_pChoiceLayout->addWidget(m_pLabelIconPowerOff, 3, 0);
+ }
+ /* Create 'shutdown' radio-button: */
+ m_pRadioButtonPowerOff = new QRadioButton;
+ if (m_pRadioButtonPowerOff)
+ {
+ /* Configure button: */
+ m_pRadioButtonPowerOff->installEventFilter(this);
+ /* Add into group/layout: */
+ pButtonGroup->addButton(m_pRadioButtonPowerOff);
+ m_pChoiceLayout->addWidget(m_pRadioButtonPowerOff, 3, 1);
+ }
+
+ /* Create 'discard' check-box: */
+ m_pCheckBoxDiscard = new QCheckBox;
+ if (m_pCheckBoxDiscard)
+ {
+ /* Add into layout: */
+ m_pChoiceLayout->addWidget(m_pCheckBoxDiscard, 4, 1);
+ }
+
+ /* Add into layout: */
+ m_pTopRightLayout->addLayout(m_pChoiceLayout);
+ }
+}
+
+void UIVMCloseDialog::prepareButtonBox()
+{
+ /* Create button-box: */
+ QIDialogButtonBox *pButtonBox = new QIDialogButtonBox;
+ if (pButtonBox)
+ {
+ /* Configure button-box: */
+ pButtonBox->setStandardButtons( QDialogButtonBox::Cancel
+ | QDialogButtonBox::Help
+ | QDialogButtonBox::NoButton
+ | QDialogButtonBox::Ok);
+ connect(pButtonBox, &QIDialogButtonBox::accepted, this, &UIVMCloseDialog::accept);
+ connect(pButtonBox, &QIDialogButtonBox::rejected, this, &UIVMCloseDialog::reject);
+ connect(pButtonBox->button(QIDialogButtonBox::Help), &QPushButton::pressed,
+ &msgCenter(), &UIMessageCenter::sltHandleHelpRequest);
+ pButtonBox->button(QIDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents);
+ uiCommon().setHelpKeyword(pButtonBox->button(QIDialogButtonBox::Help), "intro-save-machine-state");
+
+ /* Add into layout: */
+ m_pMainLayout->addWidget(pButtonBox);
+ }
+}
+
+void UIVMCloseDialog::configure()
+{
+ /* Get actual machine-state: */
+ KMachineState machineState = m_comMachine.GetState();
+
+ /* Check which close-actions are resticted: */
+ bool fIsDetachAllowed = uiCommon().isSeparateProcess() && !(m_restictedCloseActions & MachineCloseAction_Detach);
+ bool fIsStateSavingAllowed = !(m_restictedCloseActions & MachineCloseAction_SaveState);
+ bool fIsACPIShutdownAllowed = !(m_restictedCloseActions & MachineCloseAction_Shutdown);
+ bool fIsPowerOffAllowed = !(m_restictedCloseActions & MachineCloseAction_PowerOff);
+ bool fIsPowerOffAndRestoreAllowed = fIsPowerOffAllowed && !(m_restictedCloseActions & MachineCloseAction_PowerOff_RestoringSnapshot);
+
+ /* Make 'Detach' button visible/hidden depending on restriction: */
+ setButtonVisibleDetach(fIsDetachAllowed);
+ /* Make 'Detach' button enabled/disabled depending on machine-state: */
+ setButtonEnabledDetach(machineState != KMachineState_Stuck);
+
+ /* Make 'Save state' button visible/hidden depending on restriction: */
+ setButtonVisibleSave(fIsStateSavingAllowed);
+ /* Make 'Save state' button enabled/disabled depending on machine-state: */
+ setButtonEnabledSave(machineState != KMachineState_Stuck);
+
+ /* Make 'Shutdown' button visible/hidden depending on restriction: */
+ setButtonVisibleShutdown(fIsACPIShutdownAllowed);
+ /* Make 'Shutdown' button enabled/disabled depending on console and machine-state: */
+ setButtonEnabledShutdown(m_fIsACPIEnabled && machineState != KMachineState_Stuck);
+
+ /* Make 'Power off' button visible/hidden depending on restriction: */
+ setButtonVisiblePowerOff(fIsPowerOffAllowed);
+ /* Make the Restore Snapshot checkbox visible/hidden depending on snapshot count & restrictions: */
+ setCheckBoxVisibleDiscard(fIsPowerOffAndRestoreAllowed && m_comMachine.GetSnapshotCount() > 0);
+ /* Assign Restore Snapshot checkbox text: */
+ if (!m_comMachine.GetCurrentSnapshot().isNull())
+ m_strDiscardCheckBoxText = m_comMachine.GetCurrentSnapshot().GetName();
+
+ /* Check which radio-button should be initially chosen: */
+ QRadioButton *pRadioButtonToChoose = 0;
+ /* If choosing 'last choice' is possible: */
+ m_enmLastCloseAction = gEDataManager->lastMachineCloseAction(uiCommon().managedVMUuid());
+ if (m_enmLastCloseAction == MachineCloseAction_Detach && fIsDetachAllowed)
+ {
+ pRadioButtonToChoose = m_pRadioButtonDetach;
+ }
+ else if (m_enmLastCloseAction == MachineCloseAction_SaveState && fIsStateSavingAllowed)
+ {
+ pRadioButtonToChoose = m_pRadioButtonSave;
+ }
+ else if (m_enmLastCloseAction == MachineCloseAction_Shutdown && fIsACPIShutdownAllowed && m_fIsACPIEnabled)
+ {
+ pRadioButtonToChoose = m_pRadioButtonShutdown;
+ }
+ else if (m_enmLastCloseAction == MachineCloseAction_PowerOff && fIsPowerOffAllowed)
+ {
+ pRadioButtonToChoose = m_pRadioButtonPowerOff;
+ }
+ else if (m_enmLastCloseAction == MachineCloseAction_PowerOff_RestoringSnapshot && fIsPowerOffAndRestoreAllowed)
+ {
+ pRadioButtonToChoose = m_pRadioButtonPowerOff;
+ }
+ /* Else 'default choice' will be used: */
+ else
+ {
+ if (fIsDetachAllowed)
+ pRadioButtonToChoose = m_pRadioButtonDetach;
+ else if (fIsStateSavingAllowed)
+ pRadioButtonToChoose = m_pRadioButtonSave;
+ else if (fIsACPIShutdownAllowed && m_fIsACPIEnabled)
+ pRadioButtonToChoose = m_pRadioButtonShutdown;
+ else if (fIsPowerOffAllowed)
+ pRadioButtonToChoose = m_pRadioButtonPowerOff;
+ }
+
+ /* If some radio-button chosen: */
+ if (pRadioButtonToChoose)
+ {
+ /* Check and focus it: */
+ pRadioButtonToChoose->setChecked(true);
+ pRadioButtonToChoose->setFocus();
+ sltUpdateWidgetAvailability();
+ m_fValid = true;
+ }
+}
+
+void UIVMCloseDialog::updatePixmaps()
+{
+ /* Acquire hints: */
+ const int iMetricSmall = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ const int iMetricLarge = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
+
+ /* Re-apply pixmap: */
+ m_pLabelIcon->setPixmap(m_icon.pixmap(windowHandle(), QSize(iMetricLarge, iMetricLarge)));
+
+ QIcon icon;
+ icon = UIIconPool::iconSet(":/vm_create_shortcut_16px.png");
+ m_pLabelIconDetach->setPixmap(icon.pixmap(windowHandle(), QSize(iMetricSmall, iMetricSmall)));
+ icon = UIIconPool::iconSet(":/vm_save_state_16px.png");
+ m_pLabelIconSave->setPixmap(icon.pixmap(windowHandle(), QSize(iMetricSmall, iMetricSmall)));
+ icon = UIIconPool::iconSet(":/vm_shutdown_16px.png");
+ m_pLabelIconShutdown->setPixmap(icon.pixmap(windowHandle(), QSize(iMetricSmall, iMetricSmall)));
+ icon = UIIconPool::iconSet(":/vm_poweroff_16px.png");
+ m_pLabelIconPowerOff->setPixmap(icon.pixmap(windowHandle(), QSize(iMetricSmall, iMetricSmall)));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/UIVMCloseDialog.h b/src/VBox/Frontends/VirtualBox/src/runtime/UIVMCloseDialog.h
new file mode 100644
index 00000000..d679bfaa
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/UIVMCloseDialog.h
@@ -0,0 +1,194 @@
+/* $Id: UIVMCloseDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIVMCloseDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_UIVMCloseDialog_h
+#define FEQT_INCLUDED_SRC_runtime_UIVMCloseDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QHBoxLayout;
+class QLabel;
+class QRadioButton;
+class QVBoxLayout;
+class CMachine;
+
+/** QIDialog extension to handle Runtime UI close-event. */
+class UIVMCloseDialog : public QIWithRetranslateUI<QIDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs close dialog passing @a pParent to the base-class.
+ * @param comMachine Brings the machine dialog created for.
+ * @param fIsACPIEnabled Brings whether ACPI is enabled.
+ * @param restictedCloseActions Brings a set of restricted actions. */
+ UIVMCloseDialog(QWidget *pParent, CMachine &comMachine,
+ bool fIsACPIEnabled, MachineCloseAction restictedCloseActions);
+
+ /** Returns whether dialog is valid. */
+ bool isValid() const { return m_fValid; }
+
+ /** Defines dialog @a icon. */
+ void setIcon(const QIcon &icon);
+
+protected:
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Updates widgets availability. */
+ void sltUpdateWidgetAvailability();
+
+ /** Accepts the dialog. */
+ void accept();
+
+private:
+
+ /** Defines whether 'Detach' button is enabled. */
+ void setButtonEnabledDetach(bool fEnabled);
+ /** Defines whether 'Detach' button is visible. */
+ void setButtonVisibleDetach(bool fVisible);
+
+ /** Defines whether 'Save' button is enabled. */
+ void setButtonEnabledSave(bool fEnabled);
+ /** Defines whether 'Save' button is visible. */
+ void setButtonVisibleSave(bool fVisible);
+
+ /** Defines whether 'Shutdown' button is enabled. */
+ void setButtonEnabledShutdown(bool fEnabled);
+ /** Defines whether 'Shutdown' button is visible. */
+ void setButtonVisibleShutdown(bool fVisible);
+
+ /** Defines whether 'PowerOff' button is enabled. */
+ void setButtonEnabledPowerOff(bool fEnabled);
+ /** Defines whether 'PowerOff' button is visible. */
+ void setButtonVisiblePowerOff(bool fVisible);
+
+ /** Defines whether 'Discard' check-box is visible. */
+ void setCheckBoxVisibleDiscard(bool fVisible);
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares main layout. */
+ void prepareMainLayout();
+ /** Prepares top layout. */
+ void prepareTopLayout();
+ /** Prepares top-left layout. */
+ void prepareTopLeftLayout();
+ /** Prepares top-right layout. */
+ void prepareTopRightLayout();
+ /** Prepares choice layout. */
+ void prepareChoiceLayout();
+ /** Prepares button-box. */
+ void prepareButtonBox();
+
+ /** Configures dialog. */
+ void configure();
+
+ /** Updates pixmaps. */
+ void updatePixmaps();
+
+ /** Holds the live machine reference. */
+ CMachine &m_comMachine;
+ /** Holds whether ACPI is enabled. */
+ bool m_fIsACPIEnabled;
+ /** Holds a set of restricted actions. */
+ const MachineCloseAction m_restictedCloseActions;
+
+ /** Holds whether dialog is valid. */
+ bool m_fValid;
+
+ /** Holds the dialog icon. */
+ QIcon m_icon;
+
+ /** Holds the main layout instance. */
+ QVBoxLayout *m_pMainLayout;
+ /** Holds the top layout instance. */
+ QHBoxLayout *m_pTopLayout;
+ /** Holds the top-left layout instance. */
+ QVBoxLayout *m_pTopLeftLayout;
+ /** Holds the top-right layout instance. */
+ QVBoxLayout *m_pTopRightLayout;
+ /** Holds the choice layout instance. */
+ QGridLayout *m_pChoiceLayout;
+
+ /** Holds the icon label instance. */
+ QLabel *m_pLabelIcon;
+ /** Holds the text label instance. */
+ QLabel *m_pLabelText;
+
+ /** Holds the 'Detach' icon label instance. */
+ QLabel *m_pLabelIconDetach;
+ /** Holds the 'Detach' radio-button instance. */
+ QRadioButton *m_pRadioButtonDetach;
+ /** Holds the 'Save' icon label instance. */
+ QLabel *m_pLabelIconSave;
+ /** Holds the 'Save' radio-button instance. */
+ QRadioButton *m_pRadioButtonSave;
+ /** Holds the 'Shutdown' icon label instance. */
+ QLabel *m_pLabelIconShutdown;
+ /** Holds the 'Shutdown' radio-button instance. */
+ QRadioButton *m_pRadioButtonShutdown;
+ /** Holds the 'PowerOff' icon label instance. */
+ QLabel *m_pLabelIconPowerOff;
+ /** Holds the 'PowerOff' radio-button instance. */
+ QRadioButton *m_pRadioButtonPowerOff;
+
+ /** Holds the 'Discard' check-box instance. */
+ QCheckBox *m_pCheckBoxDiscard;
+ /** Holds the 'Discard' check-box text. */
+ QString m_strDiscardCheckBoxText;
+
+ /** Holds the last close action. */
+ MachineCloseAction m_enmLastCloseAction;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_UIVMCloseDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.cpp
new file mode 100644
index 00000000..017e0184
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.cpp
@@ -0,0 +1,83 @@
+/* $Id: UIKeyboardHandlerFullscreen.cpp $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandlerFullscreen class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QKeyEvent>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UIKeyboardHandlerFullscreen.h"
+#include "UIMachineLogic.h"
+#include "UIShortcutPool.h"
+
+
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+
+UIKeyboardHandlerFullscreen::UIKeyboardHandlerFullscreen(UIMachineLogic* pMachineLogic)
+ : UIKeyboardHandler(pMachineLogic)
+{
+}
+
+UIKeyboardHandlerFullscreen::~UIKeyboardHandlerFullscreen()
+{
+}
+
+bool UIKeyboardHandlerFullscreen::eventFilter(QObject *pWatchedObject, QEvent *pEvent)
+{
+ /* Check if pWatchedObject object is view: */
+ if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject))
+ {
+ /* Get corresponding screen index: */
+ ulong uScreenId = m_views.key(pWatchedView);
+ NOREF(uScreenId);
+ /* Handle view events: */
+ switch (pEvent->type())
+ {
+ case QEvent::KeyPress:
+ {
+ /* Get key-event: */
+ QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
+ /* Process Host+Home for menu popup: */
+ if ( isHostKeyPressed()
+ && gShortcutPool->shortcut(GUI_Input_MachineShortcuts, QString("PopupMenu")).sequences().contains(QKeySequence(pKeyEvent->key())))
+ {
+ /* Post request to show popup-menu: */
+ QTimer::singleShot(0, m_pMachineLogic, SLOT(sltInvokePopupMenu()));
+ /* Filter-out this event: */
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Else just propagate to base-class: */
+ return UIKeyboardHandler::eventFilter(pWatchedObject, pEvent);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.h b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.h
new file mode 100644
index 00000000..1a37f66b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.h
@@ -0,0 +1,56 @@
+/* $Id: UIKeyboardHandlerFullscreen.h $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandlerFullscreen class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_fullscreen_UIKeyboardHandlerFullscreen_h
+#define FEQT_INCLUDED_SRC_runtime_fullscreen_UIKeyboardHandlerFullscreen_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIKeyboardHandler.h"
+
+/** UIKeyboardHandler reimplementation
+ * providing machine-logic with PopupMenu keyboard handler. */
+class UIKeyboardHandlerFullscreen : public UIKeyboardHandler
+{
+ Q_OBJECT;
+
+public:
+
+ /** Fullscreen keyboard-handler constructor. */
+ UIKeyboardHandlerFullscreen(UIMachineLogic *pMachineLogic);
+ /** Fullscreen keyboard-handler destructor. */
+ virtual ~UIKeyboardHandlerFullscreen();
+
+private:
+
+ /** General event-filter. */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_fullscreen_UIKeyboardHandlerFullscreen_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineLogicFullscreen.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineLogicFullscreen.cpp
new file mode 100644
index 00000000..5d2afeed
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineLogicFullscreen.cpp
@@ -0,0 +1,825 @@
+/* $Id: UIMachineLogicFullscreen.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogicFullscreen class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QTimer>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIMessageCenter.h"
+#include "UISession.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogicFullscreen.h"
+#include "UIMachineWindowFullscreen.h"
+#include "UIMultiScreenLayout.h"
+#include "UIShortcutPool.h"
+#include "UIMachineView.h"
+#include "QIMenu.h"
+#ifdef VBOX_WS_MAC
+# include "UICocoaApplication.h"
+# include "UIExtraDataManager.h"
+# include "VBoxUtils.h"
+# include "UIFrameBuffer.h"
+# include <Carbon/Carbon.h>
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "CGraphicsAdapter.h"
+
+
+UIMachineLogicFullscreen::UIMachineLogicFullscreen(QObject *pParent, UISession *pSession)
+ : UIMachineLogic(pParent, pSession, UIVisualStateType_Fullscreen)
+ , m_pPopupMenu(0)
+#ifdef VBOX_WS_MAC
+ , m_fScreensHaveSeparateSpaces(darwinScreensHaveSeparateSpaces())
+#endif /* VBOX_WS_MAC */
+{
+ /* Create multiscreen layout: */
+ m_pScreenLayout = new UIMultiScreenLayout(this);
+}
+
+UIMachineLogicFullscreen::~UIMachineLogicFullscreen()
+{
+ /* Delete multiscreen layout: */
+ delete m_pScreenLayout;
+}
+
+int UIMachineLogicFullscreen::hostScreenForGuestScreen(int iScreenId) const
+{
+ return m_pScreenLayout->hostScreenForGuestScreen(iScreenId);
+}
+
+bool UIMachineLogicFullscreen::hasHostScreenForGuestScreen(int iScreenId) const
+{
+ return m_pScreenLayout->hasHostScreenForGuestScreen(iScreenId);
+}
+
+bool UIMachineLogicFullscreen::checkAvailability()
+{
+ /* Check if there is enough physical memory to enter fullscreen: */
+ if (uisession()->isGuestSupportsGraphics())
+ {
+ quint64 availBits = machine().GetGraphicsAdapter().GetVRAMSize() /* VRAM */ * _1M /* MiB to bytes */ * 8 /* to bits */;
+ quint64 usedBits = m_pScreenLayout->memoryRequirements();
+ if (availBits < usedBits)
+ {
+ if (!msgCenter().cannotEnterFullscreenMode(0, 0, 0, (((usedBits + 7) / 8 + _1M - 1) / _1M) * _1M))
+ return false;
+ }
+ }
+
+ /* Show the info message. */
+ const UIShortcut &shortcut =
+ gShortcutPool->shortcut(actionPool()->shortcutsExtraDataID(),
+ actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen)->shortcutExtraDataID());
+ const QString strHotKey = QString("Host+%1").arg(shortcut.primaryToPortableText());
+ if (!msgCenter().confirmGoingFullscreen(strHotKey))
+ return false;
+
+ return true;
+}
+
+Qt::WindowFlags UIMachineLogicFullscreen::windowFlags(ulong uScreenId) const
+{
+ Q_UNUSED(uScreenId);
+#ifdef VBOX_WS_MAC
+ return uScreenId == 0 || screensHaveSeparateSpaces() ? Qt::Window : Qt::FramelessWindowHint;
+#else /* !VBOX_WS_MAC */
+ return Qt::FramelessWindowHint;
+#endif /* !VBOX_WS_MAC */
+}
+
+void UIMachineLogicFullscreen::adjustMachineWindowsGeometry()
+{
+ LogRel(("GUI: UIMachineLogicFullscreen::adjustMachineWindowsGeometry\n"));
+
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+
+#ifdef VBOX_WS_MAC
+ /* Revalidate native fullscreen: */
+ revalidateNativeFullScreen();
+#else /* !VBOX_WS_MAC */
+ /* Make sure all machine-window(s) have proper geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->showInNecessaryMode();
+#endif /* !VBOX_WS_MAC */
+}
+
+#ifdef RT_OS_DARWIN
+void UIMachineLogicFullscreen::sltHandleNativeFullscreenWillEnter()
+{
+ /* Get sender machine-window: */
+ UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
+ AssertPtrReturnVoid(pMachineWindow);
+ LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenWillEnter: "
+ "Machine-window #%d will enter native fullscreen\n",
+ (int)pMachineWindow->screenId()));
+}
+
+void UIMachineLogicFullscreen::sltHandleNativeFullscreenDidEnter()
+{
+ /* Get sender machine-window: */
+ UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
+ AssertPtrReturnVoid(pMachineWindow);
+ LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenDidEnter: "
+ "Machine-window #%d did enter native fullscreen\n",
+ (int)pMachineWindow->screenId()));
+
+ /* Add machine-window to corresponding set: */
+ m_fullscreenMachineWindows.insert(pMachineWindow);
+ AssertReturnVoid(m_fullscreenMachineWindows.contains(pMachineWindow));
+
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+ /* Revalidate native fullscreen: */
+ revalidateNativeFullScreen();
+}
+
+void UIMachineLogicFullscreen::sltHandleNativeFullscreenWillExit()
+{
+ /* Get sender machine-window: */
+ UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
+ AssertPtrReturnVoid(pMachineWindow);
+ LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenWillExit: "
+ "Machine-window #%d will exit native fullscreen\n",
+ (int)pMachineWindow->screenId()));
+}
+
+void UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit()
+{
+ /* Get sender machine-window: */
+ UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
+ AssertPtrReturnVoid(pMachineWindow);
+
+ /* Remove machine-window from corresponding set: */
+ bool fResult = m_fullscreenMachineWindows.remove(pMachineWindow);
+ AssertReturnVoid(!m_fullscreenMachineWindows.contains(pMachineWindow));
+
+ /* We have same signal if window did fail to enter native fullscreen.
+ * In that case window missed in m_fullscreenMachineWindows,
+ * ignore this signal silently: */
+ if (!fResult)
+ return;
+
+ /* If that window was invalidated: */
+ if (m_invalidFullscreenMachineWindows.contains(pMachineWindow))
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit: "
+ "Machine-window #%d exited invalidated native fullscreen, revalidate it\n",
+ (int)pMachineWindow->screenId()));
+
+ /* Exclude machine-window from invalidation set: */
+ m_invalidFullscreenMachineWindows.remove(pMachineWindow);
+ AssertReturnVoid(!m_invalidFullscreenMachineWindows.contains(pMachineWindow));
+
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+ /* Revalidate native fullscreen: */
+ revalidateNativeFullScreen();
+ }
+ /* If there are no invalidated windows: */
+ else if (m_invalidFullscreenMachineWindows.isEmpty())
+ {
+ /* If there are 'fullscreen' windows: */
+ if (!m_fullscreenMachineWindows.isEmpty())
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit: "
+ "Machine-window #%d exited native fullscreen, asking others to exit too...\n",
+ (int)pMachineWindow->screenId()));
+
+ /* Ask window(s) to exit 'fullscreen' mode: */
+ emit sigNotifyAboutNativeFullscreenShouldBeExited();
+ }
+ /* If there are no 'fullscreen' windows: */
+ else
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit: "
+ "Machine-window #%d exited native fullscreen, changing visual-state to requested...\n",
+ (int)pMachineWindow->screenId()));
+
+ /* Change visual-state to requested: */
+ UIVisualStateType type = uisession()->requestedVisualState();
+ if (type == UIVisualStateType_Invalid)
+ type = UIVisualStateType_Normal;
+ uisession()->setRequestedVisualState(UIVisualStateType_Invalid);
+ uisession()->changeVisualState(type);
+ }
+ }
+}
+
+void UIMachineLogicFullscreen::sltHandleNativeFullscreenFailToEnter()
+{
+ /* Get sender machine-window: */
+ UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
+ AssertReturnVoid(pMachineWindow);
+
+ /* Make sure this window is not registered somewhere: */
+ AssertReturnVoid(!m_fullscreenMachineWindows.remove(pMachineWindow));
+ AssertReturnVoid(!m_invalidFullscreenMachineWindows.remove(pMachineWindow));
+
+ /* If there are 'fullscreen' windows: */
+ if (!m_fullscreenMachineWindows.isEmpty())
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenFailToEnter: "
+ "Machine-window #%d failed to enter native fullscreen, asking others to exit...\n",
+ (int)pMachineWindow->screenId()));
+
+ /* Ask window(s) to exit 'fullscreen' mode: */
+ emit sigNotifyAboutNativeFullscreenShouldBeExited();
+ }
+ /* If there are no 'fullscreen' windows: */
+ else
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenFailToEnter: "
+ "Machine-window #%d failed to enter native fullscreen, requesting change visual-state to normal...\n",
+ (int)pMachineWindow->screenId()));
+
+ /* Ask session to change 'fullscreen' mode to 'normal': */
+ uisession()->setRequestedVisualState(UIVisualStateType_Normal);
+
+ /* If session already initialized => push mode-change directly: */
+ if (uisession()->isInitialized())
+ sltCheckForRequestedVisualStateType();
+ }
+}
+
+void UIMachineLogicFullscreen::sltChangeVisualStateToNormal()
+{
+ /* Request 'normal' (window) visual-state: */
+ uisession()->setRequestedVisualState(UIVisualStateType_Normal);
+ /* Ask window(s) to exit 'fullscreen' mode: */
+ emit sigNotifyAboutNativeFullscreenShouldBeExited();
+}
+
+void UIMachineLogicFullscreen::sltChangeVisualStateToSeamless()
+{
+ /* Request 'seamless' visual-state: */
+ uisession()->setRequestedVisualState(UIVisualStateType_Seamless);
+ /* Ask window(s) to exit 'fullscreen' mode: */
+ emit sigNotifyAboutNativeFullscreenShouldBeExited();
+}
+
+void UIMachineLogicFullscreen::sltChangeVisualStateToScale()
+{
+ /* Request 'scale' visual-state: */
+ uisession()->setRequestedVisualState(UIVisualStateType_Scale);
+ /* Ask window(s) to exit 'fullscreen' mode: */
+ emit sigNotifyAboutNativeFullscreenShouldBeExited();
+}
+
+void UIMachineLogicFullscreen::sltCheckForRequestedVisualStateType()
+{
+ LogRel(("GUI: UIMachineLogicFullscreen::sltCheckForRequestedVisualStateType: Requested-state=%d, Machine-state=%d\n",
+ uisession()->requestedVisualState(), uisession()->machineState()));
+
+ /* Do not try to change visual-state type if machine was not started yet: */
+ if (!uisession()->isRunning() && !uisession()->isPaused())
+ return;
+
+ /* Do not try to change visual-state type in 'manual override' mode: */
+ if (uisession()->isManualOverrideMode())
+ return;
+
+ /* Check requested visual-state types: */
+ switch (uisession()->requestedVisualState())
+ {
+ /* If 'normal' visual-state type is requested: */
+ case UIVisualStateType_Normal:
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::sltCheckForRequestedVisualStateType: "
+ "Going 'normal' as requested...\n"));
+ uisession()->setRequestedVisualState(UIVisualStateType_Invalid);
+ uisession()->changeVisualState(UIVisualStateType_Normal);
+ break;
+ }
+ default:
+ break;
+ }
+}
+#endif /* RT_OS_DARWIN */
+
+void UIMachineLogicFullscreen::sltMachineStateChanged()
+{
+ /* Call to base-class: */
+ UIMachineLogic::sltMachineStateChanged();
+
+ /* If machine-state changed from 'paused' to 'running': */
+ if (uisession()->isRunning() && uisession()->wasPaused())
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::sltMachineStateChanged:"
+ "Machine-state changed from 'paused' to 'running': "
+ "Adjust machine-window geometry...\n"));
+
+ /* Make sure further code will be called just once: */
+ uisession()->forgetPreviousMachineState();
+ /* Adjust machine-window geometry if necessary: */
+ adjustMachineWindowsGeometry();
+ }
+}
+
+void UIMachineLogicFullscreen::sltInvokePopupMenu()
+{
+ /* Popup main-menu if present: */
+ if (m_pPopupMenu && !m_pPopupMenu->isEmpty())
+ {
+ m_pPopupMenu->popup(activeMachineWindow()->geometry().center());
+ QTimer::singleShot(0, m_pPopupMenu, SLOT(sltHighlightFirstAction()));
+ }
+}
+
+void UIMachineLogicFullscreen::sltScreenLayoutChanged()
+{
+ LogRel(("GUI: UIMachineLogicFullscreen::sltScreenLayoutChanged: Multi-screen layout changed\n"));
+
+#ifdef VBOX_WS_MAC
+ /* Revalidate native fullscreen: */
+ revalidateNativeFullScreen();
+#else /* !VBOX_WS_MAC */
+ /* Make sure all machine-window(s) have proper geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->showInNecessaryMode();
+#endif /* !VBOX_WS_MAC */
+}
+
+void UIMachineLogicFullscreen::sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo)
+{
+ LogRel(("GUI: UIMachineLogicFullscreen: Guest-screen count changed\n"));
+
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+
+#ifdef VBOX_WS_MAC
+ /* Revalidate native fullscreen: */
+ RT_NOREF(changeType, uScreenId, screenGeo);
+ revalidateNativeFullScreen();
+#else /* !VBOX_WS_MAC */
+ /* Call to base-class: */
+ UIMachineLogic::sltGuestMonitorChange(changeType, uScreenId, screenGeo);
+#endif /* !VBOX_WS_MAC */
+}
+
+void UIMachineLogicFullscreen::sltHostScreenCountChange()
+{
+ LogRel(("GUI: UIMachineLogicFullscreen: Host-screen count changed\n"));
+
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+
+#ifdef VBOX_WS_MAC
+ /* Revalidate native fullscreen: */
+ revalidateNativeFullScreen();
+#else /* !VBOX_WS_MAC */
+ /* Call to base-class: */
+ UIMachineLogic::sltHostScreenCountChange();
+#endif /* !VBOX_WS_MAC */
+}
+
+void UIMachineLogicFullscreen::sltHostScreenAvailableAreaChange()
+{
+ LogRel2(("GUI: UIMachineLogicFullscreen: Host-screen available-area change ignored\n"));
+}
+
+void UIMachineLogicFullscreen::sltAdditionsStateChanged()
+{
+ /* Call to base-class: */
+ UIMachineLogic::sltAdditionsStateChanged();
+
+ LogRel(("GUI: UIMachineLogicFullscreen: Additions-state actual-change event, rebuild multi-screen layout\n"));
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+}
+
+void UIMachineLogicFullscreen::prepareActionGroups()
+{
+ /* Call to base-class: */
+ UIMachineLogic::prepareActionGroups();
+
+ /* Restrict 'Adjust Window', 'Status Bar' and 'Resize' actions for 'View' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
+ (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
+ (UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize));
+#ifdef VBOX_WS_MAC
+ /* Restrict 'Window' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuBar(UIActionRestrictionLevel_Logic,
+ UIExtraDataMetaDefs::MenuType_Window);
+#endif /* VBOX_WS_MAC */
+
+ /* Take care of view-action toggle state: */
+ UIAction *pActionFullscreen = actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen);
+ if (!pActionFullscreen->isChecked())
+ {
+ pActionFullscreen->blockSignals(true);
+ pActionFullscreen->setChecked(true);
+ pActionFullscreen->blockSignals(false);
+ }
+}
+
+void UIMachineLogicFullscreen::prepareActionConnections()
+{
+ /* Call to base-class: */
+ UIMachineLogic::prepareActionConnections();
+
+ /* Prepare 'View' actions connections: */
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &UIAction::triggered,
+ this, &UIMachineLogicFullscreen::sltChangeVisualStateToNormal);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &UIAction::triggered,
+ this, &UIMachineLogicFullscreen::sltChangeVisualStateToSeamless);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &UIAction::triggered,
+ this, &UIMachineLogicFullscreen::sltChangeVisualStateToScale);
+}
+
+void UIMachineLogicFullscreen::prepareMachineWindows()
+{
+ /* Do not create machine-window(s) if they created already: */
+ if (isMachineWindowsCreated())
+ return;
+
+#ifdef VBOX_WS_MAC
+ /* Register to native notifications: */
+ UICocoaApplication::instance()->registerToNotificationOfWorkspace("NSWorkspaceDidActivateApplicationNotification", this,
+ UIMachineLogicFullscreen::nativeHandlerForApplicationActivation);
+ UICocoaApplication::instance()->registerToNotificationOfWorkspace("NSWorkspaceActiveSpaceDidChangeNotification", this,
+ UIMachineLogicFullscreen::nativeHandlerForActiveSpaceChange);
+
+ /* We have to make sure that we are getting the front most process.
+ * This is necessary for Qt versions > 4.3.3: */
+ darwinSetFrontMostProcess();
+#endif /* VBOX_WS_MAC */
+
+ /* Update the multi-screen layout: */
+ m_pScreenLayout->update();
+
+ /* Create machine-window(s): */
+ for (uint cScreenId = 0; cScreenId < machine().GetGraphicsAdapter().GetMonitorCount(); ++cScreenId)
+ addMachineWindow(UIMachineWindow::create(this, cScreenId));
+
+ /* Listen for frame-buffer resize: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ connect(pMachineWindow, &UIMachineWindow::sigFrameBufferResize,
+ this, &UIMachineLogicFullscreen::sigFrameBufferResize);
+ emit sigFrameBufferResize();
+
+ /* Connect multi-screen layout change handler: */
+ connect(m_pScreenLayout, &UIMultiScreenLayout::sigScreenLayoutChange,
+ this, &UIMachineLogicFullscreen::sltScreenLayoutChanged);
+
+#ifdef VBOX_WS_MAC
+ /* Enable native fullscreen support: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ {
+ UIMachineWindowFullscreen *pMachineWindowFullscreen = qobject_cast<UIMachineWindowFullscreen*>(pMachineWindow);
+ if (!pMachineWindow)
+ continue;
+ /* Logic => window signals: */
+ connect(this, &UIMachineLogicFullscreen::sigNotifyAboutNativeFullscreenShouldBeEntered,
+ pMachineWindowFullscreen, &UIMachineWindowFullscreen::sltEnterNativeFullscreen);
+ connect(this, &UIMachineLogicFullscreen::sigNotifyAboutNativeFullscreenShouldBeExited,
+ pMachineWindowFullscreen, &UIMachineWindowFullscreen::sltExitNativeFullscreen);
+ /* Window => logic signals: */
+ connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenWillEnter,
+ this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenWillEnter,
+ Qt::QueuedConnection);
+ connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenDidEnter,
+ this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenDidEnter,
+ Qt::QueuedConnection);
+ connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenWillExit,
+ this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenWillExit,
+ Qt::QueuedConnection);
+ connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenDidExit,
+ this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit,
+ Qt::QueuedConnection);
+ connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenFailToEnter,
+ this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenFailToEnter,
+ Qt::QueuedConnection);
+ }
+ /* Revalidate native fullscreen: */
+ revalidateNativeFullScreen();
+#endif /* VBOX_WS_MAC */
+
+ /* Mark machine-window(s) created: */
+ setMachineWindowsCreated(true);
+
+#ifdef VBOX_WS_X11
+ switch (uiCommon().typeOfWindowManager())
+ {
+ case X11WMType_GNOMEShell:
+ case X11WMType_Mutter:
+ {
+ // WORKAROUND:
+ // Under certain WMs we can loose machine-window activation due to any Qt::Tool
+ // overlay asynchronously shown above it. Qt is not become aware of such event.
+ // We are going to ask to return machine-window activation in let's say 100ms.
+ QTimer::singleShot(100, machineWindows().first(), SLOT(sltActivateWindow()));
+ break;
+ }
+ default:
+ break;
+ }
+#endif /* VBOX_WS_X11 */
+}
+
+void UIMachineLogicFullscreen::prepareMenu()
+{
+ /* Prepare popup-menu: */
+ m_pPopupMenu = new QIMenu;
+ AssertPtrReturnVoid(m_pPopupMenu);
+ {
+ /* Prepare popup-menu: */
+ foreach (QMenu *pMenu, actionPool()->menus())
+ m_pPopupMenu->addMenu(pMenu);
+ }
+}
+
+void UIMachineLogicFullscreen::cleanupMenu()
+{
+ /* Cleanup popup-menu: */
+ delete m_pPopupMenu;
+ m_pPopupMenu = 0;
+}
+
+void UIMachineLogicFullscreen::cleanupMachineWindows()
+{
+ /* Do not destroy machine-window(s) if they destroyed already: */
+ if (!isMachineWindowsCreated())
+ return;
+
+#ifdef VBOX_WS_MAC
+ /* Unregister from native notifications: */
+ UICocoaApplication::instance()->unregisterFromNotificationOfWorkspace("NSWorkspaceDidActivateApplicationNotification", this);
+ UICocoaApplication::instance()->unregisterFromNotificationOfWorkspace("NSWorkspaceActiveSpaceDidChangeNotification", this);
+#endif/* VBOX_WS_MAC */
+
+ /* Mark machine-window(s) destroyed: */
+ setMachineWindowsCreated(false);
+
+ /* Destroy machine-window(s): */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ UIMachineWindow::destroy(pMachineWindow);
+}
+
+void UIMachineLogicFullscreen::cleanupActionConnections()
+{
+ /* "View" actions disconnections: */
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &QAction::triggered,
+ this, &UIMachineLogicFullscreen::sltChangeVisualStateToNormal);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &QAction::triggered,
+ this, &UIMachineLogicFullscreen::sltChangeVisualStateToSeamless);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &QAction::triggered,
+ this, &UIMachineLogicFullscreen::sltChangeVisualStateToScale);
+
+ /* Call to base-class: */
+ UIMachineLogic::cleanupActionConnections();
+}
+
+void UIMachineLogicFullscreen::cleanupActionGroups()
+{
+ /* Take care of view-action toggle state: */
+ UIAction *pActionFullscreen = actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen);
+ if (pActionFullscreen->isChecked())
+ {
+ pActionFullscreen->blockSignals(true);
+ pActionFullscreen->setChecked(false);
+ pActionFullscreen->blockSignals(false);
+ }
+
+ /* Allow 'Adjust Window', 'Status Bar' and 'Resize' actions for 'View' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid);
+#ifdef VBOX_WS_MAC
+ /* Allow 'Window' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuBar(UIActionRestrictionLevel_Logic,
+ UIExtraDataMetaDefs::MenuType_Invalid);
+#endif /* VBOX_WS_MAC */
+
+ /* Call to base-class: */
+ UIMachineLogic::cleanupActionGroups();
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineLogicFullscreen::revalidateNativeFullScreen(UIMachineWindow *pMachineWindow)
+{
+ /* Make sure that is full-screen machine-window: */
+ UIMachineWindowFullscreen *pMachineWindowFullscreen = qobject_cast<UIMachineWindowFullscreen*>(pMachineWindow);
+ AssertPtrReturnVoid(pMachineWindowFullscreen);
+
+ /* Make sure window is not already invalidated: */
+ if (m_invalidFullscreenMachineWindows.contains(pMachineWindow))
+ return;
+
+ /* Ignore window if it is in 'fullscreen transition': */
+ if (pMachineWindowFullscreen->isInFullscreenTransition())
+ return;
+
+ /* Get screen ID: */
+ const ulong uScreenID = pMachineWindow->screenId();
+ LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: For machine-window #%d\n",
+ (int)uScreenID));
+
+ /* Validate window which can't be fullscreen: */
+ if (uScreenID != 0 && !screensHaveSeparateSpaces())
+ {
+ /* We are hiding transient window if:
+ * 1. primary window is not on active user-space
+ * 2. there is no fullscreen window or it's invalidated. */
+ if ( !darwinIsOnActiveSpace(machineWindows().first())
+ || m_fullscreenMachineWindows.isEmpty() || !m_invalidFullscreenMachineWindows.isEmpty())
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
+ "Ask transient machine-window #%d to hide\n", (int)uScreenID));
+
+ /* Make sure window hidden: */
+ pMachineWindow->hide();
+ }
+ /* If there is valid fullscreen window: */
+ else
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
+ "Ask transient machine-window #%d to show/normalize\n", (int)uScreenID));
+
+ /* Make sure window have proper geometry and shown: */
+ pMachineWindow->showInNecessaryMode();
+ }
+ }
+ /* Validate window which can be fullscreen: */
+ else
+ {
+ /* Validate window which is not in fullscreen: */
+ if (!darwinIsInFullscreenMode(pMachineWindow))
+ {
+ /* If that window
+ * 1. should really be shown and
+ * 2. is mapped to some host-screen: */
+ if ( uisession()->isScreenVisible(uScreenID)
+ && hasHostScreenForGuestScreen(uScreenID))
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
+ "Ask machine-window #%d to enter native fullscreen\n", (int)uScreenID));
+
+ /* Make sure window have proper geometry and shown: */
+ pMachineWindow->showInNecessaryMode();
+
+ /* Ask window to enter 'fullscreen' mode: */
+ emit sigNotifyAboutNativeFullscreenShouldBeEntered(pMachineWindow);
+ }
+ /* If that window
+ * is shown while shouldn't: */
+ else if (pMachineWindow->isVisible())
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
+ "Ask machine-window #%d to hide\n", (int)uScreenID));
+
+ /* Make sure window hidden: */
+ pMachineWindow->hide();
+ }
+ }
+ /* Validate window which is in fullscreen: */
+ else
+ {
+ /* Variables to compare: */
+ const int iWantedHostScreenIndex = hostScreenForGuestScreen((int)uScreenID);
+ const int iCurrentHostScreenIndex = UIDesktopWidgetWatchdog::screenNumber(pMachineWindow);
+ const QSize frameBufferSize((int)uisession()->frameBuffer(uScreenID)->width(), (int)uisession()->frameBuffer(uScreenID)->height());
+ const QSize screenSize = gpDesktop->screenGeometry(iWantedHostScreenIndex).size();
+
+ /* If that window
+ * 1. shouldn't really be shown or
+ * 2. isn't mapped to some host-screen or
+ * 3. should be located on another host-screen than currently. */
+ if ( !uisession()->isScreenVisible(uScreenID)
+ || !hasHostScreenForGuestScreen(uScreenID)
+ || iWantedHostScreenIndex != iCurrentHostScreenIndex)
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
+ "Ask machine-window #%d to exit native fullscreen\n", (int)uScreenID));
+
+ /* Mark window as invalidated: */
+ m_invalidFullscreenMachineWindows << pMachineWindow;
+
+ /* Ask window to exit 'fullscreen' mode: */
+ emit sigNotifyAboutNativeFullscreenShouldBeExited(pMachineWindow);
+ return;
+ }
+
+ /* If that window
+ * 1. have another frame-buffer size than actually should. */
+ else if (frameBufferSize != screenSize)
+ {
+ LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
+ "Ask machine-window #%d to adjust guest geometry\n", (int)uScreenID));
+
+ /* Just adjust machine-view size if necessary: */
+ pMachineWindow->adjustMachineViewSize();
+ return;
+ }
+ }
+ }
+}
+
+void UIMachineLogicFullscreen::revalidateNativeFullScreen()
+{
+ /* Revalidate all fullscreen windows: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ revalidateNativeFullScreen(pMachineWindow);
+}
+
+/* static */
+void UIMachineLogicFullscreen::nativeHandlerForApplicationActivation(QObject *pObject, const QMap<QString, QString> &userInfo)
+{
+ /* Handle arrived notification: */
+ UIMachineLogicFullscreen *pLogic = qobject_cast<UIMachineLogicFullscreen*>(pObject);
+ AssertPtrReturnVoid(pLogic);
+ {
+ /* Redirect arrived notification: */
+ pLogic->nativeHandlerForApplicationActivation(userInfo);
+ }
+}
+
+void UIMachineLogicFullscreen::nativeHandlerForApplicationActivation(const QMap<QString, QString> &userInfo)
+{
+ /* Make sure we have BundleIdentifier key: */
+ AssertReturnVoid(userInfo.contains("BundleIdentifier"));
+ /* Skip other applications: */
+ QStringList ourBundleIdentifiers;
+ ourBundleIdentifiers << "org.virtualbox.app.VirtualBox";
+ ourBundleIdentifiers << "org.virtualbox.app.VirtualBoxVM";
+ ourBundleIdentifiers << "com.citrix.DesktopPlayerVM";
+ if (!ourBundleIdentifiers.contains(userInfo.value("BundleIdentifier")))
+ return;
+
+ /* Skip if 'screen have separate spaces': */
+ if (screensHaveSeparateSpaces())
+ return;
+
+ /* Skip if there is another than needed user-space is active: */
+ if (!darwinIsOnActiveSpace(machineWindows().first()))
+ return;
+
+ LogRel(("GUI: UIMachineLogicFullscreen::nativeHandlerForApplicationActivation: "
+ "Full-screen application activated\n"));
+
+ /* Revalidate full-screen mode for transient machine-window(s): */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ if (pMachineWindow->screenId() > 0)
+ revalidateNativeFullScreen(pMachineWindow);
+}
+
+/* static */
+void UIMachineLogicFullscreen::nativeHandlerForActiveSpaceChange(QObject *pObject, const QMap<QString, QString> &userInfo)
+{
+ /* Handle arrived notification: */
+ UIMachineLogicFullscreen *pLogic = qobject_cast<UIMachineLogicFullscreen*>(pObject);
+ AssertPtrReturnVoid(pLogic);
+ {
+ /* Redirect arrived notification: */
+ pLogic->nativeHandlerForActiveSpaceChange(userInfo);
+ }
+}
+
+void UIMachineLogicFullscreen::nativeHandlerForActiveSpaceChange(const QMap<QString, QString>&)
+{
+ /* Skip if 'screen have separate spaces': */
+ if (screensHaveSeparateSpaces())
+ return;
+
+ /* Skip if there is another than needed user-space is active: */
+ if (!darwinIsOnActiveSpace(machineWindows().first()))
+ return;
+
+ LogRel(("GUI: UIMachineLogicFullscreen::nativeHandlerForActiveSpaceChange: "
+ "Full-screen user-space activated\n"));
+
+ /* Revalidate full-screen mode for transient machine-window(s): */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ if (pMachineWindow->screenId() > 0)
+ revalidateNativeFullScreen(pMachineWindow);
+}
+#endif /* VBOX_WS_MAC */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineLogicFullscreen.h b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineLogicFullscreen.h
new file mode 100644
index 00000000..27c0b37a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineLogicFullscreen.h
@@ -0,0 +1,179 @@
+/* $Id: UIMachineLogicFullscreen.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogicFullscreen class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_fullscreen_UIMachineLogicFullscreen_h
+#define FEQT_INCLUDED_SRC_runtime_fullscreen_UIMachineLogicFullscreen_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineLogic.h"
+
+/* Other includes: */
+#ifdef VBOX_WS_MAC
+# include <ApplicationServices/ApplicationServices.h>
+#endif /* VBOX_WS_MAC */
+
+/* Forward declarations: */
+class UIMultiScreenLayout;
+
+/** UIMachineLogic subclass used as full-screen machine logic implementation. */
+class UIMachineLogicFullscreen : public UIMachineLogic
+{
+ Q_OBJECT;
+
+#ifdef RT_OS_DARWIN
+signals:
+
+ /** Mac OS X: Notifies listeners about native fullscreen mode should be entered on @a pMachineWindow. */
+ void sigNotifyAboutNativeFullscreenShouldBeEntered(UIMachineWindow *pMachineWindow = 0);
+ /** Mac OS X: Notifies listeners about native fullscreen mode should be exited on @a pMachineWindow. */
+ void sigNotifyAboutNativeFullscreenShouldBeExited(UIMachineWindow *pMachineWindow = 0);
+#endif /* RT_OS_DARWIN */
+
+public:
+
+ /** Constructs full-screen logic passing @a pParent to the base-class.
+ * @param pSession Brings the session UI reference. */
+ UIMachineLogicFullscreen(QObject *pParent, UISession *pSession);
+ /** Destructs full-screen logic. */
+ virtual ~UIMachineLogicFullscreen() RT_OVERRIDE;
+
+ /** Returns an index of host-screen for guest-screen with @a iScreenId specified. */
+ int hostScreenForGuestScreen(int iScreenId) const;
+ /** Returns whether there is a host-screen for guest-screen with @a iScreenId specified. */
+ bool hasHostScreenForGuestScreen(int iScreenId) const;
+
+#ifdef VBOX_WS_MAC
+ /** Returns whether screens have separate spaces. */
+ bool screensHaveSeparateSpaces() const { return m_fScreensHaveSeparateSpaces; }
+#endif /* VBOX_WS_MAC */
+
+protected:
+
+ /* Check if this logic is available: */
+ bool checkAvailability();
+
+ /** Returns machine-window flags for 'Fullscreen' machine-logic and passed @a uScreenId. */
+ virtual Qt::WindowFlags windowFlags(ulong uScreenId) const;
+
+ /** Adjusts machine-window geometry if necessary for 'Fullscreen'. */
+ virtual void adjustMachineWindowsGeometry();
+
+private slots:
+
+#ifdef RT_OS_DARWIN
+ /** Mac OS X: Handles native notification about 'fullscreen' will be entered. */
+ void sltHandleNativeFullscreenWillEnter();
+ /** Mac OS X: Handles native notification about 'fullscreen' entered. */
+ void sltHandleNativeFullscreenDidEnter();
+ /** Mac OS X: Handles native notification about 'fullscreen' will be exited. */
+ void sltHandleNativeFullscreenWillExit();
+ /** Mac OS X: Handles native notification about 'fullscreen' exited. */
+ void sltHandleNativeFullscreenDidExit();
+ /** Mac OS X: Handles native notification about 'fullscreen' fail to enter. */
+ void sltHandleNativeFullscreenFailToEnter();
+
+ /** Mac OS X: Requests visual-state change from 'fullscreen' to 'normal' (window). */
+ void sltChangeVisualStateToNormal();
+ /** Mac OS X: Requests visual-state change from 'fullscreen' to 'seamless'. */
+ void sltChangeVisualStateToSeamless();
+ /** Mac OS X: Requests visual-state change from 'fullscreen' to 'scale'. */
+ void sltChangeVisualStateToScale();
+
+ /** Mac OS X: Checks if some visual-state type was requested. */
+ void sltCheckForRequestedVisualStateType();
+#endif /* RT_OS_DARWIN */
+
+ /* Handler: Console callback stuff: */
+ void sltMachineStateChanged();
+
+ /** Invokes popup-menu. */
+ void sltInvokePopupMenu();
+
+ /** Updates machine-window(s) location/size on screen-layout changes. */
+ void sltScreenLayoutChanged();
+
+ /** Handles guest-screen count change. */
+ virtual void sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo);
+ /** Handles host-screen count change. */
+ virtual void sltHostScreenCountChange();
+ /** Handles host-screen available-area change. */
+ virtual void sltHostScreenAvailableAreaChange();
+ /** Handles additions-state change. */
+ virtual void sltAdditionsStateChanged();
+
+private:
+
+ /* Prepare helpers: */
+ void prepareActionGroups();
+ void prepareActionConnections();
+ void prepareMachineWindows();
+ void prepareMenu();
+
+ /* Cleanup helpers: */
+ void cleanupMenu();
+ void cleanupMachineWindows();
+ void cleanupActionConnections();
+ void cleanupActionGroups();
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Revalidates 'fullscreen' mode for @a pMachineWindow. */
+ void revalidateNativeFullScreen(UIMachineWindow *pMachineWindow);
+ /** Mac OS X: Revalidates 'fullscreen' mode for all windows. */
+ void revalidateNativeFullScreen();
+
+ /** Mac OS X: Proxies native notification about application activation. */
+ static void nativeHandlerForApplicationActivation(QObject *pObject, const QMap<QString, QString> &userInfo);
+ /** Mac OS X: Handles native notification about application activation. */
+ void nativeHandlerForApplicationActivation(const QMap<QString, QString> &userInfo);
+
+ /** Mac OS X: Proxies native notification about active space change. */
+ static void nativeHandlerForActiveSpaceChange(QObject *pObject, const QMap<QString, QString> &userInfo);
+ /** Mac OS X: Handles native notification about active space change. */
+ void nativeHandlerForActiveSpaceChange(const QMap<QString, QString> &userInfo);
+#endif /* VBOX_WS_MAC */
+
+ /** Holds the popup-menu instance. */
+ QMenu *m_pPopupMenu;
+
+ /* Variables: */
+ UIMultiScreenLayout *m_pScreenLayout;
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Holds whether screens have separate spaces. */
+ const bool m_fScreensHaveSeparateSpaces;
+
+ /** Mac OS X: Contains machine-window(s) marked as 'fullscreen'. */
+ QSet<UIMachineWindow*> m_fullscreenMachineWindows;
+ /** Mac OS X: Contains machine-window(s) marked as 'invalid fullscreen'. */
+ QSet<UIMachineWindow*> m_invalidFullscreenMachineWindows;
+#endif /* VBOX_WS_MAC */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_fullscreen_UIMachineLogicFullscreen_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineViewFullscreen.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineViewFullscreen.cpp
new file mode 100644
index 00000000..cfa94ed8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineViewFullscreen.cpp
@@ -0,0 +1,187 @@
+/* $Id: UIMachineViewFullscreen.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineViewFullscreen class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QMainWindow>
+#include <QTimer>
+#ifdef VBOX_WS_MAC
+# include <QMenuBar>
+#endif /* VBOX_WS_MAC */
+
+/* GUI includes: */
+#include "UISession.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogicFullscreen.h"
+#include "UIMachineWindow.h"
+#include "UIMachineViewFullscreen.h"
+#include "UIFrameBuffer.h"
+#include "UIExtraDataManager.h"
+#include "UIDesktopWidgetWatchdog.h"
+
+/* Other VBox includes: */
+#include "VBox/log.h"
+
+/* External includes: */
+#ifdef VBOX_WS_X11
+# include <limits.h>
+#endif /* VBOX_WS_X11 */
+
+
+UIMachineViewFullscreen::UIMachineViewFullscreen(UIMachineWindow *pMachineWindow, ulong uScreenId)
+ : UIMachineView(pMachineWindow, uScreenId)
+ , m_fGuestAutoresizeEnabled(actionPool()->action(UIActionIndexRT_M_View_T_GuestAutoresize)->isChecked())
+{
+}
+
+void UIMachineViewFullscreen::sltAdditionsStateChanged()
+{
+ adjustGuestScreenSize();
+}
+
+bool UIMachineViewFullscreen::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ if (pWatched != 0 && pWatched == machineWindow())
+ {
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ {
+ /* Send guest-resize hint only if top window resizing to required dimension: */
+ QResizeEvent *pResizeEvent = static_cast<QResizeEvent*>(pEvent);
+ if (pResizeEvent->size() != calculateMaxGuestSize())
+ break;
+
+ /* Recalculate maximum guest size: */
+ setMaximumGuestSize();
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return UIMachineView::eventFilter(pWatched, pEvent);
+}
+
+void UIMachineViewFullscreen::prepareCommon()
+{
+ /* Base class common settings: */
+ UIMachineView::prepareCommon();
+
+ /* Setup size-policy: */
+ setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
+ /* Maximum size to sizehint: */
+ setMaximumSize(sizeHint());
+ /* Minimum size is ignored: */
+ setMinimumSize(0, 0);
+ /* No scrollbars: */
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+}
+
+void UIMachineViewFullscreen::prepareFilters()
+{
+ /* Base class filters: */
+ UIMachineView::prepareFilters();
+}
+
+void UIMachineViewFullscreen::prepareConsoleConnections()
+{
+ /* Base class connections: */
+ UIMachineView::prepareConsoleConnections();
+
+ /* Guest additions state-change updater: */
+ connect(uisession(), &UISession::sigAdditionsStateActualChange, this, &UIMachineViewFullscreen::sltAdditionsStateChanged);
+}
+
+void UIMachineViewFullscreen::setGuestAutoresizeEnabled(bool fEnabled)
+{
+ if (m_fGuestAutoresizeEnabled != fEnabled)
+ {
+ m_fGuestAutoresizeEnabled = fEnabled;
+
+ if (m_fGuestAutoresizeEnabled && uisession()->isGuestSupportsGraphics())
+ sltPerformGuestResize();
+ }
+}
+
+void UIMachineViewFullscreen::adjustGuestScreenSize()
+{
+ /* Step 1: Is guest-screen visible? */
+ if (!uisession()->isScreenVisible(screenId()))
+ {
+ LogRel(("GUI: UIMachineViewFullscreen::adjustGuestScreenSize: "
+ "Guest-screen #%d is not visible, adjustment is not required.\n",
+ screenId()));
+ return;
+ }
+ /* Step 2: Is guest-screen auto-resize enabled? */
+ if (!isGuestAutoresizeEnabled())
+ {
+ LogRel(("GUI: UIMachineViewFullscreen::adjustGuestScreenSize: "
+ "Guest-screen #%d auto-resize is disabled, adjustment is not required.\n",
+ screenId()));
+ return;
+ }
+
+ /* What are the desired and requested hints? */
+ const QSize sizeToApply = calculateMaxGuestSize();
+ const QSize desiredSizeHint = scaledBackward(sizeToApply);
+ const QSize requestedSizeHint = requestedGuestScreenSizeHint();
+
+ /* Step 3: Is the guest-screen of another size than necessary? */
+ if (desiredSizeHint == requestedSizeHint)
+ {
+ LogRel(("GUI: UIMachineViewFullscreen::adjustGuestScreenSize: "
+ "Desired hint %dx%d for guest-screen #%d is already in IDisplay, adjustment is not required.\n",
+ desiredSizeHint.width(), desiredSizeHint.height(), screenId()));
+ return;
+ }
+
+ /* Final step: Adjust .. */
+ LogRel(("GUI: UIMachineViewFullscreen::adjustGuestScreenSize: "
+ "Desired hint %dx%d for guest-screen #%d differs from the one in IDisplay, adjustment is required.\n",
+ desiredSizeHint.width(), desiredSizeHint.height(), screenId()));
+ sltPerformGuestResize(sizeToApply);
+ /* And remember the size to know what we are resizing out of when we exit: */
+ uisession()->setLastFullScreenSize(screenId(), scaledForward(desiredSizeHint));
+}
+
+QRect UIMachineViewFullscreen::workingArea() const
+{
+ /* Get corresponding screen: */
+ int iScreen = static_cast<UIMachineLogicFullscreen*>(machineLogic())->hostScreenForGuestScreen(screenId());
+ /* Return available geometry for that screen: */
+ return gpDesktop->screenGeometry(iScreen);
+}
+
+QSize UIMachineViewFullscreen::calculateMaxGuestSize() const
+{
+ return workingArea().size();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineViewFullscreen.h b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineViewFullscreen.h
new file mode 100644
index 00000000..431dc742
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineViewFullscreen.h
@@ -0,0 +1,85 @@
+/* $Id: UIMachineViewFullscreen.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineViewFullscreen class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_fullscreen_UIMachineViewFullscreen_h
+#define FEQT_INCLUDED_SRC_runtime_fullscreen_UIMachineViewFullscreen_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineView.h"
+
+/** UIMachineView subclass used as full-screen machine view implementation. */
+class UIMachineViewFullscreen : public UIMachineView
+{
+ Q_OBJECT;
+
+public:
+
+ /* Fullscreen machine-view constructor: */
+ UIMachineViewFullscreen(UIMachineWindow *pMachineWindow, ulong uScreenId);
+ /* Fullscreen machine-view destructor: */
+ virtual ~UIMachineViewFullscreen() {}
+
+private slots:
+
+ /* Handler: Console callback stuff: */
+ void sltAdditionsStateChanged();
+
+private:
+
+ /* Event handlers: */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+
+ /* Prepare routines: */
+ void prepareCommon();
+ void prepareFilters();
+ void prepareConsoleConnections();
+
+ /* Cleanup routines: */
+ //void cleanupConsoleConnections() {}
+ //void cleanupFilters() {}
+ //void cleanupCommon() {}
+
+ /** Returns whether the guest-screen auto-resize is enabled. */
+ virtual bool isGuestAutoresizeEnabled() const RT_OVERRIDE { return m_fGuestAutoresizeEnabled; }
+ /** Defines whether the guest-screen auto-resize is @a fEnabled. */
+ virtual void setGuestAutoresizeEnabled(bool bEnabled) RT_OVERRIDE;
+
+ /** Adjusts guest-screen size to correspond current <i>working area</i> size. */
+ void adjustGuestScreenSize();
+
+ /* Helpers: Geometry stuff: */
+ QRect workingArea() const;
+ QSize calculateMaxGuestSize() const;
+
+ /* Private variables: */
+ bool m_fGuestAutoresizeEnabled : 1;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_fullscreen_UIMachineViewFullscreen_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineWindowFullscreen.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineWindowFullscreen.cpp
new file mode 100644
index 00000000..1e2cec64
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineWindowFullscreen.cpp
@@ -0,0 +1,612 @@
+/* $Id: UIMachineWindowFullscreen.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindowFullscreen class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QMenu>
+#include <QTimer>
+#ifdef VBOX_WS_WIN
+# include <QWindow>
+#endif
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UISession.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogicFullscreen.h"
+#include "UIMachineWindowFullscreen.h"
+#include "UIMachineView.h"
+#include "UINotificationCenter.h"
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+# include "UIMachineDefs.h"
+# include "UIMiniToolBar.h"
+#elif defined(VBOX_WS_MAC)
+# include "UIFrameBuffer.h"
+# include "VBoxUtils-darwin.h"
+# include "UICocoaApplication.h"
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "CSnapshot.h"
+
+
+UIMachineWindowFullscreen::UIMachineWindowFullscreen(UIMachineLogic *pMachineLogic, ulong uScreenId)
+ : UIMachineWindow(pMachineLogic, uScreenId)
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ , m_pMiniToolBar(0)
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+#ifdef VBOX_WS_MAC
+ , m_fIsInFullscreenTransition(false)
+#endif /* VBOX_WS_MAC */
+ , m_fWasMinimized(false)
+#ifdef VBOX_WS_X11
+ , m_fIsMinimizationRequested(false)
+ , m_fIsMinimized(false)
+#endif
+{
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineWindowFullscreen::handleNativeNotification(const QString &strNativeNotificationName)
+{
+ /* Log all arrived notifications: */
+ LogRel(("UIMachineWindowFullscreen::handleNativeNotification: Notification '%s' received.\n",
+ strNativeNotificationName.toLatin1().constData()));
+
+ /* Handle 'NSWindowWillEnterFullScreenNotification' notification: */
+ if (strNativeNotificationName == "NSWindowWillEnterFullScreenNotification")
+ {
+ LogRel(("UIMachineWindowFullscreen::handleNativeNotification: "
+ "Native fullscreen mode about to enter, notifying listener...\n"));
+ emit sigNotifyAboutNativeFullscreenWillEnter();
+ }
+ /* Handle 'NSWindowDidEnterFullScreenNotification' notification: */
+ else if (strNativeNotificationName == "NSWindowDidEnterFullScreenNotification")
+ {
+ /* Mark window transition complete: */
+ m_fIsInFullscreenTransition = false;
+ LogRel(("UIMachineWindowFullscreen::handleNativeNotification: "
+ "Native fullscreen mode entered, notifying listener...\n"));
+ /* Update console's display viewport and 3D overlay: */
+ machineView()->updateViewport();
+ emit sigNotifyAboutNativeFullscreenDidEnter();
+ }
+ /* Handle 'NSWindowWillExitFullScreenNotification' notification: */
+ else if (strNativeNotificationName == "NSWindowWillExitFullScreenNotification")
+ {
+ LogRel(("UIMachineWindowFullscreen::handleNativeNotification: "
+ "Native fullscreen mode about to exit, notifying listener...\n"));
+ emit sigNotifyAboutNativeFullscreenWillExit();
+ }
+ /* Handle 'NSWindowDidExitFullScreenNotification' notification: */
+ else if (strNativeNotificationName == "NSWindowDidExitFullScreenNotification")
+ {
+ /* Mark window transition complete: */
+ m_fIsInFullscreenTransition = false;
+ LogRel(("UIMachineWindowFullscreen::handleNativeNotification: "
+ "Native fullscreen mode exited, notifying listener...\n"));
+ /* Update console's display viewport and 3D overlay: */
+ machineView()->updateViewport();
+ emit sigNotifyAboutNativeFullscreenDidExit();
+ }
+ /* Handle 'NSWindowDidFailToEnterFullScreenNotification' notification: */
+ else if (strNativeNotificationName == "NSWindowDidFailToEnterFullScreenNotification")
+ {
+ /* Mark window transition complete: */
+ m_fIsInFullscreenTransition = false;
+ LogRel(("UIMachineWindowFullscreen::handleNativeNotification: "
+ "Native fullscreen mode fail to enter, notifying listener...\n"));
+ emit sigNotifyAboutNativeFullscreenFailToEnter();
+ }
+}
+#endif /* VBOX_WS_MAC */
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+void UIMachineWindowFullscreen::sltMachineStateChanged()
+{
+ /* Call to base-class: */
+ UIMachineWindow::sltMachineStateChanged();
+
+ /* Update mini-toolbar: */
+ updateAppearanceOf(UIVisualElement_MiniToolBar);
+}
+
+void UIMachineWindowFullscreen::sltRevokeWindowActivation()
+{
+#ifdef VBOX_WS_X11
+ // WORKAROUND:
+ // We could be asked to minimize already, but just
+ // not yet executed that order to current moment.
+ if (m_fIsMinimizationRequested)
+ return;
+#endif
+
+ /* Make sure window is visible: */
+ if (!isVisible() || isMinimized())
+ return;
+
+ /* Revoke stolen activation: */
+#ifdef VBOX_WS_X11
+ raise();
+#endif /* VBOX_WS_X11 */
+ activateWindow();
+}
+
+void UIMachineWindowFullscreen::sltHandleMiniToolBarAutoHideToggled(bool fEnabled)
+{
+ /* Save mini-toolbar settings: */
+ gEDataManager->setAutoHideMiniToolbar(fEnabled, uiCommon().managedVMUuid());
+}
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#ifdef VBOX_WS_MAC
+void UIMachineWindowFullscreen::sltEnterNativeFullscreen(UIMachineWindow *pMachineWindow)
+{
+ /* Make sure it is NULL or 'this' window passed: */
+ if (pMachineWindow && pMachineWindow != this)
+ return;
+
+ /* Make sure this window has fullscreen logic: */
+ UIMachineLogicFullscreen *pFullscreenLogic = qobject_cast<UIMachineLogicFullscreen*>(machineLogic());
+ AssertPtrReturnVoid(pFullscreenLogic);
+
+ /* Make sure this window should be shown and mapped to host-screen: */
+ if (!uisession()->isScreenVisible(m_uScreenId) ||
+ !pFullscreenLogic->hasHostScreenForGuestScreen(m_uScreenId))
+ return;
+
+ /* Mark window 'transitioned to fullscreen': */
+ m_fIsInFullscreenTransition = true;
+
+ /* Enter native fullscreen mode if necessary: */
+ if ( (pFullscreenLogic->screensHaveSeparateSpaces() || m_uScreenId == 0)
+ && !darwinIsInFullscreenMode(this))
+ darwinToggleFullscreenMode(this);
+}
+
+void UIMachineWindowFullscreen::sltExitNativeFullscreen(UIMachineWindow *pMachineWindow)
+{
+ /* Make sure it is NULL or 'this' window passed: */
+ if (pMachineWindow && pMachineWindow != this)
+ return;
+
+ /* Make sure this window has fullscreen logic: */
+ UIMachineLogicFullscreen *pFullscreenLogic = qobject_cast<UIMachineLogicFullscreen*>(machineLogic());
+ AssertPtrReturnVoid(pFullscreenLogic);
+
+ /* Mark window 'transitioned from fullscreen': */
+ m_fIsInFullscreenTransition = true;
+
+ /* Exit native fullscreen mode if necessary: */
+ if ( (pFullscreenLogic->screensHaveSeparateSpaces() || m_uScreenId == 0)
+ && darwinIsInFullscreenMode(this))
+ darwinToggleFullscreenMode(this);
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineWindowFullscreen::sltShowMinimized()
+{
+#ifdef VBOX_WS_X11
+ /* Remember that we are asked to minimize: */
+ m_fIsMinimizationRequested = true;
+#endif
+
+ showMinimized();
+}
+
+void UIMachineWindowFullscreen::prepareNotificationCenter()
+{
+ if (gpNotificationCenter && (m_uScreenId == 0))
+ gpNotificationCenter->setParent(centralWidget());
+}
+
+void UIMachineWindowFullscreen::prepareVisualState()
+{
+ /* Call to base-class: */
+ UIMachineWindow::prepareVisualState();
+
+ /* The background has to go black: */
+ QPalette palette(centralWidget()->palette());
+ palette.setColor(centralWidget()->backgroundRole(), Qt::black);
+ centralWidget()->setPalette(palette);
+ centralWidget()->setAutoFillBackground(true);
+ setAutoFillBackground(true);
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /* Prepare mini-toolbar: */
+ prepareMiniToolbar();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#ifdef VBOX_WS_MAC
+ /* Make sure this window has fullscreen logic: */
+ UIMachineLogicFullscreen *pFullscreenLogic = qobject_cast<UIMachineLogicFullscreen*>(machineLogic());
+ AssertPtrReturnVoid(pFullscreenLogic);
+ /* Enable fullscreen support for every screen which requires it: */
+ if (pFullscreenLogic->screensHaveSeparateSpaces() || m_uScreenId == 0)
+ darwinEnableFullscreenSupport(this);
+ /* Enable transience support for other screens: */
+ else
+ darwinEnableTransienceSupport(this);
+ /* Register to native fullscreen notifications: */
+ UICocoaApplication::instance()->registerToNotificationOfWindow("NSWindowWillEnterFullScreenNotification", this,
+ UIMachineWindow::handleNativeNotification);
+ UICocoaApplication::instance()->registerToNotificationOfWindow("NSWindowDidEnterFullScreenNotification", this,
+ UIMachineWindow::handleNativeNotification);
+ UICocoaApplication::instance()->registerToNotificationOfWindow("NSWindowWillExitFullScreenNotification", this,
+ UIMachineWindow::handleNativeNotification);
+ UICocoaApplication::instance()->registerToNotificationOfWindow("NSWindowDidExitFullScreenNotification", this,
+ UIMachineWindow::handleNativeNotification);
+ UICocoaApplication::instance()->registerToNotificationOfWindow("NSWindowDidFailToEnterFullScreenNotification", this,
+ UIMachineWindow::handleNativeNotification);
+#endif /* VBOX_WS_MAC */
+}
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+void UIMachineWindowFullscreen::prepareMiniToolbar()
+{
+ /* Make sure mini-toolbar is not restricted: */
+ if (!gEDataManager->miniToolbarEnabled(uiCommon().managedVMUuid()))
+ return;
+
+ /* Create mini-toolbar: */
+ m_pMiniToolBar = new UIMiniToolBar(this,
+ GeometryType_Full,
+ gEDataManager->miniToolbarAlignment(uiCommon().managedVMUuid()),
+ gEDataManager->autoHideMiniToolbar(uiCommon().managedVMUuid()),
+ screenId());
+ AssertPtrReturnVoid(m_pMiniToolBar);
+ {
+ /* Configure mini-toolbar: */
+ m_pMiniToolBar->addMenus(actionPool()->menus());
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigMinimizeAction,
+ this, &UIMachineWindowFullscreen::sltShowMinimized, Qt::QueuedConnection);
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigExitAction,
+ actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &UIAction::trigger);
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigCloseAction,
+ actionPool()->action(UIActionIndex_M_Application_S_Close), &UIAction::trigger);
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigNotifyAboutWindowActivationStolen,
+ this, &UIMachineWindowFullscreen::sltRevokeWindowActivation, Qt::QueuedConnection);
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigAutoHideToggled,
+ this, &UIMachineWindowFullscreen::sltHandleMiniToolBarAutoHideToggled);
+ }
+}
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+void UIMachineWindowFullscreen::cleanupMiniToolbar()
+{
+ /* Delete mini-toolbar: */
+ delete m_pMiniToolBar;
+ m_pMiniToolBar = 0;
+}
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+void UIMachineWindowFullscreen::cleanupVisualState()
+{
+#ifdef VBOX_WS_MAC
+ /* Unregister from native fullscreen notifications: */
+ UICocoaApplication::instance()->unregisterFromNotificationOfWindow("NSWindowWillEnterFullScreenNotification", this);
+ UICocoaApplication::instance()->unregisterFromNotificationOfWindow("NSWindowDidEnterFullScreenNotification", this);
+ UICocoaApplication::instance()->unregisterFromNotificationOfWindow("NSWindowWillExitFullScreenNotification", this);
+ UICocoaApplication::instance()->unregisterFromNotificationOfWindow("NSWindowDidExitFullScreenNotification", this);
+ UICocoaApplication::instance()->unregisterFromNotificationOfWindow("NSWindowDidFailToEnterFullScreenNotification", this);
+#endif /* VBOX_WS_MAC */
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /* Cleanup mini-toolbar: */
+ cleanupMiniToolbar();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+ /* Call to base-class: */
+ UIMachineWindow::cleanupVisualState();
+}
+
+void UIMachineWindowFullscreen::cleanupNotificationCenter()
+{
+ if (gpNotificationCenter && (gpNotificationCenter->parent() == centralWidget()))
+ gpNotificationCenter->setParent(0);
+}
+
+void UIMachineWindowFullscreen::placeOnScreen()
+{
+ /* Make sure this window has fullscreen logic: */
+ UIMachineLogicFullscreen *pFullscreenLogic = qobject_cast<UIMachineLogicFullscreen*>(machineLogic());
+ AssertPtrReturnVoid(pFullscreenLogic);
+
+ /* Get corresponding host-screen: */
+ const int iHostScreen = pFullscreenLogic->hostScreenForGuestScreen(m_uScreenId);
+ /* And corresponding working area: */
+ const QRect workingArea = gpDesktop->screenGeometry(iHostScreen);
+ Q_UNUSED(workingArea);
+
+#if defined(VBOX_WS_MAC)
+
+ /* Move window to the appropriate position: */
+ move(workingArea.topLeft());
+
+ /* Resize window to the appropriate size if it's screen has no own user-space: */
+ if (!pFullscreenLogic->screensHaveSeparateSpaces() && m_uScreenId != 0)
+ resize(workingArea.size());
+ /* Resize the window if we are already in the full screen mode. This covers cases like host-resolution changes while in full screen mode: */
+ else if (darwinIsInFullscreenMode(this))
+ resize(workingArea.size());
+ else
+ {
+ /* Load normal geometry first of all: */
+ QRect geo = gEDataManager->machineWindowGeometry(UIVisualStateType_Normal, m_uScreenId, uiCommon().managedVMUuid());
+ /* If normal geometry is null => use frame-buffer size: */
+ if (geo.isNull())
+ {
+ const UIFrameBuffer *pFrameBuffer = uisession()->frameBuffer(m_uScreenId);
+ geo = QRect(QPoint(0, 0), QSize(pFrameBuffer->width(), pFrameBuffer->height()).boundedTo(workingArea.size()));
+ }
+ /* If normal geometry still null => use default size: */
+ if (geo.isNull())
+ geo = QRect(QPoint(0, 0), QSize(800, 600).boundedTo(workingArea.size()));
+ /* Move window to the center of working-area: */
+ geo.moveCenter(workingArea.center());
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, geo);
+ }
+
+#elif defined(VBOX_WS_WIN)
+
+ /* Map window onto required screen: */
+ windowHandle()->setScreen(qApp->screens().at(iHostScreen));
+ /* Set appropriate window size: */
+ resize(workingArea.size());
+
+#elif defined(VBOX_WS_X11)
+
+ /* Determine whether we should use the native full-screen mode: */
+ const bool fUseNativeFullScreen = NativeWindowSubsystem::X11SupportsFullScreenMonitorsProtocol()
+ && !gEDataManager->legacyFullscreenModeRequested();
+ if (fUseNativeFullScreen)
+ {
+ /* Tell recent window managers which host-screen this window should be mapped to: */
+ NativeWindowSubsystem::X11SetFullScreenMonitor(this, pFullscreenLogic->hostScreenForGuestScreen(m_uScreenId));
+ }
+
+ /* Set appropriate window geometry: */
+ resize(workingArea.size());
+ move(workingArea.topLeft());
+
+#else
+
+# warning "port me"
+
+#endif
+}
+
+void UIMachineWindowFullscreen::showInNecessaryMode()
+{
+ /* Make sure window has fullscreen logic: */
+ UIMachineLogicFullscreen *pFullscreenLogic = qobject_cast<UIMachineLogicFullscreen*>(machineLogic());
+ AssertPtrReturnVoid(pFullscreenLogic);
+
+#if defined(VBOX_WS_MAC)
+
+ /* If window shouldn't be shown or mapped to some host-screen: */
+ if (!uisession()->isScreenVisible(m_uScreenId) ||
+ !pFullscreenLogic->hasHostScreenForGuestScreen(m_uScreenId))
+ {
+ /* Hide window: */
+ hide();
+ }
+ /* If window should be shown and mapped to some host-screen: */
+ else
+ {
+ /* Make sure window have appropriate geometry: */
+ placeOnScreen();
+
+ /* Just show instead of showFullScreen: */
+ show();
+
+ /* Adjust machine-view size if necessary: */
+ adjustMachineViewSize();
+
+ /* Make sure machine-view have focus: */
+ m_pMachineView->setFocus();
+ }
+
+#elif defined(VBOX_WS_WIN)
+
+ /* If window shouldn't be shown or mapped to some host-screen: */
+ if (!uisession()->isScreenVisible(m_uScreenId) ||
+ !pFullscreenLogic->hasHostScreenForGuestScreen(m_uScreenId))
+ {
+ /* Remember whether the window was minimized: */
+ if (isMinimized())
+ m_fWasMinimized = true;
+
+ /* Hide window and reset it's state to NONE: */
+ setWindowState(Qt::WindowNoState);
+ hide();
+ }
+ /* If window should be shown and mapped to some host-screen: */
+ else
+ {
+ /* Check whether window was minimized: */
+ const bool fWasMinimized = isMinimized() && isVisible();
+ /* And reset it's state in such case before exposing: */
+ if (fWasMinimized)
+ setWindowState(Qt::WindowNoState);
+
+ /* Make sure window have appropriate geometry: */
+ placeOnScreen();
+
+ /* Show window: */
+ showFullScreen();
+
+ /* Restore minimized state if necessary: */
+ if (m_fWasMinimized || fWasMinimized)
+ {
+ m_fWasMinimized = false;
+ QMetaObject::invokeMethod(this, "showMinimized", Qt::QueuedConnection);
+ }
+
+ /* Adjust machine-view size if necessary: */
+ adjustMachineViewSize();
+
+ /* Make sure machine-view have focus: */
+ m_pMachineView->setFocus();
+ }
+
+#elif defined(VBOX_WS_X11)
+
+ /* If window shouldn't be shown or mapped to some host-screen: */
+ if (!uisession()->isScreenVisible(m_uScreenId) ||
+ !pFullscreenLogic->hasHostScreenForGuestScreen(m_uScreenId))
+ {
+ /* Remember whether the window was minimized: */
+ if (isMinimized())
+ m_fWasMinimized = true;
+
+ /* Hide window and reset it's state to NONE: */
+ setWindowState(Qt::WindowNoState);
+ hide();
+ }
+ /* If window should be shown and mapped to some host-screen: */
+ else
+ {
+ /* Check whether window was minimized: */
+ const bool fWasMinimized = isMinimized() && isVisible();
+ /* And reset it's state in such case before exposing: */
+ if (fWasMinimized)
+ setWindowState(Qt::WindowNoState);
+
+ /* Show window: */
+ showFullScreen();
+
+ /* Make sure window have appropriate geometry: */
+ placeOnScreen();
+
+ /* Restore full-screen state after placeOnScreen() call: */
+ setWindowState(Qt::WindowFullScreen);
+
+ /* Restore minimized state if necessary: */
+ if (m_fWasMinimized || fWasMinimized)
+ {
+ m_fWasMinimized = false;
+ QMetaObject::invokeMethod(this, "showMinimized", Qt::QueuedConnection);
+ }
+
+ /* Adjust machine-view size if necessary: */
+ adjustMachineViewSize();
+
+ /* Make sure machine-view have focus: */
+ m_pMachineView->setFocus();
+ }
+
+#else
+
+# warning "port me"
+
+#endif
+}
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+void UIMachineWindowFullscreen::updateAppearanceOf(int iElement)
+{
+ /* Call to base-class: */
+ UIMachineWindow::updateAppearanceOf(iElement);
+
+ /* Update mini-toolbar: */
+ if (iElement & UIVisualElement_MiniToolBar)
+ {
+ /* If there is a mini-toolbar: */
+ if (m_pMiniToolBar)
+ {
+ /* Get snapshot(s): */
+ QString strSnapshotName;
+ if (machine().GetSnapshotCount() > 0)
+ {
+ CSnapshot snapshot = machine().GetCurrentSnapshot();
+ strSnapshotName = " (" + snapshot.GetName() + ")";
+ }
+ /* Update mini-toolbar text: */
+ m_pMiniToolBar->setText(machineName() + strSnapshotName);
+ }
+ }
+}
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#ifdef VBOX_WS_X11
+void UIMachineWindowFullscreen::changeEvent(QEvent *pEvent)
+{
+ switch (pEvent->type())
+ {
+ case QEvent::WindowStateChange:
+ {
+ /* Watch for window state changes: */
+ QWindowStateChangeEvent *pChangeEvent = static_cast<QWindowStateChangeEvent*>(pEvent);
+ LogRel2(("GUI: UIMachineWindowFullscreen::changeEvent: Window state changed from %d to %d\n",
+ (int)pChangeEvent->oldState(), (int)windowState()));
+ if ( windowState() == Qt::WindowMinimized
+ && pChangeEvent->oldState() == Qt::WindowNoState
+ && !m_fIsMinimized)
+ {
+ /* Mark window minimized, isMinimized() is not enough due to Qt5vsX11 fight: */
+ LogRel2(("GUI: UIMachineWindowFullscreen::changeEvent: Window minimized\n"));
+ m_fIsMinimized = true;
+ }
+ else
+ if ( windowState() == Qt::WindowNoState
+ && pChangeEvent->oldState() == Qt::WindowMinimized
+ && m_fIsMinimized)
+ {
+ /* Mark window restored, and do manual restoring with showInNecessaryMode(): */
+ LogRel2(("GUI: UIMachineWindowFullscreen::changeEvent: Window restored\n"));
+ m_fIsMinimized = false;
+ /* Remember that we no more asked to minimize: */
+ m_fIsMinimizationRequested = false;
+ showInNecessaryMode();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ UIMachineWindow::changeEvent(pEvent);
+}
+#endif /* VBOX_WS_X11 */
+
+#ifdef VBOX_WS_WIN
+void UIMachineWindowFullscreen::showEvent(QShowEvent *pEvent)
+{
+ /* Expose workaround again,
+ * Qt devs will never fix that it seems.
+ * This time they forget to set 'Mapped'
+ * attribute for initially frame-less window. */
+ setAttribute(Qt::WA_Mapped);
+
+ /* Call to base-class: */
+ UIMachineWindow::showEvent(pEvent);
+}
+#endif /* VBOX_WS_WIN */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineWindowFullscreen.h b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineWindowFullscreen.h
new file mode 100644
index 00000000..82a5a752
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineWindowFullscreen.h
@@ -0,0 +1,164 @@
+/* $Id: UIMachineWindowFullscreen.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindowFullscreen class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_fullscreen_UIMachineWindowFullscreen_h
+#define FEQT_INCLUDED_SRC_runtime_fullscreen_UIMachineWindowFullscreen_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineWindow.h"
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+/* Forward declarations: */
+class UIMiniToolBar;
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+/** UIMachineWindow subclass used as full-screen machine window implementation. */
+class UIMachineWindowFullscreen : public UIMachineWindow
+{
+ Q_OBJECT;
+
+#ifdef RT_OS_DARWIN
+signals:
+ /** Mac OS X: Notifies listener about native 'fullscreen' will be entered. */
+ void sigNotifyAboutNativeFullscreenWillEnter();
+ /** Mac OS X: Notifies listener about native 'fullscreen' entered. */
+ void sigNotifyAboutNativeFullscreenDidEnter();
+ /** Mac OS X: Notifies listener about native 'fullscreen' will be exited. */
+ void sigNotifyAboutNativeFullscreenWillExit();
+ /** Mac OS X: Notifies listener about native 'fullscreen' exited. */
+ void sigNotifyAboutNativeFullscreenDidExit();
+ /** Mac OS X: Notifies listener about native 'fullscreen' fail to enter. */
+ void sigNotifyAboutNativeFullscreenFailToEnter();
+#endif /* RT_OS_DARWIN */
+
+public:
+
+ /** Constructor, passes @a pMachineLogic and @a uScreenId to the UIMachineWindow constructor. */
+ UIMachineWindowFullscreen(UIMachineLogic *pMachineLogic, ulong uScreenId);
+
+protected:
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Handles native notifications @a strNativeNotificationName for 'fullscreen' window. */
+ void handleNativeNotification(const QString &strNativeNotificationName);
+ /** Mac OS X: Returns whether window is in 'fullscreen' transition. */
+ bool isInFullscreenTransition() const { return m_fIsInFullscreenTransition; }
+#endif /* VBOX_WS_MAC */
+
+private slots:
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Handles machine state change event. */
+ void sltMachineStateChanged();
+
+ /** Revokes window activation. */
+ void sltRevokeWindowActivation();
+
+ /** Handles signal about mini-toolbar auto-hide toggled.
+ * @param fEnabled Brings whether auto-hide is enabled. */
+ void sltHandleMiniToolBarAutoHideToggled(bool fEnabled);
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#ifdef RT_OS_DARWIN
+ /** Mac OS X: Commands @a pMachineWindow to enter native 'fullscreen' mode if possible. */
+ void sltEnterNativeFullscreen(UIMachineWindow *pMachineWindow);
+ /** Mac OS X: Commands @a pMachineWindow to exit native 'fullscreen' mode if possible. */
+ void sltExitNativeFullscreen(UIMachineWindow *pMachineWindow);
+#endif /* RT_OS_DARWIN */
+
+ /** Shows window in minimized state. */
+ void sltShowMinimized();
+
+private:
+
+ /** Prepare notification-center routine. */
+ void prepareNotificationCenter();
+ /** Prepare visual-state routine. */
+ void prepareVisualState();
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Prepare mini-toolbar routine. */
+ void prepareMiniToolbar();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Cleanup mini-toolbar routine. */
+ void cleanupMiniToolbar();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+ /** Cleanup visual-state routine. */
+ void cleanupVisualState();
+ /** Cleanup notification-center routine. */
+ void cleanupNotificationCenter();
+
+ /** Updates geometry according to visual-state. */
+ void placeOnScreen();
+ /** Updates visibility according to visual-state. */
+ void showInNecessaryMode();
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Common update routine. */
+ void updateAppearanceOf(int iElement);
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#ifdef VBOX_WS_X11
+ /** X11: Handles @a pEvent about state change. */
+ void changeEvent(QEvent *pEvent);
+#endif
+
+#ifdef VBOX_WS_WIN
+ /** Win: Handles show @a pEvent. */
+ void showEvent(QShowEvent *pEvent);
+#endif
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Holds the mini-toolbar instance. */
+ UIMiniToolBar *m_pMiniToolBar;
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#ifdef VBOX_WS_MAC
+ /** Mac OS X: Reflects whether window is in 'fullscreen' transition. */
+ bool m_fIsInFullscreenTransition;
+ /** Mac OS X: Allows 'fullscreen' API access: */
+ friend class UIMachineLogicFullscreen;
+#endif /* VBOX_WS_MAC */
+
+ /** Holds whether the window was minimized before became hidden.
+ * Used to restore minimized state when the window shown again. */
+ bool m_fWasMinimized;
+#ifdef VBOX_WS_X11
+ /** X11: Holds whether the window minimization is currently requested.
+ * Used to prevent accidentally restoring to full-screen state. */
+ bool m_fIsMinimizationRequested;
+ /** X11: Holds whether the window is currently minimized.
+ * Used to restore full-screen state when the window restored again. */
+ bool m_fIsMinimized;
+#endif
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_fullscreen_UIMachineWindowFullscreen_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/information/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/runtime/information/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/information/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationConfiguration.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationConfiguration.cpp
new file mode 100644
index 00000000..56e46658
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationConfiguration.cpp
@@ -0,0 +1,283 @@
+/* $Id: UIInformationConfiguration.cpp $ */
+/** @file
+ * VBox Qt GUI - UIInformationConfiguration class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* COM includes: */
+
+/* Qt includes: */
+#include <QAction>
+#include <QApplication>
+#include <QClipboard>
+#include <QHeaderView>
+#include <QMenu>
+#include <QTableWidget>
+#include <QTextDocument>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIDetailsGenerator.h"
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIInformationConfiguration.h"
+#include "UIVirtualBoxEventHandler.h"
+
+UIInformationConfiguration::UIInformationConfiguration(QWidget *pParent, const CMachine &machine, const CConsole &console)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_machine(machine)
+ , m_console(console)
+ , m_pMainLayout(0)
+ , m_pTableWidget(0)
+ , m_pCopyWholeTableAction(0)
+ , m_iColumCount(3)
+ , m_iRowLeftMargin(0.2 * qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin))
+ , m_iRowTopMargin(0.2 * qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin))
+ , m_iRowRightMargin(0.2 * qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin))
+ , m_iRowBottomMargin(0.2 * qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin))
+{
+ prepareObjects();
+ retranslateUi();
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange,
+ this, &UIInformationConfiguration::sltMachineDataChanged);
+ connect(&uiCommon(), &UICommon::sigMediumEnumerationFinished,
+ this, &UIInformationConfiguration::sltMachineDataChanged);
+}
+
+void UIInformationConfiguration::sltMachineDataChanged()
+{
+ createTableItems();
+}
+
+void UIInformationConfiguration::sltHandleTableContextMenuRequest(const QPoint &position)
+{
+ if (!m_pCopyWholeTableAction)
+ return;
+
+ QMenu menu(this);
+ menu.addAction(m_pCopyWholeTableAction);
+ menu.exec(mapToGlobal(position));
+}
+
+void UIInformationConfiguration::sltCopyTableToClipboard()
+{
+ QClipboard *pClipboard = QApplication::clipboard();
+ if (!pClipboard)
+ return;
+ pClipboard->setText(tableData(), QClipboard::Clipboard);
+}
+
+void UIInformationConfiguration::retranslateUi()
+{
+ m_strGeneralTitle = QApplication::translate("UIVMInformationDialog", "General");
+ m_strSystemTitle = QApplication::translate("UIVMInformationDialog", "System");
+ m_strDisplayTitle = QApplication::translate("UIVMInformationDialog", "Display");
+ m_strStorageTitle = QApplication::translate("UIVMInformationDialog", "Storage");
+ m_strAudioTitle = QApplication::translate("UIVMInformationDialog", "Audio");
+ m_strNetworkTitle = QApplication::translate("UIVMInformationDialog", "Network");
+ m_strSerialPortsTitle = QApplication::translate("UIVMInformationDialog", "Serial Ports");
+ m_strUSBTitle = QApplication::translate("UIVMInformationDialog", "USB");
+ m_strSharedFoldersTitle = QApplication::translate("UIVMInformationDialog", "Shared Folders");
+ if (m_pCopyWholeTableAction)
+ m_pCopyWholeTableAction->setText(QApplication::translate("UIVMInformationDialog", "Copy All"));
+ createTableItems();
+}
+
+void UIInformationConfiguration::createTableItems()
+{
+ if (!m_pTableWidget)
+ return;
+ resetTable();
+ QFontMetrics fontMetrics(m_pTableWidget->font());
+ int iMaxColumn1Length = 0;
+ /* General section: */
+ insertTitleRow(m_strGeneralTitle, UIIconPool::iconSet(":/machine_16px.png"), fontMetrics);
+ insertInfoRows(UIDetailsGenerator::generateMachineInformationGeneral(m_machine,
+ UIExtraDataMetaDefs::DetailsElementOptionTypeGeneral_Default),
+ fontMetrics, iMaxColumn1Length);
+
+ /* System section: */
+ insertTitleRow(m_strSystemTitle, UIIconPool::iconSet(":/chipset_16px.png"), fontMetrics);
+ insertInfoRows(UIDetailsGenerator::generateMachineInformationSystem(m_machine,
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSystem_Default),
+ fontMetrics, iMaxColumn1Length);
+
+ /* Display section: */
+ insertTitleRow(m_strDisplayTitle, UIIconPool::iconSet(":/vrdp_16px.png"), fontMetrics);
+ insertInfoRows(UIDetailsGenerator::generateMachineInformationDisplay(m_machine,
+ UIExtraDataMetaDefs::DetailsElementOptionTypeDisplay_Default),
+ fontMetrics, iMaxColumn1Length);
+
+ /* Storage section: */
+ insertTitleRow(m_strStorageTitle, UIIconPool::iconSet(":/hd_16px.png"), fontMetrics);
+ insertInfoRows(UIDetailsGenerator::generateMachineInformationStorage(m_machine,
+ UIExtraDataMetaDefs::DetailsElementOptionTypeStorage_Default),
+ fontMetrics, iMaxColumn1Length);
+
+ /* Audio section: */
+ insertTitleRow(m_strAudioTitle, UIIconPool::iconSet(":/sound_16px.png"), fontMetrics);
+ insertInfoRows(UIDetailsGenerator::generateMachineInformationAudio(m_machine,
+ UIExtraDataMetaDefs::DetailsElementOptionTypeAudio_Default),
+ fontMetrics, iMaxColumn1Length);
+
+ /* Network section: */
+ insertTitleRow(m_strNetworkTitle, UIIconPool::iconSet(":/nw_16px.png"), fontMetrics);
+ insertInfoRows(UIDetailsGenerator::generateMachineInformationNetwork(m_machine,
+ UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Default),
+ fontMetrics, iMaxColumn1Length);
+
+ /* Serial port section: */
+ insertTitleRow(m_strSerialPortsTitle, UIIconPool::iconSet(":/serial_port_16px.png"), fontMetrics);
+ insertInfoRows(UIDetailsGenerator::generateMachineInformationSerial(m_machine,
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSerial_Default),
+ fontMetrics, iMaxColumn1Length);
+
+ /* USB section: */
+ insertTitleRow(m_strUSBTitle, UIIconPool::iconSet(":/usb_16px.png"), fontMetrics);
+ insertInfoRows(UIDetailsGenerator::generateMachineInformationUSB(m_machine,
+ UIExtraDataMetaDefs::DetailsElementOptionTypeUsb_Default),
+ fontMetrics, iMaxColumn1Length);
+
+ /* Share folders section: */
+ insertTitleRow(m_strSharedFoldersTitle, UIIconPool::iconSet(":/sf_16px.png"), fontMetrics);
+ insertInfoRows(UIDetailsGenerator::generateMachineInformationSharedFolders(m_machine,
+ UIExtraDataMetaDefs::DetailsElementOptionTypeSharedFolders_Default),
+ fontMetrics, iMaxColumn1Length);
+
+ m_pTableWidget->resizeColumnToContents(0);
+ /* Resize the column 1 a bit larger than the max string if contains: */
+ m_pTableWidget->setColumnWidth(1, 1.5 * iMaxColumn1Length);
+ m_pTableWidget->resizeColumnToContents(2);
+ m_pTableWidget->horizontalHeader()->setStretchLastSection(true);
+}
+
+void UIInformationConfiguration::prepareObjects()
+{
+ /* Create layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (!m_pMainLayout)
+ return;
+ m_pMainLayout->setSpacing(0);
+
+ m_pTableWidget = new QTableWidget;
+ if (m_pTableWidget)
+ {
+ /* Configure the table by hiding the headers etc.: */
+ m_pTableWidget->setColumnCount(m_iColumCount);
+ m_pTableWidget->setAlternatingRowColors(true);
+ m_pTableWidget->verticalHeader()->hide();
+ m_pTableWidget->horizontalHeader()->hide();
+ m_pTableWidget->setShowGrid(false);
+ m_pTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ m_pTableWidget->setFocusPolicy(Qt::NoFocus);
+ m_pTableWidget->setSelectionMode(QAbstractItemView::NoSelection);
+ m_pTableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_pTableWidget, &QTableWidget::customContextMenuRequested,
+ this, &UIInformationConfiguration::sltHandleTableContextMenuRequest);
+ m_pMainLayout->addWidget(m_pTableWidget);
+ }
+ m_pCopyWholeTableAction = new QAction(this);
+ connect(m_pCopyWholeTableAction, &QAction::triggered,
+ this, &UIInformationConfiguration::sltCopyTableToClipboard);
+}
+
+void UIInformationConfiguration::insertInfoRows(const UITextTable &table, const QFontMetrics &fontMetrics, int &iMaxColumn1Length)
+{
+ foreach (const UITextTableLine &line, table)
+ {
+ insertInfoRow(removeHtmlFromString(line.string1()),
+ removeHtmlFromString(line.string2()),
+ fontMetrics, iMaxColumn1Length);
+ }
+}
+
+void UIInformationConfiguration::insertTitleRow(const QString &strTitle, const QIcon &icon, const QFontMetrics &fontMetrics)
+{
+ int iRow = m_pTableWidget->rowCount();
+ m_pTableWidget->insertRow(iRow);
+ QSize iconSize;
+ icon.actualSize(iconSize);
+ m_pTableWidget->setRowHeight(iRow,
+ qMax(fontMetrics.height() + m_iRowTopMargin + m_iRowBottomMargin, iconSize.height()));
+ m_pTableWidget->setItem(iRow, 0, new QTableWidgetItem(icon, ""));
+ QTableWidgetItem *pTitleItem = new QTableWidgetItem(strTitle);
+ QFont font = pTitleItem->font();
+ font.setBold(true);
+ pTitleItem->setFont(font);
+ m_pTableWidget->setItem(iRow, 1, pTitleItem);
+}
+
+void UIInformationConfiguration::insertInfoRow(const QString strText1, const QString &strText2,
+ const QFontMetrics &fontMetrics, int &iMaxColumn1Length)
+{
+ int iRow = m_pTableWidget->rowCount();
+ m_pTableWidget->insertRow(iRow);
+ m_pTableWidget->setRowHeight(iRow, fontMetrics.height() + m_iRowTopMargin + m_iRowBottomMargin);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ iMaxColumn1Length = qMax(iMaxColumn1Length, fontMetrics.horizontalAdvance(strText1));
+#else
+ iMaxColumn1Length = qMax(iMaxColumn1Length, fontMetrics.width(strText1));
+#endif
+ m_pTableWidget->setItem(iRow, 1, new QTableWidgetItem(strText1));
+ m_pTableWidget->setItem(iRow, 2, new QTableWidgetItem(strText2));
+}
+
+void UIInformationConfiguration::resetTable()
+{
+ if (m_pTableWidget)
+ {
+ m_pTableWidget->clear();
+ m_pTableWidget->setRowCount(0);
+ m_pTableWidget->setColumnCount(m_iColumCount);
+ }
+}
+
+QString UIInformationConfiguration::removeHtmlFromString(const QString &strOriginal)
+{
+ QTextDocument textDocument;
+ textDocument.setHtml(strOriginal);
+ return textDocument.toPlainText();
+}
+
+QString UIInformationConfiguration::tableData() const
+{
+ AssertReturn(m_pTableWidget, QString());
+ AssertReturn(m_pTableWidget->columnCount() == 3, QString());
+ QStringList data;
+ for (int i = 0; i < m_pTableWidget->rowCount(); ++i)
+ {
+ /* Skip the first column as it contains only icon and no text: */
+ QTableWidgetItem *pItem = m_pTableWidget->item(i, 1);
+ QString strColumn1 = pItem ? pItem->text() : QString();
+ pItem = m_pTableWidget->item(i, 2);
+ QString strColumn2 = pItem ? pItem->text() : QString();
+ if (strColumn2.isEmpty())
+ data << strColumn1;
+ else
+ data << strColumn1 << ": " << strColumn2;
+ data << "\n";
+ }
+ return data.join(QString());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationConfiguration.h b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationConfiguration.h
new file mode 100644
index 00000000..7bdd0648
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationConfiguration.h
@@ -0,0 +1,114 @@
+/* $Id: UIInformationConfiguration.h $ */
+/** @file
+ * VBox Qt GUI - UIInformationConfiguration class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_information_UIInformationConfiguration_h
+#define FEQT_INCLUDED_SRC_runtime_information_UIInformationConfiguration_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuest.h"
+#include "CMachine.h"
+#include "CConsole.h"
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UITextTable.h"
+
+
+/* Forward declarations: */
+class QTableWidget;
+class QTableWidgetItem;
+class QTextDocument;
+class QVBoxLayout;
+
+class UIInformationConfiguration : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs information-tab passing @a pParent to the QWidget base-class constructor.
+ * @param machine is machine reference.
+ * @param console is machine console reference. */
+ UIInformationConfiguration(QWidget *pParent, const CMachine &machine, const CConsole &console);
+
+protected:
+
+ void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ void sltMachineDataChanged();
+ void sltHandleTableContextMenuRequest(const QPoint &position);
+ void sltCopyTableToClipboard();
+
+private:
+
+ void createTableItems();
+ void prepareObjects();
+ void insertTitleRow(const QString &strTitle, const QIcon &icon, const QFontMetrics &fontMetrics);
+ void insertInfoRows(const UITextTable &table, const QFontMetrics &fontMetrics, int &iMaxColumn1Length);
+ void insertInfoRow(const QString strText1, const QString &strText2,
+ const QFontMetrics &fontMetrics, int &iMaxColumn1Length);
+ void resetTable();
+ QString removeHtmlFromString(const QString &strOriginal);
+ QString tableData() const;
+
+ CMachine m_machine;
+ CConsole m_console;
+ QVBoxLayout *m_pMainLayout;
+ QTableWidget *m_pTableWidget;
+ QAction *m_pCopyWholeTableAction;
+
+ const int m_iColumCount;
+ const int m_iRowLeftMargin;
+ const int m_iRowTopMargin;
+ const int m_iRowRightMargin;
+ const int m_iRowBottomMargin;
+
+
+ /** @name Cached translated string.
+ * @{ */
+ QString m_strGeneralTitle;
+ QString m_strSystemTitle;
+ QString m_strDisplayTitle;
+ QString m_strStorageTitle;
+ QString m_strAudioTitle;
+ QString m_strNetworkTitle;
+ QString m_strSerialPortsTitle;
+ QString m_strUSBTitle;
+ QString m_strSharedFoldersTitle;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_information_UIInformationConfiguration_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationRuntime.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationRuntime.cpp
new file mode 100644
index 00000000..138eb9f8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationRuntime.cpp
@@ -0,0 +1,596 @@
+/* $Id: UIInformationRuntime.cpp $ */
+/** @file
+ * VBox Qt GUI - UIInformationRuntime class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QApplication>
+#include <QClipboard>
+#include <QHeaderView>
+#include <QMenu>
+#include <QVBoxLayout>
+#include <QTableWidget>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIIconPool.h"
+#include "UIInformationRuntime.h"
+#include "UISession.h"
+
+/* COM includes: */
+#include "CGraphicsAdapter.h"
+#include "CGuest.h"
+#include "CVRDEServerInfo.h"
+
+enum InfoRow
+{
+ InfoRow_Title = 0,
+ InfoRow_Resolution,
+ InfoRow_Uptime,
+ InfoRow_ClipboardMode,
+ InfoRow_DnDMode,
+ InfoRow_ExecutionEngine,
+ InfoRow_NestedPaging,
+ InfoRow_UnrestrictedExecution,
+ InfoRow_Paravirtualization,
+ InfoRow_GuestAdditions,
+ InfoRow_GuestOSType,
+ InfoRow_RemoteDesktop,
+ InfoRow_Max
+};
+
+/*********************************************************************************************************************************
+* UIRuntimeInfoWidget definition. *
+*********************************************************************************************************************************/
+/** A QTablWidget extention to show some runtime attributes. Some of these are updated in response to IConsole events. Uptime field
+ * is updated thru a QTimer. */
+class UIRuntimeInfoWidget : public QIWithRetranslateUI<QTableWidget>
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIRuntimeInfoWidget(QWidget *pParent, const CMachine &machine, const CConsole &console);
+ void updateScreenInfo(int iScreenId = -1);
+ void updateGAsVersion();
+ void updateVRDE();
+ void updateClipboardMode(KClipboardMode enmMode = KClipboardMode_Max);
+ void updateDnDMode(KDnDMode enmMode = KDnDMode_Max);
+ QString tableData() const;
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ void sltTimeout();
+
+private:
+
+ void createInfoRows();
+ void updateUpTime();
+ void updateTitleRow();
+ void updateOSTypeRow();
+ void updateVirtualizationInfo();
+
+ /** Searches the table for the @p item of enmLine and replaces its text. if not found inserts a new
+ * row to the end of the table. Assumes only one line of the @p enmLine exists. */
+ void updateInfoRow(InfoRow enmLine, const QString &strColumn0, const QString &strColumn1);
+ QString screenResolution(int iScreenId);
+ /** Creates to QTableWidgetItems of the @enmInfoRow using the @p strLabel and @p strInfo and inserts it
+ * to the row @p iRow. If @p iRow is -1 then the items inserted to the end of the table. */
+ void insertInfoRow(InfoRow enmInfoRow, const QString& strLabel, const QString &strInfo, int iRow = -1);
+ void computeMinimumWidth();
+
+ CMachine m_machine;
+ CConsole m_console;
+
+ /** @name Cached translated strings.
+ * @{ */
+ QString m_strTableTitle;
+ QString m_strScreenResolutionLabel;
+ QString m_strMonitorTurnedOff;
+ QString m_strUptimeLabel;
+ QString m_strClipboardModeLabel;
+ QString m_strDragAndDropLabel;
+ QString m_strExcutionEngineLabel;
+ QString m_strNestedPagingLabel;
+ QString m_strUnrestrictedExecutionLabel;
+ QString m_strParavirtualizationLabel;
+ QString m_strNestedPagingActive;
+ QString m_strNestedPagingInactive;
+ QString m_strUnrestrictedExecutionActive;
+ QString m_strUnrestrictedExecutionInactive;
+ QString m_strVRDEPortNotAvailable;
+ QString m_strGuestAdditionsLabel;
+ QString m_strGuestOSTypeLabel;
+ QString m_strRemoteDesktopLabel;
+ QString m_strExecutionEngineNotSet;
+ QString m_strOSNotDetected;
+ QString m_strGANotDetected;
+ /** @} */
+
+ int m_iFontHeight;
+ /** Computed by computing the maximum length line. Used to avoid having horizontal scroll bars. */
+ int m_iMinimumWidth;
+ QVector<QString> m_screenResolutions;
+ QVector<QString*> m_labels;
+ QTimer *m_pTimer;
+};
+
+/*********************************************************************************************************************************
+* UIRuntimeInfoWidget implementation. *
+*********************************************************************************************************************************/
+
+UIRuntimeInfoWidget::UIRuntimeInfoWidget(QWidget *pParent, const CMachine &machine, const CConsole &console)
+ : QIWithRetranslateUI<QTableWidget>(pParent)
+ , m_machine(machine)
+ , m_console(console)
+ , m_iMinimumWidth(0)
+ , m_pTimer(0)
+{
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setAlternatingRowColors(true);
+ m_iFontHeight = QFontMetrics(font()).height();
+
+ setColumnCount(2);
+ verticalHeader()->hide();
+ horizontalHeader()->hide();
+ setShowGrid(false);
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
+ setFocusPolicy(Qt::NoFocus);
+ setSelectionMode(QAbstractItemView::NoSelection);
+
+ m_pTimer = new QTimer(this);
+ if (m_pTimer)
+ {
+ connect(m_pTimer, &QTimer::timeout, this, &UIRuntimeInfoWidget::sltTimeout);
+ m_pTimer->start(5000);
+ }
+
+ m_labels << &m_strScreenResolutionLabel
+ << &m_strUptimeLabel
+ << &m_strDragAndDropLabel
+ << &m_strExcutionEngineLabel
+ << &m_strNestedPagingLabel
+ << &m_strUnrestrictedExecutionLabel
+ << &m_strParavirtualizationLabel
+ << &m_strGuestAdditionsLabel
+ << &m_strGuestOSTypeLabel
+ << &m_strRemoteDesktopLabel;
+
+
+ retranslateUi();
+ computeMinimumWidth();
+}
+
+void UIRuntimeInfoWidget::retranslateUi()
+{
+ m_strTableTitle = QApplication::translate("UIVMInformationDialog", "Runtime Attributes");
+ m_strScreenResolutionLabel = QApplication::translate("UIVMInformationDialog", "Screen Resolution");
+ m_strMonitorTurnedOff = QApplication::translate("UIVMInformationDialog", "turned off", "Screen");
+ m_strUptimeLabel = QApplication::translate("UIVMInformationDialog", "VM Uptime");
+ m_strClipboardModeLabel = QApplication::translate("UIVMInformationDialog", "Clipboard Mode");
+ m_strDragAndDropLabel = QApplication::translate("UIVMInformationDialog", "Drag and Drop Mode");
+ m_strExcutionEngineLabel = QApplication::translate("UIVMInformationDialog", "VM Execution Engine");
+ m_strNestedPagingLabel = QApplication::translate("UIVMInformationDialog", "Nested Paging");
+ m_strUnrestrictedExecutionLabel = QApplication::translate("UIVMInformationDialog", "Unrestricted Execution");
+ m_strParavirtualizationLabel = QApplication::translate("UIVMInformationDialog", "Paravirtualization Interface");
+ m_strNestedPagingActive = QApplication::translate("UIVMInformationDialog", "Active", "Nested Paging");
+ m_strNestedPagingInactive = QApplication::translate("UIVMInformationDialog", "Inactive", "Nested Paging");
+ m_strUnrestrictedExecutionActive = QApplication::translate("UIVMInformationDialog", "Active", "Unrestricted Execution");
+ m_strUnrestrictedExecutionInactive = QApplication::translate("UIVMInformationDialog", "Inactive", "Unrestricted Execution");
+ m_strVRDEPortNotAvailable = QApplication::translate("UIVMInformationDialog", "Not Available", "VRDE Port");
+ m_strGuestAdditionsLabel = QApplication::translate("UIVMInformationDialog", "Guest Additions");
+ m_strGuestOSTypeLabel = QApplication::translate("UIVMInformationDialog", "Guest OS Type");
+ m_strRemoteDesktopLabel = QApplication::translate("UIVMInformationDialog", "Remote Desktop Server Port");
+ m_strExecutionEngineNotSet = QApplication::translate("UIVMInformationDialog", "not set", "Execution Engine");
+ m_strOSNotDetected = QApplication::translate("UIVMInformationDialog", "Not Detected", "Guest OS Type");
+ m_strGANotDetected = QApplication::translate("UIVMInformationDialog", "Not Detected", "Guest Additions Version");
+
+ QString* strLongest = 0;
+ foreach (QString *strLabel, m_labels)
+ {
+ if (!strLongest)
+ strLongest = strLabel;
+ if (strLabel && strLongest->length() < strLabel->length())
+ strLongest = strLabel;
+ }
+ QFontMetrics fontMetrics(font());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ setColumnWidth(0, 1.5 * fontMetrics.horizontalAdvance(*strLongest));
+#else
+ setColumnWidth(0, 1.5 * fontMetrics.width(*strLongest));
+#endif
+
+ /* Make the API calls and populate the table: */
+ createInfoRows();
+}
+
+void UIRuntimeInfoWidget::insertInfoRow(InfoRow enmInfoRow, const QString& strLabel, const QString &strInfo, int iRow /* = -1 */)
+{
+ int iNewRow = rowCount();
+ if (iRow != -1 && iRow <= iNewRow)
+ iNewRow = iRow;
+ insertRow(iNewRow);
+ setItem(iNewRow, 1, new QTableWidgetItem(strLabel, enmInfoRow));
+ setItem(iNewRow, 2, new QTableWidgetItem(strInfo, enmInfoRow));
+ int iMargin = 0.2 * qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
+ setRowHeight(iNewRow, 2 * iMargin + m_iFontHeight);
+}
+
+QString UIRuntimeInfoWidget::screenResolution(int iScreenID)
+{
+ /* Determine resolution: */
+ ULONG uWidth = 0;
+ ULONG uHeight = 0;
+ ULONG uBpp = 0;
+ LONG xOrigin = 0;
+ LONG yOrigin = 0;
+ KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
+ m_console.GetDisplay().GetScreenResolution(iScreenID, uWidth, uHeight, uBpp, xOrigin, yOrigin, monitorStatus);
+ QString strResolution = QString("%1x%2").arg(uWidth).arg(uHeight);
+ if (uBpp)
+ strResolution += QString("x%1").arg(uBpp);
+ strResolution += QString(" @%1,%2").arg(xOrigin).arg(yOrigin);
+ if (monitorStatus == KGuestMonitorStatus_Disabled)
+ {
+ strResolution += QString(" ");
+ strResolution += m_strMonitorTurnedOff;
+ }
+ return strResolution;
+}
+
+void UIRuntimeInfoWidget::sltTimeout()
+{
+ updateUpTime();
+}
+
+void UIRuntimeInfoWidget::updateScreenInfo(int iScreenID /* = -1 */)
+{
+ ULONG uGuestScreens = m_machine.GetGraphicsAdapter().GetMonitorCount();
+ m_screenResolutions.resize(uGuestScreens);
+ if (iScreenID != -1 && iScreenID >= (int)uGuestScreens)
+ return;
+ if (iScreenID == -1)
+ {
+ for (ULONG iScreen = 0; iScreen < uGuestScreens; ++iScreen)
+ m_screenResolutions[iScreen] = screenResolution(iScreen);
+ }
+ else
+ m_screenResolutions[iScreenID] = screenResolution(iScreenID);
+ /* Delete all the rows (not only the updated screen's row) and reinsert them: */
+ int iRowCount = rowCount();
+ for (int i = iRowCount - 1; i >= 0; --i)
+ {
+ QTableWidgetItem *pItem = item(i, 1);
+ if (pItem && pItem->type() == InfoRow_Resolution)
+ removeRow(i);
+ }
+ for (ULONG iScreen = 0; iScreen < uGuestScreens; ++iScreen)
+ {
+ QString strLabel = uGuestScreens > 1 ?
+ QString("%1 %2").arg(m_strScreenResolutionLabel).arg(QString::number(iScreen)) :
+ QString("%1").arg(m_strScreenResolutionLabel);
+ /* Insert the screen resolution row at the top of the table. Row 0 is the title row: */
+ insertInfoRow(InfoRow_Resolution, strLabel, m_screenResolutions[iScreen], iScreen + 1);
+ }
+ resizeColumnToContents(1);
+ horizontalHeader()->setStretchLastSection(true);
+}
+
+void UIRuntimeInfoWidget::updateUpTime()
+{
+ CMachineDebugger debugger = m_console.GetDebugger();
+ uint32_t uUpSecs = (debugger.GetUptime() / 5000) * 5;
+ char szUptime[32];
+ uint32_t uUpDays = uUpSecs / (60 * 60 * 24);
+ uUpSecs -= uUpDays * 60 * 60 * 24;
+ uint32_t uUpHours = uUpSecs / (60 * 60);
+ uUpSecs -= uUpHours * 60 * 60;
+ uint32_t uUpMins = uUpSecs / 60;
+ uUpSecs -= uUpMins * 60;
+ RTStrPrintf(szUptime, sizeof(szUptime), "%dd %02d:%02d:%02d",
+ uUpDays, uUpHours, uUpMins, uUpSecs);
+ QString strUptime = QString(szUptime);
+ updateInfoRow(InfoRow_Uptime, QString("%1").arg(m_strUptimeLabel), strUptime);
+}
+
+void UIRuntimeInfoWidget::updateTitleRow()
+{
+ /* Add the title row always as 0th row: */
+ QTableWidgetItem *pTitleIcon = new QTableWidgetItem(UIIconPool::iconSet(":/state_running_16px.png"), "", InfoRow_Title);
+ QTableWidgetItem *pTitleItem = new QTableWidgetItem(m_strTableTitle, InfoRow_Title);
+ QFont titleFont(font());
+ titleFont.setBold(true);
+ pTitleItem->setFont(titleFont);
+ if (rowCount() < 1)
+ insertRow(0);
+ setItem(0, 0, pTitleIcon);
+ setItem(0, 1, pTitleItem);
+ resizeColumnToContents(0);
+}
+
+void UIRuntimeInfoWidget::updateOSTypeRow()
+{
+ QString strOSType = m_console.GetGuest().GetOSTypeId();
+ if (strOSType.isEmpty())
+ strOSType = m_strOSNotDetected;
+ else
+ strOSType = uiCommon().vmGuestOSTypeDescription(strOSType);
+ updateInfoRow(InfoRow_GuestOSType, QString("%1").arg(m_strGuestOSTypeLabel), strOSType);
+}
+
+void UIRuntimeInfoWidget::updateVirtualizationInfo()
+{
+
+ /* Determine virtualization attributes: */
+ CMachineDebugger debugger = m_console.GetDebugger();
+
+ QString strExecutionEngine;
+ switch (debugger.GetExecutionEngine())
+ {
+ case KVMExecutionEngine_HwVirt:
+ strExecutionEngine = "VT-x/AMD-V"; /* no translation */
+ break;
+ case KVMExecutionEngine_Emulated:
+ strExecutionEngine = "IEM"; /* no translation */
+ break;
+ case KVMExecutionEngine_NativeApi:
+ strExecutionEngine = "native API"; /* no translation */
+ break;
+ default:
+ AssertFailed();
+ RT_FALL_THRU();
+ case KVMExecutionEngine_NotSet:
+ strExecutionEngine = m_strExecutionEngineNotSet;
+ break;
+ }
+ QString strNestedPaging = debugger.GetHWVirtExNestedPagingEnabled() ?
+ m_strNestedPagingActive : m_strNestedPagingInactive;
+ QString strUnrestrictedExecution = debugger.GetHWVirtExUXEnabled() ?
+ m_strUnrestrictedExecutionActive : m_strUnrestrictedExecutionInactive;
+ QString strParavirtProvider = gpConverter->toString(m_machine.GetEffectiveParavirtProvider());
+
+ updateInfoRow(InfoRow_ExecutionEngine, QString("%1").arg(m_strExcutionEngineLabel), strExecutionEngine);
+ updateInfoRow(InfoRow_NestedPaging, QString("%1").arg(m_strNestedPagingLabel), strNestedPaging);
+ updateInfoRow(InfoRow_UnrestrictedExecution, QString("%1").arg(m_strUnrestrictedExecutionLabel), strUnrestrictedExecution);
+ updateInfoRow(InfoRow_Paravirtualization, QString("%1").arg(m_strParavirtualizationLabel), strParavirtProvider);
+}
+
+void UIRuntimeInfoWidget::updateGAsVersion()
+{
+ CGuest guest = m_console.GetGuest();
+ QString strGAVersion = guest.GetAdditionsVersion();
+ if (strGAVersion.isEmpty())
+ strGAVersion = m_strGANotDetected;
+ else
+ {
+ ULONG uRevision = guest.GetAdditionsRevision();
+ if (uRevision != 0)
+ strGAVersion += QString(" r%1").arg(uRevision);
+ }
+ updateInfoRow(InfoRow_GuestAdditions, QString("%1").arg(m_strGuestAdditionsLabel), strGAVersion);
+}
+
+void UIRuntimeInfoWidget::updateVRDE()
+{
+ int iVRDEPort = m_console.GetVRDEServerInfo().GetPort();
+ QString strVRDEInfo = (iVRDEPort == 0 || iVRDEPort == -1) ?
+ m_strVRDEPortNotAvailable : QString("%1").arg(iVRDEPort);
+ updateInfoRow(InfoRow_RemoteDesktop, QString("%1").arg(m_strRemoteDesktopLabel), strVRDEInfo);
+}
+
+void UIRuntimeInfoWidget::updateClipboardMode(KClipboardMode enmMode /* = KClipboardMode_Max */)
+{
+ if (enmMode == KClipboardMode_Max)
+ updateInfoRow(InfoRow_ClipboardMode, QString("%1").arg(m_strClipboardModeLabel),
+ gpConverter->toString(m_machine.GetClipboardMode()));
+ else
+ updateInfoRow(InfoRow_ClipboardMode, QString("%1").arg(m_strClipboardModeLabel),
+ gpConverter->toString(enmMode));
+}
+
+void UIRuntimeInfoWidget::updateDnDMode(KDnDMode enmMode /* = KDnDMode_Max */)
+{
+ if (enmMode == KDnDMode_Max)
+ updateInfoRow(InfoRow_DnDMode, QString("%1").arg(m_strDragAndDropLabel),
+ gpConverter->toString(m_machine.GetDnDMode()));
+ else
+ updateInfoRow(InfoRow_DnDMode, QString("%1").arg(m_strDragAndDropLabel),
+ gpConverter->toString(enmMode));
+}
+
+QString UIRuntimeInfoWidget::tableData() const
+{
+ AssertReturn(columnCount() == 3, QString());
+ QStringList data;
+ for (int i = 0; i < rowCount(); ++i)
+ {
+ /* Skip the first column as it contains only icon and no text: */
+ QTableWidgetItem *pItem = item(i, 1);
+ QString strColumn1 = pItem ? pItem->text() : QString();
+ pItem = item(i, 2);
+ QString strColumn2 = pItem ? pItem->text() : QString();
+ if (strColumn2.isEmpty())
+ data << strColumn1;
+ else
+ data << strColumn1 << ": " << strColumn2;
+ data << "\n";
+ }
+ return data.join(QString());
+}
+
+void UIRuntimeInfoWidget::updateInfoRow(InfoRow enmLine, const QString &strColumn0, const QString &strColumn1)
+{
+ QTableWidgetItem *pItem = 0;
+ for (int i = 0; i < rowCount() && !pItem; ++i)
+ {
+ pItem = item(i, 2);
+ if (!pItem)
+ continue;
+ if (pItem->type() != enmLine)
+ pItem = 0;
+ }
+ if (!pItem)
+ insertInfoRow(enmLine, strColumn0, strColumn1);
+ else
+ pItem->setText(strColumn1);
+}
+
+void UIRuntimeInfoWidget::createInfoRows()
+{
+ clear();
+ setRowCount(0);
+ setColumnCount(3);
+ updateTitleRow();
+ updateScreenInfo();
+ updateUpTime();
+ updateClipboardMode();
+ updateDnDMode();
+ updateVirtualizationInfo();
+ updateGAsVersion();
+ updateOSTypeRow();
+ updateVRDE();
+ resizeColumnToContents(1);
+}
+
+void UIRuntimeInfoWidget::computeMinimumWidth()
+{
+ m_iMinimumWidth = 0;
+ for (int j = 0; j < columnCount(); ++j)
+ m_iMinimumWidth += columnWidth(j);
+}
+
+
+
+/*********************************************************************************************************************************
+* UIInformationRuntime implementation. *
+*********************************************************************************************************************************/
+
+UIInformationRuntime::UIInformationRuntime(QWidget *pParent, const CMachine &machine, const CConsole &console, const UISession *pSession)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_machine(machine)
+ , m_console(console)
+ , m_pMainLayout(0)
+ , m_pRuntimeInfoWidget(0)
+ , m_pCopyWholeTableAction(0)
+{
+ if (!m_console.isNull())
+ m_comGuest = m_console.GetGuest();
+ connect(pSession, &UISession::sigAdditionsStateChange, this, &UIInformationRuntime::sltGuestAdditionsStateChange);
+ connect(pSession, &UISession::sigGuestMonitorChange, this, &UIInformationRuntime::sltGuestMonitorChange);
+ connect(pSession, &UISession::sigVRDEChange, this, &UIInformationRuntime::sltVRDEChange);
+ connect(pSession, &UISession::sigClipboardModeChange, this, &UIInformationRuntime::sltClipboardChange);
+ connect(pSession, &UISession::sigDnDModeChange, this, &UIInformationRuntime::sltDnDModeChange);
+
+ prepareObjects();
+ retranslateUi();
+}
+
+void UIInformationRuntime::retranslateUi()
+{
+ if (m_pCopyWholeTableAction)
+ m_pCopyWholeTableAction->setText(QApplication::translate("UIVMInformationDialog", "Copy All"));
+}
+
+void UIInformationRuntime::prepareObjects()
+{
+ m_pMainLayout = new QVBoxLayout(this);
+ if (!m_pMainLayout)
+ return;
+ m_pMainLayout->setSpacing(0);
+
+ m_pRuntimeInfoWidget = new UIRuntimeInfoWidget(0, m_machine, m_console);
+ AssertReturnVoid(m_pRuntimeInfoWidget);
+ connect(m_pRuntimeInfoWidget, &UIRuntimeInfoWidget::customContextMenuRequested,
+ this, &UIInformationRuntime::sltHandleTableContextMenuRequest);
+ m_pMainLayout->addWidget(m_pRuntimeInfoWidget);
+ m_pRuntimeInfoWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+
+ m_pCopyWholeTableAction = new QAction(this);
+ connect(m_pCopyWholeTableAction, &QAction::triggered, this, &UIInformationRuntime::sltHandleCopyWholeTable);
+}
+
+void UIInformationRuntime::sltGuestAdditionsStateChange()
+{
+ if (m_pRuntimeInfoWidget)
+ m_pRuntimeInfoWidget->updateGAsVersion();
+}
+
+void UIInformationRuntime::sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo)
+{
+ Q_UNUSED(changeType);
+ Q_UNUSED(screenGeo);
+ if (m_pRuntimeInfoWidget)
+ m_pRuntimeInfoWidget->updateScreenInfo(uScreenId);
+}
+
+void UIInformationRuntime::sltVRDEChange()
+{
+ if (m_pRuntimeInfoWidget)
+ m_pRuntimeInfoWidget->updateVRDE();
+}
+
+void UIInformationRuntime::sltClipboardChange(KClipboardMode enmMode)
+{
+ if (m_pRuntimeInfoWidget)
+ m_pRuntimeInfoWidget->updateClipboardMode(enmMode);
+}
+
+void UIInformationRuntime::sltDnDModeChange(KDnDMode enmMode)
+{
+ if (m_pRuntimeInfoWidget)
+ m_pRuntimeInfoWidget->updateDnDMode(enmMode);
+}
+
+void UIInformationRuntime::sltHandleTableContextMenuRequest(const QPoint &position)
+{
+ if (!m_pCopyWholeTableAction)
+ return;
+
+ QMenu menu(this);
+ menu.addAction(m_pCopyWholeTableAction);
+ menu.exec(mapToGlobal(position));
+}
+
+void UIInformationRuntime::sltHandleCopyWholeTable()
+{
+ QClipboard *pClipboard = QApplication::clipboard();
+ if (!pClipboard)
+ return;
+ if (!m_pRuntimeInfoWidget)
+ return;
+
+ pClipboard->setText(m_pRuntimeInfoWidget->tableData(), QClipboard::Clipboard);
+}
+
+#include "UIInformationRuntime.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationRuntime.h b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationRuntime.h
new file mode 100644
index 00000000..739c32b1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIInformationRuntime.h
@@ -0,0 +1,96 @@
+/* $Id: UIInformationRuntime.h $ */
+/** @file
+ * VBox Qt GUI - UIInformationRuntime class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_information_UIInformationRuntime_h
+#define FEQT_INCLUDED_SRC_runtime_information_UIInformationRuntime_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CConsole.h"
+#include "CGuest.h"
+#include "CMachine.h"
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QAction;
+class QVBoxLayout;
+class UISession;
+class UIRuntimeInfoWidget;
+
+/** UIInformationRuntime class displays a table including some
+ * run time attributes. */
+class UIInformationRuntime : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs information-tab passing @a pParent to the QWidget base-class constructor.
+ * @param machine is machine reference.
+ * @param console is machine console reference. */
+ UIInformationRuntime(QWidget *pParent, const CMachine &machine, const CConsole &console, const UISession *pSession);
+
+protected:
+
+ void retranslateUi();
+
+private slots:
+
+ /** @name These functions are connected to API events and implement necessary updates on the table.
+ * @{ */
+ void sltGuestAdditionsStateChange();
+ void sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo);
+ void sltVRDEChange();
+ void sltClipboardChange(KClipboardMode enmMode);
+ void sltDnDModeChange(KDnDMode enmMode);
+ /** @} */
+ void sltHandleTableContextMenuRequest(const QPoint &position);
+ void sltHandleCopyWholeTable();
+
+private:
+
+ void prepareObjects();
+
+ CMachine m_machine;
+ CConsole m_console;
+ CGuest m_comGuest;
+
+ /** Holds the instance of layout we create. */
+ QVBoxLayout *m_pMainLayout;
+ UIRuntimeInfoWidget *m_pRuntimeInfoWidget;
+ QAction *m_pCopyWholeTableAction;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_information_UIInformationRuntime_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/information/UIVMInformationDialog.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIVMInformationDialog.cpp
new file mode 100644
index 00000000..c46dda16
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIVMInformationDialog.cpp
@@ -0,0 +1,316 @@
+/* $Id: UIVMInformationDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVMInformationDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPushButton>
+#include <QScrollBar>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QITabWidget.h"
+#include "QIDialogButtonBox.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIInformationConfiguration.h"
+#include "UIInformationRuntime.h"
+#include "UIGuestProcessControlWidget.h"
+#include "UIMachineLogic.h"
+#include "UIMachine.h"
+#include "UIMachineView.h"
+#include "UIMachineWindow.h"
+#include "UIMessageCenter.h"
+#include "UIVMActivityMonitor.h"
+#include "UISession.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVMInformationDialog.h"
+#include "VBoxUtils.h"
+
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+#include "CConsole.h"
+#include "CSystemProperties.h"
+#include "CMachineDebugger.h"
+#include "CDisplay.h"
+#include "CStorageController.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CVRDEServerInfo.h"
+
+/* Other VBox includes: */
+#include <iprt/time.h>
+
+UIVMInformationDialog::UIVMInformationDialog(UIMachineWindow *pMachineWindow)
+ : QMainWindowWithRestorableGeometryAndRetranslateUi(0)
+ , m_pTabWidget(0)
+ , m_pMachineWindow(pMachineWindow)
+ , m_fCloseEmitted(false)
+ , m_iGeometrySaveTimerId(-1)
+{
+ if (m_pMachineWindow && !m_pMachineWindow->console().isNull())
+ {
+ CMachine comMachine = m_pMachineWindow->console().GetMachine();
+ m_uMachineId = comMachine.GetId();
+ }
+ /* Prepare: */
+ prepare();
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UIVMInformationDialog::sltMachineStateChange);
+}
+
+UIVMInformationDialog::~UIVMInformationDialog()
+{
+}
+
+bool UIVMInformationDialog::shouldBeMaximized() const
+{
+ return gEDataManager->sessionInformationDialogShouldBeMaximized();
+}
+
+void UIVMInformationDialog::retranslateUi()
+{
+ /* Setup dialog title: */
+ setWindowTitle(tr("%1 - Session Information").arg(m_pMachineWindow->machine().GetName()));
+
+ /* Translate tabs: */
+ m_pTabWidget->setTabText(Tabs_ConfigurationDetails, tr("Configuration &Details"));
+ m_pTabWidget->setTabText(Tabs_RuntimeInformation, tr("&Runtime Information"));
+ m_pTabWidget->setTabText(Tabs_ActivityMonitor, tr("VM &Activity"));
+ m_pTabWidget->setTabText(3, tr("&Guest Control"));
+
+ /* Retranslate button box buttons: */
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->button(QDialogButtonBox::Close)->setText(tr("Close"));
+ m_pButtonBox->button(QDialogButtonBox::Help)->setText(tr("Help"));
+ m_pButtonBox->button(QDialogButtonBox::Close)->setStatusTip(tr("Close dialog without saving"));
+ m_pButtonBox->button(QDialogButtonBox::Help)->setStatusTip(tr("Show dialog help"));
+ m_pButtonBox->button(QDialogButtonBox::Close)->setShortcut(Qt::Key_Escape);
+ m_pButtonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents);
+ m_pButtonBox->button(QDialogButtonBox::Close)->setToolTip(tr("Close this dialog (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Close)->shortcut().toString()));
+ m_pButtonBox->button(QDialogButtonBox::Help)->setToolTip(tr("Show Help (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Help)->shortcut().toString()));
+ }
+}
+
+void UIVMInformationDialog::closeEvent(QCloseEvent *pEvent)
+{
+ if (!m_fCloseEmitted)
+ {
+ m_fCloseEmitted = true;
+ UIVMInformationDialog::sigClose();
+ pEvent->ignore();
+ }
+}
+
+bool UIVMInformationDialog::event(QEvent *pEvent)
+{
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ case QEvent::Move:
+ {
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+ break;
+ }
+ case QEvent::Timer:
+ {
+ QTimerEvent *pTimerEvent = static_cast<QTimerEvent*>(pEvent);
+ if (pTimerEvent->timerId() == m_iGeometrySaveTimerId)
+ {
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = -1;
+ saveDialogGeometry();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return QMainWindowWithRestorableGeometryAndRetranslateUi::event(pEvent);
+}
+
+void UIVMInformationDialog::sltHandlePageChanged(int iIndex)
+{
+ /* Focus the browser on shown page: */
+ m_pTabWidget->widget(iIndex)->setFocus();
+}
+
+void UIVMInformationDialog::sltMachineStateChange(const QUuid &uMachineId, const KMachineState state)
+{
+ if (m_uMachineId != uMachineId)
+ return;
+ QWidget *pWidget = m_tabs.value(Tabs_GuestControl);
+ if (!pWidget)
+ return;
+ pWidget->setEnabled(state == KMachineState_Running);
+}
+
+void UIVMInformationDialog::saveDialogGeometry()
+{
+ const QRect geo = currentGeometry();
+ LogRel2(("GUI: UIVMInformationDialog: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ gEDataManager->setSessionInformationDialogGeometry(geo, isCurrentlyMaximized());
+}
+
+void UIVMInformationDialog::prepare()
+{
+ /* Prepare dialog: */
+ prepareThis();
+ /* Load settings: */
+ loadDialogGeometry();
+}
+
+void UIVMInformationDialog::prepareThis()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/session_info_32px.png", ":/session_info_16px.png"));
+#endif
+
+ /* Prepare central-widget: */
+ prepareCentralWidget();
+
+ /* Retranslate: */
+ retranslateUi();
+}
+
+void UIVMInformationDialog::prepareCentralWidget()
+{
+ /* Create central-widget: */
+ setCentralWidget(new QWidget);
+ AssertPtrReturnVoid(centralWidget());
+ {
+ /* Create main-layout: */
+ new QVBoxLayout(centralWidget());
+ AssertPtrReturnVoid(centralWidget()->layout());
+ {
+ /* Create tab-widget: */
+ prepareTabWidget();
+ /* Create button-box: */
+ prepareButtonBox();
+ }
+ }
+}
+
+void UIVMInformationDialog::prepareTabWidget()
+{
+ /* Create tab-widget: */
+ m_pTabWidget = new QITabWidget;
+ AssertPtrReturnVoid(m_pTabWidget);
+ {
+ /* Prepare tab-widget: */
+ m_pTabWidget->setTabIcon(Tabs_ConfigurationDetails, UIIconPool::iconSet(":/session_info_details_16px.png"));
+ m_pTabWidget->setTabIcon(Tabs_RuntimeInformation, UIIconPool::iconSet(":/session_info_runtime_16px.png"));
+
+ /* Create Configuration Details tab: */
+ UIInformationConfiguration *pInformationConfigurationWidget =
+ new UIInformationConfiguration(this, m_pMachineWindow->machine(), m_pMachineWindow->console());
+ if (pInformationConfigurationWidget)
+ {
+ m_tabs.insert(Tabs_ConfigurationDetails, pInformationConfigurationWidget);
+ m_pTabWidget->addTab(m_tabs.value(Tabs_ConfigurationDetails), QString());
+ }
+
+ /* Create Runtime Information tab: */
+ UIInformationRuntime *pInformationRuntimeWidget =
+ new UIInformationRuntime(this, m_pMachineWindow->machine(), m_pMachineWindow->console(), m_pMachineWindow->uisession());
+ if (pInformationRuntimeWidget)
+ {
+ m_tabs.insert(Tabs_RuntimeInformation, pInformationRuntimeWidget);
+ m_pTabWidget->addTab(m_tabs.value(Tabs_RuntimeInformation), QString());
+ }
+
+ /* Create Performance Monitor tab: */
+ UIVMActivityMonitor *pVMActivityMonitorWidget =
+ new UIVMActivityMonitor(EmbedTo_Dialog, this, m_pMachineWindow->machine());
+ if (pVMActivityMonitorWidget)
+ {
+ connect(m_pMachineWindow->uisession(), &UISession::sigAdditionsStateChange,
+ pVMActivityMonitorWidget, &UIVMActivityMonitor::sltGuestAdditionsStateChange);
+ m_tabs.insert(Tabs_ActivityMonitor, pVMActivityMonitorWidget);
+ m_pTabWidget->addTab(m_tabs.value(Tabs_ActivityMonitor), QString());
+ }
+
+ /* Create Guest Process Control tab: */
+ QString strMachineName;
+ if (m_pMachineWindow && m_pMachineWindow->console().isOk())
+ {
+ CMachine comMachine = m_pMachineWindow->console().GetMachine();
+ if (comMachine.isOk())
+ strMachineName = comMachine.GetName();
+ }
+ UIGuestProcessControlWidget *pGuestProcessControlWidget =
+ new UIGuestProcessControlWidget(EmbedTo_Dialog, m_pMachineWindow->console().GetGuest(),
+ this, strMachineName, false /* fShowToolbar */);
+ if (pGuestProcessControlWidget)
+ {
+ m_tabs.insert(3, pGuestProcessControlWidget);
+ m_pTabWidget->addTab(m_tabs.value(3), QString());
+ }
+
+ m_pTabWidget->setCurrentIndex(Tabs_ActivityMonitor);
+
+ /* Assign tab-widget page change handler: */
+ connect(m_pTabWidget, &QITabWidget::currentChanged, this, &UIVMInformationDialog::sltHandlePageChanged);
+
+ /* Add tab-widget into main-layout: */
+ centralWidget()->layout()->addWidget(m_pTabWidget);
+ }
+}
+
+void UIVMInformationDialog::prepareButtonBox()
+{
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ AssertPtrReturnVoid(m_pButtonBox);
+ {
+ /* Configure button-box: */
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Close | QDialogButtonBox::Help);
+ m_pButtonBox->button(QDialogButtonBox::Close)->setShortcut(Qt::Key_Escape);
+ m_pButtonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents);
+ uiCommon().setHelpKeyword(m_pButtonBox->button(QDialogButtonBox::Help), "vm-session-information");
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UIVMInformationDialog::sigClose);
+ connect(m_pButtonBox->button(QDialogButtonBox::Help), &QPushButton::pressed,
+ &(msgCenter()), &UIMessageCenter::sltHandleHelpRequest);
+ /* add button-box into main-layout: */
+ centralWidget()->layout()->addWidget(m_pButtonBox);
+ }
+}
+
+void UIVMInformationDialog::loadDialogGeometry()
+{
+ const QRect geo = gEDataManager->sessionInformationDialogGeometry(this, m_pMachineWindow);
+ LogRel2(("GUI: UIVMInformationDialog: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ restoreGeometry(geo);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/information/UIVMInformationDialog.h b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIVMInformationDialog.h
new file mode 100644
index 00000000..2ff05048
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/information/UIVMInformationDialog.h
@@ -0,0 +1,127 @@
+/* $Id: UIVMInformationDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIVMInformationDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_information_UIVMInformationDialog_h
+#define FEQT_INCLUDED_SRC_runtime_information_UIVMInformationDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMainWindow>
+
+/* GUI includes: */
+#include "QIWithRestorableGeometry.h"
+#include "QIWithRetranslateUI.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CSession.h"
+
+/* Forward declarations: */
+class QITabWidget;
+class QIDialogButtonBox;
+class UIMachineWindow;
+
+/* Type definitions: */
+typedef QIWithRestorableGeometry<QMainWindow> QMainWindowWithRestorableGeometry;
+typedef QIWithRetranslateUI<QMainWindowWithRestorableGeometry> QMainWindowWithRestorableGeometryAndRetranslateUi;
+
+
+/** QMainWindow subclass providing user
+ * with the dialog unifying VM details and statistics. */
+class UIVMInformationDialog : public QMainWindowWithRestorableGeometryAndRetranslateUi
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigClose();
+
+public:
+
+ /** Constructs information dialog for passed @a pMachineWindow. */
+ UIVMInformationDialog(UIMachineWindow *pMachineWindow);
+ /** Destructs information dialog. */
+ ~UIVMInformationDialog();
+
+ /** Returns whether the dialog should be maximized when geometry being restored. */
+ virtual bool shouldBeMaximized() const RT_OVERRIDE;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual void closeEvent(QCloseEvent *pEvent) RT_OVERRIDE;
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles tab-widget page change. */
+ void sltHandlePageChanged(int iIndex);
+ void sltMachineStateChange(const QUuid &uMachineId, const KMachineState state);
+
+private:
+ enum Tabs
+ {
+ Tabs_ConfigurationDetails = 0,
+ Tabs_RuntimeInformation,
+ Tabs_ActivityMonitor,
+ Tabs_GuestControl
+ };
+ /** Prepares all. */
+ void prepare();
+ /** Prepares this. */
+ void prepareThis();
+ /** Prepares central-widget. */
+ void prepareCentralWidget();
+ /** Prepares tab-widget. */
+ void prepareTabWidget();
+ /** Prepares tab with @a iTabIndex. */
+ void prepareTab(int iTabIndex);
+ /** Prepares button-box. */
+ void prepareButtonBox();
+ void loadDialogGeometry();
+ void saveDialogGeometry();
+
+ /** @name Widget variables.
+ * @{ */
+ /** Holds the dialog tab-widget instance. */
+ QITabWidget *m_pTabWidget;
+ /** Holds the map of dialog tab instances. */
+ QMap<int, QWidget*> m_tabs;
+ /** Holds the dialog button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+ /** Holds the machine-window reference. */
+ UIMachineWindow *m_pMachineWindow;
+ /** @} */
+ bool m_fCloseEmitted;
+ int m_iGeometrySaveTimerId;
+ QUuid m_uMachineId;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_information_UIVMInformationDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/normal/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/runtime/normal/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/normal/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.cpp
new file mode 100644
index 00000000..d0f9f068
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.cpp
@@ -0,0 +1,124 @@
+/* $Id: UIKeyboardHandlerNormal.cpp $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandlerNormal class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#ifndef VBOX_WS_MAC
+# include <QMainWindow>
+# include <QMenuBar>
+# include <QKeyEvent>
+# include <QTimer>
+#endif /* !VBOX_WS_MAC */
+
+/* GUI includes: */
+#include "UIKeyboardHandlerNormal.h"
+#ifndef VBOX_WS_MAC
+# include "UIMachineLogic.h"
+# include "UIMachineWindow.h"
+# include "UIShortcutPool.h"
+#endif /* !VBOX_WS_MAC */
+
+
+/* Namespaces: */
+#ifndef VBOX_WS_MAC
+using namespace UIExtraDataDefs;
+#endif /* !VBOX_WS_MAC */
+
+UIKeyboardHandlerNormal::UIKeyboardHandlerNormal(UIMachineLogic* pMachineLogic)
+ : UIKeyboardHandler(pMachineLogic)
+{
+}
+
+UIKeyboardHandlerNormal::~UIKeyboardHandlerNormal()
+{
+}
+
+#ifndef VBOX_WS_MAC
+bool UIKeyboardHandlerNormal::eventFilter(QObject *pWatchedObject, QEvent *pEvent)
+{
+ /* Check if pWatchedObject object is view: */
+ if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject))
+ {
+ /* Get corresponding screen index: */
+ ulong uScreenId = m_views.key(pWatchedView);
+ NOREF(uScreenId);
+ /* Handle view events: */
+ switch (pEvent->type())
+ {
+ /* We don't want this on the Mac, cause there the menu-bar isn't within the
+ * window and popping up a menu there looks really ugly. */
+ case QEvent::KeyPress:
+ {
+ /* Get key-event: */
+ QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
+ /* Process Host+Home as menu-bar activator: */
+ if ( isHostKeyPressed()
+ && gShortcutPool->shortcut(GUI_Input_MachineShortcuts, QString("PopupMenu")).sequences().contains(QKeySequence(pKeyEvent->key())))
+ {
+ /* Trying to get menu-bar: */
+ QMenuBar *pMenuBar = qobject_cast<QMainWindow*>(m_windows[uScreenId])->menuBar();
+ /* If menu-bar is present and have actions: */
+ if (pMenuBar && !pMenuBar->actions().isEmpty())
+ {
+ /* Is menu-bar visible? */
+ if (pMenuBar->isVisible())
+ {
+ /* If 'active' action is NOT chosen: */
+ if (!pMenuBar->activeAction())
+ /* Set first menu-bar action as 'active': */
+ pMenuBar->setActiveAction(pMenuBar->actions()[0]);
+ /* If 'active' action is chosen: */
+ if (pMenuBar->activeAction())
+ {
+ /* Activate 'active' menu-bar action: */
+ pMenuBar->activeAction()->activate(QAction::Trigger);
+#ifdef VBOX_WS_WIN
+ /* Windows host needs separate 'focus set'
+ * to let menubar operate while popped up: */
+ pMenuBar->setFocus();
+#endif /* VBOX_WS_WIN */
+ }
+ }
+ else
+ {
+ /* Post request to show popup-menu: */
+ QTimer::singleShot(0, m_pMachineLogic, SLOT(sltInvokePopupMenu()));
+ }
+ /* Filter-out this event: */
+ return true;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Else just propagate to base-class: */
+ return UIKeyboardHandler::eventFilter(pWatchedObject, pEvent);
+}
+#endif /* !VBOX_WS_MAC */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.h b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.h
new file mode 100644
index 00000000..8892dc05
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.h
@@ -0,0 +1,58 @@
+/* $Id: UIKeyboardHandlerNormal.h $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandlerNormal class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_normal_UIKeyboardHandlerNormal_h
+#define FEQT_INCLUDED_SRC_runtime_normal_UIKeyboardHandlerNormal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIKeyboardHandler.h"
+
+/** UIKeyboardHandler reimplementation
+ * providing machine-logic with PopupMenu keyboard handler. */
+class UIKeyboardHandlerNormal : public UIKeyboardHandler
+{
+ Q_OBJECT;
+
+public:
+
+ /** Normal keyboard-handler constructor. */
+ UIKeyboardHandlerNormal(UIMachineLogic *pMachineLogic);
+ /** Normal keyboard-handler destructor. */
+ virtual ~UIKeyboardHandlerNormal();
+
+private:
+
+#ifndef VBOX_WS_MAC
+ /** General event-filter. */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+#endif /* !VBOX_WS_MAC */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_normal_UIKeyboardHandlerNormal_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineLogicNormal.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineLogicNormal.cpp
new file mode 100644
index 00000000..c9eacd0c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineLogicNormal.cpp
@@ -0,0 +1,362 @@
+/* $Id: UIMachineLogicNormal.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogicNormal class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#ifndef VBOX_WS_MAC
+# include <QTimer>
+#endif /* !VBOX_WS_MAC */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIMessageCenter.h"
+#include "UISession.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogicNormal.h"
+#include "UIMachineWindow.h"
+#include "UIMenuBarEditorWindow.h"
+#include "UIStatusBarEditorWindow.h"
+#include "UIExtraDataManager.h"
+#include "UIFrameBuffer.h"
+#ifndef VBOX_WS_MAC
+# include "QIMenu.h"
+#else /* VBOX_WS_MAC */
+# include "VBoxUtils.h"
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CDisplay.h"
+#include "CGraphicsAdapter.h"
+
+
+UIMachineLogicNormal::UIMachineLogicNormal(QObject *pParent, UISession *pSession)
+ : UIMachineLogic(pParent, pSession, UIVisualStateType_Normal)
+#ifndef VBOX_WS_MAC
+ , m_pPopupMenu(0)
+#endif /* !VBOX_WS_MAC */
+{
+}
+
+bool UIMachineLogicNormal::checkAvailability()
+{
+ /* Normal mode is always available: */
+ return true;
+}
+
+void UIMachineLogicNormal::sltCheckForRequestedVisualStateType()
+{
+ LogRel(("GUI: UIMachineLogicNormal::sltCheckForRequestedVisualStateType: Requested-state=%d, Machine-state=%d\n",
+ uisession()->requestedVisualState(), uisession()->machineState()));
+
+ /* Do not try to change visual-state type if machine was not started yet: */
+ if (!uisession()->isRunning() && !uisession()->isPaused())
+ return;
+
+ /* Do not try to change visual-state type in 'manual override' mode: */
+ if (uisession()->isManualOverrideMode())
+ return;
+
+ /* Check requested visual-state types: */
+ switch (uisession()->requestedVisualState())
+ {
+ /* If 'seamless' visual-state type is requested: */
+ case UIVisualStateType_Seamless:
+ {
+ /* And supported: */
+ if (uisession()->isGuestSupportsSeamless())
+ {
+ LogRel(("GUI: UIMachineLogicNormal::sltCheckForRequestedVisualStateType: "
+ "Going 'seamless' as requested...\n"));
+ uisession()->setRequestedVisualState(UIVisualStateType_Invalid);
+ uisession()->changeVisualState(UIVisualStateType_Seamless);
+ }
+ else
+ LogRel(("GUI: UIMachineLogicNormal::sltCheckForRequestedVisualStateType: "
+ "Rejecting 'seamless' as is it not yet supported...\n"));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+#ifndef RT_OS_DARWIN
+void UIMachineLogicNormal::sltInvokePopupMenu()
+{
+ /* Popup main-menu if present: */
+ if (m_pPopupMenu && !m_pPopupMenu->isEmpty())
+ {
+ m_pPopupMenu->popup(activeMachineWindow()->geometry().center());
+ QTimer::singleShot(0, m_pPopupMenu, SLOT(sltHighlightFirstAction()));
+ }
+}
+#endif /* RT_OS_DARWIN */
+
+void UIMachineLogicNormal::sltOpenMenuBarSettings()
+{
+ /* Do not process if window(s) missed! */
+ AssertReturnVoid(isMachineWindowsCreated());
+
+#ifndef VBOX_WS_MAC
+ /* Make sure menu-bar is enabled: */
+ const bool fEnabled = actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility)->isChecked();
+ AssertReturnVoid(fEnabled);
+#endif /* !VBOX_WS_MAC */
+
+ /* Prevent user from opening another one editor or toggle menu-bar: */
+ actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_S_Settings)->setEnabled(false);
+#ifndef VBOX_WS_MAC
+ actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility)->setEnabled(false);
+#endif /* !VBOX_WS_MAC */
+ /* Create menu-bar editor: */
+ UIMenuBarEditorWindow *pMenuBarEditor = new UIMenuBarEditorWindow(activeMachineWindow(), actionPool());
+ AssertPtrReturnVoid(pMenuBarEditor);
+ {
+ /* Configure menu-bar editor: */
+ connect(pMenuBarEditor, &UIMenuBarEditorWindow::destroyed,
+ this, &UIMachineLogicNormal::sltMenuBarSettingsClosed);
+ /* Show window: */
+ pMenuBarEditor->show();
+ }
+}
+
+void UIMachineLogicNormal::sltMenuBarSettingsClosed()
+{
+#ifndef VBOX_WS_MAC
+ /* Make sure menu-bar is enabled: */
+ const bool fEnabled = actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility)->isChecked();
+ AssertReturnVoid(fEnabled);
+#endif /* !VBOX_WS_MAC */
+
+ /* Allow user to open editor and toggle menu-bar again: */
+ actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_S_Settings)->setEnabled(true);
+#ifndef VBOX_WS_MAC
+ actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility)->setEnabled(true);
+#endif /* !VBOX_WS_MAC */
+}
+
+#ifndef RT_OS_DARWIN
+void UIMachineLogicNormal::sltToggleMenuBar()
+{
+ /* Do not process if window(s) missed! */
+ AssertReturnVoid(isMachineWindowsCreated());
+
+ /* Invert menu-bar availability option: */
+ const bool fEnabled = gEDataManager->menuBarEnabled(uiCommon().managedVMUuid());
+ gEDataManager->setMenuBarEnabled(!fEnabled, uiCommon().managedVMUuid());
+}
+#endif /* !RT_OS_DARWIN */
+
+void UIMachineLogicNormal::sltOpenStatusBarSettings()
+{
+ /* Do not process if window(s) missed! */
+ AssertReturnVoid(isMachineWindowsCreated());
+
+ /* Make sure status-bar is enabled: */
+ const bool fEnabled = actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility)->isChecked();
+ AssertReturnVoid(fEnabled);
+
+ /* Prevent user from opening another one editor or toggle status-bar: */
+ actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_S_Settings)->setEnabled(false);
+ actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility)->setEnabled(false);
+ /* Create status-bar editor: */
+ UIStatusBarEditorWindow *pStatusBarEditor = new UIStatusBarEditorWindow(activeMachineWindow());
+ AssertPtrReturnVoid(pStatusBarEditor);
+ {
+ /* Configure status-bar editor: */
+ connect(pStatusBarEditor, &UIStatusBarEditorWindow::destroyed,
+ this, &UIMachineLogicNormal::sltStatusBarSettingsClosed);
+ /* Show window: */
+ pStatusBarEditor->show();
+ }
+}
+
+void UIMachineLogicNormal::sltStatusBarSettingsClosed()
+{
+ /* Make sure status-bar is enabled: */
+ const bool fEnabled = actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility)->isChecked();
+ AssertReturnVoid(fEnabled);
+
+ /* Allow user to open editor and toggle status-bar again: */
+ actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_S_Settings)->setEnabled(true);
+ actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility)->setEnabled(true);
+}
+
+void UIMachineLogicNormal::sltToggleStatusBar()
+{
+ /* Do not process if window(s) missed! */
+ AssertReturnVoid(isMachineWindowsCreated());
+
+ /* Invert status-bar availability option: */
+ const bool fEnabled = gEDataManager->statusBarEnabled(uiCommon().managedVMUuid());
+ gEDataManager->setStatusBarEnabled(!fEnabled, uiCommon().managedVMUuid());
+}
+
+void UIMachineLogicNormal::sltHostScreenAvailableAreaChange()
+{
+#if defined(VBOX_WS_X11) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
+ /* Prevent handling if fake screen detected: */
+ if (UIDesktopWidgetWatchdog::isFakeScreenDetected())
+ return;
+
+ /* Make sure all machine-window(s) have previous but normalized geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ if (!pMachineWindow->isMaximized())
+ pMachineWindow->restoreCachedGeometry();
+#endif /* VBOX_WS_X11 && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+ /* Call to base-class: */
+ UIMachineLogic::sltHostScreenAvailableAreaChange();
+}
+
+void UIMachineLogicNormal::prepareActionGroups()
+{
+ /* Call to base-class: */
+ UIMachineLogic::prepareActionGroups();
+
+ /* Restrict 'Remap' actions for 'View' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
+ (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
+ (UIExtraDataMetaDefs::RuntimeMenuViewActionType_Remap));
+}
+
+void UIMachineLogicNormal::prepareActionConnections()
+{
+ /* Call to base-class: */
+ UIMachineLogic::prepareActionConnections();
+
+ /* Prepare 'View' actions connections: */
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltChangeVisualStateToFullscreen);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltChangeVisualStateToSeamless);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltChangeVisualStateToScale);
+ connect(actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltOpenMenuBarSettings);
+#ifndef VBOX_WS_MAC
+ connect(actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltToggleMenuBar);
+#endif /* !VBOX_WS_MAC */
+ connect(actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltOpenStatusBarSettings);
+ connect(actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltToggleStatusBar);
+}
+
+void UIMachineLogicNormal::prepareMachineWindows()
+{
+ /* Do not create machine-window(s) if they created already: */
+ if (isMachineWindowsCreated())
+ return;
+
+#ifdef VBOX_WS_MAC /// @todo Is that really need here?
+ /* We have to make sure that we are getting the front most process.
+ * This is necessary for Qt versions > 4.3.3: */
+ ::darwinSetFrontMostProcess();
+#endif /* VBOX_WS_MAC */
+
+ /* Get monitors count: */
+ ulong uMonitorCount = machine().GetGraphicsAdapter().GetMonitorCount();
+ /* Create machine window(s): */
+ for (ulong uScreenId = 0; uScreenId < uMonitorCount; ++ uScreenId)
+ addMachineWindow(UIMachineWindow::create(this, uScreenId));
+ /* Order machine window(s): */
+ for (ulong uScreenId = uMonitorCount; uScreenId > 0; -- uScreenId)
+ machineWindows()[uScreenId - 1]->raise();
+
+ /* Listen for frame-buffer resize: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ connect(pMachineWindow, &UIMachineWindow::sigFrameBufferResize,
+ this, &UIMachineLogicNormal::sigFrameBufferResize);
+ emit sigFrameBufferResize();
+
+ /* Mark machine-window(s) created: */
+ setMachineWindowsCreated(true);
+}
+
+#ifndef VBOX_WS_MAC
+void UIMachineLogicNormal::prepareMenu()
+{
+ /* Prepare popup-menu: */
+ m_pPopupMenu = new QIMenu;
+ AssertPtrReturnVoid(m_pPopupMenu);
+ {
+ /* Prepare popup-menu: */
+ foreach (QMenu *pMenu, actionPool()->menus())
+ m_pPopupMenu->addMenu(pMenu);
+ }
+}
+#endif /* !VBOX_WS_MAC */
+
+#ifndef VBOX_WS_MAC
+void UIMachineLogicNormal::cleanupMenu()
+{
+ /* Cleanup popup-menu: */
+ delete m_pPopupMenu;
+ m_pPopupMenu = 0;
+}
+#endif /* !VBOX_WS_MAC */
+
+void UIMachineLogicNormal::cleanupMachineWindows()
+{
+ /* Do not destroy machine-window(s) if they destroyed already: */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Mark machine-window(s) destroyed: */
+ setMachineWindowsCreated(false);
+
+ /* Cleanup machine-window(s): */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ UIMachineWindow::destroy(pMachineWindow);
+}
+
+void UIMachineLogicNormal::cleanupActionConnections()
+{
+ /* "View" actions disconnections: */
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltChangeVisualStateToFullscreen);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltChangeVisualStateToSeamless);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltChangeVisualStateToScale);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltOpenMenuBarSettings);
+#ifndef VBOX_WS_MAC
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltToggleMenuBar);
+#endif /* !VBOX_WS_MAC */
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_S_Settings), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltOpenStatusBarSettings);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility), &UIAction::triggered,
+ this, &UIMachineLogicNormal::sltToggleStatusBar);
+
+ /* Call to base-class: */
+ UIMachineLogic::cleanupActionConnections();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineLogicNormal.h b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineLogicNormal.h
new file mode 100644
index 00000000..420afe08
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineLogicNormal.h
@@ -0,0 +1,108 @@
+/* $Id: UIMachineLogicNormal.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogicNormal class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_normal_UIMachineLogicNormal_h
+#define FEQT_INCLUDED_SRC_runtime_normal_UIMachineLogicNormal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineLogic.h"
+
+/** UIMachineLogic subclass used as normal machine logic implementation. */
+class UIMachineLogicNormal : public UIMachineLogic
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs normal logic passing @a pParent to the base-class.
+ * @param pSession Brings the session UI reference. */
+ UIMachineLogicNormal(QObject *pParent, UISession *pSession);
+
+protected:
+
+ /* Check if this logic is available: */
+ bool checkAvailability();
+
+ /** Returns machine-window flags for 'Normal' machine-logic and passed @a uScreenId. */
+ virtual Qt::WindowFlags windowFlags(ulong uScreenId) const { Q_UNUSED(uScreenId); return Qt::Window; }
+
+private slots:
+
+ /** Checks if some visual-state type was requested. */
+ void sltCheckForRequestedVisualStateType();
+
+#ifndef RT_OS_DARWIN
+ /** Invokes popup-menu. */
+ void sltInvokePopupMenu();
+#endif /* RT_OS_DARWIN */
+
+ /** Opens menu-bar editor.*/
+ void sltOpenMenuBarSettings();
+ /** Handles menu-bar editor closing.*/
+ void sltMenuBarSettingsClosed();
+#ifndef RT_OS_DARWIN
+ /** Toggles menu-bar presence.*/
+ void sltToggleMenuBar();
+#endif /* !RT_OS_DARWIN */
+
+ /** Opens status-bar editor.*/
+ void sltOpenStatusBarSettings();
+ /** Handles status-bar editor closing.*/
+ void sltStatusBarSettingsClosed();
+ /** Toggles status-bar presence.*/
+ void sltToggleStatusBar();
+
+ /** Handles host-screen available-area change. */
+ virtual void sltHostScreenAvailableAreaChange() RT_OVERRIDE;
+
+private:
+
+ /* Prepare helpers: */
+ virtual void prepareActionGroups() RT_OVERRIDE;
+ void prepareActionConnections();
+ void prepareMachineWindows();
+#ifndef VBOX_WS_MAC
+ void prepareMenu();
+#endif /* !VBOX_WS_MAC */
+
+ /* Cleanup helpers: */
+#ifndef VBOX_WS_MAC
+ void cleanupMenu();
+#endif /* !VBOX_WS_MAC */
+ void cleanupMachineWindows();
+ void cleanupActionConnections();
+
+#ifndef VBOX_WS_MAC
+ /** Holds the popup-menu instance. */
+ QMenu *m_pPopupMenu;
+#endif /* !VBOX_WS_MAC */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_normal_UIMachineLogicNormal_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineViewNormal.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineViewNormal.cpp
new file mode 100644
index 00000000..25383ca8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineViewNormal.cpp
@@ -0,0 +1,248 @@
+/* $Id: UIMachineViewNormal.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineViewNormal class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QMainWindow>
+#include <QMenuBar>
+#include <QScrollBar>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UISession.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogic.h"
+#include "UIMachineWindow.h"
+#include "UIMachineViewNormal.h"
+#include "UIFrameBuffer.h"
+#include "UIExtraDataManager.h"
+#include "UIDesktopWidgetWatchdog.h"
+
+/* Other VBox includes: */
+#include "VBox/log.h"
+
+
+UIMachineViewNormal::UIMachineViewNormal(UIMachineWindow *pMachineWindow, ulong uScreenId)
+ : UIMachineView(pMachineWindow, uScreenId)
+ , m_fGuestAutoresizeEnabled(actionPool()->action(UIActionIndexRT_M_View_T_GuestAutoresize)->isChecked())
+{
+}
+
+void UIMachineViewNormal::sltAdditionsStateChanged()
+{
+ adjustGuestScreenSize();
+}
+
+bool UIMachineViewNormal::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ if (pWatched != 0 && pWatched == machineWindow())
+ {
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ {
+ /* Recalculate maximum guest size: */
+ setMaximumGuestSize();
+ /* And resize guest to current window size: */
+ if (m_fGuestAutoresizeEnabled && uisession()->isGuestSupportsGraphics())
+ QTimer::singleShot(300, this, SLOT(sltPerformGuestResize()));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* For scroll-bars of the machine-view: */
+ if ( pWatched == verticalScrollBar()
+ || pWatched == horizontalScrollBar())
+ {
+ switch (pEvent->type())
+ {
+ /* On show/hide event: */
+ case QEvent::Show:
+ case QEvent::Hide:
+ {
+ /* Set maximum-size to size-hint: */
+ setMaximumSize(sizeHint());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return UIMachineView::eventFilter(pWatched, pEvent);
+}
+
+void UIMachineViewNormal::prepareCommon()
+{
+ /* Base class common settings: */
+ UIMachineView::prepareCommon();
+
+ /* Setup size-policy: */
+ setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
+ /* Set maximum-size to size-hint: */
+ setMaximumSize(sizeHint());
+}
+
+void UIMachineViewNormal::prepareFilters()
+{
+ /* Base class filters: */
+ UIMachineView::prepareFilters();
+
+ /* Install scroll-bars event-filters: */
+ verticalScrollBar()->installEventFilter(this);
+ horizontalScrollBar()->installEventFilter(this);
+
+#ifdef VBOX_WS_WIN
+ /* Install menu-bar event-filter: */
+ machineWindow()->menuBar()->installEventFilter(this);
+#endif /* VBOX_WS_WIN */
+}
+
+void UIMachineViewNormal::prepareConsoleConnections()
+{
+ /* Base class connections: */
+ UIMachineView::prepareConsoleConnections();
+
+ /* Guest additions state-change updater: */
+ connect(uisession(), &UISession::sigAdditionsStateActualChange, this, &UIMachineViewNormal::sltAdditionsStateChanged);
+}
+
+void UIMachineViewNormal::setGuestAutoresizeEnabled(bool fEnabled)
+{
+ if (m_fGuestAutoresizeEnabled != fEnabled)
+ {
+ m_fGuestAutoresizeEnabled = fEnabled;
+
+ if (m_fGuestAutoresizeEnabled && uisession()->isGuestSupportsGraphics())
+ sltPerformGuestResize();
+ }
+}
+
+void UIMachineViewNormal::resendSizeHint()
+{
+ /* Skip if another visual representation mode requested: */
+ if (uisession()->requestedVisualState() == UIVisualStateType_Seamless) // Seamless only for now.
+ return;
+
+ /* Get the last guest-screen size-hint, taking the scale factor into account. */
+ const QSize storedSizeHint = storedGuestScreenSizeHint();
+ const QSize effectiveSizeHint = scaledBackward(storedSizeHint);
+ LogRel(("GUI: UIMachineViewNormal::resendSizeHint: Restoring guest size-hint for screen %d to %dx%d\n",
+ (int)screenId(), effectiveSizeHint.width(), effectiveSizeHint.height()));
+
+ /* Expand current limitations: */
+ setMaximumGuestSize(effectiveSizeHint);
+
+ /* Temporarily restrict the size to prevent a brief resize to the
+ * frame-buffer dimensions when we exit full-screen. This is only
+ * applied if the frame-buffer is at full-screen dimensions and
+ * until the first machine view resize. */
+ m_sizeHintOverride = scaledForward(QSize(640, 480)).expandedTo(storedSizeHint);
+
+ /* Restore saved monitor information to the guest. The guest may not respond
+ * until a suitable driver or helper is enabled (or at all). We do not notify
+ * the guest (aNotify == false), because there is technically no change (same
+ * hardware as before shutdown), and notifying would interfere with the Windows
+ * guest driver which saves the video mode to the registry on shutdown. */
+ uisession()->setScreenVisibleHostDesires(screenId(), guestScreenVisibilityStatus());
+ display().SetVideoModeHint(screenId(),
+ guestScreenVisibilityStatus(),
+ false, 0, 0, effectiveSizeHint.width(), effectiveSizeHint.height(), 0, false);
+}
+
+void UIMachineViewNormal::adjustGuestScreenSize()
+{
+ LogRel(("GUI: UIMachineViewNormal::adjustGuestScreenSize: Adjust guest-screen size if necessary\n"));
+
+ /* Acquire requested guest-screen size-hint or at least actual frame-buffer size: */
+ QSize guestScreenSizeHint = requestedGuestScreenSizeHint();
+ /* Take the scale-factor(s) into account: */
+ guestScreenSizeHint = scaledForward(guestScreenSizeHint);
+
+ /* Calculate maximum possible guest screen size: */
+ const QSize maximumGuestScreenSize = calculateMaxGuestSize();
+
+ /* Adjust guest-screen size if the requested one is too big for the screen: */
+ if ( guestScreenSizeHint.width() > maximumGuestScreenSize.width()
+ || guestScreenSizeHint.height() > maximumGuestScreenSize.height())
+ sltPerformGuestResize(machineWindow()->centralWidget()->size());
+}
+
+QSize UIMachineViewNormal::sizeHint() const
+{
+ /* Call to base-class: */
+ QSize size = UIMachineView::sizeHint();
+
+ /* If guest-screen auto-resize is not enabled
+ * or the guest-additions doesn't support graphics
+ * we should take scroll-bars size-hints into account: */
+ if (!m_fGuestAutoresizeEnabled || !uisession()->isGuestSupportsGraphics())
+ {
+ if (verticalScrollBar()->isVisible())
+ size += QSize(verticalScrollBar()->sizeHint().width(), 0);
+ if (horizontalScrollBar()->isVisible())
+ size += QSize(0, horizontalScrollBar()->sizeHint().height());
+ }
+
+ /* Return resulting size-hint finally: */
+ return size;
+}
+
+QRect UIMachineViewNormal::workingArea() const
+{
+ return gpDesktop->availableGeometry(this);
+}
+
+QSize UIMachineViewNormal::calculateMaxGuestSize() const
+{
+ /* 1) The calculation below is not reliable on some (X11) platforms until we
+ * have been visible for a fraction of a second, so do the best we can
+ * otherwise.
+ * 2) We also get called early before "machineWindow" has been fully
+ * initialised, at which time we can't perform the calculation. */
+ if (!isVisible())
+ return workingArea().size() * 0.95;
+ /* The area taken up by the machine window on the desktop, including window
+ * frame, title, menu bar and status bar. */
+ QSize windowSize = machineWindow()->frameGeometry().size();
+ /* The window shouldn't be allowed to expand beyond the working area
+ * unless it already does. In that case the guest shouldn't expand it
+ * any further though. */
+ QSize maximumSize = workingArea().size().expandedTo(windowSize);
+ /* The current size of the machine display. */
+ QSize centralWidgetSize = machineWindow()->centralWidget()->size();
+ /* To work out how big the guest display can get without the window going
+ * over the maximum size we calculated above, we work out how much space
+ * the other parts of the window (frame, menu bar, status bar and so on)
+ * take up and subtract that space from the maximum window size. The
+ * central widget shouldn't be bigger than the window, but we bound it for
+ * sanity (or insanity) reasons. */
+ return maximumSize - (windowSize - centralWidgetSize.boundedTo(windowSize));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineViewNormal.h b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineViewNormal.h
new file mode 100644
index 00000000..ac33d73e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineViewNormal.h
@@ -0,0 +1,89 @@
+/* $Id: UIMachineViewNormal.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineViewNormal class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_normal_UIMachineViewNormal_h
+#define FEQT_INCLUDED_SRC_runtime_normal_UIMachineViewNormal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineView.h"
+
+/** UIMachineView subclass used as normal machine view implementation. */
+class UIMachineViewNormal : public UIMachineView
+{
+ Q_OBJECT;
+
+public:
+
+ /* Normal machine-view constructor: */
+ UIMachineViewNormal(UIMachineWindow *pMachineWindow, ulong uScreenId);
+ /* Normal machine-view destructor: */
+ virtual ~UIMachineViewNormal() {}
+
+private slots:
+
+ /* Console callback handlers: */
+ void sltAdditionsStateChanged();
+
+private:
+
+ /* Event handlers: */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+
+ /* Prepare helpers: */
+ void prepareCommon();
+ void prepareFilters();
+ void prepareConsoleConnections();
+
+ /* Cleanup helpers: */
+ //void cleanupConsoleConnections() {}
+ //void cleanupFilters() {}
+ //void cleanupCommon() {}
+
+ /** Returns whether the guest-screen auto-resize is enabled. */
+ virtual bool isGuestAutoresizeEnabled() const RT_OVERRIDE { return m_fGuestAutoresizeEnabled; }
+ /** Defines whether the guest-screen auto-resize is @a fEnabled. */
+ virtual void setGuestAutoresizeEnabled(bool bEnabled) RT_OVERRIDE;
+
+ /** Resends guest size-hint. */
+ void resendSizeHint();
+
+ /** Adjusts guest-screen size to correspond current <i>machine-window</i> size. */
+ void adjustGuestScreenSize();
+
+ /* Private helpers: */
+ QSize sizeHint() const;
+ QRect workingArea() const;
+ QSize calculateMaxGuestSize() const;
+
+ /* Private members: */
+ bool m_fGuestAutoresizeEnabled : 1;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_normal_UIMachineViewNormal_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.cpp
new file mode 100644
index 00000000..457cc29e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.cpp
@@ -0,0 +1,780 @@
+/* $Id: UIMachineWindowNormal.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindowNormal class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QMenuBar>
+#include <QTimerEvent>
+#include <QContextMenuEvent>
+#include <QResizeEvent>
+#include <QScrollBar>
+#ifdef VBOX_WS_X11
+# include <QTimer>
+#endif
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIMachineWindowNormal.h"
+#include "UIActionPoolRuntime.h"
+#include "UIExtraDataManager.h"
+#include "UIIndicatorsPool.h"
+#include "UIKeyboardHandler.h"
+#include "UIMouseHandler.h"
+#include "UIMachineLogic.h"
+#include "UIMachineView.h"
+#include "UINotificationCenter.h"
+#include "UIIconPool.h"
+#include "UISession.h"
+#include "QIStatusBar.h"
+#include "QIStatusBarIndicator.h"
+#ifndef VBOX_WS_MAC
+# include "UIMenuBar.h"
+#else /* VBOX_WS_MAC */
+# include "VBoxUtils.h"
+# include "UIImageTools.h"
+# include "UICocoaApplication.h"
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CMediumAttachment.h"
+#include "CUSBController.h"
+#include "CUSBDeviceFilters.h"
+
+
+UIMachineWindowNormal::UIMachineWindowNormal(UIMachineLogic *pMachineLogic, ulong uScreenId)
+ : UIMachineWindow(pMachineLogic, uScreenId)
+ , m_pIndicatorsPool(0)
+ , m_iGeometrySaveTimerId(-1)
+{
+}
+
+void UIMachineWindowNormal::sltMachineStateChanged()
+{
+ /* Call to base-class: */
+ UIMachineWindow::sltMachineStateChanged();
+
+ /* Update indicator-pool and virtualization stuff: */
+ updateAppearanceOf(UIVisualElement_IndicatorPoolStuff | UIVisualElement_Recording | UIVisualElement_FeaturesStuff);
+}
+
+void UIMachineWindowNormal::sltMediumChange(const CMediumAttachment &attachment)
+{
+ /* Update corresponding medium stuff: */
+ KDeviceType type = attachment.GetType();
+ if (type == KDeviceType_HardDisk)
+ updateAppearanceOf(UIVisualElement_HDStuff);
+ if (type == KDeviceType_DVD)
+ updateAppearanceOf(UIVisualElement_CDStuff);
+ if (type == KDeviceType_Floppy)
+ updateAppearanceOf(UIVisualElement_FDStuff);
+}
+
+void UIMachineWindowNormal::sltUSBControllerChange()
+{
+ /* Update USB stuff: */
+ updateAppearanceOf(UIVisualElement_USBStuff);
+}
+
+void UIMachineWindowNormal::sltUSBDeviceStateChange()
+{
+ /* Update USB stuff: */
+ updateAppearanceOf(UIVisualElement_USBStuff);
+}
+
+void UIMachineWindowNormal::sltAudioAdapterChange()
+{
+ /* Update audio stuff: */
+ updateAppearanceOf(UIVisualElement_AudioStuff);
+}
+
+void UIMachineWindowNormal::sltNetworkAdapterChange()
+{
+ /* Update network stuff: */
+ updateAppearanceOf(UIVisualElement_NetworkStuff);
+}
+
+void UIMachineWindowNormal::sltSharedFolderChange()
+{
+ /* Update shared-folders stuff: */
+ updateAppearanceOf(UIVisualElement_SharedFolderStuff);
+}
+
+void UIMachineWindowNormal::sltRecordingChange()
+{
+ /* Update video-capture stuff: */
+ updateAppearanceOf(UIVisualElement_Recording);
+}
+
+void UIMachineWindowNormal::sltCPUExecutionCapChange()
+{
+ /* Update virtualization stuff: */
+ updateAppearanceOf(UIVisualElement_FeaturesStuff);
+}
+
+void UIMachineWindowNormal::sltHandleSessionInitialized()
+{
+ /* Update virtualization stuff: */
+ updateAppearanceOf( UIVisualElement_FeaturesStuff
+ | UIVisualElement_HDStuff
+ | UIVisualElement_CDStuff
+ | UIVisualElement_FDStuff);
+}
+
+#ifndef RT_OS_DARWIN
+void UIMachineWindowNormal::sltHandleMenuBarConfigurationChange(const QUuid &uMachineID)
+{
+ /* Skip unrelated machine IDs: */
+ if (uiCommon().managedVMUuid() != uMachineID)
+ return;
+
+ /* Check whether menu-bar is enabled: */
+ const bool fEnabled = gEDataManager->menuBarEnabled(uiCommon().managedVMUuid());
+ /* Update settings action 'enable' state: */
+ QAction *pActionMenuBarSettings = actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_S_Settings);
+ pActionMenuBarSettings->setEnabled(fEnabled);
+ /* Update switch action 'checked' state: */
+ QAction *pActionMenuBarSwitch = actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility);
+ pActionMenuBarSwitch->blockSignals(true);
+ pActionMenuBarSwitch->setChecked(fEnabled);
+ pActionMenuBarSwitch->blockSignals(false);
+
+ /* Update menu-bar visibility: */
+ menuBar()->setVisible(pActionMenuBarSwitch->isChecked());
+ /* Update menu-bar: */
+ updateMenu();
+
+ /* Normalize geometry without moving: */
+ normalizeGeometry(false /* adjust position */, shouldResizeToGuestDisplay());
+}
+
+void UIMachineWindowNormal::sltHandleMenuBarContextMenuRequest(const QPoint &position)
+{
+ /* Raise action's context-menu: */
+ if (gEDataManager->menuBarContextMenuEnabled(uiCommon().managedVMUuid()))
+ actionPool()->action(UIActionIndexRT_M_View_M_MenuBar)->menu()->exec(menuBar()->mapToGlobal(position));
+}
+#endif /* !RT_OS_DARWIN */
+
+void UIMachineWindowNormal::sltHandleStatusBarConfigurationChange(const QUuid &uMachineID)
+{
+ /* Skip unrelated machine IDs: */
+ if (uiCommon().managedVMUuid() != uMachineID)
+ return;
+
+ /* Check whether status-bar is enabled: */
+ const bool fEnabled = gEDataManager->statusBarEnabled(uiCommon().managedVMUuid());
+ /* Update settings action 'enable' state: */
+ QAction *pActionStatusBarSettings = actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_S_Settings);
+ pActionStatusBarSettings->setEnabled(fEnabled);
+ /* Update switch action 'checked' state: */
+ QAction *pActionStatusBarSwitch = actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility);
+ pActionStatusBarSwitch->blockSignals(true);
+ pActionStatusBarSwitch->setChecked(fEnabled);
+ pActionStatusBarSwitch->blockSignals(false);
+
+ /* Update status-bar visibility: */
+ statusBar()->setVisible(pActionStatusBarSwitch->isChecked());
+ /* Update status-bar indicators-pool: */
+ if (m_pIndicatorsPool)
+ m_pIndicatorsPool->setAutoUpdateIndicatorStates(statusBar()->isVisible() && uisession()->isRunning());
+
+ /* Normalize geometry without moving: */
+ normalizeGeometry(false /* adjust position */, shouldResizeToGuestDisplay());
+}
+
+void UIMachineWindowNormal::sltHandleStatusBarContextMenuRequest(const QPoint &position)
+{
+ /* Raise action's context-menu: */
+ if (gEDataManager->statusBarContextMenuEnabled(uiCommon().managedVMUuid()))
+ actionPool()->action(UIActionIndexRT_M_View_M_StatusBar)->menu()->exec(statusBar()->mapToGlobal(position));
+}
+
+void UIMachineWindowNormal::sltHandleIndicatorContextMenuRequest(IndicatorType enmIndicatorType, const QPoint &indicatorPosition)
+{
+ /* Sanity check, this slot should be called if m_pIndicatorsPool present anyway: */
+ AssertPtrReturnVoid(m_pIndicatorsPool);
+ /* Determine action depending on indicator-type: */
+ UIAction *pAction = 0;
+ switch (enmIndicatorType)
+ {
+ case IndicatorType_HardDisks: pAction = actionPool()->action(UIActionIndexRT_M_Devices_M_HardDrives); break;
+ case IndicatorType_OpticalDisks: pAction = actionPool()->action(UIActionIndexRT_M_Devices_M_OpticalDevices); break;
+ case IndicatorType_FloppyDisks: pAction = actionPool()->action(UIActionIndexRT_M_Devices_M_FloppyDevices); break;
+ case IndicatorType_Audio: pAction = actionPool()->action(UIActionIndexRT_M_Devices_M_Audio); break;
+ case IndicatorType_Network: pAction = actionPool()->action(UIActionIndexRT_M_Devices_M_Network); break;
+ case IndicatorType_USB: pAction = actionPool()->action(UIActionIndexRT_M_Devices_M_USBDevices); break;
+ case IndicatorType_SharedFolders: pAction = actionPool()->action(UIActionIndexRT_M_Devices_M_SharedFolders); break;
+ case IndicatorType_Display: pAction = actionPool()->action(UIActionIndexRT_M_ViewPopup); break;
+ case IndicatorType_Recording: pAction = actionPool()->action(UIActionIndexRT_M_View_M_Recording); break;
+ case IndicatorType_Mouse: pAction = actionPool()->action(UIActionIndexRT_M_Input_M_Mouse); break;
+ case IndicatorType_Keyboard: pAction = actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard); break;
+ default: break;
+ }
+ /* Raise action's context-menu: */
+ if (pAction && pAction->isEnabled())
+ pAction->menu()->exec(m_pIndicatorsPool->mapIndicatorPositionToGlobal(enmIndicatorType, indicatorPosition));
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineWindowNormal::sltActionHovered(UIAction *pAction)
+{
+ /* Show the action message for a ten seconds: */
+ statusBar()->showMessage(pAction->statusTip(), 10000);
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineWindowNormal::prepareSessionConnections()
+{
+ /* Call to base-class: */
+ UIMachineWindow::prepareSessionConnections();
+
+ /* We should watch for console events: */
+ connect(machineLogic()->uisession(), &UISession::sigMediumChange,
+ this, &UIMachineWindowNormal::sltMediumChange);
+ connect(machineLogic()->uisession(), &UISession::sigUSBControllerChange,
+ this, &UIMachineWindowNormal::sltUSBControllerChange);
+ connect(machineLogic()->uisession(), &UISession::sigUSBDeviceStateChange,
+ this, &UIMachineWindowNormal::sltUSBDeviceStateChange);
+ connect(machineLogic()->uisession(), &UISession::sigAudioAdapterChange,
+ this, &UIMachineWindowNormal::sltAudioAdapterChange);
+ connect(machineLogic()->uisession(), &UISession::sigNetworkAdapterChange,
+ this, &UIMachineWindowNormal::sltNetworkAdapterChange);
+ connect(machineLogic()->uisession(), &UISession::sigSharedFolderChange,
+ this, &UIMachineWindowNormal::sltSharedFolderChange);
+ connect(machineLogic()->uisession(), &UISession::sigRecordingChange,
+ this, &UIMachineWindowNormal::sltRecordingChange);
+ connect(machineLogic()->uisession(), &UISession::sigCPUExecutionCapChange,
+ this, &UIMachineWindowNormal::sltCPUExecutionCapChange);
+ connect(machineLogic()->uisession(), &UISession::sigInitialized,
+ this, &UIMachineWindowNormal::sltHandleSessionInitialized);
+}
+
+#ifndef VBOX_WS_MAC
+void UIMachineWindowNormal::prepareMenu()
+{
+ /* Create menu-bar: */
+ setMenuBar(new UIMenuBar);
+ AssertPtrReturnVoid(menuBar());
+ {
+ /* Configure menu-bar: */
+ menuBar()->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(menuBar(), &UIMenuBar::customContextMenuRequested,
+ this, &UIMachineWindowNormal::sltHandleMenuBarContextMenuRequest);
+ connect(gEDataManager, &UIExtraDataManager::sigMenuBarConfigurationChange,
+ this, &UIMachineWindowNormal::sltHandleMenuBarConfigurationChange);
+ /* Update menu-bar: */
+ updateMenu();
+ }
+}
+#endif /* !VBOX_WS_MAC */
+
+void UIMachineWindowNormal::prepareStatusBar()
+{
+ /* Call to base-class: */
+ UIMachineWindow::prepareStatusBar();
+
+ /* Create status-bar: */
+ setStatusBar(new QIStatusBar);
+ AssertPtrReturnVoid(statusBar());
+ {
+ /* Configure status-bar: */
+ statusBar()->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(statusBar(), &QIStatusBar::customContextMenuRequested,
+ this, &UIMachineWindowNormal::sltHandleStatusBarContextMenuRequest);
+ /* Create indicator-pool: */
+ m_pIndicatorsPool = new UIIndicatorsPool(machineLogic()->uisession());
+ AssertPtrReturnVoid(m_pIndicatorsPool);
+ {
+ /* Configure indicator-pool: */
+ connect(m_pIndicatorsPool, &UIIndicatorsPool::sigContextMenuRequest,
+ this, &UIMachineWindowNormal::sltHandleIndicatorContextMenuRequest);
+ /* Add indicator-pool into status-bar: */
+ statusBar()->addPermanentWidget(m_pIndicatorsPool, 0);
+ }
+ /* Post-configure status-bar: */
+ connect(gEDataManager, &UIExtraDataManager::sigStatusBarConfigurationChange,
+ this, &UIMachineWindowNormal::sltHandleStatusBarConfigurationChange);
+#ifdef VBOX_WS_MAC
+ /* Make sure the status-bar is aware of action hovering: */
+ connect(actionPool(), &UIActionPool::sigActionHovered,
+ this, &UIMachineWindowNormal::sltActionHovered);
+#endif /* VBOX_WS_MAC */
+ }
+
+#ifdef VBOX_WS_MAC
+ /* For the status-bar on Cocoa: */
+ setUnifiedTitleAndToolBarOnMac(true);
+#endif /* VBOX_WS_MAC */
+}
+
+void UIMachineWindowNormal::prepareNotificationCenter()
+{
+ if (gpNotificationCenter && (m_uScreenId == 0))
+ gpNotificationCenter->setParent(centralWidget());
+}
+
+void UIMachineWindowNormal::prepareVisualState()
+{
+ /* Call to base-class: */
+ UIMachineWindow::prepareVisualState();
+
+#ifdef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ /* Customer request: The background has to go black: */
+ QPalette palette(centralWidget()->palette());
+ palette.setColor(centralWidget()->backgroundRole(), Qt::black);
+ centralWidget()->setPalette(palette);
+ centralWidget()->setAutoFillBackground(true);
+ setAutoFillBackground(true);
+#endif /* VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+
+#ifdef VBOX_WS_MAC
+ /* Beta label? */
+ if (uiCommon().showBetaLabel())
+ {
+ QPixmap betaLabel = ::betaLabel(QSize(74, darwinWindowTitleHeight(this) - 1));
+ ::darwinLabelWindow(this, &betaLabel);
+ }
+
+ /* Enable fullscreen support for every screen which requires it: */
+ if (darwinScreensHaveSeparateSpaces() || m_uScreenId == 0)
+ darwinEnableFullscreenSupport(this);
+ /* Register 'Zoom' button to use our full-screen: */
+ UICocoaApplication::instance()->registerCallbackForStandardWindowButton(this, StandardWindowButtonType_Zoom,
+ UIMachineWindow::handleStandardWindowButtonCallback);
+#endif /* VBOX_WS_MAC */
+}
+
+void UIMachineWindowNormal::loadSettings()
+{
+ /* Call to base-class: */
+ UIMachineWindow::loadSettings();
+
+ /* Load GUI customizations: */
+ {
+#ifndef VBOX_WS_MAC
+ /* Update menu-bar visibility: */
+ menuBar()->setVisible(actionPool()->action(UIActionIndexRT_M_View_M_MenuBar_T_Visibility)->isChecked());
+#endif /* !VBOX_WS_MAC */
+ /* Update status-bar visibility: */
+ statusBar()->setVisible(actionPool()->action(UIActionIndexRT_M_View_M_StatusBar_T_Visibility)->isChecked());
+ if (m_pIndicatorsPool)
+ m_pIndicatorsPool->setAutoUpdateIndicatorStates(statusBar()->isVisible() && uisession()->isRunning());
+ }
+
+#ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ /* Load window geometry: */
+ {
+ /* Load extra-data: */
+ QRect geo = gEDataManager->machineWindowGeometry(machineLogic()->visualStateType(),
+ m_uScreenId, uiCommon().managedVMUuid());
+
+ /* If we do have proper geometry: */
+ if (!geo.isNull())
+ {
+ /* Restore window geometry: */
+ m_geometry = geo;
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, m_geometry);
+
+ /* If previous machine-state was NOT SAVED => normalize window to the optimal-size: */
+ if (machine().GetState() != KMachineState_Saved && machine().GetState() != KMachineState_AbortedSaved)
+ normalizeGeometry(false /* adjust position */, shouldResizeToGuestDisplay());
+
+ /* Maximize window (if necessary): */
+ if (gEDataManager->machineWindowShouldBeMaximized(machineLogic()->visualStateType(),
+ m_uScreenId, uiCommon().managedVMUuid()))
+ setWindowState(windowState() | Qt::WindowMaximized);
+ }
+ /* If we do NOT have proper geometry: */
+ else
+ {
+ /* Normalize window to the optimal size: */
+ normalizeGeometry(true /* adjust position */, shouldResizeToGuestDisplay());
+
+ /* Move it to the screen-center: */
+ m_geometry = geometry();
+ m_geometry.moveCenter(gpDesktop->availableGeometry(this).center());
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, m_geometry);
+ }
+
+ /* Normalize to the optimal size: */
+#ifdef VBOX_WS_X11
+ QTimer::singleShot(0, this, SLOT(sltNormalizeGeometry()));
+#else /* !VBOX_WS_X11 */
+ normalizeGeometry(true /* adjust position */, shouldResizeToGuestDisplay());
+#endif /* !VBOX_WS_X11 */
+ }
+#endif /* VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+}
+
+void UIMachineWindowNormal::cleanupVisualState()
+{
+#ifdef VBOX_WS_MAC
+ /* Unregister 'Zoom' button from using our full-screen: */
+ UICocoaApplication::instance()->unregisterCallbackForStandardWindowButton(this, StandardWindowButtonType_Zoom);
+#endif /* VBOX_WS_MAC */
+}
+
+void UIMachineWindowNormal::cleanupNotificationCenter()
+{
+ if (gpNotificationCenter && (gpNotificationCenter->parent() == centralWidget()))
+ gpNotificationCenter->setParent(0);
+}
+
+void UIMachineWindowNormal::cleanupStatusBar()
+{
+ delete m_pIndicatorsPool;
+ m_pIndicatorsPool = 0;
+}
+
+void UIMachineWindowNormal::cleanupSessionConnections()
+{
+ /* We should stop watching for console events: */
+ disconnect(machineLogic()->uisession(), &UISession::sigMediumChange,
+ this, &UIMachineWindowNormal::sltMediumChange);
+ disconnect(machineLogic()->uisession(), &UISession::sigUSBControllerChange,
+ this, &UIMachineWindowNormal::sltUSBControllerChange);
+ disconnect(machineLogic()->uisession(), &UISession::sigUSBDeviceStateChange,
+ this, &UIMachineWindowNormal::sltUSBDeviceStateChange);
+ disconnect(machineLogic()->uisession(), &UISession::sigNetworkAdapterChange,
+ this, &UIMachineWindowNormal::sltNetworkAdapterChange);
+ disconnect(machineLogic()->uisession(), &UISession::sigAudioAdapterChange,
+ this, &UIMachineWindowNormal::sltAudioAdapterChange);
+ disconnect(machineLogic()->uisession(), &UISession::sigSharedFolderChange,
+ this, &UIMachineWindowNormal::sltSharedFolderChange);
+ disconnect(machineLogic()->uisession(), &UISession::sigRecordingChange,
+ this, &UIMachineWindowNormal::sltRecordingChange);
+ disconnect(machineLogic()->uisession(), &UISession::sigCPUExecutionCapChange,
+ this, &UIMachineWindowNormal::sltCPUExecutionCapChange);
+
+ /* Call to base-class: */
+ UIMachineWindow::cleanupSessionConnections();
+}
+
+bool UIMachineWindowNormal::event(QEvent *pEvent)
+{
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ {
+#ifdef VBOX_WS_X11
+ /* Prevent handling if fake screen detected: */
+ if (UIDesktopWidgetWatchdog::isFakeScreenDetected())
+ break;
+#endif /* VBOX_WS_X11 */
+
+ QResizeEvent *pResizeEvent = static_cast<QResizeEvent*>(pEvent);
+ if (!isMaximizedChecked())
+ {
+ m_geometry.setSize(pResizeEvent->size());
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Update debugger window position: */
+ updateDbgWindows();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+ }
+
+ /* Restart geometry save timer: */
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+
+ /* Let listeners know about geometry changes: */
+ emit sigGeometryChange(geometry());
+ break;
+ }
+ case QEvent::Move:
+ {
+#ifdef VBOX_WS_X11
+ /* Prevent handling if fake screen detected: */
+ if (UIDesktopWidgetWatchdog::isFakeScreenDetected())
+ break;
+#endif /* VBOX_WS_X11 */
+
+ if (!isMaximizedChecked())
+ {
+ m_geometry.moveTo(geometry().x(), geometry().y());
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Update debugger window position: */
+ updateDbgWindows();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+ }
+
+ /* Restart geometry save timer: */
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+
+ /* Let listeners know about geometry changes: */
+ emit sigGeometryChange(geometry());
+ break;
+ }
+ case QEvent::WindowActivate:
+ {
+ /* Let listeners know about geometry changes: */
+ emit sigGeometryChange(geometry());
+ break;
+ }
+ /* Handle timer event started above: */
+ case QEvent::Timer:
+ {
+ QTimerEvent *pTimerEvent = static_cast<QTimerEvent*>(pEvent);
+ if (pTimerEvent->timerId() == m_iGeometrySaveTimerId)
+ {
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = -1;
+
+ /* HACK ALERT! Just ignore this if it arrives to late to be handled. I typically get
+ these when the COM shutdown on windows flushes pending queue events. The result
+ is typically a bunch of assertions, but sometimes a NULL pointer dereference for
+ variety. Going forward here will probably re-instantiate some global objects
+ which were already cleaned up, so generally a bad idea.
+
+ A sample assertion stack:
+ # Child-SP RetAddr Call Site
+ 00 00000052`300fe370 00007fff`36ac2cc9 UICommon!CVirtualBox::GetExtraDataKeys+0x80 [E:\vbox\svn\trunk\out\win.amd64\debug\obj\UICommon\include\COMWrappers.cpp @ 3851]
+ 01 00000052`300fe430 00007fff`36ac2bf8 UICommon!UIExtraDataManager::prepareGlobalExtraDataMap+0xb9 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp @ 4845]
+ 02 00000052`300fe590 00007fff`36ab1896 UICommon!UIExtraDataManager::prepare+0x28 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp @ 4833]
+ 03 00000052`300fe5c0 00007ff7`69db2897 UICommon!UIExtraDataManager::instance+0x66 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp @ 2011]
+ 04 00000052`300fe610 00007fff`35274990 VirtualBoxVM!UIMachineWindowNormal::event+0x4b7 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\runtime\normal\UIMachineWindowNormal.cpp @ 546]
+ 05 00000052`300fe6e0 00007fff`35273a13 Qt5WidgetsVBox!QApplicationPrivate::notify_helper+0x110
+ 06 00000052`300fe710 00007fff`3cc3240a Qt5WidgetsVBox!QApplication::notify+0x18b3
+ 07 00000052`300fec50 00007fff`3cc7cd09 Qt5CoreVBox!QCoreApplication::notifyInternal2+0xba
+ 08 00000052`300fecc0 00007fff`3cc7bf7a Qt5CoreVBox!QEventDispatcherWin32Private::sendTimerEvent+0xf9
+ 09 00000052`300fed10 00007fff`7631e7e8 Qt5CoreVBox!QEventDispatcherWin32::processEvents+0xc4a
+ 0a 00000052`300fee30 00007fff`7631e229 USER32!UserCallWinProcCheckWow+0x2f8
+ 0b 00000052`300fefc0 00007fff`370c2075 USER32!DispatchMessageWorker+0x249
+ 0c 00000052`300ff040 00007fff`370c20e5 UICommon!com::NativeEventQueue::dispatchMessageOnWindows+0x145 [E:\vbox\svn\trunk\src\VBox\Main\glue\NativeEventQueue.cpp @ 416]
+ 0d 00000052`300ff090 00007fff`370c1b19 UICommon!com::processPendingEvents+0x55 [E:\vbox\svn\trunk\src\VBox\Main\glue\NativeEventQueue.cpp @ 435]
+ 0e 00000052`300ff130 00007fff`370c1ebd UICommon!com::NativeEventQueue::processEventQueue+0x149 [E:\vbox\svn\trunk\src\VBox\Main\glue\NativeEventQueue.cpp @ 562]
+ 0f 00000052`300ff1d0 00007fff`370bfa9a UICommon!com::NativeEventQueue::uninit+0x2d [E:\vbox\svn\trunk\src\VBox\Main\glue\NativeEventQueue.cpp @ 260]
+ 10 00000052`300ff210 00007fff`36b098e4 UICommon!com::Shutdown+0x5a [E:\vbox\svn\trunk\src\VBox\Main\glue\initterm.cpp @ 746]
+ 11 00000052`300ff250 00007fff`36b88c43 UICommon!COMBase::CleanupCOM+0x74 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\globals\COMDefs.cpp @ 168]
+ 12 00000052`300ff2c0 00007fff`36a700c8 UICommon!UICommon::cleanup+0x313 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\globals\UICommon.cpp @ 849]
+ 13 00000052`300ff340 00007fff`36a82ab1 UICommon!UICommon::sltCleanup+0x28 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\globals\UICommon.h @ 580]
+ 14 00000052`300ff370 00007fff`36a81a9c UICommon!QtPrivate::FunctorCall<QtPrivate::IndexesList<>,QtPrivate::List<>,void,void (__cdecl UIMessageCenter::*)(void)>::call+0x31 [E:\vbox\svn\trunk\tools\win.amd64\qt\v5.15.2-r349\include\QtCore\qobjectdefs_impl.h @ 152]
+ 15 00000052`300ff3b0 00007fff`36a82e45 UICommon!QtPrivate::FunctionPointer<void (__cdecl UIMessageCenter::*)(void)>::call<QtPrivate::List<>,void>+0x3c [E:\vbox\svn\trunk\tools\win.amd64\qt\v5.15.2-r349\include\QtCore\qobjectdefs_impl.h @ 186]
+ 16 00000052`300ff3e0 00007fff`3cc51689 UICommon!QtPrivate::QSlotObject<void (__cdecl UIMessageCenter::*)(void),QtPrivate::List<>,void>::impl+0x95 [E:\vbox\svn\trunk\tools\win.amd64\qt\v5.15.2-r349\include\QtCore\qobjectdefs_impl.h @ 419]
+ 17 00000052`300ff430 00007fff`3cc31465 Qt5CoreVBox!QObject::qt_static_metacall+0x1409
+ 18 00000052`300ff580 00007fff`3cc313ef Qt5CoreVBox!QCoreApplicationPrivate::execCleanup+0x55
+ 19 00000052`300ff5c0 00007ff7`69ce3b7a Qt5CoreVBox!QCoreApplication::exec+0x16f
+ 1a 00000052`300ff620 00007ff7`69ce4174 VirtualBoxVM!TrustedMain+0x47a [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\main.cpp @ 570]
+ 1b 00000052`300ff8b0 00007ff7`69e08af8 VirtualBoxVM!main+0x4a4 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\main.cpp @ 739]
+ 1c (Inline Function) --------`-------- VirtualBoxVM!invoke_main+0x22 [d:\A01\_work\6\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
+ 1d 00000052`300ffa00 00007fff`75107034 VirtualBoxVM!__scrt_common_main_seh+0x10c [d:\A01\_work\6\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
+ 1e 00000052`300ffa40 00007fff`76ae2651 KERNEL32!BaseThreadInitThunk+0x14
+ 1f 00000052`300ffa70 00000000`00000000 ntdll!RtlUserThreadStart+0x21 */
+ if (!UICommon::instance()->isCleaningUp())
+ {
+ LogRel2(("GUI: UIMachineWindowNormal: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ m_geometry.x(), m_geometry.y(), m_geometry.width(), m_geometry.height()));
+ gEDataManager->setMachineWindowGeometry(machineLogic()->visualStateType(),
+ m_uScreenId, m_geometry,
+ isMaximizedChecked(), uiCommon().managedVMUuid());
+ }
+ else
+ LogRel2(("GUI: UIMachineWindowNormal: Ignoring geometry save timer arriving during cleanup\n"));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return UIMachineWindow::event(pEvent);
+}
+
+void UIMachineWindowNormal::showInNecessaryMode()
+{
+ /* Make sure this window should be shown at all: */
+ if (!uisession()->isScreenVisible(m_uScreenId))
+ return hide();
+
+ /* Make sure this window is not minimized: */
+ if (isMinimized())
+ return;
+
+ /* Show in normal mode: */
+ show();
+
+ /* Normalize machine-window geometry: */
+ normalizeGeometry(true /* adjust position */, shouldResizeToGuestDisplay());
+
+ /* Make sure machine-view have focus: */
+ m_pMachineView->setFocus();
+}
+
+void UIMachineWindowNormal::restoreCachedGeometry()
+{
+ /* Restore the geometry cached by the window: */
+ resize(m_geometry.size());
+ move(m_geometry.topLeft());
+
+ /* Adjust machine-view accordingly: */
+ adjustMachineViewSize();
+}
+
+void UIMachineWindowNormal::normalizeGeometry(bool fAdjustPosition, bool fResizeToGuestDisplay)
+{
+#ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
+ /* Skip if maximized: */
+ if (isMaximized())
+ return;
+
+ /* Calculate client window offsets: */
+ QRect frGeo = frameGeometry();
+ const QRect geo = geometry();
+ const int dl = geo.left() - frGeo.left();
+ const int dt = geo.top() - frGeo.top();
+ const int dr = frGeo.right() - geo.right();
+ const int db = frGeo.bottom() - geo.bottom();
+
+ /* Get the best size w/o scroll-bars: */
+ if (fResizeToGuestDisplay)
+ {
+ /* Get widget size-hint: */
+ QSize sh = sizeHint();
+
+ /* If guest-screen auto-resize is not enabled
+ * or guest-additions doesn't support graphics
+ * we should deduce widget's size-hint on visible scroll-bar's hint: */
+ if ( !machineView()->isGuestAutoresizeEnabled()
+ || !uisession()->isGuestSupportsGraphics())
+ {
+ if (machineView()->verticalScrollBar()->isVisible())
+ sh -= QSize(machineView()->verticalScrollBar()->sizeHint().width(), 0);
+ if (machineView()->horizontalScrollBar()->isVisible())
+ sh -= QSize(0, machineView()->horizontalScrollBar()->sizeHint().height());
+ }
+
+ /* Resize the frame to fit the contents: */
+ sh -= size();
+ frGeo.setRight(frGeo.right() + sh.width());
+ frGeo.setBottom(frGeo.bottom() + sh.height());
+ }
+
+ /* Adjust size/position if necessary: */
+ QRect frGeoNew = fAdjustPosition
+ ? UIDesktopWidgetWatchdog::normalizeGeometry(frGeo, gpDesktop->overallAvailableRegion())
+ : frGeo;
+
+ /* If guest-screen auto-resize is not enabled
+ * or the guest-additions doesn't support graphics
+ * we should take scroll-bars size-hints into account: */
+ if ( frGeoNew != frGeo
+ && ( !machineView()->isGuestAutoresizeEnabled()
+ || !uisession()->isGuestSupportsGraphics()))
+ {
+ /* Determine whether we need additional space for one or both scroll-bars: */
+ QSize addition;
+ if (frGeoNew.height() < frGeo.height())
+ addition += QSize(machineView()->verticalScrollBar()->sizeHint().width() + 1, 0);
+ if (frGeoNew.width() < frGeo.width())
+ addition += QSize(0, machineView()->horizontalScrollBar()->sizeHint().height() + 1);
+
+ /* Resize the frame to fit the contents: */
+ frGeoNew.setRight(frGeoNew.right() + addition.width());
+ frGeoNew.setBottom(frGeoNew.bottom() + addition.height());
+
+ /* Adjust size/position again: */
+ frGeoNew = UIDesktopWidgetWatchdog::normalizeGeometry(frGeoNew, gpDesktop->overallAvailableRegion());
+ }
+
+ /* Finally, set the frame geometry: */
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this,
+ frGeoNew.left() + dl, frGeoNew.top() + dt,
+ frGeoNew.width() - dl - dr, frGeoNew.height() - dt - db);
+#else /* VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+ /* Customer request: There should no be
+ * machine-window resize/move on machine-view resize: */
+ Q_UNUSED(fAdjustPosition);
+ Q_UNUSED(fResizeToGuestDisplay);
+#endif /* VBOX_GUI_WITH_CUSTOMIZATIONS1 */
+}
+
+void UIMachineWindowNormal::updateAppearanceOf(int iElement)
+{
+ /* Call to base-class: */
+ UIMachineWindow::updateAppearanceOf(iElement);
+
+ /* Set status-bar indicator-pool auto update timer: */
+ if ( m_pIndicatorsPool
+ && iElement & UIVisualElement_IndicatorPoolStuff)
+ m_pIndicatorsPool->setAutoUpdateIndicatorStates(statusBar()->isVisible() && uisession()->isRunning());
+ /* Update status-bar indicator-pool appearance only when status-bar is visible: */
+ if ( m_pIndicatorsPool
+ && statusBar()->isVisible())
+ {
+ /* If VM is running: */
+ if (uisession()->isRunning())
+ {
+ if (iElement & UIVisualElement_HDStuff)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_HardDisks);
+ if (iElement & UIVisualElement_CDStuff)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_OpticalDisks);
+ if (iElement & UIVisualElement_FDStuff)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_FloppyDisks);
+ if (iElement & UIVisualElement_AudioStuff)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_Audio);
+ if (iElement & UIVisualElement_NetworkStuff)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_Network);
+ if (iElement & UIVisualElement_USBStuff)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_USB);
+ if (iElement & UIVisualElement_SharedFolderStuff)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_SharedFolders);
+ if (iElement & UIVisualElement_Display)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_Display);
+ if (iElement & UIVisualElement_FeaturesStuff)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_Features);
+ }
+ /* If VM is running or paused: */
+ if (uisession()->isRunning() || uisession()->isPaused())
+ {
+ if (iElement & UIVisualElement_Recording)
+ m_pIndicatorsPool->updateAppearance(IndicatorType_Recording);
+ }
+ }
+}
+
+#ifndef VBOX_WS_MAC
+void UIMachineWindowNormal::updateMenu()
+{
+ /* Rebuild menu-bar: */
+ menuBar()->clear();
+ foreach (QMenu *pMenu, actionPool()->menus())
+ menuBar()->addMenu(pMenu);
+}
+#endif /* !VBOX_WS_MAC */
+
+bool UIMachineWindowNormal::isMaximizedChecked()
+{
+#ifdef VBOX_WS_MAC
+ /* On the Mac the WindowStateChange signal doesn't seems to be delivered
+ * when the user get out of the maximized state. So check this ourself. */
+ return ::darwinIsWindowMaximized(this);
+#else /* VBOX_WS_MAC */
+ return isMaximized();
+#endif /* !VBOX_WS_MAC */
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.h b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.h
new file mode 100644
index 00000000..b5debb05
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.h
@@ -0,0 +1,159 @@
+/* $Id: UIMachineWindowNormal.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindowNormal class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_normal_UIMachineWindowNormal_h
+#define FEQT_INCLUDED_SRC_runtime_normal_UIMachineWindowNormal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineWindow.h"
+
+/* Forward declarations: */
+class CMediumAttachment;
+class UIIndicatorsPool;
+class UIAction;
+
+/** UIMachineWindow subclass used as normal machine window implementation. */
+class UIMachineWindowNormal : public UIMachineWindow
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about geometry change. */
+ void sigGeometryChange(const QRect &rect);
+
+public:
+
+ /** Constructor, passes @a pMachineLogic and @a uScreenId to the UIMachineWindow constructor. */
+ UIMachineWindowNormal(UIMachineLogic *pMachineLogic, ulong uScreenId);
+
+private slots:
+
+ /** Handles machine state change event. */
+ void sltMachineStateChanged();
+ /** Handles medium change event. */
+ void sltMediumChange(const CMediumAttachment &attachment);
+ /** Handles USB controller change event. */
+ void sltUSBControllerChange();
+ /** Handles USB device state change event. */
+ void sltUSBDeviceStateChange();
+ /** Handles audio adapter change event. */
+ void sltAudioAdapterChange();
+ /** Handles network adapter change event. */
+ void sltNetworkAdapterChange();
+ /** Handles shared folder change event. */
+ void sltSharedFolderChange();
+ /** Handles recording change event. */
+ void sltRecordingChange();
+ /** Handles CPU execution cap change event. */
+ void sltCPUExecutionCapChange();
+ /** Handles UISession initialized event. */
+ void sltHandleSessionInitialized();
+
+#ifndef RT_OS_DARWIN
+ /** Handles menu-bar configuration-change. */
+ void sltHandleMenuBarConfigurationChange(const QUuid &uMachineID);
+ /** Handles menu-bar context-menu-request. */
+ void sltHandleMenuBarContextMenuRequest(const QPoint &position);
+#endif /* !RT_OS_DARWIN */
+
+ /** Handles status-bar configuration-change. */
+ void sltHandleStatusBarConfigurationChange(const QUuid &uMachineID);
+ /** Handles status-bar context-menu-request. */
+ void sltHandleStatusBarContextMenuRequest(const QPoint &position);
+ /** Handles status-bar indicator context-menu-request. */
+ void sltHandleIndicatorContextMenuRequest(IndicatorType enmIndicatorType, const QPoint &indicatorPosition);
+
+#ifdef VBOX_WS_MAC
+ /** Handles signal about some @a pAction hovered. */
+ void sltActionHovered(UIAction *pAction);
+#endif /* VBOX_WS_MAC */
+
+private:
+
+ /** Prepare session connections routine. */
+ void prepareSessionConnections();
+#ifndef VBOX_WS_MAC
+ /** Prepare menu routine. */
+ void prepareMenu();
+#endif /* !VBOX_WS_MAC */
+ /** Prepare status-bar routine. */
+ void prepareStatusBar();
+ /** Prepare notification-center routine. */
+ void prepareNotificationCenter();
+ /** Prepare visual-state routine. */
+ void prepareVisualState();
+ /** Load settings routine. */
+ void loadSettings();
+
+ /** Cleanup visual-state routine. */
+ void cleanupVisualState();
+ /** Cleanup notification-center routine. */
+ void cleanupNotificationCenter();
+ /** Cleanup status-bar routine. */
+ void cleanupStatusBar();
+ /** Cleanup session connections routine. */
+ void cleanupSessionConnections();
+
+ /** Updates visibility according to visual-state. */
+ void showInNecessaryMode();
+
+ /** Restores cached window geometry. */
+ virtual void restoreCachedGeometry() RT_OVERRIDE;
+
+ /** Performs window geometry normalization according to guest-size and host's available geometry.
+ * @param fAdjustPosition Determines whether is it necessary to adjust position as well.
+ * @param fResizeToGuestDisplay Determines whether is it necessary to resize the window to fit to guest display size. */
+ virtual void normalizeGeometry(bool fAdjustPosition, bool fResizeToGuestDisplay) RT_OVERRIDE;
+
+ /** Common update routine. */
+ void updateAppearanceOf(int aElement);
+
+#ifndef VBOX_WS_MAC
+ /** Updates menu-bar content. */
+ void updateMenu();
+#endif /* !VBOX_WS_MAC */
+
+ /** Common @a pEvent handler. */
+ bool event(QEvent *pEvent);
+
+ /** Returns whether this window is maximized. */
+ bool isMaximizedChecked();
+
+ /** Holds the indicator-pool instance. */
+ UIIndicatorsPool *m_pIndicatorsPool;
+
+ /** Holds the current window geometry. */
+ QRect m_geometry;
+ /** Holds the geometry save timer ID. */
+ int m_iGeometrySaveTimerId;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_normal_UIMachineWindowNormal_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/scale/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/runtime/scale/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/scale/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIKeyboardHandlerScale.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIKeyboardHandlerScale.cpp
new file mode 100644
index 00000000..81ab0aca
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIKeyboardHandlerScale.cpp
@@ -0,0 +1,91 @@
+/* $Id: UIKeyboardHandlerScale.cpp $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandlerScale class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#ifndef VBOX_WS_MAC
+# include <QKeyEvent>
+# include <QTimer>
+#endif /* !VBOX_WS_MAC */
+
+/* GUI includes: */
+#include "UIKeyboardHandlerScale.h"
+#ifndef VBOX_WS_MAC
+# include "UIMachineLogic.h"
+# include "UIShortcutPool.h"
+#endif /* !VBOX_WS_MAC */
+
+
+#ifndef VBOX_WS_MAC
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+#endif /* !VBOX_WS_MAC */
+
+UIKeyboardHandlerScale::UIKeyboardHandlerScale(UIMachineLogic* pMachineLogic)
+ : UIKeyboardHandler(pMachineLogic)
+{
+}
+
+UIKeyboardHandlerScale::~UIKeyboardHandlerScale()
+{
+}
+
+#ifndef VBOX_WS_MAC
+bool UIKeyboardHandlerScale::eventFilter(QObject *pWatchedObject, QEvent *pEvent)
+{
+ /* Check if pWatchedObject object is view: */
+ if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject))
+ {
+ /* Get corresponding screen index: */
+ ulong uScreenId = m_views.key(pWatchedView);
+ NOREF(uScreenId);
+ /* Handle view events: */
+ switch (pEvent->type())
+ {
+ case QEvent::KeyPress:
+ {
+ /* Get key-event: */
+ QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
+ /* Process Host+Home for menu popup: */
+ if ( isHostKeyPressed()
+ && gShortcutPool->shortcut(GUI_Input_MachineShortcuts, QString("PopupMenu")).sequences().contains(QKeySequence(pKeyEvent->key())))
+ {
+ /* Post request to show popup-menu: */
+ QTimer::singleShot(0, m_pMachineLogic, SLOT(sltInvokePopupMenu()));
+ /* Filter-out this event: */
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Else just propagate to base-class: */
+ return UIKeyboardHandler::eventFilter(pWatchedObject, pEvent);
+}
+#endif /* !VBOX_WS_MAC */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIKeyboardHandlerScale.h b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIKeyboardHandlerScale.h
new file mode 100644
index 00000000..fe8a828f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIKeyboardHandlerScale.h
@@ -0,0 +1,58 @@
+/* $Id: UIKeyboardHandlerScale.h $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandlerScale class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_scale_UIKeyboardHandlerScale_h
+#define FEQT_INCLUDED_SRC_runtime_scale_UIKeyboardHandlerScale_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIKeyboardHandler.h"
+
+/** UIKeyboardHandler reimplementation
+ * providing machine-logic with PopupMenu keyboard handler. */
+class UIKeyboardHandlerScale : public UIKeyboardHandler
+{
+ Q_OBJECT;
+
+public:
+
+ /** Scale keyboard-handler constructor. */
+ UIKeyboardHandlerScale(UIMachineLogic *pMachineLogic);
+ /** Scale keyboard-handler destructor. */
+ virtual ~UIKeyboardHandlerScale();
+
+private:
+
+#ifndef VBOX_WS_MAC
+ /** General event-filter. */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+#endif /* !VBOX_WS_MAC */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_scale_UIKeyboardHandlerScale_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineLogicScale.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineLogicScale.cpp
new file mode 100644
index 00000000..ccbf3e92
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineLogicScale.cpp
@@ -0,0 +1,240 @@
+/* $Id: UIMachineLogicScale.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogicScale class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#ifndef VBOX_WS_MAC
+# include <QTimer>
+#endif
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIMessageCenter.h"
+#include "UISession.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogicScale.h"
+#include "UIMachineWindow.h"
+#include "UIShortcutPool.h"
+#ifndef VBOX_WS_MAC
+# include "QIMenu.h"
+#else
+# include "VBoxUtils.h"
+#endif
+
+/* COM includes: */
+#include "CGraphicsAdapter.h"
+
+
+UIMachineLogicScale::UIMachineLogicScale(QObject *pParent, UISession *pSession)
+ : UIMachineLogic(pParent, pSession, UIVisualStateType_Scale)
+#ifndef VBOX_WS_MAC
+ , m_pPopupMenu(0)
+#endif /* !VBOX_WS_MAC */
+{
+}
+
+bool UIMachineLogicScale::checkAvailability()
+{
+ /* Show the info message. */
+ const UIShortcut &shortcut =
+ gShortcutPool->shortcut(actionPool()->shortcutsExtraDataID(),
+ actionPool()->action(UIActionIndexRT_M_View_T_Scale)->shortcutExtraDataID());
+ const QString strHotKey = QString("Host+%1").arg(shortcut.primaryToPortableText());
+ if (!msgCenter().confirmGoingScale(strHotKey))
+ return false;
+
+ return true;
+}
+
+#ifndef VBOX_WS_MAC
+void UIMachineLogicScale::sltInvokePopupMenu()
+{
+ /* Popup main-menu if present: */
+ if (m_pPopupMenu && !m_pPopupMenu->isEmpty())
+ {
+ m_pPopupMenu->popup(activeMachineWindow()->geometry().center());
+ QTimer::singleShot(0, m_pPopupMenu, SLOT(sltHighlightFirstAction()));
+ }
+}
+#endif /* !VBOX_WS_MAC */
+
+void UIMachineLogicScale::sltHostScreenAvailableAreaChange()
+{
+#ifdef VBOX_WS_X11
+ /* Prevent handling if fake screen detected: */
+ if (UIDesktopWidgetWatchdog::isFakeScreenDetected())
+ return;
+
+ /* Make sure all machine-window(s) have previous but normalized geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->restoreCachedGeometry();
+#endif /* VBOX_WS_X11 */
+
+ /* Call to base-class: */
+ UIMachineLogic::sltHostScreenAvailableAreaChange();
+}
+
+void UIMachineLogicScale::prepareActionGroups()
+{
+ /* Call to base-class: */
+ UIMachineLogic::prepareActionGroups();
+
+ /* Restrict 'Adjust Window', 'Guest Autoresize', 'Status Bar', 'Resize' and 'Rescale' actions for 'View' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
+ (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
+ (UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_GuestAutoresize |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Rescale));
+
+ /* Take care of view-action toggle state: */
+ UIAction *pActionScale = actionPool()->action(UIActionIndexRT_M_View_T_Scale);
+ if (!pActionScale->isChecked())
+ {
+ pActionScale->blockSignals(true);
+ pActionScale->setChecked(true);
+ pActionScale->blockSignals(false);
+ }
+}
+
+void UIMachineLogicScale::prepareActionConnections()
+{
+ /* Call to base-class: */
+ UIMachineLogic::prepareActionConnections();
+
+ /* Prepare 'View' actions connections: */
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &QAction::triggered,
+ this, &UIMachineLogicScale::sltChangeVisualStateToNormal);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &QAction::triggered,
+ this, &UIMachineLogicScale::sltChangeVisualStateToFullscreen);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &QAction::triggered,
+ this, &UIMachineLogicScale::sltChangeVisualStateToSeamless);
+}
+
+void UIMachineLogicScale::prepareMachineWindows()
+{
+ /* Do not create machine-window(s) if they created already: */
+ if (isMachineWindowsCreated())
+ return;
+
+#ifdef VBOX_WS_MAC /// @todo Is that really need here?
+ /* We have to make sure that we are getting the front most process.
+ * This is necessary for Qt versions > 4.3.3: */
+ ::darwinSetFrontMostProcess();
+#endif /* VBOX_WS_MAC */
+
+ /* Get monitors count: */
+ ulong uMonitorCount = machine().GetGraphicsAdapter().GetMonitorCount();
+ /* Create machine window(s): */
+ for (ulong uScreenId = 0; uScreenId < uMonitorCount; ++ uScreenId)
+ addMachineWindow(UIMachineWindow::create(this, uScreenId));
+ /* Order machine window(s): */
+ for (ulong uScreenId = uMonitorCount; uScreenId > 0; -- uScreenId)
+ machineWindows()[uScreenId - 1]->raise();
+
+ /* Listen for frame-buffer resize: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ connect(pMachineWindow, &UIMachineWindow::sigFrameBufferResize,
+ this, &UIMachineLogicScale::sigFrameBufferResize);
+ emit sigFrameBufferResize();
+
+ /* Mark machine-window(s) created: */
+ setMachineWindowsCreated(true);
+}
+
+#ifndef VBOX_WS_MAC
+void UIMachineLogicScale::prepareMenu()
+{
+ /* Prepare popup-menu: */
+ m_pPopupMenu = new QIMenu;
+ AssertPtrReturnVoid(m_pPopupMenu);
+ {
+ /* Prepare popup-menu: */
+ foreach (QMenu *pMenu, actionPool()->menus())
+ m_pPopupMenu->addMenu(pMenu);
+ }
+}
+#endif /* !VBOX_WS_MAC */
+
+#ifndef VBOX_WS_MAC
+void UIMachineLogicScale::cleanupMenu()
+{
+ /* Cleanup popup-menu: */
+ delete m_pPopupMenu;
+ m_pPopupMenu = 0;
+}
+#endif /* !VBOX_WS_MAC */
+
+void UIMachineLogicScale::cleanupMachineWindows()
+{
+ /* Do not destroy machine-window(s) if they destroyed already: */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Mark machine-window(s) destroyed: */
+ setMachineWindowsCreated(false);
+
+ /* Cleanup machine-window(s): */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ UIMachineWindow::destroy(pMachineWindow);
+}
+
+void UIMachineLogicScale::cleanupActionConnections()
+{
+ /* "View" actions disconnections: */
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &QAction::triggered,
+ this, &UIMachineLogicScale::sltChangeVisualStateToNormal);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &QAction::triggered,
+ this, &UIMachineLogicScale::sltChangeVisualStateToFullscreen);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &QAction::triggered,
+ this, &UIMachineLogicScale::sltChangeVisualStateToSeamless);
+
+ /* Call to base-class: */
+ UIMachineLogic::cleanupActionConnections();
+
+}
+
+void UIMachineLogicScale::cleanupActionGroups()
+{
+ /* Take care of view-action toggle state: */
+ UIAction *pActionScale = actionPool()->action(UIActionIndexRT_M_View_T_Scale);
+ if (pActionScale->isChecked())
+ {
+ pActionScale->blockSignals(true);
+ pActionScale->setChecked(false);
+ pActionScale->blockSignals(false);
+ }
+
+ /* Allow 'Adjust Window', 'Guest Autoresize', 'Status Bar' and 'Resize' actions for 'View' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid);
+
+ /* Call to base-class: */
+ UIMachineLogic::cleanupActionGroups();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineLogicScale.h b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineLogicScale.h
new file mode 100644
index 00000000..a43536ce
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineLogicScale.h
@@ -0,0 +1,90 @@
+/* $Id: UIMachineLogicScale.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogicScale class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_scale_UIMachineLogicScale_h
+#define FEQT_INCLUDED_SRC_runtime_scale_UIMachineLogicScale_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineLogic.h"
+
+/** UIMachineLogic subclass used as scaled machine logic implementation. */
+class UIMachineLogicScale : public UIMachineLogic
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs scaled logic passing @a pParent to the base-class.
+ * @param pSession Brings the session UI reference. */
+ UIMachineLogicScale(QObject *pParent, UISession *pSession);
+
+protected:
+
+ /* Check if this logic is available: */
+ bool checkAvailability();
+
+ /** Returns machine-window flags for 'Scale' machine-logic and passed @a uScreenId. */
+ virtual Qt::WindowFlags windowFlags(ulong uScreenId) const { Q_UNUSED(uScreenId); return Qt::Window; }
+
+private slots:
+
+#ifndef RT_OS_DARWIN
+ /** Invokes popup-menu. */
+ void sltInvokePopupMenu();
+#endif /* !RT_OS_DARWIN */
+
+ /** Handles host-screen available-area change. */
+ virtual void sltHostScreenAvailableAreaChange() RT_OVERRIDE;
+
+private:
+
+ /* Prepare helpers: */
+ void prepareActionGroups();
+ void prepareActionConnections();
+ void prepareMachineWindows();
+#ifndef RT_OS_DARWIN
+ void prepareMenu();
+#endif /* !RT_OS_DARWIN */
+
+ /* Cleanup helpers: */
+#ifndef RT_OS_DARWIN
+ void cleanupMenu();
+#endif /* !RT_OS_DARWIN */
+ void cleanupMachineWindows();
+ void cleanupActionConnections();
+ void cleanupActionGroups();
+
+#ifndef RT_OS_DARWIN
+ /** Holds the popup-menu instance. */
+ QMenu *m_pPopupMenu;
+#endif /* !RT_OS_DARWIN */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_scale_UIMachineLogicScale_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineViewScale.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineViewScale.cpp
new file mode 100644
index 00000000..51e06273
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineViewScale.cpp
@@ -0,0 +1,228 @@
+/* $Id: UIMachineViewScale.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineViewScale class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QMainWindow>
+#include <QTimer>
+
+/* GUI includes */
+#include "UICommon.h"
+#include "UISession.h"
+#include "UIMachineLogic.h"
+#include "UIMachineWindow.h"
+#include "UIMachineViewScale.h"
+#include "UIFrameBuffer.h"
+#include "UIExtraDataManager.h"
+#include "UIDesktopWidgetWatchdog.h"
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CDisplay.h"
+#include "CGraphicsAdapter.h"
+
+/* Other VBox includes: */
+#include "VBox/log.h"
+#include <VBox/VBoxOGL.h>
+
+
+UIMachineViewScale::UIMachineViewScale(UIMachineWindow *pMachineWindow, ulong uScreenId)
+ : UIMachineView(pMachineWindow, uScreenId)
+{
+}
+
+void UIMachineViewScale::sltPerformGuestScale()
+{
+ /* Assign new frame-buffer logical-size: */
+ QSize scaledSize = size();
+ const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
+ const bool fUseUnscaledHiDPIOutput = frameBuffer()->useUnscaledHiDPIOutput();
+ scaledSize *= dDevicePixelRatioFormal;
+ if (!fUseUnscaledHiDPIOutput)
+ scaledSize /= dDevicePixelRatioActual;
+ frameBuffer()->setScaledSize(scaledSize);
+ frameBuffer()->performRescale();
+
+ /* If scaled-size is valid: */
+ if (scaledSize.isValid())
+ {
+ /* Propagate scale-factor to 3D service if necessary: */
+ if (machine().GetGraphicsAdapter().GetAccelerate3DEnabled())
+ {
+ double xScaleFactor = (double)scaledSize.width() / frameBuffer()->width();
+ double yScaleFactor = (double)scaledSize.height() / frameBuffer()->height();
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ // WORKAROUND:
+ // On Windows and Linux opposing to macOS it's only Qt which can auto scale up,
+ // not 3D overlay itself, so for auto scale-up mode we have to take that into account.
+ if (!fUseUnscaledHiDPIOutput)
+ {
+ xScaleFactor *= dDevicePixelRatioActual;
+ yScaleFactor *= dDevicePixelRatioActual;
+ }
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+ display().NotifyScaleFactorChange(m_uScreenId,
+ (uint32_t)(xScaleFactor * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
+ (uint32_t)(yScaleFactor * VBOX_OGL_SCALE_FACTOR_MULTIPLIER));
+ }
+ }
+
+ /* Scale the pause-pixmap: */
+ updateScaledPausePixmap();
+
+ /* Update viewport: */
+ viewport()->repaint();
+
+ /* Update machine-view sliders: */
+ updateSliders();
+}
+
+bool UIMachineViewScale::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ if (pWatched != 0 && pWatched == viewport())
+ {
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ {
+ /* Perform the actual resize: */
+ sltPerformGuestScale();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return UIMachineView::eventFilter(pWatched, pEvent);
+}
+
+void UIMachineViewScale::applyMachineViewScaleFactor()
+{
+ /* If scaled-size is valid: */
+ const QSize scaledSize = frameBuffer()->scaledSize();
+ const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual(); Q_UNUSED(dDevicePixelRatioActual);
+ const bool fUseUnscaledHiDPIOutput = frameBuffer()->useUnscaledHiDPIOutput();
+ if (scaledSize.isValid())
+ {
+ /* Propagate scale-factor to 3D service if necessary: */
+ if (machine().GetGraphicsAdapter().GetAccelerate3DEnabled())
+ {
+ double xScaleFactor = (double)scaledSize.width() / frameBuffer()->width();
+ double yScaleFactor = (double)scaledSize.height() / frameBuffer()->height();
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ // WORKAROUND:
+ // On Windows and Linux opposing to macOS it's only Qt which can auto scale up,
+ // not 3D overlay itself, so for auto scale-up mode we have to take that into account.
+ if (!fUseUnscaledHiDPIOutput)
+ {
+ xScaleFactor *= dDevicePixelRatioActual;
+ yScaleFactor *= dDevicePixelRatioActual;
+ }
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+ display().NotifyScaleFactorChange(m_uScreenId,
+ (uint32_t)(xScaleFactor * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
+ (uint32_t)(yScaleFactor * VBOX_OGL_SCALE_FACTOR_MULTIPLIER));
+ }
+ }
+
+ /* Take unscaled HiDPI output mode into account: */
+ frameBuffer()->setUseUnscaledHiDPIOutput(fUseUnscaledHiDPIOutput);
+ /* Propagate unscaled-hidpi-output feature to 3D service if necessary: */
+ if (machine().GetGraphicsAdapter().GetAccelerate3DEnabled())
+ display().NotifyHiDPIOutputPolicyChange(fUseUnscaledHiDPIOutput);
+
+ /* Perform frame-buffer rescaling: */
+ frameBuffer()->performRescale();
+
+ /* Update console's display viewport and 3D overlay: */
+ updateViewport();
+}
+
+void UIMachineViewScale::resendSizeHint()
+{
+ /* Get the last guest-screen size-hint, taking the scale factor into account. */
+ const QSize sizeHint = scaledBackward(storedGuestScreenSizeHint());
+ LogRel(("GUI: UIMachineViewScale::resendSizeHint: Restoring guest size-hint for screen %d to %dx%d\n",
+ (int)screenId(), sizeHint.width(), sizeHint.height()));
+
+ /* Expand current limitations: */
+ setMaximumGuestSize(sizeHint);
+
+ /* Send saved size-hint to the guest: */
+ uisession()->setScreenVisibleHostDesires(screenId(), guestScreenVisibilityStatus());
+ display().SetVideoModeHint(screenId(),
+ guestScreenVisibilityStatus(),
+ false, 0, 0, sizeHint.width(), sizeHint.height(), 0, true);
+}
+
+QSize UIMachineViewScale::sizeHint() const
+{
+ /* Base-class have its own thoughts about size-hint
+ * but scale-mode needs no size-hint to be set: */
+ return QSize();
+}
+
+QRect UIMachineViewScale::workingArea() const
+{
+ return gpDesktop->availableGeometry(this);
+}
+
+QSize UIMachineViewScale::calculateMaxGuestSize() const
+{
+ /* 1) The calculation below is not reliable on some (X11) platforms until we
+ * have been visible for a fraction of a second, so so the best we can
+ * otherwise.
+ * 2) We also get called early before "machineWindow" has been fully
+ * initialised, at which time we can't perform the calculation. */
+ if (!isVisible())
+ return workingArea().size() * 0.95;
+ /* The area taken up by the machine window on the desktop, including window
+ * frame, title, menu bar and status bar. */
+ QSize windowSize = machineWindow()->frameGeometry().size();
+ /* The window shouldn't be allowed to expand beyond the working area
+ * unless it already does. In that case the guest shouldn't expand it
+ * any further though. */
+ QSize maximumSize = workingArea().size().expandedTo(windowSize);
+ /* The current size of the machine display. */
+ QSize centralWidgetSize = machineWindow()->centralWidget()->size();
+ /* To work out how big the guest display can get without the window going
+ * over the maximum size we calculated above, we work out how much space
+ * the other parts of the window (frame, menu bar, status bar and so on)
+ * take up and subtract that space from the maximum window size. The
+ * central widget shouldn't be bigger than the window, but we bound it for
+ * sanity (or insanity) reasons. */
+ return maximumSize - (windowSize - centralWidgetSize.boundedTo(windowSize));
+}
+
+void UIMachineViewScale::updateSliders()
+{
+ if (horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ if (verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineViewScale.h b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineViewScale.h
new file mode 100644
index 00000000..7a81139f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineViewScale.h
@@ -0,0 +1,72 @@
+/* $Id: UIMachineViewScale.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineViewScale class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_scale_UIMachineViewScale_h
+#define FEQT_INCLUDED_SRC_runtime_scale_UIMachineViewScale_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineView.h"
+
+/** UIMachineView subclass used as scaled machine view implementation. */
+class UIMachineViewScale : public UIMachineView
+{
+ Q_OBJECT;
+
+public:
+
+ /* Scale machine-view constructor: */
+ UIMachineViewScale(UIMachineWindow *pMachineWindow, ulong uScreenId);
+ /* Scale machine-view destructor: */
+ virtual ~UIMachineViewScale() {}
+
+private slots:
+
+ /* Slot to perform guest resize: */
+ void sltPerformGuestScale();
+
+private:
+
+ /* Event handlers: */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+
+ /** Applies machine-view scale-factor. */
+ void applyMachineViewScaleFactor();
+
+ /** Resends guest size-hint. */
+ void resendSizeHint();
+
+ /* Private helpers: */
+ QSize sizeHint() const;
+ QRect workingArea() const;
+ QSize calculateMaxGuestSize() const;
+ void updateSliders();
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_scale_UIMachineViewScale_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineWindowScale.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineWindowScale.cpp
new file mode 100644
index 00000000..7bfcb39f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineWindowScale.cpp
@@ -0,0 +1,293 @@
+/* $Id: UIMachineWindowScale.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindowScale class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QMenu>
+#include <QTimerEvent>
+#include <QSpacerItem>
+#include <QResizeEvent>
+#ifdef VBOX_WS_X11
+# include <QTimer>
+#endif
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UISession.h"
+#include "UIMachineLogic.h"
+#include "UIMachineWindowScale.h"
+#include "UIMachineView.h"
+#include "UINotificationCenter.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils.h"
+# include "UIImageTools.h"
+# include "UICocoaApplication.h"
+#endif
+
+
+UIMachineWindowScale::UIMachineWindowScale(UIMachineLogic *pMachineLogic, ulong uScreenId)
+ : UIMachineWindow(pMachineLogic, uScreenId)
+ , m_iGeometrySaveTimerId(-1)
+{
+}
+
+void UIMachineWindowScale::prepareMainLayout()
+{
+ /* Call to base-class: */
+ UIMachineWindow::prepareMainLayout();
+
+ /* Strict spacers to hide them, they are not necessary for scale-mode: */
+ m_pTopSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
+ m_pBottomSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
+ m_pLeftSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
+ m_pRightSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
+}
+
+void UIMachineWindowScale::prepareNotificationCenter()
+{
+ if (gpNotificationCenter && (m_uScreenId == 0))
+ gpNotificationCenter->setParent(centralWidget());
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineWindowScale::prepareVisualState()
+{
+ /* Call to base-class: */
+ UIMachineWindow::prepareVisualState();
+
+ /* Beta label? */
+ if (uiCommon().showBetaLabel())
+ {
+ QPixmap betaLabel = ::betaLabel(QSize(74, darwinWindowTitleHeight(this) - 1));
+ ::darwinLabelWindow(this, &betaLabel);
+ }
+
+ /* Enable fullscreen support for every screen which requires it: */
+ if (darwinScreensHaveSeparateSpaces() || m_uScreenId == 0)
+ darwinEnableFullscreenSupport(this);
+ /* Register 'Zoom' button to use our full-screen: */
+ UICocoaApplication::instance()->registerCallbackForStandardWindowButton(this, StandardWindowButtonType_Zoom,
+ UIMachineWindow::handleStandardWindowButtonCallback);
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineWindowScale::loadSettings()
+{
+ /* Call to base-class: */
+ UIMachineWindow::loadSettings();
+
+ /* Load extra-data settings: */
+ {
+ /* Load extra-data: */
+ QRect geo = gEDataManager->machineWindowGeometry(machineLogic()->visualStateType(),
+ m_uScreenId, uiCommon().managedVMUuid());
+
+ /* If we do have proper geometry: */
+ if (!geo.isNull())
+ {
+ /* Restore window geometry: */
+ m_geometry = geo;
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, m_geometry);
+
+ /* Maximize (if necessary): */
+ if (gEDataManager->machineWindowShouldBeMaximized(machineLogic()->visualStateType(),
+ m_uScreenId, uiCommon().managedVMUuid()))
+ setWindowState(windowState() | Qt::WindowMaximized);
+ }
+ /* If we do NOT have proper geometry: */
+ else
+ {
+ /* Get available geometry, for screen with (x,y) coords if possible: */
+ QRect availableGeo = !geo.isNull() ? gpDesktop->availableGeometry(QPoint(geo.x(), geo.y())) :
+ gpDesktop->availableGeometry(this);
+
+ /* Resize to default size: */
+ resize(640, 480);
+ /* Move newly created window to the screen-center: */
+ m_geometry = geometry();
+ m_geometry.moveCenter(availableGeo.center());
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, m_geometry);
+ }
+
+ /* Normalize to the optimal size: */
+#ifdef VBOX_WS_X11
+ QTimer::singleShot(0, this, SLOT(sltNormalizeGeometry()));
+#else /* !VBOX_WS_X11 */
+ normalizeGeometry(true /* adjust position */, true /* resize to fit guest display. ignored in scaled case */);
+#endif /* !VBOX_WS_X11 */
+ }
+}
+
+#ifdef VBOX_WS_MAC
+void UIMachineWindowScale::cleanupVisualState()
+{
+ /* Unregister 'Zoom' button from using our full-screen: */
+ UICocoaApplication::instance()->unregisterCallbackForStandardWindowButton(this, StandardWindowButtonType_Zoom);
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMachineWindowScale::cleanupNotificationCenter()
+{
+ if (gpNotificationCenter && (gpNotificationCenter->parent() == centralWidget()))
+ gpNotificationCenter->setParent(0);
+}
+
+void UIMachineWindowScale::showInNecessaryMode()
+{
+ /* Make sure this window should be shown at all: */
+ if (!uisession()->isScreenVisible(m_uScreenId))
+ return hide();
+
+ /* Make sure this window is not minimized: */
+ if (isMinimized())
+ return;
+
+ /* Show in normal mode: */
+ show();
+
+ /* Make sure machine-view have focus: */
+ m_pMachineView->setFocus();
+}
+
+void UIMachineWindowScale::restoreCachedGeometry()
+{
+ /* Restore the geometry cached by the window: */
+ resize(m_geometry.size());
+ move(m_geometry.topLeft());
+
+ /* Adjust machine-view accordingly: */
+ adjustMachineViewSize();
+}
+
+void UIMachineWindowScale::normalizeGeometry(bool fAdjustPosition, bool fResizeToGuestDisplay)
+{
+ Q_UNUSED(fResizeToGuestDisplay);
+ /* Skip if maximized: */
+ if (isMaximized())
+ return;
+
+ /* Calculate client window offsets: */
+ QRect frGeo = frameGeometry();
+ const QRect geo = geometry();
+ const int dl = geo.left() - frGeo.left();
+ const int dt = geo.top() - frGeo.top();
+ const int dr = frGeo.right() - geo.right();
+ const int db = frGeo.bottom() - geo.bottom();
+
+ /* Adjust position if necessary: */
+ if (fAdjustPosition)
+ frGeo = UIDesktopWidgetWatchdog::normalizeGeometry(frGeo, gpDesktop->overallAvailableRegion());
+
+ /* Finally, set the frame geometry: */
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, frGeo.left() + dl, frGeo.top() + dt,
+ frGeo.width() - dl - dr, frGeo.height() - dt - db);
+}
+
+bool UIMachineWindowScale::event(QEvent *pEvent)
+{
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ {
+#ifdef VBOX_WS_X11
+ /* Prevent handling if fake screen detected: */
+ if (UIDesktopWidgetWatchdog::isFakeScreenDetected())
+ break;
+#endif /* VBOX_WS_X11 */
+
+ QResizeEvent *pResizeEvent = static_cast<QResizeEvent*>(pEvent);
+ if (!isMaximizedChecked())
+ {
+ m_geometry.setSize(pResizeEvent->size());
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Update debugger window position: */
+ updateDbgWindows();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+ }
+
+ /* Restart geometry save timer: */
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+ break;
+ }
+ case QEvent::Move:
+ {
+#ifdef VBOX_WS_X11
+ /* Prevent handling if fake screen detected: */
+ if (UIDesktopWidgetWatchdog::isFakeScreenDetected())
+ break;
+#endif /* VBOX_WS_X11 */
+
+ if (!isMaximizedChecked())
+ {
+ m_geometry.moveTo(geometry().x(), geometry().y());
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Update debugger window position: */
+ updateDbgWindows();
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+ }
+
+ /* Restart geometry save timer: */
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+ break;
+ }
+ /* Handle timer event started above: */
+ case QEvent::Timer:
+ {
+ QTimerEvent *pTimerEvent = static_cast<QTimerEvent*>(pEvent);
+ if (pTimerEvent->timerId() == m_iGeometrySaveTimerId)
+ {
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = -1;
+ LogRel2(("GUI: UIMachineWindowScale: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ m_geometry.x(), m_geometry.y(), m_geometry.width(), m_geometry.height()));
+ gEDataManager->setMachineWindowGeometry(machineLogic()->visualStateType(),
+ m_uScreenId, m_geometry,
+ isMaximizedChecked(), uiCommon().managedVMUuid());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return UIMachineWindow::event(pEvent);
+}
+
+bool UIMachineWindowScale::isMaximizedChecked()
+{
+#ifdef VBOX_WS_MAC
+ /* On the Mac the WindowStateChange signal doesn't seems to be delivered
+ * when the user get out of the maximized state. So check this ourself. */
+ return ::darwinIsWindowMaximized(this);
+#else /* VBOX_WS_MAC */
+ return isMaximized();
+#endif /* !VBOX_WS_MAC */
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineWindowScale.h b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineWindowScale.h
new file mode 100644
index 00000000..5d82ce54
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/scale/UIMachineWindowScale.h
@@ -0,0 +1,90 @@
+/* $Id: UIMachineWindowScale.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindowScale class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_scale_UIMachineWindowScale_h
+#define FEQT_INCLUDED_SRC_runtime_scale_UIMachineWindowScale_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineWindow.h"
+
+/** UIMachineWindow subclass used as scaled machine window implementation. */
+class UIMachineWindowScale : public UIMachineWindow
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pMachineLogic and @a uScreenId to the UIMachineWindow constructor. */
+ UIMachineWindowScale(UIMachineLogic *pMachineLogic, ulong uScreenId);
+
+private:
+
+ /** Prepare main-layout routine. */
+ void prepareMainLayout();
+ /** Prepare notification-center routine. */
+ void prepareNotificationCenter();
+#ifdef VBOX_WS_MAC
+ /** Prepare visual-state routine. */
+ void prepareVisualState();
+#endif /* VBOX_WS_MAC */
+ /** Load settings routine. */
+ void loadSettings();
+
+#ifdef VBOX_WS_MAC
+ /** Cleanup visual-state routine. */
+ void cleanupVisualState();
+#endif /* VBOX_WS_MAC */
+ /** Cleanup notification-center routine. */
+ void cleanupNotificationCenter();
+
+ /** Updates visibility according to visual-state. */
+ void showInNecessaryMode();
+
+ /** Restores cached window geometry. */
+ virtual void restoreCachedGeometry() RT_OVERRIDE;
+
+ /** Performs window geometry normalization according to guest-size and host's available geometry.
+ * @param fAdjustPosition Determines whether is it necessary to adjust position as well.
+ * @param fResizeToGuestDisplay Determines whether is it necessary to resize the window to fit to guest display size. */
+ virtual void normalizeGeometry(bool fAdjustPosition, bool fResizeToGuestDisplay) RT_OVERRIDE;
+
+ /** Common @a pEvent handler. */
+ bool event(QEvent *pEvent);
+
+ /** Returns whether this window is maximized. */
+ bool isMaximizedChecked();
+
+ /** Holds the current window geometry. */
+ QRect m_geometry;
+ /** Holds the geometry save timer ID. */
+ int m_iGeometrySaveTimerId;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_scale_UIMachineWindowScale_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/seamless/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.cpp
new file mode 100644
index 00000000..0e699a87
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.cpp
@@ -0,0 +1,91 @@
+/* $Id: UIKeyboardHandlerSeamless.cpp $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandlerSeamless class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_WS_MAC
+/* Qt includes: */
+# include <QKeyEvent>
+# include <QTimer>
+#endif /* !VBOX_WS_MAC */
+
+/* GUI includes: */
+#include "UIKeyboardHandlerSeamless.h"
+#ifndef VBOX_WS_MAC
+# include "UIMachineLogic.h"
+# include "UIShortcutPool.h"
+#endif /* !VBOX_WS_MAC */
+
+
+#ifndef VBOX_WS_MAC
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+#endif /* !VBOX_WS_MAC */
+
+UIKeyboardHandlerSeamless::UIKeyboardHandlerSeamless(UIMachineLogic* pMachineLogic)
+ : UIKeyboardHandler(pMachineLogic)
+{
+}
+
+UIKeyboardHandlerSeamless::~UIKeyboardHandlerSeamless()
+{
+}
+
+#ifndef VBOX_WS_MAC
+bool UIKeyboardHandlerSeamless::eventFilter(QObject *pWatchedObject, QEvent *pEvent)
+{
+ /* Check if pWatchedObject object is view: */
+ if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject))
+ {
+ /* Get corresponding screen index: */
+ ulong uScreenId = m_views.key(pWatchedView);
+ NOREF(uScreenId);
+ /* Handle view events: */
+ switch (pEvent->type())
+ {
+ case QEvent::KeyPress:
+ {
+ /* Get key-event: */
+ QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
+ /* Process Host+Home for menu popup: */
+ if ( isHostKeyPressed()
+ && gShortcutPool->shortcut(GUI_Input_MachineShortcuts, QString("PopupMenu")).sequences().contains(QKeySequence(pKeyEvent->key())))
+ {
+ /* Post request to show popup-menu: */
+ QTimer::singleShot(0, m_pMachineLogic, SLOT(sltInvokePopupMenu()));
+ /* Filter-out this event: */
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Else just propagate to base-class: */
+ return UIKeyboardHandler::eventFilter(pWatchedObject, pEvent);
+}
+#endif /* !VBOX_WS_MAC */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.h b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.h
new file mode 100644
index 00000000..3254e893
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.h
@@ -0,0 +1,58 @@
+/* $Id: UIKeyboardHandlerSeamless.h $ */
+/** @file
+ * VBox Qt GUI - UIKeyboardHandlerSeamless class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_seamless_UIKeyboardHandlerSeamless_h
+#define FEQT_INCLUDED_SRC_runtime_seamless_UIKeyboardHandlerSeamless_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIKeyboardHandler.h"
+
+/** UIKeyboardHandler reimplementation
+ * providing machine-logic with PopupMenu keyboard handler. */
+class UIKeyboardHandlerSeamless : public UIKeyboardHandler
+{
+ Q_OBJECT;
+
+public:
+
+ /** Seamless keyboard-handler constructor. */
+ UIKeyboardHandlerSeamless(UIMachineLogic *pMachineLogic);
+ /** Seamless keyboard-handler destructor. */
+ virtual ~UIKeyboardHandlerSeamless();
+
+private:
+
+#ifndef VBOX_WS_MAC
+ /** General event-filter. */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+#endif /* !VBOX_WS_MAC */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_seamless_UIKeyboardHandlerSeamless_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineLogicSeamless.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineLogicSeamless.cpp
new file mode 100644
index 00000000..d3304f4a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineLogicSeamless.cpp
@@ -0,0 +1,378 @@
+/* $Id: UIMachineLogicSeamless.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogicSeamless class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#ifndef VBOX_WS_MAC
+# include <QTimer>
+#endif /* !VBOX_WS_MAC */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIMessageCenter.h"
+#include "UISession.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogicSeamless.h"
+#include "UIMachineWindowSeamless.h"
+#include "UIMultiScreenLayout.h"
+#include "UIShortcutPool.h"
+#ifndef VBOX_WS_MAC
+# include "QIMenu.h"
+#else /* VBOX_WS_MAC */
+# include "VBoxUtils.h"
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "CGraphicsAdapter.h"
+
+
+UIMachineLogicSeamless::UIMachineLogicSeamless(QObject *pParent, UISession *pSession)
+ : UIMachineLogic(pParent, pSession, UIVisualStateType_Seamless)
+#ifndef VBOX_WS_MAC
+ , m_pPopupMenu(0)
+#endif /* !VBOX_WS_MAC */
+{
+ /* Create multiscreen layout: */
+ m_pScreenLayout = new UIMultiScreenLayout(this);
+}
+
+UIMachineLogicSeamless::~UIMachineLogicSeamless()
+{
+ /* Delete multiscreen layout: */
+ delete m_pScreenLayout;
+}
+
+int UIMachineLogicSeamless::hostScreenForGuestScreen(int iScreenId) const
+{
+ return m_pScreenLayout->hostScreenForGuestScreen(iScreenId);
+}
+
+bool UIMachineLogicSeamless::hasHostScreenForGuestScreen(int iScreenId) const
+{
+ return m_pScreenLayout->hasHostScreenForGuestScreen(iScreenId);
+}
+
+bool UIMachineLogicSeamless::checkAvailability()
+{
+ /* Check if there is enough physical memory to enter seamless: */
+ if (uisession()->isGuestSupportsSeamless())
+ {
+ quint64 availBits = machine().GetGraphicsAdapter().GetVRAMSize() /* VRAM */ * _1M /* MiB to bytes */ * 8 /* to bits */;
+ quint64 usedBits = m_pScreenLayout->memoryRequirements();
+ if (availBits < usedBits)
+ {
+ msgCenter().cannotEnterSeamlessMode(0, 0, 0,
+ (((usedBits + 7) / 8 + _1M - 1) / _1M) * _1M);
+ return false;
+ }
+ }
+
+ /* Show the info message. */
+ const UIShortcut &shortcut =
+ gShortcutPool->shortcut(actionPool()->shortcutsExtraDataID(),
+ actionPool()->action(UIActionIndexRT_M_View_T_Seamless)->shortcutExtraDataID());
+ const QString strHotKey = QString("Host+%1").arg(shortcut.primaryToPortableText());
+ if (!msgCenter().confirmGoingSeamless(strHotKey))
+ return false;
+
+ return true;
+}
+
+void UIMachineLogicSeamless::adjustMachineWindowsGeometry()
+{
+ LogRel(("GUI: UIMachineLogicSeamless::adjustMachineWindowsGeometry\n"));
+
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+
+ /* Make sure all machine-window(s) have proper geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->showInNecessaryMode();
+}
+
+void UIMachineLogicSeamless::sltCheckForRequestedVisualStateType()
+{
+ LogRel(("GUI: UIMachineLogicSeamless::sltCheckForRequestedVisualStateType: Requested-state=%d, Machine-state=%d\n",
+ uisession()->requestedVisualState(), uisession()->machineState()));
+
+ /* Do not try to change visual-state type if machine was not started yet: */
+ if (!uisession()->isRunning() && !uisession()->isPaused())
+ return;
+
+ /* Do not try to change visual-state type in 'manual override' mode: */
+ if (uisession()->isManualOverrideMode())
+ return;
+
+ /* If 'seamless' visual-state type is no more supported: */
+ if (!uisession()->isGuestSupportsSeamless())
+ {
+ LogRel(("GUI: UIMachineLogicSeamless::sltCheckForRequestedVisualStateType: "
+ "Leaving 'seamless' as it is no more supported...\n"));
+ uisession()->setRequestedVisualState(UIVisualStateType_Seamless);
+ uisession()->changeVisualState(UIVisualStateType_Normal);
+ }
+}
+
+void UIMachineLogicSeamless::sltMachineStateChanged()
+{
+ /* Call to base-class: */
+ UIMachineLogic::sltMachineStateChanged();
+
+ /* If machine-state changed from 'paused' to 'running': */
+ if (uisession()->isRunning() && uisession()->wasPaused())
+ {
+ LogRel(("GUI: UIMachineLogicSeamless::sltMachineStateChanged:"
+ "Machine-state changed from 'paused' to 'running': "
+ "Adjust machine-window geometry...\n"));
+
+ /* Make sure further code will be called just once: */
+ uisession()->forgetPreviousMachineState();
+ /* Adjust machine-window geometry if necessary: */
+ adjustMachineWindowsGeometry();
+ }
+}
+
+#ifndef VBOX_WS_MAC
+void UIMachineLogicSeamless::sltInvokePopupMenu()
+{
+ /* Popup main-menu if present: */
+ if (m_pPopupMenu && !m_pPopupMenu->isEmpty())
+ {
+ m_pPopupMenu->popup(activeMachineWindow()->geometry().center());
+ QTimer::singleShot(0, m_pPopupMenu, SLOT(sltHighlightFirstAction()));
+ }
+}
+#endif /* !VBOX_WS_MAC */
+
+void UIMachineLogicSeamless::sltScreenLayoutChanged()
+{
+ LogRel(("GUI: UIMachineLogicSeamless::sltScreenLayoutChanged: Multi-screen layout changed.\n"));
+
+ /* Make sure all machine-window(s) have proper geometry: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ pMachineWindow->showInNecessaryMode();
+}
+
+void UIMachineLogicSeamless::sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo)
+{
+ LogRel(("GUI: UIMachineLogicSeamless: Guest-screen count changed.\n"));
+
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+
+ /* Call to base-class: */
+ UIMachineLogic::sltGuestMonitorChange(changeType, uScreenId, screenGeo);
+}
+
+void UIMachineLogicSeamless::sltHostScreenCountChange()
+{
+ LogRel(("GUI: UIMachineLogicSeamless: Host-screen count changed.\n"));
+
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+
+ /* Call to base-class: */
+ UIMachineLogic::sltHostScreenCountChange();
+}
+
+void UIMachineLogicSeamless::sltAdditionsStateChanged()
+{
+ /* Call to base-class: */
+ UIMachineLogic::sltAdditionsStateChanged();
+
+ LogRel(("GUI: UIMachineLogicSeamless: Additions-state actual-change event, rebuild multi-screen layout\n"));
+ /* Rebuild multi-screen layout: */
+ m_pScreenLayout->rebuild();
+}
+
+void UIMachineLogicSeamless::prepareActionGroups()
+{
+ /* Call to base-class: */
+ UIMachineLogic::prepareActionGroups();
+
+ /* Restrict 'Adjust Window', 'Guest Autoresize', 'Status Bar' and 'Resize' actions for 'View' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
+ (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
+ (UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_GuestAutoresize |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar |
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize));
+#ifdef VBOX_WS_MAC
+ /* Restrict 'Window' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuBar(UIActionRestrictionLevel_Logic,
+ UIExtraDataMetaDefs::MenuType_Window);
+#endif /* VBOX_WS_MAC */
+
+ /* Take care of view-action toggle state: */
+ UIAction *pActionSeamless = actionPool()->action(UIActionIndexRT_M_View_T_Seamless);
+ if (!pActionSeamless->isChecked())
+ {
+ pActionSeamless->blockSignals(true);
+ pActionSeamless->setChecked(true);
+ pActionSeamless->blockSignals(false);
+ }
+}
+
+void UIMachineLogicSeamless::prepareActionConnections()
+{
+ /* Call to base-class: */
+ UIMachineLogic::prepareActionConnections();
+
+ /* Prepare 'View' actions connections: */
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &QAction::triggered,
+ this, &UIMachineLogicSeamless::sltChangeVisualStateToNormal);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &QAction::triggered,
+ this, &UIMachineLogicSeamless::sltChangeVisualStateToFullscreen);
+ connect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &QAction::triggered,
+ this, &UIMachineLogicSeamless::sltChangeVisualStateToScale);
+}
+
+void UIMachineLogicSeamless::prepareMachineWindows()
+{
+ /* Do not create machine-window(s) if they created already: */
+ if (isMachineWindowsCreated())
+ return;
+
+#ifdef VBOX_WS_MAC
+ /* We have to make sure that we are getting the front most process.
+ * This is necessary for Qt versions > 4.3.3: */
+ darwinSetFrontMostProcess();
+#endif /* VBOX_WS_MAC */
+
+ /* Update the multi-screen layout: */
+ m_pScreenLayout->update();
+
+ /* Create machine-window(s): */
+ for (uint cScreenId = 0; cScreenId < machine().GetGraphicsAdapter().GetMonitorCount(); ++cScreenId)
+ addMachineWindow(UIMachineWindow::create(this, cScreenId));
+
+ /* Listen for frame-buffer resize: */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ connect(pMachineWindow, &UIMachineWindow::sigFrameBufferResize,
+ this, &UIMachineLogicSeamless::sigFrameBufferResize);
+ emit sigFrameBufferResize();
+
+ /* Connect multi-screen layout change handler: */
+ connect(m_pScreenLayout, &UIMultiScreenLayout::sigScreenLayoutChange,
+ this, &UIMachineLogicSeamless::sltScreenLayoutChanged);
+
+ /* Mark machine-window(s) created: */
+ setMachineWindowsCreated(true);
+
+#ifdef VBOX_WS_X11
+ switch (uiCommon().typeOfWindowManager())
+ {
+ case X11WMType_GNOMEShell:
+ case X11WMType_Mutter:
+ {
+ // WORKAROUND:
+ // Under certain WMs we can loose machine-window activation due to any Qt::Tool
+ // overlay asynchronously shown above it. Qt is not become aware of such event.
+ // We are going to ask to return machine-window activation in let's say 100ms.
+ QTimer::singleShot(100, machineWindows().first(), SLOT(sltActivateWindow()));
+ break;
+ }
+ default:
+ break;
+ }
+#endif /* VBOX_WS_X11 */
+}
+
+#ifndef VBOX_WS_MAC
+void UIMachineLogicSeamless::prepareMenu()
+{
+ /* Prepare popup-menu: */
+ m_pPopupMenu = new QIMenu;
+ AssertPtrReturnVoid(m_pPopupMenu);
+ {
+ /* Prepare popup-menu: */
+ foreach (QMenu *pMenu, actionPool()->menus())
+ m_pPopupMenu->addMenu(pMenu);
+ }
+}
+#endif /* !VBOX_WS_MAC */
+
+#ifndef VBOX_WS_MAC
+void UIMachineLogicSeamless::cleanupMenu()
+{
+ /* Cleanup popup-menu: */
+ delete m_pPopupMenu;
+ m_pPopupMenu = 0;
+}
+#endif /* !VBOX_WS_MAC */
+
+void UIMachineLogicSeamless::cleanupMachineWindows()
+{
+ /* Do not destroy machine-window(s) if they destroyed already: */
+ if (!isMachineWindowsCreated())
+ return;
+
+ /* Mark machine-window(s) destroyed: */
+ setMachineWindowsCreated(false);
+
+ /* Destroy machine-window(s): */
+ foreach (UIMachineWindow *pMachineWindow, machineWindows())
+ UIMachineWindow::destroy(pMachineWindow);
+}
+
+void UIMachineLogicSeamless::cleanupActionConnections()
+{
+ /* "View" actions disconnections: */
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &QAction::triggered,
+ this, &UIMachineLogicSeamless::sltChangeVisualStateToNormal);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &QAction::triggered,
+ this, &UIMachineLogicSeamless::sltChangeVisualStateToFullscreen);
+ disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &QAction::triggered,
+ this, &UIMachineLogicSeamless::sltChangeVisualStateToScale);
+
+ /* Call to base-class: */
+ UIMachineLogic::cleanupActionConnections();
+}
+
+void UIMachineLogicSeamless::cleanupActionGroups()
+{
+ /* Take care of view-action toggle state: */
+ UIAction *pActionSeamless = actionPool()->action(UIActionIndexRT_M_View_T_Seamless);
+ if (pActionSeamless->isChecked())
+ {
+ pActionSeamless->blockSignals(true);
+ pActionSeamless->setChecked(false);
+ pActionSeamless->blockSignals(false);
+ }
+
+ /* Allow 'Adjust Window', 'Guest Autoresize', 'Status Bar' and 'Resize' actions for 'View' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid);
+#ifdef VBOX_WS_MAC
+ /* Allow 'Window' menu: */
+ actionPool()->toRuntime()->setRestrictionForMenuBar(UIActionRestrictionLevel_Logic,
+ UIExtraDataMetaDefs::MenuType_Invalid);
+#endif /* VBOX_WS_MAC */
+
+ /* Call to base-class: */
+ UIMachineLogic::cleanupActionGroups();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineLogicSeamless.h b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineLogicSeamless.h
new file mode 100644
index 00000000..f5c722fb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineLogicSeamless.h
@@ -0,0 +1,119 @@
+/* $Id: UIMachineLogicSeamless.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineLogicSeamless class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_seamless_UIMachineLogicSeamless_h
+#define FEQT_INCLUDED_SRC_runtime_seamless_UIMachineLogicSeamless_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineLogic.h"
+
+/* Forward declarations: */
+class UIMultiScreenLayout;
+
+/** UIMachineLogic subclass used as seamless machine logic implementation. */
+class UIMachineLogicSeamless : public UIMachineLogic
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs seamless logic passing @a pParent to the base-class.
+ * @param pSession Brings the session UI reference. */
+ UIMachineLogicSeamless(QObject *pParent, UISession *pSession);
+ /** Destructs seamless logic. */
+ virtual ~UIMachineLogicSeamless() RT_OVERRIDE;
+
+ /** Returns an index of host-screen for guest-screen with @a iScreenId specified. */
+ int hostScreenForGuestScreen(int iScreenId) const;
+ /** Returns whether there is a host-screen for guest-screen with @a iScreenId specified. */
+ bool hasHostScreenForGuestScreen(int iScreenId) const;
+
+protected:
+
+ /* Check if this logic is available: */
+ bool checkAvailability();
+
+ /** Returns machine-window flags for 'Seamless' machine-logic and passed @a uScreenId. */
+ virtual Qt::WindowFlags windowFlags(ulong uScreenId) const { Q_UNUSED(uScreenId); return Qt::FramelessWindowHint; }
+
+ /** Adjusts machine-window geometry if necessary for 'Seamless'. */
+ virtual void adjustMachineWindowsGeometry();
+
+private slots:
+
+ /** Checks if some visual-state type was requested. */
+ void sltCheckForRequestedVisualStateType();
+
+ /* Handler: Console callback stuff: */
+ void sltMachineStateChanged();
+
+ /** Updates machine-window(s) location/size on screen-layout changes. */
+ void sltScreenLayoutChanged();
+
+ /** Handles guest-screen count change. */
+ virtual void sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo);
+ /** Handles host-screen count change. */
+ virtual void sltHostScreenCountChange();
+ /** Handles additions-state change. */
+ virtual void sltAdditionsStateChanged();
+
+#ifndef RT_OS_DARWIN
+ /** Invokes popup-menu. */
+ void sltInvokePopupMenu();
+#endif /* !RT_OS_DARWIN */
+
+private:
+
+ /* Prepare helpers: */
+ void prepareActionGroups();
+ void prepareActionConnections();
+ void prepareMachineWindows();
+#ifndef VBOX_WS_MAC
+ void prepareMenu();
+#endif /* !VBOX_WS_MAC */
+
+ /* Cleanup helpers: */
+#ifndef VBOX_WS_MAC
+ void cleanupMenu();
+#endif /* !VBOX_WS_MAC */
+ void cleanupMachineWindows();
+ void cleanupActionConnections();
+ void cleanupActionGroups();
+
+ /* Variables: */
+ UIMultiScreenLayout *m_pScreenLayout;
+
+#ifndef RT_OS_DARWIN
+ /** Holds the popup-menu instance. */
+ QMenu *m_pPopupMenu;
+#endif /* !RT_OS_DARWIN */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_seamless_UIMachineLogicSeamless_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineViewSeamless.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineViewSeamless.cpp
new file mode 100644
index 00000000..e56203be
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineViewSeamless.cpp
@@ -0,0 +1,192 @@
+/* $Id: UIMachineViewSeamless.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineViewSeamless class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QMainWindow>
+#include <QTimer>
+#ifdef VBOX_WS_MAC
+# include <QMenuBar>
+#endif /* VBOX_WS_MAC */
+
+/* GUI includes: */
+#include "UISession.h"
+#include "UIMachineLogicSeamless.h"
+#include "UIMachineWindow.h"
+#include "UIMachineViewSeamless.h"
+#include "UIFrameBuffer.h"
+#include "UIExtraDataManager.h"
+#include "UIDesktopWidgetWatchdog.h"
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CDisplay.h"
+
+/* Other VBox includes: */
+#include "VBox/log.h"
+
+/* External includes: */
+#ifdef VBOX_WS_X11
+# include <limits.h>
+#endif /* VBOX_WS_X11 */
+
+
+
+UIMachineViewSeamless::UIMachineViewSeamless(UIMachineWindow *pMachineWindow, ulong uScreenId)
+ : UIMachineView(pMachineWindow, uScreenId)
+{
+ /* Prepare seamless view: */
+ prepareSeamless();
+}
+
+void UIMachineViewSeamless::sltAdditionsStateChanged()
+{
+ adjustGuestScreenSize();
+}
+
+void UIMachineViewSeamless::sltHandleSetVisibleRegion(QRegion region)
+{
+ /* Apply new seamless-region: */
+ m_pFrameBuffer->handleSetVisibleRegion(region);
+}
+
+bool UIMachineViewSeamless::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ if (pWatched != 0 && pWatched == machineWindow())
+ {
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ {
+ /* Send guest-resize hint only if top window resizing to required dimension: */
+ QResizeEvent *pResizeEvent = static_cast<QResizeEvent*>(pEvent);
+ if (pResizeEvent->size() != calculateMaxGuestSize())
+ break;
+
+ /* Recalculate maximum guest size: */
+ setMaximumGuestSize();
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return UIMachineView::eventFilter(pWatched, pEvent);
+}
+
+void UIMachineViewSeamless::prepareCommon()
+{
+ /* Base class common settings: */
+ UIMachineView::prepareCommon();
+
+ /* Setup size-policy: */
+ setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
+ /* Maximum size to sizehint: */
+ setMaximumSize(sizeHint());
+ /* Minimum size is ignored: */
+ setMinimumSize(0, 0);
+ /* No scrollbars: */
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+}
+
+void UIMachineViewSeamless::prepareFilters()
+{
+ /* Base class filters: */
+ UIMachineView::prepareFilters();
+}
+
+void UIMachineViewSeamless::prepareConsoleConnections()
+{
+ /* Base class connections: */
+ UIMachineView::prepareConsoleConnections();
+
+ /* Guest additions state-change updater: */
+ connect(uisession(), &UISession::sigAdditionsStateActualChange, this, &UIMachineViewSeamless::sltAdditionsStateChanged);
+}
+
+void UIMachineViewSeamless::prepareSeamless()
+{
+ /* Set seamless feature flag to the guest: */
+ display().SetSeamlessMode(true);
+}
+
+void UIMachineViewSeamless::cleanupSeamless()
+{
+ /* Reset seamless feature flag if possible: */
+ if (uisession()->isRunning())
+ display().SetSeamlessMode(false);
+}
+
+void UIMachineViewSeamless::adjustGuestScreenSize()
+{
+ /* Step 1: Is guest-screen visible? */
+ if (!uisession()->isScreenVisible(screenId()))
+ {
+ LogRel(("GUI: UIMachineViewSeamless::adjustGuestScreenSize: "
+ "Guest-screen #%d is not visible, adjustment is not required.\n",
+ screenId()));
+ return;
+ }
+
+ /* What are the desired and requested hints? */
+ const QSize sizeToApply = calculateMaxGuestSize();
+ const QSize desiredSizeHint = scaledBackward(sizeToApply);
+ const QSize requestedSizeHint = requestedGuestScreenSizeHint();
+
+ /* Step 2: Is the guest-screen of another size than necessary? */
+ if (desiredSizeHint == requestedSizeHint)
+ {
+ LogRel(("GUI: UIMachineViewSeamless::adjustGuestScreenSize: "
+ "Desired hint %dx%d for guest-screen #%d is already in IDisplay, adjustment is not required.\n",
+ desiredSizeHint.width(), desiredSizeHint.height(), screenId()));
+ return;
+ }
+
+ /* Final step: Adjust .. */
+ LogRel(("GUI: UIMachineViewSeamless::adjustGuestScreenSize: "
+ "Desired hint %dx%d for guest-screen #%d differs from the one in IDisplay, adjustment is required.\n",
+ desiredSizeHint.width(), desiredSizeHint.height(), screenId()));
+ sltPerformGuestResize(sizeToApply);
+ /* And remember the size to know what we are resizing out of when we exit: */
+ uisession()->setLastFullScreenSize(screenId(), scaledForward(desiredSizeHint));
+}
+
+QRect UIMachineViewSeamless::workingArea() const
+{
+ /* Get corresponding screen: */
+ int iScreen = static_cast<UIMachineLogicSeamless*>(machineLogic())->hostScreenForGuestScreen(screenId());
+ /* Return available geometry for that screen: */
+ return gpDesktop->availableGeometry(iScreen);
+}
+
+QSize UIMachineViewSeamless::calculateMaxGuestSize() const
+{
+ return workingArea().size();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineViewSeamless.h b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineViewSeamless.h
new file mode 100644
index 00000000..b075564e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineViewSeamless.h
@@ -0,0 +1,82 @@
+/* $Id: UIMachineViewSeamless.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineViewSeamless class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_seamless_UIMachineViewSeamless_h
+#define FEQT_INCLUDED_SRC_runtime_seamless_UIMachineViewSeamless_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineView.h"
+
+/** UIMachineView subclass used as seamless machine view implementation. */
+class UIMachineViewSeamless : public UIMachineView
+{
+ Q_OBJECT;
+
+public:
+
+ /* Seamless machine-view constructor: */
+ UIMachineViewSeamless(UIMachineWindow *pMachineWindow, ulong uScreenId);
+ /* Seamless machine-view destructor: */
+ virtual ~UIMachineViewSeamless() { cleanupSeamless(); }
+
+private slots:
+
+ /* Handler: Console callback stuff: */
+ void sltAdditionsStateChanged();
+
+ /* Handler: Frame-buffer SetVisibleRegion stuff: */
+ virtual void sltHandleSetVisibleRegion(QRegion region);
+
+private:
+
+ /* Event handlers: */
+ bool eventFilter(QObject *pWatched, QEvent *pEvent);
+
+ /* Prepare helpers: */
+ void prepareCommon();
+ void prepareFilters();
+ void prepareConsoleConnections();
+ void prepareSeamless();
+
+ /* Cleanup helpers: */
+ void cleanupSeamless();
+ //void cleanupConsoleConnections() {}
+ //void cleanupFilters() {}
+ //void cleanupCommon() {}
+
+ /** Adjusts guest-screen size to correspond current <i>working area</i> size. */
+ void adjustGuestScreenSize();
+
+ /* Helpers: Geometry stuff: */
+ QRect workingArea() const;
+ QSize calculateMaxGuestSize() const;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_seamless_UIMachineViewSeamless_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineWindowSeamless.cpp b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineWindowSeamless.cpp
new file mode 100644
index 00000000..840e12d8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineWindowSeamless.cpp
@@ -0,0 +1,415 @@
+/* $Id: UIMachineWindowSeamless.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindowSeamless class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QMenu>
+#include <QSpacerItem>
+#include <QTimer>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UISession.h"
+#include "UIActionPoolRuntime.h"
+#include "UIMachineLogicSeamless.h"
+#include "UIMachineWindowSeamless.h"
+#include "UIMachineView.h"
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+# include "UIMachineDefs.h"
+# include "UIMiniToolBar.h"
+#elif defined(VBOX_WS_MAC)
+# include "VBoxUtils.h"
+#endif /* VBOX_WS_MAC */
+
+/* COM includes: */
+#include "CSnapshot.h"
+
+
+UIMachineWindowSeamless::UIMachineWindowSeamless(UIMachineLogic *pMachineLogic, ulong uScreenId)
+ : UIMachineWindow(pMachineLogic, uScreenId)
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ , m_pMiniToolBar(0)
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+ , m_fWasMinimized(false)
+#ifdef VBOX_WS_X11
+ , m_fIsMinimizationRequested(false)
+ , m_fIsMinimized(false)
+#endif
+{
+}
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+void UIMachineWindowSeamless::sltMachineStateChanged()
+{
+ /* Call to base-class: */
+ UIMachineWindow::sltMachineStateChanged();
+
+ /* Update mini-toolbar: */
+ updateAppearanceOf(UIVisualElement_MiniToolBar);
+}
+
+void UIMachineWindowSeamless::sltRevokeWindowActivation()
+{
+#ifdef VBOX_WS_X11
+ // WORKAROUND:
+ // We could be asked to minimize already, but just
+ // not yet executed that order to current moment.
+ if (m_fIsMinimizationRequested)
+ return;
+#endif
+
+ /* Make sure window is visible: */
+ if (!isVisible() || isMinimized())
+ return;
+
+ /* Revoke stolen activation: */
+#ifdef VBOX_WS_X11
+ raise();
+#endif /* VBOX_WS_X11 */
+ activateWindow();
+}
+
+void UIMachineWindowSeamless::sltHandleMiniToolBarAutoHideToggled(bool fEnabled)
+{
+ /* Save mini-toolbar settings: */
+ gEDataManager->setAutoHideMiniToolbar(fEnabled, uiCommon().managedVMUuid());
+}
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+void UIMachineWindowSeamless::sltShowMinimized()
+{
+#ifdef VBOX_WS_X11
+ /* Remember that we are asked to minimize: */
+ m_fIsMinimizationRequested = true;
+#endif
+
+ showMinimized();
+}
+
+void UIMachineWindowSeamless::prepareVisualState()
+{
+ /* Call to base-class: */
+ UIMachineWindow::prepareVisualState();
+
+ /* Make sure we have no background
+ * until the first one paint-event: */
+ setAttribute(Qt::WA_NoSystemBackground);
+
+#ifdef VBOX_WITH_TRANSLUCENT_SEAMLESS
+ /* Using Qt API to enable translucent background: */
+ setAttribute(Qt::WA_TranslucentBackground);
+#endif /* VBOX_WITH_TRANSLUCENT_SEAMLESS */
+
+#ifdef VBOX_WITH_MASKED_SEAMLESS
+ /* Make sure we have no background
+ * until the first one set-region-event: */
+ setMask(m_maskGuest);
+#endif /* VBOX_WITH_MASKED_SEAMLESS */
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /* Prepare mini-toolbar: */
+ prepareMiniToolbar();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+}
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+void UIMachineWindowSeamless::prepareMiniToolbar()
+{
+ /* Make sure mini-toolbar is not restricted: */
+ if (!gEDataManager->miniToolbarEnabled(uiCommon().managedVMUuid()))
+ return;
+
+ /* Create mini-toolbar: */
+ m_pMiniToolBar = new UIMiniToolBar(this,
+ GeometryType_Available,
+ gEDataManager->miniToolbarAlignment(uiCommon().managedVMUuid()),
+ gEDataManager->autoHideMiniToolbar(uiCommon().managedVMUuid()),
+ screenId());
+ AssertPtrReturnVoid(m_pMiniToolBar);
+ {
+ /* Configure mini-toolbar: */
+ m_pMiniToolBar->addMenus(actionPool()->menus());
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigMinimizeAction,
+ this, &UIMachineWindowSeamless::sltShowMinimized, Qt::QueuedConnection);
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigExitAction,
+ actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &UIAction::trigger);
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigCloseAction,
+ actionPool()->action(UIActionIndex_M_Application_S_Close), &UIAction::trigger);
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigNotifyAboutWindowActivationStolen,
+ this, &UIMachineWindowSeamless::sltRevokeWindowActivation, Qt::QueuedConnection);
+ connect(m_pMiniToolBar, &UIMiniToolBar::sigAutoHideToggled,
+ this, &UIMachineWindowSeamless::sltHandleMiniToolBarAutoHideToggled);
+ }
+}
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+void UIMachineWindowSeamless::cleanupMiniToolbar()
+{
+ /* Delete mini-toolbar: */
+ delete m_pMiniToolBar;
+ m_pMiniToolBar = 0;
+}
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+void UIMachineWindowSeamless::cleanupVisualState()
+{
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /* Cleanup mini-toolbar: */
+ cleanupMiniToolbar();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+ /* Call to base-class: */
+ UIMachineWindow::cleanupVisualState();
+}
+
+void UIMachineWindowSeamless::placeOnScreen()
+{
+ /* Make sure this window has seamless logic: */
+ UIMachineLogicSeamless *pSeamlessLogic = qobject_cast<UIMachineLogicSeamless*>(machineLogic());
+ AssertPtrReturnVoid(pSeamlessLogic);
+
+ /* Get corresponding host-screen: */
+ const int iHostScreen = pSeamlessLogic->hostScreenForGuestScreen(m_uScreenId);
+ /* And corresponding working area: */
+ const QRect workingArea = gpDesktop->availableGeometry(iHostScreen);
+ Q_UNUSED(workingArea);
+
+#ifdef VBOX_WS_X11
+
+ /* Make sure we are located on corresponding host-screen: */
+ if ( UIDesktopWidgetWatchdog::screenCount() > 1
+ && (x() != workingArea.x() || y() != workingArea.y()))
+ {
+ // WORKAROUND:
+ // With Qt5 on KDE we can't just move the window onto desired host-screen if
+ // window is maximized. So we have to show it normal first of all:
+ if (isVisible() && isMaximized())
+ showNormal();
+
+ // WORKAROUND:
+ // With Qt5 on X11 we can't just move the window onto desired host-screen if
+ // window size is more than the available geometry (working area) of that
+ // host-screen. So we are resizing it to a smaller size first of all:
+ const QSize newSize = workingArea.size() * .9;
+ LogRel(("GUI: UIMachineWindowSeamless::placeOnScreen: Resize window: %d to smaller size: %dx%d\n",
+ m_uScreenId, newSize.width(), newSize.height()));
+ resize(newSize);
+ /* Move window onto required screen: */
+ const QPoint newPosition = workingArea.topLeft();
+ LogRel(("GUI: UIMachineWindowSeamless::placeOnScreen: Move window: %d to: %dx%d\n",
+ m_uScreenId, newPosition.x(), newPosition.y()));
+ move(newPosition);
+ }
+
+#else
+
+ /* Set appropriate window geometry: */
+ const QSize newSize = workingArea.size();
+ LogRel(("GUI: UIMachineWindowSeamless::placeOnScreen: Resize window: %d to: %dx%d\n",
+ m_uScreenId, newSize.width(), newSize.height()));
+ resize(newSize);
+ const QPoint newPosition = workingArea.topLeft();
+ LogRel(("GUI: UIMachineWindowSeamless::placeOnScreen: Move window: %d to: %dx%d\n",
+ m_uScreenId, newPosition.x(), newPosition.y()));
+ move(newPosition);
+
+#endif
+}
+
+void UIMachineWindowSeamless::showInNecessaryMode()
+{
+ /* Make sure window has seamless logic: */
+ UIMachineLogicSeamless *pSeamlessLogic = qobject_cast<UIMachineLogicSeamless*>(machineLogic());
+ AssertPtrReturnVoid(pSeamlessLogic);
+
+ /* If window shouldn't be shown or mapped to some host-screen: */
+ if (!uisession()->isScreenVisible(m_uScreenId) ||
+ !pSeamlessLogic->hasHostScreenForGuestScreen(m_uScreenId))
+ {
+ /* Remember whether the window was minimized: */
+ if (isMinimized())
+ m_fWasMinimized = true;
+
+ /* Hide window and reset it's state to NONE: */
+ setWindowState(Qt::WindowNoState);
+ hide();
+ }
+ /* If window should be shown and mapped to some host-screen: */
+ else
+ {
+ /* Check whether window was minimized: */
+ const bool fWasMinimized = isMinimized() && isVisible();
+ /* And reset it's state in such case before exposing: */
+ if (fWasMinimized)
+ setWindowState(Qt::WindowNoState);
+
+ /* Make sure window have appropriate geometry: */
+ placeOnScreen();
+
+#ifdef VBOX_WS_X11
+ /* Show window: */
+ if (!isMaximized())
+ showMaximized();
+#else
+ /* Show window: */
+ show();
+#endif
+
+ /* Restore minimized state if necessary: */
+ if (m_fWasMinimized || fWasMinimized)
+ {
+ m_fWasMinimized = false;
+ QMetaObject::invokeMethod(this, "showMinimized", Qt::QueuedConnection);
+ }
+
+ /* Adjust machine-view size if necessary: */
+ adjustMachineViewSize();
+
+ /* Make sure machine-view have focus: */
+ m_pMachineView->setFocus();
+ }
+}
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+void UIMachineWindowSeamless::updateAppearanceOf(int iElement)
+{
+ /* Call to base-class: */
+ UIMachineWindow::updateAppearanceOf(iElement);
+
+ /* Update mini-toolbar: */
+ if (iElement & UIVisualElement_MiniToolBar)
+ {
+ /* If there is a mini-toolbar: */
+ if (m_pMiniToolBar)
+ {
+ /* Get snapshot(s): */
+ QString strSnapshotName;
+ if (machine().GetSnapshotCount() > 0)
+ {
+ CSnapshot snapshot = machine().GetCurrentSnapshot();
+ strSnapshotName = " (" + snapshot.GetName() + ")";
+ }
+ /* Update mini-toolbar text: */
+ m_pMiniToolBar->setText(machineName() + strSnapshotName);
+ }
+ }
+}
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#ifdef VBOX_WS_X11
+void UIMachineWindowSeamless::changeEvent(QEvent *pEvent)
+{
+ switch (pEvent->type())
+ {
+ case QEvent::WindowStateChange:
+ {
+ /* Watch for window state changes: */
+ QWindowStateChangeEvent *pChangeEvent = static_cast<QWindowStateChangeEvent*>(pEvent);
+ LogRel2(("GUI: UIMachineWindowSeamless::changeEvent: Window state changed from %d to %d\n",
+ (int)pChangeEvent->oldState(), (int)windowState()));
+ if ( windowState() == Qt::WindowMinimized
+ && pChangeEvent->oldState() == Qt::WindowNoState
+ && !m_fIsMinimized)
+ {
+ /* Mark window minimized, isMinimized() is not enough due to Qt5vsX11 fight: */
+ LogRel2(("GUI: UIMachineWindowSeamless::changeEvent: Window minimized\n"));
+ m_fIsMinimized = true;
+ }
+ else
+ if ( windowState() == Qt::WindowNoState
+ && pChangeEvent->oldState() == Qt::WindowMinimized
+ && m_fIsMinimized)
+ {
+ /* Mark window restored, and do manual restoring with showInNecessaryMode(): */
+ LogRel2(("GUI: UIMachineWindowSeamless::changeEvent: Window restored\n"));
+ m_fIsMinimized = false;
+ /* Remember that we no more asked to minimize: */
+ m_fIsMinimizationRequested = false;
+ showInNecessaryMode();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ UIMachineWindow::changeEvent(pEvent);
+}
+#endif /* VBOX_WS_X11 */
+
+#ifdef VBOX_WS_WIN
+void UIMachineWindowSeamless::showEvent(QShowEvent *pEvent)
+{
+ /* Expose workaround again,
+ * Qt devs will never fix that it seems.
+ * This time they forget to set 'Mapped'
+ * attribute for initially frame-less window. */
+ setAttribute(Qt::WA_Mapped);
+
+ /* Call to base-class: */
+ UIMachineWindow::showEvent(pEvent);
+}
+#endif /* VBOX_WS_WIN */
+
+#ifdef VBOX_WITH_MASKED_SEAMLESS
+void UIMachineWindowSeamless::setMask(const QRegion &maskGuest)
+{
+ /* Remember new guest mask: */
+ m_maskGuest = maskGuest;
+
+ /* Prepare full mask: */
+ QRegion maskFull(m_maskGuest);
+
+ /* Shift full mask if left or top spacer width is NOT zero: */
+ if (m_pLeftSpacer->geometry().width() || m_pTopSpacer->geometry().height())
+ maskFull.translate(m_pLeftSpacer->geometry().width(), m_pTopSpacer->geometry().height());
+
+ /* Seamless-window for empty full mask should be empty too,
+ * but the QWidget::setMask() wrapper doesn't allow this.
+ * Instead, we see a full guest-screen of available-geometry size.
+ * So we have to make sure full mask have at least one pixel. */
+ if (maskFull.isEmpty())
+ maskFull += QRect(0, 0, 1, 1);
+
+ /* Make sure full mask had changed: */
+ if (m_maskFull != maskFull)
+ {
+ /* Compose viewport region to update: */
+ QRegion toUpdate = m_maskFull + maskFull;
+ /* Remember new full mask: */
+ m_maskFull = maskFull;
+ /* Assign new full mask: */
+ UIMachineWindow::setMask(m_maskFull);
+ /* Update viewport region finally: */
+ if (m_pMachineView)
+ m_pMachineView->viewport()->update(toUpdate);
+ }
+}
+#endif /* VBOX_WITH_MASKED_SEAMLESS */
diff --git a/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineWindowSeamless.h b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineWindowSeamless.h
new file mode 100644
index 00000000..61a9eef0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineWindowSeamless.h
@@ -0,0 +1,135 @@
+/* $Id: UIMachineWindowSeamless.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineWindowSeamless class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_runtime_seamless_UIMachineWindowSeamless_h
+#define FEQT_INCLUDED_SRC_runtime_seamless_UIMachineWindowSeamless_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIMachineWindow.h"
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+/* Forward declarations: */
+class UIMiniToolBar;
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+/** UIMachineWindow subclass used as seamless machine window implementation. */
+class UIMachineWindowSeamless : public UIMachineWindow
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pMachineLogic and @a uScreenId to the UIMachineWindow constructor. */
+ UIMachineWindowSeamless(UIMachineLogic *pMachineLogic, ulong uScreenId);
+
+private slots:
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Handles machine state change event. */
+ void sltMachineStateChanged();
+
+ /** Revokes window activation. */
+ void sltRevokeWindowActivation();
+
+ /** Handles signal about mini-toolbar auto-hide toggled.
+ * @param fEnabled Brings whether auto-hide is enabled. */
+ void sltHandleMiniToolBarAutoHideToggled(bool fEnabled);
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+ /** Shows window in minimized state. */
+ void sltShowMinimized();
+
+private:
+
+ /** Prepare visual-state routine. */
+ void prepareVisualState();
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Prepare mini-toolbar routine. */
+ void prepareMiniToolbar();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Cleanup mini-toolbar routine. */
+ void cleanupMiniToolbar();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+ /** Cleanup visual-state routine. */
+ void cleanupVisualState();
+
+ /** Updates geometry according to visual-state. */
+ void placeOnScreen();
+ /** Updates visibility according to visual-state. */
+ void showInNecessaryMode();
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Common update routine. */
+ void updateAppearanceOf(int iElement);
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#ifdef VBOX_WS_X11
+ /** X11: Handles @a pEvent about state change. */
+ void changeEvent(QEvent *pEvent);
+#endif
+
+#ifdef VBOX_WS_WIN
+ /** Win: Handles show @a pEvent. */
+ void showEvent(QShowEvent *pEvent);
+#endif
+
+#ifdef VBOX_WITH_MASKED_SEAMLESS
+ /** Assigns guest seamless mask. */
+ void setMask(const QRegion &maskGuest);
+#endif /* VBOX_WITH_MASKED_SEAMLESS */
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /** Holds the mini-toolbar instance. */
+ UIMiniToolBar *m_pMiniToolBar;
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+#ifdef VBOX_WITH_MASKED_SEAMLESS
+ /** Holds the full seamless mask. */
+ QRegion m_maskFull;
+ /** Holds the guest seamless mask. */
+ QRegion m_maskGuest;
+#endif /* VBOX_WITH_MASKED_SEAMLESS */
+
+ /** Holds whether the window was minimized before became hidden.
+ * Used to restore minimized state when the window shown again. */
+ bool m_fWasMinimized;
+#ifdef VBOX_WS_X11
+ /** X11: Holds whether the window minimization is currently requested.
+ * Used to prevent accidentally restoring to seamless state. */
+ bool m_fIsMinimizationRequested;
+ /** X11: Holds whether the window is currently minimized.
+ * Used to restore maximized state when the window restored again. */
+ bool m_fIsMinimized;
+#endif
+};
+
+#endif /* !FEQT_INCLUDED_SRC_runtime_seamless_UIMachineWindowSeamless_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/settings/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDefs.cpp b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDefs.cpp
new file mode 100644
index 00000000..239c3cb3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDefs.cpp
@@ -0,0 +1,54 @@
+/* $Id: UISettingsDefs.cpp $ */
+/** @file
+ * VBox Qt GUI - UISettingsDefs implementation
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UISettingsDefs.h"
+
+
+/* Using declarations: */
+using namespace UISettingsDefs;
+
+ConfigurationAccessLevel UISettingsDefs::configurationAccessLevel(KSessionState enmSessionState, KMachineState enmMachineState)
+{
+ /* Depending on passed arguments: */
+ switch (enmMachineState)
+ {
+ case KMachineState_PoweredOff:
+ case KMachineState_Teleported:
+ case KMachineState_Aborted: return enmSessionState == KSessionState_Unlocked ?
+ ConfigurationAccessLevel_Full :
+ ConfigurationAccessLevel_Partial_PoweredOff;
+ case KMachineState_AbortedSaved:
+ case KMachineState_Saved: return ConfigurationAccessLevel_Partial_Saved;
+ case KMachineState_Running:
+ case KMachineState_Paused: return ConfigurationAccessLevel_Partial_Running;
+ default: break;
+ }
+ /* Null by default: */
+ return ConfigurationAccessLevel_Null;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDefs.h b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDefs.h
new file mode 100644
index 00000000..df5f232f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDefs.h
@@ -0,0 +1,313 @@
+/* $Id: UISettingsDefs.h $ */
+/** @file
+ * VBox Qt GUI - Header with definitions and functions related to settings configuration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_UISettingsDefs_h
+#define FEQT_INCLUDED_SRC_settings_UISettingsDefs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QPair>
+#include <QString>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+
+/** Settings configuration namespace. */
+namespace UISettingsDefs
+{
+ /** Configuration access levels. */
+ enum ConfigurationAccessLevel
+ {
+ /** Configuration is not accessible. */
+ ConfigurationAccessLevel_Null,
+ /** Configuration is accessible fully. */
+ ConfigurationAccessLevel_Full,
+ /** Configuration is accessible partially, machine is in @a powered_off state. */
+ ConfigurationAccessLevel_Partial_PoweredOff,
+ /** Configuration is accessible partially, machine is in @a saved state. */
+ ConfigurationAccessLevel_Partial_Saved,
+ /** Configuration is accessible partially, machine is in @a running state. */
+ ConfigurationAccessLevel_Partial_Running,
+ };
+
+ /** Recording mode enum is used in Display setting page to determine the recording mode. */
+ enum RecordingMode
+ {
+ RecordingMode_None = 0,
+ RecordingMode_VideoAudio = 1,
+ RecordingMode_VideoOnly = 2,
+ RecordingMode_AudioOnly = 3,
+ RecordingMode_Max = 4
+ };
+
+ /** Determines configuration access level for passed @a enmSessionState and @a enmMachineState. */
+ SHARED_LIBRARY_STUFF ConfigurationAccessLevel configurationAccessLevel(KSessionState enmSessionState,
+ KMachineState enmMachineState);
+}
+
+Q_DECLARE_METATYPE(UISettingsDefs::RecordingMode);
+
+
+/** Template organizing settings object cache: */
+template <class CacheData> class UISettingsCache
+{
+public:
+
+ /** Constructs empty object cache. */
+ UISettingsCache() { m_value = qMakePair(CacheData(), CacheData()); }
+
+ /** Destructs cache object. */
+ virtual ~UISettingsCache() { /* Makes MSC happy */ }
+
+ /** Returns the NON-modifiable REFERENCE to the initial cached data. */
+ const CacheData &base() const { return m_value.first; }
+ /** Returns the NON-modifiable REFERENCE to the current cached data. */
+ const CacheData &data() const { return m_value.second; }
+ /** Returns the modifiable REFERENCE to the initial cached data. */
+ CacheData &base() { return m_value.first; }
+ /** Returns the modifiable REFERENCE to the current cached data. */
+ CacheData &data() { return m_value.second; }
+
+ /** Returns whether the cached object was removed.
+ * We assume that cached object was removed if
+ * initial data was set but current data was NOT set. */
+ virtual bool wasRemoved() const { return base() != CacheData() && data() == CacheData(); }
+
+ /** Returns whether the cached object was created.
+ * We assume that cached object was created if
+ * initial data was NOT set but current data was set. */
+ virtual bool wasCreated() const { return base() == CacheData() && data() != CacheData(); }
+
+ /** Returns whether the cached object was updated.
+ * We assume that cached object was updated if
+ * current and initial data were both set and not equal to each other. */
+ virtual bool wasUpdated() const { return base() != CacheData() && data() != CacheData() && data() != base(); }
+
+ /** Returns whether the cached object was changed.
+ * We assume that cached object was changed if
+ * it was 1. removed, 2. created or 3. updated. */
+ virtual bool wasChanged() const { return wasRemoved() || wasCreated() || wasUpdated(); }
+
+ /** Defines initial cached object data. */
+ void cacheInitialData(const CacheData &initialData) { m_value.first = initialData; }
+ /** Defines current cached object data: */
+ void cacheCurrentData(const CacheData &currentData) { m_value.second = currentData; }
+
+ /** Resets the initial and the current object data to be both empty. */
+ void clear() { m_value.first = CacheData(); m_value.second = CacheData(); }
+
+private:
+
+ /** Holds the cached object data. */
+ QPair<CacheData, CacheData> m_value;
+};
+
+
+/** Template organizing settings object cache with children. */
+template <class ParentCacheData, class ChildCacheData> class UISettingsCachePool : public UISettingsCache<ParentCacheData>
+{
+public:
+
+ /** Children map. */
+ typedef QMap<QString, ChildCacheData> UISettingsCacheChildMap;
+ /** Children map iterator. */
+ typedef QMapIterator<QString, ChildCacheData> UISettingsCacheChildIterator;
+
+ /** Constructs empty object cache. */
+ UISettingsCachePool() : UISettingsCache<ParentCacheData>() {}
+
+ /** Returns children count. */
+ int childCount() const { return m_children.size(); }
+ /** Returns the modifiable REFERENCE to the child cached data. */
+ ChildCacheData &child(const QString &strChildKey) { return m_children[strChildKey]; }
+ /** Wraps method above to return the modifiable REFERENCE to the child cached data. */
+ ChildCacheData &child(int iIndex) { return child(indexToKey(iIndex)); }
+ /** Returns the NON-modifiable COPY to the child cached data. */
+ const ChildCacheData child(const QString &strChildKey) const { return m_children[strChildKey]; }
+ /** Wraps method above to return the NON-modifiable COPY to the child cached data. */
+ const ChildCacheData child(int iIndex) const { return child(indexToKey(iIndex)); }
+
+ /** Returns whether the cache was updated.
+ * We assume that cache object was updated if current and
+ * initial data were both set and not equal to each other.
+ * Takes into account all the children. */
+ bool wasUpdated() const
+ {
+ /* First of all, cache object is considered to be updated if parent data was updated: */
+ bool fWasUpdated = UISettingsCache<ParentCacheData>::wasUpdated();
+ /* If parent data was NOT updated but also was NOT created or removed too
+ * (e.j. was NOT changed at all), we have to check children too: */
+ if (!fWasUpdated && !UISettingsCache<ParentCacheData>::wasRemoved() && !UISettingsCache<ParentCacheData>::wasCreated())
+ {
+ for (int iChildIndex = 0; !fWasUpdated && iChildIndex < childCount(); ++iChildIndex)
+ if (child(iChildIndex).wasChanged())
+ fWasUpdated = true;
+ }
+ return fWasUpdated;
+ }
+
+ /** Resets the initial and the current one data to be both empty.
+ * Removes all the children. */
+ void clear()
+ {
+ UISettingsCache<ParentCacheData>::clear();
+ m_children.clear();
+ }
+
+private:
+
+ /** Returns QString representation of passed @a iIndex. */
+ QString indexToKey(int iIndex) const
+ {
+ UISettingsCacheChildIterator childIterator(m_children);
+ for (int iChildIndex = 0; childIterator.hasNext(); ++iChildIndex)
+ {
+ childIterator.next();
+ if (iChildIndex == iIndex)
+ return childIterator.key();
+ }
+ return QString("%1").arg(iIndex, 8 /* up to 8 digits */, 10 /* base */, QChar('0') /* filler */);
+ }
+
+ /** Holds the children. */
+ UISettingsCacheChildMap m_children;
+};
+
+
+/** Template organizing settings object cache with 2 groups of children. */
+template <class ParentCacheData, class ChildCacheData1, class ChildCacheData2> class UISettingsCachePoolOfTwo : public UISettingsCache<ParentCacheData>
+{
+public:
+
+ /** Group 1 children map. */
+ typedef QMap<QString, ChildCacheData1> UISettingsCacheChildMap1;
+ /** Group 2 children map. */
+ typedef QMap<QString, ChildCacheData2> UISettingsCacheChildMap2;
+ /** Group 1 children map iterator. */
+ typedef QMapIterator<QString, ChildCacheData1> UISettingsCacheChildIterator1;
+ /** Group 2 children map iterator. */
+ typedef QMapIterator<QString, ChildCacheData2> UISettingsCacheChildIterator2;
+
+ /** Constructs empty cache object. */
+ UISettingsCachePoolOfTwo() : UISettingsCache<ParentCacheData>() {}
+
+ /** Returns group 1 children count. */
+ int childCount1() const { return m_children1.size(); }
+ /** Returns the modifiable REFERENCE to the group 1 child cached data. */
+ ChildCacheData1 &child1(const QString &strChildKey) { return m_children1[strChildKey]; }
+ /** Wraps method above to return the modifiable REFERENCE to the group 1 child cached data. */
+ ChildCacheData1 &child1(int iIndex) { return child1(indexToKey1(iIndex)); }
+ /** Returns the NON-modifiable COPY to the group 1 child cached data. */
+ const ChildCacheData1 child1(const QString &strChildKey) const { return m_children1[strChildKey]; }
+ /** Wraps method above to return the NON-modifiable COPY to the group 1 child cached data. */
+ const ChildCacheData1 child1(int iIndex) const { return child1(indexToKey1(iIndex)); }
+
+ /** Returns group 2 children count. */
+ int childCount2() const { return m_children2.size(); }
+ /** Returns the modifiable REFERENCE to the group 2 child cached data. */
+ ChildCacheData2 &child2(const QString &strChildKey) { return m_children2[strChildKey]; }
+ /** Wraps method above to return the modifiable REFERENCE to the group 2 child cached data. */
+ ChildCacheData2 &child2(int iIndex) { return child2(indexToKey2(iIndex)); }
+ /** Returns the NON-modifiable COPY to the group 2 child cached data. */
+ const ChildCacheData2 child2(const QString &strChildKey) const { return m_children2[strChildKey]; }
+ /** Wraps method above to return the NON-modifiable COPY to the group 2 child cached data. */
+ const ChildCacheData2 child2(int iIndex) const { return child2(indexToKey2(iIndex)); }
+
+ /** Returns whether the cache was updated.
+ * We assume that cache object was updated if current and
+ * initial data were both set and not equal to each other.
+ * Takes into account all the children of both groups. */
+ bool wasUpdated() const
+ {
+ /* First of all, cache object is considered to be updated if parent data was updated: */
+ bool fWasUpdated = UISettingsCache<ParentCacheData>::wasUpdated();
+ /* If parent data was NOT updated but also was NOT created or removed too
+ * (e.j. was NOT changed at all), we have to check children too: */
+ if (!fWasUpdated && !UISettingsCache<ParentCacheData>::wasRemoved() && !UISettingsCache<ParentCacheData>::wasCreated())
+ {
+ for (int iChildIndex = 0; !fWasUpdated && iChildIndex < childCount1(); ++iChildIndex)
+ if (child1(iChildIndex).wasChanged())
+ fWasUpdated = true;
+ for (int iChildIndex = 0; !fWasUpdated && iChildIndex < childCount2(); ++iChildIndex)
+ if (child2(iChildIndex).wasChanged())
+ fWasUpdated = true;
+ }
+ return fWasUpdated;
+ }
+
+ /** Resets the initial and the current one data to be both empty.
+ * Removes all the children from both groups. */
+ void clear()
+ {
+ UISettingsCache<ParentCacheData>::clear();
+ m_children1.clear();
+ m_children2.clear();
+ }
+
+private:
+
+ /** Returns QString representation of passed @a iIndex inside group 1. */
+ QString indexToKey1(int iIndex) const
+ {
+ UISettingsCacheChildIterator1 childIterator(m_children1);
+ for (int iChildIndex = 0; childIterator.hasNext(); ++iChildIndex)
+ {
+ childIterator.next();
+ if (iChildIndex == iIndex)
+ return childIterator.key();
+ }
+ return QString("%1").arg(iIndex, 8 /* up to 8 digits */, 10 /* base */, QChar('0') /* filler */);
+ }
+
+ /** Returns QString representation of passed @a iIndex inside group 2. */
+ QString indexToKey2(int iIndex) const
+ {
+ UISettingsCacheChildIterator2 childIterator(m_children2);
+ for (int iChildIndex = 0; childIterator.hasNext(); ++iChildIndex)
+ {
+ childIterator.next();
+ if (iChildIndex == iIndex)
+ return childIterator.key();
+ }
+ return QString("%1").arg(iIndex, 8 /* up to 8 digits */, 10 /* base */, QChar('0') /* filler */);
+ }
+
+ /** Holds the children of group 1. */
+ UISettingsCacheChildMap1 m_children1;
+ /** Holds the children of group 2. */
+ UISettingsCacheChildMap2 m_children2;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_settings_UISettingsDefs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialog.cpp b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialog.cpp
new file mode 100644
index 00000000..b24c5e8a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialog.cpp
@@ -0,0 +1,897 @@
+/* $Id: UISettingsDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UISettingsDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCloseEvent>
+#include <QGridLayout>
+#include <QLabel>
+#include <QProgressBar>
+#include <QPushButton>
+#include <QStackedWidget>
+#include <QTimer>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIWidgetValidator.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UIPopupCenter.h"
+#include "UISettingsDialog.h"
+#include "UISettingsPage.h"
+#include "UISettingsSelector.h"
+#include "UISettingsSerializer.h"
+#include "QIToolBar.h"
+#include "UIWarningPane.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils.h"
+#endif
+
+#ifdef VBOX_WS_MAC
+# define VBOX_GUI_WITH_TOOLBAR_SETTINGS
+#endif
+
+
+UISettingsDialog::UISettingsDialog(QWidget *pParent,
+ const QString &strCategory,
+ const QString &strControl)
+ : QIWithRetranslateUI<QMainWindow>(pParent)
+ , m_strCategory(strCategory)
+ , m_strControl(strControl)
+ , m_pSelector(0)
+ , m_pStack(0)
+ , m_enmConfigurationAccessLevel(ConfigurationAccessLevel_Null)
+ , m_pSerializeProcess(0)
+ , m_fPolished(false)
+ , m_fSerializationIsInProgress(false)
+ , m_fSerializationClean(true)
+ , m_fClosed(false)
+ , m_pStatusBar(0)
+ , m_pProcessBar(0)
+ , m_pWarningPane(0)
+ , m_fValid(true)
+ , m_fSilent(true)
+ , m_pWhatsThisTimer(new QTimer(this))
+ , m_pLabelTitle(0)
+ , m_pButtonBox(0)
+ , m_pWidgetStackHandler(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UISettingsDialog::~UISettingsDialog()
+{
+ /* Delete serializer if exists: */
+ if (serializeProcess())
+ {
+ delete m_pSerializeProcess;
+ m_pSerializeProcess = 0;
+ }
+
+ /* Recall popup-pane if any: */
+ popupCenter().recall(m_pStack, "SettingsDialogWarning");
+
+ /* Delete selector early! */
+ delete m_pSelector;
+}
+
+void UISettingsDialog::accept()
+{
+ /* Save data: */
+ save();
+
+ /* If serialization was clean: */
+ if (m_fSerializationClean)
+ {
+ /* Tell the listener to close us (once): */
+ if (!m_fClosed)
+ {
+ m_fClosed = true;
+ emit sigClose();
+ }
+ }
+}
+
+void UISettingsDialog::reject()
+{
+ if (!isSerializationInProgress())
+ close();
+}
+
+void UISettingsDialog::sltCategoryChanged(int cId)
+{
+#ifndef VBOX_WS_MAC
+ if (m_pButtonBox)
+ uiCommon().setHelpKeyword(m_pButtonBox->button(QDialogButtonBox::Help), m_pageHelpKeywords[cId]);
+#endif
+ const int iIndex = m_pages.value(cId);
+
+#ifdef VBOX_WS_MAC
+ /* If index is within the stored size list bounds: */
+ if (iIndex < m_sizeList.count())
+ {
+ /* Get current/stored size: */
+ const QSize cs = size();
+ const QSize ss = m_sizeList.at(iIndex);
+
+ /* Switch to the new page first if we are shrinking: */
+ if (cs.height() > ss.height())
+ m_pStack->setCurrentIndex(iIndex);
+
+ /* Do the animation: */
+ ::darwinWindowAnimateResize(this, QRect (x(), y(), ss.width(), ss.height()));
+
+ /* Switch to the new page last if we are zooming: */
+ if (cs.height() <= ss.height())
+ m_pStack->setCurrentIndex(iIndex);
+
+ /* Unlock all page policies but lock the current one: */
+ for (int i = 0; i < m_pStack->count(); ++i)
+ m_pStack->widget(i)->setSizePolicy(QSizePolicy::Minimum, i == iIndex ? QSizePolicy::Minimum : QSizePolicy::Ignored);
+
+ /* And make sure layouts are freshly calculated: */
+ foreach (QLayout *pLayout, findChildren<QLayout*>())
+ {
+ pLayout->update();
+ pLayout->activate();
+ }
+ }
+#else /* !VBOX_WS_MAC */
+ m_pStack->setCurrentIndex(iIndex);
+#endif /* !VBOX_WS_MAC */
+
+#ifdef VBOX_GUI_WITH_TOOLBAR_SETTINGS
+ setWindowTitle(title());
+#else
+ m_pLabelTitle->setText(m_pSelector->itemText(cId));
+#endif
+}
+
+void UISettingsDialog::sltMarkLoaded()
+{
+ /* Delete serializer if exists: */
+ if (serializeProcess())
+ {
+ delete m_pSerializeProcess;
+ m_pSerializeProcess = 0;
+ }
+
+ /* Mark serialization finished: */
+ m_fSerializationIsInProgress = false;
+}
+
+void UISettingsDialog::sltMarkSaved()
+{
+ /* Delete serializer if exists: */
+ if (serializeProcess())
+ {
+ delete m_pSerializeProcess;
+ m_pSerializeProcess = 0;
+ }
+
+ /* Mark serialization finished: */
+ m_fSerializationIsInProgress = false;
+}
+
+void UISettingsDialog::sltHandleProcessStarted()
+{
+ m_pProcessBar->setValue(0);
+ m_pStatusBar->setCurrentWidget(m_pProcessBar);
+}
+
+void UISettingsDialog::sltHandleProcessProgressChange(int iValue)
+{
+ m_pProcessBar->setValue(iValue);
+ if (m_pProcessBar->value() == m_pProcessBar->maximum())
+ {
+ if (!m_fValid || !m_fSilent)
+ m_pStatusBar->setCurrentWidget(m_pWarningPane);
+ else
+ m_pStatusBar->setCurrentIndex(0);
+ }
+}
+
+bool UISettingsDialog::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Ignore objects which are NOT widgets: */
+ if (!pObject->isWidgetType())
+ return QMainWindow::eventFilter(pObject, pEvent);
+
+ /* Ignore widgets which window is NOT settings window: */
+ QWidget *pWidget = static_cast<QWidget*>(pObject);
+ if (pWidget->window() != this)
+ return QMainWindow::eventFilter(pObject, pEvent);
+
+ /* Process different event-types: */
+ switch (pEvent->type())
+ {
+ /* Process enter/leave events to remember whats-this candidates: */
+ case QEvent::Enter:
+ case QEvent::Leave:
+ {
+ if (pEvent->type() == QEvent::Enter)
+ m_pWhatsThisCandidate = pWidget;
+ else
+ m_pWhatsThisCandidate = 0;
+
+ m_pWhatsThisTimer->start(100);
+ break;
+ }
+ /* Process focus-in event to update whats-this pane: */
+ case QEvent::FocusIn:
+ {
+ sltUpdateWhatsThis(true /* got focus? */);
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Base-class processing: */
+ return QMainWindow::eventFilter(pObject, pEvent);
+}
+
+void UISettingsDialog::retranslateUi()
+{
+ setWhatsThis(tr("<i>Select a settings category from the list on the left-hand side and move the mouse over a settings "
+ "item to get more information.</i>"));
+ m_pLabelTitle->setText(QString());
+
+ /* Translate warning stuff: */
+ m_strWarningHint = tr("Invalid settings detected");
+ if (!m_fValid || !m_fSilent)
+ m_pWarningPane->setWarningLabel(m_strWarningHint);
+
+#ifndef VBOX_GUI_WITH_TOOLBAR_SETTINGS
+ /* Retranslate current page headline: */
+ m_pLabelTitle->setText(m_pSelector->itemText(m_pSelector->currentId()));
+#endif
+
+ /* Retranslate all validators: */
+ foreach (UIPageValidator *pValidator, findChildren<UIPageValidator*>())
+ if (!pValidator->lastMessage().isEmpty())
+ revalidate(pValidator);
+ revalidate();
+}
+
+void UISettingsDialog::showEvent(QShowEvent *pEvent)
+{
+ /* Polish stuff: */
+ if (!m_fPolished)
+ {
+ m_fPolished = true;
+ polishEvent(pEvent);
+ }
+
+ /* Call to base-class: */
+ QIWithRetranslateUI<QMainWindow>::showEvent(pEvent);
+}
+
+void UISettingsDialog::polishEvent(QShowEvent*)
+{
+ /* Check what's the minimum selector size: */
+ const int iMinWidth = m_pSelector->minWidth();
+
+#ifdef VBOX_WS_MAC
+
+ /* Remove all title bar buttons (Buggy Qt): */
+ ::darwinSetHidesAllTitleButtons(this);
+
+ /* Unlock all page policies initially: */
+ for (int i = 0; i < m_pStack->count(); ++i)
+ m_pStack->widget(i)->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Ignored);
+
+ /* Activate every single page to get the optimal size: */
+ for (int i = m_pStack->count() - 1; i >= 0; --i)
+ {
+ /* Activate current page: */
+ m_pStack->setCurrentIndex(i);
+
+ /* Lock current page policy temporary: */
+ m_pStack->widget(i)->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ /* And make sure layouts are freshly calculated: */
+ foreach (QLayout *pLayout, findChildren<QLayout*>())
+ {
+ pLayout->update();
+ pLayout->activate();
+ }
+
+ /* Acquire minimum size-hint: */
+ QSize s = minimumSizeHint();
+ // WORKAROUND:
+ // Take into account the height of native tool-bar title.
+ // It will be applied only after widget is really shown.
+ // The height is 11pix * 2 (possible HiDPI support).
+ s.setHeight(s.height() + 11 * 2);
+ /* Also make sure that width is no less than tool-bar: */
+ if (iMinWidth > s.width())
+ s.setWidth(iMinWidth);
+ /* And remember the size finally: */
+ m_sizeList.insert(0, s);
+
+ /* Unlock the policy for current page again: */
+ m_pStack->widget(i)->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Ignored);
+ }
+
+ sltCategoryChanged(m_pSelector->currentId());
+
+#else /* VBOX_WS_MAC */
+
+ /* Resize to the minimum possible size: */
+ QSize s = minimumSize();
+ if (iMinWidth > s.width())
+ s.setWidth(iMinWidth);
+ resize(s);
+
+#endif /* VBOX_WS_MAC */
+
+ /* Explicit centering according to our parent: */
+ gpDesktop->centerWidget(this, parentWidget(), false);
+}
+
+void UISettingsDialog::closeEvent(QCloseEvent *pEvent)
+{
+ /* Ignore event initially: */
+ pEvent->ignore();
+
+ /* Check whether there are unsaved settings
+ * which will be lost in such case: */
+ if ( !isSettingsChanged()
+ || msgCenter().confirmSettingsDiscarding(this))
+ {
+ /* Tell the listener to close us (once): */
+ if (!m_fClosed)
+ {
+ m_fClosed = true;
+ emit sigClose();
+ }
+ }
+}
+
+void UISettingsDialog::choosePageAndTab(bool fKeepPreviousByDefault /* = false */)
+{
+ /* Setup settings window: */
+ if (!m_strCategory.isNull())
+ {
+ m_pSelector->selectByLink(m_strCategory);
+ /* Search for a widget with the given name: */
+ if (!m_strControl.isNull())
+ {
+ if (QWidget *pWidget = m_pStack->findChild<QWidget*>(m_strControl))
+ {
+ QList<QWidget*> parents;
+ QWidget *pParentWidget = pWidget;
+ while ((pParentWidget = pParentWidget->parentWidget()) != 0)
+ {
+ if (QTabWidget *pTabWidget = qobject_cast<QTabWidget*>(pParentWidget))
+ {
+ // WORKAROUND:
+ // The tab contents widget is two steps down
+ // (QTabWidget -> QStackedWidget -> QWidget).
+ QWidget *pTabPage = parents[parents.count() - 1];
+ if (pTabPage)
+ pTabPage = parents[parents.count() - 2];
+ if (pTabPage)
+ pTabWidget->setCurrentWidget(pTabPage);
+ }
+ parents.append(pParentWidget);
+ }
+ pWidget->setFocus();
+ }
+ }
+ }
+ /* First item as default (if previous is not guarded): */
+ else if (!fKeepPreviousByDefault)
+ m_pSelector->selectById(1);
+}
+
+void UISettingsDialog::loadData(QVariant &data)
+{
+ /* Mark serialization started: */
+ m_fSerializationIsInProgress = true;
+
+ /* Create settings loader: */
+ m_pSerializeProcess = new UISettingsSerializer(this, UISettingsSerializer::Load,
+ data, m_pSelector->settingPages());
+ AssertPtrReturnVoid(m_pSerializeProcess);
+ {
+ /* Configure settings loader: */
+ connect(m_pSerializeProcess, &UISettingsSerializer::sigNotifyAboutProcessStarted, this, &UISettingsDialog::sltHandleProcessStarted);
+ connect(m_pSerializeProcess, &UISettingsSerializer::sigNotifyAboutProcessProgressChanged, this, &UISettingsDialog::sltHandleProcessProgressChange);
+ connect(m_pSerializeProcess, &UISettingsSerializer::sigNotifyAboutProcessFinished, this, &UISettingsDialog::sltMarkLoaded);
+
+ /* Raise current page priority: */
+ m_pSerializeProcess->raisePriorityOfPage(m_pSelector->currentId());
+
+ /* Start settings loader: */
+ m_pSerializeProcess->start();
+
+ /* Upload data finally: */
+ data = m_pSerializeProcess->data();
+ }
+}
+
+void UISettingsDialog::saveData(QVariant &data)
+{
+ /* Mark serialization started: */
+ m_fSerializationIsInProgress = true;
+
+ /* Create the 'settings saver': */
+ QPointer<UISettingsSerializerProgress> pDlgSerializeProgress =
+ new UISettingsSerializerProgress(this, UISettingsSerializer::Save,
+ data, m_pSelector->settingPages());
+ AssertPtrReturnVoid(static_cast<UISettingsSerializerProgress*>(pDlgSerializeProgress));
+ {
+ /* Make the 'settings saver' temporary parent for all sub-dialogs: */
+ windowManager().registerNewParent(pDlgSerializeProgress, windowManager().realParentWindow(this));
+
+ /* Execute the 'settings saver': */
+ pDlgSerializeProgress->exec();
+
+ /* Any modal dialog can be destroyed in own event-loop
+ * as a part of application termination procedure..
+ * We have to check if the dialog still valid. */
+ if (pDlgSerializeProgress)
+ {
+ /* Remember whether the serialization was clean: */
+ m_fSerializationClean = pDlgSerializeProgress->isClean();
+
+ /* Upload 'settings saver' data: */
+ data = pDlgSerializeProgress->data();
+
+ /* Delete the 'settings saver': */
+ delete pDlgSerializeProgress;
+ }
+ }
+}
+
+void UISettingsDialog::setConfigurationAccessLevel(ConfigurationAccessLevel enmConfigurationAccessLevel)
+{
+ /* Make sure something changed: */
+ if (m_enmConfigurationAccessLevel == enmConfigurationAccessLevel)
+ return;
+
+ /* Apply new configuration access level: */
+ m_enmConfigurationAccessLevel = enmConfigurationAccessLevel;
+
+ /* And propagate it to settings-page(s): */
+ foreach (UISettingsPage *pPage, m_pSelector->settingPages())
+ pPage->setConfigurationAccessLevel(configurationAccessLevel());
+}
+
+void UISettingsDialog::addItem(const QString &strBigIcon,
+ const QString &strMediumIcon,
+ const QString &strSmallIcon,
+ int cId,
+ const QString &strLink,
+ UISettingsPage *pSettingsPage /* = 0 */,
+ int iParentId /* = -1 */)
+{
+ /* Add new selector item: */
+ if (QWidget *pPage = m_pSelector->addItem(strBigIcon, strMediumIcon, strSmallIcon,
+ cId, strLink, pSettingsPage, iParentId))
+ {
+ /* Add stack-widget page if created: */
+ m_pages[cId] = m_pStack->addWidget(pPage);
+ }
+ /* Assign validator if necessary: */
+ if (pSettingsPage)
+ {
+ pSettingsPage->setId(cId);
+ assignValidator(pSettingsPage);
+ }
+}
+
+void UISettingsDialog::addPageHelpKeyword(int iPageType, const QString &strHelpKeyword)
+{
+ m_pageHelpKeywords[iPageType] = strHelpKeyword;
+}
+
+void UISettingsDialog::revalidate(UIPageValidator *pValidator)
+{
+ /* Perform page revalidation: */
+ UISettingsPage *pSettingsPage = pValidator->page();
+ QList<UIValidationMessage> messages;
+ bool fIsValid = pSettingsPage->validate(messages);
+
+ /* Remember revalidation result: */
+ pValidator->setValid(fIsValid);
+
+ /* Remember warning/error message: */
+ if (messages.isEmpty())
+ pValidator->setLastMessage(QString());
+ else
+ {
+ /* Prepare title prefix: */
+ // Its the only thing preventing us from moving this method to validator.
+ const QString strTitlePrefix(m_pSelector->itemTextByPage(pSettingsPage));
+ /* Prepare text: */
+ QStringList text;
+ foreach (const UIValidationMessage &message, messages)
+ {
+ /* Prepare title: */
+ const QString strTitle(message.first.isNull() ? tr("<b>%1</b> page:").arg(strTitlePrefix) :
+ tr("<b>%1: %2</b> page:").arg(strTitlePrefix, message.first));
+ /* Prepare paragraph: */
+ QStringList paragraph(message.second);
+ paragraph.prepend(strTitle);
+ /* Format text for iterated message: */
+ text << paragraph.join("<br>");
+ }
+ /* Remember text: */
+ pValidator->setLastMessage(text.join("<br><br>"));
+ LogRelFlow(("Settings Dialog: Page validation FAILED: {%s}\n",
+ pValidator->lastMessage().toUtf8().constData()));
+ }
+}
+
+void UISettingsDialog::revalidate()
+{
+ /* Perform dialog revalidation: */
+ m_fValid = true;
+ m_fSilent = true;
+ m_pWarningPane->setWarningLabel(QString());
+
+ /* Enumerating all the validators we have: */
+ QList<UIPageValidator*> validators(findChildren<UIPageValidator*>());
+ foreach (UIPageValidator *pValidator, validators)
+ {
+ /* Is current validator have something to say? */
+ if (!pValidator->lastMessage().isEmpty())
+ {
+ /* What page is it related to? */
+ UISettingsPage *pFailedSettingsPage = pValidator->page();
+ LogRelFlow(("Settings Dialog: Dialog validation FAILED: Page *%s*\n",
+ pFailedSettingsPage->internalName().toUtf8().constData()));
+
+ /* Show error first: */
+ if (!pValidator->isValid())
+ m_fValid = false;
+ /* Show warning if message is not an error: */
+ else
+ m_fSilent = false;
+
+ /* Configure warning-pane label: */
+ m_pWarningPane->setWarningLabel(m_strWarningHint);
+
+ /* Stop dialog revalidation on first error/warning: */
+ break;
+ }
+ }
+
+ /* Make sure warning-pane visible if necessary: */
+ if ((!m_fValid || !m_fSilent) && m_pStatusBar->currentIndex() == 0)
+ m_pStatusBar->setCurrentWidget(m_pWarningPane);
+ /* Make sure empty-pane visible otherwise: */
+ else if (m_fValid && m_fSilent && m_pStatusBar->currentWidget() == m_pWarningPane)
+ m_pStatusBar->setCurrentIndex(0);
+
+ /* Lock/unlock settings-page OK button according global validity status: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(m_fValid);
+}
+
+bool UISettingsDialog::isSettingsChanged()
+{
+ bool fIsSettingsChanged = false;
+ foreach (UISettingsPage *pPage, m_pSelector->settingPages())
+ {
+ pPage->putToCache();
+ if (!fIsSettingsChanged && pPage->changed())
+ fIsSettingsChanged = true;
+ }
+ return fIsSettingsChanged;
+}
+
+void UISettingsDialog::sltHandleValidityChange(UIPageValidator *pValidator)
+{
+ /* Determine which settings-page had called for revalidation: */
+ if (UISettingsPage *pSettingsPage = pValidator->page())
+ {
+ /* Determine settings-page name: */
+ const QString strPageName(pSettingsPage->internalName());
+
+ LogRelFlow(("Settings Dialog: %s Page: Revalidation in progress..\n",
+ strPageName.toUtf8().constData()));
+
+ /* Perform page revalidation: */
+ revalidate(pValidator);
+ /* Perform inter-page recorrelation: */
+ recorrelate(pSettingsPage);
+ /* Perform dialog revalidation: */
+ revalidate();
+
+ LogRelFlow(("Settings Dialog: %s Page: Revalidation complete.\n",
+ strPageName.toUtf8().constData()));
+ }
+}
+
+void UISettingsDialog::sltHandleWarningPaneHovered(UIPageValidator *pValidator)
+{
+ LogRelFlow(("Settings Dialog: Warning-icon hovered: %s.\n", pValidator->internalName().toUtf8().constData()));
+
+ /* Show corresponding popup: */
+ if (!m_fValid || !m_fSilent)
+ {
+ popupCenter().popup(m_pStack, "SettingsDialogWarning",
+ pValidator->lastMessage());
+ }
+}
+
+void UISettingsDialog::sltHandleWarningPaneUnhovered(UIPageValidator *pValidator)
+{
+ LogRelFlow(("Settings Dialog: Warning-icon unhovered: %s.\n", pValidator->internalName().toUtf8().constData()));
+
+ /* Recall corresponding popup: */
+ popupCenter().recall(m_pStack, "SettingsDialogWarning");
+}
+
+void UISettingsDialog::sltUpdateWhatsThis(bool fGotFocus)
+{
+ QString strWhatsThisText;
+ QWidget *pWhatsThisWidget = 0;
+
+ /* If focus had NOT changed: */
+ if (!fGotFocus)
+ {
+ /* We will use the recommended candidate: */
+ if (m_pWhatsThisCandidate && m_pWhatsThisCandidate != this)
+ pWhatsThisWidget = m_pWhatsThisCandidate;
+ }
+ /* If focus had changed: */
+ else
+ {
+ /* We will use the focused widget instead: */
+ pWhatsThisWidget = QApplication::focusWidget();
+ }
+
+ /* If the given widget lacks the whats-this text, look at its parent: */
+ while (pWhatsThisWidget && pWhatsThisWidget != this)
+ {
+ strWhatsThisText = pWhatsThisWidget->whatsThis();
+ if (!strWhatsThisText.isEmpty())
+ break;
+ pWhatsThisWidget = pWhatsThisWidget->parentWidget();
+ }
+
+ if (pWhatsThisWidget && !strWhatsThisText.isEmpty())
+ pWhatsThisWidget->setToolTip(strWhatsThisText);
+}
+
+void UISettingsDialog::prepare()
+{
+ prepareWidgets();
+
+ /* Configure title: */
+ if (m_pLabelTitle)
+ {
+ /* Page-title font is bold and larger but derived from the system font: */
+ QFont pageTitleFont = font();
+ pageTitleFont.setBold(true);
+ pageTitleFont.setPointSize(pageTitleFont.pointSize() + 2);
+ m_pLabelTitle->setFont(pageTitleFont);
+ }
+
+ /* Prepare selector: */
+ QGridLayout *pMainLayout = static_cast<QGridLayout*>(centralWidget()->layout());
+ if (pMainLayout)
+ {
+#ifdef VBOX_GUI_WITH_TOOLBAR_SETTINGS
+
+ /* No page-title with tool-bar: */
+ m_pLabelTitle->hide();
+
+ /* Create modern tool-bar selector: */
+ m_pSelector = new UISettingsSelectorToolBar(this);
+ if (m_pSelector)
+ {
+ /* Configure tool-bar: */
+ static_cast<QIToolBar*>(m_pSelector->widget())->enableMacToolbar();
+
+ /* Add tool-bar into page: */
+ addToolBar(qobject_cast<QToolBar*>(m_pSelector->widget()));
+ }
+
+ /* No title in this mode, we change the title of the window: */
+ pMainLayout->setColumnMinimumWidth(0, 0);
+ pMainLayout->setHorizontalSpacing(0);
+
+#else /* !VBOX_GUI_WITH_TOOLBAR_SETTINGS */
+
+ /* Create classical tree-view selector: */
+ m_pSelector = new UISettingsSelectorTreeView(this);
+ if (m_pSelector)
+ {
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pSelector->widget(), 0, 0, 2, 1);
+
+ /* Set focus: */
+ m_pSelector->widget()->setFocus();
+ }
+
+#endif /* !VBOX_GUI_WITH_TOOLBAR_SETTINGS */
+
+ connect(m_pSelector, &UISettingsSelectorTreeView::sigCategoryChanged, this, &UISettingsDialog::sltCategoryChanged);
+ }
+
+ /* Prepare stack-handler: */
+ if (m_pWidgetStackHandler)
+ {
+ /* Create page-stack layout: */
+ QVBoxLayout *pStackLayout = new QVBoxLayout(m_pWidgetStackHandler);
+ if (pStackLayout)
+ {
+ /* Confugre page-stack layout: */
+ pStackLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create page-stack: */
+ m_pStack = new QStackedWidget;
+ if (m_pStack)
+ {
+ /* Configure page-stack: */
+ popupCenter().setPopupStackOrientation(m_pStack, UIPopupStackOrientation_Bottom);
+
+ /* Add into layout: */
+ pStackLayout->addWidget(m_pStack);
+ }
+ }
+ }
+
+ /* Prepare button-box: */
+ if (m_pButtonBox)
+ {
+ /* Create status-bar: */
+ m_pStatusBar = new QStackedWidget;
+ if (m_pStatusBar)
+ {
+ /* Add empty widget: */
+ m_pStatusBar->addWidget(new QWidget);
+
+ /* Create process-bar: */
+ m_pProcessBar = new QProgressBar;
+ if (m_pProcessBar)
+ {
+ /* Configure process-bar: */
+ m_pProcessBar->setMinimum(0);
+ m_pProcessBar->setMaximum(100);
+
+ /* Add into status-bar: */
+ m_pStatusBar->addWidget(m_pProcessBar);
+ }
+
+ /* Create warning-pane: */
+ m_pWarningPane = new UIWarningPane;
+ if (m_pWarningPane)
+ {
+ /* Configure warning-pane: */
+ connect(m_pWarningPane, &UIWarningPane::sigHoverEnter,
+ this, &UISettingsDialog::sltHandleWarningPaneHovered);
+ connect(m_pWarningPane, &UIWarningPane::sigHoverLeave,
+ this, &UISettingsDialog::sltHandleWarningPaneUnhovered);
+
+ /* Add into status-bar: */
+ m_pStatusBar->addWidget(m_pWarningPane);
+ }
+
+ /* Add status-bar to button-box: */
+ m_pButtonBox->addExtraWidget(m_pStatusBar);
+ }
+ }
+
+ /* Setup what's this stuff: */
+ qApp->installEventFilter(this);
+ m_pWhatsThisTimer->setSingleShot(true);
+ connect(m_pWhatsThisTimer, &QTimer::timeout,
+ this, &UISettingsDialog::sltUpdateWhatsThisNoFocus);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UISettingsDialog::prepareWidgets()
+{
+ /* Prepare central-widget: */
+ setCentralWidget(new QWidget);
+ if (centralWidget())
+ {
+ /* Prepare main layout: */
+ QGridLayout *pLayoutMain = new QGridLayout(centralWidget());
+ if (pLayoutMain)
+ {
+ /* Prepare title label: */
+ m_pLabelTitle = new QLabel(centralWidget());
+ if (m_pLabelTitle)
+ {
+ m_pLabelTitle->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed));
+ QPalette pal = QApplication::palette();
+ pal.setColor(QPalette::Active, QPalette::Window, pal.color(QPalette::Active, QPalette::Base));
+ m_pLabelTitle->setPalette(pal);
+ QFont fnt;
+ fnt.setFamily(QStringLiteral("Sans Serif"));
+ fnt.setPointSize(11);
+ fnt.setBold(true);
+ fnt.setWeight(QFont::ExtraBold);
+ m_pLabelTitle->setFont(fnt);
+ m_pLabelTitle->setAutoFillBackground(true);
+ m_pLabelTitle->setFrameShadow(QFrame::Sunken);
+ m_pLabelTitle->setMargin(9);
+
+ pLayoutMain->addWidget(m_pLabelTitle, 0, 1);
+ }
+
+ /* Prepare widget stack handler: */
+ m_pWidgetStackHandler = new QWidget(centralWidget());
+ if (m_pWidgetStackHandler)
+ {
+ m_pWidgetStackHandler->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
+ pLayoutMain->addWidget(m_pWidgetStackHandler, 1, 1);
+ }
+
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox(centralWidget());
+ if (m_pButtonBox)
+ {
+#ifndef VBOX_WS_MAC
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel |
+ QDialogButtonBox::NoButton | QDialogButtonBox::Help);
+ m_pButtonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents);
+#else
+ // WORKAROUND:
+ // No Help button on macOS for now, conflict with old Qt.
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel |
+ QDialogButtonBox::NoButton);
+#endif
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setShortcut(Qt::Key_Return);
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UISettingsDialog::close);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UISettingsDialog::accept);
+#ifndef VBOX_WS_MAC
+ connect(m_pButtonBox->button(QDialogButtonBox::Help), &QAbstractButton::pressed,
+ &msgCenter(), &UIMessageCenter::sltHandleHelpRequest);
+#endif
+
+ pLayoutMain->addWidget(m_pButtonBox, 2, 0, 1, 2);
+ }
+ }
+ }
+}
+
+void UISettingsDialog::assignValidator(UISettingsPage *pPage)
+{
+ /* Assign validator: */
+ UIPageValidator *pValidator = new UIPageValidator(this, pPage);
+ connect(pValidator, &UIPageValidator::sigValidityChanged, this, &UISettingsDialog::sltHandleValidityChange);
+ pPage->setValidator(pValidator);
+ m_pWarningPane->registerValidator(pValidator);
+
+ /// @todo Why here?
+ /* Configure navigation (tab-order): */
+ pPage->setOrderAfter(m_pSelector->widget());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialog.h b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialog.h
new file mode 100644
index 00000000..9b463642
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialog.h
@@ -0,0 +1,266 @@
+/* $Id: UISettingsDialog.h $ */
+/** @file
+ * VBox Qt GUI - UISettingsDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_UISettingsDialog_h
+#define FEQT_INCLUDED_SRC_settings_UISettingsDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMainWindow>
+#include <QPointer>
+#include <QVariant>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UISettingsDefs.h"
+
+/* Forward declarations: */
+class QEvent;
+class QObject;
+class QLabel;
+class QProgressBar;
+class QShowEvent;
+class QStackedWidget;
+class QTimer;
+class QIDialogButtonBox;
+class UIPageValidator;
+class UISettingsPage;
+class UISettingsSelector;
+class UISettingsSerializer;
+class UIWarningPane;
+
+/* Using declarations: */
+using namespace UISettingsDefs;
+
+/** QMainWindow subclass used as
+ * base dialog class for both Global Preferences & Machine Settings
+ * dialogs, which encapsulates most of their common functionality. */
+class SHARED_LIBRARY_STUFF UISettingsDialog : public QIWithRetranslateUI<QMainWindow>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about dialog should be closed. */
+ void sigClose();
+
+public:
+
+ /** Dialog types. */
+ enum DialogType { DialogType_Global, DialogType_Machine };
+
+ /** Constructs settings dialog passing @a pParent to the base-class.
+ * @param strCategory Brings the name of category to be opened.
+ * @param strControl Brings the name of control to be focused. */
+ UISettingsDialog(QWidget *pParent,
+ const QString &strCategory,
+ const QString &strControl);
+ /** Destructs settings dialog. */
+ virtual ~UISettingsDialog() RT_OVERRIDE;
+
+ /** Returns dialog type. */
+ virtual DialogType dialogType() const = 0;
+
+ /** Loads the dialog data. */
+ virtual void load() = 0;
+ /** Saves the dialog data. */
+ virtual void save() = 0;
+
+protected slots:
+
+ /** Hides the modal dialog and sets the result code to Accepted. */
+ virtual void accept();
+ /** Hides the modal dialog and sets the result code to Rejected. */
+ virtual void reject();
+
+ /** Handles category change to @a cId. */
+ virtual void sltCategoryChanged(int cId);
+
+ /** Marks dialog loaded. */
+ virtual void sltMarkLoaded();
+ /** Marks dialog saved. */
+ virtual void sltMarkSaved();
+
+ /** Handles process start. */
+ void sltHandleProcessStarted();
+ /** Handles process progress change to @a iValue. */
+ void sltHandleProcessProgressChange(int iValue);
+
+protected:
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles first show @a pEvent. */
+ virtual void polishEvent(QShowEvent *pEvent);
+ /** Handles close @a pEvent. */
+ virtual void closeEvent(QCloseEvent *pEvent) RT_OVERRIDE;
+
+ /** Selects page and tab.
+ * @param fKeepPreviousByDefault Brings whether we should keep current page/tab by default. */
+ void choosePageAndTab(bool fKeepPreviousByDefault = false);
+
+ /** Loads the dialog @a data. */
+ void loadData(QVariant &data);
+ /** Saves the dialog @a data. */
+ void saveData(QVariant &data);
+
+ /** Returns configuration access level. */
+ ConfigurationAccessLevel configurationAccessLevel() const { return m_enmConfigurationAccessLevel; }
+ /** Defines configuration access level. */
+ void setConfigurationAccessLevel(ConfigurationAccessLevel enmConfigurationAccessLevel);
+
+ /** Returns the serialize process instance. */
+ UISettingsSerializer *serializeProcess() const { return m_pSerializeProcess; }
+ /** Returns whether the serialization is in progress. */
+ bool isSerializationInProgress() const { return m_fSerializationIsInProgress; }
+
+ /** Returns the dialog title extension. */
+ virtual QString titleExtension() const = 0;
+ /** Returns the dialog title. */
+ virtual QString title() const = 0;
+
+ /** Adds an item (page).
+ * @param strBigIcon Brings the big icon.
+ * @param strMediumIcon Brings the medium icon.
+ * @param strSmallIcon Brings the small icon.
+ * @param cId Brings the page ID.
+ * @param strLink Brings the page link.
+ * @param pSettingsPage Brings the page reference.
+ * @param iParentId Brings the page parent ID. */
+ void addItem(const QString &strBigIcon, const QString &strMediumIcon, const QString &strSmallIcon,
+ int cId, const QString &strLink,
+ UISettingsPage* pSettingsPage = 0, int iParentId = -1);
+
+ /** Verifies data integrity between certain @a pSettingsPage and other pages. */
+ virtual void recorrelate(UISettingsPage *pSettingsPage) { Q_UNUSED(pSettingsPage); }
+
+ /** Inserts an item to the map m_pageHelpKeywords. */
+ void addPageHelpKeyword(int iPageType, const QString &strHelpKeyword);
+
+ /** Validates data correctness using certain @a pValidator. */
+ void revalidate(UIPageValidator *pValidator);
+ /** Validates data correctness. */
+ void revalidate();
+
+ /** Returns whether settings were changed. */
+ bool isSettingsChanged();
+
+ /** Holds the name of category to be opened. */
+ QString m_strCategory;
+ /** Holds the name of control to be focused. */
+ QString m_strControl;
+
+ /** Holds the page selector instance. */
+ UISettingsSelector *m_pSelector;
+ /** Holds the page stack instance. */
+ QStackedWidget *m_pStack;
+
+private slots:
+
+ /** Handles validity change for certain @a pValidator. */
+ void sltHandleValidityChange(UIPageValidator *pValidator);
+
+ /** Handles hover enter for warning pane specified by @a pValidator. */
+ void sltHandleWarningPaneHovered(UIPageValidator *pValidator);
+ /** Handles hover leave for warning pane specified by @a pValidator. */
+ void sltHandleWarningPaneUnhovered(UIPageValidator *pValidator);
+
+ /** Updates watch this information depending on whether we have @a fGotFocus. */
+ void sltUpdateWhatsThis(bool fGotFocus);
+ /** Updates watch this information. */
+ void sltUpdateWhatsThisNoFocus() { sltUpdateWhatsThis(false); }
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Assigns validater for passed @a pPage. */
+ void assignValidator(UISettingsPage *pPage);
+
+ /** Holds configuration access level. */
+ ConfigurationAccessLevel m_enmConfigurationAccessLevel;
+
+ /** Holds the serialize process instance. */
+ UISettingsSerializer *m_pSerializeProcess;
+
+ /** Holds whether dialog is polished. */
+ bool m_fPolished;
+ /** Holds whether the serialization is in progress. */
+ bool m_fSerializationIsInProgress;
+ /** Holds whether there were no serialization errors. */
+ bool m_fSerializationClean;
+ /** Holds whether the dialod had emitted signal to be closed. */
+ bool m_fClosed;
+
+ /** Holds the status-bar widget instance. */
+ QStackedWidget *m_pStatusBar;
+ /** Holds the process-bar widget instance. */
+ QProgressBar *m_pProcessBar;
+ /** Holds the warning-pane instance. */
+ UIWarningPane *m_pWarningPane;
+
+ /** Holds whether settings dialog is valid (no errors, can be warnings). */
+ bool m_fValid;
+ /** Holds whether settings dialog is silent (no errors and no warnings). */
+ bool m_fSilent;
+
+ /** Holds the warning hint. */
+ QString m_strWarningHint;
+
+ /** Holds the what's this hover timer instance. */
+ QTimer *m_pWhatsThisTimer;
+ /** Holds the what's this hover timer instance. */
+ QPointer<QWidget> m_pWhatsThisCandidate;
+
+ /** Holds the map of settings pages. */
+ QMap<int, int> m_pages;
+ /** Stores the help tag per page. Key is the page type (either GlobalSettingsPageType or MachineSettingsPageType)
+ * and value is the help tag. Used in context sensitive help: */
+ QMap<int, QString> m_pageHelpKeywords;
+
+#ifdef VBOX_WS_MAC
+ /** Holds the list of settings page sizes for animation purposes. */
+ QList<QSize> m_sizeList;
+#endif
+
+ /** @name Widgets
+ * @{ */
+ QLabel *m_pLabelTitle;
+ QIDialogButtonBox *m_pButtonBox;
+ QWidget *m_pWidgetStackHandler;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_UISettingsDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialogSpecific.cpp b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialogSpecific.cpp
new file mode 100644
index 00000000..418e0383
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialogSpecific.cpp
@@ -0,0 +1,846 @@
+/* $Id: UISettingsDialogSpecific.cpp $ */
+/** @file
+ * VBox Qt GUI - UISettingsDialogSpecific class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStackedWidget>
+
+/* GUI includes: */
+#include "QIWidgetValidator.h"
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UISettingsDefs.h"
+#include "UISettingsDialogSpecific.h"
+#include "UISettingsSerializer.h"
+#include "UISettingsSelector.h"
+#include "UIVirtualBoxEventHandler.h"
+
+/* GUI includes: Global Preferences: */
+#include "UIGlobalSettingsDisplay.h"
+#include "UIGlobalSettingsGeneral.h"
+#include "UIGlobalSettingsInput.h"
+#include "UIGlobalSettingsLanguage.h"
+#ifdef VBOX_WS_WIN
+# include "UIGlobalSettingsInterface.h"
+#endif
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+# include "UIGlobalSettingsProxy.h"
+# include "UIGlobalSettingsUpdate.h"
+#endif
+
+/* GUI includes: Machine Settings: */
+#include "UIMachineSettingsAudio.h"
+#include "UIMachineSettingsDisplay.h"
+#include "UIMachineSettingsGeneral.h"
+#include "UIMachineSettingsInterface.h"
+#include "UIMachineSettingsNetwork.h"
+#include "UIMachineSettingsSerial.h"
+#include "UIMachineSettingsSF.h"
+#include "UIMachineSettingsStorage.h"
+#include "UIMachineSettingsSystem.h"
+#include "UIMachineSettingsUSB.h"
+
+/* COM includes: */
+#include "CExtPackManager.h"
+#include "CGraphicsAdapter.h"
+#include "CUSBController.h"
+
+#ifdef VBOX_WS_MAC
+# define VBOX_GUI_WITH_TOOLBAR_SETTINGS
+#endif
+
+
+/*********************************************************************************************************************************
+* Class UISettingsDialogGlobal implementation. *
+*********************************************************************************************************************************/
+
+UISettingsDialogGlobal::UISettingsDialogGlobal(QWidget *pParent,
+ const QString &strCategory /* = QString() */,
+ const QString &strControl /* = QString() */)
+ : UISettingsDialog(pParent, strCategory, strControl)
+{
+ prepare();
+}
+
+void UISettingsDialogGlobal::retranslateUi()
+{
+ /* Selector itself: */
+ m_pSelector->widget()->setWhatsThis(tr("Allows to navigate through Global Property categories"));
+
+ /* General page: */
+ m_pSelector->setItemText(GlobalSettingsPageType_General, tr("General"));
+
+ /* Input page: */
+ m_pSelector->setItemText(GlobalSettingsPageType_Input, tr("Input"));
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* Update page: */
+ m_pSelector->setItemText(GlobalSettingsPageType_Update, tr("Update"));
+#endif
+
+ /* Language page: */
+ m_pSelector->setItemText(GlobalSettingsPageType_Language, tr("Language"));
+
+ /* Display page: */
+ m_pSelector->setItemText(GlobalSettingsPageType_Display, tr("Display"));
+
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* Proxy page: */
+ m_pSelector->setItemText(GlobalSettingsPageType_Proxy, tr("Proxy"));
+#endif
+
+#ifdef VBOX_WS_WIN
+ /* Interface page: */
+ m_pSelector->setItemText(GlobalSettingsPageType_Interface, tr("Interface"));
+#endif
+
+ /* Polish the selector: */
+ m_pSelector->polish();
+
+ /* Base-class UI translation: */
+ UISettingsDialog::retranslateUi();
+
+ /* Set dialog's name: */
+ setWindowTitle(title());
+}
+
+void UISettingsDialogGlobal::load()
+{
+ /* Get host & properties: */
+ CHost comHost = uiCommon().host();
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ /* Prepare global data: */
+ qRegisterMetaType<UISettingsDataGlobal>();
+ UISettingsDataGlobal data(comHost, comProperties);
+ QVariant varData = QVariant::fromValue(data);
+
+ /* Call to base-class: */
+ UISettingsDialog::loadData(varData);
+}
+
+void UISettingsDialogGlobal::save()
+{
+ /* Get host & properties: */
+ CHost comHost = uiCommon().host();
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ /* Prepare global data: */
+ qRegisterMetaType<UISettingsDataGlobal>();
+ UISettingsDataGlobal data(comHost, comProperties);
+ QVariant varData = QVariant::fromValue(data);
+
+ /* Call to base-class: */
+ UISettingsDialog::saveData(varData);
+
+ /* Get updated host: */
+ CHost comNewHost = varData.value<UISettingsDataGlobal>().m_host;
+ /* If host is not OK => show the error: */
+ if (!comNewHost.isOk())
+ msgCenter().cannotSetHostSettings(comNewHost, this);
+
+ /* Get updated properties: */
+ CSystemProperties comNewProperties = varData.value<UISettingsDataGlobal>().m_properties;
+ /* If properties are not OK => show the error: */
+ if (!comNewProperties.isOk())
+ msgCenter().cannotSetSystemProperties(comNewProperties, this);
+
+ /* Mark as saved: */
+ sltMarkSaved();
+}
+
+QString UISettingsDialogGlobal::titleExtension() const
+{
+#ifdef VBOX_GUI_WITH_TOOLBAR_SETTINGS
+ return m_pSelector->itemText(m_pSelector->currentId());
+#else
+ return tr("Preferences");
+#endif
+}
+
+QString UISettingsDialogGlobal::title() const
+{
+ return tr("VirtualBox - %1").arg(titleExtension());
+}
+
+void UISettingsDialogGlobal::prepare()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/global_settings_32px.png", ":/global_settings_16px.png"));
+#endif
+
+ /* Creating settings pages: */
+ QList<GlobalSettingsPageType> restrictedGlobalSettingsPages = gEDataManager->restrictedGlobalSettingsPages();
+ for (int iPageIndex = GlobalSettingsPageType_General; iPageIndex < GlobalSettingsPageType_Max; ++iPageIndex)
+ {
+ /* Make sure page was not restricted: */
+ if (restrictedGlobalSettingsPages.contains(static_cast<GlobalSettingsPageType>(iPageIndex)))
+ continue;
+
+ /* Make sure page is available: */
+ if (isPageAvailable(iPageIndex))
+ {
+ UISettingsPage *pSettingsPage = 0;
+ switch (iPageIndex)
+ {
+ /* General page: */
+ case GlobalSettingsPageType_General:
+ {
+ pSettingsPage = new UIGlobalSettingsGeneral;
+ addItem(":/machine_32px.png", ":/machine_24px.png", ":/machine_16px.png",
+ iPageIndex, "#general", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "preferences");
+ break;
+ }
+ /* Input page: */
+ case GlobalSettingsPageType_Input:
+ {
+ pSettingsPage = new UIGlobalSettingsInput;
+ addItem(":/keyboard_32px.png", ":/keyboard_24px.png", ":/keyboard_16px.png",
+ iPageIndex, "#input", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "preferences");
+ break;
+ }
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* Update page: */
+ case GlobalSettingsPageType_Update:
+ {
+ pSettingsPage = new UIGlobalSettingsUpdate;
+ addItem(":/refresh_32px.png", ":/refresh_24px.png", ":/refresh_16px.png",
+ iPageIndex, "#update", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "preferences");
+ break;
+ }
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+ /* Language page: */
+ case GlobalSettingsPageType_Language:
+ {
+ pSettingsPage = new UIGlobalSettingsLanguage;
+ addItem(":/site_32px.png", ":/site_24px.png", ":/site_16px.png",
+ iPageIndex, "#language", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "preferences");
+ break;
+ }
+ /* Display page: */
+ case GlobalSettingsPageType_Display:
+ {
+ pSettingsPage = new UIGlobalSettingsDisplay;
+ addItem(":/vrdp_32px.png", ":/vrdp_24px.png", ":/vrdp_16px.png",
+ iPageIndex, "#display", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "preferences");
+ break;
+ }
+#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
+ /* Proxy page: */
+ case GlobalSettingsPageType_Proxy:
+ {
+ pSettingsPage = new UIGlobalSettingsProxy;
+ addItem(":/proxy_32px.png", ":/proxy_24px.png", ":/proxy_16px.png",
+ iPageIndex, "#proxy", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "preferences");
+ break;
+ }
+#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
+#ifdef VBOX_WS_WIN
+ /* Interface page: */
+ case GlobalSettingsPageType_Interface:
+ {
+ pSettingsPage = new UIGlobalSettingsInterface;
+ addItem(":/interface_32px.png", ":/interface_24px.png", ":/interface_16px.png",
+ iPageIndex, "#userInterface", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "preferences");
+ break;
+ }
+#endif /* VBOX_WS_WIN */
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Assign default (full) configuration access level: */
+ setConfigurationAccessLevel(ConfigurationAccessLevel_Full);
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Choose page/tab finally: */
+ choosePageAndTab();
+}
+
+bool UISettingsDialogGlobal::isPageAvailable(int) const
+{
+ /* Add restrictions here.. */
+ return true;
+}
+
+
+/*********************************************************************************************************************************
+* Class UISettingsDialogMachine implementation. *
+*********************************************************************************************************************************/
+
+UISettingsDialogMachine::UISettingsDialogMachine(QWidget *pParent,
+ const QUuid &uMachineId,
+ UIActionPool *pActionPool,
+ const QString &strCategory /* = QString() */,
+ const QString &strControl /* = QString() */)
+ : UISettingsDialog(pParent, strCategory, strControl)
+ , m_uMachineId(uMachineId)
+ , m_pActionPool(pActionPool)
+{
+ prepare();
+}
+
+void UISettingsDialogMachine::setNewMachineId(const QUuid &uMachineId,
+ const QString &strCategory /* = QString() */,
+ const QString &strControl /* = QString() */)
+{
+ /* Cache new machine stuff: */
+ m_uMachineId = uMachineId;
+ m_strCategory = strCategory;
+ m_strControl = strControl;
+
+ /* Get corresponding machine (required to determine dialog type and page availability): */
+ m_machine = uiCommon().virtualBox().FindMachine(m_uMachineId.toString());
+ AssertReturnVoid(!m_machine.isNull());
+ m_enmSessionState = m_machine.GetSessionState();
+ m_enmMachineState = m_machine.GetState();
+
+ /* Calculate initial configuration access level: */
+ setConfigurationAccessLevel(::configurationAccessLevel(m_enmSessionState, m_enmMachineState));
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Choose page/tab: */
+ choosePageAndTab(true /* keep previous by default */);
+
+ /* Load finally: */
+ load();
+}
+
+void UISettingsDialogMachine::retranslateUi()
+{
+ /* Selector itself: */
+ m_pSelector->widget()->setWhatsThis(tr("Allows to navigate through VM Settings categories"));
+
+ /* We have to make sure that the Network, Serial pages are retranslated
+ * before they are revalidated. Cause: They do string comparing within
+ * UICommon which is retranslated at that point already: */
+ QEvent event(QEvent::LanguageChange);
+ if (QWidget *pPage = m_pSelector->idToPage(MachineSettingsPageType_Network))
+ qApp->sendEvent(pPage, &event);
+ if (QWidget *pPage = m_pSelector->idToPage(MachineSettingsPageType_Serial))
+ qApp->sendEvent(pPage, &event);
+
+ /* General page: */
+ m_pSelector->setItemText(MachineSettingsPageType_General, tr("General"));
+
+ /* System page: */
+ m_pSelector->setItemText(MachineSettingsPageType_System, tr("System"));
+
+ /* Display page: */
+ m_pSelector->setItemText(MachineSettingsPageType_Display, tr("Display"));
+
+ /* Storage page: */
+ m_pSelector->setItemText(MachineSettingsPageType_Storage, tr("Storage"));
+
+ /* Audio page: */
+ m_pSelector->setItemText(MachineSettingsPageType_Audio, tr("Audio"));
+
+ /* Network page: */
+ m_pSelector->setItemText(MachineSettingsPageType_Network, tr("Network"));
+
+ /* Ports page: */
+ m_pSelector->setItemText(MachineSettingsPageType_Ports, tr("Ports"));
+
+ /* Serial page: */
+ m_pSelector->setItemText(MachineSettingsPageType_Serial, tr("Serial Ports"));
+
+ /* USB page: */
+ m_pSelector->setItemText(MachineSettingsPageType_USB, tr("USB"));
+
+ /* SFolders page: */
+ m_pSelector->setItemText(MachineSettingsPageType_SF, tr("Shared Folders"));
+
+ /* Interface page: */
+ m_pSelector->setItemText(MachineSettingsPageType_Interface, tr("User Interface"));
+
+ /* Polish the selector: */
+ m_pSelector->polish();
+
+ /* Base-class UI translation: */
+ UISettingsDialog::retranslateUi();
+
+ /* Set dialog's name: */
+ setWindowTitle(title());
+}
+
+void UISettingsDialogMachine::load()
+{
+ /* Check that session is NOT created: */
+ if (!m_session.isNull())
+ return;
+
+ /* Prepare session: */
+ m_session = configurationAccessLevel() == ConfigurationAccessLevel_Null ? CSession() :
+ configurationAccessLevel() == ConfigurationAccessLevel_Full ? uiCommon().openSession(m_uMachineId) :
+ uiCommon().openExistingSession(m_uMachineId);
+ /* Check that session was created: */
+ if (m_session.isNull())
+ return;
+
+ /* Get machine and console: */
+ m_machine = m_session.GetMachine();
+ m_console = configurationAccessLevel() == ConfigurationAccessLevel_Full ? CConsole() : m_session.GetConsole();
+ /* Prepare machine data: */
+ qRegisterMetaType<UISettingsDataMachine>();
+ UISettingsDataMachine data(m_machine, m_console);
+ QVariant varData = QVariant::fromValue(data);
+
+ /* Call to base-class: */
+ UISettingsDialog::loadData(varData);
+}
+
+void UISettingsDialogMachine::save()
+{
+ /* Check that session is NOT created: */
+ if (!m_session.isNull())
+ return;
+
+ /* Prepare session: */
+ m_session = configurationAccessLevel() == ConfigurationAccessLevel_Null ? CSession() :
+ configurationAccessLevel() == ConfigurationAccessLevel_Full ? uiCommon().openSession(m_uMachineId) :
+ uiCommon().openExistingSession(m_uMachineId);
+ /* Check that session was created: */
+ if (m_session.isNull())
+ return;
+
+ /* Get machine and console: */
+ m_machine = m_session.GetMachine();
+ m_console = configurationAccessLevel() == ConfigurationAccessLevel_Full ? CConsole() : m_session.GetConsole();
+ /* Prepare machine data: */
+ qRegisterMetaType<UISettingsDataMachine>();
+ UISettingsDataMachine data(m_machine, m_console);
+ QVariant varData = QVariant::fromValue(data);
+
+ /* Call to base-class: */
+ UISettingsDialog::saveData(varData);
+
+ /* Get updated machine: */
+ m_machine = varData.value<UISettingsDataMachine>().m_machine;
+ /* If machine is OK => perform final operations: */
+ if (m_machine.isOk())
+ {
+ UIMachineSettingsSystem *pSystemPage =
+ qobject_cast<UIMachineSettingsSystem*>(m_pSelector->idToPage(MachineSettingsPageType_System));
+#ifdef VBOX_WITH_3D_ACCELERATION
+ UIMachineSettingsDisplay *pDisplayPage =
+ qobject_cast<UIMachineSettingsDisplay*>(m_pSelector->idToPage(MachineSettingsPageType_Display));
+#endif /* VBOX_WITH_3D_ACCELERATION */
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /* Adjust graphics controller type if necessary: */
+ if ( pDisplayPage
+ && pDisplayPage->isAcceleration3DSelected()
+ && pDisplayPage->graphicsControllerTypeCurrent() != pDisplayPage->graphicsControllerTypeRecommended())
+ m_machine.GetGraphicsAdapter().SetGraphicsControllerType(pDisplayPage->graphicsControllerTypeRecommended());
+#endif /* VBOX_WITH_3D_ACCELERATION */
+
+ /* Enable OHCI controller if HID is enabled but no USB controllers present: */
+ if (pSystemPage && pSystemPage->isHIDEnabled() && m_machine.GetUSBControllers().isEmpty())
+ m_machine.AddUSBController("OHCI", KUSBControllerType_OHCI);
+
+ /* Save settings finally: */
+ m_machine.SaveSettings();
+ }
+
+ /* If machine is NOT OK => show the error message: */
+ if (!m_machine.isOk())
+ msgCenter().cannotSaveMachineSettings(m_machine, this);
+
+ /* Mark as saved: */
+ sltMarkSaved();
+}
+
+QString UISettingsDialogMachine::titleExtension() const
+{
+#ifdef VBOX_GUI_WITH_TOOLBAR_SETTINGS
+ return m_pSelector->itemText(m_pSelector->currentId());
+#else
+ return tr("Settings");
+#endif
+}
+
+QString UISettingsDialogMachine::title() const
+{
+ QString strDialogTitle;
+ /* Get corresponding machine (required to compose dialog title): */
+ const CMachine &machine = uiCommon().virtualBox().FindMachine(m_uMachineId.toString());
+ if (!machine.isNull())
+ strDialogTitle = tr("%1 - %2").arg(machine.GetName()).arg(titleExtension());
+ return strDialogTitle;
+}
+
+void UISettingsDialogMachine::recorrelate(UISettingsPage *pSettingsPage)
+{
+ switch (pSettingsPage->id())
+ {
+ /* General page correlations: */
+ case MachineSettingsPageType_General:
+ {
+ /* Make changes on 'general' page influent 'display' page: */
+ UIMachineSettingsGeneral *pGeneralPage = qobject_cast<UIMachineSettingsGeneral*>(pSettingsPage);
+ UIMachineSettingsDisplay *pDisplayPage = qobject_cast<UIMachineSettingsDisplay*>(m_pSelector->idToPage(MachineSettingsPageType_Display));
+ if (pGeneralPage && pDisplayPage)
+ pDisplayPage->setGuestOSType(pGeneralPage->guestOSType());
+ break;
+ }
+ /* System page correlations: */
+ case MachineSettingsPageType_System:
+ {
+ /* Make changes on 'system' page influent 'general' and 'storage' page: */
+ UIMachineSettingsSystem *pSystemPage = qobject_cast<UIMachineSettingsSystem*>(pSettingsPage);
+ UIMachineSettingsStorage *pStoragePage = qobject_cast<UIMachineSettingsStorage*>(m_pSelector->idToPage(MachineSettingsPageType_Storage));
+ if (pSystemPage && pStoragePage)
+ pStoragePage->setChipsetType(pSystemPage->chipsetType());
+ break;
+ }
+ /* USB page correlations: */
+ case MachineSettingsPageType_USB:
+ {
+ /* Make changes on 'usb' page influent 'system' page: */
+ UIMachineSettingsUSB *pUsbPage = qobject_cast<UIMachineSettingsUSB*>(pSettingsPage);
+ UIMachineSettingsSystem *pSystemPage = qobject_cast<UIMachineSettingsSystem*>(m_pSelector->idToPage(MachineSettingsPageType_System));
+ if (pUsbPage && pSystemPage)
+ pSystemPage->setUSBEnabled(pUsbPage->isUSBEnabled());
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void UISettingsDialogMachine::sltCategoryChanged(int cId)
+{
+ /* Raise priority of requested page: */
+ if (serializeProcess())
+ serializeProcess()->raisePriorityOfPage(cId);
+
+ /* Call to base-class: */
+ UISettingsDialog::sltCategoryChanged(cId);
+}
+
+void UISettingsDialogMachine::sltMarkLoaded()
+{
+ /* Call for base-class: */
+ UISettingsDialog::sltMarkLoaded();
+
+ /* Unlock the session if exists: */
+ if (!m_session.isNull())
+ {
+ m_session.UnlockMachine();
+ m_session = CSession();
+ m_machine = CMachine();
+ m_console = CConsole();
+ }
+}
+
+void UISettingsDialogMachine::sltMarkSaved()
+{
+ /* Call for base-class: */
+ UISettingsDialog::sltMarkSaved();
+
+ /* Unlock the session if exists: */
+ if (!m_session.isNull())
+ {
+ m_session.UnlockMachine();
+ m_session = CSession();
+ m_machine = CMachine();
+ m_console = CConsole();
+ }
+}
+
+void UISettingsDialogMachine::sltSessionStateChanged(const QUuid &uMachineId, const KSessionState enmSessionState)
+{
+ /* Ignore if serialization is in progress: */
+ if (isSerializationInProgress())
+ return;
+ /* Ignore if thats NOT our VM: */
+ if (uMachineId != m_uMachineId)
+ return;
+
+ /* Ignore if state was NOT actually changed: */
+ if (m_enmSessionState == enmSessionState)
+ return;
+ /* Update current session state: */
+ m_enmSessionState = enmSessionState;
+
+ /* Recalculate configuration access level: */
+ updateConfigurationAccessLevel();
+}
+
+void UISettingsDialogMachine::sltMachineStateChanged(const QUuid &uMachineId, const KMachineState enmMachineState)
+{
+ /* Ignore if serialization is in progress: */
+ if (isSerializationInProgress())
+ return;
+ /* Ignore if thats NOT our VM: */
+ if (uMachineId != m_uMachineId)
+ return;
+
+ /* Ignore if state was NOT actually changed: */
+ if (m_enmMachineState == enmMachineState)
+ return;
+ /* Update current machine state: */
+ m_enmMachineState = enmMachineState;
+
+ /* Recalculate configuration access level: */
+ updateConfigurationAccessLevel();
+}
+
+void UISettingsDialogMachine::sltMachineDataChanged(const QUuid &uMachineId)
+{
+ /* Ignore if serialization is in progress: */
+ if (isSerializationInProgress())
+ return;
+ /* Ignore if thats NOT our VM: */
+ if (uMachineId != m_uMachineId)
+ return;
+
+ /* Check if user had changed something and warn him about he will loose settings on reloading: */
+ if (isSettingsChanged() && !msgCenter().confirmSettingsReloading(this))
+ return;
+
+ /* Reload data: */
+ load();
+}
+
+void UISettingsDialogMachine::prepare()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/vm_settings_32px.png", ":/vm_settings_16px.png"));
+#endif
+
+ /* Make sure settings window will be updated on session/machine state/data changes: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSessionStateChange,
+ this, &UISettingsDialogMachine::sltSessionStateChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UISettingsDialogMachine::sltMachineStateChanged);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange,
+ this, &UISettingsDialogMachine::sltMachineDataChanged);
+
+ /* Get corresponding machine (required to determine dialog type and page availability): */
+ m_machine = uiCommon().virtualBox().FindMachine(m_uMachineId.toString());
+ AssertMsg(!m_machine.isNull(), ("Can't find corresponding machine!\n"));
+ m_enmSessionState = m_machine.GetSessionState();
+ m_enmMachineState = m_machine.GetState();
+
+ /* Creating settings pages: */
+ QList<MachineSettingsPageType> restrictedMachineSettingsPages = gEDataManager->restrictedMachineSettingsPages(m_uMachineId);
+ for (int iPageIndex = MachineSettingsPageType_General; iPageIndex < MachineSettingsPageType_Max; ++iPageIndex)
+ {
+ /* Make sure page was not restricted: */
+ if (restrictedMachineSettingsPages.contains(static_cast<MachineSettingsPageType>(iPageIndex)))
+ continue;
+
+ /* Make sure page is available: */
+ if (isPageAvailable(iPageIndex))
+ {
+ UISettingsPage *pSettingsPage = 0;
+ switch (iPageIndex)
+ {
+ /* General page: */
+ case MachineSettingsPageType_General:
+ {
+ pSettingsPage = new UIMachineSettingsGeneral;
+ addItem(":/machine_32px.png", ":/machine_24px.png", ":/machine_16px.png",
+ iPageIndex, "#general", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "generalsettings");
+ break;
+ }
+ /* System page: */
+ case MachineSettingsPageType_System:
+ {
+ pSettingsPage = new UIMachineSettingsSystem;
+ addItem(":/chipset_32px.png", ":/chipset_24px.png", ":/chipset_16px.png",
+ iPageIndex, "#system", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "settings-system");
+ break;
+ }
+ /* Display page: */
+ case MachineSettingsPageType_Display:
+ {
+ pSettingsPage = new UIMachineSettingsDisplay;
+ addItem(":/vrdp_32px.png", ":/vrdp_24px.png", ":/vrdp_16px.png",
+ iPageIndex, "#display", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "settings-display");
+ break;
+ }
+ /* Storage page: */
+ case MachineSettingsPageType_Storage:
+ {
+ pSettingsPage = new UIMachineSettingsStorage(m_pActionPool);
+ addItem(":/hd_32px.png", ":/hd_24px.png", ":/hd_16px.png",
+ iPageIndex, "#storage", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "settings-storage");
+ break;
+ }
+ /* Audio page: */
+ case MachineSettingsPageType_Audio:
+ {
+ pSettingsPage = new UIMachineSettingsAudio;
+ addItem(":/sound_32px.png", ":/sound_24px.png", ":/sound_16px.png",
+ iPageIndex, "#audio", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "settings-audio");
+ break;
+ }
+ /* Network page: */
+ case MachineSettingsPageType_Network:
+ {
+ pSettingsPage = new UIMachineSettingsNetworkPage;
+ addItem(":/nw_32px.png", ":/nw_24px.png", ":/nw_16px.png",
+ iPageIndex, "#network", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "settings-network");
+ break;
+ }
+ /* Ports page: */
+ case MachineSettingsPageType_Ports:
+ {
+ addItem(":/serial_port_32px.png", ":/serial_port_24px.png", ":/serial_port_16px.png",
+ iPageIndex, "#ports");
+ break;
+ }
+ /* Serial page: */
+ case MachineSettingsPageType_Serial:
+ {
+ pSettingsPage = new UIMachineSettingsSerialPage;
+ addItem(":/serial_port_32px.png", ":/serial_port_24px.png", ":/serial_port_16px.png",
+ iPageIndex, "#serialPorts", pSettingsPage, MachineSettingsPageType_Ports);
+ addPageHelpKeyword(iPageIndex, "serialports");
+ break;
+ }
+ /* USB page: */
+ case MachineSettingsPageType_USB:
+ {
+ pSettingsPage = new UIMachineSettingsUSB;
+ addItem(":/usb_32px.png", ":/usb_24px.png", ":/usb_16px.png",
+ iPageIndex, "#usb", pSettingsPage, MachineSettingsPageType_Ports);
+ addPageHelpKeyword(iPageIndex, "usb-support");
+ break;
+ }
+ /* Shared Folders page: */
+ case MachineSettingsPageType_SF:
+ {
+ pSettingsPage = new UIMachineSettingsSF;
+ addItem(":/sf_32px.png", ":/sf_24px.png", ":/sf_16px.png",
+ iPageIndex, "#sharedFolders", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "shared-folders");
+ break;
+ }
+ /* Interface page: */
+ case MachineSettingsPageType_Interface:
+ {
+ pSettingsPage = new UIMachineSettingsInterface(m_machine.GetId());
+ addItem(":/interface_32px.png", ":/interface_24px.png", ":/interface_16px.png",
+ iPageIndex, "#userInterface", pSettingsPage);
+ addPageHelpKeyword(iPageIndex, "user-interface");
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Calculate initial configuration access level: */
+ setConfigurationAccessLevel(::configurationAccessLevel(m_enmSessionState, m_enmMachineState));
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Choose page/tab finally: */
+ choosePageAndTab();
+}
+
+bool UISettingsDialogMachine::isPageAvailable(int iPageId) const
+{
+ if (m_machine.isNull())
+ return false;
+
+ switch (iPageId)
+ {
+ case MachineSettingsPageType_Serial:
+ {
+ /* Depends on ports availability: */
+ if (!isPageAvailable(MachineSettingsPageType_Ports))
+ return false;
+ break;
+ }
+ case MachineSettingsPageType_USB:
+ {
+ /* Depends on ports availability: */
+ if (!isPageAvailable(MachineSettingsPageType_Ports))
+ return false;
+ /* Check if USB is implemented: */
+ if (!m_machine.GetUSBProxyAvailable())
+ return false;
+ /* Get the USB controller object: */
+ CUSBControllerVector controllerColl = m_machine.GetUSBControllers();
+ /* Show the machine error message if any: */
+ if ( !m_machine.isReallyOk()
+ && controllerColl.size() > 0
+ && !m_machine.GetUSBControllers().isEmpty())
+ msgCenter().warnAboutUnaccessibleUSB(m_machine, parentWidget());
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+}
+
+void UISettingsDialogMachine::updateConfigurationAccessLevel()
+{
+ /* Determine new configuration access level: */
+ const ConfigurationAccessLevel newConfigurationAccessLevel = ::configurationAccessLevel(m_enmSessionState, m_enmMachineState);
+
+ /* Make sure someting changed: */
+ if (configurationAccessLevel() == newConfigurationAccessLevel)
+ return;
+
+ /* Should we warn a user about access level decrease? */
+ const bool fShouldWeWarn = configurationAccessLevel() == ConfigurationAccessLevel_Full;
+
+ /* Apply new configuration access level: */
+ setConfigurationAccessLevel(newConfigurationAccessLevel);
+
+ /* Show a warning about access level decrease if we should: */
+ if (isSettingsChanged() && fShouldWeWarn)
+ msgCenter().warnAboutStateChange(this);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialogSpecific.h b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialogSpecific.h
new file mode 100644
index 00000000..11be884f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsDialogSpecific.h
@@ -0,0 +1,193 @@
+/* $Id: UISettingsDialogSpecific.h $ */
+/** @file
+ * VBox Qt GUI - UISettingsDialogSpecific class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_UISettingsDialogSpecific_h
+#define FEQT_INCLUDED_SRC_settings_UISettingsDialogSpecific_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPointer>
+
+/* GUI includes: */
+#include "UISettingsDialog.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CConsole.h"
+#include "CMachine.h"
+#include "CSession.h"
+
+/* Forward declarations: */
+class UIActionPool;
+
+/** UISettingsDialog extension encapsulating all the specific functionality of the Global Preferences. */
+class SHARED_LIBRARY_STUFF UISettingsDialogGlobal : public UISettingsDialog
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs settings dialog passing @a pParent to the base-class.
+ * @param strCategory Brings the name of category to be opened.
+ * @param strControl Brings the name of control to be focused. */
+ UISettingsDialogGlobal(QWidget *pParent,
+ const QString &strCategory = QString(),
+ const QString &strControl = QString());
+
+ /** Returns dialog type. */
+ virtual DialogType dialogType() const { return DialogType_Global; }
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Loads the dialog data. */
+ virtual void load() RT_OVERRIDE;
+ /** Saves the dialog data. */
+ virtual void save() RT_OVERRIDE;
+
+ /** Returns the dialog title extension. */
+ virtual QString titleExtension() const RT_OVERRIDE;
+ /** Returns the dialog title. */
+ virtual QString title() const RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Returns whether page with certain @a iPageId is available. */
+ bool isPageAvailable(int iPageId) const;
+};
+
+/** Safe pointer to cloud machine settings dialog. */
+typedef QPointer<UISettingsDialogGlobal> UISafePointerSettingsDialogGlobal;
+
+
+/** UISettingsDialog extension encapsulating all the specific functionality of the Machine Settings. */
+class SHARED_LIBRARY_STUFF UISettingsDialogMachine : public UISettingsDialog
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs settings dialog passing @a pParent to the base-class.
+ * @param uMachineId Brings the machine ID.
+ * @param pActionPool Brings the action pool instance.
+ * @param strCategory Brings the name of category to be opened.
+ * @param strControl Brings the name of control to be focused. */
+ UISettingsDialogMachine(QWidget *pParent,
+ const QUuid &uMachineId,
+ UIActionPool *pActionPool,
+ const QString &strCategory = QString(),
+ const QString &strControl = QString());
+
+ /** Returns dialog type. */
+ virtual DialogType dialogType() const { return DialogType_Machine; }
+
+ /** Update machine stuff.
+ * @param uMachineId Brings the machine ID.
+ * @param strCategory Brings the name of category to be opened.
+ * @param strControl Brings the name of control to be focused. */
+ void setNewMachineId(const QUuid &uMachineId,
+ const QString &strCategory = QString(),
+ const QString &strControl = QString());
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Loads the dialog data. */
+ virtual void load() RT_OVERRIDE;
+ /** Saves the dialog data. */
+ virtual void save() RT_OVERRIDE;
+
+ /** Returns the dialog title extension. */
+ virtual QString titleExtension() const RT_OVERRIDE;
+ /** Returns the dialog title. */
+ virtual QString title() const RT_OVERRIDE;
+
+ /** Verifies data integrity between certain @a pSettingsPage and other pages. */
+ virtual void recorrelate(UISettingsPage *pSettingsPage) RT_OVERRIDE;
+
+protected slots:
+
+ /** Handles category change to @a cId. */
+ virtual void sltCategoryChanged(int cId) RT_OVERRIDE;
+
+ /** Marks dialog loaded. */
+ virtual void sltMarkLoaded() RT_OVERRIDE;
+ /** Marks dialog saved. */
+ virtual void sltMarkSaved() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles session state change for machine with certain @a uMachineId to @a enmSessionState. */
+ void sltSessionStateChanged(const QUuid &uMachineId, const KSessionState enmSessionState);
+ /** Handles machine state change for machine with certain @a uMachineId to @a enmMachineState. */
+ void sltMachineStateChanged(const QUuid &uMachineId, const KMachineState enmMachineState);
+ /** Handles machine data change for machine with certain @a uMachineId. */
+ void sltMachineDataChanged(const QUuid &uMachineId);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Returns whether page with certain @a iPageId is available. */
+ bool isPageAvailable(int iPageId) const;
+
+ /** Recalculates configuration access level. */
+ void updateConfigurationAccessLevel();
+
+ /** Holds the machine ID. */
+ QUuid m_uMachineId;
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+
+ /** Holds the session state. */
+ KSessionState m_enmSessionState;
+ /** Holds the machine state. */
+ KMachineState m_enmMachineState;
+
+ /** Holds the session reference. */
+ CSession m_session;
+ /** Holds the machine reference. */
+ CMachine m_machine;
+ /** Holds the console reference. */
+ CConsole m_console;
+};
+
+/** Safe pointer to cloud machine settings dialog. */
+typedef QPointer<UISettingsDialogMachine> UISafePointerSettingsDialogMachine;
+
+
+#endif /* !FEQT_INCLUDED_SRC_settings_UISettingsDialogSpecific_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsPage.cpp b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsPage.cpp
new file mode 100644
index 00000000..81458fc3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsPage.cpp
@@ -0,0 +1,153 @@
+/* $Id: UISettingsPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UISettingsPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIConverter.h"
+#include "UISettingsPage.h"
+#include "QIWidgetValidator.h"
+
+
+/*********************************************************************************************************************************
+* Class UISettingsPage implementation. *
+*********************************************************************************************************************************/
+
+UISettingsPage::UISettingsPage()
+ : m_enmConfigurationAccessLevel(ConfigurationAccessLevel_Null)
+ , m_cId(-1)
+ , m_pFirstWidget(0)
+ , m_pValidator(0)
+ , m_fIsValidatorBlocked(true)
+ , m_fProcessed(false)
+ , m_fFailed(false)
+{
+}
+
+void UISettingsPage::notifyOperationProgressError(const QString &strErrorInfo)
+{
+ QMetaObject::invokeMethod(this,
+ "sigOperationProgressError",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(QString, strErrorInfo));
+}
+
+void UISettingsPage::setValidator(UIPageValidator *pValidator)
+{
+ /* Make sure validator is not yet assigned: */
+ AssertMsg(!m_pValidator, ("Validator already assigned!\n"));
+ if (m_pValidator)
+ return;
+
+ /* Assign validator: */
+ m_pValidator = pValidator;
+}
+
+void UISettingsPage::setConfigurationAccessLevel(ConfigurationAccessLevel enmConfigurationAccessLevel)
+{
+ m_enmConfigurationAccessLevel = enmConfigurationAccessLevel;
+ polishPage();
+}
+
+void UISettingsPage::revalidate()
+{
+ /* Revalidate if possible: */
+ if (m_pValidator && !m_fIsValidatorBlocked)
+ m_pValidator->revalidate();
+}
+
+
+/*********************************************************************************************************************************
+* Class UISettingsPageGlobal implementation. *
+*********************************************************************************************************************************/
+
+UISettingsPageGlobal::UISettingsPageGlobal()
+{
+}
+
+GlobalSettingsPageType UISettingsPageGlobal::internalID() const
+{
+ return static_cast<GlobalSettingsPageType>(id());
+}
+
+QString UISettingsPageGlobal::internalName() const
+{
+ return gpConverter->toInternalString(internalID());
+}
+
+QPixmap UISettingsPageGlobal::warningPixmap() const
+{
+ return gpConverter->toWarningPixmap(internalID());
+}
+
+void UISettingsPageGlobal::fetchData(const QVariant &data)
+{
+ /* Fetch data to m_host & m_properties: */
+ m_host = data.value<UISettingsDataGlobal>().m_host;
+ m_properties = data.value<UISettingsDataGlobal>().m_properties;
+}
+
+void UISettingsPageGlobal::uploadData(QVariant &data) const
+{
+ /* Upload m_host & m_properties to data: */
+ data = QVariant::fromValue(UISettingsDataGlobal(m_host, m_properties));
+}
+
+
+/*********************************************************************************************************************************
+* Class UISettingsPageMachine implementation. *
+*********************************************************************************************************************************/
+
+UISettingsPageMachine::UISettingsPageMachine()
+{
+}
+
+MachineSettingsPageType UISettingsPageMachine::internalID() const
+{
+ return static_cast<MachineSettingsPageType>(id());
+}
+
+QString UISettingsPageMachine::internalName() const
+{
+ return gpConverter->toInternalString(internalID());
+}
+
+QPixmap UISettingsPageMachine::warningPixmap() const
+{
+ return gpConverter->toWarningPixmap(internalID());
+}
+
+void UISettingsPageMachine::fetchData(const QVariant &data)
+{
+ /* Fetch data to m_machine & m_console: */
+ m_machine = data.value<UISettingsDataMachine>().m_machine;
+ m_console = data.value<UISettingsDataMachine>().m_console;
+}
+
+void UISettingsPageMachine::uploadData(QVariant &data) const
+{
+ /* Upload m_machine & m_console to data: */
+ data = QVariant::fromValue(UISettingsDataMachine(m_machine, m_console));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsPage.h b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsPage.h
new file mode 100644
index 00000000..baef9ec2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsPage.h
@@ -0,0 +1,287 @@
+/* $Id: UISettingsPage.h $ */
+/** @file
+ * VBox Qt GUI - UISettingsPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_UISettingsPage_h
+#define FEQT_INCLUDED_SRC_settings_UISettingsPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QVariant>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UISettingsDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CConsole.h"
+#include "CHost.h"
+#include "CMachine.h"
+#include "CSystemProperties.h"
+
+/* Forward declarations: */
+class QShowEvent;
+class QString;
+class QVariant;
+class QWidget;
+class UIPageValidator;
+
+/* Using declarations: */
+using namespace UISettingsDefs;
+
+
+/** Global settings data wrapper. */
+struct UISettingsDataGlobal
+{
+ /** Constructs NULL global settings data struct. */
+ UISettingsDataGlobal() {}
+ /** Constructs global settings data struct on the basis of @a comHost and @a comProperties. */
+ UISettingsDataGlobal(const CHost &comHost, const CSystemProperties &comProperties)
+ : m_host(comHost), m_properties(comProperties) {}
+ /** Holds the host reference. */
+ CHost m_host;
+ /** Holds the properties reference. */
+ CSystemProperties m_properties;
+};
+Q_DECLARE_METATYPE(UISettingsDataGlobal);
+
+
+/** Machine settings data wrapper. */
+struct UISettingsDataMachine
+{
+ /** Constructs NULL machine settings data struct. */
+ UISettingsDataMachine() {}
+ /** Constructs machine settings data struct on the basis of @a comMachine and @a comConsole. */
+ UISettingsDataMachine(const CMachine &comMachine, const CConsole &comConsole)
+ : m_machine(comMachine), m_console(comConsole) {}
+ /** Holds the machine reference. */
+ CMachine m_machine;
+ /** Holds the console reference. */
+ CConsole m_console;
+};
+Q_DECLARE_METATYPE(UISettingsDataMachine);
+
+
+/** Validation message. */
+typedef QPair<QString, QStringList> UIValidationMessage;
+
+
+/** QWidget subclass used as settings page interface. */
+class SHARED_LIBRARY_STUFF UISettingsPage : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about particular operation progress change.
+ * @param iOperations holds the number of operations CProgress have,
+ * @param strOperation holds the description of the current CProgress operation,
+ * @param iOperation holds the index of the current CProgress operation,
+ * @param iPercent holds the percentage of the current CProgress operation. */
+ void sigOperationProgressChange(ulong iOperations, QString strOperation,
+ ulong iOperation, ulong iPercent);
+
+ /** Notifies listeners about particular COM error.
+ * @param strErrorInfo holds the details of the error happened. */
+ void sigOperationProgressError(QString strErrorInfo);
+
+public:
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) = 0;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() = 0;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() = 0;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) = 0;
+
+ /** Notifies listeners about particular COM error.
+ * @param strErrorInfo Brings the details of the error happened. */
+ void notifyOperationProgressError(const QString &strErrorInfo);
+
+ /** Defines @a pValidator. */
+ void setValidator(UIPageValidator *pValidator);
+ /** Defines whether @a fIsValidatorBlocked which means not used at all. */
+ void setValidatorBlocked(bool fIsValidatorBlocked) { m_fIsValidatorBlocked = fIsValidatorBlocked; }
+ /** Performs page validation composing a list of @a messages. */
+ virtual bool validate(QList<UIValidationMessage> &messages) { Q_UNUSED(messages); return true; }
+
+ /** Returns first navigation widget. */
+ QWidget *firstWidget() const { return m_pFirstWidget; }
+ /** Defines the first navigation widget for TAB-order. */
+ virtual void setOrderAfter(QWidget *pWidget) { m_pFirstWidget = pWidget; }
+
+ /** Defines @a enmConfigurationAccessLevel. */
+ virtual void setConfigurationAccessLevel(ConfigurationAccessLevel enmConfigurationAccessLevel);
+ /** Returns configuration access level. */
+ ConfigurationAccessLevel configurationAccessLevel() const { return m_enmConfigurationAccessLevel; }
+ /** Returns whether configuration access level is Full. */
+ bool isMachineOffline() const { return configurationAccessLevel() == ConfigurationAccessLevel_Full; }
+ /** Returns whether configuration access level corresponds to machine in Powered Off state. */
+ bool isMachinePoweredOff() const { return configurationAccessLevel() == ConfigurationAccessLevel_Partial_PoweredOff; }
+ /** Returns whether configuration access level corresponds to machine in Saved state. */
+ bool isMachineSaved() const { return configurationAccessLevel() == ConfigurationAccessLevel_Partial_Saved; }
+ /** Returns whether configuration access level corresponds to machine in one of Running states. */
+ bool isMachineOnline() const { return configurationAccessLevel() == ConfigurationAccessLevel_Partial_Running; }
+ /** Returns whether configuration access level corresponds to machine in one of allowed states. */
+ bool isMachineInValidMode() const { return isMachineOffline() || isMachinePoweredOff() || isMachineSaved() || isMachineOnline(); }
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const = 0;
+
+ /** Defines page @a cId. */
+ void setId(int cId) { m_cId = cId; }
+ /** Returns page ID. */
+ int id() const { return m_cId; }
+
+ /** Returns page internal name. */
+ virtual QString internalName() const = 0;
+
+ /** Returns page warning pixmap. */
+ virtual QPixmap warningPixmap() const = 0;
+
+ /** Defines whether page is @a fProcessed. */
+ void setProcessed(bool fProcessed) { m_fProcessed = fProcessed; }
+ /** Returns whether page is processed. */
+ bool processed() const { return m_fProcessed; }
+
+ /** Defines whether page processing is @a fFailed. */
+ void setFailed(bool fFailed) { m_fFailed = fFailed; }
+ /** Returns whether page processing is failed. */
+ bool failed() const { return m_fFailed; }
+
+ /** Performs page polishing. */
+ virtual void polishPage() {}
+
+public slots:
+
+ /** Performs validation. */
+ void revalidate();
+
+protected:
+
+ /** Constructs settings page. */
+ UISettingsPage();
+
+private:
+
+ /** Holds the configuration access level. */
+ ConfigurationAccessLevel m_enmConfigurationAccessLevel;
+
+ /** Holds the page ID. */
+ int m_cId;
+
+ /** Holds the first TAB-orer widget reference. */
+ QWidget *m_pFirstWidget;
+ /** Holds the page validator. */
+ UIPageValidator *m_pValidator;
+
+ /** Holds whether page validation is blocked. */
+ bool m_fIsValidatorBlocked : 1;
+ /** Holds whether page is processed. */
+ bool m_fProcessed : 1;
+ /** Holds whether page processing is failed. */
+ bool m_fFailed : 1;
+};
+
+
+/** UISettingsPage extension used as Global Preferences page interface. */
+class SHARED_LIBRARY_STUFF UISettingsPageGlobal : public UISettingsPage
+{
+ Q_OBJECT;
+
+protected:
+
+ /** Constructs global preferences page. */
+ UISettingsPageGlobal();
+
+ /** Returns internal page ID. */
+ GlobalSettingsPageType internalID() const;
+
+ /** Returns page internal name. */
+ virtual QString internalName() const RT_OVERRIDE;
+
+ /** Returns page warning pixmap. */
+ virtual QPixmap warningPixmap() const RT_OVERRIDE;
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE { return false; }
+
+ /** Fetches data to m_properties & m_settings. */
+ void fetchData(const QVariant &data);
+ /** Uploads m_properties & m_settings to data. */
+ void uploadData(QVariant &data) const;
+
+ /** Holds the source of host preferences. */
+ CHost m_host;
+ /** Holds the source of global preferences. */
+ CSystemProperties m_properties;
+};
+
+
+/** UISettingsPage extension used as Machine Settings page interface. */
+class SHARED_LIBRARY_STUFF UISettingsPageMachine : public UISettingsPage
+{
+ Q_OBJECT;
+
+protected:
+
+ /** Constructs machine settings page. */
+ UISettingsPageMachine();
+
+ /** Returns internal page ID. */
+ MachineSettingsPageType internalID() const;
+
+ /** Returns page internal name. */
+ virtual QString internalName() const RT_OVERRIDE;
+
+ /** Returns page warning pixmap. */
+ virtual QPixmap warningPixmap() const RT_OVERRIDE;
+
+ /** Fetches data to m_machine & m_console. */
+ void fetchData(const QVariant &data);
+ /** Uploads m_machine & m_console to data. */
+ void uploadData(QVariant &data) const;
+
+ /** Holds the source of machine settings. */
+ CMachine m_machine;
+ /** Holds the source of console settings. */
+ CConsole m_console;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_settings_UISettingsPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSelector.cpp b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSelector.cpp
new file mode 100644
index 00000000..478277e8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSelector.cpp
@@ -0,0 +1,822 @@
+/* $Id: UISettingsSelector.cpp $ */
+/** @file
+ * VBox Qt GUI - UISettingsSelector class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QAction>
+#include <QActionGroup>
+#include <QHeaderView>
+#include <QLayout>
+#include <QTabWidget>
+#include <QToolButton>
+
+/* GUI includes: */
+#include "QITabWidget.h"
+#include "QITreeWidget.h"
+#include "UISettingsSelector.h"
+#include "UIIconPool.h"
+#include "UISettingsPage.h"
+#include "QIToolBar.h"
+
+
+/** QAccessibleWidget extension used as an accessibility interface for UISettingsSelectorToolBar buttons. */
+class UIAccessibilityInterfaceForUISettingsSelectorToolBarButton : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating toolbar button accessibility interface: */
+ if ( pObject
+ && strClassname == QLatin1String("QToolButton")
+ && pObject->property("Belongs to") == "UISettingsSelectorToolBar")
+ return new UIAccessibilityInterfaceForUISettingsSelectorToolBarButton(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ UIAccessibilityInterfaceForUISettingsSelectorToolBarButton(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::Button)
+ {}
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE
+ {
+ /* Make sure button still alive: */
+ AssertPtrReturn(button(), QAccessible::NoRole);
+
+ /* Return role for checkable button: */
+ if (button()->isCheckable())
+ return QAccessible::RadioButton;
+
+ /* Return default role: */
+ return QAccessible::Button;
+ }
+
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE
+ {
+ /* Prepare the button state: */
+ QAccessible::State state;
+
+ /* Make sure button still alive: */
+ AssertPtrReturn(button(), state);
+
+ /* Compose the button state: */
+ state.checkable = button()->isCheckable();
+ state.checked = button()->isChecked();
+
+ /* Return the button state: */
+ return state;
+ }
+
+private:
+
+ /** Returns corresponding toolbar button. */
+ QToolButton *button() const { return qobject_cast<QToolButton*>(widget()); }
+};
+
+
+/** Tree-widget column sections. */
+enum TreeWidgetSection
+{
+ TreeWidgetSection_Category = 0,
+ TreeWidgetSection_Id,
+ TreeWidgetSection_Link
+};
+
+
+/** Simple container of all the selector item data. */
+class UISelectorItem
+{
+public:
+
+ /** Constructs selector item.
+ * @param icon Brings the item icon.
+ * @param strText Brings the item text.
+ * @param iID Brings the item ID.
+ * @param strLink Brings the item link.
+ * @param pPage Brings the item page reference.
+ * @param iParentID Brings the item parent ID. */
+ UISelectorItem(const QIcon &icon, const QString &strText, int iID, const QString &strLink, UISettingsPage *pPage, int iParentID)
+ : m_icon(icon)
+ , m_strText(strText)
+ , m_iID(iID)
+ , m_strLink(strLink)
+ , m_pPage(pPage)
+ , m_iParentID(iParentID)
+ {}
+
+ /** Destructs selector item. */
+ virtual ~UISelectorItem() {}
+
+ /** Returns the item icon. */
+ QIcon icon() const { return m_icon; }
+ /** Returns the item text. */
+ QString text() const { return m_strText; }
+ /** Defines the item @a strText. */
+ void setText(const QString &strText) { m_strText = strText; }
+ /** Returns the item ID. */
+ int id() const { return m_iID; }
+ /** Returns the item link. */
+ QString link() const { return m_strLink; }
+ /** Returns the item page reference. */
+ UISettingsPage *page() const { return m_pPage; }
+ /** Returns the item parent ID. */
+ int parentID() const { return m_iParentID; }
+
+protected:
+
+ /** Holds the item icon. */
+ QIcon m_icon;
+ /** Holds the item text. */
+ QString m_strText;
+ /** Holds the item ID. */
+ int m_iID;
+ /** Holds the item link. */
+ QString m_strLink;
+ /** Holds the item page reference. */
+ UISettingsPage *m_pPage;
+ /** Holds the item parent ID. */
+ int m_iParentID;
+};
+
+
+/** UISelectorItem subclass used as tab-widget selector item. */
+class UISelectorActionItem : public UISelectorItem
+{
+public:
+
+ /** Constructs selector item.
+ * @param icon Brings the item icon.
+ * @param strText Brings the item text.
+ * @param iID Brings the item ID.
+ * @param strLink Brings the item link.
+ * @param pPage Brings the item page reference.
+ * @param iParentID Brings the item parent ID.
+ * @param pParent Brings the item parent. */
+ UISelectorActionItem(const QIcon &icon, const QString &strText, int iID, const QString &strLink, UISettingsPage *pPage, int iParentID, QObject *pParent)
+ : UISelectorItem(icon, strText, iID, strLink, pPage, iParentID)
+ , m_pAction(new QAction(icon, strText, pParent))
+ , m_pTabWidget(0)
+ {
+ m_pAction->setCheckable(true);
+ }
+
+ /** Returns the action instance. */
+ QAction *action() const { return m_pAction; }
+
+ /** Defines the @a pTabWidget instance. */
+ void setTabWidget(QTabWidget *pTabWidget) { m_pTabWidget = pTabWidget; }
+ /** Returns the tab-widget instance. */
+ QTabWidget *tabWidget() const { return m_pTabWidget; }
+
+protected:
+
+ /** Holds the action instance. */
+ QAction *m_pAction;
+ /** Holds the tab-widget instance. */
+ QTabWidget *m_pTabWidget;
+};
+
+
+/*********************************************************************************************************************************
+* Class UISettingsSelector implementation. *
+*********************************************************************************************************************************/
+
+UISettingsSelector::UISettingsSelector(QWidget *pParent /* = 0 */)
+ : QObject(pParent)
+{
+}
+
+UISettingsSelector::~UISettingsSelector()
+{
+ qDeleteAll(m_list);
+ m_list.clear();
+}
+
+void UISettingsSelector::setItemText(int iID, const QString &strText)
+{
+ if (UISelectorItem *pTtem = findItem(iID))
+ pTtem->setText(strText);
+}
+
+QString UISettingsSelector::itemTextByPage(UISettingsPage *pPage) const
+{
+ QString strText;
+ if (UISelectorItem *pItem = findItemByPage(pPage))
+ strText = pItem->text();
+ return strText;
+}
+
+QWidget *UISettingsSelector::idToPage(int iID) const
+{
+ UISettingsPage *pPage = 0;
+ if (UISelectorItem *pItem = findItem(iID))
+ pPage = pItem->page();
+ return pPage;
+}
+
+QList<UISettingsPage*> UISettingsSelector::settingPages() const
+{
+ QList<UISettingsPage*> list;
+ foreach (UISelectorItem *pItem, m_list)
+ if (pItem->page())
+ list << pItem->page();
+ return list;
+}
+
+QList<QWidget*> UISettingsSelector::rootPages() const
+{
+ QList<QWidget*> list;
+ foreach (UISelectorItem *pItem, m_list)
+ if (pItem->page())
+ list << pItem->page();
+ return list;
+}
+
+UISelectorItem *UISettingsSelector::findItem(int iID) const
+{
+ UISelectorItem *pResult = 0;
+ foreach (UISelectorItem *pItem, m_list)
+ if (pItem->id() == iID)
+ {
+ pResult = pItem;
+ break;
+ }
+ return pResult;
+}
+
+UISelectorItem *UISettingsSelector::findItemByLink(const QString &strLink) const
+{
+ UISelectorItem *pResult = 0;
+ foreach (UISelectorItem *pItem, m_list)
+ if (pItem->link() == strLink)
+ {
+ pResult = pItem;
+ break;
+ }
+ return pResult;
+}
+
+UISelectorItem *UISettingsSelector::findItemByPage(UISettingsPage *pPage) const
+{
+ UISelectorItem *pResult = 0;
+ foreach (UISelectorItem *pItem, m_list)
+ if (pItem->page() == pPage)
+ {
+ pResult = pItem;
+ break;
+ }
+ return pResult;
+}
+
+
+/*********************************************************************************************************************************
+* Class UISettingsSelectorTreeView implementation. *
+*********************************************************************************************************************************/
+
+UISettingsSelectorTreeView::UISettingsSelectorTreeView(QWidget *pParent /* = 0 */)
+ : UISettingsSelector(pParent)
+ , m_pTreeWidget(0)
+{
+ /* Prepare the tree-widget: */
+ m_pTreeWidget = new QITreeWidget(pParent);
+ QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
+ sizePolicy.setHorizontalStretch(0);
+ sizePolicy.setVerticalStretch(0);
+ sizePolicy.setHeightForWidth(m_pTreeWidget->sizePolicy().hasHeightForWidth());
+ const QStyle *pStyle = QApplication::style();
+ const int iIconMetric = pStyle->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pTreeWidget->setSizePolicy(sizePolicy);
+ m_pTreeWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTreeWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTreeWidget->setRootIsDecorated(false);
+ m_pTreeWidget->setUniformRowHeights(true);
+ m_pTreeWidget->setIconSize(QSize((int)(1.5 * iIconMetric), (int)(1.5 * iIconMetric)));
+ /* Add the columns: */
+ m_pTreeWidget->headerItem()->setText(TreeWidgetSection_Category, "Category");
+ m_pTreeWidget->headerItem()->setText(TreeWidgetSection_Id, "[id]");
+ m_pTreeWidget->headerItem()->setText(TreeWidgetSection_Link, "[link]");
+ /* Hide unnecessary columns and header: */
+ m_pTreeWidget->header()->hide();
+ m_pTreeWidget->hideColumn(TreeWidgetSection_Id);
+ m_pTreeWidget->hideColumn(TreeWidgetSection_Link);
+ /* Setup connections: */
+ connect(m_pTreeWidget, &QITreeWidget::currentItemChanged,
+ this, &UISettingsSelectorTreeView::sltSettingsGroupChanged);
+}
+
+UISettingsSelectorTreeView::~UISettingsSelectorTreeView()
+{
+ /* Cleanup the tree-widget: */
+ delete m_pTreeWidget;
+ m_pTreeWidget = 0;
+}
+
+QWidget *UISettingsSelectorTreeView::widget() const
+{
+ return m_pTreeWidget;
+}
+
+QWidget *UISettingsSelectorTreeView::addItem(const QString & /* strBigIcon */,
+ const QString &strMediumIcon ,
+ const QString & /* strSmallIcon */,
+ int iID,
+ const QString &strLink,
+ UISettingsPage *pPage /* = 0 */,
+ int iParentID /* = -1 */)
+{
+ QWidget *pResult = 0;
+ if (pPage != 0)
+ {
+ const QIcon icon = UIIconPool::iconSet(strMediumIcon);
+
+ UISelectorItem *pItem = new UISelectorItem(icon, "", iID, strLink, pPage, iParentID);
+ m_list.append(pItem);
+
+ QTreeWidgetItem *pTwItem = new QITreeWidgetItem(m_pTreeWidget, QStringList() << QString("")
+ << idToString(iID)
+ << strLink);
+ pTwItem->setIcon(TreeWidgetSection_Category, pItem->icon());
+ pPage->setContentsMargins(0, 0, 0, 0);
+ pPage->layout()->setContentsMargins(0, 0, 0, 0);
+ pResult = pPage;
+ }
+ return pResult;
+}
+
+void UISettingsSelectorTreeView::setItemText(int iID, const QString &strText)
+{
+ UISettingsSelector::setItemText(iID, strText);
+ QTreeWidgetItem *pItem = findItem(m_pTreeWidget, idToString(iID), TreeWidgetSection_Id);
+ if (pItem)
+ pItem->setText(TreeWidgetSection_Category, QString(" %1 ").arg(strText));
+}
+
+QString UISettingsSelectorTreeView::itemText(int iID) const
+{
+ return pagePath(idToString(iID));
+}
+
+int UISettingsSelectorTreeView::currentId() const
+{
+ int iID = -1;
+ const QTreeWidgetItem *pItem = m_pTreeWidget->currentItem();
+ if (pItem)
+ iID = pItem->text(TreeWidgetSection_Id).toInt();
+ return iID;
+}
+
+int UISettingsSelectorTreeView::linkToId(const QString &strLink) const
+{
+ int iID = -1;
+ const QTreeWidgetItem *pItem = findItem(m_pTreeWidget, strLink, TreeWidgetSection_Link);
+ if (pItem)
+ iID = pItem->text(TreeWidgetSection_Id).toInt();
+ return iID;
+}
+
+void UISettingsSelectorTreeView::selectById(int iID)
+{
+ QTreeWidgetItem *pItem = findItem(m_pTreeWidget, idToString(iID), TreeWidgetSection_Id);
+ if (pItem)
+ m_pTreeWidget->setCurrentItem(pItem);
+}
+
+void UISettingsSelectorTreeView::setVisibleById(int iID, bool fVisible)
+{
+ QTreeWidgetItem *pItem = findItem(m_pTreeWidget, idToString(iID), TreeWidgetSection_Id);
+ if (pItem)
+ pItem->setHidden(!fVisible);
+}
+
+void UISettingsSelectorTreeView::polish()
+{
+ /* Get recommended size hint: */
+ const QStyle *pStyle = QApplication::style();
+ const int iIconMetric = pStyle->pixelMetric(QStyle::PM_SmallIconSize);
+ int iItemWidth = static_cast<QAbstractItemView*>(m_pTreeWidget)->sizeHintForColumn(TreeWidgetSection_Category);
+ int iItemHeight = qMax((int)(iIconMetric * 1.5) /* icon height */,
+ m_pTreeWidget->fontMetrics().height() /* text height */);
+ /* Add some margin to every item in the tree: */
+ iItemHeight += 4 /* margin itself */ * 2 /* margin count */;
+ /* Set final size hint for items: */
+ m_pTreeWidget->setSizeHintForItems(QSize(iItemWidth , iItemHeight));
+
+ /* Adjust selector width/height: */
+ m_pTreeWidget->setFixedWidth(iItemWidth + 2 * m_pTreeWidget->frameWidth());
+ m_pTreeWidget->setMinimumHeight(m_pTreeWidget->topLevelItemCount() * iItemHeight +
+ 1 /* margin itself */ * 2 /* margin count */);
+
+ /* Sort selector by the id column: */
+ m_pTreeWidget->sortItems(TreeWidgetSection_Id, Qt::AscendingOrder);
+
+ /* Resize column(s) to content: */
+ m_pTreeWidget->resizeColumnToContents(TreeWidgetSection_Category);
+}
+
+void UISettingsSelectorTreeView::sltSettingsGroupChanged(QTreeWidgetItem *pItem,
+ QTreeWidgetItem * /* pPrevItem */)
+{
+ if (pItem)
+ {
+ const int iID = pItem->text(TreeWidgetSection_Id).toInt();
+ Assert(iID >= 0);
+ emit sigCategoryChanged(iID);
+ }
+}
+
+void UISettingsSelectorTreeView::clear()
+{
+ m_pTreeWidget->clear();
+}
+
+QString UISettingsSelectorTreeView::pagePath(const QString &strMatch) const
+{
+ const QTreeWidgetItem *pTreeItem =
+ findItem(m_pTreeWidget,
+ strMatch,
+ TreeWidgetSection_Id);
+ return path(pTreeItem);
+}
+
+QTreeWidgetItem *UISettingsSelectorTreeView::findItem(QTreeWidget *pView,
+ const QString &strMatch,
+ int iColumn) const
+{
+ QList<QTreeWidgetItem*> list =
+ pView->findItems(strMatch, Qt::MatchExactly, iColumn);
+
+ return list.count() ? list[0] : 0;
+}
+
+QString UISettingsSelectorTreeView::idToString(int iID) const
+{
+ return QString("%1").arg(iID, 2, 10, QLatin1Char('0'));
+}
+
+/* static */
+QString UISettingsSelectorTreeView::path(const QTreeWidgetItem *pItem)
+{
+ static QString strSep = ": ";
+ QString strPath;
+ const QTreeWidgetItem *pCurrentItem = pItem;
+ while (pCurrentItem)
+ {
+ if (!strPath.isNull())
+ strPath = strSep + strPath;
+ strPath = pCurrentItem->text(TreeWidgetSection_Category).simplified() + strPath;
+ pCurrentItem = pCurrentItem->parent();
+ }
+ return strPath;
+}
+
+
+/*********************************************************************************************************************************
+* Class UISettingsSelectorToolBar implementation. *
+*********************************************************************************************************************************/
+
+UISettingsSelectorToolBar::UISettingsSelectorToolBar(QWidget *pParent /* = 0 */)
+ : UISettingsSelector(pParent)
+ , m_pToolBar(0)
+ , m_pActionGroup(0)
+{
+ /* Install tool-bar button accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUISettingsSelectorToolBarButton::pFactory);
+
+ /* Prepare the toolbar: */
+ m_pToolBar = new QIToolBar(pParent);
+ m_pToolBar->setUseTextLabels(true);
+ m_pToolBar->setIconSize(QSize(32, 32));
+#ifdef VBOX_WS_MAC
+ m_pToolBar->setShowToolBarButton(false);
+#endif /* VBOX_WS_MAC */
+
+ /* Prepare the action group: */
+ m_pActionGroup = new QActionGroup(this);
+ m_pActionGroup->setExclusive(true);
+ connect(m_pActionGroup, &QActionGroup::triggered,
+ this, static_cast<void(UISettingsSelectorToolBar::*)(QAction*)>(&UISettingsSelectorToolBar::sltSettingsGroupChanged));
+}
+
+UISettingsSelectorToolBar::~UISettingsSelectorToolBar()
+{
+ /* Cleanup the action group: */
+ delete m_pActionGroup;
+ m_pActionGroup = 0;
+
+ /* Cleanup the toolbar: */
+ delete m_pToolBar;
+ m_pToolBar = 0;
+}
+
+QWidget *UISettingsSelectorToolBar::widget() const
+{
+ return m_pToolBar;
+}
+
+QWidget *UISettingsSelectorToolBar::addItem(const QString &strBigIcon,
+ const QString & /* strMediumIcon */,
+ const QString &strSmallIcon,
+ int iID,
+ const QString &strLink,
+ UISettingsPage *pPage /* = 0 */,
+ int iParentID /* = -1 */)
+{
+ const QIcon icon = UIIconPool::iconSet(strBigIcon);
+
+ QWidget *pResult = 0;
+ UISelectorActionItem *pItem = new UISelectorActionItem(icon, "", iID, strLink, pPage, iParentID, this);
+ m_list.append(pItem);
+
+ if (iParentID == -1 &&
+ pPage != 0)
+ {
+ m_pActionGroup->addAction(pItem->action());
+ m_pToolBar->addAction(pItem->action());
+ m_pToolBar->widgetForAction(pItem->action())
+ ->setProperty("Belongs to", "UISettingsSelectorToolBar");
+ pPage->setContentsMargins(0, 0, 0, 0);
+ pPage->layout()->setContentsMargins(0, 0, 0, 0);
+ pResult = pPage;
+ }
+ else if (iParentID == -1 &&
+ pPage == 0)
+ {
+ m_pActionGroup->addAction(pItem->action());
+ m_pToolBar->addAction(pItem->action());
+ m_pToolBar->widgetForAction(pItem->action())
+ ->setProperty("Belongs to", "UISettingsSelectorToolBar");
+ QITabWidget *pTabWidget = new QITabWidget();
+ pTabWidget->setIconSize(QSize(16, 16));
+ pTabWidget->setContentsMargins(0, 0, 0, 0);
+// connect(pTabWidget, SIGNAL(currentChanged(int)),
+// this, SLOT(sltSettingsGroupChanged(int)));
+ pItem->setTabWidget(pTabWidget);
+ pResult = pTabWidget;
+ }
+ else
+ {
+ UISelectorActionItem *pParent = findActionItem(iParentID);
+ if (pParent)
+ {
+ QTabWidget *pTabWidget = pParent->tabWidget();
+ pPage->setContentsMargins(9, 5, 9, 9);
+ pPage->layout()->setContentsMargins(0, 0, 0, 0);
+ const QIcon icon1 = UIIconPool::iconSet(strSmallIcon);
+ if (pTabWidget)
+ pTabWidget->addTab(pPage, icon1, "");
+ }
+ }
+ return pResult;
+}
+
+void UISettingsSelectorToolBar::setItemText(int iID, const QString &strText)
+{
+ if (UISelectorActionItem *pItem = findActionItem(iID))
+ {
+ pItem->setText(strText);
+ if (pItem->action())
+ pItem->action()->setText(strText);
+ if (pItem->parentID() &&
+ pItem->page())
+ {
+ const UISelectorActionItem *pParent = findActionItem(pItem->parentID());
+ if (pParent &&
+ pParent->tabWidget())
+ pParent->tabWidget()->setTabText(
+ pParent->tabWidget()->indexOf(pItem->page()), strText);
+ }
+ }
+}
+
+QString UISettingsSelectorToolBar::itemText(int iID) const
+{
+ QString strResult;
+ if (UISelectorItem *pItem = findItem(iID))
+ strResult = pItem->text();
+ return strResult;
+}
+
+int UISettingsSelectorToolBar::currentId() const
+{
+ const UISelectorActionItem *pAction = findActionItemByAction(m_pActionGroup->checkedAction());
+ int iID = -1;
+ if (pAction)
+ iID = pAction->id();
+ return iID;
+}
+
+int UISettingsSelectorToolBar::linkToId(const QString &strLink) const
+{
+ int iID = -1;
+ const UISelectorItem *pItem = UISettingsSelector::findItemByLink(strLink);
+ if (pItem)
+ iID = pItem->id();
+ return iID;
+}
+
+QWidget *UISettingsSelectorToolBar::idToPage(int iID) const
+{
+ QWidget *pPage = 0;
+ if (const UISelectorActionItem *pItem = findActionItem(iID))
+ {
+ pPage = pItem->page();
+ if (!pPage)
+ pPage = pItem->tabWidget();
+ }
+ return pPage;
+}
+
+QWidget *UISettingsSelectorToolBar::rootPage(int iID) const
+{
+ QWidget *pPage = 0;
+ if (const UISelectorActionItem *pItem = findActionItem(iID))
+ {
+ if (pItem->parentID() > -1)
+ pPage = rootPage(pItem->parentID());
+ else if (pItem->page())
+ pPage = pItem->page();
+ else
+ pPage = pItem->tabWidget();
+ }
+ return pPage;
+}
+
+void UISettingsSelectorToolBar::selectById(int iID)
+{
+ if (const UISelectorActionItem *pItem = findActionItem(iID))
+ {
+ if (pItem->parentID() != -1)
+ {
+ const UISelectorActionItem *pParent = findActionItem(pItem->parentID());
+ if (pParent &&
+ pParent->tabWidget())
+ {
+ pParent->action()->trigger();
+ pParent->tabWidget()->setCurrentIndex(
+ pParent->tabWidget()->indexOf(pItem->page()));
+ }
+ }
+ else
+ pItem->action()->trigger();
+ }
+}
+
+void UISettingsSelectorToolBar::setVisibleById(int iID, bool fVisible)
+{
+ const UISelectorActionItem *pItem = findActionItem(iID);
+
+ if (pItem)
+ {
+ pItem->action()->setVisible(fVisible);
+ if (pItem->parentID() > -1 &&
+ pItem->page())
+ {
+ const UISelectorActionItem *pParent = findActionItem(pItem->parentID());
+ if (pParent &&
+ pParent->tabWidget())
+ {
+ if (fVisible &&
+ pParent->tabWidget()->indexOf(pItem->page()) == -1)
+ pParent->tabWidget()->addTab(pItem->page(), pItem->text());
+ else if (!fVisible &&
+ pParent->tabWidget()->indexOf(pItem->page()) > -1)
+ pParent->tabWidget()->removeTab(
+ pParent->tabWidget()->indexOf(pItem->page()));
+ }
+ }
+ }
+
+}
+
+QList<QWidget*> UISettingsSelectorToolBar::rootPages() const
+{
+ QList<QWidget*> list;
+ foreach (UISelectorItem *pItem, m_list)
+ {
+ const UISelectorActionItem *pActionItem = static_cast<UISelectorActionItem*>(pItem);
+ if (pActionItem->parentID() == -1 &&
+ pActionItem->page())
+ list << pActionItem->page();
+ else if (pActionItem->tabWidget())
+ list << pActionItem->tabWidget();
+ }
+ return list;
+}
+
+int UISettingsSelectorToolBar::minWidth() const
+{
+ return m_pToolBar->sizeHint().width() + 2 * 10;
+}
+
+void UISettingsSelectorToolBar::sltSettingsGroupChanged(QAction *pAction)
+{
+ const UISelectorActionItem *pItem = findActionItemByAction(pAction);
+ if (pItem)
+ {
+ emit sigCategoryChanged(pItem->id());
+// if (pItem->page() &&
+// !pItem->tabWidget())
+// emit sigCategoryChanged(pItem->id());
+// else
+// {
+//
+// pItem->tabWidget()->blockSignals(true);
+// pItem->tabWidget()->setCurrentIndex(0);
+// pItem->tabWidget()->blockSignals(false);
+// printf("%s\n", qPrintable(pItem->text()));
+// UISelectorActionItem *child = static_cast<UISelectorActionItem*>(
+// findItemByPage(static_cast<UISettingsPage*>(pItem->tabWidget()->currentWidget())));
+// if (child)
+// emit sigCategoryChanged(child->id());
+// }
+ }
+}
+
+void UISettingsSelectorToolBar::sltSettingsGroupChanged(int iIndex)
+{
+ const UISelectorActionItem *pItem = findActionItemByTabWidget(qobject_cast<QTabWidget*>(sender()), iIndex);
+ if (pItem)
+ {
+ if (pItem->page() &&
+ !pItem->tabWidget())
+ emit sigCategoryChanged(pItem->id());
+ else
+ {
+ const UISelectorActionItem *pChild = static_cast<UISelectorActionItem*>(
+ findItemByPage(static_cast<UISettingsPage*>(pItem->tabWidget()->currentWidget())));
+ if (pChild)
+ emit sigCategoryChanged(pChild->id());
+ }
+ }
+}
+
+void UISettingsSelectorToolBar::clear()
+{
+ QList<QAction*> list = m_pActionGroup->actions();
+ foreach (QAction *pAction, list)
+ delete pAction;
+}
+
+UISelectorActionItem *UISettingsSelectorToolBar::findActionItem(int iID) const
+{
+ return static_cast<UISelectorActionItem*>(UISettingsSelector::findItem(iID));
+}
+
+UISelectorActionItem *UISettingsSelectorToolBar::findActionItemByAction(QAction *pAction) const
+{
+ UISelectorActionItem *pResult = 0;
+ foreach (UISelectorItem *pItem, m_list)
+ if (static_cast<UISelectorActionItem*>(pItem)->action() == pAction)
+ {
+ pResult = static_cast<UISelectorActionItem*>(pItem);
+ break;
+ }
+
+ return pResult;
+}
+
+UISelectorActionItem *UISettingsSelectorToolBar::findActionItemByTabWidget(QTabWidget *pTabWidget, int iIndex) const
+{
+ UISelectorActionItem *pResult = 0;
+ foreach (UISelectorItem *pItem, m_list)
+ if (static_cast<UISelectorActionItem*>(pItem)->tabWidget() == pTabWidget)
+ {
+ QTabWidget *pTabWidget2 = static_cast<UISelectorActionItem*>(pItem)->tabWidget(); /// @todo r=bird: same as pTabWidget?
+ pResult = static_cast<UISelectorActionItem*>(
+ findItemByPage(static_cast<UISettingsPage*>(pTabWidget2->widget(iIndex))));
+ break;
+ }
+
+ return pResult;
+
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSelector.h b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSelector.h
new file mode 100644
index 00000000..c7beba99
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSelector.h
@@ -0,0 +1,294 @@
+/* $Id: UISettingsSelector.h $ */
+/** @file
+ * VBox Qt GUI - UISettingsSelector class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_UISettingsSelector_h
+#define FEQT_INCLUDED_SRC_settings_UISettingsSelector_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QObject>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QAction;
+class QActionGroup;
+class QTabWidget;
+class QTreeWidget;
+class QTreeWidgetItem;
+class QITreeWidget;
+class UISelectorActionItem;
+class UISelectorItem;
+class UISettingsPage;
+class QIToolBar;
+
+
+/** QObject subclass providing settings dialog
+ * with the means to switch between settings pages. */
+class SHARED_LIBRARY_STUFF UISettingsSelector : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about selector @a iCategory changed. */
+ void sigCategoryChanged(int iCategory);
+
+public:
+
+ /** Constructs settings selector passing @a pParent to the base-class. */
+ UISettingsSelector(QWidget *pParent = 0);
+ /** Destructs settings selector. */
+ virtual ~UISettingsSelector() RT_OVERRIDE;
+
+ /** Returns the widget selector operates on. */
+ virtual QWidget *widget() const = 0;
+
+ /** Adds a new selector item.
+ * @param strBigIcon Brings the big icon reference.
+ * @param strMediumIcon Brings the medium icon reference.
+ * @param strSmallIcon Brings the small icon reference.
+ * @param iID Brings the selector section ID.
+ * @param strLink Brings the selector section link.
+ * @param pPage Brings the selector section page reference.
+ * @param iParentID Brings the parent section ID or -1 if there is no parent. */
+ virtual QWidget *addItem(const QString &strBigIcon, const QString &strMediumIcon, const QString &strSmallIcon,
+ int iID, const QString &strLink, UISettingsPage *pPage = 0, int iParentID = -1) = 0;
+
+ /** Defines the @a strText for section with @a iID. */
+ virtual void setItemText(int iID, const QString &strText);
+ /** Returns the text for section with @a iID. */
+ virtual QString itemText(int iID) const = 0;
+ /** Returns the text for section with @a pPage. */
+ virtual QString itemTextByPage(UISettingsPage *pPage) const;
+
+ /** Returns the current selector ID. */
+ virtual int currentId() const = 0;
+
+ /** Returns the section ID for passed @a strLink. */
+ virtual int linkToId(const QString &strLink) const = 0;
+
+ /** Returns the section page for passed @a iID. */
+ virtual QWidget *idToPage(int iID) const;
+ /** Returns the section root-page for passed @a iID. */
+ virtual QWidget *rootPage(int iID) const { return idToPage(iID); }
+
+ /** Make the section with @a iID current. */
+ virtual void selectById(int iID) = 0;
+ /** Make the section with @a strLink current. */
+ virtual void selectByLink(const QString &strLink) { selectById(linkToId(strLink)); }
+
+ /** Make the section with @a iID @a fVisible. */
+ virtual void setVisibleById(int iID, bool fVisible) = 0;
+
+ /** Returns the list of all selector pages. */
+ virtual QList<UISettingsPage*> settingPages() const;
+ /** Returns the list of all root pages. */
+ virtual QList<QWidget*> rootPages() const;
+
+ /** Performs selector polishing. */
+ virtual void polish() {}
+
+ /** Returns minimum selector width. */
+ virtual int minWidth() const { return 0; }
+
+protected:
+
+ /** Clears selector of all the items. */
+ virtual void clear() = 0;
+
+ /** Searches for item with passed @a iID. */
+ UISelectorItem *findItem(int iID) const;
+ /** Searches for item with passed @a strLink. */
+ UISelectorItem *findItemByLink(const QString &strLink) const;
+ /** Searches for item with passed @a pPage. */
+ UISelectorItem *findItemByPage(UISettingsPage *pPage) const;
+
+ /** Holds the selector item instances. */
+ QList<UISelectorItem*> m_list;
+};
+
+
+/** UISettingsSelector subclass providing settings dialog
+ * with the means to switch between settings pages.
+ * This one represented as tree-widget. */
+class SHARED_LIBRARY_STUFF UISettingsSelectorTreeView : public UISettingsSelector
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs settings selector passing @a pParent to the base-class. */
+ UISettingsSelectorTreeView(QWidget *pParent = 0);
+ /** Destructs settings selector. */
+ virtual ~UISettingsSelectorTreeView() RT_OVERRIDE;
+
+ /** Returns the widget selector operates on. */
+ virtual QWidget *widget() const RT_OVERRIDE;
+
+ /** Adds a new selector item.
+ * @param strBigIcon Brings the big icon reference.
+ * @param strMediumIcon Brings the medium icon reference.
+ * @param strSmallIcon Brings the small icon reference.
+ * @param iID Brings the selector section ID.
+ * @param strLink Brings the selector section link.
+ * @param pPage Brings the selector section page reference.
+ * @param iParentID Brings the parent section ID or -1 if there is no parent. */
+ virtual QWidget *addItem(const QString &strBigIcon, const QString &strMediumIcon, const QString &strSmallIcon,
+ int iID, const QString &strLink, UISettingsPage *pPage = 0, int iParentID = -1) RT_OVERRIDE;
+
+ /** Defines the @a strText for section with @a iID. */
+ virtual void setItemText(int iID, const QString &strText) RT_OVERRIDE;
+ /** Returns the text for section with @a iID. */
+ virtual QString itemText(int iID) const RT_OVERRIDE;
+
+ /** Returns the current selector ID. */
+ virtual int currentId() const RT_OVERRIDE;
+
+ /** Returns the section ID for passed @a strLink. */
+ virtual int linkToId(const QString &strLink) const RT_OVERRIDE;
+
+ /** Make the section with @a iID current. */
+ virtual void selectById(int iID) RT_OVERRIDE;
+
+ /** Make the section with @a iID @a fVisible. */
+ virtual void setVisibleById(int iID, bool fVisible) RT_OVERRIDE;
+
+ /** Performs selector polishing. */
+ virtual void polish() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles selector section change from @a pPrevItem to @a pItem. */
+ void sltSettingsGroupChanged(QTreeWidgetItem *pItem, QTreeWidgetItem *pPrevItem);
+
+private:
+
+ /** Clears selector of all the items. */
+ virtual void clear() RT_OVERRIDE;
+
+ /** Returns page path for passed @a strMatch. */
+ QString pagePath(const QString &strMatch) const;
+ /** Find item within the passed @a pView and @a iColumn matching @a strMatch. */
+ QTreeWidgetItem *findItem(QTreeWidget *pView, const QString &strMatch, int iColumn) const;
+ /** Performs @a iID to QString serialization. */
+ QString idToString(int iID) const;
+
+ /** Forges the full path for passed @a pItem. */
+ static QString path(const QTreeWidgetItem *pItem);
+
+ /** Holds the tree-widget instance. */
+ QITreeWidget *m_pTreeWidget;
+};
+
+
+/** UISettingsSelector subclass providing settings dialog
+ * with the means to switch between settings pages.
+ * This one represented as tab-widget. */
+class SHARED_LIBRARY_STUFF UISettingsSelectorToolBar : public UISettingsSelector
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs settings selector passing @a pParent to the base-class. */
+ UISettingsSelectorToolBar(QWidget *pParent = 0);
+ /** Destructs settings selector. */
+ virtual ~UISettingsSelectorToolBar() RT_OVERRIDE;
+
+ /** Returns the widget selector operates on. */
+ virtual QWidget *widget() const RT_OVERRIDE;
+
+ /** Adds a new selector item.
+ * @param strBigIcon Brings the big icon reference.
+ * @param strMediumIcon Brings the medium icon reference.
+ * @param strSmallIcon Brings the small icon reference.
+ * @param iID Brings the selector section ID.
+ * @param strLink Brings the selector section link.
+ * @param pPage Brings the selector section page reference.
+ * @param iParentID Brings the parent section ID or -1 if there is no parent. */
+ virtual QWidget *addItem(const QString &strBigIcon, const QString &strMediumIcon, const QString &strSmallIcon,
+ int iID, const QString &strLink, UISettingsPage *pPage = 0, int iParentID = -1) RT_OVERRIDE;
+
+ /** Defines the @a strText for section with @a iID. */
+ virtual void setItemText(int iID, const QString &strText) RT_OVERRIDE;
+ /** Returns the text for section with @a iID. */
+ virtual QString itemText(int iID) const RT_OVERRIDE;
+
+ /** Returns the current selector ID. */
+ virtual int currentId() const RT_OVERRIDE;
+
+ /** Returns the section ID for passed @a strLink. */
+ virtual int linkToId(const QString &strLink) const RT_OVERRIDE;
+
+ /** Returns the section page for passed @a iID. */
+ virtual QWidget *idToPage(int iID) const RT_OVERRIDE;
+ /** Returns the section root-page for passed @a iID. */
+ virtual QWidget *rootPage(int iID) const RT_OVERRIDE;
+
+ /** Make the section with @a iID current. */
+ virtual void selectById(int iID) RT_OVERRIDE;
+
+ /** Make the section with @a iID @a fVisible. */
+ virtual void setVisibleById(int iID, bool fVisible) RT_OVERRIDE;
+
+ /** Returns the list of all root pages. */
+ virtual QList<QWidget*> rootPages() const RT_OVERRIDE;
+
+ /** Returns minimum selector width. */
+ virtual int minWidth() const RT_OVERRIDE;
+
+private slots:
+
+ /** Handles selector section change to @a pAction. */
+ void sltSettingsGroupChanged(QAction *pAction);
+ /** Handles selector section change to @a iIndex. */
+ void sltSettingsGroupChanged(int iIndex);
+
+private:
+
+ /** Clears selector of all the items. */
+ virtual void clear() RT_OVERRIDE;
+
+ /** Searches for action item with passed @a iID. */
+ UISelectorActionItem *findActionItem(int iID) const;
+ /** Searches for action item with passed @a pAction. */
+ UISelectorActionItem *findActionItemByAction(QAction *pAction) const;
+ /** Searches for action item with passed @a pTabWidget and @a iIndex. */
+ UISelectorActionItem *findActionItemByTabWidget(QTabWidget *pTabWidget, int iIndex) const;
+
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolBar;
+ /** Holds the action group instance. */
+ QActionGroup *m_pActionGroup;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_settings_UISettingsSelector_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSerializer.cpp b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSerializer.cpp
new file mode 100644
index 00000000..7e013053
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSerializer.cpp
@@ -0,0 +1,410 @@
+/* $Id: UISettingsSerializer.cpp $ */
+/** @file
+ * VBox Qt GUI - UISettingsSerializer class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QProgressBar>
+#include <QTimer>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILabel.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UISettingsPage.h"
+#include "UISettingsSerializer.h"
+
+
+/*********************************************************************************************************************************
+* Class UISettingsSerializer implementation. *
+*********************************************************************************************************************************/
+
+UISettingsSerializer::UISettingsSerializer(QObject *pParent, SerializationDirection enmDirection,
+ const QVariant &data, const UISettingsPageList &pages)
+ : QThread(pParent)
+ , m_enmDirection(enmDirection)
+ , m_data(data)
+ , m_fSavingComplete(m_enmDirection == Load)
+ , m_iIdOfHighPriorityPage(-1)
+{
+ /* Copy the page(s) from incoming list to our map: */
+ foreach (UISettingsPage *pPage, pages)
+ m_pages.insert(pPage->id(), pPage);
+
+ /* Handling internal signals, they are also redirected in their handlers: */
+ connect(this, &UISettingsSerializer::sigNotifyAboutPageProcessed, this, &UISettingsSerializer::sltHandleProcessedPage, Qt::QueuedConnection);
+ connect(this, &UISettingsSerializer::sigNotifyAboutPagesProcessed, this, &UISettingsSerializer::sltHandleProcessedPages, Qt::QueuedConnection);
+
+ /* Redirecting unhandled internal signals: */
+ connect(this, &UISettingsSerializer::finished, this, &UISettingsSerializer::sigNotifyAboutProcessFinished, Qt::QueuedConnection);
+}
+
+UISettingsSerializer::~UISettingsSerializer()
+{
+ /* If serializer is being destructed by it's parent,
+ * thread could still be running, we have to wait
+ * for it to be finished! */
+ if (isRunning())
+ wait();
+}
+
+void UISettingsSerializer::raisePriorityOfPage(int iPageId)
+{
+ /* If that page is present and was not processed already =>
+ * we should remember which page should be processed next: */
+ if (m_pages.contains(iPageId) && !(m_pages[iPageId]->processed()))
+ m_iIdOfHighPriorityPage = iPageId;
+}
+
+void UISettingsSerializer::start(Priority priority /* = InheritPriority */)
+{
+ /* Notify listeners about we are starting: */
+ emit sigNotifyAboutProcessStarted();
+
+ /* If serializer saves settings: */
+ if (m_enmDirection == Save)
+ {
+ /* We should update internal page cache first: */
+ foreach (UISettingsPage *pPage, m_pages.values())
+ pPage->putToCache();
+ }
+
+ /* Start async serializing thread: */
+ QThread::start(priority);
+}
+
+void UISettingsSerializer::sltHandleProcessedPage(int iPageId)
+{
+ /* Make sure such page present: */
+ AssertReturnVoid(m_pages.contains(iPageId));
+
+ /* Get the page being processed: */
+ UISettingsPage *pSettingsPage = m_pages.value(iPageId);
+
+ /* If serializer loads settings: */
+ if (m_enmDirection == Load)
+ {
+ /* We should fetch internal page cache: */
+ pSettingsPage->setValidatorBlocked(true);
+ pSettingsPage->getFromCache();
+ pSettingsPage->setValidatorBlocked(false);
+ }
+
+ /* Add processed page into corresponding map: */
+ m_pagesDone.insert(iPageId, pSettingsPage);
+
+ /* Notify listeners about process reached n%: */
+ const int iValue = 100 * m_pagesDone.size() / m_pages.size();
+ emit sigNotifyAboutProcessProgressChanged(iValue);
+}
+
+void UISettingsSerializer::sltHandleProcessedPages()
+{
+ /* If serializer saves settings: */
+ if (m_enmDirection == Save)
+ {
+ /* We should flag GUI thread to unlock itself: */
+ if (!m_fSavingComplete)
+ m_fSavingComplete = true;
+ }
+ /* If serializer loads settings: */
+ else
+ {
+ /* We have to do initial validation finally: */
+ foreach (UISettingsPage *pPage, m_pages.values())
+ pPage->revalidate();
+ }
+
+ /* Notify listeners about process reached 100%: */
+ emit sigNotifyAboutProcessProgressChanged(100);
+}
+
+void UISettingsSerializer::run()
+{
+ /* Initialize COM for other thread: */
+ COMBase::InitializeCOM(false);
+
+ /* Mark all the pages initially as NOT processed: */
+ foreach (UISettingsPage *pPage, m_pages.values())
+ pPage->setProcessed(false);
+
+ /* Iterate over the all left settings pages: */
+ UISettingsPageMap pages(m_pages);
+ while (!pages.empty())
+ {
+ /* Get required page pointer, protect map by mutex while getting pointer: */
+ UISettingsPage *pPage = m_iIdOfHighPriorityPage != -1 && pages.contains(m_iIdOfHighPriorityPage) ?
+ pages.value(m_iIdOfHighPriorityPage) : *pages.begin();
+ /* Reset request of high priority: */
+ if (m_iIdOfHighPriorityPage != -1)
+ m_iIdOfHighPriorityPage = -1;
+ /* Process this page if its enabled: */
+ connect(pPage, &UISettingsPage::sigOperationProgressChange,
+ this, &UISettingsSerializer::sigOperationProgressChange);
+ connect(pPage, &UISettingsPage::sigOperationProgressError,
+ this, &UISettingsSerializer::sigOperationProgressError);
+ if (pPage->isEnabled())
+ {
+ if (m_enmDirection == Load)
+ pPage->loadToCacheFrom(m_data);
+ if (m_enmDirection == Save)
+ pPage->saveFromCacheTo(m_data);
+ }
+ /* Remember what page was processed: */
+ disconnect(pPage, &UISettingsPage::sigOperationProgressChange,
+ this, &UISettingsSerializer::sigOperationProgressChange);
+ disconnect(pPage, &UISettingsPage::sigOperationProgressError,
+ this, &UISettingsSerializer::sigOperationProgressError);
+ pPage->setProcessed(true);
+ /* Remove processed page from our map: */
+ pages.remove(pPage->id());
+ /* Notify listeners about page was processed: */
+ emit sigNotifyAboutPageProcessed(pPage->id());
+ /* If serializer saves settings => wake up GUI thread: */
+ if (m_enmDirection == Save)
+ m_condition.wakeAll();
+ /* Break further processing if page had failed: */
+ if (pPage->failed())
+ break;
+ }
+ /* Notify listeners about all pages were processed: */
+ emit sigNotifyAboutPagesProcessed();
+ /* If serializer saves settings => wake up GUI thread: */
+ if (m_enmDirection == Save)
+ m_condition.wakeAll();
+
+ /* Deinitialize COM for other thread: */
+ COMBase::CleanupCOM();
+}
+
+
+/*********************************************************************************************************************************
+* Class UISettingsSerializerProgress implementation. *
+*********************************************************************************************************************************/
+
+QString UISettingsSerializerProgress::s_strProgressDescriptionTemplate = QString("<compact elipsis=\"middle\">%1 (%2/%3)</compact>");
+
+UISettingsSerializerProgress::UISettingsSerializerProgress(QWidget *pParent,
+ UISettingsSerializer::SerializationDirection enmDirection,
+ const QVariant &data,
+ const UISettingsPageList &pages)
+ : QIWithRetranslateUI<QIDialog>(pParent)
+ , m_enmDirection(enmDirection)
+ , m_data(data)
+ , m_pages(pages)
+ , m_pSerializer(0)
+ , m_pLabelOperationProgress(0)
+ , m_pBarOperationProgress(0)
+ , m_pLabelSubOperationProgress(0)
+ , m_pBarSubOperationProgress(0)
+ , m_fClean(true)
+{
+ /* Prepare: */
+ prepare();
+ /* Translate: */
+ retranslateUi();
+}
+
+int UISettingsSerializerProgress::exec()
+{
+ /* Ask for process start: */
+ emit sigAskForProcessStart();
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QIDialog>::exec();
+}
+
+QVariant &UISettingsSerializerProgress::data()
+{
+ AssertPtrReturn(m_pSerializer, m_data);
+ return m_pSerializer->data();
+}
+
+void UISettingsSerializerProgress::prepare()
+{
+ /* Configure self: */
+ setWindowModality(Qt::WindowModal);
+ setWindowTitle(parentWidget()->windowTitle());
+ connect(this, &UISettingsSerializerProgress::sigAskForProcessStart,
+ this, &UISettingsSerializerProgress::sltStartProcess, Qt::QueuedConnection);
+
+ /* Create serializer: */
+ m_pSerializer = new UISettingsSerializer(this, m_enmDirection, m_data, m_pages);
+ AssertPtrReturnVoid(m_pSerializer);
+ {
+ /* Install progress handler: */
+ connect(m_pSerializer, &UISettingsSerializer::sigNotifyAboutProcessProgressChanged,
+ this, &UISettingsSerializerProgress::sltHandleProcessProgressChange);
+ connect(m_pSerializer, &UISettingsSerializer::sigOperationProgressChange,
+ this, &UISettingsSerializerProgress::sltHandleOperationProgressChange);
+ connect(m_pSerializer, &UISettingsSerializer::sigOperationProgressError,
+ this, &UISettingsSerializerProgress::sltHandleOperationProgressError);
+ }
+
+ /* Create layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ AssertPtrReturnVoid(pLayout);
+ {
+ /* Create top layout: */
+ QHBoxLayout *pLayoutTop = new QHBoxLayout;
+ AssertPtrReturnVoid(pLayoutTop);
+ {
+ /* Create pixmap layout: */
+ QVBoxLayout *pLayoutPixmap = new QVBoxLayout;
+ AssertPtrReturnVoid(pLayoutPixmap);
+ {
+ /* Create pixmap label: */
+ QLabel *pLabelPixmap = new QLabel;
+ AssertPtrReturnVoid(pLabelPixmap);
+ {
+ /* Configure label: */
+ const QIcon icon = UIIconPool::iconSet(":/progress_settings_90px.png");
+ pLabelPixmap->setPixmap(icon.pixmap(icon.availableSizes().value(0, QSize(90, 90))));
+ /* Add label into layout: */
+ pLayoutPixmap->addWidget(pLabelPixmap);
+ }
+ /* Add stretch: */
+ pLayoutPixmap->addStretch();
+ /* Add layout into parent: */
+ pLayoutTop->addLayout(pLayoutPixmap);
+ }
+ /* Create progress layout: */
+ QVBoxLayout *pLayoutProgress = new QVBoxLayout;
+ AssertPtrReturnVoid(pLayoutProgress);
+ {
+ /* Create operation progress label: */
+ m_pLabelOperationProgress = new QLabel;
+ AssertPtrReturnVoid(m_pLabelOperationProgress);
+ {
+ /* Add label into layout: */
+ pLayoutProgress->addWidget(m_pLabelOperationProgress);
+ }
+ /* Create operation progress bar: */
+ m_pBarOperationProgress = new QProgressBar;
+ AssertPtrReturnVoid(m_pBarOperationProgress);
+ {
+ /* Configure progress bar: */
+ m_pBarOperationProgress->setMinimumWidth(300);
+ m_pBarOperationProgress->setMaximum(100);
+ m_pBarOperationProgress->setMinimum(0);
+ m_pBarOperationProgress->setValue(0);
+ /* Add bar into layout: */
+ pLayoutProgress->addWidget(m_pBarOperationProgress);
+ }
+ /* Create sub-operation progress label: */
+ m_pLabelSubOperationProgress = new QILabel;
+ AssertPtrReturnVoid(m_pLabelSubOperationProgress);
+ {
+ /* Configure label: */
+ m_pLabelSubOperationProgress->hide();
+ m_pLabelSubOperationProgress->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
+ /* Add label into layout: */
+ pLayoutProgress->addWidget(m_pLabelSubOperationProgress);
+ }
+ /* Create sub-operation progress bar: */
+ m_pBarSubOperationProgress = new QProgressBar;
+ AssertPtrReturnVoid(m_pBarSubOperationProgress);
+ {
+ /* Configure progress bar: */
+ m_pBarSubOperationProgress->hide();
+ m_pBarSubOperationProgress->setMinimumWidth(300);
+ m_pBarSubOperationProgress->setMaximum(100);
+ m_pBarSubOperationProgress->setMinimum(0);
+ m_pBarSubOperationProgress->setValue(0);
+ /* Add bar into layout: */
+ pLayoutProgress->addWidget(m_pBarSubOperationProgress);
+ }
+ /* Add stretch: */
+ pLayoutProgress->addStretch();
+ /* Add layout into parent: */
+ pLayoutTop->addLayout(pLayoutProgress);
+ }
+ /* Add layout into parent: */
+ pLayout->addLayout(pLayoutTop);
+ }
+ }
+}
+
+void UISettingsSerializerProgress::retranslateUi()
+{
+ /* Translate operation progress label: */
+ AssertPtrReturnVoid(m_pLabelOperationProgress);
+ switch (m_pSerializer->direction())
+ {
+ case UISettingsSerializer::Load: m_pLabelOperationProgress->setText(tr("Loading Settings...")); break;
+ case UISettingsSerializer::Save: m_pLabelOperationProgress->setText(tr("Saving Settings...")); break;
+ }
+}
+
+void UISettingsSerializerProgress::closeEvent(QCloseEvent *pEvent)
+{
+ /* No need to close the dialog: */
+ pEvent->ignore();
+}
+
+void UISettingsSerializerProgress::reject()
+{
+ /* No need to reject the dialog. */
+}
+
+void UISettingsSerializerProgress::sltStartProcess()
+{
+ /* Start the serializer: */
+ m_pSerializer->start();
+}
+
+void UISettingsSerializerProgress::sltHandleProcessProgressChange(int iValue)
+{
+ /* Update the operation progress-bar with incoming value: */
+ AssertPtrReturnVoid(m_pBarOperationProgress);
+ m_pBarOperationProgress->setValue(iValue);
+ /* Hide the progress-dialog upon reaching the 100% progress: */
+ if (iValue == m_pBarOperationProgress->maximum())
+ hide();
+}
+
+void UISettingsSerializerProgress::sltHandleOperationProgressChange(ulong iOperations, QString strOperation,
+ ulong iOperation, ulong iPercent)
+{
+ /* Update the sub-operation progress label and bar: */
+ AssertPtrReturnVoid(m_pLabelSubOperationProgress);
+ AssertPtrReturnVoid(m_pBarSubOperationProgress);
+ m_pLabelSubOperationProgress->show();
+ m_pBarSubOperationProgress->show();
+ m_pLabelSubOperationProgress->setText(s_strProgressDescriptionTemplate.arg(strOperation).arg(iOperation).arg(iOperations));
+ m_pBarSubOperationProgress->setValue(iPercent);
+}
+
+void UISettingsSerializerProgress::sltHandleOperationProgressError(QString strErrorInfo)
+{
+ /* Mark the serialization process dirty: */
+ m_fClean = false;
+
+ /* Show the error message: */
+ msgCenter().cannotSaveSettings(strErrorInfo, this);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSerializer.h b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSerializer.h
new file mode 100644
index 00000000..7801b246
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSerializer.h
@@ -0,0 +1,257 @@
+/* $Id: UISettingsSerializer.h $ */
+/** @file
+ * VBox Qt GUI - UISettingsSerializer class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_UISettingsSerializer_h
+#define FEQT_INCLUDED_SRC_settings_UISettingsSerializer_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QList>
+#include <QMap>
+#include <QMutex>
+#include <QThread>
+#include <QVariant>
+#include <QWaitCondition>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCloseEvent;
+class QLabel;
+class QMutex;
+class QObject;
+class QString;
+class QThread;
+class QProgressBar;
+class QVariant;
+class QWaitCondition;
+class QWidget;
+class QILabel;
+class UISettingsPage;
+
+/* Type definitions: */
+typedef QList<UISettingsPage*> UISettingsPageList;
+typedef QMap<int, UISettingsPage*> UISettingsPageMap;
+
+
+/** QThread reimplementation used for
+ * loading/saving settings in async mode. */
+class SHARED_LIBRARY_STUFF UISettingsSerializer : public QThread
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about process has been started. */
+ void sigNotifyAboutProcessStarted();
+ /** Notifies listeners about process reached @a iValue. */
+ void sigNotifyAboutProcessProgressChanged(int iValue);
+ /** Notifies listeners about process has been finished. */
+ void sigNotifyAboutProcessFinished();
+
+ /** Notifies GUI thread about some page was processed. */
+ void sigNotifyAboutPageProcessed(int iPageId);
+ /** Notifies GUI thread about all pages were processed. */
+ void sigNotifyAboutPagesProcessed();
+
+ /** Notifies listeners about particular operation progress change.
+ * @param iOperations Brings the number of operations CProgress have.
+ * @param strOperation Brings the description of the current CProgress operation.
+ * @param iOperation Brings the index of the current CProgress operation.
+ * @param iPercent Brings the percentage of the current CProgress operation. */
+ void sigOperationProgressChange(ulong iOperations, QString strOperation,
+ ulong iOperation, ulong iPercent);
+
+ /** Notifies listeners about particular COM error.
+ * @param strErrorInfo Brings the details of the error happened. */
+ void sigOperationProgressError(QString strErrorInfo);
+
+public:
+
+ /** Serialization directions. */
+ enum SerializationDirection { Load, Save };
+
+ /** Constructs serializer passing @a pParent to the base-class.
+ * @param enmDirection Brings the load/save direction.
+ * @param data Brings the wrapper(s) to load/save the data from/to.
+ * @param pages Brings the page(s) to load/save the data to/from. */
+ UISettingsSerializer(QObject *pParent, SerializationDirection enmDirection,
+ const QVariant &data, const UISettingsPageList &pages);
+
+ /** Destructs serializer. */
+ virtual ~UISettingsSerializer() RT_OVERRIDE;
+
+ /** Returns the load/save direction. */
+ SerializationDirection direction() const { return m_enmDirection; }
+
+ /** Returns the instance of wrapper(s) to load/save the data from/to. */
+ QVariant &data() { return m_data; }
+
+ /** Returns the count of the page(s) to load/save the data to/from. */
+ int pageCount() const { return m_pages.size(); }
+
+ /** Raises the priority of page with @a iPageId. */
+ void raisePriorityOfPage(int iPageId);
+
+public slots:
+
+ /** Starts the process of data loading with passed @a priority. */
+ void start(Priority priority = InheritPriority);
+
+protected slots:
+
+ /** Handles the fact of page with @a iPageId was processed. */
+ void sltHandleProcessedPage(int iPageId);
+
+ /** Handles the fact of all pages were processed. */
+ void sltHandleProcessedPages();
+
+protected:
+
+ /** Worker-thread serialization rutine. */
+ void run();
+
+ /** Holds the load/save direction. */
+ const SerializationDirection m_enmDirection;
+
+ /** Holds the wrapper(s) to load/save the data from/to. */
+ QVariant m_data;
+ /** Holds the page(s) to load/save the data to/from. */
+ UISettingsPageMap m_pages;
+ /** Holds the page(s) to load/save the data to/from for which that task was done. */
+ UISettingsPageMap m_pagesDone;
+
+ /** Holds whether the save was complete. */
+ bool m_fSavingComplete;
+ /** Holds the ID of the high priority page. */
+ int m_iIdOfHighPriorityPage;
+ /** Holds the synchronization mutex. */
+ QMutex m_mutex;
+ /** Holds the synchronization condition. */
+ QWaitCondition m_condition;
+};
+
+
+/** QIDialog reimplementation used to
+ * reflect the settings serialization operation. */
+class SHARED_LIBRARY_STUFF UISettingsSerializerProgress : public QIWithRetranslateUI<QIDialog>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Asks itself for process start. */
+ void sigAskForProcessStart();
+
+public:
+
+ /** Constructs serializer passing @a pParent to the base-class.
+ * @param enmDirection Brings the load/save direction.
+ * @param data Brings the wrapper(s) to load/save the data from/to.
+ * @param pages Brings the page(s) to load/save the data to/from. */
+ UISettingsSerializerProgress(QWidget *pParent, UISettingsSerializer::SerializationDirection enmDirection,
+ const QVariant &data, const UISettingsPageList &pages);
+
+ /** Executes the dialog. */
+ int exec();
+
+ /** Returns the instance of wrapper(s) to load/save the data from/to. */
+ QVariant &data();
+
+ /** Returns whether there were no errors. */
+ bool isClean() const { return m_fClean; }
+
+protected:
+
+ /** Prepare routine. */
+ void prepare();
+
+ /** Translate routine: */
+ void retranslateUi();
+
+ /** Close event-handler called with the given window system @a pEvent. */
+ virtual void closeEvent(QCloseEvent *pEvent);
+
+private slots:
+
+ /** Hides the modal dialog and sets the result code to <i>Rejected</i>. */
+ virtual void reject();
+
+ /** Starts the process. */
+ void sltStartProcess();
+
+ /** Handles process progress change to @a iValue. */
+ void sltHandleProcessProgressChange(int iValue);
+
+ /** Handles particular operation progress change.
+ * @param iOperations Brings the number of operations CProgress have.
+ * @param strOperation Brings the description of the current CProgress operation.
+ * @param iOperation Brings the index of the current CProgress operation.
+ * @param iPercent Brings the percentage of the current CProgress operation. */
+ void sltHandleOperationProgressChange(ulong iOperations, QString strOperation,
+ ulong iOperation, ulong iPercent);
+
+ /** Handles particular COM error.
+ * @param strErrorInfo Brings the details of the error happened. */
+ void sltHandleOperationProgressError(QString strErrorInfo);
+
+private:
+
+ /** Holds the load/save direction. */
+ const UISettingsSerializer::SerializationDirection m_enmDirection;
+
+ /** Holds the wrapper(s) to load/save the data from/to. */
+ QVariant m_data;
+ /** Holds the page(s) to load/save the data to/from. */
+ UISettingsPageList m_pages;
+
+ /** Holds the pointer to the thread loading/saving settings in async mode. */
+ UISettingsSerializer *m_pSerializer;
+
+ /** Holds the operation progress label. */
+ QLabel *m_pLabelOperationProgress;
+ /** Holds the operation progress bar. */
+ QProgressBar *m_pBarOperationProgress;
+
+ /** Holds the sub-operation progress label. */
+ QILabel *m_pLabelSubOperationProgress;
+ /** Holds the sub-operation progress bar. */
+ QProgressBar *m_pBarSubOperationProgress;
+
+ /** Holds whether there were no errors. */
+ bool m_fClean;
+
+ /** Holds the template for the sub-operation progress label. */
+ static QString s_strProgressDescriptionTemplate;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_settings_UISettingsSerializer_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/settings/editors/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAccelerationFeaturesEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAccelerationFeaturesEditor.cpp
new file mode 100644
index 00000000..95c8cf13
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAccelerationFeaturesEditor.cpp
@@ -0,0 +1,123 @@
+/* $Id: UIAccelerationFeaturesEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIAccelerationFeaturesEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIAccelerationFeaturesEditor.h"
+
+
+UIAccelerationFeaturesEditor::UIAccelerationFeaturesEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fEnableNestedPaging(false)
+ , m_pLabel(0)
+ , m_pCheckBoxEnableNestedPaging(0)
+{
+ prepare();
+}
+
+void UIAccelerationFeaturesEditor::setEnableNestedPaging(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnableNestedPaging != fOn)
+ {
+ m_fEnableNestedPaging = fOn;
+ if (m_pCheckBoxEnableNestedPaging)
+ m_pCheckBoxEnableNestedPaging->setCheckState(m_fEnableNestedPaging ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIAccelerationFeaturesEditor::isEnabledNestedPaging() const
+{
+ return m_pCheckBoxEnableNestedPaging
+ ? m_pCheckBoxEnableNestedPaging->checkState() == Qt::Checked
+ : m_fEnableNestedPaging;
+}
+
+void UIAccelerationFeaturesEditor::setEnableNestedPagingAvailable(bool fAvailable)
+{
+ m_pCheckBoxEnableNestedPaging->setEnabled(fAvailable);
+}
+
+int UIAccelerationFeaturesEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIAccelerationFeaturesEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIAccelerationFeaturesEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Hardware Virtualization:"));
+ if (m_pCheckBoxEnableNestedPaging)
+ {
+ m_pCheckBoxEnableNestedPaging->setText(tr("Enable Nested Pa&ging"));
+ m_pCheckBoxEnableNestedPaging->setToolTip(tr("When checked, the virtual machine will try to make use of the nested "
+ "paging extension of Intel VT-x and AMD-V."));
+ }
+}
+
+void UIAccelerationFeaturesEditor::prepare()
+{
+ /* Prepare main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Prepare virtualization label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Prepare 'enable nested virtualization' check-box: */
+ m_pCheckBoxEnableNestedPaging = new QCheckBox(this);
+ if (m_pCheckBoxEnableNestedPaging)
+ {
+ connect(m_pCheckBoxEnableNestedPaging, &QCheckBox::stateChanged,
+ this, &UIAccelerationFeaturesEditor::sigChangedNestedPaging);
+ m_pLayout->addWidget(m_pCheckBoxEnableNestedPaging, 0, 1);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAccelerationFeaturesEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAccelerationFeaturesEditor.h
new file mode 100644
index 00000000..7b6ae8c7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAccelerationFeaturesEditor.h
@@ -0,0 +1,96 @@
+/* $Id: UIAccelerationFeaturesEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIAccelerationFeaturesEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIAccelerationFeaturesEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIAccelerationFeaturesEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as acceleration features editor. */
+class SHARED_LIBRARY_STUFF UIAccelerationFeaturesEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about nested paging change. */
+ void sigChangedNestedPaging();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIAccelerationFeaturesEditor(QWidget *pParent = 0);
+
+ /** Defines whether 'enable nested paging' feature in @a fOn. */
+ void setEnableNestedPaging(bool fOn);
+ /** Returns 'enable nested paging' feature value. */
+ bool isEnabledNestedPaging() const;
+ /** Defines whether 'enable nested paging' option @a fAvailable. */
+ void setEnableNestedPagingAvailable(bool fAvailable);
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Values
+ * @{ */
+ /** Holds the 'enable nested paging' feature value. */
+ bool m_fEnableNestedPaging;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the 'enable nested paging' check-box instance. */
+ QCheckBox *m_pCheckBoxEnableNestedPaging;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIAccelerationFeaturesEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioControllerEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioControllerEditor.cpp
new file mode 100644
index 00000000..1174f1ff
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioControllerEditor.cpp
@@ -0,0 +1,169 @@
+/* $Id: UIAudioControllerEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIAudioControllerEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIAudioControllerEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIAudioControllerEditor::UIAudioControllerEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KAudioControllerType_Max)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UIAudioControllerEditor::setValue(KAudioControllerType enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+KAudioControllerType UIAudioControllerEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<KAudioControllerType>() : m_enmValue;
+}
+
+int UIAudioControllerEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIAudioControllerEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIAudioControllerEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Audio &Controller:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const KAudioControllerType enmType = m_pCombo->itemData(i).value<KAudioControllerType>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Selects the type of the virtual sound card. Depending on this value, "
+ "VirtualBox will provide different audio hardware to the virtual machine."));
+ }
+}
+
+void UIAudioControllerEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIAudioControllerEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Load currently supported audio driver types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedAudioControllerTypes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KAudioControllerType_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const KAudioControllerType &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioControllerEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioControllerEditor.h
new file mode 100644
index 00000000..93e1e0f2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioControllerEditor.h
@@ -0,0 +1,98 @@
+/* $Id: UIAudioControllerEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIAudioControllerEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIAudioControllerEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIAudioControllerEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as an audio controller editor. */
+class SHARED_LIBRARY_STUFF UIAudioControllerEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIAudioControllerEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KAudioControllerType enmValue);
+ /** Returns editor value. */
+ KAudioControllerType value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KAudioControllerType> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ KAudioControllerType m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KAudioControllerType> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIAudioControllerEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioFeaturesEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioFeaturesEditor.cpp
new file mode 100644
index 00000000..c4e337d7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioFeaturesEditor.cpp
@@ -0,0 +1,143 @@
+/* $Id: UIAudioFeaturesEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIAudioFeaturesEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UIAudioFeaturesEditor.h"
+
+
+UIAudioFeaturesEditor::UIAudioFeaturesEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fEnableOutput(false)
+ , m_fEnableInput(false)
+ , m_pLabel(0)
+ , m_pCheckBoxEnableOutput(0)
+ , m_pCheckBoxEnableInput(0)
+{
+ prepare();
+}
+
+void UIAudioFeaturesEditor::setEnableOutput(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnableOutput != fOn)
+ {
+ m_fEnableOutput = fOn;
+ if (m_pCheckBoxEnableOutput)
+ m_pCheckBoxEnableOutput->setCheckState(m_fEnableOutput ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIAudioFeaturesEditor::outputEnabled() const
+{
+ return m_pCheckBoxEnableOutput
+ ? m_pCheckBoxEnableOutput->checkState() == Qt::Checked
+ : m_fEnableOutput;
+}
+
+void UIAudioFeaturesEditor::setEnableInput(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnableInput != fOn)
+ {
+ m_fEnableInput = fOn;
+ if (m_pCheckBoxEnableInput)
+ m_pCheckBoxEnableInput->setCheckState(m_fEnableInput ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIAudioFeaturesEditor::inputEnabled() const
+{
+ return m_pCheckBoxEnableInput
+ ? m_pCheckBoxEnableInput->checkState() == Qt::Checked
+ : m_fEnableInput;
+}
+
+int UIAudioFeaturesEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIAudioFeaturesEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIAudioFeaturesEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Extended Features:"));
+ if (m_pCheckBoxEnableOutput)
+ {
+ m_pCheckBoxEnableOutput->setText(tr("Enable Audio &Output"));
+ m_pCheckBoxEnableOutput->setToolTip(tr("When checked, output to the virtual audio device will reach the host. "
+ "Otherwise the guest is muted."));
+ }
+ if (m_pCheckBoxEnableInput)
+ {
+ m_pCheckBoxEnableInput->setText(tr("Enable Audio &Input"));
+ m_pCheckBoxEnableInput->setToolTip(tr("When checked, the guest will be able to capture audio input from the host. "
+ "Otherwise the guest will capture only silence."));
+ }
+}
+
+void UIAudioFeaturesEditor::prepare()
+{
+ /* Prepare main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Prepare label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+ /* Prepare 'enable output' check-box: */
+ m_pCheckBoxEnableOutput = new QCheckBox(this);
+ if (m_pCheckBoxEnableOutput)
+ m_pLayout->addWidget(m_pCheckBoxEnableOutput, 0, 1);
+ /* Prepare 'enable input' check-box: */
+ m_pCheckBoxEnableInput = new QCheckBox(this);
+ if (m_pCheckBoxEnableInput)
+ m_pLayout->addWidget(m_pCheckBoxEnableInput, 1, 1);
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioFeaturesEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioFeaturesEditor.h
new file mode 100644
index 00000000..96d741ca
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioFeaturesEditor.h
@@ -0,0 +1,98 @@
+/* $Id: UIAudioFeaturesEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIAudioFeaturesEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIAudioFeaturesEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIAudioFeaturesEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as audio features editor. */
+class SHARED_LIBRARY_STUFF UIAudioFeaturesEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIAudioFeaturesEditor(QWidget *pParent = 0);
+
+ /** Defines whether 'enable output' feature in @a fOn. */
+ void setEnableOutput(bool fOn);
+ /** Returns 'enable output' feature value. */
+ bool outputEnabled() const;
+
+ /** Defines whether 'enable input' feature in @a fOn. */
+ void setEnableInput(bool fOn);
+ /** Returns 'enable input' feature value. */
+ bool inputEnabled() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Values
+ * @{ */
+ /** Holds the 'enable output' feature value. */
+ bool m_fEnableOutput;
+ /** Holds the 'enable input' feature value. */
+ bool m_fEnableInput;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the 'enable output' check-box instance. */
+ QCheckBox *m_pCheckBoxEnableOutput;
+ /** Holds the 'enable input' check-box instance. */
+ QCheckBox *m_pCheckBoxEnableInput;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIAudioFeaturesEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioHostDriverEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioHostDriverEditor.cpp
new file mode 100644
index 00000000..d704d914
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioHostDriverEditor.cpp
@@ -0,0 +1,169 @@
+/* $Id: UIAudioHostDriverEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIAudioHostDriverEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIAudioHostDriverEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIAudioHostDriverEditor::UIAudioHostDriverEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KAudioDriverType_Max)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UIAudioHostDriverEditor::setValue(KAudioDriverType enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+KAudioDriverType UIAudioHostDriverEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<KAudioDriverType>() : m_enmValue;
+}
+
+int UIAudioHostDriverEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIAudioHostDriverEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIAudioHostDriverEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Host Audio &Driver:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const KAudioDriverType enmType = m_pCombo->itemData(i).value<KAudioDriverType>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Selects the audio output driver. The Null Audio Driver makes the guest "
+ "see an audio card, however every access to it will be ignored."));
+ }
+}
+
+void UIAudioHostDriverEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIAudioHostDriverEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Load currently supported audio driver types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedAudioDriverTypes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KAudioDriverType_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const KAudioDriverType &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioHostDriverEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioHostDriverEditor.h
new file mode 100644
index 00000000..0a18411a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioHostDriverEditor.h
@@ -0,0 +1,98 @@
+/* $Id: UIAudioHostDriverEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIAudioHostDriverEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIAudioHostDriverEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIAudioHostDriverEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as an audio host driver editor. */
+class SHARED_LIBRARY_STUFF UIAudioHostDriverEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIAudioHostDriverEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KAudioDriverType enmValue);
+ /** Returns editor value. */
+ KAudioDriverType value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KAudioDriverType> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ KAudioDriverType m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KAudioDriverType> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIAudioHostDriverEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioSettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioSettingsEditor.cpp
new file mode 100644
index 00000000..1fb23ec6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioSettingsEditor.cpp
@@ -0,0 +1,238 @@
+/* $Id: UIAudioSettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIAudioSettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIAudioControllerEditor.h"
+#include "UIAudioFeaturesEditor.h"
+#include "UIAudioHostDriverEditor.h"
+#include "UIAudioSettingsEditor.h"
+
+
+UIAudioSettingsEditor::UIAudioSettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fFeatureEnabled(false)
+ , m_pCheckboxFeature(0)
+ , m_pWidgetSettings(0)
+ , m_pEditorAudioHostDriver(0)
+ , m_pEditorAudioController(0)
+ , m_pEditorAudioFeatures(0)
+{
+ prepare();
+}
+
+void UIAudioSettingsEditor::setFeatureEnabled(bool fEnabled)
+{
+ if (m_fFeatureEnabled != fEnabled)
+ {
+ m_fFeatureEnabled = fEnabled;
+ if (m_pCheckboxFeature)
+ m_pCheckboxFeature->setChecked(m_fFeatureEnabled);
+ }
+}
+
+bool UIAudioSettingsEditor::isFeatureEnabled() const
+{
+ return m_pCheckboxFeature ? m_pCheckboxFeature->isChecked() : m_fFeatureEnabled;
+}
+
+void UIAudioSettingsEditor::setFeatureAvailable(bool fAvailable)
+{
+ if (m_pCheckboxFeature)
+ m_pCheckboxFeature->setEnabled(fAvailable);
+}
+
+void UIAudioSettingsEditor::setHostDriverType(KAudioDriverType enmType)
+{
+ if (m_pEditorAudioHostDriver)
+ m_pEditorAudioHostDriver->setValue(enmType);
+}
+
+KAudioDriverType UIAudioSettingsEditor::hostDriverType() const
+{
+ return m_pEditorAudioHostDriver ? m_pEditorAudioHostDriver->value() : KAudioDriverType_Max;
+}
+
+void UIAudioSettingsEditor::setHostDriverOptionAvailable(bool fAvailable)
+{
+ if (m_pEditorAudioHostDriver)
+ m_pEditorAudioHostDriver->setEnabled(fAvailable);
+}
+
+void UIAudioSettingsEditor::setControllerType(KAudioControllerType enmType)
+{
+ if (m_pEditorAudioController)
+ m_pEditorAudioController->setValue(enmType);
+}
+
+KAudioControllerType UIAudioSettingsEditor::controllerType() const
+{
+ return m_pEditorAudioController ? m_pEditorAudioController->value() : KAudioControllerType_Max;
+}
+
+void UIAudioSettingsEditor::setControllerOptionAvailable(bool fAvailable)
+{
+ if (m_pEditorAudioController)
+ m_pEditorAudioController->setEnabled(fAvailable);
+}
+
+void UIAudioSettingsEditor::setEnableOutput(bool fConnected)
+{
+ if (m_pEditorAudioFeatures)
+ m_pEditorAudioFeatures->setEnableOutput(fConnected);
+}
+
+bool UIAudioSettingsEditor::outputEnabled() const
+{
+ return m_pEditorAudioFeatures ? m_pEditorAudioFeatures->outputEnabled() : false;
+}
+
+void UIAudioSettingsEditor::setEnableInput(bool fConnected)
+{
+ if (m_pEditorAudioFeatures)
+ m_pEditorAudioFeatures->setEnableInput(fConnected);
+}
+
+bool UIAudioSettingsEditor::inputEnabled() const
+{
+ return m_pEditorAudioFeatures ? m_pEditorAudioFeatures->inputEnabled() : false;
+}
+
+void UIAudioSettingsEditor::setFeatureOptionsAvailable(bool fAvailable)
+{
+ if (m_pEditorAudioFeatures)
+ m_pEditorAudioFeatures->setEnabled(fAvailable);
+}
+
+void UIAudioSettingsEditor::retranslateUi()
+{
+ if (m_pCheckboxFeature)
+ {
+ m_pCheckboxFeature->setText(tr("Enable &Audio"));
+ m_pCheckboxFeature->setToolTip(tr("When checked, a virtual PCI audio card will be plugged into the virtual machine "
+ "and will communicate with the host audio system using the specified driver."));
+ }
+
+ /* These editors have own labels, but we want them to be properly layouted according to each other: */
+ int iMinimumLayoutHint = 0;
+ if (m_pEditorAudioHostDriver)
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorAudioHostDriver->minimumLabelHorizontalHint());
+ if (m_pEditorAudioController)
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorAudioController->minimumLabelHorizontalHint());
+ if (m_pEditorAudioFeatures)
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorAudioFeatures->minimumLabelHorizontalHint());
+ if (m_pEditorAudioHostDriver)
+ m_pEditorAudioHostDriver->setMinimumLayoutIndent(iMinimumLayoutHint);
+ if (m_pEditorAudioController)
+ m_pEditorAudioController->setMinimumLayoutIndent(iMinimumLayoutHint);
+ if (m_pEditorAudioFeatures)
+ m_pEditorAudioFeatures->setMinimumLayoutIndent(iMinimumLayoutHint);
+}
+
+void UIAudioSettingsEditor::sltHandleFeatureToggled()
+{
+ /* Update widget availability: */
+ updateFeatureAvailability();
+}
+
+void UIAudioSettingsEditor::prepare()
+{
+ /* Prepare stuff: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Update widget availability: */
+ updateFeatureAvailability();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIAudioSettingsEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare adapter check-box: */
+ m_pCheckboxFeature = new QCheckBox(this);
+ if (m_pCheckboxFeature)
+ pLayout->addWidget(m_pCheckboxFeature, 0, 0, 1, 2);
+
+ /* Prepare 20-px shifting spacer: */
+ QSpacerItem *pSpacerItem = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacerItem)
+ pLayout->addItem(pSpacerItem, 1, 0);
+
+ /* Prepare settings widget: */
+ m_pWidgetSettings = new QWidget(this);
+ if (m_pWidgetSettings)
+ {
+ /* Prepare settings layout: */
+ QVBoxLayout *pLayoutAudioSettings = new QVBoxLayout(m_pWidgetSettings);
+ if (pLayoutAudioSettings)
+ {
+ pLayoutAudioSettings->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare host driver editor: */
+ m_pEditorAudioHostDriver = new UIAudioHostDriverEditor(m_pWidgetSettings);
+ if (m_pEditorAudioHostDriver)
+ pLayoutAudioSettings->addWidget(m_pEditorAudioHostDriver);
+
+ /* Prepare controller editor: */
+ m_pEditorAudioController = new UIAudioControllerEditor(m_pWidgetSettings);
+ if (m_pEditorAudioController)
+ pLayoutAudioSettings->addWidget(m_pEditorAudioController);
+
+ /* Prepare features editor: */
+ m_pEditorAudioFeatures = new UIAudioFeaturesEditor(m_pWidgetSettings);
+ if (m_pEditorAudioFeatures)
+ pLayoutAudioSettings->addWidget(m_pEditorAudioFeatures);
+ }
+
+ pLayout->addWidget(m_pWidgetSettings, 1, 1);
+ }
+ }
+}
+
+void UIAudioSettingsEditor::prepareConnections()
+{
+ if (m_pCheckboxFeature)
+ connect(m_pCheckboxFeature, &QCheckBox::stateChanged,
+ this, &UIAudioSettingsEditor::sltHandleFeatureToggled);
+}
+
+void UIAudioSettingsEditor::updateFeatureAvailability()
+{
+ m_pWidgetSettings->setEnabled(m_pCheckboxFeature->isChecked());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioSettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioSettingsEditor.h
new file mode 100644
index 00000000..2c3af1fd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAudioSettingsEditor.h
@@ -0,0 +1,153 @@
+/* $Id: UIAudioSettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIAudioSettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIAudioSettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIAudioSettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+#include "UIPortForwardingTable.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class UIAudioControllerEditor;
+class UIAudioFeaturesEditor;
+class UIAudioHostDriverEditor;
+
+/** QWidget subclass used as a audio settings editor. */
+class SHARED_LIBRARY_STUFF UIAudioSettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIAudioSettingsEditor(QWidget *pParent = 0);
+
+ /** @name General stuff
+ * @{ */
+ /** Defines whether feature is @a fEnabled. */
+ void setFeatureEnabled(bool fEnabled);
+ /** Returns whether feature is enabled. */
+ bool isFeatureEnabled() const;
+
+ /** Defines whether feature @a fAvailable. */
+ void setFeatureAvailable(bool fAvailable);
+ /** @} */
+
+ /** @name Host driver editor stuff
+ * @{ */
+ /** Defines host driver @a enmType. */
+ void setHostDriverType(KAudioDriverType enmType);
+ /** Returns host driver type. */
+ KAudioDriverType hostDriverType() const;
+
+ /** Defines whether host driver option @a fAvailable. */
+ void setHostDriverOptionAvailable(bool fAvailable);
+ /** @} */
+
+ /** @name Controller editor stuff
+ * @{ */
+ /** Defines controller @a enmType. */
+ void setControllerType(KAudioControllerType enmValue);
+ /** Returns controller type. */
+ KAudioControllerType controllerType() const;
+
+ /** Defines whether controller option @a fAvailable. */
+ void setControllerOptionAvailable(bool fAvailable);
+ /** @} */
+
+ /** @name Features editor stuff
+ * @{ */
+ /** Defines whether 'enable output' feature in @a fOn. */
+ void setEnableOutput(bool fOn);
+ /** Returns 'enable output' feature value. */
+ bool outputEnabled() const;
+
+ /** Defines whether 'enable input' feature in @a fOn. */
+ void setEnableInput(bool fOn);
+ /** Returns 'enable input' feature value. */
+ bool inputEnabled() const;
+
+ /** Defines whether feature options @a fAvailable. */
+ void setFeatureOptionsAvailable(bool fAvailable);
+ /** @} */
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles feature toggling. */
+ void sltHandleFeatureToggled();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Updates feature availability. */
+ void updateFeatureAvailability();
+
+ /** @name Values
+ * @{ */
+ /** Holds whether feature is enabled. */
+ bool m_fFeatureEnabled;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the feature check-box instance. */
+ QCheckBox *m_pCheckboxFeature;
+ /** Holds the settings widget instance. */
+ QWidget *m_pWidgetSettings;
+ /** Holds the host driver editor instance. */
+ UIAudioHostDriverEditor *m_pEditorAudioHostDriver;
+ /** Holds the controller editor instance. */
+ UIAudioControllerEditor *m_pEditorAudioController;
+ /** Holds the features editor instance. */
+ UIAudioFeaturesEditor *m_pEditorAudioFeatures;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIAudioSettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAutoCaptureKeyboardEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAutoCaptureKeyboardEditor.cpp
new file mode 100644
index 00000000..349fd6e2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAutoCaptureKeyboardEditor.cpp
@@ -0,0 +1,97 @@
+/* $Id: UIAutoCaptureKeyboardEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIAutoCaptureKeyboardEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UIAutoCaptureKeyboardEditor.h"
+
+
+UIAutoCaptureKeyboardEditor::UIAutoCaptureKeyboardEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fValue(false)
+ , m_pLabel(0)
+ , m_pCheckBox(0)
+{
+ prepare();
+}
+
+void UIAutoCaptureKeyboardEditor::setValue(bool fValue)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fValue != fValue)
+ {
+ m_fValue = fValue;
+ if (m_pCheckBox)
+ m_pCheckBox->setCheckState(m_fValue ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIAutoCaptureKeyboardEditor::value() const
+{
+ return m_pCheckBox ? m_pCheckBox->checkState() == Qt::Checked : m_fValue;
+}
+
+void UIAutoCaptureKeyboardEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Extended Features:"));
+ if (m_pCheckBox)
+ {
+ m_pCheckBox->setText(tr("&Auto Capture Keyboard"));
+ m_pCheckBox->setToolTip(tr("When checked, the keyboard is automatically captured every time the VM window is "
+ "activated. When the keyboard is captured, all keystrokes (including system ones like "
+ "Alt-Tab) are directed to the VM."));
+ }
+}
+
+void UIAutoCaptureKeyboardEditor::prepare()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ pLayout->setColumnStretch(1, 1);
+
+ /* Prepare label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ pLayout->addWidget(m_pLabel, 0, 0);
+ /* Prepare check-box: */
+ m_pCheckBox = new QCheckBox(this);
+ if (m_pCheckBox)
+ pLayout->addWidget(m_pCheckBox, 0, 1);
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAutoCaptureKeyboardEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAutoCaptureKeyboardEditor.h
new file mode 100644
index 00000000..55e60c30
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIAutoCaptureKeyboardEditor.h
@@ -0,0 +1,75 @@
+/* $Id: UIAutoCaptureKeyboardEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIAutoCaptureKeyboardEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIAutoCaptureKeyboardEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIAutoCaptureKeyboardEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QLabel;
+
+/** QWidget subclass used as an auto capture keyboard editor. */
+class SHARED_LIBRARY_STUFF UIAutoCaptureKeyboardEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIAutoCaptureKeyboardEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a fValue. */
+ void setValue(bool fValue);
+ /** Returns editor value. */
+ bool value() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the value to be set. */
+ bool m_fValue;
+
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the check-box instance. */
+ QCheckBox *m_pCheckBox;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIAutoCaptureKeyboardEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBaseMemoryEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBaseMemoryEditor.cpp
new file mode 100644
index 00000000..9bc32706
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBaseMemoryEditor.cpp
@@ -0,0 +1,443 @@
+/* $Id: UIBaseMemoryEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIBaseMemoryEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QSpinBox>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+#include "UIBaseMemoryEditor.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+/** QIAdvancedSlider subclass used as a base memory slider. */
+class SHARED_LIBRARY_STUFF UIBaseMemorySlider : public QIAdvancedSlider
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs guest RAM slider passing @a pParent to the base-class. */
+ UIBaseMemorySlider(QWidget *pParent = 0);
+ /** Constructs guest RAM slider passing @a pParent and @a enmOrientation to the base-class. */
+ UIBaseMemorySlider(Qt::Orientation enmOrientation, QWidget *pParent = 0);
+
+ /** Returns the minimum RAM. */
+ uint minRAM() const;
+ /** Returns the maximum optimal RAM. */
+ uint maxRAMOpt() const;
+ /** Returns the maximum allowed RAM. */
+ uint maxRAMAlw() const;
+ /** Returns the maximum possible RAM. */
+ uint maxRAM() const;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Calculates page step for passed @a iMaximum value. */
+ int calcPageStep(int iMaximum) const;
+
+ /** Holds the minimum RAM. */
+ uint m_uMinRAM;
+ /** Holds the maximum optimal RAM. */
+ uint m_uMaxRAMOpt;
+ /** Holds the maximum allowed RAM. */
+ uint m_uMaxRAMAlw;
+ /** Holds the maximum possible RAM. */
+ uint m_uMaxRAM;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIBaseMemorySlider implementation. *
+*********************************************************************************************************************************/
+
+UIBaseMemorySlider::UIBaseMemorySlider(QWidget *pParent /* = 0 */)
+ : QIAdvancedSlider(pParent)
+ , m_uMinRAM(0)
+ , m_uMaxRAMOpt(0)
+ , m_uMaxRAMAlw(0)
+ , m_uMaxRAM(0)
+{
+ prepare();
+}
+
+UIBaseMemorySlider::UIBaseMemorySlider(Qt::Orientation enmOrientation, QWidget *pParent /* = 0 */)
+ : QIAdvancedSlider(enmOrientation, pParent)
+ , m_uMinRAM(0)
+ , m_uMaxRAMOpt(0)
+ , m_uMaxRAMAlw(0)
+ , m_uMaxRAM(0)
+{
+ prepare();
+}
+
+uint UIBaseMemorySlider::minRAM() const
+{
+ return m_uMinRAM;
+}
+
+uint UIBaseMemorySlider::maxRAMOpt() const
+{
+ return m_uMaxRAMOpt;
+}
+
+uint UIBaseMemorySlider::maxRAMAlw() const
+{
+ return m_uMaxRAMAlw;
+}
+
+uint UIBaseMemorySlider::maxRAM() const
+{
+ return m_uMaxRAM;
+}
+
+void UIBaseMemorySlider::prepare()
+{
+ ulong uFullSize = uiCommon().host().GetMemorySize();
+ CSystemProperties sys = uiCommon().virtualBox().GetSystemProperties();
+ m_uMinRAM = sys.GetMinGuestRAM();
+ m_uMaxRAM = RT_MIN(RT_ALIGN(uFullSize, _1G / _1M), sys.GetMaxGuestRAM());
+
+ /* Come up with some nice round percent boundaries relative to
+ * the system memory. A max of 75% on a 256GB config is ridiculous,
+ * even on an 8GB rig reserving 2GB for the OS is way to conservative.
+ * The max numbers can be estimated using the following program:
+ *
+ * double calcMaxPct(uint64_t cbRam)
+ * {
+ * double cbRamOverhead = cbRam * 0.0390625; // 160 bytes per page.
+ * double cbRamForTheOS = RT_MAX(RT_MIN(_512M, cbRam * 0.25), _64M);
+ * double OSPct = (cbRamOverhead + cbRamForTheOS) * 100.0 / cbRam;
+ * double MaxPct = 100 - OSPct;
+ * return MaxPct;
+ * }
+ *
+ * int main()
+ * {
+ * uint64_t cbRam = _1G;
+ * for (; !(cbRam >> 33); cbRam += _1G)
+ * printf("%8lluGB %.1f%% %8lluKB\n", cbRam >> 30, calcMaxPct(cbRam),
+ * (uint64_t)(cbRam * calcMaxPct(cbRam) / 100.0) >> 20);
+ * for (; !(cbRam >> 51); cbRam <<= 1)
+ * printf("%8lluGB %.1f%% %8lluKB\n", cbRam >> 30, calcMaxPct(cbRam),
+ * (uint64_t)(cbRam * calcMaxPct(cbRam) / 100.0) >> 20);
+ * return 0;
+ * }
+ *
+ * Note. We might wanna put these calculations somewhere global later. */
+
+ /* System RAM amount test: */
+ m_uMaxRAMAlw = (uint)(0.75 * uFullSize);
+ m_uMaxRAMOpt = (uint)(0.50 * uFullSize);
+ if (uFullSize < 3072)
+ /* done */;
+ else if (uFullSize < 4096) /* 3GB */
+ m_uMaxRAMAlw = (uint)(0.80 * uFullSize);
+ else if (uFullSize < 6144) /* 4-5GB */
+ {
+ m_uMaxRAMAlw = (uint)(0.84 * uFullSize);
+ m_uMaxRAMOpt = (uint)(0.60 * uFullSize);
+ }
+ else if (uFullSize < 8192) /* 6-7GB */
+ {
+ m_uMaxRAMAlw = (uint)(0.88 * uFullSize);
+ m_uMaxRAMOpt = (uint)(0.65 * uFullSize);
+ }
+ else if (uFullSize < 16384) /* 8-15GB */
+ {
+ m_uMaxRAMAlw = (uint)(0.90 * uFullSize);
+ m_uMaxRAMOpt = (uint)(0.70 * uFullSize);
+ }
+ else if (uFullSize < 32768) /* 16-31GB */
+ {
+ m_uMaxRAMAlw = (uint)(0.93 * uFullSize);
+ m_uMaxRAMOpt = (uint)(0.75 * uFullSize);
+ }
+ else if (uFullSize < 65536) /* 32-63GB */
+ {
+ m_uMaxRAMAlw = (uint)(0.94 * uFullSize);
+ m_uMaxRAMOpt = (uint)(0.80 * uFullSize);
+ }
+ else if (uFullSize < 131072) /* 64-127GB */
+ {
+ m_uMaxRAMAlw = (uint)(0.95 * uFullSize);
+ m_uMaxRAMOpt = (uint)(0.85 * uFullSize);
+ }
+ else /* 128GB- */
+ {
+ m_uMaxRAMAlw = (uint)(0.96 * uFullSize);
+ m_uMaxRAMOpt = (uint)(0.90 * uFullSize);
+ }
+ /* Now check the calculated maximums are out of the range for the guest
+ * RAM. If so change it accordingly. */
+ m_uMaxRAMAlw = RT_MIN(m_uMaxRAMAlw, m_uMaxRAM);
+ m_uMaxRAMOpt = RT_MIN(m_uMaxRAMOpt, m_uMaxRAM);
+
+ setPageStep(calcPageStep(m_uMaxRAM));
+ setSingleStep(pageStep() / 4);
+ setTickInterval(pageStep());
+ /* Setup the scale so that ticks are at page step boundaries */
+ if (m_uMinRAM >= static_cast<uint>(pageStep()))
+ setMinimum((m_uMinRAM / pageStep()) * pageStep());
+ else
+ setMinimum(m_uMinRAM);
+
+ setMaximum(m_uMaxRAM);
+ setSnappingEnabled(true);
+ setOptimalHint(m_uMinRAM, m_uMaxRAMOpt);
+ setWarningHint(m_uMaxRAMOpt, m_uMaxRAMAlw);
+ setErrorHint(m_uMaxRAMAlw, m_uMaxRAM);
+}
+
+int UIBaseMemorySlider::calcPageStep(int iMaximum) const
+{
+ /* Calculate a suitable page step size for the given max value.
+ * The returned size is so that there will be no more than 32
+ * pages. The minimum returned page size is 4. */
+
+ /* Reasonable max. number of page steps is 32: */
+ uint uPage = ((uint)iMaximum + 31) / 32;
+ /* Make it a power of 2: */
+ uint p = uPage, p2 = 0x1;
+ while ((p >>= 1))
+ p2 <<= 1;
+ if (uPage != p2)
+ p2 <<= 1;
+ if (p2 < 4)
+ p2 = 4;
+ return (int) p2;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIBaseMemoryEditor implementation. *
+*********************************************************************************************************************************/
+
+UIBaseMemoryEditor::UIBaseMemoryEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_iValue(0)
+ , m_pLayout(0)
+ , m_pLabelMemory(0)
+ , m_pSlider(0)
+ , m_pLabelMemoryMin(0)
+ , m_pLabelMemoryMax(0)
+ , m_pSpinBox(0)
+{
+ prepare();
+}
+
+void UIBaseMemoryEditor::setValue(int iValue)
+{
+ /* Update cached value and
+ * slider if value has changed: */
+ if (m_iValue != iValue)
+ {
+ m_iValue = iValue;
+ if (m_pSlider)
+ m_pSlider->setValue(m_iValue);
+ }
+}
+
+int UIBaseMemoryEditor::value() const
+{
+ return m_pSlider ? m_pSlider->value() : m_iValue;
+}
+
+uint UIBaseMemoryEditor::maxRAMOpt() const
+{
+ return m_pSlider ? m_pSlider->maxRAMOpt() : 0;
+}
+
+uint UIBaseMemoryEditor::maxRAMAlw() const
+{
+ return m_pSlider ? m_pSlider->maxRAMAlw() : 0;
+}
+
+int UIBaseMemoryEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabelMemory ? m_pLabelMemory->minimumSizeHint().width() : 0;
+}
+
+void UIBaseMemoryEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIBaseMemoryEditor::retranslateUi()
+{
+ if (m_pLabelMemory)
+ m_pLabelMemory->setText(tr("Base &Memory:"));
+
+ const QString strToolTip(tr("Holds the amount of base memory the virtual machine will have."));
+ if (m_pSlider)
+ m_pSlider->setToolTip(strToolTip);
+ if (m_pSpinBox)
+ {
+ m_pSpinBox->setSuffix(QString(" %1").arg(tr("MB")));
+ m_pSpinBox->setToolTip(strToolTip);
+ }
+
+ if (m_pLabelMemoryMin)
+ {
+ m_pLabelMemoryMin->setText(tr("%1 MB").arg(m_pSlider->minRAM()));
+ m_pLabelMemoryMin->setToolTip(tr("Minimum possible base memory size."));
+ }
+ if (m_pLabelMemoryMax)
+ {
+ m_pLabelMemoryMax->setText(tr("%1 MB").arg(m_pSlider->maxRAM()));
+ m_pLabelMemoryMax->setToolTip(tr("Maximum possible base memory size."));
+ }
+}
+
+void UIBaseMemoryEditor::sltHandleSliderChange()
+{
+ /* Apply spin-box value keeping it's signals disabled: */
+ if (m_pSpinBox && m_pSlider)
+ {
+ m_pSpinBox->blockSignals(true);
+ m_pSpinBox->setValue(m_pSlider->value());
+ m_pSpinBox->blockSignals(false);
+ }
+
+ /* Revalidate to send signal to listener: */
+ revalidate();
+}
+
+void UIBaseMemoryEditor::sltHandleSpinBoxChange()
+{
+ /* Apply slider value keeping it's signals disabled: */
+ if (m_pSpinBox && m_pSlider)
+ {
+ m_pSlider->blockSignals(true);
+ m_pSlider->setValue(m_pSpinBox->value());
+ m_pSlider->blockSignals(false);
+ }
+
+ /* Revalidate to send signal to listener: */
+ revalidate();
+}
+
+void UIBaseMemoryEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create memory label: */
+ m_pLabelMemory = new QLabel(this);
+ if (m_pLabelMemory)
+ {
+ m_pLabelMemory->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabelMemory, 0, 0);
+ }
+
+ /* Create slider layout: */
+ QVBoxLayout *pSliderLayout = new QVBoxLayout;
+ if (pSliderLayout)
+ {
+ pSliderLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create memory slider: */
+ m_pSlider = new UIBaseMemorySlider(this);
+ if (m_pSlider)
+ {
+ m_pSlider->setMinimumWidth(150);
+ connect(m_pSlider, &UIBaseMemorySlider::valueChanged,
+ this, &UIBaseMemoryEditor::sltHandleSliderChange);
+ pSliderLayout->addWidget(m_pSlider);
+ }
+
+ /* Create legend layout: */
+ QHBoxLayout *pLegendLayout = new QHBoxLayout;
+ if (pLegendLayout)
+ {
+ pLegendLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create min label: */
+ m_pLabelMemoryMin = new QLabel(this);
+ if (m_pLabelMemoryMin)
+ pLegendLayout->addWidget(m_pLabelMemoryMin);
+
+ /* Push labels from each other: */
+ pLegendLayout->addStretch();
+
+ /* Create max label: */
+ m_pLabelMemoryMax = new QLabel(this);
+ if (m_pLabelMemoryMax)
+ pLegendLayout->addWidget(m_pLabelMemoryMax);
+
+ /* Add legend layout to slider layout: */
+ pSliderLayout->addLayout(pLegendLayout);
+ }
+
+ /* Add slider layout to main layout: */
+ m_pLayout->addLayout(pSliderLayout, 0, 1, 2, 1);
+ }
+
+ /* Create memory spin-box: */
+ m_pSpinBox = new QSpinBox(this);
+ if (m_pSpinBox)
+ {
+ setFocusProxy(m_pSpinBox);
+ if (m_pLabelMemory)
+ m_pLabelMemory->setBuddy(m_pSpinBox);
+ m_pSpinBox->setMinimum(m_pSlider->minRAM());
+ m_pSpinBox->setMaximum(m_pSlider->maxRAM());
+ connect(m_pSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIBaseMemoryEditor::sltHandleSpinBoxChange);
+ m_pLayout->addWidget(m_pSpinBox, 0, 2);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIBaseMemoryEditor::revalidate()
+{
+ if (m_pSlider)
+ {
+ emit sigValidChanged(m_pSlider->value() < (int)m_pSlider->maxRAMAlw());
+ emit sigValueChanged(m_pSlider->value());
+ }
+}
+
+
+#include "UIBaseMemoryEditor.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBaseMemoryEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBaseMemoryEditor.h
new file mode 100644
index 00000000..b54dd42a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBaseMemoryEditor.h
@@ -0,0 +1,119 @@
+/* $Id: UIBaseMemoryEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIBaseMemoryEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIBaseMemoryEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIBaseMemoryEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QSpinBox;
+class UIBaseMemorySlider;
+
+/** QWidget subclass used as a base memory editor. */
+class SHARED_LIBRARY_STUFF UIBaseMemoryEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value has became @a fValid. */
+ void sigValidChanged(bool fValid);
+ /** Notifies listeners about @a iValue has changed. */
+ void sigValueChanged(int iValue);
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIBaseMemoryEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a iValue. */
+ void setValue(int iValue);
+ /** Returns editor value. */
+ int value() const;
+
+ /** Returns the maximum optimal RAM. */
+ uint maxRAMOpt() const;
+ /** Returns the maximum allowed RAM. */
+ uint maxRAMAlw() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles slider value changes. */
+ void sltHandleSliderChange();
+ /** Handles spin-box value changes. */
+ void sltHandleSpinBoxChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Revalidates and emits validity change signal. */
+ void revalidate();
+
+ /** Holds the value to be selected. */
+ int m_iValue;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the memory label instance. */
+ QLabel *m_pLabelMemory;
+ /** Holds the memory slider instance. */
+ UIBaseMemorySlider *m_pSlider;
+ /** Holds minimum memory label instance. */
+ QLabel *m_pLabelMemoryMin;
+ /** Holds maximum memory label instance. */
+ QLabel *m_pLabelMemoryMax;
+ /** Holds the memory spin-box instance. */
+ QSpinBox *m_pSpinBox;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIBaseMemoryEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBootOrderEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBootOrderEditor.cpp
new file mode 100644
index 00000000..2475b466
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBootOrderEditor.cpp
@@ -0,0 +1,608 @@
+/* $Id: UIBootOrderEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIBootListWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QHeaderView>
+#include <QLabel>
+#include <QRegularExpression>
+#include <QScrollBar>
+
+/* GUI includes: */
+#include "UIBootOrderEditor.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIIconPool.h"
+#include "QIToolBar.h"
+#include "QITreeWidget.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+#include "CSystemProperties.h"
+
+
+/** QITreeWidgetItem extension for our UIBootListWidget. */
+class UIBootListWidgetItem : public QITreeWidgetItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs boot-table item of passed @a enmType. */
+ UIBootListWidgetItem(KDeviceType enmType);
+
+ /** Returns the item type. */
+ KDeviceType deviceType() const;
+
+ /** Performs item translation. */
+ void retranslateUi();
+
+private:
+
+ /** Holds the item type. */
+ KDeviceType m_enmType;
+};
+
+
+/** QITreeWidget subclass used as system settings boot-table. */
+class UIBootListWidget : public QIWithRetranslateUI<QITreeWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about current table row changed. */
+ void sigRowChanged();
+
+public:
+
+ /** Constructs boot-table passing @a pParent to the base-class. */
+ UIBootListWidget(QWidget *pParent = 0);
+
+ /** Defines @a bootItems list. */
+ void setBootItems(const UIBootItemDataList &bootItems);
+ /** Returns boot item list. */
+ UIBootItemDataList bootItems() const;
+
+public slots:
+
+ /** Moves current item up. */
+ void sltMoveItemUp();
+ /** Moves current item down. */
+ void sltMoveItemDown();
+
+protected:
+
+ /** Return size hint. */
+ virtual QSize sizeHint() const;
+ /** Return minimum size hint. */
+ virtual QSize minimumSizeHint() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles drop @a pEvent. */
+ virtual void dropEvent(QDropEvent *pEvent) RT_OVERRIDE;
+
+ /** Returns a QModelIndex object pointing to the next object in the view,
+ * based on the given @a cursorAction and keyboard @a fModifiers. */
+ virtual QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction,
+ Qt::KeyboardModifiers fModifiers) RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Moves item with passed @a index to specified @a iRow. */
+ QModelIndex moveItemTo(const QModelIndex &index, int iRow);
+};
+
+
+/*********************************************************************************************************************************
+* Class UIBootListWidgetItem implementation. *
+*********************************************************************************************************************************/
+
+UIBootListWidgetItem::UIBootListWidgetItem(KDeviceType enmType)
+ : m_enmType(enmType)
+{
+ setCheckState(0, Qt::Unchecked);
+ switch(enmType)
+ {
+ case KDeviceType_Floppy: setIcon(0, UIIconPool::iconSet(":/fd_16px.png")); break;
+ case KDeviceType_DVD: setIcon(0, UIIconPool::iconSet(":/cd_16px.png")); break;
+ case KDeviceType_HardDisk: setIcon(0, UIIconPool::iconSet(":/hd_16px.png")); break;
+ case KDeviceType_Network: setIcon(0, UIIconPool::iconSet(":/nw_16px.png")); break;
+ default: break; /* Shut up, MSC! */
+ }
+ retranslateUi();
+}
+
+KDeviceType UIBootListWidgetItem::deviceType() const
+{
+ return m_enmType;
+}
+
+void UIBootListWidgetItem::retranslateUi()
+{
+ setText(0, gpConverter->toString(m_enmType));
+}
+
+
+/*********************************************************************************************************************************
+* Class UIBootListWidget implementation. *
+*********************************************************************************************************************************/
+
+UIBootListWidget::UIBootListWidget(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QITreeWidget>(pParent)
+{
+ prepare();
+}
+
+void UIBootListWidget::setBootItems(const UIBootItemDataList &bootItems)
+{
+ /* Clear initially: */
+ clear();
+
+ /* Apply internal variables data to QWidget(s): */
+ foreach (const UIBootItemData &data, bootItems)
+ {
+ UIBootListWidgetItem *pItem = new UIBootListWidgetItem(data.m_enmType);
+ pItem->setCheckState(0, data.m_fEnabled ? Qt::Checked : Qt::Unchecked);
+ addTopLevelItem(pItem);
+ }
+
+ /* Make sure at least one is chosen: */
+ if (topLevelItemCount())
+ setCurrentItem(topLevelItem(0));
+
+ /* That changes the size: */
+ updateGeometry();
+}
+
+UIBootItemDataList UIBootListWidget::bootItems() const
+{
+ /* Prepare boot items: */
+ UIBootItemDataList bootItems;
+
+ /* Enumerate all the items we have: */
+ for (int i = 0; i < topLevelItemCount(); ++i)
+ {
+ QTreeWidgetItem *pItem = topLevelItem(i);
+ UIBootItemData bootData;
+ bootData.m_enmType = static_cast<UIBootListWidgetItem*>(pItem)->deviceType();
+ bootData.m_fEnabled = pItem->checkState(0) == Qt::Checked;
+ bootItems << bootData;
+ }
+
+ /* Return boot items: */
+ return bootItems;
+}
+
+void UIBootListWidget::sltMoveItemUp()
+{
+ QModelIndex index = currentIndex();
+ moveItemTo(index, index.row() - 1);
+}
+
+void UIBootListWidget::sltMoveItemDown()
+{
+ QModelIndex index = currentIndex();
+ moveItemTo(index, index.row() + 2);
+}
+
+QSize UIBootListWidget::sizeHint() const
+{
+ return minimumSizeHint();
+}
+
+QSize UIBootListWidget::minimumSizeHint() const
+{
+ const int iH = 2 * frameWidth();
+ const int iW = iH;
+ return QSize(sizeHintForColumn(0) + iW,
+ sizeHintForRow(0) * topLevelItemCount() + iH);
+
+}
+
+void UIBootListWidget::retranslateUi()
+{
+ for (int i = 0; i < topLevelItemCount(); ++i)
+ static_cast<UIBootListWidgetItem*>(topLevelItem(i))->retranslateUi();
+}
+
+void UIBootListWidget::dropEvent(QDropEvent *pEvent)
+{
+ /* Call to base-class: */
+ QITreeWidget::dropEvent(pEvent);
+ /* Separately notify listeners: */
+ emit sigRowChanged();
+}
+
+QModelIndex UIBootListWidget::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers fModifiers)
+{
+ if (fModifiers.testFlag(Qt::ControlModifier))
+ {
+ switch (cursorAction)
+ {
+ case QAbstractItemView::MoveUp:
+ {
+ QModelIndex index = currentIndex();
+ return moveItemTo(index, index.row() - 1);
+ }
+ case QAbstractItemView::MoveDown:
+ {
+ QModelIndex index = currentIndex();
+ return moveItemTo(index, index.row() + 2);
+ }
+ case QAbstractItemView::MovePageUp:
+ {
+ QModelIndex index = currentIndex();
+ return moveItemTo(index, qMax(0, index.row() - verticalScrollBar()->pageStep()));
+ }
+ case QAbstractItemView::MovePageDown:
+ {
+ QModelIndex index = currentIndex();
+ return moveItemTo(index, qMin(model()->rowCount(), index.row() + verticalScrollBar()->pageStep() + 1));
+ }
+ case QAbstractItemView::MoveHome:
+ return moveItemTo(currentIndex(), 0);
+ case QAbstractItemView::MoveEnd:
+ return moveItemTo(currentIndex(), model()->rowCount());
+ default:
+ break;
+ }
+ }
+ return QITreeWidget::moveCursor(cursorAction, fModifiers);
+}
+
+void UIBootListWidget::prepare()
+{
+ header()->hide();
+ setRootIsDecorated(false);
+ setDragDropMode(QAbstractItemView::InternalMove);
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ setDropIndicatorShown(true);
+ connect(this, &UIBootListWidget::currentItemChanged,
+ this, &UIBootListWidget::sigRowChanged);
+}
+
+QModelIndex UIBootListWidget::moveItemTo(const QModelIndex &index, int iRow)
+{
+ /* Check validity: */
+ if (!index.isValid())
+ return QModelIndex();
+
+ /* Check sanity: */
+ if (iRow < 0 || iRow > model()->rowCount())
+ return QModelIndex();
+
+ QPersistentModelIndex oldIndex(index);
+ UIBootListWidgetItem *pItem = static_cast<UIBootListWidgetItem*>(itemFromIndex(oldIndex));
+ insertTopLevelItem(iRow, new UIBootListWidgetItem(pItem->deviceType()));
+ topLevelItem(iRow)->setCheckState(0, pItem->checkState(0));
+ QPersistentModelIndex newIndex = model()->index(iRow, 0);
+ delete takeTopLevelItem(oldIndex.row());
+ setCurrentItem(topLevelItem(newIndex.row()));
+ return QModelIndex(newIndex);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIBootDataTools implementation. *
+*********************************************************************************************************************************/
+
+UIBootItemDataList UIBootDataTools::loadBootItems(const CMachine &comMachine)
+{
+ /* Gather a list of all possible boot items.
+ * Currently, it seems, we are supporting only 4 possible boot device types:
+ * 1. Floppy, 2. DVD-ROM, 3. Hard Disk, 4. Network.
+ * But maximum boot devices count supported by machine should be retrieved
+ * through the ISystemProperties getter. Moreover, possible boot device
+ * types are not listed in some separate Main vector, so we should get them
+ * (randomely?) from the list of all device types. Until there will be a
+ * separate Main getter for list of supported boot device types, this list
+ * will be hard-coded here... */
+ QVector<KDeviceType> possibleBootItems = QVector<KDeviceType>() << KDeviceType_Floppy
+ << KDeviceType_DVD
+ << KDeviceType_HardDisk
+ << KDeviceType_Network;
+ const CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ const int iPossibleBootListSize = qMin((ULONG)4, comProperties.GetMaxBootPosition());
+ possibleBootItems.resize(iPossibleBootListSize);
+
+ /* Prepare boot items: */
+ UIBootItemDataList bootItems;
+
+ /* Gather boot-items of current VM: */
+ QList<KDeviceType> usedBootItems;
+ for (int i = 1; i <= possibleBootItems.size(); ++i)
+ {
+ const KDeviceType enmType = comMachine.GetBootOrder(i);
+ if (enmType != KDeviceType_Null)
+ {
+ usedBootItems << enmType;
+ UIBootItemData data;
+ data.m_enmType = enmType;
+ data.m_fEnabled = true;
+ bootItems << data;
+ }
+ }
+ /* Gather other unique boot-items: */
+ for (int i = 0; i < possibleBootItems.size(); ++i)
+ {
+ const KDeviceType enmType = possibleBootItems.at(i);
+ if (!usedBootItems.contains(enmType))
+ {
+ UIBootItemData data;
+ data.m_enmType = enmType;
+ data.m_fEnabled = false;
+ bootItems << data;
+ }
+ }
+
+ /* Return boot items: */
+ return bootItems;
+}
+
+void UIBootDataTools::saveBootItems(const UIBootItemDataList &bootItems, CMachine &comMachine)
+{
+ bool fSuccess = true;
+ int iBootIndex = 0;
+ for (int i = 0; fSuccess && i < bootItems.size(); ++i)
+ {
+ if (bootItems.at(i).m_fEnabled)
+ {
+ comMachine.SetBootOrder(++iBootIndex, bootItems.at(i).m_enmType);
+ fSuccess = comMachine.isOk();
+ }
+ }
+ for (int i = 0; fSuccess && i < bootItems.size(); ++i)
+ {
+ if (!bootItems.at(i).m_fEnabled)
+ {
+ comMachine.SetBootOrder(++iBootIndex, KDeviceType_Null);
+ fSuccess = comMachine.isOk();
+ }
+ }
+}
+
+QString UIBootDataTools::bootItemsToReadableString(const UIBootItemDataList &bootItems)
+{
+ /* Prepare list: */
+ QStringList list;
+ /* We are reflecting only enabled items: */
+ foreach (const UIBootItemData &bootItem, bootItems)
+ if (bootItem.m_fEnabled)
+ list << gpConverter->toString(bootItem.m_enmType);
+ /* But if list is empty we are adding Null item at least: */
+ if (list.isEmpty())
+ list << gpConverter->toString(KDeviceType_Null);
+ /* Join list to string: */
+ return list.join(", ");
+}
+
+QString UIBootDataTools::bootItemsToSerializedString(const UIBootItemDataList &bootItems)
+{
+ /* Prepare list: */
+ QStringList list;
+ /* This is simple, we are adding '+' before enabled types and '-' before disabled: */
+ foreach (const UIBootItemData &bootItem, bootItems)
+ list << (bootItem.m_fEnabled ? QString("+%1").arg(bootItem.m_enmType) : QString("-%1").arg(bootItem.m_enmType));
+ /* Join list to string: */
+ return list.join(';');
+}
+
+UIBootItemDataList UIBootDataTools::bootItemsFromSerializedString(const QString &strBootItems)
+{
+ /* Prepare list: */
+ UIBootItemDataList list;
+ /* First of all, split passed string to arguments: */
+ const QStringList arguments = strBootItems.split(';');
+ /* Now parse in backward direction, we have added '+' before enabled types and '-' before disabled: */
+ foreach (QString strArgument, arguments)
+ {
+ UIBootItemData data;
+ data.m_fEnabled = strArgument.startsWith('+');
+ strArgument.remove(QRegularExpression("[+-]"));
+ data.m_enmType = static_cast<KDeviceType>(strArgument.toInt());
+ list << data;
+ }
+ /* Return list: */
+ return list;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIBootOrderEditor implementation. *
+*********************************************************************************************************************************/
+
+UIBootOrderEditor::UIBootOrderEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLayout(0)
+ , m_pLabel(0)
+ , m_pTable(0)
+ , m_pToolbar(0)
+ , m_pMoveUp(0)
+ , m_pMoveDown(0)
+{
+ prepare();
+}
+
+void UIBootOrderEditor::setValue(const UIBootItemDataList &guiValue)
+{
+ if (m_pTable)
+ m_pTable->setBootItems(guiValue);
+}
+
+UIBootItemDataList UIBootOrderEditor::value() const
+{
+ return m_pTable ? m_pTable->bootItems() : UIBootItemDataList();
+}
+
+int UIBootOrderEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIBootOrderEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+bool UIBootOrderEditor::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Skip events sent to unrelated objects: */
+ if (m_pTable && pObject != m_pTable)
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
+
+ /* Handle only required event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::FocusIn:
+ case QEvent::FocusOut:
+ {
+ /* On focus in/out events we'd like
+ * to update actions availability: */
+ updateActionAvailability();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
+}
+
+void UIBootOrderEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("&Boot Order:"));
+ if (m_pTable)
+ m_pTable->setWhatsThis(tr("Defines the boot device order. Use the "
+ "checkboxes on the left to enable or disable individual boot devices."
+ "Move items up and down to change the device order."));
+ if (m_pMoveUp)
+ m_pMoveUp->setToolTip(tr("Moves selected boot item up."));
+ if (m_pMoveDown)
+ m_pMoveDown->setToolTip(tr("Moves selected boot item down."));
+}
+
+void UIBootOrderEditor::sltHandleCurrentBootItemChange()
+{
+ /* On current item change signals we'd like
+ * to update actions availability: */
+ updateActionAvailability();
+}
+
+void UIBootOrderEditor::prepare()
+{
+ /* Configure self: */
+ setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create table layout: */
+ QHBoxLayout *pTableLayout = new QHBoxLayout;
+ if (pTableLayout)
+ {
+ pTableLayout->setContentsMargins(0, 0, 0, 0);
+ pTableLayout->setSpacing(1);
+
+ /* Create table: */
+ m_pTable = new UIBootListWidget(this);
+ if (m_pTable)
+ {
+ setFocusProxy(m_pTable);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pTable);
+ m_pTable->setAlternatingRowColors(true);
+ m_pTable->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ connect(m_pTable, &UIBootListWidget::sigRowChanged,
+ this, &UIBootOrderEditor::sltHandleCurrentBootItemChange);
+ pTableLayout->addWidget(m_pTable);
+ }
+
+ /* Create tool-bar: */
+ m_pToolbar = new QIToolBar(this);
+ if (m_pToolbar)
+ {
+ m_pToolbar->setIconSize(QSize(16, 16));
+ m_pToolbar->setOrientation(Qt::Vertical);
+
+ /* Create Up action: */
+ m_pMoveUp = m_pToolbar->addAction(UIIconPool::iconSet(":/list_moveup_16px.png",
+ ":/list_moveup_disabled_16px.png"),
+ QString(), m_pTable, &UIBootListWidget::sltMoveItemUp);
+ /* Create Down action: */
+ m_pMoveDown = m_pToolbar->addAction(UIIconPool::iconSet(":/list_movedown_16px.png",
+ ":/list_movedown_disabled_16px.png"),
+ QString(), m_pTable, &UIBootListWidget::sltMoveItemDown);
+
+ /* Add tool-bar into table layout: */
+ pTableLayout->addWidget(m_pToolbar);
+ }
+
+ /* Add table layout to main layout: */
+ m_pLayout->addLayout(pTableLayout, 0, 1, 4, 1);
+ }
+ }
+
+ /* Update initial action availability: */
+ updateActionAvailability();
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIBootOrderEditor::updateActionAvailability()
+{
+ /* Update move up/down actions: */
+ QTreeWidgetItem *pCurrentTopLevelItem = m_pTable->currentItem();
+ const int iCurrentTopLevelItem = m_pTable->indexOfTopLevelItem(pCurrentTopLevelItem);
+ if (m_pTable && m_pMoveUp && iCurrentTopLevelItem != -1)
+ m_pMoveUp->setEnabled(m_pTable->hasFocus() && iCurrentTopLevelItem > 0);
+ if (m_pTable && m_pMoveDown && iCurrentTopLevelItem != -1)
+ m_pMoveDown->setEnabled(m_pTable->hasFocus() && iCurrentTopLevelItem < m_pTable->topLevelItemCount() - 1);
+}
+
+#include "UIBootOrderEditor.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBootOrderEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBootOrderEditor.h
new file mode 100644
index 00000000..a15d0d0b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIBootOrderEditor.h
@@ -0,0 +1,151 @@
+/* $Id: UIBootOrderEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIBootListWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIBootOrderEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIBootOrderEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QIToolBar;
+class UIBootListWidget;
+class CMachine;
+
+
+/** Boot item data structure. */
+struct UIBootItemData
+{
+ /** Constructs item data. */
+ UIBootItemData()
+ : m_enmType(KDeviceType_Null)
+ , m_fEnabled(false)
+ {}
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool operator==(const UIBootItemData &another) const
+ {
+ return true
+ && (m_enmType == another.m_enmType)
+ && (m_fEnabled == another.m_fEnabled)
+ ;
+ }
+
+ /** Holds the device type. */
+ KDeviceType m_enmType;
+ /** Holds whether the device enabled. */
+ bool m_fEnabled;
+};
+typedef QList<UIBootItemData> UIBootItemDataList;
+Q_DECLARE_METATYPE(UIBootItemDataList);
+
+
+/** Boot data tools namespace. */
+namespace UIBootDataTools
+{
+ /** Loads item list for passed @a comMachine. */
+ SHARED_LIBRARY_STUFF UIBootItemDataList loadBootItems(const CMachine &comMachine);
+ /** Saves @a bootItems list to passed @a comMachine. */
+ SHARED_LIBRARY_STUFF void saveBootItems(const UIBootItemDataList &bootItems, CMachine &comMachine);
+
+ /** Converts passed @a bootItems list into human readable string. */
+ SHARED_LIBRARY_STUFF QString bootItemsToReadableString(const UIBootItemDataList &bootItems);
+
+ /** Performs serialization for passed @a bootItems list. */
+ SHARED_LIBRARY_STUFF QString bootItemsToSerializedString(const UIBootItemDataList &bootItems);
+ /** Performs deserialization for passed @a strBootItems string. */
+ SHARED_LIBRARY_STUFF UIBootItemDataList bootItemsFromSerializedString(const QString &strBootItems);
+}
+using namespace UIBootDataTools;
+
+
+/** QWidget subclass used as boot order editor. */
+class SHARED_LIBRARY_STUFF UIBootOrderEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIBootOrderEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a guiValue. */
+ void setValue(const UIBootItemDataList &guiValue);
+ /** Returns editor value. */
+ UIBootItemDataList value() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Preprocesses Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles current item change. */
+ void sltHandleCurrentBootItemChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates action availability: */
+ void updateActionAvailability();
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the table instance. */
+ UIBootListWidget *m_pTable;
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolbar;
+ /** Holds the move up action. */
+ QAction *m_pMoveUp;
+ /** Holds the move down action. */
+ QAction *m_pMoveDown;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIBootOrderEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIChipsetEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIChipsetEditor.cpp
new file mode 100644
index 00000000..a1284237
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIChipsetEditor.cpp
@@ -0,0 +1,172 @@
+/* $Id: UIChipsetEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIChipsetEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIChipsetEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIChipsetEditor::UIChipsetEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KChipsetType_Max)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UIChipsetEditor::setValue(KChipsetType enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+KChipsetType UIChipsetEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<KChipsetType>() : m_enmValue;
+}
+
+int UIChipsetEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIChipsetEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIChipsetEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("&Chipset:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const KChipsetType enmType = m_pCombo->itemData(i).value<KChipsetType>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Selects the chipset to be emulated in this virtual machine. Note that the ICH9 chipset "
+ "emulation is experimental and not recommended except for guest systems (such as Mac OS X) "
+ "which require it."));
+ }
+}
+
+void UIChipsetEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ connect(m_pCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIChipsetEditor::sigValueChanged);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIChipsetEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Load currently supported values: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedChipsetTypes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KChipsetType_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const KChipsetType &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIChipsetEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIChipsetEditor.h
new file mode 100644
index 00000000..d5d8b4a6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIChipsetEditor.h
@@ -0,0 +1,103 @@
+/* $Id: UIChipsetEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIChipsetEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIChipsetEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIChipsetEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as a chipset editor. */
+class SHARED_LIBRARY_STUFF UIChipsetEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value change. */
+ void sigValueChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIChipsetEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KChipsetType enmValue);
+ /** Returns editor value. */
+ KChipsetType value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KChipsetType> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ KChipsetType m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KChipsetType> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIChipsetEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIColorThemeEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIColorThemeEditor.cpp
new file mode 100644
index 00000000..097b7221
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIColorThemeEditor.cpp
@@ -0,0 +1,147 @@
+/* $Id: UIColorThemeEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIColorThemeEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UIColorThemeEditor.h"
+#include "UIConverter.h"
+
+
+UIColorThemeEditor::UIColorThemeEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(UIColorThemeType_Auto)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UIColorThemeEditor::setValue(UIColorThemeType enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+UIColorThemeType UIColorThemeEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<UIColorThemeType>() : m_enmValue;
+}
+
+void UIColorThemeEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Color &Theme:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const UIColorThemeType enmType = m_pCombo->itemData(i).value<UIColorThemeType>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Selects the color theme. It can be Light, Dark or automatically detected (default)."));
+ }
+}
+
+void UIColorThemeEditor::prepare()
+{
+ /* Create main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ pLayout->addWidget(m_pLabel, 0, 0);
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIColorThemeEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Get possible values: */
+ QList<UIColorThemeType> possibleValues;
+ possibleValues << UIColorThemeType_Auto
+ << UIColorThemeType_Light
+ << UIColorThemeType_Dark;
+
+ /* Update combo with all the possible values: */
+ foreach (const UIColorThemeType &enmType, possibleValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIColorThemeEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIColorThemeEditor.h
new file mode 100644
index 00000000..64070249
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIColorThemeEditor.h
@@ -0,0 +1,82 @@
+/* $Id: UIColorThemeEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIColorThemeEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIColorThemeEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIColorThemeEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QLabel;
+
+/** QWidget subclass used as a color theme editor. */
+class SHARED_LIBRARY_STUFF UIColorThemeEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIColorThemeEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(UIColorThemeType enmValue);
+ /** Returns editor value. */
+ UIColorThemeType value() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ UIColorThemeType m_enmValue;
+
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIColorThemeEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDefaultMachineFolderEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDefaultMachineFolderEditor.cpp
new file mode 100644
index 00000000..6411bfab
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDefaultMachineFolderEditor.cpp
@@ -0,0 +1,116 @@
+/* $Id: UIDefaultMachineFolderEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDefaultMachineFolderEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDefaultMachineFolderEditor.h"
+#include "UIFilePathSelector.h"
+
+
+UIDefaultMachineFolderEditor::UIDefaultMachineFolderEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_strValue(QString())
+ , m_pLayout(0)
+ , m_pLabel(0)
+ , m_pSelector(0)
+{
+ prepare();
+}
+
+void UIDefaultMachineFolderEditor::setValue(const QString &strValue)
+{
+ /* Update cached value and editor
+ * if value has changed: */
+ if (m_strValue != strValue)
+ {
+ m_strValue = strValue;
+ if (m_pSelector)
+ m_pSelector->setPath(m_strValue);
+ }
+}
+
+QString UIDefaultMachineFolderEditor::value() const
+{
+ return m_pSelector ? m_pSelector->path() : m_strValue;
+}
+
+int UIDefaultMachineFolderEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIDefaultMachineFolderEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIDefaultMachineFolderEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Default &Machine Folder:"));
+ if (m_pSelector)
+ m_pSelector->setToolTip(tr("Holds the path to the default virtual machine folder. This folder is used, "
+ "if not explicitly specified otherwise, when creating new virtual machines."));
+}
+
+void UIDefaultMachineFolderEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create selector: */
+ m_pSelector = new UIFilePathSelector(this);
+ if (m_pSelector)
+ {
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pSelector);
+ m_pSelector->setInitialPath(uiCommon().homeFolder());
+
+ m_pLayout->addWidget(m_pSelector, 0, 1);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDefaultMachineFolderEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDefaultMachineFolderEditor.h
new file mode 100644
index 00000000..dcddef3c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDefaultMachineFolderEditor.h
@@ -0,0 +1,87 @@
+/* $Id: UIDefaultMachineFolderEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIDefaultMachineFolderEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIDefaultMachineFolderEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIDefaultMachineFolderEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class UIFilePathSelector;
+
+/** QWidget subclass used as a default machine folder editor. */
+class SHARED_LIBRARY_STUFF UIDefaultMachineFolderEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIDefaultMachineFolderEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a strValue. */
+ void setValue(const QString &strValue);
+ /** Returns editor value. */
+ QString value() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the value to be set. */
+ QString m_strValue;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the selector instance. */
+ UIFilePathSelector *m_pSelector;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIDefaultMachineFolderEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDescriptionEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDescriptionEditor.cpp
new file mode 100644
index 00000000..ca5058c4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDescriptionEditor.cpp
@@ -0,0 +1,91 @@
+/* $Id: UIDescriptionEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDescriptionEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QTextEdit>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIDescriptionEditor.h"
+
+
+UIDescriptionEditor::UIDescriptionEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pTextEdit(0)
+{
+ prepare();
+}
+
+void UIDescriptionEditor::setValue(const QString &strValue)
+{
+ /* Update cached value and
+ * text-edit if value has changed: */
+ if (m_strValue != strValue)
+ {
+ m_strValue = strValue;
+ if (m_pTextEdit)
+ m_pTextEdit->setPlainText(strValue);
+ }
+}
+
+QString UIDescriptionEditor::value() const
+{
+ return m_pTextEdit ? m_pTextEdit->toPlainText() : m_strValue;
+}
+
+void UIDescriptionEditor::retranslateUi()
+{
+ if (m_pTextEdit)
+ m_pTextEdit->setToolTip(tr("Holds the description of the virtual machine. The description field is useful "
+ "for commenting on configuration details of the installed guest OS."));
+}
+
+void UIDescriptionEditor::prepare()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare text-edit: */
+ m_pTextEdit = new QTextEdit(this);
+ if (m_pTextEdit)
+ {
+ setFocusProxy(m_pTextEdit);
+ m_pTextEdit->setAcceptRichText(false);
+#ifdef VBOX_WS_MAC
+ m_pTextEdit->setMinimumHeight(150);
+#endif
+
+ pLayout->addWidget(m_pTextEdit);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDescriptionEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDescriptionEditor.h
new file mode 100644
index 00000000..bfd0ff5b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDescriptionEditor.h
@@ -0,0 +1,72 @@
+/* $Id: UIDescriptionEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIDescriptionEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIDescriptionEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIDescriptionEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QTextEdit;
+
+/** QWidget subclass used as machine description editor. */
+class SHARED_LIBRARY_STUFF UIDescriptionEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIDescriptionEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a strValue. */
+ void setValue(const QString &strValue);
+ /** Returns editor value. */
+ QString value() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the value to be set. */
+ QString m_strValue;
+
+ /** Holds the check-box instance. */
+ QTextEdit *m_pTextEdit;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIDescriptionEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDiskEncryptionSettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDiskEncryptionSettingsEditor.cpp
new file mode 100644
index 00000000..530e0ba4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDiskEncryptionSettingsEditor.cpp
@@ -0,0 +1,288 @@
+/* $Id: UIDiskEncryptionSettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDiskEncryptionSettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QComboBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+
+/* GUI includes: */
+#include "UIConverter.h"
+#include "UIDiskEncryptionSettingsEditor.h"
+
+
+UIDiskEncryptionSettingsEditor::UIDiskEncryptionSettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fFeatureEnabled(false)
+ , m_enmCipherType(UIDiskEncryptionCipherType_Max)
+ , m_pCheckboxFeature(0)
+ , m_pWidgetSettings(0)
+ , m_pLabelCipherType(0)
+ , m_pComboCipherType(0)
+ , m_pLabelPassword1(0)
+ , m_pEditorPassword1(0)
+ , m_pLabelPassword2(0)
+ , m_pEditorPassword2(0)
+{
+ prepare();
+}
+
+void UIDiskEncryptionSettingsEditor::setFeatureEnabled(bool fEnabled)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fFeatureEnabled != fEnabled)
+ {
+ m_fFeatureEnabled = fEnabled;
+ if (m_pCheckboxFeature)
+ {
+ m_pCheckboxFeature->setChecked(m_fFeatureEnabled);
+ sltHandleFeatureToggled(m_pCheckboxFeature->isChecked());
+ }
+ }
+}
+
+bool UIDiskEncryptionSettingsEditor::isFeatureEnabled() const
+{
+ return m_pCheckboxFeature ? m_pCheckboxFeature->isChecked() : m_fFeatureEnabled;
+}
+
+void UIDiskEncryptionSettingsEditor::setCipherType(const UIDiskEncryptionCipherType &enmType)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmCipherType != enmType)
+ {
+ m_enmCipherType = enmType;
+ repopulateCombo();
+ }
+}
+
+UIDiskEncryptionCipherType UIDiskEncryptionSettingsEditor::cipherType() const
+{
+ return m_pComboCipherType ? m_pComboCipherType->currentData().value<UIDiskEncryptionCipherType>() : m_enmCipherType;
+}
+
+QString UIDiskEncryptionSettingsEditor::password1() const
+{
+ return m_pEditorPassword1 ? m_pEditorPassword1->text() : m_strPassword1;
+}
+
+QString UIDiskEncryptionSettingsEditor::password2() const
+{
+ return m_pEditorPassword2 ? m_pEditorPassword2->text() : m_strPassword2;
+}
+
+void UIDiskEncryptionSettingsEditor::retranslateUi()
+{
+ if (m_pCheckboxFeature)
+ {
+ m_pCheckboxFeature->setText(tr("En&able Disk Encryption"));
+ m_pCheckboxFeature->setToolTip(tr("When checked, disks attached to this virtual machine will be encrypted."));
+ }
+
+ if (m_pLabelCipherType)
+ m_pLabelCipherType->setText(tr("Disk Encryption C&ipher:"));
+ if (m_pComboCipherType)
+ {
+ for (int iIndex = 0; iIndex < m_pComboCipherType->count(); ++iIndex)
+ {
+ const UIDiskEncryptionCipherType enmType = m_pComboCipherType->itemData(iIndex).value<UIDiskEncryptionCipherType>();
+ m_pComboCipherType->setItemText(iIndex, gpConverter->toString(enmType));
+ }
+ m_pComboCipherType->setToolTip(tr("Holds the cipher to be used for encrypting the virtual machine disks."));
+ }
+
+ if (m_pLabelPassword1)
+ m_pLabelPassword1->setText(tr("E&nter New Password:"));
+ if (m_pEditorPassword1)
+ m_pEditorPassword1->setToolTip(tr("Holds the encryption password for disks attached to this virtual machine."));
+ if (m_pLabelPassword2)
+ m_pLabelPassword2->setText(tr("C&onfirm New Password:"));
+ if (m_pEditorPassword2)
+ m_pEditorPassword2->setToolTip(tr("Confirms the disk encryption password."));
+
+ /* Translate Cipher type combo: */
+ m_pComboCipherType->setItemText(0, tr("Leave Unchanged", "cipher type"));
+}
+
+void UIDiskEncryptionSettingsEditor::sltHandleFeatureToggled(bool fEnabled)
+{
+ /* Update widget availability: */
+ if (m_pWidgetSettings)
+ m_pWidgetSettings->setEnabled(fEnabled);
+
+ /* Notify listeners: */
+ emit sigStatusChanged();
+}
+
+void UIDiskEncryptionSettingsEditor::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIDiskEncryptionSettingsEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ pLayout->setColumnStretch(1, 1);
+
+ /* Prepare 'feature' check-box: */
+ m_pCheckboxFeature = new QCheckBox(this);
+ if (m_pCheckboxFeature)
+ pLayout->addWidget(m_pCheckboxFeature, 0, 0, 1, 2);
+
+ /* Prepare 20-px shifting spacer: */
+ QSpacerItem *pSpacerItem = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacerItem)
+ pLayout->addItem(pSpacerItem, 1, 0);
+
+ /* Prepare 'settings' widget: */
+ m_pWidgetSettings = new QWidget(this);
+ if (m_pWidgetSettings)
+ {
+ /* Prepare encryption settings widget layout: */
+ QGridLayout *m_pLayoutSettings = new QGridLayout(m_pWidgetSettings);
+ if (m_pLayoutSettings)
+ {
+ m_pLayoutSettings->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare encryption cipher label: */
+ m_pLabelCipherType = new QLabel(m_pWidgetSettings);
+ if (m_pLabelCipherType)
+ {
+ m_pLabelCipherType->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutSettings->addWidget(m_pLabelCipherType, 0, 0);
+ }
+ /* Prepare encryption cipher combo: */
+ m_pComboCipherType = new QComboBox(m_pWidgetSettings);
+ if (m_pComboCipherType)
+ {
+ if (m_pLabelCipherType)
+ m_pLabelCipherType->setBuddy(m_pComboCipherType);
+ m_pLayoutSettings->addWidget(m_pComboCipherType, 0, 1);
+ }
+
+ /* Prepare encryption password label: */
+ m_pLabelPassword1 = new QLabel(m_pWidgetSettings);
+ if (m_pLabelPassword1)
+ {
+ m_pLabelPassword1->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutSettings->addWidget(m_pLabelPassword1, 1, 0);
+ }
+ /* Prepare encryption password editor: */
+ m_pEditorPassword1 = new QLineEdit(m_pWidgetSettings);
+ if (m_pEditorPassword1)
+ {
+ if (m_pLabelPassword1)
+ m_pLabelPassword1->setBuddy(m_pEditorPassword1);
+ m_pEditorPassword1->setEchoMode(QLineEdit::Password);
+
+ m_pLayoutSettings->addWidget(m_pEditorPassword1, 1, 1);
+ }
+
+ /* Prepare encryption confirm password label: */
+ m_pLabelPassword2 = new QLabel(m_pWidgetSettings);
+ if (m_pLabelPassword2)
+ {
+ m_pLabelPassword2->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutSettings->addWidget(m_pLabelPassword2, 2, 0);
+ }
+ /* Prepare encryption confirm password editor: */
+ m_pEditorPassword2 = new QLineEdit(m_pWidgetSettings);
+ if (m_pEditorPassword2)
+ {
+ if (m_pLabelPassword2)
+ m_pLabelPassword2->setBuddy(m_pEditorPassword2);
+ m_pEditorPassword2->setEchoMode(QLineEdit::Password);
+
+ m_pLayoutSettings->addWidget(m_pEditorPassword2, 2, 1);
+ }
+ }
+
+ pLayout->addWidget(m_pWidgetSettings, 1, 1, 1, 2);
+ }
+ }
+
+ /* Update widget availability: */
+ if (m_pCheckboxFeature)
+ sltHandleFeatureToggled(m_pCheckboxFeature->isChecked());
+}
+
+void UIDiskEncryptionSettingsEditor::prepareConnections()
+{
+ if (m_pCheckboxFeature)
+ connect(m_pCheckboxFeature, &QCheckBox::toggled,
+ this, &UIDiskEncryptionSettingsEditor::sltHandleFeatureToggled);
+ if (m_pComboCipherType)
+ connect(m_pComboCipherType, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIDiskEncryptionSettingsEditor::sigCipherChanged);
+ if (m_pEditorPassword1)
+ connect(m_pEditorPassword1, &QLineEdit::textEdited,
+ this, &UIDiskEncryptionSettingsEditor::sigPasswordChanged);
+ if (m_pEditorPassword2)
+ connect(m_pEditorPassword2, &QLineEdit::textEdited,
+ this, &UIDiskEncryptionSettingsEditor::sigPasswordChanged);
+}
+
+void UIDiskEncryptionSettingsEditor::repopulateCombo()
+{
+ if (m_pComboCipherType)
+ {
+ /* Clear combo first of all: */
+ m_pComboCipherType->clear();
+
+ /// @todo get supported auth types (API not implemented), not hardcoded!
+ QVector<UIDiskEncryptionCipherType> cipherTypes =
+ QVector<UIDiskEncryptionCipherType>() << UIDiskEncryptionCipherType_Unchanged
+ << UIDiskEncryptionCipherType_XTS256
+ << UIDiskEncryptionCipherType_XTS128;
+
+ /* Take into account currently cached value: */
+ if (!cipherTypes.contains(m_enmCipherType))
+ cipherTypes.prepend(m_enmCipherType);
+
+ /* Populate combo finally: */
+ foreach (const UIDiskEncryptionCipherType &enmType, cipherTypes)
+ m_pComboCipherType->addItem(gpConverter->toString(enmType), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pComboCipherType->findData(QVariant::fromValue(m_enmCipherType));
+ if (iIndex != -1)
+ m_pComboCipherType->setCurrentIndex(iIndex);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDiskEncryptionSettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDiskEncryptionSettingsEditor.h
new file mode 100644
index 00000000..41f79461
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDiskEncryptionSettingsEditor.h
@@ -0,0 +1,136 @@
+/* $Id: UIDiskEncryptionSettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIDiskEncryptionSettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIDiskEncryptionSettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIDiskEncryptionSettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* COM includes: */
+#include "UIExtraDataDefs.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QComboBox;
+class QLabel;
+class QLineEdit;
+class QWidget;
+
+/** QWidget subclass used as a disk encryption settings editor. */
+class SHARED_LIBRARY_STUFF UIDiskEncryptionSettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notify listeners about status changed. */
+ void sigStatusChanged();
+ /** Notify listeners about cipher changed. */
+ void sigCipherChanged();
+ /** Notify listeners about password changed. */
+ void sigPasswordChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIDiskEncryptionSettingsEditor(QWidget *pParent = 0);
+
+ /** Defines whether feature is @a fEnabled. */
+ void setFeatureEnabled(bool fEnabled);
+ /** Returns whether feature is enabled. */
+ bool isFeatureEnabled() const;
+
+ /** Defines cipher @a enmType. */
+ void setCipherType(const UIDiskEncryptionCipherType &enmType);
+ /** Returns cipher type. */
+ UIDiskEncryptionCipherType cipherType() const;
+
+ /** Returns password 1. */
+ QString password1() const;
+ /** Returns password 2. */
+ QString password2() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles whether VRDE is @a fEnabled. */
+ void sltHandleFeatureToggled(bool fEnabled);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Repopulates combo-box. */
+ void repopulateCombo();
+
+ /** @name Values
+ * @{ */
+ /** Holds whether feature is enabled. */
+ bool m_fFeatureEnabled;
+ /** Holds the cipher type. */
+ UIDiskEncryptionCipherType m_enmCipherType;
+ /** Holds the password 1. */
+ QString m_strPassword1;
+ /** Holds the password 2. */
+ QString m_strPassword2;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the feature check-box instance. */
+ QCheckBox *m_pCheckboxFeature;
+ /** Holds the settings widget instance. */
+ QWidget *m_pWidgetSettings;
+ /** Holds the cipher type label instance. */
+ QLabel *m_pLabelCipherType;
+ /** Holds the cipher type combo instance. */
+ QComboBox *m_pComboCipherType;
+ /** Holds the password 1 label instance. */
+ QLabel *m_pLabelPassword1;
+ /** Holds the password 1 editor instance. */
+ QLineEdit *m_pEditorPassword1;
+ /** Holds the password 2 label instance. */
+ QLabel *m_pLabelPassword2;
+ /** Holds the password 2 editor instance. */
+ QLineEdit *m_pEditorPassword2;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIDiskEncryptionSettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayFeaturesEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayFeaturesEditor.cpp
new file mode 100644
index 00000000..1a588f8a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayFeaturesEditor.cpp
@@ -0,0 +1,153 @@
+/* $Id: UIDisplayFeaturesEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDisplayFeaturesEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UIDisplayFeaturesEditor.h"
+#ifdef VBOX_WS_X11
+# include "VBoxUtils-x11.h"
+#endif
+
+
+UIDisplayFeaturesEditor::UIDisplayFeaturesEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fActivateOnMouseHover(false)
+ , m_fDisableHostScreenSaver(false)
+ , m_pLabel(0)
+ , m_pCheckBoxActivateOnMouseHover(0)
+ , m_pCheckBoxDisableHostScreenSaver(0)
+{
+ prepare();
+}
+
+void UIDisplayFeaturesEditor::setActivateOnMouseHover(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fActivateOnMouseHover != fOn)
+ {
+ m_fActivateOnMouseHover = fOn;
+ if (m_pCheckBoxActivateOnMouseHover)
+ m_pCheckBoxActivateOnMouseHover->setCheckState(m_fActivateOnMouseHover ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIDisplayFeaturesEditor::activateOnMouseHover() const
+{
+ return m_pCheckBoxActivateOnMouseHover
+ ? m_pCheckBoxActivateOnMouseHover->checkState() == Qt::Checked
+ : m_fActivateOnMouseHover;
+}
+
+void UIDisplayFeaturesEditor::setDisableHostScreenSaver(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fDisableHostScreenSaver != fOn)
+ {
+ m_fDisableHostScreenSaver = fOn;
+ if (m_pCheckBoxDisableHostScreenSaver)
+ m_pCheckBoxDisableHostScreenSaver->setCheckState(m_fDisableHostScreenSaver ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIDisplayFeaturesEditor::disableHostScreenSaver() const
+{
+ return m_pCheckBoxDisableHostScreenSaver
+ ? m_pCheckBoxDisableHostScreenSaver->checkState() == Qt::Checked
+ : m_fDisableHostScreenSaver;
+}
+
+int UIDisplayFeaturesEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIDisplayFeaturesEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIDisplayFeaturesEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Extended Features:"));
+
+ if (m_pCheckBoxActivateOnMouseHover)
+ {
+ m_pCheckBoxActivateOnMouseHover->setText(tr("&Raise Window Under Mouse Pointer"));
+ m_pCheckBoxActivateOnMouseHover->setToolTip(tr("When checked, machine windows will be raised "
+ "when the mouse pointer moves over them."));
+ }
+
+ if (m_pCheckBoxDisableHostScreenSaver)
+ {
+ m_pCheckBoxDisableHostScreenSaver->setText(tr("&Disable Host Screen Saver"));
+ m_pCheckBoxDisableHostScreenSaver->setToolTip(tr("When checked, screen saver of "
+ "the host OS is disabled."));
+ }
+}
+
+void UIDisplayFeaturesEditor::prepare()
+{
+ /* Prepare main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Prepare label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+ /* Prepare 'activate on mouse hover' check-box: */
+ m_pCheckBoxActivateOnMouseHover = new QCheckBox(this);
+ if (m_pCheckBoxActivateOnMouseHover)
+ m_pLayout->addWidget(m_pCheckBoxActivateOnMouseHover, 0, 1);
+ /* Prepare 'disable host screen saver' check-box: */
+#if defined(VBOX_WS_WIN)
+ m_pCheckBoxDisableHostScreenSaver = new QCheckBox(this);
+#elif defined(VBOX_WS_X11)
+ if (NativeWindowSubsystem::X11CheckDBusScreenSaverServices())
+ m_pCheckBoxDisableHostScreenSaver = new QCheckBox(this);
+#endif /* VBOX_WS_X11 */
+ if (m_pCheckBoxDisableHostScreenSaver)
+ m_pLayout->addWidget(m_pCheckBoxDisableHostScreenSaver, 1, 1);
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayFeaturesEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayFeaturesEditor.h
new file mode 100644
index 00000000..71c6d488
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayFeaturesEditor.h
@@ -0,0 +1,98 @@
+/* $Id: UIDisplayFeaturesEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIDisplayFeaturesEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIDisplayFeaturesEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIDisplayFeaturesEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as display features editor. */
+class SHARED_LIBRARY_STUFF UIDisplayFeaturesEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIDisplayFeaturesEditor(QWidget *pParent = 0);
+
+ /** Defines whether 'activate on mouse hover' feature in @a fOn. */
+ void setActivateOnMouseHover(bool fOn);
+ /** Returns 'activate on mouse hover' feature value. */
+ bool activateOnMouseHover() const;
+
+ /** Defines whether 'disable host screen-saver' feature in @a fOn. */
+ void setDisableHostScreenSaver(bool fOn);
+ /** Returns 'disable host screen-saver' feature value. */
+ bool disableHostScreenSaver() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Values
+ * @{ */
+ /** Holds the 'activate on mouse hover' feature value. */
+ bool m_fActivateOnMouseHover;
+ /** Holds the 'disable host screen-saver' feature value. */
+ bool m_fDisableHostScreenSaver;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the check-box instance. */
+ QCheckBox *m_pCheckBoxActivateOnMouseHover;
+ /** Holds the check-box instance. */
+ QCheckBox *m_pCheckBoxDisableHostScreenSaver;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIDisplayFeaturesEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayScreenFeaturesEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayScreenFeaturesEditor.cpp
new file mode 100644
index 00000000..419f2125
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayScreenFeaturesEditor.cpp
@@ -0,0 +1,118 @@
+/* $Id: UIDisplayScreenFeaturesEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDisplayScreenFeaturesEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UIDisplayScreenFeaturesEditor.h"
+
+
+UIDisplayScreenFeaturesEditor::UIDisplayScreenFeaturesEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fEnable3DAcceleration(false)
+ , m_pLabel(0)
+ , m_pCheckBoxEnable3DAcceleration(0)
+{
+ prepare();
+}
+
+void UIDisplayScreenFeaturesEditor::setEnable3DAcceleration(bool fOn)
+{
+ if (m_pCheckBoxEnable3DAcceleration)
+ {
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnable3DAcceleration != fOn)
+ {
+ m_fEnable3DAcceleration = fOn;
+ m_pCheckBoxEnable3DAcceleration->setCheckState(m_fEnable3DAcceleration ? Qt::Checked : Qt::Unchecked);
+ }
+ }
+}
+
+bool UIDisplayScreenFeaturesEditor::isEnabled3DAcceleration() const
+{
+ return m_pCheckBoxEnable3DAcceleration
+ ? m_pCheckBoxEnable3DAcceleration->checkState() == Qt::Checked
+ : m_fEnable3DAcceleration;
+}
+
+int UIDisplayScreenFeaturesEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIDisplayScreenFeaturesEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIDisplayScreenFeaturesEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Extended Features:"));
+ if (m_pCheckBoxEnable3DAcceleration)
+ {
+ m_pCheckBoxEnable3DAcceleration->setText(tr("Enable &3D Acceleration"));
+ m_pCheckBoxEnable3DAcceleration->setToolTip(tr("When checked, the virtual machine will be given access "
+ "to the 3D graphics capabilities available on the host."));
+ }
+}
+
+void UIDisplayScreenFeaturesEditor::prepare()
+{
+ /* Prepare main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Prepare label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+ /* Prepare 'enable 3D acceleration' check-box: */
+ m_pCheckBoxEnable3DAcceleration = new QCheckBox(this);
+ if (m_pCheckBoxEnable3DAcceleration)
+ m_pLayout->addWidget(m_pCheckBoxEnable3DAcceleration, 0, 1);
+ }
+
+ /* Prepare connections: */
+ connect(m_pCheckBoxEnable3DAcceleration, &QCheckBox::stateChanged,
+ this, &UIDisplayScreenFeaturesEditor::sig3DAccelerationFeatureStatusChange);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayScreenFeaturesEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayScreenFeaturesEditor.h
new file mode 100644
index 00000000..8e67e183
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDisplayScreenFeaturesEditor.h
@@ -0,0 +1,94 @@
+/* $Id: UIDisplayScreenFeaturesEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIDisplayScreenFeaturesEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIDisplayScreenFeaturesEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIDisplayScreenFeaturesEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as machine display screen features editor. */
+class SHARED_LIBRARY_STUFF UIDisplayScreenFeaturesEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about 'enable 3D acceleration' feature status changed. */
+ void sig3DAccelerationFeatureStatusChange();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIDisplayScreenFeaturesEditor(QWidget *pParent = 0);
+
+ /** Defines whether 'enable 3D acceleration' feature in @a fOn. */
+ void setEnable3DAcceleration(bool fOn);
+ /** Returns 'enable 3D acceleration' feature value. */
+ bool isEnabled3DAcceleration() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Values
+ * @{ */
+ /** Holds the 'enable 3D acceleration' feature value. */
+ bool m_fEnable3DAcceleration;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the 'enable 3D acceleration' check-box instance. */
+ QCheckBox *m_pCheckBoxEnable3DAcceleration;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIDisplayScreenFeaturesEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDragAndDropEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDragAndDropEditor.cpp
new file mode 100644
index 00000000..32b9ef0d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDragAndDropEditor.cpp
@@ -0,0 +1,169 @@
+/* $Id: UIDragAndDropEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIDragAndDropEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIDragAndDropEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIDragAndDropEditor::UIDragAndDropEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KDnDMode_Max)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UIDragAndDropEditor::setValue(KDnDMode enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+KDnDMode UIDragAndDropEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<KDnDMode>() : m_enmValue;
+}
+
+int UIDragAndDropEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIDragAndDropEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIDragAndDropEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("D&rag'n'Drop:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const KDnDMode enmType = m_pCombo->itemData(i).value<KDnDMode>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Holds which data will be copied between the guest and the host OS by drag'n'drop. "
+ "This feature requires Guest Additions to be installed in the guest OS."));
+ }
+}
+
+void UIDragAndDropEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIDragAndDropEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Load currently supported audio driver types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedDnDModes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KDnDMode_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const KDnDMode &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDragAndDropEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDragAndDropEditor.h
new file mode 100644
index 00000000..8139cb63
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIDragAndDropEditor.h
@@ -0,0 +1,98 @@
+/* $Id: UIDragAndDropEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIDragAndDropEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIDragAndDropEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIDragAndDropEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as a drag&drop editor. */
+class SHARED_LIBRARY_STUFF UIDragAndDropEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIDragAndDropEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KDnDMode enmValue);
+ /** Returns editor value. */
+ KDnDMode value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KDnDMode> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ KDnDMode m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KDnDMode> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIDragAndDropEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIExecutionCapEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIExecutionCapEditor.cpp
new file mode 100644
index 00000000..3ef8d8d2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIExecutionCapEditor.cpp
@@ -0,0 +1,221 @@
+/* $Id: UIExecutionCapEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIExecutionCapEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QSpinBox>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+#include "UICommon.h"
+#include "UIExecutionCapEditor.h"
+
+UIExecutionCapEditor::UIExecutionCapEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_uMinExecCap(1)
+ , m_uMedExecCap(40)
+ , m_uMaxExecCap(100)
+ , m_pLabelExecCap(0)
+ , m_pSlider(0)
+ , m_pSpinBox(0)
+ , m_pLabelExecCapMin(0)
+ , m_pLabelExecCapMax(0)
+{
+ prepare();
+}
+
+int UIExecutionCapEditor::medExecCap() const
+{
+ return (int)m_uMedExecCap;
+}
+
+void UIExecutionCapEditor::setValue(int iValue)
+{
+ if (m_pSlider)
+ m_pSlider->setValue(iValue);
+}
+
+int UIExecutionCapEditor::value() const
+{
+ return m_pSlider ? m_pSlider->value() : 0;
+}
+
+int UIExecutionCapEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabelExecCap->minimumSizeHint().width();
+}
+
+void UIExecutionCapEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIExecutionCapEditor::retranslateUi()
+{
+ if (m_pLabelExecCap)
+ m_pLabelExecCap->setText(tr("&Execution Cap:"));
+
+ const QString strToolTip(tr("Limits the amount of time that each virtual CPU is allowed to run for. "
+ "Each virtual CPU will be allowed to use up to this percentage of the processing "
+ "time available on one physical CPU."));
+ if (m_pSlider)
+ m_pSlider->setToolTip(strToolTip);
+ if (m_pSpinBox)
+ {
+ m_pSpinBox->setSuffix(QString("%"));
+ m_pSpinBox->setToolTip(strToolTip);
+ }
+
+ if (m_pLabelExecCapMin)
+ {
+ m_pLabelExecCapMin->setText(QString("%1%").arg(m_uMinExecCap));
+ m_pLabelExecCapMin->setToolTip(tr("Minimum possible execution cap."));
+ }
+ if (m_pLabelExecCapMax)
+ {
+ m_pLabelExecCapMax->setText(QString("%1%").arg(m_uMaxExecCap));
+ m_pLabelExecCapMax->setToolTip(tr("Maximum possible virtual CPU count."));
+ }
+}
+
+void UIExecutionCapEditor::sltHandleSliderChange()
+{
+ /* Apply spin-box value keeping it's signals disabled: */
+ if (m_pSpinBox && m_pSlider)
+ {
+ m_pSpinBox->blockSignals(true);
+ m_pSpinBox->setValue(m_pSlider->value());
+ m_pSpinBox->blockSignals(false);
+ }
+
+ /* Send signal to listener: */
+ emit sigValueChanged();
+}
+
+void UIExecutionCapEditor::sltHandleSpinBoxChange()
+{
+ /* Apply slider value keeping it's signals disabled: */
+ if (m_pSpinBox && m_pSlider)
+ {
+ m_pSlider->blockSignals(true);
+ m_pSlider->setValue(m_pSpinBox->value());
+ m_pSlider->blockSignals(false);
+ }
+
+ /* Send signal to listener: */
+ emit sigValueChanged();
+}
+
+void UIExecutionCapEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create main label: */
+ m_pLabelExecCap = new QLabel(this);
+ if (m_pLabelExecCap)
+ {
+ m_pLabelExecCap->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabelExecCap, 0, 0);
+ }
+
+ /* Create slider layout: */
+ QVBoxLayout *pSliderLayout = new QVBoxLayout;
+ if (pSliderLayout)
+ {
+ pSliderLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create VCPU slider: */
+ m_pSlider = new QIAdvancedSlider(this);
+ if (m_pSlider)
+ {
+ m_pSlider->setOrientation(Qt::Horizontal);
+ m_pSlider->setPageStep(10);
+ m_pSlider->setSingleStep(1);
+ m_pSlider->setTickInterval(10);
+ m_pSlider->setMinimum(m_uMinExecCap);
+ m_pSlider->setMaximum(m_uMaxExecCap);
+ m_pSlider->setWarningHint(m_uMinExecCap, m_uMedExecCap);
+ m_pSlider->setOptimalHint(m_uMedExecCap, m_uMaxExecCap);
+ connect(m_pSlider, &QIAdvancedSlider::valueChanged,
+ this, &UIExecutionCapEditor::sltHandleSliderChange);
+ pSliderLayout->addWidget(m_pSlider);
+ }
+
+ /* Create legend layout: */
+ QHBoxLayout *pLegendLayout = new QHBoxLayout;
+ if (pLegendLayout)
+ {
+ pLegendLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create min label: */
+ m_pLabelExecCapMin = new QLabel(this);
+ if (m_pLabelExecCapMin)
+ pLegendLayout->addWidget(m_pLabelExecCapMin);
+
+ /* Push labels from each other: */
+ pLegendLayout->addStretch();
+
+ /* Create max label: */
+ m_pLabelExecCapMax = new QLabel(this);
+ if (m_pLabelExecCapMax)
+ pLegendLayout->addWidget(m_pLabelExecCapMax);
+
+ /* Add legend layout to slider layout: */
+ pSliderLayout->addLayout(pLegendLayout);
+ }
+
+ /* Add slider layout to main layout: */
+ m_pLayout->addLayout(pSliderLayout, 0, 1, 2, 1);
+ }
+
+ /* Create VCPU spin-box: */
+ m_pSpinBox = new QSpinBox(this);
+ if (m_pSpinBox)
+ {
+ setFocusProxy(m_pSpinBox);
+ if (m_pLabelExecCap)
+ m_pLabelExecCap->setBuddy(m_pSpinBox);
+ m_pSpinBox->setMinimum(m_uMinExecCap);
+ m_pSpinBox->setMaximum(m_uMaxExecCap);
+ uiCommon().setMinimumWidthAccordingSymbolCount(m_pSpinBox, 4);
+ connect(m_pSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIExecutionCapEditor::sltHandleSpinBoxChange);
+ m_pLayout->addWidget(m_pSpinBox, 0, 2);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIExecutionCapEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIExecutionCapEditor.h
new file mode 100644
index 00000000..18c5ded1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIExecutionCapEditor.h
@@ -0,0 +1,118 @@
+/* $Id: UIExecutionCapEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIExecutionCapEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIExecutionCapEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIExecutionCapEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QSpinBox;
+class QIAdvancedSlider;
+
+/** QWidget subclass used as a execution cap editor. */
+class SHARED_LIBRARY_STUFF UIExecutionCapEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value changed. */
+ void sigValueChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIExecutionCapEditor(QWidget *pParent = 0);
+
+ /** Returns the medium execution cap. */
+ int medExecCap() const;
+
+ /** Defines editor @a iValue. */
+ void setValue(int iValue);
+ /** Returns editor value. */
+ int value() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles slider value changes. */
+ void sltHandleSliderChange();
+ /** Handles spin-box value changes. */
+ void sltHandleSpinBoxChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Options
+ * @{ */
+ /** Holds the minimum execution cap. */
+ uint m_uMinExecCap;
+ /** Holds the medium execution cap. */
+ uint m_uMedExecCap;
+ /** Holds the maximum execution cap. */
+ uint m_uMaxExecCap;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the main label instance. */
+ QLabel *m_pLabelExecCap;
+ /** Holds the slider instance. */
+ QIAdvancedSlider *m_pSlider;
+ /** Holds the spinbox instance. */
+ QSpinBox *m_pSpinBox;
+ /** Holds the minimum label instance. */
+ QLabel *m_pLabelExecCapMin;
+ /** Holds the maximum label instance. */
+ QLabel *m_pLabelExecCapMax;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIExecutionCapEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIFontScaleEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIFontScaleEditor.cpp
new file mode 100644
index 00000000..1520d7c4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIFontScaleEditor.cpp
@@ -0,0 +1,235 @@
+/* $Id: UIFontScaleEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFontScaleEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QSpacerItem>
+#include <QSpinBox>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataDefs.h"
+#include "UIFontScaleEditor.h"
+
+/* External includes: */
+#include <math.h>
+
+class UIFontScaleFactorSpinBox : public QSpinBox
+{
+public:
+ UIFontScaleFactorSpinBox(QWidget *parent = 0);
+};
+
+UIFontScaleFactorSpinBox::UIFontScaleFactorSpinBox(QWidget *pParent /* = 0*/)
+ :QSpinBox(pParent)
+{
+ QLineEdit *pLineEdit = new QLineEdit;
+ if (pLineEdit)
+ {
+ pLineEdit->setReadOnly(true);
+ setLineEdit(pLineEdit);
+ }
+}
+
+UIFontScaleEditor::UIFontScaleEditor(QWidget *pParent)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLayout(0)
+ , m_pLabel(0)
+ , m_pScaleSlider(0)
+ , m_pScaleSpinBox(0)
+ , m_pMinScaleLabel(0)
+ , m_pMaxScaleLabel(0)
+ , m_iSliderRangeDivisor(10)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIFontScaleEditor::setSpinBoxWidthHint(int iHint)
+{
+ m_pScaleSpinBox->setMinimumWidth(iHint);
+}
+
+int UIFontScaleEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIFontScaleEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIFontScaleEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("F&ont Scaling:"));
+
+ if (m_pScaleSlider)
+ m_pScaleSlider->setToolTip(tr("Holds the scaling factor for the font size."));
+ if (m_pScaleSpinBox)
+ m_pScaleSpinBox->setToolTip(tr("Holds the scaling factor for the font size."));
+
+ if (m_pMinScaleLabel)
+ {
+ m_pMinScaleLabel->setText(QString("%1%").arg(m_pScaleSpinBox->minimum()));
+ m_pMinScaleLabel->setToolTip(tr("Minimum possible scale factor."));
+ }
+ if (m_pMaxScaleLabel)
+ {
+ m_pMaxScaleLabel->setText(QString("%1%").arg(m_pScaleSpinBox->maximum()));
+ m_pMaxScaleLabel->setToolTip(tr("Maximum possible scale factor."));
+ }
+}
+
+void UIFontScaleEditor::sltScaleSpinBoxValueChanged(int value)
+{
+ setSliderValue(value / m_iSliderRangeDivisor);
+}
+
+void UIFontScaleEditor::sltScaleSliderValueChanged(int value)
+{
+ setSpinBoxValue(m_iSliderRangeDivisor * value);
+ setFontScaleFactor(m_iSliderRangeDivisor * value);
+}
+
+void UIFontScaleEditor::setFontScaleFactor(int iFontScaleFactor)
+{
+ setSliderValue(iFontScaleFactor / m_iSliderRangeDivisor);
+ setSpinBoxValue(iFontScaleFactor);
+}
+
+int UIFontScaleEditor::fontScaleFactor() const
+{
+ return m_pScaleSpinBox->value();
+}
+
+void UIFontScaleEditor::sltMonitorComboIndexChanged(int)
+{
+}
+
+void UIFontScaleEditor::prepare()
+{
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Prepare label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ m_pScaleSlider = new QIAdvancedSlider(this);
+ {
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pScaleSlider);
+ m_pScaleSlider->setSnappingEnabled(true);
+ connect(m_pScaleSlider, static_cast<void(QIAdvancedSlider::*)(int)>(&QIAdvancedSlider::valueChanged),
+ this, &UIFontScaleEditor::sltScaleSliderValueChanged);
+
+ m_pLayout->addWidget(m_pScaleSlider, 0, 1, 1, 4);
+ }
+
+ m_pScaleSpinBox = new UIFontScaleFactorSpinBox(this);
+ if (m_pScaleSpinBox)
+ {
+ setFocusProxy(m_pScaleSpinBox);
+ m_pScaleSpinBox->setSuffix("%");
+ connect(m_pScaleSpinBox ,static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIFontScaleEditor::sltScaleSpinBoxValueChanged);
+ m_pLayout->addWidget(m_pScaleSpinBox, 0, 5);
+ }
+
+ m_pMinScaleLabel = new QLabel(this);
+ if (m_pMinScaleLabel)
+ m_pLayout->addWidget(m_pMinScaleLabel, 1, 1);
+
+ m_pMaxScaleLabel = new QLabel(this);
+ if (m_pMaxScaleLabel)
+ m_pLayout->addWidget(m_pMaxScaleLabel, 1, 4);
+ }
+
+ prepareScaleFactorMinMax();
+ retranslateUi();
+}
+
+void UIFontScaleEditor::prepareScaleFactorMinMax()
+{
+ m_pScaleSlider->blockSignals(true);
+ m_pScaleSpinBox->blockSignals(true);
+ const int iMinimum = UIExtraDataDefs::iFontScaleMin;
+ const int iMaximum = UIExtraDataDefs::iFontScaleMax;
+
+ /* Set slider min, max, and intervals so to make sure mouse drag stops only on ticks: */
+ m_pScaleSlider->setMinimum(iMinimum / m_iSliderRangeDivisor);
+ m_pScaleSlider->setMaximum(iMaximum / m_iSliderRangeDivisor);
+
+ m_pScaleSlider->setPageStep(2);
+ m_pScaleSlider->setSingleStep(1);
+ m_pScaleSlider->setTickInterval(1);
+ m_pScaleSpinBox->setSingleStep(10);
+
+ m_pScaleSpinBox->setMinimum(iMinimum);
+ m_pScaleSpinBox->setMaximum(iMaximum);
+
+ QLineEdit *pLineEdit = new QLineEdit;
+ pLineEdit->setReadOnly(true);
+
+ m_pScaleSlider->blockSignals(false);
+ m_pScaleSpinBox->blockSignals(false);
+}
+
+void UIFontScaleEditor::setSliderValue(int iValue)
+{
+ if (m_pScaleSlider && iValue != m_pScaleSlider->value())
+ {
+ m_pScaleSlider->blockSignals(true);
+ m_pScaleSlider->setValue(iValue);
+ m_pScaleSlider->blockSignals(false);
+ }
+}
+
+void UIFontScaleEditor::setSpinBoxValue(int iValue)
+{
+ if (m_pScaleSpinBox && iValue != m_pScaleSpinBox->value())
+ {
+ m_pScaleSpinBox->blockSignals(true);
+ m_pScaleSpinBox->setValue(iValue);
+ m_pScaleSpinBox->blockSignals(false);
+ }
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIFontScaleEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIFontScaleEditor.h
new file mode 100644
index 00000000..31d03cac
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIFontScaleEditor.h
@@ -0,0 +1,109 @@
+/* $Id: UIFontScaleEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIFontScaleEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIFontScaleEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIFontScaleEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QSpinBox;
+class QWidget;
+class QIAdvancedSlider;
+class UIFontScaleFactorSpinBox;
+
+/** QWidget reimplementation providing GUI with monitor scale factor editing functionality.
+ * It includes a combo box to select a monitor, a slider, and a spinbox to display/modify values.
+ * The first item in the combo box is used to change the scale factor of all monitors. */
+class SHARED_LIBRARY_STUFF UIFontScaleEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIFontScaleEditor(QWidget *pParent);
+
+ /** Defines minimum width @a iHint for internal spin-box. */
+ void setSpinBoxWidthHint(int iHint);
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+ void setFontScaleFactor(int iFontScaleFactor);
+ int fontScaleFactor() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** @name Internal slots handling respective widget's value update.
+ * @{ */
+ void sltScaleSpinBoxValueChanged(int iValue);
+ void sltScaleSliderValueChanged(int iValue);
+ void sltMonitorComboIndexChanged(int iIndex);
+ /** @} */
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepare min/max values of related widgets wrt. device pixel ratio(s). */
+ void prepareScaleFactorMinMax();
+
+ /** Defines slider's @a iValue. */
+ void setSliderValue(int iValue);
+ /** Defines spinbox's @a iValue. */
+ void setSpinBoxValue(int iValue);
+
+
+ /** @name Member widgets.
+ * @{ */
+ QGridLayout *m_pLayout;
+ QLabel *m_pLabel;
+ QIAdvancedSlider *m_pScaleSlider;
+ UIFontScaleFactorSpinBox *m_pScaleSpinBox;
+ QLabel *m_pMinScaleLabel;
+ QLabel *m_pMaxScaleLabel;
+ /** @} */
+ /** Hold the factor by which we divided spinbox's @a range to set slider's range to make slider mouse move stop on ticks. */
+ const int m_iSliderRangeDivisor;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIFontScaleEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIGraphicsControllerEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIGraphicsControllerEditor.cpp
new file mode 100644
index 00000000..0fc24042
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIGraphicsControllerEditor.cpp
@@ -0,0 +1,178 @@
+/* $Id: UIGraphicsControllerEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsControllerEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIGraphicsControllerEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIGraphicsControllerEditor::UIGraphicsControllerEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KGraphicsControllerType_Max)
+ , m_pLayout(0)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UIGraphicsControllerEditor::setValue(KGraphicsControllerType enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+KGraphicsControllerType UIGraphicsControllerEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<KGraphicsControllerType>() : m_enmValue;
+}
+
+int UIGraphicsControllerEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIGraphicsControllerEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIGraphicsControllerEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("&Graphics Controller:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const KGraphicsControllerType enmType = m_pCombo->itemData(i).value<KGraphicsControllerType>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Selects the graphics adapter type the virtual machine will use."));
+ }
+}
+
+void UIGraphicsControllerEditor::sltHandleCurrentIndexChanged()
+{
+ if (m_pCombo)
+ emit sigValueChanged(m_pCombo->itemData(m_pCombo->currentIndex()).value<KGraphicsControllerType>());
+}
+
+void UIGraphicsControllerEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pLayoutCombo = new QHBoxLayout;
+ if (pLayoutCombo)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ connect(m_pCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIGraphicsControllerEditor::sltHandleCurrentIndexChanged);
+
+ pLayoutCombo->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pLayoutCombo->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pLayoutCombo, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIGraphicsControllerEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Load currently supported graphics controller types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedGraphicsControllerTypes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KGraphicsControllerType_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const KGraphicsControllerType &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIGraphicsControllerEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIGraphicsControllerEditor.h
new file mode 100644
index 00000000..5485c2dc
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIGraphicsControllerEditor.h
@@ -0,0 +1,108 @@
+/* $Id: UIGraphicsControllerEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsControllerEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIGraphicsControllerEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIGraphicsControllerEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as a graphics controller editor. */
+class SHARED_LIBRARY_STUFF UIGraphicsControllerEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a enmValue change. */
+ void sigValueChanged(KGraphicsControllerType enmValue);
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIGraphicsControllerEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KGraphicsControllerType enmValue);
+ /** Returns editor value. */
+ KGraphicsControllerType value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KGraphicsControllerType> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles current index change. */
+ void sltHandleCurrentIndexChanged();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ KGraphicsControllerType m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KGraphicsControllerType> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIGraphicsControllerEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHostComboEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHostComboEditor.cpp
new file mode 100644
index 00000000..26a39b52
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHostComboEditor.cpp
@@ -0,0 +1,924 @@
+/* $Id: UIHostComboEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIHostComboEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QHBoxLayout>
+#include <QKeyEvent>
+#include <QStyleOption>
+#include <QStylePainter>
+#include <QTimer>
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+# include <QAbstractNativeEventFilter>
+#endif
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIHostComboEditor.h"
+#include "UIIconPool.h"
+#ifdef VBOX_WS_MAC
+# include "UICocoaApplication.h"
+# include "VBoxUtils-darwin.h"
+# include "DarwinKeyboard.h"
+#elif defined(VBOX_WS_WIN)
+# include "WinKeyboard.h"
+#elif defined(VBOX_WS_X11)
+# include "XKeyboard.h"
+# include "VBoxUtils-x11.h"
+#endif
+
+/* Other VBox includes: */
+#if defined(VBOX_WS_X11)
+# include <VBox/VBoxKeyboard.h>
+#endif
+
+/* External includes: */
+#if defined(VBOX_WS_MAC)
+# include <Carbon/Carbon.h>
+#elif defined(VBOX_WS_X11)
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include <X11/keysym.h>
+# include <xcb/xcb.h>
+#endif /* VBOX_WS_X11 */
+
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+
+
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+/** QAbstractNativeEventFilter extension
+ * allowing to handle native platform events.
+ * Why do we need it? It's because Qt5 have unhandled
+ * well .. let's call it 'a bug' about native keyboard events
+ * which come to top-level widget (window) instead of focused sub-widget
+ * which actually supposed to get them. The strange thing is that target of
+ * those events on at least Windows host (MSG::hwnd) is indeed window itself,
+ * not the sub-widget we expect, so that's probably the reason Qt devs
+ * haven't fixed that bug so far for Windows and Mac OS X hosts. */
+class ComboEditorEventFilter : public QAbstractNativeEventFilter
+{
+public:
+
+ /** Constructor which takes the passed @a pParent to redirect events to. */
+ ComboEditorEventFilter(UIHostComboEditorPrivate *pParent)
+ : m_pParent(pParent)
+ {}
+
+ /** Handles all native events. */
+# ifdef VBOX_IS_QT6_OR_LATER /* long replaced with qintptr since 6.0 */
+ virtual bool nativeEventFilter(const QByteArray &eventType, void *pMessage, qintptr *pResult) RT_OVERRIDE
+# else
+ virtual bool nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pResult) RT_OVERRIDE
+# endif
+ {
+ /* Redirect event to parent: */
+ return m_pParent->nativeEvent(eventType, pMessage, pResult);
+ }
+
+private:
+
+ /** Holds the passed parent reference. */
+ UIHostComboEditorPrivate *m_pParent;
+};
+#endif /* VBOX_WS_MAC || VBOX_WS_WIN */
+
+
+/*********************************************************************************************************************************
+* Namespace UINativeHotKey implementation. *
+*********************************************************************************************************************************/
+
+#ifdef VBOX_WS_X11
+namespace UINativeHotKey
+{
+ QMap<QString, QString> m_keyNames;
+}
+#endif /* VBOX_WS_X11 */
+
+QString UINativeHotKey::toString(int iKeyCode)
+{
+ QString strKeyName;
+
+#if defined(VBOX_WS_MAC)
+
+ UInt32 modMask = DarwinKeyCodeToDarwinModifierMask(iKeyCode);
+ switch (modMask)
+ {
+ case shiftKey:
+ case optionKey:
+ case controlKey:
+ case cmdKey:
+ strKeyName = UIHostComboEditor::tr("Left %1");
+ break;
+ case rightShiftKey:
+ case rightOptionKey:
+ case rightControlKey:
+ case kEventKeyModifierRightCmdKeyMask:
+ strKeyName = UIHostComboEditor::tr("Right %1");
+ break;
+ default:
+ AssertMsgFailedReturn(("modMask=%#x\n", modMask), QString());
+ }
+ switch (modMask)
+ {
+ case shiftKey:
+ case rightShiftKey:
+ strKeyName = strKeyName.arg(QChar(kShiftUnicode));
+ break;
+ case optionKey:
+ case rightOptionKey:
+ strKeyName = strKeyName.arg(QChar(kOptionUnicode));
+ break;
+ case controlKey:
+ case rightControlKey:
+ strKeyName = strKeyName.arg(QChar(kControlUnicode));
+ break;
+ case cmdKey:
+ case kEventKeyModifierRightCmdKeyMask:
+ strKeyName = strKeyName.arg(QChar(kCommandUnicode));
+ break;
+ }
+
+#elif defined(VBOX_WS_WIN)
+
+ // WORKAROUND:
+ // MapVirtualKey doesn't distinguish between right and left vkeys,
+ // even under XP, despite that it stated in MSDN. Do it by hands.
+ // Besides that it can't recognize such virtual keys as
+ // VK_DIVIDE & VK_PAUSE, this is also known bug.
+ int iScan;
+ switch (iKeyCode)
+ {
+ /* Processing special keys... */
+ case VK_PAUSE: iScan = 0x45 << 16; break;
+ case VK_RSHIFT: iScan = 0x36 << 16; break;
+ case VK_RCONTROL: iScan = (0x1D << 16) | (1 << 24); break;
+ case VK_RMENU: iScan = (0x38 << 16) | (1 << 24); break;
+ /* Processing extended keys... */
+ case VK_APPS:
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_NUMLOCK: iScan = (::MapVirtualKey(iKeyCode, 0) | 256) << 16; break;
+ default: iScan = ::MapVirtualKey(iKeyCode, 0) << 16;
+ }
+ TCHAR *pKeyName = new TCHAR[256];
+ if (::GetKeyNameText(iScan, pKeyName, 256))
+ {
+ strKeyName = QString::fromUtf16(pKeyName);
+ }
+ else
+ {
+ AssertMsgFailed(("That key have no name!\n"));
+ strKeyName = UIHostComboEditor::tr("<key_%1>").arg(iKeyCode);
+ }
+ delete[] pKeyName;
+
+#elif defined(VBOX_WS_X11)
+
+ if (char *pNativeKeyName = ::XKeysymToString((KeySym)iKeyCode))
+ {
+ strKeyName = m_keyNames[pNativeKeyName].isEmpty() ?
+ QString(pNativeKeyName) : m_keyNames[pNativeKeyName];
+ }
+ else
+ {
+ AssertMsgFailed(("That key have no name!\n"));
+ strKeyName = UIHostComboEditor::tr("<key_%1>").arg(iKeyCode);
+ }
+
+#else
+
+# warning "port me!"
+
+#endif
+
+ return strKeyName;
+}
+
+bool UINativeHotKey::isValidKey(int iKeyCode)
+{
+#if defined(VBOX_WS_MAC)
+
+ UInt32 modMask = ::DarwinKeyCodeToDarwinModifierMask(iKeyCode);
+ switch (modMask)
+ {
+ case shiftKey:
+ case optionKey:
+ case controlKey:
+ case rightShiftKey:
+ case rightOptionKey:
+ case rightControlKey:
+ case cmdKey:
+ case kEventKeyModifierRightCmdKeyMask:
+ return true;
+ default:
+ return false;
+ }
+
+#elif defined(VBOX_WS_WIN)
+
+ return ( iKeyCode >= VK_SHIFT && iKeyCode <= VK_CAPITAL
+ && iKeyCode != VK_PAUSE)
+ || (iKeyCode >= VK_LSHIFT && iKeyCode <= VK_RMENU)
+ || (iKeyCode >= VK_F1 && iKeyCode <= VK_F24)
+ || iKeyCode == VK_NUMLOCK
+ || iKeyCode == VK_SCROLL
+ || iKeyCode == VK_LWIN
+ || iKeyCode == VK_RWIN
+ || iKeyCode == VK_APPS
+ || iKeyCode == VK_PRINT;
+
+#elif defined(VBOX_WS_X11)
+
+ return (IsModifierKey(iKeyCode) /* allow modifiers */ ||
+ IsFunctionKey(iKeyCode) /* allow function keys */ ||
+ IsMiscFunctionKey(iKeyCode) /* allow miscellaneous function keys */ ||
+ iKeyCode == XK_Scroll_Lock /* allow 'Scroll Lock' missed in IsModifierKey() */) &&
+ (iKeyCode != NoSymbol /* ignore some special symbol */ &&
+ iKeyCode != XK_Insert /* ignore 'insert' included into IsMiscFunctionKey */);
+
+#else
+
+# warning "port me!"
+
+ return false;
+
+#endif
+}
+
+unsigned UINativeHotKey::modifierToSet1ScanCode(int iKeyCode)
+{
+ switch(iKeyCode)
+ {
+#if defined(VBOX_WS_MAC)
+
+ case controlKey: return 0x1D;
+ case rightControlKey: return 0x11D;
+ case shiftKey: return 0x2A;
+ case rightShiftKey: return 0x36;
+ case optionKey: return 0x38;
+ case rightOptionKey: return 0x138;
+ case cmdKey: return 0x15B;
+ case kEventKeyModifierRightCmdKeyMask: return 0x15C;
+ default: return 0;
+
+#elif defined(VBOX_WS_WIN)
+
+ case VK_CONTROL:
+ case VK_LCONTROL: return 0x1D;
+ case VK_RCONTROL: return 0x11D;
+ case VK_SHIFT:
+ case VK_LSHIFT: return 0x2A;
+ case VK_RSHIFT: return 0x36;
+ case VK_MENU:
+ case VK_LMENU: return 0x38;
+ case VK_RMENU: return 0x138;
+ case VK_LWIN: return 0x15B;
+ case VK_RWIN: return 0x15C;
+ case VK_APPS: return 0x15D;
+ default: return 0;
+
+#elif defined(VBOX_WS_X11)
+
+ case XK_Control_L: return 0x1D;
+ case XK_Control_R: return 0x11D;
+ case XK_Shift_L: return 0x2A;
+ case XK_Shift_R: return 0x36;
+ case XK_Alt_L: return 0x38;
+ case XK_ISO_Level3_Shift:
+ case XK_Alt_R: return 0x138;
+ case XK_Meta_L:
+ case XK_Super_L: return 0x15B;
+ case XK_Meta_R:
+ case XK_Super_R: return 0x15C;
+ case XK_Menu: return 0x15D;
+ default: return 0;
+
+#else
+
+# warning "port me!"
+
+ default: return 0;
+
+#endif
+ }
+}
+
+#if defined(VBOX_WS_WIN)
+
+int UINativeHotKey::distinguishModifierVKey(int wParam, int lParam)
+{
+ int iKeyCode = wParam;
+ switch (iKeyCode)
+ {
+ case VK_SHIFT:
+ {
+ UINT uCurrentScanCode = (lParam & 0x01FF0000) >> 16;
+ UINT uLeftScanCode = ::MapVirtualKey(iKeyCode, 0);
+ if (uCurrentScanCode == uLeftScanCode)
+ iKeyCode = VK_LSHIFT;
+ else
+ iKeyCode = VK_RSHIFT;
+ break;
+ }
+ case VK_CONTROL:
+ {
+ UINT uCurrentScanCode = (lParam & 0x01FF0000) >> 16;
+ UINT uLeftScanCode = ::MapVirtualKey(iKeyCode, 0);
+ if (uCurrentScanCode == uLeftScanCode)
+ iKeyCode = VK_LCONTROL;
+ else
+ iKeyCode = VK_RCONTROL;
+ break;
+ }
+ case VK_MENU:
+ {
+ UINT uCurrentScanCode = (lParam & 0x01FF0000) >> 16;
+ UINT uLeftScanCode = ::MapVirtualKey(iKeyCode, 0);
+ if (uCurrentScanCode == uLeftScanCode)
+ iKeyCode = VK_LMENU;
+ else
+ iKeyCode = VK_RMENU;
+ break;
+ }
+ }
+ return iKeyCode;
+}
+
+#elif defined(VBOX_WS_X11)
+
+void UINativeHotKey::retranslateKeyNames()
+{
+ m_keyNames["Shift_L"] = UIHostComboEditor::tr("Left Shift");
+ m_keyNames["Shift_R"] = UIHostComboEditor::tr("Right Shift");
+ m_keyNames["Control_L"] = UIHostComboEditor::tr("Left Ctrl");
+ m_keyNames["Control_R"] = UIHostComboEditor::tr("Right Ctrl");
+ m_keyNames["Alt_L"] = UIHostComboEditor::tr("Left Alt");
+ m_keyNames["Alt_R"] = UIHostComboEditor::tr("Right Alt");
+ m_keyNames["Super_L"] = UIHostComboEditor::tr("Left WinKey");
+ m_keyNames["Super_R"] = UIHostComboEditor::tr("Right WinKey");
+ m_keyNames["Menu"] = UIHostComboEditor::tr("Menu key");
+ m_keyNames["ISO_Level3_Shift"] = UIHostComboEditor::tr("Alt Gr");
+ m_keyNames["Caps_Lock"] = UIHostComboEditor::tr("Caps Lock");
+ m_keyNames["Scroll_Lock"] = UIHostComboEditor::tr("Scroll Lock");
+}
+
+#endif /* VBOX_WS_X11 */
+
+
+/*********************************************************************************************************************************
+* Namespace UIHostCombo implementation. *
+*********************************************************************************************************************************/
+
+namespace UIHostCombo
+{
+ int m_iMaxComboSize = 3;
+}
+
+int UIHostCombo::hostComboModifierIndex()
+{
+ return -1;
+}
+
+QString UIHostCombo::hostComboModifierName()
+{
+ return UIHostComboEditor::tr("Host+");
+}
+
+QString UIHostCombo::hostComboCacheKey()
+{
+ return QString(GUI_Input_MachineShortcuts) + "/" + "HostCombo";
+}
+
+QString UIHostCombo::toReadableString(const QString &strKeyCombo)
+{
+ QStringList encodedKeyList = strKeyCombo.split(',');
+ QStringList readableKeyList;
+ for (int i = 0; i < encodedKeyList.size(); ++i)
+ if (int iKeyCode = encodedKeyList[i].toInt())
+ readableKeyList << UINativeHotKey::toString(iKeyCode);
+ return readableKeyList.isEmpty() ? UIHostComboEditor::tr("None") : readableKeyList.join(" + ");
+}
+
+QList<int> UIHostCombo::toKeyCodeList(const QString &strKeyCombo)
+{
+ QStringList encodedKeyList = strKeyCombo.split(',');
+ QList<int> keyCodeList;
+ for (int i = 0; i < encodedKeyList.size(); ++i)
+ if (int iKeyCode = encodedKeyList[i].toInt())
+ keyCodeList << iKeyCode;
+ return keyCodeList;
+}
+
+QList<unsigned> UIHostCombo::modifiersToScanCodes(const QString &strKeyCombo)
+{
+ QStringList encodedKeyList = strKeyCombo.split(',');
+ QList<unsigned> scanCodeList;
+ for (int i = 0; i < encodedKeyList.size(); ++i)
+ if (unsigned idxScanCode = UINativeHotKey::modifierToSet1ScanCode(encodedKeyList[i].toInt()))
+ if (idxScanCode != 0)
+ scanCodeList << idxScanCode;
+ return scanCodeList;
+}
+
+bool UIHostCombo::isValidKeyCombo(const QString &strKeyCombo)
+{
+ QList<int> keyCodeList = toKeyCodeList(strKeyCombo);
+ if (keyCodeList.size() > m_iMaxComboSize)
+ return false;
+ for (int i = 0; i < keyCodeList.size(); ++i)
+ if (!UINativeHotKey::isValidKey(keyCodeList[i]))
+ return false;
+ return true;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIHostComboEditor implementation. *
+*********************************************************************************************************************************/
+
+UIHostComboEditor::UIHostComboEditor(QWidget *pParent)
+ : QIWithRetranslateUI<QWidget>(pParent)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIHostComboEditor::retranslateUi()
+{
+ /* Translate 'clear' tool-button: */
+ m_pButtonClear->setToolTip(QApplication::translate("UIHotKeyEditor", "Unset shortcut"));
+}
+
+void UIHostComboEditor::sltCommitData()
+{
+ /* Commit data to the listener: */
+ emit sigCommitData(this);
+}
+
+void UIHostComboEditor::prepare()
+{
+ /* Make sure QIStyledDelegate aware of us: */
+ setProperty("has_sigCommitData", true);
+ /* Configure self: */
+ setAutoFillBackground(true);
+ /* Create layout: */
+ QHBoxLayout *pLayout = new QHBoxLayout(this);
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ pLayout->setSpacing(5);
+#else
+ pLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ /* Create UIHostComboEditorPrivate instance: */
+ m_pEditor = new UIHostComboEditorPrivate;
+ {
+ /* Configure UIHostComboEditorPrivate instance: */
+ setFocusProxy(m_pEditor);
+ connect(m_pEditor, &UIHostComboEditorPrivate::sigDataChanged, this, &UIHostComboEditor::sltCommitData);
+ }
+ /* Create 'clear' tool-button: */
+ m_pButtonClear = new QIToolButton;
+ {
+ /* Configure 'clear' tool-button: */
+ m_pButtonClear->removeBorder();
+ m_pButtonClear->setIcon(UIIconPool::iconSet(":/eraser_16px.png"));
+ connect(m_pButtonClear, &QIToolButton::clicked, m_pEditor, &UIHostComboEditorPrivate::sltClear);
+ }
+ /* Add widgets to layout: */
+ pLayout->addWidget(m_pEditor);
+ pLayout->addWidget(m_pButtonClear);
+ }
+ /* Translate finally: */
+ retranslateUi();
+}
+
+void UIHostComboEditor::setCombo(const UIHostComboWrapper &strCombo)
+{
+ /* Pass combo to child: */
+ m_pEditor->setCombo(strCombo);
+}
+
+UIHostComboWrapper UIHostComboEditor::combo() const
+{
+ /* Acquire combo from child: */
+ return m_pEditor->combo();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIHostComboEditorPrivate implementation. *
+*********************************************************************************************************************************/
+
+UIHostComboEditorPrivate::UIHostComboEditorPrivate()
+ : m_pReleaseTimer(0)
+ , m_fStartNewSequence(true)
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+ , m_pPrivateEventFilter(0)
+#endif
+#ifdef VBOX_WS_WIN
+ , m_pAltGrMonitor(0)
+#endif
+{
+ /* Configure widget: */
+ setAttribute(Qt::WA_NativeWindow);
+ setContextMenuPolicy(Qt::NoContextMenu);
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
+ connect(this, &UIHostComboEditorPrivate::selectionChanged, this, &UIHostComboEditorPrivate::sltDeselect);
+
+ /* Setup release-pending-keys timer: */
+ m_pReleaseTimer = new QTimer(this);
+ m_pReleaseTimer->setInterval(200);
+ connect(m_pReleaseTimer, &QTimer::timeout, this, &UIHostComboEditorPrivate::sltReleasePendingKeys);
+
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+ /* Prepare private event filter: */
+ m_pPrivateEventFilter = new ComboEditorEventFilter(this);
+ qApp->installNativeEventFilter(m_pPrivateEventFilter);
+#endif /* VBOX_WS_MAC || VBOX_WS_WIN */
+
+#if defined(VBOX_WS_MAC)
+ m_uDarwinKeyModifiers = 0;
+#elif defined(VBOX_WS_WIN)
+ /* Prepare AltGR monitor: */
+ m_pAltGrMonitor = new WinAltGrMonitor;
+#elif defined(VBOX_WS_X11)
+ /* Initialize the X keyboard subsystem: */
+ initMappedX11Keyboard(NativeWindowSubsystem::X11GetDisplay(), gEDataManager->remappedScanCodes());
+#endif /* VBOX_WS_X11 */
+}
+
+UIHostComboEditorPrivate::~UIHostComboEditorPrivate()
+{
+#if defined(VBOX_WS_WIN)
+ /* Cleanup AltGR monitor: */
+ delete m_pAltGrMonitor;
+ m_pAltGrMonitor = 0;
+#endif /* VBOX_WS_WIN */
+
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+ /* Cleanup private event filter: */
+ qApp->removeNativeEventFilter(m_pPrivateEventFilter);
+ delete m_pPrivateEventFilter;
+ m_pPrivateEventFilter = 0;
+#endif /* VBOX_WS_MAC || VBOX_WS_WIN */
+}
+
+void UIHostComboEditorPrivate::setCombo(const UIHostComboWrapper &strCombo)
+{
+ /* Cleanup old combo: */
+ m_shownKeys.clear();
+ /* Parse newly passed combo: */
+ QList<int> keyCodeList = UIHostCombo::toKeyCodeList(strCombo.toString());
+ for (int i = 0; i < keyCodeList.size(); ++i)
+ if (int iKeyCode = keyCodeList[i])
+ m_shownKeys.insert(iKeyCode, UINativeHotKey::toString(iKeyCode));
+ /* Update text: */
+ updateText();
+}
+
+UIHostComboWrapper UIHostComboEditorPrivate::combo() const
+{
+ /* Compose current combination: */
+ QStringList keyCodeStringList;
+ QList<int> keyCodeList = m_shownKeys.keys();
+ for (int i = 0; i < keyCodeList.size(); ++i)
+ keyCodeStringList << QString::number(keyCodeList[i]);
+ /* Return current combination or "0" for "None": */
+ return keyCodeStringList.isEmpty() ? "0" : keyCodeStringList.join(",");
+}
+
+void UIHostComboEditorPrivate::sltDeselect()
+{
+ deselect();
+}
+
+void UIHostComboEditorPrivate::sltClear()
+{
+ /* Cleanup combo: */
+ m_shownKeys.clear();
+ /* Update text: */
+ updateText();
+ /* Move the focus to text-field: */
+ setFocus();
+ /* Notify data changed: */
+ emit sigDataChanged();
+}
+
+#ifdef VBOX_IS_QT6_OR_LATER /* long replaced with qintptr since 6.0 */
+bool UIHostComboEditorPrivate::nativeEvent(const QByteArray &eventType, void *pMessage, qintptr *pResult)
+#else
+bool UIHostComboEditorPrivate::nativeEvent(const QByteArray &eventType, void *pMessage, long *pResult)
+#endif
+{
+# if defined(VBOX_WS_MAC)
+
+ /* Make sure it's generic NSEvent: */
+ if (eventType != "mac_generic_NSEvent")
+ return QLineEdit::nativeEvent(eventType, pMessage, pResult);
+ EventRef event = static_cast<EventRef>(darwinCocoaToCarbonEvent(pMessage));
+
+ /* Check if some NSEvent should be filtered out: */
+ // Returning @c true means filtering-out,
+ // Returning @c false means passing event to Qt.
+ switch(::GetEventClass(event))
+ {
+ /* Watch for keyboard-events: */
+ case kEventClassKeyboard:
+ {
+ switch(::GetEventKind(event))
+ {
+ /* Watch for keyboard-modifier-events: */
+ case kEventRawKeyModifiersChanged:
+ {
+ /* Get modifier mask: */
+ UInt32 modifierMask = 0;
+ ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
+ NULL, sizeof(modifierMask), NULL, &modifierMask);
+ modifierMask = ::DarwinAdjustModifierMask(modifierMask, pMessage);
+
+ /* Do not handle unchanged masks: */
+ UInt32 uChanged = m_uDarwinKeyModifiers ^ modifierMask;
+ if (!uChanged)
+ break;
+
+ /* Convert to keycode: */
+ unsigned uKeyCode = ::DarwinModifierMaskToDarwinKeycode(uChanged);
+
+ /* Do not handle empty and multiple modifier changes: */
+ if (!uKeyCode || uKeyCode == ~0U)
+ break;
+
+ /* Handle key-event: */
+ if (processKeyEvent(uKeyCode, uChanged & modifierMask))
+ {
+ /* Save the new modifier mask state: */
+ m_uDarwinKeyModifiers = modifierMask;
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+# elif defined(VBOX_WS_WIN)
+
+ /* Make sure it's generic MSG event: */
+ if (eventType != "windows_generic_MSG")
+ return QLineEdit::nativeEvent(eventType, pMessage, pResult);
+ MSG *pEvent = static_cast<MSG*>(pMessage);
+
+ /* Check if some MSG event should be filtered out: */
+ // Returning @c true means filtering-out,
+ // Returning @c false means passing event to Qt.
+ switch (pEvent->message)
+ {
+ /* Watch for key-events: */
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ {
+ /* Parse key-event: */
+ int iKeyCode = UINativeHotKey::distinguishModifierVKey((int)pEvent->wParam, (int)pEvent->lParam);
+ unsigned iDownScanCode = (pEvent->lParam >> 16) & 0x7F;
+ const bool fPressed = !(pEvent->lParam & 0x80000000);
+ const bool fExtended = pEvent->lParam & 0x1000000;
+
+ /* If present - why not just assert this? */
+ if (m_pAltGrMonitor)
+ {
+ /* Update AltGR monitor state from key-event: */
+ m_pAltGrMonitor->updateStateFromKeyEvent(iDownScanCode, fPressed, fExtended);
+ /* And release left Ctrl key early (if required): */
+ if (m_pAltGrMonitor->isLeftControlReleaseNeeded())
+ {
+ m_pressedKeys.remove(VK_LCONTROL);
+ m_shownKeys.remove(VK_LCONTROL);
+ }
+ // WORKAROUND:
+ // Fake LCtrl release events can also end up in the released
+ // key set. Detect them on the immediately following RAlt up.
+ if (!m_pressedKeys.contains(VK_LCONTROL))
+ m_releasedKeys.remove(VK_LCONTROL);
+ }
+
+ /* Handle key-event: */
+ return processKeyEvent(iKeyCode, (pEvent->message == WM_KEYDOWN || pEvent->message == WM_SYSKEYDOWN));
+ }
+ default:
+ break;
+ }
+
+# elif defined(VBOX_WS_X11)
+
+ /* Make sure it's generic XCB event: */
+ if (eventType != "xcb_generic_event_t")
+ return QLineEdit::nativeEvent(eventType, pMessage, pResult);
+ xcb_generic_event_t *pEvent = static_cast<xcb_generic_event_t*>(pMessage);
+
+ /* Check if some XCB event should be filtered out: */
+ // Returning @c true means filtering-out,
+ // Returning @c false means passing event to Qt.
+ switch (pEvent->response_type & ~0x80)
+ {
+ /* Watch for key-events: */
+ case XCB_KEY_PRESS:
+ case XCB_KEY_RELEASE:
+ {
+ /* Parse key-event: */
+ xcb_key_press_event_t *pKeyEvent = static_cast<xcb_key_press_event_t*>(pMessage);
+ RT_GCC_NO_WARN_DEPRECATED_BEGIN
+ const KeySym ks = ::XKeycodeToKeysym(NativeWindowSubsystem::X11GetDisplay(), pKeyEvent->detail, 0);
+ RT_GCC_NO_WARN_DEPRECATED_END
+ const int iKeySym = static_cast<int>(ks);
+
+ /* Handle key-event: */
+ return processKeyEvent(iKeySym, (pEvent->response_type & ~0x80) == XCB_KEY_PRESS);
+ }
+ default:
+ break;
+ }
+
+# else
+
+# warning "port me!"
+
+# endif
+
+ /* Call to base-class: */
+ return QLineEdit::nativeEvent(eventType, pMessage, pResult);
+}
+
+void UIHostComboEditorPrivate::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Ignore most of key presses... */
+ switch (pEvent->key())
+ {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_Tab:
+ case Qt::Key_Backtab:
+ case Qt::Key_Escape:
+ return QLineEdit::keyPressEvent(pEvent);
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ pEvent->ignore();
+ return;
+ default:
+ break;
+ }
+}
+
+void UIHostComboEditorPrivate::keyReleaseEvent(QKeyEvent *pEvent)
+{
+ /* Ignore most of key presses... */
+ switch (pEvent->key())
+ {
+ case Qt::Key_Tab:
+ case Qt::Key_Backtab:
+ case Qt::Key_Escape:
+ return QLineEdit::keyReleaseEvent(pEvent);
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ pEvent->ignore();
+ return;
+ default:
+ break;
+ }
+}
+
+void UIHostComboEditorPrivate::mousePressEvent(QMouseEvent *pEvent)
+{
+ /* Handle like for usual QWidget: */
+ QWidget::mousePressEvent(pEvent);
+}
+
+void UIHostComboEditorPrivate::mouseReleaseEvent(QMouseEvent *pEvent)
+{
+ /* Handle like for usual QWidget: */
+ QWidget::mouseReleaseEvent(pEvent);
+}
+
+void UIHostComboEditorPrivate::sltReleasePendingKeys()
+{
+ /* Stop the timer, we process all pending keys at once: */
+ m_pReleaseTimer->stop();
+ /* Something to do? */
+ if (!m_releasedKeys.isEmpty())
+ {
+ /* Remove every key: */
+ QSetIterator<int> iterator(m_releasedKeys);
+ while (iterator.hasNext())
+ {
+ int iKeyCode = iterator.next();
+ m_pressedKeys.remove(iKeyCode);
+ m_shownKeys.remove(iKeyCode);
+ }
+ m_releasedKeys.clear();
+ if (m_pressedKeys.isEmpty())
+ m_fStartNewSequence = true;
+ /* Notify data changed: */
+ emit sigDataChanged();
+ }
+ /* Make sure the user see what happens: */
+ updateText();
+}
+
+bool UIHostComboEditorPrivate::processKeyEvent(int iKeyCode, bool fKeyPress)
+{
+ /* Check if symbol is valid else pass it to Qt: */
+ if (!UINativeHotKey::isValidKey(iKeyCode))
+ return false;
+
+ /* Stop the release-pending-keys timer: */
+ m_pReleaseTimer->stop();
+
+ /* Key press: */
+ if (fKeyPress)
+ {
+ /* Clear reflected symbols if new sequence started: */
+ if (m_fStartNewSequence)
+ m_shownKeys.clear();
+ /* Make sure any keys pending for releasing are processed: */
+ sltReleasePendingKeys();
+ /* Check maximum combo size: */
+ if (m_shownKeys.size() < UIHostCombo::m_iMaxComboSize)
+ {
+ /* Remember pressed symbol: */
+ m_pressedKeys << iKeyCode;
+ m_shownKeys.insert(iKeyCode, UINativeHotKey::toString(iKeyCode));
+ /* Remember what we already started a sequence: */
+ m_fStartNewSequence = false;
+ /* Notify data changed: */
+ emit sigDataChanged();
+ }
+ }
+ /* Key release: */
+ else
+ {
+ /* Queue released symbol for processing: */
+ m_releasedKeys << iKeyCode;
+
+ /* If all pressed keys are now pending for releasing we should stop further handling.
+ * Now we have the status the user want: */
+ if (m_pressedKeys == m_releasedKeys)
+ {
+ m_pressedKeys.clear();
+ m_releasedKeys.clear();
+ m_fStartNewSequence = true;
+ }
+ else
+ m_pReleaseTimer->start();
+ }
+
+ /* Update text: */
+ updateText();
+
+ /* Prevent passing to Qt: */
+ return true;
+}
+
+void UIHostComboEditorPrivate::updateText()
+{
+ QStringList shownKeyNames(m_shownKeys.values());
+ setText(shownKeyNames.isEmpty() ? UIHostComboEditor::tr("None") : shownKeyNames.join(" + "));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHostComboEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHostComboEditor.h
new file mode 100644
index 00000000..d811be45
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHostComboEditor.h
@@ -0,0 +1,261 @@
+/* $Id: UIHostComboEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIHostComboEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIHostComboEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIHostComboEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QLineEdit>
+#include <QMap>
+#include <QMetaType>
+#include <QSet>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QString;
+class QWidget;
+class QIToolButton;
+class UIHostComboEditorPrivate;
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+class ComboEditorEventFilter;
+#endif
+#ifdef VBOX_WS_WIN
+class WinAltGrMonitor;
+#endif
+
+
+/** Native hot-key namespace to unify
+ * all the related hot-key processing stuff. */
+namespace UINativeHotKey
+{
+ /** Translates passed @a iKeyCode to string. */
+ SHARED_LIBRARY_STUFF QString toString(int iKeyCode);
+
+ /** Returns whether passed @a iKeyCode is valid. */
+ SHARED_LIBRARY_STUFF bool isValidKey(int iKeyCode);
+
+ /** Translates passed @a iKeyCode in host platform
+ * encoding to the corresponding set 1 PC scan code.
+ * @note Non-modifier keys will return zero. */
+ SHARED_LIBRARY_STUFF unsigned modifierToSet1ScanCode(int iKeyCode);
+
+#if defined(VBOX_WS_WIN)
+ /** Distinguishes modifier VKey by @a wParam and @a lParam. */
+ SHARED_LIBRARY_STUFF int distinguishModifierVKey(int wParam, int lParam);
+#elif defined(VBOX_WS_X11)
+ /** Retranslates key names. */
+ SHARED_LIBRARY_STUFF void retranslateKeyNames();
+#endif
+}
+
+
+/** Host-combo namespace to unify
+ * all the related hot-combo processing stuff. */
+namespace UIHostCombo
+{
+ /** Returns host-combo modifier index. */
+ SHARED_LIBRARY_STUFF int hostComboModifierIndex();
+ /** Returns host-combo modifier name. */
+ SHARED_LIBRARY_STUFF QString hostComboModifierName();
+ /** Returns host-combo cached key. */
+ SHARED_LIBRARY_STUFF QString hostComboCacheKey();
+
+ /** Translates passed @strKeyCombo to readable string. */
+ SHARED_LIBRARY_STUFF QString toReadableString(const QString &strKeyCombo);
+ /** Translates passed @strKeyCombo to key codes list. */
+ SHARED_LIBRARY_STUFF QList<int> toKeyCodeList(const QString &strKeyCombo);
+
+ /** Returns a sequence of the set 1 PC scan codes for all
+ * modifiers contained in the (host platform format) sequence passed. */
+ SHARED_LIBRARY_STUFF QList<unsigned> modifiersToScanCodes(const QString &strKeyCombo);
+
+ /** Returns whether passed @a strKeyCombo is valid. */
+ SHARED_LIBRARY_STUFF bool isValidKeyCombo(const QString &strKeyCombo);
+}
+
+
+/** Host-combo QString wrapper. */
+class SHARED_LIBRARY_STUFF UIHostComboWrapper
+{
+public:
+
+ /** Constructs wrapper on the basis of passed @a strHostCombo. */
+ UIHostComboWrapper(const QString &strHostCombo = QString())
+ : m_strHostCombo(strHostCombo)
+ {}
+
+ /** Returns the host-combo. */
+ const QString &toString() const { return m_strHostCombo; }
+
+private:
+
+ /** Holds the host-combo. */
+ QString m_strHostCombo;
+};
+Q_DECLARE_METATYPE(UIHostComboWrapper);
+
+
+/** Host-combo editor widget. */
+class SHARED_LIBRARY_STUFF UIHostComboEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+ Q_PROPERTY(UIHostComboWrapper combo READ combo WRITE setCombo USER true);
+
+signals:
+
+ /** Notifies listener about data should be committed. */
+ void sigCommitData(QWidget *pThis);
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIHostComboEditor(QWidget *pParent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Notifies listener about data should be committed. */
+ void sltCommitData();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Defines host @a strCombo sequence. */
+ void setCombo(const UIHostComboWrapper &strCombo);
+ /** Returns host-combo sequence. */
+ UIHostComboWrapper combo() const;
+
+ /** UIHostComboEditorPrivate instance. */
+ UIHostComboEditorPrivate *m_pEditor;
+ /** <b>Clear</b> QIToolButton instance. */
+ QIToolButton *m_pButtonClear;
+};
+
+
+/** Host-combo editor widget private stuff. */
+class SHARED_LIBRARY_STUFF UIHostComboEditorPrivate : public QLineEdit
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies parent about data changed. */
+ void sigDataChanged();
+
+public:
+
+ /** Constructs editor private part. */
+ UIHostComboEditorPrivate();
+ /** Destructs editor private part. */
+ ~UIHostComboEditorPrivate();
+
+ /** Defines host @a strCombo sequence. */
+ void setCombo(const UIHostComboWrapper &strCombo);
+ /** Returns host-combo sequence. */
+ UIHostComboWrapper combo() const;
+
+public slots:
+
+ /** Clears the host-combo selection. */
+ void sltDeselect();
+ /** Clears the host-combo editor. */
+ void sltClear();
+
+protected:
+
+ /** Handles native events. */
+#ifdef VBOX_IS_QT6_OR_LATER /* long replaced with qintptr since 6.0 */
+ virtual bool nativeEvent(const QByteArray &eventType, void *pMessage, qintptr *pResult) RT_OVERRIDE;
+#else
+ virtual bool nativeEvent(const QByteArray &eventType, void *pMessage, long *pResult) RT_OVERRIDE;
+#endif
+
+ /** Handles key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+ /** Handles key-release @a pEvent. */
+ virtual void keyReleaseEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-press @a pEvent. */
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-release @a pEvent. */
+ virtual void mouseReleaseEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Releases pending keys. */
+ void sltReleasePendingKeys();
+
+private:
+
+ /** PRocesses key event of @a fKeyPress type for a passed @a iKeyCode. */
+ bool processKeyEvent(int iKeyCode, bool fKeyPress);
+
+ /** Updates text. */
+ void updateText();
+
+ /** Holds the pressed keys. */
+ QSet<int> m_pressedKeys;
+ /** Holds the released keys. */
+ QSet<int> m_releasedKeys;
+ /** Holds the shown keys. */
+ QMap<int, QString> m_shownKeys;
+
+ /** Holds the release timer instance. */
+ QTimer *m_pReleaseTimer;
+
+ /** Holds whether new sequence should be started. */
+ bool m_fStartNewSequence;
+
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+ /** Mac, Win: Holds the native event filter instance. */
+ ComboEditorEventFilter *m_pPrivateEventFilter;
+ /** Mac, Win: Allows the native event filter to redirect events directly to nativeEvent handler. */
+ friend class ComboEditorEventFilter;
+#endif /* VBOX_WS_MAC || VBOX_WS_WIN */
+
+#if defined(VBOX_WS_MAC)
+ /** Mac: Holds the current modifier key mask. */
+ uint32_t m_uDarwinKeyModifiers;
+#elif defined(VBOX_WS_WIN)
+ /** Win: Holds the object monitoring key event stream for problematic AltGr events. */
+ WinAltGrMonitor *m_pAltGrMonitor;
+#endif /* VBOX_WS_WIN */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIHostComboEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHotKeyEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHotKeyEditor.cpp
new file mode 100644
index 00000000..85c29f27
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHotKeyEditor.cpp
@@ -0,0 +1,505 @@
+/* $Id: UIHotKeyEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIHotKeyEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QKeyEvent>
+#include <QStyle>
+
+/* GUI includes; */
+#include "UIHostComboEditor.h"
+#include "UIHotKeyEditor.h"
+#include "UIIconPool.h"
+#include "QIToolButton.h"
+
+
+/** QLineEdit extension representing hot-key editor. */
+class UIHotKeyLineEdit : public QLineEdit
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs hot-key editor passing @a pParent to the base-class. */
+ UIHotKeyLineEdit(QWidget *pParent);
+
+protected slots:
+
+ /** Deselects the hot-key editor text. */
+ void sltDeselect() { deselect(); }
+
+protected:
+
+ /** Handles key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+ /** Handles key-release @a pEvent. */
+ virtual void keyReleaseEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Returns whether the passed @a pevent should be ignored. */
+ bool isKeyEventIgnored(QKeyEvent *pEvent);
+};
+
+
+/*********************************************************************************************************************************
+* Class UIHotKeyLineEdit implementation. *
+*********************************************************************************************************************************/
+
+UIHotKeyLineEdit::UIHotKeyLineEdit(QWidget *pParent)
+ : QLineEdit(pParent)
+{
+ /* Configure self: */
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
+ setContextMenuPolicy(Qt::NoContextMenu);
+
+ /* Connect selection preserver: */
+ connect(this, &UIHotKeyLineEdit::selectionChanged, this, &UIHotKeyLineEdit::sltDeselect);
+}
+
+void UIHotKeyLineEdit::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Is this event ignored? */
+ if (isKeyEventIgnored(pEvent))
+ return;
+ /* Call to base-class: */
+ QLineEdit::keyPressEvent(pEvent);
+}
+
+void UIHotKeyLineEdit::keyReleaseEvent(QKeyEvent *pEvent)
+{
+ /* Is this event ignored? */
+ if (isKeyEventIgnored(pEvent))
+ return;
+ /* Call to base-class: */
+ QLineEdit::keyReleaseEvent(pEvent);
+}
+
+bool UIHotKeyLineEdit::isKeyEventIgnored(QKeyEvent *pEvent)
+{
+ /* Ignore some keys: */
+ switch (pEvent->key())
+ {
+ /* Ignore cursor keys: */
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ pEvent->ignore();
+ return true;
+ /* Default handling for others: */
+ default: break;
+ }
+ /* Do not ignore key by default: */
+ return false;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIHotKeyEditor implementation. *
+*********************************************************************************************************************************/
+
+UIHotKeyEditor::UIHotKeyEditor(QWidget *pParent)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fIsModifiersAllowed(false)
+ , m_pMainLayout(new QHBoxLayout(this))
+ , m_pButtonLayout(new QHBoxLayout)
+ , m_pLineEdit(new UIHotKeyLineEdit(this))
+ , m_pResetButton(new QIToolButton(this))
+ , m_pClearButton(new QIToolButton(this))
+ , m_iTakenKey(-1)
+ , m_fSequenceTaken(false)
+{
+ /* Make sure QIStyledDelegate aware of us: */
+ setProperty("has_sigCommitData", true);
+ /* Configure self: */
+ setAutoFillBackground(true);
+ setFocusProxy(m_pLineEdit);
+
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setSpacing(5);
+#else
+ m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+ m_pMainLayout->addWidget(m_pLineEdit);
+ m_pMainLayout->addLayout(m_pButtonLayout);
+
+ /* Configure button layout: */
+ m_pButtonLayout->setSpacing(0);
+ m_pButtonLayout->setContentsMargins(0, 0, 0, 0);
+ m_pButtonLayout->addWidget(m_pResetButton);
+ m_pButtonLayout->addWidget(m_pClearButton);
+
+ /* Configure line-edit: */
+ m_pLineEdit->installEventFilter(this);
+
+ /* Configure tool-buttons: */
+ m_pResetButton->removeBorder();
+ m_pResetButton->setIcon(UIIconPool::iconSet(":/import_16px.png"));
+ connect(m_pResetButton, &QToolButton::clicked, this, &UIHotKeyEditor::sltReset);
+ m_pClearButton->removeBorder();
+ m_pClearButton->setIcon(UIIconPool::iconSet(":/eraser_16px.png"));
+ connect(m_pClearButton, &QToolButton::clicked, this, &UIHotKeyEditor::sltClear);
+
+ /* Translate finally: */
+ retranslateUi();
+}
+
+void UIHotKeyEditor::sltReset()
+{
+ /* Reset the seuence of the hot-key: */
+ m_hotKey.setSequence(m_hotKey.defaultSequence());
+ /* Redraw sequence: */
+ drawSequence();
+ /* Move the focut to text-field: */
+ m_pLineEdit->setFocus();
+ /* Commit data to the listener: */
+ emit sigCommitData(this);
+}
+
+void UIHotKeyEditor::sltClear()
+{
+ /* Clear the seuence of the hot-key: */
+ m_hotKey.setSequence(QString());
+ /* Redraw sequence: */
+ drawSequence();
+ /* Move the focut to text-field: */
+ m_pLineEdit->setFocus();
+ /* Commit data to the listener: */
+ emit sigCommitData(this);
+}
+
+bool UIHotKeyEditor::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* Special handling for our line-edit only: */
+ if (pWatched != m_pLineEdit)
+ return QWidget::eventFilter(pWatched, pEvent);
+
+ /* Special handling for key events only: */
+ if (pEvent->type() != QEvent::KeyPress &&
+ pEvent->type() != QEvent::KeyRelease)
+ return QWidget::eventFilter(pWatched, pEvent);
+
+ /* Cast passed event to required type: */
+ QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
+
+ /* Should we skip that event to our line-edit? */
+ if (shouldWeSkipKeyEventToLineEdit(pKeyEvent))
+ return false;
+
+ /* Fetch modifiers state: */
+ fetchModifiersState();
+
+ /* Handle key event: */
+ switch (pEvent->type())
+ {
+ case QEvent::KeyPress: handleKeyPress(pKeyEvent); break;
+ case QEvent::KeyRelease: handleKeyRelease(pKeyEvent); break;
+ default: break;
+ }
+
+ /* Fetch host-combo modifier state: */
+ checkIfHostModifierNeeded();
+
+ /* Reflect sequence: */
+ reflectSequence();
+
+ /* Prevent further key event handling: */
+ return true;
+}
+
+void UIHotKeyEditor::retranslateUi()
+{
+ m_pResetButton->setToolTip(tr("Reset shortcut to default"));
+ m_pClearButton->setToolTip(tr("Unset shortcut"));
+}
+
+void UIHotKeyEditor::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Is this event ignored? */
+ if (isKeyEventIgnored(pEvent))
+ return;
+ /* Call to base-class: */
+ return QWidget::keyPressEvent(pEvent);
+}
+
+void UIHotKeyEditor::keyReleaseEvent(QKeyEvent *pEvent)
+{
+ /* Is this event ignored? */
+ if (isKeyEventIgnored(pEvent))
+ return;
+ /* Call to base-class: */
+ return QWidget::keyReleaseEvent(pEvent);
+}
+
+bool UIHotKeyEditor::shouldWeSkipKeyEventToLineEdit(QKeyEvent *pEvent)
+{
+ /* Special handling for some keys: */
+ switch (pEvent->key())
+ {
+ /* Skip Escape to our line-edit: */
+ case Qt::Key_Escape: return true;
+ /* Skip Return/Enter to our line-edit: */
+ case Qt::Key_Return:
+ case Qt::Key_Enter: return true;
+ /* Skip cursor keys to our line-edit: */
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down: return true;
+ /* Default handling for others: */
+ default: break;
+ }
+ /* Do not skip by default: */
+ return false;
+}
+
+bool UIHotKeyEditor::isKeyEventIgnored(QKeyEvent *pEvent)
+{
+ /* Ignore some keys: */
+ switch (pEvent->key())
+ {
+ /* Ignore cursor keys: */
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ pEvent->ignore();
+ return true;
+ /* Default handling for others: */
+ default: break;
+ }
+ /* Do not ignore key by default: */
+ return false;
+}
+
+void UIHotKeyEditor::fetchModifiersState()
+{
+ /* Make sure modifiers are allowed: */
+ if (!m_fIsModifiersAllowed)
+ return;
+
+ /* If full sequence was not yet taken: */
+ if (!m_fSequenceTaken)
+ {
+ /* Recreate the set of taken modifiers: */
+ m_takenModifiers.clear();
+ Qt::KeyboardModifiers currentModifiers = QApplication::keyboardModifiers();
+ if (currentModifiers != Qt::NoModifier)
+ {
+ if ((m_takenModifiers.size() < 3) && (currentModifiers & Qt::ControlModifier))
+ m_takenModifiers << Qt::CTRL;
+ if ((m_takenModifiers.size() < 3) && (currentModifiers & Qt::AltModifier))
+ m_takenModifiers << Qt::ALT;
+ if ((m_takenModifiers.size() < 3) && (currentModifiers & Qt::MetaModifier))
+ m_takenModifiers << Qt::META;
+ }
+ }
+}
+
+void UIHotKeyEditor::checkIfHostModifierNeeded()
+{
+ /* Make sure other modifiers are NOT allowed: */
+ if (m_fIsModifiersAllowed)
+ return;
+
+ /* Clear the set of taken modifiers: */
+ m_takenModifiers.clear();
+
+ /* If taken key was set: */
+ if (m_iTakenKey != -1)
+ /* We have to add Host+ modifier: */
+ m_takenModifiers << UIHostCombo::hostComboModifierIndex();
+}
+
+bool UIHotKeyEditor::approvedKeyPressed(QKeyEvent *pKeyEvent)
+{
+ /* Qt by some reason generates text for complex cases like
+ * Backspace or Del but skip other similar things like
+ * F1 - F35, Home, End, Page UP, Page DOWN and so on.
+ * We should declare all the approved keys. */
+
+ /* Compose the set of the approved keys: */
+ QSet<int> approvedKeys;
+
+ /* Add Fn keys: */
+ for (int i = Qt::Key_F1; i <= Qt::Key_F35; ++i)
+ approvedKeys << i;
+
+ /* Add digit keys: */
+ for (int i = Qt::Key_0; i <= Qt::Key_9; ++i)
+ approvedKeys << i;
+
+ /* We allow to use only English letters in shortcuts.
+ * The reason is by some reason Qt distinguish native language
+ * letters only with no modifiers pressed.
+ * With modifiers pressed Qt thinks the letter is always English. */
+ for (int i = Qt::Key_A; i <= Qt::Key_Z; ++i)
+ approvedKeys << i;
+
+ /* Add few more special cases: */
+ approvedKeys << Qt::Key_Space << Qt::Key_Backspace
+ << Qt::Key_Insert << Qt::Key_Delete
+ << Qt::Key_Pause << Qt::Key_Print
+ << Qt::Key_Home << Qt::Key_End
+ << Qt::Key_PageUp << Qt::Key_PageDown
+ << Qt::Key_QuoteLeft << Qt::Key_AsciiTilde
+ << Qt::Key_Minus << Qt::Key_Underscore
+ << Qt::Key_Equal << Qt::Key_Plus
+ << Qt::Key_ParenLeft << Qt::Key_ParenRight
+ << Qt::Key_BraceLeft << Qt::Key_BraceRight
+ << Qt::Key_BracketLeft << Qt::Key_BracketRight
+ << Qt::Key_Backslash << Qt::Key_Bar
+ << Qt::Key_Semicolon << Qt::Key_Colon
+ << Qt::Key_Apostrophe << Qt::Key_QuoteDbl
+ << Qt::Key_Comma << Qt::Key_Period << Qt::Key_Slash
+ << Qt::Key_Less << Qt::Key_Greater << Qt::Key_Question;
+
+ /* Is this one of the approved keys? */
+ if (approvedKeys.contains(pKeyEvent->key()))
+ return true;
+
+ /* False by default: */
+ return false;
+}
+
+void UIHotKeyEditor::handleKeyPress(QKeyEvent *pKeyEvent)
+{
+ /* If full sequence was not yet taken: */
+ if (!m_fSequenceTaken)
+ {
+ /* If finalizing key is pressed: */
+ if (approvedKeyPressed(pKeyEvent))
+ {
+ /* Remember taken key: */
+ m_iTakenKey = pKeyEvent->key();
+ /* Mark full sequence taken: */
+ m_fSequenceTaken = true;
+ }
+ /* If something other is pressed: */
+ else
+ {
+ /* Clear taken key: */
+ m_iTakenKey = -1;
+ }
+ }
+}
+
+void UIHotKeyEditor::handleKeyRelease(QKeyEvent *pKeyEvent)
+{
+ /* If full sequence was taken already and no modifiers are currently held: */
+ if (m_fSequenceTaken && (pKeyEvent->modifiers() == Qt::NoModifier))
+ {
+ /* Reset taken sequence: */
+ m_fSequenceTaken = false;
+ }
+}
+
+void UIHotKeyEditor::reflectSequence()
+{
+ /* Acquire modifier names: */
+ QString strModifierNames;
+ QStringList modifierNames;
+ foreach (int iTakenModifier, m_takenModifiers)
+ {
+ if (iTakenModifier == UIHostCombo::hostComboModifierIndex())
+ modifierNames << UIHostCombo::hostComboModifierName();
+ else
+ modifierNames << QKeySequence(iTakenModifier).toString(QKeySequence::NativeText);
+ }
+ if (!modifierNames.isEmpty())
+ strModifierNames = modifierNames.join("");
+ /* Acquire main key name: */
+ QString strMainKeyName;
+ if (m_iTakenKey != -1)
+ strMainKeyName = QKeySequence(m_iTakenKey).toString(QKeySequence::NativeText);
+
+ /* Compose the text to reflect: */
+ QString strText;
+ /* If modifiers were set: */
+ if (!strModifierNames.isEmpty())
+ /* Append the text with modifier names: */
+ strText.append(strModifierNames);
+ /* If main key was set: */
+ if (!strMainKeyName.isEmpty())
+ /* Append the sequence with the main key name: */
+ strText.append(strMainKeyName);
+ /* Reflect what we've got: */
+ m_pLineEdit->setText(strText);
+
+ /* Compose the sequence to save: */
+ QString strSequence;
+ /* If main key was set: */
+ if (!strMainKeyName.isEmpty())
+ {
+ /* Append the sequence with the main key name: */
+ strSequence.append(strMainKeyName);
+ /* If modifiers are allowed: */
+ if (m_fIsModifiersAllowed)
+ /* Prepend the sequence with modifier names: */
+ strSequence.prepend(strModifierNames);
+ }
+ /* Save what we've got: */
+ m_hotKey.setSequence(strSequence);
+ /* Commit data to the listener: */
+ emit sigCommitData(this);
+}
+
+void UIHotKeyEditor::drawSequence()
+{
+ /* Compose the text to reflect: */
+ QString strText = m_hotKey.sequence();
+ /* If modifiers are not allowed and the text is not empty: */
+ if (!m_fIsModifiersAllowed && !strText.isEmpty())
+ /* Prepend the text with Host+ modifier name: */
+ strText.prepend(UIHostCombo::hostComboModifierName());
+ /* Reflect what we've got: */
+ m_pLineEdit->setText(strText);
+}
+
+UIHotKey UIHotKeyEditor::hotKey() const
+{
+ /* Return hot-key: */
+ return m_hotKey;
+}
+
+void UIHotKeyEditor::setHotKey(const UIHotKey &hotKey)
+{
+ /* Remember passed hot-key: */
+ m_hotKey = hotKey;
+ /* Remember if modifiers are allowed: */
+ m_fIsModifiersAllowed = m_hotKey.type() == UIHotKeyType_WithModifiers;
+ /* Redraw sequence: */
+ drawSequence();
+}
+
+
+#include "UIHotKeyEditor.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHotKeyEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHotKeyEditor.h
new file mode 100644
index 00000000..ba7f5509
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHotKeyEditor.h
@@ -0,0 +1,201 @@
+/* $Id: UIHotKeyEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIHotKeyEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIHotKeyEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIHotKeyEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMetaType>
+#include <QSet>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QIToolButton;
+class UIHotKeyLineEdit;
+
+
+/** Hot key types. */
+enum UIHotKeyType
+{
+ UIHotKeyType_Simple,
+ UIHotKeyType_WithModifiers
+};
+
+
+/** A string pair wrapper for hot-key sequence. */
+class UIHotKey
+{
+public:
+
+ /** Constructs null hot-key sequence. */
+ UIHotKey()
+ : m_enmType(UIHotKeyType_Simple)
+ {}
+ /** Constructs hot-key sequence on the basis of passed @a enmType, @a strSequence and @a strDefaultSequence. */
+ UIHotKey(UIHotKeyType enmType, const QString &strSequence, const QString &strDefaultSequence)
+ : m_enmType(enmType)
+ , m_strSequence(strSequence)
+ , m_strDefaultSequence(strDefaultSequence)
+ {}
+ /** Constructs hot-key sequence on the basis of @a other hot-key sequence. */
+ UIHotKey(const UIHotKey &other)
+ : m_enmType(other.type())
+ , m_strSequence(other.sequence())
+ , m_strDefaultSequence(other.defaultSequence())
+ {}
+
+ /** Makes a copy of the given other hot-key sequence and assigns it to this one. */
+ UIHotKey &operator=(const UIHotKey &other)
+ {
+ m_enmType = other.type();
+ m_strSequence = other.sequence();
+ m_strDefaultSequence = other.defaultSequence();
+ return *this;
+ }
+
+ /** Returns the type of this hot-key sequence. */
+ UIHotKeyType type() const { return m_enmType; }
+
+ /** Returns hot-key sequence. */
+ const QString &sequence() const { return m_strSequence; }
+ /** Returns default hot-key sequence. */
+ const QString &defaultSequence() const { return m_strDefaultSequence; }
+ /** Defines hot-key @a strSequence. */
+ void setSequence(const QString &strSequence) { m_strSequence = strSequence; }
+
+private:
+
+ /** Holds the type of this hot-key sequence. */
+ UIHotKeyType m_enmType;
+ /** Holds the hot-key sequence. */
+ QString m_strSequence;
+ /** Holds the default hot-key sequence. */
+ QString m_strDefaultSequence;
+};
+Q_DECLARE_METATYPE(UIHotKey);
+
+
+/** QWidget subclass wrapping real hot-key editor. */
+class SHARED_LIBRARY_STUFF UIHotKeyEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+ Q_PROPERTY(UIHotKey hotKey READ hotKey WRITE setHotKey USER true);
+
+signals:
+
+ /** Notifies listener about data should be committed. */
+ void sigCommitData(QWidget *pThis);
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIHotKeyEditor(QWidget *pParent);
+
+private slots:
+
+ /** Resets hot-key sequence to default. */
+ void sltReset();
+ /** Clears hot-key sequence. */
+ void sltClear();
+
+protected:
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+ /** Handles key-release @a pEvent. */
+ virtual void keyReleaseEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Returns whether we hould skip key-event to line-edit. */
+ bool shouldWeSkipKeyEventToLineEdit(QKeyEvent *pEvent);
+
+ /** Returns whether key @a pEvent is ignored. */
+ bool isKeyEventIgnored(QKeyEvent *pEvent);
+
+ /** Fetches actual modifier states. */
+ void fetchModifiersState();
+ /** Returns whether Host+ modifier is required. */
+ void checkIfHostModifierNeeded();
+
+ /** Handles approved key-press @a pEvent. */
+ bool approvedKeyPressed(QKeyEvent *pEvent);
+ /** Handles key-press @a pEvent. */
+ void handleKeyPress(QKeyEvent *pEvent);
+ /** Handles key-release @a pEvent. */
+ void handleKeyRelease(QKeyEvent *pEvent);
+ /** Reflects recorded sequence in editor. */
+ void reflectSequence();
+ /** Draws recorded sequence in editor. */
+ void drawSequence();
+
+ /** Returns hot-key. */
+ UIHotKey hotKey() const;
+ /** Defines @a hotKey. */
+ void setHotKey(const UIHotKey &hotKey);
+
+ /** Holds the hot-key. */
+ UIHotKey m_hotKey;
+
+ /** Holds whether the modifiers are allowed. */
+ bool m_fIsModifiersAllowed;
+
+ /** Holds the main-layout instance. */
+ QHBoxLayout *m_pMainLayout;
+ /** Holds the button-layout instance. */
+ QHBoxLayout *m_pButtonLayout;
+ /** Holds the line-edit instance. */
+ UIHotKeyLineEdit *m_pLineEdit;
+ /** Holds the reset-button instance. */
+ QIToolButton *m_pResetButton;
+ /** Holds the clear-button instance. */
+ QIToolButton *m_pClearButton;
+
+ /** Holds the taken modifiers. */
+ QSet<int> m_takenModifiers;
+ /** Holds the taken key. */
+ int m_iTakenKey;
+ /** Holds whether sequence is taken. */
+ bool m_fSequenceTaken;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIHotKeyEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UILanguageSettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UILanguageSettingsEditor.cpp
new file mode 100644
index 00000000..875bed3b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UILanguageSettingsEditor.cpp
@@ -0,0 +1,440 @@
+/* $Id: UILanguageSettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UILanguageSettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QHeaderView>
+#include <QPainter>
+#include <QRegExp>
+#include <QTranslator>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILabelSeparator.h"
+#include "QIRichTextLabel.h"
+#include "QITreeWidget.h"
+#include "UILanguageSettingsEditor.h"
+#include "UITranslator.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+
+
+/** QITreeWidgetItem subclass representing language tree-widget item. */
+class UILanguageItem : public QITreeWidgetItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs language tree-widget item passing @a pParent to the base-class.
+ * @param translator Brings the translator this item is related to.
+ * @param strId Brings the language ID this item is related to.
+ * @param fBuiltIn Brings whether the language this item related to is built in. */
+ UILanguageItem(QITreeWidget *pParent, const QTranslator &translator,
+ const QString &strId, bool fBuiltIn = false);
+ /** Constructs language tree-widget item passing @a pParent to the base-class.
+ * @param strId Brings the language ID this item is related to.
+ * @note This is a constructor for an invalid language ID (i.e. when a
+ * language file is missing or corrupt). */
+ UILanguageItem(QITreeWidget *pParent, const QString &strId);
+ /** Constructs language tree-widget item passing @a pParent to the base-class.
+ * @note This is a constructor for a default language ID
+ * (column 1 will be set to QString()). */
+ UILanguageItem(QITreeWidget *pParent);
+
+ /** Returns whether this item is for built in language. */
+ bool isBuiltIn() const { return m_fBuiltIn; }
+
+ /** Returns whether this item is less than @a another one. */
+ bool operator<(const QTreeWidgetItem &another) const;
+
+private:
+
+ /** Performs translation using passed @a translator for a
+ * passed @a pContext, @a pSourceText and @a pComment. */
+ QString tratra(const QTranslator &translator, const char *pContext,
+ const char *pSourceText, const char *pComment);
+
+ /** Holds whether this item is for built in language. */
+ bool m_fBuiltIn;
+};
+
+
+/*********************************************************************************************************************************
+* Class UILanguageItem implementation. *
+*********************************************************************************************************************************/
+
+UILanguageItem::UILanguageItem(QITreeWidget *pParent, const QTranslator &translator,
+ const QString &strId, bool fBuiltIn /* = false */)
+ : QITreeWidgetItem(pParent)
+ , m_fBuiltIn(fBuiltIn)
+{
+ Assert(!strId.isEmpty());
+
+ /* Note: context/source/comment arguments below must match strings used in UITranslator::languageName() and friends
+ * (the latter are the source of information for the lupdate tool that generates translation files). */
+
+ const QString strNativeLanguage = tratra(translator, "@@@", "English", "Native language name");
+ const QString strNativeCountry = tratra(translator, "@@@", "--", "Native language country name "
+ "(empty if this language is for all countries)");
+
+ const QString strEnglishLanguage = tratra(translator, "@@@", "English", "Language name, in English");
+ const QString strEnglishCountry = tratra(translator, "@@@", "--", "Language country name, in English "
+ "(empty if native country name is empty)");
+
+ const QString strTranslatorsName = tratra(translator, "@@@", "Oracle Corporation", "Comma-separated list of translators");
+
+ QString strItemName = strNativeLanguage;
+ QString strLanguageName = strEnglishLanguage;
+
+ if (!m_fBuiltIn)
+ {
+ if (strNativeCountry != "--")
+ strItemName += " (" + strNativeCountry + ")";
+
+ if (strEnglishCountry != "--")
+ strLanguageName += " (" + strEnglishCountry + ")";
+
+ if (strItemName != strLanguageName)
+ strLanguageName = strItemName + " / " + strLanguageName;
+ }
+ else
+ {
+ strItemName += tr(" (built-in)", "Language");
+ strLanguageName += tr(" (built-in)", "Language");
+ }
+
+ setText(0, strItemName);
+ setText(1, strId);
+ setText(2, strLanguageName);
+ setText(3, strTranslatorsName);
+
+ /* Current language appears in bold: */
+ if (text(1) == UITranslator::languageId())
+ {
+ QFont fnt = font(0);
+ fnt.setBold(true);
+ setFont(0, fnt);
+ }
+}
+
+UILanguageItem::UILanguageItem(QITreeWidget *pParent, const QString &strId)
+ : QITreeWidgetItem(pParent)
+ , m_fBuiltIn(false)
+{
+ Assert(!strId.isEmpty());
+
+ setText(0, QString("<%1>").arg(strId));
+ setText(1, strId);
+ setText(2, tr("<unavailable>", "Language"));
+ setText(3, tr("<unknown>", "Author(s)"));
+
+ /* Invalid language appears in italic: */
+ QFont fnt = font(0);
+ fnt.setItalic(true);
+ setFont(0, fnt);
+}
+
+UILanguageItem::UILanguageItem(QITreeWidget *pParent)
+ : QITreeWidgetItem(pParent)
+ , m_fBuiltIn(false)
+{
+ setText(0, tr("Default", "Language"));
+ setText(1, QString());
+ /* Empty strings of some reasonable length to prevent the info part
+ * from being shrinked too much when the list wants to be wider */
+ setText(2, " ");
+ setText(3, " ");
+
+ /* Default language item appears in italic: */
+ QFont fnt = font(0);
+ fnt.setItalic(true);
+ setFont(0, fnt);
+}
+
+bool UILanguageItem::operator<(const QTreeWidgetItem &another) const
+{
+ QString thisId = text(1);
+ QString thatId = another.text(1);
+ if (thisId.isNull())
+ return true;
+ if (thatId.isNull())
+ return false;
+ if (m_fBuiltIn)
+ return true;
+ if (another.type() == ItemType && ((UILanguageItem*)&another)->m_fBuiltIn)
+ return false;
+ return QITreeWidgetItem::operator<(another);
+}
+
+QString UILanguageItem::tratra(const QTranslator &translator, const char *pContext,
+ const char *pSourceText, const char *pComment)
+{
+ QString strMsg = translator.translate(pContext, pSourceText, pComment);
+ /* Return the source text if no translation is found: */
+ if (strMsg.isEmpty())
+ strMsg = QString(pSourceText);
+ return strMsg;
+}
+
+
+/*********************************************************************************************************************************
+* Class UILanguageSettingsEditor implementation. *
+*********************************************************************************************************************************/
+
+UILanguageSettingsEditor::UILanguageSettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fPolished(false)
+ , m_pLabelSeparator(0)
+ , m_pTreeWidget(0)
+ , m_pLabelInfo(0)
+{
+ prepare();
+}
+
+void UILanguageSettingsEditor::setValue(const QString &strValue)
+{
+ /* Update cached value and
+ * tree-widget if value has changed: */
+ if (m_strValue != strValue)
+ {
+ m_strValue = strValue;
+ if (m_pTreeWidget)
+ reloadLanguageTree(m_strValue);
+ }
+}
+
+QString UILanguageSettingsEditor::value() const
+{
+ QTreeWidgetItem *pCurrentItem = m_pTreeWidget ? m_pTreeWidget->currentItem() : 0;
+ return pCurrentItem ? pCurrentItem->text(1) : m_strValue;
+}
+
+void UILanguageSettingsEditor::retranslateUi()
+{
+ /* Translate separator label: */
+ if (m_pLabelSeparator)
+ m_pLabelSeparator->setText(tr("&Interface Languages"));
+
+ /* Translate tree-widget: */
+ if (m_pTreeWidget)
+ {
+ m_pTreeWidget->setWhatsThis(tr("Lists all available user interface languages. The effective language is written "
+ "in bold. Select Default to reset to the system default language."));
+
+ /* Translate tree-widget header: */
+ QTreeWidgetItem *pTreeWidgetHeaderItem = m_pTreeWidget->headerItem();
+ if (pTreeWidgetHeaderItem)
+ {
+ pTreeWidgetHeaderItem->setText(3, tr("Author"));
+ pTreeWidgetHeaderItem->setText(2, tr("Language"));
+ pTreeWidgetHeaderItem->setText(1, tr("Id"));
+ pTreeWidgetHeaderItem->setText(0, tr("Name"));
+ }
+
+ /* Update tree-widget contents finally: */
+ reloadLanguageTree(m_strValue);
+ }
+}
+
+void UILanguageSettingsEditor::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI<QWidget>::showEvent(pEvent);
+
+ /* Polish if necessary: */
+ if (!m_fPolished)
+ {
+ polishEvent(pEvent);
+ m_fPolished = true;
+ }
+}
+
+void UILanguageSettingsEditor::polishEvent(QShowEvent * /* pEvent */)
+{
+ /* Remember current info-label width: */
+ m_pLabelInfo->setMinimumTextWidth(m_pLabelInfo->width());
+}
+
+void UILanguageSettingsEditor::sltHandleItemPainting(QTreeWidgetItem *pItem, QPainter *pPainter)
+{
+ /* We are always expecting an item: */
+ AssertPtrReturnVoid(pItem);
+ AssertReturnVoid(pItem->type() == QITreeWidgetItem::ItemType);
+
+ /* An item of required type: */
+ QITreeWidgetItem *pItemOfRequiredType = QITreeWidgetItem::toItem(pItem);
+ AssertPtrReturnVoid(pItemOfRequiredType);
+
+ /* A language item to be honest :) */
+ UILanguageItem *pLanguageItem = qobject_cast<UILanguageItem*>(pItemOfRequiredType);
+ AssertPtrReturnVoid(pLanguageItem);
+
+ /* For built in language item: */
+ if (pLanguageItem->isBuiltIn())
+ {
+ /* We are drawing a separator line in the tree: */
+ const QRect rect = m_pTreeWidget->visualItemRect(pLanguageItem);
+ pPainter->setPen(m_pTreeWidget->palette().color(QPalette::Window));
+ pPainter->drawLine(rect.x(), rect.y() + rect.height() - 1,
+ rect.x() + rect.width(), rect.y() + rect.height() - 1);
+ }
+}
+
+void UILanguageSettingsEditor::sltHandleCurrentItemChange(QTreeWidgetItem *pCurrentItem)
+{
+ /* Make sure item chosen: */
+ if (!pCurrentItem)
+ return;
+
+ /* Disable labels for the Default language item: */
+ const bool fEnabled = !pCurrentItem->text (1).isNull();
+ m_pLabelInfo->setEnabled(fEnabled);
+ m_pLabelInfo->setText(QString("<table>"
+ "<tr><td>%1&nbsp;</td><td>%2</td></tr>"
+ "<tr><td>%3&nbsp;</td><td>%4</td></tr>"
+ "</table>")
+ .arg(tr("Language:"))
+ .arg(pCurrentItem->text(2))
+ .arg(tr("Author(s):"))
+ .arg(pCurrentItem->text(3)));
+}
+
+void UILanguageSettingsEditor::prepare()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ pLayoutMain->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare separator: */
+ m_pLabelSeparator = new QILabelSeparator(this);
+ if (m_pLabelSeparator)
+ pLayoutMain->addWidget(m_pLabelSeparator);
+
+ /* Prepare tree-widget: */
+ m_pTreeWidget = new QITreeWidget(this);
+ if (m_pTreeWidget)
+ {
+ if (m_pLabelSeparator)
+ m_pLabelSeparator->setBuddy(m_pTreeWidget);
+ m_pTreeWidget->header()->hide();
+ m_pTreeWidget->setColumnCount(4);
+ m_pTreeWidget->hideColumn(1);
+ m_pTreeWidget->hideColumn(2);
+ m_pTreeWidget->hideColumn(3);
+ m_pTreeWidget->setRootIsDecorated(false);
+
+ pLayoutMain->addWidget(m_pTreeWidget);
+ }
+
+ /* Prepare info label: */
+ m_pLabelInfo = new QIRichTextLabel(this);
+ if (m_pLabelInfo)
+ {
+ m_pLabelInfo->setWordWrapMode(QTextOption::WordWrap);
+ m_pLabelInfo->setMinimumHeight(QFontMetrics(m_pLabelInfo->font(), m_pLabelInfo).height() * 5);
+
+ pLayoutMain->addWidget(m_pLabelInfo);
+ }
+ }
+
+ /* Prepare connections: */
+ connect(m_pTreeWidget, &QITreeWidget::painted, this, &UILanguageSettingsEditor::sltHandleItemPainting);
+ connect(m_pTreeWidget, &QITreeWidget::currentItemChanged, this, &UILanguageSettingsEditor::sltHandleCurrentItemChange);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UILanguageSettingsEditor::reloadLanguageTree(const QString &strLanguageId)
+{
+ /* Clear languages tree: */
+ m_pTreeWidget->clear();
+
+ /* Load languages tree: */
+ char szNlsPath[RTPATH_MAX];
+ const int rc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
+ AssertRC(rc);
+ const QString strNlsPath = QString(szNlsPath) + UITranslator::vboxLanguageSubDirectory();
+ QDir nlsDir(strNlsPath);
+ QStringList files = nlsDir.entryList(QStringList(QString("%1*%2").arg(UITranslator::vboxLanguageFileBase(),
+ UITranslator::vboxLanguageFileExtension())),
+ QDir::Files);
+
+ QTranslator translator;
+ /* Add the default language: */
+ new UILanguageItem(m_pTreeWidget);
+ /* Add the built-in language: */
+ new UILanguageItem(m_pTreeWidget, translator, UITranslator::vboxBuiltInLanguageName(), true /* built-in */);
+ /* Add all existing languages */
+ for (QStringList::Iterator it = files.begin(); it != files.end(); ++it)
+ {
+ QString strFileName = *it;
+ QRegExp regExp(UITranslator::vboxLanguageFileBase() + UITranslator::vboxLanguageIdRegExp());
+ int iPos = regExp.indexIn(strFileName);
+ if (iPos == -1)
+ continue;
+
+ /* Skip any English version, cause this is extra handled: */
+ QString strLanguage = regExp.cap(2);
+ if (strLanguage.toLower() == "en")
+ continue;
+
+ bool fLoadOk = translator.load(strFileName, strNlsPath);
+ if (!fLoadOk)
+ continue;
+
+ new UILanguageItem(m_pTreeWidget, translator, regExp.cap(1));
+ }
+
+ /* Adjust selector list: */
+ m_pTreeWidget->resizeColumnToContents(0);
+
+ /* Search for necessary language: */
+ QList<QTreeWidgetItem*> itemsList = m_pTreeWidget->findItems(strLanguageId, Qt::MatchExactly, 1);
+ QTreeWidgetItem *pItem = itemsList.isEmpty() ? 0 : itemsList[0];
+ if (!pItem)
+ {
+ /* Add an pItem for an invalid language to represent it in the list: */
+ pItem = new UILanguageItem(m_pTreeWidget, strLanguageId);
+ m_pTreeWidget->resizeColumnToContents(0);
+ }
+ Assert(pItem);
+ if (pItem)
+ m_pTreeWidget->setCurrentItem(pItem);
+
+ m_pTreeWidget->sortItems(0, Qt::AscendingOrder);
+ m_pTreeWidget->scrollToItem(pItem);
+}
+
+
+#include "UILanguageSettingsEditor.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UILanguageSettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UILanguageSettingsEditor.h
new file mode 100644
index 00000000..fb105b18
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UILanguageSettingsEditor.h
@@ -0,0 +1,103 @@
+/* $Id: UILanguageSettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UILanguageSettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UILanguageSettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UILanguageSettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declartions: */
+class QTreeWidgetItem;
+class QILabelSeparator;
+class QIRichTextLabel;
+class QITreeWidget;
+
+/** QWidget subclass used as a language settings editor. */
+class SHARED_LIBRARY_STUFF UILanguageSettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UILanguageSettingsEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a strValue. */
+ void setValue(const QString &strValue);
+ /** Returns editor value. */
+ QString value() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles polish @a pEvent. */
+ virtual void polishEvent(QShowEvent *pEvent);
+
+private slots:
+
+ /** Handles @a pItem painting with passed @a pPainter. */
+ void sltHandleItemPainting(QTreeWidgetItem *pItem, QPainter *pPainter);
+
+ /** Handles @a pCurrentItem change. */
+ void sltHandleCurrentItemChange(QTreeWidgetItem *pCurrentItem);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Reloads language list, choosing item with @a strLanguageId as current. */
+ void reloadLanguageTree(const QString &strLanguageId);
+
+ /** Holds whether the page is polished. */
+ bool m_fPolished;
+
+ /** Holds the value to be set. */
+ QString m_strValue;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the separator label instance. */
+ QILabelSeparator *m_pLabelSeparator;
+ /** Holds the tree-widget instance. */
+ QITreeWidget *m_pTreeWidget;
+ /** Holds the info label instance. */
+ QIRichTextLabel *m_pLabelInfo;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UILanguageSettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMaximumGuestScreenSizeEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMaximumGuestScreenSizeEditor.cpp
new file mode 100644
index 00000000..f3f8af7d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMaximumGuestScreenSizeEditor.cpp
@@ -0,0 +1,277 @@
+/* $Id: UIMaximumGuestScreenSizeEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMaximumGuestScreenSizeEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QSpinBox>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIMaximumGuestScreenSizeEditor.h"
+
+
+/*********************************************************************************************************************************
+* Class UIMaximumGuestScreenSizeValue implementation. *
+*********************************************************************************************************************************/
+
+UIMaximumGuestScreenSizeValue::UIMaximumGuestScreenSizeValue(MaximumGuestScreenSizePolicy enmPolicy /* = MaximumGuestScreenSizePolicy_Any */,
+ const QSize &size /* = QSize() */)
+ : m_enmPolicy(enmPolicy)
+ , m_size(size)
+{
+}
+
+bool UIMaximumGuestScreenSizeValue::equal(const UIMaximumGuestScreenSizeValue &other) const
+{
+ return true
+ && ( ( m_enmPolicy != MaximumGuestScreenSizePolicy_Fixed
+ && m_enmPolicy == other.m_enmPolicy)
+ || ( m_enmPolicy == MaximumGuestScreenSizePolicy_Fixed
+ && m_enmPolicy == other.m_enmPolicy
+ && m_size == other.m_size))
+ ;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMaximumGuestScreenSizeEditor implementation. *
+*********************************************************************************************************************************/
+
+UIMaximumGuestScreenSizeEditor::UIMaximumGuestScreenSizeEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLayout(0)
+ , m_pLabelPolicy(0)
+ , m_pComboPolicy(0)
+ , m_pLabelMaxWidth(0)
+ , m_pSpinboxMaxWidth(0)
+ , m_pLabelMaxHeight(0)
+ , m_pSpinboxMaxHeight(0)
+{
+ prepare();
+}
+
+void UIMaximumGuestScreenSizeEditor::setValue(const UIMaximumGuestScreenSizeValue &guiValue)
+{
+ /* Update cached value if value has changed: */
+ if (m_guiValue != guiValue)
+ m_guiValue = guiValue;
+
+ /* Look for proper policy index to choose: */
+ if (m_pComboPolicy)
+ {
+ const int iIndex = m_pComboPolicy->findData(QVariant::fromValue(m_guiValue.m_enmPolicy));
+ if (iIndex != -1)
+ {
+ m_pComboPolicy->setCurrentIndex(iIndex);
+ sltHandleCurrentPolicyIndexChanged();
+ }
+ }
+ /* Load size as well: */
+ if (m_pSpinboxMaxWidth && m_pSpinboxMaxHeight)
+ {
+ if (m_guiValue.m_enmPolicy == MaximumGuestScreenSizePolicy_Fixed)
+ {
+ m_pSpinboxMaxWidth->setValue(m_guiValue.m_size.width());
+ m_pSpinboxMaxHeight->setValue(m_guiValue.m_size.height());
+ }
+ }
+}
+
+UIMaximumGuestScreenSizeValue UIMaximumGuestScreenSizeEditor::value() const
+{
+ return m_pComboPolicy && m_pSpinboxMaxWidth && m_pSpinboxMaxHeight
+ ? UIMaximumGuestScreenSizeValue(m_pComboPolicy->currentData().value<MaximumGuestScreenSizePolicy>(),
+ QSize(m_pSpinboxMaxWidth->value(), m_pSpinboxMaxHeight->value()))
+ : m_guiValue;
+}
+
+int UIMaximumGuestScreenSizeEditor::minimumLabelHorizontalHint() const
+{
+ int iMinimumHint = 0;
+ iMinimumHint = qMax(iMinimumHint, m_pLabelPolicy->minimumSizeHint().width());
+ iMinimumHint = qMax(iMinimumHint, m_pLabelMaxWidth->minimumSizeHint().width());
+ iMinimumHint = qMax(iMinimumHint, m_pLabelMaxHeight->minimumSizeHint().width());
+ return iMinimumHint;
+}
+
+void UIMaximumGuestScreenSizeEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIMaximumGuestScreenSizeEditor::retranslateUi()
+{
+ if (m_pLabelPolicy)
+ m_pLabelPolicy->setText(tr("Maximum Guest Screen &Size:"));
+ if (m_pLabelMaxWidth)
+ m_pLabelMaxWidth->setText(tr("&Width:"));
+ if (m_pSpinboxMaxWidth)
+ m_pSpinboxMaxWidth->setToolTip(tr("Holds the maximum width which we would like the guest to use."));
+ if (m_pLabelMaxHeight)
+ m_pLabelMaxHeight->setText(tr("&Height:"));
+ if (m_pSpinboxMaxHeight)
+ m_pSpinboxMaxHeight->setToolTip(tr("Holds the maximum height which we would like the guest to use."));
+
+ if (m_pComboPolicy)
+ {
+ for (int i = 0; i < m_pComboPolicy->count(); ++i)
+ {
+ const MaximumGuestScreenSizePolicy enmType = m_pComboPolicy->itemData(i).value<MaximumGuestScreenSizePolicy>();
+ m_pComboPolicy->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pComboPolicy->setToolTip(tr("Selects maximum guest screen size policy."));
+ }
+}
+
+void UIMaximumGuestScreenSizeEditor::sltHandleCurrentPolicyIndexChanged()
+{
+ if (m_pComboPolicy)
+ {
+ /* Get current size-combo tool-tip data: */
+ const QString strCurrentComboItemTip = m_pComboPolicy->currentData(Qt::ToolTipRole).toString();
+ m_pComboPolicy->setWhatsThis(strCurrentComboItemTip);
+
+ /* Get current size-combo item data: */
+ const MaximumGuestScreenSizePolicy enmPolicy = m_pComboPolicy->currentData().value<MaximumGuestScreenSizePolicy>();
+ /* Should be combo-level widgets enabled? */
+ const bool fComboLevelWidgetsEnabled = enmPolicy == MaximumGuestScreenSizePolicy_Fixed;
+ /* Enable/disable combo-level widgets: */
+ if (m_pLabelMaxWidth)
+ m_pLabelMaxWidth->setEnabled(fComboLevelWidgetsEnabled);
+ if (m_pSpinboxMaxWidth)
+ m_pSpinboxMaxWidth->setEnabled(fComboLevelWidgetsEnabled);
+ if (m_pLabelMaxHeight)
+ m_pLabelMaxHeight->setEnabled(fComboLevelWidgetsEnabled);
+ if (m_pSpinboxMaxHeight)
+ m_pSpinboxMaxHeight->setEnabled(fComboLevelWidgetsEnabled);
+ }
+}
+
+void UIMaximumGuestScreenSizeEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ const int iMinWidth = 640;
+ const int iMinHeight = 480;
+ const int iMaxSize = 16 * _1K;
+
+ /* Prepare policy label: */
+ m_pLabelPolicy = new QLabel(this);
+ if (m_pLabelPolicy)
+ {
+ m_pLabelPolicy->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabelPolicy, 0, 0);
+ }
+ /* Prepare policy combo: */
+ m_pComboPolicy = new QComboBox(this);
+ if (m_pComboPolicy)
+ {
+ if (m_pLabelPolicy)
+ m_pLabelPolicy->setBuddy(m_pComboPolicy);
+ connect(m_pComboPolicy, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
+ this, &UIMaximumGuestScreenSizeEditor::sltHandleCurrentPolicyIndexChanged);
+
+ m_pLayout->addWidget(m_pComboPolicy, 0, 1);
+ }
+
+ /* Prepare max width label: */
+ m_pLabelMaxWidth = new QLabel(this);
+ if (m_pLabelMaxWidth)
+ {
+ m_pLabelMaxWidth->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabelMaxWidth, 1, 0);
+ }
+ /* Prepare max width spinbox: */
+ m_pSpinboxMaxWidth = new QSpinBox(this);
+ if (m_pSpinboxMaxWidth)
+ {
+ if (m_pLabelMaxWidth)
+ m_pLabelMaxWidth->setBuddy(m_pSpinboxMaxWidth);
+ m_pSpinboxMaxWidth->setMinimum(iMinWidth);
+ m_pSpinboxMaxWidth->setMaximum(iMaxSize);
+
+ m_pLayout->addWidget(m_pSpinboxMaxWidth, 1, 1);
+ }
+
+ /* Prepare max height label: */
+ m_pLabelMaxHeight = new QLabel(this);
+ if (m_pLabelMaxHeight)
+ {
+ m_pLabelMaxHeight->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabelMaxHeight, 2, 0);
+ }
+ /* Prepare max width spinbox: */
+ m_pSpinboxMaxHeight = new QSpinBox(this);
+ if (m_pSpinboxMaxHeight)
+ {
+ if (m_pLabelMaxHeight)
+ m_pLabelMaxHeight->setBuddy(m_pSpinboxMaxHeight);
+ m_pSpinboxMaxHeight->setMinimum(iMinHeight);
+ m_pSpinboxMaxHeight->setMaximum(iMaxSize);
+
+ m_pLayout->addWidget(m_pSpinboxMaxHeight, 2, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMaximumGuestScreenSizeEditor::populateCombo()
+{
+ if (m_pComboPolicy)
+ {
+ /* Clear combo first of all: */
+ m_pComboPolicy->clear();
+
+ /* Init currently supported maximum guest size policy types: */
+ const QList<MaximumGuestScreenSizePolicy> supportedValues = QList<MaximumGuestScreenSizePolicy>()
+ << MaximumGuestScreenSizePolicy_Automatic
+ << MaximumGuestScreenSizePolicy_Any
+ << MaximumGuestScreenSizePolicy_Fixed;
+
+ /* Update combo with all the supported values: */
+ foreach (const MaximumGuestScreenSizePolicy &enmType, supportedValues)
+ m_pComboPolicy->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMaximumGuestScreenSizeEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMaximumGuestScreenSizeEditor.h
new file mode 100644
index 00000000..3cc2b3a8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMaximumGuestScreenSizeEditor.h
@@ -0,0 +1,131 @@
+/* $Id: UIMaximumGuestScreenSizeEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIMaximumGuestScreenSizeEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIMaximumGuestScreenSizeEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIMaximumGuestScreenSizeEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QSpinBox;
+class QComboBox;
+
+
+/** Maximum guest screen size value. */
+struct SHARED_LIBRARY_STUFF UIMaximumGuestScreenSizeValue
+{
+ /** Constructs maximum guest screen size value. */
+ UIMaximumGuestScreenSizeValue(MaximumGuestScreenSizePolicy enmPolicy = MaximumGuestScreenSizePolicy_Any,
+ const QSize &resolution = QSize());
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIMaximumGuestScreenSizeValue &other) const;
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIMaximumGuestScreenSizeValue &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIMaximumGuestScreenSizeValue &other) const { return !equal(other); }
+
+ /** Holds the maximum guest-screen policy. */
+ MaximumGuestScreenSizePolicy m_enmPolicy;
+ /** Holds the maximum guest-screen size. */
+ QSize m_size;
+};
+Q_DECLARE_METATYPE(UIMaximumGuestScreenSizeValue);
+
+
+/** QWidget subclass used as a maximum guest screen size editor. */
+class SHARED_LIBRARY_STUFF UIMaximumGuestScreenSizeEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIMaximumGuestScreenSizeEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a guiValue. */
+ void setValue(const UIMaximumGuestScreenSizeValue &guiValue);
+ /** Returns editor value. */
+ UIMaximumGuestScreenSizeValue value() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles current policy index change. */
+ void sltHandleCurrentPolicyIndexChanged();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ UIMaximumGuestScreenSizeValue m_guiValue;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout: */
+ QGridLayout *m_pLayout;
+ /** Holds the policy label instance. */
+ QLabel *m_pLabelPolicy;
+ /** Holds the policy combo instance. */
+ QComboBox *m_pComboPolicy;
+ /** Holds the max width label instance. */
+ QLabel *m_pLabelMaxWidth;
+ /** Holds the max width spinbox instance. */
+ QSpinBox *m_pSpinboxMaxWidth;
+ /** Holds the max height label instance. */
+ QLabel *m_pLabelMaxHeight;
+ /** Holds the max height spinbox instance. */
+ QSpinBox *m_pSpinboxMaxHeight;
+ /** @} */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIMaximumGuestScreenSizeEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMiniToolbarSettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMiniToolbarSettingsEditor.cpp
new file mode 100644
index 00000000..0f1ad17a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMiniToolbarSettingsEditor.cpp
@@ -0,0 +1,151 @@
+/* $Id: UIMiniToolbarSettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMiniToolbarSettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UIMiniToolbarSettingsEditor.h"
+
+
+UIMiniToolbarSettingsEditor::UIMiniToolbarSettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fShowMiniToolbar(false)
+ , m_fMiniToolbarAtTop(false)
+ , m_pLabel(0)
+ , m_pCheckBoxShowMiniToolBar(0)
+ , m_pCheckBoxMiniToolBarAtTop(0)
+{
+ prepare();
+}
+
+void UIMiniToolbarSettingsEditor::setShowMiniToolbar(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fShowMiniToolbar != fOn)
+ {
+ m_fShowMiniToolbar = fOn;
+ if (m_pCheckBoxShowMiniToolBar)
+ m_pCheckBoxShowMiniToolBar->setCheckState(m_fShowMiniToolbar ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIMiniToolbarSettingsEditor::showMiniToolbar() const
+{
+ return m_pCheckBoxShowMiniToolBar
+ ? m_pCheckBoxShowMiniToolBar->checkState() == Qt::Checked
+ : m_fShowMiniToolbar;
+}
+
+void UIMiniToolbarSettingsEditor::setMiniToolbarAtTop(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fMiniToolbarAtTop != fOn)
+ {
+ m_fMiniToolbarAtTop = fOn;
+ if (m_pCheckBoxMiniToolBarAtTop)
+ m_pCheckBoxMiniToolBarAtTop->setCheckState(m_fMiniToolbarAtTop ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIMiniToolbarSettingsEditor::miniToolbarAtTop() const
+{
+ return m_pCheckBoxMiniToolBarAtTop
+ ? m_pCheckBoxMiniToolBarAtTop->checkState() == Qt::Checked
+ : m_fMiniToolbarAtTop;
+}
+
+int UIMiniToolbarSettingsEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIMiniToolbarSettingsEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIMiniToolbarSettingsEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Mini ToolBar:"));
+ if (m_pCheckBoxShowMiniToolBar)
+ {
+ m_pCheckBoxShowMiniToolBar->setText(tr("Show in &Full-screen/Seamless"));
+ m_pCheckBoxShowMiniToolBar->setToolTip(tr("When checked, show the Mini ToolBar in full-screen and seamless modes."));
+ }
+ if (m_pCheckBoxMiniToolBarAtTop)
+ {
+ m_pCheckBoxMiniToolBarAtTop->setText(tr("Show at &Top of Screen"));
+ m_pCheckBoxMiniToolBarAtTop->setToolTip(tr("When checked, show the Mini ToolBar at the top of the screen, rather than in "
+ "its default position at the bottom of the screen."));
+ }
+}
+
+void UIMiniToolbarSettingsEditor::prepare()
+{
+ /* Prepare main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Prepare label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+ /* Prepare 'enable output' check-box: */
+ m_pCheckBoxShowMiniToolBar = new QCheckBox(this);
+ if (m_pCheckBoxShowMiniToolBar)
+ m_pLayout->addWidget(m_pCheckBoxShowMiniToolBar, 0, 1);
+ /* Prepare 'enable input' check-box: */
+ m_pCheckBoxMiniToolBarAtTop = new QCheckBox(this);
+ if (m_pCheckBoxMiniToolBarAtTop)
+ m_pLayout->addWidget(m_pCheckBoxMiniToolBarAtTop, 1, 1);
+ }
+
+ /* Prepare connections and widget availability: */
+ if ( m_pCheckBoxShowMiniToolBar
+ && m_pCheckBoxMiniToolBarAtTop)
+ {
+ connect(m_pCheckBoxShowMiniToolBar, &QCheckBox::toggled,
+ m_pCheckBoxMiniToolBarAtTop, &QCheckBox::setEnabled);
+ m_pCheckBoxMiniToolBarAtTop->setEnabled(m_pCheckBoxShowMiniToolBar->isChecked());
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMiniToolbarSettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMiniToolbarSettingsEditor.h
new file mode 100644
index 00000000..201e0759
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMiniToolbarSettingsEditor.h
@@ -0,0 +1,98 @@
+/* $Id: UIMiniToolbarSettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIMiniToolbarSettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIMiniToolbarSettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIMiniToolbarSettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as mini-toolbar editor. */
+class SHARED_LIBRARY_STUFF UIMiniToolbarSettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIMiniToolbarSettingsEditor(QWidget *pParent = 0);
+
+ /** Defines whether 'show mini-toolbar' feature in @a fOn. */
+ void setShowMiniToolbar(bool fOn);
+ /** Returns 'show mini-toolbar' feature value. */
+ bool showMiniToolbar() const;
+
+ /** Defines whether 'mini-toolbar at top' feature in @a fOn. */
+ void setMiniToolbarAtTop(bool fOn);
+ /** Returns 'mini-toolbar at top' feature value. */
+ bool miniToolbarAtTop() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Values
+ * @{ */
+ /** Holds the 'show mini-toolbar' feature value. */
+ bool m_fShowMiniToolbar;
+ /** Holds the 'mini-toolbar at top' feature value. */
+ bool m_fMiniToolbarAtTop;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the 'show mini-toolbar' check-box instance. */
+ QCheckBox *m_pCheckBoxShowMiniToolBar;
+ /** Holds the 'mini-toolbar alignment' check-box instance. */
+ QCheckBox *m_pCheckBoxMiniToolBarAtTop;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIMiniToolbarSettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMonitorCountEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMonitorCountEditor.cpp
new file mode 100644
index 00000000..aaa53209
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMonitorCountEditor.cpp
@@ -0,0 +1,208 @@
+/* $Id: UIMonitorCountEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMonitorCountEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QSpinBox>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIMonitorCountEditor.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CSystemProperties.h"
+
+
+UIMonitorCountEditor::UIMonitorCountEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_iValue(1)
+ , m_pLayout(0)
+ , m_pLabel(0)
+ , m_pSlider(0)
+ , m_pSpinBox(0)
+ , m_pLabelMin(0)
+ , m_pLabelMax(0)
+{
+ prepare();
+}
+
+void UIMonitorCountEditor::setValue(int iValue)
+{
+ if (m_iValue != iValue)
+ {
+ m_iValue = iValue;
+ if (m_pSlider)
+ m_pSlider->setValue(m_iValue);
+ if (m_pSpinBox)
+ m_pSpinBox->setValue(m_iValue);
+ }
+}
+
+int UIMonitorCountEditor::value() const
+{
+ return m_pSpinBox ? m_pSpinBox->value() : m_iValue;
+}
+
+int UIMonitorCountEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIMonitorCountEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIMonitorCountEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Mo&nitor Count:"));
+
+ if (m_pSlider)
+ m_pSlider->setToolTip(tr("Holds the amount of virtual monitors provided to the virtual machine."));
+ if (m_pSpinBox)
+ m_pSpinBox->setToolTip(tr("Holds the amount of virtual monitors provided to the virtual machine."));
+
+ if (m_pLabelMin)
+ m_pLabelMin->setToolTip(tr("Minimum possible monitor count."));
+ if (m_pLabelMax)
+ m_pLabelMax->setToolTip(tr("Maximum possible monitor count."));
+}
+
+void UIMonitorCountEditor::sltHandleSliderChange()
+{
+ /* Apply spin-box value keeping signals disabled: */
+ if (m_pSpinBox && m_pSlider)
+ {
+ m_pSpinBox->blockSignals(true);
+ m_pSpinBox->setValue(m_pSlider->value());
+ m_pSpinBox->blockSignals(false);
+ }
+
+ /* Notify listeners about value changed: */
+ emit sigValidChanged();
+}
+
+void UIMonitorCountEditor::sltHandleSpinBoxChange()
+{
+ /* Apply slider value keeping signals disabled: */
+ if (m_pSlider && m_pSpinBox)
+ {
+ m_pSlider->blockSignals(true);
+ m_pSlider->setValue(m_pSpinBox->value());
+ m_pSlider->blockSignals(false);
+ }
+
+ /* Notify listeners about value changed: */
+ emit sigValidChanged();
+}
+
+void UIMonitorCountEditor::prepare()
+{
+ /* Prepare common variables: */
+ const CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+
+ /* Prepare main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(2, 1); // spacer between min&max labels
+
+ /* Prepare main label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Prepare slider: */
+ m_pSlider = new QIAdvancedSlider(this);
+ if (m_pSlider)
+ {
+ const uint cHostScreens = UIDesktopWidgetWatchdog::screenCount();
+ const uint cMinGuestScreens = 1;
+ const uint cMaxGuestScreens = comProperties.GetMaxGuestMonitors();
+ const uint cMaxGuestScreensForSlider = qMin(cMaxGuestScreens, (uint)8);
+ m_pSlider->setOrientation(Qt::Horizontal);
+ m_pSlider->setMinimum(cMinGuestScreens);
+ m_pSlider->setMaximum(cMaxGuestScreensForSlider);
+ m_pSlider->setPageStep(1);
+ m_pSlider->setSingleStep(1);
+ m_pSlider->setTickInterval(1);
+ m_pSlider->setOptimalHint(cMinGuestScreens, cHostScreens);
+ m_pSlider->setWarningHint(cHostScreens, cMaxGuestScreensForSlider);
+
+ m_pLayout->addWidget(m_pSlider, 0, 1, 1, 3);
+ }
+
+ /* Prepare spin-box: */
+ m_pSpinBox = new QSpinBox(this);
+ if (m_pSpinBox)
+ {
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pSpinBox);
+ m_pSpinBox->setMinimum(1);
+ m_pSpinBox->setMaximum(comProperties.GetMaxGuestMonitors());
+
+ m_pLayout->addWidget(m_pSpinBox, 0, 4);
+ }
+
+ /* Prepare min label: */
+ m_pLabelMin = new QLabel(this);
+ if (m_pLabelMin)
+ {
+ m_pLabelMin->setText(QString::number(1));
+ m_pLayout->addWidget(m_pLabelMin, 1, 1);
+ }
+
+ /* Prepare max label: */
+ m_pLabelMax = new QLabel(this);
+ if (m_pLabelMax)
+ {
+ m_pLabelMax->setText(QString::number(qMin(comProperties.GetMaxGuestMonitors(), (ULONG)8)));
+ m_pLayout->addWidget(m_pLabelMax, 1, 3);
+ }
+ }
+
+ /* Prepare connections: */
+ if (m_pSlider)
+ connect(m_pSlider, &QIAdvancedSlider::valueChanged,
+ this, &UIMonitorCountEditor::sltHandleSliderChange);
+ if (m_pSpinBox)
+ connect(m_pSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIMonitorCountEditor::sltHandleSpinBoxChange);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMonitorCountEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMonitorCountEditor.h
new file mode 100644
index 00000000..28815f4e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMonitorCountEditor.h
@@ -0,0 +1,112 @@
+/* $Id: UIMonitorCountEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIMonitorCountEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIMonitorCountEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIMonitorCountEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QSpinBox;
+class QIAdvancedSlider;
+
+/** QWidget subclass used as a monitor count editor. */
+class SHARED_LIBRARY_STUFF UIMonitorCountEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value changed. */
+ void sigValidChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIMonitorCountEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a iValue. */
+ void setValue(int iValue);
+ /** Returns editor value. */
+ int value() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles slider value changes. */
+ void sltHandleSliderChange();
+ /** Handles spin-box value changes. */
+ void sltHandleSpinBoxChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Revalidates and emits validity change signal. */
+ void revalidate();
+
+ /** Holds the value to be selected. */
+ int m_iValue;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the main label instance. */
+ QLabel *m_pLabel;
+ /** Holds the slider instance. */
+ QIAdvancedSlider *m_pSlider;
+ /** Holds the spin-box instance. */
+ QSpinBox *m_pSpinBox;
+ /** Holds minimum label instance. */
+ QLabel *m_pLabelMin;
+ /** Holds maximum label instance. */
+ QLabel *m_pLabelMax;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIMonitorCountEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMotherboardFeaturesEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMotherboardFeaturesEditor.cpp
new file mode 100644
index 00000000..272cd2fa
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMotherboardFeaturesEditor.cpp
@@ -0,0 +1,298 @@
+/* $Id: UIMotherboardFeaturesEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMotherboardFeaturesEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QPushButton>
+
+/* GUI includes: */
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIMotherboardFeaturesEditor.h"
+
+
+UIMotherboardFeaturesEditor::UIMotherboardFeaturesEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fEnableIoApic(false)
+ , m_fEnableUtcTime(false)
+ , m_fEnableEfi(false)
+ , m_fEnableSecureBoot(false)
+ , m_pLabel(0)
+ , m_pCheckBoxEnableIoApic(0)
+ , m_pCheckBoxEnableUtcTime(0)
+ , m_pCheckBoxEnableEfi(0)
+ , m_pCheckBoxEnableSecureBoot(0)
+ , m_pPushButtonResetSecureBoot(0)
+{
+ prepare();
+}
+
+void UIMotherboardFeaturesEditor::setEnableIoApic(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnableIoApic != fOn)
+ {
+ m_fEnableIoApic = fOn;
+ if (m_pCheckBoxEnableIoApic)
+ m_pCheckBoxEnableIoApic->setCheckState(m_fEnableIoApic ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIMotherboardFeaturesEditor::isEnabledIoApic() const
+{
+ return m_pCheckBoxEnableIoApic
+ ? m_pCheckBoxEnableIoApic->checkState() == Qt::Checked
+ : m_fEnableIoApic;
+}
+
+void UIMotherboardFeaturesEditor::setEnableUtcTime(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnableUtcTime != fOn)
+ {
+ m_fEnableUtcTime = fOn;
+ if (m_pCheckBoxEnableUtcTime)
+ m_pCheckBoxEnableUtcTime->setCheckState(m_fEnableUtcTime ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIMotherboardFeaturesEditor::isEnabledUtcTime() const
+{
+ return m_pCheckBoxEnableUtcTime
+ ? m_pCheckBoxEnableUtcTime->checkState() == Qt::Checked
+ : m_fEnableUtcTime;
+}
+
+void UIMotherboardFeaturesEditor::setEnableEfi(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnableEfi != fOn)
+ {
+ m_fEnableEfi = fOn;
+ if (m_pCheckBoxEnableEfi)
+ m_pCheckBoxEnableEfi->setCheckState(m_fEnableEfi ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIMotherboardFeaturesEditor::isEnabledEfi() const
+{
+ return m_pCheckBoxEnableEfi
+ ? m_pCheckBoxEnableEfi->checkState() == Qt::Checked
+ : m_fEnableEfi;
+}
+
+void UIMotherboardFeaturesEditor::setEnableSecureBoot(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnableSecureBoot != fOn)
+ {
+ m_fEnableSecureBoot = fOn;
+ if (m_pCheckBoxEnableSecureBoot)
+ m_pCheckBoxEnableSecureBoot->setCheckState(m_fEnableSecureBoot ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIMotherboardFeaturesEditor::isEnabledSecureBoot() const
+{
+ return m_pCheckBoxEnableSecureBoot
+ ? m_pCheckBoxEnableSecureBoot->checkState() == Qt::Checked
+ : m_fEnableSecureBoot;
+}
+
+bool UIMotherboardFeaturesEditor::isResetSecureBoot() const
+{
+ return m_pPushButtonResetSecureBoot
+ ? m_pPushButtonResetSecureBoot->property("clicked_once").toBool()
+ : false;
+}
+
+int UIMotherboardFeaturesEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIMotherboardFeaturesEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIMotherboardFeaturesEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Extended Features:"));
+ if (m_pCheckBoxEnableIoApic)
+ {
+ m_pCheckBoxEnableIoApic->setText(tr("Enable &I/O APIC"));
+ m_pCheckBoxEnableIoApic->setToolTip(tr("When checked, the virtual machine will support the Input Output APIC (I/O APIC), "
+ "which may slightly decrease performance. Note: don't disable this feature "
+ "after having installed a Windows guest operating system!"));
+ }
+ if (m_pCheckBoxEnableUtcTime)
+ {
+ m_pCheckBoxEnableUtcTime->setText(tr("Enable Hardware Clock in &UTC Time"));
+ m_pCheckBoxEnableUtcTime->setToolTip(tr("When checked, the RTC device will report the time in UTC, otherwise in local "
+ "(host) time. Unix usually expects the hardware clock to be set to UTC."));
+ }
+ if (m_pCheckBoxEnableEfi)
+ {
+ m_pCheckBoxEnableEfi->setText(tr("Enable &EFI (special OSes only)"));
+ m_pCheckBoxEnableEfi->setToolTip(tr("When checked, the guest will support the Extended Firmware Interface (EFI), "
+ "which is required to boot certain guest OSes. Non-EFI aware OSes will not be able "
+ "to boot if this option is activated."));
+ }
+ if (m_pCheckBoxEnableSecureBoot)
+ {
+ m_pCheckBoxEnableSecureBoot->setText(tr("Enable &Secure Boot"));
+ m_pCheckBoxEnableSecureBoot->setToolTip(tr("When checked, the secure boot emulation will be enabled."));
+ }
+ if (m_pPushButtonResetSecureBoot)
+ {
+ m_pPushButtonResetSecureBoot->setText(tr("&Reset Keys to Default"));
+ m_pPushButtonResetSecureBoot->setToolTip(tr("Resets secure boot keys to default."));
+ }
+}
+
+void UIMotherboardFeaturesEditor::sltHandleEnableEfiToggling()
+{
+ /* Acquire actual feature state: */
+ const bool fOn = m_pCheckBoxEnableEfi
+ ? m_pCheckBoxEnableEfi->isChecked()
+ : false;
+
+ /* Update corresponding controls: */
+ if (m_pCheckBoxEnableSecureBoot)
+ m_pCheckBoxEnableSecureBoot->setEnabled(fOn);
+
+ /* Notify listeners: */
+ emit sigChangedEfi();
+ sltHandleEnableSecureBootToggling();
+}
+
+void UIMotherboardFeaturesEditor::sltHandleEnableSecureBootToggling()
+{
+ /* Acquire actual feature state: */
+ const bool fOn = m_pCheckBoxEnableEfi
+ && m_pCheckBoxEnableSecureBoot
+ && m_pPushButtonResetSecureBoot
+ ? m_pCheckBoxEnableEfi->isChecked()
+ && m_pCheckBoxEnableSecureBoot->isChecked()
+ && !m_pPushButtonResetSecureBoot->property("clicked_once").toBool()
+ : false;
+
+ /* Update corresponding controls: */
+ if (m_pPushButtonResetSecureBoot)
+ m_pPushButtonResetSecureBoot->setEnabled(fOn);
+
+ /* Notify listeners: */
+ emit sigChangedSecureBoot();
+}
+
+void UIMotherboardFeaturesEditor::sltResetSecureBoot()
+{
+ if (!m_pPushButtonResetSecureBoot->property("clicked_once").toBool())
+ {
+ if (msgCenter().confirmRestoringDefaultKeys())
+ {
+ m_pPushButtonResetSecureBoot->setProperty("clicked_once", true);
+ sltHandleEnableSecureBootToggling();
+ }
+ }
+}
+
+void UIMotherboardFeaturesEditor::prepare()
+{
+ /* Prepare main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Prepare label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+ /* Prepare 'enable IO APIC' check-box: */
+ m_pCheckBoxEnableIoApic = new QCheckBox(this);
+ if (m_pCheckBoxEnableIoApic)
+ {
+ connect(m_pCheckBoxEnableIoApic, &QCheckBox::stateChanged,
+ this, &UIMotherboardFeaturesEditor::sigChangedIoApic);
+ m_pLayout->addWidget(m_pCheckBoxEnableIoApic, 0, 1);
+ }
+ /* Prepare 'enable UTC time' check-box: */
+ m_pCheckBoxEnableUtcTime = new QCheckBox(this);
+ if (m_pCheckBoxEnableUtcTime)
+ {
+ connect(m_pCheckBoxEnableUtcTime, &QCheckBox::stateChanged,
+ this, &UIMotherboardFeaturesEditor::sigChangedUtcTime);
+ m_pLayout->addWidget(m_pCheckBoxEnableUtcTime, 1, 1);
+ }
+ /* Prepare 'enable EFI' check-box: */
+ m_pCheckBoxEnableEfi = new QCheckBox(this);
+ if (m_pCheckBoxEnableEfi)
+ {
+ connect(m_pCheckBoxEnableEfi, &QCheckBox::stateChanged,
+ this, &UIMotherboardFeaturesEditor::sltHandleEnableEfiToggling);
+ m_pLayout->addWidget(m_pCheckBoxEnableEfi, 2, 1);
+ }
+ /* Prepare 'enable secure boot' check-box: */
+ m_pCheckBoxEnableSecureBoot = new QCheckBox(this);
+ if (m_pCheckBoxEnableSecureBoot)
+ {
+ connect(m_pCheckBoxEnableSecureBoot, &QCheckBox::stateChanged,
+ this, &UIMotherboardFeaturesEditor::sltHandleEnableSecureBootToggling);
+ m_pLayout->addWidget(m_pCheckBoxEnableSecureBoot, 3, 1);
+ }
+ /* Prepare 'reset secure boot' tool-button: */
+ m_pPushButtonResetSecureBoot = new QPushButton(this);
+ if (m_pPushButtonResetSecureBoot)
+ {
+ m_pPushButtonResetSecureBoot->setIcon(UIIconPool::iconSet(":/refresh_16px.png"));
+ connect(m_pPushButtonResetSecureBoot, &QPushButton::clicked,
+ this, &UIMotherboardFeaturesEditor::sltResetSecureBoot);
+ m_pLayout->addWidget(m_pPushButtonResetSecureBoot, 4, 1);
+ }
+ }
+
+ /* Fetch states: */
+ sltHandleEnableEfiToggling();
+ sltHandleEnableSecureBootToggling();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMotherboardFeaturesEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMotherboardFeaturesEditor.h
new file mode 100644
index 00000000..96819e25
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIMotherboardFeaturesEditor.h
@@ -0,0 +1,143 @@
+/* $Id: UIMotherboardFeaturesEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIMotherboardFeaturesEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIMotherboardFeaturesEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIMotherboardFeaturesEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+class QPushButton;
+
+/** QWidget subclass used as motherboard features editor. */
+class SHARED_LIBRARY_STUFF UIMotherboardFeaturesEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about IO APIC change. */
+ void sigChangedIoApic();
+ /** Notifies listeners about UTC time change. */
+ void sigChangedUtcTime();
+ /** Notifies listeners about EFI change. */
+ void sigChangedEfi();
+ /** Notifies listeners about secure boot change. */
+ void sigChangedSecureBoot();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIMotherboardFeaturesEditor(QWidget *pParent = 0);
+
+ /** Defines whether 'enable IO APIC' feature in @a fOn. */
+ void setEnableIoApic(bool fOn);
+ /** Returns 'enable IO APIC' feature value. */
+ bool isEnabledIoApic() const;
+
+ /** Defines whether 'enable UTC time' feature in @a fOn. */
+ void setEnableUtcTime(bool fOn);
+ /** Returns 'enable UTC time' feature value. */
+ bool isEnabledUtcTime() const;
+
+ /** Defines whether 'enable EFI' feature in @a fOn. */
+ void setEnableEfi(bool fOn);
+ /** Returns 'enable EFI' feature value. */
+ bool isEnabledEfi() const;
+
+ /** Defines whether 'enable secure boot' feature in @a fOn. */
+ void setEnableSecureBoot(bool fOn);
+ /** Returns 'enable secure boot' feature value. */
+ bool isEnabledSecureBoot() const;
+
+ /** Returns 'reset to default secure boot keys' feature value. */
+ bool isResetSecureBoot() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles 'enable EFI' feature being toggled. */
+ void sltHandleEnableEfiToggling();
+ /** Handles 'enable secure boot' feature being toggled. */
+ void sltHandleEnableSecureBootToggling();
+
+ /** Marks corresponding push button activated. */
+ void sltResetSecureBoot();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Values
+ * @{ */
+ /** Holds the 'enable IO APIC' feature value. */
+ bool m_fEnableIoApic;
+ /** Holds the 'enable UTC time' feature value. */
+ bool m_fEnableUtcTime;
+ /** Holds the 'enable EFI' feature value. */
+ bool m_fEnableEfi;
+ /** Holds the 'enable secure boot' feature value. */
+ bool m_fEnableSecureBoot;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the 'enable IO APIC' check-box instance. */
+ QCheckBox *m_pCheckBoxEnableIoApic;
+ /** Holds the 'enable UTC time' check-box instance. */
+ QCheckBox *m_pCheckBoxEnableUtcTime;
+ /** Holds the 'enable EFI' check-box instance. */
+ QCheckBox *m_pCheckBoxEnableEfi;
+ /** Holds the 'secure boot' check-box instance. */
+ QCheckBox *m_pCheckBoxEnableSecureBoot;
+ /** Holds the 'secure boot' tool-button instance. */
+ QPushButton *m_pPushButtonResetSecureBoot;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIMotherboardFeaturesEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UINameAndSystemEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINameAndSystemEditor.cpp
new file mode 100644
index 00000000..602ab48a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINameAndSystemEditor.cpp
@@ -0,0 +1,700 @@
+/* $Id: UINameAndSystemEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UINameAndSystemEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "UICommon.h"
+#include "UIIconPool.h"
+#include "UIFilePathSelector.h"
+#include "UINameAndSystemEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+/** Defines the VM OS type ID. */
+enum
+{
+ TypeID = Qt::UserRole + 1
+};
+
+
+UINameAndSystemEditor::UINameAndSystemEditor(QWidget *pParent,
+ bool fChooseName /* = true */,
+ bool fChoosePath /* = false */,
+ bool fChooseImage /* = false */,
+ bool fChooseEdition /* = false */,
+ bool fChooseType /* = true */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fChooseName(fChooseName)
+ , m_fChoosePath(fChoosePath)
+ , m_fChooseImage(fChooseImage)
+ , m_fChooseEdition(fChooseEdition)
+ , m_fChooseType(fChooseType)
+ , m_pLayout(0)
+ , m_pLabelName(0)
+ , m_pLabelPath(0)
+ , m_pLabelImage(0)
+ , m_pLabelEdition(0)
+ , m_pLabelFamily(0)
+ , m_pLabelType(0)
+ , m_pIconType(0)
+ , m_pEditorName(0)
+ , m_pSelectorPath(0)
+ , m_pSelectorImage(0)
+ , m_pComboEdition(0)
+ , m_pComboFamily(0)
+ , m_pComboType(0)
+{
+ prepare();
+}
+
+void UINameAndSystemEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UINameAndSystemEditor::setNameStuffEnabled(bool fEnabled)
+{
+ if (m_pLabelName)
+ m_pLabelName->setEnabled(fEnabled);
+ if (m_pEditorName)
+ m_pEditorName->setEnabled(fEnabled);
+}
+
+void UINameAndSystemEditor::setPathStuffEnabled(bool fEnabled)
+{
+ if (m_pLabelPath)
+ m_pLabelPath->setEnabled(fEnabled);
+ if (m_pSelectorPath)
+ m_pSelectorPath->setEnabled(fEnabled);
+}
+
+void UINameAndSystemEditor::setOSTypeStuffEnabled(bool fEnabled)
+{
+ if (m_pLabelFamily)
+ m_pLabelFamily->setEnabled(fEnabled);
+ if (m_pLabelType)
+ m_pLabelType->setEnabled(fEnabled);
+ if (m_pIconType)
+ m_pIconType->setEnabled(fEnabled);
+ if (m_pComboFamily)
+ m_pComboFamily->setEnabled(fEnabled);
+ if (m_pComboType)
+ m_pComboType->setEnabled(fEnabled);
+}
+
+void UINameAndSystemEditor::setName(const QString &strName)
+{
+ if (!m_pEditorName)
+ return;
+ m_pEditorName->setText(strName);
+}
+
+QString UINameAndSystemEditor::name() const
+{
+ if (!m_pEditorName)
+ return QString();
+ return m_pEditorName->text();
+}
+
+void UINameAndSystemEditor::setPath(const QString &strPath)
+{
+ if (!m_pSelectorPath)
+ return;
+ m_pSelectorPath->setPath(strPath);
+}
+
+QString UINameAndSystemEditor::path() const
+{
+ if (!m_pSelectorPath)
+ return uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder();
+ return m_pSelectorPath->path();
+}
+
+void UINameAndSystemEditor::setISOImagePath(const QString &strPath)
+{
+ if (m_pSelectorImage)
+ m_pSelectorImage->setPath(strPath);
+ emit sigImageChanged(strPath);
+}
+QString UINameAndSystemEditor::ISOImagePath() const
+{
+ if (!m_pSelectorImage)
+ return QString();
+ return m_pSelectorImage->path();
+}
+
+void UINameAndSystemEditor::setTypeId(QString strTypeId, QString strFamilyId /* = QString() */)
+{
+ if (!m_pComboType)
+ return;
+ AssertMsgReturnVoid(!strTypeId.isNull(), ("Null guest OS type ID"));
+
+ /* Initialize indexes: */
+ int iTypeIndex = -1;
+ int iFamilyIndex = -1;
+
+ /* If family ID isn't empty: */
+ if (!strFamilyId.isEmpty())
+ {
+ /* Search for corresponding family ID index: */
+ iFamilyIndex = m_pComboFamily->findData(strFamilyId, TypeID);
+
+ /* If that family ID isn't present, we have to add it: */
+ if (iFamilyIndex == -1)
+ {
+ /* Append family ID to corresponding combo: */
+ m_pComboFamily->addItem(strFamilyId);
+ m_pComboFamily->setItemData(m_pComboFamily->count() - 1, strFamilyId, TypeID);
+ /* Append family ID to type cache: */
+ m_types[strFamilyId] = QList<UIGuestOSType>();
+
+ /* Search for corresponding family ID index finally: */
+ iFamilyIndex = m_pComboFamily->findData(strFamilyId, TypeID);
+ }
+ }
+ /* If family ID is empty: */
+ else
+ {
+ /* We'll try to find it by type ID: */
+ foreach (const QString &strKnownFamilyId, m_types.keys())
+ {
+ foreach (const UIGuestOSType &guiType, m_types.value(strKnownFamilyId))
+ {
+ if (guiType.typeId == strTypeId)
+ strFamilyId = strKnownFamilyId;
+ if (!strFamilyId.isNull())
+ break;
+ }
+ if (!strFamilyId.isNull())
+ break;
+ }
+
+ /* If we were unable to find it => use "Other": */
+ if (strFamilyId.isNull())
+ strFamilyId = "Other";
+
+ /* Search for corresponding family ID index finally: */
+ iFamilyIndex = m_pComboFamily->findData(strFamilyId, TypeID);
+ }
+
+ /* To that moment family ID index should be always found: */
+ AssertReturnVoid(iFamilyIndex != -1);
+ /* So we choose it: */
+ m_pComboFamily->setCurrentIndex(iFamilyIndex);
+ sltFamilyChanged(m_pComboFamily->currentIndex());
+
+ /* Search for corresponding type ID index: */
+ iTypeIndex = m_pComboType->findData(strTypeId, TypeID);
+
+ /* If that type ID isn't present, we have to add it: */
+ if (iTypeIndex == -1)
+ {
+ /* Append type ID to type cache: */
+ UIGuestOSType guiType;
+ guiType.typeId = strTypeId;
+ guiType.typeDescription = strTypeId;
+ guiType.is64bit = false;
+ m_types[strFamilyId] << guiType;
+
+ /* So we re-choose family again: */
+ m_pComboFamily->setCurrentIndex(iFamilyIndex);
+ sltFamilyChanged(m_pComboFamily->currentIndex());
+
+ /* Search for corresponding type ID index finally: */
+ iTypeIndex = m_pComboType->findData(strTypeId, TypeID);
+ }
+
+ /* To that moment type ID index should be always found: */
+ AssertReturnVoid(iTypeIndex != -1);
+ /* So we choose it: */
+ m_pComboType->setCurrentIndex(iTypeIndex);
+ sltTypeChanged(m_pComboType->currentIndex());
+}
+
+QString UINameAndSystemEditor::typeId() const
+{
+ if (!m_pComboType)
+ return QString();
+ return m_strTypeId;
+}
+
+QString UINameAndSystemEditor::familyId() const
+{
+ if (!m_pComboFamily)
+ return QString();
+ return m_strFamilyId;
+}
+
+void UINameAndSystemEditor::setType(const CGuestOSType &enmType)
+{
+ // WORKAROUND:
+ // We're getting here with a NULL enmType when creating new VMs.
+ // Very annoying, so just workarounded for now.
+ /** @todo find out the reason and way to fix that.. */
+ if (enmType.isNull())
+ return;
+
+ /* Pass to function above: */
+ setTypeId(enmType.GetId(), enmType.GetFamilyId());
+}
+
+CGuestOSType UINameAndSystemEditor::type() const
+{
+ return uiCommon().vmGuestOSType(typeId(), familyId());
+}
+
+void UINameAndSystemEditor::markNameEditor(bool fError)
+{
+ if (m_pEditorName)
+ m_pEditorName->mark(fError, fError ? tr("Invalid name") : QString("Name is valid"));
+}
+
+void UINameAndSystemEditor::markImageEditor(bool fError, const QString &strErrorMessage)
+{
+ if (m_pSelectorImage)
+ m_pSelectorImage->mark(fError, strErrorMessage);
+}
+
+void UINameAndSystemEditor::setEditionNameAndIndices(const QVector<QString> &names, const QVector<ulong> &ids)
+{
+ AssertReturnVoid(m_pComboEdition && names.size() == ids.size());
+ m_pComboEdition->clear();
+ for (int i = 0; i < names.size(); ++i)
+ m_pComboEdition->addItem(names[i], QVariant::fromValue(ids[i]) /* user data */);
+}
+
+void UINameAndSystemEditor::setEditionSelectorEnabled(bool fEnabled)
+{
+ if (m_pComboEdition)
+ m_pComboEdition->setEnabled(fEnabled);
+ if (m_pLabelEdition)
+ m_pLabelEdition->setEnabled(fEnabled);
+}
+
+bool UINameAndSystemEditor::isEditionsSelectorEmpty() const
+{
+ if (m_pComboEdition)
+ return m_pComboEdition->count() == 0;
+ return true;
+}
+
+int UINameAndSystemEditor::firstColumnWidth() const
+{
+ int iWidth = 0;
+ if (m_pLabelName)
+ iWidth = qMax(iWidth, m_pLabelName->width());
+ if (m_pLabelPath)
+ iWidth = qMax(iWidth, m_pLabelPath->width());
+ if (m_pLabelImage)
+ iWidth = qMax(iWidth, m_pLabelImage->width());
+ if (m_pLabelEdition)
+ iWidth = qMax(iWidth, m_pLabelEdition->width());
+ if (m_pLabelFamily)
+ iWidth = qMax(iWidth, m_pLabelFamily->width());
+ if (m_pLabelType)
+ iWidth = qMax(iWidth, m_pLabelType->width());
+ return iWidth;
+}
+
+void UINameAndSystemEditor::retranslateUi()
+{
+ if (m_pLabelName)
+ m_pLabelName->setText(tr("&Name:"));
+ if (m_pLabelPath)
+ m_pLabelPath->setText(tr("&Folder:"));
+ if (m_pLabelImage)
+ m_pLabelImage->setText(tr("&ISO Image:"));
+ if (m_pLabelEdition)
+ m_pLabelEdition->setText(tr("&Edition:"));
+ if (m_pLabelFamily)
+ m_pLabelFamily->setText(tr("&Type:"));
+ if (m_pLabelType)
+ m_pLabelType->setText(tr("&Version:"));
+
+ if (m_pEditorName)
+ m_pEditorName->setToolTip(tr("Holds the name for virtual machine."));
+ if (m_pSelectorPath)
+ m_pSelectorPath->setToolTip(tr("Selects the folder hosting virtual machine."));
+ if (m_pComboFamily)
+ m_pComboFamily->setToolTip(tr("Selects the operating system family that "
+ "you plan to install into this virtual machine."));
+ if (m_pComboType)
+ m_pComboType->setToolTip(tr("Selects the operating system type that "
+ "you plan to install into this virtual machine "
+ "(called a guest operating system)."));
+ if (m_pSelectorImage)
+ m_pSelectorImage->setToolTip(tr("Selects an ISO image to be attached to the "
+ "virtual machine or used in unattended install."));
+}
+
+void UINameAndSystemEditor::sltFamilyChanged(int iIndex)
+{
+ AssertPtrReturnVoid(m_pComboFamily);
+
+ /* Lock the signals of m_pComboType to prevent it's reaction on clearing: */
+ m_pComboType->blockSignals(true);
+ m_pComboType->clear();
+
+ /* Acquire family ID: */
+ m_strFamilyId = m_pComboFamily->itemData(iIndex, TypeID).toString();
+
+ /* Populate combo-box with OS types related to currently selected family id: */
+ foreach (const UIGuestOSType &guiType, m_types.value(m_strFamilyId))
+ {
+ const int idxItem = m_pComboType->count();
+ m_pComboType->insertItem(idxItem, guiType.typeDescription);
+ m_pComboType->setItemData(idxItem, guiType.typeId, TypeID);
+ }
+
+ /* Select the most recently chosen item: */
+ if (m_currentIds.contains(m_strFamilyId))
+ {
+ const QString strTypeId = m_currentIds.value(m_strFamilyId);
+ const int iTypeIndex = m_pComboType->findData(strTypeId, TypeID);
+ if (iTypeIndex != -1)
+ m_pComboType->setCurrentIndex(iTypeIndex);
+ }
+ /* Or select Windows 10 item for Windows family as default: */
+ else if (m_strFamilyId == "Windows")
+ {
+ QString strDefaultID = "Windows10";
+ if (ARCH_BITS == 64)
+ strDefaultID += "_64";
+ const int iIndexWin10 = m_pComboType->findData(strDefaultID, TypeID);
+ if (iIndexWin10 != -1)
+ m_pComboType->setCurrentIndex(iIndexWin10);
+ }
+ /* Or select Oracle Linux item for Linux family as default: */
+ else if (m_strFamilyId == "Linux")
+ {
+ QString strDefaultID = "Oracle";
+ if (ARCH_BITS == 64)
+ strDefaultID += "_64";
+ const int iIndexUbuntu = m_pComboType->findData(strDefaultID, TypeID);
+ if (iIndexUbuntu != -1)
+ m_pComboType->setCurrentIndex(iIndexUbuntu);
+ }
+ /* Else simply select the first one present: */
+ else
+ m_pComboType->setCurrentIndex(0);
+
+ /* Update all the stuff: */
+ sltTypeChanged(m_pComboType->currentIndex());
+
+ /* Unlock the signals of m_pComboType: */
+ m_pComboType->blockSignals(false);
+
+ /* Notify listeners about this change: */
+ emit sigOSFamilyChanged(m_strFamilyId);
+}
+
+void UINameAndSystemEditor::sltTypeChanged(int iIndex)
+{
+ AssertPtrReturnVoid(m_pComboType);
+
+ /* Acquire type ID: */
+ m_strTypeId = m_pComboType->itemData(iIndex, TypeID).toString();
+
+ /* Update selected type pixmap: */
+ m_pIconType->setPixmap(generalIconPool().guestOSTypePixmapDefault(m_strTypeId));
+
+ /* Save the most recently used item: */
+ m_currentIds[m_strFamilyId] = m_strTypeId;
+
+ /* Notifies listeners about OS type change: */
+ emit sigOsTypeChanged();
+}
+
+void UINameAndSystemEditor::sltSelectedEditionsChanged(int)
+{
+ emit sigEditionChanged(selectedEditionIndex());
+}
+
+void UINameAndSystemEditor::prepare()
+{
+ prepareThis();
+ prepareWidgets();
+ prepareConnections();
+ retranslateUi();
+}
+
+void UINameAndSystemEditor::prepareThis()
+{
+}
+
+void UINameAndSystemEditor::prepareWidgets()
+{
+ /* Prepare main-layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ int iRow = 0;
+
+ if (m_fChooseName)
+ {
+ /* Prepare name label: */
+ m_pLabelName = new QLabel(this);
+ if (m_pLabelName)
+ {
+ m_pLabelName->setAlignment(Qt::AlignRight);
+ m_pLabelName->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ m_pLayout->addWidget(m_pLabelName, iRow, 0);
+ }
+ /* Prepare name editor: */
+ m_pEditorName = new UIMarkableLineEdit(this);
+ if (m_pEditorName)
+ {
+ m_pLabelName->setBuddy(m_pEditorName);
+ m_pLayout->addWidget(m_pEditorName, iRow, 1, 1, 2);
+ }
+ ++iRow;
+ }
+
+ if (m_fChoosePath)
+ {
+ /* Prepare path label: */
+ m_pLabelPath = new QLabel(this);
+ if (m_pLabelPath)
+ {
+ m_pLabelPath->setAlignment(Qt::AlignRight);
+ m_pLabelPath->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ m_pLayout->addWidget(m_pLabelPath, iRow, 0);
+ }
+ /* Prepare path selector: */
+ m_pSelectorPath = new UIFilePathSelector(this);
+ if (m_pSelectorPath)
+ {
+ m_pLabelPath->setBuddy(m_pSelectorPath->focusProxy());
+ QString strDefaultMachineFolder = uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder();
+ m_pSelectorPath->setPath(strDefaultMachineFolder);
+ m_pSelectorPath->setDefaultPath(strDefaultMachineFolder);
+
+ m_pLayout->addWidget(m_pSelectorPath, iRow, 1, 1, 2);
+ }
+ ++iRow;
+ }
+
+ if (m_fChooseImage)
+ {
+ /* Prepare image label: */
+ m_pLabelImage = new QLabel(this);
+ if (m_pLabelImage)
+ {
+ m_pLabelImage->setAlignment(Qt::AlignRight);
+ m_pLabelImage->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_pLayout->addWidget(m_pLabelImage, iRow, 0);
+ }
+ /* Prepare image selector: */
+ m_pSelectorImage = new UIFilePathSelector(this);
+ if (m_pSelectorImage)
+ {
+ m_pLabelImage->setBuddy(m_pSelectorImage->focusProxy());
+ m_pSelectorImage->setResetEnabled(false);
+ m_pSelectorImage->setMode(UIFilePathSelector::Mode_File_Open);
+ m_pSelectorImage->setFileDialogFilters("ISO Images(*.iso *.ISO)");
+ m_pSelectorImage->setInitialPath(uiCommon().defaultFolderPathForType(UIMediumDeviceType_DVD));
+ m_pSelectorImage->setRecentMediaListType(UIMediumDeviceType_DVD);
+ m_pLayout->addWidget(m_pSelectorImage, iRow, 1, 1, 2);
+ }
+ ++iRow;
+ }
+
+ if (m_fChooseEdition)
+ {
+ /* Prepare edition label: */
+ m_pLabelEdition = new QLabel(this);
+ if (m_pLabelEdition)
+ {
+ m_pLabelEdition->setAlignment(Qt::AlignRight);
+ m_pLabelEdition->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_pLayout->addWidget(m_pLabelEdition, iRow, 0);
+ }
+ /* Prepare edition combo: */
+ m_pComboEdition = new QComboBox(this);
+ if (m_pComboEdition)
+ {
+ m_pLabelEdition->setBuddy(m_pComboEdition);
+ m_pLayout->addWidget(m_pComboEdition, iRow, 1, 1, 2);
+ }
+ ++iRow;
+ }
+
+ if (m_fChooseType)
+ {
+ /* Prepare VM OS family label: */
+ m_pLabelFamily = new QLabel(this);
+ if (m_pLabelFamily)
+ {
+ m_pLabelFamily->setAlignment(Qt::AlignRight);
+ m_pLabelFamily->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ m_pLayout->addWidget(m_pLabelFamily, iRow, 0);
+ }
+ /* Prepare VM OS family combo: */
+ m_pComboFamily = new QComboBox(this);
+ if (m_pComboFamily)
+ {
+ m_pLabelFamily->setBuddy(m_pComboFamily);
+ m_pLayout->addWidget(m_pComboFamily, iRow, 1);
+ }
+
+ const int iIconRow = iRow;
+ ++iRow;
+
+ /* Prepare VM OS type label: */
+ m_pLabelType = new QLabel(this);
+ if (m_pLabelType)
+ {
+ m_pLabelType->setAlignment(Qt::AlignRight);
+ m_pLabelType->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ m_pLayout->addWidget(m_pLabelType, iRow, 0);
+ }
+ /* Prepare VM OS type combo: */
+ m_pComboType = new QComboBox(this);
+ if (m_pComboType)
+ {
+ m_pLabelType->setBuddy(m_pComboType);
+ m_pLayout->addWidget(m_pComboType, iRow, 1);
+ }
+
+ ++iRow;
+
+ /* Prepare sub-layout: */
+ QVBoxLayout *pLayoutIcon = new QVBoxLayout;
+ if (pLayoutIcon)
+ {
+ /* Prepare VM OS type icon: */
+ m_pIconType = new QLabel(this);
+ if (m_pIconType)
+ {
+ m_pIconType->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ pLayoutIcon->addWidget(m_pIconType);
+ }
+
+ /* Add stretch to sub-layout: */
+ pLayoutIcon->addStretch();
+
+ /* Add into layout: */
+ m_pLayout->addLayout(pLayoutIcon, iIconRow, 2, 2, 1);
+ }
+
+ /* Initialize VM OS family combo
+ * after all widgets were created: */
+ prepareFamilyCombo();
+ }
+ }
+ /* Set top most widget of the 2nd column as focus proxy: */
+ for (int i = 0; i < m_pLayout->rowCount(); ++i)
+ {
+ QLayoutItem *pItem = m_pLayout->itemAtPosition(i, 1);
+ if (pItem && pItem->widget())
+ {
+ setFocusProxy(pItem->widget());
+ break;
+ }
+ }
+}
+
+void UINameAndSystemEditor::prepareFamilyCombo()
+{
+ AssertPtrReturnVoid(m_pComboFamily);
+
+ /* Acquire family IDs: */
+ m_familyIDs = uiCommon().vmGuestOSFamilyIDs();
+
+ /* For each known family ID: */
+ for (int i = 0; i < m_familyIDs.size(); ++i)
+ {
+ const QString &strFamilyId = m_familyIDs.at(i);
+
+ /* Append VM OS family combo: */
+ m_pComboFamily->insertItem(i, uiCommon().vmGuestOSFamilyDescription(strFamilyId));
+ m_pComboFamily->setItemData(i, strFamilyId, TypeID);
+
+ /* Fill in the type cache: */
+ m_types[strFamilyId] = QList<UIGuestOSType>();
+ foreach (const CGuestOSType &comType, uiCommon().vmGuestOSTypeList(strFamilyId))
+ {
+ UIGuestOSType guiType;
+ guiType.typeId = comType.GetId();
+ guiType.typeDescription = comType.GetDescription();
+ guiType.is64bit = comType.GetIs64Bit();
+ m_types[strFamilyId] << guiType;
+ }
+ }
+
+ /* Choose the 1st item to be the current: */
+ m_pComboFamily->setCurrentIndex(0);
+ /* And update the linked widgets accordingly: */
+ sltFamilyChanged(m_pComboFamily->currentIndex());
+}
+
+void UINameAndSystemEditor::prepareConnections()
+{
+ /* Prepare connections: */
+ if (m_pEditorName)
+ connect(m_pEditorName, &UIMarkableLineEdit::textChanged,
+ this, &UINameAndSystemEditor::sigNameChanged);
+ if (m_pSelectorPath)
+ connect(m_pSelectorPath, &UIFilePathSelector::pathChanged,
+ this, &UINameAndSystemEditor::sigPathChanged);
+ if (m_pComboFamily)
+ connect(m_pComboFamily, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UINameAndSystemEditor::sltFamilyChanged);
+ if (m_pComboType)
+ connect(m_pComboType, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UINameAndSystemEditor::sltTypeChanged);
+ if (m_pSelectorImage)
+ connect(m_pSelectorImage, &UIFilePathSelector::pathChanged,
+ this, &UINameAndSystemEditor::sigImageChanged);
+ if (m_pComboEdition)
+ connect(m_pComboEdition, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UINameAndSystemEditor::sltSelectedEditionsChanged);
+}
+
+ulong UINameAndSystemEditor::selectedEditionIndex() const
+{
+ if (!m_pComboEdition || m_pComboEdition->count() == 0)
+ return 0;
+ return m_pComboEdition->currentData().value<ulong>();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UINameAndSystemEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINameAndSystemEditor.h
new file mode 100644
index 00000000..f0c359b6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINameAndSystemEditor.h
@@ -0,0 +1,251 @@
+/* $Id: UINameAndSystemEditor.h $ */
+/** @file
+ * VBox Qt GUI - UINameAndSystemEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UINameAndSystemEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UINameAndSystemEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuestOSType.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+class QILineEdit;
+class QString;
+class UIFilePathSelector;
+class UIMarkableLineEdit;
+
+/** QWidget subclass providing complex editor for basic VM parameters. */
+class SHARED_LIBRARY_STUFF UINameAndSystemEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+ Q_PROPERTY(QString name READ name WRITE setName);
+ Q_PROPERTY(CGuestOSType type READ type WRITE setType);
+
+ /** Simple struct representing CGuestOSType cache. */
+ struct UIGuestOSType
+ {
+ QString typeId;
+ QString typeDescription;
+ bool is64bit;
+ };
+
+signals:
+
+ /** Notifies listeners about VM name change. */
+ void sigNameChanged(const QString &strNewName);
+
+ /** Notifies listeners about VM path change. */
+ void sigPathChanged(const QString &strPath);
+
+ /** Notifies listeners about VM image change. */
+ void sigImageChanged(const QString &strImage);
+
+ /** Notifies listeners about VM OS type change. */
+ void sigOsTypeChanged();
+ /** Notifies listeners about VM OS family change. */
+ void sigOSFamilyChanged(const QString &strFamilyId);
+ /** Notifies listeners about edition change. */
+ void sigEditionChanged(ulong selectedEditionIndex);
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class.
+ * @param fChooseName Controls whether we should propose to choose name.
+ * @param fChoosePath Controls whether we should propose to choose path.
+ * @param fChooseImage Controls whether we should propose to choose image.
+ * @param fChooseEdition Controls whether we should propose to choose edition.
+ * @param fChooseType Controls whether we should propose to choose type. */
+ UINameAndSystemEditor(QWidget *pParent,
+ bool fChooseName = true,
+ bool fChoosePath = false,
+ bool fChooseImage = false,
+ bool fChooseEdition = false,
+ bool fChooseType = true);
+
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+ /** Defines whether VM name stuff is @a fEnabled. */
+ void setNameStuffEnabled(bool fEnabled);
+ /** Defines whether VM path stuff is @a fEnabled. */
+ void setPathStuffEnabled(bool fEnabled);
+ /** Defines whether VM OS type stuff is @a fEnabled. */
+ void setOSTypeStuffEnabled(bool fEnabled);
+
+ /** Defines the VM @a strName. */
+ void setName(const QString &strName);
+ /** Returns the VM name. */
+ QString name() const;
+
+ /** Defines the VM @a strPath. */
+ void setPath(const QString &strPath);
+ /** Returns path string selected by the user. */
+ QString path() const;
+
+ /** Returns image string selected by the user. */
+ QString ISOImagePath() const;
+ /** Sets image path. */
+ void setISOImagePath(const QString &strPath);
+
+ /** Defines the VM OS @a strTypeId and @a strFamilyId if passed. */
+ void setTypeId(QString strTypeId, QString strFamilyId = QString());
+ /** Returns the VM OS type ID. */
+ QString typeId() const;
+ /** Returns the VM OS family ID. */
+ QString familyId() const;
+
+ /** Defines the VM OS @a enmType. */
+ void setType(const CGuestOSType &enmType);
+ /** Returns the VM OS type. */
+ CGuestOSType type() const;
+
+ /** Passes the @p fError to QILineEdit::mark(bool) effectively marking it for error. */
+ void markNameEditor(bool fError);
+ /** Passes the @p fError and @a strErrorMessage to UIFilePathSelector::mark(bool)
+ * effectively changing the background color and error-text. */
+ void markImageEditor(bool fError, const QString &strErrorMessage);
+
+ /** @p names and @p indices are parallel array storing edition names and their indices, respectively.*/
+ void setEditionNameAndIndices(const QVector<QString> &names, const QVector<ulong> &ids);
+
+ void setEditionSelectorEnabled(bool fEnabled);
+ bool isEditionsSelectorEmpty() const;
+
+ /** Returns 1st column width. */
+ int firstColumnWidth() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles VM OS family @a iIndex change. */
+ void sltFamilyChanged(int iIndex);
+ /** Handles VM OS type @a iIndex change. */
+ void sltTypeChanged(int iIndex);
+ void sltSelectedEditionsChanged(int);
+
+private:
+
+ /** @name Prepare cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares this. */
+ void prepareThis();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares VM OS family combo. */
+ void prepareFamilyCombo();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** @} */
+
+ ulong selectedEditionIndex() const;
+
+ /** @name Arguments
+ * @{ */
+ /** Holds whether we should propose to choose a name. */
+ bool m_fChooseName;
+ /** Holds whether we should propose to choose a path. */
+ bool m_fChoosePath;
+ /** Holds whether we should propose to choose an image. */
+ bool m_fChooseImage;
+ /** Holds whether we should propose to choose an edition. */
+ bool m_fChooseEdition;
+ /** Holds whether we should propose to choose a type. */
+ bool m_fChooseType;
+ /** @} */
+
+ /** @name Values
+ * @{ */
+ /** Holds the current family ID list. */
+ QStringList m_familyIDs;
+
+ /** Holds the current type cache. */
+ QMap<QString, QList<UIGuestOSType> > m_types;
+
+ /** Holds the VM OS type ID. */
+ QString m_strTypeId;
+ /** Holds the VM OS family ID. */
+ QString m_strFamilyId;
+
+ /** Holds the currently chosen OS type IDs on per-family basis. */
+ QMap<QString, QString> m_currentIds;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+
+ /** Holds the VM name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the VM path label instance. */
+ QLabel *m_pLabelPath;
+ /** Holds the ISO image label instance. */
+ QLabel *m_pLabelImage;
+ /** Holds the edition label instance. */
+ QLabel *m_pLabelEdition;
+ /** Holds the VM OS family label instance. */
+ QLabel *m_pLabelFamily;
+ /** Holds the VM OS type label instance. */
+ QLabel *m_pLabelType;
+ /** Holds the VM OS type icon instance. */
+ QLabel *m_pIconType;
+
+ /** Holds the VM name editor instance. */
+ UIMarkableLineEdit *m_pEditorName;
+ /** Holds the VM path editor instance. */
+ UIFilePathSelector *m_pSelectorPath;
+ /** Holds the file selector for ISO image (either for unattended install or to be attached to vm). */
+ UIFilePathSelector *m_pSelectorImage;
+ /** Holds the VM OS edition combo (currently only Windows ISO have this). */
+ QComboBox *m_pComboEdition;
+ /** Holds the VM OS family combo instance. */
+ QComboBox *m_pComboFamily;
+ /** Holds the VM OS type combo instance. */
+ QComboBox *m_pComboType;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UINameAndSystemEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkAttachmentEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkAttachmentEditor.cpp
new file mode 100644
index 00000000..85425f2e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkAttachmentEditor.cpp
@@ -0,0 +1,545 @@
+/* $Id: UINetworkAttachmentEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UINetworkAttachmentEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UINetworkAttachmentEditor.h"
+
+/* COM includes: */
+#include "CHostNetworkInterface.h"
+#include "CNATNetwork.h"
+#include "CSystemProperties.h"
+#ifdef VBOX_WITH_CLOUD_NET
+# include "CCloudNetwork.h"
+#endif
+#ifdef VBOX_WITH_VMNET
+# include "CHostOnlyNetwork.h"
+#endif
+
+
+/* static */
+QString UINetworkAttachmentEditor::s_strEmptyItemId = QString("#empty#");
+
+UINetworkAttachmentEditor::UINetworkAttachmentEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmRestrictedNetworkAttachmentTypes(UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid)
+ , m_enmType(KNetworkAttachmentType_Max)
+ , m_pLayout(0)
+ , m_pLabelType(0)
+ , m_pComboType(0)
+ , m_pLabelName(0)
+ , m_pComboName(0)
+{
+ prepare();
+}
+
+void UINetworkAttachmentEditor::setValueType(KNetworkAttachmentType enmType)
+{
+ if (m_enmType != enmType)
+ {
+ m_enmType = enmType;
+ populateTypeCombo();
+ }
+}
+
+KNetworkAttachmentType UINetworkAttachmentEditor::valueType() const
+{
+ return m_pComboType ? m_pComboType->currentData().value<KNetworkAttachmentType>() : m_enmType;
+}
+
+void UINetworkAttachmentEditor::setValueNames(KNetworkAttachmentType enmType, const QStringList &names)
+{
+ /* Save possible names for passed type: */
+ m_names[enmType] = names;
+
+ /* If value type is the same, update the combo as well: */
+ if (valueType() == enmType)
+ populateNameCombo();
+}
+
+void UINetworkAttachmentEditor::setValueName(KNetworkAttachmentType enmType, const QString &strName)
+{
+ /* Save current name for passed type: */
+ m_name[enmType] = strName;
+
+ /* If value type is the same, update the combo as well: */
+ if (valueType() == enmType)
+ {
+ /* Make sure combo is there: */
+ if (m_pComboName)
+ {
+ const int iIndex = m_pComboName->findText(strName);
+ if (iIndex != -1)
+ m_pComboName->setCurrentIndex(iIndex);
+ }
+ }
+}
+
+QString UINetworkAttachmentEditor::valueName(KNetworkAttachmentType enmType) const
+{
+ return m_name.value(enmType);
+}
+
+int UINetworkAttachmentEditor::minimumLabelHorizontalHint() const
+{
+ int iMinimumLabelHorizontalHint = 0;
+ if (m_pLabelType)
+ iMinimumLabelHorizontalHint = qMax(iMinimumLabelHorizontalHint, m_pLabelType->minimumSizeHint().width());
+ if (m_pLabelName)
+ iMinimumLabelHorizontalHint = qMax(iMinimumLabelHorizontalHint, m_pLabelName->minimumSizeHint().width());
+ return iMinimumLabelHorizontalHint;
+}
+
+void UINetworkAttachmentEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+/* static */
+QStringList UINetworkAttachmentEditor::bridgedAdapters()
+{
+ QStringList bridgedAdapterList;
+ foreach (const CHostNetworkInterface &comInterface, uiCommon().host().GetNetworkInterfaces())
+ {
+ if ( comInterface.GetInterfaceType() == KHostNetworkInterfaceType_Bridged
+ && !bridgedAdapterList.contains(comInterface.GetName()))
+ bridgedAdapterList << comInterface.GetName();
+ }
+ return bridgedAdapterList;
+}
+
+/* static */
+QStringList UINetworkAttachmentEditor::internalNetworks()
+{
+ return QList<QString>::fromVector(uiCommon().virtualBox().GetInternalNetworks());
+}
+
+/* static */
+QStringList UINetworkAttachmentEditor::hostInterfaces()
+{
+ QStringList hostInterfaceList;
+ foreach (const CHostNetworkInterface &comInterface, uiCommon().host().GetNetworkInterfaces())
+ {
+ if ( comInterface.GetInterfaceType() == KHostNetworkInterfaceType_HostOnly
+ && !hostInterfaceList.contains(comInterface.GetName()))
+ hostInterfaceList << comInterface.GetName();
+ }
+ return hostInterfaceList;
+}
+
+/* static */
+QStringList UINetworkAttachmentEditor::genericDrivers()
+{
+ return QList<QString>::fromVector(uiCommon().virtualBox().GetGenericNetworkDrivers());
+}
+
+/* static */
+QStringList UINetworkAttachmentEditor::natNetworks()
+{
+ QStringList natNetworkList;
+ foreach (const CNATNetwork &comNetwork, uiCommon().virtualBox().GetNATNetworks())
+ natNetworkList << comNetwork.GetNetworkName();
+ return natNetworkList;
+}
+
+#ifdef VBOX_WITH_CLOUD_NET
+/* static */
+QStringList UINetworkAttachmentEditor::cloudNetworks()
+{
+ QStringList cloudNetworkList;
+ foreach (const CCloudNetwork &comNetwork, uiCommon().virtualBox().GetCloudNetworks())
+ cloudNetworkList << comNetwork.GetNetworkName();
+ return cloudNetworkList;
+}
+#endif /* VBOX_WITH_CLOUD_NET */
+
+#ifdef VBOX_WITH_VMNET
+/* static */
+QStringList UINetworkAttachmentEditor::hostOnlyNetworks()
+{
+ QStringList hostOnlyNetworkList;
+ foreach (const CHostOnlyNetwork &comNetwork, uiCommon().virtualBox().GetHostOnlyNetworks())
+ hostOnlyNetworkList << comNetwork.GetNetworkName();
+ return hostOnlyNetworkList;
+}
+#endif /* VBOX_WITH_VMNET */
+
+void UINetworkAttachmentEditor::retranslateUi()
+{
+ /* Translate type label: */
+ if (m_pLabelType)
+ m_pLabelType->setText(tr("&Attached to:"));
+
+ /* Translate name label: */
+ if (m_pLabelName)
+ m_pLabelName->setText(tr("&Name:"));
+
+ /* Translate type combo: */
+ if (m_pComboType)
+ {
+ for (int i = 0; i < m_pComboType->count(); ++i)
+ {
+ const KNetworkAttachmentType enmType = m_pComboType->itemData(i).value<KNetworkAttachmentType>();
+ m_pComboType->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pComboType->setToolTip(tr("Holds how this virtual adapter is attached to the real network of the Host OS."));
+ }
+
+ /* Translate name combo: */
+ retranslateNameDescription();
+}
+
+void UINetworkAttachmentEditor::sltHandleCurrentTypeChanged()
+{
+ /* Update name label & combo: */
+ if (m_pLabelName)
+ m_pLabelName->setEnabled( valueType() != KNetworkAttachmentType_Null
+ && valueType() != KNetworkAttachmentType_NAT);
+ if (m_pComboName)
+ {
+ m_pComboName->setEnabled( valueType() != KNetworkAttachmentType_Null
+ && valueType() != KNetworkAttachmentType_NAT);
+ m_pComboName->setEditable( valueType() == KNetworkAttachmentType_Internal
+ || valueType() == KNetworkAttachmentType_Generic);
+ }
+
+ /* Update name combo description: */
+ retranslateNameDescription();
+
+ /* Notify listeners: */
+ emit sigValueTypeChanged();
+
+ /* Update name combo: */
+ populateNameCombo();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UINetworkAttachmentEditor::sltHandleCurrentNameChanged()
+{
+ if (m_pComboName)
+ {
+ /* Acquire new value name: */
+ QString strNewName;
+ /* Make sure that's not a name of 'empty' item: */
+ if (m_pComboName->currentData().toString() != s_strEmptyItemId)
+ strNewName = m_pComboName->currentText().simplified();
+ /* Make sure that's not an empty name itself: */
+ if (strNewName.isEmpty())
+ strNewName = QString();
+ /* If name is really changed: */
+ if (m_name[valueType()] != strNewName)
+ {
+ /* Store it: */
+ m_name[valueType()] = strNewName;
+ /* Notify listeners: */
+ emit sigValueNameChanged();
+ }
+ }
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UINetworkAttachmentEditor::prepare()
+{
+ /* Read current limitations: */
+ m_enmRestrictedNetworkAttachmentTypes = gEDataManager->restrictedNetworkAttachmentTypes();
+
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create type label: */
+ m_pLabelType = new QLabel(this);
+ if (m_pLabelType)
+ {
+ m_pLabelType->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
+ m_pLayout->addWidget(m_pLabelType, 0, 0);
+ }
+ /* Create type combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create type combo: */
+ m_pComboType = new QComboBox(this);
+ if (m_pComboType)
+ {
+ if (m_pLabelType)
+ m_pLabelType->setBuddy(m_pComboType);
+ connect(m_pComboType, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UINetworkAttachmentEditor::sltHandleCurrentTypeChanged);
+ pComboLayout->addWidget(m_pComboType);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+
+ /* Create name label: */
+ m_pLabelName = new QLabel(this);
+ m_pLabelName->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
+ if (m_pLabelName)
+ m_pLayout->addWidget(m_pLabelName, 1, 0);
+ /* Create name combo: */
+ m_pComboName = new QComboBox(this);
+ if (m_pComboName)
+ {
+ if (m_pLabelName)
+ m_pLabelName->setBuddy(m_pComboName);
+ m_pComboName->setInsertPolicy(QComboBox::NoInsert);
+ connect(m_pComboName, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UINetworkAttachmentEditor::sltHandleCurrentNameChanged);
+ connect(m_pComboName, &QComboBox::editTextChanged,
+ this, &UINetworkAttachmentEditor::sltHandleCurrentNameChanged);
+ m_pLayout->addWidget(m_pComboName, 1, 1);
+ }
+ }
+
+ /* Populate type combo: */
+ populateTypeCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UINetworkAttachmentEditor::populateTypeCombo()
+{
+ /* Make sure combo is there: */
+ if (!m_pComboType)
+ return;
+
+ /* Block signals initially: */
+ m_pComboType->blockSignals(true);
+
+ /* Clear the type combo-box: */
+ m_pComboType->clear();
+
+ /* Load currently supported network attachment types (system-properties getter): */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ QVector<KNetworkAttachmentType> supportedTypes = comProperties.GetSupportedNetworkAttachmentTypes();
+ /* Take currently requested type into account if it's different from initial one: */
+ if (!supportedTypes.contains(m_enmType) && m_enmType != KNetworkAttachmentType_Max)
+ supportedTypes.prepend(m_enmType);
+
+ /* Populate attachment types: */
+ int iAttachmentTypeIndex = 0;
+ foreach (const KNetworkAttachmentType &enmType, supportedTypes)
+ {
+ /* Filter currently restricted network attachment types (extra-data getter): */
+ if (m_enmRestrictedNetworkAttachmentTypes & toUiNetworkAdapterEnum(enmType))
+ continue;
+ m_pComboType->insertItem(iAttachmentTypeIndex, gpConverter->toString(enmType));
+ m_pComboType->setItemData(iAttachmentTypeIndex, QVariant::fromValue(enmType));
+ ++iAttachmentTypeIndex;
+ }
+
+ /* Restore previously selected type if possible: */
+ const int iIndex = m_pComboType->findData(QVariant::fromValue(m_enmType));
+ m_pComboType->setCurrentIndex(iIndex != -1 ? iIndex : 0);
+
+ /* Handle combo item change: */
+ sltHandleCurrentTypeChanged();
+
+ /* Unblock signals finally: */
+ m_pComboType->blockSignals(false);
+}
+
+void UINetworkAttachmentEditor::populateNameCombo()
+{
+ /* Make sure combo is there: */
+ if (!m_pComboName)
+ return;
+
+ /* Block signals initially: */
+ m_pComboName->blockSignals(true);
+
+ /* Clear the name combo: */
+ m_pComboName->clear();
+
+ /* Add corresponding names to combo: */
+ m_pComboName->addItems(m_names.value(valueType()));
+
+ /* Prepend 'empty' or 'default' item to combo: */
+ if (m_pComboName->count() == 0)
+ {
+ switch (valueType())
+ {
+ case KNetworkAttachmentType_Bridged:
+ case KNetworkAttachmentType_HostOnly:
+ case KNetworkAttachmentType_NATNetwork:
+#ifdef VBOX_WITH_CLOUD_NET
+ case KNetworkAttachmentType_Cloud:
+#endif
+#ifdef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnlyNetwork:
+#endif
+ {
+ /* If adapter list is empty => add 'Not selected' item: */
+ const int iIndex = m_pComboName->findData(s_strEmptyItemId);
+ if (iIndex == -1)
+ m_pComboName->insertItem(0, tr("Not selected", "network adapter name"), s_strEmptyItemId);
+ else
+ m_pComboName->setItemText(iIndex, tr("Not selected", "network adapter name"));
+ break;
+ }
+ case KNetworkAttachmentType_Internal:
+ {
+ /* Internal network list should have a default item: */
+ if (m_pComboName->findText("intnet") == -1)
+ m_pComboName->insertItem(0, "intnet");
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Restore previously selected name: */
+ const int iIndex = m_pComboName->findText(m_name.value(valueType()));
+ if (iIndex != -1)
+ m_pComboName->setCurrentIndex(iIndex);
+
+ /* Handle combo item change: */
+ sltHandleCurrentNameChanged();
+
+ /* Unblock signals finally: */
+ m_pComboName->blockSignals(false);
+}
+
+void UINetworkAttachmentEditor::retranslateNameDescription()
+{
+ /* Update name combo description: */
+ switch (valueType())
+ {
+ case KNetworkAttachmentType_Bridged:
+ m_pComboName->setToolTip(tr("Holds the network adapter on the host system that traffic "
+ "to and from this network card will go through."));
+ break;
+ case KNetworkAttachmentType_Internal:
+ m_pComboName->setToolTip(tr("Holds the name of the internal network that this network card "
+ "will be connected to. You can create a new internal network by "
+ "choosing a name which is not used by any other network cards "
+ "in this virtual machine or others."));
+ break;
+ case KNetworkAttachmentType_HostOnly:
+ m_pComboName->setToolTip(tr("Holds the virtual network adapter on the host system that traffic "
+ "to and from this network card will go through. "
+ "You can create and remove adapters using the Network Manager "
+ "tool in the virtual machine manager window."));
+ break;
+ case KNetworkAttachmentType_Generic:
+ m_pComboName->setToolTip(tr("Holds the driver to be used with this network card."));
+ break;
+ case KNetworkAttachmentType_NATNetwork:
+ m_pComboName->setToolTip(tr("Holds the name of the NAT network that this network card "
+ "will be connected to. You can create and remove networks "
+ "using the Network Manager tool in the virtual machine "
+ "manager window."));
+ break;
+#ifdef VBOX_WITH_CLOUD_NET
+ case KNetworkAttachmentType_Cloud:
+ m_pComboName->setToolTip(tr("(experimental) Holds the name of the cloud network that this network card "
+ "will be connected to. You can add and remove networks "
+ "using the Network Manager tool in the virtual machine "
+ "manager window."));
+ break;
+#endif /* VBOX_WITH_CLOUD_NET */
+#ifdef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnlyNetwork:
+ m_pComboName->setToolTip(tr("Holds the name of the host-only network that this network card "
+ "will be connected to. You can add and remove networks "
+ "using the Network Manager tool in the virtual machine "
+ "manager window."));
+ break;
+#endif /* VBOX_WITH_VMNET */
+ default:
+ m_pComboName->setToolTip(QString());
+ break;
+ }
+}
+
+void UINetworkAttachmentEditor::revalidate()
+{
+ bool fSuccess = false;
+ switch (valueType())
+ {
+ case KNetworkAttachmentType_Bridged:
+ case KNetworkAttachmentType_Internal:
+ case KNetworkAttachmentType_HostOnly:
+ case KNetworkAttachmentType_Generic:
+ case KNetworkAttachmentType_NATNetwork:
+#ifdef VBOX_WITH_CLOUD_NET
+ case KNetworkAttachmentType_Cloud:
+#endif
+#ifdef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnlyNetwork:
+#endif
+ fSuccess = !valueName(valueType()).isEmpty();
+ break;
+ default:
+ fSuccess = true;
+ break;
+ }
+ emit sigValidChanged(fSuccess);
+}
+
+/* static */
+UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork UINetworkAttachmentEditor::toUiNetworkAdapterEnum(KNetworkAttachmentType comEnum)
+{
+ switch (comEnum)
+ {
+ case KNetworkAttachmentType_NAT: return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NAT;
+ case KNetworkAttachmentType_Bridged: return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_BridgedAdapter;
+ case KNetworkAttachmentType_Internal: return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_InternalNetwork;
+ case KNetworkAttachmentType_HostOnly: return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyAdapter;
+ case KNetworkAttachmentType_Generic: return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_GenericDriver;
+ case KNetworkAttachmentType_NATNetwork: return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_NATNetwork;
+#ifdef VBOX_WITH_CLOUD_NET
+ case KNetworkAttachmentType_Cloud: return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_CloudNetwork;
+#endif
+#ifdef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnlyNetwork: return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_HostOnlyNetwork;
+#endif
+ default: return UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork_Invalid;
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkAttachmentEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkAttachmentEditor.h
new file mode 100644
index 00000000..153469b3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkAttachmentEditor.h
@@ -0,0 +1,162 @@
+/* $Id: UINetworkAttachmentEditor.h $ */
+/** @file
+ * VBox Qt GUI - UINetworkAttachmentEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UINetworkAttachmentEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UINetworkAttachmentEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as a network attachment editor. */
+class SHARED_LIBRARY_STUFF UINetworkAttachmentEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value type has changed. */
+ void sigValueTypeChanged();
+ /** Notifies listeners about value name has changed. */
+ void sigValueNameChanged();
+
+ /** Notifies listeners about value has became @a fValid. */
+ void sigValidChanged(bool fValid);
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UINetworkAttachmentEditor(QWidget *pParent = 0);
+
+ /** Defines value @a enmType. */
+ void setValueType(KNetworkAttachmentType enmType);
+ /** Returns value type. */
+ KNetworkAttachmentType valueType() const;
+
+ /** Defines value @a names for specified @a enmType. */
+ void setValueNames(KNetworkAttachmentType enmType, const QStringList &names);
+ /** Defines value @a strName for specified @a enmType. */
+ void setValueName(KNetworkAttachmentType enmType, const QString &strName);
+ /** Returns current name for specified @a enmType. */
+ QString valueName(KNetworkAttachmentType enmType) const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+ /** Returns bridged adapter list. */
+ static QStringList bridgedAdapters();
+ /** Returns internal network list. */
+ static QStringList internalNetworks();
+ /** Returns host-only interface list. */
+ static QStringList hostInterfaces();
+ /** Returns generic driver list. */
+ static QStringList genericDrivers();
+ /** Returns NAT network list. */
+ static QStringList natNetworks();
+#ifdef VBOX_WITH_CLOUD_NET
+ /** Returns cloud network list. */
+ static QStringList cloudNetworks();
+#endif
+#ifdef VBOX_WITH_VMNET
+ /** Returns host-only network list. */
+ static QStringList hostOnlyNetworks();
+#endif
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles current type change. */
+ void sltHandleCurrentTypeChanged();
+ /** Handles current name change. */
+ void sltHandleCurrentNameChanged();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates type combo. */
+ void populateTypeCombo();
+ /** Populates name combo. */
+ void populateNameCombo();
+
+ /** Retranslates name description. */
+ void retranslateNameDescription();
+
+ /** Validates editor values. */
+ void revalidate();
+
+ /** Returns UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork corresponding to passed KNetworkAttachmentType. */
+ static UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork toUiNetworkAdapterEnum(KNetworkAttachmentType comEnum);
+
+ /** Holds the empty item data id. */
+ static QString s_strEmptyItemId;
+
+ /** Holds the attachment type restrictions. */
+ UIExtraDataMetaDefs::DetailsElementOptionTypeNetwork m_enmRestrictedNetworkAttachmentTypes;
+
+ /** Holds the map of possible names. */
+ QMap<KNetworkAttachmentType, QStringList> m_names;
+ /** Holds the map of current names. */
+ QMap<KNetworkAttachmentType, QString> m_name;
+
+ /** Holds the requested type. */
+ KNetworkAttachmentType m_enmType;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the type label instance. */
+ QLabel *m_pLabelType;
+ /** Holds the type combo instance. */
+ QComboBox *m_pComboType;
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the name combo instance. */
+ QComboBox *m_pComboName;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UINetworkAttachmentEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkFeaturesEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkFeaturesEditor.cpp
new file mode 100644
index 00000000..96865f7e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkFeaturesEditor.cpp
@@ -0,0 +1,519 @@
+/* $Id: UINetworkFeaturesEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UINetworkFeaturesEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QComboBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QPushButton>
+#include <QTextEdit>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIArrowButtonSwitch.h"
+#include "QILineEdit.h"
+#include "QIToolButton.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIIconPool.h"
+#include "UINetworkFeaturesEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UINetworkFeaturesEditor::UINetworkFeaturesEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fAdvancedButtonExpanded(false)
+ , m_enmAdapterType(KNetworkAdapterType_Null)
+ , m_enmPromiscuousMode(KNetworkAdapterPromiscModePolicy_Max)
+ , m_fCableConnected(false)
+ , m_pButtonAdvanced(0)
+ , m_pWidgetSettings(0)
+ , m_pLayoutSettings(0)
+ , m_pLabelAdapterType(0)
+ , m_pComboAdapterType(0)
+ , m_pLabelPromiscuousMode(0)
+ , m_pComboPromiscuousMode(0)
+ , m_pLabelMAC(0)
+ , m_pEditorMAC(0)
+ , m_pButtonMAC(0)
+ , m_pLabelGenericProperties(0)
+ , m_pEditorGenericProperties(0)
+ , m_pCheckBoxCableConnected(0)
+ , m_pButtonPortForwarding(0)
+{
+ prepare();
+}
+
+void UINetworkFeaturesEditor::setAdvancedButtonExpanded(bool fExpanded)
+{
+ if (m_fAdvancedButtonExpanded != fExpanded)
+ {
+ m_fAdvancedButtonExpanded = fExpanded;
+ if (m_pButtonAdvanced)
+ {
+ m_pButtonAdvanced->setExpanded(m_fAdvancedButtonExpanded);
+ sltHandleAdvancedButtonStateChange();
+ }
+ }
+}
+
+bool UINetworkFeaturesEditor::advancedButtonExpanded() const
+{
+ return m_pButtonAdvanced ? m_pButtonAdvanced->isExpanded() : m_fAdvancedButtonExpanded;
+}
+
+void UINetworkFeaturesEditor::setAdapterType(const KNetworkAdapterType &enmType)
+{
+ if (m_enmAdapterType != enmType)
+ {
+ m_enmAdapterType = enmType;
+ repopulateAdapterTypeCombo();
+ }
+}
+
+KNetworkAdapterType UINetworkFeaturesEditor::adapterType() const
+{
+ return m_pComboAdapterType ? m_pComboAdapterType->currentData().value<KNetworkAdapterType>() : m_enmAdapterType;
+}
+
+void UINetworkFeaturesEditor::setPromiscuousMode(const KNetworkAdapterPromiscModePolicy &enmMode)
+{
+ if (m_enmPromiscuousMode != enmMode)
+ {
+ m_enmPromiscuousMode = enmMode;
+ repopulatePromiscuousModeCombo();
+ }
+}
+
+KNetworkAdapterPromiscModePolicy UINetworkFeaturesEditor::promiscuousMode() const
+{
+ return m_pComboPromiscuousMode ? m_pComboPromiscuousMode->currentData().value<KNetworkAdapterPromiscModePolicy>() : m_enmPromiscuousMode;
+}
+
+void UINetworkFeaturesEditor::setMACAddress(const QString &strAddress)
+{
+ if (m_strMACAddress != strAddress)
+ {
+ m_strMACAddress = strAddress;
+ if (m_pEditorMAC)
+ m_pEditorMAC->setText(m_strMACAddress);
+ }
+}
+
+QString UINetworkFeaturesEditor::macAddress() const
+{
+ return m_pEditorMAC ? m_pEditorMAC->text() : m_strMACAddress;
+}
+
+void UINetworkFeaturesEditor::setGenericProperties(const QString &strProperties)
+{
+ if (m_strGenericProperties != strProperties)
+ {
+ m_strGenericProperties = strProperties;
+ if (m_pEditorGenericProperties)
+ m_pEditorGenericProperties->setPlainText(m_strGenericProperties);
+ }
+}
+
+QString UINetworkFeaturesEditor::genericProperties() const
+{
+ return m_pEditorGenericProperties ? m_pEditorGenericProperties->toPlainText() : m_strGenericProperties;
+}
+
+void UINetworkFeaturesEditor::setCableConnected(bool fConnected)
+{
+ if (m_fCableConnected != fConnected)
+ {
+ m_fCableConnected = fConnected;
+ if (m_pCheckBoxCableConnected)
+ m_pCheckBoxCableConnected->setChecked(m_fCableConnected);
+ }
+}
+
+bool UINetworkFeaturesEditor::cableConnected() const
+{
+ return m_pCheckBoxCableConnected ? m_pCheckBoxCableConnected->isChecked() : m_fCableConnected;
+}
+
+void UINetworkFeaturesEditor::setPortForwardingRules(const UIPortForwardingDataList &rules)
+{
+ if (m_portForwardingRules != rules)
+ m_portForwardingRules = rules;
+}
+
+UIPortForwardingDataList UINetworkFeaturesEditor::portForwardingRules() const
+{
+ return m_portForwardingRules;
+}
+
+void UINetworkFeaturesEditor::setAdvancedOptionsAvailable(bool fAvailable)
+{
+ m_pButtonAdvanced->setEnabled(fAvailable);
+}
+
+void UINetworkFeaturesEditor::setAdapterOptionsAvailable(bool fAvailable)
+{
+ m_pLabelAdapterType->setEnabled(fAvailable);
+ m_pComboAdapterType->setEnabled(fAvailable);
+}
+
+void UINetworkFeaturesEditor::setPromiscuousOptionsAvailable(bool fAvailable)
+{
+ m_pLabelPromiscuousMode->setEnabled(fAvailable);
+ m_pComboPromiscuousMode->setEnabled(fAvailable);
+}
+
+void UINetworkFeaturesEditor::setMACOptionsAvailable(bool fAvailable)
+{
+ m_pLabelMAC->setEnabled(fAvailable);
+ m_pEditorMAC->setEnabled(fAvailable);
+ m_pButtonMAC->setEnabled(fAvailable);
+}
+
+void UINetworkFeaturesEditor::setGenericPropertiesAvailable(bool fAvailable)
+{
+ m_pLabelGenericProperties->setVisible(fAvailable);
+ m_pEditorGenericProperties->setVisible(fAvailable);
+}
+
+void UINetworkFeaturesEditor::setCableOptionsAvailable(bool fAvailable)
+{
+ m_pCheckBoxCableConnected->setEnabled(fAvailable);
+}
+
+void UINetworkFeaturesEditor::setForwardingOptionsAvailable(bool fAvailable)
+{
+ m_pButtonPortForwarding->setVisible(fAvailable);
+}
+
+int UINetworkFeaturesEditor::minimumLabelHorizontalHint() const
+{
+ int iMinimumLabelHorizontalHint = 0;
+ if (m_pLabelAdapterType)
+ iMinimumLabelHorizontalHint = qMax(iMinimumLabelHorizontalHint, m_pLabelAdapterType->minimumSizeHint().width());
+ if (m_pLabelPromiscuousMode)
+ iMinimumLabelHorizontalHint = qMax(iMinimumLabelHorizontalHint, m_pLabelPromiscuousMode->minimumSizeHint().width());
+ if (m_pLabelMAC)
+ iMinimumLabelHorizontalHint = qMax(iMinimumLabelHorizontalHint, m_pLabelMAC->minimumSizeHint().width());
+ if (m_pLabelGenericProperties)
+ iMinimumLabelHorizontalHint = qMax(iMinimumLabelHorizontalHint, m_pLabelGenericProperties->minimumSizeHint().width());
+ return iMinimumLabelHorizontalHint;
+}
+
+void UINetworkFeaturesEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayoutSettings)
+ m_pLayoutSettings->setColumnMinimumWidth(0, iIndent);
+}
+
+void UINetworkFeaturesEditor::generateMac()
+{
+ setMACAddress(uiCommon().host().GenerateMACAddress());
+}
+
+void UINetworkFeaturesEditor::retranslateUi()
+{
+ if (m_pButtonAdvanced)
+ {
+ m_pButtonAdvanced->setText(tr("A&dvanced"));
+ m_pButtonAdvanced->setToolTip(tr("Shows additional network adapter options."));
+ }
+
+ if (m_pLabelAdapterType)
+ m_pLabelAdapterType->setText(tr("Adapter &Type:"));
+ if (m_pComboAdapterType)
+ {
+ for (int i = 0; i < m_pComboAdapterType->count(); ++i)
+ {
+ const KNetworkAdapterType enmType = m_pComboAdapterType->itemData(i).value<KNetworkAdapterType>();
+ m_pComboAdapterType->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pComboAdapterType->setToolTip(tr("Holds the type of the virtual network adapter. Depending on this value, VirtualBox "
+ "will provide different network hardware to the virtual machine."));
+ }
+
+ if (m_pLabelPromiscuousMode)
+ m_pLabelPromiscuousMode->setText(tr("&Promiscuous Mode:"));
+ if (m_pComboPromiscuousMode)
+ {
+ for (int i = 0; i < m_pComboPromiscuousMode->count(); ++i)
+ {
+ const KNetworkAdapterPromiscModePolicy enmType = m_pComboPromiscuousMode->itemData(i).value<KNetworkAdapterPromiscModePolicy>();
+ m_pComboPromiscuousMode->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pComboPromiscuousMode->setToolTip(tr("Holds the promiscuous mode policy of the network adapter when attached to an "
+ "internal network, host only network or a bridge."));
+ }
+
+ if (m_pLabelMAC)
+ m_pLabelMAC->setText(tr("&MAC Address:"));
+ if (m_pEditorMAC)
+ m_pEditorMAC->setToolTip(tr("Holds the MAC address of this adapter. It contains exactly 12 characters chosen from "
+ "{0-9,A-F}. Note that the second character must be an even digit."));
+ if (m_pButtonMAC)
+ m_pButtonMAC->setToolTip(tr("Generates a new random MAC address."));
+
+ if (m_pLabelGenericProperties)
+ m_pLabelGenericProperties->setText(tr("Generic Properties:"));
+ if (m_pEditorGenericProperties)
+ m_pEditorGenericProperties->setToolTip(tr("Holds the configuration settings for the network attachment driver. The "
+ "settings should be of the form name=value and will depend on the "
+ "driver. Use shift-enter to add a new entry."));
+
+ if (m_pCheckBoxCableConnected)
+ {
+ m_pCheckBoxCableConnected->setText(tr("&Cable Connected"));
+ m_pCheckBoxCableConnected->setToolTip(tr("When checked, the virtual network cable is plugged in."));
+ }
+
+ if (m_pButtonPortForwarding)
+ {
+ m_pButtonPortForwarding->setText(tr("&Port Forwarding"));
+ m_pButtonPortForwarding->setToolTip(tr("Displays a window to configure port forwarding rules."));
+ }
+}
+
+void UINetworkFeaturesEditor::sltHandleAdvancedButtonStateChange()
+{
+ /* What's the state? */
+ const bool fExpanded = m_pButtonAdvanced->isExpanded();
+ /* Update widget visibility: */
+ m_pWidgetSettings->setVisible(fExpanded);
+ /* Notify listeners about the button state change: */
+ emit sigAdvancedButtonStateChange(fExpanded);
+}
+
+void UINetworkFeaturesEditor::sltOpenPortForwardingDlg()
+{
+ UIMachineSettingsPortForwardingDlg dlg(this, m_portForwardingRules);
+ if (dlg.exec() == QDialog::Accepted)
+ m_portForwardingRules = dlg.rules();
+}
+
+void UINetworkFeaturesEditor::prepare()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare advanced arrow button: */
+ m_pButtonAdvanced = new QIArrowButtonSwitch(this);
+ if (m_pButtonAdvanced)
+ {
+ const QStyle *pStyle = QApplication::style();
+ const int iIconMetric = (int)(pStyle->pixelMetric(QStyle::PM_SmallIconSize) * .625);
+ m_pButtonAdvanced->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pButtonAdvanced->setIcons(UIIconPool::iconSet(":/arrow_right_10px.png"),
+ UIIconPool::iconSet(":/arrow_down_10px.png"));
+
+ pLayout->addWidget(m_pButtonAdvanced);
+ }
+
+ /* Prepare advanced settings widget: */
+ m_pWidgetSettings = new QWidget(this);
+ if (m_pWidgetSettings)
+ {
+ /* Prepare advanced settings layout: */
+ m_pLayoutSettings = new QGridLayout(m_pWidgetSettings);
+ if (m_pLayoutSettings)
+ {
+ m_pLayoutSettings->setContentsMargins(0, 0, 0, 0);
+ m_pLayoutSettings->setColumnStretch(2, 1);
+
+ /* Prepare adapter type label: */
+ m_pLabelAdapterType = new QLabel(m_pWidgetSettings);
+ if (m_pLabelAdapterType)
+ {
+ m_pLabelAdapterType->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutSettings->addWidget(m_pLabelAdapterType, 0, 0);
+ }
+ /* Prepare adapter type combo: */
+ m_pComboAdapterType = new QComboBox(m_pWidgetSettings);
+ if (m_pComboAdapterType)
+ {
+ if (m_pLabelAdapterType)
+ m_pLabelAdapterType->setBuddy(m_pComboAdapterType);
+ m_pLayoutSettings->addWidget(m_pComboAdapterType, 0, 1, 1, 3);
+ }
+
+ /* Prepare promiscuous mode label: */
+ m_pLabelPromiscuousMode = new QLabel(m_pWidgetSettings);
+ if (m_pLabelPromiscuousMode)
+ {
+ m_pLabelPromiscuousMode->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutSettings->addWidget(m_pLabelPromiscuousMode, 1, 0);
+ }
+ /* Prepare promiscuous mode combo: */
+ m_pComboPromiscuousMode = new QComboBox(m_pWidgetSettings);
+ if (m_pComboPromiscuousMode)
+ {
+ if (m_pLabelPromiscuousMode)
+ m_pLabelPromiscuousMode->setBuddy(m_pComboPromiscuousMode);
+ m_pLayoutSettings->addWidget(m_pComboPromiscuousMode, 1, 1, 1, 3);
+ }
+
+ /* Prepare MAC label: */
+ m_pLabelMAC = new QLabel(m_pWidgetSettings);
+ if (m_pLabelMAC)
+ {
+ m_pLabelMAC->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutSettings->addWidget(m_pLabelMAC, 2, 0);
+ }
+ /* Prepare MAC editor: */
+ m_pEditorMAC = new QILineEdit(m_pWidgetSettings);
+ if (m_pEditorMAC)
+ {
+ if (m_pLabelMAC)
+ m_pLabelMAC->setBuddy(m_pEditorMAC);
+ m_pEditorMAC->setAllowToCopyContentsWhenDisabled(true);
+ m_pEditorMAC->setValidator(new QRegularExpressionValidator(QRegularExpression("[0-9A-Fa-f]{12}"), this));
+ m_pEditorMAC->setMinimumWidthByText(QString().fill('0', 12));
+
+ m_pLayoutSettings->addWidget(m_pEditorMAC, 2, 1, 1, 2);
+ }
+ /* Prepare MAC button: */
+ m_pButtonMAC = new QIToolButton(m_pWidgetSettings);
+ if (m_pButtonMAC)
+ {
+ m_pButtonMAC->setIcon(UIIconPool::iconSet(":/refresh_16px.png"));
+ m_pLayoutSettings->addWidget(m_pButtonMAC, 2, 3);
+ }
+
+ /* Prepare MAC label: */
+ m_pLabelGenericProperties = new QLabel(m_pWidgetSettings);
+ if (m_pLabelGenericProperties)
+ {
+ m_pLabelGenericProperties->setAlignment(Qt::AlignRight | Qt::AlignTop);
+ m_pLayoutSettings->addWidget(m_pLabelGenericProperties, 3, 0);
+ }
+ /* Prepare MAC editor: */
+ m_pEditorGenericProperties = new QTextEdit(m_pWidgetSettings);
+ if (m_pEditorGenericProperties)
+ m_pLayoutSettings->addWidget(m_pEditorGenericProperties, 3, 1, 1, 3);
+
+ /* Prepare cable connected check-box: */
+ m_pCheckBoxCableConnected = new QCheckBox(m_pWidgetSettings);
+ if (m_pCheckBoxCableConnected)
+ m_pLayoutSettings->addWidget(m_pCheckBoxCableConnected, 4, 1, 1, 2);
+
+ /* Prepare port forwarding button: */
+ m_pButtonPortForwarding = new QPushButton(m_pWidgetSettings);
+ if (m_pButtonPortForwarding)
+ m_pLayoutSettings->addWidget(m_pButtonPortForwarding, 5, 1);
+ }
+
+ pLayout->addWidget(m_pWidgetSettings);
+ }
+ }
+
+ /* Configure connections: */
+ if (m_pButtonAdvanced)
+ connect(m_pButtonAdvanced, &QIArrowButtonSwitch::sigClicked,
+ this, &UINetworkFeaturesEditor::sltHandleAdvancedButtonStateChange);
+ if (m_pEditorMAC)
+ connect(m_pEditorMAC, &QILineEdit::textChanged,
+ this, &UINetworkFeaturesEditor::sigMACAddressChanged);
+ if (m_pButtonMAC)
+ connect(m_pButtonMAC, &QIToolButton::clicked,
+ this, &UINetworkFeaturesEditor::generateMac);
+ if (m_pButtonPortForwarding)
+ connect(m_pButtonPortForwarding, &QPushButton::clicked,
+ this, &UINetworkFeaturesEditor::sltOpenPortForwardingDlg);
+
+ /* Update widget availability: */
+ m_pWidgetSettings->setVisible(m_pButtonAdvanced->isExpanded());
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UINetworkFeaturesEditor::repopulateAdapterTypeCombo()
+{
+ if (m_pComboAdapterType)
+ {
+ /* Clear combo first of all: */
+ m_pComboAdapterType->clear();
+
+ /* Load currently supported types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ QVector<KNetworkAdapterType> supportedTypes = comProperties.GetSupportedNetworkAdapterTypes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmAdapterType != KNetworkAdapterType_Null
+ && !supportedTypes.contains(m_enmAdapterType))
+ supportedTypes.prepend(m_enmAdapterType);
+
+ /* Update combo with all the supported values: */
+ foreach (const KNetworkAdapterType &enmType, supportedTypes)
+ m_pComboAdapterType->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pComboAdapterType->findData(QVariant::fromValue(m_enmAdapterType));
+ if (iIndex != -1)
+ m_pComboAdapterType->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
+
+void UINetworkFeaturesEditor::repopulatePromiscuousModeCombo()
+{
+ if (m_pComboPromiscuousMode)
+ {
+ /* Clear combo first of all: */
+ m_pComboPromiscuousMode->clear();
+
+ /* Populate currently supported types: */
+ QVector<KNetworkAdapterPromiscModePolicy> supportedTypes =
+ QVector<KNetworkAdapterPromiscModePolicy>() << KNetworkAdapterPromiscModePolicy_Deny
+ << KNetworkAdapterPromiscModePolicy_AllowNetwork
+ << KNetworkAdapterPromiscModePolicy_AllowAll;
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmPromiscuousMode != KNetworkAdapterPromiscModePolicy_Max
+ && !supportedTypes.contains(m_enmPromiscuousMode))
+ supportedTypes.prepend(m_enmPromiscuousMode);
+
+ /* Update combo with all the supported values: */
+ foreach (const KNetworkAdapterPromiscModePolicy &enmType, supportedTypes)
+ m_pComboPromiscuousMode->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pComboPromiscuousMode->findData(QVariant::fromValue(m_enmPromiscuousMode));
+ if (iIndex != -1)
+ m_pComboPromiscuousMode->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkFeaturesEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkFeaturesEditor.h
new file mode 100644
index 00000000..40ef8d68
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkFeaturesEditor.h
@@ -0,0 +1,201 @@
+/* $Id: UINetworkFeaturesEditor.h $ */
+/** @file
+ * VBox Qt GUI - UINetworkFeaturesEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UINetworkFeaturesEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UINetworkFeaturesEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIMachineSettingsPortForwardingDlg.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QComboBox;
+class QGridLayout;
+class QLabel;
+class QTextEdit;
+class QIArrowButtonSwitch;
+class QILineEdit;
+class QIToolButton;
+
+/** QWidget subclass used as a network features editor. */
+class SHARED_LIBRARY_STUFF UINetworkFeaturesEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about the advanced button state change to @a fExpanded. */
+ void sigAdvancedButtonStateChange(bool fExpanded);
+ /** Notifies about MAC address changed. */
+ void sigMACAddressChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UINetworkFeaturesEditor(QWidget *pParent = 0);
+
+ /** Defines whether advanced button @a fExpanded. */
+ void setAdvancedButtonExpanded(bool fExpanded);
+ /** Returns whether advanced button expanded. */
+ bool advancedButtonExpanded() const;
+
+ /** Defines adapter @a enmType. */
+ void setAdapterType(const KNetworkAdapterType &enmType);
+ /** Returns adapter type. */
+ KNetworkAdapterType adapterType() const;
+
+ /** Defines promiscuous @a enmMode. */
+ void setPromiscuousMode(const KNetworkAdapterPromiscModePolicy &enmMode);
+ /** Returns promiscuous mode. */
+ KNetworkAdapterPromiscModePolicy promiscuousMode() const;
+
+ /** Defines MAC @a strAddress. */
+ void setMACAddress(const QString &strAddress);
+ /** Returns MAC address. */
+ QString macAddress() const;
+
+ /** Defines generic @a strProperties. */
+ void setGenericProperties(const QString &strProperties);
+ /** Returns generic properties. */
+ QString genericProperties() const;
+
+ /** Defines whether cable is @a fConnected. */
+ void setCableConnected(bool fConnected);
+ /** Returns whether cable is connected. */
+ bool cableConnected() const;
+
+ /** Defines list of port forwarding @a rules. */
+ void setPortForwardingRules(const UIPortForwardingDataList &rules);
+ /** Returns list of port forwarding rules. */
+ UIPortForwardingDataList portForwardingRules() const;
+
+ /** Defines whether advanced options @a fAvailable. */
+ void setAdvancedOptionsAvailable(bool fAvailable);
+ /** Defines whether adapter options @a fAvailable. */
+ void setAdapterOptionsAvailable(bool fAvailable);
+ /** Defines whether promiscuous options @a fAvailable. */
+ void setPromiscuousOptionsAvailable(bool fAvailable);
+ /** Defines whether MAC options @a fAvailable. */
+ void setMACOptionsAvailable(bool fAvailable);
+ /** Defines whether generic properties @a fAvailable. */
+ void setGenericPropertiesAvailable(bool fAvailable);
+ /** Defines whether cable options @a fAvailable. */
+ void setCableOptionsAvailable(bool fAvailable);
+ /** Defines whether forwarding options @a fAvailable. */
+ void setForwardingOptionsAvailable(bool fAvailable);
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+public slots:
+
+ /** Generates MAC address. */
+ void generateMac();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles advanced button state change to expanded. */
+ void sltHandleAdvancedButtonStateChange();
+ /** Handles request to open port forwarding dialog. */
+ void sltOpenPortForwardingDlg();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Repopulates adapter type combo. */
+ void repopulateAdapterTypeCombo();
+ /** Repopulates promiscuous mode combo. */
+ void repopulatePromiscuousModeCombo();
+
+ /** @name Values
+ * @{ */
+ /** Holds whether advanced button expanded. */
+ bool m_fAdvancedButtonExpanded;
+ /** Holds the adapter type to be set. */
+ KNetworkAdapterType m_enmAdapterType;
+ /** Holds the promisc mode to be set. */
+ KNetworkAdapterPromiscModePolicy m_enmPromiscuousMode;
+ /** Holds the MAC address to be set. */
+ QString m_strMACAddress;
+ /** Holds the generic properties to be set. */
+ QString m_strGenericProperties;
+ /** Holds whether cable is connected. */
+ bool m_fCableConnected;
+ /** Holds the list of port forwarding rules. */
+ UIPortForwardingDataList m_portForwardingRules;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the advanced button instance. */
+ QIArrowButtonSwitch *m_pButtonAdvanced;
+ /** Holds the settings widget instance. */
+ QWidget *m_pWidgetSettings;
+ /** Holds the settings layout instance. */
+ QGridLayout *m_pLayoutSettings;
+ /** Holds the adapter type label instance. */
+ QLabel *m_pLabelAdapterType;
+ /** Holds the adapter type editor instance. */
+ QComboBox *m_pComboAdapterType;
+ /** Holds the promiscuous mode label instance. */
+ QLabel *m_pLabelPromiscuousMode;
+ /** Holds the promiscuous mode combo instance. */
+ QComboBox *m_pComboPromiscuousMode;
+ /** Holds the MAC label instance. */
+ QLabel *m_pLabelMAC;
+ /** Holds the MAC editor instance. */
+ QILineEdit *m_pEditorMAC;
+ /** Holds the MAC button instance. */
+ QIToolButton *m_pButtonMAC;
+ /** Holds the generic properties label instance. */
+ QLabel *m_pLabelGenericProperties;
+ /** Holds the generic properties editor instance. */
+ QTextEdit *m_pEditorGenericProperties;
+ /** Holds the cable connected check-box instance. */
+ QCheckBox *m_pCheckBoxCableConnected;
+ /** Holds the port forwarding button instance. */
+ QPushButton *m_pButtonPortForwarding;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UINetworkFeaturesEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkSettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkSettingsEditor.cpp
new file mode 100644
index 00000000..d5996963
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkSettingsEditor.cpp
@@ -0,0 +1,351 @@
+/* $Id: UINetworkSettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UINetworkSettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UINetworkAttachmentEditor.h"
+#include "UINetworkFeaturesEditor.h"
+#include "UINetworkSettingsEditor.h"
+
+
+UINetworkSettingsEditor::UINetworkSettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fFeatureEnabled(false)
+ , m_pCheckboxFeature(0)
+ , m_pWidgetSettings(0)
+ , m_pEditorNetworkAttachment(0)
+ , m_pEditorNetworkFeatures(0)
+{
+ prepare();
+}
+
+void UINetworkSettingsEditor::setFeatureEnabled(bool fEnabled)
+{
+ if (m_fFeatureEnabled != fEnabled)
+ {
+ m_fFeatureEnabled = fEnabled;
+ if (m_pCheckboxFeature)
+ m_pCheckboxFeature->setChecked(m_fFeatureEnabled);
+ }
+}
+
+bool UINetworkSettingsEditor::isFeatureEnabled() const
+{
+ return m_pCheckboxFeature ? m_pCheckboxFeature->isChecked() : m_fFeatureEnabled;
+}
+
+void UINetworkSettingsEditor::setFeatureAvailable(bool fAvailable)
+{
+ if (m_pCheckboxFeature)
+ m_pCheckboxFeature->setEnabled(fAvailable);
+}
+
+void UINetworkSettingsEditor::setValueType(KNetworkAttachmentType enmType)
+{
+ if (m_pEditorNetworkAttachment)
+ m_pEditorNetworkAttachment->setValueType(enmType);
+}
+
+KNetworkAttachmentType UINetworkSettingsEditor::valueType() const
+{
+ return m_pEditorNetworkAttachment ? m_pEditorNetworkAttachment->valueType() : KNetworkAttachmentType_Null;
+}
+
+void UINetworkSettingsEditor::setValueNames(KNetworkAttachmentType enmType, const QStringList &names)
+{
+ if (m_pEditorNetworkAttachment)
+ m_pEditorNetworkAttachment->setValueNames(enmType, names);
+}
+
+void UINetworkSettingsEditor::setValueName(KNetworkAttachmentType enmType, const QString &strName)
+{
+ if (m_pEditorNetworkAttachment)
+ m_pEditorNetworkAttachment->setValueName(enmType, strName);
+}
+
+QString UINetworkSettingsEditor::valueName(KNetworkAttachmentType enmType) const
+{
+ return m_pEditorNetworkAttachment ? m_pEditorNetworkAttachment->valueName(enmType) : QString();
+}
+
+void UINetworkSettingsEditor::setAttachmentOptionsAvailable(bool fAvailable)
+{
+ if (m_pEditorNetworkAttachment)
+ m_pEditorNetworkAttachment->setEnabled(fAvailable);
+}
+
+void UINetworkSettingsEditor::setAdvancedButtonExpanded(bool fExpanded)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setAdvancedButtonExpanded(fExpanded);
+}
+
+bool UINetworkSettingsEditor::advancedButtonExpanded() const
+{
+ return m_pEditorNetworkFeatures ? m_pEditorNetworkFeatures->advancedButtonExpanded() : false;
+}
+
+void UINetworkSettingsEditor::setAdapterType(const KNetworkAdapterType &enmType)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setAdapterType(enmType);
+}
+
+KNetworkAdapterType UINetworkSettingsEditor::adapterType() const
+{
+ return m_pEditorNetworkFeatures ? m_pEditorNetworkFeatures->adapterType() : KNetworkAdapterType_Null;
+}
+
+void UINetworkSettingsEditor::setPromiscuousMode(const KNetworkAdapterPromiscModePolicy &enmMode)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setPromiscuousMode(enmMode);
+}
+
+KNetworkAdapterPromiscModePolicy UINetworkSettingsEditor::promiscuousMode() const
+{
+ return m_pEditorNetworkFeatures ? m_pEditorNetworkFeatures->promiscuousMode() : KNetworkAdapterPromiscModePolicy_Deny;
+}
+
+void UINetworkSettingsEditor::setMACAddress(const QString &strAddress)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setMACAddress(strAddress);
+}
+
+QString UINetworkSettingsEditor::macAddress() const
+{
+ return m_pEditorNetworkFeatures ? m_pEditorNetworkFeatures->macAddress() : QString();
+}
+
+void UINetworkSettingsEditor::setGenericProperties(const QString &strProperties)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setGenericProperties(strProperties);
+}
+
+QString UINetworkSettingsEditor::genericProperties() const
+{
+ return m_pEditorNetworkFeatures ? m_pEditorNetworkFeatures->genericProperties() : QString();
+}
+
+void UINetworkSettingsEditor::setCableConnected(bool fConnected)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setCableConnected(fConnected);
+}
+
+bool UINetworkSettingsEditor::cableConnected() const
+{
+ return m_pEditorNetworkFeatures ? m_pEditorNetworkFeatures->cableConnected() : false;
+}
+
+void UINetworkSettingsEditor::setPortForwardingRules(const UIPortForwardingDataList &rules)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setPortForwardingRules(rules);
+}
+
+UIPortForwardingDataList UINetworkSettingsEditor::portForwardingRules() const
+{
+ return m_pEditorNetworkFeatures ? m_pEditorNetworkFeatures->portForwardingRules() : UIPortForwardingDataList();
+}
+
+void UINetworkSettingsEditor::setAdvancedOptionsAvailable(bool fAvailable)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setAdvancedOptionsAvailable(fAvailable);
+}
+
+void UINetworkSettingsEditor::setAdapterOptionsAvailable(bool fAvailable)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setAdapterOptionsAvailable(fAvailable);
+}
+
+void UINetworkSettingsEditor::setPromiscuousOptionsAvailable(bool fAvailable)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setPromiscuousOptionsAvailable(fAvailable);
+}
+
+void UINetworkSettingsEditor::setMACOptionsAvailable(bool fAvailable)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setMACOptionsAvailable(fAvailable);
+}
+
+void UINetworkSettingsEditor::setGenericPropertiesAvailable(bool fAvailable)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setGenericPropertiesAvailable(fAvailable);
+}
+
+void UINetworkSettingsEditor::setCableOptionsAvailable(bool fAvailable)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setCableOptionsAvailable(fAvailable);
+}
+
+void UINetworkSettingsEditor::setForwardingOptionsAvailable(bool fAvailable)
+{
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setForwardingOptionsAvailable(fAvailable);
+}
+
+void UINetworkSettingsEditor::retranslateUi()
+{
+ if (m_pCheckboxFeature)
+ {
+ m_pCheckboxFeature->setText(tr("&Enable Network Adapter"));
+ m_pCheckboxFeature->setToolTip(tr("When checked, plugs this virtual network adapter into the virtual machine."));
+ }
+
+ /* These editors have own labels, but we want them to be properly layouted according to each other: */
+ int iMinimumLayoutHint = 0;
+ if (m_pEditorNetworkAttachment)
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorNetworkAttachment->minimumLabelHorizontalHint());
+ if (m_pEditorNetworkFeatures)
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorNetworkFeatures->minimumLabelHorizontalHint());
+ if (m_pEditorNetworkAttachment)
+ m_pEditorNetworkAttachment->setMinimumLayoutIndent(iMinimumLayoutHint);
+ if (m_pEditorNetworkFeatures)
+ m_pEditorNetworkFeatures->setMinimumLayoutIndent(iMinimumLayoutHint);
+}
+
+void UINetworkSettingsEditor::sltHandleFeatureToggled()
+{
+ /* Update widget availability: */
+ updateFeatureAvailability();
+
+ /* Generate a new MAC address in case it's currently empty: */
+ if ( m_pCheckboxFeature->isChecked()
+ && m_pEditorNetworkFeatures->macAddress().isEmpty())
+ m_pEditorNetworkFeatures->generateMac();
+
+ /* Notify listeners: */
+ emit sigFeatureStateChanged();
+}
+
+void UINetworkSettingsEditor::sltHandleAttachmentTypeChange()
+{
+ /* Update widget availability: */
+ const KNetworkAttachmentType enmType = m_pEditorNetworkAttachment->valueType();
+ m_pEditorNetworkFeatures->setPromiscuousOptionsAvailable( enmType != KNetworkAttachmentType_Null
+ && enmType != KNetworkAttachmentType_Generic
+ && enmType != KNetworkAttachmentType_NAT);
+ m_pEditorNetworkFeatures->setGenericPropertiesAvailable(enmType == KNetworkAttachmentType_Generic);
+ m_pEditorNetworkFeatures->setForwardingOptionsAvailable(enmType == KNetworkAttachmentType_NAT);
+
+ /* Notify listeners: */
+ emit sigAttachmentTypeChanged();
+}
+
+void UINetworkSettingsEditor::prepare()
+{
+ /* Prepare stuff: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Update widget availability: */
+ updateFeatureAvailability();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UINetworkSettingsEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare adapter check-box: */
+ m_pCheckboxFeature = new QCheckBox(this);
+ if (m_pCheckboxFeature)
+ pLayout->addWidget(m_pCheckboxFeature, 0, 0, 1, 2);
+
+ /* Prepare 20-px shifting spacer: */
+ QSpacerItem *pSpacerItem = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacerItem)
+ pLayout->addItem(pSpacerItem, 1, 0);
+
+ /* Prepare adapter settings widget: */
+ m_pWidgetSettings = new QWidget(this);
+ if (m_pWidgetSettings)
+ {
+ /* Prepare adapter settings widget layout: */
+ QVBoxLayout *pLayoutAdapterSettings = new QVBoxLayout(m_pWidgetSettings);
+ if (pLayoutAdapterSettings)
+ {
+ pLayoutAdapterSettings->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare attachment type editor: */
+ m_pEditorNetworkAttachment = new UINetworkAttachmentEditor(m_pWidgetSettings);
+ if (m_pEditorNetworkAttachment)
+ pLayoutAdapterSettings->addWidget(m_pEditorNetworkAttachment);
+
+ /* Prepare advanced settingseditor: */
+ m_pEditorNetworkFeatures = new UINetworkFeaturesEditor(m_pWidgetSettings);
+ if (m_pEditorNetworkFeatures)
+ pLayoutAdapterSettings->addWidget(m_pEditorNetworkFeatures);
+ }
+
+ pLayout->addWidget(m_pWidgetSettings, 1, 1);
+ }
+ }
+}
+
+void UINetworkSettingsEditor::prepareConnections()
+{
+ if (m_pCheckboxFeature)
+ connect(m_pCheckboxFeature, &QCheckBox::stateChanged,
+ this, &UINetworkSettingsEditor::sltHandleFeatureToggled);
+ if (m_pEditorNetworkAttachment)
+ connect(m_pEditorNetworkAttachment, &UINetworkAttachmentEditor::sigValueTypeChanged,
+ this, &UINetworkSettingsEditor::sltHandleAttachmentTypeChange);
+ if (m_pEditorNetworkAttachment)
+ connect(m_pEditorNetworkAttachment, &UINetworkAttachmentEditor::sigValueNameChanged,
+ this, &UINetworkSettingsEditor::sigAlternativeNameChanged);
+ if (m_pEditorNetworkFeatures)
+ connect(m_pEditorNetworkFeatures, &UINetworkFeaturesEditor::sigAdvancedButtonStateChange,
+ this, &UINetworkSettingsEditor::sigAdvancedButtonStateChange);
+ if (m_pEditorNetworkFeatures)
+ connect(m_pEditorNetworkFeatures, &UINetworkFeaturesEditor::sigMACAddressChanged,
+ this, &UINetworkSettingsEditor::sigMACAddressChanged);
+}
+
+void UINetworkSettingsEditor::updateFeatureAvailability()
+{
+ m_pWidgetSettings->setEnabled(m_pCheckboxFeature->isChecked());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkSettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkSettingsEditor.h
new file mode 100644
index 00000000..4e7cc9ee
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UINetworkSettingsEditor.h
@@ -0,0 +1,205 @@
+/* $Id: UINetworkSettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UINetworkSettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UINetworkSettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UINetworkSettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+#include "UIPortForwardingTable.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class UINetworkAttachmentEditor;
+class UINetworkFeaturesEditor;
+
+/** QWidget subclass used as a network settings editor. */
+class SHARED_LIBRARY_STUFF UINetworkSettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** @name Attachment editor stuff
+ * @{ */
+ /** Notifies about feature state changed. */
+ void sigFeatureStateChanged();
+ /** Notifies about attachment type changed. */
+ void sigAttachmentTypeChanged();
+ /** Notifies about alternative name changed. */
+ void sigAlternativeNameChanged();
+ /** @} */
+
+ /** @name Features editor stuff
+ * @{ */
+ /** Notifies about the advanced button state change to @a fExpanded. */
+ void sigAdvancedButtonStateChange(bool fExpanded);
+ /** Notifies about MAC address changed. */
+ void sigMACAddressChanged();
+ /** @} */
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UINetworkSettingsEditor(QWidget *pParent = 0);
+
+ /** @name General stuff
+ * @{ */
+ /** Defines whether feature is @a fEnabled. */
+ void setFeatureEnabled(bool fEnabled);
+ /** Returns whether feature is enabled. */
+ bool isFeatureEnabled() const;
+
+ /** Defines whether feature @a fAvailable. */
+ void setFeatureAvailable(bool fAvailable);
+ /** @} */
+
+ /** @name Attachment editor stuff
+ * @{ */
+ /** Defines value @a enmType. */
+ void setValueType(KNetworkAttachmentType enmType);
+ /** Returns value type. */
+ KNetworkAttachmentType valueType() const;
+
+ /** Defines value @a names for specified @a enmType. */
+ void setValueNames(KNetworkAttachmentType enmType, const QStringList &names);
+ /** Defines value @a strName for specified @a enmType. */
+ void setValueName(KNetworkAttachmentType enmType, const QString &strName);
+ /** Returns current name for specified @a enmType. */
+ QString valueName(KNetworkAttachmentType enmType) const;
+
+ /** Defines whether attachment options @a fAvailable. */
+ void setAttachmentOptionsAvailable(bool fAvailable);
+ /** @} */
+
+ /** @name Features editor stuff
+ * @{ */
+ /** Defines whether advanced button @a fExpanded. */
+ void setAdvancedButtonExpanded(bool fExpanded);
+ /** Returns whether advanced button expanded. */
+ bool advancedButtonExpanded() const;
+
+ /** Defines adapter @a enmType. */
+ void setAdapterType(const KNetworkAdapterType &enmType);
+ /** Returns adapter type. */
+ KNetworkAdapterType adapterType() const;
+
+ /** Defines promiscuous @a enmMode. */
+ void setPromiscuousMode(const KNetworkAdapterPromiscModePolicy &enmMode);
+ /** Returns promiscuous mode. */
+ KNetworkAdapterPromiscModePolicy promiscuousMode() const;
+
+ /** Defines MAC @a strAddress. */
+ void setMACAddress(const QString &strAddress);
+ /** Returns MAC address. */
+ QString macAddress() const;
+
+ /** Defines generic @a strProperties. */
+ void setGenericProperties(const QString &strProperties);
+ /** Returns generic properties. */
+ QString genericProperties() const;
+
+ /** Defines whether cable is @a fConnected. */
+ void setCableConnected(bool fConnected);
+ /** Returns whether cable is connected. */
+ bool cableConnected() const;
+
+ /** Defines list of port forwarding @a rules. */
+ void setPortForwardingRules(const UIPortForwardingDataList &rules);
+ /** Returns list of port forwarding rules. */
+ UIPortForwardingDataList portForwardingRules() const;
+
+ /** Defines whether advanced options @a fAvailable. */
+ void setAdvancedOptionsAvailable(bool fAvailable);
+ /** Defines whether adapter options @a fAvailable. */
+ void setAdapterOptionsAvailable(bool fAvailable);
+ /** Defines whether promiscuous options @a fAvailable. */
+ void setPromiscuousOptionsAvailable(bool fAvailable);
+ /** Defines whether MAC options @a fAvailable. */
+ void setMACOptionsAvailable(bool fAvailable);
+ /** Defines whether generic properties @a fAvailable. */
+ void setGenericPropertiesAvailable(bool fAvailable);
+ /** Defines whether cable options @a fAvailable. */
+ void setCableOptionsAvailable(bool fAvailable);
+ /** Defines whether forwarding options @a fAvailable. */
+ void setForwardingOptionsAvailable(bool fAvailable);
+ /** @} */
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles feature toggling. */
+ void sltHandleFeatureToggled();
+ /** Handles adapter attachment type change. */
+ void sltHandleAttachmentTypeChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Updates feature availability. */
+ void updateFeatureAvailability();
+
+ /** @name Values
+ * @{ */
+ /** Holds whether feature is enabled. */
+ bool m_fFeatureEnabled;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the feature check-box instance. */
+ QCheckBox *m_pCheckboxFeature;
+ /** Holds the settings widget instance. */
+ QWidget *m_pWidgetSettings;
+ /** Holds the network attachment editor instance. */
+ UINetworkAttachmentEditor *m_pEditorNetworkAttachment;
+ /** Holds the network features editor instance. */
+ UINetworkFeaturesEditor *m_pEditorNetworkFeatures;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UINetworkSettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIParavirtProviderEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIParavirtProviderEditor.cpp
new file mode 100644
index 00000000..f8634a76
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIParavirtProviderEditor.cpp
@@ -0,0 +1,169 @@
+/* $Id: UIParavirtProviderEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIParavirtProviderEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIParavirtProviderEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIParavirtProviderEditor::UIParavirtProviderEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KParavirtProvider_Max)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UIParavirtProviderEditor::setValue(KParavirtProvider enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+KParavirtProvider UIParavirtProviderEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<KParavirtProvider>() : m_enmValue;
+}
+
+int UIParavirtProviderEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIParavirtProviderEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIParavirtProviderEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("&Paravirtualization Interface:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const KParavirtProvider enmType = m_pCombo->itemData(i).value<KParavirtProvider>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Selects the paravirtualization guest interface "
+ "provider to be used by this virtual machine."));
+ }
+}
+
+void UIParavirtProviderEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIParavirtProviderEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Load currently supported paravirt provider types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedParavirtProviders();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KParavirtProvider_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const KParavirtProvider &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIParavirtProviderEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIParavirtProviderEditor.h
new file mode 100644
index 00000000..caf57074
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIParavirtProviderEditor.h
@@ -0,0 +1,98 @@
+/* $Id: UIParavirtProviderEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIParavirtProviderEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIParavirtProviderEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIParavirtProviderEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as an paravirtualization provider editor. */
+class SHARED_LIBRARY_STUFF UIParavirtProviderEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIParavirtProviderEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KParavirtProvider enmValue);
+ /** Returns editor value. */
+ KParavirtProvider value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KParavirtProvider> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ KParavirtProvider m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KParavirtProvider> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIParavirtProviderEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIPointingHIDEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIPointingHIDEditor.cpp
new file mode 100644
index 00000000..755b7024
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIPointingHIDEditor.cpp
@@ -0,0 +1,171 @@
+/* $Id: UIPointingHIDEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPointingHIDEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIPointingHIDEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIPointingHIDEditor::UIPointingHIDEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KPointingHIDType_Max)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UIPointingHIDEditor::setValue(KPointingHIDType enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+KPointingHIDType UIPointingHIDEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<KPointingHIDType>() : m_enmValue;
+}
+
+int UIPointingHIDEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIPointingHIDEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIPointingHIDEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("&Pointing Device:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const KPointingHIDType enmType = m_pCombo->itemData(i).value<KPointingHIDType>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Determines whether the emulated pointing device is a standard PS/2 mouse, "
+ "a USB tablet or a USB multi-touch tablet."));
+ }
+}
+
+void UIPointingHIDEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ connect(m_pCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIPointingHIDEditor::sigValueChanged);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIPointingHIDEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Load currently supported values: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedPointingHIDTypes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KPointingHIDType_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const KPointingHIDType &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIPointingHIDEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIPointingHIDEditor.h
new file mode 100644
index 00000000..e4aea5b4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIPointingHIDEditor.h
@@ -0,0 +1,103 @@
+/* $Id: UIPointingHIDEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIPointingHIDEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIPointingHIDEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIPointingHIDEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as a pointing HID editor. */
+class SHARED_LIBRARY_STUFF UIPointingHIDEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value change. */
+ void sigValueChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIPointingHIDEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KPointingHIDType enmValue);
+ /** Returns editor value. */
+ KPointingHIDType value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KPointingHIDType> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ KPointingHIDType m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KPointingHIDType> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIPointingHIDEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProcessorFeaturesEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProcessorFeaturesEditor.cpp
new file mode 100644
index 00000000..f784f291
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProcessorFeaturesEditor.cpp
@@ -0,0 +1,161 @@
+/* $Id: UIProcessorFeaturesEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIProcessorFeaturesEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UIProcessorFeaturesEditor.h"
+
+
+UIProcessorFeaturesEditor::UIProcessorFeaturesEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fEnablePae(false)
+ , m_fEnableNestedVirtualization(false)
+ , m_pLabel(0)
+ , m_pCheckBoxEnablePae(0)
+ , m_pCheckBoxEnableNestedVirtualization(0)
+{
+ prepare();
+}
+
+void UIProcessorFeaturesEditor::setEnablePae(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnablePae != fOn)
+ {
+ m_fEnablePae = fOn;
+ if (m_pCheckBoxEnablePae)
+ m_pCheckBoxEnablePae->setCheckState(m_fEnablePae ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIProcessorFeaturesEditor::isEnabledPae() const
+{
+ return m_pCheckBoxEnablePae
+ ? m_pCheckBoxEnablePae->checkState() == Qt::Checked
+ : m_fEnablePae;
+}
+
+void UIProcessorFeaturesEditor::setEnablePaeAvailable(bool fAvailable)
+{
+ m_pCheckBoxEnablePae->setEnabled(fAvailable);
+}
+
+void UIProcessorFeaturesEditor::setEnableNestedVirtualization(bool fOn)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fEnableNestedVirtualization != fOn)
+ {
+ m_fEnableNestedVirtualization = fOn;
+ if (m_pCheckBoxEnableNestedVirtualization)
+ m_pCheckBoxEnableNestedVirtualization->setCheckState(m_fEnableNestedVirtualization ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+bool UIProcessorFeaturesEditor::isEnabledNestedVirtualization() const
+{
+ return m_pCheckBoxEnableNestedVirtualization
+ ? m_pCheckBoxEnableNestedVirtualization->checkState() == Qt::Checked
+ : m_fEnableNestedVirtualization;
+}
+
+void UIProcessorFeaturesEditor::setEnableNestedVirtualizationAvailable(bool fAvailable)
+{
+ m_pCheckBoxEnableNestedVirtualization->setEnabled(fAvailable);
+}
+
+int UIProcessorFeaturesEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIProcessorFeaturesEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIProcessorFeaturesEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Extended Features:"));
+ if (m_pCheckBoxEnablePae)
+ {
+ m_pCheckBoxEnablePae->setText(tr("Enable PA&E/NX"));
+ m_pCheckBoxEnablePae->setToolTip(tr("When checked, the Physical Address Extension (PAE) feature of the host CPU will be "
+ "exposed to the virtual machine."));
+ }
+ if (m_pCheckBoxEnableNestedVirtualization)
+ {
+ m_pCheckBoxEnableNestedVirtualization->setText(tr("Enable Nested &VT-x/AMD-V"));
+ m_pCheckBoxEnableNestedVirtualization->setToolTip(tr("When checked, the nested hardware virtualization CPU feature will "
+ "be exposed to the virtual machine."));
+ }
+}
+
+void UIProcessorFeaturesEditor::prepare()
+{
+ /* Prepare main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Prepare label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+ /* Prepare 'enable PAE' check-box: */
+ m_pCheckBoxEnablePae = new QCheckBox(this);
+ if (m_pCheckBoxEnablePae)
+ {
+ connect(m_pCheckBoxEnablePae, &QCheckBox::stateChanged,
+ this, &UIProcessorFeaturesEditor::sigChangedPae);
+ m_pLayout->addWidget(m_pCheckBoxEnablePae, 0, 1);
+ }
+ /* Prepare 'enable nested virtualization' check-box: */
+ m_pCheckBoxEnableNestedVirtualization = new QCheckBox(this);
+ if (m_pCheckBoxEnableNestedVirtualization)
+ {
+ connect(m_pCheckBoxEnableNestedVirtualization, &QCheckBox::stateChanged,
+ this, &UIProcessorFeaturesEditor::sigChangedNestedVirtualization);
+ m_pLayout->addWidget(m_pCheckBoxEnableNestedVirtualization, 1, 1);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProcessorFeaturesEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProcessorFeaturesEditor.h
new file mode 100644
index 00000000..1aa8b0e2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProcessorFeaturesEditor.h
@@ -0,0 +1,109 @@
+/* $Id: UIProcessorFeaturesEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIProcessorFeaturesEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIProcessorFeaturesEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIProcessorFeaturesEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as processor features editor. */
+class SHARED_LIBRARY_STUFF UIProcessorFeaturesEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about PAE change. */
+ void sigChangedPae();
+ /** Notifies listeners about nested virtualization change. */
+ void sigChangedNestedVirtualization();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIProcessorFeaturesEditor(QWidget *pParent = 0);
+
+ /** Defines whether 'enable PAE' feature in @a fOn. */
+ void setEnablePae(bool fOn);
+ /** Returns 'enable PAE' feature value. */
+ bool isEnabledPae() const;
+ /** Defines whether 'enable PAE' option @a fAvailable. */
+ void setEnablePaeAvailable(bool fAvailable);
+
+ /** Defines whether 'enable nested virtualization' feature in @a fOn. */
+ void setEnableNestedVirtualization(bool fOn);
+ /** Returns 'enable nested virtualization' feature value. */
+ bool isEnabledNestedVirtualization() const;
+ /** Defines whether 'enable nested virtualization' option @a fAvailable. */
+ void setEnableNestedVirtualizationAvailable(bool fAvailable);
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Values
+ * @{ */
+ /** Holds the 'enable PAE' feature value. */
+ bool m_fEnablePae;
+ /** Holds the 'enable nested virtualization' feature value. */
+ bool m_fEnableNestedVirtualization;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the 'enable PAE' check-box instance. */
+ QCheckBox *m_pCheckBoxEnablePae;
+ /** Holds the 'enable nested virtualization' check-box instance. */
+ QCheckBox *m_pCheckBoxEnableNestedVirtualization;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIProcessorFeaturesEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProxyFeaturesEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProxyFeaturesEditor.cpp
new file mode 100644
index 00000000..e62fcdc0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProxyFeaturesEditor.cpp
@@ -0,0 +1,236 @@
+/* $Id: UIProxyFeaturesEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIProxyFeaturesEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QGridLayout>
+#include <QLabel>
+#include <QRadioButton>
+#include <QRegularExpressionValidator>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "UIProxyFeaturesEditor.h"
+
+
+UIProxyFeaturesEditor::UIProxyFeaturesEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmProxyMode(KProxyMode_Max)
+ , m_pButtonGroup(0)
+ , m_pRadioButtonProxyAuto(0)
+ , m_pRadioButtonProxyDisabled(0)
+ , m_pRadioButtonProxyEnabled(0)
+ , m_pWidgetSettings(0)
+ , m_pLabelHost(0)
+ , m_pEditorHost(0)
+{
+ prepare();
+}
+
+void UIProxyFeaturesEditor::setProxyMode(KProxyMode enmMode)
+{
+ /* Update cached value and
+ * radio-buttons if value has changed: */
+ if (m_enmProxyMode != enmMode)
+ {
+ m_enmProxyMode = enmMode;
+ switch (m_enmProxyMode)
+ {
+ case KProxyMode_System:
+ if (m_pRadioButtonProxyAuto)
+ m_pRadioButtonProxyAuto->setChecked(true);
+ break;
+ case KProxyMode_NoProxy:
+ if (m_pRadioButtonProxyDisabled)
+ m_pRadioButtonProxyDisabled->setChecked(true);
+ break;
+ case KProxyMode_Manual:
+ if (m_pRadioButtonProxyEnabled)
+ m_pRadioButtonProxyEnabled->setChecked(true);
+ break;
+ case KProxyMode_Max:
+ break;
+ }
+ }
+
+ /* Update widgets availability: */
+ sltHandleProxyModeChanged();
+}
+
+KProxyMode UIProxyFeaturesEditor::proxyMode() const
+{
+ return m_pRadioButtonProxyEnabled && m_pRadioButtonProxyEnabled->isChecked()
+ ? KProxyMode_Manual
+ : m_pRadioButtonProxyDisabled && m_pRadioButtonProxyDisabled->isChecked()
+ ? KProxyMode_NoProxy
+ : m_pRadioButtonProxyAuto && m_pRadioButtonProxyAuto->isChecked()
+ ? KProxyMode_System
+ : m_enmProxyMode;
+}
+
+void UIProxyFeaturesEditor::setProxyHost(const QString &strHost)
+{
+ /* Update cached value and
+ * line-edit if value has changed: */
+ if (m_strProxyHost != strHost)
+ {
+ m_strProxyHost = strHost;
+ if (m_pEditorHost)
+ m_pEditorHost->setText(m_strProxyHost);
+ }
+}
+
+QString UIProxyFeaturesEditor::proxyHost() const
+{
+ return m_pEditorHost ? m_pEditorHost->text() : m_strProxyHost;
+}
+
+void UIProxyFeaturesEditor::retranslateUi()
+{
+ /* Translate proxy mode editor: */
+ if (m_pRadioButtonProxyAuto)
+ {
+ m_pRadioButtonProxyAuto->setText(tr("&Auto-detect Host Proxy Settings"));
+ m_pRadioButtonProxyAuto->setToolTip(tr("When chosen, VirtualBox will try to auto-detect host proxy settings for tasks "
+ "like downloading Guest Additions from the network or checking for updates."));
+ }
+ if (m_pRadioButtonProxyDisabled)
+ {
+ m_pRadioButtonProxyDisabled->setText(tr("&Direct Connection to the Internet"));
+ m_pRadioButtonProxyDisabled->setToolTip(tr("When chosen, VirtualBox will use direct Internet connection for tasks like "
+ "downloading Guest Additions from the network or checking for updates."));
+ }
+ if (m_pRadioButtonProxyEnabled)
+ {
+ m_pRadioButtonProxyEnabled->setText(tr("&Manual Proxy Configuration"));
+ m_pRadioButtonProxyEnabled->setToolTip(tr("When chosen, VirtualBox will use the proxy settings supplied for tasks like "
+ "downloading Guest Additions from the network or checking for updates."));
+ }
+
+ /* Translate proxy host editor: */
+ if (m_pLabelHost)
+ m_pLabelHost->setText(tr("&URL:"));
+ if (m_pEditorHost)
+ m_pEditorHost->setToolTip(tr("Holds the proxy URL. "
+ "The format is: "
+ "<table cellspacing=0 style='white-space:pre'>"
+ "<tr><td>[{type}://][{userid}[:{password}]@]{server}[:{port}]</td></tr>"
+ "<tr><td>http://username:password@proxy.host.com:port</td></tr>"
+ "</table>"));
+}
+
+void UIProxyFeaturesEditor::sltHandleProxyModeChanged()
+{
+ /* Update widgets availability: */
+ m_pWidgetSettings->setEnabled(m_pRadioButtonProxyEnabled->isChecked());
+
+ /* Notify listeners: */
+ emit sigProxyModeChanged();
+}
+
+void UIProxyFeaturesEditor::prepare()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare button-group: */
+ m_pButtonGroup = new QButtonGroup(this);
+ if (m_pButtonGroup)
+ {
+ /* Prepare 'proxy auto' button: */
+ m_pRadioButtonProxyAuto = new QRadioButton(this);
+ if (m_pRadioButtonProxyAuto)
+ {
+ m_pButtonGroup->addButton(m_pRadioButtonProxyAuto);
+ pLayout->addWidget(m_pRadioButtonProxyAuto, 0, 0, 1, 2);
+ }
+ /* Prepare 'proxy disabled' button: */
+ m_pRadioButtonProxyDisabled = new QRadioButton(this);
+ if (m_pRadioButtonProxyDisabled)
+ {
+ m_pButtonGroup->addButton(m_pRadioButtonProxyDisabled);
+ pLayout->addWidget(m_pRadioButtonProxyDisabled, 1, 0, 1, 2);
+ }
+ /* Prepare 'proxy enabled' button: */
+ m_pRadioButtonProxyEnabled = new QRadioButton(this);
+ if (m_pRadioButtonProxyEnabled)
+ {
+ m_pButtonGroup->addButton(m_pRadioButtonProxyEnabled);
+ pLayout->addWidget(m_pRadioButtonProxyEnabled, 2, 0, 1, 2);
+ }
+ }
+
+ /* Prepare 20-px shifting spacer: */
+ QSpacerItem *pSpacerItem = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacerItem)
+ pLayout->addItem(pSpacerItem, 3, 0);
+
+ /* Prepare settings widget: */
+ m_pWidgetSettings = new QWidget(this);
+ if (m_pWidgetSettings)
+ {
+ /* Prepare settings layout widget: */
+ QHBoxLayout *pLayoutSettings = new QHBoxLayout(m_pWidgetSettings);
+ if (pLayoutSettings)
+ {
+ pLayoutSettings->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare host label: */
+ m_pLabelHost = new QLabel(m_pWidgetSettings);
+ if (m_pLabelHost)
+ {
+ m_pLabelHost->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutSettings->addWidget(m_pLabelHost);
+ }
+ /* Prepare host editor: */
+ m_pEditorHost = new QILineEdit(m_pWidgetSettings);
+ if (m_pEditorHost)
+ {
+ if (m_pLabelHost)
+ m_pLabelHost->setBuddy(m_pEditorHost);
+ m_pEditorHost->setValidator(new QRegularExpressionValidator(QRegularExpression("\\S+"), m_pEditorHost));
+
+ pLayoutSettings->addWidget(m_pEditorHost);
+ }
+ }
+
+ pLayout->addWidget(m_pWidgetSettings, 3, 1);
+ }
+ }
+
+ /* Prepare connections: */
+ connect(m_pButtonGroup, static_cast<void(QButtonGroup::*)(QAbstractButton*)>(&QButtonGroup::buttonClicked),
+ this, &UIProxyFeaturesEditor::sltHandleProxyModeChanged);
+ connect(m_pEditorHost, &QILineEdit::textEdited,
+ this, &UIProxyFeaturesEditor::sigProxyHostChanged);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProxyFeaturesEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProxyFeaturesEditor.h
new file mode 100644
index 00000000..a96edd28
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIProxyFeaturesEditor.h
@@ -0,0 +1,115 @@
+/* $Id: UIProxyFeaturesEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIProxyFeaturesEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIProxyFeaturesEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIProxyFeaturesEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QButtonGroup;
+class QLabel;
+class QRadioButton;
+class QILineEdit;
+
+/** QWidget subclass used as global proxy features editor. */
+class SHARED_LIBRARY_STUFF UIProxyFeaturesEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about proxy mode changed. */
+ void sigProxyModeChanged();
+ /** Notifies listeners about proxy host changed. */
+ void sigProxyHostChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIProxyFeaturesEditor(QWidget *pParent = 0);
+
+ /** Defines proxy @a enmMode. */
+ void setProxyMode(KProxyMode enmMode);
+ /** Returns proxy mode. */
+ KProxyMode proxyMode() const;
+
+ /** Defines proxy @a strHost. */
+ void setProxyHost(const QString &strHost);
+ /** Returns proxy host. */
+ QString proxyHost() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles proxy mode change. */
+ void sltHandleProxyModeChanged();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Values
+ * @{ */
+ /** Holds the proxy mode. */
+ KProxyMode m_enmProxyMode;
+ /** Holds the proxy host. */
+ QString m_strProxyHost;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the button-group instance. */
+ QButtonGroup *m_pButtonGroup;
+ /** Holds the 'proxy auto' radio-button instance. */
+ QRadioButton *m_pRadioButtonProxyAuto;
+ /** Holds the 'proxy disabled' radio-button instance. */
+ QRadioButton *m_pRadioButtonProxyDisabled;
+ /** Holds the 'proxy enabled' radio-button instance. */
+ QRadioButton *m_pRadioButtonProxyEnabled;
+ /** Holds the settings widget instance. */
+ QWidget *m_pWidgetSettings;
+ /** Holds the host label instance. */
+ QLabel *m_pLabelHost;
+ /** Holds the host editor instance. */
+ QILineEdit *m_pEditorHost;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIProxyFeaturesEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIRecordingSettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIRecordingSettingsEditor.cpp
new file mode 100644
index 00000000..5a9db322
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIRecordingSettingsEditor.cpp
@@ -0,0 +1,971 @@
+/* $Id: UIRecordingSettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIRecordingSettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QHBoxLayout>
+#include <QGridLayout>
+#include <QLabel>
+#include <QSpinBox>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIFilePathSelector.h"
+#include "UIFilmContainer.h"
+#include "UIRecordingSettingsEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+/* Defines: */
+#define VIDEO_CAPTURE_BIT_RATE_MIN 32
+#define VIDEO_CAPTURE_BIT_RATE_MAX 2048
+
+
+UIRecordingSettingsEditor::UIRecordingSettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fFeatureEnabled(false)
+ , m_fOptionsAvailable(false)
+ , m_fScreenOptionsAvailable(false)
+ , m_enmMode(UISettingsDefs::RecordingMode_Max)
+ , m_iFrameWidth(0)
+ , m_iFrameHeight(0)
+ , m_iFrameRate(0)
+ , m_iBitRate(0)
+ , m_iAudioQualityRate(0)
+ , m_pCheckboxFeature(0)
+ , m_pLabelMode(0)
+ , m_pComboMode(0)
+ , m_pLabelFilePath(0)
+ , m_pEditorFilePath(0)
+ , m_pLabelFrameSize(0)
+ , m_pComboFrameSize(0)
+ , m_pSpinboxFrameWidth(0)
+ , m_pSpinboxFrameHeight(0)
+ , m_pLabelFrameRate(0)
+ , m_pWidgetFrameRateSettings(0)
+ , m_pSliderFrameRate(0)
+ , m_pSpinboxFrameRate(0)
+ , m_pLabelFrameRateMin(0)
+ , m_pLabelFrameRateMax(0)
+ , m_pLabelVideoQuality(0)
+ , m_pWidgetVideoQualitySettings(0)
+ , m_pSliderVideoQuality(0)
+ , m_pSpinboxVideoQuality(0)
+ , m_pLabelVideoQualityMin(0)
+ , m_pLabelVideoQualityMed(0)
+ , m_pLabelVideoQualityMax(0)
+ , m_pLabelAudioQuality(0)
+ , m_pWidgetAudioQualitySettings(0)
+ , m_pSliderAudioQuality(0)
+ , m_pLabelAudioQualityMin(0)
+ , m_pLabelAudioQualityMed(0)
+ , m_pLabelAudioQualityMax(0)
+ , m_pLabelSizeHint(0)
+ , m_pLabelScreens(0)
+ , m_pScrollerScreens(0)
+{
+ prepare();
+}
+
+void UIRecordingSettingsEditor::setFeatureEnabled(bool fEnabled)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fFeatureEnabled != fEnabled)
+ {
+ m_fFeatureEnabled = fEnabled;
+ if (m_pCheckboxFeature)
+ {
+ m_pCheckboxFeature->setChecked(m_fFeatureEnabled);
+ sltHandleFeatureToggled();
+ }
+ }
+}
+
+bool UIRecordingSettingsEditor::isFeatureEnabled() const
+{
+ return m_pCheckboxFeature ? m_pCheckboxFeature->isChecked() : m_fFeatureEnabled;
+}
+
+void UIRecordingSettingsEditor::setOptionsAvailable(bool fAvailable)
+{
+ /* Update cached value and
+ * widget availability if value has changed: */
+ if (m_fOptionsAvailable != fAvailable)
+ {
+ m_fOptionsAvailable = fAvailable;
+ updateWidgetAvailability();
+ }
+}
+
+void UIRecordingSettingsEditor::setScreenOptionsAvailable(bool fAvailable)
+{
+ /* Update cached value and
+ * widget availability if value has changed: */
+ if (m_fScreenOptionsAvailable != fAvailable)
+ {
+ m_fScreenOptionsAvailable = fAvailable;
+ updateWidgetAvailability();
+ }
+}
+
+void UIRecordingSettingsEditor::setMode(UISettingsDefs::RecordingMode enmMode)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmMode != enmMode)
+ {
+ m_enmMode = enmMode;
+ populateComboMode();
+ updateWidgetVisibility();
+ }
+}
+
+UISettingsDefs::RecordingMode UIRecordingSettingsEditor::mode() const
+{
+ return m_pComboMode ? m_pComboMode->currentData().value<UISettingsDefs::RecordingMode>() : m_enmMode;
+}
+
+void UIRecordingSettingsEditor::setFolder(const QString &strFolder)
+{
+ /* Update cached value and
+ * file editor if value has changed: */
+ if (m_strFolder != strFolder)
+ {
+ m_strFolder = strFolder;
+ if (m_pEditorFilePath)
+ m_pEditorFilePath->setInitialPath(m_strFolder);
+ }
+}
+
+QString UIRecordingSettingsEditor::folder() const
+{
+ return m_pEditorFilePath ? m_pEditorFilePath->initialPath() : m_strFolder;
+}
+
+void UIRecordingSettingsEditor::setFilePath(const QString &strFilePath)
+{
+ /* Update cached value and
+ * file editor if value has changed: */
+ if (m_strFilePath != strFilePath)
+ {
+ m_strFilePath = strFilePath;
+ if (m_pEditorFilePath)
+ m_pEditorFilePath->setPath(m_strFilePath);
+ }
+}
+
+QString UIRecordingSettingsEditor::filePath() const
+{
+ return m_pEditorFilePath ? m_pEditorFilePath->path() : m_strFilePath;
+}
+
+void UIRecordingSettingsEditor::setFrameWidth(int iWidth)
+{
+ /* Update cached value and
+ * spin-box if value has changed: */
+ if (m_iFrameWidth != iWidth)
+ {
+ m_iFrameWidth = iWidth;
+ if (m_pSpinboxFrameWidth)
+ m_pSpinboxFrameWidth->setValue(m_iFrameWidth);
+ }
+}
+
+int UIRecordingSettingsEditor::frameWidth() const
+{
+ return m_pSpinboxFrameWidth ? m_pSpinboxFrameWidth->value() : m_iFrameWidth;
+}
+
+void UIRecordingSettingsEditor::setFrameHeight(int iHeight)
+{
+ /* Update cached value and
+ * spin-box if value has changed: */
+ if (m_iFrameHeight != iHeight)
+ {
+ m_iFrameHeight = iHeight;
+ if (m_pSpinboxFrameHeight)
+ m_pSpinboxFrameHeight->setValue(m_iFrameHeight);
+ }
+}
+
+int UIRecordingSettingsEditor::frameHeight() const
+{
+ return m_pSpinboxFrameHeight ? m_pSpinboxFrameHeight->value() : m_iFrameHeight;
+}
+
+void UIRecordingSettingsEditor::setFrameRate(int iRate)
+{
+ /* Update cached value and
+ * spin-box if value has changed: */
+ if (m_iFrameRate != iRate)
+ {
+ m_iFrameRate = iRate;
+ if (m_pSpinboxFrameRate)
+ m_pSpinboxFrameRate->setValue(m_iFrameRate);
+ }
+}
+
+int UIRecordingSettingsEditor::frameRate() const
+{
+ return m_pSpinboxFrameRate ? m_pSpinboxFrameRate->value() : m_iFrameRate;
+}
+
+void UIRecordingSettingsEditor::setBitRate(int iRate)
+{
+ /* Update cached value and
+ * spin-box if value has changed: */
+ if (m_iBitRate != iRate)
+ {
+ m_iBitRate = iRate;
+ if (m_pSpinboxVideoQuality)
+ m_pSpinboxVideoQuality->setValue(m_iBitRate);
+ }
+}
+
+int UIRecordingSettingsEditor::bitRate() const
+{
+ return m_pSpinboxVideoQuality ? m_pSpinboxVideoQuality->value() : m_iBitRate;
+}
+
+void UIRecordingSettingsEditor::setAudioQualityRate(int iRate)
+{
+ /* Update cached value and
+ * slider if value has changed: */
+ if (m_iAudioQualityRate != iRate)
+ {
+ m_iAudioQualityRate = iRate;
+ if (m_pSliderAudioQuality)
+ m_pSliderAudioQuality->setValue(m_iAudioQualityRate);
+ }
+}
+
+int UIRecordingSettingsEditor::audioQualityRate() const
+{
+ return m_pSliderAudioQuality ? m_pSliderAudioQuality->value() : m_iAudioQualityRate;
+}
+
+void UIRecordingSettingsEditor::setScreens(const QVector<BOOL> &screens)
+{
+ /* Update cached value and
+ * editor if value has changed: */
+ if (m_screens != screens)
+ {
+ m_screens = screens;
+ if (m_pScrollerScreens)
+ m_pScrollerScreens->setValue(m_screens);
+ }
+}
+
+QVector<BOOL> UIRecordingSettingsEditor::screens() const
+{
+ return m_pScrollerScreens ? m_pScrollerScreens->value() : m_screens;
+}
+
+void UIRecordingSettingsEditor::retranslateUi()
+{
+ m_pCheckboxFeature->setText(tr("&Enable Recording"));
+ m_pCheckboxFeature->setToolTip(tr("When checked, VirtualBox will record the virtual machine session as a video file."));
+
+ m_pLabelMode->setText(tr("Recording &Mode:"));
+ for (int iIndex = 0; iIndex < m_pComboMode->count(); ++iIndex)
+ {
+ const UISettingsDefs::RecordingMode enmType =
+ m_pComboMode->itemData(iIndex).value<UISettingsDefs::RecordingMode>();
+ m_pComboMode->setItemText(iIndex, gpConverter->toString(enmType));
+ }
+ m_pComboMode->setToolTip(tr("Holds the recording mode."));
+
+ m_pLabelFilePath->setText(tr("File &Path:"));
+ m_pEditorFilePath->setToolTip(tr("Holds the filename VirtualBox uses to save the recorded content."));
+
+ m_pLabelFrameSize->setText(tr("Frame Si&ze:"));
+ m_pComboFrameSize->setItemText(0, tr("User Defined"));
+ m_pComboFrameSize->setToolTip(tr("Holds the resolution (frame size) of the recorded video."));
+ m_pSpinboxFrameWidth->setToolTip(tr("Holds the horizontal resolution (frame width) of the recorded video."));
+ m_pSpinboxFrameHeight->setToolTip(tr("Holds the vertical resolution (frame height) of the recorded video."));
+
+ m_pLabelFrameRate->setText(tr("Frame R&ate:"));
+ m_pSliderFrameRate->setToolTip(tr("Holds the maximum number of frames per second. Additional frames "
+ "will be skipped. Reducing this value will increase the number of skipped "
+ "frames and reduce the file size."));
+ m_pSpinboxFrameRate->setSuffix(QString(" %1").arg(tr("fps")));
+ m_pSpinboxFrameRate->setToolTip(tr("Holds the maximum number of frames per second. Additional frames "
+ "will be skipped. Reducing this value will increase the number of skipped "
+ "frames and reduce the file size."));
+ m_pLabelFrameRateMin->setText(tr("%1 fps").arg(m_pSliderFrameRate->minimum()));
+ m_pLabelFrameRateMin->setToolTip(tr("Minimum possible frame rate."));
+ m_pLabelFrameRateMax->setText(tr("%1 fps").arg(m_pSliderFrameRate->maximum()));
+ m_pLabelFrameRateMax->setToolTip(tr("Maximum possible frame rate."));
+
+ m_pLabelVideoQuality->setText(tr("&Video Quality:"));
+ m_pSliderVideoQuality->setToolTip(tr("Holds the quality. Increasing this value will make the video "
+ "look better at the cost of an increased file size."));
+ m_pSpinboxVideoQuality->setSuffix(QString(" %1").arg(tr("kbps")));
+ m_pSpinboxVideoQuality->setToolTip(tr("Holds the bitrate in kilobits per second. Increasing this value "
+ "will make the video look better at the cost of an increased file size."));
+ m_pLabelVideoQualityMin->setText(tr("low", "quality"));
+ m_pLabelVideoQualityMed->setText(tr("medium", "quality"));
+ m_pLabelVideoQualityMax->setText(tr("high", "quality"));
+
+ m_pLabelAudioQuality->setText(tr("&Audio Quality:"));
+ m_pSliderAudioQuality->setToolTip(tr("Holds the quality. Increasing this value will make the audio "
+ "sound better at the cost of an increased file size."));
+ m_pLabelAudioQualityMin->setText(tr("low", "quality"));
+ m_pLabelAudioQualityMed->setText(tr("medium", "quality"));
+ m_pLabelAudioQualityMax->setText(tr("high", "quality"));
+
+ m_pLabelScreens->setText(tr("Scree&ns:"));
+
+ updateRecordingFileSizeHint();
+}
+
+void UIRecordingSettingsEditor::sltHandleFeatureToggled()
+{
+ /* Update widget availability: */
+ updateWidgetAvailability();
+}
+
+void UIRecordingSettingsEditor::sltHandleModeComboChange()
+{
+ /* Update widget availability: */
+ updateWidgetAvailability();
+}
+
+void UIRecordingSettingsEditor::sltHandleVideoFrameSizeComboChange()
+{
+ /* Get the proposed size: */
+ const int iCurrentIndex = m_pComboFrameSize->currentIndex();
+ const QSize videoCaptureSize = m_pComboFrameSize->itemData(iCurrentIndex).toSize();
+
+ /* Make sure its valid: */
+ if (!videoCaptureSize.isValid())
+ return;
+
+ /* Apply proposed size: */
+ m_pSpinboxFrameWidth->setValue(videoCaptureSize.width());
+ m_pSpinboxFrameHeight->setValue(videoCaptureSize.height());
+}
+
+void UIRecordingSettingsEditor::sltHandleVideoFrameWidthChange()
+{
+ /* Look for preset: */
+ lookForCorrespondingFrameSizePreset();
+ /* Update quality and bit-rate: */
+ sltHandleVideoBitRateSliderChange();
+}
+
+void UIRecordingSettingsEditor::sltHandleVideoFrameHeightChange()
+{
+ /* Look for preset: */
+ lookForCorrespondingFrameSizePreset();
+ /* Update quality and bit-rate: */
+ sltHandleVideoBitRateSliderChange();
+}
+
+void UIRecordingSettingsEditor::sltHandleVideoFrameRateSliderChange()
+{
+ /* Apply proposed frame-rate: */
+ m_pSpinboxFrameRate->blockSignals(true);
+ m_pSpinboxFrameRate->setValue(m_pSliderFrameRate->value());
+ m_pSpinboxFrameRate->blockSignals(false);
+ /* Update quality and bit-rate: */
+ sltHandleVideoBitRateSliderChange();
+}
+
+void UIRecordingSettingsEditor::sltHandleVideoFrameRateSpinboxChange()
+{
+ /* Apply proposed frame-rate: */
+ m_pSliderFrameRate->blockSignals(true);
+ m_pSliderFrameRate->setValue(m_pSpinboxFrameRate->value());
+ m_pSliderFrameRate->blockSignals(false);
+ /* Update quality and bit-rate: */
+ sltHandleVideoBitRateSliderChange();
+}
+
+void UIRecordingSettingsEditor::sltHandleVideoBitRateSliderChange()
+{
+ /* Calculate/apply proposed bit-rate: */
+ m_pSpinboxVideoQuality->blockSignals(true);
+ m_pSpinboxVideoQuality->setValue(calculateBitRate(m_pSpinboxFrameWidth->value(),
+ m_pSpinboxFrameHeight->value(),
+ m_pSpinboxFrameRate->value(),
+ m_pSliderVideoQuality->value()));
+ m_pSpinboxVideoQuality->blockSignals(false);
+ updateRecordingFileSizeHint();
+}
+
+void UIRecordingSettingsEditor::sltHandleVideoBitRateSpinboxChange()
+{
+ /* Calculate/apply proposed quality: */
+ m_pSliderVideoQuality->blockSignals(true);
+ m_pSliderVideoQuality->setValue(calculateQuality(m_pSpinboxFrameWidth->value(),
+ m_pSpinboxFrameHeight->value(),
+ m_pSpinboxFrameRate->value(),
+ m_pSpinboxVideoQuality->value()));
+ m_pSliderVideoQuality->blockSignals(false);
+ updateRecordingFileSizeHint();
+}
+
+void UIRecordingSettingsEditor::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIRecordingSettingsEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ pLayout->setColumnStretch(1, 1);
+
+ /* Prepare 'feature' check-box: */
+ m_pCheckboxFeature = new QCheckBox(this);
+ if (m_pCheckboxFeature)
+ {
+ // this name is used from outside, have a look at UIMachineLogic..
+ m_pCheckboxFeature->setObjectName("m_pCheckboxVideoCapture");
+ pLayout->addWidget(m_pCheckboxFeature, 0, 0, 1, 2);
+ }
+
+ /* Prepare 20-px shifting spacer: */
+ QSpacerItem *pSpacerItem = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacerItem)
+ pLayout->addItem(pSpacerItem, 1, 0);
+
+ /* Prepare 'settings' widget: */
+ QWidget *pWidgetSettings = new QWidget(this);
+ if (pWidgetSettings)
+ {
+ /* Prepare recording settings widget layout: */
+ QGridLayout *pLayoutSettings = new QGridLayout(pWidgetSettings);
+ if (pLayoutSettings)
+ {
+ pLayoutSettings->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare recording mode label: */
+ m_pLabelMode = new QLabel(pWidgetSettings);
+ if (m_pLabelMode)
+ {
+ m_pLabelMode->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutSettings->addWidget(m_pLabelMode, 0, 0);
+ }
+ /* Prepare recording mode combo: */
+ m_pComboMode = new QComboBox(pWidgetSettings);
+ if (m_pComboMode)
+ {
+ if (m_pLabelMode)
+ m_pLabelMode->setBuddy(m_pComboMode);
+ m_pComboMode->addItem(QString(), QVariant::fromValue(UISettingsDefs::RecordingMode_VideoAudio));
+ m_pComboMode->addItem(QString(), QVariant::fromValue(UISettingsDefs::RecordingMode_VideoOnly));
+ m_pComboMode->addItem(QString(), QVariant::fromValue(UISettingsDefs::RecordingMode_AudioOnly));
+
+ pLayoutSettings->addWidget(m_pComboMode, 0, 1, 1, 3);
+ }
+
+ /* Prepare recording file path label: */
+ m_pLabelFilePath = new QLabel(pWidgetSettings);
+ if (m_pLabelFilePath)
+ {
+ m_pLabelFilePath->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutSettings->addWidget(m_pLabelFilePath, 1, 0);
+ }
+ /* Prepare recording file path editor: */
+ m_pEditorFilePath = new UIFilePathSelector(pWidgetSettings);
+ if (m_pEditorFilePath)
+ {
+ if (m_pLabelFilePath)
+ m_pLabelFilePath->setBuddy(m_pEditorFilePath->focusProxy());
+ m_pEditorFilePath->setEditable(false);
+ m_pEditorFilePath->setMode(UIFilePathSelector::Mode_File_Save);
+
+ pLayoutSettings->addWidget(m_pEditorFilePath, 1, 1, 1, 3);
+ }
+
+ /* Prepare recording frame size label: */
+ m_pLabelFrameSize = new QLabel(pWidgetSettings);
+ if (m_pLabelFrameSize)
+ {
+ m_pLabelFrameSize->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutSettings->addWidget(m_pLabelFrameSize, 2, 0);
+ }
+ /* Prepare recording frame size combo: */
+ m_pComboFrameSize = new QComboBox(pWidgetSettings);
+ if (m_pComboFrameSize)
+ {
+ if (m_pLabelFrameSize)
+ m_pLabelFrameSize->setBuddy(m_pComboFrameSize);
+ m_pComboFrameSize->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed));
+ m_pComboFrameSize->addItem(""); /* User Defined */
+ m_pComboFrameSize->addItem("320 x 200 (16:10)", QSize(320, 200));
+ m_pComboFrameSize->addItem("640 x 480 (4:3)", QSize(640, 480));
+ m_pComboFrameSize->addItem("720 x 400 (9:5)", QSize(720, 400));
+ m_pComboFrameSize->addItem("720 x 480 (3:2)", QSize(720, 480));
+ m_pComboFrameSize->addItem("800 x 600 (4:3)", QSize(800, 600));
+ m_pComboFrameSize->addItem("1024 x 768 (4:3)", QSize(1024, 768));
+ m_pComboFrameSize->addItem("1152 x 864 (4:3)", QSize(1152, 864));
+ m_pComboFrameSize->addItem("1280 x 720 (16:9)", QSize(1280, 720));
+ m_pComboFrameSize->addItem("1280 x 800 (16:10)", QSize(1280, 800));
+ m_pComboFrameSize->addItem("1280 x 960 (4:3)", QSize(1280, 960));
+ m_pComboFrameSize->addItem("1280 x 1024 (5:4)", QSize(1280, 1024));
+ m_pComboFrameSize->addItem("1366 x 768 (16:9)", QSize(1366, 768));
+ m_pComboFrameSize->addItem("1440 x 900 (16:10)", QSize(1440, 900));
+ m_pComboFrameSize->addItem("1440 x 1080 (4:3)", QSize(1440, 1080));
+ m_pComboFrameSize->addItem("1600 x 900 (16:9)", QSize(1600, 900));
+ m_pComboFrameSize->addItem("1680 x 1050 (16:10)", QSize(1680, 1050));
+ m_pComboFrameSize->addItem("1600 x 1200 (4:3)", QSize(1600, 1200));
+ m_pComboFrameSize->addItem("1920 x 1080 (16:9)", QSize(1920, 1080));
+ m_pComboFrameSize->addItem("1920 x 1200 (16:10)", QSize(1920, 1200));
+ m_pComboFrameSize->addItem("1920 x 1440 (4:3)", QSize(1920, 1440));
+ m_pComboFrameSize->addItem("2880 x 1800 (16:10)", QSize(2880, 1800));
+
+ pLayoutSettings->addWidget(m_pComboFrameSize, 2, 1);
+ }
+ /* Prepare recording frame width spinbox: */
+ m_pSpinboxFrameWidth = new QSpinBox(pWidgetSettings);
+ if (m_pSpinboxFrameWidth)
+ {
+ uiCommon().setMinimumWidthAccordingSymbolCount(m_pSpinboxFrameWidth, 5);
+ m_pSpinboxFrameWidth->setMinimum(16);
+ m_pSpinboxFrameWidth->setMaximum(2880);
+
+ pLayoutSettings->addWidget(m_pSpinboxFrameWidth, 2, 2);
+ }
+ /* Prepare recording frame height spinbox: */
+ m_pSpinboxFrameHeight = new QSpinBox(pWidgetSettings);
+ if (m_pSpinboxFrameHeight)
+ {
+ uiCommon().setMinimumWidthAccordingSymbolCount(m_pSpinboxFrameHeight, 5);
+ m_pSpinboxFrameHeight->setMinimum(16);
+ m_pSpinboxFrameHeight->setMaximum(1800);
+
+ pLayoutSettings->addWidget(m_pSpinboxFrameHeight, 2, 3);
+ }
+
+ /* Prepare recording frame rate label: */
+ m_pLabelFrameRate = new QLabel(pWidgetSettings);
+ if (m_pLabelFrameRate)
+ {
+ m_pLabelFrameRate->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutSettings->addWidget(m_pLabelFrameRate, 3, 0);
+ }
+ /* Prepare recording frame rate widget: */
+ m_pWidgetFrameRateSettings = new QWidget(pWidgetSettings);
+ if (m_pWidgetFrameRateSettings)
+ {
+ /* Prepare recording frame rate layout: */
+ QVBoxLayout *pLayoutRecordingFrameRate = new QVBoxLayout(m_pWidgetFrameRateSettings);
+ if (pLayoutRecordingFrameRate)
+ {
+ pLayoutRecordingFrameRate->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare recording frame rate slider: */
+ m_pSliderFrameRate = new QIAdvancedSlider(m_pWidgetFrameRateSettings);
+ if (m_pSliderFrameRate)
+ {
+ m_pSliderFrameRate->setOrientation(Qt::Horizontal);
+ m_pSliderFrameRate->setMinimum(1);
+ m_pSliderFrameRate->setMaximum(30);
+ m_pSliderFrameRate->setPageStep(1);
+ m_pSliderFrameRate->setSingleStep(1);
+ m_pSliderFrameRate->setTickInterval(1);
+ m_pSliderFrameRate->setSnappingEnabled(true);
+ m_pSliderFrameRate->setOptimalHint(1, 25);
+ m_pSliderFrameRate->setWarningHint(25, 30);
+
+ pLayoutRecordingFrameRate->addWidget(m_pSliderFrameRate);
+ }
+ /* Prepare recording frame rate scale layout: */
+ QHBoxLayout *pLayoutRecordingFrameRateScale = new QHBoxLayout;
+ if (pLayoutRecordingFrameRateScale)
+ {
+ pLayoutRecordingFrameRateScale->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare recording frame rate min label: */
+ m_pLabelFrameRateMin = new QLabel(m_pWidgetFrameRateSettings);
+ if (m_pLabelFrameRateMin)
+ pLayoutRecordingFrameRateScale->addWidget(m_pLabelFrameRateMin);
+ pLayoutRecordingFrameRateScale->addStretch();
+ /* Prepare recording frame rate max label: */
+ m_pLabelFrameRateMax = new QLabel(m_pWidgetFrameRateSettings);
+ if (m_pLabelFrameRateMax)
+ pLayoutRecordingFrameRateScale->addWidget(m_pLabelFrameRateMax);
+
+ pLayoutRecordingFrameRate->addLayout(pLayoutRecordingFrameRateScale);
+ }
+ }
+
+ pLayoutSettings->addWidget(m_pWidgetFrameRateSettings, 3, 1, 2, 1);
+ }
+ /* Prepare recording frame rate spinbox: */
+ m_pSpinboxFrameRate = new QSpinBox(pWidgetSettings);
+ if (m_pSpinboxFrameRate)
+ {
+ if (m_pLabelFrameRate)
+ m_pLabelFrameRate->setBuddy(m_pSpinboxFrameRate);
+ uiCommon().setMinimumWidthAccordingSymbolCount(m_pSpinboxFrameRate, 3);
+ m_pSpinboxFrameRate->setMinimum(1);
+ m_pSpinboxFrameRate->setMaximum(30);
+
+ pLayoutSettings->addWidget(m_pSpinboxFrameRate, 3, 2, 1, 2);
+ }
+
+ /* Prepare recording video quality label: */
+ m_pLabelVideoQuality = new QLabel(pWidgetSettings);
+ if (m_pLabelVideoQuality)
+ {
+ m_pLabelVideoQuality->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutSettings->addWidget(m_pLabelVideoQuality, 5, 0);
+ }
+ /* Prepare recording video quality widget: */
+ m_pWidgetVideoQualitySettings = new QWidget(pWidgetSettings);
+ if (m_pWidgetVideoQualitySettings)
+ {
+ /* Prepare recording video quality layout: */
+ QVBoxLayout *pLayoutRecordingVideoQuality = new QVBoxLayout(m_pWidgetVideoQualitySettings);
+ if (pLayoutRecordingVideoQuality)
+ {
+ pLayoutRecordingVideoQuality->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare recording video quality slider: */
+ m_pSliderVideoQuality = new QIAdvancedSlider(m_pWidgetVideoQualitySettings);
+ if (m_pSliderVideoQuality)
+ {
+ m_pSliderVideoQuality->setOrientation(Qt::Horizontal);
+ m_pSliderVideoQuality->setMinimum(1);
+ m_pSliderVideoQuality->setMaximum(10);
+ m_pSliderVideoQuality->setPageStep(1);
+ m_pSliderVideoQuality->setSingleStep(1);
+ m_pSliderVideoQuality->setTickInterval(1);
+ m_pSliderVideoQuality->setSnappingEnabled(true);
+ m_pSliderVideoQuality->setOptimalHint(1, 5);
+ m_pSliderVideoQuality->setWarningHint(5, 9);
+ m_pSliderVideoQuality->setErrorHint(9, 10);
+
+ pLayoutRecordingVideoQuality->addWidget(m_pSliderVideoQuality);
+ }
+ /* Prepare recording video quality scale layout: */
+ QHBoxLayout *pLayoutRecordingVideoQialityScale = new QHBoxLayout;
+ if (pLayoutRecordingVideoQialityScale)
+ {
+ pLayoutRecordingVideoQialityScale->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare recording video quality min label: */
+ m_pLabelVideoQualityMin = new QLabel(m_pWidgetVideoQualitySettings);
+ if (m_pLabelVideoQualityMin)
+ pLayoutRecordingVideoQialityScale->addWidget(m_pLabelVideoQualityMin);
+ pLayoutRecordingVideoQialityScale->addStretch();
+ /* Prepare recording video quality med label: */
+ m_pLabelVideoQualityMed = new QLabel(m_pWidgetVideoQualitySettings);
+ if (m_pLabelVideoQualityMed)
+ pLayoutRecordingVideoQialityScale->addWidget(m_pLabelVideoQualityMed);
+ pLayoutRecordingVideoQialityScale->addStretch();
+ /* Prepare recording video quality max label: */
+ m_pLabelVideoQualityMax = new QLabel(m_pWidgetVideoQualitySettings);
+ if (m_pLabelVideoQualityMax)
+ pLayoutRecordingVideoQialityScale->addWidget(m_pLabelVideoQualityMax);
+
+ pLayoutRecordingVideoQuality->addLayout(pLayoutRecordingVideoQialityScale);
+ }
+ }
+
+ pLayoutSettings->addWidget(m_pWidgetVideoQualitySettings, 5, 1, 2, 1);
+ }
+ /* Prepare recording video quality spinbox: */
+ m_pSpinboxVideoQuality = new QSpinBox(pWidgetSettings);
+ if (m_pSpinboxVideoQuality)
+ {
+ if (m_pLabelVideoQuality)
+ m_pLabelVideoQuality->setBuddy(m_pSpinboxVideoQuality);
+ uiCommon().setMinimumWidthAccordingSymbolCount(m_pSpinboxVideoQuality, 5);
+ m_pSpinboxVideoQuality->setMinimum(VIDEO_CAPTURE_BIT_RATE_MIN);
+ m_pSpinboxVideoQuality->setMaximum(VIDEO_CAPTURE_BIT_RATE_MAX);
+
+ pLayoutSettings->addWidget(m_pSpinboxVideoQuality, 5, 2, 1, 2);
+ }
+
+ /* Prepare recording audio quality label: */
+ m_pLabelAudioQuality = new QLabel(pWidgetSettings);
+ if (m_pLabelAudioQuality)
+ {
+ m_pLabelAudioQuality->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutSettings->addWidget(m_pLabelAudioQuality, 7, 0);
+ }
+ /* Prepare recording audio quality widget: */
+ m_pWidgetAudioQualitySettings = new QWidget(pWidgetSettings);
+ if (m_pWidgetAudioQualitySettings)
+ {
+ /* Prepare recording audio quality layout: */
+ QVBoxLayout *pLayoutRecordingAudioQuality = new QVBoxLayout(m_pWidgetAudioQualitySettings);
+ if (pLayoutRecordingAudioQuality)
+ {
+ pLayoutRecordingAudioQuality->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare recording audio quality slider: */
+ m_pSliderAudioQuality = new QIAdvancedSlider(m_pWidgetAudioQualitySettings);
+ if (m_pSliderAudioQuality)
+ {
+ if (m_pLabelAudioQuality)
+ m_pLabelAudioQuality->setBuddy(m_pSliderAudioQuality);
+ m_pSliderAudioQuality->setOrientation(Qt::Horizontal);
+ m_pSliderAudioQuality->setMinimum(1);
+ m_pSliderAudioQuality->setMaximum(3);
+ m_pSliderAudioQuality->setPageStep(1);
+ m_pSliderAudioQuality->setSingleStep(1);
+ m_pSliderAudioQuality->setTickInterval(1);
+ m_pSliderAudioQuality->setSnappingEnabled(true);
+ m_pSliderAudioQuality->setOptimalHint(1, 2);
+ m_pSliderAudioQuality->setWarningHint(2, 3);
+
+ pLayoutRecordingAudioQuality->addWidget(m_pSliderAudioQuality);
+ }
+ /* Prepare recording audio quality scale layout: */
+ QHBoxLayout *pLayoutRecordingAudioQualityScale = new QHBoxLayout;
+ if (pLayoutRecordingAudioQualityScale)
+ {
+ pLayoutRecordingAudioQualityScale->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare recording audio quality min label: */
+ m_pLabelAudioQualityMin = new QLabel(m_pWidgetAudioQualitySettings);
+ if (m_pLabelAudioQualityMin)
+ pLayoutRecordingAudioQualityScale->addWidget(m_pLabelAudioQualityMin);
+ pLayoutRecordingAudioQualityScale->addStretch();
+ /* Prepare recording audio quality med label: */
+ m_pLabelAudioQualityMed = new QLabel(m_pWidgetAudioQualitySettings);
+ if (m_pLabelAudioQualityMed)
+ pLayoutRecordingAudioQualityScale->addWidget(m_pLabelAudioQualityMed);
+ pLayoutRecordingAudioQualityScale->addStretch();
+ /* Prepare recording audio quality max label: */
+ m_pLabelAudioQualityMax = new QLabel(m_pWidgetAudioQualitySettings);
+ if (m_pLabelAudioQualityMax)
+ pLayoutRecordingAudioQualityScale->addWidget(m_pLabelAudioQualityMax);
+
+ pLayoutRecordingAudioQuality->addLayout(pLayoutRecordingAudioQualityScale);
+ }
+ }
+
+ pLayoutSettings->addWidget(m_pWidgetAudioQualitySettings, 7, 1, 2, 1);
+ }
+
+ /* Prepare recording size hint label: */
+ m_pLabelSizeHint = new QLabel(pWidgetSettings);
+ if (m_pLabelSizeHint)
+ pLayoutSettings->addWidget(m_pLabelSizeHint, 9, 1);
+
+ /* Prepare recording screens label: */
+ m_pLabelScreens = new QLabel(pWidgetSettings);
+ if (m_pLabelScreens)
+ {
+ m_pLabelScreens->setAlignment(Qt::AlignRight | Qt::AlignTop);
+ pLayoutSettings->addWidget(m_pLabelScreens, 10, 0);
+ }
+ /* Prepare recording screens scroller: */
+ m_pScrollerScreens = new UIFilmContainer(pWidgetSettings);
+ if (m_pScrollerScreens)
+ {
+ if (m_pLabelScreens)
+ m_pLabelScreens->setBuddy(m_pScrollerScreens);
+ pLayoutSettings->addWidget(m_pScrollerScreens, 10, 1, 1, 3);
+ }
+ }
+
+ pLayout->addWidget(pWidgetSettings, 1, 1, 1, 2);
+ }
+ }
+
+ /* Update widget availability: */
+ updateWidgetAvailability();
+}
+
+void UIRecordingSettingsEditor::prepareConnections()
+{
+ connect(m_pCheckboxFeature, &QCheckBox::toggled,
+ this, &UIRecordingSettingsEditor::sltHandleFeatureToggled);
+ connect(m_pComboMode, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIRecordingSettingsEditor::sltHandleModeComboChange);
+ connect(m_pComboFrameSize, static_cast<void(QComboBox::*)(int)>(&QComboBox:: currentIndexChanged),
+ this, &UIRecordingSettingsEditor::sltHandleVideoFrameSizeComboChange);
+ connect(m_pSpinboxFrameWidth, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIRecordingSettingsEditor::sltHandleVideoFrameWidthChange);
+ connect(m_pSpinboxFrameHeight, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIRecordingSettingsEditor::sltHandleVideoFrameHeightChange);
+ connect(m_pSliderFrameRate, &QIAdvancedSlider::valueChanged,
+ this, &UIRecordingSettingsEditor::sltHandleVideoFrameRateSliderChange);
+ connect(m_pSpinboxFrameRate, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIRecordingSettingsEditor::sltHandleVideoFrameRateSpinboxChange);
+ connect(m_pSliderVideoQuality, &QIAdvancedSlider::valueChanged,
+ this, &UIRecordingSettingsEditor::sltHandleVideoBitRateSliderChange);
+ connect(m_pSpinboxVideoQuality, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIRecordingSettingsEditor::sltHandleVideoBitRateSpinboxChange);
+}
+
+void UIRecordingSettingsEditor::populateComboMode()
+{
+ if (m_pComboMode)
+ {
+ /* Clear combo first of all: */
+ m_pComboMode->clear();
+
+ /* Load currently supported recording features: */
+ const int iSupportedFlag = uiCommon().supportedRecordingFeatures();
+ m_supportedValues.clear();
+ if (!iSupportedFlag)
+ m_supportedValues << UISettingsDefs::RecordingMode_None;
+ else
+ {
+ if ( (iSupportedFlag & KRecordingFeature_Video)
+ && (iSupportedFlag & KRecordingFeature_Audio))
+ m_supportedValues << UISettingsDefs::RecordingMode_VideoAudio;
+ if (iSupportedFlag & KRecordingFeature_Video)
+ m_supportedValues << UISettingsDefs::RecordingMode_VideoOnly;
+ if (iSupportedFlag & KRecordingFeature_Audio)
+ m_supportedValues << UISettingsDefs::RecordingMode_AudioOnly;
+ }
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmMode != UISettingsDefs::RecordingMode_Max
+ && !m_supportedValues.contains(m_enmMode))
+ m_supportedValues.prepend(m_enmMode);
+
+ /* Update combo with all the supported values: */
+ foreach (const UISettingsDefs::RecordingMode &enmType, m_supportedValues)
+ m_pComboMode->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pComboMode->findData(QVariant::fromValue(m_enmMode));
+ if (iIndex != -1)
+ m_pComboMode->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
+
+void UIRecordingSettingsEditor::updateWidgetVisibility()
+{
+ /* Only the Audio stuff can be totally disabled, so we will add the code for hiding Audio stuff only: */
+ const bool fAudioSettingsVisible = m_supportedValues.isEmpty()
+ || m_supportedValues.contains(UISettingsDefs::RecordingMode_AudioOnly);
+ m_pWidgetAudioQualitySettings->setVisible(fAudioSettingsVisible);
+ m_pLabelAudioQuality->setVisible(fAudioSettingsVisible);
+}
+
+void UIRecordingSettingsEditor::updateWidgetAvailability()
+{
+ const bool fFeatureEnabled = m_pCheckboxFeature->isChecked();
+ const UISettingsDefs::RecordingMode enmRecordingMode =
+ m_pComboMode->currentData().value<UISettingsDefs::RecordingMode>();
+ const bool fRecordVideo = enmRecordingMode == UISettingsDefs::RecordingMode_VideoOnly
+ || enmRecordingMode == UISettingsDefs::RecordingMode_VideoAudio;
+ const bool fRecordAudio = enmRecordingMode == UISettingsDefs::RecordingMode_AudioOnly
+ || enmRecordingMode == UISettingsDefs::RecordingMode_VideoAudio;
+
+ m_pLabelMode->setEnabled(fFeatureEnabled && m_fOptionsAvailable);
+ m_pComboMode->setEnabled(fFeatureEnabled && m_fOptionsAvailable);
+ m_pLabelFilePath->setEnabled(fFeatureEnabled && m_fOptionsAvailable);
+ m_pEditorFilePath->setEnabled(fFeatureEnabled && m_fOptionsAvailable);
+
+ m_pLabelFrameSize->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+ m_pComboFrameSize->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+ m_pSpinboxFrameWidth->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+ m_pSpinboxFrameHeight->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+
+ m_pLabelFrameRate->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+ m_pWidgetFrameRateSettings->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+ m_pSpinboxFrameRate->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+
+ m_pLabelVideoQuality->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+ m_pWidgetVideoQualitySettings->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+ m_pSpinboxVideoQuality->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+
+ m_pLabelAudioQuality->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordAudio);
+ m_pWidgetAudioQualitySettings->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordAudio);
+
+ m_pLabelSizeHint->setEnabled(fFeatureEnabled && m_fOptionsAvailable && fRecordVideo);
+
+ m_pLabelScreens->setEnabled(fFeatureEnabled && m_fScreenOptionsAvailable && fRecordVideo);
+ m_pScrollerScreens->setEnabled(fFeatureEnabled && m_fScreenOptionsAvailable && fRecordVideo);
+}
+
+void UIRecordingSettingsEditor::updateRecordingFileSizeHint()
+{
+ m_pLabelSizeHint->setText(tr("<i>About %1MB per 5 minute video</i>")
+ .arg(m_pSpinboxVideoQuality->value() * 300 / 8 / 1024));
+}
+
+void UIRecordingSettingsEditor::lookForCorrespondingFrameSizePreset()
+{
+ lookForCorrespondingPreset(m_pComboFrameSize,
+ QSize(m_pSpinboxFrameWidth->value(),
+ m_pSpinboxFrameHeight->value()));
+}
+
+/* static */
+void UIRecordingSettingsEditor::lookForCorrespondingPreset(QComboBox *pComboBox, const QVariant &data)
+{
+ /* Use passed iterator to look for corresponding preset of passed combo-box: */
+ const int iLookupResult = pComboBox->findData(data);
+ if (iLookupResult != -1 && pComboBox->currentIndex() != iLookupResult)
+ pComboBox->setCurrentIndex(iLookupResult);
+ else if (iLookupResult == -1 && pComboBox->currentIndex() != 0)
+ pComboBox->setCurrentIndex(0);
+}
+
+/* static */
+int UIRecordingSettingsEditor::calculateBitRate(int iFrameWidth, int iFrameHeight, int iFrameRate, int iQuality)
+{
+ /* Linear quality<=>bit-rate scale-factor: */
+ const double dResult = (double)iQuality
+ * (double)iFrameWidth * (double)iFrameHeight * (double)iFrameRate
+ / (double)10 /* translate quality to [%] */
+ / (double)1024 /* translate bit-rate to [kbps] */
+ / (double)18.75 /* linear scale factor */;
+ return (int)dResult;
+}
+
+/* static */
+int UIRecordingSettingsEditor::calculateQuality(int iFrameWidth, int iFrameHeight, int iFrameRate, int iBitRate)
+{
+ /* Linear bit-rate<=>quality scale-factor: */
+ const double dResult = (double)iBitRate
+ / (double)iFrameWidth / (double)iFrameHeight / (double)iFrameRate
+ * (double)10 /* translate quality to [%] */
+ * (double)1024 /* translate bit-rate to [kbps] */
+ * (double)18.75 /* linear scale factor */;
+ return (int)dResult;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIRecordingSettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIRecordingSettingsEditor.h
new file mode 100644
index 00000000..5a0bd327
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIRecordingSettingsEditor.h
@@ -0,0 +1,273 @@
+/* $Id: UIRecordingSettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIRecordingSettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIRecordingSettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIRecordingSettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UISettingsDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Other VBox includes: */
+#include <VBox/com/com.h>
+
+/* Forward declarations: */
+class QCheckBox;
+class QComboBox;
+class QLabel;
+class QSpinBox;
+class QWidget;
+class QIAdvancedSlider;
+class UIFilePathSelector;
+class UIFilmContainer;
+
+/** QWidget subclass used as a recording settings editor. */
+class SHARED_LIBRARY_STUFF UIRecordingSettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIRecordingSettingsEditor(QWidget *pParent = 0);
+
+ /** Defines whether feature is @a fEnabled. */
+ void setFeatureEnabled(bool fEnabled);
+ /** Returns whether feature is enabled. */
+ bool isFeatureEnabled() const;
+
+ /** Defines whether options are @a fAvailable. */
+ void setOptionsAvailable(bool fAvailable);
+ /** Defines whether screen options are @a fAvailable. */
+ void setScreenOptionsAvailable(bool fAvailable);
+
+ /** Defines @a enmMode. */
+ void setMode(UISettingsDefs::RecordingMode enmMode);
+ /** Return mode. */
+ UISettingsDefs::RecordingMode mode() const;
+
+ /** Defines @a strFolder. */
+ void setFolder(const QString &strFolder);
+ /** Returns folder. */
+ QString folder() const;
+ /** Defines @a strFilePath. */
+ void setFilePath(const QString &strFilePath);
+ /** Returns file path. */
+ QString filePath() const;
+
+ /** Defines frame @a iWidth. */
+ void setFrameWidth(int iWidth);
+ /** Returns frame width. */
+ int frameWidth() const;
+ /** Defines frame @a iHeight. */
+ void setFrameHeight(int iHeight);
+ /** Returns frame height. */
+ int frameHeight() const;
+
+ /** Defines frame @a iRate. */
+ void setFrameRate(int iRate);
+ /** Returns frame rate. */
+ int frameRate() const;
+
+ /** Defines bit @a iRate. */
+ void setBitRate(int iRate);
+ /** Returns bit rate. */
+ int bitRate() const;
+
+ /** Defines audio quality @a iRate. */
+ void setAudioQualityRate(int iRate);
+ /** Returns audio quality rate. */
+ int audioQualityRate() const;
+
+ /** Defines enabled @a screens. */
+ void setScreens(const QVector<BOOL> &screens);
+ /** Returns enabled screens. */
+ QVector<BOOL> screens() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles feature toggling. */
+ void sltHandleFeatureToggled();
+ /** Handles mode change. */
+ void sltHandleModeComboChange();
+ /** Handles frame size change. */
+ void sltHandleVideoFrameSizeComboChange();
+ /** Handles frame width change. */
+ void sltHandleVideoFrameWidthChange();
+ /** Handles frame height change. */
+ void sltHandleVideoFrameHeightChange();
+ /** Handles frame rate slider change. */
+ void sltHandleVideoFrameRateSliderChange();
+ /** Handles frame rate spinbox change. */
+ void sltHandleVideoFrameRateSpinboxChange();
+ /** Handles bit-rate slider change. */
+ void sltHandleVideoBitRateSliderChange();
+ /** Handles bit-rate spinbox change. */
+ void sltHandleVideoBitRateSpinboxChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Populates mode combo-box. */
+ void populateComboMode();
+
+ /** Updates widget visibility. */
+ void updateWidgetVisibility();
+ /** Updates widget availability. */
+ void updateWidgetAvailability();
+ /** Updates recording file size hint. */
+ void updateRecordingFileSizeHint();
+ /** Searches for corresponding frame size preset. */
+ void lookForCorrespondingFrameSizePreset();
+
+ /** Searches for the @a data field in corresponding @a pComboBox. */
+ static void lookForCorrespondingPreset(QComboBox *pComboBox, const QVariant &data);
+ /** Calculates recording video bit-rate for passed @a iFrameWidth, @a iFrameHeight, @a iFrameRate and @a iQuality. */
+ static int calculateBitRate(int iFrameWidth, int iFrameHeight, int iFrameRate, int iQuality);
+ /** Calculates recording video quality for passed @a iFrameWidth, @a iFrameHeight, @a iFrameRate and @a iBitRate. */
+ static int calculateQuality(int iFrameWidth, int iFrameHeight, int iFrameRate, int iBitRate);
+
+ /** @name Values
+ * @{ */
+ /** Holds whether feature is enabled. */
+ bool m_fFeatureEnabled;
+
+ /** Holds whether options are available. */
+ bool m_fOptionsAvailable;
+ /** Holds whether screen options are available. */
+ bool m_fScreenOptionsAvailable;
+
+ /** Holds the list of supported modes. */
+ QVector<UISettingsDefs::RecordingMode> m_supportedValues;
+ /** Holds the mode. */
+ UISettingsDefs::RecordingMode m_enmMode;
+
+ /** Holds the folder. */
+ QString m_strFolder;
+ /** Holds the file path. */
+ QString m_strFilePath;
+
+ /** Holds the frame width. */
+ int m_iFrameWidth;
+ /** Holds the frame height. */
+ int m_iFrameHeight;
+ /** Holds the frame rate. */
+ int m_iFrameRate;
+ /** Holds the bit rate. */
+ int m_iBitRate;
+ /** Holds the audio quality rate. */
+ int m_iAudioQualityRate;
+
+ /** Holds the screens. */
+ QVector<BOOL> m_screens;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the feature check-box instance. */
+ QCheckBox *m_pCheckboxFeature;
+ /** Holds the mode label instance. */
+ QLabel *m_pLabelMode;
+ /** Holds the mode combo instance. */
+ QComboBox *m_pComboMode;
+ /** Holds the file path label instance. */
+ QLabel *m_pLabelFilePath;
+ /** Holds the file path editor instance. */
+ UIFilePathSelector *m_pEditorFilePath;
+ /** Holds the frame size label instance. */
+ QLabel *m_pLabelFrameSize;
+ /** Holds the frame size combo instance. */
+ QComboBox *m_pComboFrameSize;
+ /** Holds the frame width spinbox instance. */
+ QSpinBox *m_pSpinboxFrameWidth;
+ /** Holds the frame height spinbox instance. */
+ QSpinBox *m_pSpinboxFrameHeight;
+ /** Holds the frame rate label instance. */
+ QLabel *m_pLabelFrameRate;
+ /** Holds the frame rate settings widget instance. */
+ QWidget *m_pWidgetFrameRateSettings;
+ /** Holds the frame rate slider instance. */
+ QIAdvancedSlider *m_pSliderFrameRate;
+ /** Holds the frame rate spinbox instance. */
+ QSpinBox *m_pSpinboxFrameRate;
+ /** Holds the frame rate min label instance. */
+ QLabel *m_pLabelFrameRateMin;
+ /** Holds the frame rate max label instance. */
+ QLabel *m_pLabelFrameRateMax;
+ /** Holds the video quality label instance. */
+ QLabel *m_pLabelVideoQuality;
+ /** Holds the video quality settings widget instance. */
+ QWidget *m_pWidgetVideoQualitySettings;
+ /** Holds the video quality slider instance. */
+ QIAdvancedSlider *m_pSliderVideoQuality;
+ /** Holds the video quality spinbox instance. */
+ QSpinBox *m_pSpinboxVideoQuality;
+ /** Holds the video quality min label instance. */
+ QLabel *m_pLabelVideoQualityMin;
+ /** Holds the video quality med label instance. */
+ QLabel *m_pLabelVideoQualityMed;
+ /** Holds the video quality max label instance. */
+ QLabel *m_pLabelVideoQualityMax;
+ /** Holds the audio quality label instance. */
+ QLabel *m_pLabelAudioQuality;
+ /** Holds the audio quality settings widget instance. */
+ QWidget *m_pWidgetAudioQualitySettings;
+ /** Holds the audio quality slider instance. */
+ QIAdvancedSlider *m_pSliderAudioQuality;
+ /** Holds the audio quality min label instance. */
+ QLabel *m_pLabelAudioQualityMin;
+ /** Holds the audio quality med label instance. */
+ QLabel *m_pLabelAudioQualityMed;
+ /** Holds the audio quality max label instance. */
+ QLabel *m_pLabelAudioQualityMax;
+ /** Holds the size hint label instance. */
+ QLabel *m_pLabelSizeHint;
+ /** Holds the screens label instance. */
+ QLabel *m_pLabelScreens;
+ /** Holds the screens scroller instance. */
+ UIFilmContainer *m_pScrollerScreens;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIRecordingSettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIScaleFactorEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIScaleFactorEditor.cpp
new file mode 100644
index 00000000..0012f31f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIScaleFactorEditor.cpp
@@ -0,0 +1,369 @@
+/* $Id: UIScaleFactorEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIScaleFactorEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QSpacerItem>
+#include <QSpinBox>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIScaleFactorEditor.h"
+
+/* External includes: */
+#include <math.h>
+
+
+UIScaleFactorEditor::UIScaleFactorEditor(QWidget *pParent)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLayout(0)
+ , m_pLabel(0)
+ , m_pMonitorComboBox(0)
+ , m_pScaleSlider(0)
+ , m_pScaleSpinBox(0)
+ , m_pMinScaleLabel(0)
+ , m_pMaxScaleLabel(0)
+ , m_dDefaultScaleFactor(1.0)
+{
+ /* Prepare: */
+ prepare();
+ /* Append a default scale factor to the list as an global scale factor: */
+ m_scaleFactors.append(1.0);
+}
+
+void UIScaleFactorEditor::setMonitorCount(int iMonitorCount)
+{
+ if (!m_pMonitorComboBox)
+ return;
+ /* We always have 0th for global scale factor (in combo box and scale factor list): */
+ int iEndMonitorCount = iMonitorCount + 1;
+ int iCurrentMonitorCount = m_pMonitorComboBox->count();
+ if (iEndMonitorCount == iCurrentMonitorCount)
+ return;
+ m_pMonitorComboBox->setEnabled(iMonitorCount > 1);
+ m_pMonitorComboBox->blockSignals(true);
+ int iCurrentMonitorIndex = m_pMonitorComboBox->currentIndex();
+ if (iCurrentMonitorCount < iEndMonitorCount)
+ {
+ for (int i = iCurrentMonitorCount; i < iEndMonitorCount; ++i)
+ m_pMonitorComboBox->insertItem(i, tr("Monitor %1").arg(i));
+ }
+ else
+ {
+ for (int i = iCurrentMonitorCount - 1; i >= iEndMonitorCount; --i)
+ m_pMonitorComboBox->removeItem(i);
+ }
+ m_pMonitorComboBox->setEnabled(iMonitorCount > 1);
+ /* If we have a single monitor select the "All Monitors" item in the combo
+ but make sure we retain the scale factor of the 0th monitor: */
+ if (iMonitorCount <= 1)
+ {
+ if (m_scaleFactors.size() >= 2)
+ m_scaleFactors[0] = m_scaleFactors[1];
+ m_pMonitorComboBox->setCurrentIndex(0);
+ }
+ m_pMonitorComboBox->blockSignals(false);
+ /* Update the slider and spinbox values if the combobox index has changed: */
+ if (iCurrentMonitorIndex != m_pMonitorComboBox->currentIndex())
+ updateValuesAfterMonitorChange();
+}
+
+void UIScaleFactorEditor::setScaleFactors(const QList<double> &scaleFactors)
+{
+ m_scaleFactors.clear();
+ /* If we have a single value from the extra data and we treat if as a default scale factor: */
+ if (scaleFactors.size() == 1)
+ {
+ m_dDefaultScaleFactor = scaleFactors.at(0);
+ m_scaleFactors.append(m_dDefaultScaleFactor);
+ setIsGlobalScaleFactor(true);
+ return;
+ }
+
+ /* Insert 0th element as the global scalar value: */
+ m_scaleFactors.append(m_dDefaultScaleFactor);
+ m_scaleFactors.append(scaleFactors);
+ setIsGlobalScaleFactor(false);
+}
+
+QList<double> UIScaleFactorEditor::scaleFactors() const
+{
+ QList<double> scaleFactorList;
+ if (m_scaleFactors.size() == 0)
+ return scaleFactorList;
+
+ /* Decide if the users wants a global (not per monitor) scaling. */
+ bool globalScaleFactor = false;
+
+ /* if "All Monitors" item is selected in the combobox: */
+ if (m_pMonitorComboBox && m_pMonitorComboBox->currentIndex() == 0)
+ globalScaleFactor = true;
+ /* Also check if all of the monitor scale factors equal to the global scale factor: */
+ if (!globalScaleFactor)
+ {
+ globalScaleFactor = true;
+ for (int i = 1; i < m_scaleFactors.size() && globalScaleFactor; ++i)
+ if (m_scaleFactors[0] != m_scaleFactors[i])
+ globalScaleFactor = false;
+ }
+
+ if (globalScaleFactor)
+ {
+ scaleFactorList << m_scaleFactors.at(0);
+ }
+ else
+ {
+ /* Skip the 0th scale factor: */
+ for (int i = 1; i < m_scaleFactors.size(); ++i)
+ scaleFactorList.append(m_scaleFactors.at(i));
+ }
+
+ return scaleFactorList;
+}
+
+void UIScaleFactorEditor::setIsGlobalScaleFactor(bool bFlag)
+{
+ if (!m_pMonitorComboBox)
+ return;
+ if (bFlag && m_pMonitorComboBox->count() >= 1)
+ m_pMonitorComboBox->setCurrentIndex(0);
+ else
+ if(m_pMonitorComboBox->count() >= 2)
+ m_pMonitorComboBox->setCurrentIndex(1);
+ updateValuesAfterMonitorChange();
+}
+
+void UIScaleFactorEditor::setDefaultScaleFactor(double dDefaultScaleFactor)
+{
+ m_dDefaultScaleFactor = dDefaultScaleFactor;
+}
+
+void UIScaleFactorEditor::setSpinBoxWidthHint(int iHint)
+{
+ m_pScaleSpinBox->setMinimumWidth(iHint);
+}
+
+int UIScaleFactorEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIScaleFactorEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIScaleFactorEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Scale &Factor:"));
+
+ if (m_pMonitorComboBox)
+ {
+ if (m_pMonitorComboBox->count() > 0)
+ {
+ m_pMonitorComboBox->setItemText(0, tr("All Monitors"));
+ for (int i = 1; i < m_pMonitorComboBox->count(); ++i)
+ m_pMonitorComboBox->setItemText(i, tr("Monitor %1").arg(i));
+ }
+ m_pMonitorComboBox->setToolTip(tr("Selects the index of monitor guest screen scale factor being defined for."));
+ }
+
+ if (m_pScaleSlider)
+ m_pScaleSlider->setToolTip(tr("Holds the guest screen scale factor."));
+ if (m_pScaleSpinBox)
+ m_pScaleSpinBox->setToolTip(tr("Holds the guest screen scale factor."));
+
+ if (m_pMinScaleLabel)
+ {
+ m_pMinScaleLabel->setText(QString("%1%").arg(m_pScaleSlider->minimum()));
+ m_pMinScaleLabel->setToolTip(tr("Minimum possible scale factor."));
+ }
+ if (m_pMaxScaleLabel)
+ {
+ m_pMaxScaleLabel->setText(QString("%1%").arg(m_pScaleSlider->maximum()));
+ m_pMaxScaleLabel->setToolTip(tr("Maximum possible scale factor."));
+ }
+}
+
+void UIScaleFactorEditor::sltScaleSpinBoxValueChanged(int value)
+{
+ setSliderValue(value);
+ if (m_pMonitorComboBox)
+ setScaleFactor(m_pMonitorComboBox->currentIndex(), value);
+}
+
+void UIScaleFactorEditor::sltScaleSliderValueChanged(int value)
+{
+ setSpinBoxValue(value);
+ if (m_pMonitorComboBox)
+ setScaleFactor(m_pMonitorComboBox->currentIndex(), value);
+}
+
+void UIScaleFactorEditor::sltMonitorComboIndexChanged(int)
+{
+ updateValuesAfterMonitorChange();
+}
+
+void UIScaleFactorEditor::prepare()
+{
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+ m_pLayout->setColumnStretch(2, 1);
+
+ /* Prepare label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ m_pMonitorComboBox = new QComboBox(this);
+ if (m_pMonitorComboBox)
+ {
+ m_pMonitorComboBox->insertItem(0, "All Monitors");
+ connect(m_pMonitorComboBox ,static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIScaleFactorEditor::sltMonitorComboIndexChanged);
+
+ m_pLayout->addWidget(m_pMonitorComboBox, 0, 1);
+ }
+
+ m_pScaleSlider = new QIAdvancedSlider(this);
+ {
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pScaleSlider);
+ m_pScaleSlider->setPageStep(10);
+ m_pScaleSlider->setSingleStep(1);
+ m_pScaleSlider->setTickInterval(10);
+ m_pScaleSlider->setSnappingEnabled(true);
+ connect(m_pScaleSlider, static_cast<void(QIAdvancedSlider::*)(int)>(&QIAdvancedSlider::valueChanged),
+ this, &UIScaleFactorEditor::sltScaleSliderValueChanged);
+
+ m_pLayout->addWidget(m_pScaleSlider, 0, 2, 1, 2);
+ }
+
+ m_pScaleSpinBox = new QSpinBox(this);
+ if (m_pScaleSpinBox)
+ {
+ setFocusProxy(m_pScaleSpinBox);
+ m_pScaleSpinBox->setSuffix("%");
+ connect(m_pScaleSpinBox ,static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIScaleFactorEditor::sltScaleSpinBoxValueChanged);
+ m_pLayout->addWidget(m_pScaleSpinBox, 0, 4);
+ }
+
+ m_pMinScaleLabel = new QLabel(this);
+ if (m_pMinScaleLabel)
+ m_pLayout->addWidget(m_pMinScaleLabel, 1, 2);
+
+ m_pMaxScaleLabel = new QLabel(this);
+ if (m_pMaxScaleLabel)
+ m_pLayout->addWidget(m_pMaxScaleLabel, 1, 3);
+ }
+
+ prepareScaleFactorMinMaxValues();
+ retranslateUi();
+}
+
+void UIScaleFactorEditor::prepareScaleFactorMinMaxValues()
+{
+ const int iHostScreenCount = UIDesktopWidgetWatchdog::screenCount();
+ if (iHostScreenCount == 0)
+ return;
+ double dMaxDevicePixelRatio = UIDesktopWidgetWatchdog::devicePixelRatio(0);
+ for (int i = 1; i < iHostScreenCount; ++i)
+ if (dMaxDevicePixelRatio < UIDesktopWidgetWatchdog::devicePixelRatio(i))
+ dMaxDevicePixelRatio = UIDesktopWidgetWatchdog::devicePixelRatio(i);
+
+ const int iMinimum = 100;
+ const int iMaximum = ceil(iMinimum + 100 * dMaxDevicePixelRatio);
+
+ const int iStep = 25;
+
+ m_pScaleSlider->setMinimum(iMinimum);
+ m_pScaleSlider->setMaximum(iMaximum);
+ m_pScaleSlider->setPageStep(iStep);
+ m_pScaleSlider->setSingleStep(1);
+ m_pScaleSlider->setTickInterval(iStep);
+ m_pScaleSpinBox->setMinimum(iMinimum);
+ m_pScaleSpinBox->setMaximum(iMaximum);
+}
+
+void UIScaleFactorEditor::setScaleFactor(int iMonitorIndex, int iScaleFactor)
+{
+ /* Make sure we have the corresponding scale values for all monitors: */
+ if (m_pMonitorComboBox->count() > m_scaleFactors.size())
+ {
+ for (int i = m_scaleFactors.size(); i < m_pMonitorComboBox->count(); ++i)
+ m_scaleFactors.append(m_dDefaultScaleFactor);
+ }
+ m_scaleFactors[iMonitorIndex] = iScaleFactor / 100.0;
+}
+
+void UIScaleFactorEditor::setSliderValue(int iValue)
+{
+ if (m_pScaleSlider && iValue != m_pScaleSlider->value())
+ {
+ m_pScaleSlider->blockSignals(true);
+ m_pScaleSlider->setValue(iValue);
+ m_pScaleSlider->blockSignals(false);
+ }
+}
+
+void UIScaleFactorEditor::setSpinBoxValue(int iValue)
+{
+ if (m_pScaleSpinBox && iValue != m_pScaleSpinBox->value())
+ {
+ m_pScaleSpinBox->blockSignals(true);
+ m_pScaleSpinBox->setValue(iValue);
+ m_pScaleSpinBox->blockSignals(false);
+ }
+}
+
+void UIScaleFactorEditor::updateValuesAfterMonitorChange()
+{
+ /* Set the spinbox value for the currently selected monitor: */
+ if (m_pMonitorComboBox)
+ {
+ int currentMonitorIndex = m_pMonitorComboBox->currentIndex();
+ while (m_scaleFactors.size() <= currentMonitorIndex)
+ m_scaleFactors.append(m_dDefaultScaleFactor);
+
+ setSpinBoxValue(100 * m_scaleFactors.at(currentMonitorIndex));
+ setSliderValue(100 * m_scaleFactors.at(currentMonitorIndex));
+
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIScaleFactorEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIScaleFactorEditor.h
new file mode 100644
index 00000000..c96f2a0b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIScaleFactorEditor.h
@@ -0,0 +1,127 @@
+/* $Id: UIScaleFactorEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIScaleFactorEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIScaleFactorEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIScaleFactorEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+class QSpinBox;
+class QWidget;
+class QIAdvancedSlider;
+
+/** QWidget reimplementation providing GUI with monitor scale factor editing functionality.
+ * It includes a combo box to select a monitor, a slider, and a spinbox to display/modify values.
+ * The first item in the combo box is used to change the scale factor of all monitors. */
+class SHARED_LIBRARY_STUFF UIScaleFactorEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIScaleFactorEditor(QWidget *pParent);
+
+ /** Defines @a iMonitorCount. */
+ void setMonitorCount(int iMonitorCount);
+ /** Defines a list of guest-screen @a scaleFactors. */
+ void setScaleFactors(const QList<double> &scaleFactors);
+
+ /** Returns either a single global scale factor or a list of scale factor for each monitor. */
+ QList<double> scaleFactors() const;
+
+ /** Defines @a dDefaultScaleFactor. */
+ void setDefaultScaleFactor(double dDefaultScaleFactor);
+
+ /** Defines minimum width @a iHint for internal spin-box. */
+ void setSpinBoxWidthHint(int iHint);
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** @name Internal slots handling respective widget's value update.
+ * @{ */
+ void sltScaleSpinBoxValueChanged(int iValue);
+ void sltScaleSliderValueChanged(int iValue);
+ void sltMonitorComboIndexChanged(int iIndex);
+ /** @} */
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepare min/max values of related widgets wrt. device pixel ratio(s). */
+ void prepareScaleFactorMinMaxValues();
+
+ /** Defines whether scale factor is @a fGlobal one. */
+ void setIsGlobalScaleFactor(bool fGlobal);
+ /** Defines @a iScaleFactor for certain @a iMonitorIndex. */
+ void setScaleFactor(int iMonitorIndex, int iScaleFactor);
+ /** Defines slider's @a iValue. */
+ void setSliderValue(int iValue);
+ /** Defines spinbox's @a iValue. */
+ void setSpinBoxValue(int iValue);
+
+ /** Sets the spinbox and slider to scale factor of currently selected monitor. */
+ void updateValuesAfterMonitorChange();
+
+ /** @name Member widgets.
+ * @{ */
+ QGridLayout *m_pLayout;
+ QLabel *m_pLabel;
+ QComboBox *m_pMonitorComboBox;
+ QIAdvancedSlider *m_pScaleSlider;
+ QSpinBox *m_pScaleSpinBox;
+ QLabel *m_pMinScaleLabel;
+ QLabel *m_pMaxScaleLabel;
+ /** @} */
+
+ /** Holds the per-monitor scale factors. The 0th item is for all monitors (global). */
+ QList<double> m_scaleFactors;
+ /** Holds the default scale factor. */
+ double m_dDefaultScaleFactor;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIScaleFactorEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedClipboardEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedClipboardEditor.cpp
new file mode 100644
index 00000000..12fbff8b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedClipboardEditor.cpp
@@ -0,0 +1,169 @@
+/* $Id: UISharedClipboardEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UISharedClipboardEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UISharedClipboardEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UISharedClipboardEditor::UISharedClipboardEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KClipboardMode_Max)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UISharedClipboardEditor::setValue(KClipboardMode enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+KClipboardMode UISharedClipboardEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<KClipboardMode>() : m_enmValue;
+}
+
+int UISharedClipboardEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UISharedClipboardEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UISharedClipboardEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("&Shared Clipboard:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const KClipboardMode enmType = m_pCombo->itemData(i).value<KClipboardMode>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Holds which clipboard data will be copied between the guest and the host OS. "
+ "This feature requires Guest Additions to be installed in the guest OS."));
+ }
+}
+
+void UISharedClipboardEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UISharedClipboardEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Load currently supported audio driver types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedClipboardModes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KClipboardMode_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const KClipboardMode &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedClipboardEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedClipboardEditor.h
new file mode 100644
index 00000000..0063cfef
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedClipboardEditor.h
@@ -0,0 +1,98 @@
+/* $Id: UISharedClipboardEditor.h $ */
+/** @file
+ * VBox Qt GUI - UISharedClipboardEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UISharedClipboardEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UISharedClipboardEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as a shared clipboard editor. */
+class SHARED_LIBRARY_STUFF UISharedClipboardEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UISharedClipboardEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KClipboardMode enmValue);
+ /** Returns editor value. */
+ KClipboardMode value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KClipboardMode> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ KClipboardMode m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KClipboardMode> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UISharedClipboardEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFolderDetailsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFolderDetailsEditor.cpp
new file mode 100644
index 00000000..04bda40f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFolderDetailsEditor.cpp
@@ -0,0 +1,347 @@
+/* $Id: UISharedFolderDetailsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UISharedFolderDetailsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes */
+#include <QCheckBox>
+#include <QDir>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+
+/* GUI includes */
+#include "QIDialogButtonBox.h"
+#include "UICommon.h"
+#include "UIFilePathSelector.h"
+#include "UISharedFolderDetailsEditor.h"
+
+
+UISharedFolderDetailsEditor::UISharedFolderDetailsEditor(EditorType enmType,
+ bool fUsePermanent,
+ const QStringList &usedNames,
+ QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI2<QIDialog>(pParent)
+ , m_enmType(enmType)
+ , m_fUsePermanent(fUsePermanent)
+ , m_usedNames(usedNames)
+ , m_pLabelPath(0)
+ , m_pSelectorPath(0)
+ , m_pLabelName(0)
+ , m_pEditorName(0)
+ , m_pLabelAutoMountPoint(0)
+ , m_pEditorAutoMountPoint(0)
+ , m_pCheckBoxReadonly(0)
+ , m_pCheckBoxAutoMount(0)
+ , m_pCheckBoxPermanent(0)
+ , m_pButtonBox(0)
+{
+ prepare();
+}
+
+void UISharedFolderDetailsEditor::setPath(const QString &strPath)
+{
+ if (m_pSelectorPath)
+ m_pSelectorPath->setPath(strPath);
+}
+
+QString UISharedFolderDetailsEditor::path() const
+{
+ return m_pSelectorPath ? m_pSelectorPath->path() : QString();
+}
+
+void UISharedFolderDetailsEditor::setName(const QString &strName)
+{
+ if (m_pEditorName)
+ m_pEditorName->setText(strName);
+}
+
+QString UISharedFolderDetailsEditor::name() const
+{
+ return m_pEditorName ? m_pEditorName->text() : QString();
+}
+
+void UISharedFolderDetailsEditor::setWriteable(bool fWritable)
+{
+ if (m_pCheckBoxReadonly)
+ m_pCheckBoxReadonly->setChecked(!fWritable);
+}
+
+bool UISharedFolderDetailsEditor::isWriteable() const
+{
+ return m_pCheckBoxReadonly ? !m_pCheckBoxReadonly->isChecked() : false;
+}
+
+void UISharedFolderDetailsEditor::setAutoMount(bool fAutoMount)
+{
+ if (m_pCheckBoxAutoMount)
+ m_pCheckBoxAutoMount->setChecked(fAutoMount);
+}
+
+bool UISharedFolderDetailsEditor::isAutoMounted() const
+{
+ return m_pCheckBoxAutoMount ? m_pCheckBoxAutoMount->isChecked() : false;
+}
+
+void UISharedFolderDetailsEditor::setAutoMountPoint(const QString &strAutoMountPoint)
+{
+ if (m_pEditorAutoMountPoint)
+ m_pEditorAutoMountPoint->setText(strAutoMountPoint);
+}
+
+QString UISharedFolderDetailsEditor::autoMountPoint() const
+{
+ return m_pEditorAutoMountPoint ? m_pEditorAutoMountPoint->text() : QString();
+}
+
+void UISharedFolderDetailsEditor::setPermanent(bool fPermanent)
+{
+ if (m_pCheckBoxPermanent)
+ m_pCheckBoxPermanent->setChecked(fPermanent);
+}
+
+bool UISharedFolderDetailsEditor::isPermanent() const
+{
+ return m_fUsePermanent ? m_pCheckBoxPermanent->isChecked() : true;
+}
+
+void UISharedFolderDetailsEditor::retranslateUi()
+{
+ switch (m_enmType)
+ {
+ case EditorType_Add: setWindowTitle(tr("Add Share")); break;
+ case EditorType_Edit: setWindowTitle(tr("Edit Share")); break;
+ default: break;
+ }
+
+ if (m_pLabelPath)
+ m_pLabelPath->setText(tr("Folder Path:"));
+ if (m_pLabelName)
+ m_pLabelName->setText(tr("Folder Name:"));
+ if (m_pEditorName)
+ m_pEditorName->setToolTip(tr("Holds the name of the shared folder "
+ "(as it will be seen by the guest OS)."));
+ if (m_pSelectorPath)
+ m_pSelectorPath->setToolTip(tr("Holds the path of the shared folder"));
+ if (m_pButtonBox && m_pButtonBox->button(QDialogButtonBox::Ok))
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setToolTip(tr("Apply the changes and close this dialog"));
+ if (m_pButtonBox && m_pButtonBox->button(QDialogButtonBox::Cancel))
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setToolTip(tr("Cancel"));
+
+ if (m_pCheckBoxReadonly)
+ {
+ m_pCheckBoxReadonly->setText(tr("&Read-only"));
+ m_pCheckBoxReadonly->setToolTip(tr("When checked, the guest OS will not be able "
+ "to write to the specified shared folder."));
+ }
+ if (m_pCheckBoxAutoMount)
+ {
+ m_pCheckBoxAutoMount->setText(tr("&Auto-mount"));
+ m_pCheckBoxAutoMount->setToolTip(tr("When checked, the guest OS will try to "
+ "automatically mount the shared folder on startup."));
+ }
+ if (m_pLabelAutoMountPoint)
+ m_pLabelAutoMountPoint->setText(tr("Mount point:"));
+ if (m_pEditorAutoMountPoint)
+ m_pEditorAutoMountPoint->setToolTip(tr("Where to automatically mount the folder in the guest. "
+ "A drive letter (e.g. 'G:') for Windows and OS/2 guests, path for the others. "
+ "If left empty the guest will pick something fitting."));
+ if (m_pCheckBoxPermanent)
+ {
+ m_pCheckBoxPermanent->setText(tr("&Make Permanent"));
+ m_pCheckBoxPermanent->setToolTip(tr("When checked, this shared folder will be permanent."));
+ }
+}
+
+void UISharedFolderDetailsEditor::sltValidate()
+{
+ if (m_pButtonBox)
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled( !m_pSelectorPath->path().isEmpty()
+ && QDir(m_pSelectorPath->path()).exists()
+ && !m_pEditorName->text().trimmed().isEmpty()
+ && !m_pEditorName->text().contains(" ")
+ && !m_usedNames.contains(m_pEditorName->text()));
+}
+
+void UISharedFolderDetailsEditor::sltSelectPath()
+{
+ if ( !m_pSelectorPath
+ || !m_pSelectorPath->isPathSelected())
+ return;
+
+ QString strFolderName(m_pSelectorPath->path());
+#if defined (VBOX_WS_WIN) || defined (Q_OS_OS2)
+ if (strFolderName[0].isLetter() && strFolderName[1] == ':' && strFolderName[2] == 0)
+ {
+ /* UIFilePathSelector returns root path as 'X:', which is invalid path.
+ * Append the trailing backslash to get a valid root path 'X:\': */
+ strFolderName += "\\";
+ m_pSelectorPath->setPath(strFolderName);
+ }
+#endif /* VBOX_WS_WIN || Q_OS_OS2 */
+
+ if (!m_pEditorName)
+ return;
+
+ QDir folder(strFolderName);
+ if (!folder.isRoot())
+ {
+ /* Processing non-root folder */
+ m_pEditorName->setText(folder.dirName().replace(' ', '_'));
+ }
+ else
+ {
+ /* Processing root folder: */
+#if defined (VBOX_WS_WIN) || defined (Q_OS_OS2)
+ m_pEditorName->setText(strFolderName.toUpper()[0] + "_DRIVE");
+#elif defined (VBOX_WS_X11)
+ m_pEditorName->setText("ROOT");
+#endif
+ }
+
+ /* Validate the field values: */
+ sltValidate();
+}
+
+void UISharedFolderDetailsEditor::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Validate the initial field values: */
+ sltValidate();
+
+ /* Adjust dialog size: */
+ adjustSize();
+
+#ifdef VBOX_WS_MAC
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ setFixedSize(minimumSize());
+#endif /* VBOX_WS_MAC */
+}
+
+void UISharedFolderDetailsEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setRowStretch(6, 1);
+
+ /* Prepare path label: */
+ m_pLabelPath = new QLabel;
+ if (m_pLabelPath)
+ {
+ m_pLabelPath->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelPath, 0, 0);
+ }
+ /* Prepare path selector: */
+ m_pSelectorPath = new UIFilePathSelector;
+ if (m_pSelectorPath)
+ {
+ m_pSelectorPath->setResetEnabled(false);
+ m_pSelectorPath->setInitialPath(QDir::homePath());
+
+ pLayout->addWidget(m_pSelectorPath, 0, 1);
+ }
+
+ /* Prepare name label: */
+ m_pLabelName = new QLabel;
+ if (m_pLabelName)
+ {
+ m_pLabelName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelName, 1, 0);
+ }
+ /* Prepare name editor: */
+ m_pEditorName = new QLineEdit;
+ if (m_pEditorName)
+ pLayout->addWidget(m_pEditorName, 1, 1);
+
+ /* Prepare auto-mount point: */
+ m_pLabelAutoMountPoint = new QLabel;
+ if (m_pLabelAutoMountPoint)
+ {
+ m_pLabelAutoMountPoint->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelAutoMountPoint, 2, 0);
+ }
+ /* Prepare auto-mount editor: */
+ m_pEditorAutoMountPoint = new QLineEdit;
+ if (m_pEditorAutoMountPoint)
+ pLayout->addWidget(m_pEditorAutoMountPoint, 2, 1);
+
+ /* Prepare read-only check-box: */
+ m_pCheckBoxReadonly = new QCheckBox;
+ if (m_pCheckBoxReadonly)
+ pLayout->addWidget(m_pCheckBoxReadonly, 3, 1);
+ /* Prepare auto-mount check-box: */
+ m_pCheckBoxAutoMount = new QCheckBox;
+ if (m_pCheckBoxAutoMount)
+ pLayout->addWidget(m_pCheckBoxAutoMount, 4, 1);
+ /* Prepare permanent check-box: */
+ m_pCheckBoxPermanent = new QCheckBox(this);
+ if (m_pCheckBoxPermanent)
+ {
+ m_pCheckBoxPermanent->setHidden(!m_fUsePermanent);
+ pLayout->addWidget(m_pCheckBoxPermanent, 5, 1);
+ }
+
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ pLayout->addWidget(m_pButtonBox, 7, 0, 1, 2);
+ }
+ }
+}
+
+void UISharedFolderDetailsEditor::prepareConnections()
+{
+ if (m_pSelectorPath)
+ {
+ connect(m_pSelectorPath, static_cast<void(UIFilePathSelector::*)(int)>(&UIFilePathSelector::currentIndexChanged),
+ this, &UISharedFolderDetailsEditor::sltSelectPath);
+ connect(m_pSelectorPath, &UIFilePathSelector::pathChanged,
+ this, &UISharedFolderDetailsEditor::sltSelectPath);
+ }
+ if (m_pEditorName)
+ connect(m_pEditorName, &QLineEdit::textChanged,
+ this, &UISharedFolderDetailsEditor::sltValidate);
+ if (m_fUsePermanent)
+ connect(m_pCheckBoxPermanent, &QCheckBox::toggled,
+ this, &UISharedFolderDetailsEditor::sltValidate);
+ if (m_pButtonBox)
+ {
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted,
+ this, &UISharedFolderDetailsEditor::accept);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected,
+ this, &UISharedFolderDetailsEditor::reject);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFolderDetailsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFolderDetailsEditor.h
new file mode 100644
index 00000000..d9857c78
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFolderDetailsEditor.h
@@ -0,0 +1,154 @@
+/* $Id: UISharedFolderDetailsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UISharedFolderDetailsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UISharedFolderDetailsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UISharedFolderDetailsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QLabel;
+class QLineEdit;
+class QIDialogButtonBox;
+class UIFilePathSelector;
+
+/** QIDialog subclass used as a shared folders editor. */
+class SHARED_LIBRARY_STUFF UISharedFolderDetailsEditor : public QIWithRetranslateUI2<QIDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Editor types. */
+ enum EditorType
+ {
+ EditorType_Add,
+ EditorType_Edit
+ };
+
+ /** Constructs editor passing @a pParent to the base-class.
+ * @param enmType Brings editor type.
+ * @param fUsePermanent Brings whether folder can be permanent.
+ * @param usedNames Brings existing folder names. */
+ UISharedFolderDetailsEditor(EditorType enmType,
+ bool fUsePermanent,
+ const QStringList &usedNames,
+ QWidget *pParent = 0);
+
+ /** Defines folder @a strPath. */
+ void setPath(const QString &strPath);
+ /** Returns folder path. */
+ QString path() const;
+
+ /** Defines folder @a strName. */
+ void setName(const QString &strName);
+ /** Returns folder name. */
+ QString name() const;
+
+ /** Defines whether folder is @a fWritable. */
+ void setWriteable(bool fWriteable);
+ /** Returns whether folder is writable. */
+ bool isWriteable() const;
+
+ /** Defines whether folder supports @a fAutoMount. */
+ void setAutoMount(bool fAutoMount);
+ /** Returns whether folder supports auto mount. */
+ bool isAutoMounted() const;
+
+ /** Defines folder @a strAutoMountPoint. */
+ void setAutoMountPoint(const QString &strAutoMountPoint);
+ /** Returns folder auto mount point. */
+ QString autoMountPoint() const;
+
+ /** Defines whether folder is @a fPermanent. */
+ void setPermanent(bool fPermanent);
+ /** Returns whether folder is permanent. */
+ bool isPermanent() const;
+
+protected:
+
+ /** Handles translation event. */
+ void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Holds signal about folder path selected. */
+ void sltSelectPath();
+ /** Checks editor validness. */
+ void sltValidate();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** @name Arguments
+ * @{ */
+ /** Holds editor type. */
+ EditorType m_enmType;
+ /** Holds whether folder can be permanent. */
+ bool m_fUsePermanent;
+ /** Holds existing folder names. */
+ QStringList m_usedNames;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the path label instance. */
+ QLabel *m_pLabelPath;
+ /** Holds the path selector instance. */
+ UIFilePathSelector *m_pSelectorPath;
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the name editor instance. */
+ QLineEdit *m_pEditorName;
+ /** Holds the auto-mount point label instance. */
+ QLabel *m_pLabelAutoMountPoint;
+ /** Holds the auto-mount point editor instance. */
+ QLineEdit *m_pEditorAutoMountPoint;
+ /** Holds the read-only check-box instance. */
+ QCheckBox *m_pCheckBoxReadonly;
+ /** Holds the auto-mount check-box instance. */
+ QCheckBox *m_pCheckBoxAutoMount;
+ /** Holds the permanent check-box instance. */
+ QCheckBox *m_pCheckBoxPermanent;
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UISharedFolderDetailsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFoldersEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFoldersEditor.cpp
new file mode 100644
index 00000000..985265ff
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFoldersEditor.cpp
@@ -0,0 +1,853 @@
+/* $Id: UISharedFoldersEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UISharedFoldersEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QMenu>
+#include <QRegExp>
+#include <QTimer>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILabelSeparator.h"
+#include "QIToolBar.h"
+#include "QITreeWidget.h"
+#include "UIIconPool.h"
+#include "UISharedFolderDetailsEditor.h"
+#include "UISharedFoldersEditor.h"
+#include "VBoxUtils.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** Shared Folder tree-widget item. */
+class SFTreeViewItem : public QITreeWidgetItem, public UIDataSharedFolder
+{
+ Q_OBJECT;
+
+public:
+
+ /** Format type. */
+ enum FormatType
+ {
+ FormatType_Invalid,
+ FormatType_EllipsisStart,
+ FormatType_EllipsisMiddle,
+ FormatType_EllipsisEnd,
+ FormatType_EllipsisFile
+ };
+
+ /** Constructs shared folder type (root) item.
+ * @param pParent Brings the item parent.
+ * @param enmFormat Brings the item format type. */
+ SFTreeViewItem(QITreeWidget *pParent, FormatType enmFormat);
+ /** Constructs shared folder (child) item.
+ * @param pParent Brings the item parent.
+ * @param enmFormat Brings the item format type. */
+ SFTreeViewItem(SFTreeViewItem *pParent, FormatType enmFormat);
+
+ /** Returns whether this item is less than the @a other one. */
+ bool operator<(const QTreeWidgetItem &other) const;
+
+ /** Returns child item number @a iIndex. */
+ SFTreeViewItem *child(int iIndex) const;
+
+ /** Returns text of item number @a iIndex. */
+ QString getText(int iIndex) const;
+
+ /** Updates item fields. */
+ void updateFields();
+
+ /** Adjusts item layout. */
+ void adjustText();
+
+protected:
+
+ /** Returns default text. */
+ virtual QString defaultText() const RT_OVERRIDE;
+
+private:
+
+ /** Performs item @a iColumn processing. */
+ void processColumn(int iColumn);
+
+ /** Holds the item format type. */
+ FormatType m_enmFormat;
+ /** Holds the item text fields. */
+ QStringList m_fields;
+};
+
+
+/*********************************************************************************************************************************
+* Class SFTreeViewItem implementation. *
+*********************************************************************************************************************************/
+
+SFTreeViewItem::SFTreeViewItem(QITreeWidget *pParent, FormatType enmFormat)
+ : QITreeWidgetItem(pParent)
+ , m_enmFormat(enmFormat)
+{
+ setFirstColumnSpanned(true);
+ setFlags(flags() ^ Qt::ItemIsSelectable);
+}
+
+SFTreeViewItem::SFTreeViewItem(SFTreeViewItem *pParent, FormatType enmFormat)
+ : QITreeWidgetItem(pParent)
+ , m_enmFormat(enmFormat)
+{
+}
+
+bool SFTreeViewItem::operator<(const QTreeWidgetItem &other) const
+{
+ /* Root items should always been sorted by type field: */
+ return parentItem() ? text(0) < other.text(0) :
+ text(1) < other.text(1);
+}
+
+SFTreeViewItem *SFTreeViewItem::child(int iIndex) const
+{
+ QTreeWidgetItem *pItem = QTreeWidgetItem::child(iIndex);
+ return pItem ? static_cast<SFTreeViewItem*>(pItem) : 0;
+}
+
+QString SFTreeViewItem::getText(int iIndex) const
+{
+ return iIndex >= 0 && iIndex < m_fields.size() ? m_fields.at(iIndex) : QString();
+}
+
+void SFTreeViewItem::updateFields()
+{
+ /* Clear fields: */
+ m_fields.clear();
+
+ /* For root items: */
+ if (!parentItem())
+ m_fields << m_strName
+ << QString::number((int)m_enmType);
+ /* For child items: */
+ else
+ m_fields << m_strName
+ << m_strPath
+ << (m_fWritable ? tr("Full") : tr("Read-only"))
+ << (m_fAutoMount ? tr("Yes") : "")
+ << m_strAutoMountPoint;
+
+ /* Adjust item layout: */
+ adjustText();
+}
+
+void SFTreeViewItem::adjustText()
+{
+ for (int i = 0; i < treeWidget()->columnCount(); ++i)
+ processColumn(i);
+}
+
+QString SFTreeViewItem::defaultText() const
+{
+ return parentItem()
+ ? tr("%1, %2: %3, %4: %5, %6: %7, %8: %9",
+ "col.1 text, col.2 name: col.2 text, col.3 name: col.3 text, col.4 name: col.4 text, col.5 name: col.5 text")
+ .arg(text(0))
+ .arg(parentTree()->headerItem()->text(1)).arg(text(1))
+ .arg(parentTree()->headerItem()->text(2)).arg(text(2))
+ .arg(parentTree()->headerItem()->text(3)).arg(text(3))
+ .arg(parentTree()->headerItem()->text(4)).arg(text(4))
+ : text(0);
+}
+
+void SFTreeViewItem::processColumn(int iColumn)
+{
+ QString strOneString = getText(iColumn);
+ if (strOneString.isNull())
+ return;
+ const QFontMetrics fm = treeWidget()->fontMetrics();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const int iOldSize = fm.horizontalAdvance(strOneString);
+#else
+ const int iOldSize = fm.width(strOneString);
+#endif
+ const int iItemIndent = parentItem() ? treeWidget()->indentation() * 2 : treeWidget()->indentation();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iIndentSize = fm.horizontalAdvance(" ... ");
+#else
+ int iIndentSize = fm.width(" ... ");
+#endif
+ if (iColumn == 0)
+ iIndentSize += iItemIndent;
+ const int cWidth = !parentItem() ? treeWidget()->viewport()->width() : treeWidget()->columnWidth(iColumn);
+
+ /* Compress text: */
+ int iStart = 0;
+ int iFinish = 0;
+ int iPosition = 0;
+ int iTextWidth = 0;
+ do
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ iTextWidth = fm.horizontalAdvance(strOneString);
+#else
+ iTextWidth = fm.width(strOneString);
+#endif
+ if ( iTextWidth
+ && iTextWidth + iIndentSize > cWidth)
+ {
+ iStart = 0;
+ iFinish = strOneString.length();
+
+ /* Selecting remove position: */
+ switch (m_enmFormat)
+ {
+ case FormatType_EllipsisStart:
+ iPosition = iStart;
+ break;
+ case FormatType_EllipsisMiddle:
+ iPosition = (iFinish - iStart) / 2;
+ break;
+ case FormatType_EllipsisEnd:
+ iPosition = iFinish - 1;
+ break;
+ case FormatType_EllipsisFile:
+ {
+ const QRegExp regExp("([\\\\/][^\\\\^/]+[\\\\/]?$)");
+ const int iNewFinish = regExp.indexIn(strOneString);
+ if (iNewFinish != -1)
+ iFinish = iNewFinish;
+ iPosition = (iFinish - iStart) / 2;
+ break;
+ }
+ default:
+ AssertMsgFailed(("Invalid format type\n"));
+ }
+
+ if (iPosition == iFinish)
+ break;
+
+ strOneString.remove(iPosition, 1);
+ }
+ }
+ while ( iTextWidth
+ && (iTextWidth + iIndentSize > cWidth));
+
+ if (iPosition || m_enmFormat == FormatType_EllipsisFile)
+ strOneString.insert(iPosition, "...");
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const int iNewSize = fm.horizontalAdvance(strOneString);
+#else
+ const int iNewSize = fm.width(strOneString);
+#endif
+ setText(iColumn, iNewSize < iOldSize ? strOneString : m_fields.at(iColumn));
+ setToolTip(iColumn, text(iColumn) == getText(iColumn) ? QString() : getText(iColumn));
+
+ /* Calculate item's size-hint: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ setSizeHint(iColumn, QSize(fm.horizontalAdvance(QString(" %1 ").arg(getText(iColumn))), fm.height()));
+#else
+ setSizeHint(iColumn, QSize(fm.width(QString(" %1 ").arg(getText(iColumn))), fm.height()));
+#endif
+}
+
+
+/*********************************************************************************************************************************
+* Class UISharedFoldersEditor implementation. *
+*********************************************************************************************************************************/
+
+UISharedFoldersEditor::UISharedFoldersEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLabelSeparator(0)
+ , m_pLayoutTree(0)
+ , m_pTreeWidget(0)
+ , m_pToolbar(0)
+ , m_pActionAdd(0)
+ , m_pActionEdit(0)
+ , m_pActionRemove(0)
+{
+ prepare();
+}
+
+void UISharedFoldersEditor::setValue(const QList<UIDataSharedFolder> &guiValue)
+{
+ /* Update cached value and
+ * tree-widget if value has changed: */
+ if (m_guiValue != guiValue)
+ {
+ m_guiValue = guiValue;
+ reloadTree();
+ }
+}
+
+QList<UIDataSharedFolder> UISharedFoldersEditor::value() const
+{
+ /* Sanity check: */
+ if (!m_pTreeWidget)
+ return m_guiValue;
+
+ /* Prepare result: */
+ QList<UIDataSharedFolder> result;
+
+ /* For each folder type: */
+ QTreeWidgetItem *pMainRootItem = m_pTreeWidget->invisibleRootItem();
+ for (int iFolderTypeIndex = 0; iFolderTypeIndex < pMainRootItem->childCount(); ++iFolderTypeIndex)
+ {
+ /* Get folder root item: */
+ const SFTreeViewItem *pFolderTypeRoot = static_cast<SFTreeViewItem*>(pMainRootItem->child(iFolderTypeIndex));
+
+ /* For each folder of current type: */
+ for (int iFolderIndex = 0; iFolderIndex < pFolderTypeRoot->childCount(); ++iFolderIndex)
+ {
+ /* Gather and cache new data: */
+ const SFTreeViewItem *pItem = static_cast<SFTreeViewItem*>(pFolderTypeRoot->child(iFolderIndex));
+ result << *pItem;
+ }
+ }
+
+ /* Return result: */
+ return result;
+}
+
+void UISharedFoldersEditor::setFeatureAvailable(bool fAvailable)
+{
+ if (m_pLabelSeparator)
+ m_pLabelSeparator->setEnabled(fAvailable);
+ if (m_pTreeWidget)
+ m_pTreeWidget->setEnabled(fAvailable);
+ if (m_pToolbar)
+ m_pToolbar->setEnabled(fAvailable);
+}
+
+void UISharedFoldersEditor::setFoldersAvailable(UISharedFolderType enmType, bool fAvailable)
+{
+ m_foldersAvailable[enmType] = fAvailable;
+ updateRootItemsVisibility();
+}
+
+void UISharedFoldersEditor::retranslateUi()
+{
+ /* Translate separator label: */
+ if (m_pLabelSeparator)
+ m_pLabelSeparator->setText(tr("Shared &Folders"));
+
+ /* Translate tree-widget: */
+ if (m_pTreeWidget)
+ {
+ m_pTreeWidget->setWhatsThis(tr("Lists all shared folders accessible to this machine. Use 'net use x: \\\\vboxsvr\\share' "
+ "to access a shared folder named 'share' from a DOS-like OS, or 'mount -t vboxsf "
+ "share mount_point' to access it from a Linux OS. This feature requires Guest Additions."));
+
+ /* Translate tree-widget header: */
+ QTreeWidgetItem *pTreeWidgetHeaderItem = m_pTreeWidget->headerItem();
+ if (pTreeWidgetHeaderItem)
+ {
+ pTreeWidgetHeaderItem->setText(4, tr("At"));
+ pTreeWidgetHeaderItem->setText(3, tr("Auto Mount"));
+ pTreeWidgetHeaderItem->setText(2, tr("Access"));
+ pTreeWidgetHeaderItem->setText(1, tr("Path"));
+ pTreeWidgetHeaderItem->setText(0, tr("Name"));
+ }
+
+ /* Update tree-widget contents finally: */
+ reloadTree();
+ }
+
+ /* Translate actions: */
+ if (m_pActionAdd)
+ {
+ m_pActionAdd->setText(tr("Add Shared Folder"));
+ m_pActionAdd->setToolTip(tr("Adds new shared folder."));
+ }
+ if (m_pActionEdit)
+ {
+ m_pActionEdit->setText(tr("Edit Shared Folder"));
+ m_pActionEdit->setToolTip(tr("Edits selected shared folder."));
+ }
+ if (m_pActionRemove)
+ {
+ m_pActionRemove->setText(tr("Remove Shared Folder"));
+ m_pActionRemove->setToolTip(tr("Removes selected shared folder."));
+ }
+}
+
+void UISharedFoldersEditor::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI<QWidget>::showEvent(pEvent);
+
+ /* Connect header-resize signal just before widget is shown
+ * after all the items properly loaded and initialized: */
+ connect(m_pTreeWidget->header(), &QHeaderView::sectionResized,
+ this, &UISharedFoldersEditor::sltAdjustTreeFields,
+ Qt::UniqueConnection);
+
+ /* Adjusting size after all pending show events are processed: */
+ QTimer::singleShot(0, this, SLOT(sltAdjustTree()));
+}
+
+void UISharedFoldersEditor::resizeEvent(QResizeEvent * /* pEvent */)
+{
+ sltAdjustTree();
+}
+
+void UISharedFoldersEditor::sltAdjustTree()
+{
+ /*
+ * Calculates required columns sizes to max out column 2
+ * and let all other columns stay at their minimum sizes.
+ *
+ * Columns
+ * 0 = Tree view / name
+ * 1 = Path
+ * 2 = Writable flag
+ * 3 = Auto-mount flag
+ * 4 = Auto mount point
+ */
+ QAbstractItemView *pItemView = m_pTreeWidget;
+ QHeaderView *pItemHeader = m_pTreeWidget->header();
+ const int iTotal = m_pTreeWidget->viewport()->width();
+
+ const int mw0 = qMax(pItemView->sizeHintForColumn(0), pItemHeader->sectionSizeHint(0));
+ const int mw2 = qMax(pItemView->sizeHintForColumn(2), pItemHeader->sectionSizeHint(2));
+ const int mw3 = qMax(pItemView->sizeHintForColumn(3), pItemHeader->sectionSizeHint(3));
+ const int mw4 = qMax(pItemView->sizeHintForColumn(4), pItemHeader->sectionSizeHint(4));
+#if 0 /** @todo Neither approach is perfect. Short folder names, short paths, plenty of white space, but there is often '...' in column 0. */
+
+ const int w0 = mw0 < iTotal / 5 ? mw0 : iTotal / 5;
+ const int w2 = mw2 < iTotal / 5 ? mw2 : iTotal / 5;
+ const int w3 = mw3 < iTotal / 5 ? mw3 : iTotal / 5;
+ const int w4 = mw4 < iTotal / 5 ? mw4 : iTotal / 5;
+
+ /* Giving 1st column all the available space. */
+ const int w1 = iTotal - w0 - w2 - w3 - w4;
+#else
+ const int mw1 = qMax(pItemView->sizeHintForColumn(1), pItemHeader->sectionSizeHint(1));
+ const int iHintTotal = mw0 + mw1 + mw2 + mw3 + mw4;
+ int w0, w1, w2, w3, w4;
+ int cExcess = iTotal - iHintTotal;
+ if (cExcess >= 0)
+ {
+ /* give excess width to column 1 (path) */
+ w0 = mw0;
+ w1 = mw1 + cExcess;
+ w2 = mw2;
+ w3 = mw3;
+ w4 = mw4;
+ }
+ else
+ {
+ w0 = mw0 < iTotal / 5 ? mw0 : iTotal / 5;
+ w2 = mw2 < iTotal / 5 ? mw2 : iTotal / 5;
+ w3 = mw3 < iTotal / 5 ? mw3 : iTotal / 5;
+ w4 = mw4 < iTotal / 5 ? mw4 : iTotal / 5;
+ w1 = iTotal - w0 - w2 - w3 - w4;
+ }
+#endif
+ m_pTreeWidget->setColumnWidth(0, w0);
+ m_pTreeWidget->setColumnWidth(1, w1);
+ m_pTreeWidget->setColumnWidth(2, w2);
+ m_pTreeWidget->setColumnWidth(3, w3);
+ m_pTreeWidget->setColumnWidth(4, w4);
+}
+
+void UISharedFoldersEditor::sltAdjustTreeFields()
+{
+ QTreeWidgetItem *pMainRoot = m_pTreeWidget->invisibleRootItem();
+ for (int i = 0; i < pMainRoot->childCount(); ++i)
+ {
+ SFTreeViewItem *pSubRoot = static_cast<SFTreeViewItem*>(pMainRoot->child(i));
+ pSubRoot->adjustText();
+ for (int j = 0; j < pSubRoot->childCount(); ++j)
+ {
+ SFTreeViewItem *pItem = static_cast<SFTreeViewItem*>(pSubRoot->child(j));
+ pItem->adjustText();
+ }
+ }
+}
+
+void UISharedFoldersEditor::sltHandleCurrentItemChange(QTreeWidgetItem *pCurrentItem)
+{
+ if (pCurrentItem && pCurrentItem->parent() && !pCurrentItem->isSelected())
+ pCurrentItem->setSelected(true);
+ const bool fAddEnabled = pCurrentItem;
+ const bool fRemoveEnabled = fAddEnabled && pCurrentItem->parent();
+ m_pActionAdd->setEnabled(fAddEnabled);
+ m_pActionEdit->setEnabled(fRemoveEnabled);
+ m_pActionRemove->setEnabled(fRemoveEnabled);
+}
+
+void UISharedFoldersEditor::sltHandleDoubleClick(QTreeWidgetItem *pItem)
+{
+ const bool fEditEnabled = pItem && pItem->parent();
+ if (fEditEnabled)
+ sltEditFolder();
+}
+
+void UISharedFoldersEditor::sltHandleContextMenuRequest(const QPoint &position)
+{
+ QMenu menu;
+ QTreeWidgetItem *pItem = m_pTreeWidget->itemAt(position);
+ if (m_pTreeWidget->isEnabled() && pItem && pItem->flags() & Qt::ItemIsSelectable)
+ {
+ menu.addAction(m_pActionEdit);
+ menu.addAction(m_pActionRemove);
+ }
+ else
+ {
+ menu.addAction(m_pActionAdd);
+ }
+ if (!menu.isEmpty())
+ menu.exec(m_pTreeWidget->viewport()->mapToGlobal(position));
+}
+
+void UISharedFoldersEditor::sltAddFolder()
+{
+ /* Configure shared folder details editor: */
+ UISharedFolderDetailsEditor dlgFolderDetails(UISharedFolderDetailsEditor::EditorType_Add,
+ m_foldersAvailable.value(UISharedFolderType_Console),
+ usedList(true),
+ this);
+
+ /* Run folder details dialog: */
+ if (dlgFolderDetails.exec() == QDialog::Accepted)
+ {
+ const QString strName = dlgFolderDetails.name();
+ const QString strPath = dlgFolderDetails.path();
+ const UISharedFolderType enmType = dlgFolderDetails.isPermanent() ? UISharedFolderType_Machine : UISharedFolderType_Console;
+ /* Shared folder's name & path could not be empty: */
+ Assert(!strName.isEmpty() && !strPath.isEmpty());
+
+ /* Prepare new data: */
+ UIDataSharedFolder newFolderData;
+ newFolderData.m_enmType = enmType;
+ newFolderData.m_strName = strName;
+ newFolderData.m_strPath = strPath;
+ newFolderData.m_fWritable = dlgFolderDetails.isWriteable();
+ newFolderData.m_fAutoMount = dlgFolderDetails.isAutoMounted();
+ newFolderData.m_strAutoMountPoint = dlgFolderDetails.autoMountPoint();
+
+ /* Add new folder item: */
+ addSharedFolderItem(newFolderData, true /* its new? */);
+
+ /* Sort tree-widget before adjusting: */
+ m_pTreeWidget->sortItems(0, Qt::AscendingOrder);
+ /* Adjust tree-widget finally: */
+ sltAdjustTree();
+ }
+}
+
+void UISharedFoldersEditor::sltEditFolder()
+{
+ /* Check current folder item: */
+ SFTreeViewItem *pItem = static_cast<SFTreeViewItem*>(m_pTreeWidget->currentItem());
+ AssertPtrReturnVoid(pItem);
+ AssertPtrReturnVoid(pItem->parentItem());
+
+ /* Configure shared folder details editor: */
+ UISharedFolderDetailsEditor dlgFolderDetails(UISharedFolderDetailsEditor::EditorType_Edit,
+ m_foldersAvailable.value(UISharedFolderType_Console),
+ usedList(false),
+ this);
+ dlgFolderDetails.setPath(pItem->m_strPath);
+ dlgFolderDetails.setName(pItem->m_strName);
+ dlgFolderDetails.setPermanent(pItem->m_enmType == UISharedFolderType_Machine);
+ dlgFolderDetails.setWriteable(pItem->m_fWritable);
+ dlgFolderDetails.setAutoMount(pItem->m_fAutoMount);
+ dlgFolderDetails.setAutoMountPoint(pItem->m_strAutoMountPoint);
+
+ /* Run folder details dialog: */
+ if (dlgFolderDetails.exec() == QDialog::Accepted)
+ {
+ const QString strName = dlgFolderDetails.name();
+ const QString strPath = dlgFolderDetails.path();
+ const UISharedFolderType enmType = dlgFolderDetails.isPermanent() ? UISharedFolderType_Machine : UISharedFolderType_Console;
+ /* Shared folder's name & path could not be empty: */
+ Assert(!strName.isEmpty() && !strPath.isEmpty());
+
+ /* Update edited tree-widget item: */
+ pItem->m_enmType = enmType;
+ pItem->m_strName = strName;
+ pItem->m_strPath = strPath;
+ pItem->m_fWritable = dlgFolderDetails.isWriteable();
+ pItem->m_fAutoMount = dlgFolderDetails.isAutoMounted();
+ pItem->m_strAutoMountPoint = dlgFolderDetails.autoMountPoint();
+ pItem->updateFields();
+
+ /* Searching for a root of the edited tree-widget item: */
+ SFTreeViewItem *pRoot = root(enmType);
+ if (pItem->parentItem() != pRoot)
+ {
+ /* Move the tree-widget item to a new location: */
+ pItem->parentItem()->takeChild(pItem->parentItem()->indexOfChild(pItem));
+ pRoot->insertChild(pRoot->childCount(), pItem);
+
+ /* Update tree-widget: */
+ m_pTreeWidget->scrollToItem(pItem);
+ m_pTreeWidget->setCurrentItem(pItem);
+ sltHandleCurrentItemChange(pItem);
+ }
+
+ /* Sort tree-widget before adjusting: */
+ m_pTreeWidget->sortItems(0, Qt::AscendingOrder);
+ /* Adjust tree-widget finally: */
+ sltAdjustTree();
+ }
+}
+
+void UISharedFoldersEditor::sltRemoveFolder()
+{
+ /* Check current folder item: */
+ QTreeWidgetItem *pItem = m_pTreeWidget->currentItem();
+ AssertPtrReturnVoid(pItem);
+
+ /* Delete corresponding item: */
+ delete pItem;
+
+ /* Adjust tree-widget finally: */
+ sltAdjustTree();
+}
+
+void UISharedFoldersEditor::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UISharedFoldersEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare separator: */
+ m_pLabelSeparator = new QILabelSeparator(this);
+ if (m_pLabelSeparator)
+ pLayout->addWidget(m_pLabelSeparator);
+
+ /* Prepare view layout: */
+ m_pLayoutTree = new QHBoxLayout;
+ if (m_pLayoutTree)
+ {
+ m_pLayoutTree->setContentsMargins(0, 0, 0, 0);
+ m_pLayoutTree->setSpacing(3);
+
+ /* Prepare tree-widget: */
+ prepareTreeWidget();
+ /* Prepare toolbar: */
+ prepareToolbar();
+
+ pLayout->addLayout(m_pLayoutTree);
+ }
+ }
+}
+
+void UISharedFoldersEditor::prepareTreeWidget()
+{
+ /* Prepare shared folders tree-widget: */
+ m_pTreeWidget = new QITreeWidget(this);
+ if (m_pTreeWidget)
+ {
+ if (m_pLabelSeparator)
+ m_pLabelSeparator->setBuddy(m_pTreeWidget);
+ m_pTreeWidget->header()->setSectionsMovable(false);
+ m_pTreeWidget->setMinimumSize(QSize(0, 200));
+ m_pTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTreeWidget->setUniformRowHeights(true);
+ m_pTreeWidget->setAllColumnsShowFocus(true);
+
+ m_pLayoutTree->addWidget(m_pTreeWidget);
+ }
+}
+
+void UISharedFoldersEditor::prepareToolbar()
+{
+ /* Prepare shared folders toolbar: */
+ m_pToolbar = new QIToolBar(this);
+ if (m_pToolbar)
+ {
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pToolbar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolbar->setOrientation(Qt::Vertical);
+
+ /* Prepare 'add shared folder' action: */
+ m_pActionAdd = m_pToolbar->addAction(UIIconPool::iconSet(":/sf_add_16px.png",
+ ":/sf_add_disabled_16px.png"),
+ QString(), this, SLOT(sltAddFolder()));
+ if (m_pActionAdd)
+ m_pActionAdd->setShortcuts(QList<QKeySequence>() << QKeySequence("Ins") << QKeySequence("Ctrl+N"));
+
+ /* Prepare 'edit shared folder' action: */
+ m_pActionEdit = m_pToolbar->addAction(UIIconPool::iconSet(":/sf_edit_16px.png",
+ ":/sf_edit_disabled_16px.png"),
+ QString(), this, SLOT(sltEditFolder()));
+ if (m_pActionEdit)
+ m_pActionEdit->setShortcuts(QList<QKeySequence>() << QKeySequence("Space") << QKeySequence("F2"));
+
+ /* Prepare 'remove shared folder' action: */
+ m_pActionRemove = m_pToolbar->addAction(UIIconPool::iconSet(":/sf_remove_16px.png",
+ ":/sf_remove_disabled_16px.png"),
+ QString(), this, SLOT(sltRemoveFolder()));
+ if (m_pActionRemove)
+ m_pActionRemove->setShortcuts(QList<QKeySequence>() << QKeySequence("Del") << QKeySequence("Ctrl+R"));
+
+ m_pLayoutTree->addWidget(m_pToolbar);
+ }
+}
+
+void UISharedFoldersEditor::prepareConnections()
+{
+ /* Configure tree-widget connections: */
+ connect(m_pTreeWidget, &QITreeWidget::currentItemChanged,
+ this, &UISharedFoldersEditor::sltHandleCurrentItemChange);
+ connect(m_pTreeWidget, &QITreeWidget::itemDoubleClicked,
+ this, &UISharedFoldersEditor::sltHandleDoubleClick);
+ connect(m_pTreeWidget, &QITreeWidget::customContextMenuRequested,
+ this, &UISharedFoldersEditor::sltHandleContextMenuRequest);
+}
+
+QStringList UISharedFoldersEditor::usedList(bool fIncludeSelected)
+{
+ /* Make the used names list: */
+ QStringList list;
+ QTreeWidgetItemIterator it(m_pTreeWidget);
+ while (*it)
+ {
+ if ((*it)->parent() && (fIncludeSelected || !(*it)->isSelected()))
+ list << static_cast<SFTreeViewItem*>(*it)->getText(0);
+ ++it;
+ }
+ return list;
+}
+
+SFTreeViewItem *UISharedFoldersEditor::root(UISharedFolderType enmSharedFolderType)
+{
+ /* Search for the corresponding root item among all the top-level items: */
+ SFTreeViewItem *pRootItem = 0;
+ QTreeWidgetItem *pMainRootItem = m_pTreeWidget->invisibleRootItem();
+ for (int iFolderTypeIndex = 0; iFolderTypeIndex < pMainRootItem->childCount(); ++iFolderTypeIndex)
+ {
+ /* Get iterated item: */
+ SFTreeViewItem *pIteratedItem = static_cast<SFTreeViewItem*>(pMainRootItem->child(iFolderTypeIndex));
+ /* If iterated item type is what we are looking for: */
+ if (pIteratedItem->m_enmType == enmSharedFolderType)
+ {
+ /* Remember the item: */
+ pRootItem = static_cast<SFTreeViewItem*>(pIteratedItem);
+ /* And break further search: */
+ break;
+ }
+ }
+ /* Return root item: */
+ return pRootItem;
+}
+
+void UISharedFoldersEditor::setRootItemVisible(UISharedFolderType enmSharedFolderType, bool fVisible)
+{
+ /* Search for the corresponding root item among all the top-level items: */
+ SFTreeViewItem *pRootItem = root(enmSharedFolderType);
+ /* If root item, we are looking for, still not found: */
+ if (!pRootItem)
+ {
+ /* Create new shared folder type item: */
+ pRootItem = new SFTreeViewItem(m_pTreeWidget, SFTreeViewItem::FormatType_EllipsisEnd);
+ if (pRootItem)
+ {
+ /* Configure item: */
+ pRootItem->m_enmType = enmSharedFolderType;
+ switch (enmSharedFolderType)
+ {
+ case UISharedFolderType_Machine: pRootItem->m_strName = tr(" Machine Folders"); break;
+ case UISharedFolderType_Console: pRootItem->m_strName = tr(" Transient Folders"); break;
+ default: break;
+ }
+ pRootItem->updateFields();
+ }
+ }
+ /* Expand/collaps it if necessary: */
+ pRootItem->setExpanded(fVisible);
+ /* And hide/show it if necessary: */
+ pRootItem->setHidden(!fVisible);
+}
+
+void UISharedFoldersEditor::updateRootItemsVisibility()
+{
+ /* Update (show/hide) machine (permanent) root item: */
+ setRootItemVisible(UISharedFolderType_Machine, m_foldersAvailable.value(UISharedFolderType_Machine));
+ /* Update (show/hide) console (temporary) root item: */
+ setRootItemVisible(UISharedFolderType_Console, m_foldersAvailable.value(UISharedFolderType_Console));
+}
+
+void UISharedFoldersEditor::addSharedFolderItem(const UIDataSharedFolder &sharedFolderData, bool fChoose)
+{
+ /* Create shared folder item: */
+ SFTreeViewItem *pItem = new SFTreeViewItem(root(sharedFolderData.m_enmType), SFTreeViewItem::FormatType_EllipsisFile);
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->m_enmType = sharedFolderData.m_enmType;
+ pItem->m_strName = sharedFolderData.m_strName;
+ pItem->m_strPath = sharedFolderData.m_strPath;
+ pItem->m_fWritable = sharedFolderData.m_fWritable;
+ pItem->m_fAutoMount = sharedFolderData.m_fAutoMount;
+ pItem->m_strAutoMountPoint = sharedFolderData.m_strAutoMountPoint;
+ pItem->updateFields();
+
+ /* Select this item if it's new: */
+ if (fChoose)
+ {
+ m_pTreeWidget->scrollToItem(pItem);
+ m_pTreeWidget->setCurrentItem(pItem);
+ sltHandleCurrentItemChange(pItem);
+ }
+ }
+}
+
+void UISharedFoldersEditor::reloadTree()
+{
+ /* Sanity check: */
+ if (!m_pTreeWidget)
+ return;
+
+ /* Clear list initially: */
+ m_pTreeWidget->clear();
+
+ /* Update root items visibility: */
+ updateRootItemsVisibility();
+
+ /* For each folder => load it from cache: */
+ foreach (const UIDataSharedFolder &guiData, m_guiValue)
+ addSharedFolderItem(guiData, false /* its new? */);
+
+ /* Choose first folder as current: */
+ m_pTreeWidget->setCurrentItem(m_pTreeWidget->topLevelItem(0));
+ sltHandleCurrentItemChange(m_pTreeWidget->currentItem());
+}
+
+
+#include "UISharedFoldersEditor.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFoldersEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFoldersEditor.h
new file mode 100644
index 00000000..5f99e1a5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISharedFoldersEditor.h
@@ -0,0 +1,196 @@
+/* $Id: UISharedFoldersEditor.h $ */
+/** @file
+ * VBox Qt GUI - UISharedFoldersEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UISharedFoldersEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UISharedFoldersEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+
+/* Forward declartions: */
+class QHBoxLayout;
+class QTreeWidgetItem;
+class QILabelSeparator;
+class QIToolBar;
+class QITreeWidget;
+class SFTreeViewItem;
+
+/** Shared Folder data. */
+struct UIDataSharedFolder
+{
+ /** Constructs data. */
+ UIDataSharedFolder()
+ : m_enmType(UISharedFolderType_Machine)
+ , m_strName()
+ , m_strPath()
+ , m_fWritable(false)
+ , m_fAutoMount(false)
+ , m_strAutoMountPoint()
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSharedFolder &other) const
+ {
+ return true
+ && m_enmType == other.m_enmType
+ && m_strName == other.m_strName
+ && m_strPath == other.m_strPath
+ && m_fWritable == other.m_fWritable
+ && m_fAutoMount == other.m_fAutoMount
+ && m_strAutoMountPoint == other.m_strAutoMountPoint
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSharedFolder &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSharedFolder &other) const { return !equal(other); }
+
+ /** Holds the shared folder type. */
+ UISharedFolderType m_enmType;
+ /** Holds the shared folder name. */
+ QString m_strName;
+ /** Holds the shared folder path. */
+ QString m_strPath;
+ /** Holds whether the shared folder should be writeable. */
+ bool m_fWritable;
+ /** Holds whether the shared folder should be auto-mounted at startup. */
+ bool m_fAutoMount;
+ /** Where in the guest to try auto mount the shared folder (drive for
+ * Windows & OS/2, path for unixy guests). */
+ QString m_strAutoMountPoint;
+};
+
+/** QWidget subclass used as a shared folders editor. */
+class SHARED_LIBRARY_STUFF UISharedFoldersEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UISharedFoldersEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a strValue. */
+ void setValue(const QList<UIDataSharedFolder> &guiValue);
+ /** Returns editor value. */
+ QList<UIDataSharedFolder> value() const;
+
+ /** Defines whether feature @a fAvailable. */
+ void setFeatureAvailable(bool fAvailable);
+ /** Defines whether folders of certain @a enmType are @a fAvailable. */
+ void setFoldersAvailable(UISharedFolderType enmType, bool fAvailable);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Performs request to adjust tree. */
+ void sltAdjustTree();
+ /** Performs request to adjust tree fields. */
+ void sltAdjustTreeFields();
+
+ /** Handles @a pCurrentItem change. */
+ void sltHandleCurrentItemChange(QTreeWidgetItem *pCurrentItem);
+ /** Handles @a pItem double-click. */
+ void sltHandleDoubleClick(QTreeWidgetItem *pItem);
+ /** Handles context menu request for @a position. */
+ void sltHandleContextMenuRequest(const QPoint &position);
+
+ /** Handles command to add shared folder. */
+ void sltAddFolder();
+ /** Handles command to edit shared folder. */
+ void sltEditFolder();
+ /** Handles command to remove shared folder. */
+ void sltRemoveFolder();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepare tree-widget. */
+ void prepareTreeWidget();
+ /** Prepare tool-bar. */
+ void prepareToolbar();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Returns a list of used shared folder names. */
+ QStringList usedList(bool fIncludeSelected);
+
+ /** Returns the tree-view root item for corresponding shared folder @a type. */
+ SFTreeViewItem *root(UISharedFolderType type);
+ /** Defines whether the root item of @a enmFoldersType is @a fVisible. */
+ void setRootItemVisible(UISharedFolderType enmFoldersType, bool fVisible);
+ /** Updates root item visibility. */
+ void updateRootItemsVisibility();
+ /** Creates shared folder item based on passed @a data. */
+ void addSharedFolderItem(const UIDataSharedFolder &sharedFolderData, bool fChoose);
+ /** Reloads tree. */
+ void reloadTree();
+
+ /** Holds the value to be set. */
+ QList<UIDataSharedFolder> m_guiValue;
+
+ /** Holds whether folders of certain type are available. */
+ QMap<UISharedFolderType, bool> m_foldersAvailable;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the widget separator instance. */
+ QILabelSeparator *m_pLabelSeparator;
+ /** Holds the tree layout instance. */
+ QHBoxLayout *m_pLayoutTree;
+ /** Holds the tree-widget instance. */
+ QITreeWidget *m_pTreeWidget;
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolbar;
+ /** Holds the 'add shared folder' action instance. */
+ QAction *m_pActionAdd;
+ /** Holds the 'edit shared folder' action instance. */
+ QAction *m_pActionEdit;
+ /** Holds the 'remove shared folder' action instance. */
+ QAction *m_pActionRemove;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UISharedFoldersEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIShortcutConfigurationEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIShortcutConfigurationEditor.cpp
new file mode 100644
index 00000000..4035d652
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIShortcutConfigurationEditor.cpp
@@ -0,0 +1,1000 @@
+/* $Id: UIShortcutConfigurationEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIShortcutConfigurationEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QItemEditorFactory>
+#include <QTabWidget>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIStyledItemDelegate.h"
+#include "QITableView.h"
+#include "UIActionPool.h"
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIHostComboEditor.h"
+#include "UIHotKeyEditor.h"
+#include "UIMessageCenter.h"
+#include "UIShortcutConfigurationEditor.h"
+#include "UIShortcutPool.h"
+
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+
+
+/** Table column indexes. */
+enum TableColumnIndex
+{
+ TableColumnIndex_Description,
+ TableColumnIndex_Sequence,
+ TableColumnIndex_Max
+};
+
+
+/** QITableViewCell subclass for shortcut configuration editor. */
+class UIShortcutTableViewCell : public QITableViewCell
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs table cell on the basis of passed arguments.
+ * @param pParent Brings the row this cell belongs too.
+ * @param strText Brings the text describing this cell. */
+ UIShortcutTableViewCell(QITableViewRow *pParent, const QString &strText)
+ : QITableViewCell(pParent)
+ , m_strText(strText)
+ {}
+
+ /** Returns the cell text. */
+ virtual QString text() const RT_OVERRIDE { return m_strText; }
+
+private:
+
+ /** Holds the cell text. */
+ QString m_strText;
+};
+
+
+/** QITableViewRow subclass for shortcut configuration editor. */
+class UIShortcutTableViewRow : public QITableViewRow, public UIShortcutConfigurationItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs table row on the basis of passed arguments.
+ * @param pParent Brings the table this row belongs too.
+ * @param item Brings the item this row is based on. */
+ UIShortcutTableViewRow(QITableView *pParent = 0, const UIShortcutConfigurationItem &item = UIShortcutConfigurationItem())
+ : QITableViewRow(pParent)
+ , UIShortcutConfigurationItem(item)
+ {
+ createCells();
+ }
+
+ /** Constructs table row on the basis of @a another one. */
+ UIShortcutTableViewRow(const UIShortcutTableViewRow &another)
+ : QITableViewRow(another.table())
+ , UIShortcutConfigurationItem(another)
+ {
+ createCells();
+ }
+
+ /** Destructs table row. */
+ virtual ~UIShortcutTableViewRow() RT_OVERRIDE
+ {
+ destroyCells();
+ }
+
+ /** Copies a table row from @a another one. */
+ UIShortcutTableViewRow &operator=(const UIShortcutTableViewRow &another)
+ {
+ /* Reassign variables: */
+ setTable(another.table());
+ UIShortcutConfigurationItem::operator=(another);
+
+ /* Recreate cells: */
+ destroyCells();
+ createCells();
+
+ /* Return this: */
+ return *this;
+ }
+
+ /** Returns whether this row equals to @a another one. */
+ bool operator==(const UIShortcutTableViewRow &another) const
+ {
+ /* Compare variables: */
+ return UIShortcutConfigurationItem::operator==(another);
+ }
+
+protected:
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE
+ {
+ return TableColumnIndex_Max;
+ }
+
+ /** Returns the child item with @a iIndex. */
+ virtual QITableViewCell *childItem(int iIndex) const RT_OVERRIDE
+ {
+ switch (iIndex)
+ {
+ case TableColumnIndex_Description: return m_cells.first;
+ case TableColumnIndex_Sequence: return m_cells.second;
+ default: break;
+ }
+ return 0;
+ }
+
+private:
+
+ /** Creates cells. */
+ void createCells()
+ {
+ /* Create cells on the basis of description and current sequence: */
+ m_cells = qMakePair(new UIShortcutTableViewCell(this, description()),
+ new UIShortcutTableViewCell(this, currentSequence()));
+ }
+
+ /** Destroys cells. */
+ void destroyCells()
+ {
+ /* Destroy cells: */
+ delete m_cells.first;
+ delete m_cells.second;
+ m_cells.first = 0;
+ m_cells.second = 0;
+ }
+
+ /** Holds the cell instances. */
+ QPair<UIShortcutTableViewCell*, UIShortcutTableViewCell*> m_cells;
+};
+
+/** Shortcut configuration editor row list. */
+typedef QList<UIShortcutTableViewRow> UIShortcutTableViewContent;
+
+
+/** Shortcut item sorting functor. */
+class UIShortcutItemSortingFunctor
+{
+public:
+
+ /** Constructs shortcut item sorting functor.
+ * @param iColumn Brings the column sorting should be done according to.
+ * @param m_enmOrder Brings the sorting order to be applied. */
+ UIShortcutItemSortingFunctor(int iColumn, Qt::SortOrder enmOrder)
+ : m_iColumn(iColumn)
+ , m_enmOrder(enmOrder)
+ {}
+
+ /** Returns whether the @a item1 is more/less than the @a item2.
+ * @note Order depends on the one set through constructor, stored in m_enmOrder. */
+ bool operator()(const UIShortcutTableViewRow &item1, const UIShortcutTableViewRow &item2)
+ {
+ switch (m_iColumn)
+ {
+ case TableColumnIndex_Description:
+ return m_enmOrder == Qt::AscendingOrder
+ ? item1.description() < item2.description()
+ : item1.description() > item2.description();
+ case TableColumnIndex_Sequence:
+ return m_enmOrder == Qt::AscendingOrder
+ ? item1.currentSequence() < item2.currentSequence()
+ : item1.currentSequence() > item2.currentSequence();
+ default:
+ break;
+ }
+ return m_enmOrder == Qt::AscendingOrder
+ ? item1.key() < item2.key()
+ : item1.key() > item2.key();
+ }
+
+private:
+
+ /** Holds the column sorting should be done according to. */
+ int m_iColumn;
+ /** Holds the sorting order to be applied. */
+ Qt::SortOrder m_enmOrder;
+};
+
+
+/** QAbstractTableModel subclass representing shortcut configuration model. */
+class UIShortcutConfigurationModel : public QAbstractTableModel
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about shortcuts loaded. */
+ void sigShortcutsLoaded();
+ /** Notifies about data changed. */
+ void sigDataChanged();
+
+public:
+
+ /** Constructs model passing @a pParent to the base-class.
+ * @param enmType Brings the action-pool type this model is related to. */
+ UIShortcutConfigurationModel(QObject *pParent, UIActionPoolType enmType);
+
+ /** Defines the parent @a pTable reference. */
+ void setTable(UIShortcutConfigurationTable *pTable);
+
+ /** Returns the number of children. */
+ int childCount() const;
+ /** Returns the child item with @a iIndex. */
+ QITableViewRow *childItem(int iIndex);
+
+ /** Loads a @a list of shortcuts to the model. */
+ void load(const UIShortcutConfigurationList &list);
+ /** Saves the model shortcuts to a @a list. */
+ void save(UIShortcutConfigurationList &list);
+
+ /** Returns whether all shortcuts unique. */
+ bool isAllShortcutsUnique();
+
+public slots:
+
+ /** Handle filtering @a strText change. */
+ void sltHandleFilterTextChange(const QString &strText);
+
+protected:
+
+ /** Returns the number of rows under the given @a parent. */
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const RT_OVERRIDE;
+ /** Returns the number of columns under the given @a parent. */
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const RT_OVERRIDE;
+
+ /** Returns the item flags for the given @a index. */
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const RT_OVERRIDE;
+ /** Returns the data for the given @a iRole and @a iSection in the header with the specified @a enmOrientation. */
+ virtual QVariant headerData(int iSection, Qt::Orientation enmOrientation, int iRole = Qt::DisplayRole) const RT_OVERRIDE;
+ /** Returns the data stored under the given @a iRole for the item referred to by the @a index. */
+ virtual QVariant data(const QModelIndex &index, int iRole = Qt::DisplayRole) const RT_OVERRIDE;
+ /** Sets the @a iRole data for the item at @a index to @a value. */
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int iRole = Qt::EditRole) RT_OVERRIDE;
+
+ /** Sorts the model by @a iColumn in the given @a enmOrder. */
+ virtual void sort(int iColumn, Qt::SortOrder enmOrder = Qt::AscendingOrder) RT_OVERRIDE;
+
+private:
+
+ /** Applies filter. */
+ void applyFilter();
+
+ /** Holds the action-pool type this model is related to. */
+ UIActionPoolType m_enmType;
+
+ /** Holds the parent table reference. */
+ UIShortcutConfigurationTable *m_pTable;
+
+ /** Holds current filter. */
+ QString m_strFilter;
+
+ /** Holds current shortcut list. */
+ UIShortcutTableViewContent m_shortcuts;
+ /** Holds current filtered shortcut list. */
+ UIShortcutTableViewContent m_filteredShortcuts;
+
+ /** Holds a set of currently duplicated sequences. */
+ QSet<QString> m_duplicatedSequences;
+};
+
+
+/** QITableView subclass representing shortcut configuration table. */
+class UIShortcutConfigurationTable : public QITableView
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs table passing @a pParent to the base-class.
+ * @param pModel Brings the model this table is bound to.
+ * @param strObjectName Brings the object name this table has, required for fast referencing. */
+ UIShortcutConfigurationTable(QWidget *pParent, UIShortcutConfigurationModel *pModel, const QString &strObjectName);
+ /** Destructs table. */
+ virtual ~UIShortcutConfigurationTable() RT_OVERRIDE;
+
+protected:
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child item with @a iIndex. */
+ virtual QITableViewRow *childItem(int iIndex) const RT_OVERRIDE;
+
+private slots:
+
+ /** Handles shortcuts loaded signal. */
+ void sltHandleShortcutsLoaded();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the item editor factory instance. */
+ QItemEditorFactory *m_pItemEditorFactory;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIShortcutConfigurationModel implementation. *
+*********************************************************************************************************************************/
+
+UIShortcutConfigurationModel::UIShortcutConfigurationModel(QObject *pParent, UIActionPoolType enmType)
+ : QAbstractTableModel(pParent)
+ , m_enmType(enmType)
+ , m_pTable(0)
+{
+}
+
+void UIShortcutConfigurationModel::setTable(UIShortcutConfigurationTable *pTable)
+{
+ m_pTable = pTable;
+}
+
+int UIShortcutConfigurationModel::childCount() const
+{
+ /* Return row count: */
+ return rowCount();
+}
+
+QITableViewRow *UIShortcutConfigurationModel::childItem(int iIndex)
+{
+ /* Make sure index is within the bounds: */
+ AssertReturn(iIndex >= 0 && iIndex < m_filteredShortcuts.size(), 0);
+ /* Return corresponding filtered row: */
+ return &m_filteredShortcuts[iIndex];
+}
+
+void UIShortcutConfigurationModel::load(const UIShortcutConfigurationList &list)
+{
+ /* Load a list of passed shortcuts: */
+ foreach (const UIShortcutConfigurationItem &item, list)
+ {
+ /* Filter out unnecessary items: */
+ if ( (m_enmType == UIActionPoolType_Manager && item.key().startsWith(GUI_Input_MachineShortcuts))
+ || (m_enmType == UIActionPoolType_Runtime && item.key().startsWith(GUI_Input_SelectorShortcuts)))
+ continue;
+ /* Add suitable item to the model as a new shortcut: */
+ m_shortcuts << UIShortcutTableViewRow(m_pTable, item);
+ }
+ /* Apply filter: */
+ applyFilter();
+ /* Notify table: */
+ emit sigShortcutsLoaded();
+}
+
+void UIShortcutConfigurationModel::save(UIShortcutConfigurationList &list)
+{
+ /* Save cached model shortcuts: */
+ foreach (const UIShortcutTableViewRow &row, m_shortcuts)
+ {
+ const UIShortcutConfigurationItem &item = row;
+
+ /* Search for corresponding item position: */
+ const int iShortcutItemPosition = UIShortcutSearchFunctor<UIShortcutConfigurationItem>()(list, item);
+ /* Make sure position is valid: */
+ if (iShortcutItemPosition == -1)
+ continue;
+ /* Save cached model shortcut to a list: */
+ list[iShortcutItemPosition] = item;
+ }
+}
+
+bool UIShortcutConfigurationModel::isAllShortcutsUnique()
+{
+ /* Enumerate all the sequences: */
+ QMultiMap<QString, QString> usedSequences;
+ foreach (const UIShortcutTableViewRow &item, m_shortcuts)
+ {
+ QString strKey = item.currentSequence();
+ if (!strKey.isEmpty())
+ {
+ const QString strScope = item.scope();
+ strKey = strScope.isNull() ? strKey : QString("%1: %2").arg(strScope, strKey);
+ usedSequences.insert(strKey, item.key());
+ }
+ }
+ /* Enumerate all the duplicated sequences: */
+ QSet<QString> duplicatedSequences;
+ foreach (const QString &strKey, usedSequences.keys())
+ if (usedSequences.count(strKey) > 1)
+ {
+ foreach (const QString &strValue, usedSequences.values(strKey))
+ duplicatedSequences |= strValue;
+ }
+ /* Is there something changed? */
+ if (m_duplicatedSequences != duplicatedSequences)
+ {
+ m_duplicatedSequences = duplicatedSequences;
+ emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+ }
+ /* Are there duplicated shortcuts? */
+ if (!m_duplicatedSequences.isEmpty())
+ return false;
+ /* True by default: */
+ return true;
+}
+
+void UIShortcutConfigurationModel::sltHandleFilterTextChange(const QString &strText)
+{
+ m_strFilter = strText;
+ applyFilter();
+}
+
+int UIShortcutConfigurationModel::rowCount(const QModelIndex& /* parent = QModelIndex() */) const
+{
+ return m_filteredShortcuts.size();
+}
+
+int UIShortcutConfigurationModel::columnCount(const QModelIndex& /* parent = QModelIndex() */) const
+{
+ return TableColumnIndex_Max;
+}
+
+Qt::ItemFlags UIShortcutConfigurationModel::flags(const QModelIndex &index) const
+{
+ /* No flags for invalid index: */
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case TableColumnIndex_Description: return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ case TableColumnIndex_Sequence: return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
+ default: break;
+ }
+ /* No flags by default: */
+ return Qt::NoItemFlags;
+}
+
+QVariant UIShortcutConfigurationModel::headerData(int iSection,
+ Qt::Orientation enmOrientation,
+ int iRole /* = Qt::DisplayRole */) const
+{
+ /* Switch for different roles: */
+ switch (iRole)
+ {
+ case Qt::DisplayRole:
+ {
+ /* Invalid for vertical header: */
+ if (enmOrientation == Qt::Vertical)
+ return QString();
+ /* Switch for different columns: */
+ switch (iSection)
+ {
+ case TableColumnIndex_Description: return UIShortcutConfigurationEditor::tr("Name");
+ case TableColumnIndex_Sequence: return UIShortcutConfigurationEditor::tr("Shortcut");
+ default: break;
+ }
+ /* Invalid for other cases: */
+ return QString();
+ }
+ default:
+ break;
+ }
+ /* Invalid by default: */
+ return QVariant();
+}
+
+QVariant UIShortcutConfigurationModel::data(const QModelIndex &index, int iRole /* = Qt::DisplayRole */) const
+{
+ /* No data for invalid index: */
+ if (!index.isValid())
+ return QVariant();
+ const int iIndex = index.row();
+ /* Switch for different roles: */
+ switch (iRole)
+ {
+ case Qt::DisplayRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case TableColumnIndex_Description:
+ {
+ /* Return shortcut scope and description: */
+ const QString strScope = m_filteredShortcuts[iIndex].scope();
+ const QString strDescription = m_filteredShortcuts[iIndex].description();
+ return strScope.isNull() ? strDescription : QString("%1: %2").arg(strScope, strDescription);
+ }
+ case TableColumnIndex_Sequence:
+ {
+ /* If that is host-combo cell: */
+ if (m_filteredShortcuts[iIndex].key() == UIHostCombo::hostComboCacheKey())
+ /* We should return host-combo: */
+ return UIHostCombo::toReadableString(m_filteredShortcuts[iIndex].currentSequence());
+ /* In other cases we should return hot-combo: */
+ QString strHotCombo = m_filteredShortcuts[iIndex].currentSequence();
+ /* But if that is machine table and hot-combo is not empty: */
+ if (m_enmType == UIActionPoolType_Runtime && !strHotCombo.isEmpty())
+ /* We should prepend it with Host+ prefix: */
+ strHotCombo.prepend(UIHostCombo::hostComboModifierName());
+ /* Return what we've got: */
+ return strHotCombo;
+ }
+ default: break;
+ }
+ /* Invalid for other cases: */
+ return QString();
+ }
+ case Qt::EditRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case TableColumnIndex_Sequence:
+ return m_filteredShortcuts[iIndex].key() == UIHostCombo::hostComboCacheKey()
+ ? QVariant::fromValue(UIHostComboWrapper(m_filteredShortcuts[iIndex].currentSequence()))
+ : QVariant::fromValue(UIHotKey( m_enmType == UIActionPoolType_Runtime
+ ? UIHotKeyType_Simple
+ : UIHotKeyType_WithModifiers,
+ m_filteredShortcuts[iIndex].currentSequence(),
+ m_filteredShortcuts[iIndex].defaultSequence()));
+ default:
+ break;
+ }
+ /* Invalid for other cases: */
+ return QString();
+ }
+ case Qt::FontRole:
+ {
+ /* Do we have a default font? */
+ QFont font(QApplication::font());
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case TableColumnIndex_Sequence:
+ {
+ if ( m_filteredShortcuts[iIndex].key() != UIHostCombo::hostComboCacheKey()
+ && m_filteredShortcuts[iIndex].currentSequence() != m_filteredShortcuts[iIndex].defaultSequence())
+ font.setBold(true);
+ break;
+ }
+ default: break;
+ }
+ /* Return resulting font: */
+ return font;
+ }
+ case Qt::ForegroundRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case TableColumnIndex_Sequence:
+ {
+ if (m_duplicatedSequences.contains(m_filteredShortcuts[iIndex].key()))
+ return QBrush(Qt::red);
+ break;
+ }
+ default: break;
+ }
+ /* Default for other cases: */
+ return QString();
+ }
+ default: break;
+ }
+ /* Invalid by default: */
+ return QVariant();
+}
+
+bool UIShortcutConfigurationModel::setData(const QModelIndex &index, const QVariant &value, int iRole /* = Qt::EditRole */)
+{
+ /* Nothing to set for invalid index: */
+ if (!index.isValid())
+ return false;
+ /* Switch for different roles: */
+ switch (iRole)
+ {
+ case Qt::EditRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case TableColumnIndex_Sequence:
+ {
+ /* Get index: */
+ const int iIndex = index.row();
+ /* Set sequence to shortcut: */
+ UIShortcutTableViewRow &filteredShortcut = m_filteredShortcuts[iIndex];
+ const int iShortcutIndex = UIShortcutSearchFunctor<UIShortcutTableViewRow>()(m_shortcuts, filteredShortcut);
+ if (iShortcutIndex != -1)
+ {
+ filteredShortcut.setCurrentSequence( filteredShortcut.key() == UIHostCombo::hostComboCacheKey()
+ ? value.value<UIHostComboWrapper>().toString()
+ : value.value<UIHotKey>().sequence());
+ m_shortcuts[iShortcutIndex] = filteredShortcut;
+ emit sigDataChanged();
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ /* Nothing to set by default: */
+ return false;
+}
+
+void UIShortcutConfigurationModel::sort(int iColumn, Qt::SortOrder order /* = Qt::AscendingOrder */)
+{
+ /* Sort whole the list: */
+ std::stable_sort(m_shortcuts.begin(), m_shortcuts.end(), UIShortcutItemSortingFunctor(iColumn, order));
+ /* Make sure host-combo item is always the first one: */
+ UIShortcutConfigurationItem fakeHostComboItem(UIHostCombo::hostComboCacheKey(), QString(), QString(), QString(), QString());
+ UIShortcutTableViewRow fakeHostComboTableViewRow(0, fakeHostComboItem);
+ const int iIndexOfHostComboItem = UIShortcutSearchFunctor<UIShortcutTableViewRow>()(m_shortcuts, fakeHostComboTableViewRow);
+ if (iIndexOfHostComboItem != -1)
+ {
+ UIShortcutTableViewRow hostComboItem = m_shortcuts.takeAt(iIndexOfHostComboItem);
+ m_shortcuts.prepend(hostComboItem);
+ }
+ /* Apply the filter: */
+ applyFilter();
+ /* Notify the model: */
+ emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+}
+
+void UIShortcutConfigurationModel::applyFilter()
+{
+ /* Erase items first if necessary: */
+ if (!m_filteredShortcuts.isEmpty())
+ {
+ beginRemoveRows(QModelIndex(), 0, m_filteredShortcuts.size() - 1);
+ m_filteredShortcuts.clear();
+ endRemoveRows();
+ }
+
+ /* If filter is empty: */
+ if (m_strFilter.isEmpty())
+ {
+ /* Just add all the items: */
+ m_filteredShortcuts = m_shortcuts;
+ }
+ else
+ {
+ /* Check if the description matches the filter: */
+ foreach (const UIShortcutTableViewRow &item, m_shortcuts)
+ {
+ /* If neither scope nor description or sequence matches the filter, skip item: */
+ if ( !item.scope().contains(m_strFilter, Qt::CaseInsensitive)
+ && !item.description().contains(m_strFilter, Qt::CaseInsensitive)
+ && !item.currentSequence().contains(m_strFilter, Qt::CaseInsensitive))
+ continue;
+ /* Add that item: */
+ m_filteredShortcuts << item;
+ }
+ }
+
+ /* Add items finally if necessary: */
+ if (!m_filteredShortcuts.isEmpty())
+ {
+ beginInsertRows(QModelIndex(), 0, m_filteredShortcuts.size() - 1);
+ endInsertRows();
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIShortcutConfigurationTable implementation. *
+*********************************************************************************************************************************/
+
+UIShortcutConfigurationTable::UIShortcutConfigurationTable(QWidget *pParent,
+ UIShortcutConfigurationModel *pModel,
+ const QString &strObjectName)
+ : QITableView(pParent)
+ , m_pItemEditorFactory(0)
+{
+ /* Set object name: */
+ setObjectName(strObjectName);
+ /* Set model: */
+ setModel(pModel);
+
+ /* Prepare all: */
+ prepare();
+}
+
+UIShortcutConfigurationTable::~UIShortcutConfigurationTable()
+{
+ /* Cleanup all: */
+ cleanup();
+}
+
+int UIShortcutConfigurationTable::childCount() const
+{
+ /* Redirect request to table model: */
+ return qobject_cast<UIShortcutConfigurationModel*>(model())->childCount();
+}
+
+QITableViewRow *UIShortcutConfigurationTable::childItem(int iIndex) const
+{
+ /* Redirect request to table model: */
+ return qobject_cast<UIShortcutConfigurationModel*>(model())->childItem(iIndex);
+}
+
+void UIShortcutConfigurationTable::sltHandleShortcutsLoaded()
+{
+ /* Resize columns to feat contents: */
+ resizeColumnsToContents();
+
+ /* Configure sorting: */
+ sortByColumn(TableColumnIndex_Description, Qt::AscendingOrder);
+ setSortingEnabled(true);
+}
+
+void UIShortcutConfigurationTable::prepare()
+{
+ /* Configure self: */
+ setTabKeyNavigation(false);
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ setEditTriggers(QAbstractItemView::CurrentChanged | QAbstractItemView::SelectedClicked);
+
+ /* Configure headers: */
+ verticalHeader()->hide();
+ verticalHeader()->setDefaultSectionSize((int)(verticalHeader()->minimumSectionSize() * 1.33));
+ horizontalHeader()->setStretchLastSection(false);
+ horizontalHeader()->setSectionResizeMode(TableColumnIndex_Description, QHeaderView::Interactive);
+ horizontalHeader()->setSectionResizeMode(TableColumnIndex_Sequence, QHeaderView::Stretch);
+
+ /* Connect model: */
+ UIShortcutConfigurationModel *pHotKeyTableModel = qobject_cast<UIShortcutConfigurationModel*>(model());
+ if (pHotKeyTableModel)
+ connect(pHotKeyTableModel, &UIShortcutConfigurationModel::sigShortcutsLoaded,
+ this, &UIShortcutConfigurationTable::sltHandleShortcutsLoaded);
+
+ /* Check if we do have proper item delegate: */
+ QIStyledItemDelegate *pStyledItemDelegate = qobject_cast<QIStyledItemDelegate*>(itemDelegate());
+ if (pStyledItemDelegate)
+ {
+ /* Configure item delegate: */
+ pStyledItemDelegate->setWatchForEditorDataCommits(true);
+
+ /* Create new item editor factory: */
+ m_pItemEditorFactory = new QItemEditorFactory;
+ if (m_pItemEditorFactory)
+ {
+ /* Register UIHotKeyEditor as the UIHotKey editor: */
+ int iHotKeyTypeId = qRegisterMetaType<UIHotKey>();
+ QStandardItemEditorCreator<UIHotKeyEditor> *pHotKeyItemEditorCreator = new QStandardItemEditorCreator<UIHotKeyEditor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iHotKeyTypeId, pHotKeyItemEditorCreator);
+
+ /* Register UIHostComboEditor as the UIHostComboWrapper editor: */
+ int iHostComboTypeId = qRegisterMetaType<UIHostComboWrapper>();
+ QStandardItemEditorCreator<UIHostComboEditor> *pHostComboItemEditorCreator = new QStandardItemEditorCreator<UIHostComboEditor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iHostComboTypeId, pHostComboItemEditorCreator);
+
+ /* Assign configured item editor factory to item delegate: */
+ pStyledItemDelegate->setItemEditorFactory(m_pItemEditorFactory);
+ }
+ }
+}
+
+void UIShortcutConfigurationTable::cleanup()
+{
+ /* Cleanup item editor factory: */
+ delete m_pItemEditorFactory;
+ m_pItemEditorFactory = 0;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIShortcutConfigurationEditor implementation. *
+*********************************************************************************************************************************/
+
+UIShortcutConfigurationEditor::UIShortcutConfigurationEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pModelManager(0)
+ , m_pModelRuntime(0)
+ , m_pTabWidget(0)
+ , m_pEditorFilterManager(0), m_pTableManager(0)
+ , m_pEditorFilterRuntime(0), m_pTableRuntime(0)
+{
+ prepare();
+}
+
+void UIShortcutConfigurationEditor::load(const UIShortcutConfigurationList &value)
+{
+ m_pModelManager->load(value);
+ m_pModelRuntime->load(value);
+}
+
+void UIShortcutConfigurationEditor::save(UIShortcutConfigurationList &value) const
+{
+ m_pModelManager->save(value);
+ m_pModelRuntime->save(value);
+}
+
+bool UIShortcutConfigurationEditor::isShortcutsUniqueManager() const
+{
+ return m_pModelManager->isAllShortcutsUnique();
+}
+
+bool UIShortcutConfigurationEditor::isShortcutsUniqueRuntime() const
+{
+ return m_pModelRuntime->isAllShortcutsUnique();
+}
+
+QString UIShortcutConfigurationEditor::tabNameManager() const
+{
+ return m_pTabWidget->tabText(TableIndex_Manager);
+}
+
+QString UIShortcutConfigurationEditor::tabNameRuntime() const
+{
+ return m_pTabWidget->tabText(TableIndex_Runtime);
+}
+
+void UIShortcutConfigurationEditor::retranslateUi()
+{
+ m_pTabWidget->setTabText(TableIndex_Manager, tr("&VirtualBox Manager"));
+ m_pTabWidget->setTabText(TableIndex_Runtime, tr("Virtual &Machine"));
+ m_pTableManager->setWhatsThis(tr("Lists all available shortcuts which can be configured."));
+ m_pTableRuntime->setWhatsThis(tr("Lists all available shortcuts which can be configured."));
+ m_pEditorFilterManager->setToolTip(tr("Holds a sequence to filter the shortcut list."));
+ m_pEditorFilterRuntime->setToolTip(tr("Holds a sequence to filter the shortcut list."));
+}
+
+void UIShortcutConfigurationEditor::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIShortcutConfigurationEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare tab-widget: */
+ m_pTabWidget = new QTabWidget(this);
+ if (m_pTabWidget)
+ {
+ /* Prepare 'Manager UI' tab: */
+ prepareTabManager();
+ /* Prepare 'Runtime UI' tab: */
+ prepareTabRuntime();
+
+ /* Add tab-widget into layout: */
+ pMainLayout->addWidget(m_pTabWidget);
+ }
+ }
+}
+
+void UIShortcutConfigurationEditor::prepareTabManager()
+{
+ /* Prepare Manager UI tab: */
+ QWidget *pTabManager = new QWidget;
+ if (pTabManager)
+ {
+ /* Prepare Manager UI layout: */
+ QVBoxLayout *pLayoutManager = new QVBoxLayout(pTabManager);
+ if (pLayoutManager)
+ {
+ pLayoutManager->setSpacing(1);
+#ifdef VBOX_WS_MAC
+ /* On Mac OS X and X11 we can do a bit of smoothness: */
+ pLayoutManager->setContentsMargins(0, 0, 0, 0);
+#endif
+
+ /* Prepare Manager UI filter editor: */
+ m_pEditorFilterManager = new QLineEdit(pTabManager);
+ if (m_pEditorFilterManager)
+ pLayoutManager->addWidget(m_pEditorFilterManager);
+
+ /* Prepare Manager UI model: */
+ m_pModelManager = new UIShortcutConfigurationModel(this, UIActionPoolType_Manager);
+
+ /* Prepare Manager UI table: */
+ m_pTableManager = new UIShortcutConfigurationTable(pTabManager, m_pModelManager, "m_pTableManager");
+ if (m_pTableManager)
+ {
+ m_pModelManager->setTable(m_pTableManager);
+ pLayoutManager->addWidget(m_pTableManager);
+ }
+ }
+
+ m_pTabWidget->insertTab(TableIndex_Manager, pTabManager, QString());
+ }
+}
+
+void UIShortcutConfigurationEditor::prepareTabRuntime()
+{
+ /* Create Runtime UI tab: */
+ QWidget *pTabMachine = new QWidget;
+ if (pTabMachine)
+ {
+ /* Prepare Runtime UI layout: */
+ QVBoxLayout *pLayoutMachine = new QVBoxLayout(pTabMachine);
+ if (pLayoutMachine)
+ {
+ pLayoutMachine->setSpacing(1);
+#ifdef VBOX_WS_MAC
+ /* On Mac OS X and X11 we can do a bit of smoothness: */
+ pLayoutMachine->setContentsMargins(0, 0, 0, 0);
+#endif
+
+ /* Prepare Runtime UI filter editor: */
+ m_pEditorFilterRuntime = new QLineEdit(pTabMachine);
+ if (m_pEditorFilterRuntime)
+ pLayoutMachine->addWidget(m_pEditorFilterRuntime);
+
+ /* Prepare Runtime UI model: */
+ m_pModelRuntime = new UIShortcutConfigurationModel(this, UIActionPoolType_Runtime);
+
+ /* Create Runtime UI table: */
+ m_pTableRuntime = new UIShortcutConfigurationTable(pTabMachine, m_pModelRuntime, "m_pTableRuntime");
+ if (m_pTableRuntime)
+ {
+ m_pModelRuntime->setTable(m_pTableRuntime);
+ pLayoutMachine->addWidget(m_pTableRuntime);
+ }
+ }
+
+ m_pTabWidget->insertTab(TableIndex_Runtime, pTabMachine, QString());
+
+ /* In the VM process we start by displaying the Runtime UI tab: */
+ if (uiCommon().uiType() == UICommon::UIType_RuntimeUI)
+ m_pTabWidget->setCurrentWidget(pTabMachine);
+ }
+}
+
+void UIShortcutConfigurationEditor::prepareConnections()
+{
+ /* Configure 'Manager UI' connections: */
+ connect(m_pEditorFilterManager, &QLineEdit::textChanged,
+ m_pModelManager, &UIShortcutConfigurationModel::sltHandleFilterTextChange);
+ connect(m_pModelManager, &UIShortcutConfigurationModel::sigDataChanged,
+ this, &UIShortcutConfigurationEditor::sigValueChanged);
+
+ /* Configure 'Runtime UI' connections: */
+ connect(m_pEditorFilterRuntime, &QLineEdit::textChanged,
+ m_pModelRuntime, &UIShortcutConfigurationModel::sltHandleFilterTextChange);
+ connect(m_pModelRuntime, &UIShortcutConfigurationModel::sigDataChanged,
+ this, &UIShortcutConfigurationEditor::sigValueChanged);
+}
+
+# include "UIShortcutConfigurationEditor.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIShortcutConfigurationEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIShortcutConfigurationEditor.h
new file mode 100644
index 00000000..dae208bf
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIShortcutConfigurationEditor.h
@@ -0,0 +1,222 @@
+/* $Id: UIShortcutConfigurationEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIShortcutConfigurationEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIShortcutConfigurationEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIShortcutConfigurationEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declartions: */
+class QLineEdit;
+class QTabWidget;
+class UIShortcutConfigurationModel;
+class UIShortcutConfigurationTable;
+
+/** Shortcut search functor template. */
+template <class BaseClass>
+class UIShortcutSearchFunctor : public BaseClass
+{
+public:
+
+ /** Constructs search functor. */
+ UIShortcutSearchFunctor() {}
+
+ /** Returns the position of the 1st occurrence of the
+ * @a shortcut in the @a shortcuts list, or -1 otherwise. */
+ int operator()(const QList<BaseClass> &shortcuts, const BaseClass &shortcut)
+ {
+ for (int i = 0; i < shortcuts.size(); ++i)
+ {
+ const BaseClass &iteratedShortcut = shortcuts.at(i);
+ if (iteratedShortcut.key() == shortcut.key())
+ return i;
+ }
+ return -1;
+ }
+};
+
+/** Shortcut configuration item. */
+class SHARED_LIBRARY_STUFF UIShortcutConfigurationItem
+{
+public:
+
+ /** Constructs item on the basis of passed arguments.
+ * @param strKey Brings the unique key identifying held sequence.
+ * @param strScope Brings the scope of the held sequence.
+ * @param strDescription Brings the deescription for the held sequence.
+ * @param strCurrentSequence Brings the current held sequence.
+ * @param strDefaultSequence Brings the default held sequence. */
+ UIShortcutConfigurationItem(const QString &strKey = QString(),
+ const QString &strScope = QString(),
+ const QString &strDescription = QString(),
+ const QString &strCurrentSequence = QString(),
+ const QString &strDefaultSequence = QString())
+ : m_strKey(strKey)
+ , m_strScope(strScope)
+ , m_strDescription(strDescription)
+ , m_strCurrentSequence(strCurrentSequence)
+ , m_strDefaultSequence(strDefaultSequence)
+ {}
+
+ /** Constructs item on the basis of @a another one. */
+ UIShortcutConfigurationItem(const UIShortcutConfigurationItem &another)
+ : m_strKey(another.key())
+ , m_strScope(another.scope())
+ , m_strDescription(another.description())
+ , m_strCurrentSequence(another.currentSequence())
+ , m_strDefaultSequence(another.defaultSequence())
+ {}
+
+ /** Returns the key. */
+ QString key() const { return m_strKey; }
+ /** Returns the scope. */
+ QString scope() const { return m_strScope; }
+ /** Returns the description. */
+ QString description() const { return m_strDescription; }
+ /** Returns the current sequence. */
+ QString currentSequence() const { return m_strCurrentSequence; }
+ /** Returns the default sequence. */
+ QString defaultSequence() const { return m_strDefaultSequence; }
+
+ /** Defines @a strCurrentSequence. */
+ void setCurrentSequence(const QString &strCurrentSequence) { m_strCurrentSequence = strCurrentSequence; }
+
+ /** Copies an item from @a another one. */
+ UIShortcutConfigurationItem &operator=(const UIShortcutConfigurationItem &another)
+ {
+ /* Reassign variables: */
+ m_strKey = another.key();
+ m_strScope = another.scope();
+ m_strDescription = another.description();
+ m_strCurrentSequence = another.currentSequence();
+ m_strDefaultSequence = another.defaultSequence();
+
+ /* Return this: */
+ return *this;
+ }
+
+ /** Returns whether this item equals to @a another one. */
+ bool operator==(const UIShortcutConfigurationItem &another) const
+ {
+ /* Compare by key, scope and current sequence: */
+ return true
+ && (key() == another.key())
+ && (scope() == another.scope())
+ && (currentSequence() == another.currentSequence())
+ ;
+ }
+
+private:
+
+ /** Holds the key. */
+ QString m_strKey;
+ /** Holds the scope. */
+ QString m_strScope;
+ /** Holds the description. */
+ QString m_strDescription;
+ /** Holds the current sequence. */
+ QString m_strCurrentSequence;
+ /** Holds the default sequence. */
+ QString m_strDefaultSequence;
+};
+
+/** Shortcut configuration list. */
+typedef QList<UIShortcutConfigurationItem> UIShortcutConfigurationList;
+
+/** QWidget subclass used as a shortcut configuration editor. */
+class SHARED_LIBRARY_STUFF UIShortcutConfigurationEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+ /** Table indexes. */
+ enum { TableIndex_Manager, TableIndex_Runtime };
+
+signals:
+
+ /** Notifies listeners about value change. */
+ void sigValueChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIShortcutConfigurationEditor(QWidget *pParent = 0);
+
+ /** Loads shortcut configuration list from passed @a value. */
+ void load(const UIShortcutConfigurationList &value);
+ /** Saves shortcut configuration list to passed @a value. */
+ void save(UIShortcutConfigurationList &value) const;
+
+ /** Returns whether manager shortcuts are unique. */
+ bool isShortcutsUniqueManager() const;
+ /** Returns whether runtime shortcuts are unique. */
+ bool isShortcutsUniqueRuntime() const;
+
+ /** Returns manager tab name. */
+ QString tabNameManager() const;
+ /** Returns runtime tab name. */
+ QString tabNameRuntime() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares Manager UI tab. */
+ void prepareTabManager();
+ /** Prepares Runtime UI tab. */
+ void prepareTabRuntime();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Holds the Manager UI shortcut configuration model instance. */
+ UIShortcutConfigurationModel *m_pModelManager;
+ /** Holds the Runtime UI shortcut configuration model instance. */
+ UIShortcutConfigurationModel *m_pModelRuntime;
+
+ /** Holds the tab-widget instance. */
+ QTabWidget *m_pTabWidget;
+ /** Holds the Manager UI shortcuts filter instance. */
+ QLineEdit *m_pEditorFilterManager;
+ /** Holds the Manager UI shortcuts table instance. */
+ UIShortcutConfigurationTable *m_pTableManager;
+ /** Holds the Runtime UI shortcuts filter instance. */
+ QLineEdit *m_pEditorFilterRuntime;
+ /** Holds the Runtime UI shortcuts table instance. */
+ UIShortcutConfigurationTable *m_pTableRuntime;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIShortcutConfigurationEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UISnapshotFolderEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISnapshotFolderEditor.cpp
new file mode 100644
index 00000000..30aa592c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISnapshotFolderEditor.cpp
@@ -0,0 +1,129 @@
+/* $Id: UISnapshotFolderEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UISnapshotFolderEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIFilePathSelector.h"
+#include "UISnapshotFolderEditor.h"
+
+
+UISnapshotFolderEditor::UISnapshotFolderEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLabel(0)
+ , m_pSelector(0)
+{
+ prepare();
+}
+
+void UISnapshotFolderEditor::setPath(const QString &strPath)
+{
+ /* Update cached value and
+ * editor if value has changed: */
+ if (m_strPath != strPath)
+ {
+ m_strPath = strPath;
+ if (m_pSelector)
+ m_pSelector->setPath(m_strPath);
+ }
+}
+
+QString UISnapshotFolderEditor::path() const
+{
+ return m_pSelector ? m_pSelector->path() : m_strPath;
+}
+
+void UISnapshotFolderEditor::setInitialPath(const QString &strInitialPath)
+{
+ /* Update cached value and
+ * editor if value has changed: */
+ if (m_strInitialPath != strInitialPath)
+ {
+ m_strInitialPath = strInitialPath;
+ if (m_pSelector)
+ m_pSelector->setInitialPath(m_strInitialPath);
+ }
+}
+
+QString UISnapshotFolderEditor::initialPath() const
+{
+ return m_pSelector ? m_pSelector->initialPath() : m_strInitialPath;
+}
+
+int UISnapshotFolderEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UISnapshotFolderEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UISnapshotFolderEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("S&napshot Folder:"));
+ if (m_pSelector)
+ m_pSelector->setToolTip(tr("Holds the path where snapshots of this virtual machine will be stored. "
+ "Be aware that snapshots can take quite a lot of storage space."));
+}
+
+void UISnapshotFolderEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create selector: */
+ m_pSelector = new UIFilePathSelector(this);
+ if (m_pSelector)
+ {
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pSelector);
+ m_pLayout->addWidget(m_pSelector, 0, 1);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UISnapshotFolderEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISnapshotFolderEditor.h
new file mode 100644
index 00000000..885192a0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UISnapshotFolderEditor.h
@@ -0,0 +1,94 @@
+/* $Id: UISnapshotFolderEditor.h $ */
+/** @file
+ * VBox Qt GUI - UISnapshotFolderEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UISnapshotFolderEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UISnapshotFolderEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class UIFilePathSelector;
+
+/** QWidget subclass used as a snapshot folder editor. */
+class SHARED_LIBRARY_STUFF UISnapshotFolderEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UISnapshotFolderEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a strPath. */
+ void setPath(const QString &strPath);
+ /** Returns editor path. */
+ QString path() const;
+
+ /** Defines editor @a strInitialPath. */
+ void setInitialPath(const QString &strInitialPath);
+ /** Returns editor initial path. */
+ QString initialPath() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the path to be set. */
+ QString m_strPath;
+ /** Holds the initial path to be set. */
+ QString m_strInitialPath;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the selector instance. */
+ UIFilePathSelector *m_pSelector;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UISnapshotFolderEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIStorageSettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIStorageSettingsEditor.cpp
new file mode 100644
index 00000000..3e628a3f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIStorageSettingsEditor.cpp
@@ -0,0 +1,5098 @@
+/* $Id: UIStorageSettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIStorageSettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QAbstractItemModel>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QCommonStyle>
+#include <QDrag>
+#include <QDragMoveEvent>
+#include <QGridLayout>
+#include <QItemDelegate>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMenu>
+#include <QMimeData>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QSpinBox>
+#include <QStackedWidget>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILabel.h"
+#include "QILabelSeparator.h"
+#include "QISplitter.h"
+#include "QIToolBar.h"
+#include "QIToolButton.h"
+#include "QITreeView.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMedium.h"
+#include "UIMediumSelector.h"
+#include "UIMessageCenter.h"
+#include "UIStorageSettingsEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+/* Defines: */
+typedef QList<StorageSlot> SlotsList;
+typedef QList<KDeviceType> DeviceTypeList;
+typedef QList<KStorageBus> ControllerBusList;
+typedef QList<KStorageControllerType> ControllerTypeList;
+Q_DECLARE_METATYPE(SlotsList);
+Q_DECLARE_METATYPE(DeviceTypeList);
+Q_DECLARE_METATYPE(ControllerBusList);
+Q_DECLARE_METATYPE(ControllerTypeList);
+
+
+/** Item states. */
+enum ItemState
+{
+ ItemState_Default,
+ ItemState_Collapsed,
+ ItemState_Expanded,
+ ItemState_Max
+};
+
+
+/** Pixmap types. */
+enum PixmapType
+{
+ PixmapType_Invalid,
+
+ PixmapType_ControllerAddEn,
+ PixmapType_ControllerAddDis,
+ PixmapType_ControllerDelEn,
+ PixmapType_ControllerDelDis,
+
+ PixmapType_AttachmentAddEn,
+ PixmapType_AttachmentAddDis,
+ PixmapType_AttachmentDelEn,
+ PixmapType_AttachmentDelDis,
+
+ PixmapType_IDEControllerNormal,
+ PixmapType_IDEControllerExpand,
+ PixmapType_IDEControllerCollapse,
+ PixmapType_SATAControllerNormal,
+ PixmapType_SATAControllerExpand,
+ PixmapType_SATAControllerCollapse,
+ PixmapType_SCSIControllerNormal,
+ PixmapType_SCSIControllerExpand,
+ PixmapType_SCSIControllerCollapse,
+ PixmapType_SASControllerNormal,
+ PixmapType_SASControllerExpand,
+ PixmapType_SASControllerCollapse,
+ PixmapType_USBControllerNormal,
+ PixmapType_USBControllerExpand,
+ PixmapType_USBControllerCollapse,
+ PixmapType_NVMeControllerNormal,
+ PixmapType_NVMeControllerExpand,
+ PixmapType_NVMeControllerCollapse,
+ PixmapType_VirtioSCSIControllerNormal,
+ PixmapType_VirtioSCSIControllerExpand,
+ PixmapType_VirtioSCSIControllerCollapse,
+ PixmapType_FloppyControllerNormal,
+ PixmapType_FloppyControllerExpand,
+ PixmapType_FloppyControllerCollapse,
+
+ PixmapType_IDEControllerAddEn,
+ PixmapType_IDEControllerAddDis,
+ PixmapType_SATAControllerAddEn,
+ PixmapType_SATAControllerAddDis,
+ PixmapType_SCSIControllerAddEn,
+ PixmapType_SCSIControllerAddDis,
+ PixmapType_SASControllerAddEn,
+ PixmapType_SASControllerAddDis,
+ PixmapType_USBControllerAddEn,
+ PixmapType_USBControllerAddDis,
+ PixmapType_NVMeControllerAddEn,
+ PixmapType_NVMeControllerAddDis,
+ PixmapType_VirtioSCSIControllerAddEn,
+ PixmapType_VirtioSCSIControllerAddDis,
+ PixmapType_FloppyControllerAddEn,
+ PixmapType_FloppyControllerAddDis,
+
+ PixmapType_HDAttachmentNormal,
+ PixmapType_CDAttachmentNormal,
+ PixmapType_FDAttachmentNormal,
+
+ PixmapType_HDAttachmentAddEn,
+ PixmapType_HDAttachmentAddDis,
+ PixmapType_CDAttachmentAddEn,
+ PixmapType_CDAttachmentAddDis,
+ PixmapType_FDAttachmentAddEn,
+ PixmapType_FDAttachmentAddDis,
+
+ PixmapType_ChooseExistingEn,
+ PixmapType_ChooseExistingDis,
+ PixmapType_CDUnmountEnabled,
+ PixmapType_CDUnmountDisabled,
+ PixmapType_FDUnmountEnabled,
+ PixmapType_FDUnmountDisabled,
+
+ PixmapType_Max
+};
+
+
+/** UIIconPool interface extension for Storage settings editor. */
+class UIIconPoolStorageSettings : public UIIconPool
+{
+public:
+
+ /** Create icon-pool instance. */
+ static void create();
+ /** Destroy icon-pool instance. */
+ static void destroy();
+
+ /** Returns pixmap corresponding to passed @a enmPixmapType. */
+ QPixmap pixmap(PixmapType enmPixmapType) const;
+ /** Returns icon (probably merged) corresponding to passed @a enmPixmapType and @a enmPixmapDisabledType. */
+ QIcon icon(PixmapType enmPixmapType, PixmapType enmPixmapDisabledType = PixmapType_Invalid) const;
+
+private:
+
+ /** Icon-pool constructor. */
+ UIIconPoolStorageSettings();
+ /** Icon-pool destructor. */
+ virtual ~UIIconPoolStorageSettings() RT_OVERRIDE;
+
+ /** Icon-pool instance access method. */
+ static UIIconPoolStorageSettings *instance();
+
+ /** Icon-pool instance. */
+ static UIIconPoolStorageSettings *s_pInstance;
+ /** Icon-pool names cache. */
+ QMap<PixmapType, QString> m_names;
+ /** Icon-pool icons cache. */
+ mutable QMap<PixmapType, QIcon> m_icons;
+
+ /** Allows for shortcut access. */
+ friend UIIconPoolStorageSettings *iconPool();
+};
+UIIconPoolStorageSettings *iconPool() { return UIIconPoolStorageSettings::instance(); }
+
+
+/** QITreeViewItem subclass used as abstract storage tree-view item. */
+class AbstractItem : public QITreeViewItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Item types. */
+ enum ItemType
+ {
+ Type_InvalidItem = 0,
+ Type_RootItem = 1,
+ Type_ControllerItem = 2,
+ Type_AttachmentItem = 3
+ };
+
+ /** Constructs top-level item passing @a pParentTree to the base-class. */
+ AbstractItem(QITreeView *pParentTree);
+ /** Constructs sub-level item passing @a pParentItem to the base-class. */
+ AbstractItem(AbstractItem *pParentItem);
+ /** Destructs item. */
+ virtual ~AbstractItem() RT_OVERRIDE;
+
+ /** Returns parent-item. */
+ AbstractItem *parent() const;
+ /** Returns ID. */
+ QUuid id() const;
+
+ /** Returns machine ID. */
+ QUuid machineId() const;
+ /** Defines @a uMachineId. */
+ void setMachineId(const QUuid &uMachineId);
+
+ /** Returns runtime type information. */
+ virtual ItemType rtti() const = 0;
+
+ /** Returns child item with specified @a iIndex. */
+ virtual AbstractItem *childItem(int iIndex) const = 0;
+ /** Returns child item with specified @a uId. */
+ virtual AbstractItem *childItemById(const QUuid &uId) const = 0;
+ /** Returns position of specified child @a pItem. */
+ virtual int posOfChild(AbstractItem *pItem) const = 0;
+
+ /** Returns tool-tip information. */
+ virtual QString toolTip() const = 0;
+ /** Returns pixmap information for specified @a enmState. */
+ virtual QPixmap pixmap(ItemState enmState = ItemState_Default) = 0;
+
+protected:
+
+ /** Adds a child @a pItem. */
+ virtual void addChild(AbstractItem *pItem) = 0;
+ /** Removes the child @a pItem. */
+ virtual void delChild(AbstractItem *pItem) = 0;
+
+private:
+
+ /** Holds the parent item reference. */
+ AbstractItem *m_pParentItem;
+ /** Holds the item ID. */
+ QUuid m_uId;
+ /** Holds the item machine ID. */
+ QUuid m_uMachineId;
+};
+Q_DECLARE_METATYPE(AbstractItem::ItemType);
+
+
+/** AbstractItem subclass used as root storage tree-view item. */
+class RootItem : public AbstractItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs top-level item passing @a pParentTree to the base-class. */
+ RootItem(QITreeView *pParentTree);
+ /** Destructs item. */
+ virtual ~RootItem() RT_OVERRIDE;
+
+ /** Returns a number of children of certain @a enmBus type. */
+ ULONG childCount(KStorageBus enmBus) const;
+
+protected:
+
+ /** Returns runtime type information. */
+ virtual ItemType rtti() const RT_OVERRIDE;
+
+ /** Returns child item with specified @a iIndex. */
+ virtual AbstractItem *childItem(int iIndex) const RT_OVERRIDE;
+ /** Returns child item with specified @a uId. */
+ virtual AbstractItem *childItemById(const QUuid &uId) const RT_OVERRIDE;
+ /** Returns position of specified child @a pItem. */
+ virtual int posOfChild(AbstractItem *pItem) const RT_OVERRIDE;
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+
+ /** Returns the item text. */
+ virtual QString text() const RT_OVERRIDE;
+ /** Returns tool-tip information. */
+ virtual QString toolTip() const RT_OVERRIDE;
+ /** Returns pixmap information for specified @a enmState. */
+ virtual QPixmap pixmap(ItemState enmState) RT_OVERRIDE;
+
+ /** Adds a child @a pItem. */
+ virtual void addChild(AbstractItem *pItem) RT_OVERRIDE;
+ /** Removes the child @a pItem. */
+ virtual void delChild(AbstractItem *pItem) RT_OVERRIDE;
+
+private:
+
+ /** Holds the list of controller items. */
+ QList<AbstractItem*> m_controllers;
+};
+
+
+/** AbstractItem subclass used as controller storage tree-view item. */
+class ControllerItem : public AbstractItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs sub-level item passing @a pParentItem to the base-class.
+ * @param strName Brings the name.
+ * @param enmBus Brings the bus.
+ * @param enmType Brings the type. */
+ ControllerItem(AbstractItem *pParentItem, const QString &strName,
+ KStorageBus enmBus, KStorageControllerType enmType);
+ /** Destructs item. */
+ virtual ~ControllerItem() RT_OVERRIDE;
+
+ /** Defines current @a strName. */
+ void setName(const QString &strName);
+ /** Returns current name. */
+ QString name() const;
+
+ /** Defines @a enmBus. */
+ void setBus(KStorageBus enmBus);
+ /** Returns bus. */
+ KStorageBus bus() const;
+ /** Returns possible buses to switch from current one. */
+ ControllerBusList buses() const;
+
+ /** Defines @a enmType. */
+ void setType(KStorageControllerType enmType);
+ /** Returns type. */
+ KStorageControllerType type() const;
+ /** Returns possible types for specified @a enmBus to switch from current one. */
+ ControllerTypeList types(KStorageBus enmBus) const;
+
+ /** Defines current @a uPortCount. */
+ void setPortCount(uint uPortCount);
+ /** Returns current port count. */
+ uint portCount();
+ /** Returns maximum port count. */
+ uint maxPortCount();
+
+ /** Defines whether controller @a fUseIoCache. */
+ void setUseIoCache(bool fUseIoCache);
+ /** Returns whether controller uses IO cache. */
+ bool useIoCache() const;
+
+ /** Returns possible controller slots. */
+ SlotsList allSlots() const;
+ /** Returns used controller slots. */
+ SlotsList usedSlots() const;
+ /** Returns supported device type list. */
+ DeviceTypeList deviceTypeList() const;
+
+ /** Defines a list of @a attachments. */
+ void setAttachments(const QList<AbstractItem*> &attachments) { m_attachments = attachments; }
+ /** Returns a list of attachments. */
+ QList<AbstractItem*> attachments() const { return m_attachments; }
+ /** Returns an ID list of attached media of specified @a enmType. */
+ QList<QUuid> attachmentIDs(KDeviceType enmType = KDeviceType_Null) const;
+
+private:
+
+ /** Returns runtime type information. */
+ virtual ItemType rtti() const RT_OVERRIDE;
+
+ /** Returns child item with specified @a iIndex. */
+ virtual AbstractItem *childItem(int iIndex) const RT_OVERRIDE;
+ /** Returns child item with specified @a uId. */
+ virtual AbstractItem *childItemById(const QUuid &uId) const RT_OVERRIDE;
+ /** Returns position of specified child @a pItem. */
+ virtual int posOfChild(AbstractItem *pItem) const RT_OVERRIDE;
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+
+ /** Returns the item text. */
+ virtual QString text() const RT_OVERRIDE;
+ /** Returns tool-tip information. */
+ virtual QString toolTip() const RT_OVERRIDE;
+ /** Returns pixmap information for specified @a enmState. */
+ virtual QPixmap pixmap(ItemState enmState) RT_OVERRIDE;
+
+ /** Adds a child @a pItem. */
+ virtual void addChild(AbstractItem *pItem) RT_OVERRIDE;
+ /** Removes the child @a pItem. */
+ virtual void delChild(AbstractItem *pItem) RT_OVERRIDE;
+
+ /** Updates possible buses. */
+ void updateBusInfo();
+ /** Updates possible types. */
+ void updateTypeInfo();
+ /** Updates pixmaps of possible buses. */
+ void updatePixmaps();
+
+ /** Holds the current name. */
+ QString m_strName;
+
+ /** Holds the bus. */
+ KStorageBus m_enmBus;
+ /** Holds the type. */
+ KStorageControllerType m_enmType;
+
+ /** Holds the possible buses. */
+ ControllerBusList m_buses;
+ /** Holds the possible types on per bus basis. */
+ QMap<KStorageBus, ControllerTypeList> m_types;
+ /** Holds the pixmaps of possible buses. */
+ QList<PixmapType> m_pixmaps;
+
+ /** Holds the current port count. */
+ uint m_uPortCount;
+ /** Holds whether controller uses IO cache. */
+ bool m_fUseIoCache;
+
+ /** Holds the list of attachments. */
+ QList<AbstractItem*> m_attachments;
+};
+
+
+/** AbstractItem subclass used as attachment storage tree-view item. */
+class AttachmentItem : public AbstractItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs sub-level item passing @a pParentItem to the base-class.
+ * @param enmDeviceType Brings the attachment device type. */
+ AttachmentItem(AbstractItem *pParentItem, KDeviceType enmDeviceType);
+
+ /** Defines @a enmDeviceType. */
+ void setDeviceType(KDeviceType enmDeviceType);
+ /** Returns device type. */
+ KDeviceType deviceType() const;
+ /** Returns possible device types. */
+ DeviceTypeList deviceTypes() const;
+
+ /** Defines storage @a slot. */
+ void setStorageSlot(const StorageSlot &slot);
+ /** Returns storage slot. */
+ StorageSlot storageSlot() const;
+ /** Returns possible storage slots. */
+ SlotsList storageSlots() const;
+
+ /** Defines @a uMediumId. */
+ void setMediumId(const QUuid &uMediumId);
+ /** Returns the medium id. */
+ QUuid mediumId() const;
+
+ /** Returns whether attachment is a host drive. */
+ bool isHostDrive() const;
+
+ /** Defines whether attachment is @a fPassthrough. */
+ void setPassthrough(bool fPassthrough);
+ /** Returns whether attachment is passthrough. */
+ bool isPassthrough() const;
+
+ /** Defines whether attachment is @a fTemporaryEjectable. */
+ void setTempEject(bool fTemporaryEjectable);
+ /** Returns whether attachment is temporary ejectable. */
+ bool isTempEject() const;
+
+ /** Defines whether attachment is @a fNonRotational. */
+ void setNonRotational(bool fNonRotational);
+ /** Returns whether attachment is non-rotational. */
+ bool isNonRotational() const;
+
+ /** Returns whether attachment is @a fIsHotPluggable. */
+ void setHotPluggable(bool fIsHotPluggable);
+ /** Returns whether attachment is hot-pluggable. */
+ bool isHotPluggable() const;
+
+ /** Returns medium size. */
+ QString size() const;
+ /** Returns logical medium size. */
+ QString logicalSize() const;
+ /** Returns medium location. */
+ QString location() const;
+ /** Returns medium format. */
+ QString format() const;
+ /** Returns medium details. */
+ QString details() const;
+ /** Returns medium usage. */
+ QString usage() const;
+ /** Returns medium encryption password ID. */
+ QString encryptionPasswordId() const;
+
+private:
+
+ /** Caches medium information. */
+ void cache();
+
+ /** Returns runtime type information. */
+ virtual ItemType rtti() const RT_OVERRIDE;
+
+ /** Returns child item with specified @a iIndex. */
+ virtual AbstractItem *childItem(int iIndex) const RT_OVERRIDE;
+ /** Returns child item with specified @a uId. */
+ virtual AbstractItem *childItemById(const QUuid &uId) const RT_OVERRIDE;
+ /** Returns position of specified child @a pItem. */
+ virtual int posOfChild(AbstractItem *pItem) const RT_OVERRIDE;
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+
+ /** Returns the item text. */
+ virtual QString text() const RT_OVERRIDE;
+ /** Returns tool-tip information. */
+ virtual QString toolTip() const RT_OVERRIDE;
+ /** Returns pixmap information for specified @a enmState. */
+ virtual QPixmap pixmap(ItemState enmState) RT_OVERRIDE;
+
+ /** Adds a child @a pItem. */
+ virtual void addChild(AbstractItem *pItem) RT_OVERRIDE;
+ /** Removes the child @a pItem. */
+ virtual void delChild(AbstractItem *pItem) RT_OVERRIDE;
+
+ /** Holds the device type. */
+ KDeviceType m_enmDeviceType;
+ /** Holds the storage slot. */
+ StorageSlot m_storageSlot;
+ /** Holds the medium ID. */
+ QUuid m_uMediumId;
+ /** Holds whether attachment is a host drive. */
+ bool m_fHostDrive;
+ /** Holds whether attachment is passthrough. */
+ bool m_fPassthrough;
+ /** Holds whether attachment is temporary ejectable. */
+ bool m_fTempEject;
+ /** Holds whether attachment is non-rotational. */
+ bool m_fNonRotational;
+ /** Holds whether attachment is hot-pluggable. */
+ bool m_fHotPluggable;
+
+ /** Holds the name. */
+ QString m_strName;
+ /** Holds the tool-tip. */
+ QString m_strTip;
+ /** Holds the pixmap. */
+ QPixmap m_strPixmap;
+
+ /** Holds the medium size. */
+ QString m_strSize;
+ /** Holds the logical medium size. */
+ QString m_strLogicalSize;
+ /** Holds the medium location. */
+ QString m_strLocation;
+ /** Holds the medium format. */
+ QString m_strFormat;
+ /** Holds the medium details. */
+ QString m_strDetails;
+ /** Holds the medium usage. */
+ QString m_strUsage;
+ /** Holds the medium encryption password ID. */
+ QString m_strAttEncryptionPasswordID;
+};
+
+
+/** QAbstractItemModel subclass used as complex storage model. */
+class StorageModel : public QAbstractItemModel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Data roles. */
+ enum DataRole
+ {
+ R_ItemId = Qt::UserRole + 1,
+ R_ItemPixmap,
+ R_ItemPixmapRect,
+ R_ItemName,
+ R_ItemNamePoint,
+ R_ItemType,
+ R_IsController,
+ R_IsAttachment,
+
+ R_ToolTipType,
+ R_IsMoreIDEControllersPossible,
+ R_IsMoreSATAControllersPossible,
+ R_IsMoreSCSIControllersPossible,
+ R_IsMoreFloppyControllersPossible,
+ R_IsMoreSASControllersPossible,
+ R_IsMoreUSBControllersPossible,
+ R_IsMoreNVMeControllersPossible,
+ R_IsMoreVirtioSCSIControllersPossible,
+ R_IsMoreAttachmentsPossible,
+
+ R_CtrName,
+ R_CtrType,
+ R_CtrTypesForIDE,
+ R_CtrTypesForSATA,
+ R_CtrTypesForSCSI,
+ R_CtrTypesForFloppy,
+ R_CtrTypesForSAS,
+ R_CtrTypesForUSB,
+ R_CtrTypesForPCIe,
+ R_CtrTypesForVirtioSCSI,
+ R_CtrDevices,
+ R_CtrBusType,
+ R_CtrBusTypes,
+ R_CtrPortCount,
+ R_CtrMaxPortCount,
+ R_CtrIoCache,
+
+ R_AttSlot,
+ R_AttSlots,
+ R_AttDevice,
+ R_AttMediumId,
+ R_AttIsShowDiffs,
+ R_AttIsHostDrive,
+ R_AttIsPassthrough,
+ R_AttIsTempEject,
+ R_AttIsNonRotational,
+ R_AttIsHotPluggable,
+ R_AttSize,
+ R_AttLogicalSize,
+ R_AttLocation,
+ R_AttFormat,
+ R_AttDetails,
+ R_AttUsage,
+ R_AttEncryptionPasswordID,
+
+ R_Margin,
+ R_Spacing,
+ R_IconSize,
+
+ R_HDPixmapEn,
+ R_CDPixmapEn,
+ R_FDPixmapEn,
+
+ R_HDPixmapAddEn,
+ R_HDPixmapAddDis,
+ R_CDPixmapAddEn,
+ R_CDPixmapAddDis,
+ R_FDPixmapAddEn,
+ R_FDPixmapAddDis,
+ R_HDPixmapRect,
+ R_CDPixmapRect,
+ R_FDPixmapRect
+ };
+
+ /** Tool-tip types. */
+ enum ToolTipType
+ {
+ ToolTipType_Default = 0,
+ ToolTipType_Expander = 1,
+ ToolTipType_HDAdder = 2,
+ ToolTipType_CDAdder = 3,
+ ToolTipType_FDAdder = 4
+ };
+
+ /** Constructs storage model passing @a pParentTree to the base-class. */
+ StorageModel(QITreeView *pParentTree);
+ /** Destructs storage model. */
+ virtual ~StorageModel() RT_OVERRIDE;
+
+ /** Returns row count for the passed @a parentIndex. */
+ int rowCount(const QModelIndex &parentIndex = QModelIndex()) const;
+ /** Returns column count for the passed @a parentIndex. */
+ int columnCount(const QModelIndex &parentIndex = QModelIndex()) const;
+
+ /** Returns root item. */
+ QModelIndex root() const;
+ /** Returns item specified by @a iRow, @a iColum and @a parentIndex. */
+ QModelIndex index(int iRow, int iColumn, const QModelIndex &parentIndex = QModelIndex()) const;
+ /** Returns parent item of @a specifiedIndex item. */
+ QModelIndex parent(const QModelIndex &specifiedIndex) const;
+
+ /** Returns model data for @a specifiedIndex and @a iRole. */
+ QVariant data(const QModelIndex &specifiedIndex, int iRole) const;
+ /** Defines model data for @a specifiedIndex and @a iRole as @a value. */
+ bool setData(const QModelIndex &specifiedIndex, const QVariant &value, int iRole);
+
+ /** Adds controller with certain @a strCtrName, @a enmBus and @a enmType. */
+ QModelIndex addController(const QString &strCtrName, KStorageBus enmBus, KStorageControllerType enmType);
+ /** Deletes controller with certain @a uCtrId. */
+ void delController(const QUuid &uCtrId);
+
+ /** Adds attachment with certain @a enmDeviceType and @a uMediumId to controller with certain @a uCtrId. */
+ QModelIndex addAttachment(const QUuid &uCtrId, KDeviceType enmDeviceType, const QUuid &uMediumId);
+ /** Deletes attachment with certain @a uAttId from controller with certain @a uCtrId. */
+ void delAttachment(const QUuid &uCtrId, const QUuid &uAttId);
+ /** Moves attachment with certain @a uAttId from controller with certain @a uCtrOldId to one with another @a uCtrNewId. */
+ void moveAttachment(const QUuid &uAttId, const QUuid &uCtrOldId, const QUuid &uCtrNewId);
+
+ /** Returns device type of attachment with certain @a uAttId from controller with certain @a uCtrId. */
+ KDeviceType attachmentDeviceType(const QUuid &uCtrId, const QUuid &uAttId) const;
+
+ /** Defines @a uMachineId for reference. */
+ void setMachineId(const QUuid &uMachineId);
+
+ /** Sorts the contents of model by @a iColumn and @a enmOrder. */
+ void sort(int iColumn = 0, Qt::SortOrder enmOrder = Qt::AscendingOrder);
+ /** Returns attachment index by specified @a controllerIndex and @a attachmentStorageSlot. */
+ QModelIndex attachmentBySlot(QModelIndex controllerIndex, StorageSlot attachmentStorageSlot);
+
+ /** Returns chipset type. */
+ KChipsetType chipsetType() const;
+ /** Defines @a enmChipsetType. */
+ void setChipsetType(KChipsetType enmChipsetType);
+
+ /** Defines @a enmConfigurationAccessLevel. */
+ void setConfigurationAccessLevel(ConfigurationAccessLevel enmConfigurationAccessLevel);
+
+ /** Clears model of all contents. */
+ void clear();
+
+ /** Returns current controller types. */
+ QMap<KStorageBus, int> currentControllerTypes() const;
+ /** Returns maximum controller types. */
+ QMap<KStorageBus, int> maximumControllerTypes() const;
+
+ /** Returns bus corresponding to passed enmRole. */
+ static KStorageBus roleToBus(StorageModel::DataRole enmRole);
+ /** Returns role corresponding to passed enmBus. */
+ static StorageModel::DataRole busToRole(KStorageBus enmBus);
+
+private:
+
+ /** Returns model flags for @a specifiedIndex. */
+ Qt::ItemFlags flags(const QModelIndex &specifiedIndex) const;
+
+ /** Holds the root item instance. */
+ AbstractItem *m_pRootItem;
+
+ /** Holds the enabled plus pixmap instance. */
+ QPixmap m_pixmapPlusEn;
+ /** Holds the disabled plus pixmap instance. */
+ QPixmap m_pixmapPlusDis;
+ /** Holds the enabled minus pixmap instance. */
+ QPixmap m_pixmapMinusEn;
+ /** Holds the disabled minus pixmap instance. */
+ QPixmap m_pixmapMinusDis;
+
+ /** Holds the tool-tip type. */
+ ToolTipType m_enmToolTipType;
+
+ /** Holds the chipset type. */
+ KChipsetType m_enmChipsetType;
+
+ /** Holds configuration access level. */
+ ConfigurationAccessLevel m_enmConfigurationAccessLevel;
+};
+Q_DECLARE_METATYPE(StorageModel::ToolTipType);
+
+
+/** QItemDelegate subclass used as storage table item delegate. */
+class StorageDelegate : public QItemDelegate
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs storage delegate passing @a pParent to the base-class. */
+ StorageDelegate(QObject *pParent);
+
+private:
+
+ /** Paints @a index item with specified @a option using specified @a pPainter. */
+ void paint(QPainter *pPainter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+};
+
+
+/** QObject subclass used as UI medium ID holder.
+ * Used for compliance with other storage page widgets
+ * which caching and holding corresponding information. */
+class UIMediumIDHolder : public QObject
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notify about medium ID changed. */
+ void sigChanged();
+
+public:
+
+ /** Constructs medium ID holder passing @a pParent to the base-class. */
+ UIMediumIDHolder(QWidget *pParent) : QObject(pParent) {}
+
+ /** Defines medium @a uId. */
+ void setId(const QUuid &uId) { m_uId = uId; emit sigChanged(); }
+ /** Returns medium ID. */
+ QUuid id() const { return m_uId; }
+
+ /** Defines medium device @a enmType. */
+ void setType(UIMediumDeviceType enmType) { m_enmType = enmType; }
+ /** Returns medium device type. */
+ UIMediumDeviceType type() const { return m_enmType; }
+
+ /** Returns whether medium ID is null. */
+ bool isNull() const { return m_uId == UIMedium().id(); }
+
+private:
+
+ /** Holds the medium ID. */
+ QUuid m_uId;
+ /** Holds the medium device type. */
+ UIMediumDeviceType m_enmType;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIIconPoolStorageSettings implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIIconPoolStorageSettings *UIIconPoolStorageSettings::s_pInstance = 0;
+UIIconPoolStorageSettings *UIIconPoolStorageSettings::instance() { return s_pInstance; }
+void UIIconPoolStorageSettings::create() { new UIIconPoolStorageSettings; }
+void UIIconPoolStorageSettings::destroy() { delete s_pInstance; }
+
+QPixmap UIIconPoolStorageSettings::pixmap(PixmapType enmPixmapType) const
+{
+ /* Prepare fallback pixmap: */
+ static QPixmap nullPixmap;
+
+ /* If we do NOT have that 'pixmap type' icon cached already: */
+ if (!m_icons.contains(enmPixmapType))
+ {
+ /* Compose proper icon if we have that 'pixmap type' known: */
+ if (m_names.contains(enmPixmapType))
+ m_icons[enmPixmapType] = iconSet(m_names[enmPixmapType]);
+ /* Assign fallback icon if we do NOT have that 'pixmap type' known: */
+ else
+ m_icons[enmPixmapType] = iconSet(nullPixmap);
+ }
+
+ /* Retrieve corresponding icon: */
+ const QIcon &icon = m_icons[enmPixmapType];
+ AssertMsgReturn(!icon.isNull(),
+ ("Undefined icon for type '%d'.", (int)enmPixmapType),
+ nullPixmap);
+
+ /* Retrieve available sizes for that icon: */
+ const QList<QSize> availableSizes = icon.availableSizes();
+ AssertMsgReturn(!availableSizes.isEmpty(),
+ ("Undefined icon for type '%s'.", (int)enmPixmapType),
+ nullPixmap);
+
+ /* Determine icon metric: */
+ const QStyle *pStyle = QApplication::style();
+ const int iIconMetric = pStyle->pixelMetric(QStyle::PM_SmallIconSize);
+
+ /* Return pixmap of first available size: */
+ return icon.pixmap(QSize(iIconMetric, iIconMetric));
+}
+
+QIcon UIIconPoolStorageSettings::icon(PixmapType enmPixmapType,
+ PixmapType enmPixmapDisabledType /* = PixmapType_Invalid */) const
+{
+ /* Prepare fallback pixmap: */
+ static QPixmap nullPixmap;
+ /* Prepare fallback icon: */
+ static QIcon nullIcon;
+
+ /* If we do NOT have that 'pixmap type' icon cached already: */
+ if (!m_icons.contains(enmPixmapType))
+ {
+ /* Compose proper icon if we have that 'pixmap type' known: */
+ if (m_names.contains(enmPixmapType))
+ m_icons[enmPixmapType] = iconSet(m_names[enmPixmapType]);
+ /* Assign fallback icon if we do NOT have that 'pixmap type' known: */
+ else
+ m_icons[enmPixmapType] = iconSet(nullPixmap);
+ }
+
+ /* Retrieve normal icon: */
+ const QIcon &icon = m_icons[enmPixmapType];
+ AssertMsgReturn(!icon.isNull(),
+ ("Undefined icon for type '%d'.", (int)enmPixmapType),
+ nullIcon);
+
+ /* If 'disabled' icon is invalid => just return 'normal' icon: */
+ if (enmPixmapDisabledType == PixmapType_Invalid)
+ return icon;
+
+ /* If we do NOT have that 'pixmap disabled type' icon cached already: */
+ if (!m_icons.contains(enmPixmapDisabledType))
+ {
+ /* Compose proper icon if we have that 'pixmap disabled type' known: */
+ if (m_names.contains(enmPixmapDisabledType))
+ m_icons[enmPixmapDisabledType] = iconSet(m_names[enmPixmapDisabledType]);
+ /* Assign fallback icon if we do NOT have that 'pixmap disabled type' known: */
+ else
+ m_icons[enmPixmapDisabledType] = iconSet(nullPixmap);
+ }
+
+ /* Retrieve disabled icon: */
+ const QIcon &iconDisabled = m_icons[enmPixmapDisabledType];
+ AssertMsgReturn(!iconDisabled.isNull(),
+ ("Undefined icon for type '%d'.", (int)enmPixmapDisabledType),
+ nullIcon);
+
+ /* Return icon composed on the basis of two above: */
+ QIcon resultIcon = icon;
+ foreach (const QSize &size, iconDisabled.availableSizes())
+ resultIcon.addPixmap(iconDisabled.pixmap(size), QIcon::Disabled);
+ return resultIcon;
+}
+
+UIIconPoolStorageSettings::UIIconPoolStorageSettings()
+{
+ /* Connect instance: */
+ s_pInstance = this;
+
+ /* Controller file-names: */
+ m_names.insert(PixmapType_ControllerAddEn, ":/controller_add_16px.png");
+ m_names.insert(PixmapType_ControllerAddDis, ":/controller_add_disabled_16px.png");
+ m_names.insert(PixmapType_ControllerDelEn, ":/controller_remove_16px.png");
+ m_names.insert(PixmapType_ControllerDelDis, ":/controller_remove_disabled_16px.png");
+ /* Attachment file-names: */
+ m_names.insert(PixmapType_AttachmentAddEn, ":/attachment_add_16px.png");
+ m_names.insert(PixmapType_AttachmentAddDis, ":/attachment_add_disabled_16px.png");
+ m_names.insert(PixmapType_AttachmentDelEn, ":/attachment_remove_16px.png");
+ m_names.insert(PixmapType_AttachmentDelDis, ":/attachment_remove_disabled_16px.png");
+ /* Specific controller default/expand/collapse file-names: */
+ m_names.insert(PixmapType_IDEControllerNormal, ":/ide_16px.png");
+ m_names.insert(PixmapType_IDEControllerExpand, ":/ide_expand_16px.png");
+ m_names.insert(PixmapType_IDEControllerCollapse, ":/ide_collapse_16px.png");
+ m_names.insert(PixmapType_SATAControllerNormal, ":/sata_16px.png");
+ m_names.insert(PixmapType_SATAControllerExpand, ":/sata_expand_16px.png");
+ m_names.insert(PixmapType_SATAControllerCollapse, ":/sata_collapse_16px.png");
+ m_names.insert(PixmapType_SCSIControllerNormal, ":/scsi_16px.png");
+ m_names.insert(PixmapType_SCSIControllerExpand, ":/scsi_expand_16px.png");
+ m_names.insert(PixmapType_SCSIControllerCollapse, ":/scsi_collapse_16px.png");
+ m_names.insert(PixmapType_SASControllerNormal, ":/sas_16px.png");
+ m_names.insert(PixmapType_SASControllerExpand, ":/sas_expand_16px.png");
+ m_names.insert(PixmapType_SASControllerCollapse, ":/sas_collapse_16px.png");
+ m_names.insert(PixmapType_USBControllerNormal, ":/usb_16px.png");
+ m_names.insert(PixmapType_USBControllerExpand, ":/usb_expand_16px.png");
+ m_names.insert(PixmapType_USBControllerCollapse, ":/usb_collapse_16px.png");
+ m_names.insert(PixmapType_NVMeControllerNormal, ":/pcie_16px.png");
+ m_names.insert(PixmapType_NVMeControllerExpand, ":/pcie_expand_16px.png");
+ m_names.insert(PixmapType_NVMeControllerCollapse, ":/pcie_collapse_16px.png");
+ m_names.insert(PixmapType_VirtioSCSIControllerNormal, ":/virtio_scsi_16px.png");
+ m_names.insert(PixmapType_VirtioSCSIControllerExpand, ":/virtio_scsi_expand_16px.png");
+ m_names.insert(PixmapType_VirtioSCSIControllerCollapse, ":/virtio_scsi_collapse_16px.png");
+ m_names.insert(PixmapType_FloppyControllerNormal, ":/floppy_16px.png");
+ m_names.insert(PixmapType_FloppyControllerExpand, ":/floppy_expand_16px.png");
+ m_names.insert(PixmapType_FloppyControllerCollapse, ":/floppy_collapse_16px.png");
+ /* Specific controller add file-names: */
+ m_names.insert(PixmapType_IDEControllerAddEn, ":/ide_add_16px.png");
+ m_names.insert(PixmapType_IDEControllerAddDis, ":/ide_add_disabled_16px.png");
+ m_names.insert(PixmapType_SATAControllerAddEn, ":/sata_add_16px.png");
+ m_names.insert(PixmapType_SATAControllerAddDis, ":/sata_add_disabled_16px.png");
+ m_names.insert(PixmapType_SCSIControllerAddEn, ":/scsi_add_16px.png");
+ m_names.insert(PixmapType_SCSIControllerAddDis, ":/scsi_add_disabled_16px.png");
+ m_names.insert(PixmapType_SASControllerAddEn, ":/sas_add_16px.png");
+ m_names.insert(PixmapType_SASControllerAddDis, ":/sas_add_disabled_16px.png");
+ m_names.insert(PixmapType_USBControllerAddEn, ":/usb_add_16px.png");
+ m_names.insert(PixmapType_USBControllerAddDis, ":/usb_add_disabled_16px.png");
+ m_names.insert(PixmapType_NVMeControllerAddEn, ":/pcie_add_16px.png");
+ m_names.insert(PixmapType_NVMeControllerAddDis, ":/pcie_add_disabled_16px.png");
+ m_names.insert(PixmapType_VirtioSCSIControllerAddEn, ":/virtio_scsi_add_16px.png");
+ m_names.insert(PixmapType_VirtioSCSIControllerAddDis, ":/virtio_scsi_add_disabled_16px.png");
+ m_names.insert(PixmapType_FloppyControllerAddEn, ":/floppy_add_16px.png");
+ m_names.insert(PixmapType_FloppyControllerAddDis, ":/floppy_add_disabled_16px.png");
+ /* Specific attachment file-names: */
+ m_names.insert(PixmapType_HDAttachmentNormal, ":/hd_16px.png");
+ m_names.insert(PixmapType_CDAttachmentNormal, ":/cd_16px.png");
+ m_names.insert(PixmapType_FDAttachmentNormal, ":/fd_16px.png");
+ /* Specific attachment add file-names: */
+ m_names.insert(PixmapType_HDAttachmentAddEn, ":/hd_add_16px.png");
+ m_names.insert(PixmapType_HDAttachmentAddDis, ":/hd_add_disabled_16px.png");
+ m_names.insert(PixmapType_CDAttachmentAddEn, ":/cd_add_16px.png");
+ m_names.insert(PixmapType_CDAttachmentAddDis, ":/cd_add_disabled_16px.png");
+ m_names.insert(PixmapType_FDAttachmentAddEn, ":/fd_add_16px.png");
+ m_names.insert(PixmapType_FDAttachmentAddDis, ":/fd_add_disabled_16px.png");
+ /* Specific attachment custom file-names: */
+ m_names.insert(PixmapType_ChooseExistingEn, ":/select_file_16px.png");
+ m_names.insert(PixmapType_ChooseExistingDis, ":/select_file_disabled_16px.png");
+ m_names.insert(PixmapType_CDUnmountEnabled, ":/cd_unmount_16px.png");
+ m_names.insert(PixmapType_CDUnmountDisabled, ":/cd_unmount_disabled_16px.png");
+ m_names.insert(PixmapType_FDUnmountEnabled, ":/fd_unmount_16px.png");
+ m_names.insert(PixmapType_FDUnmountDisabled, ":/fd_unmount_disabled_16px.png");
+}
+
+UIIconPoolStorageSettings::~UIIconPoolStorageSettings()
+{
+ /* Disconnect instance: */
+ s_pInstance = 0;
+}
+
+
+/*********************************************************************************************************************************
+* Class AbstractItem implementation. *
+*********************************************************************************************************************************/
+
+AbstractItem::AbstractItem(QITreeView *pParentTree)
+ : QITreeViewItem(pParentTree)
+ , m_pParentItem(0)
+ , m_uId(QUuid::createUuid())
+{
+ if (m_pParentItem)
+ m_pParentItem->addChild(this);
+}
+
+AbstractItem::AbstractItem(AbstractItem *pParentItem)
+ : QITreeViewItem(pParentItem)
+ , m_pParentItem(pParentItem)
+ , m_uId(QUuid::createUuid())
+{
+ if (m_pParentItem)
+ m_pParentItem->addChild(this);
+}
+
+AbstractItem::~AbstractItem()
+{
+ if (m_pParentItem)
+ m_pParentItem->delChild(this);
+}
+
+AbstractItem *AbstractItem::parent() const
+{
+ return m_pParentItem;
+}
+
+QUuid AbstractItem::id() const
+{
+ return m_uId;
+}
+
+QUuid AbstractItem::machineId() const
+{
+ return m_uMachineId;
+}
+
+void AbstractItem::setMachineId(const QUuid &uMachineId)
+{
+ m_uMachineId = uMachineId;
+}
+
+
+/*********************************************************************************************************************************
+* Class RootItem implementation. *
+*********************************************************************************************************************************/
+
+RootItem::RootItem(QITreeView *pParentTree)
+ : AbstractItem(pParentTree)
+{
+}
+
+RootItem::~RootItem()
+{
+ while (!m_controllers.isEmpty())
+ delete m_controllers.first();
+}
+
+ULONG RootItem::childCount(KStorageBus enmBus) const
+{
+ ULONG uResult = 0;
+ foreach (AbstractItem *pItem, m_controllers)
+ {
+ ControllerItem *pItemController = qobject_cast<ControllerItem*>(pItem);
+ if (pItemController->bus() == enmBus)
+ ++ uResult;
+ }
+ return uResult;
+}
+
+AbstractItem::ItemType RootItem::rtti() const
+{
+ return Type_RootItem;
+}
+
+AbstractItem *RootItem::childItem(int iIndex) const
+{
+ return m_controllers[iIndex];
+}
+
+AbstractItem *RootItem::childItemById(const QUuid &uId) const
+{
+ for (int i = 0; i < childCount(); ++ i)
+ if (m_controllers[i]->id() == uId)
+ return m_controllers[i];
+ return 0;
+}
+
+int RootItem::posOfChild(AbstractItem *pItem) const
+{
+ return m_controllers.indexOf(pItem);
+}
+
+int RootItem::childCount() const
+{
+ return m_controllers.size();
+}
+
+QString RootItem::text() const
+{
+ return QString();
+}
+
+QString RootItem::toolTip() const
+{
+ return QString();
+}
+
+QPixmap RootItem::pixmap(ItemState /* enmState */)
+{
+ return QPixmap();
+}
+
+void RootItem::addChild(AbstractItem *pItem)
+{
+ m_controllers << pItem;
+}
+
+void RootItem::delChild(AbstractItem *pItem)
+{
+ m_controllers.removeAll(pItem);
+}
+
+
+/*********************************************************************************************************************************
+* Class ControllerItem implementation. *
+*********************************************************************************************************************************/
+
+ControllerItem::ControllerItem(AbstractItem *pParentItem, const QString &strName,
+ KStorageBus enmBus, KStorageControllerType enmType)
+ : AbstractItem(pParentItem)
+ , m_strName(strName)
+ , m_enmBus(enmBus)
+ , m_enmType(enmType)
+ , m_uPortCount(0)
+ , m_fUseIoCache(false)
+{
+ /* Check for proper parent type: */
+ AssertMsg(parent()->rtti() == AbstractItem::Type_RootItem, ("Incorrect parent type!\n"));
+
+ AssertMsg(m_enmBus != KStorageBus_Null, ("Wrong Bus Type {%d}!\n", m_enmBus));
+ AssertMsg(m_enmType != KStorageControllerType_Null, ("Wrong Controller Type {%d}!\n", m_enmType));
+
+ updateBusInfo();
+ updateTypeInfo();
+ updatePixmaps();
+
+ m_fUseIoCache = uiCommon().virtualBox().GetSystemProperties().GetDefaultIoCacheSettingForStorageController(enmType);
+}
+
+ControllerItem::~ControllerItem()
+{
+ while (!m_attachments.isEmpty())
+ delete m_attachments.first();
+}
+
+void ControllerItem::setName(const QString &strName)
+{
+ m_strName = strName;
+}
+
+QString ControllerItem::name() const
+{
+ return m_strName;
+}
+
+void ControllerItem::setBus(KStorageBus enmBus)
+{
+ m_enmBus = enmBus;
+
+ updateBusInfo();
+ updateTypeInfo();
+ updatePixmaps();
+}
+
+KStorageBus ControllerItem::bus() const
+{
+ return m_enmBus;
+}
+
+ControllerBusList ControllerItem::buses() const
+{
+ return m_buses;
+}
+
+void ControllerItem::setType(KStorageControllerType enmType)
+{
+ m_enmType = enmType;
+
+ updateTypeInfo();
+}
+
+KStorageControllerType ControllerItem::type() const
+{
+ return m_enmType;
+}
+
+ControllerTypeList ControllerItem::types(KStorageBus enmBus) const
+{
+ return m_types.value(enmBus);
+}
+
+void ControllerItem::setPortCount(uint uPortCount)
+{
+ /* Limit maximum port count: */
+ m_uPortCount = qMin(uPortCount, (uint)uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(bus()));
+}
+
+uint ControllerItem::portCount()
+{
+ /* Recalculate actual port count: */
+ for (int i = 0; i < m_attachments.size(); ++i)
+ {
+ AttachmentItem *pItem = qobject_cast<AttachmentItem*>(m_attachments.at(i));
+ if (m_uPortCount < (uint)pItem->storageSlot().port + 1)
+ m_uPortCount = (uint)pItem->storageSlot().port + 1;
+ }
+ return m_uPortCount;
+}
+
+uint ControllerItem::maxPortCount()
+{
+ return (uint)uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(bus());
+}
+
+void ControllerItem::setUseIoCache(bool fUseIoCache)
+{
+ m_fUseIoCache = fUseIoCache;
+}
+
+bool ControllerItem::useIoCache() const
+{
+ return m_fUseIoCache;
+}
+
+SlotsList ControllerItem::allSlots() const
+{
+ SlotsList allSlots;
+ CSystemProperties comProps = uiCommon().virtualBox().GetSystemProperties();
+ for (ULONG i = 0; i < comProps.GetMaxPortCountForStorageBus(bus()); ++ i)
+ for (ULONG j = 0; j < comProps.GetMaxDevicesPerPortForStorageBus(bus()); ++ j)
+ allSlots << StorageSlot(bus(), i, j);
+ return allSlots;
+}
+
+SlotsList ControllerItem::usedSlots() const
+{
+ SlotsList usedSlots;
+ for (int i = 0; i < m_attachments.size(); ++ i)
+ usedSlots << qobject_cast<AttachmentItem*>(m_attachments.at(i))->storageSlot();
+ return usedSlots;
+}
+
+DeviceTypeList ControllerItem::deviceTypeList() const
+{
+ return uiCommon().virtualBox().GetSystemProperties().GetDeviceTypesForStorageBus(m_enmBus).toList();
+}
+
+QList<QUuid> ControllerItem::attachmentIDs(KDeviceType enmType /* = KDeviceType_Null */) const
+{
+ QList<QUuid> ids;
+ foreach (AbstractItem *pItem, m_attachments)
+ {
+ AttachmentItem *pItemAttachment = qobject_cast<AttachmentItem*>(pItem);
+ if ( enmType == KDeviceType_Null
+ || pItemAttachment->deviceType() == enmType)
+ ids << pItem->id();
+ }
+ return ids;
+}
+
+AbstractItem::ItemType ControllerItem::rtti() const
+{
+ return Type_ControllerItem;
+}
+
+AbstractItem *ControllerItem::childItem(int iIndex) const
+{
+ return m_attachments[iIndex];
+}
+
+AbstractItem *ControllerItem::childItemById(const QUuid &uId) const
+{
+ for (int i = 0; i < childCount(); ++ i)
+ if (m_attachments[i]->id() == uId)
+ return m_attachments[i];
+ return 0;
+}
+
+int ControllerItem::posOfChild(AbstractItem *pItem) const
+{
+ return m_attachments.indexOf(pItem);
+}
+
+int ControllerItem::childCount() const
+{
+ return m_attachments.size();
+}
+
+QString ControllerItem::text() const
+{
+ return UIStorageSettingsEditor::tr("Controller: %1").arg(name());
+}
+
+QString ControllerItem::toolTip() const
+{
+ return UIStorageSettingsEditor::tr("<nobr><b>%1</b></nobr><br>"
+ "<nobr>Bus:&nbsp;&nbsp;%2</nobr><br>"
+ "<nobr>Type:&nbsp;&nbsp;%3</nobr>")
+ .arg(m_strName)
+ .arg(gpConverter->toString(bus()))
+ .arg(gpConverter->toString(type()));
+}
+
+QPixmap ControllerItem::pixmap(ItemState enmState)
+{
+ return iconPool()->pixmap(m_pixmaps.at(enmState));
+}
+
+void ControllerItem::addChild(AbstractItem *pItem)
+{
+ m_attachments << pItem;
+}
+
+void ControllerItem::delChild(AbstractItem *pItem)
+{
+ m_attachments.removeAll(pItem);
+}
+
+void ControllerItem::updateBusInfo()
+{
+ /* Clear the buses initially: */
+ m_buses.clear();
+
+ /* Load currently supported storage buses: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ const QVector<KStorageBus> supportedBuses = comProperties.GetSupportedStorageBuses();
+
+ /* If current bus is NOT KStorageBus_Floppy: */
+ if (m_enmBus != KStorageBus_Floppy)
+ {
+ /* We update the list with all supported buses
+ * and remove the current one from that list. */
+ m_buses << supportedBuses.toList();
+ m_buses.removeAll(m_enmBus);
+ }
+
+ /* And prepend current bus finally: */
+ m_buses.prepend(m_enmBus);
+}
+
+void ControllerItem::updateTypeInfo()
+{
+ /* Clear the types initially: */
+ m_types.clear();
+
+ /* Load currently supported storage buses & types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ const QVector<KStorageBus> supportedBuses = comProperties.GetSupportedStorageBuses();
+ const QVector<KStorageControllerType> supportedTypes = comProperties.GetSupportedStorageControllerTypes();
+
+ /* We update the list with all supported buses
+ * and remove the current one from that list. */
+ ControllerBusList possibleBuses = supportedBuses.toList();
+ possibleBuses.removeAll(m_enmBus);
+
+ /* And prepend current bus finally: */
+ possibleBuses.prepend(m_enmBus);
+
+ /* Enumerate possible buses: */
+ foreach (const KStorageBus &enmBus, possibleBuses)
+ {
+ /* Enumerate possible types and check whether type is supported or already selected before adding it: */
+ foreach (const KStorageControllerType &enmType, comProperties.GetStorageControllerTypesForStorageBus(enmBus))
+ if (supportedTypes.contains(enmType) || enmType == m_enmType)
+ m_types[enmBus] << enmType;
+ }
+}
+
+void ControllerItem::updatePixmaps()
+{
+ m_pixmaps.clear();
+
+ for (int i = 0; i < ItemState_Max; ++i)
+ {
+ m_pixmaps << PixmapType_Invalid;
+ switch (m_enmBus)
+ {
+ case KStorageBus_IDE: m_pixmaps[i] = static_cast<PixmapType>(PixmapType_IDEControllerNormal + i); break;
+ case KStorageBus_SATA: m_pixmaps[i] = static_cast<PixmapType>(PixmapType_SATAControllerNormal + i); break;
+ case KStorageBus_SCSI: m_pixmaps[i] = static_cast<PixmapType>(PixmapType_SCSIControllerNormal + i); break;
+ case KStorageBus_Floppy: m_pixmaps[i] = static_cast<PixmapType>(PixmapType_FloppyControllerNormal + i); break;
+ case KStorageBus_SAS: m_pixmaps[i] = static_cast<PixmapType>(PixmapType_SASControllerNormal + i); break;
+ case KStorageBus_USB: m_pixmaps[i] = static_cast<PixmapType>(PixmapType_USBControllerNormal + i); break;
+ case KStorageBus_PCIe: m_pixmaps[i] = static_cast<PixmapType>(PixmapType_NVMeControllerNormal + i); break;
+ case KStorageBus_VirtioSCSI: m_pixmaps[i] = static_cast<PixmapType>(PixmapType_VirtioSCSIControllerNormal + i); break;
+ default: break;
+ }
+ AssertMsg(m_pixmaps[i] != PixmapType_Invalid, ("Invalid item state pixmap!\n"));
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class AttachmentItem implementation. *
+*********************************************************************************************************************************/
+
+AttachmentItem::AttachmentItem(AbstractItem *pParentItem, KDeviceType enmDeviceType)
+ : AbstractItem(pParentItem)
+ , m_enmDeviceType(enmDeviceType)
+ , m_fHostDrive(false)
+ , m_fPassthrough(false)
+ , m_fTempEject(false)
+ , m_fNonRotational(false)
+ , m_fHotPluggable(false)
+{
+ /* Check for proper parent type: */
+ AssertMsg(parent()->rtti() == AbstractItem::Type_ControllerItem, ("Incorrect parent type!\n"));
+
+ /* Select default slot: */
+ AssertMsg(!storageSlots().isEmpty(), ("There should be at least one available slot!\n"));
+ m_storageSlot = storageSlots()[0];
+}
+
+void AttachmentItem::setDeviceType(KDeviceType enmDeviceType)
+{
+ m_enmDeviceType = enmDeviceType;
+}
+
+KDeviceType AttachmentItem::deviceType() const
+{
+ return m_enmDeviceType;
+}
+
+DeviceTypeList AttachmentItem::deviceTypes() const
+{
+ return qobject_cast<ControllerItem*>(parent())->deviceTypeList();
+}
+
+void AttachmentItem::setStorageSlot(const StorageSlot &storageSlot)
+{
+ m_storageSlot = storageSlot;
+}
+
+StorageSlot AttachmentItem::storageSlot() const
+{
+ return m_storageSlot;
+}
+
+SlotsList AttachmentItem::storageSlots() const
+{
+ ControllerItem *pItemController = qobject_cast<ControllerItem*>(parent());
+
+ /* Filter list from used slots: */
+ SlotsList allSlots(pItemController->allSlots());
+ SlotsList usedSlots(pItemController->usedSlots());
+ foreach(StorageSlot usedSlot, usedSlots)
+ if (usedSlot != m_storageSlot)
+ allSlots.removeAll(usedSlot);
+
+ return allSlots;
+}
+
+void AttachmentItem::setMediumId(const QUuid &uMediumId)
+{
+ /// @todo is this required?
+ //AssertMsg(!aAttMediumId.isNull(), ("Medium ID value can't be null!\n"));
+ m_uMediumId = uiCommon().medium(uMediumId).id();
+ cache();
+}
+
+QUuid AttachmentItem::mediumId() const
+{
+ return m_uMediumId;
+}
+
+bool AttachmentItem::isHostDrive() const
+{
+ return m_fHostDrive;
+}
+
+void AttachmentItem::setPassthrough(bool fPassthrough)
+{
+ m_fPassthrough = fPassthrough;
+}
+
+bool AttachmentItem::isPassthrough() const
+{
+ return m_fPassthrough;
+}
+
+void AttachmentItem::setTempEject(bool fTempEject)
+{
+ m_fTempEject = fTempEject;
+}
+
+bool AttachmentItem::isTempEject() const
+{
+ return m_fTempEject;
+}
+
+void AttachmentItem::setNonRotational(bool fNonRotational)
+{
+ m_fNonRotational = fNonRotational;
+}
+
+bool AttachmentItem::isNonRotational() const
+{
+ return m_fNonRotational;
+}
+
+void AttachmentItem::setHotPluggable(bool fHotPluggable)
+{
+ m_fHotPluggable = fHotPluggable;
+}
+
+bool AttachmentItem::isHotPluggable() const
+{
+ return m_fHotPluggable;
+}
+
+QString AttachmentItem::size() const
+{
+ return m_strSize;
+}
+
+QString AttachmentItem::logicalSize() const
+{
+ return m_strLogicalSize;
+}
+
+QString AttachmentItem::location() const
+{
+ return m_strLocation;
+}
+
+QString AttachmentItem::format() const
+{
+ return m_strFormat;
+}
+
+QString AttachmentItem::details() const
+{
+ return m_strDetails;
+}
+
+QString AttachmentItem::usage() const
+{
+ return m_strUsage;
+}
+
+QString AttachmentItem::encryptionPasswordId() const
+{
+ return m_strAttEncryptionPasswordID;
+}
+
+void AttachmentItem::cache()
+{
+ UIMedium guiMedium = uiCommon().medium(m_uMediumId);
+
+ /* Cache medium information: */
+ m_strName = guiMedium.name(true);
+ m_strTip = guiMedium.toolTipCheckRO(true, m_enmDeviceType != KDeviceType_HardDisk);
+ m_strPixmap = guiMedium.iconCheckRO(true);
+ m_fHostDrive = guiMedium.isHostDrive();
+
+ /* Cache additional information: */
+ m_strSize = guiMedium.size(true);
+ m_strLogicalSize = guiMedium.logicalSize(true);
+ m_strLocation = guiMedium.location(true);
+ m_strAttEncryptionPasswordID = QString("--");
+ if (guiMedium.isNull())
+ {
+ m_strFormat = QString("--");
+ }
+ else
+ {
+ switch (m_enmDeviceType)
+ {
+ case KDeviceType_HardDisk:
+ {
+ m_strFormat = QString("%1 (%2)").arg(guiMedium.hardDiskType(true)).arg(guiMedium.hardDiskFormat(true));
+ m_strDetails = guiMedium.storageDetails();
+ const QString strAttEncryptionPasswordID = guiMedium.encryptionPasswordID();
+ if (!strAttEncryptionPasswordID.isNull())
+ m_strAttEncryptionPasswordID = strAttEncryptionPasswordID;
+ break;
+ }
+ case KDeviceType_DVD:
+ case KDeviceType_Floppy:
+ {
+ m_strFormat = m_fHostDrive ? UIStorageSettingsEditor::tr("Host Drive") : UIStorageSettingsEditor::tr("Image", "storage image");
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ m_strUsage = guiMedium.usage(true);
+
+ /* Fill empty attributes: */
+ if (m_strUsage.isEmpty())
+ m_strUsage = QString("--");
+}
+
+AbstractItem::ItemType AttachmentItem::rtti() const
+{
+ return Type_AttachmentItem;
+}
+
+AbstractItem *AttachmentItem::childItem(int /* iIndex */) const
+{
+ return 0;
+}
+
+AbstractItem *AttachmentItem::childItemById(const QUuid& /* uId */) const
+{
+ return 0;
+}
+
+int AttachmentItem::posOfChild(AbstractItem * /* pItem */) const
+{
+ return 0;
+}
+
+int AttachmentItem::childCount() const
+{
+ return 0;
+}
+
+QString AttachmentItem::text() const
+{
+ return m_strName;
+}
+
+QString AttachmentItem::toolTip() const
+{
+ return m_strTip;
+}
+
+QPixmap AttachmentItem::pixmap(ItemState /* enmState */)
+{
+ if (m_strPixmap.isNull())
+ {
+ switch (m_enmDeviceType)
+ {
+ case KDeviceType_HardDisk:
+ m_strPixmap = iconPool()->pixmap(PixmapType_HDAttachmentNormal);
+ break;
+ case KDeviceType_DVD:
+ m_strPixmap = iconPool()->pixmap(PixmapType_CDAttachmentNormal);
+ break;
+ case KDeviceType_Floppy:
+ m_strPixmap = iconPool()->pixmap(PixmapType_FDAttachmentNormal);
+ break;
+ default:
+ break;
+ }
+ }
+ return m_strPixmap;
+}
+
+void AttachmentItem::addChild(AbstractItem * /* pItem */)
+{
+}
+
+void AttachmentItem::delChild(AbstractItem * /* pItem */)
+{
+}
+
+
+/*********************************************************************************************************************************
+* Class StorageModel implementation. *
+*********************************************************************************************************************************/
+
+StorageModel::StorageModel(QITreeView *pParentTree)
+ : QAbstractItemModel(pParentTree)
+ , m_pRootItem(new RootItem(pParentTree))
+ , m_enmToolTipType(ToolTipType_Default)
+ , m_enmChipsetType(KChipsetType_PIIX3)
+ , m_enmConfigurationAccessLevel(ConfigurationAccessLevel_Null)
+{
+}
+
+StorageModel::~StorageModel()
+{
+ delete m_pRootItem;
+}
+
+int StorageModel::rowCount(const QModelIndex &parentIndex) const
+{
+ return !parentIndex.isValid() ? 1 /* only root item has invalid parent */ :
+ static_cast<AbstractItem*>(parentIndex.internalPointer())->childCount();
+}
+
+int StorageModel::columnCount(const QModelIndex & /* parentIndex */) const
+{
+ return 1;
+}
+
+QModelIndex StorageModel::root() const
+{
+ return index(0, 0);
+}
+
+QModelIndex StorageModel::index(int iRow, int iColumn, const QModelIndex &parentIndex) const
+{
+ if (!hasIndex(iRow, iColumn, parentIndex))
+ return QModelIndex();
+
+ AbstractItem *pItem = !parentIndex.isValid() ? m_pRootItem :
+ static_cast<AbstractItem*>(parentIndex.internalPointer())->childItem(iRow);
+
+ return pItem ? createIndex(iRow, iColumn, pItem) : QModelIndex();
+}
+
+QModelIndex StorageModel::parent(const QModelIndex &specifiedIndex) const
+{
+ if (!specifiedIndex.isValid())
+ return QModelIndex();
+
+ AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer());
+ AbstractItem *pParentOfItem = pItem->parent();
+ AbstractItem *pParentOfParent = pParentOfItem ? pParentOfItem->parent() : 0;
+ int iPosition = pParentOfParent ? pParentOfParent->posOfChild(pParentOfItem) : 0;
+
+ if (pParentOfItem)
+ return createIndex(iPosition, 0, pParentOfItem);
+ else
+ return QModelIndex();
+}
+
+QVariant StorageModel::data(const QModelIndex &specifiedIndex, int iRole) const
+{
+ if (!specifiedIndex.isValid())
+ return QVariant();
+
+ switch (iRole)
+ {
+ /* Basic Attributes: */
+ case Qt::FontRole:
+ {
+ return QVariant(qApp->font());
+ }
+ case Qt::SizeHintRole:
+ {
+ QFontMetrics fm(data(specifiedIndex, Qt::FontRole).value<QFont>());
+ int iMinimumHeight = qMax(fm.height(), data(specifiedIndex, R_IconSize).toInt());
+ int iMargin = data(specifiedIndex, R_Margin).toInt();
+ return QSize(1 /* ignoring width */, 2 * iMargin + iMinimumHeight);
+ }
+ case Qt::ToolTipRole:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ {
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ {
+ QString strTip(pItem->toolTip());
+ switch (m_enmToolTipType)
+ {
+ case ToolTipType_Expander:
+ if (index(0, 0, specifiedIndex).isValid())
+ strTip = UIStorageSettingsEditor::tr("<nobr>Expands/Collapses&nbsp;item.</nobr>");
+ break;
+ case ToolTipType_HDAdder:
+ strTip = UIStorageSettingsEditor::tr("<nobr>Adds&nbsp;hard&nbsp;disk.</nobr>");
+ break;
+ case ToolTipType_CDAdder:
+ strTip = UIStorageSettingsEditor::tr("<nobr>Adds&nbsp;optical&nbsp;drive.</nobr>");
+ break;
+ case ToolTipType_FDAdder:
+ strTip = UIStorageSettingsEditor::tr("<nobr>Adds&nbsp;floppy&nbsp;drive.</nobr>");
+ break;
+ default:
+ break;
+ }
+ return strTip;
+ }
+ return pItem->toolTip();
+ }
+ return QString();
+ }
+
+ /* Advanced Attributes: */
+ case R_ItemId:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ return pItem->id();
+ return QUuid();
+ }
+ case R_ItemPixmap:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ {
+ ItemState enmState = ItemState_Default;
+ if (hasChildren(specifiedIndex))
+ if (QTreeView *view = qobject_cast<QTreeView*>(QObject::parent()))
+ enmState = view->isExpanded(specifiedIndex) ? ItemState_Expanded : ItemState_Collapsed;
+ return pItem->pixmap(enmState);
+ }
+ return QPixmap();
+ }
+ case R_ItemPixmapRect:
+ {
+ int iMargin = data(specifiedIndex, R_Margin).toInt();
+ int iWidth = data(specifiedIndex, R_IconSize).toInt();
+ return QRect(iMargin, iMargin, iWidth, iWidth);
+ }
+ case R_ItemName:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ return pItem->text();
+ return QString();
+ }
+ case R_ItemNamePoint:
+ {
+ int iMargin = data(specifiedIndex, R_Margin).toInt();
+ int iSpacing = data(specifiedIndex, R_Spacing).toInt();
+ int iWidth = data(specifiedIndex, R_IconSize).toInt();
+ QFontMetrics fm(data(specifiedIndex, Qt::FontRole).value<QFont>());
+ QSize sizeHint = data(specifiedIndex, Qt::SizeHintRole).toSize();
+ return QPoint(iMargin + iWidth + 2 * iSpacing,
+ sizeHint.height() / 2 + fm.ascent() / 2 - 1 /* base line */);
+ }
+ case R_ItemType:
+ {
+ QVariant result(QVariant::fromValue(AbstractItem::Type_InvalidItem));
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ result.setValue(pItem->rtti());
+ return result;
+ }
+ case R_IsController:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ return pItem->rtti() == AbstractItem::Type_ControllerItem;
+ return false;
+ }
+ case R_IsAttachment:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ return pItem->rtti() == AbstractItem::Type_AttachmentItem;
+ return false;
+ }
+
+ case R_ToolTipType:
+ {
+ return QVariant::fromValue(m_enmToolTipType);
+ }
+ case R_IsMoreIDEControllersPossible:
+ {
+ return (m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full) &&
+ (qobject_cast<RootItem*>(m_pRootItem)->childCount(KStorageBus_IDE) <
+ uiCommon().virtualBox().GetSystemProperties().GetMaxInstancesOfStorageBus(chipsetType(), KStorageBus_IDE));
+ }
+ case R_IsMoreSATAControllersPossible:
+ {
+ return (m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full) &&
+ (qobject_cast<RootItem*>(m_pRootItem)->childCount(KStorageBus_SATA) <
+ uiCommon().virtualBox().GetSystemProperties().GetMaxInstancesOfStorageBus(chipsetType(), KStorageBus_SATA));
+ }
+ case R_IsMoreSCSIControllersPossible:
+ {
+ return (m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full) &&
+ (qobject_cast<RootItem*>(m_pRootItem)->childCount(KStorageBus_SCSI) <
+ uiCommon().virtualBox().GetSystemProperties().GetMaxInstancesOfStorageBus(chipsetType(), KStorageBus_SCSI));
+ }
+ case R_IsMoreFloppyControllersPossible:
+ {
+ return (m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full) &&
+ (qobject_cast<RootItem*>(m_pRootItem)->childCount(KStorageBus_Floppy) <
+ uiCommon().virtualBox().GetSystemProperties().GetMaxInstancesOfStorageBus(chipsetType(), KStorageBus_Floppy));
+ }
+ case R_IsMoreSASControllersPossible:
+ {
+ return (m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full) &&
+ (qobject_cast<RootItem*>(m_pRootItem)->childCount(KStorageBus_SAS) <
+ uiCommon().virtualBox().GetSystemProperties().GetMaxInstancesOfStorageBus(chipsetType(), KStorageBus_SAS));
+ }
+ case R_IsMoreUSBControllersPossible:
+ {
+ return (m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full) &&
+ (qobject_cast<RootItem*>(m_pRootItem)->childCount(KStorageBus_USB) <
+ uiCommon().virtualBox().GetSystemProperties().GetMaxInstancesOfStorageBus(chipsetType(), KStorageBus_USB));
+ }
+ case R_IsMoreNVMeControllersPossible:
+ {
+ return (m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full) &&
+ (qobject_cast<RootItem*>(m_pRootItem)->childCount(KStorageBus_PCIe) <
+ uiCommon().virtualBox().GetSystemProperties().GetMaxInstancesOfStorageBus(chipsetType(), KStorageBus_PCIe));
+ }
+ case R_IsMoreVirtioSCSIControllersPossible:
+ {
+ return (m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full) &&
+ (qobject_cast<RootItem*>(m_pRootItem)->childCount(KStorageBus_VirtioSCSI) <
+ uiCommon().virtualBox().GetSystemProperties().GetMaxInstancesOfStorageBus(chipsetType(), KStorageBus_VirtioSCSI));
+ }
+ case R_IsMoreAttachmentsPossible:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ {
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ {
+ ControllerItem *pItemController = qobject_cast<ControllerItem*>(pItem);
+ CSystemProperties comProps = uiCommon().virtualBox().GetSystemProperties();
+ const bool fIsMoreAttachmentsPossible = (ULONG)rowCount(specifiedIndex) <
+ (comProps.GetMaxPortCountForStorageBus(pItemController->bus()) *
+ comProps.GetMaxDevicesPerPortForStorageBus(pItemController->bus()));
+ if (fIsMoreAttachmentsPossible)
+ {
+ switch (m_enmConfigurationAccessLevel)
+ {
+ case ConfigurationAccessLevel_Full:
+ return true;
+ case ConfigurationAccessLevel_Partial_Running:
+ {
+ switch (pItemController->bus())
+ {
+ case KStorageBus_USB:
+ return true;
+ case KStorageBus_SATA:
+ return (uint)rowCount(specifiedIndex) < pItemController->portCount();
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ case R_CtrName:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ return qobject_cast<ControllerItem*>(pItem)->name();
+ return QString();
+ }
+ case R_CtrType:
+ {
+ QVariant result(QVariant::fromValue(KStorageControllerType_Null));
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ result.setValue(qobject_cast<ControllerItem*>(pItem)->type());
+ return result;
+ }
+ case R_CtrTypesForIDE:
+ case R_CtrTypesForSATA:
+ case R_CtrTypesForSCSI:
+ case R_CtrTypesForFloppy:
+ case R_CtrTypesForSAS:
+ case R_CtrTypesForUSB:
+ case R_CtrTypesForPCIe:
+ case R_CtrTypesForVirtioSCSI:
+ {
+ QVariant result(QVariant::fromValue(ControllerTypeList()));
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ result.setValue(qobject_cast<ControllerItem*>(pItem)->types(roleToBus((StorageModel::DataRole)iRole)));
+ return result;
+ }
+ case R_CtrDevices:
+ {
+ QVariant result(QVariant::fromValue(DeviceTypeList()));
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ result.setValue(qobject_cast<ControllerItem*>(pItem)->deviceTypeList());
+ return result;
+ }
+ case R_CtrBusType:
+ {
+ QVariant result(QVariant::fromValue(KStorageBus_Null));
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ result.setValue(qobject_cast<ControllerItem*>(pItem)->bus());
+ return result;
+ }
+ case R_CtrBusTypes:
+ {
+ QVariant result(QVariant::fromValue(ControllerBusList()));
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ result.setValue(qobject_cast<ControllerItem*>(pItem)->buses());
+ return result;
+ }
+ case R_CtrPortCount:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ return qobject_cast<ControllerItem*>(pItem)->portCount();
+ return 0;
+ }
+ case R_CtrMaxPortCount:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ return qobject_cast<ControllerItem*>(pItem)->maxPortCount();
+ return 0;
+ }
+ case R_CtrIoCache:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ return qobject_cast<ControllerItem*>(pItem)->useIoCache();
+ return false;
+ }
+
+ case R_AttSlot:
+ {
+ QVariant result(QVariant::fromValue(StorageSlot()));
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ result.setValue(qobject_cast<AttachmentItem*>(pItem)->storageSlot());
+ return result;
+ }
+ case R_AttSlots:
+ {
+ QVariant result(QVariant::fromValue(SlotsList()));
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ result.setValue(qobject_cast<AttachmentItem*>(pItem)->storageSlots());
+ return result;
+ }
+ case R_AttDevice:
+ {
+ QVariant result(QVariant::fromValue(KDeviceType_Null));
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ result.setValue(qobject_cast<AttachmentItem*>(pItem)->deviceType());
+ return result;
+ }
+ case R_AttMediumId:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->mediumId();
+ return QUuid();
+ }
+ case R_AttIsHostDrive:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->isHostDrive();
+ return false;
+ }
+ case R_AttIsPassthrough:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->isPassthrough();
+ return false;
+ }
+ case R_AttIsTempEject:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->isTempEject();
+ return false;
+ }
+ case R_AttIsNonRotational:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->isNonRotational();
+ return false;
+ }
+ case R_AttIsHotPluggable:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->isHotPluggable();
+ return false;
+ }
+ case R_AttSize:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->size();
+ return QString();
+ }
+ case R_AttLogicalSize:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->logicalSize();
+ return QString();
+ }
+ case R_AttLocation:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->location();
+ return QString();
+ }
+ case R_AttFormat:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->format();
+ return QString();
+ }
+ case R_AttDetails:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->details();
+ return QString();
+ }
+ case R_AttUsage:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->usage();
+ return QString();
+ }
+ case R_AttEncryptionPasswordID:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ return qobject_cast<AttachmentItem*>(pItem)->encryptionPasswordId();
+ return QString();
+ }
+ case R_Margin:
+ {
+ return 4;
+ }
+ case R_Spacing:
+ {
+ return 4;
+ }
+ case R_IconSize:
+ {
+ return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ }
+
+ case R_HDPixmapEn:
+ {
+ return iconPool()->pixmap(PixmapType_HDAttachmentNormal);
+ }
+ case R_CDPixmapEn:
+ {
+ return iconPool()->pixmap(PixmapType_CDAttachmentNormal);
+ }
+ case R_FDPixmapEn:
+ {
+ return iconPool()->pixmap(PixmapType_FDAttachmentNormal);
+ }
+
+ case R_HDPixmapAddEn:
+ {
+ return iconPool()->pixmap(PixmapType_HDAttachmentAddEn);
+ }
+ case R_HDPixmapAddDis:
+ {
+ return iconPool()->pixmap(PixmapType_HDAttachmentAddDis);
+ }
+ case R_CDPixmapAddEn:
+ {
+ return iconPool()->pixmap(PixmapType_CDAttachmentAddEn);
+ }
+ case R_CDPixmapAddDis:
+ {
+ return iconPool()->pixmap(PixmapType_CDAttachmentAddDis);
+ }
+ case R_FDPixmapAddEn:
+ {
+ return iconPool()->pixmap(PixmapType_FDAttachmentAddEn);
+ }
+ case R_FDPixmapAddDis:
+ {
+ return iconPool()->pixmap(PixmapType_FDAttachmentAddDis);
+ }
+ case R_HDPixmapRect:
+ {
+ int iMargin = data(specifiedIndex, R_Margin).toInt();
+ int iWidth = data(specifiedIndex, R_IconSize).toInt();
+ return QRect(0 - iWidth - iMargin, iMargin, iWidth, iWidth);
+ }
+ case R_CDPixmapRect:
+ {
+ int iMargin = data(specifiedIndex, R_Margin).toInt();
+ int iSpacing = data(specifiedIndex, R_Spacing).toInt();
+ int iWidth = data(specifiedIndex, R_IconSize).toInt();
+ return QRect(0 - iWidth - iSpacing - iWidth - iMargin, iMargin, iWidth, iWidth);
+ }
+ case R_FDPixmapRect:
+ {
+ int iMargin = data(specifiedIndex, R_Margin).toInt();
+ int iWidth = data(specifiedIndex, R_IconSize).toInt();
+ return QRect(0 - iWidth - iMargin, iMargin, iWidth, iWidth);
+ }
+
+ default:
+ break;
+ }
+ return QVariant();
+}
+
+bool StorageModel::setData(const QModelIndex &specifiedIndex, const QVariant &aValue, int iRole)
+{
+ if (!specifiedIndex.isValid())
+ return QAbstractItemModel::setData(specifiedIndex, aValue, iRole);
+
+ switch (iRole)
+ {
+ case R_ToolTipType:
+ {
+ m_enmToolTipType = aValue.value<ToolTipType>();
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ case R_CtrName:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ {
+ qobject_cast<ControllerItem*>(pItem)->setName(aValue.toString());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ case R_CtrBusType:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ {
+ /* Acquire controller item and requested storage bus type: */
+ ControllerItem *pItemController = qobject_cast<ControllerItem*>(pItem);
+ const KStorageBus enmNewCtrBusType = aValue.value<KStorageBus>();
+
+ /* PCIe devices allows for hard-drives attachments only,
+ * no optical devices. So, lets make sure that rule is fulfilled. */
+ if (enmNewCtrBusType == KStorageBus_PCIe)
+ {
+ const QList<QUuid> opticalIds = pItemController->attachmentIDs(KDeviceType_DVD);
+ if (!opticalIds.isEmpty())
+ {
+ if (!msgCenter().confirmStorageBusChangeWithOpticalRemoval(qobject_cast<QWidget*>(QObject::parent())))
+ return false;
+ foreach (const QUuid &uId, opticalIds)
+ delAttachment(pItemController->id(), uId);
+ }
+ }
+
+ /* Lets make sure there is enough of place for all the remaining attachments: */
+ const uint uMaxPortCount =
+ (uint)uiCommon().virtualBox().GetSystemProperties().GetMaxPortCountForStorageBus(enmNewCtrBusType);
+ const uint uMaxDevicePerPortCount =
+ (uint)uiCommon().virtualBox().GetSystemProperties().GetMaxDevicesPerPortForStorageBus(enmNewCtrBusType);
+ const QList<QUuid> ids = pItemController->attachmentIDs();
+ if (uMaxPortCount * uMaxDevicePerPortCount < (uint)ids.size())
+ {
+ if (!msgCenter().confirmStorageBusChangeWithExcessiveRemoval(qobject_cast<QWidget*>(QObject::parent())))
+ return false;
+ for (int i = uMaxPortCount * uMaxDevicePerPortCount; i < ids.size(); ++i)
+ delAttachment(pItemController->id(), ids.at(i));
+ }
+
+ /* Push new bus/controller type: */
+ pItemController->setBus(enmNewCtrBusType);
+ pItemController->setType(pItemController->types(enmNewCtrBusType).first());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+
+ /* Make sure each of remaining attachments has valid slot: */
+ foreach (AbstractItem *pChildItem, pItemController->attachments())
+ {
+ AttachmentItem *pChildItemAttachment = qobject_cast<AttachmentItem*>(pChildItem);
+ const SlotsList availableSlots = pChildItemAttachment->storageSlots();
+ const StorageSlot currentSlot = pChildItemAttachment->storageSlot();
+ if (!availableSlots.isEmpty() && !availableSlots.contains(currentSlot))
+ pChildItemAttachment->setStorageSlot(availableSlots.first());
+ }
+
+ /* This means success: */
+ return true;
+ }
+ return false;
+ }
+ case R_CtrType:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ {
+ qobject_cast<ControllerItem*>(pItem)->setType(aValue.value<KStorageControllerType>());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ case R_CtrPortCount:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ {
+ qobject_cast<ControllerItem*>(pItem)->setPortCount(aValue.toUInt());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ case R_CtrIoCache:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_ControllerItem)
+ {
+ qobject_cast<ControllerItem*>(pItem)->setUseIoCache(aValue.toBool());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ case R_AttSlot:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ {
+ qobject_cast<AttachmentItem*>(pItem)->setStorageSlot(aValue.value<StorageSlot>());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ sort();
+ return true;
+ }
+ return false;
+ }
+ case R_AttDevice:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ {
+ qobject_cast<AttachmentItem*>(pItem)->setDeviceType(aValue.value<KDeviceType>());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ case R_AttMediumId:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ {
+ qobject_cast<AttachmentItem*>(pItem)->setMediumId(aValue.toUuid());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ case R_AttIsPassthrough:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ {
+ qobject_cast<AttachmentItem*>(pItem)->setPassthrough(aValue.toBool());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ case R_AttIsTempEject:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ {
+ qobject_cast<AttachmentItem*>(pItem)->setTempEject(aValue.toBool());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ case R_AttIsNonRotational:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ {
+ qobject_cast<AttachmentItem*>(pItem)->setNonRotational(aValue.toBool());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ case R_AttIsHotPluggable:
+ {
+ if (AbstractItem *pItem = static_cast<AbstractItem*>(specifiedIndex.internalPointer()))
+ if (pItem->rtti() == AbstractItem::Type_AttachmentItem)
+ {
+ qobject_cast<AttachmentItem*>(pItem)->setHotPluggable(aValue.toBool());
+ emit dataChanged(specifiedIndex, specifiedIndex);
+ return true;
+ }
+ return false;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+QModelIndex StorageModel::addController(const QString &aCtrName, KStorageBus enmBus, KStorageControllerType enmType)
+{
+ beginInsertRows(root(), m_pRootItem->childCount(), m_pRootItem->childCount());
+ new ControllerItem(m_pRootItem, aCtrName, enmBus, enmType);
+ endInsertRows();
+ return index(m_pRootItem->childCount() - 1, 0, root());
+}
+
+void StorageModel::delController(const QUuid &uCtrId)
+{
+ if (AbstractItem *pItem = m_pRootItem->childItemById(uCtrId))
+ {
+ int iItemPosition = m_pRootItem->posOfChild(pItem);
+ beginRemoveRows(root(), iItemPosition, iItemPosition);
+ delete pItem;
+ endRemoveRows();
+ }
+}
+
+QModelIndex StorageModel::addAttachment(const QUuid &uCtrId, KDeviceType enmDeviceType, const QUuid &uMediumId)
+{
+ if (AbstractItem *pParentItem = m_pRootItem->childItemById(uCtrId))
+ {
+ int iParentPosition = m_pRootItem->posOfChild(pParentItem);
+ QModelIndex parentIndex = index(iParentPosition, 0, root());
+ beginInsertRows(parentIndex, pParentItem->childCount(), pParentItem->childCount());
+ AttachmentItem *pItem = new AttachmentItem(pParentItem, enmDeviceType);
+ pItem->setHotPluggable(m_enmConfigurationAccessLevel != ConfigurationAccessLevel_Full);
+ pItem->setMediumId(uMediumId);
+ endInsertRows();
+ return index(pParentItem->childCount() - 1, 0, parentIndex);
+ }
+ return QModelIndex();
+}
+
+void StorageModel::delAttachment(const QUuid &uCtrId, const QUuid &uAttId)
+{
+ if (AbstractItem *pParentItem = m_pRootItem->childItemById(uCtrId))
+ {
+ int iParentPosition = m_pRootItem->posOfChild(pParentItem);
+ if (AbstractItem *pItem = pParentItem->childItemById(uAttId))
+ {
+ int iItemPosition = pParentItem->posOfChild(pItem);
+ beginRemoveRows(index(iParentPosition, 0, root()), iItemPosition, iItemPosition);
+ delete pItem;
+ endRemoveRows();
+ }
+ }
+}
+
+void StorageModel::moveAttachment(const QUuid &uAttId, const QUuid &uCtrOldId, const QUuid &uCtrNewId)
+{
+ /* No known info about attachment device type and medium ID: */
+ KDeviceType enmDeviceType = KDeviceType_Null;
+ QUuid uMediumId;
+
+ /* First of all we are looking for old controller item: */
+ AbstractItem *pOldItem = m_pRootItem->childItemById(uCtrOldId);
+ if (pOldItem)
+ {
+ /* And acquire controller position: */
+ const int iOldCtrPosition = m_pRootItem->posOfChild(pOldItem);
+
+ /* Then we are looking for an attachment item: */
+ if (AbstractItem *pSubItem = pOldItem->childItemById(uAttId))
+ {
+ /* And make sure this is really an attachment: */
+ AttachmentItem *pSubItemAttachment = qobject_cast<AttachmentItem*>(pSubItem);
+ if (pSubItemAttachment)
+ {
+ /* This way we can acquire actual attachment device type and medium ID: */
+ enmDeviceType = pSubItemAttachment->deviceType();
+ uMediumId = pSubItemAttachment->mediumId();
+
+ /* And delete atachment item finally: */
+ const int iAttPosition = pOldItem->posOfChild(pSubItem);
+ beginRemoveRows(index(iOldCtrPosition, 0, root()), iAttPosition, iAttPosition);
+ delete pSubItem;
+ endRemoveRows();
+ }
+ }
+ }
+
+ /* As the last step we are looking for new controller item: */
+ AbstractItem *pNewItem = m_pRootItem->childItemById(uCtrNewId);
+ if (pNewItem)
+ {
+ /* And acquire controller position: */
+ const int iNewCtrPosition = m_pRootItem->posOfChild(pNewItem);
+
+ /* Then we have to make sure moved attachment is valid: */
+ if (enmDeviceType != KDeviceType_Null)
+ {
+ /* And create new attachment item finally: */
+ QModelIndex newCtrIndex = index(iNewCtrPosition, 0, root());
+ beginInsertRows(newCtrIndex, pNewItem->childCount(), pNewItem->childCount());
+ AttachmentItem *pItem = new AttachmentItem(pNewItem, enmDeviceType);
+ pItem->setHotPluggable(m_enmConfigurationAccessLevel != ConfigurationAccessLevel_Full);
+ pItem->setMediumId(uMediumId);
+ endInsertRows();
+ }
+ }
+}
+
+KDeviceType StorageModel::attachmentDeviceType(const QUuid &uCtrId, const QUuid &uAttId) const
+{
+ /* First of all we are looking for top-level (controller) item: */
+ AbstractItem *pTopLevelItem = m_pRootItem->childItemById(uCtrId);
+ if (pTopLevelItem)
+ {
+ /* Then we are looking for sub-level (attachment) item: */
+ AbstractItem *pSubLevelItem = pTopLevelItem->childItemById(uAttId);
+ if (pSubLevelItem)
+ {
+ /* And make sure this is really an attachment: */
+ AttachmentItem *pAttachmentItem = qobject_cast<AttachmentItem*>(pSubLevelItem);
+ if (pAttachmentItem)
+ {
+ /* This way we can acquire actual attachment device type: */
+ return pAttachmentItem->deviceType();
+ }
+ }
+ }
+
+ /* Null by default: */
+ return KDeviceType_Null;
+}
+
+void StorageModel::setMachineId(const QUuid &uMachineId)
+{
+ m_pRootItem->setMachineId(uMachineId);
+}
+
+void StorageModel::sort(int /* iColumn */, Qt::SortOrder enmOrder)
+{
+ /* Count of controller items: */
+ int iItemLevel1Count = m_pRootItem->childCount();
+ /* For each of controller items: */
+ for (int iItemLevel1Pos = 0; iItemLevel1Pos < iItemLevel1Count; ++iItemLevel1Pos)
+ {
+ /* Get iterated controller item: */
+ AbstractItem *pItemLevel1 = m_pRootItem->childItem(iItemLevel1Pos);
+ ControllerItem *pControllerItem = qobject_cast<ControllerItem*>(pItemLevel1);
+ /* Count of attachment items: */
+ int iItemLevel2Count = pItemLevel1->childCount();
+ /* Prepare empty list for sorted attachments: */
+ QList<AbstractItem*> newAttachments;
+ /* For each of attachment items: */
+ for (int iItemLevel2Pos = 0; iItemLevel2Pos < iItemLevel2Count; ++iItemLevel2Pos)
+ {
+ /* Get iterated attachment item: */
+ AbstractItem *pItemLevel2 = pItemLevel1->childItem(iItemLevel2Pos);
+ AttachmentItem *pAttachmentItem = qobject_cast<AttachmentItem*>(pItemLevel2);
+ /* Get iterated attachment storage slot: */
+ StorageSlot attachmentSlot = pAttachmentItem->storageSlot();
+ int iInsertPosition = 0;
+ for (; iInsertPosition < newAttachments.size(); ++iInsertPosition)
+ {
+ /* Get sorted attachment item: */
+ AbstractItem *pNewItemLevel2 = newAttachments[iInsertPosition];
+ AttachmentItem *pNewAttachmentItem = qobject_cast<AttachmentItem*>(pNewItemLevel2);
+ /* Get sorted attachment storage slot: */
+ StorageSlot newAttachmentSlot = pNewAttachmentItem->storageSlot();
+ /* Apply sorting rule: */
+ if (((enmOrder == Qt::AscendingOrder) && (attachmentSlot < newAttachmentSlot)) ||
+ ((enmOrder == Qt::DescendingOrder) && (attachmentSlot > newAttachmentSlot)))
+ break;
+ }
+ /* Insert iterated attachment into sorted position: */
+ newAttachments.insert(iInsertPosition, pItemLevel2);
+ }
+
+ /* If that controller has attachments: */
+ if (iItemLevel2Count)
+ {
+ /* We should update corresponding model-indexes: */
+ QModelIndex controllerIndex = index(iItemLevel1Pos, 0, root());
+ pControllerItem->setAttachments(newAttachments);
+ /* That is actually beginMoveRows() + endMoveRows() which
+ * unfortunately become available only in Qt 4.6 version. */
+ beginRemoveRows(controllerIndex, 0, iItemLevel2Count - 1);
+ endRemoveRows();
+ beginInsertRows(controllerIndex, 0, iItemLevel2Count - 1);
+ endInsertRows();
+ }
+ }
+}
+
+QModelIndex StorageModel::attachmentBySlot(QModelIndex controllerIndex, StorageSlot attachmentStorageSlot)
+{
+ /* Check what parent model index is valid, set and of 'controller' type: */
+ AssertMsg(controllerIndex.isValid(), ("Controller index should be valid!\n"));
+ AbstractItem *pParentItem = static_cast<AbstractItem*>(controllerIndex.internalPointer());
+ AssertMsg(pParentItem, ("Parent item should be set!\n"));
+ AssertMsg(pParentItem->rtti() == AbstractItem::Type_ControllerItem, ("Parent item should be of 'controller' type!\n"));
+ NOREF(pParentItem);
+
+ /* Search for suitable attachment one by one: */
+ for (int i = 0; i < rowCount(controllerIndex); ++i)
+ {
+ QModelIndex curAttachmentIndex = index(i, 0, controllerIndex);
+ StorageSlot curAttachmentStorageSlot = data(curAttachmentIndex, R_AttSlot).value<StorageSlot>();
+ if (curAttachmentStorageSlot == attachmentStorageSlot)
+ return curAttachmentIndex;
+ }
+ return QModelIndex();
+}
+
+KChipsetType StorageModel::chipsetType() const
+{
+ return m_enmChipsetType;
+}
+
+void StorageModel::setChipsetType(KChipsetType enmChipsetType)
+{
+ m_enmChipsetType = enmChipsetType;
+}
+
+void StorageModel::setConfigurationAccessLevel(ConfigurationAccessLevel enmConfigurationAccessLevel)
+{
+ m_enmConfigurationAccessLevel = enmConfigurationAccessLevel;
+}
+
+void StorageModel::clear()
+{
+ while (m_pRootItem->childCount())
+ {
+ beginRemoveRows(root(), 0, 0);
+ delete m_pRootItem->childItem(0);
+ endRemoveRows();
+ }
+}
+
+QMap<KStorageBus, int> StorageModel::currentControllerTypes() const
+{
+ QMap<KStorageBus, int> currentMap;
+ for (int iStorageBusType = KStorageBus_IDE; iStorageBusType < KStorageBus_Max; ++iStorageBusType)
+ {
+ currentMap.insert((KStorageBus)iStorageBusType,
+ qobject_cast<RootItem*>(m_pRootItem)->childCount((KStorageBus)iStorageBusType));
+ }
+ return currentMap;
+}
+
+QMap<KStorageBus, int> StorageModel::maximumControllerTypes() const
+{
+ QMap<KStorageBus, int> maximumMap;
+ for (int iStorageBusType = KStorageBus_IDE; iStorageBusType < KStorageBus_Max; ++iStorageBusType)
+ {
+ maximumMap.insert((KStorageBus)iStorageBusType,
+ uiCommon().virtualBox().GetSystemProperties().GetMaxInstancesOfStorageBus(chipsetType(), (KStorageBus)iStorageBusType));
+ }
+ return maximumMap;
+}
+
+/* static */
+KStorageBus StorageModel::roleToBus(StorageModel::DataRole enmRole)
+{
+ QMap<StorageModel::DataRole, KStorageBus> typeRoles;
+ typeRoles[StorageModel::R_CtrTypesForIDE] = KStorageBus_IDE;
+ typeRoles[StorageModel::R_CtrTypesForSATA] = KStorageBus_SATA;
+ typeRoles[StorageModel::R_CtrTypesForSCSI] = KStorageBus_SCSI;
+ typeRoles[StorageModel::R_CtrTypesForFloppy] = KStorageBus_Floppy;
+ typeRoles[StorageModel::R_CtrTypesForSAS] = KStorageBus_SAS;
+ typeRoles[StorageModel::R_CtrTypesForUSB] = KStorageBus_USB;
+ typeRoles[StorageModel::R_CtrTypesForPCIe] = KStorageBus_PCIe;
+ typeRoles[StorageModel::R_CtrTypesForVirtioSCSI] = KStorageBus_VirtioSCSI;
+ return typeRoles.value(enmRole);
+}
+
+/* static */
+StorageModel::DataRole StorageModel::busToRole(KStorageBus enmBus)
+{
+ QMap<KStorageBus, StorageModel::DataRole> typeRoles;
+ typeRoles[KStorageBus_IDE] = StorageModel::R_CtrTypesForIDE;
+ typeRoles[KStorageBus_SATA] = StorageModel::R_CtrTypesForSATA;
+ typeRoles[KStorageBus_SCSI] = StorageModel::R_CtrTypesForSCSI;
+ typeRoles[KStorageBus_Floppy] = StorageModel::R_CtrTypesForFloppy;
+ typeRoles[KStorageBus_SAS] = StorageModel::R_CtrTypesForSAS;
+ typeRoles[KStorageBus_USB] = StorageModel::R_CtrTypesForUSB;
+ typeRoles[KStorageBus_PCIe] = StorageModel::R_CtrTypesForPCIe;
+ typeRoles[KStorageBus_VirtioSCSI] = StorageModel::R_CtrTypesForVirtioSCSI;
+ return typeRoles.value(enmBus);
+}
+
+Qt::ItemFlags StorageModel::flags(const QModelIndex &specifiedIndex) const
+{
+ return !specifiedIndex.isValid() ? QAbstractItemModel::flags(specifiedIndex) :
+ Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+
+/*********************************************************************************************************************************
+* Class StorageDelegate implementation. *
+*********************************************************************************************************************************/
+
+StorageDelegate::StorageDelegate(QObject *pParent)
+ : QItemDelegate(pParent)
+{
+}
+
+void StorageDelegate::paint(QPainter *pPainter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if (!index.isValid()) return;
+
+ /* Initialize variables: */
+ QStyle::State enmState = option.state;
+ QRect rect = option.rect;
+ const StorageModel *pModel = qobject_cast<const StorageModel*>(index.model());
+ Assert(pModel);
+
+ pPainter->save();
+
+ /* Draw item background: */
+ QItemDelegate::drawBackground(pPainter, option, index);
+
+ /* Setup foreground settings: */
+ QPalette::ColorGroup cg = enmState & QStyle::State_Active ? QPalette::Active : QPalette::Inactive;
+ bool fSelected = enmState & QStyle::State_Selected;
+ bool fFocused = enmState & QStyle::State_HasFocus;
+ bool fGrayOnLoosingFocus = QApplication::style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, &option) != 0;
+ pPainter->setPen(option.palette.color(cg, fSelected &&(fFocused || !fGrayOnLoosingFocus) ?
+ QPalette::HighlightedText : QPalette::Text));
+
+ pPainter->translate(rect.x(), rect.y());
+
+ /* Draw Item Pixmap: */
+ pPainter->drawPixmap(pModel->data(index, StorageModel::R_ItemPixmapRect).toRect().topLeft(),
+ pModel->data(index, StorageModel::R_ItemPixmap).value<QPixmap>());
+
+ /* Draw compressed item name: */
+ int iMargin = pModel->data(index, StorageModel::R_Margin).toInt();
+ int iIconWidth = pModel->data(index, StorageModel::R_IconSize).toInt();
+ int iSpacing = pModel->data(index, StorageModel::R_Spacing).toInt();
+ QPoint textPosition = pModel->data(index, StorageModel::R_ItemNamePoint).toPoint();
+ int iTextWidth = rect.width() - textPosition.x();
+ if (pModel->data(index, StorageModel::R_IsController).toBool() && enmState & QStyle::State_Selected)
+ {
+ iTextWidth -= (2 * iSpacing + iIconWidth + iMargin);
+ if (pModel->data(index, StorageModel::R_CtrBusType).value<KStorageBus>() != KStorageBus_Floppy)
+ iTextWidth -= (iSpacing + iIconWidth);
+ }
+ QString strText(pModel->data(index, StorageModel::R_ItemName).toString());
+ QString strShortText(strText);
+ QFont font = pModel->data(index, Qt::FontRole).value<QFont>();
+ QFontMetrics fm(font);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ while ((strShortText.size() > 1) && (fm.horizontalAdvance(strShortText) + fm.horizontalAdvance("...") > iTextWidth))
+#else
+ while ((strShortText.size() > 1) && (fm.width(strShortText) + fm.width("...") > iTextWidth))
+#endif
+ strShortText.truncate(strShortText.size() - 1);
+ if (strShortText != strText)
+ strShortText += "...";
+ pPainter->setFont(font);
+ pPainter->drawText(textPosition, strShortText);
+
+ /* Draw Controller Additions: */
+ if (pModel->data(index, StorageModel::R_IsController).toBool() && enmState & QStyle::State_Selected)
+ {
+ DeviceTypeList devicesList(pModel->data(index, StorageModel::R_CtrDevices).value<DeviceTypeList>());
+ for (int i = 0; i < devicesList.size(); ++ i)
+ {
+ KDeviceType enmDeviceType = devicesList[i];
+
+ QRect deviceRect;
+ QPixmap devicePixmap;
+ switch (enmDeviceType)
+ {
+ case KDeviceType_HardDisk:
+ {
+ deviceRect = pModel->data(index, StorageModel::R_HDPixmapRect).value<QRect>();
+ devicePixmap = pModel->data(index, StorageModel::R_IsMoreAttachmentsPossible).toBool() ?
+ pModel->data(index, StorageModel::R_HDPixmapAddEn).value<QPixmap>() :
+ pModel->data(index, StorageModel::R_HDPixmapAddDis).value<QPixmap>();
+ break;
+ }
+ case KDeviceType_DVD:
+ {
+ deviceRect = pModel->data(index, StorageModel::R_CDPixmapRect).value<QRect>();
+ devicePixmap = pModel->data(index, StorageModel::R_IsMoreAttachmentsPossible).toBool() ?
+ pModel->data(index, StorageModel::R_CDPixmapAddEn).value<QPixmap>() :
+ pModel->data(index, StorageModel::R_CDPixmapAddDis).value<QPixmap>();
+ break;
+ }
+ case KDeviceType_Floppy:
+ {
+ deviceRect = pModel->data(index, StorageModel::R_FDPixmapRect).value<QRect>();
+ devicePixmap = pModel->data(index, StorageModel::R_IsMoreAttachmentsPossible).toBool() ?
+ pModel->data(index, StorageModel::R_FDPixmapAddEn).value<QPixmap>() :
+ pModel->data(index, StorageModel::R_FDPixmapAddDis).value<QPixmap>();
+ break;
+ }
+ default:
+ break;
+ }
+
+ pPainter->drawPixmap(QPoint(rect.width() + deviceRect.x(), deviceRect.y()), devicePixmap);
+ }
+ }
+
+ pPainter->restore();
+
+ drawFocus(pPainter, option, rect);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIStorageSettingsEditor implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+const QString UIStorageSettingsEditor::s_strControllerMimeType = QString("application/virtualbox;value=StorageControllerID");
+const QString UIStorageSettingsEditor::s_strAttachmentMimeType = QString("application/virtualbox;value=StorageAttachmentID");
+
+UIStorageSettingsEditor::UIStorageSettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fLoadingInProgress(0)
+ , m_enmConfigurationAccessLevel(ConfigurationAccessLevel_Null)
+ , m_pActionPool(0)
+ , m_pModelStorage(0)
+ , m_pMediumIdHolder(new UIMediumIDHolder(this))
+ , m_pSplitter(0)
+ , m_pWidgetLeftPane(0)
+ , m_pLabelSeparatorLeftPane(0)
+ , m_pLayoutTree(0)
+ , m_pTreeViewStorage(0)
+ , m_pLayoutToolbar(0)
+ , m_pToolbar(0)
+ , m_pActionAddController(0)
+ , m_pActionRemoveController(0)
+ , m_pActionAddAttachment(0)
+ , m_pActionRemoveAttachment(0)
+ , m_pActionAddAttachmentHD(0)
+ , m_pActionAddAttachmentCD(0)
+ , m_pActionAddAttachmentFD(0)
+ , m_pStackRightPane(0)
+ , m_pLabelSeparatorEmpty(0)
+ , m_pLabelInfo(0)
+ , m_pLabelSeparatorParameters(0)
+ , m_pLabelName(0)
+ , m_pEditorName(0)
+ , m_pLabelType(0)
+ , m_pComboType(0)
+ , m_pLabelPortCount(0)
+ , m_pSpinboxPortCount(0)
+ , m_pCheckBoxIoCache(0)
+ , m_pLabelSeparatorAttributes(0)
+ , m_pLabelMedium(0)
+ , m_pComboSlot(0)
+ , m_pToolButtonOpen(0)
+ , m_pCheckBoxPassthrough(0)
+ , m_pCheckBoxTempEject(0)
+ , m_pCheckBoxNonRotational(0)
+ , m_pCheckBoxHotPluggable(0)
+ , m_pLabelSeparatorInformation(0)
+ , m_pLabelHDFormat(0)
+ , m_pFieldHDFormat(0)
+ , m_pLabelCDFDType(0)
+ , m_pFieldCDFDType(0)
+ , m_pLabelHDVirtualSize(0)
+ , m_pFieldHDVirtualSize(0)
+ , m_pLabelHDActualSize(0)
+ , m_pFieldHDActualSize(0)
+ , m_pLabelCDFDSize(0)
+ , m_pFieldCDFDSize(0)
+ , m_pLabelHDDetails(0)
+ , m_pFieldHDDetails(0)
+ , m_pLabelLocation(0)
+ , m_pFieldLocation(0)
+ , m_pLabelUsage(0)
+ , m_pFieldUsage(0)
+ , m_pLabelEncryption(0)
+ , m_pFieldEncryption(0)
+{
+ prepare();
+}
+
+UIStorageSettingsEditor::~UIStorageSettingsEditor()
+{
+ cleanup();
+}
+
+void UIStorageSettingsEditor::setActionPool(UIActionPool *pActionPool)
+{
+ m_pActionPool = pActionPool;
+}
+
+void UIStorageSettingsEditor::setMachineId(const QUuid &uMachineId)
+{
+ m_uMachineId = uMachineId;
+ if (m_pModelStorage)
+ m_pModelStorage->setMachineId(uMachineId);
+}
+
+void UIStorageSettingsEditor::setMachineName(const QString &strName)
+{
+ m_strMachineName = strName;
+}
+
+void UIStorageSettingsEditor::setMachineSettingsFilePath(const QString &strFilePath)
+{
+ m_strMachineSettingsFilePath = strFilePath;
+}
+
+void UIStorageSettingsEditor::setMachineGuestOSTypeId(const QString &strId)
+{
+ m_strMachineGuestOSTypeId = strId;
+}
+
+void UIStorageSettingsEditor::setConfigurationAccessLevel(ConfigurationAccessLevel enmConfigurationAccessLevel)
+{
+ /* Check whether update is required: */
+ if (m_enmConfigurationAccessLevel != enmConfigurationAccessLevel)
+ {
+ /* Update value and let model know: */
+ m_enmConfigurationAccessLevel = enmConfigurationAccessLevel;
+ if (m_pModelStorage)
+ m_pModelStorage->setConfigurationAccessLevel(enmConfigurationAccessLevel);
+
+ /* Check actual level: */
+ const bool fMachineOffline = m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full;
+ const bool fMachinePoweredOff = m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Partial_PoweredOff;
+ const bool fMachineSaved = m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Partial_Saved;
+ const bool fMachineOnline = m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Partial_Running;
+ const bool fMachineInValidMode = fMachineOffline || fMachinePoweredOff || fMachineSaved || fMachineOnline;
+
+ /* Declare required variables: */
+ const QModelIndex index = m_pTreeViewStorage->currentIndex();
+ const KDeviceType enmDeviceType = m_pModelStorage->data(index, StorageModel::R_AttDevice).value<KDeviceType>();
+
+ /* Polish left pane availability: */
+ m_pLabelSeparatorLeftPane->setEnabled(fMachineInValidMode);
+ m_pTreeViewStorage->setEnabled(fMachineInValidMode);
+
+ /* Polish empty information pane availability: */
+ m_pLabelSeparatorEmpty->setEnabled(fMachineInValidMode);
+ m_pLabelInfo->setEnabled(fMachineInValidMode);
+
+ /* Polish controllers pane availability: */
+ m_pLabelSeparatorParameters->setEnabled(fMachineInValidMode);
+ m_pLabelName->setEnabled(fMachineOffline);
+ m_pEditorName->setEnabled(fMachineOffline);
+ m_pLabelType->setEnabled(fMachineOffline);
+ m_pComboType->setEnabled(fMachineOffline);
+ m_pLabelPortCount->setEnabled(fMachineOffline);
+ m_pSpinboxPortCount->setEnabled(fMachineOffline);
+ m_pCheckBoxIoCache->setEnabled(fMachineOffline);
+
+ /* Polish attachments pane availability: */
+ m_pLabelSeparatorAttributes->setEnabled(fMachineInValidMode);
+ m_pLabelMedium->setEnabled(fMachineOffline || (fMachineOnline && enmDeviceType != KDeviceType_HardDisk));
+ m_pComboSlot->setEnabled(fMachineOffline);
+ m_pToolButtonOpen->setEnabled(fMachineOffline || (fMachineOnline && enmDeviceType != KDeviceType_HardDisk));
+ m_pCheckBoxPassthrough->setEnabled(fMachineOffline);
+ m_pCheckBoxTempEject->setEnabled(fMachineInValidMode);
+ m_pCheckBoxNonRotational->setEnabled(fMachineOffline);
+ m_pCheckBoxHotPluggable->setEnabled(fMachineOffline);
+ m_pLabelSeparatorInformation->setEnabled(fMachineInValidMode);
+ m_pLabelHDFormat->setEnabled(fMachineInValidMode);
+ m_pFieldHDFormat->setEnabled(fMachineInValidMode);
+ m_pLabelCDFDType->setEnabled(fMachineInValidMode);
+ m_pFieldCDFDType->setEnabled(fMachineInValidMode);
+ m_pLabelHDVirtualSize->setEnabled(fMachineInValidMode);
+ m_pFieldHDVirtualSize->setEnabled(fMachineInValidMode);
+ m_pLabelHDActualSize->setEnabled(fMachineInValidMode);
+ m_pFieldHDActualSize->setEnabled(fMachineInValidMode);
+ m_pLabelCDFDSize->setEnabled(fMachineInValidMode);
+ m_pFieldCDFDSize->setEnabled(fMachineInValidMode);
+ m_pLabelHDDetails->setEnabled(fMachineInValidMode);
+ m_pFieldHDDetails->setEnabled(fMachineInValidMode);
+ m_pLabelLocation->setEnabled(fMachineInValidMode);
+ m_pFieldLocation->setEnabled(fMachineInValidMode);
+ m_pLabelUsage->setEnabled(fMachineInValidMode);
+ m_pFieldUsage->setEnabled(fMachineInValidMode);
+ m_pLabelEncryption->setEnabled(fMachineInValidMode);
+ m_pFieldEncryption->setEnabled(fMachineInValidMode);
+
+ /* Update remaining stuff: */
+ sltUpdateActionStates();
+ sltGetInformation();
+ }
+}
+
+void UIStorageSettingsEditor::setChipsetType(KChipsetType enmType)
+{
+ if (m_pModelStorage)
+ {
+ /* Make sure chipset type has changed: */
+ if (m_pModelStorage->chipsetType() != enmType)
+ {
+ /* Update chipset type value: */
+ m_pModelStorage->setChipsetType(enmType);
+ sltUpdateActionStates();
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+ }
+ }
+}
+
+KChipsetType UIStorageSettingsEditor::chipsetType() const
+{
+ return m_pModelStorage ? m_pModelStorage->chipsetType() : KChipsetType_Null;
+}
+
+QMap<KStorageBus, int> UIStorageSettingsEditor::currentControllerTypes() const
+{
+ return m_pModelStorage ? m_pModelStorage->currentControllerTypes() : QMap<KStorageBus, int>();
+}
+
+QMap<KStorageBus, int> UIStorageSettingsEditor::maximumControllerTypes() const
+{
+ return m_pModelStorage ? m_pModelStorage->maximumControllerTypes() : QMap<KStorageBus, int>();
+}
+
+void UIStorageSettingsEditor::setValue(const QList<UIDataStorageController> &controllers,
+ const QList<QList<UIDataStorageAttachment> > &attachments)
+{
+ /* Clear model initially: */
+ m_pModelStorage->clear();
+
+ /* For each controller: */
+ for (int iControllerIndex = 0; iControllerIndex < controllers.size(); ++iControllerIndex)
+ {
+ /* Get old data from cache: */
+ const UIDataStorageController &oldControllerData = controllers.at(iControllerIndex);
+
+ /* Load old data from cache: */
+ const QModelIndex controllerIndex = m_pModelStorage->addController(oldControllerData.m_strName,
+ oldControllerData.m_enmBus,
+ oldControllerData.m_enmType);
+ const QUuid controllerId = QUuid(m_pModelStorage->data(controllerIndex, StorageModel::R_ItemId).toString());
+ m_pModelStorage->setData(controllerIndex, oldControllerData.m_uPortCount, StorageModel::R_CtrPortCount);
+ m_pModelStorage->setData(controllerIndex, oldControllerData.m_fUseHostIOCache, StorageModel::R_CtrIoCache);
+
+ /* For each attachment: */
+ const QList<UIDataStorageAttachment> &controllerAttachments = attachments.at(iControllerIndex);
+ for (int iAttachmentIndex = 0; iAttachmentIndex < controllerAttachments.size(); ++iAttachmentIndex)
+ {
+ /* Get old data from cache: */
+ const UIDataStorageAttachment &oldAttachmentData = controllerAttachments.at(iAttachmentIndex);
+
+ /* Load old data from cache: */
+ const QModelIndex attachmentIndex = m_pModelStorage->addAttachment(controllerId,
+ oldAttachmentData.m_enmDeviceType,
+ oldAttachmentData.m_uMediumId);
+ const StorageSlot attachmentStorageSlot(oldControllerData.m_enmBus,
+ oldAttachmentData.m_iPort,
+ oldAttachmentData.m_iDevice);
+ m_pModelStorage->setData(attachmentIndex, QVariant::fromValue(attachmentStorageSlot), StorageModel::R_AttSlot);
+ m_pModelStorage->setData(attachmentIndex, oldAttachmentData.m_fPassthrough, StorageModel::R_AttIsPassthrough);
+ m_pModelStorage->setData(attachmentIndex, oldAttachmentData.m_fTempEject, StorageModel::R_AttIsTempEject);
+ m_pModelStorage->setData(attachmentIndex, oldAttachmentData.m_fNonRotational, StorageModel::R_AttIsNonRotational);
+ m_pModelStorage->setData(attachmentIndex, oldAttachmentData.m_fHotPluggable, StorageModel::R_AttIsHotPluggable);
+ }
+ }
+
+ /* Choose first controller as current: */
+ if (m_pModelStorage->rowCount(m_pModelStorage->root()) > 0)
+ m_pTreeViewStorage->setCurrentIndex(m_pModelStorage->index(0, 0, m_pModelStorage->root()));
+
+ /* Fetch recent information: */
+ sltHandleCurrentItemChange();
+}
+
+void UIStorageSettingsEditor::getValue(QList<UIDataStorageController> &controllers,
+ QList<QList<UIDataStorageAttachment> > &attachments)
+{
+ /* For each controller: */
+ const QModelIndex rootIndex = m_pModelStorage->root();
+ for (int iControllerIndex = 0; iControllerIndex < m_pModelStorage->rowCount(rootIndex); ++iControllerIndex)
+ {
+ /* Prepare new data & key: */
+ UIDataStorageController newControllerData;
+
+ /* Gather new data & cache key from model: */
+ const QModelIndex controllerIndex = m_pModelStorage->index(iControllerIndex, 0, rootIndex);
+ newControllerData.m_strName = m_pModelStorage->data(controllerIndex, StorageModel::R_CtrName).toString();
+ newControllerData.m_enmBus = m_pModelStorage->data(controllerIndex, StorageModel::R_CtrBusType).value<KStorageBus>();
+ newControllerData.m_enmType = m_pModelStorage->data(controllerIndex, StorageModel::R_CtrType).value<KStorageControllerType>();
+ newControllerData.m_uPortCount = m_pModelStorage->data(controllerIndex, StorageModel::R_CtrPortCount).toUInt();
+ newControllerData.m_fUseHostIOCache = m_pModelStorage->data(controllerIndex, StorageModel::R_CtrIoCache).toBool();
+ newControllerData.m_strKey = newControllerData.m_strName;
+
+ /* For each attachment: */
+ QList<UIDataStorageAttachment> controllerAttachments;
+ for (int iAttachmentIndex = 0; iAttachmentIndex < m_pModelStorage->rowCount(controllerIndex); ++iAttachmentIndex)
+ {
+ /* Prepare new data & key: */
+ UIDataStorageAttachment newAttachmentData;
+
+ /* Gather new data & cache key from model: */
+ const QModelIndex attachmentIndex = m_pModelStorage->index(iAttachmentIndex, 0, controllerIndex);
+ newAttachmentData.m_enmDeviceType = m_pModelStorage->data(attachmentIndex, StorageModel::R_AttDevice).value<KDeviceType>();
+ const StorageSlot attachmentSlot = m_pModelStorage->data(attachmentIndex, StorageModel::R_AttSlot).value<StorageSlot>();
+ newAttachmentData.m_iPort = attachmentSlot.port;
+ newAttachmentData.m_iDevice = attachmentSlot.device;
+ newAttachmentData.m_fPassthrough = m_pModelStorage->data(attachmentIndex, StorageModel::R_AttIsPassthrough).toBool();
+ newAttachmentData.m_fTempEject = m_pModelStorage->data(attachmentIndex, StorageModel::R_AttIsTempEject).toBool();
+ newAttachmentData.m_fNonRotational = m_pModelStorage->data(attachmentIndex, StorageModel::R_AttIsNonRotational).toBool();
+ newAttachmentData.m_fHotPluggable = m_pModelStorage->data(attachmentIndex, StorageModel::R_AttIsHotPluggable).toBool();
+ newAttachmentData.m_uMediumId = QUuid(m_pModelStorage->data(attachmentIndex, StorageModel::R_AttMediumId).toString());
+ newAttachmentData.m_strKey = QString("%1:%2").arg(newAttachmentData.m_iPort)
+ .arg(newAttachmentData.m_iDevice);
+
+ /* Cache new data: */
+ controllerAttachments << newAttachmentData;
+ }
+
+ /* Cache new data: */
+ controllers << newControllerData;
+ attachments << controllerAttachments;
+ }
+}
+
+void UIStorageSettingsEditor::retranslateUi()
+{
+ m_pLabelSeparatorLeftPane->setText(tr("&Storage Devices"));
+ m_pLabelSeparatorEmpty->setText(tr("Information"));
+ m_pLabelInfo->setText(tr("The Storage Tree can contain several controllers of different types. This machine currently has no "
+ "controllers."));
+ m_pLabelSeparatorParameters->setText(tr("Attributes"));
+ m_pLabelName->setText(tr("&Name:"));
+ m_pEditorName->setToolTip(tr("Holds the name of the storage controller currently selected in the Storage Tree."));
+ m_pLabelType->setText(tr("&Type:"));
+ m_pComboType->setToolTip(tr("Selects the sub-type of the storage controller currently selected in the Storage Tree."));
+ m_pLabelPortCount->setText(tr("&Port Count:"));
+ m_pSpinboxPortCount->setToolTip(tr("Selects the port count of the SATA storage controller currently selected in the "
+ "Storage Tree. This must be at least one more than the highest port number you need to "
+ "use."));
+ m_pCheckBoxIoCache->setToolTip(tr("When checked, allows to use host I/O caching capabilities."));
+ m_pCheckBoxIoCache->setText(tr("Use Host I/O Cache"));
+ m_pLabelSeparatorAttributes->setText(tr("Attributes"));
+ m_pComboSlot->setToolTip(tr("Selects the slot on the storage controller used by this attachment. The available slots depend "
+ "on the type of the controller and other attachments on it."));
+ m_pToolButtonOpen->setText(QString());
+ m_pCheckBoxPassthrough->setToolTip(tr("When checked, allows the guest to send ATAPI commands directly to the host-drive "
+ "which makes it possible to use CD/DVD writers connected to the host inside the VM. "
+ "Note that writing audio CD inside the VM is not yet supported."));
+ m_pCheckBoxPassthrough->setText(tr("&Passthrough"));
+ m_pCheckBoxTempEject->setToolTip(tr("When checked, the virtual disk will not be removed when the guest system ejects it."));
+ m_pCheckBoxTempEject->setText(tr("&Live CD/DVD"));
+ m_pCheckBoxNonRotational->setToolTip(tr("When checked, the guest system will see the virtual disk as a solid-state device."));
+ m_pCheckBoxNonRotational->setText(tr("&Solid-state Drive"));
+ m_pCheckBoxHotPluggable->setToolTip(tr("When checked, the guest system will see the virtual disk as a hot-pluggable device."));
+ m_pCheckBoxHotPluggable->setText(tr("&Hot-pluggable"));
+ m_pLabelSeparatorInformation->setText(tr("Information"));
+ m_pLabelHDFormat->setText(tr("Type (Format):"));
+ m_pLabelCDFDType->setText(tr("Type:"));
+ m_pLabelHDVirtualSize->setText(tr("Virtual Size:"));
+ m_pLabelHDActualSize->setText(tr("Actual Size:"));
+ m_pLabelCDFDSize->setText(tr("Size:"));
+ m_pLabelHDDetails->setText(tr("Details:"));
+ m_pLabelLocation->setText(tr("Location:"));
+ m_pLabelUsage->setText(tr("Attached to:"));
+ m_pLabelEncryption->setText(tr("Encrypted with key:"));
+
+ /* Translate storage-view: */
+ m_pTreeViewStorage->setWhatsThis(tr("Lists all storage controllers for this machine and "
+ "the virtual images and host drives attached to them."));
+
+ /* Translate tool-bar: */
+ m_pActionAddController->setShortcut(QKeySequence("Ins"));
+ m_pActionRemoveController->setShortcut(QKeySequence("Del"));
+ m_pActionAddAttachment->setShortcut(QKeySequence("+"));
+ m_pActionRemoveAttachment->setShortcut(QKeySequence("-"));
+
+ m_pActionAddController->setText(tr("Add Controller"));
+ m_addControllerActions.value(KStorageControllerType_PIIX3)->setText(tr("PIIX3 (IDE)"));
+ m_addControllerActions.value(KStorageControllerType_PIIX4)->setText(tr("PIIX4 (Default IDE)"));
+ m_addControllerActions.value(KStorageControllerType_ICH6)->setText(tr("ICH6 (IDE)"));
+ m_addControllerActions.value(KStorageControllerType_IntelAhci)->setText(tr("AHCI (SATA)"));
+ m_addControllerActions.value(KStorageControllerType_LsiLogic)->setText(tr("LsiLogic (Default SCSI)"));
+ m_addControllerActions.value(KStorageControllerType_BusLogic)->setText(tr("BusLogic (SCSI)"));
+ m_addControllerActions.value(KStorageControllerType_LsiLogicSas)->setText(tr("LsiLogic SAS (SAS)"));
+ m_addControllerActions.value(KStorageControllerType_I82078)->setText(tr("I82078 (Floppy)"));
+ m_addControllerActions.value(KStorageControllerType_USB)->setText(tr("USB"));
+ m_addControllerActions.value(KStorageControllerType_NVMe)->setText(tr("NVMe (PCIe)"));
+ m_addControllerActions.value(KStorageControllerType_VirtioSCSI)->setText(tr("virtio-scsi"));
+ m_pActionRemoveController->setText(tr("Remove Controller"));
+ m_pActionAddAttachment->setText(tr("Add Attachment"));
+ m_pActionAddAttachmentHD->setText(tr("Hard Disk"));
+ m_pActionAddAttachmentCD->setText(tr("Optical Drive"));
+ m_pActionAddAttachmentFD->setText(tr("Floppy Drive"));
+ m_pActionRemoveAttachment->setText(tr("Remove Attachment"));
+
+ m_pActionAddController->setToolTip(tr("Adds new storage controller."));
+ m_pActionRemoveController->setToolTip(tr("Removes selected storage controller."));
+ m_pActionAddAttachment->setToolTip(tr("Adds new storage attachment."));
+ m_pActionRemoveAttachment->setToolTip(tr("Removes selected storage attachment."));
+
+ m_pActionAddController->setToolTip(m_pActionAddController->whatsThis());
+ m_pActionRemoveController->setToolTip(m_pActionRemoveController->whatsThis());
+ m_pActionAddAttachment->setToolTip(m_pActionAddAttachment->whatsThis());
+ m_pActionRemoveAttachment->setToolTip(m_pActionRemoveAttachment->whatsThis());
+}
+
+void UIStorageSettingsEditor::showEvent(QShowEvent *pEvent)
+{
+ /* Turn splitter back to sane proportions: */
+ m_pSplitter->setSizes(QList<int>() << 0.4 * width() << 0.6 * width());
+
+ /* Call to base-class: */
+ QIWithRetranslateUI<QWidget>::showEvent(pEvent);
+}
+
+void UIStorageSettingsEditor::sltHandleMediumEnumerated(const QUuid &uMediumId)
+{
+ /* Search for corresponding medium: */
+ const UIMedium medium = uiCommon().medium(uMediumId);
+
+ const QModelIndex rootIndex = m_pModelStorage->root();
+ for (int i = 0; i < m_pModelStorage->rowCount(rootIndex); ++i)
+ {
+ const QModelIndex controllerIndex = m_pModelStorage->index(i, 0, rootIndex);
+ for (int j = 0; j < m_pModelStorage->rowCount(controllerIndex); ++j)
+ {
+ const QModelIndex attachmentIndex = m_pModelStorage->index(j, 0, controllerIndex);
+ const QUuid attMediumId(m_pModelStorage->data(attachmentIndex, StorageModel::R_AttMediumId).toString());
+ if (attMediumId == medium.id())
+ {
+ m_pModelStorage->setData(attachmentIndex, attMediumId, StorageModel::R_AttMediumId);
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+ }
+ }
+ }
+}
+
+void UIStorageSettingsEditor::sltHandleMediumDeleted(const QUuid &uMediumId)
+{
+ QModelIndex rootIndex = m_pModelStorage->root();
+ for (int i = 0; i < m_pModelStorage->rowCount(rootIndex); ++i)
+ {
+ QModelIndex controllerIndex = m_pModelStorage->index(i, 0, rootIndex);
+ for (int j = 0; j < m_pModelStorage->rowCount(controllerIndex); ++j)
+ {
+ QModelIndex attachmentIndex = m_pModelStorage->index(j, 0, controllerIndex);
+ QUuid attMediumId(m_pModelStorage->data(attachmentIndex, StorageModel::R_AttMediumId).toString());
+ if (attMediumId == uMediumId)
+ {
+ m_pModelStorage->setData(attachmentIndex, UIMedium().id(), StorageModel::R_AttMediumId);
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+ }
+ }
+ }
+}
+
+void UIStorageSettingsEditor::sltAddController()
+{
+ /* Load currently supported storage buses and types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ const QVector<KStorageBus> supportedBuses = comProperties.GetSupportedStorageBuses();
+ const QVector<KStorageControllerType> supportedTypes = comProperties.GetSupportedStorageControllerTypes();
+
+ /* Prepare menu: */
+ QMenu menu;
+ foreach (const KStorageControllerType &enmType, supportedTypes)
+ {
+ QAction *pAction = m_addControllerActions.value(enmType);
+ if (supportedBuses.contains(comProperties.GetStorageBusForStorageControllerType(enmType)))
+ menu.addAction(pAction);
+ }
+
+ /* Popup it finally: */
+ menu.exec(QCursor::pos());
+}
+
+void UIStorageSettingsEditor::sltAddControllerPIIX3()
+{
+ addControllerWrapper(generateUniqueControllerName("PIIX3"), KStorageBus_IDE, KStorageControllerType_PIIX3);
+}
+
+void UIStorageSettingsEditor::sltAddControllerPIIX4()
+{
+ addControllerWrapper(generateUniqueControllerName("PIIX4"), KStorageBus_IDE, KStorageControllerType_PIIX4);
+}
+
+void UIStorageSettingsEditor::sltAddControllerICH6()
+{
+ addControllerWrapper(generateUniqueControllerName("ICH6"), KStorageBus_IDE, KStorageControllerType_ICH6);
+}
+
+void UIStorageSettingsEditor::sltAddControllerAHCI()
+{
+ addControllerWrapper(generateUniqueControllerName("AHCI"), KStorageBus_SATA, KStorageControllerType_IntelAhci);
+}
+
+void UIStorageSettingsEditor::sltAddControllerLsiLogic()
+{
+ addControllerWrapper(generateUniqueControllerName("LsiLogic"), KStorageBus_SCSI, KStorageControllerType_LsiLogic);
+}
+
+void UIStorageSettingsEditor::sltAddControllerBusLogic()
+{
+ addControllerWrapper(generateUniqueControllerName("BusLogic"), KStorageBus_SCSI, KStorageControllerType_BusLogic);
+}
+
+void UIStorageSettingsEditor::sltAddControllerFloppy()
+{
+ addControllerWrapper(generateUniqueControllerName("Floppy"), KStorageBus_Floppy, KStorageControllerType_I82078);
+}
+
+void UIStorageSettingsEditor::sltAddControllerLsiLogicSAS()
+{
+ addControllerWrapper(generateUniqueControllerName("LsiLogic SAS"), KStorageBus_SAS, KStorageControllerType_LsiLogicSas);
+}
+
+void UIStorageSettingsEditor::sltAddControllerUSB()
+{
+ addControllerWrapper(generateUniqueControllerName("USB"), KStorageBus_USB, KStorageControllerType_USB);
+}
+
+void UIStorageSettingsEditor::sltAddControllerNVMe()
+{
+ addControllerWrapper(generateUniqueControllerName("NVMe"), KStorageBus_PCIe, KStorageControllerType_NVMe);
+}
+
+void UIStorageSettingsEditor::sltAddControllerVirtioSCSI()
+{
+ addControllerWrapper(generateUniqueControllerName("VirtIO"), KStorageBus_VirtioSCSI, KStorageControllerType_VirtioSCSI);
+}
+
+void UIStorageSettingsEditor::sltRemoveController()
+{
+ const QModelIndex index = m_pTreeViewStorage->currentIndex();
+ if (!m_pModelStorage->data(index, StorageModel::R_IsController).toBool())
+ return;
+
+ m_pModelStorage->delController(QUuid(m_pModelStorage->data(index, StorageModel::R_ItemId).toString()));
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+}
+
+void UIStorageSettingsEditor::sltAddAttachment()
+{
+ const QModelIndex index = m_pTreeViewStorage->currentIndex();
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsController).toBool());
+
+ const DeviceTypeList deviceTypeList(m_pModelStorage->data(index, StorageModel::R_CtrDevices).value<DeviceTypeList>());
+ const bool fJustTrigger = deviceTypeList.size() == 1;
+ const bool fShowMenu = deviceTypeList.size() > 1;
+ QMenu menu;
+ foreach (const KDeviceType &enmDeviceType, deviceTypeList)
+ {
+ switch (enmDeviceType)
+ {
+ case KDeviceType_HardDisk:
+ if (fJustTrigger)
+ m_pActionAddAttachmentHD->trigger();
+ if (fShowMenu)
+ menu.addAction(m_pActionAddAttachmentHD);
+ break;
+ case KDeviceType_DVD:
+ if (fJustTrigger)
+ m_pActionAddAttachmentCD->trigger();
+ if (fShowMenu)
+ menu.addAction(m_pActionAddAttachmentCD);
+ break;
+ case KDeviceType_Floppy:
+ if (fJustTrigger)
+ m_pActionAddAttachmentFD->trigger();
+ if (fShowMenu)
+ menu.addAction(m_pActionAddAttachmentFD);
+ break;
+ default:
+ break;
+ }
+ }
+ if (fShowMenu)
+ menu.exec(QCursor::pos());
+}
+
+void UIStorageSettingsEditor::sltAddAttachmentHD()
+{
+ addAttachmentWrapper(KDeviceType_HardDisk);
+}
+
+void UIStorageSettingsEditor::sltAddAttachmentCD()
+{
+ addAttachmentWrapper(KDeviceType_DVD);
+}
+
+void UIStorageSettingsEditor::sltAddAttachmentFD()
+{
+ addAttachmentWrapper(KDeviceType_Floppy);
+}
+
+void UIStorageSettingsEditor::sltRemoveAttachment()
+{
+ const QModelIndex index = m_pTreeViewStorage->currentIndex();
+
+ const KDeviceType enmDeviceType = m_pModelStorage->data(index, StorageModel::R_AttDevice).value<KDeviceType>();
+ /* Check if this would be the last DVD. If so let the user confirm this again. */
+ if ( enmDeviceType == KDeviceType_DVD
+ && deviceCount(KDeviceType_DVD) == 1)
+ {
+ if (!msgCenter().confirmRemovingOfLastDVDDevice(this))
+ return;
+ }
+
+ const QModelIndex parentIndex = index.parent();
+ if (!index.isValid() || !parentIndex.isValid() ||
+ !m_pModelStorage->data(index, StorageModel::R_IsAttachment).toBool() ||
+ !m_pModelStorage->data(parentIndex, StorageModel::R_IsController).toBool())
+ return;
+
+ m_pModelStorage->delAttachment(QUuid(m_pModelStorage->data(parentIndex, StorageModel::R_ItemId).toString()),
+ QUuid(m_pModelStorage->data(index, StorageModel::R_ItemId).toString()));
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+}
+
+void UIStorageSettingsEditor::sltGetInformation()
+{
+ m_fLoadingInProgress = true;
+
+ const QModelIndex index = m_pTreeViewStorage->currentIndex();
+ if (!index.isValid() || index == m_pModelStorage->root())
+ {
+ /* Showing Initial Page: */
+ m_pStackRightPane->setCurrentIndex(0);
+ }
+ else
+ {
+ switch (m_pModelStorage->data(index, StorageModel::R_ItemType).value<AbstractItem::ItemType>())
+ {
+ case AbstractItem::Type_ControllerItem:
+ {
+ /* Getting Controller Name: */
+ const QString strCtrName = m_pModelStorage->data(index, StorageModel::R_CtrName).toString();
+ if (m_pEditorName->text() != strCtrName)
+ m_pEditorName->setText(strCtrName);
+
+ /* Rebuild type combo: */
+ m_pComboType->clear();
+ /* Getting controller buses: */
+ const ControllerBusList controllerBusList(m_pModelStorage->data(index, StorageModel::R_CtrBusTypes).value<ControllerBusList>());
+ foreach (const KStorageBus &enmCurrentBus, controllerBusList)
+ {
+ /* Getting controller types: */
+ const ControllerTypeList controllerTypeList(m_pModelStorage->data(index, m_pModelStorage->busToRole(enmCurrentBus)).value<ControllerTypeList>());
+ foreach (const KStorageControllerType &enmCurrentType, controllerTypeList)
+ {
+ m_pComboType->addItem(gpConverter->toString(enmCurrentType));
+ m_pComboType->setItemData(m_pComboType->count() - 1, QVariant::fromValue(enmCurrentBus), StorageModel::R_CtrBusType);
+ m_pComboType->setItemData(m_pComboType->count() - 1, QVariant::fromValue(enmCurrentType), StorageModel::R_CtrType);
+ }
+ }
+ const KStorageControllerType enmType = m_pModelStorage->data(index, StorageModel::R_CtrType).value<KStorageControllerType>();
+ const int iCtrPos = m_pComboType->findData(enmType, StorageModel::R_CtrType);
+ m_pComboType->setCurrentIndex(iCtrPos == -1 ? 0 : iCtrPos);
+
+ const KStorageBus enmBus = m_pModelStorage->data(index, StorageModel::R_CtrBusType).value<KStorageBus>();
+ m_pLabelPortCount->setVisible(enmBus == KStorageBus_SATA || enmBus == KStorageBus_SAS);
+ m_pSpinboxPortCount->setVisible(enmBus == KStorageBus_SATA || enmBus == KStorageBus_SAS);
+ const uint uPortCount = m_pModelStorage->data(index, StorageModel::R_CtrPortCount).toUInt();
+ const uint uMaxPortCount = m_pModelStorage->data(index, StorageModel::R_CtrMaxPortCount).toUInt();
+ m_pSpinboxPortCount->setMaximum(uMaxPortCount);
+ m_pSpinboxPortCount->setValue(uPortCount);
+
+ const bool fUseIoCache = m_pModelStorage->data(index, StorageModel::R_CtrIoCache).toBool();
+ m_pCheckBoxIoCache->setChecked(fUseIoCache);
+
+ /* Showing Controller Page: */
+ m_pStackRightPane->setCurrentIndex(1);
+ break;
+ }
+ case AbstractItem::Type_AttachmentItem:
+ {
+ /* Getting Attachment Slot: */
+ m_pComboSlot->clear();
+ const SlotsList slotsList(m_pModelStorage->data(index, StorageModel::R_AttSlots).value<SlotsList>());
+ for (int i = 0; i < slotsList.size(); ++i)
+ m_pComboSlot->insertItem(m_pComboSlot->count(), gpConverter->toString(slotsList[i]));
+ const StorageSlot slt = m_pModelStorage->data(index, StorageModel::R_AttSlot).value<StorageSlot>();
+ const int iAttSlotPos = m_pComboSlot->findText(gpConverter->toString(slt));
+ m_pComboSlot->setCurrentIndex(iAttSlotPos == -1 ? 0 : iAttSlotPos);
+ m_pComboSlot->setToolTip(m_pComboSlot->itemText(m_pComboSlot->currentIndex()));
+
+ /* Getting Attachment Medium: */
+ const KDeviceType enmDeviceType = m_pModelStorage->data(index, StorageModel::R_AttDevice).value<KDeviceType>();
+ switch (enmDeviceType)
+ {
+ case KDeviceType_HardDisk:
+ m_pLabelMedium->setText(tr("Hard &Disk:"));
+ m_pToolButtonOpen->setIcon(iconPool()->icon(PixmapType_HDAttachmentNormal));
+ m_pToolButtonOpen->setToolTip(tr("Choose or create a virtual hard disk file. The virtual machine will "
+ "see the data in the file as the contents of the virtual hard disk."));
+ break;
+ case KDeviceType_DVD:
+ m_pLabelMedium->setText(tr("Optical &Drive:"));
+ m_pToolButtonOpen->setIcon(iconPool()->icon(PixmapType_CDAttachmentNormal));
+ m_pToolButtonOpen->setToolTip(tr("Choose a virtual optical disk or a physical drive to use with the "
+ "virtual drive. The virtual machine will see a disk inserted into the "
+ "drive with the data in the file or on the disk in the physical drive "
+ "as its contents."));
+ break;
+ case KDeviceType_Floppy:
+ m_pLabelMedium->setText(tr("Floppy &Drive:"));
+ m_pToolButtonOpen->setIcon(iconPool()->icon(PixmapType_FDAttachmentNormal));
+ m_pToolButtonOpen->setToolTip(tr("Choose a virtual floppy disk or a physical drive to use with the "
+ "virtual drive. The virtual machine will see a disk inserted into the "
+ "drive with the data in the file or on the disk in the physical drive "
+ "as its contents."));
+ break;
+ default:
+ break;
+ }
+
+ /* Get hot-pluggable state: */
+ const bool fIsHotPluggable = m_pModelStorage->data(index, StorageModel::R_AttIsHotPluggable).toBool();
+
+ /* Fetch device-type, medium-id: */
+ m_pMediumIdHolder->setType(mediumTypeToLocal(enmDeviceType));
+ m_pMediumIdHolder->setId(QUuid(m_pModelStorage->data(index, StorageModel::R_AttMediumId).toString()));
+
+ /* Get/fetch editable state: */
+ const bool fIsEditable = (m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full)
+ || ( m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Partial_Running
+ && enmDeviceType != KDeviceType_HardDisk)
+ || ( m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Partial_Running
+ && enmDeviceType == KDeviceType_HardDisk && fIsHotPluggable);
+ m_pLabelMedium->setEnabled(fIsEditable);
+ m_pToolButtonOpen->setEnabled(fIsEditable);
+
+ /* Getting Passthrough state: */
+ const bool fHostDrive = m_pModelStorage->data(index, StorageModel::R_AttIsHostDrive).toBool();
+ m_pCheckBoxPassthrough->setVisible(enmDeviceType == KDeviceType_DVD && fHostDrive);
+ m_pCheckBoxPassthrough->setChecked(fHostDrive && m_pModelStorage->data(index, StorageModel::R_AttIsPassthrough).toBool());
+
+ /* Getting TempEject state: */
+ m_pCheckBoxTempEject->setVisible(enmDeviceType == KDeviceType_DVD && !fHostDrive);
+ m_pCheckBoxTempEject->setChecked(!fHostDrive && m_pModelStorage->data(index, StorageModel::R_AttIsTempEject).toBool());
+
+ /* Getting NonRotational state: */
+ m_pCheckBoxNonRotational->setVisible(enmDeviceType == KDeviceType_HardDisk);
+ m_pCheckBoxNonRotational->setChecked(m_pModelStorage->data(index, StorageModel::R_AttIsNonRotational).toBool());
+
+ /* Fetch hot-pluggable state: */
+ m_pCheckBoxHotPluggable->setVisible(slt.bus == KStorageBus_SATA);
+ m_pCheckBoxHotPluggable->setChecked(fIsHotPluggable);
+
+ /* Update optional widgets visibility: */
+ updateAdditionalDetails(enmDeviceType);
+
+ /* Getting Other Information: */
+ m_pFieldHDFormat->setText(compressText(m_pModelStorage->data(index, StorageModel::R_AttFormat).toString()));
+ m_pFieldCDFDType->setText(compressText(m_pModelStorage->data(index, StorageModel::R_AttFormat).toString()));
+ m_pFieldHDVirtualSize->setText(compressText(m_pModelStorage->data(index, StorageModel::R_AttLogicalSize).toString()));
+ m_pFieldHDActualSize->setText(compressText(m_pModelStorage->data(index, StorageModel::R_AttSize).toString()));
+ m_pFieldCDFDSize->setText(compressText(m_pModelStorage->data(index, StorageModel::R_AttSize).toString()));
+ m_pFieldHDDetails->setText(compressText(m_pModelStorage->data(index, StorageModel::R_AttDetails).toString()));
+ m_pFieldLocation->setText(compressText(m_pModelStorage->data(index, StorageModel::R_AttLocation).toString()));
+ m_pFieldUsage->setText(compressText(m_pModelStorage->data(index, StorageModel::R_AttUsage).toString()));
+ m_pFieldEncryption->setText(compressText(m_pModelStorage->data(index, StorageModel::R_AttEncryptionPasswordID).toString()));
+
+ /* Showing Attachment Page: */
+ m_pStackRightPane->setCurrentIndex(2);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+
+ m_fLoadingInProgress = false;
+}
+
+void UIStorageSettingsEditor::sltSetInformation()
+{
+ const QModelIndex index = m_pTreeViewStorage->currentIndex();
+ if (m_fLoadingInProgress || !index.isValid() || index == m_pModelStorage->root())
+ return;
+
+ QObject *pSender = sender();
+ switch (m_pModelStorage->data(index, StorageModel::R_ItemType).value<AbstractItem::ItemType>())
+ {
+ case AbstractItem::Type_ControllerItem:
+ {
+ /* Setting Controller Name: */
+ if (pSender == m_pEditorName)
+ m_pModelStorage->setData(index, m_pEditorName->text(), StorageModel::R_CtrName);
+ /* Setting Controller Sub-Type: */
+ else if (pSender == m_pComboType)
+ {
+ const KStorageBus enmBus = m_pComboType->currentData(StorageModel::R_CtrBusType).value<KStorageBus>();
+ const KStorageControllerType enmType = m_pComboType->currentData(StorageModel::R_CtrType).value<KStorageControllerType>();
+ const bool fResult =
+ m_pModelStorage->setData(index, QVariant::fromValue(enmBus), StorageModel::R_CtrBusType);
+ if (fResult)
+ m_pModelStorage->setData(index, QVariant::fromValue(enmType), StorageModel::R_CtrType);
+ }
+ else if (pSender == m_pSpinboxPortCount)
+ m_pModelStorage->setData(index, m_pSpinboxPortCount->value(), StorageModel::R_CtrPortCount);
+ else if (pSender == m_pCheckBoxIoCache)
+ m_pModelStorage->setData(index, m_pCheckBoxIoCache->isChecked(), StorageModel::R_CtrIoCache);
+ break;
+ }
+ case AbstractItem::Type_AttachmentItem:
+ {
+ /* Setting Attachment Slot: */
+ if (pSender == m_pComboSlot)
+ {
+ QModelIndex controllerIndex = m_pModelStorage->parent(index);
+ StorageSlot attachmentStorageSlot = gpConverter->fromString<StorageSlot>(m_pComboSlot->currentText());
+ m_pModelStorage->setData(index, QVariant::fromValue(attachmentStorageSlot), StorageModel::R_AttSlot);
+ QModelIndex theSameIndexAtNewPosition = m_pModelStorage->attachmentBySlot(controllerIndex, attachmentStorageSlot);
+ AssertMsg(theSameIndexAtNewPosition.isValid(), ("Current attachment disappears!\n"));
+ m_pTreeViewStorage->setCurrentIndex(theSameIndexAtNewPosition);
+ }
+ /* Setting Attachment Medium: */
+ else if (pSender == m_pMediumIdHolder)
+ m_pModelStorage->setData(index, m_pMediumIdHolder->id(), StorageModel::R_AttMediumId);
+ else if (pSender == m_pCheckBoxPassthrough)
+ {
+ if (m_pModelStorage->data(index, StorageModel::R_AttIsHostDrive).toBool())
+ m_pModelStorage->setData(index, m_pCheckBoxPassthrough->isChecked(), StorageModel::R_AttIsPassthrough);
+ }
+ else if (pSender == m_pCheckBoxTempEject)
+ {
+ if (!m_pModelStorage->data(index, StorageModel::R_AttIsHostDrive).toBool())
+ m_pModelStorage->setData(index, m_pCheckBoxTempEject->isChecked(), StorageModel::R_AttIsTempEject);
+ }
+ else if (pSender == m_pCheckBoxNonRotational)
+ {
+ m_pModelStorage->setData(index, m_pCheckBoxNonRotational->isChecked(), StorageModel::R_AttIsNonRotational);
+ }
+ else if (pSender == m_pCheckBoxHotPluggable)
+ {
+ m_pModelStorage->setData(index, m_pCheckBoxHotPluggable->isChecked(), StorageModel::R_AttIsHotPluggable);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ emit sigValueChanged();
+ sltUpdateActionStates();
+ sltGetInformation();
+}
+
+void UIStorageSettingsEditor::sltPrepareOpenMediumMenu()
+{
+ /* This slot should be called only by open-medium menu: */
+ QMenu *pOpenMediumMenu = qobject_cast<QMenu*>(sender());
+ AssertMsg(pOpenMediumMenu, ("Can't access open-medium menu!\n"));
+ if (pOpenMediumMenu)
+ {
+ /* Erase menu initially: */
+ pOpenMediumMenu->clear();
+ /* Depending on current medium type: */
+ switch (m_pMediumIdHolder->type())
+ {
+ case UIMediumDeviceType_HardDisk:
+ {
+ /* Add "Choose a virtual hard disk" action: */
+ addChooseExistingMediumAction(pOpenMediumMenu, tr("Choose/Create a Virtual Hard Disk..."));
+ addChooseDiskFileAction(pOpenMediumMenu, tr("Choose a disk file..."));
+ pOpenMediumMenu->addSeparator();
+ /* Add recent media list: */
+ addRecentMediumActions(pOpenMediumMenu, m_pMediumIdHolder->type());
+ break;
+ }
+ case UIMediumDeviceType_DVD:
+ {
+ /* Add "Choose a virtual optical disk" action: */
+ addChooseExistingMediumAction(pOpenMediumMenu, tr("Choose/Create a Virtual Optical Disk..."));
+ addChooseDiskFileAction(pOpenMediumMenu, tr("Choose a disk file..."));
+ /* Add "Choose a physical drive" actions: */
+ addChooseHostDriveActions(pOpenMediumMenu);
+ pOpenMediumMenu->addSeparator();
+ /* Add recent media list: */
+ addRecentMediumActions(pOpenMediumMenu, m_pMediumIdHolder->type());
+ /* Add "Eject current medium" action: */
+ pOpenMediumMenu->addSeparator();
+ QAction *pEjectCurrentMedium = pOpenMediumMenu->addAction(tr("Remove Disk from Virtual Drive"));
+ pEjectCurrentMedium->setEnabled(!m_pMediumIdHolder->isNull());
+ pEjectCurrentMedium->setIcon(iconPool()->icon(PixmapType_CDUnmountEnabled, PixmapType_CDUnmountDisabled));
+ connect(pEjectCurrentMedium, &QAction::triggered, this, &UIStorageSettingsEditor::sltUnmountDevice);
+ break;
+ }
+ case UIMediumDeviceType_Floppy:
+ {
+ /* Add "Choose a virtual floppy disk" action: */
+ addChooseExistingMediumAction(pOpenMediumMenu, tr("Choose/Create a Virtual Floppy Disk..."));
+ addChooseDiskFileAction(pOpenMediumMenu, tr("Choose a disk file..."));
+ /* Add "Choose a physical drive" actions: */
+ addChooseHostDriveActions(pOpenMediumMenu);
+ pOpenMediumMenu->addSeparator();
+ /* Add recent media list: */
+ addRecentMediumActions(pOpenMediumMenu, m_pMediumIdHolder->type());
+ /* Add "Eject current medium" action: */
+ pOpenMediumMenu->addSeparator();
+ QAction *pEjectCurrentMedium = pOpenMediumMenu->addAction(tr("Remove Disk from Virtual Drive"));
+ pEjectCurrentMedium->setEnabled(!m_pMediumIdHolder->isNull());
+ pEjectCurrentMedium->setIcon(iconPool()->icon(PixmapType_FDUnmountEnabled, PixmapType_FDUnmountDisabled));
+ connect(pEjectCurrentMedium, &QAction::triggered, this, &UIStorageSettingsEditor::sltUnmountDevice);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void UIStorageSettingsEditor::sltUnmountDevice()
+{
+ m_pMediumIdHolder->setId(UIMedium().id());
+}
+
+void UIStorageSettingsEditor::sltChooseExistingMedium()
+{
+ const QString strMachineFolder(QFileInfo(m_strMachineSettingsFilePath).absolutePath());
+
+ QUuid uCurrentMediumId;
+ if (m_pMediumIdHolder)
+ uCurrentMediumId = m_pMediumIdHolder->id();
+ QUuid uSelectedMediumId;
+ int iResult = UIMediumSelector::openMediumSelectorDialog(window(), m_pMediumIdHolder->type(), uCurrentMediumId, uSelectedMediumId,
+ strMachineFolder, m_strMachineName,
+ m_strMachineGuestOSTypeId,
+ true /* enable create action: */, m_uMachineId, m_pActionPool);
+
+ if (iResult == UIMediumSelector::ReturnCode_Rejected ||
+ (iResult == UIMediumSelector::ReturnCode_Accepted && uSelectedMediumId.isNull()))
+ return;
+ if (iResult == static_cast<int>(UIMediumSelector::ReturnCode_LeftEmpty) &&
+ (m_pMediumIdHolder->type() != UIMediumDeviceType_DVD && m_pMediumIdHolder->type() != UIMediumDeviceType_Floppy))
+ return;
+
+ m_pMediumIdHolder->setId(uSelectedMediumId);
+}
+
+void UIStorageSettingsEditor::sltChooseDiskFile()
+{
+ const QString strMachineFolder(QFileInfo(m_strMachineSettingsFilePath).absolutePath());
+
+ QUuid uMediumId = uiCommon().openMediumWithFileOpenDialog(m_pMediumIdHolder->type(), QApplication::activeWindow(), strMachineFolder);
+ if (uMediumId.isNull())
+ return;
+ m_pMediumIdHolder->setId(uMediumId);
+}
+
+void UIStorageSettingsEditor::sltChooseHostDrive()
+{
+ /* This slot should be called ONLY by choose-host-drive action: */
+ QAction *pChooseHostDriveAction = qobject_cast<QAction*>(sender());
+ AssertMsg(pChooseHostDriveAction, ("Can't access choose-host-drive action!\n"));
+ if (pChooseHostDriveAction)
+ m_pMediumIdHolder->setId(QUuid(pChooseHostDriveAction->data().toString()));
+}
+
+void UIStorageSettingsEditor::sltChooseRecentMedium()
+{
+ /* This slot should be called ONLY by choose-recent-medium action: */
+ QAction *pChooseRecentMediumAction = qobject_cast<QAction*>(sender());
+ AssertMsg(pChooseRecentMediumAction, ("Can't access choose-recent-medium action!\n"));
+ if (pChooseRecentMediumAction)
+ {
+ /* Get recent medium type & name: */
+ const QStringList mediumInfoList = pChooseRecentMediumAction->data().toString().split(',');
+ const UIMediumDeviceType enmMediumType = (UIMediumDeviceType)mediumInfoList[0].toUInt();
+ const QString strMediumLocation = mediumInfoList[1];
+ const QUuid uMediumId = uiCommon().openMedium(enmMediumType, strMediumLocation, this);
+ if (!uMediumId.isNull())
+ m_pMediumIdHolder->setId(uMediumId);
+ }
+}
+
+void UIStorageSettingsEditor::sltUpdateActionStates()
+{
+ const QModelIndex index = m_pTreeViewStorage->currentIndex();
+
+ const bool fIDEPossible = m_pModelStorage->data(index, StorageModel::R_IsMoreIDEControllersPossible).toBool();
+ const bool fSATAPossible = m_pModelStorage->data(index, StorageModel::R_IsMoreSATAControllersPossible).toBool();
+ const bool fSCSIPossible = m_pModelStorage->data(index, StorageModel::R_IsMoreSCSIControllersPossible).toBool();
+ const bool fFloppyPossible = m_pModelStorage->data(index, StorageModel::R_IsMoreFloppyControllersPossible).toBool();
+ const bool fSASPossible = m_pModelStorage->data(index, StorageModel::R_IsMoreSASControllersPossible).toBool();
+ const bool fUSBPossible = m_pModelStorage->data(index, StorageModel::R_IsMoreUSBControllersPossible).toBool();
+ const bool fNVMePossible = m_pModelStorage->data(index, StorageModel::R_IsMoreNVMeControllersPossible).toBool();
+ const bool fVirtioSCSIPossible = m_pModelStorage->data(index, StorageModel::R_IsMoreVirtioSCSIControllersPossible).toBool();
+
+ const bool fController = m_pModelStorage->data(index, StorageModel::R_IsController).toBool();
+ const bool fAttachment = m_pModelStorage->data(index, StorageModel::R_IsAttachment).toBool();
+ const bool fAttachmentsPossible = m_pModelStorage->data(index, StorageModel::R_IsMoreAttachmentsPossible).toBool();
+ const bool fIsAttachmentHotPluggable = m_pModelStorage->data(index, StorageModel::R_AttIsHotPluggable).toBool();
+
+ /* Configure "add controller" actions: */
+ m_pActionAddController->setEnabled(fIDEPossible || fSATAPossible || fSCSIPossible || fFloppyPossible ||
+ fSASPossible || fUSBPossible || fNVMePossible || fVirtioSCSIPossible);
+ m_addControllerActions.value(KStorageControllerType_PIIX3)->setEnabled(fIDEPossible);
+ m_addControllerActions.value(KStorageControllerType_PIIX4)->setEnabled(fIDEPossible);
+ m_addControllerActions.value(KStorageControllerType_ICH6)->setEnabled(fIDEPossible);
+ m_addControllerActions.value(KStorageControllerType_IntelAhci)->setEnabled(fSATAPossible);
+ m_addControllerActions.value(KStorageControllerType_LsiLogic)->setEnabled(fSCSIPossible);
+ m_addControllerActions.value(KStorageControllerType_BusLogic)->setEnabled(fSCSIPossible);
+ m_addControllerActions.value(KStorageControllerType_I82078)->setEnabled(fFloppyPossible);
+ m_addControllerActions.value(KStorageControllerType_LsiLogicSas)->setEnabled(fSASPossible);
+ m_addControllerActions.value(KStorageControllerType_USB)->setEnabled(fUSBPossible);
+ m_addControllerActions.value(KStorageControllerType_NVMe)->setEnabled(fNVMePossible);
+ m_addControllerActions.value(KStorageControllerType_VirtioSCSI)->setEnabled(fVirtioSCSIPossible);
+
+ /* Configure "add attachment" actions: */
+ m_pActionAddAttachment->setEnabled(fController && fAttachmentsPossible);
+ m_pActionAddAttachmentHD->setEnabled(fController && fAttachmentsPossible);
+ m_pActionAddAttachmentCD->setEnabled(fController && fAttachmentsPossible);
+ m_pActionAddAttachmentFD->setEnabled(fController && fAttachmentsPossible);
+
+ /* Configure "delete controller" action: */
+ const bool fControllerInSuitableState = m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full;
+ m_pActionRemoveController->setEnabled(fController && fControllerInSuitableState);
+
+ /* Configure "delete attachment" action: */
+ const bool fAttachmentInSuitableState = m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Full
+ || ( m_enmConfigurationAccessLevel == ConfigurationAccessLevel_Partial_Running
+ && fIsAttachmentHotPluggable);
+ m_pActionRemoveAttachment->setEnabled(fAttachment && fAttachmentInSuitableState);
+}
+
+void UIStorageSettingsEditor::sltHandleRowInsertion(const QModelIndex &parentIndex, int iPosition)
+{
+ const QModelIndex index = m_pModelStorage->index(iPosition, 0, parentIndex);
+
+ switch (m_pModelStorage->data(index, StorageModel::R_ItemType).value<AbstractItem::ItemType>())
+ {
+ case AbstractItem::Type_ControllerItem:
+ {
+ /* Select the newly created Controller Item: */
+ m_pTreeViewStorage->setCurrentIndex(index);
+ break;
+ }
+ case AbstractItem::Type_AttachmentItem:
+ {
+ /* Expand parent if it is not expanded yet: */
+ if (!m_pTreeViewStorage->isExpanded(parentIndex))
+ m_pTreeViewStorage->setExpanded(parentIndex, true);
+ break;
+ }
+ default:
+ break;
+ }
+
+ sltUpdateActionStates();
+ sltGetInformation();
+}
+
+void UIStorageSettingsEditor::sltHandleRowRemoval()
+{
+ if (m_pModelStorage->rowCount (m_pModelStorage->root()) == 0)
+ m_pTreeViewStorage->setCurrentIndex (m_pModelStorage->root());
+
+ sltUpdateActionStates();
+ sltGetInformation();
+}
+
+void UIStorageSettingsEditor::sltHandleCurrentItemChange()
+{
+ sltUpdateActionStates();
+ sltGetInformation();
+}
+
+void UIStorageSettingsEditor::sltHandleContextMenuRequest(const QPoint &position)
+{
+ /* Forget last mouse press position: */
+ m_mousePressPosition = QPoint();
+
+ const QModelIndex index = m_pTreeViewStorage->indexAt(position);
+ if (!index.isValid())
+ return sltAddController();
+
+ QMenu menu;
+ switch (m_pModelStorage->data(index, StorageModel::R_ItemType).value<AbstractItem::ItemType>())
+ {
+ case AbstractItem::Type_ControllerItem:
+ {
+ const DeviceTypeList deviceTypeList(m_pModelStorage->data(index, StorageModel::R_CtrDevices).value<DeviceTypeList>());
+ foreach (KDeviceType enmDeviceType, deviceTypeList)
+ {
+ switch (enmDeviceType)
+ {
+ case KDeviceType_HardDisk:
+ menu.addAction(m_pActionAddAttachmentHD);
+ break;
+ case KDeviceType_DVD:
+ menu.addAction(m_pActionAddAttachmentCD);
+ break;
+ case KDeviceType_Floppy:
+ menu.addAction(m_pActionAddAttachmentFD);
+ break;
+ default:
+ break;
+ }
+ }
+ menu.addAction(m_pActionRemoveController);
+ break;
+ }
+ case AbstractItem::Type_AttachmentItem:
+ {
+ menu.addAction(m_pActionRemoveAttachment);
+ break;
+ }
+ default:
+ break;
+ }
+ if (!menu.isEmpty())
+ menu.exec(m_pTreeViewStorage->viewport()->mapToGlobal(position));
+}
+
+void UIStorageSettingsEditor::sltHandleDrawItemBranches(QPainter *pPainter, const QRect &rect, const QModelIndex &index)
+{
+ if (!index.parent().isValid() || !index.parent().parent().isValid())
+ return;
+
+ pPainter->save();
+ QStyleOption options;
+ options.initFrom(m_pTreeViewStorage);
+ options.rect = rect;
+ options.state |= QStyle::State_Item;
+ if (index.row() < m_pModelStorage->rowCount(index.parent()) - 1)
+ options.state |= QStyle::State_Sibling;
+ /* This pen is commonly used by different
+ * look and feel styles to paint tree-view branches. */
+ const QPen pen(QBrush(options.palette.dark().color(), Qt::Dense4Pattern), 0);
+ pPainter->setPen(pen);
+ /* If we want tree-view branches to be always painted we have to use QCommonStyle::drawPrimitive()
+ * because QCommonStyle performs branch painting as opposed to particular inherited sub-classing styles. */
+ qobject_cast<QCommonStyle*>(style())->QCommonStyle::drawPrimitive(QStyle::PE_IndicatorBranch, &options, pPainter);
+ pPainter->restore();
+}
+
+void UIStorageSettingsEditor::sltHandleMouseMove(QMouseEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+
+ const QModelIndex index = m_pTreeViewStorage->indexAt(pEvent->pos());
+ const QRect indexRect = m_pTreeViewStorage->visualRect(index);
+
+ /* Expander tool-tip: */
+ if (m_pModelStorage->data(index, StorageModel::R_IsController).toBool())
+ {
+ QRect expanderRect = m_pModelStorage->data(index, StorageModel::R_ItemPixmapRect).toRect();
+ expanderRect.translate(indexRect.x(), indexRect.y());
+ if (expanderRect.contains(pEvent->pos()))
+ {
+ pEvent->setAccepted(true);
+ if (m_pModelStorage->data(index, StorageModel::R_ToolTipType).value<StorageModel::ToolTipType>() != StorageModel::ToolTipType_Expander)
+ m_pModelStorage->setData(index, QVariant::fromValue(StorageModel::ToolTipType_Expander), StorageModel::R_ToolTipType);
+ return;
+ }
+ }
+
+ /* Adder tool-tip: */
+ if (m_pModelStorage->data(index, StorageModel::R_IsController).toBool() &&
+ m_pTreeViewStorage->currentIndex() == index)
+ {
+ const DeviceTypeList devicesList(m_pModelStorage->data(index, StorageModel::R_CtrDevices).value<DeviceTypeList>());
+ for (int i = 0; i < devicesList.size(); ++ i)
+ {
+ const KDeviceType enmDeviceType = devicesList[i];
+
+ QRect deviceRect;
+ switch (enmDeviceType)
+ {
+ case KDeviceType_HardDisk:
+ {
+ deviceRect = m_pModelStorage->data(index, StorageModel::R_HDPixmapRect).toRect();
+ break;
+ }
+ case KDeviceType_DVD:
+ {
+ deviceRect = m_pModelStorage->data(index, StorageModel::R_CDPixmapRect).toRect();
+ break;
+ }
+ case KDeviceType_Floppy:
+ {
+ deviceRect = m_pModelStorage->data(index, StorageModel::R_FDPixmapRect).toRect();
+ break;
+ }
+ default:
+ break;
+ }
+ deviceRect.translate(indexRect.x() + indexRect.width(), indexRect.y());
+
+ if (deviceRect.contains(pEvent->pos()))
+ {
+ pEvent->setAccepted(true);
+ switch (enmDeviceType)
+ {
+ case KDeviceType_HardDisk:
+ {
+ if (m_pModelStorage->data(index, StorageModel::R_ToolTipType).value<StorageModel::ToolTipType>() != StorageModel::ToolTipType_HDAdder)
+ m_pModelStorage->setData(index, QVariant::fromValue(StorageModel::ToolTipType_HDAdder), StorageModel::R_ToolTipType);
+ break;
+ }
+ case KDeviceType_DVD:
+ {
+ if (m_pModelStorage->data(index, StorageModel::R_ToolTipType).value<StorageModel::ToolTipType>() != StorageModel::ToolTipType_CDAdder)
+ m_pModelStorage->setData(index, QVariant::fromValue(StorageModel::ToolTipType_CDAdder), StorageModel::R_ToolTipType);
+ break;
+ }
+ case KDeviceType_Floppy:
+ {
+ if (m_pModelStorage->data(index, StorageModel::R_ToolTipType).value<StorageModel::ToolTipType>() != StorageModel::ToolTipType_FDAdder)
+ m_pModelStorage->setData(index, QVariant::fromValue(StorageModel::ToolTipType_FDAdder), StorageModel::R_ToolTipType);
+ break;
+ }
+ default:
+ break;
+ }
+ return;
+ }
+ }
+ }
+
+ /* Default tool-tip: */
+ if (m_pModelStorage->data(index, StorageModel::R_ToolTipType).value<StorageModel::ToolTipType>() != StorageModel::ToolTipType_Default)
+ m_pModelStorage->setData(index, StorageModel::ToolTipType_Default, StorageModel::R_ToolTipType);
+
+ /* Check whether we should initiate dragging: */
+ if ( !m_mousePressPosition.isNull()
+ && QLineF(pEvent->screenPos(), m_mousePressPosition).length() >= QApplication::startDragDistance())
+ {
+ /* Forget last mouse press position: */
+ m_mousePressPosition = QPoint();
+
+ /* Check what item we are hovering currently: */
+ QModelIndex index = m_pTreeViewStorage->indexAt(pEvent->pos());
+ AbstractItem *pItem = static_cast<AbstractItem*>(index.internalPointer());
+ /* And make sure this is attachment item, we are supporting dragging for this kind only: */
+ AttachmentItem *pItemAttachment = qobject_cast<AttachmentItem*>(pItem);
+ if (pItemAttachment)
+ {
+ /* Initialize dragging: */
+ pEvent->setAccepted(true);
+ QDrag *pDrag = new QDrag(this);
+ if (pDrag)
+ {
+ /* Assign pixmap: */
+ pDrag->setPixmap(pItem->pixmap());
+ /* Prepare mime: */
+ QMimeData *pMimeData = new QMimeData;
+ if (pMimeData)
+ {
+ pMimeData->setData(s_strControllerMimeType, pItemAttachment->parent()->id().toString().toLatin1());
+ pMimeData->setData(s_strAttachmentMimeType, pItemAttachment->id().toString().toLatin1());
+ pDrag->setMimeData(pMimeData);
+ }
+ /* Start dragging: */
+ pDrag->exec();
+ }
+ }
+ }
+}
+
+void UIStorageSettingsEditor::sltHandleMouseClick(QMouseEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+
+ /* Acquire indexes: */
+ const QModelIndex currentIndex = m_pTreeViewStorage->currentIndex();
+ const QModelIndex index = m_pTreeViewStorage->indexAt(pEvent->pos());
+ const QRect indexRect = m_pTreeViewStorage->visualRect(index);
+
+ /* Remember last mouse press position only if we pressed current index: */
+ if (index == currentIndex)
+ m_mousePressPosition = pEvent->globalPos();
+
+ /* Expander icon: */
+ if (m_pModelStorage->data(index, StorageModel::R_IsController).toBool())
+ {
+ QRect expanderRect = m_pModelStorage->data(index, StorageModel::R_ItemPixmapRect).toRect();
+ expanderRect.translate(indexRect.x(), indexRect.y());
+ if (expanderRect.contains(pEvent->pos()))
+ {
+ pEvent->setAccepted(true);
+ m_pTreeViewStorage->setExpanded(index, !m_pTreeViewStorage->isExpanded(index));
+ return;
+ }
+ }
+
+ /* Adder icons: */
+ if (m_pModelStorage->data(index, StorageModel::R_IsController).toBool() &&
+ m_pTreeViewStorage->currentIndex() == index)
+ {
+ const DeviceTypeList devicesList(m_pModelStorage->data(index, StorageModel::R_CtrDevices).value<DeviceTypeList>());
+ for (int i = 0; i < devicesList.size(); ++ i)
+ {
+ const KDeviceType enmDeviceType = devicesList[i];
+
+ QRect deviceRect;
+ switch (enmDeviceType)
+ {
+ case KDeviceType_HardDisk:
+ {
+ deviceRect = m_pModelStorage->data(index, StorageModel::R_HDPixmapRect).toRect();
+ break;
+ }
+ case KDeviceType_DVD:
+ {
+ deviceRect = m_pModelStorage->data(index, StorageModel::R_CDPixmapRect).toRect();
+ break;
+ }
+ case KDeviceType_Floppy:
+ {
+ deviceRect = m_pModelStorage->data(index, StorageModel::R_FDPixmapRect).toRect();
+ break;
+ }
+ default:
+ break;
+ }
+ deviceRect.translate(indexRect.x() + indexRect.width(), indexRect.y());
+
+ if (deviceRect.contains(pEvent->pos()))
+ {
+ pEvent->setAccepted(true);
+ if (m_pActionAddAttachment->isEnabled())
+ addAttachmentWrapper(enmDeviceType);
+ return;
+ }
+ }
+ }
+}
+
+void UIStorageSettingsEditor::sltHandleMouseRelease(QMouseEvent *)
+{
+ /* Forget last mouse press position: */
+ m_mousePressPosition = QPoint();
+}
+
+void UIStorageSettingsEditor::sltHandleDragEnter(QDragEnterEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+
+ /* Accept event but not the proposed action: */
+ pEvent->accept();
+}
+
+void UIStorageSettingsEditor::sltHandleDragMove(QDragMoveEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+ /* And mime-data is set: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ AssertPtrReturnVoid(pMimeData);
+
+ /* Make sure mime-data format is valid: */
+ if ( !pMimeData->hasFormat(UIStorageSettingsEditor::s_strControllerMimeType)
+ || !pMimeData->hasFormat(UIStorageSettingsEditor::s_strAttachmentMimeType))
+ return;
+
+ /* Get controller/attachment ids: */
+ const QString strControllerId = pMimeData->data(UIStorageSettingsEditor::s_strControllerMimeType);
+ const QString strAttachmentId = pMimeData->data(UIStorageSettingsEditor::s_strAttachmentMimeType);
+
+ /* Check what item we are hovering currently: */
+ QModelIndex index = m_pTreeViewStorage->indexAt(pEvent->pos());
+ AbstractItem *pItem = static_cast<AbstractItem*>(index.internalPointer());
+ /* And make sure this is controller item, we are supporting dropping for this kind only: */
+ ControllerItem *pItemController = qobject_cast<ControllerItem*>(pItem);
+ if (!pItemController || pItemController->id().toString() == strControllerId)
+ return;
+ /* Then make sure we support such attachment device type: */
+ const DeviceTypeList devicesList(m_pModelStorage->data(index, StorageModel::R_CtrDevices).value<DeviceTypeList>());
+ if (!devicesList.contains(m_pModelStorage->attachmentDeviceType(QUuid(strControllerId), QUuid(strAttachmentId))))
+ return;
+ /* Also make sure there is enough place for new attachment: */
+ const bool fIsMoreAttachmentsPossible = m_pModelStorage->data(index, StorageModel::R_IsMoreAttachmentsPossible).toBool();
+ if (!fIsMoreAttachmentsPossible)
+ return;
+
+ /* Accept drag-enter event: */
+ pEvent->acceptProposedAction();
+}
+
+void UIStorageSettingsEditor::sltHandleDragDrop(QDropEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+ /* And mime-data is set: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ AssertPtrReturnVoid(pMimeData);
+
+ /* Check what item we are hovering currently: */
+ QModelIndex index = m_pTreeViewStorage->indexAt(pEvent->pos());
+ AbstractItem *pItem = static_cast<AbstractItem*>(index.internalPointer());
+ /* And make sure this is controller item, we are supporting dropping for this kind only: */
+ ControllerItem *pItemController = qobject_cast<ControllerItem*>(pItem);
+ if (pItemController)
+ {
+ /* Get controller/attachment ids: */
+ const QString strControllerId = pMimeData->data(UIStorageSettingsEditor::s_strControllerMimeType);
+ const QString strAttachmentId = pMimeData->data(UIStorageSettingsEditor::s_strAttachmentMimeType);
+ m_pModelStorage->moveAttachment(QUuid(strAttachmentId), QUuid(strControllerId), pItemController->id());
+ }
+}
+
+void UIStorageSettingsEditor::prepare()
+{
+ /* Create icon-pool: */
+ UIIconPoolStorageSettings::create();
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIStorageSettingsEditor::prepareWidgets()
+{
+ /* Create main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create splitter: */
+ m_pSplitter = new QISplitter(this);
+ if (m_pSplitter)
+ {
+ m_pSplitter->setChildrenCollapsible(false);
+ m_pSplitter->setOrientation(Qt::Horizontal);
+ m_pSplitter->setHandleWidth(4);
+
+ /* Prepare panes: */
+ prepareLeftPane();
+ prepareRightPane();
+
+ pLayout->addWidget(m_pSplitter);
+ }
+ }
+}
+
+void UIStorageSettingsEditor::prepareLeftPane()
+{
+ /* Prepare left pane: */
+ m_pWidgetLeftPane = new QWidget(m_pSplitter);
+ if (m_pWidgetLeftPane)
+ {
+ /* Prepare left pane layout: */
+ QVBoxLayout *pLayoutLeftPane = new QVBoxLayout(m_pWidgetLeftPane);
+ if (pLayoutLeftPane)
+ {
+ pLayoutLeftPane->setContentsMargins(0, 0, 10, 0);
+
+ /* Prepare left separator: */
+ m_pLabelSeparatorLeftPane = new QILabelSeparator(m_pWidgetLeftPane);
+ if (m_pLabelSeparatorLeftPane)
+ pLayoutLeftPane->addWidget(m_pLabelSeparatorLeftPane);
+
+ /* Prepare storage layout: */
+ m_pLayoutTree = new QVBoxLayout;
+ if (m_pLayoutTree)
+ {
+#ifdef VBOX_WS_MAC
+ m_pLayoutTree->setContentsMargins(3, 0, 3, 0);
+ m_pLayoutTree->setSpacing(3);
+#else
+ m_pLayoutTree->setContentsMargins(0, 0, 0, 0);
+ m_pLayoutTree->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 3);
+#endif
+
+ /* Prepare tree-view: */
+ prepareTreeView();
+
+ /* Prepare toolbar layout: */
+ m_pLayoutToolbar = new QHBoxLayout;
+ if (m_pLayoutToolbar)
+ {
+ m_pLayoutToolbar->addStretch();
+
+ /* Prepare toolbar: */
+ prepareToolBar();
+
+ m_pLayoutTree->addLayout(m_pLayoutToolbar);
+ }
+
+ pLayoutLeftPane->addLayout(m_pLayoutTree);
+ }
+ }
+
+ m_pSplitter->addWidget(m_pWidgetLeftPane);
+ }
+}
+
+void UIStorageSettingsEditor::prepareTreeView()
+{
+ /* Prepare tree-view: */
+ m_pTreeViewStorage = new QITreeView(m_pWidgetLeftPane);
+ if (m_pTreeViewStorage)
+ {
+ if (m_pLabelSeparatorLeftPane)
+ m_pLabelSeparatorLeftPane->setBuddy(m_pTreeViewStorage);
+ m_pTreeViewStorage->setMouseTracking(true);
+ m_pTreeViewStorage->setAcceptDrops(true);
+ m_pTreeViewStorage->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ /* Prepare storage model: */
+ m_pModelStorage = new StorageModel(m_pTreeViewStorage);
+ if (m_pModelStorage)
+ {
+ m_pTreeViewStorage->setModel(m_pModelStorage);
+ m_pTreeViewStorage->setRootIndex(m_pModelStorage->root());
+ m_pTreeViewStorage->setCurrentIndex(m_pModelStorage->root());
+ }
+
+ /* Prepare storage delegate: */
+ StorageDelegate *pStorageDelegate = new StorageDelegate(m_pTreeViewStorage);
+ if (pStorageDelegate)
+ m_pTreeViewStorage->setItemDelegate(pStorageDelegate);
+
+ m_pLayoutTree->addWidget(m_pTreeViewStorage);
+ }
+}
+
+void UIStorageSettingsEditor::prepareToolBar()
+{
+ /* Prepare toolbar: */
+ m_pToolbar = new QIToolBar(m_pWidgetLeftPane);
+ if (m_pToolbar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pToolbar->setIconSize(QSize(iIconMetric, iIconMetric));
+
+ /* Prepare 'Add Controller' action: */
+ m_pActionAddController = new QAction(this);
+ if (m_pActionAddController)
+ {
+ m_pActionAddController->setIcon(iconPool()->icon(PixmapType_ControllerAddEn, PixmapType_ControllerAddDis));
+ m_pToolbar->addAction(m_pActionAddController);
+ }
+
+ /* Prepare 'Add PIIX3 Controller' action: */
+ m_addControllerActions[KStorageControllerType_PIIX3] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_PIIX3))
+ m_addControllerActions.value(KStorageControllerType_PIIX3)->setIcon(iconPool()->icon(PixmapType_IDEControllerAddEn, PixmapType_IDEControllerAddDis));
+ /* Prepare 'Add PIIX4 Controller' action: */
+ m_addControllerActions[KStorageControllerType_PIIX4] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_PIIX4))
+ m_addControllerActions.value(KStorageControllerType_PIIX4)->setIcon(iconPool()->icon(PixmapType_IDEControllerAddEn, PixmapType_IDEControllerAddDis));
+ /* Prepare 'Add ICH6 Controller' action: */
+ m_addControllerActions[KStorageControllerType_ICH6] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_ICH6))
+ m_addControllerActions.value(KStorageControllerType_ICH6)->setIcon(iconPool()->icon(PixmapType_IDEControllerAddEn, PixmapType_IDEControllerAddDis));
+ /* Prepare 'Add AHCI Controller' action: */
+ m_addControllerActions[KStorageControllerType_IntelAhci] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_IntelAhci))
+ m_addControllerActions.value(KStorageControllerType_IntelAhci)->setIcon(iconPool()->icon(PixmapType_SATAControllerAddEn, PixmapType_SATAControllerAddDis));
+ /* Prepare 'Add LsiLogic Controller' action: */
+ m_addControllerActions[KStorageControllerType_LsiLogic] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_LsiLogic))
+ m_addControllerActions.value(KStorageControllerType_LsiLogic)->setIcon(iconPool()->icon(PixmapType_SCSIControllerAddEn, PixmapType_SCSIControllerAddDis));
+ /* Prepare 'Add BusLogic Controller' action: */
+ m_addControllerActions[KStorageControllerType_BusLogic] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_BusLogic))
+ m_addControllerActions.value(KStorageControllerType_BusLogic)->setIcon(iconPool()->icon(PixmapType_SCSIControllerAddEn, PixmapType_SCSIControllerAddDis));
+ /* Prepare 'Add Floppy Controller' action: */
+ m_addControllerActions[KStorageControllerType_I82078] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_I82078))
+ m_addControllerActions.value(KStorageControllerType_I82078)->setIcon(iconPool()->icon(PixmapType_FloppyControllerAddEn, PixmapType_FloppyControllerAddDis));
+ /* Prepare 'Add LsiLogic SAS Controller' action: */
+ m_addControllerActions[KStorageControllerType_LsiLogicSas] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_LsiLogicSas))
+ m_addControllerActions.value(KStorageControllerType_LsiLogicSas)->setIcon(iconPool()->icon(PixmapType_SASControllerAddEn, PixmapType_SASControllerAddDis));
+ /* Prepare 'Add USB Controller' action: */
+ m_addControllerActions[KStorageControllerType_USB] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_USB))
+ m_addControllerActions.value(KStorageControllerType_USB)->setIcon(iconPool()->icon(PixmapType_USBControllerAddEn, PixmapType_USBControllerAddDis));
+ /* Prepare 'Add NVMe Controller' action: */
+ m_addControllerActions[KStorageControllerType_NVMe] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_NVMe))
+ m_addControllerActions.value(KStorageControllerType_NVMe)->setIcon(iconPool()->icon(PixmapType_NVMeControllerAddEn, PixmapType_NVMeControllerAddDis));
+ /* Prepare 'Add virtio-scsi Controller' action: */
+ m_addControllerActions[KStorageControllerType_VirtioSCSI] = new QAction(this);
+ if (m_addControllerActions.value(KStorageControllerType_VirtioSCSI))
+ m_addControllerActions.value(KStorageControllerType_VirtioSCSI)->setIcon(iconPool()->icon(PixmapType_VirtioSCSIControllerAddEn, PixmapType_VirtioSCSIControllerAddDis));
+
+ /* Prepare 'Remove Controller' action: */
+ m_pActionRemoveController = new QAction(this);
+ if (m_pActionRemoveController)
+ {
+ m_pActionRemoveController->setIcon(iconPool()->icon(PixmapType_ControllerDelEn, PixmapType_ControllerDelDis));
+ m_pToolbar->addAction(m_pActionRemoveController);
+ }
+
+ /* Prepare 'Add Attachment' action: */
+ m_pActionAddAttachment = new QAction(this);
+ if (m_pActionAddAttachment)
+ {
+ m_pActionAddAttachment->setIcon(iconPool()->icon(PixmapType_AttachmentAddEn, PixmapType_AttachmentAddDis));
+ m_pToolbar->addAction(m_pActionAddAttachment);
+ }
+
+ /* Prepare 'Add HD Attachment' action: */
+ m_pActionAddAttachmentHD = new QAction(this);
+ if (m_pActionAddAttachmentHD)
+ m_pActionAddAttachmentHD->setIcon(iconPool()->icon(PixmapType_HDAttachmentAddEn, PixmapType_HDAttachmentAddDis));
+ /* Prepare 'Add CD Attachment' action: */
+ m_pActionAddAttachmentCD = new QAction(this);
+ if (m_pActionAddAttachmentCD)
+ m_pActionAddAttachmentCD->setIcon(iconPool()->icon(PixmapType_CDAttachmentAddEn, PixmapType_CDAttachmentAddDis));
+ /* Prepare 'Add FD Attachment' action: */
+ m_pActionAddAttachmentFD = new QAction(this);
+ if (m_pActionAddAttachmentFD)
+ m_pActionAddAttachmentFD->setIcon(iconPool()->icon(PixmapType_FDAttachmentAddEn, PixmapType_FDAttachmentAddDis));
+
+ /* Prepare 'Remove Attachment' action: */
+ m_pActionRemoveAttachment = new QAction(this);
+ if (m_pActionRemoveAttachment)
+ {
+ m_pActionRemoveAttachment->setIcon(iconPool()->icon(PixmapType_AttachmentDelEn, PixmapType_AttachmentDelDis));
+ m_pToolbar->addAction(m_pActionRemoveAttachment);
+ }
+
+ m_pLayoutToolbar->addWidget(m_pToolbar);
+ }
+}
+
+void UIStorageSettingsEditor::prepareRightPane()
+{
+ /* Prepare right pane: */
+ m_pStackRightPane = new QStackedWidget(m_pSplitter);
+ if (m_pStackRightPane)
+ {
+ /* Prepare stack contents: */
+ prepareEmptyWidget();
+ prepareControllerWidget();
+ prepareAttachmentWidget();
+
+ m_pSplitter->addWidget(m_pStackRightPane);
+ }
+}
+
+void UIStorageSettingsEditor::prepareEmptyWidget()
+{
+ /* Prepare widget for empty case: */
+ QWidget *pWidgetEmpty = new QWidget;
+ if (pWidgetEmpty)
+ {
+ /* Create widget layout for empty case: */
+ QGridLayout *pLayoutEmpty = new QGridLayout(pWidgetEmpty);
+ if (pLayoutEmpty)
+ {
+ pLayoutEmpty->setContentsMargins(10, 0, 0, 0);
+ pLayoutEmpty->setRowStretch(2, 1);
+
+ /* Prepare separator for empty case: */
+ m_pLabelSeparatorEmpty = new QILabelSeparator(pWidgetEmpty);
+ if (m_pLabelSeparatorEmpty)
+ pLayoutEmpty->addWidget(m_pLabelSeparatorEmpty, 0, 0, 1, 2);
+
+ /* Prepare label for empty case: */
+ m_pLabelInfo = new QLabel(pWidgetEmpty);
+ if (m_pLabelInfo)
+ {
+ m_pLabelInfo->setWordWrap(true);
+ pLayoutEmpty->addWidget(m_pLabelInfo, 1, 1);
+ }
+
+ pLayoutEmpty->setColumnMinimumWidth(0, 10);
+ }
+
+ m_pStackRightPane->addWidget(pWidgetEmpty);
+ }
+}
+
+void UIStorageSettingsEditor::prepareControllerWidget()
+{
+ /* Create widget for controller case: */
+ QWidget *pWidgetController = new QWidget;
+ if (pWidgetController)
+ {
+ /* Create widget layout for controller case: */
+ QGridLayout *m_pLayoutController = new QGridLayout(pWidgetController);
+ if (m_pLayoutController)
+ {
+ m_pLayoutController->setContentsMargins(10, 0, 0, 0);
+ m_pLayoutController->setRowStretch(5, 1);
+
+ /* Prepare separator for controller case: */
+ m_pLabelSeparatorParameters = new QILabelSeparator(pWidgetController);
+ if (m_pLabelSeparatorParameters)
+ m_pLayoutController->addWidget(m_pLabelSeparatorParameters, 0, 0, 1, 3);
+
+ /* Prepare name label: */
+ m_pLabelName = new QLabel(pWidgetController);
+ if (m_pLabelName)
+ {
+ m_pLabelName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutController->addWidget(m_pLabelName, 1, 1);
+ }
+ /* Prepare name editor: */
+ m_pEditorName = new QLineEdit(pWidgetController);
+ if (m_pEditorName)
+ {
+ if (m_pLabelName)
+ m_pLabelName->setBuddy(m_pEditorName);
+ m_pLayoutController->addWidget(m_pEditorName, 1, 2);
+ }
+
+ /* Prepare type label: */
+ m_pLabelType = new QLabel(pWidgetController);
+ if (m_pLabelType)
+ {
+ m_pLabelType->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutController->addWidget(m_pLabelType, 2, 1);
+ }
+ /* Prepare type combo: */
+ m_pComboType = new QComboBox(pWidgetController);
+ if (m_pComboType)
+ {
+ if (m_pLabelType)
+ m_pLabelType->setBuddy(m_pComboType);
+ m_pComboType->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ m_pLayoutController->addWidget(m_pComboType, 2, 2);
+ }
+
+ /* Prepare port count label: */
+ m_pLabelPortCount = new QLabel(pWidgetController);
+ if (m_pLabelPortCount)
+ {
+ m_pLabelPortCount->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutController->addWidget(m_pLabelPortCount, 3, 1);
+ }
+ /* Prepare port count spinbox: */
+ m_pSpinboxPortCount = new QSpinBox(pWidgetController);
+ if (m_pSpinboxPortCount)
+ {
+ if (m_pLabelPortCount)
+ m_pLabelPortCount->setBuddy(m_pSpinboxPortCount);
+ m_pLayoutController->addWidget(m_pSpinboxPortCount, 3, 2);
+ }
+
+ /* Prepare port count check-box: */
+ m_pCheckBoxIoCache = new QCheckBox(pWidgetController);
+ if (m_pCheckBoxIoCache)
+ m_pLayoutController->addWidget(m_pCheckBoxIoCache, 4, 2);
+
+ m_pLayoutController->setColumnMinimumWidth(0, 10);
+ }
+
+ m_pStackRightPane->addWidget(pWidgetController);
+ }
+}
+
+void UIStorageSettingsEditor::prepareAttachmentWidget()
+{
+ /* Create widget for attachment case: */
+ QWidget *pWidgetAttachment = new QWidget;
+ if (pWidgetAttachment)
+ {
+ /* Create widget layout for attachment case: */
+ QGridLayout *m_pLayoutAttachment = new QGridLayout(pWidgetAttachment);
+ if (m_pLayoutAttachment)
+ {
+ m_pLayoutAttachment->setContentsMargins(10, 0, 0, 0);
+ m_pLayoutAttachment->setColumnStretch(2, 1);
+ m_pLayoutAttachment->setRowStretch(13, 1);
+
+ /* Prepare separator for attachment case: */
+ m_pLabelSeparatorAttributes = new QILabelSeparator(pWidgetAttachment);
+ if (m_pLabelSeparatorAttributes)
+ m_pLayoutAttachment->addWidget(m_pLabelSeparatorAttributes, 0, 0, 1, 3);
+
+ /* Prepare medium label: */
+ m_pLabelMedium = new QLabel(pWidgetAttachment);
+ if (m_pLabelMedium)
+ {
+ m_pLabelMedium->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelMedium, 1, 1);
+ }
+
+ /* Prepare slot layout: */
+ QHBoxLayout *pLayoutContainer = new QHBoxLayout;
+ if (pLayoutContainer)
+ {
+ pLayoutContainer->setContentsMargins(0, 0, 0, 0);
+ pLayoutContainer->setSpacing(1);
+
+ /* Prepare slot combo: */
+ m_pComboSlot = new QComboBox(pWidgetAttachment);
+ if (m_pComboSlot)
+ pLayoutContainer->addWidget(m_pComboSlot);
+
+ /* Prepare slot combo: */
+ m_pToolButtonOpen = new QIToolButton(pWidgetAttachment);
+ if (m_pToolButtonOpen)
+ {
+ if (m_pLabelMedium)
+ m_pLabelMedium->setBuddy(m_pToolButtonOpen);
+
+ /* Prepare open medium menu: */
+ QMenu *pOpenMediumMenu = new QMenu(m_pToolButtonOpen);
+ if (pOpenMediumMenu)
+ m_pToolButtonOpen->setMenu(pOpenMediumMenu);
+
+ pLayoutContainer->addWidget(m_pToolButtonOpen);
+ }
+
+ m_pLayoutAttachment->addLayout(pLayoutContainer, 1, 2);
+ }
+
+ /* Prepare attachment settings layout: */
+ QVBoxLayout *pLayoutAttachmentSettings = new QVBoxLayout;
+ if (pLayoutAttachmentSettings)
+ {
+ pLayoutAttachmentSettings->setContentsMargins(0, 0, 0, 0);
+ pLayoutAttachmentSettings->setSpacing(0);
+
+ /* Prepare attachment passthrough check-box: */
+ m_pCheckBoxPassthrough = new QCheckBox(pWidgetAttachment);
+ if (m_pCheckBoxPassthrough)
+ pLayoutAttachmentSettings->addWidget(m_pCheckBoxPassthrough);
+
+ /* Prepare attachment temporary eject check-box: */
+ m_pCheckBoxTempEject = new QCheckBox(pWidgetAttachment);
+ if (m_pCheckBoxTempEject)
+ pLayoutAttachmentSettings->addWidget(m_pCheckBoxTempEject);
+
+ /* Prepare attachment non rotational check-box: */
+ m_pCheckBoxNonRotational = new QCheckBox(pWidgetAttachment);
+ if (m_pCheckBoxNonRotational)
+ pLayoutAttachmentSettings->addWidget(m_pCheckBoxNonRotational);
+
+ /* Prepare attachment hot pluggable check-box: */
+ m_pCheckBoxHotPluggable = new QCheckBox(pWidgetAttachment);
+ if (m_pCheckBoxHotPluggable)
+ pLayoutAttachmentSettings->addWidget(m_pCheckBoxHotPluggable);
+
+ m_pLayoutAttachment->addLayout(pLayoutAttachmentSettings, 2, 2);
+ }
+
+ /* Prepare separator for attachment case: */
+ m_pLabelSeparatorInformation = new QILabelSeparator(pWidgetAttachment);
+ if (m_pLabelSeparatorInformation)
+ m_pLayoutAttachment->addWidget(m_pLabelSeparatorInformation, 3, 0, 1, 3);
+
+ /* Prepare HD format label: */
+ m_pLabelHDFormat = new QLabel(pWidgetAttachment);
+ if (m_pLabelHDFormat)
+ {
+ m_pLabelHDFormat->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelHDFormat, 4, 1);
+ }
+ /* Prepare HD format field: */
+ m_pFieldHDFormat = new QILabel(pWidgetAttachment);
+ if (m_pFieldHDFormat)
+ {
+ m_pFieldHDFormat->setFullSizeSelection(true);
+ m_pFieldHDFormat->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
+ m_pLayoutAttachment->addWidget(m_pFieldHDFormat, 4, 2);
+ }
+
+ /* Prepare CD/FD type label: */
+ m_pLabelCDFDType = new QLabel(pWidgetAttachment);
+ if (m_pLabelCDFDType)
+ {
+ m_pLabelCDFDType->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelCDFDType, 5, 1);
+ }
+ /* Prepare CD/FD type field: */
+ m_pFieldCDFDType = new QILabel(pWidgetAttachment);
+ if (m_pFieldCDFDType)
+ {
+ m_pFieldCDFDType->setFullSizeSelection(true);
+ m_pFieldCDFDType->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
+ m_pLayoutAttachment->addWidget(m_pFieldCDFDType, 5, 2);
+ }
+
+ /* Prepare HD virtual size label: */
+ m_pLabelHDVirtualSize = new QLabel(pWidgetAttachment);
+ if (m_pLabelHDVirtualSize)
+ {
+ m_pLabelHDVirtualSize->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelHDVirtualSize, 6, 1);
+ }
+ /* Prepare HD virtual size field: */
+ m_pFieldHDVirtualSize = new QILabel(pWidgetAttachment);
+ if (m_pFieldHDVirtualSize)
+ {
+ m_pFieldHDVirtualSize->setFullSizeSelection(true);
+ m_pFieldHDVirtualSize->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
+ m_pLayoutAttachment->addWidget(m_pFieldHDVirtualSize, 6, 2);
+ }
+
+ /* Prepare HD actual size label: */
+ m_pLabelHDActualSize = new QLabel(pWidgetAttachment);
+ if (m_pLabelHDActualSize)
+ {
+ m_pLabelHDActualSize->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelHDActualSize, 7, 1);
+ }
+ /* Prepare HD actual size field: */
+ m_pFieldHDActualSize = new QILabel(pWidgetAttachment);
+ if (m_pFieldHDActualSize)
+ {
+ m_pFieldHDActualSize->setFullSizeSelection(true);
+ m_pFieldHDActualSize->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
+ m_pLayoutAttachment->addWidget(m_pFieldHDActualSize, 7, 2);
+ }
+
+ /* Prepare CD/FD size label: */
+ m_pLabelCDFDSize = new QLabel(pWidgetAttachment);
+ if (m_pLabelCDFDSize)
+ {
+ m_pLabelCDFDSize->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelCDFDSize, 8, 1);
+ }
+ /* Prepare CD/FD size field: */
+ m_pFieldCDFDSize = new QILabel(pWidgetAttachment);
+ if (m_pFieldCDFDSize)
+ {
+ m_pFieldCDFDSize->setFullSizeSelection(true);
+ m_pFieldCDFDSize->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
+ m_pLayoutAttachment->addWidget(m_pFieldCDFDSize, 8, 2);
+ }
+
+ /* Prepare HD details label: */
+ m_pLabelHDDetails = new QLabel(pWidgetAttachment);
+ if (m_pLabelHDDetails)
+ {
+ m_pLabelHDDetails->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelHDDetails, 9, 1);
+ }
+ /* Prepare HD details field: */
+ m_pFieldHDDetails = new QILabel(pWidgetAttachment);
+ if (m_pFieldHDDetails)
+ {
+ m_pFieldHDDetails->setFullSizeSelection(true);
+ m_pFieldHDDetails->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
+ m_pLayoutAttachment->addWidget(m_pFieldHDDetails, 9, 2);
+ }
+
+ /* Prepare location label: */
+ m_pLabelLocation = new QLabel(pWidgetAttachment);
+ if (m_pLabelLocation)
+ {
+ m_pLabelLocation->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelLocation, 10, 1);
+ }
+ /* Prepare location field: */
+ m_pFieldLocation = new QILabel(pWidgetAttachment);
+ if (m_pFieldLocation)
+ {
+ m_pFieldLocation->setFullSizeSelection(true);
+ m_pFieldLocation->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
+ m_pLayoutAttachment->addWidget(m_pFieldLocation, 10, 2);
+ }
+
+ /* Prepare usage label: */
+ m_pLabelUsage = new QLabel(pWidgetAttachment);
+ if (m_pLabelUsage)
+ {
+ m_pLabelUsage->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelUsage, 11, 1);
+ }
+ /* Prepare usage field: */
+ m_pFieldUsage = new QILabel(pWidgetAttachment);
+ if (m_pFieldUsage)
+ {
+ m_pFieldUsage->setFullSizeSelection(true);
+ m_pFieldUsage->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
+ m_pLayoutAttachment->addWidget(m_pFieldUsage, 11, 2);
+ }
+
+ /* Prepare encryption label: */
+ m_pLabelEncryption = new QLabel(pWidgetAttachment);
+ if (m_pLabelEncryption)
+ {
+ m_pLabelEncryption->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayoutAttachment->addWidget(m_pLabelEncryption, 12, 1);
+ }
+ /* Prepare encryption field: */
+ m_pFieldEncryption = new QILabel(pWidgetAttachment);
+ if (m_pFieldEncryption)
+ {
+ m_pFieldEncryption->setFullSizeSelection(true);
+ m_pFieldEncryption->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
+ m_pLayoutAttachment->addWidget(m_pFieldEncryption, 12, 2);
+ }
+
+ m_pLayoutAttachment->setColumnMinimumWidth(0, 10);
+ }
+
+ m_pStackRightPane->addWidget(pWidgetAttachment);
+ }
+}
+
+void UIStorageSettingsEditor::prepareConnections()
+{
+ /* Configure this: */
+ connect(&uiCommon(), &UICommon::sigMediumEnumerated,
+ this, &UIStorageSettingsEditor::sltHandleMediumEnumerated);
+ connect(&uiCommon(), &UICommon::sigMediumDeleted,
+ this, &UIStorageSettingsEditor::sltHandleMediumDeleted);
+
+ /* Configure tree-view: */
+ connect(m_pTreeViewStorage, &QITreeView::currentItemChanged,
+ this, &UIStorageSettingsEditor::sltHandleCurrentItemChange);
+ connect(m_pTreeViewStorage, &QITreeView::customContextMenuRequested,
+ this, &UIStorageSettingsEditor::sltHandleContextMenuRequest);
+ connect(m_pTreeViewStorage, &QITreeView::drawItemBranches,
+ this, &UIStorageSettingsEditor::sltHandleDrawItemBranches);
+ connect(m_pTreeViewStorage, &QITreeView::mouseMoved,
+ this, &UIStorageSettingsEditor::sltHandleMouseMove);
+ connect(m_pTreeViewStorage, &QITreeView::mousePressed,
+ this, &UIStorageSettingsEditor::sltHandleMouseClick);
+ connect(m_pTreeViewStorage, &QITreeView::mouseReleased,
+ this, &UIStorageSettingsEditor::sltHandleMouseRelease);
+ connect(m_pTreeViewStorage, &QITreeView::mouseDoubleClicked,
+ this, &UIStorageSettingsEditor::sltHandleMouseClick);
+ connect(m_pTreeViewStorage, &QITreeView::dragEntered,
+ this, &UIStorageSettingsEditor::sltHandleDragEnter);
+ connect(m_pTreeViewStorage, &QITreeView::dragMoved,
+ this, &UIStorageSettingsEditor::sltHandleDragMove);
+ connect(m_pTreeViewStorage, &QITreeView::dragDropped,
+ this, &UIStorageSettingsEditor::sltHandleDragDrop);
+
+ /* Create model: */
+ connect(m_pModelStorage, &StorageModel::rowsInserted,
+ this, &UIStorageSettingsEditor::sltHandleRowInsertion);
+ connect(m_pModelStorage, &StorageModel::rowsRemoved,
+ this, &UIStorageSettingsEditor::sltHandleRowRemoval);
+
+ /* Configure actions: */
+ connect(m_pActionAddController, &QAction::triggered, this, &UIStorageSettingsEditor::sltAddController);
+ connect(m_addControllerActions.value(KStorageControllerType_PIIX3), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerPIIX3);
+ connect(m_addControllerActions.value(KStorageControllerType_PIIX4), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerPIIX4);
+ connect(m_addControllerActions.value(KStorageControllerType_ICH6), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerICH6);
+ connect(m_addControllerActions.value(KStorageControllerType_IntelAhci), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerAHCI);
+ connect(m_addControllerActions.value(KStorageControllerType_LsiLogic), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerLsiLogic);
+ connect(m_addControllerActions.value(KStorageControllerType_BusLogic), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerBusLogic);
+ connect(m_addControllerActions.value(KStorageControllerType_I82078), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerFloppy);
+ connect(m_addControllerActions.value(KStorageControllerType_LsiLogicSas), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerLsiLogicSAS);
+ connect(m_addControllerActions.value(KStorageControllerType_USB), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerUSB);
+ connect(m_addControllerActions.value(KStorageControllerType_NVMe), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerNVMe);
+ connect(m_addControllerActions.value(KStorageControllerType_VirtioSCSI), &QAction::triggered, this, &UIStorageSettingsEditor::sltAddControllerVirtioSCSI);
+ connect(m_pActionRemoveController, &QAction::triggered, this, &UIStorageSettingsEditor::sltRemoveController);
+ connect(m_pActionAddAttachment, &QAction::triggered, this, &UIStorageSettingsEditor::sltAddAttachment);
+ connect(m_pActionAddAttachmentHD, &QAction::triggered, this, &UIStorageSettingsEditor::sltAddAttachmentHD);
+ connect(m_pActionAddAttachmentCD, &QAction::triggered, this, &UIStorageSettingsEditor::sltAddAttachmentCD);
+ connect(m_pActionAddAttachmentFD, &QAction::triggered, this, &UIStorageSettingsEditor::sltAddAttachmentFD);
+ connect(m_pActionRemoveAttachment, &QAction::triggered, this, &UIStorageSettingsEditor::sltRemoveAttachment);
+
+ /* Configure tool-button: */
+ connect(m_pToolButtonOpen, &QIToolButton::clicked, m_pToolButtonOpen, &QIToolButton::showMenu);
+ /* Configure menu: */
+ connect(m_pToolButtonOpen->menu(), &QMenu::aboutToShow, this, &UIStorageSettingsEditor::sltPrepareOpenMediumMenu);
+
+ /* Configure widgets: */
+ connect(m_pMediumIdHolder, &UIMediumIDHolder::sigChanged,
+ this, &UIStorageSettingsEditor::sltSetInformation);
+ connect(m_pSpinboxPortCount, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIStorageSettingsEditor::sltSetInformation);
+ connect(m_pEditorName, &QLineEdit::textEdited,
+ this, &UIStorageSettingsEditor::sltSetInformation);
+ connect(m_pComboType, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
+ this, &UIStorageSettingsEditor::sltSetInformation);
+ connect(m_pComboSlot, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
+ this, &UIStorageSettingsEditor::sltSetInformation);
+ connect(m_pCheckBoxIoCache, &QCheckBox::stateChanged,
+ this, &UIStorageSettingsEditor::sltSetInformation);
+ connect(m_pCheckBoxPassthrough, &QCheckBox::stateChanged,
+ this, &UIStorageSettingsEditor::sltSetInformation);
+ connect(m_pCheckBoxTempEject, &QCheckBox::stateChanged,
+ this, &UIStorageSettingsEditor::sltSetInformation);
+ connect(m_pCheckBoxNonRotational, &QCheckBox::stateChanged,
+ this, &UIStorageSettingsEditor::sltSetInformation);
+ connect(m_pCheckBoxHotPluggable, &QCheckBox::stateChanged,
+ this, &UIStorageSettingsEditor::sltSetInformation);
+}
+
+void UIStorageSettingsEditor::cleanup()
+{
+ /* Destroy icon-pool: */
+ UIIconPoolStorageSettings::destroy();
+}
+
+void UIStorageSettingsEditor::addControllerWrapper(const QString &strName, KStorageBus enmBus, KStorageControllerType enmType)
+{
+#ifdef RT_STRICT
+ const QModelIndex index = m_pTreeViewStorage->currentIndex();
+ switch (enmBus)
+ {
+ case KStorageBus_IDE:
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsMoreIDEControllersPossible).toBool());
+ break;
+ case KStorageBus_SATA:
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsMoreSATAControllersPossible).toBool());
+ break;
+ case KStorageBus_SCSI:
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsMoreSCSIControllersPossible).toBool());
+ break;
+ case KStorageBus_SAS:
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsMoreSASControllersPossible).toBool());
+ break;
+ case KStorageBus_Floppy:
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsMoreFloppyControllersPossible).toBool());
+ break;
+ case KStorageBus_USB:
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsMoreUSBControllersPossible).toBool());
+ break;
+ case KStorageBus_PCIe:
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsMoreNVMeControllersPossible).toBool());
+ break;
+ case KStorageBus_VirtioSCSI:
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsMoreVirtioSCSIControllersPossible).toBool());
+ break;
+ default:
+ break;
+ }
+#endif
+
+ m_pModelStorage->addController(strName, enmBus, enmType);
+ emit sigValueChanged();
+}
+
+void UIStorageSettingsEditor::addAttachmentWrapper(KDeviceType enmDeviceType)
+{
+ const QModelIndex index = m_pTreeViewStorage->currentIndex();
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsController).toBool());
+ Assert(m_pModelStorage->data(index, StorageModel::R_IsMoreAttachmentsPossible).toBool());
+ const QString strMachineFolder(QFileInfo(m_strMachineSettingsFilePath).absolutePath());
+
+ QUuid uMediumId;
+ int iResult = UIMediumSelector::openMediumSelectorDialog(window(), UIMediumDefs::mediumTypeToLocal(enmDeviceType),
+ QUuid() /* current medium Id */, uMediumId,
+ strMachineFolder, m_strMachineName,
+ m_strMachineGuestOSTypeId,
+ true /* enable cr1eate action: */, m_uMachineId, m_pActionPool);
+
+ /* Continue only if iResult is either UIMediumSelector::ReturnCode_Accepted or UIMediumSelector::ReturnCode_LeftEmpty: */
+ /* If iResult is UIMediumSelector::ReturnCode_Accepted then we have to have a valid uMediumId: */
+ if (iResult == UIMediumSelector::ReturnCode_Rejected ||
+ (iResult == UIMediumSelector::ReturnCode_Accepted && uMediumId.isNull()))
+ return;
+
+ /* Only DVDs and floppy can be created empty: */
+ if (iResult == static_cast<int>(UIMediumSelector::ReturnCode_LeftEmpty) &&
+ (enmDeviceType != KDeviceType_DVD && enmDeviceType != KDeviceType_Floppy))
+ return;
+
+ m_pModelStorage->addAttachment(QUuid(m_pModelStorage->data(index, StorageModel::R_ItemId).toString()), enmDeviceType, uMediumId);
+ m_pModelStorage->sort();
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+}
+
+void UIStorageSettingsEditor::updateAdditionalDetails(KDeviceType enmDeviceType)
+{
+ m_pLabelHDFormat->setVisible(enmDeviceType == KDeviceType_HardDisk);
+ m_pFieldHDFormat->setVisible(enmDeviceType == KDeviceType_HardDisk);
+
+ m_pLabelCDFDType->setVisible(enmDeviceType != KDeviceType_HardDisk);
+ m_pFieldCDFDType->setVisible(enmDeviceType != KDeviceType_HardDisk);
+
+ m_pLabelHDVirtualSize->setVisible(enmDeviceType == KDeviceType_HardDisk);
+ m_pFieldHDVirtualSize->setVisible(enmDeviceType == KDeviceType_HardDisk);
+
+ m_pLabelHDActualSize->setVisible(enmDeviceType == KDeviceType_HardDisk);
+ m_pFieldHDActualSize->setVisible(enmDeviceType == KDeviceType_HardDisk);
+
+ m_pLabelCDFDSize->setVisible(enmDeviceType != KDeviceType_HardDisk);
+ m_pFieldCDFDSize->setVisible(enmDeviceType != KDeviceType_HardDisk);
+
+ m_pLabelHDDetails->setVisible(enmDeviceType == KDeviceType_HardDisk);
+ m_pFieldHDDetails->setVisible(enmDeviceType == KDeviceType_HardDisk);
+
+ m_pLabelEncryption->setVisible(enmDeviceType == KDeviceType_HardDisk);
+ m_pFieldEncryption->setVisible(enmDeviceType == KDeviceType_HardDisk);
+}
+
+QString UIStorageSettingsEditor::generateUniqueControllerName(const QString &strTemplate) const
+{
+ int iMaxNumber = 0;
+ const QModelIndex rootIndex = m_pModelStorage->root();
+ for (int i = 0; i < m_pModelStorage->rowCount(rootIndex); ++i)
+ {
+ const QModelIndex controllerIndex = m_pModelStorage->index(i, 0, rootIndex);
+ const QString strName = m_pModelStorage->data(controllerIndex, StorageModel::R_CtrName).toString();
+ if (strName.startsWith(strTemplate))
+ {
+ const QString strNumber(strName.right(strName.size() - strTemplate.size()));
+ bool fConverted = false;
+ const int iNumber = strNumber.toInt(&fConverted);
+ iMaxNumber = fConverted && (iNumber > iMaxNumber) ? iNumber : 1;
+ }
+ }
+ return iMaxNumber ? QString("%1 %2").arg(strTemplate).arg(++iMaxNumber) : strTemplate;
+}
+
+uint32_t UIStorageSettingsEditor::deviceCount(KDeviceType enmType) const
+{
+ uint32_t cDevices = 0;
+ const QModelIndex rootIndex = m_pModelStorage->root();
+ for (int i = 0; i < m_pModelStorage->rowCount(rootIndex); ++i)
+ {
+ const QModelIndex controllerIndex = m_pModelStorage->index(i, 0, rootIndex);
+ for (int j = 0; j < m_pModelStorage->rowCount(controllerIndex); ++j)
+ {
+ const QModelIndex attachmentIndex = m_pModelStorage->index(j, 0, controllerIndex);
+ const KDeviceType enmDeviceType = m_pModelStorage->data(attachmentIndex, StorageModel::R_AttDevice).value<KDeviceType>();
+ if (enmDeviceType == enmType)
+ ++cDevices;
+ }
+ }
+
+ return cDevices;
+}
+
+void UIStorageSettingsEditor::addChooseExistingMediumAction(QMenu *pOpenMediumMenu, const QString &strActionName)
+{
+ QAction *pChooseExistingMedium = pOpenMediumMenu->addAction(strActionName);
+ pChooseExistingMedium->setIcon(iconPool()->icon(PixmapType_ChooseExistingEn, PixmapType_ChooseExistingDis));
+ connect(pChooseExistingMedium, &QAction::triggered, this, &UIStorageSettingsEditor::sltChooseExistingMedium);
+}
+
+void UIStorageSettingsEditor::addChooseDiskFileAction(QMenu *pOpenMediumMenu, const QString &strActionName)
+{
+ QAction *pChooseDiskFile = pOpenMediumMenu->addAction(strActionName);
+ pChooseDiskFile->setIcon(iconPool()->icon(PixmapType_ChooseExistingEn, PixmapType_ChooseExistingDis));
+ connect(pChooseDiskFile, &QAction::triggered, this, &UIStorageSettingsEditor::sltChooseDiskFile);
+}
+
+void UIStorageSettingsEditor::addChooseHostDriveActions(QMenu *pOpenMediumMenu)
+{
+ foreach (const QUuid &uMediumId, uiCommon().mediumIDs())
+ {
+ const UIMedium guiMedium = uiCommon().medium(uMediumId);
+ if (guiMedium.isHostDrive() && m_pMediumIdHolder->type() == guiMedium.type())
+ {
+ QAction *pHostDriveAction = pOpenMediumMenu->addAction(guiMedium.name());
+ pHostDriveAction->setData(guiMedium.id());
+ connect(pHostDriveAction, &QAction::triggered, this, &UIStorageSettingsEditor::sltChooseHostDrive);
+ }
+ }
+}
+
+void UIStorageSettingsEditor::addRecentMediumActions(QMenu *pOpenMediumMenu, UIMediumDeviceType enmRecentMediumType)
+{
+ /* Get recent-medium list: */
+ QStringList recentMediumList;
+ switch (enmRecentMediumType)
+ {
+ case UIMediumDeviceType_HardDisk: recentMediumList = gEDataManager->recentListOfHardDrives(); break;
+ case UIMediumDeviceType_DVD: recentMediumList = gEDataManager->recentListOfOpticalDisks(); break;
+ case UIMediumDeviceType_Floppy: recentMediumList = gEDataManager->recentListOfFloppyDisks(); break;
+ default: break;
+ }
+ /* For every list-item: */
+ for (int iIndex = 0; iIndex < recentMediumList.size(); ++iIndex)
+ {
+ /* Prepare corresponding action: */
+ const QString &strRecentMediumLocation = recentMediumList.at(iIndex);
+ if (QFile::exists(strRecentMediumLocation))
+ {
+ QAction *pChooseRecentMediumAction = pOpenMediumMenu->addAction(QFileInfo(strRecentMediumLocation).fileName(),
+ this, SLOT(sltChooseRecentMedium()));
+ pChooseRecentMediumAction->setData(QString("%1,%2").arg(enmRecentMediumType).arg(strRecentMediumLocation));
+ }
+ }
+}
+
+/* static */
+QString UIStorageSettingsEditor::compressText(const QString &strText)
+{
+ return QString("<nobr><compact elipsis=\"end\">%1</compact></nobr>").arg(strText);
+}
+
+
+# include "UIStorageSettingsEditor.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIStorageSettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIStorageSettingsEditor.h
new file mode 100644
index 00000000..d3259b07
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIStorageSettingsEditor.h
@@ -0,0 +1,520 @@
+/* $Id: UIStorageSettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIStorageSettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIStorageSettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIStorageSettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIMediumDefs.h"
+#include "UISettingsDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Using declarations: */
+using namespace UISettingsDefs;
+
+/* Forward declarations: */
+class QCheckBox;
+class QComboBox;
+class QHBoxLayout;
+class QLabel;
+class QLineEdit;
+class QSpinBox;
+class QStackedWidget;
+class QVBoxLayout;
+class QILabel;
+class QILabelSeparator;
+class QISplitter;
+class QIToolBar;
+class QIToolButton;
+class QITreeView;
+class UIActionPool;
+class UIMediumIDHolder;
+class StorageModel;
+
+/** Storage Attachment data structure. */
+struct UIDataStorageAttachment
+{
+ /** Constructs data. */
+ UIDataStorageAttachment()
+ : m_enmDeviceType(KDeviceType_Null)
+ , m_iPort(-1)
+ , m_iDevice(-1)
+ , m_uMediumId(QUuid())
+ , m_fPassthrough(false)
+ , m_fTempEject(false)
+ , m_fNonRotational(false)
+ , m_fHotPluggable(false)
+ , m_strKey(QString())
+ {}
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool equal(const UIDataStorageAttachment &another) const
+ {
+ return true
+ && (m_enmDeviceType == another.m_enmDeviceType)
+ && (m_iPort == another.m_iPort)
+ && (m_iDevice == another.m_iDevice)
+ && (m_uMediumId == another.m_uMediumId)
+ && (m_fPassthrough == another.m_fPassthrough)
+ && (m_fTempEject == another.m_fTempEject)
+ && (m_fNonRotational == another.m_fNonRotational)
+ && (m_fHotPluggable == another.m_fHotPluggable)
+ && (m_strKey == another.m_strKey)
+ ;
+ }
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool operator==(const UIDataStorageAttachment &another) const { return equal(another); }
+ /** Returns whether @a another passed data is different from this one. */
+ bool operator!=(const UIDataStorageAttachment &another) const { return !equal(another); }
+
+ /** Holds the device type. */
+ KDeviceType m_enmDeviceType;
+ /** Holds the port. */
+ LONG m_iPort;
+ /** Holds the device. */
+ LONG m_iDevice;
+ /** Holds the medium ID. */
+ QUuid m_uMediumId;
+ /** Holds whether the attachment being passed through. */
+ bool m_fPassthrough;
+ /** Holds whether the attachment being temporarily eject. */
+ bool m_fTempEject;
+ /** Holds whether the attachment is solid-state. */
+ bool m_fNonRotational;
+ /** Holds whether the attachment is hot-pluggable. */
+ bool m_fHotPluggable;
+ /** Holds the unique key. */
+ QString m_strKey;
+};
+
+/** Storage Controller data structure. */
+struct UIDataStorageController
+{
+ /** Constructs data. */
+ UIDataStorageController()
+ : m_strName(QString())
+ , m_enmBus(KStorageBus_Null)
+ , m_enmType(KStorageControllerType_Null)
+ , m_uPortCount(0)
+ , m_fUseHostIOCache(false)
+ , m_strKey(QString())
+ {}
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool equal(const UIDataStorageController &another) const
+ {
+ return true
+ && (m_strName == another.m_strName)
+ && (m_enmBus == another.m_enmBus)
+ && (m_enmType == another.m_enmType)
+ && (m_uPortCount == another.m_uPortCount)
+ && (m_fUseHostIOCache == another.m_fUseHostIOCache)
+ && (m_strKey == another.m_strKey)
+ ;
+ }
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool operator==(const UIDataStorageController &another) const { return equal(another); }
+ /** Returns whether @a another passed data is different from this one. */
+ bool operator!=(const UIDataStorageController &another) const { return !equal(another); }
+
+ /** Holds the name. */
+ QString m_strName;
+ /** Holds the bus. */
+ KStorageBus m_enmBus;
+ /** Holds the type. */
+ KStorageControllerType m_enmType;
+ /** Holds the port count. */
+ uint m_uPortCount;
+ /** Holds whether the controller uses host IO cache. */
+ bool m_fUseHostIOCache;
+ /** Holds the unique key. */
+ QString m_strKey;
+};
+
+/** QWidget subclass used as acceleration features editor. */
+class SHARED_LIBRARY_STUFF UIStorageSettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value change. */
+ void sigValueChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIStorageSettingsEditor(QWidget *pParent = 0);
+ /** Destructs editor. */
+ virtual ~UIStorageSettingsEditor() RT_OVERRIDE;
+
+ /** Defines @a pActionPool. */
+ void setActionPool(UIActionPool *pActionPool);
+
+ /** Defines machine @a uMachineId. */
+ void setMachineId(const QUuid &uMachineId);
+ /** Defines machine @a strName. */
+ void setMachineName(const QString &strName);
+ /** Defines machine settings @a strFilePath. */
+ void setMachineSettingsFilePath(const QString &strFilePath);
+ /** Defines machine guest OS type @a strId. */
+ void setMachineGuestOSTypeId(const QString &strId);
+
+ /** Defines @a enmConfigurationAccessLevel. */
+ void setConfigurationAccessLevel(ConfigurationAccessLevel enmConfigurationAccessLevel);
+
+ /** Defines chipset @a enmType. */
+ void setChipsetType(KChipsetType enmType);
+ /** Returns chipset type. */
+ KChipsetType chipsetType() const;
+
+ /** Returns current controller types. */
+ QMap<KStorageBus, int> currentControllerTypes() const;
+ /** Returns maximum controller types. */
+ QMap<KStorageBus, int> maximumControllerTypes() const;
+
+ /** Defines a set of @a controllers and @a attachments. */
+ void setValue(const QList<UIDataStorageController> &controllers,
+ const QList<QList<UIDataStorageAttachment> > &attachments);
+ /** Acquires a set of @a controllers and @a attachments. */
+ void getValue(QList<UIDataStorageController> &controllers,
+ QList<QList<UIDataStorageAttachment> > &attachments);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles enumeration of medium with @a uMediumId. */
+ void sltHandleMediumEnumerated(const QUuid &uMediumId);
+ /** Handles removing of medium with @a uMediumId. */
+ void sltHandleMediumDeleted(const QUuid &uMediumId);
+
+ /** Handles command to add controller. */
+ void sltAddController();
+ /** Handles command to add PIIX3 controller. */
+ void sltAddControllerPIIX3();
+ /** Handles command to add PIIX4 controller. */
+ void sltAddControllerPIIX4();
+ /** Handles command to add ICH6 controller. */
+ void sltAddControllerICH6();
+ /** Handles command to add AHCI controller. */
+ void sltAddControllerAHCI();
+ /** Handles command to add LsiLogic controller. */
+ void sltAddControllerLsiLogic();
+ /** Handles command to add BusLogic controller. */
+ void sltAddControllerBusLogic();
+ /** Handles command to add Floppy controller. */
+ void sltAddControllerFloppy();
+ /** Handles command to add SAS controller. */
+ void sltAddControllerLsiLogicSAS();
+ /** Handles command to add USB controller. */
+ void sltAddControllerUSB();
+ /** Handles command to add NVMe controller. */
+ void sltAddControllerNVMe();
+ /** Handles command to add virtio-scsi controller. */
+ void sltAddControllerVirtioSCSI();
+ /** Handles command to remove controller. */
+ void sltRemoveController();
+
+ /** Handles command to add attachment. */
+ void sltAddAttachment();
+ /** Handles command to add HD attachment. */
+ void sltAddAttachmentHD();
+ /** Handles command to add CD attachment. */
+ void sltAddAttachmentCD();
+ /** Handles command to add FD attachment. */
+ void sltAddAttachmentFD();
+ /** Handles command to remove attachment. */
+ void sltRemoveAttachment();
+
+ /** Loads information from model to widgets. */
+ void sltGetInformation();
+ /** Saves information from widgets to model. */
+ void sltSetInformation();
+
+ /** Prepares 'Open Medium' menu. */
+ void sltPrepareOpenMediumMenu();
+ /** Unmounts current device. */
+ void sltUnmountDevice();
+ /** Mounts existing medium. */
+ void sltChooseExistingMedium();
+ /** Mounts a medium from a disk file. */
+ void sltChooseDiskFile();
+ /** Mounts existing host-drive. */
+ void sltChooseHostDrive();
+ /** Mounts one of recent media. */
+ void sltChooseRecentMedium();
+
+ /** Updates action states. */
+ void sltUpdateActionStates();
+
+ /** Handles row insertion into @a parentIndex on @a iPosition. */
+ void sltHandleRowInsertion(const QModelIndex &parentIndex, int iPosition);
+ /** Handles row removal. */
+ void sltHandleRowRemoval();
+
+ /** Handles current item change. */
+ void sltHandleCurrentItemChange();
+
+ /** Handles context menu request for @a position. */
+ void sltHandleContextMenuRequest(const QPoint &position);
+
+ /** Handles item branch drawing with @a pPainter, within @a rect for item with @a index. */
+ void sltHandleDrawItemBranches(QPainter *pPainter, const QRect &rect, const QModelIndex &index);
+
+ /** Handles mouse-move @a pEvent. */
+ void sltHandleMouseMove(QMouseEvent *pEvent);
+ /** Handles mouse-click @a pEvent. */
+ void sltHandleMouseClick(QMouseEvent *pEvent);
+ /** Handles mouse-release @a pEvent. */
+ void sltHandleMouseRelease(QMouseEvent *pEvent);
+
+ /** Handles drag-enter @a pEvent. */
+ void sltHandleDragEnter(QDragEnterEvent *pEvent);
+ /** Handles drag-move @a pEvent. */
+ void sltHandleDragMove(QDragMoveEvent *pEvent);
+ /** Handles drag-drop @a pEvent. */
+ void sltHandleDragDrop(QDropEvent *pEvent);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares left pane. */
+ void prepareLeftPane();
+ /** Prepares tree view. */
+ void prepareTreeView();
+ /** Prepares toolbar. */
+ void prepareToolBar();
+ /** Prepares right pane. */
+ void prepareRightPane();
+ /** Prepares empty widget. */
+ void prepareEmptyWidget();
+ /** Prepares controller widget. */
+ void prepareControllerWidget();
+ /** Prepares attachment widget. */
+ void prepareAttachmentWidget();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Adds controller with @a strName, @a enmBus and @a enmType. */
+ void addControllerWrapper(const QString &strName, KStorageBus enmBus, KStorageControllerType enmType);
+ /** Adds attachment with @a enmDevice. */
+ void addAttachmentWrapper(KDeviceType enmDevice);
+
+ /** Updates additions details according to passed @a enmType. */
+ void updateAdditionalDetails(KDeviceType enmType);
+
+ /** Generates unique controller name based on passed @a strTemplate. */
+ QString generateUniqueControllerName(const QString &strTemplate) const;
+
+ /** Returns current devices count for passed @a enmType. */
+ uint32_t deviceCount(KDeviceType enmType) const;
+
+ /** Adds 'Choose/Create Medium' action into passed @a pOpenMediumMenu under passed @a strActionName. */
+ void addChooseExistingMediumAction(QMenu *pOpenMediumMenu, const QString &strActionName);
+ /** Adds 'Choose Disk File' action into passed @a pOpenMediumMenu under passed @a strActionName. */
+ void addChooseDiskFileAction(QMenu *pOpenMediumMenu, const QString &strActionName);
+ /** Adds 'Choose Host Drive' actions into passed @a pOpenMediumMenu. */
+ void addChooseHostDriveActions(QMenu *pOpenMediumMenu);
+ /** Adds 'Choose Recent Medium' actions of passed @a enmRecentMediumType into passed @a pOpenMediumMenu. */
+ void addRecentMediumActions(QMenu *pOpenMediumMenu, UIMediumDeviceType enmRecentMediumType);
+
+ /** Returns result of @a strText being compressed. */
+ static QString compressText(const QString &strText);
+
+ /** @name General
+ * @{ */
+ /** Holds the controller mime-type for the D&D system. */
+ static const QString s_strControllerMimeType;
+ /** Holds the attachment mime-type for the D&D system. */
+ static const QString s_strAttachmentMimeType;
+
+ /** Holds whether the loading is in progress. */
+ bool m_fLoadingInProgress;
+
+ /** Holds the machine ID. */
+ QUuid m_uMachineId;
+ /** Holds the machine settings file-path. */
+ QString m_strMachineName;
+ /** Holds the machine settings file-path. */
+ QString m_strMachineSettingsFilePath;
+ /** Holds the machine guest OS type ID. */
+ QString m_strMachineGuestOSTypeId;
+
+ /** Holds configuration access level. */
+ ConfigurationAccessLevel m_enmConfigurationAccessLevel;
+
+ /** Holds the last mouse-press position. */
+ QPoint m_mousePressPosition;
+ /** @} */
+
+ /** @name Objects
+ * @{ */
+ /** Holds the action pool instance. */
+ UIActionPool *m_pActionPool;
+
+ /** Holds the storage-model instance. */
+ StorageModel *m_pModelStorage;
+
+ /** Holds the medium ID wrapper instance. */
+ UIMediumIDHolder *m_pMediumIdHolder;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the splitter instance. */
+ QISplitter *m_pSplitter;
+
+ /** Holds the left pane instance. */
+ QWidget *m_pWidgetLeftPane;
+ /** Holds the left pane separator instance. */
+ QILabelSeparator *m_pLabelSeparatorLeftPane;
+ /** Holds the tree-view layout instance. */
+ QVBoxLayout *m_pLayoutTree;
+ /** Holds the tree-view instance. */
+ QITreeView *m_pTreeViewStorage;
+ /** Holds the toolbar layout instance. */
+ QHBoxLayout *m_pLayoutToolbar;
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolbar;
+ /** Holds the 'Add Controller' action instance. */
+ QAction *m_pActionAddController;
+ /** Holds the 'Remove Controller' action instance. */
+ QAction *m_pActionRemoveController;
+ /** Holds the map of add controller action instances. */
+ QMap<KStorageControllerType, QAction*> m_addControllerActions;
+ /** Holds the 'Add Attachment' action instance. */
+ QAction *m_pActionAddAttachment;
+ /** Holds the 'Remove Attachment' action instance. */
+ QAction *m_pActionRemoveAttachment;
+ /** Holds the 'Add HD Attachment' action instance. */
+ QAction *m_pActionAddAttachmentHD;
+ /** Holds the 'Add CD Attachment' action instance. */
+ QAction *m_pActionAddAttachmentCD;
+ /** Holds the 'Add FD Attachment' action instance. */
+ QAction *m_pActionAddAttachmentFD;
+
+ /** Holds the right pane instance. */
+ QStackedWidget *m_pStackRightPane;
+ /** Holds the right pane empty widget separator instance. */
+ QILabelSeparator *m_pLabelSeparatorEmpty;
+ /** Holds the info label instance. */
+ QLabel *m_pLabelInfo;
+ /** Holds the right pane controller widget separator instance. */
+ QILabelSeparator *m_pLabelSeparatorParameters;
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the name editor instance. */
+ QLineEdit *m_pEditorName;
+ /** Holds the type label instance. */
+ QLabel *m_pLabelType;
+ /** Holds the type combo instance. */
+ QComboBox *m_pComboType;
+ /** Holds the port count label instance. */
+ QLabel *m_pLabelPortCount;
+ /** Holds the port count spinbox instance. */
+ QSpinBox *m_pSpinboxPortCount;
+ /** Holds the IO cache check-box instance. */
+ QCheckBox *m_pCheckBoxIoCache;
+ /** Holds the right pane attachment widget separator instance. */
+ QILabelSeparator *m_pLabelSeparatorAttributes;
+ /** Holds the medium label instance. */
+ QLabel *m_pLabelMedium;
+ /** Holds the slot combo instance. */
+ QComboBox *m_pComboSlot;
+ /** Holds the open tool-button instance. */
+ QIToolButton *m_pToolButtonOpen;
+ /** Holds the passthrough check-box instance. */
+ QCheckBox *m_pCheckBoxPassthrough;
+ /** Holds the temporary eject check-box instance. */
+ QCheckBox *m_pCheckBoxTempEject;
+ /** Holds the non-rotational check-box instance. */
+ QCheckBox *m_pCheckBoxNonRotational;
+ /** Holds the hot-pluggable check-box instance. */
+ QCheckBox *m_pCheckBoxHotPluggable;
+ /** Holds the right pane attachment widget separator instance. */
+ QILabelSeparator *m_pLabelSeparatorInformation;
+ /** Holds the HD format label instance. */
+ QLabel *m_pLabelHDFormat;
+ /** Holds the HD format field instance. */
+ QILabel *m_pFieldHDFormat;
+ /** Holds the CD/FD type label instance. */
+ QLabel *m_pLabelCDFDType;
+ /** Holds the CD/FD type field instance. */
+ QILabel *m_pFieldCDFDType;
+ /** Holds the HD virtual size label instance. */
+ QLabel *m_pLabelHDVirtualSize;
+ /** Holds the HD virtual size field instance. */
+ QILabel *m_pFieldHDVirtualSize;
+ /** Holds the HD actual size label instance. */
+ QLabel *m_pLabelHDActualSize;
+ /** Holds the HD actual size field instance. */
+ QILabel *m_pFieldHDActualSize;
+ /** Holds the CD/FD size label instance. */
+ QLabel *m_pLabelCDFDSize;
+ /** Holds the CD/FD size field instance. */
+ QILabel *m_pFieldCDFDSize;
+ /** Holds the HD details label instance. */
+ QLabel *m_pLabelHDDetails;
+ /** Holds the HD details field instance. */
+ QILabel *m_pFieldHDDetails;
+ /** Holds the location label instance. */
+ QLabel *m_pLabelLocation;
+ /** Holds the location field instance. */
+ QILabel *m_pFieldLocation;
+ /** Holds the usage label instance. */
+ QLabel *m_pLabelUsage;
+ /** Holds the usage field instance. */
+ QILabel *m_pFieldUsage;
+ /** Holds the encryption label instance. */
+ QLabel *m_pLabelEncryption;
+ /** Holds the encryption field instance. */
+ QILabel *m_pFieldEncryption;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIStorageSettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UITpmEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UITpmEditor.cpp
new file mode 100644
index 00000000..dbd7b283
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UITpmEditor.cpp
@@ -0,0 +1,170 @@
+/* $Id: UITpmEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UITpmEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UITpmEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UITpmEditor::UITpmEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KTpmType_Max)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UITpmEditor::setValue(KTpmType enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+KTpmType UITpmEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<KTpmType>() : m_enmValue;
+}
+
+int UITpmEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UITpmEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UITpmEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("&TPM:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const KTpmType enmType = m_pCombo->itemData(i).value<KTpmType>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Selects the TPM type to be emulated in this virtual machine."));
+ }
+}
+
+void UITpmEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ connect(m_pCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UITpmEditor::sigValueChanged);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UITpmEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_pCombo->clear();
+
+ /* Load currently supported values: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedTpmTypes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KTpmType_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const KTpmType &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UITpmEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UITpmEditor.h
new file mode 100644
index 00000000..7f3f2e71
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UITpmEditor.h
@@ -0,0 +1,103 @@
+/* $Id: UITpmEditor.h $ */
+/** @file
+ * VBox Qt GUI - UITpmEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UITpmEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UITpmEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as a TPM editor. */
+class SHARED_LIBRARY_STUFF UITpmEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value change. */
+ void sigValueChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UITpmEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KTpmType enmValue);
+ /** Returns editor value. */
+ KTpmType value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KTpmType> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the value to be selected. */
+ KTpmType m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KTpmType> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UITpmEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBControllerEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBControllerEditor.cpp
new file mode 100644
index 00000000..78a42d15
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBControllerEditor.cpp
@@ -0,0 +1,186 @@
+/* $Id: UIUSBControllerEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUSBControllerEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QRadioButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIUSBControllerEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIUSBControllerEditor::UIUSBControllerEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(KUSBControllerType_Max)
+ , m_pRadioButtonUSB1(0)
+ , m_pRadioButtonUSB2(0)
+ , m_pRadioButtonUSB3(0)
+{
+ prepare();
+}
+
+void UIUSBControllerEditor::setValue(KUSBControllerType enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ updateButtonSet();
+ }
+}
+
+KUSBControllerType UIUSBControllerEditor::value() const
+{
+ if ( m_pRadioButtonUSB1
+ && m_pRadioButtonUSB1->isChecked())
+ return KUSBControllerType_OHCI;
+ else if ( m_pRadioButtonUSB2
+ && m_pRadioButtonUSB2->isChecked())
+ return KUSBControllerType_EHCI;
+ else if ( m_pRadioButtonUSB3
+ && m_pRadioButtonUSB3->isChecked())
+ return KUSBControllerType_XHCI;
+ return m_enmValue;
+}
+
+void UIUSBControllerEditor::retranslateUi()
+{
+ if (m_pRadioButtonUSB1)
+ {
+ m_pRadioButtonUSB1->setText(tr("USB &1.1 (OHCI) Controller"));
+ m_pRadioButtonUSB1->setToolTip(tr("When chosen, enables the virtual USB OHCI controller of "
+ "this machine. The USB OHCI controller provides USB 1.0 support."));
+ }
+ if (m_pRadioButtonUSB2)
+ {
+ m_pRadioButtonUSB2->setText(tr("USB &2.0 (OHCI + EHCI) Controller"));
+ m_pRadioButtonUSB2->setToolTip(tr("When chosen, enables the virtual USB OHCI and EHCI "
+ "controllers of this machine. Together they provide USB 2.0 support."));
+ }
+ if (m_pRadioButtonUSB3)
+ {
+ m_pRadioButtonUSB3->setText(tr("USB &3.0 (xHCI) Controller"));
+ m_pRadioButtonUSB3->setToolTip(tr("When chosen, enables the virtual USB xHCI controller of "
+ "this machine. The USB xHCI controller provides USB 3.0 support."));
+ }
+}
+
+void UIUSBControllerEditor::prepare()
+{
+ /* Create main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare button-group: */
+ QButtonGroup *pButtonGroup = new QButtonGroup(this);
+ if (pButtonGroup)
+ {
+ /* Prepare USB1 radio-button: */
+ m_pRadioButtonUSB1 = new QRadioButton(this);
+ if (m_pRadioButtonUSB1)
+ {
+ m_pRadioButtonUSB1->setVisible(false);
+ pLayout->addWidget(m_pRadioButtonUSB1);
+ }
+ /* Prepare USB2 radio-button: */
+ m_pRadioButtonUSB2 = new QRadioButton(this);
+ if (m_pRadioButtonUSB2)
+ {
+ m_pRadioButtonUSB2->setVisible(false);
+ pLayout->addWidget(m_pRadioButtonUSB2);
+ }
+ /* Prepare USB3 radio-button: */
+ m_pRadioButtonUSB3 = new QRadioButton(this);
+ if (m_pRadioButtonUSB3)
+ {
+ m_pRadioButtonUSB3->setVisible(false);
+ pLayout->addWidget(m_pRadioButtonUSB3);
+ }
+
+ connect(pButtonGroup, static_cast<void(QButtonGroup::*)(QAbstractButton*)>(&QButtonGroup::buttonClicked),
+ this, &UIUSBControllerEditor::sigValueChanged);
+ }
+ }
+
+ /* Update button set: */
+ updateButtonSet();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIUSBControllerEditor::updateButtonSet()
+{
+ /* Load currently supported types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_supportedValues = comProperties.GetSupportedUSBControllerTypes();
+
+ /* Make sure requested value if sane is present as well: */
+ if ( m_enmValue != KUSBControllerType_Max
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update visibility for all values: */
+ if (m_pRadioButtonUSB1)
+ m_pRadioButtonUSB1->setVisible(m_supportedValues.contains(KUSBControllerType_OHCI));
+ if (m_pRadioButtonUSB2)
+ m_pRadioButtonUSB2->setVisible(m_supportedValues.contains(KUSBControllerType_EHCI));
+ if (m_pRadioButtonUSB3)
+ m_pRadioButtonUSB3->setVisible(m_supportedValues.contains(KUSBControllerType_XHCI));
+
+ /* Look for proper button to choose: */
+ switch (m_enmValue)
+ {
+ default:
+ case KUSBControllerType_OHCI:
+ {
+ if (m_pRadioButtonUSB1)
+ m_pRadioButtonUSB1->setChecked(true);
+ break;
+ }
+ case KUSBControllerType_EHCI:
+ {
+ if (m_pRadioButtonUSB2)
+ m_pRadioButtonUSB2->setChecked(true);
+ break;
+ }
+ case KUSBControllerType_XHCI:
+ {
+ if (m_pRadioButtonUSB3)
+ m_pRadioButtonUSB3->setChecked(true);
+ break;
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBControllerEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBControllerEditor.h
new file mode 100644
index 00000000..2183a482
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBControllerEditor.h
@@ -0,0 +1,97 @@
+/* $Id: UIUSBControllerEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIUSBControllerEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIUSBControllerEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIUSBControllerEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QRadioButton;
+
+/** QWidget subclass used as a USB controller editor. */
+class SHARED_LIBRARY_STUFF UIUSBControllerEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value change. */
+ void sigValueChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIUSBControllerEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a enmValue. */
+ void setValue(KUSBControllerType enmValue);
+ /** Returns editor value. */
+ KUSBControllerType value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<KUSBControllerType> supportedValues() const { return m_supportedValues; }
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates button set. */
+ void updateButtonSet();
+
+ /** Holds the value to be selected. */
+ KUSBControllerType m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<KUSBControllerType> m_supportedValues;
+
+ /** Holds the USB1 radio-button instance. */
+ QRadioButton *m_pRadioButtonUSB1;
+ /** Holds the USB2 radio-button instance. */
+ QRadioButton *m_pRadioButtonUSB2;
+ /** Holds the USB3 radio-button instance. */
+ QRadioButton *m_pRadioButtonUSB3;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIUSBControllerEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFilterDetailsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFilterDetailsEditor.cpp
new file mode 100644
index 00000000..7014e62e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFilterDetailsEditor.cpp
@@ -0,0 +1,490 @@
+/* $Id: UIUSBFilterDetailsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUSBFilterDetailsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QPushButton>
+#include <QRegularExpressionValidator>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QILineEdit.h"
+#include "UIConverter.h"
+#include "UIUSBFilterDetailsEditor.h"
+
+
+UIUSBFilterDetailsEditor::UIUSBFilterDetailsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI2<QIDialog>(pParent, Qt::Sheet)
+ , m_pLabelName(0)
+ , m_pEditorName(0)
+ , m_pLabelVendorID(0)
+ , m_pEditorVendorID(0)
+ , m_pLabelProductID(0)
+ , m_pEditorProductID(0)
+ , m_pLabelRevision(0)
+ , m_pEditorRevision(0)
+ , m_pLabelManufacturer(0)
+ , m_pEditorManufacturer(0)
+ , m_pLabelProduct(0)
+ , m_pEditorProduct(0)
+ , m_pLabelSerialNo(0)
+ , m_pEditorSerialNo(0)
+ , m_pLabelPort(0)
+ , m_pEditorPort(0)
+ , m_pLabelRemote(0)
+ , m_pComboRemote(0)
+ , m_pButtonBox(0)
+{
+ prepare();
+}
+
+void UIUSBFilterDetailsEditor::setName(const QString &strName)
+{
+ if (m_pEditorName)
+ m_pEditorName->setText(strName);
+}
+
+QString UIUSBFilterDetailsEditor::name() const
+{
+ return m_pEditorName ? wiped(m_pEditorName->text()) : QString();
+}
+
+void UIUSBFilterDetailsEditor::setVendorID(const QString &strVendorID)
+{
+ if (m_pEditorVendorID)
+ m_pEditorVendorID->setText(strVendorID);
+}
+
+QString UIUSBFilterDetailsEditor::vendorID() const
+{
+ return m_pEditorVendorID ? wiped(m_pEditorVendorID->text()) : QString();
+}
+
+void UIUSBFilterDetailsEditor::setProductID(const QString &strProductID)
+{
+ if (m_pEditorProductID)
+ m_pEditorProductID->setText(strProductID);
+}
+
+QString UIUSBFilterDetailsEditor::productID() const
+{
+ return m_pEditorProductID ? wiped(m_pEditorProductID->text()) : QString();
+}
+
+void UIUSBFilterDetailsEditor::setRevision(const QString &strRevision)
+{
+ if (m_pEditorRevision)
+ m_pEditorRevision->setText(strRevision);
+}
+
+QString UIUSBFilterDetailsEditor::revision() const
+{
+ return m_pEditorRevision ? wiped(m_pEditorRevision->text()) : QString();
+}
+
+void UIUSBFilterDetailsEditor::setManufacturer(const QString &strManufacturer)
+{
+ if (m_pEditorManufacturer)
+ m_pEditorManufacturer->setText(strManufacturer);
+}
+
+QString UIUSBFilterDetailsEditor::manufacturer() const
+{
+ return m_pEditorManufacturer ? wiped(m_pEditorManufacturer->text()) : QString();
+}
+
+void UIUSBFilterDetailsEditor::setProduct(const QString &strProduct)
+{
+ if (m_pEditorProduct)
+ m_pEditorProduct->setText(strProduct);
+}
+
+QString UIUSBFilterDetailsEditor::product() const
+{
+ return m_pEditorProduct ? wiped(m_pEditorProduct->text()) : QString();
+}
+
+void UIUSBFilterDetailsEditor::setSerialNo(const QString &strSerialNo)
+{
+ if (m_pEditorSerialNo)
+ m_pEditorSerialNo->setText(strSerialNo);
+}
+
+QString UIUSBFilterDetailsEditor::serialNo() const
+{
+ return m_pEditorSerialNo ? wiped(m_pEditorSerialNo->text()) : QString();
+}
+
+void UIUSBFilterDetailsEditor::setPort(const QString &strPort)
+{
+ if (m_pEditorPort)
+ m_pEditorPort->setText(strPort);
+}
+
+QString UIUSBFilterDetailsEditor::port() const
+{
+ return m_pEditorPort ? wiped(m_pEditorPort->text()) : QString();
+}
+
+void UIUSBFilterDetailsEditor::setRemoteMode(const UIRemoteMode &enmRemoteMode)
+{
+ /* Look for proper index to choose: */
+ if (m_pComboRemote)
+ {
+ const int iIndex = m_pComboRemote->findData(QVariant::fromValue(enmRemoteMode));
+ if (iIndex != -1)
+ m_pComboRemote->setCurrentIndex(iIndex);
+ }
+}
+
+UIRemoteMode UIUSBFilterDetailsEditor::remoteMode() const
+{
+ return m_pComboRemote ? m_pComboRemote->currentData().value<UIRemoteMode>() : UIRemoteMode_Any;
+}
+
+void UIUSBFilterDetailsEditor::retranslateUi()
+{
+ setWindowTitle(tr("USB Filter Details"));
+
+ if (m_pLabelName)
+ m_pLabelName->setText(tr("&Name:"));
+ if (m_pEditorName)
+ m_pEditorName->setToolTip(tr("Holds the filter name."));
+
+ if (m_pLabelVendorID)
+ m_pLabelVendorID->setText(tr("&Vendor ID:"));
+ if (m_pEditorVendorID)
+ m_pEditorVendorID->setToolTip(tr("Holds the vendor ID filter. The <i>exact match</i> string format is <tt>XXXX</tt> "
+ "where <tt>X</tt> is a hexadecimal digit. An empty string will match any value."));
+
+ if (m_pLabelProductID)
+ m_pLabelProductID->setText(tr("&Product ID:"));
+ if (m_pEditorProductID)
+ m_pEditorProductID->setToolTip(tr("Holds the product ID filter. The <i>exact match</i> string format is <tt>XXXX</tt> "
+ "where <tt>X</tt> is a hexadecimal digit. An empty string will match any value."));
+
+ if (m_pLabelRevision)
+ m_pLabelRevision->setText(tr("&Revision:"));
+ if (m_pEditorRevision)
+ m_pEditorRevision->setToolTip(tr("Holds the revision number filter. The <i>exact match</i> string format is "
+ "<tt>IIFF</tt> where <tt>I</tt> is a decimal digit of the integer part and <tt>F</tt> "
+ "is a decimal digit of the fractional part. An empty string will match any value."));
+
+ if (m_pLabelManufacturer)
+ m_pLabelManufacturer->setText(tr("&Manufacturer:"));
+ if (m_pEditorManufacturer)
+ m_pEditorManufacturer->setToolTip(tr("Holds the manufacturer filter as an <i>exact match</i> string. An empty string "
+ "will match any value."));
+
+ if (m_pLabelProduct)
+ m_pLabelProduct->setText(tr("Pro&duct:"));
+ if (m_pEditorProduct)
+ m_pEditorProduct->setToolTip(tr("Holds the product name filter as an <i>exact match</i> string. An empty string will "
+ "match any value."));
+
+ if (m_pLabelSerialNo)
+ m_pLabelSerialNo->setText(tr("&Serial No.:"));
+ if (m_pEditorSerialNo)
+ m_pEditorSerialNo->setToolTip(tr("Holds the serial number filter as an <i>exact match</i> string. An empty string will "
+ "match any value."));
+
+ if (m_pLabelPort)
+ m_pLabelPort->setText(tr("Por&t:"));
+ if (m_pEditorPort)
+ m_pEditorPort->setToolTip(tr("Holds the host USB port filter as an <i>exact match</i> string. An empty string will match "
+ "any value."));
+
+ if (m_pLabelRemote)
+ m_pLabelRemote->setText(tr("R&emote:"));
+ if (m_pComboRemote)
+ {
+ for (int i = 0; i < m_pComboRemote->count(); ++i)
+ {
+ const UIRemoteMode enmType = m_pComboRemote->itemData(i).value<UIRemoteMode>();
+ m_pComboRemote->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pComboRemote->setToolTip(tr("Holds whether this filter applies to USB devices attached locally to the host computer "
+ "(No), to a VRDP client's computer (Yes), or both (Any)."));
+ }
+}
+
+void UIUSBFilterDetailsEditor::sltRevalidate()
+{
+ /* Cast sender to editor: */
+ QILineEdit *pEditor = qobject_cast<QILineEdit*>(sender());
+ AssertPtrReturnVoid(pEditor);
+
+ /* Performs sender's validation: */
+ revalidate(pEditor);
+}
+
+void UIUSBFilterDetailsEditor::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Adjust dialog size: */
+ adjustSize();
+}
+
+void UIUSBFilterDetailsEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setRowStretch(9, 1);
+
+ /* Prepare name label: */
+ m_pLabelName = new QLabel(this);
+ if (m_pLabelName)
+ {
+ m_pLabelName->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelName, 0, 0);
+ }
+ /* Prepare name editor: */
+ m_pEditorName = new QILineEdit(this);
+ if (m_pEditorName)
+ {
+ if (m_pLabelName)
+ m_pLabelName->setBuddy(m_pEditorName);
+ m_pEditorName->setMinimumWidthByText(QString().fill('0', 32));
+ m_pEditorName->setValidator(new QRegularExpressionValidator(QRegularExpression(".+"), this));
+ connect(m_pEditorName, &QLineEdit::textChanged,
+ this, &UIUSBFilterDetailsEditor::sltRevalidate);
+ pLayout->addWidget(m_pEditorName, 0, 1);
+ }
+
+ /* Prepare vendor ID label: */
+ m_pLabelVendorID = new QLabel(this);
+ if (m_pLabelVendorID)
+ {
+ m_pLabelVendorID->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelVendorID, 1, 0);
+ }
+ /* Prepare vendor ID editor: */
+ m_pEditorVendorID = new QILineEdit(this);
+ if (m_pEditorVendorID)
+ {
+ if (m_pLabelVendorID)
+ m_pLabelVendorID->setBuddy(m_pEditorVendorID);
+ m_pEditorVendorID->setMinimumWidthByText(QString().fill('0', 8));
+ m_pEditorVendorID->setValidator(new QRegularExpressionValidator(QRegularExpression("[0-9a-fA-F]{0,4}"), this));
+ connect(m_pEditorVendorID, &QLineEdit::textChanged,
+ this, &UIUSBFilterDetailsEditor::sltRevalidate);
+ pLayout->addWidget(m_pEditorVendorID, 1, 1);
+ }
+
+ /* Prepare product ID label: */
+ m_pLabelProductID = new QLabel(this);
+ if (m_pLabelProductID)
+ {
+ m_pLabelProductID->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelProductID, 2, 0);
+ }
+ /* Prepare product ID editor: */
+ m_pEditorProductID = new QILineEdit(this);
+ if (m_pEditorProductID)
+ {
+ if (m_pLabelProductID)
+ m_pLabelProductID->setBuddy(m_pEditorProductID);
+ m_pEditorProductID->setMinimumWidthByText(QString().fill('0', 8));
+ m_pEditorProductID->setValidator(new QRegularExpressionValidator(QRegularExpression("[0-9a-fA-F]{0,4}"), this));
+ connect(m_pEditorProductID, &QLineEdit::textChanged,
+ this, &UIUSBFilterDetailsEditor::sltRevalidate);
+ pLayout->addWidget(m_pEditorProductID, 2, 1);
+ }
+
+ /* Prepare revision label: */
+ m_pLabelRevision = new QLabel(this);
+ if (m_pLabelRevision)
+ {
+ m_pLabelRevision->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelRevision, 3, 0);
+ }
+ /* Prepare revision editor: */
+ m_pEditorRevision = new QILineEdit(this);
+ if (m_pEditorRevision)
+ {
+ if (m_pLabelRevision)
+ m_pLabelRevision->setBuddy(m_pEditorRevision);
+ m_pEditorRevision->setMinimumWidthByText(QString().fill('0', 8));
+ m_pEditorRevision->setValidator(new QRegularExpressionValidator(QRegularExpression("[0-9a-fA-F]{0,4}"), this));
+ connect(m_pEditorRevision, &QLineEdit::textChanged,
+ this, &UIUSBFilterDetailsEditor::sltRevalidate);
+ pLayout->addWidget(m_pEditorRevision, 3, 1);
+ }
+
+ /* Prepare manufacturer label: */
+ m_pLabelManufacturer = new QLabel(this);
+ if (m_pLabelManufacturer)
+ {
+ m_pLabelManufacturer->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelManufacturer, 4, 0);
+ }
+ /* Prepare manufacturer editor: */
+ m_pEditorManufacturer = new QILineEdit(this);
+ if (m_pEditorManufacturer)
+ {
+ if (m_pLabelManufacturer)
+ m_pLabelManufacturer->setBuddy(m_pEditorManufacturer);
+ m_pEditorManufacturer->setMinimumWidthByText(QString().fill('0', 8));
+ pLayout->addWidget(m_pEditorManufacturer, 4, 1);
+ }
+
+ /* Prepare product label: */
+ m_pLabelProduct = new QLabel(this);
+ if (m_pLabelProduct)
+ {
+ m_pLabelProduct->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelProduct, 5, 0);
+ }
+ /* Prepare product editor: */
+ m_pEditorProduct = new QILineEdit(this);
+ if (m_pEditorProduct)
+ {
+ if (m_pLabelProduct)
+ m_pLabelProduct->setBuddy(m_pEditorProduct);
+ m_pEditorProduct->setMinimumWidthByText(QString().fill('0', 8));
+ pLayout->addWidget(m_pEditorProduct, 5, 1);
+ }
+
+ /* Prepare serial NO label: */
+ m_pLabelSerialNo = new QLabel(this);
+ if (m_pLabelSerialNo)
+ {
+ m_pLabelSerialNo->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelSerialNo, 6, 0);
+ }
+ /* Prepare serial NO editor: */
+ m_pEditorSerialNo = new QILineEdit(this);
+ if (m_pEditorSerialNo)
+ {
+ if (m_pLabelSerialNo)
+ m_pLabelSerialNo->setBuddy(m_pEditorSerialNo);
+ m_pEditorSerialNo->setMinimumWidthByText(QString().fill('0', 8));
+ pLayout->addWidget(m_pEditorSerialNo, 6, 1);
+ }
+
+ /* Prepare port label: */
+ m_pLabelPort = new QLabel(this);
+ if (m_pLabelPort)
+ {
+ m_pLabelPort->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelPort, 7, 0);
+ }
+ /* Prepare port editor: */
+ m_pEditorPort = new QILineEdit(this);
+ if (m_pEditorPort)
+ {
+ if (m_pLabelPort)
+ m_pLabelPort->setBuddy(m_pEditorPort);
+ m_pEditorPort->setMinimumWidthByText(QString().fill('0', 8));
+ m_pEditorPort->setValidator(new QRegularExpressionValidator(QRegularExpression("(0[xX])?[0-9a-fA-F]{0,4}"), this));
+ connect(m_pEditorPort, &QLineEdit::textChanged,
+ this, &UIUSBFilterDetailsEditor::sltRevalidate);
+ pLayout->addWidget(m_pEditorPort, 7, 1);
+ }
+
+ /* Prepare remote label: */
+ m_pLabelRemote = new QLabel(this);
+ if (m_pLabelRemote)
+ {
+ m_pLabelRemote->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayout->addWidget(m_pLabelRemote, 8, 0);
+ }
+ /* Prepare remote combo: */
+ m_pComboRemote = new QComboBox(this);
+ if (m_pComboRemote)
+ {
+ if (m_pLabelRemote)
+ m_pLabelRemote->setBuddy(m_pComboRemote);
+ m_pComboRemote->addItem(QString(), QVariant::fromValue(UIRemoteMode_Any)); /* Any */
+ m_pComboRemote->addItem(QString(), QVariant::fromValue(UIRemoteMode_On)); /* Yes */
+ m_pComboRemote->addItem(QString(), QVariant::fromValue(UIRemoteMode_Off)); /* No */
+ pLayout->addWidget(m_pComboRemote, 8, 1);
+ }
+
+ /* Prepare button-box: */
+ m_pButtonBox = new QIDialogButtonBox(this);
+ if (m_pButtonBox)
+ {
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ pLayout->addWidget(m_pButtonBox, 10, 0, 1, 2);
+ }
+ }
+}
+
+void UIUSBFilterDetailsEditor::prepareConnections()
+{
+ if (m_pButtonBox)
+ {
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted,
+ this, &UIUSBFilterDetailsEditor::accept);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected,
+ this, &UIUSBFilterDetailsEditor::reject);
+ }
+}
+
+void UIUSBFilterDetailsEditor::revalidate(QILineEdit *pEditor)
+{
+ /* Acquire current validator: */
+ const QValidator *pValidator = pEditor->validator();
+ AssertPtrReturnVoid(pValidator);
+
+ /* Validate current text: */
+ QString strText = pEditor->text();
+ int iPos = 0;
+ const QValidator::State enmState = pValidator->validate(strText, iPos);
+
+ /* Store current validation verdict: */
+ m_valid[pEditor] = enmState == QValidator::Acceptable;
+
+ /* Calculate overall validation result: */
+ bool fValid = true;
+ foreach (bool fValidOne, m_valid.values())
+ if (!fValidOne)
+ {
+ fValid = false;
+ break;
+ }
+
+ /* Enable/disable button-box Ok button accordingly: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(fValid);
+}
+
+/* static */
+QString UIUSBFilterDetailsEditor::wiped(const QString &strString)
+{
+ return strString.isEmpty() ? QString() : strString;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFilterDetailsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFilterDetailsEditor.h
new file mode 100644
index 00000000..8934c3bc
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFilterDetailsEditor.h
@@ -0,0 +1,170 @@
+/* $Id: UIUSBFilterDetailsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIUSBFilterDetailsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIUSBFilterDetailsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIUSBFilterDetailsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "QIDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QLabel;
+class QIDialogButtonBox;
+class QILineEdit;
+
+/** QIDialog subclass used as a USB filter editor. */
+class SHARED_LIBRARY_STUFF UIUSBFilterDetailsEditor : public QIWithRetranslateUI2<QIDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIUSBFilterDetailsEditor(QWidget *pParent = 0);
+
+ /** Defines @a strName. */
+ void setName(const QString &strName);
+ /** Returns name. */
+ QString name() const;
+
+ /** Defines @a strVendorID. */
+ void setVendorID(const QString &strVendorID);
+ /** Returns vendor ID. */
+ QString vendorID() const;
+
+ /** Defines @a strProductID. */
+ void setProductID(const QString &strProductID);
+ /** Returns product ID. */
+ QString productID() const;
+
+ /** Defines @a strRevision. */
+ void setRevision(const QString &strRevision);
+ /** Returns revision. */
+ QString revision() const;
+
+ /** Defines @a strManufacturer. */
+ void setManufacturer(const QString &strManufacturer);
+ /** Returns manufacturer. */
+ QString manufacturer() const;
+
+ /** Defines @a strProduct. */
+ void setProduct(const QString &strProduct);
+ /** Returns product. */
+ QString product() const;
+
+ /** Defines @a strSerialNo. */
+ void setSerialNo(const QString &strSerialNo);
+ /** Returns serial no. */
+ QString serialNo() const;
+
+ /** Defines @a strPort. */
+ void setPort(const QString &strPort);
+ /** Returns port. */
+ QString port() const;
+
+ /** Defines @a enmRemoteMode. */
+ void setRemoteMode(const UIRemoteMode &enmRemoteMode);
+ /** Returns port. */
+ UIRemoteMode remoteMode() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Performs validation for connected sender. */
+ void sltRevalidate();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Performs validation for @a pEditor. */
+ void revalidate(QILineEdit *pEditor);
+
+ /** Wipes out @a strString if it's empty. */
+ static QString wiped(const QString &strString);
+
+ /** Holds whether editors currently valid. */
+ QMap<QILineEdit*, bool> m_valid;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the name editor instance. */
+ QILineEdit *m_pEditorName;
+ /** Holds the vendor ID label instance. */
+ QLabel *m_pLabelVendorID;
+ /** Holds the vendor ID editor instance. */
+ QILineEdit *m_pEditorVendorID;
+ /** Holds the product ID label instance. */
+ QLabel *m_pLabelProductID;
+ /** Holds the product ID editor instance. */
+ QILineEdit *m_pEditorProductID;
+ /** Holds the revision label instance. */
+ QLabel *m_pLabelRevision;
+ /** Holds the revision editor instance. */
+ QILineEdit *m_pEditorRevision;
+ /** Holds the manufacturer label instance. */
+ QLabel *m_pLabelManufacturer;
+ /** Holds the manufacturer editor instance. */
+ QILineEdit *m_pEditorManufacturer;
+ /** Holds the product label instance. */
+ QLabel *m_pLabelProduct;
+ /** Holds the product editor instance. */
+ QILineEdit *m_pEditorProduct;
+ /** Holds the serial NO label instance. */
+ QLabel *m_pLabelSerialNo;
+ /** Holds the serial NO editor instance. */
+ QILineEdit *m_pEditorSerialNo;
+ /** Holds the port label instance. */
+ QLabel *m_pLabelPort;
+ /** Holds the port editor instance. */
+ QILineEdit *m_pEditorPort;
+ /** Holds the remote label instance. */
+ QLabel *m_pLabelRemote;
+ /** Holds the remote combo instance. */
+ QComboBox *m_pComboRemote;
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIUSBFilterDetailsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFiltersEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFiltersEditor.cpp
new file mode 100644
index 00000000..aeb44de5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFiltersEditor.cpp
@@ -0,0 +1,700 @@
+/* $Id: UIUSBFiltersEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUSBFiltersEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QMenu>
+#include <QRegExp>
+#include <QToolTip>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILabelSeparator.h"
+#include "QIToolBar.h"
+#include "QITreeWidget.h"
+#include "UICommon.h"
+#include "UIIconPool.h"
+#include "UIUSBFilterDetailsEditor.h"
+#include "UIUSBFiltersEditor.h"
+
+/* COM includes: */
+#include "CConsole.h"
+#include "CHostUSBDevice.h"
+#include "CUSBDevice.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+
+/** USB Filter tree-widget item. */
+class USBFilterTreeWidgetItem : public QITreeWidgetItem, public UIDataUSBFilter
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs USB filter type (root) item. */
+ USBFilterTreeWidgetItem(QITreeWidget *pParent) : QITreeWidgetItem(pParent) {}
+
+ /** Updates item fields. */
+ void updateFields();
+
+protected:
+
+ /** Returns default text. */
+ virtual QString defaultText() const RT_OVERRIDE;
+};
+
+
+/** USB Filter popup menu. */
+class UIUSBMenu : public QMenu
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs USB Filter menu passing @a pParent to the base-class. */
+ UIUSBMenu(QWidget *pParent);
+
+ /** Returns USB device related to passed action. */
+ const CUSBDevice& getUSB(QAction *pAction);
+
+ /** Defines @a comConsole. */
+ void setConsole(const CConsole &comConsole);
+
+protected:
+
+ /** Handles any @a pEvent handler. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Prepares menu contents. */
+ void processAboutToShow();
+
+private:
+
+ /** Holds the USB device map. */
+ QMap<QAction*, CUSBDevice> m_usbDeviceMap;
+
+ /** Holds the console. */
+ CConsole m_comConsole;
+};
+
+
+/*********************************************************************************************************************************
+* Class USBFilterTreeWidgetItem implementation. *
+*********************************************************************************************************************************/
+
+void USBFilterTreeWidgetItem::updateFields()
+{
+ setText(0, m_strName);
+}
+
+QString USBFilterTreeWidgetItem::defaultText() const
+{
+ return checkState(0) == Qt::Checked
+ ? UIUSBFiltersEditor::tr("%1, Active", "col.1 text, col.1 state").arg(text(0))
+ : text(0);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIUSBMenu implementation. *
+*********************************************************************************************************************************/
+
+UIUSBMenu::UIUSBMenu(QWidget *pParent)
+ : QMenu(pParent)
+{
+ connect(this, &UIUSBMenu::aboutToShow,
+ this, &UIUSBMenu::processAboutToShow);
+}
+
+const CUSBDevice &UIUSBMenu::getUSB(QAction *pAction)
+{
+ return m_usbDeviceMap[pAction];
+}
+
+void UIUSBMenu::setConsole(const CConsole &comConsole)
+{
+ m_comConsole = comConsole;
+}
+
+bool UIUSBMenu::event(QEvent *pEvent)
+{
+ /* We provide dynamic tooltips for the usb devices: */
+ if (pEvent->type() == QEvent::ToolTip)
+ {
+ QHelpEvent *pHelpEvent = static_cast<QHelpEvent*>(pEvent);
+ QAction *pAction = actionAt(pHelpEvent->pos());
+ if (pAction)
+ {
+ CUSBDevice usb = m_usbDeviceMap[pAction];
+ if (!usb.isNull())
+ {
+ QToolTip::showText(pHelpEvent->globalPos(), uiCommon().usbToolTip(usb));
+ return true;
+ }
+ }
+ }
+ /* Call to base-class: */
+ return QMenu::event(pEvent);
+}
+
+void UIUSBMenu::processAboutToShow()
+{
+ /* Clear lists initially: */
+ clear();
+ m_usbDeviceMap.clear();
+
+ /* Get host for further activities: */
+ CHost comHost = uiCommon().host();
+
+ /* Check whether we have host USB devices at all: */
+ bool fIsUSBEmpty = comHost.GetUSBDevices().size() == 0;
+ if (fIsUSBEmpty)
+ {
+ /* Empty action for no USB device case: */
+ QAction *pAction = addAction(tr("<no devices available>", "USB devices"));
+ pAction->setEnabled(false);
+ pAction->setToolTip(tr("No supported devices connected to the host PC", "USB device tooltip"));
+ }
+ else
+ {
+ /* Action per each host USB device: */
+ foreach (const CHostUSBDevice &comHostUsb, comHost.GetUSBDevices())
+ {
+ CUSBDevice comUsb(comHostUsb);
+ QAction *pAction = addAction(uiCommon().usbDetails(comUsb));
+ pAction->setCheckable(true);
+ m_usbDeviceMap[pAction] = comUsb;
+ /* Check if created item was already attached to this session: */
+ if (!m_comConsole.isNull())
+ {
+ CUSBDevice attachedUSB = m_comConsole.FindUSBDeviceById(comUsb.GetId());
+ pAction->setChecked(!attachedUSB.isNull());
+ pAction->setEnabled(comHostUsb.GetState() != KUSBDeviceState_Unavailable);
+ }
+ }
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIUSBFiltersEditor implementation. *
+*********************************************************************************************************************************/
+
+UIUSBFiltersEditor::UIUSBFiltersEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLabelSeparator(0)
+ , m_pLayoutTree(0)
+ , m_pTreeWidget(0)
+ , m_pToolbar(0)
+ , m_pActionNew(0)
+ , m_pActionAdd(0)
+ , m_pActionEdit(0)
+ , m_pActionRemove(0)
+ , m_pActionMoveUp(0)
+ , m_pActionMoveDown(0)
+ , m_pMenuUSBDevices(0)
+{
+ prepare();
+}
+
+void UIUSBFiltersEditor::setValue(const QList<UIDataUSBFilter> &guiValue)
+{
+ /* Update cached value and
+ * tree-widget if value has changed: */
+ if (m_guiValue != guiValue)
+ {
+ m_guiValue = guiValue;
+ reloadTree();
+ }
+}
+
+QList<UIDataUSBFilter> UIUSBFiltersEditor::value() const
+{
+ /* Sanity check: */
+ if (!m_pTreeWidget)
+ return m_guiValue;
+
+ /* Prepare result: */
+ QList<UIDataUSBFilter> result;
+
+ /* For each filter: */
+ QTreeWidgetItem *pMainRootItem = m_pTreeWidget->invisibleRootItem();
+ for (int iFilterIndex = 0; iFilterIndex < pMainRootItem->childCount(); ++iFilterIndex)
+ {
+ /* Gather and cache new data: */
+ const USBFilterTreeWidgetItem *pItem = static_cast<USBFilterTreeWidgetItem*>(pMainRootItem->child(iFilterIndex));
+ result << *pItem;
+ }
+
+ /* Return result: */
+ return result;
+}
+
+void UIUSBFiltersEditor::retranslateUi()
+{
+ /* Tags: */
+ m_strTrUSBFilterName = tr("New Filter %1", "usb");
+
+ /* Translate separator label: */
+ if (m_pLabelSeparator)
+ m_pLabelSeparator->setText(tr("USB Device &Filters"));
+
+ /* Translate tree-widget: */
+ if (m_pTreeWidget)
+ m_pTreeWidget->setWhatsThis(tr("Lists all USB filters of this machine. The checkbox to the left defines whether the "
+ "particular filter is enabled or not. Use the context menu or buttons to the right to "
+ "add or remove USB filters."));
+
+ /* Translate actions: */
+ if (m_pActionNew)
+ {
+ m_pActionNew->setText(tr("Add Empty Filter"));
+ m_pActionNew->setToolTip(tr("Adds new USB filter with all fields initially set to empty strings. "
+ "Note that such a filter will match any attached USB device."));
+ }
+ if (m_pActionAdd)
+ {
+ m_pActionAdd->setText(tr("Add Filter From Device"));
+ m_pActionAdd->setToolTip(tr("Adds new USB filter with all fields set to the values of the "
+ "selected USB device attached to the host PC."));
+ }
+ if (m_pActionEdit)
+ {
+ m_pActionEdit->setText(tr("Edit Filter"));
+ m_pActionEdit->setToolTip(tr("Edits selected USB filter."));
+ }
+ if (m_pActionRemove)
+ {
+ m_pActionRemove->setText(tr("Remove Filter"));
+ m_pActionRemove->setToolTip(tr("Removes selected USB filter."));
+ }
+ if (m_pActionMoveUp)
+ {
+ m_pActionMoveUp->setText(tr("Move Filter Up"));
+ m_pActionMoveUp->setToolTip(tr("Moves selected USB filter up."));
+ }
+ if (m_pActionMoveDown)
+ {
+ m_pActionMoveDown->setText(tr("Move Filter Down"));
+ m_pActionMoveDown->setToolTip(tr("Moves selected USB filter down."));
+ }
+}
+
+void UIUSBFiltersEditor::sltHandleCurrentItemChange(QTreeWidgetItem *pCurrentItem)
+{
+ if (pCurrentItem && !pCurrentItem->isSelected())
+ pCurrentItem->setSelected(true);
+ m_pActionEdit->setEnabled(pCurrentItem);
+ m_pActionRemove->setEnabled(pCurrentItem);
+ m_pActionMoveUp->setEnabled(pCurrentItem && m_pTreeWidget->itemAbove(pCurrentItem));
+ m_pActionMoveDown->setEnabled(pCurrentItem && m_pTreeWidget->itemBelow(pCurrentItem));
+}
+
+void UIUSBFiltersEditor::sltHandleDoubleClick(QTreeWidgetItem *pItem)
+{
+ AssertPtrReturnVoid(pItem);
+ sltEditFilter();
+}
+
+void UIUSBFiltersEditor::sltHandleContextMenuRequest(const QPoint &position)
+{
+ QMenu menu;
+ QTreeWidgetItem *pItem = m_pTreeWidget->itemAt(position);
+ if (m_pTreeWidget->isEnabled() && pItem && pItem->flags() & Qt::ItemIsSelectable)
+ {
+ menu.addAction(m_pActionEdit);
+ menu.addAction(m_pActionRemove);
+ menu.addSeparator();
+ menu.addAction(m_pActionMoveUp);
+ menu.addAction(m_pActionMoveDown);
+ }
+ else
+ {
+ menu.addAction(m_pActionNew);
+ menu.addAction(m_pActionAdd);
+ }
+ if (!menu.isEmpty())
+ menu.exec(m_pTreeWidget->viewport()->mapToGlobal(position));
+}
+
+void UIUSBFiltersEditor::sltCreateFilter()
+{
+ /* Search for the max available filter index: */
+ int iMaxFilterIndex = 0;
+ const QRegExp regExp(QString("^") + m_strTrUSBFilterName.arg("([0-9]+)") + QString("$"));
+ QTreeWidgetItemIterator iterator(m_pTreeWidget);
+ while (*iterator)
+ {
+ const QString filterName = (*iterator)->text(0);
+ const int pos = regExp.indexIn(filterName);
+ if (pos != -1)
+ iMaxFilterIndex = regExp.cap(1).toInt() > iMaxFilterIndex ?
+ regExp.cap(1).toInt() : iMaxFilterIndex;
+ ++iterator;
+ }
+
+ /* Prepare new data: */
+ UIDataUSBFilter newFilterData;
+ newFilterData.m_fActive = true;
+ newFilterData.m_strName = m_strTrUSBFilterName.arg(iMaxFilterIndex + 1);
+
+ /* Add new filter item: */
+ addUSBFilterItem(newFilterData, true /* its new? */);
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+}
+
+void UIUSBFiltersEditor::sltAddFilter()
+{
+ if (m_pMenuUSBDevices)
+ m_pMenuUSBDevices->exec(QCursor::pos());
+}
+
+void UIUSBFiltersEditor::sltAddFilterConfirmed(QAction *pAction)
+{
+ if (!m_pMenuUSBDevices)
+ return;
+ /* Get USB device: */
+ const CUSBDevice comUsb = m_pMenuUSBDevices->getUSB(pAction);
+ if (comUsb.isNull())
+ return;
+
+ /* Prepare new USB filter data: */
+ UIDataUSBFilter newFilterData;
+ newFilterData.m_fActive = true;
+ newFilterData.m_strName = uiCommon().usbDetails(comUsb);
+ newFilterData.m_strVendorId = QString::number(comUsb.GetVendorId(), 16).toUpper().rightJustified(4, '0');
+ newFilterData.m_strProductId = QString::number(comUsb.GetProductId(), 16).toUpper().rightJustified(4, '0');
+ newFilterData.m_strRevision = QString::number(comUsb.GetRevision(), 16).toUpper().rightJustified(4, '0');
+ /* The port property depends on the host computer rather than on the USB
+ * device itself; for this reason only a few people will want to use it
+ * in the filter since the same device plugged into a different socket
+ * will not match the filter in this case. */
+ newFilterData.m_strPort = QString::asprintf("%#06hX", comUsb.GetPort());
+ newFilterData.m_strManufacturer = comUsb.GetManufacturer();
+ newFilterData.m_strProduct = comUsb.GetProduct();
+ newFilterData.m_strSerialNumber = comUsb.GetSerialNumber();
+ newFilterData.m_enmRemoteMode = comUsb.GetRemote() ? UIRemoteMode_On : UIRemoteMode_Off;
+
+ /* Add new USB filter item: */
+ addUSBFilterItem(newFilterData, true /* its new? */);
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+}
+
+void UIUSBFiltersEditor::sltEditFilter()
+{
+ /* Check current filter item: */
+ USBFilterTreeWidgetItem *pItem = static_cast<USBFilterTreeWidgetItem*>(m_pTreeWidget->currentItem());
+ AssertPtrReturnVoid(pItem);
+
+ /* Configure USB filter details editor: */
+ UIUSBFilterDetailsEditor dlgFolderDetails(this); /// @todo convey usedList!
+ dlgFolderDetails.setName(pItem->m_strName);
+ dlgFolderDetails.setVendorID(pItem->m_strVendorId);
+ dlgFolderDetails.setProductID(pItem->m_strProductId);
+ dlgFolderDetails.setRevision(pItem->m_strRevision);
+ dlgFolderDetails.setManufacturer(pItem->m_strManufacturer);
+ dlgFolderDetails.setProduct(pItem->m_strProduct);
+ dlgFolderDetails.setSerialNo(pItem->m_strSerialNumber);
+ dlgFolderDetails.setPort(pItem->m_strPort);
+ dlgFolderDetails.setRemoteMode(pItem->m_enmRemoteMode);
+
+ /* Run filter details dialog: */
+ if (dlgFolderDetails.exec() == QDialog::Accepted)
+ {
+ /* Prepare new data: */
+ pItem->m_strName = dlgFolderDetails.name();
+ pItem->m_strVendorId = dlgFolderDetails.vendorID();
+ pItem->m_strProductId = dlgFolderDetails.productID();
+ pItem->m_strRevision = dlgFolderDetails.revision();
+ pItem->m_strManufacturer = dlgFolderDetails.manufacturer();
+ pItem->m_strProduct = dlgFolderDetails.product();
+ pItem->m_strSerialNumber = dlgFolderDetails.serialNo();
+ pItem->m_strPort = dlgFolderDetails.port();
+ pItem->m_enmRemoteMode = dlgFolderDetails.remoteMode();
+ pItem->updateFields();
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+ }
+}
+
+void UIUSBFiltersEditor::sltRemoveFilter()
+{
+ /* Check current USB filter item: */
+ QTreeWidgetItem *pItem = m_pTreeWidget->currentItem();
+ AssertPtrReturnVoid(pItem);
+
+ /* Delete corresponding item: */
+ delete pItem;
+
+ /* Notify listeners: */
+ emit sigValueChanged();
+}
+
+void UIUSBFiltersEditor::sltMoveFilterUp()
+{
+ /* Check current USB filter item: */
+ QTreeWidgetItem *pItem = m_pTreeWidget->currentItem();
+ AssertPtrReturnVoid(pItem);
+
+ /* Move the item up: */
+ const int iIndex = m_pTreeWidget->indexOfTopLevelItem(pItem);
+ QTreeWidgetItem *pTakenItem = m_pTreeWidget->takeTopLevelItem(iIndex);
+ Assert(pItem == pTakenItem);
+ m_pTreeWidget->insertTopLevelItem(iIndex - 1, pTakenItem);
+
+ /* Make sure moved item still chosen: */
+ m_pTreeWidget->setCurrentItem(pTakenItem);
+}
+
+void UIUSBFiltersEditor::sltMoveFilterDown()
+{
+ /* Check current USB filter item: */
+ QTreeWidgetItem *pItem = m_pTreeWidget->currentItem();
+ AssertPtrReturnVoid(pItem);
+
+ /* Move the item down: */
+ const int iIndex = m_pTreeWidget->indexOfTopLevelItem(pItem);
+ QTreeWidgetItem *pTakenItem = m_pTreeWidget->takeTopLevelItem(iIndex);
+ Assert(pItem == pTakenItem);
+ m_pTreeWidget->insertTopLevelItem(iIndex + 1, pTakenItem);
+
+ /* Make sure moved item still chosen: */
+ m_pTreeWidget->setCurrentItem(pTakenItem);
+}
+
+void UIUSBFiltersEditor::sltHandleActivityStateChange(QTreeWidgetItem *pChangedItem)
+{
+ /* Check changed USB filter item: */
+ USBFilterTreeWidgetItem *pItem = static_cast<USBFilterTreeWidgetItem*>(pChangedItem);
+ AssertPtrReturnVoid(pItem);
+
+ /* Update corresponding item: */
+ pItem->m_fActive = pItem->checkState(0) == Qt::Checked;
+}
+
+void UIUSBFiltersEditor::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIUSBFiltersEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare separator: */
+ m_pLabelSeparator = new QILabelSeparator(this);
+ if (m_pLabelSeparator)
+ pLayout->addWidget(m_pLabelSeparator);
+
+ /* Prepare view layout: */
+ m_pLayoutTree = new QHBoxLayout;
+ if (m_pLayoutTree)
+ {
+ m_pLayoutTree->setContentsMargins(0, 0, 0, 0);
+ m_pLayoutTree->setSpacing(3);
+
+ /* Prepare tree-widget: */
+ prepareTreeWidget();
+ /* Prepare toolbar: */
+ prepareToolbar();
+
+ /* Update action availability: */
+ sltHandleCurrentItemChange(m_pTreeWidget->currentItem());
+
+ pLayout->addLayout(m_pLayoutTree);
+ }
+ }
+}
+
+void UIUSBFiltersEditor::prepareTreeWidget()
+{
+ /* Prepare shared folders tree-widget: */
+ m_pTreeWidget = new QITreeWidget(this);
+ if (m_pTreeWidget)
+ {
+ if (m_pLabelSeparator)
+ m_pLabelSeparator->setBuddy(m_pTreeWidget);
+ m_pTreeWidget->header()->hide();
+ m_pTreeWidget->setRootIsDecorated(false);
+ m_pTreeWidget->setUniformRowHeights(true);
+ m_pTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ m_pLayoutTree->addWidget(m_pTreeWidget);
+ }
+}
+
+void UIUSBFiltersEditor::prepareToolbar()
+{
+ /* Prepare shared folders toolbar: */
+ m_pToolbar = new QIToolBar(this);
+ if (m_pToolbar)
+ {
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pToolbar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolbar->setOrientation(Qt::Vertical);
+
+ /* Prepare 'New USB Filter' action: */
+ m_pActionNew = m_pToolbar->addAction(UIIconPool::iconSet(":/usb_new_16px.png",
+ ":/usb_new_disabled_16px.png"),
+ QString(), this, SLOT(sltCreateFilter()));
+ if (m_pActionNew)
+ m_pActionNew->setShortcuts(QList<QKeySequence>() << QKeySequence("Ins") << QKeySequence("Ctrl+N"));
+
+ /* Prepare 'Add USB Filter' action: */
+ m_pActionAdd = m_pToolbar->addAction(UIIconPool::iconSet(":/usb_add_16px.png",
+ ":/usb_add_disabled_16px.png"),
+ QString(), this, SLOT(sltAddFilter()));
+ if (m_pActionAdd)
+ m_pActionAdd->setShortcuts(QList<QKeySequence>() << QKeySequence("Alt+Ins") << QKeySequence("Ctrl+A"));
+
+ /* Prepare 'Edit USB Filter' action: */
+ m_pActionEdit = m_pToolbar->addAction(UIIconPool::iconSet(":/usb_filter_edit_16px.png",
+ ":/usb_filter_edit_disabled_16px.png"),
+ QString(), this, SLOT(sltEditFilter()));
+ if (m_pActionEdit)
+ m_pActionEdit->setShortcuts(QList<QKeySequence>() << QKeySequence("Alt+Return") << QKeySequence("Ctrl+Return"));
+
+ /* Prepare 'Remove USB Filter' action: */
+ m_pActionRemove = m_pToolbar->addAction(UIIconPool::iconSet(":/usb_remove_16px.png",
+ ":/usb_remove_disabled_16px.png"),
+ QString(), this, SLOT(sltRemoveFilter()));
+ if (m_pActionRemove)
+ m_pActionRemove->setShortcuts(QList<QKeySequence>() << QKeySequence("Del") << QKeySequence("Ctrl+R"));
+
+ /* Prepare 'Move USB Filter Up' action: */
+ m_pActionMoveUp = m_pToolbar->addAction(UIIconPool::iconSet(":/usb_moveup_16px.png",
+ ":/usb_moveup_disabled_16px.png"),
+ QString(), this, SLOT(sltMoveFilterUp()));
+ if (m_pActionMoveUp)
+ m_pActionMoveUp->setShortcuts(QList<QKeySequence>() << QKeySequence("Alt+Up") << QKeySequence("Ctrl+Up"));
+
+ /* Prepare 'Move USB Filter Down' action: */
+ m_pActionMoveDown = m_pToolbar->addAction(UIIconPool::iconSet(":/usb_movedown_16px.png",
+ ":/usb_movedown_disabled_16px.png"),
+ QString(), this, SLOT(sltMoveFilterDown()));
+ if (m_pActionMoveDown)
+ m_pActionMoveDown->setShortcuts(QList<QKeySequence>() << QKeySequence("Alt+Down") << QKeySequence("Ctrl+Down"));
+
+ /* Prepare USB devices menu: */
+ m_pMenuUSBDevices = new UIUSBMenu(this);
+
+ m_pLayoutTree->addWidget(m_pToolbar);
+ }
+}
+
+void UIUSBFiltersEditor::prepareConnections()
+{
+ /* Configure tree-widget connections: */
+ if (m_pTreeWidget)
+ {
+ connect(m_pTreeWidget, &QITreeWidget::currentItemChanged,
+ this, &UIUSBFiltersEditor::sltHandleCurrentItemChange);
+ connect(m_pTreeWidget, &QITreeWidget::itemDoubleClicked,
+ this, &UIUSBFiltersEditor::sltHandleDoubleClick);
+ connect(m_pTreeWidget, &QITreeWidget::customContextMenuRequested,
+ this, &UIUSBFiltersEditor::sltHandleContextMenuRequest);
+ connect(m_pTreeWidget, &QITreeWidget::itemChanged,
+ this, &UIUSBFiltersEditor::sltHandleActivityStateChange);
+ }
+
+ /* Configure USB device menu connections: */
+ if (m_pMenuUSBDevices)
+ connect(m_pMenuUSBDevices, &UIUSBMenu::triggered,
+ this, &UIUSBFiltersEditor::sltAddFilterConfirmed);
+}
+
+void UIUSBFiltersEditor::addUSBFilterItem(const UIDataUSBFilter &data, bool fChoose)
+{
+ /* Create USB filter item: */
+ USBFilterTreeWidgetItem *pItem = new USBFilterTreeWidgetItem(m_pTreeWidget);
+ if (pItem)
+ {
+ /* Configure item: */
+ pItem->setCheckState(0, data.m_fActive ? Qt::Checked : Qt::Unchecked);
+ pItem->m_fActive = data.m_fActive;
+ pItem->m_strName = data.m_strName;
+ pItem->m_strVendorId = data.m_strVendorId;
+ pItem->m_strProductId = data.m_strProductId;
+ pItem->m_strRevision = data.m_strRevision;
+ pItem->m_strManufacturer = data.m_strManufacturer;
+ pItem->m_strProduct = data.m_strProduct;
+ pItem->m_strSerialNumber = data.m_strSerialNumber;
+ pItem->m_strPort = data.m_strPort;
+ pItem->m_enmRemoteMode = data.m_enmRemoteMode;
+ pItem->updateFields();
+
+ /* Select this item if it's new: */
+ if (fChoose)
+ {
+ m_pTreeWidget->scrollToItem(pItem);
+ m_pTreeWidget->setCurrentItem(pItem);
+ sltHandleCurrentItemChange(pItem);
+ }
+ }
+}
+
+void UIUSBFiltersEditor::reloadTree()
+{
+ /* Sanity check: */
+ if (!m_pTreeWidget)
+ return;
+
+ /* Clear list initially: */
+ m_pTreeWidget->clear();
+
+ /* For each filter => load it from cache: */
+ foreach (const UIDataUSBFilter &guiData, m_guiValue)
+ addUSBFilterItem(guiData, false /* its new? */);
+
+ /* Choose first filter as current: */
+ m_pTreeWidget->setCurrentItem(m_pTreeWidget->topLevelItem(0));
+ sltHandleCurrentItemChange(m_pTreeWidget->currentItem());
+}
+
+
+#include "UIUSBFiltersEditor.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFiltersEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFiltersEditor.h
new file mode 100644
index 00000000..3180b59b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBFiltersEditor.h
@@ -0,0 +1,205 @@
+/* $Id: UIUSBFiltersEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIUSBFiltersEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIUSBFiltersEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIUSBFiltersEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+
+/* Forward declartions: */
+class QHBoxLayout;
+class QTreeWidgetItem;
+class QILabelSeparator;
+class QIToolBar;
+class QITreeWidget;
+class UIUSBMenu;
+
+/** USB Filter data. */
+struct UIDataUSBFilter
+{
+ /** Constructs data. */
+ UIDataUSBFilter()
+ : m_fActive(true)
+ , m_enmRemoteMode(UIRemoteMode_Any)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataUSBFilter &other) const
+ {
+ return true
+ && m_fActive == other.m_fActive
+ && m_strName == other.m_strName
+ && m_strVendorId == other.m_strVendorId
+ && m_strProductId == other.m_strProductId
+ && m_strRevision == other.m_strRevision
+ && m_strManufacturer == other.m_strManufacturer
+ && m_strProduct == other.m_strProduct
+ && m_strSerialNumber == other.m_strSerialNumber
+ && m_strPort == other.m_strPort
+ && m_enmRemoteMode == other.m_enmRemoteMode
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataUSBFilter &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataUSBFilter &other) const { return !equal(other); }
+
+ /** Holds whether USB filter is active. */
+ bool m_fActive;
+ /** Holds the USB filter name. */
+ QString m_strName;
+ /** Holds the USB filter vendor ID. */
+ QString m_strVendorId;
+ /** Holds the USB filter product ID. */
+ QString m_strProductId;
+ /** Holds the USB filter revision. */
+ QString m_strRevision;
+ /** Holds the USB filter manufacturer. */
+ QString m_strManufacturer;
+ /** Holds the USB filter product. */
+ QString m_strProduct;
+ /** Holds the USB filter serial number. */
+ QString m_strSerialNumber;
+ /** Holds the USB filter port. */
+ QString m_strPort;
+ /** Holds the USB filter remote. */
+ UIRemoteMode m_enmRemoteMode;
+};
+
+/** QWidget subclass used as a USB filters editor. */
+class SHARED_LIBRARY_STUFF UIUSBFiltersEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value change. */
+ void sigValueChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIUSBFiltersEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a strValue. */
+ void setValue(const QList<UIDataUSBFilter> &guiValue);
+ /** Returns editor value. */
+ QList<UIDataUSBFilter> value() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles @a pCurrentItem change. */
+ void sltHandleCurrentItemChange(QTreeWidgetItem *pCurrentItem);
+ /** Handles @a pItem double-click. */
+ void sltHandleDoubleClick(QTreeWidgetItem *pItem);
+ /** Handles context menu request for @a position. */
+ void sltHandleContextMenuRequest(const QPoint &position);
+
+ /** Handles command to create USB filter. */
+ void sltCreateFilter();
+ /** Handles command to add USB filter. */
+ void sltAddFilter();
+ /** Handles command to confirm add of existing USB filter defined by @a pAction. */
+ void sltAddFilterConfirmed(QAction *pAction);
+ /** Handles command to edit USB filter. */
+ void sltEditFilter();
+ /** Handles command to remove USB filter. */
+ void sltRemoveFilter();
+ /** Handles command to move chosen USB filter up. */
+ void sltMoveFilterUp();
+ /** Handles command to move chosen USB filter down. */
+ void sltMoveFilterDown();
+
+ /** Handles USB filter tree activity state change for @a pChangedItem. */
+ void sltHandleActivityStateChange(QTreeWidgetItem *pChangedItem);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepare tree-widget. */
+ void prepareTreeWidget();
+ /** Prepare tool-bar. */
+ void prepareToolbar();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Creates USB filter item based on passed @a data. */
+ void addUSBFilterItem(const UIDataUSBFilter &data, bool fChoose);
+ /** Reloads tree. */
+ void reloadTree();
+
+ /** Holds the value to be set. */
+ QList<UIDataUSBFilter> m_guiValue;
+
+ /** @name Internal
+ * @{ */
+ /** Holds the "New Filter %1" translation tag. */
+ QString m_strTrUSBFilterName;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the widget separator instance. */
+ QILabelSeparator *m_pLabelSeparator;
+ /** Holds the tree layout instance. */
+ QHBoxLayout *m_pLayoutTree;
+ /** Holds the tree-widget instance. */
+ QITreeWidget *m_pTreeWidget;
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolbar;
+ /** Holds the 'new USB filter' action instance. */
+ QAction *m_pActionNew;
+ /** Holds the 'add USB filter' action instance. */
+ QAction *m_pActionAdd;
+ /** Holds the 'edit USB filter' action instance. */
+ QAction *m_pActionEdit;
+ /** Holds the 'remove USB filter' action instance. */
+ QAction *m_pActionRemove;
+ /** Holds the Move Up action instance. */
+ QAction *m_pActionMoveUp;
+ /** Holds the Move Down action instance. */
+ QAction *m_pActionMoveDown;
+ /** Holds the USB devices menu instance. */
+ UIUSBMenu *m_pMenuUSBDevices;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIUSBFiltersEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBSettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBSettingsEditor.cpp
new file mode 100644
index 00000000..cf8f3854
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBSettingsEditor.cpp
@@ -0,0 +1,196 @@
+/* $Id: UIUSBSettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUSBSettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIUSBControllerEditor.h"
+#include "UIUSBSettingsEditor.h"
+
+
+UIUSBSettingsEditor::UIUSBSettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fFeatureEnabled(false)
+ , m_pCheckboxFeature(0)
+ , m_pWidgetSettings(0)
+ , m_pEditorController(0)
+ , m_pEditorFilters(0)
+{
+ prepare();
+}
+
+void UIUSBSettingsEditor::setFeatureEnabled(bool fEnabled)
+{
+ if (m_fFeatureEnabled != fEnabled)
+ {
+ m_fFeatureEnabled = fEnabled;
+ if (m_pCheckboxFeature)
+ m_pCheckboxFeature->setChecked(m_fFeatureEnabled);
+ }
+}
+
+bool UIUSBSettingsEditor::isFeatureEnabled() const
+{
+ return m_pCheckboxFeature ? m_pCheckboxFeature->isChecked() : m_fFeatureEnabled;
+}
+
+void UIUSBSettingsEditor::setFeatureAvailable(bool fAvailable)
+{
+ if (m_pCheckboxFeature)
+ m_pCheckboxFeature->setEnabled(fAvailable);
+}
+
+void UIUSBSettingsEditor::setUsbControllerType(KUSBControllerType enmType)
+{
+ if (m_pEditorController)
+ m_pEditorController->setValue(enmType);
+}
+
+KUSBControllerType UIUSBSettingsEditor::usbControllerType() const
+{
+ return m_pEditorController ? m_pEditorController->value() : KUSBControllerType_Max;
+}
+
+void UIUSBSettingsEditor::setUsbControllerOptionAvailable(bool fAvailable)
+{
+ if (m_pEditorController)
+ m_pEditorController->setEnabled(fAvailable);
+}
+
+void UIUSBSettingsEditor::setUsbFilters(const QList<UIDataUSBFilter> &filters)
+{
+ if (m_pEditorFilters)
+ m_pEditorFilters->setValue(filters);
+}
+
+QList<UIDataUSBFilter> UIUSBSettingsEditor::usbFilters() const
+{
+ return m_pEditorFilters ? m_pEditorFilters->value() : QList<UIDataUSBFilter>();
+}
+
+void UIUSBSettingsEditor::setUsbFiltersOptionAvailable(bool fAvailable)
+{
+ if (m_pEditorFilters)
+ m_pEditorFilters->setEnabled(fAvailable);
+}
+
+void UIUSBSettingsEditor::retranslateUi()
+{
+ if (m_pCheckboxFeature)
+ {
+ m_pCheckboxFeature->setText(tr("Enable &USB Controller"));
+ m_pCheckboxFeature->setToolTip(tr("When checked, enables the virtual USB controller of this machine."));
+ }
+}
+
+void UIUSBSettingsEditor::sltHandleFeatureToggled()
+{
+ /* Update widget availability: */
+ updateFeatureAvailability();
+}
+
+void UIUSBSettingsEditor::prepare()
+{
+ /* Prepare stuff: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Update widget availability: */
+ updateFeatureAvailability();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIUSBSettingsEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare adapter check-box: */
+ m_pCheckboxFeature = new QCheckBox(this);
+ if (m_pCheckboxFeature)
+ pLayout->addWidget(m_pCheckboxFeature, 0, 0, 1, 2);
+
+ /* Prepare 20-px shifting spacer: */
+ QSpacerItem *pSpacerItem = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacerItem)
+ pLayout->addItem(pSpacerItem, 1, 0);
+
+ /* Prepare settings widget: */
+ m_pWidgetSettings = new QWidget(this);
+ if (m_pWidgetSettings)
+ {
+ /* Prepare settings layout: */
+ QVBoxLayout *pLayoutSettings = new QVBoxLayout(m_pWidgetSettings);
+ if (pLayoutSettings)
+ {
+ pLayoutSettings->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare USB controller editor: */
+ m_pEditorController = new UIUSBControllerEditor(m_pWidgetSettings);
+ if (m_pEditorController)
+ pLayoutSettings->addWidget(m_pEditorController);
+
+ /* Prepare USB filters editor: */
+ m_pEditorFilters = new UIUSBFiltersEditor(m_pWidgetSettings);
+ if (m_pEditorFilters)
+ pLayoutSettings->addWidget(m_pEditorFilters);
+ }
+
+ pLayout->addWidget(m_pWidgetSettings, 1, 1);
+ }
+ }
+}
+
+void UIUSBSettingsEditor::prepareConnections()
+{
+ if (m_pCheckboxFeature)
+ {
+ connect(m_pCheckboxFeature, &QCheckBox::stateChanged,
+ this, &UIUSBSettingsEditor::sltHandleFeatureToggled);
+ connect(m_pCheckboxFeature, &QCheckBox::stateChanged,
+ this, &UIUSBSettingsEditor::sigValueChanged);
+ }
+ if (m_pEditorController)
+ connect(m_pEditorController, &UIUSBControllerEditor::sigValueChanged,
+ this, &UIUSBSettingsEditor::sigValueChanged);
+ if (m_pEditorFilters)
+ connect(m_pEditorFilters, &UIUSBFiltersEditor::sigValueChanged,
+ this, &UIUSBSettingsEditor::sigValueChanged);
+}
+
+void UIUSBSettingsEditor::updateFeatureAvailability()
+{
+ m_pWidgetSettings->setEnabled(m_pCheckboxFeature->isChecked());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBSettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBSettingsEditor.h
new file mode 100644
index 00000000..26646c5a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUSBSettingsEditor.h
@@ -0,0 +1,138 @@
+/* $Id: UIUSBSettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIUSBSettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIUSBSettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIUSBSettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+#include "UIUSBFiltersEditor.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class UIUSBControllerEditor;
+
+/** QWidget subclass used as a USB settings editor. */
+class SHARED_LIBRARY_STUFF UIUSBSettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value change. */
+ void sigValueChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIUSBSettingsEditor(QWidget *pParent = 0);
+
+ /** @name General stuff
+ * @{ */
+ /** Defines whether feature is @a fEnabled. */
+ void setFeatureEnabled(bool fEnabled);
+ /** Returns whether feature is enabled. */
+ bool isFeatureEnabled() const;
+
+ /** Defines whether feature @a fAvailable. */
+ void setFeatureAvailable(bool fAvailable);
+ /** @} */
+
+ /** @name USB controller editor stuff
+ * @{ */
+ /** Defines USB controller @a enmType. */
+ void setUsbControllerType(KUSBControllerType enmType);
+ /** Returns USB controller type. */
+ KUSBControllerType usbControllerType() const;
+
+ /** Defines whether USB controller option @a fAvailable. */
+ void setUsbControllerOptionAvailable(bool fAvailable);
+ /** @} */
+
+ /** @name USB filters editor stuff
+ * @{ */
+ /** Defines a list of USB @a filters. */
+ void setUsbFilters(const QList<UIDataUSBFilter> &filters);
+ /** Returns a list of USB filters. */
+ QList<UIDataUSBFilter> usbFilters() const;
+
+ /** Defines whether USB filters option @a fAvailable. */
+ void setUsbFiltersOptionAvailable(bool fAvailable);
+ /** @} */
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles feature toggling. */
+ void sltHandleFeatureToggled();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Updates feature availability. */
+ void updateFeatureAvailability();
+
+ /** @name Values
+ * @{ */
+ /** Holds whether feature is enabled. */
+ bool m_fFeatureEnabled;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the feature check-box instance. */
+ QCheckBox *m_pCheckboxFeature;
+ /** Holds the settings widget instance. */
+ QWidget *m_pWidgetSettings;
+ /** Holds the controller editor instance. */
+ UIUSBControllerEditor *m_pEditorController;
+ /** Holds the filters editor instance. */
+ UIUSBFiltersEditor *m_pEditorFilters;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIUSBSettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUpdateSettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUpdateSettingsEditor.cpp
new file mode 100644
index 00000000..5da29a7a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUpdateSettingsEditor.cpp
@@ -0,0 +1,316 @@
+/* $Id: UIUpdateSettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUpdateSettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QRadioButton>
+
+/* GUI includes: */
+#include "UIUpdateSettingsEditor.h"
+
+
+UIUpdateSettingsEditor::UIUpdateSettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pCheckBox(0)
+ , m_pWidgetUpdateSettings(0)
+ , m_pLabelUpdatePeriod(0)
+ , m_pComboUpdatePeriod(0)
+ , m_pLabelUpdateDate(0)
+ , m_pFieldUpdateDate(0)
+ , m_pLabelUpdateFilter(0)
+ , m_pRadioButtonGroup(0)
+{
+ prepare();
+}
+
+void UIUpdateSettingsEditor::setValue(const VBoxUpdateData &guiValue)
+{
+ /* Update cached value and
+ * widgets if value has changed: */
+ if (m_guiValue != guiValue)
+ {
+ m_guiValue = guiValue;
+
+ if (m_pCheckBox)
+ {
+ m_pCheckBox->setChecked(m_guiValue.isCheckEnabled());
+
+ foreach (const KUpdateChannel &enmUpdateChannel, m_mapRadioButtons.keys())
+ if (m_mapRadioButtons.value(enmUpdateChannel))
+ m_mapRadioButtons.value(enmUpdateChannel)->setVisible(
+ m_guiValue.updateChannel() == enmUpdateChannel
+ || m_guiValue.supportedUpdateChannels().contains(enmUpdateChannel));
+
+ if (m_pCheckBox->isChecked())
+ {
+ if (m_pComboUpdatePeriod)
+ m_pComboUpdatePeriod->setCurrentIndex(m_guiValue.updatePeriod());
+ if (m_mapRadioButtons.value(m_guiValue.updateChannel()))
+ m_mapRadioButtons.value(m_guiValue.updateChannel())->setChecked(true);
+ }
+
+ sltHandleUpdateToggle(m_pCheckBox->isChecked());
+ }
+ }
+}
+
+VBoxUpdateData UIUpdateSettingsEditor::value() const
+{
+ return VBoxUpdateData(isCheckEnabled(), updatePeriod(), updateChannel());
+}
+
+void UIUpdateSettingsEditor::retranslateUi()
+{
+ /* Translate check-box: */
+ if (m_pCheckBox)
+ {
+ m_pCheckBox->setToolTip(tr("When checked, the application will periodically connect to the VirtualBox "
+ "website and check whether a new VirtualBox version is available."));
+ m_pCheckBox->setText(tr("&Check for Updates"));
+ }
+
+ /* Translate period widgets: */
+ if (m_pLabelUpdatePeriod)
+ m_pLabelUpdatePeriod->setText(tr("&Once per:"));
+ if (m_pComboUpdatePeriod)
+ {
+ m_pComboUpdatePeriod->setToolTip(tr("Selects how often the new version check should be performed."));
+ const int iCurrenIndex = m_pComboUpdatePeriod->currentIndex();
+ m_pComboUpdatePeriod->clear();
+ VBoxUpdateData::populate();
+ m_pComboUpdatePeriod->insertItems(0, VBoxUpdateData::list());
+ m_pComboUpdatePeriod->setCurrentIndex(iCurrenIndex == -1 ? 0 : iCurrenIndex);
+ }
+ if (m_pLabelUpdateDate)
+ m_pLabelUpdateDate->setText(tr("Next Check:"));
+ if (m_pLabelUpdateFilter)
+ m_pLabelUpdateFilter->setText(tr("Check for:"));
+
+ /* Translate branch widgets: */
+ if (m_mapRadioButtons.value(KUpdateChannel_Stable))
+ {
+ m_mapRadioButtons.value(KUpdateChannel_Stable)->setText(tr("&Stable Release Versions"));
+ m_mapRadioButtons.value(KUpdateChannel_Stable)->setToolTip(tr("When chosen, you will be notified "
+ "about stable updates to VirtualBox."));
+ }
+ if (m_mapRadioButtons.value(KUpdateChannel_All))
+ {
+ m_mapRadioButtons.value(KUpdateChannel_All)->setText(tr("&All New Releases"));
+ m_mapRadioButtons.value(KUpdateChannel_All)->setToolTip(tr("When chosen, you will be notified "
+ "about all new VirtualBox releases."));
+ }
+ if (m_mapRadioButtons.value(KUpdateChannel_WithBetas))
+ {
+ m_mapRadioButtons.value(KUpdateChannel_WithBetas)->setText(tr("All New Releases and &Pre-Releases"));
+ m_mapRadioButtons.value(KUpdateChannel_WithBetas)->setToolTip(tr("When chosen, you will be notified "
+ "about all new VirtualBox releases and "
+ "pre-release versions of VirtualBox."));
+ }
+ if (m_mapRadioButtons.value(KUpdateChannel_WithTesting))
+ {
+ m_mapRadioButtons.value(KUpdateChannel_WithTesting)->setText(tr("All New Releases, &Pre-Releases and Testing Builds"));
+ m_mapRadioButtons.value(KUpdateChannel_WithTesting)->setToolTip(tr("When chosen, you will be notified "
+ "about all new VirtualBox releases, "
+ "pre-release versions and testing builds of "
+ "VirtualBox."));
+ }
+}
+
+void UIUpdateSettingsEditor::sltHandleUpdateToggle(bool fEnabled)
+{
+ /* Update activity status: */
+ if (m_pWidgetUpdateSettings)
+ m_pWidgetUpdateSettings->setEnabled(fEnabled);
+
+ /* Update time of next check: */
+ sltHandleUpdatePeriodChange();
+
+ /* Choose stable branch if update enabled but branch isn't chosen: */
+ if ( fEnabled
+ && m_pRadioButtonGroup
+ && !m_pRadioButtonGroup->checkedButton()
+ && m_mapRadioButtons.value(KUpdateChannel_Stable))
+ m_mapRadioButtons.value(KUpdateChannel_Stable)->setChecked(true);
+}
+
+void UIUpdateSettingsEditor::sltHandleUpdatePeriodChange()
+{
+ if (m_pFieldUpdateDate)
+ m_pFieldUpdateDate->setText(VBoxUpdateData(isCheckEnabled(), updatePeriod(), updateChannel()).dateToString());
+}
+
+void UIUpdateSettingsEditor::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIUpdateSettingsEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ pLayout->setRowStretch(2, 1);
+
+ /* Prepare update check-box: */
+ m_pCheckBox = new QCheckBox(this);
+ if (m_pCheckBox)
+ pLayout->addWidget(m_pCheckBox, 0, 0, 1, 2);
+
+ /* Prepare 20-px shifting spacer: */
+ QSpacerItem *pSpacerItem = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacerItem)
+ pLayout->addItem(pSpacerItem, 1, 0);
+
+ /* Prepare update settings widget: */
+ m_pWidgetUpdateSettings = new QWidget(this);
+ if (m_pWidgetUpdateSettings)
+ {
+ /* Prepare update settings widget layout: */
+ QGridLayout *pLayoutUpdateSettings = new QGridLayout(m_pWidgetUpdateSettings);
+ if (pLayoutUpdateSettings)
+ {
+ pLayoutUpdateSettings->setContentsMargins(0, 0, 0, 0);
+ pLayoutUpdateSettings->setColumnStretch(2, 1);
+ pLayoutUpdateSettings->setRowStretch(5, 1);
+
+ /* Prepare update period label: */
+ m_pLabelUpdatePeriod = new QLabel(m_pWidgetUpdateSettings);
+ if (m_pLabelUpdatePeriod)
+ {
+ m_pLabelUpdatePeriod->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutUpdateSettings->addWidget(m_pLabelUpdatePeriod, 0, 0);
+ }
+ /* Prepare update period combo: */
+ m_pComboUpdatePeriod = new QComboBox(m_pWidgetUpdateSettings);
+ if (m_pComboUpdatePeriod)
+ {
+ if (m_pLabelUpdatePeriod)
+ m_pLabelUpdatePeriod->setBuddy(m_pComboUpdatePeriod);
+ m_pComboUpdatePeriod->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ m_pComboUpdatePeriod->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+
+ pLayoutUpdateSettings->addWidget(m_pComboUpdatePeriod, 0, 1);
+ }
+
+ /* Prepare update date label: */
+ m_pLabelUpdateDate = new QLabel(m_pWidgetUpdateSettings);
+ if (m_pLabelUpdateDate)
+ {
+ m_pLabelUpdateDate->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutUpdateSettings->addWidget(m_pLabelUpdateDate, 1, 0);
+ }
+ /* Prepare update date field: */
+ m_pFieldUpdateDate = new QLabel(m_pWidgetUpdateSettings);
+ if (m_pFieldUpdateDate)
+ pLayoutUpdateSettings->addWidget(m_pFieldUpdateDate, 1, 1);
+
+ /* Prepare update date label: */
+ m_pLabelUpdateFilter = new QLabel(m_pWidgetUpdateSettings);
+ if (m_pLabelUpdateFilter)
+ {
+ m_pLabelUpdateFilter->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutUpdateSettings->addWidget(m_pLabelUpdateFilter, 2, 0);
+ }
+ /* Prepare radio-button group: */
+ m_pRadioButtonGroup = new QButtonGroup(m_pWidgetUpdateSettings);
+ if (m_pRadioButtonGroup)
+ {
+ /* Prepare 'update to "stable"' radio-button: */
+ m_mapRadioButtons[KUpdateChannel_Stable] = new QRadioButton(m_pWidgetUpdateSettings);
+ if (m_mapRadioButtons.value(KUpdateChannel_Stable))
+ {
+ m_mapRadioButtons.value(KUpdateChannel_Stable)->setVisible(false);
+ m_pRadioButtonGroup->addButton(m_mapRadioButtons.value(KUpdateChannel_Stable));
+ pLayoutUpdateSettings->addWidget(m_mapRadioButtons.value(KUpdateChannel_Stable), 2, 1);
+ }
+ /* Prepare 'update to "all release"' radio-button: */
+ m_mapRadioButtons[KUpdateChannel_All] = new QRadioButton(m_pWidgetUpdateSettings);
+ if (m_mapRadioButtons.value(KUpdateChannel_All))
+ {
+ m_mapRadioButtons.value(KUpdateChannel_All)->setVisible(false);
+ m_pRadioButtonGroup->addButton(m_mapRadioButtons.value(KUpdateChannel_All));
+ pLayoutUpdateSettings->addWidget(m_mapRadioButtons.value(KUpdateChannel_All), 3, 1);
+ }
+ /* Prepare 'update to "with betas"' radio-button: */
+ m_mapRadioButtons[KUpdateChannel_WithBetas] = new QRadioButton(m_pWidgetUpdateSettings);
+ if (m_mapRadioButtons.value(KUpdateChannel_WithBetas))
+ {
+ m_mapRadioButtons.value(KUpdateChannel_WithBetas)->setVisible(false);
+ m_pRadioButtonGroup->addButton(m_mapRadioButtons.value(KUpdateChannel_WithBetas));
+ pLayoutUpdateSettings->addWidget(m_mapRadioButtons.value(KUpdateChannel_WithBetas), 4, 1);
+ }
+ /* Prepare 'update to "with testing"' radio-button: */
+ m_mapRadioButtons[KUpdateChannel_WithTesting] = new QRadioButton(m_pWidgetUpdateSettings);
+ if (m_mapRadioButtons.value(KUpdateChannel_WithTesting))
+ {
+ m_mapRadioButtons.value(KUpdateChannel_WithTesting)->setVisible(false);
+ m_pRadioButtonGroup->addButton(m_mapRadioButtons.value(KUpdateChannel_WithTesting));
+ pLayoutUpdateSettings->addWidget(m_mapRadioButtons.value(KUpdateChannel_WithTesting), 5, 1);
+ }
+ }
+ }
+
+ pLayout->addWidget(m_pWidgetUpdateSettings, 1, 1);
+ }
+ }
+}
+
+void UIUpdateSettingsEditor::prepareConnections()
+{
+ if (m_pCheckBox)
+ connect(m_pCheckBox, &QCheckBox::toggled, this, &UIUpdateSettingsEditor::sltHandleUpdateToggle);
+ if (m_pComboUpdatePeriod)
+ connect(m_pComboUpdatePeriod, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
+ this, &UIUpdateSettingsEditor::sltHandleUpdatePeriodChange);
+}
+
+bool UIUpdateSettingsEditor::isCheckEnabled() const
+{
+ return m_pCheckBox ? m_pCheckBox->isChecked() : m_guiValue.isCheckEnabled();
+}
+
+UpdatePeriodType UIUpdateSettingsEditor::updatePeriod() const
+{
+ return m_pComboUpdatePeriod ? (UpdatePeriodType)m_pComboUpdatePeriod->currentIndex() : m_guiValue.updatePeriod();
+}
+
+KUpdateChannel UIUpdateSettingsEditor::updateChannel() const
+{
+ QAbstractButton *pCheckedButton = m_pRadioButtonGroup ? m_pRadioButtonGroup->checkedButton() : 0;
+ return m_mapRadioButtons.key(pCheckedButton, m_guiValue.updateChannel());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUpdateSettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUpdateSettingsEditor.h
new file mode 100644
index 00000000..c0e26d68
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIUpdateSettingsEditor.h
@@ -0,0 +1,118 @@
+/* $Id: UIUpdateSettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIUpdateSettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIUpdateSettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIUpdateSettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIUpdateDefs.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QButtonGroup;
+class QCheckBox;
+class QComboBox;
+class QLabel;
+
+/** QWidget subclass used as a update settings editor. */
+class SHARED_LIBRARY_STUFF UIUpdateSettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIUpdateSettingsEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a guiValue. */
+ void setValue(const VBoxUpdateData &guiValue);
+ /** Returns editor value. */
+ VBoxUpdateData value() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles whether update is @a fEnabled. */
+ void sltHandleUpdateToggle(bool fEnabled);
+ /** Handles update period change. */
+ void sltHandleUpdatePeriodChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Returns whether check is enabled. */
+ bool isCheckEnabled() const;
+ /** Returns update period. */
+ UpdatePeriodType updatePeriod() const;
+ /** Returns update channel. */
+ KUpdateChannel updateChannel() const;
+
+ /** Holds the value to be set. */
+ VBoxUpdateData m_guiValue;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the update check-box instance. */
+ QCheckBox *m_pCheckBox;
+ /** Holds the update settings widget instance. */
+ QWidget *m_pWidgetUpdateSettings;
+ /** Holds the update period label instance. */
+ QLabel *m_pLabelUpdatePeriod;
+ /** Holds the update period combo instance. */
+ QComboBox *m_pComboUpdatePeriod;
+ /** Holds the update date label instance. */
+ QLabel *m_pLabelUpdateDate;
+ /** Holds the update date field instance. */
+ QLabel *m_pFieldUpdateDate;
+ /** Holds the update filter label instance. */
+ QLabel *m_pLabelUpdateFilter;
+
+ /** Holds the radio button group instance. */
+ QButtonGroup *m_pRadioButtonGroup;
+ /** Holds the radio button map instance. */
+ QMap<KUpdateChannel, QAbstractButton*> m_mapRadioButtons;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIUpdateSettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDEAuthLibraryEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDEAuthLibraryEditor.cpp
new file mode 100644
index 00000000..215972d9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDEAuthLibraryEditor.cpp
@@ -0,0 +1,116 @@
+/* $Id: UIVRDEAuthLibraryEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVRDEAuthLibraryEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIFilePathSelector.h"
+#include "UIVRDEAuthLibraryEditor.h"
+
+
+UIVRDEAuthLibraryEditor::UIVRDEAuthLibraryEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_strValue(QString())
+ , m_pLabel(0)
+ , m_pSelector(0)
+{
+ prepare();
+}
+
+void UIVRDEAuthLibraryEditor::setValue(const QString &strValue)
+{
+ /* Update cached value and
+ * editor if value has changed: */
+ if (m_strValue != strValue)
+ {
+ m_strValue = strValue;
+ if (m_pSelector)
+ m_pSelector->setPath(strValue);
+ }
+}
+
+QString UIVRDEAuthLibraryEditor::value() const
+{
+ return m_pSelector ? m_pSelector->path() : m_strValue;
+}
+
+int UIVRDEAuthLibraryEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIVRDEAuthLibraryEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIVRDEAuthLibraryEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("V&RDP Authentication Library:"));
+ if (m_pSelector)
+ m_pSelector->setToolTip(tr("Holds the path to the library that provides "
+ "authentication for Remote Display (VRDP) clients."));
+}
+
+void UIVRDEAuthLibraryEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setColumnStretch(1, 1);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create selector: */
+ m_pSelector = new UIFilePathSelector(this);
+ if (m_pSelector)
+ {
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pSelector);
+ m_pSelector->setInitialPath(uiCommon().homeFolder());
+ m_pSelector->setMode(UIFilePathSelector::Mode_File_Open);
+
+ m_pLayout->addWidget(m_pSelector, 0, 1);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDEAuthLibraryEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDEAuthLibraryEditor.h
new file mode 100644
index 00000000..dc141b94
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDEAuthLibraryEditor.h
@@ -0,0 +1,87 @@
+/* $Id: UIVRDEAuthLibraryEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIVRDEAuthLibraryEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIVRDEAuthLibraryEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIVRDEAuthLibraryEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class UIFilePathSelector;
+
+/** QWidget subclass used as a VRDE auth library editor. */
+class SHARED_LIBRARY_STUFF UIVRDEAuthLibraryEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIVRDEAuthLibraryEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a strValue. */
+ void setValue(const QString &strValue);
+ /** Returns editor value. */
+ QString value() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the value to be set. */
+ QString m_strValue;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the selector instance. */
+ UIFilePathSelector *m_pSelector;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIVRDEAuthLibraryEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDESettingsEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDESettingsEditor.cpp
new file mode 100644
index 00000000..ba28c752
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDESettingsEditor.cpp
@@ -0,0 +1,359 @@
+/* $Id: UIVRDESettingsEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVRDESettingsEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QComboBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+
+/* GUI includes: */
+#include "UIConverter.h"
+#include "UIVRDESettingsEditor.h"
+
+
+UIVRDESettingsEditor::UIVRDESettingsEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fFeatureEnabled(false)
+ , m_enmAuthType(KAuthType_Max)
+ , m_fMultipleConnectionsAllowed(false)
+ , m_pCheckboxFeature(0)
+ , m_pWidgetSettings(0)
+ , m_pLabelPort(0)
+ , m_pEditorPort(0)
+ , m_pLabelAuthMethod(0)
+ , m_pComboAuthType(0)
+ , m_pLabelTimeout(0)
+ , m_pEditorTimeout(0)
+ , m_pLabelOptions(0)
+ , m_pCheckboxMultipleConnections(0)
+{
+ prepare();
+}
+
+void UIVRDESettingsEditor::setFeatureEnabled(bool fEnabled)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fFeatureEnabled != fEnabled)
+ {
+ m_fFeatureEnabled = fEnabled;
+ if (m_pCheckboxFeature)
+ {
+ m_pCheckboxFeature->setChecked(m_fFeatureEnabled);
+ sltHandleFeatureToggled(m_pCheckboxFeature->isChecked());
+ }
+ }
+}
+
+bool UIVRDESettingsEditor::isFeatureEnabled() const
+{
+ return m_pCheckboxFeature ? m_pCheckboxFeature->isChecked() : m_fFeatureEnabled;
+}
+
+void UIVRDESettingsEditor::setVRDEOptionsAvailable(bool fAvailable)
+{
+ if (m_pLabelOptions)
+ m_pLabelOptions->setEnabled(fAvailable);
+ if (m_pCheckboxMultipleConnections)
+ m_pCheckboxMultipleConnections->setEnabled(fAvailable);
+}
+
+void UIVRDESettingsEditor::setPort(const QString &strPort)
+{
+ /* Update cached value and
+ * line-edit if value has changed: */
+ if (m_strPort != strPort)
+ {
+ m_strPort = strPort;
+ if (m_pEditorPort)
+ m_pEditorPort->setText(m_strPort);
+ }
+}
+
+QString UIVRDESettingsEditor::port() const
+{
+ return m_pEditorPort ? m_pEditorPort->text() : m_strPort;
+}
+
+void UIVRDESettingsEditor::setAuthType(const KAuthType &enmType)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmAuthType != enmType)
+ {
+ m_enmAuthType = enmType;
+ repopulateComboAuthType();
+ }
+}
+
+KAuthType UIVRDESettingsEditor::authType() const
+{
+ return m_pComboAuthType ? m_pComboAuthType->currentData().value<KAuthType>() : m_enmAuthType;
+}
+
+void UIVRDESettingsEditor::setTimeout(const QString &strTimeout)
+{
+ /* Update cached value and
+ * line-edit if value has changed: */
+ if (m_strTimeout != strTimeout)
+ {
+ m_strTimeout = strTimeout;
+ if (m_pEditorTimeout)
+ m_pEditorTimeout->setText(m_strTimeout);
+ }
+}
+
+QString UIVRDESettingsEditor::timeout() const
+{
+ return m_pEditorTimeout ? m_pEditorTimeout->text() : m_strTimeout;
+}
+
+void UIVRDESettingsEditor::setMultipleConnectionsAllowed(bool fAllowed)
+{
+ /* Update cached value and
+ * check-box if value has changed: */
+ if (m_fMultipleConnectionsAllowed != fAllowed)
+ {
+ m_fMultipleConnectionsAllowed = fAllowed;
+ if (m_pCheckboxMultipleConnections)
+ m_pCheckboxMultipleConnections->setChecked(m_fMultipleConnectionsAllowed);
+ }
+}
+
+bool UIVRDESettingsEditor::isMultipleConnectionsAllowed() const
+{
+ return m_pCheckboxMultipleConnections ? m_pCheckboxMultipleConnections->isChecked() : m_fMultipleConnectionsAllowed;
+}
+
+void UIVRDESettingsEditor::retranslateUi()
+{
+ if (m_pCheckboxFeature)
+ {
+ m_pCheckboxFeature->setText(tr("&Enable Server"));
+ m_pCheckboxFeature->setToolTip(tr("When checked, the VM will act as a Remote Desktop Protocol (RDP) server, allowing "
+ "remote clients to connect and operate the VM (when it is running) using a standard "
+ "RDP client."));
+ }
+
+ if (m_pLabelPort)
+ m_pLabelPort->setText(tr("Server &Port:"));
+ if (m_pEditorPort)
+ m_pEditorPort->setToolTip(tr("Holds the VRDP Server port number. You may specify 0 (zero), to select port 3389, the "
+ "standard port for RDP."));
+
+ if (m_pLabelAuthMethod)
+ m_pLabelAuthMethod->setText(tr("Authentication &Method:"));
+ if (m_pComboAuthType)
+ {
+ for (int iIndex = 0; iIndex < m_pComboAuthType->count(); ++iIndex)
+ {
+ const KAuthType enmType = m_pComboAuthType->itemData(iIndex).value<KAuthType>();
+ m_pComboAuthType->setItemText(iIndex, gpConverter->toString(enmType));
+ }
+ m_pComboAuthType->setToolTip(tr("Selects the VRDP authentication method."));
+ }
+
+ if (m_pLabelTimeout)
+ m_pLabelTimeout->setText(tr("Authentication &Timeout:"));
+ if (m_pEditorTimeout)
+ m_pEditorTimeout->setToolTip(tr("Holds the timeout for guest authentication, in milliseconds."));
+
+ if (m_pLabelOptions)
+ m_pLabelOptions->setText(tr("Extended Features:"));
+ if (m_pCheckboxMultipleConnections)
+ {
+ m_pCheckboxMultipleConnections->setText(tr("&Allow Multiple Connections"));
+ m_pCheckboxMultipleConnections->setToolTip(tr("When checked, multiple simultaneous connections to the VM are "
+ "permitted."));
+ }
+}
+
+void UIVRDESettingsEditor::sltHandleFeatureToggled(bool fEnabled)
+{
+ /* Update widget availability: */
+ if (m_pWidgetSettings)
+ m_pWidgetSettings->setEnabled(fEnabled);
+
+ /* Notify listeners: */
+ emit sigChanged();
+}
+
+void UIVRDESettingsEditor::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIVRDESettingsEditor::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ pLayout->setColumnStretch(1, 1);
+
+ /* Prepare 'feature' check-box: */
+ m_pCheckboxFeature = new QCheckBox(this);
+ if (m_pCheckboxFeature)
+ pLayout->addWidget(m_pCheckboxFeature, 0, 0, 1, 2);
+
+ /* Prepare 20-px shifting spacer: */
+ QSpacerItem *pSpacerItem = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacerItem)
+ pLayout->addItem(pSpacerItem, 1, 0);
+
+ /* Prepare 'settings' widget: */
+ m_pWidgetSettings = new QWidget(this);
+ if (m_pWidgetSettings)
+ {
+ /* Prepare 'settings' layout: */
+ QGridLayout *pLayoutRemoteDisplaySettings = new QGridLayout(m_pWidgetSettings);
+ if (pLayoutRemoteDisplaySettings)
+ {
+ pLayoutRemoteDisplaySettings->setContentsMargins(0, 0, 0, 0);
+ pLayoutRemoteDisplaySettings->setColumnStretch(1, 1);
+
+ /* Prepare 'port' label: */
+ m_pLabelPort = new QLabel(m_pWidgetSettings);
+ if (m_pLabelPort)
+ {
+ m_pLabelPort->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutRemoteDisplaySettings->addWidget(m_pLabelPort, 0, 0);
+ }
+ /* Prepare 'port' editor: */
+ m_pEditorPort = new QLineEdit(m_pWidgetSettings);
+ if (m_pEditorPort)
+ {
+ if (m_pLabelPort)
+ m_pLabelPort->setBuddy(m_pEditorPort);
+ m_pEditorPort->setValidator(new QRegularExpressionValidator(
+ QRegularExpression("(([0-9]{1,5}(\\-[0-9]{1,5}){0,1}),)*([0-9]{1,5}(\\-[0-9]{1,5}){0,1})"), this));
+
+ pLayoutRemoteDisplaySettings->addWidget(m_pEditorPort, 0, 1, 1, 2);
+ }
+
+ /* Prepare 'auth type' label: */
+ m_pLabelAuthMethod = new QLabel(m_pWidgetSettings);
+ if (m_pLabelAuthMethod)
+ {
+ m_pLabelAuthMethod->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutRemoteDisplaySettings->addWidget(m_pLabelAuthMethod, 1, 0);
+ }
+ /* Prepare 'auth type' combo: */
+ m_pComboAuthType = new QComboBox(m_pWidgetSettings);
+ if (m_pComboAuthType)
+ {
+ if (m_pLabelAuthMethod)
+ m_pLabelAuthMethod->setBuddy(m_pComboAuthType);
+ m_pComboAuthType->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
+ pLayoutRemoteDisplaySettings->addWidget(m_pComboAuthType, 1, 1, 1, 2);
+ }
+
+ /* Prepare 'timeout' label: */
+ m_pLabelTimeout = new QLabel(m_pWidgetSettings);
+ if (m_pLabelTimeout)
+ {
+ m_pLabelTimeout->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutRemoteDisplaySettings->addWidget(m_pLabelTimeout, 2, 0);
+ }
+ /* Prepare 'timeout' editor: */
+ m_pEditorTimeout = new QLineEdit(m_pWidgetSettings);
+ if (m_pEditorTimeout)
+ {
+ if (m_pLabelTimeout)
+ m_pLabelTimeout->setBuddy(m_pEditorTimeout);
+ m_pEditorTimeout->setValidator(new QIntValidator(this));
+
+ pLayoutRemoteDisplaySettings->addWidget(m_pEditorTimeout, 2, 1, 1, 2);
+ }
+
+ /* Prepare 'options' label: */
+ m_pLabelOptions = new QLabel(m_pWidgetSettings);
+ if (m_pLabelOptions)
+ {
+ m_pLabelOptions->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutRemoteDisplaySettings->addWidget(m_pLabelOptions, 3, 0);
+ }
+ /* Prepare 'multiple connections' check-box: */
+ m_pCheckboxMultipleConnections = new QCheckBox(m_pWidgetSettings);
+ if (m_pCheckboxMultipleConnections)
+ pLayoutRemoteDisplaySettings->addWidget(m_pCheckboxMultipleConnections, 3, 1);
+ }
+
+ pLayout->addWidget(m_pWidgetSettings, 1, 1, 1, 2);
+ }
+ }
+
+ /* Update widget availability: */
+ if (m_pCheckboxFeature)
+ sltHandleFeatureToggled(m_pCheckboxFeature->isChecked());
+}
+
+void UIVRDESettingsEditor::prepareConnections()
+{
+ if (m_pCheckboxFeature)
+ connect(m_pCheckboxFeature, &QCheckBox::toggled, this, &UIVRDESettingsEditor::sltHandleFeatureToggled);
+ if (m_pEditorPort)
+ connect(m_pEditorPort, &QLineEdit::textChanged, this, &UIVRDESettingsEditor::sigChanged);
+ if (m_pEditorTimeout)
+ connect(m_pEditorTimeout, &QLineEdit::textChanged, this, &UIVRDESettingsEditor::sigChanged);
+}
+
+void UIVRDESettingsEditor::repopulateComboAuthType()
+{
+ if (m_pComboAuthType)
+ {
+ /* Clear combo first of all: */
+ m_pComboAuthType->clear();
+
+ /// @todo get supported auth types (API not implemented), not hardcoded!
+ QVector<KAuthType> authTypes = QVector<KAuthType>() << KAuthType_Null
+ << KAuthType_External
+ << KAuthType_Guest;
+
+ /* Take into account currently cached value: */
+ if (!authTypes.contains(m_enmAuthType))
+ authTypes.prepend(m_enmAuthType);
+
+ /* Populate combo finally: */
+ foreach (const KAuthType &enmType, authTypes)
+ m_pComboAuthType->addItem(gpConverter->toString(enmType), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pComboAuthType->findData(QVariant::fromValue(m_enmAuthType));
+ if (iIndex != -1)
+ m_pComboAuthType->setCurrentIndex(iIndex);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDESettingsEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDESettingsEditor.h
new file mode 100644
index 00000000..43b000fd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVRDESettingsEditor.h
@@ -0,0 +1,151 @@
+/* $Id: UIVRDESettingsEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIVRDESettingsEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIVRDESettingsEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIVRDESettingsEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QComboBox;
+class QLabel;
+class QLineEdit;
+class QWidget;
+
+/** QWidget subclass used as a VRDE settings editor. */
+class SHARED_LIBRARY_STUFF UIVRDESettingsEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notify listeners about some status changed. */
+ void sigChanged();
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIVRDESettingsEditor(QWidget *pParent = 0);
+
+ /** Defines whether feature is @a fEnabled. */
+ void setFeatureEnabled(bool fEnabled);
+ /** Returns whether feature is enabled. */
+ bool isFeatureEnabled() const;
+
+ /** Defines whether VRDE options are @a fAvailable. */
+ void setVRDEOptionsAvailable(bool fAvailable);
+
+ /** Defines @a strPort. */
+ void setPort(const QString &strPort);
+ /** Returns port. */
+ QString port() const;
+
+ /** Defines auth @a enmType. */
+ void setAuthType(const KAuthType &enmType);
+ /** Returns auth type. */
+ KAuthType authType() const;
+
+ /** Defines @a strTimeout. */
+ void setTimeout(const QString &strTimeout);
+ /** Returns timeout. */
+ QString timeout() const;
+
+ /** Defines whether multiple connections @a fAllowed. */
+ void setMultipleConnectionsAllowed(bool fAllowed);
+ /** Returns whether multiple connections allowed. */
+ bool isMultipleConnectionsAllowed() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles whether VRDE is @a fEnabled. */
+ void sltHandleFeatureToggled(bool fEnabled);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Repopulates auth type combo-box. */
+ void repopulateComboAuthType();
+
+ /** @name Values
+ * @{ */
+ /** Holds whether feature is enabled. */
+ bool m_fFeatureEnabled;
+ /** Holds the port. */
+ QString m_strPort;
+ /** Holds the auth type. */
+ KAuthType m_enmAuthType;
+ /** Holds the timeout. */
+ QString m_strTimeout;
+ /** Returns whether multiple connections allowed. */
+ bool m_fMultipleConnectionsAllowed;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the feature check-box instance. */
+ QCheckBox *m_pCheckboxFeature;
+ /** Holds the settings widget instance. */
+ QWidget *m_pWidgetSettings;
+ /** Holds the port label instance. */
+ QLabel *m_pLabelPort;
+ /** Holds the port editor instance. */
+ QLineEdit *m_pEditorPort;
+ /** Holds the port auth method label instance. */
+ QLabel *m_pLabelAuthMethod;
+ /** Holds the port auth method combo instance. */
+ QComboBox *m_pComboAuthType;
+ /** Holds the timeout label instance. */
+ QLabel *m_pLabelTimeout;
+ /** Holds the timeout editor instance. */
+ QLineEdit *m_pEditorTimeout;
+ /** Holds the options label instance. */
+ QLabel *m_pLabelOptions;
+ /** Holds the multiple connection check-box instance. */
+ QCheckBox *m_pCheckboxMultipleConnections;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIVRDESettingsEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVideoMemoryEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVideoMemoryEditor.cpp
new file mode 100644
index 00000000..4fe867b2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVideoMemoryEditor.cpp
@@ -0,0 +1,385 @@
+/* $Id: UIVideoMemoryEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVideoMemoryEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QSpinBox>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+#include "UICommon.h"
+#include "UIVideoMemoryEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIVideoMemoryEditor::UIVideoMemoryEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_iValue(0)
+ , m_comGuestOSType(CGuestOSType())
+ , m_cGuestScreenCount(1)
+ , m_enmGraphicsControllerType(KGraphicsControllerType_Null)
+#ifdef VBOX_WITH_3D_ACCELERATION
+ , m_f3DAccelerationSupported(false)
+ , m_f3DAccelerationEnabled(false)
+#endif
+ , m_iMinVRAM(0)
+ , m_iMaxVRAM(0)
+ , m_iMaxVRAMVisible(0)
+ , m_pLayout(0)
+ , m_pLabelMemory(0)
+ , m_pSlider(0)
+ , m_pLabelMemoryMin(0)
+ , m_pLabelMemoryMax(0)
+ , m_pSpinBox(0)
+{
+ prepare();
+}
+
+void UIVideoMemoryEditor::setValue(int iValue)
+{
+ /* Update cached value and
+ * slider if value has changed: */
+ if (m_iValue != iValue)
+ {
+ m_iValue = RT_MIN(iValue, m_iMaxVRAM);
+ if (m_pSlider)
+ m_pSlider->setValue(m_iValue);
+
+ /* Update requirements: */
+ updateRequirements();
+ }
+}
+
+int UIVideoMemoryEditor::value() const
+{
+ return m_pSlider ? m_pSlider->value() : m_iValue;
+}
+
+void UIVideoMemoryEditor::setGuestOSType(const CGuestOSType &comGuestOSType)
+{
+ /* Update cached value and
+ * requirements if value has changed: */
+ if (m_comGuestOSType != comGuestOSType)
+ {
+ /* Remember new guest OS type: */
+ m_comGuestOSType = comGuestOSType;
+
+ /* Update requirements: */
+ updateRequirements();
+ }
+}
+
+void UIVideoMemoryEditor::setGuestScreenCount(int cGuestScreenCount)
+{
+ /* Update cached value and
+ * requirements if value has changed: */
+ if (m_cGuestScreenCount != cGuestScreenCount)
+ {
+ /* Remember new guest screen count: */
+ m_cGuestScreenCount = cGuestScreenCount;
+
+ /* Update requirements: */
+ updateRequirements();
+ }
+}
+
+void UIVideoMemoryEditor::setGraphicsControllerType(const KGraphicsControllerType &enmGraphicsControllerType)
+{
+ /* Update cached value and
+ * requirements if value has changed: */
+ if (m_enmGraphicsControllerType != enmGraphicsControllerType)
+ {
+ /* Remember new graphics controller type: */
+ m_enmGraphicsControllerType = enmGraphicsControllerType;
+
+ /* Update requirements: */
+ updateRequirements();
+ }
+}
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+void UIVideoMemoryEditor::set3DAccelerationSupported(bool fSupported)
+{
+ /* Update cached value and
+ * requirements if value has changed: */
+ if (m_f3DAccelerationSupported != fSupported)
+ {
+ /* Remember new 3D acceleration: */
+ m_f3DAccelerationSupported = fSupported;
+
+ /* Update requirements: */
+ updateRequirements();
+ }
+}
+
+void UIVideoMemoryEditor::set3DAccelerationEnabled(bool fEnabled)
+{
+ /* Update cached value and
+ * requirements if value has changed: */
+ if (m_f3DAccelerationEnabled != fEnabled)
+ {
+ /* Remember new 3D acceleration: */
+ m_f3DAccelerationEnabled = fEnabled;
+
+ /* Update requirements: */
+ updateRequirements();
+ }
+}
+#endif /* VBOX_WITH_3D_ACCELERATION */
+
+int UIVideoMemoryEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabelMemory->minimumSizeHint().width();
+}
+
+void UIVideoMemoryEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIVideoMemoryEditor::retranslateUi()
+{
+ if (m_pLabelMemory)
+ m_pLabelMemory->setText(tr("Video &Memory:"));
+
+ const QString strToolTip(tr("Holds the amount of video memory provided to the virtual machine."));
+ if (m_pSlider)
+ m_pSlider->setToolTip(strToolTip);
+ if (m_pSpinBox)
+ {
+ m_pSpinBox->setSuffix(QString(" %1").arg(tr("MB")));
+ m_pSpinBox->setToolTip(strToolTip);
+ }
+
+ if (m_pLabelMemoryMin)
+ {
+ m_pLabelMemoryMin->setText(tr("%1 MB").arg(m_iMinVRAM));
+ m_pLabelMemoryMin->setToolTip(tr("Minimum possible video memory size."));
+ }
+ if (m_pLabelMemoryMax)
+ {
+ m_pLabelMemoryMax->setText(tr("%1 MB").arg(m_iMaxVRAMVisible));
+ m_pLabelMemoryMax->setToolTip(tr("Maximum possible video memory size."));
+ }
+}
+
+void UIVideoMemoryEditor::sltHandleSliderChange()
+{
+ /* Apply spin-box value keeping it's signals disabled: */
+ if (m_pSpinBox && m_pSlider)
+ {
+ m_pSpinBox->blockSignals(true);
+ m_pSpinBox->setValue(m_pSlider->value());
+ m_pSpinBox->blockSignals(false);
+ }
+
+ /* Revalidate to send signal to listener: */
+ revalidate();
+}
+
+void UIVideoMemoryEditor::sltHandleSpinBoxChange()
+{
+ /* Apply slider value keeping it's signals disabled: */
+ if (m_pSpinBox && m_pSlider)
+ {
+ m_pSlider->blockSignals(true);
+ m_pSlider->setValue(m_pSpinBox->value());
+ m_pSlider->blockSignals(false);
+ }
+
+ /* Revalidate to send signal to listener: */
+ revalidate();
+}
+
+void UIVideoMemoryEditor::prepare()
+{
+ /* Prepare common variables: */
+ const CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ m_iMinVRAM = comProperties.GetMinGuestVRAM();
+ m_iMaxVRAM = comProperties.GetMaxGuestVRAM();
+ m_iMaxVRAMVisible = m_iMaxVRAM;
+
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create memory label: */
+ m_pLabelMemory = new QLabel(this);
+ if (m_pLabelMemory)
+ {
+ m_pLabelMemory->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabelMemory, 0, 0);
+ }
+
+ /* Create slider layout: */
+ QVBoxLayout *pSliderLayout = new QVBoxLayout;
+ if (pSliderLayout)
+ {
+ pSliderLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create memory slider: */
+ m_pSlider = new QIAdvancedSlider(this);
+ if (m_pSlider)
+ {
+ m_pSlider->setMinimum(m_iMinVRAM);
+ m_pSlider->setMaximum(m_iMaxVRAMVisible);
+ m_pSlider->setPageStep(calculatePageStep(m_iMaxVRAMVisible));
+ m_pSlider->setSingleStep(m_pSlider->pageStep() / 4);
+ m_pSlider->setTickInterval(m_pSlider->pageStep());
+ m_pSlider->setSnappingEnabled(true);
+ m_pSlider->setErrorHint(0, 1);
+ m_pSlider->setMinimumWidth(150);
+ connect(m_pSlider, &QIAdvancedSlider::valueChanged,
+ this, &UIVideoMemoryEditor::sltHandleSliderChange);
+ pSliderLayout->addWidget(m_pSlider);
+ }
+
+ /* Create legend layout: */
+ QHBoxLayout *pLegendLayout = new QHBoxLayout;
+ if (pLegendLayout)
+ {
+ pLegendLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create min label: */
+ m_pLabelMemoryMin = new QLabel(this);
+ if (m_pLabelMemoryMin)
+ pLegendLayout->addWidget(m_pLabelMemoryMin);
+
+ /* Push labels from each other: */
+ pLegendLayout->addStretch();
+
+ /* Create max label: */
+ m_pLabelMemoryMax = new QLabel(this);
+ if (m_pLabelMemoryMax)
+ pLegendLayout->addWidget(m_pLabelMemoryMax);
+
+ /* Add legend layout to slider layout: */
+ pSliderLayout->addLayout(pLegendLayout);
+ }
+
+ /* Add slider layout to main layout: */
+ m_pLayout->addLayout(pSliderLayout, 0, 1, 2, 1);
+ }
+
+ /* Create memory spin-box: */
+ m_pSpinBox = new QSpinBox(this);
+ if (m_pSpinBox)
+ {
+ setFocusProxy(m_pSpinBox);
+ if (m_pLabelMemory)
+ m_pLabelMemory->setBuddy(m_pSpinBox);
+ m_pSpinBox->setMinimum(m_iMinVRAM);
+ m_pSpinBox->setMaximum(m_iMaxVRAMVisible);
+ connect(m_pSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIVideoMemoryEditor::sltHandleSpinBoxChange);
+ m_pLayout->addWidget(m_pSpinBox, 0, 2);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIVideoMemoryEditor::updateRequirements()
+{
+ /* Make sure guest OS type is set: */
+ if (m_comGuestOSType.isNull())
+ return;
+
+ /* Init visible maximum VRAM: */
+ m_iMaxVRAMVisible = m_cGuestScreenCount * 32;
+
+ /* Get monitors count and recommended VRAM: */
+ int iNeedMBytes = UICommon::requiredVideoMemory(m_comGuestOSType.GetId(), m_cGuestScreenCount) / _1M;
+ /* Adjust visible maximum VRAM to be no less than 128MB (if possible): */
+ if (m_iMaxVRAMVisible < 128 && m_iMaxVRAM >= 128)
+ m_iMaxVRAMVisible = 128;
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+ if (m_f3DAccelerationEnabled && m_f3DAccelerationSupported)
+ {
+ /* Adjust recommended VRAM to be no less than 128MB: */
+ iNeedMBytes = qMax(iNeedMBytes, 128);
+ /* Adjust visible maximum VRAM to be no less than 256MB (if possible): */
+ if (m_iMaxVRAMVisible < 256 && m_iMaxVRAM >= 256)
+ m_iMaxVRAMVisible = 256;
+ }
+#endif /* VBOX_WITH_3D_ACCELERATION */
+
+ /* Adjust visible maximum VRAM to be no less than initial VRAM: */
+ m_iMaxVRAMVisible = qMax(m_iMaxVRAMVisible, m_iValue);
+ /* Adjust visible maximum VRAM to be no less than recommended VRAM: */
+ m_iMaxVRAMVisible = qMax(m_iMaxVRAMVisible, iNeedMBytes);
+
+ /* Adjust recommended VRAM to be no more than actual maximum VRAM: */
+ iNeedMBytes = qMin(iNeedMBytes, m_iMaxVRAM);
+ /* Adjust visible maximum VRAM to be no more than actual maximum VRAM: */
+ m_iMaxVRAMVisible = qMin(m_iMaxVRAMVisible, m_iMaxVRAM);
+
+ if (m_pSpinBox)
+ m_pSpinBox->setMaximum(m_iMaxVRAMVisible);
+ if (m_pSlider)
+ {
+ m_pSlider->setMaximum(m_iMaxVRAMVisible);
+ m_pSlider->setPageStep(calculatePageStep(m_iMaxVRAMVisible));
+ m_pSlider->setWarningHint(1, qMin(iNeedMBytes, m_iMaxVRAMVisible));
+ m_pSlider->setOptimalHint(qMin(iNeedMBytes, m_iMaxVRAMVisible), m_iMaxVRAMVisible);
+ }
+ if (m_pLabelMemoryMax)
+ m_pLabelMemoryMax->setText(tr("%1 MB").arg(m_iMaxVRAMVisible));
+}
+
+void UIVideoMemoryEditor::revalidate()
+{
+ if (m_pSlider)
+ emit sigValidChanged( m_enmGraphicsControllerType == KGraphicsControllerType_Null
+ || m_pSlider->value() > 0);
+}
+
+/* static */
+int UIVideoMemoryEditor::calculatePageStep(int iMax)
+{
+ /* Reasonable max. number of page steps is 32. */
+ const uint uPage = ((uint)iMax + 31) / 32;
+ /* Make it a power of 2: */
+ uint uP = uPage, p2 = 0x1;
+ while ((uP >>= 1))
+ p2 <<= 1;
+ if (uPage != p2)
+ p2 <<= 1;
+ if (p2 < 4)
+ p2 = 4;
+ return (int)p2;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVideoMemoryEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVideoMemoryEditor.h
new file mode 100644
index 00000000..db1ab203
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVideoMemoryEditor.h
@@ -0,0 +1,163 @@
+/* $Id: UIVideoMemoryEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIVideoMemoryEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIVideoMemoryEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIVideoMemoryEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuestOSType.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QSpinBox;
+class QIAdvancedSlider;
+
+/** QWidget subclass used as a video memory editor. */
+class SHARED_LIBRARY_STUFF UIVideoMemoryEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about value has became @a fValid. */
+ void sigValidChanged(bool fValid);
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIVideoMemoryEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a iValue. */
+ void setValue(int iValue);
+ /** Returns editor value. */
+ int value() const;
+
+ /** Defines @a comGuestOSType. */
+ void setGuestOSType(const CGuestOSType &comGuestOSType);
+
+ /** Defines @a cGuestScreenCount. */
+ void setGuestScreenCount(int cGuestScreenCount);
+
+ /** Defines @a enmGraphicsControllerType. */
+ void setGraphicsControllerType(const KGraphicsControllerType &enmGraphicsControllerType);
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /** Defines whether 3D acceleration is @a fSupported. */
+ void set3DAccelerationSupported(bool fSupported);
+ /** Defines whether 3D acceleration is @a fEnabled. */
+ void set3DAccelerationEnabled(bool fEnabled);
+#endif
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles slider value changes. */
+ void sltHandleSliderChange();
+ /** Handles spin-box value changes. */
+ void sltHandleSpinBoxChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Update requirements. */
+ void updateRequirements();
+
+ /** Revalidates and emits validity change signal. */
+ void revalidate();
+
+ /** Calculates the reasonably sane slider page step. */
+ static int calculatePageStep(int iMax);
+
+ /** Holds the value to be selected. */
+ int m_iValue;
+
+ /** @name Options
+ * @{ */
+ /** Holds the guest OS type ID. */
+ CGuestOSType m_comGuestOSType;
+ /** Holds the guest screen count. */
+ int m_cGuestScreenCount;
+ /** Holds the graphics controller type. */
+ KGraphicsControllerType m_enmGraphicsControllerType;
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /** Holds whether 3D acceleration is supported. */
+ bool m_f3DAccelerationSupported;
+ /** Holds whether 3D acceleration is enabled. */
+ bool m_f3DAccelerationEnabled;
+#endif
+
+ /** Holds the minimum lower limit of VRAM (MiB). */
+ int m_iMinVRAM;
+ /** Holds the maximum upper limit of VRAM (MiB). */
+ int m_iMaxVRAM;
+ /** Holds the upper limit of VRAM (MiB) for this dialog.
+ * @note This value is lower than m_iMaxVRAM to save
+ * careless users from setting useless big values. */
+ int m_iMaxVRAMVisible;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the memory label instance. */
+ QLabel *m_pLabelMemory;
+ /** Holds the memory slider instance. */
+ QIAdvancedSlider *m_pSlider;
+ /** Holds minimum memory label instance. */
+ QLabel *m_pLabelMemoryMin;
+ /** Holds maximum memory label instance. */
+ QLabel *m_pLabelMemoryMax;
+ /** Holds the memory spin-box instance. */
+ QSpinBox *m_pSpinBox;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIVideoMemoryEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVirtualCPUEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVirtualCPUEditor.cpp
new file mode 100644
index 00000000..d812da2d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVirtualCPUEditor.cpp
@@ -0,0 +1,225 @@
+/* $Id: UIVirtualCPUEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVirtualCPUEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QSpinBox>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIAdvancedSlider.h"
+#include "UICommon.h"
+#include "UIVirtualCPUEditor.h"
+
+/* COM includes */
+#include "COMEnums.h"
+#include "CSystemProperties.h"
+
+UIVirtualCPUEditor::UIVirtualCPUEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_uMinVCPUCount(1)
+ , m_uMaxVCPUCount(1)
+ , m_pLabelVCPU(0)
+ , m_pSlider(0)
+ , m_pSpinBox(0)
+ , m_pLabelVCPUMin(0)
+ , m_pLabelVCPUMax(0)
+{
+ prepare();
+}
+
+int UIVirtualCPUEditor::maxVCPUCount() const
+{
+ return (int)m_uMaxVCPUCount;
+}
+
+void UIVirtualCPUEditor::setValue(int iValue)
+{
+ if (m_pSlider)
+ m_pSlider->setValue(iValue);
+}
+
+int UIVirtualCPUEditor::value() const
+{
+ return m_pSlider ? m_pSlider->value() : 0;
+}
+
+int UIVirtualCPUEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabelVCPU->minimumSizeHint().width();
+}
+
+void UIVirtualCPUEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIVirtualCPUEditor::retranslateUi()
+{
+ if (m_pLabelVCPU)
+ m_pLabelVCPU->setText(tr("&Processors:"));
+
+ QString strToolTip(tr("Holds the number of virtual CPUs in the virtual machine. You need hardware "
+ "virtualization support on your host system to use more than one virtual CPU."));
+ if (m_pSlider)
+ m_pSlider->setToolTip(strToolTip);
+ if (m_pSpinBox)
+ m_pSpinBox->setToolTip(strToolTip);
+
+ if (m_pLabelVCPUMin)
+ {
+ m_pLabelVCPUMin->setText(tr("%1 CPU", "%1 is 1 for now").arg(m_uMinVCPUCount));
+ m_pLabelVCPUMin->setToolTip(tr("Minimum possible virtual CPU count."));
+ }
+ if (m_pLabelVCPUMax)
+ {
+ m_pLabelVCPUMax->setText(tr("%1 CPUs", "%1 is host cpu count * 2 for now").arg(m_uMaxVCPUCount));
+ m_pLabelVCPUMax->setToolTip(tr("Maximum possible virtual CPU count."));
+ }
+}
+
+void UIVirtualCPUEditor::sltHandleSliderChange()
+{
+ /* Apply spin-box value keeping it's signals disabled: */
+ if (m_pSpinBox && m_pSlider)
+ {
+ m_pSpinBox->blockSignals(true);
+ m_pSpinBox->setValue(m_pSlider->value());
+ m_pSpinBox->blockSignals(false);
+ }
+
+ /* Send signal to listener: */
+ emit sigValueChanged(m_pSlider->value());
+}
+
+void UIVirtualCPUEditor::sltHandleSpinBoxChange()
+{
+ /* Apply slider value keeping it's signals disabled: */
+ if (m_pSpinBox && m_pSlider)
+ {
+ m_pSlider->blockSignals(true);
+ m_pSlider->setValue(m_pSpinBox->value());
+ m_pSlider->blockSignals(false);
+ }
+
+ /* Send signal to listener: */
+ emit sigValueChanged(m_pSpinBox->value());
+}
+
+void UIVirtualCPUEditor::prepare()
+{
+ /* Prepare common variables: */
+ const CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ const uint uHostCPUs = uiCommon().host().GetProcessorOnlineCoreCount();
+ m_uMinVCPUCount = comProperties.GetMinGuestCPUCount();
+ m_uMaxVCPUCount = qMin(2 * uHostCPUs, (uint)comProperties.GetMaxGuestCPUCount());
+
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create main label: */
+ m_pLabelVCPU = new QLabel(this);
+ if (m_pLabelVCPU)
+ {
+ m_pLabelVCPU->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabelVCPU, 0, 0);
+ }
+
+ /* Create slider layout: */
+ QVBoxLayout *pSliderLayout = new QVBoxLayout;
+ if (pSliderLayout)
+ {
+ pSliderLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create VCPU slider: */
+ m_pSlider = new QIAdvancedSlider(this);
+ if (m_pSlider)
+ {
+ m_pSlider->setMinimumWidth(150);
+ m_pSlider->setMinimum(m_uMinVCPUCount);
+ m_pSlider->setMaximum(m_uMaxVCPUCount);
+ m_pSlider->setPageStep(1);
+ m_pSlider->setSingleStep(1);
+ m_pSlider->setTickInterval(1);
+ m_pSlider->setOptimalHint(1, uHostCPUs);
+ m_pSlider->setWarningHint(uHostCPUs, m_uMaxVCPUCount);
+ connect(m_pSlider, &QIAdvancedSlider::valueChanged,
+ this, &UIVirtualCPUEditor::sltHandleSliderChange);
+ pSliderLayout->addWidget(m_pSlider);
+ }
+
+ /* Create legend layout: */
+ QHBoxLayout *pLegendLayout = new QHBoxLayout;
+ if (pLegendLayout)
+ {
+ pLegendLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create min label: */
+ m_pLabelVCPUMin = new QLabel(this);
+ if (m_pLabelVCPUMin)
+ pLegendLayout->addWidget(m_pLabelVCPUMin);
+
+ /* Push labels from each other: */
+ pLegendLayout->addStretch();
+
+ /* Create max label: */
+ m_pLabelVCPUMax = new QLabel(this);
+ if (m_pLabelVCPUMax)
+ pLegendLayout->addWidget(m_pLabelVCPUMax);
+
+ /* Add legend layout to slider layout: */
+ pSliderLayout->addLayout(pLegendLayout);
+ }
+
+ /* Add slider layout to main layout: */
+ m_pLayout->addLayout(pSliderLayout, 0, 1, 2, 1);
+ }
+
+ /* Create VCPU spin-box: */
+ m_pSpinBox = new QSpinBox(this);
+ if (m_pSpinBox)
+ {
+ setFocusProxy(m_pSpinBox);
+ if (m_pLabelVCPU)
+ m_pLabelVCPU->setBuddy(m_pSpinBox);
+ m_pSpinBox->setMinimum(m_uMinVCPUCount);
+ m_pSpinBox->setMaximum(m_uMaxVCPUCount);
+ connect(m_pSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ this, &UIVirtualCPUEditor::sltHandleSpinBoxChange);
+ m_pLayout->addWidget(m_pSpinBox, 0, 2);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVirtualCPUEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVirtualCPUEditor.h
new file mode 100644
index 00000000..ed60d79f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVirtualCPUEditor.h
@@ -0,0 +1,116 @@
+/* $Id: UIVirtualCPUEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIVirtualCPUEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIVirtualCPUEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIVirtualCPUEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QSpinBox;
+class QIAdvancedSlider;
+
+/** QWidget subclass used as a virtual CPU editor. */
+class SHARED_LIBRARY_STUFF UIVirtualCPUEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a iValue changed. */
+ void sigValueChanged(int iValue);
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIVirtualCPUEditor(QWidget *pParent = 0);
+
+ /** Returns the maximum virtual CPU count. */
+ int maxVCPUCount() const;
+
+ /** Defines editor @a iValue. */
+ void setValue(int iValue);
+ /** Returns editor value. */
+ int value() const;
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles slider value changes. */
+ void sltHandleSliderChange();
+ /** Handles spin-box value changes. */
+ void sltHandleSpinBoxChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** @name Options
+ * @{ */
+ /** Holds the minimum virtual CPU count. */
+ uint m_uMinVCPUCount;
+ /** Holds the maximum virtual CPU count. */
+ uint m_uMaxVCPUCount;
+ /** @} */
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the main label instance. */
+ QLabel *m_pLabelVCPU;
+ /** Holds the slider instance. */
+ QIAdvancedSlider *m_pSlider;
+ /** Holds the spinbox instance. */
+ QSpinBox *m_pSpinBox;
+ /** Holds the minimum label instance. */
+ QLabel *m_pLabelVCPUMin;
+ /** Holds the maximum label instance. */
+ QLabel *m_pLabelVCPUMax;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIVirtualCPUEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVisualStateEditor.cpp b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVisualStateEditor.cpp
new file mode 100644
index 00000000..a595151b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVisualStateEditor.cpp
@@ -0,0 +1,191 @@
+/* $Id: UIVisualStateEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIVisualStateEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIVisualStateEditor.h"
+
+
+UIVisualStateEditor::UIVisualStateEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_enmValue(UIVisualStateType_Invalid)
+ , m_pLayout(0)
+ , m_pLabel(0)
+ , m_pCombo(0)
+{
+ prepare();
+}
+
+void UIVisualStateEditor::setMachineId(const QUuid &uMachineId)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_uMachineId != uMachineId)
+ {
+ m_uMachineId = uMachineId;
+ populateCombo();
+ }
+}
+
+void UIVisualStateEditor::setValue(UIVisualStateType enmValue)
+{
+ /* Update cached value and
+ * combo if value has changed: */
+ if (m_enmValue != enmValue)
+ {
+ m_enmValue = enmValue;
+ populateCombo();
+ }
+}
+
+UIVisualStateType UIVisualStateEditor::value() const
+{
+ return m_pCombo ? m_pCombo->currentData().value<UIVisualStateType>() : m_enmValue;
+}
+
+int UIVisualStateEditor::minimumLabelHorizontalHint() const
+{
+ return m_pLabel ? m_pLabel->minimumSizeHint().width() : 0;
+}
+
+void UIVisualStateEditor::setMinimumLayoutIndent(int iIndent)
+{
+ if (m_pLayout)
+ m_pLayout->setColumnMinimumWidth(0, iIndent);
+}
+
+void UIVisualStateEditor::retranslateUi()
+{
+ if (m_pLabel)
+ m_pLabel->setText(tr("Visual &State:"));
+ if (m_pCombo)
+ {
+ for (int i = 0; i < m_pCombo->count(); ++i)
+ {
+ const UIVisualStateType enmType = m_pCombo->itemData(i).value<UIVisualStateType>();
+ m_pCombo->setItemText(i, gpConverter->toString(enmType));
+ }
+ m_pCombo->setToolTip(tr("Selects the visual state. If machine is running it will be applied "
+ "as soon as possible, otherwise desired one will be defined."));
+ }
+}
+
+void UIVisualStateEditor::prepare()
+{
+ /* Create main layout: */
+ m_pLayout = new QGridLayout(this);
+ if (m_pLayout)
+ {
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pLayout->addWidget(m_pLabel, 0, 0);
+ }
+
+ /* Create combo layout: */
+ QHBoxLayout *pComboLayout = new QHBoxLayout;
+ if (pComboLayout)
+ {
+ /* Create combo: */
+ m_pCombo = new QComboBox(this);
+ if (m_pCombo)
+ {
+ /* This is necessary since contents is dynamical now: */
+ m_pCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ if (m_pLabel)
+ m_pLabel->setBuddy(m_pCombo);
+ pComboLayout->addWidget(m_pCombo);
+ }
+
+ /* Add stretch: */
+ pComboLayout->addStretch();
+
+ /* Add combo-layout into main-layout: */
+ m_pLayout->addLayout(pComboLayout, 0, 1);
+ }
+ }
+
+ /* Populate combo: */
+ populateCombo();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIVisualStateEditor::populateCombo()
+{
+ if (m_pCombo)
+ {
+ /* Clear combo first of all: */
+ m_supportedValues.clear();
+ m_pCombo->clear();
+
+ /* Get possible values: */
+ QList<UIVisualStateType> possibleValues;
+ possibleValues << UIVisualStateType_Normal
+ << UIVisualStateType_Fullscreen
+ << UIVisualStateType_Seamless
+ << UIVisualStateType_Scale;
+
+ /* Load currently supported visual state types: */
+ const UIVisualStateType enmRestrictedTypes = m_uMachineId.isNull()
+ ? UIVisualStateType_Invalid
+ : gEDataManager->restrictedVisualStates(m_uMachineId);
+ foreach (const UIVisualStateType &enmPossibleValue, possibleValues)
+ if (!(enmPossibleValue & enmRestrictedTypes))
+ m_supportedValues << enmPossibleValue;
+
+ /* Make sure requested value if sane is present as well: */
+ if ( possibleValues.contains(m_enmValue)
+ && !m_supportedValues.contains(m_enmValue))
+ m_supportedValues.prepend(m_enmValue);
+
+ /* Update combo with all the supported values: */
+ foreach (const UIVisualStateType &enmType, m_supportedValues)
+ m_pCombo->addItem(QString(), QVariant::fromValue(enmType));
+
+ /* Look for proper index to choose: */
+ const int iIndex = m_pCombo->findData(QVariant::fromValue(m_enmValue));
+ if (iIndex != -1)
+ m_pCombo->setCurrentIndex(iIndex);
+
+ /* Retranslate finally: */
+ retranslateUi();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVisualStateEditor.h b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVisualStateEditor.h
new file mode 100644
index 00000000..4bbf037f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/editors/UIVisualStateEditor.h
@@ -0,0 +1,103 @@
+/* $Id: UIVisualStateEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIVisualStateEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_editors_UIVisualStateEditor_h
+#define FEQT_INCLUDED_SRC_settings_editors_UIVisualStateEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QUuid>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QComboBox;
+class QGridLayout;
+class QLabel;
+
+/** QWidget subclass used as a visual state editor. */
+class SHARED_LIBRARY_STUFF UIVisualStateEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs editor passing @a pParent to the base-class. */
+ UIVisualStateEditor(QWidget *pParent = 0);
+
+ /** Defines editor @a uMachineId. */
+ void setMachineId(const QUuid &uMachineId);
+
+ /** Defines editor @a enmValue. */
+ void setValue(UIVisualStateType enmValue);
+ /** Returns editor value. */
+ UIVisualStateType value() const;
+
+ /** Returns the vector of supported values. */
+ QVector<UIVisualStateType> supportedValues() const { return m_supportedValues; }
+
+ /** Returns minimum layout hint. */
+ int minimumLabelHorizontalHint() const;
+ /** Defines minimum layout @a iIndent. */
+ void setMinimumLayoutIndent(int iIndent);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Populates combo. */
+ void populateCombo();
+
+ /** Holds the machine id. */
+ QUuid m_uMachineId;
+
+ /** Holds the value to be selected. */
+ UIVisualStateType m_enmValue;
+
+ /** Holds the vector of supported values. */
+ QVector<UIVisualStateType> m_supportedValues;
+
+ /** Holds the main layout instance. */
+ QGridLayout *m_pLayout;
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+ /** Holds the combo instance. */
+ QComboBox *m_pCombo;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_editors_UIVisualStateEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/settings/global/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsDisplay.cpp b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsDisplay.cpp
new file mode 100644
index 00000000..b405a58e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsDisplay.cpp
@@ -0,0 +1,300 @@
+/* $Id: UIGlobalSettingsDisplay.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsDisplay class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIFontScaleEditor.h"
+#include "UIDisplayFeaturesEditor.h"
+#include "UIGlobalSettingsDisplay.h"
+#include "UIMaximumGuestScreenSizeEditor.h"
+#include "UIScaleFactorEditor.h"
+
+
+/** Global settings: Display page data structure. */
+struct UIDataSettingsGlobalDisplay
+{
+ /** Constructs data. */
+ UIDataSettingsGlobalDisplay()
+ : m_fActivateHoveredMachineWindow(false)
+ , m_fDisableHostScreenSaver(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsGlobalDisplay &other) const
+ {
+ return true
+ && (m_guiMaximumGuestScreenSizeValue == other.m_guiMaximumGuestScreenSizeValue)
+ && (m_scaleFactors == other.m_scaleFactors)
+ && (m_fActivateHoveredMachineWindow == other.m_fActivateHoveredMachineWindow)
+ && (m_fDisableHostScreenSaver == other.m_fDisableHostScreenSaver)
+ && (m_iFontScalingFactor == other.m_iFontScalingFactor)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsGlobalDisplay &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsGlobalDisplay &other) const { return !equal(other); }
+
+ /** Holds the maximum guest-screen size value. */
+ UIMaximumGuestScreenSizeValue m_guiMaximumGuestScreenSizeValue;
+ /** Holds the guest screen scale-factor. */
+ QList<double> m_scaleFactors;
+ /** Holds whether we should automatically activate machine window under the mouse cursor. */
+ bool m_fActivateHoveredMachineWindow;
+ /** Holds whether we should disable host sceen saver on a vm is running. */
+ bool m_fDisableHostScreenSaver;
+ /** Holds font scaling factor. */
+ int m_iFontScalingFactor;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIGlobalSettingsDisplay implementation. *
+*********************************************************************************************************************************/
+
+UIGlobalSettingsDisplay::UIGlobalSettingsDisplay()
+ : m_pCache(0)
+ , m_pEditorMaximumGuestScreenSize(0)
+ , m_pEditorScaleFactor(0)
+ , m_pEditorGlobalDisplayFeatures(0)
+ , m_pFontScaleEditor(0)
+{
+ prepare();
+}
+
+UIGlobalSettingsDisplay::~UIGlobalSettingsDisplay()
+{
+ cleanup();
+}
+
+bool UIGlobalSettingsDisplay::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIGlobalSettingsDisplay::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Cache old data: */
+ UIDataSettingsGlobalDisplay oldData;
+ oldData.m_guiMaximumGuestScreenSizeValue = UIMaximumGuestScreenSizeValue(gEDataManager->maxGuestResolutionPolicy(),
+ gEDataManager->maxGuestResolutionForPolicyFixed());
+ oldData.m_scaleFactors = gEDataManager->scaleFactors(UIExtraDataManager::GlobalID);
+ oldData.m_fActivateHoveredMachineWindow = gEDataManager->activateHoveredMachineWindow();
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ oldData.m_fDisableHostScreenSaver = gEDataManager->disableHostScreenSaver();
+#endif
+ oldData.m_iFontScalingFactor = gEDataManager->fontScaleFactor();
+ m_pCache->cacheInitialData(oldData);
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsDisplay::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Load old data from cache: */
+ const UIDataSettingsGlobalDisplay &oldData = m_pCache->base();
+ if (m_pEditorMaximumGuestScreenSize)
+ m_pEditorMaximumGuestScreenSize->setValue(oldData.m_guiMaximumGuestScreenSizeValue);
+ if (m_pEditorScaleFactor)
+ {
+ m_pEditorScaleFactor->setScaleFactors(oldData.m_scaleFactors);
+ m_pEditorScaleFactor->setMonitorCount(UIDesktopWidgetWatchdog::screenCount());
+ }
+ if (m_pEditorGlobalDisplayFeatures)
+ {
+ m_pEditorGlobalDisplayFeatures->setActivateOnMouseHover(oldData.m_fActivateHoveredMachineWindow);
+ m_pEditorGlobalDisplayFeatures->setDisableHostScreenSaver(oldData.m_fDisableHostScreenSaver);
+ }
+ if (m_pFontScaleEditor)
+ m_pFontScaleEditor->setFontScaleFactor(oldData.m_iFontScalingFactor);
+}
+
+void UIGlobalSettingsDisplay::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsGlobalDisplay newData = m_pCache->base();
+
+ /* Cache new data: */
+ if (m_pEditorMaximumGuestScreenSize)
+ newData.m_guiMaximumGuestScreenSizeValue = m_pEditorMaximumGuestScreenSize->value();
+ if (m_pEditorScaleFactor)
+ newData.m_scaleFactors = m_pEditorScaleFactor->scaleFactors();
+ if (m_pEditorGlobalDisplayFeatures)
+ {
+ newData.m_fActivateHoveredMachineWindow = m_pEditorGlobalDisplayFeatures->activateOnMouseHover();
+ newData.m_fDisableHostScreenSaver = m_pEditorGlobalDisplayFeatures->disableHostScreenSaver();
+ }
+ if (m_pFontScaleEditor)
+ newData.m_iFontScalingFactor = m_pFontScaleEditor->fontScaleFactor();
+ m_pCache->cacheCurrentData(newData);
+}
+
+void UIGlobalSettingsDisplay::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsDisplay::retranslateUi()
+{
+ /* These editors have own labels, but we want them to be properly layouted according to each other: */
+ int iMinimumLayoutHint = 0;
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorMaximumGuestScreenSize->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorScaleFactor->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorGlobalDisplayFeatures->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pFontScaleEditor->minimumLabelHorizontalHint());
+ m_pEditorMaximumGuestScreenSize->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorScaleFactor->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorGlobalDisplayFeatures->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pFontScaleEditor->setMinimumLayoutIndent(iMinimumLayoutHint);
+}
+
+void UIGlobalSettingsDisplay::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheGlobalDisplay;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIGlobalSettingsDisplay::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare 'maximum guest screen size' editor: */
+ m_pEditorMaximumGuestScreenSize = new UIMaximumGuestScreenSizeEditor(this);
+ if (m_pEditorMaximumGuestScreenSize)
+ pLayout->addWidget(m_pEditorMaximumGuestScreenSize);
+
+ /* Prepare 'scale-factor' editor: */
+ m_pEditorScaleFactor = new UIScaleFactorEditor(this);
+ if (m_pEditorScaleFactor)
+ pLayout->addWidget(m_pEditorScaleFactor);
+
+ /* Prepare 'global display features' editor: */
+ m_pEditorGlobalDisplayFeatures = new UIDisplayFeaturesEditor(this);
+ if (m_pEditorGlobalDisplayFeatures)
+ pLayout->addWidget(m_pEditorGlobalDisplayFeatures);
+
+ /* Prepare 'font scale' editor: */
+ m_pFontScaleEditor = new UIFontScaleEditor(this);
+ if (m_pFontScaleEditor)
+ pLayout->addWidget(m_pFontScaleEditor);
+
+ /* Add stretch to the end: */
+ pLayout->addStretch();
+ }
+}
+
+void UIGlobalSettingsDisplay::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIGlobalSettingsDisplay::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save display settings from cache: */
+ if ( fSuccess
+ && m_pCache->wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsGlobalDisplay &oldData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsGlobalDisplay &newData = m_pCache->data();
+
+ /* Save maximum guest screen size and policy: */
+ if ( fSuccess
+ && newData.m_guiMaximumGuestScreenSizeValue != oldData.m_guiMaximumGuestScreenSizeValue)
+ /* fSuccess = */ gEDataManager->setMaxGuestScreenResolution(newData.m_guiMaximumGuestScreenSizeValue.m_enmPolicy,
+ newData.m_guiMaximumGuestScreenSizeValue.m_size);
+ /* Save guest-screen scale-factor: */
+ if ( fSuccess
+ && newData.m_scaleFactors != oldData.m_scaleFactors)
+ /* fSuccess = */ gEDataManager->setScaleFactors(newData.m_scaleFactors, UIExtraDataManager::GlobalID);
+ /* Save whether hovered machine-window should be activated automatically: */
+ if ( fSuccess
+ && newData.m_fActivateHoveredMachineWindow != oldData.m_fActivateHoveredMachineWindow)
+ /* fSuccess = */ gEDataManager->setActivateHoveredMachineWindow(newData.m_fActivateHoveredMachineWindow);
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /* Save whether the host screen saver is to be disable when a vm is running: */
+ if ( fSuccess
+ && newData.m_fDisableHostScreenSaver != oldData.m_fDisableHostScreenSaver)
+ /* fSuccess = */ gEDataManager->setDisableHostScreenSaver(newData.m_fDisableHostScreenSaver);
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+ /* Save font scale factor: */
+ if ( fSuccess
+ && newData.m_iFontScalingFactor != oldData.m_iFontScalingFactor)
+ /* fSuccess = */ gEDataManager->setFontScaleFactor(newData.m_iFontScalingFactor);
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsDisplay.h b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsDisplay.h
new file mode 100644
index 00000000..e7161c4c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsDisplay.h
@@ -0,0 +1,107 @@
+/* $Id: UIGlobalSettingsDisplay.h $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsDisplay class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsDisplay_h
+#define FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsDisplay_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class UIDisplayFeaturesEditor;
+class UIFontScaleEditor;
+class UIMaximumGuestScreenSizeEditor;
+class UIScaleFactorEditor;
+struct UIDataSettingsGlobalDisplay;
+typedef UISettingsCache<UIDataSettingsGlobalDisplay> UISettingsCacheGlobalDisplay;
+
+/** Global settings: Display page. */
+class SHARED_LIBRARY_STUFF UIGlobalSettingsDisplay : public UISettingsPageGlobal
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Display settings page. */
+ UIGlobalSettingsDisplay();
+ /** Destructs Display settings page. */
+ virtual ~UIGlobalSettingsDisplay() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheGlobalDisplay *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the maximum guest screen size editor instance. */
+ UIMaximumGuestScreenSizeEditor *m_pEditorMaximumGuestScreenSize;
+ /** Holds the scale-factor editor instance. */
+ UIScaleFactorEditor *m_pEditorScaleFactor;
+ /** Holds the global display features editor instance. */
+ UIDisplayFeaturesEditor *m_pEditorGlobalDisplayFeatures;
+ /** Holds the font scale editor instance. */
+ UIFontScaleEditor *m_pFontScaleEditor;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsDisplay_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsGeneral.cpp b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsGeneral.cpp
new file mode 100644
index 00000000..44a91607
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsGeneral.cpp
@@ -0,0 +1,245 @@
+/* $Id: UIGlobalSettingsGeneral.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsGeneral class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDefaultMachineFolderEditor.h"
+#include "UIErrorString.h"
+#include "UIGlobalSettingsGeneral.h"
+#include "UIVRDEAuthLibraryEditor.h"
+
+
+/** Global settings: General page data structure. */
+struct UIDataSettingsGlobalGeneral
+{
+ /** Constructs data. */
+ UIDataSettingsGlobalGeneral()
+ : m_strDefaultMachineFolder(QString())
+ , m_strVRDEAuthLibrary(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsGlobalGeneral &other) const
+ {
+ return true
+ && (m_strDefaultMachineFolder == other.m_strDefaultMachineFolder)
+ && (m_strVRDEAuthLibrary == other.m_strVRDEAuthLibrary)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsGlobalGeneral &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsGlobalGeneral &other) const { return !equal(other); }
+
+ /** Holds the 'default machine folder' path. */
+ QString m_strDefaultMachineFolder;
+ /** Holds the 'VRDE auth library' name. */
+ QString m_strVRDEAuthLibrary;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIGlobalSettingsGeneral implementation. *
+*********************************************************************************************************************************/
+
+UIGlobalSettingsGeneral::UIGlobalSettingsGeneral()
+ : m_pCache(0)
+ , m_pEditorDefaultMachineFolder(0)
+ , m_pEditorVRDEAuthLibrary(0)
+{
+ prepare();
+}
+
+UIGlobalSettingsGeneral::~UIGlobalSettingsGeneral()
+{
+ cleanup();
+}
+
+bool UIGlobalSettingsGeneral::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIGlobalSettingsGeneral::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Cache old data: */
+ UIDataSettingsGlobalGeneral oldData;
+ oldData.m_strDefaultMachineFolder = m_properties.GetDefaultMachineFolder();
+ oldData.m_strVRDEAuthLibrary = m_properties.GetVRDEAuthLibrary();
+ m_pCache->cacheInitialData(oldData);
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsGeneral::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Load old data from cache: */
+ const UIDataSettingsGlobalGeneral &oldData = m_pCache->base();
+ if (m_pEditorDefaultMachineFolder)
+ m_pEditorDefaultMachineFolder->setValue(oldData.m_strDefaultMachineFolder);
+ if (m_pEditorVRDEAuthLibrary)
+ m_pEditorVRDEAuthLibrary->setValue(oldData.m_strVRDEAuthLibrary);
+}
+
+void UIGlobalSettingsGeneral::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsGlobalGeneral newData = m_pCache->base();
+
+ /* Cache new data: */
+ if (m_pEditorDefaultMachineFolder)
+ newData.m_strDefaultMachineFolder = m_pEditorDefaultMachineFolder->value();
+ if (m_pEditorVRDEAuthLibrary)
+ newData.m_strVRDEAuthLibrary = m_pEditorVRDEAuthLibrary->value();
+ m_pCache->cacheCurrentData(newData);
+}
+
+void UIGlobalSettingsGeneral::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsGeneral::retranslateUi()
+{
+ /* These editors have own labels, but we want them to be properly layouted according to each other: */
+ int iMinimumLayoutHint = 0;
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorDefaultMachineFolder->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorVRDEAuthLibrary->minimumLabelHorizontalHint());
+ m_pEditorDefaultMachineFolder->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorVRDEAuthLibrary->setMinimumLayoutIndent(iMinimumLayoutHint);
+}
+
+void UIGlobalSettingsGeneral::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheGlobalGeneral;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIGlobalSettingsGeneral::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare 'default machine folder' editor: */
+ m_pEditorDefaultMachineFolder = new UIDefaultMachineFolderEditor(this);
+ if (m_pEditorDefaultMachineFolder)
+ pLayout->addWidget(m_pEditorDefaultMachineFolder);
+
+ /* Prepare 'VRDE auth library' editor: */
+ m_pEditorVRDEAuthLibrary = new UIVRDEAuthLibraryEditor(this);
+ if (m_pEditorVRDEAuthLibrary)
+ pLayout->addWidget(m_pEditorVRDEAuthLibrary);
+
+ /* Add stretch to the end: */
+ pLayout->addStretch();
+ }
+}
+
+void UIGlobalSettingsGeneral::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIGlobalSettingsGeneral::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save settings from cache: */
+ if ( fSuccess
+ && m_pCache->wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsGlobalGeneral &oldData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsGlobalGeneral &newData = m_pCache->data();
+
+ /* Save 'default machine folder': */
+ if ( fSuccess
+ && newData.m_strDefaultMachineFolder != oldData.m_strDefaultMachineFolder)
+ {
+ m_properties.SetDefaultMachineFolder(newData.m_strDefaultMachineFolder);
+ fSuccess = m_properties.isOk();
+ }
+ /* Save 'VRDE auth library': */
+ if ( fSuccess
+ && newData.m_strVRDEAuthLibrary != oldData.m_strVRDEAuthLibrary)
+ {
+ m_properties.SetVRDEAuthLibrary(newData.m_strVRDEAuthLibrary);
+ fSuccess = m_properties.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_properties));
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsGeneral.h b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsGeneral.h
new file mode 100644
index 00000000..c2afee32
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsGeneral.h
@@ -0,0 +1,101 @@
+/* $Id: UIGlobalSettingsGeneral.h $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsGeneral class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsGeneral_h
+#define FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsGeneral_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class UIDefaultMachineFolderEditor;
+class UIVRDEAuthLibraryEditor;
+struct UIDataSettingsGlobalGeneral;
+typedef UISettingsCache<UIDataSettingsGlobalGeneral> UISettingsCacheGlobalGeneral;
+
+/** Global settings: General page. */
+class SHARED_LIBRARY_STUFF UIGlobalSettingsGeneral : public UISettingsPageGlobal
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs settings page. */
+ UIGlobalSettingsGeneral();
+ /** Destructs settings page. */
+ virtual ~UIGlobalSettingsGeneral() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheGlobalGeneral *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds 'default machine folder' editor instance. */
+ UIDefaultMachineFolderEditor *m_pEditorDefaultMachineFolder;
+ /** Holds 'VRDE auth library' editor instance. */
+ UIVRDEAuthLibraryEditor *m_pEditorVRDEAuthLibrary;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsGeneral_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.cpp b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.cpp
new file mode 100644
index 00000000..b6cbe219
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.cpp
@@ -0,0 +1,304 @@
+/* $Id: UIGlobalSettingsInput.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsInput class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIAutoCaptureKeyboardEditor.h"
+#include "UIExtraDataManager.h"
+#include "UIGlobalSettingsInput.h"
+#include "UIHostComboEditor.h"
+#include "UIShortcutConfigurationEditor.h"
+#include "UIShortcutPool.h"
+#include "UITranslator.h"
+
+
+/** Global settings: Input page data structure. */
+struct UIDataSettingsGlobalInput
+{
+ /** Constructs cache. */
+ UIDataSettingsGlobalInput()
+ : m_fAutoCapture(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsGlobalInput &other) const
+ {
+ return true
+ && (m_shortcuts == other.m_shortcuts)
+ && (m_fAutoCapture == other.m_fAutoCapture)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsGlobalInput &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsGlobalInput &other) const { return !equal(other); }
+
+ /** Holds the shortcut configuration list. */
+ UIShortcutConfigurationList m_shortcuts;
+ /** Holds whether the keyboard auto-capture is enabled. */
+ bool m_fAutoCapture;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIGlobalSettingsInput implementation. *
+*********************************************************************************************************************************/
+
+UIGlobalSettingsInput::UIGlobalSettingsInput()
+ : m_pCache(0)
+ , m_pEditorShortcutConfiguration(0)
+ , m_pEditorAutoCaptureKeyboard(0)
+{
+ prepare();
+}
+
+UIGlobalSettingsInput::~UIGlobalSettingsInput()
+{
+ cleanup();
+}
+
+bool UIGlobalSettingsInput::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIGlobalSettingsInput::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Cache old data: */
+ UIDataSettingsGlobalInput oldData;
+ UIShortcutConfigurationList list;
+ list << UIShortcutConfigurationItem(UIHostCombo::hostComboCacheKey(),
+ QString(),
+ tr("Host Key Combination"),
+ gEDataManager->hostKeyCombination(),
+ QString());
+ const QMap<QString, UIShortcut> &shortcuts = gShortcutPool->shortcuts();
+ const QList<QString> shortcutKeys = shortcuts.keys();
+ foreach (const QString &strShortcutKey, shortcutKeys)
+ {
+ const UIShortcut &shortcut = shortcuts.value(strShortcutKey);
+ list << UIShortcutConfigurationItem(strShortcutKey,
+ shortcut.scope(),
+ UITranslator::removeAccelMark(shortcut.description()),
+ shortcut.primaryToNativeText(),
+ shortcut.defaultSequence().toString(QKeySequence::NativeText));
+ }
+ oldData.m_shortcuts = list;
+ oldData.m_fAutoCapture = gEDataManager->autoCaptureEnabled();
+ m_pCache->cacheInitialData(oldData);
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsInput::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Load old data from cache: */
+ const UIDataSettingsGlobalInput &oldData = m_pCache->base();
+ if (m_pEditorShortcutConfiguration)
+ m_pEditorShortcutConfiguration->load(oldData.m_shortcuts);
+ if (m_pEditorAutoCaptureKeyboard)
+ m_pEditorAutoCaptureKeyboard->setValue(oldData.m_fAutoCapture);
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIGlobalSettingsInput::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsGlobalInput newData = m_pCache->base();
+
+ /* Cache new data: */
+ if (m_pEditorShortcutConfiguration)
+ m_pEditorShortcutConfiguration->save(newData.m_shortcuts);
+ if (m_pEditorAutoCaptureKeyboard)
+ newData.m_fAutoCapture = m_pEditorAutoCaptureKeyboard->value();
+ m_pCache->cacheCurrentData(newData);
+}
+
+void UIGlobalSettingsInput::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+bool UIGlobalSettingsInput::validate(QList<UIValidationMessage> &messages)
+{
+ /* Pass by default: */
+ bool fPass = true;
+
+ /* Check VirtualBox Manager page for unique shortcuts: */
+ if (!m_pEditorShortcutConfiguration->isShortcutsUniqueManager())
+ {
+ UIValidationMessage message;
+ message.first = UITranslator::removeAccelMark(m_pEditorShortcutConfiguration->tabNameManager());
+ message.second << tr("Some items have the same shortcuts assigned.");
+ messages << message;
+ fPass = false;
+ }
+
+ /* Check Virtual Runtime page for unique shortcuts: */
+ if (!m_pEditorShortcutConfiguration->isShortcutsUniqueRuntime())
+ {
+ UIValidationMessage message;
+ message.first = UITranslator::removeAccelMark(m_pEditorShortcutConfiguration->tabNameRuntime());
+ message.second << tr("Some items have the same shortcuts assigned.");
+ messages << message;
+ fPass = false;
+ }
+
+ /* Return result: */
+ return fPass;
+}
+
+void UIGlobalSettingsInput::retranslateUi()
+{
+}
+
+void UIGlobalSettingsInput::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheGlobalInput;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIGlobalSettingsInput::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare 'shortcut configuration' editor: */
+ m_pEditorShortcutConfiguration = new UIShortcutConfigurationEditor(this);
+ if (m_pEditorShortcutConfiguration)
+ pLayout->addWidget(m_pEditorShortcutConfiguration);
+
+ /* Prepare 'auto capture keyboard' editor: */
+ m_pEditorAutoCaptureKeyboard = new UIAutoCaptureKeyboardEditor(this);
+ if (m_pEditorAutoCaptureKeyboard)
+ pLayout->addWidget(m_pEditorAutoCaptureKeyboard);
+ }
+}
+
+void UIGlobalSettingsInput::prepareConnections()
+{
+ connect(m_pEditorShortcutConfiguration, &UIShortcutConfigurationEditor::sigValueChanged,
+ this, &UIGlobalSettingsInput::revalidate);
+}
+
+void UIGlobalSettingsInput::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIGlobalSettingsInput::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save settings from cache: */
+ if ( fSuccess
+ && m_pCache->wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsGlobalInput &oldData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsGlobalInput &newData = m_pCache->data();
+
+ /* Save new host-combo shortcut from cache: */
+ if (fSuccess)
+ {
+ const UIShortcutConfigurationItem fakeHostComboItem(UIHostCombo::hostComboCacheKey(), QString(), QString(), QString(), QString());
+ const int iHostComboItemBase = UIShortcutSearchFunctor<UIShortcutConfigurationItem>()(oldData.m_shortcuts, fakeHostComboItem);
+ const int iHostComboItemData = UIShortcutSearchFunctor<UIShortcutConfigurationItem>()(newData.m_shortcuts, fakeHostComboItem);
+ const QString strHostComboBase = iHostComboItemBase != -1 ? oldData.m_shortcuts.at(iHostComboItemBase).currentSequence() : QString();
+ const QString strHostComboData = iHostComboItemData != -1 ? newData.m_shortcuts.at(iHostComboItemData).currentSequence() : QString();
+ if (strHostComboData != strHostComboBase)
+ /* fSuccess = */ gEDataManager->setHostKeyCombination(strHostComboData);
+ }
+
+ /* Save other new shortcuts from cache: */
+ if (fSuccess)
+ {
+ QMap<QString, QString> sequencesBase;
+ QMap<QString, QString> sequencesData;
+ foreach (const UIShortcutConfigurationItem &item, oldData.m_shortcuts)
+ sequencesBase.insert(item.key(), item.currentSequence());
+ foreach (const UIShortcutConfigurationItem &item, newData.m_shortcuts)
+ sequencesData.insert(item.key(), item.currentSequence());
+ if (sequencesData != sequencesBase)
+ /* fSuccess = */ gShortcutPool->setOverrides(sequencesData);
+ }
+
+ /* Save other new things from cache: */
+ if ( fSuccess
+ && newData.m_fAutoCapture != oldData.m_fAutoCapture)
+ /* fSuccess = */ gEDataManager->setAutoCaptureEnabled(newData.m_fAutoCapture);
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.h b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.h
new file mode 100644
index 00000000..d488c5f4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.h
@@ -0,0 +1,106 @@
+/* $Id: UIGlobalSettingsInput.h $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsInput class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsInput_h
+#define FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsInput_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class UIAutoCaptureKeyboardEditor;
+class UIShortcutConfigurationEditor;
+struct UIDataSettingsGlobalInput;
+typedef UISettingsCache<UIDataSettingsGlobalInput> UISettingsCacheGlobalInput;
+
+/** Global settings: Input page. */
+class SHARED_LIBRARY_STUFF UIGlobalSettingsInput : public UISettingsPageGlobal
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs settings page. */
+ UIGlobalSettingsInput();
+ /** Destructs settings page. */
+ virtual ~UIGlobalSettingsInput() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ virtual bool validate(QList<UIValidationMessage> &messages) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheGlobalInput *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the 'shortcut configuration' editor instance. */
+ UIShortcutConfigurationEditor *m_pEditorShortcutConfiguration;
+ /** Holds the 'auto capture keyboard' editor instance. */
+ UIAutoCaptureKeyboardEditor *m_pEditorAutoCaptureKeyboard;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsInput_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInterface.cpp b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInterface.cpp
new file mode 100644
index 00000000..a8bc7a24
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInterface.cpp
@@ -0,0 +1,211 @@
+/* $Id: UIGlobalSettingsInterface.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsInterface class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIColorThemeEditor.h"
+#include "UIExtraDataManager.h"
+#include "UIGlobalSettingsInterface.h"
+
+
+/** Global settings: User Interface page data structure. */
+struct UIDataSettingsGlobalInterface
+{
+ /** Constructs data. */
+ UIDataSettingsGlobalInterface()
+ : m_enmColorTheme(UIColorThemeType_Auto)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsGlobalInterface &other) const
+ {
+ return true
+ && (m_enmColorTheme == other.m_enmColorTheme)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsGlobalInterface &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsGlobalInterface &other) const { return !equal(other); }
+
+ /** Holds the color-theme. */
+ UIColorThemeType m_enmColorTheme;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIGlobalSettingsInterface implementation. *
+*********************************************************************************************************************************/
+
+UIGlobalSettingsInterface::UIGlobalSettingsInterface()
+ : m_pCache(0)
+ , m_pEditorColorTheme(0)
+{
+ prepare();
+}
+
+UIGlobalSettingsInterface::~UIGlobalSettingsInterface()
+{
+ cleanup();
+}
+
+bool UIGlobalSettingsInterface::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIGlobalSettingsInterface::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Cache old data: */
+ UIDataSettingsGlobalInterface oldData;
+ oldData.m_enmColorTheme = gEDataManager->colorTheme();
+ m_pCache->cacheInitialData(oldData);
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsInterface::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Load old data from cache: */
+ const UIDataSettingsGlobalInterface &oldData = m_pCache->base();
+ if (m_pEditorColorTheme)
+ m_pEditorColorTheme->setValue(oldData.m_enmColorTheme);
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIGlobalSettingsInterface::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsGlobalInterface newData;
+
+ /* Cache new data: */
+ if (m_pEditorColorTheme)
+ newData.m_enmColorTheme = m_pEditorColorTheme->value();
+ m_pCache->cacheCurrentData(newData);
+}
+
+void UIGlobalSettingsInterface::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsInterface::retranslateUi()
+{
+}
+
+void UIGlobalSettingsInterface::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheGlobalInterface;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIGlobalSettingsInterface::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare 'color-theme' editor: */
+ m_pEditorColorTheme = new UIColorThemeEditor(this);
+ if (m_pEditorColorTheme)
+ pLayout->addWidget(m_pEditorColorTheme);
+
+ /* Add stretch to the end: */
+ pLayout->addStretch();
+ }
+}
+
+void UIGlobalSettingsInterface::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIGlobalSettingsInterface::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save settings from cache: */
+ if ( fSuccess
+ && m_pCache->wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsGlobalInterface &oldData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsGlobalInterface &newData = m_pCache->data();
+
+ /* Save 'color-theme': */
+ if ( fSuccess
+ && newData.m_enmColorTheme != oldData.m_enmColorTheme)
+ /* fSuccess = */ gEDataManager->setColorTheme(newData.m_enmColorTheme);
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInterface.h b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInterface.h
new file mode 100644
index 00000000..1fc29448
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInterface.h
@@ -0,0 +1,98 @@
+/* $Id: UIGlobalSettingsInterface.h $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsInterface class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsInterface_h
+#define FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsInterface_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class UIColorThemeEditor;
+struct UIDataSettingsGlobalInterface;
+typedef UISettingsCache<UIDataSettingsGlobalInterface> UISettingsCacheGlobalInterface;
+
+/** Global settings: User Interface page. */
+class SHARED_LIBRARY_STUFF UIGlobalSettingsInterface : public UISettingsPageGlobal
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs User Interface settings page. */
+ UIGlobalSettingsInterface();
+ /** Destructs User Interface settings page. */
+ virtual ~UIGlobalSettingsInterface() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheGlobalInterface *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the color theme label instance. */
+ UIColorThemeEditor *m_pEditorColorTheme;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsInterface_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsLanguage.cpp b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsLanguage.cpp
new file mode 100644
index 00000000..468182fb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsLanguage.cpp
@@ -0,0 +1,205 @@
+/* $Id: UIGlobalSettingsLanguage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsLanguage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIExtraDataManager.h"
+#include "UIGlobalSettingsLanguage.h"
+#include "UILanguageSettingsEditor.h"
+
+
+/** Global settings: Language page data structure. */
+struct UIDataSettingsGlobalLanguage
+{
+ /** Constructs data. */
+ UIDataSettingsGlobalLanguage()
+ : m_strLanguageId(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsGlobalLanguage &other) const
+ {
+ return true
+ && (m_strLanguageId == other.m_strLanguageId)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsGlobalLanguage &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsGlobalLanguage &other) const { return !equal(other); }
+
+ /** Holds the current language id. */
+ QString m_strLanguageId;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIGlobalSettingsLanguage implementation. *
+*********************************************************************************************************************************/
+
+UIGlobalSettingsLanguage::UIGlobalSettingsLanguage()
+ : m_pCache(0)
+ , m_pEditorLanguageSettings(0)
+{
+ prepare();
+}
+
+UIGlobalSettingsLanguage::~UIGlobalSettingsLanguage()
+{
+ cleanup();
+}
+
+bool UIGlobalSettingsLanguage::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIGlobalSettingsLanguage::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Cache old data: */
+ UIDataSettingsGlobalLanguage oldData;
+ oldData.m_strLanguageId = gEDataManager->languageId();
+ m_pCache->cacheInitialData(oldData);
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsLanguage::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Load old data from cache: */
+ const UIDataSettingsGlobalLanguage &oldData = m_pCache->base();
+ if (m_pEditorLanguageSettings)
+ m_pEditorLanguageSettings->setValue(oldData.m_strLanguageId);
+}
+
+void UIGlobalSettingsLanguage::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsGlobalLanguage newData = m_pCache->base();
+
+ /* Cache new data: */
+ if (m_pEditorLanguageSettings)
+ newData.m_strLanguageId = m_pEditorLanguageSettings->value();
+ m_pCache->cacheCurrentData(newData);
+}
+
+void UIGlobalSettingsLanguage::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsLanguage::retranslateUi()
+{
+}
+
+void UIGlobalSettingsLanguage::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheGlobalLanguage;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIGlobalSettingsLanguage::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare 'language settings' editor: */
+ m_pEditorLanguageSettings = new UILanguageSettingsEditor(this);
+ if (m_pEditorLanguageSettings)
+ pLayout->addWidget(m_pEditorLanguageSettings);
+ }
+}
+
+void UIGlobalSettingsLanguage::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIGlobalSettingsLanguage::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save settings from cache: */
+ if ( fSuccess
+ && m_pCache->wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsGlobalLanguage &oldData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsGlobalLanguage &newData = m_pCache->data();
+
+ /* Save new data from cache: */
+ if ( fSuccess
+ && newData.m_strLanguageId != oldData.m_strLanguageId)
+ /* fSuccess = */ gEDataManager->setLanguageId(newData.m_strLanguageId);
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsLanguage.h b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsLanguage.h
new file mode 100644
index 00000000..69fac3d6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsLanguage.h
@@ -0,0 +1,98 @@
+/* $Id: UIGlobalSettingsLanguage.h $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsLanguage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsLanguage_h
+#define FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsLanguage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declartions: */
+class UILanguageSettingsEditor;
+struct UIDataSettingsGlobalLanguage;
+typedef UISettingsCache<UIDataSettingsGlobalLanguage> UISettingsCacheGlobalLanguage;
+
+/** Global settings: Language page. */
+class SHARED_LIBRARY_STUFF UIGlobalSettingsLanguage : public UISettingsPageGlobal
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Language settings page. */
+ UIGlobalSettingsLanguage();
+ /** Destructs Language settings page. */
+ virtual ~UIGlobalSettingsLanguage() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheGlobalLanguage *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the language settings editor instance. */
+ UILanguageSettingsEditor *m_pEditorLanguageSettings;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsLanguage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsProxy.cpp b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsProxy.cpp
new file mode 100644
index 00000000..280fe2a6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsProxy.cpp
@@ -0,0 +1,300 @@
+/* $Id: UIGlobalSettingsProxy.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsProxy class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIGlobalSettingsProxy.h"
+#include "UIErrorString.h"
+#include "UIExtraDataManager.h"
+#include "UIProxyFeaturesEditor.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+/** Global settings: Proxy page data structure. */
+struct UIDataSettingsGlobalProxy
+{
+ /** Constructs data. */
+ UIDataSettingsGlobalProxy()
+ : m_enmProxyMode(KProxyMode_System)
+ , m_strProxyHost(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsGlobalProxy &other) const
+ {
+ return true
+ && (m_enmProxyMode == other.m_enmProxyMode)
+ && (m_strProxyHost == other.m_strProxyHost)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsGlobalProxy &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsGlobalProxy &other) const { return !equal(other); }
+
+ /** Holds the proxy mode. */
+ KProxyMode m_enmProxyMode;
+ /** Holds the proxy host. */
+ QString m_strProxyHost;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIGlobalSettingsProxy implementation. *
+*********************************************************************************************************************************/
+
+UIGlobalSettingsProxy::UIGlobalSettingsProxy()
+ : m_pCache(0)
+{
+ prepare();
+}
+
+UIGlobalSettingsProxy::~UIGlobalSettingsProxy()
+{
+ cleanup();
+}
+
+bool UIGlobalSettingsProxy::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIGlobalSettingsProxy::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Cache old data: */
+ UIDataSettingsGlobalProxy oldData;
+ oldData.m_enmProxyMode = m_properties.GetProxyMode();
+ oldData.m_strProxyHost = m_properties.GetProxyURL();
+ m_pCache->cacheInitialData(oldData);
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsProxy::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Load old data from cache: */
+ const UIDataSettingsGlobalProxy &oldData = m_pCache->base();
+ if (m_pEditorProxyFeatures)
+ {
+ m_pEditorProxyFeatures->setProxyMode(oldData.m_enmProxyMode);
+ m_pEditorProxyFeatures->setProxyHost(oldData.m_strProxyHost);
+ }
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIGlobalSettingsProxy::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsGlobalProxy newData = m_pCache->base();
+
+ /* Cache new data: */
+ if (m_pEditorProxyFeatures)
+ {
+ newData.m_enmProxyMode = m_pEditorProxyFeatures->proxyMode();
+ newData.m_strProxyHost = m_pEditorProxyFeatures->proxyHost();
+ }
+ m_pCache->cacheCurrentData(newData);
+}
+
+void UIGlobalSettingsProxy::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+bool UIGlobalSettingsProxy::validate(QList<UIValidationMessage> &messages)
+{
+ /* Pass if proxy is disabled: */
+ if (m_pEditorProxyFeatures->proxyMode() != KProxyMode_Manual)
+ return true;
+
+ /* Pass by default: */
+ bool fPass = true;
+
+ /* Prepare message: */
+ UIValidationMessage message;
+
+ /* Check for URL presence: */
+ if (m_pEditorProxyFeatures->proxyHost().trimmed().isEmpty())
+ {
+ message.second << tr("No proxy URL is currently specified.");
+ fPass = false;
+ }
+
+ else
+
+ /* Check for URL validness: */
+ if (!QUrl(m_pEditorProxyFeatures->proxyHost().trimmed()).isValid())
+ {
+ message.second << tr("Invalid proxy URL is currently specified.");
+ fPass = true;
+ }
+
+ else
+
+ /* Check for password presence: */
+ if (!QUrl(m_pEditorProxyFeatures->proxyHost().trimmed()).password().isEmpty())
+ {
+ message.second << tr("You have provided a proxy password. "
+ "Please be aware that the password will be saved in plain text. "
+ "You may wish to configure a system-wide proxy instead and not "
+ "store application-specific settings.");
+ fPass = true;
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+
+ /* Return result: */
+ return fPass;
+}
+
+void UIGlobalSettingsProxy::retranslateUi()
+{
+}
+
+void UIGlobalSettingsProxy::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheGlobalProxy;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIGlobalSettingsProxy::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare 'proxy features' editor: */
+ m_pEditorProxyFeatures = new UIProxyFeaturesEditor(this);
+ if (m_pEditorProxyFeatures)
+ pLayout->addWidget(m_pEditorProxyFeatures);
+
+ /* Add stretch to the end: */
+ pLayout->addStretch();
+ }
+}
+
+void UIGlobalSettingsProxy::prepareConnections()
+{
+ connect(m_pEditorProxyFeatures, &UIProxyFeaturesEditor::sigProxyModeChanged,
+ this, &UIGlobalSettingsProxy::revalidate);
+ connect(m_pEditorProxyFeatures, &UIProxyFeaturesEditor::sigProxyHostChanged,
+ this, &UIGlobalSettingsProxy::revalidate);
+}
+
+void UIGlobalSettingsProxy::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIGlobalSettingsProxy::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save settings from cache: */
+ if ( fSuccess
+ && m_pCache->wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsGlobalProxy &oldData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsGlobalProxy &newData = m_pCache->data();
+
+ /* Save new data from cache: */
+ if ( fSuccess
+ && newData.m_enmProxyMode != oldData.m_enmProxyMode)
+ {
+ m_properties.SetProxyMode(newData.m_enmProxyMode);
+ fSuccess &= m_properties.isOk();
+ }
+ if ( fSuccess
+ && newData.m_strProxyHost != oldData.m_strProxyHost)
+ {
+ m_properties.SetProxyURL(newData.m_strProxyHost);
+ fSuccess &= m_properties.isOk();
+ }
+
+ /* Drop the old extra data setting if still around: */
+ if ( fSuccess
+ && !gEDataManager->proxySettings().isEmpty())
+ /* fSuccess = */ gEDataManager->setProxySettings(QString());
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_properties));
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsProxy.h b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsProxy.h
new file mode 100644
index 00000000..7d5f2eea
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsProxy.h
@@ -0,0 +1,104 @@
+/* $Id: UIGlobalSettingsProxy.h $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsProxy class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsProxy_h
+#define FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsProxy_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+#include "VBoxUtils.h"
+
+/* Forward declarations: */
+class UIProxyFeaturesEditor;
+struct UIDataSettingsGlobalProxy;
+typedef UISettingsCache<UIDataSettingsGlobalProxy> UISettingsCacheGlobalProxy;
+
+/** Global settings: Proxy page. */
+class SHARED_LIBRARY_STUFF UIGlobalSettingsProxy : public UISettingsPageGlobal
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Proxy settings page. */
+ UIGlobalSettingsProxy();
+ /** Destructs Proxy settings page. */
+ virtual ~UIGlobalSettingsProxy() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ virtual bool validate(QList<UIValidationMessage> &messages) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares wıdgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheGlobalProxy *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the proxy features editor instance. */
+ UIProxyFeaturesEditor *m_pEditorProxyFeatures;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsProxy_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsUpdate.cpp b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsUpdate.cpp
new file mode 100644
index 00000000..88214ad5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsUpdate.cpp
@@ -0,0 +1,217 @@
+/* $Id: UIGlobalSettingsUpdate.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsUpdate class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIExtraDataManager.h"
+#include "UIGlobalSettingsUpdate.h"
+#include "UIUpdateSettingsEditor.h"
+
+
+/** Global settings: Update page data structure. */
+struct UIDataSettingsGlobalUpdate
+{
+ /** Constructs data. */
+ UIDataSettingsGlobalUpdate()
+ : m_guiUpdateData(VBoxUpdateData())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsGlobalUpdate &other) const
+ {
+ return true
+ && (m_guiUpdateData == other.m_guiUpdateData)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsGlobalUpdate &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsGlobalUpdate &other) const { return !equal(other); }
+
+ /** Holds VBox update data. */
+ VBoxUpdateData m_guiUpdateData;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIGlobalSettingsUpdate implementation. *
+*********************************************************************************************************************************/
+
+UIGlobalSettingsUpdate::UIGlobalSettingsUpdate()
+ : m_pCache(0)
+ , m_pEditorUpdateSettings(0)
+{
+ prepare();
+}
+
+UIGlobalSettingsUpdate::~UIGlobalSettingsUpdate()
+{
+ cleanup();
+}
+
+bool UIGlobalSettingsUpdate::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIGlobalSettingsUpdate::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Cache old data: */
+ UIDataSettingsGlobalUpdate oldData;
+ VBoxUpdateData guiUpdateData;
+ /* Load old data from host: */
+ guiUpdateData.load(m_host);
+ oldData.m_guiUpdateData = guiUpdateData;
+ m_pCache->cacheInitialData(oldData);
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsUpdate::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Load old data from cache: */
+ const UIDataSettingsGlobalUpdate &oldData = m_pCache->base();
+ if (m_pEditorUpdateSettings)
+ m_pEditorUpdateSettings->setValue(oldData.m_guiUpdateData);
+}
+
+void UIGlobalSettingsUpdate::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsGlobalUpdate newData = m_pCache->base();
+
+ /* Cache new data: */
+ if (m_pEditorUpdateSettings)
+ newData.m_guiUpdateData = m_pEditorUpdateSettings->value();
+ m_pCache->cacheCurrentData(newData);
+}
+
+void UIGlobalSettingsUpdate::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to properties: */
+ UISettingsPageGlobal::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload properties to data: */
+ UISettingsPageGlobal::uploadData(data);
+}
+
+void UIGlobalSettingsUpdate::retranslateUi()
+{
+}
+
+void UIGlobalSettingsUpdate::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheGlobalUpdate;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIGlobalSettingsUpdate::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare 'update settings' editor: */
+ m_pEditorUpdateSettings = new UIUpdateSettingsEditor(this);
+ if (m_pEditorUpdateSettings)
+ pLayout->addWidget(m_pEditorUpdateSettings);
+
+ /* Add stretch to the end: */
+ pLayout->addStretch();
+ }
+}
+
+void UIGlobalSettingsUpdate::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIGlobalSettingsUpdate::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save update settings from cache: */
+ if ( fSuccess
+ && m_pCache->wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsGlobalUpdate &oldData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsGlobalUpdate &newData = m_pCache->data();
+
+ /* Save new data from cache: */
+ if ( fSuccess
+ && newData != oldData)
+ {
+ /* We still prefer data to be saved to extra-data as well, for backward compartibility: */
+ /* fSuccess = */ gEDataManager->setApplicationUpdateData(newData.m_guiUpdateData.data());
+ /* Save new data to host finally: */
+ const VBoxUpdateData guiUpdateData = newData.m_guiUpdateData;
+ fSuccess = guiUpdateData.save(m_host);
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsUpdate.h b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsUpdate.h
new file mode 100644
index 00000000..d91bb9a8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsUpdate.h
@@ -0,0 +1,99 @@
+/* $Id: UIGlobalSettingsUpdate.h $ */
+/** @file
+ * VBox Qt GUI - UIGlobalSettingsUpdate class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsUpdate_h
+#define FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsUpdate_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+#include "UIUpdateDefs.h"
+
+/* Forward declarations: */
+class UIUpdateSettingsEditor;
+struct UIDataSettingsGlobalUpdate;
+typedef UISettingsCache<UIDataSettingsGlobalUpdate> UISettingsCacheGlobalUpdate;
+
+/** Global settings: Update page. */
+class SHARED_LIBRARY_STUFF UIGlobalSettingsUpdate : public UISettingsPageGlobal
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs settings page. */
+ UIGlobalSettingsUpdate();
+ /** Destructs settings page. */
+ virtual ~UIGlobalSettingsUpdate() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheGlobalUpdate *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the update settings editor instance. */
+ UIUpdateSettingsEditor *m_pEditorUpdateSettings;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_global_UIGlobalSettingsUpdate_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/settings/machine/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsAudio.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsAudio.cpp
new file mode 100644
index 00000000..8a189f28
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsAudio.cpp
@@ -0,0 +1,312 @@
+/* $Id: UIMachineSettingsAudio.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsAudio class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIAudioSettingsEditor.h"
+#include "UIErrorString.h"
+#include "UIMachineSettingsAudio.h"
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CAudioSettings.h"
+
+
+/** Machine settings: Audio page data structure. */
+struct UIDataSettingsMachineAudio
+{
+ /** Constructs data. */
+ UIDataSettingsMachineAudio()
+ : m_fAudioEnabled(false)
+ , m_audioDriverType(KAudioDriverType_Null)
+ , m_audioControllerType(KAudioControllerType_AC97)
+ , m_fAudioOutputEnabled(false)
+ , m_fAudioInputEnabled(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineAudio &other) const
+ {
+ return true
+ && (m_fAudioEnabled == other.m_fAudioEnabled)
+ && (m_audioDriverType == other.m_audioDriverType)
+ && (m_audioControllerType == other.m_audioControllerType)
+ && (m_fAudioOutputEnabled == other.m_fAudioOutputEnabled)
+ && (m_fAudioInputEnabled == other.m_fAudioInputEnabled)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineAudio &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineAudio &other) const { return !equal(other); }
+
+ /** Holds whether the audio is enabled. */
+ bool m_fAudioEnabled;
+ /** Holds the audio driver type. */
+ KAudioDriverType m_audioDriverType;
+ /** Holds the audio controller type. */
+ KAudioControllerType m_audioControllerType;
+ /** Holds whether the audio output is enabled. */
+ bool m_fAudioOutputEnabled;
+ /** Holds whether the audio input is enabled. */
+ bool m_fAudioInputEnabled;
+};
+
+
+UIMachineSettingsAudio::UIMachineSettingsAudio()
+ : m_pCache(0)
+ , m_pEditorAudioSettings(0)
+{
+ prepare();
+}
+
+UIMachineSettingsAudio::~UIMachineSettingsAudio()
+{
+ cleanup();
+}
+
+bool UIMachineSettingsAudio::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsAudio::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Prepare old data: */
+ UIDataSettingsMachineAudio oldAudioData;
+
+ /* Check whether adapter is valid: */
+ const CAudioSettings &comAudioSettings = m_machine.GetAudioSettings();
+ const CAudioAdapter &comAdapter = comAudioSettings.GetAdapter();
+ if (!comAdapter.isNull())
+ {
+ /* Gather old data: */
+ oldAudioData.m_fAudioEnabled = comAdapter.GetEnabled();
+ oldAudioData.m_audioDriverType = comAdapter.GetAudioDriver();
+ oldAudioData.m_audioControllerType = comAdapter.GetAudioController();
+ oldAudioData.m_fAudioOutputEnabled = comAdapter.GetEnabledOut();
+ oldAudioData.m_fAudioInputEnabled = comAdapter.GetEnabledIn();
+ }
+
+ /* Cache old data: */
+ m_pCache->cacheInitialData(oldAudioData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsAudio::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Get old data from cache: */
+ const UIDataSettingsMachineAudio &oldAudioData = m_pCache->base();
+
+ /* Load old data from cache: */
+ if (m_pEditorAudioSettings)
+ {
+ m_pEditorAudioSettings->setFeatureEnabled(oldAudioData.m_fAudioEnabled);
+ m_pEditorAudioSettings->setHostDriverType(oldAudioData.m_audioDriverType);
+ m_pEditorAudioSettings->setControllerType(oldAudioData.m_audioControllerType);
+ m_pEditorAudioSettings->setEnableOutput(oldAudioData.m_fAudioOutputEnabled);
+ m_pEditorAudioSettings->setEnableInput(oldAudioData.m_fAudioInputEnabled);
+ }
+
+ /* Polish page finally: */
+ polishPage();
+}
+
+void UIMachineSettingsAudio::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsMachineAudio newAudioData;
+
+ /* Cache new data: */
+ if (m_pEditorAudioSettings)
+ {
+ newAudioData.m_fAudioEnabled = m_pEditorAudioSettings->isFeatureEnabled();
+ newAudioData.m_audioDriverType = m_pEditorAudioSettings->hostDriverType();
+ newAudioData.m_audioControllerType = m_pEditorAudioSettings->controllerType();
+ newAudioData.m_fAudioOutputEnabled = m_pEditorAudioSettings->outputEnabled();
+ newAudioData.m_fAudioInputEnabled = m_pEditorAudioSettings->inputEnabled();
+ }
+ m_pCache->cacheCurrentData(newAudioData);
+}
+
+void UIMachineSettingsAudio::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsAudio::retranslateUi()
+{
+}
+
+void UIMachineSettingsAudio::polishPage()
+{
+ /* Polish audio page availability: */
+ if (m_pEditorAudioSettings)
+ {
+ m_pEditorAudioSettings->setFeatureAvailable(isMachineOffline());
+ m_pEditorAudioSettings->setHostDriverOptionAvailable(isMachineOffline() || isMachineSaved());
+ m_pEditorAudioSettings->setControllerOptionAvailable(isMachineOffline());
+ m_pEditorAudioSettings->setFeatureOptionsAvailable(isMachineInValidMode());
+ }
+}
+
+void UIMachineSettingsAudio::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheMachineAudio;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsAudio::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare settings editor: */
+ m_pEditorAudioSettings = new UIAudioSettingsEditor(this);
+ if (m_pEditorAudioSettings)
+ pLayout->addWidget(m_pEditorAudioSettings);
+
+ pLayout->addStretch();
+ }
+}
+
+void UIMachineSettingsAudio::prepareConnections()
+{
+}
+
+void UIMachineSettingsAudio::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIMachineSettingsAudio::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save audio settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineAudio &oldAudioData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineAudio &newAudioData = m_pCache->data();
+
+ /* Get audio adapter for further activities: */
+ const CAudioSettings comAudioSettings = m_machine.GetAudioSettings();
+
+ CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ fSuccess = m_machine.isOk() && comAdapter.isNotNull();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ else
+ {
+ /* Save whether audio is enabled: */
+ if (fSuccess && isMachineOffline() && newAudioData.m_fAudioEnabled != oldAudioData.m_fAudioEnabled)
+ {
+ comAdapter.SetEnabled(newAudioData.m_fAudioEnabled);
+ fSuccess = comAdapter.isOk();
+ }
+ /* Save audio driver type: */
+ if (fSuccess && (isMachineOffline() || isMachineSaved()) && newAudioData.m_audioDriverType != oldAudioData.m_audioDriverType)
+ {
+ comAdapter.SetAudioDriver(newAudioData.m_audioDriverType);
+ fSuccess = comAdapter.isOk();
+ }
+ /* Save audio controller type: */
+ if (fSuccess && isMachineOffline() && newAudioData.m_audioControllerType != oldAudioData.m_audioControllerType)
+ {
+ comAdapter.SetAudioController(newAudioData.m_audioControllerType);
+ fSuccess = comAdapter.isOk();
+ }
+ /* Save whether audio output is enabled: */
+ if (fSuccess && isMachineInValidMode() && newAudioData.m_fAudioOutputEnabled != oldAudioData.m_fAudioOutputEnabled)
+ {
+ comAdapter.SetEnabledOut(newAudioData.m_fAudioOutputEnabled);
+ fSuccess = comAdapter.isOk();
+ }
+ /* Save whether audio input is enabled: */
+ if (fSuccess && isMachineInValidMode() && newAudioData.m_fAudioInputEnabled != oldAudioData.m_fAudioInputEnabled)
+ {
+ comAdapter.SetEnabledIn(newAudioData.m_fAudioInputEnabled);
+ fSuccess = comAdapter.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comAdapter));
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsAudio.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsAudio.h
new file mode 100644
index 00000000..e2cbdedd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsAudio.h
@@ -0,0 +1,103 @@
+/* $Id: UIMachineSettingsAudio.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsAudio class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsAudio_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsAudio_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class UIAudioSettingsEditor;
+struct UIDataSettingsMachineAudio;
+typedef UISettingsCache<UIDataSettingsMachineAudio> UISettingsCacheMachineAudio;
+
+/** Machine settings: Audio page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsAudio : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Audio settings page. */
+ UIMachineSettingsAudio();
+ /** Destructs Audio settings page. */
+ virtual ~UIMachineSettingsAudio() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheMachineAudio *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the audio settings editor instance. */
+ UIAudioSettingsEditor *m_pEditorAudioSettings;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsAudio_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsDisplay.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsDisplay.cpp
new file mode 100644
index 00000000..027b26ea
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsDisplay.cpp
@@ -0,0 +1,1399 @@
+/* $Id: UIMachineSettingsDisplay.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsDisplay class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QFileInfo>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QITabWidget.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIErrorString.h"
+#include "UIExtraDataManager.h"
+#include "UIGraphicsControllerEditor.h"
+#ifdef VBOX_WITH_3D_ACCELERATION
+# include "UIDisplayScreenFeaturesEditor.h"
+#endif
+#include "UIMachineSettingsDisplay.h"
+#include "UIMonitorCountEditor.h"
+#include "UIRecordingSettingsEditor.h"
+#include "UIScaleFactorEditor.h"
+#include "UITranslator.h"
+#include "UIVideoMemoryEditor.h"
+#include "UIVRDESettingsEditor.h"
+
+/* COM includes: */
+#include "CExtPackManager.h"
+#include "CGraphicsAdapter.h"
+#include "CRecordingScreenSettings.h"
+#include "CRecordingSettings.h"
+#include "CVRDEServer.h"
+
+
+/** Machine settings: Display page data structure. */
+struct UIDataSettingsMachineDisplay
+{
+ /** Constructs data. */
+ UIDataSettingsMachineDisplay()
+ : m_iCurrentVRAM(0)
+ , m_cGuestScreenCount(0)
+ , m_graphicsControllerType(KGraphicsControllerType_Null)
+#ifdef VBOX_WITH_3D_ACCELERATION
+ , m_f3dAccelerationEnabled(false)
+#endif
+ , m_fRemoteDisplayServerSupported(false)
+ , m_fRemoteDisplayServerEnabled(false)
+ , m_strRemoteDisplayPort(QString())
+ , m_remoteDisplayAuthType(KAuthType_Null)
+ , m_uRemoteDisplayTimeout(0)
+ , m_fRemoteDisplayMultiConnAllowed(false)
+ , m_fRecordingEnabled(false)
+ , m_strRecordingFolder(QString())
+ , m_strRecordingFilePath(QString())
+ , m_iRecordingVideoFrameWidth(0)
+ , m_iRecordingVideoFrameHeight(0)
+ , m_iRecordingVideoFrameRate(0)
+ , m_iRecordingVideoBitRate(0)
+ , m_strRecordingVideoOptions(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineDisplay &other) const
+ {
+ return true
+ && (m_iCurrentVRAM == other.m_iCurrentVRAM)
+ && (m_cGuestScreenCount == other.m_cGuestScreenCount)
+ && (m_scaleFactors == other.m_scaleFactors)
+ && (m_graphicsControllerType == other.m_graphicsControllerType)
+#ifdef VBOX_WITH_3D_ACCELERATION
+ && (m_f3dAccelerationEnabled == other.m_f3dAccelerationEnabled)
+#endif
+ && (m_fRemoteDisplayServerSupported == other.m_fRemoteDisplayServerSupported)
+ && (m_fRemoteDisplayServerEnabled == other.m_fRemoteDisplayServerEnabled)
+ && (m_strRemoteDisplayPort == other.m_strRemoteDisplayPort)
+ && (m_remoteDisplayAuthType == other.m_remoteDisplayAuthType)
+ && (m_uRemoteDisplayTimeout == other.m_uRemoteDisplayTimeout)
+ && (m_fRemoteDisplayMultiConnAllowed == other.m_fRemoteDisplayMultiConnAllowed)
+ && (m_fRecordingEnabled == other.m_fRecordingEnabled)
+ && (m_strRecordingFilePath == other.m_strRecordingFilePath)
+ && (m_iRecordingVideoFrameWidth == other.m_iRecordingVideoFrameWidth)
+ && (m_iRecordingVideoFrameHeight == other.m_iRecordingVideoFrameHeight)
+ && (m_iRecordingVideoFrameRate == other.m_iRecordingVideoFrameRate)
+ && (m_iRecordingVideoBitRate == other.m_iRecordingVideoBitRate)
+ && (m_vecRecordingScreens == other.m_vecRecordingScreens)
+ && (m_strRecordingVideoOptions == other.m_strRecordingVideoOptions)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineDisplay &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineDisplay &other) const { return !equal(other); }
+
+ /** Recording options. */
+ enum RecordingOption
+ {
+ RecordingOption_Unknown,
+ RecordingOption_AC,
+ RecordingOption_VC,
+ RecordingOption_AC_Profile
+ };
+
+ /** Returns enum value corresponding to passed @a strKey. */
+ static RecordingOption toRecordingOptionKey(const QString &strKey)
+ {
+ /* Compare case-sensitive: */
+ QMap<QString, RecordingOption> keys;
+ keys["ac_enabled"] = RecordingOption_AC;
+ keys["vc_enabled"] = RecordingOption_VC;
+ keys["ac_profile"] = RecordingOption_AC_Profile;
+ /* Return known value or RecordingOption_Unknown otherwise: */
+ return keys.value(strKey, RecordingOption_Unknown);
+ }
+
+ /** Returns string representation for passed enum @a enmKey. */
+ static QString fromRecordingOptionKey(RecordingOption enmKey)
+ {
+ /* Compare case-sensitive: */
+ QMap<RecordingOption, QString> values;
+ values[RecordingOption_AC] = "ac_enabled";
+ values[RecordingOption_VC] = "vc_enabled";
+ values[RecordingOption_AC_Profile] = "ac_profile";
+ /* Return known value or QString() otherwise: */
+ return values.value(enmKey);
+ }
+
+ /** Parses recording options. */
+ static void parseRecordingOptions(const QString &strOptions,
+ QList<RecordingOption> &outKeys,
+ QStringList &outValues)
+ {
+ outKeys.clear();
+ outValues.clear();
+ const QStringList aPairs = strOptions.split(',');
+ foreach (const QString &strPair, aPairs)
+ {
+ const QStringList aPair = strPair.split('=');
+ if (aPair.size() != 2)
+ continue;
+ const RecordingOption enmKey = toRecordingOptionKey(aPair.value(0));
+ if (enmKey == RecordingOption_Unknown)
+ continue;
+ outKeys << enmKey;
+ outValues << aPair.value(1);
+ }
+ }
+
+ /** Serializes recording options. */
+ static void serializeRecordingOptions(const QList<RecordingOption> &inKeys,
+ const QStringList &inValues,
+ QString &strOptions)
+ {
+ QStringList aPairs;
+ for (int i = 0; i < inKeys.size(); ++i)
+ {
+ QStringList aPair;
+ aPair << fromRecordingOptionKey(inKeys.value(i));
+ aPair << inValues.value(i);
+ aPairs << aPair.join('=');
+ }
+ strOptions = aPairs.join(',');
+ }
+
+ /** Returns whether passed Recording @a enmOption is enabled. */
+ static bool isRecordingOptionEnabled(const QString &strOptions,
+ RecordingOption enmOption)
+ {
+ QList<RecordingOption> aKeys;
+ QStringList aValues;
+ parseRecordingOptions(strOptions, aKeys, aValues);
+ int iIndex = aKeys.indexOf(enmOption);
+ if (iIndex == -1)
+ return false; /* If option is missing, assume disabled (false). */
+ if (aValues.value(iIndex).compare("true", Qt::CaseInsensitive) == 0)
+ return true;
+ return false;
+ }
+
+ /** Searches for ac_profile and return 1 for "low", 2 for "med", and 3 for "high". Returns 2
+ if ac_profile is missing */
+ static int getAudioQualityFromOptions(const QString &strOptions)
+ {
+ QList<RecordingOption> aKeys;
+ QStringList aValues;
+ parseRecordingOptions(strOptions, aKeys, aValues);
+ int iIndex = aKeys.indexOf(RecordingOption_AC_Profile);
+ if (iIndex == -1)
+ return 2;
+ if (aValues.value(iIndex).compare("low", Qt::CaseInsensitive) == 0)
+ return 1;
+ if (aValues.value(iIndex).compare("high", Qt::CaseInsensitive) == 0)
+ return 3;
+ return 2;
+ }
+
+ /** Sets the video recording options for @a enmOptions to @a values. */
+ static QString setRecordingOptions(const QString &strOptions,
+ const QVector<RecordingOption> &enmOptions,
+ const QStringList &values)
+ {
+ if (enmOptions.size() != values.size())
+ return QString();
+ QList<RecordingOption> aKeys;
+ QStringList aValues;
+ parseRecordingOptions(strOptions, aKeys, aValues);
+ for(int i = 0; i < values.size(); ++i)
+ {
+ QString strValue = values[i];
+ int iIndex = aKeys.indexOf(enmOptions[i]);
+ if (iIndex == -1)
+ {
+ aKeys << enmOptions[i];
+ aValues << strValue;
+ }
+ else
+ {
+ aValues[iIndex] = strValue;
+ }
+ }
+ QString strResult;
+ serializeRecordingOptions(aKeys, aValues, strResult);
+ return strResult;
+ }
+
+ /** Holds the video RAM amount. */
+ int m_iCurrentVRAM;
+ /** Holds the guest screen count. */
+ int m_cGuestScreenCount;
+ /** Holds the guest screen scale-factor. */
+ QList<double> m_scaleFactors;
+ /** Holds the graphics controller type. */
+ KGraphicsControllerType m_graphicsControllerType;
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /** Holds whether the 3D acceleration is enabled. */
+ bool m_f3dAccelerationEnabled;
+#endif
+ /** Holds whether the remote display server is supported. */
+ bool m_fRemoteDisplayServerSupported;
+ /** Holds whether the remote display server is enabled. */
+ bool m_fRemoteDisplayServerEnabled;
+ /** Holds the remote display server port. */
+ QString m_strRemoteDisplayPort;
+ /** Holds the remote display server auth type. */
+ KAuthType m_remoteDisplayAuthType;
+ /** Holds the remote display server timeout. */
+ ulong m_uRemoteDisplayTimeout;
+ /** Holds whether the remote display server allows multiple connections. */
+ bool m_fRemoteDisplayMultiConnAllowed;
+
+ /** Holds whether recording is enabled. */
+ bool m_fRecordingEnabled;
+ /** Holds the recording folder. */
+ QString m_strRecordingFolder;
+ /** Holds the recording file path. */
+ QString m_strRecordingFilePath;
+ /** Holds the recording frame width. */
+ int m_iRecordingVideoFrameWidth;
+ /** Holds the recording frame height. */
+ int m_iRecordingVideoFrameHeight;
+ /** Holds the recording frame rate. */
+ int m_iRecordingVideoFrameRate;
+ /** Holds the recording bit rate. */
+ int m_iRecordingVideoBitRate;
+ /** Holds which of the guest screens should be recorded. */
+ QVector<BOOL> m_vecRecordingScreens;
+ /** Holds the video recording options. */
+ QString m_strRecordingVideoOptions;
+};
+
+
+UIMachineSettingsDisplay::UIMachineSettingsDisplay()
+ : m_comGuestOSType(CGuestOSType())
+#ifdef VBOX_WITH_3D_ACCELERATION
+ , m_fWddmModeSupported(false)
+#endif
+ , m_enmGraphicsControllerTypeRecommended(KGraphicsControllerType_Null)
+ , m_pCache(0)
+ , m_pTabWidget(0)
+ , m_pTabScreen(0)
+ , m_pEditorVideoMemorySize(0)
+ , m_pEditorMonitorCount(0)
+ , m_pEditorScaleFactor(0)
+ , m_pEditorGraphicsController(0)
+#ifdef VBOX_WITH_3D_ACCELERATION
+ , m_pEditorDisplayScreenFeatures(0)
+#endif
+ , m_pTabRemoteDisplay(0)
+ , m_pEditorVRDESettings(0)
+ , m_pTabRecording(0)
+ , m_pEditorRecordingSettings(0)
+{
+ prepare();
+}
+
+UIMachineSettingsDisplay::~UIMachineSettingsDisplay()
+{
+ cleanup();
+}
+
+void UIMachineSettingsDisplay::setGuestOSType(CGuestOSType comGuestOSType)
+{
+ /* Check if guest OS type changed: */
+ if (m_comGuestOSType == comGuestOSType)
+ return;
+
+ /* Remember new guest OS type: */
+ m_comGuestOSType = comGuestOSType;
+ m_pEditorVideoMemorySize->setGuestOSType(m_comGuestOSType);
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /* Check if WDDM mode supported by the guest OS type: */
+ const QString strGuestOSTypeId = m_comGuestOSType.isNotNull() ? m_comGuestOSType.GetId() : QString();
+ m_fWddmModeSupported = UICommon::isWddmCompatibleOsType(strGuestOSTypeId);
+ m_pEditorVideoMemorySize->set3DAccelerationSupported(m_fWddmModeSupported);
+#endif /* VBOX_WITH_3D_ACCELERATION */
+ /* Acquire recommended graphics controller type: */
+ m_enmGraphicsControllerTypeRecommended = m_comGuestOSType.GetRecommendedGraphicsController();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+bool UIMachineSettingsDisplay::isAcceleration3DSelected() const
+{
+ return m_pEditorDisplayScreenFeatures->isEnabled3DAcceleration();
+}
+#endif /* VBOX_WITH_3D_ACCELERATION */
+
+KGraphicsControllerType UIMachineSettingsDisplay::graphicsControllerTypeRecommended() const
+{
+ return m_pEditorGraphicsController->supportedValues().contains(m_enmGraphicsControllerTypeRecommended)
+ ? m_enmGraphicsControllerTypeRecommended
+ : graphicsControllerTypeCurrent();
+}
+
+KGraphicsControllerType UIMachineSettingsDisplay::graphicsControllerTypeCurrent() const
+{
+ return m_pEditorGraphicsController->value();
+}
+
+bool UIMachineSettingsDisplay::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsDisplay::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Prepare old data: */
+ UIDataSettingsMachineDisplay oldDisplayData;
+
+ /* Check whether graphics adapter is valid: */
+ const CGraphicsAdapter &comGraphics = m_machine.GetGraphicsAdapter();
+ if (!comGraphics.isNull())
+ {
+ /* Gather old 'Screen' data: */
+ oldDisplayData.m_iCurrentVRAM = comGraphics.GetVRAMSize();
+ oldDisplayData.m_cGuestScreenCount = comGraphics.GetMonitorCount();
+ oldDisplayData.m_scaleFactors = gEDataManager->scaleFactors(m_machine.GetId());
+ oldDisplayData.m_graphicsControllerType = comGraphics.GetGraphicsControllerType();
+#ifdef VBOX_WITH_3D_ACCELERATION
+ oldDisplayData.m_f3dAccelerationEnabled = comGraphics.GetAccelerate3DEnabled();
+#endif
+ }
+
+ /* Check whether remote display server is valid: */
+ const CVRDEServer &vrdeServer = m_machine.GetVRDEServer();
+ oldDisplayData.m_fRemoteDisplayServerSupported = !vrdeServer.isNull();
+ if (!vrdeServer.isNull())
+ {
+ /* Gather old 'Remote Display' data: */
+ oldDisplayData.m_fRemoteDisplayServerEnabled = vrdeServer.GetEnabled();
+ oldDisplayData.m_strRemoteDisplayPort = vrdeServer.GetVRDEProperty("TCP/Ports");
+ oldDisplayData.m_remoteDisplayAuthType = vrdeServer.GetAuthType();
+ oldDisplayData.m_uRemoteDisplayTimeout = vrdeServer.GetAuthTimeout();
+ oldDisplayData.m_fRemoteDisplayMultiConnAllowed = vrdeServer.GetAllowMultiConnection();
+ }
+
+ /* Gather old 'Recording' data: */
+ CRecordingSettings recordingSettings = m_machine.GetRecordingSettings();
+ Assert(recordingSettings.isNotNull());
+ oldDisplayData.m_fRecordingEnabled = recordingSettings.GetEnabled();
+
+ /* For now we're using the same settings for all screens; so get settings from screen 0 and work with that. */
+ /** @todo r=andy Since VBox 7.0 (settings 1.19) the per-screen settings can be handled. i.e. screens can have
+ * different settings. See @bugref{10259} */
+ CRecordingScreenSettings comRecordingScreen0Settings = recordingSettings.GetScreenSettings(0);
+ if (!comRecordingScreen0Settings.isNull())
+ {
+ oldDisplayData.m_strRecordingFolder = QFileInfo(m_machine.GetSettingsFilePath()).absolutePath();
+ oldDisplayData.m_strRecordingFilePath = comRecordingScreen0Settings.GetFilename();
+ oldDisplayData.m_iRecordingVideoFrameWidth = comRecordingScreen0Settings.GetVideoWidth();
+ oldDisplayData.m_iRecordingVideoFrameHeight = comRecordingScreen0Settings.GetVideoHeight();
+ oldDisplayData.m_iRecordingVideoFrameRate = comRecordingScreen0Settings.GetVideoFPS();
+ oldDisplayData.m_iRecordingVideoBitRate = comRecordingScreen0Settings.GetVideoRate();
+ oldDisplayData.m_strRecordingVideoOptions = comRecordingScreen0Settings.GetOptions();
+ }
+
+ CRecordingScreenSettingsVector comRecordingScreenSettingsVector = recordingSettings.GetScreens();
+ oldDisplayData.m_vecRecordingScreens.resize(comRecordingScreenSettingsVector.size());
+ for (int iScreenIndex = 0; iScreenIndex < comRecordingScreenSettingsVector.size(); ++iScreenIndex)
+ {
+ CRecordingScreenSettings comRecordingScreenSettings = comRecordingScreenSettingsVector.at(iScreenIndex);
+ if (!comRecordingScreenSettings.isNull())
+ oldDisplayData.m_vecRecordingScreens[iScreenIndex] = comRecordingScreenSettings.GetEnabled();
+ }
+
+ /* Cache old data: */
+ m_pCache->cacheInitialData(oldDisplayData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsDisplay::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Get old data from cache: */
+ const UIDataSettingsMachineDisplay &oldDisplayData = m_pCache->base();
+
+ /* Load old 'Screen' data from cache: */
+ if (m_pEditorMonitorCount)
+ m_pEditorMonitorCount->setValue(oldDisplayData.m_cGuestScreenCount);
+ if (m_pEditorScaleFactor)
+ {
+ m_pEditorScaleFactor->setScaleFactors(oldDisplayData.m_scaleFactors);
+ m_pEditorScaleFactor->setMonitorCount(oldDisplayData.m_cGuestScreenCount);
+ }
+ if (m_pEditorGraphicsController)
+ m_pEditorGraphicsController->setValue(oldDisplayData.m_graphicsControllerType);
+#ifdef VBOX_WITH_3D_ACCELERATION
+ if (m_pEditorDisplayScreenFeatures)
+ m_pEditorDisplayScreenFeatures->setEnable3DAcceleration(oldDisplayData.m_f3dAccelerationEnabled);
+#endif
+ /* Push required value to m_pEditorVideoMemorySize: */
+ sltHandleMonitorCountChange();
+ sltHandleGraphicsControllerComboChange();
+#ifdef VBOX_WITH_3D_ACCELERATION
+ sltHandle3DAccelerationFeatureStateChange();
+#endif
+ // Should be the last one for this tab, since it depends on some of others:
+ if (m_pEditorVideoMemorySize)
+ m_pEditorVideoMemorySize->setValue(oldDisplayData.m_iCurrentVRAM);
+
+ /* If remote display server is supported: */
+ if (oldDisplayData.m_fRemoteDisplayServerSupported)
+ {
+ /* Load old 'Remote Display' data from cache: */
+ if (m_pEditorVRDESettings)
+ {
+ m_pEditorVRDESettings->setFeatureEnabled(oldDisplayData.m_fRemoteDisplayServerEnabled);
+ m_pEditorVRDESettings->setPort(oldDisplayData.m_strRemoteDisplayPort);
+ m_pEditorVRDESettings->setAuthType(oldDisplayData.m_remoteDisplayAuthType);
+ m_pEditorVRDESettings->setTimeout(QString::number(oldDisplayData.m_uRemoteDisplayTimeout));
+ m_pEditorVRDESettings->setMultipleConnectionsAllowed(oldDisplayData.m_fRemoteDisplayMultiConnAllowed);
+ }
+ }
+
+ if (m_pEditorRecordingSettings)
+ {
+ /* Load old 'Recording' data from cache: */
+ m_pEditorRecordingSettings->setFeatureEnabled(oldDisplayData.m_fRecordingEnabled);
+ m_pEditorRecordingSettings->setFolder(oldDisplayData.m_strRecordingFolder);
+ m_pEditorRecordingSettings->setFilePath(oldDisplayData.m_strRecordingFilePath);
+ m_pEditorRecordingSettings->setFrameWidth(oldDisplayData.m_iRecordingVideoFrameWidth);
+ m_pEditorRecordingSettings->setFrameHeight(oldDisplayData.m_iRecordingVideoFrameHeight);
+ m_pEditorRecordingSettings->setFrameRate(oldDisplayData.m_iRecordingVideoFrameRate);
+ m_pEditorRecordingSettings->setBitRate(oldDisplayData.m_iRecordingVideoBitRate);
+ m_pEditorRecordingSettings->setScreens(oldDisplayData.m_vecRecordingScreens);
+
+ /* Load old 'Recording' options: */
+ const bool fRecordVideo =
+ UIDataSettingsMachineDisplay::isRecordingOptionEnabled(oldDisplayData.m_strRecordingVideoOptions,
+ UIDataSettingsMachineDisplay::RecordingOption_VC);
+ const bool fRecordAudio =
+ UIDataSettingsMachineDisplay::isRecordingOptionEnabled(oldDisplayData.m_strRecordingVideoOptions,
+ UIDataSettingsMachineDisplay::RecordingOption_AC);
+ UISettingsDefs::RecordingMode enmMode;
+ if (fRecordAudio && fRecordVideo)
+ enmMode = UISettingsDefs::RecordingMode_VideoAudio;
+ else if (fRecordAudio && !fRecordVideo)
+ enmMode = UISettingsDefs::RecordingMode_AudioOnly;
+ else
+ enmMode = UISettingsDefs::RecordingMode_VideoOnly;
+ m_pEditorRecordingSettings->setMode(enmMode);
+ const int iAudioQualityRate =
+ UIDataSettingsMachineDisplay::getAudioQualityFromOptions(oldDisplayData.m_strRecordingVideoOptions);
+ m_pEditorRecordingSettings->setAudioQualityRate(iAudioQualityRate);
+ }
+
+ /* Polish page finally: */
+ polishPage();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMachineSettingsDisplay::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsMachineDisplay newDisplayData;
+
+ /* Gather new 'Screen' data: */
+ if (m_pEditorVideoMemorySize)
+ newDisplayData.m_iCurrentVRAM = m_pEditorVideoMemorySize->value();
+ if (m_pEditorMonitorCount)
+ newDisplayData.m_cGuestScreenCount = m_pEditorMonitorCount->value();
+ if (m_pEditorScaleFactor)
+ newDisplayData.m_scaleFactors = m_pEditorScaleFactor->scaleFactors();
+ if (m_pEditorGraphicsController)
+ newDisplayData.m_graphicsControllerType = m_pEditorGraphicsController->value();
+#ifdef VBOX_WITH_3D_ACCELERATION
+ if (m_pEditorDisplayScreenFeatures)
+ newDisplayData.m_f3dAccelerationEnabled = m_pEditorDisplayScreenFeatures->isEnabled3DAcceleration();
+#endif
+
+ /* If remote display server is supported: */
+ newDisplayData.m_fRemoteDisplayServerSupported = m_pCache->base().m_fRemoteDisplayServerSupported;
+ if ( newDisplayData.m_fRemoteDisplayServerSupported
+ && m_pEditorVRDESettings)
+ {
+ /* Gather new 'Remote Display' data: */
+ newDisplayData.m_fRemoteDisplayServerEnabled = m_pEditorVRDESettings->isFeatureEnabled();
+ newDisplayData.m_strRemoteDisplayPort = m_pEditorVRDESettings->port();
+ newDisplayData.m_remoteDisplayAuthType = m_pEditorVRDESettings->authType();
+ newDisplayData.m_uRemoteDisplayTimeout = m_pEditorVRDESettings->timeout().toULong();
+ newDisplayData.m_fRemoteDisplayMultiConnAllowed = m_pEditorVRDESettings->isMultipleConnectionsAllowed();
+ }
+
+ if (m_pEditorRecordingSettings)
+ {
+ /* Gather new 'Recording' data: */
+ newDisplayData.m_fRecordingEnabled = m_pEditorRecordingSettings->isFeatureEnabled();
+ newDisplayData.m_strRecordingFolder = m_pEditorRecordingSettings->folder();
+ newDisplayData.m_strRecordingFilePath = m_pEditorRecordingSettings->filePath();
+ newDisplayData.m_iRecordingVideoFrameWidth = m_pEditorRecordingSettings->frameWidth();
+ newDisplayData.m_iRecordingVideoFrameHeight = m_pEditorRecordingSettings->frameHeight();
+ newDisplayData.m_iRecordingVideoFrameRate = m_pEditorRecordingSettings->frameRate();
+ newDisplayData.m_iRecordingVideoBitRate = m_pEditorRecordingSettings->bitRate();
+ newDisplayData.m_vecRecordingScreens = m_pEditorRecordingSettings->screens();
+
+ /* Gather new 'Recording' options: */
+ const UISettingsDefs::RecordingMode enmRecordingMode = m_pEditorRecordingSettings->mode();
+ QStringList optionValues;
+ optionValues.append( (enmRecordingMode == UISettingsDefs::RecordingMode_VideoAudio)
+ || (enmRecordingMode == UISettingsDefs::RecordingMode_VideoOnly)
+ ? "true" : "false");
+ optionValues.append( (enmRecordingMode == UISettingsDefs::RecordingMode_VideoAudio)
+ || (enmRecordingMode == UISettingsDefs::RecordingMode_AudioOnly)
+ ? "true" : "false");
+ switch (m_pEditorRecordingSettings->audioQualityRate())
+ {
+ case 1: optionValues.append("low"); break;
+ case 2: optionValues.append("med"); break;
+ default: optionValues.append("high"); break;
+ }
+ QVector<UIDataSettingsMachineDisplay::RecordingOption> optionKeys;
+ optionKeys.append(UIDataSettingsMachineDisplay::RecordingOption_VC);
+ optionKeys.append(UIDataSettingsMachineDisplay::RecordingOption_AC);
+ optionKeys.append(UIDataSettingsMachineDisplay::RecordingOption_AC_Profile);
+ newDisplayData.m_strRecordingVideoOptions =
+ UIDataSettingsMachineDisplay::setRecordingOptions(m_pCache->base().m_strRecordingVideoOptions,
+ optionKeys, optionValues);
+ }
+
+ /* Cache new data: */
+ m_pCache->cacheCurrentData(newDisplayData);
+}
+
+void UIMachineSettingsDisplay::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+bool UIMachineSettingsDisplay::validate(QList<UIValidationMessage> &messages)
+{
+ /* Pass by default: */
+ bool fPass = true;
+
+ /* Screen tab: */
+ {
+ /* Prepare message: */
+ UIValidationMessage message;
+ message.first = UITranslator::removeAccelMark(m_pTabWidget->tabText(0));
+
+ /* Video RAM amount test: */
+ if (shouldWeWarnAboutLowVRAM() && !m_comGuestOSType.isNull())
+ {
+ quint64 uNeedBytes = UICommon::requiredVideoMemory(m_comGuestOSType.GetId(), m_pEditorMonitorCount->value());
+
+ /* Basic video RAM amount test: */
+ if ((quint64)m_pEditorVideoMemorySize->value() * _1M < uNeedBytes)
+ {
+ message.second << tr("The virtual machine is currently assigned less than <b>%1</b> of video memory "
+ "which is the minimum amount required to switch to full-screen or seamless mode.")
+ .arg(UITranslator::formatSize(uNeedBytes, 0, FormatSize_RoundUp));
+ }
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /* 3D acceleration video RAM amount test: */
+ else if (m_pEditorDisplayScreenFeatures->isEnabled3DAcceleration() && m_fWddmModeSupported)
+ {
+ uNeedBytes = qMax(uNeedBytes, (quint64) 128 * _1M);
+ if ((quint64)m_pEditorVideoMemorySize->value() * _1M < uNeedBytes)
+ {
+ message.second << tr("The virtual machine is set up to use hardware graphics acceleration "
+ "and the operating system hint is set to Windows Vista or later. "
+ "For best performance you should set the machine's video memory to at least <b>%1</b>.")
+ .arg(UITranslator::formatSize(uNeedBytes, 0, FormatSize_RoundUp));
+ }
+ }
+#endif /* VBOX_WITH_3D_ACCELERATION */
+ }
+
+ /* Graphics controller type test: */
+ if (!m_comGuestOSType.isNull())
+ {
+ if (graphicsControllerTypeCurrent() != graphicsControllerTypeRecommended())
+ {
+#ifdef VBOX_WITH_3D_ACCELERATION
+ if (m_pEditorDisplayScreenFeatures->isEnabled3DAcceleration())
+ message.second << tr("The virtual machine is configured to use 3D acceleration. This will work only if you "
+ "pick a different graphics controller (%1). Either disable 3D acceleration or switch "
+ "to required graphics controller type. The latter will be done automatically if you "
+ "confirm your changes.")
+ .arg(gpConverter->toString(m_enmGraphicsControllerTypeRecommended));
+ else
+#endif /* VBOX_WITH_3D_ACCELERATION */
+ message.second << tr("The virtual machine is configured to use a graphics controller other than the "
+ "recommended one (%1). Please consider switching unless you have a reason to keep the "
+ "currently selected graphics controller.")
+ .arg(gpConverter->toString(m_enmGraphicsControllerTypeRecommended));
+ }
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+ }
+
+ /* Remote Display tab: */
+ {
+ /* Prepare message: */
+ UIValidationMessage message;
+ message.first = UITranslator::removeAccelMark(m_pTabWidget->tabText(1));
+
+ /* Extension Pack presence test: */
+ if (m_pEditorVRDESettings->isFeatureEnabled())
+ {
+ CExtPackManager extPackManager = uiCommon().virtualBox().GetExtensionPackManager();
+ if (!extPackManager.isNull() && !extPackManager.IsExtPackUsable(GUI_ExtPackName))
+ {
+ message.second << tr("Remote Display is currently enabled for this virtual machine. "
+ "However, this requires the <i>%1</i> to be installed. "
+ "Please install the Extension Pack from the VirtualBox download site as "
+ "otherwise your VM will be started with Remote Display disabled.")
+ .arg(GUI_ExtPackName);
+ }
+ }
+
+ /* Check VRDE server port: */
+ if (m_pEditorVRDESettings->port().trimmed().isEmpty())
+ {
+ message.second << tr("The VRDE server port value is not currently specified.");
+ fPass = false;
+ }
+
+ /* Check VRDE server timeout: */
+ if (m_pEditorVRDESettings->timeout().trimmed().isEmpty())
+ {
+ message.second << tr("The VRDE authentication timeout value is not currently specified.");
+ fPass = false;
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+ }
+
+ /* Return result: */
+ return fPass;
+}
+
+void UIMachineSettingsDisplay::setOrderAfter(QWidget *pWidget)
+{
+ /* Screen tab-order: */
+ setTabOrder(pWidget, m_pTabWidget->focusProxy());
+ setTabOrder(m_pTabWidget->focusProxy(), m_pEditorVideoMemorySize);
+ setTabOrder(m_pEditorVideoMemorySize, m_pEditorMonitorCount);
+ setTabOrder(m_pEditorMonitorCount, m_pEditorScaleFactor);
+ setTabOrder(m_pEditorScaleFactor, m_pEditorGraphicsController);
+#ifdef VBOX_WITH_3D_ACCELERATION
+ setTabOrder(m_pEditorGraphicsController, m_pEditorDisplayScreenFeatures);
+ setTabOrder(m_pEditorDisplayScreenFeatures, m_pEditorVRDESettings);
+#else
+ setTabOrder(m_pEditorGraphicsController, m_pEditorVRDESettings);
+#endif
+
+ /* Remote Display tab-order: */
+ setTabOrder(m_pEditorVRDESettings, m_pEditorRecordingSettings);
+}
+
+void UIMachineSettingsDisplay::retranslateUi()
+{
+ /* Translate tab-widget: */
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabScreen), tr("&Screen"));
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabRemoteDisplay), tr("&Remote Display"));
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabRecording), tr("Re&cording"));
+
+ /* These editors have own labels, but we want them to be properly layouted according to each other: */
+ int iMinimumLayoutHint = 0;
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorVideoMemorySize->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorMonitorCount->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorScaleFactor->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorGraphicsController->minimumLabelHorizontalHint());
+#ifdef VBOX_WITH_3D_ACCELERATION
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorDisplayScreenFeatures->minimumLabelHorizontalHint());
+#endif
+ m_pEditorVideoMemorySize->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorMonitorCount->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorScaleFactor->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorGraphicsController->setMinimumLayoutIndent(iMinimumLayoutHint);
+#ifdef VBOX_WITH_3D_ACCELERATION
+ m_pEditorDisplayScreenFeatures->setMinimumLayoutIndent(iMinimumLayoutHint);
+#endif
+}
+
+void UIMachineSettingsDisplay::polishPage()
+{
+ /* Get old data from cache: */
+ const UIDataSettingsMachineDisplay &oldDisplayData = m_pCache->base();
+
+ /* Polish 'Screen' availability: */
+ m_pEditorVideoMemorySize->setEnabled(isMachineOffline());
+ m_pEditorMonitorCount->setEnabled(isMachineOffline());
+ m_pEditorScaleFactor->setEnabled(isMachineInValidMode());
+ m_pEditorGraphicsController->setEnabled(isMachineOffline());
+#ifdef VBOX_WITH_3D_ACCELERATION
+ m_pEditorDisplayScreenFeatures->setEnabled(isMachineOffline());
+#endif
+
+ /* Polish 'Remote Display' availability: */
+ m_pTabWidget->setTabEnabled(1, oldDisplayData.m_fRemoteDisplayServerSupported);
+ m_pTabRemoteDisplay->setEnabled(isMachineInValidMode());
+ m_pEditorVRDESettings->setVRDEOptionsAvailable(isMachineOffline() || isMachineSaved());
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ /* Polish 'Recording' visibility: */
+ m_pTabWidget->setTabVisible(m_pTabWidget->indexOf(m_pTabRecording), uiCommon().supportedRecordingFeatures());
+ /* Polish 'Recording' availability: */
+ m_pTabRecording->setEnabled(isMachineInValidMode());
+#else
+ /* Polish 'Recording' availability: */
+ m_pTabWidget->setTabEnabled(2, isMachineInValidMode() && uiCommon().supportedRecordingFeatures());
+ m_pTabRecording->setEnabled(isMachineInValidMode() && uiCommon().supportedRecordingFeatures());
+#endif
+ // Recording options should be enabled only if:
+ // 1. Machine is in 'offline' or 'saved' state,
+ // 2. Machine is in 'online' state and video recording is *disabled* currently.
+ const bool fIsRecordingOptionsEnabled =
+ ((isMachineOffline() || isMachineSaved()))
+ || (isMachineOnline() && !m_pCache->base().m_fRecordingEnabled);
+ m_pEditorRecordingSettings->setOptionsAvailable(fIsRecordingOptionsEnabled);
+ // Recording screens option should be enabled only if:
+ // 1. Machine is in *any* valid state.
+ const bool fIsRecordingScreenOptionsEnabled =
+ isMachineInValidMode();
+ m_pEditorRecordingSettings->setScreenOptionsAvailable(fIsRecordingScreenOptionsEnabled);
+}
+
+void UIMachineSettingsDisplay::sltHandleMonitorCountChange()
+{
+ /* Update recording tab screen count: */
+ updateGuestScreenCount();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMachineSettingsDisplay::sltHandleGraphicsControllerComboChange()
+{
+ /* Update Video RAM requirements: */
+ m_pEditorVideoMemorySize->setGraphicsControllerType(m_pEditorGraphicsController->value());
+
+ /* Revalidate: */
+ revalidate();
+}
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+void UIMachineSettingsDisplay::sltHandle3DAccelerationFeatureStateChange()
+{
+ /* Update Video RAM requirements: */
+ m_pEditorVideoMemorySize->set3DAccelerationEnabled(m_pEditorDisplayScreenFeatures->isEnabled3DAcceleration());
+
+ /* Revalidate: */
+ revalidate();
+}
+#endif /* VBOX_WITH_3D_ACCELERATION */
+
+void UIMachineSettingsDisplay::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheMachineDisplay;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsDisplay::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare tab-widget: */
+ m_pTabWidget = new QITabWidget(this);
+ if (m_pTabWidget)
+ {
+ /* Prepare each tab separately: */
+ prepareTabScreen();
+ prepareTabRemoteDisplay();
+ prepareTabRecording();
+
+ pLayout->addWidget(m_pTabWidget);
+ }
+ }
+}
+
+void UIMachineSettingsDisplay::prepareTabScreen()
+{
+ /* Prepare 'Screen' tab: */
+ m_pTabScreen = new QWidget;
+ if (m_pTabScreen)
+ {
+ /* Prepare 'Screen' tab layout: */
+ QVBoxLayout *pLayoutScreen = new QVBoxLayout(m_pTabScreen);
+ if (pLayoutScreen)
+ {
+ /* Prepare video memory editor: */
+ m_pEditorVideoMemorySize = new UIVideoMemoryEditor(m_pTabScreen);
+ if (m_pEditorVideoMemorySize)
+ pLayoutScreen->addWidget(m_pEditorVideoMemorySize);
+
+ /* Prepare monitor count editor: */
+ m_pEditorMonitorCount = new UIMonitorCountEditor(m_pTabScreen);
+ if (m_pEditorMonitorCount)
+ pLayoutScreen->addWidget(m_pEditorMonitorCount);
+
+ /* Prepare scale factor editor: */
+ m_pEditorScaleFactor = new UIScaleFactorEditor(m_pTabScreen);
+ if (m_pEditorScaleFactor)
+ pLayoutScreen->addWidget(m_pEditorScaleFactor);
+
+ /* Prepare graphics controller editor: */
+ m_pEditorGraphicsController = new UIGraphicsControllerEditor(m_pTabScreen);
+ if (m_pEditorGraphicsController)
+ pLayoutScreen->addWidget(m_pEditorGraphicsController);
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /* Prepare display screen features editor: */
+ m_pEditorDisplayScreenFeatures = new UIDisplayScreenFeaturesEditor(m_pTabScreen);
+ if (m_pEditorDisplayScreenFeatures)
+ pLayoutScreen->addWidget(m_pEditorDisplayScreenFeatures);
+#endif /* VBOX_WITH_3D_ACCELERATION */
+
+ pLayoutScreen->addStretch();
+ }
+
+ m_pTabWidget->addTab(m_pTabScreen, QString());
+ }
+}
+
+void UIMachineSettingsDisplay::prepareTabRemoteDisplay()
+{
+ /* Prepare 'Remote Display' tab: */
+ m_pTabRemoteDisplay = new QWidget;
+ if (m_pTabRemoteDisplay)
+ {
+ /* Prepare 'Remote Display' tab layout: */
+ QVBoxLayout *pLayoutRemoteDisplay = new QVBoxLayout(m_pTabRemoteDisplay);
+ if (pLayoutRemoteDisplay)
+ {
+ /* Prepare remote display settings editor: */
+ m_pEditorVRDESettings = new UIVRDESettingsEditor(m_pTabRemoteDisplay);
+ if (m_pEditorVRDESettings)
+ pLayoutRemoteDisplay->addWidget(m_pEditorVRDESettings);
+
+ pLayoutRemoteDisplay->addStretch();
+ }
+
+ m_pTabWidget->addTab(m_pTabRemoteDisplay, QString());
+ }
+}
+
+void UIMachineSettingsDisplay::prepareTabRecording()
+{
+ /* Prepare 'Recording' tab: */
+ m_pTabRecording = new QWidget;
+ if (m_pTabRecording)
+ {
+ /* Prepare 'Recording' tab layout: */
+ QVBoxLayout *pLayoutRecording = new QVBoxLayout(m_pTabRecording);
+ if (pLayoutRecording)
+ {
+ /* Prepare recording editor: */
+ m_pEditorRecordingSettings = new UIRecordingSettingsEditor(m_pTabRecording);
+ if (m_pEditorRecordingSettings)
+ pLayoutRecording->addWidget(m_pEditorRecordingSettings);
+
+ pLayoutRecording->addStretch();
+ }
+
+ m_pTabWidget->addTab(m_pTabRecording, QString());
+ }
+}
+
+void UIMachineSettingsDisplay::prepareConnections()
+{
+ /* Configure 'Screen' connections: */
+ connect(m_pEditorVideoMemorySize, &UIVideoMemoryEditor::sigValidChanged,
+ this, &UIMachineSettingsDisplay::revalidate);
+ connect(m_pEditorMonitorCount, &UIMonitorCountEditor::sigValidChanged,
+ this, &UIMachineSettingsDisplay::sltHandleMonitorCountChange);
+ connect(m_pEditorGraphicsController, &UIGraphicsControllerEditor::sigValueChanged,
+ this, &UIMachineSettingsDisplay::sltHandleGraphicsControllerComboChange);
+#ifdef VBOX_WITH_3D_ACCELERATION
+ connect(m_pEditorDisplayScreenFeatures, &UIDisplayScreenFeaturesEditor::sig3DAccelerationFeatureStatusChange,
+ this, &UIMachineSettingsDisplay::sltHandle3DAccelerationFeatureStateChange);
+#endif
+
+ /* Configure 'Remote Display' connections: */
+ connect(m_pEditorVRDESettings, &UIVRDESettingsEditor::sigChanged,
+ this, &UIMachineSettingsDisplay::revalidate);
+}
+
+void UIMachineSettingsDisplay::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIMachineSettingsDisplay::shouldWeWarnAboutLowVRAM()
+{
+ bool fResult = true;
+
+ QStringList excludingOSList = QStringList()
+ << "Other" << "DOS" << "Netware" << "L4" << "QNX" << "JRockitVE";
+ if (m_comGuestOSType.isNull() || excludingOSList.contains(m_comGuestOSType.GetId()))
+ fResult = false;
+
+ return fResult;
+}
+
+void UIMachineSettingsDisplay::updateGuestScreenCount()
+{
+ /* Update copy of the cached item to get the desired result: */
+ QVector<BOOL> screens = m_pCache->base().m_vecRecordingScreens;
+ screens.resize(m_pEditorMonitorCount->value());
+ m_pEditorRecordingSettings->setScreens(screens);
+ m_pEditorScaleFactor->setMonitorCount(m_pEditorMonitorCount->value());
+}
+
+bool UIMachineSettingsDisplay::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save display settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* Save 'Screen' data from cache: */
+ if (fSuccess)
+ fSuccess = saveScreenData();
+ /* Save 'Remote Display' data from cache: */
+ if (fSuccess)
+ fSuccess = saveRemoteDisplayData();
+ /* Save 'Video Capture' data from cache: */
+ if (fSuccess)
+ fSuccess = saveRecordingData();
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsDisplay::saveScreenData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Screen' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineDisplay &oldDisplayData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineDisplay &newDisplayData = m_pCache->data();
+
+ /* Get graphics adapter for further activities: */
+ CGraphicsAdapter comGraphics = m_machine.GetGraphicsAdapter();
+ fSuccess = m_machine.isOk() && comGraphics.isNotNull();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ else
+ {
+ /* Save video RAM size: */
+ if (fSuccess && isMachineOffline() && newDisplayData.m_iCurrentVRAM != oldDisplayData.m_iCurrentVRAM)
+ {
+ comGraphics.SetVRAMSize(newDisplayData.m_iCurrentVRAM);
+ fSuccess = comGraphics.isOk();
+ }
+ /* Save guest screen count: */
+ if (fSuccess && isMachineOffline() && newDisplayData.m_cGuestScreenCount != oldDisplayData.m_cGuestScreenCount)
+ {
+ comGraphics.SetMonitorCount(newDisplayData.m_cGuestScreenCount);
+ fSuccess = comGraphics.isOk();
+ }
+ /* Save the Graphics Controller Type: */
+ if (fSuccess && isMachineOffline() && newDisplayData.m_graphicsControllerType != oldDisplayData.m_graphicsControllerType)
+ {
+ comGraphics.SetGraphicsControllerType(newDisplayData.m_graphicsControllerType);
+ fSuccess = comGraphics.isOk();
+ }
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /* Save whether 3D acceleration is enabled: */
+ if (fSuccess && isMachineOffline() && newDisplayData.m_f3dAccelerationEnabled != oldDisplayData.m_f3dAccelerationEnabled)
+ {
+ comGraphics.SetAccelerate3DEnabled(newDisplayData.m_f3dAccelerationEnabled);
+ fSuccess = comGraphics.isOk();
+ }
+#endif
+
+ /* Get machine ID for further activities: */
+ QUuid uMachineId;
+ if (fSuccess)
+ {
+ uMachineId = m_machine.GetId();
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+
+ /* Save guest-screen scale-factor: */
+ if (fSuccess && newDisplayData.m_scaleFactors != oldDisplayData.m_scaleFactors)
+ /* fSuccess = */ gEDataManager->setScaleFactors(newDisplayData.m_scaleFactors, uMachineId);
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsDisplay::saveRemoteDisplayData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Remote Display' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineDisplay &oldDisplayData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineDisplay &newDisplayData = m_pCache->data();
+
+ /* Get remote display server for further activities: */
+ CVRDEServer comServer = m_machine.GetVRDEServer();
+ fSuccess = m_machine.isOk() && comServer.isNotNull();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ else
+ {
+ /* Save whether remote display server is enabled: */
+ if (fSuccess && newDisplayData.m_fRemoteDisplayServerEnabled != oldDisplayData.m_fRemoteDisplayServerEnabled)
+ {
+ comServer.SetEnabled(newDisplayData.m_fRemoteDisplayServerEnabled);
+ fSuccess = comServer.isOk();
+ }
+ /* Save remote display server port: */
+ if (fSuccess && newDisplayData.m_strRemoteDisplayPort != oldDisplayData.m_strRemoteDisplayPort)
+ {
+ comServer.SetVRDEProperty("TCP/Ports", newDisplayData.m_strRemoteDisplayPort);
+ fSuccess = comServer.isOk();
+ }
+ /* Save remote display server auth type: */
+ if (fSuccess && newDisplayData.m_remoteDisplayAuthType != oldDisplayData.m_remoteDisplayAuthType)
+ {
+ comServer.SetAuthType(newDisplayData.m_remoteDisplayAuthType);
+ fSuccess = comServer.isOk();
+ }
+ /* Save remote display server timeout: */
+ if (fSuccess && newDisplayData.m_uRemoteDisplayTimeout != oldDisplayData.m_uRemoteDisplayTimeout)
+ {
+ comServer.SetAuthTimeout(newDisplayData.m_uRemoteDisplayTimeout);
+ fSuccess = comServer.isOk();
+ }
+ /* Save whether remote display server allows multiple connections: */
+ if ( fSuccess
+ && (isMachineOffline() || isMachineSaved())
+ && (newDisplayData.m_fRemoteDisplayMultiConnAllowed != oldDisplayData.m_fRemoteDisplayMultiConnAllowed))
+ {
+ comServer.SetAllowMultiConnection(newDisplayData.m_fRemoteDisplayMultiConnAllowed);
+ fSuccess = comServer.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comServer));
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsDisplay::saveRecordingData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+
+ /* Get old data from cache: */
+ const UIDataSettingsMachineDisplay &oldDisplayData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineDisplay &newDisplayData = m_pCache->data();
+
+ CRecordingSettings recordingSettings = m_machine.GetRecordingSettings();
+ Assert(recordingSettings.isNotNull());
+
+ /** @todo r=andy Make the code below more compact -- too much redundancy here. */
+
+ /* Save new 'Recording' data for online case: */
+ if (isMachineOnline())
+ {
+ /* If 'Recording' was *enabled*: */
+ if (oldDisplayData.m_fRecordingEnabled)
+ {
+ /* Save whether recording is enabled: */
+ if (fSuccess && newDisplayData.m_fRecordingEnabled != oldDisplayData.m_fRecordingEnabled)
+ {
+ recordingSettings.SetEnabled(newDisplayData.m_fRecordingEnabled);
+ fSuccess = recordingSettings.isOk();
+ }
+
+ // We can still save the *screens* option.
+ /* Save recording screens: */
+ if (fSuccess)
+ {
+ CRecordingScreenSettingsVector comRecordingScreenSettingsVector = recordingSettings.GetScreens();
+ for (int iScreenIndex = 0; fSuccess && iScreenIndex < comRecordingScreenSettingsVector.size(); ++iScreenIndex)
+ {
+ if (newDisplayData.m_vecRecordingScreens[iScreenIndex] == oldDisplayData.m_vecRecordingScreens[iScreenIndex])
+ continue;
+
+ CRecordingScreenSettings comRecordingScreenSettings = comRecordingScreenSettingsVector.at(iScreenIndex);
+ comRecordingScreenSettings.SetEnabled(newDisplayData.m_vecRecordingScreens[iScreenIndex]);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ }
+ }
+ /* If 'Recording' was *disabled*: */
+ else
+ {
+ CRecordingScreenSettingsVector comRecordingScreenSettingsVector = recordingSettings.GetScreens();
+ for (int iScreenIndex = 0; fSuccess && iScreenIndex < comRecordingScreenSettingsVector.size(); ++iScreenIndex)
+ {
+ CRecordingScreenSettings comRecordingScreenSettings = comRecordingScreenSettingsVector.at(iScreenIndex);
+
+ // We should save all the options *before* 'Recording' activation.
+ // And finally we should *enable* Recording if necessary.
+ /* Save recording file path: */
+ if (fSuccess && newDisplayData.m_strRecordingFilePath != oldDisplayData.m_strRecordingFilePath)
+ {
+ comRecordingScreenSettings.SetFilename(newDisplayData.m_strRecordingFilePath);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save recording frame width: */
+ if (fSuccess && newDisplayData.m_iRecordingVideoFrameWidth != oldDisplayData.m_iRecordingVideoFrameWidth)
+ {
+ comRecordingScreenSettings.SetVideoWidth(newDisplayData.m_iRecordingVideoFrameWidth);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save recording frame height: */
+ if (fSuccess && newDisplayData.m_iRecordingVideoFrameHeight != oldDisplayData.m_iRecordingVideoFrameHeight)
+ {
+ comRecordingScreenSettings.SetVideoHeight(newDisplayData.m_iRecordingVideoFrameHeight);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save recording frame rate: */
+ if (fSuccess && newDisplayData.m_iRecordingVideoFrameRate != oldDisplayData.m_iRecordingVideoFrameRate)
+ {
+ comRecordingScreenSettings.SetVideoFPS(newDisplayData.m_iRecordingVideoFrameRate);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save recording frame bit rate: */
+ if (fSuccess && newDisplayData.m_iRecordingVideoBitRate != oldDisplayData.m_iRecordingVideoBitRate)
+ {
+ comRecordingScreenSettings.SetVideoRate(newDisplayData.m_iRecordingVideoBitRate);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save recording options: */
+ if (fSuccess && newDisplayData.m_strRecordingVideoOptions != oldDisplayData.m_strRecordingVideoOptions)
+ {
+ comRecordingScreenSettings.SetOptions(newDisplayData.m_strRecordingVideoOptions);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Finally, save the screen's recording state: */
+ /* Note: Must come last, as modifying options with an enabled recording state is not possible. */
+ if (fSuccess && newDisplayData.m_vecRecordingScreens != oldDisplayData.m_vecRecordingScreens)
+ {
+ comRecordingScreenSettings.SetEnabled(newDisplayData.m_vecRecordingScreens[iScreenIndex]);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+
+ if (!fSuccess)
+ {
+ if (!comRecordingScreenSettings.isOk())
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comRecordingScreenSettings));
+ break; /* No point trying to handle the other screens (if any). */
+ }
+ }
+
+ /* Save whether recording is enabled:
+ * Do this last, as after enabling recording no changes via API aren't allowed anymore. */
+ if (fSuccess && newDisplayData.m_fRecordingEnabled != oldDisplayData.m_fRecordingEnabled)
+ {
+ recordingSettings.SetEnabled(newDisplayData.m_fRecordingEnabled);
+ fSuccess = recordingSettings.isOk();
+ }
+ }
+ }
+ /* Save new 'Recording' data for offline case: */
+ else
+ {
+ CRecordingScreenSettingsVector comRecordingScreenSettingsVector = recordingSettings.GetScreens();
+ for (int iScreenIndex = 0; fSuccess && iScreenIndex < comRecordingScreenSettingsVector.size(); ++iScreenIndex)
+ {
+ CRecordingScreenSettings comRecordingScreenSettings = comRecordingScreenSettingsVector.at(iScreenIndex);
+
+ /* Save recording file path: */
+ if (fSuccess && newDisplayData.m_strRecordingFilePath != oldDisplayData.m_strRecordingFilePath)
+ {
+ comRecordingScreenSettings.SetFilename(newDisplayData.m_strRecordingFilePath);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save recording frame width: */
+ if (fSuccess && newDisplayData.m_iRecordingVideoFrameWidth != oldDisplayData.m_iRecordingVideoFrameWidth)
+ {
+ comRecordingScreenSettings.SetVideoWidth(newDisplayData.m_iRecordingVideoFrameWidth);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save recording frame height: */
+ if (fSuccess && newDisplayData.m_iRecordingVideoFrameHeight != oldDisplayData.m_iRecordingVideoFrameHeight)
+ {
+ comRecordingScreenSettings.SetVideoHeight(newDisplayData.m_iRecordingVideoFrameHeight);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save recording frame rate: */
+ if (fSuccess && newDisplayData.m_iRecordingVideoFrameRate != oldDisplayData.m_iRecordingVideoFrameRate)
+ {
+ comRecordingScreenSettings.SetVideoFPS(newDisplayData.m_iRecordingVideoFrameRate);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save recording frame bit rate: */
+ if (fSuccess && newDisplayData.m_iRecordingVideoBitRate != oldDisplayData.m_iRecordingVideoBitRate)
+ {
+ comRecordingScreenSettings.SetVideoRate(newDisplayData.m_iRecordingVideoBitRate);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+ /* Save capture options: */
+ if (fSuccess && newDisplayData.m_strRecordingVideoOptions != oldDisplayData.m_strRecordingVideoOptions)
+ {
+ comRecordingScreenSettings.SetOptions(newDisplayData.m_strRecordingVideoOptions);
+ fSuccess = comRecordingScreenSettings.isOk();
+
+ /** @todo The m_strRecordingVideoOptions must not be used for enabling / disabling the recording features;
+ * instead, SetFeatures() has to be used for 7.0.
+ *
+ * Also for the audio profile settings (Hz rate, bits, ++)
+ * there are now audioHz, audioBits, audioChannels and so on.
+ *
+ * So it's probably best to get rid of m_strRecordingVideoOptions altogether.
+ */
+ QVector<KRecordingFeature> vecFeatures;
+ if (UIDataSettingsMachineDisplay::isRecordingOptionEnabled(newDisplayData.m_strRecordingVideoOptions,
+ UIDataSettingsMachineDisplay::RecordingOption_VC))
+ vecFeatures.append(KRecordingFeature_Video);
+
+ if (UIDataSettingsMachineDisplay::isRecordingOptionEnabled(newDisplayData.m_strRecordingVideoOptions,
+ UIDataSettingsMachineDisplay::RecordingOption_AC))
+ vecFeatures.append(KRecordingFeature_Audio);
+
+ comRecordingScreenSettings.SetFeatures(vecFeatures);
+ }
+
+ /* Finally, save the screen's recording state: */
+ /* Note: Must come last, as modifying options with an enabled recording state is not possible. */
+ if (fSuccess && newDisplayData.m_vecRecordingScreens != oldDisplayData.m_vecRecordingScreens)
+ {
+ comRecordingScreenSettings.SetEnabled(newDisplayData.m_vecRecordingScreens[iScreenIndex]);
+ fSuccess = comRecordingScreenSettings.isOk();
+ }
+
+ if (!fSuccess)
+ {
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comRecordingScreenSettings));
+ break; /* No point trying to handle the other screens (if any). */
+ }
+ }
+
+ /* Save whether recording is enabled:
+ * Do this last, as after enabling recording no changes via API aren't allowed anymore. */
+ if (fSuccess && newDisplayData.m_fRecordingEnabled != oldDisplayData.m_fRecordingEnabled)
+ {
+ recordingSettings.SetEnabled(newDisplayData.m_fRecordingEnabled);
+ fSuccess = recordingSettings.isOk();
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ {
+ if (!recordingSettings.isOk())
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(recordingSettings));
+ else if (!m_machine.isOk()) /* Machine could indicate an error when saving the settings. */
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsDisplay.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsDisplay.h
new file mode 100644
index 00000000..b316ccd3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsDisplay.h
@@ -0,0 +1,196 @@
+/* $Id: UIMachineSettingsDisplay.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsDisplay class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsDisplay_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsDisplay_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* COM includes: */
+#include "CGuestOSType.h"
+
+/* Forward declarations: */
+class QITabWidget;
+class UIGraphicsControllerEditor;
+#ifdef VBOX_WITH_3D_ACCELERATION
+class UIDisplayScreenFeaturesEditor;
+#endif
+class UIMonitorCountEditor;
+class UIRecordingSettingsEditor;
+class UIScaleFactorEditor;
+class UIVideoMemoryEditor;
+class UIVRDESettingsEditor;
+struct UIDataSettingsMachineDisplay;
+typedef UISettingsCache<UIDataSettingsMachineDisplay> UISettingsCacheMachineDisplay;
+
+/** Machine settings: Display page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsDisplay : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Display settings page. */
+ UIMachineSettingsDisplay();
+ /** Destructs Display settings page. */
+ virtual ~UIMachineSettingsDisplay() RT_OVERRIDE;
+
+ /** Defines @a comGuestOSType. */
+ void setGuestOSType(CGuestOSType comGuestOSType);
+
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /** Returns whether 3D Acceleration is enabled. */
+ bool isAcceleration3DSelected() const;
+#endif
+
+ /** Returns recommended graphics controller type. */
+ KGraphicsControllerType graphicsControllerTypeRecommended() const;
+ /** Returns current graphics controller type. */
+ KGraphicsControllerType graphicsControllerTypeCurrent() const;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ virtual bool validate(QList<UIValidationMessage> &messages) RT_OVERRIDE;
+
+ /** Defines TAB order for passed @a pWidget. */
+ virtual void setOrderAfter(QWidget *pWidget) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles monitor count change. */
+ void sltHandleMonitorCountChange();
+ /** Handles Graphics Controller combo change. */
+ void sltHandleGraphicsControllerComboChange();
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /** Handles 3D Acceleration feature state change. */
+ void sltHandle3DAccelerationFeatureStateChange();
+#endif
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares 'Screen' tab. */
+ void prepareTabScreen();
+ /** Prepares 'Remote Display' tab. */
+ void prepareTabRemoteDisplay();
+ /** Prepares 'Recording' tab. */
+ void prepareTabRecording();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Returns whether the VRAM requirements are important. */
+ bool shouldWeWarnAboutLowVRAM();
+
+ /** Updates guest-screen count. */
+ void updateGuestScreenCount();
+ /** Saves existing data from cache. */
+ bool saveData();
+ /** Saves existing 'Screen' data from cache. */
+ bool saveScreenData();
+ /** Saves existing 'Remote Display' data from cache. */
+ bool saveRemoteDisplayData();
+ /** Saves existing 'Recording' data from cache. */
+ bool saveRecordingData();
+
+ /** Holds the guest OS type ID. */
+ CGuestOSType m_comGuestOSType;
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /** Holds whether the guest OS supports WDDM. */
+ bool m_fWddmModeSupported;
+#endif
+ /** Holds recommended graphics controller type. */
+ KGraphicsControllerType m_enmGraphicsControllerTypeRecommended;
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheMachineDisplay *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the tab-widget instance. */
+ QITabWidget *m_pTabWidget;
+
+ /** Holds the 'Screen' tab instance. */
+ QWidget *m_pTabScreen;
+ /** Holds the video memory size editor instance. */
+ UIVideoMemoryEditor *m_pEditorVideoMemorySize;
+ /** Holds the monitor count spinbox instance. */
+ UIMonitorCountEditor *m_pEditorMonitorCount;
+ /** Holds the scale factor editor instance. */
+ UIScaleFactorEditor *m_pEditorScaleFactor;
+ /** Holds the graphics controller editor instance. */
+ UIGraphicsControllerEditor *m_pEditorGraphicsController;
+#ifdef VBOX_WITH_3D_ACCELERATION
+ /** Holds the display screen features editor instance. */
+ UIDisplayScreenFeaturesEditor *m_pEditorDisplayScreenFeatures;
+#endif
+
+ /** Holds the 'Remote Display' tab instance. */
+ QWidget *m_pTabRemoteDisplay;
+ /** Holds the VRDE settings editor instance. */
+ UIVRDESettingsEditor *m_pEditorVRDESettings;
+
+ /** Holds the 'Recording' tab instance. */
+ QWidget *m_pTabRecording;
+ /** Holds the Recording settings editor instance. */
+ UIRecordingSettingsEditor *m_pEditorRecordingSettings;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsDisplay_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsGeneral.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsGeneral.cpp
new file mode 100644
index 00000000..eadb1267
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsGeneral.cpp
@@ -0,0 +1,978 @@
+/* $Id: UIMachineSettingsGeneral.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsGeneral class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QFileInfo>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QITabWidget.h"
+#include "UIAddDiskEncryptionPasswordDialog.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIDescriptionEditor.h"
+#include "UIDiskEncryptionSettingsEditor.h"
+#include "UIDragAndDropEditor.h"
+#include "UIErrorString.h"
+#include "UIMachineSettingsGeneral.h"
+#include "UIModalWindowManager.h"
+#include "UINameAndSystemEditor.h"
+#include "UIProgressObject.h"
+#include "UISharedClipboardEditor.h"
+#include "UISnapshotFolderEditor.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "CExtPack.h"
+#include "CExtPackManager.h"
+#include "CMedium.h"
+#include "CMediumAttachment.h"
+#include "CProgress.h"
+
+
+/** Machine settings: General page data structure. */
+struct UIDataSettingsMachineGeneral
+{
+ /** Constructs data. */
+ UIDataSettingsMachineGeneral()
+ : m_strName(QString())
+ , m_strGuestOsTypeId(QString())
+ , m_strSnapshotsFolder(QString())
+ , m_strSnapshotsHomeDir(QString())
+ , m_clipboardMode(KClipboardMode_Disabled)
+ , m_dndMode(KDnDMode_Disabled)
+ , m_strDescription(QString())
+ , m_fEncryptionEnabled(false)
+ , m_fEncryptionCipherChanged(false)
+ , m_fEncryptionPasswordChanged(false)
+ , m_enmEncryptionCipherType(UIDiskEncryptionCipherType_Max)
+ , m_strEncryptionPassword(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineGeneral &other) const
+ {
+ return true
+ && (m_strName == other.m_strName)
+ && (m_strGuestOsTypeId == other.m_strGuestOsTypeId)
+ && (m_strSnapshotsFolder == other.m_strSnapshotsFolder)
+ && (m_clipboardMode == other.m_clipboardMode)
+ && (m_dndMode == other.m_dndMode)
+ && (m_strDescription == other.m_strDescription)
+ && (m_fEncryptionEnabled == other.m_fEncryptionEnabled)
+ && (m_fEncryptionCipherChanged == other.m_fEncryptionCipherChanged)
+ && (m_fEncryptionPasswordChanged == other.m_fEncryptionPasswordChanged)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineGeneral &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineGeneral &other) const { return !equal(other); }
+
+ /** Holds the VM name. */
+ QString m_strName;
+ /** Holds the VM OS type ID. */
+ QString m_strGuestOsTypeId;
+
+ /** Holds the VM snapshot folder. */
+ QString m_strSnapshotsFolder;
+ /** Holds the default VM snapshot folder. */
+ QString m_strSnapshotsHomeDir;
+ /** Holds the VM clipboard mode. */
+ KClipboardMode m_clipboardMode;
+ /** Holds the VM drag&drop mode. */
+ KDnDMode m_dndMode;
+
+ /** Holds the VM description. */
+ QString m_strDescription;
+
+ /** Holds whether the encryption is enabled. */
+ bool m_fEncryptionEnabled;
+ /** Holds whether the encryption cipher was changed. */
+ bool m_fEncryptionCipherChanged;
+ /** Holds whether the encryption password was changed. */
+ bool m_fEncryptionPasswordChanged;
+ /** Holds the encryption cipher index. */
+ UIDiskEncryptionCipherType m_enmEncryptionCipherType;
+ /** Holds the encryption password. */
+ QString m_strEncryptionPassword;
+ /** Holds the encrypted medium ids. */
+ EncryptedMediumMap m_encryptedMedia;
+ /** Holds the encryption passwords. */
+ EncryptionPasswordMap m_encryptionPasswords;
+};
+
+
+UIMachineSettingsGeneral::UIMachineSettingsGeneral()
+ : m_fEncryptionCipherChanged(false)
+ , m_fEncryptionPasswordChanged(false)
+ , m_pCache(0)
+ , m_pTabWidget(0)
+ , m_pTabBasic(0)
+ , m_pEditorNameAndSystem(0)
+ , m_pTabAdvanced(0)
+ , m_pEditorSnapshotFolder(0)
+ , m_pEditorClipboard(0)
+ , m_pEditorDragAndDrop(0)
+ , m_pTabDescription(0)
+ , m_pEditorDescription(0)
+ , m_pTabEncryption(0)
+ , m_pEditorDiskEncryptionSettings(0)
+{
+ prepare();
+}
+
+UIMachineSettingsGeneral::~UIMachineSettingsGeneral()
+{
+ cleanup();
+}
+
+CGuestOSType UIMachineSettingsGeneral::guestOSType() const
+{
+ AssertPtrReturn(m_pEditorNameAndSystem, CGuestOSType());
+ return m_pEditorNameAndSystem->type();
+}
+
+bool UIMachineSettingsGeneral::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsGeneral::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Prepare old data: */
+ UIDataSettingsMachineGeneral oldGeneralData;
+
+ /* Gather old 'Basic' data: */
+ oldGeneralData.m_strName = m_machine.GetName();
+ oldGeneralData.m_strGuestOsTypeId = m_machine.GetOSTypeId();
+
+ /* Gather old 'Advanced' data: */
+ oldGeneralData.m_strSnapshotsFolder = m_machine.GetSnapshotFolder();
+ oldGeneralData.m_strSnapshotsHomeDir = QFileInfo(m_machine.GetSettingsFilePath()).absolutePath();
+ oldGeneralData.m_clipboardMode = m_machine.GetClipboardMode();
+ oldGeneralData.m_dndMode = m_machine.GetDnDMode();
+
+ /* Gather old 'Description' data: */
+ oldGeneralData.m_strDescription = m_machine.GetDescription();
+
+ /* Gather old 'Encryption' data: */
+ QString strCipher;
+ bool fEncryptionCipherCommon = true;
+ /* Prepare the map of the encrypted media: */
+ EncryptedMediumMap encryptedMedia;
+ foreach (const CMediumAttachment &attachment, m_machine.GetMediumAttachments())
+ {
+ /* Check hard-drive attachments only: */
+ if (attachment.GetType() == KDeviceType_HardDisk)
+ {
+ /* Get the attachment medium base: */
+ const CMedium comMedium = attachment.GetMedium();
+ /* Check medium encryption attributes: */
+ QString strCurrentCipher;
+ const QString strCurrentPasswordId = comMedium.GetEncryptionSettings(strCurrentCipher);
+ if (comMedium.isOk())
+ {
+ encryptedMedia.insert(strCurrentPasswordId, comMedium.GetId());
+ if (strCurrentCipher != strCipher)
+ {
+ if (strCipher.isNull())
+ strCipher = strCurrentCipher;
+ else
+ fEncryptionCipherCommon = false;
+ }
+ }
+ }
+ }
+ oldGeneralData.m_fEncryptionEnabled = !encryptedMedia.isEmpty();
+ oldGeneralData.m_fEncryptionCipherChanged = false;
+ oldGeneralData.m_fEncryptionPasswordChanged = false;
+ if (fEncryptionCipherCommon)
+ oldGeneralData.m_enmEncryptionCipherType = gpConverter->fromInternalString<UIDiskEncryptionCipherType>(strCipher);
+ oldGeneralData.m_encryptedMedia = encryptedMedia;
+
+ /* Cache old data: */
+ m_pCache->cacheInitialData(oldGeneralData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsGeneral::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Get old data from cache: */
+ const UIDataSettingsMachineGeneral &oldGeneralData = m_pCache->base();
+
+ /* Load old 'Basic' data from cache: */
+ if (m_pEditorNameAndSystem)
+ {
+ m_pEditorNameAndSystem->setName(oldGeneralData.m_strName);
+ m_pEditorNameAndSystem->setTypeId(oldGeneralData.m_strGuestOsTypeId);
+ }
+
+ /* Load old 'Advanced' data from cache: */
+ if (m_pEditorSnapshotFolder)
+ {
+ m_pEditorSnapshotFolder->setPath(oldGeneralData.m_strSnapshotsFolder);
+ m_pEditorSnapshotFolder->setInitialPath(oldGeneralData.m_strSnapshotsHomeDir);
+ }
+ if (m_pEditorClipboard)
+ m_pEditorClipboard->setValue(oldGeneralData.m_clipboardMode);
+ if (m_pEditorDragAndDrop)
+ m_pEditorDragAndDrop->setValue(oldGeneralData.m_dndMode);
+
+ /* Load old 'Description' data from cache: */
+ if (m_pEditorDescription)
+ m_pEditorDescription->setValue(oldGeneralData.m_strDescription);
+
+ /* Load old 'Encryption' data from cache: */
+ if (m_pEditorDiskEncryptionSettings)
+ {
+ m_pEditorDiskEncryptionSettings->setFeatureEnabled(oldGeneralData.m_fEncryptionEnabled);
+ m_pEditorDiskEncryptionSettings->setCipherType(oldGeneralData.m_enmEncryptionCipherType);
+ }
+ if (m_fEncryptionCipherChanged)
+ m_fEncryptionCipherChanged = oldGeneralData.m_fEncryptionCipherChanged;
+ if (m_fEncryptionPasswordChanged)
+ m_fEncryptionPasswordChanged = oldGeneralData.m_fEncryptionPasswordChanged;
+
+ /* Polish page finally: */
+ polishPage();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMachineSettingsGeneral::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsMachineGeneral newGeneralData;
+
+ /* Gather new 'Basic' data: */
+ if (m_pEditorNameAndSystem)
+ {
+ newGeneralData.m_strName = m_pEditorNameAndSystem->name();
+ newGeneralData.m_strGuestOsTypeId = m_pEditorNameAndSystem->typeId();
+ }
+
+ /* Gather new 'Advanced' data: */
+ if (m_pEditorSnapshotFolder)
+ newGeneralData.m_strSnapshotsFolder = m_pEditorSnapshotFolder->path();
+ if (m_pEditorClipboard)
+ newGeneralData.m_clipboardMode = m_pEditorClipboard->value();
+ if (m_pEditorDragAndDrop)
+ newGeneralData.m_dndMode = m_pEditorDragAndDrop->value();
+
+ /* Gather new 'Description' data: */
+ if (m_pEditorDescription)
+ newGeneralData.m_strDescription = m_pEditorDescription->value().isEmpty()
+ ? QString() : m_pEditorDescription->value();
+
+ /* Gather new 'Encryption' data: */
+ if (m_pEditorDiskEncryptionSettings)
+ {
+ newGeneralData.m_fEncryptionEnabled = m_pEditorDiskEncryptionSettings->isFeatureEnabled();
+ newGeneralData.m_fEncryptionCipherChanged = m_fEncryptionCipherChanged;
+ newGeneralData.m_fEncryptionPasswordChanged = m_fEncryptionPasswordChanged;
+ newGeneralData.m_enmEncryptionCipherType = m_pEditorDiskEncryptionSettings->cipherType();
+ newGeneralData.m_strEncryptionPassword = m_pEditorDiskEncryptionSettings->password1();
+ newGeneralData.m_encryptedMedia = m_pCache->base().m_encryptedMedia;
+ /* If encryption status, cipher or password is changed: */
+ if (newGeneralData.m_fEncryptionEnabled != m_pCache->base().m_fEncryptionEnabled ||
+ newGeneralData.m_fEncryptionCipherChanged != m_pCache->base().m_fEncryptionCipherChanged ||
+ newGeneralData.m_fEncryptionPasswordChanged != m_pCache->base().m_fEncryptionPasswordChanged)
+ {
+ /* Ask for the disk encryption passwords if necessary: */
+ if (!m_pCache->base().m_encryptedMedia.isEmpty())
+ {
+ /* Create corresponding dialog: */
+ QWidget *pDlgParent = windowManager().realParentWindow(window());
+ QPointer<UIAddDiskEncryptionPasswordDialog> pDlg =
+ new UIAddDiskEncryptionPasswordDialog(pDlgParent,
+ newGeneralData.m_strName,
+ newGeneralData.m_encryptedMedia);
+ /* Execute it and acquire the result: */
+ if (pDlg->exec() == QDialog::Accepted)
+ newGeneralData.m_encryptionPasswords = pDlg->encryptionPasswords();
+ /* Delete dialog if still valid: */
+ if (pDlg)
+ delete pDlg;
+ }
+ }
+ }
+
+ /* Cache new data: */
+ m_pCache->cacheCurrentData(newGeneralData);
+}
+
+void UIMachineSettingsGeneral::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+bool UIMachineSettingsGeneral::validate(QList<UIValidationMessage> &messages)
+{
+ /* Pass by default: */
+ bool fPass = true;
+
+ /* Prepare message: */
+ UIValidationMessage message;
+
+ /* 'Basic' tab validations: */
+ message.first = UITranslator::removeAccelMark(m_pTabWidget->tabText(0));
+ message.second.clear();
+
+ /* VM name validation: */
+ AssertPtrReturn(m_pEditorNameAndSystem, false);
+ if (m_pEditorNameAndSystem->name().trimmed().isEmpty())
+ {
+ message.second << tr("No name specified for the virtual machine.");
+ fPass = false;
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+
+ /* 'Encryption' tab validations: */
+ message.first = UITranslator::removeAccelMark(m_pTabWidget->tabText(3));
+ message.second.clear();
+
+ /* Encryption validation: */
+ AssertPtrReturn(m_pEditorDiskEncryptionSettings, false);
+ if (m_pEditorDiskEncryptionSettings->isFeatureEnabled())
+ {
+ /* Encryption Extension Pack presence test: */
+ CExtPackManager extPackManager = uiCommon().virtualBox().GetExtensionPackManager();
+ if (!extPackManager.isNull() && !extPackManager.IsExtPackUsable(GUI_ExtPackName))
+ {
+ message.second << tr("You are trying to enable disk encryption for this virtual machine. "
+ "However, this requires the <i>%1</i> to be installed. "
+ "Please install the Extension Pack from the VirtualBox download site.")
+ .arg(GUI_ExtPackName);
+ fPass = false;
+ }
+
+ /* Cipher should be chosen if once changed: */
+ if ( !m_pCache->base().m_fEncryptionEnabled
+ || m_fEncryptionCipherChanged)
+ {
+ if (m_pEditorDiskEncryptionSettings->cipherType() == UIDiskEncryptionCipherType_Unchanged)
+ message.second << tr("Disk encryption cipher type not specified.");
+ fPass = false;
+ }
+
+ /* Password should be entered and confirmed if once changed: */
+ if (!m_pCache->base().m_fEncryptionEnabled ||
+ m_fEncryptionPasswordChanged)
+ {
+ if (m_pEditorDiskEncryptionSettings->password1().isEmpty())
+ message.second << tr("Disk encryption password empty.");
+ else
+ if (m_pEditorDiskEncryptionSettings->password1() !=
+ m_pEditorDiskEncryptionSettings->password2())
+ message.second << tr("Disk encryption passwords do not match.");
+ fPass = false;
+ }
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+
+ /* Return result: */
+ return fPass;
+}
+
+void UIMachineSettingsGeneral::setOrderAfter(QWidget *pWidget)
+{
+ /* 'Basic' tab: */
+ AssertPtrReturnVoid(pWidget);
+ AssertPtrReturnVoid(m_pTabWidget);
+ AssertPtrReturnVoid(m_pTabWidget->focusProxy());
+ AssertPtrReturnVoid(m_pEditorNameAndSystem);
+ setTabOrder(pWidget, m_pTabWidget->focusProxy());
+ setTabOrder(m_pTabWidget->focusProxy(), m_pEditorNameAndSystem);
+
+ /* 'Advanced' tab: */
+ AssertPtrReturnVoid(m_pEditorSnapshotFolder);
+ AssertPtrReturnVoid(m_pEditorClipboard);
+ AssertPtrReturnVoid(m_pEditorDragAndDrop);
+ setTabOrder(m_pEditorNameAndSystem, m_pEditorSnapshotFolder);
+ setTabOrder(m_pEditorSnapshotFolder, m_pEditorClipboard);
+ setTabOrder(m_pEditorClipboard, m_pEditorDragAndDrop);
+
+ /* 'Description' tab: */
+ AssertPtrReturnVoid(m_pEditorDescription);
+ setTabOrder(m_pEditorDragAndDrop, m_pEditorDescription);
+}
+
+void UIMachineSettingsGeneral::retranslateUi()
+{
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabBasic), tr("Basi&c"));
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabAdvanced), tr("A&dvanced"));
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabDescription), tr("D&escription"));
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabEncryption), tr("Disk Enc&ryption"));
+
+ /* These editors have own labels, but we want them to be properly layouted according to each other: */
+ int iMinimumLayoutHint = 0;
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorSnapshotFolder->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorClipboard->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorDragAndDrop->minimumLabelHorizontalHint());
+ m_pEditorSnapshotFolder->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorClipboard->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorDragAndDrop->setMinimumLayoutIndent(iMinimumLayoutHint);
+}
+
+void UIMachineSettingsGeneral::polishPage()
+{
+ /* Polish 'Basic' availability: */
+ AssertPtrReturnVoid(m_pEditorNameAndSystem);
+ m_pEditorNameAndSystem->setNameStuffEnabled(isMachineOffline() || isMachineSaved());
+ m_pEditorNameAndSystem->setPathStuffEnabled(isMachineOffline());
+ m_pEditorNameAndSystem->setOSTypeStuffEnabled(isMachineOffline());
+
+ /* Polish 'Advanced' availability: */
+ AssertPtrReturnVoid(m_pEditorSnapshotFolder);
+ AssertPtrReturnVoid(m_pEditorClipboard);
+ AssertPtrReturnVoid(m_pEditorDragAndDrop);
+ m_pEditorSnapshotFolder->setEnabled(isMachineOffline());
+ m_pEditorClipboard->setEnabled(isMachineInValidMode());
+ m_pEditorDragAndDrop->setEnabled(isMachineInValidMode());
+
+ /* Polish 'Description' availability: */
+ AssertPtrReturnVoid(m_pEditorDescription);
+ m_pEditorDescription->setEnabled(isMachineInValidMode());
+
+ /* Polish 'Encryption' availability: */
+ AssertPtrReturnVoid(m_pEditorDiskEncryptionSettings);
+ m_pEditorDiskEncryptionSettings->setEnabled(isMachineOffline());
+}
+
+void UIMachineSettingsGeneral::sltHandleEncryptionCipherChanged()
+{
+ m_fEncryptionCipherChanged = true;
+ revalidate();
+}
+
+void UIMachineSettingsGeneral::sltHandleEncryptionPasswordChanged()
+{
+ m_fEncryptionCipherChanged = true;
+ m_fEncryptionPasswordChanged = true;
+ revalidate();
+}
+
+void UIMachineSettingsGeneral::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheMachineGeneral;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsGeneral::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QHBoxLayout *pLayoutMain = new QHBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare tab-widget: */
+ m_pTabWidget = new QITabWidget(this);
+ if (m_pTabWidget)
+ {
+ /* Prepare each tab separately: */
+ prepareTabBasic();
+ prepareTabAdvanced();
+ prepareTabDescription();
+ prepareTabEncryption();
+
+ pLayoutMain->addWidget(m_pTabWidget);
+ }
+ }
+}
+
+void UIMachineSettingsGeneral::prepareTabBasic()
+{
+ /* Prepare 'Basic' tab: */
+ m_pTabBasic = new QWidget;
+ if (m_pTabBasic)
+ {
+ /* Prepare 'Basic' tab layout: */
+ QVBoxLayout *pLayoutBasic = new QVBoxLayout(m_pTabBasic);
+ if (pLayoutBasic)
+ {
+ /* Prepare name and system editor: */
+ m_pEditorNameAndSystem = new UINameAndSystemEditor(m_pTabBasic);
+ if (m_pEditorNameAndSystem)
+ pLayoutBasic->addWidget(m_pEditorNameAndSystem);
+
+ pLayoutBasic->addStretch();
+ }
+
+ m_pTabWidget->addTab(m_pTabBasic, QString());
+ }
+}
+
+void UIMachineSettingsGeneral::prepareTabAdvanced()
+{
+ /* Prepare 'Advanced' tab: */
+ m_pTabAdvanced = new QWidget;
+ if (m_pTabAdvanced)
+ {
+ /* Prepare 'Advanced' tab layout: */
+ QVBoxLayout *pLayoutAdvanced = new QVBoxLayout(m_pTabAdvanced);
+ if (pLayoutAdvanced)
+ {
+ /* Prepare snapshot folder editor: */
+ m_pEditorSnapshotFolder = new UISnapshotFolderEditor(m_pTabAdvanced);
+ if (m_pEditorSnapshotFolder)
+ pLayoutAdvanced->addWidget(m_pEditorSnapshotFolder);
+
+ /* Prepare clipboard editor: */
+ m_pEditorClipboard = new UISharedClipboardEditor(m_pTabAdvanced);
+ if (m_pEditorClipboard)
+ pLayoutAdvanced->addWidget(m_pEditorClipboard);
+
+ /* Prepare drag&drop editor: */
+ m_pEditorDragAndDrop = new UIDragAndDropEditor(m_pTabAdvanced);
+ if (m_pEditorDragAndDrop)
+ pLayoutAdvanced->addWidget(m_pEditorDragAndDrop);
+
+ pLayoutAdvanced->addStretch();
+ }
+
+ m_pTabWidget->addTab(m_pTabAdvanced, QString());
+ }
+}
+
+void UIMachineSettingsGeneral::prepareTabDescription()
+{
+ /* Prepare 'Description' tab: */
+ m_pTabDescription = new QWidget;
+ if (m_pTabDescription)
+ {
+ /* Prepare 'Description' tab layout: */
+ QVBoxLayout *pLayoutDescription = new QVBoxLayout(m_pTabDescription);
+ if (pLayoutDescription)
+ {
+ /* Prepare description editor: */
+ m_pEditorDescription = new UIDescriptionEditor(m_pTabDescription);
+ if (m_pEditorDescription)
+ {
+ m_pEditorDescription->setObjectName(QStringLiteral("m_pEditorDescription"));
+ pLayoutDescription->addWidget(m_pEditorDescription);
+ }
+ }
+
+ m_pTabWidget->addTab(m_pTabDescription, QString());
+ }
+}
+
+void UIMachineSettingsGeneral::prepareTabEncryption()
+{
+ /* Prepare 'Encryption' tab: */
+ m_pTabEncryption = new QWidget;
+ if (m_pTabEncryption)
+ {
+ /* Prepare 'Encryption' tab layout: */
+ QVBoxLayout *pLayoutEncryption = new QVBoxLayout(m_pTabEncryption);
+ if (pLayoutEncryption)
+ {
+ /* Prepare disk encryption settings editor: */
+ m_pEditorDiskEncryptionSettings = new UIDiskEncryptionSettingsEditor(m_pTabEncryption);
+ if (m_pEditorDiskEncryptionSettings)
+ pLayoutEncryption->addWidget(m_pEditorDiskEncryptionSettings);
+
+ pLayoutEncryption->addStretch();
+ }
+
+ m_pTabWidget->addTab(m_pTabEncryption, QString());
+ }
+}
+
+void UIMachineSettingsGeneral::prepareConnections()
+{
+ /* Configure 'Basic' connections: */
+ connect(m_pEditorNameAndSystem, &UINameAndSystemEditor::sigOsTypeChanged,
+ this, &UIMachineSettingsGeneral::revalidate);
+ connect(m_pEditorNameAndSystem, &UINameAndSystemEditor::sigNameChanged,
+ this, &UIMachineSettingsGeneral::revalidate);
+
+ /* Configure 'Encryption' connections: */
+ connect(m_pEditorDiskEncryptionSettings, &UIDiskEncryptionSettingsEditor::sigStatusChanged,
+ this, &UIMachineSettingsGeneral::revalidate);
+ connect(m_pEditorDiskEncryptionSettings, &UIDiskEncryptionSettingsEditor::sigCipherChanged,
+ this, &UIMachineSettingsGeneral::sltHandleEncryptionCipherChanged);
+ connect(m_pEditorDiskEncryptionSettings, &UIDiskEncryptionSettingsEditor::sigPasswordChanged,
+ this, &UIMachineSettingsGeneral::sltHandleEncryptionPasswordChanged);
+}
+
+void UIMachineSettingsGeneral::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIMachineSettingsGeneral::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save general settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* Save 'Basic' data from cache: */
+ if (fSuccess)
+ fSuccess = saveBasicData();
+ /* Save 'Advanced' data from cache: */
+ if (fSuccess)
+ fSuccess = saveAdvancedData();
+ /* Save 'Description' data from cache: */
+ if (fSuccess)
+ fSuccess = saveDescriptionData();
+ /* Save 'Encryption' data from cache: */
+ if (fSuccess)
+ fSuccess = saveEncryptionData();
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsGeneral::saveBasicData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Basic' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineGeneral &oldGeneralData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineGeneral &newGeneralData = m_pCache->data();
+
+ /* Save machine OS type ID: */
+ if (isMachineOffline() && newGeneralData.m_strGuestOsTypeId != oldGeneralData.m_strGuestOsTypeId)
+ {
+ if (fSuccess)
+ {
+ m_machine.SetOSTypeId(newGeneralData.m_strGuestOsTypeId);
+ fSuccess = m_machine.isOk();
+ }
+ if (fSuccess)
+ {
+ // Must update long mode CPU feature bit when os type changed:
+ CVirtualBox vbox = uiCommon().virtualBox();
+ // Should we check global object getters?
+ const CGuestOSType &comNewType = vbox.GetGuestOSType(newGeneralData.m_strGuestOsTypeId);
+ m_machine.SetCPUProperty(KCPUPropertyType_LongMode, comNewType.GetIs64Bit());
+ fSuccess = m_machine.isOk();
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsGeneral::saveAdvancedData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Advanced' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineGeneral &oldGeneralData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineGeneral &newGeneralData = m_pCache->data();
+
+ /* Save machine clipboard mode: */
+ if (fSuccess && newGeneralData.m_clipboardMode != oldGeneralData.m_clipboardMode)
+ {
+ m_machine.SetClipboardMode(newGeneralData.m_clipboardMode);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save machine D&D mode: */
+ if (fSuccess && newGeneralData.m_dndMode != oldGeneralData.m_dndMode)
+ {
+ m_machine.SetDnDMode(newGeneralData.m_dndMode);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save machine snapshot folder: */
+ if (fSuccess && isMachineOffline() && newGeneralData.m_strSnapshotsFolder != oldGeneralData.m_strSnapshotsFolder)
+ {
+ m_machine.SetSnapshotFolder(newGeneralData.m_strSnapshotsFolder);
+ fSuccess = m_machine.isOk();
+ }
+ // VM name from 'Basic' data should go after the snapshot folder from the 'Advanced' data
+ // as otherwise VM rename magic can collide with the snapshot folder one.
+ /* Save machine name: */
+ if (fSuccess && (isMachineOffline() || isMachineSaved()) && newGeneralData.m_strName != oldGeneralData.m_strName)
+ {
+ m_machine.SetName(newGeneralData.m_strName);
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsGeneral::saveDescriptionData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Description' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineGeneral &oldGeneralData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineGeneral &newGeneralData = m_pCache->data();
+
+ /* Save machine description: */
+ if (fSuccess && newGeneralData.m_strDescription != oldGeneralData.m_strDescription)
+ {
+ m_machine.SetDescription(newGeneralData.m_strDescription);
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsGeneral::saveEncryptionData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Encryption' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineGeneral &oldGeneralData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineGeneral &newGeneralData = m_pCache->data();
+
+ /* Make sure it either encryption state is changed itself,
+ * or the encryption was already enabled and either cipher or password is changed. */
+ if ( isMachineOffline()
+ && ( newGeneralData.m_fEncryptionEnabled != oldGeneralData.m_fEncryptionEnabled
+ || ( oldGeneralData.m_fEncryptionEnabled
+ && ( newGeneralData.m_fEncryptionCipherChanged != oldGeneralData.m_fEncryptionCipherChanged
+ || newGeneralData.m_fEncryptionPasswordChanged != oldGeneralData.m_fEncryptionPasswordChanged))))
+ {
+ /* Get machine name for further activities: */
+ QString strMachineName;
+ if (fSuccess)
+ {
+ strMachineName = m_machine.GetName();
+ fSuccess = m_machine.isOk();
+ }
+ /* Get machine attachments for further activities: */
+ CMediumAttachmentVector attachments;
+ if (fSuccess)
+ {
+ attachments = m_machine.GetMediumAttachments();
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+
+ /* For each attachment: */
+ for (int iIndex = 0; fSuccess && iIndex < attachments.size(); ++iIndex)
+ {
+ /* Get current attachment: */
+ const CMediumAttachment &comAttachment = attachments.at(iIndex);
+
+ /* Get attachment type for further activities: */
+ KDeviceType enmType = KDeviceType_Null;
+ if (fSuccess)
+ {
+ enmType = comAttachment.GetType();
+ fSuccess = comAttachment.isOk();
+ }
+ /* Get attachment medium for further activities: */
+ CMedium comMedium;
+ if (fSuccess)
+ {
+ comMedium = comAttachment.GetMedium();
+ fSuccess = comAttachment.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comAttachment));
+ else
+ {
+ /* Pass hard-drives only: */
+ if (enmType != KDeviceType_HardDisk)
+ continue;
+
+ /* Get medium id for further activities: */
+ QUuid uMediumId;
+ if (fSuccess)
+ {
+ uMediumId = comMedium.GetId();
+ fSuccess = comMedium.isOk();
+ }
+
+ /* Create encryption update progress: */
+ CProgress comProgress;
+ if (fSuccess)
+ {
+ /* Cipher attribute changed? */
+ const QString strNewCipher
+ = newGeneralData.m_fEncryptionCipherChanged && newGeneralData.m_fEncryptionEnabled
+ ? gpConverter->toInternalString(newGeneralData.m_enmEncryptionCipherType)
+ : QString();
+
+ /* Password attribute changed? */
+ QString strNewPassword;
+ QString strNewPasswordId;
+ if (newGeneralData.m_fEncryptionPasswordChanged)
+ {
+ strNewPassword = newGeneralData.m_fEncryptionEnabled ?
+ newGeneralData.m_strEncryptionPassword : QString();
+ strNewPasswordId = newGeneralData.m_fEncryptionEnabled ?
+ strMachineName : QString();
+ }
+
+ /* Get the maps of encrypted media and their passwords: */
+ const EncryptedMediumMap &encryptedMedium = newGeneralData.m_encryptedMedia;
+ const EncryptionPasswordMap &encryptionPasswords = newGeneralData.m_encryptionPasswords;
+
+ /* Check if old password exists/provided: */
+ const QString strOldPasswordId = encryptedMedium.key(uMediumId);
+ const QString strOldPassword = encryptionPasswords.value(strOldPasswordId);
+
+ /* Create encryption progress: */
+ comProgress = comMedium.ChangeEncryption(strOldPassword,
+ strNewCipher,
+ strNewPassword,
+ strNewPasswordId);
+ fSuccess = comMedium.isOk();
+ }
+
+ /* Create encryption update progress object: */
+ QPointer<UIProgressObject> pObject;
+ if (fSuccess)
+ {
+ pObject = new UIProgressObject(comProgress);
+ if (pObject)
+ {
+ connect(pObject.data(), &UIProgressObject::sigProgressChange,
+ this, &UIMachineSettingsGeneral::sigOperationProgressChange,
+ Qt::QueuedConnection);
+ connect(pObject.data(), &UIProgressObject::sigProgressError,
+ this, &UIMachineSettingsGeneral::sigOperationProgressError,
+ Qt::BlockingQueuedConnection);
+ pObject->exec();
+ if (pObject)
+ delete pObject;
+ else
+ {
+ // Premature application shutdown,
+ // exit immediately:
+ return true;
+ }
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comMedium));
+ }
+ }
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsGeneral.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsGeneral.h
new file mode 100644
index 00000000..80431d38
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsGeneral.h
@@ -0,0 +1,174 @@
+/* $Id: UIMachineSettingsGeneral.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsGeneral class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsGeneral_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsGeneral_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class QITabWidget;
+class UIDescriptionEditor;
+class UIDiskEncryptionSettingsEditor;
+class UIDragAndDropEditor;
+class UINameAndSystemEditor;
+class UISharedClipboardEditor;
+class UISnapshotFolderEditor;
+struct UIDataSettingsMachineGeneral;
+typedef UISettingsCache<UIDataSettingsMachineGeneral> UISettingsCacheMachineGeneral;
+
+/** Machine settings: General page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsGeneral : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs General settings page. */
+ UIMachineSettingsGeneral();
+ /** Destructs General settings page. */
+ virtual ~UIMachineSettingsGeneral() RT_OVERRIDE;
+
+ /** Returns the VM OS type ID. */
+ CGuestOSType guestOSType() const;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads data into the cache from the corresponding external object(s).
+ * @note This task COULD be performed in other than GUI thread. */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data into the corresponding widgets from cache,
+ * @note This task SHOULD be performed in GUI thread only! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves the data from the corresponding widgets into the cache,
+ * @note This task SHOULD be performed in GUI thread only! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Save data from cache into the corresponding external object(s).
+ * @note This task COULD be performed in other than GUI thread. */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ virtual bool validate(QList<UIValidationMessage> &messages) RT_OVERRIDE;
+
+ /** Defines TAB order for passed @a pWidget. */
+ virtual void setOrderAfter(QWidget *pWidget) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles encryption cipher change. */
+ void sltHandleEncryptionCipherChanged();
+ /** Handles encryption password change. */
+ void sltHandleEncryptionPasswordChanged();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares 'Basic' tab. */
+ void prepareTabBasic();
+ /** Prepares 'Advanced' tab. */
+ void prepareTabAdvanced();
+ /** Prepares 'Description' tab. */
+ void prepareTabDescription();
+ /** Prepares 'Encryption' tab. */
+ void prepareTabEncryption();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing general data from cache. */
+ bool saveData();
+ /** Saves existing 'Basic' data from cache. */
+ bool saveBasicData();
+ /** Saves existing 'Advanced' data from cache. */
+ bool saveAdvancedData();
+ /** Saves existing 'Description' data from cache. */
+ bool saveDescriptionData();
+ /** Saves existing 'Encryption' data from cache. */
+ bool saveEncryptionData();
+
+ /** Holds whether the encryption cipher was changed.
+ * We are holding that argument here because we do not know
+ * the old <i>cipher</i> for sure to compare the new one with. */
+ bool m_fEncryptionCipherChanged;
+ /** Holds whether the encryption password was changed.
+ * We are holding that argument here because we do not know
+ * the old <i>password</i> at all to compare the new one with. */
+ bool m_fEncryptionPasswordChanged;
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheMachineGeneral *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the tab-widget instance. */
+ QITabWidget *m_pTabWidget;
+
+ /** Holds the 'Basic' tab instance. */
+ QWidget *m_pTabBasic;
+ /** Holds the name and system editor instance. */
+ UINameAndSystemEditor *m_pEditorNameAndSystem;
+
+ /** Holds the 'Advanced' tab instance. */
+ QWidget *m_pTabAdvanced;
+ /** Holds the snapshot folder editor instance. */
+ UISnapshotFolderEditor *m_pEditorSnapshotFolder;
+ /** Holds the shared clipboard editor instance. */
+ UISharedClipboardEditor *m_pEditorClipboard;
+ /** Holds the drag and drop editor instance. */
+ UIDragAndDropEditor *m_pEditorDragAndDrop;
+
+ /** Holds the 'Description' tab instance. */
+ QWidget *m_pTabDescription;
+ /** Holds the description editor instance. */
+ UIDescriptionEditor *m_pEditorDescription;
+
+ /** Holds the 'Encryption' tab instance. */
+ QWidget *m_pTabEncryption;
+ /** Holds the cipher settings editor instance. */
+ UIDiskEncryptionSettingsEditor *m_pEditorDiskEncryptionSettings;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsGeneral_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsInterface.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsInterface.cpp
new file mode 100644
index 00000000..759d74e6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsInterface.cpp
@@ -0,0 +1,592 @@
+/* $Id: UIMachineSettingsInterface.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsInterface class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIActionPool.h"
+#include "UIExtraDataManager.h"
+#include "UIMachineSettingsInterface.h"
+#include "UIStatusBarEditorWindow.h"
+#include "UIMenuBarEditorWindow.h"
+#include "UIMiniToolbarSettingsEditor.h"
+#include "UIVisualStateEditor.h"
+
+/** Machine settings: User Interface page data structure. */
+struct UIDataSettingsMachineInterface
+{
+ /** Constructs data. */
+ UIDataSettingsMachineInterface()
+ : m_fStatusBarEnabled(false)
+#ifndef VBOX_WS_MAC
+ , m_fMenuBarEnabled(false)
+#endif /* !VBOX_WS_MAC */
+ , m_restrictionsOfMenuBar(UIExtraDataMetaDefs::MenuType_Invalid)
+ , m_restrictionsOfMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType_Invalid)
+ , m_restrictionsOfMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Invalid)
+ , m_restrictionsOfMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid)
+ , m_restrictionsOfMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_Invalid)
+ , m_restrictionsOfMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Invalid)
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ , m_restrictionsOfMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Invalid)
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ , m_restrictionsOfMenuWindow(UIExtraDataMetaDefs::MenuWindowActionType_Invalid)
+#endif /* VBOX_WS_MAC */
+ , m_restrictionsOfMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType_Invalid)
+#ifndef VBOX_WS_MAC
+ , m_fShowMiniToolbar(false)
+ , m_fMiniToolbarAtTop(false)
+#endif /* !VBOX_WS_MAC */
+ , m_enmVisualState(UIVisualStateType_Invalid)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineInterface &other) const
+ {
+ return true
+ && (m_fStatusBarEnabled == other.m_fStatusBarEnabled)
+ && (m_statusBarRestrictions == other.m_statusBarRestrictions)
+ && (m_statusBarOrder == other.m_statusBarOrder)
+#ifndef VBOX_WS_MAC
+ && (m_fMenuBarEnabled == other.m_fMenuBarEnabled)
+#endif /* !VBOX_WS_MAC */
+ && (m_restrictionsOfMenuBar == other.m_restrictionsOfMenuBar)
+ && (m_restrictionsOfMenuApplication == other.m_restrictionsOfMenuApplication)
+ && (m_restrictionsOfMenuMachine == other.m_restrictionsOfMenuMachine)
+ && (m_restrictionsOfMenuView == other.m_restrictionsOfMenuView)
+ && (m_restrictionsOfMenuInput == other.m_restrictionsOfMenuInput)
+ && (m_restrictionsOfMenuDevices == other.m_restrictionsOfMenuDevices)
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ && (m_restrictionsOfMenuDebug == other.m_restrictionsOfMenuDebug)
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ && (m_restrictionsOfMenuWindow == other.m_restrictionsOfMenuWindow)
+#endif /* VBOX_WS_MAC */
+ && (m_restrictionsOfMenuHelp == other.m_restrictionsOfMenuHelp)
+#ifndef VBOX_WS_MAC
+ && (m_fShowMiniToolbar == other.m_fShowMiniToolbar)
+ && (m_fMiniToolbarAtTop == other.m_fMiniToolbarAtTop)
+#endif /* !VBOX_WS_MAC */
+ && (m_enmVisualState == other.m_enmVisualState)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineInterface &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineInterface &other) const { return !equal(other); }
+
+ /** Holds whether the status-bar is enabled. */
+ bool m_fStatusBarEnabled;
+ /** Holds the status-bar indicator restrictions. */
+ QList<IndicatorType> m_statusBarRestrictions;
+ /** Holds the status-bar indicator order. */
+ QList<IndicatorType> m_statusBarOrder;
+
+#ifndef VBOX_WS_MAC
+ /** Holds whether the menu-bar is enabled. */
+ bool m_fMenuBarEnabled;
+#endif /* !VBOX_WS_MAC */
+ /** Holds the menu-bar menu restrictions. */
+ UIExtraDataMetaDefs::MenuType m_restrictionsOfMenuBar;
+ /** Holds the Application menu restrictions. */
+ UIExtraDataMetaDefs::MenuApplicationActionType m_restrictionsOfMenuApplication;
+ /** Holds the Machine menu restrictions. */
+ UIExtraDataMetaDefs::RuntimeMenuMachineActionType m_restrictionsOfMenuMachine;
+ /** Holds the View menu restrictions. */
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType m_restrictionsOfMenuView;
+ /** Holds the Input menu restrictions. */
+ UIExtraDataMetaDefs::RuntimeMenuInputActionType m_restrictionsOfMenuInput;
+ /** Holds the Devices menu restrictions. */
+ UIExtraDataMetaDefs::RuntimeMenuDevicesActionType m_restrictionsOfMenuDevices;
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Holds the Debug menu restrictions. */
+ UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType m_restrictionsOfMenuDebug;
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ /** Holds the Window menu restrictions. */
+ UIExtraDataMetaDefs::MenuWindowActionType m_restrictionsOfMenuWindow;
+#endif /* VBOX_WS_MAC */
+ /** Holds the Help menu restrictions. */
+ UIExtraDataMetaDefs::MenuHelpActionType m_restrictionsOfMenuHelp;
+
+#ifndef VBOX_WS_MAC
+ /** Holds whether the mini-toolbar is enabled. */
+ bool m_fShowMiniToolbar;
+ /** Holds whether the mini-toolbar should be aligned at top of screen. */
+ bool m_fMiniToolbarAtTop;
+#endif /* !VBOX_WS_MAC */
+
+ /** Holds the visual state. */
+ UIVisualStateType m_enmVisualState;
+};
+
+
+UIMachineSettingsInterface::UIMachineSettingsInterface(const QUuid &uMachineId)
+ : m_uMachineId(uMachineId)
+ , m_pActionPool(0)
+ , m_pCache(0)
+ , m_pEditorMenuBar(0)
+ , m_pEditorVisualState(0)
+ , m_pEditorMiniToolabSettings(0)
+ , m_pEditorStatusBar(0)
+{
+ prepare();
+}
+
+UIMachineSettingsInterface::~UIMachineSettingsInterface()
+{
+ cleanup();
+}
+
+bool UIMachineSettingsInterface::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsInterface::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Prepare old data: */
+ UIDataSettingsMachineInterface oldInterfaceData;
+
+ /* Gather old data: */
+ oldInterfaceData.m_fStatusBarEnabled = gEDataManager->statusBarEnabled(m_machine.GetId());
+ oldInterfaceData.m_statusBarRestrictions = gEDataManager->restrictedStatusBarIndicators(m_machine.GetId());
+ oldInterfaceData.m_statusBarOrder = gEDataManager->statusBarIndicatorOrder(m_machine.GetId());
+#ifndef VBOX_WS_MAC
+ oldInterfaceData.m_fMenuBarEnabled = gEDataManager->menuBarEnabled(m_machine.GetId());
+#endif
+ oldInterfaceData.m_restrictionsOfMenuBar = gEDataManager->restrictedRuntimeMenuTypes(m_machine.GetId());
+ oldInterfaceData.m_restrictionsOfMenuApplication = gEDataManager->restrictedRuntimeMenuApplicationActionTypes(m_machine.GetId());
+ oldInterfaceData.m_restrictionsOfMenuMachine = gEDataManager->restrictedRuntimeMenuMachineActionTypes(m_machine.GetId());
+ oldInterfaceData.m_restrictionsOfMenuView = gEDataManager->restrictedRuntimeMenuViewActionTypes(m_machine.GetId());
+ oldInterfaceData.m_restrictionsOfMenuInput = gEDataManager->restrictedRuntimeMenuInputActionTypes(m_machine.GetId());
+ oldInterfaceData.m_restrictionsOfMenuDevices = gEDataManager->restrictedRuntimeMenuDevicesActionTypes(m_machine.GetId());
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ oldInterfaceData.m_restrictionsOfMenuDebug = gEDataManager->restrictedRuntimeMenuDebuggerActionTypes(m_machine.GetId());
+#endif
+#ifdef VBOX_WS_MAC
+ oldInterfaceData.m_restrictionsOfMenuWindow = gEDataManager->restrictedRuntimeMenuWindowActionTypes(m_machine.GetId());
+#endif
+ oldInterfaceData.m_restrictionsOfMenuHelp = gEDataManager->restrictedRuntimeMenuHelpActionTypes(m_machine.GetId());
+#ifndef VBOX_WS_MAC
+ oldInterfaceData.m_fShowMiniToolbar = gEDataManager->miniToolbarEnabled(m_machine.GetId());
+ oldInterfaceData.m_fMiniToolbarAtTop = gEDataManager->miniToolbarAlignment(m_machine.GetId()) == Qt::AlignTop;
+#endif
+ oldInterfaceData.m_enmVisualState = gEDataManager->requestedVisualState(m_machine.GetId());
+
+ /* Cache old data: */
+ m_pCache->cacheInitialData(oldInterfaceData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsInterface::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Get old data from cache: */
+ const UIDataSettingsMachineInterface &oldInterfaceData = m_pCache->base();
+
+ /* Load old data from cache: */
+ if (m_pEditorStatusBar)
+ {
+ m_pEditorStatusBar->setStatusBarEnabled(oldInterfaceData.m_fStatusBarEnabled);
+ m_pEditorStatusBar->setStatusBarConfiguration(oldInterfaceData.m_statusBarRestrictions,
+ oldInterfaceData.m_statusBarOrder);
+ }
+ if (m_pEditorMenuBar)
+ {
+#ifndef VBOX_WS_MAC
+ m_pEditorMenuBar->setMenuBarEnabled(oldInterfaceData.m_fMenuBarEnabled);
+#endif
+ m_pEditorMenuBar->setRestrictionsOfMenuBar(oldInterfaceData.m_restrictionsOfMenuBar);
+ m_pEditorMenuBar->setRestrictionsOfMenuApplication(oldInterfaceData.m_restrictionsOfMenuApplication);
+ m_pEditorMenuBar->setRestrictionsOfMenuMachine(oldInterfaceData.m_restrictionsOfMenuMachine);
+ m_pEditorMenuBar->setRestrictionsOfMenuView(oldInterfaceData.m_restrictionsOfMenuView);
+ m_pEditorMenuBar->setRestrictionsOfMenuInput(oldInterfaceData.m_restrictionsOfMenuInput);
+ m_pEditorMenuBar->setRestrictionsOfMenuDevices(oldInterfaceData.m_restrictionsOfMenuDevices);
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ m_pEditorMenuBar->setRestrictionsOfMenuDebug(oldInterfaceData.m_restrictionsOfMenuDebug);
+#endif
+#ifdef VBOX_WS_MAC
+ m_pEditorMenuBar->setRestrictionsOfMenuWindow(oldInterfaceData.m_restrictionsOfMenuWindow);
+#endif
+ m_pEditorMenuBar->setRestrictionsOfMenuHelp(oldInterfaceData.m_restrictionsOfMenuHelp);
+ }
+#ifndef VBOX_WS_MAC
+ if (m_pEditorMiniToolabSettings)
+ {
+ m_pEditorMiniToolabSettings->setShowMiniToolbar(oldInterfaceData.m_fShowMiniToolbar);
+ m_pEditorMiniToolabSettings->setMiniToolbarAtTop(oldInterfaceData.m_fMiniToolbarAtTop);
+ }
+#endif
+ if (m_pEditorVisualState)
+ {
+ m_pEditorVisualState->setMachineId(m_machine.GetId());
+ m_pEditorVisualState->setValue(oldInterfaceData.m_enmVisualState);
+ }
+
+ /* Polish page finally: */
+ polishPage();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMachineSettingsInterface::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsMachineInterface newInterfaceData;
+
+ /* Cache new data: */
+ if (m_pEditorStatusBar)
+ {
+ newInterfaceData.m_fStatusBarEnabled = m_pEditorStatusBar->isStatusBarEnabled();
+ newInterfaceData.m_statusBarRestrictions = m_pEditorStatusBar->statusBarIndicatorRestrictions();
+ newInterfaceData.m_statusBarOrder = m_pEditorStatusBar->statusBarIndicatorOrder();
+ }
+ if (m_pEditorMenuBar)
+ {
+#ifndef VBOX_WS_MAC
+ newInterfaceData.m_fMenuBarEnabled = m_pEditorMenuBar->isMenuBarEnabled();
+#endif
+ newInterfaceData.m_restrictionsOfMenuBar = m_pEditorMenuBar->restrictionsOfMenuBar();
+ newInterfaceData.m_restrictionsOfMenuApplication = m_pEditorMenuBar->restrictionsOfMenuApplication();
+ newInterfaceData.m_restrictionsOfMenuMachine = m_pEditorMenuBar->restrictionsOfMenuMachine();
+ newInterfaceData.m_restrictionsOfMenuView = m_pEditorMenuBar->restrictionsOfMenuView();
+ newInterfaceData.m_restrictionsOfMenuInput = m_pEditorMenuBar->restrictionsOfMenuInput();
+ newInterfaceData.m_restrictionsOfMenuDevices = m_pEditorMenuBar->restrictionsOfMenuDevices();
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ newInterfaceData.m_restrictionsOfMenuDebug = m_pEditorMenuBar->restrictionsOfMenuDebug();
+#endif
+#ifdef VBOX_WS_MAC
+ newInterfaceData.m_restrictionsOfMenuWindow = m_pEditorMenuBar->restrictionsOfMenuWindow();
+#endif
+ newInterfaceData.m_restrictionsOfMenuHelp = m_pEditorMenuBar->restrictionsOfMenuHelp();
+ }
+#ifndef VBOX_WS_MAC
+ if (m_pEditorMiniToolabSettings)
+ {
+ newInterfaceData.m_fShowMiniToolbar = m_pEditorMiniToolabSettings->showMiniToolbar();
+ newInterfaceData.m_fMiniToolbarAtTop = m_pEditorMiniToolabSettings->miniToolbarAtTop();
+ }
+#endif
+ if (m_pEditorVisualState)
+ newInterfaceData.m_enmVisualState = m_pEditorVisualState->value();
+ m_pCache->cacheCurrentData(newInterfaceData);
+}
+
+void UIMachineSettingsInterface::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsInterface::retranslateUi()
+{
+ /* These editors have own labels, but we want them to be properly layouted according to each other: */
+ int iMinimumLayoutHint = 0;
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorVisualState->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorMiniToolabSettings->minimumLabelHorizontalHint());
+ m_pEditorVisualState->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorMiniToolabSettings->setMinimumLayoutIndent(iMinimumLayoutHint);
+}
+
+void UIMachineSettingsInterface::polishPage()
+{
+ /* Polish interface page availability: */
+ m_pEditorMenuBar->setEnabled(isMachineInValidMode());
+#ifdef VBOX_WS_MAC
+ m_pEditorMiniToolabSettings->hide();
+#else
+ m_pEditorMiniToolabSettings->setEnabled(isMachineInValidMode());
+#endif
+ m_pEditorStatusBar->setEnabled(isMachineInValidMode());
+}
+
+void UIMachineSettingsInterface::prepare()
+{
+ /* Prepare action-pool: */
+ m_pActionPool = UIActionPool::create(UIActionPoolType_Runtime);
+
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheMachineInterface;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsInterface::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare menu-bar editor: */
+ m_pEditorMenuBar = new UIMenuBarEditorWidget(this);
+ if (m_pEditorMenuBar)
+ {
+ m_pEditorMenuBar->setActionPool(m_pActionPool);
+ m_pEditorMenuBar->setMachineID(m_uMachineId);
+
+ pLayout->addWidget(m_pEditorMenuBar);
+ }
+
+ /* Prepare visual-state editor: */
+ m_pEditorVisualState = new UIVisualStateEditor(this);
+ if (m_pEditorVisualState)
+ pLayout->addWidget(m_pEditorVisualState);
+
+ /* Prepare mini-toolbar settings editor: */
+ m_pEditorMiniToolabSettings = new UIMiniToolbarSettingsEditor(this);
+ if (m_pEditorMiniToolabSettings)
+ pLayout->addWidget(m_pEditorMiniToolabSettings);
+
+ pLayout->addStretch();
+
+ /* Prepare status-bar editor: */
+ m_pEditorStatusBar = new UIStatusBarEditorWidget(this);
+ if (m_pEditorStatusBar)
+ {
+ m_pEditorStatusBar->setMachineID(m_uMachineId);
+ pLayout->addWidget(m_pEditorStatusBar);
+ }
+ }
+}
+
+void UIMachineSettingsInterface::prepareConnections()
+{
+}
+
+void UIMachineSettingsInterface::cleanup()
+{
+ /* Cleanup action-pool: */
+ UIActionPool::destroy(m_pActionPool);
+
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIMachineSettingsInterface::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save display settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* Save 'Menu-bar' data from cache: */
+ if (fSuccess)
+ fSuccess = saveMenuBarData();
+ /* Save 'Status-bar' data from cache: */
+ if (fSuccess)
+ fSuccess = saveStatusBarData();
+ /* Save 'Mini-toolbar' data from cache: */
+ if (fSuccess)
+ fSuccess = saveMiniToolbarData();
+ /* Save 'Visual State' data from cache: */
+ if (fSuccess)
+ fSuccess = saveVisualStateData();
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsInterface::saveMenuBarData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Menu-bar' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineInterface &oldInterfaceData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineInterface &newInterfaceData = m_pCache->data();
+
+#ifndef VBOX_WS_MAC
+ /* Save whether menu-bar is enabled: */
+ if (fSuccess && newInterfaceData.m_fMenuBarEnabled != oldInterfaceData.m_fMenuBarEnabled)
+ /* fSuccess = */ gEDataManager->setMenuBarEnabled(newInterfaceData.m_fMenuBarEnabled, m_machine.GetId());
+#endif
+ /* Save menu-bar restrictions: */
+ if (fSuccess && newInterfaceData.m_restrictionsOfMenuBar != oldInterfaceData.m_restrictionsOfMenuBar)
+ /* fSuccess = */ gEDataManager->setRestrictedRuntimeMenuTypes(newInterfaceData.m_restrictionsOfMenuBar, m_machine.GetId());
+ /* Save menu-bar Application menu restrictions: */
+ if (fSuccess && newInterfaceData.m_restrictionsOfMenuApplication != oldInterfaceData.m_restrictionsOfMenuApplication)
+ /* fSuccess = */ gEDataManager->setRestrictedRuntimeMenuApplicationActionTypes(newInterfaceData.m_restrictionsOfMenuApplication, m_machine.GetId());
+ /* Save menu-bar Machine menu restrictions: */
+ if (fSuccess && newInterfaceData.m_restrictionsOfMenuMachine != oldInterfaceData.m_restrictionsOfMenuMachine)
+ /* fSuccess = */ gEDataManager->setRestrictedRuntimeMenuMachineActionTypes(newInterfaceData.m_restrictionsOfMenuMachine, m_machine.GetId());
+ /* Save menu-bar View menu restrictions: */
+ if (fSuccess && newInterfaceData.m_restrictionsOfMenuView != oldInterfaceData.m_restrictionsOfMenuView)
+ /* fSuccess = */ gEDataManager->setRestrictedRuntimeMenuViewActionTypes(newInterfaceData.m_restrictionsOfMenuView, m_machine.GetId());
+ /* Save menu-bar Input menu restrictions: */
+ if (fSuccess && newInterfaceData.m_restrictionsOfMenuInput != oldInterfaceData.m_restrictionsOfMenuInput)
+ /* fSuccess = */ gEDataManager->setRestrictedRuntimeMenuInputActionTypes(newInterfaceData.m_restrictionsOfMenuInput, m_machine.GetId());
+ /* Save menu-bar Devices menu restrictions: */
+ if (fSuccess && newInterfaceData.m_restrictionsOfMenuDevices != oldInterfaceData.m_restrictionsOfMenuDevices)
+ /* fSuccess = */ gEDataManager->setRestrictedRuntimeMenuDevicesActionTypes(newInterfaceData.m_restrictionsOfMenuDevices, m_machine.GetId());
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /* Save menu-bar Debug menu restrictions: */
+ if (fSuccess && newInterfaceData.m_restrictionsOfMenuDebug != oldInterfaceData.m_restrictionsOfMenuDebug)
+ /* fSuccess = */ gEDataManager->setRestrictedRuntimeMenuDebuggerActionTypes(newInterfaceData.m_restrictionsOfMenuDebug, m_machine.GetId());
+#endif
+#ifdef VBOX_WS_MAC
+ /* Save menu-bar Window menu restrictions: */
+ if (fSuccess && newInterfaceData.m_restrictionsOfMenuWindow != oldInterfaceData.m_restrictionsOfMenuWindow)
+ /* fSuccess = */ gEDataManager->setRestrictedRuntimeMenuWindowActionTypes(newInterfaceData.m_restrictionsOfMenuWindow, m_machine.GetId());
+#endif
+ /* Save menu-bar Help menu restrictions: */
+ if (fSuccess && newInterfaceData.m_restrictionsOfMenuHelp != oldInterfaceData.m_restrictionsOfMenuHelp)
+ /* fSuccess = */ gEDataManager->setRestrictedRuntimeMenuHelpActionTypes(newInterfaceData.m_restrictionsOfMenuHelp, m_machine.GetId());
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsInterface::saveStatusBarData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Status-bar' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineInterface &oldInterfaceData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineInterface &newInterfaceData = m_pCache->data();
+
+ /* Save whether status-bar is enabled: */
+ if (fSuccess && newInterfaceData.m_fStatusBarEnabled != oldInterfaceData.m_fStatusBarEnabled)
+ /* fSuccess = */ gEDataManager->setStatusBarEnabled(newInterfaceData.m_fStatusBarEnabled, m_machine.GetId());
+ /* Save status-bar restrictions: */
+ if (fSuccess && newInterfaceData.m_statusBarRestrictions != oldInterfaceData.m_statusBarRestrictions)
+ /* fSuccess = */ gEDataManager->setRestrictedStatusBarIndicators(newInterfaceData.m_statusBarRestrictions, m_machine.GetId());
+ /* Save status-bar order: */
+ if (fSuccess && newInterfaceData.m_statusBarOrder != oldInterfaceData.m_statusBarOrder)
+ /* fSuccess = */ gEDataManager->setStatusBarIndicatorOrder(newInterfaceData.m_statusBarOrder, m_machine.GetId());
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsInterface::saveMiniToolbarData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Mini-toolbar' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineInterface &oldInterfaceData = m_pCache->base(); Q_UNUSED(oldInterfaceData);
+ /* Get new data from cache: */
+ const UIDataSettingsMachineInterface &newInterfaceData = m_pCache->data(); Q_UNUSED(newInterfaceData);
+
+#ifndef VBOX_WS_MAC
+ /* Save whether mini-toolbar is enabled: */
+ if (fSuccess && newInterfaceData.m_fShowMiniToolbar != oldInterfaceData.m_fShowMiniToolbar)
+ /* fSuccess = */ gEDataManager->setMiniToolbarEnabled(newInterfaceData.m_fShowMiniToolbar, m_machine.GetId());
+ /* Save whether mini-toolbar should be location at top of screen: */
+ if (fSuccess && newInterfaceData.m_fMiniToolbarAtTop != oldInterfaceData.m_fMiniToolbarAtTop)
+ /* fSuccess = */ gEDataManager->setMiniToolbarAlignment(newInterfaceData.m_fMiniToolbarAtTop ? Qt::AlignTop : Qt::AlignBottom, m_machine.GetId());
+#endif
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsInterface::saveVisualStateData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Visual State' data from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineInterface &oldInterfaceData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineInterface &newInterfaceData = m_pCache->data();
+
+ /* Save desired visual state: */
+ if (fSuccess && newInterfaceData.m_enmVisualState != oldInterfaceData.m_enmVisualState)
+ /* fSuccess = */ gEDataManager->setRequestedVisualState(newInterfaceData.m_enmVisualState, m_machine.GetId());
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsInterface.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsInterface.h
new file mode 100644
index 00000000..61e6583b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsInterface.h
@@ -0,0 +1,127 @@
+/* $Id: UIMachineSettingsInterface.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsInterface class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsInterface_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsInterface_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class QLabel;
+class UIActionPool;
+class UIMenuBarEditorWidget;
+class UIMiniToolbarSettingsEditor;
+class UIStatusBarEditorWidget;
+class UIVisualStateEditor;
+struct UIDataSettingsMachineInterface;
+typedef UISettingsCache<UIDataSettingsMachineInterface> UISettingsCacheMachineInterface;
+
+/** Machine settings: User Interface page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsInterface : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs User Interface settings page. */
+ UIMachineSettingsInterface(const QUuid &uMachineId);
+ /** Destructs User Interface settings page. */
+ virtual ~UIMachineSettingsInterface() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+ /** Saves existing 'Menu-bar' data from cache. */
+ bool saveMenuBarData();
+ /** Saves existing 'Status-bar' data from cache. */
+ bool saveStatusBarData();
+ /** Saves existing 'Mini-toolbar' data from cache. */
+ bool saveMiniToolbarData();
+ /** Saves existing 'Visual State' data from cache. */
+ bool saveVisualStateData();
+
+ /** Holds the machine ID copy. */
+ const QUuid m_uMachineId;
+ /** Holds the action-pool instance. */
+ UIActionPool *m_pActionPool;
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheMachineInterface *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the menu-bar editor instance. */
+ UIMenuBarEditorWidget *m_pEditorMenuBar;
+ /** Holds the visual state editor instance. */
+ UIVisualStateEditor *m_pEditorVisualState;
+ /** Holds the mini-toolbar settings editor instance. */
+ UIMiniToolbarSettingsEditor *m_pEditorMiniToolabSettings;
+ /** Holds the status-bar editor instance. */
+ UIStatusBarEditorWidget *m_pEditorStatusBar;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsInterface_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsNetwork.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsNetwork.cpp
new file mode 100644
index 00000000..bb129110
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsNetwork.cpp
@@ -0,0 +1,1311 @@
+/* $Id: UIMachineSettingsNetwork.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsNetwork class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QRegularExpression>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QITabWidget.h"
+#include "UICommon.h"
+#include "UIErrorString.h"
+#include "UIMachineSettingsNetwork.h"
+#include "UINetworkAttachmentEditor.h"
+#include "UINetworkSettingsEditor.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "CNATEngine.h"
+#include "CNetworkAdapter.h"
+
+
+QString wipedOutString(const QString &strInputString)
+{
+ return strInputString.isEmpty() ? QString() : strInputString;
+}
+
+
+/** Machine settings: Network Adapter data structure. */
+struct UIDataSettingsMachineNetworkAdapter
+{
+ /** Constructs data. */
+ UIDataSettingsMachineNetworkAdapter()
+ : m_iSlot(0)
+ , m_fAdapterEnabled(false)
+ , m_adapterType(KNetworkAdapterType_Null)
+ , m_attachmentType(KNetworkAttachmentType_Null)
+ , m_promiscuousMode(KNetworkAdapterPromiscModePolicy_Deny)
+ , m_strBridgedAdapterName(QString())
+ , m_strInternalNetworkName(QString())
+ , m_strHostInterfaceName(QString())
+ , m_strGenericDriverName(QString())
+ , m_strGenericProperties(QString())
+ , m_strNATNetworkName(QString())
+#ifdef VBOX_WITH_CLOUD_NET
+ , m_strCloudNetworkName(QString())
+#endif
+#ifdef VBOX_WITH_VMNET
+ , m_strHostOnlyNetworkName(QString())
+#endif
+ , m_strMACAddress(QString())
+ , m_fCableConnected(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineNetworkAdapter &other) const
+ {
+ return true
+ && (m_iSlot == other.m_iSlot)
+ && (m_fAdapterEnabled == other.m_fAdapterEnabled)
+ && (m_adapterType == other.m_adapterType)
+ && (m_attachmentType == other.m_attachmentType)
+ && (m_promiscuousMode == other.m_promiscuousMode)
+ && (m_strBridgedAdapterName == other.m_strBridgedAdapterName)
+ && (m_strInternalNetworkName == other.m_strInternalNetworkName)
+ && (m_strHostInterfaceName == other.m_strHostInterfaceName)
+ && (m_strGenericDriverName == other.m_strGenericDriverName)
+ && (m_strGenericProperties == other.m_strGenericProperties)
+ && (m_strNATNetworkName == other.m_strNATNetworkName)
+#ifdef VBOX_WITH_CLOUD_NET
+ && (m_strCloudNetworkName == other.m_strCloudNetworkName)
+#endif
+#ifdef VBOX_WITH_VMNET
+ && (m_strHostOnlyNetworkName == other.m_strHostOnlyNetworkName)
+#endif
+ && (m_strMACAddress == other.m_strMACAddress)
+ && (m_fCableConnected == other.m_fCableConnected)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineNetworkAdapter &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineNetworkAdapter &other) const { return !equal(other); }
+
+ /** Holds the network adapter slot number. */
+ int m_iSlot;
+ /** Holds whether the network adapter is enabled. */
+ bool m_fAdapterEnabled;
+ /** Holds the network adapter type. */
+ KNetworkAdapterType m_adapterType;
+ /** Holds the network attachment type. */
+ KNetworkAttachmentType m_attachmentType;
+ /** Holds the network promiscuous mode policy. */
+ KNetworkAdapterPromiscModePolicy m_promiscuousMode;
+ /** Holds the bridged adapter name. */
+ QString m_strBridgedAdapterName;
+ /** Holds the internal network name. */
+ QString m_strInternalNetworkName;
+ /** Holds the host interface name. */
+ QString m_strHostInterfaceName;
+ /** Holds the generic driver name. */
+ QString m_strGenericDriverName;
+ /** Holds the generic driver properties. */
+ QString m_strGenericProperties;
+ /** Holds the NAT network name. */
+ QString m_strNATNetworkName;
+#ifdef VBOX_WITH_CLOUD_NET
+ /** Holds the cloud network name. */
+ QString m_strCloudNetworkName;
+#endif
+#ifdef VBOX_WITH_VMNET
+ /** Holds the host-only network name. */
+ QString m_strHostOnlyNetworkName;
+#endif
+ /** Holds the network adapter MAC address. */
+ QString m_strMACAddress;
+ /** Holds whether the network adapter is connected. */
+ bool m_fCableConnected;
+};
+
+
+/** Machine settings: Network page data structure. */
+struct UIDataSettingsMachineNetwork
+{
+ /** Constructs data. */
+ UIDataSettingsMachineNetwork() {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineNetwork & /* other */) const { return true; }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineNetwork & /* other */) const { return false; }
+};
+
+
+/** Machine settings: Network Adapter tab. */
+class UIMachineSettingsNetwork : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about alternative name was changed. */
+ void sigAlternativeNameChanged();
+
+ /** Notifies about advanced button state change to @a fExpanded. */
+ void sigAdvancedButtonStateChange(bool fExpanded);
+
+ /** Notifies about validity changed. */
+ void sigValidityChanged();
+
+public:
+
+ /** Constructs tab passing @a pParent to the base-class. */
+ UIMachineSettingsNetwork(UIMachineSettingsNetworkPage *pParent);
+
+ /** Loads adapter data from @a adapterCache. */
+ void getAdapterDataFromCache(const UISettingsCacheMachineNetworkAdapter &adapterCache);
+ /** Saves adapter data to @a adapterCache. */
+ void putAdapterDataToCache(UISettingsCacheMachineNetworkAdapter &adapterCache);
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ bool validate(QList<UIValidationMessage> &messages);
+
+ /** Configures tab order according to passed @a pWidget. */
+ QWidget *setOrderAfter(QWidget *pWidget);
+
+ /** Returns tab title. */
+ QString tabTitle() const;
+ /** Returns tab attachment type. */
+ KNetworkAttachmentType attachmentType() const;
+ /** Returne tab alternative name for @a enmType specified. */
+ QString alternativeName(KNetworkAttachmentType enmType = KNetworkAttachmentType_Null) const;
+
+ /** Performs tab polishing. */
+ void polishTab();
+ /** Reloads tab alternatives. */
+ void reloadAlternatives();
+
+ /** Defines whether the advanced button is @a fExpanded. */
+ void setAdvancedButtonExpanded(bool fExpanded);
+
+protected:
+
+ /** Handles translation event. */
+ void retranslateUi();
+
+private slots:
+
+ /** Handles adapter alternative name change. */
+ void sltHandleAlternativeNameChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Holds parent page reference. */
+ UIMachineSettingsNetworkPage *m_pParent;
+
+ /** Holds tab slot number. */
+ int m_iSlot;
+
+ /** Holds the network settings editor instance. */
+ UINetworkSettingsEditor *m_pEditorNetworkSettings;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMachineSettingsNetwork implementation. *
+*********************************************************************************************************************************/
+
+UIMachineSettingsNetwork::UIMachineSettingsNetwork(UIMachineSettingsNetworkPage *pParent)
+ : QIWithRetranslateUI<QWidget>(0)
+ , m_pParent(pParent)
+ , m_iSlot(-1)
+ , m_pEditorNetworkSettings(0)
+{
+ prepare();
+}
+
+void UIMachineSettingsNetwork::getAdapterDataFromCache(const UISettingsCacheMachineNetworkAdapter &adapterCache)
+{
+ /* Get old data: */
+ const UIDataSettingsMachineNetworkAdapter &oldAdapterData = adapterCache.base();
+
+ /* Load slot number: */
+ m_iSlot = oldAdapterData.m_iSlot;
+
+ if (m_pEditorNetworkSettings)
+ {
+ /* Load adapter activity state: */
+ m_pEditorNetworkSettings->setFeatureEnabled(oldAdapterData.m_fAdapterEnabled);
+
+ /* Load attachment type: */
+ m_pEditorNetworkSettings->setValueType(oldAdapterData.m_attachmentType);
+ /* Load alternative names: */
+ m_pEditorNetworkSettings->setValueName(KNetworkAttachmentType_Bridged, wipedOutString(oldAdapterData.m_strBridgedAdapterName));
+ m_pEditorNetworkSettings->setValueName(KNetworkAttachmentType_Internal, wipedOutString(oldAdapterData.m_strInternalNetworkName));
+ m_pEditorNetworkSettings->setValueName(KNetworkAttachmentType_HostOnly, wipedOutString(oldAdapterData.m_strHostInterfaceName));
+ m_pEditorNetworkSettings->setValueName(KNetworkAttachmentType_Generic, wipedOutString(oldAdapterData.m_strGenericDriverName));
+ m_pEditorNetworkSettings->setValueName(KNetworkAttachmentType_NATNetwork, wipedOutString(oldAdapterData.m_strNATNetworkName));
+#ifdef VBOX_WITH_CLOUD_NET
+ m_pEditorNetworkSettings->setValueName(KNetworkAttachmentType_Cloud, wipedOutString(oldAdapterData.m_strCloudNetworkName));
+#endif
+#ifdef VBOX_WITH_VMNET
+ m_pEditorNetworkSettings->setValueName(KNetworkAttachmentType_HostOnlyNetwork, wipedOutString(oldAdapterData.m_strHostOnlyNetworkName));
+#endif
+
+ /* Load settings: */
+ m_pEditorNetworkSettings->setAdapterType(oldAdapterData.m_adapterType);
+ m_pEditorNetworkSettings->setPromiscuousMode(oldAdapterData.m_promiscuousMode);
+ m_pEditorNetworkSettings->setMACAddress(oldAdapterData.m_strMACAddress);
+ m_pEditorNetworkSettings->setGenericProperties(oldAdapterData.m_strGenericProperties);
+ m_pEditorNetworkSettings->setCableConnected(oldAdapterData.m_fCableConnected);
+
+ /* Load port forwarding rules: */
+ UIPortForwardingDataList portForwardingRules;
+ for (int i = 0; i < adapterCache.childCount(); ++i)
+ portForwardingRules << adapterCache.child(i).base();
+ m_pEditorNetworkSettings->setPortForwardingRules(portForwardingRules);
+ }
+
+ /* Reload alternatives: */
+ reloadAlternatives();
+}
+
+void UIMachineSettingsNetwork::putAdapterDataToCache(UISettingsCacheMachineNetworkAdapter &adapterCache)
+{
+ /* Prepare new data: */
+ UIDataSettingsMachineNetworkAdapter newAdapterData;
+
+ /* Save slot number: */
+ newAdapterData.m_iSlot = m_iSlot;
+
+ if (m_pEditorNetworkSettings)
+ {
+ /* Save adapter activity state: */
+ newAdapterData.m_fAdapterEnabled = m_pEditorNetworkSettings->isFeatureEnabled();
+
+ /* Save attachment type & alternative name: */
+ newAdapterData.m_attachmentType = attachmentType();
+ newAdapterData.m_strBridgedAdapterName = m_pEditorNetworkSettings->valueName(KNetworkAttachmentType_Bridged);
+ newAdapterData.m_strInternalNetworkName = m_pEditorNetworkSettings->valueName(KNetworkAttachmentType_Internal);
+ newAdapterData.m_strHostInterfaceName = m_pEditorNetworkSettings->valueName(KNetworkAttachmentType_HostOnly);
+ newAdapterData.m_strGenericDriverName = m_pEditorNetworkSettings->valueName(KNetworkAttachmentType_Generic);
+ newAdapterData.m_strNATNetworkName = m_pEditorNetworkSettings->valueName(KNetworkAttachmentType_NATNetwork);
+#ifdef VBOX_WITH_CLOUD_NET
+ newAdapterData.m_strCloudNetworkName = m_pEditorNetworkSettings->valueName(KNetworkAttachmentType_Cloud);
+#endif
+#ifdef VBOX_WITH_VMNET
+ newAdapterData.m_strHostOnlyNetworkName = m_pEditorNetworkSettings->valueName(KNetworkAttachmentType_HostOnlyNetwork);
+#endif
+
+ /* Save settings: */
+ newAdapterData.m_adapterType = m_pEditorNetworkSettings->adapterType();
+ newAdapterData.m_promiscuousMode = m_pEditorNetworkSettings->promiscuousMode();
+ newAdapterData.m_strMACAddress = m_pEditorNetworkSettings->macAddress();
+ newAdapterData.m_strGenericProperties = m_pEditorNetworkSettings->genericProperties();
+ newAdapterData.m_fCableConnected = m_pEditorNetworkSettings->cableConnected();
+
+ /* Save port forwarding rules: */
+ foreach (const UIDataPortForwardingRule &rule, m_pEditorNetworkSettings->portForwardingRules())
+ adapterCache.child(rule.name).cacheCurrentData(rule);
+ }
+
+ /* Cache new data: */
+ adapterCache.cacheCurrentData(newAdapterData);
+}
+
+bool UIMachineSettingsNetwork::validate(QList<UIValidationMessage> &messages)
+{
+ /* Pass by default: */
+ bool fPass = true;
+
+ /* Prepare message: */
+ UIValidationMessage message;
+ message.first = UITranslator::removeAccelMark(tabTitle());
+
+ /* Validate enabled adapter only: */
+ if ( m_pEditorNetworkSettings
+ && m_pEditorNetworkSettings->isFeatureEnabled())
+ {
+ /* Validate alternatives: */
+ switch (attachmentType())
+ {
+ case KNetworkAttachmentType_Bridged:
+ {
+ if (alternativeName().isNull())
+ {
+ message.second << tr("No bridged network adapter is currently selected.");
+ fPass = false;
+ }
+ break;
+ }
+ case KNetworkAttachmentType_Internal:
+ {
+ if (alternativeName().isNull())
+ {
+ message.second << tr("No internal network name is currently specified.");
+ fPass = false;
+ }
+ break;
+ }
+#ifndef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnly:
+ {
+ if (alternativeName().isNull())
+ {
+ message.second << tr("No host-only network adapter is currently selected.");
+ fPass = false;
+ }
+ break;
+ }
+#else /* VBOX_WITH_VMNET */
+ case KNetworkAttachmentType_HostOnly:
+ {
+ message.second << tr("Host-only adapters are no longer supported, use host-only networks instead.");
+ fPass = false;
+ break;
+ }
+#endif /* VBOX_WITH_VMNET */
+ case KNetworkAttachmentType_Generic:
+ {
+ if (alternativeName().isNull())
+ {
+ message.second << tr("No generic driver is currently selected.");
+ fPass = false;
+ }
+ break;
+ }
+ case KNetworkAttachmentType_NATNetwork:
+ {
+ if (alternativeName().isNull())
+ {
+ message.second << tr("No NAT network name is currently specified.");
+ fPass = false;
+ }
+ break;
+ }
+#ifdef VBOX_WITH_CLOUD_NET
+ case KNetworkAttachmentType_Cloud:
+ {
+ if (alternativeName().isNull())
+ {
+ message.second << tr("No cloud network name is currently specified.");
+ fPass = false;
+ }
+ break;
+ }
+#endif /* VBOX_WITH_CLOUD_NET */
+#ifdef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnlyNetwork:
+ {
+ if (alternativeName().isNull())
+ {
+ message.second << tr("No host-only network name is currently specified.");
+ fPass = false;
+ }
+ break;
+ }
+#endif /* VBOX_WITH_VMNET */
+ default:
+ break;
+ }
+
+ /* Validate MAC-address length: */
+ if (m_pEditorNetworkSettings->macAddress().size() < 12)
+ {
+ message.second << tr("The MAC address must be 12 hexadecimal digits long.");
+ fPass = false;
+ }
+
+ /* Make sure MAC-address is unicast: */
+ if (m_pEditorNetworkSettings->macAddress().size() >= 2)
+ {
+ if (m_pEditorNetworkSettings->macAddress().indexOf(QRegularExpression("^[0-9A-Fa-f][02468ACEace]")) != 0)
+ {
+ message.second << tr("The second digit in the MAC address may not be odd as only unicast addresses are allowed.");
+ fPass = false;
+ }
+ }
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+
+ /* Return result: */
+ return fPass;
+}
+
+QWidget *UIMachineSettingsNetwork::setOrderAfter(QWidget *pWidget)
+{
+ setTabOrder(pWidget, m_pEditorNetworkSettings);
+ return m_pEditorNetworkSettings;
+}
+
+QString UIMachineSettingsNetwork::tabTitle() const
+{
+ return UICommon::tr("Adapter %1").arg(QString("&%1").arg(m_iSlot + 1));
+}
+
+KNetworkAttachmentType UIMachineSettingsNetwork::attachmentType() const
+{
+ return m_pEditorNetworkSettings ? m_pEditorNetworkSettings->valueType() : KNetworkAttachmentType_Null;
+}
+
+QString UIMachineSettingsNetwork::alternativeName(KNetworkAttachmentType enmType /* = KNetworkAttachmentType_Null */) const
+{
+ if (enmType == KNetworkAttachmentType_Null)
+ enmType = attachmentType();
+ return m_pEditorNetworkSettings ? m_pEditorNetworkSettings->valueName(enmType) : QString();
+}
+
+void UIMachineSettingsNetwork::polishTab()
+{
+ if ( m_pEditorNetworkSettings
+ && m_pParent)
+ {
+ /* General stuff: */
+ m_pEditorNetworkSettings->setFeatureAvailable(m_pParent->isMachineOffline());
+
+ /* Attachment stuff: */
+ m_pEditorNetworkSettings->setAttachmentOptionsAvailable(m_pParent->isMachineInValidMode());
+
+ /* Advanced stuff: */
+ m_pEditorNetworkSettings->setAdvancedOptionsAvailable(m_pParent->isMachineInValidMode());
+ m_pEditorNetworkSettings->setAdapterOptionsAvailable(m_pParent->isMachineOffline());
+ m_pEditorNetworkSettings->setPromiscuousOptionsAvailable( attachmentType() != KNetworkAttachmentType_Null
+ && attachmentType() != KNetworkAttachmentType_Generic
+ && attachmentType() != KNetworkAttachmentType_NAT);
+ m_pEditorNetworkSettings->setMACOptionsAvailable(m_pParent->isMachineOffline());
+ m_pEditorNetworkSettings->setGenericPropertiesAvailable(attachmentType() == KNetworkAttachmentType_Generic);
+ m_pEditorNetworkSettings->setCableOptionsAvailable(m_pParent->isMachineInValidMode());
+ m_pEditorNetworkSettings->setForwardingOptionsAvailable(attachmentType() == KNetworkAttachmentType_NAT);
+ }
+}
+
+void UIMachineSettingsNetwork::reloadAlternatives()
+{
+ if ( m_pEditorNetworkSettings
+ && m_pParent)
+ {
+ m_pEditorNetworkSettings->setValueNames(KNetworkAttachmentType_Bridged, m_pParent->bridgedAdapterList());
+ m_pEditorNetworkSettings->setValueNames(KNetworkAttachmentType_Internal, m_pParent->internalNetworkList());
+ m_pEditorNetworkSettings->setValueNames(KNetworkAttachmentType_HostOnly, m_pParent->hostInterfaceList());
+ m_pEditorNetworkSettings->setValueNames(KNetworkAttachmentType_Generic, m_pParent->genericDriverList());
+ m_pEditorNetworkSettings->setValueNames(KNetworkAttachmentType_NATNetwork, m_pParent->natNetworkList());
+#ifdef VBOX_WITH_CLOUD_NET
+ m_pEditorNetworkSettings->setValueNames(KNetworkAttachmentType_Cloud, m_pParent->cloudNetworkList());
+#endif
+#ifdef VBOX_WITH_VMNET
+ m_pEditorNetworkSettings->setValueNames(KNetworkAttachmentType_HostOnlyNetwork, m_pParent->hostOnlyNetworkList());
+#endif
+ }
+}
+
+void UIMachineSettingsNetwork::setAdvancedButtonExpanded(bool fExpanded)
+{
+ if (m_pEditorNetworkSettings)
+ m_pEditorNetworkSettings->setAdvancedButtonExpanded(fExpanded);
+}
+
+void UIMachineSettingsNetwork::retranslateUi()
+{
+ /* Reload alternatives: */
+ reloadAlternatives();
+}
+
+void UIMachineSettingsNetwork::sltHandleAlternativeNameChange()
+{
+ if (m_pEditorNetworkSettings)
+ {
+ /* Notify other adapter tabs if alternative name for certain type is changed: */
+ switch (attachmentType())
+ {
+ case KNetworkAttachmentType_Internal:
+ case KNetworkAttachmentType_Generic:
+ {
+ if (!m_pEditorNetworkSettings->valueName(attachmentType()).isNull())
+ emit sigAlternativeNameChanged();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Notify validity changed: */
+ emit sigValidityChanged();
+}
+
+void UIMachineSettingsNetwork::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsNetwork::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare settings editor: */
+ m_pEditorNetworkSettings = new UINetworkSettingsEditor(this);
+ if (m_pEditorNetworkSettings)
+ pLayout->addWidget(m_pEditorNetworkSettings);
+
+ pLayout->addStretch();
+ }
+}
+
+void UIMachineSettingsNetwork::prepareConnections()
+{
+ if (m_pEditorNetworkSettings)
+ {
+ /* Attachment connections: */
+ connect(m_pEditorNetworkSettings, &UINetworkSettingsEditor::sigFeatureStateChanged,
+ this, &UIMachineSettingsNetwork::sigValidityChanged);
+ connect(m_pEditorNetworkSettings, &UINetworkSettingsEditor::sigAttachmentTypeChanged,
+ this, &UIMachineSettingsNetwork::sigValidityChanged);
+ connect(m_pEditorNetworkSettings, &UINetworkSettingsEditor::sigAlternativeNameChanged,
+ this, &UIMachineSettingsNetwork::sltHandleAlternativeNameChange);
+
+ /* Advanced connections: */
+ connect(m_pEditorNetworkSettings, &UINetworkSettingsEditor::sigAdvancedButtonStateChange,
+ this, &UIMachineSettingsNetwork::sigAdvancedButtonStateChange);
+ connect(m_pEditorNetworkSettings, &UINetworkSettingsEditor::sigMACAddressChanged,
+ this, &UIMachineSettingsNetwork::sigValidityChanged);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMachineSettingsNetworkPage implementation. *
+*********************************************************************************************************************************/
+
+UIMachineSettingsNetworkPage::UIMachineSettingsNetworkPage()
+ : m_pCache(0)
+ , m_pTabWidget(0)
+{
+ prepare();
+}
+
+UIMachineSettingsNetworkPage::~UIMachineSettingsNetworkPage()
+{
+ cleanup();
+}
+
+bool UIMachineSettingsNetworkPage::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsNetworkPage::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Cache name lists: */
+ refreshBridgedAdapterList();
+ refreshInternalNetworkList(true);
+ refreshHostInterfaceList();
+ refreshGenericDriverList(true);
+ refreshNATNetworkList();
+#ifdef VBOX_WITH_CLOUD_NET
+ refreshCloudNetworkList();
+#endif
+#ifdef VBOX_WITH_VMNET
+ refreshHostOnlyNetworkList();
+#endif
+
+ /* Prepare old data: */
+ UIDataSettingsMachineNetwork oldNetworkData;
+
+ /* For each network adapter: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ /* Prepare old data: */
+ UIDataSettingsMachineNetworkAdapter oldAdapterData;
+
+ /* Check whether adapter is valid: */
+ const CNetworkAdapter &comAdapter = m_machine.GetNetworkAdapter(iSlot);
+ if (!comAdapter.isNull())
+ {
+ /* Gather old data: */
+ oldAdapterData.m_iSlot = iSlot;
+ oldAdapterData.m_fAdapterEnabled = comAdapter.GetEnabled();
+ oldAdapterData.m_attachmentType = comAdapter.GetAttachmentType();
+ oldAdapterData.m_strBridgedAdapterName = wipedOutString(comAdapter.GetBridgedInterface());
+ oldAdapterData.m_strInternalNetworkName = wipedOutString(comAdapter.GetInternalNetwork());
+ oldAdapterData.m_strHostInterfaceName = wipedOutString(comAdapter.GetHostOnlyInterface());
+ oldAdapterData.m_strGenericDriverName = wipedOutString(comAdapter.GetGenericDriver());
+ oldAdapterData.m_strNATNetworkName = wipedOutString(comAdapter.GetNATNetwork());
+#ifdef VBOX_WITH_CLOUD_NET
+ oldAdapterData.m_strCloudNetworkName = wipedOutString(comAdapter.GetCloudNetwork());
+#endif
+#ifdef VBOX_WITH_VMNET
+ oldAdapterData.m_strHostOnlyNetworkName = wipedOutString(comAdapter.GetHostOnlyNetwork());
+#endif
+ oldAdapterData.m_adapterType = comAdapter.GetAdapterType();
+ oldAdapterData.m_promiscuousMode = comAdapter.GetPromiscModePolicy();
+ oldAdapterData.m_strMACAddress = comAdapter.GetMACAddress();
+ oldAdapterData.m_strGenericProperties = loadGenericProperties(comAdapter);
+ oldAdapterData.m_fCableConnected = comAdapter.GetCableConnected();
+ foreach (const QString &strRedirect, comAdapter.GetNATEngine().GetRedirects())
+ {
+ /* Gather old data & cache key: */
+ const QStringList &forwardingData = strRedirect.split(',');
+ AssertMsg(forwardingData.size() == 6, ("Redirect rule should be composed of 6 parts!\n"));
+ const UIDataPortForwardingRule oldForwardingData(forwardingData.at(0),
+ (KNATProtocol)forwardingData.at(1).toUInt(),
+ forwardingData.at(2),
+ forwardingData.at(3).toUInt(),
+ forwardingData.at(4),
+ forwardingData.at(5).toUInt());
+ const QString &strForwardingKey = forwardingData.at(0);
+ /* Cache old data: */
+ m_pCache->child(iSlot).child(strForwardingKey).cacheInitialData(oldForwardingData);
+ }
+ }
+
+ /* Cache old data: */
+ m_pCache->child(iSlot).cacheInitialData(oldAdapterData);
+ }
+
+ /* Cache old data: */
+ m_pCache->cacheInitialData(oldNetworkData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsNetworkPage::getFromCache()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return;
+
+ /* Setup tab order: */
+ AssertPtrReturnVoid(firstWidget());
+ setTabOrder(firstWidget(), m_pTabWidget->focusProxy());
+ QWidget *pLastFocusWidget = m_pTabWidget->focusProxy();
+
+ /* For each adapter: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ /* Get adapter page: */
+ UIMachineSettingsNetwork *pTab = qobject_cast<UIMachineSettingsNetwork*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+
+ /* Load old data from cache: */
+ pTab->getAdapterDataFromCache(m_pCache->child(iSlot));
+
+ /* Setup tab order: */
+ pLastFocusWidget = pTab->setOrderAfter(pLastFocusWidget);
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Polish page finally: */
+ polishPage();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMachineSettingsNetworkPage::putToCache()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsMachineNetwork newNetworkData;
+
+ /* For each adapter: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ /* Get adapter page: */
+ UIMachineSettingsNetwork *pTab = qobject_cast<UIMachineSettingsNetwork*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+
+ /* Gather new data: */
+ pTab->putAdapterDataToCache(m_pCache->child(iSlot));
+ }
+
+ /* Cache new data: */
+ m_pCache->cacheCurrentData(newNetworkData);
+}
+
+void UIMachineSettingsNetworkPage::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+bool UIMachineSettingsNetworkPage::validate(QList<UIValidationMessage> &messages)
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return false;
+
+ /* Pass by default: */
+ bool fValid = true;
+
+ /* Delegate validation to adapter tabs: */
+ for (int iIndex = 0; iIndex < m_pTabWidget->count(); ++iIndex)
+ {
+ UIMachineSettingsNetwork *pTab = qobject_cast<UIMachineSettingsNetwork*>(m_pTabWidget->widget(iIndex));
+ AssertPtrReturn(pTab, false);
+ if (!pTab->validate(messages))
+ fValid = false;
+ }
+
+ /* Return result: */
+ return fValid;
+}
+
+void UIMachineSettingsNetworkPage::retranslateUi()
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return;
+
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ UIMachineSettingsNetwork *pTab = qobject_cast<UIMachineSettingsNetwork*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+ m_pTabWidget->setTabText(iSlot, pTab->tabTitle());
+ }
+}
+
+void UIMachineSettingsNetworkPage::polishPage()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return;
+
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ m_pTabWidget->setTabEnabled(iSlot,
+ isMachineOffline() ||
+ (isMachineInValidMode() &&
+ m_pCache->childCount() > iSlot &&
+ m_pCache->child(iSlot).base().m_fAdapterEnabled));
+ UIMachineSettingsNetwork *pTab = qobject_cast<UIMachineSettingsNetwork*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+ pTab->polishTab();
+ }
+}
+
+void UIMachineSettingsNetworkPage::sltHandleAlternativeNameChange()
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return;
+
+ /* Determine the sender tab: */
+ UIMachineSettingsNetwork *pSender = qobject_cast<UIMachineSettingsNetwork*>(sender());
+ AssertPtrReturnVoid(pSender);
+
+ /* Enumerate alternatives for certain types: */
+ switch (pSender->attachmentType())
+ {
+ case KNetworkAttachmentType_Internal: refreshInternalNetworkList(); break;
+ case KNetworkAttachmentType_Generic: refreshGenericDriverList(); break;
+ default: break;
+ }
+
+ /* Update alternatives for all the tabs besides the sender: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ /* Get the iterated tab: */
+ UIMachineSettingsNetwork *pTab = qobject_cast<UIMachineSettingsNetwork*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+
+ /* Update all the tabs (except sender): */
+ if (pTab != pSender)
+ pTab->reloadAlternatives();
+ }
+}
+
+void UIMachineSettingsNetworkPage::sltHandleAdvancedButtonStateChange(bool fExpanded)
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return;
+
+ /* Update the advanced button states for all the pages: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ UIMachineSettingsNetwork *pTab = qobject_cast<UIMachineSettingsNetwork*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+ pTab->setAdvancedButtonExpanded(fExpanded);
+ }
+}
+
+void UIMachineSettingsNetworkPage::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheMachineNetwork;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Create main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Creating tab-widget: */
+ m_pTabWidget = new QITabWidget;
+ if (m_pTabWidget)
+ {
+ /* How many adapters to display: */
+ /** @todo r=klaus this needs to be done based on the actual chipset type of the VM,
+ * but in this place the m_machine field isn't set yet. My observation (on Linux)
+ * is that the limitation to 4 isn't necessary any more, but this needs to be checked
+ * on all platforms to be certain that it's usable everywhere. */
+ const ulong uCount = qMin((ULONG)4, uiCommon().virtualBox().GetSystemProperties().GetMaxNetworkAdapters(KChipsetType_PIIX3));
+
+ /* Create corresponding adapter tabs: */
+ for (ulong uSlot = 0; uSlot < uCount; ++uSlot)
+ {
+ /* Create adapter tab: */
+ UIMachineSettingsNetwork *pTab = new UIMachineSettingsNetwork(this);
+ if (pTab)
+ {
+ /* Tab connections: */
+ connect(pTab, &UIMachineSettingsNetwork::sigAlternativeNameChanged,
+ this, &UIMachineSettingsNetworkPage::sltHandleAlternativeNameChange);
+ connect(pTab, &UIMachineSettingsNetwork::sigAdvancedButtonStateChange,
+ this, &UIMachineSettingsNetworkPage::sltHandleAdvancedButtonStateChange);
+ connect(pTab, &UIMachineSettingsNetwork::sigValidityChanged,
+ this, &UIMachineSettingsNetworkPage::revalidate);
+
+ /* Add tab into tab-widget: */
+ m_pTabWidget->addTab(pTab, pTab->tabTitle());
+ }
+ }
+
+ /* Add tab-widget into layout: */
+ pLayoutMain->addWidget(m_pTabWidget);
+ }
+ }
+}
+
+void UIMachineSettingsNetworkPage::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+void UIMachineSettingsNetworkPage::refreshBridgedAdapterList()
+{
+ /* Reload bridged adapters: */
+ m_bridgedAdapterList = UINetworkAttachmentEditor::bridgedAdapters();
+}
+
+void UIMachineSettingsNetworkPage::refreshInternalNetworkList(bool fFullRefresh /* = false */)
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return;
+
+ /* Reload internal network list: */
+ m_internalNetworkList.clear();
+ /* Get internal network names from other VMs: */
+ if (fFullRefresh)
+ m_internalNetworkListSaved = UINetworkAttachmentEditor::internalNetworks();
+ m_internalNetworkList << m_internalNetworkListSaved;
+ /* Append internal network list with names from all the tabs: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ UIMachineSettingsNetwork *pTab = qobject_cast<UIMachineSettingsNetwork*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+ const QString strName = pTab->alternativeName(KNetworkAttachmentType_Internal);
+ if (!strName.isEmpty() && !m_internalNetworkList.contains(strName))
+ m_internalNetworkList << strName;
+ }
+}
+
+#ifdef VBOX_WITH_CLOUD_NET
+void UIMachineSettingsNetworkPage::refreshCloudNetworkList()
+{
+ /* Reload cloud network list: */
+ m_cloudNetworkList = UINetworkAttachmentEditor::cloudNetworks();
+}
+#endif /* VBOX_WITH_CLOUD_NET */
+
+#ifdef VBOX_WITH_VMNET
+void UIMachineSettingsNetworkPage::refreshHostOnlyNetworkList()
+{
+ /* Reload host-only network list: */
+ m_hostOnlyNetworkList = UINetworkAttachmentEditor::hostOnlyNetworks();
+}
+#endif /* VBOX_WITH_VMNET */
+
+void UIMachineSettingsNetworkPage::refreshHostInterfaceList()
+{
+ /* Reload host interfaces: */
+ m_hostInterfaceList = UINetworkAttachmentEditor::hostInterfaces();
+}
+
+void UIMachineSettingsNetworkPage::refreshGenericDriverList(bool fFullRefresh /* = false */)
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return;
+
+ /* Load generic driver list: */
+ m_genericDriverList.clear();
+ /* Get generic driver names from other VMs: */
+ if (fFullRefresh)
+ m_genericDriverListSaved = UINetworkAttachmentEditor::genericDrivers();
+ m_genericDriverList << m_genericDriverListSaved;
+ /* Append generic driver list with names from all the tabs: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ UIMachineSettingsNetwork *pTab = qobject_cast<UIMachineSettingsNetwork*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+ const QString strName = pTab->alternativeName(KNetworkAttachmentType_Generic);
+ if (!strName.isEmpty() && !m_genericDriverList.contains(strName))
+ m_genericDriverList << strName;
+ }
+}
+
+void UIMachineSettingsNetworkPage::refreshNATNetworkList()
+{
+ /* Reload nat networks: */
+ m_natNetworkList = UINetworkAttachmentEditor::natNetworks();
+}
+
+/* static */
+QString UIMachineSettingsNetworkPage::loadGenericProperties(const CNetworkAdapter &adapter)
+{
+ /* Prepare formatted string: */
+ QVector<QString> names;
+ QVector<QString> props;
+ props = adapter.GetProperties(QString(), names);
+ QString strResult;
+ /* Load generic properties: */
+ for (int i = 0; i < names.size(); ++i)
+ {
+ strResult += names[i] + "=" + props[i];
+ if (i < names.size() - 1)
+ strResult += "\n";
+ }
+ /* Return formatted string: */
+ return strResult;
+}
+
+/* static */
+bool UIMachineSettingsNetworkPage::saveGenericProperties(CNetworkAdapter &comAdapter, const QString &strProperties)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save generic properties: */
+ if (fSuccess)
+ {
+ /* Acquire 'added' properties: */
+ const QStringList newProps = strProperties.split("\n");
+
+ /* Insert 'added' properties: */
+ QHash<QString, QString> hash;
+ for (int i = 0; fSuccess && i < newProps.size(); ++i)
+ {
+ /* Parse property line: */
+ const QString strLine = newProps.at(i);
+ const QString strKey = strLine.section('=', 0, 0);
+ const QString strVal = strLine.section('=', 1, -1);
+ if (strKey.isEmpty() || strVal.isEmpty())
+ continue;
+ /* Save property in the adapter and the hash: */
+ comAdapter.SetProperty(strKey, strVal);
+ fSuccess = comAdapter.isOk();
+ hash[strKey] = strVal;
+ }
+
+ /* Acquire actual properties ('added' and 'removed'): */
+ QVector<QString> names;
+ QVector<QString> props;
+ if (fSuccess)
+ {
+ props = comAdapter.GetProperties(QString(), names);
+ fSuccess = comAdapter.isOk();
+ }
+
+ /* Exclude 'removed' properties: */
+ for (int i = 0; fSuccess && i < names.size(); ++i)
+ {
+ /* Get property name and value: */
+ const QString strKey = names.at(i);
+ const QString strVal = props.at(i);
+ if (strVal == hash.value(strKey))
+ continue;
+ /* Remove property from the adapter: */
+ // Actually we are _replacing_ property value,
+ // not _removing_ it at all, but we are replacing it
+ // with default constructed value, which is QString().
+ comAdapter.SetProperty(strKey, hash.value(strKey));
+ fSuccess = comAdapter.isOk();
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsNetworkPage::saveData()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save network settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* For each adapter: */
+ for (int iSlot = 0; fSuccess && iSlot < m_pTabWidget->count(); ++iSlot)
+ fSuccess = saveAdapterData(iSlot);
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsNetworkPage::saveAdapterData(int iSlot)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save adapter settings from cache: */
+ if (fSuccess && m_pCache->child(iSlot).wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineNetworkAdapter &oldAdapterData = m_pCache->child(iSlot).base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineNetworkAdapter &newAdapterData = m_pCache->child(iSlot).data();
+
+ /* Get network adapter for further activities: */
+ CNetworkAdapter comAdapter = m_machine.GetNetworkAdapter(iSlot);
+ fSuccess = m_machine.isOk() && comAdapter.isNotNull();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ else
+ {
+ /* Save whether the adapter is enabled: */
+ if (fSuccess && isMachineOffline() && newAdapterData.m_fAdapterEnabled != oldAdapterData.m_fAdapterEnabled)
+ {
+ comAdapter.SetEnabled(newAdapterData.m_fAdapterEnabled);
+ fSuccess = comAdapter.isOk();
+ }
+ /* Save adapter type: */
+ if (fSuccess && isMachineOffline() && newAdapterData.m_adapterType != oldAdapterData.m_adapterType)
+ {
+ comAdapter.SetAdapterType(newAdapterData.m_adapterType);
+ fSuccess = comAdapter.isOk();
+ }
+ /* Save adapter MAC address: */
+ if (fSuccess && isMachineOffline() && newAdapterData.m_strMACAddress != oldAdapterData.m_strMACAddress)
+ {
+ comAdapter.SetMACAddress(newAdapterData.m_strMACAddress);
+ fSuccess = comAdapter.isOk();
+ }
+ /* Save adapter attachment type: */
+ switch (newAdapterData.m_attachmentType)
+ {
+ case KNetworkAttachmentType_Bridged:
+ {
+ if (fSuccess && newAdapterData.m_strBridgedAdapterName != oldAdapterData.m_strBridgedAdapterName)
+ {
+ comAdapter.SetBridgedInterface(newAdapterData.m_strBridgedAdapterName);
+ fSuccess = comAdapter.isOk();
+ }
+ break;
+ }
+ case KNetworkAttachmentType_Internal:
+ {
+ if (fSuccess && newAdapterData.m_strInternalNetworkName != oldAdapterData.m_strInternalNetworkName)
+ {
+ comAdapter.SetInternalNetwork(newAdapterData.m_strInternalNetworkName);
+ fSuccess = comAdapter.isOk();
+ }
+ break;
+ }
+ case KNetworkAttachmentType_HostOnly:
+ {
+ if (fSuccess && newAdapterData.m_strHostInterfaceName != oldAdapterData.m_strHostInterfaceName)
+ {
+ comAdapter.SetHostOnlyInterface(newAdapterData.m_strHostInterfaceName);
+ fSuccess = comAdapter.isOk();
+ }
+ break;
+ }
+ case KNetworkAttachmentType_Generic:
+ {
+ if (fSuccess && newAdapterData.m_strGenericDriverName != oldAdapterData.m_strGenericDriverName)
+ {
+ comAdapter.SetGenericDriver(newAdapterData.m_strGenericDriverName);
+ fSuccess = comAdapter.isOk();
+ }
+ if (fSuccess && newAdapterData.m_strGenericProperties != oldAdapterData.m_strGenericProperties)
+ fSuccess = saveGenericProperties(comAdapter, newAdapterData.m_strGenericProperties);
+ break;
+ }
+ case KNetworkAttachmentType_NATNetwork:
+ {
+ if (fSuccess && newAdapterData.m_strNATNetworkName != oldAdapterData.m_strNATNetworkName)
+ {
+ comAdapter.SetNATNetwork(newAdapterData.m_strNATNetworkName);
+ fSuccess = comAdapter.isOk();
+ }
+ break;
+ }
+#ifdef VBOX_WITH_CLOUD_NET
+ case KNetworkAttachmentType_Cloud:
+ {
+ if (fSuccess && newAdapterData.m_strCloudNetworkName != oldAdapterData.m_strCloudNetworkName)
+ {
+ comAdapter.SetCloudNetwork(newAdapterData.m_strCloudNetworkName);
+ fSuccess = comAdapter.isOk();
+ }
+ break;
+ }
+#endif /* VBOX_WITH_CLOUD_NET */
+#ifdef VBOX_WITH_VMNET
+ case KNetworkAttachmentType_HostOnlyNetwork:
+ {
+ if (fSuccess && newAdapterData.m_strHostOnlyNetworkName != oldAdapterData.m_strHostOnlyNetworkName)
+ {
+ comAdapter.SetHostOnlyNetwork(newAdapterData.m_strHostOnlyNetworkName);
+ fSuccess = comAdapter.isOk();
+ }
+ break;
+ }
+#endif /* VBOX_WITH_VMNET */
+ default:
+ break;
+ }
+ if (fSuccess && newAdapterData.m_attachmentType != oldAdapterData.m_attachmentType)
+ {
+ comAdapter.SetAttachmentType(newAdapterData.m_attachmentType);
+ fSuccess = comAdapter.isOk();
+ }
+ /* Save adapter promiscuous mode: */
+ if (fSuccess && newAdapterData.m_promiscuousMode != oldAdapterData.m_promiscuousMode)
+ {
+ comAdapter.SetPromiscModePolicy(newAdapterData.m_promiscuousMode);
+ fSuccess = comAdapter.isOk();
+ }
+ /* Save whether the adapter cable connected: */
+ if (fSuccess && newAdapterData.m_fCableConnected != oldAdapterData.m_fCableConnected)
+ {
+ comAdapter.SetCableConnected(newAdapterData.m_fCableConnected);
+ fSuccess = comAdapter.isOk();
+ }
+
+ /* Get NAT engine for further activities: */
+ CNATEngine comEngine;
+ if (fSuccess)
+ {
+ comEngine = comAdapter.GetNATEngine();
+ fSuccess = comAdapter.isOk() && comEngine.isNotNull();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comAdapter));
+ else
+ {
+ /* Save adapter port forwarding rules: */
+ if ( oldAdapterData.m_attachmentType == KNetworkAttachmentType_NAT
+ || newAdapterData.m_attachmentType == KNetworkAttachmentType_NAT)
+ {
+ /* For each rule: */
+ for (int iRule = 0; fSuccess && iRule < m_pCache->child(iSlot).childCount(); ++iRule)
+ {
+ /* Get rule cache: */
+ const UISettingsCachePortForwardingRule &ruleCache = m_pCache->child(iSlot).child(iRule);
+
+ /* Remove rule marked for 'remove' or 'update': */
+ if (ruleCache.wasRemoved() || ruleCache.wasUpdated())
+ {
+ comEngine.RemoveRedirect(ruleCache.base().name);
+ fSuccess = comEngine.isOk();
+ }
+ }
+ for (int iRule = 0; fSuccess && iRule < m_pCache->child(iSlot).childCount(); ++iRule)
+ {
+ /* Get rule cache: */
+ const UISettingsCachePortForwardingRule &ruleCache = m_pCache->child(iSlot).child(iRule);
+
+ /* Create rule marked for 'create' or 'update': */
+ if (ruleCache.wasCreated() || ruleCache.wasUpdated())
+ {
+ comEngine.AddRedirect(ruleCache.data().name, ruleCache.data().protocol,
+ ruleCache.data().hostIp, ruleCache.data().hostPort.value(),
+ ruleCache.data().guestIp, ruleCache.data().guestPort.value());
+ fSuccess = comEngine.isOk();
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comEngine));
+ }
+ }
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+# include "UIMachineSettingsNetwork.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsNetwork.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsNetwork.h
new file mode 100644
index 00000000..c6a0a313
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsNetwork.h
@@ -0,0 +1,178 @@
+/* $Id: UIMachineSettingsNetwork.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsNetwork class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsNetwork_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsNetwork_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+#include "UIMachineSettingsPortForwardingDlg.h"
+
+/* Forward declarations: */
+class QITabWidget;
+struct UIDataSettingsMachineNetwork;
+struct UIDataSettingsMachineNetworkAdapter;
+typedef UISettingsCache<UIDataPortForwardingRule> UISettingsCachePortForwardingRule;
+typedef UISettingsCachePool<UIDataSettingsMachineNetworkAdapter, UISettingsCachePortForwardingRule> UISettingsCacheMachineNetworkAdapter;
+typedef UISettingsCachePool<UIDataSettingsMachineNetwork, UISettingsCacheMachineNetworkAdapter> UISettingsCacheMachineNetwork;
+
+/** Machine settings: Network page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsNetworkPage : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Network settings page. */
+ UIMachineSettingsNetworkPage();
+ /** Destructs Network settings page. */
+ virtual ~UIMachineSettingsNetworkPage() RT_OVERRIDE;
+
+ /** Returns the bridged adapter list. */
+ const QStringList &bridgedAdapterList() const { return m_bridgedAdapterList; }
+ /** Returns the internal network list. */
+ const QStringList &internalNetworkList() const { return m_internalNetworkList; }
+ /** Returns the host-only interface list. */
+ const QStringList &hostInterfaceList() const { return m_hostInterfaceList; }
+ /** Returns the generic driver list. */
+ const QStringList &genericDriverList() const { return m_genericDriverList; }
+ /** Returns the NAT network list. */
+ const QStringList &natNetworkList() const { return m_natNetworkList; }
+#ifdef VBOX_WITH_CLOUD_NET
+ /** Returns the cloud network list. */
+ const QStringList &cloudNetworkList() const { return m_cloudNetworkList; }
+#endif
+#ifdef VBOX_WITH_VMNET
+ /** Returns the host-only network list. */
+ const QStringList &hostOnlyNetworkList() const { return m_hostOnlyNetworkList; }
+#endif
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ virtual bool validate(QList<UIValidationMessage> &messages) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles adapter alternative name change. */
+ void sltHandleAlternativeNameChange();
+ /** Handles whether the advanced button is @a fExpanded. */
+ void sltHandleAdvancedButtonStateChange(bool fExpanded);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Repopulates bridged adapter list. */
+ void refreshBridgedAdapterList();
+ /** Repopulates internal network list. */
+ void refreshInternalNetworkList(bool fFullRefresh = false);
+ /** Repopulates host-only interface list. */
+ void refreshHostInterfaceList();
+ /** Repopulates generic driver list. */
+ void refreshGenericDriverList(bool fFullRefresh = false);
+ /** Repopulates NAT network list. */
+ void refreshNATNetworkList();
+#ifdef VBOX_WITH_CLOUD_NET
+ /** Repopulates cloud network list. */
+ void refreshCloudNetworkList();
+#endif
+#ifdef VBOX_WITH_VMNET
+ /** Repopulates host-only network list. */
+ void refreshHostOnlyNetworkList();
+#endif
+
+ /** Loads generic properties from passed @a adapter. */
+ static QString loadGenericProperties(const CNetworkAdapter &adapter);
+ /** Saves generic @a strProperties to passed @a adapter. */
+ static bool saveGenericProperties(CNetworkAdapter &comAdapter, const QString &strProperties);
+
+ /** Saves existing data from cache. */
+ bool saveData();
+ /** Saves existing adapter data from cache. */
+ bool saveAdapterData(int iSlot);
+
+ /** Holds the bridged adapter list. */
+ QStringList m_bridgedAdapterList;
+ /** Holds the internal network list. */
+ QStringList m_internalNetworkList;
+ /** Holds the saved internal network list. */
+ QStringList m_internalNetworkListSaved;
+ /** Holds the host-only interface list. */
+ QStringList m_hostInterfaceList;
+ /** Holds the generic driver list. */
+ QStringList m_genericDriverList;
+ /** Holds the saved generic driver list. */
+ QStringList m_genericDriverListSaved;
+ /** Holds the NAT network list. */
+ QStringList m_natNetworkList;
+#ifdef VBOX_WITH_CLOUD_NET
+ /** Holds the cloud network list. */
+ QStringList m_cloudNetworkList;
+#endif
+#ifdef VBOX_WITH_VMNET
+ /** Holds the host-only network list. */
+ QStringList m_hostOnlyNetworkList;
+#endif
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheMachineNetwork *m_pCache;
+
+ /** Holds the tab-widget instance. */
+ QITabWidget *m_pTabWidget;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsNetwork_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsPortForwardingDlg.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsPortForwardingDlg.cpp
new file mode 100644
index 00000000..4fd99487
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsPortForwardingDlg.cpp
@@ -0,0 +1,112 @@
+/* $Id: UIMachineSettingsPortForwardingDlg.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsPortForwardingDlg class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+#include <QPushButton>
+
+/* GUI includes: */
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIMachineSettingsPortForwardingDlg.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "QIDialogButtonBox.h"
+
+
+UIMachineSettingsPortForwardingDlg::UIMachineSettingsPortForwardingDlg(QWidget *pParent,
+ const UIPortForwardingDataList &rules)
+ : QIWithRetranslateUI<QIDialog>(pParent)
+ , m_pTable(0)
+ , m_pButtonBox(0)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/nw_32px.png", ":/nw_16px.png"));
+#endif
+
+ /* Create layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ {
+ /* Create table: */
+ m_pTable = new UIPortForwardingTable(rules, false, true);
+ {
+ /* Configure table: */
+ m_pTable->layout()->setContentsMargins(0, 0, 0, 0);
+ }
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
+ {
+ /* Configure button-box: */
+ connect(m_pButtonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
+ this, &UIMachineSettingsPortForwardingDlg::accept);
+ connect(m_pButtonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
+ this, &UIMachineSettingsPortForwardingDlg::reject);
+ }
+ /* Add widgets into layout: */
+ pMainLayout->addWidget(m_pTable);
+ pMainLayout->addWidget(m_pButtonBox);
+ }
+
+ /* Retranslate dialog: */
+ retranslateUi();
+
+ /* Limit the minimum size to 33% of screen size: */
+ setMinimumSize(gpDesktop->screenGeometry(this).size() / 3);
+}
+
+const UIPortForwardingDataList UIMachineSettingsPortForwardingDlg::rules() const
+{
+ return m_pTable->rules();
+}
+
+void UIMachineSettingsPortForwardingDlg::accept()
+{
+ /* Make sure table has own data committed: */
+ m_pTable->makeSureEditorDataCommitted();
+ /* Validate table: */
+ bool fPassed = m_pTable->validate();
+ if (!fPassed)
+ return;
+ /* Call to base-class: */
+ QIWithRetranslateUI<QIDialog>::accept();
+}
+
+void UIMachineSettingsPortForwardingDlg::reject()
+{
+ /* Ask user to discard table changes if necessary: */
+ if ( m_pTable->isChanged()
+ && !msgCenter().confirmCancelingPortForwardingDialog(window()))
+ return;
+ /* Call to base-class: */
+ QIWithRetranslateUI<QIDialog>::reject();
+}
+
+void UIMachineSettingsPortForwardingDlg::retranslateUi()
+{
+ /* Set window title: */
+ setWindowTitle(tr("Port Forwarding Rules"));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsPortForwardingDlg.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsPortForwardingDlg.h
new file mode 100644
index 00000000..2f0b2699
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsPortForwardingDlg.h
@@ -0,0 +1,71 @@
+/* $Id: UIMachineSettingsPortForwardingDlg.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsPortForwardingDlg class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsPortForwardingDlg_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsPortForwardingDlg_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "QIDialog.h"
+#include "UIPortForwardingTable.h"
+
+/* Forward declarations: */
+class QIDialogButtonBox;
+
+/* Machine settings / Network page / NAT attachment / Port forwarding dialog: */
+class SHARED_LIBRARY_STUFF UIMachineSettingsPortForwardingDlg : public QIWithRetranslateUI<QIDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /* Constructor/destructor: */
+ UIMachineSettingsPortForwardingDlg(QWidget *pParent, const UIPortForwardingDataList &rules);
+
+ /* API: Rules stuff: */
+ const UIPortForwardingDataList rules() const;
+
+private slots:
+
+ /* Handlers: Dialog stuff: */
+ void accept();
+ void reject();
+
+private:
+
+ /* Handler: Translation stuff: */
+ void retranslateUi();
+
+ /* Widgets: */
+ UIPortForwardingTable *m_pTable;
+ QIDialogButtonBox *m_pButtonBox;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsPortForwardingDlg_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSF.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSF.cpp
new file mode 100644
index 00000000..6b39c633
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSF.cpp
@@ -0,0 +1,500 @@
+/* $Id: UIMachineSettingsSF.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsSF class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIErrorString.h"
+#include "UIMachineSettingsSF.h"
+#include "UISharedFoldersEditor.h"
+
+
+/** Machine settings: Shared Folder data structure. */
+struct UIDataSettingsSharedFolder
+{
+ /** Constructs data. */
+ UIDataSettingsSharedFolder() {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsSharedFolder &other) const
+ {
+ return true
+ && m_guiData == other.m_guiData
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsSharedFolder &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsSharedFolder &other) const { return !equal(other); }
+
+ /** Holds the shared folder data. */
+ UIDataSharedFolder m_guiData;
+};
+
+
+/** Machine settings: Shared Folders page data structure. */
+struct UIDataSettingsSharedFolders
+{
+ /** Constructs data. */
+ UIDataSettingsSharedFolders() {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsSharedFolders & /* other */) const { return true; }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsSharedFolders & /* other */) const { return false; }
+};
+
+
+UIMachineSettingsSF::UIMachineSettingsSF()
+ : m_pCache(0)
+ , m_pEditorSharedFolders(0)
+{
+ prepare();
+}
+
+UIMachineSettingsSF::~UIMachineSettingsSF()
+{
+ cleanup();
+}
+
+bool UIMachineSettingsSF::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsSF::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Prepare old data: */
+ UIDataSettingsSharedFolders oldFoldersData;
+
+ /* Get actual folders: */
+ QMultiMap<UISharedFolderType, CSharedFolder> folders;
+ /* Load machine (permanent) folders if allowed: */
+ if (isSharedFolderTypeSupported(UISharedFolderType_Machine))
+ {
+ foreach (const CSharedFolder &folder, getSharedFolders(UISharedFolderType_Machine))
+ folders.insert(UISharedFolderType_Machine, folder);
+ }
+ /* Load console (temporary) folders if allowed: */
+ if (isSharedFolderTypeSupported(UISharedFolderType_Console))
+ {
+ foreach (const CSharedFolder &folder, getSharedFolders(UISharedFolderType_Console))
+ folders.insert(UISharedFolderType_Console, folder);
+ }
+
+ /* For each folder type: */
+ foreach (const UISharedFolderType &enmFolderType, folders.keys())
+ {
+ /* For each folder of current type: */
+ const QList<CSharedFolder> &currentTypeFolders = folders.values(enmFolderType);
+ for (int iFolderIndex = 0; iFolderIndex < currentTypeFolders.size(); ++iFolderIndex)
+ {
+ /* Prepare old data & cache key: */
+ UIDataSettingsSharedFolder oldFolderData;
+ QString strFolderKey = QString::number(iFolderIndex);
+
+ /* Check whether folder is valid: */
+ const CSharedFolder &comFolder = currentTypeFolders.at(iFolderIndex);
+ if (!comFolder.isNull())
+ {
+ /* Gather old data: */
+ oldFolderData.m_guiData.m_enmType = enmFolderType;
+ oldFolderData.m_guiData.m_strName = comFolder.GetName();
+ oldFolderData.m_guiData.m_strPath = comFolder.GetHostPath();
+ oldFolderData.m_guiData.m_fWritable = comFolder.GetWritable();
+ oldFolderData.m_guiData.m_fAutoMount = comFolder.GetAutoMount();
+ oldFolderData.m_guiData.m_strAutoMountPoint = comFolder.GetAutoMountPoint();
+ /* Override folder cache key: */
+ strFolderKey = oldFolderData.m_guiData.m_strName;
+ }
+
+ /* Cache old data: */
+ m_pCache->child(strFolderKey).cacheInitialData(oldFolderData);
+ }
+ }
+
+ /* Cache old data: */
+ m_pCache->cacheInitialData(oldFoldersData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsSF::getFromCache()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pEditorSharedFolders)
+ return;
+
+ /* Load old data from cache: */
+ QList<UIDataSharedFolder> folders;
+ for (int iFolderIndex = 0; iFolderIndex < m_pCache->childCount(); ++iFolderIndex)
+ folders << m_pCache->child(iFolderIndex).base().m_guiData;
+ m_pEditorSharedFolders->setValue(folders);
+
+ /* Polish page finally: */
+ polishPage();
+}
+
+void UIMachineSettingsSF::putToCache()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pEditorSharedFolders)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsSharedFolders newFoldersData;
+
+ /* Cache new data: */
+ foreach (const UIDataSharedFolder &guiData, m_pEditorSharedFolders->value())
+ {
+ /* Gather and cache new data: */
+ UIDataSettingsSharedFolder newFolderData;
+ newFolderData.m_guiData = guiData;
+ m_pCache->child(newFolderData.m_guiData.m_strName).cacheCurrentData(newFolderData);
+ }
+ m_pCache->cacheCurrentData(newFoldersData);
+}
+
+void UIMachineSettingsSF::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsSF::retranslateUi()
+{
+}
+
+void UIMachineSettingsSF::polishPage()
+{
+ /* Polish availability: */
+ m_pEditorSharedFolders->setFeatureAvailable(isMachineInValidMode());
+ m_pEditorSharedFolders->setFoldersAvailable(UISharedFolderType_Machine, isSharedFolderTypeSupported(UISharedFolderType_Machine));
+ m_pEditorSharedFolders->setFoldersAvailable(UISharedFolderType_Console, isSharedFolderTypeSupported(UISharedFolderType_Console));
+}
+
+void UIMachineSettingsSF::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheSharedFolders;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsSF::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare settings editor: */
+ m_pEditorSharedFolders = new UISharedFoldersEditor(this);
+ if (m_pEditorSharedFolders)
+ pLayout->addWidget(m_pEditorSharedFolders);
+ }
+}
+
+void UIMachineSettingsSF::prepareConnections()
+{
+}
+
+void UIMachineSettingsSF::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIMachineSettingsSF::isSharedFolderTypeSupported(UISharedFolderType enmSharedFolderType) const
+{
+ switch (enmSharedFolderType)
+ {
+ case UISharedFolderType_Machine: return isMachineInValidMode();
+ case UISharedFolderType_Console: return isMachineOnline();
+ default: return false;
+ }
+}
+
+CSharedFolderVector UIMachineSettingsSF::getSharedFolders(UISharedFolderType enmFoldersType)
+{
+ /* Wrap up the getter below: */
+ CSharedFolderVector folders;
+ getSharedFolders(enmFoldersType, folders);
+ return folders;
+}
+
+bool UIMachineSettingsSF::getSharedFolders(UISharedFolderType enmFoldersType, CSharedFolderVector &folders)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Load folders of passed type: */
+ if (fSuccess)
+ {
+ /* Make sure folder type is supported: */
+ AssertReturn(isSharedFolderTypeSupported(enmFoldersType), false);
+ switch (enmFoldersType)
+ {
+ case UISharedFolderType_Machine:
+ {
+ /* Make sure machine was specified: */
+ AssertReturn(!m_machine.isNull(), false);
+ /* Load machine folders: */
+ folders = m_machine.GetSharedFolders();
+ fSuccess = m_machine.isOk();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+
+ break;
+ }
+ case UISharedFolderType_Console:
+ {
+ /* Make sure console was specified: */
+ AssertReturn(!m_console.isNull(), false);
+ /* Load console folders: */
+ folders = m_console.GetSharedFolders();
+ fSuccess = m_console.isOk();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_console));
+
+ break;
+ }
+ default:
+ AssertFailedReturn(false);
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsSF::getSharedFolder(const QString &strFolderName, const CSharedFolderVector &folders, CSharedFolder &comFolder)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Look for a folder with passed name: */
+ for (int iFolderIndex = 0; fSuccess && iFolderIndex < folders.size(); ++iFolderIndex)
+ {
+ /* Get current folder: */
+ const CSharedFolder &comCurrentFolder = folders.at(iFolderIndex);
+
+ /* Get current folder name for further activities: */
+ QString strCurrentFolderName;
+ if (fSuccess)
+ {
+ strCurrentFolderName = comCurrentFolder.GetName();
+ fSuccess = comCurrentFolder.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comCurrentFolder));
+
+ /* If that's the folder we are looking for => take it: */
+ if (fSuccess && strCurrentFolderName == strFolderName)
+ comFolder = folders[iFolderIndex];
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsSF::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save folders settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* For each folder: */
+ for (int iFolderIndex = 0; fSuccess && iFolderIndex < m_pCache->childCount(); ++iFolderIndex)
+ {
+ /* Get folder cache: */
+ const UISettingsCacheSharedFolder &folderCache = m_pCache->child(iFolderIndex);
+
+ /* Remove folder marked for 'remove' or 'update': */
+ if (fSuccess && (folderCache.wasRemoved() || folderCache.wasUpdated()))
+ fSuccess = removeSharedFolder(folderCache);
+
+ /* Create folder marked for 'create' or 'update': */
+ if (fSuccess && (folderCache.wasCreated() || folderCache.wasUpdated()))
+ fSuccess = createSharedFolder(folderCache);
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsSF::removeSharedFolder(const UISettingsCacheSharedFolder &folderCache)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Remove folder: */
+ if (fSuccess)
+ {
+ /* Get folder data: */
+ const UIDataSettingsSharedFolder &newFolderData = folderCache.base();
+ const UISharedFolderType enmFoldersType = newFolderData.m_guiData.m_enmType;
+ const QString strFolderName = newFolderData.m_guiData.m_strName;
+
+ /* Get current folders: */
+ CSharedFolderVector folders;
+ if (fSuccess)
+ fSuccess = getSharedFolders(enmFoldersType, folders);
+
+ /* Search for a folder with the same name: */
+ CSharedFolder comFolder;
+ if (fSuccess)
+ fSuccess = getSharedFolder(strFolderName, folders, comFolder);
+
+ /* Make sure such folder really exists: */
+ if (fSuccess && !comFolder.isNull())
+ {
+ /* Remove existing folder: */
+ switch (enmFoldersType)
+ {
+ case UISharedFolderType_Machine:
+ {
+ /* Remove existing folder: */
+ m_machine.RemoveSharedFolder(strFolderName);
+ /* Check that machine is OK: */
+ fSuccess = m_machine.isOk();
+ if (!fSuccess)
+ {
+ /* Show error message: */
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ break;
+ }
+ case UISharedFolderType_Console:
+ {
+ /* Remove existing folder: */
+ m_console.RemoveSharedFolder(strFolderName);
+ /* Check that console is OK: */
+ fSuccess = m_console.isOk();
+ if (!fSuccess)
+ {
+ /* Show error message: */
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_console));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsSF::createSharedFolder(const UISettingsCacheSharedFolder &folderCache)
+{
+ /* Get folder data: */
+ const UIDataSettingsSharedFolder &newFolderData = folderCache.data();
+ const UISharedFolderType enmFoldersType = newFolderData.m_guiData.m_enmType;
+ const QString strFolderName = newFolderData.m_guiData.m_strName;
+ const QString strFolderPath = newFolderData.m_guiData.m_strPath;
+ const bool fIsWritable = newFolderData.m_guiData.m_fWritable;
+ const bool fIsAutoMount = newFolderData.m_guiData.m_fAutoMount;
+ const QString strAutoMountPoint = newFolderData.m_guiData.m_strAutoMountPoint;
+
+ /* Get current folders: */
+ CSharedFolderVector folders;
+ bool fSuccess = getSharedFolders(enmFoldersType, folders);
+
+ /* Search for a folder with the same name: */
+ CSharedFolder comFolder;
+ if (fSuccess)
+ fSuccess = getSharedFolder(strFolderName, folders, comFolder);
+
+ /* Make sure such folder doesn't exist: */
+ if (fSuccess && comFolder.isNull())
+ {
+ /* Create new folder: */
+ switch (enmFoldersType)
+ {
+ case UISharedFolderType_Machine:
+ {
+ /* Create new folder: */
+ m_machine.CreateSharedFolder(strFolderName, strFolderPath, fIsWritable, fIsAutoMount, strAutoMountPoint);
+ /* Show error if the operation failed: */
+ fSuccess = m_machine.isOk();
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ break;
+ }
+ case UISharedFolderType_Console:
+ {
+ /* Create new folder: */
+ m_console.CreateSharedFolder(strFolderName, strFolderPath, fIsWritable, fIsAutoMount, strAutoMountPoint);
+ /* Show error if the operation failed: */
+ fSuccess = m_console.isOk();
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_console));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSF.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSF.h
new file mode 100644
index 00000000..b7f99758
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSF.h
@@ -0,0 +1,124 @@
+/* $Id: UIMachineSettingsSF.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsSF class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsSF_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsSF_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* COM includes: */
+#include "CSharedFolder.h"
+
+/* Forward declarations: */
+class UISharedFoldersEditor;
+
+struct UIDataSettingsSharedFolder;
+struct UIDataSettingsSharedFolders;
+typedef UISettingsCache<UIDataSettingsSharedFolder> UISettingsCacheSharedFolder;
+typedef UISettingsCachePool<UIDataSettingsSharedFolders, UISettingsCacheSharedFolder> UISettingsCacheSharedFolders;
+
+
+/** Machine settings: Shared Folders page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsSF : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Shared Folders settings page. */
+ UIMachineSettingsSF();
+ /** Destructs Shared Folders settings page. */
+ virtual ~UIMachineSettingsSF() RT_OVERRIDE;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares Widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Returns whether the corresponding @a enmFoldersType supported. */
+ bool isSharedFolderTypeSupported(UISharedFolderType enmFoldersType) const;
+
+ /** Gathers a vector of shared folders of the passed @a enmFoldersType. */
+ CSharedFolderVector getSharedFolders(UISharedFolderType enmFoldersType);
+ /** Gathers a vector of shared folders of the passed @a enmFoldersType. */
+ bool getSharedFolders(UISharedFolderType enmFoldersType, CSharedFolderVector &folders);
+ /** Look for a folder with the the passed @a strFolderName. */
+ bool getSharedFolder(const QString &strFolderName, const CSharedFolderVector &folders, CSharedFolder &comFolder);
+
+ /** Saves existing data from cache. */
+ bool saveData();
+ /** Removes shared folder defined by a @a folderCache. */
+ bool removeSharedFolder(const UISettingsCacheSharedFolder &folderCache);
+ /** Creates shared folder defined by a @a folderCache. */
+ bool createSharedFolder(const UISettingsCacheSharedFolder &folderCache);
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheSharedFolders *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the shared folders editor instance. */
+ UISharedFoldersEditor *m_pEditorSharedFolders;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsSF_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSerial.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSerial.cpp
new file mode 100644
index 00000000..5e60da69
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSerial.cpp
@@ -0,0 +1,1153 @@
+/* $Id: UIMachineSettingsSerial.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsSerial class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDir>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QRegularExpressionValidator>
+
+/* GUI includes: */
+#include "QITabWidget.h"
+#include "QIWidgetValidator.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIErrorString.h"
+#include "UIMachineSettingsSerial.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "CSerialPort.h"
+
+
+/** Machine settings: Serial Port tab data structure. */
+struct UIDataSettingsMachineSerialPort
+{
+ /** Constructs data. */
+ UIDataSettingsMachineSerialPort()
+ : m_iSlot(-1)
+ , m_fPortEnabled(false)
+ , m_uIRQ(0)
+ , m_uIOBase(0)
+ , m_hostMode(KPortMode_Disconnected)
+ , m_fServer(false)
+ , m_strPath(QString())
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineSerialPort &other) const
+ {
+ return true
+ && (m_iSlot == other.m_iSlot)
+ && (m_fPortEnabled == other.m_fPortEnabled)
+ && (m_uIRQ == other.m_uIRQ)
+ && (m_uIOBase == other.m_uIOBase)
+ && (m_hostMode == other.m_hostMode)
+ && (m_fServer == other.m_fServer)
+ && (m_strPath == other.m_strPath)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineSerialPort &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineSerialPort &other) const { return !equal(other); }
+
+ /** Holds the serial port slot number. */
+ int m_iSlot;
+ /** Holds whether the serial port is enabled. */
+ bool m_fPortEnabled;
+ /** Holds the serial port IRQ. */
+ ulong m_uIRQ;
+ /** Holds the serial port IO base. */
+ ulong m_uIOBase;
+ /** Holds the serial port host mode. */
+ KPortMode m_hostMode;
+ /** Holds whether the serial port is server. */
+ bool m_fServer;
+ /** Holds the serial port path. */
+ QString m_strPath;
+};
+
+
+/** Machine settings: Serial page data structure. */
+struct UIDataSettingsMachineSerial
+{
+ /** Constructs data. */
+ UIDataSettingsMachineSerial() {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineSerial & /* other */) const { return true; }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineSerial & /* other */) const { return false; }
+};
+
+
+/** Machine settings: Serial Port tab. */
+class UIMachineSettingsSerial : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about port changed. */
+ void sigPortChanged();
+ /** Notifies about path changed. */
+ void sigPathChanged();
+ /** Notifies about validity changed. */
+ void sigValidityChanged();
+
+public:
+
+ /** Constructs tab passing @a pParent to the base-class. */
+ UIMachineSettingsSerial(UIMachineSettingsSerialPage *pParent);
+
+ /** Loads port data from @a portCache. */
+ void getPortDataFromCache(const UISettingsCacheMachineSerialPort &portCache);
+ /** Saves port data to @a portCache. */
+ void putPortDataToCache(UISettingsCacheMachineSerialPort &portCache);
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ bool validate(QList<UIValidationMessage> &messages);
+
+ /** Configures tab order according to passed @a pWidget. */
+ QWidget *setOrderAfter(QWidget *pWidget);
+
+ /** Returns tab title. */
+ QString tabTitle() const;
+ /** Returns whether port is enabled. */
+ bool isPortEnabled() const;
+ /** Returns IRQ. */
+ QString irq() const;
+ /** Returns IO port. */
+ QString ioPort() const;
+ /** Returns path. */
+ QString path() const;
+
+ /** Performs tab polishing. */
+ void polishTab();
+
+protected:
+
+ /** Handles translation event. */
+ void retranslateUi();
+
+private slots:
+
+ /** Handles port availability being toggled to @a fOn. */
+ void sltHandlePortAvailabilityToggled(bool fOn);
+ /** Handles port standard @a strOption being activated. */
+ void sltHandlePortStandardOptionActivated(const QString &strOption);
+ /** Handles port mode change to item with certain @a iIndex. */
+ void sltHandlePortModeChange(int iIndex);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Populates combo-boxes. */
+ void populateComboboxes();
+
+ /** Holds the parent page reference. */
+ UIMachineSettingsSerialPage *m_pParent;
+
+ /** Holds the port slot number. */
+ int m_iSlot;
+ /** Holds the port mode. */
+ KPortMode m_enmPortMode;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the port check-box instance. */
+ QCheckBox *m_pCheckBoxPort;
+ /** Holds the port settings widget instance. */
+ QWidget *m_pWidgetPortSettings;
+ /** Holds the number label instance. */
+ QLabel *m_pLabelNumber;
+ /** Holds the number combo instance. */
+ QComboBox *m_pComboNumber;
+ /** Holds the IRQ label instance. */
+ QLabel *m_pLabelIRQ;
+ /** Holds the IRQ editor instance. */
+ QLineEdit *m_pLineEditIRQ;
+ /** Holds the IO port label instance. */
+ QLabel *m_pLabelIOPort;
+ /** Holds the IO port editor instance. */
+ QLineEdit *m_pLineEditIOPort;
+ /** Holds the mode label instance. */
+ QLabel *m_pLabelMode;
+ /** Holds the mode combo instance. */
+ QComboBox *m_pComboMode;
+ /** Holds the pipe check-box instance. */
+ QCheckBox *m_pCheckBoxPipe;
+ /** Holds the path label instance. */
+ QLabel *m_pLabelPath;
+ /** Holds the path editor instance. */
+ QLineEdit *m_pEditorPath;
+ /** @} */
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMachineSettingsSerial implementation. *
+*********************************************************************************************************************************/
+
+UIMachineSettingsSerial::UIMachineSettingsSerial(UIMachineSettingsSerialPage *pParent)
+ : QIWithRetranslateUI<QWidget>(0)
+ , m_pParent(pParent)
+ , m_iSlot(-1)
+ , m_enmPortMode(KPortMode_Max)
+ , m_pCheckBoxPort(0)
+ , m_pWidgetPortSettings(0)
+ , m_pLabelNumber(0)
+ , m_pComboNumber(0)
+ , m_pLabelIRQ(0)
+ , m_pLineEditIRQ(0)
+ , m_pLabelIOPort(0)
+ , m_pLineEditIOPort(0)
+ , m_pLabelMode(0)
+ , m_pComboMode(0)
+ , m_pCheckBoxPipe(0)
+ , m_pLabelPath(0)
+ , m_pEditorPath(0)
+{
+ prepare();
+}
+
+void UIMachineSettingsSerial::getPortDataFromCache(const UISettingsCacheMachineSerialPort &portCache)
+{
+ /* Get old data: */
+ const UIDataSettingsMachineSerialPort &oldPortData = portCache.base();
+
+ /* Load port number: */
+ m_iSlot = oldPortData.m_iSlot;
+
+ /* Load port data: */
+ if (m_pCheckBoxPort)
+ m_pCheckBoxPort->setChecked(oldPortData.m_fPortEnabled);
+ if (m_pComboNumber)
+ m_pComboNumber->setCurrentIndex(m_pComboNumber->findText(UITranslator::toCOMPortName(oldPortData.m_uIRQ, oldPortData.m_uIOBase)));
+ if (m_pLineEditIRQ)
+ m_pLineEditIRQ->setText(QString::number(oldPortData.m_uIRQ));
+ if (m_pLineEditIOPort)
+ m_pLineEditIOPort->setText("0x" + QString::number(oldPortData.m_uIOBase, 16).toUpper());
+ m_enmPortMode = oldPortData.m_hostMode;
+ if (m_pCheckBoxPipe)
+ m_pCheckBoxPipe->setChecked(!oldPortData.m_fServer);
+ if (m_pEditorPath)
+ m_pEditorPath->setText(oldPortData.m_strPath);
+
+ /* Repopulate combo-boxes content: */
+ populateComboboxes();
+ /* Ensure everything is up-to-date */
+ if (m_pCheckBoxPort)
+ sltHandlePortAvailabilityToggled(m_pCheckBoxPort->isChecked());
+}
+
+void UIMachineSettingsSerial::putPortDataToCache(UISettingsCacheMachineSerialPort &portCache)
+{
+ /* Prepare new data: */
+ UIDataSettingsMachineSerialPort newPortData;
+
+ /* Save port number: */
+ newPortData.m_iSlot = m_iSlot;
+
+ /* Save port data: */
+ if (m_pCheckBoxPort)
+ newPortData.m_fPortEnabled = m_pCheckBoxPort->isChecked();
+ if (m_pLineEditIRQ)
+ newPortData.m_uIRQ = m_pLineEditIRQ->text().toULong(NULL, 0);
+ if (m_pLineEditIOPort)
+ newPortData.m_uIOBase = m_pLineEditIOPort->text().toULong(NULL, 0);
+ if (m_pCheckBoxPipe)
+ newPortData.m_fServer = !m_pCheckBoxPipe->isChecked();
+ if (m_pComboMode)
+ newPortData.m_hostMode = m_pComboMode->currentData().value<KPortMode>();
+ if (m_pEditorPath)
+ newPortData.m_strPath = QDir::toNativeSeparators(m_pEditorPath->text());
+
+ /* Cache new data: */
+ portCache.cacheCurrentData(newPortData);
+}
+
+bool UIMachineSettingsSerial::validate(QList<UIValidationMessage> &messages)
+{
+ /* Pass by default: */
+ bool fPass = true;
+
+ /* Prepare message: */
+ UIValidationMessage message;
+ message.first = UITranslator::removeAccelMark(tabTitle());
+
+ if ( m_pCheckBoxPort
+ && m_pCheckBoxPort->isChecked())
+ {
+ /* Check the port attribute emptiness & uniqueness: */
+ const QString strIRQ = m_pLineEditIRQ ? m_pLineEditIRQ->text() : QString();
+ const QString strIOPort = m_pLineEditIOPort ? m_pLineEditIOPort->text() : QString();
+ const QPair<QString, QString> port = qMakePair(strIRQ, strIOPort);
+
+ if (strIRQ.isEmpty())
+ {
+ message.second << UIMachineSettingsSerial::tr("No IRQ is currently specified.");
+ fPass = false;
+ }
+ if (strIOPort.isEmpty())
+ {
+ message.second << UIMachineSettingsSerial::tr("No I/O port is currently specified.");
+ fPass = false;
+ }
+ if ( !strIRQ.isEmpty()
+ && !strIOPort.isEmpty())
+ {
+ QVector<QPair<QString, QString> > ports;
+ if (m_pParent)
+ {
+ ports = m_pParent->ports();
+ ports.removeAt(m_iSlot);
+ }
+ if (ports.contains(port))
+ {
+ message.second << UIMachineSettingsSerial::tr("Two or more ports have the same settings.");
+ fPass = false;
+ }
+ }
+
+ const KPortMode enmMode = m_pComboMode->currentData().value<KPortMode>();
+ if (enmMode != KPortMode_Disconnected)
+ {
+ const QString strPath(m_pEditorPath->text());
+
+ if (strPath.isEmpty())
+ {
+ message.second << UIMachineSettingsSerial::tr("No port path is currently specified.");
+ fPass = false;
+ }
+ else
+ {
+ QVector<QString> paths;
+ if (m_pParent)
+ {
+ paths = m_pParent->paths();
+ paths.removeAt(m_iSlot);
+ }
+ if (paths.contains(strPath))
+ {
+ message.second << UIMachineSettingsSerial::tr("There are currently duplicate port paths specified.");
+ fPass = false;
+ }
+ }
+ }
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+
+ /* Return result: */
+ return fPass;
+}
+
+QWidget *UIMachineSettingsSerial::setOrderAfter(QWidget *pWidget)
+{
+ setTabOrder(pWidget, m_pCheckBoxPort);
+ setTabOrder(m_pCheckBoxPort, m_pComboNumber);
+ setTabOrder(m_pComboNumber, m_pLineEditIRQ);
+ setTabOrder(m_pLineEditIRQ, m_pLineEditIOPort);
+ setTabOrder(m_pLineEditIOPort, m_pComboMode);
+ setTabOrder(m_pComboMode, m_pCheckBoxPipe);
+ setTabOrder(m_pCheckBoxPipe, m_pEditorPath);
+ return m_pEditorPath;
+}
+
+QString UIMachineSettingsSerial::tabTitle() const
+{
+ return QString(tr("Port %1", "serial ports")).arg(QString("&%1").arg(m_iSlot + 1));
+}
+
+bool UIMachineSettingsSerial::isPortEnabled() const
+{
+ return m_pCheckBoxPort->isChecked();
+}
+
+QString UIMachineSettingsSerial::irq() const
+{
+ return m_pLineEditIRQ->text();
+}
+
+QString UIMachineSettingsSerial::ioPort() const
+{
+ return m_pLineEditIOPort->text();
+}
+
+QString UIMachineSettingsSerial::path() const
+{
+ return m_pEditorPath->text();
+}
+
+void UIMachineSettingsSerial::polishTab()
+{
+ /* Sanity check: */
+ if (!m_pParent)
+ return;
+
+ /* Polish port page: */
+ ulong uIRQ, uIOBase;
+ const bool fStd = m_pComboNumber ? UITranslator::toCOMPortNumbers(m_pComboNumber->currentText(), uIRQ, uIOBase) : false;
+ const KPortMode enmMode = m_pComboMode ? m_pComboMode->currentData().value<KPortMode>() : KPortMode_Max;
+ if (m_pCheckBoxPort)
+ m_pCheckBoxPort->setEnabled(m_pParent->isMachineOffline());
+ if (m_pLabelNumber)
+ m_pLabelNumber->setEnabled(m_pParent->isMachineOffline());
+ if (m_pComboNumber)
+ m_pComboNumber->setEnabled(m_pParent->isMachineOffline());
+ if (m_pLabelIRQ)
+ m_pLabelIRQ->setEnabled(m_pParent->isMachineOffline());
+ if (m_pLineEditIRQ)
+ m_pLineEditIRQ->setEnabled(!fStd && m_pParent->isMachineOffline());
+ if (m_pLabelIOPort)
+ m_pLabelIOPort->setEnabled(m_pParent->isMachineOffline());
+ if (m_pLineEditIOPort)
+ m_pLineEditIOPort->setEnabled(!fStd && m_pParent->isMachineOffline());
+ if (m_pLabelMode)
+ m_pLabelMode->setEnabled(m_pParent->isMachineOffline());
+ if (m_pComboMode)
+ m_pComboMode->setEnabled(m_pParent->isMachineOffline());
+ if (m_pCheckBoxPipe)
+ m_pCheckBoxPipe->setEnabled( (enmMode == KPortMode_HostPipe || enmMode == KPortMode_TCP)
+ && m_pParent->isMachineOffline());
+ if (m_pLabelPath)
+ m_pLabelPath->setEnabled( enmMode != KPortMode_Disconnected
+ && m_pParent->isMachineOffline());
+ if (m_pEditorPath)
+ m_pEditorPath->setEnabled( enmMode != KPortMode_Disconnected
+ && m_pParent->isMachineOffline());
+}
+
+void UIMachineSettingsSerial::retranslateUi()
+{
+ if (m_pCheckBoxPort)
+ {
+ m_pCheckBoxPort->setText(tr("&Enable Serial Port"));
+ m_pCheckBoxPort->setToolTip(tr("When checked, enables the given serial port of the virtual machine."));
+ }
+ if (m_pLabelNumber)
+ m_pLabelNumber->setText(tr("Port &Number:"));
+ if (m_pComboNumber)
+ {
+ m_pComboNumber->setItemText(m_pComboNumber->count() - 1, UITranslator::toCOMPortName(0, 0));
+ m_pComboNumber->setToolTip(tr("Selects the serial port number. You can choose one of the standard serial ports or select "
+ "User-defined and specify port parameters manually."));
+ }
+ if (m_pLabelIRQ)
+ m_pLabelIRQ->setText(tr("&IRQ:"));
+ if (m_pLineEditIRQ)
+ m_pLineEditIRQ->setToolTip(tr("Holds the IRQ number of this serial port. This should be a whole number between "
+ "<tt>0</tt> and <tt>255</tt>. Values greater than <tt>15</tt> may only be used if the "
+ "I/O APIC setting is enabled for this virtual machine."));
+ if (m_pLabelIOPort)
+ m_pLabelIOPort->setText(tr("I/O Po&rt:"));
+ if (m_pLineEditIOPort)
+ m_pLineEditIOPort->setToolTip(tr("Holds the base I/O port address of this serial port. Valid values are integer numbers "
+ "in range from <tt>0</tt> to <tt>0xFFFF</tt>."));
+ if (m_pLabelMode)
+ m_pLabelMode->setText(tr("Port &Mode:"));
+ if (m_pComboMode)
+ m_pComboMode->setToolTip(tr("Selects the working mode of this serial port. If you select Disconnected, the guest "
+ "OS will detect the serial port but will not be able to operate it."));
+ if (m_pCheckBoxPipe)
+ {
+ m_pCheckBoxPipe->setText(tr("&Connect to existing pipe/socket"));
+ m_pCheckBoxPipe->setToolTip(tr("When checked, the virtual machine will assume that the pipe or socket specified in the "
+ "Path/Address field exists and try to use it. Otherwise, the pipe or socket will "
+ "be created by the virtual machine when it starts."));
+ }
+ if (m_pLabelPath)
+ m_pLabelPath->setText(tr("&Path/Address:"));
+ if (m_pEditorPath)
+ m_pEditorPath->setToolTip(tr("In Host Pipe mode: Holds the path to the serial port's pipe on the host. "
+ "Examples: \"\\\\.\\pipe\\myvbox\" or \"/tmp/myvbox\", for Windows and UNIX-like systems "
+ "respectively. In Host Device mode: Holds the host serial device name. "
+ "Examples: \"COM1\" or \"/dev/ttyS0\". In Raw File mode: Holds the file-path "
+ "on the host system, where the serial output will be dumped. In TCP mode: "
+ "Holds the TCP \"port\" when in server mode, or \"hostname:port\" when in client mode."));
+
+ /* Translate combo-boxes content: */
+ populateComboboxes();
+}
+
+void UIMachineSettingsSerial::sltHandlePortAvailabilityToggled(bool fOn)
+{
+ /* Update availability: */
+ m_pWidgetPortSettings->setEnabled(m_pCheckBoxPort->isChecked());
+ if (fOn)
+ {
+ sltHandlePortStandardOptionActivated(m_pComboNumber->currentText());
+ sltHandlePortModeChange(m_pComboMode->currentIndex());
+ }
+
+ /* Notify port/path changed: */
+ emit sigPortChanged();
+ emit sigPathChanged();
+}
+
+void UIMachineSettingsSerial::sltHandlePortStandardOptionActivated(const QString &strText)
+{
+ /* Update availability: */
+ ulong uIRQ, uIOBase;
+ bool fStd = UITranslator::toCOMPortNumbers(strText, uIRQ, uIOBase);
+ m_pLineEditIRQ->setEnabled(!fStd);
+ m_pLineEditIOPort->setEnabled(!fStd);
+ if (fStd)
+ {
+ m_pLineEditIRQ->setText(QString::number(uIRQ));
+ m_pLineEditIOPort->setText("0x" + QString::number(uIOBase, 16).toUpper());
+ }
+
+ /* Notify validity changed: */
+ emit sigValidityChanged();
+}
+
+void UIMachineSettingsSerial::sltHandlePortModeChange(int iIndex)
+{
+ /* Update availability: */
+ const KPortMode enmMode = m_pComboMode->itemData(iIndex).value<KPortMode>();
+ m_pCheckBoxPipe->setEnabled(enmMode == KPortMode_HostPipe || enmMode == KPortMode_TCP);
+ m_pEditorPath->setEnabled(enmMode != KPortMode_Disconnected);
+ m_pLabelPath->setEnabled(enmMode != KPortMode_Disconnected);
+
+ /* Notify validity changed: */
+ emit sigValidityChanged();
+}
+
+void UIMachineSettingsSerial::prepare()
+{
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsSerial::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QGridLayout *pLayoutMain = new QGridLayout(this);
+ if (pLayoutMain)
+ {
+ pLayoutMain->setRowStretch(2, 1);
+
+ /* Prepare port check-box: */
+ m_pCheckBoxPort = new QCheckBox(this);
+ if (m_pCheckBoxPort)
+ pLayoutMain->addWidget(m_pCheckBoxPort, 0, 0, 1, 2);
+
+ /* Prepare 20-px shifting spacer: */
+ QSpacerItem *pSpacerItem = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ if (pSpacerItem)
+ pLayoutMain->addItem(pSpacerItem, 1, 0);
+
+ /* Prepare adapter settings widget: */
+ m_pWidgetPortSettings = new QWidget(this);
+ if (m_pWidgetPortSettings)
+ {
+ /* Prepare adapter settings widget layout: */
+ QGridLayout *pLayoutPortSettings = new QGridLayout(m_pWidgetPortSettings);
+ if (pLayoutPortSettings)
+ {
+ pLayoutPortSettings->setContentsMargins(0, 0, 0, 0);
+ pLayoutPortSettings->setColumnStretch(6, 1);
+
+ /* Prepare number label: */
+ m_pLabelNumber = new QLabel(m_pWidgetPortSettings);
+ if (m_pLabelNumber)
+ {
+ m_pLabelNumber->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutPortSettings->addWidget(m_pLabelNumber, 0, 0);
+ }
+ /* Prepare number combo: */
+ m_pComboNumber = new QComboBox(m_pWidgetPortSettings);
+ if (m_pComboNumber)
+ {
+ if (m_pLabelNumber)
+ m_pLabelNumber->setBuddy(m_pComboNumber);
+ m_pComboNumber->insertItem(0, UITranslator::toCOMPortName(0, 0));
+ m_pComboNumber->insertItems(0, UITranslator::COMPortNames());
+ pLayoutPortSettings->addWidget(m_pComboNumber, 0, 1);
+ }
+ /* Prepare IRQ label: */
+ m_pLabelIRQ = new QLabel(m_pWidgetPortSettings);
+ if (m_pLabelIRQ)
+ pLayoutPortSettings->addWidget(m_pLabelIRQ, 0, 2);
+ /* Prepare IRQ label: */
+ m_pLineEditIRQ = new QLineEdit(m_pWidgetPortSettings);
+ if (m_pLineEditIRQ)
+ {
+ if (m_pLabelIRQ)
+ m_pLabelIRQ->setBuddy(m_pLineEditIRQ);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ m_pLineEditIRQ->setFixedWidth(m_pLineEditIRQ->fontMetrics().horizontalAdvance("8888"));
+#else
+ m_pLineEditIRQ->setFixedWidth(m_pLineEditIRQ->fontMetrics().width("8888"));
+#endif
+ m_pLineEditIRQ->setValidator(new QIULongValidator(0, 255, this));
+ pLayoutPortSettings->addWidget(m_pLineEditIRQ, 0, 3);
+ }
+ /* Prepare IO port label: */
+ m_pLabelIOPort = new QLabel(m_pWidgetPortSettings);
+ if (m_pLabelIOPort)
+ pLayoutPortSettings->addWidget(m_pLabelIOPort, 0, 4);
+ /* Prepare IO port label: */
+ m_pLineEditIOPort = new QLineEdit(m_pWidgetPortSettings);
+ if (m_pLineEditIOPort)
+ {
+ if (m_pLabelIOPort)
+ m_pLabelIOPort->setBuddy(m_pLineEditIOPort);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ m_pLineEditIOPort->setFixedWidth(m_pLineEditIOPort->fontMetrics().horizontalAdvance("8888888"));
+#else
+ m_pLineEditIOPort->setFixedWidth(m_pLineEditIOPort->fontMetrics().width("8888888"));
+#endif
+ m_pLineEditIOPort->setValidator(new QIULongValidator(0, 0xFFFF, this));
+ pLayoutPortSettings->addWidget(m_pLineEditIOPort, 0, 5);
+ }
+
+ /* Prepare mode label: */
+ m_pLabelMode = new QLabel(m_pWidgetPortSettings);
+ if (m_pLabelMode)
+ {
+ m_pLabelMode->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutPortSettings->addWidget(m_pLabelMode, 1, 0);
+ }
+ /* Prepare mode combo: */
+ m_pComboMode = new QComboBox(m_pWidgetPortSettings);
+ if (m_pComboMode)
+ {
+ if (m_pLabelMode)
+ m_pLabelMode->setBuddy(m_pComboMode);
+ pLayoutPortSettings->addWidget(m_pComboMode, 1, 1);
+ }
+
+ /* Prepare pipe check-box: */
+ m_pCheckBoxPipe = new QCheckBox(m_pWidgetPortSettings);
+ if (m_pCheckBoxPipe)
+ pLayoutPortSettings->addWidget(m_pCheckBoxPipe, 2, 1, 1, 5);
+
+ /* Prepare path label: */
+ m_pLabelPath = new QLabel(m_pWidgetPortSettings);
+ if (m_pLabelPath)
+ {
+ m_pLabelPath->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutPortSettings->addWidget(m_pLabelPath, 3, 0);
+ }
+ /* Prepare path editor: */
+ m_pEditorPath = new QLineEdit(m_pWidgetPortSettings);
+ if (m_pEditorPath)
+ {
+ if (m_pLabelPath)
+ m_pLabelPath->setBuddy(m_pEditorPath);
+ m_pEditorPath->setValidator(new QRegularExpressionValidator(QRegularExpression(".+"), this));
+ pLayoutPortSettings->addWidget(m_pEditorPath, 3, 1, 1, 6);
+ }
+ }
+
+ pLayoutMain->addWidget(m_pWidgetPortSettings, 1, 1);
+ }
+ }
+}
+
+void UIMachineSettingsSerial::prepareConnections()
+{
+ if (m_pCheckBoxPort)
+ connect(m_pCheckBoxPort, &QCheckBox::toggled, this, &UIMachineSettingsSerial::sltHandlePortAvailabilityToggled);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ if (m_pComboNumber)
+ connect(m_pComboNumber, static_cast<void(QComboBox::*)(const QString&)>(&QComboBox::textActivated),
+ this, &UIMachineSettingsSerial::sltHandlePortStandardOptionActivated);
+#else
+ if (m_pComboNumber)
+ connect(m_pComboNumber, static_cast<void(QComboBox::*)(const QString&)>(&QComboBox::activated),
+ this, &UIMachineSettingsSerial::sltHandlePortStandardOptionActivated);
+#endif
+ if (m_pLineEditIRQ)
+ connect(m_pLineEditIRQ, &QLineEdit::textChanged, this, &UIMachineSettingsSerial::sigPortChanged);
+ if (m_pLineEditIOPort)
+ connect(m_pLineEditIOPort, &QLineEdit::textChanged, this, &UIMachineSettingsSerial::sigPortChanged);
+ if (m_pComboMode)
+ connect(m_pComboMode, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
+ this, &UIMachineSettingsSerial::sltHandlePortModeChange);
+ if (m_pEditorPath)
+ connect(m_pEditorPath, &QLineEdit::textChanged, this, &UIMachineSettingsSerial::sigPathChanged);
+}
+
+void UIMachineSettingsSerial::populateComboboxes()
+{
+ /* Port mode: */
+ {
+ /* Clear the port mode combo-box: */
+ m_pComboMode->clear();
+
+ /* Load currently supported port moded: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ QVector<KPortMode> supportedModes = comProperties.GetSupportedPortModes();
+ /* Take currently requested mode into account if it's sane: */
+ if (!supportedModes.contains(m_enmPortMode) && m_enmPortMode != KPortMode_Max)
+ supportedModes.prepend(m_enmPortMode);
+
+ /* Populate port modes: */
+ int iPortModeIndex = 0;
+ foreach (const KPortMode &enmMode, supportedModes)
+ {
+ m_pComboMode->insertItem(iPortModeIndex, gpConverter->toString(enmMode));
+ m_pComboMode->setItemData(iPortModeIndex, QVariant::fromValue(enmMode));
+ m_pComboMode->setItemData(iPortModeIndex, m_pComboMode->itemText(iPortModeIndex), Qt::ToolTipRole);
+ ++iPortModeIndex;
+ }
+
+ /* Choose requested port mode: */
+ const int iIndex = m_pComboMode->findData(m_enmPortMode);
+ m_pComboMode->setCurrentIndex(iIndex != -1 ? iIndex : 0);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMachineSettingsSerialPage implementation. *
+*********************************************************************************************************************************/
+
+UIMachineSettingsSerialPage::UIMachineSettingsSerialPage()
+ : m_pCache(0)
+ , m_pTabWidget(0)
+{
+ prepare();
+}
+
+UIMachineSettingsSerialPage::~UIMachineSettingsSerialPage()
+{
+ cleanup();
+}
+
+bool UIMachineSettingsSerialPage::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsSerialPage::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Cache lists: */
+ refreshPorts();
+ refreshPaths();
+
+ /* Prepare old data: */
+ UIDataSettingsMachineSerial oldSerialData;
+
+ /* For each serial port: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ /* Prepare old data: */
+ UIDataSettingsMachineSerialPort oldPortData;
+
+ /* Check whether port is valid: */
+ const CSerialPort &comPort = m_machine.GetSerialPort(iSlot);
+ if (!comPort.isNull())
+ {
+ /* Gather old data: */
+ oldPortData.m_iSlot = iSlot;
+ oldPortData.m_fPortEnabled = comPort.GetEnabled();
+ oldPortData.m_uIRQ = comPort.GetIRQ();
+ oldPortData.m_uIOBase = comPort.GetIOBase();
+ oldPortData.m_hostMode = comPort.GetHostMode();
+ oldPortData.m_fServer = comPort.GetServer();
+ oldPortData.m_strPath = comPort.GetPath();
+ }
+
+ /* Cache old data: */
+ m_pCache->child(iSlot).cacheInitialData(oldPortData);
+ }
+
+ /* Cache old data: */
+ m_pCache->cacheInitialData(oldSerialData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsSerialPage::getFromCache()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return;
+
+ /* Setup tab order: */
+ AssertPtrReturnVoid(firstWidget());
+ setTabOrder(firstWidget(), m_pTabWidget->focusProxy());
+ QWidget *pLastFocusWidget = m_pTabWidget->focusProxy();
+
+ /* For each port: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ /* Get port page: */
+ UIMachineSettingsSerial *pTab = qobject_cast<UIMachineSettingsSerial*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+
+ /* Load old data from cache: */
+ pTab->getPortDataFromCache(m_pCache->child(iSlot));
+
+ /* Setup tab order: */
+ pLastFocusWidget = pTab->setOrderAfter(pLastFocusWidget);
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Polish page finally: */
+ polishPage();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMachineSettingsSerialPage::putToCache()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsMachineSerial newSerialData;
+
+ /* For each port: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ /* Getting port page: */
+ UIMachineSettingsSerial *pTab = qobject_cast<UIMachineSettingsSerial*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+
+ /* Gather new data: */
+ pTab->putPortDataToCache(m_pCache->child(iSlot));
+ }
+
+ /* Cache new data: */
+ m_pCache->cacheCurrentData(newSerialData);
+}
+
+void UIMachineSettingsSerialPage::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+bool UIMachineSettingsSerialPage::validate(QList<UIValidationMessage> &messages)
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return false;
+
+ /* Pass by default: */
+ bool fValid = true;
+
+ /* Delegate validation to adapter tabs: */
+ for (int iIndex = 0; iIndex < m_pTabWidget->count(); ++iIndex)
+ {
+ UIMachineSettingsSerial *pTab = qobject_cast<UIMachineSettingsSerial*>(m_pTabWidget->widget(iIndex));
+ AssertPtrReturn(pTab, false);
+ if (!pTab->validate(messages))
+ fValid = false;
+ }
+
+ /* Return result: */
+ return fValid;
+}
+
+void UIMachineSettingsSerialPage::retranslateUi()
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return;
+
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ UIMachineSettingsSerial *pTab = qobject_cast<UIMachineSettingsSerial*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+ m_pTabWidget->setTabText(iSlot, pTab->tabTitle());
+ }
+}
+
+void UIMachineSettingsSerialPage::polishPage()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return;
+
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ m_pTabWidget->setTabEnabled(iSlot,
+ isMachineOffline() ||
+ (isMachineInValidMode() &&
+ m_pCache->childCount() > iSlot &&
+ m_pCache->child(iSlot).base().m_fPortEnabled));
+ UIMachineSettingsSerial *pTab = qobject_cast<UIMachineSettingsSerial*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+ pTab->polishTab();
+ }
+}
+
+void UIMachineSettingsSerialPage::sltHandlePortChange()
+{
+ refreshPorts();
+ revalidate();
+}
+
+void UIMachineSettingsSerialPage::sltHandlePathChange()
+{
+ refreshPaths();
+ revalidate();
+}
+
+void UIMachineSettingsSerialPage::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheMachineSerial;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Create main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Creating tab-widget: */
+ m_pTabWidget = new QITabWidget;
+ if (m_pTabWidget)
+ {
+ /* How many ports to display: */
+ const ulong uCount = uiCommon().virtualBox().GetSystemProperties().GetSerialPortCount();
+
+ /* Create corresponding port tabs: */
+ for (ulong uSlot = 0; uSlot < uCount; ++uSlot)
+ {
+ /* Create port tab: */
+ UIMachineSettingsSerial *pTab = new UIMachineSettingsSerial(this);
+ if (pTab)
+ {
+ /* Tab connections: */
+ connect(pTab, &UIMachineSettingsSerial::sigPortChanged,
+ this, &UIMachineSettingsSerialPage::sltHandlePortChange);
+ connect(pTab, &UIMachineSettingsSerial::sigPathChanged,
+ this, &UIMachineSettingsSerialPage::sltHandlePathChange);
+ connect(pTab, &UIMachineSettingsSerial::sigValidityChanged,
+ this, &UIMachineSettingsSerialPage::revalidate);
+
+ /* Add tab into tab-widget: */
+ m_pTabWidget->addTab(pTab, pTab->tabTitle());
+ }
+ }
+
+ /* Add tab-widget into layout: */
+ pLayoutMain->addWidget(m_pTabWidget);
+ }
+ }
+}
+
+void UIMachineSettingsSerialPage::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+void UIMachineSettingsSerialPage::refreshPorts()
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return;
+
+ /* Reload port list: */
+ m_ports.clear();
+ m_ports.resize(m_pTabWidget->count());
+ /* Append port list with data from all the tabs: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ UIMachineSettingsSerial *pTab = qobject_cast<UIMachineSettingsSerial*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+ m_ports[iSlot] = pTab->isPortEnabled() ? qMakePair(pTab->irq(), pTab->ioPort()) : qMakePair(QString(), QString());
+ }
+}
+
+void UIMachineSettingsSerialPage::refreshPaths()
+{
+ /* Sanity check: */
+ if (!m_pTabWidget)
+ return;
+
+ /* Reload path list: */
+ m_paths.clear();
+ m_paths.resize(m_pTabWidget->count());
+ /* Append path list with data from all the tabs: */
+ for (int iSlot = 0; iSlot < m_pTabWidget->count(); ++iSlot)
+ {
+ UIMachineSettingsSerial *pTab = qobject_cast<UIMachineSettingsSerial*>(m_pTabWidget->widget(iSlot));
+ AssertPtrReturnVoid(pTab);
+ m_paths[iSlot] = pTab->isPortEnabled() ? pTab->path() : QString();
+ }
+}
+
+bool UIMachineSettingsSerialPage::saveData()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pTabWidget)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save serial settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* For each port: */
+ for (int iSlot = 0; fSuccess && iSlot < m_pTabWidget->count(); ++iSlot)
+ fSuccess = savePortData(iSlot);
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsSerialPage::savePortData(int iSlot)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save adapter settings from cache: */
+ if (fSuccess && m_pCache->child(iSlot).wasChanged())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineSerialPort &oldPortData = m_pCache->child(iSlot).base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineSerialPort &newPortData = m_pCache->child(iSlot).data();
+
+ /* Get serial port for further activities: */
+ CSerialPort comPort = m_machine.GetSerialPort(iSlot);
+ fSuccess = m_machine.isOk() && comPort.isNotNull();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ else
+ {
+ // This *must* be first.
+ // If the requested host mode is changed to disconnected we should do it first.
+ // That allows to automatically fulfill the requirements for some of the settings below.
+ /* Save port host mode: */
+ if ( fSuccess && isMachineOffline()
+ && newPortData.m_hostMode != oldPortData.m_hostMode
+ && newPortData.m_hostMode == KPortMode_Disconnected)
+ {
+ comPort.SetHostMode(newPortData.m_hostMode);
+ fSuccess = comPort.isOk();
+ }
+ /* Save whether the port is enabled: */
+ if (fSuccess && isMachineOffline() && newPortData.m_fPortEnabled != oldPortData.m_fPortEnabled)
+ {
+ comPort.SetEnabled(newPortData.m_fPortEnabled);
+ fSuccess = comPort.isOk();
+ }
+ /* Save port IRQ: */
+ if (fSuccess && isMachineOffline() && newPortData.m_uIRQ != oldPortData.m_uIRQ)
+ {
+ comPort.SetIRQ(newPortData.m_uIRQ);
+ fSuccess = comPort.isOk();
+ }
+ /* Save port IO base: */
+ if (fSuccess && isMachineOffline() && newPortData.m_uIOBase != oldPortData.m_uIOBase)
+ {
+ comPort.SetIOBase(newPortData.m_uIOBase);
+ fSuccess = comPort.isOk();
+ }
+ /* Save whether the port is server: */
+ if (fSuccess && isMachineOffline() && newPortData.m_fServer != oldPortData.m_fServer)
+ {
+ comPort.SetServer(newPortData.m_fServer);
+ fSuccess = comPort.isOk();
+ }
+ /* Save port path: */
+ if (fSuccess && isMachineOffline() && newPortData.m_strPath != oldPortData.m_strPath)
+ {
+ comPort.SetPath(newPortData.m_strPath);
+ fSuccess = comPort.isOk();
+ }
+ // This *must* be last.
+ // The host mode will be changed to disconnected if some of the necessary
+ // settings above will not meet the requirements for the selected mode.
+ /* Save port host mode: */
+ if ( fSuccess && isMachineOffline()
+ && newPortData.m_hostMode != oldPortData.m_hostMode
+ && newPortData.m_hostMode != KPortMode_Disconnected)
+ {
+ comPort.SetHostMode(newPortData.m_hostMode);
+ fSuccess = comPort.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comPort));
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+# include "UIMachineSettingsSerial.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSerial.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSerial.h
new file mode 100644
index 00000000..f8db26fd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSerial.h
@@ -0,0 +1,126 @@
+/* $Id: UIMachineSettingsSerial.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsSerial class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsSerial_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsSerial_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class QITabWidget;
+class UIMachineSettingsSerialPage;
+struct UIDataSettingsMachineSerial;
+struct UIDataSettingsMachineSerialPort;
+typedef UISettingsCache<UIDataSettingsMachineSerialPort> UISettingsCacheMachineSerialPort;
+typedef UISettingsCachePool<UIDataSettingsMachineSerial, UISettingsCacheMachineSerialPort> UISettingsCacheMachineSerial;
+
+/** Machine settings: Serial page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsSerialPage : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Serial settings page. */
+ UIMachineSettingsSerialPage();
+ /** Destructs Serial settings page. */
+ virtual ~UIMachineSettingsSerialPage() RT_OVERRIDE;
+
+ /** Returns ports. */
+ QVector<QPair<QString, QString> > ports() const { return m_ports; }
+ /** Returns paths. */
+ QVector<QString> paths() const { return m_paths; }
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ virtual bool validate(QList<UIValidationMessage> &messages) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles port change. */
+ void sltHandlePortChange();
+ /** Handles path change. */
+ void sltHandlePathChange();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Repopulates ports. */
+ void refreshPorts();
+ /** Repopulates paths. */
+ void refreshPaths();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+ /** Saves existing port data from cache. */
+ bool savePortData(int iSlot);
+
+ /** Holds the ports. */
+ QVector<QPair<QString, QString> > m_ports;
+ /** Holds the paths. */
+ QVector<QString> m_paths;
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheMachineSerial *m_pCache;
+
+ /** Holds the tab-widget instance. */
+ QITabWidget *m_pTabWidget;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsSerial_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsStorage.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsStorage.cpp
new file mode 100644
index 00000000..21700143
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsStorage.cpp
@@ -0,0 +1,1047 @@
+/* $Id: UIMachineSettingsStorage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsStorage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIErrorString.h"
+#include "UIMachineSettingsStorage.h"
+#include "UIMedium.h"
+#include "UIStorageSettingsEditor.h"
+
+/* COM includes: */
+#include "CMediumAttachment.h"
+#include "CStorageController.h"
+
+
+/** Machine settings: Storage Attachment data structure. */
+struct UIDataSettingsMachineStorageAttachment
+{
+ /** Constructs data. */
+ UIDataSettingsMachineStorageAttachment() {}
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineStorageAttachment &another) const
+ {
+ return true
+ && (m_guiValue == another.m_guiValue)
+ ;
+ }
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineStorageAttachment &another) const { return equal(another); }
+ /** Returns whether @a another passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineStorageAttachment &another) const { return !equal(another); }
+
+ /** Holds the storage attachment data. */
+ UIDataStorageAttachment m_guiValue;
+};
+
+
+/** Machine settings: Storage Controller data structure. */
+struct UIDataSettingsMachineStorageController
+{
+ /** Constructs data. */
+ UIDataSettingsMachineStorageController() {}
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineStorageController &another) const
+ {
+ return true
+ && (m_guiValue == another.m_guiValue)
+ ;
+ }
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineStorageController &another) const { return equal(another); }
+ /** Returns whether @a another passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineStorageController &another) const { return !equal(another); }
+
+ /** Holds the storage controller data. */
+ UIDataStorageController m_guiValue;
+};
+
+
+/** Machine settings: Storage page data structure. */
+struct UIDataSettingsMachineStorage
+{
+ /** Constructs data. */
+ UIDataSettingsMachineStorage() {}
+
+ /** Returns whether @a another passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineStorage & /* another */) const { return true; }
+ /** Returns whether @a another passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineStorage & /* another */) const { return false; }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMachineSettingsStorage implementation. *
+*********************************************************************************************************************************/
+
+UIMachineSettingsStorage::UIMachineSettingsStorage(UIActionPool *pActionPool)
+ : m_pActionPool(pActionPool)
+ , m_pCache(0)
+ , m_pEditorStorageSettings(0)
+{
+ prepare();
+}
+
+UIMachineSettingsStorage::~UIMachineSettingsStorage()
+{
+ cleanup();
+}
+
+void UIMachineSettingsStorage::setChipsetType(KChipsetType enmType)
+{
+ if (m_pEditorStorageSettings)
+ m_pEditorStorageSettings->setChipsetType(enmType);
+}
+
+bool UIMachineSettingsStorage::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsStorage::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Prepare old data: */
+ UIDataSettingsMachineStorage oldStorageData;
+
+ /* Gather old data: */
+ m_uMachineId = m_machine.GetId();
+ m_strMachineName = m_machine.GetName();
+ m_strMachineSettingsFilePath = m_machine.GetSettingsFilePath();
+ m_strMachineGuestOSTypeId = m_machine.GetOSTypeId();
+
+ /* For each controller: */
+ const CStorageControllerVector &controllers = m_machine.GetStorageControllers();
+ for (int iControllerIndex = 0; iControllerIndex < controllers.size(); ++iControllerIndex)
+ {
+ /* Prepare old data & cache key: */
+ UIDataSettingsMachineStorageController oldControllerData;
+ QString strControllerKey = QString::number(iControllerIndex);
+
+ /* Check whether controller is valid: */
+ const CStorageController &comController = controllers.at(iControllerIndex);
+ if (!comController.isNull())
+ {
+ /* Gather old data: */
+ oldControllerData.m_guiValue.m_strName = comController.GetName();
+ oldControllerData.m_guiValue.m_enmBus = comController.GetBus();
+ oldControllerData.m_guiValue.m_enmType = comController.GetControllerType();
+ oldControllerData.m_guiValue.m_uPortCount = comController.GetPortCount();
+ oldControllerData.m_guiValue.m_fUseHostIOCache = comController.GetUseHostIOCache();
+ oldControllerData.m_guiValue.m_strKey = oldControllerData.m_guiValue.m_strName;
+ /* Override controller cache key: */
+ strControllerKey = oldControllerData.m_guiValue.m_strKey;
+
+ /* Sort attachments before caching/fetching: */
+ const CMediumAttachmentVector &attachmentVector =
+ m_machine.GetMediumAttachmentsOfController(oldControllerData.m_guiValue.m_strName);
+ QMap<StorageSlot, CMediumAttachment> attachmentMap;
+ foreach (const CMediumAttachment &comAttachment, attachmentVector)
+ {
+ const StorageSlot storageSlot(oldControllerData.m_guiValue.m_enmBus,
+ comAttachment.GetPort(), comAttachment.GetDevice());
+ attachmentMap.insert(storageSlot, comAttachment);
+ }
+ const QList<CMediumAttachment> &attachments = attachmentMap.values();
+
+ /* For each attachment: */
+ for (int iAttachmentIndex = 0; iAttachmentIndex < attachments.size(); ++iAttachmentIndex)
+ {
+ /* Prepare old data & cache key: */
+ UIDataSettingsMachineStorageAttachment oldAttachmentData;
+ QString strAttachmentKey = QString::number(iAttachmentIndex);
+
+ /* Check whether attachment is valid: */
+ const CMediumAttachment &comAttachment = attachments.at(iAttachmentIndex);
+ if (!comAttachment.isNull())
+ {
+ /* Gather old data: */
+ oldAttachmentData.m_guiValue.m_enmDeviceType = comAttachment.GetType();
+ oldAttachmentData.m_guiValue.m_iPort = comAttachment.GetPort();
+ oldAttachmentData.m_guiValue.m_iDevice = comAttachment.GetDevice();
+ oldAttachmentData.m_guiValue.m_fPassthrough = comAttachment.GetPassthrough();
+ oldAttachmentData.m_guiValue.m_fTempEject = comAttachment.GetTemporaryEject();
+ oldAttachmentData.m_guiValue.m_fNonRotational = comAttachment.GetNonRotational();
+ oldAttachmentData.m_guiValue.m_fHotPluggable = comAttachment.GetHotPluggable();
+ const CMedium comMedium = comAttachment.GetMedium();
+ oldAttachmentData.m_guiValue.m_uMediumId = comMedium.isNull() ? UIMedium::nullID() : comMedium.GetId();
+ oldAttachmentData.m_guiValue.m_strKey = QString("%1:%2").arg(oldAttachmentData.m_guiValue.m_iPort)
+ .arg(oldAttachmentData.m_guiValue.m_iDevice);
+ /* Override attachment cache key: */
+ strAttachmentKey = oldAttachmentData.m_guiValue.m_strKey;
+ }
+
+ /* Cache old data: */
+ m_pCache->child(strControllerKey).child(strAttachmentKey).cacheInitialData(oldAttachmentData);
+ }
+ }
+
+ /* Cache old data: */
+ m_pCache->child(strControllerKey).cacheInitialData(oldControllerData);
+ }
+
+ /* Cache old data: */
+ m_pCache->cacheInitialData(oldStorageData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsStorage::getFromCache()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pEditorStorageSettings)
+ return;
+
+ /* Load old data from cache: */
+ m_pEditorStorageSettings->setMachineId(m_uMachineId);
+ m_pEditorStorageSettings->setMachineName(m_strMachineName);
+ m_pEditorStorageSettings->setMachineSettingsFilePath(m_strMachineSettingsFilePath);
+ m_pEditorStorageSettings->setMachineGuestOSTypeId(m_strMachineGuestOSTypeId);
+
+ /* Load old data from cache: */
+ QList<UIDataStorageController> controllers;
+ QList<QList<UIDataStorageAttachment> > attachments;
+
+ /* For each controller: */
+ for (int iControllerIndex = 0; iControllerIndex < m_pCache->childCount(); ++iControllerIndex)
+ {
+ /* Get controller cache: */
+ const UISettingsCacheMachineStorageController &controllerCache = m_pCache->child(iControllerIndex);
+ /* Get old data from cache: */
+ const UIDataSettingsMachineStorageController &oldControllerData = controllerCache.base();
+
+ /* For each attachment: */
+ QList<UIDataStorageAttachment> controllerAttachments;
+ for (int iAttachmentIndex = 0; iAttachmentIndex < controllerCache.childCount(); ++iAttachmentIndex)
+ {
+ /* Get attachment cache: */
+ const UISettingsCacheMachineStorageAttachment &attachmentCache = controllerCache.child(iAttachmentIndex);
+ /* Get old data from cache: */
+ const UIDataSettingsMachineStorageAttachment &oldAttachmentData = attachmentCache.base();
+
+ /* Append controller's attachment: */
+ controllerAttachments << oldAttachmentData.m_guiValue;
+ }
+
+ /* Append controller & controller's attachments: */
+ controllers << oldControllerData.m_guiValue;
+ attachments << controllerAttachments;
+ }
+
+ /* Set to editor: */
+ m_pEditorStorageSettings->setValue(controllers, attachments);
+
+ /* Polish page finally: */
+ polishPage();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMachineSettingsStorage::putToCache()
+{
+ /* Sanity check: */
+ if ( !m_pCache
+ || !m_pEditorStorageSettings)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsMachineStorage newStorageData;
+
+ /* Save new data to cache: */
+ QList<UIDataStorageController> controllers;
+ QList<QList<UIDataStorageAttachment> > attachments;
+
+ /* Get from editor: */
+ m_pEditorStorageSettings->getValue(controllers, attachments);
+
+ /* For each controller: */
+ for (int iControllerIndex = 0; iControllerIndex < controllers.size(); ++iControllerIndex)
+ {
+ /* Acquire controller: */
+ const UIDataStorageController &controller = controllers.at(iControllerIndex);
+
+ /* Gather new data & cache key from model: */
+ UIDataSettingsMachineStorageController newControllerData;
+ newControllerData.m_guiValue = controller;
+ const QString strControllerKey = newControllerData.m_guiValue.m_strKey;
+
+ /* For each attachment: */
+ const QList<UIDataStorageAttachment> &controllerAttachments = attachments.at(iControllerIndex);
+ for (int iAttachmentIndex = 0; iAttachmentIndex < controllerAttachments.size(); ++iAttachmentIndex)
+ {
+ /* Acquire attachment: */
+ const UIDataStorageAttachment &attachment = controllerAttachments.at(iAttachmentIndex);
+
+ /* Gather new data & cache key from model: */
+ UIDataSettingsMachineStorageAttachment newAttachmentData;
+ newAttachmentData.m_guiValue = attachment;
+ const QString strAttachmentKey = newAttachmentData.m_guiValue.m_strKey;
+
+ /* Cache new data: */
+ m_pCache->child(strControllerKey).child(strAttachmentKey).cacheCurrentData(newAttachmentData);
+ }
+
+ /* Cache new data: */
+ m_pCache->child(strControllerKey).cacheCurrentData(newControllerData);
+ }
+
+ /* Cache new data: */
+ m_pCache->cacheCurrentData(newStorageData);
+}
+
+void UIMachineSettingsStorage::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+bool UIMachineSettingsStorage::validate(QList<UIValidationMessage> &messages)
+{
+ /* Pass by default: */
+ bool fPass = true;
+
+ /* Prepare message: */
+ UIValidationMessage message;
+
+ /* Save new data to cache: */
+ QList<UIDataStorageController> controllers;
+ QList<QList<UIDataStorageAttachment> > attachments;
+
+ /* Get from editor: */
+ m_pEditorStorageSettings->getValue(controllers, attachments);
+
+ /* Check controllers for name emptiness & coincidence.
+ * Check attachments for the hd presence / uniqueness. */
+ QMap<QString, QString> config;
+ QMap<int, QString> names;
+ /* For each controller: */
+ for (int iControllerIndex = 0; iControllerIndex < controllers.size(); ++iControllerIndex)
+ {
+ const UIDataStorageController &controller = controllers.at(iControllerIndex);
+ const QString strControllerName = controller.m_strName;
+
+ /* Check for name emptiness: */
+ if (strControllerName.isEmpty())
+ {
+ message.second << tr("No name is currently specified for the controller at position <b>%1</b>.")
+ .arg(iControllerIndex + 1);
+ fPass = false;
+ }
+ /* Check for name coincidence: */
+ if (names.values().contains(strControllerName))
+ {
+ message.second << tr("The controller at position <b>%1</b> has the same name as the controller at position <b>%2</b>.")
+ .arg(iControllerIndex + 1).arg(names.key(strControllerName) + 1);
+ fPass = false;
+ }
+ else
+ names.insert(iControllerIndex, strControllerName);
+
+ /* For each attachment: */
+ const QList<UIDataStorageAttachment> &controllerAttachments = attachments.at(iControllerIndex);
+ for (int iAttachmentIndex = 0; iAttachmentIndex < controllerAttachments.size(); ++iAttachmentIndex)
+ {
+ const UIDataStorageAttachment &attachment = controllerAttachments.at(iAttachmentIndex);
+ const StorageSlot guiAttachmentSlot = StorageSlot(controller.m_enmBus, attachment.m_iPort, attachment.m_iDevice);
+ const KDeviceType enmDeviceType = attachment.m_enmDeviceType;
+ const QString strKey = attachment.m_uMediumId.toString();
+ const QString strValue = QString("%1 (%2)").arg(strControllerName, gpConverter->toString(guiAttachmentSlot));
+ /* Check for emptiness: */
+ if (uiCommon().medium(QUuid(strKey)).isNull() && enmDeviceType == KDeviceType_HardDisk)
+ {
+ message.second << tr("No hard disk is selected for <i>%1</i>.")
+ .arg(strValue);
+ fPass = false;
+ }
+ /* Check for coincidence: */
+ if (!uiCommon().medium(QUuid(strKey)).isNull() && config.contains(strKey) && enmDeviceType != KDeviceType_DVD)
+ {
+ message.second << tr("<i>%1</i> is using a disk that is already attached to <i>%2</i>.")
+ .arg(strValue).arg(config[strKey]);
+ fPass = false;
+ }
+ else
+ config.insert(strKey, strValue);
+ }
+ }
+
+ /* Check for excessive controllers on Storage page controllers list: */
+ QStringList excessiveList;
+ const QMap<KStorageBus, int> currentType = m_pEditorStorageSettings->currentControllerTypes();
+ const QMap<KStorageBus, int> maximumType = m_pEditorStorageSettings->maximumControllerTypes();
+ for (int iStorageBusType = KStorageBus_IDE; iStorageBusType < KStorageBus_Max; ++iStorageBusType)
+ {
+ if (currentType[(KStorageBus)iStorageBusType] > maximumType[(KStorageBus)iStorageBusType])
+ {
+ QString strExcessiveRecord = QString("%1 (%2)");
+ strExcessiveRecord = strExcessiveRecord.arg(QString("<b>%1</b>").arg(gpConverter->toString((KStorageBus)iStorageBusType)));
+ strExcessiveRecord = strExcessiveRecord.arg(maximumType[(KStorageBus)iStorageBusType] == 1 ?
+ tr("at most one supported", "controller") :
+ tr("up to %1 supported", "controllers").arg(maximumType[(KStorageBus)iStorageBusType]));
+ excessiveList << strExcessiveRecord;
+ }
+ }
+ if (!excessiveList.isEmpty())
+ {
+ message.second << tr("The machine currently has more storage controllers assigned than a %1 chipset supports. "
+ "Please change the chipset type on the System settings page or reduce the number "
+ "of the following storage controllers on the Storage settings page: %2")
+ .arg(gpConverter->toString(m_pEditorStorageSettings->chipsetType()))
+ .arg(excessiveList.join(", "));
+ fPass = false;
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+
+ /* Return result: */
+ return fPass;
+}
+
+void UIMachineSettingsStorage::setConfigurationAccessLevel(ConfigurationAccessLevel enmLevel)
+{
+ /* Update model 'configuration access level': */
+ m_pEditorStorageSettings->setConfigurationAccessLevel(enmLevel);
+ /* Update 'configuration access level' of base class: */
+ UISettingsPageMachine::setConfigurationAccessLevel(enmLevel);
+}
+
+void UIMachineSettingsStorage::retranslateUi()
+{
+}
+
+void UIMachineSettingsStorage::polishPage()
+{
+ m_pEditorStorageSettings->setConfigurationAccessLevel(configurationAccessLevel());
+}
+
+void UIMachineSettingsStorage::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheMachineStorage;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Start full medium-enumeration (if necessary): */
+ if (!uiCommon().isFullMediumEnumerationRequested())
+ uiCommon().enumerateMedia();
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsStorage::prepareWidgets()
+{
+ /* Create main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Create storage settings editor: */
+ m_pEditorStorageSettings = new UIStorageSettingsEditor(this);
+ if (m_pEditorStorageSettings)
+ {
+ m_pEditorStorageSettings->setActionPool(m_pActionPool);
+ pLayout->addWidget(m_pEditorStorageSettings);
+ }
+ }
+}
+
+void UIMachineSettingsStorage::prepareConnections()
+{
+ connect(m_pEditorStorageSettings, &UIStorageSettingsEditor::sigValueChanged,
+ this, &UIMachineSettingsStorage::revalidate);
+}
+
+void UIMachineSettingsStorage::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIMachineSettingsStorage::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save storage settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* For each controller ('removing' step): */
+ // We need to separately remove controllers first because
+ // there could be limited amount of controllers available.
+ for (int iControllerIndex = 0; fSuccess && iControllerIndex < m_pCache->childCount(); ++iControllerIndex)
+ {
+ /* Get controller cache: */
+ const UISettingsCacheMachineStorageController &controllerCache = m_pCache->child(iControllerIndex);
+
+ /* Remove controller marked for 'remove' or 'update' (if it can't be updated): */
+ if (controllerCache.wasRemoved() || (controllerCache.wasUpdated() && !isControllerCouldBeUpdated(controllerCache)))
+ fSuccess = removeStorageController(controllerCache);
+ }
+
+ /* For each controller ('updating' step): */
+ // We need to separately update controllers first because
+ // attachments should be removed/updated/created same separate way.
+ for (int iControllerIndex = 0; fSuccess && iControllerIndex < m_pCache->childCount(); ++iControllerIndex)
+ {
+ /* Get controller cache: */
+ const UISettingsCacheMachineStorageController &controllerCache = m_pCache->child(iControllerIndex);
+
+ /* Update controller marked for 'update' (if it can be updated): */
+ if (controllerCache.wasUpdated() && isControllerCouldBeUpdated(controllerCache))
+ fSuccess = updateStorageController(controllerCache, true);
+ }
+ for (int iControllerIndex = 0; fSuccess && iControllerIndex < m_pCache->childCount(); ++iControllerIndex)
+ {
+ /* Get controller cache: */
+ const UISettingsCacheMachineStorageController &controllerCache = m_pCache->child(iControllerIndex);
+
+ /* Update controller marked for 'update' (if it can be updated): */
+ if (controllerCache.wasUpdated() && isControllerCouldBeUpdated(controllerCache))
+ fSuccess = updateStorageController(controllerCache, false);
+ }
+
+ /* For each controller ('creating' step): */
+ // Finally we are creating new controllers,
+ // with attachments which were released for sure.
+ for (int iControllerIndex = 0; fSuccess && iControllerIndex < m_pCache->childCount(); ++iControllerIndex)
+ {
+ /* Get controller cache: */
+ const UISettingsCacheMachineStorageController &controllerCache = m_pCache->child(iControllerIndex);
+
+ /* Create controller marked for 'create' or 'update' (if it can't be updated): */
+ if (controllerCache.wasCreated() || (controllerCache.wasUpdated() && !isControllerCouldBeUpdated(controllerCache)))
+ fSuccess = createStorageController(controllerCache);
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsStorage::removeStorageController(const UISettingsCacheMachineStorageController &controllerCache)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Remove controller: */
+ if (fSuccess && isMachineOffline())
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineStorageController &oldControllerData = controllerCache.base();
+
+ /* Search for a controller with the same name: */
+ const CStorageController &comController = m_machine.GetStorageControllerByName(oldControllerData.m_guiValue.m_strName);
+ fSuccess = m_machine.isOk() && comController.isNotNull();
+
+ /* Make sure controller really exists: */
+ if (fSuccess)
+ {
+ /* Remove controller with all the attachments at one shot: */
+ m_machine.RemoveStorageController(oldControllerData.m_guiValue.m_strName);
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsStorage::createStorageController(const UISettingsCacheMachineStorageController &controllerCache)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Create controller: */
+ if (fSuccess && isMachineOffline())
+ {
+ /* Get new data from cache: */
+ const UIDataSettingsMachineStorageController &newControllerData = controllerCache.data();
+
+ /* Search for a controller with the same name: */
+ const CMachine comMachine(m_machine);
+ CStorageController comController = comMachine.GetStorageControllerByName(newControllerData.m_guiValue.m_strName);
+ fSuccess = !comMachine.isOk() && comController.isNull();
+ AssertReturn(fSuccess, false);
+
+ /* Make sure controller doesn't exist: */
+ if (fSuccess)
+ {
+ /* Create controller: */
+ comController = m_machine.AddStorageController(newControllerData.m_guiValue.m_strName,
+ newControllerData.m_guiValue.m_enmBus);
+ fSuccess = m_machine.isOk() && comController.isNotNull();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ else
+ {
+ /* Save controller type: */
+ if (fSuccess)
+ {
+ comController.SetControllerType(newControllerData.m_guiValue.m_enmType);
+ fSuccess = comController.isOk();
+ }
+ /* Save whether controller uses host IO cache: */
+ if (fSuccess)
+ {
+ comController.SetUseHostIOCache(newControllerData.m_guiValue.m_fUseHostIOCache);
+ fSuccess = comController.isOk();
+ }
+ /* Save controller port number: */
+ if ( fSuccess
+ && ( newControllerData.m_guiValue.m_enmBus == KStorageBus_SATA
+ || newControllerData.m_guiValue.m_enmBus == KStorageBus_SAS
+ || newControllerData.m_guiValue.m_enmBus == KStorageBus_PCIe
+ || newControllerData.m_guiValue.m_enmBus == KStorageBus_VirtioSCSI))
+ {
+ ULONG uNewPortCount = newControllerData.m_guiValue.m_uPortCount;
+ if (fSuccess)
+ {
+ uNewPortCount = qMax(uNewPortCount, comController.GetMinPortCount());
+ fSuccess = comController.isOk();
+ }
+ if (fSuccess)
+ {
+ uNewPortCount = qMin(uNewPortCount, comController.GetMaxPortCount());
+ fSuccess = comController.isOk();
+ }
+ if (fSuccess)
+ {
+ comController.SetPortCount(uNewPortCount);
+ fSuccess = comController.isOk();
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comController));
+
+ /* For each attachment: */
+ for (int iAttachmentIndex = 0; fSuccess && iAttachmentIndex < controllerCache.childCount(); ++iAttachmentIndex)
+ {
+ /* Get attachment cache: */
+ const UISettingsCacheMachineStorageAttachment &attachmentCache = controllerCache.child(iAttachmentIndex);
+
+ /* Create attachment if it was not 'removed': */
+ if (!attachmentCache.wasRemoved())
+ fSuccess = createStorageAttachment(controllerCache, attachmentCache);
+ }
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsStorage::updateStorageController(const UISettingsCacheMachineStorageController &controllerCache,
+ bool fRemovingStep)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Update controller: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineStorageController &oldControllerData = controllerCache.base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineStorageController &newControllerData = controllerCache.data();
+
+ /* Search for a controller with the same name: */
+ CStorageController comController = m_machine.GetStorageControllerByName(oldControllerData.m_guiValue.m_strName);
+ fSuccess = m_machine.isOk() && comController.isNotNull();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ else
+ {
+ /* Save controller type: */
+ if (fSuccess && newControllerData.m_guiValue.m_enmType != oldControllerData.m_guiValue.m_enmType)
+ {
+ comController.SetControllerType(newControllerData.m_guiValue.m_enmType);
+ fSuccess = comController.isOk();
+ }
+ /* Save whether controller uses IO cache: */
+ if (fSuccess && newControllerData.m_guiValue.m_fUseHostIOCache != oldControllerData.m_guiValue.m_fUseHostIOCache)
+ {
+ comController.SetUseHostIOCache(newControllerData.m_guiValue.m_fUseHostIOCache);
+ fSuccess = comController.isOk();
+ }
+ /* Save controller port number: */
+ if ( fSuccess
+ && newControllerData.m_guiValue.m_uPortCount != oldControllerData.m_guiValue.m_uPortCount
+ && ( newControllerData.m_guiValue.m_enmBus == KStorageBus_SATA
+ || newControllerData.m_guiValue.m_enmBus == KStorageBus_SAS
+ || newControllerData.m_guiValue.m_enmBus == KStorageBus_PCIe
+ || newControllerData.m_guiValue.m_enmBus == KStorageBus_VirtioSCSI))
+ {
+ ULONG uNewPortCount = newControllerData.m_guiValue.m_uPortCount;
+ if (fSuccess)
+ {
+ uNewPortCount = qMax(uNewPortCount, comController.GetMinPortCount());
+ fSuccess = comController.isOk();
+ }
+ if (fSuccess)
+ {
+ uNewPortCount = qMin(uNewPortCount, comController.GetMaxPortCount());
+ fSuccess = comController.isOk();
+ }
+ if (fSuccess)
+ {
+ comController.SetPortCount(uNewPortCount);
+ fSuccess = comController.isOk();
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comController));
+
+ // We need to separately remove attachments first because
+ // there could be limited amount of attachments or media available.
+ if (fRemovingStep)
+ {
+ /* For each attachment ('removing' step): */
+ for (int iAttachmentIndex = 0; fSuccess && iAttachmentIndex < controllerCache.childCount(); ++iAttachmentIndex)
+ {
+ /* Get attachment cache: */
+ const UISettingsCacheMachineStorageAttachment &attachmentCache = controllerCache.child(iAttachmentIndex);
+
+ /* Remove attachment marked for 'remove' or 'update' (if it can't be updated): */
+ if ( attachmentCache.wasRemoved()
+ || (attachmentCache.wasUpdated() && !isAttachmentCouldBeUpdated(attachmentCache)))
+ fSuccess = removeStorageAttachment(controllerCache, attachmentCache);
+ }
+ }
+ else
+ {
+ /* For each attachment ('creating' step): */
+ for (int iAttachmentIndex = 0; fSuccess && iAttachmentIndex < controllerCache.childCount(); ++iAttachmentIndex)
+ {
+ /* Get attachment cache: */
+ const UISettingsCacheMachineStorageAttachment &attachmentCache = controllerCache.child(iAttachmentIndex);
+
+ /* Create attachment marked for 'create' or 'update' (if it can't be updated): */
+ if ( attachmentCache.wasCreated()
+ || (attachmentCache.wasUpdated() && !isAttachmentCouldBeUpdated(attachmentCache)))
+ fSuccess = createStorageAttachment(controllerCache, attachmentCache);
+
+ else
+
+ /* Update attachment marked for 'update' (if it can be updated): */
+ if (attachmentCache.wasUpdated() && isAttachmentCouldBeUpdated(attachmentCache))
+ fSuccess = updateStorageAttachment(controllerCache, attachmentCache);
+ }
+ }
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsStorage::removeStorageAttachment(const UISettingsCacheMachineStorageController &controllerCache,
+ const UISettingsCacheMachineStorageAttachment &attachmentCache)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Remove attachment: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineStorageController &oldControllerData = controllerCache.base();
+ /* Get old data from cache: */
+ const UIDataSettingsMachineStorageAttachment &oldAttachmentData = attachmentCache.base();
+
+ /* Search for an attachment with the same parameters: */
+ const CMediumAttachment &comAttachment = m_machine.GetMediumAttachment(oldControllerData.m_guiValue.m_strName,
+ oldAttachmentData.m_guiValue.m_iPort,
+ oldAttachmentData.m_guiValue.m_iDevice);
+ fSuccess = m_machine.isOk() && comAttachment.isNotNull();
+
+ /* Make sure attachment really exists: */
+ if (fSuccess)
+ {
+ /* Remove attachment: */
+ m_machine.DetachDevice(oldControllerData.m_guiValue.m_strName,
+ oldAttachmentData.m_guiValue.m_iPort,
+ oldAttachmentData.m_guiValue.m_iDevice);
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsStorage::createStorageAttachment(const UISettingsCacheMachineStorageController &controllerCache,
+ const UISettingsCacheMachineStorageAttachment &attachmentCache)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Create attachment: */
+ if (fSuccess)
+ {
+ /* Get new data from cache: */
+ const UIDataSettingsMachineStorageController &newControllerData = controllerCache.data();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineStorageAttachment &newAttachmentData = attachmentCache.data();
+
+ /* Search for an attachment with the same parameters: */
+ const CMachine comMachine(m_machine);
+ const CMediumAttachment &comAttachment = comMachine.GetMediumAttachment(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice);
+ fSuccess = !comMachine.isOk() && comAttachment.isNull();
+ AssertReturn(fSuccess, false);
+
+ /* Make sure attachment doesn't exist: */
+ if (fSuccess)
+ {
+ /* Create attachment: */
+ const UIMedium vboxMedium = uiCommon().medium(newAttachmentData.m_guiValue.m_uMediumId);
+ const CMedium comMedium = vboxMedium.medium();
+ m_machine.AttachDevice(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ newAttachmentData.m_guiValue.m_enmDeviceType,
+ comMedium);
+ fSuccess = m_machine.isOk();
+ }
+
+ if (newAttachmentData.m_guiValue.m_enmDeviceType == KDeviceType_DVD)
+ {
+ /* Save whether this is a passthrough device: */
+ if (fSuccess && isMachineOffline())
+ {
+ m_machine.PassthroughDevice(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ newAttachmentData.m_guiValue.m_fPassthrough);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save whether this is a live cd device: */
+ if (fSuccess)
+ {
+ m_machine.TemporaryEjectDevice(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ newAttachmentData.m_guiValue.m_fTempEject);
+ fSuccess = m_machine.isOk();
+ }
+ }
+ else if (newAttachmentData.m_guiValue.m_enmDeviceType == KDeviceType_HardDisk)
+ {
+ /* Save whether this is a ssd device: */
+ if (fSuccess && isMachineOffline())
+ {
+ m_machine.NonRotationalDevice(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ newAttachmentData.m_guiValue.m_fNonRotational);
+ fSuccess = m_machine.isOk();
+ }
+ }
+
+ if (newControllerData.m_guiValue.m_enmBus == KStorageBus_SATA)
+ {
+ /* Save whether this device is hot-pluggable: */
+ if (fSuccess && isMachineOffline())
+ {
+ m_machine.SetHotPluggableForDevice(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ newAttachmentData.m_guiValue.m_fHotPluggable);
+ fSuccess = m_machine.isOk();
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsStorage::updateStorageAttachment(const UISettingsCacheMachineStorageController &controllerCache,
+ const UISettingsCacheMachineStorageAttachment &attachmentCache)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Update attachment: */
+ if (fSuccess)
+ {
+ /* Get new data from cache: */
+ const UIDataSettingsMachineStorageController &newControllerData = controllerCache.data();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineStorageAttachment &newAttachmentData = attachmentCache.data();
+
+ /* Search for an attachment with the same parameters: */
+ const CMediumAttachment &comAttachment = m_machine.GetMediumAttachment(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice);
+ fSuccess = m_machine.isOk() && comAttachment.isNotNull();
+
+ /* Make sure attachment doesn't exist: */
+ if (fSuccess)
+ {
+ /* Remount attachment: */
+ const UIMedium vboxMedium = uiCommon().medium(newAttachmentData.m_guiValue.m_uMediumId);
+ const CMedium comMedium = vboxMedium.medium();
+ m_machine.MountMedium(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ comMedium,
+ true /* force? */);
+ fSuccess = m_machine.isOk();
+ }
+
+ if (newAttachmentData.m_guiValue.m_enmDeviceType == KDeviceType_DVD)
+ {
+ /* Save whether this is a passthrough device: */
+ if (fSuccess && isMachineOffline())
+ {
+ m_machine.PassthroughDevice(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ newAttachmentData.m_guiValue.m_fPassthrough);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save whether this is a live cd device: */
+ if (fSuccess)
+ {
+ m_machine.TemporaryEjectDevice(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ newAttachmentData.m_guiValue.m_fTempEject);
+ fSuccess = m_machine.isOk();
+ }
+ }
+ else if (newAttachmentData.m_guiValue.m_enmDeviceType == KDeviceType_HardDisk)
+ {
+ /* Save whether this is a ssd device: */
+ if (fSuccess && isMachineOffline())
+ {
+ m_machine.NonRotationalDevice(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ newAttachmentData.m_guiValue.m_fNonRotational);
+ fSuccess = m_machine.isOk();
+ }
+ }
+
+ if (newControllerData.m_guiValue.m_enmBus == KStorageBus_SATA)
+ {
+ /* Save whether this device is hot-pluggable: */
+ if (fSuccess && isMachineOffline())
+ {
+ m_machine.SetHotPluggableForDevice(newControllerData.m_guiValue.m_strName,
+ newAttachmentData.m_guiValue.m_iPort,
+ newAttachmentData.m_guiValue.m_iDevice,
+ newAttachmentData.m_guiValue.m_fHotPluggable);
+ fSuccess = m_machine.isOk();
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsStorage::isControllerCouldBeUpdated(const UISettingsCacheMachineStorageController &controllerCache) const
+{
+ /* IController interface doesn't allow to change 'bus' attribute but allows
+ * to change 'name' attribute which can conflict with another one controller.
+ * Both those attributes could be changed in GUI directly or indirectly.
+ * For such cases we have to recreate IController instance,
+ * for other cases we will update controller attributes only. */
+ const UIDataSettingsMachineStorageController &oldControllerData = controllerCache.base();
+ const UIDataSettingsMachineStorageController &newControllerData = controllerCache.data();
+ return true
+ && (newControllerData.m_guiValue.m_strName == oldControllerData.m_guiValue.m_strName)
+ && (newControllerData.m_guiValue.m_enmBus == oldControllerData.m_guiValue.m_enmBus)
+ ;
+}
+
+bool UIMachineSettingsStorage::isAttachmentCouldBeUpdated(const UISettingsCacheMachineStorageAttachment &attachmentCache) const
+{
+ /* IMediumAttachment could be indirectly updated through IMachine
+ * only if attachment type, device and port were NOT changed and is one of the next types:
+ * KDeviceType_Floppy or KDeviceType_DVD.
+ * For other cases we will recreate attachment fully: */
+ const UIDataSettingsMachineStorageAttachment &oldAttachmentData = attachmentCache.base();
+ const UIDataSettingsMachineStorageAttachment &newAttachmentData = attachmentCache.data();
+ return true
+ && (newAttachmentData.m_guiValue.m_enmDeviceType == oldAttachmentData.m_guiValue.m_enmDeviceType)
+ && (newAttachmentData.m_guiValue.m_iPort == oldAttachmentData.m_guiValue.m_iPort)
+ && (newAttachmentData.m_guiValue.m_iDevice == oldAttachmentData.m_guiValue.m_iDevice)
+ && ( newAttachmentData.m_guiValue.m_enmDeviceType == KDeviceType_Floppy
+ || newAttachmentData.m_guiValue.m_enmDeviceType == KDeviceType_DVD)
+ ;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsStorage.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsStorage.h
new file mode 100644
index 00000000..a935af9e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsStorage.h
@@ -0,0 +1,155 @@
+/* $Id: UIMachineSettingsStorage.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsStorage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsStorage_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsStorage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class UIActionPool;
+class UIStorageSettingsEditor;
+struct UIDataSettingsMachineStorage;
+struct UIDataSettingsMachineStorageController;
+struct UIDataSettingsMachineStorageAttachment;
+typedef UISettingsCache<UIDataSettingsMachineStorageAttachment> UISettingsCacheMachineStorageAttachment;
+typedef UISettingsCachePool<UIDataSettingsMachineStorageController, UISettingsCacheMachineStorageAttachment> UISettingsCacheMachineStorageController;
+typedef UISettingsCachePool<UIDataSettingsMachineStorage, UISettingsCacheMachineStorageController> UISettingsCacheMachineStorage;
+
+/** Machine settings: Storage page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsStorage : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about storage changed. */
+ void sigStorageChanged();
+
+public:
+
+ /** Constructs Storage settings page. */
+ UIMachineSettingsStorage(UIActionPool *pActionPool);
+ /** Destructs Storage settings page. */
+ virtual ~UIMachineSettingsStorage() RT_OVERRIDE;
+
+ /** Defines chipset @a enmType. */
+ void setChipsetType(KChipsetType enmType);
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ virtual bool validate(QList<UIValidationMessage> &messages) RT_OVERRIDE;
+
+ /** Defines the configuration access @a enmLevel. */
+ virtual void setConfigurationAccessLevel(ConfigurationAccessLevel enmLevel) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+ /** Removes existing storage controller described by the @a controllerCache. */
+ bool removeStorageController(const UISettingsCacheMachineStorageController &controllerCache);
+ /** Creates existing storage controller described by the @a controllerCache. */
+ bool createStorageController(const UISettingsCacheMachineStorageController &controllerCache);
+ /** Updates existing storage controller described by the @a controllerCache. */
+ bool updateStorageController(const UISettingsCacheMachineStorageController &controllerCache,
+ bool fRemovingStep);
+ /** Removes existing storage attachment described by the @a controllerCache and @a attachmentCache. */
+ bool removeStorageAttachment(const UISettingsCacheMachineStorageController &controllerCache,
+ const UISettingsCacheMachineStorageAttachment &attachmentCache);
+ /** Creates existing storage attachment described by the @a controllerCache and @a attachmentCache. */
+ bool createStorageAttachment(const UISettingsCacheMachineStorageController &controllerCache,
+ const UISettingsCacheMachineStorageAttachment &attachmentCache);
+ /** Updates existing storage attachment described by the @a controllerCache and @a attachmentCache. */
+ bool updateStorageAttachment(const UISettingsCacheMachineStorageController &controllerCache,
+ const UISettingsCacheMachineStorageAttachment &attachmentCache);
+ /** Returns whether the controller described by the @a controllerCache could be updated or recreated otherwise. */
+ bool isControllerCouldBeUpdated(const UISettingsCacheMachineStorageController &controllerCache) const;
+ /** Returns whether the attachment described by the @a attachmentCache could be updated or recreated otherwise. */
+ bool isAttachmentCouldBeUpdated(const UISettingsCacheMachineStorageAttachment &attachmentCache) const;
+
+ /** Holds the action pool instance. */
+ UIActionPool *m_pActionPool;
+
+ /** Holds the machine ID. */
+ QUuid m_uMachineId;
+ /** Holds the machine name. */
+ QString m_strMachineName;
+ /** Holds the machine settings file-path. */
+ QString m_strMachineSettingsFilePath;
+ /** Holds the machine guest OS type ID. */
+ QString m_strMachineGuestOSTypeId;
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheMachineStorage *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the storage settings editor instance. */
+ UIStorageSettingsEditor *m_pEditorStorageSettings;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsStorage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSystem.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSystem.cpp
new file mode 100644
index 00000000..ba83fc6c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSystem.cpp
@@ -0,0 +1,1050 @@
+/* $Id: UIMachineSettingsSystem.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsSystem class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QITabWidget.h"
+#include "UIAccelerationFeaturesEditor.h"
+#include "UIBaseMemoryEditor.h"
+#include "UIBootOrderEditor.h"
+#include "UIChipsetEditor.h"
+#include "UICommon.h"
+#include "UIErrorString.h"
+#include "UIExecutionCapEditor.h"
+#include "UIMachineSettingsSystem.h"
+#include "UIMotherboardFeaturesEditor.h"
+#include "UIParavirtProviderEditor.h"
+#include "UIPointingHIDEditor.h"
+#include "UIProcessorFeaturesEditor.h"
+#include "UITpmEditor.h"
+#include "UITranslator.h"
+#include "UIVirtualCPUEditor.h"
+
+/* COM includes: */
+#include "CBIOSSettings.h"
+#include "CNvramStore.h"
+#include "CTrustedPlatformModule.h"
+#include "CUefiVariableStore.h"
+
+
+/** Machine settings: System page data structure. */
+struct UIDataSettingsMachineSystem
+{
+ /** Constructs data. */
+ UIDataSettingsMachineSystem()
+ /* Support flags: */
+ : m_fSupportedPAE(false)
+ , m_fSupportedNestedHwVirtEx(false)
+ , m_fSupportedHwVirtEx(false)
+ , m_fSupportedNestedPaging(false)
+ /* Motherboard data: */
+ , m_iMemorySize(-1)
+ , m_bootItems(UIBootItemDataList())
+ , m_chipsetType(KChipsetType_Null)
+ , m_tpmType(KTpmType_None)
+ , m_pointingHIDType(KPointingHIDType_None)
+ , m_fEnabledIoApic(false)
+ , m_fEnabledEFI(false)
+ , m_fEnabledUTC(false)
+ , m_fAvailableSecureBoot(false)
+ , m_fEnabledSecureBoot(false)
+ , m_fResetSecureBoot(false)
+ /* CPU data: */
+ , m_cCPUCount(-1)
+ , m_iCPUExecCap(-1)
+ , m_fEnabledPAE(false)
+ , m_fEnabledNestedHwVirtEx(false)
+ /* Acceleration data: */
+ , m_paravirtProvider(KParavirtProvider_None)
+ , m_fEnabledNestedPaging(false)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineSystem &other) const
+ {
+ return true
+ /* Support flags: */
+ && (m_fSupportedPAE == other.m_fSupportedPAE)
+ && (m_fSupportedNestedHwVirtEx == other.m_fSupportedNestedHwVirtEx)
+ && (m_fSupportedHwVirtEx == other.m_fSupportedHwVirtEx)
+ && (m_fSupportedNestedPaging == other.m_fSupportedNestedPaging)
+ /* Motherboard data: */
+ && (m_iMemorySize == other.m_iMemorySize)
+ && (m_bootItems == other.m_bootItems)
+ && (m_chipsetType == other.m_chipsetType)
+ && (m_tpmType == other.m_tpmType)
+ && (m_pointingHIDType == other.m_pointingHIDType)
+ && (m_fEnabledIoApic == other.m_fEnabledIoApic)
+ && (m_fEnabledEFI == other.m_fEnabledEFI)
+ && (m_fEnabledUTC == other.m_fEnabledUTC)
+ && (m_fAvailableSecureBoot == other.m_fAvailableSecureBoot)
+ && (m_fEnabledSecureBoot == other.m_fEnabledSecureBoot)
+ && (m_fResetSecureBoot == other.m_fResetSecureBoot)
+ /* CPU data: */
+ && (m_cCPUCount == other.m_cCPUCount)
+ && (m_iCPUExecCap == other.m_iCPUExecCap)
+ && (m_fEnabledPAE == other.m_fEnabledPAE)
+ && (m_fEnabledNestedHwVirtEx == other.m_fEnabledNestedHwVirtEx)
+ /* Acceleration data: */
+ && (m_paravirtProvider == other.m_paravirtProvider)
+ && (m_fEnabledNestedPaging == other.m_fEnabledNestedPaging)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineSystem &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineSystem &other) const { return !equal(other); }
+
+ /** Holds whether the PAE is supported. */
+ bool m_fSupportedPAE;
+ /** Holds whether the Nested HW Virt Ex is supported. */
+ bool m_fSupportedNestedHwVirtEx;
+ /** Holds whether the HW Virt Ex is supported. */
+ bool m_fSupportedHwVirtEx;
+ /** Holds whether the Nested Paging is supported. */
+ bool m_fSupportedNestedPaging;
+
+ /** Holds the RAM size. */
+ int m_iMemorySize;
+ /** Holds the boot items. */
+ UIBootItemDataList m_bootItems;
+ /** Holds the chipset type. */
+ KChipsetType m_chipsetType;
+ /** Holds the TPM type. */
+ KTpmType m_tpmType;
+ /** Holds the pointing HID type. */
+ KPointingHIDType m_pointingHIDType;
+ /** Holds whether the IO APIC is enabled. */
+ bool m_fEnabledIoApic;
+ /** Holds whether the EFI is enabled. */
+ bool m_fEnabledEFI;
+ /** Holds whether the UTC is enabled. */
+ bool m_fEnabledUTC;
+ /** Holds whether the secure boot is available. */
+ bool m_fAvailableSecureBoot;
+ /** Holds whether the secure boot is enabled. */
+ bool m_fEnabledSecureBoot;
+ /** Holds whether the secure boot is reseted. */
+ bool m_fResetSecureBoot;
+
+ /** Holds the CPU count. */
+ int m_cCPUCount;
+ /** Holds the CPU execution cap. */
+ int m_iCPUExecCap;
+ /** Holds whether the PAE is enabled. */
+ bool m_fEnabledPAE;
+ /** Holds whether the Nested HW Virt Ex is enabled. */
+ bool m_fEnabledNestedHwVirtEx;
+
+ /** Holds the paravirtualization provider. */
+ KParavirtProvider m_paravirtProvider;
+ /** Holds whether the Nested Paging is enabled. */
+ bool m_fEnabledNestedPaging;
+};
+
+
+UIMachineSettingsSystem::UIMachineSettingsSystem()
+ : m_fIsUSBEnabled(false)
+ , m_pCache(0)
+ , m_pTabWidget(0)
+ , m_pTabMotherboard(0)
+ , m_pEditorBaseMemory(0)
+ , m_pEditorBootOrder(0)
+ , m_pEditorChipset(0)
+ , m_pEditorTpm(0)
+ , m_pEditorPointingHID(0)
+ , m_pEditorMotherboardFeatures(0)
+ , m_pTabProcessor(0)
+ , m_pEditorVCPU(0)
+ , m_pEditorExecCap(0)
+ , m_pEditorProcessorFeatures(0)
+ , m_pTabAcceleration(0)
+ , m_pEditorParavirtProvider(0)
+ , m_pEditorAccelerationFeatures(0)
+{
+ prepare();
+}
+
+UIMachineSettingsSystem::~UIMachineSettingsSystem()
+{
+ cleanup();
+}
+
+bool UIMachineSettingsSystem::isHWVirtExSupported() const
+{
+ AssertPtrReturn(m_pCache, false);
+ return m_pCache->base().m_fSupportedHwVirtEx;
+}
+
+bool UIMachineSettingsSystem::isNestedPagingSupported() const
+{
+ AssertPtrReturn(m_pCache, false);
+ return m_pCache->base().m_fSupportedNestedPaging;
+}
+
+bool UIMachineSettingsSystem::isNestedPagingEnabled() const
+{
+ return m_pEditorAccelerationFeatures->isEnabledNestedPaging();
+}
+
+bool UIMachineSettingsSystem::isNestedHWVirtExSupported() const
+{
+ AssertPtrReturn(m_pCache, false);
+ return m_pCache->base().m_fSupportedNestedHwVirtEx;
+}
+
+bool UIMachineSettingsSystem::isNestedHWVirtExEnabled() const
+{
+ return m_pEditorProcessorFeatures->isEnabledNestedVirtualization();
+}
+
+bool UIMachineSettingsSystem::isHIDEnabled() const
+{
+ return m_pEditorPointingHID->value() != KPointingHIDType_PS2Mouse;
+}
+
+KChipsetType UIMachineSettingsSystem::chipsetType() const
+{
+ return m_pEditorChipset->value();
+}
+
+void UIMachineSettingsSystem::setUSBEnabled(bool fEnabled)
+{
+ /* Make sure USB status has changed: */
+ if (m_fIsUSBEnabled == fEnabled)
+ return;
+
+ /* Update USB status value: */
+ m_fIsUSBEnabled = fEnabled;
+
+ /* Revalidate: */
+ revalidate();
+}
+
+bool UIMachineSettingsSystem::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsSystem::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Prepare old data: */
+ UIDataSettingsMachineSystem oldSystemData;
+
+ /* Gather support flags: */
+ oldSystemData.m_fSupportedPAE = uiCommon().host().GetProcessorFeature(KProcessorFeature_PAE);
+ oldSystemData.m_fSupportedNestedHwVirtEx = uiCommon().host().GetProcessorFeature(KProcessorFeature_NestedHWVirt);
+ oldSystemData.m_fSupportedHwVirtEx = uiCommon().host().GetProcessorFeature(KProcessorFeature_HWVirtEx);
+ oldSystemData.m_fSupportedNestedPaging = uiCommon().host().GetProcessorFeature(KProcessorFeature_NestedPaging);
+
+ /* Gather old 'Motherboard' data: */
+ oldSystemData.m_iMemorySize = m_machine.GetMemorySize();
+ oldSystemData.m_bootItems = loadBootItems(m_machine);
+ oldSystemData.m_chipsetType = m_machine.GetChipsetType();
+ oldSystemData.m_tpmType = m_machine.GetTrustedPlatformModule().GetType();
+ oldSystemData.m_pointingHIDType = m_machine.GetPointingHIDType();
+ oldSystemData.m_fEnabledIoApic = m_machine.GetBIOSSettings().GetIOAPICEnabled();
+ oldSystemData.m_fEnabledEFI = m_machine.GetFirmwareType() >= KFirmwareType_EFI && m_machine.GetFirmwareType() <= KFirmwareType_EFIDUAL;
+ oldSystemData.m_fEnabledUTC = m_machine.GetRTCUseUTC();
+ CNvramStore comStoreLvl1 = m_machine.GetNonVolatileStore();
+ CUefiVariableStore comStoreLvl2 = comStoreLvl1.GetUefiVariableStore();
+ oldSystemData.m_fAvailableSecureBoot = comStoreLvl2.isNotNull();
+ oldSystemData.m_fEnabledSecureBoot = oldSystemData.m_fAvailableSecureBoot
+ ? comStoreLvl2.GetSecureBootEnabled()
+ : false;
+ oldSystemData.m_fResetSecureBoot = false;
+
+ /* Gather old 'Processor' data: */
+ oldSystemData.m_cCPUCount = oldSystemData.m_fSupportedHwVirtEx ? m_machine.GetCPUCount() : 1;
+ oldSystemData.m_iCPUExecCap = m_machine.GetCPUExecutionCap();
+ oldSystemData.m_fEnabledPAE = m_machine.GetCPUProperty(KCPUPropertyType_PAE);
+ oldSystemData.m_fEnabledNestedHwVirtEx = m_machine.GetCPUProperty(KCPUPropertyType_HWVirt);
+
+ /* Gather old 'Acceleration' data: */
+ oldSystemData.m_paravirtProvider = m_machine.GetParavirtProvider();
+ oldSystemData.m_fEnabledNestedPaging = m_machine.GetHWVirtExProperty(KHWVirtExPropertyType_NestedPaging);
+
+ /* Cache old data: */
+ m_pCache->cacheInitialData(oldSystemData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsSystem::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Get old data from cache: */
+ const UIDataSettingsMachineSystem &oldSystemData = m_pCache->base();
+
+ /* Load old 'Motherboard' data from cache: */
+ if (m_pEditorBaseMemory)
+ m_pEditorBaseMemory->setValue(oldSystemData.m_iMemorySize);
+ if (m_pEditorBootOrder)
+ m_pEditorBootOrder->setValue(oldSystemData.m_bootItems);
+ if (m_pEditorChipset)
+ m_pEditorChipset->setValue(oldSystemData.m_chipsetType);
+ if (m_pEditorTpm)
+ m_pEditorTpm->setValue(oldSystemData.m_tpmType);
+ if (m_pEditorPointingHID)
+ m_pEditorPointingHID->setValue(oldSystemData.m_pointingHIDType);
+ if (m_pEditorMotherboardFeatures)
+ {
+ m_pEditorMotherboardFeatures->setEnableIoApic(oldSystemData.m_fEnabledIoApic);
+ m_pEditorMotherboardFeatures->setEnableEfi(oldSystemData.m_fEnabledEFI);
+ m_pEditorMotherboardFeatures->setEnableUtcTime(oldSystemData.m_fEnabledUTC);
+ m_pEditorMotherboardFeatures->setEnableSecureBoot(oldSystemData.m_fEnabledSecureBoot);
+ }
+
+ /* Load old 'Processor' data from cache: */
+ if (m_pEditorVCPU)
+ m_pEditorVCPU->setValue(oldSystemData.m_cCPUCount);
+ if (m_pEditorExecCap)
+ m_pEditorExecCap->setValue(oldSystemData.m_iCPUExecCap);
+ if (m_pEditorProcessorFeatures)
+ {
+ m_pEditorProcessorFeatures->setEnablePae(oldSystemData.m_fEnabledPAE);
+ m_pEditorProcessorFeatures->setEnableNestedVirtualization(oldSystemData.m_fEnabledNestedHwVirtEx);
+ }
+
+ /* Load old 'Acceleration' data from cache: */
+ if (m_pEditorParavirtProvider)
+ m_pEditorParavirtProvider->setValue(oldSystemData.m_paravirtProvider);
+ if (m_pEditorAccelerationFeatures)
+ m_pEditorAccelerationFeatures->setEnableNestedPaging(oldSystemData.m_fEnabledNestedPaging);
+
+ /* Polish page finally: */
+ polishPage();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMachineSettingsSystem::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new data: */
+ UIDataSettingsMachineSystem newSystemData;
+
+ /* Gather support flags: */
+ newSystemData.m_fSupportedPAE = m_pCache->base().m_fSupportedPAE;
+ newSystemData.m_fSupportedNestedHwVirtEx = isNestedHWVirtExSupported();
+ newSystemData.m_fSupportedHwVirtEx = isHWVirtExSupported();
+ newSystemData.m_fSupportedNestedPaging = isNestedPagingSupported();
+
+ /* Gather 'Motherboard' data: */
+ if (m_pEditorBaseMemory)
+ newSystemData.m_iMemorySize = m_pEditorBaseMemory->value();
+ if (m_pEditorBootOrder)
+ newSystemData.m_bootItems = m_pEditorBootOrder->value();
+ if (m_pEditorChipset)
+ newSystemData.m_chipsetType = m_pEditorChipset->value();
+ if (m_pEditorTpm)
+ newSystemData.m_tpmType = m_pEditorTpm->value();
+ if (m_pEditorPointingHID)
+ newSystemData.m_pointingHIDType = m_pEditorPointingHID->value();
+ if ( m_pEditorMotherboardFeatures
+ && m_pEditorVCPU
+ && m_pEditorChipset)
+ newSystemData.m_fEnabledIoApic = m_pEditorMotherboardFeatures->isEnabledIoApic()
+ || m_pEditorVCPU->value() > 1
+ || m_pEditorChipset->value() == KChipsetType_ICH9;
+ if (m_pEditorMotherboardFeatures)
+ newSystemData.m_fEnabledEFI = m_pEditorMotherboardFeatures->isEnabledEfi();
+ if (m_pEditorMotherboardFeatures)
+ newSystemData.m_fEnabledUTC = m_pEditorMotherboardFeatures->isEnabledUtcTime();
+ if (m_pEditorMotherboardFeatures)
+ {
+ newSystemData.m_fAvailableSecureBoot = m_pCache->base().m_fAvailableSecureBoot;
+ newSystemData.m_fEnabledSecureBoot = m_pEditorMotherboardFeatures->isEnabledSecureBoot();
+ newSystemData.m_fResetSecureBoot = m_pEditorMotherboardFeatures->isResetSecureBoot();
+ }
+
+ /* Gather 'Processor' data: */
+ if (m_pEditorVCPU)
+ newSystemData.m_cCPUCount = m_pEditorVCPU->value();
+ if (m_pEditorExecCap)
+ newSystemData.m_iCPUExecCap = m_pEditorExecCap->value();
+ if (m_pEditorProcessorFeatures)
+ newSystemData.m_fEnabledPAE = m_pEditorProcessorFeatures->isEnabledPae();
+ newSystemData.m_fEnabledNestedHwVirtEx = isNestedHWVirtExEnabled();
+
+ /* Gather 'Acceleration' data: */
+ if (m_pEditorParavirtProvider)
+ newSystemData.m_paravirtProvider = m_pEditorParavirtProvider->value();
+ /* Enable Nested Paging automatically if it's supported and
+ * Nested HW Virt Ex is requested. */
+ newSystemData.m_fEnabledNestedPaging = isNestedPagingEnabled()
+ || ( isNestedPagingSupported()
+ && isNestedHWVirtExEnabled());
+
+ /* Cache new data: */
+ m_pCache->cacheCurrentData(newSystemData);
+}
+
+void UIMachineSettingsSystem::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+bool UIMachineSettingsSystem::validate(QList<UIValidationMessage> &messages)
+{
+ /* Pass by default: */
+ bool fPass = true;
+
+ /* Motherboard tab: */
+ {
+ /* Prepare message: */
+ UIValidationMessage message;
+ message.first = UITranslator::removeAccelMark(m_pTabWidget->tabText(0));
+
+ /* RAM amount test: */
+ const ulong uFullSize = uiCommon().host().GetMemorySize();
+ if (m_pEditorBaseMemory->value() > (int)m_pEditorBaseMemory->maxRAMAlw())
+ {
+ message.second << tr(
+ "More than <b>%1%</b> of the host computer's memory (<b>%2</b>) is assigned to the virtual machine. "
+ "Not enough memory is left for the host operating system. Please select a smaller amount.")
+ .arg((unsigned)qRound((double)m_pEditorBaseMemory->maxRAMAlw() / uFullSize * 100.0))
+ .arg(UITranslator::formatSize((uint64_t)uFullSize * _1M));
+ fPass = false;
+ }
+ else if (m_pEditorBaseMemory->value() > (int)m_pEditorBaseMemory->maxRAMOpt())
+ {
+ message.second << tr(
+ "More than <b>%1%</b> of the host computer's memory (<b>%2</b>) is assigned to the virtual machine. "
+ "There might not be enough memory left for the host operating system. Please consider selecting a smaller amount.")
+ .arg((unsigned)qRound((double)m_pEditorBaseMemory->maxRAMOpt() / uFullSize * 100.0))
+ .arg(UITranslator::formatSize((uint64_t)uFullSize * _1M));
+ }
+
+ /* Chipset type vs IO-APIC test: */
+ if (m_pEditorChipset->value() == KChipsetType_ICH9 && !m_pEditorMotherboardFeatures->isEnabledIoApic())
+ {
+ message.second << tr(
+ "The I/O APIC feature is not currently enabled in the Motherboard section of the System page. "
+ "This is needed to support a chipset of type ICH9. "
+ "It will be enabled automatically if you confirm your changes.");
+ }
+
+ /* HID vs USB test: */
+ if (isHIDEnabled() && !m_fIsUSBEnabled)
+ {
+ message.second << tr(
+ "The USB controller emulation is not currently enabled on the USB page. "
+ "This is needed to support an emulated USB pointing device. "
+ "It will be enabled automatically if you confirm your changes.");
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+ }
+
+ /* CPU tab: */
+ {
+ /* Prepare message: */
+ UIValidationMessage message;
+ message.first = UITranslator::removeAccelMark(m_pTabWidget->tabText(1));
+
+ /* VCPU amount test: */
+ const int cTotalCPUs = uiCommon().host().GetProcessorOnlineCoreCount();
+ if (m_pEditorVCPU->value() > 2 * cTotalCPUs)
+ {
+ message.second << tr(
+ "For performance reasons, the number of virtual CPUs attached to the virtual machine may not be more than twice the number "
+ "of physical CPUs on the host (<b>%1</b>). Please reduce the number of virtual CPUs.")
+ .arg(cTotalCPUs);
+ fPass = false;
+ }
+ else if (m_pEditorVCPU->value() > cTotalCPUs)
+ {
+ message.second << tr(
+ "More virtual CPUs are assigned to the virtual machine than the number of physical CPUs on the host system (<b>%1</b>). "
+ "This is likely to degrade the performance of your virtual machine. Please consider reducing the number of virtual CPUs.")
+ .arg(cTotalCPUs);
+ }
+
+ /* VCPU vs IO-APIC test: */
+ if (m_pEditorVCPU->value() > 1 && !m_pEditorMotherboardFeatures->isEnabledIoApic())
+ {
+ message.second << tr(
+ "The I/O APIC feature is not currently enabled in the Motherboard section of the System page. "
+ "This is needed to support more than one virtual processor. "
+ "It will be enabled automatically if you confirm your changes.");
+ }
+
+ /* CPU execution cap test: */
+ if (m_pEditorExecCap->value() < m_pEditorExecCap->medExecCap())
+ {
+ message.second << tr("The processor execution cap is set to a low value. This may make the machine feel slow to respond.");
+ }
+
+ /* Warn user about possible performance degradation and suggest lowering # of CPUs assigned to the VM instead: */
+ if (m_pEditorExecCap->value() < 100)
+ {
+ if (m_pEditorVCPU->maxVCPUCount() > 1 && m_pEditorVCPU->value() > 1)
+ {
+ message.second << tr("Please consider lowering the number of CPUs assigned to the virtual machine rather "
+ "than setting the processor execution cap.");
+ }
+ else if (m_pEditorVCPU->maxVCPUCount() > 1)
+ {
+ message.second << tr("Lowering the processor execution cap may result in a decline in performance.");
+ }
+ }
+
+ /* Nested HW Virt Ex: */
+ if (isNestedHWVirtExEnabled())
+ {
+ /* Nested Paging test: */
+ if (isHWVirtExSupported() && isNestedPagingSupported() && !isNestedPagingEnabled())
+ {
+ message.second << tr(
+ "The nested paging is not currently enabled in the Acceleration section of the System page. "
+ "This is needed to support nested hardware virtualization. "
+ "It will be enabled automatically if you confirm your changes.");
+ }
+ }
+
+ /* Serialize message: */
+ if (!message.second.isEmpty())
+ messages << message;
+ }
+
+ /* Return result: */
+ return fPass;
+}
+
+void UIMachineSettingsSystem::setOrderAfter(QWidget *pWidget)
+{
+ /* Configure navigation for 'motherboard' tab: */
+ setTabOrder(pWidget, m_pTabWidget->focusProxy());
+ setTabOrder(m_pTabWidget->focusProxy(), m_pEditorBaseMemory);
+ setTabOrder(m_pEditorBaseMemory, m_pEditorBootOrder);
+ setTabOrder(m_pEditorBootOrder, m_pEditorChipset);
+ setTabOrder(m_pEditorChipset, m_pEditorTpm);
+ setTabOrder(m_pEditorTpm, m_pEditorPointingHID);
+ setTabOrder(m_pEditorPointingHID, m_pEditorMotherboardFeatures);
+ setTabOrder(m_pEditorMotherboardFeatures, m_pEditorVCPU);
+
+ /* Configure navigation for 'processor' tab: */
+ setTabOrder(m_pEditorVCPU, m_pEditorExecCap);
+ setTabOrder(m_pEditorExecCap, m_pEditorProcessorFeatures);
+ setTabOrder(m_pEditorProcessorFeatures, m_pEditorParavirtProvider);
+
+ /* Configure navigation for 'acceleration' tab: */
+ setTabOrder(m_pEditorParavirtProvider, m_pEditorAccelerationFeatures);
+}
+
+void UIMachineSettingsSystem::retranslateUi()
+{
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabMotherboard), tr("&Motherboard"));
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabProcessor), tr("&Processor"));
+ m_pTabWidget->setTabText(m_pTabWidget->indexOf(m_pTabAcceleration), tr("Acce&leration"));
+
+ /* These editors have own labels, but we want them to be properly layouted according to each other: */
+ int iMinimumLayoutHint = 0;
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorBaseMemory->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorBootOrder->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorChipset->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorTpm->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorPointingHID->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorMotherboardFeatures->minimumLabelHorizontalHint());
+ m_pEditorBaseMemory->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorBootOrder->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorChipset->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorTpm->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorPointingHID->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorMotherboardFeatures->setMinimumLayoutIndent(iMinimumLayoutHint);
+ iMinimumLayoutHint = 0;
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorVCPU->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorExecCap->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorProcessorFeatures->minimumLabelHorizontalHint());
+ m_pEditorVCPU->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorExecCap->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorProcessorFeatures->setMinimumLayoutIndent(iMinimumLayoutHint);
+ iMinimumLayoutHint = 0;
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorParavirtProvider->minimumLabelHorizontalHint());
+ iMinimumLayoutHint = qMax(iMinimumLayoutHint, m_pEditorAccelerationFeatures->minimumLabelHorizontalHint());
+ m_pEditorParavirtProvider->setMinimumLayoutIndent(iMinimumLayoutHint);
+ m_pEditorAccelerationFeatures->setMinimumLayoutIndent(iMinimumLayoutHint);
+}
+
+void UIMachineSettingsSystem::polishPage()
+{
+ /* Get old data from cache: */
+ const UIDataSettingsMachineSystem &systemData = m_pCache->base();
+
+ /* Polish 'Motherboard' availability: */
+ m_pEditorBaseMemory->setEnabled(isMachineOffline());
+ m_pEditorBootOrder->setEnabled(isMachineOffline());
+ m_pEditorChipset->setEnabled(isMachineOffline());
+ m_pEditorTpm->setEnabled(isMachineOffline());
+ m_pEditorPointingHID->setEnabled(isMachineOffline());
+ m_pEditorMotherboardFeatures->setEnabled(isMachineOffline());
+
+ /* Polish 'Processor' availability: */
+ m_pEditorVCPU->setEnabled(isMachineOffline() && systemData.m_fSupportedHwVirtEx);
+ m_pEditorExecCap->setEnabled(isMachineInValidMode());
+ m_pEditorProcessorFeatures->setEnablePaeAvailable(isMachineOffline() && systemData.m_fSupportedPAE);
+ m_pEditorProcessorFeatures->setEnableNestedVirtualizationAvailable( isMachineOffline()
+ && ( systemData.m_fSupportedNestedHwVirtEx
+ || systemData.m_fEnabledNestedHwVirtEx));
+
+ /* Polish 'Acceleration' availability: */
+ m_pEditorParavirtProvider->setEnabled(isMachineOffline());
+ m_pEditorAccelerationFeatures->setEnabled(isMachineOffline());
+ m_pEditorAccelerationFeatures->setEnableNestedPagingAvailable( (systemData.m_fSupportedNestedPaging && isMachineOffline())
+ || (systemData.m_fEnabledNestedPaging && isMachineOffline()));
+}
+
+void UIMachineSettingsSystem::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheMachineSystem;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsSystem::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare tab-widget: */
+ m_pTabWidget = new QITabWidget(this);
+ if (m_pTabWidget)
+ {
+ /* Prepare each tab separately: */
+ prepareTabMotherboard();
+ prepareTabProcessor();
+ prepareTabAcceleration();
+
+ pLayoutMain->addWidget(m_pTabWidget);
+ }
+ }
+}
+
+void UIMachineSettingsSystem::prepareTabMotherboard()
+{
+ /* Prepare 'Motherboard' tab: */
+ m_pTabMotherboard = new QWidget;
+ if (m_pTabMotherboard)
+ {
+ /* Prepare 'Motherboard' tab layout: */
+ QGridLayout *pLayoutMotherboard = new QGridLayout(m_pTabMotherboard);
+ if (pLayoutMotherboard)
+ {
+ pLayoutMotherboard->setColumnStretch(1, 1);
+ pLayoutMotherboard->setRowStretch(6, 1);
+
+ /* Prepare base memory editor: */
+ m_pEditorBaseMemory = new UIBaseMemoryEditor(m_pTabMotherboard);
+ if (m_pEditorBaseMemory)
+ pLayoutMotherboard->addWidget(m_pEditorBaseMemory, 0, 0, 1, 2);
+
+ /* Prepare boot order editor: */
+ m_pEditorBootOrder = new UIBootOrderEditor(m_pTabMotherboard);
+ if (m_pEditorBootOrder)
+ pLayoutMotherboard->addWidget(m_pEditorBootOrder, 1, 0);
+
+ /* Prepare chipset editor: */
+ m_pEditorChipset = new UIChipsetEditor(m_pTabMotherboard);
+ if (m_pEditorChipset)
+ pLayoutMotherboard->addWidget(m_pEditorChipset, 2, 0);
+
+ /* Prepare TPM editor: */
+ m_pEditorTpm = new UITpmEditor(m_pTabMotherboard);
+ if (m_pEditorTpm)
+ pLayoutMotherboard->addWidget(m_pEditorTpm, 3, 0);
+
+ /* Prepare pointing HID editor: */
+ m_pEditorPointingHID = new UIPointingHIDEditor(m_pTabMotherboard);
+ if (m_pEditorPointingHID)
+ pLayoutMotherboard->addWidget(m_pEditorPointingHID, 4, 0);
+
+ /* Prepare motherboard features editor: */
+ m_pEditorMotherboardFeatures = new UIMotherboardFeaturesEditor(m_pTabMotherboard);
+ if (m_pEditorMotherboardFeatures)
+ pLayoutMotherboard->addWidget(m_pEditorMotherboardFeatures, 5, 0);
+ }
+
+ m_pTabWidget->addTab(m_pTabMotherboard, QString());
+ }
+}
+
+void UIMachineSettingsSystem::prepareTabProcessor()
+{
+ /* Prepare 'Processor' tab: */
+ m_pTabProcessor = new QWidget;
+ if (m_pTabProcessor)
+ {
+ /* Prepare 'Processor' tab layout: */
+ QGridLayout *pLayoutProcessor = new QGridLayout(m_pTabProcessor);
+ if (pLayoutProcessor)
+ {
+ pLayoutProcessor->setColumnStretch(1, 1);
+ pLayoutProcessor->setRowStretch(3, 1);
+
+ /* Prepare VCPU editor : */
+ m_pEditorVCPU = new UIVirtualCPUEditor(m_pTabProcessor);
+ if (m_pEditorVCPU)
+ pLayoutProcessor->addWidget(m_pEditorVCPU, 0, 0, 1, 2);
+
+ /* Prepare exec cap editor : */
+ m_pEditorExecCap = new UIExecutionCapEditor(m_pTabProcessor);
+ if (m_pEditorExecCap)
+ pLayoutProcessor->addWidget(m_pEditorExecCap, 1, 0, 1, 2);
+
+ /* Prepare processor features editor: */
+ m_pEditorProcessorFeatures = new UIProcessorFeaturesEditor(m_pTabProcessor);
+ if (m_pEditorProcessorFeatures)
+ pLayoutProcessor->addWidget(m_pEditorProcessorFeatures, 2, 0);
+ }
+
+ m_pTabWidget->addTab(m_pTabProcessor, QString());
+ }
+}
+
+void UIMachineSettingsSystem::prepareTabAcceleration()
+{
+ /* Prepare 'Acceleration' tab: */
+ m_pTabAcceleration = new QWidget;
+ if (m_pTabAcceleration)
+ {
+ /* Prepare 'Acceleration' tab layout: */
+ QGridLayout *pLayoutAcceleration = new QGridLayout(m_pTabAcceleration);
+ if (pLayoutAcceleration)
+ {
+ pLayoutAcceleration->setColumnStretch(2, 1);
+ pLayoutAcceleration->setRowStretch(3, 1);
+
+ /* Prepare paravirtualization provider editor: */
+ m_pEditorParavirtProvider = new UIParavirtProviderEditor(m_pTabAcceleration);
+ if (m_pEditorParavirtProvider)
+ pLayoutAcceleration->addWidget(m_pEditorParavirtProvider, 0, 0, 1, 2);
+
+ /* Prepare acceleration features editor: */
+ m_pEditorAccelerationFeatures = new UIAccelerationFeaturesEditor(m_pTabAcceleration);
+ if (m_pEditorAccelerationFeatures)
+ pLayoutAcceleration->addWidget(m_pEditorAccelerationFeatures, 1, 0);
+
+ m_pTabWidget->addTab(m_pTabAcceleration, QString());
+ }
+ }
+}
+
+void UIMachineSettingsSystem::prepareConnections()
+{
+ /* Configure 'Motherboard' connections: */
+ connect(m_pEditorChipset, &UIChipsetEditor::sigValueChanged,
+ this, &UIMachineSettingsSystem::revalidate);
+ connect(m_pEditorTpm, &UITpmEditor::sigValueChanged,
+ this, &UIMachineSettingsSystem::revalidate);
+ connect(m_pEditorPointingHID, &UIPointingHIDEditor::sigValueChanged,
+ this, &UIMachineSettingsSystem::revalidate);
+ connect(m_pEditorBaseMemory, &UIBaseMemoryEditor::sigValidChanged,
+ this, &UIMachineSettingsSystem::revalidate);
+ connect(m_pEditorMotherboardFeatures, &UIMotherboardFeaturesEditor::sigChangedIoApic,
+ this, &UIMachineSettingsSystem::revalidate);
+
+ /* Configure 'Processor' connections: */
+ connect(m_pEditorVCPU, &UIVirtualCPUEditor::sigValueChanged,
+ this, &UIMachineSettingsSystem::revalidate);
+ connect(m_pEditorExecCap, &UIExecutionCapEditor::sigValueChanged,
+ this, &UIMachineSettingsSystem::revalidate);
+ connect(m_pEditorProcessorFeatures, &UIProcessorFeaturesEditor::sigChangedNestedVirtualization,
+ this, &UIMachineSettingsSystem::revalidate);
+
+ /* Configure 'Acceleration' connections: */
+ connect(m_pEditorAccelerationFeatures, &UIAccelerationFeaturesEditor::sigChangedNestedPaging,
+ this, &UIMachineSettingsSystem::revalidate);
+}
+
+void UIMachineSettingsSystem::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIMachineSettingsSystem::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save general settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* Save 'Motherboard' data from cache: */
+ if (fSuccess)
+ fSuccess = saveMotherboardData();
+ /* Save 'Processor' data from cache: */
+ if (fSuccess)
+ fSuccess = saveProcessorData();
+ /* Save 'Acceleration' data from cache: */
+ if (fSuccess)
+ fSuccess = saveAccelerationData();
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsSystem::saveMotherboardData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Motherboard' settings from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineSystem &oldSystemData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineSystem &newSystemData = m_pCache->data();
+
+ /* Save memory size: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_iMemorySize != oldSystemData.m_iMemorySize)
+ {
+ m_machine.SetMemorySize(newSystemData.m_iMemorySize);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save boot items: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_bootItems != oldSystemData.m_bootItems)
+ {
+ saveBootItems(newSystemData.m_bootItems, m_machine);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save chipset type: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_chipsetType != oldSystemData.m_chipsetType)
+ {
+ m_machine.SetChipsetType(newSystemData.m_chipsetType);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save TPM type: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_tpmType != oldSystemData.m_tpmType)
+ {
+ CTrustedPlatformModule comModule = m_machine.GetTrustedPlatformModule();
+ comModule.SetType(newSystemData.m_tpmType);
+ fSuccess = comModule.isOk();
+ /// @todo convey error info ..
+ }
+ /* Save pointing HID type: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_pointingHIDType != oldSystemData.m_pointingHIDType)
+ {
+ m_machine.SetPointingHIDType(newSystemData.m_pointingHIDType);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save whether IO APIC is enabled: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_fEnabledIoApic != oldSystemData.m_fEnabledIoApic)
+ {
+ m_machine.GetBIOSSettings().SetIOAPICEnabled(newSystemData.m_fEnabledIoApic);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save firware type (whether EFI is enabled): */
+ if (fSuccess && isMachineOffline() && newSystemData.m_fEnabledEFI != oldSystemData.m_fEnabledEFI)
+ {
+ m_machine.SetFirmwareType(newSystemData.m_fEnabledEFI ? KFirmwareType_EFI : KFirmwareType_BIOS);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save whether UTC is enabled: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_fEnabledUTC != oldSystemData.m_fEnabledUTC)
+ {
+ m_machine.SetRTCUseUTC(newSystemData.m_fEnabledUTC);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save whether secure boot is enabled: */
+ if ( fSuccess && isMachineOffline()
+ && ( newSystemData.m_fEnabledSecureBoot != oldSystemData.m_fEnabledSecureBoot
+ || newSystemData.m_fResetSecureBoot != oldSystemData.m_fResetSecureBoot))
+ {
+ CNvramStore comStoreLvl1 = m_machine.GetNonVolatileStore();
+ CUefiVariableStore comStoreLvl2 = comStoreLvl1.GetUefiVariableStore();
+
+ /* Enabling secure boot? */
+ if ( newSystemData.m_fEnabledSecureBoot
+ && newSystemData.m_fEnabledEFI)
+ {
+ /* Secure boot was NOT available
+ * or requested to be reseted: */
+ if ( !newSystemData.m_fAvailableSecureBoot
+ || newSystemData.m_fResetSecureBoot)
+ {
+ /* Init if required: */
+ if (!newSystemData.m_fAvailableSecureBoot)
+ comStoreLvl1.InitUefiVariableStore(0);
+ /* Enroll everything: */
+ comStoreLvl2 = comStoreLvl1.GetUefiVariableStore();
+ comStoreLvl2.EnrollOraclePlatformKey();
+ comStoreLvl2.EnrollDefaultMsSignatures();
+ }
+ comStoreLvl2.SetSecureBootEnabled(true);
+ fSuccess = comStoreLvl2.isOk();
+ /// @todo convey error info ..
+ }
+ /* Disabling secure boot? */
+ else if (!newSystemData.m_fEnabledSecureBoot)
+ {
+ comStoreLvl2.SetSecureBootEnabled(false);
+ fSuccess = comStoreLvl2.isOk();
+ /// @todo convey error info ..
+ }
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsSystem::saveProcessorData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Processor' settings from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineSystem &oldSystemData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineSystem &newSystemData = m_pCache->data();
+
+ /* Save CPU count: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_cCPUCount != oldSystemData.m_cCPUCount)
+ {
+ m_machine.SetCPUCount(newSystemData.m_cCPUCount);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save whether PAE is enabled: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_fEnabledPAE != oldSystemData.m_fEnabledPAE)
+ {
+ m_machine.SetCPUProperty(KCPUPropertyType_PAE, newSystemData.m_fEnabledPAE);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save whether Nested HW Virt Ex is enabled: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_fEnabledNestedHwVirtEx != oldSystemData.m_fEnabledNestedHwVirtEx)
+ {
+ m_machine.SetCPUProperty(KCPUPropertyType_HWVirt, newSystemData.m_fEnabledNestedHwVirtEx);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save CPU execution cap: */
+ if (fSuccess && newSystemData.m_iCPUExecCap != oldSystemData.m_iCPUExecCap)
+ {
+ m_machine.SetCPUExecutionCap(newSystemData.m_iCPUExecCap);
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsSystem::saveAccelerationData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save 'Acceleration' settings from cache: */
+ if (fSuccess)
+ {
+ /* Get old data from cache: */
+ const UIDataSettingsMachineSystem &oldSystemData = m_pCache->base();
+ /* Get new data from cache: */
+ const UIDataSettingsMachineSystem &newSystemData = m_pCache->data();
+
+ /* Save paravirtualization provider: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_paravirtProvider != oldSystemData.m_paravirtProvider)
+ {
+ m_machine.SetParavirtProvider(newSystemData.m_paravirtProvider);
+ fSuccess = m_machine.isOk();
+ }
+ /* Save whether the nested paging is enabled: */
+ if (fSuccess && isMachineOffline() && newSystemData.m_fEnabledNestedPaging != oldSystemData.m_fEnabledNestedPaging)
+ {
+ m_machine.SetHWVirtExProperty(KHWVirtExPropertyType_NestedPaging, newSystemData.m_fEnabledNestedPaging);
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSystem.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSystem.h
new file mode 100644
index 00000000..c7b7cb17
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsSystem.h
@@ -0,0 +1,188 @@
+/* $Id: UIMachineSettingsSystem.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsSystem class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsSystem_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsSystem_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+struct UIDataSettingsMachineSystem;
+typedef UISettingsCache<UIDataSettingsMachineSystem> UISettingsCacheMachineSystem;
+class QITabWidget;
+class UIAccelerationFeaturesEditor;
+class UIBaseMemoryEditor;
+class UIBootOrderEditor;
+class UIChipsetEditor;
+class UIExecutionCapEditor;
+class UIMotherboardFeaturesEditor;
+class UIParavirtProviderEditor;
+class UIPointingHIDEditor;
+class UIProcessorFeaturesEditor;
+class UITpmEditor;
+class UIVirtualCPUEditor;
+
+/** Machine settings: System page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsSystem : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs System settings page. */
+ UIMachineSettingsSystem();
+ /** Destructs System settings page. */
+ virtual ~UIMachineSettingsSystem() RT_OVERRIDE;
+
+ /** Returns whether the HW Virt Ex is supported. */
+ bool isHWVirtExSupported() const;
+
+ /** Returns whether the Nested Paging is supported. */
+ bool isNestedPagingSupported() const;
+ /** Returns whether the Nested Paging is enabled. */
+ bool isNestedPagingEnabled() const;
+
+ /** Returns whether the Nested HW Virt Ex is supported. */
+ bool isNestedHWVirtExSupported() const;
+ /** Returns whether the Nested HW Virt Ex is enabled. */
+ bool isNestedHWVirtExEnabled() const;
+
+ /** Returns whether the HID is enabled. */
+ bool isHIDEnabled() const;
+
+ /** Returns the chipset type. */
+ KChipsetType chipsetType() const;
+
+ /** Defines whether the USB is enabled. */
+ void setUSBEnabled(bool fEnabled);
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ virtual bool validate(QList<UIValidationMessage> &messages) RT_OVERRIDE;
+
+ /** Defines TAB order for passed @a pWidget. */
+ virtual void setOrderAfter(QWidget *pWidget) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares 'Motherboard' tab. */
+ void prepareTabMotherboard();
+ /** Prepares 'Processor' tab. */
+ void prepareTabProcessor();
+ /** Prepares 'Acceleration' tab. */
+ void prepareTabAcceleration();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+ /** Saves existing 'Motherboard' data from cache. */
+ bool saveMotherboardData();
+ /** Saves existing 'Processor' data from cache. */
+ bool saveProcessorData();
+ /** Saves existing 'Acceleration' data from cache. */
+ bool saveAccelerationData();
+
+ /** Holds whether the USB is enabled. */
+ bool m_fIsUSBEnabled;
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheMachineSystem *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the tab-widget instance. */
+ QITabWidget *m_pTabWidget;
+
+ /** Holds the 'Motherboard' tab instance. */
+ QWidget *m_pTabMotherboard;
+ /** Holds the base memory editor instance. */
+ UIBaseMemoryEditor *m_pEditorBaseMemory;
+ /** Holds the boot order editor instance. */
+ UIBootOrderEditor *m_pEditorBootOrder;
+ /** Holds the chipset editor instance. */
+ UIChipsetEditor *m_pEditorChipset;
+ /** Holds the TPM editor instance. */
+ UITpmEditor *m_pEditorTpm;
+ /** Holds the pointing HID editor instance. */
+ UIPointingHIDEditor *m_pEditorPointingHID;
+ /** Holds the motherboard features editor instance. */
+ UIMotherboardFeaturesEditor *m_pEditorMotherboardFeatures;
+
+ /** Holds the 'Processor' tab instance. */
+ QWidget *m_pTabProcessor;
+ /** Holds the VCPU editor instance. */
+ UIVirtualCPUEditor *m_pEditorVCPU;
+ /** Holds the exec cap editor instance. */
+ UIExecutionCapEditor *m_pEditorExecCap;
+ /** Holds the motherboard features editor instance. */
+ UIProcessorFeaturesEditor *m_pEditorProcessorFeatures;
+
+ /** Holds the 'Acceleration' tab instance. */
+ QWidget *m_pTabAcceleration;
+ /** Holds the paravirtualization provider editor instance. */
+ UIParavirtProviderEditor *m_pEditorParavirtProvider;
+ /** Holds the acceleration features editor instance. */
+ UIAccelerationFeaturesEditor *m_pEditorAccelerationFeatures;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsSystem_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsUSB.cpp b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsUSB.cpp
new file mode 100644
index 00000000..6eba6e0c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsUSB.cpp
@@ -0,0 +1,729 @@
+/* $Id: UIMachineSettingsUSB.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsUSB class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QComboBox>
+#include <QHeaderView>
+#include <QHelpEvent>
+#include <QLineEdit>
+#include <QMenu>
+#include <QRadioButton>
+#include <QRegExp>
+#include <QSpacerItem>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILabelSeparator.h"
+#include "QITreeWidget.h"
+#include "QIWidgetValidator.h"
+#include "UIConverter.h"
+#include "UIIconPool.h"
+#include "UIMachineSettingsUSB.h"
+#include "UIErrorString.h"
+#include "QIToolBar.h"
+#include "UICommon.h"
+#include "UIUSBFiltersEditor.h"
+#include "UIUSBSettingsEditor.h"
+
+/* COM includes: */
+#include "CExtPack.h"
+#include "CExtPackManager.h"
+#include "CUSBController.h"
+#include "CUSBDeviceFilter.h"
+#include "CUSBDeviceFilters.h"
+
+
+/** Machine settings: USB filter data structure. */
+struct UIDataSettingsMachineUSBFilter
+{
+ /** Constructs data. */
+ UIDataSettingsMachineUSBFilter() {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineUSBFilter &other) const
+ {
+ return true
+ && (m_guiData == other.m_guiData)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineUSBFilter &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineUSBFilter &other) const { return !equal(other); }
+
+ /** Holds the USB filter data. */
+ UIDataUSBFilter m_guiData;
+};
+
+
+/** Machine settings: USB page data structure. */
+struct UIDataSettingsMachineUSB
+{
+ /** Constructs data. */
+ UIDataSettingsMachineUSB()
+ : m_fUSBEnabled(false)
+ , m_enmUSBControllerType(KUSBControllerType_Null)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSettingsMachineUSB &other) const
+ {
+ return true
+ && (m_fUSBEnabled == other.m_fUSBEnabled)
+ && (m_enmUSBControllerType == other.m_enmUSBControllerType)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSettingsMachineUSB &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSettingsMachineUSB &other) const { return !equal(other); }
+
+ /** Holds whether the USB is enabled. */
+ bool m_fUSBEnabled;
+ /** Holds the USB controller type. */
+ KUSBControllerType m_enmUSBControllerType;
+};
+
+
+UIMachineSettingsUSB::UIMachineSettingsUSB()
+ : m_pCache(0)
+ , m_pEditorUsbSettings(0)
+{
+ prepare();
+}
+
+UIMachineSettingsUSB::~UIMachineSettingsUSB()
+{
+ cleanup();
+}
+
+bool UIMachineSettingsUSB::isUSBEnabled() const
+{
+ return m_pEditorUsbSettings->isFeatureEnabled();
+}
+
+bool UIMachineSettingsUSB::changed() const
+{
+ return m_pCache ? m_pCache->wasChanged() : false;
+}
+
+void UIMachineSettingsUSB::loadToCacheFrom(QVariant &data)
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Clear cache initially: */
+ m_pCache->clear();
+
+ /* Prepare old USB data: */
+ UIDataSettingsMachineUSB oldUsbData;
+
+ /* Gather old USB data: */
+ oldUsbData.m_fUSBEnabled = !m_machine.GetUSBControllers().isEmpty();
+ oldUsbData.m_enmUSBControllerType = m_machine.GetUSBControllerCountByType(KUSBControllerType_XHCI) > 0 ? KUSBControllerType_XHCI
+ : m_machine.GetUSBControllerCountByType(KUSBControllerType_EHCI) > 0 ? KUSBControllerType_EHCI
+ : m_machine.GetUSBControllerCountByType(KUSBControllerType_OHCI) > 0 ? KUSBControllerType_OHCI
+ : KUSBControllerType_Null;
+
+ /* Check whether controller is valid: */
+ const CUSBDeviceFilters &comFiltersObject = m_machine.GetUSBDeviceFilters();
+ if (!comFiltersObject.isNull())
+ {
+ /* For each filter: */
+ const CUSBDeviceFilterVector &filters = comFiltersObject.GetDeviceFilters();
+ for (int iFilterIndex = 0; iFilterIndex < filters.size(); ++iFilterIndex)
+ {
+ /* Prepare old data: */
+ UIDataSettingsMachineUSBFilter oldFilterData;
+
+ /* Check whether filter is valid: */
+ const CUSBDeviceFilter &filter = filters.at(iFilterIndex);
+ if (!filter.isNull())
+ {
+ /* Gather old data: */
+ oldFilterData.m_guiData.m_fActive = filter.GetActive();
+ oldFilterData.m_guiData.m_strName = filter.GetName();
+ oldFilterData.m_guiData.m_strVendorId = filter.GetVendorId();
+ oldFilterData.m_guiData.m_strProductId = filter.GetProductId();
+ oldFilterData.m_guiData.m_strRevision = filter.GetRevision();
+ oldFilterData.m_guiData.m_strManufacturer = filter.GetManufacturer();
+ oldFilterData.m_guiData.m_strProduct = filter.GetProduct();
+ oldFilterData.m_guiData.m_strSerialNumber = filter.GetSerialNumber();
+ oldFilterData.m_guiData.m_strPort = filter.GetPort();
+ const QString strRemote = filter.GetRemote();
+ UIRemoteMode enmRemoteMode = UIRemoteMode_Any;
+ if (strRemote == "true" || strRemote == "yes" || strRemote == "1")
+ enmRemoteMode = UIRemoteMode_On;
+ else if (strRemote == "false" || strRemote == "no" || strRemote == "0")
+ enmRemoteMode = UIRemoteMode_Off;
+ oldFilterData.m_guiData.m_enmRemoteMode = enmRemoteMode;
+ }
+
+ /* Cache old data: */
+ m_pCache->child(iFilterIndex).cacheInitialData(oldFilterData);
+ }
+ }
+
+ /* Cache old USB data: */
+ m_pCache->cacheInitialData(oldUsbData);
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+void UIMachineSettingsUSB::getFromCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Get old USB data from cache: */
+ const UIDataSettingsMachineUSB &oldUsbData = m_pCache->base();
+
+ /* Load old USB data from cache: */
+ if (m_pEditorUsbSettings)
+ {
+ m_pEditorUsbSettings->setFeatureEnabled(oldUsbData.m_fUSBEnabled);
+ m_pEditorUsbSettings->setUsbControllerType(oldUsbData.m_enmUSBControllerType);
+
+ /* For each filter => load it from cache: */
+ QList<UIDataUSBFilter> filters;
+ for (int iFilterIndex = 0; iFilterIndex < m_pCache->childCount(); ++iFilterIndex)
+ {
+ const UIDataSettingsMachineUSBFilter &oldUsbFilterData = m_pCache->child(iFilterIndex).base();
+ UIDataUSBFilter filter;
+ filter.m_fActive = oldUsbFilterData.m_guiData.m_fActive;
+ filter.m_strName = oldUsbFilterData.m_guiData.m_strName;
+ filter.m_strVendorId = oldUsbFilterData.m_guiData.m_strVendorId;
+ filter.m_strProductId = oldUsbFilterData.m_guiData.m_strProductId;
+ filter.m_strRevision = oldUsbFilterData.m_guiData.m_strRevision;
+ filter.m_strManufacturer = oldUsbFilterData.m_guiData.m_strManufacturer;
+ filter.m_strProduct = oldUsbFilterData.m_guiData.m_strProduct;
+ filter.m_strSerialNumber = oldUsbFilterData.m_guiData.m_strSerialNumber;
+ filter.m_strPort = oldUsbFilterData.m_guiData.m_strPort;
+ filter.m_enmRemoteMode = oldUsbFilterData.m_guiData.m_enmRemoteMode;
+ filters << filter;
+ }
+ m_pEditorUsbSettings->setUsbFilters(filters);
+ }
+
+ /* Polish page finally: */
+ polishPage();
+
+ /* Revalidate: */
+ revalidate();
+}
+
+void UIMachineSettingsUSB::putToCache()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return;
+
+ /* Prepare new USB data: */
+ UIDataSettingsMachineUSB newUsbData;
+
+ /* Gather new USB data: */
+ if (m_pEditorUsbSettings)
+ {
+ newUsbData.m_fUSBEnabled = m_pEditorUsbSettings->isFeatureEnabled();
+ newUsbData.m_enmUSBControllerType = newUsbData.m_fUSBEnabled
+ ? m_pEditorUsbSettings->usbControllerType()
+ : KUSBControllerType_Null;
+ /* For each filter => save it to cache: */
+ const QList<UIDataUSBFilter> filters = m_pEditorUsbSettings->usbFilters();
+ for (int iFilterIndex = 0; iFilterIndex < filters.size(); ++iFilterIndex)
+ {
+ /* Gather and cache new data: */
+ UIDataUSBFilter filter = filters.at(iFilterIndex);
+ UIDataSettingsMachineUSBFilter newUsbFilterData;
+ newUsbFilterData.m_guiData.m_fActive = filter.m_fActive;
+ newUsbFilterData.m_guiData.m_strName = filter.m_strName;
+ newUsbFilterData.m_guiData.m_strVendorId = filter.m_strVendorId;
+ newUsbFilterData.m_guiData.m_strProductId = filter.m_strProductId;
+ newUsbFilterData.m_guiData.m_strRevision = filter.m_strRevision;
+ newUsbFilterData.m_guiData.m_strManufacturer = filter.m_strManufacturer;
+ newUsbFilterData.m_guiData.m_strProduct = filter.m_strProduct;
+ newUsbFilterData.m_guiData.m_strSerialNumber = filter.m_strSerialNumber;
+ newUsbFilterData.m_guiData.m_strPort = filter.m_strPort;
+ newUsbFilterData.m_guiData.m_enmRemoteMode = filter.m_enmRemoteMode;
+ m_pCache->child(iFilterIndex).cacheCurrentData(newUsbFilterData);
+ }
+ }
+
+ /* Cache new USB data: */
+ m_pCache->cacheCurrentData(newUsbData);
+}
+
+void UIMachineSettingsUSB::saveFromCacheTo(QVariant &data)
+{
+ /* Fetch data to machine: */
+ UISettingsPageMachine::fetchData(data);
+
+ /* Update data and failing state: */
+ setFailed(!saveData());
+
+ /* Upload machine to data: */
+ UISettingsPageMachine::uploadData(data);
+}
+
+bool UIMachineSettingsUSB::validate(QList<UIValidationMessage> &messages)
+{
+ Q_UNUSED(messages);
+
+ /* Pass by default: */
+ bool fPass = true;
+
+ /* Return result: */
+ return fPass;
+}
+
+void UIMachineSettingsUSB::setOrderAfter(QWidget *pWidget)
+{
+ setTabOrder(pWidget, m_pEditorUsbSettings);
+}
+
+void UIMachineSettingsUSB::retranslateUi()
+{
+}
+
+void UIMachineSettingsUSB::polishPage()
+{
+ /* Polish USB page availability: */
+ m_pEditorUsbSettings->setFeatureAvailable(isMachineOffline());
+ m_pEditorUsbSettings->setUsbControllerOptionAvailable(isMachineOffline());
+ m_pEditorUsbSettings->setUsbFiltersOptionAvailable(isMachineInValidMode());
+}
+
+void UIMachineSettingsUSB::prepare()
+{
+ /* Prepare cache: */
+ m_pCache = new UISettingsCacheMachineUSB;
+ AssertPtrReturnVoid(m_pCache);
+
+ /* Prepare everything: */
+ prepareWidgets();
+ prepareConnections();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIMachineSettingsUSB::prepareWidgets()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Prepare settings editor: */
+ m_pEditorUsbSettings = new UIUSBSettingsEditor(this);
+ if (m_pEditorUsbSettings)
+ pLayout->addWidget(m_pEditorUsbSettings);
+ }
+}
+
+void UIMachineSettingsUSB::prepareConnections()
+{
+ /* Configure validation connections: */
+ connect(m_pEditorUsbSettings, &UIUSBSettingsEditor::sigValueChanged,
+ this, &UIMachineSettingsUSB::revalidate);
+}
+
+void UIMachineSettingsUSB::cleanup()
+{
+ /* Cleanup cache: */
+ delete m_pCache;
+ m_pCache = 0;
+}
+
+bool UIMachineSettingsUSB::saveData()
+{
+ /* Sanity check: */
+ if (!m_pCache)
+ return false;
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Save USB settings from cache: */
+ if (fSuccess && isMachineInValidMode() && m_pCache->wasChanged())
+ {
+ /* Get new USB data from cache: */
+ const UIDataSettingsMachineUSB &newUsbData = m_pCache->data();
+
+ /* Save USB data: */
+ if (fSuccess && isMachineOffline())
+ {
+ /* Remove USB controllers: */
+ if (!newUsbData.m_fUSBEnabled)
+ fSuccess = removeUSBControllers();
+
+ else
+
+ /* Create/update USB controllers: */
+ if (newUsbData.m_fUSBEnabled)
+ fSuccess = createUSBControllers(newUsbData.m_enmUSBControllerType);
+ }
+
+ /* Save USB filters data: */
+ if (fSuccess)
+ {
+ /* Make sure filters object really exists: */
+ CUSBDeviceFilters comFiltersObject = m_machine.GetUSBDeviceFilters();
+ fSuccess = m_machine.isOk() && comFiltersObject.isNotNull();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ else
+ {
+ /* For each filter data set: */
+ int iOperationPosition = 0;
+ for (int iFilterIndex = 0; fSuccess && iFilterIndex < m_pCache->childCount(); ++iFilterIndex)
+ {
+ /* Check if USB filter data was changed: */
+ const UISettingsCacheMachineUSBFilter &filterCache = m_pCache->child(iFilterIndex);
+
+ /* Remove filter marked for 'remove' or 'update': */
+ if (fSuccess && (filterCache.wasRemoved() || filterCache.wasUpdated()))
+ {
+ fSuccess = removeUSBFilter(comFiltersObject, iOperationPosition);
+ if (fSuccess && filterCache.wasRemoved())
+ --iOperationPosition;
+ }
+
+ /* Create filter marked for 'create' or 'update': */
+ if (fSuccess && (filterCache.wasCreated() || filterCache.wasUpdated()))
+ fSuccess = createUSBFilter(comFiltersObject, iOperationPosition, filterCache.data());
+
+ /* Advance operation position: */
+ ++iOperationPosition;
+ }
+ }
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsUSB::removeUSBControllers(const QSet<KUSBControllerType> &types /* = QSet<KUSBControllerType>() */)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Remove controllers: */
+ if (fSuccess && isMachineOffline())
+ {
+ /* Get controllers for further activities: */
+ const CUSBControllerVector &controllers = m_machine.GetUSBControllers();
+ fSuccess = m_machine.isOk();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+
+ /* For each controller: */
+ for (int iControllerIndex = 0; fSuccess && iControllerIndex < controllers.size(); ++iControllerIndex)
+ {
+ /* Get current controller: */
+ const CUSBController &comController = controllers.at(iControllerIndex);
+
+ /* Get controller type for further activities: */
+ KUSBControllerType enmType = KUSBControllerType_Null;
+ if (fSuccess)
+ {
+ enmType = comController.GetType();
+ fSuccess = comController.isOk();
+ }
+ /* Get controller name for further activities: */
+ QString strName;
+ if (fSuccess)
+ {
+ strName = comController.GetName();
+ fSuccess = comController.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comController));
+ else
+ {
+ /* Pass only if requested types were not defined or contains the one we found: */
+ if (!types.isEmpty() && !types.contains(enmType))
+ continue;
+
+ /* Remove controller: */
+ if (fSuccess)
+ {
+ m_machine.RemoveUSBController(comController.GetName());
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsUSB::createUSBControllers(KUSBControllerType enmType)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Add controllers: */
+ if (fSuccess && isMachineOffline())
+ {
+ /* Get each controller count for further activities: */
+ ULONG cOhciCtls = 0;
+ if (fSuccess)
+ {
+ cOhciCtls = m_machine.GetUSBControllerCountByType(KUSBControllerType_OHCI);
+ fSuccess = m_machine.isOk();
+ }
+ ULONG cEhciCtls = 0;
+ if (fSuccess)
+ {
+ cEhciCtls = m_machine.GetUSBControllerCountByType(KUSBControllerType_EHCI);
+ fSuccess = m_machine.isOk();
+ }
+ ULONG cXhciCtls = 0;
+ if (fSuccess)
+ {
+ cXhciCtls = m_machine.GetUSBControllerCountByType(KUSBControllerType_XHCI);
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ else
+ {
+ /* For requested controller type: */
+ switch (enmType)
+ {
+ case KUSBControllerType_OHCI:
+ {
+ /* Remove excessive controllers: */
+ if (cXhciCtls || cEhciCtls)
+ fSuccess = removeUSBControllers(QSet<KUSBControllerType>()
+ << KUSBControllerType_XHCI
+ << KUSBControllerType_EHCI);
+
+ /* Add required controller: */
+ if (fSuccess && !cOhciCtls)
+ {
+ m_machine.AddUSBController("OHCI", KUSBControllerType_OHCI);
+ fSuccess = m_machine.isOk();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+
+ break;
+ }
+ case KUSBControllerType_EHCI:
+ {
+ /* Remove excessive controllers: */
+ if (cXhciCtls)
+ fSuccess = removeUSBControllers(QSet<KUSBControllerType>()
+ << KUSBControllerType_XHCI);
+
+ /* Add required controllers: */
+ if (fSuccess)
+ {
+ if (fSuccess && !cOhciCtls)
+ {
+ m_machine.AddUSBController("OHCI", KUSBControllerType_OHCI);
+ fSuccess = m_machine.isOk();
+ }
+ if (fSuccess && !cEhciCtls)
+ {
+ m_machine.AddUSBController("EHCI", KUSBControllerType_EHCI);
+ fSuccess = m_machine.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+
+ break;
+ }
+ case KUSBControllerType_XHCI:
+ {
+ /* Remove excessive controllers: */
+ if (cEhciCtls || cOhciCtls)
+ fSuccess = removeUSBControllers(QSet<KUSBControllerType>()
+ << KUSBControllerType_EHCI
+ << KUSBControllerType_OHCI);
+
+ /* Add required controller: */
+ if (fSuccess && !cXhciCtls)
+ {
+ m_machine.AddUSBController("xHCI", KUSBControllerType_XHCI);
+ fSuccess = m_machine.isOk();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(m_machine));
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsUSB::removeUSBFilter(CUSBDeviceFilters &comFiltersObject, int iPosition)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Remove filter: */
+ if (fSuccess)
+ {
+ /* Remove filter: */
+ comFiltersObject.RemoveDeviceFilter(iPosition);
+ fSuccess = comFiltersObject.isOk();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comFiltersObject));
+ }
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UIMachineSettingsUSB::createUSBFilter(CUSBDeviceFilters &comFiltersObject, int iPosition, const UIDataSettingsMachineUSBFilter &filterData)
+{
+ /* Prepare result: */
+ bool fSuccess = true;
+ /* Add filter: */
+ if (fSuccess)
+ {
+ /* Create filter: */
+ CUSBDeviceFilter comFilter = comFiltersObject.CreateDeviceFilter(filterData.m_guiData.m_strName);
+ fSuccess = comFiltersObject.isOk() && comFilter.isNotNull();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comFiltersObject));
+ else
+ {
+ /* Save whether filter is active: */
+ if (fSuccess)
+ {
+ comFilter.SetActive(filterData.m_guiData.m_fActive);
+ fSuccess = comFilter.isOk();
+ }
+ /* Save filter Vendor ID: */
+ if (fSuccess)
+ {
+ comFilter.SetVendorId(filterData.m_guiData.m_strVendorId);
+ fSuccess = comFilter.isOk();
+ }
+ /* Save filter Product ID: */
+ if (fSuccess)
+ {
+ comFilter.SetProductId(filterData.m_guiData.m_strProductId);
+ fSuccess = comFilter.isOk();
+ }
+ /* Save filter revision: */
+ if (fSuccess)
+ {
+ comFilter.SetRevision(filterData.m_guiData.m_strRevision);
+ fSuccess = comFilter.isOk();
+ }
+ /* Save filter manufacturer: */
+ if (fSuccess)
+ {
+ comFilter.SetManufacturer(filterData.m_guiData.m_strManufacturer);
+ fSuccess = comFilter.isOk();
+ }
+ /* Save filter product: */
+ if (fSuccess)
+ {
+ comFilter.SetProduct(filterData.m_guiData.m_strProduct);
+ fSuccess = comFilter.isOk();
+ }
+ /* Save filter serial number: */
+ if (fSuccess)
+ {
+ comFilter.SetSerialNumber(filterData.m_guiData.m_strSerialNumber);
+ fSuccess = comFilter.isOk();
+ }
+ /* Save filter port: */
+ if (fSuccess)
+ {
+ comFilter.SetPort(filterData.m_guiData.m_strPort);
+ fSuccess = comFilter.isOk();
+ }
+ /* Save filter remote mode: */
+ if (fSuccess)
+ {
+ QString strRemote;
+ switch (filterData.m_guiData.m_enmRemoteMode)
+ {
+ case UIRemoteMode_On: strRemote = "1"; break;
+ case UIRemoteMode_Off: strRemote = "0"; break;
+ default: break;
+ }
+ comFilter.SetRemote(strRemote);
+ fSuccess = comFilter.isOk();
+ }
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comFilter));
+ else
+ {
+ /* Insert filter onto corresponding position: */
+ comFiltersObject.InsertDeviceFilter(iPosition, comFilter);
+ fSuccess = comFiltersObject.isOk();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ notifyOperationProgressError(UIErrorString::formatErrorInfo(comFiltersObject));
+ }
+ }
+ }
+ /* Return result: */
+ return fSuccess;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsUSB.h b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsUSB.h
new file mode 100644
index 00000000..7ce15486
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/settings/machine/UIMachineSettingsUSB.h
@@ -0,0 +1,122 @@
+/* $Id: UIMachineSettingsUSB.h $ */
+/** @file
+ * VBox Qt GUI - UIMachineSettingsUSB class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsUSB_h
+#define FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsUSB_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UISettingsPage.h"
+
+/* Forward declarations: */
+class UIUSBSettingsEditor;
+struct UIDataSettingsMachineUSB;
+struct UIDataSettingsMachineUSBFilter;
+typedef UISettingsCache<UIDataSettingsMachineUSBFilter> UISettingsCacheMachineUSBFilter;
+typedef UISettingsCachePool<UIDataSettingsMachineUSB, UISettingsCacheMachineUSBFilter> UISettingsCacheMachineUSB;
+
+/** Machine settings: USB page. */
+class SHARED_LIBRARY_STUFF UIMachineSettingsUSB : public UISettingsPageMachine
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs USB settings page. */
+ UIMachineSettingsUSB();
+ /** Destructs USB settings page. */
+ virtual ~UIMachineSettingsUSB() RT_OVERRIDE;
+
+ /** Returns whether the USB is enabled. */
+ bool isUSBEnabled() const;
+
+protected:
+
+ /** Returns whether the page content was changed. */
+ virtual bool changed() const RT_OVERRIDE;
+
+ /** Loads settings from external object(s) packed inside @a data to cache.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void loadToCacheFrom(QVariant &data) RT_OVERRIDE;
+ /** Loads data from cache to corresponding widgets.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void getFromCache() RT_OVERRIDE;
+
+ /** Saves data from corresponding widgets to cache.
+ * @note This task WILL be performed in the GUI thread only, all widget interactions here! */
+ virtual void putToCache() RT_OVERRIDE;
+ /** Saves settings from cache to external object(s) packed inside @a data.
+ * @note This task WILL be performed in other than the GUI thread, no widget interactions! */
+ virtual void saveFromCacheTo(QVariant &data) RT_OVERRIDE;
+
+ /** Performs validation, updates @a messages list if something is wrong. */
+ virtual bool validate(QList<UIValidationMessage> &messages) RT_OVERRIDE;
+
+ /** Defines TAB order for passed @a pWidget. */
+ virtual void setOrderAfter(QWidget *pWidget) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs final page polishing. */
+ virtual void polishPage() RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Saves existing data from cache. */
+ bool saveData();
+ /** Removes USB controllers of passed @a types. */
+ bool removeUSBControllers(const QSet<KUSBControllerType> &types = QSet<KUSBControllerType>());
+ /** Creates USB controllers of passed @a enmType. */
+ bool createUSBControllers(KUSBControllerType enmType);
+ /** Removes USB filter at passed @a iPosition of the @a filtersObject. */
+ bool removeUSBFilter(CUSBDeviceFilters &comFiltersObject, int iPosition);
+ /** Creates USB filter at passed @a iPosition of the @a filtersObject using the @a filterData. */
+ bool createUSBFilter(CUSBDeviceFilters &comFiltersObject, int iPosition, const UIDataSettingsMachineUSBFilter &filterData);
+
+ /** Holds the page data cache instance. */
+ UISettingsCacheMachineUSB *m_pCache;
+
+ /** @name Widgets
+ * @{ */
+ /** Holds the USB settings editor instance. */
+ UIUSBSettingsEditor *m_pEditorUsbSettings;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_settings_machine_UIMachineSettingsUSB_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/snapshots/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/snapshots/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/snapshots/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotDetailsWidget.cpp b/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotDetailsWidget.cpp
new file mode 100644
index 00000000..1167f7b2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotDetailsWidget.cpp
@@ -0,0 +1,2081 @@
+/* $Id: UISnapshotDetailsWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UISnapshotDetailsWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QHBoxLayout>
+#include <QDateTime>
+#include <QDir>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPainter>
+#include <QPushButton>
+#include <QRegularExpression>
+#include <QScrollArea>
+#include <QTabWidget>
+#include <QTextBrowser>
+#include <QTextEdit>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIFlowLayout.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UICursor.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIIconPool.h"
+#include "UISnapshotDetailsWidget.h"
+#include "UIMessageCenter.h"
+#include "UITranslator.h"
+#include "VBoxUtils.h"
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CAudioSettings.h"
+#include "CRecordingSettings.h"
+#include "CRecordingScreenSettings.h"
+#include "CMachine.h"
+#include "CMedium.h"
+#include "CMediumAttachment.h"
+#include "CNetworkAdapter.h"
+#include "CSerialPort.h"
+#include "CSharedFolder.h"
+#include "CStorageController.h"
+#include "CSystemProperties.h"
+#include "CUSBController.h"
+#include "CUSBDeviceFilter.h"
+#include "CUSBDeviceFilters.h"
+#include "CVRDEServer.h"
+
+/* Forward declarations: */
+class UISnapshotDetailsElement;
+
+
+/** QAccessibleObject extension used as an accessibility interface for UISnapshotDetailsElement. */
+class UIAccessibilityInterfaceForUISnapshotDetailsElement : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating UISnapshotDetailsElement accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UISnapshotDetailsElement"))
+ return new UIAccessibilityInterfaceForUISnapshotDetailsElement(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ UIAccessibilityInterfaceForUISnapshotDetailsElement(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::StaticText)
+ {}
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding UISnapshotDetailsElement. */
+ UISnapshotDetailsElement *browser() const;
+};
+
+
+/** QWiget extension providing GUI with snapshot details elements. */
+class UISnapshotDetailsElement : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about @a link was clicked. */
+ void sigAnchorClicked(const QUrl &link);
+
+public:
+
+ /** Constructs details element passing @a pParent to the base-class.
+ * @param strName Brings the element name.
+ * @param icon Brings the element icon.
+ * @param fLinkSupport Brings whether we should construct text-browser
+ * instead of simple text-edit otherwise. */
+ UISnapshotDetailsElement(const QString &strName, const QIcon &icon,
+ bool fLinkSupport, QWidget *pParent = 0);
+
+ /** Returns underlying text-document. */
+ QTextDocument *document() const;
+
+ /** Defines text-document text. */
+ void setText(const QString &strText);
+
+ /** Returns the minimum size-hint. */
+ QSize minimumSizeHint() const;
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates pixmap. */
+ void updatePixmap();
+
+ /** Holds the element name.*/
+ QString m_strName;
+ /** Holds the element icon. */
+ QIcon m_icon;
+ /** Holds whether we should construct text-browser
+ * instead of simple text-edit otherwise. */
+ bool m_fLinkSupport;
+
+ /** Holds the text-edit interface instance. */
+ QTextEdit *m_pTextEdit;
+};
+
+
+/** QWiget extension providing GUI with snapshot screenshot viewer widget. */
+class UIScreenshotViewer : public QIWithRetranslateUI2<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs screenshow viewer passing @a pParent to the base-class.
+ * @param pixmapScreenshot Brings the screenshot to show.
+ * @param strSnapshotName Brings the snapshot name.
+ * @param strMachineName Brings the machine name. */
+ UIScreenshotViewer(const QPixmap &pixmapScreenshot,
+ const QString &strSnapshotName,
+ const QString &strMachineName,
+ QWidget *pParent = 0);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles polish @a pEvent. */
+ virtual void polishEvent(QShowEvent *pEvent);
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse press @a pEvent. */
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles key press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Adjusts window size. */
+ void adjustWindowSize();
+
+ /** Adjusts picture. */
+ void adjustPicture();
+
+ /** Holds whether this widget was polished. */
+ bool m_fPolished;
+
+ /** Holds the screenshot to show. */
+ QPixmap m_pixmapScreenshot;
+ /** Holds the snapshot name. */
+ QString m_strSnapshotName;
+ /** Holds the machine name. */
+ QString m_strMachineName;
+
+ /** Holds the scroll-area instance. */
+ QScrollArea *m_pScrollArea;
+ /** Holds the picture label instance. */
+ QLabel *m_pLabelPicture;
+
+ /** Holds whether we are in zoom mode. */
+ bool m_fZoomMode;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIAccessibilityInterfaceForUISnapshotDetailsElement implementation. *
+*********************************************************************************************************************************/
+
+QString UIAccessibilityInterfaceForUISnapshotDetailsElement::text(QAccessible::Text enmTextRole) const
+{
+ /* Make sure browser still alive: */
+ AssertPtrReturn(browser(), QString());
+
+ /* Return the description: */
+ if (enmTextRole == QAccessible::Description)
+ {
+ /* Sanity check: */
+ AssertPtrReturn(browser()->document(), QString());
+ return browser()->document()->toPlainText();
+ }
+
+ /* Null-string by default: */
+ return QString();
+}
+
+UISnapshotDetailsElement *UIAccessibilityInterfaceForUISnapshotDetailsElement::browser() const
+{
+ return qobject_cast<UISnapshotDetailsElement*>(widget());
+}
+
+
+/*********************************************************************************************************************************
+* Class UISnapshotDetailsElement implementation. *
+*********************************************************************************************************************************/
+
+UISnapshotDetailsElement::UISnapshotDetailsElement(const QString &strName, const QIcon &icon,
+ bool fLinkSupport, QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_strName(strName)
+ , m_icon(icon)
+ , m_fLinkSupport(fLinkSupport)
+ , m_pTextEdit(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+QTextDocument *UISnapshotDetailsElement::document() const
+{
+ /* Pass to private object: */
+ return m_pTextEdit->document();
+}
+
+void UISnapshotDetailsElement::setText(const QString &strText)
+{
+ /* Pass to private object: */
+ m_pTextEdit->setText(strText);
+ /* Update the layout: */
+ updateGeometry();
+}
+
+QSize UISnapshotDetailsElement::minimumSizeHint() const
+{
+ /* Calculate minimum size-hint on the basis of:
+ * 1. context and text-documnt margins, 2. text-document ideal width and height: */
+ int iTop = 0, iLeft = 0, iRight = 0, iBottom = 0;
+ layout()->getContentsMargins(&iTop, &iLeft, &iRight, &iBottom);
+ const QSize size = m_pTextEdit->document()->size().toSize();
+ const int iDocumentMargin = (int)m_pTextEdit->document()->documentMargin();
+ const int iIdealWidth = (int)m_pTextEdit->document()->idealWidth() + 2 * iDocumentMargin + iLeft + iRight;
+ const int iIdealHeight = size.height() + 2 * iDocumentMargin + iTop + iBottom;
+ return QSize(iIdealWidth, iIdealHeight);
+}
+
+bool UISnapshotDetailsElement::event(QEvent *pEvent)
+{
+ /* Handle know event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Show:
+ case QEvent::ScreenChangeInternal:
+ {
+ /* Update pixmap: */
+ updatePixmap();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QWidget::event(pEvent);
+}
+
+void UISnapshotDetailsElement::paintEvent(QPaintEvent * /* pEvent */)
+{
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Prepare palette colors: */
+ const QPalette pal = QApplication::palette();
+ QColor color0 = pal.color(QPalette::Window);
+ QColor color1 = pal.color(QPalette::Window).lighter(110);
+ color1.setAlpha(0);
+ QColor color2 = pal.color(QPalette::Window).darker(200);
+
+ /* Invent pixel metric: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Top-left corner: */
+ QRadialGradient grad1(QPointF(iMetric, iMetric), iMetric);
+ {
+ grad1.setColorAt(0, color2);
+ grad1.setColorAt(1, color1);
+ }
+ /* Top-right corner: */
+ QRadialGradient grad2(QPointF(width() - iMetric, iMetric), iMetric);
+ {
+ grad2.setColorAt(0, color2);
+ grad2.setColorAt(1, color1);
+ }
+ /* Bottom-left corner: */
+ QRadialGradient grad3(QPointF(iMetric, height() - iMetric), iMetric);
+ {
+ grad3.setColorAt(0, color2);
+ grad3.setColorAt(1, color1);
+ }
+ /* Botom-right corner: */
+ QRadialGradient grad4(QPointF(width() - iMetric, height() - iMetric), iMetric);
+ {
+ grad4.setColorAt(0, color2);
+ grad4.setColorAt(1, color1);
+ }
+
+ /* Top line: */
+ QLinearGradient grad5(QPointF(iMetric, 0), QPointF(iMetric, iMetric));
+ {
+ grad5.setColorAt(0, color1);
+ grad5.setColorAt(1, color2);
+ }
+ /* Bottom line: */
+ QLinearGradient grad6(QPointF(iMetric, height()), QPointF(iMetric, height() - iMetric));
+ {
+ grad6.setColorAt(0, color1);
+ grad6.setColorAt(1, color2);
+ }
+ /* Left line: */
+ QLinearGradient grad7(QPointF(0, height() - iMetric), QPointF(iMetric, height() - iMetric));
+ {
+ grad7.setColorAt(0, color1);
+ grad7.setColorAt(1, color2);
+ }
+ /* Right line: */
+ QLinearGradient grad8(QPointF(width(), height() - iMetric), QPointF(width() - iMetric, height() - iMetric));
+ {
+ grad8.setColorAt(0, color1);
+ grad8.setColorAt(1, color2);
+ }
+
+ /* Paint shape/shadow: */
+ painter.fillRect(QRect(iMetric, iMetric, width() - iMetric * 2, height() - iMetric * 2), color0);
+ painter.fillRect(QRect(0, 0, iMetric, iMetric), grad1);
+ painter.fillRect(QRect(width() - iMetric, 0, iMetric, iMetric), grad2);
+ painter.fillRect(QRect(0, height() - iMetric, iMetric, iMetric), grad3);
+ painter.fillRect(QRect(width() - iMetric, height() - iMetric, iMetric, iMetric), grad4);
+ painter.fillRect(QRect(iMetric, 0, width() - iMetric * 2, iMetric), grad5);
+ painter.fillRect(QRect(iMetric, height() - iMetric, width() - iMetric * 2, iMetric), grad6);
+ painter.fillRect(QRect(0, iMetric, iMetric, height() - iMetric * 2), grad7);
+ painter.fillRect(QRect(width() - iMetric, iMetric, iMetric, height() - iMetric * 2), grad8);
+}
+
+void UISnapshotDetailsElement::prepare()
+{
+ /* Install QIComboBox accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUISnapshotDetailsElement::pFactory);
+
+ /* Create layout: */
+ new QHBoxLayout(this);
+ AssertPtrReturnVoid(layout());
+ {
+ /* Invent pixel metric: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Configure layout: */
+ layout()->setContentsMargins(iMetric, iMetric, iMetric, iMetric);
+
+ /* Create text-browser if requested, text-edit otherwise: */
+ m_pTextEdit = m_fLinkSupport ? new QTextBrowser : new QTextEdit;
+ AssertPtrReturnVoid(m_pTextEdit);
+ {
+ /* Configure that we have: */
+ m_pTextEdit->setReadOnly(true);
+ m_pTextEdit->setFocusPolicy(Qt::NoFocus);
+ m_pTextEdit->setFrameShape(QFrame::NoFrame);
+ m_pTextEdit->viewport()->setAutoFillBackground(false);
+ m_pTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTextEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTextEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ if (m_fLinkSupport)
+ {
+ // WORKAROUND:
+ // Intentionally using old kind of API here:
+ connect(m_pTextEdit, SIGNAL(anchorClicked(const QUrl &)),
+ this, SIGNAL(sigAnchorClicked(const QUrl &)));
+ }
+
+ /* Add into layout: */
+ layout()->addWidget(m_pTextEdit);
+ }
+ }
+
+ /* Update pixmap: */
+ updatePixmap();
+}
+
+void UISnapshotDetailsElement::updatePixmap()
+{
+ /* Re-register icon in the element's text-document: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ document()->addResource(
+ QTextDocument::ImageResource,
+ QUrl(QString("details://%1").arg(m_strName)),
+ QVariant(m_icon.pixmap(window()->windowHandle(), QSize(iMetric, iMetric))));
+}
+
+
+/*********************************************************************************************************************************
+* Class UIScreenshotViewer implementation. *
+*********************************************************************************************************************************/
+
+UIScreenshotViewer::UIScreenshotViewer(const QPixmap &pixmapScreenshot,
+ const QString &strSnapshotName,
+ const QString &strMachineName,
+ QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI2<QWidget>(pParent, Qt::Tool)
+ , m_fPolished(false)
+ , m_pixmapScreenshot(pixmapScreenshot)
+ , m_strSnapshotName(strSnapshotName)
+ , m_strMachineName(strMachineName)
+ , m_pScrollArea(0)
+ , m_pLabelPicture(0)
+ , m_fZoomMode(true)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIScreenshotViewer::retranslateUi()
+{
+ /* Translate window title: */
+ setWindowTitle(tr("Screenshot of %1 (%2)").arg(m_strSnapshotName).arg(m_strMachineName));
+}
+
+void UIScreenshotViewer::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI2<QWidget>::showEvent(pEvent);
+
+ /* Make sure we should polish dialog: */
+ if (m_fPolished)
+ return;
+
+ /* Call to polish-event: */
+ polishEvent(pEvent);
+
+ /* Mark dialog as polished: */
+ m_fPolished = true;
+}
+
+void UIScreenshotViewer::polishEvent(QShowEvent * /* pEvent */)
+{
+ /* Adjust the picture: */
+ adjustPicture();
+}
+
+void UIScreenshotViewer::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI2<QWidget>::resizeEvent(pEvent);
+
+ /* Adjust the picture: */
+ adjustPicture();
+}
+
+void UIScreenshotViewer::mousePressEvent(QMouseEvent *pEvent)
+{
+ /* Toggle the zoom mode: */
+ m_fZoomMode = !m_fZoomMode;
+
+ /* Adjust the windiow size: */
+ adjustWindowSize();
+ /* Adjust the picture: */
+ adjustPicture();
+
+ /* Call to base-class: */
+ QIWithRetranslateUI2<QWidget>::mousePressEvent(pEvent);
+}
+
+void UIScreenshotViewer::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Close on escape: */
+ if (pEvent->key() == Qt::Key_Escape)
+ close();
+
+ /* Call to base-class: */
+ QIWithRetranslateUI2<QWidget>::keyPressEvent(pEvent);
+}
+
+void UIScreenshotViewer::prepare()
+{
+ /* Screenshot viewer is an application-modal window: */
+ setWindowModality(Qt::ApplicationModal);
+ /* With the pointing-hand cursor: */
+ UICursor::setCursor(this, Qt::PointingHandCursor);
+ /* And it's being deleted when closed: */
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ /* Create layout: */
+ new QVBoxLayout(this);
+ AssertPtrReturnVoid(layout());
+ {
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+
+ /* Create scroll-area: */
+ m_pScrollArea = new QScrollArea;
+ AssertPtrReturnVoid(m_pScrollArea);
+ {
+ /* Configure scroll-area: */
+ m_pScrollArea->setWidgetResizable (true);
+
+ /* Create picture label: */
+ m_pLabelPicture = new QLabel;
+ AssertPtrReturnVoid(m_pLabelPicture);
+ {
+ /* Add into scroll-area: */
+ m_pScrollArea->setWidget(m_pLabelPicture);
+ }
+
+ /* Add into layout: */
+ layout()->addWidget(m_pScrollArea);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+
+ /* Adjust window size: */
+ adjustWindowSize();
+
+ /* Center according requested widget: */
+ gpDesktop->centerWidget(this, parentWidget(), false);
+}
+
+void UIScreenshotViewer::adjustWindowSize()
+{
+ /* Acquire current host-screen size, fallback to 1024x768 if failed: */
+ QSize screenSize = gpDesktop->screenGeometry(parentWidget()).size();
+ if (!screenSize.isValid())
+ screenSize = QSize(1024, 768);
+ const int iInitWidth = screenSize.width() * .50 /* 50% of host-screen width */;
+
+ /* Calculate screenshot aspect-ratio: */
+ const double dAspectRatio = (double)m_pixmapScreenshot.height() / m_pixmapScreenshot.width();
+
+ /* Calculate maximum window size: */
+ const QSize maxSize = m_fZoomMode
+ ? screenSize * .9 /* 90% of host-screen size */ +
+ QSize(m_pScrollArea->frameWidth() * 2, m_pScrollArea->frameWidth() * 2)
+ : m_pixmapScreenshot.size() /* just the screenshot size */ +
+ QSize(m_pScrollArea->frameWidth() * 2, m_pScrollArea->frameWidth() * 2);
+
+ /* Calculate initial window size: */
+ const QSize initSize = QSize(iInitWidth, (int)(iInitWidth * dAspectRatio)).boundedTo(maxSize);
+
+ /* Apply maximum window size restrictions: */
+ setMaximumSize(maxSize);
+ /* Apply initial window size: */
+ resize(initSize);
+}
+
+void UIScreenshotViewer::adjustPicture()
+{
+ if (m_fZoomMode)
+ {
+ /* Adjust visual aspects: */
+ m_pScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pLabelPicture->setPixmap(m_pixmapScreenshot.scaled(m_pScrollArea->viewport()->size(),
+ Qt::IgnoreAspectRatio,
+ Qt::SmoothTransformation));
+ m_pLabelPicture->setToolTip(tr("Click to view non-scaled screenshot."));
+ }
+ else
+ {
+ /* Adjust visual aspects: */
+ m_pScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_pScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_pLabelPicture->setPixmap(m_pixmapScreenshot);
+ m_pLabelPicture->setToolTip(tr("Click to view scaled screenshot."));
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UISnapshotDetailsWidget implementation. *
+*********************************************************************************************************************************/
+
+UISnapshotDetailsWidget::UISnapshotDetailsWidget(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pTabWidget(0)
+ , m_pLayoutOptions(0)
+ , m_pLabelName(0), m_pEditorName(0), m_pErrorPaneName(0)
+ , m_pLabelDescription(0), m_pBrowserDescription(0), m_pErrorPaneDescription(0)
+ , m_pButtonBox(0)
+ , m_pLayoutDetails(0)
+ , m_pScrollAreaDetails(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UISnapshotDetailsWidget::setData(const CMachine &comMachine)
+{
+ /* Cache old/new data: */
+ m_oldData = UIDataSnapshot();
+ m_newData = m_oldData;
+
+ /* Cache machine/snapshot: */
+ m_comMachine = comMachine;
+ m_comSnapshot = CSnapshot();
+
+ /* Retranslate buttons: */
+ retranslateButtons();
+ /* Load snapshot data: */
+ loadSnapshotData();
+}
+
+void UISnapshotDetailsWidget::setData(const UIDataSnapshot &data, const CSnapshot &comSnapshot)
+{
+ /* Cache old/new data: */
+ m_oldData = data;
+ m_newData = m_oldData;
+
+ /* Cache machine/snapshot: */
+ m_comMachine = CMachine();
+ m_comSnapshot = comSnapshot;
+
+ /* Retranslate buttons: */
+ retranslateButtons();
+ /* Load snapshot data: */
+ loadSnapshotData();
+}
+
+void UISnapshotDetailsWidget::clearData()
+{
+ /* Reset old/new data: */
+ m_oldData = UIDataSnapshot();
+ m_newData = m_oldData;
+
+ /* Reset machine/snapshot: */
+ m_comMachine = CMachine();
+ m_comSnapshot = CSnapshot();
+
+ /* Retranslate buttons: */
+ retranslateButtons();
+ /* Load snapshot data: */
+ loadSnapshotData();
+}
+
+void UISnapshotDetailsWidget::retranslateUi()
+{
+ /* Translate labels: */
+ m_pTabWidget->setTabText(0, tr("&Attributes"));
+ m_pTabWidget->setTabText(1, tr("&Information"));
+ m_pLabelName->setText(tr("&Name:"));
+ m_pLabelDescription->setText(tr("&Description:"));
+ m_pEditorName->setToolTip(tr("Holds the snapshot name."));
+ m_pBrowserDescription->setToolTip(tr("Holds the snapshot description."));
+
+ /* Translate placeholders: */
+ m_pEditorName->setPlaceholderText( m_comMachine.isNotNull()
+ ? tr("Enter a name for the new snapshot...")
+ : m_comSnapshot.isNotNull()
+ ? tr("Enter a name for this snapshot...")
+ : QString());
+
+ /* Translate buttons: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setShortcut(QString("Ctrl+Return"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
+ retranslateButtons();
+
+ /* Update the picture tool-tip and visibility: */
+ m_details.value(DetailsElementType_Preview)->setToolTip(tr("Click to enlarge the screenshot."));
+ if (!m_pixmapScreenshot.isNull() && m_details.value(DetailsElementType_Preview)->isHidden())
+ m_details.value(DetailsElementType_Preview)->setHidden(false);
+ else if (m_pixmapScreenshot.isNull() && !m_details.value(DetailsElementType_Preview)->isHidden())
+ m_details.value(DetailsElementType_Preview)->setHidden(true);
+
+ /* Prepare machine: */
+ const CMachine &comMachine = m_comMachine.isNotNull()
+ ? m_comMachine
+ : m_comSnapshot.isNotNull()
+ ? m_comSnapshot.GetMachine()
+ : CMachine();
+
+ /* Make sure machine is valid: */
+ if (comMachine.isNotNull())
+ {
+ /* Update USB details visibility: */
+ const CUSBDeviceFilters &comFilters = comMachine.GetUSBDeviceFilters();
+ const bool fUSBMissing = comFilters.isNull() || !comMachine.GetUSBProxyAvailable();
+ if (fUSBMissing && !m_details.value(DetailsElementType_USB)->isHidden())
+ m_details.value(DetailsElementType_USB)->setHidden(true);
+
+ /* Rebuild the details report: */
+ foreach (const DetailsElementType &enmType, m_details.keys())
+ m_details.value(enmType)->setText(detailsReport(enmType, comMachine, comMachine.GetCurrentSnapshot()));
+ }
+
+ /* Retranslate validation: */
+ retranslateValidation();
+}
+
+void UISnapshotDetailsWidget::retranslateButtons()
+{
+ /* Common: 'Reset' button: */
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setText(tr("Reset"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setStatusTip(tr("Reset changes in current snapshot details"));
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->
+ setToolTip(tr("Reset Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Cancel)->shortcut().toString()));
+
+ if (m_comMachine.isNotNull())
+ {
+ /* Machine: 'Take' button: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText(tr("Take"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(tr("Take snapshot on the basis of current machine state"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->
+ setToolTip(tr("Take Snapshot (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+ else
+ {
+ /* Snapshot: 'Apply' button: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setText(tr("Apply"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setStatusTip(tr("Apply changes in current snapshot details"));
+ m_pButtonBox->button(QDialogButtonBox::Ok)->
+ setToolTip(tr("Apply Changes (%1)").arg(m_pButtonBox->button(QDialogButtonBox::Ok)->shortcut().toString()));
+ }
+}
+
+void UISnapshotDetailsWidget::sltHandleNameChange()
+{
+ m_newData.setName(m_pEditorName->text());
+ revalidate(m_pErrorPaneName);
+ updateButtonStates();
+}
+
+void UISnapshotDetailsWidget::sltHandleDescriptionChange()
+{
+ m_newData.setDescription(m_pBrowserDescription->toPlainText());
+ revalidate(m_pErrorPaneDescription);
+ updateButtonStates();
+}
+
+void UISnapshotDetailsWidget::sltHandleAnchorClicked(const QUrl &link)
+{
+ /* Get the link out of url: */
+ const QString strLink = link.toString();
+ if (strLink == "#thumbnail")
+ {
+ /* We are creating screenshot viewer and show it: */
+ UIScreenshotViewer *pViewer = new UIScreenshotViewer(m_pixmapScreenshot,
+ m_comSnapshot.GetMachine().GetName(),
+ m_comSnapshot.GetName(),
+ this);
+ pViewer->show();
+ pViewer->activateWindow();
+ }
+}
+
+void UISnapshotDetailsWidget::sltHandleChangeAccepted()
+{
+ /* Disable buttons first of all: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
+
+ /* Notify listeners: */
+ emit sigDataChangeAccepted();
+}
+
+void UISnapshotDetailsWidget::sltHandleChangeRejected()
+{
+ /* Reset new data to old: */
+ m_newData = m_oldData;
+
+ /* Load snapshot data: */
+ loadSnapshotData();
+}
+
+void UISnapshotDetailsWidget::prepare()
+{
+ /* Create layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ AssertPtrReturnVoid(pLayout);
+ {
+ /* Configure layout: */
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create tab-widget: */
+ m_pTabWidget = new QTabWidget;
+ AssertPtrReturnVoid(m_pTabWidget);
+ {
+ /* Prepare 'Options' tab: */
+ prepareTabOptions();
+ /* Prepare 'Details' tab: */
+ prepareTabDetails();
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pTabWidget);
+ }
+ }
+}
+
+void UISnapshotDetailsWidget::prepareTabOptions()
+{
+ /* Create widget itself: */
+ QWidget *pWidget = new QWidget;
+ AssertPtrReturnVoid(pWidget);
+ {
+ /* Create 'Options' layout: */
+ m_pLayoutOptions = new QGridLayout(pWidget);
+ AssertPtrReturnVoid(m_pLayoutOptions);
+ {
+#ifdef VBOX_WS_MAC
+ /* Configure layout: */
+ m_pLayoutOptions->setSpacing(10);
+ m_pLayoutOptions->setContentsMargins(10, 10, 10, 10);
+#endif
+
+ /* Get the required icon metric: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+
+ /* Create name label: */
+ m_pLabelName = new QLabel;
+ AssertPtrReturnVoid(m_pLabelName);
+ {
+ /* Configure label: */
+ m_pLabelName->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
+
+ /* Add into layout: */
+ m_pLayoutOptions->addWidget(m_pLabelName, 0, 0);
+ }
+ /* Create name layout: */
+ QHBoxLayout *pLayoutName = new QHBoxLayout;
+ AssertPtrReturnVoid(pLayoutName);
+ {
+ /* Create name editor: */
+ m_pEditorName = new QLineEdit;
+ AssertPtrReturnVoid(m_pEditorName);
+ {
+ /* Configure editor: */
+ m_pLabelName->setBuddy(m_pEditorName);
+ QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ policy.setHorizontalStretch(1);
+ m_pEditorName->setSizePolicy(policy);
+ connect(m_pEditorName, &QLineEdit::textChanged,
+ this, &UISnapshotDetailsWidget::sltHandleNameChange);
+
+ /* Add into layout: */
+ pLayoutName->addWidget(m_pEditorName);
+ }
+ /* Create name error pane: */
+ m_pErrorPaneName = new QLabel;
+ AssertPtrReturnVoid(m_pErrorPaneName);
+ {
+ /* Configure error pane: */
+ m_pErrorPaneName->setAlignment(Qt::AlignCenter);
+ m_pErrorPaneName->setPixmap(UIIconPool::iconSet(":/status_error_16px.png")
+ .pixmap(QSize(iIconMetric, iIconMetric)));
+
+ /* Add into layout: */
+ pLayoutName->addWidget(m_pErrorPaneName);
+ }
+
+ /* Add into layout: */
+ m_pLayoutOptions->addLayout(pLayoutName, 0, 1);
+ }
+
+ /* Create description label: */
+ m_pLabelDescription = new QLabel;
+ AssertPtrReturnVoid(m_pLabelDescription);
+ {
+ /* Configure label: */
+ m_pLabelDescription->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignTop);
+
+ /* Add into layout: */
+ m_pLayoutOptions->addWidget(m_pLabelDescription, 1, 0);
+ }
+ /* Create description layout: */
+ QHBoxLayout *pLayoutDescription = new QHBoxLayout;
+ AssertPtrReturnVoid(pLayoutDescription);
+ {
+ /* Create description browser: */
+ m_pBrowserDescription = new QTextEdit;
+ AssertPtrReturnVoid(m_pBrowserDescription);
+ {
+ /* Configure browser: */
+ m_pLabelDescription->setBuddy(m_pBrowserDescription);
+ m_pBrowserDescription->setTabChangesFocus(true);
+ m_pBrowserDescription->setAcceptRichText(false);
+ QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ policy.setHorizontalStretch(1);
+ m_pBrowserDescription->setSizePolicy(policy);
+ connect(m_pBrowserDescription, &QTextEdit::textChanged,
+ this, &UISnapshotDetailsWidget::sltHandleDescriptionChange);
+
+ /* Add into layout: */
+ pLayoutDescription->addWidget(m_pBrowserDescription);
+ }
+ /* Create description error pane: */
+ m_pErrorPaneDescription = new QLabel;
+ AssertPtrReturnVoid(m_pErrorPaneDescription);
+ {
+ /* Configure error pane: */
+ m_pErrorPaneDescription->setAlignment(Qt::AlignCenter);
+ m_pErrorPaneDescription->setPixmap(UIIconPool::iconSet(":/status_error_16px.png")
+ .pixmap(QSize(iIconMetric, iIconMetric)));
+
+ /* Add into layout: */
+ pLayoutDescription->addWidget(m_pErrorPaneDescription);
+ }
+
+ /* Add into layout: */
+ m_pLayoutOptions->addLayout(pLayoutDescription, 1, 1);
+ }
+
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ AssertPtrReturnVoid(m_pButtonBox);
+ {
+ /* Configure button-box: */
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UISnapshotDetailsWidget::sltHandleChangeAccepted);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UISnapshotDetailsWidget::sltHandleChangeRejected);
+
+ /* Add into layout: */
+ m_pLayoutOptions->addWidget(m_pButtonBox, 2, 0, 1, 2);
+ }
+ }
+
+ /* Add to tab-widget: */
+ m_pTabWidget->addTab(pWidget, QString());
+ }
+}
+
+void UISnapshotDetailsWidget::prepareTabDetails()
+{
+ /* Create details scroll-area: */
+ m_pScrollAreaDetails = new QScrollArea;
+ AssertPtrReturnVoid(m_pScrollAreaDetails);
+ {
+ /* Configure browser: */
+ m_pScrollAreaDetails->setWidgetResizable(true);
+ m_pScrollAreaDetails->setFrameShadow(QFrame::Plain);
+ m_pScrollAreaDetails->setFrameShape(QFrame::NoFrame);
+ m_pScrollAreaDetails->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
+ m_pScrollAreaDetails->viewport()->setAutoFillBackground(false);
+
+ /* Create details widget: */
+ QWidget *pWidgetDetails = new QWidget;
+ AssertPtrReturnVoid(pWidgetDetails);
+ {
+ /* Create 'Details' layout: */
+ m_pLayoutDetails = new QVBoxLayout(pWidgetDetails);
+ AssertPtrReturnVoid(m_pLayoutDetails);
+ {
+ /* Metric: */
+ const int iSpacing = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Configure layout: */
+ m_pLayoutDetails->setSpacing(iSpacing);
+#ifdef VBOX_WS_MAC
+ m_pLayoutDetails->setContentsMargins(10, 10, 10, 10);
+#endif
+
+ /* Create layout 1: */
+ QHBoxLayout *pLayout1 = new QHBoxLayout;
+ AssertPtrReturnVoid(pLayout1);
+ {
+ /* Create left layout: */
+ QIFlowLayout *pLayoutLeft = new QIFlowLayout;
+ AssertPtrReturnVoid(pLayoutLeft);
+ {
+ /* Configure layout: */
+ pLayoutLeft->setSpacing(iSpacing);
+ pLayoutLeft->setContentsMargins(0, 0, 0, 0);
+
+ /* Create 'General' element: */
+ m_details[DetailsElementType_General] = createDetailsElement(DetailsElementType_General);
+ AssertPtrReturnVoid(m_details[DetailsElementType_General]);
+ pLayoutLeft->addWidget(m_details[DetailsElementType_General]);
+
+ /* Create 'System' element: */
+ m_details[DetailsElementType_System] = createDetailsElement(DetailsElementType_System);
+ AssertPtrReturnVoid(m_details[DetailsElementType_System]);
+ pLayoutLeft->addWidget(m_details[DetailsElementType_System]);
+
+ /* Add to layout: */
+ pLayout1->addLayout(pLayoutLeft);
+ }
+
+ /* Create right layout: */
+ QVBoxLayout *pLayoutRight = new QVBoxLayout;
+ AssertPtrReturnVoid(pLayoutRight);
+ {
+ /* Configure layout: */
+ pLayoutLeft->setSpacing(iSpacing);
+ pLayoutRight->setContentsMargins(0, 0, 0, 0);
+
+ /* Create 'Preview' element: */
+ m_details[DetailsElementType_Preview] = createDetailsElement(DetailsElementType_Preview);
+ AssertPtrReturnVoid(m_details[DetailsElementType_Preview]);
+ connect(m_details[DetailsElementType_Preview], &UISnapshotDetailsElement::sigAnchorClicked,
+ this, &UISnapshotDetailsWidget::sltHandleAnchorClicked);
+ pLayoutRight->addWidget(m_details[DetailsElementType_Preview]);
+ pLayoutRight->addStretch();
+
+ /* Add to layout: */
+ pLayout1->addLayout(pLayoutRight);
+ }
+
+ /* Add into layout: */
+ m_pLayoutDetails->addLayout(pLayout1);
+ }
+
+ /* Create layout 2: */
+ QIFlowLayout *pLayout2 = new QIFlowLayout;
+ {
+ /* Configure layout: */
+ pLayout2->setSpacing(iSpacing);
+
+ /* Create 'Display' element: */
+ m_details[DetailsElementType_Display] = createDetailsElement(DetailsElementType_Display);
+ AssertPtrReturnVoid(m_details[DetailsElementType_Display]);
+ pLayout2->addWidget(m_details[DetailsElementType_Display]);
+
+ /* Create 'Audio' element: */
+ m_details[DetailsElementType_Audio] = createDetailsElement(DetailsElementType_Audio);
+ AssertPtrReturnVoid(m_details[DetailsElementType_Audio]);
+ pLayout2->addWidget(m_details[DetailsElementType_Audio]);
+
+ /* Create 'Storage' element: */
+ m_details[DetailsElementType_Storage] = createDetailsElement(DetailsElementType_Storage);
+ AssertPtrReturnVoid(m_details[DetailsElementType_Storage]);
+ pLayout2->addWidget(m_details[DetailsElementType_Storage]);
+
+ /* Create 'Network' element: */
+ m_details[DetailsElementType_Network] = createDetailsElement(DetailsElementType_Network);
+ AssertPtrReturnVoid(m_details[DetailsElementType_Network]);
+ pLayout2->addWidget(m_details[DetailsElementType_Network]);
+
+ /* Create 'Serial' element: */
+ m_details[DetailsElementType_Serial] = createDetailsElement(DetailsElementType_Serial);
+ AssertPtrReturnVoid(m_details[DetailsElementType_Serial]);
+ pLayout2->addWidget(m_details[DetailsElementType_Serial]);
+
+ /* Create 'USB' element: */
+ m_details[DetailsElementType_USB] = createDetailsElement(DetailsElementType_USB);
+ AssertPtrReturnVoid(m_details[DetailsElementType_USB]);
+ pLayout2->addWidget(m_details[DetailsElementType_USB]);
+
+ /* Create 'SF' element: */
+ m_details[DetailsElementType_SF] = createDetailsElement(DetailsElementType_SF);
+ AssertPtrReturnVoid(m_details[DetailsElementType_SF]);
+ pLayout2->addWidget(m_details[DetailsElementType_SF]);
+
+ /* Add into layout: */
+ m_pLayoutDetails->addLayout(pLayout2);
+ }
+
+ /* Add stretch: */
+ m_pLayoutDetails->addStretch();
+ }
+
+ /* Add to scroll-area: */
+ m_pScrollAreaDetails->setWidget(pWidgetDetails);
+ pWidgetDetails->setAutoFillBackground(false);
+ }
+
+ /* Add to tab-widget: */
+ m_pTabWidget->addTab(m_pScrollAreaDetails, QString());
+ }
+}
+
+/* static */
+UISnapshotDetailsElement *UISnapshotDetailsWidget::createDetailsElement(DetailsElementType enmType)
+{
+ /* Create element: */
+ const bool fWithHypertextNavigation = enmType == DetailsElementType_Preview;
+ UISnapshotDetailsElement *pElement = new UISnapshotDetailsElement(gpConverter->toInternalString(enmType),
+ gpConverter->toIcon(enmType),
+ fWithHypertextNavigation);
+ AssertPtrReturn(pElement, 0);
+ {
+ /* Configure element: */
+ switch (enmType)
+ {
+ case DetailsElementType_Preview:
+ pElement->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ break;
+ default:
+ pElement->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ break;
+ }
+ }
+ /* Return element: */
+ return pElement;
+}
+
+void UISnapshotDetailsWidget::loadSnapshotData()
+{
+ /* Read general snapshot properties: */
+ m_pEditorName->setText(m_newData.name());
+ m_pBrowserDescription->setText(m_newData.description());
+ revalidate();
+
+ /* If there is a machine: */
+ if (m_comMachine.isNotNull())
+ {
+ /* No screenshot: */
+ m_pixmapScreenshot = QPixmap();
+ }
+ /* If there is a snapshot: */
+ else if (m_comSnapshot.isNotNull())
+ {
+ /* Read snapshot display contents: */
+ CMachine comMachine = m_comSnapshot.GetMachine();
+ ULONG iWidth = 0, iHeight = 0;
+
+ /* Get screenshot if present: */
+ QVector<BYTE> screenData = comMachine.ReadSavedScreenshotToArray(0, KBitmapFormat_PNG, iWidth, iHeight);
+ m_pixmapScreenshot = screenData.size() != 0 ? QPixmap::fromImage(QImage::fromData(screenData.data(),
+ screenData.size(),
+ "PNG"))
+ : QPixmap();
+
+ /* Register thumbnail pixmap in preview element: */
+ // WORKAROUND:
+ // We are generating it from the screenshot because thumbnail
+ // returned by the CMachine::ReadSavedThumbnailToArray is too small.
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
+ const QSize thumbnailSize = QSize(iIconMetric * 4, iIconMetric * 4);
+ const QPixmap pixThumbnail = m_pixmapScreenshot.isNull() ? m_pixmapScreenshot
+ : m_pixmapScreenshot.scaled(thumbnailSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ m_details.value(DetailsElementType_Preview)->document()->addResource(
+ QTextDocument::ImageResource, QUrl("details://thumbnail"), QVariant(pixThumbnail));
+ }
+
+ /* Retranslate: */
+ retranslateUi();
+
+ /* Update button states finally: */
+ updateButtonStates();
+}
+
+void UISnapshotDetailsWidget::revalidate(QWidget *pWidget /* = 0 */)
+{
+ if (!pWidget || pWidget == m_pErrorPaneName)
+ {
+ const bool fError = m_newData.name().isEmpty();
+ m_pErrorPaneName->setVisible(fError && m_comMachine.isNull());
+ }
+ if (!pWidget || pWidget == m_pErrorPaneDescription)
+ {
+ const bool fError = false;
+ m_pErrorPaneDescription->setVisible(fError);
+ }
+
+ /* Retranslate validation: */
+ retranslateValidation(pWidget);
+}
+
+void UISnapshotDetailsWidget::retranslateValidation(QWidget *pWidget /* = 0 */)
+{
+ if (!pWidget || pWidget == m_pErrorPaneName)
+ m_pErrorPaneName->setToolTip(tr("Snapshot name is empty"));
+}
+
+void UISnapshotDetailsWidget::updateButtonStates()
+{
+// if (m_oldData != m_newData)
+// printf("Snapshot: %s, %s\n",
+// m_newData.m_strName.toUtf8().constData(),
+// m_newData.m_strDescription.toUtf8().constData());
+
+ /* Update 'Apply' / 'Reset' button states: */
+ m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(m_oldData != m_newData);
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_oldData != m_newData);
+}
+
+QString UISnapshotDetailsWidget::detailsReport(DetailsElementType enmType,
+ const CMachine &comMachine,
+ const CSnapshot &comSnapshot /* = CSnapshot() */) const
+{
+ /* Details templates: */
+ static const char *sTableTpl =
+ "<table border=0 cellspacing=1 cellpadding=0 style='white-space:pre'>%1</table>";
+ static const char *sSectionBoldTpl1 =
+ "<tr>"
+ "<td width=%3 rowspan=%1 align=left><img src='%2'></td>"
+ "<td colspan=3><nobr><b>%4</b></nobr></td>"
+ "</tr>"
+ "%5";
+ static const char *sSectionBoldTpl2 =
+ "<tr>"
+ "<td width=%3 rowspan=%1 align=left><img src='%2'></td>"
+ "<td><nobr><b>%4</b></nobr></td>"
+ "</tr>"
+ "%5";
+ static const char *sSectionItemTpl1 =
+ "<tr><td><nobr>%1</nobr></td><td/><td/></tr>";
+ static const char *sSectionItemTpl2 =
+ "<tr><td><nobr>%1:</nobr></td><td/><td>%2</td></tr>";
+ static const char *sSectionItemTpl3 =
+ "<tr><td><nobr>%1</nobr></td><td/><td/></tr>";
+ static const char *sSectionItemTpl4 =
+ "<tr><td><a href='%2'><img src='%1'/></a></td></tr>";
+
+ /* Use the const ref on the basis of implicit QString constructor: */
+ const QString &strSectionTpl = enmType == DetailsElementType_Preview
+ ? sSectionBoldTpl2 : sSectionBoldTpl1;
+
+ /* Determine icon metric: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ const int iIconArea = iIconMetric * 1.375;
+
+ /* Acquire current snapshot machine if any: */
+ const CMachine comMachineOld = comSnapshot.isNotNull() ? comSnapshot.GetMachine() : comMachine;
+
+ /* Compose report: */
+ QString strReport;
+ QString strItem;
+ int iRowCount = 0;
+ switch (enmType)
+ {
+ case DetailsElementType_General:
+ {
+ /* Name: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Name", "details (general)"),
+ empReport(comMachine.GetName(), comMachineOld.GetName()));
+
+ /* Operating System: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Operating System", "details (general)"),
+ empReport(uiCommon().vmGuestOSTypeDescription(comMachine.GetOSTypeId()),
+ uiCommon().vmGuestOSTypeDescription(comMachineOld.GetOSTypeId())));
+
+ /* Location of the settings file: */
+ QString strSettingsFilePath = comMachine.GetSettingsFilePath();
+ QString strOldSettingsFilePath = comMachineOld.GetSettingsFilePath();
+ QString strSettingsFolder = !strSettingsFilePath.isEmpty() ?
+ QDir::toNativeSeparators(QFileInfo(strSettingsFilePath).absolutePath()) : QString();
+ QString strOldSettingsFolder = !strOldSettingsFilePath.isEmpty() ?
+ QDir::toNativeSeparators(QFileInfo(strOldSettingsFilePath).absolutePath()) : QString();
+
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Settings File Location", "details (general)"),
+ empReport(strSettingsFolder, strOldSettingsFolder));
+
+ /* Groups? */
+ const QString strGroups = groupReport(comMachine);
+ const QString strGroupsOld = groupReport(comMachineOld);
+ if (!strGroups.isNull())
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Groups", "details (general)"),
+ empReport(strGroups, strGroupsOld));
+ }
+
+ break;
+ }
+ case DetailsElementType_System:
+ {
+ /* Base Memory: */
+ ++iRowCount;
+ const QString strMemory = QApplication::translate("UIDetails", "%1 MB", "details").arg(comMachine.GetMemorySize());
+ const QString strMemoryOld = QApplication::translate("UIDetails", "%1 MB", "details").arg(comMachineOld.GetMemorySize());
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Base Memory", "details (system)"),
+ empReport(strMemory, strMemoryOld));
+
+ /* Processors? */
+ const int cCpu = comMachine.GetCPUCount();
+ const int cCpuOld = comMachineOld.GetCPUCount();
+ if (cCpu > 1)
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Processors", "details (system)"),
+ empReport(QString::number(cCpu), QString::number(cCpuOld)));
+ }
+
+ /* Execution Cap? */
+ const ULONG uExecutionCap = comMachine.GetCPUExecutionCap();
+ if (uExecutionCap < 100)
+ {
+ ++iRowCount;
+ const QString strExecutionCap = QApplication::translate("UIDetails", "%1%", "details").arg(uExecutionCap);
+ const QString strExecutionCapOld = QApplication::translate("UIDetails", "%1%", "details").arg(comMachineOld.GetCPUExecutionCap());
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Execution Cap", "details (system)"),
+ empReport(strExecutionCap, strExecutionCapOld));
+ }
+
+ /* Boot Order: */
+ ++iRowCount;
+ const QString strBootOrder = bootOrderReport(comMachine);
+ const QString strBootOrderOld = bootOrderReport(comMachineOld);
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Boot Order", "details (system)"),
+ empReport(strBootOrder, strBootOrderOld));
+
+ /* Chipset Type? */
+ const KChipsetType enmChipsetType = comMachine.GetChipsetType();
+ const KChipsetType enmChipsetTypeOld = comMachineOld.GetChipsetType();
+ if (enmChipsetType == KChipsetType_ICH9)
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Chipset Type", "details (system)"),
+ empReport(gpConverter->toString(enmChipsetType),
+ gpConverter->toString(enmChipsetTypeOld)));
+ }
+
+ /* EFI? */
+ const QString strEfiState = efiStateReport(comMachine);
+ const QString strEfiStateOld = efiStateReport(comMachineOld);
+ if (!strEfiState.isNull())
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "EFI", "details (system)"),
+ empReport(strEfiState, strEfiStateOld));
+ }
+
+ /* Acceleration? */
+ const QString strAcceleration = accelerationReport(comMachine);
+ const QString strAccelerationOld = accelerationReport(comMachineOld);
+ if (!strAcceleration.isNull())
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Acceleration", "details (system)"),
+ empReport(strAcceleration, strAccelerationOld));
+ }
+
+ break;
+ }
+ case DetailsElementType_Preview:
+ {
+ /* Preview: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl4).arg("details://thumbnail").arg("#thumbnail");
+
+ break;
+ }
+ case DetailsElementType_Display:
+ {
+ const CGraphicsAdapter &comGraphics = comMachine.GetGraphicsAdapter();
+ const CGraphicsAdapter &comGraphicsOld = comMachineOld.GetGraphicsAdapter();
+
+ /* Video Memory: */
+ ++iRowCount;
+ const QString strVram = QApplication::translate("UIDetails", "%1 MB", "details").arg(comGraphics.GetVRAMSize());
+ const QString strVramOld = QApplication::translate("UIDetails", "%1 MB", "details").arg(comGraphicsOld.GetVRAMSize());
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Video Memory", "details (display)"),
+ empReport(strVram, strVramOld));
+
+ /* Screens? */
+ const int cScreens = comGraphics.GetMonitorCount();
+ const int cScreensOld = comGraphicsOld.GetMonitorCount();
+ if (cScreens > 1)
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Screens", "details (display)"),
+ empReport(QString::number(cScreens), QString::number(cScreensOld)));
+ }
+
+ /* Scale-factor? */
+ const double uScaleFactor = scaleFactorReport(comMachine);
+ const double uScaleFactorOld = scaleFactorReport(comMachineOld);
+ if (uScaleFactor != 1.0)
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Scale-factor", "details (display)"),
+ empReport(QString::number(uScaleFactor, 'f', 2),
+ QString::number(uScaleFactorOld, 'f', 2)));
+ }
+
+ /* Graphics Controller: */
+ ++iRowCount;
+ const QString strGc = gpConverter->toString(comGraphics.GetGraphicsControllerType());
+ const QString strGcOld = gpConverter->toString(comGraphicsOld.GetGraphicsControllerType());
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Graphics Controller", "details (display)"),
+ empReport(strGc, strGcOld));
+
+ /* Acceleration? */
+ const QString strAcceleration = displayAccelerationReport(comGraphics);
+ const QString strAccelerationOld = displayAccelerationReport(comGraphicsOld);
+ if (!strAcceleration.isNull())
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Acceleration", "details (display)"),
+ empReport(strAcceleration, strAccelerationOld));
+ }
+
+ /* Remote Desktop Server: */
+ QStringList aVrdeReport = vrdeServerReport(comMachine);
+ QStringList aVrdeReportOld = vrdeServerReport(comMachineOld);
+ if (!aVrdeReport.isEmpty())
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Remote Desktop Server Port", "details (display/vrde)"),
+ empReport(aVrdeReport.value(0), aVrdeReportOld.value(0)));
+ }
+ else
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Remote Desktop Server", "details (display/vrde)"),
+ empReport(QApplication::translate("UIDetails", "Disabled", "details (display/vrde/VRDE server)"), aVrdeReportOld.isEmpty()));
+ }
+
+ /* Recording: */
+ QStringList aRecordingReport = recordingReport(comMachine);
+ QStringList aRecordingReportOld = recordingReport(comMachineOld);
+ if (!aRecordingReport.isEmpty())
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Recording File", "details (display/recording)"),
+ empReport(aRecordingReport.value(0), aRecordingReportOld.value(0)));
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Recording Attributes", "details (display/recording)"),
+ empReport(aRecordingReport.value(1), aRecordingReportOld.value(1)));
+ }
+ else
+ {
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Recording", "details (display/recording)"),
+ empReport(QApplication::translate("UIDetails", "Disabled", "details (display/recording)"), aRecordingReportOld.isEmpty()));
+ }
+
+ break;
+ }
+ case DetailsElementType_Storage:
+ {
+ /* Storage: */
+ QPair<QStringList, QList<QMap<QString, QString> > > report = storageReport(comMachine);
+ QStringList aControllers = report.first;
+ QList<QMap<QString, QString> > aAttachments = report.second;
+ QPair<QStringList, QList<QMap<QString, QString> > > reportOld = storageReport(comMachineOld);
+ QStringList aControllersOld = reportOld.first;
+ QList<QMap<QString, QString> > aAttachmentsOld = reportOld.second;
+
+ /* Iterate through storage controllers: */
+ for (int i = 0; i < aControllers.size(); ++i)
+ {
+ /* Add controller information: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl3).arg(empReport(aControllers.value(i), aControllersOld.value(i)));
+
+ /* Iterate through storage attachments: */
+ QMap<QString, QString> aCurrentAttachments = aAttachments.value(i);
+ QMap<QString, QString> aCurrentAttachmentsOld = aAttachmentsOld.value(i);
+ for (int j = 0; j < aCurrentAttachments.keys().size(); ++j)
+ {
+ const QString &strSlotInfo = empReport(aCurrentAttachments.keys().value(j),
+ aCurrentAttachmentsOld.keys().value(j));
+ const QString &strMediumInfo = empReport(aCurrentAttachments.value(aCurrentAttachments.keys().value(j)),
+ aCurrentAttachmentsOld.value(aCurrentAttachments.keys().value(j)));
+ /* Add attachment information: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(strSlotInfo, strMediumInfo);
+ }
+ }
+
+ /* Handle side-case: */
+ if (strItem.isNull())
+ {
+ /* Not Attached: */
+ ++iRowCount;
+ strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Not Attached", "details (storage)"), aControllersOld.isEmpty()));
+ }
+
+ break;
+ }
+ case DetailsElementType_Audio:
+ {
+ /* Audio: */
+ QStringList aReport = audioReport(comMachine);
+ QStringList aReportOld = audioReport(comMachineOld);
+
+ /* If there is something to report: */
+ if (!aReport.isEmpty())
+ {
+ /* Host Driver: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Host Driver", "details (audio)"),
+ empReport(aReport.value(0), aReportOld.value(0)));
+
+ /* Controller: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Controller", "details (audio)"),
+ empReport(aReport.value(1), aReportOld.value(1)));
+
+#ifdef VBOX_WITH_AUDIO_INOUT_INFO
+ /* Output: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Audio Output", "details (audio)"),
+ empReport(aReport.value(2), aReportOld.value(2)));
+
+ /* Input: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Audio Input", "details (audio)"),
+ empReport(aReport.value(3), aReportOld.value(3)));
+#endif /* VBOX_WITH_AUDIO_INOUT_INFO */
+ }
+
+ /* Handle side-case: */
+ if (strItem.isNull())
+ {
+ /* Disabled: */
+ ++iRowCount;
+ strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Disabled", "details (audio)"), aReportOld.isEmpty()));
+ }
+
+ break;
+ }
+ case DetailsElementType_Network:
+ {
+ /* Network: */
+ QStringList aReport = networkReport(comMachine);
+ QStringList aReportOld = networkReport(comMachineOld);
+
+ /* Iterate through network adapters: */
+ for (int i = 0; i < aReport.size(); ++i)
+ {
+ const QString &strAdapterInformation = aReport.value(i);
+ const QString &strAdapterInformationOld = aReportOld.value(i);
+ /* Add adapter information: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Adapter %1", "details (network)").arg(i + 1),
+ empReport(strAdapterInformation, strAdapterInformationOld));
+ }
+
+ /* Handle side-case: */
+ if (strItem.isNull())
+ {
+ /* Disabled: */
+ ++iRowCount;
+ strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Disabled", "details (network/adapter)"), aReportOld.isEmpty()));
+ }
+
+ break;
+ }
+ case DetailsElementType_Serial:
+ {
+ /* Serial: */
+ QStringList aReport = serialReport(comMachine);
+ QStringList aReportOld = serialReport(comMachineOld);
+
+ /* Iterate through serial ports: */
+ for (int i = 0; i < aReport.size(); ++i)
+ {
+ const QString &strPortInformation = aReport.value(i);
+ const QString &strPortInformationOld = aReportOld.value(i);
+ /* Add port information: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Port %1", "details (serial)").arg(i + 1),
+ empReport(strPortInformation, strPortInformationOld));
+ }
+
+ /* Handle side-case: */
+ if (strItem.isNull())
+ {
+ /* Disabled: */
+ ++iRowCount;
+ strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Disabled", "details (serial)"), aReportOld.isEmpty()));
+ }
+
+ break;
+ }
+ case DetailsElementType_USB:
+ {
+ /* USB: */
+ QStringList aReport = usbReport(comMachine);
+ QStringList aReportOld = usbReport(comMachineOld);
+
+ /* If there is something to report: */
+ if (!aReport.isEmpty())
+ {
+ /* USB Controller: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "USB Controller", "details (usb)"),
+ empReport(aReport.value(0), aReportOld.value(0)));
+
+ /* Device Filters: */
+ ++iRowCount;
+ strItem += QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Device Filters", "details (usb)"),
+ empReport(aReport.value(1), aReportOld.value(1)));
+ }
+
+ /* Handle side-case: */
+ if (strItem.isNull())
+ {
+ /* Disabled: */
+ ++iRowCount;
+ strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "Disabled", "details (usb)"), aReportOld.isEmpty()));
+ }
+
+ break;
+ }
+ case DetailsElementType_SF:
+ {
+ /* Shared Folders: */
+ const ulong cFolders = comMachine.GetSharedFolders().size();
+ const ulong cFoldersOld = comMachineOld.GetSharedFolders().size();
+ if (cFolders > 0)
+ {
+ ++iRowCount;
+ strItem = QString(sSectionItemTpl2).arg(QApplication::translate("UIDetails", "Shared Folders", "details (shared folders)"),
+ empReport(QString::number(cFolders), QString::number(cFoldersOld)));
+ }
+ else
+ {
+ ++iRowCount;
+ strItem = QString(sSectionItemTpl1).arg(empReport(QApplication::translate("UIDetails", "None", "details (shared folders)"), cFoldersOld == 0));
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Append report: */
+ if (enmType != DetailsElementType_Preview || !m_pixmapScreenshot.isNull())
+ strReport += strSectionTpl
+ .arg(1 + iRowCount) /* rows */
+ .arg(QString("details://%1").arg(gpConverter->toInternalString(enmType)), /* icon */
+ QString::number(iIconArea), /* icon area */
+ QString("%1:").arg(gpConverter->toString(enmType)), /* title */
+ strItem /* items */);
+
+ /* Return report as table: */
+ return QString(sTableTpl).arg(strReport);
+}
+
+/* static */
+QString UISnapshotDetailsWidget::groupReport(const CMachine &comMachine)
+{
+ /* Prepare report: */
+ QStringList aReport = comMachine.GetGroups().toList();
+ /* Do not show groups for machine which is in root group only: */
+ if (aReport.size() == 1)
+ aReport.removeAll("/");
+ /* For all groups => trim first '/' symbol: */
+ for (int i = 0; i < aReport.size(); ++i)
+ {
+ QString &strGroup = aReport[i];
+ if (strGroup.startsWith("/") && strGroup != "/")
+ strGroup.remove(0, 1);
+ }
+ /* Compose and return report: */
+ return aReport.isEmpty() ? QString() : aReport.join(", ");
+}
+
+/* static */
+QString UISnapshotDetailsWidget::bootOrderReport(const CMachine &comMachine)
+{
+ /* Prepare report: */
+ QStringList aReport;
+ /* Iterate through boot device types: */
+ for (ulong i = 1; i <= uiCommon().virtualBox().GetSystemProperties().GetMaxBootPosition(); ++i)
+ {
+ const KDeviceType enmDevice = comMachine.GetBootOrder(i);
+ if (enmDevice != KDeviceType_Null)
+ aReport << gpConverter->toString(enmDevice);
+ }
+ /* Make sure report contains at least something: */
+ if (aReport.isEmpty())
+ aReport << gpConverter->toString(KDeviceType_Null);
+ /* Compose and return report: */
+ return aReport.isEmpty() ? QString() : aReport.join(", ");
+}
+
+/* static */
+QString UISnapshotDetailsWidget::efiStateReport(const CMachine &comMachine)
+{
+ /* Prepare report: */
+ QString strReport;
+ switch (comMachine.GetFirmwareType())
+ {
+ case KFirmwareType_EFI:
+ case KFirmwareType_EFI32:
+ case KFirmwareType_EFI64:
+ case KFirmwareType_EFIDUAL:
+ {
+ strReport = QApplication::translate("UIDetails", "Enabled", "details (system/EFI)");
+ break;
+ }
+ default:
+ {
+ /* strReport = */ QApplication::translate("UIDetails", "Disabled", "details (system/EFI)");
+ break;
+ }
+ }
+ /* Return report: */
+ return strReport;
+}
+
+/* static */
+QString UISnapshotDetailsWidget::accelerationReport(const CMachine &comMachine)
+{
+ /* Prepare report: */
+ QStringList aReport;
+ /* VT-x/AMD-V and Nested Paging? */
+ if (uiCommon().host().GetProcessorFeature(KProcessorFeature_HWVirtEx))
+ {
+ /* VT-x/AMD-V? */
+ if (comMachine.GetHWVirtExProperty(KHWVirtExPropertyType_Enabled))
+ {
+ aReport << QApplication::translate("UIDetails", "VT-x/AMD-V", "details (system)");
+ /* Nested Paging? */
+ if (comMachine.GetHWVirtExProperty(KHWVirtExPropertyType_NestedPaging))
+ aReport << QApplication::translate("UIDetails", "Nested Paging", "details (system)");
+ }
+ }
+ /* PAE/NX? */
+ if (comMachine.GetCPUProperty(KCPUPropertyType_PAE))
+ aReport << QApplication::translate("UIDetails", "PAE/NX", "details (system)");
+ /* Paravirtualization Interface? */
+ switch (comMachine.GetEffectiveParavirtProvider())
+ {
+ case KParavirtProvider_Minimal: aReport << QApplication::translate("UIDetails", "Minimal Paravirtualization", "details (system)"); break;
+ case KParavirtProvider_HyperV: aReport << QApplication::translate("UIDetails", "Hyper-V Paravirtualization", "details (system)"); break;
+ case KParavirtProvider_KVM: aReport << QApplication::translate("UIDetails", "KVM Paravirtualization", "details (system)"); break;
+ default: break;
+ }
+ /* Compose and return report: */
+ return aReport.isEmpty() ? QString() : aReport.join(", ");
+}
+
+/* static */
+double UISnapshotDetailsWidget::scaleFactorReport(CMachine comMachine)
+{
+ // WORKAROUND:
+ // IMachine::GetExtraData still non-const..
+ CMachine comExtraDataMachine = comMachine;
+ /* Prepare report: */
+ const QString strScaleFactor = comExtraDataMachine.GetExtraData(UIExtraDataDefs::GUI_ScaleFactor);
+ /* Try to convert loaded data to double: */
+ bool fOk = false;
+ double dReport = strScaleFactor.toDouble(&fOk);
+ /* Invent the default value: */
+ if (!fOk || !dReport)
+ dReport = 1.0;
+ /* Return report: */
+ return dReport;
+}
+
+/* static */
+QString UISnapshotDetailsWidget::displayAccelerationReport(CGraphicsAdapter comGraphics)
+{
+ /* Prepare report: */
+ QStringList aReport;
+ /* 3D Acceleration? */
+ if (comGraphics.GetAccelerate3DEnabled())
+ aReport << QApplication::translate("UIDetails", "3D", "details (display)");
+ /* Compose and return report: */
+ return aReport.isEmpty() ? QString() : aReport.join(", ");
+}
+
+/* static */
+QStringList UISnapshotDetailsWidget::vrdeServerReport(CMachine comMachine)
+{
+ /* Prepare report: */
+ QStringList aReport;
+ /* Acquire VRDE server: */
+ const CVRDEServer &comServer = comMachine.GetVRDEServer();
+ if (comServer.GetEnabled())
+ {
+ /* Remote Desktop Server Port: */
+ aReport << comServer.GetVRDEProperty("TCP/Ports");
+ }
+ /* Return report: */
+ return aReport;
+}
+
+/* static */
+QStringList UISnapshotDetailsWidget::recordingReport(CMachine comMachine)
+{
+ /* Prepare report: */
+ QStringList aReport;
+ /* Acquire recording status: */
+ CRecordingSettings comRecordingSettings = comMachine.GetRecordingSettings();
+ /* For now all screens have the same config: */
+ CRecordingScreenSettings comRecordingScreen0Settings = comRecordingSettings.GetScreenSettings(0);
+ if (comRecordingScreen0Settings.GetEnabled())
+ {
+ /* Recording file: */
+ aReport << comRecordingScreen0Settings.GetFilename();
+ /* Recording attributes: */
+ aReport << QApplication::translate("UIDetails", "Frame Size: %1x%2, Frame Rate: %3fps, Bit Rate: %4kbps")
+ .arg(comRecordingScreen0Settings.GetVideoWidth())
+ .arg(comRecordingScreen0Settings.GetVideoHeight())
+ .arg(comRecordingScreen0Settings.GetVideoFPS())
+ .arg(comRecordingScreen0Settings.GetVideoRate());
+ }
+ /* Return report: */
+ return aReport;
+}
+
+/* static */
+QPair<QStringList, QList<QMap<QString, QString> > > UISnapshotDetailsWidget::storageReport(CMachine comMachine)
+{
+ /* Prepare report: */
+ QStringList aControllers;
+ QList<QMap<QString, QString> > aAttachments;
+ /* Iterate through machine storage controllers: */
+ foreach (const CStorageController &comController, comMachine.GetStorageControllers())
+ {
+ /* Append controller information: */
+ aControllers << QApplication::translate("UIMachineSettingsStorage", "Controller: %1").arg(comController.GetName());
+
+ /* Prepare attachment information: */
+ QMap<QString, QString> mapAttachments;
+ /* Iterate through machine storage attachments: */
+ foreach (const CMediumAttachment &comAttachment, comMachine.GetMediumAttachmentsOfController(comController.GetName()))
+ {
+ /* Prepare current slot information: */
+ const QString strSlotInfo = QString("&nbsp;&nbsp;")
+ + gpConverter->toString(StorageSlot(comController.GetBus(),
+ comAttachment.GetPort(),
+ comAttachment.GetDevice()))
+ + ( comAttachment.GetType() == KDeviceType_DVD
+ ? QApplication::translate("UIDetails", "[Optical Drive]", "details (storage)").prepend(' ')
+ : QString());
+
+ /* Prepare current medium information: */
+ const QString strMediumInfo = comAttachment.isOk()
+ ? wipeHtmlStuff(uiCommon().storageDetails(comAttachment.GetMedium(), false))
+ : QString();
+
+ /* Cache current slot/medium information: */
+ if (!strMediumInfo.isNull())
+ mapAttachments.insert(strSlotInfo, strMediumInfo);
+ }
+ /* Append attachment information: */
+ aAttachments << mapAttachments;
+ }
+ /* Compose and return report: */
+ return qMakePair(aControllers, aAttachments);
+}
+
+/* static */
+QStringList UISnapshotDetailsWidget::audioReport(CMachine comMachine)
+{
+ /* Prepare report: */
+ QStringList aReport;
+ /* Acquire audio adapter: */
+ const CAudioSettings comAudioSettings = comMachine.GetAudioSettings();
+ const CAudioAdapter &comAdapter = comAudioSettings.GetAdapter();
+ if (comAdapter.GetEnabled())
+ {
+ /* Host Driver: */
+ aReport << gpConverter->toString(comAdapter.GetAudioDriver());
+
+ /* Controller: */
+ aReport << gpConverter->toString(comAdapter.GetAudioController());
+
+#ifdef VBOX_WITH_AUDIO_INOUT_INFO
+ /* Output: */
+ aReport << ( comAdapter.GetEnabledOut()
+ ? QApplication::translate("UIDetails", "Enabled", "details (audio/output)")
+ : QApplication::translate("UIDetails", "Disabled", "details (audio/output)"));
+
+ /* Input: */
+ aReport << ( comAdapter.GetEnabledIn()
+ ? QApplication::translate("UIDetails", "Enabled", "details (audio/input)")
+ : QApplication::translate("UIDetails", "Disabled", "details (audio/input)"));
+#endif /* VBOX_WITH_AUDIO_INOUT_INFO */
+ }
+ /* Return report: */
+ return aReport;
+}
+
+/* static */
+QStringList UISnapshotDetailsWidget::networkReport(CMachine comMachine)
+{
+ /* Prepare report: */
+ QStringList aReport;
+ /* Iterate through machine network adapters: */
+ const ulong iCount = uiCommon().virtualBox().GetSystemProperties().GetMaxNetworkAdapters(comMachine.GetChipsetType());
+ for (ulong iSlot = 0; iSlot < iCount; ++iSlot)
+ {
+ /* Get current network adapter: */
+ const CNetworkAdapter &comAdapter = comMachine.GetNetworkAdapter(iSlot);
+ if (comAdapter.GetEnabled())
+ {
+ /* Use adapter type string as template: */
+ QString strInfo = gpConverter->toString(comAdapter.GetAdapterType()).replace(QRegularExpression("\\s\\(.+\\)"), " (%1)");
+ /* Don't use the adapter type string for types that have an additional
+ * symbolic network/interface name field, use this name instead: */
+ const KNetworkAttachmentType enmType = comAdapter.GetAttachmentType();
+ switch (enmType)
+ {
+ case KNetworkAttachmentType_Bridged:
+ strInfo = strInfo.arg(QApplication::translate("UIDetails", "Bridged Adapter, %1", "details (network)")
+ .arg(comAdapter.GetBridgedInterface()));
+ break;
+ case KNetworkAttachmentType_Internal:
+ strInfo = strInfo.arg(QApplication::translate("UIDetails", "Internal Network, '%1'", "details (network)")
+ .arg(comAdapter.GetInternalNetwork()));
+ break;
+ case KNetworkAttachmentType_HostOnly:
+ strInfo = strInfo.arg(QApplication::translate("UIDetails", "Host-only Adapter, '%1'", "details (network)")
+ .arg(comAdapter.GetHostOnlyInterface()));
+ break;
+ case KNetworkAttachmentType_Generic:
+ {
+ QString strGenericDriverProperties(summarizeGenericProperties(comAdapter));
+ strInfo = strInfo.arg( strGenericDriverProperties.isNull()
+ ? strInfo.arg(QApplication::translate("UIDetails", "Generic Driver, '%1'", "details (network)")
+ .arg(comAdapter.GetGenericDriver()))
+ : strInfo.arg(QApplication::translate("UIDetails", "Generic Driver, '%1' { %2 }", "details (network)")
+ .arg(comAdapter.GetGenericDriver(), strGenericDriverProperties)));
+ break;
+ }
+ case KNetworkAttachmentType_NATNetwork:
+ strInfo = strInfo.arg(QApplication::translate("UIDetails", "NAT Network, '%1'", "details (network)")
+ .arg(comAdapter.GetNATNetwork()));
+ break;
+ default:
+ strInfo = strInfo.arg(gpConverter->toString(enmType));
+ break;
+ }
+ /* Append adapter information: */
+ aReport << strInfo;
+ }
+ }
+ /* Return report: */
+ return aReport;
+}
+
+/* static */
+QStringList UISnapshotDetailsWidget::serialReport(CMachine comMachine)
+{
+ /* Prepare report: */
+ QStringList aReport;
+ /* Iterate through machine serial ports: */
+ const ulong iCount = uiCommon().virtualBox().GetSystemProperties().GetSerialPortCount();
+ for (ulong iSlot = 0; iSlot < iCount; ++iSlot)
+ {
+ /* Get current serial port: */
+ const CSerialPort &comPort = comMachine.GetSerialPort(iSlot);
+ if (comPort.GetEnabled())
+ {
+ /* Determine port mode: */
+ const KPortMode enmMode = comPort.GetHostMode();
+ /* Compose the data: */
+ QStringList aInfo;
+ aInfo << UITranslator::toCOMPortName(comPort.GetIRQ(), comPort.GetIOBase());
+ if ( enmMode == KPortMode_HostPipe
+ || enmMode == KPortMode_HostDevice
+ || enmMode == KPortMode_TCP
+ || enmMode == KPortMode_RawFile)
+ aInfo << QString("%1 (<nobr>%2</nobr>)")
+ .arg(gpConverter->toString(enmMode))
+ .arg(QDir::toNativeSeparators(comPort.GetPath()));
+ else
+ aInfo << gpConverter->toString(enmMode);
+ /* Append port information: */
+ aReport << aInfo.join(", ");
+ }
+ }
+ /* Return report: */
+ return aReport;
+}
+
+/* static */
+QStringList UISnapshotDetailsWidget::usbReport(CMachine comMachine)
+{
+ /* Prepare report: */
+ QStringList aReport;
+ /* Acquire USB filters object: */
+ const CUSBDeviceFilters &comFiltersObject = comMachine.GetUSBDeviceFilters();
+ if ( !comFiltersObject.isNull()
+ && comMachine.GetUSBProxyAvailable())
+ {
+ /* Acquire USB controllers: */
+ const CUSBControllerVector aControllers = comMachine.GetUSBControllers();
+ if (!aControllers.isEmpty())
+ {
+ /* USB Controller: */
+ QStringList aControllerList;
+ foreach (const CUSBController &comController, aControllers)
+ aControllerList << gpConverter->toString(comController.GetType());
+ aReport << aControllerList.join(", ");
+
+ /* Device Filters: */
+ const CUSBDeviceFilterVector &aFilters = comFiltersObject.GetDeviceFilters();
+ uint cActive = 0;
+ foreach (const CUSBDeviceFilter &comFilter, aFilters)
+ if (comFilter.GetActive())
+ ++cActive;
+ aReport << QApplication::translate("UIDetails", "%1 (%2 active)", "details (usb)")
+ .arg(aFilters.size()).arg(cActive);
+ }
+ }
+ /* Return report: */
+ return aReport;
+}
+
+/* static */
+QString UISnapshotDetailsWidget::wipeHtmlStuff(const QString &strString)
+{
+ return QString(strString).remove(QRegularExpression("<i>|</i>|<b>|</b>"));
+}
+
+/* static */
+QString UISnapshotDetailsWidget::empReport(const QString &strValue, const QString &strOldValue)
+{
+ return strValue == strOldValue ? strValue : QString("<u>%1</u>").arg(strValue);
+}
+
+/* static */
+QString UISnapshotDetailsWidget::empReport(const QString &strValue, bool fIgnore)
+{
+ return fIgnore ? strValue : QString("<u>%1</u>").arg(strValue);
+}
+
+/* static */
+QString UISnapshotDetailsWidget::summarizeGenericProperties(const CNetworkAdapter &comNetwork)
+{
+ QVector<QString> names;
+ QVector<QString> props;
+ props = comNetwork.GetProperties(QString(), names);
+ QString strResult;
+ for (int i = 0; i < names.size(); ++i)
+ {
+ strResult += names[i] + "=" + props[i];
+ if (i < names.size() - 1)
+ strResult += ", ";
+ }
+ return strResult;
+}
+
+#include "UISnapshotDetailsWidget.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotDetailsWidget.h b/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotDetailsWidget.h
new file mode 100644
index 00000000..35437e64
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotDetailsWidget.h
@@ -0,0 +1,263 @@
+/* $Id: UISnapshotDetailsWidget.h $ */
+/** @file
+ * VBox Qt GUI - UISnapshotDetailsWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_snapshots_UISnapshotDetailsWidget_h
+#define FEQT_INCLUDED_SRC_snapshots_UISnapshotDetailsWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGraphicsAdapter.h"
+#include "CMachine.h"
+#include "CSnapshot.h"
+
+/* Forward declarations: */
+class CNetworkAdapter;
+class QGridLayout;
+class QLabel;
+class QLineEdit;
+class QScrollArea;
+class QTabWidget;
+class QTextEdit;
+class QVBoxLayout;
+class QWidget;
+class QIDialogButtonBox;
+class UISnapshotDetailsElement;
+
+
+/** Snapshot pane: Snapshot data. */
+class UIDataSnapshot
+{
+public:
+
+ /** Constructs data. */
+ UIDataSnapshot()
+ : m_strName(QString())
+ , m_strDescription(QString())
+ {}
+
+ /** Returns name. */
+ QString name() const { return m_strName; }
+ /** Defines @a strName. */
+ void setName(const QString &strName) { m_strName = strName; }
+
+ /** Returns description. */
+ QString description() const { return m_strDescription; }
+ /** Defines @a strDescription. */
+ void setDescription(const QString &strDescription) { m_strDescription = strDescription; }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataSnapshot &other) const
+ {
+ return true
+ && (m_strName == other.m_strName)
+ && (m_strDescription == other.m_strDescription)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataSnapshot &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataSnapshot &other) const { return !equal(other); }
+
+protected:
+
+ /** Holds the name. */
+ QString m_strName;
+ /** Holds the description. */
+ QString m_strDescription;
+};
+
+
+/** QWidget extension providing GUI with snapshot details-widget. */
+class UISnapshotDetailsWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about data change accepted and should be applied. */
+ void sigDataChangeAccepted();
+
+public:
+
+ /** Constructs snapshot details-widget passing @a pParent to the base-class. */
+ UISnapshotDetailsWidget(QWidget *pParent = 0);
+
+ /** Returns the snapshot data. */
+ const UIDataSnapshot &data() const { return m_newData; }
+ /** Defines the @a comMachine. */
+ void setData(const CMachine &comMachine);
+ /** Defines the @a comSnapshot and it's @a data. */
+ void setData(const UIDataSnapshot &data, const CSnapshot &comSnapshot);
+ /** Clears the snapshot data. */
+ void clearData();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** Handles buttons translation. */
+ void retranslateButtons();
+
+private slots:
+
+ /** Handles snapshot name change. */
+ void sltHandleNameChange();
+ /** Handles snapshot description change. */
+ void sltHandleDescriptionChange();
+
+ /** Handles snapshot details anchor clicks. */
+ void sltHandleAnchorClicked(const QUrl &link);
+
+ /** Handles snapshot details change accepting. */
+ void sltHandleChangeAccepted();
+ /** Handles snapshot details change rejecting. */
+ void sltHandleChangeRejected();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares tab-widget. */
+ void prepareTabWidget();
+ /** Prepares 'Options' tab. */
+ void prepareTabOptions();
+ /** Prepares 'Details' tab. */
+ void prepareTabDetails();
+
+ /** Creates details element of passed @a enmType. */
+ static UISnapshotDetailsElement *createDetailsElement(DetailsElementType enmType);
+
+ /** Loads snapshot data. */
+ void loadSnapshotData();
+
+ /** Revalidates changes for passed @a pWidget. */
+ void revalidate(QWidget *pWidget = 0);
+ /** Retranslates validation for passed @a pWidget. */
+ void retranslateValidation(QWidget *pWidget = 0);
+ /** Updates button states. */
+ void updateButtonStates();
+
+ /** Returns details report of requested @a enmType for a given @a comMachine. */
+ QString detailsReport(DetailsElementType enmType, const CMachine &comMachine, const CSnapshot &comSnapshot = CSnapshot()) const;
+
+ /** Acquires @a comMachine group report. */
+ static QString groupReport(const CMachine &comMachine);
+ /** Acquires @a comMachine boot order report. */
+ static QString bootOrderReport(const CMachine &comMachine);
+ /** Acquires @a comMachine efi state report. */
+ static QString efiStateReport(const CMachine &comMachine);
+ /** Acquires @a comMachine acceleration report. */
+ static QString accelerationReport(const CMachine &comMachine);
+ /** Acquires @a comMachine scale-factor report. */
+ static double scaleFactorReport(CMachine comMachine);
+ /** Acquires @a comMachine display acceleration report. */
+ static QString displayAccelerationReport(CGraphicsAdapter comGraphics);
+ /** Acquires @a comMachine VRDE server report. */
+ static QStringList vrdeServerReport(CMachine comMachine);
+ /** Acquires @a comMachine recording report. */
+ static QStringList recordingReport(CMachine comMachine);
+ /** Acquires @a comMachine storage report. */
+ static QPair<QStringList, QList<QMap<QString, QString> > > storageReport(CMachine comMachine);
+ /** Acquires @a comMachine audio report. */
+ static QStringList audioReport(CMachine comMachine);
+ /** Acquires @a comMachine network report. */
+ static QStringList networkReport(CMachine comMachine);
+ /** Acquires @a comMachine serial report. */
+ static QStringList serialReport(CMachine comMachine);
+ /** Acquires @a comMachine usb report. */
+ static QStringList usbReport(CMachine comMachine);
+
+ /** Wipes the HTML stuff from the passed @a strString. */
+ static QString wipeHtmlStuff(const QString &strString);
+
+ /** Prepares emhasized report for a given @a strValue, comparing to @a strOldValue. */
+ static QString empReport(const QString &strValue, const QString &strOldValue);
+ /** Prepares emhasized report for a given @a strValue, depending on @a fDo flag. */
+ static QString empReport(const QString &strValue, bool fIgnore);
+
+ /** Summarizes generic properties. */
+ static QString summarizeGenericProperties(const CNetworkAdapter &adapter);
+
+ /** Holds the machine object to load data from. */
+ CMachine m_comMachine;
+ /** Holds the snapshot object to load data from. */
+ CSnapshot m_comSnapshot;
+
+ /** Holds the old data copy. */
+ UIDataSnapshot m_oldData;
+ /** Holds the new data copy. */
+ UIDataSnapshot m_newData;
+
+ /** Holds the cached screenshot. */
+ QPixmap m_pixmapScreenshot;
+
+ /** Holds the tab-widget instance. */
+ QTabWidget *m_pTabWidget;
+
+ /** Holds the 'Options' layout instance. */
+ QGridLayout *m_pLayoutOptions;
+
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the name editor instance. */
+ QLineEdit *m_pEditorName;
+ /** Holds the name error pane. */
+ QLabel *m_pErrorPaneName;
+
+ /** Holds the description label instance. */
+ QLabel *m_pLabelDescription;
+ /** Holds the description editor instance. */
+ QTextEdit *m_pBrowserDescription;
+ /** Holds the description error pane. */
+ QLabel *m_pErrorPaneDescription;
+
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+
+ /** Holds the 'Details' layout instance. */
+ QVBoxLayout *m_pLayoutDetails;
+
+ /** Holds the details scroll-area instance. */
+ QScrollArea *m_pScrollAreaDetails;
+
+ /** Holds the details element map. */
+ QMap<DetailsElementType, UISnapshotDetailsElement*> m_details;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_snapshots_UISnapshotDetailsWidget_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotPane.cpp b/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotPane.cpp
new file mode 100644
index 00000000..0186ba5a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotPane.cpp
@@ -0,0 +1,1901 @@
+/* $Id: UISnapshotPane.cpp $ */
+/** @file
+ * VBox Qt GUI - UISnapshotPane class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QApplication>
+#include <QDateTime>
+#include <QHeaderView>
+#include <QIcon>
+#include <QMenu>
+#include <QPointer>
+#include <QReadWriteLock>
+#include <QRegExp>
+#include <QScrollBar>
+#include <QTimer>
+#include <QVBoxLayout>
+#include <QWriteLocker>
+
+/* GUI includes: */
+#include "QIMessageBox.h"
+#include "QIToolBar.h"
+#include "QITreeWidget.h"
+#include "UIActionPoolManager.h"
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINotificationCenter.h"
+#include "UISnapshotDetailsWidget.h"
+#include "UISnapshotPane.h"
+#include "UITakeSnapshotDialog.h"
+#include "UITranslator.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualMachineItem.h"
+#include "UIVirtualMachineItemLocal.h"
+#include "UIWizardCloneVM.h"
+
+/* COM includes: */
+#include "CConsole.h"
+
+
+/** Snapshot tree column tags. */
+enum
+{
+ Column_Name,
+ Column_Taken,
+ Column_Max,
+};
+
+
+/** QITreeWidgetItem subclass for snapshots items. */
+class UISnapshotItem : public QITreeWidgetItem, public UIDataSnapshot
+{
+ Q_OBJECT;
+
+public:
+
+ /** Casts QTreeWidgetItem* to UISnapshotItem* if possible. */
+ static UISnapshotItem *toSnapshotItem(QTreeWidgetItem *pItem);
+ /** Casts const QTreeWidgetItem* to const UISnapshotItem* if possible. */
+ static const UISnapshotItem *toSnapshotItem(const QTreeWidgetItem *pItem);
+
+ /** Constructs normal snapshot item (child of tree-widget). */
+ UISnapshotItem(UISnapshotPane *pSnapshotWidget,
+ QITreeWidget *pTreeWidget,
+ const CSnapshot &comSnapshot,
+ bool fExtendedNameRequired);
+ /** Constructs normal snapshot item (child of tree-widget-item). */
+ UISnapshotItem(UISnapshotPane *pSnapshotWidget,
+ QITreeWidgetItem *pRootItem,
+ const CSnapshot &comSnapshot);
+
+ /** Constructs "current state" item (child of tree-widget). */
+ UISnapshotItem(UISnapshotPane *pSnapshotWidget,
+ QITreeWidget *pTreeWidget,
+ const CMachine &comMachine,
+ bool fExtendedNameRequired);
+ /** Constructs "current state" item (child of tree-widget-item). */
+ UISnapshotItem(UISnapshotPane *pSnapshotWidget,
+ QITreeWidgetItem *pRootItem,
+ const CMachine &comMachine);
+
+ /** Returns item machine. */
+ CMachine machine() const { return m_comMachine; }
+ /** Returns item machine ID. */
+ QUuid machineID() const { return m_uMachineID; }
+ /** Returns item snapshot. */
+ CSnapshot snapshot() const { return m_comSnapshot; }
+ /** Returns item snapshot ID. */
+ QUuid snapshotID() const { return m_uSnapshotID; }
+
+ /** Returns whether this is the "current state" item. */
+ bool isCurrentStateItem() const { return m_fCurrentStateItem; }
+
+ /** Returns whether this is the "current snapshot" item. */
+ bool isCurrentSnapshotItem() const { return m_fCurrentSnapshotItem; }
+ /** Defines whether this is the @a fCurrent snapshot item. */
+ void setCurrentSnapshotItem(bool fCurrent);
+
+ /** Calculates and returns the current item level. */
+ int level() const;
+
+ /** Recaches the item's contents. */
+ void recache();
+
+ /** Returns current machine state. */
+ KMachineState machineState() const;
+ /** Defines current machine @a enmState. */
+ void setMachineState(KMachineState enmState);
+
+ /** Updates item age. */
+ SnapshotAgeFormat updateAge();
+
+protected:
+
+ /** Recaches item tool-tip. */
+ void recacheToolTip();
+
+private:
+
+ /** Holds whether this item requires extended name. */
+ bool m_fExtendedNameRequired;
+
+ /** Holds the pointer to the snapshot-widget this item belongs to. */
+ QPointer<UISnapshotPane> m_pSnapshotWidget;
+
+ /** Holds whether this is a "current state" item. */
+ bool m_fCurrentStateItem;
+ /** Holds whether this is a "current snapshot" item. */
+ bool m_fCurrentSnapshotItem;
+
+ /** Holds the snapshot COM wrapper. */
+ CSnapshot m_comSnapshot;
+ /** Holds the machine COM wrapper. */
+ CMachine m_comMachine;
+
+ /** Holds the machine ID. */
+ QUuid m_uMachineID;
+ /** Holds the "current snapshot" ID. */
+ QUuid m_uSnapshotID;
+ /** Holds whether the "current snapshot" is online one. */
+ bool m_fOnline;
+
+ /** Holds the item timestamp. */
+ QDateTime m_timestamp;
+
+ /** Holds whether the current state is modified. */
+ bool m_fCurrentStateModified;
+ /** Holds the cached machine state. */
+ KMachineState m_enmMachineState;
+};
+
+
+/** QScrollBar subclass for snapshots widget. */
+class UISnapshotScrollBar : public QScrollBar
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notify listeners about our visibility changed. */
+ void sigNotifyAboutVisibilityChange();
+
+public:
+
+ /** Constructs scroll-bar passing @a enmOrientation and @a pParent to the base-class. */
+ UISnapshotScrollBar(Qt::Orientation enmOrientation, QWidget *pParent = 0);
+
+protected:
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+};
+
+
+/** QITreeWidget subclass for snapshots items. */
+class UISnapshotTree : public QITreeWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notify listeners about one of scroll-bars visibility changed. */
+ void sigNotifyAboutScrollBarVisibilityChange();
+
+public:
+
+ /** Constructs snapshot tree passing @a pParent to the base-class. */
+ UISnapshotTree(QWidget *pParent);
+};
+
+
+/*********************************************************************************************************************************
+* Class UISnapshotItem implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UISnapshotItem *UISnapshotItem::toSnapshotItem(QTreeWidgetItem *pItem)
+{
+ /* Get QITreeWidgetItem item first: */
+ QITreeWidgetItem *pIItem = QITreeWidgetItem::toItem(pItem);
+ if (!pIItem)
+ return 0;
+
+ /* Return casted UISnapshotItem then: */
+ return qobject_cast<UISnapshotItem*>(pIItem);
+}
+
+/* static */
+const UISnapshotItem *UISnapshotItem::toSnapshotItem(const QTreeWidgetItem *pItem)
+{
+ /* Get QITreeWidgetItem item first: */
+ const QITreeWidgetItem *pIItem = QITreeWidgetItem::toItem(pItem);
+ if (!pIItem)
+ return 0;
+
+ /* Return casted UISnapshotItem then: */
+ return qobject_cast<const UISnapshotItem*>(pIItem);
+}
+
+UISnapshotItem::UISnapshotItem(UISnapshotPane *pSnapshotWidget,
+ QITreeWidget *pTreeWidget,
+ const CSnapshot &comSnapshot,
+ bool fExtendedNameRequired)
+ : QITreeWidgetItem(pTreeWidget)
+ , m_fExtendedNameRequired(fExtendedNameRequired)
+ , m_pSnapshotWidget(pSnapshotWidget)
+ , m_fCurrentStateItem(false)
+ , m_fCurrentSnapshotItem(false)
+ , m_comSnapshot(comSnapshot)
+ , m_fOnline(false)
+ , m_fCurrentStateModified(false)
+ , m_enmMachineState(KMachineState_Null)
+{
+}
+
+UISnapshotItem::UISnapshotItem(UISnapshotPane *pSnapshotWidget,
+ QITreeWidgetItem *pRootItem,
+ const CSnapshot &comSnapshot)
+ : QITreeWidgetItem(pRootItem)
+ , m_fExtendedNameRequired(false)
+ , m_pSnapshotWidget(pSnapshotWidget)
+ , m_fCurrentStateItem(false)
+ , m_fCurrentSnapshotItem(false)
+ , m_comSnapshot(comSnapshot)
+ , m_fOnline(false)
+ , m_fCurrentStateModified(false)
+ , m_enmMachineState(KMachineState_Null)
+{
+}
+
+UISnapshotItem::UISnapshotItem(UISnapshotPane *pSnapshotWidget,
+ QITreeWidget *pTreeWidget,
+ const CMachine &comMachine,
+ bool fExtendedNameRequired)
+ : QITreeWidgetItem(pTreeWidget)
+ , m_fExtendedNameRequired(fExtendedNameRequired)
+ , m_pSnapshotWidget(pSnapshotWidget)
+ , m_fCurrentStateItem(true)
+ , m_fCurrentSnapshotItem(false)
+ , m_comMachine(comMachine)
+ , m_fOnline(false)
+ , m_fCurrentStateModified(false)
+ , m_enmMachineState(KMachineState_Null)
+{
+ /* Set the bold font state
+ * for "current state" item: */
+ QFont myFont = font(Column_Name);
+ myFont.setBold(true);
+ setFont(Column_Name, myFont);
+
+ /* Fetch current machine state: */
+ setMachineState(m_comMachine.GetState());
+}
+
+UISnapshotItem::UISnapshotItem(UISnapshotPane *pSnapshotWidget,
+ QITreeWidgetItem *pRootItem,
+ const CMachine &comMachine)
+ : QITreeWidgetItem(pRootItem)
+ , m_fExtendedNameRequired(false)
+ , m_pSnapshotWidget(pSnapshotWidget)
+ , m_fCurrentStateItem(true)
+ , m_fCurrentSnapshotItem(false)
+ , m_comMachine(comMachine)
+ , m_fOnline(false)
+ , m_fCurrentStateModified(false)
+ , m_enmMachineState(KMachineState_Null)
+{
+ /* Set the bold font state
+ * for "current state" item: */
+ QFont myFont = font(Column_Name);
+ myFont.setBold(true);
+ setFont(Column_Name, myFont);
+
+ /* Fetch current machine state: */
+ setMachineState(m_comMachine.GetState());
+}
+
+int UISnapshotItem::level() const
+{
+ const QTreeWidgetItem *pItem = this;
+ int iResult = 0;
+ while (pItem->parent())
+ {
+ ++iResult;
+ pItem = pItem->parent();
+ }
+ return iResult;
+}
+
+void UISnapshotItem::setCurrentSnapshotItem(bool fCurrent)
+{
+ /* Remember the state: */
+ m_fCurrentSnapshotItem = fCurrent;
+
+ /* Set/clear the bold font state
+ * for "current snapshot" item: */
+ QFont myFont = font(Column_Name);
+ myFont.setBold(fCurrent);
+ setFont(Column_Name, myFont);
+
+ /* Update tool-tip: */
+ recacheToolTip();
+}
+
+void UISnapshotItem::recache()
+{
+ /* For "current state" item: */
+ if (m_fCurrentStateItem)
+ {
+ /* Fetch machine information: */
+ AssertReturnVoid(m_comMachine.isNotNull());
+ m_uMachineID = m_comMachine.GetId();
+ m_fCurrentStateModified = m_comMachine.GetCurrentStateModified();
+ m_strName = m_fCurrentStateModified
+ ? tr("Current State (changed)", "Current State (Modified)")
+ : tr("Current State", "Current State (Unmodified)");
+ const QString strFinalName = m_fExtendedNameRequired
+ ? QString("%1 (%2)").arg(m_strName, m_comMachine.GetName())
+ : m_strName;
+ setText(Column_Name, strFinalName);
+ m_strDescription = m_fCurrentStateModified
+ ? tr("The current state differs from the state stored in the current snapshot")
+ : QTreeWidgetItem::parent() != 0
+ ? tr("The current state is identical to the state stored in the current snapshot")
+ : QString();
+ }
+ /* For snapshot item: */
+ else
+ {
+ /* Fetch snapshot information: */
+ AssertReturnVoid(m_comSnapshot.isNotNull());
+ const CMachine comMachine = m_comSnapshot.GetMachine();
+ m_uMachineID = comMachine.GetId();
+ m_uSnapshotID = m_comSnapshot.GetId();
+ m_strName = m_comSnapshot.GetName();
+ const QString strFinalName = m_fExtendedNameRequired
+ ? QString("%1 (%2)").arg(m_strName, comMachine.GetName())
+ : m_strName;
+ setText(Column_Name, strFinalName);
+ m_fOnline = m_comSnapshot.GetOnline();
+ setIcon(Column_Name, *m_pSnapshotWidget->snapshotItemIcon(m_fOnline));
+ m_strDescription = m_comSnapshot.GetDescription();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
+ m_timestamp.setSecsSinceEpoch(m_comSnapshot.GetTimeStamp() / 1000);
+#else
+ m_timestamp.setTime_t(m_comSnapshot.GetTimeStamp() / 1000);
+#endif
+ m_fCurrentStateModified = false;
+ }
+
+ /* Update tool-tip: */
+ recacheToolTip();
+}
+
+KMachineState UISnapshotItem::machineState() const
+{
+ /* Make sure machine is valid: */
+ if (m_comMachine.isNull())
+ return KMachineState_Null;
+
+ /* Return cached state: */
+ return m_enmMachineState;
+}
+
+void UISnapshotItem::setMachineState(KMachineState enmState)
+{
+ /* Make sure machine is valid: */
+ if (m_comMachine.isNull())
+ return;
+
+ /* Cache new state: */
+ m_enmMachineState = enmState;
+ /* Set corresponding icon: */
+ setIcon(Column_Name, gpConverter->toIcon(m_enmMachineState));
+ /* Update timestamp: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
+ m_timestamp.setSecsSinceEpoch(m_comMachine.GetLastStateChange() / 1000);
+#else
+ m_timestamp.setTime_t(m_comMachine.GetLastStateChange() / 1000);
+#endif
+}
+
+SnapshotAgeFormat UISnapshotItem::updateAge()
+{
+ /* Prepare age: */
+ QString strAge;
+
+ /* Age: [date time|%1d ago|%1h ago|%1min ago|%1sec ago] */
+ SnapshotAgeFormat enmAgeFormat;
+ const QDateTime now = QDateTime::currentDateTime();
+ QDateTime then = m_timestamp;
+ if (then > now)
+ then = now; /* can happen if the host time is wrong */
+ if (then.daysTo(now) > 30)
+ {
+ strAge = QLocale::system().toString(then, QLocale::ShortFormat);
+ enmAgeFormat = SnapshotAgeFormat_Max;
+ }
+ else if (then.secsTo(now) > 60 * 60 * 24)
+ {
+ strAge = QString("%1 (%2)")
+ .arg(QLocale::system().toString(then, QLocale::ShortFormat),
+ UITranslator::daysToStringAgo(then.secsTo(now) / 60 / 60 / 24));
+ enmAgeFormat = SnapshotAgeFormat_InDays;
+ }
+ else if (then.secsTo(now) > 60 * 60)
+ {
+ strAge = QString("%1 (%2)")
+ .arg(QLocale::system().toString(then, QLocale::ShortFormat),
+ UITranslator::hoursToStringAgo(then.secsTo(now) / 60 / 60));
+ enmAgeFormat = SnapshotAgeFormat_InHours;
+ }
+ else if (then.secsTo(now) > 60)
+ {
+ strAge = QString("%1 (%2)")
+ .arg(QLocale::system().toString(then, QLocale::ShortFormat),
+ UITranslator::minutesToStringAgo(then.secsTo(now) / 60));
+ enmAgeFormat = SnapshotAgeFormat_InMinutes;
+ }
+ else
+ {
+ strAge = QString("%1 (%2)")
+ .arg(QLocale::system().toString(then, QLocale::ShortFormat),
+ UITranslator::secondsToStringAgo(then.secsTo(now)));
+ enmAgeFormat = SnapshotAgeFormat_InSeconds;
+ }
+
+ /* Update data: */
+ if (!m_fCurrentStateItem)
+ setText(Column_Taken, strAge);
+
+ /* Return age: */
+ return enmAgeFormat;
+}
+
+void UISnapshotItem::recacheToolTip()
+{
+ /* Is the saved date today? */
+ const bool fDateTimeToday = m_timestamp.date() == QDate::currentDate();
+
+ /* Compose date time: */
+ QString strDateTime = fDateTimeToday
+ ? QLocale::system().toString(m_timestamp.time(), QLocale::ShortFormat)
+ : QLocale::system().toString(m_timestamp, QLocale::ShortFormat);
+
+ /* Prepare details: */
+ QString strDetails;
+
+ /* For "current state" item: */
+ if (m_fCurrentStateItem)
+ {
+ strDateTime = tr("%1 since %2", "Current State (time or date + time)")
+ .arg(gpConverter->toString(m_enmMachineState)).arg(strDateTime);
+ }
+ /* For snapshot item: */
+ else
+ {
+ /* Gather details: */
+ QStringList details;
+ if (isCurrentSnapshotItem())
+ details << tr("current", "snapshot");
+ details << (m_fOnline ? tr("online", "snapshot")
+ : tr("offline", "snapshot"));
+ strDetails = QString(" (%1)").arg(details.join(", "));
+
+ /* Add date/time information: */
+ if (fDateTimeToday)
+ strDateTime = tr("Taken at %1", "Snapshot (time)").arg(strDateTime);
+ else
+ strDateTime = tr("Taken on %1", "Snapshot (date + time)").arg(strDateTime);
+ }
+
+ /* Prepare tool-tip: */
+ QString strToolTip = QString("<nobr><b>%1</b>%2</nobr><br><nobr>%3</nobr>")
+ .arg(name()).arg(strDetails).arg(strDateTime);
+
+ /* Append description if any: */
+ if (!m_strDescription.isEmpty())
+ strToolTip += "<hr>" + m_strDescription;
+
+ /* Assign tool-tip finally: */
+ setToolTip(Column_Name, strToolTip);
+}
+
+
+/*********************************************************************************************************************************
+* Class UISnapshotScrollBar implementation. *
+*********************************************************************************************************************************/
+UISnapshotScrollBar::UISnapshotScrollBar(Qt::Orientation enmOrientation, QWidget *pParent /* = 0 */)
+ : QScrollBar(enmOrientation, pParent)
+{
+}
+
+void UISnapshotScrollBar::showEvent(QShowEvent *pEvent)
+{
+ QScrollBar::showEvent(pEvent);
+ emit sigNotifyAboutVisibilityChange();
+}
+
+
+/*********************************************************************************************************************************
+* Class UISnapshotTree implementation. *
+*********************************************************************************************************************************/
+
+UISnapshotTree::UISnapshotTree(QWidget *pParent)
+ : QITreeWidget(pParent)
+{
+ /* Configure snapshot tree: */
+ setAutoScroll(false);
+ setColumnCount(Column_Max);
+ setAllColumnsShowFocus(true);
+ setAlternatingRowColors(true);
+ setExpandsOnDoubleClick(false);
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ setEditTriggers( QAbstractItemView::SelectedClicked
+ | QAbstractItemView::EditKeyPressed);
+
+ /* Replace scroll-bars: */
+ UISnapshotScrollBar *pScrollBarH = new UISnapshotScrollBar(Qt::Horizontal, this);
+ if (pScrollBarH)
+ {
+ connect(pScrollBarH, &UISnapshotScrollBar::sigNotifyAboutVisibilityChange,
+ this, &UISnapshotTree::sigNotifyAboutScrollBarVisibilityChange);
+ setHorizontalScrollBar(pScrollBarH);
+ }
+ UISnapshotScrollBar *pScrollBarV = new UISnapshotScrollBar(Qt::Vertical, this);
+ if (pScrollBarV)
+ {
+ connect(pScrollBarV, &UISnapshotScrollBar::sigNotifyAboutVisibilityChange,
+ this, &UISnapshotTree::sigNotifyAboutScrollBarVisibilityChange);
+ setVerticalScrollBar(pScrollBarV);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UISnapshotPane implementation. *
+*********************************************************************************************************************************/
+
+UISnapshotPane::UISnapshotPane(UIActionPool *pActionPool, bool fShowToolbar /* = true */, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pActionPool(pActionPool)
+ , m_fShowToolbar(fShowToolbar)
+ , m_pLockReadWrite(0)
+ , m_pIconSnapshotOffline(0)
+ , m_pIconSnapshotOnline(0)
+ , m_pTimerUpdateAge(0)
+ , m_pLayoutMain(0)
+ , m_pToolBar(0)
+ , m_pSnapshotTree(0)
+ , m_pDetailsWidget(0)
+{
+ prepare();
+}
+
+UISnapshotPane::~UISnapshotPane()
+{
+ cleanup();
+}
+
+void UISnapshotPane::setMachineItems(const QList<UIVirtualMachineItem*> &items)
+{
+ /* Wipe out old stuff: */
+ m_machines.clear();
+ m_sessionStates.clear();
+ m_operationAllowed.clear();
+
+ /* For each machine item: */
+ foreach (UIVirtualMachineItem *pItem, items)
+ {
+ /* Parse machine details: */
+ AssertPtrReturnVoid(pItem);
+ CMachine comMachine = pItem->toLocal()->machine();
+
+ /* Cache passed machine: */
+ if (!comMachine.isNull())
+ {
+ const QUuid uMachineId = comMachine.GetId();
+ const KSessionState enmSessionState = comMachine.GetSessionState();
+ const bool fAllowance = gEDataManager->machineSnapshotOperationsEnabled(uMachineId);
+ m_machines[uMachineId] = comMachine;
+ m_sessionStates[uMachineId] = enmSessionState;
+ m_operationAllowed[uMachineId] = fAllowance;
+ }
+ }
+
+ /* Refresh everything: */
+ refreshAll();
+}
+
+const QIcon *UISnapshotPane::snapshotItemIcon(bool fOnline) const
+{
+ return !fOnline ? m_pIconSnapshotOffline : m_pIconSnapshotOnline;
+}
+
+bool UISnapshotPane::isCurrentStateItemSelected() const
+{
+ UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+ return m_currentStateItems.values().contains(pSnapshotItem);
+}
+
+void UISnapshotPane::retranslateUi()
+{
+ /* Translate snapshot tree: */
+ m_pSnapshotTree->setWhatsThis(tr("Contains the snapshot tree of the current virtual machine"));
+
+ /* Translate toolbar: */
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is a bug in Qt Cocoa which result in showing a "more arrow" when
+ // the necessary size of the toolbar is increased. Also for some languages
+ // the with doesn't match if the text increase. So manually adjust the size
+ // after changing the text. */
+ if (m_pToolBar)
+ m_pToolBar->updateLayout();
+#endif
+
+ /* Translate snapshot tree: */
+ const QStringList fields = QStringList()
+ << tr("Name", "snapshot")
+ << tr("Taken", "snapshot");
+ m_pSnapshotTree->setHeaderLabels(fields);
+
+ /* Refresh whole the tree: */
+ refreshAll();
+}
+
+void UISnapshotPane::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI<QWidget>::resizeEvent(pEvent);
+
+ /* Adjust snapshot tree: */
+ adjustTreeWidget();
+}
+
+void UISnapshotPane::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIWithRetranslateUI<QWidget>::showEvent(pEvent);
+
+ /* Adjust snapshot tree: */
+ adjustTreeWidget();
+}
+
+void UISnapshotPane::sltHandleMachineDataChange(const QUuid &uMachineId)
+{
+ /* Make sure it's our VM: */
+ if (!m_machines.keys().contains(uMachineId))
+ return;
+
+ /* Prevent snapshot editing in the meantime: */
+ QWriteLocker locker(m_pLockReadWrite);
+
+ /* Recache "current state" item data: */
+ m_currentStateItems.value(uMachineId)->recache();
+
+ /* Choose current item again (to update details-widget): */
+ sltHandleCurrentItemChange();
+}
+
+void UISnapshotPane::sltHandleMachineStateChange(const QUuid &uMachineId, const KMachineState enmState)
+{
+ /* Make sure it's our VM: */
+ if (!m_machines.keys().contains(uMachineId))
+ return;
+
+ /* Prevent snapshot editing in the meantime: */
+ QWriteLocker locker(m_pLockReadWrite);
+
+ /* Recache "current state" item data and machine-state: */
+ m_currentStateItems.value(uMachineId)->recache();
+ m_currentStateItems.value(uMachineId)->setMachineState(enmState);
+}
+
+void UISnapshotPane::sltHandleSessionStateChange(const QUuid &uMachineId, const KSessionState enmState)
+{
+ /* Make sure it's our VM: */
+ if (!m_machines.keys().contains(uMachineId))
+ return;
+
+ /* Prevent snapshot editing in the meantime: */
+ QWriteLocker locker(m_pLockReadWrite);
+
+ /* Recache current session-state: */
+ m_sessionStates[uMachineId] = enmState;
+
+ /* Update action states: */
+ updateActionStates();
+}
+
+void UISnapshotPane::sltHandleSnapshotTake(const QUuid &uMachineId, const QUuid &uSnapshotId)
+{
+ /* Make sure it's our VM: */
+ if (!m_machines.keys().contains(uMachineId))
+ return;
+
+ LogRel(("GUI: Updating snapshot tree after TAKING snapshot with MachineID={%s}, SnapshotID={%s}...\n",
+ uMachineId.toString().toUtf8().constData(), uSnapshotId.toString().toUtf8().constData()));
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ {
+ /* Prevent snapshot editing in the meantime: */
+ QWriteLocker locker(m_pLockReadWrite);
+
+ /* Acquire corresponding machine: */
+ CMachine comMachine = m_machines.value(uMachineId);
+
+ /* Search for corresponding snapshot: */
+ CSnapshot comSnapshot = comMachine.FindSnapshot(uSnapshotId.toString());
+ fSuccess = comMachine.isOk() && !comSnapshot.isNull();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ UINotificationMessage::cannotFindSnapshotById(comMachine, uSnapshotId);
+ else
+ {
+ /* Where will be the newly created item located? */
+ UISnapshotItem *pParentItem = 0;
+
+ /* Acquire snapshot parent: */
+ CSnapshot comParentSnapshot = comSnapshot.GetParent();
+ if (comParentSnapshot.isNotNull())
+ {
+ /* Acquire parent snapshot id: */
+ const QUuid uParentSnapshotId = comParentSnapshot.GetId();
+ fSuccess = comParentSnapshot.isOk();
+
+ /* Show error message if necessary: */
+ if (!fSuccess)
+ UINotificationMessage::cannotAcquireSnapshotParameter(comSnapshot);
+ else
+ {
+ /* Search for an existing parent-item with such id: */
+ pParentItem = findItem(uParentSnapshotId);
+ fSuccess = pParentItem;
+ }
+ }
+
+ /* Make sure this parent-item is a parent of "current state" item as well: */
+ if (fSuccess)
+ fSuccess = qobject_cast<UISnapshotItem*>(m_currentStateItems.value(uMachineId)->parentItem()) == pParentItem;
+ /* Make sure this parent-item is a "current snapshot" item as well: */
+ if (fSuccess)
+ fSuccess = m_currentSnapshotItems.value(uMachineId) == pParentItem;
+
+ /* Create new item: */
+ if (fSuccess)
+ {
+ /* Delete "current state" item first of all: */
+ UISnapshotItem *pCurrentStateItem = m_currentStateItems.value(uMachineId);
+ m_currentStateItems[uMachineId] = 0;
+ delete pCurrentStateItem;
+ pCurrentStateItem = 0;
+
+ /* Create "current snapshot" item for a newly taken snapshot: */
+ if (m_currentSnapshotItems.value(uMachineId))
+ m_currentSnapshotItems.value(uMachineId)->setCurrentSnapshotItem(false);
+ m_currentSnapshotItems[uMachineId] = pParentItem
+ ? new UISnapshotItem(this, pParentItem, comSnapshot)
+ : new UISnapshotItem(this, m_pSnapshotTree, comSnapshot, m_machines.size() > 1);
+ /* Mark it as current: */
+ m_currentSnapshotItems.value(uMachineId)->setCurrentSnapshotItem(true);
+ /* And recache it's content: */
+ m_currentSnapshotItems.value(uMachineId)->recache();
+
+ /* Create "current state" item as a child of "current snapshot" item: */
+ m_currentStateItems[uMachineId] = new UISnapshotItem(this, m_currentSnapshotItems.value(uMachineId), comMachine);
+ /* Recache it's content: */
+ m_currentStateItems.value(uMachineId)->recache();
+ /* And choose is as current one: */
+ m_pSnapshotTree->setCurrentItem(m_currentStateItems.value(uMachineId));
+ sltHandleCurrentItemChange();
+
+ LogRel(("GUI: Snapshot tree update successful!\n"));
+ }
+ }
+ }
+
+ /* Just refresh everything as fallback: */
+ if (!fSuccess)
+ {
+ LogRel(("GUI: Snapshot tree update failed! Rebuilding from scratch...\n"));
+ refreshAll();
+ }
+}
+
+void UISnapshotPane::sltHandleSnapshotDelete(const QUuid &uMachineId, const QUuid &uSnapshotId)
+{
+ /* Make sure it's our VM: */
+ if (!m_machines.keys().contains(uMachineId))
+ return;
+
+ LogRel(("GUI: Updating snapshot tree after DELETING snapshot with MachineID={%s}, SnapshotID={%s}...\n",
+ uMachineId.toString().toUtf8().constData(), uSnapshotId.toString().toUtf8().constData()));
+
+ /* Prepare result: */
+ bool fSuccess = false;
+ {
+ /* Prevent snapshot editing in the meantime: */
+ QWriteLocker locker(m_pLockReadWrite);
+
+ /* Search for an existing item with such id: */
+ UISnapshotItem *pItem = findItem(uSnapshotId);
+ fSuccess = pItem;
+
+ /* Make sure item has no more than one child: */
+ if (fSuccess)
+ fSuccess = pItem->childCount() <= 1;
+
+ /* Detach child before deleting item: */
+ QTreeWidgetItem *pChild = 0;
+ if (fSuccess && pItem->childCount() == 1)
+ pChild = pItem->takeChild(0);
+
+ /* Check whether item has parent: */
+ QTreeWidgetItem *pParent = 0;
+ if (fSuccess)
+ pParent = pItem->QTreeWidgetItem::parent();
+
+ /* If item has child: */
+ if (pChild)
+ {
+ /* Determine where the item located: */
+ int iIndexOfChild = -1;
+ if (fSuccess)
+ {
+ if (pParent)
+ iIndexOfChild = pParent->indexOfChild(pItem);
+ else
+ iIndexOfChild = m_pSnapshotTree->indexOfTopLevelItem(pItem);
+ fSuccess = iIndexOfChild != -1;
+ }
+
+ /* And move child into this place: */
+ if (fSuccess)
+ {
+ if (pParent)
+ pParent->insertChild(iIndexOfChild, pChild);
+ else
+ m_pSnapshotTree->insertTopLevelItem(iIndexOfChild, pChild);
+ expandItemChildren(pChild);
+ }
+ }
+
+ /* Delete item finally: */
+ if (fSuccess)
+ {
+ if (pItem == m_currentSnapshotItems.value(uMachineId))
+ {
+ m_currentSnapshotItems[uMachineId] = UISnapshotItem::toSnapshotItem(pParent);
+ if (m_currentSnapshotItems.value(uMachineId))
+ m_currentSnapshotItems.value(uMachineId)->setCurrentSnapshotItem(true);
+ }
+ delete pItem;
+ pItem = 0;
+
+ LogRel(("GUI: Snapshot tree update successful!\n"));
+ }
+ }
+
+ /* Just refresh everything as fallback: */
+ if (!fSuccess)
+ {
+ LogRel(("GUI: Snapshot tree update failed! Rebuilding from scratch...\n"));
+ refreshAll();
+ }
+}
+
+void UISnapshotPane::sltHandleSnapshotChange(const QUuid &uMachineId, const QUuid &uSnapshotId)
+{
+ /* Make sure it's our VM: */
+ if (!m_machines.keys().contains(uMachineId))
+ return;
+
+ LogRel(("GUI: Updating snapshot tree after CHANGING snapshot with MachineID={%s}, SnapshotID={%s}...\n",
+ uMachineId.toString().toUtf8().constData(), uSnapshotId.toString().toUtf8().constData()));
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ {
+ /* Prevent snapshot editing in the meantime: */
+ QWriteLocker locker(m_pLockReadWrite);
+
+ /* Search for an existing item with such id: */
+ UISnapshotItem *pItem = findItem(uSnapshotId);
+ fSuccess = pItem;
+
+ /* Update the item: */
+ if (fSuccess)
+ {
+ /* Recache it: */
+ pItem->recache();
+ /* And choose it again if it's current one (to update details-widget): */
+ if (UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem()) == pItem)
+ sltHandleCurrentItemChange();
+
+ LogRel(("GUI: Snapshot tree update successful!\n"));
+ }
+ }
+
+ /* Just refresh everything as fallback: */
+ if (!fSuccess)
+ {
+ LogRel(("GUI: Snapshot tree update failed! Rebuilding from scratch...\n"));
+ refreshAll();
+ }
+}
+
+void UISnapshotPane::sltHandleSnapshotRestore(const QUuid &uMachineId, const QUuid &uSnapshotId)
+{
+ /* Make sure it's our VM: */
+ if (!m_machines.keys().contains(uMachineId))
+ return;
+
+ LogRel(("GUI: Updating snapshot tree after RESTORING snapshot with MachineID={%s}, SnapshotID={%s}...\n",
+ uMachineId.toString().toUtf8().constData(), uSnapshotId.toString().toUtf8().constData()));
+
+ /* Prepare result: */
+ bool fSuccess = true;
+ {
+ /* Prevent snapshot editing in the meantime: */
+ QWriteLocker locker(m_pLockReadWrite);
+
+ /* Search for an existing item with such id: */
+ UISnapshotItem *pItem = findItem(uSnapshotId);
+ fSuccess = pItem;
+
+ /* Choose this item as new "current snapshot": */
+ if (fSuccess)
+ {
+ /* Delete "current state" item first of all: */
+ UISnapshotItem *pCurrentStateItem = m_currentStateItems.value(uMachineId);
+ m_currentStateItems[uMachineId] = 0;
+ delete pCurrentStateItem;
+ pCurrentStateItem = 0;
+
+ /* Move the "current snapshot" token from one to another: */
+ AssertPtrReturnVoid(m_currentSnapshotItems.value(uMachineId));
+ m_currentSnapshotItems.value(uMachineId)->setCurrentSnapshotItem(false);
+ m_currentSnapshotItems[uMachineId] = pItem;
+ m_currentSnapshotItems.value(uMachineId)->setCurrentSnapshotItem(true);
+
+ /* Create "current state" item as a child of "current snapshot" item: */
+ m_currentStateItems[uMachineId] = new UISnapshotItem(this, m_currentSnapshotItems.value(uMachineId), m_machines.value(uMachineId));
+ m_currentStateItems.value(uMachineId)->recache();
+ /* And choose is as current one: */
+ m_pSnapshotTree->setCurrentItem(m_currentStateItems.value(uMachineId));
+ sltHandleCurrentItemChange();
+
+ LogRel(("GUI: Snapshot tree update successful!\n"));
+ }
+ }
+
+ /* Just refresh everything as fallback: */
+ if (!fSuccess)
+ {
+ LogRel(("GUI: Snapshot tree update failed! Rebuilding from scratch...\n"));
+ refreshAll();
+ }
+}
+
+void UISnapshotPane::sltUpdateSnapshotsAge()
+{
+ /* Stop timer if active: */
+ if (m_pTimerUpdateAge->isActive())
+ m_pTimerUpdateAge->stop();
+
+ /* Search for smallest snapshot age to optimize timer timeout: */
+ const SnapshotAgeFormat enmAge = traverseSnapshotAge(m_pSnapshotTree->invisibleRootItem());
+ switch (enmAge)
+ {
+ case SnapshotAgeFormat_InSeconds: m_pTimerUpdateAge->setInterval(5 * 1000); break;
+ case SnapshotAgeFormat_InMinutes: m_pTimerUpdateAge->setInterval(60 * 1000); break;
+ case SnapshotAgeFormat_InHours: m_pTimerUpdateAge->setInterval(60 * 60 * 1000); break;
+ case SnapshotAgeFormat_InDays: m_pTimerUpdateAge->setInterval(24 * 60 * 60 * 1000); break;
+ default: m_pTimerUpdateAge->setInterval(0); break;
+ }
+
+ /* Restart timer if necessary: */
+ if (m_pTimerUpdateAge->interval() > 0)
+ m_pTimerUpdateAge->start();
+}
+
+void UISnapshotPane::sltToggleSnapshotDetailsVisibility(bool fVisible)
+{
+ /* Save the setting: */
+ gEDataManager->setSnapshotManagerDetailsExpanded(fVisible);
+ /* Show/hide details-widget: */
+ m_pDetailsWidget->setVisible(fVisible);
+ /* If details-widget is visible: */
+ if (m_pDetailsWidget->isVisible())
+ {
+ /* Acquire selected item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+ AssertPtrReturnVoid(pSnapshotItem);
+ /* Update details-widget: */
+ if (pSnapshotItem->isCurrentStateItem())
+ {
+ if (m_machines.value(pSnapshotItem->machineID()).isNull())
+ m_pDetailsWidget->clearData();
+ else
+ m_pDetailsWidget->setData(m_machines.value(pSnapshotItem->machineID()));
+ }
+ else
+ m_pDetailsWidget->setData(*pSnapshotItem, pSnapshotItem->snapshot());
+ }
+ /* Cleanup invisible details-widget: */
+ else
+ m_pDetailsWidget->clearData();
+}
+
+void UISnapshotPane::sltApplySnapshotDetailsChanges()
+{
+ /* Acquire selected item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+ AssertPtrReturnVoid(pSnapshotItem);
+
+ /* For current state item: */
+ if (pSnapshotItem->isCurrentStateItem())
+ {
+ /* Get item data: */
+ UIDataSnapshot newData = m_pDetailsWidget->data();
+
+ /* Take snapshot: */
+ UINotificationProgressSnapshotTake *pNotification = new UINotificationProgressSnapshotTake(m_machines.value(pSnapshotItem->machineID()),
+ newData.name(),
+ newData.description());
+ gpNotificationCenter->append(pNotification);
+ }
+ /* For snapshot items: */
+ else
+ {
+ /* Make sure nothing being edited in the meantime: */
+ if (!m_pLockReadWrite->tryLockForWrite())
+ return;
+
+ /* Make sure that's a snapshot item indeed: */
+ CSnapshot comSnapshot = pSnapshotItem->snapshot();
+ AssertReturnVoid(comSnapshot.isNotNull());
+
+ /* Get item data: */
+ UIDataSnapshot oldData = *pSnapshotItem;
+ UIDataSnapshot newData = m_pDetailsWidget->data();
+ AssertReturnVoid(newData != oldData);
+
+ /* Open a session (this call will handle all errors): */
+ CSession comSession;
+ if (m_sessionStates.value(pSnapshotItem->machineID()) != KSessionState_Unlocked)
+ comSession = uiCommon().openExistingSession(pSnapshotItem->machineID());
+ else
+ comSession = uiCommon().openSession(pSnapshotItem->machineID());
+ if (comSession.isNotNull())
+ {
+ /* Get corresponding machine object: */
+ CMachine comMachine = comSession.GetMachine();
+
+ /* Perform separate independent steps: */
+ do
+ {
+ /* Save snapshot name: */
+ if (newData.name() != oldData.name())
+ {
+ comSnapshot.SetName(newData.name());
+ if (!comSnapshot.isOk())
+ {
+ UINotificationMessage::cannotChangeSnapshot(comSnapshot, oldData.name(), comMachine.GetName());
+ break;
+ }
+ }
+
+ /* Save snapshot description: */
+ if (newData.description() != oldData.description())
+ {
+ comSnapshot.SetDescription(newData.description());
+ if (!comSnapshot.isOk())
+ {
+ UINotificationMessage::cannotChangeSnapshot(comSnapshot, oldData.name(), comMachine.GetName());
+ break;
+ }
+ }
+ }
+ while (0);
+
+ /* Cleanup session: */
+ comSession.UnlockMachine();
+ }
+
+ /* Allows editing again: */
+ m_pLockReadWrite->unlock();
+ }
+
+ /* Adjust snapshot tree: */
+ adjustTreeWidget();
+}
+
+void UISnapshotPane::sltHandleCurrentItemChange()
+{
+ /* Acquire "current snapshot" item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+
+ /* Make the current item visible: */
+ sltHandleScrollBarVisibilityChange();
+
+ /* Update action states: */
+ updateActionStates();
+
+ /* Update details-widget if it's visible: */
+ if (pSnapshotItem && !m_pDetailsWidget->isHidden())
+ {
+ if (pSnapshotItem->isCurrentStateItem())
+ {
+ CMachine comMachine = m_machines.value(pSnapshotItem->machineID());
+ if (comMachine.isNull())
+ m_pDetailsWidget->clearData();
+ else
+ m_pDetailsWidget->setData(comMachine);
+ }
+ else
+ m_pDetailsWidget->setData(*pSnapshotItem, pSnapshotItem->snapshot());
+ }
+ /* Cleanup invisible details-widget: */
+ else
+ m_pDetailsWidget->clearData();
+
+ /* Notify listeners: */
+ emit sigCurrentItemChange();
+}
+
+void UISnapshotPane::sltHandleContextMenuRequest(const QPoint &position)
+{
+ /* Search for corresponding item: */
+ const QTreeWidgetItem *pItem = m_pSnapshotTree->itemAt(position);
+ if (!pItem)
+ return;
+
+ /* Acquire corresponding snapshot item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(pItem);
+ AssertReturnVoid(pSnapshotItem);
+
+ /* Prepare menu: */
+ QMenu menu;
+ /* For snapshot item: */
+ if (m_currentSnapshotItems.value(pSnapshotItem->machineID()) && !pSnapshotItem->isCurrentStateItem())
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Delete));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Restore));
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_T_Properties));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Clone));
+ }
+ /* For "current state" item: */
+ else
+ {
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Take));
+ menu.addSeparator();
+ menu.addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Clone));
+ }
+
+ /* Show menu: */
+ menu.exec(m_pSnapshotTree->viewport()->mapToGlobal(position));
+}
+
+void UISnapshotPane::sltHandleItemChange(QTreeWidgetItem *pItem)
+{
+ /* Make sure nothing being edited in the meantime: */
+ if (!m_pLockReadWrite->tryLockForWrite())
+ return;
+
+ /* Acquire corresponding snapshot item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(pItem);
+ AssertPtr(pSnapshotItem);
+ if (pSnapshotItem)
+ {
+ /* Make sure that's a snapshot item indeed: */
+ CSnapshot comSnapshot = pSnapshotItem->snapshot();
+ if (comSnapshot.isNotNull())
+ {
+ /* Rename corresponding snapshot if necessary: */
+ if (comSnapshot.GetName() != pSnapshotItem->name())
+ {
+ /* We need to open a session when we manipulate the snapshot data of a machine: */
+ CSession comSession = uiCommon().openExistingSession(comSnapshot.GetMachine().GetId());
+ if (!comSession.isNull())
+ {
+ /// @todo Add settings save validation.
+
+ /* Save snapshot name: */
+ comSnapshot.SetName(pSnapshotItem->name());
+
+ /* Close the session again: */
+ comSession.UnlockMachine();
+ }
+ }
+ }
+ }
+
+ /* Allows editing again: */
+ m_pLockReadWrite->unlock();
+
+ /* Adjust snapshot tree: */
+ adjustTreeWidget();
+}
+
+void UISnapshotPane::sltHandleItemDoubleClick(QTreeWidgetItem *pItem)
+{
+ /* Acquire corresponding snapshot item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(pItem);
+ AssertReturnVoid(pSnapshotItem);
+
+ /* If this is a snapshot item: */
+ if (pSnapshotItem)
+ {
+ /* Handle Ctrl+DoubleClick: */
+ if (QApplication::keyboardModifiers() == Qt::ControlModifier)
+ {
+ /* As snapshot-restore procedure: */
+ if (pSnapshotItem->isCurrentStateItem())
+ takeSnapshot(true /* automatically */);
+ else
+ restoreSnapshot(true /* suppress non-critical warnings */);
+ }
+ /* Handle Ctrl+Shift+DoubleClick: */
+ else if (QApplication::keyboardModifiers() == (Qt::KeyboardModifiers)(Qt::ControlModifier | Qt::ShiftModifier))
+ {
+ /* As snapshot-delete procedure: */
+ if (!pSnapshotItem->isCurrentStateItem())
+ deleteSnapshot(true /* automatically */);
+ }
+ /* Handle other kinds of DoubleClick: */
+ else
+ {
+ /* As show details-widget procedure: */
+ m_pActionPool->action(UIActionIndexMN_M_Snapshot_T_Properties)->setChecked(true);
+ }
+ }
+}
+
+void UISnapshotPane::sltHandleScrollBarVisibilityChange()
+{
+ /* Acquire "current snapshot" item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+
+ /* Make the current item visible: */
+ if (pSnapshotItem)
+ {
+ m_pSnapshotTree->horizontalScrollBar()->setValue(0);
+ m_pSnapshotTree->scrollToItem(pSnapshotItem);
+ m_pSnapshotTree->horizontalScrollBar()->setValue(m_pSnapshotTree->indentation() * pSnapshotItem->level());
+ }
+}
+
+void UISnapshotPane::prepare()
+{
+ /* Create read-write locker: */
+ m_pLockReadWrite = new QReadWriteLock;
+
+ /* Create pixmaps: */
+ m_pIconSnapshotOffline = new QIcon(UIIconPool::iconSet(":/snapshot_offline_16px.png"));
+ m_pIconSnapshotOnline = new QIcon(UIIconPool::iconSet(":/snapshot_online_16px.png"));
+
+ /* Create timer: */
+ m_pTimerUpdateAge = new QTimer;
+ if (m_pTimerUpdateAge)
+ {
+ /* Configure timer: */
+ m_pTimerUpdateAge->setSingleShot(true);
+ connect(m_pTimerUpdateAge, &QTimer::timeout, this, &UISnapshotPane::sltUpdateSnapshotsAge);
+ }
+
+ /* Prepare connections: */
+ prepareConnections();
+ /* Prepare actions: */
+ prepareActions();
+ /* Prepare widgets: */
+ prepareWidgets();
+
+ /* Load settings: */
+ loadSettings();
+
+ /* Register help topic: */
+ uiCommon().setHelpKeyword(this, "snapshots");
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UISnapshotPane::prepareConnections()
+{
+ /* Configure Main event connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange,
+ this, &UISnapshotPane::sltHandleMachineDataChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
+ this, &UISnapshotPane::sltHandleMachineStateChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSessionStateChange,
+ this, &UISnapshotPane::sltHandleSessionStateChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotTake,
+ this, &UISnapshotPane::sltHandleSnapshotTake);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotDelete,
+ this, &UISnapshotPane::sltHandleSnapshotDelete);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotChange,
+ this, &UISnapshotPane::sltHandleSnapshotChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotRestore,
+ this, &UISnapshotPane::sltHandleSnapshotRestore);
+}
+
+void UISnapshotPane::prepareActions()
+{
+ /* First of all, add actions which has smaller shortcut scope: */
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Take));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Delete));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Restore));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_T_Properties));
+ addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Clone));
+
+ /* Connect actions: */
+ connect(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Take), &UIAction::triggered,
+ this, &UISnapshotPane::sltTakeSnapshot);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Delete), &UIAction::triggered,
+ this, &UISnapshotPane::sltDeleteSnapshot);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Restore), &UIAction::triggered,
+ this, &UISnapshotPane::sltRestoreSnapshot);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Snapshot_T_Properties), &UIAction::toggled,
+ this, &UISnapshotPane::sltToggleSnapshotDetailsVisibility);
+ connect(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Clone), &UIAction::triggered,
+ this, &UISnapshotPane::sltCloneSnapshot);
+}
+
+void UISnapshotPane::prepareWidgets()
+{
+ /* Create layout: */
+ m_pLayoutMain = new QVBoxLayout(this);
+ if (m_pLayoutMain)
+ {
+ /* Configure layout: */
+ m_pLayoutMain->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ m_pLayoutMain->setSpacing(10);
+#else
+ m_pLayoutMain->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
+#endif
+
+ /* Prepare toolbar, if requested: */
+ if (m_fShowToolbar)
+ prepareToolbar();
+ /* Prepare snapshot tree: */
+ prepareTreeWidget();
+ /* Prepare details-widget: */
+ prepareDetailsWidget();
+ }
+}
+
+void UISnapshotPane::prepareToolbar()
+{
+ /* Create snapshot toolbar: */
+ m_pToolBar = new QIToolBar(this);
+ if (m_pToolBar)
+ {
+ /* Configure toolbar: */
+ const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+
+ /* Add toolbar actions: */
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Take));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Delete));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Restore));
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_T_Properties));
+ m_pToolBar->addSeparator();
+ m_pToolBar->addAction(m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Clone));
+
+ /* Add into layout: */
+ m_pLayoutMain->addWidget(m_pToolBar);
+ }
+}
+
+void UISnapshotPane::prepareTreeWidget()
+{
+ /* Create snapshot tree: */
+ m_pSnapshotTree = new UISnapshotTree(this);
+ if (m_pSnapshotTree)
+ {
+ /* Configure tree: */
+ connect(m_pSnapshotTree, &UISnapshotTree::currentItemChanged,
+ this, &UISnapshotPane::sltHandleCurrentItemChange);
+ connect(m_pSnapshotTree, &UISnapshotTree::customContextMenuRequested,
+ this, &UISnapshotPane::sltHandleContextMenuRequest);
+ connect(m_pSnapshotTree, &UISnapshotTree::itemChanged,
+ this, &UISnapshotPane::sltHandleItemChange);
+ connect(m_pSnapshotTree, &UISnapshotTree::itemDoubleClicked,
+ this, &UISnapshotPane::sltHandleItemDoubleClick);
+ connect(m_pSnapshotTree, &UISnapshotTree::sigNotifyAboutScrollBarVisibilityChange,
+ this, &UISnapshotPane::sltHandleScrollBarVisibilityChange, Qt::QueuedConnection);
+
+ /* Add into layout: */
+ m_pLayoutMain->addWidget(m_pSnapshotTree, 1);
+ }
+}
+
+void UISnapshotPane::prepareDetailsWidget()
+{
+ /* Create details-widget: */
+ m_pDetailsWidget = new UISnapshotDetailsWidget(this);
+ if (m_pDetailsWidget)
+ {
+ /* Configure details-widget: */
+ m_pDetailsWidget->setVisible(false);
+ connect(m_pDetailsWidget, &UISnapshotDetailsWidget::sigDataChangeAccepted,
+ this, &UISnapshotPane::sltApplySnapshotDetailsChanges);
+
+ /* Add into layout: */
+ m_pLayoutMain->addWidget(m_pDetailsWidget, 1);
+ }
+}
+
+void UISnapshotPane::loadSettings()
+{
+ /* Details action/widget: */
+ m_pActionPool->action(UIActionIndexMN_M_Snapshot_T_Properties)->
+ setChecked(gEDataManager->snapshotManagerDetailsExpanded());
+}
+
+void UISnapshotPane::refreshAll()
+{
+ /* Prevent snapshot editing in the meantime: */
+ QWriteLocker locker(m_pLockReadWrite);
+
+ /* If VM list is empty, just updated the current item: */
+ if (m_machines.isEmpty())
+ {
+ /* Clear the tree: */
+ m_pSnapshotTree->clear();
+ return;
+ }
+
+ /* Remember the selected item and it's first child: */
+ QUuid uSelectedItem, uFirstChildOfSelectedItem;
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+ if (pSnapshotItem)
+ {
+ uSelectedItem = pSnapshotItem->snapshotID();
+ if (pSnapshotItem->child(0))
+ uFirstChildOfSelectedItem = UISnapshotItem::toSnapshotItem(pSnapshotItem->child(0))->snapshotID();
+ }
+
+ /* Clear the tree: */
+ m_pSnapshotTree->clear();
+
+ /* Iterates over all the machines: */
+ foreach (const QUuid &uMachineId, m_machines.keys())
+ {
+ CMachine comMachine = m_machines.value(uMachineId);
+
+ /* If machine has snapshots: */
+ if (comMachine.GetSnapshotCount() > 0)
+ {
+ /* Get the first snapshot: */
+ const CSnapshot comSnapshot = comMachine.FindSnapshot(QString());
+
+ /* Populate snapshot tree: */
+ populateSnapshots(uMachineId, comSnapshot, 0);
+ /* And make sure it has "current snapshot" item: */
+ Assert(m_currentSnapshotItems.value(uMachineId));
+
+ /* Add the "current state" item as a child to "current snapshot" item: */
+ m_currentStateItems[uMachineId] = new UISnapshotItem(this, m_currentSnapshotItems.value(uMachineId), comMachine);
+ m_currentStateItems.value(uMachineId)->recache();
+
+ /* Search for a previously selected item: */
+ UISnapshotItem *pCurrentItem = findItem(uSelectedItem);
+ if (pCurrentItem == 0)
+ pCurrentItem = findItem(uFirstChildOfSelectedItem);
+ if (pCurrentItem == 0)
+ pCurrentItem = m_currentStateItems.value(uMachineId);
+
+ /* Choose current item: */
+ m_pSnapshotTree->setCurrentItem(pCurrentItem);
+ sltHandleCurrentItemChange();
+ }
+ /* If machine has no snapshots: */
+ else
+ {
+ /* There is no "current snapshot" item: */
+ m_currentSnapshotItems[uMachineId] = 0;
+
+ /* Add the "current state" item as a child of snapshot tree: */
+ m_currentStateItems[uMachineId] = new UISnapshotItem(this, m_pSnapshotTree, comMachine, m_machines.size() > 1);
+ m_currentStateItems.value(uMachineId)->recache();
+
+ /* Choose current item: */
+ m_pSnapshotTree->setCurrentItem(m_currentStateItems.value(uMachineId));
+ sltHandleCurrentItemChange();
+ }
+ }
+
+ /* Update age: */
+ sltUpdateSnapshotsAge();
+
+ /* Adjust snapshot tree: */
+ adjustTreeWidget();
+}
+
+void UISnapshotPane::populateSnapshots(const QUuid &uMachineId, const CSnapshot &comSnapshot, QITreeWidgetItem *pItem)
+{
+ /* Create a child of passed item: */
+ UISnapshotItem *pSnapshotItem = pItem ? new UISnapshotItem(this, pItem, comSnapshot)
+ : new UISnapshotItem(this, m_pSnapshotTree, comSnapshot, m_machines.size() > 1);
+ /* And recache it's content: */
+ pSnapshotItem->recache();
+
+ /* Mark snapshot item as "current" and remember it: */
+ CSnapshot comCurrentSnapshot = m_machines.value(uMachineId).GetCurrentSnapshot();
+ if (!comCurrentSnapshot.isNull() && comCurrentSnapshot.GetId() == comSnapshot.GetId())
+ {
+ pSnapshotItem->setCurrentSnapshotItem(true);
+ m_currentSnapshotItems[uMachineId] = pSnapshotItem;
+ }
+
+ /* Walk through the children recursively: */
+ foreach (const CSnapshot &comIteratedSnapshot, comSnapshot.GetChildren())
+ populateSnapshots(uMachineId, comIteratedSnapshot, pSnapshotItem);
+
+ /* Expand the newly created item: */
+ pSnapshotItem->setExpanded(true);
+}
+
+void UISnapshotPane::cleanup()
+{
+ /* Stop timer if active: */
+ if (m_pTimerUpdateAge->isActive())
+ m_pTimerUpdateAge->stop();
+ /* Destroy timer: */
+ delete m_pTimerUpdateAge;
+ m_pTimerUpdateAge = 0;
+
+ /* Destroy icons: */
+ delete m_pIconSnapshotOffline;
+ delete m_pIconSnapshotOnline;
+ m_pIconSnapshotOffline = 0;
+ m_pIconSnapshotOnline = 0;
+
+ /* Destroy read-write locker: */
+ delete m_pLockReadWrite;
+ m_pLockReadWrite = 0;
+}
+
+void UISnapshotPane::updateActionStates()
+{
+ /* Acquire "current snapshot" item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+
+ /* Check whether another direct session is opened: */
+ const bool fBusy = !pSnapshotItem || m_sessionStates.value(pSnapshotItem->machineID()) != KSessionState_Unlocked;
+
+ /* Acquire machine-state of the "current state" item: */
+ KMachineState enmState = KMachineState_Null;
+ if ( pSnapshotItem
+ && m_currentStateItems.value(pSnapshotItem->machineID()))
+ enmState = m_currentStateItems.value(pSnapshotItem->machineID())->machineState();
+
+ /* Determine whether taking or deleting snapshots is possible: */
+ const bool fCanTakeDeleteSnapshot = !fBusy
+ || enmState == KMachineState_PoweredOff
+ || enmState == KMachineState_Saved
+ || enmState == KMachineState_Aborted
+ || enmState == KMachineState_AbortedSaved
+ || enmState == KMachineState_Running
+ || enmState == KMachineState_Paused;
+
+ /* Update 'Take' action: */
+ m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Take)->setEnabled(
+ pSnapshotItem
+ && m_operationAllowed.value(pSnapshotItem->machineID())
+ && ( ( fCanTakeDeleteSnapshot
+ && m_currentSnapshotItems.value(pSnapshotItem->machineID())
+ && pSnapshotItem->isCurrentStateItem())
+ || (!m_currentSnapshotItems.value(pSnapshotItem->machineID())))
+ );
+
+ /* Update 'Delete' action: */
+ m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Delete)->setEnabled(
+ pSnapshotItem
+ && m_operationAllowed.value(pSnapshotItem->machineID())
+ && fCanTakeDeleteSnapshot
+ && m_currentSnapshotItems.value(pSnapshotItem->machineID())
+ && pSnapshotItem
+ && !pSnapshotItem->isCurrentStateItem()
+ );
+
+ /* Update 'Restore' action: */
+ m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Restore)->setEnabled(
+ !fBusy
+ && pSnapshotItem
+ && m_currentSnapshotItems.value(pSnapshotItem->machineID())
+ && !pSnapshotItem->isCurrentStateItem()
+ );
+
+ /* Update 'Show Details' action: */
+ m_pActionPool->action(UIActionIndexMN_M_Snapshot_T_Properties)->setEnabled(
+ pSnapshotItem
+ );
+
+ /* Update 'Clone' action: */
+ m_pActionPool->action(UIActionIndexMN_M_Snapshot_S_Clone)->setEnabled(
+ pSnapshotItem
+ && ( !pSnapshotItem->isCurrentStateItem()
+ || !fBusy)
+ );
+}
+
+bool UISnapshotPane::takeSnapshot(bool fAutomatically /* = false */)
+{
+ /* Acquire "current snapshot" item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+ AssertPtrReturn(pSnapshotItem, false);
+
+ /* Acquire machine: */
+ const CMachine comMachine = m_machines.value(pSnapshotItem->machineID());
+
+ /* Search for a maximum existing snapshot index: */
+ int iMaximumIndex = 0;
+ const QString strNameTemplate = tr("Snapshot %1");
+ const QRegExp reName(QString("^") + strNameTemplate.arg("([0-9]+)") + QString("$"));
+ QTreeWidgetItemIterator iterator(m_pSnapshotTree);
+ while (*iterator)
+ {
+ const QString strName = static_cast<UISnapshotItem*>(*iterator)->name();
+ const int iPosition = reName.indexIn(strName);
+ if (iPosition != -1)
+ iMaximumIndex = reName.cap(1).toInt() > iMaximumIndex
+ ? reName.cap(1).toInt()
+ : iMaximumIndex;
+ ++iterator;
+ }
+
+ /* Prepare snapshot name/description: */
+ QString strFinalName = strNameTemplate.arg(iMaximumIndex + 1);
+ QString strFinalDescription;
+
+ /* In manual mode we should show take snapshot dialog: */
+ if (!fAutomatically)
+ {
+ /* Create take-snapshot dialog: */
+ QWidget *pDlgParent = windowManager().realParentWindow(this);
+ QPointer<UITakeSnapshotDialog> pDlg = new UITakeSnapshotDialog(pDlgParent, comMachine);
+ windowManager().registerNewParent(pDlg, pDlgParent);
+
+ /* Assign corresponding icon: */
+ QIcon icon = generalIconPool().userMachineIcon(comMachine);
+ if (icon.isNull())
+ icon = generalIconPool().guestOSTypeIcon(comMachine.GetOSTypeId());
+ pDlg->setIcon(icon);
+
+ /* Assign corresponding snapshot name: */
+ pDlg->setName(strFinalName);
+
+ /* Show Take Snapshot dialog: */
+ if (pDlg->exec() != QDialog::Accepted)
+ {
+ /* Cleanup dialog if it wasn't destroyed in own loop: */
+ delete pDlg;
+ return false;
+ }
+
+ /* Acquire final snapshot name/description: */
+ strFinalName = pDlg->name().trimmed();
+ strFinalDescription = pDlg->description();
+
+ /* Cleanup dialog: */
+ delete pDlg;
+ }
+
+ /* Take snapshot: */
+ UINotificationProgressSnapshotTake *pNotification = new UINotificationProgressSnapshotTake(comMachine,
+ strFinalName,
+ strFinalDescription);
+ gpNotificationCenter->append(pNotification);
+
+ /* Return result: */
+ return true;
+}
+
+bool UISnapshotPane::deleteSnapshot(bool fAutomatically /* = false */)
+{
+ /* Acquire "current snapshot" item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+ AssertPtrReturn(pSnapshotItem, false);
+
+ /* Acquire machine: */
+ const CMachine comMachine = m_machines.value(pSnapshotItem->machineID());
+
+ /* Get corresponding snapshot: */
+ const CSnapshot comSnapshot = pSnapshotItem->snapshot();
+ AssertReturn(!comSnapshot.isNull(), false);
+
+ /* In manual mode we should ask if user really wants to remove the selected snapshot: */
+ if (!fAutomatically && !msgCenter().confirmSnapshotRemoval(comSnapshot.GetName()))
+ return false;
+
+#if 0
+ /** @todo check available space on the target filesystem etc etc. */
+ if (!msgCenter().warnAboutSnapshotRemovalFreeSpace(comSnapshot.GetName(),
+ "/home/juser/.VirtualBox/Machines/SampleVM/Snapshots/{01020304-0102-0102-0102-010203040506}.vdi",
+ "59 GiB",
+ "15 GiB"))
+ return false;
+#endif
+
+ /* Delete snapshot: */
+ UINotificationProgressSnapshotDelete *pNotification = new UINotificationProgressSnapshotDelete(comMachine,
+ pSnapshotItem->snapshotID());
+ gpNotificationCenter->append(pNotification);
+
+ /* Return result: */
+ return true;
+}
+
+bool UISnapshotPane::restoreSnapshot(bool fAutomatically /* = false */)
+{
+ /* Acquire "current snapshot" item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+ AssertPtrReturn(pSnapshotItem, false);
+
+ /* Acquire machine: */
+ const CMachine comMachine = m_machines.value(pSnapshotItem->machineID());
+
+ /* Get corresponding snapshot: */
+ const CSnapshot comSnapshot = pSnapshotItem->snapshot();
+ AssertReturn(!comSnapshot.isNull(), false);
+
+ /* In manual mode we should check whether current state is changed: */
+ if (!fAutomatically && comMachine.GetCurrentStateModified())
+ {
+ /* Ask if user really wants to restore the selected snapshot: */
+ int iResultCode = msgCenter().confirmSnapshotRestoring(comSnapshot.GetName(), comMachine.GetCurrentStateModified());
+ if (iResultCode & AlertButton_Cancel)
+ return false;
+
+ /* Ask if user also wants to create new snapshot of current state which is changed: */
+ if (iResultCode & AlertOption_CheckBox)
+ {
+ /* Take snapshot of changed current state: */
+ m_pSnapshotTree->setCurrentItem(m_currentStateItems.value(pSnapshotItem->machineID()));
+ if (!takeSnapshot())
+ return false;
+ }
+ }
+
+ /* Restore snapshot: */
+ UINotificationProgressSnapshotRestore *pNotification = new UINotificationProgressSnapshotRestore(comMachine, comSnapshot);
+ gpNotificationCenter->append(pNotification);
+
+ /* Return result: */
+ return true;
+}
+
+void UISnapshotPane::cloneSnapshot()
+{
+ /* Acquire "current snapshot" item: */
+ const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
+ AssertReturnVoid(pSnapshotItem);
+
+ /* Get desired machine/snapshot: */
+ CMachine comMachine;
+ CSnapshot comSnapshot;
+ if (pSnapshotItem->isCurrentStateItem())
+ comMachine = pSnapshotItem->machine();
+ else
+ {
+ comSnapshot = pSnapshotItem->snapshot();
+ AssertReturnVoid(!comSnapshot.isNull());
+ comMachine = comSnapshot.GetMachine();
+ }
+ AssertReturnVoid(!comMachine.isNull());
+
+ /* Show Clone VM wizard: */
+ QPointer<UINativeWizard> pWizard = new UIWizardCloneVM(this, comMachine, QString(), comSnapshot);
+ pWizard->exec();
+ if (pWizard)
+ delete pWizard;
+}
+
+void UISnapshotPane::adjustTreeWidget()
+{
+ /* Get the snapshot tree abstract interface: */
+ QAbstractItemView *pItemView = m_pSnapshotTree;
+ /* Get the snapshot tree header-view: */
+ QHeaderView *pItemHeader = m_pSnapshotTree->header();
+
+ /* Calculate the total snapshot tree width: */
+ const int iTotal = m_pSnapshotTree->viewport()->width();
+
+ /* Look for a minimum width hint for Taken column: */
+ const int iMinWidth1 = qMax(pItemView->sizeHintForColumn(Column_Taken), pItemHeader->sectionSizeHint(Column_Taken));
+ /* Propose suitable width hint for Taken column (but no more than the half of existing space): */
+ const int iWidth1 = iMinWidth1 < iTotal / Column_Max ? iMinWidth1 : iTotal / Column_Max;
+
+ /* Look for a minimum width hint for Name column: */
+ const int iMinWidth0 = qMax(pItemView->sizeHintForColumn(Column_Name), pItemHeader->sectionSizeHint(Column_Name));
+ /* Propose suitable width hint for important column (at least all remaining space and no less than the hint itself): */
+ const int iWidth0 = iMinWidth0 > iTotal - iWidth1 ? iMinWidth0 : iTotal - iWidth1;
+
+ /* Apply the proposal: */
+ m_pSnapshotTree->setColumnWidth(Column_Taken, iWidth1);
+ m_pSnapshotTree->setColumnWidth(Column_Name, iWidth0);
+}
+
+UISnapshotItem *UISnapshotPane::findItem(const QUuid &uSnapshotID) const
+{
+ /* Search for the first item with required ID: */
+ QTreeWidgetItemIterator it(m_pSnapshotTree);
+ while (*it)
+ {
+ UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(*it);
+ if (pSnapshotItem->snapshotID() == uSnapshotID)
+ return pSnapshotItem;
+ ++it;
+ }
+
+ /* Null by default: */
+ return 0;
+}
+
+SnapshotAgeFormat UISnapshotPane::traverseSnapshotAge(QTreeWidgetItem *pItem) const
+{
+ /* Acquire corresponding snapshot item: */
+ UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(pItem);
+
+ /* Fetch the snapshot age of the root if it's valid: */
+ SnapshotAgeFormat age = pSnapshotItem ? pSnapshotItem->updateAge() : SnapshotAgeFormat_Max;
+
+ /* Walk through the children recursively: */
+ for (int i = 0; i < pItem->childCount(); ++i)
+ {
+ /* Fetch the smallest snapshot age of the children: */
+ const SnapshotAgeFormat newAge = traverseSnapshotAge(pItem->child(i));
+ /* Remember the smallest snapshot age among existing: */
+ age = newAge < age ? newAge : age;
+ }
+
+ /* Return result: */
+ return age;
+}
+
+void UISnapshotPane::expandItemChildren(QTreeWidgetItem *pItem)
+{
+ pItem ->setExpanded(true);
+ for (int i = 0; i < pItem->childCount(); ++i)
+ expandItemChildren(pItem->child(i));
+}
+
+#include "UISnapshotPane.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotPane.h b/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotPane.h
new file mode 100644
index 00000000..03f51e13
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotPane.h
@@ -0,0 +1,273 @@
+/* $Id: UISnapshotPane.h $ */
+/** @file
+ * VBox Qt GUI - UISnapshotPane class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_snapshots_UISnapshotPane_h
+#define FEQT_INCLUDED_SRC_snapshots_UISnapshotPane_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CMachine.h"
+
+/* Forward declarations: */
+class QIcon;
+class QReadWriteLock;
+class QTimer;
+class QTreeWidgetItem;
+class QVBoxLayout;
+class QIToolBar;
+class QITreeWidgetItem;
+class UIActionPool;
+class UISnapshotDetailsWidget;
+class UISnapshotItem;
+class UISnapshotTree;
+class UIVirtualMachineItem;
+
+
+/** Snapshot age format. */
+enum SnapshotAgeFormat
+{
+ SnapshotAgeFormat_InSeconds,
+ SnapshotAgeFormat_InMinutes,
+ SnapshotAgeFormat_InHours,
+ SnapshotAgeFormat_InDays,
+ SnapshotAgeFormat_Max
+};
+
+
+/** QWidget extension providing GUI with the pane to control snapshot related functionality. */
+class UISnapshotPane : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about current item change. */
+ void sigCurrentItemChange();
+
+public:
+
+ /** Constructs snapshot pane passing @a pParent to the base-class. */
+ UISnapshotPane(UIActionPool *pActionPool, bool fShowToolbar = true, QWidget *pParent = 0);
+ /** Destructs snapshot pane. */
+ virtual ~UISnapshotPane() RT_OVERRIDE;
+
+ /** Defines the machine @a items to be parsed. */
+ void setMachineItems(const QList<UIVirtualMachineItem*> &items);
+
+ /** Returns cached snapshot-item icon depending on @a fOnline flag. */
+ const QIcon *snapshotItemIcon(bool fOnline) const;
+
+ /** Returns whether "current state" item selected. */
+ bool isCurrentStateItemSelected() const;
+
+protected:
+
+ /** @name Qt event handlers.
+ * @{ */
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** @} */
+
+private slots:
+
+ /** @name Main event handlers.
+ * @{ */
+ /** Handles machine data change for machine with @a uMachineId. */
+ void sltHandleMachineDataChange(const QUuid &uMachineId);
+ /** Handles machine @a enmState change for machine with @a uMachineId. */
+ void sltHandleMachineStateChange(const QUuid &uMachineId, const KMachineState enmState);
+
+ /** Handles session @a enmState change for machine with @a uMachineId. */
+ void sltHandleSessionStateChange(const QUuid &uMachineId, const KSessionState enmState);
+
+ /** Handles snapshot take event for machine with @a uMachineId. */
+ void sltHandleSnapshotTake(const QUuid &uMachineId, const QUuid &uSnapshotId);
+ /** Handles snapshot delete event for machine with @a uMachineId. */
+ void sltHandleSnapshotDelete(const QUuid &uMachineId, const QUuid &uSnapshotId);
+ /** Handles snapshot change event for machine with @a uMachineId. */
+ void sltHandleSnapshotChange(const QUuid &uMachineId, const QUuid &uSnapshotId);
+ /** Handles snapshot restore event for machine with @a uMachineId. */
+ void sltHandleSnapshotRestore(const QUuid &uMachineId, const QUuid &uSnapshotId);
+ /** @} */
+
+ /** @name Timer event handlers.
+ * @{ */
+ /** Updates snapshots age. */
+ void sltUpdateSnapshotsAge();
+ /** @} */
+
+ /** @name Toolbar handlers.
+ * @{ */
+ /** Handles command to take a snapshot. */
+ void sltTakeSnapshot() { takeSnapshot(); }
+ /** Handles command to restore the snapshot. */
+ void sltRestoreSnapshot() { restoreSnapshot(); }
+ /** Handles command to delete the snapshot. */
+ void sltDeleteSnapshot() { deleteSnapshot(); }
+ /** Handles command to make snapshot details @a fVisible. */
+ void sltToggleSnapshotDetailsVisibility(bool fVisible);
+ /** Handles command to apply snapshot details changes. */
+ void sltApplySnapshotDetailsChanges();
+ /** Proposes to clone the snapshot. */
+ void sltCloneSnapshot() { cloneSnapshot(); }
+ /** @} */
+
+ /** @name Tree-widget handlers.
+ * @{ */
+ /** Handles tree-widget current item change. */
+ void sltHandleCurrentItemChange();
+ /** Handles context menu request for tree-widget @a position. */
+ void sltHandleContextMenuRequest(const QPoint &position);
+ /** Handles tree-widget @a pItem change. */
+ void sltHandleItemChange(QTreeWidgetItem *pItem);
+ /** Handles tree-widget @a pItem double-click. */
+ void sltHandleItemDoubleClick(QTreeWidgetItem *pItem);
+ /** Handles tree-widget's scroll-bar visibility change. */
+ void sltHandleScrollBarVisibilityChange();
+ /** @} */
+
+private:
+
+ /** @name Prepare/cleanup cascade.
+ * @{ */
+ /** Prepares all. */
+ void prepare();
+ /** Prepares connections. */
+ void prepareConnections();
+ /** Prepares actions. */
+ void prepareActions();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares toolbar. */
+ void prepareToolbar();
+ /** Prepares tree-widget. */
+ void prepareTreeWidget();
+ /** Prepares details-widget. */
+ void prepareDetailsWidget();
+ /** Load settings: */
+ void loadSettings();
+
+ /** Refreshes everything. */
+ void refreshAll();
+ /** Populates snapshot items for corresponding @a comSnapshot using @a pItem as parent. */
+ void populateSnapshots(const QUuid &uMachineId, const CSnapshot &comSnapshot, QITreeWidgetItem *pItem);
+
+ /** Cleanups all. */
+ void cleanup();
+ /** @} */
+
+ /** @name Toolbar helpers.
+ * @{ */
+ /** Updates action states. */
+ void updateActionStates();
+
+ /** Proposes to take a snapshot. */
+ bool takeSnapshot(bool fAutomatically = false);
+ /** Proposes to delete the snapshot. */
+ bool deleteSnapshot(bool fAutomatically = false);
+ /** Proposes to restore the snapshot. */
+ bool restoreSnapshot(bool fAutomatically = false);
+ /** Proposes to clone the snapshot. */
+ void cloneSnapshot();
+ /** @} */
+
+ /** @name Tree-widget helpers.
+ * @{ */
+ /** Handles command to adjust snapshot tree. */
+ void adjustTreeWidget();
+
+ /** Searches for an item with corresponding @a uSnapshotID. */
+ UISnapshotItem *findItem(const QUuid &uSnapshotID) const;
+
+ /** Searches for smallest snapshot age starting with @a pItem as parent. */
+ SnapshotAgeFormat traverseSnapshotAge(QTreeWidgetItem *pItem) const;
+
+ /** Expand all the children starting with @a pItem. */
+ void expandItemChildren(QTreeWidgetItem *pItem);
+ /** @} */
+
+ /** @name General variables.
+ * @{ */
+ /** Holds the action-pool reference. */
+ UIActionPool *m_pActionPool;
+ /** Holds whether we should show toolbar. */
+ bool m_fShowToolbar;
+
+ /** Holds the COM machine object list. */
+ QMap<QUuid, CMachine> m_machines;
+ /** Holds the cached session state list. */
+ QMap<QUuid, KSessionState> m_sessionStates;
+ /** Holds the list of operation allowance states. */
+ QMap<QUuid, bool> m_operationAllowed;
+
+ /** Holds the snapshot item editing protector. */
+ QReadWriteLock *m_pLockReadWrite;
+
+ /** Holds the cached snapshot-item pixmap for 'offline' state. */
+ QIcon *m_pIconSnapshotOffline;
+ /** Holds the cached snapshot-item pixmap for 'online' state. */
+ QIcon *m_pIconSnapshotOnline;
+
+ /** Holds the snapshot age update timer. */
+ QTimer *m_pTimerUpdateAge;
+ /** @} */
+
+ /** @name Widget variables.
+ * @{ */
+ /** Holds the main layout instance. */
+ QVBoxLayout *m_pLayoutMain;
+
+ /** Holds the toolbar instance. */
+ QIToolBar *m_pToolBar;
+
+ /** Holds the snapshot tree instance. */
+ UISnapshotTree *m_pSnapshotTree;
+
+ /** Holds the "current snapshot" item list. */
+ QMap<QUuid, UISnapshotItem*> m_currentSnapshotItems;
+ /** Holds the "current state" item list. */
+ QMap<QUuid, UISnapshotItem*> m_currentStateItems;
+
+ /** Holds the details-widget instance. */
+ UISnapshotDetailsWidget *m_pDetailsWidget;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_snapshots_UISnapshotPane_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/softkeyboard/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/softkeyboard/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/softkeyboard/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/softkeyboard/UISoftKeyboard.cpp b/src/VBox/Frontends/VirtualBox/src/softkeyboard/UISoftKeyboard.cpp
new file mode 100644
index 00000000..d5a38c0d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/softkeyboard/UISoftKeyboard.cpp
@@ -0,0 +1,4481 @@
+/* $Id: UISoftKeyboard.cpp $ */
+/** @file
+ * VBox Qt GUI - UISoftKeyboard class implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QCheckBox>
+#include <QColorDialog>
+#include <QComboBox>
+#include <QDir>
+#include <QFile>
+#include <QGroupBox>
+#include <QInputDialog>
+#include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QListWidget>
+#include <QPainter>
+#include <QPicture>
+#include <QPushButton>
+#include <QSplitter>
+#include <QStatusBar>
+#include <QStyle>
+#include <QStackedWidget>
+#include <QToolButton>
+#include <QXmlStreamReader>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UISession.h"
+#include "UISoftKeyboard.h"
+#include "UICommon.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+/* External includes: */
+# include <math.h>
+
+/* Forward declarations: */
+class UISoftKeyboardColorButton;
+class UISoftKeyboardLayout;
+class UISoftKeyboardRow;
+class UISoftKeyboardWidget;
+
+const int iMessageTimeout = 3000;
+/** Key position are used to identify respective keys. */
+const int iCapsLockPosition = 30;
+const int iNumLockPosition = 90;
+const int iScrollLockPosition = 125;
+
+/** Set a generous file size limit. */
+const qint64 iFileSizeLimit = _256K;
+const QString strSubDirectorName("keyboardLayouts");
+
+/** Name, background color, normal font color, hover color, edited button background color, pressed button font color. */
+const char* predefinedColorThemes[][6] = {{"Clear Night","#000000", "#ffffff", "#859900", "#9b6767", "#000000"},
+ {"Gobi Dark","#002b36", "#fdf6e3", "#859900", "#cb4b16", "#002b36"},
+ {"Gobi Light","#fdf6e3", "#002b36", "#2aa198", "#cb4b16", "#bf4040"},
+ {0, 0, 0, 0, 0, 0}};
+
+typedef QPair<QLabel*, UISoftKeyboardColorButton*> ColorSelectLabelButton;
+
+enum KeyState
+{
+ KeyState_NotPressed,
+ KeyState_Pressed,
+ KeyState_Locked,
+ KeyState_Max
+};
+
+enum KeyType
+{
+ /** Can be in KeyState_NotPressed and KeyState_Pressed states. */
+ KeyType_Ordinary,
+ /** e.g. CapsLock, NumLock. Can be only in KeyState_NotPressed, KeyState_Locked */
+ KeyType_Lock,
+ /** e.g. Shift Can be in all 3 states*/
+ KeyType_Modifier,
+ KeyType_Max
+};
+
+enum KeyboardColorType
+{
+ KeyboardColorType_Background = 0,
+ KeyboardColorType_Font,
+ KeyboardColorType_Hover,
+ KeyboardColorType_Edit,
+ KeyboardColorType_Pressed,
+ KeyboardColorType_Max
+};
+
+enum KeyboardRegion
+{
+ KeyboardRegion_Main = 0,
+ KeyboardRegion_NumPad,
+ KeyboardRegion_MultimediaKeys,
+ KeyboardRegion_Max
+};
+
+struct UIKeyCaptions
+{
+ UIKeyCaptions(const QString &strBase, const QString &strShift,
+ const QString &strAltGr, const QString &strShiftAltGr)
+ : m_strBase(strBase)
+ , m_strShift(strShift)
+ , m_strAltGr(strAltGr)
+ , m_strShiftAltGr(strShiftAltGr)
+ {
+ m_strBase.replace("\\n", "\n");
+ m_strShift.replace("\\n", "\n");
+ m_strAltGr.replace("\\n", "\n");
+ m_strShiftAltGr.replace("\\n", "\n");
+ }
+ UIKeyCaptions(){}
+ bool operator==(const UIKeyCaptions &other) const
+ {
+ return (m_strBase == other.m_strBase &&
+ m_strShift == other.m_strShift &&
+ m_strAltGr == other.m_strAltGr &&
+ m_strShiftAltGr == other.m_strShiftAltGr);
+ }
+ QString m_strBase;
+ QString m_strShift;
+ QString m_strAltGr;
+ QString m_strShiftAltGr;
+};
+
+/** Returns a QPointF which lies on the line [p0, p1] and with a distance @p fDistance to p0. */
+QPointF pointInBetween(qreal fDistance, const QPointF &p0, const QPointF &p1)
+{
+ QPointF vectorP0P1 = p1 - p0;
+ qreal length = sqrt(vectorP0P1.x() * vectorP0P1.x() + vectorP0P1.y() * vectorP0P1.y());
+ if (length == 0)
+ return QPointF();
+ /* Normalize the vector and add it to starting point: */
+ vectorP0P1 = (fDistance / length) * vectorP0P1 + p0;
+ return vectorP0P1;
+}
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardColorButton definition. *
+*********************************************************************************************************************************/
+
+class UISoftKeyboardColorButton : public QPushButton
+{
+ Q_OBJECT;
+
+public:
+
+ UISoftKeyboardColorButton(KeyboardColorType enmColorType, QWidget *pParent = 0);
+ KeyboardColorType colorType() const;
+
+public:
+
+ KeyboardColorType m_enmColorType;
+};
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardPhysicalLayout definition. *
+*********************************************************************************************************************************/
+
+/** This class is used to represent the physical layout of a keyboard (in contrast to UISoftKeyboardLayout).
+ * Physical layouts are read from an xml file where keys are placed in rows. Each UISoftKeyboardLayout must refer to a
+ * refer to a UISoftKeyboardPhysicalLayout instance. An example of an UISoftKeyboardPhysicalLayout instance is 103 key ISO layout.*/
+class UISoftKeyboardPhysicalLayout
+{
+
+public:
+ UISoftKeyboardPhysicalLayout();
+
+ void setName(const QString &strName);
+ const QString &name() const;
+
+ void setFileName(const QString &strName);
+ const QString &fileName() const;
+
+ void setUid(const QUuid &uid);
+ const QUuid &uid() const;
+
+ const QVector<UISoftKeyboardRow> &rows() const;
+ QVector<UISoftKeyboardRow> &rows();
+
+ void setLockKey(int iKeyPosition, UISoftKeyboardKey *pKey);
+ void updateLockKeyStates(bool fCapsLockState, bool fNumLockState, bool fScrollLockState);
+ void reset();
+
+ void setDefaultKeyWidth(int iDefaultKeyWidth);
+ int defaultKeyWidth() const;
+
+ /** Returns the sum totalHeight() of all rows(). */
+ int totalHeight() const;
+
+private:
+
+ void updateLockKeyState(bool fLockState, UISoftKeyboardKey *pKey);
+ QString m_strFileName;
+ QUuid m_uId;
+ QString m_strName;
+ QVector<UISoftKeyboardRow> m_rows;
+ int m_iDefaultKeyWidth;
+ /** Scroll, Num, and Caps Lock keys' states are updated thru some API events. Thus we keep their pointers in a containter. */
+ QMap<int, UISoftKeyboardKey*> m_lockKeys;
+};
+
+/*********************************************************************************************************************************
+* UIKeyboardLayoutEditor definition. *
+*********************************************************************************************************************************/
+
+/** A QWidget extension thru which we can edit key captions, the physical layout of the keyboard, name of the layout etc. */
+class UIKeyboardLayoutEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigLayoutEdited();
+ void sigUIKeyCaptionsEdited(UISoftKeyboardKey* pKey);
+ void sigGoBackButton();
+
+public:
+
+ UIKeyboardLayoutEditor(QWidget *pParent = 0);
+ void setKey(UISoftKeyboardKey *pKey);
+ void setLayoutToEdit(UISoftKeyboardLayout *pLayout);
+ void setPhysicalLayoutList(const QVector<UISoftKeyboardPhysicalLayout> &physicalLayouts);
+ void reset();
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ void sltCaptionsUpdate();
+ void sltPhysicalLayoutChanged();
+ void sltLayoutNameChanged(const QString &strCaption);
+ void sltLayoutNativeNameChanged(const QString &strCaption);
+
+private:
+
+ void prepareObjects();
+ void prepareConnections();
+ QWidget *prepareKeyCaptionEditWidgets();
+ void resetKeyWidgets();
+ QGridLayout *m_pEditorLayout;
+ QToolButton *m_pGoBackButton;
+ QGroupBox *m_pSelectedKeyGroupBox;
+ QGroupBox *m_pCaptionEditGroupBox;
+ QComboBox *m_pPhysicalLayoutCombo;
+ QLabel *m_pTitleLabel;
+ QLabel *m_pPhysicalLayoutLabel;
+ QLabel *m_pLayoutNameLabel;
+ QLabel *m_pLayoutNativeNameLabel;
+ QLabel *m_pScanCodeLabel;
+ QLabel *m_pPositionLabel;
+ QLabel *m_pBaseCaptionLabel;
+ QLabel *m_pShiftCaptionLabel;
+ QLabel *m_pAltGrCaptionLabel;
+ QLabel *m_pShiftAltGrCaptionLabel;
+ QLineEdit *m_pLayoutNameEdit;
+ QLineEdit *m_pLayoutNativeNameEdit;
+ QLineEdit *m_pScanCodeEdit;
+ QLineEdit *m_pPositionEdit;
+ QLineEdit *m_pBaseCaptionEdit;
+ QLineEdit *m_pShiftCaptionEdit;
+ QLineEdit *m_pAltGrCaptionEdit;
+ QLineEdit *m_pShiftAltGrCaptionEdit;
+
+ /** The key which is being currently edited. Might be Null. */
+ UISoftKeyboardKey *m_pKey;
+ /** The layout which is being currently edited. */
+ UISoftKeyboardLayout *m_pLayout;
+};
+
+/*********************************************************************************************************************************
+* UILayoutSelector definition. *
+*********************************************************************************************************************************/
+
+class UILayoutSelector : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigSaveLayout();
+ void sigCopyLayout();
+ void sigDeleteLayout();
+ void sigLayoutSelectionChanged(const QUuid &strSelectedLayoutUid);
+ void sigShowLayoutEditor();
+ void sigCloseLayoutList();
+
+public:
+
+ UILayoutSelector(QWidget *pParent = 0);
+ void setLayoutList(const QStringList &layoutNames, QList<QUuid> layoutIdList);
+ void setCurrentLayout(const QUuid &layoutUid);
+ void setCurrentLayoutIsEditable(bool fEditable);
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ void sltCurrentItemChanged(QListWidgetItem *pCurrent, QListWidgetItem *pPrevious);
+
+private:
+
+ void prepareObjects();
+
+ QListWidget *m_pLayoutListWidget;
+ QToolButton *m_pApplyLayoutButton;
+ QToolButton *m_pEditLayoutButton;
+ QToolButton *m_pCopyLayoutButton;
+ QToolButton *m_pSaveLayoutButton;
+ QToolButton *m_pDeleteLayoutButton;
+ QLabel *m_pTitleLabel;
+ QToolButton *m_pCloseButton;
+};
+
+/*********************************************************************************************************************************
+* UISoftKeyboardRow definition. *
+*********************************************************************************************************************************/
+
+/** UISoftKeyboardRow represents a row in the physical keyboard. The rows are read from a physical layout file and contained
+ * keys are added to rows in the order they appear in that file.*/
+class UISoftKeyboardRow
+{
+
+public:
+
+ UISoftKeyboardRow();
+
+ void setDefaultWidth(int iWidth);
+ int defaultWidth() const;
+
+ void setDefaultHeight(int iHeight);
+ int defaultHeight() const;
+
+ /* Return the sum of the maximum key height and m_iSpaceHeightAfter */
+ int totalHeight() const;
+
+ QVector<UISoftKeyboardKey> &keys();
+ const QVector<UISoftKeyboardKey> &keys() const;
+
+ void setSpaceHeightAfter(int iSpace);
+ int spaceHeightAfter() const;
+
+ int leftMargin() const;
+ void setLeftMargin(int iMargin);
+
+private:
+
+ /** Default width and height might be inherited from the layout and overwritten in row settings. */
+ int m_iDefaultWidth;
+ int m_iDefaultHeight;
+
+ QVector<UISoftKeyboardKey> m_keys;
+ int m_iSpaceHeightAfter;
+ /* The width of the empty space before the 1st key. */
+ int m_iLeftMargin;
+};
+
+/*********************************************************************************************************************************
+* UISoftKeyboardKey definition. *
+*********************************************************************************************************************************/
+
+/** UISoftKeyboardKey is a place holder for a keyboard key. Graphical key represantations are drawn according to this class.
+ * The position of a key within the physical layout is read from the layout file. Note that UISoftKeyboardKey usually does not have
+ * caption field(s). Captions are kept by UISoftKeyboardLayout since same keys may (and usually do) have different captions in
+ * different layouts. So called static captions are exections. They are defined in physical layout files and kept as member of
+ * UISoftKeyboardKey. When a static caption exits captions (if any) from the keyboard layout files are ignored. */
+class UISoftKeyboardKey
+{
+public:
+
+ UISoftKeyboardKey();
+
+ const QRect keyGeometry() const;
+ void setKeyGeometry(const QRect &rect);
+
+ void setWidth(int iWidth);
+ int width() const;
+
+ void setHeight(int iHeight);
+ int height() const;
+
+ void setScanCode(LONG scanCode);
+ LONG scanCode() const;
+
+ void addScanCodePrefix(LONG scanCode);
+
+ void setUsageId(LONG usageId);
+ void setUsagePage(LONG usagePage);
+ QPair<LONG, LONG> usagePageIdPair() const;
+
+ void setSpaceWidthAfter(int iSpace);
+ int spaceWidthAfter() const;
+
+ void setPosition(int iPosition);
+ int position() const;
+
+ void setType(KeyType enmType);
+ KeyType type() const;
+
+ KeyboardRegion keyboardRegion() const;
+ void setKeyboardRegion(KeyboardRegion enmRegion);
+
+ void setCutout(int iCorner, int iWidth, int iHeight);
+
+ KeyState state() const;
+ void setState(KeyState state);
+
+ void setStaticCaption(const QString &strCaption);
+ const QString &staticCaption() const;
+
+ void setImageByName(const QString &strCaption);
+ const QImage &image() const;
+
+ void setParentWidget(UISoftKeyboardWidget* pParent);
+ QVector<LONG> scanCodeWithPrefix() const;
+
+ void setIsOSMenuKey(bool fFlag);
+ bool isOSMenuKey() const;
+
+ void release();
+ void press();
+
+ void setPoints(const QVector<QPointF> &points);
+ const QVector<QPointF> &points() const;
+ const QPainterPath &painterPath() const;
+
+
+ void setCornerRadius(float fCornerRadius);
+
+ QPolygonF polygonInGlobal() const;
+
+ int cutoutCorner() const;
+ int cutoutWidth() const;
+ int cutoutHeight() const;
+
+ void updateLockState(bool fLocked);
+ void reset();
+
+private:
+
+ void updateState(bool fPressed);
+ /** Creates a path out of points m_points with rounded corners. */
+ void computePainterPath();
+
+ QRect m_keyGeometry;
+ /** Stores the key points (vertices) in local coordinates. */
+ QVector<QPointF> m_points;
+ /** We cache the path since re-computing that at each draw is meaningless. */
+ QPainterPath m_painterPath;
+ KeyType m_enmType;
+ KeyState m_enmState;
+ /** Key width as it is read from the xml file. */
+ int m_iWidth;
+ /** Key height as it is read from the xml file. */
+ int m_iHeight;
+ int m_iSpaceWidthAfter;
+ LONG m_scanCode;
+ QVector<LONG> m_scanCodePrefix;
+
+ /** @name Cutouts are used to create non-rectangle keys polygons.
+ * @{ */
+ int m_iCutoutWidth;
+ int m_iCutoutHeight;
+ /** -1 is for no cutout. 0 is the topleft, 2 is the top right and so on. */
+ int m_iCutoutCorner;
+ /** @} */
+
+ /** Key's position in the layout. */
+ int m_iPosition;
+ UISoftKeyboardWidget *m_pParentWidget;
+ LONG m_iUsageId;
+ LONG m_iUsagePage;
+ KeyboardRegion m_enmKeyboardRegion;
+ /** This is used for multimedia keys, OS key etc where we want to have a non-modifiable
+ * caption (usually a single char). This caption is defined in the physical layout file
+ * and has precedence over the captions defined in keyboard layout files. */
+ QString m_strStaticCaption;
+ bool m_fIsOSMenuKey;
+ double m_fCornerRadius;
+ QImage m_image;
+};
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardLayout definition. *
+*********************************************************************************************************************************/
+/** UISoftKeyboardLayout represents mainly a set of captions for the keys. It refers to a phsical layout which defines the
+ * positioning and number of keys (alongside with scan codes etc.). UISoftKeyboardLayout instances are read from xml files. An
+ * example for UISoftKeyboardLayout instance is 'US International' keyboard layout. */
+class UISoftKeyboardLayout
+{
+
+public:
+
+ UISoftKeyboardLayout();
+
+ void setName(const QString &strName);
+ const QString &name() const;
+
+ void setNativeName(const QString &strLocaName);
+ const QString &nativeName() const;
+
+ /** Combines name and native name and returns the string. */
+ QString nameString() const;
+
+ void setSourceFilePath(const QString& strSourceFilePath);
+ const QString& sourceFilePath() const;
+
+ void setIsFromResources(bool fIsFromResources);
+ bool isFromResources() const;
+
+ void setEditable(bool fEditable);
+ bool editable() const;
+
+ void setPhysicalLayoutUuid(const QUuid &uuid);
+ const QUuid &physicalLayoutUuid() const;
+
+ void addOrUpdateUIKeyCaptions(int iKeyPosition, const UIKeyCaptions &keyCaptions);
+ UIKeyCaptions keyCaptions(int iKeyPosition) const;
+
+ bool operator==(const UISoftKeyboardLayout &otherLayout) const;
+
+ QString baseCaption(int iKeyPosition) const;
+ QString shiftCaption(int iKeyPosition) const;
+
+ QString altGrCaption(int iKeyPosition) const;
+ QString shiftAltGrCaption(int iKeyPosition) const;
+
+ void setEditedBuNotSaved(bool fEditedButNotsaved);
+ bool editedButNotSaved() const;
+
+ void setUid(const QUuid &uid);
+ QUuid uid() const;
+
+ void drawTextInRect(const UISoftKeyboardKey &key, QPainter &painter);
+ void drawKeyImageInRect(const UISoftKeyboardKey &key, QPainter &painter);
+
+private:
+
+ QMap<int, UIKeyCaptions> m_keyCaptionsMap;
+ /** Caching the font sizes we used for font rendering since it is not a very cheap process to compute these. */
+ QMap<int, int> m_keyCaptionsFontSizeMap;
+ /** The UUID of the physical layout used by this layout. */
+ QUuid m_physicalLayoutUuid;
+ /** This is the English name of the layout. */
+ QString m_strName;
+ QString m_strNativeName;
+ QString m_strSourceFilePath;
+ bool m_fEditable;
+ bool m_fIsFromResources;
+ bool m_fEditedButNotSaved;
+ QUuid m_uid;
+};
+
+/*********************************************************************************************************************************
+* UISoftKeyboardColorTheme definition. *
+*********************************************************************************************************************************/
+
+class UISoftKeyboardColorTheme
+{
+
+public:
+
+ UISoftKeyboardColorTheme();
+ UISoftKeyboardColorTheme(const QString &strName,
+ const QString &strBackgroundColor,
+ const QString &strNormalFontColor,
+ const QString &strHoverColor,
+ const QString &strEditedButtonBackgroundColor,
+ const QString &strPressedButtonFontColor);
+
+ void setColor(KeyboardColorType enmColorType, const QColor &color);
+ QColor color(KeyboardColorType enmColorType) const;
+ QStringList colorsToStringList() const;
+ void colorsFromStringList(const QStringList &colorStringList);
+
+ const QString &name() const;
+ void setName(const QString &strName);
+
+ bool isEditable() const;
+ void setIsEditable(bool fIsEditable);
+
+private:
+
+ QVector<QColor> m_colors;
+ QString m_strName;
+ bool m_fIsEditable;
+};
+
+/*********************************************************************************************************************************
+* UISoftKeyboardWidget definition. *
+*********************************************************************************************************************************/
+
+/** The container widget for keyboard keys. It also handles all the keyboard related events. paintEvent of this class
+ * handles drawing of the soft keyboard. */
+class UISoftKeyboardWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+ enum Mode
+ {
+ Mode_LayoutEdit,
+ Mode_Keyboard,
+ Mode_Max
+ };
+
+signals:
+
+ void sigStatusBarMessage(const QString &strMessage);
+ void sigPutKeyboardSequence(QVector<LONG> sequence);
+ void sigPutUsageCodesPress(QVector<QPair<LONG, LONG> > sequence);
+ void sigPutUsageCodesRelease(QVector<QPair<LONG, LONG> > sequence);
+ void sigCurrentLayoutChange();
+ void sigKeyToEdit(UISoftKeyboardKey* pKey);
+ void sigCurrentColorThemeChanged();
+ void sigOptionsChanged();
+
+public:
+
+ UISoftKeyboardWidget(QWidget *pParent = 0);
+
+ virtual QSize minimumSizeHint() const RT_OVERRIDE;
+ virtual QSize sizeHint() const RT_OVERRIDE;
+ void keyStateChange(UISoftKeyboardKey* pKey);
+ void loadLayouts();
+
+ void setCurrentLayout(const QUuid &layoutUid);
+ UISoftKeyboardLayout *currentLayout();
+
+ QStringList layoutNameList() const;
+ QList<QUuid> layoutUidList() const;
+ const QVector<UISoftKeyboardPhysicalLayout> &physicalLayouts() const;
+ void deleteCurrentLayout();
+ void toggleEditMode(bool fIsEditMode);
+
+ void saveCurentLayoutToFile();
+ void copyCurentLayout();
+ float layoutAspectRatio();
+
+ bool hideOSMenuKeys() const;
+ void setHideOSMenuKeys(bool fHide);
+
+ bool hideNumPad() const;
+ void setHideNumPad(bool fHide);
+
+ bool hideMultimediaKeys() const;
+ void setHideMultimediaKeys(bool fHide);
+
+ QColor color(KeyboardColorType enmColorType) const;
+ void setColor(KeyboardColorType ennmColorType, const QColor &color);
+
+ QStringList colorsToStringList(const QString &strColorThemeName);
+ void colorsFromStringList(const QString &strColorThemeName, const QStringList &colorStringList);
+
+ /** Unlike modifier and ordinary keys we update the state of the Lock keys thru event singals we receieve
+ * from the guest OS. Parameter f???State is true if the corresponding key is locked. */
+ void updateLockKeyStates(bool fCapsLockState, bool fNumLockState, bool fScrollLockState);
+ void reset();
+
+ QStringList colorThemeNames() const;
+ QString currentColorThemeName() const;
+ void setColorThemeByName(const QString &strColorThemeName);
+ void parentDialogDeactivated();
+ bool isColorThemeEditable() const;
+ /** Returns a list of layout names that have been edited but not yet saved to a file. */
+ QStringList unsavedLayoutsNameList() const;
+
+protected:
+
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ virtual void mouseReleaseEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ virtual void mouseMoveEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ void addLayout(const UISoftKeyboardLayout &newLayout);
+ void setNewMinimumSize(const QSize &size);
+ void setInitialSize(int iWidth, int iHeight);
+ /** Searches for the key which contains the position of the @p pEvent and returns it if found. */
+ UISoftKeyboardKey *keyUnderMouse(QMouseEvent *pEvent);
+ UISoftKeyboardKey *keyUnderMouse(const QPoint &point);
+ void handleKeyPress(UISoftKeyboardKey *pKey);
+ void handleKeyRelease(UISoftKeyboardKey *pKey);
+ /** Sends usage id/page to API when a modifier key is right clicked. useful for testing and things like
+ * Window key press for start menu opening. This works orthogonal to left clicks.*/
+ void modifierKeyPressRelease(UISoftKeyboardKey *pKey, bool fRelease);
+ bool loadPhysicalLayout(const QString &strLayoutFileName, KeyboardRegion keyboardRegion = KeyboardRegion_Main);
+ bool loadKeyboardLayout(const QString &strLayoutName);
+ void prepareObjects();
+ void prepareColorThemes();
+ UISoftKeyboardPhysicalLayout *findPhysicalLayout(const QUuid &uuid);
+ /** Sets m_pKeyBeingEdited. */
+ void setKeyBeingEdited(UISoftKeyboardKey *pKey);
+ bool layoutByNameExists(const QString &strName) const;
+
+ /** Looks under the default keyboard layout folder and add the file names to the fileList. */
+ void lookAtDefaultLayoutFolder(QStringList &fileList);
+ UISoftKeyboardColorTheme *colorTheme(const QString &strColorThemeName);
+ void showKeyTooltip(UISoftKeyboardKey *pKey);
+
+ UISoftKeyboardKey *m_pKeyUnderMouse;
+ UISoftKeyboardKey *m_pKeyBeingEdited;
+
+ UISoftKeyboardKey *m_pKeyPressed;
+ UISoftKeyboardColorTheme *m_currentColorTheme;
+ QVector<UISoftKeyboardColorTheme> m_colorThemes;
+ QVector<UISoftKeyboardKey*> m_pressedModifiers;
+ QVector<UISoftKeyboardPhysicalLayout> m_physicalLayouts;
+ UISoftKeyboardPhysicalLayout m_numPadLayout;
+ UISoftKeyboardPhysicalLayout m_multiMediaKeysLayout;
+ QMap<QUuid, UISoftKeyboardLayout> m_layouts;
+ QUuid m_uCurrentLayoutId;
+ /** Key is the key position as read from the layout and value is the message we show as mouse hovers over the key. */
+ QMap<int, QString> m_keyTooltips;
+
+ QSize m_minimumSize;
+ float m_fScaleFactorX;
+ float m_fScaleFactorY;
+ int m_iInitialHeight;
+ /** This is the width of the keyboard including the numpad but without m_iInitialWidthNoNumPad */
+ int m_iInitialWidth;
+ int m_iInitialWidthNoNumPad;
+ /** This widt is added while drawing the keyboard not to key geometries. */
+ int m_iBeforeNumPadWidth;
+ int m_iXSpacing;
+ int m_iYSpacing;
+ int m_iLeftMargin;
+ int m_iTopMargin;
+ int m_iRightMargin;
+ int m_iBottomMargin;
+ Mode m_enmMode;
+ bool m_fHideOSMenuKeys;
+ bool m_fHideNumPad;
+ bool m_fHideMultimediaKeys;
+};
+
+/*********************************************************************************************************************************
+* UIPhysicalLayoutReader definition. *
+*********************************************************************************************************************************/
+
+class UIPhysicalLayoutReader
+{
+
+public:
+
+ bool parseXMLFile(const QString &strFileName, UISoftKeyboardPhysicalLayout &physicalLayout);
+ static QVector<QPointF> computeKeyVertices(const UISoftKeyboardKey &key);
+
+private:
+
+ void parseKey(UISoftKeyboardRow &row);
+ void parseRow(int iDefaultWidth, int iDefaultHeight, QVector<UISoftKeyboardRow> &rows);
+ /** Parses the horizontal space between keys. */
+ void parseKeySpace(UISoftKeyboardRow &row);
+ void parseCutout(UISoftKeyboardKey &key);
+
+ QXmlStreamReader m_xmlReader;
+};
+
+/*********************************************************************************************************************************
+* UIKeyboardLayoutReader definition. *
+*********************************************************************************************************************************/
+
+class UIKeyboardLayoutReader
+{
+
+public:
+
+ bool parseFile(const QString &strFileName, UISoftKeyboardLayout &layout);
+
+private:
+
+ void parseKey(UISoftKeyboardLayout &layout);
+ QXmlStreamReader m_xmlReader;
+ /** Map key is the key position and the value is the captions of the key. */
+};
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardStatusBarWidget definition. *
+*********************************************************************************************************************************/
+
+class UISoftKeyboardStatusBarWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigShowHideSidePanel();
+ void sigShowSettingWidget();
+ void sigResetKeyboard();
+ void sigHelpButtonPressed();
+
+public:
+
+ UISoftKeyboardStatusBarWidget(QWidget *pParent = 0);
+ void updateLayoutNameInStatusBar(const QString &strMessage);
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ void prepareObjects();
+ QToolButton *m_pLayoutListButton;
+ QToolButton *m_pSettingsButton;
+ QToolButton *m_pResetButton;
+ QToolButton *m_pHelpButton;
+ QLabel *m_pMessageLabel;
+};
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardSettingsWidget definition. *
+*********************************************************************************************************************************/
+
+class UISoftKeyboardSettingsWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigHideNumPad(bool fHide);
+ void sigHideOSMenuKeys(bool fHide);
+ void sigHideMultimediaKeys(bool fHide);
+ void sigColorCellClicked(int iColorRow);
+ void sigCloseSettingsWidget();
+ void sigColorThemeSelectionChanged(const QString &strColorThemeName);
+
+public:
+
+ UISoftKeyboardSettingsWidget(QWidget *pParent = 0);
+ void setHideOSMenuKeys(bool fHide);
+ void setHideNumPad(bool fHide);
+ void setHideMultimediaKeys(bool fHide);
+ void setColorSelectionButtonBackgroundAndTooltip(KeyboardColorType enmColorType, const QColor &color, bool fIsColorEditable);
+ void setColorThemeNames(const QStringList &colorThemeNames);
+ void setCurrentColorThemeName(const QString &strColorThemeName);
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ void sltColorSelectionButtonClicked();
+
+private:
+
+ void prepareObjects();
+
+ QCheckBox *m_pHideNumPadCheckBox;
+ QCheckBox *m_pShowOsMenuButtonsCheckBox;
+ QCheckBox *m_pHideMultimediaKeysCheckBox;
+ QGroupBox *m_pColorThemeGroupBox;
+ QComboBox *m_pColorThemeComboBox;
+ QLabel *m_pTitleLabel;
+ QToolButton *m_pCloseButton;
+ QVector<ColorSelectLabelButton> m_colorSelectLabelsButtons;
+};
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardColorButton implementation. *
+*********************************************************************************************************************************/
+
+
+UISoftKeyboardColorButton::UISoftKeyboardColorButton(KeyboardColorType enmColorType, QWidget *pParent /*= 0 */)
+ :QPushButton(pParent)
+ , m_enmColorType(enmColorType){}
+
+KeyboardColorType UISoftKeyboardColorButton::colorType() const
+{
+ return m_enmColorType;
+}
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardPhysicalLayout implementation. *
+*********************************************************************************************************************************/
+
+UISoftKeyboardPhysicalLayout::UISoftKeyboardPhysicalLayout()
+ :m_iDefaultKeyWidth(50)
+{
+}
+
+void UISoftKeyboardPhysicalLayout::setName(const QString &strName)
+{
+ m_strName = strName;
+}
+
+const QString &UISoftKeyboardPhysicalLayout::name() const
+{
+ return m_strName;
+}
+
+void UISoftKeyboardPhysicalLayout::setFileName(const QString &strName)
+{
+ m_strFileName = strName;
+}
+
+const QString &UISoftKeyboardPhysicalLayout::fileName() const
+{
+ return m_strFileName;
+}
+
+void UISoftKeyboardPhysicalLayout::setUid(const QUuid &uid)
+{
+ m_uId = uid;
+}
+
+const QUuid &UISoftKeyboardPhysicalLayout::uid() const
+{
+ return m_uId;
+}
+
+const QVector<UISoftKeyboardRow> &UISoftKeyboardPhysicalLayout::rows() const
+{
+ return m_rows;
+}
+
+QVector<UISoftKeyboardRow> &UISoftKeyboardPhysicalLayout::rows()
+{
+ return m_rows;
+}
+
+void UISoftKeyboardPhysicalLayout::setLockKey(int iKeyPosition, UISoftKeyboardKey *pKey)
+{
+ m_lockKeys[iKeyPosition] = pKey;
+}
+
+void UISoftKeyboardPhysicalLayout::updateLockKeyStates(bool fCapsLockState, bool fNumLockState, bool fScrollLockState)
+{
+ updateLockKeyState(fCapsLockState, m_lockKeys.value(iCapsLockPosition, 0));
+ updateLockKeyState(fNumLockState, m_lockKeys.value(iNumLockPosition, 0));
+ updateLockKeyState(fScrollLockState, m_lockKeys.value(iScrollLockPosition, 0));
+}
+
+void UISoftKeyboardPhysicalLayout::setDefaultKeyWidth(int iDefaultKeyWidth)
+{
+ m_iDefaultKeyWidth = iDefaultKeyWidth;
+}
+
+int UISoftKeyboardPhysicalLayout::defaultKeyWidth() const
+{
+ return m_iDefaultKeyWidth;
+}
+
+void UISoftKeyboardPhysicalLayout::reset()
+{
+ for (int i = 0; i < m_rows.size(); ++i)
+ {
+ for (int j = 0; j < m_rows[i].keys().size(); ++j)
+ {
+ m_rows[i].keys()[j].reset();
+ }
+ }
+}
+
+int UISoftKeyboardPhysicalLayout::totalHeight() const
+{
+ int iHeight = 0;
+ for (int i = 0; i < m_rows.size(); ++i)
+ iHeight += m_rows[i].totalHeight();
+ return iHeight;
+}
+
+void UISoftKeyboardPhysicalLayout::updateLockKeyState(bool fLockState, UISoftKeyboardKey *pKey)
+{
+ if (!pKey)
+ return;
+ pKey->updateLockState(fLockState);
+}
+
+/*********************************************************************************************************************************
+* UIKeyboardLayoutEditor implementation. *
+*********************************************************************************************************************************/
+
+UIKeyboardLayoutEditor::UIKeyboardLayoutEditor(QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_pEditorLayout(0)
+ , m_pGoBackButton(0)
+ , m_pSelectedKeyGroupBox(0)
+ , m_pCaptionEditGroupBox(0)
+ , m_pPhysicalLayoutCombo(0)
+ , m_pTitleLabel(0)
+ , m_pPhysicalLayoutLabel(0)
+ , m_pLayoutNameLabel(0)
+ , m_pLayoutNativeNameLabel(0)
+ , m_pScanCodeLabel(0)
+ , m_pPositionLabel(0)
+ , m_pBaseCaptionLabel(0)
+ , m_pShiftCaptionLabel(0)
+ , m_pAltGrCaptionLabel(0)
+ , m_pShiftAltGrCaptionLabel(0)
+ , m_pLayoutNameEdit(0)
+ , m_pLayoutNativeNameEdit(0)
+ , m_pScanCodeEdit(0)
+ , m_pPositionEdit(0)
+ , m_pBaseCaptionEdit(0)
+ , m_pShiftCaptionEdit(0)
+ , m_pAltGrCaptionEdit(0)
+ , m_pShiftAltGrCaptionEdit(0)
+ , m_pKey(0)
+ , m_pLayout(0)
+{
+ setAutoFillBackground(true);
+ prepareObjects();
+}
+
+void UIKeyboardLayoutEditor::setKey(UISoftKeyboardKey *pKey)
+{
+ if (m_pKey == pKey || !m_pLayout)
+ return;
+ /* First apply the pending changes to the key that has been edited: */
+ if (m_pKey)
+ {
+ UIKeyCaptions captions = m_pLayout->keyCaptions(m_pKey->position());
+ if (captions.m_strBase != m_pBaseCaptionEdit->text() ||
+ captions.m_strShift != m_pShiftCaptionEdit->text() ||
+ captions.m_strAltGr != m_pAltGrCaptionEdit->text() ||
+ captions.m_strShiftAltGr != m_pShiftAltGrCaptionEdit->text())
+ m_pLayout->addOrUpdateUIKeyCaptions(m_pKey->position(),
+ UIKeyCaptions(m_pBaseCaptionEdit->text(),
+ m_pShiftCaptionEdit->text(),
+ m_pAltGrCaptionEdit->text(),
+ m_pShiftAltGrCaptionEdit->text()));
+ }
+ m_pKey = pKey;
+ if (m_pSelectedKeyGroupBox)
+ m_pSelectedKeyGroupBox->setEnabled(m_pKey);
+ if (!m_pKey)
+ {
+ resetKeyWidgets();
+ return;
+ }
+ if (m_pScanCodeEdit)
+ m_pScanCodeEdit->setText(QString::number(m_pKey->scanCode(), 16));
+ if (m_pPositionEdit)
+ m_pPositionEdit->setText(QString::number(m_pKey->position()));
+ UIKeyCaptions captions = m_pLayout->keyCaptions(m_pKey->position());
+ if (m_pBaseCaptionEdit)
+ m_pBaseCaptionEdit->setText(captions.m_strBase);
+ if (m_pShiftCaptionEdit)
+ m_pShiftCaptionEdit->setText(captions.m_strShift);
+ if (m_pAltGrCaptionEdit)
+ m_pAltGrCaptionEdit->setText(captions.m_strAltGr);
+ if (m_pShiftAltGrCaptionEdit)
+ m_pShiftAltGrCaptionEdit->setText(captions.m_strShiftAltGr);
+ m_pBaseCaptionEdit->setFocus();
+}
+
+void UIKeyboardLayoutEditor::setLayoutToEdit(UISoftKeyboardLayout *pLayout)
+{
+ if (m_pLayout == pLayout)
+ return;
+
+ m_pLayout = pLayout;
+ if (!m_pLayout)
+ reset();
+
+ if (m_pLayoutNameEdit)
+ m_pLayoutNameEdit->setText(m_pLayout ? m_pLayout->name() : QString());
+
+ if (m_pLayoutNativeNameEdit)
+ m_pLayoutNativeNameEdit->setText(m_pLayout ? m_pLayout->nativeName() : QString());
+
+ if (m_pPhysicalLayoutCombo && m_pLayout)
+ {
+ int iIndex = m_pPhysicalLayoutCombo->findData(m_pLayout->physicalLayoutUuid());
+ if (iIndex != -1)
+ m_pPhysicalLayoutCombo->setCurrentIndex(iIndex);
+ }
+ update();
+}
+
+void UIKeyboardLayoutEditor::setPhysicalLayoutList(const QVector<UISoftKeyboardPhysicalLayout> &physicalLayouts)
+{
+ if (!m_pPhysicalLayoutCombo)
+ return;
+ m_pPhysicalLayoutCombo->clear();
+ foreach (const UISoftKeyboardPhysicalLayout &physicalLayout, physicalLayouts)
+ m_pPhysicalLayoutCombo->addItem(physicalLayout.name(), physicalLayout.uid());
+}
+
+void UIKeyboardLayoutEditor::retranslateUi()
+{
+ if (m_pTitleLabel)
+ m_pTitleLabel->setText(UISoftKeyboard::tr("Layout Editor"));
+ if (m_pGoBackButton)
+ {
+ m_pGoBackButton->setToolTip(UISoftKeyboard::tr("Return Back to Layout List"));
+ m_pGoBackButton->setText(UISoftKeyboard::tr("Back to Layout List"));
+ }
+ if (m_pPhysicalLayoutLabel)
+ m_pPhysicalLayoutLabel->setText(UISoftKeyboard::tr("Physical Layout"));
+ if (m_pLayoutNameLabel)
+ m_pLayoutNameLabel->setText(UISoftKeyboard::tr("English Name"));
+ if (m_pLayoutNameEdit)
+ m_pLayoutNameEdit->setToolTip(UISoftKeyboard::tr("Name of the Layout in English"));
+ if (m_pLayoutNativeNameLabel)
+ m_pLayoutNativeNameLabel->setText(UISoftKeyboard::tr("Native Language Name"));
+ if (m_pLayoutNativeNameEdit)
+ m_pLayoutNativeNameEdit->setToolTip(UISoftKeyboard::tr("Name of the Layout in the native Language"));
+ if (m_pScanCodeLabel)
+ m_pScanCodeLabel->setText(UISoftKeyboard::tr("Scan Code"));
+ if (m_pScanCodeEdit)
+ m_pScanCodeEdit->setToolTip(UISoftKeyboard::tr("The scan code the key produces. Not editable"));
+ if (m_pPositionLabel)
+ m_pPositionLabel->setText(UISoftKeyboard::tr("Position"));
+ if (m_pPositionEdit)
+ m_pPositionEdit->setToolTip(UISoftKeyboard::tr("The physical position of the key. Not editable"));
+ if (m_pBaseCaptionLabel)
+ m_pBaseCaptionLabel->setText(UISoftKeyboard::tr("Base"));
+ if (m_pShiftCaptionLabel)
+ m_pShiftCaptionLabel->setText(UISoftKeyboard::tr("Shift"));
+ if (m_pAltGrCaptionLabel)
+ m_pAltGrCaptionLabel->setText(UISoftKeyboard::tr("AltGr"));
+ if (m_pShiftAltGrCaptionLabel)
+ m_pShiftAltGrCaptionLabel->setText(UISoftKeyboard::tr("ShiftAltGr"));
+ if (m_pCaptionEditGroupBox)
+ m_pCaptionEditGroupBox->setTitle(UISoftKeyboard::tr("Captions"));
+ if (m_pSelectedKeyGroupBox)
+ m_pSelectedKeyGroupBox->setTitle(UISoftKeyboard::tr("Selected Key"));
+}
+
+void UIKeyboardLayoutEditor::sltCaptionsUpdate()
+{
+ if (!m_pKey || !m_pLayout)
+ return;
+ m_pLayout->addOrUpdateUIKeyCaptions(m_pKey->position(),
+ UIKeyCaptions(m_pBaseCaptionEdit->text(),
+ m_pShiftCaptionEdit->text(),
+ m_pAltGrCaptionEdit->text(),
+ m_pShiftAltGrCaptionEdit->text()));
+ emit sigUIKeyCaptionsEdited(m_pKey);
+}
+
+void UIKeyboardLayoutEditor::sltPhysicalLayoutChanged()
+{
+ if (!m_pPhysicalLayoutCombo || !m_pLayout)
+ return;
+ QUuid currentData = m_pPhysicalLayoutCombo->currentData().toUuid();
+ if (!currentData.isNull())
+ m_pLayout->setPhysicalLayoutUuid(currentData);
+ emit sigLayoutEdited();
+}
+
+void UIKeyboardLayoutEditor::sltLayoutNameChanged(const QString &strName)
+{
+ if (!m_pLayout || m_pLayout->name() == strName)
+ return;
+ m_pLayout->setName(strName);
+ emit sigLayoutEdited();
+}
+
+void UIKeyboardLayoutEditor::sltLayoutNativeNameChanged(const QString &strNativeName)
+{
+ if (!m_pLayout || m_pLayout->nativeName() == strNativeName)
+ return;
+ m_pLayout->setNativeName(strNativeName);
+ emit sigLayoutEdited();
+}
+
+void UIKeyboardLayoutEditor::prepareObjects()
+{
+ m_pEditorLayout = new QGridLayout;
+ if (!m_pEditorLayout)
+ return;
+ setLayout(m_pEditorLayout);
+
+ QHBoxLayout *pTitleLayout = new QHBoxLayout;
+ m_pGoBackButton = new QToolButton;
+ m_pGoBackButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ m_pGoBackButton->setIcon(UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_ArrowBack));
+ m_pGoBackButton->setAutoRaise(true);
+ m_pEditorLayout->addWidget(m_pGoBackButton, 0, 0, 1, 1);
+ connect(m_pGoBackButton, &QToolButton::clicked, this, &UIKeyboardLayoutEditor::sigGoBackButton);
+ m_pTitleLabel = new QLabel;
+ pTitleLayout->addWidget(m_pTitleLabel);
+ pTitleLayout->addStretch(2);
+ pTitleLayout->addWidget(m_pGoBackButton);
+ m_pEditorLayout->addLayout(pTitleLayout, 0, 0, 1, 2);
+
+ m_pLayoutNativeNameLabel = new QLabel;
+ m_pLayoutNativeNameEdit = new QLineEdit;
+ m_pLayoutNativeNameLabel->setBuddy(m_pLayoutNativeNameEdit);
+ m_pEditorLayout->addWidget(m_pLayoutNativeNameLabel, 2, 0, 1, 1);
+ m_pEditorLayout->addWidget(m_pLayoutNativeNameEdit, 2, 1, 1, 1);
+ connect(m_pLayoutNativeNameEdit, &QLineEdit::textChanged, this, &UIKeyboardLayoutEditor::sltLayoutNativeNameChanged);
+
+ m_pLayoutNameLabel = new QLabel;
+ m_pLayoutNameEdit = new QLineEdit;
+ m_pLayoutNameLabel->setBuddy(m_pLayoutNameEdit);
+ m_pEditorLayout->addWidget(m_pLayoutNameLabel, 3, 0, 1, 1);
+ m_pEditorLayout->addWidget(m_pLayoutNameEdit, 3, 1, 1, 1);
+ connect(m_pLayoutNameEdit, &QLineEdit::textChanged, this, &UIKeyboardLayoutEditor::sltLayoutNameChanged);
+
+
+ m_pPhysicalLayoutLabel = new QLabel;
+ m_pPhysicalLayoutCombo = new QComboBox;
+ m_pPhysicalLayoutLabel->setBuddy(m_pPhysicalLayoutCombo);
+ m_pEditorLayout->addWidget(m_pPhysicalLayoutLabel, 4, 0, 1, 1);
+ m_pEditorLayout->addWidget(m_pPhysicalLayoutCombo, 4, 1, 1, 1);
+ connect(m_pPhysicalLayoutCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UIKeyboardLayoutEditor::sltPhysicalLayoutChanged);
+
+ m_pSelectedKeyGroupBox = new QGroupBox;
+ m_pSelectedKeyGroupBox->setEnabled(false);
+
+ m_pEditorLayout->addWidget(m_pSelectedKeyGroupBox, 5, 0, 1, 2);
+ QGridLayout *pSelectedKeyLayout = new QGridLayout(m_pSelectedKeyGroupBox);
+ pSelectedKeyLayout->setSpacing(0);
+ pSelectedKeyLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pScanCodeLabel = new QLabel;
+ m_pScanCodeEdit = new QLineEdit;
+ m_pScanCodeLabel->setBuddy(m_pScanCodeEdit);
+ m_pScanCodeEdit->setEnabled(false);
+ pSelectedKeyLayout->addWidget(m_pScanCodeLabel, 0, 0);
+ pSelectedKeyLayout->addWidget(m_pScanCodeEdit, 0, 1);
+
+ m_pPositionLabel= new QLabel;
+ m_pPositionEdit = new QLineEdit;
+ m_pPositionEdit->setEnabled(false);
+ m_pPositionLabel->setBuddy(m_pPositionEdit);
+ pSelectedKeyLayout->addWidget(m_pPositionLabel, 1, 0);
+ pSelectedKeyLayout->addWidget(m_pPositionEdit, 1, 1);
+
+ QWidget *pCaptionEditor = prepareKeyCaptionEditWidgets();
+ if (pCaptionEditor)
+ pSelectedKeyLayout->addWidget(pCaptionEditor, 2, 0, 2, 2);
+
+ QSpacerItem *pSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding);
+ if (pSpacer)
+ pSelectedKeyLayout->addItem(pSpacer, 4, 1);
+
+ retranslateUi();
+}
+
+QWidget *UIKeyboardLayoutEditor::prepareKeyCaptionEditWidgets()
+{
+ m_pCaptionEditGroupBox = new QGroupBox;
+ if (!m_pCaptionEditGroupBox)
+ return 0;
+ m_pCaptionEditGroupBox->setFlat(false);
+ QGridLayout *pCaptionEditorLayout = new QGridLayout(m_pCaptionEditGroupBox);
+ pCaptionEditorLayout->setSpacing(0);
+ pCaptionEditorLayout->setContentsMargins(0, 0, 0, 0);
+
+ if (!pCaptionEditorLayout)
+ return 0;
+
+ m_pBaseCaptionLabel = new QLabel;
+ m_pBaseCaptionEdit = new QLineEdit;
+ m_pBaseCaptionLabel->setBuddy(m_pBaseCaptionEdit);
+ pCaptionEditorLayout->addWidget(m_pBaseCaptionLabel, 0, 0);
+ pCaptionEditorLayout->addWidget(m_pBaseCaptionEdit, 0, 1);
+ connect(m_pBaseCaptionEdit, &QLineEdit::textChanged, this, &UIKeyboardLayoutEditor::sltCaptionsUpdate);
+
+ m_pShiftCaptionLabel = new QLabel;
+ m_pShiftCaptionEdit = new QLineEdit;
+ m_pShiftCaptionLabel->setBuddy(m_pShiftCaptionEdit);
+ pCaptionEditorLayout->addWidget(m_pShiftCaptionLabel, 1, 0);
+ pCaptionEditorLayout->addWidget(m_pShiftCaptionEdit, 1, 1);
+ connect(m_pShiftCaptionEdit, &QLineEdit::textChanged, this, &UIKeyboardLayoutEditor::sltCaptionsUpdate);
+
+ m_pAltGrCaptionLabel = new QLabel;
+ m_pAltGrCaptionEdit = new QLineEdit;
+ m_pAltGrCaptionLabel->setBuddy(m_pAltGrCaptionEdit);
+ pCaptionEditorLayout->addWidget(m_pAltGrCaptionLabel, 2, 0);
+ pCaptionEditorLayout->addWidget(m_pAltGrCaptionEdit, 2, 1);
+ connect(m_pAltGrCaptionEdit, &QLineEdit::textChanged, this, &UIKeyboardLayoutEditor::sltCaptionsUpdate);
+
+ m_pShiftAltGrCaptionLabel = new QLabel;
+ m_pShiftAltGrCaptionEdit = new QLineEdit;
+ m_pShiftAltGrCaptionLabel->setBuddy(m_pShiftAltGrCaptionEdit);
+ pCaptionEditorLayout->addWidget(m_pShiftAltGrCaptionLabel, 3, 0);
+ pCaptionEditorLayout->addWidget(m_pShiftAltGrCaptionEdit, 3, 1);
+ connect(m_pShiftAltGrCaptionEdit, &QLineEdit::textChanged, this, &UIKeyboardLayoutEditor::sltCaptionsUpdate);
+
+ QSpacerItem *pSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding);
+ if (pSpacer)
+ pCaptionEditorLayout->addItem(pSpacer, 4, 1);
+ return m_pCaptionEditGroupBox;
+}
+
+void UIKeyboardLayoutEditor::reset()
+{
+ if (m_pLayoutNameEdit)
+ m_pLayoutNameEdit->clear();
+ resetKeyWidgets();
+}
+
+void UIKeyboardLayoutEditor::resetKeyWidgets()
+{
+ if (m_pScanCodeEdit)
+ m_pScanCodeEdit->clear();
+ if (m_pPositionEdit)
+ m_pPositionEdit->clear();
+ if (m_pBaseCaptionEdit)
+ m_pBaseCaptionEdit->clear();
+ if (m_pShiftCaptionEdit)
+ m_pShiftCaptionEdit->clear();
+ if (m_pAltGrCaptionEdit)
+ m_pAltGrCaptionEdit->clear();
+ if (m_pShiftAltGrCaptionEdit)
+ m_pShiftAltGrCaptionEdit->clear();
+}
+
+/*********************************************************************************************************************************
+* UILayoutSelector implementation. *
+*********************************************************************************************************************************/
+
+UILayoutSelector::UILayoutSelector(QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLayoutListWidget(0)
+ , m_pApplyLayoutButton(0)
+ , m_pEditLayoutButton(0)
+ , m_pCopyLayoutButton(0)
+ , m_pSaveLayoutButton(0)
+ , m_pDeleteLayoutButton(0)
+ , m_pTitleLabel(0)
+ , m_pCloseButton(0)
+{
+ prepareObjects();
+}
+
+void UILayoutSelector::setCurrentLayout(const QUuid &layoutUid)
+{
+ if (!m_pLayoutListWidget)
+ return;
+ if (layoutUid.isNull())
+ {
+ m_pLayoutListWidget->selectionModel()->clear();
+ return;
+ }
+ QListWidgetItem *pFoundItem = 0;
+ for (int i = 0; i < m_pLayoutListWidget->count() && !pFoundItem; ++i)
+ {
+ QListWidgetItem *pItem = m_pLayoutListWidget->item(i);
+ if (!pItem)
+ continue;
+ if (pItem->data(Qt::UserRole).toUuid() == layoutUid)
+ pFoundItem = pItem;
+ }
+ if (!pFoundItem)
+ return;
+ if (pFoundItem == m_pLayoutListWidget->currentItem())
+ return;
+ m_pLayoutListWidget->blockSignals(true);
+ m_pLayoutListWidget->setCurrentItem(pFoundItem);
+ m_pLayoutListWidget->blockSignals(false);
+}
+
+void UILayoutSelector::setCurrentLayoutIsEditable(bool fEditable)
+{
+ if (m_pEditLayoutButton)
+ m_pEditLayoutButton->setEnabled(fEditable);
+ if (m_pSaveLayoutButton)
+ m_pSaveLayoutButton->setEnabled(fEditable);
+ if (m_pDeleteLayoutButton)
+ m_pDeleteLayoutButton->setEnabled(fEditable);
+}
+
+void UILayoutSelector::setLayoutList(const QStringList &layoutNames, QList<QUuid> layoutUidList)
+{
+ if (!m_pLayoutListWidget || layoutNames.size() != layoutUidList.size())
+ return;
+ QUuid currentItemUid;
+ if (m_pLayoutListWidget->currentItem())
+ currentItemUid = m_pLayoutListWidget->currentItem()->data(Qt::UserRole).toUuid();
+ m_pLayoutListWidget->blockSignals(true);
+ m_pLayoutListWidget->clear();
+ for (int i = 0; i < layoutNames.size(); ++i)
+ {
+ QListWidgetItem *pItem = new QListWidgetItem(layoutNames[i], m_pLayoutListWidget);
+ pItem->setData(Qt::UserRole, layoutUidList[i]);
+ m_pLayoutListWidget->addItem(pItem);
+ if (layoutUidList[i] == currentItemUid)
+ m_pLayoutListWidget->setCurrentItem(pItem);
+ }
+ m_pLayoutListWidget->sortItems();
+ m_pLayoutListWidget->blockSignals(false);
+}
+
+void UILayoutSelector::retranslateUi()
+{
+ if (m_pApplyLayoutButton)
+ m_pApplyLayoutButton->setToolTip(UISoftKeyboard::tr("Use the selected layout"));
+ if (m_pEditLayoutButton)
+ m_pEditLayoutButton->setToolTip(UISoftKeyboard::tr("Edit the selected layout"));
+ if (m_pDeleteLayoutButton)
+ m_pDeleteLayoutButton->setToolTip(UISoftKeyboard::tr("Delete the selected layout"));
+ if (m_pCopyLayoutButton)
+ m_pCopyLayoutButton->setToolTip(UISoftKeyboard::tr("Copy the selected layout"));
+ if (m_pSaveLayoutButton)
+ m_pSaveLayoutButton->setToolTip(UISoftKeyboard::tr("Save the selected layout into File"));
+ if (m_pTitleLabel)
+ m_pTitleLabel->setText(UISoftKeyboard::tr("Layout List"));
+ if (m_pCloseButton)
+ {
+ m_pCloseButton->setToolTip(UISoftKeyboard::tr("Close the layout list"));
+ m_pCloseButton->setText("Close");
+ }
+}
+
+void UILayoutSelector::prepareObjects()
+{
+ QVBoxLayout *pLayout = new QVBoxLayout;
+ if (!pLayout)
+ return;
+ pLayout->setSpacing(0);
+ setLayout(pLayout);
+
+ QHBoxLayout *pTitleLayout = new QHBoxLayout;
+ m_pCloseButton = new QToolButton;
+ m_pCloseButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ m_pCloseButton->setIcon(UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_DialogCancel));
+ m_pCloseButton->setAutoRaise(true);
+ connect(m_pCloseButton, &QToolButton::clicked, this, &UILayoutSelector::sigCloseLayoutList);
+ m_pTitleLabel = new QLabel;
+ pTitleLayout->addWidget(m_pTitleLabel);
+ pTitleLayout->addStretch(2);
+ pTitleLayout->addWidget(m_pCloseButton);
+ pLayout->addLayout(pTitleLayout);
+
+ m_pLayoutListWidget = new QListWidget;
+ pLayout->addWidget(m_pLayoutListWidget);
+ m_pLayoutListWidget->setSortingEnabled(true);
+ connect(m_pLayoutListWidget, &QListWidget::currentItemChanged, this, &UILayoutSelector::sltCurrentItemChanged);
+
+ m_pLayoutListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+ QHBoxLayout *pButtonsLayout = new QHBoxLayout;
+ pLayout->addLayout(pButtonsLayout);
+
+ m_pEditLayoutButton = new QToolButton;
+ m_pEditLayoutButton->setIcon(UIIconPool::iconSet(":/soft_keyboard_layout_edit_16px.png", ":/soft_keyboard_layout_edit_disabled_16px.png"));
+ pButtonsLayout->addWidget(m_pEditLayoutButton);
+ connect(m_pEditLayoutButton, &QToolButton::clicked, this, &UILayoutSelector::sigShowLayoutEditor);
+
+ m_pCopyLayoutButton = new QToolButton;
+ m_pCopyLayoutButton->setIcon(UIIconPool::iconSet(":/soft_keyboard_layout_copy_16px.png", ":/soft_keyboard_layout_copy_disabled_16px.png"));
+ pButtonsLayout->addWidget(m_pCopyLayoutButton);
+ connect(m_pCopyLayoutButton, &QToolButton::clicked, this, &UILayoutSelector::sigCopyLayout);
+
+ m_pSaveLayoutButton = new QToolButton;
+ m_pSaveLayoutButton->setIcon(UIIconPool::iconSet(":/soft_keyboard_layout_save_16px.png", ":/soft_keyboard_layout_save_disabled_16px.png"));
+ pButtonsLayout->addWidget(m_pSaveLayoutButton);
+ connect(m_pSaveLayoutButton, &QToolButton::clicked, this, &UILayoutSelector::sigSaveLayout);
+
+ m_pDeleteLayoutButton = new QToolButton;
+ m_pDeleteLayoutButton->setIcon(UIIconPool::iconSet(":/soft_keyboard_layout_remove_16px.png", ":/soft_keyboard_layout_remove_disabled_16px.png"));
+ pButtonsLayout->addWidget(m_pDeleteLayoutButton);
+ connect(m_pDeleteLayoutButton, &QToolButton::clicked, this, &UILayoutSelector::sigDeleteLayout);
+
+ pButtonsLayout->addStretch(2);
+
+ retranslateUi();
+}
+
+void UILayoutSelector::sltCurrentItemChanged(QListWidgetItem *pCurrent, QListWidgetItem *pPrevious)
+{
+ Q_UNUSED(pPrevious);
+ if (!pCurrent)
+ return;
+ emit sigLayoutSelectionChanged(pCurrent->data(Qt::UserRole).toUuid());
+}
+
+/*********************************************************************************************************************************
+* UISoftKeyboardRow implementation. *
+*********************************************************************************************************************************/
+
+UISoftKeyboardRow::UISoftKeyboardRow()
+ : m_iDefaultWidth(0)
+ , m_iDefaultHeight(0)
+ , m_iSpaceHeightAfter(0)
+ , m_iLeftMargin(0)
+{
+}
+
+void UISoftKeyboardRow::setDefaultWidth(int iWidth)
+{
+ m_iDefaultWidth = iWidth;
+}
+
+int UISoftKeyboardRow::defaultWidth() const
+{
+ return m_iDefaultWidth;
+}
+
+int UISoftKeyboardRow::totalHeight() const
+{
+ int iMaxHeight = 0;
+ for (int i = 0; i < m_keys.size(); ++i)
+ iMaxHeight = qMax(iMaxHeight, m_keys[i].height());
+ return iMaxHeight + m_iSpaceHeightAfter;
+}
+
+void UISoftKeyboardRow::setDefaultHeight(int iHeight)
+{
+ m_iDefaultHeight = iHeight;
+}
+
+int UISoftKeyboardRow::defaultHeight() const
+{
+ return m_iDefaultHeight;
+}
+
+QVector<UISoftKeyboardKey> &UISoftKeyboardRow::keys()
+{
+ return m_keys;
+}
+
+const QVector<UISoftKeyboardKey> &UISoftKeyboardRow::keys() const
+{
+ return m_keys;
+}
+
+void UISoftKeyboardRow::setSpaceHeightAfter(int iSpace)
+{
+ m_iSpaceHeightAfter = iSpace;
+}
+
+int UISoftKeyboardRow::spaceHeightAfter() const
+{
+ return m_iSpaceHeightAfter;
+}
+
+int UISoftKeyboardRow::leftMargin() const
+{
+ return m_iLeftMargin;
+}
+
+void UISoftKeyboardRow::setLeftMargin(int iMargin)
+{
+ m_iLeftMargin = iMargin;
+}
+
+/*********************************************************************************************************************************
+* UISoftKeyboardKey implementation. *
+*********************************************************************************************************************************/
+
+UISoftKeyboardKey::UISoftKeyboardKey()
+ : m_enmType(KeyType_Ordinary)
+ , m_enmState(KeyState_NotPressed)
+ , m_iWidth(0)
+ , m_iHeight(0)
+ , m_iSpaceWidthAfter(0)
+ , m_scanCode(0)
+ , m_iCutoutWidth(0)
+ , m_iCutoutHeight(0)
+ , m_iCutoutCorner(-1)
+ , m_iPosition(0)
+ , m_pParentWidget(0)
+ , m_enmKeyboardRegion(KeyboardRegion_Main)
+ , m_fIsOSMenuKey(false)
+ , m_fCornerRadius(5.)
+{
+}
+
+const QRect UISoftKeyboardKey::keyGeometry() const
+{
+ return m_keyGeometry;
+}
+
+void UISoftKeyboardKey::setKeyGeometry(const QRect &rect)
+{
+ m_keyGeometry = rect;
+}
+
+
+void UISoftKeyboardKey::setWidth(int iWidth)
+{
+ m_iWidth = iWidth;
+}
+
+int UISoftKeyboardKey::width() const
+{
+ return m_iWidth;
+}
+
+void UISoftKeyboardKey::setHeight(int iHeight)
+{
+ m_iHeight = iHeight;
+}
+
+int UISoftKeyboardKey::height() const
+{
+ return m_iHeight;
+}
+
+void UISoftKeyboardKey::setScanCode(LONG scanCode)
+{
+ m_scanCode = scanCode;
+}
+
+LONG UISoftKeyboardKey::scanCode() const
+{
+ return m_scanCode;
+}
+
+void UISoftKeyboardKey::addScanCodePrefix(LONG scanCodePrefix)
+{
+ m_scanCodePrefix << scanCodePrefix;
+}
+
+void UISoftKeyboardKey::setSpaceWidthAfter(int iSpace)
+{
+ m_iSpaceWidthAfter = iSpace;
+}
+
+int UISoftKeyboardKey::spaceWidthAfter() const
+{
+ return m_iSpaceWidthAfter;
+}
+
+void UISoftKeyboardKey::setUsageId(LONG usageId)
+{
+ m_iUsageId = usageId;
+}
+
+void UISoftKeyboardKey::setUsagePage(LONG usagePage)
+{
+ m_iUsagePage = usagePage;
+}
+
+QPair<LONG, LONG> UISoftKeyboardKey::usagePageIdPair() const
+{
+ return QPair<LONG, LONG>(m_iUsageId, m_iUsagePage);
+}
+
+void UISoftKeyboardKey::setPosition(int iPosition)
+{
+ m_iPosition = iPosition;
+}
+
+int UISoftKeyboardKey::position() const
+{
+ return m_iPosition;
+}
+
+void UISoftKeyboardKey::setType(KeyType enmType)
+{
+ m_enmType = enmType;
+}
+
+KeyType UISoftKeyboardKey::type() const
+{
+ return m_enmType;
+}
+
+KeyboardRegion UISoftKeyboardKey::keyboardRegion() const
+{
+ return m_enmKeyboardRegion;
+}
+
+void UISoftKeyboardKey::setKeyboardRegion(KeyboardRegion enmRegion)
+{
+ m_enmKeyboardRegion = enmRegion;
+}
+
+void UISoftKeyboardKey::setCutout(int iCorner, int iWidth, int iHeight)
+{
+ m_iCutoutCorner = iCorner;
+ m_iCutoutWidth = iWidth;
+ m_iCutoutHeight = iHeight;
+}
+
+KeyState UISoftKeyboardKey::state() const
+{
+ return m_enmState;
+}
+
+void UISoftKeyboardKey::setState(KeyState state)
+{
+ m_enmState = state;
+}
+
+void UISoftKeyboardKey::setStaticCaption(const QString &strCaption)
+{
+ m_strStaticCaption = strCaption;
+}
+
+const QString &UISoftKeyboardKey::staticCaption() const
+{
+ return m_strStaticCaption;
+}
+
+void UISoftKeyboardKey::setImageByName(const QString &strImageFileName)
+{
+ if (strImageFileName.isEmpty())
+ return;
+ m_image = QImage(QString(":/%1").arg(strImageFileName));
+}
+
+const QImage &UISoftKeyboardKey::image() const
+{
+ return m_image;
+}
+
+void UISoftKeyboardKey::setParentWidget(UISoftKeyboardWidget* pParent)
+{
+ m_pParentWidget = pParent;
+}
+
+void UISoftKeyboardKey::setIsOSMenuKey(bool fFlag)
+{
+ m_fIsOSMenuKey = fFlag;
+}
+
+bool UISoftKeyboardKey::isOSMenuKey() const
+{
+ return m_fIsOSMenuKey;
+}
+
+void UISoftKeyboardKey::release()
+{
+ /* Lock key states are controlled by the event signals we get from the guest OS. See updateLockKeyState function: */
+ if (m_enmType != KeyType_Lock)
+ updateState(false);
+}
+
+void UISoftKeyboardKey::press()
+{
+ /* Lock key states are controlled by the event signals we get from the guest OS. See updateLockKeyState function: */
+ if (m_enmType != KeyType_Lock)
+ updateState(true);
+}
+
+void UISoftKeyboardKey::setPoints(const QVector<QPointF> &points)
+{
+ m_points = points;
+ computePainterPath();
+}
+
+const QVector<QPointF> &UISoftKeyboardKey::points() const
+{
+ return m_points;
+}
+
+const QPainterPath &UISoftKeyboardKey::painterPath() const
+{
+ return m_painterPath;
+}
+
+void UISoftKeyboardKey::computePainterPath()
+{
+ if (m_points.size() < 3)
+ return;
+
+ m_painterPath = QPainterPath(pointInBetween(m_fCornerRadius, m_points[0], m_points[1]));
+ for (int i = 0; i < m_points.size(); ++i)
+ {
+ QPointF p0 = pointInBetween(m_fCornerRadius, m_points[(i+1)%m_points.size()], m_points[i]);
+ QPointF p1 = pointInBetween(m_fCornerRadius, m_points[(i+1)%m_points.size()], m_points[(i+2)%m_points.size()]);
+ m_painterPath.lineTo(p0);
+ m_painterPath.quadTo(m_points[(i+1)%m_points.size()], p1);
+ }
+}
+
+void UISoftKeyboardKey::setCornerRadius(float fCornerRadius)
+{
+ m_fCornerRadius = fCornerRadius;
+}
+
+QPolygonF UISoftKeyboardKey::polygonInGlobal() const
+{
+ QPolygonF globalPolygon(m_points);
+ globalPolygon.translate(m_keyGeometry.x(), m_keyGeometry.y());
+ return globalPolygon;
+}
+
+int UISoftKeyboardKey::cutoutCorner() const
+{
+ return m_iCutoutCorner;
+}
+
+int UISoftKeyboardKey::cutoutWidth() const
+{
+ return m_iCutoutWidth;
+}
+
+int UISoftKeyboardKey::cutoutHeight() const
+{
+ return m_iCutoutHeight;
+}
+
+void UISoftKeyboardKey::updateState(bool fPressed)
+{
+ KeyState enmPreviousState = state();
+ if (m_enmType == KeyType_Modifier)
+ {
+ if (fPressed)
+ {
+ if (m_enmState == KeyState_NotPressed)
+ m_enmState = KeyState_Pressed;
+ else if(m_enmState == KeyState_Pressed)
+ m_enmState = KeyState_Locked;
+ else
+ m_enmState = KeyState_NotPressed;
+ }
+ else
+ {
+ if(m_enmState == KeyState_Pressed)
+ m_enmState = KeyState_NotPressed;
+ }
+ }
+ else if (m_enmType == KeyType_Lock)
+ {
+ m_enmState = fPressed ? KeyState_Locked : KeyState_NotPressed;
+ }
+ else if (m_enmType == KeyType_Ordinary)
+ {
+ if (m_enmState == KeyState_NotPressed)
+ m_enmState = KeyState_Pressed;
+ else
+ m_enmState = KeyState_NotPressed;
+ }
+ if (enmPreviousState != state() && m_pParentWidget)
+ m_pParentWidget->keyStateChange(this);
+}
+
+void UISoftKeyboardKey::updateLockState(bool fLocked)
+{
+ if (m_enmType != KeyType_Lock)
+ return;
+ if (fLocked && m_enmState == KeyState_Locked)
+ return;
+ if (!fLocked && m_enmState == KeyState_NotPressed)
+ return;
+ updateState(fLocked);
+}
+
+void UISoftKeyboardKey::reset()
+{
+ m_enmState = KeyState_NotPressed;
+}
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardLayout implementation. *
+*********************************************************************************************************************************/
+
+UISoftKeyboardLayout::UISoftKeyboardLayout()
+ : m_fEditable(true)
+ , m_fIsFromResources(false)
+ , m_fEditedButNotSaved(false)
+ , m_uid(QUuid::createUuid())
+{
+}
+
+QString UISoftKeyboardLayout::nameString() const
+{
+ QString strCombinedName;
+ if (nativeName().isEmpty() && !name().isEmpty())
+ strCombinedName = name();
+ else if (!nativeName().isEmpty() && name().isEmpty())
+ strCombinedName = nativeName();
+ else
+ strCombinedName = QString("%1 (%2)").arg(nativeName()).arg(name());
+ return strCombinedName;
+}
+
+void UISoftKeyboardLayout::setSourceFilePath(const QString& strSourceFilePath)
+{
+ m_strSourceFilePath = strSourceFilePath;
+ setEditedBuNotSaved(true);
+}
+
+const QString& UISoftKeyboardLayout::sourceFilePath() const
+{
+ return m_strSourceFilePath;
+}
+
+void UISoftKeyboardLayout::setIsFromResources(bool fIsFromResources)
+{
+ m_fIsFromResources = fIsFromResources;
+ setEditedBuNotSaved(true);
+}
+
+bool UISoftKeyboardLayout::isFromResources() const
+{
+ return m_fIsFromResources;
+}
+
+void UISoftKeyboardLayout::setName(const QString &strName)
+{
+ m_strName = strName;
+ setEditedBuNotSaved(true);
+}
+
+const QString &UISoftKeyboardLayout::name() const
+{
+ return m_strName;
+}
+
+void UISoftKeyboardLayout::setNativeName(const QString &strNativeName)
+{
+ m_strNativeName = strNativeName;
+ setEditedBuNotSaved(true);
+}
+
+const QString &UISoftKeyboardLayout::nativeName() const
+{
+ return m_strNativeName;
+}
+
+void UISoftKeyboardLayout::setEditable(bool fEditable)
+{
+ m_fEditable = fEditable;
+ setEditedBuNotSaved(true);
+}
+
+bool UISoftKeyboardLayout::editable() const
+{
+ return m_fEditable;
+}
+
+void UISoftKeyboardLayout::setPhysicalLayoutUuid(const QUuid &uuid)
+{
+ m_physicalLayoutUuid = uuid;
+ setEditedBuNotSaved(true);
+}
+
+const QUuid &UISoftKeyboardLayout::physicalLayoutUuid() const
+{
+ return m_physicalLayoutUuid;
+}
+
+void UISoftKeyboardLayout::addOrUpdateUIKeyCaptions(int iKeyPosition, const UIKeyCaptions &keyCaptions)
+{
+ if (m_keyCaptionsMap[iKeyPosition] == keyCaptions)
+ return;
+ m_keyCaptionsMap[iKeyPosition] = keyCaptions;
+ /* Updating the captions invalidates the cached font size. We set it to 0, thereby forcing its recomputaion: */
+ m_keyCaptionsFontSizeMap[iKeyPosition] = 0;
+ setEditedBuNotSaved(true);
+}
+
+UIKeyCaptions UISoftKeyboardLayout::keyCaptions(int iKeyPosition) const
+{
+ return m_keyCaptionsMap[iKeyPosition];
+}
+
+bool UISoftKeyboardLayout::operator==(const UISoftKeyboardLayout &otherLayout) const
+{
+ if (m_strName != otherLayout.m_strName)
+ return false;
+ if (m_strNativeName != otherLayout.m_strNativeName)
+ return false;
+ if (m_physicalLayoutUuid != otherLayout.m_physicalLayoutUuid)
+ return false;
+ if (m_fEditable != otherLayout.m_fEditable)
+ return false;
+ if (m_strSourceFilePath != otherLayout.m_strSourceFilePath)
+ return false;
+ if (m_fIsFromResources != otherLayout.m_fIsFromResources)
+ return false;
+ return true;
+}
+
+QString UISoftKeyboardLayout::baseCaption(int iKeyPosition) const
+{
+ return m_keyCaptionsMap.value(iKeyPosition, UIKeyCaptions()).m_strBase;
+}
+
+QString UISoftKeyboardLayout::shiftCaption(int iKeyPosition) const
+{
+ if (!m_keyCaptionsMap.contains(iKeyPosition))
+ return QString();
+ return m_keyCaptionsMap[iKeyPosition].m_strShift;
+}
+
+QString UISoftKeyboardLayout::altGrCaption(int iKeyPosition) const
+{
+ if (!m_keyCaptionsMap.contains(iKeyPosition))
+ return QString();
+ return m_keyCaptionsMap[iKeyPosition].m_strAltGr;
+}
+
+QString UISoftKeyboardLayout::shiftAltGrCaption(int iKeyPosition) const
+{
+ if (!m_keyCaptionsMap.contains(iKeyPosition))
+ return QString();
+ return m_keyCaptionsMap[iKeyPosition].m_strShiftAltGr;
+}
+
+void UISoftKeyboardLayout::setEditedBuNotSaved(bool fEditedButNotsaved)
+{
+ m_fEditedButNotSaved = fEditedButNotsaved;
+}
+
+bool UISoftKeyboardLayout::editedButNotSaved() const
+{
+ return m_fEditedButNotSaved;
+}
+
+void UISoftKeyboardLayout::setUid(const QUuid &uid)
+{
+ m_uid = uid;
+ setEditedBuNotSaved(true);
+}
+
+QUuid UISoftKeyboardLayout::uid() const
+{
+ return m_uid;
+}
+
+void UISoftKeyboardLayout::drawTextInRect(const UISoftKeyboardKey &key, QPainter &painter)
+{
+ int iKeyPosition = key.position();
+ const QRect &keyGeometry = key.keyGeometry();
+ QFont painterFont(painter.font());
+
+ QString strBaseCaption;
+ QString strShiftCaption;
+ QString strShiftAltGrCaption;
+ QString strAltGrCaption;
+
+ /* Static captions which are defined in the physical layout files have precedence over
+ the one define in the keyboard layouts. In effect they stay the same for all the
+ keyboard layouts sharing the same physical layout: */
+
+ if (key.staticCaption().isEmpty())
+ {
+ strBaseCaption = baseCaption(iKeyPosition);
+ strShiftCaption = shiftCaption(iKeyPosition);
+ strShiftAltGrCaption = shiftAltGrCaption(iKeyPosition);
+ strAltGrCaption = altGrCaption(iKeyPosition);
+ }
+ else
+ strBaseCaption = key.staticCaption();
+
+ const QString &strTopleftString = !strShiftCaption.isEmpty() ? strShiftCaption : strBaseCaption;
+ const QString &strBottomleftString = !strShiftCaption.isEmpty() ? strBaseCaption : QString();
+
+ int iFontSize = 30;
+ if (!m_keyCaptionsFontSizeMap.contains(iKeyPosition) || m_keyCaptionsFontSizeMap.value(iKeyPosition) == 0)
+ {
+ do
+ {
+ painterFont.setPixelSize(iFontSize);
+ painterFont.setBold(true);
+ painter.setFont(painterFont);
+ QFontMetrics fontMetrics = painter.fontMetrics();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iMargin = 0.25 * fontMetrics.horizontalAdvance('X');
+#else
+ int iMargin = 0.25 * fontMetrics.width('X');
+#endif
+
+ int iTopWidth = 0;
+ /* Some captions are multi line using \n as separator: */
+ QStringList strList;
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ strList << strTopleftString.split("\n", Qt::SkipEmptyParts)
+ << strShiftAltGrCaption.split("\n", Qt::SkipEmptyParts);
+#else
+ strList << strTopleftString.split("\n", QString::SkipEmptyParts)
+ << strShiftAltGrCaption.split("\n", QString::SkipEmptyParts);
+#endif
+ foreach (const QString &strPart, strList)
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ iTopWidth = qMax(iTopWidth, fontMetrics.horizontalAdvance(strPart));
+#else
+ iTopWidth = qMax(iTopWidth, fontMetrics.width(strPart));
+#endif
+ strList.clear();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ strList << strBottomleftString.split("\n", Qt::SkipEmptyParts)
+ << strAltGrCaption.split("\n", Qt::SkipEmptyParts);
+#else
+ strList << strBottomleftString.split("\n", QString::SkipEmptyParts)
+ << strAltGrCaption.split("\n", QString::SkipEmptyParts);
+#endif
+
+ int iBottomWidth = 0;
+ foreach (const QString &strPart, strList)
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ iBottomWidth = qMax(iBottomWidth, fontMetrics.horizontalAdvance(strPart));
+#else
+ iBottomWidth = qMax(iBottomWidth, fontMetrics.width(strPart));
+#endif
+ int iTextWidth = 2 * iMargin + qMax(iTopWidth, iBottomWidth);
+ int iTextHeight = 0;
+
+ if (key.keyboardRegion() == KeyboardRegion_MultimediaKeys)
+ iTextHeight = 2 * iMargin + fontMetrics.height();
+ else
+ iTextHeight = 2 * iMargin + 2 * fontMetrics.height();
+
+ if (iTextWidth >= keyGeometry.width() || iTextHeight >= keyGeometry.height())
+ --iFontSize;
+ else
+ break;
+
+ }while(iFontSize > 1);
+ m_keyCaptionsFontSizeMap[iKeyPosition] = iFontSize;
+ }
+ else
+ {
+ iFontSize = m_keyCaptionsFontSizeMap[iKeyPosition];
+ painterFont.setPixelSize(iFontSize);
+ painterFont.setBold(true);
+ painter.setFont(painterFont);
+ }
+
+ QFontMetrics fontMetrics = painter.fontMetrics();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iMargin = 0.25 * fontMetrics.horizontalAdvance('X');
+#else
+ int iMargin = 0.25 * fontMetrics.width('X');
+#endif
+ QRect textRect;
+ if (key.keyboardRegion() == KeyboardRegion_MultimediaKeys)
+ textRect = QRect(2 * iMargin, iMargin,
+ keyGeometry.width() - 2 * iMargin,
+ keyGeometry.height() - 2 * iMargin);
+ else
+ textRect = QRect(iMargin, iMargin,
+ keyGeometry.width() - 2 * iMargin,
+ keyGeometry.height() - 2 * iMargin);
+
+ if (key.keyboardRegion() == KeyboardRegion_MultimediaKeys)
+ {
+ painter.drawText(QRect(0, 0, keyGeometry.width(), keyGeometry.height()),
+ Qt::AlignHCenter | Qt::AlignVCenter, strTopleftString);
+ }
+ else
+ {
+ painter.drawText(textRect, Qt::AlignLeft | Qt::AlignTop, strTopleftString);
+ painter.drawText(textRect, Qt::AlignLeft | Qt::AlignBottom, strBottomleftString);
+ painter.drawText(textRect, Qt::AlignRight | Qt::AlignTop, strShiftAltGrCaption);
+ painter.drawText(textRect, Qt::AlignRight | Qt::AlignBottom, strAltGrCaption);
+ }
+}
+
+void UISoftKeyboardLayout::drawKeyImageInRect(const UISoftKeyboardKey &key, QPainter &painter)
+{
+ if (key.image().isNull())
+ return;
+ const QRect &keyGeometry = key.keyGeometry();
+ int iMargin = 0.1 * qMax(keyGeometry.width(), keyGeometry.height());
+ int size = qMin(keyGeometry.width() - 2 * iMargin, keyGeometry.height() - 2 * iMargin);
+ painter.drawImage(QRect(0.5 * (keyGeometry.width() - size), 0.5 * (keyGeometry.height() - size),
+ size, size), key.image());
+}
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardColorTheme implementation. *
+*********************************************************************************************************************************/
+
+UISoftKeyboardColorTheme::UISoftKeyboardColorTheme()
+ : m_colors(QVector<QColor>(KeyboardColorType_Max))
+ , m_fIsEditable(false)
+{
+ m_colors[KeyboardColorType_Background].setNamedColor("#ff878787");
+ m_colors[KeyboardColorType_Font].setNamedColor("#ff000000");
+ m_colors[KeyboardColorType_Hover].setNamedColor("#ff676767");
+ m_colors[KeyboardColorType_Edit].setNamedColor("#ff9b6767");
+ m_colors[KeyboardColorType_Pressed].setNamedColor("#fffafafa");
+}
+
+UISoftKeyboardColorTheme::UISoftKeyboardColorTheme(const QString &strName,
+ const QString &strBackgroundColor,
+ const QString &strNormalFontColor,
+ const QString &strHoverColor,
+ const QString &strEditedButtonBackgroundColor,
+ const QString &strPressedButtonFontColor)
+ :m_colors(QVector<QColor>(KeyboardColorType_Max))
+ ,m_strName(strName)
+ , m_fIsEditable(false)
+{
+ m_colors[KeyboardColorType_Background].setNamedColor(strBackgroundColor);
+ m_colors[KeyboardColorType_Font].setNamedColor(strNormalFontColor);
+ m_colors[KeyboardColorType_Hover].setNamedColor(strHoverColor);
+ m_colors[KeyboardColorType_Edit].setNamedColor(strEditedButtonBackgroundColor);
+ m_colors[KeyboardColorType_Pressed].setNamedColor(strPressedButtonFontColor);
+}
+
+
+void UISoftKeyboardColorTheme::setColor(KeyboardColorType enmColorType, const QColor &color)
+{
+ if ((int) enmColorType >= m_colors.size())
+ return;
+ m_colors[(int)enmColorType] = color;
+}
+
+QColor UISoftKeyboardColorTheme::color(KeyboardColorType enmColorType) const
+{
+ if ((int) enmColorType >= m_colors.size())
+ return QColor();
+ return m_colors[(int)enmColorType];
+}
+
+QStringList UISoftKeyboardColorTheme::colorsToStringList() const
+{
+ QStringList colorStringList;
+ foreach (const QColor &color, m_colors)
+ colorStringList << color.name(QColor::HexArgb);
+ return colorStringList;
+}
+
+void UISoftKeyboardColorTheme::colorsFromStringList(const QStringList &colorStringList)
+{
+ for (int i = 0; i < colorStringList.size() && i < m_colors.size(); ++i)
+ {
+ if (!QColor::isValidColor(colorStringList[i]))
+ continue;
+ m_colors[i].setNamedColor(colorStringList[i]);
+ }
+}
+
+const QString &UISoftKeyboardColorTheme::name() const
+{
+ return m_strName;
+}
+
+void UISoftKeyboardColorTheme::setName(const QString &strName)
+{
+ m_strName = strName;
+}
+
+bool UISoftKeyboardColorTheme::isEditable() const
+{
+ return m_fIsEditable;
+}
+
+void UISoftKeyboardColorTheme::setIsEditable(bool fIsEditable)
+{
+ m_fIsEditable = fIsEditable;
+}
+
+/*********************************************************************************************************************************
+* UISoftKeyboardWidget implementation. *
+*********************************************************************************************************************************/
+
+UISoftKeyboardWidget::UISoftKeyboardWidget(QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_pKeyUnderMouse(0)
+ , m_pKeyBeingEdited(0)
+ , m_pKeyPressed(0)
+ , m_currentColorTheme(0)
+ , m_iInitialHeight(0)
+ , m_iInitialWidth(0)
+ , m_iInitialWidthNoNumPad(0)
+ , m_iBeforeNumPadWidth(30)
+ , m_iXSpacing(5)
+ , m_iYSpacing(5)
+ , m_iLeftMargin(10)
+ , m_iTopMargin(10)
+ , m_iRightMargin(10)
+ , m_iBottomMargin(10)
+ , m_enmMode(Mode_Keyboard)
+ , m_fHideOSMenuKeys(false)
+ , m_fHideNumPad(false)
+ , m_fHideMultimediaKeys(false)
+{
+ prepareObjects();
+ prepareColorThemes();
+ retranslateUi();
+}
+
+QSize UISoftKeyboardWidget::minimumSizeHint() const
+{
+ float fScale = 0.5f;
+ return QSize(fScale * m_minimumSize.width(), fScale * m_minimumSize.height());
+}
+
+QSize UISoftKeyboardWidget::sizeHint() const
+{
+ float fScale = 0.5f;
+ return QSize(fScale * m_minimumSize.width(), fScale * m_minimumSize.height());
+}
+
+void UISoftKeyboardWidget::paintEvent(QPaintEvent *pEvent) /* override */
+{
+ Q_UNUSED(pEvent);
+ if (!m_layouts.contains(m_uCurrentLayoutId))
+ return;
+
+ UISoftKeyboardLayout &currentLayout = m_layouts[m_uCurrentLayoutId];
+
+ if (m_iInitialWidth == 0 || m_iInitialWidthNoNumPad == 0 || m_iInitialHeight == 0)
+ return;
+
+ if (!m_fHideNumPad)
+ m_fScaleFactorX = width() / (float) (m_iInitialWidth + m_iBeforeNumPadWidth);
+ else
+ m_fScaleFactorX = width() / (float) m_iInitialWidthNoNumPad;
+
+ if (!m_fHideMultimediaKeys)
+ m_fScaleFactorY = height() / (float) m_iInitialHeight;
+ else
+ m_fScaleFactorY = height() / (float)(m_iInitialHeight - m_multiMediaKeysLayout.totalHeight());
+
+ QPainter painter(this);
+ QFont painterFont(font());
+ painterFont.setPixelSize(15);
+ painterFont.setBold(true);
+ painter.setFont(painterFont);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setRenderHint(QPainter::SmoothPixmapTransform);
+ painter.scale(m_fScaleFactorX, m_fScaleFactorY);
+ int unitSize = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
+ float fLedRadius = 0.8 * unitSize;
+ float fLedMargin = 5;//0.6 * unitSize;
+
+ UISoftKeyboardPhysicalLayout *pPhysicalLayout = findPhysicalLayout(currentLayout.physicalLayoutUuid());
+ if (!pPhysicalLayout)
+ return;
+
+ QVector<UISoftKeyboardRow> &rows = pPhysicalLayout->rows();
+ for (int i = 0; i < rows.size(); ++i)
+ {
+ QVector<UISoftKeyboardKey> &keys = rows[i].keys();
+ for (int j = 0; j < keys.size(); ++j)
+ {
+ UISoftKeyboardKey &key = keys[j];
+
+ if (m_fHideOSMenuKeys && key.isOSMenuKey())
+ continue;
+
+ if (m_fHideNumPad && key.keyboardRegion() == KeyboardRegion_NumPad)
+ continue;
+
+ if (m_fHideMultimediaKeys && key.keyboardRegion() == KeyboardRegion_MultimediaKeys)
+ continue;
+
+ if (m_fHideMultimediaKeys)
+ painter.translate(key.keyGeometry().x(), key.keyGeometry().y() - m_multiMediaKeysLayout.totalHeight());
+ else
+ painter.translate(key.keyGeometry().x(), key.keyGeometry().y());
+
+ if(&key == m_pKeyBeingEdited)
+ painter.setBrush(QBrush(color(KeyboardColorType_Edit)));
+ else if (&key == m_pKeyUnderMouse)
+ painter.setBrush(QBrush(color(KeyboardColorType_Hover)));
+ else
+ painter.setBrush(QBrush(color(KeyboardColorType_Background)));
+
+ if (&key == m_pKeyPressed)
+ painter.setPen(QPen(color(KeyboardColorType_Pressed), 2));
+ else
+ painter.setPen(QPen(color(KeyboardColorType_Font), 2));
+
+ /* Draw the key shape: */
+ painter.drawPath(key.painterPath());
+
+ if (key.keyboardRegion() == KeyboardRegion_MultimediaKeys)
+ currentLayout.drawKeyImageInRect(key, painter);
+ else
+ currentLayout.drawTextInRect(key, painter);
+ /* Draw small LED like circles on the modifier/lock keys: */
+ if (key.type() != KeyType_Ordinary)
+ {
+ QColor ledColor;
+ if (key.type() == KeyType_Lock)
+ {
+ if (key.state() == KeyState_NotPressed)
+ ledColor = color(KeyboardColorType_Font);
+ else
+ ledColor = QColor(0, 255, 0);
+ }
+ else
+ {
+ if (key.state() == KeyState_NotPressed)
+ ledColor = color(KeyboardColorType_Font);
+ else if (key.state() == KeyState_Pressed)
+ ledColor = QColor(0, 191, 204);
+ else
+ ledColor = QColor(255, 50, 50);
+ }
+ if (m_enmMode == Mode_LayoutEdit)
+ ledColor = color(KeyboardColorType_Font);
+ painter.setBrush(ledColor);
+ painter.setPen(ledColor);
+ QRectF rectangle(key.keyGeometry().width() - 2 * fLedMargin, key.keyGeometry().height() - 2 * fLedMargin,
+ fLedRadius, fLedRadius);
+ painter.drawEllipse(rectangle);
+ }
+ if (m_fHideMultimediaKeys)
+ painter.translate(-key.keyGeometry().x(), -key.keyGeometry().y() + m_multiMediaKeysLayout.totalHeight());
+ else
+ painter.translate(-key.keyGeometry().x(), -key.keyGeometry().y());
+ }
+ }
+}
+
+void UISoftKeyboardWidget::mousePressEvent(QMouseEvent *pEvent)
+{
+ QWidget::mousePressEvent(pEvent);
+ if (pEvent->button() != Qt::RightButton && pEvent->button() != Qt::LeftButton)
+ return;
+
+ m_pKeyPressed = keyUnderMouse(pEvent);
+ if (!m_pKeyPressed)
+ return;
+
+ /* Handling the right button press: */
+ if (pEvent->button() == Qt::RightButton)
+ modifierKeyPressRelease(m_pKeyPressed, false);
+ else
+ {
+ /* Handling the left button press: */
+ if (m_enmMode == Mode_Keyboard)
+ handleKeyPress(m_pKeyPressed);
+ else if (m_enmMode == Mode_LayoutEdit)
+ setKeyBeingEdited(m_pKeyUnderMouse);
+ }
+ update();
+}
+
+void UISoftKeyboardWidget::mouseReleaseEvent(QMouseEvent *pEvent)
+{
+ QWidget::mouseReleaseEvent(pEvent);
+
+ if (pEvent->button() != Qt::RightButton && pEvent->button() != Qt::LeftButton)
+ return;
+
+ if (!m_pKeyPressed)
+ return;
+ if (pEvent->button() == Qt::RightButton)
+ modifierKeyPressRelease(m_pKeyPressed, true);
+ else
+ {
+ if (m_enmMode == Mode_Keyboard)
+ handleKeyRelease(m_pKeyPressed);
+ }
+ m_pKeyPressed = 0;
+ update();
+}
+
+void UISoftKeyboardWidget::mouseMoveEvent(QMouseEvent *pEvent)
+{
+ QWidget::mouseMoveEvent(pEvent);
+ UISoftKeyboardKey *pPreviousKeyUnderMouse = m_pKeyUnderMouse;
+ keyUnderMouse(pEvent);
+ if (pPreviousKeyUnderMouse != m_pKeyUnderMouse)
+ showKeyTooltip(m_pKeyUnderMouse);
+}
+
+void UISoftKeyboardWidget::retranslateUi()
+{
+ m_keyTooltips[317] = UISoftKeyboard::tr("Power off");
+ m_keyTooltips[300] = UISoftKeyboard::tr("Web browser go back");
+ m_keyTooltips[301] = UISoftKeyboard::tr("Web browser go the home page");
+ m_keyTooltips[302] = UISoftKeyboard::tr("Web browser go forward");
+ m_keyTooltips[315] = UISoftKeyboard::tr("Web browser reload the current page");
+ m_keyTooltips[314] = UISoftKeyboard::tr("Web browser stop loading the page");
+ m_keyTooltips[313] = UISoftKeyboard::tr("Web browser search");
+
+ m_keyTooltips[307] = UISoftKeyboard::tr("Jump back to previous media track");
+ m_keyTooltips[308] = UISoftKeyboard::tr("Jump to next media track");
+ m_keyTooltips[309] = UISoftKeyboard::tr("Stop playing");
+ m_keyTooltips[310] = UISoftKeyboard::tr("Play or pause playing");
+
+ m_keyTooltips[303] = UISoftKeyboard::tr("Start email application");
+ m_keyTooltips[311] = UISoftKeyboard::tr("Start calculator");
+ m_keyTooltips[312] = UISoftKeyboard::tr("Show 'My Computer'");
+ m_keyTooltips[316] = UISoftKeyboard::tr("Show Media folder");
+
+ m_keyTooltips[304] = UISoftKeyboard::tr("Mute");
+ m_keyTooltips[305] = UISoftKeyboard::tr("Volume down");
+ m_keyTooltips[306] = UISoftKeyboard::tr("Volume up");
+}
+
+void UISoftKeyboardWidget::saveCurentLayoutToFile()
+{
+ if (!m_layouts.contains(m_uCurrentLayoutId))
+ return;
+ UISoftKeyboardLayout &currentLayout = m_layouts[m_uCurrentLayoutId];
+ QString strHomeFolder = uiCommon().homeFolder();
+ QDir dir(strHomeFolder);
+ if (!dir.exists(strSubDirectorName))
+ {
+ if (!dir.mkdir(strSubDirectorName))
+ {
+ sigStatusBarMessage(QString("%1 %2").arg(UISoftKeyboard::tr("Error! Could not create folder under").arg(strHomeFolder)));
+ return;
+ }
+ }
+
+ strHomeFolder += QString(QDir::separator()) + strSubDirectorName;
+ QInputDialog dialog(this);
+ dialog.setInputMode(QInputDialog::TextInput);
+ dialog.setWindowModality(Qt::WindowModal);
+ dialog.setWindowTitle(UISoftKeyboard::tr("Provide a file name"));
+ dialog.setTextValue(currentLayout.name());
+ dialog.setLabelText(QString("%1 %2").arg(UISoftKeyboard::tr("The file will be saved under:<br>")).arg(strHomeFolder));
+ if (dialog.exec() == QDialog::Rejected)
+ return;
+ QString strFileName(dialog.textValue());
+ if (strFileName.isEmpty() || strFileName.contains("..") || strFileName.contains(QDir::separator()))
+ {
+ sigStatusBarMessage(QString("%1 %2").arg(strFileName).arg(UISoftKeyboard::tr(" is an invalid file name")));
+ return;
+ }
+
+ UISoftKeyboardPhysicalLayout *pPhysicalLayout = findPhysicalLayout(currentLayout.physicalLayoutUuid());
+ if (!pPhysicalLayout)
+ {
+ sigStatusBarMessage("The layout file could not be saved");
+ return;
+ }
+
+ QFileInfo fileInfo(strFileName);
+ if (fileInfo.suffix().compare("xml", Qt::CaseInsensitive) != 0)
+ strFileName += ".xml";
+ strFileName = strHomeFolder + QString(QDir::separator()) + strFileName;
+ QFile xmlFile(strFileName);
+ if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
+ {
+ sigStatusBarMessage("The layout file could not be saved");
+ return;
+ }
+
+ QXmlStreamWriter xmlWriter;
+ xmlWriter.setDevice(&xmlFile);
+
+ xmlWriter.setAutoFormatting(true);
+ xmlWriter.writeStartDocument("1.0");
+ xmlWriter.writeStartElement("layout");
+ xmlWriter.writeTextElement("name", currentLayout.name());
+ xmlWriter.writeTextElement("nativename", currentLayout.nativeName());
+ xmlWriter.writeTextElement("physicallayoutid", pPhysicalLayout->uid().toString());
+ xmlWriter.writeTextElement("id", currentLayout.uid().toString());
+
+ QVector<UISoftKeyboardRow> &rows = pPhysicalLayout->rows();
+ for (int i = 0; i < rows.size(); ++i)
+ {
+ QVector<UISoftKeyboardKey> &keys = rows[i].keys();
+
+ for (int j = 0; j < keys.size(); ++j)
+ {
+ xmlWriter.writeStartElement("key");
+
+ UISoftKeyboardKey &key = keys[j];
+ xmlWriter.writeTextElement("position", QString::number(key.position()));
+ xmlWriter.writeTextElement("basecaption", currentLayout.baseCaption(key.position()));
+ xmlWriter.writeTextElement("shiftcaption", currentLayout.shiftCaption(key.position()));
+ xmlWriter.writeTextElement("altgrcaption", currentLayout.altGrCaption(key.position()));
+ xmlWriter.writeTextElement("shiftaltgrcaption", currentLayout.shiftAltGrCaption(key.position()));
+ xmlWriter.writeEndElement();
+ }
+ }
+ xmlWriter.writeEndElement();
+ xmlWriter.writeEndDocument();
+
+ xmlFile.close();
+ currentLayout.setSourceFilePath(strFileName);
+ currentLayout.setEditedBuNotSaved(false);
+ sigStatusBarMessage(QString("%1 %2").arg(strFileName).arg(UISoftKeyboard::tr(" is saved")));
+}
+
+void UISoftKeyboardWidget::copyCurentLayout()
+{
+ UISoftKeyboardLayout newLayout(m_layouts[m_uCurrentLayoutId]);
+
+ QString strNewName = QString("%1-%2").arg(newLayout.name()).arg(UISoftKeyboard::tr("Copy"));
+ int iCount = 1;
+ while (layoutByNameExists(strNewName))
+ {
+ strNewName = QString("%1-%2-%3").arg(newLayout.name()).arg(UISoftKeyboard::tr("Copy")).arg(QString::number(iCount));
+ ++iCount;
+ }
+
+ newLayout.setName(strNewName);
+ newLayout.setEditedBuNotSaved(true);
+ newLayout.setEditable(true);
+ newLayout.setIsFromResources(false);
+ newLayout.setSourceFilePath(QString());
+ newLayout.setUid(QUuid::createUuid());
+ addLayout(newLayout);
+}
+
+float UISoftKeyboardWidget::layoutAspectRatio()
+{
+ if (m_iInitialWidth == 0)
+ return 1.f;
+ return m_iInitialHeight / (float) m_iInitialWidth;
+}
+
+bool UISoftKeyboardWidget::hideOSMenuKeys() const
+{
+ return m_fHideOSMenuKeys;
+}
+
+void UISoftKeyboardWidget::setHideOSMenuKeys(bool fHide)
+{
+ if (m_fHideOSMenuKeys == fHide)
+ return;
+ m_fHideOSMenuKeys = fHide;
+ update();
+ emit sigOptionsChanged();
+}
+
+bool UISoftKeyboardWidget::hideNumPad() const
+{
+ return m_fHideNumPad;
+}
+
+void UISoftKeyboardWidget::setHideNumPad(bool fHide)
+{
+ if (m_fHideNumPad == fHide)
+ return;
+ m_fHideNumPad = fHide;
+ update();
+ emit sigOptionsChanged();
+}
+
+bool UISoftKeyboardWidget::hideMultimediaKeys() const
+{
+ return m_fHideMultimediaKeys;
+}
+
+void UISoftKeyboardWidget::setHideMultimediaKeys(bool fHide)
+{
+ if (m_fHideMultimediaKeys == fHide)
+ return;
+ m_fHideMultimediaKeys = fHide;
+ update();
+ emit sigOptionsChanged();
+}
+
+QColor UISoftKeyboardWidget::color(KeyboardColorType enmColorType) const
+{
+ if (!m_currentColorTheme)
+ return QColor();
+ return m_currentColorTheme->color(enmColorType);
+}
+
+void UISoftKeyboardWidget::setColor(KeyboardColorType enmColorType, const QColor &color)
+{
+ if (m_currentColorTheme)
+ m_currentColorTheme->setColor(enmColorType, color);
+ update();
+}
+
+QStringList UISoftKeyboardWidget::colorsToStringList(const QString &strColorThemeName)
+{
+ UISoftKeyboardColorTheme *pTheme = colorTheme(strColorThemeName);
+ if (!pTheme)
+ return QStringList();
+ return pTheme->colorsToStringList();
+}
+
+void UISoftKeyboardWidget::colorsFromStringList(const QString &strColorThemeName, const QStringList &colorStringList)
+{
+ UISoftKeyboardColorTheme *pTheme = colorTheme(strColorThemeName);
+ if (!pTheme)
+ return;
+ pTheme->colorsFromStringList(colorStringList);
+}
+
+void UISoftKeyboardWidget::updateLockKeyStates(bool fCapsLockState, bool fNumLockState, bool fScrollLockState)
+{
+ for (int i = 0; i < m_physicalLayouts.size(); ++i)
+ m_physicalLayouts[i].updateLockKeyStates(fCapsLockState, fNumLockState, fScrollLockState);
+ update();
+}
+
+QStringList UISoftKeyboardWidget::colorThemeNames() const
+{
+ QStringList nameList;
+ foreach (const UISoftKeyboardColorTheme &theme, m_colorThemes)
+ {
+ nameList << theme.name();
+ }
+ return nameList;
+}
+
+QString UISoftKeyboardWidget::currentColorThemeName() const
+{
+ if (!m_currentColorTheme)
+ return QString();
+ return m_currentColorTheme->name();
+}
+
+void UISoftKeyboardWidget::setColorThemeByName(const QString &strColorThemeName)
+{
+ if (strColorThemeName.isEmpty())
+ return;
+ if (m_currentColorTheme && m_currentColorTheme->name() == strColorThemeName)
+ return;
+ for (int i = 0; i < m_colorThemes.size(); ++i)
+ {
+ if (m_colorThemes[i].name() == strColorThemeName)
+ {
+ m_currentColorTheme = &(m_colorThemes[i]);
+ break;
+ }
+ }
+ update();
+ emit sigCurrentColorThemeChanged();
+}
+
+void UISoftKeyboardWidget::parentDialogDeactivated()
+{
+ if (!underMouse())
+ m_pKeyUnderMouse = 0;
+ update();
+}
+
+bool UISoftKeyboardWidget::isColorThemeEditable() const
+{
+ if (!m_currentColorTheme)
+ return false;
+ return m_currentColorTheme->isEditable();
+}
+
+QStringList UISoftKeyboardWidget::unsavedLayoutsNameList() const
+{
+ QStringList nameList;
+ foreach (const UISoftKeyboardLayout &layout, m_layouts)
+ {
+ if (layout.editedButNotSaved())
+ nameList << layout.nameString();
+ }
+ return nameList;
+}
+
+void UISoftKeyboardWidget::deleteCurrentLayout()
+{
+ if (!m_layouts.contains(m_uCurrentLayoutId))
+ return;
+
+ /* Make sure we will have at least one layout remaining. */
+ if (m_layouts.size() <= 1)
+ return;
+
+ const UISoftKeyboardLayout &layout = m_layouts.value(m_uCurrentLayoutId);
+ if (!layout.editable() || layout.isFromResources())
+ return;
+
+ QDir fileToDelete;
+ QString strFilePath(layout.sourceFilePath());
+
+ bool fFileExists = false;
+ if (!strFilePath.isEmpty())
+ fFileExists = fileToDelete.exists(strFilePath);
+ /* It might be that the layout copied but not yet saved into a file: */
+ if (fFileExists)
+ {
+ if (!msgCenter().questionBinary(this, MessageType_Question,
+ QString(UISoftKeyboard::tr("This will delete the keyboard layout file as well. Proceed?")),
+ 0 /* auto-confirm id */,
+ QString("Delete") /* ok button text */,
+ QString() /* cancel button text */,
+ false /* ok button by default? */))
+ return;
+
+ if (fileToDelete.remove(strFilePath))
+ sigStatusBarMessage(UISoftKeyboard::tr("The file %1 has been deleted").arg(strFilePath));
+ else
+ sigStatusBarMessage(UISoftKeyboard::tr("Deleting the file %1 has failed").arg(strFilePath));
+ }
+
+ m_layouts.remove(m_uCurrentLayoutId);
+ setCurrentLayout(m_layouts.firstKey());
+}
+
+void UISoftKeyboardWidget::toggleEditMode(bool fIsEditMode)
+{
+ if (fIsEditMode)
+ m_enmMode = Mode_LayoutEdit;
+ else
+ {
+ m_enmMode = Mode_Keyboard;
+ m_pKeyBeingEdited = 0;
+ }
+ update();
+}
+
+void UISoftKeyboardWidget::addLayout(const UISoftKeyboardLayout &newLayout)
+{
+ if (m_layouts.contains(newLayout.uid()))
+ return;
+ m_layouts[newLayout.uid()] = newLayout;
+}
+
+void UISoftKeyboardWidget::setNewMinimumSize(const QSize &size)
+{
+ m_minimumSize = size;
+ updateGeometry();
+}
+
+void UISoftKeyboardWidget::setInitialSize(int iWidth, int iHeight)
+{
+ m_iInitialWidth = iWidth;
+ m_iInitialHeight = iHeight;
+}
+
+UISoftKeyboardKey *UISoftKeyboardWidget::keyUnderMouse(QMouseEvent *pEvent)
+{
+ QPoint eventPosition(pEvent->pos().x() / m_fScaleFactorX, pEvent->pos().y() / m_fScaleFactorY);
+ if (m_fHideMultimediaKeys)
+ eventPosition.setY(eventPosition.y() + m_multiMediaKeysLayout.totalHeight());
+ return keyUnderMouse(eventPosition);
+}
+
+UISoftKeyboardKey *UISoftKeyboardWidget::keyUnderMouse(const QPoint &eventPosition)
+{
+ const UISoftKeyboardLayout &currentLayout = m_layouts.value(m_uCurrentLayoutId);
+
+ UISoftKeyboardPhysicalLayout *pPhysicalLayout = findPhysicalLayout(currentLayout.physicalLayoutUuid());
+ if (!pPhysicalLayout)
+ return 0;
+
+ UISoftKeyboardKey *pKey = 0;
+ QVector<UISoftKeyboardRow> &rows = pPhysicalLayout->rows();
+ for (int i = 0; i < rows.size(); ++i)
+ {
+ QVector<UISoftKeyboardKey> &keys = rows[i].keys();
+ for (int j = 0; j < keys.size(); ++j)
+ {
+ UISoftKeyboardKey &key = keys[j];
+ if (key.polygonInGlobal().containsPoint(eventPosition, Qt::OddEvenFill))
+ {
+ pKey = &key;
+ break;
+ }
+ }
+ }
+ if (m_pKeyUnderMouse != pKey)
+ {
+ m_pKeyUnderMouse = pKey;
+ update();
+ }
+ return pKey;
+}
+
+void UISoftKeyboardWidget::handleKeyRelease(UISoftKeyboardKey *pKey)
+{
+ if (!pKey)
+ return;
+ if (pKey->type() == KeyType_Ordinary)
+ pKey->release();
+ /* We only send the scan codes of Ordinary keys: */
+ if (pKey->type() == KeyType_Modifier)
+ return;
+
+#if 0
+
+ QVector<LONG> sequence;
+ if (!pKey->scanCodePrefix().isEmpty())
+ sequence << pKey->scanCodePrefix();
+ sequence << (pKey->scanCode() | 0x80);
+
+ /* Add the pressed modifiers in the reverse order: */
+ for (int i = m_pressedModifiers.size() - 1; i >= 0; --i)
+ {
+ UISoftKeyboardKey *pModifier = m_pressedModifiers[i];
+ if (!pModifier->scanCodePrefix().isEmpty())
+ sequence << pModifier->scanCodePrefix();
+ sequence << (pModifier->scanCode() | 0x80);
+ /* Release the pressed modifiers (if there are not locked): */
+ pModifier->release();
+ }
+ emit sigPutKeyboardSequence(sequence);
+
+#else
+
+ QVector<QPair<LONG, LONG> > sequence;
+ sequence << QPair<LONG, LONG>(pKey->usagePageIdPair());
+ /* Add the pressed modifiers in the reverse order: */
+ for (int i = m_pressedModifiers.size() - 1; i >= 0; --i)
+ {
+ UISoftKeyboardKey *pModifier = m_pressedModifiers[i];
+ sequence << pModifier->usagePageIdPair();
+ /* Release the pressed modifiers (if there are not locked): */
+ pModifier->release();
+ }
+ emit sigPutUsageCodesRelease(sequence);
+
+#endif
+}
+
+void UISoftKeyboardWidget::handleKeyPress(UISoftKeyboardKey *pKey)
+{
+ if (!pKey)
+ return;
+ pKey->press();
+
+ if (pKey->type() == KeyType_Modifier)
+ return;
+
+#if 0
+ QVector<LONG> sequence;
+ /* Add the pressed modifiers first: */
+ for (int i = 0; i < m_pressedModifiers.size(); ++i)
+ {
+ UISoftKeyboardKey *pModifier = m_pressedModifiers[i];
+ if (!pModifier->scanCodePrefix().isEmpty())
+ sequence << pModifier->scanCodePrefix();
+ sequence << pModifier->scanCode();
+ }
+
+ if (!pKey->scanCodePrefix().isEmpty())
+ sequence << pKey->scanCodePrefix();
+ sequence << pKey->scanCode();
+ emit sigPutKeyboardSequence(sequence);
+
+#else
+
+ QVector<QPair<LONG, LONG> > sequence;
+
+ /* Add the pressed modifiers first: */
+ for (int i = 0; i < m_pressedModifiers.size(); ++i)
+ {
+ UISoftKeyboardKey *pModifier = m_pressedModifiers[i];
+ sequence << pModifier->usagePageIdPair();
+ }
+
+ sequence << pKey->usagePageIdPair();
+ emit sigPutUsageCodesPress(sequence);
+
+#endif
+}
+
+void UISoftKeyboardWidget::modifierKeyPressRelease(UISoftKeyboardKey *pKey, bool fRelease)
+{
+ if (!pKey || pKey->type() != KeyType_Modifier)
+ return;
+
+ pKey->setState(KeyState_NotPressed);
+
+ QVector<QPair<LONG, LONG> > sequence;
+ sequence << pKey->usagePageIdPair();
+ if (fRelease)
+ emit sigPutUsageCodesRelease(sequence);
+ else
+ emit sigPutUsageCodesPress(sequence);
+}
+
+void UISoftKeyboardWidget::keyStateChange(UISoftKeyboardKey* pKey)
+{
+ if (!pKey)
+ return;
+ if (pKey->type() == KeyType_Modifier)
+ {
+ if (pKey->state() == KeyState_NotPressed)
+ m_pressedModifiers.removeOne(pKey);
+ else
+ if (!m_pressedModifiers.contains(pKey))
+ m_pressedModifiers.append(pKey);
+ }
+}
+
+void UISoftKeyboardWidget::setCurrentLayout(const QUuid &layoutUid)
+{
+ if (m_uCurrentLayoutId == layoutUid || !m_layouts.contains(layoutUid))
+ return;
+
+ UISoftKeyboardPhysicalLayout *pPhysicalLayout = findPhysicalLayout(m_layouts[layoutUid].physicalLayoutUuid());
+ if (!pPhysicalLayout)
+ return;
+
+ m_uCurrentLayoutId = layoutUid;
+ emit sigCurrentLayoutChange();
+ update();
+}
+
+UISoftKeyboardLayout *UISoftKeyboardWidget::currentLayout()
+{
+ if (!m_layouts.contains(m_uCurrentLayoutId))
+ return 0;
+ return &(m_layouts[m_uCurrentLayoutId]);
+}
+
+bool UISoftKeyboardWidget::loadPhysicalLayout(const QString &strLayoutFileName, KeyboardRegion keyboardRegion /* = KeyboardRegion_Main */)
+{
+ if (strLayoutFileName.isEmpty())
+ return false;
+ UIPhysicalLayoutReader reader;
+ UISoftKeyboardPhysicalLayout *newPhysicalLayout = 0;
+ if (keyboardRegion == KeyboardRegion_Main)
+ {
+ m_physicalLayouts.append(UISoftKeyboardPhysicalLayout());
+ newPhysicalLayout = &(m_physicalLayouts.back());
+ }
+ else if (keyboardRegion == KeyboardRegion_NumPad)
+ newPhysicalLayout = &(m_numPadLayout);
+ else if (keyboardRegion == KeyboardRegion_MultimediaKeys)
+ newPhysicalLayout = &(m_multiMediaKeysLayout);
+ else
+ return false;
+
+ if (!reader.parseXMLFile(strLayoutFileName, *newPhysicalLayout))
+ {
+ m_physicalLayouts.removeLast();
+ return false;
+ }
+
+ for (int i = 0; i < newPhysicalLayout->rows().size(); ++i)
+ {
+ UISoftKeyboardRow &row = newPhysicalLayout->rows()[i];
+ for (int j = 0; j < row.keys().size(); ++j)
+ row.keys()[j].setKeyboardRegion(keyboardRegion);
+ }
+
+ if (keyboardRegion == KeyboardRegion_NumPad || keyboardRegion == KeyboardRegion_MultimediaKeys)
+ return true;
+
+ /* Go thru all the keys row by row and construct their geometries: */
+ int iY = m_iTopMargin;
+ int iMaxWidth = 0;
+ int iMaxWidthNoNumPad = 0;
+ const QVector<UISoftKeyboardRow> &numPadRows = m_numPadLayout.rows();
+ QVector<UISoftKeyboardRow> &rows = newPhysicalLayout->rows();
+
+ /* Prepend the multimedia rows to the layout */
+ const QVector<UISoftKeyboardRow> &multimediaRows = m_multiMediaKeysLayout.rows();
+ for (int i = multimediaRows.size() - 1; i >= 0; --i)
+ rows.prepend(multimediaRows[i]);
+
+ for (int i = 0; i < rows.size(); ++i)
+ {
+ UISoftKeyboardRow &row = rows[i];
+ /* Insert the numpad rows at the end of keyboard rows starting with appending 0th numpad row to the
+ end of (1 + multimediaRows.size())th layout row: */
+ if (i > multimediaRows.size())
+ {
+ int iNumPadRowIndex = i - (1 + multimediaRows.size());
+ if (iNumPadRowIndex >= 0 && iNumPadRowIndex < numPadRows.size())
+ {
+ for (int m = 0; m < numPadRows[iNumPadRowIndex].keys().size(); ++m)
+ row.keys().append(numPadRows[iNumPadRowIndex].keys()[m]);
+ }
+ }
+
+ int iX = m_iLeftMargin + row.leftMargin();
+ int iXNoNumPad = m_iLeftMargin;
+ int iRowHeight = row.defaultHeight();
+ int iKeyWidth = 0;
+ for (int j = 0; j < row.keys().size(); ++j)
+ {
+ UISoftKeyboardKey &key = (row.keys())[j];
+ if (key.position() == iScrollLockPosition ||
+ key.position() == iNumLockPosition ||
+ key.position() == iCapsLockPosition)
+ newPhysicalLayout->setLockKey(key.position(), &key);
+
+ if (key.keyboardRegion() == KeyboardRegion_NumPad)
+ key.setKeyGeometry(QRect(iX + m_iBeforeNumPadWidth, iY, key.width(), key.height()));
+ else
+ key.setKeyGeometry(QRect(iX, iY, key.width(), key.height()));
+
+ key.setCornerRadius(0.1 * newPhysicalLayout->defaultKeyWidth());
+ key.setPoints(UIPhysicalLayoutReader::computeKeyVertices(key));
+ key.setParentWidget(this);
+
+ iKeyWidth = key.width();
+ if (j < row.keys().size() - 1)
+ iKeyWidth += m_iXSpacing;
+ if (key.spaceWidthAfter() != 0 && j != row.keys().size() - 1)
+ iKeyWidth += (m_iXSpacing + key.spaceWidthAfter());
+
+ iX += iKeyWidth;
+ if (key.keyboardRegion() != KeyboardRegion_NumPad)
+ iXNoNumPad += iKeyWidth;
+ }
+ if (row.spaceHeightAfter() != 0)
+ iY += row.spaceHeightAfter() + m_iYSpacing;
+ iMaxWidth = qMax(iMaxWidth, iX);
+ iMaxWidthNoNumPad = qMax(iMaxWidthNoNumPad, iXNoNumPad);
+
+ iY += iRowHeight;
+ if (i < rows.size() - 1)
+ iY += m_iYSpacing;
+ }
+ int iInitialWidth = iMaxWidth + m_iRightMargin;
+ int iInitialWidthNoNumPad = iMaxWidthNoNumPad + m_iRightMargin;
+ int iInitialHeight = iY + m_iBottomMargin;
+ m_iInitialWidth = qMax(m_iInitialWidth, iInitialWidth);
+ m_iInitialWidthNoNumPad = qMax(m_iInitialWidthNoNumPad, iInitialWidthNoNumPad);
+ m_iInitialHeight = qMax(m_iInitialHeight, iInitialHeight);
+ return true;
+}
+
+bool UISoftKeyboardWidget::loadKeyboardLayout(const QString &strLayoutFileName)
+{
+ if (strLayoutFileName.isEmpty())
+ return false;
+
+ UIKeyboardLayoutReader keyboardLayoutReader;
+
+ UISoftKeyboardLayout newLayout;
+ if (!keyboardLayoutReader.parseFile(strLayoutFileName, newLayout))
+ return false;
+
+ UISoftKeyboardPhysicalLayout *pPhysicalLayout = findPhysicalLayout(newLayout.physicalLayoutUuid());
+ /* If no pyhsical layout with the UUID the keyboard layout refers is found then cancel loading the keyboard layout: */
+ if (!pPhysicalLayout)
+ return false;
+
+ /* Make sure we have unique lay1out UUIDs: */
+ int iCount = 0;
+ foreach (const UISoftKeyboardLayout &layout, m_layouts)
+ {
+ if (layout.uid() == newLayout.uid())
+ ++iCount;
+ }
+ if (iCount > 1)
+ return false;
+
+ newLayout.setSourceFilePath(strLayoutFileName);
+ addLayout(newLayout);
+ return true;
+}
+
+UISoftKeyboardPhysicalLayout *UISoftKeyboardWidget::findPhysicalLayout(const QUuid &uuid)
+{
+ for (int i = 0; i < m_physicalLayouts.size(); ++i)
+ {
+ if (m_physicalLayouts[i].uid() == uuid)
+ return &(m_physicalLayouts[i]);
+ }
+ return 0;
+}
+
+void UISoftKeyboardWidget::reset()
+{
+ m_pressedModifiers.clear();
+ m_pKeyUnderMouse = 0;
+ m_pKeyBeingEdited = 0;
+ m_pKeyPressed = 0;
+ m_enmMode = Mode_Keyboard;
+
+ for (int i = 0; i < m_physicalLayouts.size(); ++i)
+ m_physicalLayouts[i].reset();
+}
+
+void UISoftKeyboardWidget::loadLayouts()
+{
+ /* Load physical layouts from resources: Numpad and multimedia layout files should be read first
+ since we insert these to other layouts: */
+ loadPhysicalLayout(":/numpad.xml", KeyboardRegion_NumPad);
+ loadPhysicalLayout(":/multimedia_keys.xml", KeyboardRegion_MultimediaKeys);
+ QStringList physicalLayoutNames;
+ physicalLayoutNames << ":/101_ansi.xml"
+ << ":/102_iso.xml"
+ << ":/106_japanese.xml"
+ << ":/103_iso.xml"
+ << ":/103_ansi.xml";
+ foreach (const QString &strName, physicalLayoutNames)
+ loadPhysicalLayout(strName);
+
+ setNewMinimumSize(QSize(m_iInitialWidth, m_iInitialHeight));
+ setInitialSize(m_iInitialWidth, m_iInitialHeight);
+
+ /* Add keyboard layouts from resources: */
+ QStringList keyboardLayoutNames;
+ keyboardLayoutNames << ":/us_international.xml"
+ << ":/german.xml"
+ << ":/us.xml"
+ << ":/greek.xml"
+ << ":/japanese.xml"
+ << ":/brazilian.xml"
+ << ":/korean.xml";
+
+ foreach (const QString &strName, keyboardLayoutNames)
+ loadKeyboardLayout(strName);
+ /* Mark the layouts we load from the resources as non-editable: */
+ for (QMap<QUuid, UISoftKeyboardLayout>::iterator iterator = m_layouts.begin(); iterator != m_layouts.end(); ++iterator)
+ {
+ iterator.value().setEditable(false);
+ iterator.value().setIsFromResources(true);
+ }
+ keyboardLayoutNames.clear();
+ /* Add keyboard layouts from the defalt keyboard layout folder: */
+ lookAtDefaultLayoutFolder(keyboardLayoutNames);
+ foreach (const QString &strName, keyboardLayoutNames)
+ loadKeyboardLayout(strName);
+
+ if (m_layouts.isEmpty())
+ return;
+ for (QMap<QUuid, UISoftKeyboardLayout>::iterator iterator = m_layouts.begin(); iterator != m_layouts.end(); ++iterator)
+ iterator.value().setEditedBuNotSaved(false);
+ /* Block sigCurrentLayoutChange since it causes saving set layout to exra data: */
+ blockSignals(true);
+ setCurrentLayout(m_layouts.firstKey());
+ blockSignals(false);
+}
+
+void UISoftKeyboardWidget::prepareObjects()
+{
+ setMouseTracking(true);
+}
+
+void UISoftKeyboardWidget::prepareColorThemes()
+{
+ int iIndex = 0;
+ while (predefinedColorThemes[iIndex][0])
+ {
+ m_colorThemes << UISoftKeyboardColorTheme(predefinedColorThemes[iIndex][0],
+ predefinedColorThemes[iIndex][1],
+ predefinedColorThemes[iIndex][2],
+ predefinedColorThemes[iIndex][3],
+ predefinedColorThemes[iIndex][4],
+ predefinedColorThemes[iIndex][5]);
+ ++iIndex;
+ }
+
+ UISoftKeyboardColorTheme customTheme;
+ customTheme.setName("Custom");
+ customTheme.setIsEditable(true);
+ m_colorThemes.append(customTheme);
+ m_currentColorTheme = &(m_colorThemes.back());
+}
+
+void UISoftKeyboardWidget::setKeyBeingEdited(UISoftKeyboardKey* pKey)
+{
+ if (m_pKeyBeingEdited == pKey)
+ return;
+ m_pKeyBeingEdited = pKey;
+ emit sigKeyToEdit(pKey);
+}
+
+bool UISoftKeyboardWidget::layoutByNameExists(const QString &strName) const
+{
+ foreach (const UISoftKeyboardLayout &layout, m_layouts)
+ {
+ if (layout.name() == strName)
+ return true;
+ }
+ return false;
+}
+
+void UISoftKeyboardWidget::lookAtDefaultLayoutFolder(QStringList &fileList)
+{
+ QString strFolder = QString("%1%2%3").arg(uiCommon().homeFolder()).arg(QDir::separator()).arg(strSubDirectorName);
+ QDir dir(strFolder);
+ if (!dir.exists())
+ return;
+ QStringList filters;
+ filters << "*.xml";
+ dir.setNameFilters(filters);
+ QFileInfoList fileInfoList = dir.entryInfoList();
+ foreach (const QFileInfo &fileInfo, fileInfoList)
+ fileList << fileInfo.absoluteFilePath();
+}
+
+UISoftKeyboardColorTheme *UISoftKeyboardWidget::colorTheme(const QString &strColorThemeName)
+{
+ for (int i = 0; i < m_colorThemes.size(); ++i)
+ {
+ if (m_colorThemes[i].name() == strColorThemeName)
+ return &(m_colorThemes[i]);
+ }
+ return 0;
+}
+
+void UISoftKeyboardWidget::showKeyTooltip(UISoftKeyboardKey *pKey)
+{
+ if (pKey && m_keyTooltips.contains(pKey->position()))
+ sigStatusBarMessage(m_keyTooltips[pKey->position()]);
+ else
+ sigStatusBarMessage(QString());
+
+}
+
+QStringList UISoftKeyboardWidget::layoutNameList() const
+{
+ QStringList layoutNames;
+ foreach (const UISoftKeyboardLayout &layout, m_layouts)
+ layoutNames << layout.nameString();
+ return layoutNames;
+}
+
+QList<QUuid> UISoftKeyboardWidget::layoutUidList() const
+{
+ QList<QUuid> layoutUids;
+ foreach (const UISoftKeyboardLayout &layout, m_layouts)
+ layoutUids << layout.uid();
+ return layoutUids;
+}
+
+const QVector<UISoftKeyboardPhysicalLayout> &UISoftKeyboardWidget::physicalLayouts() const
+{
+ return m_physicalLayouts;
+}
+
+/*********************************************************************************************************************************
+* UIPhysicalLayoutReader implementation. *
+*********************************************************************************************************************************/
+
+bool UIPhysicalLayoutReader::parseXMLFile(const QString &strFileName, UISoftKeyboardPhysicalLayout &physicalLayout)
+{
+ QFile xmlFile(strFileName);
+ if (!xmlFile.exists())
+ return false;
+
+ if (xmlFile.size() >= iFileSizeLimit)
+ return false;
+
+ if (!xmlFile.open(QIODevice::ReadOnly))
+ return false;
+
+ m_xmlReader.setDevice(&xmlFile);
+
+ if (!m_xmlReader.readNextStartElement() || m_xmlReader.name() != QLatin1String("physicallayout"))
+ return false;
+ physicalLayout.setFileName(strFileName);
+
+ QXmlStreamAttributes attributes = m_xmlReader.attributes();
+ int iDefaultWidth = attributes.value("defaultWidth").toInt();
+ int iDefaultHeight = attributes.value("defaultHeight").toInt();
+ QVector<UISoftKeyboardRow> &rows = physicalLayout.rows();
+ physicalLayout.setDefaultKeyWidth(iDefaultWidth);
+
+ while (m_xmlReader.readNextStartElement())
+ {
+ if (m_xmlReader.name() == QLatin1String("row"))
+ parseRow(iDefaultWidth, iDefaultHeight, rows);
+ else if (m_xmlReader.name() == QLatin1String("name"))
+ physicalLayout.setName(m_xmlReader.readElementText());
+ else if (m_xmlReader.name() == QLatin1String("id"))
+ physicalLayout.setUid(QUuid(m_xmlReader.readElementText()));
+ else
+ m_xmlReader.skipCurrentElement();
+ }
+
+ return true;
+}
+
+void UIPhysicalLayoutReader::parseRow(int iDefaultWidth, int iDefaultHeight, QVector<UISoftKeyboardRow> &rows)
+{
+ rows.append(UISoftKeyboardRow());
+ UISoftKeyboardRow &row = rows.back();
+
+ row.setDefaultWidth(iDefaultWidth);
+ row.setDefaultHeight(iDefaultHeight);
+ row.setSpaceHeightAfter(0);
+
+ /* Override the layout attributes if the row also has them: */
+ QXmlStreamAttributes attributes = m_xmlReader.attributes();
+ if (attributes.hasAttribute("defaultWidth"))
+ row.setDefaultWidth(attributes.value("defaultWidth").toInt());
+ if (attributes.hasAttribute("defaultHeight"))
+ row.setDefaultHeight(attributes.value("defaultHeight").toInt());
+ while (m_xmlReader.readNextStartElement())
+ {
+ if (m_xmlReader.name() == QLatin1String("key"))
+ parseKey(row);
+ else if (m_xmlReader.name() == QLatin1String("space"))
+ parseKeySpace(row);
+ else
+ m_xmlReader.skipCurrentElement();
+ }
+}
+
+void UIPhysicalLayoutReader::parseKey(UISoftKeyboardRow &row)
+{
+ row.keys().append(UISoftKeyboardKey());
+ UISoftKeyboardKey &key = row.keys().back();
+ key.setWidth(row.defaultWidth());
+ key.setHeight(row.defaultHeight());
+ QString strKeyCap;
+ while (m_xmlReader.readNextStartElement())
+ {
+ if (m_xmlReader.name() == QLatin1String("width"))
+ key.setWidth(m_xmlReader.readElementText().toInt());
+ else if (m_xmlReader.name() == QLatin1String("height"))
+ key.setHeight(m_xmlReader.readElementText().toInt());
+ else if (m_xmlReader.name() == QLatin1String("scancode"))
+ {
+ QString strCode = m_xmlReader.readElementText();
+ bool fOk = false;
+ key.setScanCode(strCode.toInt(&fOk, 16));
+ }
+ else if (m_xmlReader.name() == QLatin1String("scancodeprefix"))
+ {
+ QString strCode = m_xmlReader.readElementText();
+ QStringList strList;
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ strList << strCode.split('-', Qt::SkipEmptyParts);
+#else
+ strList << strCode.split('-', QString::SkipEmptyParts);
+#endif
+ foreach (const QString &strPrefix, strList)
+ {
+ bool fOk = false;
+ LONG iCode = strPrefix.toInt(&fOk, 16);
+ if (fOk)
+ key.addScanCodePrefix(iCode);
+ }
+ }
+ else if (m_xmlReader.name() == QLatin1String("usageid"))
+ {
+ QString strCode = m_xmlReader.readElementText();
+ bool fOk = false;
+ key.setUsageId(strCode.toInt(&fOk, 16));
+ }
+ else if (m_xmlReader.name() == QLatin1String("usagepage"))
+ {
+ QString strCode = m_xmlReader.readElementText();
+ bool fOk = false;
+ key.setUsagePage(strCode.toInt(&fOk, 16));
+ }
+ else if (m_xmlReader.name() == QLatin1String("cutout"))
+ parseCutout(key);
+ else if (m_xmlReader.name() == QLatin1String("position"))
+ key.setPosition(m_xmlReader.readElementText().toInt());
+ else if (m_xmlReader.name() == QLatin1String("type"))
+ {
+ QString strType = m_xmlReader.readElementText();
+ if (strType == "modifier")
+ key.setType(KeyType_Modifier);
+ else if (strType == "lock")
+ key.setType(KeyType_Lock);
+ }
+ else if (m_xmlReader.name() == QLatin1String("osmenukey"))
+ {
+ if (m_xmlReader.readElementText() == "true")
+ key.setIsOSMenuKey(true);
+ }
+ else if (m_xmlReader.name() == QLatin1String("staticcaption"))
+ key.setStaticCaption(m_xmlReader.readElementText());
+ else if (m_xmlReader.name() == QLatin1String("image"))
+ key.setImageByName(m_xmlReader.readElementText());
+ else
+ m_xmlReader.skipCurrentElement();
+ }
+}
+
+void UIPhysicalLayoutReader::parseKeySpace(UISoftKeyboardRow &row)
+{
+ int iWidth = row.defaultWidth();
+ int iHeight = 0;
+ while (m_xmlReader.readNextStartElement())
+ {
+ if (m_xmlReader.name() == QLatin1String("width"))
+ iWidth = m_xmlReader.readElementText().toInt();
+ else if (m_xmlReader.name() == QLatin1String("height"))
+ iHeight = m_xmlReader.readElementText().toInt();
+ else
+ m_xmlReader.skipCurrentElement();
+ }
+ row.setSpaceHeightAfter(iHeight);
+ /* If we have keys add the parsed space to the last key as the 'space after': */
+ if (!row.keys().empty())
+ row.keys().back().setSpaceWidthAfter(iWidth);
+ /* If we have no keys than this is the initial space left to first key: */
+ else
+ row.setLeftMargin(iWidth);
+}
+
+void UIPhysicalLayoutReader::parseCutout(UISoftKeyboardKey &key)
+{
+ int iWidth = 0;
+ int iHeight = 0;
+ int iCorner = 0;
+ while (m_xmlReader.readNextStartElement())
+ {
+ if (m_xmlReader.name() == QLatin1String("width"))
+ iWidth = m_xmlReader.readElementText().toInt();
+ else if (m_xmlReader.name() == QLatin1String("height"))
+ iHeight = m_xmlReader.readElementText().toInt();
+ else if (m_xmlReader.name() == QLatin1String("corner"))
+ {
+ QString strCorner = m_xmlReader.readElementText();
+ if (strCorner == "topLeft")
+ iCorner = 0;
+ else if(strCorner == "topRight")
+ iCorner = 1;
+ else if(strCorner == "bottomRight")
+ iCorner = 2;
+ else if(strCorner == "bottomLeft")
+ iCorner = 3;
+ }
+ else
+ m_xmlReader.skipCurrentElement();
+ }
+ key.setCutout(iCorner, iWidth, iHeight);
+}
+
+QVector<QPointF> UIPhysicalLayoutReader::computeKeyVertices(const UISoftKeyboardKey &key)
+{
+ QVector<QPointF> vertices;
+
+ if (key.cutoutCorner() == -1 || key.width() <= key.cutoutWidth() || key.height() <= key.cutoutHeight())
+ {
+ vertices.append(QPoint(0, 0));
+ vertices.append(QPoint(key.width(), 0));
+ vertices.append(QPoint(key.width(), key.height()));
+ vertices.append(QPoint(0, key.height()));
+ return vertices;
+ }
+ if (key.cutoutCorner() == 0)
+ {
+ vertices.append(QPoint(key.cutoutWidth(), 0));
+ vertices.append(QPoint(key.width(), 0));
+ vertices.append(QPoint(key.width(), key.height()));
+ vertices.append(QPoint(0, key.height()));
+ vertices.append(QPoint(0, key.cutoutHeight()));
+ vertices.append(QPoint(key.cutoutWidth(), key.cutoutHeight()));
+ }
+ else if (key.cutoutCorner() == 1)
+ {
+ vertices.append(QPoint(0, 0));
+ vertices.append(QPoint(key.width() - key.cutoutWidth(), 0));
+ vertices.append(QPoint(key.width() - key.cutoutWidth(), key.cutoutHeight()));
+ vertices.append(QPoint(key.width(), key.cutoutHeight()));
+ vertices.append(QPoint(key.width(), key.height()));
+ vertices.append(QPoint(0, key.height()));
+ }
+ else if (key.cutoutCorner() == 2)
+ {
+ vertices.append(QPoint(0, 0));
+ vertices.append(QPoint(key.width(), 0));
+ vertices.append(QPoint(key.width(), key.cutoutHeight()));
+ vertices.append(QPoint(key.width() - key.cutoutWidth(), key.cutoutHeight()));
+ vertices.append(QPoint(key.width() - key.cutoutWidth(), key.height()));
+ vertices.append(QPoint(0, key.height()));
+ }
+ else if (key.cutoutCorner() == 3)
+ {
+ vertices.append(QPoint(0, 0));
+ vertices.append(QPoint(key.width(), 0));
+ vertices.append(QPoint(key.width(), key.height()));
+ vertices.append(QPoint(key.cutoutWidth(), key.height()));
+ vertices.append(QPoint(key.cutoutWidth(), key.height() - key.cutoutHeight()));
+ vertices.append(QPoint(0, key.height() - key.cutoutHeight()));
+ }
+ return vertices;
+}
+
+
+/*********************************************************************************************************************************
+* UIKeyboardLayoutReader implementation. *
+*********************************************************************************************************************************/
+
+bool UIKeyboardLayoutReader::parseFile(const QString &strFileName, UISoftKeyboardLayout &layout)
+{
+ QFile xmlFile(strFileName);
+ if (!xmlFile.exists())
+ return false;
+
+ if (xmlFile.size() >= iFileSizeLimit)
+ return false;
+
+ if (!xmlFile.open(QIODevice::ReadOnly))
+ return false;
+
+ m_xmlReader.setDevice(&xmlFile);
+
+ if (!m_xmlReader.readNextStartElement() || m_xmlReader.name() != QLatin1String("layout"))
+ return false;
+
+ while (m_xmlReader.readNextStartElement())
+ {
+ if (m_xmlReader.name() == QLatin1String("key"))
+ parseKey(layout);
+ else if (m_xmlReader.name() == QLatin1String("name"))
+ layout.setName(m_xmlReader.readElementText());
+ else if (m_xmlReader.name() == QLatin1String("nativename"))
+ layout.setNativeName(m_xmlReader.readElementText());
+ else if (m_xmlReader.name() == QLatin1String("physicallayoutid"))
+ layout.setPhysicalLayoutUuid(QUuid(m_xmlReader.readElementText()));
+ else if (m_xmlReader.name() == QLatin1String("id"))
+ layout.setUid(QUuid(m_xmlReader.readElementText()));
+ else
+ m_xmlReader.skipCurrentElement();
+ }
+ return true;
+}
+
+void UIKeyboardLayoutReader::parseKey(UISoftKeyboardLayout &layout)
+{
+ UIKeyCaptions keyCaptions;
+ int iKeyPosition = 0;
+ while (m_xmlReader.readNextStartElement())
+ {
+ if (m_xmlReader.name() == QLatin1String("basecaption"))
+ {
+ keyCaptions.m_strBase = m_xmlReader.readElementText();
+ keyCaptions.m_strBase.replace("\\n", "\n");
+ }
+ else if (m_xmlReader.name() == QLatin1String("shiftcaption"))
+ {
+ keyCaptions.m_strShift = m_xmlReader.readElementText();
+ keyCaptions.m_strShift.replace("\\n", "\n");
+ }
+ else if (m_xmlReader.name() == QLatin1String("altgrcaption"))
+ {
+ keyCaptions.m_strAltGr = m_xmlReader.readElementText();
+ keyCaptions.m_strAltGr.replace("\\n", "\n");
+ }
+ else if (m_xmlReader.name() == QLatin1String("shiftaltgrcaption"))
+ {
+ keyCaptions.m_strShiftAltGr = m_xmlReader.readElementText();
+ keyCaptions.m_strShiftAltGr.replace("\\n", "\n");
+ }
+ else if (m_xmlReader.name() == QLatin1String("position"))
+ iKeyPosition = m_xmlReader.readElementText().toInt();
+ else
+ m_xmlReader.skipCurrentElement();
+ }
+ layout.addOrUpdateUIKeyCaptions(iKeyPosition, keyCaptions);
+}
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardStatusBarWidget implementation. *
+*********************************************************************************************************************************/
+
+UISoftKeyboardStatusBarWidget::UISoftKeyboardStatusBarWidget(QWidget *pParent /* = 0*/ )
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pLayoutListButton(0)
+ , m_pSettingsButton(0)
+ , m_pResetButton(0)
+ , m_pHelpButton(0)
+ , m_pMessageLabel(0)
+{
+ prepareObjects();
+}
+
+void UISoftKeyboardStatusBarWidget::retranslateUi()
+{
+ if (m_pLayoutListButton)
+ m_pLayoutListButton->setToolTip(UISoftKeyboard::tr("Layout List"));
+ if (m_pSettingsButton)
+ m_pSettingsButton->setToolTip(UISoftKeyboard::tr("Settings"));
+ if (m_pResetButton)
+ m_pResetButton->setToolTip(UISoftKeyboard::tr("Reset the keyboard and release all keys"));
+ if (m_pHelpButton)
+ m_pHelpButton->setToolTip(UISoftKeyboard::tr("Help"));
+}
+
+void UISoftKeyboardStatusBarWidget::prepareObjects()
+{
+ QHBoxLayout *pLayout = new QHBoxLayout;
+ if (!pLayout)
+ return;
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ setLayout(pLayout);
+
+ m_pMessageLabel = new QLabel;
+ pLayout->addWidget(m_pMessageLabel);
+
+ m_pLayoutListButton = new QToolButton;
+ if (m_pLayoutListButton)
+ {
+ m_pLayoutListButton->setIcon(UIIconPool::iconSet(":/soft_keyboard_layout_list_16px.png", ":/soft_keyboard_layout_list_disabled_16px.png"));
+ m_pLayoutListButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pLayoutListButton->resize(QSize(iIconMetric, iIconMetric));
+ m_pLayoutListButton->setStyleSheet("QToolButton { border: 0px none black; margin: 0px 0px 0px 0px; } QToolButton::menu-indicator {image: none;}");
+ connect(m_pLayoutListButton, &QToolButton::clicked, this, &UISoftKeyboardStatusBarWidget::sigShowHideSidePanel);
+ pLayout->addWidget(m_pLayoutListButton);
+ }
+
+ m_pSettingsButton = new QToolButton;
+ if (m_pSettingsButton)
+ {
+ m_pSettingsButton->setIcon(UIIconPool::iconSet(":/soft_keyboard_settings_16px.png", ":/soft_keyboard_settings_disabled_16px.png"));
+ m_pSettingsButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pSettingsButton->resize(QSize(iIconMetric, iIconMetric));
+ m_pSettingsButton->setStyleSheet("QToolButton { border: 0px none black; margin: 0px 0px 0px 0px; } QToolButton::menu-indicator {image: none;}");
+ connect(m_pSettingsButton, &QToolButton::clicked, this, &UISoftKeyboardStatusBarWidget::sigShowSettingWidget);
+ pLayout->addWidget(m_pSettingsButton);
+ }
+
+ m_pResetButton = new QToolButton;
+ if (m_pResetButton)
+ {
+ m_pResetButton->setIcon(UIIconPool::iconSet(":/soft_keyboard_reset_16px.png"));
+ m_pResetButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pResetButton->resize(QSize(iIconMetric, iIconMetric));
+ m_pResetButton->setStyleSheet("QToolButton { border: 0px none black; margin: 0px 0px 0px 0px; } QToolButton::menu-indicator {image: none;}");
+ connect(m_pResetButton, &QToolButton::clicked, this, &UISoftKeyboardStatusBarWidget::sigResetKeyboard);
+ pLayout->addWidget(m_pResetButton);
+ }
+
+ m_pHelpButton = new QToolButton;
+ if (m_pHelpButton)
+ {
+ m_pHelpButton->setIcon(UIIconPool::iconSet(":/soft_keyboard_help_16px.png"));
+ m_pHelpButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pHelpButton->resize(QSize(iIconMetric, iIconMetric));
+ m_pHelpButton->setStyleSheet("QToolButton { border: 0px none black; margin: 0px 0px 0px 0px; } QToolButton::menu-indicator {image: none;}");
+ connect(m_pHelpButton, &QToolButton::clicked, this, &UISoftKeyboardStatusBarWidget::sigHelpButtonPressed);
+ pLayout->addWidget(m_pHelpButton);
+ }
+
+ retranslateUi();
+}
+
+void UISoftKeyboardStatusBarWidget::updateLayoutNameInStatusBar(const QString &strMessage)
+{
+ if (!m_pMessageLabel)
+ return;
+ m_pMessageLabel->setText(strMessage);
+}
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboardSettingsWidget implementation. *
+*********************************************************************************************************************************/
+
+UISoftKeyboardSettingsWidget::UISoftKeyboardSettingsWidget(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pHideNumPadCheckBox(0)
+ , m_pShowOsMenuButtonsCheckBox(0)
+ , m_pHideMultimediaKeysCheckBox(0)
+ , m_pColorThemeGroupBox(0)
+ , m_pColorThemeComboBox(0)
+ , m_pTitleLabel(0)
+ , m_pCloseButton(0)
+
+{
+ prepareObjects();
+}
+
+void UISoftKeyboardSettingsWidget::setHideOSMenuKeys(bool fHide)
+{
+ if (m_pShowOsMenuButtonsCheckBox)
+ m_pShowOsMenuButtonsCheckBox->setChecked(fHide);
+}
+
+void UISoftKeyboardSettingsWidget::setHideNumPad(bool fHide)
+{
+ if (m_pHideNumPadCheckBox)
+ m_pHideNumPadCheckBox->setChecked(fHide);
+}
+
+void UISoftKeyboardSettingsWidget::setHideMultimediaKeys(bool fHide)
+{
+ if (m_pHideMultimediaKeysCheckBox)
+ m_pHideMultimediaKeysCheckBox->setChecked(fHide);
+}
+
+void UISoftKeyboardSettingsWidget::setColorSelectionButtonBackgroundAndTooltip(KeyboardColorType enmColorType,
+ const QColor &color, bool fIsColorEditable)
+{
+ if (m_colorSelectLabelsButtons.size() > enmColorType && m_colorSelectLabelsButtons[enmColorType].second)
+ {
+ UISoftKeyboardColorButton *pButton = m_colorSelectLabelsButtons[enmColorType].second;
+ QPalette pal = pButton->palette();
+ pal.setColor(QPalette::Button, color);
+ pButton->setAutoFillBackground(true);
+ pButton->setPalette(pal);
+ pButton->setToolTip(fIsColorEditable ? UISoftKeyboard::tr("Click to change the color.") : UISoftKeyboard::tr("This color theme is not editable."));
+ pButton->update();
+ }
+}
+
+void UISoftKeyboardSettingsWidget::setColorThemeNames(const QStringList &colorThemeNames)
+{
+ if (!m_pColorThemeComboBox)
+ return;
+ m_pColorThemeComboBox->blockSignals(true);
+ m_pColorThemeComboBox->clear();
+ foreach (const QString &strName, colorThemeNames)
+ m_pColorThemeComboBox->addItem(strName);
+ m_pColorThemeComboBox->blockSignals(false);
+}
+
+void UISoftKeyboardSettingsWidget::setCurrentColorThemeName(const QString &strColorThemeName)
+{
+ if (!m_pColorThemeComboBox)
+ return;
+ int iItemIndex = m_pColorThemeComboBox->findText(strColorThemeName, Qt::MatchFixedString);
+ if (iItemIndex == -1)
+ return;
+ m_pColorThemeComboBox->blockSignals(true);
+ m_pColorThemeComboBox->setCurrentIndex(iItemIndex);
+ m_pColorThemeComboBox->blockSignals(false);
+}
+
+void UISoftKeyboardSettingsWidget::retranslateUi()
+{
+ if (m_pTitleLabel)
+ m_pTitleLabel->setText(UISoftKeyboard::tr("Keyboard Settings"));
+ if (m_pCloseButton)
+ {
+ m_pCloseButton->setToolTip(UISoftKeyboard::tr("Close the layout list"));
+ m_pCloseButton->setText("Close");
+ }
+ if (m_pHideNumPadCheckBox)
+ m_pHideNumPadCheckBox->setText(UISoftKeyboard::tr("Hide NumPad"));
+ if (m_pShowOsMenuButtonsCheckBox)
+ m_pShowOsMenuButtonsCheckBox->setText(UISoftKeyboard::tr("Hide OS/Menu Keys"));
+ if (m_pHideMultimediaKeysCheckBox)
+ m_pHideMultimediaKeysCheckBox->setText(UISoftKeyboard::tr("Hide Multimedia Keys"));
+ if (m_pColorThemeGroupBox)
+ m_pColorThemeGroupBox->setTitle(UISoftKeyboard::tr("Color Themes"));
+
+ if (m_colorSelectLabelsButtons.size() == KeyboardColorType_Max)
+ {
+ if (m_colorSelectLabelsButtons[KeyboardColorType_Background].first)
+ m_colorSelectLabelsButtons[KeyboardColorType_Background].first->setText(UISoftKeyboard::tr("Button Background Color"));
+ if (m_colorSelectLabelsButtons[KeyboardColorType_Font].first)
+ m_colorSelectLabelsButtons[KeyboardColorType_Font].first->setText(UISoftKeyboard::tr("Button Font Color"));
+ if (m_colorSelectLabelsButtons[KeyboardColorType_Hover].first)
+ m_colorSelectLabelsButtons[KeyboardColorType_Hover].first->setText(UISoftKeyboard::tr("Button Hover Color"));
+ if (m_colorSelectLabelsButtons[KeyboardColorType_Edit].first)
+ m_colorSelectLabelsButtons[KeyboardColorType_Edit].first->setText(UISoftKeyboard::tr("Button Edit Color"));
+ if (m_colorSelectLabelsButtons[KeyboardColorType_Pressed].first)
+ m_colorSelectLabelsButtons[KeyboardColorType_Pressed].first->setText(UISoftKeyboard::tr("Pressed Button Font Color"));
+ }
+}
+
+void UISoftKeyboardSettingsWidget::prepareObjects()
+{
+ QGridLayout *pSettingsLayout = new QGridLayout;
+ if (!pSettingsLayout)
+ return;
+
+ QHBoxLayout *pTitleLayout = new QHBoxLayout;
+ m_pCloseButton = new QToolButton;
+ m_pCloseButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ m_pCloseButton->setIcon(UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_DialogCancel));
+ m_pCloseButton->setAutoRaise(true);
+ connect(m_pCloseButton, &QToolButton::clicked, this, &UISoftKeyboardSettingsWidget::sigCloseSettingsWidget);
+ m_pTitleLabel = new QLabel;
+ pTitleLayout->addWidget(m_pTitleLabel);
+ pTitleLayout->addStretch(2);
+ pTitleLayout->addWidget(m_pCloseButton);
+ pSettingsLayout->addLayout(pTitleLayout, 0, 0, 1, 2);
+
+ m_pHideNumPadCheckBox = new QCheckBox;
+ m_pShowOsMenuButtonsCheckBox = new QCheckBox;
+ m_pHideMultimediaKeysCheckBox = new QCheckBox;
+ pSettingsLayout->addWidget(m_pHideNumPadCheckBox, 1, 0, 1, 1);
+ pSettingsLayout->addWidget(m_pShowOsMenuButtonsCheckBox, 2, 0, 1, 1);
+ pSettingsLayout->addWidget(m_pHideMultimediaKeysCheckBox, 3, 0, 1, 1);
+ connect(m_pHideNumPadCheckBox, &QCheckBox::toggled, this, &UISoftKeyboardSettingsWidget::sigHideNumPad);
+ connect(m_pShowOsMenuButtonsCheckBox, &QCheckBox::toggled, this, &UISoftKeyboardSettingsWidget::sigHideOSMenuKeys);
+ connect(m_pHideMultimediaKeysCheckBox, &QCheckBox::toggled, this, &UISoftKeyboardSettingsWidget::sigHideMultimediaKeys);
+
+ /* A groupbox to host the color selection widgets: */
+ m_pColorThemeGroupBox = new QGroupBox;
+ QVBoxLayout *pGroupBoxLayout = new QVBoxLayout(m_pColorThemeGroupBox);
+ pSettingsLayout->addWidget(m_pColorThemeGroupBox, 4, 0, 1, 1);
+
+ m_pColorThemeComboBox = new QComboBox;
+ pGroupBoxLayout->addWidget(m_pColorThemeComboBox);
+ connect(m_pColorThemeComboBox, &QComboBox::currentTextChanged, this, &UISoftKeyboardSettingsWidget::sigColorThemeSelectionChanged);
+
+ /* Creating and configuring the color selection buttons: */
+ QGridLayout *pColorSelectionLayout = new QGridLayout;
+ pColorSelectionLayout->setSpacing(1);
+ pGroupBoxLayout->addLayout(pColorSelectionLayout);
+ for (int i = KeyboardColorType_Background; i < KeyboardColorType_Max; ++i)
+ {
+ QLabel *pLabel = new QLabel;
+ UISoftKeyboardColorButton *pButton = new UISoftKeyboardColorButton((KeyboardColorType)i);
+ pButton->setFlat(true);
+ pButton->setMaximumWidth(3 * qApp->style()->pixelMetric(QStyle::PM_LargeIconSize));
+ pColorSelectionLayout->addWidget(pLabel, i, 0, 1, 1);
+ pColorSelectionLayout->addWidget(pButton, i, 1, 1, 1);
+ m_colorSelectLabelsButtons.append(ColorSelectLabelButton(pLabel, pButton));
+ connect(pButton, &UISoftKeyboardColorButton::clicked, this, &UISoftKeyboardSettingsWidget::sltColorSelectionButtonClicked);
+ }
+
+ QSpacerItem *pSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding);
+ if (pSpacer)
+ pSettingsLayout->addItem(pSpacer, 6, 0);
+
+ setLayout(pSettingsLayout);
+ retranslateUi();
+}
+
+void UISoftKeyboardSettingsWidget::sltColorSelectionButtonClicked()
+{
+ UISoftKeyboardColorButton *pButton = qobject_cast<UISoftKeyboardColorButton*>(sender());
+ if (!pButton)
+ return;
+ emit sigColorCellClicked((int)pButton->colorType());
+}
+
+
+/*********************************************************************************************************************************
+* UISoftKeyboard implementation. *
+*********************************************************************************************************************************/
+
+UISoftKeyboard::UISoftKeyboard(QWidget *pParent,
+ UISession *pSession, QWidget *pCenterWidget, QString strMachineName /* = QString()*/)
+ : QMainWindowWithRestorableGeometryAndRetranslateUi(pParent)
+ , m_pSession(pSession)
+ , m_pCenterWidget(pCenterWidget)
+ , m_pMainLayout(0)
+ , m_strMachineName(strMachineName)
+ , m_pSplitter(0)
+ , m_pSidePanelWidget(0)
+ , m_pKeyboardWidget(0)
+ , m_pLayoutEditor(0)
+ , m_pLayoutSelector(0)
+ , m_pSettingsWidget(0)
+ , m_pStatusBarWidget(0)
+ , m_iGeometrySaveTimerId(-1)
+{
+ setWindowTitle(QString("%1 - %2").arg(m_strMachineName).arg(tr("Soft Keyboard")));
+ prepareObjects();
+ prepareConnections();
+
+ if (m_pKeyboardWidget)
+ {
+ m_pKeyboardWidget->loadLayouts();
+ if (m_pLayoutEditor)
+ m_pLayoutEditor->setPhysicalLayoutList(m_pKeyboardWidget->physicalLayouts());
+ }
+
+ loadSettings();
+ configure();
+ retranslateUi();
+ uiCommon().setHelpKeyword(this, "soft-keyb");
+}
+
+UISoftKeyboard::~UISoftKeyboard()
+{
+}
+
+void UISoftKeyboard::retranslateUi()
+{
+}
+
+bool UISoftKeyboard::shouldBeMaximized() const
+{
+ return gEDataManager->softKeyboardDialogShouldBeMaximized();
+}
+
+void UISoftKeyboard::closeEvent(QCloseEvent *event)
+{
+ QStringList strNameList = m_pKeyboardWidget->unsavedLayoutsNameList();
+ /* Show a warning dialog when there are not saved layouts: */
+ if (m_pKeyboardWidget && !strNameList.empty())
+ {
+ QString strJoinedString = strNameList.join("<br/>");
+ if (!msgCenter().questionBinary(this, MessageType_Warning,
+ tr("<p>Following layouts are edited/copied but not saved:</p>%1"
+ "<p>Closing this dialog will cause loosing the changes. Proceed?</p>").arg(strJoinedString),
+ 0 /* auto-confirm id */,
+ "Ok", "Cancel"))
+ {
+ event->ignore();
+ return;
+ }
+ }
+ keyboard().ReleaseKeys();
+ emit sigClose();
+ event->ignore();
+}
+
+bool UISoftKeyboard::event(QEvent *pEvent)
+{
+ if (pEvent->type() == QEvent::WindowDeactivate)
+ {
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->parentDialogDeactivated();
+ }
+ else if (pEvent->type() == QEvent::KeyPress)
+ {
+ QKeyEvent *pKeyEvent = dynamic_cast<QKeyEvent*>(pEvent);
+ if (pKeyEvent)
+ {
+ if (QKeySequence(pKeyEvent->key()) == QKeySequence::HelpContents)
+ sltHandleHelpRequest();
+ }
+ }
+ else if (pEvent->type() == QEvent::Resize ||
+ pEvent->type() == QEvent::Move)
+ {
+ if (m_iGeometrySaveTimerId != -1)
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = startTimer(300);
+ }
+ else if (pEvent->type() == QEvent::Timer)
+ {
+ QTimerEvent *pTimerEvent = static_cast<QTimerEvent*>(pEvent);
+ if (pTimerEvent->timerId() == m_iGeometrySaveTimerId)
+ {
+ killTimer(m_iGeometrySaveTimerId);
+ m_iGeometrySaveTimerId = -1;
+ saveDialogGeometry();
+ }
+ }
+
+ return QMainWindowWithRestorableGeometryAndRetranslateUi::event(pEvent);
+}
+
+void UISoftKeyboard::sltKeyboardLedsChange()
+{
+ bool fNumLockLed = m_pSession->isNumLock();
+ bool fCapsLockLed = m_pSession->isCapsLock();
+ bool fScrollLockLed = m_pSession->isScrollLock();
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->updateLockKeyStates(fCapsLockLed, fNumLockLed, fScrollLockLed);
+}
+
+void UISoftKeyboard::sltPutKeyboardSequence(QVector<LONG> sequence)
+{
+ keyboard().PutScancodes(sequence);
+}
+
+void UISoftKeyboard::sltPutUsageCodesPress(QVector<QPair<LONG, LONG> > sequence)
+{
+ for (int i = 0; i < sequence.size(); ++i)
+ keyboard().PutUsageCode(sequence[i].first, sequence[i].second, false);
+}
+
+void UISoftKeyboard::sltPutUsageCodesRelease(QVector<QPair<LONG, LONG> > sequence)
+{
+ for (int i = 0; i < sequence.size(); ++i)
+ keyboard().PutUsageCode(sequence[i].first, sequence[i].second, true);
+}
+
+void UISoftKeyboard::sltLayoutSelectionChanged(const QUuid &layoutUid)
+{
+ if (!m_pKeyboardWidget)
+ return;
+ m_pKeyboardWidget->setCurrentLayout(layoutUid);
+ if (m_pLayoutSelector && m_pKeyboardWidget->currentLayout())
+ m_pLayoutSelector->setCurrentLayoutIsEditable(m_pKeyboardWidget->currentLayout()->editable());
+}
+
+void UISoftKeyboard::sltCurentLayoutChanged()
+{
+ if (!m_pKeyboardWidget)
+ return;
+ UISoftKeyboardLayout *pCurrentLayout = m_pKeyboardWidget->currentLayout();
+
+ /* Update the status bar string: */
+ if (!pCurrentLayout)
+ return;
+ updateStatusBarMessage(pCurrentLayout->nameString());
+ saveCurrentLayout();
+}
+
+void UISoftKeyboard::sltShowLayoutSelector()
+{
+ if (m_pSidePanelWidget && m_pLayoutSelector)
+ m_pSidePanelWidget->setCurrentWidget(m_pLayoutSelector);
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->toggleEditMode(false);
+ if (m_pLayoutEditor)
+ m_pLayoutEditor->setKey(0);
+}
+
+void UISoftKeyboard::sltShowLayoutEditor()
+{
+ if (m_pSidePanelWidget && m_pLayoutEditor)
+ {
+ m_pLayoutEditor->setLayoutToEdit(m_pKeyboardWidget->currentLayout());
+ m_pSidePanelWidget->setCurrentWidget(m_pLayoutEditor);
+ }
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->toggleEditMode(true);
+}
+
+void UISoftKeyboard::sltKeyToEditChanged(UISoftKeyboardKey* pKey)
+{
+ if (m_pLayoutEditor)
+ m_pLayoutEditor->setKey(pKey);
+}
+
+void UISoftKeyboard::sltLayoutEdited()
+{
+ if (!m_pKeyboardWidget)
+ return;
+ m_pKeyboardWidget->update();
+ updateLayoutSelectorList();
+ UISoftKeyboardLayout *pCurrentLayout = m_pKeyboardWidget->currentLayout();
+
+ /* Update the status bar string: */
+ QString strLayoutName = pCurrentLayout ? pCurrentLayout->name() : QString();
+ updateStatusBarMessage(strLayoutName);
+}
+
+void UISoftKeyboard::sltKeyCaptionsEdited(UISoftKeyboardKey* pKey)
+{
+ Q_UNUSED(pKey);
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->update();
+}
+
+void UISoftKeyboard::sltShowHideSidePanel()
+{
+ if (!m_pSidePanelWidget)
+ return;
+ m_pSidePanelWidget->setVisible(!m_pSidePanelWidget->isVisible());
+
+ if (m_pSidePanelWidget->isVisible() && m_pSettingsWidget->isVisible())
+ m_pSettingsWidget->setVisible(false);
+}
+
+void UISoftKeyboard::sltShowHideSettingsWidget()
+{
+ if (!m_pSettingsWidget)
+ return;
+ m_pSettingsWidget->setVisible(!m_pSettingsWidget->isVisible());
+ if (m_pSidePanelWidget->isVisible() && m_pSettingsWidget->isVisible())
+ m_pSidePanelWidget->setVisible(false);
+}
+
+void UISoftKeyboard::sltHandleColorThemeListSelection(const QString &strColorThemeName)
+{
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->setColorThemeByName(strColorThemeName);
+ saveSelectedColorThemeName();
+}
+
+void UISoftKeyboard::sltHandleKeyboardWidgetColorThemeChange()
+{
+ for (int i = (int)KeyboardColorType_Background;
+ i < (int)KeyboardColorType_Max; ++i)
+ {
+ KeyboardColorType enmType = (KeyboardColorType)i;
+ m_pSettingsWidget->setColorSelectionButtonBackgroundAndTooltip(enmType,
+ m_pKeyboardWidget->color(enmType),
+ m_pKeyboardWidget->isColorThemeEditable());
+ }
+}
+
+void UISoftKeyboard::sltCopyLayout()
+{
+ if (!m_pKeyboardWidget)
+ return;
+ m_pKeyboardWidget->copyCurentLayout();
+ updateLayoutSelectorList();
+}
+
+void UISoftKeyboard::sltSaveLayout()
+{
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->saveCurentLayoutToFile();
+}
+
+void UISoftKeyboard::sltDeleteLayout()
+{
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->deleteCurrentLayout();
+ updateLayoutSelectorList();
+ if (m_pKeyboardWidget && m_pKeyboardWidget->currentLayout() && m_pLayoutSelector)
+ {
+ m_pLayoutSelector->setCurrentLayout(m_pKeyboardWidget->currentLayout()->uid());
+ m_pLayoutSelector->setCurrentLayoutIsEditable(m_pKeyboardWidget->currentLayout()->editable());
+ }
+}
+
+void UISoftKeyboard::sltStatusBarMessage(const QString &strMessage)
+{
+ statusBar()->showMessage(strMessage, iMessageTimeout);
+}
+
+void UISoftKeyboard::sltShowHideOSMenuKeys(bool fHide)
+{
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->setHideOSMenuKeys(fHide);
+}
+
+void UISoftKeyboard::sltShowHideNumPad(bool fHide)
+{
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->setHideNumPad(fHide);
+}
+
+void UISoftKeyboard::sltShowHideMultimediaKeys(bool fHide)
+{
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->setHideMultimediaKeys(fHide);
+}
+
+void UISoftKeyboard::sltHandleColorCellClick(int iColorRow)
+{
+ if (!m_pKeyboardWidget || iColorRow >= static_cast<int>(KeyboardColorType_Max))
+ return;
+
+ if (!m_pKeyboardWidget->isColorThemeEditable())
+ return;
+ const QColor &currentColor = m_pKeyboardWidget->color(static_cast<KeyboardColorType>(iColorRow));
+ QColorDialog colorDialog(currentColor, this);
+
+ if (colorDialog.exec() == QDialog::Rejected)
+ return;
+ QColor newColor = colorDialog.selectedColor();
+ if (currentColor == newColor)
+ return;
+ m_pKeyboardWidget->setColor(static_cast<KeyboardColorType>(iColorRow), newColor);
+ m_pSettingsWidget->setColorSelectionButtonBackgroundAndTooltip(static_cast<KeyboardColorType>(iColorRow),
+ newColor, m_pKeyboardWidget->isColorThemeEditable());
+ saveCustomColorTheme();
+}
+
+void UISoftKeyboard::sltResetKeyboard()
+{
+ if (m_pKeyboardWidget)
+ m_pKeyboardWidget->reset();
+ if (m_pLayoutEditor)
+ m_pLayoutEditor->reset();
+ keyboard().ReleaseKeys();
+ update();
+}
+
+void UISoftKeyboard::sltHandleHelpRequest()
+{
+ emit sigHelpRequested(uiCommon().helpKeyword(this));
+}
+
+void UISoftKeyboard::prepareObjects()
+{
+ m_pSplitter = new QSplitter;
+ if (!m_pSplitter)
+ return;
+ setCentralWidget(m_pSplitter);
+ m_pSidePanelWidget = new QStackedWidget;
+ if (!m_pSidePanelWidget)
+ return;
+
+ m_pSidePanelWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+ m_pSidePanelWidget->hide();
+
+ m_pLayoutSelector = new UILayoutSelector;
+ if (m_pLayoutSelector)
+ m_pSidePanelWidget->addWidget(m_pLayoutSelector);
+
+ m_pLayoutEditor = new UIKeyboardLayoutEditor;
+ if (m_pLayoutEditor)
+ m_pSidePanelWidget->addWidget(m_pLayoutEditor);
+
+ m_pSettingsWidget = new UISoftKeyboardSettingsWidget;
+ if (m_pSettingsWidget)
+ {
+ m_pSettingsWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+ m_pSettingsWidget->hide();
+ }
+ m_pKeyboardWidget = new UISoftKeyboardWidget;
+ if (!m_pKeyboardWidget)
+ return;
+ m_pKeyboardWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ m_pKeyboardWidget->updateGeometry();
+ m_pSplitter->addWidget(m_pKeyboardWidget);
+ m_pSplitter->addWidget(m_pSidePanelWidget);
+ m_pSplitter->addWidget(m_pSettingsWidget);
+
+ m_pSplitter->setCollapsible(0, false);
+ m_pSplitter->setCollapsible(1, false);
+ m_pSplitter->setCollapsible(2, false);
+
+ statusBar()->setStyleSheet( "QStatusBar::item { border: 0px}" );
+ m_pStatusBarWidget = new UISoftKeyboardStatusBarWidget;
+ statusBar()->addPermanentWidget(m_pStatusBarWidget);
+
+ retranslateUi();
+}
+
+void UISoftKeyboard::prepareConnections()
+{
+ connect(m_pSession, &UISession::sigKeyboardLedsChange, this, &UISoftKeyboard::sltKeyboardLedsChange);
+ connect(m_pKeyboardWidget, &UISoftKeyboardWidget::sigPutKeyboardSequence, this, &UISoftKeyboard::sltPutKeyboardSequence);
+ connect(m_pKeyboardWidget, &UISoftKeyboardWidget::sigPutUsageCodesPress, this, &UISoftKeyboard::sltPutUsageCodesPress);
+ connect(m_pKeyboardWidget, &UISoftKeyboardWidget::sigPutUsageCodesRelease, this, &UISoftKeyboard::sltPutUsageCodesRelease);
+ connect(m_pKeyboardWidget, &UISoftKeyboardWidget::sigCurrentLayoutChange, this, &UISoftKeyboard::sltCurentLayoutChanged);
+ connect(m_pKeyboardWidget, &UISoftKeyboardWidget::sigKeyToEdit, this, &UISoftKeyboard::sltKeyToEditChanged);
+ connect(m_pKeyboardWidget, &UISoftKeyboardWidget::sigStatusBarMessage, this, &UISoftKeyboard::sltStatusBarMessage);
+ connect(m_pKeyboardWidget, &UISoftKeyboardWidget::sigCurrentColorThemeChanged, this, &UISoftKeyboard::sltHandleKeyboardWidgetColorThemeChange);
+ connect(m_pKeyboardWidget, &UISoftKeyboardWidget::sigOptionsChanged, this, &UISoftKeyboard::sltSaveSettings);
+
+ connect(m_pLayoutSelector, &UILayoutSelector::sigLayoutSelectionChanged, this, &UISoftKeyboard::sltLayoutSelectionChanged);
+ connect(m_pLayoutSelector, &UILayoutSelector::sigShowLayoutEditor, this, &UISoftKeyboard::sltShowLayoutEditor);
+ connect(m_pLayoutSelector, &UILayoutSelector::sigCloseLayoutList, this, &UISoftKeyboard::sltShowHideSidePanel);
+ connect(m_pLayoutSelector, &UILayoutSelector::sigSaveLayout, this, &UISoftKeyboard::sltSaveLayout);
+ connect(m_pLayoutSelector, &UILayoutSelector::sigDeleteLayout, this, &UISoftKeyboard::sltDeleteLayout);
+ connect(m_pLayoutSelector, &UILayoutSelector::sigCopyLayout, this, &UISoftKeyboard::sltCopyLayout);
+ connect(m_pLayoutEditor, &UIKeyboardLayoutEditor::sigGoBackButton, this, &UISoftKeyboard::sltShowLayoutSelector);
+ connect(m_pLayoutEditor, &UIKeyboardLayoutEditor::sigLayoutEdited, this, &UISoftKeyboard::sltLayoutEdited);
+ connect(m_pLayoutEditor, &UIKeyboardLayoutEditor::sigUIKeyCaptionsEdited, this, &UISoftKeyboard::sltKeyCaptionsEdited);
+
+ connect(m_pStatusBarWidget, &UISoftKeyboardStatusBarWidget::sigShowHideSidePanel, this, &UISoftKeyboard::sltShowHideSidePanel);
+ connect(m_pStatusBarWidget, &UISoftKeyboardStatusBarWidget::sigShowSettingWidget, this, &UISoftKeyboard::sltShowHideSettingsWidget);
+ connect(m_pStatusBarWidget, &UISoftKeyboardStatusBarWidget::sigResetKeyboard, this, &UISoftKeyboard::sltResetKeyboard);
+ connect(m_pStatusBarWidget, &UISoftKeyboardStatusBarWidget::sigHelpButtonPressed, this, &UISoftKeyboard::sltHandleHelpRequest);
+
+ connect(m_pSettingsWidget, &UISoftKeyboardSettingsWidget::sigHideOSMenuKeys, this, &UISoftKeyboard::sltShowHideOSMenuKeys);
+ connect(m_pSettingsWidget, &UISoftKeyboardSettingsWidget::sigHideNumPad, this, &UISoftKeyboard::sltShowHideNumPad);
+ connect(m_pSettingsWidget, &UISoftKeyboardSettingsWidget::sigHideMultimediaKeys, this, &UISoftKeyboard::sltShowHideMultimediaKeys);
+ connect(m_pSettingsWidget, &UISoftKeyboardSettingsWidget::sigColorCellClicked, this, &UISoftKeyboard::sltHandleColorCellClick);
+ connect(m_pSettingsWidget, &UISoftKeyboardSettingsWidget::sigCloseSettingsWidget, this, &UISoftKeyboard::sltShowHideSettingsWidget);
+ connect(m_pSettingsWidget, &UISoftKeyboardSettingsWidget::sigColorThemeSelectionChanged, this, &UISoftKeyboard::sltHandleColorThemeListSelection);
+
+ connect(this, &UISoftKeyboard::sigHelpRequested, &msgCenter(), &UIMessageCenter::sltHandleHelpRequest);
+ connect(&uiCommon(), &UICommon::sigAskToCommitData, this, &UISoftKeyboard::sltReleaseKeys);
+}
+
+void UISoftKeyboard::saveDialogGeometry()
+{
+ const QRect geo = currentGeometry();
+ LogRel2(("GUI: UISoftKeyboard: Saving geometry as: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ gEDataManager->setSoftKeyboardDialogGeometry(geo, isCurrentlyMaximized());
+}
+
+void UISoftKeyboard::saveCustomColorTheme()
+{
+ if (!m_pKeyboardWidget)
+ return;
+ /* Save the changes to the 'Custom' color theme to extra data: */
+ QStringList colors = m_pKeyboardWidget->colorsToStringList("Custom");
+ colors.prepend("Custom");
+ gEDataManager->setSoftKeyboardColorTheme(colors);
+}
+
+void UISoftKeyboard::saveSelectedColorThemeName()
+{
+ if (!m_pKeyboardWidget)
+ return;
+ gEDataManager->setSoftKeyboardSelectedColorTheme(m_pKeyboardWidget->currentColorThemeName());
+}
+
+void UISoftKeyboard::saveCurrentLayout()
+{
+ if (m_pKeyboardWidget && m_pKeyboardWidget->currentLayout())
+ gEDataManager->setSoftKeyboardSelectedLayout(m_pKeyboardWidget->currentLayout()->uid());
+}
+
+void UISoftKeyboard::sltSaveSettings()
+{
+ /* Save other settings: */
+ if (m_pKeyboardWidget)
+ {
+ gEDataManager->setSoftKeyboardOptions(m_pKeyboardWidget->hideNumPad(),
+ m_pKeyboardWidget->hideOSMenuKeys(),
+ m_pKeyboardWidget->hideMultimediaKeys());
+ }
+}
+
+void UISoftKeyboard::sltReleaseKeys()
+{
+ keyboard().ReleaseKeys();
+}
+
+void UISoftKeyboard::loadSettings()
+{
+ /* Invent default window geometry: */
+ float fKeyboardAspectRatio = 1.0f;
+ if (m_pKeyboardWidget)
+ fKeyboardAspectRatio = m_pKeyboardWidget->layoutAspectRatio();
+ const QRect availableGeo = gpDesktop->availableGeometry(this);
+ const int iDefaultWidth = availableGeo.width() / 2;
+ const int iDefaultHeight = iDefaultWidth * fKeyboardAspectRatio;
+ QRect defaultGeo(0, 0, iDefaultWidth, iDefaultHeight);
+
+ /* Load geometry from extradata: */
+ const QRect geo = gEDataManager->softKeyboardDialogGeometry(this, m_pCenterWidget, defaultGeo);
+ LogRel2(("GUI: UISoftKeyboard: Restoring geometry to: Origin=%dx%d, Size=%dx%d\n",
+ geo.x(), geo.y(), geo.width(), geo.height()));
+ restoreGeometry(geo);
+
+ /* Load other settings: */
+ if (m_pKeyboardWidget)
+ {
+ QStringList colorTheme = gEDataManager->softKeyboardColorTheme();
+ if (!colorTheme.empty())
+ {
+ /* The fist item is the theme name and the rest are color codes: */
+ QString strThemeName = colorTheme[0];
+ colorTheme.removeFirst();
+ m_pKeyboardWidget->colorsFromStringList(strThemeName, colorTheme);
+ }
+ m_pKeyboardWidget->setColorThemeByName(gEDataManager->softKeyboardSelectedColorTheme());
+ m_pKeyboardWidget->setCurrentLayout(gEDataManager->softKeyboardSelectedLayout());
+
+ /* Load other options from exra data: */
+ bool fHideNumPad = false;
+ bool fHideOSMenuKeys = false;
+ bool fHideMultimediaKeys = false;
+ gEDataManager->softKeyboardOptions(fHideNumPad, fHideOSMenuKeys, fHideMultimediaKeys);
+ m_pKeyboardWidget->setHideNumPad(fHideNumPad);
+ m_pKeyboardWidget->setHideOSMenuKeys(fHideOSMenuKeys);
+ m_pKeyboardWidget->setHideMultimediaKeys(fHideMultimediaKeys);
+ }
+}
+
+void UISoftKeyboard::configure()
+{
+#ifndef VBOX_WS_MAC
+ /* Assign window icon: */
+ setWindowIcon(UIIconPool::iconSetFull(":/soft_keyboard_32px.png", ":/soft_keyboard_16px.png"));
+#endif
+
+ if (m_pKeyboardWidget && m_pSettingsWidget)
+ {
+ m_pSettingsWidget->setHideOSMenuKeys(m_pKeyboardWidget->hideOSMenuKeys());
+ m_pSettingsWidget->setHideNumPad(m_pKeyboardWidget->hideNumPad());
+ m_pSettingsWidget->setHideMultimediaKeys(m_pKeyboardWidget->hideMultimediaKeys());
+
+ m_pSettingsWidget->setColorThemeNames(m_pKeyboardWidget->colorThemeNames());
+ m_pSettingsWidget->setCurrentColorThemeName(m_pKeyboardWidget->currentColorThemeName());
+
+ for (int i = (int)KeyboardColorType_Background;
+ i < (int)KeyboardColorType_Max; ++i)
+ {
+ KeyboardColorType enmType = (KeyboardColorType)i;
+ m_pSettingsWidget->setColorSelectionButtonBackgroundAndTooltip(enmType,
+ m_pKeyboardWidget->color(enmType),
+ m_pKeyboardWidget->isColorThemeEditable());
+ }
+ }
+ updateLayoutSelectorList();
+ if (m_pKeyboardWidget && m_pKeyboardWidget->currentLayout() && m_pLayoutSelector)
+ {
+ m_pLayoutSelector->setCurrentLayout(m_pKeyboardWidget->currentLayout()->uid());
+ m_pLayoutSelector->setCurrentLayoutIsEditable(m_pKeyboardWidget->currentLayout()->editable());
+ }
+}
+
+void UISoftKeyboard::updateStatusBarMessage(const QString &strName)
+{
+ if (!m_pStatusBarWidget)
+ return;
+ QString strMessage;
+ if (!strName.isEmpty())
+ {
+ strMessage += QString("%1: %2").arg(tr("Layout")).arg(strName);
+ m_pStatusBarWidget->updateLayoutNameInStatusBar(strMessage);
+ }
+ else
+ m_pStatusBarWidget->updateLayoutNameInStatusBar(QString());
+}
+
+void UISoftKeyboard::updateLayoutSelectorList()
+{
+ if (!m_pKeyboardWidget || !m_pLayoutSelector)
+ return;
+ m_pLayoutSelector->setLayoutList(m_pKeyboardWidget->layoutNameList(), m_pKeyboardWidget->layoutUidList());
+}
+
+CKeyboard& UISoftKeyboard::keyboard() const
+{
+ return m_pSession->keyboard();
+}
+
+#include "UISoftKeyboard.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/softkeyboard/UISoftKeyboard.h b/src/VBox/Frontends/VirtualBox/src/softkeyboard/UISoftKeyboard.h
new file mode 100644
index 00000000..71c29eef
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/softkeyboard/UISoftKeyboard.h
@@ -0,0 +1,148 @@
+/* $Id: UISoftKeyboard.h $ */
+/** @file
+ * VBox Qt GUI - UISoftKeyboard class declaration.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_softkeyboard_UISoftKeyboard_h
+#define FEQT_INCLUDED_SRC_softkeyboard_UISoftKeyboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMainWindow>
+
+/* COM includes: */
+#include "COMDefs.h"
+
+/* GUI includes: */
+#include "QIWithRestorableGeometry.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class CKeyboard;
+class QHBoxLayout;
+class QToolButton;
+class UIKeyboardLayoutEditor;
+class UILayoutSelector;
+class UISession;
+class UISoftKeyboardKey;
+class UISoftKeyboardSettingsWidget;
+class UISoftKeyboardStatusBarWidget;
+class UISoftKeyboardWidget;
+class QSplitter;
+class QStackedWidget;
+
+/* Type definitions: */
+typedef QIWithRestorableGeometry<QMainWindow> QMainWindowWithRestorableGeometry;
+typedef QIWithRetranslateUI<QMainWindowWithRestorableGeometry> QMainWindowWithRestorableGeometryAndRetranslateUi;
+
+class UISoftKeyboard : public QMainWindowWithRestorableGeometryAndRetranslateUi
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigHelpRequested(const QString &strHelpKeyword);
+ void sigClose();
+
+public:
+
+ UISoftKeyboard(QWidget *pParent, UISession *pSession, QWidget *pCenterWidget,
+ QString strMachineName = QString());
+ ~UISoftKeyboard();
+
+protected:
+
+ virtual void retranslateUi() RT_OVERRIDE;
+ virtual bool shouldBeMaximized() const RT_OVERRIDE;
+ virtual void closeEvent(QCloseEvent *event) RT_OVERRIDE;
+ bool event(QEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ void sltKeyboardLedsChange();
+ void sltPutKeyboardSequence(QVector<LONG> sequence);
+ void sltPutUsageCodesPress(QVector<QPair<LONG, LONG> > sequence);
+ void sltPutUsageCodesRelease(QVector<QPair<LONG, LONG> > sequence);
+
+ /** Handles the signal we get from the layout selector widget.
+ * Selection changed is forwarded to the keyboard widget. */
+ void sltLayoutSelectionChanged(const QUuid &layoutUid);
+ /** Handles the signal we get from the keyboard widget. */
+ void sltCurentLayoutChanged();
+ void sltShowLayoutSelector();
+ void sltShowLayoutEditor();
+ void sltKeyToEditChanged(UISoftKeyboardKey* pKey);
+ void sltLayoutEdited();
+ /** Make th necessary changes to data structures when th key captions updated. */
+ void sltKeyCaptionsEdited(UISoftKeyboardKey* pKey);
+ void sltShowHideSidePanel();
+ void sltShowHideSettingsWidget();
+ void sltHandleColorThemeListSelection(const QString &strColorThemeName);
+ void sltHandleKeyboardWidgetColorThemeChange();
+ void sltCopyLayout();
+ void sltSaveLayout();
+ void sltDeleteLayout();
+ void sltStatusBarMessage(const QString &strMessage);
+ void sltShowHideOSMenuKeys(bool fShow);
+ void sltShowHideNumPad(bool fShow);
+ void sltShowHideMultimediaKeys(bool fHide);
+ void sltHandleColorCellClick(int iColorRow);
+ void sltResetKeyboard();
+ void sltHandleHelpRequest();
+ void sltSaveSettings();
+ void sltReleaseKeys();
+
+private:
+
+ void prepareObjects();
+ void prepareConnections();
+ void loadSettings();
+ void saveCustomColorTheme();
+ void saveSelectedColorThemeName();
+ void saveCurrentLayout();
+ void saveDialogGeometry();
+ void configure();
+ void updateStatusBarMessage(const QString &strLayoutName);
+ void updateLayoutSelectorList();
+ CKeyboard& keyboard() const;
+
+ UISession *m_pSession;
+ QWidget *m_pCenterWidget;
+ QHBoxLayout *m_pMainLayout;
+ QString m_strMachineName;
+ QSplitter *m_pSplitter;
+ QStackedWidget *m_pSidePanelWidget;
+ UISoftKeyboardWidget *m_pKeyboardWidget;
+ UIKeyboardLayoutEditor *m_pLayoutEditor;
+ UILayoutSelector *m_pLayoutSelector;
+
+ UISoftKeyboardSettingsWidget *m_pSettingsWidget;
+ UISoftKeyboardStatusBarWidget *m_pStatusBarWidget;
+ int m_iGeometrySaveTimerId;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_softkeyboard_UISoftKeyboard_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/widgets/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIAddDiskEncryptionPasswordDialog.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIAddDiskEncryptionPasswordDialog.cpp
new file mode 100644
index 00000000..6396b373
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIAddDiskEncryptionPasswordDialog.cpp
@@ -0,0 +1,622 @@
+/* $Id: UIAddDiskEncryptionPasswordDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIAddDiskEncryptionPasswordDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAbstractTableModel>
+#include <QHeaderView>
+#include <QItemEditorFactory>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QStandardItemEditorCreator>
+#include <QTableView>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QIStyledItemDelegate.h"
+#include "QIWithRetranslateUI.h"
+#include "UICommon.h"
+#include "UIAddDiskEncryptionPasswordDialog.h"
+#include "UIIconPool.h"
+#include "UIMedium.h"
+#include "UINotificationCenter.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/** UIEncryptionDataTable field indexes. */
+enum UIEncryptionDataTableSection
+{
+ UIEncryptionDataTableSection_Id,
+ UIEncryptionDataTableSection_Password,
+ UIEncryptionDataTableSection_Max
+};
+
+template<class T>
+static QStringList toStringList(const QList<T> &list)
+{
+ QStringList l;
+ foreach(const T &t, list)
+ l << t.toString();
+ return l;
+}
+
+/** QLineEdit extension used as
+ * the embedded password editor for the UIEncryptionDataTable. */
+class UIPasswordEditor : public QLineEdit
+{
+ Q_OBJECT;
+
+ /** Holds the current password of the editor. */
+ Q_PROPERTY(QString password READ password WRITE setPassword USER true);
+
+signals:
+
+ /** Notifies listeners about data should be committed. */
+ void sigCommitData(QWidget *pThis);
+
+ /** Notifies listeners about Enter/Return key triggering. */
+ void sigEnterKeyTriggered();
+
+public:
+
+ /** Constructs password editor passing @a pParent to the base-class. */
+ UIPasswordEditor(QWidget *pParent);
+
+protected:
+
+ /** Handles key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Commits data to the listeners. */
+ void sltCommitData() { emit sigCommitData(this); }
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Property: Returns the current password of the editor. */
+ QString password() const { return QLineEdit::text(); }
+ /** Property: Defines the current @a strPassword of the editor. */
+ void setPassword(const QString &strPassword) { QLineEdit::setText(strPassword); }
+};
+
+
+/** QAbstractTableModel extension used as
+ * the data representation model for the UIEncryptionDataTable. */
+class UIEncryptionDataModel : public QAbstractTableModel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs model passing @a pParent to the base-class.
+ * @param encryptedMedia Brings the lists of medium ids (values) encrypted with passwords with ids (keys). */
+ UIEncryptionDataModel(QObject *pParent, const EncryptedMediumMap &encryptedMedia);
+
+ /** Returns the shallow copy of the encryption password map instance. */
+ EncryptionPasswordMap encryptionPasswords() const { return m_encryptionPasswords; }
+
+ /** Returns the row count, taking optional @a parent instead of root if necessary. */
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ /** Returns the column count, taking optional @a parent instead of root if necessary. */
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+ /** Returns the @a index flags. */
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ /** Returns the header data for the @a iSection, @a orientation and @a iRole. */
+ virtual QVariant headerData(int iSection, Qt::Orientation orientation, int iRole) const;
+
+ /** Returns the @a index data for the @a iRole. */
+ virtual QVariant data(const QModelIndex &index, int iRole = Qt::DisplayRole) const;
+ /** Defines the @a index data for the @a iRole as @a value. */
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int iRole = Qt::EditRole);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the encrypted medium map reference. */
+ const EncryptedMediumMap &m_encryptedMedia;
+
+ /** Holds the encryption password map instance. */
+ EncryptionPasswordMap m_encryptionPasswords;
+};
+
+
+/** QTableView extension used to
+ * allow the UIAddDiskEncryptionPasswordDialog to enter
+ * disk encryption passwords for particular password ids. */
+class UIEncryptionDataTable : public QTableView
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about editor's Enter/Return key triggering. */
+ void sigEditorEnterKeyTriggered();
+
+public:
+
+ /** Constructs table.
+ * @param encryptedMedia Brings the lists of medium ids (values) encrypted with passwords with ids (keys). */
+ UIEncryptionDataTable(const EncryptedMediumMap &encryptedMedia);
+ /** Destructs table. */
+ virtual ~UIEncryptionDataTable() RT_OVERRIDE;
+
+ /** Returns the shallow copy of the encryption password map
+ * acquired from the UIEncryptionDataModel instance. */
+ EncryptionPasswordMap encryptionPasswords() const;
+
+ /** Initiates the editor for the first index available. */
+ void editFirstIndex();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the encrypted medium map reference. */
+ const EncryptedMediumMap &m_encryptedMedia;
+
+ /** Holds the encryption-data model instance. */
+ UIEncryptionDataModel *m_pModelEncryptionData;
+
+ /** Holds the item editor factory instance. */
+ QItemEditorFactory *m_pItemEditorFactory;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIPasswordEditor implementation. *
+*********************************************************************************************************************************/
+
+UIPasswordEditor::UIPasswordEditor(QWidget *pParent)
+ : QLineEdit(pParent)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIPasswordEditor::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Call to base-class: */
+ QLineEdit::keyPressEvent(pEvent);
+
+ /* Broadcast Enter/Return key press: */
+ switch (pEvent->key())
+ {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ emit sigEnterKeyTriggered();
+ pEvent->accept();
+ break;
+ default:
+ break;
+ }
+}
+
+void UIPasswordEditor::prepare()
+{
+ /* Make sure QIStyledDelegate aware of us: */
+ setProperty("has_sigCommitData", true);
+ setProperty("has_sigEnterKeyTriggered", true);
+ /* Set echo mode: */
+ setEchoMode(QLineEdit::Password);
+ /* Listen for the text changes: */
+ connect(this, &UIPasswordEditor::textChanged,
+ this, &UIPasswordEditor::sltCommitData);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIEncryptionDataModel implementation. *
+*********************************************************************************************************************************/
+
+UIEncryptionDataModel::UIEncryptionDataModel(QObject *pParent, const EncryptedMediumMap &encryptedMedia)
+ : QAbstractTableModel(pParent)
+ , m_encryptedMedia(encryptedMedia)
+{
+ /* Prepare: */
+ prepare();
+}
+
+int UIEncryptionDataModel::rowCount(const QModelIndex &parent /* = QModelIndex() */) const
+{
+ Q_UNUSED(parent);
+ return m_encryptionPasswords.size();
+}
+
+int UIEncryptionDataModel::columnCount(const QModelIndex &parent /* = QModelIndex() */) const
+{
+ Q_UNUSED(parent);
+ return UIEncryptionDataTableSection_Max;
+}
+
+Qt::ItemFlags UIEncryptionDataModel::flags(const QModelIndex &index) const
+{
+ /* Check index validness: */
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ /* Depending on column index: */
+ switch (index.column())
+ {
+ case UIEncryptionDataTableSection_Id: return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ case UIEncryptionDataTableSection_Password: return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
+ default: break;
+ }
+ /* No flags by default: */
+ return Qt::NoItemFlags;
+}
+
+QVariant UIEncryptionDataModel::headerData(int iSection, Qt::Orientation orientation, int iRole) const
+{
+ /* Check argument validness: */
+ if (iRole != Qt::DisplayRole || orientation != Qt::Horizontal)
+ return QVariant();
+ /* Depending on column index: */
+ switch (iSection)
+ {
+ case UIEncryptionDataTableSection_Id: return UIAddDiskEncryptionPasswordDialog::tr("ID", "password table field");
+ case UIEncryptionDataTableSection_Password: return UIAddDiskEncryptionPasswordDialog::tr("Password", "password table field");
+ default: break;
+ }
+ /* Null value by default: */
+ return QVariant();
+}
+
+QVariant UIEncryptionDataModel::data(const QModelIndex &index, int iRole /* = Qt::DisplayRole */) const
+{
+ /* Check argument validness: */
+ if (!index.isValid())
+ return QVariant();
+ /* Depending on role: */
+ switch (iRole)
+ {
+ case Qt::DisplayRole:
+ {
+ /* Depending on column index: */
+ switch (index.column())
+ {
+ case UIEncryptionDataTableSection_Id:
+ return m_encryptionPasswords.keys().at(index.row());
+ case UIEncryptionDataTableSection_Password:
+ return QString().fill('*', m_encryptionPasswords.value(m_encryptionPasswords.keys().at(index.row())).size());
+ default:
+ return QVariant();
+ }
+ break;
+ }
+ case Qt::EditRole:
+ {
+ /* Depending on column index: */
+ switch (index.column())
+ {
+ case UIEncryptionDataTableSection_Password:
+ return m_encryptionPasswords.value(m_encryptionPasswords.keys().at(index.row()));
+ default:
+ return QVariant();
+ }
+ break;
+ }
+ case Qt::ToolTipRole:
+ {
+ /* We are generating tool-tip here and not in retranslateUi() because of the tricky plural form handling,
+ * but be quiet, it's safe enough because the tool-tip being re-acquired every time on mouse-hovering. */
+ const QList<QUuid> encryptedMedia = m_encryptedMedia.values(m_encryptionPasswords.keys().at(index.row()));
+ return UIAddDiskEncryptionPasswordDialog::tr("<nobr>Used by the following %n hard disk(s):</nobr><br>%1",
+ "This text is never used with n == 0. "
+ "Feel free to drop the %n where possible, "
+ "we only included it because of problems with Qt Linguist "
+ "(but the user can see how many hard drives are in the tool-tip "
+ "and doesn't need to be told).",
+ encryptedMedia.size())
+ .arg(toStringList(encryptedMedia).join("<br>"));
+ }
+ default:
+ break;
+ }
+ /* Null value by default: */
+ return QVariant();
+}
+
+bool UIEncryptionDataModel::setData(const QModelIndex &index, const QVariant &value, int iRole /* = Qt::EditRole */)
+{
+ /* Check argument validness: */
+ if (!index.isValid() || iRole != Qt::EditRole)
+ return false;
+ /* Depending on column index: */
+ switch (index.column())
+ {
+ case UIEncryptionDataTableSection_Password:
+ {
+ /* Update password: */
+ const int iRow = index.row();
+ const QString strPassword = value.toString();
+ const QString strKey = m_encryptionPasswords.keys().at(iRow);
+ m_encryptionPasswords[strKey] = strPassword;
+ break;
+ }
+ default:
+ break;
+ }
+ /* Nothing to set by default: */
+ return false;
+}
+
+void UIEncryptionDataModel::prepare()
+{
+ /* Populate the map of passwords and statuses: */
+ foreach (const QString &strPasswordId, m_encryptedMedia.keys())
+ m_encryptionPasswords.insert(strPasswordId, QString());
+}
+
+
+/*********************************************************************************************************************************
+* Class UIEncryptionDataTable implementation. *
+*********************************************************************************************************************************/
+
+UIEncryptionDataTable::UIEncryptionDataTable(const EncryptedMediumMap &encryptedMedia)
+ : m_encryptedMedia(encryptedMedia)
+ , m_pModelEncryptionData(0)
+ , m_pItemEditorFactory(0)
+{
+ prepare();
+}
+
+UIEncryptionDataTable::~UIEncryptionDataTable()
+{
+ cleanup();
+}
+
+EncryptionPasswordMap UIEncryptionDataTable::encryptionPasswords() const
+{
+ AssertPtrReturn(m_pModelEncryptionData, EncryptionPasswordMap());
+ return m_pModelEncryptionData->encryptionPasswords();
+}
+
+void UIEncryptionDataTable::editFirstIndex()
+{
+ AssertPtrReturnVoid(m_pModelEncryptionData);
+ /* Compose the password field index of the first available table record: */
+ const QModelIndex index = m_pModelEncryptionData->index(0, UIEncryptionDataTableSection_Password);
+ /* Navigate table to the corresponding index: */
+ setCurrentIndex(index);
+ /* Compose the fake mouse-event which will trigger the embedded editor: */
+ QMouseEvent event(QEvent::MouseButtonPress, QPoint(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+ /* Initiate the embedded editor for the corresponding index: */
+ edit(index, QAbstractItemView::SelectedClicked, &event);
+}
+
+void UIEncryptionDataTable::prepare()
+{
+ /* Create encryption-data model: */
+ m_pModelEncryptionData = new UIEncryptionDataModel(this, m_encryptedMedia);
+ if (m_pModelEncryptionData)
+ {
+ /* Assign configured model to table: */
+ setModel(m_pModelEncryptionData);
+ }
+
+ /* Create item delegate: */
+ QIStyledItemDelegate *pStyledItemDelegate = new QIStyledItemDelegate(this);
+ if (pStyledItemDelegate)
+ {
+ /* Create new item editor factory: */
+ m_pItemEditorFactory = new QItemEditorFactory;
+ if (m_pItemEditorFactory)
+ {
+ /* Register UIPasswordEditor as the QString editor: */
+ QStandardItemEditorCreator<UIPasswordEditor> *pQStringItemEditorCreator = new QStandardItemEditorCreator<UIPasswordEditor>();
+ if (pQStringItemEditorCreator)
+ m_pItemEditorFactory->registerEditor(QVariant::String, pQStringItemEditorCreator);
+
+ /* Assign configured item editor factory to table delegate: */
+ pStyledItemDelegate->setItemEditorFactory(m_pItemEditorFactory);
+ }
+
+ /* Assign configured item delegate to table: */
+ delete itemDelegate();
+ setItemDelegate(pStyledItemDelegate);
+
+ /* Configure item delegate: */
+ pStyledItemDelegate->setWatchForEditorDataCommits(true);
+ pStyledItemDelegate->setWatchForEditorEnterKeyTriggering(true);
+ connect(pStyledItemDelegate, &QIStyledItemDelegate::sigEditorEnterKeyTriggered,
+ this, &UIEncryptionDataTable::sigEditorEnterKeyTriggered);
+ }
+
+ /* Configure table: */
+ setTabKeyNavigation(false);
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ setEditTriggers(QAbstractItemView::CurrentChanged | QAbstractItemView::SelectedClicked);
+
+ /* Configure headers: */
+ verticalHeader()->hide();
+ verticalHeader()->setDefaultSectionSize((int)(verticalHeader()->minimumSectionSize() * 1.33));
+ horizontalHeader()->setStretchLastSection(false);
+ horizontalHeader()->setSectionResizeMode(UIEncryptionDataTableSection_Id, QHeaderView::Interactive);
+ horizontalHeader()->setSectionResizeMode(UIEncryptionDataTableSection_Password, QHeaderView::Stretch);
+}
+
+void UIEncryptionDataTable::cleanup()
+{
+ /* Cleanup item editor factory: */
+ delete m_pItemEditorFactory;
+ m_pItemEditorFactory = 0;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIAddDiskEncryptionPasswordDialog implementation. *
+*********************************************************************************************************************************/
+
+UIAddDiskEncryptionPasswordDialog::UIAddDiskEncryptionPasswordDialog(QWidget *pParent,
+ const QString &strMachineName,
+ const EncryptedMediumMap &encryptedMedia)
+ : QIWithRetranslateUI<QDialog>(pParent)
+ , m_strMachineName(strMachineName)
+ , m_encryptedMedia(encryptedMedia)
+ , m_pLabelDescription(0)
+ , m_pTableEncryptionData(0)
+ , m_pButtonBox(0)
+{
+ /* Prepare: */
+ prepare();
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+EncryptionPasswordMap UIAddDiskEncryptionPasswordDialog::encryptionPasswords() const
+{
+ AssertPtrReturn(m_pTableEncryptionData, EncryptionPasswordMap());
+ return m_pTableEncryptionData->encryptionPasswords();
+}
+
+void UIAddDiskEncryptionPasswordDialog::retranslateUi()
+{
+ /* Translate the dialog title: */
+ setWindowTitle(tr("%1 - Disk Encryption").arg(m_strMachineName));
+
+ /* Translate the description label: */
+ AssertPtrReturnVoid(m_pLabelDescription);
+ m_pLabelDescription->setText(tr("This virtual machine is password protected. "
+ "Please enter the %n encryption password(s) below.",
+ "This text is never used with n == 0. "
+ "Feel free to drop the %n where possible, "
+ "we only included it because of problems with Qt Linguist "
+ "(but the user can see how many passwords are in the list "
+ "and doesn't need to be told).",
+ m_encryptedMedia.uniqueKeys().size()));
+}
+
+void UIAddDiskEncryptionPasswordDialog::accept()
+{
+ /* Validate passwords status: */
+ foreach (const QString &strPasswordId, m_encryptedMedia.uniqueKeys())
+ {
+ const QUuid uMediumId = m_encryptedMedia.values(strPasswordId).first();
+ const QString strPassword = m_pTableEncryptionData->encryptionPasswords().value(strPasswordId);
+ if (!isPasswordValid(uMediumId, strPassword))
+ {
+ UINotificationMessage::warnAboutInvalidEncryptionPassword(strPasswordId);
+ AssertPtrReturnVoid(m_pTableEncryptionData);
+ m_pTableEncryptionData->setFocus();
+ m_pTableEncryptionData->editFirstIndex();
+ return;
+ }
+ }
+ /* Call to base-class: */
+ QIWithRetranslateUI<QDialog>::accept();
+}
+
+void UIAddDiskEncryptionPasswordDialog::prepare()
+{
+ /* Configure self: */
+ setWindowModality(Qt::WindowModal);
+
+ /* Create main-layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Create input-layout: */
+ QVBoxLayout *pInputLayout = new QVBoxLayout;
+ if (pInputLayout)
+ {
+ /* Create description label: */
+ m_pLabelDescription = new QLabel;
+ if (m_pLabelDescription)
+ {
+ /* Add label into layout: */
+ pInputLayout->addWidget(m_pLabelDescription);
+ }
+
+ /* Create encryption-data table: */
+ m_pTableEncryptionData = new UIEncryptionDataTable(m_encryptedMedia);
+ if (m_pTableEncryptionData)
+ {
+ /* Configure encryption-data table: */
+ connect(m_pTableEncryptionData, &UIEncryptionDataTable::sigEditorEnterKeyTriggered,
+ this, &UIAddDiskEncryptionPasswordDialog::sltEditorEnterKeyTriggered);
+ m_pTableEncryptionData->setFocus();
+ m_pTableEncryptionData->editFirstIndex();
+ /* Add label into layout: */
+ pInputLayout->addWidget(m_pTableEncryptionData);
+ }
+
+ /* Add layout into parent: */
+ pMainLayout->addLayout(pInputLayout);
+ }
+
+ /* Create button-box: */
+ m_pButtonBox = new QIDialogButtonBox;
+ if (m_pButtonBox)
+ {
+ /* Configure button-box: */
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UIAddDiskEncryptionPasswordDialog::accept);
+ connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UIAddDiskEncryptionPasswordDialog::reject);
+
+ /* Add button-box into layout: */
+ pMainLayout->addWidget(m_pButtonBox);
+ }
+ }
+}
+
+/* static */
+bool UIAddDiskEncryptionPasswordDialog::isPasswordValid(const QUuid &uMediumId, const QString strPassword)
+{
+ /* Look for the medium with passed ID: */
+ const UIMedium uimedium = uiCommon().medium(uMediumId);
+ if (!uimedium.isNull())
+ {
+ /* Check wrapped medium for validity: */
+ const CMedium medium = uimedium.medium();
+ if (!medium.isNull())
+ {
+ /* Check whether the password is suitable for that medium: */
+ medium.CheckEncryptionPassword(strPassword);
+ return medium.isOk();
+ }
+ }
+ /* False by default: */
+ return false;
+}
+
+
+#include "UIAddDiskEncryptionPasswordDialog.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIAddDiskEncryptionPasswordDialog.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIAddDiskEncryptionPasswordDialog.h
new file mode 100644
index 00000000..46dece01
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIAddDiskEncryptionPasswordDialog.h
@@ -0,0 +1,107 @@
+/* $Id: UIAddDiskEncryptionPasswordDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIAddDiskEncryptionPasswordDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIAddDiskEncryptionPasswordDialog_h
+#define FEQT_INCLUDED_SRC_widgets_UIAddDiskEncryptionPasswordDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QDialog>
+#include <QMap>
+#include <QMultiMap>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class QIDialogButtonBox;
+class UIEncryptionDataTable;
+
+/* Type definitions: */
+typedef QMultiMap<QString, QUuid> EncryptedMediumMap;
+typedef QMap<QString, QString> EncryptionPasswordMap;
+typedef QMap<QString, bool> EncryptionPasswordStatusMap;
+
+/** QDialog subclass used to
+ * allow the user to enter disk encryption passwords for particular password IDs. */
+class SHARED_LIBRARY_STUFF UIAddDiskEncryptionPasswordDialog : public QIWithRetranslateUI<QDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs dialog passing @a pParent to the base-class.
+ * @param strMachineName Brings the name of the machine we show this dialog for.
+ * @param encryptedMedia Brings the lists of medium ids (values) encrypted with passwords with ids (keys). */
+ UIAddDiskEncryptionPasswordDialog(QWidget *pParent, const QString &strMachineName, const EncryptedMediumMap &encryptedMedia);
+
+ /** Returns the shallow copy of the encryption password map
+ * acquired from the UIEncryptionDataTable instance. */
+ EncryptionPasswordMap encryptionPasswords() const;
+
+protected:
+
+ /** Translation routine. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles editor's Enter/Return key triggering. */
+ void sltEditorEnterKeyTriggered() { accept(); }
+
+ /** Performs passwords validation.
+ * If all passwords are valid,
+ * this slot calls to base-class. */
+ void accept();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Returns whether passed @a strPassword is valid for medium with passed @a uMediumId. */
+ static bool isPasswordValid(const QUuid &uMediumId, const QString strPassword);
+
+ /** Holds the name of the machine we show this dialog for. */
+ const QString m_strMachineName;
+
+ /** Holds the encrypted medium map reference. */
+ const EncryptedMediumMap &m_encryptedMedia;
+
+ /** Holds the description label instance. */
+ QLabel *m_pLabelDescription;
+ /** Holds the encryption-data table instance. */
+ UIEncryptionDataTable *m_pTableEncryptionData;
+ /** Holds the button-box instance. */
+ QIDialogButtonBox *m_pButtonBox;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIAddDiskEncryptionPasswordDialog_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceEditorWidget.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceEditorWidget.cpp
new file mode 100644
index 00000000..6050b48d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceEditorWidget.cpp
@@ -0,0 +1,1937 @@
+/* $Id: UIApplianceEditorWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIApplianceEditorWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDir>
+#include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QRegExp>
+#include <QSpinBox>
+#include <QTextEdit>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QITreeView.h"
+#include "UICommon.h"
+#include "UIGuestOSTypeSelectionButton.h"
+#include "UIApplianceEditorWidget.h"
+#include "UIConverter.h"
+#include "UIFilePathSelector.h"
+#include "UIIconPool.h"
+#include "UILineTextEdit.h"
+#include "UIMessageCenter.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+/** Describes the interface of Appliance item.
+ * Represented as a tree structure with a parent & multiple children. */
+class UIApplianceModelItem : public QITreeViewItem
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs root item with specified @a iNumber, @a enmType and @a pParent. */
+ UIApplianceModelItem(int iNumber, ApplianceModelItemType enmType, QITreeView *pParent);
+ /** Constructs non-root item with specified @a iNumber, @a enmType and @a pParentItem. */
+ UIApplianceModelItem(int iNumber, ApplianceModelItemType enmType, UIApplianceModelItem *pParentItem);
+ /** Destructs item. */
+ virtual ~UIApplianceModelItem();
+
+ /** Returns the item type. */
+ ApplianceModelItemType type() const { return m_enmType; }
+
+ /** Returns the parent of the item. */
+ UIApplianceModelItem *parent() const { return m_pParentItem; }
+
+ /** Appends the passed @a pChildItem to the item's list of children. */
+ void appendChild(UIApplianceModelItem *pChildItem);
+ /** Returns the child specified by the @a iIndex. */
+ virtual UIApplianceModelItem *childItem(int iIndex) const RT_OVERRIDE;
+
+ /** Returns the row of the item in the parent. */
+ int row() const;
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the number of columns. */
+ int columnCount() const { return 3; }
+
+ /** Returns the item text. */
+ virtual QString text() const RT_OVERRIDE;
+
+ /** Returns the item flags for the given @a iColumn. */
+ virtual Qt::ItemFlags itemFlags(int /* iColumn */) const { return Qt::ItemFlags(); }
+
+ /** Defines the @a iRole data for the item at @a iColumn to @a value. */
+ virtual bool setData(int /* iColumn */, const QVariant & /* value */, int /* iRole */) { return false; }
+ /** Returns the data stored under the given @a iRole for the item referred to by the @a iColumn. */
+ virtual QVariant data(int /* iColumn */, int /* iRole */) const { return QVariant(); }
+
+ /** Returns the widget used to edit the item specified by @a idx for editing.
+ * @param pParent Brings the parent to be assigned for newly created editor.
+ * @param styleOption Bring the style option set for the newly created editor. */
+ virtual QWidget *createEditor(QWidget * /* pParent */, const QStyleOptionViewItem & /* styleOption */, const QModelIndex & /* idx */) const { return 0; }
+
+ /** Defines the contents of the given @a pEditor to the data for the item at the given @a idx. */
+ virtual bool setEditorData(QWidget * /* pEditor */, const QModelIndex & /* idx */) const { return false; }
+ /** Defines the data for the item at the given @a idx in the @a pModel to the contents of the given @a pEditor. */
+ virtual bool setModelData(QWidget * /* pEditor */, QAbstractItemModel * /* pModel */, const QModelIndex & /* idx */) { return false; }
+
+ /** Restores the default values. */
+ virtual void restoreDefaults() {}
+
+ /** Cache currently stored values, such as @a finalStates, @a finalValues and @a finalExtraValues. */
+ virtual void putBack(QVector<BOOL> &finalStates, QVector<QString> &finalValues, QVector<QString> &finalExtraValues);
+
+protected:
+
+ /** Holds the item number. */
+ int m_iNumber;
+ /** Holds the item type. */
+ ApplianceModelItemType m_enmType;
+
+ /** Holds the parent item reference. */
+ UIApplianceModelItem *m_pParentItem;
+ /** Holds the list of children item instances. */
+ QList<UIApplianceModelItem*> m_childItems;
+};
+
+
+/** UIApplianceModelItem subclass representing Appliance Virtual System item. */
+class UIVirtualSystemItem : public UIApplianceModelItem
+{
+public:
+
+ /** Constructs item passing @a iNumber and @a pParentItem to the base-class.
+ * @param comDescription Brings the Virtual System Description. */
+ UIVirtualSystemItem(int iNumber, CVirtualSystemDescription comDescription, UIApplianceModelItem *pParentItem);
+
+ /** Returns the data stored under the given @a iRole for the item referred to by the @a iColumn. */
+ virtual QVariant data(int iColumn, int iRole) const RT_OVERRIDE;
+
+ /** Cache currently stored values, such as @a finalStates, @a finalValues and @a finalExtraValues. */
+ virtual void putBack(QVector<BOOL> &finalStates, QVector<QString> &finalValues, QVector<QString> &finalExtraValues) RT_OVERRIDE;
+
+private:
+
+ /** Holds the Virtual System Description. */
+ CVirtualSystemDescription m_comDescription;
+};
+
+
+/** UIApplianceModelItem subclass representing Appliance Virtual Hardware item. */
+class UIVirtualHardwareItem : public UIApplianceModelItem
+{
+ friend class UIApplianceSortProxyModel;
+
+ /** Data roles. */
+ enum
+ {
+ TypeRole = Qt::UserRole,
+ ModifiedRole
+ };
+
+public:
+
+ /** Constructs item passing @a iNumber and @a pParentItem to the base-class.
+ * @param pParent Brings the parent reference.
+ * @param enmVSDType Brings the Virtual System Description type.
+ * @param strRef Brings something totally useless.
+ * @param strOrigValue Brings the original value.
+ * @param strConfigValue Brings the configuration value.
+ * @param strExtraConfigValue Brings the extra configuration value. */
+ UIVirtualHardwareItem(UIApplianceModel *pParent,
+ int iNumber,
+ KVirtualSystemDescriptionType enmVSDType,
+ const QString &strRef,
+ const QString &strOrigValue,
+ const QString &strConfigValue,
+ const QString &strExtraConfigValue,
+ UIApplianceModelItem *pParentItem);
+
+ /** Returns the item flags for the given @a iColumn. */
+ virtual Qt::ItemFlags itemFlags(int iColumn) const RT_OVERRIDE;
+
+ /** Defines the @a iRole data for the item at @a iColumn to @a value. */
+ virtual bool setData(int iColumn, const QVariant &value, int iRole) RT_OVERRIDE;
+ /** Returns the data stored under the given @a iRole for the item referred to by the @a iColumn. */
+ virtual QVariant data(int iColumn, int iRole) const RT_OVERRIDE;
+
+ /** Returns the widget used to edit the item specified by @a idx for editing.
+ * @param pParent Brings the parent to be assigned for newly created editor.
+ * @param styleOption Bring the style option set for the newly created editor. */
+ virtual QWidget *createEditor(QWidget *pParent, const QStyleOptionViewItem &styleOption, const QModelIndex &idx) const RT_OVERRIDE;
+
+ /** Defines the contents of the given @a pEditor to the data for the item at the given @a idx. */
+ virtual bool setEditorData(QWidget *pEditor, const QModelIndex &idx) const RT_OVERRIDE;
+ /** Defines the data for the item at the given @a idx in the @a pModel to the contents of the given @a pEditor. */
+ virtual bool setModelData(QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex &idx) RT_OVERRIDE;
+
+ /** Restores the default values. */
+ virtual void restoreDefaults() RT_OVERRIDE;
+
+ /** Cache currently stored values, such as @a finalStates, @a finalValues and @a finalExtraValues. */
+ virtual void putBack(QVector<BOOL> &finalStates, QVector<QString> &finalValues, QVector<QString> &finalExtraValues) RT_OVERRIDE;
+
+ KVirtualSystemDescriptionType systemDescriptionType() const;
+
+private:
+
+ /** Holds the parent reference. */
+ UIApplianceModel *m_pParent;
+
+ /** Holds the Virtual System Description type. */
+ KVirtualSystemDescriptionType m_enmVSDType;
+ /** Holds something totally useless. */
+ QString m_strRef;
+ /** Holds the original value. */
+ QString m_strOrigValue;
+ /** Holds the configuration value. */
+ QString m_strConfigValue;
+ /** Holds the default configuration value. */
+ QString m_strConfigDefaultValue;
+ /** Holds the extra configuration value. */
+ QString m_strExtraConfigValue;
+ /** Holds the item check state. */
+ Qt::CheckState m_checkState;
+ /** Holds whether item was modified. */
+ bool m_fModified;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIApplianceModelItem implementation. *
+*********************************************************************************************************************************/
+
+UIApplianceModelItem::UIApplianceModelItem(int iNumber, ApplianceModelItemType enmType, QITreeView *pParent)
+ : QITreeViewItem(pParent)
+ , m_iNumber(iNumber)
+ , m_enmType(enmType)
+ , m_pParentItem(0)
+{
+}
+
+UIApplianceModelItem::UIApplianceModelItem(int iNumber, ApplianceModelItemType enmType, UIApplianceModelItem *pParentItem)
+ : QITreeViewItem(pParentItem)
+ , m_iNumber(iNumber)
+ , m_enmType(enmType)
+ , m_pParentItem(pParentItem)
+{
+}
+
+UIApplianceModelItem::~UIApplianceModelItem()
+{
+ qDeleteAll(m_childItems);
+}
+
+void UIApplianceModelItem::appendChild(UIApplianceModelItem *pChildItem)
+{
+ AssertPtr(pChildItem);
+ m_childItems << pChildItem;
+}
+
+UIApplianceModelItem *UIApplianceModelItem::childItem(int iIndex) const
+{
+ return m_childItems.value(iIndex);
+}
+
+int UIApplianceModelItem::row() const
+{
+ if (m_pParentItem)
+ return m_pParentItem->m_childItems.indexOf(const_cast<UIApplianceModelItem*>(this));
+
+ return 0;
+}
+
+int UIApplianceModelItem::childCount() const
+{
+ return m_childItems.count();
+}
+
+QString UIApplianceModelItem::text() const
+{
+ switch (type())
+ {
+ case ApplianceModelItemType_VirtualSystem:
+ return tr("%1", "col.1 text")
+ .arg(data(ApplianceViewSection_Description, Qt::DisplayRole).toString());
+ case ApplianceModelItemType_VirtualHardware:
+ return tr("%1: %2", "col.1 text: col.2 text")
+ .arg(data(ApplianceViewSection_Description, Qt::DisplayRole).toString())
+ .arg(data(ApplianceViewSection_ConfigValue, Qt::DisplayRole).toString());
+ default:
+ break;
+ }
+ return QString();
+}
+
+void UIApplianceModelItem::putBack(QVector<BOOL> &finalStates, QVector<QString> &finalValues, QVector<QString> &finalExtraValues)
+{
+ for (int i = 0; i < childCount(); ++i)
+ childItem(i)->putBack(finalStates, finalValues, finalExtraValues);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualSystemItem implementation. *
+*********************************************************************************************************************************/
+
+UIVirtualSystemItem::UIVirtualSystemItem(int iNumber, CVirtualSystemDescription comDescription, UIApplianceModelItem *pParentItem)
+ : UIApplianceModelItem(iNumber, ApplianceModelItemType_VirtualSystem, pParentItem)
+ , m_comDescription(comDescription)
+{
+}
+
+QVariant UIVirtualSystemItem::data(int iColumn, int iRole) const
+{
+ QVariant value;
+ if (iColumn == ApplianceViewSection_Description &&
+ iRole == Qt::DisplayRole)
+ value = UIApplianceEditorWidget::tr("Virtual System %1").arg(m_iNumber + 1);
+ return value;
+}
+
+void UIVirtualSystemItem::putBack(QVector<BOOL> &finalStates, QVector<QString> &finalValues, QVector<QString> &finalExtraValues)
+{
+ /* Resize the vectors */
+ unsigned long iCount = m_comDescription.GetCount();
+ AssertReturnVoid(iCount > 0);
+ finalStates.resize(iCount);
+ finalValues.resize(iCount);
+ finalExtraValues.resize(iCount);
+ /* Recursively fill the vectors */
+ UIApplianceModelItem::putBack(finalStates, finalValues, finalExtraValues);
+ /* Set all final values at once */
+ m_comDescription.SetFinalValues(finalStates, finalValues, finalExtraValues);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIVirtualHardwareItem implementation. *
+*********************************************************************************************************************************/
+
+UIVirtualHardwareItem::UIVirtualHardwareItem(UIApplianceModel *pParent,
+ int iNumber,
+ KVirtualSystemDescriptionType enmVSDType,
+ const QString &strRef,
+ const QString &strOrigValue,
+ const QString &strConfigValue,
+ const QString &strExtraConfigValue,
+ UIApplianceModelItem *pParentItem)
+ : UIApplianceModelItem(iNumber, ApplianceModelItemType_VirtualHardware, pParentItem)
+ , m_pParent(pParent)
+ , m_enmVSDType(enmVSDType)
+ , m_strRef(strRef)
+ , m_strOrigValue(enmVSDType == KVirtualSystemDescriptionType_Memory ? UITranslator::byteStringToMegaByteString(strOrigValue) : strOrigValue)
+ , m_strConfigValue(enmVSDType == KVirtualSystemDescriptionType_Memory ? UITranslator::byteStringToMegaByteString(strConfigValue) : strConfigValue)
+ , m_strConfigDefaultValue(strConfigValue)
+ , m_strExtraConfigValue(enmVSDType == KVirtualSystemDescriptionType_Memory ? UITranslator::byteStringToMegaByteString(strExtraConfigValue) : strExtraConfigValue)
+ , m_checkState(Qt::Checked)
+ , m_fModified(false)
+{
+}
+
+Qt::ItemFlags UIVirtualHardwareItem::itemFlags(int iColumn) const
+{
+ Qt::ItemFlags enmFlags = Qt::ItemFlags();
+ if (iColumn == ApplianceViewSection_ConfigValue)
+ {
+ /* Some items are checkable */
+ if (m_enmVSDType == KVirtualSystemDescriptionType_Floppy ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CDROM ||
+ m_enmVSDType == KVirtualSystemDescriptionType_USBController ||
+ m_enmVSDType == KVirtualSystemDescriptionType_SoundCard ||
+ m_enmVSDType == KVirtualSystemDescriptionType_NetworkAdapter ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CloudPublicIP ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CloudKeepObject ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CloudLaunchInstance)
+ enmFlags |= Qt::ItemIsUserCheckable;
+ /* Some items are editable */
+ if ((m_enmVSDType == KVirtualSystemDescriptionType_Name ||
+ m_enmVSDType == KVirtualSystemDescriptionType_Product ||
+ m_enmVSDType == KVirtualSystemDescriptionType_ProductUrl ||
+ m_enmVSDType == KVirtualSystemDescriptionType_Vendor ||
+ m_enmVSDType == KVirtualSystemDescriptionType_VendorUrl ||
+ m_enmVSDType == KVirtualSystemDescriptionType_Version ||
+ m_enmVSDType == KVirtualSystemDescriptionType_Description ||
+ m_enmVSDType == KVirtualSystemDescriptionType_License ||
+ m_enmVSDType == KVirtualSystemDescriptionType_OS ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CPU ||
+ m_enmVSDType == KVirtualSystemDescriptionType_Memory ||
+ m_enmVSDType == KVirtualSystemDescriptionType_SoundCard ||
+ m_enmVSDType == KVirtualSystemDescriptionType_NetworkAdapter ||
+ m_enmVSDType == KVirtualSystemDescriptionType_HardDiskControllerIDE ||
+ m_enmVSDType == KVirtualSystemDescriptionType_HardDiskImage ||
+ m_enmVSDType == KVirtualSystemDescriptionType_SettingsFile ||
+ m_enmVSDType == KVirtualSystemDescriptionType_BaseFolder ||
+ m_enmVSDType == KVirtualSystemDescriptionType_PrimaryGroup ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CloudInstanceShape ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CloudDomain ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CloudBootDiskSize ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CloudBucket ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CloudOCIVCN ||
+ m_enmVSDType == KVirtualSystemDescriptionType_CloudOCISubnet) &&
+ m_checkState == Qt::Checked) /* Item has to be enabled */
+ enmFlags |= Qt::ItemIsEditable;
+ }
+ return enmFlags;
+}
+
+bool UIVirtualHardwareItem::setData(int iColumn, const QVariant &value, int iRole)
+{
+ bool fDone = false;
+ switch (iRole)
+ {
+ case Qt::CheckStateRole:
+ {
+ if (iColumn == ApplianceViewSection_ConfigValue)
+ {
+ switch (m_enmVSDType)
+ {
+ /* These hardware items can be disabled: */
+ case KVirtualSystemDescriptionType_Floppy:
+ case KVirtualSystemDescriptionType_CDROM:
+ case KVirtualSystemDescriptionType_USBController:
+ case KVirtualSystemDescriptionType_SoundCard:
+ case KVirtualSystemDescriptionType_NetworkAdapter:
+ {
+ m_checkState = static_cast<Qt::CheckState>(value.toInt());
+ fDone = true;
+ break;
+ }
+ /* These option items can be enabled: */
+ case KVirtualSystemDescriptionType_CloudPublicIP:
+ case KVirtualSystemDescriptionType_CloudKeepObject:
+ case KVirtualSystemDescriptionType_CloudLaunchInstance:
+ {
+ if (value.toInt() == Qt::Unchecked)
+ m_strConfigValue = "false";
+ else if (value.toInt() == Qt::Checked)
+ m_strConfigValue = "true";
+ fDone = true;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case Qt::EditRole:
+ {
+ if (iColumn == ApplianceViewSection_OriginalValue)
+ m_strOrigValue = value.toString();
+ else if (iColumn == ApplianceViewSection_ConfigValue)
+ m_strConfigValue = value.toString();
+ break;
+ }
+ default: break;
+ }
+ return fDone;
+}
+
+QVariant UIVirtualHardwareItem::data(int iColumn, int iRole) const
+{
+ QVariant value;
+ switch (iRole)
+ {
+ case Qt::EditRole:
+ {
+ if (iColumn == ApplianceViewSection_OriginalValue)
+ value = m_strOrigValue;
+ else if (iColumn == ApplianceViewSection_ConfigValue)
+ value = m_strConfigValue;
+ break;
+ }
+ case Qt::DisplayRole:
+ {
+ if (iColumn == ApplianceViewSection_Description)
+ {
+ switch (m_enmVSDType)
+ {
+ case KVirtualSystemDescriptionType_Name: value = UIApplianceEditorWidget::tr("Name"); break;
+ case KVirtualSystemDescriptionType_Product: value = UIApplianceEditorWidget::tr("Product"); break;
+ case KVirtualSystemDescriptionType_ProductUrl: value = UIApplianceEditorWidget::tr("Product-URL"); break;
+ case KVirtualSystemDescriptionType_Vendor: value = UIApplianceEditorWidget::tr("Vendor"); break;
+ case KVirtualSystemDescriptionType_VendorUrl: value = UIApplianceEditorWidget::tr("Vendor-URL"); break;
+ case KVirtualSystemDescriptionType_Version: value = UIApplianceEditorWidget::tr("Version"); break;
+ case KVirtualSystemDescriptionType_Description: value = UIApplianceEditorWidget::tr("Description"); break;
+ case KVirtualSystemDescriptionType_License: value = UIApplianceEditorWidget::tr("License"); break;
+ case KVirtualSystemDescriptionType_OS: value = UIApplianceEditorWidget::tr("Guest OS Type"); break;
+ case KVirtualSystemDescriptionType_CPU: value = UIApplianceEditorWidget::tr("CPU"); break;
+ case KVirtualSystemDescriptionType_Memory: value = UIApplianceEditorWidget::tr("RAM"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerIDE: value = UIApplianceEditorWidget::tr("Storage Controller (IDE)"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerSATA: value = UIApplianceEditorWidget::tr("Storage Controller (SATA)"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerSCSI: value = UIApplianceEditorWidget::tr("Storage Controller (SCSI)"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerVirtioSCSI: value = UIApplianceEditorWidget::tr("Storage Controller (VirtioSCSI)"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerSAS: value = UIApplianceEditorWidget::tr("Storage Controller (SAS)"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerNVMe: value = UIApplianceEditorWidget::tr("Storage Controller (NVMe)"); break;
+ case KVirtualSystemDescriptionType_CDROM: value = UIApplianceEditorWidget::tr("DVD"); break;
+ case KVirtualSystemDescriptionType_Floppy: value = UIApplianceEditorWidget::tr("Floppy"); break;
+ case KVirtualSystemDescriptionType_NetworkAdapter: value = UIApplianceEditorWidget::tr("Network Adapter"); break;
+ case KVirtualSystemDescriptionType_USBController: value = UIApplianceEditorWidget::tr("USB Controller"); break;
+ case KVirtualSystemDescriptionType_SoundCard: value = UIApplianceEditorWidget::tr("Sound Card"); break;
+ case KVirtualSystemDescriptionType_HardDiskImage: value = UIApplianceEditorWidget::tr("Virtual Disk Image"); break;
+ case KVirtualSystemDescriptionType_SettingsFile: value = UIApplianceEditorWidget::tr("Settings File"); break;
+ case KVirtualSystemDescriptionType_BaseFolder: value = UIApplianceEditorWidget::tr("Base Folder"); break;
+ case KVirtualSystemDescriptionType_PrimaryGroup: value = UIApplianceEditorWidget::tr("Primary Group"); break;
+ case KVirtualSystemDescriptionType_CloudProfileName:
+ case KVirtualSystemDescriptionType_CloudInstanceShape:
+ case KVirtualSystemDescriptionType_CloudDomain:
+ case KVirtualSystemDescriptionType_CloudBootDiskSize:
+ case KVirtualSystemDescriptionType_CloudBucket:
+ case KVirtualSystemDescriptionType_CloudOCIVCN:
+ case KVirtualSystemDescriptionType_CloudOCISubnet:
+ case KVirtualSystemDescriptionType_CloudPublicIP:
+ case KVirtualSystemDescriptionType_CloudKeepObject:
+ case KVirtualSystemDescriptionType_CloudLaunchInstance: value = UIApplianceEditorWidget::tr(m_pParent->nameHint(m_enmVSDType).toUtf8().constData()); break;
+ default: value = UIApplianceEditorWidget::tr("Unknown Hardware Item"); break;
+ }
+ }
+ else if (iColumn == ApplianceViewSection_OriginalValue)
+ value = m_strOrigValue;
+ else if (iColumn == ApplianceViewSection_ConfigValue)
+ {
+ switch (m_enmVSDType)
+ {
+ case KVirtualSystemDescriptionType_Description:
+ case KVirtualSystemDescriptionType_License:
+ {
+ /* Shorten the big text if there is more than
+ * one line */
+ QString strTmp(m_strConfigValue);
+ int i = strTmp.indexOf('\n');
+ if (i > -1)
+ strTmp.replace(i, strTmp.length(), "...");
+ value = strTmp; break;
+ }
+ case KVirtualSystemDescriptionType_OS: value = uiCommon().vmGuestOSTypeDescription(m_strConfigValue); break;
+ case KVirtualSystemDescriptionType_Memory: value = m_strConfigValue + " " + UICommon::tr("MB", "size suffix MBytes=1024 KBytes"); break;
+ case KVirtualSystemDescriptionType_SoundCard: value = gpConverter->toString(static_cast<KAudioControllerType>(m_strConfigValue.toInt())); break;
+ case KVirtualSystemDescriptionType_NetworkAdapter: value = gpConverter->toString(static_cast<KNetworkAdapterType>(m_strConfigValue.toInt())); break;
+ case KVirtualSystemDescriptionType_CloudInstanceShape:
+ case KVirtualSystemDescriptionType_CloudDomain:
+ case KVirtualSystemDescriptionType_CloudBootDiskSize:
+ case KVirtualSystemDescriptionType_CloudBucket:
+ case KVirtualSystemDescriptionType_CloudOCIVCN:
+ case KVirtualSystemDescriptionType_CloudOCISubnet:
+ {
+ /* Get VSD type hint and check which kind of data it is.
+ * These VSD types can have masks if represented by arrays. */
+ const QVariant get = m_pParent->getHint(m_enmVSDType);
+ switch (m_pParent->kindHint(m_enmVSDType))
+ {
+ case ParameterKind_Array:
+ {
+ QString strMask;
+ AbstractVSDParameterArray array = get.value<AbstractVSDParameterArray>();
+ /* Every array member is a complex value, - string pair,
+ * "first" is always present while "second" can be null. */
+ foreach (const QIStringPair &pair, array.values)
+ {
+ /* If "second" isn't null & equal to m_strConfigValue => return "first": */
+ if (!pair.second.isNull() && pair.second == m_strConfigValue)
+ {
+ strMask = pair.first;
+ break;
+ }
+ }
+ /* Use mask if found, m_strConfigValue otherwise: */
+ value = strMask.isNull() ? m_strConfigValue : strMask;
+ break;
+ }
+ default:
+ {
+ value = m_strConfigValue;
+ break;
+ }
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_CloudPublicIP: break;
+ case KVirtualSystemDescriptionType_CloudKeepObject: break;
+ case KVirtualSystemDescriptionType_CloudLaunchInstance: break;
+ default: value = m_strConfigValue; break;
+ }
+ }
+ break;
+ }
+ case Qt::ToolTipRole:
+ {
+ if (iColumn == ApplianceViewSection_ConfigValue)
+ {
+ if (!m_strOrigValue.isEmpty())
+ {
+ /* Prepare tool-tip pattern/body: */
+ const QString strToolTipPattern = UIApplianceEditorWidget::tr("<b>Original Value:</b> %1");
+ QString strToolTipBody;
+
+ /* Handle certain VSD types separately: */
+ switch (m_enmVSDType)
+ {
+ case KVirtualSystemDescriptionType_CloudInstanceShape:
+ case KVirtualSystemDescriptionType_CloudDomain:
+ case KVirtualSystemDescriptionType_CloudBootDiskSize:
+ case KVirtualSystemDescriptionType_CloudBucket:
+ case KVirtualSystemDescriptionType_CloudOCIVCN:
+ case KVirtualSystemDescriptionType_CloudOCISubnet:
+ {
+ /* Get VSD type hint and check which kind of data it is.
+ * These VSD types can have masks if represented by arrays. */
+ const QVariant get = m_pParent->getHint(m_enmVSDType);
+ switch (m_pParent->kindHint(m_enmVSDType))
+ {
+ case ParameterKind_Array:
+ {
+ QString strMask;
+ AbstractVSDParameterArray array = get.value<AbstractVSDParameterArray>();
+ /* Every array member is a complex value, - string pair,
+ * "first" is always present while "second" can be null. */
+ foreach (const QIStringPair &pair, array.values)
+ {
+ /* If "second" isn't null & equal to m_strOrigValue => return "first": */
+ if (!pair.second.isNull() && pair.second == m_strOrigValue)
+ {
+ strMask = pair.first;
+ break;
+ }
+ }
+ /* Use mask if found: */
+ if (!strMask.isNull())
+ strToolTipBody = strMask;
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Make sure we have at least something: */
+ if (strToolTipBody.isNull())
+ strToolTipBody = m_strOrigValue;
+ /* Compose tool-tip finally: */
+ value = strToolTipPattern.arg(strToolTipBody);
+ }
+ }
+ break;
+ }
+ case Qt::DecorationRole:
+ {
+ if (iColumn == ApplianceViewSection_Description)
+ {
+ switch (m_enmVSDType)
+ {
+ case KVirtualSystemDescriptionType_Name: value = UIIconPool::iconSet(":/name_16px.png"); break;
+ case KVirtualSystemDescriptionType_Product:
+ case KVirtualSystemDescriptionType_ProductUrl:
+ case KVirtualSystemDescriptionType_Vendor:
+ case KVirtualSystemDescriptionType_VendorUrl:
+ case KVirtualSystemDescriptionType_Version:
+ case KVirtualSystemDescriptionType_Description:
+ case KVirtualSystemDescriptionType_License: value = UIIconPool::iconSet(":/description_16px.png"); break;
+ case KVirtualSystemDescriptionType_OS: value = UIIconPool::iconSet(":/system_type_16px.png"); break;
+ case KVirtualSystemDescriptionType_CPU: value = UIIconPool::iconSet(":/cpu_16px.png"); break;
+ case KVirtualSystemDescriptionType_Memory: value = UIIconPool::iconSet(":/ram_16px.png"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerIDE: value = UIIconPool::iconSet(":/ide_16px.png"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerSATA: value = UIIconPool::iconSet(":/sata_16px.png"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerSCSI: value = UIIconPool::iconSet(":/scsi_16px.png"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerVirtioSCSI: value = UIIconPool::iconSet(":/virtio_scsi_16px.png"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerSAS: value = UIIconPool::iconSet(":/sas_16px.png"); break;
+ case KVirtualSystemDescriptionType_HardDiskControllerNVMe: value = UIIconPool::iconSet(":/pcie_16px.png"); break;
+ case KVirtualSystemDescriptionType_HardDiskImage: value = UIIconPool::iconSet(":/hd_16px.png"); break;
+ case KVirtualSystemDescriptionType_CDROM: value = UIIconPool::iconSet(":/cd_16px.png"); break;
+ case KVirtualSystemDescriptionType_Floppy: value = UIIconPool::iconSet(":/fd_16px.png"); break;
+ case KVirtualSystemDescriptionType_NetworkAdapter: value = UIIconPool::iconSet(":/nw_16px.png"); break;
+ case KVirtualSystemDescriptionType_USBController: value = UIIconPool::iconSet(":/usb_16px.png"); break;
+ case KVirtualSystemDescriptionType_SoundCard: value = UIIconPool::iconSet(":/sound_16px.png"); break;
+ case KVirtualSystemDescriptionType_BaseFolder: value = generalIconPool().defaultSystemIcon(QFileIconProvider::Folder); break;
+ case KVirtualSystemDescriptionType_PrimaryGroup: value = UIIconPool::iconSet(":/vm_group_name_16px.png"); break;
+ case KVirtualSystemDescriptionType_CloudProfileName:
+ case KVirtualSystemDescriptionType_CloudInstanceShape:
+ case KVirtualSystemDescriptionType_CloudDomain:
+ case KVirtualSystemDescriptionType_CloudBootDiskSize:
+ case KVirtualSystemDescriptionType_CloudBucket:
+ case KVirtualSystemDescriptionType_CloudOCIVCN:
+ case KVirtualSystemDescriptionType_CloudOCISubnet:
+ case KVirtualSystemDescriptionType_CloudPublicIP:
+ case KVirtualSystemDescriptionType_CloudKeepObject:
+ case KVirtualSystemDescriptionType_CloudLaunchInstance: value = UIIconPool::iconSet(":/session_info_16px.png"); break;
+ default: break;
+ }
+ }
+ else if (iColumn == ApplianceViewSection_ConfigValue && m_enmVSDType == KVirtualSystemDescriptionType_OS)
+ value = generalIconPool().guestOSTypeIcon(m_strConfigValue);
+ break;
+ }
+ case Qt::FontRole:
+ {
+ /* If the item is unchecked mark it with italic text. */
+ if (iColumn == ApplianceViewSection_ConfigValue &&
+ m_checkState == Qt::Unchecked)
+ {
+ QFont font = qApp->font();
+ font.setItalic(true);
+ value = font;
+ }
+ break;
+ }
+ case Qt::ForegroundRole:
+ {
+ /* If the item is unchecked mark it with gray text. */
+ if (iColumn == ApplianceViewSection_ConfigValue &&
+ m_checkState == Qt::Unchecked)
+ {
+ QPalette pal = qApp->palette();
+ value = pal.brush(QPalette::Disabled, QPalette::WindowText);
+ }
+ break;
+ }
+ case Qt::CheckStateRole:
+ {
+ if (iColumn == ApplianceViewSection_ConfigValue)
+ {
+ switch (m_enmVSDType)
+ {
+ /* These hardware items can be disabled: */
+ case KVirtualSystemDescriptionType_Floppy:
+ case KVirtualSystemDescriptionType_CDROM:
+ case KVirtualSystemDescriptionType_USBController:
+ case KVirtualSystemDescriptionType_SoundCard:
+ case KVirtualSystemDescriptionType_NetworkAdapter:
+ {
+ value = m_checkState;
+ break;
+ }
+ /* These option items can be enabled: */
+ case KVirtualSystemDescriptionType_CloudPublicIP:
+ case KVirtualSystemDescriptionType_CloudKeepObject:
+ case KVirtualSystemDescriptionType_CloudLaunchInstance:
+ {
+ if (m_strConfigValue == "true")
+ value = Qt::Checked;
+ else
+ value = Qt::Unchecked;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case UIVirtualHardwareItem::TypeRole:
+ {
+ value = m_enmVSDType;
+ break;
+ }
+ case UIVirtualHardwareItem::ModifiedRole:
+ {
+ if (iColumn == ApplianceViewSection_ConfigValue)
+ value = m_fModified;
+ break;
+ }
+ }
+ return value;
+}
+
+QWidget *UIVirtualHardwareItem::createEditor(QWidget *pParent, const QStyleOptionViewItem & /* styleOption */, const QModelIndex &idx) const
+{
+ QWidget *pEditor = 0;
+ if (idx.column() == ApplianceViewSection_ConfigValue)
+ {
+ switch (m_enmVSDType)
+ {
+ case KVirtualSystemDescriptionType_OS:
+ {
+ UIGuestOSTypeSelectionButton *pButton = new UIGuestOSTypeSelectionButton(pParent);
+ /* Fill the background with the highlight color in the case
+ * the button hasn't a rectangle shape. This prevents the
+ * display of parts from the current text on the Mac. */
+#ifdef VBOX_WS_MAC
+ /* Use the palette from the tree view, not the one from the
+ * editor. */
+ QPalette p = pButton->palette();
+ p.setBrush(QPalette::Highlight, pParent->palette().brush(QPalette::Highlight));
+ pButton->setPalette(p);
+#endif /* VBOX_WS_MAC */
+ pButton->setAutoFillBackground(true);
+ pButton->setBackgroundRole(QPalette::Highlight);
+ pEditor = pButton;
+ break;
+ }
+ case KVirtualSystemDescriptionType_Name:
+ case KVirtualSystemDescriptionType_Product:
+ case KVirtualSystemDescriptionType_ProductUrl:
+ case KVirtualSystemDescriptionType_Vendor:
+ case KVirtualSystemDescriptionType_VendorUrl:
+ case KVirtualSystemDescriptionType_Version:
+ {
+ QLineEdit *pLineEdit = new QLineEdit(pParent);
+ pEditor = pLineEdit;
+ break;
+ }
+ case KVirtualSystemDescriptionType_Description:
+ case KVirtualSystemDescriptionType_License:
+ {
+ UILineTextEdit *pLineTextEdit = new UILineTextEdit(pParent);
+ pEditor = pLineTextEdit;
+ break;
+ }
+ case KVirtualSystemDescriptionType_CPU:
+ {
+ QSpinBox *pSpinBox = new QSpinBox(pParent);
+ pSpinBox->setRange(UIApplianceEditorWidget::minGuestCPUCount(), UIApplianceEditorWidget::maxGuestCPUCount());
+ pEditor = pSpinBox;
+ break;
+ }
+ case KVirtualSystemDescriptionType_Memory:
+ {
+ QSpinBox *pSpinBox = new QSpinBox(pParent);
+ pSpinBox->setRange(UIApplianceEditorWidget::minGuestRAM(), UIApplianceEditorWidget::maxGuestRAM());
+ pSpinBox->setSuffix(" " + UICommon::tr("MB", "size suffix MBytes=1024 KBytes"));
+ pEditor = pSpinBox;
+ break;
+ }
+ case KVirtualSystemDescriptionType_SoundCard:
+ {
+ QComboBox *pComboBox = new QComboBox(pParent);
+ pComboBox->addItem(gpConverter->toString(KAudioControllerType_AC97), KAudioControllerType_AC97);
+ pComboBox->addItem(gpConverter->toString(KAudioControllerType_SB16), KAudioControllerType_SB16);
+ pComboBox->addItem(gpConverter->toString(KAudioControllerType_HDA), KAudioControllerType_HDA);
+ pEditor = pComboBox;
+ break;
+ }
+ case KVirtualSystemDescriptionType_NetworkAdapter:
+ {
+ /* Create combo editor: */
+ QComboBox *pComboBox = new QComboBox(pParent);
+ /* Load currently supported network adapter types: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ QVector<KNetworkAdapterType> supportedTypes = comProperties.GetSupportedNetworkAdapterTypes();
+ /* Take currently requested type into account if it's sane: */
+ const KNetworkAdapterType enmAdapterType = static_cast<KNetworkAdapterType>(m_strConfigValue.toInt());
+ if (!supportedTypes.contains(enmAdapterType) && enmAdapterType != KNetworkAdapterType_Null)
+ supportedTypes.prepend(enmAdapterType);
+ /* Populate adapter types: */
+ int iAdapterTypeIndex = 0;
+ foreach (const KNetworkAdapterType &enmType, supportedTypes)
+ {
+ pComboBox->insertItem(iAdapterTypeIndex, gpConverter->toString(enmType));
+ pComboBox->setItemData(iAdapterTypeIndex, QVariant::fromValue((int)enmType));
+ pComboBox->setItemData(iAdapterTypeIndex, pComboBox->itemText(iAdapterTypeIndex), Qt::ToolTipRole);
+ ++iAdapterTypeIndex;
+ }
+ /* Pass editor back: */
+ pEditor = pComboBox;
+ break;
+ }
+ case KVirtualSystemDescriptionType_HardDiskControllerIDE:
+ {
+ QComboBox *pComboBox = new QComboBox(pParent);
+ pComboBox->addItem(gpConverter->toString(KStorageControllerType_PIIX3), "PIIX3");
+ pComboBox->addItem(gpConverter->toString(KStorageControllerType_PIIX4), "PIIX4");
+ pComboBox->addItem(gpConverter->toString(KStorageControllerType_ICH6), "ICH6");
+ pEditor = pComboBox;
+ break;
+ }
+ case KVirtualSystemDescriptionType_HardDiskImage:
+ {
+ UIFilePathSelector *pFileChooser = new UIFilePathSelector(pParent);
+ pFileChooser->setMode(UIFilePathSelector::Mode_File_Save);
+ pFileChooser->setResetEnabled(false);
+ pEditor = pFileChooser;
+ break;
+ }
+ case KVirtualSystemDescriptionType_SettingsFile:
+ {
+ UIFilePathSelector *pFileChooser = new UIFilePathSelector(pParent);
+ pFileChooser->setMode(UIFilePathSelector::Mode_File_Save);
+ pFileChooser->setResetEnabled(false);
+ pEditor = pFileChooser;
+ break;
+ }
+ case KVirtualSystemDescriptionType_BaseFolder:
+ {
+ UIFilePathSelector *pFileChooser = new UIFilePathSelector(pParent);
+ pFileChooser->setMode(UIFilePathSelector::Mode_Folder);
+ pFileChooser->setResetEnabled(false);
+ pEditor = pFileChooser;
+ break;
+ }
+ case KVirtualSystemDescriptionType_PrimaryGroup:
+ {
+ QComboBox *pComboBox = new QComboBox(pParent);
+ pComboBox->setEditable(true);
+ QVector<QString> groupsVector = uiCommon().virtualBox().GetMachineGroups();
+
+ for (int i = 0; i < groupsVector.size(); ++i)
+ pComboBox->addItem(groupsVector.at(i));
+ pEditor = pComboBox;
+ break;
+ }
+ case KVirtualSystemDescriptionType_CloudInstanceShape:
+ case KVirtualSystemDescriptionType_CloudDomain:
+ case KVirtualSystemDescriptionType_CloudBootDiskSize:
+ case KVirtualSystemDescriptionType_CloudBucket:
+ case KVirtualSystemDescriptionType_CloudOCIVCN:
+ case KVirtualSystemDescriptionType_CloudOCISubnet:
+ {
+ const QVariant get = m_pParent->getHint(m_enmVSDType);
+ switch (m_pParent->kindHint(m_enmVSDType))
+ {
+ case ParameterKind_Double:
+ {
+ AbstractVSDParameterDouble value = get.value<AbstractVSDParameterDouble>();
+ QSpinBox *pSpinBox = new QSpinBox(pParent);
+ pSpinBox->setRange(value.minimum, value.maximum);
+ pSpinBox->setSuffix(QString(" %1").arg(UICommon::tr(value.unit.toUtf8().constData())));
+ pEditor = pSpinBox;
+ break;
+ }
+ case ParameterKind_String:
+ {
+ QLineEdit *pLineEdit = new QLineEdit(pParent);
+ pEditor = pLineEdit;
+ break;
+ }
+ case ParameterKind_Array:
+ {
+ AbstractVSDParameterArray value = get.value<AbstractVSDParameterArray>();
+ QComboBox *pComboBox = new QComboBox(pParent);
+ /* Every array member is a complex value, - string pair,
+ * "first" is always present while "second" can be null. */
+ foreach (const QIStringPair &pair, value.values)
+ {
+ /* First always goes to combo item text: */
+ pComboBox->addItem(pair.first);
+ /* If "second" present => it goes to new item data: */
+ if (!pair.second.isNull())
+ pComboBox->setItemData(pComboBox->count() - 1, pair.second);
+ /* Otherwise => "first" goes to new item data as well: */
+ else
+ pComboBox->setItemData(pComboBox->count() - 1, pair.first);
+ }
+ pEditor = pComboBox;
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default: break;
+ }
+ }
+ return pEditor;
+}
+
+bool UIVirtualHardwareItem::setEditorData(QWidget *pEditor, const QModelIndex & /* idx */) const
+{
+ bool fDone = false;
+ switch (m_enmVSDType)
+ {
+ case KVirtualSystemDescriptionType_OS:
+ {
+ if (UIGuestOSTypeSelectionButton *pButton = qobject_cast<UIGuestOSTypeSelectionButton*>(pEditor))
+ {
+ pButton->setOSTypeId(m_strConfigValue);
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_HardDiskControllerIDE:
+ {
+ if (QComboBox *pComboBox = qobject_cast<QComboBox*>(pEditor))
+ {
+ int i = pComboBox->findData(m_strConfigValue);
+ if (i != -1)
+ pComboBox->setCurrentIndex(i);
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_CPU:
+ case KVirtualSystemDescriptionType_Memory:
+ {
+ if (QSpinBox *pSpinBox = qobject_cast<QSpinBox*>(pEditor))
+ {
+ pSpinBox->setValue(m_strConfigValue.toInt());
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_Name:
+ case KVirtualSystemDescriptionType_Product:
+ case KVirtualSystemDescriptionType_ProductUrl:
+ case KVirtualSystemDescriptionType_Vendor:
+ case KVirtualSystemDescriptionType_VendorUrl:
+ case KVirtualSystemDescriptionType_Version:
+ {
+ if (QLineEdit *pLineEdit = qobject_cast<QLineEdit*>(pEditor))
+ {
+ pLineEdit->setText(m_strConfigValue);
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_Description:
+ case KVirtualSystemDescriptionType_License:
+ {
+ if (UILineTextEdit *pLineTextEdit = qobject_cast<UILineTextEdit*>(pEditor))
+ {
+ pLineTextEdit->setText(m_strConfigValue);
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_SoundCard:
+ case KVirtualSystemDescriptionType_NetworkAdapter:
+ {
+ if (QComboBox *pComboBox = qobject_cast<QComboBox*>(pEditor))
+ {
+ int i = pComboBox->findData(m_strConfigValue.toInt());
+ if (i != -1)
+ pComboBox->setCurrentIndex(i);
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_HardDiskImage:
+ case KVirtualSystemDescriptionType_SettingsFile:
+ case KVirtualSystemDescriptionType_BaseFolder:
+ {
+ if (UIFilePathSelector *pFileChooser = qobject_cast<UIFilePathSelector*>(pEditor))
+ {
+ pFileChooser->setPath(m_strConfigValue);
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_PrimaryGroup:
+ {
+ if (QComboBox *pGroupCombo = qobject_cast<QComboBox*>(pEditor))
+ {
+ pGroupCombo->setCurrentText(m_strConfigValue);
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_CloudInstanceShape:
+ case KVirtualSystemDescriptionType_CloudDomain:
+ case KVirtualSystemDescriptionType_CloudBootDiskSize:
+ case KVirtualSystemDescriptionType_CloudBucket:
+ case KVirtualSystemDescriptionType_CloudOCIVCN:
+ case KVirtualSystemDescriptionType_CloudOCISubnet:
+ {
+ switch (m_pParent->kindHint(m_enmVSDType))
+ {
+ case ParameterKind_Double:
+ {
+ if (QSpinBox *pSpinBox = qobject_cast<QSpinBox*>(pEditor))
+ {
+ pSpinBox->setValue(m_strConfigValue.toInt());
+ fDone = true;
+ }
+ break;
+ }
+ case ParameterKind_String:
+ {
+ if (QLineEdit *pLineEdit = qobject_cast<QLineEdit*>(pEditor))
+ {
+ pLineEdit->setText(m_strConfigValue);
+ fDone = true;
+ }
+ break;
+ }
+ case ParameterKind_Array:
+ {
+ if (QComboBox *pComboBox = qobject_cast<QComboBox*>(pEditor))
+ {
+ /* Every array member is a complex value, - string pair,
+ * "first" is always present while "second" can be null.
+ * Actual config value is always stored in item data. */
+ const int iIndex = pComboBox->findData(m_strConfigValue);
+ /* If item was found => choose it: */
+ if (iIndex != -1)
+ pComboBox->setCurrentIndex(iIndex);
+ /* Otherwise => just choose the text: */
+ else
+ pComboBox->setCurrentText(m_strConfigValue);
+ fDone = true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default: break;
+ }
+ return fDone;
+}
+
+bool UIVirtualHardwareItem::setModelData(QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex & idx)
+{
+ bool fDone = false;
+ switch (m_enmVSDType)
+ {
+ case KVirtualSystemDescriptionType_OS:
+ {
+ if (UIGuestOSTypeSelectionButton *pButton = qobject_cast<UIGuestOSTypeSelectionButton*>(pEditor))
+ {
+ m_strConfigValue = pButton->osTypeId();
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_HardDiskControllerIDE:
+ {
+ if (QComboBox *pComboBox = qobject_cast<QComboBox*>(pEditor))
+ {
+ m_strConfigValue = pComboBox->itemData(pComboBox->currentIndex()).toString();
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_CPU:
+ case KVirtualSystemDescriptionType_Memory:
+ {
+ if (QSpinBox *pSpinBox = qobject_cast<QSpinBox*>(pEditor))
+ {
+ m_strConfigValue = QString::number(pSpinBox->value());
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_Name:
+ {
+ if (QLineEdit *pLineEdit = qobject_cast<QLineEdit*>(pEditor))
+ {
+ /* When the VM name is changed the path of the disk images
+ * should be also changed. So first of all find all disk
+ * images corresponding to this appliance. Next check if
+ * they are modified by the user already. If not change the
+ * path to the new path. */
+ /* Create an index of this position, but in column 0. */
+ QModelIndex c0Index = pModel->index(idx.row(), 0, idx.parent());
+ /* Query all items with the type HardDiskImage and which
+ * are child's of this item. */
+ QModelIndexList list = pModel->match(c0Index,
+ UIVirtualHardwareItem::TypeRole,
+ KVirtualSystemDescriptionType_HardDiskImage,
+ -1,
+ Qt::MatchExactly | Qt::MatchWrap | Qt::MatchRecursive);
+ for (int i = 0; i < list.count(); ++i)
+ {
+ /* Get the index for the config value column. */
+ QModelIndex hdIndex = pModel->index(list.at(i).row(), ApplianceViewSection_ConfigValue, list.at(i).parent());
+ /* Ignore it if was already modified by the user. */
+ if (!hdIndex.data(ModifiedRole).toBool())
+ /* Replace any occurrence of the old VM name with
+ * the new VM name. */
+ {
+ QStringList splittedOriginalPath = hdIndex.data(Qt::EditRole).toString().split(QDir::separator());
+ QStringList splittedNewPath;
+
+ foreach (QString a, splittedOriginalPath)
+ {
+ (a.compare(m_strConfigValue) == 0) ? splittedNewPath << pLineEdit->text() : splittedNewPath << a;
+ }
+
+ QString newPath = splittedNewPath.join(QDir::separator());
+
+ pModel->setData(hdIndex,
+ newPath,
+ Qt::EditRole);
+ }
+ }
+ m_strConfigValue = pLineEdit->text();
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_Product:
+ case KVirtualSystemDescriptionType_ProductUrl:
+ case KVirtualSystemDescriptionType_Vendor:
+ case KVirtualSystemDescriptionType_VendorUrl:
+ case KVirtualSystemDescriptionType_Version:
+ {
+ if (QLineEdit *pLineEdit = qobject_cast<QLineEdit*>(pEditor))
+ {
+ m_strConfigValue = pLineEdit->text();
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_Description:
+ case KVirtualSystemDescriptionType_License:
+ {
+ if (UILineTextEdit *pLineTextEdit = qobject_cast<UILineTextEdit*>(pEditor))
+ {
+ m_strConfigValue = pLineTextEdit->text();
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_SoundCard:
+ case KVirtualSystemDescriptionType_NetworkAdapter:
+ {
+ if (QComboBox *pComboBox = qobject_cast<QComboBox*>(pEditor))
+ {
+ m_strConfigValue = pComboBox->itemData(pComboBox->currentIndex()).toString();
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_PrimaryGroup:
+ {
+ if (QComboBox *pComboBox = qobject_cast<QComboBox*>(pEditor))
+ {
+ m_strConfigValue = pComboBox->currentText();
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_HardDiskImage:
+ case KVirtualSystemDescriptionType_BaseFolder:
+ {
+ if (UIFilePathSelector *pFileChooser = qobject_cast<UIFilePathSelector*>(pEditor))
+ {
+ m_strConfigValue = pFileChooser->path();
+ fDone = true;
+ }
+ break;
+ }
+ case KVirtualSystemDescriptionType_CloudInstanceShape:
+ case KVirtualSystemDescriptionType_CloudDomain:
+ case KVirtualSystemDescriptionType_CloudBootDiskSize:
+ case KVirtualSystemDescriptionType_CloudBucket:
+ case KVirtualSystemDescriptionType_CloudOCIVCN:
+ case KVirtualSystemDescriptionType_CloudOCISubnet:
+ {
+ switch (m_pParent->kindHint(m_enmVSDType))
+ {
+ case ParameterKind_Double:
+ {
+ if (QSpinBox *pSpinBox = qobject_cast<QSpinBox*>(pEditor))
+ {
+ m_strConfigValue = QString::number(pSpinBox->value());
+ fDone = true;
+ }
+ break;
+ }
+ case ParameterKind_String:
+ {
+ if (QLineEdit *pLineEdit = qobject_cast<QLineEdit*>(pEditor))
+ {
+ m_strConfigValue = pLineEdit->text();
+ fDone = true;
+ }
+ break;
+ }
+ case ParameterKind_Array:
+ {
+ if (QComboBox *pComboBox = qobject_cast<QComboBox*>(pEditor))
+ {
+ /* Every array member is a complex value, - string pair,
+ * "first" is always present while "second" can be null.
+ * Actual config value is always stored in item data. */
+ const QString strData = pComboBox->currentData().toString();
+ /* If item data isn't null => pass it: */
+ if (!strData.isNull())
+ m_strConfigValue = strData;
+ /* Otherwise => just pass the text: */
+ else
+ m_strConfigValue = pComboBox->currentText();
+ fDone = true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ default: break;
+ }
+ if (fDone)
+ m_fModified = true;
+
+ return fDone;
+}
+
+void UIVirtualHardwareItem::restoreDefaults()
+{
+ m_strConfigValue = m_strConfigDefaultValue;
+ m_checkState = Qt::Checked;
+}
+
+void UIVirtualHardwareItem::putBack(QVector<BOOL> &finalStates, QVector<QString> &finalValues, QVector<QString> &finalExtraValues)
+{
+ finalStates[m_iNumber] = m_checkState == Qt::Checked;
+ /* It's always stored in bytes in VSD according to the old internal agreement within the team */
+ finalValues[m_iNumber] = m_enmVSDType == KVirtualSystemDescriptionType_Memory ? UITranslator::megabyteStringToByteString(m_strConfigValue) : m_strConfigValue;
+ finalExtraValues[m_iNumber] = m_enmVSDType == KVirtualSystemDescriptionType_Memory ? UITranslator::megabyteStringToByteString(m_strExtraConfigValue) : m_strExtraConfigValue;
+
+ UIApplianceModelItem::putBack(finalStates, finalValues, finalExtraValues);
+}
+
+
+KVirtualSystemDescriptionType UIVirtualHardwareItem::systemDescriptionType() const
+{
+ return m_enmVSDType;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIApplianceModel implementation. *
+*********************************************************************************************************************************/
+
+UIApplianceModel::UIApplianceModel(QVector<CVirtualSystemDescription>& aVSDs, QITreeView *pParent)
+ : QAbstractItemModel(pParent)
+ , m_pRootItem(new UIApplianceModelItem(0, ApplianceModelItemType_Root, pParent))
+{
+ for (int iVSDIndex = 0; iVSDIndex < aVSDs.size(); ++iVSDIndex)
+ {
+ CVirtualSystemDescription vsd = aVSDs[iVSDIndex];
+
+ UIVirtualSystemItem *pVirtualSystemItem = new UIVirtualSystemItem(iVSDIndex, vsd, m_pRootItem);
+ m_pRootItem->appendChild(pVirtualSystemItem);
+
+ /** @todo ask Dmitry about include/COMDefs.h:232 */
+ QVector<KVirtualSystemDescriptionType> types;
+ QVector<QString> refs;
+ QVector<QString> origValues;
+ QVector<QString> configValues;
+ QVector<QString> extraConfigValues;
+
+ QList<int> hdIndexes;
+ QMap<int, UIVirtualHardwareItem*> controllerMap;
+ vsd.GetDescription(types, refs, origValues, configValues, extraConfigValues);
+ for (int i = 0; i < types.size(); ++i)
+ {
+ if (types[i] == KVirtualSystemDescriptionType_SettingsFile)
+ continue;
+ /* We add the hard disk images in an second step, so save a
+ reference to them. */
+ else if (types[i] == KVirtualSystemDescriptionType_HardDiskImage)
+ hdIndexes << i;
+ else
+ {
+ UIVirtualHardwareItem *pHardwareItem = new UIVirtualHardwareItem(this, i, types[i], refs[i], origValues[i], configValues[i], extraConfigValues[i], pVirtualSystemItem);
+ pVirtualSystemItem->appendChild(pHardwareItem);
+ /* Save the hard disk controller types in an extra map */
+ if (types[i] == KVirtualSystemDescriptionType_HardDiskControllerIDE ||
+ types[i] == KVirtualSystemDescriptionType_HardDiskControllerSATA ||
+ types[i] == KVirtualSystemDescriptionType_HardDiskControllerSCSI ||
+ types[i] == KVirtualSystemDescriptionType_HardDiskControllerVirtioSCSI ||
+ types[i] == KVirtualSystemDescriptionType_HardDiskControllerSAS ||
+ types[i] == KVirtualSystemDescriptionType_HardDiskControllerNVMe)
+ controllerMap[i] = pHardwareItem;
+ }
+ }
+ QRegExp rx("controller=(\\d+);?");
+ /* Now process the hard disk images */
+ for (int iHDIndex = 0; iHDIndex < hdIndexes.size(); ++iHDIndex)
+ {
+ int i = hdIndexes[iHDIndex];
+ QString ecnf = extraConfigValues[i];
+ if (rx.indexIn(ecnf) != -1)
+ {
+ /* Get the controller */
+ UIVirtualHardwareItem *pControllerItem = controllerMap[rx.cap(1).toInt()];
+ if (pControllerItem)
+ {
+ /* New hardware item as child of the controller */
+ UIVirtualHardwareItem *pStorageItem = new UIVirtualHardwareItem(this, i, types[i], refs[i], origValues[i], configValues[i], extraConfigValues[i], pControllerItem);
+ pControllerItem->appendChild(pStorageItem);
+ }
+ }
+ }
+ }
+}
+
+UIApplianceModel::~UIApplianceModel()
+{
+ if (m_pRootItem)
+ delete m_pRootItem;
+}
+
+QModelIndex UIApplianceModel::root() const
+{
+ return index(0, 0);
+}
+
+QModelIndex UIApplianceModel::index(int iRow, int iColumn, const QModelIndex &parentIdx /* = QModelIndex() */) const
+{
+ if (!hasIndex(iRow, iColumn, parentIdx))
+ return QModelIndex();
+
+ UIApplianceModelItem *pItem = !parentIdx.isValid() ? m_pRootItem :
+ static_cast<UIApplianceModelItem*>(parentIdx.internalPointer())->childItem(iRow);
+
+ return pItem ? createIndex(iRow, iColumn, pItem) : QModelIndex();
+}
+
+QModelIndex UIApplianceModel::parent(const QModelIndex &idx) const
+{
+ if (!idx.isValid())
+ return QModelIndex();
+
+ UIApplianceModelItem *pItem = static_cast<UIApplianceModelItem*>(idx.internalPointer());
+ UIApplianceModelItem *pParentItem = pItem->parent();
+
+ if (pParentItem)
+ return createIndex(pParentItem->row(), 0, pParentItem);
+ else
+ return QModelIndex();
+}
+
+int UIApplianceModel::rowCount(const QModelIndex &parentIdx /* = QModelIndex() */) const
+{
+ return !parentIdx.isValid() ? 1 /* only root item has invalid parent */ :
+ static_cast<UIApplianceModelItem*>(parentIdx.internalPointer())->childCount();
+}
+
+int UIApplianceModel::columnCount(const QModelIndex &parentIdx /* = QModelIndex() */) const
+{
+ return !parentIdx.isValid() ? m_pRootItem->columnCount() :
+ static_cast<UIApplianceModelItem*>(parentIdx.internalPointer())->columnCount();
+}
+
+Qt::ItemFlags UIApplianceModel::flags(const QModelIndex &idx) const
+{
+ if (!idx.isValid())
+ return Qt::ItemFlags();
+
+ UIApplianceModelItem *pItem = static_cast<UIApplianceModelItem*>(idx.internalPointer());
+
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | pItem->itemFlags(idx.column());
+}
+
+QVariant UIApplianceModel::headerData(int iSection, Qt::Orientation enmOrientation, int iRole) const
+{
+ if (iRole != Qt::DisplayRole ||
+ enmOrientation != Qt::Horizontal)
+ return QVariant();
+
+ QString strTitle;
+ switch (iSection)
+ {
+ case ApplianceViewSection_Description: strTitle = UIApplianceEditorWidget::tr("Description"); break;
+ case ApplianceViewSection_ConfigValue: strTitle = UIApplianceEditorWidget::tr("Configuration"); break;
+ }
+ return strTitle;
+}
+
+bool UIApplianceModel::setData(const QModelIndex &idx, const QVariant &value, int iRole)
+{
+ if (!idx.isValid())
+ return false;
+
+ UIApplianceModelItem *pTtem = static_cast<UIApplianceModelItem*>(idx.internalPointer());
+
+ return pTtem->setData(idx.column(), value, iRole);
+}
+
+QVariant UIApplianceModel::data(const QModelIndex &idx, int iRole /* = Qt::DisplayRole */) const
+{
+ if (!idx.isValid())
+ return QVariant();
+
+ UIApplianceModelItem *pTtem = static_cast<UIApplianceModelItem*>(idx.internalPointer());
+
+ return pTtem->data(idx.column(), iRole);
+}
+
+QModelIndex UIApplianceModel::buddy(const QModelIndex &idx) const
+{
+ if (!idx.isValid())
+ return QModelIndex();
+
+ if (idx.column() == ApplianceViewSection_ConfigValue)
+ return idx;
+ else
+ return index(idx.row(), ApplianceViewSection_ConfigValue, idx.parent());
+}
+
+void UIApplianceModel::restoreDefaults(QModelIndex parentIdx /* = QModelIndex() */)
+{
+ /* By default use the root: */
+ if (!parentIdx.isValid())
+ parentIdx = root();
+
+ /* Get corresponding parent item and enumerate it's children: */
+ UIApplianceModelItem *pParentItem = static_cast<UIApplianceModelItem*>(parentIdx.internalPointer());
+ for (int i = 0; i < pParentItem->childCount(); ++i)
+ {
+ /* Reset children item data to default: */
+ pParentItem->childItem(i)->restoreDefaults();
+ /* Recursively process children item: */
+ restoreDefaults(index(i, 0, parentIdx));
+ }
+ /* Notify the model about the changes: */
+ emit dataChanged(index(0, 0, parentIdx), index(pParentItem->childCount() - 1, 0, parentIdx));
+}
+
+void UIApplianceModel::putBack()
+{
+ QVector<BOOL> v1;
+ QVector<QString> v2;
+ QVector<QString> v3;
+ m_pRootItem->putBack(v1, v2, v3);
+}
+
+
+void UIApplianceModel::setVirtualSystemBaseFolder(const QString& path)
+{
+ if (!m_pRootItem)
+ return;
+ /* For each Virtual System: */
+ for (int i = 0; i < m_pRootItem->childCount(); ++i)
+ {
+ UIVirtualSystemItem *pVirtualSystem = dynamic_cast<UIVirtualSystemItem*>(m_pRootItem->childItem(i));
+ if (!pVirtualSystem)
+ continue;
+ int iItemCount = pVirtualSystem->childCount();
+ for (int j = 0; j < iItemCount; ++j)
+ {
+ UIVirtualHardwareItem *pHardwareItem = dynamic_cast<UIVirtualHardwareItem*>(pVirtualSystem->childItem(j));
+ if (!pHardwareItem)
+ continue;
+ if (pHardwareItem->systemDescriptionType() != KVirtualSystemDescriptionType_BaseFolder)
+ continue;
+ QVariant data(path);
+ pHardwareItem->setData(ApplianceViewSection_ConfigValue, data, Qt::EditRole);
+ QModelIndex index = createIndex(pHardwareItem->row(), 0, pHardwareItem);
+ emit dataChanged(index, index);
+ }
+ }
+}
+
+void UIApplianceModel::setVsdHints(const AbstractVSDParameterList &hints)
+{
+ m_listVsdHints = hints;
+}
+
+QString UIApplianceModel::nameHint(KVirtualSystemDescriptionType enmType) const
+{
+ foreach (const AbstractVSDParameter &parameter, m_listVsdHints)
+ if (parameter.type == enmType)
+ return parameter.name;
+ return QString();
+}
+
+AbstractVSDParameterKind UIApplianceModel::kindHint(KVirtualSystemDescriptionType enmType) const
+{
+ foreach (const AbstractVSDParameter &parameter, m_listVsdHints)
+ if (parameter.type == enmType)
+ return parameter.kind;
+ return ParameterKind_Invalid;
+}
+
+QVariant UIApplianceModel::getHint(KVirtualSystemDescriptionType enmType) const
+{
+ foreach (const AbstractVSDParameter &parameter, m_listVsdHints)
+ if (parameter.type == enmType)
+ return parameter.get;
+ return QVariant();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIApplianceDelegate implementation. *
+*********************************************************************************************************************************/
+
+UIApplianceDelegate::UIApplianceDelegate(QAbstractProxyModel *pProxy)
+ : QItemDelegate(pProxy)
+ , m_pProxy(pProxy)
+{
+}
+
+QWidget *UIApplianceDelegate::createEditor(QWidget *pParent, const QStyleOptionViewItem &styleOption, const QModelIndex &idx) const
+{
+ if (!idx.isValid())
+ return QItemDelegate::createEditor(pParent, styleOption, idx);
+
+ QModelIndex index(idx);
+ if (m_pProxy)
+ index = m_pProxy->mapToSource(idx);
+
+ UIApplianceModelItem *pItem = static_cast<UIApplianceModelItem*>(index.internalPointer());
+ QWidget *pEditor = pItem->createEditor(pParent, styleOption, index);
+
+ if (!pEditor)
+ return QItemDelegate::createEditor(pParent, styleOption, index);
+
+ /* Allow UILineTextEdit to commit data early: */
+ if (qobject_cast<UILineTextEdit*>(pEditor))
+ connect(pEditor, SIGNAL(sigFinished(QWidget*)), this, SIGNAL(commitData(QWidget*)));
+
+ return pEditor;
+}
+
+void UIApplianceDelegate::setEditorData(QWidget *pEditor, const QModelIndex &idx) const
+{
+ if (!idx.isValid())
+ return QItemDelegate::setEditorData(pEditor, idx);
+
+ QModelIndex index(idx);
+ if (m_pProxy)
+ index = m_pProxy->mapToSource(idx);
+
+ UIApplianceModelItem *pItem = static_cast<UIApplianceModelItem*>(index.internalPointer());
+
+ if (!pItem->setEditorData(pEditor, index))
+ QItemDelegate::setEditorData(pEditor, index);
+}
+
+void UIApplianceDelegate::setModelData(QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex &idx) const
+{
+ if (!idx.isValid())
+ return QItemDelegate::setModelData(pEditor, pModel, idx);
+
+ QModelIndex index = pModel->index(idx.row(), idx.column());
+ if (m_pProxy)
+ index = m_pProxy->mapToSource(idx);
+
+ UIApplianceModelItem *pItem = static_cast<UIApplianceModelItem*>(index.internalPointer());
+ if (!pItem->setModelData(pEditor, pModel, idx))
+ QItemDelegate::setModelData(pEditor, pModel, idx);
+}
+
+void UIApplianceDelegate::updateEditorGeometry(QWidget *pEditor, const QStyleOptionViewItem &styleOption, const QModelIndex & /* idx */) const
+{
+ if (pEditor)
+ pEditor->setGeometry(styleOption.rect);
+}
+
+QSize UIApplianceDelegate::sizeHint(const QStyleOptionViewItem &styleOption, const QModelIndex &idx) const
+{
+ QSize size = QItemDelegate::sizeHint(styleOption, idx);
+#ifdef VBOX_WS_MAC
+ int h = 28;
+#else
+ int h = 24;
+#endif
+ size.setHeight(RT_MAX(h, size.height()));
+ return size;
+}
+
+#ifdef VBOX_WS_MAC
+bool UIApplianceDelegate::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ if (pEvent->type() == QEvent::FocusOut)
+ {
+ /* On Mac OS X Cocoa the OS type selector widget loses it focus when
+ * the popup menu is shown. Prevent this here, cause otherwise the new
+ * selected OS will not be updated. */
+ UIGuestOSTypeSelectionButton *pButton = qobject_cast<UIGuestOSTypeSelectionButton*>(pObject);
+ if (pButton && pButton->isMenuShown())
+ return false;
+ /* The same counts for the text edit buttons of the license or
+ * description fields. */
+ else if (qobject_cast<UILineTextEdit*>(pObject))
+ return false;
+ }
+
+ return QItemDelegate::eventFilter(pObject, pEvent);
+}
+#endif /* VBOX_WS_MAC */
+
+
+/*********************************************************************************************************************************
+* Class UIApplianceSortProxyModel implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+KVirtualSystemDescriptionType UIApplianceSortProxyModel::s_aSortList[] =
+{
+ KVirtualSystemDescriptionType_Name,
+ KVirtualSystemDescriptionType_Product,
+ KVirtualSystemDescriptionType_ProductUrl,
+ KVirtualSystemDescriptionType_Vendor,
+ KVirtualSystemDescriptionType_VendorUrl,
+ KVirtualSystemDescriptionType_Version,
+ KVirtualSystemDescriptionType_Description,
+ KVirtualSystemDescriptionType_License,
+ KVirtualSystemDescriptionType_OS,
+ KVirtualSystemDescriptionType_CPU,
+ KVirtualSystemDescriptionType_Memory,
+ KVirtualSystemDescriptionType_Floppy,
+ KVirtualSystemDescriptionType_CDROM,
+ KVirtualSystemDescriptionType_USBController,
+ KVirtualSystemDescriptionType_SoundCard,
+ KVirtualSystemDescriptionType_NetworkAdapter,
+ KVirtualSystemDescriptionType_HardDiskControllerIDE,
+ KVirtualSystemDescriptionType_HardDiskControllerSATA,
+ KVirtualSystemDescriptionType_HardDiskControllerSCSI,
+ KVirtualSystemDescriptionType_HardDiskControllerVirtioSCSI,
+ KVirtualSystemDescriptionType_HardDiskControllerSAS,
+ KVirtualSystemDescriptionType_HardDiskControllerNVMe,
+ /* OCI */
+ KVirtualSystemDescriptionType_CloudProfileName,
+ KVirtualSystemDescriptionType_CloudBucket,
+ KVirtualSystemDescriptionType_CloudKeepObject,
+ KVirtualSystemDescriptionType_CloudLaunchInstance,
+ KVirtualSystemDescriptionType_CloudInstanceShape,
+ KVirtualSystemDescriptionType_CloudBootDiskSize,
+ KVirtualSystemDescriptionType_CloudOCIVCN,
+ KVirtualSystemDescriptionType_CloudOCISubnet,
+ KVirtualSystemDescriptionType_CloudPublicIP,
+ KVirtualSystemDescriptionType_CloudDomain
+};
+
+UIApplianceSortProxyModel::UIApplianceSortProxyModel(QObject *pParent)
+ : QSortFilterProxyModel(pParent)
+{
+}
+
+bool UIApplianceSortProxyModel::filterAcceptsRow(int iSourceRow, const QModelIndex &srcParenIdx) const
+{
+ /* By default enable all, we will explicitly filter out below */
+ if (srcParenIdx.isValid())
+ {
+ QModelIndex i = index(iSourceRow, 0, srcParenIdx);
+ if (i.isValid())
+ {
+ UIApplianceModelItem *pItem = static_cast<UIApplianceModelItem*>(i.internalPointer());
+ /* We filter hardware types only */
+ if (pItem->type() == ApplianceModelItemType_VirtualHardware)
+ {
+ UIVirtualHardwareItem *hwItem = static_cast<UIVirtualHardwareItem*>(pItem);
+ /* The license type shouldn't be displayed */
+ if (m_aFilteredList.contains(hwItem->m_enmVSDType))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool UIApplianceSortProxyModel::lessThan(const QModelIndex &leftIdx, const QModelIndex &rightIdx) const
+{
+ if (!leftIdx.isValid() ||
+ !rightIdx.isValid())
+ return false;
+
+ UIApplianceModelItem *pLeftItem = static_cast<UIApplianceModelItem*>(leftIdx.internalPointer());
+ UIApplianceModelItem *pRightItem = static_cast<UIApplianceModelItem*>(rightIdx.internalPointer());
+
+ /* We sort hardware types only */
+ if (!(pLeftItem->type() == ApplianceModelItemType_VirtualHardware &&
+ pRightItem->type() == ApplianceModelItemType_VirtualHardware))
+ return false;
+
+ UIVirtualHardwareItem *pHwLeft = static_cast<UIVirtualHardwareItem*>(pLeftItem);
+ UIVirtualHardwareItem *pHwRight = static_cast<UIVirtualHardwareItem*>(pRightItem);
+
+ for (unsigned int i = 0; i < RT_ELEMENTS(s_aSortList); ++i)
+ if (pHwLeft->m_enmVSDType == s_aSortList[i])
+ {
+ for (unsigned int a = 0; a <= i; ++a)
+ if (pHwRight->m_enmVSDType == s_aSortList[a])
+ return true;
+ return false;
+ }
+
+ return true;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIApplianceEditorWidget implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+int UIApplianceEditorWidget::m_minGuestRAM = -1;
+int UIApplianceEditorWidget::m_maxGuestRAM = -1;
+int UIApplianceEditorWidget::m_minGuestCPUCount = -1;
+int UIApplianceEditorWidget::m_maxGuestCPUCount = -1;
+
+UIApplianceEditorWidget::UIApplianceEditorWidget(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pModel(0)
+{
+ /* Make sure all static content is properly initialized */
+ initSystemSettings();
+
+ /* Create layout: */
+ m_pLayout = new QVBoxLayout(this);
+ {
+ /* Configure information layout: */
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create information pane: */
+ m_pPaneInformation = new QWidget;
+ {
+ /* Create information layout: */
+ QVBoxLayout *m_pLayoutInformation = new QVBoxLayout(m_pPaneInformation);
+ {
+ /* Configure information layout: */
+ m_pLayoutInformation->setContentsMargins(0, 0, 0, 0);
+
+ /* Create tree-view: */
+ m_pTreeViewSettings = new QITreeView;
+ {
+ /* Configure tree-view: */
+ m_pTreeViewSettings->setAlternatingRowColors(true);
+ m_pTreeViewSettings->setAllColumnsShowFocus(true);
+ m_pTreeViewSettings->header()->setStretchLastSection(true);
+ m_pTreeViewSettings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
+ m_pTreeViewSettings->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
+
+ /* Add tree-view into information layout: */
+ m_pLayoutInformation->addWidget(m_pTreeViewSettings);
+ }
+
+
+
+ }
+
+ /* Add information pane into layout: */
+ m_pLayout->addWidget(m_pPaneInformation);
+ }
+
+ /* Create warning pane: */
+ m_pPaneWarning = new QWidget;
+ {
+ /* Configure warning pane: */
+ m_pPaneWarning->hide();
+ m_pPaneWarning->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+
+ /* Create warning layout: */
+ QVBoxLayout *m_pLayoutWarning = new QVBoxLayout(m_pPaneWarning);
+ {
+ /* Configure warning layout: */
+ m_pLayoutWarning->setContentsMargins(0, 0, 0, 0);
+
+ /* Create label: */
+ m_pLabelWarning = new QLabel;
+ {
+ /* Add label into warning layout: */
+ m_pLayoutWarning->addWidget(m_pLabelWarning);
+ }
+
+ /* Create text-edit: */
+ m_pTextEditWarning = new QTextEdit;
+ {
+ /* Configure text-edit: */
+ m_pTextEditWarning->setReadOnly(true);
+ m_pTextEditWarning->setMaximumHeight(50);
+ m_pTextEditWarning->setAutoFormatting(QTextEdit::AutoBulletList);
+
+ /* Add text-edit into warning layout: */
+ m_pLayoutWarning->addWidget(m_pTextEditWarning);
+ }
+ }
+
+ /* Add warning pane into layout: */
+ m_pLayout->addWidget(m_pPaneWarning);
+ }
+ }
+
+ /* Translate finally: */
+ retranslateUi();
+}
+
+void UIApplianceEditorWidget::clear()
+{
+ /* Wipe model: */
+ delete m_pModel;
+ m_pModel = 0;
+
+ /* And appliance: */
+ m_comAppliance = CAppliance();
+}
+
+void UIApplianceEditorWidget::setAppliance(const CAppliance &comAppliance)
+{
+ m_comAppliance = comAppliance;
+}
+
+void UIApplianceEditorWidget::setVsdHints(const AbstractVSDParameterList &hints)
+{
+ /* Save here as well: */
+ m_listVsdHints = hints;
+
+ /* Make sure model exists, it's being created in sub-classes: */
+ if (m_pModel)
+ m_pModel->setVsdHints(m_listVsdHints);
+}
+
+void UIApplianceEditorWidget::setVirtualSystemBaseFolder(const QString &strPath)
+{
+ /* Make sure model exists, it's being created in sub-classes: */
+ if (m_pModel)
+ m_pModel->setVirtualSystemBaseFolder(strPath);
+}
+
+void UIApplianceEditorWidget::restoreDefaults()
+{
+ /* Make sure model exists, it's being created in sub-classes: */
+ if (m_pModel)
+ m_pModel->restoreDefaults();
+}
+
+void UIApplianceEditorWidget::retranslateUi()
+{
+ /* Translate information pane tree-view: */
+ m_pTreeViewSettings->setWhatsThis(tr("Detailed list of all components of all virtual machines of the current appliance"));
+
+ /* Translate warning pane label: */
+ m_pLabelWarning->setText(tr("Warnings:"));
+}
+
+/* static */
+void UIApplianceEditorWidget::initSystemSettings()
+{
+ if (m_minGuestRAM == -1)
+ {
+ /* We need some global defaults from the current VirtualBox
+ installation */
+ CSystemProperties sp = uiCommon().virtualBox().GetSystemProperties();
+ m_minGuestRAM = sp.GetMinGuestRAM();
+ m_maxGuestRAM = sp.GetMaxGuestRAM();
+ m_minGuestCPUCount = sp.GetMinGuestCPUCount();
+ m_maxGuestCPUCount = sp.GetMaxGuestCPUCount();
+ }
+}
+
+
+#include "UIApplianceEditorWidget.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceEditorWidget.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceEditorWidget.h
new file mode 100644
index 00000000..d8389946
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceEditorWidget.h
@@ -0,0 +1,376 @@
+/* $Id: UIApplianceEditorWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIApplianceEditorWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIApplianceEditorWidget_h
+#define FEQT_INCLUDED_SRC_widgets_UIApplianceEditorWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QAbstractItemModel>
+#include <QItemDelegate>
+#include <QSortFilterProxyModel>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CAppliance.h"
+#include "CVirtualSystemDescription.h"
+
+/* Forward declarations: */
+class UIApplianceModelItem;
+class QCheckBox;
+class QLabel;
+class QTextEdit;
+class QITreeView;
+class QVBoxLayout;
+
+
+/** Abstract VSD parameter kinds. */
+enum AbstractVSDParameterKind
+{
+ ParameterKind_Invalid,
+ ParameterKind_Bool,
+ ParameterKind_Double,
+ ParameterKind_String,
+ ParameterKind_Array
+};
+
+/** Abstract VSD parameter of Bool type, internal level. */
+struct AbstractVSDParameterBool
+{
+ /** Public default constructor to fit Q_DECLARE_METATYPE rule. */
+ AbstractVSDParameterBool()
+ : value(false) {}
+ /** Public copy constructor to fit Q_DECLARE_METATYPE rule. */
+ AbstractVSDParameterBool(const AbstractVSDParameterBool &other)
+ : value(other.value) {}
+ /** Holds the value. */
+ bool value;
+};
+Q_DECLARE_METATYPE(AbstractVSDParameterBool);
+
+/** Abstract VSD parameter of Double type, internal level. */
+struct AbstractVSDParameterDouble
+{
+ /** Public default constructor to fit Q_DECLARE_METATYPE rule. */
+ AbstractVSDParameterDouble()
+ : minimum(0), maximum(0), unit(QString()) {}
+ /** Public copy constructor to fit Q_DECLARE_METATYPE rule. */
+ AbstractVSDParameterDouble(const AbstractVSDParameterDouble &other)
+ : minimum(other.minimum), maximum(other.maximum), unit(other.unit) {}
+ /** Holds the minimum/base value. */
+ double minimum;
+ /** Holds the maximum value. */
+ double maximum;
+ /** Holds the unit. */
+ QString unit;
+};
+Q_DECLARE_METATYPE(AbstractVSDParameterDouble);
+
+/** Abstract VSD parameter of String type, internal level. */
+struct AbstractVSDParameterString
+{
+ /** Public default constructor to fit Q_DECLARE_METATYPE rule. */
+ AbstractVSDParameterString()
+ : value(QString()) {}
+ /** Public copy constructor to fit Q_DECLARE_METATYPE rule. */
+ AbstractVSDParameterString(const AbstractVSDParameterString &other)
+ : value(other.value) {}
+ /** Holds the value. */
+ QString value;
+};
+Q_DECLARE_METATYPE(AbstractVSDParameterString);
+
+/** Abstract VSD parameter of Array type, internal level. */
+struct AbstractVSDParameterArray
+{
+ /** Public default constructor to fit Q_DECLARE_METATYPE rule. */
+ AbstractVSDParameterArray()
+ : values(QIStringPairList()) {}
+ /** Public copy constructor to fit Q_DECLARE_METATYPE rule. */
+ AbstractVSDParameterArray(const AbstractVSDParameterArray &other)
+ : values(other.values) {}
+ /** Holds the values array. */
+ QIStringPairList values;
+};
+Q_DECLARE_METATYPE(AbstractVSDParameterArray);
+
+/** Abstract VSD parameter interface, facade level. */
+struct AbstractVSDParameter
+{
+ /** Holds the parameter name. */
+ QString name;
+ /** Holds the parameter type. */
+ KVirtualSystemDescriptionType type;
+ /** Holds the parameter kind. */
+ AbstractVSDParameterKind kind;
+ /** Holds the parameter abstract getter. */
+ QVariant get;
+};
+
+/** Abstract VSD parameter list. */
+typedef QList<AbstractVSDParameter> AbstractVSDParameterList;
+Q_DECLARE_METATYPE(AbstractVSDParameterList);
+
+
+/** Appliance tree-view section types. */
+enum ApplianceViewSection
+{
+ ApplianceViewSection_Description = 0,
+ ApplianceViewSection_OriginalValue,
+ ApplianceViewSection_ConfigValue
+};
+
+
+/** Appliance model item types. */
+enum ApplianceModelItemType
+{
+ ApplianceModelItemType_Root,
+ ApplianceModelItemType_VirtualSystem,
+ ApplianceModelItemType_VirtualHardware
+};
+
+
+/** QAbstractItemModel subclass used as Appliance model. */
+class UIApplianceModel : public QAbstractItemModel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs the Appliance model passing @a pParent to the base-class.
+ * @param aVSDs Brings the Virtual System descriptions. */
+ UIApplianceModel(QVector<CVirtualSystemDescription>& aVSDs, QITreeView *pParent);
+ /** Destructs the Appliance model. */
+ ~UIApplianceModel();
+
+ /** Returns the root index in the model. */
+ virtual QModelIndex root() const;
+ /** Returns the index of the item in the model specified by the given @a iRow, @a iColumn and @a parentIdx. */
+ virtual QModelIndex index(int iRow, int iColumn, const QModelIndex &parentIdx = QModelIndex()) const RT_OVERRIDE;
+ /** Returns the parent of the model item with the given @a idx. */
+ virtual QModelIndex parent(const QModelIndex &idx) const RT_OVERRIDE;
+
+ /** Returns the number of rows for the children of the given @a parentIdx. */
+ virtual int rowCount(const QModelIndex &parentIdx = QModelIndex()) const RT_OVERRIDE;
+ /** Returns the number of columns for the children of the given @a parentIdx. */
+ virtual int columnCount(const QModelIndex &parentIdx = QModelIndex()) const RT_OVERRIDE;
+
+ /** Returns the item flags for the given @a idx. */
+ virtual Qt::ItemFlags flags(const QModelIndex &idx) const RT_OVERRIDE;
+ /** Returns the data for the given @a iRole and @a iSection in the header with the specified @a enmOrientation. */
+ virtual QVariant headerData(int iSection, Qt::Orientation enmOrientation, int iRole) const RT_OVERRIDE;
+
+ /** Defines the @a iRole data for the item at @a idx to @a value. */
+ virtual bool setData(const QModelIndex &idx, const QVariant &value, int iRole) RT_OVERRIDE;
+ /** Returns the data stored under the given @a iRole for the item referred to by the @a idx. */
+ virtual QVariant data(const QModelIndex &idx, int iRole = Qt::DisplayRole) const RT_OVERRIDE;
+
+ /** Returns a model index for the buddy of the item represented by @a idx. */
+ virtual QModelIndex buddy(const QModelIndex &idx) const RT_OVERRIDE;
+
+ /** Restores the default values for the item with the given @a parentIdx. */
+ void restoreDefaults(QModelIndex parentIdx = QModelIndex());
+
+ /** Cache currently stored values. */
+ void putBack();
+
+ void setVirtualSystemBaseFolder(const QString& path);
+
+ /** Defines the list of VSD @a hints. */
+ void setVsdHints(const AbstractVSDParameterList &hints);
+ /** Returns a name hint for certain VSD @a enmType. */
+ QString nameHint(KVirtualSystemDescriptionType enmType) const;
+ /** Returns a kind hint for certain VSD @a enmType. */
+ AbstractVSDParameterKind kindHint(KVirtualSystemDescriptionType enmType) const;
+ /** Returns a value hint for certain VSD @a enmType. */
+ QVariant getHint(KVirtualSystemDescriptionType enmType) const;
+
+private:
+
+ /** Holds the list of VSD hints. */
+ AbstractVSDParameterList m_listVsdHints;
+
+ /** Holds the root item reference. */
+ UIApplianceModelItem *m_pRootItem;
+};
+
+
+/** QItemDelegate subclass used to create various Appliance model editors. */
+class UIApplianceDelegate : public QItemDelegate
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs the Appliance Delegate.
+ * @param pProxy Brings the proxy model reference used to redirect requests to. */
+ UIApplianceDelegate(QAbstractProxyModel *pProxy);
+
+ /** Returns the widget used to edit the item specified by @a idx for editing.
+ * @param pParent Brings the parent to be assigned for newly created editor.
+ * @param styleOption Bring the style option set for the newly created editor. */
+ virtual QWidget *createEditor(QWidget *pParent, const QStyleOptionViewItem &styleOption, const QModelIndex &idx) const RT_OVERRIDE;
+
+ /** Defines the contents of the given @a pEditor to the data for the item at the given @a idx. */
+ virtual void setEditorData(QWidget *pEditor, const QModelIndex &idx) const RT_OVERRIDE;
+ /** Defines the data for the item at the given @a idx in the @a pModel to the contents of the given @a pEditor. */
+ virtual void setModelData(QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex &idx) const RT_OVERRIDE;
+
+ /** Updates the geometry of the @a pEditor for the item with the given @a idx, according to the rectangle specified in the @a styleOption. */
+ virtual void updateEditorGeometry(QWidget *pEditor, const QStyleOptionViewItem &styleOption, const QModelIndex &idx) const RT_OVERRIDE;
+
+ /** Returns the size hint for the item at the given @a idx and specified @a styleOption. */
+ virtual QSize sizeHint(const QStyleOptionViewItem &styleOption, const QModelIndex &idx) const RT_OVERRIDE;
+
+protected:
+
+#ifdef VBOX_WS_MAC
+ /** Filters @a pEvent if this object has been installed as an event filter for the watched @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+#endif
+
+private:
+
+ /** Holds the proxy model reference used to redirect requests to. */
+ QAbstractProxyModel *m_pProxy;
+};
+
+
+/** QSortFilterProxyModel subclass used as the Appliance Sorting Proxy model. */
+class UIApplianceSortProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs the Appliance Sorting Proxy model passing @a pParent to the base-class. */
+ UIApplianceSortProxyModel(QObject *pParent = 0);
+
+protected:
+
+ /** Returns whether item in the row indicated by the given @a iSourceRow and @a srcParenIdx should be included in the model. */
+ virtual bool filterAcceptsRow(int iSourceRow, const QModelIndex &srcParenIdx) const RT_OVERRIDE;
+
+ /** Returns whether value of the item referred to by the given index @a leftIdx is less
+ * than the value of the item referred to by the given index @a rightIdx. */
+ virtual bool lessThan(const QModelIndex &leftIdx, const QModelIndex &rightIdx) const RT_OVERRIDE;
+
+ /** Holds the array of sorted Virtual System Description types. */
+ static KVirtualSystemDescriptionType s_aSortList[];
+
+ /** Holds the filtered list of Virtual System Description types. */
+ QList<KVirtualSystemDescriptionType> m_aFilteredList;
+};
+
+
+/** QWidget subclass used as the Appliance Editor widget. */
+class UIApplianceEditorWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs the Appliance Editor widget passing @a pParent to the base-class. */
+ UIApplianceEditorWidget(QWidget *pParent = 0);
+
+ /** Clears everything. */
+ void clear();
+
+ /** Defines @a comAppliance wrapper instance. */
+ virtual void setAppliance(const CAppliance &comAppliance);
+
+ /** Defines the list of VSD @a hints. */
+ void setVsdHints(const AbstractVSDParameterList &hints);
+
+ /** Defines virtual system base folder @a strPath. */
+ void setVirtualSystemBaseFolder(const QString &strPath);
+
+ /** Returns the minimum guest RAM. */
+ static int minGuestRAM() { return m_minGuestRAM; }
+ /** Returns the maximum guest RAM. */
+ static int maxGuestRAM() { return m_maxGuestRAM; }
+ /** Returns the minimum guest CPU count. */
+ static int minGuestCPUCount() { return m_minGuestCPUCount; }
+ /** Returns the maximum guest CPU count. */
+ static int maxGuestCPUCount() { return m_maxGuestCPUCount; }
+
+public slots:
+
+ /** Restores the default values. */
+ void restoreDefaults();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Holds the currently set appliance reference. */
+ CAppliance m_comAppliance;
+
+ /** Holds the list of VSD hints. */
+ AbstractVSDParameterList m_listVsdHints;
+
+ /** Holds the Appliance model reference. */
+ UIApplianceModel *m_pModel;
+
+ QVBoxLayout *m_pLayout;
+
+ /** Holds the information pane instance. */
+ QWidget *m_pPaneInformation;
+ /** Holds the settings tree-view instance. */
+ QITreeView *m_pTreeViewSettings;
+
+ /** Holds the warning pane instance. */
+ QWidget *m_pPaneWarning;
+ /** Holds the warning label instance. */
+ QLabel *m_pLabelWarning;
+ /** Holds the warning browser instance. */
+ QTextEdit *m_pTextEditWarning;
+
+private:
+
+ /** Performs Appliance settings initialization. */
+ static void initSystemSettings();
+
+ /** Holds the minimum guest RAM. */
+ static int m_minGuestRAM;
+ /** Holds the maximum guest RAM. */
+ static int m_maxGuestRAM;
+ /** Holds the minimum guest CPU count. */
+ static int m_minGuestCPUCount;
+ /** Holds the maximum guest CPU count. */
+ static int m_maxGuestCPUCount;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIApplianceEditorWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceExportEditorWidget.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceExportEditorWidget.cpp
new file mode 100644
index 00000000..86ebcc4a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceExportEditorWidget.cpp
@@ -0,0 +1,125 @@
+/* $Id: UIApplianceExportEditorWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIApplianceExportEditorWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QTextEdit>
+
+/* GUI includes: */
+#include "QITreeView.h"
+#include "UIApplianceExportEditorWidget.h"
+#include "UIMessageCenter.h"
+
+
+/** UIApplianceSortProxyModel subclass for Export Appliance wizard. */
+class ExportSortProxyModel : public UIApplianceSortProxyModel
+{
+public:
+
+ /** Constructs proxy model passing @a pParent to the base-class. */
+ ExportSortProxyModel(QObject *pParent = 0)
+ : UIApplianceSortProxyModel(pParent)
+ {
+ m_aFilteredList
+ << KVirtualSystemDescriptionType_OS
+ << KVirtualSystemDescriptionType_CPU
+ << KVirtualSystemDescriptionType_Memory
+ << KVirtualSystemDescriptionType_Floppy
+ << KVirtualSystemDescriptionType_CDROM
+ << KVirtualSystemDescriptionType_USBController
+ << KVirtualSystemDescriptionType_SoundCard
+ << KVirtualSystemDescriptionType_NetworkAdapter
+ << KVirtualSystemDescriptionType_HardDiskControllerIDE
+ << KVirtualSystemDescriptionType_HardDiskControllerSATA
+ << KVirtualSystemDescriptionType_HardDiskControllerSCSI
+ << KVirtualSystemDescriptionType_HardDiskControllerVirtioSCSI
+ << KVirtualSystemDescriptionType_HardDiskControllerSAS
+ << KVirtualSystemDescriptionType_HardDiskControllerNVMe
+ << KVirtualSystemDescriptionType_CloudProfileName;
+ }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIApplianceExportEditorWidget implementation. *
+*********************************************************************************************************************************/
+
+UIApplianceExportEditorWidget::UIApplianceExportEditorWidget(QWidget *pParent /* = 0 */)
+ : UIApplianceEditorWidget(pParent)
+{
+}
+
+void UIApplianceExportEditorWidget::setAppliance(const CAppliance &comAppliance)
+{
+ /* Cleanup previous stuff: */
+ clear();
+
+ /* Call to base-class: */
+ UIApplianceEditorWidget::setAppliance(comAppliance);
+
+ /* Prepare model: */
+ QVector<CVirtualSystemDescription> vsds = m_comAppliance.GetVirtualSystemDescriptions();
+ m_pModel = new UIApplianceModel(vsds, m_pTreeViewSettings);
+ if (m_pModel)
+ {
+ m_pModel->setVsdHints(m_listVsdHints);
+
+ /* Create proxy model: */
+ ExportSortProxyModel *pProxy = new ExportSortProxyModel(m_pModel);
+ if (pProxy)
+ {
+ pProxy->setSourceModel(m_pModel);
+ pProxy->sort(ApplianceViewSection_Description, Qt::DescendingOrder);
+
+ /* Set our own model: */
+ m_pTreeViewSettings->setModel(pProxy);
+ /* Set our own delegate: */
+ UIApplianceDelegate *pDelegate = new UIApplianceDelegate(pProxy);
+ if (pDelegate)
+ m_pTreeViewSettings->setItemDelegate(pDelegate);
+
+ /* For now we hide the original column. This data is displayed as tooltip also. */
+ m_pTreeViewSettings->setColumnHidden(ApplianceViewSection_OriginalValue, true);
+ m_pTreeViewSettings->expandAll();
+ /* Set model root index and make it current: */
+ m_pTreeViewSettings->setRootIndex(pProxy->mapFromSource(m_pModel->root()));
+ m_pTreeViewSettings->setCurrentIndex(pProxy->mapFromSource(m_pModel->root()));
+ }
+ }
+
+ /* Check for warnings & if there are one display them: */
+ const QVector<QString> warnings = m_comAppliance.GetWarnings();
+ const bool fWarningsEnabled = warnings.size() > 0;
+ foreach (const QString &strText, warnings)
+ m_pTextEditWarning->append("- " + strText);
+ m_pPaneWarning->setVisible(fWarningsEnabled);
+}
+
+void UIApplianceExportEditorWidget::prepareExport()
+{
+ if (m_comAppliance.isNotNull())
+ m_pModel->putBack();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceExportEditorWidget.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceExportEditorWidget.h
new file mode 100644
index 00000000..3f131c32
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceExportEditorWidget.h
@@ -0,0 +1,57 @@
+/* $Id: UIApplianceExportEditorWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIApplianceExportEditorWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIApplianceExportEditorWidget_h
+#define FEQT_INCLUDED_SRC_widgets_UIApplianceExportEditorWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIApplianceEditorWidget.h"
+
+/* COM includes: */
+#include "CAppliance.h"
+
+/** UIApplianceEditorWidget subclass for Export Appliance wizard. */
+class UIApplianceExportEditorWidget: public UIApplianceEditorWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs widget passing @a pParent to the base-class. */
+ UIApplianceExportEditorWidget(QWidget *pParent = 0);
+
+ /** Assigns @a comAppliance and populates widget contents. */
+ virtual void setAppliance(const CAppliance &comAppliance) /* override final */;
+
+ /** Prepares export by pushing edited data back to appliance. */
+ void prepareExport();
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIApplianceExportEditorWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceImportEditorWidget.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceImportEditorWidget.cpp
new file mode 100644
index 00000000..739716b3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceImportEditorWidget.cpp
@@ -0,0 +1,108 @@
+/* $Id: UIApplianceImportEditorWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIApplianceImportEditorWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QTextEdit>
+
+/* GUI includes: */
+#include "QITreeView.h"
+#include "UIApplianceImportEditorWidget.h"
+#include "UIMessageCenter.h"
+
+
+/** UIApplianceSortProxyModel subclass for Export Appliance wizard. */
+class ImportSortProxyModel : public UIApplianceSortProxyModel
+{
+public:
+
+ /** Constructs proxy model passing @a pParent to the base-class. */
+ ImportSortProxyModel(QObject *pParent = 0)
+ : UIApplianceSortProxyModel(pParent)
+ {
+ m_aFilteredList << KVirtualSystemDescriptionType_License;
+ }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIApplianceImportEditorWidget implementation. *
+*********************************************************************************************************************************/
+
+UIApplianceImportEditorWidget::UIApplianceImportEditorWidget(QWidget *pParent /* = 0 */)
+ : UIApplianceEditorWidget(pParent)
+{
+}
+
+void UIApplianceImportEditorWidget::setAppliance(const CAppliance &comAppliance)
+{
+ /* Cleanup previous stuff: */
+ clear();
+
+ /* Call to base-class: */
+ UIApplianceEditorWidget::setAppliance(comAppliance);
+
+ /* Prepare model: */
+ QVector<CVirtualSystemDescription> vsds = m_comAppliance.GetVirtualSystemDescriptions();
+ m_pModel = new UIApplianceModel(vsds, m_pTreeViewSettings);
+ if (m_pModel)
+ {
+ /* Create proxy model: */
+ ImportSortProxyModel *pProxy = new ImportSortProxyModel(m_pModel);
+ if (pProxy)
+ {
+ pProxy->setSourceModel(m_pModel);
+ pProxy->sort(ApplianceViewSection_Description, Qt::DescendingOrder);
+
+ /* Set our own model: */
+ m_pTreeViewSettings->setModel(pProxy);
+ /* Set our own delegate: */
+ UIApplianceDelegate *pDelegate = new UIApplianceDelegate(pProxy);
+ if (pDelegate)
+ m_pTreeViewSettings->setItemDelegate(pDelegate);
+
+ /* For now we hide the original column. This data is displayed as tooltip also. */
+ m_pTreeViewSettings->setColumnHidden(ApplianceViewSection_OriginalValue, true);
+ m_pTreeViewSettings->expandAll();
+ /* Set model root index and make it current: */
+ m_pTreeViewSettings->setRootIndex(pProxy->mapFromSource(m_pModel->root()));
+ m_pTreeViewSettings->setCurrentIndex(pProxy->mapFromSource(m_pModel->root()));
+ }
+ }
+
+ /* Check for warnings & if there are one display them: */
+ const QVector<QString> warnings = m_comAppliance.GetWarnings();
+ const bool fWarningsEnabled = warnings.size() > 0;
+ foreach (const QString &strText, warnings)
+ m_pTextEditWarning->append("- " + strText);
+ m_pPaneWarning->setVisible(fWarningsEnabled);
+}
+
+void UIApplianceImportEditorWidget::prepareImport()
+{
+ if (m_comAppliance.isNotNull())
+ m_pModel->putBack();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceImportEditorWidget.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceImportEditorWidget.h
new file mode 100644
index 00000000..091f71e2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIApplianceImportEditorWidget.h
@@ -0,0 +1,57 @@
+/* $Id: UIApplianceImportEditorWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIApplianceImportEditorWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIApplianceImportEditorWidget_h
+#define FEQT_INCLUDED_SRC_widgets_UIApplianceImportEditorWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIApplianceEditorWidget.h"
+
+/* COM includes: */
+#include "CAppliance.h"
+
+/** UIApplianceEditorWidget subclass for Import Appliance wizard. */
+class UIApplianceImportEditorWidget: public UIApplianceEditorWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs widget passing @a pParent to the base-class. */
+ UIApplianceImportEditorWidget(QWidget *pParent = 0);
+
+ /** Assigns @a comAppliance and populates widget contents. */
+ virtual void setAppliance(const CAppliance &comAppliance) /* override final */;
+
+ /** Prepares import by pushing edited data back to appliance. */
+ void prepareImport();
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIApplianceImportEditorWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIEmptyFilePathSelector.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIEmptyFilePathSelector.cpp
new file mode 100644
index 00000000..37f0382a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIEmptyFilePathSelector.cpp
@@ -0,0 +1,272 @@
+/* $Id: UIEmptyFilePathSelector.cpp $ */
+/** @file
+ * VBox Qt GUI - UIEmptyFilePathSelector class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Local includes */
+#include "QIFileDialog.h"
+#include "QIToolButton.h"
+#include "QILabel.h"
+#include "QILineEdit.h"
+#include "UIIconPool.h"
+#include "UIEmptyFilePathSelector.h"
+#include "UICommon.h"
+
+/* Global includes */
+#include <iprt/assert.h>
+#include <QAction>
+#include <QApplication>
+#include <QClipboard>
+#include <QDir>
+#include <QFocusEvent>
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QTimer>
+
+
+UIEmptyFilePathSelector::UIEmptyFilePathSelector (QWidget *aParent /* = NULL */)
+ : QIWithRetranslateUI<QWidget> (aParent)
+ , mPathWgt (NULL)
+ , mLabel (NULL)
+ , mMode (UIEmptyFilePathSelector::Mode_File_Open)
+ , mLineEdit (NULL)
+ , m_fButtonToolTipSet(false)
+ , mHomeDir (QDir::current().absolutePath())
+ , mIsModified (false)
+{
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+
+ mMainLayout = new QHBoxLayout (this);
+ mMainLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ mMainLayout->setSpacing(5);
+#endif
+
+ mSelectButton = new QToolButton(this);
+#ifdef VBOX_WS_MAC
+ mSelectButton->setStyleSheet("QToolButton { border: 0px none black; margin: 0px 0px 0px 0px; } QToolButton::menu-indicator {image: none;}");
+#else
+ mSelectButton->setAutoRaise(true);
+#endif
+ mSelectButton->setIcon(UIIconPool::iconSet(":/select_file_16px.png", ":/select_file_disabled_16px.png"));
+ connect(mSelectButton, &QToolButton::clicked, this, &UIEmptyFilePathSelector::choose);
+ mMainLayout->addWidget(mSelectButton);
+
+ setEditable (false);
+
+ retranslateUi();
+}
+
+void UIEmptyFilePathSelector::setMode (UIEmptyFilePathSelector::Mode aMode)
+{
+ mMode = aMode;
+}
+
+UIEmptyFilePathSelector::Mode UIEmptyFilePathSelector::mode() const
+{
+ return mMode;
+}
+
+void UIEmptyFilePathSelector::setButtonPosition (ButtonPosition aPos)
+{
+ if (aPos == LeftPosition)
+ {
+ mMainLayout->setDirection (QBoxLayout::LeftToRight);
+ setTabOrder (mSelectButton, mPathWgt);
+ }
+ else
+ {
+ mMainLayout->setDirection (QBoxLayout::RightToLeft);
+ setTabOrder (mPathWgt, mSelectButton);
+ }
+}
+
+UIEmptyFilePathSelector::ButtonPosition UIEmptyFilePathSelector::buttonPosition() const
+{
+ return mMainLayout->direction() == QBoxLayout::LeftToRight ? LeftPosition : RightPosition;
+}
+
+void UIEmptyFilePathSelector::setEditable (bool aOn)
+{
+ if (mPathWgt)
+ {
+ delete mPathWgt;
+ mLabel = NULL;
+ mLineEdit = NULL;
+ }
+
+ if (aOn)
+ {
+ mPathWgt = mLineEdit = new QILineEdit (this);
+ setFocusProxy(mLineEdit);
+ connect (mLineEdit, SIGNAL (textChanged (const QString&)),
+ this, SLOT (textChanged (const QString&)));
+ }
+ else
+ {
+ mPathWgt = mLabel = new QILabel (this);
+ mLabel->setWordWrap (true);
+ }
+ mMainLayout->addWidget (mPathWgt, 2);
+ setButtonPosition (buttonPosition());
+
+ setPath (mPath);
+}
+
+bool UIEmptyFilePathSelector::isEditable() const
+{
+ return mLabel ? false : true;
+}
+
+void UIEmptyFilePathSelector::setChooserVisible (bool aOn)
+{
+ mSelectButton->setVisible (aOn);
+}
+
+bool UIEmptyFilePathSelector::isChooserVisible() const
+{
+ return mSelectButton->isVisible();
+}
+
+void UIEmptyFilePathSelector::setPath (const QString& aPath)
+{
+ QString tmpPath = QDir::toNativeSeparators (aPath);
+ if (mLabel)
+ mLabel->setText (QString ("<compact elipsis=\"start\">%1</compact>").arg (tmpPath));
+ else if (mLineEdit)
+ mLineEdit->setText (tmpPath);
+ textChanged(tmpPath);
+}
+
+QString UIEmptyFilePathSelector::path() const
+{
+ return mPath;
+}
+
+void UIEmptyFilePathSelector::setDefaultSaveExt (const QString &aExt)
+{
+ mDefaultSaveExt = aExt;
+}
+
+QString UIEmptyFilePathSelector::defaultSaveExt() const
+{
+ return mDefaultSaveExt;
+}
+
+void UIEmptyFilePathSelector::setChooseButtonToolTip(const QString &strToolTip)
+{
+ m_fButtonToolTipSet = !strToolTip.isEmpty();
+ mSelectButton->setToolTip(strToolTip);
+}
+
+QString UIEmptyFilePathSelector::chooseButtonToolTip() const
+{
+ return mSelectButton->toolTip();
+}
+
+void UIEmptyFilePathSelector::setFileDialogTitle (const QString& aTitle)
+{
+ mFileDialogTitle = aTitle;
+}
+
+QString UIEmptyFilePathSelector::fileDialogTitle() const
+{
+ return mFileDialogTitle;
+}
+
+void UIEmptyFilePathSelector::setFileFilters (const QString& aFilters)
+{
+ mFileFilters = aFilters;
+}
+
+QString UIEmptyFilePathSelector::fileFilters() const
+{
+ return mFileFilters;
+}
+
+void UIEmptyFilePathSelector::setHomeDir (const QString& aDir)
+{
+ mHomeDir = aDir;
+}
+
+QString UIEmptyFilePathSelector::homeDir() const
+{
+ return mHomeDir;
+}
+
+void UIEmptyFilePathSelector::retranslateUi()
+{
+ if (!m_fButtonToolTipSet)
+ mSelectButton->setToolTip(tr("Choose..."));
+}
+
+void UIEmptyFilePathSelector::choose()
+{
+ QString path = mPath;
+
+ /* Check whether we have file-name information available: */
+ const QString strFileName = QFileInfo(path).fileName();
+
+ /* Preparing initial directory. */
+ QString initDir = path.isNull() ? mHomeDir :
+ QIFileDialog::getFirstExistingDir (path);
+ if (initDir.isNull())
+ initDir = mHomeDir;
+
+ /* Append file-name information if any: */
+ if (!strFileName.isEmpty())
+ initDir = QDir(initDir).absoluteFilePath(strFileName);
+
+ switch (mMode)
+ {
+ case UIEmptyFilePathSelector::Mode_File_Open:
+ path = QIFileDialog::getOpenFileName (initDir, mFileFilters, window(), mFileDialogTitle); break;
+ case UIEmptyFilePathSelector::Mode_File_Save:
+ {
+ path = QIFileDialog::getSaveFileName (initDir, mFileFilters, window(), mFileDialogTitle);
+ if (!path.isEmpty() && QFileInfo (path).suffix().isEmpty())
+ path = QString ("%1.%2").arg (path).arg (mDefaultSaveExt);
+ break;
+ }
+ case UIEmptyFilePathSelector::Mode_Folder:
+ path = QIFileDialog::getExistingDirectory (initDir, window(), mFileDialogTitle); break;
+ }
+ if (path.isEmpty())
+ return;
+
+ path.remove(QRegularExpression("[\\\\/]$"));
+ setPath (path);
+}
+
+void UIEmptyFilePathSelector::textChanged (const QString& aPath)
+{
+ const QString oldPath = mPath;
+ mPath = aPath;
+ if (oldPath != mPath)
+ {
+ mIsModified = true;
+ emit pathChanged (mPath);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIEmptyFilePathSelector.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIEmptyFilePathSelector.h
new file mode 100644
index 00000000..d99d809e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIEmptyFilePathSelector.h
@@ -0,0 +1,134 @@
+/* $Id: UIEmptyFilePathSelector.h $ */
+/** @file
+ * VBox Qt GUI - UIEmptyFilePathSelector class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIEmptyFilePathSelector_h
+#define FEQT_INCLUDED_SRC_widgets_UIEmptyFilePathSelector_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* VBox includes */
+#include "QIWithRetranslateUI.h"
+
+/* Qt includes */
+#include <QComboBox>
+
+/* VBox forward declarations */
+class QILabel;
+class QILineEdit;
+
+/* Qt forward declarations */
+class QHBoxLayout;
+class QAction;
+class QToolButton;
+
+
+class UIEmptyFilePathSelector: public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ enum Mode
+ {
+ Mode_Folder = 0,
+ Mode_File_Open,
+ Mode_File_Save
+ };
+
+ enum ButtonPosition
+ {
+ LeftPosition,
+ RightPosition
+ };
+
+ UIEmptyFilePathSelector (QWidget *aParent = NULL);
+
+ void setMode (UIEmptyFilePathSelector::Mode aMode);
+ UIEmptyFilePathSelector::Mode mode() const;
+
+ void setButtonPosition (ButtonPosition aPos);
+ ButtonPosition buttonPosition() const;
+
+ void setEditable (bool aOn);
+ bool isEditable() const;
+
+ void setChooserVisible (bool aOn);
+ bool isChooserVisible() const;
+
+ QString path() const;
+
+ void setDefaultSaveExt (const QString &aExt);
+ QString defaultSaveExt() const;
+
+ bool isModified () const { return mIsModified; }
+ void resetModified () { mIsModified = false; }
+
+ void setChooseButtonToolTip(const QString &strToolTip);
+ QString chooseButtonToolTip() const;
+
+ void setFileDialogTitle (const QString& aTitle);
+ QString fileDialogTitle() const;
+
+ void setFileFilters (const QString& aFilters);
+ QString fileFilters() const;
+
+ void setHomeDir (const QString& aDir);
+ QString homeDir() const;
+
+signals:
+ void pathChanged (QString);
+
+public slots:
+ void setPath (const QString& aPath);
+
+protected:
+ void retranslateUi();
+
+private slots:
+ void choose();
+ void textChanged (const QString& aPath);
+
+private:
+ /* Private member vars */
+ QHBoxLayout *mMainLayout;
+ QWidget *mPathWgt;
+ QILabel *mLabel;
+ UIEmptyFilePathSelector::Mode mMode;
+ QILineEdit *mLineEdit;
+ QToolButton *mSelectButton;
+ bool m_fButtonToolTipSet;
+ QString mFileDialogTitle;
+ QString mFileFilters;
+ QString mDefaultSaveExt;
+ QString mHomeDir;
+ bool mIsModified;
+ QString mPath;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIEmptyFilePathSelector_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIFilePathSelector.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIFilePathSelector.cpp
new file mode 100644
index 00000000..43fbc76b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIFilePathSelector.cpp
@@ -0,0 +1,665 @@
+/* $Id: UIFilePathSelector.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFilePathSelector class implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QApplication>
+#include <QClipboard>
+#include <QDir>
+#include <QFocusEvent>
+#include <QHBoxLayout>
+#include <QLineEdit>
+#ifdef VBOX_WS_WIN
+# include <QListView>
+#endif
+#include <QRegExp>
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "QILabel.h"
+#include "QILineEdit.h"
+#include "QIToolButton.h"
+#include "UICommon.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIFilePathSelector.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/** Returns first position of difference between passed strings. */
+static int differFrom(const QString &str1, const QString &str2)
+{
+ if (str1 == str2)
+ return -1;
+
+ int iMinLength = qMin(str1.size(), str2.size());
+ int iIndex = 0;
+ for (iIndex = 0; iIndex < iMinLength; ++iIndex)
+ if (str1[iIndex] != str2[iIndex])
+ break;
+ return iIndex;
+}
+
+UIFilePathSelector::UIFilePathSelector(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QIComboBox>(pParent)
+ , m_enmMode(Mode_Folder)
+ , m_strInitialPath(QDir::current().absolutePath())
+ , m_fResetEnabled(true)
+ , m_fEditable(true)
+ , m_fModified(false)
+ , m_fEditableMode(false)
+ , m_fMouseAwaited(false)
+ , m_fToolTipOverriden(false)
+ , m_pCopyAction(new QAction(this))
+ , m_iRecentListSeparatorPosition(ResetId + 1)
+ , m_enmRecentMediaListType(UIMediumDeviceType_Invalid)
+{
+#ifdef VBOX_WS_WIN
+ // WORKAROUND:
+ // On at least Windows host there is a bug with
+ // the QListView which doesn't take into account
+ // the item size change caused by assigning item's
+ // icon of another size or unassigning icon at all.
+ if (view()->inherits("QListView"))
+ qobject_cast<QListView*>(view())->setUniformItemSizes(true);
+#endif /* VBOX_WS_WIN */
+
+ /* Populate items: */
+ insertItem(PathId, "");
+ insertItem(SelectId, "");
+ insertItem(ResetId, "");
+
+ /* Attaching known icons: */
+ setItemIcon(SelectId, UIIconPool::iconSet(":/select_file_16px.png"));
+ setItemIcon(ResetId, UIIconPool::iconSet(":/eraser_16px.png"));
+
+ /* Setup context menu: */
+ addAction(m_pCopyAction);
+ m_pCopyAction->setShortcut(QKeySequence(QKeySequence::Copy));
+ m_pCopyAction->setShortcutContext(Qt::WidgetShortcut);
+
+ /* Initial setup: */
+ setInsertPolicy(QComboBox::NoInsert);
+ setContextMenuPolicy(Qt::ActionsContextMenu);
+ setMinimumWidth(200);
+
+ /* Setup connections: */
+ connect(this, static_cast<void(UIFilePathSelector::*)(int)>(&UIFilePathSelector::activated), this, &UIFilePathSelector::onActivated);
+ connect(m_pCopyAction, &QAction::triggered, this, &UIFilePathSelector::copyToClipboard);
+ connect(&uiCommon(), &UICommon::sigRecentMediaListUpdated, this, &UIFilePathSelector::sltRecentMediaListUpdated);
+
+ /* Editable by default: */
+ setEditable(true);
+
+ /* Applying language settings: */
+ retranslateUi();
+}
+
+void UIFilePathSelector::setMode(Mode enmMode)
+{
+ m_enmMode = enmMode;
+
+ retranslateUi();
+}
+
+void UIFilePathSelector::setEditable(bool fEditable)
+{
+ m_fEditable = fEditable;
+
+ if (m_fEditable)
+ {
+ QIComboBox::setEditable(true);
+
+ /* Install combo-box event-filter: */
+ Assert(comboBox());
+ comboBox()->installEventFilter(this);
+
+ /* Install line-edit connection/event-filter: */
+ Assert(lineEdit());
+ connect(lineEdit(), &QLineEdit::textEdited,
+ this, &UIFilePathSelector::onTextEdited);
+ lineEdit()->installEventFilter(this);
+ }
+ else
+ {
+ if (lineEdit())
+ {
+ /* Remove line-edit event-filter/connection: */
+ lineEdit()->removeEventFilter(this);
+ disconnect(lineEdit(), &QLineEdit::textEdited,
+ this, &UIFilePathSelector::onTextEdited);
+ }
+ if (comboBox())
+ {
+ /* Remove combo-box event-filter: */
+ comboBox()->removeEventFilter(this);
+ }
+ QIComboBox::setEditable(false);
+ }
+}
+
+void UIFilePathSelector::setResetEnabled(bool fEnabled)
+{
+ /* Cache requested state: */
+ m_fResetEnabled = fEnabled;
+
+ /* Update recent list separator position: */
+ m_iRecentListSeparatorPosition = fEnabled ? ResetId + 1 : ResetId;
+
+ if (!fEnabled && count() - 1 == ResetId)
+ removeItem(ResetId);
+ else if (fEnabled && count() - 1 == ResetId - 1)
+ {
+ insertItem(ResetId, "");
+ setItemIcon(ResetId, UIIconPool::iconSet(":/eraser_16px.png"));
+ }
+
+ sltRecentMediaListUpdated(m_enmRecentMediaListType);
+ retranslateUi();
+}
+
+bool UIFilePathSelector::isValid() const
+{
+ if (m_strPath.isNull() || m_strPath.isEmpty())
+ return false;
+ QFileInfo fileInfo(m_strPath);
+ if (!fileInfo.exists() || !fileInfo.isReadable())
+ return false;
+ return true;
+}
+
+void UIFilePathSelector::setToolTip(const QString &strToolTip)
+{
+ /* Call to base-class: */
+ QIComboBox::setToolTip(strToolTip);
+
+ /* Remember if the tool-tip overriden: */
+ m_fToolTipOverriden = !toolTip().isEmpty();
+}
+
+void UIFilePathSelector::setDefaultPath(const QString &strDefaultPath)
+{
+ if (m_strDefaultPath == strDefaultPath)
+ return;
+ m_strDefaultPath = strDefaultPath;
+ if (currentIndex() == ResetId)
+ setPath(m_strDefaultPath);
+}
+
+const QString& UIFilePathSelector::defaultPath() const
+{
+ return m_strDefaultPath;
+}
+
+void UIFilePathSelector::setRecentMediaListType(UIMediumDeviceType enmMediumType)
+{
+ m_enmRecentMediaListType = enmMediumType;
+ sltRecentMediaListUpdated(enmMediumType);
+}
+
+UIMediumDeviceType UIFilePathSelector::recentMediaListType() const
+{
+ return m_enmRecentMediaListType;
+}
+
+void UIFilePathSelector::setPath(const QString &strPath, bool fRefreshText /* = true */)
+{
+ m_strPath = strPath.isEmpty() ? QString() :
+ QDir::toNativeSeparators(strPath);
+ if (fRefreshText)
+ refreshText();
+}
+
+bool UIFilePathSelector::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* If the object is private combo-box: */
+ if (pObject == comboBox())
+ {
+ /* Handle focus events related to private child: */
+ switch (pEvent->type())
+ {
+ case QEvent::FocusIn: focusInEvent(static_cast<QFocusEvent*>(pEvent)); break;
+ case QEvent::FocusOut: focusOutEvent(static_cast<QFocusEvent*>(pEvent)); break;
+ default: break;
+ }
+ }
+
+ /* If the object is private line-edit: */
+ if (pObject == lineEdit())
+ {
+ if (m_fMouseAwaited && (pEvent->type() == QEvent::MouseButtonPress))
+ QMetaObject::invokeMethod(this, "refreshText", Qt::QueuedConnection);
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QIComboBox>::eventFilter(pObject, pEvent);
+}
+
+void UIFilePathSelector::resizeEvent(QResizeEvent *pEvent)
+{
+ QIWithRetranslateUI<QIComboBox>::resizeEvent(pEvent);
+ refreshText();
+}
+
+void UIFilePathSelector::focusInEvent(QFocusEvent *pEvent)
+{
+ if (isPathSelected())
+ {
+ if (m_fEditable)
+ m_fEditableMode = true;
+ if (pEvent->reason() == Qt::MouseFocusReason)
+ m_fMouseAwaited = true;
+ else
+ refreshText();
+ }
+ QIWithRetranslateUI<QIComboBox>::focusInEvent(pEvent);
+}
+
+void UIFilePathSelector::focusOutEvent(QFocusEvent *pEvent)
+{
+ if (isPathSelected())
+ {
+ m_fEditableMode = false;
+ refreshText();
+ }
+ QIWithRetranslateUI<QIComboBox>::focusOutEvent(pEvent);
+}
+
+void UIFilePathSelector::retranslateUi()
+{
+ /* Retranslate copy action: */
+ m_pCopyAction->setText(tr("&Copy"));
+
+ /* Retranslate 'select' item: */
+ setItemText(SelectId, tr("Other..."));
+
+ /* Retranslate 'reset' item: */
+ if (count() - 1 == ResetId)
+ setItemText(ResetId, tr("Reset"));
+
+ /* Set tool-tips of the above two items based on the mode: */
+ switch (m_enmMode)
+ {
+ case Mode_Folder:
+ setItemData(SelectId,
+ tr("Displays a window to select a different folder."),
+ Qt::ToolTipRole);
+ setItemData(ResetId,
+ tr("Resets the folder path to the default value."),
+ Qt::ToolTipRole);
+ break;
+ case Mode_File_Open:
+ case Mode_File_Save:
+ setItemData(SelectId,
+ tr("Displays a window to select a different file."),
+ Qt::ToolTipRole);
+ setItemData(ResetId,
+ tr("Resets the file path to the default value."),
+ Qt::ToolTipRole);
+ break;
+ default:
+ AssertFailedBreak();
+ }
+
+ /* If selector is NOT focused => we interpret the "nothing selected"
+ * item depending on "reset to default" feature state: */
+ if (isResetEnabled())
+ {
+ /* If "reset to default" is enabled: */
+ m_strNoneText = tr("<reset to default>");
+ m_strNoneToolTip = tr("The actual default path value will be displayed after "
+ "accepting the changes and opening this window again.");
+ }
+ else
+ {
+ /* If "reset to default" is NOT enabled: */
+ m_strNoneText = tr("<not selected>");
+ m_strNoneToolTip = tr("Please use the <b>Other...</b> item from the drop-down "
+ "list to select a path.");
+ }
+
+ /* Finally, retranslate current item: */
+ refreshText();
+}
+
+void UIFilePathSelector::onActivated(int iIndex)
+{
+ /* Since the presence of ResetId and position of recent list separator
+ * are dynamical now, we should control condition more carefully: */
+ if (iIndex == SelectId)
+ selectPath();
+ else if (m_fResetEnabled && iIndex == ResetId)
+ {
+ if (m_strDefaultPath.isEmpty())
+ changePath(QString());
+ else
+ changePath(m_strDefaultPath);
+ }
+ else if (iIndex >= m_iRecentListSeparatorPosition)
+ {
+ /* Switch back to Path item early, lineEdit() in refreshText()
+ * should be related to this exactly item: */
+ setCurrentIndex(PathId);
+ changePath(itemText(iIndex));
+ }
+
+ setCurrentIndex(PathId);
+ setFocus();
+}
+
+void UIFilePathSelector::onTextEdited(const QString &strPath)
+{
+ changePath(strPath, false /* refresh text? */);
+}
+
+void UIFilePathSelector::copyToClipboard()
+{
+ QString text(fullPath());
+ /* Copy the current text to the selection and global clipboard. */
+ if (QApplication::clipboard()->supportsSelection())
+ QApplication::clipboard()->setText(text, QClipboard::Selection);
+ QApplication::clipboard()->setText(text, QClipboard::Clipboard);
+}
+
+void UIFilePathSelector::changePath(const QString &strPath,
+ bool fRefreshText /* = true */)
+{
+ const QString strOldPath = QDir::toNativeSeparators(m_strPath);
+ setPath(strPath, fRefreshText);
+ if (!m_fModified && m_strPath != strOldPath)
+ m_fModified = true;
+ emit pathChanged(strPath);
+}
+
+void UIFilePathSelector::selectPath()
+{
+ /* Prepare initial directory: */
+ QString strInitPath;
+ /* If something already chosen: */
+ if (!m_strPath.isEmpty())
+ {
+ /* If that is just a single file/folder (object) name: */
+ const QString strObjectName = QFileInfo(m_strPath).fileName();
+ if (strObjectName == m_strPath)
+ {
+ /* Use the initial path: */
+ strInitPath = m_strInitialPath;
+ }
+ /* If that is full file/folder (object) path: */
+ else
+ {
+ /* Use the first existing dir of m_strPath: */
+ strInitPath = QIFileDialog::getFirstExistingDir(m_strPath);
+ }
+ /* Finally, append object name itself: */
+ strInitPath = QDir(strInitPath).absoluteFilePath(strObjectName);
+ }
+ /* Use the initial path by default: */
+ if (strInitPath.isNull())
+ strInitPath = m_strInitialPath;
+
+ /* Open the choose-file/folder dialog: */
+ QString strSelPath;
+ switch (m_enmMode)
+ {
+ case Mode_File_Open:
+ strSelPath = QIFileDialog::getOpenFileName(strInitPath, m_strFileDialogFilters, window(), m_strFileDialogTitle); break;
+ case Mode_File_Save:
+ {
+ strSelPath = QIFileDialog::getSaveFileName(strInitPath, m_strFileDialogFilters, window(), m_strFileDialogTitle);
+ if (!strSelPath.isEmpty() && QFileInfo(strSelPath).suffix().isEmpty())
+ {
+ if (m_strFileDialogDefaultSaveExtension.isEmpty())
+ strSelPath = QString("%1").arg(strSelPath);
+ else
+ strSelPath = QString("%1.%2").arg(strSelPath).arg(m_strFileDialogDefaultSaveExtension);
+ }
+ break;
+ }
+ case Mode_Folder:
+ strSelPath = QIFileDialog::getExistingDirectory(strInitPath, window(), m_strFileDialogTitle); break;
+ }
+
+ /* Do nothing if nothing chosen: */
+ if (strSelPath.isNull())
+ return;
+
+ /* Wipe out excessive slashes: */
+ strSelPath.remove(QRegularExpression("[\\\\/]$"));
+
+ /* Apply chosen path: */
+ changePath(strSelPath);
+}
+
+QIcon UIFilePathSelector::defaultIcon() const
+{
+ if (m_enmMode == Mode_Folder)
+ return generalIconPool().defaultSystemIcon(QFileIconProvider::Folder);
+ else
+ return generalIconPool().defaultSystemIcon(QFileIconProvider::File);
+}
+
+QString UIFilePathSelector::fullPath(bool fAbsolute /* = true */) const
+{
+ if (m_strPath.isNull())
+ return m_strPath;
+
+ QString strResult;
+ switch (m_enmMode)
+ {
+ case Mode_Folder:
+ strResult = fAbsolute ? QDir(m_strPath).absolutePath() :
+ QDir(m_strPath).path();
+ break;
+ case Mode_File_Open:
+ case Mode_File_Save:
+ strResult = fAbsolute ? QFileInfo(m_strPath).absoluteFilePath() :
+ QFileInfo(m_strPath).filePath();
+ break;
+ default:
+ AssertFailedBreak();
+ }
+ return QDir::toNativeSeparators(strResult);
+}
+
+QString UIFilePathSelector::shrinkText(int iWidth) const
+{
+ QString strFullText(fullPath(false));
+ if (strFullText.isEmpty())
+ return strFullText;
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int iOldSize = fontMetrics().horizontalAdvance(strFullText);
+ int iIndentSize = fontMetrics().horizontalAdvance("x...x");
+#else
+ int iOldSize = fontMetrics().width(strFullText);
+ int iIndentSize = fontMetrics().width("x...x");
+#endif
+
+ /* Compress text: */
+ int iStart = 0;
+ int iFinish = 0;
+ int iPosition = 0;
+ int iTextWidth = 0;
+ do {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ iTextWidth = fontMetrics().horizontalAdvance(strFullText);
+#else
+ iTextWidth = fontMetrics().width(strFullText);
+#endif
+ if (iTextWidth + iIndentSize > iWidth)
+ {
+ iStart = 0;
+ iFinish = strFullText.length();
+
+ /* Selecting remove position: */
+ QRegExp regExp("([\\\\/][^\\\\^/]+[\\\\/]?$)");
+ int iNewFinish = regExp.indexIn(strFullText);
+ if (iNewFinish != -1)
+ iFinish = iNewFinish;
+ iPosition = (iFinish - iStart) / 2;
+
+ if (iPosition == iFinish)
+ break;
+
+ strFullText.remove(iPosition, 1);
+ }
+ } while (iTextWidth + iIndentSize > iWidth);
+
+ strFullText.insert(iPosition, "...");
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ int newSize = fontMetrics().horizontalAdvance(strFullText);
+#else
+ int newSize = fontMetrics().width(strFullText);
+#endif
+
+ return newSize < iOldSize ? strFullText : fullPath(false);
+}
+
+void UIFilePathSelector::refreshText()
+{
+ if (m_fEditable && m_fEditableMode)
+ {
+ /* Cursor positioning variables: */
+ int iCurPos = -1;
+ int iDiffPos = -1;
+ int iFromRight = -1;
+
+ if (m_fMouseAwaited)
+ {
+ /* Store the cursor position: */
+ iCurPos = lineEdit()->cursorPosition();
+ iDiffPos = differFrom(lineEdit()->text(), m_strPath);
+ iFromRight = lineEdit()->text().size() - iCurPos;
+ }
+
+ /* In editable mode there should be no any icon
+ * and text have be corresponding real stored path
+ * which can be absolute or relative. */
+ if (lineEdit()->text() != m_strPath)
+ setItemText(PathId, m_strPath);
+ setItemIcon(PathId, QIcon());
+
+ /* Set the tool-tip: */
+ if (!m_fToolTipOverriden)
+ QIComboBox::setToolTip(fullPath());
+ setItemData(PathId, toolTip(), Qt::ToolTipRole);
+
+ if (m_fMouseAwaited)
+ {
+ m_fMouseAwaited = false;
+
+ /* Restore the position to the right of dots: */
+ if (iDiffPos != -1 && iCurPos >= iDiffPos + 3)
+ lineEdit()->setCursorPosition(lineEdit()->text().size() -
+ iFromRight);
+ /* Restore the position to the center of text: */
+ else if (iDiffPos != -1 && iCurPos > iDiffPos)
+ lineEdit()->setCursorPosition(lineEdit()->text().size() / 2);
+ /* Restore the position to the left of dots: */
+ else
+ lineEdit()->setCursorPosition(iCurPos);
+ }
+ }
+ else if (m_strPath.isNull())
+ {
+ /* If we are not in editable mode and no path is
+ * stored here - show the translated 'none' string. */
+ if (itemText(PathId) != m_strNoneText)
+ {
+ setItemText(PathId, m_strNoneText);
+ setItemIcon(PathId, QIcon());
+
+ /* Set the tool-tip: */
+ if (!m_fToolTipOverriden)
+ QIComboBox::setToolTip(m_strNoneToolTip);
+ setItemData(PathId, toolTip(), Qt::ToolTipRole);
+ }
+ }
+ else
+ {
+ /* Compress text in combobox: */
+ QStyleOptionComboBox options;
+ options.initFrom(this);
+ QRect rect = QApplication::style()->subControlRect(
+ QStyle::CC_ComboBox, &options, QStyle::SC_ComboBoxEditField);
+ setItemText(PathId, shrinkText(rect.width() - iconSize().width()));
+
+ /* Attach corresponding icon: */
+ setItemIcon(PathId, QFileInfo(m_strPath).exists() ?
+ generalIconPool().defaultFileIcon(QFileInfo(m_strPath)) :
+ defaultIcon());
+
+ /* Set the tool-tip: */
+ if (!m_fToolTipOverriden)
+ QIComboBox::setToolTip(fullPath());
+ setItemData(PathId, toolTip(), Qt::ToolTipRole);
+ }
+}
+
+void UIFilePathSelector::sltRecentMediaListUpdated(UIMediumDeviceType enmMediumType)
+{
+ /* Remove the recent media list from the end of the combo: */
+ while (count() > m_iRecentListSeparatorPosition)
+ removeItem(count() - 1);
+
+ if (enmMediumType != m_enmRecentMediaListType)
+ return;
+ QStringList recentMedia;
+
+ switch (enmMediumType)
+ {
+ case UIMediumDeviceType_DVD:
+ recentMedia = gEDataManager->recentListOfOpticalDisks();
+ break;
+ case UIMediumDeviceType_Floppy:
+ recentMedia = gEDataManager->recentListOfFloppyDisks();
+ break;
+ case UIMediumDeviceType_HardDisk:
+ recentMedia = gEDataManager->recentListOfHardDrives();
+ break;
+ default:
+ break;
+ }
+
+ /* Remove the media which is not there not not readable: */
+ QStringList existingMedia;
+ foreach (QString strMediaPath, recentMedia)
+ {
+ QFileInfo info(strMediaPath);
+ if (info.exists() && info.isReadable())
+ existingMedia << strMediaPath;
+ }
+ if (existingMedia.isEmpty())
+ return;
+
+ insertSeparator(m_iRecentListSeparatorPosition);
+ foreach (const QString strPath, existingMedia)
+ addItem(strPath);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIFilePathSelector.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIFilePathSelector.h
new file mode 100644
index 00000000..27621573
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIFilePathSelector.h
@@ -0,0 +1,244 @@
+/* $Id: UIFilePathSelector.h $ */
+/** @file
+ * VBox Qt GUI - UIFilePathSelector class declaration.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIFilePathSelector_h
+#define FEQT_INCLUDED_SRC_widgets_UIFilePathSelector_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+#include "UIMediumDefs.h"
+
+/* Forward declarations: */
+class QAction;
+class QFocusEvent;
+class QHBoxLayout;
+class QObject;
+class QResizeEvent;
+class QWidget;
+class QString;
+class QILabel;
+class QILineEdit;
+class QIToolButton;
+
+/** QIComboBox subclass providing GUI with the
+ * possibility to choose/reflect file/folder path. */
+class SHARED_LIBRARY_STUFF UIFilePathSelector : public QIWithRetranslateUI<QIComboBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notify listeners about @a strPath changed. */
+ void pathChanged(const QString &strPath);
+
+public:
+
+ /** Modes file-path selector operates in. */
+ enum Mode
+ {
+ Mode_Folder = 0,
+ Mode_File_Open,
+ Mode_File_Save
+ };
+
+ /** Combo-box field IDs file-path selector uses. */
+ enum
+ {
+ PathId = 0,
+ SelectId,
+ ResetId
+ };
+
+ /** Constructs file-path selector passing @a pParent to QIComboBox base-class. */
+ UIFilePathSelector(QWidget *pParent = 0);
+
+ /** Defines the @a enmMode to operate in. */
+ void setMode(Mode enmMode);
+ /** Returns the mode to operate in. */
+ Mode mode() const { return m_enmMode; }
+
+ /** Defines whether the path is @a fEditable. */
+ void setEditable(bool fEditable);
+ /** Returns whether the path is editable. */
+ bool isEditable() const { return m_fEditable; }
+
+ /** Defines whether the reseting to defauilt path is @a fEnabled. */
+ void setResetEnabled(bool fEnabled);
+ /** Returns whether the reseting to defauilt path is enabled. */
+ bool isResetEnabled() const { return count() - 1 == ResetId; }
+
+ /** Defines the file-dialog @a strTitle. */
+ void setFileDialogTitle(const QString &strTitle) { m_strFileDialogTitle = strTitle; }
+ /** Returns the file-dialog title. */
+ QString fileDialogTitle() const { return m_strFileDialogTitle; }
+
+ /** Defines the file-dialog @a strFilters. */
+ void setFileDialogFilters(const QString &strFilters) { m_strFileDialogFilters = strFilters; }
+ /** Returns the file-dialog filters. */
+ QString fileDialogFilters() const { return m_strFileDialogFilters; }
+
+ /** Defines the file-dialog @a strDefaultSaveExtension. */
+ void setFileDialogDefaultSaveExtension(const QString &strDefaultSaveExtension) { m_strFileDialogDefaultSaveExtension = strDefaultSaveExtension; }
+ /** Returns the file-dialog default save extension. */
+ QString fileDialogDefaultSaveExtension() const { return m_strFileDialogDefaultSaveExtension; }
+
+ /** Resets path modified state to false. */
+ void resetModified() { m_fModified = false; }
+ /** Returns whether the path is modified. */
+ bool isModified() const { return m_fModified; }
+ /** Returns whether the path is selected. */
+ bool isPathSelected() const { return currentIndex() == PathId; }
+
+ /** Returns the path. */
+ QString path() const { return m_strPath; }
+ /** Returns the path which we pass to QFileDialog as initial path. */
+ QString initialPath() const { return m_strInitialPath; }
+
+ /** Returns true if the selected path points to an existing/readable file. */
+ bool isValid() const;
+
+ /** Sets overriden widget's @a strToolTip.
+ * @note If nothing set it's generated automatically. */
+ void setToolTip(const QString &strToolTip);
+
+ void setDefaultPath(const QString &strDefaultPath);
+ const QString& defaultPath() const;
+
+ void setRecentMediaListType(UIMediumDeviceType enmMediumType);
+ UIMediumDeviceType recentMediaListType() const;
+
+public slots:
+
+ /** Defines the @a strPath and @a fRefreshText after that. */
+ void setPath(const QString &strPath, bool fRefreshText = true);
+
+ /** Defines the @a strInitialPath. */
+ void setInitialPath(const QString &strInitialPath) { m_strInitialPath = strInitialPath; }
+
+protected:
+
+ /** Preprocesses every @a pEvent sent to @a pObject. */
+ bool eventFilter(QObject *pObject, QEvent *pEvent);
+
+ /** Handles resize @a pEvent. */
+ void resizeEvent(QResizeEvent *pEvent);
+
+ /** Handles focus-in @a pEvent. */
+ void focusInEvent(QFocusEvent *pEvent);
+ /** Handles focus-out @a pEvent. */
+ void focusOutEvent(QFocusEvent *pEvent);
+
+ /** Handles translation event. */
+ void retranslateUi();
+
+private slots:
+
+ /** Handles combo-box @a iIndex activation. */
+ void onActivated(int iIndex);
+
+ /** Handles combo-box @a strText editing. */
+ void onTextEdited(const QString &strText);
+
+ /** Handles combo-box text copying. */
+ void copyToClipboard();
+
+ /** Refreshes combo-box text according to chosen path. */
+ void refreshText();
+
+ void sltRecentMediaListUpdated(UIMediumDeviceType enmMediumType);
+
+private:
+
+ /** Provokes change to @a strPath and @a fRefreshText after that. */
+ void changePath(const QString &strPath, bool fRefreshText = true);
+
+ /** Call for file-dialog to choose path. */
+ void selectPath();
+
+ /** Returns default icon. */
+ QIcon defaultIcon() const;
+
+ /** Returns full path @a fAbsolute if necessary. */
+ QString fullPath(bool fAbsolute = true) const;
+
+ /** Shrinks the reflected text to @a iWidth pixels. */
+ QString shrinkText(int iWidth) const;
+
+ /** Holds the mode to operate in. */
+ Mode m_enmMode;
+
+ /** Holds the path. */
+ QString m_strPath;
+ /** Holds the path which we pass to QFileDialog as initial path. */
+ QString m_strInitialPath;
+
+ /** Holds the file-dialog title. */
+ QString m_strFileDialogTitle;
+ /** Holds the file-dialog filters. */
+ QString m_strFileDialogFilters;
+ /** Holds the file-dialog default save extension. */
+ QString m_strFileDialogDefaultSaveExtension;
+
+ /** Holds the cached text for empty path. */
+ QString m_strNoneText;
+ /** Holds the cached tool-tip for empty path. */
+ QString m_strNoneToolTip;
+
+ /** Holds whether editor has Reset action. */
+ bool m_fResetEnabled;
+
+ /** Holds whether the path is editable. */
+ bool m_fEditable;
+ /** Holds whether the path is modified. */
+ bool m_fModified;
+
+ /** Holds whether we are in editable mode. */
+ bool m_fEditableMode;
+ /** Holds whether we are expecting mouse events. */
+ bool m_fMouseAwaited;
+
+ /** Holds whether the tool-tip overriden. */
+ bool m_fToolTipOverriden;
+
+ /** Holds the copy action instance. */
+ QAction *m_pCopyAction;
+
+ /** Path is set to m_strDefaultPath when it is reset. */
+ QString m_strDefaultPath;
+
+ /** Holds the recent list separator position. */
+ int m_iRecentListSeparatorPosition;
+ /** Holds whether medium type for recent media list. If it is UIMediumDeviceType_Invalid the recent list is not shown. */
+ UIMediumDeviceType m_enmRecentMediaListType;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIFilePathSelector_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIFilmContainer.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIFilmContainer.cpp
new file mode 100644
index 00000000..53a4e3e3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIFilmContainer.cpp
@@ -0,0 +1,328 @@
+/* $Id: UIFilmContainer.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFilmContainer class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QHBoxLayout>
+#include <QPainter>
+#include <QScrollArea>
+#include <QScrollBar>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIFilmContainer.h"
+
+
+/** QWidget subclass providing GUI with UIFilmContainer item prototype.
+ * @todo Rename to something more suitable like UIScreenThumbnail. */
+class UIFilm : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs film widget passing @a pParent to the base-class.
+ * @param iScreenIndex Brings the guest-screen index this film referencing.
+ * @param fEnabled Brings whether the guest-screen mentioned above is enabled. */
+ UIFilm(int iScreenIndex, BOOL fEnabled, QWidget *pParent = 0);
+
+ /** Returns whether guest-screen is enabled. */
+ bool checked() const;
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Returns minimum size-hint. */
+ virtual QSize minimumSizeHint() const RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares layout. */
+ void prepareLayout();
+ /** Prepares check-box. */
+ void prepareCheckBox();
+
+ /** Holds the guest-screen index. */
+ int m_iScreenIndex;
+ /** Holds whether guest-screen was enabled. */
+ BOOL m_fWasEnabled;
+
+ /** Holds the main-layout instance. */
+ QVBoxLayout *m_pMainLayout;
+ /** Holds the check-box instance. */
+ QCheckBox *m_pCheckBox;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIFilm implementation. *
+*********************************************************************************************************************************/
+
+UIFilm::UIFilm(int iScreenIndex, BOOL fEnabled, QWidget *pParent /* = 0*/)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_iScreenIndex(iScreenIndex)
+ , m_fWasEnabled(fEnabled)
+ , m_pCheckBox(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+bool UIFilm::checked() const
+{
+ /* Is the check-box currently checked? */
+ return m_pCheckBox->isChecked();
+}
+
+void UIFilm::retranslateUi()
+{
+ /* Translate check-box: */
+ m_pCheckBox->setText(QApplication::translate("UIMachineSettingsDisplay", "Screen %1").arg(m_iScreenIndex + 1));
+ m_pCheckBox->setWhatsThis(QApplication::translate("UIMachineSettingsDisplay", "When checked, enables video recording for screen %1.").arg(m_iScreenIndex + 1));
+}
+
+void UIFilm::paintEvent(QPaintEvent *)
+{
+ /* Compose painting rectangle: */
+ const QRect rect(1, 1, width() - 2, height() - 2);
+
+ /* Create painter: */
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ /* Configure painter clipping: */
+ QPainterPath path;
+ int iDiameter = 6;
+ QSizeF arcSize(2 * iDiameter, 2 * iDiameter);
+ path.moveTo(rect.x() + iDiameter, rect.y());
+ path.arcTo(QRectF(path.currentPosition(), arcSize).translated(-iDiameter, 0), 90, 90);
+ path.lineTo(path.currentPosition().x(), rect.height() - iDiameter);
+ path.arcTo(QRectF(path.currentPosition(), arcSize).translated(0, -iDiameter), 180, 90);
+ path.lineTo(rect.width() - iDiameter, path.currentPosition().y());
+ path.arcTo(QRectF(path.currentPosition(), arcSize).translated(-iDiameter, -2 * iDiameter), 270, 90);
+ path.lineTo(path.currentPosition().x(), rect.y() + iDiameter);
+ path.arcTo(QRectF(path.currentPosition(), arcSize).translated(-2 * iDiameter, -iDiameter), 0, 90);
+ path.closeSubpath();
+
+ /* Get current background color: */
+ QColor currentColor(palette().color(backgroundRole()));
+
+ /* Fill with background: */
+ painter.setClipPath(path);
+ QColor newColor1 = currentColor;
+ QColor newColor2 = currentColor.darker(125);
+ QLinearGradient headerGradient(rect.topLeft(), rect.bottomRight());
+ headerGradient.setColorAt(0, newColor1);
+ headerGradient.setColorAt(1, newColor2);
+ painter.fillRect(rect, headerGradient);
+
+ /* Stroke with border: */
+ QColor strokeColor = currentColor.darker(150);
+ painter.setClipping(false);
+ painter.strokePath(path, strokeColor);
+}
+
+QSize UIFilm::minimumSizeHint() const
+{
+ /* Return 16:9 aspect-ratio msh: */
+ QSize msh = QWidget::minimumSizeHint();
+ return QSize(msh.width(), (msh.width() * 9) / 16);
+}
+
+void UIFilm::prepare()
+{
+ /* Prepare layout: */
+ prepareLayout();
+ /* Prepare check-box: */
+ prepareCheckBox();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIFilm::prepareLayout()
+{
+ /* Create layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (m_pMainLayout)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ m_pMainLayout->setContentsMargins(10, 10, 15, 10);
+#endif
+
+ /* Add strech: */
+ m_pMainLayout->addStretch();
+ }
+}
+
+void UIFilm::prepareCheckBox()
+{
+ /* Create check-box: */
+ m_pCheckBox = new QCheckBox;
+ if (m_pCheckBox)
+ {
+ /* Configure check-box: */
+ m_pCheckBox->setChecked(static_cast<bool>(m_fWasEnabled));
+ /* Configure font: */
+ QFont currentFont = m_pCheckBox->font();
+#ifdef VBOX_WS_MAC
+ currentFont.setPointSize(currentFont.pointSize() - 2);
+#else
+ currentFont.setPointSize(currentFont.pointSize() - 1);
+#endif
+ m_pCheckBox->setFont(currentFont);
+
+ /* Insert into layout: */
+ m_pMainLayout->insertWidget(0, m_pCheckBox);
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIFilmContainer implementation. *
+*********************************************************************************************************************************/
+
+UIFilmContainer::UIFilmContainer(QWidget *pParent /* = 0*/)
+ : QWidget(pParent)
+ , m_pMainLayout(0)
+ , m_pScroller(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+QVector<BOOL> UIFilmContainer::value() const
+{
+ /* Enumerate all the existing widgets: */
+ QVector<BOOL> value;
+ foreach (UIFilm *pWidget, m_widgets)
+ value << static_cast<BOOL>(pWidget->checked());
+
+ /* Return value: */
+ return value;
+}
+
+void UIFilmContainer::setValue(const QVector<BOOL> &value)
+{
+ /* Cleanup viewport/widget list: */
+ delete m_pScroller->takeWidget();
+ m_widgets.clear();
+
+ /* Create widget: */
+ QWidget *pWidget = new QWidget;
+ if (pWidget)
+ {
+ /* Create widget-layout: */
+ QHBoxLayout *pWidgetLayout = new QHBoxLayout(pWidget);
+ if (pWidgetLayout)
+ {
+ /* Configure widget-layout: */
+ pWidgetLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ pWidgetLayout->setContentsMargins(5, 5, 5, 5);
+#else
+ pWidgetLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2);
+#endif
+
+ /* Create new films according passed vector: */
+ for (int iScreenIndex = 0; iScreenIndex < value.size(); ++iScreenIndex)
+ {
+ /* Create new film: */
+ UIFilm *pFilm = new UIFilm(iScreenIndex, value[iScreenIndex]);
+ if (pFilm)
+ {
+ /* Add film into the widget list: */
+ m_widgets << pFilm;
+
+ /* Add into layout: */
+ pWidgetLayout->addWidget(pFilm);
+ }
+ }
+ }
+
+ /* Assign scroller with widget: */
+ m_pScroller->setWidget(pWidget);
+ /* Reconfigure scroller widget: */
+ m_pScroller->widget()->setAutoFillBackground(false);
+ /* And adjust that widget geometry: */
+ QSize msh = m_pScroller->widget()->minimumSizeHint();
+ int iMinimumHeight = msh.height();
+ m_pScroller->viewport()->setFixedHeight(iMinimumHeight);
+ }
+}
+
+void UIFilmContainer::prepare()
+{
+ /* Prepare layout: */
+ prepareLayout();
+ /* Prepare scroller: */
+ prepareScroller();
+
+ /* Append with 'default' value: */
+ setValue(QVector<BOOL>() << true);
+}
+
+void UIFilmContainer::prepareLayout()
+{
+ /* Create layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ if (m_pMainLayout)
+ {
+ /* Configure layout: */
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+ m_pMainLayout->setSpacing(0);
+ }
+}
+
+void UIFilmContainer::prepareScroller()
+{
+ /* Create scroller: */
+ m_pScroller = new QScrollArea;
+ if (m_pScroller)
+ {
+ /* Configure scroller: */
+ m_pScroller->setFrameShape(QFrame::NoFrame);
+ m_pScroller->viewport()->setAutoFillBackground(false);
+ m_pScroller->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_pScroller->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
+
+ /* Add into layout: */
+ m_pMainLayout->addWidget(m_pScroller);
+ }
+}
+
+
+#include "UIFilmContainer.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIFilmContainer.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIFilmContainer.h
new file mode 100644
index 00000000..f1a401ba
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIFilmContainer.h
@@ -0,0 +1,83 @@
+/* $Id: UIFilmContainer.h $ */
+/** @file
+ * VBox Qt GUI - UIFilmContainer class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIFilmContainer_h
+#define FEQT_INCLUDED_SRC_widgets_UIFilmContainer_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <VBox/com/com.h>
+
+/* Forward declarations: */
+class QCheckBox;
+class QScrollArea;
+class QVBoxLayout;
+class UIFilm;
+
+/** QWidget subclass providing GUI with QScrollArea-based container for UIFilm widgets.
+ * @todo Rename to something more suitable like UIScreenThumbnailContainer. */
+class SHARED_LIBRARY_STUFF UIFilmContainer : public QWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs film-container passing @a pParent to the base-class. */
+ UIFilmContainer(QWidget *pParent = 0);
+
+ /** Returns the film-container check-box values. */
+ QVector<BOOL> value() const;
+ /** Defines the film-container check-box @a values. */
+ void setValue(const QVector<BOOL> &values);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares layout. */
+ void prepareLayout();
+ /** Prepares scroller. */
+ void prepareScroller();
+
+ /** Holds the main layout intance. */
+ QVBoxLayout *m_pMainLayout;
+ /** Holds the scroller intance. */
+ QScrollArea *m_pScroller;
+ /** Holds the list of film widgets. */
+ QList<UIFilm*> m_widgets;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIFilmContainer_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIFormEditorWidget.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIFormEditorWidget.cpp
new file mode 100644
index 00000000..7f19d0f0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIFormEditorWidget.cpp
@@ -0,0 +1,1584 @@
+/* $Id: UIFormEditorWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UIFormEditorWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QEvent>
+#include <QHeaderView>
+#include <QItemEditorFactory>
+#include <QPointer>
+#include <QPushButton>
+#include <QSortFilterProxyModel>
+#include <QSpinBox>
+#include <QTextEdit>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIDialogButtonBox.h"
+#include "QIStyledItemDelegate.h"
+#include "QITableView.h"
+#include "QIWithRetranslateUI.h"
+#include "UIFormEditorWidget.h"
+#include "UIIconPool.h"
+#include "UINotificationCenter.h"
+
+/* COM includes: */
+#include "CBooleanFormValue.h"
+#include "CChoiceFormValue.h"
+#include "CForm.h"
+#include "CFormValue.h"
+#include "CRangedIntegerFormValue.h"
+#include "CStringFormValue.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/* VirtualBox interface declarations: */
+#include <VBox/com/VirtualBox.h>
+
+
+/** Form Editor data types. */
+enum UIFormEditorDataType
+{
+ UIFormEditorDataType_Name,
+ UIFormEditorDataType_Value,
+ UIFormEditorDataType_Max
+};
+
+
+/** Class used to hold text data. */
+class TextData
+{
+public:
+
+ /** Constructs null text data. */
+ TextData() {}
+ /** Constructs text data on the basis of passed @a strText and @a index. */
+ TextData(const QString &strText, const QModelIndex index = QModelIndex())
+ : m_strText(strText), m_index(index) {}
+ /** Constructs text data on the basis of @a another text data. */
+ TextData(const TextData &another)
+ : m_strText(another.text()), m_index(another.index()) {}
+
+ /** Assigns values of @a another text to this one. */
+ TextData &operator=(const TextData &another)
+ {
+ m_strText = another.text();
+ m_index = another.index();
+ return *this;
+ }
+
+ /** Returns text value. */
+ QString text() const { return m_strText; }
+
+ /** Defines model @a index. */
+ void setIndex(const QModelIndex &index) { m_index = index; }
+ /** Returns model index. */
+ QModelIndex index() const { return m_index; }
+
+private:
+
+ /** Holds text value. */
+ QString m_strText;
+ /** Holds model index. */
+ QModelIndex m_index;
+};
+Q_DECLARE_METATYPE(TextData);
+
+
+/** Class used to hold choice data. */
+class ChoiceData
+{
+public:
+
+ /** Constructs null choice data. */
+ ChoiceData()
+ : m_iSelectedIndex(-1) {}
+ /** Constructs choice data on the basis of passed @a values and @a iSelectedIndex. */
+ ChoiceData(const QVector<QString> &values, int iSelectedIndex)
+ : m_values(values), m_iSelectedIndex(iSelectedIndex) {}
+ /** Constructs choice data on the basis of @a another choice data. */
+ ChoiceData(const ChoiceData &another)
+ : m_values(another.values()), m_iSelectedIndex(another.selectedIndex()) {}
+
+ /** Assigns values of @a another choice to this one. */
+ ChoiceData &operator=(const ChoiceData &another)
+ {
+ m_values = another.values();
+ m_iSelectedIndex = another.selectedIndex();
+ return *this;
+ }
+
+ /** Returns values vector. */
+ QVector<QString> values() const { return m_values; }
+ /** Returns selected index. */
+ int selectedIndex() const { return m_iSelectedIndex; }
+ /** Returns selected value. */
+ QString selectedValue() const
+ {
+ return m_iSelectedIndex >= 0 && m_iSelectedIndex < m_values.size()
+ ? m_values.at(m_iSelectedIndex) : QString();
+ }
+
+private:
+
+ /** Holds values vector. */
+ QVector<QString> m_values;
+ /** Holds selected index. */
+ int m_iSelectedIndex;
+};
+Q_DECLARE_METATYPE(ChoiceData);
+
+
+/** Class used to hold ranged-integer data. */
+class RangedIntegerData
+{
+public:
+
+ /** Constructs null ranged-integer data. */
+ RangedIntegerData()
+ : m_iMinimum(-1), m_iMaximum(-1)
+ , m_iInteger(-1), m_strSuffix(QString()) {}
+ /** Constructs ranged-integer data on the basis of passed @a iMinimum, @a iMaximum, @a iInteger and @a strSuffix. */
+ RangedIntegerData(int iMinimum, int iMaximum, int iInteger, const QString strSuffix)
+ : m_iMinimum(iMinimum), m_iMaximum(iMaximum)
+ , m_iInteger(iInteger), m_strSuffix(strSuffix) {}
+ /** Constructs ranged-integer data on the basis of @a another ranged-integer data. */
+ RangedIntegerData(const RangedIntegerData &another)
+ : m_iMinimum(another.minimum()), m_iMaximum(another.maximum())
+ , m_iInteger(another.integer()), m_strSuffix(another.suffix()) {}
+
+ /** Assigns values of @a another ranged-integer to this one. */
+ RangedIntegerData &operator=(const RangedIntegerData &another)
+ {
+ m_iMinimum = another.minimum();
+ m_iMaximum = another.maximum();
+ m_iInteger = another.integer();
+ m_strSuffix = another.suffix();
+ return *this;
+ }
+
+ /** Returns minimum value. */
+ int minimum() const { return m_iMinimum; }
+ /** Returns maximum value. */
+ int maximum() const { return m_iMaximum; }
+ /** Returns current value. */
+ int integer() const { return m_iInteger; }
+ /** Returns suffix value. */
+ QString suffix() const { return m_strSuffix; }
+
+private:
+
+ /** Holds minimum value. */
+ int m_iMinimum;
+ /** Holds maximum value. */
+ int m_iMaximum;
+ /** Holds current value. */
+ int m_iInteger;
+ /** Holds suffix value. */
+ QString m_strSuffix;
+};
+Q_DECLARE_METATYPE(RangedIntegerData);
+
+
+/** QWidget extension used as dummy TextData editor.
+ * It's not actually an editor, but Edit... button instead which opens
+ * real editor passing stored model index received from TextData value. */
+class TextEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+ Q_PROPERTY(TextData text READ text WRITE setText USER true);
+
+public:
+
+ /** Constructs TextData editor passing @a pParent to the base-class. */
+ TextEditor(QWidget *pParent = 0);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles button click. */
+ void sltHandleButtonClick();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Defines @a text. */
+ void setText(const TextData &text);
+ /** Returns text. */
+ TextData text() const;
+
+ /** Holds the button instance. */
+ QPushButton *m_pButton;
+ /** Holds the multiline text. */
+ QString m_strMultilineText;
+ /** Holds the model index. */
+ QModelIndex m_index;
+};
+
+
+/** QComboBox extension used as ChoiceData editor. */
+class ChoiceEditor : public QComboBox
+{
+ Q_OBJECT;
+ Q_PROPERTY(ChoiceData choice READ choice WRITE setChoice USER true);
+
+signals:
+
+ /** Notifies listener about data should be committed. */
+ void sigCommitData(QWidget *pThis);
+
+public:
+
+ /** Constructs ChoiceData editor passing @a pParent to the base-class. */
+ ChoiceEditor(QWidget *pParent = 0);
+
+private slots:
+
+ /** Handles current index change. */
+ void sltCurrentIndexChanged();
+
+private:
+
+ /** Defines the @a choice. */
+ void setChoice(const ChoiceData &choice);
+ /** Returns the choice. */
+ ChoiceData choice() const;
+};
+
+
+/** QSpinBox extension used as RangedIntegerData editor. */
+class RangedIntegerEditor : public QSpinBox
+{
+ Q_OBJECT;
+ Q_PROPERTY(RangedIntegerData rangedInteger READ rangedInteger WRITE setRangedInteger USER true);
+
+public:
+
+ /** Constructs RangedIntegerData editor passing @a pParent to the base-class. */
+ RangedIntegerEditor(QWidget *pParent = 0);
+
+private:
+
+ /** Defines @a rangedInteger. */
+ void setRangedInteger(const RangedIntegerData &rangedInteger);
+ /** Returns ranged-integer. */
+ RangedIntegerData rangedInteger() const;
+
+ /** Holds the unchanged suffix. */
+ QString m_strSuffix;
+};
+
+
+/** QITableViewCell extension used as Form Editor table-view cell. */
+class UIFormEditorCell : public QITableViewCell
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs table cell on the basis of certain @a strText, passing @a pParent to the base-class. */
+ UIFormEditorCell(QITableViewRow *pParent, const QString &strText = QString());
+
+ /** Returns the cell text. */
+ virtual QString text() const RT_OVERRIDE { return m_strText; }
+
+ /** Defines the cell @a strText. */
+ void setText(const QString &strText) { m_strText = strText; }
+
+private:
+
+ /** Holds the cell text. */
+ QString m_strText;
+};
+
+
+/** QITableViewRow extension used as Form Editor table-view row. */
+class UIFormEditorRow : public QITableViewRow
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs table row on the basis of certain @a comValue, passing @a pParent to the base-class.
+ * @param pFormEditorWidget Brings the root form-editor widget reference. */
+ UIFormEditorRow(QITableView *pParent, UIFormEditorWidget *pFormEditorWidget, const CFormValue &comValue);
+ /** Destructs table row. */
+ virtual ~UIFormEditorRow() RT_OVERRIDE;
+
+ /** Returns value type. */
+ KFormValueType valueType() const { return m_enmValueType; }
+
+ /** Returns the row name as string. */
+ QString nameToString() const;
+ /** Returns the row value as string. */
+ QString valueToString() const;
+
+ /** Returns whether the row is enabled. */
+ bool isEnabled() const;
+ /** Returns whether the row is visible. */
+ bool isVisible() const;
+
+ /** Returns value cast to bool. */
+ bool toBool() const;
+ /** Defines @a fBool value. */
+ void setBool(bool fBool);
+
+ /** Returns whether cached string value is multiline. */
+ bool isMultilineString() const;
+ /** Returns value cast to text. */
+ TextData toText() const;
+ /** Defines @a text value. */
+ void setText(const TextData &text);
+ /** Returns value cast to string. */
+ QString toString() const;
+ /** Defines @a strString value. */
+ void setString(const QString &strString);
+
+ /** Returns value cast to choice. */
+ ChoiceData toChoice() const;
+ /** Defines @a choice value. */
+ void setChoice(const ChoiceData &choice);
+
+ /** Returns value cast to ranged-integer. */
+ RangedIntegerData toRangedInteger() const;
+ /** Defines @a rangedInteger value. */
+ void setRangedInteger(const RangedIntegerData &rangedInteger);
+
+ /** Updates value cells. */
+ void updateValueCells();
+
+ /** Check whether generation value is changed. */
+ bool isGenerationChanged() const;
+
+protected:
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child item with @a iIndex. */
+ virtual QITableViewCell *childItem(int iIndex) const RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the root form-editor widget reference. */
+ UIFormEditorWidget *m_pFormEditorWidget;
+
+ /** Holds the row value. */
+ CFormValue m_comValue;
+
+ /** Holds the value type. */
+ KFormValueType m_enmValueType;
+
+ /** Holds current generation value. */
+ int m_iGeneration;
+
+ /** Holds cached bool value. */
+ bool m_fBool;
+ /** Holds whether cached string value is multiline. */
+ bool m_fMultilineString;
+ /** Holds cached text value. */
+ TextData m_text;
+ /** Holds cached string value. */
+ QString m_strString;
+ /** Holds cached choice value. */
+ ChoiceData m_choice;
+ /** Holds cached ranged-integer value. */
+ RangedIntegerData m_rangedInteger;
+
+ /** Holds the cell instances. */
+ QVector<UIFormEditorCell*> m_cells;
+};
+
+
+/** QAbstractTableModel subclass used as Form Editor data model. */
+class UIFormEditorModel : public QAbstractTableModel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Form Editor model passing @a pParent to the base-class. */
+ UIFormEditorModel(UIFormEditorWidget *pParent);
+ /** Destructs Port Forwarding model. */
+ virtual ~UIFormEditorModel() RT_OVERRIDE;
+
+ /** Clears form. */
+ void clearForm();
+ /** Defines form @a values. */
+ void setFormValues(const CFormValueVector &values);
+
+ /** Returns the number of children. */
+ int childCount() const;
+ /** Returns the child item with @a iIndex. */
+ QITableViewRow *childItem(int iIndex) const;
+
+ /** Returns the index of the item in the model specified by the given @a iRow, @a iColumn and @a parentIdx. */
+ virtual QModelIndex index(int iRow, int iColumn, const QModelIndex &parentIdx = QModelIndex()) const RT_OVERRIDE;
+
+ /** Returns flags for item with certain @a index. */
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const RT_OVERRIDE;
+
+ /** Returns row count of certain @a parent. */
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const RT_OVERRIDE;
+ /** Returns column count of certain @a parent. */
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const RT_OVERRIDE;
+
+ /** Returns header data for @a iSection, @a enmOrientation and @a iRole specified. */
+ virtual QVariant headerData(int iSection, Qt::Orientation enmOrientation, int iRole) const RT_OVERRIDE;
+
+ /** Defines the @a iRole data for item with @a index as @a value. */
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int iRole = Qt::EditRole) RT_OVERRIDE;
+ /** Returns the @a iRole data for item with @a index. */
+ virtual QVariant data(const QModelIndex &index, int iRole) const RT_OVERRIDE;
+
+ /** Creates actual TextData editor for specified @a index. */
+ void createTextDataEditor(const QModelIndex &index);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Returns the parent table-view reference. */
+ QITableView *view() const;
+
+ /** Updates row generation values. */
+ void updateGeneration();
+
+ /** Returns icon hint for specified @a strItemName. */
+ QIcon iconHint(const QString &strItemName) const;
+
+ /** Holds the root form-editor widget reference. */
+ UIFormEditorWidget *m_pFormEditorWidget;
+
+ /** Holds the Form Editor row list. */
+ QList<UIFormEditorRow*> m_dataList;
+
+ /** Holds the hardcoded icon name map. */
+ QMap<QString, QIcon> m_icons;
+};
+
+
+/** QSortFilterProxyModel subclass used as the Form Editor proxy-model. */
+class UIFormEditorProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs the Form Editor proxy-model passing @a pParent to the base-class. */
+ UIFormEditorProxyModel(QObject *pParent = 0);
+
+ /** Returns the number of children. */
+ int childCount() const;
+ /** Returns the child item with @a iIndex. */
+ QITableViewRow *childItem(int iIndex) const;
+
+protected:
+
+ /** Returns whether item in the row indicated by the given @a iSourceRow and @a srcParenIdx should be included in the model. */
+ virtual bool filterAcceptsRow(int iSourceRow, const QModelIndex &srcParenIdx) const RT_OVERRIDE;
+};
+
+
+/** QITableView extension used as Form Editor table-view. */
+class UIFormEditorView : public QITableView
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Form Editor table-view. */
+ UIFormEditorView(QWidget *pParent = 0);
+
+protected:
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child item with @a iIndex. */
+ virtual QITableViewRow *childItem(int iIndex) const RT_OVERRIDE;
+};
+
+
+/*********************************************************************************************************************************
+* Class TextEditor implementation. *
+*********************************************************************************************************************************/
+
+TextEditor::TextEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pButton(0)
+{
+ prepare();
+}
+
+void TextEditor::retranslateUi()
+{
+ m_pButton->setText(UIFormEditorWidget::tr("Edit..."));
+}
+
+void TextEditor::sltHandleButtonClick()
+{
+ /* Redirect the edit call if possible: */
+ do
+ {
+ /* Get the view: */
+ if ( !parent()
+ || !parent()->parent())
+ break;
+ UIFormEditorView *pView = qobject_cast<UIFormEditorView*>(parent()->parent());
+
+ /* Get the proxy model: */
+ if ( !pView
+ || !pView->model())
+ break;
+ UIFormEditorProxyModel *pProxyModel = qobject_cast<UIFormEditorProxyModel*>(pView->model());
+
+ /* Get the source model: */
+ if ( !pProxyModel
+ || !pProxyModel->sourceModel())
+ break;
+ UIFormEditorModel *pSourceModel = qobject_cast<UIFormEditorModel*>(pProxyModel->sourceModel());
+
+ /* Execute the call: */
+ if (!pSourceModel)
+ break;
+ pSourceModel->createTextDataEditor(m_index);
+ }
+ while (0);
+}
+
+void TextEditor::prepare()
+{
+ /* Create layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0 ,0);
+ /* Create button: */
+ m_pButton = new QPushButton(this);
+ if (m_pButton)
+ {
+ connect(m_pButton, &QPushButton::clicked, this, &TextEditor::sltHandleButtonClick);
+ pLayout->addWidget(m_pButton);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void TextEditor::setText(const TextData &text)
+{
+ m_strMultilineText = text.text();
+ m_index = text.index();
+}
+
+TextData TextEditor::text() const
+{
+ return TextData(m_strMultilineText, m_index);
+}
+
+
+/*********************************************************************************************************************************
+* Class ChoiceEditor implementation. *
+*********************************************************************************************************************************/
+
+ChoiceEditor::ChoiceEditor(QWidget *pParent /* = 0 */)
+ : QComboBox(pParent)
+{
+ /* Make sure QIStyledDelegate aware of us: */
+ setProperty("has_sigCommitData", true);
+ /* Configure connections: */
+ connect(this, static_cast<void(ChoiceEditor::*)(int)>(&ChoiceEditor::currentIndexChanged),
+ this, &ChoiceEditor::sltCurrentIndexChanged);
+}
+
+void ChoiceEditor::sltCurrentIndexChanged()
+{
+ emit sigCommitData(this);
+}
+
+void ChoiceEditor::setChoice(const ChoiceData &choice)
+{
+ clear();
+ addItems(choice.values().toList());
+ setCurrentIndex(choice.selectedIndex());
+}
+
+ChoiceData ChoiceEditor::choice() const
+{
+ QVector<QString> choices(count());
+ for (int i = 0; i < count(); ++i)
+ choices[i] = itemText(i);
+ return ChoiceData(choices, currentIndex());
+}
+
+
+/*********************************************************************************************************************************
+* Class RangedIntegerEditor implementation. *
+*********************************************************************************************************************************/
+
+RangedIntegerEditor::RangedIntegerEditor(QWidget *pParent /* = 0 */)
+ : QSpinBox(pParent)
+{
+}
+
+void RangedIntegerEditor::setRangedInteger(const RangedIntegerData &rangedInteger)
+{
+ setMinimum(rangedInteger.minimum());
+ setMaximum(rangedInteger.maximum());
+ setValue(rangedInteger.integer());
+ m_strSuffix = rangedInteger.suffix();
+ setSuffix(m_strSuffix.isEmpty() ? QString() :
+ QString(" %1").arg(QApplication::translate("UICommon", m_strSuffix.toUtf8().constData())));
+}
+
+RangedIntegerData RangedIntegerEditor::rangedInteger() const
+{
+ return RangedIntegerData(minimum(), maximum(), value(), m_strSuffix);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIFormEditorCell implementation. *
+*********************************************************************************************************************************/
+
+UIFormEditorCell::UIFormEditorCell(QITableViewRow *pParent, const QString &strText /* = QString() */)
+ : QITableViewCell(pParent)
+ , m_strText(strText)
+{
+}
+
+
+/*********************************************************************************************************************************
+* Class UIFormEditorRow implementation. *
+*********************************************************************************************************************************/
+
+UIFormEditorRow::UIFormEditorRow(QITableView *pParent, UIFormEditorWidget *pFormEditorWidget, const CFormValue &comValue)
+ : QITableViewRow(pParent)
+ , m_pFormEditorWidget(pFormEditorWidget)
+ , m_comValue(comValue)
+ , m_enmValueType(KFormValueType_Max)
+ , m_iGeneration(0)
+ , m_fBool(false)
+ , m_fMultilineString(false)
+ , m_text(TextData())
+ , m_strString(QString())
+ , m_choice(ChoiceData())
+ , m_rangedInteger(RangedIntegerData())
+{
+ prepare();
+}
+
+UIFormEditorRow::~UIFormEditorRow()
+{
+ cleanup();
+}
+
+QString UIFormEditorRow::nameToString() const
+{
+ return m_cells.at(UIFormEditorDataType_Name)->text();
+}
+
+QString UIFormEditorRow::valueToString() const
+{
+ return m_cells.at(UIFormEditorDataType_Value)->text();
+}
+
+bool UIFormEditorRow::isEnabled() const
+{
+ return m_comValue.GetEnabled();
+}
+
+bool UIFormEditorRow::isVisible() const
+{
+ return m_comValue.GetVisible();
+}
+
+bool UIFormEditorRow::toBool() const
+{
+ AssertReturn(valueType() == KFormValueType_Boolean, false);
+ return m_fBool;
+}
+
+void UIFormEditorRow::setBool(bool fBool)
+{
+ AssertReturnVoid(valueType() == KFormValueType_Boolean);
+ CBooleanFormValue comValue(m_comValue);
+ UINotificationProgressVsdFormValueSet *pNotification = new UINotificationProgressVsdFormValueSet(comValue,
+ fBool);
+ UINotificationCenter *pCenter = m_pFormEditorWidget->notificationCenter()
+ ? m_pFormEditorWidget->notificationCenter() : gpNotificationCenter;
+ pCenter->handleNow(pNotification);
+ updateValueCells();
+}
+
+bool UIFormEditorRow::isMultilineString() const
+{
+ AssertReturn(valueType() == KFormValueType_String, false);
+ return m_fMultilineString;
+}
+
+TextData UIFormEditorRow::toText() const
+{
+ AssertReturn(valueType() == KFormValueType_String, TextData());
+ return m_text;
+}
+
+void UIFormEditorRow::setText(const TextData &text)
+{
+ AssertReturnVoid(valueType() == KFormValueType_String);
+ CStringFormValue comValue(m_comValue);
+ UINotificationProgressVsdFormValueSet *pNotification = new UINotificationProgressVsdFormValueSet(comValue,
+ text.text());
+ UINotificationCenter *pCenter = m_pFormEditorWidget->notificationCenter()
+ ? m_pFormEditorWidget->notificationCenter() : gpNotificationCenter;
+ pCenter->handleNow(pNotification);
+ updateValueCells();
+}
+
+QString UIFormEditorRow::toString() const
+{
+ AssertReturn(valueType() == KFormValueType_String, QString());
+ return m_strString;
+}
+
+void UIFormEditorRow::setString(const QString &strString)
+{
+ AssertReturnVoid(valueType() == KFormValueType_String);
+ CStringFormValue comValue(m_comValue);
+ UINotificationProgressVsdFormValueSet *pNotification = new UINotificationProgressVsdFormValueSet(comValue,
+ strString);
+ UINotificationCenter *pCenter = m_pFormEditorWidget->notificationCenter()
+ ? m_pFormEditorWidget->notificationCenter() : gpNotificationCenter;
+ pCenter->handleNow(pNotification);
+ updateValueCells();
+}
+
+ChoiceData UIFormEditorRow::toChoice() const
+{
+ AssertReturn(valueType() == KFormValueType_Choice, ChoiceData());
+ return m_choice;
+}
+
+void UIFormEditorRow::setChoice(const ChoiceData &choice)
+{
+ /* Do nothing for empty choices: */
+ if (choice.selectedIndex() == -1)
+ return;
+
+ AssertReturnVoid(valueType() == KFormValueType_Choice);
+ CChoiceFormValue comValue(m_comValue);
+ UINotificationProgressVsdFormValueSet *pNotification = new UINotificationProgressVsdFormValueSet(comValue,
+ choice.selectedIndex());
+ UINotificationCenter *pCenter = m_pFormEditorWidget->notificationCenter()
+ ? m_pFormEditorWidget->notificationCenter() : gpNotificationCenter;
+ pCenter->handleNow(pNotification);
+ updateValueCells();
+}
+
+RangedIntegerData UIFormEditorRow::toRangedInteger() const
+{
+ AssertReturn(valueType() == KFormValueType_RangedInteger, RangedIntegerData());
+ return m_rangedInteger;
+}
+
+void UIFormEditorRow::setRangedInteger(const RangedIntegerData &rangedInteger)
+{
+ AssertReturnVoid(valueType() == KFormValueType_RangedInteger);
+ CRangedIntegerFormValue comValue(m_comValue);
+ UINotificationProgressVsdFormValueSet *pNotification = new UINotificationProgressVsdFormValueSet(comValue,
+ rangedInteger.integer());
+ UINotificationCenter *pCenter = m_pFormEditorWidget->notificationCenter()
+ ? m_pFormEditorWidget->notificationCenter() : gpNotificationCenter;
+ pCenter->handleNow(pNotification);
+ updateValueCells();
+}
+
+void UIFormEditorRow::updateValueCells()
+{
+ m_iGeneration = m_comValue.GetGeneration();
+ /// @todo check for errors
+
+ switch (m_enmValueType)
+ {
+ case KFormValueType_Boolean:
+ {
+ CBooleanFormValue comValue(m_comValue);
+ m_fBool = comValue.GetSelected();
+ m_cells[UIFormEditorDataType_Value]->setText(m_fBool ? "True" : "False");
+ /// @todo check for errors
+ break;
+ }
+ case KFormValueType_String:
+ {
+ CStringFormValue comValue(m_comValue);
+ m_fMultilineString = comValue.GetMultiline();
+ const QString strString = comValue.GetString();
+ if (m_fMultilineString)
+ m_text = TextData(strString);
+ else
+ m_strString = strString;
+ m_cells[UIFormEditorDataType_Value]->setText(strString);
+ /// @todo check for errors
+ break;
+ }
+ case KFormValueType_Choice:
+ {
+ CChoiceFormValue comValue(m_comValue);
+ const QVector<QString> values = comValue.GetValues();
+ const int iSelectedIndex = comValue.GetSelectedIndex();
+ m_choice = ChoiceData(values, iSelectedIndex);
+ m_cells[UIFormEditorDataType_Value]->setText(m_choice.selectedValue());
+ /// @todo check for errors
+ break;
+ }
+ case KFormValueType_RangedInteger:
+ {
+ CRangedIntegerFormValue comValue(m_comValue);
+ const int iMinimum = comValue.GetMinimum();
+ const int iMaximum = comValue.GetMaximum();
+ const int iInteger = comValue.GetInteger();
+ const QString strSuffix = comValue.GetSuffix();
+ m_rangedInteger = RangedIntegerData(iMinimum, iMaximum, iInteger, strSuffix);
+ m_cells[UIFormEditorDataType_Value]->setText( m_rangedInteger.suffix().isEmpty()
+ ? QString::number(m_rangedInteger.integer())
+ : QString("%1 %2").arg(m_rangedInteger.integer())
+ .arg(m_rangedInteger.suffix()));
+ /// @todo check for errors
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+bool UIFormEditorRow::isGenerationChanged() const
+{
+ const int iGeneration = m_comValue.GetGeneration();
+ /// @todo check for errors
+ return m_iGeneration != iGeneration;
+}
+
+int UIFormEditorRow::childCount() const
+{
+ /* Return cell count: */
+ return UIFormEditorDataType_Max;
+}
+
+QITableViewCell *UIFormEditorRow::childItem(int iIndex) const
+{
+ /* Make sure index within the bounds: */
+ AssertReturn(iIndex >= 0 && iIndex < m_cells.size(), 0);
+ /* Return corresponding cell: */
+ return m_cells.at(iIndex);
+}
+
+void UIFormEditorRow::prepare()
+{
+ /* Cache value type: */
+ m_enmValueType = m_comValue.GetType();
+ /// @todo check for errors
+
+ /* Create cells on the basis of variables we have: */
+ m_cells.resize(UIFormEditorDataType_Max);
+ const QString strName = m_comValue.GetLabel();
+ /// @todo check for errors
+ m_cells[UIFormEditorDataType_Name] = new UIFormEditorCell(this, strName);
+ m_cells[UIFormEditorDataType_Value] = new UIFormEditorCell(this);
+ updateValueCells();
+}
+
+void UIFormEditorRow::cleanup()
+{
+ /* Destroy cells: */
+ qDeleteAll(m_cells);
+ m_cells.clear();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIFormEditorModel implementation. *
+*********************************************************************************************************************************/
+
+UIFormEditorModel::UIFormEditorModel(UIFormEditorWidget *pParent)
+ : QAbstractTableModel(pParent)
+ , m_pFormEditorWidget(pParent)
+{
+ prepare();
+}
+
+UIFormEditorModel::~UIFormEditorModel()
+{
+ /* Delete the cached data: */
+ qDeleteAll(m_dataList);
+ m_dataList.clear();
+}
+
+void UIFormEditorModel::clearForm()
+{
+ beginRemoveRows(QModelIndex(), 0, m_dataList.size());
+ qDeleteAll(m_dataList);
+ m_dataList.clear();
+ endRemoveRows();
+}
+
+void UIFormEditorModel::setFormValues(const CFormValueVector &values)
+{
+ /* Delete old lines: */
+ clearForm();
+
+ /* Add new lines: */
+ beginInsertRows(QModelIndex(), 0, values.size() - 1);
+ foreach (const CFormValue &comValue, values)
+ m_dataList << new UIFormEditorRow(view(), m_pFormEditorWidget, comValue);
+ endInsertRows();
+}
+
+int UIFormEditorModel::childCount() const
+{
+ return rowCount();
+}
+
+QITableViewRow *UIFormEditorModel::childItem(int iIndex) const
+{
+ /* Make sure index within the bounds: */
+ AssertReturn(iIndex >= 0 && iIndex < m_dataList.size(), 0);
+ /* Return corresponding row: */
+ return m_dataList[iIndex];
+}
+
+QModelIndex UIFormEditorModel::index(int iRow, int iColumn, const QModelIndex &parentIdx /* = QModelIndex() */) const
+{
+ /* No index for unknown items: */
+ if (!hasIndex(iRow, iColumn, parentIdx))
+ return QModelIndex();
+
+ /* Provide index users with packed item pointer: */
+ UIFormEditorRow *pItem = iRow >= 0 && iRow < m_dataList.size() ? m_dataList.at(iRow) : 0;
+ return pItem ? createIndex(iRow, iColumn, pItem) : QModelIndex();
+}
+
+Qt::ItemFlags UIFormEditorModel::flags(const QModelIndex &index) const
+{
+ /* Check index validness: */
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIFormEditorDataType_Name:
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ case UIFormEditorDataType_Value:
+ {
+ Qt::ItemFlags enmFlags = Qt::NoItemFlags;
+ if (m_dataList[index.row()]->isEnabled())
+ {
+ enmFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ enmFlags |= m_dataList[index.row()]->valueType() == KFormValueType_Boolean
+ ? Qt::ItemIsUserCheckable : Qt::ItemIsEditable;
+ }
+ return enmFlags;
+ }
+ default:
+ return Qt::NoItemFlags;
+ }
+}
+
+int UIFormEditorModel::rowCount(const QModelIndex &) const
+{
+ return m_dataList.size();
+}
+
+int UIFormEditorModel::columnCount(const QModelIndex &) const
+{
+ return UIFormEditorDataType_Max;
+}
+
+QVariant UIFormEditorModel::headerData(int iSection, Qt::Orientation enmOrientation, int iRole) const
+{
+ /* Check argument validness: */
+ if (iRole != Qt::DisplayRole || enmOrientation != Qt::Horizontal)
+ return QVariant();
+ /* Switch for different columns: */
+ switch (iSection)
+ {
+ case UIFormEditorDataType_Name:
+ return UIFormEditorWidget::tr("Name");
+ case UIFormEditorDataType_Value:
+ return UIFormEditorWidget::tr("Value");
+ default:
+ return QVariant();
+ }
+}
+
+bool UIFormEditorModel::setData(const QModelIndex &index, const QVariant &value, int iRole /* = Qt::EditRole */)
+{
+ /* Check index validness: */
+ if (!index.isValid())
+ return false;
+ /* Switch for different roles: */
+ switch (iRole)
+ {
+ /* Checkstate role: */
+ case Qt::CheckStateRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIFormEditorDataType_Value:
+ {
+ if (m_dataList[index.row()]->valueType() == KFormValueType_Boolean)
+ {
+ const Qt::CheckState enmCheckState = static_cast<Qt::CheckState>(value.toInt());
+ m_dataList[index.row()]->setBool(enmCheckState == Qt::Checked);
+ emit dataChanged(index, index);
+ updateGeneration();
+ return true;
+ }
+ else
+ return false;
+ }
+ default:
+ return false;
+ }
+ }
+ /* Edit role: */
+ case Qt::EditRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIFormEditorDataType_Value:
+ {
+ switch (m_dataList[index.row()]->valueType())
+ {
+ case KFormValueType_String:
+ {
+ if (value.canConvert<TextData>())
+ m_dataList[index.row()]->setText(value.value<TextData>());
+ else
+ m_dataList[index.row()]->setString(value.toString());
+ emit dataChanged(index, index);
+ updateGeneration();
+ return true;
+ }
+ case KFormValueType_Choice:
+ {
+ m_dataList[index.row()]->setChoice(value.value<ChoiceData>());
+ emit dataChanged(index, index);
+ updateGeneration();
+ return true;
+ }
+ case KFormValueType_RangedInteger:
+ {
+ m_dataList[index.row()]->setRangedInteger(value.value<RangedIntegerData>());
+ emit dataChanged(index, index);
+ updateGeneration();
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+ default:
+ return false;
+ }
+ }
+ default:
+ return false;
+ }
+}
+
+QVariant UIFormEditorModel::data(const QModelIndex &index, int iRole) const
+{
+ /* Check index validness: */
+ if (!index.isValid())
+ return QVariant();
+ /* Switch for different roles: */
+ switch (iRole)
+ {
+ /* Decoration role: */
+ case Qt::DecorationRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIFormEditorDataType_Name: return iconHint(m_dataList[index.row()]->nameToString());
+ default: return QVariant();
+ }
+ }
+ /* Checkstate role: */
+ case Qt::CheckStateRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIFormEditorDataType_Value:
+ return m_dataList[index.row()]->valueType() == KFormValueType_Boolean
+ ? m_dataList[index.row()]->toBool()
+ ? Qt::Checked
+ : Qt::Unchecked
+ : QVariant();
+ default:
+ return QVariant();
+ }
+ }
+ /* Display role: */
+ case Qt::DisplayRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIFormEditorDataType_Name:
+ return m_dataList[index.row()]->nameToString();
+ case UIFormEditorDataType_Value:
+ return m_dataList[index.row()]->valueType() != KFormValueType_Boolean
+ ? m_dataList[index.row()]->valueToString()
+ : QVariant();
+ default:
+ return QVariant();
+ }
+ }
+ /* Edit role: */
+ case Qt::EditRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIFormEditorDataType_Value:
+ {
+ switch (m_dataList[index.row()]->valueType())
+ {
+ case KFormValueType_String:
+ {
+ if (m_dataList[index.row()]->isMultilineString())
+ {
+ TextData td = m_dataList[index.row()]->toText();
+ td.setIndex(index);
+ return QVariant::fromValue(td);
+ }
+ else
+ return QVariant::fromValue(m_dataList[index.row()]->toString());
+ }
+ case KFormValueType_Choice:
+ return QVariant::fromValue(m_dataList[index.row()]->toChoice());
+ case KFormValueType_RangedInteger:
+ return QVariant::fromValue(m_dataList[index.row()]->toRangedInteger());
+ default:
+ return QVariant();
+ }
+ }
+ default:
+ return QVariant();
+ }
+ }
+ /* Alignment role: */
+ case Qt::TextAlignmentRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIFormEditorDataType_Name:
+ return (int)(Qt::AlignLeft | Qt::AlignVCenter);
+ case UIFormEditorDataType_Value:
+ return m_dataList[index.row()]->valueType() != KFormValueType_Boolean
+ ? (int)(Qt::AlignLeft | Qt::AlignVCenter)
+ : (int)(Qt::AlignCenter);
+ default:
+ return QVariant();
+ }
+ }
+ default:
+ return QVariant();
+ }
+}
+
+void UIFormEditorModel::createTextDataEditor(const QModelIndex &index)
+{
+ /* Create dialog on-the-fly: */
+ QPointer<QIDialog> pDialog = new QIDialog(view());
+ if (pDialog)
+ {
+ /* We will need that pointer: */
+ QTextEdit *pEditor = 0;
+ /* Create layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(pDialog);
+ if (pLayout)
+ {
+ /* Create text-editor: */
+ pEditor = new QTextEdit;
+ if (pEditor)
+ {
+ const TextData td = data(index, Qt::EditRole).value<TextData>();
+ pEditor->setPlainText(td.text());
+ pLayout->addWidget(pEditor);
+ }
+ /* Create button-box: */
+ QIDialogButtonBox *pBox = new QIDialogButtonBox;
+ if (pBox)
+ {
+ pBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+ connect(pBox, &QIDialogButtonBox::accepted, pDialog.data(), &QIDialog::accept);
+ connect(pBox, &QIDialogButtonBox::rejected, pDialog.data(), &QIDialog::reject);
+ pLayout->addWidget(pBox);
+ }
+ }
+ /* Execute the dialog: */
+ if (pDialog->execute() == QDialog::Accepted)
+ {
+ const TextData td = TextData(pEditor->toPlainText(), index);
+ setData(index, QVariant::fromValue(td));
+ }
+ /* Cleanup: */
+ delete pDialog;
+ }
+}
+
+void UIFormEditorModel::prepare()
+{
+ /* Prepare hardcoded icons map: */
+ m_icons["Name"] = UIIconPool::iconSet(":/name_16px.png");
+ m_icons["Display Name"] = UIIconPool::iconSet(":/name_16px.png");
+ m_icons["Type"] = UIIconPool::iconSet(":/system_type_16px.png");
+ m_icons["Version"] = UIIconPool::iconSet(":/system_version_16px.png");
+ m_icons["CPU"] = UIIconPool::iconSet(":/cpu_16px.png");
+ m_icons["Memory"] = UIIconPool::iconSet(":/ram_16px.png");
+ m_icons["Description"] = UIIconPool::iconSet(":/description_16px.png");
+ m_icons["Bucket"] = UIIconPool::iconSet(":/bucket_16px.png");
+ m_icons["Keep Object"] = UIIconPool::iconSet(":/keep_object_16px.png");
+ m_icons["Launch VM"] = UIIconPool::iconSet(":/launch_vm_16px.png");
+ m_icons["Availability Domain"] = UIIconPool::iconSet(":/availability_domain_16px.png");
+ m_icons["Shape"] = UIIconPool::iconSet(":/shape_16px.png");
+ m_icons["Disk Size"] = UIIconPool::iconSet(":/disk_size_16px.png");
+ m_icons["VCN"] = UIIconPool::iconSet(":/vcn_16px.png");
+ m_icons["Subnet"] = UIIconPool::iconSet(":/subnet_16px.png");
+ m_icons["Assign Public IP"] = UIIconPool::iconSet(":/assign_public_ip_16px.png");
+}
+
+QITableView *UIFormEditorModel::view() const
+{
+ return m_pFormEditorWidget->view();
+}
+
+void UIFormEditorModel::updateGeneration()
+{
+ for (int i = 0; i < m_dataList.size(); ++i)
+ {
+ UIFormEditorRow *pRow = m_dataList.at(i);
+ if (pRow->isGenerationChanged())
+ {
+ pRow->updateValueCells();
+ const QModelIndex changedIndex = index(i, 1);
+ emit dataChanged(changedIndex, changedIndex);
+ }
+ }
+}
+
+QIcon UIFormEditorModel::iconHint(const QString &strItemName) const
+{
+ return m_icons.value(strItemName, UIIconPool::iconSet(":/session_info_16px.png"));
+}
+
+
+/*********************************************************************************************************************************
+* Class UIFormEditorProxyModel implementation. *
+*********************************************************************************************************************************/
+
+UIFormEditorProxyModel::UIFormEditorProxyModel(QObject *pParent /* = 0 */)
+ : QSortFilterProxyModel(pParent)
+{
+}
+
+int UIFormEditorProxyModel::childCount() const
+{
+ return rowCount();
+}
+
+QITableViewRow *UIFormEditorProxyModel::childItem(int iIndex) const
+{
+ /* Make sure iIndex within the bounds: */
+ AssertReturn(iIndex >= 0 && iIndex < rowCount(), 0);
+ /* Acquire actual index of source model: */
+ const QModelIndex i = sourceModel()->index(iIndex, 0);
+ AssertReturn(i.isValid(), 0);
+ /* Get packed item pointer: */
+ UIFormEditorRow *pItem = static_cast<UIFormEditorRow*>(i.internalPointer());
+ AssertReturn(pItem, 0);
+ return pItem;
+}
+
+bool UIFormEditorProxyModel::filterAcceptsRow(int iSourceRow, const QModelIndex &sourceParent) const
+{
+ /* Acquire actual index of source model: */
+ QModelIndex i = sourceModel()->index(iSourceRow, 0, sourceParent);
+ if (i.isValid())
+ {
+ /* Get packed item pointer: */
+ UIFormEditorRow *pItem = static_cast<UIFormEditorRow*>(i.internalPointer());
+ /* Filter invisible items: */
+ if (!pItem->isVisible())
+ return false;
+ }
+ return true;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIFormEditorView implementation. *
+*********************************************************************************************************************************/
+
+UIFormEditorView::UIFormEditorView(QWidget * /* pParent = 0 */)
+{
+ /* Configure widget a bit: */
+ setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed);
+}
+
+int UIFormEditorView::childCount() const
+{
+ /* Redirect request to model: */
+ AssertPtrReturn(model(), 0);
+ return qobject_cast<UIFormEditorProxyModel*>(model())->childCount();
+}
+
+QITableViewRow *UIFormEditorView::childItem(int iIndex) const
+{
+ /* Redirect request to model: */
+ AssertPtrReturn(model(), 0);
+ return qobject_cast<UIFormEditorProxyModel*>(model())->childItem(iIndex);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIFormEditorWidget implementation. *
+*********************************************************************************************************************************/
+
+UIFormEditorWidget::UIFormEditorWidget(QWidget *pParent /* = 0 */,
+ UINotificationCenter *pNotificationCenter /* = 0 */)
+ : QWidget(pParent)
+ , m_pNotificationCenter(pNotificationCenter)
+ , m_pTableView(0)
+ , m_pTableModel(0)
+ , m_pItemEditorFactory(0)
+{
+ prepare();
+}
+
+UIFormEditorWidget::~UIFormEditorWidget()
+{
+ cleanup();
+}
+
+UIFormEditorView *UIFormEditorWidget::view() const
+{
+ return m_pTableView;
+}
+
+QHeaderView *UIFormEditorWidget::horizontalHeader() const
+{
+ AssertPtrReturn(m_pTableView, 0);
+ return m_pTableView->horizontalHeader();
+}
+
+QHeaderView *UIFormEditorWidget::verticalHeader() const
+{
+ AssertPtrReturn(m_pTableView, 0);
+ return m_pTableView->verticalHeader();
+}
+
+void UIFormEditorWidget::setWhatsThis(const QString &strWhatsThis)
+{
+ AssertPtrReturnVoid(m_pTableView);
+ m_pTableView->setWhatsThis(strWhatsThis);
+}
+
+void UIFormEditorWidget::clearForm()
+{
+ m_pTableModel->clearForm();
+ adjustTable();
+}
+
+void UIFormEditorWidget::setValues(const QVector<CFormValue> &values)
+{
+ m_pTableModel->setFormValues(values);
+ adjustTable();
+}
+
+void UIFormEditorWidget::setForm(const CForm &comForm)
+{
+ AssertPtrReturnVoid(m_pTableModel);
+ /// @todo add some check..
+ setValues(comForm.GetValues());
+}
+
+void UIFormEditorWidget::setVirtualSystemDescriptionForm(const CVirtualSystemDescriptionForm &comForm)
+{
+ AssertPtrReturnVoid(m_pTableModel);
+ /// @todo add some check..
+ setValues(comForm.GetValues());
+}
+
+void UIFormEditorWidget::makeSureEditorDataCommitted()
+{
+ m_pTableView->makeSureEditorDataCommitted();
+}
+
+bool UIFormEditorWidget::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Process events for table only: */
+ if (pObject != m_pTableView)
+ return QWidget::eventFilter(pObject, pEvent);
+
+ /* Process different event-types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Show:
+ case QEvent::Resize:
+ {
+ /* Adjust table: */
+ adjustTable();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QWidget::eventFilter(pObject, pEvent);
+}
+
+void UIFormEditorWidget::prepare()
+{
+ /* Create layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create model: */
+ m_pTableModel = new UIFormEditorModel(this);
+
+ /* Create proxy-model: */
+ UIFormEditorProxyModel *pProxyModel = new UIFormEditorProxyModel(this);
+ if (pProxyModel)
+ pProxyModel->setSourceModel(m_pTableModel);
+
+ /* Create view: */
+ m_pTableView = new UIFormEditorView(this);
+ if (m_pTableView)
+ {
+ m_pTableView->setModel(pProxyModel);
+ m_pTableView->setTabKeyNavigation(false);
+ m_pTableView->verticalHeader()->hide();
+ m_pTableView->verticalHeader()->setDefaultSectionSize((int)(m_pTableView->verticalHeader()->minimumSectionSize() * 1.33));
+ m_pTableView->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_pTableView->installEventFilter(this);
+
+ /* We certainly have abstract item delegate: */
+ QAbstractItemDelegate *pAbstractItemDelegate = m_pTableView->itemDelegate();
+ if (pAbstractItemDelegate)
+ {
+ /* But is this also styled item delegate? */
+ QIStyledItemDelegate *pStyledItemDelegate = qobject_cast<QIStyledItemDelegate*>(pAbstractItemDelegate);
+ if (pStyledItemDelegate)
+ {
+ /* Configure item delegate: */
+ pStyledItemDelegate->setWatchForEditorDataCommits(true);
+
+ /* Create new item editor factory: */
+ m_pItemEditorFactory = new QItemEditorFactory;
+ if (m_pItemEditorFactory)
+ {
+ /* Register TextEditor as the TextData editor: */
+ int iTextId = qRegisterMetaType<TextData>();
+ QStandardItemEditorCreator<TextEditor> *pTextEditorItemCreator = new QStandardItemEditorCreator<TextEditor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iTextId, pTextEditorItemCreator);
+
+ /* Register ChoiceEditor as the ChoiceData editor: */
+ int iChoiceId = qRegisterMetaType<ChoiceData>();
+ QStandardItemEditorCreator<ChoiceEditor> *pChoiceEditorItemCreator = new QStandardItemEditorCreator<ChoiceEditor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iChoiceId, pChoiceEditorItemCreator);
+
+ /* Register RangedIntegerEditor as the RangedIntegerData editor: */
+ int iRangedIntegerId = qRegisterMetaType<RangedIntegerData>();
+ QStandardItemEditorCreator<RangedIntegerEditor> *pRangedIntegerEditorItemCreator = new QStandardItemEditorCreator<RangedIntegerEditor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iRangedIntegerId, pRangedIntegerEditorItemCreator);
+
+ /* Set newly created item editor factory for table delegate: */
+ pStyledItemDelegate->setItemEditorFactory(m_pItemEditorFactory);
+ }
+ }
+ }
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pTableView);
+ }
+ }
+}
+
+void UIFormEditorWidget::cleanup()
+{
+ delete m_pItemEditorFactory;
+ m_pItemEditorFactory = 0;
+}
+
+void UIFormEditorWidget::adjustTable()
+{
+ m_pTableView->horizontalHeader()->setStretchLastSection(false);
+ /* If table is NOT empty: */
+ if (m_pTableModel->rowCount())
+ {
+ /* Resize table to contents size-hint and emit a spare place for first column: */
+ m_pTableView->resizeColumnsToContents();
+ const int iFullWidth = m_pTableView->viewport()->width();
+ const int iNameWidth = m_pTableView->horizontalHeader()->sectionSize(UIFormEditorDataType_Name);
+ const int iValueWidth = qMax(0, iFullWidth - iNameWidth);
+ m_pTableView->horizontalHeader()->resizeSection(UIFormEditorDataType_Value, iValueWidth);
+ }
+ /* If table is empty: */
+ else
+ {
+ /* Resize table columns to be equal in size: */
+ const int iFullWidth = m_pTableView->viewport()->width();
+ m_pTableView->horizontalHeader()->resizeSection(UIFormEditorDataType_Name, iFullWidth / 2);
+ m_pTableView->horizontalHeader()->resizeSection(UIFormEditorDataType_Value, iFullWidth / 2);
+ }
+ m_pTableView->horizontalHeader()->setStretchLastSection(true);
+}
+
+
+#include "UIFormEditorWidget.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIFormEditorWidget.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIFormEditorWidget.h
new file mode 100644
index 00000000..cae60a11
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIFormEditorWidget.h
@@ -0,0 +1,120 @@
+/* $Id: UIFormEditorWidget.h $ */
+/** @file
+ * VBox Qt GUI - UIFormEditorWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIFormEditorWidget_h
+#define FEQT_INCLUDED_SRC_widgets_UIFormEditorWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPointer>
+#include <QWidget>
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CFormValue.h"
+
+/* Forward declarations: */
+class QHeaderView;
+class QItemEditorFactory;
+class UIFormEditorModel;
+class UIFormEditorView;
+class UINotificationCenter;
+class CForm;
+class CVirtualSystemDescriptionForm;
+
+/** QWidget subclass representing model/view Form Editor widget. */
+class UIFormEditorWidget : public QWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Form Editor widget passing @a pParent to the base-class.
+ * @param pNotificationCenter Brings the notification-center this widget should report to. */
+ UIFormEditorWidget(QWidget *pParent = 0, UINotificationCenter *pNotificationCenter = 0);
+ /** Destructs Form Editor widget. */
+ virtual ~UIFormEditorWidget() RT_OVERRIDE;
+
+ /** Returns the notification-center reference. */
+ UINotificationCenter *notificationCenter() const { return m_pNotificationCenter; }
+ /** Defines the @a pNotificationCenter reference. */
+ void setNotificationCenter(UINotificationCenter *pNotificationCenter) { m_pNotificationCenter = pNotificationCenter; }
+
+ /** Returns table-view reference. */
+ UIFormEditorView *view() const;
+ /** Returns horizontal header reference. */
+ QHeaderView *horizontalHeader() const;
+ /** Returns vertical header reference. */
+ QHeaderView *verticalHeader() const;
+ /** Defines table-view @a strWhatsThis help text. */
+ void setWhatsThis(const QString &strWhatsThis);
+
+ /** Clears form. */
+ void clearForm();
+ /** Defines @a values to be edited. */
+ void setValues(const QVector<CFormValue> &values);
+ /** Defines @a comForm to be edited. */
+ void setForm(const CForm &comForm);
+ /** Defines virtual system description @a comForm to be edited. */
+ void setVirtualSystemDescriptionForm(const CVirtualSystemDescriptionForm &comForm);
+
+ /** Makes sure current editor data committed. */
+ void makeSureEditorDataCommitted();
+
+protected:
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Adjusts table column sizes. */
+ void adjustTable();
+
+ /** Holds the notification-center reference. */
+ UINotificationCenter *m_pNotificationCenter;
+
+ /** Holds the table-view instance. */
+ UIFormEditorView *m_pTableView;
+ /** Holds the table-model instance. */
+ UIFormEditorModel *m_pTableModel;
+
+ /** Holds the item editor factory instance. */
+ QItemEditorFactory *m_pItemEditorFactory;
+};
+
+/** Safe pointer to Form Editor widget. */
+typedef QPointer<UIFormEditorWidget> UIFormEditorWidgetPointer;
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIFormEditorWidget_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIGuestOSTypeSelectionButton.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIGuestOSTypeSelectionButton.cpp
new file mode 100644
index 00000000..0ca556d2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIGuestOSTypeSelectionButton.cpp
@@ -0,0 +1,112 @@
+/* $Id: UIGuestOSTypeSelectionButton.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGuestOSTypeSelectionButton class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes */
+#include <QMenu>
+#include <QSignalMapper>
+#include <QStyle>
+
+/* GUI includes */
+#include "UICommon.h"
+#include "UIGuestOSTypeSelectionButton.h"
+#include "UIIconPool.h"
+
+
+UIGuestOSTypeSelectionButton::UIGuestOSTypeSelectionButton(QWidget *pParent)
+ : QIWithRetranslateUI<QPushButton>(pParent)
+{
+ /* Determine icon metric: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ setIconSize(QSize(iIconMetric, iIconMetric));
+
+ /* We have to make sure that the button has strong focus, otherwise
+ * the editing is ended when the menu is shown: */
+ setFocusPolicy(Qt::StrongFocus);
+
+ /* Create a signal mapper so that we not have to react to
+ * every single menu activation ourself: */
+ m_pSignalMapper = new QSignalMapper(this);
+ if (m_pSignalMapper)
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ connect(m_pSignalMapper, static_cast<void(QSignalMapper::*)(const QString &)>(&QSignalMapper::mappedString),
+ this, &UIGuestOSTypeSelectionButton::setOSTypeId);
+#else
+ connect(m_pSignalMapper, static_cast<void(QSignalMapper::*)(const QString &)>(&QSignalMapper::mapped),
+ this, &UIGuestOSTypeSelectionButton::setOSTypeId);
+#endif
+
+ /* Create main menu: */
+ m_pMainMenu = new QMenu(pParent);
+ if (m_pMainMenu)
+ setMenu(m_pMainMenu);
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+bool UIGuestOSTypeSelectionButton::isMenuShown() const
+{
+ return m_pMainMenu->isVisible();
+}
+
+void UIGuestOSTypeSelectionButton::setOSTypeId(const QString &strOSTypeId)
+{
+ m_strOSTypeId = strOSTypeId;
+ CGuestOSType enmType = uiCommon().vmGuestOSType(strOSTypeId);
+
+#ifndef VBOX_WS_MAC
+ /* Looks ugly on the Mac: */
+ setIcon(generalIconPool().guestOSTypePixmapDefault(enmType.GetId()));
+#endif
+
+ setText(enmType.GetDescription());
+}
+
+void UIGuestOSTypeSelectionButton::retranslateUi()
+{
+ populateMenu();
+}
+
+void UIGuestOSTypeSelectionButton::populateMenu()
+{
+ /* Clea initially: */
+ m_pMainMenu->clear();
+
+ /* Create a list of all possible OS types: */
+ foreach(const QString &strFamilyId, uiCommon().vmGuestOSFamilyIDs())
+ {
+ QMenu *pSubMenu = m_pMainMenu->addMenu(uiCommon().vmGuestOSFamilyDescription(strFamilyId));
+ foreach (const CGuestOSType &comType, uiCommon().vmGuestOSTypeList(strFamilyId))
+ {
+ QAction *pAction = pSubMenu->addAction(generalIconPool().guestOSTypePixmapDefault(comType.GetId()),
+ comType.GetDescription());
+ connect(pAction, &QAction::triggered,
+ m_pSignalMapper, static_cast<void(QSignalMapper::*)(void)>(&QSignalMapper::map));
+ m_pSignalMapper->setMapping(pAction, comType.GetId());
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIGuestOSTypeSelectionButton.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIGuestOSTypeSelectionButton.h
new file mode 100644
index 00000000..5990334b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIGuestOSTypeSelectionButton.h
@@ -0,0 +1,84 @@
+/* $Id: UIGuestOSTypeSelectionButton.h $ */
+/** @file
+ * VBox Qt GUI - UIGuestOSTypeSelectionButton class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIGuestOSTypeSelectionButton_h
+#define FEQT_INCLUDED_SRC_widgets_UIGuestOSTypeSelectionButton_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPushButton>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QMenu;
+class QSignalMapper;
+
+/** QPushButton sub-class for choosing guest OS family/type inside appliance editor widget. */
+class UIGuestOSTypeSelectionButton : public QIWithRetranslateUI<QPushButton>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs a button passing @a pParent to the base-class. */
+ UIGuestOSTypeSelectionButton(QWidget *pParent);
+
+ /** Returns whether the menu is shown. */
+ bool isMenuShown() const;
+
+ /** Returns current guest OS type ID. */
+ QString osTypeId() const { return m_strOSTypeId; }
+
+public slots:
+
+ /** Defines current guest @a strOSTypeId. */
+ void setOSTypeId(const QString &strOSTypeId);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private:
+
+ /** Populates menu. */
+ void populateMenu();
+
+ /** Holds the current guest OS type ID. */
+ QString m_strOSTypeId;
+
+ /** Holds the menu instance. */
+ QMenu *m_pMainMenu;
+ /** Holds the signal mapper instance. */
+ QSignalMapper *m_pSignalMapper;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIGuestOSTypeSelectionButton_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UILineTextEdit.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UILineTextEdit.cpp
new file mode 100644
index 00000000..051eee5d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UILineTextEdit.cpp
@@ -0,0 +1,136 @@
+/* $Id: UILineTextEdit.cpp $ */
+/** @file
+ * VBox Qt GUI - UILineTextEdit class definitions.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDialogButtonBox>
+#include <QFile>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QTextEdit>
+#include <QTextStream>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "UICommon.h"
+#include "UILineTextEdit.h"
+
+
+////////////////////////////////////////////////////////////////////////////////
+// UITextEditor
+
+UITextEditor::UITextEditor(QWidget *pParent /* = NULL */)
+ : QIWithRetranslateUI<QIDialog>(pParent)
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ pMainLayout->setContentsMargins(12, 12, 12, 12);
+
+ /* We need a text editor */
+ m_pTextEdit = new QTextEdit(this);
+ pMainLayout->addWidget(m_pTextEdit);
+ /* and some buttons to interact with */
+ m_pButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
+ m_pOpenButton = new QPushButton(this);
+ m_pButtonBox->addButton(m_pOpenButton, QDialogButtonBox::ActionRole);
+ pMainLayout->addWidget(m_pButtonBox);
+ /* Connect the buttons so that they are useful */
+ connect(m_pButtonBox, &QDialogButtonBox::accepted,
+ this, &UITextEditor::accept);
+ connect(m_pButtonBox, &QDialogButtonBox::rejected,
+ this, &UITextEditor::reject);
+ connect(m_pOpenButton, &QPushButton::clicked,
+ this, &UITextEditor::open);
+
+ /* Applying language settings */
+ retranslateUi();
+}
+
+void UITextEditor::setText(const QString& strText)
+{
+ m_pTextEdit->setText(strText);
+}
+
+QString UITextEditor::text() const
+{
+ return m_pTextEdit->toPlainText();
+}
+
+void UITextEditor::retranslateUi()
+{
+ setWindowTitle(tr("Edit text"));
+ m_pOpenButton->setText(tr("&Replace..."));
+ m_pOpenButton->setToolTip(tr("Replaces the current text with the content of a file."));
+}
+
+void UITextEditor::open()
+{
+ QString fileName = QIFileDialog::getOpenFileName(uiCommon().documentsPath(), tr("Text (*.txt);;All (*.*)"), this, tr("Select a file to open..."));
+ if (!fileName.isEmpty())
+ {
+ QFile file(fileName);
+ if (file.open(QFile::ReadOnly))
+ {
+ QTextStream in(&file);
+ m_pTextEdit->setPlainText(in.readAll());
+ }
+ }
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// UILineTextEdit
+
+UILineTextEdit::UILineTextEdit(QWidget *pParent /* = NULL */)
+ : QIWithRetranslateUI<QPushButton>(pParent)
+{
+ connect(this, &UILineTextEdit::clicked,
+ this, &UILineTextEdit::edit);
+
+ /* Don't interpret the Enter Key. */
+ setAutoDefault(false);
+ setDefault(false);
+
+ setFocusPolicy(Qt::StrongFocus);
+ retranslateUi();
+}
+
+void UILineTextEdit::retranslateUi()
+{
+ QPushButton::setText(tr("&Edit"));
+}
+
+void UILineTextEdit::edit()
+{
+ UITextEditor te(this);
+ te.setText(m_strText);
+ if (te.exec() == QDialog::Accepted)
+ {
+ m_strText = te.text();
+ /* Notify listener(s) about we finished: */
+ emit sigFinished(this);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UILineTextEdit.h b/src/VBox/Frontends/VirtualBox/src/widgets/UILineTextEdit.h
new file mode 100644
index 00000000..f2e44b58
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UILineTextEdit.h
@@ -0,0 +1,101 @@
+/* $Id: UILineTextEdit.h $ */
+/** @file
+ * VBox Qt GUI - UILineTextEdit class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UILineTextEdit_h
+#define FEQT_INCLUDED_SRC_widgets_UILineTextEdit_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* VBox includes */
+#include "QIDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Qt includes */
+#include <QPushButton>
+
+/* Qt forward declarations */
+class QTextEdit;
+class QDialogButtonBox;
+
+////////////////////////////////////////////////////////////////////////////////
+// UITextEditor
+
+class UITextEditor: public QIWithRetranslateUI<QIDialog>
+{
+ Q_OBJECT;
+
+public:
+ UITextEditor(QWidget *pParent = NULL);
+
+ void setText(const QString& strText);
+ QString text() const;
+
+protected:
+ void retranslateUi();
+
+private slots:
+ void open();
+
+private:
+ /* Private member vars */
+ QTextEdit *m_pTextEdit;
+ QDialogButtonBox *m_pButtonBox;
+ QPushButton *m_pOpenButton;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// UILineTextEdit
+
+class UILineTextEdit: public QIWithRetranslateUI<QPushButton>
+{
+ Q_OBJECT;
+
+signals:
+
+ /* Notifier: Editing stuff: */
+ void sigFinished(QWidget *pThis);
+
+public:
+ UILineTextEdit(QWidget *pParent = NULL);
+
+ void setText(const QString& strText) { m_strText = strText; }
+ QString text() const { return m_strText; }
+
+protected:
+ void retranslateUi();
+
+private slots:
+ void edit();
+
+private:
+ /* Private member vars */
+ QString m_strText;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UILineTextEdit_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMediaComboBox.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIMediaComboBox.cpp
new file mode 100644
index 00000000..10b199c6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMediaComboBox.cpp
@@ -0,0 +1,278 @@
+/* $Id: UIMediaComboBox.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMediaComboBox class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAbstractItemView>
+#include <QDir>
+#include <QFileInfo>
+
+/* GUI includes: */
+#include "UIMediaComboBox.h"
+#include "UIMedium.h"
+
+
+UIMediaComboBox::UIMediaComboBox(QWidget *pParent /* = 0 */)
+ : QComboBox(pParent)
+ , m_enmMediaType(UIMediumDeviceType_Invalid)
+ , m_uMachineId(QUuid())
+ , m_uLastItemId(QUuid())
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIMediaComboBox::refresh()
+{
+ /* Clearing lists: */
+ clear(), m_media.clear();
+
+ /* Use the medium creation handler to add all the items: */
+ foreach (const QUuid &uMediumId, uiCommon().mediumIDs())
+ sltHandleMediumCreated(uMediumId);
+
+ /* If at least one real medium present,
+ * remove null medium: */
+ if (count() > 1)
+ {
+ removeItem(0);
+ m_media.erase(m_media.begin());
+ }
+
+ /* Notify listeners about active item changed. */
+ emit activated(currentIndex());
+}
+
+void UIMediaComboBox::repopulate()
+{
+ /* Start medium-enumeration for optical drives/images (if necessary): */
+ if ( m_enmMediaType == UIMediumDeviceType_DVD
+ && !uiCommon().isFullMediumEnumerationRequested())
+ {
+ CMediumVector comMedia;
+ comMedia << uiCommon().host().GetDVDDrives();
+ comMedia << uiCommon().virtualBox().GetDVDImages();
+ uiCommon().enumerateMedia(comMedia);
+ }
+ refresh();
+}
+
+void UIMediaComboBox::setCurrentItem(const QUuid &uItemId)
+{
+ m_uLastItemId = uItemId;
+
+ int iIndex;
+ // WORKAROUND:
+ // Note that the media combo-box may be not populated here yet,
+ // so we don't assert..
+ if (findMediaIndex(uItemId, iIndex))
+ {
+ QComboBox::setCurrentIndex(iIndex);
+ emit activated(iIndex);
+ }
+}
+
+QUuid UIMediaComboBox::id(int iIndex /* = -1 */) const
+{
+ AssertReturn(iIndex == -1 ||
+ (iIndex >= 0 && iIndex < m_media.size()),
+ QUuid());
+
+ if (iIndex == -1)
+ iIndex = currentIndex();
+ return iIndex == -1 ? QUuid() : m_media.at(iIndex).id;
+}
+
+QString UIMediaComboBox::location(int iIndex /* = -1 */) const
+{
+ AssertReturn(iIndex == -1 ||
+ (iIndex >= 0 && iIndex < m_media.size()),
+ QString());
+
+ if (iIndex == -1)
+ iIndex = currentIndex();
+ return iIndex == -1 ? QString() : m_media.at(iIndex).location;
+}
+
+void UIMediaComboBox::sltHandleMediumCreated(const QUuid &uMediumId)
+{
+ /* Search for corresponding medium: */
+ UIMedium guiMedium = uiCommon().medium(uMediumId);
+
+ /* Ignore media (and their children) which are
+ * marked as hidden or attached to hidden machines only: */
+ if (UIMedium::isMediumAttachedToHiddenMachinesOnly(guiMedium))
+ return;
+
+ /* Add only 1. NULL medium and 2. media of required type: */
+ if (!guiMedium.isNull() && guiMedium.type() != m_enmMediaType)
+ return;
+
+ /* Ignore all diffs: */
+ if (guiMedium.type() == UIMediumDeviceType_HardDisk && guiMedium.parentID() != UIMedium::nullID())
+ return;
+
+ /* Append medium into combo-box: */
+ appendItem(guiMedium);
+
+ /* Activate the required item if any: */
+ if (guiMedium.id() == m_uLastItemId)
+ setCurrentItem(guiMedium.id());
+ /* Select last added item if there is no item selected: */
+ else if (currentText().isEmpty())
+ QComboBox::setCurrentIndex(count() - 1);
+}
+
+void UIMediaComboBox::sltHandleMediumEnumerated(const QUuid &uMediumId)
+{
+ /* Search for corresponding medium: */
+ UIMedium guiMedium = uiCommon().medium(uMediumId);
+
+ /* Add only 1. NULL medium and 2. media of required type: */
+ if (!guiMedium.isNull() && guiMedium.type() != m_enmMediaType)
+ return;
+
+ /* Search for corresponding item index: */
+ int iIndex;
+ if (!findMediaIndex(guiMedium.id(), iIndex))
+ return;
+
+ /* Replace medium in combo-box: */
+ replaceItem(iIndex, guiMedium);
+
+ /* Ensure the parent dialog handles the change of the selected item's data: */
+ emit activated(currentIndex());
+}
+
+void UIMediaComboBox::sltHandleMediumDeleted(const QUuid &uMediumId)
+{
+ /* Search for corresponding item index: */
+ int iIndex;
+ if (!findMediaIndex(uMediumId, iIndex))
+ return;
+
+ /* Replace medium from combo-box: */
+ removeItem(iIndex);
+ m_media.erase(m_media.begin() + iIndex);
+
+ /* If no real medium left, add the NULL medium: */
+ if (count() == 0)
+ sltHandleMediumCreated(UIMedium::nullID());
+
+ /* Ensure the parent dialog handles the change of the selected item: */
+ emit activated(currentIndex());
+}
+
+void UIMediaComboBox::sltHandleMediumEnumerationStart()
+{
+ refresh();
+}
+
+void UIMediaComboBox::sltHandleComboActivated(int iIndex)
+{
+ AssertReturnVoid(iIndex >= 0 && iIndex < m_media.size());
+
+ m_uLastItemId = m_media.at(iIndex).id;
+
+ updateToolTip(iIndex);
+}
+
+void UIMediaComboBox::sltHandleComboHovered(const QModelIndex &index)
+{
+ /* Set the combo-box item's tooltip: */
+ const int iIndex = index.row();
+ view()->viewport()->setToolTip(QString());
+ view()->viewport()->setToolTip(m_media.at(iIndex).toolTip);
+}
+
+void UIMediaComboBox::prepare()
+{
+ /* Setup the elide mode: */
+ view()->setTextElideMode(Qt::ElideRight);
+ QSizePolicy sp1(QSizePolicy::Ignored, QSizePolicy::Fixed, QSizePolicy::ComboBox);
+ sp1.setHorizontalStretch(2);
+ setSizePolicy(sp1);
+
+ /* Setup medium-processing handlers: */
+ connect(&uiCommon(), &UICommon::sigMediumCreated,
+ this, &UIMediaComboBox::sltHandleMediumCreated);
+ connect(&uiCommon(), &UICommon::sigMediumDeleted,
+ this, &UIMediaComboBox::sltHandleMediumDeleted);
+
+ /* Setup medium-enumeration handlers: */
+ connect(&uiCommon(), &UICommon::sigMediumEnumerationStarted,
+ this, &UIMediaComboBox::sltHandleMediumEnumerationStart);
+ connect(&uiCommon(), &UICommon::sigMediumEnumerated,
+ this, &UIMediaComboBox::sltHandleMediumEnumerated);
+
+ /* Setup other connections: */
+ connect(this, static_cast<void(UIMediaComboBox::*)(int)>(&UIMediaComboBox::activated),
+ this, &UIMediaComboBox::sltHandleComboActivated);
+ connect(view(), &QAbstractItemView::entered,
+ this, &UIMediaComboBox::sltHandleComboHovered);
+}
+
+void UIMediaComboBox::updateToolTip(int iIndex)
+{
+ /* Set the combo-box tooltip: */
+ setToolTip(QString());
+ if (iIndex >= 0 && iIndex < m_media.size())
+ setToolTip(m_media.at(iIndex).toolTip);
+}
+
+void UIMediaComboBox::appendItem(const UIMedium &guiMedium)
+{
+ m_media.append(Medium(guiMedium.id(), guiMedium.location(),
+ guiMedium.toolTipCheckRO(true, false)));
+
+ insertItem(count(), guiMedium.iconCheckRO(true), guiMedium.details(true));
+}
+
+void UIMediaComboBox::replaceItem(int iIndex, const UIMedium &guiMedium)
+{
+ AssertReturnVoid(iIndex >= 0 && iIndex < m_media.size());
+
+ m_media[iIndex].id = guiMedium.id();
+ m_media[iIndex].location = guiMedium.location();
+ m_media[iIndex].toolTip = guiMedium.toolTipCheckRO(true, false);
+
+ setItemText(iIndex, guiMedium.details(true));
+ setItemIcon(iIndex, guiMedium.iconCheckRO(true));
+
+ if (iIndex == currentIndex())
+ updateToolTip(iIndex);
+}
+
+bool UIMediaComboBox::findMediaIndex(const QUuid &uId, int &iIndex)
+{
+ iIndex = 0;
+
+ for (; iIndex < m_media.size(); ++ iIndex)
+ if (m_media.at(iIndex).id == uId)
+ break;
+
+ return iIndex < m_media.size();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMediaComboBox.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIMediaComboBox.h
new file mode 100644
index 00000000..244f35ae
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMediaComboBox.h
@@ -0,0 +1,140 @@
+/* $Id: UIMediaComboBox.h $ */
+/** @file
+ * VBox Qt GUI - UIMediaComboBox class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIMediaComboBox_h
+#define FEQT_INCLUDED_SRC_widgets_UIMediaComboBox_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QComboBox>
+#include <QString>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UILibraryDefs.h"
+
+/** QComboBox subclass representing a list of registered media. */
+class SHARED_LIBRARY_STUFF UIMediaComboBox : public QComboBox
+{
+ Q_OBJECT;
+
+public:
+
+ /** Base-to-diff media map. */
+ typedef QMap<QString, QString> BaseToDiffMap;
+
+ /** Constructs media combo-box passing @a pParent to the base-class. */
+ UIMediaComboBox(QWidget *pParent = 0);
+
+ /** Performs refresh. */
+ void refresh();
+ /** Performs repopulation. */
+ void repopulate();
+
+ /** Defines @a enmMediaType. */
+ void setType(UIMediumDeviceType enmMediaType) { m_enmMediaType = enmMediaType; }
+ /** Returns media type. */
+ UIMediumDeviceType type() const { return m_enmMediaType; }
+
+ /** Defines @a uMachineId. */
+ void setMachineId(const QUuid &uMachineId) { m_uMachineId = uMachineId; }
+
+ /** Defines current item through @a uItemId. */
+ void setCurrentItem(const QUuid &uItemId);
+
+ /** Returns id of item with certain @a iIndex. */
+ QUuid id(int iIndex = -1) const;
+ /** Returns location of item with certain @a iIndex. */
+ QString location(int iIndex = -1) const;
+
+protected slots:
+
+ /** Habdles medium-created signal for medium with @a uMediumId. */
+ void sltHandleMediumCreated(const QUuid &uMediumId);
+ /** Habdles medium-enumerated signal for medium with @a uMediumId. */
+ void sltHandleMediumEnumerated(const QUuid &uMediumId);
+ /** Habdles medium-deleted signal for medium with @a uMediumId. */
+ void sltHandleMediumDeleted(const QUuid &uMediumId);
+
+ /** Handles medium-enumeration start. */
+ void sltHandleMediumEnumerationStart();
+
+ /** Handles combo activation for item with certain @a iIndex. */
+ void sltHandleComboActivated(int iIndex);
+
+ /** Handles combo hovering for item with certain @a index. */
+ void sltHandleComboHovered(const QModelIndex &index);
+
+protected:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Uses the tool-tip of the item with @a iIndex. */
+ void updateToolTip(int iIndex);
+
+ /** Appends item for certain @a guiMedium. */
+ void appendItem(const UIMedium &guiMedium);
+ /** Replases item on certain @a iPosition with new item based on @a guiMedium. */
+ void replaceItem(int iPosition, const UIMedium &guiMedium);
+
+ /** Searches for a @a iIndex of medium with certain @a uId. */
+ bool findMediaIndex(const QUuid &uId, int &iIndex);
+
+ /** Holds the media type. */
+ UIMediumDeviceType m_enmMediaType;
+
+ /** Holds the machine ID. */
+ QUuid m_uMachineId;
+
+ /** Simplified media description. */
+ struct Medium
+ {
+ Medium() {}
+ Medium(const QUuid &uId,
+ const QString &strLocation,
+ const QString &strToolTip)
+ : id(uId), location(strLocation), toolTip(strToolTip)
+ {}
+
+ QUuid id;
+ QString location;
+ QString toolTip;
+ };
+ /** Vector of simplified media descriptions. */
+ typedef QVector<Medium> Media;
+
+ /** Holds currently cached media descriptions. */
+ Media m_media;
+
+ /** Holds the last chosen medium ID. */
+ QUuid m_uLastItemId;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIMediaComboBox_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMediumSizeEditor.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIMediumSizeEditor.cpp
new file mode 100644
index 00000000..f823e802
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMediumSizeEditor.cpp
@@ -0,0 +1,303 @@
+/* $Id: UIMediumSizeEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMediumSizeEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QRegularExpressionValidator>
+#include <QSlider>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIMediumSizeEditor.h"
+#include "UITranslator.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+const qulonglong UIMediumSizeEditor::m_uSectorSize = 512;
+
+UIMediumSizeEditor::UIMediumSizeEditor(QWidget *pParent, qulonglong uMinimumSize /* = _4M */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_uSizeMin(uMinimumSize)
+ , m_uSizeMax(uiCommon().virtualBox().GetSystemProperties().GetInfoVDSize())
+ , m_iSliderScale(calculateSliderScale(m_uSizeMax))
+ , m_uSize(0)
+ , m_enmSizeSuffix(SizeSuffix_Byte)
+ , m_pSlider(0)
+ , m_pLabelMinSize(0)
+ , m_pLabelMaxSize(0)
+ , m_pEditor(0)
+{
+ /* Prepare: */
+ prepare();
+ QString strRegEx = QString("[^\\d%1]").arg(UITranslator::decimalSep());
+ m_regExNonDigitOrSeparator = QRegularExpression(strRegEx);
+}
+
+void UIMediumSizeEditor::setMediumSize(qulonglong uSize)
+{
+ /* Remember the new size: */
+ m_uSize = uSize;
+
+ /* And assign it to the slider & editor: */
+ m_pSlider->blockSignals(true);
+ m_pSlider->setValue(sizeMBToSlider(m_uSize, m_iSliderScale));
+ m_pSlider->blockSignals(false);
+ m_pEditor->blockSignals(true);
+ m_pEditor->setText(UITranslator::formatSize(m_uSize));
+ m_enmSizeSuffix = UITranslator::parseSizeSuffix(m_pEditor->text());
+ m_pEditor->blockSignals(false);
+}
+
+void UIMediumSizeEditor::retranslateUi()
+{
+ /* Translate labels: */
+ m_pLabelMinSize->setText(UITranslator::formatSize(m_uSizeMin));
+ m_pLabelMaxSize->setText(UITranslator::formatSize(m_uSizeMax));
+
+ /* Translate fields: */
+ m_pSlider->setToolTip(tr("Holds the size of this medium."));
+ m_pEditor->setToolTip(tr("Holds the size of this medium."));
+ m_pLabelMinSize->setToolTip(tr("Minimum size for this medium."));
+ m_pLabelMaxSize->setToolTip(tr("Maximum size for this medium."));
+}
+
+void UIMediumSizeEditor::sltSizeSliderChanged(int iValue)
+{
+ /* Update the current size: */
+ m_uSize = sliderToSizeMB(iValue, m_iSliderScale);
+ /* Update the other widget: */
+ m_pEditor->blockSignals(true);
+ m_pEditor->setText(UITranslator::formatSize(m_uSize));
+ m_enmSizeSuffix = UITranslator::parseSizeSuffix(m_pEditor->text());
+ m_pEditor->blockSignals(false);
+ /* Notify the listeners: */
+ emit sigSizeChanged(m_uSize);
+}
+
+void UIMediumSizeEditor::sltSizeEditorTextChanged()
+{
+ QString strSizeString = ensureSizeSuffix(m_pEditor->text());
+
+
+ m_pEditor->blockSignals(true);
+ int iCursorPosition = m_pEditor->cursorPosition();
+ m_pEditor->setText(strSizeString);
+ m_pEditor->setCursorPosition(iCursorPosition);
+ m_pEditor->blockSignals(false);
+
+ /* Update the current size: */
+ m_uSize = checkSectorSizeAlignment(UITranslator::parseSize(strSizeString));
+
+ /* Update the other widget: */
+ m_pSlider->blockSignals(true);
+ m_pSlider->setValue(sizeMBToSlider(m_uSize, m_iSliderScale));
+ m_pSlider->blockSignals(false);
+ /* Notify the listeners: */
+ emit sigSizeChanged(m_uSize);
+}
+
+QString UIMediumSizeEditor::ensureSizeSuffix(const QString &strSizeString)
+{
+ /* Try to update the m_enmSizeSuffix: */
+ if (UITranslator::hasSizeSuffix(strSizeString))
+ m_enmSizeSuffix = UITranslator::parseSizeSuffix(strSizeString);
+
+ QString strOnlyDigits(strSizeString);
+ /* Remove any chars from the string except digits and decimal separator and then add a space and size suffix: */
+ return QString("%1 %2").arg(strOnlyDigits.remove(m_regExNonDigitOrSeparator)).arg(gpConverter->toString(m_enmSizeSuffix));
+}
+
+void UIMediumSizeEditor::prepare()
+{
+ /* Create layout: */
+ QGridLayout *pLayout = new QGridLayout(this);
+ if (pLayout)
+ {
+ /* Configure layout: */
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ pLayout->setColumnStretch(0, 1);
+ pLayout->setColumnStretch(1, 1);
+ pLayout->setColumnStretch(2, 0);
+
+ /* Create size slider: */
+ m_pSlider = new QSlider;
+ if (m_pSlider)
+ {
+ /* Configure slider: */
+ m_pSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ m_pSlider->setOrientation(Qt::Horizontal);
+ m_pSlider->setTickPosition(QSlider::TicksBelow);
+ m_pSlider->setFocusPolicy(Qt::StrongFocus);
+ m_pSlider->setPageStep(m_iSliderScale);
+ m_pSlider->setSingleStep(m_iSliderScale / 8);
+ m_pSlider->setTickInterval(0);
+ m_pSlider->setMinimum(sizeMBToSlider(m_uSizeMin, m_iSliderScale));
+ m_pSlider->setMaximum(sizeMBToSlider(m_uSizeMax, m_iSliderScale));
+ connect(m_pSlider, &QSlider::valueChanged,
+ this, &UIMediumSizeEditor::sltSizeSliderChanged);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pSlider, 0, 0, 1, 2, Qt::AlignTop);
+ }
+
+ /* Create minimum size label: */
+ m_pLabelMinSize = new QLabel;
+ if (m_pLabelMinSize)
+ {
+ /* Configure label: */
+ m_pLabelMinSize->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pLabelMinSize, 1, 0);
+ }
+
+ /* Create maximum size label: */
+ m_pLabelMaxSize = new QLabel;
+ if (m_pLabelMaxSize)
+ {
+ /* Configure label: */
+ m_pLabelMaxSize->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pLabelMaxSize, 1, 1);
+ }
+
+ /* Create size editor: */
+ m_pEditor = new QILineEdit;
+ if (m_pEditor)
+ {
+ /* Configure editor: */
+ m_pEditor->installEventFilter(this);
+ m_pEditor->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_pEditor->setFixedWidthByText("88888.88 MB");
+ m_pEditor->setAlignment(Qt::AlignRight);
+ m_pEditor->setValidator(new QRegularExpressionValidator(QRegularExpression(UITranslator::sizeRegexp()), this));
+ connect(m_pEditor, &QILineEdit::textChanged,
+ this, &UIMediumSizeEditor::sltSizeEditorTextChanged);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pEditor, 0, 2, Qt::AlignTop);
+ }
+ }
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+/* static */
+int UIMediumSizeEditor::calculateSliderScale(qulonglong uMaximumMediumSize)
+{
+ /* Detect how many steps to recognize between adjacent powers of 2
+ * to ensure that the last slider step is exactly that we need: */
+ int iSliderScale = 0;
+ int iPower = log2i(uMaximumMediumSize);
+ qulonglong uTickMB = (qulonglong)1 << iPower;
+ if (uTickMB < uMaximumMediumSize)
+ {
+ qulonglong uTickMBNext = (qulonglong)1 << (iPower + 1);
+ qulonglong uGap = uTickMBNext - uMaximumMediumSize;
+ iSliderScale = (int)((uTickMBNext - uTickMB) / uGap);
+#ifdef VBOX_WS_MAC
+ // WORKAROUND:
+ // There is an issue with Qt5 QSlider under OSX:
+ // Slider tick count (maximum - minimum) is limited with some
+ // "magical number" - 588351, having it more than that brings
+ // unpredictable results like slider token jumping and disappearing,
+ // so we are limiting tick count by lowering slider-scale 128 times.
+ iSliderScale /= 128;
+#endif /* VBOX_WS_MAC */
+ }
+ return qMax(iSliderScale, 8);
+}
+
+/* static */
+int UIMediumSizeEditor::log2i(qulonglong uValue)
+{
+ if (!uValue)
+ return 0;
+ int iPower = -1;
+ while (uValue)
+ {
+ ++iPower;
+ uValue >>= 1;
+ }
+ return iPower;
+}
+
+/* static */
+int UIMediumSizeEditor::sizeMBToSlider(qulonglong uValue, int iSliderScale)
+{
+ /* Make sure *any* slider value is multiple of m_uSectorSize: */
+ uValue /= m_uSectorSize;
+
+ /* Calculate result: */
+ int iPower = log2i(uValue);
+ qulonglong uTickMB = qulonglong (1) << iPower;
+ qulonglong uTickMBNext = qulonglong (1) << (iPower + 1);
+ int iStep = (uValue - uTickMB) * iSliderScale / (uTickMBNext - uTickMB);
+ int iResult = iPower * iSliderScale + iStep;
+
+ /* Return result: */
+ return iResult;
+}
+
+/* static */
+qulonglong UIMediumSizeEditor::sliderToSizeMB(int uValue, int iSliderScale)
+{
+ /* Calculate result: */
+ int iPower = uValue / iSliderScale;
+ int iStep = uValue % iSliderScale;
+ qulonglong uTickMB = qulonglong (1) << iPower;
+ qulonglong uTickMBNext = qulonglong (1) << (iPower + 1);
+ qulonglong uResult = uTickMB + (uTickMBNext - uTickMB) * iStep / iSliderScale;
+
+ /* Make sure *any* slider value is multiple of m_uSectorSize: */
+ uResult *= m_uSectorSize;
+
+ /* Return result: */
+ return uResult;
+}
+
+void UIMediumSizeEditor::updateSizeToolTips(qulonglong uSize)
+{
+ const QString strToolTip = tr("<nobr>%1 (%2 B)</nobr>").arg(UITranslator::formatSize(uSize)).arg(uSize);
+ m_pSlider->setToolTip(strToolTip);
+ m_pEditor->setToolTip(strToolTip);
+}
+
+qulonglong UIMediumSizeEditor::checkSectorSizeAlignment(qulonglong uSize)
+{
+ if (m_uSectorSize == 0 || uSize % m_uSectorSize == 0)
+ return uSize;
+ qulonglong uNewSize = (uSize / m_uSectorSize) * m_uSectorSize;
+ return uNewSize;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMediumSizeEditor.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIMediumSizeEditor.h
new file mode 100644
index 00000000..9550a75e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMediumSizeEditor.h
@@ -0,0 +1,126 @@
+/* $Id: UIMediumSizeEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIMediumSizeEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIMediumSizeEditor_h
+#define FEQT_INCLUDED_SRC_widgets_UIMediumSizeEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QRegularExpression>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIDefs.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class QSlider;
+class QString;
+class QWidget;
+class QILineEdit;
+
+/** Medium size editor widget. */
+class SHARED_LIBRARY_STUFF UIMediumSizeEditor : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about medium size changed. */
+ void sigSizeChanged(qulonglong uSize);
+
+public:
+
+ /** Constructs medium size editor passing @a pParent to the base-class. */
+ UIMediumSizeEditor(QWidget *pParent = 0, qulonglong uMinimumSize = _4M);
+
+ /** Returns the medium size. */
+ qulonglong mediumSize() const { return m_uSize; }
+ /** Sets the initial medium size as the widget is created. */
+ void setMediumSize(qulonglong uSize);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Handles size slider change. */
+ void sltSizeSliderChanged(int iValue);
+ /** Handles size editor text edit finished signal. */
+ void sltSizeEditorTextChanged();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Calculates slider scale according to passed @a uMaximumMediumSize. */
+ static int calculateSliderScale(qulonglong uMaximumMediumSize);
+ /** Returns log2 for passed @a uValue. */
+ static int log2i(qulonglong uValue);
+ /** Converts passed bytes @a uValue to slides scaled value using @a iSliderScale. */
+ static int sizeMBToSlider(qulonglong uValue, int iSliderScale);
+ /** Converts passed slider @a uValue to bytes unscaled value using @a iSliderScale. */
+ static qulonglong sliderToSizeMB(int uValue, int iSliderScale);
+ /** Updates slider/editor tool-tips. */
+ void updateSizeToolTips(qulonglong uSize);
+ /** Checks if the uSize is divisible by m_uSectorSize */
+ qulonglong checkSectorSizeAlignment(qulonglong uSize);
+ QString ensureSizeSuffix(const QString &strSizeString);
+
+ /* Holds the block size. We force m_uSize to be multiple of this number. */
+ static const qulonglong m_uSectorSize;
+ /** Holds the minimum medium size. */
+ const qulonglong m_uSizeMin;
+ /** Holds the maximum medium size. */
+ const qulonglong m_uSizeMax;
+ /** Holds the slider scale. */
+ const int m_iSliderScale;
+ /** Holds the current medium size. */
+ qulonglong m_uSize;
+ SizeSuffix m_enmSizeSuffix;
+
+ /** Holds the size slider. */
+ QSlider *m_pSlider;
+ /** Holds the minimum size label. */
+ QLabel *m_pLabelMinSize;
+ /** Holds the maximum size label. */
+ QLabel *m_pLabelMaxSize;
+ /** Holds the size editor. */
+ QILineEdit *m_pEditor;
+
+ /* A regular expression used to remove any character from a QString which is neither a digit nor decimal separator. */
+ QRegularExpression m_regExNonDigitOrSeparator;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIMediumSizeEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBar.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBar.cpp
new file mode 100644
index 00000000..e6aba74f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBar.cpp
@@ -0,0 +1,70 @@
+/* $Id: UIMenuBar.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMenuBar class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPainter>
+#include <QPaintEvent>
+#include <QPixmapCache>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIImageTools.h"
+#include "UIMenuBar.h"
+
+
+UIMenuBar::UIMenuBar(QWidget *pParent /* = 0 */)
+ : QMenuBar(pParent)
+ , m_fShowBetaLabel(false)
+{
+ /* Check for beta versions: */
+ if (uiCommon().showBetaLabel())
+ m_fShowBetaLabel = true;
+}
+
+void UIMenuBar::paintEvent(QPaintEvent *pEvent)
+{
+ /* Call to base-class: */
+ QMenuBar::paintEvent(pEvent);
+
+ /* Draw BETA label if necessary: */
+ if (m_fShowBetaLabel)
+ {
+ QPixmap betaLabel;
+ const QString key("vbox:betaLabel");
+ if (!QPixmapCache::find(key, &betaLabel))
+ {
+ betaLabel = ::betaLabel(QSize(80, 16), this);
+ QPixmapCache::insert(key, betaLabel);
+ }
+ QSize s = size();
+ QPainter painter(this);
+ painter.setClipRect(pEvent->rect());
+ const double dDpr = UIDesktopWidgetWatchdog::devicePixelRatio(this);
+ painter.drawPixmap(s.width() - betaLabel.width() / dDpr - 10, (height() - betaLabel.height() / dDpr) / 2, betaLabel);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBar.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBar.h
new file mode 100644
index 00000000..5fd3dc4d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBar.h
@@ -0,0 +1,66 @@
+/* $Id: UIMenuBar.h $ */
+/** @file
+ * VBox Qt GUI - UIMenuBar class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIMenuBar_h
+#define FEQT_INCLUDED_SRC_widgets_UIMenuBar_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMenuBar>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QPaintEvent;
+class QWidget;
+
+/** QMenuBar extension
+ * which reflects BETA label when necessary. */
+class SHARED_LIBRARY_STUFF UIMenuBar: public QMenuBar
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor, passes @a pParent to the QMenuBar constructor. */
+ UIMenuBar(QWidget *pParent = 0);
+
+protected:
+
+ /** Paint event handler. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Reflects whether we should show BETA label or not. */
+ bool m_fShowBetaLabel;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIMenuBar_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBarEditorWindow.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBarEditorWindow.cpp
new file mode 100644
index 00000000..418f5d01
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBarEditorWindow.cpp
@@ -0,0 +1,1408 @@
+/* $Id: UIMenuBarEditorWindow.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMenuBarEditorWindow class implementation.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QHBoxLayout>
+#include <QMenu>
+#include <QMenuBar>
+#include <QMetaEnum>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QStyleOptionToolButton>
+#ifndef VBOX_WS_MAC
+# include <QCheckBox>
+#endif
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UICommon.h"
+#include "UIActionPoolRuntime.h"
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMachineWindow.h"
+#include "UIMenuBarEditorWindow.h"
+#include "QIToolBar.h"
+
+/* Forward declarations: */
+class QAccessibleInterface;
+class QRect;
+class UIAccessibilityInterfaceForUIMenuBarEditorButton;
+
+
+/** Menu-bar editor button segment types. */
+enum UIMenuBarEditorSegment
+{
+ UIMenuBarEditorSegment_Button,
+ UIMenuBarEditorSegment_Menu,
+ UIMenuBarEditorSegment_Max,
+};
+
+
+/** QAccessibleWidget extension used as an accessibility interface for UIMenuBarEditor button segments. */
+class UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment : public QAccessibleInterface
+{
+public:
+
+ /** Constructs an accessibility interface.
+ * @param pParent Brings the parent interface we are linked to.
+ * @param enmIndex Brings the index of segment we are referring to. */
+ UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment(UIAccessibilityInterfaceForUIMenuBarEditorButton *pParent,
+ UIMenuBarEditorSegment enmIndex);
+
+ /** Returns whether the interface is valid. */
+ virtual bool isValid() const RT_OVERRIDE { return true; }
+
+ /** Returns the wrapped object. */
+ virtual QObject *object() const RT_OVERRIDE { return 0; }
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE;
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE { return 0; }
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int /* iIndex */) const RT_OVERRIDE { return 0; }
+ /** Returns the child at position QPoint(@a x, @a y). */
+ virtual QAccessibleInterface *childAt(int /* x */, int /* y */) const RT_OVERRIDE { return 0; }
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface * /* pChild */) const RT_OVERRIDE { return -1; }
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE;
+
+ /** Defines a @a strText for the passed @a enmTextRole. */
+ virtual void setText(QAccessible::Text /* enmTextRole */, const QString & /* strText */) RT_OVERRIDE {}
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text /* enmTextRole */) const RT_OVERRIDE;
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE { return QAccessible::Button; }
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE { return QAccessible::State(); }
+
+private:
+
+ /** Holds the parent interface we are linked to. */
+ UIAccessibilityInterfaceForUIMenuBarEditorButton *m_pParent;
+
+ /** Holds the index of segment we are referring to. */
+ const UIMenuBarEditorSegment m_enmIndex;
+};
+
+
+/** QAccessibleWidget extension used as an accessibility interface for UIMenuBarEditor buttons. */
+class UIAccessibilityInterfaceForUIMenuBarEditorButton : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject);
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ UIAccessibilityInterfaceForUIMenuBarEditorButton(QWidget *pWidget);
+ /** Destructs an accessibility interface. */
+ ~UIAccessibilityInterfaceForUIMenuBarEditorButton();
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE;
+
+ /** Returns the rect of sub-element @a enmSegment. */
+ QRect subRect(UIMenuBarEditorSegment enmSegment) const;
+ /** Returns the text of sub-element @a enmSegment. */
+ QString subText(UIMenuBarEditorSegment enmSegment) const;
+
+private:
+
+ /** Returns corresponding toolbar button. */
+ QToolButton *button() const { return qobject_cast<QToolButton*>(widget()); }
+
+ /** Holds the map of instances of sub-element interfaces. */
+ QMap<UIMenuBarEditorSegment, UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment*> m_elements;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment implementation. *
+*********************************************************************************************************************************/
+
+UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment::UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment(
+ UIAccessibilityInterfaceForUIMenuBarEditorButton *pParent,
+ UIMenuBarEditorSegment enmIndex)
+ : m_pParent(pParent)
+ , m_enmIndex(enmIndex)
+{
+}
+
+QAccessibleInterface *UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment::parent() const
+{
+ return m_pParent;
+}
+
+QRect UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment::rect() const
+{
+ return m_pParent->subRect(m_enmIndex);
+}
+
+QString UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment::text(QAccessible::Text) const
+{
+ return m_pParent->subText(m_enmIndex);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIAccessibilityInterfaceForUIMenuBarEditorButton implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+QAccessibleInterface *UIAccessibilityInterfaceForUIMenuBarEditorButton::pFactory(const QString &strClassname, QObject *pObject)
+{
+ /* Creating toolbar button accessibility interface: */
+ if ( pObject
+ && strClassname == QLatin1String("QToolButton")
+ && pObject->property("Belongs to") == "UIMenuBarEditorWidget")
+ return new UIAccessibilityInterfaceForUIMenuBarEditorButton(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+}
+
+UIAccessibilityInterfaceForUIMenuBarEditorButton::UIAccessibilityInterfaceForUIMenuBarEditorButton(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::Button)
+{
+ /* Prepare button with popup menu: */
+ if (button()->popupMode() == QToolButton::MenuButtonPopup)
+ {
+ for (int iIndex = 0; iIndex < (int)UIMenuBarEditorSegment_Max; ++iIndex)
+ m_elements[(UIMenuBarEditorSegment)iIndex] =
+ new UIAccessibilityInterfaceForUIMenuBarEditorButtonSegment(this, (UIMenuBarEditorSegment)iIndex);
+ }
+}
+
+UIAccessibilityInterfaceForUIMenuBarEditorButton::~UIAccessibilityInterfaceForUIMenuBarEditorButton()
+{
+ /* Cleanup button with popup menu: */
+ qDeleteAll(m_elements);
+ m_elements.clear();
+}
+
+int UIAccessibilityInterfaceForUIMenuBarEditorButton::childCount() const
+{
+ /* Sanity check: */
+ AssertPtrReturn(button(), 0);
+
+ /* Return child count for a button with popup menu: */
+ if (button()->popupMode() == QToolButton::MenuButtonPopup)
+ return UIMenuBarEditorSegment_Max;
+
+ /* Call to base-class: */
+ return QAccessibleWidget::childCount();
+}
+
+QAccessibleInterface *UIAccessibilityInterfaceForUIMenuBarEditorButton::child(int iIndex) const
+{
+ /* Sanity check: */
+ AssertPtrReturn(button(), 0);
+ AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
+
+ /* Return the child with the passed iIndex for a button with popup menu: */
+ if (button()->popupMode() == QToolButton::MenuButtonPopup)
+ return m_elements.value((UIMenuBarEditorSegment)iIndex);
+
+ /* Call to base-class: */
+ return QAccessibleWidget::child(iIndex);
+}
+
+QAccessible::Role UIAccessibilityInterfaceForUIMenuBarEditorButton::role() const
+{
+ /* Sanity check: */
+ AssertPtrReturn(button(), QAccessibleWidget::role());
+
+ /* Return role for button with popup menu: */
+ if (button()->popupMode() == QToolButton::MenuButtonPopup)
+ return QAccessible::ToolBar;
+
+ /* Call to base-class: */
+ return QAccessibleWidget::role();
+}
+
+QRect UIAccessibilityInterfaceForUIMenuBarEditorButton::subRect(UIMenuBarEditorSegment enmSegment) const
+{
+ /* Sanity check: */
+ AssertReturn(button()->popupMode() == QToolButton::MenuButtonPopup, QRect());
+
+ /* Return the rect of segment with the passed enmIndex for a button with popup menu: */
+ switch (enmSegment)
+ {
+ case UIMenuBarEditorSegment_Button:
+ {
+ QStyleOptionToolButton options;
+ options.initFrom(button());
+ options.features |= QStyleOptionToolButton::MenuButtonPopup;
+ QRect rect = button()->style()->subControlRect(QStyle::CC_ToolButton, &options, QStyle::SC_ToolButton);
+ rect.moveTo(button()->mapToGlobal(rect.topLeft()));
+ return rect;
+ }
+ case UIMenuBarEditorSegment_Menu:
+ {
+ QStyleOptionToolButton options;
+ options.initFrom(button());
+ options.features |= QStyleOptionToolButton::MenuButtonPopup;
+ QRect rect = button()->style()->subControlRect(QStyle::CC_ToolButton, &options, QStyle::SC_ToolButtonMenu);
+ rect.moveTo(button()->mapToGlobal(rect.topLeft()));
+ return rect;
+ }
+ default:
+ break;
+ }
+
+ /* Null rect by default: */
+ return QRect();
+}
+
+QString UIAccessibilityInterfaceForUIMenuBarEditorButton::subText(UIMenuBarEditorSegment enmSegment) const
+{
+ /* Sanity check: */
+ AssertReturn(button()->popupMode() == QToolButton::MenuButtonPopup, QString());
+
+ /* Return the text of segment with the passed enmIndex for a button with popup menu: */
+ switch (enmSegment)
+ {
+ case UIMenuBarEditorSegment_Button:
+ return UIMenuBarEditorWidget::tr("Toggle menu %1").arg(QAccessibleWidget::text(QAccessible::Description));
+ case UIMenuBarEditorSegment_Menu:
+ return UIMenuBarEditorWidget::tr("Popup menu %1").arg(QAccessibleWidget::text(QAccessible::Description));
+ default:
+ break;
+ }
+
+ /* Null string by default: */
+ return QString();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMenuBarEditorWindow implementation. *
+*********************************************************************************************************************************/
+
+UIMenuBarEditorWindow::UIMenuBarEditorWindow(UIMachineWindow *pParent, UIActionPool *pActionPool)
+#ifndef VBOX_WS_MAC
+ : UISlidingToolBar(pParent, pParent->menuBar(), new UIMenuBarEditorWidget(0, false, uiCommon().managedVMUuid(), pActionPool), UISlidingToolBar::Position_Top)
+#else
+ : UISlidingToolBar(pParent, 0, new UIMenuBarEditorWidget(0, false, uiCommon().managedVMUuid(), pActionPool), UISlidingToolBar::Position_Top)
+#endif
+{
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMenuBarEditorWidget implementation. *
+*********************************************************************************************************************************/
+
+UIMenuBarEditorWidget::UIMenuBarEditorWidget(QWidget *pParent,
+ bool fStartedFromVMSettings /* = true */,
+ const QUuid &uMachineID /* = QUuid() */,
+ UIActionPool *pActionPool /* = 0 */)
+ : QIWithRetranslateUI2<QWidget>(pParent)
+ , m_fPrepared(false)
+ , m_fStartedFromVMSettings(fStartedFromVMSettings)
+ , m_uMachineID(uMachineID)
+ , m_pActionPool(pActionPool)
+ , m_pMainLayout(0)
+ , m_pToolBar(0)
+ , m_pButtonClose(0)
+#ifndef VBOX_WS_MAC
+ , m_pCheckBoxEnable(0)
+#endif
+ , m_restrictionsOfMenuBar(UIExtraDataMetaDefs::MenuType_Invalid)
+ , m_restrictionsOfMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType_Invalid)
+ , m_restrictionsOfMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Invalid)
+ , m_restrictionsOfMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid)
+ , m_restrictionsOfMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType_Invalid)
+ , m_restrictionsOfMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Invalid)
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ , m_restrictionsOfMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Invalid)
+#endif
+#ifdef VBOX_WS_MAC
+ , m_restrictionsOfMenuWindow(UIExtraDataMetaDefs::MenuWindowActionType_Invalid)
+#endif
+ , m_restrictionsOfMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType_Invalid)
+
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIMenuBarEditorWidget::setMachineID(const QUuid &uMachineID)
+{
+ /* Remember new machine ID: */
+ m_uMachineID = uMachineID;
+ /* Prepare: */
+ prepare();
+}
+
+void UIMenuBarEditorWidget::setActionPool(UIActionPool *pActionPool)
+{
+ /* Remember new action-pool: */
+ m_pActionPool = pActionPool;
+ /* Prepare: */
+ prepare();
+}
+
+#ifndef VBOX_WS_MAC
+bool UIMenuBarEditorWidget::isMenuBarEnabled() const
+{
+ /* For VM settings only: */
+ AssertReturn(m_fStartedFromVMSettings, false);
+
+ /* Acquire enable-checkbox if possible: */
+ AssertPtrReturn(m_pCheckBoxEnable, false);
+ return m_pCheckBoxEnable->isChecked();
+}
+
+void UIMenuBarEditorWidget::setMenuBarEnabled(bool fEnabled)
+{
+ /* For VM settings only: */
+ AssertReturnVoid(m_fStartedFromVMSettings);
+
+ /* Update enable-checkbox if possible: */
+ AssertPtrReturnVoid(m_pCheckBoxEnable);
+ m_pCheckBoxEnable->setChecked(fEnabled);
+}
+#endif /* !VBOX_WS_MAC */
+
+void UIMenuBarEditorWidget::setRestrictionsOfMenuBar(UIExtraDataMetaDefs::MenuType restrictions)
+{
+ /* Cache passed restrictions: */
+ m_restrictionsOfMenuBar = restrictions;
+ /* Get static meta-object: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* We have UIExtraDataMetaDefs::MenuType enum registered, so we can enumerate it: */
+ const int iEnumIndex = smo.indexOfEnumerator("MenuType");
+ const QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::MenuType enumValue =
+ static_cast<UIExtraDataMetaDefs::MenuType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip MenuType_Invalid & MenuType_All enum-value: */
+ if (enumValue == UIExtraDataMetaDefs::MenuType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::MenuType_All)
+ continue;
+ /* Which key required action registered under? */
+ const QString strKey = gpConverter->toInternalString(enumValue);
+ if (!m_actions.contains(strKey))
+ continue;
+ /* Update action 'checked' state: */
+ m_actions.value(strKey)->setChecked(!(m_restrictionsOfMenuBar & enumValue));
+ }
+}
+
+void UIMenuBarEditorWidget::setRestrictionsOfMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType restrictions)
+{
+ /* Cache passed restrictions: */
+ m_restrictionsOfMenuApplication = restrictions;
+ /* Get static meta-object: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* We have UIExtraDataMetaDefs::MenuApplicationActionType enum registered, so we can enumerate it: */
+ const int iEnumIndex = smo.indexOfEnumerator("MenuApplicationActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::MenuApplicationActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::MenuApplicationActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip MenuApplicationActionType_Invalid & MenuApplicationActionType_All enum-value: */
+ if (enumValue == UIExtraDataMetaDefs::MenuApplicationActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::MenuApplicationActionType_All)
+ continue;
+ /* Which key required action registered under? */
+ const QString strKey = gpConverter->toInternalString(enumValue);
+ if (!m_actions.contains(strKey))
+ continue;
+ /* Update action 'checked' state: */
+ m_actions.value(strKey)->setChecked(!(m_restrictionsOfMenuApplication & enumValue));
+ }
+}
+
+void UIMenuBarEditorWidget::setRestrictionsOfMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType restrictions)
+{
+ /* Cache passed restrictions: */
+ m_restrictionsOfMenuMachine = restrictions;
+ /* Get static meta-object: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* We have UIExtraDataMetaDefs::RuntimeMenuMachineActionType enum registered, so we can enumerate it: */
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuMachineActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuMachineActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuMachineActionType_Invalid & RuntimeMenuMachineActionType_All enum-value: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuMachineActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuMachineActionType_All)
+ continue;
+ /* Which key required action registered under? */
+ const QString strKey = gpConverter->toInternalString(enumValue);
+ if (!m_actions.contains(strKey))
+ continue;
+ /* Update action 'checked' state: */
+ m_actions.value(strKey)->setChecked(!(m_restrictionsOfMenuMachine & enumValue));
+ }
+}
+
+void UIMenuBarEditorWidget::setRestrictionsOfMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType restrictions)
+{
+ /* Cache passed restrictions: */
+ m_restrictionsOfMenuView = restrictions;
+ /* Get static meta-object: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* We have UIExtraDataMetaDefs::RuntimeMenuViewActionType enum registered, so we can enumerate it: */
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuViewActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuViewActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuViewActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuViewActionType_Invalid & RuntimeMenuViewActionType_All enum-value: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuViewActionType_All)
+ continue;
+ /* Which key required action registered under? */
+ const QString strKey = gpConverter->toInternalString(enumValue);
+ if (!m_actions.contains(strKey))
+ continue;
+ /* Update action 'checked' state: */
+ m_actions.value(strKey)->setChecked(!(m_restrictionsOfMenuView & enumValue));
+ }
+}
+
+void UIMenuBarEditorWidget::setRestrictionsOfMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType restrictions)
+{
+ /* Cache passed restrictions: */
+ m_restrictionsOfMenuInput = restrictions;
+ /* Get static meta-object: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* We have UIExtraDataMetaDefs::RuntimeMenuInputActionType enum registered, so we can enumerate it: */
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuInputActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuInputActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuInputActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuInputActionType_Invalid & RuntimeMenuInputActionType_All enum-value: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuInputActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuInputActionType_All)
+ continue;
+ /* Which key required action registered under? */
+ const QString strKey = gpConverter->toInternalString(enumValue);
+ if (!m_actions.contains(strKey))
+ continue;
+ /* Update action 'checked' state: */
+ m_actions.value(strKey)->setChecked(!(m_restrictionsOfMenuInput & enumValue));
+ }
+}
+
+void UIMenuBarEditorWidget::setRestrictionsOfMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType restrictions)
+{
+ /* Cache passed restrictions: */
+ m_restrictionsOfMenuDevices = restrictions;
+ /* Get static meta-object: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* We have UIExtraDataMetaDefs::RuntimeMenuDevicesActionType enum registered, so we can enumerate it: */
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuDevicesActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuDevicesActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuDevicesActionType_Invalid & RuntimeMenuDevicesActionType_All enum-value: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuDevicesActionType_All)
+ continue;
+ /* Which key required action registered under? */
+ const QString strKey = gpConverter->toInternalString(enumValue);
+ if (!m_actions.contains(strKey))
+ continue;
+ /* Update action 'checked' state: */
+ m_actions.value(strKey)->setChecked(!(m_restrictionsOfMenuDevices & enumValue));
+ }
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+void UIMenuBarEditorWidget::setRestrictionsOfMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType restrictions)
+{
+ /* Cache passed restrictions: */
+ m_restrictionsOfMenuDebug = restrictions;
+ /* Get static meta-object: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* We have UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType enum registered, so we can enumerate it: */
+ const int iEnumIndex = smo.indexOfEnumerator("RuntimeMenuDebuggerActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip RuntimeMenuDebuggerActionType_Invalid & RuntimeMenuDebuggerActionType_All enum-value: */
+ if (enumValue == UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType_All)
+ continue;
+ /* Which key required action registered under? */
+ const QString strKey = gpConverter->toInternalString(enumValue);
+ if (!m_actions.contains(strKey))
+ continue;
+ /* Update action 'checked' state: */
+ m_actions.value(strKey)->setChecked(!(m_restrictionsOfMenuDebug & enumValue));
+ }
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+void UIMenuBarEditorWidget::setRestrictionsOfMenuWindow(UIExtraDataMetaDefs::MenuWindowActionType restrictions)
+{
+ /* Cache passed restrictions: */
+ m_restrictionsOfMenuWindow = restrictions;
+ /* Get static meta-object: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* We have UIExtraDataMetaDefs::MenuWindowActionType enum registered, so we can enumerate it: */
+ const int iEnumIndex = smo.indexOfEnumerator("MenuWindowActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::MenuWindowActionType enumValue =
+ static_cast<const UIExtraDataMetaDefs::MenuWindowActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip MenuWindowActionType_Invalid & MenuWindowActionType_All enum-value: */
+ if (enumValue == UIExtraDataMetaDefs::MenuWindowActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::MenuWindowActionType_All)
+ continue;
+ /* Which key required action registered under? */
+ const QString strKey = gpConverter->toInternalString(enumValue);
+ if (!m_actions.contains(strKey))
+ continue;
+ /* Update action 'checked' state: */
+ m_actions.value(strKey)->setChecked(!(m_restrictionsOfMenuWindow & enumValue));
+ }
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMenuBarEditorWidget::setRestrictionsOfMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType restrictions)
+{
+ /* Cache passed restrictions: */
+ m_restrictionsOfMenuHelp = restrictions;
+ /* Get static meta-object: */
+ const QMetaObject &smo = UIExtraDataMetaDefs::staticMetaObject;
+
+ /* We have UIExtraDataMetaDefs::MenuHelpActionType enum registered, so we can enumerate it: */
+ const int iEnumIndex = smo.indexOfEnumerator("MenuHelpActionType");
+ QMetaEnum metaEnum = smo.enumerator(iEnumIndex);
+ /* Handle other enum-values: */
+ for (int iKeyIndex = 0; iKeyIndex < metaEnum.keyCount(); ++iKeyIndex)
+ {
+ /* Get iterated enum-value: */
+ const UIExtraDataMetaDefs::MenuHelpActionType enumValue =
+ static_cast<UIExtraDataMetaDefs::MenuHelpActionType>(metaEnum.keyToValue(metaEnum.key(iKeyIndex)));
+ /* Skip MenuHelpActionType_Invalid & MenuHelpActionType_All enum-value: */
+ if (enumValue == UIExtraDataMetaDefs::MenuHelpActionType_Invalid ||
+ enumValue == UIExtraDataMetaDefs::MenuHelpActionType_All)
+ continue;
+ /* Which key required action registered under? */
+ const QString strKey = gpConverter->toInternalString(enumValue);
+ if (!m_actions.contains(strKey))
+ continue;
+ /* Update action 'checked' state: */
+ m_actions.value(strKey)->setChecked(!(m_restrictionsOfMenuHelp & enumValue));
+ }
+}
+
+void UIMenuBarEditorWidget::retranslateUi()
+{
+ /* Translate widget itself: */
+ setToolTip(tr("Allows to modify VM menu-bar contents."));
+
+ /* Translate close-button if necessary: */
+ if (!m_fStartedFromVMSettings && m_pButtonClose)
+ m_pButtonClose->setToolTip(tr("Close"));
+#ifndef VBOX_WS_MAC
+ /* Translate enable-checkbox if necessary: */
+ if (m_fStartedFromVMSettings && m_pCheckBoxEnable)
+ m_pCheckBoxEnable->setToolTip(tr("Enable Menu Bar"));
+#endif
+}
+
+void UIMenuBarEditorWidget::paintEvent(QPaintEvent *)
+{
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Prepare palette colors: */
+ const QPalette pal = QApplication::palette();
+ QColor color0 = pal.color(QPalette::Window);
+ QColor color1 = pal.color(QPalette::Window).lighter(110);
+ color1.setAlpha(0);
+ QColor color2 = pal.color(QPalette::Window).darker(200);
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ QColor color3 = pal.color(QPalette::Window).darker(120);
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+ /* Acquire metric: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Left corner: */
+ QRadialGradient grad1(QPointF(iMetric, height() - iMetric), iMetric);
+ {
+ grad1.setColorAt(0, color2);
+ grad1.setColorAt(1, color1);
+ }
+ /* Right corner: */
+ QRadialGradient grad2(QPointF(width() - iMetric, height() - iMetric), iMetric);
+ {
+ grad2.setColorAt(0, color2);
+ grad2.setColorAt(1, color1);
+ }
+ /* Bottom line: */
+ QLinearGradient grad3(QPointF(iMetric, height()), QPointF(iMetric, height() - iMetric));
+ {
+ grad3.setColorAt(0, color1);
+ grad3.setColorAt(1, color2);
+ }
+ /* Left line: */
+ QLinearGradient grad4(QPointF(0, height() - iMetric), QPointF(iMetric, height() - iMetric));
+ {
+ grad4.setColorAt(0, color1);
+ grad4.setColorAt(1, color2);
+ }
+ /* Right line: */
+ QLinearGradient grad5(QPointF(width(), height() - iMetric), QPointF(width() - iMetric, height() - iMetric));
+ {
+ grad5.setColorAt(0, color1);
+ grad5.setColorAt(1, color2);
+ }
+
+ /* Paint shape/shadow: */
+ painter.fillRect(QRect(iMetric, 0, width() - iMetric * 2, height() - iMetric), color0); // background
+ painter.fillRect(QRect(0, height() - iMetric, iMetric, iMetric), grad1); // left corner
+ painter.fillRect(QRect(width() - iMetric, height() - iMetric, iMetric, iMetric), grad2); // right corner
+ painter.fillRect(QRect(iMetric, height() - iMetric, width() - iMetric * 2, iMetric), grad3); // bottom line
+ painter.fillRect(QRect(0, 0, iMetric, height() - iMetric), grad4); // left line
+ painter.fillRect(QRect(width() - iMetric, 0, iMetric, height() - iMetric), grad5); // right line
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /* Paint frames: */
+ painter.save();
+ painter.setPen(color3);
+ painter.drawLine(QLine(QPoint(iMetric + 1, 0),
+ QPoint(iMetric + 1, height() - 1 - iMetric - 1)));
+ painter.drawLine(QLine(QPoint(iMetric + 1, height() - 1 - iMetric - 1),
+ QPoint(width() - 1 - iMetric - 1, height() - 1 - iMetric - 1)));
+ painter.drawLine(QLine(QPoint(width() - 1 - iMetric - 1, height() - 1 - iMetric - 1),
+ QPoint(width() - 1 - iMetric - 1, 0)));
+ if (m_fStartedFromVMSettings)
+ painter.drawLine(QLine(QPoint(width() - 1 - iMetric - 1, 0), QPoint(iMetric + 1, 0)));
+ painter.restore();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+}
+
+void UIMenuBarEditorWidget::sltHandleConfigurationChange(const QUuid &uMachineID)
+{
+ /* Skip unrelated machine IDs: */
+ if (machineID() != uMachineID)
+ return;
+
+ /* Recache menu-bar configuration: */
+ setRestrictionsOfMenuBar(gEDataManager->restrictedRuntimeMenuTypes(machineID()));
+ setRestrictionsOfMenuApplication(gEDataManager->restrictedRuntimeMenuApplicationActionTypes(machineID()));
+ setRestrictionsOfMenuMachine(gEDataManager->restrictedRuntimeMenuMachineActionTypes(machineID()));
+ setRestrictionsOfMenuView(gEDataManager->restrictedRuntimeMenuViewActionTypes(machineID()));
+ setRestrictionsOfMenuInput(gEDataManager->restrictedRuntimeMenuInputActionTypes(machineID()));
+ setRestrictionsOfMenuDevices(gEDataManager->restrictedRuntimeMenuDevicesActionTypes(machineID()));
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ setRestrictionsOfMenuDebug(gEDataManager->restrictedRuntimeMenuDebuggerActionTypes(machineID()));
+#endif
+#ifdef VBOX_WS_MAC
+ setRestrictionsOfMenuWindow(gEDataManager->restrictedRuntimeMenuWindowActionTypes(machineID()));
+#endif
+ setRestrictionsOfMenuHelp(gEDataManager->restrictedRuntimeMenuHelpActionTypes(machineID()));
+}
+
+void UIMenuBarEditorWidget::sltHandleMenuBarMenuClick()
+{
+ /* Make sure sender is valid: */
+ QAction *pAction = qobject_cast<QAction*>(sender());
+ AssertPtrReturnVoid(pAction);
+
+ /* Depending on triggered action class: */
+ switch (pAction->property("class").toInt())
+ {
+ case UIExtraDataMetaDefs::MenuType_All:
+ {
+ /* Get sender type: */
+ const UIExtraDataMetaDefs::MenuType enmType =
+ static_cast<UIExtraDataMetaDefs::MenuType>(pAction->property("type").toInt());
+ /* Invert restriction for sender type: */
+ m_restrictionsOfMenuBar = (UIExtraDataMetaDefs::MenuType)(m_restrictionsOfMenuBar ^ enmType);
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply menu-bar restrictions from cache: */
+ setRestrictionsOfMenuBar(m_restrictionsOfMenuBar);
+ }
+ else
+ {
+ /* Save updated menu-bar restrictions: */
+ gEDataManager->setRestrictedRuntimeMenuTypes(m_restrictionsOfMenuBar, machineID());
+ }
+ break;
+ }
+ case UIExtraDataMetaDefs::MenuType_Application:
+ {
+ /* Get sender type: */
+ const UIExtraDataMetaDefs::MenuApplicationActionType enmType =
+ static_cast<UIExtraDataMetaDefs::MenuApplicationActionType>(pAction->property("type").toInt());
+ /* Invert restriction for sender type: */
+ m_restrictionsOfMenuApplication = (UIExtraDataMetaDefs::MenuApplicationActionType)(m_restrictionsOfMenuApplication ^ enmType);
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply menu-bar restrictions from cache: */
+ setRestrictionsOfMenuApplication(m_restrictionsOfMenuApplication);
+ }
+ else
+ {
+ /* Save updated menu-bar restrictions: */
+ gEDataManager->setRestrictedRuntimeMenuApplicationActionTypes(m_restrictionsOfMenuApplication, machineID());
+ }
+ break;
+ }
+ case UIExtraDataMetaDefs::MenuType_Machine:
+ {
+ /* Get sender type: */
+ const UIExtraDataMetaDefs::RuntimeMenuMachineActionType enmType =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuMachineActionType>(pAction->property("type").toInt());
+ /* Invert restriction for sender type: */
+ m_restrictionsOfMenuMachine = (UIExtraDataMetaDefs::RuntimeMenuMachineActionType)(m_restrictionsOfMenuMachine ^ enmType);
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply menu-bar restrictions from cache: */
+ setRestrictionsOfMenuMachine(m_restrictionsOfMenuMachine);
+ }
+ else
+ {
+ /* Save updated menu-bar restrictions: */
+ gEDataManager->setRestrictedRuntimeMenuMachineActionTypes(m_restrictionsOfMenuMachine, machineID());
+ }
+ break;
+ }
+ case UIExtraDataMetaDefs::MenuType_View:
+ {
+ /* Get sender type: */
+ const UIExtraDataMetaDefs::RuntimeMenuViewActionType enmType =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuViewActionType>(pAction->property("type").toInt());
+ /* Invert restriction for sender type: */
+ m_restrictionsOfMenuView = (UIExtraDataMetaDefs::RuntimeMenuViewActionType)(m_restrictionsOfMenuView ^ enmType);
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply menu-bar restrictions from cache: */
+ setRestrictionsOfMenuView(m_restrictionsOfMenuView);
+ }
+ else
+ {
+ /* Save updated menu-bar restrictions: */
+ gEDataManager->setRestrictedRuntimeMenuViewActionTypes(m_restrictionsOfMenuView, machineID());
+ }
+ break;
+ }
+ case UIExtraDataMetaDefs::MenuType_Input:
+ {
+ /* Get sender type: */
+ const UIExtraDataMetaDefs::RuntimeMenuInputActionType enmType =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuInputActionType>(pAction->property("type").toInt());
+ /* Invert restriction for sender type: */
+ m_restrictionsOfMenuInput = (UIExtraDataMetaDefs::RuntimeMenuInputActionType)(m_restrictionsOfMenuInput ^ enmType);
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply menu-bar restrictions from cache: */
+ setRestrictionsOfMenuInput(m_restrictionsOfMenuInput);
+ }
+ else
+ {
+ /* Save updated menu-bar restrictions: */
+ gEDataManager->setRestrictedRuntimeMenuInputActionTypes(m_restrictionsOfMenuInput, machineID());
+ }
+ break;
+ }
+ case UIExtraDataMetaDefs::MenuType_Devices:
+ {
+ /* Get sender type: */
+ const UIExtraDataMetaDefs::RuntimeMenuDevicesActionType enmType =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuDevicesActionType>(pAction->property("type").toInt());
+ /* Invert restriction for sender type: */
+ m_restrictionsOfMenuDevices = (UIExtraDataMetaDefs::RuntimeMenuDevicesActionType)(m_restrictionsOfMenuDevices ^ enmType);
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply menu-bar restrictions from cache: */
+ setRestrictionsOfMenuDevices(m_restrictionsOfMenuDevices);
+ }
+ else
+ {
+ /* Save updated menu-bar restrictions: */
+ gEDataManager->setRestrictedRuntimeMenuDevicesActionTypes(m_restrictionsOfMenuDevices, machineID());
+ }
+ break;
+ }
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ case UIExtraDataMetaDefs::MenuType_Debug:
+ {
+ /* Get sender type: */
+ const UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType enmType =
+ static_cast<UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType>(pAction->property("type").toInt());
+ /* Invert restriction for sender type: */
+ m_restrictionsOfMenuDebug = (UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType)(m_restrictionsOfMenuDebug ^ enmType);
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply menu-bar restrictions from cache: */
+ setRestrictionsOfMenuDebug(m_restrictionsOfMenuDebug);
+ }
+ else
+ {
+ /* Save updated menu-bar restrictions: */
+ gEDataManager->setRestrictedRuntimeMenuDebuggerActionTypes(m_restrictionsOfMenuDebug, machineID());
+ }
+ break;
+ }
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+#ifdef VBOX_WS_MAC
+ case UIExtraDataMetaDefs::MenuType_Window:
+ {
+ /* Get sender type: */
+ const UIExtraDataMetaDefs::MenuWindowActionType enmType =
+ static_cast<UIExtraDataMetaDefs::MenuWindowActionType>(pAction->property("type").toInt());
+ /* Invert restriction for sender type: */
+ m_restrictionsOfMenuWindow = (UIExtraDataMetaDefs::MenuWindowActionType)(m_restrictionsOfMenuWindow ^ enmType);
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply menu-bar restrictions from cache: */
+ setRestrictionsOfMenuWindow(m_restrictionsOfMenuWindow);
+ }
+ else
+ {
+ /* Save updated menu-bar restrictions: */
+ gEDataManager->setRestrictedRuntimeMenuWindowActionTypes(m_restrictionsOfMenuWindow, machineID());
+ }
+ break;
+ }
+#endif /* VBOX_WS_MAC */
+ case UIExtraDataMetaDefs::MenuType_Help:
+ {
+ /* Get sender type: */
+ const UIExtraDataMetaDefs::MenuHelpActionType enmType =
+ static_cast<UIExtraDataMetaDefs::MenuHelpActionType>(pAction->property("type").toInt());
+ /* Invert restriction for sender type: */
+ m_restrictionsOfMenuHelp = (UIExtraDataMetaDefs::MenuHelpActionType)(m_restrictionsOfMenuHelp ^ enmType);
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply menu-bar restrictions from cache: */
+ setRestrictionsOfMenuHelp(m_restrictionsOfMenuHelp);
+ }
+ else
+ {
+ /* Save updated menu-bar restrictions: */
+ gEDataManager->setRestrictedRuntimeMenuHelpActionTypes(m_restrictionsOfMenuHelp, machineID());
+ } break;
+ }
+ default: break;
+ }
+}
+
+void UIMenuBarEditorWidget::prepare()
+{
+ /* Do nothing if already prepared: */
+ if (m_fPrepared)
+ return;
+
+ /* Do not prepare if machine ID or action-pool is not set: */
+ if (m_uMachineID.isNull() || !m_pActionPool)
+ return;
+
+ /* Install tool-bar button accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUIMenuBarEditorButton::pFactory);
+
+ /* Create main-layout: */
+ m_pMainLayout = new QHBoxLayout(this);
+ AssertPtrReturnVoid(m_pMainLayout);
+ {
+ /* Configure main-layout: */
+ int iLeft, iTop, iRight, iBottom;
+ m_pMainLayout->getContentsMargins(&iLeft, &iTop, &iRight, &iBottom);
+ /* Acquire metric: */
+ const int iStandardMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+ const int iMinimumMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+ /* Standard margins should not be too small/large: */
+ iLeft = iStandardMetric;
+ iTop = iStandardMetric;
+ iRight = iStandardMetric;
+ iBottom = iStandardMetric;
+ /* Top margin should be smaller for the common case: */
+ if (iTop >= iMinimumMetric)
+ iTop -= iMinimumMetric;
+#ifndef VBOX_WS_MAC
+ /* Right margin should be bigger for the settings case: */
+ if (m_fStartedFromVMSettings)
+ iRight += iMinimumMetric;
+#endif /* !VBOX_WS_MAC */
+ /* Apply margins/spacing finally: */
+ m_pMainLayout->setContentsMargins(iLeft, iTop, iRight, iBottom);
+ m_pMainLayout->setSpacing(0);
+ /* Create tool-bar: */
+ m_pToolBar = new QIToolBar;
+ AssertPtrReturnVoid(m_pToolBar);
+ {
+ /* Prepare menus: */
+ prepareMenus();
+ /* Add tool-bar into main-layout: */
+ m_pMainLayout->addWidget(m_pToolBar);
+ }
+ /* Insert stretch: */
+ m_pMainLayout->addStretch();
+ /* Create close-button if necessary: */
+ if (!m_fStartedFromVMSettings)
+ {
+ m_pButtonClose = new QIToolButton;
+ AssertPtrReturnVoid(m_pButtonClose);
+ {
+ /* Configure close-button: */
+ m_pButtonClose->setFocusPolicy(Qt::StrongFocus);
+ m_pButtonClose->setShortcut(Qt::Key_Escape);
+ m_pButtonClose->setIcon(UIIconPool::iconSet(":/ok_16px.png"));
+ connect(m_pButtonClose, SIGNAL(clicked(bool)), this, SIGNAL(sigCancelClicked()));
+ /* Add close-button into main-layout: */
+ m_pMainLayout->addWidget(m_pButtonClose);
+ }
+ }
+#ifndef VBOX_WS_MAC
+ /* Create enable-checkbox if necessary: */
+ else
+ {
+ m_pCheckBoxEnable = new QCheckBox;
+ AssertPtrReturnVoid(m_pCheckBoxEnable);
+ {
+ /* Configure enable-checkbox: */
+ m_pCheckBoxEnable->setFocusPolicy(Qt::StrongFocus);
+ /* Add enable-checkbox into main-layout: */
+ m_pMainLayout->addWidget(m_pCheckBoxEnable);
+ }
+ }
+#endif /* !VBOX_WS_MAC */
+ }
+
+ /* Mark as prepared: */
+ m_fPrepared = true;
+
+ /* Translate contents: */
+ retranslateUi();
+}
+
+void UIMenuBarEditorWidget::prepareMenus()
+{
+ /* Create menus: */
+ prepareMenuApplication();
+ prepareMenuMachine();
+ prepareMenuView();
+ prepareMenuInput();
+ prepareMenuDevices();
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ prepareMenuDebug();
+#endif
+#ifdef VBOX_WS_MAC
+ prepareMenuWindow();
+#endif
+ prepareMenuHelp();
+
+ if (!m_fStartedFromVMSettings)
+ {
+ /* Cache menu-bar configuration: */
+ setRestrictionsOfMenuBar(gEDataManager->restrictedRuntimeMenuTypes(machineID()));
+ setRestrictionsOfMenuApplication(gEDataManager->restrictedRuntimeMenuApplicationActionTypes(machineID()));
+ setRestrictionsOfMenuMachine(gEDataManager->restrictedRuntimeMenuMachineActionTypes(machineID()));
+ setRestrictionsOfMenuView(gEDataManager->restrictedRuntimeMenuViewActionTypes(machineID()));
+ setRestrictionsOfMenuInput(gEDataManager->restrictedRuntimeMenuInputActionTypes(machineID()));
+ setRestrictionsOfMenuDevices(gEDataManager->restrictedRuntimeMenuDevicesActionTypes(machineID()));
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ setRestrictionsOfMenuDebug(gEDataManager->restrictedRuntimeMenuDebuggerActionTypes(machineID()));
+#endif
+#ifdef VBOX_WS_MAC
+ setRestrictionsOfMenuWindow(gEDataManager->restrictedRuntimeMenuWindowActionTypes(machineID()));
+#endif
+ setRestrictionsOfMenuHelp(gEDataManager->restrictedRuntimeMenuHelpActionTypes(machineID()));
+ /* And listen for the menu-bar configuration changes after that: */
+ connect(gEDataManager, &UIExtraDataManager::sigMenuBarConfigurationChange,
+ this, &UIMenuBarEditorWidget::sltHandleConfigurationChange);
+ }
+}
+
+#ifdef VBOX_WS_MAC
+QMenu *UIMenuBarEditorWidget::prepareNamedMenu(const QString &strName)
+{
+ /* Create named menu: */
+ QMenu *pNamedMenu = new QMenu(strName, m_pToolBar);
+ AssertPtrReturn(pNamedMenu, 0);
+ {
+ /* Configure named menu: */
+ pNamedMenu->setProperty("class", UIExtraDataMetaDefs::MenuType_Application);
+ /* Get named menu action: */
+ QAction *pNamedMenuAction = pNamedMenu->menuAction();
+ AssertPtrReturn(pNamedMenuAction, 0);
+ {
+ /* Add menu action into tool-bar: */
+ m_pToolBar->addAction(pNamedMenuAction);
+ /* Get named menu tool-button: */
+ QToolButton *pNamedMenuToolButton = qobject_cast<QToolButton*>(m_pToolBar->widgetForAction(pNamedMenuAction));
+ AssertPtrReturn(pNamedMenuToolButton, 0);
+ {
+ /* Configure named menu tool-button: */
+ pNamedMenuToolButton->setProperty("Belongs to", "UIMenuBarEditorWidget");
+ pNamedMenuToolButton->setPopupMode(QToolButton::MenuButtonPopup);
+ pNamedMenuToolButton->setAutoRaise(true);
+ /* Update the accessibility interface to take "Belongs to" into account: */
+ QAccessibleInterface *pInterface = QAccessible::queryAccessibleInterface(pNamedMenuToolButton);
+ if (pInterface)
+ {
+ QAccessible::deleteAccessibleInterface(QAccessible::uniqueId(pInterface));
+ QAccessible::queryAccessibleInterface(pNamedMenuToolButton); // <= new one, proper..
+ }
+ /* Create spacing after named menu tool-button: */
+ QWidget *pSpacing = new QWidget;
+ AssertPtrReturn(pSpacing, 0);
+ {
+ /* Configure spacing: */
+ pSpacing->setFixedSize(5, 1);
+ /* Add spacing into tool-bar: */
+ m_pToolBar->addWidget(pSpacing);
+ }
+ }
+ }
+ }
+ /* Return named menu: */
+ return pNamedMenu;
+}
+#endif /* VBOX_WS_MAC */
+
+QMenu *UIMenuBarEditorWidget::prepareCopiedMenu(const UIAction *pAction)
+{
+ /* Create copied menu: */
+ QMenu *pCopiedMenu = new QMenu(pAction->name(), m_pToolBar);
+ AssertPtrReturn(pCopiedMenu, 0);
+ {
+ /* Configure copied menu: */
+ pCopiedMenu->setProperty("class", pAction->extraDataID());
+ /* Get copied menu action: */
+ QAction *pCopiedMenuAction = pCopiedMenu->menuAction();
+ AssertPtrReturn(pCopiedMenuAction, 0);
+ {
+ /* Configure copied menu action: */
+ pCopiedMenuAction->setCheckable(true);
+ pCopiedMenuAction->setProperty("class", UIExtraDataMetaDefs::MenuType_All);
+ pCopiedMenuAction->setProperty("type", pAction->extraDataID());
+ connect(pCopiedMenuAction, &QAction::triggered, this, &UIMenuBarEditorWidget::sltHandleMenuBarMenuClick);
+ m_actions.insert(pAction->extraDataKey(), pCopiedMenuAction);
+ /* Add menu action into tool-bar: */
+ m_pToolBar->addAction(pCopiedMenuAction);
+ /* Get copied menu tool-button: */
+ QToolButton *pCopiedMenuToolButton = qobject_cast<QToolButton*>(m_pToolBar->widgetForAction(pCopiedMenuAction));
+ AssertPtrReturn(pCopiedMenuToolButton, 0);
+ {
+ /* Configure copied menu tool-button: */
+ pCopiedMenuToolButton->setProperty("Belongs to", "UIMenuBarEditorWidget");
+ pCopiedMenuToolButton->setPopupMode(QToolButton::MenuButtonPopup);
+ pCopiedMenuToolButton->setAutoRaise(true);
+ /* Update the accessibility interface to take "Belongs to" into account: */
+ QAccessibleInterface *pInterface = QAccessible::queryAccessibleInterface(pCopiedMenuToolButton);
+ if (pInterface)
+ {
+ QAccessible::deleteAccessibleInterface(QAccessible::uniqueId(pInterface));
+ QAccessible::queryAccessibleInterface(pCopiedMenuToolButton); // <= new one, proper..
+ }
+ /* Create spacing after copied menu tool-button: */
+ QWidget *pSpacing = new QWidget;
+ AssertPtrReturn(pSpacing, 0);
+ {
+ /* Configure spacing: */
+ pSpacing->setFixedSize(5, 1);
+ /* Add spacing into tool-bar: */
+ m_pToolBar->addWidget(pSpacing);
+ }
+ }
+ }
+ }
+ /* Return copied menu: */
+ return pCopiedMenu;
+}
+
+#if 0
+QMenu *UIMenuBarEditorWidget::prepareCopiedSubMenu(QMenu *pMenu, const UIAction *pAction)
+{
+ /* Create copied sub-menu: */
+ QMenu *pCopiedMenu = pMenu->addMenu(pAction->name());
+ AssertPtrReturn(pCopiedMenu, 0);
+ {
+ /* Configure copied sub-menu: */
+ pCopiedMenu->setProperty("class", pMenu->property("class"));
+ /* Get copied sub-menu action: */
+ QAction *pCopiedMenuAction = pCopiedMenu->menuAction();
+ AssertPtrReturn(pCopiedMenuAction, 0);
+ {
+ /* Configure copied sub-menu: */
+ pCopiedMenuAction->setCheckable(true);
+ pCopiedMenuAction->setProperty("class", pCopiedMenu->property("class"));
+ pCopiedMenuAction->setProperty("type", pAction->extraDataID());
+ connect(pCopiedMenuAction, &QAction::triggered, &UIMenuBarEditorWidget::sltHandleMenuBarMenuClick);
+ m_actions.insert(pAction->extraDataKey(), pCopiedMenuAction);
+ }
+ }
+ /* Return copied sub-menu: */
+ return pCopiedMenu;
+}
+#endif
+
+QAction *UIMenuBarEditorWidget::prepareNamedAction(QMenu *pMenu, const QString &strName,
+ int iExtraDataID, const QString &strExtraDataID)
+{
+ /* Create copied action: */
+ QAction *pCopiedAction = pMenu->addAction(strName);
+ AssertPtrReturn(pCopiedAction, 0);
+ {
+ /* Configure copied action: */
+ pCopiedAction->setCheckable(true);
+ pCopiedAction->setProperty("class", pMenu->property("class"));
+ pCopiedAction->setProperty("type", iExtraDataID);
+ connect(pCopiedAction, &QAction::triggered, this, &UIMenuBarEditorWidget::sltHandleMenuBarMenuClick);
+ m_actions.insert(strExtraDataID, pCopiedAction);
+ }
+ /* Return copied action: */
+ return pCopiedAction;
+}
+
+QAction *UIMenuBarEditorWidget::prepareCopiedAction(QMenu *pMenu, const UIAction *pAction)
+{
+ /* Create copied action: */
+ QAction *pCopiedAction = pMenu->addAction(pAction->name());
+ AssertPtrReturn(pCopiedAction, 0);
+ {
+ /* Configure copied action: */
+ pCopiedAction->setCheckable(true);
+ pCopiedAction->setProperty("class", pMenu->property("class"));
+ pCopiedAction->setProperty("type", pAction->extraDataID());
+ connect(pCopiedAction, &QAction::triggered, this, &UIMenuBarEditorWidget::sltHandleMenuBarMenuClick);
+ m_actions.insert(pAction->extraDataKey(), pCopiedAction);
+ }
+ /* Return copied action: */
+ return pCopiedAction;
+}
+
+void UIMenuBarEditorWidget::prepareMenuApplication()
+{
+ /* Copy menu: */
+#ifdef VBOX_WS_MAC
+ QMenu *pMenu = prepareNamedMenu("Application");
+#else /* !VBOX_WS_MAC */
+ QMenu *pMenu = prepareCopiedMenu(actionPool()->action(UIActionIndex_M_Application));
+#endif /* !VBOX_WS_MAC */
+ AssertPtrReturnVoid(pMenu);
+ {
+#ifdef VBOX_WS_MAC
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_M_Application_S_About));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_M_Application_S_ResetWarnings));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_M_Application_S_Preferences));
+#else /* !VBOX_WS_MAC */
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_M_Application_S_Preferences));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_M_Application_S_ResetWarnings));
+#endif /* !VBOX_WS_MAC */
+ }
+}
+
+void UIMenuBarEditorWidget::prepareMenuMachine()
+{
+ /* Copy menu: */
+ QMenu *pMenu = prepareCopiedMenu(actionPool()->action(UIActionIndexRT_M_Machine));
+ AssertPtrReturnVoid(pMenu);
+ {
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_Settings));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_TakeSnapshot));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_ShowInformation));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_ShowFileManager));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_T_Pause));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_Reset));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_Detach));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_SaveState));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_Shutdown));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_PowerOff));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Machine_S_ShowLogDialog));
+ }
+}
+
+void UIMenuBarEditorWidget::prepareMenuView()
+{
+ /* Copy menu: */
+ QMenu *pMenu = prepareCopiedMenu(actionPool()->action(UIActionIndexRT_M_View));
+ AssertPtrReturnVoid(pMenu);
+ {
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_T_Seamless));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_T_Scale));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_S_AdjustWindow));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_T_GuestAutoresize));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_S_TakeScreenshot));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_M_Recording_T_Start));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_T_VRDEServer));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_M_MenuBar));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_View_M_StatusBar));
+ pMenu->addSeparator();
+ prepareNamedAction(pMenu, tr("Virtual Screen Resize"),
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize,
+ gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize));
+ prepareNamedAction(pMenu, tr("Virtual Screen Remap"),
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Remap,
+ gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Remap));
+ prepareNamedAction(pMenu, tr("Virtual Screen Rescale"),
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType_Rescale,
+ gpConverter->toInternalString(UIExtraDataMetaDefs::RuntimeMenuViewActionType_Rescale));
+ }
+}
+
+void UIMenuBarEditorWidget::prepareMenuInput()
+{
+ /* Copy menu: */
+ QMenu *pMenu = prepareCopiedMenu(actionPool()->action(UIActionIndexRT_M_Input));
+ AssertPtrReturnVoid(pMenu);
+ {
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Input_M_Keyboard));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Input_M_Mouse_T_Integration));
+ }
+}
+
+void UIMenuBarEditorWidget::prepareMenuDevices()
+{
+ /* Copy menu: */
+ QMenu *pMenu = prepareCopiedMenu(actionPool()->action(UIActionIndexRT_M_Devices));
+ AssertPtrReturnVoid(pMenu);
+ {
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_HardDrives));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_OpticalDevices));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_FloppyDevices));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_Audio));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_Network));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_USBDevices));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_WebCams));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_SharedFolders));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_SharedClipboard));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_M_DragAndDrop));
+ pMenu->addSeparator();
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_S_InsertGuestAdditionsDisk));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Devices_S_UpgradeGuestAdditions));
+ }
+}
+
+#ifdef VBOX_WITH_DEBUGGER_GUI
+void UIMenuBarEditorWidget::prepareMenuDebug()
+{
+ /* Copy menu: */
+ QMenu *pMenu = prepareCopiedMenu(actionPool()->action(UIActionIndexRT_M_Debug));
+ AssertPtrReturnVoid(pMenu);
+ {
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Debug_S_ShowStatistics));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Debug_S_ShowCommandLine));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndexRT_M_Debug_T_Logging));
+ }
+}
+#endif /* VBOX_WITH_DEBUGGER_GUI */
+
+#ifdef VBOX_WS_MAC
+void UIMenuBarEditorWidget::prepareMenuWindow()
+{
+ /* Copy menu: */
+ QMenu *pMenu = prepareCopiedMenu(actionPool()->action(UIActionIndex_M_Window));
+ AssertPtrReturnVoid(pMenu);
+ {
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_M_Window_S_Minimize));
+ pMenu->addSeparator();
+ prepareNamedAction(pMenu, tr("Switch"),
+ UIExtraDataMetaDefs::MenuWindowActionType_Switch,
+ gpConverter->toInternalString(UIExtraDataMetaDefs::MenuWindowActionType_Switch));
+ }
+}
+#endif /* VBOX_WS_MAC */
+
+void UIMenuBarEditorWidget::prepareMenuHelp()
+{
+ /* Copy menu: */
+ QMenu *pMenu = prepareCopiedMenu(actionPool()->action(UIActionIndex_Menu_Help));
+ AssertPtrReturnVoid(pMenu);
+ {
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_Simple_Contents));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_Simple_WebSite));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_Simple_BugTracker));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_Simple_Forums));
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_Simple_Oracle));
+ pMenu->addSeparator();
+#ifndef VBOX_WS_MAC
+ prepareCopiedAction(pMenu, actionPool()->action(UIActionIndex_Simple_About));
+#endif
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBarEditorWindow.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBarEditorWindow.h
new file mode 100644
index 00000000..fb56ad6a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuBarEditorWindow.h
@@ -0,0 +1,275 @@
+/* $Id: UIMenuBarEditorWindow.h $ */
+/** @file
+ * VBox Qt GUI - UIMenuBarEditorWindow class declaration.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIMenuBarEditorWindow_h
+#define FEQT_INCLUDED_SRC_widgets_UIMenuBarEditorWindow_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QUuid>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+#include "UISlidingToolBar.h"
+
+/* Forward declarations: */
+class QAction;
+class QHBoxLayout;
+class QMenu;
+#ifndef VBOX_WS_MAC
+class QCheckBox;
+#endif
+class QString;
+class QWidget;
+class QIToolButton;
+class UIAction;
+class UIActionPool;
+class QIToolBar;
+class UIMachineWindow;
+
+
+/** UISlidingToolBar subclass
+ * providing user with possibility to edit menu-bar layout. */
+class SHARED_LIBRARY_STUFF UIMenuBarEditorWindow : public UISlidingToolBar
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs sliding toolbar passing @a pParent to the base-class.
+ * @param pActionPool Brings the action-pool reference for internal use. */
+ UIMenuBarEditorWindow(UIMachineWindow *pParent, UIActionPool *pActionPool);
+};
+
+
+/** QWidget subclass
+ * used as menu-bar editor widget. */
+class SHARED_LIBRARY_STUFF UIMenuBarEditorWidget : public QIWithRetranslateUI2<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about Cancel button click. */
+ void sigCancelClicked();
+
+public:
+
+ /** Constructs menu-bar editor widget passing @a pParent to the base-class.
+ * @param fStartedFromVMSettings Brings whether 'this' is a part of VM settings.
+ * @param uMachineID Brings the machine ID to be used by the editor.
+ * @param pActionPool Brings the action-pool to be used by the editor. */
+ UIMenuBarEditorWidget(QWidget *pParent,
+ bool fStartedFromVMSettings = true,
+ const QUuid &uMachineID = QUuid(),
+ UIActionPool *pActionPool = 0);
+
+ /** Returns the machine ID instance. */
+ const QUuid &machineID() const { return m_uMachineID; }
+ /** Defines the @a uMachineID instance. */
+ void setMachineID(const QUuid &uMachineID);
+
+ /** Returns the action-pool reference. */
+ const UIActionPool *actionPool() const { return m_pActionPool; }
+ /** Defines the @a pActionPool reference. */
+ void setActionPool(UIActionPool *pActionPool);
+
+#ifndef VBOX_WS_MAC
+ /** Returns whether the menu-bar enabled. */
+ bool isMenuBarEnabled() const;
+ /** Defines whether the menu-bar @a fEnabled. */
+ void setMenuBarEnabled(bool fEnabled);
+#endif
+
+ /** Returns the cached restrictions of menu-bar. */
+ UIExtraDataMetaDefs::MenuType restrictionsOfMenuBar() const { return m_restrictionsOfMenuBar; }
+ /** Returns the cached restrictions of menu 'Application'. */
+ UIExtraDataMetaDefs::MenuApplicationActionType restrictionsOfMenuApplication() const { return m_restrictionsOfMenuApplication; }
+ /** Returns the cached restrictions of menu 'Machine'. */
+ UIExtraDataMetaDefs::RuntimeMenuMachineActionType restrictionsOfMenuMachine() const { return m_restrictionsOfMenuMachine; }
+ /** Returns the cached restrictions of menu 'View'. */
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType restrictionsOfMenuView() const { return m_restrictionsOfMenuView; }
+ /** Returns the cached restrictions of menu 'Input'. */
+ UIExtraDataMetaDefs::RuntimeMenuInputActionType restrictionsOfMenuInput() const { return m_restrictionsOfMenuInput; }
+ /** Returns the cached restrictions of menu 'Devices'. */
+ UIExtraDataMetaDefs::RuntimeMenuDevicesActionType restrictionsOfMenuDevices() const { return m_restrictionsOfMenuDevices; }
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Returns the cached restrictions of menu 'Debug'. */
+ UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType restrictionsOfMenuDebug() const { return m_restrictionsOfMenuDebug; }
+#endif
+#ifdef VBOX_WS_MAC
+ /** Returns the cached restrictions of menu 'Window'. */
+ UIExtraDataMetaDefs::MenuWindowActionType restrictionsOfMenuWindow() const { return m_restrictionsOfMenuWindow; }
+#endif
+ /** Returns the cached restrictions of menu 'Help'. */
+ UIExtraDataMetaDefs::MenuHelpActionType restrictionsOfMenuHelp() const { return m_restrictionsOfMenuHelp; }
+
+ /** Defines the cached @a restrictions of menu-bar. */
+ void setRestrictionsOfMenuBar(UIExtraDataMetaDefs::MenuType restrictions);
+ /** Defines the cached @a restrictions of menu 'Application'. */
+ void setRestrictionsOfMenuApplication(UIExtraDataMetaDefs::MenuApplicationActionType restrictions);
+ /** Defines the cached @a restrictions of menu 'Machine'. */
+ void setRestrictionsOfMenuMachine(UIExtraDataMetaDefs::RuntimeMenuMachineActionType restrictions);
+ /** Defines the cached @a restrictions of menu 'View'. */
+ void setRestrictionsOfMenuView(UIExtraDataMetaDefs::RuntimeMenuViewActionType restrictions);
+ /** Defines the cached @a restrictions of menu 'Input'. */
+ void setRestrictionsOfMenuInput(UIExtraDataMetaDefs::RuntimeMenuInputActionType restrictions);
+ /** Defines the cached @a restrictions of menu 'Devices'. */
+ void setRestrictionsOfMenuDevices(UIExtraDataMetaDefs::RuntimeMenuDevicesActionType restrictions);
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Defines the cached @a restrictions of menu 'Debug'. */
+ void setRestrictionsOfMenuDebug(UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType restrictions);
+#endif
+#ifdef VBOX_WS_MAC
+ /** Defines the cached @a restrictions of menu 'Window'. */
+ void setRestrictionsOfMenuWindow(UIExtraDataMetaDefs::MenuWindowActionType restrictions);
+#endif
+ /** Defines the cached @a restrictions of menu 'Help'. */
+ void setRestrictionsOfMenuHelp(UIExtraDataMetaDefs::MenuHelpActionType restrictions);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles configuration change. */
+ void sltHandleConfigurationChange(const QUuid &uMachineID);
+
+ /** Handles menu-bar menu click. */
+ void sltHandleMenuBarMenuClick();
+
+private:
+
+ /** Prepare routine. */
+ void prepare();
+
+#ifdef VBOX_WS_MAC
+ /** Prepare named menu routine. */
+ QMenu *prepareNamedMenu(const QString &strName);
+#endif
+ /** Prepare copied menu routine. */
+ QMenu *prepareCopiedMenu(const UIAction *pAction);
+#if 0
+ /** Prepare copied sub-menu routine. */
+ QMenu *prepareCopiedSubMenu(QMenu *pMenu, const UIAction *pAction);
+#endif
+ /** Prepare named action routine. */
+ QAction *prepareNamedAction(QMenu *pMenu, const QString &strName,
+ int iExtraDataID, const QString &strExtraDataID);
+ /** Prepare copied action routine. */
+ QAction *prepareCopiedAction(QMenu *pMenu, const UIAction *pAction);
+
+ /** Prepare menus routine. */
+ void prepareMenus();
+ /** Prepare 'Application' menu routine. */
+ void prepareMenuApplication();
+ /** Prepare 'Machine' menu routine. */
+ void prepareMenuMachine();
+ /** Prepare 'View' menu routine. */
+ void prepareMenuView();
+ /** Prepare 'Input' menu routine. */
+ void prepareMenuInput();
+ /** Prepare 'Devices' menu routine. */
+ void prepareMenuDevices();
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Prepare 'Debug' menu routine. */
+ void prepareMenuDebug();
+#endif
+#ifdef VBOX_WS_MAC
+ /** Prepare 'Window' menu routine. */
+ void prepareMenuWindow();
+#endif
+ /** Prepare 'Help' menu routine. */
+ void prepareMenuHelp();
+
+ /** @name General
+ * @{ */
+ /** Holds whether 'this' is prepared. */
+ bool m_fPrepared;
+ /** Holds whether 'this' is a part of VM settings. */
+ bool m_fStartedFromVMSettings;
+ /** Holds the machine ID instance. */
+ QUuid m_uMachineID;
+ /** Holds the action-pool reference. */
+ const UIActionPool *m_pActionPool;
+ /** @} */
+
+ /** @name Contents
+ * @{ */
+ /** Holds the main-layout instance. */
+ QHBoxLayout *m_pMainLayout;
+ /** Holds the tool-bar instance. */
+ QIToolBar *m_pToolBar;
+ /** Holds the close-button instance. */
+ QIToolButton *m_pButtonClose;
+#ifndef VBOX_WS_MAC
+ /** Holds the enable-checkbox instance. */
+ QCheckBox *m_pCheckBoxEnable;
+#endif
+ /** Holds tool-bar action references. */
+ QMap<QString, QAction*> m_actions;
+ /** @} */
+
+ /** @name Contents: Restrictions
+ * @{ */
+ /** Holds the cached restrictions of menu-bar. */
+ UIExtraDataMetaDefs::MenuType m_restrictionsOfMenuBar;
+ /** Holds the cached restrictions of menu 'Application'. */
+ UIExtraDataMetaDefs::MenuApplicationActionType m_restrictionsOfMenuApplication;
+ /** Holds the cached restrictions of menu 'Machine'. */
+ UIExtraDataMetaDefs::RuntimeMenuMachineActionType m_restrictionsOfMenuMachine;
+ /** Holds the cached restrictions of menu 'View'. */
+ UIExtraDataMetaDefs::RuntimeMenuViewActionType m_restrictionsOfMenuView;
+ /** Holds the cached restrictions of menu 'Input'. */
+ UIExtraDataMetaDefs::RuntimeMenuInputActionType m_restrictionsOfMenuInput;
+ /** Holds the cached restrictions of menu 'Devices'. */
+ UIExtraDataMetaDefs::RuntimeMenuDevicesActionType m_restrictionsOfMenuDevices;
+#ifdef VBOX_WITH_DEBUGGER_GUI
+ /** Holds the cached restrictions of menu 'Debug'. */
+ UIExtraDataMetaDefs::RuntimeMenuDebuggerActionType m_restrictionsOfMenuDebug;
+#endif
+#ifdef VBOX_WS_MAC
+ /** Holds the cached restrictions of menu 'Window'. */
+ UIExtraDataMetaDefs::MenuWindowActionType m_restrictionsOfMenuWindow;
+#endif
+ /** Holds the cached restrictions of menu 'Help'. */
+ UIExtraDataMetaDefs::MenuHelpActionType m_restrictionsOfMenuHelp;
+ /** @} */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIMenuBarEditorWindow_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuToolBar.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuToolBar.cpp
new file mode 100644
index 00000000..6b8713fb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuToolBar.cpp
@@ -0,0 +1,333 @@
+/* $Id: UIMenuToolBar.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMenuToolBar class implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QHBoxLayout>
+#include <QPainter>
+#include <QPainterPath>
+#include <QPainterPathStroker>
+#include <QStyle>
+#include <QToolButton>
+
+/* GUI includes: */
+#include "UIMenuToolBar.h"
+#include "QIToolBar.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/** QIToolBar extension
+ * holding single drop-down menu of actions. */
+class UIMenuToolBarPrivate : public QIToolBar
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs toolbar. */
+ UIMenuToolBarPrivate(QWidget *pParent = 0);
+
+ /** Rebuilds toolbar shape. */
+ void rebuildShape();
+
+ /** Defines toolbar alignment @a enmType. */
+ void setAlignmentType(UIMenuToolBar::AlignmentType enmType);
+
+ /** Defines toolbar menu action. */
+ void setMenuAction(QAction *pAction);
+
+protected:
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles polish @a pEvent. */
+ virtual void polishEvent(QShowEvent *pEvent);
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Holds whether this widget was polished. */
+ bool m_fPolished;
+
+ /** Holds the left margin instance. */
+ QWidget *m_pMarginLeft;
+ /** Holds the right margin instance. */
+ QWidget *m_pMarginRight;
+
+ /** Holds the menu toolbar alignment type. */
+ UIMenuToolBar::AlignmentType m_enmAlignmentType;
+
+ /** Holds the shape. */
+ QPainterPath m_shape;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMenuToolBarPrivate implementation. *
+*********************************************************************************************************************************/
+
+UIMenuToolBarPrivate::UIMenuToolBarPrivate(QWidget *pParent /* = 0 */)
+ : QIToolBar(pParent)
+ , m_fPolished(false)
+ , m_pMarginLeft(0)
+ , m_pMarginRight(0)
+ , m_enmAlignmentType(UIMenuToolBar::AlignmentType_TopLeft)
+{
+ /* Rebuild shape: */
+ rebuildShape();
+}
+
+void UIMenuToolBarPrivate::rebuildShape()
+{
+ /* Get the metric: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ const int iRounding = qMax(iIconMetric / 4, 4);
+
+ /* Configure margins: */
+ if (m_pMarginLeft && m_pMarginRight)
+ {
+ const int iStandardMargin = iRounding;
+ int iLeftMargin = iStandardMargin;
+ int iRightMargin = iStandardMargin;
+ if ( m_enmAlignmentType == UIMenuToolBar::AlignmentType_TopLeft
+ || m_enmAlignmentType == UIMenuToolBar::AlignmentType_BottomLeft)
+ iRightMargin += iRounding;
+ if ( m_enmAlignmentType == UIMenuToolBar::AlignmentType_TopRight
+ || m_enmAlignmentType == UIMenuToolBar::AlignmentType_BottomRight)
+ iLeftMargin += iRounding;
+ m_pMarginLeft->setMinimumWidth(iLeftMargin);
+ m_pMarginRight->setMinimumWidth(iRightMargin);
+ }
+
+ /* Rebuild shape: */
+ QPainterPath shape;
+ switch (m_enmAlignmentType)
+ {
+ case UIMenuToolBar::AlignmentType_TopLeft:
+ shape.moveTo(width(), height());
+ shape.lineTo(shape.currentPosition().x(), iRounding * 2);
+ shape.arcTo(QRectF(shape.currentPosition(), QSizeF(iRounding * 4, iRounding * 4))
+ .translated(-iRounding * 4, -iRounding * 2), 0, 90);
+ shape.lineTo(0, shape.currentPosition().y());
+ shape.lineTo(shape.currentPosition().x(), height());
+ shape.closeSubpath();
+ break;
+ case UIMenuToolBar::AlignmentType_TopRight:
+ shape.moveTo(0, height());
+ shape.lineTo(shape.currentPosition().x(), iRounding * 2);
+ shape.arcTo(QRectF(shape.currentPosition(), QSizeF(iRounding * 4, iRounding * 4))
+ .translated(0, -iRounding * 2), 180, -90);
+ shape.lineTo(width(), shape.currentPosition().y());
+ shape.lineTo(shape.currentPosition().x(), height());
+ shape.closeSubpath();
+ break;
+ case UIMenuToolBar::AlignmentType_BottomLeft:
+ shape.moveTo(width(), 0);
+ shape.lineTo(shape.currentPosition().x(), height() - iRounding * 2);
+ shape.arcTo(QRectF(shape.currentPosition(), QSizeF(iRounding * 4, iRounding * 4))
+ .translated(-iRounding * 4, -iRounding * 2), 0, -90);
+ shape.lineTo(0, shape.currentPosition().y());
+ shape.lineTo(shape.currentPosition().x(), 0);
+ shape.closeSubpath();
+ break;
+ case UIMenuToolBar::AlignmentType_BottomRight:
+ shape.moveTo(0, 0);
+ shape.lineTo(shape.currentPosition().x(), height() - iRounding * 2);
+ shape.arcTo(QRectF(shape.currentPosition(), QSizeF(iRounding * 4, iRounding * 4))
+ .translated(0, -iRounding * 2), 180, 90);
+ shape.lineTo(width(), shape.currentPosition().y());
+ shape.lineTo(shape.currentPosition().x(), 0);
+ shape.closeSubpath();
+ break;
+ }
+ m_shape = shape;
+}
+
+void UIMenuToolBarPrivate::setAlignmentType(UIMenuToolBar::AlignmentType enmType)
+{
+ /* Set alignment type: */
+ m_enmAlignmentType = enmType;
+
+ /* Rebuild shape: */
+ rebuildShape();
+}
+
+void UIMenuToolBarPrivate::setMenuAction(QAction *pAction)
+{
+ /* Clear first: */
+ clear();
+ delete m_pMarginLeft;
+ m_pMarginLeft = 0;
+ delete m_pMarginRight;
+ m_pMarginRight = 0;
+
+ /* Create left margin: */
+ m_pMarginLeft = widgetForAction(addWidget(new QWidget));
+
+ /* Add action itself: */
+ addAction(pAction);
+
+ /* Configure the newly added action's button: */
+ QToolButton *pButton = qobject_cast<QToolButton*>(widgetForAction(pAction));
+ AssertPtrReturnVoid(pButton);
+ {
+ /* Configure tool-button: */
+ pButton->setAutoRaise(true);
+ pButton->setPopupMode(QToolButton::InstantPopup);
+ }
+
+ /* Create right margin: */
+ m_pMarginRight = widgetForAction(addWidget(new QWidget));
+
+ /* Rebuild shape: */
+ rebuildShape();
+}
+
+void UIMenuToolBarPrivate::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIToolBar::showEvent(pEvent);
+
+ /* Make sure we should polish dialog: */
+ if (m_fPolished)
+ return;
+
+ /* Call to polish-event: */
+ polishEvent(pEvent);
+
+ /* Mark dialog as polished: */
+ m_fPolished = true;
+}
+
+void UIMenuToolBarPrivate::polishEvent(QShowEvent * /* pEvent */)
+{
+ /* Rebuild shape: */
+ rebuildShape();
+}
+
+void UIMenuToolBarPrivate::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIToolBar::resizeEvent(pEvent);
+
+ /* Rebuild shape: */
+ rebuildShape();
+}
+
+void UIMenuToolBarPrivate::paintEvent(QPaintEvent * /* pEvent */)
+{
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Fill background: */
+ if (!m_shape.isEmpty())
+ {
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setClipPath(m_shape);
+ }
+ QRect backgroundRect = rect();
+ QColor backgroundColor = QApplication::palette().color(QPalette::Window);
+ QLinearGradient headerGradient(backgroundRect.bottomLeft(), backgroundRect.topLeft());
+ headerGradient.setColorAt(0, backgroundColor.darker(120));
+ headerGradient.setColorAt(1, backgroundColor.darker(104));
+ painter.fillRect(backgroundRect, headerGradient);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMenuToolBar implementation. *
+*********************************************************************************************************************************/
+
+UIMenuToolBar::UIMenuToolBar(QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIMenuToolBar::prepare()
+{
+ /* Create layout: */
+ new QHBoxLayout(this);
+ AssertPtrReturnVoid(layout());
+ {
+ /* Configure layout: */
+ layout()->setContentsMargins(0, 0, 0, 0);
+
+ /* Create menu-toolbar: */
+ m_pToolbar = new UIMenuToolBarPrivate;
+ AssertPtrReturnVoid(m_pToolbar);
+ {
+ /* Configure menu-toolbar: */
+ m_pToolbar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+
+ /* Add into layout: */
+ layout()->addWidget(m_pToolbar);
+ }
+ }
+}
+
+void UIMenuToolBar::setAlignmentType(AlignmentType enmType)
+{
+ /* Pass to private object: */
+ return m_pToolbar->setAlignmentType(enmType);
+}
+
+void UIMenuToolBar::setIconSize(const QSize &size)
+{
+ /* Pass to private object: */
+ return m_pToolbar->setIconSize(size);
+}
+
+void UIMenuToolBar::setMenuAction(QAction *pAction)
+{
+ /* Pass to private object: */
+ return m_pToolbar->setMenuAction(pAction);
+}
+
+void UIMenuToolBar::setToolButtonStyle(Qt::ToolButtonStyle enmStyle)
+{
+ /* Pass to private object: */
+ return m_pToolbar->setToolButtonStyle(enmStyle);
+}
+
+QWidget *UIMenuToolBar::widgetForAction(QAction *pAction) const
+{
+ /* Pass to private object: */
+ return m_pToolbar->widgetForAction(pAction);
+}
+
+#include "UIMenuToolBar.moc"
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuToolBar.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuToolBar.h
new file mode 100644
index 00000000..020503fc
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMenuToolBar.h
@@ -0,0 +1,86 @@
+/* $Id: UIMenuToolBar.h $ */
+/** @file
+ * VBox Qt GUI - UIMenuToolBar class declaration.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIMenuToolBar_h
+#define FEQT_INCLUDED_SRC_widgets_UIMenuToolBar_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* Forward declarations: */
+class UIMenuToolBarPrivate;
+
+
+/** QWidget wrapper for QIToolBar extension
+ * holding single drop-down menu of actions. */
+class UIMenuToolBar : public QWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Menu toolbar alignment types. */
+ enum AlignmentType
+ {
+ AlignmentType_TopLeft,
+ AlignmentType_TopRight,
+ AlignmentType_BottomLeft,
+ AlignmentType_BottomRight,
+ };
+
+ /** Constructs menu-toolbar wrapper. */
+ UIMenuToolBar(QWidget *pParent = 0);
+
+ /** Defines toolbar alignment @a enmType. */
+ void setAlignmentType(AlignmentType enmType);
+
+ /** Defines toolbar icon @a size. */
+ void setIconSize(const QSize &size);
+
+ /** Defines toolbar menu action. */
+ void setMenuAction(QAction *pAction);
+
+ /** Defines toolbar tool button @a enmStyle. */
+ void setToolButtonStyle(Qt::ToolButtonStyle enmStyle);
+
+ /** Returns toolbar widget for passed @a pAction. */
+ QWidget *widgetForAction(QAction *pAction) const;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the menu-toolbar instance. */
+ UIMenuToolBarPrivate *m_pToolbar;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIMenuToolBar_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMiniToolBar.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIMiniToolBar.cpp
new file mode 100644
index 00000000..8ac46c99
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMiniToolBar.cpp
@@ -0,0 +1,1215 @@
+/* $Id: UIMiniToolBar.cpp $ */
+/** @file
+ * VBox Qt GUI - UIMiniToolBar class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QLabel>
+#include <QMenu>
+#include <QMoveEvent>
+#include <QPainter>
+#include <QPainterPath>
+#include <QPainterPathStroker>
+#include <QStateMachine>
+#include <QStyle>
+#include <QTimer>
+#include <QToolButton>
+#include <QVBoxLayout>
+#include <QWindow>
+#ifdef VBOX_WS_X11
+# include <QWindowStateChangeEvent>
+#endif
+
+/* GUI includes: */
+#include "UIMiniToolBar.h"
+#include "UIAnimationFramework.h"
+#include "UIIconPool.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UICommon.h"
+#ifdef VBOX_WS_X11
+# include "UIExtraDataManager.h"
+#endif
+
+
+/** QIToolBar reimplementation
+ * providing UIMiniToolBar with mini-toolbar. */
+class UIMiniToolBarPrivate : public QIToolBar
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about we are resized. */
+ void sigResized();
+
+ /** Notifies listeners about action triggered to toggle auto-hide. */
+ void sigAutoHideToggled();
+ /** Notifies listeners about action triggered to minimize. */
+ void sigMinimizeAction();
+ /** Notifies listeners about action triggered to exit. */
+ void sigExitAction();
+ /** Notifies listeners about action triggered to close. */
+ void sigCloseAction();
+
+public:
+
+ /** Constructor. */
+ UIMiniToolBarPrivate();
+
+ /** Defines @a alignment. */
+ void setAlignment(Qt::Alignment alignment);
+
+ /** Returns whether we do auto-hide. */
+ bool autoHide() const;
+ /** Defines whether we do @a fAutoHide. */
+ void setAutoHide(bool fAutoHide);
+
+ /** Defines our @a strText. */
+ void setText(const QString &strText);
+
+ /** Adds our @a menus. */
+ void addMenus(const QList<QMenu*> &menus);
+
+protected:
+
+ /** Show @a pEvent handler. */
+ virtual void showEvent(QShowEvent *pEvent);
+ /** Polish @a pEvent handler. */
+ virtual void polishEvent(QShowEvent *pEvent);
+ /** Resize @a pEvent handler. */
+ virtual void resizeEvent(QResizeEvent *pEvent);
+ /** Paint @a pEvent handler. */
+ virtual void paintEvent(QPaintEvent *pEvent);
+
+private:
+
+ /** Prepare routine. */
+ void prepare();
+
+ /** Rebuilds our shape. */
+ void rebuildShape();
+
+ /** Holds whether this widget was polished. */
+ bool m_fPolished;
+ /** Holds the alignment type. */
+ Qt::Alignment m_alignment;
+ /** Holds the shape. */
+ QPainterPath m_shape;
+
+ /** Holds the action to toggle auto-hide. */
+ QAction *m_pAutoHideAction;
+ /** Holds the name label. */
+ QLabel *m_pLabel;
+ /** Holds the action to trigger minimize. */
+ QAction *m_pMinimizeAction;
+ /** Holds the action to trigger exit. */
+ QAction *m_pRestoreAction;
+ /** Holds the action to trigger close. */
+ QAction *m_pCloseAction;
+
+ /** Holds the pointer to the place to insert menu. */
+ QAction *m_pMenuInsertPosition;
+
+ /** Holds the spacings. */
+ QList<QWidget*> m_spacings;
+ /** Holds the margins. */
+ QList<QWidget*> m_margins;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMiniToolBarPrivate implementation. *
+*********************************************************************************************************************************/
+
+UIMiniToolBarPrivate::UIMiniToolBarPrivate()
+ /* Variables: General stuff: */
+ : m_fPolished(false)
+ , m_alignment(Qt::AlignBottom)
+ /* Variables: Contents stuff: */
+ , m_pAutoHideAction(0)
+ , m_pLabel(0)
+ , m_pMinimizeAction(0)
+ , m_pRestoreAction(0)
+ , m_pCloseAction(0)
+ /* Variables: Menu stuff: */
+ , m_pMenuInsertPosition(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIMiniToolBarPrivate::setAlignment(Qt::Alignment alignment)
+{
+ /* Make sure alignment really changed: */
+ if (m_alignment == alignment)
+ return;
+
+ /* Update alignment: */
+ m_alignment = alignment;
+
+ /* Rebuild shape: */
+ rebuildShape();
+}
+
+bool UIMiniToolBarPrivate::autoHide() const
+{
+ /* Return auto-hide: */
+ return !m_pAutoHideAction->isChecked();
+}
+
+void UIMiniToolBarPrivate::setAutoHide(bool fAutoHide)
+{
+ /* Make sure auto-hide really changed: */
+ if (m_pAutoHideAction->isChecked() == !fAutoHide)
+ return;
+
+ /* Update auto-hide: */
+ m_pAutoHideAction->setChecked(!fAutoHide);
+}
+
+void UIMiniToolBarPrivate::setText(const QString &strText)
+{
+ /* Make sure text really changed: */
+ if (m_pLabel->text() == strText)
+ return;
+
+ /* Update text: */
+ m_pLabel->setText(strText);
+
+ /* Resize to sizehint: */
+ resize(sizeHint());
+}
+
+void UIMiniToolBarPrivate::addMenus(const QList<QMenu*> &menus)
+{
+ /* For each of the passed menu items: */
+ for (int i = 0; i < menus.size(); ++i)
+ {
+ /* Get corresponding menu-action: */
+ QAction *pAction = menus[i]->menuAction();
+ /* Insert it into corresponding place: */
+ insertAction(m_pMenuInsertPosition, pAction);
+ /* Configure corresponding tool-button: */
+ if (QToolButton *pButton = qobject_cast<QToolButton*>(widgetForAction(pAction)))
+ {
+ pButton->setPopupMode(QToolButton::InstantPopup);
+ pButton->setAutoRaise(true);
+ }
+ /* Add some spacing: */
+ if (i != menus.size() - 1)
+ m_spacings << widgetForAction(insertWidget(m_pMenuInsertPosition, new QWidget(this)));
+ }
+
+ /* Resize to sizehint: */
+ resize(sizeHint());
+}
+
+void UIMiniToolBarPrivate::showEvent(QShowEvent *pEvent)
+{
+ /* Make sure we should polish dialog: */
+ if (m_fPolished)
+ return;
+
+ /* Call to polish-event: */
+ polishEvent(pEvent);
+
+ /* Mark dialog as polished: */
+ m_fPolished = true;
+}
+
+void UIMiniToolBarPrivate::polishEvent(QShowEvent*)
+{
+ /* Toolbar spacings: */
+ foreach(QWidget *pSpacing, m_spacings)
+ pSpacing->setMinimumWidth(5);
+
+ /* Title spacings: */
+ foreach(QWidget *pLableMargin, m_margins)
+ pLableMargin->setMinimumWidth(15);
+
+ /* Resize to sizehint: */
+ resize(sizeHint());
+}
+
+void UIMiniToolBarPrivate::resizeEvent(QResizeEvent*)
+{
+ /* Rebuild shape: */
+ rebuildShape();
+
+ /* Notify listeners: */
+ emit sigResized();
+}
+
+void UIMiniToolBarPrivate::paintEvent(QPaintEvent*)
+{
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Fill background: */
+ if (!m_shape.isEmpty())
+ {
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setClipPath(m_shape);
+ }
+ QRect backgroundRect = rect();
+ QColor backgroundColor = QApplication::palette().color(QPalette::Window);
+ QLinearGradient headerGradient(backgroundRect.bottomLeft(), backgroundRect.topLeft());
+ headerGradient.setColorAt(0, backgroundColor.darker(120));
+ headerGradient.setColorAt(1, backgroundColor.darker(90));
+ painter.fillRect(backgroundRect, headerGradient);
+}
+
+void UIMiniToolBarPrivate::prepare()
+{
+ /* Determine icon metric: */
+ const QStyle *pStyle = QApplication::style();
+ const int iIconMetric = pStyle->pixelMetric(QStyle::PM_SmallIconSize);
+
+ /* Configure toolbar: */
+ setIconSize(QSize(iIconMetric, iIconMetric));
+
+ /* Left margin: */
+#ifdef VBOX_WS_X11
+ if (uiCommon().isCompositingManagerRunning())
+ m_spacings << widgetForAction(addWidget(new QWidget));
+#else /* !VBOX_WS_X11 */
+ m_spacings << widgetForAction(addWidget(new QWidget));
+#endif /* !VBOX_WS_X11 */
+
+ /* Prepare push-pin: */
+ m_pAutoHideAction = new QAction(this);
+ m_pAutoHideAction->setIcon(UIIconPool::iconSet(":/pin_16px.png"));
+ m_pAutoHideAction->setToolTip(UIMiniToolBar::tr("Always show the toolbar"));
+ m_pAutoHideAction->setCheckable(true);
+ connect(m_pAutoHideAction, SIGNAL(toggled(bool)), this, SIGNAL(sigAutoHideToggled()));
+ addAction(m_pAutoHideAction);
+
+ /* Left menu margin: */
+ m_spacings << widgetForAction(addWidget(new QWidget));
+
+ /* Right menu margin: */
+ m_pMenuInsertPosition = addWidget(new QWidget);
+ m_spacings << widgetForAction(m_pMenuInsertPosition);
+
+ /* Left label margin: */
+ m_margins << widgetForAction(addWidget(new QWidget));
+
+ /* Insert a label for VM Name: */
+ m_pLabel = new QLabel;
+ m_pLabel->setAlignment(Qt::AlignCenter);
+ addWidget(m_pLabel);
+
+ /* Right label margin: */
+ m_margins << widgetForAction(addWidget(new QWidget));
+
+ /* Minimize action: */
+ m_pMinimizeAction = new QAction(this);
+ m_pMinimizeAction->setIcon(UIIconPool::iconSet(":/minimize_16px.png"));
+ m_pMinimizeAction->setToolTip(UIMiniToolBar::tr("Minimize Window"));
+ connect(m_pMinimizeAction, SIGNAL(triggered()), this, SIGNAL(sigMinimizeAction()));
+ addAction(m_pMinimizeAction);
+
+ /* Exit action: */
+ m_pRestoreAction = new QAction(this);
+ m_pRestoreAction->setIcon(UIIconPool::iconSet(":/restore_16px.png"));
+ m_pRestoreAction->setToolTip(UIMiniToolBar::tr("Exit Full Screen or Seamless Mode"));
+ connect(m_pRestoreAction, SIGNAL(triggered()), this, SIGNAL(sigExitAction()));
+ addAction(m_pRestoreAction);
+
+ /* Close action: */
+ m_pCloseAction = new QAction(this);
+ m_pCloseAction->setIcon(UIIconPool::iconSet(":/close_16px.png"));
+ m_pCloseAction->setToolTip(UIMiniToolBar::tr("Close VM"));
+ connect(m_pCloseAction, SIGNAL(triggered()), this, SIGNAL(sigCloseAction()));
+ addAction(m_pCloseAction);
+
+ /* Right margin: */
+#ifdef VBOX_WS_X11
+ if (uiCommon().isCompositingManagerRunning())
+ m_spacings << widgetForAction(addWidget(new QWidget));
+#else /* !VBOX_WS_X11 */
+ m_spacings << widgetForAction(addWidget(new QWidget));
+#endif /* !VBOX_WS_X11 */
+}
+
+void UIMiniToolBarPrivate::rebuildShape()
+{
+#ifdef VBOX_WS_X11
+ if (!uiCommon().isCompositingManagerRunning())
+ return;
+#endif /* VBOX_WS_X11 */
+
+ /* Rebuild shape: */
+ QPainterPath shape;
+ switch (m_alignment)
+ {
+ case Qt::AlignTop:
+ {
+ shape.moveTo(0, 0);
+ shape.lineTo(shape.currentPosition().x(), height() - 10);
+ shape.arcTo(QRectF(shape.currentPosition(), QSizeF(20, 20)).translated(0, -10), 180, 90);
+ shape.lineTo(width() - 10, shape.currentPosition().y());
+ shape.arcTo(QRectF(shape.currentPosition(), QSizeF(20, 20)).translated(-10, -20), 270, 90);
+ shape.lineTo(shape.currentPosition().x(), 0);
+ shape.closeSubpath();
+ break;
+ }
+ case Qt::AlignBottom:
+ {
+ shape.moveTo(0, height());
+ shape.lineTo(shape.currentPosition().x(), 10);
+ shape.arcTo(QRectF(shape.currentPosition(), QSizeF(20, 20)).translated(0, -10), 180, -90);
+ shape.lineTo(width() - 10, shape.currentPosition().y());
+ shape.arcTo(QRectF(shape.currentPosition(), QSizeF(20, 20)).translated(-10, 0), 90, -90);
+ shape.lineTo(shape.currentPosition().x(), height());
+ shape.closeSubpath();
+ break;
+ }
+ default:
+ break;
+ }
+ m_shape = shape;
+
+ /* Update: */
+ update();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIMiniToolBar implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+Qt::WindowFlags UIMiniToolBar::defaultWindowFlags(GeometryType geometryType)
+{
+ /* Not everywhere: */
+ Q_UNUSED(geometryType);
+
+#ifdef VBOX_WS_X11
+ /* Depending on current WM: */
+ switch (uiCommon().typeOfWindowManager())
+ {
+ // WORKAROUND:
+ // By strange reason, frameless full-screen windows under certain WMs
+ // do not respect the transient relationship between each other.
+ // By nor less strange reason, frameless full-screen *tool* windows
+ // respects such relationship, so we are doing what WM want.
+ case X11WMType_GNOMEShell:
+ case X11WMType_KWin:
+ case X11WMType_Metacity:
+ case X11WMType_Mutter:
+ case X11WMType_Xfwm4:
+ return geometryType == GeometryType_Full ?
+ Qt::Tool | Qt::FramelessWindowHint :
+ Qt::Window | Qt::FramelessWindowHint;
+ default: break;
+ }
+#endif /* VBOX_WS_X11 */
+
+ /* Frameless window by default: */
+ return Qt::Window | Qt::FramelessWindowHint;
+}
+
+UIMiniToolBar::UIMiniToolBar(QWidget *pParent,
+ GeometryType geometryType,
+ Qt::Alignment alignment,
+ bool fAutoHide /* = true */,
+ int iWindowIndex /* = -1 */)
+ : QWidget(0, defaultWindowFlags(geometryType))
+ /* Variables: General stuff: */
+ , m_pParent(pParent)
+ , m_geometryType(geometryType)
+ , m_alignment(alignment)
+ , m_fAutoHide(fAutoHide)
+ , m_iWindowIndex(iWindowIndex)
+ /* Variables: Contents stuff: */
+ , m_pArea(0)
+ , m_pToolbar(0)
+ /* Variables: Hover stuff: */
+ , m_fHovered(false)
+ , m_pHoverEnterTimer(0)
+ , m_pHoverLeaveTimer(0)
+ , m_pAnimation(0)
+#ifdef VBOX_WS_X11
+ , m_fIsParentMinimized(false)
+#endif
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIMiniToolBar::~UIMiniToolBar()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIMiniToolBar::setAlignment(Qt::Alignment alignment)
+{
+ /* Make sure toolbar created: */
+ AssertPtrReturnVoid(m_pToolbar);
+
+ /* Make sure alignment really changed: */
+ if (m_alignment == alignment)
+ return;
+
+ /* Update alignment: */
+ m_alignment = alignment;
+
+ /* Adjust geometry: */
+ adjustGeometry();
+
+ /* Propagate to child to update shape: */
+ m_pToolbar->setAlignment(m_alignment);
+}
+
+void UIMiniToolBar::setAutoHide(bool fAutoHide, bool fPropagateToChild /* = true */)
+{
+ /* Make sure toolbar created: */
+ AssertPtrReturnVoid(m_pToolbar);
+
+ /* Make sure auto-hide really changed: */
+ if (m_fAutoHide == fAutoHide)
+ return;
+
+ /* Update auto-hide: */
+ m_fAutoHide = fAutoHide;
+
+ /* Adjust geometry: */
+ adjustGeometry();
+
+ /* Propagate to child to update action if necessary: */
+ if (fPropagateToChild)
+ m_pToolbar->setAutoHide(m_fAutoHide);
+}
+
+void UIMiniToolBar::setText(const QString &strText)
+{
+ /* Make sure toolbar created: */
+ AssertPtrReturnVoid(m_pToolbar);
+
+ /* Propagate to child: */
+ m_pToolbar->setText(strText);
+}
+
+void UIMiniToolBar::addMenus(const QList<QMenu*> &menus)
+{
+ /* Make sure toolbar created: */
+ AssertPtrReturnVoid(m_pToolbar);
+
+ /* Propagate to child: */
+ m_pToolbar->addMenus(menus);
+}
+
+void UIMiniToolBar::adjustGeometry()
+{
+ /* Resize toolbar to minimum size: */
+ m_pToolbar->resize(m_pToolbar->sizeHint());
+
+ /* Calculate toolbar position: */
+ int iX = 0, iY = 0;
+ iX = width() / 2 - m_pToolbar->width() / 2;
+ switch (m_alignment)
+ {
+ case Qt::AlignTop: iY = 0; break;
+ case Qt::AlignBottom: iY = height() - m_pToolbar->height(); break;
+ default: break;
+ }
+
+ /* Update auto-hide animation: */
+ m_shownToolbarPosition = QPoint(iX, iY);
+ switch (m_alignment)
+ {
+ case Qt::AlignTop: m_hiddenToolbarPosition = m_shownToolbarPosition - QPoint(0, m_pToolbar->height() - 3); break;
+ case Qt::AlignBottom: m_hiddenToolbarPosition = m_shownToolbarPosition + QPoint(0, m_pToolbar->height() - 3); break;
+ }
+ m_pAnimation->update();
+
+ /* Update toolbar geometry if known: */
+ if (property("AnimationState").toString() == "Final")
+ m_pToolbar->move(m_shownToolbarPosition);
+ else
+ m_pToolbar->move(m_hiddenToolbarPosition);
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /* Adjust window mask: */
+ setMask(m_pToolbar->geometry());
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+}
+
+bool UIMiniToolBar::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* Detect if we have window activation stolen: */
+ if (pWatched == this && pEvent->type() == QEvent::WindowActivate)
+ {
+#if defined(VBOX_WS_WIN)
+ /* Just call the method asynchronously, after possible popups opened: */
+ QTimer::singleShot(0, this, SLOT(sltCheckWindowActivationSanity()));
+#elif defined(VBOX_WS_X11)
+ // WORKAROUND:
+ // Under certain WMs we can receive stolen activation event too early,
+ // returning activation to initial source immediately makes no sense.
+ // In fact, Qt is not become aware of actual window activation later,
+ // so we are going to check for window activation in let's say 100ms.
+ QTimer::singleShot(100, this, SLOT(sltCheckWindowActivationSanity()));
+#endif /* VBOX_WS_X11 */
+ }
+
+ /* If that's parent window event: */
+ if (pWatched == m_pParent)
+ {
+ switch (pEvent->type())
+ {
+ case QEvent::Hide:
+ {
+ /* Skip if parent or we are minimized: */
+ if ( isParentMinimized()
+ || isMinimized())
+ break;
+
+ /* Asynchronously call for sltHide(): */
+ LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent hide event\n"));
+ QMetaObject::invokeMethod(this, "sltHide", Qt::QueuedConnection);
+ break;
+ }
+ case QEvent::Show:
+ {
+ /* Skip if parent or we are minimized: */
+ if ( isParentMinimized()
+ || isMinimized())
+ break;
+
+ /* Asynchronously call for sltShow(): */
+ LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent show event\n"));
+ QMetaObject::invokeMethod(this, "sltShow", Qt::QueuedConnection);
+ break;
+ }
+ case QEvent::Move:
+ {
+ // WORKAROUND:
+ // In certain cases there can be that parent is moving outside of
+ // full-screen geometry. That for example can happen if virtual
+ // desktop being changed. We should ignore Move event in such case.
+ /* Skip if parent is outside of full-screen geometry: */
+ QMoveEvent *pMoveEvent = static_cast<QMoveEvent*>(pEvent);
+ if (!gpDesktop->screenGeometry(m_pParent).contains(pMoveEvent->pos()))
+ break;
+ /* Skip if parent or we are invisible: */
+ if ( !m_pParent->isVisible()
+ || !isVisible())
+ break;
+ /* Skip if parent or we are minimized: */
+ if ( isParentMinimized()
+ || isMinimized())
+ break;
+
+ /* Asynchronously call for sltShow(): */
+ LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent move event\n"));
+ QMetaObject::invokeMethod(this, "sltShow", Qt::QueuedConnection);
+ break;
+ }
+ case QEvent::Resize:
+ {
+ /* Skip if parent or we are invisible: */
+ if ( !m_pParent->isVisible()
+ || !isVisible())
+ break;
+ /* Skip if parent or we are minimized: */
+ if ( isParentMinimized()
+ || isMinimized())
+ break;
+
+ /* Asynchronously call for sltShow(): */
+ LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent resize event\n"));
+ QMetaObject::invokeMethod(this, "sltShow", Qt::QueuedConnection);
+ break;
+ }
+#ifdef VBOX_WS_X11
+ case QEvent::WindowStateChange:
+ {
+ /* Watch for parent window state changes: */
+ QWindowStateChangeEvent *pChangeEvent = static_cast<QWindowStateChangeEvent*>(pEvent);
+ LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent window state changed from %d to %d\n",
+ (int)pChangeEvent->oldState(), (int)m_pParent->windowState()));
+
+ if ( m_pParent->windowState() & Qt::WindowMinimized
+ && !m_fIsParentMinimized)
+ {
+ /* Mark parent window minimized, isMinimized() is not enough due to Qt5vsX11 fight: */
+ LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent window is minimized\n"));
+ m_fIsParentMinimized = true;
+ }
+ else
+ if (m_fIsParentMinimized)
+ {
+ switch (m_geometryType)
+ {
+ case GeometryType_Available:
+ {
+ if ( m_pParent->windowState() == Qt::WindowMaximized
+ && pChangeEvent->oldState() == Qt::WindowNoState)
+ {
+ /* Mark parent window non-minimized, isMinimized() is not enough due to Qt5vsX11 fight: */
+ LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent window is maximized\n"));
+ m_fIsParentMinimized = false;
+ }
+ break;
+ }
+ case GeometryType_Full:
+ {
+ if ( m_pParent->windowState() == Qt::WindowFullScreen
+ && pChangeEvent->oldState() == Qt::WindowNoState)
+ {
+ /* Mark parent window non-minimized, isMinimized() is not enough due to Qt5vsX11 fight: */
+ LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent window is full-screen\n"));
+ m_fIsParentMinimized = false;
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+#endif /* VBOX_WS_X11 */
+ default:
+ break;
+ }
+ }
+
+ /* Call to base-class: */
+ return QWidget::eventFilter(pWatched, pEvent);
+}
+
+void UIMiniToolBar::resizeEvent(QResizeEvent*)
+{
+ /* Adjust geometry: */
+ adjustGeometry();
+}
+
+#ifdef VBOX_IS_QT6_OR_LATER /* QWidget::enterEvent uses QEnterEvent since qt6 */
+void UIMiniToolBar::enterEvent(QEnterEvent*)
+#else
+void UIMiniToolBar::enterEvent(QEvent*)
+#endif
+{
+ /* Stop the hover-leave timer if necessary: */
+ if (m_pHoverLeaveTimer && m_pHoverLeaveTimer->isActive())
+ m_pHoverLeaveTimer->stop();
+
+ /* Start the hover-enter timer: */
+ if (m_pHoverEnterTimer)
+ m_pHoverEnterTimer->start();
+}
+
+void UIMiniToolBar::leaveEvent(QEvent*)
+{
+ // WORKAROUND:
+ // No idea why, but GUI receives mouse leave event
+ // when the mouse cursor is on the border of screen
+ // even if underlying widget is on the border of
+ // screen as well, we should detect and ignore that.
+ // Besides that, this is a good way to keep the
+ // tool-bar visible when the mouse moving through
+ // the desktop strut till the real screen border.
+ const QPoint cursorPosition = QCursor::pos();
+ if ( cursorPosition.y() <= y() + 1
+ || cursorPosition.y() >= y() + height() - 1)
+ return;
+
+ /* Stop the hover-enter timer if necessary: */
+ if (m_pHoverEnterTimer && m_pHoverEnterTimer->isActive())
+ m_pHoverEnterTimer->stop();
+
+ /* Start the hover-leave timer: */
+ if (m_fAutoHide && m_pHoverLeaveTimer)
+ m_pHoverLeaveTimer->start();
+}
+
+void UIMiniToolBar::sltHandleToolbarResize()
+{
+ /* Adjust geometry: */
+ adjustGeometry();
+}
+
+void UIMiniToolBar::sltAutoHideToggled()
+{
+ /* Propagate from child: */
+ setAutoHide(m_pToolbar->autoHide(), false);
+ emit sigAutoHideToggled(m_pToolbar->autoHide());
+}
+
+void UIMiniToolBar::sltHoverEnter()
+{
+ /* Mark as 'hovered' if necessary: */
+ if (!m_fHovered)
+ {
+ m_fHovered = true;
+ emit sigHoverEnter();
+ }
+}
+
+void UIMiniToolBar::sltHoverLeave()
+{
+ /* Mark as 'unhovered' if necessary: */
+ if (m_fHovered)
+ {
+ m_fHovered = false;
+ if (m_fAutoHide)
+ emit sigHoverLeave();
+ }
+}
+
+void UIMiniToolBar::sltCheckWindowActivationSanity()
+{
+ /* Do nothing if parent window is already active: */
+ if ( m_pParent
+ && QGuiApplication::focusWindow() == m_pParent->windowHandle())
+ return;
+
+ /* We can't touch window activation if have modal or popup
+ * window opened, otherwise internal Qt state get flawed: */
+ if ( QApplication::activeModalWidget()
+ || QApplication::activePopupWidget())
+ {
+ /* But we should recheck the state in let's say 300ms: */
+ QTimer::singleShot(300, this, SLOT(sltCheckWindowActivationSanity()));
+ return;
+ }
+
+ /* Notify listener about we have stole window activation: */
+ emit sigNotifyAboutWindowActivationStolen();
+}
+
+void UIMiniToolBar::sltHide()
+{
+ LogRel(("GUI: Hide mini-toolbar for window #%d\n", m_iWindowIndex));
+
+#if defined(VBOX_WS_MAC)
+
+ // Nothing
+
+#elif defined(VBOX_WS_WIN)
+
+ /* Reset window state to NONE and hide it: */
+ setWindowState(Qt::WindowNoState);
+ hide();
+
+#elif defined(VBOX_WS_X11)
+
+ /* Just hide window: */
+ hide();
+
+#else
+
+# warning "port me"
+
+#endif
+}
+
+void UIMiniToolBar::sltShow()
+{
+ LogRel(("GUI: Show mini-toolbar for window #%d\n", m_iWindowIndex));
+
+ /* Update transience: */
+ sltAdjustTransience();
+
+#if defined(VBOX_WS_MAC)
+
+ // Nothing
+
+#elif defined(VBOX_WS_WIN)
+
+ // WORKAROUND:
+ // If the host-screen is changed => we should
+ // reset window state to NONE first because
+ // we need an expose on showFullScreen call.
+ if (m_geometryType == GeometryType_Full)
+ setWindowState(Qt::WindowNoState);
+
+ /* Adjust window: */
+ sltAdjust();
+ /* Show window in necessary mode: */
+ switch (m_geometryType)
+ {
+ case GeometryType_Available:
+ {
+ /* Show normal: */
+ show();
+ break;
+ }
+ case GeometryType_Full:
+ {
+ /* Show full-screen: */
+ showFullScreen();
+ break;
+ }
+ }
+
+#elif defined(VBOX_WS_X11)
+
+ /* Show window in necessary mode: */
+ switch (m_geometryType)
+ {
+ case GeometryType_Available:
+ {
+ /* Adjust window: */
+ sltAdjust();
+ /* Show maximized: */
+ if (!isMaximized())
+ showMaximized();
+ break;
+ }
+ case GeometryType_Full:
+ {
+ /* Show full-screen: */
+ showFullScreen();
+ /* Adjust window: */
+ sltAdjust();
+ break;
+ }
+ }
+
+#else
+
+# warning "port me"
+
+#endif
+
+ /* Simulate toolbar auto-hiding: */
+ simulateToolbarAutoHiding();
+}
+
+void UIMiniToolBar::sltAdjust()
+{
+ LogRel(("GUI: Adjust mini-toolbar for window #%d\n", m_iWindowIndex));
+
+ /* Get corresponding host-screen: */
+ const int iHostScreenCount = UIDesktopWidgetWatchdog::screenCount();
+ int iHostScreen = UIDesktopWidgetWatchdog::screenNumber(m_pParent);
+ // WORKAROUND:
+ // When switching host-screen count, especially in complex cases where RDP client is "replacing" host-screen(s) with own virtual-screen(s),
+ // Qt could behave quite arbitrary and laggy, and due to racing there could be a situation when QDesktopWidget::screenNumber() returns -1
+ // as a host-screen number where the parent window is currently located. We should handle this situation anyway, so let's assume the parent
+ // window is located on primary (0) host-screen if it's present or ignore this request at all.
+ if (iHostScreen < 0 || iHostScreen >= iHostScreenCount)
+ {
+ if (iHostScreenCount > 0)
+ {
+ LogRel(("GUI: Mini-toolbar parent window #%d is located on invalid host-screen #%d. Fallback to primary.\n", m_iWindowIndex, iHostScreen));
+ iHostScreen = 0;
+ }
+ else
+ {
+ LogRel(("GUI: Mini-toolbar parent window #%d is located on invalid host-screen #%d. Ignore request.\n", m_iWindowIndex, iHostScreen));
+ return;
+ }
+ }
+
+ /* Get corresponding working area: */
+ QRect workingArea;
+ switch (m_geometryType)
+ {
+ case GeometryType_Available: workingArea = gpDesktop->availableGeometry(iHostScreen); break;
+ case GeometryType_Full: workingArea = gpDesktop->screenGeometry(iHostScreen); break;
+ }
+ Q_UNUSED(workingArea);
+
+#if defined(VBOX_WS_MAC)
+
+ // Nothing
+
+#elif defined(VBOX_WS_WIN)
+
+ switch (m_geometryType)
+ {
+ case GeometryType_Available:
+ {
+ /* Set appropriate window size: */
+ const QSize newSize = workingArea.size();
+ LogRel(("GUI: Resize mini-toolbar for window #%d to %dx%d\n",
+ m_iWindowIndex, newSize.width(), newSize.height()));
+ resize(newSize);
+
+ /* Move window onto required screen: */
+ const QPoint newPosition = workingArea.topLeft();
+ LogRel(("GUI: Move mini-toolbar for window #%d to %dx%d\n",
+ m_iWindowIndex, newPosition.x(), newPosition.y()));
+ move(newPosition);
+
+ break;
+ }
+ case GeometryType_Full:
+ {
+ /* Map window onto required screen: */
+ LogRel(("GUI: Map mini-toolbar for window #%d to screen %d of %d\n",
+ m_iWindowIndex, iHostScreen, qApp->screens().size()));
+ windowHandle()->setScreen(qApp->screens().at(iHostScreen));
+
+ /* Set appropriate window size: */
+ const QSize newSize = workingArea.size();
+ LogRel(("GUI: Resize mini-toolbar for window #%d to %dx%d\n",
+ m_iWindowIndex, newSize.width(), newSize.height()));
+ resize(newSize);
+
+ break;
+ }
+ }
+
+#elif defined(VBOX_WS_X11)
+
+ switch (m_geometryType)
+ {
+ case GeometryType_Available:
+ {
+ /* Make sure we are located on corresponding host-screen: */
+ if ( UIDesktopWidgetWatchdog::screenCount() > 1
+ && (x() != workingArea.x() || y() != workingArea.y()))
+ {
+ // WORKAROUND:
+ // With Qt5 on KDE we can't just move the window onto desired host-screen if
+ // window is maximized. So we have to show it normal first of all:
+ if (isVisible() && isMaximized())
+ showNormal();
+
+ // WORKAROUND:
+ // With Qt5 on X11 we can't just move the window onto desired host-screen if
+ // window size is more than the available geometry (working area) of that
+ // host-screen. So we are resizing it to a smaller size first of all:
+ const QSize newSize = workingArea.size() * .9;
+ LogRel(("GUI: Resize mini-toolbar for window #%d to smaller size %dx%d\n",
+ m_iWindowIndex, newSize.width(), newSize.height()));
+ resize(newSize);
+
+ /* Move window onto required screen: */
+ const QPoint newPosition = workingArea.topLeft();
+ LogRel(("GUI: Move mini-toolbar for window #%d to %dx%d\n",
+ m_iWindowIndex, newPosition.x(), newPosition.y()));
+ move(newPosition);
+ }
+
+ break;
+ }
+ case GeometryType_Full:
+ {
+ /* Determine whether we should use the native full-screen mode: */
+ const bool fUseNativeFullScreen = NativeWindowSubsystem::X11SupportsFullScreenMonitorsProtocol()
+ && !gEDataManager->legacyFullscreenModeRequested();
+ if (fUseNativeFullScreen)
+ {
+ /* Tell recent window managers which host-screen this window should be mapped to: */
+ NativeWindowSubsystem::X11SetFullScreenMonitor(this, iHostScreen);
+ }
+
+ /* Set appropriate window size: */
+ const QSize newSize = workingArea.size();
+ LogRel(("GUI: Resize mini-toolbar for window #%d to %dx%d\n",
+ m_iWindowIndex, newSize.width(), newSize.height()));
+ resize(newSize);
+
+ /* Move window onto required screen: */
+ const QPoint newPosition = workingArea.topLeft();
+ LogRel(("GUI: Move mini-toolbar for window #%d to %dx%d\n",
+ m_iWindowIndex, newPosition.x(), newPosition.y()));
+ move(newPosition);
+
+ /* Re-apply the full-screen state lost on above move(): */
+ setWindowState(Qt::WindowFullScreen);
+
+ break;
+ }
+ }
+
+#else
+
+# warning "port me"
+
+#endif
+}
+
+void UIMiniToolBar::sltAdjustTransience()
+{
+ // WORKAROUND:
+ // Make sure win id is generated,
+ // else Qt5 can crash otherwise.
+ winId();
+ m_pParent->winId();
+
+ /* Add the transience dependency: */
+ windowHandle()->setTransientParent(m_pParent->windowHandle());
+}
+
+void UIMiniToolBar::prepare()
+{
+ /* Install event-filters: */
+ installEventFilter(this);
+ m_pParent->installEventFilter(this);
+
+#if defined(VBOX_WS_WIN)
+ /* No background until first paint-event: */
+ setAttribute(Qt::WA_NoSystemBackground);
+ /* Enable translucency through Qt API: */
+ setAttribute(Qt::WA_TranslucentBackground);
+#elif defined(VBOX_WS_X11)
+ /* Enable translucency through Qt API if supported: */
+ if (uiCommon().isCompositingManagerRunning())
+ setAttribute(Qt::WA_TranslucentBackground);
+#endif /* VBOX_WS_X11 */
+
+ /* Make sure we have no focus: */
+ setFocusPolicy(Qt::NoFocus);
+
+ /* Prepare area: */
+ m_pArea = new QWidget;
+ {
+ /* Allow any area size: */
+ m_pArea->setMinimumSize(QSize(1, 1));
+ /* Configure own background: */
+ QPalette pal = m_pArea->palette();
+ pal.setColor(QPalette::Window, QColor(Qt::transparent));
+ m_pArea->setPalette(pal);
+ /* Layout area according parent-widget: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+ pMainLayout->addWidget(m_pArea);
+ /* Make sure we have no focus: */
+ m_pArea->setFocusPolicy(Qt::NoFocus);
+ }
+
+ /* Prepare mini-toolbar: */
+ m_pToolbar = new UIMiniToolBarPrivate;
+ {
+ /* Make sure we have no focus: */
+ m_pToolbar->setFocusPolicy(Qt::NoFocus);
+ /* Propagate known options to child: */
+ m_pToolbar->setAutoHide(m_fAutoHide);
+ m_pToolbar->setAlignment(m_alignment);
+ /* Configure own background: */
+ QPalette pal = m_pToolbar->palette();
+ pal.setColor(QPalette::Window, QApplication::palette().color(QPalette::Window));
+ m_pToolbar->setPalette(pal);
+ /* Configure child connections: */
+ connect(m_pToolbar, &UIMiniToolBarPrivate::sigResized, this, &UIMiniToolBar::sltHandleToolbarResize);
+ connect(m_pToolbar, &UIMiniToolBarPrivate::sigAutoHideToggled, this, &UIMiniToolBar::sltAutoHideToggled);
+ connect(m_pToolbar, &UIMiniToolBarPrivate::sigMinimizeAction, this, &UIMiniToolBar::sigMinimizeAction);
+ connect(m_pToolbar, &UIMiniToolBarPrivate::sigExitAction, this, &UIMiniToolBar::sigExitAction);
+ connect(m_pToolbar, &UIMiniToolBarPrivate::sigCloseAction, this, &UIMiniToolBar::sigCloseAction);
+ /* Add child to area: */
+ m_pToolbar->setParent(m_pArea);
+ /* Make sure we have no focus: */
+ m_pToolbar->setFocusPolicy(Qt::NoFocus);
+ }
+
+ /* Prepare hover-enter/leave timers: */
+ m_pHoverEnterTimer = new QTimer(this);
+ {
+ m_pHoverEnterTimer->setSingleShot(true);
+ m_pHoverEnterTimer->setInterval(500);
+ connect(m_pHoverEnterTimer, &QTimer::timeout, this, &UIMiniToolBar::sltHoverEnter);
+ }
+ m_pHoverLeaveTimer = new QTimer(this);
+ {
+ m_pHoverLeaveTimer->setSingleShot(true);
+ m_pHoverLeaveTimer->setInterval(500);
+ connect(m_pHoverLeaveTimer, &QTimer::timeout, this, &UIMiniToolBar::sltHoverLeave);
+ }
+
+ /* Install 'auto-hide' animation to 'toolbarPosition' property: */
+ m_pAnimation = UIAnimation::installPropertyAnimation(this,
+ "toolbarPosition",
+ "hiddenToolbarPosition", "shownToolbarPosition",
+ SIGNAL(sigHoverEnter()), SIGNAL(sigHoverLeave()),
+ true);
+
+ /* Adjust geometry first time: */
+ adjustGeometry();
+
+#ifdef VBOX_WS_X11
+ /* Hide mini-toolbar from taskbar and pager: */
+ NativeWindowSubsystem::X11SetSkipTaskBarFlag(this);
+ NativeWindowSubsystem::X11SetSkipPagerFlag(this);
+#endif
+}
+
+void UIMiniToolBar::cleanup()
+{
+ /* Stop hover-enter/leave timers: */
+ if (m_pHoverEnterTimer && m_pHoverEnterTimer->isActive())
+ m_pHoverEnterTimer->stop();
+ if (m_pHoverLeaveTimer && m_pHoverLeaveTimer->isActive())
+ m_pHoverLeaveTimer->stop();
+
+ /* Destroy animation before toolbar: */
+ delete m_pAnimation;
+ m_pAnimation = 0;
+
+ /* Destroy toolbar after animation: */
+ delete m_pToolbar;
+ m_pToolbar = 0;
+}
+
+void UIMiniToolBar::simulateToolbarAutoHiding()
+{
+ /* This simulation helps user to notice
+ * toolbar location, so it will be used only
+ * 1. if toolbar unhovered and
+ * 2. auto-hide feature enabled: */
+ if (m_fHovered || !m_fAutoHide)
+ return;
+
+ /* Simulate hover-leave event: */
+ m_fHovered = true;
+ m_pHoverLeaveTimer->start();
+}
+
+void UIMiniToolBar::setToolbarPosition(QPoint point)
+{
+ /* Update position: */
+ AssertPtrReturnVoid(m_pToolbar);
+ m_pToolbar->move(point);
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /* Update window mask: */
+ setMask(m_pToolbar->geometry());
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+}
+
+QPoint UIMiniToolBar::toolbarPosition() const
+{
+ /* Return position: */
+ AssertPtrReturn(m_pToolbar, QPoint());
+ return m_pToolbar->pos();
+}
+
+bool UIMiniToolBar::isParentMinimized() const
+{
+#ifdef VBOX_WS_X11
+ return m_fIsParentMinimized;
+#else
+ return m_pParent->isMinimized();
+#endif
+}
+
+#include "UIMiniToolBar.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIMiniToolBar.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIMiniToolBar.h
new file mode 100644
index 00000000..c8c1e279
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIMiniToolBar.h
@@ -0,0 +1,223 @@
+/* $Id: UIMiniToolBar.h $ */
+/** @file
+ * VBox Qt GUI - UIMiniToolBar class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIMiniToolBar_h
+#define FEQT_INCLUDED_SRC_widgets_UIMiniToolBar_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIToolBar.h"
+
+/* Forward declarations: */
+class QMenu;
+class QTimer;
+class QLabel;
+class UIAnimation;
+class UIMiniToolBarPrivate;
+
+/** Geometry types. */
+enum GeometryType
+{
+ GeometryType_Available,
+ GeometryType_Full
+};
+
+
+/** QWidget reimplementation
+ * providing GUI with slideable mini-toolbar used in full-screen/seamless modes. */
+class UIMiniToolBar : public QWidget
+{
+ Q_OBJECT;
+ Q_PROPERTY(QPoint toolbarPosition READ toolbarPosition WRITE setToolbarPosition);
+ Q_PROPERTY(QPoint hiddenToolbarPosition READ hiddenToolbarPosition);
+ Q_PROPERTY(QPoint shownToolbarPosition READ shownToolbarPosition);
+
+signals:
+
+ /** Notifies listeners about action triggered to minimize. */
+ void sigMinimizeAction();
+ /** Notifies listeners about action triggered to exit. */
+ void sigExitAction();
+ /** Notifies listeners about action triggered to close. */
+ void sigCloseAction();
+
+ /** Notifies listeners about we are hovered. */
+ void sigHoverEnter();
+ /** Notifies listeners about we are unhovered. */
+ void sigHoverLeave();
+
+ /** Notifies listeners about we stole window activation. */
+ void sigNotifyAboutWindowActivationStolen();
+
+ /** Notifies listeners about auto-hide toggled.
+ * @param fEnabled Brings whether auto-hide is enabled. */
+ void sigAutoHideToggled(bool fEnabled);
+
+public:
+
+ /** Proposes default set of window flags for particular platform. */
+ static Qt::WindowFlags defaultWindowFlags(GeometryType geometryType);
+
+ /** Constructor, passes @a pParent to the QWidget constructor.
+ * @param geometryType determines the geometry type,
+ * @param alignment determines the alignment type,
+ * @param fAutoHide determines whether we should auto-hide.
+ * @param iWindowIndex determines the parent window index. */
+ UIMiniToolBar(QWidget *pParent,
+ GeometryType geometryType,
+ Qt::Alignment alignment,
+ bool fAutoHide = true,
+ int iWindowIndex = -1);
+ /** Destructor. */
+ ~UIMiniToolBar();
+
+ /** Defines @a alignment. */
+ void setAlignment(Qt::Alignment alignment);
+
+ /** Returns whether internal widget do auto-hide. */
+ bool autoHide() const { return m_fAutoHide; }
+ /** Defines whether internal widget do @a fAutoHide.
+ * @param fPropagateToChild determines should we propagate defined
+ * option value to internal widget. */
+ void setAutoHide(bool fAutoHide, bool fPropagateToChild = true);
+
+ /** Defines @a strText for internal widget. */
+ void setText(const QString &strText);
+
+ /** Adds @a menus to internal widget. */
+ void addMenus(const QList<QMenu*> &menus);
+
+ /** Adjusts geometry. */
+ void adjustGeometry();
+
+protected:
+
+ /** Filters @a pEvent if <i>this</i> object has been
+ * installed as an event-filter for the @a pWatched. */
+ virtual bool eventFilter(QObject *pWatched, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Resize @a pEvent handler. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Mouse enter @a pEvent handler. */
+#ifdef VBOX_IS_QT6_OR_LATER /* QWidget::enterEvent uses QEnterEvent since qt6 */
+ virtual void enterEvent(QEnterEvent *pEvent) RT_OVERRIDE;
+#else
+ virtual void enterEvent(QEvent *pEvent) RT_OVERRIDE;
+#endif
+ /** Mouse leave @a pEvent handler. */
+ virtual void leaveEvent(QEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles internal widget resize event. */
+ void sltHandleToolbarResize();
+
+ /** Handles internal widget auto-hide toggling. */
+ void sltAutoHideToggled();
+
+ /** Handles hovering. */
+ void sltHoverEnter();
+ /** Handles unhovering. */
+ void sltHoverLeave();
+
+ /** Check whether we still have window activation token. */
+ void sltCheckWindowActivationSanity();
+
+ /** Hides window. */
+ void sltHide();
+ /** Shows and adjusts window according to parent. */
+ void sltShow();
+ /** Adjusts window according to parent. */
+ void sltAdjust();
+ /** Adjusts window transience according to parent. */
+ void sltAdjustTransience();
+
+private:
+
+ /** Prepare routine. */
+ void prepare();
+ /** Cleanup routine. */
+ void cleanup();
+
+ /** Simulates auto-hide animation. */
+ void simulateToolbarAutoHiding();
+
+ /** Defines internal widget @a position. */
+ void setToolbarPosition(QPoint position);
+ /** Returns internal widget position. */
+ QPoint toolbarPosition() const;
+ /** Returns internal widget position when it's hidden. */
+ QPoint hiddenToolbarPosition() const { return m_hiddenToolbarPosition; }
+ /** Returns internal widget position when it's shown. */
+ QPoint shownToolbarPosition() const { return m_shownToolbarPosition; }
+
+ /** Returns whether the parent is currently minimized. */
+ bool isParentMinimized() const;
+
+ /** Holds the parent reference. */
+ QWidget *m_pParent;
+
+ /** Holds the geometry type. */
+ const GeometryType m_geometryType;
+ /** Holds the alignment type. */
+ Qt::Alignment m_alignment;
+ /** Holds whether we should auto-hide. */
+ bool m_fAutoHide;
+ /** Holds the parent window index. */
+ int m_iWindowIndex;
+
+ /** Holds the area. */
+ QWidget *m_pArea;
+ /** Holds the internal widget. */
+ UIMiniToolBarPrivate *m_pToolbar;
+
+ /** Holds whether we are hovered. */
+ bool m_fHovered;
+ /** Holds the hover timer. */
+ QTimer *m_pHoverEnterTimer;
+ /** Holds the unhover timer. */
+ QTimer *m_pHoverLeaveTimer;
+ /** Holds the internal widget position when it's hidden. */
+ QPoint m_hiddenToolbarPosition;
+ /** Holds the internal widget position when it's shown. */
+ QPoint m_shownToolbarPosition;
+ /** Holds the animation framework object. */
+ UIAnimation *m_pAnimation;
+
+#ifdef VBOX_WS_X11
+ /** X11: Holds whether the parent is currently minimized.
+ * Used to restore the full-screen/maximized state
+ * when the parent restored again. */
+ bool m_fIsParentMinimized;
+#endif
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIMiniToolBar_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupBox.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupBox.cpp
new file mode 100644
index 00000000..85e4653d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupBox.cpp
@@ -0,0 +1,476 @@
+/* $Id: UIPopupBox.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPopupBox/UIPopupBoxGroup classes implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QLabel>
+#include <QPaintEvent>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIPopupBox.h"
+#ifdef VBOX_WS_MAC
+# include "UIImageTools.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Class UIPopupBox implementation. *
+*********************************************************************************************************************************/
+
+UIPopupBox::UIPopupBox(QWidget *pParent)
+ : QWidget(pParent)
+ , m_pTitleIcon(0)
+ , m_pWarningIcon(0)
+ , m_pTitleLabel(0)
+ , m_fLinkEnabled(false)
+ , m_fOpened(true)
+ , m_fHovered(false)
+ , m_pContentWidget(0)
+ , m_pLabelPath(0)
+ , m_iArrowWidth(9)
+{
+ /* Configure self: */
+ installEventFilter(this);
+
+ /* Configure painter-path: */
+ m_arrowPath.lineTo(m_iArrowWidth / 2.0, m_iArrowWidth / 2.0);
+ m_arrowPath.lineTo(m_iArrowWidth, 0);
+
+ /* Create main-layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Create title-layout: */
+ QHBoxLayout *pTitleLayout = new QHBoxLayout;
+ if (pTitleLayout)
+ {
+ /* Create title-icon label. */
+ m_pTitleIcon = new QLabel;
+ if (m_pTitleIcon)
+ {
+ /* Configure label: */
+ m_pTitleIcon->installEventFilter(this);
+
+ /* Add into layout: */
+ pTitleLayout->addWidget(m_pTitleIcon);
+ }
+ /* Create warning-icon label. */
+ m_pWarningIcon = new QLabel;
+ if (m_pWarningIcon)
+ {
+ /* Configure label: */
+ m_pWarningIcon->setHidden(true);
+ m_pWarningIcon->installEventFilter(this);
+
+ /* Add into layout: */
+ pTitleLayout->addWidget(m_pWarningIcon);
+ }
+ /* Create title-text label. */
+ m_pTitleLabel = new QLabel;
+ if (m_pTitleLabel)
+ {
+ /* Configure label: */
+ m_pTitleLabel->installEventFilter(this);
+ connect(m_pTitleLabel, SIGNAL(linkActivated(const QString)),
+ this, SIGNAL(sigTitleClicked(const QString)));
+
+ /* Add into layout: */
+ pTitleLayout->addWidget(m_pTitleLabel, Qt::AlignLeft);
+ }
+
+ /* Add into layout: */
+ pMainLayout->addLayout(pTitleLayout);
+ }
+ }
+
+}
+
+UIPopupBox::~UIPopupBox()
+{
+ /* Delete label painter-path if any: */
+ if (m_pLabelPath)
+ delete m_pLabelPath;
+}
+
+void UIPopupBox::setTitleIcon(const QIcon &icon)
+{
+ /* Remember new title-icon: */
+ m_titleIcon = icon;
+ /* Update title-icon: */
+ updateTitleIcon();
+ /* Recalculate title-size: */
+ recalc();
+}
+
+QIcon UIPopupBox::titleIcon() const
+{
+ return m_titleIcon;
+}
+
+void UIPopupBox::setWarningIcon(const QIcon &icon)
+{
+ /* Remember new warning-icon: */
+ m_warningIcon = icon;
+ /* Update warning-icon: */
+ updateWarningIcon();
+ /* Recalculate title-size: */
+ recalc();
+}
+
+QIcon UIPopupBox::warningIcon() const
+{
+ return m_warningIcon;
+}
+
+void UIPopupBox::setTitle(const QString &strTitle)
+{
+ /* Remember new title: */
+ m_strTitle = strTitle;
+ /* Update title: */
+ updateTitle();
+ /* Recalculate title-size: */
+ recalc();
+}
+
+QString UIPopupBox::title() const
+{
+ return m_strTitle;
+}
+
+void UIPopupBox::setTitleLink(const QString &strLink)
+{
+ /* Remember new title-link: */
+ m_strLink = strLink;
+ /* Update title: */
+ updateTitle();
+}
+
+QString UIPopupBox::titleLink() const
+{
+ return m_strLink;
+}
+
+void UIPopupBox::setTitleLinkEnabled(bool fEnabled)
+{
+ /* Remember new title-link availability flag: */
+ m_fLinkEnabled = fEnabled;
+ /* Update title: */
+ updateTitle();
+}
+
+bool UIPopupBox::isTitleLinkEnabled() const
+{
+ return m_fLinkEnabled;
+}
+
+void UIPopupBox::setContentWidget(QWidget *pWidget)
+{
+ /* Cleanup old content-widget if any: */
+ if (m_pContentWidget)
+ {
+ m_pContentWidget->removeEventFilter(this);
+ layout()->removeWidget(m_pContentWidget);
+ }
+ /* Prepare new content-wodget: */
+ m_pContentWidget = pWidget;
+ layout()->addWidget(m_pContentWidget);
+ m_pContentWidget->installEventFilter(this);
+ recalc();
+}
+
+QWidget* UIPopupBox::contentWidget() const
+{
+ return m_pContentWidget;
+}
+
+void UIPopupBox::setOpen(bool fOpened)
+{
+ /* Check if we should toggle popup-box: */
+ if (m_fOpened == fOpened)
+ return;
+
+ /* Store new value: */
+ m_fOpened = fOpened;
+
+ /* Update content-widget if present or this itself: */
+ if (m_pContentWidget)
+ m_pContentWidget->setVisible(m_fOpened);
+ else
+ update();
+
+ /* Notify listeners about content-widget visibility: */
+ if (m_pContentWidget && m_pContentWidget->isVisible())
+ emit sigUpdateContentWidget();
+}
+
+void UIPopupBox::toggleOpen()
+{
+ /* Switch 'opened' state: */
+ setOpen(!m_fOpened);
+
+ /* Notify listeners about toggling: */
+ emit sigToggled(m_fOpened);
+}
+
+bool UIPopupBox::isOpen() const
+{
+ return m_fOpened;
+}
+
+bool UIPopupBox::event(QEvent *pEvent)
+{
+ /* Handle know event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Show:
+ case QEvent::ScreenChangeInternal:
+ {
+ /* Update pixmaps: */
+ updateTitleIcon();
+ updateWarningIcon();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QWidget::event(pEvent);
+}
+
+bool UIPopupBox::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Handle all mouse-event to update hover: */
+ QEvent::Type type = pEvent->type();
+ if (type == QEvent::Enter ||
+ type == QEvent::Leave ||
+ type == QEvent::MouseMove ||
+ type == QEvent::Wheel)
+ updateHover();
+ /* Call to base-class: */
+ return QWidget::eventFilter(pObject, pEvent);
+}
+
+void UIPopupBox::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Recalculate title-size: */
+ recalc();
+ /* Call to base-class: */
+ QWidget::resizeEvent(pEvent);
+}
+
+void UIPopupBox::paintEvent(QPaintEvent *pEvent)
+{
+ /* Create painter: */
+ QPainter painter(this);
+ painter.setClipRect(pEvent->rect());
+
+ QPalette pal = QApplication::palette();
+ painter.setClipPath(*m_pLabelPath);
+ QColor base = pal.color(QPalette::Active, QPalette::Window);
+ QRect rect = QRect(QPoint(0, 0), size()).adjusted(0, 0, -1, -1);
+ /* Base background */
+ painter.fillRect(QRect(QPoint(0, 0), size()), pal.brush(QPalette::Active, QPalette::Base));
+ /* Top header background */
+ const int iMaxHeightHint = qMax(m_pTitleLabel->sizeHint().height(),
+ m_pTitleIcon->sizeHint().height());
+ QLinearGradient lg(rect.x(), rect.y(), rect.x(), rect.y() + 2 * 5 + iMaxHeightHint);
+ lg.setColorAt(0, base.darker(95));
+ lg.setColorAt(1, base.darker(110));
+ int theight = rect.height();
+ if (m_fOpened)
+ theight = 2 * 5 + iMaxHeightHint;
+ painter.fillRect(QRect(rect.x(), rect.y(), rect.width(), theight), lg);
+ /* Outer round rectangle line */
+ painter.setClipping(false);
+ painter.strokePath(*m_pLabelPath, base.darker(110));
+ /* Arrow */
+ if (m_fHovered)
+ {
+ painter.setBrush(base.darker(106));
+ painter.setPen(QPen(base.darker(128), 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
+ QSizeF s = m_arrowPath.boundingRect().size();
+ if (m_fOpened)
+ {
+ painter.translate(rect.x() + rect.width() - s.width() - 10, rect.y() + theight / 2 + s.height() / 2);
+ /* Flip */
+ painter.scale(1, -1);
+ }
+ else
+ painter.translate(rect.x() + rect.width() - s.width() - 10, rect.y() + theight / 2 - s.height() / 2 + 1);
+
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.drawPath(m_arrowPath);
+ }
+}
+
+void UIPopupBox::mouseDoubleClickEvent(QMouseEvent *)
+{
+ /* Toggle popup-box: */
+ toggleOpen();
+}
+
+void UIPopupBox::updateTitleIcon()
+{
+ /* Assign title-icon: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pTitleIcon->setPixmap(m_titleIcon.pixmap(window()->windowHandle(), QSize(iIconMetric, iIconMetric)));
+}
+
+void UIPopupBox::updateWarningIcon()
+{
+ /* Hide warning-icon if its null: */
+ m_pWarningIcon->setHidden(m_warningIcon.isNull());
+
+ /* Assign warning-icon: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pWarningIcon->setPixmap(m_warningIcon.pixmap(window()->windowHandle(), QSize(iIconMetric, iIconMetric)));
+}
+
+void UIPopupBox::updateTitle()
+{
+ /* If title-link is disabled or not set: */
+ if (!m_fLinkEnabled || m_strLink.isEmpty())
+ {
+ /* We should just set simple text title: */
+ m_pTitleLabel->setText(QString("<b>%1</b>").arg(m_strTitle));
+ }
+ /* If title-link is enabled and set: */
+ else if (m_fLinkEnabled && !m_strLink.isEmpty())
+ {
+ /* We should set html reference title: */
+ QPalette pal = m_pTitleLabel->palette();
+ m_pTitleLabel->setText(QString("<b><a style=\"text-decoration: none; color: %1\" href=\"%2\">%3</a></b>")
+ .arg(m_fHovered ? pal.color(QPalette::Link).name() : pal.color(QPalette::WindowText).name())
+ .arg(m_strLink)
+ .arg(m_strTitle));
+ }
+}
+
+void UIPopupBox::updateHover()
+{
+ /* Calculate new header-hover state: */
+ bool fNewHovered = m_fHovered;
+ if (m_pLabelPath && m_pLabelPath->contains(mapFromGlobal(QCursor::pos())))
+ fNewHovered = true;
+ else
+ fNewHovered = false;
+
+ /* Check if we should toggle hover: */
+ if (m_fHovered == fNewHovered)
+ return;
+
+ /* If header-hover state switched from disabled to enabled: */
+ if (!m_fHovered && fNewHovered)
+ /* Notify listeners: */
+ emit sigGotHover();
+
+ /* Toggle hover: */
+ toggleHover(fNewHovered);
+}
+
+void UIPopupBox::revokeHover()
+{
+ /* Check if we should toggle hover: */
+ if (m_fHovered == false)
+ return;
+
+ /* Toggle hover off: */
+ toggleHover(false);
+}
+
+void UIPopupBox::toggleHover(bool fHeaderHover)
+{
+ /* Remember header-hover state: */
+ m_fHovered = fHeaderHover;
+
+ /* Update title: */
+ updateTitle();
+
+ /* Call for update: */
+ update();
+}
+
+void UIPopupBox::recalc()
+{
+ if (m_pLabelPath)
+ delete m_pLabelPath;
+ QRect rect = QRect(QPoint(0, 0), size()).adjusted(0, 0, -1, -1);
+ int d = 18; // 22
+ m_pLabelPath = new QPainterPath(QPointF(rect.x() + rect.width() - d, rect.y()));
+ m_pLabelPath->arcTo(QRectF(rect.x(), rect.y(), d, d), 90, 90);
+ m_pLabelPath->arcTo(QRectF(rect.x(), rect.y() + rect.height() - d, d, d), 180, 90);
+ m_pLabelPath->arcTo(QRectF(rect.x() + rect.width() - d, rect.y() + rect.height() - d, d, d), 270, 90);
+ m_pLabelPath->arcTo(QRectF(rect.x() + rect.width() - d, rect.y(), d, d), 0, 90);
+ m_pLabelPath->closeSubpath();
+ update();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIPopupBoxGroup implementation. *
+*********************************************************************************************************************************/
+
+UIPopupBoxGroup::UIPopupBoxGroup(QObject *pParent)
+ : QObject(pParent)
+{
+}
+
+UIPopupBoxGroup::~UIPopupBoxGroup()
+{
+ /* Clear the list early: */
+ m_list.clear();
+}
+
+void UIPopupBoxGroup::addPopupBox(UIPopupBox *pPopupBox)
+{
+ /* Add popup-box into list: */
+ m_list << pPopupBox;
+
+ /* Connect got-hover signal of the popup-box to hover-change slot of the popup-box group: */
+ connect(pPopupBox, &UIPopupBox::sigGotHover, this, &UIPopupBoxGroup::sltHoverChanged);
+}
+
+void UIPopupBoxGroup::sltHoverChanged()
+{
+ /* Fetch the sender: */
+ UIPopupBox *pPopupBox = qobject_cast<UIPopupBox*>(sender());
+
+ /* Check if sender popup-box exists/registered: */
+ if (!pPopupBox || !m_list.contains(pPopupBox))
+ return;
+
+ /* Filter the sender: */
+ QList<UIPopupBox*> list(m_list);
+ list.removeOne(pPopupBox);
+
+ /* Notify all other popup-boxes: */
+ for (int i = 0; i < list.size(); ++i)
+ list[i]->revokeHover();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupBox.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupBox.h
new file mode 100644
index 00000000..a2da01ce
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupBox.h
@@ -0,0 +1,225 @@
+/* $Id: UIPopupBox.h $ */
+/** @file
+ * VBox Qt GUI - UIPopupBox/UIPopupBoxGroup classes declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIPopupBox_h
+#define FEQT_INCLUDED_SRC_widgets_UIPopupBox_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QWidget>
+#include <QPainter>
+#include <QPainterPath>
+#include <QPainterPathStroker>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QEvent;
+class QIcon;
+class QLabel;
+class QMouseEvent;
+class QObject;
+class QPainterPath;
+class QPaintEvent;
+class QResizeEvent;
+class QString;
+class QWidget;
+
+
+/** QWidget extension,
+ * wrapping content-widget with nice collapsable frame. */
+class SHARED_LIBRARY_STUFF UIPopupBox : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about title with @a strLink was clicked. */
+ void sigTitleClicked(const QString &strLink);
+
+ /** Notifies box was toggled and currently @a fOpened. */
+ void sigToggled(bool fOpened);
+
+ /** Asks to update contents widget. */
+ void sigUpdateContentWidget();
+
+ /** Notify about box is hovered. */
+ void sigGotHover();
+
+public:
+
+ /** Construct popup-box passing @a pParent to the base-class. */
+ UIPopupBox(QWidget *pParent);
+ /** Destruct popup-box. */
+ virtual ~UIPopupBox() RT_OVERRIDE;
+
+ /** Defines title @a icon. */
+ void setTitleIcon(const QIcon &icon);
+ /** Returns title icon. */
+ QIcon titleIcon() const;
+
+ /** Defines warning @a icon. */
+ void setWarningIcon(const QIcon &icon);
+ /** Returns warnings icon. */
+ QIcon warningIcon() const;
+
+ /** Defines @a strTitle. */
+ void setTitle(const QString &strTitle);
+ /** Returns title. */
+ QString title() const;
+
+ /** Defines title @a strLink. */
+ void setTitleLink(const QString &strLink);
+ /** Returns title link. */
+ QString titleLink() const;
+ /** Defines whether title link is @a fEnabled. */
+ void setTitleLinkEnabled(bool fEnabled);
+ /** Returns whether title link is enabled. */
+ bool isTitleLinkEnabled() const;
+
+ /** Defines content @a pWidget. */
+ void setContentWidget(QWidget *pWidget);
+ /** Returns content widget. */
+ QWidget *contentWidget() const;
+
+ /** Defines whether box is @a fOpened. */
+ void setOpen(bool fOpened);
+ /** Toggles current opened state. */
+ void toggleOpen();
+ /** Returns whether box is opened. */
+ bool isOpen() const;
+
+ /** Calls for content iwdget update. */
+ void callForUpdateContentWidget() { emit sigUpdateContentWidget(); }
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Pre-handles standard Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse double-click @a pEvent. */
+ virtual void mouseDoubleClickEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Updates title icon. */
+ void updateTitleIcon();
+ /** Updates warning icon. */
+ void updateWarningIcon();
+ /** Updates title. */
+ void updateTitle();
+ /** Updates hovered state. */
+ void updateHover();
+ /** Revokes hovered state. */
+ void revokeHover();
+ /** Toggles hovered state to @a fHeaderHover. */
+ void toggleHover(bool fHeaderHover);
+ /** Recalculates geometry. */
+ void recalc();
+
+ /** Holds the title icon label. */
+ QLabel *m_pTitleIcon;
+ /** Holds the warning icon label. */
+ QLabel *m_pWarningIcon;
+ /** Holds the title label. */
+ QLabel *m_pTitleLabel;
+
+ /** Holds the title icon. */
+ QIcon m_titleIcon;
+ /** Holds the warning icon. */
+ QIcon m_warningIcon;
+ /** Holds the title text. */
+ QString m_strTitle;
+ /** Holds the link icon. */
+ QString m_strLink;
+
+ /** Holds whether the link is enabled. */
+ bool m_fLinkEnabled : 1;
+ /** Holds whether box is opened. */
+ bool m_fOpened : 1;
+ /** Holds whether header is hovered. */
+ bool m_fHovered : 1;
+
+ /** Holds the content widget. */
+ QWidget *m_pContentWidget;
+
+ /** Holds the label painter path. */
+ QPainterPath *m_pLabelPath;
+
+ /** Holds the arrow width. */
+ const int m_iArrowWidth;
+ /** Holds the arrow painter-path. */
+ QPainterPath m_arrowPath;
+
+ /** Allow popup-box group to access private API. */
+ friend class UIPopupBoxGroup;
+};
+
+
+/** QObject extension,
+ * provides a container to organize groups of popup-boxes. */
+class SHARED_LIBRARY_STUFF UIPopupBoxGroup : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Construct popup-box passing @a pParent to the base-class. */
+ UIPopupBoxGroup(QObject *pParent);
+ /** Destruct popup-box. */
+ virtual ~UIPopupBoxGroup() RT_OVERRIDE;
+
+ /** Adds @a pPopupBox into group. */
+ void addPopupBox(UIPopupBox *pPopupBox);
+
+private slots:
+
+ /** Handles group hovering. */
+ void sltHoverChanged();
+
+private:
+
+ /** Holds the list of popup-boxes. */
+ QList<UIPopupBox*> m_list;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIPopupBox_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPane.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPane.cpp
new file mode 100644
index 00000000..12c306d8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPane.cpp
@@ -0,0 +1,559 @@
+/* $Id: UIPopupPane.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPopupPane class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QPainter>
+#include <QTextEdit>
+
+/* GUI includes: */
+#include "UIPopupPane.h"
+#include "UIPopupPaneMessage.h"
+#include "UIPopupPaneDetails.h"
+#include "UIPopupPaneButtonPane.h"
+#include "UIAnimationFramework.h"
+#include "QIMessageBox.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+UIPopupPane::UIPopupPane(QWidget *pParent,
+ const QString &strMessage, const QString &strDetails,
+ const QMap<int, QString> &buttonDescriptions)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_fPolished(false)
+ , m_iLayoutMargin(10), m_iLayoutSpacing(5)
+ , m_strMessage(strMessage), m_strDetails(strDetails)
+ , m_buttonDescriptions(buttonDescriptions)
+ , m_fShown(false)
+ , m_pShowAnimation(0)
+ , m_fCanLooseFocus(!m_buttonDescriptions.isEmpty())
+ , m_fFocused(!m_fCanLooseFocus)
+ , m_fHovered(m_fFocused)
+ , m_iDefaultOpacity(180)
+ , m_iHoveredOpacity(250)
+ , m_iOpacity(m_fHovered ? m_iHoveredOpacity : m_iDefaultOpacity)
+ , m_pMessagePane(0), m_pDetailsPane(0), m_pButtonPane(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIPopupPane::recall()
+{
+ /* Close popup-pane with *escape* button: */
+ done(m_pButtonPane->escapeButton());
+}
+
+void UIPopupPane::setMessage(const QString &strMessage)
+{
+ /* Make sure the message has changed: */
+ if (m_strMessage == strMessage)
+ return;
+
+ /* Fetch new message: */
+ m_strMessage = strMessage;
+ m_pMessagePane->setText(m_strMessage);
+}
+
+void UIPopupPane::setDetails(const QString &strDetails)
+{
+ /* Make sure the details has changed: */
+ if (m_strDetails == strDetails)
+ return;
+
+ /* Fetch new details: */
+ m_strDetails = strDetails;
+ m_pDetailsPane->setText(prepareDetailsText());
+}
+
+void UIPopupPane::setMinimumSizeHint(const QSize &minimumSizeHint)
+{
+ /* Make sure the size-hint has changed: */
+ if (m_minimumSizeHint == minimumSizeHint)
+ return;
+
+ /* Fetch new size-hint: */
+ m_minimumSizeHint = minimumSizeHint;
+
+ /* Notify parent popup-stack: */
+ emit sigSizeHintChanged();
+}
+
+void UIPopupPane::layoutContent()
+{
+ /* Variables: */
+ const int iWidth = width();
+ const int iHeight = height();
+ const QSize buttonPaneMinimumSizeHint = m_pButtonPane->minimumSizeHint();
+ const int iButtonPaneMinimumWidth = buttonPaneMinimumSizeHint.width();
+ const int iButtonPaneMinimumHeight = buttonPaneMinimumSizeHint.height();
+ const int iTextPaneWidth = iWidth - 2 * m_iLayoutMargin - m_iLayoutSpacing - iButtonPaneMinimumWidth;
+ const int iTextPaneHeight = m_pMessagePane->minimumSizeHint().height();
+ const int iMaximumHeight = qMax(iTextPaneHeight, iButtonPaneMinimumHeight);
+ const int iMinimumHeight = qMin(iTextPaneHeight, iButtonPaneMinimumHeight);
+ const int iHeightShift = (iMaximumHeight - iMinimumHeight) / 2;
+ const bool fTextPaneShifted = iTextPaneHeight < iButtonPaneMinimumHeight;
+ const int iTextPaneYOffset = fTextPaneShifted ? m_iLayoutMargin + iHeightShift : m_iLayoutMargin;
+
+ /* Message-pane: */
+ m_pMessagePane->move(m_iLayoutMargin, iTextPaneYOffset);
+ m_pMessagePane->resize(iTextPaneWidth, iTextPaneHeight);
+ m_pMessagePane->layoutContent();
+
+ /* Button-pane: */
+ m_pButtonPane->move(m_iLayoutMargin + iTextPaneWidth + m_iLayoutSpacing,
+ m_iLayoutMargin);
+ m_pButtonPane->resize(iButtonPaneMinimumWidth,
+ iHeight - m_iLayoutSpacing);
+
+ /* Details-pane: */
+ if (m_pDetailsPane->isVisible())
+ {
+ m_pDetailsPane->move(m_iLayoutMargin,
+ iTextPaneYOffset + iTextPaneHeight + m_iLayoutSpacing);
+ m_pDetailsPane->resize(iTextPaneWidth + iButtonPaneMinimumWidth,
+ m_pDetailsPane->minimumSizeHint().height());
+ m_pDetailsPane->layoutContent();
+ }
+}
+
+void UIPopupPane::sltMarkAsShown()
+{
+ /* Mark popup-pane as 'shown': */
+ m_fShown = true;
+}
+
+void UIPopupPane::sltHandleProposalForSize(QSize newSize)
+{
+ /* Prepare the width: */
+ int iWidth = newSize.width();
+
+ /* Subtract layout margins: */
+ iWidth -= 2 * m_iLayoutMargin;
+ /* Subtract layout spacing: */
+ iWidth -= m_iLayoutSpacing;
+ /* Subtract button-pane width: */
+ iWidth -= m_pButtonPane->minimumSizeHint().width();
+
+ /* Propose resulting width to the panes: */
+ emit sigProposePaneWidth(iWidth);
+
+ /* Prepare the height: */
+ int iHeight = newSize.height();
+ /* Determine maximum height of the message-pane / button-pane: */
+ int iExtraHeight = qMax(m_pMessagePane->expandedSizeHint().height(),
+ m_pButtonPane->minimumSizeHint().height());
+
+ /* Subtract height of the message pane: */
+ iHeight -= iExtraHeight;
+ /* Subtract layout margins: */
+ iHeight -= 2 * m_iLayoutMargin;
+ /* Subtract layout spacing: */
+ iHeight -= m_iLayoutSpacing;
+
+ /* Propose resulting height to details-pane: */
+ emit sigProposeDetailsPaneHeight(iHeight);
+}
+
+void UIPopupPane::sltUpdateSizeHint()
+{
+ /* Calculate minimum width-hint: */
+ int iMinimumWidthHint = 0;
+ {
+ /* Take into account layout: */
+ iMinimumWidthHint += 2 * m_iLayoutMargin;
+ {
+ /* Take into account widgets: */
+ iMinimumWidthHint += m_pMessagePane->minimumSizeHint().width();
+ iMinimumWidthHint += m_iLayoutSpacing;
+ iMinimumWidthHint += m_pButtonPane->minimumSizeHint().width();
+ }
+ }
+
+ /* Calculate minimum height-hint: */
+ int iMinimumHeightHint = 0;
+ {
+ /* Take into account layout: */
+ iMinimumHeightHint += 2 * m_iLayoutMargin;
+ iMinimumHeightHint += m_iLayoutSpacing;
+ {
+ /* Take into account widgets: */
+ const int iTextPaneHeight = m_pMessagePane->minimumSizeHint().height();
+ const int iButtonBoxHeight = m_pButtonPane->minimumSizeHint().height();
+ iMinimumHeightHint += qMax(iTextPaneHeight, iButtonBoxHeight);
+ /* Add the height of details-pane only if it is visible: */
+ if (m_pDetailsPane->isVisible())
+ iMinimumHeightHint += m_pDetailsPane->minimumSizeHint().height();
+ }
+ }
+
+ /* Compose minimum size-hints: */
+ m_hiddenSizeHint = QSize(iMinimumWidthHint, 1);
+ m_shownSizeHint = QSize(iMinimumWidthHint, iMinimumHeightHint);
+ m_minimumSizeHint = m_fShown ? m_shownSizeHint : m_hiddenSizeHint;
+
+ /* Update 'show/hide' animation: */
+ if (m_pShowAnimation)
+ m_pShowAnimation->update();
+
+ /* Notify parent popup-stack: */
+ emit sigSizeHintChanged();
+}
+
+void UIPopupPane::sltButtonClicked(int iButtonID)
+{
+ /* Complete popup with corresponding code: */
+ done(iButtonID);
+}
+
+void UIPopupPane::prepare()
+{
+ /* Prepare this: */
+ installEventFilter(this);
+ /* Prepare background: */
+ prepareBackground();
+ /* Prepare content: */
+ prepareContent();
+ /* Prepare animation: */
+ prepareAnimation();
+
+ /* Update size-hint: */
+ sltUpdateSizeHint();
+}
+
+void UIPopupPane::prepareBackground()
+{
+ /* Prepare palette: */
+ QPalette pal = QApplication::palette();
+ pal.setColor(QPalette::Window, QApplication::palette().color(QPalette::Window));
+ setPalette(pal);
+}
+
+void UIPopupPane::prepareContent()
+{
+ /* Create message-pane: */
+ m_pMessagePane = new UIPopupPaneMessage(this, m_strMessage, m_fFocused);
+ {
+ /* Configure message-pane: */
+ connect(this, &UIPopupPane::sigProposePaneWidth, m_pMessagePane, &UIPopupPaneMessage::sltHandleProposalForWidth);
+ connect(m_pMessagePane, &UIPopupPaneMessage::sigSizeHintChanged, this, &UIPopupPane::sltUpdateSizeHint);
+ m_pMessagePane->installEventFilter(this);
+ }
+
+ /* Create button-box: */
+ m_pButtonPane = new UIPopupPaneButtonPane(this);
+ {
+ /* Configure button-box: */
+ connect(m_pButtonPane, &UIPopupPaneButtonPane::sigButtonClicked, this, &UIPopupPane::sltButtonClicked);
+ m_pButtonPane->installEventFilter(this);
+ m_pButtonPane->setButtons(m_buttonDescriptions);
+ }
+
+ /* Create details-pane: */
+ m_pDetailsPane = new UIPopupPaneDetails(this, prepareDetailsText(), m_fFocused);
+ {
+ /* Configure details-pane: */
+ connect(this, &UIPopupPane::sigProposePaneWidth, m_pDetailsPane, &UIPopupPaneDetails::sltHandleProposalForWidth);
+ connect(this, &UIPopupPane::sigProposeDetailsPaneHeight, m_pDetailsPane, &UIPopupPaneDetails::sltHandleProposalForHeight);
+ connect(m_pDetailsPane, &UIPopupPaneDetails::sigSizeHintChanged, this, &UIPopupPane::sltUpdateSizeHint);
+ m_pDetailsPane->installEventFilter(this);
+ }
+
+ /* Prepare focus rules: */
+ setFocusPolicy(Qt::StrongFocus);
+ m_pMessagePane->setFocusPolicy(Qt::StrongFocus);
+ m_pButtonPane->setFocusPolicy(Qt::StrongFocus);
+ m_pDetailsPane->setFocusPolicy(Qt::StrongFocus);
+ setFocusProxy(m_pButtonPane);
+ m_pMessagePane->setFocusProxy(m_pButtonPane);
+ m_pDetailsPane->setFocusProxy(m_pButtonPane);
+
+ /* Translate UI finally: */
+ retranslateUi();
+}
+
+void UIPopupPane::prepareAnimation()
+{
+ /* Install 'show' animation for 'minimumSizeHint' property: */
+ connect(this, SIGNAL(sigToShow()), this, SIGNAL(sigShow()), Qt::QueuedConnection);
+ m_pShowAnimation = UIAnimation::installPropertyAnimation(this, "minimumSizeHint", "hiddenSizeHint", "shownSizeHint",
+ SIGNAL(sigShow()), SIGNAL(sigHide()));
+ connect(m_pShowAnimation, &UIAnimation::sigStateEnteredFinal, this, &UIPopupPane::sltMarkAsShown);
+
+ /* Install 'hover' animation for 'opacity' property: */
+ UIAnimation::installPropertyAnimation(this, "opacity", "defaultOpacity", "hoveredOpacity",
+ SIGNAL(sigHoverEnter()), SIGNAL(sigHoverLeave()), m_fHovered);
+}
+
+void UIPopupPane::retranslateUi()
+{
+ /* Translate tool-tips: */
+ retranslateToolTips();
+}
+
+void UIPopupPane::retranslateToolTips()
+{
+ /* Translate pane & message-pane tool-tips: */
+ if (m_fFocused)
+ {
+ setToolTip(QString());
+ m_pMessagePane->setToolTip(QString());
+ }
+ else
+ {
+ setToolTip(QApplication::translate("UIPopupCenter", "Click for full details"));
+ m_pMessagePane->setToolTip(QApplication::translate("UIPopupCenter", "Click for full details"));
+ }
+}
+
+bool UIPopupPane::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Handle events for allowed widgets only: */
+ if ( pObject != this
+ && pObject != m_pMessagePane
+ && pObject != m_pButtonPane
+ && pObject != m_pDetailsPane)
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
+
+ /* Depending on event-type: */
+ switch (pEvent->type())
+ {
+ /* Something is hovered: */
+ case QEvent::HoverEnter:
+ case QEvent::Enter:
+ {
+ /* Hover pane if not yet hovered: */
+ if (!m_fHovered)
+ {
+ m_fHovered = true;
+ emit sigHoverEnter();
+ }
+ break;
+ }
+ /* Nothing is hovered: */
+ case QEvent::Leave:
+ {
+ /* Unhover pane if hovered but not focused: */
+ if (pObject == this && m_fHovered && !m_fFocused)
+ {
+ m_fHovered = false;
+ emit sigHoverLeave();
+ }
+ break;
+ }
+ /* Pane is clicked with mouse: */
+ case QEvent::MouseButtonPress:
+ {
+ /* Focus pane if not focused: */
+ if (!m_fFocused)
+ {
+ m_fFocused = true;
+ emit sigFocusEnter();
+ /* Hover pane if not hovered: */
+ if (!m_fHovered)
+ {
+ m_fHovered = true;
+ emit sigHoverEnter();
+ }
+ /* Translate tool-tips: */
+ retranslateToolTips();
+ }
+ break;
+ }
+ /* Pane is unfocused: */
+ case QEvent::FocusOut:
+ {
+ /* Unhocus pane if focused: */
+ if (m_fCanLooseFocus && m_fFocused)
+ {
+ m_fFocused = false;
+ emit sigFocusLeave();
+ /* Unhover pane if hovered: */
+ if (m_fHovered)
+ {
+ m_fHovered = false;
+ emit sigHoverLeave();
+ }
+ /* Translate tool-tips: */
+ retranslateToolTips();
+ }
+ break;
+ }
+ /* Default case: */
+ default: break;
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
+}
+
+void UIPopupPane::showEvent(QShowEvent *pEvent)
+{
+ /* Call to base-class: */
+ QWidget::showEvent(pEvent);
+
+ /* Polish border: */
+ if (m_fPolished)
+ return;
+ m_fPolished = true;
+
+ /* Call to polish event: */
+ polishEvent(pEvent);
+}
+
+void UIPopupPane::polishEvent(QShowEvent *)
+{
+ /* Focus if marked as 'focused': */
+ if (m_fFocused)
+ setFocus();
+
+ /* Emit signal to start *show* animation: */
+ emit sigToShow();
+}
+
+void UIPopupPane::paintEvent(QPaintEvent *)
+{
+ /* Compose painting rectangle,
+ * Shifts are required for the antialiasing support: */
+ const QRect rect(1, 1, width() - 2, height() - 2);
+
+ /* Create painter: */
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ /* Configure clipping: */
+ configureClipping(rect, painter);
+
+ /* Paint background: */
+ paintBackground(rect, painter);
+
+ /* Paint frame: */
+ paintFrame(painter);
+}
+
+void UIPopupPane::configureClipping(const QRect &rect, QPainter &painter)
+{
+ /* Configure clipping: */
+ QPainterPath path;
+ int iDiameter = 6;
+ QSizeF arcSize(2 * iDiameter, 2 * iDiameter);
+ path.moveTo(rect.x() + iDiameter, rect.y());
+ path.arcTo(QRectF(path.currentPosition(), arcSize).translated(-iDiameter, 0), 90, 90);
+ path.lineTo(path.currentPosition().x(), rect.y() + rect.height() - iDiameter);
+ path.arcTo(QRectF(path.currentPosition(), arcSize).translated(0, -iDiameter), 180, 90);
+ path.lineTo(rect.x() + rect.width() - iDiameter, path.currentPosition().y());
+ path.arcTo(QRectF(path.currentPosition(), arcSize).translated(-iDiameter, -2 * iDiameter), 270, 90);
+ path.lineTo(path.currentPosition().x(), rect.y() + iDiameter);
+ path.arcTo(QRectF(path.currentPosition(), arcSize).translated(-2 * iDiameter, -iDiameter), 0, 90);
+ path.closeSubpath();
+ painter.setClipPath(path);
+}
+
+void UIPopupPane::paintBackground(const QRect &rect, QPainter &painter)
+{
+ /* Paint background: */
+ QColor currentColor(palette().color(QPalette::Window));
+ QColor newColor1(currentColor.red(), currentColor.green(), currentColor.blue(), opacity());
+ QColor newColor2 = newColor1.darker(115);
+ QLinearGradient headerGradient(rect.topLeft(), rect.bottomLeft());
+ headerGradient.setColorAt(0, newColor1);
+ headerGradient.setColorAt(1, newColor2);
+ painter.fillRect(rect, headerGradient);
+}
+
+void UIPopupPane::paintFrame(QPainter &painter)
+{
+ /* Paint frame: */
+ QColor currentColor(palette().color(QPalette::Window).darker(150));
+ QPainterPath path = painter.clipPath();
+ painter.setClipping(false);
+ painter.strokePath(path, currentColor);
+}
+
+void UIPopupPane::done(int iResultCode)
+{
+ /* Notify listeners: */
+ emit sigDone(iResultCode);
+}
+
+QString UIPopupPane::prepareDetailsText() const
+{
+ if (m_strDetails.isEmpty())
+ return QString();
+
+ QStringPairList aDetailsList;
+ prepareDetailsList(aDetailsList);
+ if (aDetailsList.isEmpty())
+ return QString();
+
+ if (aDetailsList.size() == 1)
+ return tr("<p><b>Details:</b>") + m_strDetails + "</p>";
+
+ QString strResultText;
+ for (int iListIdx = 0; iListIdx < aDetailsList.size(); ++iListIdx)
+ {
+ strResultText += tr("<p><b>Details:</b> (%1 of %2)").arg(iListIdx + 1).arg(aDetailsList.size());
+ const QString strFirstPart = aDetailsList.at(iListIdx).first;
+ const QString strSecondPart = aDetailsList.at(iListIdx).second;
+ if (strFirstPart.isEmpty())
+ strResultText += strSecondPart + "</p>";
+ else
+ strResultText += QString("%1<br>%2").arg(strFirstPart, strSecondPart) + "</p>";
+ }
+ return strResultText;
+}
+
+void UIPopupPane::prepareDetailsList(QStringPairList &aDetailsList) const
+{
+ if (m_strDetails.isEmpty())
+ return;
+
+ /* Split details into paragraphs: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList aParagraphs(m_strDetails.split("<!--EOP-->", Qt::SkipEmptyParts));
+#else
+ QStringList aParagraphs(m_strDetails.split("<!--EOP-->", QString::SkipEmptyParts));
+#endif
+ /* Make sure details-text has at least one paragraph: */
+ AssertReturnVoid(!aParagraphs.isEmpty());
+
+ /* Enumerate all the paragraphs: */
+ foreach (const QString &strParagraph, aParagraphs)
+ {
+ /* Split each paragraph into pairs: */
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList aParts(strParagraph.split("<!--EOM-->", Qt::KeepEmptyParts));
+#else
+ QStringList aParts(strParagraph.split("<!--EOM-->", QString::KeepEmptyParts));
+#endif
+ /* Make sure each paragraph consist of 2 parts: */
+ AssertReturnVoid(aParts.size() == 2);
+ /* Append each pair into details-list: */
+ aDetailsList << QStringPair(aParts.at(0), aParts.at(1));
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPane.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPane.h
new file mode 100644
index 00000000..a2d19222
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPane.h
@@ -0,0 +1,256 @@
+/* $Id: UIPopupPane.h $ */
+/** @file
+ * VBox Qt GUI - UIPopupPane class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIPopupPane_h
+#define FEQT_INCLUDED_SRC_widgets_UIPopupPane_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declaration: */
+class QEvent;
+class QObject;
+class QPainter;
+class QPaintEvent;
+class QRect;
+class QShowEvent;
+class QSize;
+class QString;
+class QWidget;
+class UIAnimation;
+class UIPopupPaneDetails;
+class UIPopupPaneMessage;
+class UIPopupPaneButtonPane;
+
+/** QWidget extension used as popup-center pane prototype. */
+class SHARED_LIBRARY_STUFF UIPopupPane : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+ Q_PROPERTY(QSize hiddenSizeHint READ hiddenSizeHint);
+ Q_PROPERTY(QSize shownSizeHint READ shownSizeHint);
+ Q_PROPERTY(QSize minimumSizeHint READ minimumSizeHint WRITE setMinimumSizeHint);
+ Q_PROPERTY(int defaultOpacity READ defaultOpacity);
+ Q_PROPERTY(int hoveredOpacity READ hoveredOpacity);
+ Q_PROPERTY(int opacity READ opacity WRITE setOpacity);
+
+signals:
+
+ /** Asks to show itself asynchronously. */
+ void sigToShow();
+ /** Asks to hide itself asynchronously. */
+ void sigToHide();
+ /** Asks to show itself instantly. */
+ void sigShow();
+ /** Asks to hide itself instantly. */
+ void sigHide();
+
+ /** Notifies about hover enter. */
+ void sigHoverEnter();
+ /** Notifies about hover leave. */
+ void sigHoverLeave();
+
+ /** Notifies about focus enter. */
+ void sigFocusEnter();
+ /** Notifies about focus leave. */
+ void sigFocusLeave();
+
+ /** Proposes pane @a iWidth. */
+ void sigProposePaneWidth(int iWidth);
+ /** Proposes details pane @a iHeight. */
+ void sigProposeDetailsPaneHeight(int iHeight);
+ /** Notifies about size-hint changed. */
+ void sigSizeHintChanged();
+
+ /** Asks to close with @a iResultCode. */
+ void sigDone(int iResultCode) const;
+
+public:
+
+ /** Constructs popup-pane.
+ * @param pParent Brings the parent.
+ * @param strMessage Brings the pane message.
+ * @param strDetails Brings the pane details.
+ * @param buttonDescriptions Brings the button descriptions. */
+ UIPopupPane(QWidget *pParent,
+ const QString &strMessage, const QString &strDetails,
+ const QMap<int, QString> &buttonDescriptions);
+
+ /** Recalls itself. */
+ void recall();
+
+ /** Defines the @a strMessage. */
+ void setMessage(const QString &strMessage);
+ /** Defines the @a strDetails. */
+ void setDetails(const QString &strDetails);
+
+ /** Returns minimum size-hint. */
+ QSize minimumSizeHint() const { return m_minimumSizeHint; }
+ /** Defines @a minimumSizeHint. */
+ void setMinimumSizeHint(const QSize &minimumSizeHint);
+ /** Lays the content out. */
+ void layoutContent();
+
+public slots:
+
+ /** Handles proposal for a @a newSize. */
+ void sltHandleProposalForSize(QSize newSize);
+
+private slots:
+
+ /** Marks pane as fully shown. */
+ void sltMarkAsShown();
+
+ /** Updates size-hint. */
+ void sltUpdateSizeHint();
+
+ /** Handles a click of button with @a iButtonID. */
+ void sltButtonClicked(int iButtonID);
+
+private:
+
+ /** A pair of strings. */
+ typedef QPair<QString, QString> QStringPair;
+ /** A list of string pairs. */
+ typedef QList<QStringPair> QStringPairList;
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares background. */
+ void prepareBackground();
+ /** Prepares content. */
+ void prepareContent();
+ /** Prepares animation. */
+ void prepareAnimation();
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ /** Translats tool-tips. */
+ void retranslateToolTips();
+
+ /** Pre-handles standard Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles first show @a pEvent. */
+ void polishEvent(QShowEvent *pEvent);
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Assigns clipping of @a rect geometry for passed @a painter. */
+ void configureClipping(const QRect &rect, QPainter &painter);
+ /** Paints background of @a rect geometry using @a painter. */
+ void paintBackground(const QRect &rect, QPainter &painter);
+ /** Paints frame of @a rect geometry using @a painter. */
+ void paintFrame(QPainter &painter);
+
+ /** Closes pane with @a iResultCode. */
+ void done(int iResultCode);
+
+ /** Returns size-hint in hidden state. */
+ QSize hiddenSizeHint() const { return m_hiddenSizeHint; }
+ /** Returns size-hint in shown state. */
+ QSize shownSizeHint() const { return m_shownSizeHint; }
+
+ /** Returns default opacity. */
+ int defaultOpacity() const { return m_iDefaultOpacity; }
+ /** Returns hovered opacity. */
+ int hoveredOpacity() const { return m_iHoveredOpacity; }
+ /** Returns current opacity. */
+ int opacity() const { return m_iOpacity; }
+ /** Defines current @a iOpacity. */
+ void setOpacity(int iOpacity) { m_iOpacity = iOpacity; update(); }
+
+ /** Returns details text. */
+ QString prepareDetailsText() const;
+ /** Prepares passed @a aDetailsList. */
+ void prepareDetailsList(QStringPairList &aDetailsList) const;
+
+ /** Holds whether the pane was polished. */
+ bool m_fPolished;
+
+ /** Holds the pane ID. */
+ const QString m_strId;
+
+ /** Holds the layout margin. */
+ const int m_iLayoutMargin;
+ /** Holds the layout spacing. */
+ const int m_iLayoutSpacing;
+
+ /** Holds the minimum size-hint. */
+ QSize m_minimumSizeHint;
+
+ /** Holds the pane message. */
+ QString m_strMessage;
+ /** Holds the pane details. */
+ QString m_strDetails;
+
+ /** Holds the button descriptions. */
+ QMap<int, QString> m_buttonDescriptions;
+
+ /** Holds whether the pane is shown fully. */
+ bool m_fShown;
+ /** Holds the show/hide animation instance. */
+ UIAnimation *m_pShowAnimation;
+ /** Holds the size-hint of pane in hidden state. */
+ QSize m_hiddenSizeHint;
+ /** Holds the size-hint of pane in shown state. */
+ QSize m_shownSizeHint;
+
+ /** Holds whether the pane can loose focus. */
+ bool m_fCanLooseFocus;
+ /** Holds whether the pane is focused. */
+ bool m_fFocused;
+
+ /** Holds whether the pane is hovered. */
+ bool m_fHovered;
+ /** Holds the default opacity. */
+ const int m_iDefaultOpacity;
+ /** Holds the hovered opacity. */
+ const int m_iHoveredOpacity;
+ /** Holds the current opacity. */
+ int m_iOpacity;
+
+ /** Holds the message pane instance. */
+ UIPopupPaneMessage *m_pMessagePane;
+ /** Holds the details pane instance. */
+ UIPopupPaneDetails *m_pDetailsPane;
+ /** Holds the buttons pane instance. */
+ UIPopupPaneButtonPane *m_pButtonPane;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIPopupPane_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneButtonPane.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneButtonPane.cpp
new file mode 100644
index 00000000..3665fd77
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneButtonPane.cpp
@@ -0,0 +1,242 @@
+/* $Id: UIPopupPaneButtonPane.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPopupPaneButtonPane class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QHBoxLayout>
+#include <QKeyEvent>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIMessageBox.h"
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIPopupPaneButtonPane.h"
+
+
+UIPopupPaneButtonPane::UIPopupPaneButtonPane(QWidget *pParent /* = 0*/)
+ : QWidget(pParent)
+ , m_iDefaultButton(0)
+ , m_iEscapeButton(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIPopupPaneButtonPane::setButtons(const QMap<int, QString> &buttonDescriptions)
+{
+ /* Make sure something changed: */
+ if (m_buttonDescriptions == buttonDescriptions)
+ return;
+
+ /* Assign new button-descriptions: */
+ m_buttonDescriptions = buttonDescriptions;
+
+ /* Recreate buttons: */
+ cleanupButtons();
+ prepareButtons();
+}
+
+void UIPopupPaneButtonPane::sltButtonClicked()
+{
+ /* Make sure the slot is called by the button: */
+ QIToolButton *pButton = qobject_cast<QIToolButton*>(sender());
+ if (!pButton)
+ return;
+
+ /* Make sure we still have that button: */
+ int iButtonID = m_buttons.key(pButton, 0);
+ if (!iButtonID)
+ return;
+
+ /* Notify listeners button was clicked: */
+ emit sigButtonClicked(iButtonID);
+}
+
+void UIPopupPaneButtonPane::prepare()
+{
+ /* Prepare layouts: */
+ prepareLayouts();
+}
+
+void UIPopupPaneButtonPane::prepareLayouts()
+{
+ /* Create main-layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Configure layout: */
+ pMainLayout->setSpacing(0);
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create button-layout: */
+ m_pButtonLayout = new QHBoxLayout;
+ if (m_pButtonLayout)
+ {
+ /* Configure layout: */
+ m_pButtonLayout->setSpacing(0);
+ m_pButtonLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Add into layout: */
+ pMainLayout->addLayout(m_pButtonLayout);
+ }
+
+ /* Add stretch: */
+ pMainLayout->addStretch();
+ }
+}
+
+void UIPopupPaneButtonPane::prepareButtons()
+{
+ /* Add all the buttons: */
+ const QList<int> &buttonsIDs = m_buttonDescriptions.keys();
+ foreach (int iButtonID, buttonsIDs)
+ if (QIToolButton *pButton = addButton(iButtonID, m_buttonDescriptions[iButtonID]))
+ {
+ m_pButtonLayout->addWidget(pButton);
+ m_buttons[iButtonID] = pButton;
+ connect(pButton, &QIToolButton::clicked, this, &UIPopupPaneButtonPane::sltButtonClicked);
+ if (pButton->property("default").toBool())
+ m_iDefaultButton = iButtonID;
+ if (pButton->property("escape").toBool())
+ m_iEscapeButton = iButtonID;
+ }
+}
+
+void UIPopupPaneButtonPane::cleanupButtons()
+{
+ /* Remove all the buttons: */
+ const QList<int> &buttonsIDs = m_buttons.keys();
+ foreach (int iButtonID, buttonsIDs)
+ {
+ delete m_buttons[iButtonID];
+ m_buttons.remove(iButtonID);
+ }
+}
+
+void UIPopupPaneButtonPane::keyPressEvent(QKeyEvent *pEvent)
+{
+ /* Depending on pressed key: */
+ switch (pEvent->key())
+ {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ {
+ if (m_iDefaultButton)
+ {
+ pEvent->accept();
+ emit sigButtonClicked(m_iDefaultButton);
+ return;
+ }
+ break;
+ }
+ case Qt::Key_Escape:
+ {
+ if (m_iEscapeButton)
+ {
+ pEvent->accept();
+ emit sigButtonClicked(m_iEscapeButton);
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ /* Call to base-class: */
+ QWidget::keyPressEvent(pEvent);
+}
+
+/* static */
+QIToolButton *UIPopupPaneButtonPane::addButton(int iButtonID, const QString &strToolTip)
+{
+ /* Create button: */
+ QIToolButton *pButton = new QIToolButton;
+ if (pButton)
+ {
+ /* Configure button: */
+ pButton->removeBorder();
+ pButton->setToolTip(strToolTip.isEmpty() ? defaultToolTip(iButtonID) : strToolTip);
+ pButton->setIcon(defaultIcon(iButtonID));
+
+ /* Sign the 'default' button: */
+ if (iButtonID & AlertButtonOption_Default)
+ pButton->setProperty("default", true);
+ /* Sign the 'escape' button: */
+ if (iButtonID & AlertButtonOption_Escape)
+ pButton->setProperty("escape", true);
+ }
+
+ /* Return button: */
+ return pButton;
+}
+
+/* static */
+QString UIPopupPaneButtonPane::defaultToolTip(int iButtonID)
+{
+ QString strToolTip;
+ switch (iButtonID & AlertButtonMask)
+ {
+ case AlertButton_Ok: strToolTip = QIMessageBox::tr("OK"); break;
+ case AlertButton_Cancel:
+ {
+ switch (iButtonID & AlertOptionMask)
+ {
+ case AlertOption_AutoConfirmed: strToolTip = QApplication::translate("UIMessageCenter", "Do not show this message again"); break;
+ default: strToolTip = QIMessageBox::tr("Cancel"); break;
+ }
+ break;
+ }
+ case AlertButton_Choice1: strToolTip = QIMessageBox::tr("Yes"); break;
+ case AlertButton_Choice2: strToolTip = QIMessageBox::tr("No"); break;
+ default: strToolTip = QString(); break;
+ }
+ return strToolTip;
+}
+
+/* static */
+QIcon UIPopupPaneButtonPane::defaultIcon(int iButtonID)
+{
+ QIcon icon;
+ switch (iButtonID & AlertButtonMask)
+ {
+ case AlertButton_Ok: icon = UIIconPool::iconSet(":/ok_16px.png"); break;
+ case AlertButton_Cancel:
+ {
+ switch (iButtonID & AlertOptionMask)
+ {
+ case AlertOption_AutoConfirmed: icon = UIIconPool::iconSet(":/close_popup_16px.png"); break;
+ default: icon = UIIconPool::iconSet(":/cancel_16px.png"); break;
+ }
+ break;
+ }
+ case AlertButton_Choice1: break;
+ case AlertButton_Choice2: break;
+ default: break;
+ }
+ return icon;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneButtonPane.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneButtonPane.h
new file mode 100644
index 00000000..a2f0aa80
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneButtonPane.h
@@ -0,0 +1,111 @@
+/* $Id: UIPopupPaneButtonPane.h $ */
+/** @file
+ * VBox Qt GUI - UIPopupPaneButtonPane class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIPopupPaneButtonPane_h
+#define FEQT_INCLUDED_SRC_widgets_UIPopupPaneButtonPane_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+#include <QMap>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QIcon;
+class QKeyEvent;
+class QString;
+class QIToolButton;
+
+/** QWidget extension providing GUI with popup-pane button-pane prototype class. */
+class SHARED_LIBRARY_STUFF UIPopupPaneButtonPane : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about button with @a iButtonID being clicked. */
+ void sigButtonClicked(int iButtonID);
+
+public:
+
+ /** Constructs popup-button pane passing @a pParent to the base-class. */
+ UIPopupPaneButtonPane(QWidget *pParent = 0);
+
+ /** Defines @a buttonDescriptions. */
+ void setButtons(const QMap<int, QString> &buttonDescriptions);
+ /** Returns default button. */
+ int defaultButton() const { return m_iDefaultButton; }
+ /** Returns escape button. */
+ int escapeButton() const { return m_iEscapeButton; }
+
+private slots:
+
+ /** Handles button click. */
+ void sltButtonClicked();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares layouts. */
+ void prepareLayouts();
+ /** Prepares buttons. */
+ void prepareButtons();
+ /** Cleanups buttons. */
+ void cleanupButtons();
+
+ /** Handles key-press @a pEvent. */
+ virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
+
+ /** Adds button with @a iButtonID and @a strToolTip. */
+ static QIToolButton *addButton(int iButtonID, const QString &strToolTip);
+ /** Returns default tool-tip for button @a iButtonID. */
+ static QString defaultToolTip(int iButtonID);
+ /** Returns default icon for button @a iButtonID. */
+ static QIcon defaultIcon(int iButtonID);
+
+ /** Holds the button layout. */
+ QHBoxLayout *m_pButtonLayout;
+
+ /** Holds the button descriptions. */
+ QMap<int, QString> m_buttonDescriptions;
+ /** Holds the button instances. */
+ QMap<int, QIToolButton*> m_buttons;
+
+ /** Holds default button. */
+ int m_iDefaultButton;
+ /** Holds escape button. */
+ int m_iEscapeButton;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIPopupPaneButtonPane_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneDetails.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneDetails.cpp
new file mode 100644
index 00000000..4b802089
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneDetails.cpp
@@ -0,0 +1,269 @@
+/* $Id: UIPopupPaneDetails.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPopupPaneDetails class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QTextDocument>
+#include <QTextEdit>
+
+/* GUI includes: */
+#include "UIAnimationFramework.h"
+#include "UIPopupPane.h"
+#include "UIPopupPaneDetails.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+UIPopupPaneDetails::UIPopupPaneDetails(QWidget *pParent, const QString &strText, bool fFocused)
+ : QWidget(pParent)
+ , m_iLayoutMargin(5)
+ , m_iLayoutSpacing(10)
+ , m_strText(strText)
+ , m_pTextEdit(0)
+ , m_iDesiredTextEditWidth(-1)
+ , m_iMaximumPaneHeight(-1)
+ , m_iMaximumTextEditHeight(0)
+ , m_iTextContentMargin(5)
+ , m_fFocused(fFocused)
+ , m_pAnimation(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIPopupPaneDetails::setText(const QString &strText)
+{
+ /* Make sure the text has changed: */
+ if (m_strText == strText)
+ return;
+
+ /* Fetch new text: */
+ m_strText = strText;
+ m_pTextEdit->setText(m_strText);
+
+ /* Update size-hint/visibility: */
+ updateSizeHint();
+ updateVisibility();
+}
+
+QSize UIPopupPaneDetails::minimumSizeHint() const
+{
+ /* Check if desired-width set: */
+ if (m_iDesiredTextEditWidth >= 0)
+ /* Dependent size-hint: */
+ return m_minimumSizeHint;
+ /* Golden-rule size-hint by default: */
+ return QWidget::minimumSizeHint();
+}
+
+void UIPopupPaneDetails::setMinimumSizeHint(const QSize &minimumSizeHint)
+{
+ /* Make sure the size-hint has changed: */
+ if (m_minimumSizeHint == minimumSizeHint)
+ return;
+
+ /* Fetch new size-hint: */
+ m_minimumSizeHint = minimumSizeHint;
+
+ /* Notify parent popup-pane: */
+ emit sigSizeHintChanged();
+}
+
+void UIPopupPaneDetails::layoutContent()
+{
+ /* Variables: */
+ const int iWidth = width();
+ const int iHeight = height();
+ const int iTextEditWidth = m_textEditSizeHint.width();
+ const int iTextEditHeight = m_textEditSizeHint.height();
+
+ /* TextEdit: */
+ m_pTextEdit->move(m_iLayoutMargin, m_iLayoutMargin);
+ m_pTextEdit->resize(qMin(iWidth, iTextEditWidth), qMin(iHeight, iTextEditHeight));
+ /* Text-document: */
+ QTextDocument *pTextDocument = m_pTextEdit->document();
+ if (pTextDocument)
+ {
+ pTextDocument->adjustSize();
+ pTextDocument->setTextWidth(m_pTextEdit->width() - m_iTextContentMargin);
+ }
+}
+
+void UIPopupPaneDetails::sltHandleProposalForWidth(int iWidth)
+{
+ /* Make sure the desired-width has changed: */
+ if (m_iDesiredTextEditWidth == iWidth)
+ return;
+
+ /* Fetch new desired-width: */
+ m_iDesiredTextEditWidth = iWidth;
+
+ /* Update size-hint: */
+ updateSizeHint();
+}
+
+void UIPopupPaneDetails::sltHandleProposalForHeight(int iHeight)
+{
+ /* Make sure the desired-height has changed: */
+ if (m_iMaximumPaneHeight == iHeight)
+ return;
+
+ /* Fetch new desired-height: */
+ m_iMaximumPaneHeight = iHeight;
+ m_iMaximumTextEditHeight = m_iMaximumPaneHeight - 2 * m_iLayoutMargin;
+
+ /* Update size-hint: */
+ updateSizeHint();
+}
+
+void UIPopupPaneDetails::sltFocusEnter()
+{
+ /* Ignore if already focused: */
+ if (m_fFocused)
+ return;
+
+ /* Update focus state: */
+ m_fFocused = true;
+
+ /* Update visibility: */
+ updateVisibility();
+
+ /* Notify listeners: */
+ emit sigFocusEnter();
+}
+
+void UIPopupPaneDetails::sltFocusLeave()
+{
+ /* Ignore if already unfocused: */
+ if (!m_fFocused)
+ return;
+
+ /* Update focus state: */
+ m_fFocused = false;
+
+ /* Update visibility: */
+ updateVisibility();
+
+ /* Notify listeners: */
+ emit sigFocusLeave();
+}
+
+void UIPopupPaneDetails::prepare()
+{
+ /* Prepare content: */
+ prepareContent();
+ /* Prepare animation: */
+ prepareAnimation();
+
+ /* Update size-hint/visibility: */
+ updateSizeHint();
+ updateVisibility();
+}
+
+void UIPopupPaneDetails::prepareContent()
+{
+ /* Create text-editor: */
+ m_pTextEdit = new QTextEdit(this);
+ if (m_pTextEdit)
+ {
+ /* Configure text-editor: */
+ m_pTextEdit->setFont(tuneFont(m_pTextEdit->font()));
+ m_pTextEdit->setText(m_strText);
+ m_pTextEdit->setFocusProxy(this);
+ }
+}
+
+void UIPopupPaneDetails::prepareAnimation()
+{
+ UIPopupPane *pPopupPane = qobject_cast<UIPopupPane*>(parent());
+ AssertReturnVoid(pPopupPane);
+ {
+ /* Propagate parent signals: */
+ connect(pPopupPane, &UIPopupPane::sigFocusEnter, this, &UIPopupPaneDetails::sltFocusEnter);
+ connect(pPopupPane, &UIPopupPane::sigFocusLeave, this, &UIPopupPaneDetails::sltFocusLeave);
+ }
+ /* Install geometry animation for 'minimumSizeHint' property: */
+ m_pAnimation = UIAnimation::installPropertyAnimation(this, "minimumSizeHint", "collapsedSizeHint", "expandedSizeHint",
+ SIGNAL(sigFocusEnter()), SIGNAL(sigFocusLeave()), m_fFocused);
+}
+
+void UIPopupPaneDetails::updateSizeHint()
+{
+ /* Recalculate collapsed size-hint: */
+ {
+ /* Collapsed size-hint with 0 height: */
+ m_collapsedSizeHint = QSize(m_iDesiredTextEditWidth, 0);
+ }
+
+ /* Recalculate expanded size-hint: */
+ {
+ int iNewHeight = m_iMaximumPaneHeight;
+ QTextDocument *pTextDocument = m_pTextEdit->document();
+ if (pTextDocument)
+ {
+ /* Adjust text-edit size: */
+ pTextDocument->adjustSize();
+ /* Get corresponding QTextDocument size: */
+ QSize textSize = pTextDocument->size().toSize();
+ /* Make sure the text edits height is no larger than that of container widget: */
+ iNewHeight = qMin(m_iMaximumTextEditHeight, textSize.height() + 2 * m_iLayoutMargin);
+ }
+ /* Recalculate label size-hint: */
+ m_textEditSizeHint = QSize(m_iDesiredTextEditWidth, iNewHeight);
+ /* Expanded size-hint contains full-size label: */
+ m_expandedSizeHint = m_textEditSizeHint;
+ }
+
+ /* Update current size-hint: */
+ m_minimumSizeHint = m_fFocused ? m_expandedSizeHint : m_collapsedSizeHint;
+
+ /* Update animation: */
+ if (m_pAnimation)
+ m_pAnimation->update();
+
+ /* Notify parent popup-pane: */
+ emit sigSizeHintChanged();
+}
+
+void UIPopupPaneDetails::updateVisibility()
+{
+ if (m_fFocused && !m_strText.isEmpty())
+ show();
+ else
+ hide();
+}
+
+/* static */
+QFont UIPopupPaneDetails::tuneFont(QFont font)
+{
+#if defined(VBOX_WS_MAC)
+ font.setPointSize(font.pointSize() - 2);
+#elif defined(VBOX_WS_X11)
+ font.setPointSize(font.pointSize() - 1);
+#endif
+ return font;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneDetails.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneDetails.h
new file mode 100644
index 00000000..8610b0df
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneDetails.h
@@ -0,0 +1,150 @@
+/* $Id: UIPopupPaneDetails.h $ */
+/** @file
+ * VBox Qt GUI - UIPopupPaneDetails class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIPopupPaneDetails_h
+#define FEQT_INCLUDED_SRC_widgets_UIPopupPaneDetails_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QTextEdit;
+class UIAnimation;
+
+/** QWidget extension providing GUI with popup-pane details-pane prototype class. */
+class SHARED_LIBRARY_STUFF UIPopupPaneDetails : public QWidget
+{
+ Q_OBJECT;
+ Q_PROPERTY(QSize collapsedSizeHint READ collapsedSizeHint);
+ Q_PROPERTY(QSize expandedSizeHint READ expandedSizeHint);
+ Q_PROPERTY(QSize minimumSizeHint READ minimumSizeHint WRITE setMinimumSizeHint);
+
+signals:
+
+ /** Notifies about focus enter. */
+ void sigFocusEnter();
+ /** Notifies about focus enter. */
+ void sigFocusLeave();
+
+ /** Notifies about size-hint change. */
+ void sigSizeHintChanged();
+
+public:
+
+ /** Constructs details-pane passing @a pParent to the base-class.
+ * @param strText Brings the details text.
+ * @param fFcoused Brings whether the pane is focused. */
+ UIPopupPaneDetails(QWidget *pParent, const QString &strText, bool fFocused);
+
+ /** Defines the details @a strText. */
+ void setText(const QString &strText);
+
+ /** Returns the details minimum size-hint. */
+ QSize minimumSizeHint() const;
+ /** Defines the details @a minimumSizeHint. */
+ void setMinimumSizeHint(const QSize &minimumSizeHint);
+ /** Lays the content out. */
+ void layoutContent();
+
+ /** Returns the collapsed size-hint. */
+ QSize collapsedSizeHint() const { return m_collapsedSizeHint; }
+ /** Returns the expanded size-hint. */
+ QSize expandedSizeHint() const { return m_expandedSizeHint; }
+
+public slots:
+
+ /** Handles proposal for @a iWidth. */
+ void sltHandleProposalForWidth(int iWidth);
+ /** Handles proposal for @a iHeight. */
+ void sltHandleProposalForHeight(int iHeight);
+
+ /** Handles focus enter. */
+ void sltFocusEnter();
+ /** Handles focus leave. */
+ void sltFocusLeave();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares content. */
+ void prepareContent();
+ /** Prepares animations. */
+ void prepareAnimation();
+
+ /** Updates size-hint. */
+ void updateSizeHint();
+ /** Updates visibility. */
+ void updateVisibility();
+
+ /** Adjusts @a font. */
+ static QFont tuneFont(QFont font);
+
+ /** Holds the layout margin. */
+ const int m_iLayoutMargin;
+ /** Holds the layout spacing. */
+ const int m_iLayoutSpacing;
+
+ /** Holds the text-editor size-hint. */
+ QSize m_textEditSizeHint;
+ /** Holds the collapsed size-hint. */
+ QSize m_collapsedSizeHint;
+ /** Holds the expanded size-hint. */
+ QSize m_expandedSizeHint;
+ /** Holds the minimum size-hint. */
+ QSize m_minimumSizeHint;
+
+ /** Holds the text. */
+ QString m_strText;
+
+ /** Holds the text-editor instance. */
+ QTextEdit *m_pTextEdit;
+
+ /** Holds the desired textr-editor width. */
+ int m_iDesiredTextEditWidth;
+ /** Holds the maximum pane height. */
+ int m_iMaximumPaneHeight;
+ /** Holds the desired textr-editor height. */
+ int m_iMaximumTextEditHeight;
+ /** Holds the text content margin. */
+ int m_iTextContentMargin;
+
+ /** Holds whether details-pane is focused. */
+ bool m_fFocused;
+
+ /** Holds the animation instance. */
+ UIAnimation *m_pAnimation;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIPopupPaneDetails_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneMessage.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneMessage.cpp
new file mode 100644
index 00000000..0893631a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneMessage.cpp
@@ -0,0 +1,219 @@
+/* $Id: UIPopupPaneMessage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPopupPaneMessage class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QLabel>
+#include <QCheckBox>
+
+/* GUI includes: */
+#include "UIAnimationFramework.h"
+#include "UIPopupPane.h"
+#include "UIPopupPaneMessage.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+UIPopupPaneMessage::UIPopupPaneMessage(QWidget *pParent, const QString &strText, bool fFocused)
+ : QWidget(pParent)
+ , m_iLayoutMargin(0)
+ , m_iLayoutSpacing(10)
+ , m_strText(strText)
+ , m_pLabel(0)
+ , m_iDesiredLabelWidth(-1)
+ , m_fFocused(fFocused)
+ , m_pAnimation(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIPopupPaneMessage::setText(const QString &strText)
+{
+ /* Make sure the text has changed: */
+ if (m_strText == strText)
+ return;
+
+ /* Fetch new text: */
+ m_strText = strText;
+ m_pLabel->setText(m_strText);
+
+ /* Update size-hint: */
+ updateSizeHint();
+}
+
+QSize UIPopupPaneMessage::minimumSizeHint() const
+{
+ /* Check if desired-width set: */
+ if (m_iDesiredLabelWidth >= 0)
+ /* Dependent size-hint: */
+ return m_minimumSizeHint;
+ /* Golden-rule size-hint by default: */
+ return QWidget::minimumSizeHint();
+}
+
+void UIPopupPaneMessage::setMinimumSizeHint(const QSize &minimumSizeHint)
+{
+ /* Make sure the size-hint has changed: */
+ if (m_minimumSizeHint == minimumSizeHint)
+ return;
+
+ /* Fetch new size-hint: */
+ m_minimumSizeHint = minimumSizeHint;
+
+ /* Notify parent popup-pane: */
+ emit sigSizeHintChanged();
+}
+
+void UIPopupPaneMessage::layoutContent()
+{
+ /* Variables: */
+ const int iWidth = width();
+ const int iHeight = height();
+ const int iLabelWidth = m_labelSizeHint.width();
+ const int iLabelHeight = m_labelSizeHint.height();
+
+ /* Label: */
+ m_pLabel->move(m_iLayoutMargin, m_iLayoutMargin);
+ m_pLabel->resize(qMin(iWidth, iLabelWidth), qMin(iHeight, iLabelHeight));
+}
+
+void UIPopupPaneMessage::sltHandleProposalForWidth(int iWidth)
+{
+ /* Make sure the desired-width has changed: */
+ if (m_iDesiredLabelWidth == iWidth)
+ return;
+
+ /* Fetch new desired-width: */
+ m_iDesiredLabelWidth = iWidth;
+
+ /* Update size-hint: */
+ updateSizeHint();
+}
+
+void UIPopupPaneMessage::sltFocusEnter()
+{
+ /* Ignore if already focused: */
+ if (m_fFocused)
+ return;
+
+ /* Update focus state: */
+ m_fFocused = true;
+
+ /* Notify listeners: */
+ emit sigFocusEnter();
+}
+
+void UIPopupPaneMessage::sltFocusLeave()
+{
+ /* Ignore if already unfocused: */
+ if (!m_fFocused)
+ return;
+
+ /* Update focus state: */
+ m_fFocused = false;
+
+ /* Notify listeners: */
+ emit sigFocusLeave();
+}
+
+void UIPopupPaneMessage::prepare()
+{
+ /* Prepare content: */
+ prepareContent();
+ /* Prepare animation: */
+ prepareAnimation();
+
+ /* Update size-hint: */
+ updateSizeHint();
+}
+
+void UIPopupPaneMessage::prepareContent()
+{
+ /* Create label: */
+ m_pLabel = new QLabel(this);
+ if (m_pLabel)
+ {
+ /* Configure label: */
+ m_pLabel->setFont(tuneFont(m_pLabel->font()));
+ m_pLabel->setWordWrap(true);
+ m_pLabel->setFocusPolicy(Qt::NoFocus);
+ m_pLabel->setText(m_strText);
+ }
+}
+
+void UIPopupPaneMessage::prepareAnimation()
+{
+ UIPopupPane *pPopupPane = qobject_cast<UIPopupPane*>(parent());
+ AssertReturnVoid(pPopupPane);
+ {
+ /* Propagate parent signals: */
+ connect(pPopupPane, &UIPopupPane::sigFocusEnter, this, &UIPopupPaneMessage::sltFocusEnter);
+ connect(pPopupPane, &UIPopupPane::sigFocusLeave, this, &UIPopupPaneMessage::sltFocusLeave);
+ }
+ /* Install geometry animation for 'minimumSizeHint' property: */
+ m_pAnimation = UIAnimation::installPropertyAnimation(this, "minimumSizeHint", "collapsedSizeHint", "expandedSizeHint",
+ SIGNAL(sigFocusEnter()), SIGNAL(sigFocusLeave()), m_fFocused);
+}
+
+void UIPopupPaneMessage::updateSizeHint()
+{
+ /* Recalculate collapsed size-hint: */
+ {
+ /* Collapsed size-hint contains only one-text-line label: */
+ QFontMetrics fm(m_pLabel->font(), m_pLabel);
+ m_collapsedSizeHint = QSize(m_iDesiredLabelWidth, fm.height());
+ }
+
+ /* Recalculate expanded size-hint: */
+ {
+ /* Recalculate label size-hint: */
+ m_labelSizeHint = QSize(m_iDesiredLabelWidth, m_pLabel->heightForWidth(m_iDesiredLabelWidth));
+ /* Expanded size-hint contains full-size label: */
+ m_expandedSizeHint = m_labelSizeHint;
+ }
+
+ /* Update current size-hint: */
+ m_minimumSizeHint = m_fFocused ? m_expandedSizeHint : m_collapsedSizeHint;
+
+ /* Update animation: */
+ if (m_pAnimation)
+ m_pAnimation->update();
+
+ /* Notify parent popup-pane: */
+ emit sigSizeHintChanged();
+}
+
+/* static */
+QFont UIPopupPaneMessage::tuneFont(QFont font)
+{
+#if defined(VBOX_WS_MAC)
+ font.setPointSize(font.pointSize() - 2);
+#elif defined(VBOX_WS_X11)
+ font.setPointSize(font.pointSize() - 1);
+#endif
+ return font;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneMessage.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneMessage.h
new file mode 100644
index 00000000..3e40052a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupPaneMessage.h
@@ -0,0 +1,141 @@
+/* $Id: UIPopupPaneMessage.h $ */
+/** @file
+ * VBox Qt GUI - UIPopupPaneMessage class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIPopupPaneMessage_h
+#define FEQT_INCLUDED_SRC_widgets_UIPopupPaneMessage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class UIAnimation;
+
+/** QWidget extension providing GUI with popup-pane message-pane prototype class. */
+class SHARED_LIBRARY_STUFF UIPopupPaneMessage : public QWidget
+{
+ Q_OBJECT;
+ Q_PROPERTY(QSize collapsedSizeHint READ collapsedSizeHint);
+ Q_PROPERTY(QSize expandedSizeHint READ expandedSizeHint);
+ Q_PROPERTY(QSize minimumSizeHint READ minimumSizeHint WRITE setMinimumSizeHint);
+
+signals:
+
+ /** Notifies about focus enter. */
+ void sigFocusEnter();
+ /** Notifies about focus enter. */
+ void sigFocusLeave();
+
+ /** Notifies about size-hint change. */
+ void sigSizeHintChanged();
+
+public:
+
+ /** Constructs message-pane passing @a pParent to the base-class.
+ * @param strText Brings the message text.
+ * @param fFcoused Brings whether the pane is focused. */
+ UIPopupPaneMessage(QWidget *pParent, const QString &strText, bool fFocused);
+
+ /** Defines the message @a strText. */
+ void setText(const QString &strText);
+
+ /** Returns the message minimum size-hint. */
+ QSize minimumSizeHint() const;
+ /** Defines the message @a minimumSizeHint. */
+ void setMinimumSizeHint(const QSize &minimumSizeHint);
+ /** Lays the content out. */
+ void layoutContent();
+
+ /** Returns the collapsed size-hint. */
+ QSize collapsedSizeHint() const { return m_collapsedSizeHint; }
+ /** Returns the expanded size-hint. */
+ QSize expandedSizeHint() const { return m_expandedSizeHint; }
+
+public slots:
+
+ /** Handles proposal for @a iWidth. */
+ void sltHandleProposalForWidth(int iWidth);
+
+private slots:
+
+ /** Handles focus enter. */
+ void sltFocusEnter();
+ /** Handles focus leave. */
+ void sltFocusLeave();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares content. */
+ void prepareContent();
+ /** Prepares animations. */
+ void prepareAnimation();
+
+ /** Updates size-hint. */
+ void updateSizeHint();
+
+ /** Adjusts @a font. */
+ static QFont tuneFont(QFont font);
+
+ /** Holds the layout margin. */
+ const int m_iLayoutMargin;
+ /** Holds the layout spacing. */
+ const int m_iLayoutSpacing;
+
+ /** Holds the label size-hint. */
+ QSize m_labelSizeHint;
+ /** Holds the collapsed size-hint. */
+ QSize m_collapsedSizeHint;
+ /** Holds the expanded size-hint. */
+ QSize m_expandedSizeHint;
+ /** Holds the minimum size-hint. */
+ QSize m_minimumSizeHint;
+
+ /** Holds the text. */
+ QString m_strText;
+
+ /** Holds the label instance. */
+ QLabel *m_pLabel;
+
+ /** Holds the desired label width. */
+ int m_iDesiredLabelWidth;
+
+ /** Holds whether message-pane is focused. */
+ bool m_fFocused;
+
+ /** Holds the animation instance. */
+ UIAnimation *m_pAnimation;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIPopupPaneMessage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStack.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStack.cpp
new file mode 100644
index 00000000..c9454909
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStack.cpp
@@ -0,0 +1,358 @@
+/* $Id: UIPopupStack.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPopupStack class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QEvent>
+#include <QMainWindow>
+#include <QMenuBar>
+#include <QScrollArea>
+#include <QStatusBar>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UICursor.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIPopupStack.h"
+#include "UIPopupStackViewport.h"
+
+
+UIPopupStack::UIPopupStack(const QString &strID, UIPopupStackOrientation enmOrientation)
+ : m_strID(strID)
+ , m_enmOrientation(enmOrientation)
+ , m_pScrollArea(0)
+ , m_pScrollViewport(0)
+ , m_iParentMenuBarHeight(0)
+ , m_iParentStatusBarHeight(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+bool UIPopupStack::exists(const QString &strID) const
+{
+ /* Redirect question to viewport: */
+ return m_pScrollViewport->exists(strID);
+}
+
+void UIPopupStack::createPopupPane(const QString &strID,
+ const QString &strMessage, const QString &strDetails,
+ const QMap<int, QString> &buttonDescriptions)
+{
+ /* Redirect request to viewport: */
+ m_pScrollViewport->createPopupPane(strID,
+ strMessage, strDetails,
+ buttonDescriptions);
+
+ /* Propagate size: */
+ propagateSize();
+}
+
+void UIPopupStack::updatePopupPane(const QString &strID,
+ const QString &strMessage, const QString &strDetails)
+{
+ /* Redirect request to viewport: */
+ m_pScrollViewport->updatePopupPane(strID,
+ strMessage, strDetails);
+}
+
+void UIPopupStack::recallPopupPane(const QString &strID)
+{
+ /* Redirect request to viewport: */
+ m_pScrollViewport->recallPopupPane(strID);
+}
+
+void UIPopupStack::setOrientation(UIPopupStackOrientation enmOrientation)
+{
+ /* Make sure orientation has changed: */
+ if (m_enmOrientation == enmOrientation)
+ return;
+
+ /* Update orientation: */
+ m_enmOrientation = enmOrientation;
+ sltAdjustGeometry();
+}
+
+void UIPopupStack::setParent(QWidget *pParent)
+{
+ /* Call to base-class: */
+ QWidget::setParent(pParent);
+ /* Recalculate parent menu-bar height: */
+ m_iParentMenuBarHeight = parentMenuBarHeight(pParent);
+ /* Recalculate parent status-bar height: */
+ m_iParentStatusBarHeight = parentStatusBarHeight(pParent);
+}
+
+void UIPopupStack::setParent(QWidget *pParent, Qt::WindowFlags enmFlags)
+{
+ /* Call to base-class: */
+ QWidget::setParent(pParent, enmFlags);
+ /* Recalculate parent menu-bar height: */
+ m_iParentMenuBarHeight = parentMenuBarHeight(pParent);
+ /* Recalculate parent status-bar height: */
+ m_iParentStatusBarHeight = parentStatusBarHeight(pParent);
+}
+
+bool UIPopupStack::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ /* Call to base-class if that is not parent event: */
+ if (!parent() || pWatched != parent())
+ return QWidget::eventFilter(pWatched, pEvent);
+
+ /* Handle parent geometry events: */
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ {
+ /* Propagate size: */
+ propagateSize();
+ /* Adjust geometry: */
+ sltAdjustGeometry();
+ break;
+ }
+ case QEvent::Move:
+ {
+ /* Adjust geometry: */
+ sltAdjustGeometry();
+ break;
+ }
+ default:
+ break; /* Shuts up MSC. */
+ }
+
+ /* Call to base-class: */
+ return QWidget::eventFilter(pWatched, pEvent);
+}
+
+void UIPopupStack::showEvent(QShowEvent*)
+{
+ /* Propagate size: */
+ propagateSize();
+ /* Adjust geometry: */
+ sltAdjustGeometry();
+}
+
+void UIPopupStack::sltAdjustGeometry()
+{
+ /* Make sure parent is currently set: */
+ if (!parent())
+ return;
+
+ /* Read parent geometry: */
+ QRect geo(parentWidget()->geometry());
+ if (!parentWidget()->isWindow())
+ geo.moveTo(parentWidget()->mapToGlobal(QPoint(0, 0)));
+
+ /* Determine size: */
+ int iWidth = parentWidget()->width();
+ int iHeight = parentWidget()->height();
+ /* Subtract menu-bar and status-bar heights: */
+ iHeight -= (m_iParentMenuBarHeight + m_iParentStatusBarHeight);
+ /* Check if minimum height is even less than current: */
+ if (m_pScrollViewport)
+ {
+ /* Get minimum viewport height: */
+ int iMinimumHeight = m_pScrollViewport->minimumSizeHint().height();
+ /* Subtract layout margins: */
+ int iLeft, iTop, iRight, iBottom;
+ m_pMainLayout->getContentsMargins(&iLeft, &iTop, &iRight, &iBottom);
+ iMinimumHeight += (iTop + iBottom);
+ /* Compare minimum and current height: */
+ iHeight = qMin(iHeight, iMinimumHeight);
+ }
+
+ /* Determine origin: */
+ int iX = 0;
+ int iY = 0;
+ /* Shift for top-level window: */
+ if (isWindow())
+ {
+ iX += geo.x();
+ iY += geo.y();
+ }
+ switch (m_enmOrientation)
+ {
+ case UIPopupStackOrientation_Top:
+ {
+ /* Just add menu-bar height: */
+ iY += m_iParentMenuBarHeight;
+ break;
+ }
+ case UIPopupStackOrientation_Bottom:
+ {
+ /* Shift to bottom: */
+ iY += (geo.height() - iHeight);
+ /* And subtract status-bar height: */
+ iY -= m_iParentStatusBarHeight;
+ break;
+ }
+ }
+
+ /* Adjust geometry: */
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, iX, iY, iWidth, iHeight);
+}
+
+void UIPopupStack::sltPopupPaneRemoved(QString)
+{
+ /* Move focus to the parent: */
+ if (parentWidget())
+ parentWidget()->setFocus();
+}
+
+void UIPopupStack::sltPopupPanesRemoved()
+{
+ /* Ask popup-center to remove us: */
+ emit sigRemove(m_strID);
+}
+
+void UIPopupStack::prepare()
+{
+ /* Configure background: */
+ setAutoFillBackground(false);
+#if defined(VBOX_WS_WIN) || defined (VBOX_WS_MAC)
+ /* Using Qt API to enable translucent background for the Win/Mac host: */
+ setAttribute(Qt::WA_TranslucentBackground);
+#endif
+
+#ifdef VBOX_WS_MAC
+ /* Do not hide popup-stack: */
+ setAttribute(Qt::WA_MacAlwaysShowToolWindow);
+#endif
+
+ /* Prepare content: */
+ prepareContent();
+}
+
+void UIPopupStack::prepareContent()
+{
+ /* Create main-layout: */
+ m_pMainLayout = new QVBoxLayout(this);
+ {
+ /* Configure main-layout: */
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+ /* Create scroll-area: */
+ m_pScrollArea = new QScrollArea;
+ {
+ /* Configure scroll-area: */
+ UICursor::setCursor(m_pScrollArea, Qt::ArrowCursor);
+ m_pScrollArea->setWidgetResizable(true);
+ m_pScrollArea->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
+ m_pScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ //m_pScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ QPalette pal = m_pScrollArea->palette();
+#ifndef VBOX_IS_QT6_OR_LATER /** @todo Qt::transparent glitches in qt6? */
+ pal.setColor(QPalette::Window, QColor(Qt::transparent));
+#endif
+ m_pScrollArea->setPalette(pal);
+ /* Create scroll-viewport: */
+ m_pScrollViewport = new UIPopupStackViewport;
+ {
+ /* Configure scroll-viewport: */
+ UICursor::setCursor(m_pScrollViewport, Qt::ArrowCursor);
+ /* Connect scroll-viewport: */
+ connect(this, &UIPopupStack::sigProposeStackViewportSize,
+ m_pScrollViewport, &UIPopupStackViewport::sltHandleProposalForSize);
+ connect(m_pScrollViewport, &UIPopupStackViewport::sigSizeHintChanged,
+ this, &UIPopupStack::sltAdjustGeometry);
+ connect(m_pScrollViewport, &UIPopupStackViewport::sigPopupPaneDone,
+ this, &UIPopupStack::sigPopupPaneDone);
+ connect(m_pScrollViewport, &UIPopupStackViewport::sigPopupPaneRemoved,
+ this, &UIPopupStack::sltPopupPaneRemoved);
+ connect(m_pScrollViewport, &UIPopupStackViewport::sigPopupPanesRemoved,
+ this, &UIPopupStack::sltPopupPanesRemoved);
+ }
+ /* Assign scroll-viewport to scroll-area: */
+ m_pScrollArea->setWidget(m_pScrollViewport);
+ }
+ /* Add scroll-area to layout: */
+ m_pMainLayout->addWidget(m_pScrollArea);
+ }
+}
+
+void UIPopupStack::propagateSize()
+{
+ /* Make sure parent is currently set: */
+ if (!parent())
+ return;
+
+ /* Get parent size: */
+ QSize newSize = parentWidget()->size();
+ /* Subtract left/right layout margins: */
+ if (m_pMainLayout)
+ {
+ int iLeft, iTop, iRight, iBottom;
+ m_pMainLayout->getContentsMargins(&iLeft, &iTop, &iRight, &iBottom);
+ newSize.setWidth(newSize.width() - (iLeft + iRight));
+ newSize.setHeight(newSize.height() - (iTop + iBottom));
+ }
+ /* Subtract scroll-area frame-width: */
+ if (m_pScrollArea)
+ {
+ newSize.setWidth(newSize.width() - (2 * m_pScrollArea->frameWidth()));
+ newSize.setHeight(newSize.height() - (2 * m_pScrollArea->frameWidth()));
+ }
+ newSize.setHeight(newSize.height() - (m_iParentMenuBarHeight + m_iParentStatusBarHeight));
+
+ /* Propose resulting size to viewport: */
+ emit sigProposeStackViewportSize(newSize);
+}
+
+/* static */
+int UIPopupStack::parentMenuBarHeight(QWidget *pParent)
+{
+ /* Menu-bar can exist only on QMainWindow sub-class: */
+ if (pParent)
+ {
+ if (QMainWindow *pMainWindow = qobject_cast<QMainWindow*>(pParent))
+ {
+ /* Search for existing menu-bar child: */
+ if (QMenuBar *pMenuBar = pMainWindow->findChild<QMenuBar*>())
+ return pMenuBar->height();
+ }
+ }
+ /* Zero by default: */
+ return 0;
+}
+
+/* static */
+int UIPopupStack::parentStatusBarHeight(QWidget *pParent)
+{
+ /* Status-bar can exist only on QMainWindow sub-class: */
+ if (pParent)
+ {
+ if (QMainWindow *pMainWindow = qobject_cast<QMainWindow*>(pParent))
+ {
+ /* Search for existing status-bar child: */
+ if (QStatusBar *pStatusBar = pMainWindow->findChild<QStatusBar*>())
+ if (pStatusBar->isVisible())
+ return pStatusBar->height();
+
+ }
+ }
+ /* Zero by default: */
+ return 0;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStack.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStack.h
new file mode 100644
index 00000000..1f6f4786
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStack.h
@@ -0,0 +1,144 @@
+/* $Id: UIPopupStack.h $ */
+/** @file
+ * VBox Qt GUI - UIPopupStack class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIPopupStack_h
+#define FEQT_INCLUDED_SRC_widgets_UIPopupStack_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+#include <QMap>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+#include "UIPopupCenter.h"
+
+/* Forward declaration: */
+class QEvent;
+class QObject;
+class QScrollArea;
+class QShowEvent;
+class QSize;
+class QString;
+class QVBoxLayout;
+class UIPopupStackViewport;
+
+/** QWidget extension providing GUI with popup-stack prototype class. */
+class SHARED_LIBRARY_STUFF UIPopupStack : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about popup-stack viewport size change. */
+ void sigProposeStackViewportSize(QSize newSize);
+
+ /** Asks to close popup-pane with @a strID and @a iResultCode. */
+ void sigPopupPaneDone(QString strID, int iResultCode);
+
+ /** Asks to close popup-stack with @a strID. */
+ void sigRemove(QString strID);
+
+public:
+
+ /** Constructs popup-stack with passed @a strID and @a enmOrientation. */
+ UIPopupStack(const QString &strID, UIPopupStackOrientation enmOrientation);
+
+ /** Returns whether pane with passed @a strID exists. */
+ bool exists(const QString &strID) const;
+ /** Creates pane with passed @a strID, @a strMessage, @a strDetails and @a buttonDescriptions. */
+ void createPopupPane(const QString &strID,
+ const QString &strMessage, const QString &strDetails,
+ const QMap<int, QString> &buttonDescriptions);
+ /** Updates pane with passed @a strID, @a strMessage and @a strDetails. */
+ void updatePopupPane(const QString &strID,
+ const QString &strMessage, const QString &strDetails);
+ /** Recalls pane with passed @a strID. */
+ void recallPopupPane(const QString &strID);
+ /** Defines stack @a enmOrientation. */
+ void setOrientation(UIPopupStackOrientation enmOrientation);
+
+ /** Defines stack @a pParent*/
+ void setParent(QWidget *pParent);
+ /** Defines stack @a pParent and @a enmFlags. */
+ void setParent(QWidget *pParent, Qt::WindowFlags enmFlags);
+
+protected:
+
+ /** Pre-handles standard Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Adjusts geometry. */
+ void sltAdjustGeometry();
+
+ /** Handles removal of the popup-pane with @a strID. */
+ void sltPopupPaneRemoved(QString strID);
+ /** Handles removal of all the popup-panes. */
+ void sltPopupPanesRemoved();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares contents. */
+ void prepareContent();
+
+ /** Propagates size. */
+ void propagateSize();
+
+ /** Returns @a pParent menu-bar height. */
+ static int parentMenuBarHeight(QWidget *pParent);
+ /** Returns @a pParent status-bar height. */
+ static int parentStatusBarHeight(QWidget *pParent);
+
+ /** Holds the stack ID. */
+ QString m_strID;
+ /** Holds the stack orientation. */
+ UIPopupStackOrientation m_enmOrientation;
+
+ /** Holds the main-layout instance. */
+ QVBoxLayout *m_pMainLayout;
+ /** Holds the scroll-area instance. */
+ QScrollArea *m_pScrollArea;
+ /** Holds the scroll-viewport instance. */
+ UIPopupStackViewport *m_pScrollViewport;
+
+ /** Holds the parent menu-bar height. */
+ int m_iParentMenuBarHeight;
+ /** Holds the parent status-bar height. */
+ int m_iParentStatusBarHeight;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIPopupStack_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStackViewport.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStackViewport.cpp
new file mode 100644
index 00000000..08048224
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStackViewport.cpp
@@ -0,0 +1,220 @@
+/* $Id: UIPopupStackViewport.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPopupStackViewport class implementation.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIPopupPane.h"
+#include "UIPopupStackViewport.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+UIPopupStackViewport::UIPopupStackViewport()
+ : m_iLayoutMargin(1)
+ , m_iLayoutSpacing(1)
+{
+}
+
+bool UIPopupStackViewport::exists(const QString &strID) const
+{
+ /* Is there already popup-pane with the same ID? */
+ return m_panes.contains(strID);
+}
+
+void UIPopupStackViewport::createPopupPane(const QString &strID,
+ const QString &strMessage, const QString &strDetails,
+ const QMap<int, QString> &buttonDescriptions)
+{
+ /* Make sure there is no such popup-pane already: */
+ if (m_panes.contains(strID))
+ {
+ AssertMsgFailed(("Popup-pane already exists!"));
+ return;
+ }
+
+ /* Create new popup-pane: */
+ UIPopupPane *pPopupPane = m_panes[strID] = new UIPopupPane(this,
+ strMessage, strDetails,
+ buttonDescriptions);
+
+ /* Attach popup-pane connection: */
+ connect(this, &UIPopupStackViewport::sigProposePopupPaneSize, pPopupPane, &UIPopupPane::sltHandleProposalForSize);
+ connect(pPopupPane, &UIPopupPane::sigSizeHintChanged, this, &UIPopupStackViewport::sltAdjustGeometry);
+ connect(pPopupPane, &UIPopupPane::sigDone, this, &UIPopupStackViewport::sltPopupPaneDone);
+
+ /* Show popup-pane: */
+ pPopupPane->show();
+}
+
+void UIPopupStackViewport::updatePopupPane(const QString &strID,
+ const QString &strMessage, const QString &strDetails)
+{
+ /* Make sure there is such popup-pane already: */
+ if (!m_panes.contains(strID))
+ {
+ AssertMsgFailed(("Popup-pane doesn't exists!"));
+ return;
+ }
+
+ /* Get existing popup-pane: */
+ UIPopupPane *pPopupPane = m_panes[strID];
+
+ /* Update message and details: */
+ pPopupPane->setMessage(strMessage);
+ pPopupPane->setDetails(strDetails);
+}
+
+void UIPopupStackViewport::recallPopupPane(const QString &strID)
+{
+ /* Make sure there is such popup-pane already: */
+ if (!m_panes.contains(strID))
+ {
+ AssertMsgFailed(("Popup-pane doesn't exists!"));
+ return;
+ }
+
+ /* Get existing popup-pane: */
+ UIPopupPane *pPopupPane = m_panes[strID];
+
+ /* Recall popup-pane: */
+ pPopupPane->recall();
+}
+
+void UIPopupStackViewport::sltHandleProposalForSize(QSize newSize)
+{
+ /* Subtract layout margins: */
+ newSize.setWidth(newSize.width() - 2 * m_iLayoutMargin);
+ newSize.setHeight(newSize.height() - 2 * m_iLayoutMargin);
+
+ /* Propagate resulting size to popups: */
+ emit sigProposePopupPaneSize(newSize);
+}
+
+void UIPopupStackViewport::sltAdjustGeometry()
+{
+ /* Update size-hint: */
+ updateSizeHint();
+
+ /* Layout content: */
+ layoutContent();
+
+ /* Notify parent popup-stack: */
+ emit sigSizeHintChanged();
+}
+
+void UIPopupStackViewport::sltPopupPaneDone(int iResultCode)
+{
+ /* Make sure the sender is the popup-pane: */
+ UIPopupPane *pPopupPane = qobject_cast<UIPopupPane*>(sender());
+ if (!pPopupPane)
+ {
+ AssertMsgFailed(("Should be called by popup-pane only!"));
+ return;
+ }
+
+ /* Make sure the popup-pane still exists: */
+ const QString strID(m_panes.key(pPopupPane, QString()));
+ if (strID.isNull())
+ {
+ AssertMsgFailed(("Popup-pane already destroyed!"));
+ return;
+ }
+
+ /* Notify listeners about popup-pane removal: */
+ emit sigPopupPaneDone(strID, iResultCode);
+
+ /* Delete popup-pane asyncronously.
+ * To avoid issues with events which already posted: */
+ m_panes.remove(strID);
+ pPopupPane->deleteLater();
+
+ /* Notify listeners about popup-pane removed: */
+ emit sigPopupPaneRemoved(strID);
+
+ /* Adjust geometry: */
+ sltAdjustGeometry();
+
+ /* Make sure this stack still contains popup-panes: */
+ if (!m_panes.isEmpty())
+ return;
+
+ /* Notify listeners about popup-stack: */
+ emit sigPopupPanesRemoved();
+}
+
+void UIPopupStackViewport::updateSizeHint()
+{
+ /* Calculate minimum width-hint: */
+ int iMinimumWidthHint = 0;
+ {
+ /* Take into account all the panes: */
+ foreach (UIPopupPane *pPane, m_panes)
+ iMinimumWidthHint = qMax(iMinimumWidthHint, pPane->minimumSizeHint().width());
+
+ /* And two margins finally: */
+ iMinimumWidthHint += 2 * m_iLayoutMargin;
+ }
+
+ /* Calculate minimum height-hint: */
+ int iMinimumHeightHint = 0;
+ {
+ /* Take into account all the panes: */
+ foreach (UIPopupPane *pPane, m_panes)
+ iMinimumHeightHint += pPane->minimumSizeHint().height();
+
+ /* Take into account all the spacings, if any: */
+ if (!m_panes.isEmpty())
+ iMinimumHeightHint += (m_panes.size() - 1) * m_iLayoutSpacing;
+
+ /* And two margins finally: */
+ iMinimumHeightHint += 2 * m_iLayoutMargin;
+ }
+
+ /* Compose minimum size-hint: */
+ m_minimumSizeHint = QSize(iMinimumWidthHint, iMinimumHeightHint);
+}
+
+void UIPopupStackViewport::layoutContent()
+{
+ /* Get attributes: */
+ int iX = m_iLayoutMargin;
+ int iY = m_iLayoutMargin;
+
+ /* Layout every pane we have: */
+ foreach (UIPopupPane *pPane, m_panes)
+ {
+ /* Get pane attributes: */
+ QSize paneSize = pPane->minimumSizeHint();
+ const int iPaneWidth = paneSize.width();
+ const int iPaneHeight = paneSize.height();
+ /* Adjust geometry for the pane: */
+ pPane->setGeometry(iX, iY, iPaneWidth, iPaneHeight);
+ pPane->layoutContent();
+ /* Increment placeholder: */
+ iY += (iPaneHeight + m_iLayoutSpacing);
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStackViewport.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStackViewport.h
new file mode 100644
index 00000000..389c1dba
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupStackViewport.h
@@ -0,0 +1,119 @@
+/* $Id: UIPopupStackViewport.h $ */
+/** @file
+ * VBox Qt GUI - UIPopupStackViewport class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIPopupStackViewport_h
+#define FEQT_INCLUDED_SRC_widgets_UIPopupStackViewport_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+#include <QMap>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declaration: */
+class QSize;
+class QString;
+class UIPopupPane;
+
+/** QWidget extension providing GUI with popup-stack viewport prototype class. */
+class SHARED_LIBRARY_STUFF UIPopupStackViewport : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about popup-pane size change. */
+ void sigProposePopupPaneSize(QSize newSize);
+
+ /** Notifies about size-hint change. */
+ void sigSizeHintChanged();
+
+ /** Asks to close popup-pane with @a strID and @a iResultCode. */
+ void sigPopupPaneDone(QString strID, int iResultCode);
+ /** Notifies about popup-pane with @a strID was removed. */
+ void sigPopupPaneRemoved(QString strID);
+ /** Notifies about popup-panes were removed. */
+ void sigPopupPanesRemoved();
+
+public:
+
+ /** Constructs popup-stack viewport. */
+ UIPopupStackViewport();
+
+ /** Returns whether pane with passed @a strID exists. */
+ bool exists(const QString &strID) const;
+ /** Creates pane with passed @a strID, @a strMessage, @a strDetails and @a buttonDescriptions. */
+ void createPopupPane(const QString &strID,
+ const QString &strMessage, const QString &strDetails,
+ const QMap<int, QString> &buttonDescriptions);
+ /** Updates pane with passed @a strID, @a strMessage and @a strDetails. */
+ void updatePopupPane(const QString &strID,
+ const QString &strMessage, const QString &strDetails);
+ /** Recalls pane with passed @a strID. */
+ void recallPopupPane(const QString &strID);
+
+ /** Returns minimum size-hint. */
+ QSize minimumSizeHint() const { return m_minimumSizeHint; }
+
+public slots:
+
+ /** Handle proposal for @a newSize. */
+ void sltHandleProposalForSize(QSize newSize);
+
+private slots:
+
+ /** Adjusts geometry. */
+ void sltAdjustGeometry();
+
+ /** Handles reuqest to dismiss popup-pane with @a iButtonCode. */
+ void sltPopupPaneDone(int iButtonCode);
+
+private:
+
+ /** Updates size-hint. */
+ void updateSizeHint();
+ /** Lays the content out. */
+ void layoutContent();
+
+ /** Holds the layout margin. */
+ const int m_iLayoutMargin;
+ /** Holds the layout spacing. */
+ const int m_iLayoutSpacing;
+
+ /** Holds the minimum size-hint. */
+ QSize m_minimumSizeHint;
+
+ /** Holds the popup-pane instances. */
+ QMap<QString, UIPopupPane*> m_panes;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIPopupStackViewport_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPortForwardingTable.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIPortForwardingTable.cpp
new file mode 100644
index 00000000..d7654d4a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPortForwardingTable.cpp
@@ -0,0 +1,1242 @@
+/* $Id: UIPortForwardingTable.cpp $ */
+/** @file
+ * VBox Qt GUI - UIPortForwardingTable class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QComboBox>
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <QItemEditorFactory>
+#include <QLineEdit>
+#include <QMenu>
+#include <QRegExp>
+#include <QSpinBox>
+#include <QStyledItemDelegate>
+
+/* GUI includes: */
+#include "QITableView.h"
+#include "UIConverter.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIPortForwardingTable.h"
+#include "QIToolBar.h"
+
+/* Other VBox includes: */
+#include <iprt/cidr.h>
+
+/* External includes: */
+#include <math.h>
+
+
+/** Port Forwarding data types. */
+enum UIPortForwardingDataType
+{
+ UIPortForwardingDataType_Name,
+ UIPortForwardingDataType_Protocol,
+ UIPortForwardingDataType_HostIp,
+ UIPortForwardingDataType_HostPort,
+ UIPortForwardingDataType_GuestIp,
+ UIPortForwardingDataType_GuestPort,
+ UIPortForwardingDataType_Max
+};
+
+
+/** QLineEdit extension used as name editor. */
+class NameEditor : public QLineEdit
+{
+ Q_OBJECT;
+ Q_PROPERTY(NameData name READ name WRITE setName USER true);
+
+public:
+
+ /** Constructs name editor passing @a pParent to the base-class. */
+ NameEditor(QWidget *pParent = 0) : QLineEdit(pParent)
+ {
+ setFrame(false);
+ setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+ setValidator(new QRegularExpressionValidator(QRegularExpression("[^,:]*"), this));
+ }
+
+private:
+
+ /** Defines the @a strName. */
+ void setName(NameData strName)
+ {
+ setText(strName);
+ }
+
+ /** Returns the name. */
+ NameData name() const
+ {
+ return text();
+ }
+};
+
+
+/** QComboBox extension used as protocol editor. */
+class ProtocolEditor : public QComboBox
+{
+ Q_OBJECT;
+ Q_PROPERTY(KNATProtocol protocol READ protocol WRITE setProtocol USER true);
+
+public:
+
+ /** Constructs protocol editor passing @a pParent to the base-class. */
+ ProtocolEditor(QWidget *pParent = 0) : QComboBox(pParent)
+ {
+ addItem(gpConverter->toString(KNATProtocol_UDP), QVariant::fromValue(KNATProtocol_UDP));
+ addItem(gpConverter->toString(KNATProtocol_TCP), QVariant::fromValue(KNATProtocol_TCP));
+ }
+
+private:
+
+ /** Defines the @a enmProtocol. */
+ void setProtocol(KNATProtocol enmProtocol)
+ {
+ for (int i = 0; i < count(); ++i)
+ {
+ if (itemData(i).value<KNATProtocol>() == enmProtocol)
+ {
+ setCurrentIndex(i);
+ break;
+ }
+ }
+ }
+
+ /** Returns the protocol. */
+ KNATProtocol protocol() const
+ {
+ return itemData(currentIndex()).value<KNATProtocol>();
+ }
+};
+
+
+/** QLineEdit extension used as IPv4 editor. */
+class IPv4Editor : public QLineEdit
+{
+ Q_OBJECT;
+ Q_PROPERTY(IpData ip READ ip WRITE setIp USER true);
+
+public:
+
+ /** Constructs IPv4-editor passing @a pParent to the base-class. */
+ IPv4Editor(QWidget *pParent = 0) : QLineEdit(pParent)
+ {
+ setFrame(false);
+ setAlignment(Qt::AlignCenter);
+ // Decided to not use it for now:
+ // setValidator(new IPv4Validator(this));
+ }
+
+private:
+
+ /** Defines the @a strIp. */
+ void setIp(IpData strIp)
+ {
+ setText(strIp);
+ }
+
+ /** Returns the ip. */
+ IpData ip() const
+ {
+ return text() == "..." ? QString() : text();
+ }
+};
+
+
+/** QLineEdit extension used as IPv6 editor. */
+class IPv6Editor : public QLineEdit
+{
+ Q_OBJECT;
+ Q_PROPERTY(IpData ip READ ip WRITE setIp USER true);
+
+public:
+
+ /** Constructs IPv6-editor passing @a pParent to the base-class. */
+ IPv6Editor(QWidget *pParent = 0) : QLineEdit(pParent)
+ {
+ setFrame(false);
+ setAlignment(Qt::AlignCenter);
+ // Decided to not use it for now:
+ // setValidator(new IPv6Validator(this));
+ }
+
+private:
+
+ /** Defines the @a strIp. */
+ void setIp(IpData strIp)
+ {
+ setText(strIp);
+ }
+
+ /** Returns the ip. */
+ IpData ip() const
+ {
+ return text() == "..." ? QString() : text();
+ }
+};
+
+
+/** QSpinBox extension used as Port editor. */
+class PortEditor : public QSpinBox
+{
+ Q_OBJECT;
+ Q_PROPERTY(PortData port READ port WRITE setPort USER true);
+
+public:
+
+ /** Constructs Port-editor passing @a pParent to the base-class. */
+ PortEditor(QWidget *pParent = 0) : QSpinBox(pParent)
+ {
+ setFrame(false);
+ setRange(0, (1 << (8 * sizeof(ushort))) - 1);
+ }
+
+private:
+
+ /** Defines the @a port. */
+ void setPort(PortData port)
+ {
+ setValue(port.value());
+ }
+
+ /** Returns the port. */
+ PortData port() const
+ {
+ return value();
+ }
+};
+
+
+/** QITableViewCell extension used as Port Forwarding table-view cell. */
+class UIPortForwardingCell : public QITableViewCell
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs table cell passing @a pParent to the base-class.
+ * @param strName Brings the name. */
+ UIPortForwardingCell(QITableViewRow *pParent, const NameData &strName)
+ : QITableViewCell(pParent)
+ , m_strText(strName)
+ {}
+
+ /** Constructs table cell passing @a pParent to the base-class.
+ * @param enmProtocol Brings the protocol type. */
+ UIPortForwardingCell(QITableViewRow *pParent, KNATProtocol enmProtocol)
+ : QITableViewCell(pParent)
+ , m_strText(gpConverter->toString(enmProtocol))
+ {}
+
+ /** Constructs table cell passing @a pParent to the base-class.
+ * @param strIp Brings the IP address. */
+ UIPortForwardingCell(QITableViewRow *pParent, const IpData &strIp)
+ : QITableViewCell(pParent)
+ , m_strText(strIp)
+ {}
+
+ /** Constructs table cell passing @a pParent to the base-class.
+ * @param port Brings the port. */
+ UIPortForwardingCell(QITableViewRow *pParent, PortData port)
+ : QITableViewCell(pParent)
+ , m_strText(QString::number(port.value()))
+ {}
+
+ /** Returns the cell text. */
+ virtual QString text() const RT_OVERRIDE { return m_strText; }
+
+private:
+
+ /** Holds the cell text. */
+ QString m_strText;
+};
+
+
+/** QITableViewRow extension used as Port Forwarding table-view row. */
+class UIPortForwardingRow : public QITableViewRow
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs table row passing @a pParent to the base-class.
+ * @param strName Brings the unique rule name.
+ * @param enmProtocol Brings the rule protocol type.
+ * @param strHostIp Brings the rule host IP address.
+ * @param hostPort Brings the rule host port.
+ * @param strGuestIp Brings the rule guest IP address.
+ * @param guestPort Brings the rule guest port. */
+ UIPortForwardingRow(QITableView *pParent,
+ const NameData &strName, KNATProtocol enmProtocol,
+ const IpData &strHostIp, PortData hostPort,
+ const IpData &strGuestIp, PortData guestPort)
+ : QITableViewRow(pParent)
+ , m_strName(strName), m_enmProtocol(enmProtocol)
+ , m_strHostIp(strHostIp), m_hostPort(hostPort)
+ , m_strGuestIp(strGuestIp), m_guestPort(guestPort)
+ {
+ /* Create cells: */
+ createCells();
+ }
+
+ /** Destructs table row. */
+ ~UIPortForwardingRow()
+ {
+ /* Destroy cells: */
+ destroyCells();
+ }
+
+ /** Returns the unique rule name. */
+ NameData name() const { return m_strName; }
+ /** Defines the unique rule name. */
+ void setName(const NameData &strName)
+ {
+ m_strName = strName;
+ delete m_cells[UIPortForwardingDataType_Name];
+ m_cells[UIPortForwardingDataType_Name] = new UIPortForwardingCell(this, m_strName);
+ }
+
+ /** Returns the rule protocol type. */
+ KNATProtocol protocol() const { return m_enmProtocol; }
+ /** Defines the rule protocol type. */
+ void setProtocol(KNATProtocol enmProtocol)
+ {
+ m_enmProtocol = enmProtocol;
+ delete m_cells[UIPortForwardingDataType_Protocol];
+ m_cells[UIPortForwardingDataType_Protocol] = new UIPortForwardingCell(this, m_enmProtocol);
+ }
+
+ /** Returns the rule host IP address. */
+ IpData hostIp() const { return m_strHostIp; }
+ /** Defines the rule host IP address. */
+ void setHostIp(const IpData &strHostIp)
+ {
+ m_strHostIp = strHostIp;
+ delete m_cells[UIPortForwardingDataType_HostIp];
+ m_cells[UIPortForwardingDataType_HostIp] = new UIPortForwardingCell(this, m_strHostIp);
+ }
+
+ /** Returns the rule host port. */
+ PortData hostPort() const { return m_hostPort; }
+ /** Defines the rule host port. */
+ void setHostPort(PortData hostPort)
+ {
+ m_hostPort = hostPort;
+ delete m_cells[UIPortForwardingDataType_HostPort];
+ m_cells[UIPortForwardingDataType_HostPort] = new UIPortForwardingCell(this, m_hostPort);
+ }
+
+ /** Returns the rule guest IP address. */
+ IpData guestIp() const { return m_strGuestIp; }
+ /** Defines the rule guest IP address. */
+ void setGuestIp(const IpData &strGuestIp)
+ {
+ m_strGuestIp = strGuestIp;
+ delete m_cells[UIPortForwardingDataType_GuestIp];
+ m_cells[UIPortForwardingDataType_GuestIp] = new UIPortForwardingCell(this, m_strGuestIp);
+ }
+
+ /** Returns the rule guest port. */
+ PortData guestPort() const { return m_guestPort; }
+ /** Defines the rule guest port. */
+ void setGuestPort(PortData guestPort)
+ {
+ m_guestPort = guestPort;
+ delete m_cells[UIPortForwardingDataType_GuestPort];
+ m_cells[UIPortForwardingDataType_GuestPort] = new UIPortForwardingCell(this, m_guestPort);
+ }
+
+protected:
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE
+ {
+ /* Return cell count: */
+ return UIPortForwardingDataType_Max;
+ }
+
+ /** Returns the child item with @a iIndex. */
+ virtual QITableViewCell *childItem(int iIndex) const RT_OVERRIDE
+ {
+ /* Make sure index within the bounds: */
+ AssertReturn(iIndex >= 0 && iIndex < m_cells.size(), 0);
+ /* Return corresponding cell: */
+ return m_cells[iIndex];
+ }
+
+private:
+
+ /** Creates cells. */
+ void createCells()
+ {
+ /* Create cells on the basis of variables we have: */
+ m_cells.resize(UIPortForwardingDataType_Max);
+ m_cells[UIPortForwardingDataType_Name] = new UIPortForwardingCell(this, m_strName);
+ m_cells[UIPortForwardingDataType_Protocol] = new UIPortForwardingCell(this, m_enmProtocol);
+ m_cells[UIPortForwardingDataType_HostIp] = new UIPortForwardingCell(this, m_strHostIp);
+ m_cells[UIPortForwardingDataType_HostPort] = new UIPortForwardingCell(this, m_hostPort);
+ m_cells[UIPortForwardingDataType_GuestIp] = new UIPortForwardingCell(this, m_strGuestIp);
+ m_cells[UIPortForwardingDataType_GuestPort] = new UIPortForwardingCell(this, m_guestPort);
+ }
+
+ /** Destroys cells. */
+ void destroyCells()
+ {
+ /* Destroy cells: */
+ qDeleteAll(m_cells);
+ m_cells.clear();
+ }
+
+ /** Holds the unique rule name. */
+ NameData m_strName;
+ /** Holds the rule protocol type. */
+ KNATProtocol m_enmProtocol;
+ /** Holds the rule host IP address. */
+ IpData m_strHostIp;
+ /** Holds the rule host port. */
+ PortData m_hostPort;
+ /** Holds the rule guest IP address. */
+ IpData m_strGuestIp;
+ /** Holds the rule guest port. */
+ PortData m_guestPort;
+
+ /** Holds the cell instances. */
+ QVector<UIPortForwardingCell*> m_cells;
+};
+
+
+/** QAbstractTableModel subclass used as port forwarding data model. */
+class UIPortForwardingModel : public QAbstractTableModel
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Port Forwarding model passing @a pParent to the base-class.
+ * @param rules Brings the list of port forwarding rules to load initially. */
+ UIPortForwardingModel(QITableView *pParent, const UIPortForwardingDataList &rules = UIPortForwardingDataList());
+ /** Destructs Port Forwarding model. */
+ ~UIPortForwardingModel();
+
+ /** Returns the number of children. */
+ int childCount() const;
+ /** Returns the child item with @a iIndex. */
+ QITableViewRow *childItem(int iIndex) const;
+
+ /** Returns the list of port forwarding rules. */
+ UIPortForwardingDataList rules() const;
+ /** Defines the list of port forwarding @a newRules. */
+ void setRules(const UIPortForwardingDataList &newRules);
+ /** Adds empty port forwarding rule for certain @a index. */
+ void addRule(const QModelIndex &index);
+ /** Removes port forwarding rule with certain @a index. */
+ void removeRule(const QModelIndex &index);
+
+ /** Defines guest address @a strHint. */
+ void setGuestAddressHint(const QString &strHint);
+
+ /** Returns flags for item with certain @a index. */
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ /** Returns row count of certain @a parent. */
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+
+ /** Returns column count of certain @a parent. */
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+ /** Returns header data.
+ * @param iSection Brings the number of section we aquire data for.
+ * @param enmOrientation Brings the orientation of header we aquire data for.
+ * @param iRole Brings the role we aquire data for. */
+ QVariant headerData(int iSection, Qt::Orientation enmOrientation, int iRole) const;
+
+ /** Defines the @a iRole data for item with @a index as @a value. */
+ bool setData(const QModelIndex &index, const QVariant &value, int iRole = Qt::EditRole);
+ /** Returns the @a iRole data for item with @a index. */
+ QVariant data(const QModelIndex &index, int iRole) const;
+
+private:
+
+ /** Return the parent table-view reference. */
+ QITableView *parentTable() const;
+
+ /** Holds the port forwarding row list. */
+ QList<UIPortForwardingRow*> m_dataList;
+
+ /** Holds the guest address hint. */
+ QString m_strGuestAddressHint;
+};
+
+
+/** QITableView extension used as Port Forwarding table-view. */
+class UIPortForwardingView : public QITableView
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Port Forwarding table-view. */
+ UIPortForwardingView() {}
+
+protected:
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE;
+ /** Returns the child item with @a iIndex. */
+ virtual QITableViewRow *childItem(int iIndex) const RT_OVERRIDE;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIPortForwardingModel implementation. *
+*********************************************************************************************************************************/
+
+UIPortForwardingModel::UIPortForwardingModel(QITableView *pParent,
+ const UIPortForwardingDataList &rules /* = UIPortForwardingDataList() */)
+ : QAbstractTableModel(pParent)
+{
+ /* Fetch the incoming data: */
+ foreach (const UIDataPortForwardingRule &rule, rules)
+ m_dataList << new UIPortForwardingRow(pParent,
+ rule.name, rule.protocol,
+ rule.hostIp, rule.hostPort,
+ rule.guestIp, rule.guestPort);
+}
+
+UIPortForwardingModel::~UIPortForwardingModel()
+{
+ /* Delete the cached data: */
+ qDeleteAll(m_dataList);
+ m_dataList.clear();
+}
+
+int UIPortForwardingModel::childCount() const
+{
+ /* Return row count: */
+ return rowCount();
+}
+
+QITableViewRow *UIPortForwardingModel::childItem(int iIndex) const
+{
+ /* Make sure index within the bounds: */
+ AssertReturn(iIndex >= 0 && iIndex < m_dataList.size(), 0);
+ /* Return corresponding row: */
+ return m_dataList[iIndex];
+}
+
+UIPortForwardingDataList UIPortForwardingModel::rules() const
+{
+ /* Return the cached data: */
+ UIPortForwardingDataList data;
+ foreach (const UIPortForwardingRow *pRow, m_dataList)
+ data << UIDataPortForwardingRule(pRow->name(), pRow->protocol(),
+ pRow->hostIp(), pRow->hostPort(),
+ pRow->guestIp(), pRow->guestPort());
+ return data;
+}
+
+void UIPortForwardingModel::setRules(const UIPortForwardingDataList &newRules)
+{
+ /* Clear old data first of all: */
+ if (!m_dataList.isEmpty())
+ {
+ beginRemoveRows(QModelIndex(), 0, m_dataList.size() - 1);
+ foreach (const UIPortForwardingRow *pRow, m_dataList)
+ delete pRow;
+ m_dataList.clear();
+ endRemoveRows();
+ }
+
+ /* Fetch incoming data: */
+ if (!newRules.isEmpty())
+ {
+ beginInsertRows(QModelIndex(), 0, newRules.size() - 1);
+ foreach (const UIDataPortForwardingRule &rule, newRules)
+ m_dataList << new UIPortForwardingRow(qobject_cast<QITableView*>(parent()),
+ rule.name, rule.protocol,
+ rule.hostIp, rule.hostPort,
+ rule.guestIp, rule.guestPort);
+ endInsertRows();
+ }
+}
+
+void UIPortForwardingModel::addRule(const QModelIndex &index)
+{
+ beginInsertRows(QModelIndex(), m_dataList.size(), m_dataList.size());
+ /* Search for existing "Rule [NUMBER]" record: */
+ uint uMaxIndex = 0;
+ QString strTemplate("Rule %1");
+ QRegExp regExp(strTemplate.arg("(\\d+)"));
+ for (int i = 0; i < m_dataList.size(); ++i)
+ if (regExp.indexIn(m_dataList[i]->name()) > -1)
+ uMaxIndex = regExp.cap(1).toUInt() > uMaxIndex ? regExp.cap(1).toUInt() : uMaxIndex;
+ /* If index is valid => copy data: */
+ if (index.isValid())
+ m_dataList << new UIPortForwardingRow(parentTable(),
+ strTemplate.arg(++uMaxIndex), m_dataList[index.row()]->protocol(),
+ m_dataList[index.row()]->hostIp(), m_dataList[index.row()]->hostPort(),
+ m_dataList[index.row()]->guestIp(), m_dataList[index.row()]->guestPort());
+ /* If index is NOT valid => use default values: */
+ else
+ m_dataList << new UIPortForwardingRow(parentTable(),
+ strTemplate.arg(++uMaxIndex), KNATProtocol_TCP,
+ QString(""), 0, m_strGuestAddressHint, 0);
+ endInsertRows();
+}
+
+void UIPortForwardingModel::removeRule(const QModelIndex &index)
+{
+ if (!index.isValid())
+ return;
+ beginRemoveRows(QModelIndex(), index.row(), index.row());
+ delete m_dataList.at(index.row());
+ m_dataList.removeAt(index.row());
+ endRemoveRows();
+}
+
+void UIPortForwardingModel::setGuestAddressHint(const QString &strHint)
+{
+ m_strGuestAddressHint = strHint;
+}
+
+Qt::ItemFlags UIPortForwardingModel::flags(const QModelIndex &index) const
+{
+ /* Check index validness: */
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ /* All columns have similar flags: */
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
+}
+
+int UIPortForwardingModel::rowCount(const QModelIndex &) const
+{
+ return m_dataList.size();
+}
+
+int UIPortForwardingModel::columnCount(const QModelIndex &) const
+{
+ return UIPortForwardingDataType_Max;
+}
+
+QVariant UIPortForwardingModel::headerData(int iSection, Qt::Orientation enmOrientation, int iRole) const
+{
+ /* Display role for horizontal header: */
+ if (iRole == Qt::DisplayRole && enmOrientation == Qt::Horizontal)
+ {
+ /* Switch for different columns: */
+ switch (iSection)
+ {
+ case UIPortForwardingDataType_Name: return UIPortForwardingTable::tr("Name");
+ case UIPortForwardingDataType_Protocol: return UIPortForwardingTable::tr("Protocol");
+ case UIPortForwardingDataType_HostIp: return UIPortForwardingTable::tr("Host IP");
+ case UIPortForwardingDataType_HostPort: return UIPortForwardingTable::tr("Host Port");
+ case UIPortForwardingDataType_GuestIp: return UIPortForwardingTable::tr("Guest IP");
+ case UIPortForwardingDataType_GuestPort: return UIPortForwardingTable::tr("Guest Port");
+ default: break;
+ }
+ }
+ /* Return wrong value: */
+ return QVariant();
+}
+
+bool UIPortForwardingModel::setData(const QModelIndex &index, const QVariant &value, int iRole /* = Qt::EditRole */)
+{
+ /* Check index validness: */
+ if (!index.isValid() || iRole != Qt::EditRole)
+ return false;
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIPortForwardingDataType_Name:
+ m_dataList[index.row()]->setName(value.value<NameData>());
+ emit dataChanged(index, index);
+ return true;
+ case UIPortForwardingDataType_Protocol:
+ m_dataList[index.row()]->setProtocol(value.value<KNATProtocol>());
+ emit dataChanged(index, index);
+ return true;
+ case UIPortForwardingDataType_HostIp:
+ m_dataList[index.row()]->setHostIp(value.value<IpData>());
+ emit dataChanged(index, index);
+ return true;
+ case UIPortForwardingDataType_HostPort:
+ m_dataList[index.row()]->setHostPort(value.value<PortData>());
+ emit dataChanged(index, index);
+ return true;
+ case UIPortForwardingDataType_GuestIp:
+ m_dataList[index.row()]->setGuestIp(value.value<IpData>());
+ emit dataChanged(index, index);
+ return true;
+ case UIPortForwardingDataType_GuestPort:
+ m_dataList[index.row()]->setGuestPort(value.value<PortData>());
+ emit dataChanged(index, index);
+ return true;
+ default: return false;
+ }
+ /* not reached! */
+}
+
+QVariant UIPortForwardingModel::data(const QModelIndex &index, int iRole) const
+{
+ /* Check index validness: */
+ if (!index.isValid())
+ return QVariant();
+ /* Switch for different roles: */
+ switch (iRole)
+ {
+ /* Display role: */
+ case Qt::DisplayRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIPortForwardingDataType_Name: return m_dataList[index.row()]->name();
+ case UIPortForwardingDataType_Protocol: return gpConverter->toString(m_dataList[index.row()]->protocol());
+ case UIPortForwardingDataType_HostIp: return m_dataList[index.row()]->hostIp();
+ case UIPortForwardingDataType_HostPort: return m_dataList[index.row()]->hostPort().value();
+ case UIPortForwardingDataType_GuestIp: return m_dataList[index.row()]->guestIp();
+ case UIPortForwardingDataType_GuestPort: return m_dataList[index.row()]->guestPort().value();
+ default: return QVariant();
+ }
+ }
+ /* Edit role: */
+ case Qt::EditRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIPortForwardingDataType_Name: return QVariant::fromValue(m_dataList[index.row()]->name());
+ case UIPortForwardingDataType_Protocol: return QVariant::fromValue(m_dataList[index.row()]->protocol());
+ case UIPortForwardingDataType_HostIp: return QVariant::fromValue(m_dataList[index.row()]->hostIp());
+ case UIPortForwardingDataType_HostPort: return QVariant::fromValue(m_dataList[index.row()]->hostPort());
+ case UIPortForwardingDataType_GuestIp: return QVariant::fromValue(m_dataList[index.row()]->guestIp());
+ case UIPortForwardingDataType_GuestPort: return QVariant::fromValue(m_dataList[index.row()]->guestPort());
+ default: return QVariant();
+ }
+ }
+ /* Alignment role: */
+ case Qt::TextAlignmentRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIPortForwardingDataType_Name:
+ case UIPortForwardingDataType_Protocol:
+ case UIPortForwardingDataType_HostPort:
+ case UIPortForwardingDataType_GuestPort:
+ return (int)(Qt::AlignLeft | Qt::AlignVCenter);
+ case UIPortForwardingDataType_HostIp:
+ case UIPortForwardingDataType_GuestIp:
+ return Qt::AlignCenter;
+ default: return QVariant();
+ }
+ }
+ case Qt::SizeHintRole:
+ {
+ /* Switch for different columns: */
+ switch (index.column())
+ {
+ case UIPortForwardingDataType_HostIp:
+ case UIPortForwardingDataType_GuestIp:
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ return QSize(QApplication::fontMetrics().horizontalAdvance(" 888.888.888.888 "),
+ QApplication::fontMetrics().height());
+#else
+ return QSize(QApplication::fontMetrics().width(" 888.888.888.888 "), QApplication::fontMetrics().height());
+#endif
+ default: return QVariant();
+ }
+ }
+ default: break;
+ }
+ /* Return wrong value: */
+ return QVariant();
+}
+
+QITableView *UIPortForwardingModel::parentTable() const
+{
+ return qobject_cast<QITableView*>(parent());
+}
+
+
+/*********************************************************************************************************************************
+* Class UIPortForwardingView implementation. *
+*********************************************************************************************************************************/
+
+int UIPortForwardingView::childCount() const
+{
+ /* Redirect request to table model: */
+ return qobject_cast<UIPortForwardingModel*>(model())->childCount();
+}
+
+QITableViewRow *UIPortForwardingView::childItem(int iIndex) const
+{
+ /* Redirect request to table model: */
+ return qobject_cast<UIPortForwardingModel*>(model())->childItem(iIndex);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIPortForwardingTable implementation. *
+*********************************************************************************************************************************/
+
+UIPortForwardingTable::UIPortForwardingTable(const UIPortForwardingDataList &rules, bool fIPv6, bool fAllowEmptyGuestIPs)
+ : m_rules(rules)
+ , m_fIPv6(fIPv6)
+ , m_fAllowEmptyGuestIPs(fAllowEmptyGuestIPs)
+ , m_fTableDataChanged(false)
+ , m_pLayout(0)
+ , m_pTableView(0)
+ , m_pToolBar(0)
+ , m_pItemEditorFactory(0)
+ , m_pTableModel(0)
+ , m_pActionAdd(0)
+ , m_pActionCopy(0)
+ , m_pActionRemove(0)
+{
+ prepare();
+}
+
+UIPortForwardingTable::~UIPortForwardingTable()
+{
+ cleanup();
+}
+
+UIPortForwardingDataList UIPortForwardingTable::rules() const
+{
+ return m_pTableModel->rules();
+}
+
+void UIPortForwardingTable::setRules(const UIPortForwardingDataList &newRules,
+ bool fHoldPosition /* = false */)
+{
+ /* Remember last chosen item: */
+ const QModelIndex currentIndex = m_pTableView->currentIndex();
+ QITableViewRow *pCurrentItem = currentIndex.isValid() ? m_pTableModel->childItem(currentIndex.row()) : 0;
+ const QString strCurrentName = pCurrentItem ? pCurrentItem->childItem(0)->text() : QString();
+
+ /* Update the list of rules: */
+ m_rules = newRules;
+ m_pTableModel->setRules(m_rules);
+ sltAdjustTable();
+
+ /* Restore last chosen item: */
+ if (fHoldPosition && !strCurrentName.isEmpty())
+ {
+ for (int i = 0; i < m_pTableModel->childCount(); ++i)
+ {
+ QITableViewRow *pItem = m_pTableModel->childItem(i);
+ const QString strName = pItem ? pItem->childItem(0)->text() : QString();
+ if (strName == strCurrentName)
+ m_pTableView->setCurrentIndex(m_pTableModel->index(i, 0));
+ }
+ }
+}
+
+void UIPortForwardingTable::setGuestAddressHint(const QString &strHint)
+{
+ m_strGuestAddressHint = strHint;
+ m_pTableModel->setGuestAddressHint(m_strGuestAddressHint);
+}
+
+bool UIPortForwardingTable::validate() const
+{
+ /* Validate table: */
+ QList<NameData> names;
+ QList<UIPortForwardingDataUnique> rules;
+ for (int i = 0; i < m_pTableModel->rowCount(); ++i)
+ {
+ /* Some of variables: */
+ const NameData strName = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_Name), Qt::EditRole).value<NameData>();
+ const KNATProtocol enmProtocol = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_Protocol), Qt::EditRole).value<KNATProtocol>();
+ const PortData hostPort = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_HostPort), Qt::EditRole).value<PortData>().value();
+ const PortData guestPort = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_GuestPort), Qt::EditRole).value<PortData>().value();
+ const IpData strHostIp = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_HostIp), Qt::EditRole).value<IpData>();
+ const IpData strGuestIp = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_GuestIp), Qt::EditRole).value<IpData>();
+
+ /* If at least one port is 'zero': */
+ if (hostPort.value() == 0 || guestPort.value() == 0)
+ return msgCenter().warnAboutIncorrectPort(window());
+ /* If at least one address is incorrect: */
+ if (!( strHostIp.trimmed().isEmpty()
+ || RTNetIsIPv4AddrStr(strHostIp.toUtf8().constData())
+ || RTNetIsIPv6AddrStr(strHostIp.toUtf8().constData())
+ || RTNetStrIsIPv4AddrAny(strHostIp.toUtf8().constData())
+ || RTNetStrIsIPv6AddrAny(strHostIp.toUtf8().constData())))
+ return msgCenter().warnAboutIncorrectAddress(window());
+ if (!( strGuestIp.trimmed().isEmpty()
+ || RTNetIsIPv4AddrStr(strGuestIp.toUtf8().constData())
+ || RTNetIsIPv6AddrStr(strGuestIp.toUtf8().constData())
+ || RTNetStrIsIPv4AddrAny(strGuestIp.toUtf8().constData())
+ || RTNetStrIsIPv6AddrAny(strGuestIp.toUtf8().constData())))
+ return msgCenter().warnAboutIncorrectAddress(window());
+ /* If empty guest address is not allowed: */
+ if ( !m_fAllowEmptyGuestIPs
+ && strGuestIp.isEmpty())
+ return msgCenter().warnAboutEmptyGuestAddress(window());
+
+ /* Make sure non of the names were previosly used: */
+ if (!names.contains(strName))
+ names << strName;
+ else
+ return msgCenter().warnAboutNameShouldBeUnique(window());
+
+ /* Make sure non of the rules were previosly used: */
+ UIPortForwardingDataUnique rule(enmProtocol, hostPort, strHostIp);
+ if (!rules.contains(rule))
+ rules << rule;
+ else
+ return msgCenter().warnAboutRulesConflict(window());
+ }
+ /* True by default: */
+ return true;
+}
+
+void UIPortForwardingTable::makeSureEditorDataCommitted()
+{
+ m_pTableView->makeSureEditorDataCommitted();
+}
+
+bool UIPortForwardingTable::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Process table: */
+ if (pObject == m_pTableView)
+ {
+ /* Process different event-types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Show:
+ case QEvent::Resize:
+ {
+ /* Make sure layout requests really processed first of all: */
+ QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
+ /* Adjust table: */
+ sltAdjustTable();
+ break;
+ }
+ case QEvent::FocusIn:
+ case QEvent::FocusOut:
+ {
+ /* Update actions: */
+ sltCurrentChanged();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
+}
+
+void UIPortForwardingTable::retranslateUi()
+{
+ /* Table translations: */
+ m_pTableView->setWhatsThis(tr("Contains a list of port forwarding rules."));
+
+ /* Set action's text: */
+ m_pActionAdd->setText(tr("Add New Rule"));
+ m_pActionCopy->setText(tr("Copy Selected Rule"));
+ m_pActionRemove->setText(tr("Remove Selected Rule"));
+
+ m_pActionAdd->setWhatsThis(tr("Adds new port forwarding rule."));
+ m_pActionCopy->setWhatsThis(tr("Copies selected port forwarding rule."));
+ m_pActionRemove->setWhatsThis(tr("Removes selected port forwarding rule."));
+
+ m_pActionAdd->setToolTip(m_pActionAdd->whatsThis());
+ m_pActionCopy->setToolTip(m_pActionCopy->whatsThis());
+ m_pActionRemove->setToolTip(m_pActionRemove->whatsThis());
+}
+
+void UIPortForwardingTable::sltAddRule()
+{
+ m_pTableModel->addRule(QModelIndex());
+ m_pTableView->setFocus();
+ m_pTableView->setCurrentIndex(m_pTableModel->index(m_pTableModel->rowCount() - 1, 0));
+ sltCurrentChanged();
+ sltAdjustTable();
+}
+
+void UIPortForwardingTable::sltCopyRule()
+{
+ m_pTableModel->addRule(m_pTableView->currentIndex());
+ m_pTableView->setFocus();
+ m_pTableView->setCurrentIndex(m_pTableModel->index(m_pTableModel->rowCount() - 1, 0));
+ sltCurrentChanged();
+ sltAdjustTable();
+}
+
+void UIPortForwardingTable::sltRemoveRule()
+{
+ m_pTableModel->removeRule(m_pTableView->currentIndex());
+ m_pTableView->setFocus();
+ sltCurrentChanged();
+ sltAdjustTable();
+}
+
+void UIPortForwardingTable::sltTableDataChanged()
+{
+ m_fTableDataChanged = true;
+ emit sigDataChanged();
+}
+
+void UIPortForwardingTable::sltCurrentChanged()
+{
+ bool fTableFocused = m_pTableView->hasFocus();
+ bool fTableChildFocused = m_pTableView->findChildren<QWidget*>().contains(QApplication::focusWidget());
+ bool fTableOrChildFocused = fTableFocused || fTableChildFocused;
+ m_pActionCopy->setEnabled(m_pTableView->currentIndex().isValid() && fTableOrChildFocused);
+ m_pActionRemove->setEnabled(m_pTableView->currentIndex().isValid() && fTableOrChildFocused);
+}
+
+void UIPortForwardingTable::sltShowTableContexMenu(const QPoint &pos)
+{
+ /* Prepare context menu: */
+ QMenu menu(m_pTableView);
+ /* If some index is currently selected: */
+ if (m_pTableView->indexAt(pos).isValid())
+ {
+ menu.addAction(m_pActionCopy);
+ menu.addAction(m_pActionRemove);
+ }
+ /* If no valid index selected: */
+ else
+ {
+ menu.addAction(m_pActionAdd);
+ }
+ menu.exec(m_pTableView->viewport()->mapToGlobal(pos));
+}
+
+void UIPortForwardingTable::sltAdjustTable()
+{
+ /* If table is NOT empty: */
+ if (m_pTableModel->rowCount())
+ {
+ /* Resize table to contents size-hint and emit a spare place for first column: */
+ m_pTableView->resizeColumnsToContents();
+ uint uFullWidth = m_pTableView->viewport()->width();
+ for (uint u = 1; u < UIPortForwardingDataType_Max; ++u)
+ uFullWidth -= m_pTableView->horizontalHeader()->sectionSize(u);
+ m_pTableView->horizontalHeader()->resizeSection(UIPortForwardingDataType_Name, uFullWidth);
+ }
+ /* If table is empty: */
+ else
+ {
+ /* Resize table columns to be equal in size: */
+ uint uFullWidth = m_pTableView->viewport()->width();
+ for (uint u = 0; u < UIPortForwardingDataType_Max; ++u)
+ m_pTableView->horizontalHeader()->resizeSection(u, uFullWidth / UIPortForwardingDataType_Max);
+ }
+}
+
+void UIPortForwardingTable::prepare()
+{
+ /* Prepare layout: */
+ prepareLayout();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIPortForwardingTable::prepareLayout()
+{
+ /* Create layout: */
+ m_pLayout = new QHBoxLayout(this);
+ if (m_pLayout)
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ /* On macOS we can do a bit of smoothness: */
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLayout->setSpacing(3);
+#else
+ m_pLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 3);
+#endif
+
+ /* Prepare table-view: */
+ prepareTableView();
+
+ /* Prepare toolbar: */
+ prepareToolbar();
+ }
+}
+
+void UIPortForwardingTable::prepareTableView()
+{
+ /* Create table-view: */
+ m_pTableView = new UIPortForwardingView;
+ if (m_pTableView)
+ {
+ /* Configure table-view: */
+ m_pTableView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ m_pTableView->setTabKeyNavigation(false);
+ m_pTableView->verticalHeader()->hide();
+ m_pTableView->verticalHeader()->setDefaultSectionSize((int)(m_pTableView->verticalHeader()->minimumSectionSize() * 1.33));
+ m_pTableView->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_pTableView->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_pTableView->installEventFilter(this);
+
+ /* Prepare model: */
+ prepareTableModel();
+
+ /* Finish configure table-view (after model is configured): */
+ m_pTableView->setModel(m_pTableModel);
+ connect(m_pTableView, &UIPortForwardingView::sigCurrentChanged, this, &UIPortForwardingTable::sltCurrentChanged);
+ connect(m_pTableView, &UIPortForwardingView::customContextMenuRequested, this, &UIPortForwardingTable::sltShowTableContexMenu);
+
+ /* Prepare delegates: */
+ prepareTableDelegates();
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pTableView);
+ }
+}
+
+void UIPortForwardingTable::prepareTableModel()
+{
+ /* Create table-model: */
+ m_pTableModel = new UIPortForwardingModel(m_pTableView, m_rules);
+ if (m_pTableModel)
+ {
+ /* Configure table-model: */
+ connect(m_pTableModel, &UIPortForwardingModel::dataChanged,
+ this, &UIPortForwardingTable::sltTableDataChanged);
+ connect(m_pTableModel, &UIPortForwardingModel::rowsInserted,
+ this, &UIPortForwardingTable::sltTableDataChanged);
+ connect(m_pTableModel, &UIPortForwardingModel::rowsRemoved,
+ this, &UIPortForwardingTable::sltTableDataChanged);
+ }
+}
+
+void UIPortForwardingTable::prepareTableDelegates()
+{
+ /* We certainly have abstract item delegate: */
+ QAbstractItemDelegate *pAbstractItemDelegate = m_pTableView->itemDelegate();
+ if (pAbstractItemDelegate)
+ {
+ /* But is this also styled item delegate? */
+ QStyledItemDelegate *pStyledItemDelegate = qobject_cast<QStyledItemDelegate*>(pAbstractItemDelegate);
+ if (pStyledItemDelegate)
+ {
+ /* Create new item editor factory: */
+ m_pItemEditorFactory = new QItemEditorFactory;
+ if (m_pItemEditorFactory)
+ {
+ /* Register NameEditor as the NameData editor: */
+ int iNameId = qRegisterMetaType<NameData>();
+ QStandardItemEditorCreator<NameEditor> *pNameEditorItemCreator = new QStandardItemEditorCreator<NameEditor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iNameId, pNameEditorItemCreator);
+
+ /* Register ProtocolEditor as the KNATProtocol editor: */
+ int iProtocolId = qRegisterMetaType<KNATProtocol>();
+ QStandardItemEditorCreator<ProtocolEditor> *pProtocolEditorItemCreator = new QStandardItemEditorCreator<ProtocolEditor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iProtocolId, pProtocolEditorItemCreator);
+
+ /* Register IPv4Editor/IPv6Editor as the IpData editor: */
+ int iIpId = qRegisterMetaType<IpData>();
+ if (!m_fIPv6)
+ {
+ QStandardItemEditorCreator<IPv4Editor> *pIPv4EditorItemCreator = new QStandardItemEditorCreator<IPv4Editor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iIpId, pIPv4EditorItemCreator);
+ }
+ else
+ {
+ QStandardItemEditorCreator<IPv6Editor> *pIPv6EditorItemCreator = new QStandardItemEditorCreator<IPv6Editor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iIpId, pIPv6EditorItemCreator);
+ }
+
+ /* Register PortEditor as the PortData editor: */
+ int iPortId = qRegisterMetaType<PortData>();
+ QStandardItemEditorCreator<PortEditor> *pPortEditorItemCreator = new QStandardItemEditorCreator<PortEditor>();
+ m_pItemEditorFactory->registerEditor((QVariant::Type)iPortId, pPortEditorItemCreator);
+
+ /* Set newly created item editor factory for table delegate: */
+ pStyledItemDelegate->setItemEditorFactory(m_pItemEditorFactory);
+ }
+ }
+ }
+}
+
+void UIPortForwardingTable::prepareToolbar()
+{
+ /* Create toolbar: */
+ m_pToolBar = new QIToolBar;
+ if (m_pToolBar)
+ {
+ /* Determine icon metric: */
+ const QStyle *pStyle = QApplication::style();
+ const int iIconMetric = pStyle->pixelMetric(QStyle::PM_SmallIconSize);
+
+ /* Configure toolbar: */
+ m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
+ m_pToolBar->setOrientation(Qt::Vertical);
+
+ /* Create 'Add' action: */
+ m_pActionAdd = new QAction(this);
+ if (m_pActionAdd)
+ {
+ /* Configure action: */
+ m_pActionAdd->setShortcut(QKeySequence("Ins"));
+ m_pActionAdd->setIcon(UIIconPool::iconSet(":/controller_add_16px.png", ":/controller_add_disabled_16px.png"));
+ connect(m_pActionAdd, &QAction::triggered, this, &UIPortForwardingTable::sltAddRule);
+ m_pToolBar->addAction(m_pActionAdd);
+ }
+
+ /* Create 'Copy' action: */
+ m_pActionCopy = new QAction(this);
+ if (m_pActionCopy)
+ {
+ /* Configure action: */
+ m_pActionCopy->setIcon(UIIconPool::iconSet(":/controller_add_16px.png", ":/controller_add_disabled_16px.png"));
+ connect(m_pActionCopy, &QAction::triggered, this, &UIPortForwardingTable::sltCopyRule);
+ }
+
+ /* Create 'Remove' action: */
+ m_pActionRemove = new QAction(this);
+ if (m_pActionRemove)
+ {
+ /* Configure action: */
+ m_pActionRemove->setShortcut(QKeySequence("Del"));
+ m_pActionRemove->setIcon(UIIconPool::iconSet(":/controller_remove_16px.png", ":/controller_remove_disabled_16px.png"));
+ connect(m_pActionRemove, &QAction::triggered, this, &UIPortForwardingTable::sltRemoveRule);
+ m_pToolBar->addAction(m_pActionRemove);
+ }
+
+ /* Add into layout: */
+ m_pLayout->addWidget(m_pToolBar);
+ }
+}
+
+void UIPortForwardingTable::cleanup()
+{
+ delete m_pItemEditorFactory;
+ m_pItemEditorFactory = 0;
+}
+
+
+#include "UIPortForwardingTable.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIPortForwardingTable.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIPortForwardingTable.h
new file mode 100644
index 00000000..d7c5e01b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIPortForwardingTable.h
@@ -0,0 +1,323 @@
+/* $Id: UIPortForwardingTable.h $ */
+/** @file
+ * VBox Qt GUI - UIPortForwardingTable class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIPortForwardingTable_h
+#define FEQT_INCLUDED_SRC_widgets_UIPortForwardingTable_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+#include <QWidget>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QAction;
+class QHBoxLayout;
+class QItemEditorFactory;
+class QIDialogButtonBox;
+class QITableView;
+class UIPortForwardingModel;
+class QIToolBar;
+
+/** QString subclass used to distinguish name data from simple QString. */
+class NameData : public QString
+{
+public:
+
+ /** Constructs null name data. */
+ NameData() : QString() {}
+ /** Constructs name data passing @a strName to the base-class. */
+ NameData(const QString &strName) : QString(strName) {}
+};
+Q_DECLARE_METATYPE(NameData);
+
+
+/** QString subclass used to distinguish IP data from simple QString. */
+class IpData : public QString
+{
+public:
+
+ /** Constructs null IP data. */
+ IpData() : QString() {}
+ /** Constructs name data passing @a strIp to the base-class. */
+ IpData(const QString &strIp) : QString(strIp) {}
+};
+Q_DECLARE_METATYPE(IpData);
+
+
+/** Wrapper for ushort used to distinguish port data from simple ushort. */
+class PortData
+{
+public:
+
+ /** Constructs null port data. */
+ PortData() : m_uValue(0) {}
+ /** Constructs port data based on @a uValue. */
+ PortData(ushort uValue) : m_uValue(uValue) {}
+
+ /** Returns whether this port data is equal to @a another. */
+ bool operator==(const PortData &another) const { return m_uValue == another.m_uValue; }
+
+ /** Returns serialized port data value. */
+ ushort value() const { return m_uValue; }
+
+private:
+
+ /** Holds the port data value. */
+ ushort m_uValue;
+};
+Q_DECLARE_METATYPE(PortData);
+
+
+/** Port Forwarding Rule structure. */
+struct UIDataPortForwardingRule
+{
+ /** Constructs data. */
+ UIDataPortForwardingRule()
+ : name(QString())
+ , protocol(KNATProtocol_UDP)
+ , hostIp(IpData())
+ , hostPort(PortData())
+ , guestIp(IpData())
+ , guestPort(PortData())
+ {}
+
+ /** Constructs data on the basis of passed arguments.
+ * @param strName Brings the rule name.
+ * @param enmProtocol Brings the rule protocol.
+ * @param strHostIP Brings the rule host IP.
+ * @param uHostPort Brings the rule host port.
+ * @param strGuestIP Brings the rule guest IP.
+ * @param uGuestPort Brings the rule guest port. */
+ UIDataPortForwardingRule(const NameData &strName,
+ KNATProtocol enmProtocol,
+ const IpData &strHostIP,
+ PortData uHostPort,
+ const IpData &strGuestIP,
+ PortData uGuestPort)
+ : name(strName)
+ , protocol(enmProtocol)
+ , hostIp(strHostIP)
+ , hostPort(uHostPort)
+ , guestIp(strGuestIP)
+ , guestPort(uGuestPort)
+ {}
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool equal(const UIDataPortForwardingRule &other) const
+ {
+ return true
+ && (name == other.name)
+ && (protocol == other.protocol)
+ && (hostIp == other.hostIp)
+ && (hostPort == other.hostPort)
+ && (guestIp == other.guestIp)
+ && (guestPort == other.guestPort)
+ ;
+ }
+
+ /** Returns whether the @a other passed data is equal to this one. */
+ bool operator==(const UIDataPortForwardingRule &other) const { return equal(other); }
+ /** Returns whether the @a other passed data is different from this one. */
+ bool operator!=(const UIDataPortForwardingRule &other) const { return !equal(other); }
+
+ /** Holds the rule name. */
+ NameData name;
+ /** Holds the rule protocol. */
+ KNATProtocol protocol;
+ /** Holds the rule host IP. */
+ IpData hostIp;
+ /** Holds the rule host port. */
+ PortData hostPort;
+ /** Holds the rule guest IP. */
+ IpData guestIp;
+ /** Holds the rule guest port. */
+ PortData guestPort;
+};
+
+/** Port Forwarding Data list. */
+typedef QList<UIDataPortForwardingRule> UIPortForwardingDataList;
+
+
+/** Unique part of port forwarding data. */
+struct UIPortForwardingDataUnique
+{
+ /** Constructs unique port forwarding data based on
+ * @a enmProtocol, @a uHostPort and @a uHostPort. */
+ UIPortForwardingDataUnique(KNATProtocol enmProtocol,
+ PortData uHostPort,
+ const IpData &strHostIp)
+ : protocol(enmProtocol)
+ , hostPort(uHostPort)
+ , hostIp(strHostIp)
+ {}
+
+ /** Returns whether this port data is equal to @a another. */
+ bool operator==(const UIPortForwardingDataUnique &another) const
+ {
+ return protocol == another.protocol
+ && hostPort == another.hostPort
+ && ( hostIp.isEmpty() || another.hostIp.isEmpty()
+ || hostIp == "0.0.0.0" || another.hostIp == "0.0.0.0"
+ || hostIp == another.hostIp);
+ }
+
+ /** Holds the port forwarding data protocol type. */
+ KNATProtocol protocol;
+ /** Holds the port forwarding data host port. */
+ PortData hostPort;
+ /** Holds the port forwarding data host IP. */
+ IpData hostIp;
+};
+
+
+/** QWidget subclass representig Port Forwarding table. */
+class SHARED_LIBRARY_STUFF UIPortForwardingTable : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about table data changed. */
+ void sigDataChanged();
+
+public:
+
+ /** Constructs Port Forwarding table.
+ * @param rules Brings the current list of Port Forwarding rules.
+ * @param fIPv6 Brings whether this table contains IPv6 rules, not IPv4.
+ * @param fAllowEmptyGuestIPs Brings whether this table allows empty guest IPs. */
+ UIPortForwardingTable(const UIPortForwardingDataList &rules, bool fIPv6, bool fAllowEmptyGuestIPs);
+ /** Destructs Port Forwarding table. */
+ virtual ~UIPortForwardingTable() RT_OVERRIDE;
+ /** Returns the list of port forwarding rules. */
+ UIPortForwardingDataList rules() const;
+ /** Defines the list of port forwarding @a newRules.
+ * @param fHoldPosition Holds whether we should try to keep
+ * port forwarding rule position intact. */
+ void setRules(const UIPortForwardingDataList &newRules,
+ bool fHoldPosition = false);
+
+ /** Defines guest address @a strHint. */
+ void setGuestAddressHint(const QString &strHint);
+
+ /** Validates the table. */
+ bool validate() const;
+
+ /** Returns whether the table data was changed. */
+ bool isChanged() const { return m_fTableDataChanged; }
+
+ /** Makes sure current editor data committed. */
+ void makeSureEditorDataCommitted();
+
+protected:
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+private slots:
+
+ /** Adds the rule. */
+ void sltAddRule();
+ /** Copies the rule. */
+ void sltCopyRule();
+ /** Removes the rule. */
+ void sltRemoveRule();
+
+ /** Marks table data as changed. */
+ void sltTableDataChanged();
+
+ /** Handles current item change. */
+ void sltCurrentChanged();
+ /** Handles request to show context-menu in certain @a position. */
+ void sltShowTableContexMenu(const QPoint &position);
+ /** Adjusts table column sizes. */
+ void sltAdjustTable();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares layout. */
+ void prepareLayout();
+ /** Prepares table-view. */
+ void prepareTableView();
+ /** Prepares table-model. */
+ void prepareTableModel();
+ /** Prepares table-delegates. */
+ void prepareTableDelegates();
+ /** Prepares toolbar. */
+ void prepareToolbar();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Holds the list of port forwarding rules. */
+ UIPortForwardingDataList m_rules;
+
+ /** Holds the guest address hint. */
+ QString m_strGuestAddressHint;
+
+ /** Holds whether this table contains IPv6 rules, not IPv4. */
+ bool m_fIPv6 : 1;
+ /** Holds whether this table allows empty guest IPs. */
+ bool m_fAllowEmptyGuestIPs : 1;
+ /** Holds whether this table data was changed. */
+ bool m_fTableDataChanged : 1;
+
+ /** Holds the layout instance. */
+ QHBoxLayout *m_pLayout;
+ /** Holds the table-view instance. */
+ QITableView *m_pTableView;
+ /** Holds the tool-bar instance. */
+ QIToolBar *m_pToolBar;
+ /** Holds the item editor factory instance. */
+ QItemEditorFactory *m_pItemEditorFactory;
+
+ /** Holds the table-model instance. */
+ UIPortForwardingModel *m_pTableModel;
+
+ /** Holds the Add action instance. */
+ QAction *m_pActionAdd;
+ /** Holds the Copy action instance. */
+ QAction *m_pActionCopy;
+ /** Holds the Remove action instance. */
+ QAction *m_pActionRemove;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIPortForwardingTable_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIProgressDialog.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIProgressDialog.cpp
new file mode 100644
index 00000000..93f40dae
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIProgressDialog.cpp
@@ -0,0 +1,559 @@
+/* $Id: UIProgressDialog.cpp $ */
+/** @file
+ * VBox Qt GUI - UIProgressDialog class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCloseEvent>
+#include <QEventLoop>
+#include <QProgressBar>
+#include <QTime>
+#include <QTimer>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "QILabel.h"
+#include "UIErrorString.h"
+#include "UIExtraDataManager.h"
+#include "UIMainEventListener.h"
+#include "UIModalWindowManager.h"
+#include "UIProgressDialog.h"
+#include "UIProgressEventHandler.h"
+#include "UISpecialControls.h"
+#include "UITranslator.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+/* COM includes: */
+#include "CEventListener.h"
+#include "CEventSource.h"
+#include "CProgress.h"
+
+
+const char *UIProgressDialog::m_spcszOpDescTpl = "%1 ... (%2/%3)";
+
+UIProgressDialog::UIProgressDialog(CProgress &comProgress,
+ const QString &strTitle,
+ QPixmap *pImage /* = 0 */,
+ int cMinDuration /* = 2000 */,
+ QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI2<QIDialog>(pParent, Qt::MSWindowsFixedSizeDialogHint | Qt::WindowTitleHint)
+ , m_comProgress(comProgress)
+ , m_strTitle(strTitle)
+ , m_pImage(pImage)
+ , m_cMinDuration(cMinDuration)
+ , m_fLegacyHandling(gEDataManager->legacyProgressHandlingRequested())
+ , m_pLabelImage(0)
+ , m_pLabelDescription(0)
+ , m_pProgressBar(0)
+ , m_pButtonCancel(0)
+ , m_pLabelEta(0)
+ , m_cOperations(m_comProgress.GetOperationCount())
+ , m_uCurrentOperation(m_comProgress.GetOperation() + 1)
+ , m_uCurrentOperationWeight(m_comProgress.GetOperationWeight())
+ , m_fCancelEnabled(false)
+ , m_fEnded(false)
+ , m_pEventHandler(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+UIProgressDialog::~UIProgressDialog()
+{
+ /* Cleanup: */
+ cleanup();
+}
+
+void UIProgressDialog::retranslateUi()
+{
+ m_pButtonCancel->setText(tr("&Cancel"));
+ m_pButtonCancel->setToolTip(tr("Cancel the current operation"));
+}
+
+int UIProgressDialog::run(int cRefreshInterval)
+{
+ /* Make sure progress hasn't finished already: */
+ if (!m_comProgress.isOk() || m_comProgress.GetCompleted())
+ {
+ /* Progress completed? */
+ if (m_comProgress.isOk())
+ return Accepted;
+ /* Or aborted? */
+ else
+ return Rejected;
+ }
+
+ /* Start refresh timer (if necessary): */
+ int id = 0;
+ if (m_fLegacyHandling)
+ id = startTimer(cRefreshInterval);
+
+ /* Set busy cursor.
+ * We don't do this on the Mac, cause regarding the design rules of
+ * Apple there is no busy window behavior. A window should always be
+ * responsive and it is in our case (We show the progress dialog bar). */
+#ifndef VBOX_WS_MAC
+ if (m_fCancelEnabled)
+ QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
+ else
+ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+#endif /* VBOX_WS_MAC */
+
+ /* Create a local event-loop: */
+ {
+ /* Guard ourself for the case
+ * we destroyed ourself in our event-loop: */
+ QPointer<UIProgressDialog> guard = this;
+
+ /* Holds the modal loop, but don't show the window immediately: */
+ execute(false);
+
+ /* Are we still valid? */
+ if (guard.isNull())
+ return Rejected;
+ }
+
+ /* Kill refresh timer (if necessary): */
+ if (m_fLegacyHandling)
+ killTimer(id);
+
+#ifndef VBOX_WS_MAC
+ /* Reset the busy cursor */
+ QApplication::restoreOverrideCursor();
+#endif /* VBOX_WS_MAC */
+
+ return result();
+}
+
+void UIProgressDialog::show()
+{
+ /* We should not show progress-dialog
+ * if it was already finalized but not yet closed.
+ * This could happens in case of some other
+ * modal dialog prevents our event-loop from
+ * being exit overlapping 'this'. */
+ if (!m_fEnded)
+ QIDialog::show();
+}
+
+void UIProgressDialog::reject()
+{
+ if (m_fCancelEnabled)
+ sltCancelOperation();
+}
+
+void UIProgressDialog::timerEvent(QTimerEvent *)
+{
+ /* Call the timer event handling delegate: */
+ handleTimerEvent();
+}
+
+void UIProgressDialog::closeEvent(QCloseEvent *pEvent)
+{
+ if (m_fCancelEnabled)
+ sltCancelOperation();
+ else
+ pEvent->ignore();
+}
+
+void UIProgressDialog::sltHandleProgressPercentageChange(const QUuid &, const int iPercent)
+{
+ /* New mode only: */
+ AssertReturnVoid(!m_fLegacyHandling);
+
+ /* Update progress: */
+ updateProgressState();
+ updateProgressPercentage(iPercent);
+}
+
+void UIProgressDialog::sltHandleProgressTaskComplete(const QUuid &)
+{
+ /* New mode only: */
+ AssertReturnVoid(!m_fLegacyHandling);
+
+ /* If progress-dialog is not yet ended but progress is aborted or completed: */
+ if (!m_fEnded && (!m_comProgress.isOk() || m_comProgress.GetCompleted()))
+ {
+ /* Set progress to 100%: */
+ updateProgressPercentage(100);
+
+ /* Try to close the dialog: */
+ closeProgressDialog();
+ }
+}
+
+void UIProgressDialog::sltHandleWindowStackChange()
+{
+ /* If progress-dialog is not yet ended but progress is aborted or completed: */
+ if (!m_fEnded && (!m_comProgress.isOk() || m_comProgress.GetCompleted()))
+ {
+ /* Try to close the dialog: */
+ closeProgressDialog();
+ }
+}
+
+void UIProgressDialog::sltCancelOperation()
+{
+ m_pButtonCancel->setEnabled(false);
+ m_comProgress.Cancel();
+}
+
+void UIProgressDialog::prepare()
+{
+ /* Setup dialog: */
+ if (m_strTitle.isNull())
+ setWindowTitle(m_comProgress.GetDescription());
+ else
+ setWindowTitle(QString("%1: %2").arg(m_strTitle, m_comProgress.GetDescription()));
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+#ifdef VBOX_WS_MAC
+ ::darwinSetHidesAllTitleButtons(this);
+#endif
+
+ /* Make sure dialog is handling window stack changes: */
+ connect(&windowManager(), &UIModalWindowManager::sigStackChanged,
+ this, &UIProgressDialog::sltHandleWindowStackChange);
+
+ /* Prepare: */
+ prepareEventHandler();
+ prepareWidgets();
+}
+
+void UIProgressDialog::prepareEventHandler()
+{
+ if (!m_fLegacyHandling)
+ {
+ /* Create CProgress event handler: */
+ m_pEventHandler = new UIProgressEventHandler(this, m_comProgress);
+ connect(m_pEventHandler, &UIProgressEventHandler::sigProgressPercentageChange,
+ this, &UIProgressDialog::sltHandleProgressPercentageChange);
+ connect(m_pEventHandler, &UIProgressEventHandler::sigProgressTaskComplete,
+ this, &UIProgressDialog::sltHandleProgressTaskComplete);
+ }
+}
+
+void UIProgressDialog::prepareWidgets()
+{
+ /* Create main layout: */
+ QHBoxLayout *pMainLayout = new QHBoxLayout(this);
+ AssertPtrReturnVoid(pMainLayout);
+ {
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ if (m_pImage)
+ pMainLayout->setContentsMargins(30, 15, 30, 15);
+ else
+ pMainLayout->setContentsMargins(6, 6, 6, 6);
+#endif
+
+ /* If there is image: */
+ if (m_pImage)
+ {
+ /* Create image label: */
+ m_pLabelImage = new QLabel;
+ AssertPtrReturnVoid(m_pLabelImage);
+ {
+ /* Configure label: */
+ m_pLabelImage->setPixmap(*m_pImage);
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pLabelImage);
+ }
+ }
+
+ /* Create description layout: */
+ QVBoxLayout *pDescriptionLayout = new QVBoxLayout;
+ AssertPtrReturnVoid(pDescriptionLayout);
+ {
+ /* Configure layout: */
+ pDescriptionLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Add stretch: */
+ pDescriptionLayout->addStretch(1);
+
+ /* Create description label: */
+ m_pLabelDescription = new QILabel;
+ AssertPtrReturnVoid(m_pLabelDescription);
+ {
+ /* Configure label: */
+ if (m_cOperations > 1)
+ m_pLabelDescription->setText(QString(m_spcszOpDescTpl)
+ .arg(m_comProgress.GetOperationDescription())
+ .arg(m_uCurrentOperation).arg(m_cOperations));
+ else
+ m_pLabelDescription->setText(QString("%1 ...")
+ .arg(m_comProgress.GetOperationDescription()));
+
+ /* Add into layout: */
+ pDescriptionLayout->addWidget(m_pLabelDescription, 0, Qt::AlignHCenter);
+ }
+
+ /* Create proggress layout: */
+ QHBoxLayout *pProgressLayout = new QHBoxLayout;
+ AssertPtrReturnVoid(pProgressLayout);
+ {
+ /* Configure layout: */
+ pProgressLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create progress-bar: */
+ m_pProgressBar = new QProgressBar;
+ AssertPtrReturnVoid(m_pProgressBar);
+ {
+ /* Configure progress-bar: */
+ // WORKAROUND:
+ // Based on agreement implemented in r131088 and r131090,
+ // if progress has just one operation with weight equal to 1,
+ // we should make it "infinite" by setting maximum to minimum.
+ // But be aware that this can and will be overridden by
+ // updated progress percentage if it's changing.
+ if (m_cOperations == 1 && m_uCurrentOperationWeight == 1)
+ m_pProgressBar->setMaximum(0);
+ else
+ m_pProgressBar->setMaximum(100);
+ m_pProgressBar->setValue(0);
+
+ /* Add into layout: */
+ pProgressLayout->addWidget(m_pProgressBar, 0, Qt::AlignVCenter);
+ }
+
+ /* Create cancel button: */
+ m_pButtonCancel = new UIMiniCancelButton;
+ AssertPtrReturnVoid(m_pButtonCancel);
+ {
+ /* Configure cancel button: */
+ m_fCancelEnabled = m_comProgress.GetCancelable();
+ m_pButtonCancel->setEnabled(m_fCancelEnabled);
+ m_pButtonCancel->setFocusPolicy(Qt::ClickFocus);
+ connect(m_pButtonCancel, &UIMiniCancelButton::clicked, this, &UIProgressDialog::sltCancelOperation);
+
+ /* Add into layout: */
+ pProgressLayout->addWidget(m_pButtonCancel, 0, Qt::AlignVCenter);
+ }
+
+ /* Add into layout: */
+ pDescriptionLayout->addLayout(pProgressLayout);
+ }
+
+ /* Create estimation label: */
+ m_pLabelEta = new QILabel;
+ {
+ /* Add into layout: */
+ pDescriptionLayout->addWidget(m_pLabelEta, 0, Qt::AlignLeft | Qt::AlignVCenter);
+ }
+
+ /* Add stretch: */
+ pDescriptionLayout->addStretch(1);
+
+ /* Add into layout: */
+ pMainLayout->addLayout(pDescriptionLayout);
+ }
+ }
+
+ /* Translate finally: */
+ retranslateUi();
+
+ /* The progress dialog will be shown automatically after
+ * the duration is over if progress is not finished yet. */
+ QTimer::singleShot(m_cMinDuration, this, SLOT(show()));
+}
+
+void UIProgressDialog::cleanupWidgets()
+{
+ /* Nothing for now. */
+}
+
+void UIProgressDialog::cleanupEventHandler()
+{
+ if (!m_fLegacyHandling)
+ {
+ /* Destroy CProgress event handler: */
+ delete m_pEventHandler;
+ m_pEventHandler = 0;
+ }
+}
+
+void UIProgressDialog::cleanup()
+{
+ /* Wait for CProgress to complete: */
+ m_comProgress.WaitForCompletion(-1);
+
+ /* Call the timer event handling delegate: */
+ if (m_fLegacyHandling)
+ handleTimerEvent();
+
+ /* Cleanup: */
+ cleanupEventHandler();
+ cleanupWidgets();
+}
+
+void UIProgressDialog::updateProgressState()
+{
+ /* Mark progress canceled if so: */
+ if (m_comProgress.GetCanceled())
+ m_pLabelEta->setText(tr("Canceling..."));
+ /* Update the progress dialog otherwise: */
+ else
+ {
+ /* Update ETA: */
+ const long iNewTime = m_comProgress.GetTimeRemaining();
+ long iSeconds;
+ long iMinutes;
+ long iHours;
+ long iDays;
+
+ iSeconds = iNewTime < 0 ? 0 : iNewTime;
+ iMinutes = iSeconds / 60;
+ iSeconds -= iMinutes * 60;
+ iHours = iMinutes / 60;
+ iMinutes -= iHours * 60;
+ iDays = iHours / 24;
+ iHours -= iDays * 24;
+
+ const QString strDays = UITranslator::daysToString(iDays);
+ const QString strHours = UITranslator::hoursToString(iHours);
+ const QString strMinutes = UITranslator::minutesToString(iMinutes);
+ const QString strSeconds = UITranslator::secondsToString(iSeconds);
+
+ const QString strTwoComp = tr("%1, %2 remaining", "You may wish to translate this more like \"Time remaining: %1, %2\"");
+ const QString strOneComp = tr("%1 remaining", "You may wish to translate this more like \"Time remaining: %1\"");
+
+ if (iDays > 1 && iHours > 0)
+ m_pLabelEta->setText(strTwoComp.arg(strDays).arg(strHours));
+ else if (iDays > 1)
+ m_pLabelEta->setText(strOneComp.arg(strDays));
+ else if (iDays > 0 && iHours > 0)
+ m_pLabelEta->setText(strTwoComp.arg(strDays).arg(strHours));
+ else if (iDays > 0 && iMinutes > 5)
+ m_pLabelEta->setText(strTwoComp.arg(strDays).arg(strMinutes));
+ else if (iDays > 0)
+ m_pLabelEta->setText(strOneComp.arg(strDays));
+ else if (iHours > 2)
+ m_pLabelEta->setText(strOneComp.arg(strHours));
+ else if (iHours > 0 && iMinutes > 0)
+ m_pLabelEta->setText(strTwoComp.arg(strHours).arg(strMinutes));
+ else if (iHours > 0)
+ m_pLabelEta->setText(strOneComp.arg(strHours));
+ else if (iMinutes > 2)
+ m_pLabelEta->setText(strOneComp.arg(strMinutes));
+ else if (iMinutes > 0 && iSeconds > 5)
+ m_pLabelEta->setText(strTwoComp.arg(strMinutes).arg(strSeconds));
+ else if (iMinutes > 0)
+ m_pLabelEta->setText(strOneComp.arg(strMinutes));
+ else if (iSeconds > 5)
+ m_pLabelEta->setText(strOneComp.arg(strSeconds));
+ else if (iSeconds > 0)
+ m_pLabelEta->setText(tr("A few seconds remaining"));
+ else
+ m_pLabelEta->clear();
+
+ /* Then operation text (if changed): */
+ ulong uNewOp = m_comProgress.GetOperation() + 1;
+ if (uNewOp != m_uCurrentOperation)
+ {
+ m_uCurrentOperation = uNewOp;
+ m_uCurrentOperationWeight = m_comProgress.GetOperationWeight();
+ m_pLabelDescription->setText(QString(m_spcszOpDescTpl)
+ .arg(m_comProgress.GetOperationDescription())
+ .arg(m_uCurrentOperation).arg(m_cOperations));
+ }
+
+ /* Then cancel button: */
+ m_fCancelEnabled = m_comProgress.GetCancelable();
+ m_pButtonCancel->setEnabled(m_fCancelEnabled);
+ }
+}
+
+void UIProgressDialog::updateProgressPercentage(int iPercent /* = -1 */)
+{
+ /* Handle default call: */
+ if (iPercent == -1)
+ iPercent = m_comProgress.GetPercent();
+
+ /* Make sure percentage is reflected properly
+ * if progress was "infinite" initially: */
+ if ( m_pProgressBar->maximum() == 0
+ && iPercent > 0 && iPercent < 100)
+ m_pProgressBar->setMaximum(100);
+
+ /* Update operation percentage: */
+ m_pProgressBar->setValue(iPercent);
+
+ /* Notify listeners about the operation progress update: */
+ emit sigProgressChange(m_cOperations, m_comProgress.GetOperationDescription(),
+ m_comProgress.GetOperation() + 1, iPercent);
+}
+
+void UIProgressDialog::closeProgressDialog()
+{
+ /* If window is on the top of the stack: */
+ if (windowManager().isWindowOnTheTopOfTheModalWindowStack(this))
+ {
+ /* Progress completed? */
+ if (m_comProgress.isOk())
+ done(Accepted);
+ /* Or aborted? */
+ else
+ done(Rejected);
+
+ /* Mark progress-dialog finished: */
+ m_fEnded = true;
+ }
+}
+
+void UIProgressDialog::handleTimerEvent()
+{
+ /* Old mode only: */
+ AssertReturnVoid(m_fLegacyHandling);
+
+ /* If progress-dialog is ended: */
+ if (m_fEnded)
+ {
+ // WORKAROUND:
+ // We should hide progress-dialog if it was already ended but not yet closed. This could happen
+ // in case if some other modal dialog prevents our event-loop from being exit overlapping 'this'.
+ /* If window is on the top of the stack and still shown: */
+ if (!isHidden() && windowManager().isWindowOnTheTopOfTheModalWindowStack(this))
+ hide();
+
+ return;
+ }
+
+ /* If progress-dialog is not yet ended but progress is aborted or completed: */
+ if (!m_comProgress.isOk() || m_comProgress.GetCompleted())
+ {
+ /* Set progress to 100%: */
+ updateProgressPercentage(100);
+
+ /* Try to close the dialog: */
+ return closeProgressDialog();
+ }
+
+ /* Update progress: */
+ updateProgressState();
+ updateProgressPercentage();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIProgressDialog.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIProgressDialog.h
new file mode 100644
index 00000000..6aef2fb9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIProgressDialog.h
@@ -0,0 +1,181 @@
+/* $Id: UIProgressDialog.h $ */
+/** @file
+ * VBox Qt GUI - UIProgressDialog class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIProgressDialog_h
+#define FEQT_INCLUDED_SRC_widgets_UIProgressDialog_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class QProgressBar;
+class QILabel;
+class UIMiniCancelButton;
+class UIProgressEventHandler;
+class CProgress;
+
+/** QProgressDialog enhancement that allows to:
+ * 1) prevent closing the dialog when it has no cancel button;
+ * 2) effectively track the IProgress object completion (w/o using
+ * IProgress::waitForCompletion() and w/o blocking the UI thread in any other way for too long).
+ * @note The CProgress instance is passed as a non-const reference to the constructor (to memorize COM errors if they happen),
+ * and therefore must not be destroyed before the created UIProgressDialog instance is destroyed. */
+class SHARED_LIBRARY_STUFF UIProgressDialog : public QIWithRetranslateUI2<QIDialog>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about wrapped CProgress change.
+ * @param iOperations Brings the number of operations CProgress have.
+ * @param strOperation Brings the description of the current CProgress operation.
+ * @param iOperation Brings the index of the current CProgress operation.
+ * @param iPercent Brings the percentage of the current CProgress operation. */
+ void sigProgressChange(ulong iOperations, QString strOperation,
+ ulong iOperation, ulong iPercent);
+
+public:
+
+ /** Constructs progress-dialog passing @a pParent to the base-class.
+ * @param comProgress Brings the progress reference.
+ * @param strTitle Brings the progress-dialog title.
+ * @param pImage Brings the progress-dialog image.
+ * @param cMinDuration Brings the minimum duration before the progress-dialog is shown. */
+ UIProgressDialog(CProgress &comProgress, const QString &strTitle,
+ QPixmap *pImage = 0, int cMinDuration = 2000, QWidget *pParent = 0);
+ /** Destructs progress-dialog. */
+ virtual ~UIProgressDialog() RT_OVERRIDE;
+
+ /** Executes the progress-dialog within its loop with passed @a iRefreshInterval. */
+ int run(int iRefreshInterval);
+
+public slots:
+
+ /** Shows progress-dialog if it's not yet shown. */
+ void show();
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Rejects dialog. */
+ virtual void reject() RT_OVERRIDE;
+
+ /** Handles timer @a pEvent. */
+ virtual void timerEvent(QTimerEvent *pEvent) RT_OVERRIDE;
+ /** Handles close @a pEvent. */
+ virtual void closeEvent(QCloseEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles percentage changed event for progress with @a uProgressId to @a iPercent. */
+ void sltHandleProgressPercentageChange(const QUuid &uProgressId, const int iPercent);
+ /** Handles task completed event for progress with @a uProgressId. */
+ void sltHandleProgressTaskComplete(const QUuid &uProgressId);
+
+ /** Handles window stack changed signal. */
+ void sltHandleWindowStackChange();
+
+ /** Handles request to cancel operation. */
+ void sltCancelOperation();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares event handler. */
+ void prepareEventHandler();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Cleanups widgets. */
+ void cleanupWidgets();
+ /** Cleanups event handler. */
+ void cleanupEventHandler();
+ /** Cleanups all. */
+ void cleanup();
+
+ /** Updates progress-dialog state. */
+ void updateProgressState();
+ /** Updates progress-dialog percentage. */
+ void updateProgressPercentage(int iPercent = -1);
+
+ /** Closes progress dialog (if possible). */
+ void closeProgressDialog();
+
+ /** Performes timer event handling. */
+ void handleTimerEvent();
+
+ /** Holds the progress reference. */
+ CProgress &m_comProgress;
+ /** Holds the progress-dialog title. */
+ QString m_strTitle;
+ /** Holds the dialog image. */
+ QPixmap *m_pImage;
+ /** Holds the minimum duration before the progress-dialog is shown. */
+ int m_cMinDuration;
+
+ /** Holds whether legacy handling is requested for this progress. */
+ bool m_fLegacyHandling;
+
+ /** Holds the image label instance. */
+ QLabel *m_pLabelImage;
+ /** Holds the description label instance. */
+ QILabel *m_pLabelDescription;
+ /** Holds the progress-bar instance. */
+ QProgressBar *m_pProgressBar;
+ /** Holds the cancel button instance. */
+ UIMiniCancelButton *m_pButtonCancel;
+ /** Holds the ETA label instance. */
+ QILabel *m_pLabelEta;
+
+ /** Holds the amount of operations. */
+ const ulong m_cOperations;
+ /** Holds the number of current operation. */
+ ulong m_uCurrentOperation;
+ /** Holds the weight of current operation. */
+ ulong m_uCurrentOperationWeight;
+ /** Holds whether progress cancel is enabled. */
+ bool m_fCancelEnabled;
+ /** Holds whether the progress has ended. */
+ bool m_fEnded;
+
+ /** Holds the progress event handler instance. */
+ UIProgressEventHandler *m_pEventHandler;
+
+ /** Holds the operation description template. */
+ static const char *m_spcszOpDescTpl;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIProgressDialog_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISearchLineEdit.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UISearchLineEdit.cpp
new file mode 100644
index 00000000..7d72e2e1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISearchLineEdit.cpp
@@ -0,0 +1,125 @@
+/* $Id: UISearchLineEdit.cpp $ */
+/** @file
+ * VBox Qt GUI - UIsearchLineEdit class definitions.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes */
+#include <QApplication>
+#include <QPainter>
+
+/* GUI includes: */
+#include "UISearchLineEdit.h"
+
+UISearchLineEdit::UISearchLineEdit(QWidget *pParent /* = 0 */)
+ :QLineEdit(pParent)
+ , m_iMatchCount(0)
+ , m_iScrollToIndex(-1)
+ , m_fMark(true)
+ , m_unmarkColor(palette().color(QPalette::Base))
+ , m_markColor(QColor(m_unmarkColor.red(),
+ 0.7 * m_unmarkColor.green(),
+ 0.7 * m_unmarkColor.blue()))
+{
+}
+
+void UISearchLineEdit::paintEvent(QPaintEvent *pEvent)
+{
+ QLineEdit::paintEvent(pEvent);
+
+ /* No search terms. no search. nothing to show here: */
+ if (text().isEmpty())
+ {
+ colorBackground(false);
+ return;
+ }
+ /* Draw the total match count and the current scrolled item's index on the right hand side of the line edit: */
+ QPainter painter(this);
+ QFont pfont = font();
+ QString strText = QString("%1/%2").arg(QString::number(m_iScrollToIndex + 1)).arg(QString::number(m_iMatchCount));
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ QSize textSize(QApplication::fontMetrics().horizontalAdvance(strText),
+ QApplication::fontMetrics().height());
+#else
+ QSize textSize(QApplication::fontMetrics().width(strText),
+ QApplication::fontMetrics().height());
+#endif
+
+ /* Dont draw anything if we dont have enough space: */
+ if (textSize.width() > 0.5 * width())
+ return;
+ int iTopMargin = (height() - textSize.height()) / 2;
+ int iRightMargin = iTopMargin;
+
+ QColor fontColor(Qt::black);
+ painter.setPen(fontColor);
+ painter.setFont(pfont);
+
+ painter.drawText(QRect(width() - textSize.width() - iRightMargin, iTopMargin, textSize.width(), textSize.height()),
+ Qt::AlignCenter | Qt::AlignVCenter, strText);
+ colorBackground(m_iMatchCount == 0);
+}
+
+void UISearchLineEdit::setMatchCount(int iMatchCount)
+{
+ if (m_iMatchCount == iMatchCount)
+ return;
+ m_iMatchCount = iMatchCount;
+ repaint();
+}
+
+void UISearchLineEdit::setScrollToIndex(int iScrollToIndex)
+{
+ if (m_iScrollToIndex == iScrollToIndex)
+ return;
+ m_iScrollToIndex = iScrollToIndex;
+ repaint();
+}
+
+void UISearchLineEdit::reset()
+{
+ clear();
+ m_iMatchCount = 0;
+ m_iScrollToIndex = 0;
+ colorBackground(false);
+}
+
+void UISearchLineEdit::colorBackground(bool fWarning)
+{
+ QPalette mPalette = QApplication::palette();
+ /** Make sure we reset color. */
+ if (!fWarning || !m_fMark)
+ {
+ mPalette.setColor(QPalette::Base, m_unmarkColor);
+ setPalette(mPalette);
+ return;
+ }
+
+ if (m_fMark && fWarning)
+ {
+ mPalette.setColor(QPalette::Base, m_markColor);
+ setPalette(mPalette);
+ return;
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISearchLineEdit.h b/src/VBox/Frontends/VirtualBox/src/widgets/UISearchLineEdit.h
new file mode 100644
index 00000000..3c2605bd
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISearchLineEdit.h
@@ -0,0 +1,75 @@
+/* $Id: UISearchLineEdit.h $ */
+/** @file
+ * VBox Qt GUI - UISearchLineEdit class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UISearchLineEdit_h
+#define FEQT_INCLUDED_SRC_widgets_UISearchLineEdit_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/* Qt includes */
+#include <QLineEdit>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** A QLineEdit extension with an overlay label drawn on the right hand side of it.
+ * mostly used for entering a search term and then label show total number of matched items
+ * and currently selected, scrolled item. */
+class SHARED_LIBRARY_STUFF UISearchLineEdit : public QLineEdit
+{
+
+ Q_OBJECT;
+
+public:
+
+ UISearchLineEdit(QWidget *pParent = 0);
+ void setMatchCount(int iMatchCount);
+ void setScrollToIndex(int iScrollToIndex);
+ void reset();
+
+protected:
+
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ void colorBackground(bool fWarning);
+
+ /** Stores the total number of matched items. */
+ int m_iMatchCount;
+ /** Stores the index of the currently scrolled/made-visible item withing the list of search results.
+ * Must be smaller that or equal to m_iMatchCount. */
+ int m_iScrollToIndex;
+ /** When true we color line edit background with a more reddish color. */
+ bool m_fMark;
+ QColor m_unmarkColor;
+ QColor m_markColor;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UISearchLineEdit_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingAnimation.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingAnimation.cpp
new file mode 100644
index 00000000..c4d8ab60
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingAnimation.cpp
@@ -0,0 +1,196 @@
+/* $Id: UISlidingAnimation.cpp $ */
+/** @file
+ * VBox Qt GUI - UISlidingAnimation class implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QEvent>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIAnimationFramework.h"
+#include "UISlidingAnimation.h"
+
+
+UISlidingAnimation::UISlidingAnimation(Qt::Orientation enmOrientation, bool fReverse, QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_enmOrientation(enmOrientation)
+ , m_fReverse(fReverse)
+ , m_pAnimation(0)
+ , m_fIsInProgress(false)
+ , m_pWidget(0)
+ , m_pLabel1(0)
+ , m_pLabel2(0)
+ , m_pWidget1(0)
+ , m_pWidget2(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UISlidingAnimation::setWidgets(QWidget *pWidget1, QWidget *pWidget2)
+{
+ /* Remember rendered widgets: */
+ m_pWidget1 = pWidget1;
+ m_pWidget2 = pWidget2;
+}
+
+void UISlidingAnimation::animate(SlidingDirection enmDirection)
+{
+ /* Mark animation started: */
+ m_fIsInProgress = true;
+
+ /* Acquire parent size: */
+ const QSize parentSize = parentWidget()->size();
+
+ /* Update animation boundaries based on parent size: */
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal:
+ {
+ m_startWidgetGeometry = QRect( 0, 0,
+ 2 * parentSize.width(), parentSize.height());
+ m_finalWidgetGeometry = QRect(- parentSize.width(), 0,
+ 2 * parentSize.width(), parentSize.height());
+ break;
+ }
+ case Qt::Vertical:
+ {
+ m_startWidgetGeometry = QRect(0, 0,
+ parentSize.width(), 2 * parentSize.height());
+ m_finalWidgetGeometry = QRect(0, - parentSize.height(),
+ parentSize.width(), 2 * parentSize.height());
+ break;
+ }
+ }
+ if (m_pAnimation)
+ m_pAnimation->update();
+
+ /* Update label content: */
+ QPixmap pixmap1(parentSize);
+ QPixmap pixmap2(parentSize);
+ m_pWidget1->render(&pixmap1);
+ m_pWidget2->render(&pixmap2);
+ m_pLabel1->setPixmap(pixmap1);
+ m_pLabel2->setPixmap(pixmap2);
+
+ /* Update initial widget geometry: */
+ switch (enmDirection)
+ {
+ case SlidingDirection_Forward:
+ {
+ setWidgetGeometry(m_startWidgetGeometry);
+ emit sigForward();
+ break;
+ }
+ case SlidingDirection_Reverse:
+ {
+ setWidgetGeometry(m_finalWidgetGeometry);
+ emit sigReverse();
+ break;
+ }
+ }
+}
+
+void UISlidingAnimation::sltHandleStateEnteredStart()
+{
+ /* If animation started: */
+ if (m_fIsInProgress)
+ {
+ /* Mark animation finished: */
+ m_fIsInProgress = false;
+ /* And notify listeners: */
+ emit sigAnimationComplete(SlidingDirection_Reverse);
+ }
+}
+
+void UISlidingAnimation::sltHandleStateEnteredFinal()
+{
+ /* If animation started: */
+ if (m_fIsInProgress)
+ {
+ /* Mark animation finished: */
+ m_fIsInProgress = false;
+ /* And notify listeners: */
+ emit sigAnimationComplete(SlidingDirection_Forward);
+ }
+}
+
+void UISlidingAnimation::prepare()
+{
+ /* Create animation: */
+ m_pAnimation = UIAnimation::installPropertyAnimation(this,
+ "widgetGeometry",
+ "startWidgetGeometry", "finalWidgetGeometry",
+ SIGNAL(sigForward()), SIGNAL(sigReverse()), m_fReverse);
+ connect(m_pAnimation, &UIAnimation::sigStateEnteredStart, this, &UISlidingAnimation::sltHandleStateEnteredStart);
+ connect(m_pAnimation, &UIAnimation::sigStateEnteredFinal, this, &UISlidingAnimation::sltHandleStateEnteredFinal);
+
+ /* Create private sliding widget: */
+ m_pWidget = new QWidget(this);
+ if (m_pWidget)
+ {
+ /* Create layout: */
+ QBoxLayout *pLayout = 0;
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal: pLayout = new QHBoxLayout(m_pWidget); break;
+ case Qt::Vertical: pLayout = new QVBoxLayout(m_pWidget); break;
+ }
+ if (pLayout)
+ {
+ /* Configure layout: */
+ pLayout->setSpacing(0);
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create 1st label: */
+ m_pLabel1 = new QLabel;
+ if (m_pLabel1)
+ pLayout->addWidget(m_pLabel1);
+ /* Create 2nd label: */
+ m_pLabel2 = new QLabel;
+ if (m_pLabel2)
+ pLayout->addWidget(m_pLabel2);
+ }
+ }
+
+ /* Assign initial widget geometry: */
+ m_pWidget->setGeometry(0, 0, width(), height());
+}
+
+void UISlidingAnimation::setWidgetGeometry(const QRect &rect)
+{
+ /* Define widget geometry: */
+ if (m_pWidget)
+ m_pWidget->setGeometry(rect);
+}
+
+QRect UISlidingAnimation::widgetGeometry() const
+{
+ /* Return widget geometry: */
+ return m_pWidget ? m_pWidget->geometry() : QRect();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingAnimation.h b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingAnimation.h
new file mode 100644
index 00000000..274ca5d4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingAnimation.h
@@ -0,0 +1,131 @@
+/* $Id: UISlidingAnimation.h $ */
+/** @file
+ * VBox Qt GUI - UISlidingAnimation class declaration.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UISlidingAnimation_h
+#define FEQT_INCLUDED_SRC_widgets_UISlidingAnimation_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* Forward declarations: */
+class QLabel;
+class QRect;
+class QWidget;
+class UIAnimation;
+
+
+/** Sliding direction. */
+enum SlidingDirection
+{
+ SlidingDirection_Forward,
+ SlidingDirection_Reverse
+};
+
+
+/** QWidget extension which renders a sliding animation
+ * while transiting from one widget to another. */
+class UISlidingAnimation : public QWidget
+{
+ Q_OBJECT;
+ Q_PROPERTY(QRect widgetGeometry READ widgetGeometry WRITE setWidgetGeometry);
+ Q_PROPERTY(QRect startWidgetGeometry READ startWidgetGeometry);
+ Q_PROPERTY(QRect finalWidgetGeometry READ finalWidgetGeometry);
+
+signals:
+
+ /** Commands to move animation in forward direction. */
+ void sigForward();
+ /** Commands to move animation in reverse direction. */
+ void sigReverse();
+
+ /** Notifies listeners about animation in specified @a enmDirection is complete. */
+ void sigAnimationComplete(SlidingDirection enmDirection);
+
+public:
+
+ /** Constructs sliding animation passing @a pParent to the base-class.
+ * @param enmOrientation Brings the widget orientation.
+ * @param fReverse Brings whether the animation should be initially reversed. */
+ UISlidingAnimation(Qt::Orientation enmOrientation, bool fReverse, QWidget *pParent = 0);
+
+ /** Defines @a pWidget1 and @a pWidget2. */
+ void setWidgets(QWidget *pWidget1, QWidget *pWidget2);
+
+ /** Animates cached pWidget1 and pWidget2 in passed @a enmDirection. */
+ void animate(SlidingDirection enmDirection);
+
+private slots:
+
+ /** Handles entering for 'Start' state. */
+ void sltHandleStateEnteredStart();
+ /** Handles entering for 'Final' state. */
+ void sltHandleStateEnteredFinal();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Defines sub-window geometry. */
+ void setWidgetGeometry(const QRect &rect);
+ /** Returns sub-window geometry. */
+ QRect widgetGeometry() const;
+ /** Returns sub-window start-geometry. */
+ QRect startWidgetGeometry() const { return m_startWidgetGeometry; }
+ /** Returns sub-window final-geometry. */
+ QRect finalWidgetGeometry() const { return m_finalWidgetGeometry; }
+
+ /** Holds the widget orientation. */
+ Qt::Orientation m_enmOrientation;
+ /** Holds whether the animation should be initially reversed. */
+ bool m_fReverse;
+ /** Holds the animation instance. */
+ UIAnimation *m_pAnimation;
+ /** Holds whether animation is in progress. */
+ bool m_fIsInProgress;
+ /** Holds sub-window start-geometry. */
+ QRect m_startWidgetGeometry;
+ /** Holds sub-window final-geometry. */
+ QRect m_finalWidgetGeometry;
+
+ /** Holds the sliding widget instance. */
+ QWidget *m_pWidget;
+ /** Holds the 1st label instance. */
+ QLabel *m_pLabel1;
+ /** Holds the 2nd label instance. */
+ QLabel *m_pLabel2;
+
+ /** Holds the 1st rendered-widget reference. */
+ QWidget *m_pWidget1;
+ /** Holds the 2nd rendered-widget reference. */
+ QWidget *m_pWidget2;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UISlidingAnimation_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingToolBar.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingToolBar.cpp
new file mode 100644
index 00000000..4fa830a3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingToolBar.cpp
@@ -0,0 +1,321 @@
+/* $Id: UISlidingToolBar.cpp $ */
+/** @file
+ * VBox Qt GUI - UISlidingToolBar class implementation.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UISlidingToolBar.h"
+#include "UIAnimationFramework.h"
+#include "UIMachineWindow.h"
+#include "UIMenuBarEditorWindow.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils-darwin.h"
+#endif
+
+
+UISlidingToolBar::UISlidingToolBar(QWidget *pParentWidget, QWidget *pIndentWidget, QWidget *pChildWidget, Position enmPosition)
+ : QWidget(pParentWidget, Qt::Tool | Qt::FramelessWindowHint)
+ , m_enmPosition(enmPosition)
+ , m_parentRect(pParentWidget ? pParentWidget->geometry() : QRect())
+ , m_indentRect(pIndentWidget ? pIndentWidget->geometry() : QRect())
+ , m_pAnimation(0)
+ , m_fExpanded(false)
+ , m_pMainLayout(0)
+ , m_pArea(0)
+ , m_pWidget(pChildWidget)
+{
+ /* Prepare: */
+ prepare();
+}
+
+#ifdef VBOX_WS_MAC
+bool UISlidingToolBar::event(QEvent *pEvent)
+{
+ /* Depending on event-type: */
+ switch (pEvent->type())
+ {
+ case QEvent::Resize:
+ case QEvent::WindowActivate:
+ {
+ // WORKAROUND:
+ // By some strange reason
+ // cocoa resets NSWindow::setHasShadow option
+ // for frameless windows on every window resize/activation.
+ // So we have to make sure window still has no shadows.
+ darwinSetWindowHasShadow(this, false);
+ break;
+ }
+ default:
+ break;
+ }
+ /* Call to base-class: */
+ return QWidget::event(pEvent);
+}
+#endif /* VBOX_WS_MAC */
+
+void UISlidingToolBar::showEvent(QShowEvent *)
+{
+ /* If window isn't expanded: */
+ if (!m_fExpanded)
+ {
+ /* Start expand animation: */
+ emit sigShown();
+ }
+}
+
+void UISlidingToolBar::closeEvent(QCloseEvent *pEvent)
+{
+ /* If window isn't expanded: */
+ if (!m_fExpanded)
+ {
+ /* Ignore close-event: */
+ pEvent->ignore();
+ return;
+ }
+
+ /* If animation state is Final: */
+ const QString strAnimationState = property("AnimationState").toString();
+ bool fAnimationComplete = strAnimationState == "Final";
+ if (fAnimationComplete)
+ {
+ /* Ignore close-event: */
+ pEvent->ignore();
+ /* And start collapse animation: */
+ emit sigCollapse();
+ }
+}
+
+void UISlidingToolBar::sltParentGeometryChanged(const QRect &parentRect)
+{
+ /* Update rectangle: */
+ m_parentRect = parentRect;
+ /* Adjust geometry: */
+ adjustGeometry();
+ /* Update animation: */
+ updateAnimation();
+}
+
+void UISlidingToolBar::prepare()
+{
+ /* Tell the application we are not that important: */
+ setAttribute(Qt::WA_QuitOnClose, false);
+ /* Delete window when closed: */
+ setAttribute(Qt::WA_DeleteOnClose);
+
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
+ /* Make sure we have no background
+ * until the first one paint-event: */
+ setAttribute(Qt::WA_NoSystemBackground);
+ /* Use Qt API to enable translucency: */
+ setAttribute(Qt::WA_TranslucentBackground);
+#elif defined(VBOX_WS_X11)
+ if (uiCommon().isCompositingManagerRunning())
+ {
+ /* Use Qt API to enable translucency: */
+ setAttribute(Qt::WA_TranslucentBackground);
+ }
+#endif /* VBOX_WS_X11 */
+
+ /* Prepare contents: */
+ prepareContents();
+ /* Prepare geometry: */
+ prepareGeometry();
+ /* Prepare animation: */
+ prepareAnimation();
+}
+
+void UISlidingToolBar::prepareContents()
+{
+ /* Create main-layout: */
+ m_pMainLayout = new QHBoxLayout(this);
+ if (m_pMainLayout)
+ {
+ /* Configure main-layout: */
+ m_pMainLayout->setContentsMargins(0, 0, 0, 0);
+ m_pMainLayout->setSpacing(0);
+ /* Create area: */
+ m_pArea = new QWidget;
+ if (m_pArea)
+ {
+ /* Configure area: */
+ m_pArea->setAcceptDrops(true);
+ m_pArea->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ QPalette pal1 = m_pArea->palette();
+ pal1.setColor(QPalette::Window, QColor(Qt::transparent));
+ m_pArea->setPalette(pal1);
+ /* Make sure valid child-widget passed: */
+ if (m_pWidget)
+ {
+ /* Configure child-widget: */
+ QPalette pal2 = m_pWidget->palette();
+ pal2.setColor(QPalette::Window, QApplication::palette().color(QPalette::Window));
+ m_pWidget->setPalette(pal2);
+ /* Using abstract (old-style) connection here(!) since the base classes can be different: */
+ connect(m_pWidget, SIGNAL(sigCancelClicked()), this, SLOT(close()));
+ /* Add child-widget into area: */
+ m_pWidget->setParent(m_pArea);
+ }
+ /* Add area into main-layout: */
+ m_pMainLayout->addWidget(m_pArea);
+ }
+ }
+}
+
+void UISlidingToolBar::prepareGeometry()
+{
+ /* Prepare geometry based on parent and sub-window size-hints,
+ * But move sub-window to initial position: */
+ const QSize sh = m_pWidget->sizeHint();
+ switch (m_enmPosition)
+ {
+ case Position_Top:
+ {
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, m_parentRect.x(), m_parentRect.y() + m_indentRect.height(),
+ qMax(m_parentRect.width(), sh.width()), sh.height());
+ m_pWidget->setGeometry(0, -sh.height(), qMax(width(), sh.width()), sh.height());
+ break;
+ }
+ case Position_Bottom:
+ {
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, m_parentRect.x(), m_parentRect.y() + m_parentRect.height() - m_indentRect.height() - sh.height(),
+ qMax(m_parentRect.width(), sh.width()), sh.height());
+ m_pWidget->setGeometry(0, sh.height(), qMax(width(), sh.width()), sh.height());
+ break;
+ }
+ }
+
+#ifdef VBOX_WS_X11
+ if (!uiCommon().isCompositingManagerRunning())
+ {
+ /* Use Xshape otherwise: */
+ setMask(m_pWidget->geometry());
+ }
+#endif
+
+#ifdef VBOX_WS_WIN
+ /* Raise tool-window for proper z-order. */
+ raise();
+#endif
+
+ /* Activate window after it was shown: */
+ connect(this, &UISlidingToolBar::sigShown,
+ this, &UISlidingToolBar::sltActivateWindow, Qt::QueuedConnection);
+ /* Update window geometry after parent geometry changed: */
+ /* Leave this in the old connection syntax for now: */
+ connect(parent(), SIGNAL(sigGeometryChange(const QRect&)),
+ this, SLOT(sltParentGeometryChanged(const QRect&)));
+}
+
+void UISlidingToolBar::prepareAnimation()
+{
+ /* Prepare sub-window geometry animation itself: */
+ connect(this, SIGNAL(sigShown()), this, SIGNAL(sigExpand()), Qt::QueuedConnection);
+ m_pAnimation = UIAnimation::installPropertyAnimation(this,
+ "widgetGeometry",
+ "startWidgetGeometry", "finalWidgetGeometry",
+ SIGNAL(sigExpand()), SIGNAL(sigCollapse()));
+ connect(m_pAnimation, &UIAnimation::sigStateEnteredStart, this, &UISlidingToolBar::sltMarkAsCollapsed);
+ connect(m_pAnimation, &UIAnimation::sigStateEnteredFinal, this, &UISlidingToolBar::sltMarkAsExpanded);
+ /* Update geometry animation: */
+ updateAnimation();
+}
+
+void UISlidingToolBar::adjustGeometry()
+{
+ /* Adjust geometry based on parent and sub-window size-hints: */
+ const QSize sh = m_pWidget->sizeHint();
+ switch (m_enmPosition)
+ {
+ case Position_Top:
+ {
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, m_parentRect.x(), m_parentRect.y() + m_indentRect.height(),
+ qMax(m_parentRect.width(), sh.width()), sh.height());
+ break;
+ }
+ case Position_Bottom:
+ {
+ UIDesktopWidgetWatchdog::setTopLevelGeometry(this, m_parentRect.x(), m_parentRect.y() + m_parentRect.height() - m_indentRect.height() - sh.height(),
+ qMax(m_parentRect.width(), sh.width()), sh.height());
+ break;
+ }
+ }
+ /* And move sub-window to corresponding position: */
+ m_pWidget->setGeometry(0, 0, qMax(width(), sh.width()), sh.height());
+
+#ifdef VBOX_WS_X11
+ if (!uiCommon().isCompositingManagerRunning())
+ {
+ /* Use Xshape otherwise: */
+ setMask(m_pWidget->geometry());
+ }
+#endif
+
+#ifdef VBOX_WS_WIN
+ /* Raise tool-window for proper z-order. */
+ raise();
+#endif
+}
+
+void UISlidingToolBar::updateAnimation()
+{
+ /* Skip if no animation created: */
+ if (!m_pAnimation)
+ return;
+
+ /* Recalculate sub-window geometry animation boundaries based on size-hint: */
+ const QSize sh = m_pWidget->sizeHint();
+ switch (m_enmPosition)
+ {
+ case Position_Top: m_startWidgetGeometry = QRect(0, -sh.height(), qMax(width(), sh.width()), sh.height()); break;
+ case Position_Bottom: m_startWidgetGeometry = QRect(0, sh.height(), qMax(width(), sh.width()), sh.height()); break;
+ }
+ m_finalWidgetGeometry = QRect(0, 0, qMax(width(), sh.width()), sh.height());
+ m_pAnimation->update();
+}
+
+void UISlidingToolBar::setWidgetGeometry(const QRect &rect)
+{
+ /* Apply sub-window geometry: */
+ m_pWidget->setGeometry(rect);
+
+#ifdef VBOX_WS_X11
+ if (!uiCommon().isCompositingManagerRunning())
+ {
+ /* Use Xshape otherwise: */
+ setMask(m_pWidget->geometry());
+ }
+#endif
+}
+
+QRect UISlidingToolBar::widgetGeometry() const
+{
+ /* Return sub-window geometry: */
+ return m_pWidget->geometry();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingToolBar.h b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingToolBar.h
new file mode 100644
index 00000000..c6a3e33b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingToolBar.h
@@ -0,0 +1,167 @@
+/* $Id: UISlidingToolBar.h $ */
+/** @file
+ * VBox Qt GUI - UISlidingToolBar class declaration.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UISlidingToolBar_h
+#define FEQT_INCLUDED_SRC_widgets_UISlidingToolBar_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QCloseEvent;
+class QEvent;
+class QHBoxLayout;
+class QRect;
+class QShowEvent;
+class QWidget;
+class UIAnimation;
+
+/** QWidget subclass
+ * providing GUI with slideable tool-bar. */
+class SHARED_LIBRARY_STUFF UISlidingToolBar : public QWidget
+{
+ Q_OBJECT;
+ Q_PROPERTY(QRect widgetGeometry READ widgetGeometry WRITE setWidgetGeometry);
+ Q_PROPERTY(QRect startWidgetGeometry READ startWidgetGeometry);
+ Q_PROPERTY(QRect finalWidgetGeometry READ finalWidgetGeometry);
+
+signals:
+
+ /** Notifies about window shown. */
+ void sigShown();
+ /** Commands window to expand. */
+ void sigExpand();
+ /** Commands window to collapse. */
+ void sigCollapse();
+
+public:
+
+ /** Possible positions. */
+ enum Position
+ {
+ Position_Top,
+ Position_Bottom
+ };
+
+ /** Constructs sliding tool-bar passing @a pParentWidget to the base-class.
+ * @param pParentWidget Brings the parent-widget geometry.
+ * @param pIndentWidget Brings the indent-widget geometry.
+ * @param pChildWidget Brings the child-widget to be injected into tool-bar.
+ * @param enmPosition Brings the tool-bar position. */
+ UISlidingToolBar(QWidget *pParentWidget, QWidget *pIndentWidget, QWidget *pChildWidget, Position enmPosition);
+
+public slots:
+
+ /** Performs window activation. */
+ void sltActivateWindow() { activateWindow(); }
+
+protected:
+
+#ifdef VBOX_WS_MAC
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+#endif
+ /** Handles show @a pEvent. */
+ virtual void showEvent(QShowEvent *pEvent) RT_OVERRIDE;
+ /** Handles close @a pEvent. */
+ virtual void closeEvent(QCloseEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Marks window as expanded. */
+ void sltMarkAsExpanded() { m_fExpanded = true; }
+ /** Marks window as collapsed. */
+ void sltMarkAsCollapsed() { close(); m_fExpanded = false; }
+
+ /** Handles parent geometry change. */
+ void sltParentGeometryChanged(const QRect &parentRect);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares contents. */
+ void prepareContents();
+ /** Prepares geometry. */
+ void prepareGeometry();
+ /** Prepares animation. */
+ void prepareAnimation();
+
+ /** Updates geometry. */
+ void adjustGeometry();
+ /** Updates animation. */
+ void updateAnimation();
+
+ /** Defines sub-window geometry. */
+ void setWidgetGeometry(const QRect &rect);
+ /** Returns sub-window geometry. */
+ QRect widgetGeometry() const;
+ /** Returns sub-window start-geometry. */
+ QRect startWidgetGeometry() const { return m_startWidgetGeometry; }
+ /** Returns sub-window final-geometry. */
+ QRect finalWidgetGeometry() const { return m_finalWidgetGeometry; }
+
+ /** @name Geometry
+ * @{ */
+ /** Holds the tool-bar position. */
+ const Position m_enmPosition;
+ /** Holds the cached parent-widget geometry. */
+ QRect m_parentRect;
+ /** Holds the cached indent-widget geometry. */
+ QRect m_indentRect;
+ /** @} */
+
+ /** @name Geometry: Animation
+ * @{ */
+ /** Holds the expand/collapse animation instance. */
+ UIAnimation *m_pAnimation;
+ /** Holds whether window is expanded. */
+ bool m_fExpanded;
+ /** Holds sub-window start-geometry. */
+ QRect m_startWidgetGeometry;
+ /** Holds sub-window final-geometry. */
+ QRect m_finalWidgetGeometry;
+ /** @} */
+
+ /** @name Contents
+ * @{ */
+ /** Holds the main-layout instance. */
+ QHBoxLayout *m_pMainLayout;
+ /** Holds the area instance. */
+ QWidget *m_pArea;
+ /** Holds the child-widget reference. */
+ QWidget *m_pWidget;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UISlidingToolBar_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingWidget.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingWidget.cpp
new file mode 100755
index 00000000..6de2fead
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingWidget.cpp
@@ -0,0 +1,188 @@
+/* $Id: UISlidingWidget.cpp $ */
+/** @file
+ * VBox Qt GUI - UISlidingWidget class implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QEvent>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIAnimationFramework.h"
+#include "UISlidingWidget.h"
+
+
+UISlidingWidget::UISlidingWidget(Qt::Orientation enmOrientation, QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_enmOrientation(enmOrientation)
+ , m_enmState(State_Start)
+ , m_pAnimation(0)
+ , m_pWidget(0)
+ , m_pLayout(0)
+ , m_pWidget1(0)
+ , m_pWidget2(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+QSize UISlidingWidget::minimumSizeHint() const
+{
+ /* Return maximum of minimum size-hints: */
+ QSize msh = QSize();
+ if (m_pWidget1)
+ msh = msh.expandedTo(m_pWidget1->minimumSizeHint());
+ if (m_pWidget2)
+ msh = msh.expandedTo(m_pWidget2->minimumSizeHint());
+ return msh;
+}
+
+void UISlidingWidget::setWidgets(QWidget *pWidget1, QWidget *pWidget2)
+{
+ /* Clear animation/widgets if any: */
+ delete m_pAnimation;
+ delete m_pWidget1;
+ delete m_pWidget2;
+
+ /* Remember widgets: */
+ m_pWidget1 = pWidget1;
+ m_pWidget2 = pWidget2;
+ m_pLayout->addWidget(m_pWidget1);
+ m_pLayout->addWidget(m_pWidget2);
+
+ /* Install new animation: */
+ m_pAnimation = UIAnimation::installPropertyAnimation(this,
+ "widgetGeometry",
+ "startWidgetGeometry", "finalWidgetGeometry",
+ SIGNAL(sigForward()), SIGNAL(sigBackward()));
+ connect(m_pAnimation, &UIAnimation::sigStateEnteredStart, this, &UISlidingWidget::sltSetStateToStart);
+ connect(m_pAnimation, &UIAnimation::sigStateEnteredFinal, this, &UISlidingWidget::sltSetStateToFinal);
+
+ /* Update animation: */
+ updateAnimation();
+ /* Update widget geometry: */
+ m_pWidget->setGeometry(m_enmState == State_Final ? m_finalWidgetGeometry : m_startWidgetGeometry);
+}
+
+bool UISlidingWidget::event(QEvent *pEvent)
+{
+ /* Process desired events: */
+ switch (pEvent->type())
+ {
+ case QEvent::LayoutRequest:
+ {
+ // WORKAROUND:
+ // Since we are not connected to
+ // our children LayoutRequest,
+ // we should update geometry
+ // ourselves.
+ updateGeometry();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base class: */
+ return QWidget::event(pEvent);
+}
+
+void UISlidingWidget::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QWidget::resizeEvent(pEvent);
+
+ /* Update animation: */
+ updateAnimation();
+ /* Update widget geometry: */
+ m_pWidget->setGeometry(m_enmState == State_Final ? m_finalWidgetGeometry : m_startWidgetGeometry);
+}
+
+void UISlidingWidget::prepare()
+{
+ /* Create private sliding widget: */
+ m_pWidget = new QWidget(this);
+ if (m_pWidget)
+ {
+ /* Create layout: */
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal: m_pLayout = new QHBoxLayout(m_pWidget); break;
+ case Qt::Vertical: m_pLayout = new QVBoxLayout(m_pWidget); break;
+ }
+ if (m_pLayout)
+ {
+ /* Configure layout: */
+ m_pLayout->setSpacing(0);
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+ }
+ }
+
+ /* Update animation: */
+ updateAnimation();
+ /* Update widget geometry: */
+ m_pWidget->setGeometry(m_enmState == State_Final ? m_finalWidgetGeometry : m_startWidgetGeometry);
+}
+
+void UISlidingWidget::updateAnimation()
+{
+ /* Recalculate sub-window geometry animation boundaries based on size-hint: */
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal:
+ {
+ m_startWidgetGeometry = QRect( 0, 0,
+ 2 * width(), height());
+ m_finalWidgetGeometry = QRect(- width(), 0,
+ 2 * width(), height());
+ break;
+ }
+ case Qt::Vertical:
+ {
+ m_startWidgetGeometry = QRect(0, 0,
+ width(), 2 * height());
+ m_finalWidgetGeometry = QRect(0, - height(),
+ width(), 2 * height());
+ break;
+ }
+ }
+
+ /* Update animation finally: */
+ if (m_pAnimation)
+ m_pAnimation->update();
+}
+
+void UISlidingWidget::setWidgetGeometry(const QRect &rect)
+{
+ /* Apply widget geometry: */
+ m_pWidget->setGeometry(rect);
+}
+
+QRect UISlidingWidget::widgetGeometry() const
+{
+ /* Return widget geometry: */
+ return m_pWidget->geometry();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingWidget.h b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingWidget.h
new file mode 100644
index 00000000..cb68dbcf
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISlidingWidget.h
@@ -0,0 +1,147 @@
+/* $Id: UISlidingWidget.h $ */
+/** @file
+ * VBox Qt GUI - UISlidingWidget class declaration.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UISlidingWidget_h
+#define FEQT_INCLUDED_SRC_widgets_UISlidingWidget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+/* Forward declarations: */
+class QBoxLayout;
+class QRect;
+class QWidget;
+class UIAnimation;
+
+
+/** Some kind of splitter which allows to switch between
+ * two widgets using horizontal sliding animation. */
+class UISlidingWidget : public QWidget
+{
+ Q_OBJECT;
+ Q_PROPERTY(QRect widgetGeometry READ widgetGeometry WRITE setWidgetGeometry);
+ Q_PROPERTY(QRect startWidgetGeometry READ startWidgetGeometry);
+ Q_PROPERTY(QRect finalWidgetGeometry READ finalWidgetGeometry);
+
+signals:
+
+ /** Commands to move animation forward. */
+ void sigForward();
+ /** Commands to move animation backward. */
+ void sigBackward();
+
+public:
+
+ /** Sliding state. */
+ enum State
+ {
+ State_Start,
+ State_GoingForward,
+ State_Final,
+ State_GoingBackward
+ };
+
+ /** Constructs sliding widget passing @a pParent to the base-class.
+ * @param enmOrientation Brings the widget orientation. */
+ UISlidingWidget(Qt::Orientation enmOrientation, QWidget *pParent = 0);
+
+ /** Holds the minimum widget size. */
+ virtual QSize minimumSizeHint() const /* pverride */;
+
+ /** Defines @a pWidget1 and @a pWidget2. */
+ void setWidgets(QWidget *pWidget1, QWidget *pWidget2);
+
+ /** Returns sliding state. */
+ State state() const { return m_enmState; }
+
+ /** Moves animation forward. */
+ void moveForward() { m_enmState = State_GoingForward; emit sigForward(); }
+ /** Moves animation backward. */
+ void moveBackward() { m_enmState = State_GoingBackward; emit sigBackward(); }
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Marks state as start. */
+ void sltSetStateToStart() { m_enmState = State_Start; }
+ /** Marks state as final. */
+ void sltSetStateToFinal() { m_enmState = State_Final; }
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates animation. */
+ void updateAnimation();
+
+ /** Defines sub-window geometry. */
+ void setWidgetGeometry(const QRect &rect);
+ /** Returns sub-window geometry. */
+ QRect widgetGeometry() const;
+ /** Returns sub-window start-geometry. */
+ QRect startWidgetGeometry() const { return m_startWidgetGeometry; }
+ /** Returns sub-window final-geometry. */
+ QRect finalWidgetGeometry() const { return m_finalWidgetGeometry; }
+
+ /** Holds the widget orientation. */
+ Qt::Orientation m_enmOrientation;
+
+ /** Holds whether we are in animation final state. */
+ State m_enmState;
+ /** Holds the shift left/right animation instance. */
+ UIAnimation *m_pAnimation;
+ /** Holds sub-window start-geometry. */
+ QRect m_startWidgetGeometry;
+ /** Holds sub-window final-geometry. */
+ QRect m_finalWidgetGeometry;
+
+ /** Holds the private sliding widget instance. */
+ QWidget *m_pWidget;
+ /** Holds the widget layout instance. */
+ QBoxLayout *m_pLayout;
+ /** Holds the 1st widget reference. */
+ QWidget *m_pWidget1;
+ /** Holds the 2nd widget reference. */
+ QWidget *m_pWidget2;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UISlidingWidget_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISpecialControls.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UISpecialControls.cpp
new file mode 100644
index 00000000..f0c76950
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISpecialControls.cpp
@@ -0,0 +1,199 @@
+/* $Id: UISpecialControls.cpp $ */
+/** @file
+ * VBox Qt GUI - UISpecialControls implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#ifndef VBOX_DARWIN_USE_NATIVE_CONTROLS
+# include <QBitmap>
+# include <QMouseEvent>
+# include <QPainter>
+# include <QSignalMapper>
+#endif
+
+/* GUI includes: */
+#include "UIIconPool.h"
+#include "UISpecialControls.h"
+
+
+#ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
+
+
+/*********************************************************************************************************************************
+* Class UIMiniCancelButton implementation. *
+*********************************************************************************************************************************/
+
+UIMiniCancelButton::UIMiniCancelButton(QWidget *pParent /* = 0 */)
+ : QAbstractButton(pParent)
+{
+ setShortcut(QKeySequence(Qt::Key_Escape));
+ m_pButton = new UICocoaButton(this, UICocoaButton::CancelButton);
+ connect(m_pButton, SIGNAL(clicked()), this, SIGNAL(clicked()));
+ setFixedSize(m_pButton->size());
+}
+
+void UIMiniCancelButton::resizeEvent(QResizeEvent *)
+{
+ m_pButton->resize(size());
+}
+
+
+/*********************************************************************************************************************************
+* Class UIHelpButton implementation. *
+*********************************************************************************************************************************/
+
+UIHelpButton::UIHelpButton(QWidget *pParent /* = 0 */)
+ : QPushButton(pParent)
+{
+ setShortcut(QKeySequence(QKeySequence::HelpContents));
+ m_pButton = new UICocoaButton(this, UICocoaButton::HelpButton);
+ connect(m_pButton, SIGNAL(clicked()), this, SIGNAL(clicked()));
+ setFixedSize(m_pButton->size());
+}
+
+
+#else /* !VBOX_DARWIN_USE_NATIVE_CONTROLS */
+
+
+/*********************************************************************************************************************************
+* Class UIMiniCancelButton implementation. *
+*********************************************************************************************************************************/
+
+UIMiniCancelButton::UIMiniCancelButton(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QIToolButton>(pParent)
+{
+ setAutoRaise(true);
+ setFocusPolicy(Qt::TabFocus);
+ setShortcut(QKeySequence(Qt::Key_Escape));
+ setIcon(UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_DialogCancel));
+}
+
+
+/*********************************************************************************************************************************
+* Class UIHelpButton implementation. *
+*********************************************************************************************************************************/
+
+/* From: src/gui/styles/qmacstyle_mac.cpp */
+static const int PushButtonLeftOffset = 6;
+static const int PushButtonTopOffset = 4;
+static const int PushButtonRightOffset = 12;
+static const int PushButtonBottomOffset = 4;
+
+UIHelpButton::UIHelpButton(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QPushButton>(pParent)
+{
+# ifdef VBOX_WS_MAC
+ m_pButtonPressed = false;
+ m_pNormalPixmap = new QPixmap(":/help_button_normal_mac_24px.png");
+ m_pPressedPixmap = new QPixmap(":/help_button_pressed_mac_24px.png");
+ m_size = m_pNormalPixmap->size();
+ m_pMask = new QImage(m_pNormalPixmap->mask().toImage());
+ m_BRect = QRect(PushButtonLeftOffset,
+ PushButtonTopOffset,
+ m_size.width(),
+ m_size.height());
+# endif /* VBOX_WS_MAC */
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIHelpButton::initFrom(QPushButton *pOther)
+{
+ /* Copy settings from pOther: */
+ setIcon(pOther->icon());
+ setText(pOther->text());
+ setShortcut(pOther->shortcut());
+ setFlat(pOther->isFlat());
+ setAutoDefault(pOther->autoDefault());
+ setDefault(pOther->isDefault());
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UIHelpButton::retranslateUi()
+{
+ QPushButton::setText(tr("&Help"));
+ if (QPushButton::shortcut().isEmpty())
+ QPushButton::setShortcut(QKeySequence::HelpContents);
+}
+
+# ifdef VBOX_WS_MAC
+UIHelpButton::~UIHelpButton()
+{
+ delete m_pNormalPixmap;
+ delete m_pPressedPixmap;
+ delete m_pMask;
+}
+
+QSize UIHelpButton::sizeHint() const
+{
+ return QSize(m_size.width() + PushButtonLeftOffset + PushButtonRightOffset,
+ m_size.height() + PushButtonTopOffset + PushButtonBottomOffset);
+}
+
+void UIHelpButton::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+ painter.drawPixmap(PushButtonLeftOffset, PushButtonTopOffset, m_pButtonPressed ? *m_pPressedPixmap: *m_pNormalPixmap);
+}
+
+bool UIHelpButton::hitButton(const QPoint &position) const
+{
+ if (m_BRect.contains(position))
+ return m_pMask->pixel(position.x() - PushButtonLeftOffset,
+ position.y() - PushButtonTopOffset) == 0xff000000;
+ else
+ return false;
+}
+
+void UIHelpButton::mousePressEvent(QMouseEvent *pEvent)
+{
+ if (hitButton(pEvent->pos()))
+ m_pButtonPressed = true;
+ QPushButton::mousePressEvent(pEvent);
+ update();
+}
+
+void UIHelpButton::mouseReleaseEvent(QMouseEvent *pEvent)
+{
+ QPushButton::mouseReleaseEvent(pEvent);
+ m_pButtonPressed = false;
+ update();
+}
+
+void UIHelpButton::leaveEvent(QEvent *pEvent)
+{
+ QPushButton::leaveEvent(pEvent);
+ m_pButtonPressed = false;
+ update();
+}
+# endif /* VBOX_WS_MAC */
+
+
+#endif /* !VBOX_DARWIN_USE_NATIVE_CONTROLS */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UISpecialControls.h b/src/VBox/Frontends/VirtualBox/src/widgets/UISpecialControls.h
new file mode 100644
index 00000000..d72d6994
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UISpecialControls.h
@@ -0,0 +1,199 @@
+/* $Id: UISpecialControls.h $ */
+/** @file
+ * VBox Qt GUI - UISpecialControls declarations.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UISpecialControls_h
+#define FEQT_INCLUDED_SRC_widgets_UISpecialControls_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QPushButton>
+#ifndef VBOX_DARWIN_USE_NATIVE_CONTROLS
+# include <QLineEdit>
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+#ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
+# include "UICocoaSpecialControls.h"
+#else
+# include "QIToolButton.h"
+#endif
+
+/* Forward declarations: */
+#ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
+class UICocoaButton;
+#endif
+
+
+#ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
+
+/** QAbstractButton subclass, used as mini cancel button. */
+class SHARED_LIBRARY_STUFF UIMiniCancelButton : public QAbstractButton
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs mini cancel-button passing @a pParent to the base-class. */
+ UIMiniCancelButton(QWidget *pParent = 0);
+
+ /** Defines button @a strText. */
+ void setText(const QString &strText) { m_pButton->setText(strText); }
+ /** Defines button @a strToolTip. */
+ void setToolTip(const QString &strToolTip) { m_pButton->setToolTip(strToolTip); }
+ /** Removes button border. */
+ void removeBorder() {}
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE { Q_UNUSED(pEvent); }
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Holds the wrapped cocoa button instance. */
+ UICocoaButton *m_pButton;
+};
+
+
+/** QAbstractButton subclass, used as mini cancel button. */
+class SHARED_LIBRARY_STUFF UIHelpButton : public QPushButton
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs help-button passing @a pParent to the base-class. */
+ UIHelpButton(QWidget *pParent = 0);
+
+ /** Defines button @a strToolTip. */
+ void setToolTip(const QString &strToolTip) { m_pButton->setToolTip(strToolTip); }
+
+ /** Inits this button from pOther. */
+ void initFrom(QPushButton *pOther) { Q_UNUSED(pOther); }
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE { Q_UNUSED(pEvent); }
+
+private:
+
+ /** Holds the wrapped cocoa button instance. */
+ UICocoaButton *m_pButton;
+};
+
+#else /* !VBOX_DARWIN_USE_NATIVE_CONTROLS */
+
+/** QAbstractButton subclass, used as mini cancel button. */
+class SHARED_LIBRARY_STUFF UIMiniCancelButton : public QIWithRetranslateUI<QIToolButton>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs mini cancel-button passing @a pParent to the base-class. */
+ UIMiniCancelButton(QWidget *pParent = 0);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE {};
+};
+
+
+/** QAbstractButton subclass, used as mini cancel button. */
+class SHARED_LIBRARY_STUFF UIHelpButton : public QIWithRetranslateUI<QPushButton>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs help-button passing @a pParent to the base-class. */
+ UIHelpButton(QWidget *pParent = 0);
+
+# ifdef VBOX_WS_MAC
+ /** Destructs help-button. */
+ ~UIHelpButton();
+
+ /** Returns size-hint. */
+ QSize sizeHint() const;
+# endif /* VBOX_WS_MAC */
+
+ /** Inits this button from pOther. */
+ void initFrom(QPushButton *pOther);
+
+protected:
+
+ /** Handles translation event. */
+ void retranslateUi();
+
+# ifdef VBOX_WS_MAC
+ /** Handles button hit as certain @a position. */
+ bool hitButton(const QPoint &position) const;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse-press @a pEvent. */
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-release @a pEvent. */
+ virtual void mouseReleaseEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-leave @a pEvent. */
+ virtual void leaveEvent(QEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Holds the pressed button instance. */
+ bool m_pButtonPressed;
+
+ /** Holds the button size. */
+ QSize m_size;
+
+ /** Holds the normal pixmap instance. */
+ QPixmap *m_pNormalPixmap;
+ /** Holds the pressed pixmap instance. */
+ QPixmap *m_pPressedPixmap;
+
+ /** Holds the button mask instance. */
+ QImage *m_pMask;
+
+ /** Holds the button rect. */
+ QRect m_BRect;
+# endif /* VBOX_WS_MAC */
+};
+
+#endif /* !VBOX_DARWIN_USE_NATIVE_CONTROLS */
+
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UISpecialControls_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIStatusBarEditorWindow.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIStatusBarEditorWindow.cpp
new file mode 100644
index 00000000..7005c8a2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIStatusBarEditorWindow.cpp
@@ -0,0 +1,893 @@
+/* $Id: UIStatusBarEditorWindow.cpp $ */
+/** @file
+ * VBox Qt GUI - UIStatusBarEditorWindow class implementation.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleWidget>
+#include <QCheckBox>
+#include <QDrag>
+#include <QHBoxLayout>
+#include <QMimeData>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QPixmap>
+#include <QStatusBar>
+#include <QStyleOption>
+#include <QStylePainter>
+
+/* GUI includes: */
+#include "QIToolButton.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMachineWindow.h"
+#include "UIStatusBarEditorWindow.h"
+
+/* Forward declarations: */
+class QAccessibleInterface;
+class QMouseEvent;
+class QObject;
+class QPixmap;
+class QPoint;
+class QSize;
+
+
+/** QWidget subclass used as status-bar editor button. */
+class UIStatusBarEditorButton : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about click. */
+ void sigClick();
+
+ /** Notifies about drag-object destruction. */
+ void sigDragObjectDestroy();
+
+public:
+
+ /** Holds the mime-type for the D&D system. */
+ static const QString MimeType;
+
+ /** Constructs the button of passed @a enmType. */
+ UIStatusBarEditorButton(IndicatorType enmType);
+
+ /** Returns button type. */
+ IndicatorType type() const { return m_enmType; }
+
+ /** Returns button size-hint. */
+ QSize sizeHint() const { return m_size; }
+
+ /** Returns whether button is checked. */
+ bool isChecked() const;
+ /** Defines whether button is @a fChecked. */
+ void setChecked(bool fChecked);
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse-press @a pEvent. */
+ virtual void mousePressEvent(QMouseEvent *pEvent);
+ /** Handles mouse-release @a pEvent. */
+ virtual void mouseReleaseEvent(QMouseEvent *pEvent);
+ /** Handles mouse-enter @a pEvent. */
+ virtual void enterEvent(QEvent *pEvent);
+ /** Handles mouse-leave @a pEvent. */
+ virtual void leaveEvent(QEvent *pEvent);
+ /** Handles mouse-move @a pEvent. */
+ virtual void mouseMoveEvent(QMouseEvent *pEvent);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates pixmap. */
+ void updatePixmap();
+
+ /** Holds the button type. */
+ IndicatorType m_enmType;
+ /** Holds the button size. */
+ QSize m_size;
+ /** Holds the button pixmap. */
+ QPixmap m_pixmap;
+ /** Holds the button pixmap size. */
+ QSize m_pixmapSize;
+ /** Holds whether button is checked. */
+ bool m_fChecked;
+ /** Holds whether button is hovered. */
+ bool m_fHovered;
+ /** Holds the last mouse-press position. */
+ QPoint m_mousePressPosition;
+};
+
+
+/** QAccessibleWidget extension used as an accessibility interface for UIStatusBarEditor buttons. */
+class UIAccessibilityInterfaceForUIStatusBarEditorButton : public QAccessibleWidget
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject);
+
+ /** Constructs an accessibility interface passing @a pWidget to the base-class. */
+ UIAccessibilityInterfaceForUIStatusBarEditorButton(QWidget *pWidget);
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
+
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE;
+
+private:
+
+ /** Returns corresponding toolbar button. */
+ UIStatusBarEditorButton *button() const { return qobject_cast<UIStatusBarEditorButton*>(widget()); }
+};
+
+
+/*********************************************************************************************************************************
+* Class UIAccessibilityInterfaceForUIStatusBarEditorButton implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+QAccessibleInterface *UIAccessibilityInterfaceForUIStatusBarEditorButton::pFactory(const QString &strClassname, QObject *pObject)
+{
+ /* Creating toolbar button accessibility interface: */
+ if ( pObject
+ && strClassname == QLatin1String("UIStatusBarEditorButton"))
+ return new UIAccessibilityInterfaceForUIStatusBarEditorButton(qobject_cast<QWidget*>(pObject));
+
+ /* Null by default: */
+ return 0;
+}
+
+UIAccessibilityInterfaceForUIStatusBarEditorButton::UIAccessibilityInterfaceForUIStatusBarEditorButton(QWidget *pWidget)
+ : QAccessibleWidget(pWidget, QAccessible::CheckBox)
+{
+}
+
+QString UIAccessibilityInterfaceForUIStatusBarEditorButton::text(QAccessible::Text /* enmTextRole */) const
+{
+ /* Make sure view still alive: */
+ AssertPtrReturn(button(), QString());
+
+ /* Return view tool-tip: */
+ return gpConverter->toString(button()->type());
+}
+
+QAccessible::State UIAccessibilityInterfaceForUIStatusBarEditorButton::state() const /* override */
+{
+ /* Prepare the button state: */
+ QAccessible::State state;
+
+ /* Make sure button still alive: */
+ AssertPtrReturn(button(), state);
+
+ /* Compose the button state: */
+ state.checkable = true;
+ state.checked = button()->isChecked();
+
+ /* Return the button state: */
+ return state;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIStatusBarEditorButton implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+const QString UIStatusBarEditorButton::MimeType = QString("application/virtualbox;value=IndicatorType");
+
+UIStatusBarEditorButton::UIStatusBarEditorButton(IndicatorType enmType)
+ : m_enmType(enmType)
+ , m_fChecked(false)
+ , m_fHovered(false)
+{
+ prepare();
+}
+
+bool UIStatusBarEditorButton::isChecked() const
+{
+ return m_fChecked;
+}
+
+void UIStatusBarEditorButton::setChecked(bool fChecked)
+{
+ /* Update 'checked' state: */
+ m_fChecked = fChecked;
+ /* Update: */
+ update();
+}
+
+bool UIStatusBarEditorButton::event(QEvent *pEvent)
+{
+ /* Handle know event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Show:
+ case QEvent::ScreenChangeInternal:
+ {
+ /* Update pixmap: */
+ updatePixmap();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::event(pEvent);
+}
+
+void UIStatusBarEditorButton::retranslateUi()
+{
+ /* Translate tool-tip: */
+ setToolTip(UIStatusBarEditorWidget::tr("<nobr><b>Click</b> to toggle indicator presence.</nobr><br>"
+ "<nobr><b>Drag&Drop</b> to change indicator position.</nobr>"));
+}
+
+void UIStatusBarEditorButton::paintEvent(QPaintEvent *)
+{
+ /* Create style-painter: */
+ QStylePainter painter(this);
+ /* Prepare option set for check-box: */
+ QStyleOptionButton option;
+ option.initFrom(this);
+ /* Use the size of 'this': */
+ option.rect = QRect(0, 0, width(), height());
+ /* But do not use hover bit of 'this' since
+ * we already have another hovered-state representation: */
+ if (option.state & QStyle::State_MouseOver)
+ option.state &= ~QStyle::State_MouseOver;
+ /* Remember checked-state: */
+ if (m_fChecked)
+ option.state |= QStyle::State_On;
+ /* Draw check-box for hovered-state: */
+ if (m_fHovered)
+ painter.drawControl(QStyle::CE_CheckBox, option);
+ /* Draw pixmap for unhovered-state: */
+ else
+ {
+ QRect pixmapRect = QRect(QPoint(0, 0), m_pixmapSize);
+ pixmapRect.moveCenter(option.rect.center());
+ painter.drawItemPixmap(pixmapRect, Qt::AlignCenter, m_pixmap);
+ }
+}
+
+void UIStatusBarEditorButton::mousePressEvent(QMouseEvent *pEvent)
+{
+ /* We are interested in left button only: */
+ if (pEvent->button() != Qt::LeftButton)
+ return;
+
+ /* Remember mouse-press position: */
+ m_mousePressPosition = pEvent->globalPos();
+}
+
+void UIStatusBarEditorButton::mouseReleaseEvent(QMouseEvent *pEvent)
+{
+ /* We are interested in left button only: */
+ if (pEvent->button() != Qt::LeftButton)
+ return;
+
+ /* Forget mouse-press position: */
+ m_mousePressPosition = QPoint();
+
+ /* Notify about click: */
+ emit sigClick();
+}
+
+void UIStatusBarEditorButton::enterEvent(QEvent *)
+{
+ /* Make sure button isn't hovered: */
+ if (m_fHovered)
+ return;
+
+ /* Invert hovered state: */
+ m_fHovered = true;
+ /* Update: */
+ update();
+}
+
+void UIStatusBarEditorButton::leaveEvent(QEvent *)
+{
+ /* Make sure button is hovered: */
+ if (!m_fHovered)
+ return;
+
+ /* Invert hovered state: */
+ m_fHovered = false;
+ /* Update: */
+ update();
+}
+
+void UIStatusBarEditorButton::mouseMoveEvent(QMouseEvent *pEvent)
+{
+ /* Make sure item isn't already dragged: */
+ if (m_mousePressPosition.isNull())
+ return QWidget::mouseMoveEvent(pEvent);
+
+ /* Make sure item is really dragged: */
+ if (QLineF(pEvent->globalPos(), m_mousePressPosition).length() <
+ QApplication::startDragDistance())
+ return QWidget::mouseMoveEvent(pEvent);
+
+ /* Revoke hovered state: */
+ m_fHovered = false;
+ /* Update: */
+ update();
+
+ /* Initialize dragging: */
+ m_mousePressPosition = QPoint();
+ QDrag *pDrag = new QDrag(this);
+ connect(pDrag, SIGNAL(destroyed(QObject*)), this, SIGNAL(sigDragObjectDestroy()));
+ QMimeData *pMimeData = new QMimeData;
+ pMimeData->setData(MimeType, gpConverter->toInternalString(m_enmType).toLatin1());
+ pDrag->setMimeData(pMimeData);
+ pDrag->setPixmap(m_pixmap);
+ pDrag->exec();
+}
+
+void UIStatusBarEditorButton::prepare()
+{
+ /* Track mouse events: */
+ setMouseTracking(true);
+
+ /* Calculate icon size: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pixmapSize = QSize(iIconMetric, iIconMetric);
+
+ /* Cache button size-hint: */
+ QStyleOptionButton option;
+ option.initFrom(this);
+ const QRect minRect = QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &option);
+ m_size = m_pixmapSize.expandedTo(minRect.size());
+
+ /* Update pixmap: */
+ updatePixmap();
+
+ /* Translate finally: */
+ retranslateUi();
+}
+
+void UIStatusBarEditorButton::updatePixmap()
+{
+ /* Recache pixmap for assigned type: */
+ const QIcon icon = gpConverter->toIcon(m_enmType);
+ if (window())
+ m_pixmap = icon.pixmap(window()->windowHandle(), m_pixmapSize);
+ else
+ m_pixmap = icon.pixmap(m_pixmapSize);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIStatusBarEditorWindow implementation. *
+*********************************************************************************************************************************/
+
+UIStatusBarEditorWindow::UIStatusBarEditorWindow(UIMachineWindow *pParent)
+ : UISlidingToolBar(pParent, pParent->statusBar(), new UIStatusBarEditorWidget(0, false, uiCommon().managedVMUuid()), UISlidingToolBar::Position_Bottom)
+{
+}
+
+
+/*********************************************************************************************************************************
+* Class UIStatusBarEditorWidget implementation. *
+*********************************************************************************************************************************/
+
+UIStatusBarEditorWidget::UIStatusBarEditorWidget(QWidget *pParent,
+ bool fStartedFromVMSettings /* = true */,
+ const QUuid &uMachineID /* = QString() */)
+ : QIWithRetranslateUI2<QWidget>(pParent)
+ , m_fPrepared(false)
+ , m_fStartedFromVMSettings(fStartedFromVMSettings)
+ , m_uMachineID(uMachineID)
+ , m_pMainLayout(0), m_pButtonLayout(0)
+ , m_pButtonClose(0)
+ , m_pCheckBoxEnable(0)
+ , m_pButtonDropToken(0)
+ , m_fDropAfterTokenButton(true)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIStatusBarEditorWidget::setMachineID(const QUuid &uMachineID)
+{
+ /* Remember new machine ID: */
+ m_uMachineID = uMachineID;
+ /* Prepare: */
+ prepare();
+}
+
+bool UIStatusBarEditorWidget::isStatusBarEnabled() const
+{
+ /* For VM settings only: */
+ AssertReturn(m_fStartedFromVMSettings, false);
+
+ /* Acquire enable-checkbox if possible: */
+ AssertPtrReturn(m_pCheckBoxEnable, false);
+ return m_pCheckBoxEnable->isChecked();
+}
+
+void UIStatusBarEditorWidget::setStatusBarEnabled(bool fEnabled)
+{
+ /* For VM settings only: */
+ AssertReturnVoid(m_fStartedFromVMSettings);
+
+ /* Update enable-checkbox if possible: */
+ AssertPtrReturnVoid(m_pCheckBoxEnable);
+ m_pCheckBoxEnable->setChecked(fEnabled);
+}
+
+void UIStatusBarEditorWidget::setStatusBarConfiguration(const QList<IndicatorType> &restrictions,
+ const QList<IndicatorType> &order)
+{
+ /* Cache passed restrictions: */
+ m_restrictions = restrictions;
+
+ /* Cache passed order: */
+ m_order = order;
+ /* Append order with missed indicators: */
+ for (int iType = IndicatorType_Invalid; iType < IndicatorType_Max; ++iType)
+ if (iType != IndicatorType_Invalid && iType != IndicatorType_KeyboardExtension &&
+ !m_order.contains((IndicatorType)iType))
+ m_order << (IndicatorType)iType;
+
+ /* Update configuration for all existing buttons: */
+ foreach (const IndicatorType &enmType, m_order)
+ {
+ /* Get button: */
+ UIStatusBarEditorButton *pButton = m_buttons.value(enmType);
+ /* Make sure button exists: */
+ if (!pButton)
+ continue;
+ /* Update button 'checked' state: */
+ pButton->setChecked(!m_restrictions.contains(enmType));
+ /* Make sure it have valid position: */
+ const int iWantedIndex = position(enmType);
+ const int iActualIndex = m_pButtonLayout->indexOf(pButton);
+ if (iActualIndex != iWantedIndex)
+ {
+ /* Re-inject button into main-layout at proper position: */
+ m_pButtonLayout->removeWidget(pButton);
+ m_pButtonLayout->insertWidget(iWantedIndex, pButton);
+ }
+ }
+}
+
+void UIStatusBarEditorWidget::retranslateUi()
+{
+ /* Translate widget itself: */
+ setToolTip(tr("Allows to modify VM status-bar contents."));
+
+ /* Translate close-button if necessary: */
+ if (!m_fStartedFromVMSettings && m_pButtonClose)
+ m_pButtonClose->setToolTip(tr("Close"));
+ /* Translate enable-checkbox if necessary: */
+ if (m_fStartedFromVMSettings && m_pCheckBoxEnable)
+ m_pCheckBoxEnable->setToolTip(tr("Enable Status Bar"));
+}
+
+void UIStatusBarEditorWidget::paintEvent(QPaintEvent *)
+{
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Prepare palette colors: */
+ const QPalette pal = QApplication::palette();
+ QColor color0 = pal.color(QPalette::Window);
+ QColor color1 = pal.color(QPalette::Window).lighter(110);
+ color1.setAlpha(0);
+ QColor color2 = pal.color(QPalette::Window).darker(200);
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ QColor color3 = pal.color(QPalette::Window).darker(120);
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+ /* Acquire metric: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Left corner: */
+ QRadialGradient grad1(QPointF(iMetric, iMetric), iMetric);
+ {
+ grad1.setColorAt(0, color2);
+ grad1.setColorAt(1, color1);
+ }
+ /* Right corner: */
+ QRadialGradient grad2(QPointF(width() - iMetric, iMetric), iMetric);
+ {
+ grad2.setColorAt(0, color2);
+ grad2.setColorAt(1, color1);
+ }
+ /* Top line: */
+ QLinearGradient grad3(QPointF(iMetric, 0), QPointF(iMetric, iMetric));
+ {
+ grad3.setColorAt(0, color1);
+ grad3.setColorAt(1, color2);
+ }
+ /* Left line: */
+ QLinearGradient grad4(QPointF(0, iMetric), QPointF(iMetric, iMetric));
+ {
+ grad4.setColorAt(0, color1);
+ grad4.setColorAt(1, color2);
+ }
+ /* Right line: */
+ QLinearGradient grad5(QPointF(width(), iMetric), QPointF(width() - iMetric, iMetric));
+ {
+ grad5.setColorAt(0, color1);
+ grad5.setColorAt(1, color2);
+ }
+
+ /* Paint shape/shadow: */
+ painter.fillRect(QRect(iMetric, iMetric, width() - iMetric * 2, height() - iMetric), color0); // background
+ painter.fillRect(QRect(0, 0, iMetric, iMetric), grad1); // left corner
+ painter.fillRect(QRect(width() - iMetric, 0, iMetric, iMetric), grad2); // right corner
+ painter.fillRect(QRect(iMetric, 0, width() - iMetric * 2, iMetric), grad3); // bottom line
+ painter.fillRect(QRect(0, iMetric, iMetric, height() - iMetric), grad4); // left line
+ painter.fillRect(QRect(width() - iMetric, iMetric, iMetric, height() - iMetric), grad5); // right line
+
+#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
+ /* Paint frames: */
+ painter.save();
+ painter.setPen(color3);
+ painter.drawLine(QLine(QPoint(iMetric + 1, iMetric + 1),
+ QPoint(width() - 1 - iMetric - 1, iMetric + 1)));
+ painter.drawLine(QLine(QPoint(width() - 1 - iMetric - 1, iMetric + 1),
+ QPoint(width() - 1 - iMetric - 1, height() - 1)));
+ painter.drawLine(QLine(QPoint(width() - 1 - iMetric - 1, height() - 1),
+ QPoint(iMetric + 1, height() - 1)));
+ painter.drawLine(QLine(QPoint(iMetric + 1, height() - 1),
+ QPoint(iMetric + 1, iMetric + 1)));
+ painter.restore();
+#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
+
+ /* Paint drop token: */
+ if (m_pButtonDropToken)
+ {
+ QStyleOption option;
+ option.state |= QStyle::State_Horizontal;
+ const QRect geo = m_pButtonDropToken->geometry();
+ option.rect = !m_fDropAfterTokenButton ?
+ QRect(geo.topLeft() - QPoint(iMetric, iMetric),
+ geo.bottomLeft() + QPoint(0, iMetric)) :
+ QRect(geo.topRight() - QPoint(0, iMetric),
+ geo.bottomRight() + QPoint(iMetric, iMetric));
+ QApplication::style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator,
+ &option, &painter);
+ }
+}
+
+void UIStatusBarEditorWidget::dragEnterEvent(QDragEnterEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+ /* And mime-data is set: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ AssertPtrReturnVoid(pMimeData);
+ /* Make sure mime-data format is valid: */
+ if (!pMimeData->hasFormat(UIStatusBarEditorButton::MimeType))
+ return;
+
+ /* Accept drag-enter event: */
+ pEvent->acceptProposedAction();
+}
+
+void UIStatusBarEditorWidget::dragMoveEvent(QDragMoveEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+ /* And mime-data is set: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ AssertPtrReturnVoid(pMimeData);
+ /* Make sure mime-data format is valid: */
+ if (!pMimeData->hasFormat(UIStatusBarEditorButton::MimeType))
+ return;
+
+ /* Reset token: */
+ m_pButtonDropToken = 0;
+ m_fDropAfterTokenButton = true;
+
+ /* Get event position: */
+ const QPoint pos = pEvent->pos();
+ /* Search for most suitable button: */
+ foreach (const IndicatorType &enmType, m_order)
+ {
+ m_pButtonDropToken = m_buttons.value(enmType);
+ const QRect geo = m_pButtonDropToken->geometry();
+ if (pos.x() < geo.center().x())
+ {
+ m_fDropAfterTokenButton = false;
+ break;
+ }
+ }
+ /* Update: */
+ update();
+}
+
+void UIStatusBarEditorWidget::dragLeaveEvent(QDragLeaveEvent *)
+{
+ /* Reset token: */
+ m_pButtonDropToken = 0;
+ m_fDropAfterTokenButton = true;
+ /* Update: */
+ update();
+}
+
+void UIStatusBarEditorWidget::dropEvent(QDropEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+ /* And mime-data is set: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ AssertPtrReturnVoid(pMimeData);
+ /* Make sure mime-data format is valid: */
+ if (!pMimeData->hasFormat(UIStatusBarEditorButton::MimeType))
+ return;
+
+ /* Make sure token-button set: */
+ if (!m_pButtonDropToken)
+ return;
+
+ /* Determine type of token-button: */
+ const IndicatorType tokenType = m_pButtonDropToken->type();
+ /* Determine type of dropped-button: */
+ const QString strDroppedType =
+ QString::fromLatin1(pMimeData->data(UIStatusBarEditorButton::MimeType));
+ const IndicatorType droppedType =
+ gpConverter->fromInternalString<IndicatorType>(strDroppedType);
+
+ /* Make sure these types are different: */
+ if (droppedType == tokenType)
+ return;
+
+ /* Remove type of dropped-button: */
+ m_order.removeAll(droppedType);
+ /* Insert type of dropped-button into position of token-button: */
+ int iPosition = m_order.indexOf(tokenType);
+ if (m_fDropAfterTokenButton)
+ ++iPosition;
+ m_order.insert(iPosition, droppedType);
+
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply status-bar configuration from cache: */
+ setStatusBarConfiguration(m_restrictions, m_order);
+ }
+ else
+ {
+ /* Save updated status-bar indicator order: */
+ gEDataManager->setStatusBarIndicatorOrder(m_order, machineID());
+ }
+}
+
+void UIStatusBarEditorWidget::sltHandleConfigurationChange(const QUuid &uMachineID)
+{
+ /* Skip unrelated machine IDs: */
+ if (machineID() != uMachineID)
+ return;
+
+ /* Recache status-bar configuration: */
+ setStatusBarConfiguration(gEDataManager->restrictedStatusBarIndicators(machineID()),
+ gEDataManager->statusBarIndicatorOrder(machineID()));
+}
+
+void UIStatusBarEditorWidget::sltHandleButtonClick()
+{
+ /* Make sure sender is valid: */
+ UIStatusBarEditorButton *pButton = qobject_cast<UIStatusBarEditorButton*>(sender());
+ AssertPtrReturnVoid(pButton);
+
+ /* Get sender type: */
+ const IndicatorType enmType = pButton->type();
+
+ /* Invert restriction for sender type: */
+ if (m_restrictions.contains(enmType))
+ m_restrictions.removeAll(enmType);
+ else
+ m_restrictions.append(enmType);
+
+ if (m_fStartedFromVMSettings)
+ {
+ /* Reapply status-bar configuration from cache: */
+ setStatusBarConfiguration(m_restrictions, m_order);
+ }
+ else
+ {
+ /* Save updated status-bar indicator restrictions: */
+ gEDataManager->setRestrictedStatusBarIndicators(m_restrictions, machineID());
+ }
+}
+
+void UIStatusBarEditorWidget::sltHandleDragObjectDestroy()
+{
+ /* Reset token: */
+ m_pButtonDropToken = 0;
+ m_fDropAfterTokenButton = true;
+ /* Update: */
+ update();
+}
+
+void UIStatusBarEditorWidget::prepare()
+{
+ /* Do nothing if already prepared: */
+ if (m_fPrepared)
+ return;
+
+ /* Do not prepare if machine ID is not set: */
+ if (m_uMachineID.isNull())
+ return;
+
+ /* Install tool-bar button accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUIStatusBarEditorButton::pFactory);
+
+ /* Track D&D events: */
+ setAcceptDrops(true);
+
+ /* Create main-layout: */
+ m_pMainLayout = new QHBoxLayout(this);
+ AssertPtrReturnVoid(m_pMainLayout);
+ {
+ /* Configure main-layout: */
+ int iLeft, iTop, iRight, iBottom;
+ m_pMainLayout->getContentsMargins(&iLeft, &iTop, &iRight, &iBottom);
+ /* Acquire metric: */
+ const int iStandardMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+ const int iMinimumMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+ /* Standard margins should not be too small/large: */
+ iLeft = iStandardMetric;
+ iTop = iStandardMetric;
+ iRight = iStandardMetric;
+ iBottom = iStandardMetric;
+ /* Bottom margin should be smaller for the common case: */
+ if (iBottom >= iMinimumMetric)
+ iBottom -= iMinimumMetric;
+ /* Left margin should be bigger for the settings case: */
+ if (m_fStartedFromVMSettings)
+ iLeft += iMinimumMetric;
+ /* Apply margins/spacing finally: */
+ m_pMainLayout->setContentsMargins(iLeft, iTop, iRight, iBottom);
+ m_pMainLayout->setSpacing(0);
+ /* Create close-button if necessary: */
+ if (!m_fStartedFromVMSettings)
+ {
+ m_pButtonClose = new QIToolButton;
+ AssertPtrReturnVoid(m_pButtonClose);
+ {
+ /* Configure close-button: */
+ m_pButtonClose->setFocusPolicy(Qt::StrongFocus);
+ m_pButtonClose->setShortcut(Qt::Key_Escape);
+ m_pButtonClose->setIcon(UIIconPool::iconSet(":/ok_16px.png"));
+ connect(m_pButtonClose, SIGNAL(clicked(bool)), this, SIGNAL(sigCancelClicked()));
+ /* Add close-button into main-layout: */
+ m_pMainLayout->addWidget(m_pButtonClose);
+ }
+ }
+ /* Create enable-checkbox if necessary: */
+ else
+ {
+ m_pCheckBoxEnable = new QCheckBox;
+ AssertPtrReturnVoid(m_pCheckBoxEnable);
+ {
+ /* Configure enable-checkbox: */
+ m_pCheckBoxEnable->setFocusPolicy(Qt::StrongFocus);
+ /* Add enable-checkbox into main-layout: */
+ m_pMainLayout->addWidget(m_pCheckBoxEnable);
+ }
+ }
+ /* Insert stretch: */
+ m_pMainLayout->addStretch();
+ /* Create button-layout: */
+ m_pButtonLayout = new QHBoxLayout;
+ AssertPtrReturnVoid(m_pButtonLayout);
+ {
+ /* Configure button-layout: */
+ m_pButtonLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ m_pButtonLayout->setSpacing(5);
+#else
+ m_pButtonLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2);
+#endif
+ /* Add button-layout into main-layout: */
+ m_pMainLayout->addLayout(m_pButtonLayout);
+ }
+ /* Prepare status buttons: */
+ prepareStatusButtons();
+ }
+
+ /* Mark as prepared: */
+ m_fPrepared = true;
+
+ /* Translate contents: */
+ retranslateUi();
+}
+
+void UIStatusBarEditorWidget::prepareStatusButtons()
+{
+ /* Create status buttons: */
+ for (int i = IndicatorType_Invalid; i < IndicatorType_Max; ++i)
+ {
+ /* Get current type: */
+ const IndicatorType enmType = (IndicatorType)i;
+ /* Skip inappropriate types: */
+ if (enmType == IndicatorType_Invalid || enmType == IndicatorType_KeyboardExtension)
+ continue;
+ /* Create status button: */
+ prepareStatusButton(enmType);
+ }
+
+ if (!m_fStartedFromVMSettings)
+ {
+ /* Cache status-bar configuration: */
+ setStatusBarConfiguration(gEDataManager->restrictedStatusBarIndicators(machineID()),
+ gEDataManager->statusBarIndicatorOrder(machineID()));
+ /* And listen for the status-bar configuration changes after that: */
+ connect(gEDataManager, &UIExtraDataManager::sigStatusBarConfigurationChange,
+ this, &UIStatusBarEditorWidget::sltHandleConfigurationChange);
+ }
+}
+
+void UIStatusBarEditorWidget::prepareStatusButton(IndicatorType enmType)
+{
+ /* Create status button: */
+ UIStatusBarEditorButton *pButton = new UIStatusBarEditorButton(enmType);
+ AssertPtrReturnVoid(pButton);
+ {
+ /* Configure status button: */
+ connect(pButton, &UIStatusBarEditorButton::sigClick, this, &UIStatusBarEditorWidget::sltHandleButtonClick);
+ connect(pButton, &UIStatusBarEditorButton::sigDragObjectDestroy, this, &UIStatusBarEditorWidget::sltHandleDragObjectDestroy);
+ /* Add status button into button-layout: */
+ m_pButtonLayout->addWidget(pButton);
+ /* Insert status button into map: */
+ m_buttons.insert(enmType, pButton);
+ }
+}
+
+int UIStatusBarEditorWidget::position(IndicatorType enmType) const
+{
+ int iPosition = 0;
+ foreach (const IndicatorType &iteratedType, m_order)
+ if (iteratedType == enmType)
+ return iPosition;
+ else
+ ++iPosition;
+ return iPosition;
+}
+
+
+#include "UIStatusBarEditorWindow.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIStatusBarEditorWindow.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIStatusBarEditorWindow.h
new file mode 100644
index 00000000..247d7121
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIStatusBarEditorWindow.h
@@ -0,0 +1,192 @@
+/* $Id: UIStatusBarEditorWindow.h $ */
+/** @file
+ * VBox Qt GUI - UIStatusBarEditorWindow class declaration.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIStatusBarEditorWindow_h
+#define FEQT_INCLUDED_SRC_widgets_UIStatusBarEditorWindow_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QList>
+#include <QMap>
+#include <QUuid>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+#include "UISlidingToolBar.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QDragEnterEvent;
+class QDragLeaveEvent;
+class QDragMoveEvent;
+class QDropEvent;
+class QHBoxLayout;
+class QPaintEvent;
+class QString;
+class QWidget;
+class QIToolButton;
+class UIMachineWindow;
+class UIStatusBarEditorButton;
+
+
+/** UISlidingToolBar subclass
+ * providing user with possibility to edit status-bar layout. */
+class SHARED_LIBRARY_STUFF UIStatusBarEditorWindow : public UISlidingToolBar
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs sliding toolbar passing @a pParent to the base-class. */
+ UIStatusBarEditorWindow(UIMachineWindow *pParent);
+};
+
+
+/** QWidget subclass
+ * used as status-bar editor widget. */
+class SHARED_LIBRARY_STUFF UIStatusBarEditorWidget : public QIWithRetranslateUI2<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about Cancel button click. */
+ void sigCancelClicked();
+
+public:
+
+ /** Constructs status-bar editor widget passing @a pParent to the base-class.
+ * @param fStartedFromVMSettings Brings whether 'this' is a part of VM settings.
+ * @param uMachineID Brings the machine ID to be used by the editor. */
+ UIStatusBarEditorWidget(QWidget *pParent,
+ bool fStartedFromVMSettings = true,
+ const QUuid &uMachineID = QUuid());
+
+ /** Returns the machine ID instance. */
+ const QUuid &machineID() const { return m_uMachineID; }
+ /** Defines the @a uMachineID instance. */
+ void setMachineID(const QUuid &uMachineID);
+
+ /** Returns whether the status-bar enabled. */
+ bool isStatusBarEnabled() const;
+ /** Defines whether the status-bar @a fEnabled. */
+ void setStatusBarEnabled(bool fEnabled);
+
+ /** Returns status-bar indicator restrictions. */
+ const QList<IndicatorType> &statusBarIndicatorRestrictions() const { return m_restrictions; }
+ /** Returns status-bar indicator order. */
+ const QList<IndicatorType> &statusBarIndicatorOrder() const { return m_order; }
+ /** Defines status-bar indicator @a restrictions and @a order. */
+ void setStatusBarConfiguration(const QList<IndicatorType> &restrictions, const QList<IndicatorType> &order);
+
+protected:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles drag-enter @a pEvent. */
+ virtual void dragEnterEvent(QDragEnterEvent *pEvent) RT_OVERRIDE;
+ /** Handles drag-move @a pEvent. */
+ virtual void dragMoveEvent(QDragMoveEvent *pEvent) RT_OVERRIDE;
+ /** Handles drag-leave @a pEvent. */
+ virtual void dragLeaveEvent(QDragLeaveEvent *pEvent) RT_OVERRIDE;
+ /** Handles drop @a pEvent. */
+ virtual void dropEvent(QDropEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles configuration change. */
+ void sltHandleConfigurationChange(const QUuid &uMachineID);
+
+ /** Handles button click. */
+ void sltHandleButtonClick();
+
+ /** Handles drag object destroy. */
+ void sltHandleDragObjectDestroy();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares status-buttons. */
+ void prepareStatusButtons();
+ /** Prepares status-button of certain @a enmType. */
+ void prepareStatusButton(IndicatorType enmType);
+
+ /** Returns position for passed @a enmType. */
+ int position(IndicatorType enmType) const;
+
+ /** @name General
+ * @{ */
+ /** Holds whether 'this' is prepared. */
+ bool m_fPrepared;
+ /** Holds whether 'this' is a part of VM settings. */
+ bool m_fStartedFromVMSettings;
+ /** Holds the machine ID instance. */
+ QUuid m_uMachineID;
+ /** @} */
+
+ /** @name Contents
+ * @{ */
+ /** Holds the main-layout instance. */
+ QHBoxLayout *m_pMainLayout;
+ /** Holds the button-layout instance. */
+ QHBoxLayout *m_pButtonLayout;
+ /** Holds the close-button instance. */
+ QIToolButton *m_pButtonClose;
+ /** Holds the enable-checkbox instance. */
+ QCheckBox *m_pCheckBoxEnable;
+ /** Holds status-bar buttons. */
+ QMap<IndicatorType, UIStatusBarEditorButton*> m_buttons;
+ /** @} */
+
+ /** @name Contents: Restrictions
+ * @{ */
+ /** Holds the cached status-bar button restrictions. */
+ QList<IndicatorType> m_restrictions;
+ /** @} */
+
+ /** @name Contents: Order
+ * @{ */
+ /** Holds the cached status-bar button order. */
+ QList<IndicatorType> m_order;
+ /** Holds the token-button to drop dragged-button nearby. */
+ UIStatusBarEditorButton *m_pButtonDropToken;
+ /** Holds whether dragged-button should be dropped <b>after</b> the token-button. */
+ bool m_fDropAfterTokenButton;
+ /** @} */
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIStatusBarEditorWindow_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UITabBar.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UITabBar.cpp
new file mode 100644
index 00000000..924f0544
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UITabBar.cpp
@@ -0,0 +1,1083 @@
+/* $Id: UITabBar.cpp $ */
+/** @file
+ * VBox Qt GUI - UITabBar class implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAction>
+#include <QApplication>
+#include <QDrag>
+#include <QDragEnterEvent>
+#include <QDragMoveEvent>
+#include <QDropEvent>
+#include <QEvent>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QMimeData>
+#include <QMouseEvent>
+#include <QStyleOption>
+#include <QPainter>
+#ifdef VBOX_WS_MAC
+# include <QStackedLayout>
+#endif
+#include <QStyle>
+#include <QToolButton>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIIconPool.h"
+#include "UITabBar.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+/* Forward declarations: */
+class QApplication;
+class QDrag;
+class QEvent;
+class QHBoxLayout;
+class QLabel;
+class QMimeData;
+class QMouseEvent;
+#ifdef VBOX_WS_MAC
+class QStackedLayout;
+#endif
+class QStyle;
+class QStyleOption;
+class QToolButton;
+
+
+/** Our own skinnable implementation of tabs for tab-bar. */
+class UITabBarItem : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about item was clicked. */
+ void sigClicked(UITabBarItem *pItem);
+
+ /** Notifies about item close button was clicked. */
+ void sigCloseClicked(UITabBarItem *pItem);
+
+ /** Notifies about drag-object destruction. */
+ void sigDragObjectDestroy();
+
+public:
+
+ /** Position styles. */
+ enum PositionStyle { PositionStyle_Left, PositionStyle_Middle, PositionStyle_Right, PositionStyle_Single };
+
+ /** Holds the mime-type for the D&D system. */
+ static const QString MimeType;
+
+ /** Creates tab-bar item on the basis of passed @a uuid and @a pAction. */
+ UITabBarItem(const QUuid &uuid, const QAction *pAction);
+
+ /** Returns item ID. */
+ const QUuid uuid() const { return m_uuid; }
+
+ /** Defines the item @a enmPositionStyle. */
+ void setPositionStyle(PositionStyle enmPositionStyle);
+
+ /** Marks item @a fCurrent. */
+ void setCurrent(bool fCurrent);
+
+protected:
+
+ /** Handles any Qt @a pEvent. */
+ virtual bool event(QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles mouse-press @a pEvent. */
+ virtual void mousePressEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-release @a pEvent. */
+ virtual void mouseReleaseEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-move @a pEvent. */
+ virtual void mouseMoveEvent(QMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-enter @a pEvent. */
+#ifdef VBOX_IS_QT6_OR_LATER /* QWidget::enterEvent uses QEnterEvent since qt6 */
+ virtual void enterEvent(QEnterEvent *pEvent) RT_OVERRIDE;
+#else
+ virtual void enterEvent(QEvent *pEvent) RT_OVERRIDE;
+#endif
+ /** Handles mouse-leave @a pEvent. */
+ virtual void leaveEvent(QEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles close button click. */
+ void sltCloseClicked() { emit sigCloseClicked(this); }
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Update pixmap. */
+ void updatePixmap();
+
+ /** Holds the item ID. */
+ const QUuid m_uuid;
+ /** Holds the item action reference. */
+ const QAction *m_pAction;
+
+ /** Holds the item position style. */
+ PositionStyle m_enmPosition;
+
+ /** Holds whether the item is current. */
+ bool m_fCurrent;
+ /** Holds whether the item is hovered. */
+ bool m_fHovered;
+
+ /** Holds the main layout instance. */
+ QHBoxLayout *m_pLayout;
+#ifdef VBOX_WS_MAC
+ /** Holds the stacked layout instance. */
+ QStackedLayout *m_pLayoutStacked;
+#endif
+
+ /** Holds the icon label instance. */
+ QLabel *m_pLabelIcon;
+ /** Holds the name label instance. */
+ QLabel *m_pLabelName;
+ /** Holds the close button instance. */
+ QToolButton *m_pButtonClose;
+
+ /** Holds the last mouse-press position. */
+ QPoint m_mousePressPosition;
+};
+
+
+/*********************************************************************************************************************************
+* Class UITabBarItem implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+const QString UITabBarItem::MimeType = QString("application/virtualbox;value=TabID");
+
+UITabBarItem::UITabBarItem(const QUuid &uuid, const QAction *pAction)
+ : m_uuid(uuid)
+ , m_pAction(pAction)
+ , m_enmPosition(PositionStyle_Single)
+ , m_fCurrent(false)
+ , m_fHovered(false)
+ , m_pLayout(0)
+#ifdef VBOX_WS_MAC
+ , m_pLayoutStacked(0)
+#endif
+ , m_pLabelIcon(0)
+ , m_pLabelName(0)
+ , m_pButtonClose(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UITabBarItem::setPositionStyle(PositionStyle enmPosition)
+{
+ /* Remember the position: */
+ m_enmPosition = enmPosition;
+
+ /* And call for repaint: */
+ update();
+}
+
+void UITabBarItem::setCurrent(bool fCurrent)
+{
+ /* Remember the state: */
+ m_fCurrent = fCurrent;
+
+#ifdef VBOX_WS_MAC
+ /* Adjust name color: */
+ QPalette pal = qApp->palette();
+ if (m_fCurrent)
+ pal.setColor(QPalette::ButtonText, pal.color(QPalette::BrightText));
+ m_pLabelName->setPalette(pal);
+#endif
+
+ /* And call for repaint: */
+ update();
+}
+
+bool UITabBarItem::event(QEvent *pEvent)
+{
+ /* Handle know event types: */
+ switch (pEvent->type())
+ {
+ case QEvent::Show:
+ case QEvent::ScreenChangeInternal:
+ {
+ /* Update pixmap: */
+ updatePixmap();
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QWidget>::event(pEvent);
+}
+
+void UITabBarItem::retranslateUi()
+{
+ /* Translate label: */
+ m_pLabelName->setText(m_pAction->text().remove('&'));
+}
+
+void UITabBarItem::paintEvent(QPaintEvent * /* pEvent */)
+{
+#ifdef VBOX_WS_MAC
+
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Prepare palette colors: */
+ const QPalette pal = QApplication::palette();
+ const QColor color0 = m_fCurrent
+ ? pal.color(QPalette::Shadow).darker(110)
+ : pal.color(QPalette::Window).lighter(105);
+ const QColor color1 = pal.color(QPalette::Window);
+ const QColor color2 = color0.darker(120);
+ const QColor color3 = color0.darker(130);
+
+ /* Invent pixel metric: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
+
+ /* Top-left corner: */
+ QRadialGradient grad1(QPointF(iMetric, iMetric), iMetric);
+ {
+ grad1.setColorAt(0, color0);
+ grad1.setColorAt(.8, color0);
+ grad1.setColorAt(.81, color2);
+ grad1.setColorAt(1, color1);
+ }
+ /* Top-right corner: */
+ QRadialGradient grad2(QPointF(width() - iMetric, iMetric), iMetric);
+ {
+ grad2.setColorAt(0, color0);
+ grad2.setColorAt(.8, color0);
+ grad2.setColorAt(.81, color2);
+ grad2.setColorAt(1, color1);
+ }
+ /* Bottom-left corner: */
+ QRadialGradient grad3(QPointF(iMetric, height() - iMetric), iMetric);
+ {
+ grad3.setColorAt(0, color0);
+ grad3.setColorAt(.8, color0);
+ grad3.setColorAt(.81, color3);
+ grad3.setColorAt(1, color1);
+ }
+ /* Botom-right corner: */
+ QRadialGradient grad4(QPointF(width() - iMetric, height() - iMetric), iMetric);
+ {
+ grad4.setColorAt(0, color0);
+ grad4.setColorAt(.8, color0);
+ grad4.setColorAt(.81, color3);
+ grad4.setColorAt(1, color1);
+ }
+
+ /* Top line: */
+ QLinearGradient grad5(QPointF(iMetric, 0), QPointF(iMetric, iMetric));
+ {
+ grad5.setColorAt(0, color1);
+ grad5.setColorAt(.19, color2);
+ grad5.setColorAt(.2, color0);
+ grad5.setColorAt(1, color0);
+ }
+ /* Bottom line: */
+ QLinearGradient grad6(QPointF(iMetric, height()), QPointF(iMetric, height() - iMetric));
+ {
+ grad6.setColorAt(0, color1);
+ grad6.setColorAt(.19, color3);
+ grad6.setColorAt(.2, color0);
+ grad6.setColorAt(1, color0);
+ }
+ /* Left line: */
+ QLinearGradient grad7(QPointF(0, height() - iMetric), QPointF(iMetric, height() - iMetric));
+ {
+ grad7.setColorAt(0, color1);
+ grad7.setColorAt(.19, color2);
+ grad7.setColorAt(.2, color0);
+ grad7.setColorAt(1, color0);
+ }
+ /* Right line: */
+ QLinearGradient grad8(QPointF(width(), height() - iMetric), QPointF(width() - iMetric, height() - iMetric));
+ {
+ grad8.setColorAt(0, color1);
+ grad8.setColorAt(.19, color2);
+ grad8.setColorAt(.2, color0);
+ grad8.setColorAt(1, color0);
+ }
+
+ /* Paint: */
+ painter.fillRect(QRect(iMetric, iMetric, width() - iMetric * 2, height() - iMetric * 2), color0);
+
+ if (m_enmPosition == PositionStyle_Left || m_enmPosition == PositionStyle_Single)
+ {
+ painter.fillRect(QRect(0, 0, iMetric, iMetric), grad1);
+ painter.fillRect(QRect(0, height() - iMetric, iMetric, iMetric), grad3);
+ }
+ if (m_enmPosition == PositionStyle_Right || m_enmPosition == PositionStyle_Single)
+ {
+ painter.fillRect(QRect(width() - iMetric, 0, iMetric, iMetric), grad2);
+ painter.fillRect(QRect(width() - iMetric, height() - iMetric, iMetric, iMetric), grad4);
+ }
+
+ int iX = 0;
+ int iYL = 0;
+ int iYR = 0;
+ int iWid = width();
+ int iHeiL = height();
+ int iHeiR = height();
+ if (m_enmPosition == PositionStyle_Left || m_enmPosition == PositionStyle_Single)
+ {
+ iX = iMetric;
+ iYL = iMetric;
+ iWid -= iMetric;
+ iHeiL -= iMetric * 2;
+ }
+ if (m_enmPosition == PositionStyle_Right || m_enmPosition == PositionStyle_Single)
+ {
+ iYR = iMetric;
+ iWid -= iMetric;
+ iHeiR -= iMetric * 2;
+ }
+ painter.fillRect(QRect(0, iYL, iMetric, iHeiL), grad7);
+ painter.fillRect(QRect(width() - iMetric, iYR, iMetric, iHeiR), grad8);
+ painter.fillRect(QRect(iX, 0, iWid, iMetric), grad5);
+ painter.fillRect(QRect(iX, height() - iMetric, iWid, iMetric), grad6);
+
+#else /* !VBOX_WS_MAC */
+
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Prepare palette colors: */
+ const QPalette pal = QApplication::palette();
+ const QColor color0 = m_fCurrent ? pal.color(QPalette::Base)
+ : m_fHovered ? pal.color(QPalette::Base).darker(102)
+ : pal.color(QPalette::Button).darker(102);
+ QColor color1 = color0;
+ color1.setAlpha(0);
+ QColor color2 = pal.color(QPalette::Shadow);
+
+ /* Invent pixel metric: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
+
+ /* Top-left corner: */
+ QRadialGradient grad1(QPointF(iMetric, iMetric), iMetric);
+ {
+ grad1.setColorAt(0, color0);
+ grad1.setColorAt(.8, color1);
+ grad1.setColorAt(.9, color2);
+ grad1.setColorAt(1, color1);
+ }
+ /* Bottom-left corner: */
+ QRadialGradient grad2(QPointF(iMetric, height() - iMetric), iMetric);
+ {
+ grad2.setColorAt(0, color0);
+ grad2.setColorAt(.8, color1);
+ grad2.setColorAt(.9, color2);
+ grad2.setColorAt(1, color1);
+ }
+ /* Top-right corner: */
+ QRadialGradient grad3(QPointF(width() - iMetric, iMetric), iMetric);
+ {
+ grad3.setColorAt(0, color0);
+ grad3.setColorAt(.8, color1);
+ grad3.setColorAt(.9, color2);
+ grad3.setColorAt(1, color1);
+ }
+ /* Botom-right corner: */
+ QRadialGradient grad4(QPointF(width() - iMetric, height() - iMetric), iMetric);
+ {
+ grad4.setColorAt(0, color0);
+ grad4.setColorAt(.8, color1);
+ grad4.setColorAt(.9, color2);
+ grad4.setColorAt(1, color1);
+ }
+
+ /* Left line: */
+ QLinearGradient grad5(QPointF(0, height() - iMetric), QPointF(iMetric, height() - iMetric));
+ {
+ grad5.setColorAt(0, color1);
+ grad5.setColorAt(.1, color2);
+ grad5.setColorAt(.2, color1);
+ grad5.setColorAt(1, color0);
+ }
+ /* Right line: */
+ QLinearGradient grad6(QPointF(width(), height() - iMetric), QPointF(width() - iMetric, height() - iMetric));
+ {
+ grad6.setColorAt(0, color1);
+ grad6.setColorAt(.1, color2);
+ grad6.setColorAt(.2, color1);
+ grad6.setColorAt(1, color0);
+ }
+ /* Top line: */
+ QLinearGradient grad7(QPointF(iMetric, 0), QPointF(iMetric, iMetric));
+ {
+ grad7.setColorAt(0, color1);
+ grad7.setColorAt(.1, color2);
+ grad7.setColorAt(.2, color1);
+ grad7.setColorAt(1, color0);
+ }
+ /* Bottom line: */
+ QLinearGradient grad8(QPointF(iMetric, height()), QPointF(iMetric, height() - iMetric));
+ {
+ grad8.setColorAt(0, color1);
+ grad8.setColorAt(.1, color2);
+ grad8.setColorAt(.2, color1);
+ grad8.setColorAt(1, color0);
+ }
+
+ /* Paint: */
+ painter.fillRect(QRect(iMetric, iMetric, width() - iMetric * 2, height() - iMetric * 2), color0);
+
+ if (m_enmPosition == PositionStyle_Left || m_enmPosition == PositionStyle_Single)
+ {
+ painter.fillRect(QRect(0, 0, iMetric, iMetric), grad1);
+ painter.fillRect(QRect(0, height() - iMetric, iMetric, iMetric), grad2);
+ }
+ if (m_enmPosition == PositionStyle_Right || m_enmPosition == PositionStyle_Single)
+ {
+ painter.fillRect(QRect(width() - iMetric, 0, iMetric, iMetric), grad3);
+ painter.fillRect(QRect(width() - iMetric, height() - iMetric, iMetric, iMetric), grad4);
+ }
+
+ int iX = 0;
+ int iYL = 0;
+ int iYR = 0;
+ int iWid = width();
+ int iHeiL = height();
+ int iHeiR = height();
+ if (m_enmPosition == PositionStyle_Left || m_enmPosition == PositionStyle_Single)
+ {
+ iX = iMetric;
+ iYL = iMetric;
+ iWid -= iMetric;
+ iHeiL -= iMetric * 2;
+ }
+ if (m_enmPosition == PositionStyle_Right || m_enmPosition == PositionStyle_Single)
+ {
+ iYR = iMetric;
+ iWid -= iMetric;
+ iHeiR -= iMetric * 2;
+ }
+
+ QPainterPath path5;
+ path5.moveTo(0, 0);
+ path5.lineTo(iMetric, iMetric);
+ path5.lineTo(iMetric, height() - iMetric);
+ path5.lineTo(0, height());
+ path5.closeSubpath();
+ painter.setClipPath(path5);
+ painter.fillRect(QRect(0, iYL, iMetric, iHeiL), grad5);
+ painter.setClipping(false);
+
+ QPainterPath path6;
+ path6.moveTo(width(), 0);
+ path6.lineTo(width() - iMetric, iMetric);
+ path6.lineTo(width() - iMetric, height() - iMetric);
+ path6.lineTo(width(), height());
+ path6.closeSubpath();
+ painter.setClipPath(path6);
+ painter.fillRect(QRect(width() - iMetric, iYR, iMetric, iHeiR), grad6);
+ painter.setClipping(false);
+
+ QPainterPath path7;
+ path7.moveTo(0, 0);
+ path7.lineTo(iMetric, iMetric);
+ path7.lineTo(width() - iMetric, iMetric);
+ path7.lineTo(width(), 0);
+ path7.closeSubpath();
+ painter.setClipPath(path7);
+ painter.fillRect(QRect(iX, 0, iWid, iMetric), grad7);
+ painter.setClipping(false);
+
+ QPainterPath path8;
+ path8.moveTo(0, height());
+ path8.lineTo(iMetric, height() - iMetric);
+ path8.lineTo(width() - iMetric, height() - iMetric);
+ path8.lineTo(width(), height());
+ path8.closeSubpath();
+ painter.setClipPath(path8);
+ painter.fillRect(QRect(iX, height() - iMetric, iWid, iMetric), grad8);
+ painter.setClipping(false);
+
+#endif /* !VBOX_WS_MAC */
+}
+
+void UITabBarItem::mousePressEvent(QMouseEvent *pEvent)
+{
+ /* We are interested in left button only: */
+ if (pEvent->button() != Qt::LeftButton)
+ return QWidget::mousePressEvent(pEvent);
+
+ /* Remember mouse-press position: */
+ m_mousePressPosition = pEvent->globalPos();
+}
+
+void UITabBarItem::mouseReleaseEvent(QMouseEvent *pEvent)
+{
+ /* We are interested in left button only: */
+ if (pEvent->button() != Qt::LeftButton)
+ return QWidget::mouseReleaseEvent(pEvent);
+
+ /* Forget mouse-press position: */
+ m_mousePressPosition = QPoint();
+
+ /* Notify listeners about the item was clicked: */
+ emit sigClicked(this);
+}
+
+void UITabBarItem::mouseMoveEvent(QMouseEvent *pEvent)
+{
+ /* Make sure item isn't already dragged: */
+ if (m_mousePressPosition.isNull())
+ return QWidget::mouseMoveEvent(pEvent);
+
+ /* Make sure item is now being dragged: */
+ if (QLineF(pEvent->globalPos(), m_mousePressPosition).length() < QApplication::startDragDistance())
+ return QWidget::mouseMoveEvent(pEvent);
+
+ /* Revoke hovered state: */
+#ifdef VBOX_WS_MAC
+ m_pLayoutStacked->setCurrentWidget(m_pLabelIcon);
+#endif
+ m_fHovered = false;
+ /* And call for repaint: */
+ update();
+
+ /* Initialize dragging: */
+ m_mousePressPosition = QPoint();
+ QDrag *pDrag = new QDrag(this);
+ connect(pDrag, &QObject::destroyed, this, &UITabBarItem::sigDragObjectDestroy);
+ QMimeData *pMimeData = new QMimeData;
+ pMimeData->setData(MimeType, uuid().toByteArray());
+ pDrag->setMimeData(pMimeData);
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ pDrag->setPixmap(m_pAction->icon().pixmap(window()->windowHandle(), QSize(iMetric, iMetric)));
+ pDrag->exec();
+}
+
+#ifdef VBOX_IS_QT6_OR_LATER /* QWidget::enterEvent uses QEnterEvent since qt6 */
+void UITabBarItem::enterEvent(QEnterEvent *pEvent)
+#else
+void UITabBarItem::enterEvent(QEvent *pEvent)
+#endif
+{
+ /* Make sure button isn't hovered: */
+ if (m_fHovered)
+ return QWidget::enterEvent(pEvent);
+
+ /* Invert hovered state: */
+#ifdef VBOX_WS_MAC
+ m_pLayoutStacked->setCurrentWidget(m_pButtonClose);
+#endif
+ m_fHovered = true;
+ /* And call for repaint: */
+ update();
+}
+
+void UITabBarItem::leaveEvent(QEvent *pEvent)
+{
+ /* Make sure button is hovered: */
+ if (!m_fHovered)
+ return QWidget::leaveEvent(pEvent);
+
+ /* Invert hovered state: */
+#ifdef VBOX_WS_MAC
+ m_pLayoutStacked->setCurrentWidget(m_pLabelIcon);
+#endif
+ m_fHovered = false;
+ /* And call for repaint: */
+ update();
+}
+
+void UITabBarItem::prepare()
+{
+ /* Configure self: */
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ /* Create main layout: */
+ m_pLayout = new QHBoxLayout(this);
+ if (m_pLayout)
+ {
+ /* Invent pixel metric: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+#ifdef VBOX_WS_MAC
+ const int iMargin = iMetric / 4;
+#else
+ const int iMargin = iMetric / 2;
+#endif
+ const int iSpacing = iMargin / 2;
+#ifdef VBOX_WS_MAC
+ const int iMetricCloseButton = iMetric * 3 / 4;
+#else
+ const int iMetricCloseButton = iMetric * 2 / 3;
+#endif
+
+ /* Configure layout: */
+#ifdef VBOX_WS_MAC
+ m_pLayout->setContentsMargins(iMargin + iSpacing, iMargin, iMargin + iSpacing, iMargin);
+#else
+ m_pLayout->setContentsMargins(iMargin + iSpacing, iMargin, iMargin, iMargin);
+#endif
+ m_pLayout->setSpacing(iSpacing);
+
+ /* Create icon label: */
+ m_pLabelIcon = new QLabel;
+ if (m_pLabelIcon)
+ {
+ /* Configure label: */
+ m_pLabelIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ }
+
+ /* Create name label: */
+ m_pLabelName = new QLabel;
+ if (m_pLabelName)
+ {
+ /* Configure label: */
+ m_pLabelName->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ }
+
+ /* Create close button: */
+ m_pButtonClose = new QToolButton;
+ if (m_pButtonClose)
+ {
+ /* Configure button: */
+ m_pButtonClose->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ m_pButtonClose->setIconSize(QSize(iMetricCloseButton, iMetricCloseButton));
+ m_pButtonClose->setIcon(UIIconPool::iconSet(":/close_16px.png"));
+#ifdef VBOX_WS_MAC
+ m_pButtonClose->setStyleSheet("QToolButton { border: 0px }");
+#else
+ m_pButtonClose->setAutoRaise(true);
+#endif
+ connect(m_pButtonClose, &QToolButton::clicked, this, &UITabBarItem::sltCloseClicked);
+ }
+
+#ifdef VBOX_WS_MAC
+ /* Create stacked-layout: */
+ m_pLayoutStacked = new QStackedLayout(m_pLayout);
+ if (m_pLayoutStacked)
+ {
+ m_pLayoutStacked->setAlignment(Qt::AlignCenter);
+
+ /* Add icon-label and close-button into stacked-layout: */
+ m_pLayoutStacked->addWidget(m_pLabelIcon);
+ m_pLayoutStacked->addWidget(m_pButtonClose);
+ m_pLayoutStacked->setAlignment(m_pLabelIcon, Qt::AlignCenter);
+ m_pLayoutStacked->setAlignment(m_pButtonClose, Qt::AlignCenter);
+
+ /* Add stacked-layout into main-layout: */
+ m_pLayout->addLayout(m_pLayoutStacked);
+ }
+
+ /* Add name-label into main-layout: */
+ m_pLayout->addWidget(m_pLabelName);
+#else /* !VBOX_WS_MAC */
+ /* Add everything into main-layout: */
+ m_pLayout->addWidget(m_pLabelIcon);
+ m_pLayout->addWidget(m_pLabelName);
+ m_pLayout->addWidget(m_pButtonClose);
+#endif /* !VBOX_WS_MAC */
+ }
+
+ /* Update pixmap: */
+ updatePixmap();
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+void UITabBarItem::updatePixmap()
+{
+ /* Configure label icon: */
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pLabelIcon->setPixmap(m_pAction->icon().pixmap(window()->windowHandle(), QSize(iMetric, iMetric)));
+}
+
+
+/*********************************************************************************************************************************
+* Class UITabBar implementation. *
+*********************************************************************************************************************************/
+
+UITabBar::UITabBar(Alignment enmAlignment, QWidget *pParent /* = 0 */)
+ : QWidget(pParent)
+ , m_enmAlignment(enmAlignment)
+ , m_pLayoutMain(0)
+ , m_pLayoutTab(0)
+ , m_pCurrentItem(0)
+ , m_pItemToken(0)
+ , m_fDropAfterTokenItem(0)
+{
+ /* Prepare: */
+ prepare();
+}
+
+QUuid UITabBar::addTab(const QAction *pAction)
+{
+ /* Generate unique ID: */
+ const QUuid uuid = QUuid::createUuid();
+ /* Create new tab item: */
+ UITabBarItem *pItem = new UITabBarItem(uuid, pAction);
+ AssertPtrReturn(pItem, QUuid());
+ {
+ /* Configure item: */
+ connect(pItem, &UITabBarItem::sigClicked, this, &UITabBar::sltHandleMakeChildCurrent);
+ connect(pItem, &UITabBarItem::sigCloseClicked, this, &UITabBar::sltHandleChildClose);
+ connect(pItem, &UITabBarItem::sigDragObjectDestroy, this, &UITabBar::sltHandleDragObjectDestroy);
+ /* Add item into layout and list: */
+ switch (m_enmAlignment)
+ {
+ case Align_Left:
+ m_pLayoutTab->addWidget(pItem);
+ m_aItems.append(pItem);
+ break;
+ case Align_Right:
+ m_pLayoutTab->insertWidget(0, pItem);
+ m_aItems.prepend(pItem);
+ break;
+ }
+ /* Update children styles: */
+ updateChildrenStyles();
+ /* Return unique ID: */
+ return uuid;
+ }
+}
+
+bool UITabBar::removeTab(const QUuid &uuid)
+{
+ /* Prepare result: */
+ bool fSuccess = false;
+
+ /* Do we need to bother about current item? */
+ bool fMoveCurrent = m_pCurrentItem->uuid() == uuid;
+
+ /* Search through all the items we have: */
+ for (int i = 0; i < m_aItems.size(); ++i)
+ {
+ /* Get iterated item: */
+ UITabBarItem *pItem = m_aItems.at(i);
+ /* If that item is what we are looking for: */
+ if (pItem->uuid() == uuid)
+ {
+ /* Delete it and wipe it from the list: */
+ delete pItem;
+ m_aItems[i] = 0;
+ fSuccess = true;
+ }
+ }
+ /* Flush wiped out items: */
+ m_aItems.removeAll((UITabBarItem *)0);
+
+ /* If we had removed current item: */
+ if (fMoveCurrent)
+ {
+ /* Mark it null initially: */
+ m_pCurrentItem = 0;
+ /* But choose something suitable if we have: */
+ if (!m_aItems.isEmpty())
+ sltHandleMakeChildCurrent(m_aItems.first());
+ }
+
+ /* Update children styles: */
+ updateChildrenStyles();
+
+ /* Return result: */
+ return fSuccess;
+}
+
+bool UITabBar::setCurrent(const QUuid &uuid)
+{
+ /* Prepare result: */
+ bool fSuccess = false;
+
+ /* Search through all the items we have: */
+ for (int i = 0; i < m_aItems.size(); ++i)
+ {
+ /* Get iterated item: */
+ UITabBarItem *pItem = m_aItems.at(i);
+ /* If that item is what we are looking for: */
+ if (pItem->uuid() == uuid)
+ {
+ /* Make it current: */
+ sltHandleMakeChildCurrent(pItem);
+ fSuccess = true;
+ break;
+ }
+ }
+
+ /* Return result: */
+ return fSuccess;
+}
+
+QList<QUuid> UITabBar::tabOrder() const
+{
+ QList<QUuid> list;
+ foreach (UITabBarItem *pItem, m_aItems)
+ list << pItem->uuid();
+ return list;
+}
+
+void UITabBar::paintEvent(QPaintEvent *pEvent)
+{
+ /* Call to base-class: */
+ QWidget::paintEvent(pEvent);
+
+ /* If we have a token item: */
+ if (m_pItemToken)
+ {
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Paint drop token: */
+ QStyleOption option;
+ option.state |= QStyle::State_Horizontal;
+ const QRect geo = m_pItemToken->geometry();
+ option.rect = !m_fDropAfterTokenItem
+ ? QRect(geo.topLeft() - QPoint(5, 5),
+ geo.bottomLeft() + QPoint(0, 5))
+ : QRect(geo.topRight() - QPoint(0, 5),
+ geo.bottomRight() + QPoint(5, 5));
+ QApplication::style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator,
+ &option, &painter);
+ }
+}
+
+void UITabBar::dragEnterEvent(QDragEnterEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+ /* And mime-data is set: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ AssertPtrReturnVoid(pMimeData);
+
+ /* Make sure mime-data format is valid: */
+ if (!pMimeData->hasFormat(UITabBarItem::MimeType))
+ return;
+
+ /* Accept drag-enter event: */
+ pEvent->acceptProposedAction();
+}
+
+void UITabBar::dragMoveEvent(QDragMoveEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+ /* And mime-data is set: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ AssertPtrReturnVoid(pMimeData);
+
+ /* Make sure mime-data format is valid: */
+ if (!pMimeData->hasFormat(UITabBarItem::MimeType))
+ return;
+
+ /* Reset token: */
+ m_pItemToken = 0;
+ m_fDropAfterTokenItem = true;
+
+ /* Get event position: */
+ const QPoint pos = pEvent->pos();
+ /* Search for most suitable item: */
+ foreach (UITabBarItem *pItem, m_aItems)
+ {
+ /* Advance token: */
+ m_pItemToken = pItem;
+ const QRect geo = m_pItemToken->geometry();
+ if (pos.x() < geo.center().x())
+ {
+ m_fDropAfterTokenItem = false;
+ break;
+ }
+ }
+
+ /* Update: */
+ update();
+}
+
+void UITabBar::dragLeaveEvent(QDragLeaveEvent * /* pEvent */)
+{
+ /* Reset token: */
+ m_pItemToken = 0;
+ m_fDropAfterTokenItem = true;
+
+ /* Update: */
+ update();
+}
+
+void UITabBar::dropEvent(QDropEvent *pEvent)
+{
+ /* Make sure event is valid: */
+ AssertPtrReturnVoid(pEvent);
+ /* And mime-data is set: */
+ const QMimeData *pMimeData = pEvent->mimeData();
+ AssertPtrReturnVoid(pMimeData);
+
+ /* Make sure mime-data format is valid: */
+ if (!pMimeData->hasFormat(UITabBarItem::MimeType))
+ return;
+
+ /* Make sure token-item set: */
+ if (!m_pItemToken)
+ return;
+
+ /* Determine ID of token-item: */
+ const QUuid tokenUuid = m_pItemToken->uuid();
+ /* Determine ID of dropped-item: */
+ const QUuid droppedUuid(pMimeData->data(UITabBarItem::MimeType));
+
+ /* Make sure these uuids are different: */
+ if (droppedUuid == tokenUuid)
+ return;
+
+ /* Search for an item with dropped ID: */
+ UITabBarItem *pItemDropped = 0;
+ foreach (UITabBarItem *pItem, m_aItems)
+ {
+ if (pItem->uuid() == droppedUuid)
+ {
+ pItemDropped = pItem;
+ break;
+ }
+ }
+
+ /* Make sure dropped-item found: */
+ if (!pItemDropped)
+ return;
+
+ /* Remove dropped-item: */
+ m_aItems.removeAll(pItemDropped);
+ m_pLayoutTab->removeWidget(pItemDropped);
+ /* Insert dropped-item into position of token-item: */
+ int iPosition = m_aItems.indexOf(m_pItemToken);
+ AssertReturnVoid(iPosition != -1);
+ if (m_fDropAfterTokenItem)
+ ++iPosition;
+ m_aItems.insert(iPosition, pItemDropped);
+ m_pLayoutTab->insertWidget(iPosition, pItemDropped);
+
+ /* Update children styles: */
+ updateChildrenStyles();
+}
+
+void UITabBar::sltHandleMakeChildCurrent(UITabBarItem *pItem)
+{
+ /* Make sure item exists: */
+ AssertPtrReturnVoid(pItem);
+
+ /* Remove current mark from current item if exists: */
+ if (m_pCurrentItem)
+ m_pCurrentItem->setCurrent(false);
+
+ /* Assign new current item: */
+ m_pCurrentItem = pItem;
+
+ /* Place current mark onto current item if exists: */
+ if (m_pCurrentItem)
+ m_pCurrentItem->setCurrent(true);
+
+ /* Notify listeners: */
+ emit sigCurrentTabChanged(pItem->uuid());
+}
+
+void UITabBar::sltHandleChildClose(UITabBarItem *pItem)
+{
+ /* Make sure item exists: */
+ AssertPtrReturnVoid(pItem);
+
+ /* Notify listeners: */
+ emit sigTabRequestForClosing(pItem->uuid());
+}
+
+void UITabBar::sltHandleDragObjectDestroy()
+{
+ /* Reset token: */
+ m_pItemToken = 0;
+ m_fDropAfterTokenItem = true;
+
+ /* Update: */
+ update();
+}
+
+void UITabBar::prepare()
+{
+ /* Track D&D events: */
+ setAcceptDrops(true);
+
+ /* Create main layout: */
+ m_pLayoutMain = new QHBoxLayout(this);
+ AssertPtrReturnVoid(m_pLayoutMain);
+ {
+ /* Configure layout: */
+ m_pLayoutMain->setSpacing(0);
+ m_pLayoutMain->setContentsMargins(0, 0, 0, 0);
+
+ /* Add strech to beginning: */
+ if (m_enmAlignment == Align_Right)
+ m_pLayoutMain->addStretch();
+
+ /* Create tab layout: */
+ m_pLayoutTab = new QHBoxLayout;
+ AssertPtrReturnVoid(m_pLayoutTab);
+ {
+ /* Add into layout: */
+ m_pLayoutMain->addLayout(m_pLayoutTab);
+ }
+
+ /* Add strech to end: */
+ if (m_enmAlignment == Align_Left)
+ m_pLayoutMain->addStretch();
+ }
+}
+
+void UITabBar::updateChildrenStyles()
+{
+ /* Single child has corresponding (rounded) style: */
+ if (m_aItems.size() == 1)
+ m_aItems.first()->setPositionStyle(UITabBarItem::PositionStyle_Single);
+ /* If there are more than one child: */
+ else if (m_aItems.size() > 1)
+ {
+ /* First make all children have no rounded sides: */
+ foreach (UITabBarItem *pItem, m_aItems)
+ pItem->setPositionStyle(UITabBarItem::PositionStyle_Middle);
+ /* Then make first child rounded left, while last rounded right: */
+ m_aItems.first()->setPositionStyle(UITabBarItem::PositionStyle_Left);
+ m_aItems.last()->setPositionStyle(UITabBarItem::PositionStyle_Right);
+ }
+ /* Repaint: */
+ update();
+}
+
+#include "UITabBar.moc"
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UITabBar.h b/src/VBox/Frontends/VirtualBox/src/widgets/UITabBar.h
new file mode 100644
index 00000000..3bfd7c65
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UITabBar.h
@@ -0,0 +1,156 @@
+/* $Id: UITabBar.h $ */
+/** @file
+ * VBox Qt GUI - UITabBar class declaration.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UITabBar_h
+#define FEQT_INCLUDED_SRC_widgets_UITabBar_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QString>
+#include <QUuid>
+#include <QWidget>
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+/* Forward declarations: */
+class QAction;
+class QDragEnterEvent;
+class QDragLeaveEvent;
+class QDragMoveEvent;
+class QDropEvent;
+class QHBoxLayout;
+class QIcon;
+class QPaintEvent;
+class QString;
+class QUuid;
+class QWidget;
+class UITabBarItem;
+
+
+/** Our own skinnable implementation of tab-bar.
+ * The idea is to make tab-bar analog which looks more interesting
+ * on various platforms, allows for various skins, and tiny adjustments. */
+class UITabBar : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about tab with @a uuid requested closing. */
+ void sigTabRequestForClosing(const QUuid &uuid);
+ /** Notifies about tab with @a uuid set to current. */
+ void sigCurrentTabChanged(const QUuid &uuid);
+
+public:
+
+ /** Alignment types. */
+ enum Alignment { Align_Left, Align_Right };
+
+ /** Constructs tab-bar passing @a pParent to the base-class. */
+ UITabBar(Alignment enmAlignment, QWidget *pParent = 0);
+
+ /** Adds new tab for passed @a pAction. @returns unique tab ID. */
+ QUuid addTab(const QAction *pAction);
+
+ /** Removes tab with passed @a uuid. */
+ bool removeTab(const QUuid &uuid);
+
+ /** Makes tab with passed @a uuid current. */
+ bool setCurrent(const QUuid &uuid);
+
+ /** Return tab-bar order ID list. */
+ QList<QUuid> tabOrder() const;
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles drag-enter @a pEvent. */
+ virtual void dragEnterEvent(QDragEnterEvent *pEvent) RT_OVERRIDE;
+ /** Handles drag-move @a pEvent. */
+ virtual void dragMoveEvent(QDragMoveEvent *pEvent) RT_OVERRIDE;
+ /** Handles drag-leave @a pEvent. */
+ virtual void dragLeaveEvent(QDragLeaveEvent *pEvent) RT_OVERRIDE;
+ /** Handles drop @a pEvent. */
+ virtual void dropEvent(QDropEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles request to make @a pItem current. */
+ void sltHandleMakeChildCurrent(UITabBarItem *pItem);
+
+ /** Handles request to close @a pItem. */
+ void sltHandleChildClose(UITabBarItem *pItem);
+
+ /** Handles drag object destruction. */
+ void sltHandleDragObjectDestroy();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Updates children styles. */
+ void updateChildrenStyles();
+
+ /** @name Contents: Common
+ * @{ */
+ /** Holds the alignment. */
+ Alignment m_enmAlignment;
+ /** @} */
+
+ /** @name Contents: Widgets
+ * @{ */
+ /** Holds the main layout instance. */
+ QHBoxLayout *m_pLayoutMain;
+ /** Holds the tab layout instance. */
+ QHBoxLayout *m_pLayoutTab;
+
+ /** Holds the current item reference. */
+ UITabBarItem *m_pCurrentItem;
+
+ /** Holds the array of items instances. */
+ QList<UITabBarItem*> m_aItems;
+ /** @} */
+
+ /** @name Contents: Order
+ * @{ */
+ /** Holds the token-item to drop dragged-item nearby. */
+ UITabBarItem *m_pItemToken;
+
+ /** Holds whether the dragged-item should be dropped <b>after</b> the token-item. */
+ bool m_fDropAfterTokenItem;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UITabBar_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIToolBox.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIToolBox.cpp
new file mode 100644
index 00000000..773c6b8d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIToolBox.cpp
@@ -0,0 +1,479 @@
+/* $Id: UIToolBox.cpp $ */
+/** @file
+ * VBox Qt GUI - UIToolBox class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QLabel>
+#include <QPainter>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "QIToolButton.h"
+#include "UICommon.h"
+#include "UIIconPool.h"
+#include "UIToolBox.h"
+#include "UIWizardNewVM.h"
+
+
+/*********************************************************************************************************************************
+* UIToolPageButton definition. *
+*********************************************************************************************************************************/
+/** A QAbstractButton extension used to show collapse/expand icons. More importantly
+ * it is buddy to the title label which may include some mnemonics. This makes it possible
+ * to expand pages via keyboard. */
+class UIToolPageButton : public QAbstractButton
+{
+
+ Q_OBJECT;
+
+public:
+
+ UIToolPageButton(QWidget *pParent = 0);
+ void setPixmap(const QPixmap &pixmap);
+
+protected:
+
+ virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
+ virtual QSize sizeHint() const RT_OVERRIDE;
+
+private:
+
+ /** Holds the pixmap of the expand/collapser icon. We keep
+ * QPixmap instead of QIcon since it is rotated when the
+ * the page is expanded and end product of rotation is a pixmap.
+ * and we use QPainter to draw pixmap.*/
+ QPixmap m_pixmap;
+};
+
+
+/*********************************************************************************************************************************
+* UIToolBoxPage definition. *
+*********************************************************************************************************************************/
+
+class UIToolBoxPage : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigShowPageWidget();
+
+public:
+
+ UIToolBoxPage(bool fEnableCheckBoxEnabled = false, QWidget *pParent = 0);
+ void setTitle(const QString &strTitle);
+ void setTitleBackgroundColor(const QColor &color);
+ void setExpanded(bool fExpanded);
+ int index() const;
+ void setIndex(int iIndex);
+ int totalHeight() const;
+ int titleHeight() const;
+ QSize pageWidgetSize() const;
+ void setTitleIcon(const QIcon &icon, const QString &strToolTip);
+
+protected:
+
+ virtual bool eventFilter(QObject *pWatched, QEvent *pEvent) RT_OVERRIDE;
+ virtual void retranslateUi() /* override final */;
+
+private slots:
+
+ void sltHandleEnableToggle(int iState);
+
+private:
+
+ void prepare(bool fEnableCheckBoxEnabled);
+ void setExpandCollapseIcon();
+ /* @p pWidget's ownership is transferred to the page. */
+ void setWidget(QWidget *pWidget);
+
+ bool m_fExpanded;
+ QVBoxLayout *m_pLayout;
+ QWidget *m_pTitleContainerWidget;
+ QLabel *m_pTitleLabel;
+ QLabel *m_pIconLabel;
+ QCheckBox *m_pEnableCheckBox;
+
+ QWidget *m_pWidget;
+ int m_iIndex;
+ bool m_fExpandCollapseIconVisible;
+ QIcon m_expandCollapseIcon;
+ UIToolPageButton *m_pTitleButton;
+ QString m_strTitle;
+ friend class UIToolBox;
+};
+
+
+/*********************************************************************************************************************************
+* UIToolPageButton implementation. *
+*********************************************************************************************************************************/
+
+UIToolPageButton::UIToolPageButton(QWidget *pParent /* = 0 */)
+ : QAbstractButton(pParent)
+{
+}
+
+void UIToolPageButton::paintEvent(QPaintEvent *pEvent)
+{
+ Q_UNUSED(pEvent);
+ if (!m_pixmap.isNull())
+ {
+ QPainter painter(this);
+ painter.drawPixmap(0 /* origin X */,
+ 0 /* origin Y */,
+ m_pixmap.width() / m_pixmap.devicePixelRatio() /* width */,
+ m_pixmap.height() / m_pixmap.devicePixelRatio() /* height */,
+ m_pixmap /* pixmap itself */);
+ }
+}
+
+void UIToolPageButton::setPixmap(const QPixmap &pixmap)
+{
+ m_pixmap = pixmap;
+ updateGeometry();
+ update();
+}
+
+QSize UIToolPageButton::sizeHint() const
+{
+ if (m_pixmap.isNull())
+ return QSize(0,0);
+ return m_pixmap.size() / m_pixmap.devicePixelRatio();
+}
+
+
+/*********************************************************************************************************************************
+* UIToolBoxPage implementation. *
+*********************************************************************************************************************************/
+
+UIToolBoxPage::UIToolBoxPage(bool fEnableCheckBoxEnabled /* = false */, QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QWidget>(pParent)
+ , m_fExpanded(false)
+ , m_pLayout(0)
+ , m_pTitleContainerWidget(0)
+ , m_pTitleLabel(0)
+ , m_pIconLabel(0)
+ , m_pEnableCheckBox(0)
+ , m_pWidget(0)
+ , m_iIndex(0)
+ , m_fExpandCollapseIconVisible(true)
+ , m_pTitleButton(0)
+{
+ prepare(fEnableCheckBoxEnabled);
+}
+
+void UIToolBoxPage::setTitle(const QString &strTitle)
+{
+ m_strTitle = strTitle;
+ if (!m_pTitleLabel)
+ return;
+ m_pTitleLabel->setText(strTitle);
+ retranslateUi();
+}
+
+void UIToolBoxPage::prepare(bool fEnableCheckBoxEnabled)
+{
+ m_expandCollapseIcon = UIIconPool::iconSet(":/expanding_collapsing_16px.png");
+
+ m_pLayout = new QVBoxLayout(this);
+ m_pLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pTitleContainerWidget = new QWidget;
+ m_pTitleContainerWidget->installEventFilter(this);
+ QHBoxLayout *pTitleLayout = new QHBoxLayout(m_pTitleContainerWidget);
+ pTitleLayout->setContentsMargins(qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
+ .4f * qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
+ qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
+ .4f * qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
+
+ m_pTitleButton = new UIToolPageButton;
+ pTitleLayout->addWidget(m_pTitleButton);
+ connect(m_pTitleButton, &QAbstractButton::clicked, this, &UIToolBoxPage::sigShowPageWidget);
+
+
+ if (fEnableCheckBoxEnabled)
+ {
+ m_pEnableCheckBox = new QCheckBox;
+ pTitleLayout->addWidget(m_pEnableCheckBox);
+ connect(m_pEnableCheckBox, &QCheckBox::stateChanged, this, &UIToolBoxPage::sltHandleEnableToggle);
+ }
+
+ m_pTitleLabel = new QLabel;
+ m_pTitleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ m_pTitleLabel->setBuddy(m_pTitleButton);
+
+ pTitleLayout->addWidget(m_pTitleLabel);
+ m_pIconLabel = new QLabel;
+ pTitleLayout->addWidget(m_pIconLabel, Qt::AlignLeft);
+ pTitleLayout->addStretch();
+ m_pTitleContainerWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ m_pLayout->addWidget(m_pTitleContainerWidget);
+
+ setExpandCollapseIcon();
+ retranslateUi();
+}
+
+void UIToolBoxPage::setWidget(QWidget *pWidget)
+{
+ if (!m_pLayout || !pWidget)
+ return;
+ m_pWidget = pWidget;
+ m_pLayout->addWidget(m_pWidget);
+
+ if (m_pEnableCheckBox)
+ m_pWidget->setEnabled(m_pEnableCheckBox->checkState() == Qt::Checked);
+
+ m_pWidget->hide();
+}
+
+void UIToolBoxPage::setTitleBackgroundColor(const QColor &color)
+{
+ if (!m_pTitleLabel)
+ return;
+ QPalette palette = m_pTitleContainerWidget->palette();
+ palette.setColor(QPalette::Window, color);
+ m_pTitleContainerWidget->setPalette(palette);
+ m_pTitleContainerWidget->setAutoFillBackground(true);
+}
+
+void UIToolBoxPage::setExpanded(bool fVisible)
+{
+ if (m_pWidget)
+ m_pWidget->setVisible(fVisible);
+ m_fExpanded = fVisible;
+ setExpandCollapseIcon();
+}
+
+int UIToolBoxPage::index() const
+{
+ return m_iIndex;
+}
+
+void UIToolBoxPage::setIndex(int iIndex)
+{
+ m_iIndex = iIndex;
+}
+
+int UIToolBoxPage::totalHeight() const
+{
+ return pageWidgetSize().height() + titleHeight();
+}
+
+void UIToolBoxPage::setTitleIcon(const QIcon &icon, const QString &strToolTip)
+{
+ if (!m_pIconLabel)
+ return;
+ if (icon.isNull())
+ {
+ m_pIconLabel->setPixmap(QPixmap());
+ return;
+ }
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ m_pIconLabel->setPixmap(icon.pixmap(windowHandle(), QSize(iMetric, iMetric)));
+ m_pIconLabel->setToolTip(strToolTip);
+}
+
+int UIToolBoxPage::titleHeight() const
+{
+ if (m_pTitleContainerWidget && m_pTitleContainerWidget->sizeHint().isValid())
+ return m_pTitleContainerWidget->sizeHint().height();
+ return 0;
+}
+
+QSize UIToolBoxPage::pageWidgetSize() const
+{
+ if (m_pWidget && m_pWidget->sizeHint().isValid())
+ return m_pWidget->sizeHint();
+ return QSize();
+}
+
+bool UIToolBoxPage::eventFilter(QObject *pWatched, QEvent *pEvent)
+{
+ if (pWatched == m_pTitleContainerWidget)
+ {
+ if (pEvent->type() == QEvent::MouseButtonPress)
+ emit sigShowPageWidget();
+ }
+ return QWidget::eventFilter(pWatched, pEvent);
+
+}
+
+void UIToolBoxPage::sltHandleEnableToggle(int iState)
+{
+ if (m_pWidget)
+ m_pWidget->setEnabled(iState == Qt::Checked);
+}
+
+void UIToolBoxPage::setExpandCollapseIcon()
+{
+ if (!m_fExpandCollapseIconVisible)
+ {
+ m_pTitleButton->setVisible(false);
+ return;
+ }
+ const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ QPixmap basePixmap = m_expandCollapseIcon.pixmap(windowHandle(), QSize(iMetric, iMetric));
+ if (!m_fExpanded)
+ m_pTitleButton->setPixmap(basePixmap);
+ else
+ {
+ QTransform transform;
+ transform.rotate(90);
+ QPixmap transformedPixmap = basePixmap.transformed(transform);
+ transformedPixmap.setDevicePixelRatio(basePixmap.devicePixelRatio());
+ m_pTitleButton->setPixmap(transformedPixmap);
+ }
+}
+
+void UIToolBoxPage::retranslateUi()
+{
+ if (m_pTitleButton)
+ m_pTitleButton->setToolTip(UIToolBox::tr("Expands the page \"%1\"").arg(m_strTitle.remove('&')));
+}
+
+
+/*********************************************************************************************************************************
+* UIToolBox implementation. *
+*********************************************************************************************************************************/
+
+UIToolBox::UIToolBox(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QFrame>(pParent)
+ , m_iCurrentPageIndex(-1)
+ , m_iPageCount(0)
+{
+ prepare();
+}
+
+bool UIToolBox::insertPage(int iIndex, QWidget *pWidget, const QString &strTitle, bool fAddEnableCheckBox /* = false */)
+{
+ if (m_pages.contains(iIndex))
+ return false;
+
+ /* Remove the stretch from the end of the layout: */
+ QLayoutItem *pItem = m_pMainLayout->takeAt(m_pMainLayout->count() - 1);
+ delete pItem;
+
+ ++m_iPageCount;
+ UIToolBoxPage *pNewPage = new UIToolBoxPage(fAddEnableCheckBox, 0);;
+
+ pNewPage->setWidget(pWidget);
+ pNewPage->setIndex(iIndex);
+ pNewPage->setTitle(strTitle);
+
+ const QPalette pal = QApplication::palette();
+ QColor tabBackgroundColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(130);
+ pNewPage->setTitleBackgroundColor(tabBackgroundColor);
+
+ m_pages[iIndex] = pNewPage;
+ m_pMainLayout->insertWidget(iIndex, pNewPage);
+
+ connect(pNewPage, &UIToolBoxPage::sigShowPageWidget,
+ this, &UIToolBox::sltHandleShowPageWidget);
+
+ /* Add stretch at the end: */
+ m_pMainLayout->addStretch(1);
+ return iIndex;
+}
+
+QSize UIToolBox::minimumSizeHint() const
+{
+
+ int iMaxPageHeight = 0;
+ int iTotalTitleHeight = 0;
+ int iWidth = 0;
+ foreach(UIToolBoxPage *pPage, m_pages)
+ {
+ QSize pageWidgetSize(pPage->pageWidgetSize());
+ iMaxPageHeight = qMax(iMaxPageHeight, pageWidgetSize.height());
+ iTotalTitleHeight += pPage->titleHeight();
+ iWidth = qMax(pageWidgetSize.width(), iWidth);
+ }
+ int iHeight = m_iPageCount * (qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) +
+ qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin)) +
+ iTotalTitleHeight +
+ iMaxPageHeight;
+ return QSize(iWidth, iHeight);
+}
+
+void UIToolBox::setPageEnabled(int iIndex, bool fEnabled)
+{
+ m_pages.value(iIndex)->setEnabled(fEnabled);
+}
+
+void UIToolBox::setPageTitle(int iIndex, const QString &strTitle)
+{
+ QMap<int, UIToolBoxPage*>::iterator iterator = m_pages.find(iIndex);
+ if (iterator == m_pages.end())
+ return;
+ iterator.value()->setTitle(strTitle);
+}
+
+void UIToolBox::setPageTitleIcon(int iIndex, const QIcon &icon, const QString &strIconToolTip /* = QString() */)
+{
+ QMap<int, UIToolBoxPage*>::iterator iterator = m_pages.find(iIndex);
+ if (iterator == m_pages.end())
+ return;
+ iterator.value()->setTitleIcon(icon, strIconToolTip);
+}
+
+void UIToolBox::setCurrentPage(int iIndex)
+{
+ m_iCurrentPageIndex = iIndex;
+ QMap<int, UIToolBoxPage*>::iterator iterator = m_pages.find(iIndex);
+ if (iterator == m_pages.end())
+ return;
+ foreach(UIToolBoxPage *pPage, m_pages)
+ pPage->setExpanded(false);
+
+ iterator.value()->setExpanded(true);
+}
+
+void UIToolBox::retranslateUi()
+{
+}
+
+void UIToolBox::prepare()
+{
+ m_pMainLayout = new QVBoxLayout(this);
+ m_pMainLayout->addStretch();
+
+ retranslateUi();
+}
+
+void UIToolBox::sltHandleShowPageWidget()
+{
+ UIToolBoxPage *pPage = qobject_cast<UIToolBoxPage*>(sender());
+ if (!pPage)
+ return;
+ setCurrentPage(pPage->index());
+ update();
+}
+
+#include "UIToolBox.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIToolBox.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIToolBox.h
new file mode 100644
index 00000000..f6cfbb48
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIToolBox.h
@@ -0,0 +1,84 @@
+/* $Id: UIToolBox.h $ */
+/** @file
+ * VBox Qt GUI - UIToolBox class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIToolBox_h
+#define FEQT_INCLUDED_SRC_widgets_UIToolBox_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QFrame>
+#include <QMap>
+
+/* Local includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QVBoxLayout;
+class QLabel;
+class UIToolBoxPage;
+
+/** A Qframe extension with similar API and functionality like QToolBox. I needed some for
+ * flexibility (like a second icon at the right hand side of the title etc.). */
+class SHARED_LIBRARY_STUFF UIToolBox : public QIWithRetranslateUI<QFrame>
+{
+
+ Q_OBJECT;
+
+signals:
+
+
+public:
+
+ UIToolBox(QWidget *pParent = 0);
+ bool insertPage(int iIndex, QWidget *pWidget, const QString &strTitle, bool fAddEnableCheckBox = false);
+ void setPageEnabled(int iIndex, bool fEnabled);
+ void setPageTitle(int iIndex, const QString &strTitle);
+ void setPageTitleIcon(int iIndex, const QIcon &icon, const QString &strIconToolTip = QString());
+ void setCurrentPage(int iIndex);
+ virtual QSize minimumSizeHint() const RT_OVERRIDE;
+
+protected:
+
+ virtual void retranslateUi() /* override final */;
+
+private slots:
+
+ void sltHandleShowPageWidget();
+
+private:
+
+ void prepare();
+
+ QVBoxLayout *m_pMainLayout;
+ QMap<int, UIToolBoxPage*> m_pages;
+ int m_iCurrentPageIndex;
+ int m_iPageCount;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIToolBox_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIWarningPane.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/UIWarningPane.cpp
new file mode 100644
index 00000000..bcc46b5a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIWarningPane.cpp
@@ -0,0 +1,204 @@
+/* $Id: UIWarningPane.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWarningPane class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QEvent>
+#include <QLabel>
+#include <QTimer>
+
+/* GUI includes: */
+#include "QIWidgetValidator.h"
+#include "UIWarningPane.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+UIWarningPane::UIWarningPane(QWidget *pParent)
+ : QWidget(pParent)
+ , m_pIconLayout(0)
+ , m_pTextLabel(0)
+ , m_pHoverTimer(0)
+ , m_iHoveredIconLabelPosition(-1)
+{
+ /* Prepare: */
+ prepare();
+}
+
+void UIWarningPane::setWarningLabel(const QString &strWarningLabel)
+{
+ /* Assign passed text directly to warning-label: */
+ m_pTextLabel->setText(strWarningLabel);
+}
+
+void UIWarningPane::registerValidator(UIPageValidator *pValidator)
+{
+ /* Make sure validator exists: */
+ AssertPtrReturnVoid(pValidator);
+
+ /* Make sure validator is not registered yet: */
+ if (m_validators.contains(pValidator))
+ {
+ AssertMsgFailed(("Validator is registered already!\n"));
+ return;
+ }
+
+ /* Register validator: */
+ m_validators << pValidator;
+
+ /* Create icon-label for newly registered validator: */
+ QLabel *pIconLabel = new QLabel;
+ {
+ /* Configure icon-label: */
+ pIconLabel->setMouseTracking(true);
+ pIconLabel->installEventFilter(this);
+ pIconLabel->setPixmap(pValidator->warningPixmap());
+ connect(pValidator, &UIPageValidator::sigShowWarningIcon, pIconLabel, &QLabel::show);
+ connect(pValidator, &UIPageValidator::sigHideWarningIcon, pIconLabel, &QLabel::hide);
+
+ /* Add icon-label into list: */
+ m_icons << pIconLabel;
+ /* Add icon-label into layout: */
+ m_pIconLayout->addWidget(pIconLabel);
+ }
+
+ /* Mark icon as 'unhovered': */
+ m_hovered << false;
+}
+
+bool UIWarningPane::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Depending on event-type: */
+ switch (pEvent->type())
+ {
+ /* One of icons hovered: */
+ case QEvent::MouseMove:
+ {
+ /* Cast object to label: */
+ if (QLabel *pIconLabel = qobject_cast<QLabel*>(pObject))
+ {
+ /* Search for the corresponding icon: */
+ if (m_icons.contains(pIconLabel))
+ {
+ /* Mark icon-label hovered if not yet: */
+ int iIconLabelPosition = m_icons.indexOf(pIconLabel);
+ if (!m_hovered[iIconLabelPosition])
+ {
+ m_hovered[iIconLabelPosition] = true;
+ m_iHoveredIconLabelPosition = iIconLabelPosition;
+ m_pHoverTimer->start();
+ }
+ }
+ }
+ break;
+ }
+
+ /* One of icons unhovered: */
+ case QEvent::Leave:
+ {
+ /* Cast object to label: */
+ if (QLabel *pIconLabel = qobject_cast<QLabel*>(pObject))
+ {
+ /* Search for the corresponding icon: */
+ if (m_icons.contains(pIconLabel))
+ {
+ /* Mark icon-label unhovered if not yet: */
+ int iIconLabelPosition = m_icons.indexOf(pIconLabel);
+ if (m_hovered[iIconLabelPosition])
+ {
+ m_hovered[iIconLabelPosition] = false;
+ if (m_pHoverTimer->isActive())
+ {
+ m_pHoverTimer->stop();
+ m_iHoveredIconLabelPosition = -1;
+ }
+ else
+ emit sigHoverLeave(m_validators[iIconLabelPosition]);
+ }
+ }
+ }
+ break;
+ }
+
+ /* Default case: */
+ default:
+ break;
+ }
+
+ /* Call to base-class: */
+ return QWidget::eventFilter(pObject, pEvent);
+}
+
+void UIWarningPane::sltHandleHoverTimer()
+{
+ /* Notify listeners about hovering: */
+ if (m_iHoveredIconLabelPosition >= 0 && m_iHoveredIconLabelPosition < m_validators.size())
+ emit sigHoverEnter(m_validators[m_iHoveredIconLabelPosition]);
+}
+
+void UIWarningPane::prepare()
+{
+ /* Create main-layout: */
+ QHBoxLayout *pMainLayout = new QHBoxLayout(this);
+ {
+ /* Configure layout: */
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Add left stretch: */
+ pMainLayout->addStretch();
+
+ /* Create text-label: */
+ m_pTextLabel = new QLabel;
+ {
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pTextLabel);
+ }
+
+ /* Create layout: */
+ m_pIconLayout = new QHBoxLayout;
+ {
+ /* Configure layout: */
+ m_pIconLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Add into layout: */
+ pMainLayout->addLayout(m_pIconLayout);
+ }
+
+ /* Create hover-timer: */
+ m_pHoverTimer = new QTimer(this);
+ {
+ /* Configure timer: */
+ m_pHoverTimer->setInterval(200);
+ m_pHoverTimer->setSingleShot(true);
+ connect(m_pHoverTimer, &QTimer::timeout, this, &UIWarningPane::sltHandleHoverTimer);
+ }
+
+ /* Add right stretch: */
+ pMainLayout->addStretch();
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/UIWarningPane.h b/src/VBox/Frontends/VirtualBox/src/widgets/UIWarningPane.h
new file mode 100644
index 00000000..bdd86d2b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/UIWarningPane.h
@@ -0,0 +1,108 @@
+/* $Id: UIWarningPane.h $ */
+/** @file
+ * VBox Qt GUI - UIWarningPane class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_UIWarningPane_h
+#define FEQT_INCLUDED_SRC_widgets_UIWarningPane_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QWidget>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QHBoxLayout;
+class QEvent;
+class QLabel;
+class QObject;
+class QString;
+class QTimer;
+class QWidget;
+class UIPageValidator;
+
+/** QWidget subclass used a settings dialog warning-pane. */
+class SHARED_LIBRARY_STUFF UIWarningPane : public QWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about hover enter event.
+ * @param pValidator Brings the validator reference. */
+ void sigHoverEnter(UIPageValidator *pValidator);
+ /** Notifies about hover leave event.
+ * @param pValidator Brings the validator reference. */
+ void sigHoverLeave(UIPageValidator *pValidator);
+
+public:
+
+ /** Constructs warning-pane passing @a pParent to the base-class. */
+ UIWarningPane(QWidget *pParent = 0);
+
+ /** Defines current @a strWarningLabel text. */
+ void setWarningLabel(const QString &strWarningLabel);
+
+ /** Registers corresponding @a pValidator. */
+ void registerValidator(UIPageValidator *pValidator);
+
+protected:
+
+ /** Preprocesses Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles hover timer timeout. */
+ void sltHandleHoverTimer();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Holds the icon layout instance. */
+ QHBoxLayout *m_pIconLayout;
+ /** Holds the text label instance. */
+ QLabel *m_pTextLabel;
+
+ /** Holds the page validators list. */
+ QList<UIPageValidator*> m_validators;
+ /** Holds the page icons list. */
+ QList<QLabel*> m_icons;
+ /** Holds the icons hovered-states list. */
+ QList<bool> m_hovered;
+
+ /** Holds the hover timer instance. */
+ QTimer *m_pHoverTimer;
+ /** Holds the hovered icon-label position. */
+ int m_iHoveredIconLabelPosition;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_UIWarningPane_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsButton.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsButton.cpp
new file mode 100644
index 00000000..5d183420
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsButton.cpp
@@ -0,0 +1,214 @@
+/* $Id: UIGraphicsButton.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsButton class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QGraphicsScene>
+#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsView>
+#include <QPainter>
+#include <QStyle>
+#include <QTimerEvent>
+
+/* GUI includes: */
+#include "UIGraphicsButton.h"
+
+
+UIGraphicsButton::UIGraphicsButton(QIGraphicsWidget *pParent, const QIcon &icon)
+ : QIGraphicsWidget(pParent)
+ , m_icon(icon)
+ , m_enmClickPolicy(ClickPolicy_OnRelease)
+ , m_iDelayId(0)
+ , m_iRepeatId(0)
+ , m_dIconScaleIndex(0)
+{
+ refresh();
+}
+
+void UIGraphicsButton::setIconScaleIndex(double dIndex)
+{
+ if (dIndex >= 0)
+ m_dIconScaleIndex = dIndex;
+}
+
+double UIGraphicsButton::iconScaleIndex() const
+{
+ return m_dIconScaleIndex;
+}
+
+void UIGraphicsButton::setClickPolicy(ClickPolicy enmPolicy)
+{
+ m_enmClickPolicy = enmPolicy;
+}
+
+UIGraphicsButton::ClickPolicy UIGraphicsButton::clickPolicy() const
+{
+ return m_enmClickPolicy;
+}
+
+QVariant UIGraphicsButton::data(int iKey) const
+{
+ switch (iKey)
+ {
+ case GraphicsButton_Margin:
+ return 0;
+ case GraphicsButton_IconSize:
+ {
+ int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ if (m_dIconScaleIndex > 0)
+ iMetric *= m_dIconScaleIndex;
+ return QSize(iMetric, iMetric);
+ }
+ case GraphicsButton_Icon:
+ return m_icon;
+ default:
+ break;
+ }
+ return QVariant();
+}
+
+QSizeF UIGraphicsButton::sizeHint(Qt::SizeHint enmType, const QSizeF &constraint /* = QSizeF() */) const
+{
+ /* For minimum size-hint: */
+ if (enmType == Qt::MinimumSize)
+ {
+ /* Prepare variables: */
+ const int iMargin = data(GraphicsButton_Margin).toInt();
+ const QSize iconSize = data(GraphicsButton_IconSize).toSize();
+ /* Perform calculations: */
+ int iWidth = 2 * iMargin + iconSize.width();
+ int iHeight = 2 * iMargin + iconSize.height();
+ return QSize(iWidth, iHeight);
+ }
+
+ /* Call to base-class: */
+ return QIGraphicsWidget::sizeHint(enmType, constraint);
+}
+
+void UIGraphicsButton::paint(QPainter *pPainter, const QStyleOptionGraphicsItem* /* pOption */, QWidget* /* pWidget = 0 */)
+{
+ /* Prepare variables: */
+ const int iMargin = data(GraphicsButton_Margin).toInt();
+ const QIcon icon = data(GraphicsButton_Icon).value<QIcon>();
+ const QSize expectedIconSize = data(GraphicsButton_IconSize).toSize();
+
+ /* Determine which QWindow this QGraphicsWidget belongs to.
+ * This is required for proper HiDPI-aware pixmap calculations. */
+ QWindow *pWindow = 0;
+ if ( scene()
+ && !scene()->views().isEmpty()
+ && scene()->views().first()
+ && scene()->views().first()->window())
+ pWindow = scene()->views().first()->window()->windowHandle();
+
+ /* Acquire pixmap, adjust it to be in center of button if necessary: */
+ const QPixmap pixmap = icon.pixmap(pWindow, expectedIconSize);
+ const QSize actualIconSize = pixmap.size() / pixmap.devicePixelRatio();
+ QPoint position = QPoint(iMargin, iMargin);
+ if (actualIconSize != expectedIconSize)
+ {
+ const int iDx = (expectedIconSize.width() - actualIconSize.width()) / 2;
+ const int iDy = (expectedIconSize.height() - actualIconSize.height()) / 2;
+ position += QPoint(iDx, iDy);
+ }
+
+ /* Draw the pixmap finally: */
+ pPainter->drawPixmap(position, pixmap);
+}
+
+void UIGraphicsButton::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::mousePressEvent(pEvent);
+
+ /* Accepting this event allows to get release-event: */
+ pEvent->accept();
+
+ /* For click-on-press policy: */
+ if (m_enmClickPolicy == ClickPolicy_OnPress)
+ {
+ /* Notify listeners about button click: */
+ emit sigButtonClicked();
+ /* Start delay timer: */
+ m_iDelayId = startTimer(500);
+ }
+}
+
+void UIGraphicsButton::mouseReleaseEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::mouseReleaseEvent(pEvent);
+
+ /* Depending on click policy: */
+ switch (m_enmClickPolicy)
+ {
+ /* For click-on-release policy: */
+ case ClickPolicy_OnRelease:
+ {
+ /* Notify listeners about button click: */
+ emit sigButtonClicked();
+ break;
+ }
+ /* For click-on-press policy: */
+ case ClickPolicy_OnPress:
+ {
+ /* We should stop all timers: */
+ killTimer(m_iDelayId);
+ killTimer(m_iRepeatId);
+ m_iDelayId = 0;
+ m_iRepeatId = 0;
+ break;
+ }
+ }
+}
+
+void UIGraphicsButton::timerEvent(QTimerEvent *pEvent)
+{
+ /* For click-on-press policy: */
+ if (m_enmClickPolicy == ClickPolicy_OnPress)
+ {
+ /* We should auto-repeat button click: */
+ emit sigButtonClicked();
+
+ /* For delay timer: */
+ if (pEvent->timerId() == m_iDelayId)
+ {
+ /* We should stop it and start repeat timer: */
+ killTimer(m_iDelayId);
+ m_iDelayId = 0;
+ m_iRepeatId = startTimer(90);
+ }
+ }
+}
+
+void UIGraphicsButton::refresh()
+{
+ /* Refresh geometry: */
+ updateGeometry();
+ /* Resize to minimum size-hint: */
+ resize(minimumSizeHint());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsButton.h b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsButton.h
new file mode 100644
index 00000000..9b33d0a3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsButton.h
@@ -0,0 +1,122 @@
+/* $Id: UIGraphicsButton.h $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsButton class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsButton_h
+#define FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsButton_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+
+/* GUI includes: */
+#include "QIGraphicsWidget.h"
+
+/* Forward declarations: */
+class QGraphicsSceneMouseEvent;
+class QGraphicsSceneHoverEvent;
+class QPropertyAnimation;
+
+/** QIGraphicsWidget subclass providing GUI with graphics-button representation. */
+class UIGraphicsButton : public QIGraphicsWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about button was clicked. */
+ void sigButtonClicked();
+
+public:
+
+ /** Click policy. */
+ enum ClickPolicy { ClickPolicy_OnRelease, ClickPolicy_OnPress };
+
+ /** Constructs graphics button passing @a pParent to the base-class.
+ * @param icon Brings the button icon. */
+ UIGraphicsButton(QIGraphicsWidget *pParent, const QIcon &icon);
+
+ /** Defines icon scale @a dIndex. */
+ void setIconScaleIndex(double dIndex);
+ /** Returns icon scale index. */
+ double iconScaleIndex() const;
+
+ /** Defines click @a enmPolicy. */
+ void setClickPolicy(ClickPolicy enmPolicy);
+ /** Returns click policy. */
+ ClickPolicy clickPolicy() const;
+
+protected:
+
+ /** Data enumerator. */
+ enum GraphicsButton
+ {
+ GraphicsButton_Margin,
+ GraphicsButton_IconSize,
+ GraphicsButton_Icon
+ };
+
+ /** Returns data stored for certain @a iKey: */
+ virtual QVariant data(int iKey) const;
+
+ /** Returns size-hint of certain @a enmType, restricted by passed @a constraint. */
+ virtual QSizeF sizeHint(Qt::SizeHint enmType, const QSizeF &constraint = QSizeF()) const RT_OVERRIDE;
+
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOption, QWidget *pWidget = 0) RT_OVERRIDE;
+
+ /** Handles mouse-press @a pEvent. */
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-release @a pEvent. */
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles timer @a pEvent. */
+ virtual void timerEvent(QTimerEvent *pEvent) RT_OVERRIDE;
+
+ /** Updates button.
+ * @todo rename to prepare() */
+ virtual void refresh();
+
+private:
+
+ /** Holds the button icon. */
+ QIcon m_icon;
+
+ /** Holds the click policy. */
+ ClickPolicy m_enmClickPolicy;
+
+ /** Holds the delay timer ID. */
+ int m_iDelayId;
+ /** Holds the repeat timer ID. */
+ int m_iRepeatId;
+
+ /** Holds the icon scale index. */
+ double m_dIconScaleIndex;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsButton_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsRotatorButton.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsRotatorButton.cpp
new file mode 100644
index 00000000..3e049bcb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsRotatorButton.cpp
@@ -0,0 +1,241 @@
+/* $Id: UIGraphicsRotatorButton.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsRotatorButton class definition.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStateMachine>
+#include <QPropertyAnimation>
+#include <QSignalTransition>
+#include <QMouseEventTransition>
+
+/* GUI includes: */
+#include "UIGraphicsRotatorButton.h"
+#include "UIIconPool.h"
+
+
+UIGraphicsRotatorButton::UIGraphicsRotatorButton(QIGraphicsWidget *pParent,
+ const QString &strPropertyName,
+ bool fToggled,
+ bool fReflected /* = false */,
+ int iAnimationDuration /* = 300 */)
+ : UIGraphicsButton(pParent, UIIconPool::iconSet(":/expanding_collapsing_16px.png"))
+ , m_fReflected(fReflected)
+ , m_state(fToggled ? UIGraphicsRotatorButtonState_Rotated : UIGraphicsRotatorButtonState_Default)
+ , m_pAnimationMachine(0)
+ , m_iAnimationDuration(iAnimationDuration)
+ , m_pForwardButtonAnimation(0)
+ , m_pBackwardButtonAnimation(0)
+ , m_pForwardSubordinateAnimation(0)
+ , m_pBackwardSubordinateAnimation(0)
+{
+ /* Configure: */
+ setAutoHandleButtonClick(true);
+
+ /* Create state machine: */
+ m_pAnimationMachine = new QStateMachine(this);
+ /* Create 'default' state: */
+ QState *pStateDefault = new QState(m_pAnimationMachine);
+ pStateDefault->assignProperty(this, "state", QVariant::fromValue(UIGraphicsRotatorButtonState_Default));
+ pStateDefault->assignProperty(this, "rotation", m_fReflected ? 180 : 0);
+ /* Create 'animating' state: */
+ QState *pStateAnimating = new QState(m_pAnimationMachine);
+ pStateAnimating->assignProperty(this, "state", QVariant::fromValue(UIGraphicsRotatorButtonState_Animating));
+ /* Create 'rotated' state: */
+ QState *pStateRotated = new QState(m_pAnimationMachine);
+ pStateRotated->assignProperty(this, "state", QVariant::fromValue(UIGraphicsRotatorButtonState_Rotated));
+ pStateRotated->assignProperty(this, "rotation", 90);
+
+ /* Forward button animation: */
+ m_pForwardButtonAnimation = new QPropertyAnimation(this, "rotation", this);
+ m_pForwardButtonAnimation->setDuration(m_iAnimationDuration);
+ m_pForwardButtonAnimation->setStartValue(m_fReflected ? 180 : 0);
+ m_pForwardButtonAnimation->setEndValue(90);
+ /* Backward button animation: */
+ m_pBackwardButtonAnimation = new QPropertyAnimation(this, "rotation", this);
+ m_pBackwardButtonAnimation->setDuration(m_iAnimationDuration);
+ m_pBackwardButtonAnimation->setStartValue(90);
+ m_pBackwardButtonAnimation->setEndValue(m_fReflected ? 180 : 0);
+
+ /* Forward subordinate animation: */
+ m_pForwardSubordinateAnimation = new QPropertyAnimation(pParent, strPropertyName.toLatin1(), this);
+ m_pForwardSubordinateAnimation->setDuration(m_iAnimationDuration);
+ m_pForwardSubordinateAnimation->setEasingCurve(QEasingCurve::InCubic);
+ /* Backward subordinate animation: */
+ m_pBackwardSubordinateAnimation = new QPropertyAnimation(pParent, strPropertyName.toLatin1(), this);
+ m_pBackwardSubordinateAnimation->setDuration(m_iAnimationDuration);
+ m_pBackwardSubordinateAnimation->setEasingCurve(QEasingCurve::InCubic);
+
+ /* Default => Animating: */
+ QSignalTransition *pDefaultToAnimating = pStateDefault->addTransition(this, SIGNAL(sigToAnimating()), pStateAnimating);
+ pDefaultToAnimating->addAnimation(m_pForwardButtonAnimation);
+ pDefaultToAnimating->addAnimation(m_pForwardSubordinateAnimation);
+ /* Animating => Rotated: */
+ connect(m_pForwardButtonAnimation, SIGNAL(finished()), this, SIGNAL(sigToRotated()), Qt::QueuedConnection);
+ pStateAnimating->addTransition(this, SIGNAL(sigToRotated()), pStateRotated);
+
+ /* Rotated => Animating: */
+ QSignalTransition *pRotatedToAnimating = pStateRotated->addTransition(this, SIGNAL(sigToAnimating()), pStateAnimating);
+ pRotatedToAnimating->addAnimation(m_pBackwardButtonAnimation);
+ pRotatedToAnimating->addAnimation(m_pBackwardSubordinateAnimation);
+ /* Animating => Default: */
+ connect(m_pBackwardButtonAnimation, SIGNAL(finished()), this, SIGNAL(sigToDefault()), Qt::QueuedConnection);
+ pStateAnimating->addTransition(this, SIGNAL(sigToDefault()), pStateDefault);
+
+ /* Default => Rotated: */
+ pStateDefault->addTransition(this, SIGNAL(sigToRotated()), pStateRotated);
+
+ /* Rotated => Default: */
+ pStateRotated->addTransition(this, SIGNAL(sigToDefault()), pStateDefault);
+
+ /* Initial state is 'default': */
+ m_pAnimationMachine->setInitialState(!fToggled ? pStateDefault : pStateRotated);
+ /* Start state-machine: */
+ m_pAnimationMachine->start();
+
+ /* Refresh: */
+ refresh();
+}
+
+void UIGraphicsRotatorButton::setAutoHandleButtonClick(bool fEnabled)
+{
+ /* Disconnect button-click signal: */
+ disconnect(this, &UIGraphicsRotatorButton::sigButtonClicked, this, &UIGraphicsRotatorButton::sltButtonClicked);
+ if (fEnabled)
+ {
+ /* Connect button-click signal: */
+ connect(this, &UIGraphicsRotatorButton::sigButtonClicked, this, &UIGraphicsRotatorButton::sltButtonClicked);
+ }
+}
+
+void UIGraphicsRotatorButton::setToggled(bool fToggled, bool fAnimated /* = true */)
+{
+ /* Not during animation: */
+ if (isAnimationRunning())
+ return;
+
+ /* Make sure something has changed: */
+ switch (state())
+ {
+ case UIGraphicsRotatorButtonState_Default:
+ {
+ if (!fToggled)
+ return;
+ break;
+ }
+ case UIGraphicsRotatorButtonState_Rotated:
+ {
+ if (fToggled)
+ return;
+ break;
+ }
+ default: break;
+ }
+
+ /* Should be animated? */
+ if (fAnimated)
+ {
+ /* Rotation start: */
+ emit sigRotationStart();
+ emit sigToAnimating();
+ }
+ else
+ {
+ if (fToggled)
+ emit sigToRotated();
+ else
+ emit sigToDefault();
+ }
+}
+
+void UIGraphicsRotatorButton::setAnimationRange(int iStart, int iEnd)
+{
+ m_pForwardSubordinateAnimation->setStartValue(iStart);
+ m_pForwardSubordinateAnimation->setEndValue(iEnd);
+ m_pBackwardSubordinateAnimation->setStartValue(iEnd);
+ m_pBackwardSubordinateAnimation->setEndValue(iStart);
+}
+
+bool UIGraphicsRotatorButton::isAnimationRunning() const
+{
+ return m_pForwardSubordinateAnimation->state() == QAbstractAnimation::Running ||
+ m_pBackwardSubordinateAnimation->state() == QAbstractAnimation::Running;
+}
+
+void UIGraphicsRotatorButton::sltButtonClicked()
+{
+ /* Toggle state: */
+ switch (state())
+ {
+ case UIGraphicsRotatorButtonState_Default: setToggled(true); break;
+ case UIGraphicsRotatorButtonState_Rotated: setToggled(false); break;
+ default: break;
+ }
+}
+
+void UIGraphicsRotatorButton::refresh()
+{
+ /* Update rotation center: */
+ QSizeF sh = minimumSizeHint();
+ setTransformOriginPoint(sh.width() / 2, sh.height() / 2);
+ /* Update rotation state: */
+ updateRotationState();
+ /* Call to base-class: */
+ UIGraphicsButton::refresh();
+}
+
+void UIGraphicsRotatorButton::updateRotationState()
+{
+ switch (state())
+ {
+ case UIGraphicsRotatorButtonState_Default: setRotation(m_fReflected ? 180 : 0); break;
+ case UIGraphicsRotatorButtonState_Rotated: setRotation(90); break;
+ default: break;
+ }
+}
+
+UIGraphicsRotatorButtonState UIGraphicsRotatorButton::state() const
+{
+ return m_state;
+}
+
+void UIGraphicsRotatorButton::setState(UIGraphicsRotatorButtonState state)
+{
+ m_state = state;
+ switch (m_state)
+ {
+ case UIGraphicsRotatorButtonState_Default:
+ {
+ emit sigRotationFinish(false);
+ break;
+ }
+ case UIGraphicsRotatorButtonState_Rotated:
+ {
+ emit sigRotationFinish(true);
+ break;
+ }
+ default: break;
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsRotatorButton.h b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsRotatorButton.h
new file mode 100644
index 00000000..6a50c3a0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsRotatorButton.h
@@ -0,0 +1,117 @@
+/* $Id: UIGraphicsRotatorButton.h $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsRotatorButton class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsRotatorButton_h
+#define FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsRotatorButton_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIGraphicsButton.h"
+
+/* Forward declarations: */
+class QStateMachine;
+class QPropertyAnimation;
+
+/* Rotator graphics-button states: */
+enum UIGraphicsRotatorButtonState
+{
+ UIGraphicsRotatorButtonState_Default,
+ UIGraphicsRotatorButtonState_Animating,
+ UIGraphicsRotatorButtonState_Rotated
+};
+Q_DECLARE_METATYPE(UIGraphicsRotatorButtonState);
+
+/* Rotator graphics-button representation: */
+class UIGraphicsRotatorButton : public UIGraphicsButton
+{
+ Q_OBJECT;
+ Q_PROPERTY(UIGraphicsRotatorButtonState state READ state WRITE setState);
+
+signals:
+
+ /* Rotation internal stuff: */
+ void sigToAnimating();
+ void sigToRotated();
+ void sigToDefault();
+
+ /* Rotation external stuff: */
+ void sigRotationStart();
+ void sigRotationFinish(bool fRotated);
+
+public:
+
+ /* Constructor: */
+ UIGraphicsRotatorButton(QIGraphicsWidget *pParent,
+ const QString &strPropertyName,
+ bool fToggled,
+ bool fReflected = false,
+ int iAnimationDuration = 300);
+
+ /* API: Button-click stuff: */
+ void setAutoHandleButtonClick(bool fEnabled);
+
+ /* API: Toggle stuff: */
+ void setToggled(bool fToggled, bool fAnimated = true);
+
+ /* API: Subordinate animation stuff: */
+ void setAnimationRange(int iStart, int iEnd);
+ bool isAnimationRunning() const;
+
+protected slots:
+
+ /* Handler: Button-click stuff: */
+ void sltButtonClicked();
+
+protected:
+
+ /* Helpers: Update stuff: */
+ void refresh();
+
+private:
+
+ /* Helpers: Rotate stuff: */
+ void updateRotationState();
+
+ /* Property stiff: */
+ UIGraphicsRotatorButtonState state() const;
+ void setState(UIGraphicsRotatorButtonState state);
+
+ /* Variables: */
+ bool m_fReflected;
+ UIGraphicsRotatorButtonState m_state;
+ QStateMachine *m_pAnimationMachine;
+ int m_iAnimationDuration;
+ QPropertyAnimation *m_pForwardButtonAnimation;
+ QPropertyAnimation *m_pBackwardButtonAnimation;
+ QPropertyAnimation *m_pForwardSubordinateAnimation;
+ QPropertyAnimation *m_pBackwardSubordinateAnimation;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsRotatorButton_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollArea.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollArea.cpp
new file mode 100644
index 00000000..a2b71e72
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollArea.cpp
@@ -0,0 +1,354 @@
+/* $Id: UIGraphicsScrollArea.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsScrollArea class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGraphicsScene>
+#include <QGraphicsView>
+
+/* GUI includes: */
+#include "UIGraphicsScrollArea.h"
+#include "UIGraphicsScrollBar.h"
+#ifdef VBOX_WS_MAC
+# include "VBoxUtils.h"
+#endif
+
+
+UIGraphicsScrollArea::UIGraphicsScrollArea(Qt::Orientation enmOrientation, QGraphicsScene *pScene /* = 0 */)
+ : m_enmOrientation(enmOrientation)
+ , m_fAutoHideMode(true)
+ , m_pScrollBar(0)
+ , m_pViewport(0)
+{
+ pScene->addItem(this);
+ prepare();
+}
+
+UIGraphicsScrollArea::UIGraphicsScrollArea(Qt::Orientation enmOrientation, QIGraphicsWidget *pParent /* = 0 */)
+ : QIGraphicsWidget(pParent)
+ , m_enmOrientation(enmOrientation)
+ , m_fAutoHideMode(true)
+ , m_pScrollBar(0)
+ , m_pViewport(0)
+{
+ prepare();
+}
+
+QSizeF UIGraphicsScrollArea::minimumSizeHint() const
+{
+ /* Minimum size-hint of scroll-bar by default: */
+ QSizeF msh = m_pScrollBar->minimumSizeHint();
+ if (m_pViewport)
+ {
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal:
+ {
+ /* Expand it with viewport height: */
+ const int iWidgetHeight = m_pViewport->size().height();
+ if (m_fAutoHideMode)
+ {
+ if (msh.height() < iWidgetHeight)
+ msh.setHeight(iWidgetHeight);
+ }
+ else
+ msh.setHeight(msh.height() + iWidgetHeight);
+ break;
+ }
+ case Qt::Vertical:
+ {
+ /* Expand it with viewport width: */
+ const int iWidgetWidth = m_pViewport->size().width();
+ if (m_fAutoHideMode)
+ {
+ if (msh.width() < iWidgetWidth)
+ msh.setWidth(iWidgetWidth);
+ }
+ else
+ msh.setWidth(msh.width() + iWidgetWidth);
+ break;
+ }
+ }
+ }
+ return msh;
+}
+
+void UIGraphicsScrollArea::setViewport(QIGraphicsWidget *pViewport)
+{
+ /* Forget previous widget: */
+ if (m_pViewport)
+ {
+ m_pViewport->removeEventFilter(this);
+ m_pViewport->setParentItem(0);
+ m_pViewport = 0;
+ }
+
+ /* Remember passed widget: */
+ if (pViewport)
+ {
+ m_pViewport = pViewport;
+ m_pViewport->setParentItem(this);
+ m_pViewport->installEventFilter(this);
+ }
+
+ /* Layout widgets: */
+ layoutWidgets();
+}
+
+QIGraphicsWidget *UIGraphicsScrollArea::viewport() const
+{
+ return m_pViewport;
+}
+
+int UIGraphicsScrollArea::scrollingValue() const
+{
+ return m_pScrollBar->value();
+}
+
+void UIGraphicsScrollArea::setScrollingValue(int iValue)
+{
+ iValue = qMax(iValue, 0);
+ iValue = qMin(iValue, m_pScrollBar->maximum());
+ m_pScrollBar->setValue(iValue);
+}
+
+void UIGraphicsScrollArea::scrollBy(int iDelta)
+{
+ m_pScrollBar->setValue(m_pScrollBar->value() + iDelta);
+}
+
+void UIGraphicsScrollArea::makeSureRectIsVisible(const QRectF &rect)
+{
+ /* Make sure rect size is bound by the scroll-area size: */
+ QRectF actualRect = rect;
+ QSizeF actualRectSize = actualRect.size();
+ actualRectSize = actualRectSize.boundedTo(size());
+ actualRect.setSize(actualRectSize);
+
+ /* Acquire scroll-area scene position: */
+ const QPointF saPos = mapToScene(QPointF(0, 0));
+
+ switch (m_enmOrientation)
+ {
+ /* Scroll viewport horizontally: */
+ case Qt::Horizontal:
+ {
+ /* If rectangle is at least partially right of visible area: */
+ if (actualRect.x() + actualRect.width() - saPos.x() > size().width())
+ m_pScrollBar->setValue(m_pScrollBar->value() + actualRect.x() + actualRect.width() - saPos.x() - size().width());
+ /* If rectangle is at least partially left of visible area: */
+ else if (actualRect.x() - saPos.x() < 0)
+ m_pScrollBar->setValue(m_pScrollBar->value() + actualRect.x() - saPos.x());
+ break;
+ }
+ /* Scroll viewport vertically: */
+ case Qt::Vertical:
+ {
+ /* If rectangle is at least partially under visible area: */
+ if (actualRect.y() + actualRect.height() - saPos.y() > size().height())
+ m_pScrollBar->setValue(m_pScrollBar->value() + actualRect.y() + actualRect.height() - saPos.y() - size().height());
+ /* If rectangle is at least partially above visible area: */
+ else if (actualRect.y() - saPos.y() < 0)
+ m_pScrollBar->setValue(m_pScrollBar->value() + actualRect.y() - saPos.y());
+ break;
+ }
+ }
+}
+
+bool UIGraphicsScrollArea::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Handle layout requests for m_pViewport if set: */
+ if ( m_pViewport
+ && pObject == m_pViewport
+ && pEvent->type() == QEvent::LayoutRequest)
+ layoutWidgets();
+
+ /* Handle wheel events for first scene view if set: */
+ if ( scene()
+ && !scene()->views().isEmpty()
+ && pObject == scene()->views().first()
+ && pEvent->type() == QEvent::Wheel)
+ {
+ QWheelEvent *pWheelEvent = static_cast<QWheelEvent*>(pEvent);
+ const QPoint angleDelta = pWheelEvent->angleDelta();
+#ifdef VBOX_WS_MAC
+ const QPoint pixelDelta = pWheelEvent->pixelDelta();
+#endif
+ switch (m_enmOrientation)
+ {
+ /* Scroll viewport horizontally: */
+ case Qt::Horizontal:
+ {
+ if (angleDelta.x() > 0)
+#ifdef VBOX_WS_MAC
+ m_pScrollBar->setValue(m_pScrollBar->value() - pixelDelta.y());
+#else
+ m_pScrollBar->setValue(m_pScrollBar->value() - m_pScrollBar->step());
+#endif
+ else if (angleDelta.x() < 0)
+#ifdef VBOX_WS_MAC
+ m_pScrollBar->setValue(m_pScrollBar->value() - pixelDelta.y());
+#else
+ m_pScrollBar->setValue(m_pScrollBar->value() + m_pScrollBar->step());
+#endif
+ break;
+ }
+ /* Scroll viewport vertically: */
+ case Qt::Vertical:
+ {
+ if (angleDelta.y() > 0)
+#ifdef VBOX_WS_MAC
+ m_pScrollBar->setValue(m_pScrollBar->value() - pixelDelta.y());
+#else
+ m_pScrollBar->setValue(m_pScrollBar->value() - m_pScrollBar->step());
+#endif
+ else if (angleDelta.y() < 0)
+#ifdef VBOX_WS_MAC
+ m_pScrollBar->setValue(m_pScrollBar->value() - pixelDelta.y());
+#else
+ m_pScrollBar->setValue(m_pScrollBar->value() + m_pScrollBar->step());
+#endif
+ break;
+ }
+ }
+ }
+
+ /* Call to base-class: */
+ return QIGraphicsWidget::eventFilter(pObject, pEvent);
+}
+
+void UIGraphicsScrollArea::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::resizeEvent(pEvent);
+
+ /* Layout widgets: */
+ layoutWidgets();
+}
+
+void UIGraphicsScrollArea::sltHandleScrollBarValueChange(int iValue)
+{
+ switch (m_enmOrientation)
+ {
+ /* Shift viewport horizontally: */
+ case Qt::Horizontal: m_pViewport->setPos(-iValue, 0); break;
+ /* Shift viewport vertically: */
+ case Qt::Vertical: m_pViewport->setPos(0, -iValue); break;
+ }
+}
+
+void UIGraphicsScrollArea::prepare()
+{
+ /* Prepare/layout widgets: */
+ prepareWidgets();
+ layoutWidgets();
+}
+
+void UIGraphicsScrollArea::prepareWidgets()
+{
+#ifdef VBOX_WS_MAC
+ /* Check whether scroll-bar is in auto-hide (overlay) mode: */
+ m_fAutoHideMode = darwinIsScrollerStyleOverlay();
+#endif
+
+ /* Create scroll-bar: */
+ m_pScrollBar = new UIGraphicsScrollBar(m_enmOrientation, m_fAutoHideMode, this);
+ if (m_pScrollBar)
+ {
+ m_pScrollBar->setZValue(1);
+ connect(m_pScrollBar, &UIGraphicsScrollBar::sigValueChanged,
+ this, &UIGraphicsScrollArea::sltHandleScrollBarValueChange);
+ }
+}
+
+void UIGraphicsScrollArea::layoutWidgets()
+{
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal:
+ {
+ /* Align scroll-bar horizontally: */
+ m_pScrollBar->resize(size().width(), m_pScrollBar->minimumSizeHint().height());
+ m_pScrollBar->setPos(0, size().height() - m_pScrollBar->size().height());
+ if (m_pViewport)
+ {
+ /* Adjust scroll-bar maximum value according to viewport width: */
+ const int iWidth = size().width();
+ const int iWidgetWidth = m_pViewport->size().width();
+ if (iWidgetWidth > iWidth)
+ m_pScrollBar->setMaximum(iWidgetWidth - iWidth);
+ else
+ m_pScrollBar->setMaximum(0);
+ }
+ break;
+ }
+ case Qt::Vertical:
+ {
+ /* Align scroll-bar vertically: */
+ m_pScrollBar->resize(m_pScrollBar->minimumSizeHint().width(), size().height());
+ m_pScrollBar->setPos(size().width() - m_pScrollBar->size().width(), 0);
+ if (m_pViewport)
+ {
+ /* Adjust scroll-bar maximum value according to viewport height: */
+ const int iHeight = size().height();
+ const int iWidgetHeight = m_pViewport->size().height();
+ if (iWidgetHeight > iHeight)
+ m_pScrollBar->setMaximum(iWidgetHeight - iHeight);
+ else
+ m_pScrollBar->setMaximum(0);
+ }
+ break;
+ }
+ }
+
+ /* Make scroll-bar visible only when there is viewport and maximum more than minimum: */
+ m_pScrollBar->setVisible(m_pViewport && m_pScrollBar->maximum() > m_pScrollBar->minimum());
+
+ if (m_pViewport)
+ {
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal:
+ {
+ /* Calculate geometry deduction: */
+ const int iDeduction = !m_fAutoHideMode && m_pScrollBar->isVisible() ? m_pScrollBar->minimumSizeHint().height() : 0;
+ /* Align viewport and shift it horizontally: */
+ m_pViewport->resize(m_pViewport->minimumSizeHint().width(), size().height() - iDeduction);
+ m_pViewport->setPos(-m_pScrollBar->value(), 0);
+ break;
+ }
+ case Qt::Vertical:
+ {
+ /* Calculate geometry deduction: */
+ const int iDeduction = !m_fAutoHideMode && m_pScrollBar->isVisible() ? m_pScrollBar->minimumSizeHint().width() : 0;
+ /* Align viewport and shift it vertically: */
+ m_pViewport->resize(size().width() - iDeduction, m_pViewport->minimumSizeHint().height());
+ m_pViewport->setPos(0, -m_pScrollBar->value());
+ break;
+ }
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollArea.h b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollArea.h
new file mode 100644
index 00000000..4d8cbb75
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollArea.h
@@ -0,0 +1,106 @@
+/* $Id: UIGraphicsScrollArea.h $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsScrollArea class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsScrollArea_h
+#define FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsScrollArea_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIGraphicsWidget.h"
+
+/* Forward declarations: */
+class QGraphicsScene;
+class UIGraphicsScrollBar;
+
+/** QIGraphicsWidget subclass providing GUI with graphics scroll-area. */
+class UIGraphicsScrollArea : public QIGraphicsWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs graphics scroll-area of requested @a enmOrientation, embedding it directly to passed @a pScene. */
+ UIGraphicsScrollArea(Qt::Orientation enmOrientation, QGraphicsScene *pScene);
+
+ /** Constructs graphics scroll-area of requested @a enmOrientation passing @a pParent to the base-class. */
+ UIGraphicsScrollArea(Qt::Orientation enmOrientation, QIGraphicsWidget *pParent = 0);
+
+ /** Returns minimum size-hint. */
+ virtual QSizeF minimumSizeHint() const RT_OVERRIDE;
+
+ /** Defines scroll-area @a pViewport. */
+ void setViewport(QIGraphicsWidget *pViewport);
+ /** Returns scroll-area viewport. */
+ QIGraphicsWidget *viewport() const;
+
+ /** Returns scrolling location value in pixels. */
+ int scrollingValue() const;
+ /** Defines scrolling location @a iValue in pixels. */
+ void setScrollingValue(int iValue);
+ /** Performs scrolling by @a iDelta pixels. */
+ void scrollBy(int iDelta);
+
+ /** Makes sure passed @a rect is visible. */
+ void makeSureRectIsVisible(const QRectF &rect);
+
+protected:
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles scroll-bar @a iValue change. */
+ void sltHandleScrollBarValueChange(int iValue);
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares all. */
+ void prepareWidgets();
+
+ /** Layout widgets. */
+ void layoutWidgets();
+
+ /** Holds the orientation. */
+ Qt::Orientation m_enmOrientation;
+ /** Holds whether scroll-bar is in auto-hide mode. */
+ bool m_fAutoHideMode;
+
+ /** Holds the scroll-bar instance. */
+ UIGraphicsScrollBar *m_pScrollBar;
+ /** Holds the viewport instance. */
+ QIGraphicsWidget *m_pViewport;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsScrollArea_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollBar.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollBar.cpp
new file mode 100644
index 00000000..c584bc64
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollBar.cpp
@@ -0,0 +1,1077 @@
+/* $Id: UIGraphicsScrollBar.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsScrollBar class implementation.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QApplication>
+#include <QGraphicsScene>
+#include <QGraphicsSceneMouseEvent>
+#include <QPainter>
+#include <QPropertyAnimation>
+#include <QSignalTransition>
+#include <QState>
+#include <QStateMachine>
+#include <QStyle>
+#include <QStyleOptionGraphicsItem>
+#include <QTimer>
+#include <QTimerEvent>
+
+/* GUI includes: */
+#include "UIGraphicsButton.h"
+#include "UIGraphicsScrollBar.h"
+#include "UIIconPool.h"
+
+
+/** Small functor for QTimer::singleShot worker to perform delayed scrolling. */
+class ScrollFunctor
+{
+public:
+
+ /** Performs delayed scrolling of specified @a pScrollBar to certain @a pos. */
+ ScrollFunctor(UIGraphicsScrollBar *pScrollBar, const QPointF &pos)
+ : m_pScrollBar(pScrollBar)
+ , m_pos(pos)
+ {}
+
+ /** Contains functor's body. */
+ void operator()()
+ {
+ m_pScrollBar->scrollTo(m_pos, 100);
+ }
+
+private:
+
+ /** Holds the scroll-bar reference to scroll. */
+ UIGraphicsScrollBar *m_pScrollBar;
+ /** Holds the position to scroll to. */
+ QPointF m_pos;
+};
+
+
+/** QIGraphicsWidget subclass providing GUI with graphics scroll-bar taken. */
+class UIGraphicsScrollBarToken : public QIGraphicsWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about mouse moved to certain @a pos. */
+ void sigMouseMoved(const QPointF &pos);
+
+public:
+
+ /** Constructs graphics scroll-bar token passing @a pParent to the base-class. */
+ UIGraphicsScrollBarToken(Qt::Orientation enmOrientation, QIGraphicsWidget *pParent = 0);
+
+ /** Returns minimum size-hint. */
+ virtual QSizeF minimumSizeHint() const RT_OVERRIDE;
+
+ /** Returns whether token is hovered. */
+ bool isHovered() const { return m_fHovered; }
+
+protected:
+
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
+
+ /** Handles mouse-press @a pEvent. */
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-release @a pEvent. */
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles hover enter @a pEvent. */
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+ /** Handles hover leave @a pEvent. */
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Updates scroll-bar extent value. */
+ void updateExtent();
+
+ /** Holds the orientation. */
+ const Qt::Orientation m_enmOrientation;
+
+ /** Holds the scroll-bar extent. */
+ int m_iExtent;
+
+ /** Holds whether item is hovered. */
+ bool m_fHovered;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIGraphicsScrollBarToken implementation. *
+*********************************************************************************************************************************/
+
+UIGraphicsScrollBarToken::UIGraphicsScrollBarToken(Qt::Orientation enmOrientation, QIGraphicsWidget *pParent /* = 0 */)
+ : QIGraphicsWidget(pParent)
+ , m_enmOrientation(enmOrientation)
+ , m_iExtent(0)
+ , m_fHovered(false)
+{
+ prepare();
+}
+
+QSizeF UIGraphicsScrollBarToken::minimumSizeHint() const
+{
+ /* Calculate minimum size-hint depending on orientation: */
+ switch (m_enmOrientation)
+ {
+#ifdef VBOX_WS_MAC
+ case Qt::Horizontal: return QSizeF(2 * m_iExtent, m_iExtent);
+ case Qt::Vertical: return QSizeF(m_iExtent, 2 * m_iExtent);
+#else
+ case Qt::Horizontal: return QSizeF(m_iExtent, m_iExtent);
+ case Qt::Vertical: return QSizeF(m_iExtent, m_iExtent);
+#endif
+ }
+
+ /* Call to base-class: */
+ return QIGraphicsWidget::minimumSizeHint();
+}
+
+void UIGraphicsScrollBarToken::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *)
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare color: */
+ const QPalette pal = QApplication::palette();
+
+#ifdef VBOX_WS_MAC
+
+ /* Draw background: */
+ QColor backgroundColor = pal.color(QPalette::Active, QPalette::Window).darker(190);
+ QRectF actualRectangle = pOptions->rect;
+ actualRectangle.setLeft(pOptions->rect.left() + .22 * pOptions->rect.width());
+ actualRectangle.setRight(pOptions->rect.right() - .22 * pOptions->rect.width());
+ const double dRadius = actualRectangle.width() / 2;
+ QPainterPath painterPath = QPainterPath(QPoint(actualRectangle.x(), actualRectangle.y() + dRadius));
+ painterPath.arcTo(QRectF(actualRectangle.x(), actualRectangle.y(), 2 * dRadius, 2 * dRadius), 180, -180);
+ painterPath.lineTo(actualRectangle.x() + 2 * dRadius, actualRectangle.y() + actualRectangle.height() - dRadius);
+ painterPath.arcTo(QRectF(actualRectangle.x(), actualRectangle.y() + actualRectangle.height() - 2 * dRadius, 2 * dRadius, 2 * dRadius), 0, -180);
+ painterPath.closeSubpath();
+ pPainter->setClipPath(painterPath);
+ pPainter->fillRect(actualRectangle, backgroundColor);
+
+#else
+
+ /* Draw background: */
+ QColor backgroundColor = pal.color(QPalette::Active, QPalette::Window).darker(140);
+ pPainter->fillRect(pOptions->rect, backgroundColor);
+
+#endif
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+void UIGraphicsScrollBarToken::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::mousePressEvent(pEvent);
+
+ /* Accept event to be able to receive mouse move events: */
+ pEvent->accept();
+}
+
+void UIGraphicsScrollBarToken::mouseMoveEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::mouseMoveEvent(pEvent);
+
+ /* Let listeners know about our mouse move events. */
+ emit sigMouseMoved(mapToParent(pEvent->pos()));
+}
+
+void UIGraphicsScrollBarToken::hoverMoveEvent(QGraphicsSceneHoverEvent *)
+{
+ if (!m_fHovered)
+ m_fHovered = true;
+}
+
+void UIGraphicsScrollBarToken::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
+{
+ if (m_fHovered)
+ m_fHovered = false;
+}
+
+void UIGraphicsScrollBarToken::prepare()
+{
+ setAcceptHoverEvents(true);
+
+ updateExtent();
+ resize(minimumSizeHint());
+}
+
+void UIGraphicsScrollBarToken::updateExtent()
+{
+ m_iExtent = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+ updateGeometry();
+}
+
+
+/*********************************************************************************************************************************
+* Class UIGraphicsScrollBar implementation. *
+*********************************************************************************************************************************/
+
+UIGraphicsScrollBar::UIGraphicsScrollBar(Qt::Orientation enmOrientation, bool fAutoHideMode, QGraphicsScene *pScene)
+ : m_enmOrientation(enmOrientation)
+ , m_fAutoHideMode(fAutoHideMode)
+ , m_iExtent(-1)
+ , m_iMinimum(0)
+ , m_iMaximum(100)
+ , m_iValue(0)
+ , m_pButton1(0)
+ , m_pButton2(0)
+ , m_pToken(0)
+ , m_fHovered(false)
+ , m_iHoverOnTimerId(0)
+ , m_iHoverOffTimerId(0)
+ , m_iHoveringValue(0)
+ , m_fScrollInProgress(false)
+#ifdef VBOX_WS_MAC
+ , m_fRevealed(false)
+ , m_iRevealingValue(m_fAutoHideMode ? 0 : 50)
+ , m_iRevealOnTimerId(0)
+ , m_iRevealOffTimerId(0)
+#endif
+{
+ pScene->addItem(this);
+ prepare();
+}
+
+UIGraphicsScrollBar::UIGraphicsScrollBar(Qt::Orientation enmOrientation, bool fAutoHideMode, QIGraphicsWidget *pParent /* = 0 */)
+ : QIGraphicsWidget(pParent)
+ , m_enmOrientation(enmOrientation)
+ , m_fAutoHideMode(fAutoHideMode)
+ , m_iExtent(-1)
+ , m_iMinimum(0)
+ , m_iMaximum(100)
+ , m_iValue(0)
+ , m_pButton1(0)
+ , m_pButton2(0)
+ , m_pToken(0)
+ , m_fHovered(false)
+ , m_iHoverOnTimerId(0)
+ , m_iHoverOffTimerId(0)
+ , m_iHoveringValue(0)
+ , m_fScrollInProgress(false)
+#ifdef VBOX_WS_MAC
+ , m_fRevealed(false)
+ , m_iRevealingValue(m_fAutoHideMode ? 0 : 50)
+ , m_iRevealOnTimerId(0)
+ , m_iRevealOffTimerId(0)
+#endif
+{
+ prepare();
+}
+
+QSizeF UIGraphicsScrollBar::minimumSizeHint() const
+{
+ /* Calculate minimum size-hint depending on orientation: */
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal: return QSizeF(3 * m_iExtent, m_iExtent);
+ case Qt::Vertical: return QSizeF(m_iExtent, 3 * m_iExtent);
+ }
+
+ /* Call to base-class: */
+ return QIGraphicsWidget::minimumSizeHint();
+}
+
+int UIGraphicsScrollBar::step() const
+{
+ return 2 * QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+}
+
+int UIGraphicsScrollBar::pageStep() const
+{
+ return 3 * step();
+}
+
+void UIGraphicsScrollBar::setMinimum(int iMinimum)
+{
+ m_iMinimum = iMinimum;
+ if (m_iMaximum < m_iMinimum)
+ m_iMaximum = m_iMinimum;
+ if (m_iValue < m_iMinimum)
+ {
+ m_iValue = m_iMinimum;
+ emit sigValueChanged(m_iValue);
+ }
+ layoutToken();
+}
+
+int UIGraphicsScrollBar::minimum() const
+{
+ return m_iMinimum;
+}
+
+void UIGraphicsScrollBar::setMaximum(int iMaximum)
+{
+ m_iMaximum = iMaximum;
+ if (m_iMinimum > m_iMaximum)
+ m_iMinimum = m_iMaximum;
+ if (m_iValue > m_iMaximum)
+ {
+ m_iValue = m_iMaximum;
+ emit sigValueChanged(m_iValue);
+ }
+ layoutToken();
+}
+
+int UIGraphicsScrollBar::maximum() const
+{
+ return m_iMaximum;
+}
+
+void UIGraphicsScrollBar::setValue(int iValue)
+{
+ if (iValue > m_iMaximum)
+ iValue = m_iMaximum;
+ if (iValue < m_iMinimum)
+ iValue = m_iMinimum;
+ m_iValue = iValue;
+ emit sigValueChanged(m_iValue);
+ layoutToken();
+}
+
+int UIGraphicsScrollBar::value() const
+{
+ return m_iValue;
+}
+
+void UIGraphicsScrollBar::scrollTo(const QPointF &desiredPos, int iDelay /* = 500 */)
+{
+ /* Prepare current, desired and intermediate positions: */
+ const QPointF currentPos = actualTokenPosition();
+ const int iCurrentY = currentPos.y();
+ const int iCurrentX = currentPos.x();
+ const int iDesiredY = desiredPos.y();
+ const int iDesiredX = desiredPos.x();
+ QPointF intermediatePos;
+
+ /* Calculate intermediate position depending on orientation: */
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal:
+ {
+ if (iCurrentX < iDesiredX)
+ {
+ intermediatePos.setY(desiredPos.y());
+ intermediatePos.setX(iCurrentX + pageStep() < iDesiredX ? iCurrentX + pageStep() : iDesiredX);
+ }
+ else if (iCurrentX > iDesiredX)
+ {
+ intermediatePos.setY(desiredPos.y());
+ intermediatePos.setX(iCurrentX - pageStep() > iDesiredX ? iCurrentX - pageStep() : iDesiredX);
+ }
+ break;
+ }
+ case Qt::Vertical:
+ {
+ if (iCurrentY < iDesiredY)
+ {
+ intermediatePos.setX(desiredPos.x());
+ intermediatePos.setY(iCurrentY + pageStep() < iDesiredY ? iCurrentY + pageStep() : iDesiredY);
+ }
+ else if (iCurrentY > iDesiredY)
+ {
+ intermediatePos.setX(desiredPos.x());
+ intermediatePos.setY(iCurrentY - pageStep() > iDesiredY ? iCurrentY - pageStep() : iDesiredY);
+ }
+ break;
+ }
+ }
+
+ /* Move token to intermediate position: */
+ if (!intermediatePos.isNull())
+ sltTokenMoved(intermediatePos);
+
+ /* Continue, if we haven't reached required position: */
+ if ((intermediatePos != desiredPos) && m_fScrollInProgress)
+ QTimer::singleShot(iDelay, ScrollFunctor(this, desiredPos));
+}
+
+void UIGraphicsScrollBar::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::resizeEvent(pEvent);
+
+ /* Layout widgets: */
+ layoutWidgets();
+}
+
+void UIGraphicsScrollBar::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *)
+{
+ /* Acquire rectangle: */
+ const QRect rectangle = pOptions->rect;
+ /* Paint background: */
+ paintBackground(pPainter, rectangle);
+}
+
+void UIGraphicsScrollBar::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::mousePressEvent(pEvent);
+
+ /* Mark event accepted so that it couldn't
+ * influence underlying widgets: */
+ pEvent->accept();
+
+ /* Start scrolling sequence: */
+ m_fScrollInProgress = true;
+ scrollTo(pEvent->pos());
+}
+
+void UIGraphicsScrollBar::mouseReleaseEvent(QGraphicsSceneMouseEvent *pEvent)
+{
+ /* Call to base-class: */
+ QIGraphicsWidget::mousePressEvent(pEvent);
+
+ /* Mark event accepted so that it couldn't
+ * influence underlying widgets: */
+ pEvent->accept();
+
+ /* Stop scrolling if any: */
+ m_fScrollInProgress = false;
+}
+
+void UIGraphicsScrollBar::hoverMoveEvent(QGraphicsSceneHoverEvent *)
+{
+ /* Only if not yet hovered, that way we
+ * make sure trigger emitted just once: */
+ if (!m_fHovered)
+ {
+ /* Start hover-on timer, handled in timerEvent() below: */
+ m_iHoverOnTimerId = startTimer(m_fAutoHideMode ? 400 : 100);
+ m_fHovered = true;
+ }
+ /* Update in any case: */
+ update();
+}
+
+void UIGraphicsScrollBar::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
+{
+ /* Only if it's still hovered, that way we
+ * make sure trigger emitted just once: */
+ if (m_fHovered)
+ {
+ /* Start hover-off timer, handled in timerEvent() below: */
+ m_iHoverOffTimerId = startTimer(m_fAutoHideMode ? 1000 : 100);
+ m_fHovered = false;
+ }
+ /* Update in any case: */
+ update();
+}
+
+void UIGraphicsScrollBar::timerEvent(QTimerEvent *pEvent)
+{
+ /* Kill timer in any case: */
+ const int iTimerId = pEvent->timerId();
+ killTimer(iTimerId);
+
+ /* If that is hover-on timer: */
+ if (m_iHoverOnTimerId != 0 && iTimerId == m_iHoverOnTimerId)
+ {
+ /* Wait for timer no more: */
+ m_iHoverOnTimerId = 0;
+ /* Emit hover-on trigger if hovered: */
+ if (m_fHovered)
+ emit sigHoverEnter();
+ /* Update in any case: */
+ update();
+ }
+
+ else
+
+ /* If that is hover-off timer: */
+ if (m_iHoverOffTimerId != 0 && iTimerId == m_iHoverOffTimerId)
+ {
+ /* Wait for timer no more: */
+ m_iHoverOffTimerId = 0;
+ /* Emit hover-off trigger if not hovered: */
+ if (!m_fHovered && !m_pToken->isHovered())
+ emit sigHoverLeave();
+ /* Update in any case: */
+ update();
+ }
+
+#ifdef VBOX_WS_MAC
+
+ else
+
+ /* If that is reveal-in timer: */
+ if (m_iRevealOnTimerId != 0 && iTimerId == m_iRevealOnTimerId)
+ {
+ /* Wait for timer no more: */
+ m_iRevealOnTimerId = 0;
+ }
+
+ else
+
+ /* If that is reveal-out timer: */
+ if (m_iRevealOffTimerId != 0 && iTimerId == m_iRevealOffTimerId)
+ {
+ /* Wait for timer no more: */
+ m_iRevealOffTimerId = 0;
+ /* Emit reveal-out signal if not hovered or was reinvoked not long time ago: */
+ if (!m_fHovered && !m_pToken->isHovered() && m_iRevealOnTimerId == 0)
+ emit sigRevealLeave();
+ /* Restart timer otherwise: */
+ else
+ m_iRevealOffTimerId = startTimer(m_fAutoHideMode ? 2000 : 100);
+ /* Update in any case: */
+ update();
+ }
+
+#endif
+}
+
+void UIGraphicsScrollBar::sltButton1Clicked()
+{
+ setValue(value() - step());
+}
+
+void UIGraphicsScrollBar::sltButton2Clicked()
+{
+ setValue(value() + step());
+}
+
+void UIGraphicsScrollBar::sltTokenMoved(const QPointF &pos)
+{
+ /* Depending on orientation: */
+ double dRatio = 0;
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal:
+ {
+ /* We have to calculate the X coord of the token, leaving Y untouched: */
+#ifdef VBOX_WS_MAC
+ const int iMin = 0;
+ const int iMax = size().width() - 2 * m_iExtent;
+#else
+ const int iMin = m_iExtent;
+ const int iMax = size().width() - 2 * m_iExtent;
+#endif
+ int iX = pos.x() - m_iExtent / 2;
+ iX = qMax(iX, iMin);
+ iX = qMin(iX, iMax);
+ /* And then calculate new ratio to update value finally: */
+ dRatio = iMax > iMin ? (double)(iX - iMin) / (iMax - iMin) : 0;
+ break;
+ }
+ case Qt::Vertical:
+ {
+ /* We have to calculate the Y coord of the token, leaving X untouched: */
+#ifdef VBOX_WS_MAC
+ const int iMin = 0;
+ const int iMax = size().height() - 2 * m_iExtent;
+#else
+ const int iMin = m_iExtent;
+ const int iMax = size().height() - 2 * m_iExtent;
+#endif
+ int iY = pos.y() - m_iExtent / 2;
+ iY = qMax(iY, iMin);
+ iY = qMin(iY, iMax);
+ /* And then calculate new ratio to update value finally: */
+ dRatio = iMax > iMin ? (double)(iY - iMin) / (iMax - iMin) : 0;
+ break;
+ }
+ }
+
+ /* Update value according to calculated ratio: */
+ m_iValue = dRatio * (m_iMaximum - m_iMinimum) + m_iMinimum;
+ emit sigValueChanged(m_iValue);
+ layoutToken();
+}
+
+void UIGraphicsScrollBar::sltStateLeftDefault()
+{
+ if (m_pButton1)
+ m_pButton1->hide();
+ if (m_pButton2)
+ m_pButton2->hide();
+ if (m_pToken)
+ m_pToken->hide();
+}
+
+void UIGraphicsScrollBar::sltStateLeftHovered()
+{
+ if (m_pButton1)
+ m_pButton1->hide();
+ if (m_pButton2)
+ m_pButton2->hide();
+ if (m_pToken)
+ m_pToken->hide();
+}
+
+void UIGraphicsScrollBar::sltStateEnteredDefault()
+{
+ if (m_pButton1)
+ m_pButton1->hide();
+ if (m_pButton2)
+ m_pButton2->hide();
+ if (m_pToken)
+ m_pToken->hide();
+}
+
+void UIGraphicsScrollBar::sltStateEnteredHovered()
+{
+ if (m_pButton1)
+ m_pButton1->show();
+ if (m_pButton2)
+ m_pButton2->show();
+ if (m_pToken)
+ m_pToken->show();
+}
+
+#ifdef VBOX_WS_MAC
+void UIGraphicsScrollBar::sltHandleRevealingStart()
+{
+ /* Only if not yet revealed, that way we
+ * make sure trigger emitted just once: */
+ if (!m_fRevealed)
+ {
+ /* Mark token revealed: */
+ m_fRevealed = true;
+ /* Emit reveal signal immediately: */
+ emit sigRevealEnter();
+ }
+
+ /* Restart fresh sustain timer: */
+ m_iRevealOnTimerId = startTimer(m_fAutoHideMode ? 1000 : 100);
+}
+
+void UIGraphicsScrollBar::sltStateEnteredFaded()
+{
+ /* Mark token faded: */
+ m_fRevealed = false;
+}
+
+void UIGraphicsScrollBar::sltStateEnteredRevealed()
+{
+ /* Start reveal-out timer: */
+ m_iRevealOffTimerId = startTimer(m_fAutoHideMode ? 2000 : 100);
+}
+#endif /* VBOX_WS_MAC */
+
+void UIGraphicsScrollBar::prepare()
+{
+ /* Configure self: */
+ setAcceptHoverEvents(true);
+
+ /* Prepare/layout widgets: */
+ prepareWidgets();
+ updateExtent();
+ layoutWidgets();
+
+ /* Prepare animation: */
+ prepareAnimation();
+}
+
+void UIGraphicsScrollBar::prepareWidgets()
+{
+ prepareButtons();
+ prepareToken();
+}
+
+void UIGraphicsScrollBar::prepareButtons()
+{
+#ifndef VBOX_WS_MAC
+ /* Create buttons depending on orientation: */
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal:
+ {
+ m_pButton1 = new UIGraphicsButton(this, UIIconPool::iconSet(":/arrow_left_10px.png"));
+ m_pButton2 = new UIGraphicsButton(this, UIIconPool::iconSet(":/arrow_right_10px.png"));
+ break;
+ }
+ case Qt::Vertical:
+ {
+ m_pButton1 = new UIGraphicsButton(this, UIIconPool::iconSet(":/arrow_up_10px.png"));
+ m_pButton2 = new UIGraphicsButton(this, UIIconPool::iconSet(":/arrow_down_10px.png"));
+ break;
+ }
+ }
+
+ if (m_pButton1)
+ {
+ /* We use 10px icons, not 16px, let buttons know that: */
+ m_pButton1->setIconScaleIndex((double)10 / 16);
+ /* Also we want to have buttons react on mouse presses for auto-repeat feature: */
+ m_pButton1->setClickPolicy(UIGraphicsButton::ClickPolicy_OnPress);
+ connect(m_pButton1, &UIGraphicsButton::sigButtonClicked,
+ this, &UIGraphicsScrollBar::sltButton1Clicked);
+ }
+ if (m_pButton2)
+ {
+ /* We use 10px icons, not 16px, let buttons know that: */
+ m_pButton2->setIconScaleIndex((double)10 / 16);
+ /* Also we want to have buttons react on mouse presses for auto-repeat feature: */
+ m_pButton2->setClickPolicy(UIGraphicsButton::ClickPolicy_OnPress);
+ connect(m_pButton2, &UIGraphicsButton::sigButtonClicked,
+ this, &UIGraphicsScrollBar::sltButton2Clicked);
+ }
+#endif
+}
+
+void UIGraphicsScrollBar::prepareToken()
+{
+ /* Create token: */
+ m_pToken = new UIGraphicsScrollBarToken(m_enmOrientation, this);
+ if (m_pToken)
+ connect(m_pToken, &UIGraphicsScrollBarToken::sigMouseMoved,
+ this, &UIGraphicsScrollBar::sltTokenMoved);
+}
+
+void UIGraphicsScrollBar::prepareAnimation()
+{
+ prepareHoveringAnimation();
+#ifdef VBOX_WS_MAC
+ prepareRevealingAnimation();
+#endif
+}
+
+void UIGraphicsScrollBar::prepareHoveringAnimation()
+{
+ /* Create hovering animation machine: */
+ QStateMachine *pHoveringMachine = new QStateMachine(this);
+ if (pHoveringMachine)
+ {
+ /* Create 'default' state: */
+ QState *pStateDefault = new QState(pHoveringMachine);
+ /* Create 'hovered' state: */
+ QState *pStateHovered = new QState(pHoveringMachine);
+
+ /* Configure 'default' state: */
+ if (pStateDefault)
+ {
+ /* When we entering default state => we assigning hoveringValue to 0: */
+ pStateDefault->assignProperty(this, "hoveringValue", 0);
+ connect(pStateDefault, &QState::propertiesAssigned, this, &UIGraphicsScrollBar::sltStateEnteredDefault);
+
+ /* Add state transitions: */
+ QSignalTransition *pDefaultToHovered = pStateDefault->addTransition(this, SIGNAL(sigHoverEnter()), pStateHovered);
+ if (pDefaultToHovered)
+ {
+ connect(pDefaultToHovered, &QSignalTransition::triggered, this, &UIGraphicsScrollBar::sltStateLeftDefault);
+
+ /* Create forward animation: */
+ QPropertyAnimation *pHoveringAnimationForward = new QPropertyAnimation(this, "hoveringValue", this);
+ if (pHoveringAnimationForward)
+ {
+ pHoveringAnimationForward->setDuration(200);
+ pHoveringAnimationForward->setStartValue(0);
+ pHoveringAnimationForward->setEndValue(100);
+
+ /* Add to transition: */
+ pDefaultToHovered->addAnimation(pHoveringAnimationForward);
+ }
+ }
+ }
+
+ /* Configure 'hovered' state: */
+ if (pStateHovered)
+ {
+ /* When we entering hovered state => we assigning hoveringValue to 100: */
+ pStateHovered->assignProperty(this, "hoveringValue", 100);
+ connect(pStateHovered, &QState::propertiesAssigned, this, &UIGraphicsScrollBar::sltStateEnteredHovered);
+
+ /* Add state transitions: */
+ QSignalTransition *pHoveredToDefault = pStateHovered->addTransition(this, SIGNAL(sigHoverLeave()), pStateDefault);
+ if (pHoveredToDefault)
+ {
+ connect(pHoveredToDefault, &QSignalTransition::triggered, this, &UIGraphicsScrollBar::sltStateLeftHovered);
+
+ /* Create backward animation: */
+ QPropertyAnimation *pHoveringAnimationBackward = new QPropertyAnimation(this, "hoveringValue", this);
+ if (pHoveringAnimationBackward)
+ {
+ pHoveringAnimationBackward->setDuration(200);
+ pHoveringAnimationBackward->setStartValue(100);
+ pHoveringAnimationBackward->setEndValue(0);
+
+ /* Add to transition: */
+ pHoveredToDefault->addAnimation(pHoveringAnimationBackward);
+ }
+ }
+ }
+
+ /* Initial state is 'default': */
+ pHoveringMachine->setInitialState(pStateDefault);
+ /* Start state-machine: */
+ pHoveringMachine->start();
+ }
+}
+
+#ifdef VBOX_WS_MAC
+void UIGraphicsScrollBar::prepareRevealingAnimation()
+{
+ /* Create revealing animation machine: */
+ QStateMachine *pRevealingMachine = new QStateMachine(this);
+ if (pRevealingMachine)
+ {
+ /* Create 'faded' state: */
+ QState *pStateFaded = new QState(pRevealingMachine);
+ /* Create 'revealed' state: */
+ QState *pStateRevealed = new QState(pRevealingMachine);
+
+ /* Configure 'faded' state: */
+ if (pStateFaded)
+ {
+ /* When we entering fade state => we assigning revealingValue to 0: */
+ pStateFaded->assignProperty(this, "revealingValue", m_fAutoHideMode ? 0 : 50);
+ connect(pStateFaded, &QState::propertiesAssigned, this, &UIGraphicsScrollBar::sltStateEnteredFaded);
+
+ /* Add state transitions: */
+ QSignalTransition *pFadeToRevealed = pStateFaded->addTransition(this, SIGNAL(sigRevealEnter()), pStateRevealed);
+ if (pFadeToRevealed)
+ {
+ /* Create forward animation: */
+ QPropertyAnimation *pRevealingAnimationForward = new QPropertyAnimation(this, "revealingValue", this);
+ if (pRevealingAnimationForward)
+ {
+ pRevealingAnimationForward->setDuration(200);
+ pRevealingAnimationForward->setStartValue(m_fAutoHideMode ? 0 : 50);
+ pRevealingAnimationForward->setEndValue(100);
+
+ /* Add to transition: */
+ pFadeToRevealed->addAnimation(pRevealingAnimationForward);
+ }
+ }
+ }
+
+ /* Configure 'revealed' state: */
+ if (pStateRevealed)
+ {
+ /* When we entering revealed state => we assigning revealingValue to 100: */
+ pStateRevealed->assignProperty(this, "revealingValue", 100);
+ connect(pStateRevealed, &QState::propertiesAssigned, this, &UIGraphicsScrollBar::sltStateEnteredRevealed);
+
+ /* Add state transitions: */
+ QSignalTransition *pRevealedToFaded = pStateRevealed->addTransition(this, SIGNAL(sigRevealLeave()), pStateFaded);
+ if (pRevealedToFaded)
+ {
+ /* Create backward animation: */
+ QPropertyAnimation *pRevealingAnimationBackward = new QPropertyAnimation(this, "revealingValue", this);
+ if (pRevealingAnimationBackward)
+ {
+ pRevealingAnimationBackward->setDuration(200);
+ pRevealingAnimationBackward->setStartValue(100);
+ pRevealingAnimationBackward->setEndValue(m_fAutoHideMode ? 0 : 50);
+
+ /* Add to transition: */
+ pRevealedToFaded->addAnimation(pRevealingAnimationBackward);
+ }
+ }
+ }
+
+ /* Initial state is 'fade': */
+ pRevealingMachine->setInitialState(pStateFaded);
+ /* Start state-machine: */
+ pRevealingMachine->start();
+ }
+
+ /* Install self-listener: */
+ connect(this, &UIGraphicsScrollBar::sigValueChanged, this, &UIGraphicsScrollBar::sltHandleRevealingStart);
+}
+#endif /* VBOX_WS_MAC */
+
+void UIGraphicsScrollBar::updateExtent()
+{
+ /* Make sure extent value is not smaller than the button size: */
+ m_iExtent = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+#ifndef VBOX_WS_MAC
+ m_iExtent = qMax(m_iExtent, (int)m_pButton1->minimumSizeHint().width());
+ m_iExtent = qMax(m_iExtent, (int)m_pButton2->minimumSizeHint().width());
+#endif
+ updateGeometry();
+}
+
+void UIGraphicsScrollBar::layoutWidgets()
+{
+ layoutButtons();
+ layoutToken();
+}
+
+void UIGraphicsScrollBar::layoutButtons()
+{
+#ifndef VBOX_WS_MAC
+ // WORKAROUND:
+ // We are calculating proper button shift delta, because
+ // button size can be smaller than scroll-bar extent value.
+
+ int iDelta1 = 0;
+ if (m_iExtent > m_pButton1->minimumSizeHint().width())
+ iDelta1 = (m_iExtent - m_pButton1->minimumSizeHint().width() + 1) / 2;
+ m_pButton1->setPos(iDelta1, iDelta1);
+
+ int iDelta2 = 0;
+ if (m_iExtent > m_pButton2->minimumSizeHint().width())
+ iDelta2 = (m_iExtent - m_pButton2->minimumSizeHint().width() + 1) / 2;
+ m_pButton2->setPos(size().width() - m_iExtent + iDelta2, size().height() - m_iExtent + iDelta2);
+#endif
+}
+
+void UIGraphicsScrollBar::layoutToken()
+{
+ m_pToken->setPos(actualTokenPosition());
+ update();
+}
+
+QPoint UIGraphicsScrollBar::actualTokenPosition() const
+{
+ /* We calculating ratio on the basis of current/minimum/maximum values: */
+ const double dRatio = m_iMaximum > m_iMinimum ? (double)(m_iValue - m_iMinimum) / (m_iMaximum - m_iMinimum) : 0;
+
+ /* Prepare result: */
+ QPoint position;
+
+ /* Depending on orientation: */
+ switch (m_enmOrientation)
+ {
+ case Qt::Horizontal:
+ {
+ /* We have to adjust the X coord of the token, leaving Y unchanged: */
+#ifdef VBOX_WS_MAC
+ const int iMin = 0;
+ const int iMax = size().width() - 2 * m_iExtent;
+#else
+ const int iMin = m_iExtent;
+ const int iMax = size().width() - 2 * m_iExtent;
+#endif
+ int iX = dRatio * (iMax - iMin) + iMin;
+ position = QPoint(iX, 0);
+ break;
+ }
+ case Qt::Vertical:
+ {
+ /* We have to adjust the Y coord of the token, leaving X unchanged: */
+#ifdef VBOX_WS_MAC
+ const int iMin = 0;
+ const int iMax = size().height() - 2 * m_iExtent;
+#else
+ const int iMin = m_iExtent;
+ const int iMax = size().height() - 2 * m_iExtent;
+#endif
+ int iY = dRatio * (iMax - iMin) + iMin;
+ position = QPoint(0, iY);
+ break;
+ }
+ }
+
+ /* Return result: */
+ return position;
+}
+
+void UIGraphicsScrollBar::paintBackground(QPainter *pPainter, const QRect &rectangle) const
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare color: */
+ const QPalette pal = QApplication::palette();
+
+#ifdef VBOX_WS_MAC
+
+ /* Draw background if necessary: */
+ pPainter->save();
+ QColor windowColor = pal.color(QPalette::Active, QPalette::Window);
+ if (m_fAutoHideMode)
+ windowColor.setAlpha(255 * ((double)m_iHoveringValue / 100));
+ pPainter->fillRect(rectangle, windowColor);
+ pPainter->restore();
+
+ /* Draw frame if necessary: */
+ pPainter->save();
+ QColor frameColor = pal.color(QPalette::Active, QPalette::Window);
+ if (m_fAutoHideMode)
+ frameColor.setAlpha(255 * ((double)m_iHoveringValue / 100));
+ frameColor = frameColor.darker(120);
+ pPainter->setPen(frameColor);
+ pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
+ pPainter->restore();
+
+ /* Emulate token when necessary: */
+ if (m_iHoveringValue < 100)
+ {
+ QColor tokenColor = pal.color(QPalette::Active, QPalette::Window);
+ tokenColor.setAlpha(255 * ((double)m_iRevealingValue / 100));
+ tokenColor = tokenColor.darker(190);
+ QRectF tokenRectangle = QRect(actualTokenPosition(), QSize(m_iExtent, 2 * m_iExtent));
+ QRectF actualRectangle = tokenRectangle;
+ if (m_fAutoHideMode)
+ {
+ actualRectangle.setLeft(tokenRectangle.left() + .22 * tokenRectangle.width() + .22 * tokenRectangle.width() * ((double)100 - m_iHoveringValue) / 100);
+ actualRectangle.setRight(tokenRectangle.right() - .22 * tokenRectangle.width() + .22 * tokenRectangle.width() * ((double)100 - m_iHoveringValue) / 100 - 1);
+ }
+ else
+ {
+ actualRectangle.setLeft(tokenRectangle.left() + .22 * tokenRectangle.width());
+ actualRectangle.setRight(tokenRectangle.right() - .22 * tokenRectangle.width() - 1);
+ }
+ const double dRadius = actualRectangle.width() / 2;
+ QPainterPath painterPath = QPainterPath(QPoint(actualRectangle.x(), actualRectangle.y() + dRadius));
+ painterPath.arcTo(QRectF(actualRectangle.x(), actualRectangle.y(), 2 * dRadius, 2 * dRadius), 180, -180);
+ painterPath.lineTo(actualRectangle.x() + 2 * dRadius, actualRectangle.y() + actualRectangle.height() - dRadius);
+ painterPath.arcTo(QRectF(actualRectangle.x(), actualRectangle.y() + actualRectangle.height() - 2 * dRadius, 2 * dRadius, 2 * dRadius), 0, -180);
+ painterPath.closeSubpath();
+ pPainter->setClipPath(painterPath);
+ pPainter->fillRect(actualRectangle, tokenColor);
+ }
+
+#else
+
+ /* Draw background: */
+ QColor backgroundColor = pal.color(QPalette::Active, QPalette::Window);
+ backgroundColor.setAlpha(50 + (double)m_iHoveringValue / 100 * 150);
+ QRect actualRectangle = rectangle;
+ actualRectangle.setLeft(actualRectangle.left() + .85 * actualRectangle.width() * ((double)100 - m_iHoveringValue) / 100);
+ pPainter->fillRect(actualRectangle, backgroundColor);
+
+ /* Emulate token when necessary: */
+ if (m_iHoveringValue < 100)
+ {
+ QColor tokenColor = pal.color(QPalette::Active, QPalette::Window).darker(140);
+ QRect tokenRectangle = QRect(actualTokenPosition(), QSize(m_iExtent, m_iExtent));
+ tokenRectangle.setLeft(tokenRectangle.left() + .85 * tokenRectangle.width() * ((double)100 - m_iHoveringValue) / 100);
+ pPainter->fillRect(tokenRectangle, tokenColor);
+ }
+
+#endif
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+
+#include "UIGraphicsScrollBar.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollBar.h b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollBar.h
new file mode 100644
index 00000000..c4ea422b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollBar.h
@@ -0,0 +1,245 @@
+/* $Id: UIGraphicsScrollBar.h $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsScrollBar class declaration.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsScrollBar_h
+#define FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsScrollBar_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIGraphicsWidget.h"
+
+/* Forward declarations: */
+class QGraphicsScene;
+class UIGraphicsButton;
+class UIGraphicsScrollBarToken;
+
+/** QIGraphicsWidget subclass providing GUI with graphics scroll-bar. */
+class UIGraphicsScrollBar : public QIGraphicsWidget
+{
+ Q_OBJECT;
+ Q_PROPERTY(int hoveringValue READ hoveringValue WRITE setHoveringValue);
+#ifdef VBOX_WS_MAC
+ Q_PROPERTY(int revealingValue READ revealingValue WRITE setRevealingValue);
+#endif
+
+signals:
+
+ /** Notifies listeners about hover enter. */
+ void sigHoverEnter();
+ /** Notifies listeners about hover leave. */
+ void sigHoverLeave();
+
+#ifdef VBOX_WS_MAC
+ /** Notifies listeners about token should be revealed. */
+ void sigRevealEnter();
+ /** Notifies listeners about token should be faded. */
+ void sigRevealLeave();
+#endif
+
+ /** Notifies listeners about @a iValue has changed. */
+ void sigValueChanged(int iValue);
+
+public:
+
+ /** Constructs graphics scroll-bar of requested @a enmOrientation, embedding it directly to passed @a pScene.
+ * @param fAutoHideMode Brings whether scroll-bar should be created in auto-hide mode. */
+ UIGraphicsScrollBar(Qt::Orientation enmOrientation, bool fAutoHideMode, QGraphicsScene *pScene);
+
+ /** Constructs graphics scroll-bar of requested @a enmOrientation passing @a pParent to the base-class.
+ * @param fAutoHideMode Brings whether scroll-bar should be created in auto-hide mode. */
+ UIGraphicsScrollBar(Qt::Orientation enmOrientation, bool fAutoHideMode, QIGraphicsWidget *pParent = 0);
+
+ /** Returns minimum size-hint. */
+ virtual QSizeF minimumSizeHint() const RT_OVERRIDE;
+
+ /** Returns scrolling step. */
+ int step() const;
+ /** Returns page scrolling step. */
+ int pageStep() const;
+
+ /** Defines @a iMinimum scroll-bar value. */
+ void setMinimum(int iMinimum);
+ /** Returns minimum scroll-bar value. */
+ int minimum() const;
+
+ /** Defines @a iMaximum scroll-bar value. */
+ void setMaximum(int iMaximum);
+ /** Returns minimum scroll-bar value. */
+ int maximum() const;
+
+ /** Defines current scroll-bar @a iValue. */
+ void setValue(int iValue);
+ /** Returns current scroll-bar value. */
+ int value() const;
+
+ /** Performs scrolling to certain @a desiredPos with certain @a iDelay. */
+ void scrollTo(const QPointF &desiredPos, int iDelay = 500);
+
+protected:
+
+ /** Handles resize @a pEvent. */
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *pEvent) RT_OVERRIDE;
+
+ /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
+ virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
+
+ /** Handles mouse-press @a pEvent. */
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+ /** Handles mouse-release @a pEvent. */
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles hover enter @a pEvent. */
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+ /** Handles hover leave @a pEvent. */
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
+
+ /** Handles timer @a pEvent. */
+ virtual void timerEvent(QTimerEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ /** Handles button 1 click signal. */
+ void sltButton1Clicked();
+ /** Handles button 2 click signal. */
+ void sltButton2Clicked();
+
+ /** Handles token being moved to cpecified @a pos. */
+ void sltTokenMoved(const QPointF &pos);
+
+ /** Handles default state leaving. */
+ void sltStateLeftDefault();
+ /** Handles hovered state leaving. */
+ void sltStateLeftHovered();
+ /** Handles default state entering. */
+ void sltStateEnteredDefault();
+ /** Handles hovered state entering. */
+ void sltStateEnteredHovered();
+
+#ifdef VBOX_WS_MAC
+ /** Handles signals to start revealing. */
+ void sltHandleRevealingStart();
+ /** Handles faded state entering. */
+ void sltStateEnteredFaded();
+ /** Handles revealed state entering. */
+ void sltStateEnteredRevealed();
+#endif
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Prepares widgets. */
+ void prepareWidgets();
+ /** Prepares buttons. */
+ void prepareButtons();
+ /** Prepares token. */
+ void prepareToken();
+ /** Prepares animation. */
+ void prepareAnimation();
+ /** Prepares hovering animation. */
+ void prepareHoveringAnimation();
+#ifdef VBOX_WS_MAC
+ /** Prepares revealing animation. */
+ void prepareRevealingAnimation();
+#endif
+
+ /** Updates scroll-bar extent value. */
+ void updateExtent();
+ /** Layout widgets. */
+ void layoutWidgets();
+ /** Layout buttons. */
+ void layoutButtons();
+ /** Layout token. */
+ void layoutToken();
+
+ /** Returns actual token position. */
+ QPoint actualTokenPosition() const;
+
+ /** Paints background using specified @a pPainter and certain @a rectangle. */
+ void paintBackground(QPainter *pPainter, const QRect &rectangle) const;
+
+ /** Defines hovering animation @a iValue. */
+ void setHoveringValue(int iValue) { m_iHoveringValue = iValue; update(); }
+ /** Returns hovering animation value. */
+ int hoveringValue() const { return m_iHoveringValue; }
+
+#ifdef VBOX_WS_MAC
+ /** Defines revealing animation @a iValue. */
+ void setRevealingValue(int iValue) { m_iRevealingValue = iValue; update(); }
+ /** Returns revealing animation value. */
+ int revealingValue() const { return m_iRevealingValue; }
+#endif
+
+ /** Holds the orientation. */
+ const Qt::Orientation m_enmOrientation;
+ /** Holds whether scroll-bar is in auto-hide mode. */
+ bool m_fAutoHideMode;
+
+ /** Holds the scroll-bar extent. */
+ int m_iExtent;
+
+ /** Holds the minimum scroll-bar value. */
+ int m_iMinimum;
+ /** Holds the maximum scroll-bar value. */
+ int m_iMaximum;
+ /** Holds the current scroll-bar value. */
+ int m_iValue;
+
+ /** Holds the 1st arrow button instance. */
+ UIGraphicsButton *m_pButton1;
+ /** Holds the 2nd arrow button instance. */
+ UIGraphicsButton *m_pButton2;
+ /** Holds the scroll-bar token instance. */
+ UIGraphicsScrollBarToken *m_pToken;
+
+ /** Holds whether item is hovered. */
+ bool m_fHovered;
+ /** Holds the hover-on timer id. */
+ int m_iHoverOnTimerId;
+ /** Holds the hover-off timer id. */
+ int m_iHoverOffTimerId;
+ /** Holds the hovering animation value. */
+ int m_iHoveringValue;
+
+ /** Holds whether we are scrolling. */
+ bool m_fScrollInProgress;
+
+#ifdef VBOX_WS_MAC
+ /** Holds whether token is revealed. */
+ bool m_fRevealed;
+ /** Holds the revealing animation value. */
+ int m_iRevealingValue;
+ /** Holds the reveal-out timer id. */
+ int m_iRevealOnTimerId;
+ /** Holds the reveal-out timer id. */
+ int m_iRevealOffTimerId;
+#endif
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsScrollBar_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.cpp
new file mode 100644
index 00000000..2a568816
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.cpp
@@ -0,0 +1,541 @@
+/* $Id: UIGraphicsTextPane.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsTextPane class implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAccessibleObject>
+#include <QPainter>
+#include <QTextLayout>
+#include <QApplication>
+#include <QFontMetrics>
+#include <QGraphicsSceneHoverEvent>
+#include <QRegularExpression>
+
+/* GUI includes: */
+#include "UICursor.h"
+#include "UIGraphicsTextPane.h"
+#include "UIRichTextString.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+/** QAccessibleObject extension used as an accessibility interface for UITextTableLine. */
+class UIAccessibilityInterfaceForUITextTableLine : public QAccessibleObject
+{
+public:
+
+ /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
+ static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
+ {
+ /* Creating Details-view accessibility interface: */
+ if (pObject && strClassname == QLatin1String("UITextTableLine"))
+ return new UIAccessibilityInterfaceForUITextTableLine(pObject);
+
+ /* Null by default: */
+ return 0;
+ }
+
+ /** Constructs an accessibility interface passing @a pObject to the base-class. */
+ UIAccessibilityInterfaceForUITextTableLine(QObject *pObject)
+ : QAccessibleObject(pObject)
+ {}
+
+ /** Returns the parent. */
+ virtual QAccessibleInterface *parent() const RT_OVERRIDE
+ {
+ /* Make sure line still alive: */
+ AssertPtrReturn(line(), 0);
+
+ /* Return the parent: */
+ return QAccessible::queryAccessibleInterface(line()->parent());
+ }
+
+ /** Returns the number of children. */
+ virtual int childCount() const RT_OVERRIDE { return 0; }
+ /** Returns the child with the passed @a iIndex. */
+ virtual QAccessibleInterface *child(int /* iIndex */) const RT_OVERRIDE { return 0; }
+ /** Returns the index of the passed @a pChild. */
+ virtual int indexOfChild(const QAccessibleInterface * /* pChild */) const RT_OVERRIDE { return -1; }
+
+ /** Returns the rect. */
+ virtual QRect rect() const RT_OVERRIDE
+ {
+ /* Make sure parent still alive: */
+ AssertPtrReturn(parent(), QRect());
+
+ /* Return the parent's rect for now: */
+ /// @todo Return sub-rect.
+ return parent()->rect();
+ }
+
+ /** Returns a text for the passed @a enmTextRole. */
+ virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE
+ {
+ /* Make sure line still alive: */
+ AssertPtrReturn(line(), QString());
+
+ /* Return the description: */
+ if (enmTextRole == QAccessible::Description)
+ return UIGraphicsTextPane::tr("%1: %2", "'key: value', like 'Name: MyVM'").arg(line()->string1(), line()->string2());
+
+ /* Null-string by default: */
+ return QString();
+ }
+
+ /** Returns the role. */
+ virtual QAccessible::Role role() const RT_OVERRIDE { return QAccessible::StaticText; }
+ /** Returns the state. */
+ virtual QAccessible::State state() const RT_OVERRIDE { return QAccessible::State(); }
+
+private:
+
+ /** Returns corresponding text-table line. */
+ UITextTableLine *line() const { return qobject_cast<UITextTableLine*>(object()); }
+};
+
+UIGraphicsTextPane::UIGraphicsTextPane(QIGraphicsWidget *pParent, QPaintDevice *pPaintDevice)
+ : QIGraphicsWidget(pParent)
+ , m_pPaintDevice(pPaintDevice)
+ , m_iMargin(0)
+ , m_iSpacing(10)
+ , m_iMinimumTextColumnWidth(100)
+ , m_fMinimumSizeHintInvalidated(true)
+ , m_iMinimumTextWidth(0)
+ , m_iMinimumTextHeight(0)
+ , m_fAnchorCanBeHovered(true)
+{
+ /* Install text-table line accessibility interface factory: */
+ QAccessible::installFactory(UIAccessibilityInterfaceForUITextTableLine::pFactory);
+
+ /* We do support hover-events: */
+ setAcceptHoverEvents(true);
+}
+
+UIGraphicsTextPane::~UIGraphicsTextPane()
+{
+ /* Clear text-layouts: */
+ while (!m_leftList.isEmpty()) delete m_leftList.takeLast();
+ while (!m_rightList.isEmpty()) delete m_rightList.takeLast();
+}
+
+void UIGraphicsTextPane::setText(const UITextTable &text)
+{
+ /* Clear text: */
+ m_text.clear();
+
+ /* For each the line of the passed table: */
+ foreach (const UITextTableLine &line, text)
+ {
+ /* Lines: */
+ QString strLeftLine = line.string1();
+ QString strRightLine = line.string2();
+
+ /* If 2nd line is NOT empty: */
+ if (!strRightLine.isEmpty())
+ {
+ /* Take both lines 'as is': */
+ m_text << UITextTableLine(strLeftLine, strRightLine, parentWidget());
+ }
+ /* If 2nd line is empty: */
+ else
+ {
+ /* Parse the 1st one to sub-lines: */
+ QStringList subLines = strLeftLine.split(QRegularExpression("\\n"));
+ foreach (const QString &strSubLine, subLines)
+ m_text << UITextTableLine(strSubLine, QString(), parentWidget());
+ }
+ }
+
+ /* Update text-layout: */
+ updateTextLayout(true);
+
+ /* Update minimum size-hint: */
+ updateGeometry();
+}
+
+void UIGraphicsTextPane::setAnchorRoleRestricted(const QString &strAnchorRole, bool fRestricted)
+{
+ /* Make sure something changed: */
+ if ( (fRestricted && m_restrictedAnchorRoles.contains(strAnchorRole))
+ || (!fRestricted && !m_restrictedAnchorRoles.contains(strAnchorRole)))
+ return;
+
+ /* Apply new value: */
+ if (fRestricted)
+ m_restrictedAnchorRoles << strAnchorRole;
+ else
+ m_restrictedAnchorRoles.remove(strAnchorRole);
+
+ /* Reset hovered anchor: */
+ m_strHoveredAnchor.clear();
+ updateHoverStuff();
+}
+
+void UIGraphicsTextPane::updateTextLayout(bool fFull /* = false */)
+{
+ /* Prepare variables: */
+ QFontMetrics fm(font(), m_pPaintDevice);
+ int iMaximumTextWidth = (int)size().width() - 2 * m_iMargin - m_iSpacing;
+
+ /* Search for the maximum column widths: */
+ int iMaximumLeftColumnWidth = 0;
+ int iMaximumRightColumnWidth = 0;
+ bool fSingleColumnText = true;
+ foreach (const UITextTableLine &line, m_text)
+ {
+ bool fRightColumnPresent = !line.string2().isEmpty();
+ if (fRightColumnPresent)
+ fSingleColumnText = false;
+ QString strLeftLine = fRightColumnPresent ? line.string1() + ":" : line.string1();
+ QString strRightLine = line.string2();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ iMaximumLeftColumnWidth = qMax(iMaximumLeftColumnWidth, fm.horizontalAdvance(strLeftLine));
+ iMaximumRightColumnWidth = qMax(iMaximumRightColumnWidth, fm.horizontalAdvance(strRightLine));
+#else
+ iMaximumLeftColumnWidth = qMax(iMaximumLeftColumnWidth, fm.width(strLeftLine));
+ iMaximumRightColumnWidth = qMax(iMaximumRightColumnWidth, fm.width(strRightLine));
+#endif
+ }
+ iMaximumLeftColumnWidth += 1;
+ iMaximumRightColumnWidth += 1;
+
+ /* Calculate text attributes: */
+ int iLeftColumnWidth = 0;
+ int iRightColumnWidth = 0;
+ /* Left column only: */
+ if (fSingleColumnText)
+ {
+ /* Full update? */
+ if (fFull)
+ {
+ /* Minimum width for left column: */
+ int iMinimumLeftColumnWidth = qMin(m_iMinimumTextColumnWidth, iMaximumLeftColumnWidth);
+ /* Minimum width for whole text: */
+ m_iMinimumTextWidth = iMinimumLeftColumnWidth;
+ }
+
+ /* Current width for left column: */
+ iLeftColumnWidth = qMax(m_iMinimumTextColumnWidth, iMaximumTextWidth);
+ }
+ /* Two columns: */
+ else
+ {
+ /* Full update? */
+ if (fFull)
+ {
+ /* Minimum width for left column: */
+ int iMinimumLeftColumnWidth = iMaximumLeftColumnWidth;
+ /* Minimum width for right column: */
+ int iMinimumRightColumnWidth = qMin(m_iMinimumTextColumnWidth, iMaximumRightColumnWidth);
+ /* Minimum width for whole text: */
+ m_iMinimumTextWidth = iMinimumLeftColumnWidth + m_iSpacing + iMinimumRightColumnWidth;
+ }
+
+ /* Current width for left column: */
+ iLeftColumnWidth = iMaximumLeftColumnWidth;
+ /* Current width for right column: */
+ iRightColumnWidth = iMaximumTextWidth - iLeftColumnWidth;
+ }
+
+ /* Clear old text-layouts: */
+ while (!m_leftList.isEmpty()) delete m_leftList.takeLast();
+ while (!m_rightList.isEmpty()) delete m_rightList.takeLast();
+
+ /* Prepare new text-layouts: */
+ int iTextX = m_iMargin;
+ int iTextY = m_iMargin;
+ /* Populate text-layouts: */
+ m_iMinimumTextHeight = 0;
+ foreach (const UITextTableLine &line, m_text)
+ {
+ /* Left layout: */
+ int iLeftColumnHeight = 0;
+ if (!line.string1().isEmpty())
+ {
+ bool fRightColumnPresent = !line.string2().isEmpty();
+ m_leftList << buildTextLayout(font(), m_pPaintDevice,
+ fRightColumnPresent ? line.string1() + ":" : line.string1(),
+ iLeftColumnWidth, iLeftColumnHeight,
+ m_strHoveredAnchor);
+ m_leftList.last()->setPosition(QPointF(iTextX, iTextY));
+ }
+
+ /* Right layout: */
+ int iRightColumnHeight = 0;
+ if (!line.string2().isEmpty())
+ {
+ m_rightList << buildTextLayout(font(), m_pPaintDevice,
+ line.string2(),
+ iRightColumnWidth, iRightColumnHeight,
+ m_strHoveredAnchor);
+ m_rightList.last()->setPosition(QPointF(iTextX + iLeftColumnWidth + m_iSpacing, iTextY));
+ }
+
+ /* Maximum colum height? */
+ int iMaximumColumnHeight = qMax(iLeftColumnHeight, iRightColumnHeight);
+
+ /* Indent Y: */
+ iTextY += iMaximumColumnHeight;
+ /* Append summary text height: */
+ m_iMinimumTextHeight += iMaximumColumnHeight;
+ }
+}
+
+void UIGraphicsTextPane::updateGeometry()
+{
+ /* Discard cached minimum size-hint: */
+ m_fMinimumSizeHintInvalidated = true;
+
+ /* Call to base-class to notify layout if any: */
+ QIGraphicsWidget::updateGeometry();
+
+ /* And notify listeners which are not layouts: */
+ emit sigGeometryChanged();
+}
+
+QSizeF UIGraphicsTextPane::sizeHint(Qt::SizeHint which, const QSizeF &constraint /* = QSizeF() */) const
+{
+ /* For minimum size-hint: */
+ if (which == Qt::MinimumSize)
+ {
+ /* If minimum size-hint invalidated: */
+ if (m_fMinimumSizeHintInvalidated)
+ {
+ /* Recache minimum size-hint: */
+ m_minimumSizeHint = QSizeF(2 * m_iMargin + m_iMinimumTextWidth,
+ 2 * m_iMargin + m_iMinimumTextHeight);
+ m_fMinimumSizeHintInvalidated = false;
+ }
+ /* Return cached minimum size-hint: */
+ return m_minimumSizeHint;
+ }
+ /* Call to base-class for other size-hints: */
+ return QIGraphicsWidget::sizeHint(which, constraint);
+}
+
+void UIGraphicsTextPane::resizeEvent(QGraphicsSceneResizeEvent*)
+{
+ /* Update text-layout: */
+ updateTextLayout();
+
+ /* Update minimum size-hint: */
+ updateGeometry();
+}
+
+void UIGraphicsTextPane::hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent)
+{
+ /* Redirect to common handler: */
+ handleHoverEvent(pEvent);
+}
+
+void UIGraphicsTextPane::hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent)
+{
+ /* Redirect to common handler: */
+ handleHoverEvent(pEvent);
+}
+
+void UIGraphicsTextPane::handleHoverEvent(QGraphicsSceneHoverEvent *pEvent)
+{
+ /* Ignore if anchor can't be hovered: */
+ if (!m_fAnchorCanBeHovered)
+ return;
+
+ /* Prepare variables: */
+ QPoint mousePosition = pEvent->pos().toPoint();
+ QString strHoveredAnchor;
+ QString strHoveredAnchorRole;
+
+ /* Search for hovered-anchor in the left list: */
+ strHoveredAnchor = searchForHoveredAnchor(m_pPaintDevice, m_leftList, mousePosition);
+ strHoveredAnchorRole = strHoveredAnchor.section(',', 0, 0);
+ if (!strHoveredAnchor.isNull() && !m_restrictedAnchorRoles.contains(strHoveredAnchorRole))
+ {
+ m_strHoveredAnchor = strHoveredAnchor;
+ return updateHoverStuff();
+ }
+
+ /* Then search for hovered-anchor in the right one: */
+ strHoveredAnchor = searchForHoveredAnchor(m_pPaintDevice, m_rightList, mousePosition);
+ strHoveredAnchorRole = strHoveredAnchor.section(',', 0, 0);
+ if (!strHoveredAnchor.isNull() && !m_restrictedAnchorRoles.contains(strHoveredAnchorRole))
+ {
+ m_strHoveredAnchor = strHoveredAnchor;
+ return updateHoverStuff();
+ }
+
+ /* Finally clear it for good: */
+ if (!m_strHoveredAnchor.isNull())
+ {
+ m_strHoveredAnchor.clear();
+ return updateHoverStuff();
+ }
+}
+
+void UIGraphicsTextPane::updateHoverStuff()
+{
+ /* Update mouse-cursor: */
+ if (m_strHoveredAnchor.isNull())
+ UICursor::unsetCursor(this);
+ else
+ UICursor::setCursor(this, Qt::PointingHandCursor);
+
+ /* Update text-layout: */
+ updateTextLayout();
+
+ /* Update tool-tip: */
+ const QString strType = m_strHoveredAnchor.section(',', 0, 0);
+ if (strType == "#attach" || strType == "#mount")
+ setToolTip(m_strHoveredAnchor.section(',', -1));
+ else
+ setToolTip(QString());
+
+ /* Update text-pane: */
+ update();
+}
+
+void UIGraphicsTextPane::mousePressEvent(QGraphicsSceneMouseEvent*)
+{
+ /* Make sure some anchor hovered: */
+ if (m_strHoveredAnchor.isNull())
+ return;
+
+ /* Restrict anchor hovering: */
+ m_fAnchorCanBeHovered = false;
+
+ /* Cache clicked anchor: */
+ QString strClickedAnchor = m_strHoveredAnchor;
+
+ /* Clear hovered anchor: */
+ m_strHoveredAnchor.clear();
+ updateHoverStuff();
+
+ /* Notify listeners about anchor clicked: */
+ emit sigAnchorClicked(strClickedAnchor);
+
+ /* Allow anchor hovering again: */
+ m_fAnchorCanBeHovered = true;
+}
+
+void UIGraphicsTextPane::paint(QPainter *pPainter, const QStyleOptionGraphicsItem*, QWidget*)
+{
+ /* Draw all the text-layouts: */
+ foreach (QTextLayout *pTextLayout, m_leftList)
+ pTextLayout->draw(pPainter, QPoint(0, 0));
+ foreach (QTextLayout *pTextLayout, m_rightList)
+ pTextLayout->draw(pPainter, QPoint(0, 0));
+}
+
+/* static */
+QTextLayout* UIGraphicsTextPane::buildTextLayout(const QFont &font, QPaintDevice *pPaintDevice,
+ const QString &strText, int iWidth, int &iHeight,
+ const QString &strHoveredAnchor)
+{
+ /* Prepare variables: */
+ QFontMetrics fm(font, pPaintDevice);
+ int iLeading = fm.leading();
+
+ /* Parse incoming string with UIRichTextString capabilities: */
+ //printf("Text: {%s}\n", strText.toUtf8().constData());
+ UIRichTextString ms(strText);
+ ms.setHoveredAnchor(strHoveredAnchor);
+
+ /* Create layout; */
+ QTextLayout *pTextLayout = new QTextLayout(ms.toString(), font, pPaintDevice);
+ pTextLayout->setFormats(ms.formatRanges());
+
+ /* Configure layout: */
+ QTextOption textOption;
+ textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ pTextLayout->setTextOption(textOption);
+
+ /* Build layout: */
+ pTextLayout->beginLayout();
+ while (1)
+ {
+ QTextLine line = pTextLayout->createLine();
+ if (!line.isValid())
+ break;
+
+ line.setLineWidth(iWidth);
+ iHeight += iLeading;
+ line.setPosition(QPointF(0, iHeight));
+ iHeight += (int)line.height();
+ }
+ pTextLayout->endLayout();
+
+ /* Return layout: */
+ return pTextLayout;
+}
+
+/* static */
+QString UIGraphicsTextPane::searchForHoveredAnchor(QPaintDevice *pPaintDevice, const QList<QTextLayout*> &list, const QPoint &mousePosition)
+{
+ /* Analyze passed text-layouts: */
+ foreach (QTextLayout *pTextLayout, list)
+ {
+ /* Prepare variables: */
+ QFontMetrics fm(pTextLayout->font(), pPaintDevice);
+
+ /* Text-layout attributes: */
+ const QPoint layoutPosition = pTextLayout->position().toPoint();
+ const QString strLayoutText = pTextLayout->text();
+
+ /* Enumerate format ranges: */
+ foreach (const QTextLayout::FormatRange &range, pTextLayout->formats())
+ {
+ /* Skip unrelated formats: */
+ if (!range.format.isAnchor())
+ continue;
+
+ /* Parse 'anchor' format: */
+ const int iStart = range.start;
+ const int iLength = range.length;
+ QRegion formatRegion;
+ for (int iTextPosition = iStart; iTextPosition < iStart + iLength; ++iTextPosition)
+ {
+ QTextLine layoutLine = pTextLayout->lineForTextPosition(iTextPosition);
+ QPoint linePosition = layoutLine.position().toPoint();
+ int iSymbolX = (int)layoutLine.cursorToX(iTextPosition);
+ QRect symbolRect = QRect(layoutPosition.x() + linePosition.x() + iSymbolX,
+ layoutPosition.y() + linePosition.y(),
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ fm.horizontalAdvance(strLayoutText[iTextPosition]) + 1,
+#else
+ fm.width(strLayoutText[iTextPosition]) + 1,
+#endif
+ fm.height());
+ formatRegion += symbolRect;
+ }
+
+ /* Is that something we looking for? */
+ if (formatRegion.contains(mousePosition))
+ return range.format.anchorHref();
+ }
+ }
+
+ /* Null string by default: */
+ return QString();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.h b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.h
new file mode 100644
index 00000000..4eedd82a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.h
@@ -0,0 +1,142 @@
+/* $Id: UIGraphicsTextPane.h $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsTextPane class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsTextPane_h
+#define FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsTextPane_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIGraphicsWidget.h"
+#include "UITextTable.h"
+
+/* Forward declarations: */
+class QTextLayout;
+
+
+/** QIGraphicsWidget reimplementation to draw QTextLayout content. */
+class UIGraphicsTextPane : public QIGraphicsWidget
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies listeners about size-hint changes. */
+ void sigGeometryChanged();
+
+ /** Notifies listeners about anchor clicked. */
+ void sigAnchorClicked(const QString &strAnchor);
+
+public:
+
+ /** Graphics text-pane constructor. */
+ UIGraphicsTextPane(QIGraphicsWidget *pParent, QPaintDevice *pPaintDevice);
+ /** Graphics text-pane destructor. */
+ ~UIGraphicsTextPane();
+
+ /** Returns whether contained text is empty. */
+ bool isEmpty() const { return m_text.isEmpty(); }
+ /** Returns contained text. */
+ UITextTable &text() { return m_text; }
+ /** Defines contained text. */
+ void setText(const UITextTable &text);
+
+ /** Defines whether passed @a strAnchorRole is @a fRestricted. */
+ void setAnchorRoleRestricted(const QString &strAnchorRole, bool fRestricted);
+
+private:
+
+ /** Update text-layout. */
+ void updateTextLayout(bool fFull = false);
+
+ /** Notifies listeners about size-hint changes. */
+ void updateGeometry();
+ /** Returns the size-hint to constrain the content. */
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+ /** This event handler is delivered after the widget has been resized. */
+ void resizeEvent(QGraphicsSceneResizeEvent *pEvent);
+
+ /** This event handler called when mouse hovers widget. */
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent);
+ /** This event handler called when mouse leaves widget. */
+ void hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent);
+ /** Summarize two hover-event handlers above. */
+ void handleHoverEvent(QGraphicsSceneHoverEvent *pEvent);
+ /** Update hover stuff. */
+ void updateHoverStuff();
+
+ /** This event handler called when mouse press widget. */
+ void mousePressEvent(QGraphicsSceneMouseEvent *pEvent);
+
+ /** Paints the contents in local coordinates. */
+ void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOption, QWidget *pWidget = 0);
+
+ /** Builds new text-layout. */
+ static QTextLayout* buildTextLayout(const QFont &font, QPaintDevice *pPaintDevice,
+ const QString &strText, int iWidth, int &iHeight,
+ const QString &strHoveredAnchor);
+
+ /** Search for hovered anchor in passed text-layout @a list. */
+ static QString searchForHoveredAnchor(QPaintDevice *pPaintDevice, const QList<QTextLayout*> &list, const QPoint &mousePosition);
+
+ /** Paint-device to scale to. */
+ QPaintDevice *m_pPaintDevice;
+
+ /** Margin. */
+ const int m_iMargin;
+ /** Spacing. */
+ const int m_iSpacing;
+ /** Minimum text-column width: */
+ const int m_iMinimumTextColumnWidth;
+
+ /** Minimum size-hint invalidation flag. */
+ mutable bool m_fMinimumSizeHintInvalidated;
+ /** Minimum size-hint cache. */
+ mutable QSizeF m_minimumSizeHint;
+ /** Minimum text-width. */
+ int m_iMinimumTextWidth;
+ /** Minimum text-height. */
+ int m_iMinimumTextHeight;
+
+ /** Contained text. */
+ UITextTable m_text;
+ /** Left text-layout list. */
+ QList<QTextLayout*> m_leftList;
+ /** Right text-layout list. */
+ QList<QTextLayout*> m_rightList;
+
+ /** Holds whether anchor can be hovered. */
+ bool m_fAnchorCanBeHovered;
+ /** Holds restricted anchor roles. */
+ QSet<QString> m_restrictedAnchorRoles;
+ /** Holds currently hovered anchor. */
+ QString m_strHoveredAnchor;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsTextPane_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsToolBar.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsToolBar.cpp
new file mode 100644
index 00000000..e54012a4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsToolBar.cpp
@@ -0,0 +1,119 @@
+/* $Id: UIGraphicsToolBar.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsToolBar class definition.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UIGraphicsToolBar.h"
+#include "UIGraphicsButton.h"
+
+
+UIGraphicsToolBar::UIGraphicsToolBar(QIGraphicsWidget *pParent, int iRows, int iColumns)
+ : QIGraphicsWidget(pParent)
+ , m_iMargin(3)
+ , m_iRows(iRows)
+ , m_iColumns(iColumns)
+{
+}
+
+int UIGraphicsToolBar::toolBarMargin() const
+{
+ return m_iMargin;
+}
+
+void UIGraphicsToolBar::setToolBarMargin(int iMargin)
+{
+ m_iMargin = iMargin;
+}
+
+void UIGraphicsToolBar::insertItem(UIGraphicsButton *pButton, int iRow, int iColumn)
+{
+ UIGraphicsToolBarIndex key = qMakePair(iRow, iColumn);
+ m_buttons.insert(key, pButton);
+}
+
+void UIGraphicsToolBar::updateLayout()
+{
+ /* For all the rows: */
+ for (int iRow = 0; iRow < m_iRows; ++iRow)
+ {
+ /* For all the columns: */
+ for (int iColumn = 0; iColumn < m_iColumns; ++iColumn)
+ {
+ /* Generate key: */
+ UIGraphicsToolBarIndex key = qMakePair(iRow, iColumn);
+ /* Check if key present: */
+ if (m_buttons.contains(key))
+ {
+ /* Get corresponding button: */
+ UIGraphicsButton *pButton = m_buttons.value(key);
+ QSize minimumSize = pButton->minimumSizeHint().toSize();
+ pButton->setPos(toolBarMargin() + iColumn * minimumSize.width(),
+ toolBarMargin() + iRow * minimumSize.height());
+ }
+ }
+ }
+}
+
+QSizeF UIGraphicsToolBar::sizeHint(Qt::SizeHint which, const QSizeF &constraint /* = QSizeF() */) const
+{
+ /* If Qt::MinimumSize hint requested: */
+ if (which == Qt::MinimumSize)
+ {
+ /* Prepare variables: */
+ int iProposedWidth = 2 * toolBarMargin();
+ int iProposedHeight = 2 * toolBarMargin();
+ /* Search for any button: */
+ UIGraphicsButton *pButton = 0;
+ for (int iRow = 0; !pButton && iRow < m_iRows; ++iRow)
+ {
+ /* For all the columns: */
+ for (int iColumn = 0; !pButton && iColumn < m_iColumns; ++iColumn)
+ {
+ /* Generate key: */
+ UIGraphicsToolBarIndex key = qMakePair(iRow, iColumn);
+ /* Check if key present: */
+ if (m_buttons.contains(key))
+ {
+ /* Get corresponding button: */
+ pButton = m_buttons.value(key);
+ }
+ }
+ }
+ /* If any button found: */
+ if (pButton)
+ {
+ /* Get button minimum-size: */
+ QSize minimumSize = pButton->minimumSizeHint().toSize();
+ iProposedWidth += m_iColumns * minimumSize.width();
+ iProposedHeight += m_iRows * minimumSize.height();
+ }
+ /* Return result: */
+ return QSizeF(iProposedWidth, iProposedHeight);
+ }
+ /* Else call to base-class: */
+ return QIGraphicsWidget::sizeHint(which, constraint);
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsToolBar.h b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsToolBar.h
new file mode 100644
index 00000000..8fa92500
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsToolBar.h
@@ -0,0 +1,78 @@
+/* $Id: UIGraphicsToolBar.h $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsToolBar class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsToolBar_h
+#define FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsToolBar_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIGraphicsWidget.h"
+
+/* Forward declarations: */
+class UIGraphicsButton;
+
+/* Graphics tool-bar: */
+class UIGraphicsToolBar : public QIGraphicsWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /* Constructor: */
+ UIGraphicsToolBar(QIGraphicsWidget *pParent, int iRows, int iColumns);
+
+ /* API: Margin stuff: */
+ int toolBarMargin() const;
+ void setToolBarMargin(int iMargin);
+
+ /* API: Children stuff: */
+ void insertItem(UIGraphicsButton *pButton, int iRow, int iColumn);
+
+ /* API: Layout stuff: */
+ void updateLayout();
+
+protected:
+
+ /* Typedefs: */
+ typedef QPair<int, int> UIGraphicsToolBarIndex;
+
+ /* Helpers: Layout stuff: */
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+private:
+
+ /* Variables: */
+ int m_iMargin;
+ int m_iRows;
+ int m_iColumns;
+ QMap<UIGraphicsToolBarIndex, UIGraphicsButton*> m_buttons;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsToolBar_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsZoomButton.cpp b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsZoomButton.cpp
new file mode 100644
index 00000000..64491833
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsZoomButton.cpp
@@ -0,0 +1,188 @@
+/* $Id: UIGraphicsZoomButton.cpp $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsZoomButton class definition.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStateMachine>
+#include <QSignalTransition>
+#include <QPropertyAnimation>
+#include <QPainter>
+#include <QStyleOptionGraphicsItem>
+
+/* GUI includes: */
+#include "UIGraphicsZoomButton.h"
+
+
+UIGraphicsZoomButton::UIGraphicsZoomButton(QIGraphicsWidget *pParent, const QIcon &icon, int iDirection)
+ : UIGraphicsButton(pParent, icon)
+ , m_iIndent(4)
+ , m_iDirection(iDirection)
+ , m_iAnimationDuration(200)
+ , m_pStateMachine(0)
+ , m_pForwardAnimation(0)
+ , m_pBackwardAnimation(0)
+ , m_fStateDefault(true)
+{
+ /* Setup: */
+ setAcceptHoverEvents(true);
+
+ /* Create state machine: */
+ m_pStateMachine = new QStateMachine(this);
+
+ /* Create 'default' state: */
+ QState *pStateDefault = new QState(m_pStateMachine);
+ pStateDefault->assignProperty(this, "stateDefault", true);
+
+ /* Create 'zoomed' state: */
+ QState *pStateZoomed = new QState(m_pStateMachine);
+ pStateZoomed->assignProperty(this, "stateDefault", false);
+
+ /* Initial state is 'default': */
+ m_pStateMachine->setInitialState(pStateDefault);
+
+ /* Zoom animation: */
+ m_pForwardAnimation = new QPropertyAnimation(this, "geometry", this);
+ m_pForwardAnimation->setDuration(m_iAnimationDuration);
+
+ /* Unzoom animation: */
+ m_pBackwardAnimation = new QPropertyAnimation(this, "geometry", this);
+ m_pBackwardAnimation->setDuration(m_iAnimationDuration);
+
+ /* Add state transitions: */
+ QSignalTransition *pDefaultToZoomed = pStateDefault->addTransition(this, SIGNAL(sigHoverEnter()), pStateZoomed);
+ pDefaultToZoomed->addAnimation(m_pForwardAnimation);
+
+ QSignalTransition *pZoomedToDefault = pStateZoomed->addTransition(this, SIGNAL(sigHoverLeave()), pStateDefault);
+ pZoomedToDefault->addAnimation(m_pBackwardAnimation);
+
+ /* Start state-machine: */
+ m_pStateMachine->start();
+}
+
+int UIGraphicsZoomButton::indent() const
+{
+ return m_iIndent;
+}
+
+void UIGraphicsZoomButton::setIndent(int iIndent)
+{
+ m_iIndent = iIndent;
+}
+
+void UIGraphicsZoomButton::updateAnimation()
+{
+ QRectF oldRect = geometry();
+ QRectF newRect = oldRect;
+ if (m_iDirection & UIGraphicsZoomDirection_Top)
+ newRect.setTop(newRect.top() - m_iIndent);
+ if (m_iDirection & UIGraphicsZoomDirection_Bottom)
+ newRect.setBottom(newRect.bottom() + m_iIndent);
+ if (m_iDirection & UIGraphicsZoomDirection_Left)
+ newRect.setLeft(newRect.left() - m_iIndent);
+ if (m_iDirection & UIGraphicsZoomDirection_Right)
+ newRect.setRight(newRect.right() + m_iIndent);
+ if (!(m_iDirection & UIGraphicsZoomDirection_Left) &&
+ !(m_iDirection & UIGraphicsZoomDirection_Right))
+ {
+ newRect.setLeft(newRect.left() - m_iIndent / 2);
+ newRect.setRight(newRect.right() + m_iIndent / 2);
+ }
+ if (!(m_iDirection & UIGraphicsZoomDirection_Top) &&
+ !(m_iDirection & UIGraphicsZoomDirection_Bottom))
+ {
+ newRect.setTop(newRect.top() - m_iIndent / 2);
+ newRect.setBottom(newRect.bottom() + m_iIndent / 2);
+ }
+ m_pForwardAnimation->setStartValue(oldRect);
+ m_pForwardAnimation->setEndValue(newRect);
+ m_pBackwardAnimation->setStartValue(newRect);
+ m_pBackwardAnimation->setEndValue(oldRect);
+}
+
+QVariant UIGraphicsZoomButton::data(int iKey) const
+{
+ /* Known key? */
+ switch (iKey)
+ {
+ case GraphicsButton_Margin: return 1;
+ default: break;
+ }
+ /* Call to base-class: */
+ return UIGraphicsButton::data(iKey);
+}
+
+void UIGraphicsZoomButton::hoverEnterEvent(QGraphicsSceneHoverEvent*)
+{
+ emit sigHoverEnter();
+}
+
+void UIGraphicsZoomButton::hoverLeaveEvent(QGraphicsSceneHoverEvent*)
+{
+ emit sigHoverLeave();
+}
+
+void UIGraphicsZoomButton::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOption, QWidget*)
+{
+ /* Save painter: */
+ pPainter->save();
+
+ /* Prepare variables: */
+ int iMargin = data(GraphicsButton_Margin).toInt();
+ QRect paintRect = pOption->rect;
+ paintRect.setTopLeft(paintRect.topLeft() + QPoint(iMargin, iMargin));
+ paintRect.setBottomRight(paintRect.bottomRight() - QPoint(iMargin, iMargin));
+ QIcon icon = data(GraphicsButton_Icon).value<QIcon>();
+ QSize iconSize = data(GraphicsButton_IconSize).toSize();
+
+ /* Make painter beauty: */
+ pPainter->setRenderHint(QPainter::SmoothPixmapTransform);
+
+ /* Draw pixmap: */
+ pPainter->drawPixmap(/* Pixmap rectangle: */
+ paintRect,
+ /* Pixmap size: */
+ icon.pixmap(iconSize));
+
+ /* Restore painter: */
+ pPainter->restore();
+}
+
+bool UIGraphicsZoomButton::isAnimationRunning() const
+{
+ return m_pForwardAnimation->state() == QAbstractAnimation::Running ||
+ m_pBackwardAnimation->state() == QAbstractAnimation::Running;
+}
+
+bool UIGraphicsZoomButton::stateDefault() const
+{
+ return m_fStateDefault;
+}
+
+void UIGraphicsZoomButton::setStateDefault(bool fStateDefault)
+{
+ m_fStateDefault = fStateDefault;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsZoomButton.h b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsZoomButton.h
new file mode 100644
index 00000000..ce7277e4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsZoomButton.h
@@ -0,0 +1,109 @@
+/* $Id: UIGraphicsZoomButton.h $ */
+/** @file
+ * VBox Qt GUI - UIGraphicsZoomButton class declaration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsZoomButton_h
+#define FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsZoomButton_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIGraphicsButton.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+/* Forward declarations: */
+class QStateMachine;
+class QPropertyAnimation;
+
+/* Zoom direction: */
+enum UIGraphicsZoomDirection
+{
+ UIGraphicsZoomDirection_Top = RT_BIT(0),
+ UIGraphicsZoomDirection_Bottom = RT_BIT(1),
+ UIGraphicsZoomDirection_Left = RT_BIT(2),
+ UIGraphicsZoomDirection_Right = RT_BIT(3)
+};
+
+/* Zoom graphics-button representation: */
+class UIGraphicsZoomButton : public UIGraphicsButton
+{
+ Q_OBJECT;
+ Q_PROPERTY(bool stateDefault READ stateDefault WRITE setStateDefault);
+
+signals:
+
+ /* Notify listeners about hover events: */
+ void sigHoverEnter();
+ void sigHoverLeave();
+
+public:
+
+ /* Constructor: */
+ UIGraphicsZoomButton(QIGraphicsWidget *pParent, const QIcon &icon, int iDirection);
+
+ /* API: Zoom stuff: */
+ int indent() const;
+ void setIndent(int iIndent);
+
+ /* API: Animation stuff: */
+ void updateAnimation();
+
+protected:
+
+ /* Data provider: */
+ QVariant data(int iKey) const;
+
+ /* Handler: Mouse hover: */
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *pEvent);
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent);
+
+ /* Paint stuff: */
+ void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOption, QWidget *pWidget = 0);
+
+private:
+
+ /* Animation stuff: */
+ bool isAnimationRunning() const;
+
+ /* Property stuff: */
+ bool stateDefault() const;
+ void setStateDefault(bool fStateDefault);
+
+ /* Variables: */
+ int m_iIndent;
+ int m_iDirection;
+ int m_iAnimationDuration;
+ QStateMachine *m_pStateMachine;
+ QPropertyAnimation *m_pForwardAnimation;
+ QPropertyAnimation *m_pBackwardAnimation;
+ bool m_fStateDefault;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_widgets_graphics_UIGraphicsZoomButton_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.cpp
new file mode 100644
index 00000000..a4431ec2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.cpp
@@ -0,0 +1,760 @@
+/* $Id: UINativeWizard.cpp $ */
+/** @file
+ * VBox Qt GUI - UINativeWizard class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPainter>
+#include <QPushButton>
+#include <QStackedWidget>
+#include <QStyle>
+#include <QVBoxLayout>
+#include <QWindow>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINativeWizard.h"
+#include "UINativeWizardPage.h"
+#include "UINotificationCenter.h"
+
+
+#ifdef VBOX_WS_MAC
+UIFrame::UIFrame(QWidget *pParent)
+ : QWidget(pParent)
+{
+}
+
+void UIFrame::paintEvent(QPaintEvent *pEvent)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pEvent);
+
+ /* Prepare painter: */
+ QPainter painter(this);
+
+ /* Limit painting with incoming rectangle: */
+ painter.setClipRect(pEvent->rect());
+
+ /* Check whether we should use Active or Inactive palette: */
+ const bool fActive = parentWidget() && parentWidget()->isActiveWindow();
+
+ /* Paint background: */
+ QColor backgroundColor = QGuiApplication::palette().color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window);
+ backgroundColor.setAlpha(100);
+ painter.setPen(backgroundColor);
+ painter.setBrush(backgroundColor);
+ painter.drawRect(rect());
+
+ /* Paint borders: */
+ painter.setPen(QGuiApplication::palette().color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).darker(130));
+ QLine line1(0, 0, rect().width() - 1, 0);
+ QLine line2(rect().width() - 1, 0, rect().width() - 1, rect().height() - 1);
+ QLine line3(rect().width() - 1, rect().height() - 1, 0, rect().height() - 1);
+ QLine line4(0, rect().height() - 1, 0, 0);
+ painter.drawLine(line1);
+ painter.drawLine(line2);
+ painter.drawLine(line3);
+ painter.drawLine(line4);
+}
+#endif /* VBOX_WS_MAC */
+
+
+UINativeWizard::UINativeWizard(QWidget *pParent,
+ WizardType enmType,
+ WizardMode enmMode /* = WizardMode_Auto */,
+ const QString &strHelpTag /* = QString() */)
+ : QIWithRetranslateUI<QDialog>(pParent)
+ , m_enmType(enmType)
+ , m_enmMode(enmMode == WizardMode_Auto ? gEDataManager->modeForWizardType(m_enmType) : enmMode)
+ , m_strHelpHashtag(strHelpTag)
+ , m_iLastIndex(-1)
+ , m_pLabelPixmap(0)
+ , m_pLayoutRight(0)
+ , m_pLabelPageTitle(0)
+ , m_pWidgetStack(0)
+ , m_pNotificationCenter(0)
+{
+ prepare();
+}
+
+UINativeWizard::~UINativeWizard()
+{
+ cleanup();
+}
+
+UINotificationCenter *UINativeWizard::notificationCenter() const
+{
+ return m_pNotificationCenter;
+}
+
+bool UINativeWizard::handleNotificationProgressNow(UINotificationProgress *pProgress)
+{
+ wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ const bool fResult = m_pNotificationCenter->handleNow(pProgress);
+ wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ return fResult;
+}
+
+QPushButton *UINativeWizard::wizardButton(const WizardButtonType &enmType) const
+{
+ return m_buttons.value(enmType);
+}
+
+int UINativeWizard::exec()
+{
+ /* Init wizard: */
+ init();
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI<QDialog>::exec();
+}
+
+void UINativeWizard::setPixmapName(const QString &strName)
+{
+ m_strPixmapName = strName;
+}
+
+bool UINativeWizard::isPageVisible(int iIndex) const
+{
+ return !m_invisiblePages.contains(iIndex);
+}
+
+void UINativeWizard::setPageVisible(int iIndex, bool fVisible)
+{
+ AssertMsgReturnVoid(iIndex || fVisible, ("Can't hide 1st wizard page!\n"));
+ if (fVisible)
+ m_invisiblePages.remove(iIndex);
+ else
+ m_invisiblePages.insert(iIndex);
+ /* Update the button labels since the last visible page might have changed. Thus 'Next' <-> 'Finish' might be needed: */
+ retranslateUi();
+}
+
+int UINativeWizard::addPage(UINativeWizardPage *pPage)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pPage, -1);
+ AssertPtrReturn(pPage->layout(), -1);
+
+ /* Adjust page layout: */
+ const int iL = 0;
+ const int iT = 0;
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
+ const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
+ pPage->layout()->setContentsMargins(iL, iT, iR, iB);
+
+ /* Add page to wizard's stack: */
+ m_pWidgetStack->blockSignals(true);
+ const int iIndex = m_pWidgetStack->addWidget(pPage);
+ m_pWidgetStack->blockSignals(false);
+
+ /* Make sure wizard is aware of page validity changes: */
+ connect(pPage, &UINativeWizardPage::completeChanged,
+ this, &UINativeWizard::sltCompleteChanged);
+
+ /* Returns added page index: */
+ return iIndex;
+}
+
+void UINativeWizard::retranslateUi()
+{
+ /* Translate Help button: */
+ QPushButton *pButtonHelp = wizardButton(WizardButtonType_Help);
+ if (pButtonHelp)
+ {
+ pButtonHelp->setText(tr("&Help"));
+ pButtonHelp->setToolTip(tr("Open corresponding Help topic."));
+ }
+
+ /* Translate basic/expert button: */
+ QPushButton *pButtonExpert = wizardButton(WizardButtonType_Expert);
+ AssertMsgReturnVoid(pButtonExpert, ("No Expert wizard button found!\n"));
+ switch (m_enmMode)
+ {
+ case WizardMode_Basic:
+ pButtonExpert->setText(tr("&Expert Mode"));
+ pButtonExpert->setToolTip(tr("Switch to the Expert Mode, "
+ "a one-page dialog for experienced users."));
+ break;
+ case WizardMode_Expert:
+ pButtonExpert->setText(tr("&Guided Mode"));
+ pButtonExpert->setToolTip(tr("Switch to the Guided Mode, "
+ "a step-by-step dialog with detailed explanations."));
+ break;
+ default:
+ AssertMsgFailed(("Invalid wizard mode: %d", m_enmMode));
+ break;
+ }
+
+ /* Translate Back button: */
+ QPushButton *pButtonBack = wizardButton(WizardButtonType_Back);
+ AssertMsgReturnVoid(pButtonBack, ("No Back wizard button found!\n"));
+ pButtonBack->setText(tr("&Back"));
+ pButtonBack->setToolTip(tr("Go to previous wizard page."));
+
+ /* Translate Next button: */
+ QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
+ AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
+ if (!isLastVisiblePage(m_pWidgetStack->currentIndex()))
+ {
+ pButtonNext->setText(tr("&Next"));
+ pButtonNext->setToolTip(tr("Go to next wizard page."));
+ }
+ else
+ {
+ pButtonNext->setText(tr("&Finish"));
+ pButtonNext->setToolTip(tr("Commit all wizard data."));
+ }
+
+ /* Translate Cancel button: */
+ QPushButton *pButtonCancel = wizardButton(WizardButtonType_Cancel);
+ AssertMsgReturnVoid(pButtonCancel, ("No Cancel wizard button found!\n"));
+ pButtonCancel->setText(tr("&Cancel"));
+ pButtonCancel->setToolTip(tr("Cancel wizard execution."));
+}
+
+void UINativeWizard::sltCurrentIndexChanged(int iIndex /* = -1 */)
+{
+ /* Update translation: */
+ retranslateUi();
+
+ /* Sanity check: */
+ AssertPtrReturnVoid(m_pWidgetStack);
+
+ /* -1 means current one page: */
+ if (iIndex == -1)
+ iIndex = m_pWidgetStack->currentIndex();
+
+ /* Hide/show Expert button (hidden by default): */
+ bool fIsExpertButtonAvailable = false;
+ /* Show Expert button for 1st page: */
+ if (iIndex == 0)
+ fIsExpertButtonAvailable = true;
+ /* Hide/show Expert button finally: */
+ QPushButton *pButtonExpert = wizardButton(WizardButtonType_Expert);
+ AssertMsgReturnVoid(pButtonExpert, ("No Expert wizard button found!\n"));
+ pButtonExpert->setVisible(fIsExpertButtonAvailable);
+
+ /* Disable/enable Back button: */
+ QPushButton *pButtonBack = wizardButton(WizardButtonType_Back);
+ AssertMsgReturnVoid(pButtonBack, ("No Back wizard button found!\n"));
+ pButtonBack->setEnabled(iIndex > 0);
+
+ /* Initialize corresponding page: */
+ UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(m_pWidgetStack->widget(iIndex));
+ AssertPtrReturnVoid(pPage);
+ m_pLabelPageTitle->setText(pPage->title());
+ if (iIndex > m_iLastIndex)
+ pPage->initializePage();
+
+ /* Disable/enable Next button: */
+ QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
+ AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
+ pButtonNext->setEnabled(pPage->isComplete());
+
+ /* Update last index: */
+ m_iLastIndex = iIndex;
+}
+
+void UINativeWizard::sltCompleteChanged()
+{
+ /* Make sure sender is current widget: */
+ QWidget *pSender = qobject_cast<QWidget*>(sender());
+ if (pSender != m_pWidgetStack->currentWidget())
+ return;
+
+ /* Allow Next button only if current page is complete: */
+ UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(pSender);
+ QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
+ AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
+ pButtonNext->setEnabled(pPage->isComplete());
+}
+
+void UINativeWizard::sltExpert()
+{
+ /* Toggle mode: */
+ switch (m_enmMode)
+ {
+ case WizardMode_Basic: m_enmMode = WizardMode_Expert; break;
+ case WizardMode_Expert: m_enmMode = WizardMode_Basic; break;
+ default: AssertMsgFailed(("Invalid mode: %d", m_enmMode)); break;
+ }
+ gEDataManager->setModeForWizardType(m_enmType, m_enmMode);
+
+ /* Reinit everything: */
+ deinit();
+ init();
+}
+
+void UINativeWizard::sltPrevious()
+{
+ /* For all allowed pages besides the 1st one we going backward: */
+ bool fPreviousFound = false;
+ int iIteratedIndex = m_pWidgetStack->currentIndex();
+ while (!fPreviousFound && iIteratedIndex > 0)
+ if (isPageVisible(--iIteratedIndex))
+ fPreviousFound = true;
+ if (fPreviousFound)
+ m_pWidgetStack->setCurrentIndex(iIteratedIndex);
+}
+
+void UINativeWizard::sltNext()
+{
+ /* Look for Next button: */
+ QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
+ AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
+
+ /* Validate page before going forward: */
+ AssertReturnVoid(m_pWidgetStack->currentIndex() < m_pWidgetStack->count());
+ UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(m_pWidgetStack->currentWidget());
+ AssertPtrReturnVoid(pPage);
+ pButtonNext->setEnabled(false);
+ const bool fIsPageValid = pPage->validatePage();
+ pButtonNext->setEnabled(true);
+ if (!fIsPageValid)
+ return;
+
+ /* For all allowed pages besides the last one we going forward: */
+ bool fNextFound = false;
+ int iIteratedIndex = m_pWidgetStack->currentIndex();
+ while (!fNextFound && iIteratedIndex < m_pWidgetStack->count() - 1)
+ if (isPageVisible(++iIteratedIndex))
+ fNextFound = true;
+ if (fNextFound)
+ m_pWidgetStack->setCurrentIndex(iIteratedIndex);
+ /* For last one we just accept the wizard: */
+ else
+ accept();
+}
+
+void UINativeWizard::prepare()
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* No need for margins and spacings between sub-layouts: */
+ pLayoutMain->setContentsMargins(0, 0, 0, 0);
+ pLayoutMain->setSpacing(0);
+
+ /* Prepare upper layout: */
+ QHBoxLayout *pLayoutUpper = new QHBoxLayout;
+ if (pLayoutUpper)
+ {
+#ifdef VBOX_WS_MAC
+ /* No need for bottom margin on macOS, reseting others to default: */
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
+ const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
+ pLayoutUpper->setContentsMargins(iL, iT, iR, 0);
+#endif /* VBOX_WS_MAC */
+ /* Reset spacing to default, it was flawed by parent inheritance: */
+ const int iSpacing = qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
+ pLayoutUpper->setSpacing(iSpacing);
+
+ /* Prepare pixmap label: */
+ m_pLabelPixmap = new QLabel(this);
+ if (m_pLabelPixmap)
+ {
+ m_pLabelPixmap->setAlignment(Qt::AlignTop);
+#ifdef VBOX_WS_MAC
+ /* On macOS this label contains background, which isn't a part of layout, moving manually: */
+ m_pLabelPixmap->move(0, 0);
+ /* Spacer to make look&feel native on macOS: */
+ QSpacerItem *pSpacer = new QSpacerItem(200, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ pLayoutUpper->addItem(pSpacer);
+#else /* !VBOX_WS_MAC */
+ /* Just add label into layout on other platforms: */
+ pLayoutUpper->addWidget(m_pLabelPixmap);
+#endif /* !VBOX_WS_MAC */
+ }
+
+ /* Prepare right layout: */
+ m_pLayoutRight = new QVBoxLayout;
+ if (m_pLayoutRight)
+ {
+ /* Prepare page title label: */
+ m_pLabelPageTitle = new QLabel(this);
+ if (m_pLabelPageTitle)
+ {
+ /* Title should have big/fat font: */
+ QFont labelFont = m_pLabelPageTitle->font();
+ labelFont.setBold(true);
+ labelFont.setPointSize(labelFont.pointSize() + 4);
+ m_pLabelPageTitle->setFont(labelFont);
+
+ m_pLayoutRight->addWidget(m_pLabelPageTitle);
+ }
+
+#ifdef VBOX_WS_MAC
+ /* Prepare frame around widget-stack on macOS for nativity purposes: */
+ UIFrame *pFrame = new UIFrame(this);
+ if (pFrame)
+ {
+ /* Prepare frame layout: */
+ QVBoxLayout *pLayoutFrame = new QVBoxLayout(pFrame);
+ if (pLayoutFrame)
+ {
+ /* Prepare widget-stack: */
+ m_pWidgetStack = new QStackedWidget(pFrame);
+ if (m_pWidgetStack)
+ {
+ connect(m_pWidgetStack, &QStackedWidget::currentChanged, this, &UINativeWizard::sltCurrentIndexChanged);
+ pLayoutFrame->addWidget(m_pWidgetStack);
+ }
+ }
+
+ /* Add to layout: */
+ m_pLayoutRight->addWidget(pFrame);
+ }
+#else /* !VBOX_WS_MAC */
+ /* Prepare widget-stack directly on other platforms: */
+ m_pWidgetStack = new QStackedWidget(this);
+ if (m_pWidgetStack)
+ {
+ connect(m_pWidgetStack, &QStackedWidget::currentChanged, this, &UINativeWizard::sltCurrentIndexChanged);
+ m_pLayoutRight->addWidget(m_pWidgetStack);
+ }
+#endif /* !VBOX_WS_MAC */
+
+ /* Add to layout: */
+ pLayoutUpper->addLayout(m_pLayoutRight);
+ }
+
+ /* Add to layout: */
+ pLayoutMain->addLayout(pLayoutUpper, 1);
+ }
+
+ /* Prepare bottom widget: */
+ QWidget *pWidgetBottom = new QWidget(this);
+ if (pWidgetBottom)
+ {
+#ifndef VBOX_WS_MAC
+ /* Adjust palette a bit on Windows/X11 for native purposes: */
+ pWidgetBottom->setAutoFillBackground(true);
+ QPalette pal = QGuiApplication::palette();
+ pal.setColor(QPalette::Active, QPalette::Window, pal.color(QPalette::Active, QPalette::Window).darker(110));
+ pal.setColor(QPalette::Inactive, QPalette::Window, pal.color(QPalette::Inactive, QPalette::Window).darker(110));
+ pWidgetBottom->setPalette(pal);
+#endif /* !VBOX_WS_MAC */
+
+ /* Prepare bottom layout: */
+ QHBoxLayout *pLayoutBottom = new QHBoxLayout(pWidgetBottom);
+ if (pLayoutBottom)
+ {
+ /* Reset margins to default, they were flawed by parent inheritance: */
+ const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
+ const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
+ const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
+ const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
+ pLayoutBottom->setContentsMargins(iL, iT, iR, iB);
+
+ // WORKAROUND:
+ // Prepare dialog button-box? Huh, no .. QWizard has different opinion.
+ // So we are hardcoding order, same on all platforms, which is the case.
+ for (int i = WizardButtonType_Invalid + 1; i < WizardButtonType_Max; ++i)
+ {
+ const WizardButtonType enmType = (WizardButtonType)i;
+ /* Create Help button only if help hash tag is set.
+ * Create other buttons in any case: */
+ if (enmType != WizardButtonType_Help || !m_strHelpHashtag.isEmpty())
+ m_buttons[enmType] = new QPushButton(pWidgetBottom);
+ QPushButton *pButton = wizardButton(enmType);
+ if (pButton)
+ pLayoutBottom->addWidget(pButton);
+ if (enmType == WizardButtonType_Help)
+ pLayoutBottom->addStretch(1);
+ if ( pButton
+ && enmType == WizardButtonType_Next)
+ pButton->setDefault(true);
+ }
+ /* Connect buttons: */
+ if (wizardButton(WizardButtonType_Help))
+ {
+ connect(wizardButton(WizardButtonType_Help), &QPushButton::clicked,
+ &(msgCenter()), &UIMessageCenter::sltHandleHelpRequest);
+ wizardButton(WizardButtonType_Help)->setShortcut(QKeySequence::HelpContents);
+ uiCommon().setHelpKeyword(wizardButton(WizardButtonType_Help), m_strHelpHashtag);
+ }
+ connect(wizardButton(WizardButtonType_Expert), &QPushButton::clicked,
+ this, &UINativeWizard::sltExpert);
+ connect(wizardButton(WizardButtonType_Back), &QPushButton::clicked,
+ this, &UINativeWizard::sltPrevious);
+ connect(wizardButton(WizardButtonType_Next), &QPushButton::clicked,
+ this, &UINativeWizard::sltNext);
+ connect(wizardButton(WizardButtonType_Cancel), &QPushButton::clicked,
+ this, &UINativeWizard::reject);
+ }
+
+ /* Add to layout: */
+ pLayoutMain->addWidget(pWidgetBottom);
+ }
+ }
+
+ /* Prepare local notification-center: */
+ m_pNotificationCenter = new UINotificationCenter(this);
+}
+
+void UINativeWizard::cleanup()
+{
+ /* Cleanup local notification-center: */
+ delete m_pNotificationCenter;
+ m_pNotificationCenter = 0;
+}
+
+void UINativeWizard::init()
+{
+ /* Populate pages: */
+ populatePages();
+
+ /* Translate wizard: */
+ retranslateUi();
+ /* Translate wizard pages: */
+ retranslatePages();
+
+ /* Resize wizard to 'golden ratio': */
+ resizeToGoldenRatio();
+
+ /* Make sure current page initialized: */
+ sltCurrentIndexChanged();
+}
+
+void UINativeWizard::deinit()
+{
+ /* Remove all the pages: */
+ m_pWidgetStack->blockSignals(true);
+ while (m_pWidgetStack->count() > 0)
+ {
+ QWidget *pLastWidget = m_pWidgetStack->widget(m_pWidgetStack->count() - 1);
+ m_pWidgetStack->removeWidget(pLastWidget);
+ delete pLastWidget;
+ }
+ m_pWidgetStack->blockSignals(false);
+
+ /* Update last index: */
+ m_iLastIndex = -1;
+ /* Update invisible pages: */
+ m_invisiblePages.clear();
+
+ /* Clean wizard finally: */
+ cleanWizard();
+}
+
+void UINativeWizard::retranslatePages()
+{
+ /* Translate all the pages: */
+ for (int i = 0; i < m_pWidgetStack->count(); ++i)
+ qobject_cast<UINativeWizardPage*>(m_pWidgetStack->widget(i))->retranslate();
+}
+
+void UINativeWizard::resizeToGoldenRatio()
+{
+ /* Standard top margin: */
+ const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
+ m_pLayoutRight->setContentsMargins(0, iT, 0, 0);
+ /* Show title label for Basic mode case: */
+ m_pLabelPageTitle->setVisible(m_enmMode == WizardMode_Basic);
+#ifndef VBOX_WS_MAC
+ /* Hide/show pixmap label on Windows/X11 only, on macOS it's in the background: */
+ m_pLabelPixmap->setVisible(!m_strPixmapName.isEmpty());
+#endif /* !VBOX_WS_MAC */
+
+ /* For wizard in Basic mode: */
+ if (m_enmMode == WizardMode_Basic)
+ {
+ /* Temporary hide all the QIRichTextLabel(s) to exclude
+ * influence onto m_pWidgetStack minimum size-hint below: */
+ foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
+ pLabel->hide();
+ /* Gather suitable dimensions: */
+ const int iStepWidth = 100;
+ const int iMinWidth = qMax(100, m_pWidgetStack->minimumSizeHint().width());
+ const int iMaxWidth = qMax(iMinWidth, gpDesktop->availableGeometry(this).width() * 3 / 4);
+ /* Show all the QIRichTextLabel(s) again, they were hidden above: */
+ foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
+ pLabel->show();
+ /* Now look for a golden ratio: */
+ int iCurrentWidth = iMinWidth;
+ do
+ {
+ /* Assign current QIRichTextLabel(s) width: */
+ foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
+ pLabel->setMinimumTextWidth(iCurrentWidth);
+
+ /* Calculate current ratio: */
+ const QSize msh = m_pWidgetStack->minimumSizeHint();
+ int iWidth = msh.width();
+ int iHeight = msh.height();
+#ifndef VBOX_WS_MAC
+ /* Advance width for standard watermark width: */
+ if (!m_strPixmapName.isEmpty())
+ iWidth += 145;
+ /* Advance height for spacing & title height: */
+ if (m_pLayoutRight)
+ {
+ int iL, iT, iR, iB;
+ m_pLayoutRight->getContentsMargins(&iL, &iT, &iR, &iB);
+ iHeight += iT + m_pLayoutRight->spacing() + iB;
+ }
+ if (m_pLabelPageTitle)
+ iHeight += m_pLabelPageTitle->minimumSizeHint().height();
+#endif /* !VBOX_WS_MAC */
+ const double dRatio = (double)iWidth / iHeight;
+ if (dRatio > 1.6)
+ break;
+
+ /* Advance current width: */
+ iCurrentWidth += iStepWidth;
+ }
+ while (iCurrentWidth < iMaxWidth);
+ }
+
+#ifdef VBOX_WS_MAC
+ /* Assign background finally: */
+ if (!m_strPixmapName.isEmpty())
+ assignBackground();
+#else
+ /* Assign watermark finally: */
+ if (!m_strPixmapName.isEmpty())
+ assignWatermark();
+#endif /* !VBOX_WS_MAC */
+
+ /* Make sure layouts are freshly updated & activated: */
+ foreach (QLayout *pLayout, findChildren<QLayout*>())
+ {
+ pLayout->update();
+ pLayout->activate();
+ }
+ QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
+
+ /* Resize to minimum size-hint: */
+ resize(minimumSizeHint());
+}
+
+bool UINativeWizard::isLastVisiblePage(int iPageIndex) const
+{
+ if (!m_pWidgetStack)
+ return false;
+ if (iPageIndex == -1)
+ return false;
+ /* The page itself is not visible: */
+ if (m_invisiblePages.contains(iPageIndex))
+ return false;
+ bool fLastVisible = true;
+ /* Look at the page coming after the page with @p iPageIndex and check if they are visible: */
+ for (int i = iPageIndex + 1; i < m_pWidgetStack->count(); ++i)
+ {
+ if (!m_invisiblePages.contains(i))
+ {
+ fLastVisible = false;
+ break;
+ }
+ }
+ return fLastVisible;
+}
+
+#ifdef VBOX_WS_MAC
+void UINativeWizard::assignBackground()
+{
+ /* Load pixmap to icon first, this will gather HiDPI pixmaps as well: */
+ const QIcon icon = UIIconPool::iconSet(m_strPixmapName);
+
+ /* Acquire pixmap of required size and scale (on basis of parent-widget's device pixel ratio): */
+ const QSize standardSize(620, 440);
+ const QPixmap pixmapOld = icon.pixmap(parentWidget()->windowHandle(), standardSize);
+
+ /* Assign background finally: */
+ m_pLabelPixmap->setPixmap(pixmapOld);
+ m_pLabelPixmap->resize(m_pLabelPixmap->minimumSizeHint());
+}
+
+#else
+
+void UINativeWizard::assignWatermark()
+{
+ /* Load pixmap to icon first, this will gather HiDPI pixmaps as well: */
+ const QIcon icon = UIIconPool::iconSet(m_strPixmapName);
+
+ /* Acquire pixmap of required size and scale (on basis of parent-widget's device pixel ratio): */
+ const QSize standardSize(145, 290);
+ const QPixmap pixmapOld = icon.pixmap(parentWidget()->windowHandle(), standardSize);
+
+ /* Convert watermark to image which allows to manage pixel data directly: */
+ const QImage imageOld = pixmapOld.toImage();
+ /* Use the right-top watermark pixel as frame color: */
+ const QRgb rgbFrame = imageOld.pixel(imageOld.width() - 1, 0);
+
+ /* Compose desired height up to pixmap device pixel ratio: */
+ int iL, iT, iR, iB;
+ m_pLayoutRight->getContentsMargins(&iL, &iT, &iR, &iB);
+ const int iSpacing = iT + m_pLayoutRight->spacing() + iB;
+ const int iTitleHeight = m_pLabelPageTitle->minimumSizeHint().height();
+ const int iStackHeight = m_pWidgetStack->minimumSizeHint().height();
+ const int iDesiredHeight = (iTitleHeight + iSpacing + iStackHeight) * pixmapOld.devicePixelRatio();
+ /* Create final image on the basis of incoming, applying the rules: */
+ QImage imageNew(imageOld.width(), qMax(imageOld.height(), iDesiredHeight), imageOld.format());
+ for (int y = 0; y < imageNew.height(); ++y)
+ {
+ for (int x = 0; x < imageNew.width(); ++x)
+ {
+ /* Border rule: */
+ if (x == imageNew.width() - 1)
+ imageNew.setPixel(x, y, rgbFrame);
+ /* Horizontal extension rule - use last used color: */
+ else if (x >= imageOld.width() && y < imageOld.height())
+ imageNew.setPixel(x, y, imageOld.pixel(imageOld.width() - 1, y));
+ /* Vertical extension rule - use last used color: */
+ else if (y >= imageOld.height() && x < imageOld.width())
+ imageNew.setPixel(x, y, imageOld.pixel(x, imageOld.height() - 1));
+ /* Common extension rule - use last used color: */
+ else if (x >= imageOld.width() && y >= imageOld.height())
+ imageNew.setPixel(x, y, imageOld.pixel(imageOld.width() - 1, imageOld.height() - 1));
+ /* Else just copy color: */
+ else
+ imageNew.setPixel(x, y, imageOld.pixel(x, y));
+ }
+ }
+
+ /* Convert processed image to pixmap: */
+ QPixmap pixmapNew = QPixmap::fromImage(imageNew);
+ /* For HiDPI support parent-widget's device pixel ratio is to be taken into account: */
+ double dRatio = 1.0;
+ if ( parentWidget()
+ && parentWidget()->window()
+ && parentWidget()->window()->windowHandle())
+ dRatio = parentWidget()->window()->windowHandle()->devicePixelRatio();
+ pixmapNew.setDevicePixelRatio(dRatio);
+ /* Assign watermark finally: */
+ m_pLabelPixmap->setPixmap(pixmapNew);
+}
+
+#endif /* !VBOX_WS_MAC */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.h b/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.h
new file mode 100644
index 00000000..096c7a52
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.h
@@ -0,0 +1,220 @@
+/* $Id: UINativeWizard.h $ */
+/** @file
+ * VBox Qt GUI - UINativeWizard class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_UINativeWizard_h
+#define FEQT_INCLUDED_SRC_wizards_UINativeWizard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QPointer>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class QPushButton;
+class QStackedWidget;
+class QVBoxLayout;
+class UINativeWizardPage;
+class UINotificationCenter;
+class UINotificationProgress;
+
+/** Native wizard buttons. */
+enum WizardButtonType
+{
+ WizardButtonType_Invalid,
+ WizardButtonType_Help,
+ WizardButtonType_Expert,
+ WizardButtonType_Back,
+ WizardButtonType_Next,
+ WizardButtonType_Cancel,
+ WizardButtonType_Max,
+};
+Q_DECLARE_METATYPE(WizardButtonType);
+
+#ifdef VBOX_WS_MAC
+/** QWidget-based QFrame analog with one particular purpose to
+ * simulate macOS wizard frame without influencing palette hierarchy. */
+class SHARED_LIBRARY_STUFF UIFrame : public QWidget
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs UIFrame passing @a pParent to the base-class. */
+ UIFrame(QWidget *pParent);
+
+protected:
+
+ /** Handles paint @a pEvent. */
+ virtual void paintEvent(QPaintEvent *pEvent) /* final */;
+};
+#endif /* VBOX_WS_MAC */
+
+/** QDialog extension with advanced functionality emulating QWizard behavior. */
+class SHARED_LIBRARY_STUFF UINativeWizard : public QIWithRetranslateUI<QDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs wizard passing @a pParent to the base-class.
+ * @param enmType Brings the wizard type.
+ * @param enmMode Brings the wizard mode.
+ * @param strHelpHashtag Brings the wizard help hashtag. */
+ UINativeWizard(QWidget *pParent,
+ WizardType enmType,
+ WizardMode enmMode = WizardMode_Auto,
+ const QString &strHelpHashtag = QString());
+ /** Destructs wizard. */
+ virtual ~UINativeWizard() RT_OVERRIDE;
+
+ /** Returns local notification-center reference. */
+ UINotificationCenter *notificationCenter() const;
+ /** Immediately handles notification @a pProgress object. */
+ bool handleNotificationProgressNow(UINotificationProgress *pProgress);
+
+ /** Returns wizard button of specified @a enmType. */
+ QPushButton *wizardButton(const WizardButtonType &enmType) const;
+
+public slots:
+
+ /** Executes wizard in window modal mode.
+ * @note You shouldn't have to override it! */
+ virtual int exec() /* final */;
+
+protected:
+
+ /** Returns wizard type. */
+ WizardType type() const { return m_enmType; }
+ /** Returns wizard mode. */
+ WizardMode mode() const { return m_enmMode; }
+ /** Defines @a strName for wizard button of specified @a enmType. */
+ void setWizardButtonName(const WizardButtonType &enmType, const QString &strName);
+
+ /** Defines pixmap @a strName. */
+ void setPixmapName(const QString &strName);
+
+ /** Returns whether the page with certain @a iIndex is visible. */
+ bool isPageVisible(int iIndex) const;
+ /** Defines whether the page with certain @a iIndex is @a fVisible. */
+ void setPageVisible(int iIndex, bool fVisible);
+
+ /** Appends wizard @a pPage.
+ * @returns assigned page index. */
+ int addPage(UINativeWizardPage *pPage);
+ /** Populates pages.
+ * @note In your subclasses you should add
+ * pages via addPage declared above. */
+ virtual void populatePages() = 0;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Performs wizard-specific cleanup in case of wizard-mode change
+ * such as folder deletion in New VM wizard etc. */
+ virtual void cleanWizard() {}
+
+private slots:
+
+ /** Handles current-page change to page with @a iIndex. */
+ void sltCurrentIndexChanged(int iIndex = -1);
+ /** Handles page validity changes. */
+ void sltCompleteChanged();
+
+ /** Toggles between basic and expert modes. */
+ void sltExpert();
+ /** Switches to previous page. */
+ void sltPrevious();
+ /** Switches to next page. */
+ void sltNext();
+
+private:
+
+ /** Prepares all. */
+ void prepare();
+ /** Cleanups all. */
+ void cleanup();
+ /** Inits all. */
+ void init();
+ /** Deinits all. */
+ void deinit();
+
+ /** Performs pages translation. */
+ void retranslatePages();
+
+ /** Resizes wizard to golden ratio. */
+ void resizeToGoldenRatio();
+#ifdef VBOX_WS_MAC
+ /** Assigns wizard background. */
+ void assignBackground();
+#else
+ /** Assigns wizard watermark. */
+ void assignWatermark();
+#endif
+ /** Checks if the pages coming after the page with iPageIndex is visible or not. Returns true if
+ * page with iPageIndex is the last visible page of the wizard. Returns false otherwise. */
+ bool isLastVisiblePage(int iPageIndex) const;
+
+ /** Holds the wizard type. */
+ WizardType m_enmType;
+ /** Holds the wizard mode. */
+ WizardMode m_enmMode;
+ /** Holds the wizard help hashtag. */
+ QString m_strHelpHashtag;
+ /** Holds the pixmap name. */
+ QString m_strPixmapName;
+ /** Holds the last entered page index. */
+ int m_iLastIndex;
+ /** Holds the set of invisible pages. */
+ QSet<int> m_invisiblePages;
+
+ /** Holds the pixmap label instance. */
+ QLabel *m_pLabelPixmap;
+ /** Holds the right layout instance. */
+ QVBoxLayout *m_pLayoutRight;
+ /** Holds the title label instance. */
+ QLabel *m_pLabelPageTitle;
+ /** Holds the widget-stack instance. */
+ QStackedWidget *m_pWidgetStack;
+ /** Holds button instance map. */
+ QMap<WizardButtonType, QPushButton*> m_buttons;
+
+ /** Holds the local notification-center instance. */
+ UINotificationCenter *m_pNotificationCenter;
+};
+
+/** Native wizard interface pointer. */
+typedef QPointer<UINativeWizard> UINativeWizardPointer;
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_UINativeWizard_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.cpp
new file mode 100644
index 00000000..da6377e2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.cpp
@@ -0,0 +1,52 @@
+/* $Id: UINativeWizardPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UINativeWizardPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+#include "UINativeWizardPage.h"
+
+
+UINativeWizardPage::UINativeWizardPage()
+{
+}
+
+void UINativeWizardPage::setTitle(const QString &strTitle)
+{
+ m_strTitle = strTitle;
+}
+
+QString UINativeWizardPage::title() const
+{
+ return m_strTitle;
+}
+
+UINativeWizard *UINativeWizardPage::wizard() const
+{
+ return parentWidget() && parentWidget()->window()
+ ? qobject_cast<UINativeWizard*>(parentWidget()->window())
+ : 0;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.h
new file mode 100644
index 00000000..df7866e7
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.h
@@ -0,0 +1,92 @@
+/* $Id: UINativeWizardPage.h $ */
+/** @file
+ * VBox Qt GUI - UINativeWizardPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_UINativeWizardPage_h
+#define FEQT_INCLUDED_SRC_wizards_UINativeWizardPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class UINativeWizard;
+
+/** QWidget extension with advanced functionality emulating QWizardPage behavior. */
+class SHARED_LIBRARY_STUFF UINativeWizardPage : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ /** Notifies about page validity changes. */
+ void completeChanged();
+
+public:
+
+ /** Constructs wizard page. */
+ UINativeWizardPage();
+
+ /** Redirects the translation call to actual handler. */
+ void retranslate() { retranslateUi(); }
+
+ /** Defines page @a strTitle. */
+ void setTitle(const QString &strTitle);
+ /** Returns page title. */
+ QString title() const;
+
+ /** Handles the page initialization. */
+ virtual void initializePage() {}
+ /** Tests the page for completeness, enables the Next button if Ok.
+ * @returns whether all conditions to go to next page are satisfied. */
+ virtual bool isComplete() const { return true; }
+ /** Tests the page for validity, tranfers to the Next page is Ok.
+ * @returns whether page state to go to next page is bearable. */
+ virtual bool validatePage() { return true; }
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UINativeWizard *wizard() const;
+
+ template<typename T>
+ T *wizardWindow() const
+ {
+ return parentWidget() && parentWidget()->window()
+ ? qobject_cast<T*>(parentWidget()->window())
+ : 0;
+ }
+
+
+ /** Holds the page title. */
+ QString m_strTitle;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_UINativeWizardPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVM.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVM.cpp
new file mode 100644
index 00000000..90750844
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVM.cpp
@@ -0,0 +1,126 @@
+/* $Id: UIWizardAddCloudVM.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardAddCloudVM class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UINotificationCenter.h"
+#include "UIWizardAddCloudVM.h"
+#include "UIWizardAddCloudVMPageExpert.h"
+#include "UIWizardAddCloudVMPageSource.h"
+
+/* COM includes: */
+#include "CCloudMachine.h"
+
+
+UIWizardAddCloudVM::UIWizardAddCloudVM(QWidget *pParent,
+ const QString &strFullGroupName /* = QString() */)
+ : UINativeWizard(pParent, WizardType_AddCloudVM)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign watermark: */
+ setPixmapName(":/wizard_new_cloud_vm.png");
+#else
+ /* Assign background image: */
+ setPixmapName(":/wizard_new_cloud_vm_bg.png");
+#endif
+
+ /* Parse passed full group name: */
+ const QString strProviderShortName = strFullGroupName.section('/', 1, 1);
+ const QString strProfileName = strFullGroupName.section('/', 2, 2);
+ if (!strProviderShortName.isEmpty() && !strProfileName.isEmpty())
+ {
+ m_strProviderShortName = strProviderShortName;
+ m_strProfileName = strProfileName;
+ }
+}
+
+bool UIWizardAddCloudVM::addCloudVMs()
+{
+ /* Prepare result: */
+ bool fResult = false;
+
+ /* Acquire prepared client: */
+ CCloudClient comClient = client();
+ AssertReturn(comClient.isNotNull(), fResult);
+
+ /* For each cloud instance name we have: */
+ foreach (const QString &strInstanceName, instanceIds())
+ {
+ /* Initiate cloud VM add procedure: */
+ CCloudMachine comMachine;
+
+ /* Add cloud VM: */
+ UINotificationProgressCloudMachineAdd *pNotification = new UINotificationProgressCloudMachineAdd(comClient,
+ comMachine,
+ strInstanceName,
+ providerShortName(),
+ profileName());
+ connect(pNotification, &UINotificationProgressCloudMachineAdd::sigCloudMachineAdded,
+ &uiCommon(), &UICommon::sltHandleCloudMachineAdded);
+ gpNotificationCenter->append(pNotification);
+
+ /* Positive: */
+ fResult = true;
+ }
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardAddCloudVM::populatePages()
+{
+ /* Create corresponding pages: */
+ switch (mode())
+ {
+ case WizardMode_Basic:
+ {
+ addPage(new UIWizardAddCloudVMPageSource);
+ break;
+ }
+ case WizardMode_Expert:
+ {
+ addPage(new UIWizardAddCloudVMPageExpert);
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid mode: %d", mode()));
+ break;
+ }
+ }
+}
+
+void UIWizardAddCloudVM::retranslateUi()
+{
+ /* Call to base-class: */
+ UINativeWizard::retranslateUi();
+
+ /* Translate wizard: */
+ setWindowTitle(tr("Add Cloud Virtual Machine"));
+ /// @todo implement this?
+ //setButtonText(QWizard::FinishButton, tr("Add"));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVM.h b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVM.h
new file mode 100644
index 00000000..5d8e1ae0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVM.h
@@ -0,0 +1,98 @@
+/* $Id: UIWizardAddCloudVM.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardAddCloudVM class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_addcloudvm_UIWizardAddCloudVM_h
+#define FEQT_INCLUDED_SRC_wizards_addcloudvm_UIWizardAddCloudVM_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudClient.h"
+
+/** Add Cloud VM wizard. */
+class UIWizardAddCloudVM : public UINativeWizard
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Add Cloud VM wizard passing @a pParent to the base-class.
+ * @param strFullGroupName Brings full group name (/provider/profile) to add VM to. */
+ UIWizardAddCloudVM(QWidget *pParent, const QString &strFullGroupName = QString());
+
+ /** Defines @a strProviderShortName. */
+ void setProviderShortName(const QString &strProviderShortName) { m_strProviderShortName = strProviderShortName; }
+ /** Returns provider short name. */
+ QString providerShortName() const { return m_strProviderShortName; }
+
+ /** Defines @a strProfileName. */
+ void setProfileName(const QString &strProfileName) { m_strProfileName = strProfileName; }
+ /** Returns profile name. */
+ QString profileName() const { return m_strProfileName; }
+
+ /** Defines @a instanceIds. */
+ void setInstanceIds(const QStringList &instanceIds) { m_instanceIds = instanceIds; }
+ /** Returns instance IDs. */
+ QStringList instanceIds() const { return m_instanceIds; }
+
+ /** Defines Cloud @a comClient object wrapper. */
+ void setClient(const CCloudClient &comClient) { m_comClient = comClient; }
+ /** Returns Cloud Client object wrapper. */
+ CCloudClient client() const { return m_comClient; }
+
+ /** Adds cloud VMs. */
+ bool addCloudVMs();
+
+protected:
+
+ /** Populates pages. */
+ virtual void populatePages() /* override final */;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+private:
+
+ /** Holds the short provider name. */
+ QString m_strProviderShortName;
+ /** Holds the profile name. */
+ QString m_strProfileName;
+ /** Holds the instance ids. */
+ QStringList m_instanceIds;
+ /** Holds the Cloud Client object wrapper. */
+ CCloudClient m_comClient;
+};
+
+/** Safe pointer to add cloud vm wizard. */
+typedef QPointer<UIWizardAddCloudVM> UISafePointerWizardAddCloudVM;
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_addcloudvm_UIWizardAddCloudVM_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageExpert.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageExpert.cpp
new file mode 100644
index 00000000..991e43ea
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageExpert.cpp
@@ -0,0 +1,278 @@
+/* $Id: UIWizardAddCloudVMPageExpert.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardAddCloudVMPageExpert class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <QListWidget>
+#include <QPushButton>
+#include <QTableWidget>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIToolButton.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIIconPool.h"
+#include "UIToolBox.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualBoxManager.h"
+#include "UIWizardAddCloudVM.h"
+#include "UIWizardAddCloudVMPageExpert.h"
+
+/* Namespaces: */
+using namespace UIWizardAddCloudVMSource;
+
+
+UIWizardAddCloudVMPageExpert::UIWizardAddCloudVMPageExpert()
+ : m_pToolBox(0)
+ , m_pProviderLabel(0)
+ , m_pProviderComboBox(0)
+ , m_pProfileLabel(0)
+ , m_pProfileComboBox(0)
+ , m_pProfileToolButton(0)
+ , m_pSourceInstanceLabel(0)
+ , m_pSourceInstanceList(0)
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare tool-box: */
+ m_pToolBox = new UIToolBox(this);
+ if (m_pToolBox)
+ {
+ /* Prepare location widget: */
+ QWidget *pWidgetLocation = new QWidget(m_pToolBox);
+ if (pWidgetLocation)
+ {
+ /* Prepare location layout: */
+ QVBoxLayout *pLayoutLocation = new QVBoxLayout(pWidgetLocation);
+ if (pLayoutLocation)
+ {
+ pLayoutLocation->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare provider combo-box: */
+ m_pProviderComboBox = new QIComboBox(pWidgetLocation);
+ if (m_pProviderComboBox)
+ pLayoutLocation->addWidget(m_pProviderComboBox);
+
+ /* Prepare profile layout: */
+ QHBoxLayout *pLayoutProfile = new QHBoxLayout;
+ if (pLayoutProfile)
+ {
+ pLayoutProfile->setContentsMargins(0, 0, 0, 0);
+ pLayoutProfile->setSpacing(1);
+
+ /* Prepare profile combo-box: */
+ m_pProfileComboBox = new QIComboBox(pWidgetLocation);
+ if (m_pProfileComboBox)
+ pLayoutProfile->addWidget(m_pProfileComboBox);
+
+ /* Prepare profile tool-button: */
+ m_pProfileToolButton = new QIToolButton(pWidgetLocation);
+ if (m_pProfileToolButton)
+ {
+ m_pProfileToolButton->setIcon(UIIconPool::iconSet(":/cloud_profile_manager_16px.png",
+ ":/cloud_profile_manager_disabled_16px.png"));
+ pLayoutProfile->addWidget(m_pProfileToolButton);
+ }
+
+ /* Add into layout: */
+ pLayoutLocation->addLayout(pLayoutProfile);
+ }
+ }
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(0, pWidgetLocation, QString());
+ }
+
+ /* Prepare source widget: */
+ QWidget *pWidgetSource = new QWidget(m_pToolBox);
+ if (pWidgetSource)
+ {
+ /* Prepare source layout: */
+ QVBoxLayout *pLayoutSource = new QVBoxLayout(pWidgetSource);
+ if (pLayoutSource)
+ {
+ pLayoutSource->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare source instances table: */
+ m_pSourceInstanceList = new QListWidget(pWidgetSource);
+ if (m_pSourceInstanceList)
+ {
+ /* A bit of look&feel: */
+ m_pSourceInstanceList->setAlternatingRowColors(true);
+ /* Allow to select more than one item to add: */
+ m_pSourceInstanceList->setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+ /* Add into layout: */
+ pLayoutSource->addWidget(m_pSourceInstanceList);
+ }
+ }
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(1, pWidgetSource, QString());
+ }
+
+ /* Add into layout: */
+ pLayoutMain->addWidget(m_pToolBox);
+ }
+ }
+
+ /* Setup connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UIWizardAddCloudVMPageExpert::sltHandleProviderComboChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UIWizardAddCloudVMPageExpert::sltHandleProviderComboChange);
+ connect(m_pProviderComboBox, &QIComboBox::activated,
+ this, &UIWizardAddCloudVMPageExpert::sltHandleProviderComboChange);
+ connect(m_pProfileComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardAddCloudVMPageExpert::sltHandleProfileComboChange);
+ connect(m_pProfileToolButton, &QIToolButton::clicked,
+ this, &UIWizardAddCloudVMPageExpert::sltHandleProfileButtonClick);
+ connect(m_pSourceInstanceList, &QListWidget::itemSelectionChanged,
+ this, &UIWizardAddCloudVMPageExpert::sltHandleSourceInstanceChange);
+}
+
+UIWizardAddCloudVM *UIWizardAddCloudVMPageExpert::wizard() const
+{
+ return qobject_cast<UIWizardAddCloudVM*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardAddCloudVMPageExpert::retranslateUi()
+{
+ /* Translate tool-box: */
+ if (m_pToolBox)
+ {
+ m_pToolBox->setPageTitle(0, UIWizardAddCloudVM::tr("Location"));
+ m_pToolBox->setPageTitle(1, UIWizardAddCloudVM::tr("Source"));
+ }
+
+ /* Translate profile stuff: */
+ if (m_pProfileToolButton)
+ m_pProfileToolButton->setToolTip(UIWizardAddCloudVM::tr("Open Cloud Profile Manager..."));
+
+ /* Translate received values of Source combo-box.
+ * We are enumerating starting from 0 for simplicity: */
+ if (m_pProviderComboBox)
+ for (int i = 0; i < m_pProviderComboBox->count(); ++i)
+ {
+ m_pProviderComboBox->setItemText(i, m_pProviderComboBox->itemData(i, ProviderData_Name).toString());
+ m_pProviderComboBox->setItemData(i, UIWizardAddCloudVM::tr("Add VM from cloud service provider."), Qt::ToolTipRole);
+ }
+
+ /* Update tool-tips: */
+ updateComboToolTip(m_pProviderComboBox);
+}
+
+void UIWizardAddCloudVMPageExpert::initializePage()
+{
+ /* Choose 1st tool to be chosen initially: */
+ m_pToolBox->setCurrentPage(0);
+ /* Populate providers: */
+ populateProviders(m_pProviderComboBox, wizard()->notificationCenter());
+ /* Translate providers: */
+ retranslateUi();
+ /* Fetch it, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltHandleProviderComboChange", Qt::QueuedConnection);
+ /* Make image list focused by default: */
+ m_pSourceInstanceList->setFocus();
+}
+
+bool UIWizardAddCloudVMPageExpert::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Make sure client is not NULL and
+ * at least one instance is selected: */
+ fResult = wizard()->client().isNotNull()
+ && !wizard()->instanceIds().isEmpty();
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardAddCloudVMPageExpert::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Try to add cloud VMs: */
+ fResult = wizard()->addCloudVMs();
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardAddCloudVMPageExpert::sltHandleProviderComboChange()
+{
+ /* Update combo tool-tip: */
+ updateComboToolTip(m_pProviderComboBox);
+
+ /* Update wizard fields: */
+ wizard()->setProviderShortName(m_pProviderComboBox->currentData(ProviderData_ShortName).toString());
+
+ /* Update profiles: */
+ populateProfiles(m_pProfileComboBox, wizard()->notificationCenter(), wizard()->providerShortName(), wizard()->profileName());
+ sltHandleProfileComboChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardAddCloudVMPageExpert::sltHandleProfileComboChange()
+{
+ /* Update wizard fields: */
+ wizard()->setProfileName(m_pProfileComboBox->currentData(ProfileData_Name).toString());
+ wizard()->setClient(cloudClientByName(wizard()->providerShortName(), wizard()->profileName(), wizard()->notificationCenter()));
+
+ /* Update profile instances: */
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ populateProfileInstances(m_pSourceInstanceList, wizard()->notificationCenter(), wizard()->client());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ sltHandleSourceInstanceChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardAddCloudVMPageExpert::sltHandleProfileButtonClick()
+{
+ if (gpManager)
+ gpManager->openCloudProfileManager();
+}
+
+void UIWizardAddCloudVMPageExpert::sltHandleSourceInstanceChange()
+{
+ /* Update wizard fields: */
+ wizard()->setInstanceIds(currentListWidgetData(m_pSourceInstanceList));
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageExpert.h b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageExpert.h
new file mode 100644
index 00000000..cad2d524
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageExpert.h
@@ -0,0 +1,105 @@
+/* $Id: UIWizardAddCloudVMPageExpert.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardAddCloudVMPageExpert class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_addcloudvm_UIWizardAddCloudVMPageExpert_h
+#define FEQT_INCLUDED_SRC_wizards_addcloudvm_UIWizardAddCloudVMPageExpert_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIWizardAddCloudVMPageSource.h"
+
+/* Forward declarations: */
+class UIToolBox;
+class UIWizardAddCloudVM;
+
+/** UINativeWizardPage extension for Expert page of the Add Cloud VM wizard,
+ * based on UIWizardAddCloudVMPage1 namespace functions. */
+class UIWizardAddCloudVMPageExpert : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs expert page. */
+ UIWizardAddCloudVMPageExpert();
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardAddCloudVM *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Handles change in provider combo-box. */
+ void sltHandleProviderComboChange();
+
+ /** Handles change in profile combo-box. */
+ void sltHandleProfileComboChange();
+ /** Handles profile tool-button click. */
+ void sltHandleProfileButtonClick();
+
+ /** Handles change in instance list. */
+ void sltHandleSourceInstanceChange();
+
+private:
+
+ /** Holds the tool-box instance. */
+ UIToolBox *m_pToolBox;
+
+ /** Holds the provider type label instance. */
+ QLabel *m_pProviderLabel;
+ /** Holds the provider type combo-box instance. */
+ QIComboBox *m_pProviderComboBox;
+
+ /** Holds the profile label instance. */
+ QLabel *m_pProfileLabel;
+ /** Holds the profile combo-box instance. */
+ QIComboBox *m_pProfileComboBox;
+ /** Holds the profile management tool-button instance. */
+ QIToolButton *m_pProfileToolButton;
+
+ /** Holds the source instance label instance. */
+ QLabel *m_pSourceInstanceLabel;
+ /** Holds the source instance list instance. */
+ QListWidget *m_pSourceInstanceList;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_addcloudvm_UIWizardAddCloudVMPageExpert_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageSource.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageSource.cpp
new file mode 100644
index 00000000..8a86ec05
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageSource.cpp
@@ -0,0 +1,544 @@
+/* $Id: UIWizardAddCloudVMPageSource.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardAddCloudVMPageSource class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QHeaderView>
+#include <QLabel>
+#include <QListWidget>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIRichTextLabel.h"
+#include "QIToolButton.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualBoxManager.h"
+#include "UIWizardAddCloudVM.h"
+#include "UIWizardAddCloudVMPageSource.h"
+
+/* COM includes: */
+#include "CStringArray.h"
+
+/* Namespaces: */
+using namespace UIWizardAddCloudVMSource;
+
+
+/*********************************************************************************************************************************
+* Namespace UIWizardAddCloudVMSource implementation. *
+*********************************************************************************************************************************/
+
+void UIWizardAddCloudVMSource::populateProviders(QIComboBox *pCombo, UINotificationCenter *pCenter)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (pCombo->currentIndex() != -1)
+ strOldData = pCombo->currentData(ProviderData_ShortName).toString();
+ /* Otherwise "OCI" should be the default one: */
+ else
+ strOldData = "OCI";
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Iterate through existing providers: */
+ foreach (const CCloudProvider &comProvider, listCloudProviders(pCenter))
+ {
+ /* Skip if we have nothing to populate (file missing?): */
+ if (comProvider.isNull())
+ continue;
+ /* Acquire provider name: */
+ QString strProviderName;
+ if (!cloudProviderName(comProvider, strProviderName, pCenter))
+ continue;
+ /* Acquire provider short name: */
+ QString strProviderShortName;
+ if (!cloudProviderShortName(comProvider, strProviderShortName, pCenter))
+ continue;
+
+ /* Compose empty item, fill the data: */
+ pCombo->addItem(QString());
+ pCombo->setItemData(pCombo->count() - 1, strProviderName, ProviderData_Name);
+ pCombo->setItemData(pCombo->count() - 1, strProviderShortName, ProviderData_ShortName);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = pCombo->findData(strOldData, ProviderData_ShortName);
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+}
+
+void UIWizardAddCloudVMSource::populateProfiles(QIComboBox *pCombo,
+ UINotificationCenter *pCenter,
+ const QString &strProviderShortName,
+ const QString &strProfileName)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+ /* Acquire provider: */
+ CCloudProvider comProvider = cloudProviderByShortName(strProviderShortName, pCenter);
+ AssertReturnVoid(comProvider.isNotNull());
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (pCombo->currentIndex() != -1)
+ strOldData = pCombo->currentData(ProfileData_Name).toString();
+ else if (!strProfileName.isEmpty())
+ strOldData = strProfileName;
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Acquire restricted accounts: */
+ const QStringList restrictedProfiles = gEDataManager->cloudProfileManagerRestrictions();
+
+ /* Iterate through existing profiles: */
+ QStringList allowedProfileNames;
+ QStringList restrictedProfileNames;
+ foreach (const CCloudProfile &comProfile, listCloudProfiles(comProvider, pCenter))
+ {
+ /* Skip if we have nothing to populate (wtf happened?): */
+ if (comProfile.isNull())
+ continue;
+ /* Acquire current profile name: */
+ QString strCurrentProfileName;
+ if (!cloudProfileName(comProfile, strCurrentProfileName, pCenter))
+ continue;
+
+ /* Compose full profile name: */
+ const QString strFullProfileName = QString("/%1/%2").arg(strProviderShortName).arg(strCurrentProfileName);
+ /* Append to appropriate list: */
+ if (restrictedProfiles.contains(strFullProfileName))
+ restrictedProfileNames.append(strCurrentProfileName);
+ else
+ allowedProfileNames.append(strCurrentProfileName);
+ }
+
+ /* Add allowed items: */
+ foreach (const QString &strAllowedProfileName, allowedProfileNames)
+ {
+ /* Compose item, fill it's data: */
+ pCombo->addItem(strAllowedProfileName);
+ pCombo->setItemData(pCombo->count() - 1, strAllowedProfileName, ProfileData_Name);
+ QFont fnt = pCombo->font();
+ fnt.setBold(true);
+ pCombo->setItemData(pCombo->count() - 1, fnt, Qt::FontRole);
+ }
+ /* Add restricted items: */
+ foreach (const QString &strRestrictedProfileName, restrictedProfileNames)
+ {
+ /* Compose item, fill it's data: */
+ pCombo->addItem(strRestrictedProfileName);
+ pCombo->setItemData(pCombo->count() - 1, strRestrictedProfileName, ProfileData_Name);
+ QBrush brsh;
+ brsh.setColor(Qt::gray);
+ pCombo->setItemData(pCombo->count() - 1, brsh, Qt::ForegroundRole);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = pCombo->findData(strOldData, ProfileData_Name);
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+}
+
+void UIWizardAddCloudVMSource::populateProfileInstances(QListWidget *pList, UINotificationCenter *pCenter, const CCloudClient &comClient)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pList);
+ AssertReturnVoid(comClient.isNotNull());
+
+ /* Block signals while updating: */
+ pList->blockSignals(true);
+
+ /* Clear list initially: */
+ pList->clear();
+
+ /* Gather instance names and ids: */
+ CStringArray comNames;
+ CStringArray comIDs;
+ if (listCloudSourceInstances(comClient, comNames, comIDs, pCenter))
+ {
+ /* Push acquired names to list rows: */
+ const QVector<QString> names = comNames.GetValues();
+ const QVector<QString> ids = comIDs.GetValues();
+ for (int i = 0; i < names.size(); ++i)
+ {
+ /* Create list item: */
+ QListWidgetItem *pItem = new QListWidgetItem(names.at(i), pList);
+ if (pItem)
+ {
+ pItem->setFlags(pItem->flags() & ~Qt::ItemIsEditable);
+ pItem->setData(Qt::UserRole, ids.at(i));
+ }
+ }
+ }
+
+ /* Choose the 1st one by default if possible: */
+ if (pList->count())
+ pList->setCurrentRow(0);
+
+ /* Unblock signals after update: */
+ pList->blockSignals(false);
+}
+
+void UIWizardAddCloudVMSource::updateComboToolTip(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ const int iCurrentIndex = pCombo->currentIndex();
+ if (iCurrentIndex != -1)
+ {
+ const QString strCurrentToolTip = pCombo->itemData(iCurrentIndex, Qt::ToolTipRole).toString();
+ AssertMsg(!strCurrentToolTip.isEmpty(), ("Tool-tip data not found!\n"));
+ pCombo->setToolTip(strCurrentToolTip);
+ }
+}
+
+QStringList UIWizardAddCloudVMSource::currentListWidgetData(QListWidget *pList)
+{
+ QStringList result;
+ foreach (QListWidgetItem *pItem, pList->selectedItems())
+ result << pItem->data(Qt::UserRole).toString();
+ return result;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWizardAddCloudVMPageSource implementation. *
+*********************************************************************************************************************************/
+
+UIWizardAddCloudVMPageSource::UIWizardAddCloudVMPageSource()
+ : m_pLabelMain(0)
+ , m_pProviderLayout(0)
+ , m_pProviderLabel(0)
+ , m_pProviderComboBox(0)
+ , m_pLabelDescription(0)
+ , m_pOptionsLayout(0)
+ , m_pProfileLabel(0)
+ , m_pProfileComboBox(0)
+ , m_pProfileToolButton(0)
+ , m_pSourceInstanceLabel(0)
+ , m_pSourceInstanceList(0)
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare main label: */
+ m_pLabelMain = new QIRichTextLabel(this);
+ if (m_pLabelMain)
+ pLayoutMain->addWidget(m_pLabelMain);
+
+ /* Prepare provider layout: */
+ m_pProviderLayout = new QGridLayout;
+ if (m_pProviderLayout)
+ {
+ m_pProviderLayout->setContentsMargins(0, 0, 0, 0);
+ m_pProviderLayout->setColumnStretch(0, 0);
+ m_pProviderLayout->setColumnStretch(1, 1);
+
+ /* Prepare provider label: */
+ m_pProviderLabel = new QLabel(this);
+ if (m_pProviderLabel)
+ m_pProviderLayout->addWidget(m_pProviderLabel, 0, 0, Qt::AlignRight);
+ /* Prepare provider combo-box: */
+ m_pProviderComboBox = new QIComboBox(this);
+ if (m_pProviderComboBox)
+ {
+ m_pProviderLabel->setBuddy(m_pProviderComboBox);
+ m_pProviderLayout->addWidget(m_pProviderComboBox, 0, 1);
+ }
+
+ /* Add into layout: */
+ pLayoutMain->addLayout(m_pProviderLayout);
+ }
+
+ /* Prepare description label: */
+ m_pLabelDescription = new QIRichTextLabel(this);
+ if (m_pLabelDescription)
+ pLayoutMain->addWidget(m_pLabelDescription);
+
+ /* Prepare options layout: */
+ m_pOptionsLayout = new QGridLayout;
+ if (m_pOptionsLayout)
+ {
+ m_pOptionsLayout->setContentsMargins(0, 0, 0, 0);
+ m_pOptionsLayout->setColumnStretch(0, 0);
+ m_pOptionsLayout->setColumnStretch(1, 1);
+ m_pOptionsLayout->setRowStretch(1, 0);
+ m_pOptionsLayout->setRowStretch(2, 1);
+
+ /* Prepare profile label: */
+ m_pProfileLabel = new QLabel(this);
+ if (m_pProfileLabel)
+ m_pOptionsLayout->addWidget(m_pProfileLabel, 0, 0, Qt::AlignRight);
+
+ /* Prepare profile layout: */
+ QHBoxLayout *pProfileLayout = new QHBoxLayout;
+ if (pProfileLayout)
+ {
+ pProfileLayout->setContentsMargins(0, 0, 0, 0);
+ pProfileLayout->setSpacing(1);
+
+ /* Prepare profile combo-box: */
+ m_pProfileComboBox = new QIComboBox(this);
+ if (m_pProfileComboBox)
+ {
+ m_pProfileLabel->setBuddy(m_pProfileComboBox);
+ pProfileLayout->addWidget(m_pProfileComboBox);
+ }
+
+ /* Prepare profile tool-button: */
+ m_pProfileToolButton = new QIToolButton(this);
+ if (m_pProfileToolButton)
+ {
+ m_pProfileToolButton->setIcon(UIIconPool::iconSet(":/cloud_profile_manager_16px.png",
+ ":/cloud_profile_manager_disabled_16px.png"));
+ pProfileLayout->addWidget(m_pProfileToolButton);
+ }
+
+ /* Add into layout: */
+ m_pOptionsLayout->addLayout(pProfileLayout, 0, 1);
+ }
+
+ /* Prepare source instance label: */
+ m_pSourceInstanceLabel = new QLabel(this);
+ if (m_pSourceInstanceLabel)
+ m_pOptionsLayout->addWidget(m_pSourceInstanceLabel, 1, 0, Qt::AlignRight);
+
+ /* Prepare source instances table: */
+ m_pSourceInstanceList = new QListWidget(this);
+ if (m_pSourceInstanceList)
+ {
+ m_pSourceInstanceLabel->setBuddy(m_pSourceInstanceLabel);
+ /* Make source image list fit 50 symbols
+ * horizontally and 8 lines vertically: */
+ const QFontMetrics fm(m_pSourceInstanceList->font());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const int iFontWidth = fm.horizontalAdvance('x');
+#else
+ const int iFontWidth = fm.width('x');
+#endif
+ const int iTotalWidth = 50 * iFontWidth;
+ const int iFontHeight = fm.height();
+ const int iTotalHeight = 8 * iFontHeight;
+ m_pSourceInstanceList->setMinimumSize(QSize(iTotalWidth, iTotalHeight));
+ /* A bit of look&feel: */
+ m_pSourceInstanceList->setAlternatingRowColors(true);
+ /* Allow to select more than one item to add: */
+ m_pSourceInstanceList->setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+ /* Add into layout: */
+ m_pOptionsLayout->addWidget(m_pSourceInstanceList, 1, 1, 2, 1);
+ }
+
+ /* Add into layout: */
+ pLayoutMain->addLayout(m_pOptionsLayout);
+ }
+ }
+
+ /* Setup connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UIWizardAddCloudVMPageSource::sltHandleProviderComboChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UIWizardAddCloudVMPageSource::sltHandleProviderComboChange);
+ connect(m_pProviderComboBox, &QIComboBox::activated,
+ this, &UIWizardAddCloudVMPageSource::sltHandleProviderComboChange);
+ connect(m_pProfileComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardAddCloudVMPageSource::sltHandleProfileComboChange);
+ connect(m_pProfileToolButton, &QIToolButton::clicked,
+ this, &UIWizardAddCloudVMPageSource::sltHandleProfileButtonClick);
+ connect(m_pSourceInstanceList, &QListWidget::itemSelectionChanged,
+ this, &UIWizardAddCloudVMPageSource::sltHandleSourceInstanceChange);
+}
+
+UIWizardAddCloudVM *UIWizardAddCloudVMPageSource::wizard() const
+{
+ return qobject_cast<UIWizardAddCloudVM*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardAddCloudVMPageSource::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardAddCloudVM::tr("Source to add from"));
+
+ /* Translate main label: */
+ m_pLabelMain->setText(UIWizardAddCloudVM::tr("Please choose the source to add cloud virtual machine from. This can "
+ "be one of known cloud service providers below."));
+
+ /* Translate provider label: */
+ m_pProviderLabel->setText(UIWizardAddCloudVM::tr("&Source:"));
+ /* Translate received values of Source combo-box.
+ * We are enumerating starting from 0 for simplicity: */
+ for (int i = 0; i < m_pProviderComboBox->count(); ++i)
+ {
+ m_pProviderComboBox->setItemText(i, m_pProviderComboBox->itemData(i, ProviderData_Name).toString());
+ m_pProviderComboBox->setItemData(i, UIWizardAddCloudVM::tr("Add VM from cloud service provider."), Qt::ToolTipRole);
+ }
+
+ /* Translate description label: */
+ m_pLabelDescription->setText(UIWizardAddCloudVM::tr("Please choose one of cloud service profiles you have registered to "
+ "add virtual machine from. Existing instance list will be "
+ "updated. To continue, select at least one instance to add virtual "
+ "machine on the basis of it."));
+
+ /* Translate profile stuff: */
+ m_pProfileLabel->setText(UIWizardAddCloudVM::tr("&Profile:"));
+ m_pProfileToolButton->setToolTip(UIWizardAddCloudVM::tr("Open Cloud Profile Manager..."));
+ m_pSourceInstanceLabel->setText(UIWizardAddCloudVM::tr("&Instances:"));
+
+ /* Adjust label widths: */
+ QList<QWidget*> labels;
+ labels << m_pProviderLabel;
+ labels << m_pProfileLabel;
+ labels << m_pSourceInstanceLabel;
+ int iMaxWidth = 0;
+ foreach (QWidget *pLabel, labels)
+ iMaxWidth = qMax(iMaxWidth, pLabel->minimumSizeHint().width());
+ m_pProviderLayout->setColumnMinimumWidth(0, iMaxWidth);
+ m_pOptionsLayout->setColumnMinimumWidth(0, iMaxWidth);
+
+ /* Update tool-tips: */
+ updateComboToolTip(m_pProviderComboBox);
+}
+
+void UIWizardAddCloudVMPageSource::initializePage()
+{
+ /* Populate providers: */
+ populateProviders(m_pProviderComboBox, wizard()->notificationCenter());
+ /* Translate providers: */
+ retranslateUi();
+ /* Fetch it, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltHandleProviderComboChange", Qt::QueuedConnection);
+ /* Make image list focused by default: */
+ m_pSourceInstanceList->setFocus();
+}
+
+bool UIWizardAddCloudVMPageSource::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Make sure client is not NULL and
+ * at least one instance is selected: */
+ fResult = wizard()->client().isNotNull()
+ && !wizard()->instanceIds().isEmpty();
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardAddCloudVMPageSource::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Try to add cloud VMs: */
+ fResult = wizard()->addCloudVMs();
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardAddCloudVMPageSource::sltHandleProviderComboChange()
+{
+ /* Update combo tool-tip: */
+ updateComboToolTip(m_pProviderComboBox);
+
+ /* Update wizard fields: */
+ wizard()->setProviderShortName(m_pProviderComboBox->currentData(ProviderData_ShortName).toString());
+
+ /* Update profiles: */
+ populateProfiles(m_pProfileComboBox, wizard()->notificationCenter(), wizard()->providerShortName(), wizard()->profileName());
+ sltHandleProfileComboChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardAddCloudVMPageSource::sltHandleProfileComboChange()
+{
+ /* Update wizard fields: */
+ wizard()->setProfileName(m_pProfileComboBox->currentData(ProfileData_Name).toString());
+ wizard()->setClient(cloudClientByName(wizard()->providerShortName(), wizard()->profileName(), wizard()->notificationCenter()));
+
+ /* Update profile instances: */
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ populateProfileInstances(m_pSourceInstanceList, wizard()->notificationCenter(), wizard()->client());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ sltHandleSourceInstanceChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardAddCloudVMPageSource::sltHandleProfileButtonClick()
+{
+ if (gpManager)
+ gpManager->openCloudProfileManager();
+}
+
+void UIWizardAddCloudVMPageSource::sltHandleSourceInstanceChange()
+{
+ /* Update wizard fields: */
+ wizard()->setInstanceIds(currentListWidgetData(m_pSourceInstanceList));
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageSource.h b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageSource.h
new file mode 100644
index 00000000..c31ca7ef
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/addcloudvm/UIWizardAddCloudVMPageSource.h
@@ -0,0 +1,153 @@
+/* $Id: UIWizardAddCloudVMPageSource.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardAddCloudVMPageSource class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_addcloudvm_UIWizardAddCloudVMPageSource_h
+#define FEQT_INCLUDED_SRC_wizards_addcloudvm_UIWizardAddCloudVMPageSource_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudClient.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QListWidget;
+class QIComboBox;
+class QIRichTextLabel;
+class QIToolButton;
+class UINotificationCenter;
+class UIWizardAddCloudVM;
+
+/** Provider combo data fields. */
+enum
+{
+ ProviderData_Name = Qt::UserRole + 1,
+ ProviderData_ShortName = Qt::UserRole + 2
+};
+
+/** Profile combo data fields. */
+enum
+{
+ ProfileData_Name = Qt::UserRole + 1
+};
+
+/** Namespace for source page of the Add Cloud VM wizard. */
+namespace UIWizardAddCloudVMSource
+{
+ /** Populates @a pCombo with known providers. */
+ void populateProviders(QIComboBox *pCombo, UINotificationCenter *pCenter);
+ /** Populates @a pCombo with known profiles.
+ * @param strProviderShortName Brings the short name of provider profiles related to.
+ * @param strProfileName Brings the name of profile to be chosen by default. */
+ void populateProfiles(QIComboBox *pCombo, UINotificationCenter *pCenter, const QString &strProviderShortName, const QString &strProfileName);
+ /** Populates @a pList with profile instances available in @a comClient. */
+ void populateProfileInstances(QListWidget *pList, UINotificationCenter *pCenter, const CCloudClient &comClient);
+
+ /** Updates @a pCombo tool-tips. */
+ void updateComboToolTip(QIComboBox *pCombo);
+
+ /** Returns current user data for @a pList specified. */
+ QStringList currentListWidgetData(QListWidget *pList);
+}
+
+/** UINativeWizardPage extension for source page of the Add Cloud VM wizard,
+ * based on UIWizardAddCloudVMSource namespace functions. */
+class UIWizardAddCloudVMPageSource : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs source page. */
+ UIWizardAddCloudVMPageSource();
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardAddCloudVM *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Handles change in provider combo-box. */
+ void sltHandleProviderComboChange();
+
+ /** Handles change in profile combo-box. */
+ void sltHandleProfileComboChange();
+ /** Handles profile tool-button click. */
+ void sltHandleProfileButtonClick();
+
+ /** Handles change in instance list. */
+ void sltHandleSourceInstanceChange();
+
+private:
+
+ /** Holds the main label instance. */
+ QIRichTextLabel *m_pLabelMain;
+
+ /** Holds the provider layout instance. */
+ QGridLayout *m_pProviderLayout;
+ /** Holds the provider type label instance. */
+ QLabel *m_pProviderLabel;
+ /** Holds the provider type combo-box instance. */
+ QIComboBox *m_pProviderComboBox;
+
+ /** Holds the description label instance. */
+ QIRichTextLabel *m_pLabelDescription;
+
+ /** Holds the options layout instance. */
+ QGridLayout *m_pOptionsLayout;
+ /** Holds the profile label instance. */
+ QLabel *m_pProfileLabel;
+ /** Holds the profile combo-box instance. */
+ QIComboBox *m_pProfileComboBox;
+ /** Holds the profile management tool-button instance. */
+ QIToolButton *m_pProfileToolButton;
+ /** Holds the source instance label instance. */
+ QLabel *m_pSourceInstanceLabel;
+ /** Holds the source instance list instance. */
+ QListWidget *m_pSourceInstanceList;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_addcloudvm_UIWizardAddCloudVMPageSource_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVD.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVD.cpp
new file mode 100644
index 00000000..e518eb3b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVD.cpp
@@ -0,0 +1,217 @@
+/* $Id: UIWizardCloneVD.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVD class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIMedium.h"
+#include "UINotificationCenter.h"
+#include "UIWizardCloneVD.h"
+#include "UIWizardCloneVDFormatPage.h"
+#include "UIWizardCloneVDVariantPage.h"
+#include "UIWizardCloneVDPathSizePage.h"
+#include "UIWizardCloneVDExpertPage.h"
+
+/* COM includes: */
+#include "CMediumFormat.h"
+
+UIWizardCloneVD::UIWizardCloneVD(QWidget *pParent, const CMedium &comSourceVirtualDisk)
+ : UINativeWizard(pParent, WizardType_CloneVD)
+ , m_comSourceVirtualDisk(comSourceVirtualDisk)
+ , m_enmDeviceType(m_comSourceVirtualDisk.GetDeviceType())
+ , m_iMediumVariantPageIndex(-1)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign watermark: */
+ setPixmapName(":/wizard_new_harddisk.png");
+#else /* VBOX_WS_MAC */
+ /* Assign background image: */
+ setPixmapName(":/wizard_new_harddisk_bg.png");
+#endif /* VBOX_WS_MAC */
+}
+
+const CMedium &UIWizardCloneVD::sourceVirtualDisk() const
+{
+ return m_comSourceVirtualDisk;
+}
+
+KDeviceType UIWizardCloneVD::deviceType() const
+{
+ return m_enmDeviceType;
+}
+
+bool UIWizardCloneVD::copyVirtualDisk()
+{
+ /* Check attributes: */
+ AssertReturn(!m_strMediumPath.isNull(), false);
+ AssertReturn(m_uMediumSize > 0, false);
+
+ /* Get VBox object: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Create new virtual disk image: */
+ CMedium comVirtualDisk = comVBox.CreateMedium(m_comMediumFormat.GetName(), m_strMediumPath, KAccessMode_ReadWrite, m_enmDeviceType);
+ if (!comVBox.isOk())
+ {
+ UINotificationMessage::cannotCreateMediumStorage(comVBox, m_strMediumPath, notificationCenter());
+ return false;
+ }
+
+ /* Compose medium-variant: */
+ QVector<KMediumVariant> variants(sizeof(qulonglong) * 8);
+ for (int i = 0; i < variants.size(); ++i)
+ {
+ qulonglong temp = m_uMediumVariant;
+ temp &= Q_UINT64_C(1) << i;
+ variants[i] = (KMediumVariant)temp;
+ }
+
+ /* Copy medium: */
+ UINotificationProgressMediumCopy *pNotification = new UINotificationProgressMediumCopy(m_comSourceVirtualDisk,
+ comVirtualDisk,
+ variants);
+ connect(pNotification, &UINotificationProgressMediumCopy::sigMediumCopied,
+ &uiCommon(), &UICommon::sltHandleMediumCreated);
+ gpNotificationCenter->append(pNotification);
+
+ /* Positive: */
+ return true;
+}
+
+void UIWizardCloneVD::retranslateUi()
+{
+ /* Translate wizard: */
+ setWindowTitle(tr("Copy Virtual Disk"));
+ UINativeWizard::retranslateUi();
+}
+
+void UIWizardCloneVD::populatePages()
+{
+ /* Create corresponding pages: */
+ switch (mode())
+ {
+ case WizardMode_Basic:
+
+ {
+ addPage(new UIWizardCloneVDFormatPage(m_enmDeviceType));
+ m_iMediumVariantPageIndex = addPage(new UIWizardCloneVDVariantPage);
+ addPage(new UIWizardCloneVDPathSizePage(sourceDiskLogicalSize()));
+ break;
+ }
+ case WizardMode_Expert:
+ {
+ addPage(new UIWizardCloneVDExpertPage(m_enmDeviceType, sourceDiskLogicalSize()));
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid mode: %d", mode()));
+ break;
+ }
+ }
+}
+
+const CMediumFormat &UIWizardCloneVD::mediumFormat() const
+{
+ return m_comMediumFormat;
+}
+
+void UIWizardCloneVD::setMediumFormat(const CMediumFormat &comMediumFormat)
+{
+ m_comMediumFormat = comMediumFormat;
+ if (mode() == WizardMode_Basic)
+ setMediumVariantPageVisibility();
+}
+
+qulonglong UIWizardCloneVD::mediumVariant() const
+{
+ return m_uMediumVariant;
+}
+
+void UIWizardCloneVD::setMediumVariant(qulonglong uMediumVariant)
+{
+ m_uMediumVariant = uMediumVariant;
+}
+
+qulonglong UIWizardCloneVD::mediumSize() const
+{
+ return m_uMediumSize;
+}
+
+void UIWizardCloneVD::setMediumSize(qulonglong uMediumSize)
+{
+ m_uMediumSize = uMediumSize;
+}
+
+const QString &UIWizardCloneVD::mediumPath() const
+{
+ return m_strMediumPath;
+}
+
+void UIWizardCloneVD::setMediumPath(const QString &strPath)
+{
+ m_strMediumPath = strPath;
+}
+
+qulonglong UIWizardCloneVD::sourceDiskLogicalSize() const
+{
+ if (m_comSourceVirtualDisk.isNull())
+ return 0;
+ return m_comSourceVirtualDisk.GetLogicalSize();
+}
+
+QString UIWizardCloneVD::sourceDiskFilePath() const
+{
+ if (m_comSourceVirtualDisk.isNull())
+ return QString();
+ return m_comSourceVirtualDisk.GetLocation();
+}
+
+QString UIWizardCloneVD::sourceDiskName() const
+{
+ if (m_comSourceVirtualDisk.isNull())
+ return QString();
+ return m_comSourceVirtualDisk.GetName();
+}
+
+void UIWizardCloneVD::setMediumVariantPageVisibility()
+{
+ AssertReturnVoid(!m_comMediumFormat.isNull());
+ ULONG uCapabilities = 0;
+ QVector<KMediumFormatCapabilities> capabilities;
+ capabilities = m_comMediumFormat.GetCapabilities();
+ for (int i = 0; i < capabilities.size(); i++)
+ uCapabilities |= capabilities[i];
+
+ int cTest = 0;
+ if (uCapabilities & KMediumFormatCapabilities_CreateDynamic)
+ ++cTest;
+ if (uCapabilities & KMediumFormatCapabilities_CreateFixed)
+ ++cTest;
+ if (uCapabilities & KMediumFormatCapabilities_CreateSplit2G)
+ ++cTest;
+ setPageVisible(m_iMediumVariantPageIndex, cTest > 1);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVD.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVD.h
new file mode 100644
index 00000000..a37836c8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVD.h
@@ -0,0 +1,106 @@
+/* $Id: UIWizardCloneVD.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVD class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVD_h
+#define FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVD_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMedium.h"
+#include "CMediumFormat.h"
+
+/** Clone Virtual Disk wizard: */
+class UIWizardCloneVD : public UINativeWizard
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs wizard to clone @a comSourceVirtualDisk passing @a pParent to the base-class. */
+ UIWizardCloneVD(QWidget *pParent, const CMedium &comSourceVirtualDisk);
+
+ /** Returns source virtual-disk. */
+ const CMedium &sourceVirtualDisk() const;
+
+ /** Makes a copy of source virtual-disk. */
+ bool copyVirtualDisk();
+
+ /** @name Parameter setter/getters
+ * @{ */
+ /** Returns the source virtual-disk device type. */
+ KDeviceType deviceType() const;
+
+ const CMediumFormat &mediumFormat() const;
+ void setMediumFormat(const CMediumFormat &comMediumFormat);
+
+ qulonglong mediumVariant() const;
+ void setMediumVariant(qulonglong uMediumVariant);
+
+ qulonglong mediumSize() const;
+ void setMediumSize(qulonglong uMediumSize);
+
+ const QString &mediumPath() const;
+ void setMediumPath(const QString &strPath);
+
+ qulonglong sourceDiskLogicalSize() const;
+ QString sourceDiskFilePath() const;
+ QString sourceDiskName() const;
+ /** @} */
+
+protected:
+
+ virtual void populatePages() /* final override */;
+
+private:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ void setMediumVariantPageVisibility();
+
+ /** @name Parameters needed during medium cloning
+ * @{ */
+ CMediumFormat m_comMediumFormat;
+ qulonglong m_uMediumVariant;
+ /** Holds the source virtual disk wrapper. */
+ CMedium m_comSourceVirtualDisk;
+
+ /** Holds the source virtual-disk device type. */
+ KDeviceType m_enmDeviceType;
+ int m_iMediumVariantPageIndex;
+ qulonglong m_uMediumSize;
+ QString m_strMediumPath;
+ QString m_strSourceDiskPath;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVD_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDExpertPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDExpertPage.cpp
new file mode 100644
index 00000000..a9cbcc8c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDExpertPage.cpp
@@ -0,0 +1,220 @@
+/* $Id: UIWizardCloneVDExpertPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVDExpertPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QGridLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UINotificationCenter.h"
+#include "UIWizardCloneVD.h"
+#include "UIWizardCloneVDExpertPage.h"
+#include "UIWizardDiskEditors.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+UIWizardCloneVDExpertPage::UIWizardCloneVDExpertPage(KDeviceType enmDeviceType, qulonglong uSourceDiskLogicaSize)
+ : m_pFormatComboBox(0)
+ , m_pVariantWidget(0)
+ , m_pMediumSizePathGroupBox(0)
+ , m_pFormatVariantGroupBox(0)
+ , m_enmDeviceType(enmDeviceType)
+{
+ prepare(enmDeviceType, uSourceDiskLogicaSize);
+}
+
+void UIWizardCloneVDExpertPage::prepare(KDeviceType enmDeviceType, qulonglong uSourceDiskLogicaSize)
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+
+ m_pMediumSizePathGroupBox = new UIMediumSizeAndPathGroupBox(true /* expert mode */, 0 /* parent */, uSourceDiskLogicaSize);
+
+ if (m_pMediumSizePathGroupBox)
+ {
+ pMainLayout->addWidget(m_pMediumSizePathGroupBox);
+ connect(m_pMediumSizePathGroupBox, &UIMediumSizeAndPathGroupBox::sigMediumLocationButtonClicked,
+ this, &UIWizardCloneVDExpertPage::sltSelectLocationButtonClicked);
+ connect(m_pMediumSizePathGroupBox, &UIMediumSizeAndPathGroupBox::sigMediumPathChanged,
+ this, &UIWizardCloneVDExpertPage::sltMediumPathChanged);
+ connect(m_pMediumSizePathGroupBox, &UIMediumSizeAndPathGroupBox::sigMediumSizeChanged,
+ this, &UIWizardCloneVDExpertPage::sltMediumSizeChanged);
+ }
+
+ m_pFormatComboBox = new UIDiskFormatsComboBox(true /* expert mode */, enmDeviceType, 0);
+ if (m_pFormatComboBox)
+ connect(m_pFormatComboBox, &UIDiskFormatsComboBox::sigMediumFormatChanged,
+ this, &UIWizardCloneVDExpertPage::sltMediumFormatChanged);
+
+ m_pVariantWidget = new UIDiskVariantWidget(0);
+ if (m_pVariantWidget)
+ connect(m_pVariantWidget, &UIDiskVariantWidget::sigMediumVariantChanged,
+ this, &UIWizardCloneVDExpertPage::sltMediumVariantChanged);
+
+ m_pFormatVariantGroupBox = new QGroupBox;
+ if (m_pFormatVariantGroupBox)
+ {
+ QHBoxLayout *pFormatVariantLayout = new QHBoxLayout(m_pFormatVariantGroupBox);
+ pFormatVariantLayout->addWidget(m_pFormatComboBox, 0, Qt::AlignTop);
+ pFormatVariantLayout->addWidget(m_pVariantWidget);
+ pMainLayout->addWidget(m_pFormatVariantGroupBox);
+ }
+}
+
+void UIWizardCloneVDExpertPage::sltMediumFormatChanged()
+{
+ if (wizardWindow<UIWizardCloneVD>() && m_pFormatComboBox)
+ wizardWindow<UIWizardCloneVD>()->setMediumFormat(m_pFormatComboBox->mediumFormat());
+ updateDiskWidgetsAfterMediumFormatChange();
+ emit completeChanged();
+}
+
+void UIWizardCloneVDExpertPage::sltSelectLocationButtonClicked()
+{
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ AssertReturnVoid(pWizard);
+ CMediumFormat comMediumFormat(pWizard->mediumFormat());
+ QString strSelectedPath =
+ UIWizardDiskEditors::openFileDialogForDiskFile(pWizard->mediumPath(), comMediumFormat, pWizard->deviceType(), pWizard);
+
+ if (strSelectedPath.isEmpty())
+ return;
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(strSelectedPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), pWizard->deviceType()));
+ QFileInfo mediumPath(strMediumPath);
+ m_pMediumSizePathGroupBox->setMediumFilePath(QDir::toNativeSeparators(mediumPath.absoluteFilePath()));
+}
+
+void UIWizardCloneVDExpertPage::sltMediumVariantChanged(qulonglong uVariant)
+{
+ if (wizardWindow<UIWizardCloneVD>())
+ wizardWindow<UIWizardCloneVD>()->setMediumVariant(uVariant);
+}
+
+void UIWizardCloneVDExpertPage::sltMediumSizeChanged(qulonglong uSize)
+{
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ AssertReturnVoid(pWizard);
+ pWizard->setMediumSize(uSize);
+ emit completeChanged();
+}
+
+void UIWizardCloneVDExpertPage::sltMediumPathChanged(const QString &strPath)
+{
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ AssertReturnVoid(pWizard);
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(strPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), pWizard->deviceType()));
+ pWizard->setMediumPath(strMediumPath);
+ emit completeChanged();
+}
+
+void UIWizardCloneVDExpertPage::retranslateUi()
+{
+ if (m_pFormatVariantGroupBox)
+ m_pFormatVariantGroupBox->setTitle(UIWizardCloneVD::tr("Hard Disk File &Type and Variant"));
+}
+
+void UIWizardCloneVDExpertPage::initializePage()
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVD>() && m_pMediumSizePathGroupBox && m_pFormatComboBox && m_pVariantWidget);
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+
+ pWizard->setMediumFormat(m_pFormatComboBox->mediumFormat());
+
+ pWizard->setMediumVariant(m_pVariantWidget->mediumVariant());
+ m_pVariantWidget->updateMediumVariantWidgetsAfterFormatChange(pWizard->mediumFormat());
+
+ /* Initialize medium size widget and wizard's medium size parameter: */
+ m_pMediumSizePathGroupBox->blockSignals(true);
+ m_pMediumSizePathGroupBox->setMediumSize(pWizard->sourceDiskLogicalSize());
+ pWizard->setMediumSize(m_pMediumSizePathGroupBox->mediumSize());
+ QString strExtension = UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), pWizard->deviceType());
+ QString strSourceDiskPath = QDir::toNativeSeparators(QFileInfo(pWizard->sourceDiskFilePath()).absolutePath());
+ /* Disk name without the format extension: */
+ QString strDiskName = QString("%1_%2").arg(QFileInfo(pWizard->sourceDiskName()).completeBaseName()).arg(UIWizardCloneVD::tr("copy"));
+ QString strMediumFilePath =
+ UIWizardDiskEditors::constructMediumFilePath(UIWizardDiskEditors::appendExtension(strDiskName,
+ strExtension), strSourceDiskPath);
+ m_pMediumSizePathGroupBox->setMediumFilePath(strMediumFilePath);
+ pWizard->setMediumPath(strMediumFilePath);
+ m_pMediumSizePathGroupBox->blockSignals(false);
+
+ /* Translate page: */
+ retranslateUi();
+}
+
+bool UIWizardCloneVDExpertPage::isComplete() const
+{
+ bool fResult = true;
+
+ if (m_pFormatComboBox)
+ fResult = m_pFormatComboBox->mediumFormat().isNull();
+ if (m_pVariantWidget)
+ fResult = m_pVariantWidget->isComplete();
+ if (m_pMediumSizePathGroupBox)
+ fResult = m_pMediumSizePathGroupBox->isComplete();
+
+ return fResult;
+}
+
+bool UIWizardCloneVDExpertPage::validatePage()
+{
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ AssertReturn(pWizard, false);
+
+ QString strMediumPath(pWizard->mediumPath());
+
+ if (QFileInfo(strMediumPath).exists())
+ {
+ UINotificationMessage::cannotOverwriteMediumStorage(strMediumPath, wizard()->notificationCenter());
+ return false;
+ }
+ return pWizard->copyVirtualDisk();
+}
+
+void UIWizardCloneVDExpertPage::updateDiskWidgetsAfterMediumFormatChange()
+{
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ AssertReturnVoid(pWizard && m_pVariantWidget && m_pMediumSizePathGroupBox && m_pFormatComboBox);
+ const CMediumFormat &comMediumFormat = pWizard->mediumFormat();
+ AssertReturnVoid(!comMediumFormat.isNull());
+
+ m_pVariantWidget->blockSignals(true);
+ m_pVariantWidget->updateMediumVariantWidgetsAfterFormatChange(comMediumFormat);
+ m_pVariantWidget->blockSignals(false);
+
+ m_pMediumSizePathGroupBox->blockSignals(true);
+ m_pMediumSizePathGroupBox->updateMediumPath(comMediumFormat, m_pFormatComboBox->formatExtensions(), m_enmDeviceType);
+ m_pMediumSizePathGroupBox->blockSignals(false);
+ /* Update the wizard parameters explicitly since we blocked th signals: */
+ pWizard->setMediumPath(m_pMediumSizePathGroupBox->mediumFilePath());
+ pWizard->setMediumVariant(m_pVariantWidget->mediumVariant());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDExpertPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDExpertPage.h
new file mode 100644
index 00000000..b6f73cec
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDExpertPage.h
@@ -0,0 +1,97 @@
+/* $Id: UIWizardCloneVDExpertPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVDExpertPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDExpertPage_h
+#define FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDExpertPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QGroupBox;
+class UIDiskFormatsComboBox;
+class UIDiskVariantWidget;
+class UIMediumSizeAndPathGroupBox;
+
+
+/** Expert page of the Clone Virtual Disk Image wizard: */
+class UIWizardCloneVDExpertPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs the page.
+ * @param comSourceVirtualDisk Brings the initial source disk to make copy from.
+ * @param enmDeviceType Brings the device type to limit format to. */
+ UIWizardCloneVDExpertPage(KDeviceType enmDeviceType, qulonglong uSourceDiskLogicaSize);
+
+private slots:
+
+ /** Handles medium format change. */
+ void sltMediumFormatChanged();
+
+ /** Handles target disk change. */
+ void sltSelectLocationButtonClicked();
+ void sltMediumPathChanged(const QString &strPath);
+ void sltMediumVariantChanged(qulonglong uVariant);
+ void sltMediumSizeChanged(qulonglong uSize);
+
+private:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+ /** Prepares the page. */
+ virtual void initializePage() RT_OVERRIDE;
+
+ /** Returns whether the page is complete. */
+ virtual bool isComplete() const RT_OVERRIDE;
+
+ /** Returns whether the page is valid. */
+ virtual bool validatePage() RT_OVERRIDE;
+
+ void prepare(KDeviceType enmDeviceType, qulonglong uSourceDiskLogicaSize);
+
+ /** Sets the target disk name and location. */
+ void setTargetLocation();
+ void updateDiskWidgetsAfterMediumFormatChange();
+
+ UIDiskFormatsComboBox *m_pFormatComboBox;
+ UIDiskVariantWidget *m_pVariantWidget;
+ UIMediumSizeAndPathGroupBox *m_pMediumSizePathGroupBox;
+ QGroupBox *m_pFormatVariantGroupBox;
+ KDeviceType m_enmDeviceType;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDExpertPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDFormatPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDFormatPage.cpp
new file mode 100644
index 00000000..3d1982d4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDFormatPage.cpp
@@ -0,0 +1,105 @@
+/* $Id: UIWizardCloneVDFormatPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVDFormatPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIWizardCloneVDFormatPage.h"
+#include "UIWizardCloneVD.h"
+#include "UIWizardDiskEditors.h"
+#include "UICommon.h"
+#include "QIRichTextLabel.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+UIWizardCloneVDFormatPage::UIWizardCloneVDFormatPage(KDeviceType enmDeviceType)
+ : m_pLabel(0)
+ , m_pFormatGroupBox(0)
+{
+ prepare(enmDeviceType);
+}
+
+void UIWizardCloneVDFormatPage::prepare(KDeviceType enmDeviceType)
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ m_pLabel = new QIRichTextLabel(this);
+ if (m_pLabel)
+ pMainLayout->addWidget(m_pLabel);
+ m_pFormatGroupBox = new UIDiskFormatsGroupBox(false /* expert mode */, enmDeviceType, 0);
+ if (m_pFormatGroupBox)
+ {
+ pMainLayout->addWidget(m_pFormatGroupBox);
+ connect(m_pFormatGroupBox, &UIDiskFormatsGroupBox::sigMediumFormatChanged,
+ this, &UIWizardCloneVDFormatPage::sltMediumFormatChanged);
+ }
+ pMainLayout->addStretch();
+ retranslateUi();
+}
+
+void UIWizardCloneVDFormatPage::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardCloneVD::tr("Virtual Hard disk file type"));
+
+ /* Translate widgets: */
+ m_pLabel->setText(UIWizardCloneVD::tr("Please choose the type of file that you would like to use "
+ "for the destination virtual disk image. If you do not need to use it "
+ "with other virtualization software you can leave this setting unchanged."));
+}
+
+void UIWizardCloneVDFormatPage::initializePage()
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVD>());
+ /* Translate page: */
+ retranslateUi();
+ if (!m_userModifiedParameters.contains("MediumFormat"))
+ {
+ if (m_pFormatGroupBox)
+ wizardWindow<UIWizardCloneVD>()->setMediumFormat(m_pFormatGroupBox->mediumFormat());
+ }
+}
+
+bool UIWizardCloneVDFormatPage::isComplete() const
+{
+ if (m_pFormatGroupBox)
+ {
+ if (m_pFormatGroupBox->mediumFormat().isNull())
+ return false;
+ }
+ return true;
+}
+
+void UIWizardCloneVDFormatPage::sltMediumFormatChanged()
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVD>());
+ if (m_pFormatGroupBox)
+ wizardWindow<UIWizardCloneVD>()->setMediumFormat(m_pFormatGroupBox->mediumFormat());
+ m_userModifiedParameters << "MediumFormat";
+ emit completeChanged();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDFormatPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDFormatPage.h
new file mode 100644
index 00000000..9c3b61d8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDFormatPage.h
@@ -0,0 +1,81 @@
+/* $Id: UIWizardCloneVDFormatPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVDFormatPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDFormatPage_h
+#define FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDFormatPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSet>
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class QIRichTextLabel;
+class UIDiskFormatsGroupBox;
+
+/** 2nd page of the Clone Virtual Disk Image wizard (basic extension): */
+class UIWizardCloneVDFormatPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs basic page.
+ * @param enmDeviceType Brings the device type to limit format to. */
+ UIWizardCloneVDFormatPage(KDeviceType enmDeviceType);
+
+private slots:
+
+ void sltMediumFormatChanged();
+
+private:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ void prepare(KDeviceType enmDeviceType);
+
+ /** Prepares the page. */
+ virtual void initializePage() RT_OVERRIDE;
+
+ /** Returns whether the page is complete. */
+ virtual bool isComplete() const RT_OVERRIDE;
+
+ /** Holds the description label instance. */
+ QIRichTextLabel *m_pLabel;
+ UIDiskFormatsGroupBox *m_pFormatGroupBox;
+
+ QSet<QString> m_userModifiedParameters;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDFormatPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDPathSizePage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDPathSizePage.cpp
new file mode 100644
index 00000000..ab3dbbe2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDPathSizePage.cpp
@@ -0,0 +1,155 @@
+/* $Id: UIWizardCloneVDPathSizePage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVDPathSizePage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UINotificationCenter.h"
+#include "UIWizardCloneVDPathSizePage.h"
+#include "UIWizardDiskEditors.h"
+#include "UIWizardCloneVD.h"
+
+UIWizardCloneVDPathSizePage::UIWizardCloneVDPathSizePage(qulonglong uSourceDiskLogicaSize)
+ : m_pMediumSizePathGroupBox(0)
+{
+ prepare(uSourceDiskLogicaSize);
+}
+
+void UIWizardCloneVDPathSizePage::prepare(qulonglong uSourceDiskLogicaSize)
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ m_pMediumSizePathGroupBox = new UIMediumSizeAndPathGroupBox(false /* expert mode */, 0 /* parent */, uSourceDiskLogicaSize);
+ if (m_pMediumSizePathGroupBox)
+ {
+ pMainLayout->addWidget(m_pMediumSizePathGroupBox);
+ connect(m_pMediumSizePathGroupBox, &UIMediumSizeAndPathGroupBox::sigMediumLocationButtonClicked,
+ this, &UIWizardCloneVDPathSizePage::sltSelectLocationButtonClicked);
+ connect(m_pMediumSizePathGroupBox, &UIMediumSizeAndPathGroupBox::sigMediumPathChanged,
+ this, &UIWizardCloneVDPathSizePage::sltMediumPathChanged);
+ connect(m_pMediumSizePathGroupBox, &UIMediumSizeAndPathGroupBox::sigMediumSizeChanged,
+ this, &UIWizardCloneVDPathSizePage::sltMediumSizeChanged);
+ }
+ pMainLayout->addStretch();
+ retranslateUi();
+}
+
+void UIWizardCloneVDPathSizePage::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardCloneVD::tr("Location and size of the disk image"));
+}
+
+void UIWizardCloneVDPathSizePage::initializePage()
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVD>() && m_pMediumSizePathGroupBox);
+ /* Translate page: */
+ retranslateUi();
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ m_pMediumSizePathGroupBox->blockSignals(true);
+
+ /* Initialize medium size widget and wizard's medium size parameter: */
+ if (!m_userModifiedParameters.contains("MediumSize"))
+ {
+ m_pMediumSizePathGroupBox->setMediumSize(pWizard->sourceDiskLogicalSize());
+ pWizard->setMediumSize(m_pMediumSizePathGroupBox->mediumSize());
+ }
+
+ if (!m_userModifiedParameters.contains("MediumPath"))
+ {
+ QString strExtension = UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), pWizard->deviceType());
+ QString strSourceDiskPath = QDir::toNativeSeparators(QFileInfo(pWizard->sourceDiskFilePath()).absolutePath());
+ /* Disk name without the format extension: */
+ QString strDiskName = QString("%1_%2").arg(QFileInfo(pWizard->sourceDiskName()).completeBaseName()).arg(UIWizardCloneVD::tr("copy"));
+
+ QString strMediumFilePath =
+ UIWizardDiskEditors::constructMediumFilePath(UIWizardDiskEditors::appendExtension(strDiskName,
+ strExtension), strSourceDiskPath);
+ m_pMediumSizePathGroupBox->setMediumFilePath(strMediumFilePath);
+ pWizard->setMediumPath(strMediumFilePath);
+ }
+ m_pMediumSizePathGroupBox->blockSignals(false);
+}
+
+bool UIWizardCloneVDPathSizePage::isComplete() const
+{
+ AssertReturn(m_pMediumSizePathGroupBox, false);
+ return m_pMediumSizePathGroupBox->isComplete();
+}
+
+bool UIWizardCloneVDPathSizePage::validatePage()
+{
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ AssertReturn(pWizard, false);
+ /* Make sure such file doesn't exists already: */
+ QString strMediumPath(pWizard->mediumPath());
+ if (QFileInfo(strMediumPath).exists())
+ {
+ UINotificationMessage::cannotOverwriteMediumStorage(strMediumPath, wizard()->notificationCenter());
+ return false;
+ }
+ return pWizard->copyVirtualDisk();
+}
+
+void UIWizardCloneVDPathSizePage::sltSelectLocationButtonClicked()
+{
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ AssertReturnVoid(pWizard);
+ CMediumFormat comMediumFormat(pWizard->mediumFormat());
+ QString strSelectedPath =
+ UIWizardDiskEditors::openFileDialogForDiskFile(pWizard->mediumPath(), comMediumFormat, pWizard->deviceType(), pWizard);
+
+ if (strSelectedPath.isEmpty())
+ return;
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(strSelectedPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), pWizard->deviceType()));
+ QFileInfo mediumPath(strMediumPath);
+ m_pMediumSizePathGroupBox->setMediumFilePath(QDir::toNativeSeparators(mediumPath.absoluteFilePath()));
+}
+
+void UIWizardCloneVDPathSizePage::sltMediumPathChanged(const QString &strPath)
+{
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ AssertReturnVoid(pWizard);
+ m_userModifiedParameters << "MediumPath";
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(strPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), pWizard->deviceType()));
+ pWizard->setMediumPath(strMediumPath);
+ emit completeChanged();
+}
+
+void UIWizardCloneVDPathSizePage::sltMediumSizeChanged(qulonglong uSize)
+{
+ UIWizardCloneVD *pWizard = wizardWindow<UIWizardCloneVD>();
+ AssertReturnVoid(pWizard);
+ m_userModifiedParameters << "MediumSize";
+ pWizard->setMediumSize(uSize);
+ emit completeChanged();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDPathSizePage.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDPathSizePage.h
new file mode 100644
index 00000000..23226cdc
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDPathSizePage.h
@@ -0,0 +1,83 @@
+/* $Id: UIWizardCloneVDPathSizePage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVDPathSizePage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDPathSizePage_h
+#define FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDPathSizePage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QVariant>
+#include <QSet>
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declarations: */
+class UIMediumSizeAndPathGroupBox;
+
+/** 4th page of the Clone Virtual Disk Image wizard (basic extension): */
+class UIWizardCloneVDPathSizePage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs basic page. */
+ UIWizardCloneVDPathSizePage(qulonglong uSourceDiskLogicaSize);
+
+private slots:
+
+ /** Handles command to open target disk. */
+ void sltSelectLocationButtonClicked();
+ void sltMediumPathChanged(const QString &strPath);
+ void sltMediumSizeChanged(qulonglong uSize);
+
+private:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ void prepare(qulonglong uSourceDiskLogicaSize);
+
+ /** Prepares the page. */
+ virtual void initializePage() RT_OVERRIDE;
+
+ /** Returns whether the page is complete. */
+ virtual bool isComplete() const RT_OVERRIDE;
+
+ /** Returns whether the page is valid. */
+ virtual bool validatePage() RT_OVERRIDE;
+
+ UIMediumSizeAndPathGroupBox *m_pMediumSizePathGroupBox;
+ QSet<QString> m_userModifiedParameters;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDPathSizePage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDVariantPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDVariantPage.cpp
new file mode 100644
index 00000000..e2f886f5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDVariantPage.cpp
@@ -0,0 +1,136 @@
+/* $Id: UIWizardCloneVDVariantPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVDVariantPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIWizardDiskEditors.h"
+#include "UIWizardCloneVDVariantPage.h"
+#include "UIWizardCloneVD.h"
+#include "QIRichTextLabel.h"
+
+/* COM includes: */
+#include "CMediumFormat.h"
+
+UIWizardCloneVDVariantPage::UIWizardCloneVDVariantPage()
+ : m_pDescriptionLabel(0)
+ , m_pDynamicLabel(0)
+ , m_pFixedLabel(0)
+ , m_pSplitLabel(0)
+ , m_pVariantWidget(0)
+{
+ prepare();
+}
+
+void UIWizardCloneVDVariantPage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+
+ m_pDescriptionLabel = new QIRichTextLabel(this);
+ if (m_pDescriptionLabel)
+ pMainLayout->addWidget(m_pDescriptionLabel);
+
+ m_pDynamicLabel = new QIRichTextLabel(this);
+ if (m_pDynamicLabel)
+ pMainLayout->addWidget(m_pDynamicLabel);
+
+ m_pFixedLabel = new QIRichTextLabel(this);
+ if (m_pFixedLabel)
+ pMainLayout->addWidget(m_pFixedLabel);
+
+ m_pSplitLabel = new QIRichTextLabel(this);
+ if (m_pSplitLabel)
+ pMainLayout->addWidget(m_pSplitLabel);
+
+ m_pVariantWidget = new UIDiskVariantWidget(0);
+ if (m_pVariantWidget)
+ {
+ pMainLayout->addWidget(m_pVariantWidget);
+ connect(m_pVariantWidget, &UIDiskVariantWidget::sigMediumVariantChanged,
+ this, &UIWizardCloneVDVariantPage::sltMediumVariantChanged);
+
+ }
+ retranslateUi();
+}
+
+
+void UIWizardCloneVDVariantPage::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardCloneVD::tr("Storage on physical hard disk"));
+
+ /* Translate widgets: */
+ m_pDescriptionLabel->setText(UIWizardCloneVD::tr("Please choose whether the new virtual disk image file should grow as it is used "
+ "(dynamically allocated) or if it should be created at its maximum size (fixed size)."));
+ m_pDynamicLabel->setText(UIWizardCloneVD::tr("<p>A <b>dynamically allocated</b> disk image file will only use space "
+ "on your physical hard disk as it fills up (up to a maximum <b>fixed size</b>), "
+ "although it will not shrink again automatically when space on it is freed.</p>"));
+ m_pFixedLabel->setText(UIWizardCloneVD::tr("<p>A <b>fixed size</b> disk image file may take longer to create on some "
+ "systems but is often faster to use.</p>"));
+ m_pSplitLabel->setText(UIWizardCloneVD::tr("<p>You can also choose to <b>split</b> the disk image file into several files "
+ "of up to two gigabytes each. This is mainly useful if you wish to store the "
+ "virtual machine on removable USB devices or old systems, some of which cannot "
+ "handle very large files."));
+}
+
+void UIWizardCloneVDVariantPage::initializePage()
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVD>());
+ /* Translate page: */
+ retranslateUi();
+
+ setWidgetVisibility(wizardWindow<UIWizardCloneVD>()->mediumFormat());
+ if (m_pVariantWidget)
+ wizardWindow<UIWizardCloneVD>()->setMediumVariant(m_pVariantWidget->mediumVariant());
+}
+
+bool UIWizardCloneVDVariantPage::isComplete() const
+{
+ AssertReturn(m_pVariantWidget, false);
+ return m_pVariantWidget->isComplete();
+}
+
+void UIWizardCloneVDVariantPage::setWidgetVisibility(const CMediumFormat &mediumFormat)
+{
+ AssertReturnVoid(m_pVariantWidget);
+
+ m_pVariantWidget->updateMediumVariantWidgetsAfterFormatChange(mediumFormat);
+
+ if (m_pDynamicLabel)
+ m_pDynamicLabel->setHidden(!m_pVariantWidget->isCreateDynamicPossible());
+ if (m_pFixedLabel)
+ m_pFixedLabel->setHidden(!m_pVariantWidget->isCreateFixedPossible());
+ if (m_pSplitLabel)
+ m_pSplitLabel->setHidden(!m_pVariantWidget->isCreateSplitPossible());
+}
+
+void UIWizardCloneVDVariantPage::sltMediumVariantChanged(qulonglong uVariant)
+{
+ if (wizardWindow<UIWizardCloneVD>())
+ wizardWindow<UIWizardCloneVD>()->setMediumVariant(uVariant);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDVariantPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDVariantPage.h
new file mode 100644
index 00000000..a559251e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevd/UIWizardCloneVDVariantPage.h
@@ -0,0 +1,79 @@
+/* $Id: UIWizardCloneVDVariantPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVDVariantPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDVariantPage_h
+#define FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDVariantPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QIRichTextLabel;
+class CMediumFormat;
+class UIDiskVariantWidget;
+
+class UIWizardCloneVDVariantPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs basic page. */
+ UIWizardCloneVDVariantPage();
+
+private slots:
+
+ void sltMediumVariantChanged(qulonglong uVariant);
+
+private:
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+ void prepare();
+
+ /** Prepares the page. */
+ virtual void initializePage() RT_OVERRIDE;
+
+ /** Returns whether the page is complete. */
+ virtual bool isComplete() const RT_OVERRIDE;
+ void setWidgetVisibility(const CMediumFormat &mediumFormat);
+
+ /** Holds the description label instance. */
+ QIRichTextLabel *m_pDescriptionLabel;
+ /** Holds the 'Dynamic' description label instance. */
+ QIRichTextLabel *m_pDynamicLabel;
+ /** Holds the 'Fixed' description label instance. */
+ QIRichTextLabel *m_pFixedLabel;
+ /** Holds the 'Split to 2GB files' description label instance. */
+ QIRichTextLabel *m_pSplitLabel;
+ UIDiskVariantWidget *m_pVariantWidget;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevd_UIWizardCloneVDVariantPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVM.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVM.cpp
new file mode 100644
index 00000000..a8043039
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVM.cpp
@@ -0,0 +1,272 @@
+/* $Id: UIWizardCloneVM.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVM class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UINotificationCenter.h"
+#include "UIWizardCloneVM.h"
+#include "UIWizardCloneVMNamePathPage.h"
+#include "UIWizardCloneVMTypePage.h"
+#include "UIWizardCloneVMModePage.h"
+#include "UIWizardCloneVMExpertPage.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIWizardCloneVM::UIWizardCloneVM(QWidget *pParent, const CMachine &machine,
+ const QString &strGroup, CSnapshot snapshot /* = CSnapshot() */)
+ : UINativeWizard(pParent, WizardType_CloneVM, WizardMode_Auto, "clone" /* help keyword */)
+ , m_machine(machine)
+ , m_snapshot(snapshot)
+ , m_strGroup(strGroup)
+ , m_iCloneModePageIndex(-1)
+ , m_strCloneName(!machine.isNull() ? machine.GetName() : QString())
+ , m_enmCloneMode(KCloneMode_MachineState)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign watermark: */
+ setPixmapName(":/wizard_clone.png");
+#else /* VBOX_WS_MAC */
+ /* Assign background image: */
+ setPixmapName(":/wizard_clone_bg.png");
+#endif /* VBOX_WS_MAC */
+}
+
+void UIWizardCloneVM::setCloneModePageVisible(bool fIsFullClone)
+{
+ if (m_iCloneModePageIndex == -1)
+ return;
+ setPageVisible(m_iCloneModePageIndex, fIsFullClone);
+}
+
+bool UIWizardCloneVM::isCloneModePageVisible() const
+{
+ /* If we did not create the clone mode page return false: */
+ if (m_iCloneModePageIndex == -1)
+ return false;
+ return isPageVisible(m_iCloneModePageIndex);
+}
+
+void UIWizardCloneVM::setCloneName(const QString &strCloneName)
+{
+ m_strCloneName = strCloneName;
+}
+
+const QString &UIWizardCloneVM::cloneName() const
+{
+ return m_strCloneName;
+}
+
+void UIWizardCloneVM::setCloneFilePath(const QString &strCloneFilePath)
+{
+ m_strCloneFilePath = strCloneFilePath;
+}
+
+const QString &UIWizardCloneVM::cloneFilePath() const
+{
+ return m_strCloneFilePath;
+}
+
+MACAddressClonePolicy UIWizardCloneVM::macAddressClonePolicy() const
+{
+ return m_enmMACAddressClonePolicy;
+}
+
+void UIWizardCloneVM::setMacAddressPolicy(MACAddressClonePolicy enmMACAddressClonePolicy)
+{
+ m_enmMACAddressClonePolicy = enmMACAddressClonePolicy;
+}
+
+bool UIWizardCloneVM::keepDiskNames() const
+{
+ return m_fKeepDiskNames;
+}
+
+void UIWizardCloneVM::setKeepDiskNames(bool fKeepDiskNames)
+{
+ m_fKeepDiskNames = fKeepDiskNames;
+}
+
+bool UIWizardCloneVM::keepHardwareUUIDs() const
+{
+ return m_fKeepHardwareUUIDs;
+}
+
+void UIWizardCloneVM::setKeepHardwareUUIDs(bool fKeepHardwareUUIDs)
+{
+ m_fKeepHardwareUUIDs = fKeepHardwareUUIDs;
+}
+
+bool UIWizardCloneVM::linkedClone() const
+{
+ return m_fLinkedClone;
+}
+
+void UIWizardCloneVM::setLinkedClone(bool fLinkedClone)
+{
+ m_fLinkedClone = fLinkedClone;
+}
+
+KCloneMode UIWizardCloneVM::cloneMode() const
+{
+ return m_enmCloneMode;
+}
+
+void UIWizardCloneVM::setCloneMode(KCloneMode enmCloneMode)
+{
+ m_enmCloneMode = enmCloneMode;
+}
+
+bool UIWizardCloneVM::machineHasSnapshot() const
+{
+ AssertReturn(!m_machine.isNull(), false);
+ return m_machine.GetSnapshotCount() > 0;
+}
+
+bool UIWizardCloneVM::cloneVM()
+{
+ /* Prepare machine for cloning: */
+ CMachine srcMachine = m_machine;
+
+ /* If the user like to create a linked clone from the current machine, we have to take a little bit more action.
+ * First we create an snapshot, so that new differencing images on the source VM are created. Based on that we
+ * could use the new snapshot machine for cloning. */
+ if (m_fLinkedClone && m_snapshot.isNull())
+ {
+ /* Compose snapshot name: */
+ const QString strSnapshotName = tr("Linked Base for %1 and %2").arg(m_machine.GetName()).arg(m_strCloneName);
+
+ /* Take the snapshot: */
+ UINotificationProgressSnapshotTake *pNotification = new UINotificationProgressSnapshotTake(srcMachine,
+ strSnapshotName,
+ QString());
+ UINotificationReceiver receiver;
+ connect(pNotification, &UINotificationProgressSnapshotTake::sigSnapshotTaken,
+ &receiver, &UINotificationReceiver::setReceiverProperty);
+ if (!handleNotificationProgressNow(pNotification))
+ return false;
+
+ /* Acquire created snapshot id: */
+ QUuid uSnapshotId = receiver.property("received_value").toUuid();
+
+ /* Look for created snapshot: */
+ const CSnapshot comCreatedSnapshot = m_machine.FindSnapshot(uSnapshotId.toString());
+ if (comCreatedSnapshot.isNull())
+ {
+ UINotificationMessage::cannotFindSnapshotByName(m_machine, strSnapshotName, notificationCenter());
+ return false;
+ }
+
+ /* Update machine for cloning finally: */
+ srcMachine = comCreatedSnapshot.GetMachine();
+ }
+
+ /* Get VBox object: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ /* Create a new machine object: */
+ CMachine cloneMachine = comVBox.CreateMachine(m_strCloneFilePath, m_strCloneName, QVector<QString>(), QString(), QString(),
+ QString(), QString(), QString());
+ if (!comVBox.isOk())
+ {
+ UINotificationMessage::cannotCreateMachine(comVBox, notificationCenter());
+ return false;
+ }
+
+ /* Clone options vector to pass to cloning: */
+ QVector<KCloneOptions> options;
+ /* Set the selected MAC address policy: */
+ switch (m_enmMACAddressClonePolicy)
+ {
+ case MACAddressClonePolicy_KeepAllMACs:
+ options.append(KCloneOptions_KeepAllMACs);
+ break;
+ case MACAddressClonePolicy_KeepNATMACs:
+ options.append(KCloneOptions_KeepNATMACs);
+ break;
+ default:
+ break;
+ }
+ if (m_fKeepDiskNames)
+ options.append(KCloneOptions_KeepDiskNames);
+ if (m_fKeepHardwareUUIDs)
+ options.append(KCloneOptions_KeepHwUUIDs);
+ /* Linked clones requested? */
+ if (m_fLinkedClone)
+ options.append(KCloneOptions_Link);
+
+ /* Clone VM: */
+ UINotificationProgressMachineCopy *pNotification = new UINotificationProgressMachineCopy(srcMachine,
+ cloneMachine,
+ m_enmCloneMode,
+ options);
+ connect(pNotification, &UINotificationProgressMachineCopy::sigMachineCopied,
+ &uiCommon(), &UICommon::sltHandleMachineCreated);
+ gpNotificationCenter->append(pNotification);
+
+ return true;
+}
+
+void UIWizardCloneVM::retranslateUi()
+{
+ /* Call to base-class: */
+ UINativeWizard::retranslateUi();
+
+ /* Translate wizard: */
+ setWindowTitle(tr("Clone Virtual Machine"));
+}
+
+void UIWizardCloneVM::populatePages()
+{
+ QString strDefaultMachineFolder = uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder();
+ /* Create corresponding pages: */
+ switch (mode())
+ {
+ case WizardMode_Basic:
+ {
+ addPage(new UIWizardCloneVMNamePathPage(m_strCloneName, strDefaultMachineFolder, m_strGroup));
+ addPage(new UIWizardCloneVMTypePage(m_snapshot.isNull()));
+ if (machineHasSnapshot())
+ m_iCloneModePageIndex = addPage(new UIWizardCloneVMModePage(m_snapshot.isNull() ? false : m_snapshot.GetChildrenCount() > 0));
+ break;
+ }
+ case WizardMode_Expert:
+ {
+ addPage(new UIWizardCloneVMExpertPage(m_machine.GetName(),
+ strDefaultMachineFolder,
+ m_snapshot.isNull(),
+ m_snapshot.isNull() ? false : m_snapshot.GetChildrenCount() > 0,
+ m_strGroup));
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid mode: %d", mode()));
+ break;
+ }
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVM.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVM.h
new file mode 100644
index 00000000..713f9a94
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVM.h
@@ -0,0 +1,107 @@
+/* $Id: UIWizardCloneVM.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVM class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVM_h
+#define FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVM_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+#include "UIWizardCloneVMEditors.h"
+
+/* COM includes: */
+#include "CMachine.h"
+#include "CSnapshot.h"
+
+/** Clone Virtual Machine wizard: */
+class UIWizardCloneVM : public UINativeWizard
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardCloneVM(QWidget *pParent, const CMachine &machine,
+ const QString &strGroup, CSnapshot snapshot = CSnapshot());
+ void setCloneModePageVisible(bool fIsFullClone);
+ bool isCloneModePageVisible() const;
+ /** Clone VM stuff. */
+ bool cloneVM();
+ bool machineHasSnapshot() const;
+
+ /** @name Parameter setter/getters
+ * @{ */
+ void setCloneName(const QString &strCloneName);
+ const QString &cloneName() const;
+
+ void setCloneFilePath(const QString &strCloneFilePath);
+ const QString &cloneFilePath() const;
+
+ MACAddressClonePolicy macAddressClonePolicy() const;
+ void setMacAddressPolicy(MACAddressClonePolicy enmMACAddressClonePolicy);
+
+ bool keepDiskNames() const;
+ void setKeepDiskNames(bool fKeepDiskNames);
+
+ bool keepHardwareUUIDs() const;
+ void setKeepHardwareUUIDs(bool fKeepHardwareUUIDs);
+
+ bool linkedClone() const;
+ void setLinkedClone(bool fLinkedClone);
+
+ KCloneMode cloneMode() const;
+ void setCloneMode(KCloneMode enmCloneMode);
+ /** @} */
+
+protected:
+
+ virtual void populatePages() /* final override */;
+
+private:
+
+ void retranslateUi();
+ void prepare();
+
+ CMachine m_machine;
+ CSnapshot m_snapshot;
+ QString m_strGroup;
+ int m_iCloneModePageIndex;
+
+ /** @name Parameters needed during machine cloning
+ * @{ */
+ QString m_strCloneName;
+ QString m_strCloneFilePath;
+ MACAddressClonePolicy m_enmMACAddressClonePolicy;
+ bool m_fKeepDiskNames;
+ bool m_fKeepHardwareUUIDs;
+ bool m_fLinkedClone;
+ KCloneMode m_enmCloneMode;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVM_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMExpertPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMExpertPage.cpp
new file mode 100644
index 00000000..7d533b49
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMExpertPage.cpp
@@ -0,0 +1,202 @@
+/* $Id: UIWizardCloneVMExpertPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVMExpertPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QLabel>
+#include <QRadioButton>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "UICommon.h"
+#include "UIFilePathSelector.h"
+#include "UIWizardCloneVMExpertPage.h"
+#include "UIWizardCloneVM.h"
+#include "UIWizardCloneVMNamePathPage.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+
+UIWizardCloneVMExpertPage::UIWizardCloneVMExpertPage(const QString &strOriginalName, const QString &strDefaultPath,
+ bool /*fAdditionalInfo*/, bool fShowChildsOption, const QString &strGroup)
+ : m_pNamePathGroupBox(0)
+ , m_pCloneTypeGroupBox(0)
+ , m_pCloneModeGroupBox(0)
+ , m_pAdditionalOptionsGroupBox(0)
+ , m_strGroup(strGroup)
+{
+ prepare(strOriginalName, strDefaultPath, fShowChildsOption);
+}
+
+void UIWizardCloneVMExpertPage::prepare(const QString &strOriginalName, const QString &strDefaultPath, bool fShowChildsOption)
+{
+ QGridLayout *pMainLayout = new QGridLayout(this);
+ AssertReturnVoid(pMainLayout);
+ m_pNamePathGroupBox = new UICloneVMNamePathEditor(strOriginalName, strDefaultPath);
+ if (m_pNamePathGroupBox)
+ {
+ pMainLayout->addWidget(m_pNamePathGroupBox, 0, 0, 3, 2);
+ connect(m_pNamePathGroupBox, &UICloneVMNamePathEditor::sigCloneNameChanged,
+ this, &UIWizardCloneVMExpertPage::sltCloneNameChanged);
+ connect(m_pNamePathGroupBox, &UICloneVMNamePathEditor::sigClonePathChanged,
+ this, &UIWizardCloneVMExpertPage::sltClonePathChanged);
+ }
+
+ m_pCloneTypeGroupBox = new UICloneVMCloneTypeGroupBox;
+ if (m_pCloneTypeGroupBox)
+ pMainLayout->addWidget(m_pCloneTypeGroupBox, 3, 0, 2, 1);
+
+ m_pCloneModeGroupBox = new UICloneVMCloneModeGroupBox(fShowChildsOption);
+ if (m_pCloneModeGroupBox)
+ pMainLayout->addWidget(m_pCloneModeGroupBox, 3, 1, 2, 1);
+
+ m_pAdditionalOptionsGroupBox = new UICloneVMAdditionalOptionsEditor;
+ if (m_pAdditionalOptionsGroupBox)
+ {
+ pMainLayout->addWidget(m_pAdditionalOptionsGroupBox, 5, 0, 2, 2);
+ connect(m_pAdditionalOptionsGroupBox, &UICloneVMAdditionalOptionsEditor::sigMACAddressClonePolicyChanged,
+ this, &UIWizardCloneVMExpertPage::sltMACAddressClonePolicyChanged);
+ connect(m_pAdditionalOptionsGroupBox, &UICloneVMAdditionalOptionsEditor::sigKeepDiskNamesToggled,
+ this, &UIWizardCloneVMExpertPage::sltKeepDiskNamesToggled);
+ connect(m_pAdditionalOptionsGroupBox, &UICloneVMAdditionalOptionsEditor::sigKeepHardwareUUIDsToggled,
+ this, &UIWizardCloneVMExpertPage::sltKeepHardwareUUIDsToggled);
+ }
+ if (m_pCloneTypeGroupBox)
+ connect(m_pCloneTypeGroupBox, &UICloneVMCloneTypeGroupBox::sigFullCloneSelected,
+ this, &UIWizardCloneVMExpertPage::sltCloneTypeChanged);
+
+ retranslateUi();
+}
+
+void UIWizardCloneVMExpertPage::retranslateUi()
+{
+ /* Translate widgets: */
+ if (m_pNamePathGroupBox)
+ m_pNamePathGroupBox->setTitle(UIWizardCloneVM::tr("New machine &name and path"));
+ if (m_pCloneTypeGroupBox)
+ m_pCloneTypeGroupBox->setTitle(UIWizardCloneVM::tr("Clone type"));
+ if (m_pCloneModeGroupBox)
+ m_pCloneModeGroupBox->setTitle(UIWizardCloneVM::tr("Snapshots"));
+ if (m_pAdditionalOptionsGroupBox)
+ m_pAdditionalOptionsGroupBox->setTitle(UIWizardCloneVM::tr("Additional options"));
+}
+
+void UIWizardCloneVMExpertPage::initializePage()
+{
+ UIWizardCloneVM *pWizard = wizardWindow<UIWizardCloneVM>();
+ AssertReturnVoid(pWizard);
+ if (m_pNamePathGroupBox)
+ {
+ m_pNamePathGroupBox->setFocus();
+ pWizard->setCloneName(m_pNamePathGroupBox->cloneName());
+ pWizard->setCloneFilePath(
+ UIWizardCloneVMNamePathCommon::composeCloneFilePath(m_pNamePathGroupBox->cloneName(), m_strGroup, m_pNamePathGroupBox->clonePath()));
+ }
+ if (m_pAdditionalOptionsGroupBox)
+ {
+ pWizard->setMacAddressPolicy(m_pAdditionalOptionsGroupBox->macAddressClonePolicy());
+ pWizard->setKeepDiskNames(m_pAdditionalOptionsGroupBox->keepDiskNames());
+ pWizard->setKeepHardwareUUIDs(m_pAdditionalOptionsGroupBox->keepHardwareUUIDs());
+ }
+ if (m_pCloneTypeGroupBox)
+ pWizard->setLinkedClone(!m_pCloneTypeGroupBox->isFullClone());
+ if (m_pCloneModeGroupBox)
+ pWizard->setCloneMode(m_pCloneModeGroupBox->cloneMode());
+
+ setCloneModeGroupBoxEnabled();
+
+ retranslateUi();
+}
+
+void UIWizardCloneVMExpertPage::setCloneModeGroupBoxEnabled()
+{
+ UIWizardCloneVM *pWizard = wizardWindow<UIWizardCloneVM>();
+ AssertReturnVoid(pWizard);
+
+ if (m_pCloneModeGroupBox)
+ m_pCloneModeGroupBox->setEnabled(pWizard->machineHasSnapshot() && !pWizard->linkedClone());
+}
+
+bool UIWizardCloneVMExpertPage::isComplete() const
+{
+ return m_pNamePathGroupBox && m_pNamePathGroupBox->isComplete(m_strGroup);
+}
+
+bool UIWizardCloneVMExpertPage::validatePage()
+{
+ AssertReturn(wizardWindow<UIWizardCloneVM>(), false);
+ return wizardWindow<UIWizardCloneVM>()->cloneVM();
+}
+
+void UIWizardCloneVMExpertPage::sltCloneNameChanged(const QString &strCloneName)
+{
+ UIWizardCloneVM *pWizard = wizardWindow<UIWizardCloneVM>();
+ AssertReturnVoid(pWizard);
+ AssertReturnVoid(m_pNamePathGroupBox);
+ pWizard->setCloneName(strCloneName);
+ pWizard->setCloneFilePath(
+ UIWizardCloneVMNamePathCommon::composeCloneFilePath(strCloneName, m_strGroup, m_pNamePathGroupBox->clonePath()));
+ emit completeChanged();
+}
+
+void UIWizardCloneVMExpertPage::sltClonePathChanged(const QString &strClonePath)
+{
+ AssertReturnVoid(m_pNamePathGroupBox && wizardWindow<UIWizardCloneVM>());
+ wizardWindow<UIWizardCloneVM>()->setCloneFilePath(
+ UIWizardCloneVMNamePathCommon::composeCloneFilePath(m_pNamePathGroupBox->cloneName(), m_strGroup, strClonePath));
+ emit completeChanged();
+}
+
+void UIWizardCloneVMExpertPage::sltMACAddressClonePolicyChanged(MACAddressClonePolicy enmMACAddressClonePolicy)
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ wizardWindow<UIWizardCloneVM>()->setMacAddressPolicy(enmMACAddressClonePolicy);
+}
+
+void UIWizardCloneVMExpertPage::sltKeepDiskNamesToggled(bool fKeepDiskNames)
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ wizardWindow<UIWizardCloneVM>()->setKeepDiskNames(fKeepDiskNames);
+}
+
+void UIWizardCloneVMExpertPage::sltKeepHardwareUUIDsToggled(bool fKeepHardwareUUIDs)
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ wizardWindow<UIWizardCloneVM>()->setKeepHardwareUUIDs(fKeepHardwareUUIDs);
+}
+
+void UIWizardCloneVMExpertPage::sltCloneTypeChanged(bool fIsFullClone)
+{
+ UIWizardCloneVM *pWizard = wizardWindow<UIWizardCloneVM>();
+ AssertReturnVoid(pWizard);
+ pWizard->setLinkedClone(!fIsFullClone);
+ setCloneModeGroupBoxEnabled();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMExpertPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMExpertPage.h
new file mode 100644
index 00000000..9651a60e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMExpertPage.h
@@ -0,0 +1,85 @@
+/* $Id: UIWizardCloneVMExpertPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVMExpertPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMExpertPage_h
+#define FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMExpertPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Local includes: */
+#include "UINativeWizardPage.h"
+#include "UIWizardCloneVMEditors.h"
+
+/* Forward declarations: */
+class UICloneVMAdditionalOptionsEditor;
+class UICloneVMCloneModeGroupBox;
+class UICloneVMCloneTypeGroupBox;
+class UICloneVMNamePathEditor;
+
+/** Expert page of the Clone Virtual Machine wizard. */
+class UIWizardCloneVMExpertPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor. */
+ UIWizardCloneVMExpertPage(const QString &strOriginalName, const QString &strDefaultPath,
+ bool fAdditionalInfo, bool fShowChildsOption, const QString &strGroup);
+
+private slots:
+
+ void sltCloneNameChanged(const QString &strCloneName);
+ void sltClonePathChanged(const QString &strClonePath);
+ void sltMACAddressClonePolicyChanged(MACAddressClonePolicy enmMACAddressClonePolicy);
+ void sltKeepDiskNamesToggled(bool fKeepDiskNames);
+ void sltKeepHardwareUUIDsToggled(bool fKeepHardwareUUIDs);
+ void sltCloneTypeChanged(bool fIsFullClone);
+
+private:
+
+ /** Translation stuff. */
+ void retranslateUi();
+
+ /** Prepare stuff. */
+ void initializePage();
+ void prepare(const QString &strOriginalName, const QString &strDefaultPath, bool fShowChildsOption);
+
+ /** Validation stuff. */
+ bool isComplete() const;
+ bool validatePage();
+ void setCloneModeGroupBoxEnabled();
+
+ UICloneVMNamePathEditor *m_pNamePathGroupBox;
+ UICloneVMCloneTypeGroupBox *m_pCloneTypeGroupBox;
+ UICloneVMCloneModeGroupBox *m_pCloneModeGroupBox;
+ UICloneVMAdditionalOptionsEditor *m_pAdditionalOptionsGroupBox;
+ QString m_strGroup;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMExpertPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMModePage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMModePage.cpp
new file mode 100644
index 00000000..0d811f74
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMModePage.cpp
@@ -0,0 +1,127 @@
+/* $Id: UIWizardCloneVMModePage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVMModePage class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Global includes: */
+#include <QVBoxLayout>
+
+/* Local includes: */
+#include "UIWizardCloneVM.h"
+#include "UIWizardCloneVMEditors.h"
+#include "UIWizardCloneVMModePage.h"
+#include "QIRichTextLabel.h"
+
+UIWizardCloneVMModePage::UIWizardCloneVMModePage(bool fShowChildsOption)
+ : m_pLabel(0)
+ , m_pCloneModeGroupBox(0)
+ , m_fShowChildsOption(fShowChildsOption)
+{
+ prepare();
+}
+
+void UIWizardCloneVMModePage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ AssertReturnVoid(pMainLayout);
+ m_pLabel = new QIRichTextLabel(this);
+
+ if (m_pLabel)
+ pMainLayout->addWidget(m_pLabel);
+
+ m_pCloneModeGroupBox = new UICloneVMCloneModeGroupBox(m_fShowChildsOption);
+ if (m_pCloneModeGroupBox)
+ {
+ pMainLayout->addWidget(m_pCloneModeGroupBox);
+ m_pCloneModeGroupBox->setFlat(true);
+ connect(m_pCloneModeGroupBox, &UICloneVMCloneModeGroupBox::sigCloneModeChanged,
+ this, &UIWizardCloneVMModePage::sltCloneModeChanged);
+ }
+ pMainLayout->addStretch();
+
+ retranslateUi();
+}
+
+void UIWizardCloneVMModePage::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardCloneVM::tr("Snapshots"));
+
+ /* Translate widgets: */
+ const QString strGeneral = UIWizardCloneVM::tr("<p>Please choose which parts of the snapshot tree "
+ "should be cloned with the machine.</p>");
+ const QString strOpt1 = UIWizardCloneVM::tr("<p>If you choose <b>Current machine state</b>, "
+ "the new machine will reflect the current state "
+ "of the original machine and will have no snapshots.</p>");
+ const QString strOpt2 = UIWizardCloneVM::tr("<p>If you choose <b>Current snapshot tree branch</b>, "
+ "the new machine will reflect the current state "
+ "of the original machine and will have matching snapshots "
+ "for all snapshots in the tree branch "
+ "starting at the current state in the original machine.</p>");
+ const QString strOpt3 = UIWizardCloneVM::tr("<p>If you choose <b>Everything</b>, "
+ "the new machine will reflect the current state "
+ "of the original machine and will have matching snapshots "
+ "for all snapshots in the original machine.</p>");
+ if (m_fShowChildsOption)
+ m_pLabel->setText(QString("<p>%1</p><p>%2 %3 %4</p>")
+ .arg(strGeneral)
+ .arg(strOpt1)
+ .arg(strOpt2)
+ .arg(strOpt3));
+ else
+ m_pLabel->setText(QString("<p>%1</p><p>%2 %3</p>")
+ .arg(strGeneral)
+ .arg(strOpt1)
+ .arg(strOpt3));
+}
+
+void UIWizardCloneVMModePage::initializePage()
+{
+
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ if (m_pCloneModeGroupBox && !m_userModifiedParameters.contains("CloneMode"))
+ wizardWindow<UIWizardCloneVM>()->setCloneMode(m_pCloneModeGroupBox->cloneMode());
+
+ retranslateUi();
+}
+
+bool UIWizardCloneVMModePage::validatePage()
+{
+ bool fResult = true;
+
+ UIWizardCloneVM *pWizard = wizardWindow<UIWizardCloneVM>();
+ AssertReturn(pWizard, false);
+ /* Try to clone VM: */
+ fResult = pWizard->cloneVM();
+
+ return fResult;
+}
+
+void UIWizardCloneVMModePage::sltCloneModeChanged(KCloneMode enmCloneMode)
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ m_userModifiedParameters << "CloneMode";
+ wizardWindow<UIWizardCloneVM>()->setCloneMode(enmCloneMode);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMModePage.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMModePage.h
new file mode 100644
index 00000000..c5cd24d6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMModePage.h
@@ -0,0 +1,81 @@
+/* $Id: UIWizardCloneVMModePage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVMModePage class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMModePage_h
+#define FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMModePage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSet>
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+/* Forward declaration: */
+class QIRichTextLabel;
+class UICloneVMCloneModeGroupBox;
+
+/** 3rd page of the Clone Virtual Machine wizard (basic extension). */
+class UIWizardCloneVMModePage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor. */
+ UIWizardCloneVMModePage(bool fShowChildsOption);
+
+private slots:
+
+ void sltCloneModeChanged(KCloneMode enmCloneMode);
+
+private:
+
+ /** Translation stuff. */
+ void retranslateUi();
+
+ /** Prepare stuff. */
+ void initializePage();
+ void prepare();
+
+ /** Validation stuff. */
+ bool validatePage();
+
+ /** Widgets. */
+ QIRichTextLabel *m_pLabel;
+ UICloneVMCloneModeGroupBox *m_pCloneModeGroupBox;
+
+ bool m_fShowChildsOption;
+ QSet<QString> m_userModifiedParameters;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMModePage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMNamePathPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMNamePathPage.cpp
new file mode 100644
index 00000000..bfbe866f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMNamePathPage.cpp
@@ -0,0 +1,191 @@
+/* $Id: UIWizardCloneVMNamePathPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVMNamePathPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "UIWizardCloneVM.h"
+#include "UIWizardCloneVMNamePathPage.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "CVirtualBox.h"
+
+QString UIWizardCloneVMNamePathCommon::composeCloneFilePath(const QString &strCloneName, const QString &strGroup, const QString &strFolderPath)
+{
+ CVirtualBox vbox = uiCommon().virtualBox();
+ return QDir::toNativeSeparators(vbox.ComposeMachineFilename(strCloneName, strGroup, QString(), strFolderPath));
+}
+
+UIWizardCloneVMNamePathPage::UIWizardCloneVMNamePathPage(const QString &strOriginalName, const QString &strDefaultPath, const QString &strGroup)
+ : m_pNamePathEditor(0)
+ , m_pAdditionalOptionsEditor(0)
+ , m_strOriginalName(strOriginalName)
+ , m_strGroup(strGroup)
+{
+ prepare(strDefaultPath);
+}
+
+void UIWizardCloneVMNamePathPage::retranslateUi()
+{
+ setTitle(UIWizardCloneVM::tr("New machine name and path"));
+
+ if (m_pMainLabel)
+ m_pMainLabel->setText(UIWizardCloneVM::tr("<p>Please choose a name and optionally a folder for the new virtual machine. "
+ "The new machine will be a clone of the machine <b>%1</b>.</p>")
+ .arg(m_strOriginalName));
+
+ int iMaxWidth = 0;
+ if (m_pNamePathEditor)
+ iMaxWidth = qMax(iMaxWidth, m_pNamePathEditor->firstColumnWidth());
+ if (m_pAdditionalOptionsEditor)
+ iMaxWidth = qMax(iMaxWidth, m_pAdditionalOptionsEditor->firstColumnWidth());
+
+ if (m_pNamePathEditor)
+ m_pNamePathEditor->setFirstColumnWidth(iMaxWidth);
+ if (m_pAdditionalOptionsEditor)
+ m_pAdditionalOptionsEditor->setFirstColumnWidth(iMaxWidth);
+}
+
+void UIWizardCloneVMNamePathPage::initializePage()
+{
+ UIWizardCloneVM *pWizard = wizardWindow<UIWizardCloneVM>();
+ AssertReturnVoid(pWizard);
+ retranslateUi();
+ if (m_pNamePathEditor)
+ {
+ m_pNamePathEditor->setFocus();
+ if (!m_userModifiedParameters.contains("CloneName"))
+ pWizard->setCloneName(m_pNamePathEditor->cloneName());
+ if (!m_userModifiedParameters.contains("CloneFilePath"))
+ pWizard->setCloneFilePath(UIWizardCloneVMNamePathCommon::composeCloneFilePath(m_pNamePathEditor->cloneName(),
+ m_strGroup, m_pNamePathEditor->clonePath()));
+
+ }
+ if (m_pAdditionalOptionsEditor)
+ {
+ if (!m_userModifiedParameters.contains("MacAddressPolicy"))
+ pWizard->setMacAddressPolicy(m_pAdditionalOptionsEditor->macAddressClonePolicy());
+ if (!m_userModifiedParameters.contains("KeepDiskNames"))
+ pWizard->setKeepDiskNames(m_pAdditionalOptionsEditor->keepDiskNames());
+ if (!m_userModifiedParameters.contains("KeepHardwareUUIDs"))
+ pWizard->setKeepHardwareUUIDs(m_pAdditionalOptionsEditor->keepHardwareUUIDs());
+ }
+}
+
+void UIWizardCloneVMNamePathPage::prepare(const QString &strDefaultClonePath)
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+
+ AssertReturnVoid(pMainLayout);
+
+ m_pMainLabel = new QIRichTextLabel(this);
+ if (m_pMainLabel)
+ pMainLayout->addWidget(m_pMainLabel);
+
+ m_pNamePathEditor = new UICloneVMNamePathEditor(m_strOriginalName, strDefaultClonePath);
+ if (m_pNamePathEditor)
+ {
+ m_pNamePathEditor->setFlat(true);
+ m_pNamePathEditor->setLayoutContentsMargins(0, 0, 0, 0);
+ pMainLayout->addWidget(m_pNamePathEditor);
+ connect(m_pNamePathEditor, &UICloneVMNamePathEditor::sigCloneNameChanged,
+ this, &UIWizardCloneVMNamePathPage::sltCloneNameChanged);
+ connect(m_pNamePathEditor, &UICloneVMNamePathEditor::sigClonePathChanged,
+ this, &UIWizardCloneVMNamePathPage::sltClonePathChanged);
+ }
+
+ m_pAdditionalOptionsEditor = new UICloneVMAdditionalOptionsEditor;
+ if (m_pAdditionalOptionsEditor)
+ {
+ m_pAdditionalOptionsEditor->setFlat(true);
+ pMainLayout->addWidget(m_pAdditionalOptionsEditor);
+ connect(m_pAdditionalOptionsEditor, &UICloneVMAdditionalOptionsEditor::sigMACAddressClonePolicyChanged,
+ this, &UIWizardCloneVMNamePathPage::sltMACAddressClonePolicyChanged);
+ connect(m_pAdditionalOptionsEditor, &UICloneVMAdditionalOptionsEditor::sigKeepDiskNamesToggled,
+ this, &UIWizardCloneVMNamePathPage::sltKeepDiskNamesToggled);
+ connect(m_pAdditionalOptionsEditor, &UICloneVMAdditionalOptionsEditor::sigKeepHardwareUUIDsToggled,
+ this, &UIWizardCloneVMNamePathPage::sltKeepHardwareUUIDsToggled);
+ }
+
+ pMainLayout->addStretch();
+
+ retranslateUi();
+}
+
+bool UIWizardCloneVMNamePathPage::isComplete() const
+{
+ return m_pNamePathEditor && m_pNamePathEditor->isComplete(m_strGroup);
+}
+
+void UIWizardCloneVMNamePathPage::sltCloneNameChanged(const QString &strCloneName)
+{
+ UIWizardCloneVM *pWizard = wizardWindow<UIWizardCloneVM>();
+ AssertReturnVoid(pWizard);
+ AssertReturnVoid(m_pNamePathEditor);
+ m_userModifiedParameters << "CloneName";
+ m_userModifiedParameters << "CloneFilePath";
+ pWizard->setCloneName(strCloneName);
+ pWizard->setCloneFilePath(UIWizardCloneVMNamePathCommon::composeCloneFilePath(strCloneName, m_strGroup, m_pNamePathEditor->clonePath()));
+ emit completeChanged();
+}
+
+void UIWizardCloneVMNamePathPage::sltClonePathChanged(const QString &strClonePath)
+{
+ AssertReturnVoid(m_pNamePathEditor);
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ m_userModifiedParameters << "CloneFilePath";
+ wizardWindow<UIWizardCloneVM>()->setCloneFilePath(UIWizardCloneVMNamePathCommon::composeCloneFilePath(m_pNamePathEditor->cloneName(), m_strGroup, strClonePath));
+ emit completeChanged();
+}
+
+void UIWizardCloneVMNamePathPage::sltMACAddressClonePolicyChanged(MACAddressClonePolicy enmMACAddressClonePolicy)
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ m_userModifiedParameters << "MacAddressPolicy";
+ wizardWindow<UIWizardCloneVM>()->setMacAddressPolicy(enmMACAddressClonePolicy);
+ emit completeChanged();
+}
+
+void UIWizardCloneVMNamePathPage::sltKeepDiskNamesToggled(bool fKeepDiskNames)
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ m_userModifiedParameters << "KeepDiskNames";
+ wizardWindow<UIWizardCloneVM>()->setKeepDiskNames(fKeepDiskNames);
+ emit completeChanged();
+}
+
+void UIWizardCloneVMNamePathPage::sltKeepHardwareUUIDsToggled(bool fKeepHardwareUUIDs)
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ m_userModifiedParameters << "KeepHardwareUUIDs";
+ wizardWindow<UIWizardCloneVM>()->setKeepHardwareUUIDs(fKeepHardwareUUIDs);
+ emit completeChanged();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMNamePathPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMNamePathPage.h
new file mode 100644
index 00000000..61a27cd1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMNamePathPage.h
@@ -0,0 +1,83 @@
+/* $Id: UIWizardCloneVMNamePathPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVMNamePathPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMNamePathPage_h
+#define FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMNamePathPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSet>
+
+/* Local includes: */
+#include "UINativeWizardPage.h"
+#include "UIWizardCloneVMEditors.h"
+
+/* Forward declarations: */
+class UICloneVMAdditionalOptionsEditor;
+class UICloneVMNamePathEditor;
+class QIRichTextLabel;
+
+namespace UIWizardCloneVMNamePathCommon
+{
+ QString composeCloneFilePath(const QString &strCloneName, const QString &strGroup, const QString &strFolderPath);
+}
+
+class UIWizardCloneVMNamePathPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardCloneVMNamePathPage(const QString &strOriginalName, const QString &strDefaultPath, const QString &strGroup);
+
+private slots:
+
+ void sltCloneNameChanged(const QString &strCloneName);
+ void sltClonePathChanged(const QString &strClonePath);
+ void sltMACAddressClonePolicyChanged(MACAddressClonePolicy enmMACAddressClonePolicy);
+ void sltKeepDiskNamesToggled(bool fKeepDiskNames);
+ void sltKeepHardwareUUIDsToggled(bool fKeepHardwareUUIDs);
+
+private:
+
+ void retranslateUi();
+ void initializePage();
+ void prepare(const QString &strDefaultClonePath);
+ /** Validation stuff */
+ bool isComplete() const;
+
+ QIRichTextLabel *m_pMainLabel;
+ UICloneVMNamePathEditor *m_pNamePathEditor;
+ UICloneVMAdditionalOptionsEditor *m_pAdditionalOptionsEditor;
+ QString m_strOriginalName;
+ QString m_strGroup;
+ QSet<QString> m_userModifiedParameters;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMNamePathPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMTypePage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMTypePage.cpp
new file mode 100644
index 00000000..ae452d4a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMTypePage.cpp
@@ -0,0 +1,124 @@
+/* $Id: UIWizardCloneVMTypePage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVMTypePage class implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIWizardCloneVMTypePage.h"
+#include "UIWizardCloneVM.h"
+#include "QIRichTextLabel.h"
+#include "UIWizardCloneVMEditors.h"
+
+UIWizardCloneVMTypePage::UIWizardCloneVMTypePage(bool fAdditionalInfo)
+ : m_pLabel(0)
+ , m_fAdditionalInfo(fAdditionalInfo)
+ , m_pCloneTypeGroupBox(0)
+{
+ prepare();
+}
+
+void UIWizardCloneVMTypePage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ AssertReturnVoid(pMainLayout);
+ m_pLabel = new QIRichTextLabel(this);
+ if (m_pLabel)
+ pMainLayout->addWidget(m_pLabel);
+
+ m_pCloneTypeGroupBox = new UICloneVMCloneTypeGroupBox;
+ if (m_pCloneTypeGroupBox)
+ {
+ m_pCloneTypeGroupBox->setFlat(true);
+ pMainLayout->addWidget(m_pCloneTypeGroupBox);
+ connect(m_pCloneTypeGroupBox, &UICloneVMCloneTypeGroupBox::sigFullCloneSelected,
+ this, &UIWizardCloneVMTypePage::sltCloneTypeChanged);
+ }
+
+ pMainLayout->addStretch();
+}
+
+void UIWizardCloneVMTypePage::sltCloneTypeChanged(bool fIsFullClone)
+{
+ UIWizardCloneVM *pWizard = wizardWindow<UIWizardCloneVM>();
+ AssertReturnVoid(pWizard);
+ m_userModifiedParameters << "LinkedClone";
+ pWizard->setLinkedClone(!fIsFullClone);
+ /* Show/hide 3rd page according to linked clone toggle: */
+ pWizard->setCloneModePageVisible(fIsFullClone);
+}
+
+void UIWizardCloneVMTypePage::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardCloneVM::tr("Clone type"));
+
+ /* Translate widgets: */
+ QString strLabel = UIWizardCloneVM::tr("<p>Please choose the type of clone you wish to create.</p>"
+ "<p>If you choose <b>Full clone</b>, "
+ "an exact copy (including all virtual hard disk files) "
+ "of the original virtual machine will be created.</p>"
+ "<p>If you choose <b>Linked clone</b>, "
+ "a new machine will be created, but the virtual hard disk files "
+ "will be tied to the virtual hard disk files of original machine "
+ "and you will not be able to move the new virtual machine "
+ "to a different computer without moving the original as well.</p>");
+ if (m_fAdditionalInfo)
+ strLabel += UIWizardCloneVM::tr("<p>If you create a <b>Linked clone</b> then a new snapshot will be created "
+ "in the original virtual machine as part of the cloning process.</p>");
+ if (m_pLabel)
+ m_pLabel->setText(strLabel);
+}
+
+void UIWizardCloneVMTypePage::initializePage()
+{
+ AssertReturnVoid(wizardWindow<UIWizardCloneVM>());
+ retranslateUi();
+ if (m_pCloneTypeGroupBox && !m_userModifiedParameters.contains("LinkedClone"))
+ wizardWindow<UIWizardCloneVM>()->setLinkedClone(!m_pCloneTypeGroupBox->isFullClone());
+}
+
+bool UIWizardCloneVMTypePage::validatePage()
+{
+ UIWizardCloneVM *pWizard = wizardWindow<UIWizardCloneVM>();
+ AssertReturn(pWizard, false);
+
+ /* This page could be final: */
+ if (!pWizard->isCloneModePageVisible())
+ {
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Trying to clone VM: */
+ fResult = pWizard->cloneVM();
+
+ /* Return result: */
+ return fResult;
+ }
+ else
+ return true;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMTypePage.h b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMTypePage.h
new file mode 100644
index 00000000..ac4d539e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/clonevm/UIWizardCloneVMTypePage.h
@@ -0,0 +1,69 @@
+/* $Id: UIWizardCloneVMTypePage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardCloneVMTypePage class declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMTypePage_h
+#define FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMTypePage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSet>
+
+/* Local includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QIRichTextLabel;
+class UICloneVMCloneTypeGroupBox;
+
+class UIWizardCloneVMTypePage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardCloneVMTypePage(bool fAdditionalInfo);
+
+private slots:
+
+ void sltCloneTypeChanged(bool fIsFullClone);
+
+private:
+
+ void retranslateUi();
+ void initializePage();
+ void prepare();
+ bool validatePage();
+
+ QIRichTextLabel *m_pLabel;
+ bool m_fAdditionalInfo;
+ UICloneVMCloneTypeGroupBox *m_pCloneTypeGroupBox;
+ QSet<QString> m_userModifiedParameters;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_clonevm_UIWizardCloneVMTypePage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/editors/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIHostnameDomainNameEditor.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIHostnameDomainNameEditor.cpp
new file mode 100644
index 00000000..f1bf2c56
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIHostnameDomainNameEditor.cpp
@@ -0,0 +1,199 @@
+/* $Id: UIHostnameDomainNameEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIHostnameDomainNameEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QRegularExpressionValidator>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "QIRichTextLabel.h"
+#include "QIToolButton.h"
+#include "UICommon.h"
+#include "UIIconPool.h"
+#include "UIHostnameDomainNameEditor.h"
+#include "UIWizardNewVM.h"
+
+
+
+UIHostnameDomainNameEditor::UIHostnameDomainNameEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pHostnameLineEdit(0)
+ , m_pDomainNameLineEdit(0)
+ , m_pHostnameLabel(0)
+ , m_pDomainNameLabel(0)
+ , m_pMainLayout(0)
+{
+ prepare();
+}
+
+QString UIHostnameDomainNameEditor::hostname() const
+{
+ if (m_pHostnameLineEdit)
+ return m_pHostnameLineEdit->text();
+ return QString();
+}
+
+bool UIHostnameDomainNameEditor::isComplete() const
+{
+ return m_pHostnameLineEdit && m_pHostnameLineEdit->hasAcceptableInput() &&
+ m_pDomainNameLineEdit && m_pDomainNameLineEdit->hasAcceptableInput();
+}
+
+void UIHostnameDomainNameEditor::mark()
+{
+ if (m_pHostnameLineEdit)
+ m_pHostnameLineEdit->mark(!m_pHostnameLineEdit->hasAcceptableInput(),
+ tr("Hostname should be at least 2 character long. "
+ "Allowed characters are alphanumerics, \"-\" and \".\""));
+ if (m_pDomainNameLineEdit)
+ m_pDomainNameLineEdit->mark(!m_pDomainNameLineEdit->hasAcceptableInput(),
+ tr("Domain name should be at least 2 character long. "
+ "Allowed characters are alphanumerics, \"-\" and \".\""));
+}
+
+void UIHostnameDomainNameEditor::setHostname(const QString &strHostname)
+{
+ if (m_pHostnameLineEdit)
+ m_pHostnameLineEdit->setText(strHostname);
+}
+
+QString UIHostnameDomainNameEditor::domainName() const
+{
+ if (m_pDomainNameLineEdit)
+ return m_pDomainNameLineEdit->text();
+ return QString();
+}
+
+void UIHostnameDomainNameEditor::setDomainName(const QString &strDomain)
+{
+ if (m_pDomainNameLineEdit)
+ m_pDomainNameLineEdit->setText(strDomain);
+}
+
+QString UIHostnameDomainNameEditor::hostnameDomainName() const
+{
+ if (m_pHostnameLineEdit && m_pDomainNameLineEdit)
+ return QString("%1.%2").arg(m_pHostnameLineEdit->text()).arg(m_pDomainNameLineEdit->text());
+ return QString();
+}
+
+int UIHostnameDomainNameEditor::firstColumnWidth() const
+{
+ int iWidth = 0;
+ if (m_pHostnameLabel)
+ iWidth = qMax(iWidth, m_pHostnameLabel->minimumSizeHint().width());
+ if (m_pDomainNameLabel)
+ iWidth = qMax(iWidth, m_pDomainNameLabel->minimumSizeHint().width());
+ return iWidth;
+}
+
+void UIHostnameDomainNameEditor::setFirstColumnWidth(int iWidth)
+{
+ if (m_pMainLayout)
+ m_pMainLayout->setColumnMinimumWidth(0, iWidth);
+}
+
+void UIHostnameDomainNameEditor::retranslateUi()
+{
+ if (m_pHostnameLabel)
+ m_pHostnameLabel->setText(tr("Hostna&me:"));
+ if (m_pHostnameLineEdit)
+ m_pHostnameLineEdit->setToolTip(tr("Holds the hostname."));
+ if (m_pDomainNameLabel)
+ m_pDomainNameLabel->setText(tr("&Domain Name:"));
+ if (m_pDomainNameLineEdit)
+ m_pDomainNameLineEdit->setToolTip(tr("Holds the domain name."));
+}
+
+template<class T>
+void UIHostnameDomainNameEditor::addLineEdit(int &iRow, QLabel *&pLabel, T *&pLineEdit, QGridLayout *pLayout)
+{
+ AssertReturnVoid(pLayout);
+ if (pLabel || pLineEdit)
+ return;
+
+ pLabel = new QLabel;
+ AssertReturnVoid(pLabel);
+ pLabel->setAlignment(Qt::AlignRight);
+ //pLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ pLayout->addWidget(pLabel, iRow, 0, 1, 1);
+
+ pLineEdit = new T;
+ AssertReturnVoid(pLineEdit);
+
+ pLayout->addWidget(pLineEdit, iRow, 1, 1, 3);
+ pLabel->setBuddy(pLineEdit);
+ ++iRow;
+ return;
+}
+
+void UIHostnameDomainNameEditor::prepare()
+{
+ m_pMainLayout = new QGridLayout;
+ m_pMainLayout->setColumnStretch(0, 0);
+ m_pMainLayout->setColumnStretch(1, 1);
+ if (!m_pMainLayout)
+ return;
+ setLayout(m_pMainLayout);
+ int iRow = 0;
+ addLineEdit<UIMarkableLineEdit>(iRow, m_pHostnameLabel, m_pHostnameLineEdit, m_pMainLayout);
+ addLineEdit<QILineEdit>(iRow, m_pDomainNameLabel, m_pDomainNameLineEdit, m_pMainLayout);
+
+ /* Host name and domain should be strings of minimum length of 2 and composed of alpha numerics, '-', and '.'
+ * Exclude strings with . at the end: */
+ m_pHostnameLineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression("^[a-zA-Z0-9-.]{2,}[$a-zA-Z0-9-]"), this));
+ m_pDomainNameLineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression("^[a-zA-Z0-9-.]{2,}[$a-zA-Z0-9-]"), this));
+
+ connect(m_pHostnameLineEdit, &UIMarkableLineEdit::textChanged,
+ this, &UIHostnameDomainNameEditor::sltHostnameChanged);
+ connect(m_pDomainNameLineEdit, &QILineEdit::textChanged,
+ this, &UIHostnameDomainNameEditor::sltDomainChanged);
+
+ retranslateUi();
+}
+
+void UIHostnameDomainNameEditor::sltHostnameChanged()
+{
+ m_pHostnameLineEdit->mark(!m_pHostnameLineEdit->hasAcceptableInput(),
+ tr("Hostname should be at least 2 character long. "
+ "Allowed characters are alphanumerics, \"-\" and \".\""));
+ emit sigHostnameDomainNameChanged(hostnameDomainName(), isComplete());
+}
+
+void UIHostnameDomainNameEditor::sltDomainChanged()
+{
+ m_pDomainNameLineEdit->mark(!m_pDomainNameLineEdit->hasAcceptableInput(),
+ tr("Domain name should be at least 2 character long. "
+ "Allowed characters are alphanumerics, \"-\" and \".\""));
+ emit sigHostnameDomainNameChanged(hostnameDomainName(), isComplete());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIHostnameDomainNameEditor.h b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIHostnameDomainNameEditor.h
new file mode 100644
index 00000000..0a858af4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIHostnameDomainNameEditor.h
@@ -0,0 +1,99 @@
+/* $Id: UIHostnameDomainNameEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIHostnameDomainNameEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_editors_UIHostnameDomainNameEditor_h
+#define FEQT_INCLUDED_SRC_wizards_editors_UIHostnameDomainNameEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QWidget>
+
+/* Local includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QILineEdit;
+class UIMarkableLineEdit;
+class UIPasswordLineEdit;
+
+class UIHostnameDomainNameEditor : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigHostnameDomainNameChanged(const QString &strHostNameDomain, bool fIsComplete);
+
+public:
+
+ UIHostnameDomainNameEditor(QWidget *pParent = 0);
+
+ QString hostname() const;
+ void setHostname(const QString &strHostname);
+
+ QString domainName() const;
+ void setDomainName(const QString &strDomain);
+
+ QString hostnameDomainName() const;
+
+ bool isComplete() const;
+ void mark();
+
+ int firstColumnWidth() const;
+ void setFirstColumnWidth(int iWidth);
+
+
+protected:
+
+ void retranslateUi();
+
+private slots:
+
+ void sltHostnameChanged();
+ void sltDomainChanged();
+
+private:
+
+ void prepare();
+ template<class T>
+ void addLineEdit(int &iRow, QLabel *&pLabel, T *&pLineEdit, QGridLayout *pLayout);
+
+ UIMarkableLineEdit *m_pHostnameLineEdit;
+ QILineEdit *m_pDomainNameLineEdit;
+
+ QLabel *m_pHostnameLabel;
+ QLabel *m_pDomainNameLabel;
+ QGridLayout *m_pMainLayout;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_editors_UIHostnameDomainNameEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIUserNamePasswordEditor.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIUserNamePasswordEditor.cpp
new file mode 100644
index 00000000..6aa62eb1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIUserNamePasswordEditor.cpp
@@ -0,0 +1,379 @@
+/* $Id: UIUserNamePasswordEditor.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUserNamePasswordEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QStyle>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "QIRichTextLabel.h"
+#include "QIToolButton.h"
+#include "UICursor.h"
+#include "UIIconPool.h"
+#include "UIUserNamePasswordEditor.h"
+#include "UIWizardNewVM.h"
+
+
+UIPasswordLineEdit::UIPasswordLineEdit(QWidget *pParent /*= 0 */)
+ : QLineEdit(pParent)
+ , m_pTextVisibilityButton(0)
+ , m_pErrorIconLabel(0)
+ , m_fMarkForError(false)
+{
+ prepare();
+}
+
+void UIPasswordLineEdit::toggleTextVisibility(bool fTextVisible)
+{
+ AssertPtrReturnVoid(m_pTextVisibilityButton);
+
+ if (fTextVisible)
+ {
+ setEchoMode(QLineEdit::Normal);
+ if (m_pTextVisibilityButton)
+ m_pTextVisibilityButton->setIcon(UIIconPool::iconSet(":/eye_closed_10px.png"));
+ }
+ else
+ {
+ setEchoMode(QLineEdit::Password);
+ if (m_pTextVisibilityButton)
+ m_pTextVisibilityButton->setIcon(UIIconPool::iconSet(":/eye_10px.png"));
+ }
+}
+
+void UIPasswordLineEdit::mark(bool fError, const QString &strErrorToolTip)
+{
+ /* Check if something really changed: */
+ if (m_fMarkForError == fError && m_strErrorToolTip == strErrorToolTip)
+ return;
+
+ /* Save new values: */
+ m_fMarkForError = fError;
+ m_strErrorToolTip = strErrorToolTip;
+
+ /* Update accordingly: */
+ if (m_fMarkForError)
+ {
+ /* Create label if absent: */
+ if (!m_pErrorIconLabel)
+ m_pErrorIconLabel = new QLabel(this);
+
+ /* Update label content, visibility & position: */
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) * .625;
+ const int iShift = height() > iIconMetric ? (height() - iIconMetric) / 2 : 0;
+ m_pErrorIconLabel->setPixmap(m_markIcon.pixmap(windowHandle(), QSize(iIconMetric, iIconMetric)));
+ m_pErrorIconLabel->setToolTip(m_strErrorToolTip);
+ int iIconX = width() - iIconMetric - iShift;
+ if (m_pTextVisibilityButton)
+ iIconX -= m_pTextVisibilityButton->width() - iShift;
+ m_pErrorIconLabel->move(iIconX, iShift);
+ m_pErrorIconLabel->show();
+ }
+ else
+ {
+ /* Hide label: */
+ if (m_pErrorIconLabel)
+ m_pErrorIconLabel->hide();
+ }
+}
+
+void UIPasswordLineEdit::prepare()
+{
+ m_markIcon = UIIconPool::iconSet(":/status_error_16px.png");
+ /* Prepare text visibility button: */
+ m_pTextVisibilityButton = new QIToolButton(this);
+ if (m_pTextVisibilityButton)
+ {
+ m_pTextVisibilityButton->setIconSize(QSize(10, 10));
+ m_pTextVisibilityButton->setFocusPolicy(Qt::ClickFocus);
+ UICursor::setCursor(m_pTextVisibilityButton, Qt::ArrowCursor);
+ m_pTextVisibilityButton->show();
+ connect(m_pTextVisibilityButton, &QToolButton::clicked, this, &UIPasswordLineEdit::sltHandleTextVisibilityChange);
+ }
+ m_pErrorIconLabel = new QLabel(this);
+ toggleTextVisibility(false);
+ adjustTextVisibilityButtonGeometry();
+}
+
+void UIPasswordLineEdit::adjustTextVisibilityButtonGeometry()
+{
+ AssertPtrReturnVoid(m_pTextVisibilityButton);
+
+#ifdef VBOX_WS_MAC
+ /* Do not forget to update QIToolButton size on macOS, it's FIXED: */
+ m_pTextVisibilityButton->setFixedSize(m_pTextVisibilityButton->minimumSizeHint());
+ /* Calculate suitable position for a QIToolButton, it's FRAMELESS: */
+ const int iWidth = m_pTextVisibilityButton->width();
+ const int iMinHeight = qMin(height(), m_pTextVisibilityButton->height());
+ const int iMaxHeight = qMax(height(), m_pTextVisibilityButton->height());
+ const int iHalfHeightDiff = (iMaxHeight - iMinHeight) / 2;
+ m_pTextVisibilityButton->setGeometry(width() - iWidth - iHalfHeightDiff, iHalfHeightDiff, iWidth, iWidth);
+#else
+ int iFrameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ int iSize = height() - 2 * iFrameWidth;
+ m_pTextVisibilityButton->setGeometry(width() - iSize, iFrameWidth, iSize, iSize);
+#endif
+}
+
+void UIPasswordLineEdit::resizeEvent(QResizeEvent *pEvent)
+{
+ /* Call to base-class: */
+ QLineEdit::resizeEvent(pEvent);
+ adjustTextVisibilityButtonGeometry();
+
+ /* Update error label position: */
+ if (m_pErrorIconLabel)
+ {
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) * .625;
+ const int iShift = height() > iIconMetric ? (height() - iIconMetric) / 2 : 0;
+ int iIconX = width() - iIconMetric - iShift;
+ if (m_pTextVisibilityButton)
+ iIconX -= m_pTextVisibilityButton->width() - iShift;
+ m_pErrorIconLabel->move(iIconX, iShift);
+ }
+}
+
+void UIPasswordLineEdit::sltHandleTextVisibilityChange()
+{
+ bool fTextVisible = false;
+ if (echoMode() == QLineEdit::Normal)
+ fTextVisible = false;
+ else
+ fTextVisible = true;
+ toggleTextVisibility(fTextVisible);
+ emit sigTextVisibilityToggled(fTextVisible);
+}
+
+
+/*********************************************************************************************************************************
+* UIUserNamePasswordEditor implementation. *
+*********************************************************************************************************************************/
+
+UIUserNamePasswordEditor::UIUserNamePasswordEditor(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pUserNameLineEdit(0)
+ , m_pPasswordLineEdit(0)
+ , m_pPasswordRepeatLineEdit(0)
+ , m_pUserNameLabel(0)
+ , m_pPasswordLabel(0)
+ , m_pPasswordRepeatLabel(0)
+ , m_fShowPlaceholderText(true)
+ , m_fLabelsVisible(true)
+{
+ prepare();
+}
+
+QString UIUserNamePasswordEditor::userName() const
+{
+ if (m_pUserNameLineEdit)
+ return m_pUserNameLineEdit->text();
+ return QString();
+}
+
+void UIUserNamePasswordEditor::setUserName(const QString &strUserName)
+{
+ if (m_pUserNameLineEdit)
+ return m_pUserNameLineEdit->setText(strUserName);
+}
+
+QString UIUserNamePasswordEditor::password() const
+{
+ if (m_pPasswordLineEdit)
+ return m_pPasswordLineEdit->text();
+ return QString();
+}
+
+void UIUserNamePasswordEditor::setPassword(const QString &strPassword)
+{
+ if (m_pPasswordLineEdit)
+ m_pPasswordLineEdit->setText(strPassword);
+ if (m_pPasswordRepeatLineEdit)
+ m_pPasswordRepeatLineEdit->setText(strPassword);
+}
+
+bool UIUserNamePasswordEditor::isUserNameComplete()
+{
+ bool fComplete = (m_pUserNameLineEdit && !m_pUserNameLineEdit->text().isEmpty());
+ if (m_pUserNameLineEdit)
+ m_pUserNameLineEdit->mark(!fComplete, UIUserNamePasswordEditor::tr("Invalid username"));
+ return fComplete;
+}
+
+bool UIUserNamePasswordEditor::isPasswordComplete()
+{
+ bool fPasswordOK = true;
+ if (m_pPasswordLineEdit && m_pPasswordRepeatLineEdit)
+ {
+ if (m_pPasswordLineEdit->text() != m_pPasswordRepeatLineEdit->text())
+ fPasswordOK = false;
+ if (m_pPasswordLineEdit->text().isEmpty())
+ fPasswordOK = false;
+ m_pPasswordLineEdit->mark(!fPasswordOK, m_strPasswordError);
+ m_pPasswordRepeatLineEdit->mark(!fPasswordOK, m_strPasswordError);
+ }
+ return fPasswordOK;
+}
+
+bool UIUserNamePasswordEditor::isComplete()
+{
+ bool fUserNameField = isUserNameComplete();
+ bool fPasswordField = isPasswordComplete();
+ return fUserNameField && fPasswordField;
+}
+
+void UIUserNamePasswordEditor::setPlaceholderTextEnabled(bool fEnabled)
+{
+ if (m_fShowPlaceholderText == fEnabled)
+ return;
+ m_fShowPlaceholderText = fEnabled;
+ retranslateUi();
+}
+
+void UIUserNamePasswordEditor::setLabelsVisible(bool fVisible)
+{
+ if (m_fLabelsVisible == fVisible)
+ return;
+ m_fLabelsVisible = fVisible;
+ m_pUserNameLabel->setVisible(fVisible);
+ m_pPasswordLabel->setVisible(fVisible);
+ m_pPasswordRepeatLabel->setVisible(fVisible);
+
+}
+
+void UIUserNamePasswordEditor::retranslateUi()
+{
+ QString strPassword = tr("Pass&word");
+ QString strRepeatPassword = tr("&Repeat Password");
+ QString strUsername = tr("U&sername");
+ if (m_pUserNameLabel)
+ m_pUserNameLabel->setText(QString("%1%2").arg(strUsername).arg(":"));
+
+ if (m_pPasswordLabel)
+ m_pPasswordLabel->setText(QString("%1%2").arg(strPassword).arg(":"));
+
+ if (m_pPasswordRepeatLabel)
+ m_pPasswordRepeatLabel->setText(QString("%1%2").arg(strRepeatPassword).arg(":"));
+
+ if (m_fShowPlaceholderText)
+ {
+ if(m_pUserNameLineEdit)
+ m_pUserNameLineEdit->setPlaceholderText(strUsername.remove('&'));
+ if (m_pPasswordLineEdit)
+ m_pPasswordLineEdit->setPlaceholderText(strPassword.remove('&'));
+ if (m_pPasswordRepeatLineEdit)
+ m_pPasswordRepeatLineEdit->setPlaceholderText(strRepeatPassword.remove('&'));
+ }
+ else
+ {
+ if(m_pUserNameLineEdit)
+ m_pUserNameLineEdit->setPlaceholderText(QString());
+ if (m_pPasswordLineEdit)
+ m_pPasswordLineEdit->setPlaceholderText(QString());
+ if (m_pPasswordRepeatLineEdit)
+ m_pPasswordRepeatLineEdit->setPlaceholderText(QString());
+ }
+ if(m_pUserNameLineEdit)
+ m_pUserNameLineEdit->setToolTip(tr("Holds username."));
+ if (m_pPasswordLineEdit)
+ m_pPasswordLineEdit->setToolTip(tr("Holds password."));
+ if (m_pPasswordRepeatLineEdit)
+ m_pPasswordRepeatLineEdit->setToolTip(tr("Holds the repeated password."));
+ m_strPasswordError = tr("Invalid password pair");
+}
+
+template <class T>
+void UIUserNamePasswordEditor::addLineEdit(int &iRow, QLabel *&pLabel, T *&pLineEdit, QGridLayout *pLayout)
+{
+ if (!pLayout || pLabel || pLineEdit)
+ return;
+ pLabel = new QLabel;
+ if (!pLabel)
+ return;
+ pLabel->setAlignment(Qt::AlignRight);
+ pLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ pLayout->addWidget(pLabel, iRow, 0, 1, 1);
+
+ pLineEdit = new T;
+ if (!pLineEdit)
+ return;
+ pLayout->addWidget(pLineEdit, iRow, 1, 1, 3);
+
+ pLabel->setBuddy(pLineEdit);
+ ++iRow;
+ return;
+}
+
+void UIUserNamePasswordEditor::prepare()
+{
+ QGridLayout *pMainLayout = new QGridLayout;
+ pMainLayout->setColumnStretch(0, 0);
+ pMainLayout->setColumnStretch(1, 1);
+ if (!pMainLayout)
+ return;
+ setLayout(pMainLayout);
+ int iRow = 0;
+ addLineEdit<UIMarkableLineEdit>(iRow, m_pUserNameLabel, m_pUserNameLineEdit, pMainLayout);
+ addLineEdit<UIPasswordLineEdit>(iRow, m_pPasswordLabel, m_pPasswordLineEdit, pMainLayout);
+ addLineEdit<UIPasswordLineEdit>(iRow, m_pPasswordRepeatLabel, m_pPasswordRepeatLineEdit, pMainLayout);
+
+ connect(m_pPasswordLineEdit, &UIPasswordLineEdit::sigTextVisibilityToggled,
+ this, &UIUserNamePasswordEditor::sltHandlePasswordVisibility);
+ connect(m_pPasswordRepeatLineEdit, &UIPasswordLineEdit::sigTextVisibilityToggled,
+ this, &UIUserNamePasswordEditor::sltHandlePasswordVisibility);
+ connect(m_pPasswordLineEdit, &UIPasswordLineEdit::textChanged,
+ this, &UIUserNamePasswordEditor::sltPasswordChanged);
+ connect(m_pPasswordRepeatLineEdit, &UIPasswordLineEdit::textChanged,
+ this, &UIUserNamePasswordEditor::sltPasswordChanged);
+ connect(m_pUserNameLineEdit, &UIMarkableLineEdit::textChanged,
+ this, &UIUserNamePasswordEditor::sltUserNameChanged);
+
+ retranslateUi();
+}
+
+void UIUserNamePasswordEditor::sltHandlePasswordVisibility(bool fPasswordVisible)
+{
+ if (m_pPasswordLineEdit)
+ m_pPasswordLineEdit->toggleTextVisibility(fPasswordVisible);
+ if (m_pPasswordRepeatLineEdit)
+ m_pPasswordRepeatLineEdit->toggleTextVisibility(fPasswordVisible);
+}
+
+void UIUserNamePasswordEditor::sltUserNameChanged()
+{
+ isUserNameComplete();
+ emit sigUserNameChanged(m_pUserNameLineEdit->text());
+}
+
+void UIUserNamePasswordEditor::sltPasswordChanged()
+{
+ isPasswordComplete();
+ emit sigPasswordChanged(m_pPasswordLineEdit->text());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIUserNamePasswordEditor.h b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIUserNamePasswordEditor.h
new file mode 100644
index 00000000..b488656e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIUserNamePasswordEditor.h
@@ -0,0 +1,145 @@
+/* $Id: UIUserNamePasswordEditor.h $ */
+/** @file
+ * VBox Qt GUI - UIUserNamePasswordEditor class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_editors_UIUserNamePasswordEditor_h
+#define FEQT_INCLUDED_SRC_wizards_editors_UIUserNamePasswordEditor_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QLineEdit>
+#include <QWidget>
+
+/* Local includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QILineEdit;
+class QIToolButton;
+class UIMarkableLineEdit;
+class UIPasswordLineEdit;
+
+class SHARED_LIBRARY_STUFF UIPasswordLineEdit : public QLineEdit
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigTextVisibilityToggled(bool fTextVisible);
+
+public:
+
+ UIPasswordLineEdit(QWidget *pParent = 0);
+ void toggleTextVisibility(bool fTextVisible);
+ void mark(bool fError, const QString &strErrorToolTip);
+
+protected:
+
+ virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
+
+private slots:
+
+ void sltHandleTextVisibilityChange();
+
+private:
+
+ void prepare();
+ void adjustTextVisibilityButtonGeometry();
+
+ QIToolButton *m_pTextVisibilityButton;
+ QIcon m_markIcon;
+ QLabel *m_pErrorIconLabel;
+ QString m_strErrorToolTip;
+ /** When true the line edit is marked with some icon to indicate some error. */
+ bool m_fMarkForError;
+};
+
+class SHARED_LIBRARY_STUFF UIUserNamePasswordEditor : public QIWithRetranslateUI<QWidget>
+{
+
+ Q_OBJECT;
+
+signals:
+
+ void sigUserNameChanged(const QString &strUserName);
+ void sigPasswordChanged(const QString &strPassword);
+
+public:
+
+ UIUserNamePasswordEditor(QWidget *pParent = 0);
+
+ QString userName() const;
+ void setUserName(const QString &strUserName);
+
+ QString password() const;
+ void setPassword(const QString &strPassword);
+
+ /** Returns false if username or password fields are empty, or password fields do not match. */
+ bool isComplete();
+
+ /** When fEnabled true place holder texts for the line edits are shown. */
+ void setPlaceholderTextEnabled(bool fEnabled);
+ void setLabelsVisible(bool fVisible);
+
+protected:
+
+ void retranslateUi();
+
+private slots:
+
+ void sltHandlePasswordVisibility(bool fPasswordVisible);
+ void sltUserNameChanged();
+ void sltPasswordChanged();
+
+private:
+
+ void prepare();
+ template <class T>
+ void addLineEdit(int &iRow, QLabel *&pLabel, T *&pLineEdit, QGridLayout *pLayout);
+
+ bool isUserNameComplete();
+ bool isPasswordComplete();
+
+ UIMarkableLineEdit *m_pUserNameLineEdit;
+ UIPasswordLineEdit *m_pPasswordLineEdit;
+ UIPasswordLineEdit *m_pPasswordRepeatLineEdit;
+
+ QLabel *m_pUserNameLabel;
+ QLabel *m_pPasswordLabel;
+ QLabel *m_pPasswordRepeatLabel;
+
+ bool m_fShowPlaceholderText;
+ bool m_fLabelsVisible;
+
+ QString m_strPasswordError;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_editors_UIUserNamePasswordEditor_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardCloneVMEditors.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardCloneVMEditors.cpp
new file mode 100644
index 00000000..c5316bd3
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardCloneVMEditors.cpp
@@ -0,0 +1,590 @@
+/* $Id: UIWizardCloneVMEditors.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUserNamePasswordEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDir>
+#include <QLabel>
+#include <QRadioButton>
+#include <QGridLayout>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "UICommon.h"
+#include "UIFilePathSelector.h"
+#include "UIWizardCloneVM.h"
+#include "UIWizardCloneVMEditors.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+#include "COMEnums.h"
+#include "CSystemProperties.h"
+
+
+/*********************************************************************************************************************************
+* UICloneVMNamePathEditor implementation. *
+*********************************************************************************************************************************/
+
+UICloneVMNamePathEditor::UICloneVMNamePathEditor(const QString &strOriginalName, const QString &strDefaultPath, QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QGroupBox>(pParent)
+ , m_pContainerLayout(0)
+ , m_pNameLineEdit(0)
+ , m_pPathSelector(0)
+ , m_pNameLabel(0)
+ , m_pPathLabel(0)
+ , m_strOriginalName(strOriginalName)
+ , m_strDefaultPath(strDefaultPath)
+{
+ prepare();
+}
+
+bool UICloneVMNamePathEditor::isComplete(const QString &strMachineGroup)
+{
+ AssertReturn(m_pNameLineEdit && m_pPathSelector, false);
+
+ bool fInvalidName = m_pNameLineEdit->text().isEmpty();
+ m_pNameLineEdit->mark(fInvalidName, UIWizardCloneVM::tr("Clone name cannot be empty"));
+
+ const QString &strPath = m_pPathSelector->path();
+ QDir dir(strPath);
+ bool fInvalidPath = strPath.isEmpty() || !dir.exists() || !dir.isReadable();
+ m_pPathSelector->mark(fInvalidPath, UIWizardCloneVM::tr("Path is invalid"));
+
+ /* Check if there is already a machine folder for this name and path: */
+ bool fExists = false;
+ if (!fInvalidName)
+ {
+ CVirtualBox vbox = uiCommon().virtualBox();
+ QString strCloneFilePath =
+ vbox.ComposeMachineFilename(m_pNameLineEdit->text(), strMachineGroup, QString(), m_pPathSelector->path());
+ fExists = QDir(QDir::toNativeSeparators(QFileInfo(strCloneFilePath).absolutePath())).exists();
+ m_pNameLineEdit->mark(fExists, UIWizardCloneVM::tr("The clone name is not unique"));
+ }
+
+ return !fInvalidName && !fInvalidPath && !fExists;
+}
+
+QString UICloneVMNamePathEditor::cloneName() const
+{
+ if (m_pNameLineEdit)
+ return m_pNameLineEdit->text();
+ return QString();
+}
+
+void UICloneVMNamePathEditor::setCloneName(const QString &strName)
+{
+ if (m_pNameLineEdit)
+ m_pNameLineEdit->setText(strName);
+}
+
+QString UICloneVMNamePathEditor::clonePath() const
+{
+ if (m_pPathSelector)
+ return m_pPathSelector->path();
+ return QString();
+}
+
+void UICloneVMNamePathEditor::setClonePath(const QString &strPath)
+{
+ if (m_pPathSelector)
+ m_pPathSelector->setPath(strPath);
+}
+
+void UICloneVMNamePathEditor::setFirstColumnWidth(int iWidth)
+{
+ if (m_pContainerLayout)
+ m_pContainerLayout->setColumnMinimumWidth(0, iWidth);
+}
+
+int UICloneVMNamePathEditor::firstColumnWidth() const
+{
+ int iMaxWidth = 0;
+ if (m_pNameLabel)
+ iMaxWidth = qMax(iMaxWidth, m_pNameLabel->minimumSizeHint().width());
+ if (m_pPathLabel)
+ iMaxWidth = qMax(iMaxWidth, m_pPathLabel->minimumSizeHint().width());
+ return iMaxWidth;
+}
+
+void UICloneVMNamePathEditor::setLayoutContentsMargins(int iLeft, int iTop, int iRight, int iBottom)
+{
+ if (m_pContainerLayout)
+ m_pContainerLayout->setContentsMargins(iLeft, iTop, iRight, iBottom);
+}
+
+void UICloneVMNamePathEditor::prepare()
+{
+ m_pContainerLayout = new QGridLayout(this);
+
+
+ m_pNameLabel = new QLabel;
+ if (m_pNameLabel)
+ {
+ m_pNameLabel->setAlignment(Qt::AlignRight);
+ m_pNameLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_pContainerLayout->addWidget(m_pNameLabel, 0, 0, 1, 1);
+ }
+
+ m_pNameLineEdit = new UIMarkableLineEdit();
+ if (m_pNameLineEdit)
+ {
+ m_pContainerLayout->addWidget(m_pNameLineEdit, 0, 1, 1, 1);
+ m_pNameLineEdit->setText(UIWizardCloneVM::tr("%1 Clone").arg(m_strOriginalName));
+ connect(m_pNameLineEdit, &UIMarkableLineEdit::textChanged,
+ this, &UICloneVMNamePathEditor::sigCloneNameChanged);
+ if (m_pNameLabel)
+ m_pNameLabel->setBuddy(m_pNameLineEdit);
+ }
+
+ m_pPathLabel = new QLabel(this);
+ if (m_pPathLabel)
+ {
+ m_pPathLabel->setAlignment(Qt::AlignRight);
+ m_pPathLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_pContainerLayout->addWidget(m_pPathLabel, 1, 0, 1, 1);
+ }
+
+ m_pPathSelector = new UIFilePathSelector(this);
+ if (m_pPathSelector)
+ {
+ m_pContainerLayout->addWidget(m_pPathSelector, 1, 1, 1, 1);
+ m_pPathSelector->setPath(m_strDefaultPath);
+ connect(m_pPathSelector, &UIFilePathSelector::pathChanged,
+ this, &UICloneVMNamePathEditor::sigClonePathChanged);
+ if (m_pPathLabel)
+ m_pPathLabel->setBuddy(m_pPathSelector);
+
+ }
+
+ retranslateUi();
+}
+
+void UICloneVMNamePathEditor::retranslateUi()
+{
+ if (m_pNameLabel)
+ m_pNameLabel->setText(UIWizardCloneVM::tr("&Name:"));
+ if (m_pPathLabel)
+ m_pPathLabel->setText(UIWizardCloneVM::tr("&Path:"));
+ if (m_pNameLineEdit)
+ m_pNameLineEdit->setToolTip(UIWizardCloneVM::tr("Holds a name for the new virtual machine."));
+ if (m_pPathSelector)
+ m_pPathSelector->setToolTip(UIWizardCloneVM::tr("Specifies The location of the new virtual machine in host's storage."));
+}
+
+
+/*********************************************************************************************************************************
+* UICloneVMAdditionalOptionsEditor implementation. *
+*********************************************************************************************************************************/
+
+
+UICloneVMAdditionalOptionsEditor::UICloneVMAdditionalOptionsEditor(QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QGroupBox>(pParent)
+ , m_pContainerLayout(0)
+ , m_pMACComboBoxLabel(0)
+ , m_pMACComboBox(0)
+ , m_pAdditionalOptionsLabel(0)
+ , m_pKeepDiskNamesCheckBox(0)
+ , m_pKeepHWUUIDsCheckBox(0)
+{
+ prepare();
+}
+
+void UICloneVMAdditionalOptionsEditor::setLayoutContentsMargins(int iLeft, int iTop, int iRight, int iBottom)
+{
+ if (m_pContainerLayout)
+ m_pContainerLayout->setContentsMargins(iLeft, iTop, iRight, iBottom);
+}
+
+void UICloneVMAdditionalOptionsEditor::setFirstColumnWidth(int iWidth)
+{
+ if (m_pContainerLayout)
+ m_pContainerLayout->setColumnMinimumWidth(0, iWidth);
+}
+
+int UICloneVMAdditionalOptionsEditor::firstColumnWidth() const
+{
+ int iMaxWidth = 0;
+ if (m_pMACComboBoxLabel)
+ iMaxWidth = qMax(iMaxWidth, m_pMACComboBoxLabel->minimumSizeHint().width());
+ if (m_pAdditionalOptionsLabel)
+ iMaxWidth = qMax(iMaxWidth, m_pAdditionalOptionsLabel->minimumSizeHint().width());
+ return iMaxWidth;
+}
+
+MACAddressClonePolicy UICloneVMAdditionalOptionsEditor::macAddressClonePolicy() const
+{
+ return m_pMACComboBox->currentData().value<MACAddressClonePolicy>();
+}
+
+void UICloneVMAdditionalOptionsEditor::setMACAddressClonePolicy(MACAddressClonePolicy enmMACAddressClonePolicy)
+{
+ const int iIndex = m_pMACComboBox->findData(enmMACAddressClonePolicy);
+ AssertMsg(iIndex != -1, ("Data not found!"));
+ m_pMACComboBox->setCurrentIndex(iIndex);
+}
+
+bool UICloneVMAdditionalOptionsEditor::keepHardwareUUIDs() const
+{
+ if (m_pKeepHWUUIDsCheckBox)
+ return m_pKeepHWUUIDsCheckBox->isChecked();
+ return false;
+}
+
+bool UICloneVMAdditionalOptionsEditor::keepDiskNames() const
+{
+ if (m_pKeepDiskNamesCheckBox)
+ m_pKeepDiskNamesCheckBox->isChecked();
+ return false;
+}
+
+void UICloneVMAdditionalOptionsEditor::prepare()
+{
+ m_pContainerLayout = new QGridLayout(this);
+
+ m_pMACComboBoxLabel = new QLabel;
+ if (m_pMACComboBoxLabel)
+ {
+ m_pMACComboBoxLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
+ m_pContainerLayout->addWidget(m_pMACComboBoxLabel, 2, 0, 1, 1);
+ }
+
+ m_pMACComboBox = new QComboBox;
+ if (m_pMACComboBox)
+ {
+ m_pContainerLayout->addWidget(m_pMACComboBox, 2, 1, 1, 1);
+ connect(m_pMACComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &UICloneVMAdditionalOptionsEditor::sltMACAddressClonePolicyChanged);
+ if (m_pMACComboBoxLabel)
+ m_pMACComboBoxLabel->setBuddy(m_pMACComboBox);
+ }
+ m_pMACComboBox->blockSignals(true);
+ populateMACAddressClonePolicies();
+ m_pMACComboBox->blockSignals(false);
+
+ /* Load currently supported clone options: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ const QVector<KCloneOptions> supportedOptions = comProperties.GetSupportedCloneOptions();
+ /* Check whether we support additional clone options at all: */
+ int iVerticalPosition = 3;
+ const bool fSupportedKeepDiskNames = supportedOptions.contains(KCloneOptions_KeepDiskNames);
+ const bool fSupportedKeepHWUUIDs = supportedOptions.contains(KCloneOptions_KeepHwUUIDs);
+ if (fSupportedKeepDiskNames || fSupportedKeepHWUUIDs)
+ {
+ m_pAdditionalOptionsLabel = new QLabel;
+ if (m_pAdditionalOptionsLabel)
+ {
+ m_pAdditionalOptionsLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
+ m_pContainerLayout->addWidget(m_pAdditionalOptionsLabel, iVerticalPosition, 0, 1, 1);
+ }
+ }
+ if (fSupportedKeepDiskNames)
+ {
+ m_pKeepDiskNamesCheckBox = new QCheckBox;
+ if (m_pKeepDiskNamesCheckBox)
+ {
+ m_pContainerLayout->addWidget(m_pKeepDiskNamesCheckBox, iVerticalPosition++, 1, 1, 1);
+ connect(m_pKeepDiskNamesCheckBox, &QCheckBox::toggled,
+ this, &UICloneVMAdditionalOptionsEditor::sigKeepDiskNamesToggled);
+ }
+ }
+ if (fSupportedKeepHWUUIDs)
+ {
+ m_pKeepHWUUIDsCheckBox = new QCheckBox;
+ if (m_pKeepHWUUIDsCheckBox)
+ {
+ m_pContainerLayout->addWidget(m_pKeepHWUUIDsCheckBox, iVerticalPosition++, 1, 1, 1);
+ connect(m_pKeepHWUUIDsCheckBox, &QCheckBox::toggled,
+ this, &UICloneVMAdditionalOptionsEditor::sigKeepHardwareUUIDsToggled);
+ }
+ }
+
+
+ retranslateUi();
+}
+
+void UICloneVMAdditionalOptionsEditor::retranslateUi()
+{
+ m_pMACComboBoxLabel->setText(UIWizardCloneVM::tr("MAC Address P&olicy:"));
+ m_pMACComboBox->setToolTip(UIWizardCloneVM::tr("Determines MAC address policy for clonning:"));
+ for (int i = 0; i < m_pMACComboBox->count(); ++i)
+ {
+ const MACAddressClonePolicy enmPolicy = m_pMACComboBox->itemData(i).value<MACAddressClonePolicy>();
+ switch (enmPolicy)
+ {
+ case MACAddressClonePolicy_KeepAllMACs:
+ {
+ m_pMACComboBox->setItemText(i, UIWizardCloneVM::tr("Include all network adapter MAC addresses"));
+ m_pMACComboBox->setItemData(i, UIWizardCloneVM::tr("Include all network adapter MAC addresses during "
+ "cloning."), Qt::ToolTipRole);
+ break;
+ }
+ case MACAddressClonePolicy_KeepNATMACs:
+ {
+ m_pMACComboBox->setItemText(i, UIWizardCloneVM::tr("Include only NAT network adapter MAC addresses"));
+ m_pMACComboBox->setItemData(i, UIWizardCloneVM::tr("Include only NAT network adapter MAC addresses during "
+ "cloning."), Qt::ToolTipRole);
+ break;
+ }
+ case MACAddressClonePolicy_StripAllMACs:
+ {
+ m_pMACComboBox->setItemText(i, UIWizardCloneVM::tr("Generate new MAC addresses for all network adapters"));
+ m_pMACComboBox->setItemData(i, UIWizardCloneVM::tr("Generate new MAC addresses for all network adapters during "
+ "cloning."), Qt::ToolTipRole);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (m_pAdditionalOptionsLabel)
+ m_pAdditionalOptionsLabel->setText(UIWizardCloneVM::tr("Additional Options:"));
+ if (m_pKeepDiskNamesCheckBox)
+ {
+ m_pKeepDiskNamesCheckBox->setToolTip(UIWizardCloneVM::tr("When checked, disk names will be preserved during cloning."));
+ m_pKeepDiskNamesCheckBox->setText(UIWizardCloneVM::tr("Keep &Disk Names"));
+ }
+ if (m_pKeepHWUUIDsCheckBox)
+ {
+ m_pKeepHWUUIDsCheckBox->setToolTip(UIWizardCloneVM::tr("When checked, hardware UUIDs will be preserved during cloning."));
+ m_pKeepHWUUIDsCheckBox->setText(UIWizardCloneVM::tr("Keep Hard&ware UUIDs"));
+ }
+}
+
+void UICloneVMAdditionalOptionsEditor::sltMACAddressClonePolicyChanged()
+{
+ emit sigMACAddressClonePolicyChanged(macAddressClonePolicy());
+ updateMACAddressClonePolicyComboToolTip();
+}
+
+void UICloneVMAdditionalOptionsEditor::updateMACAddressClonePolicyComboToolTip()
+{
+ if (!m_pMACComboBox)
+ return;
+ const QString strCurrentToolTip = m_pMACComboBox->currentData(Qt::ToolTipRole).toString();
+ AssertMsg(!strCurrentToolTip.isEmpty(), ("Tool-tip data not found!"));
+ m_pMACComboBox->setToolTip(strCurrentToolTip);
+}
+
+void UICloneVMAdditionalOptionsEditor::populateMACAddressClonePolicies()
+{
+ AssertReturnVoid(m_pMACComboBox && m_pMACComboBox->count() == 0);
+
+ /* Map known clone options to known MAC address export policies: */
+ QMap<KCloneOptions, MACAddressClonePolicy> knownOptions;
+ knownOptions[KCloneOptions_KeepAllMACs] = MACAddressClonePolicy_KeepAllMACs;
+ knownOptions[KCloneOptions_KeepNATMACs] = MACAddressClonePolicy_KeepNATMACs;
+
+ /* Load currently supported clone options: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ const QVector<KCloneOptions> supportedOptions = comProperties.GetSupportedCloneOptions();
+
+ /* Check which of supported options/policies are known: */
+ QList<MACAddressClonePolicy> supportedPolicies;
+ foreach (const KCloneOptions &enmOption, supportedOptions)
+ if (knownOptions.contains(enmOption))
+ supportedPolicies << knownOptions.value(enmOption);
+
+ /* Add supported policies first: */
+ foreach (const MACAddressClonePolicy &enmPolicy, supportedPolicies)
+ m_pMACComboBox->addItem(QString(), QVariant::fromValue(enmPolicy));
+
+ /* Add hardcoded policy finally: */
+ m_pMACComboBox->addItem(QString(), QVariant::fromValue(MACAddressClonePolicy_StripAllMACs));
+
+ /* Set default: */
+ if (supportedPolicies.contains(MACAddressClonePolicy_KeepNATMACs))
+ setMACAddressClonePolicy(MACAddressClonePolicy_KeepNATMACs);
+ else
+ setMACAddressClonePolicy(MACAddressClonePolicy_StripAllMACs);
+}
+
+
+/*********************************************************************************************************************************
+* UICloneVMAdditionalOptionsEditor implementation. *
+*********************************************************************************************************************************/
+
+UICloneVMCloneTypeGroupBox::UICloneVMCloneTypeGroupBox(QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QGroupBox>(pParent)
+ , m_pButtonGroup(0)
+ , m_pFullCloneRadio(0)
+ , m_pLinkedCloneRadio(0)
+{
+ prepare();
+}
+
+bool UICloneVMCloneTypeGroupBox::isFullClone() const
+{
+ if (m_pFullCloneRadio)
+ return m_pFullCloneRadio->isChecked();
+ return true;
+}
+
+void UICloneVMCloneTypeGroupBox::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ AssertReturnVoid(pMainLayout);
+ /* Prepare clone-type options button-group: */
+ m_pButtonGroup = new QButtonGroup(this);
+ if (m_pButtonGroup)
+ {
+ /* Prepare full clone option radio-button: */
+ m_pFullCloneRadio = new QRadioButton(this);
+ if (m_pFullCloneRadio)
+ {
+ m_pFullCloneRadio->setChecked(true);
+ m_pButtonGroup->addButton(m_pFullCloneRadio);
+ pMainLayout->addWidget(m_pFullCloneRadio);
+ }
+
+ /* Load currently supported clone options: */
+ CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
+ const QVector<KCloneOptions> supportedOptions = comProperties.GetSupportedCloneOptions();
+ /* Check whether we support linked clone option at all: */
+ const bool fSupportedLinkedClone = supportedOptions.contains(KCloneOptions_Link);
+
+ /* Prepare linked clone option radio-button: */
+ if (fSupportedLinkedClone)
+ {
+ m_pLinkedCloneRadio = new QRadioButton(this);
+ if (m_pLinkedCloneRadio)
+ {
+ m_pButtonGroup->addButton(m_pLinkedCloneRadio);
+ pMainLayout->addWidget(m_pLinkedCloneRadio);
+ }
+ }
+ }
+
+ connect(m_pButtonGroup, static_cast<void(QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked),
+ this, &UICloneVMCloneTypeGroupBox::sltButtonClicked);
+
+ retranslateUi();
+}
+
+void UICloneVMCloneTypeGroupBox::retranslateUi()
+{
+ if (m_pFullCloneRadio)
+ {
+ m_pFullCloneRadio->setText(UIWizardCloneVM::tr("&Full clone"));
+ m_pFullCloneRadio->setToolTip(UIWizardCloneVM::tr("When chosen, all the virtual disks of the source vm are also cloned."));
+ }
+ if (m_pLinkedCloneRadio)
+ {
+ m_pLinkedCloneRadio->setText(UIWizardCloneVM::tr("&Linked clone"));
+ m_pLinkedCloneRadio->setToolTip(UIWizardCloneVM::tr("When chosen, the cloned vm will save space by sharing the source VM's disk images."));
+ }
+}
+
+void UICloneVMCloneTypeGroupBox::sltButtonClicked(QAbstractButton *)
+{
+ emit sigFullCloneSelected(m_pFullCloneRadio && m_pFullCloneRadio->isChecked());
+}
+
+
+/*********************************************************************************************************************************
+* UICloneVMAdditionalOptionsEditor implementation. *
+*********************************************************************************************************************************/
+
+UICloneVMCloneModeGroupBox::UICloneVMCloneModeGroupBox(bool fShowChildsOption, QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QGroupBox>(pParent)
+ , m_fShowChildsOption(fShowChildsOption)
+ , m_pMachineRadio(0)
+ , m_pMachineAndChildsRadio(0)
+ , m_pAllRadio(0)
+{
+ prepare();
+}
+
+void UICloneVMCloneModeGroupBox::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ AssertReturnVoid(pMainLayout);
+
+ QButtonGroup *pButtonGroup = new QButtonGroup(this);
+ m_pMachineRadio = new QRadioButton(this);
+ if (m_pMachineRadio)
+ {
+ m_pMachineRadio->setChecked(true);
+ pButtonGroup->addButton(m_pMachineRadio);
+ }
+ m_pMachineAndChildsRadio = new QRadioButton(this);
+ if (m_pMachineAndChildsRadio)
+ {
+ if (!m_fShowChildsOption)
+ m_pMachineAndChildsRadio->hide();
+ pButtonGroup->addButton(m_pMachineAndChildsRadio);
+ }
+
+ m_pAllRadio = new QRadioButton(this);
+ if (m_pAllRadio)
+ pButtonGroup->addButton(m_pAllRadio);
+
+ pMainLayout->addWidget(m_pMachineRadio);
+ pMainLayout->addWidget(m_pMachineAndChildsRadio);
+ pMainLayout->addWidget(m_pAllRadio);
+ pMainLayout->addStretch();
+
+
+ connect(pButtonGroup, static_cast<void(QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked),
+ this, &UICloneVMCloneModeGroupBox::sltButtonClicked);
+
+ retranslateUi();
+}
+
+void UICloneVMCloneModeGroupBox::retranslateUi()
+{
+ if (m_pMachineRadio)
+ {
+ m_pMachineRadio->setText(UIWizardCloneVM::tr("Current &machine state"));
+ m_pMachineRadio->setToolTip(UIWizardCloneVM::tr("When chosen, only the current state of the source vm is cloned."));
+ }
+ if (m_pMachineAndChildsRadio)
+ m_pMachineAndChildsRadio->setText(UIWizardCloneVM::tr("Current &snapshot tree branch"));
+ if (m_pAllRadio)
+ {
+ m_pAllRadio->setText(UIWizardCloneVM::tr("&Everything"));
+ m_pAllRadio->setToolTip(UIWizardCloneVM::tr("When chosen, all the saved states of the source vm are also cloned."));
+ }
+}
+
+
+void UICloneVMCloneModeGroupBox::sltButtonClicked()
+{
+ emit sigCloneModeChanged(cloneMode());
+}
+
+KCloneMode UICloneVMCloneModeGroupBox::cloneMode() const
+{
+ KCloneMode enmCloneMode = KCloneMode_MachineState;
+ if (m_pMachineAndChildsRadio && m_pMachineAndChildsRadio->isChecked())
+ enmCloneMode = KCloneMode_MachineAndChildStates;
+ else if (m_pAllRadio && m_pAllRadio->isChecked())
+ enmCloneMode = KCloneMode_AllStates;
+ return enmCloneMode;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardCloneVMEditors.h b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardCloneVMEditors.h
new file mode 100644
index 00000000..33e543e5
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardCloneVMEditors.h
@@ -0,0 +1,212 @@
+/* $Id: UIWizardCloneVMEditors.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardDiskEditors class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_editors_UIWizardCloneVMEditors_h
+#define FEQT_INCLUDED_SRC_wizards_editors_UIWizardCloneVMEditors_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QGroupBox>
+
+/* Local includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Other VBox includes: */
+#include "COMEnums.h"
+
+
+/* Forward declarations: */
+class QAbstractButton;
+class QButtonGroup;
+class QCheckBox;
+class QGridLayout;
+class QComboBox;
+class QLabel;
+class QRadioButton;
+class QILineEdit;
+class UIFilePathSelector;
+class UIMarkableLineEdit;
+
+/** MAC address policies. */
+enum MACAddressClonePolicy
+{
+ MACAddressClonePolicy_KeepAllMACs,
+ MACAddressClonePolicy_KeepNATMACs,
+ MACAddressClonePolicy_StripAllMACs,
+ MACAddressClonePolicy_MAX
+};
+Q_DECLARE_METATYPE(MACAddressClonePolicy);
+
+class UICloneVMNamePathEditor : public QIWithRetranslateUI<QGroupBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigCloneNameChanged(const QString &strCloneName);
+ void sigClonePathChanged(const QString &strClonePath);
+
+public:
+
+ UICloneVMNamePathEditor(const QString &strOriginalName, const QString &strDefaultPath, QWidget *pParent = 0);
+
+ void setFirstColumnWidth(int iWidth);
+ int firstColumnWidth() const;
+
+ void setLayoutContentsMargins(int iLeft, int iTop, int iRight, int iBottom);
+
+ QString cloneName() const;
+ void setCloneName(const QString &strName);
+
+ QString clonePath() const;
+ void setClonePath(const QString &strPath);
+
+ bool isComplete(const QString &strMachineGroup);
+
+private:
+
+ void prepare();
+ virtual void retranslateUi() /* override final */;
+
+ QGridLayout *m_pContainerLayout;
+ UIMarkableLineEdit *m_pNameLineEdit;
+ UIFilePathSelector *m_pPathSelector;
+ QLabel *m_pNameLabel;
+ QLabel *m_pPathLabel;
+
+ QString m_strOriginalName;
+ QString m_strDefaultPath;
+};
+
+
+class UICloneVMAdditionalOptionsEditor : public QIWithRetranslateUI<QGroupBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigMACAddressClonePolicyChanged(MACAddressClonePolicy enmMACAddressClonePolicy);
+ void sigKeepDiskNamesToggled(bool fKeepDiskNames);
+ void sigKeepHardwareUUIDsToggled(bool fKeepHardwareUUIDs);
+
+public:
+
+ UICloneVMAdditionalOptionsEditor(QWidget *pParent = 0);
+
+ bool isComplete();
+
+ void setLayoutContentsMargins(int iLeft, int iTop, int iRight, int iBottom);
+
+ MACAddressClonePolicy macAddressClonePolicy() const;
+ void setMACAddressClonePolicy(MACAddressClonePolicy enmMACAddressClonePolicy);
+
+ void setFirstColumnWidth(int iWidth);
+ int firstColumnWidth() const;
+
+ bool keepHardwareUUIDs() const;
+ bool keepDiskNames() const;
+
+private slots:
+
+ void sltMACAddressClonePolicyChanged();
+
+private:
+
+ void prepare();
+ virtual void retranslateUi() /* override final */;
+ void populateMACAddressClonePolicies();
+ void updateMACAddressClonePolicyComboToolTip();
+
+ QGridLayout *m_pContainerLayout;
+ QLabel *m_pMACComboBoxLabel;
+ QComboBox *m_pMACComboBox;
+ QLabel *m_pAdditionalOptionsLabel;
+ QCheckBox *m_pKeepDiskNamesCheckBox;
+ QCheckBox *m_pKeepHWUUIDsCheckBox;
+};
+
+class UICloneVMCloneTypeGroupBox : public QIWithRetranslateUI<QGroupBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigFullCloneSelected(bool fSelected);
+
+public:
+
+ UICloneVMCloneTypeGroupBox(QWidget *pParent = 0);
+ bool isFullClone() const;
+
+private slots:
+
+ void sltButtonClicked(QAbstractButton *);
+
+private:
+
+ void prepare();
+ virtual void retranslateUi() /* override final */;
+
+ QButtonGroup *m_pButtonGroup;
+ QRadioButton *m_pFullCloneRadio;
+ QRadioButton *m_pLinkedCloneRadio;
+};
+
+
+class UICloneVMCloneModeGroupBox : public QIWithRetranslateUI<QGroupBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigCloneModeChanged(KCloneMode enmCloneMode);
+
+public:
+
+ UICloneVMCloneModeGroupBox(bool fShowChildsOption, QWidget *pParent = 0);
+ KCloneMode cloneMode() const;
+
+private slots:
+
+ void sltButtonClicked();
+
+private:
+
+ void prepare();
+ virtual void retranslateUi() /* override final */;
+
+ bool m_fShowChildsOption;
+ QRadioButton *m_pMachineRadio;
+ QRadioButton *m_pMachineAndChildsRadio;
+ QRadioButton *m_pAllRadio;
+};
+
+
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_editors_UIWizardCloneVMEditors_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardDiskEditors.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardDiskEditors.cpp
new file mode 100644
index 00000000..5cfd028f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardDiskEditors.cpp
@@ -0,0 +1,700 @@
+/* $Id: UIWizardDiskEditors.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUserNamePasswordEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QDir>
+#include <QFileInfo>
+#include <QLabel>
+#include <QRadioButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "QIToolButton.h"
+#include "QIRichTextLabel.h"
+#include "UICommon.h"
+#include "UIConverter.h"
+#include "UIFilePathSelector.h"
+#include "UIHostnameDomainNameEditor.h"
+#include "UIIconPool.h"
+#include "UIMediumSizeEditor.h"
+#include "UIUserNamePasswordEditor.h"
+#include "UIWizardDiskEditors.h"
+#include "UIWizardNewVM.h"
+#include "UIWizardNewVMDiskPage.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+#include "iprt/fs.h"
+#include "CSystemProperties.h"
+
+
+/*********************************************************************************************************************************
+* UIWizardDiskEditors implementation. *
+*********************************************************************************************************************************/
+
+QString UIWizardDiskEditors::appendExtension(const QString &strName, const QString &strExtension)
+{
+ /* Convert passed name to native separators: */
+ QString strFileName = QDir::toNativeSeparators(strName);
+
+ /* Remove all trailing dots to avoid multiple dots before extension: */
+ int iLen;
+ while (iLen = strFileName.length(), iLen > 0 && strFileName[iLen - 1] == '.')
+ strFileName.truncate(iLen - 1);
+
+ /* Add passed extension if its not done yet: */
+ if (QFileInfo(strFileName).suffix().toLower() != strExtension)
+ strFileName += QString(".%1").arg(strExtension);
+
+ /* Return result: */
+ return strFileName;
+}
+
+QString UIWizardDiskEditors::constructMediumFilePath(const QString &strFileName, const QString &strPath)
+{
+ /* Wrap file-info around received file name: */
+ QFileInfo fileInfo(strFileName);
+ /* If path-info is relative or there is no path-info at all: */
+ if (fileInfo.fileName() == strFileName || fileInfo.isRelative())
+ {
+ /* Resolve path on the basis of path we have: */
+ fileInfo = QFileInfo(strPath, strFileName);
+ }
+ /* Return full absolute hard disk file path: */
+ return QDir::toNativeSeparators(fileInfo.absoluteFilePath());
+}
+
+bool UIWizardDiskEditors::checkFATSizeLimitation(const qulonglong uVariant, const QString &strMediumPath, const qulonglong uSize)
+{
+ /* If the hard disk is split into 2GB parts then no need to make further checks: */
+ if (uVariant & KMediumVariant_VmdkSplit2G)
+ return true;
+ RTFSTYPE enmType;
+ int rc = RTFsQueryType(QFileInfo(strMediumPath).absolutePath().toLatin1().constData(), &enmType);
+ if (RT_SUCCESS(rc))
+ {
+ if (enmType == RTFSTYPE_FAT)
+ {
+ /* Limit the medium size to 4GB. minus 128 MB for file overhead: */
+ qulonglong fatLimit = _4G - _128M;
+ if (uSize >= fatLimit)
+ return false;
+ }
+ }
+ return true;
+}
+
+QString UIWizardDiskEditors::openFileDialogForDiskFile(const QString &strInitialPath, const CMediumFormat &comMediumFormat,
+ KDeviceType enmDeviceType, QWidget *pParent)
+{
+ QString strChosenFilePath;
+ QFileInfo initialPath(strInitialPath);
+ QDir folder = initialPath.path();
+ QString strFileName = initialPath.fileName();
+
+ // /* Set the first parent folder that exists as the current: */
+ while (!folder.exists() && !folder.isRoot())
+ {
+ QFileInfo folderInfo(folder.absolutePath());
+ if (folder == QDir(folderInfo.absolutePath()))
+ break;
+ folder = folderInfo.absolutePath();
+ }
+ AssertReturn(folder.exists() && !folder.isRoot(), strChosenFilePath);
+
+ QVector<QString> fileExtensions;
+ QVector<KDeviceType> deviceTypes;
+ comMediumFormat.DescribeFileExtensions(fileExtensions, deviceTypes);
+ QStringList validExtensionList;
+ for (int i = 0; i < fileExtensions.size(); ++i)
+ if (deviceTypes[i] == enmDeviceType)
+ validExtensionList << QString("*.%1").arg(fileExtensions[i]);
+ /* Compose full filter list: */
+ QString strBackendsList = QString("%1 (%2)").arg(comMediumFormat.GetName()).arg(validExtensionList.join(" "));
+
+ strChosenFilePath = QIFileDialog::getSaveFileName(folder.absoluteFilePath(strFileName),
+ strBackendsList, pParent,
+ UICommon::tr("Please choose a location for new virtual hard disk file"));
+ return strChosenFilePath;
+}
+
+QString UIWizardDiskEditors::defaultExtension(const CMediumFormat &mediumFormatRef, KDeviceType enmDeviceType)
+{
+ if (!mediumFormatRef.isNull())
+ {
+ /* Load extension / device list: */
+ QVector<QString> fileExtensions;
+ QVector<KDeviceType> deviceTypes;
+ CMediumFormat mediumFormat(mediumFormatRef);
+ mediumFormat.DescribeFileExtensions(fileExtensions, deviceTypes);
+ for (int i = 0; i < fileExtensions.size(); ++i)
+ if (deviceTypes[i] == enmDeviceType)
+ return fileExtensions[i].toLower();
+ }
+ AssertMsgFailed(("Extension can't be NULL!\n"));
+ return QString();
+}
+
+QString UIWizardDiskEditors::stripFormatExtension(const QString &strFileName, const QStringList &formatExtensions)
+{
+ QString result(strFileName);
+ foreach (const QString &strExtension, formatExtensions)
+ {
+ if (strFileName.endsWith(strExtension, Qt::CaseInsensitive))
+ {
+ /* Add the dot to extenstion: */
+ QString strExtensionWithDot(strExtension);
+ strExtensionWithDot.prepend('.');
+ int iIndex = strFileName.lastIndexOf(strExtensionWithDot, -1, Qt::CaseInsensitive);
+ result.remove(iIndex, strExtensionWithDot.length());
+ }
+ }
+ return result;
+}
+
+
+/*********************************************************************************************************************************
+* UIDiskVariantWidget implementation. *
+*********************************************************************************************************************************/
+
+
+UIDiskVariantWidget::UIDiskVariantWidget(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pFixedCheckBox(0)
+ , m_pSplitBox(0)
+{
+ prepare();
+}
+
+void UIDiskVariantWidget::prepare()
+{
+ QVBoxLayout *pVariantLayout = new QVBoxLayout(this);
+ AssertReturnVoid(pVariantLayout);
+ m_pFixedCheckBox = new QCheckBox;
+ m_pSplitBox = new QCheckBox;
+ connect(m_pFixedCheckBox, &QCheckBox::toggled, this, &UIDiskVariantWidget::sltVariantChanged);
+ connect(m_pSplitBox, &QCheckBox::toggled, this, &UIDiskVariantWidget::sltVariantChanged);
+ pVariantLayout->addWidget(m_pFixedCheckBox);
+ pVariantLayout->addWidget(m_pSplitBox);
+ pVariantLayout->addStretch();
+ retranslateUi();
+}
+
+void UIDiskVariantWidget::retranslateUi()
+{
+ if (m_pFixedCheckBox)
+ {
+ m_pFixedCheckBox->setText(tr("Pre-allocate &Full Size"));
+ m_pFixedCheckBox->setToolTip(tr("When checked, the virtual disk image is allocated with its full size during VM creation time"));
+ }
+ if (m_pSplitBox)
+ {
+ m_pSplitBox->setText(tr("&Split into 2GB parts"));
+ m_pSplitBox->setToolTip(tr("When checked, the virtual hard disk file is split into 2GB parts."));
+ }
+}
+
+qulonglong UIDiskVariantWidget::mediumVariant() const
+{
+ /* Initial value: */
+ qulonglong uMediumVariant = (qulonglong)KMediumVariant_Max;
+
+ /* Exclusive options: */
+ if (m_pFixedCheckBox && m_pFixedCheckBox->isChecked())
+ uMediumVariant = (qulonglong)KMediumVariant_Fixed;
+ else
+ uMediumVariant = (qulonglong)KMediumVariant_Standard;
+
+ /* Additional options: */
+ if (m_pSplitBox && m_pSplitBox->isChecked())
+ uMediumVariant |= (qulonglong)KMediumVariant_VmdkSplit2G;
+
+ /* Return options: */
+ return uMediumVariant;
+}
+
+void UIDiskVariantWidget::setMediumVariant(qulonglong uMediumVariant)
+{
+ /* Exclusive options: */
+ if (uMediumVariant & (qulonglong)KMediumVariant_Fixed)
+ {
+ m_pFixedCheckBox->click();
+ m_pFixedCheckBox->setFocus();
+ }
+
+ /* Additional options: */
+ m_pSplitBox->setChecked(uMediumVariant & (qulonglong)KMediumVariant_VmdkSplit2G);
+}
+
+void UIDiskVariantWidget::updateMediumVariantWidgetsAfterFormatChange(const CMediumFormat &mediumFormat)
+{
+ AssertReturnVoid(m_pFixedCheckBox && m_pSplitBox);
+ ULONG uCapabilities = 0;
+ QVector<KMediumFormatCapabilities> capabilities;
+ capabilities = mediumFormat.GetCapabilities();
+ for (int i = 0; i < capabilities.size(); i++)
+ uCapabilities |= capabilities[i];
+
+ m_fIsCreateDynamicPossible = uCapabilities & KMediumFormatCapabilities_CreateDynamic;
+ m_fIsCreateFixedPossible = uCapabilities & KMediumFormatCapabilities_CreateFixed;
+ m_fIsCreateSplitPossible = uCapabilities & KMediumFormatCapabilities_CreateSplit2G;
+ m_pFixedCheckBox->setEnabled(true);
+ if (!m_fIsCreateDynamicPossible)
+ {
+ m_pFixedCheckBox->setChecked(true);
+ m_pFixedCheckBox->setEnabled(false);
+ }
+ if (!m_fIsCreateFixedPossible)
+ {
+ m_pFixedCheckBox->setChecked(false);
+ m_pFixedCheckBox->setEnabled(false);
+ }
+
+ m_pSplitBox->setEnabled(m_fIsCreateSplitPossible);
+ if (!m_fIsCreateSplitPossible)
+ m_pSplitBox->setChecked(false);
+ emit sigMediumVariantChanged(mediumVariant());
+}
+
+bool UIDiskVariantWidget::isComplete() const
+{
+ /* Make sure medium variant is correct: */
+ return mediumVariant() != (qulonglong)KMediumVariant_Max;
+}
+
+bool UIDiskVariantWidget::isCreateDynamicPossible() const
+{
+ return m_fIsCreateDynamicPossible;
+}
+
+bool UIDiskVariantWidget::isCreateFixedPossible() const
+{
+ return m_fIsCreateFixedPossible;
+}
+
+bool UIDiskVariantWidget::isCreateSplitPossible() const
+{
+ return m_fIsCreateSplitPossible;
+}
+
+void UIDiskVariantWidget::sltVariantChanged()
+{
+ emit sigMediumVariantChanged(mediumVariant());
+}
+
+
+/*********************************************************************************************************************************
+* UIMediumSizeAndPathGroupBox implementation. *
+*********************************************************************************************************************************/
+
+UIMediumSizeAndPathGroupBox::UIMediumSizeAndPathGroupBox(bool fExpertMode, QWidget *pParent, qulonglong uMinimumMediumSize)
+ : QIWithRetranslateUI<QGroupBox>(pParent)
+ , m_pLocationEditor(0)
+ , m_pLocationOpenButton(0)
+ , m_pMediumSizeEditor(0)
+ , m_pLocationLabel(0)
+ , m_pSizeLabel(0)
+ , m_fExpertMode(fExpertMode)
+{
+ prepare(uMinimumMediumSize);
+}
+
+bool UIMediumSizeAndPathGroupBox::isComplete() const
+{
+ if (QFileInfo(mediumFilePath()).exists())
+ {
+ m_pLocationEditor->mark(true, tr("Disk file name is not unique"));
+ return false;
+ }
+ m_pLocationEditor->mark(false);
+ return true;
+}
+
+void UIMediumSizeAndPathGroupBox::prepare(qulonglong uMinimumMediumSize)
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ /* Location widgets: */
+ if (!m_fExpertMode)
+ m_pLocationLabel = new QIRichTextLabel;
+ QHBoxLayout *pLocationLayout = new QHBoxLayout;
+ m_pLocationEditor = new QILineEdit;
+ m_pLocationOpenButton = new QIToolButton;
+ if (m_pLocationOpenButton)
+ {
+ m_pLocationOpenButton->setAutoRaise(true);
+ m_pLocationOpenButton->setIcon(UIIconPool::iconSet(":/select_file_16px.png", "select_file_disabled_16px.png"));
+ }
+ if (m_pLocationEditor)
+ m_pLocationEditor->setToolTip(tr("Holds the location of the virtual disk file."));
+ if (m_pLocationOpenButton)
+ m_pLocationEditor->setToolTip(tr("Opens file selection dialog so that a location for the disk file can be selected."));
+ pLocationLayout->addWidget(m_pLocationEditor);
+ pLocationLayout->addWidget(m_pLocationOpenButton);
+
+ /* Size widgets: */
+ if (!m_fExpertMode)
+ m_pSizeLabel = new QIRichTextLabel;
+ m_pMediumSizeEditor = new UIMediumSizeEditor(0 /* parent */, uMinimumMediumSize);
+
+ /* Add widgets to main layout: */
+ if (m_pLocationLabel)
+ pMainLayout->addWidget(m_pLocationLabel);
+ pMainLayout->addLayout(pLocationLayout);
+
+ if (m_pSizeLabel)
+ pMainLayout->addWidget(m_pSizeLabel);
+ pMainLayout->addWidget(m_pMediumSizeEditor);
+
+ connect(m_pMediumSizeEditor, &UIMediumSizeEditor::sigSizeChanged,
+ this, &UIMediumSizeAndPathGroupBox::sigMediumSizeChanged);
+
+ connect(m_pLocationEditor, &QILineEdit::textChanged,
+ this, &UIMediumSizeAndPathGroupBox::sigMediumPathChanged);
+
+ connect(m_pLocationOpenButton, &QIToolButton::clicked,
+ this, &UIMediumSizeAndPathGroupBox::sigMediumLocationButtonClicked);
+
+ retranslateUi();
+}
+void UIMediumSizeAndPathGroupBox::retranslateUi()
+{
+ if (m_fExpertMode)
+ setTitle(tr("Hard Disk File Location and Size"));
+ if (m_pLocationOpenButton)
+ m_pLocationOpenButton->setToolTip(tr("Specify a location for new virtual hard disk file..."));
+
+ if (!m_fExpertMode && m_pLocationLabel)
+ m_pLocationLabel->setText(tr("Please type the name of the new virtual hard disk file into the box below or "
+ "click on the folder icon to select a different folder to create the file in."));
+ if (!m_fExpertMode && m_pSizeLabel)
+ m_pSizeLabel->setText(tr("Select the size of the virtual hard disk in megabytes. "
+ "This size is the limit on the amount of file data "
+ "that a virtual machine will be able to store on the hard disk."));
+}
+
+QString UIMediumSizeAndPathGroupBox::mediumName() const
+{
+ if (!m_pLocationEditor)
+ return QString();
+ return QFileInfo(m_pLocationEditor->text()).completeBaseName();
+}
+
+QString UIMediumSizeAndPathGroupBox::mediumFilePath() const
+{
+ if (!m_pLocationEditor)
+ return QString();
+ return m_pLocationEditor->text();
+}
+
+void UIMediumSizeAndPathGroupBox::setMediumFilePath(const QString &strMediumPath)
+{
+ if (!m_pLocationEditor)
+ return;
+ m_pLocationEditor->setText(strMediumPath);
+}
+
+void UIMediumSizeAndPathGroupBox::updateMediumPath(const CMediumFormat &mediumFormat, const QStringList &formatExtensions,
+ KDeviceType enmDeviceType)
+{
+ /* Compose virtual-disk extension: */
+ QString strDefaultExtension = UIWizardDiskEditors::defaultExtension(mediumFormat, enmDeviceType);
+ /* Update m_pLocationEditor's text if necessary: */
+ if (!m_pLocationEditor->text().isEmpty() && !strDefaultExtension.isEmpty())
+ {
+ QFileInfo fileInfo(m_pLocationEditor->text());
+ if (fileInfo.suffix() != strDefaultExtension)
+ {
+ QFileInfo newFileInfo(QDir(fileInfo.absolutePath()),
+ QString("%1.%2").
+ arg(UIWizardDiskEditors::stripFormatExtension(fileInfo.fileName(), formatExtensions)).
+ arg(strDefaultExtension));
+ setMediumFilePath(newFileInfo.absoluteFilePath());
+ }
+ }
+}
+
+QString UIMediumSizeAndPathGroupBox::mediumPath() const
+{
+ if (!m_pLocationEditor)
+ return QString();
+ return QDir::toNativeSeparators(QFileInfo(m_pLocationEditor->text()).absolutePath());
+}
+
+qulonglong UIMediumSizeAndPathGroupBox::mediumSize() const
+{
+ if (m_pMediumSizeEditor)
+ return m_pMediumSizeEditor->mediumSize();
+ return 0;
+}
+
+void UIMediumSizeAndPathGroupBox::setMediumSize(qulonglong uSize)
+{
+ if (m_pMediumSizeEditor)
+ return m_pMediumSizeEditor->setMediumSize(uSize);
+}
+
+/*********************************************************************************************************************************
+* UIDiskFormatBase implementation. *
+*********************************************************************************************************************************/
+
+UIDiskFormatBase::UIDiskFormatBase(KDeviceType enmDeviceType, bool fExpertMode)
+ : m_enmDeviceType(enmDeviceType)
+ , m_fExpertMode(fExpertMode)
+{
+}
+
+UIDiskFormatBase::~UIDiskFormatBase()
+{
+}
+
+const CMediumFormat &UIDiskFormatBase::VDIMediumFormat() const
+{
+ return m_comVDIMediumFormat;
+}
+
+void UIDiskFormatBase::populateFormats(){
+ /* Enumerate medium formats in special order: */
+ CSystemProperties properties = uiCommon().virtualBox().GetSystemProperties();
+ const QVector<CMediumFormat> &formats = properties.GetMediumFormats();
+ QMap<QString, CMediumFormat> vdi, preferred, others;
+ foreach (const CMediumFormat &format, formats)
+ {
+ if (format.GetName() == "VDI")
+ {
+ vdi[format.GetId()] = format;
+ m_comVDIMediumFormat = format;
+ }
+ else
+ {
+ const QVector<KMediumFormatCapabilities> &capabilities = format.GetCapabilities();
+ if (capabilities.contains(KMediumFormatCapabilities_Preferred))
+ preferred[format.GetId()] = format;
+ else
+ others[format.GetId()] = format;
+ }
+ }
+
+ /* Create buttons for VDI, preferred and others: */
+ foreach (const QString &strId, vdi.keys())
+ addFormat(vdi.value(strId), true);
+ foreach (const QString &strId, preferred.keys())
+ addFormat(preferred.value(strId), true);
+
+ if (m_fExpertMode || m_enmDeviceType == KDeviceType_DVD || m_enmDeviceType == KDeviceType_Floppy)
+ {
+ foreach (const QString &strId, others.keys())
+ addFormat(others.value(strId));
+ }
+}
+
+void UIDiskFormatBase::addFormat(CMediumFormat medFormat, bool fPreferred /* = false */)
+{
+ AssertReturnVoid(!medFormat.isNull());
+ /* Check that medium format supports creation: */
+ ULONG uFormatCapabilities = 0;
+ QVector<KMediumFormatCapabilities> capabilities;
+ capabilities = medFormat.GetCapabilities();
+ for (int i = 0; i < capabilities.size(); i++)
+ uFormatCapabilities |= capabilities[i];
+
+ if (!(uFormatCapabilities & KMediumFormatCapabilities_CreateFixed ||
+ uFormatCapabilities & KMediumFormatCapabilities_CreateDynamic))
+ return;
+
+ /* Check that medium format supports creation of virtual hard-disks: */
+ QVector<QString> fileExtensions;
+ QVector<KDeviceType> deviceTypes;
+ medFormat.DescribeFileExtensions(fileExtensions, deviceTypes);
+ if (!deviceTypes.contains(m_enmDeviceType))
+ return;
+ m_formatList << Format(medFormat, UIWizardDiskEditors::defaultExtension(medFormat, m_enmDeviceType), fPreferred);
+}
+
+QStringList UIDiskFormatBase::formatExtensions() const
+{
+ QStringList extensionList;
+ foreach (const Format &format, m_formatList)
+ extensionList << format.m_strExtension;
+ return extensionList;
+}
+
+bool UIDiskFormatBase::isExpertMode() const
+{
+ return m_fExpertMode;
+}
+
+/*********************************************************************************************************************************
+* UIDiskFormatsGroupBox implementation. *
+*********************************************************************************************************************************/
+
+UIDiskFormatsGroupBox::UIDiskFormatsGroupBox(bool fExpertMode, KDeviceType enmDeviceType, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , UIDiskFormatBase(enmDeviceType, fExpertMode)
+ , m_pFormatButtonGroup(0)
+ , m_pMainLayout(0)
+{
+ prepare();
+}
+
+CMediumFormat UIDiskFormatsGroupBox::mediumFormat() const
+{
+ if (!m_pFormatButtonGroup)
+ return CMediumFormat();
+ int iIndex = m_pFormatButtonGroup->checkedId();
+ if (iIndex < 0 || iIndex >= m_formatList.size())
+ return CMediumFormat();
+ return m_formatList[iIndex].m_comFormat;
+}
+
+void UIDiskFormatsGroupBox::setMediumFormat(const CMediumFormat &mediumFormat)
+{
+ int iPosition = -1;
+ for (int i = 0; i < m_formatList.size(); ++i)
+ {
+ if (mediumFormat == m_formatList[i].m_comFormat)
+ iPosition = i;
+ }
+ if (iPosition >= 0)
+ {
+ m_pFormatButtonGroup->button(iPosition)->click();
+ m_pFormatButtonGroup->button(iPosition)->setFocus();
+ }
+}
+
+void UIDiskFormatsGroupBox::prepare()
+{
+ m_pMainLayout = new QVBoxLayout(this);
+ populateFormats();
+ createFormatWidgets();
+ retranslateUi();
+}
+
+void UIDiskFormatsGroupBox::retranslateUi()
+{
+ QList<QAbstractButton*> buttons = m_pFormatButtonGroup ? m_pFormatButtonGroup->buttons() : QList<QAbstractButton*>();
+ for (int i = 0; i < buttons.size(); ++i)
+ {
+ QAbstractButton *pButton = buttons[i];
+ const CMediumFormat &format = m_formatList[m_pFormatButtonGroup->id(pButton)].m_comFormat;
+ if (format.isNull())
+ continue;
+ UIMediumFormat enmFormat = gpConverter->fromInternalString<UIMediumFormat>(format.GetName());
+ pButton->setText(gpConverter->toString(enmFormat));
+ }
+}
+
+void UIDiskFormatsGroupBox::createFormatWidgets()
+{
+ AssertReturnVoid(m_pMainLayout);
+ AssertReturnVoid(!m_formatList.isEmpty());
+ m_pFormatButtonGroup = new QButtonGroup(this);
+ AssertReturnVoid(m_pFormatButtonGroup);
+
+ for (int i = 0; i < m_formatList.size(); ++i)
+ {
+ QRadioButton *pFormatButton = new QRadioButton;
+ if (!pFormatButton)
+ continue;
+
+ /* Make the preferred button font bold: */
+ if (m_formatList[i].m_fPreferred && isExpertMode())
+ {
+ QFont font = pFormatButton->font();
+ font.setBold(true);
+ pFormatButton->setFont(font);
+ }
+ m_pMainLayout->addWidget(pFormatButton);
+ m_pFormatButtonGroup->addButton(pFormatButton, i);
+ }
+
+ setMediumFormat(m_formatList[0].m_comFormat);
+ connect(m_pFormatButtonGroup, static_cast<void(QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked),
+ this, &UIDiskFormatsGroupBox::sigMediumFormatChanged);
+}
+
+
+/*********************************************************************************************************************************
+* UIDiskFormatsGroupBox implementation. *
+*********************************************************************************************************************************/
+
+UIDiskFormatsComboBox::UIDiskFormatsComboBox(bool fExpertMode, KDeviceType enmDeviceType, QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QIComboBox>(pParent)
+ , UIDiskFormatBase(enmDeviceType, fExpertMode)
+{
+ prepare();
+}
+
+void UIDiskFormatsComboBox::prepare()
+{
+ populateFormats();
+ foreach (const Format &format, m_formatList)
+ {
+ addItem(format.m_comFormat.GetName());
+ }
+
+ connect(this, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIDiskFormatsComboBox::sigMediumFormatChanged);
+
+ retranslateUi();
+}
+
+CMediumFormat UIDiskFormatsComboBox::mediumFormat() const
+{
+ int iIndex = currentIndex();
+ if (iIndex < 0 || iIndex >= m_formatList.size())
+ return CMediumFormat();
+ return m_formatList[iIndex].m_comFormat;
+}
+
+void UIDiskFormatsComboBox::setMediumFormat(const CMediumFormat &mediumFormat)
+{
+ int iPosition = -1;
+ for (int i = 0; i < m_formatList.size(); ++i)
+ {
+ if (mediumFormat == m_formatList[i].m_comFormat)
+ iPosition = i;
+ }
+ if (iPosition >= 0)
+ setCurrentIndex(iPosition);
+}
+
+void UIDiskFormatsComboBox::retranslateUi()
+{
+ for (int i = 0; i < count(); ++i)
+ {
+ if (i >= m_formatList.size())
+ break;
+ const CMediumFormat &format = m_formatList[i].m_comFormat;
+ if (format.isNull())
+ continue;
+ UIMediumFormat enmFormat = gpConverter->fromInternalString<UIMediumFormat>(format.GetName());
+ setItemText(i, gpConverter->toString(enmFormat));
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardDiskEditors.h b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardDiskEditors.h
new file mode 100644
index 00000000..344b836c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardDiskEditors.h
@@ -0,0 +1,244 @@
+/* $Id: UIWizardDiskEditors.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardDiskEditors class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_editors_UIWizardDiskEditors_h
+#define FEQT_INCLUDED_SRC_wizards_editors_UIWizardDiskEditors_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QGroupBox>
+#include <QVector>
+
+/* Local includes: */
+#include "QIComboBox.h"
+#include "QIWithRetranslateUI.h"
+
+
+/* Forward declarations: */
+class CMediumFormat;
+class QButtonGroup;
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+class QVBoxLayout;
+class QIRichTextLabel;
+class QILineEdit;
+class QIToolButton;
+class UIFilePathSelector;
+class UIHostnameDomainNameEditor;
+class UIPasswordLineEdit;
+class UIUserNamePasswordEditor;
+class UIMediumSizeEditor;
+
+/* Other VBox includes: */
+#include "COMEnums.h"
+#include "CMediumFormat.h"
+
+/** A set of static utility functions used by several wizard in the context of virtual media. */
+namespace UIWizardDiskEditors
+{
+ /** Makes sure that @strFileName is suffixed witht the @strExtension. */
+ SHARED_LIBRARY_STUFF QString appendExtension(const QString &strName, const QString &strExtension);
+ SHARED_LIBRARY_STUFF QString constructMediumFilePath(const QString &strFileName, const QString &strPath);
+ SHARED_LIBRARY_STUFF bool checkFATSizeLimitation(const qulonglong uVariant, const QString &strMediumPath, const qulonglong uSize);
+ SHARED_LIBRARY_STUFF QString openFileDialogForDiskFile(const QString &strInitialPath, const CMediumFormat &comMediumFormat,
+ KDeviceType enmDeviceType, QWidget *pParent);
+ /** Attempts to find a file extention for the device type @p enmDeviceType within the extensions
+ * returned by CMediumFormat::DescribeFileExtensions(..). */
+ SHARED_LIBRARY_STUFF QString defaultExtension(const CMediumFormat &mediumFormatRef, KDeviceType enmDeviceType);
+ /** Removes the file extension from @strFileName if @p formatExtensions contains it. */
+ SHARED_LIBRARY_STUFF QString stripFormatExtension(const QString &strFileName,
+ const QStringList &formatExtensions);
+};
+
+class SHARED_LIBRARY_STUFF UIDiskVariantWidget : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigMediumVariantChanged(qulonglong uVariant);
+
+public:
+
+ UIDiskVariantWidget(QWidget *pParent = 0);
+ /** Enable/disable medium variant radio button depending on the capabilities of the medium format. */
+ void updateMediumVariantWidgetsAfterFormatChange(const CMediumFormat &mediumFormat);
+ qulonglong mediumVariant() const;
+ void setMediumVariant(qulonglong uMediumVariant);
+ bool isComplete() const;
+
+ bool isCreateDynamicPossible() const;
+ bool isCreateFixedPossible() const;
+ bool isCreateSplitPossible() const;
+
+private slots:
+
+ void sltVariantChanged();
+
+private:
+
+ void prepare();
+ virtual void retranslateUi() /* override final */;
+
+ QCheckBox *m_pFixedCheckBox;
+ QCheckBox *m_pSplitBox;
+ bool m_fIsCreateDynamicPossible;
+ bool m_fIsCreateFixedPossible;
+ bool m_fIsCreateSplitPossible;
+};
+
+
+class SHARED_LIBRARY_STUFF UIMediumSizeAndPathGroupBox : public QIWithRetranslateUI<QGroupBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigMediumSizeChanged(qulonglong uSize);
+ void sigMediumPathChanged(const QString &strPath);
+ void sigMediumLocationButtonClicked();
+
+public:
+
+ UIMediumSizeAndPathGroupBox(bool fExpertMode, QWidget *pParent, qulonglong uMinimumMediumSize);
+ /** Returns name of the medium file without extension and path. */
+ QString mediumName() const;
+ /** Returns the file pat of the medium file including file name and extension. */
+ QString mediumFilePath() const;
+ void setMediumFilePath(const QString &strMediumPath);
+ /** Returns path of the medium file without the file name. */
+ QString mediumPath() const;
+ /** Checks if the file extension is correct. Fixs it if necessary. */
+ void updateMediumPath(const CMediumFormat &mediumFormat, const QStringList &formatExtensions, KDeviceType enmDeviceType);
+ qulonglong mediumSize() const;
+ void setMediumSize(qulonglong uSize);
+
+ bool isComplete() const;
+
+private:
+
+ void prepare(qulonglong uMinimumMediumSize);
+ virtual void retranslateUi() /* override final */;
+
+ QILineEdit *m_pLocationEditor;
+ QIToolButton *m_pLocationOpenButton;
+ UIMediumSizeEditor *m_pMediumSizeEditor;
+ QIRichTextLabel *m_pLocationLabel;
+ QIRichTextLabel *m_pSizeLabel;
+ bool m_fExpertMode;
+};
+
+/** Base class for the widgets used to select virtual medium format. It implements mutual functioanlity
+ * like finding name, extension etc for a CMediumFormat and device type. */
+class SHARED_LIBRARY_STUFF UIDiskFormatBase
+{
+public:
+
+ UIDiskFormatBase(KDeviceType enmDeviceType, bool fExpertMode);
+ virtual ~UIDiskFormatBase();
+ virtual CMediumFormat mediumFormat() const = 0;
+ virtual void setMediumFormat(const CMediumFormat &mediumFormat) = 0;
+
+ const CMediumFormat &VDIMediumFormat() const;
+ QStringList formatExtensions() const;
+
+protected:
+
+ struct Format
+ {
+ CMediumFormat m_comFormat;
+ QString m_strExtension;
+ bool m_fPreferred;
+ Format(const CMediumFormat &comFormat,
+ const QString &strExtension, bool fPreferred)
+ : m_comFormat(comFormat)
+ , m_strExtension(strExtension)
+ , m_fPreferred(fPreferred){}
+ Format(){}
+ };
+
+ void addFormat(CMediumFormat medFormat, bool fPreferred = false);
+ void populateFormats();
+ bool isExpertMode() const;
+ QVector<Format> m_formatList;
+
+private:
+
+ CMediumFormat m_comVDIMediumFormat;
+ KDeviceType m_enmDeviceType;
+ bool m_fExpertMode;
+};
+
+class SHARED_LIBRARY_STUFF UIDiskFormatsGroupBox : public QIWithRetranslateUI<QWidget>, public UIDiskFormatBase
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigMediumFormatChanged();
+
+public:
+
+ UIDiskFormatsGroupBox(bool fExpertMode, KDeviceType enmDeviceType, QWidget *pParent = 0);
+ virtual CMediumFormat mediumFormat() const /* override final */;
+ virtual void setMediumFormat(const CMediumFormat &mediumFormat) /* override final */;
+
+private:
+
+ void prepare();
+ void createFormatWidgets();
+ virtual void retranslateUi() /* override final */;
+
+ QButtonGroup *m_pFormatButtonGroup;
+ QVBoxLayout *m_pMainLayout;
+};
+
+class SHARED_LIBRARY_STUFF UIDiskFormatsComboBox : public QIWithRetranslateUI<QIComboBox>, public UIDiskFormatBase
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigMediumFormatChanged();
+
+public:
+
+ UIDiskFormatsComboBox(bool fExpertMode, KDeviceType enmDeviceType, QWidget *pParent = 0);
+ virtual CMediumFormat mediumFormat() const /* override final */;
+ virtual void setMediumFormat(const CMediumFormat &mediumFormat) /* override final */;
+
+private:
+
+ void prepare();
+ virtual void retranslateUi() /* override final */;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_editors_UIWizardDiskEditors_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardNewVMEditors.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardNewVMEditors.cpp
new file mode 100644
index 00000000..18d00b81
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardNewVMEditors.cpp
@@ -0,0 +1,426 @@
+/* $Id: UIWizardNewVMEditors.cpp $ */
+/** @file
+ * VBox Qt GUI - UIUserNamePasswordEditor class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QLabel>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILineEdit.h"
+#include "UIBaseMemoryEditor.h"
+#include "UICommon.h"
+#include "UIHostnameDomainNameEditor.h"
+#include "UIFilePathSelector.h"
+#include "UIUserNamePasswordEditor.h"
+#include "UIVirtualCPUEditor.h"
+#include "UIWizardNewVM.h"
+#include "UIWizardNewVMEditors.h"
+#include "UIWizardNewVMUnattendedPage.h"
+
+/* Other VBox includes: */
+#include "iprt/assert.h"
+
+
+/*********************************************************************************************************************************
+* UIUserNamePasswordGroupBox implementation. *
+*********************************************************************************************************************************/
+
+UIUserNamePasswordGroupBox::UIUserNamePasswordGroupBox(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QGroupBox>(pParent)
+ , m_pUserNamePasswordEditor(0)
+{
+ prepare();
+}
+
+void UIUserNamePasswordGroupBox::prepare()
+{
+ QVBoxLayout *pUserNameContainerLayout = new QVBoxLayout(this);
+ m_pUserNamePasswordEditor = new UIUserNamePasswordEditor;
+ AssertReturnVoid(m_pUserNamePasswordEditor);
+ m_pUserNamePasswordEditor->setLabelsVisible(true);
+ m_pUserNamePasswordEditor->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ pUserNameContainerLayout->addWidget(m_pUserNamePasswordEditor);
+
+ connect(m_pUserNamePasswordEditor, &UIUserNamePasswordEditor::sigPasswordChanged,
+ this, &UIUserNamePasswordGroupBox::sigPasswordChanged);
+ connect(m_pUserNamePasswordEditor, &UIUserNamePasswordEditor::sigUserNameChanged,
+ this, &UIUserNamePasswordGroupBox::sigUserNameChanged);
+ retranslateUi();
+}
+
+void UIUserNamePasswordGroupBox::retranslateUi()
+{
+ setTitle(UIWizardNewVM::tr("Username and Password"));
+}
+
+QString UIUserNamePasswordGroupBox::userName() const
+{
+ if (m_pUserNamePasswordEditor)
+ return m_pUserNamePasswordEditor->userName();
+ return QString();
+}
+
+void UIUserNamePasswordGroupBox::setUserName(const QString &strUserName)
+{
+ if (m_pUserNamePasswordEditor)
+ m_pUserNamePasswordEditor->setUserName(strUserName);
+}
+
+QString UIUserNamePasswordGroupBox::password() const
+{
+ if (m_pUserNamePasswordEditor)
+ return m_pUserNamePasswordEditor->password();
+ return QString();
+}
+
+void UIUserNamePasswordGroupBox::setPassword(const QString &strPassword)
+{
+ if (m_pUserNamePasswordEditor)
+ m_pUserNamePasswordEditor->setPassword(strPassword);
+}
+
+bool UIUserNamePasswordGroupBox::isComplete()
+{
+ if (m_pUserNamePasswordEditor)
+ return m_pUserNamePasswordEditor->isComplete();
+ return false;
+}
+
+void UIUserNamePasswordGroupBox::setLabelsVisible(bool fVisible)
+{
+ if (m_pUserNamePasswordEditor)
+ m_pUserNamePasswordEditor->setLabelsVisible(fVisible);
+}
+
+
+/*********************************************************************************************************************************
+* UIGAInstallationGroupBox implementation. *
+*********************************************************************************************************************************/
+
+UIGAInstallationGroupBox::UIGAInstallationGroupBox(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QGroupBox>(pParent)
+ , m_pGAISOPathLabel(0)
+ , m_pGAISOFilePathSelector(0)
+
+{
+ prepare();
+}
+
+void UIGAInstallationGroupBox::prepare()
+{
+ setCheckable(true);
+
+ QHBoxLayout *pGAInstallationISOLayout = new QHBoxLayout(this);
+ AssertReturnVoid(pGAInstallationISOLayout);
+ m_pGAISOPathLabel = new QLabel;
+ AssertReturnVoid(m_pGAISOPathLabel);
+ m_pGAISOPathLabel->setAlignment(Qt::AlignRight);
+ m_pGAISOPathLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ pGAInstallationISOLayout->addWidget(m_pGAISOPathLabel);
+
+ m_pGAISOFilePathSelector = new UIFilePathSelector;
+ AssertReturnVoid(m_pGAISOFilePathSelector);
+
+ m_pGAISOFilePathSelector->setResetEnabled(false);
+ m_pGAISOFilePathSelector->setMode(UIFilePathSelector::Mode_File_Open);
+ m_pGAISOFilePathSelector->setFileDialogFilters("ISO Images(*.iso *.ISO)");
+ m_pGAISOFilePathSelector->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ m_pGAISOFilePathSelector->setInitialPath(uiCommon().defaultFolderPathForType(UIMediumDeviceType_DVD));
+ m_pGAISOFilePathSelector->setRecentMediaListType(UIMediumDeviceType_DVD);
+ if (m_pGAISOPathLabel)
+ m_pGAISOPathLabel->setBuddy(m_pGAISOFilePathSelector);
+
+ pGAInstallationISOLayout->addWidget(m_pGAISOFilePathSelector);
+
+ connect(m_pGAISOFilePathSelector, &UIFilePathSelector::pathChanged,
+ this, &UIGAInstallationGroupBox::sigPathChanged);
+ connect(this, &UIGAInstallationGroupBox::toggled,
+ this, &UIGAInstallationGroupBox::sltToggleWidgetsEnabled);
+ retranslateUi();
+}
+
+void UIGAInstallationGroupBox::retranslateUi()
+{
+ if (m_pGAISOFilePathSelector)
+ m_pGAISOFilePathSelector->setToolTip(UIWizardNewVM::tr("Selects an installation medium (ISO file) for the Guest Additions."));
+ if (m_pGAISOPathLabel)
+ m_pGAISOPathLabel->setText(UIWizardNewVM::tr("Guest &Additions ISO:"));
+ setTitle(UIWizardNewVM::tr("Gu&est Additions"));
+ setToolTip(UIWizardNewVM::tr("When checked, the guest additions will be installed after the guest OS install."));
+}
+
+QString UIGAInstallationGroupBox::path() const
+{
+ if (m_pGAISOFilePathSelector)
+ return m_pGAISOFilePathSelector->path();
+ return QString();
+}
+
+void UIGAInstallationGroupBox::setPath(const QString &strPath, bool fRefreshText /* = true */)
+{
+ if (m_pGAISOFilePathSelector)
+ m_pGAISOFilePathSelector->setPath(strPath, fRefreshText);
+}
+
+void UIGAInstallationGroupBox::mark()
+{
+ bool fError = !UIWizardNewVMUnattendedCommon::checkGAISOFile(path());
+ if (m_pGAISOFilePathSelector)
+ m_pGAISOFilePathSelector->mark(fError, UIWizardNewVM::tr("Invalid Guest Additions installation media"));
+}
+
+bool UIGAInstallationGroupBox::isComplete() const
+{
+ if (!isChecked())
+ return true;
+ return UIWizardNewVMUnattendedCommon::checkGAISOFile(path());
+}
+
+void UIGAInstallationGroupBox::sltToggleWidgetsEnabled(bool fEnabled)
+{
+ if (m_pGAISOPathLabel)
+ m_pGAISOPathLabel->setEnabled(fEnabled);
+
+ if (m_pGAISOFilePathSelector)
+ m_pGAISOFilePathSelector->setEnabled(fEnabled);
+}
+
+
+/*********************************************************************************************************************************
+* UIAdditionalUnattendedOptions implementation. *
+*********************************************************************************************************************************/
+
+UIAdditionalUnattendedOptions::UIAdditionalUnattendedOptions(QWidget *pParent /* = 0 */)
+ :QIWithRetranslateUI<QGroupBox>(pParent)
+ , m_pProductKeyLabel(0)
+ , m_pProductKeyLineEdit(0)
+ , m_pHostnameDomainNameEditor(0)
+ , m_pStartHeadlessCheckBox(0)
+{
+ prepare();
+}
+
+void UIAdditionalUnattendedOptions::prepare()
+{
+ m_pMainLayout = new QGridLayout(this);
+ m_pMainLayout->setColumnStretch(0, 0);
+ m_pMainLayout->setColumnStretch(1, 1);
+ m_pProductKeyLabel = new QLabel;
+ if (m_pProductKeyLabel)
+ {
+ m_pProductKeyLabel->setAlignment(Qt::AlignRight);
+ m_pProductKeyLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_pMainLayout->addWidget(m_pProductKeyLabel, 0, 0);
+ }
+ m_pProductKeyLineEdit = new QILineEdit;
+ if (m_pProductKeyLineEdit)
+ {
+ m_pProductKeyLineEdit->setInputMask(">NNNNN-NNNNN-NNNNN-NNNNN-NNNNN;#");
+ if (m_pProductKeyLabel)
+ m_pProductKeyLabel->setBuddy(m_pProductKeyLineEdit);
+ m_pMainLayout->addWidget(m_pProductKeyLineEdit, 0, 1, 1, 2);
+ }
+
+ m_pHostnameDomainNameEditor = new UIHostnameDomainNameEditor;
+ if (m_pHostnameDomainNameEditor)
+ m_pMainLayout->addWidget(m_pHostnameDomainNameEditor, 1, 0, 2, 3);
+
+ m_pStartHeadlessCheckBox = new QCheckBox;
+ if (m_pStartHeadlessCheckBox)
+ m_pMainLayout->addWidget(m_pStartHeadlessCheckBox, 3, 1);
+
+ if (m_pHostnameDomainNameEditor)
+ connect(m_pHostnameDomainNameEditor, &UIHostnameDomainNameEditor::sigHostnameDomainNameChanged,
+ this, &UIAdditionalUnattendedOptions::sigHostnameDomainNameChanged);
+ if (m_pProductKeyLineEdit)
+ connect(m_pProductKeyLineEdit, &QILineEdit::textChanged,
+ this, &UIAdditionalUnattendedOptions::sigProductKeyChanged);
+ if (m_pStartHeadlessCheckBox)
+ connect(m_pStartHeadlessCheckBox, &QCheckBox::toggled,
+ this, &UIAdditionalUnattendedOptions::sigStartHeadlessChanged);
+
+ retranslateUi();
+}
+
+void UIAdditionalUnattendedOptions::retranslateUi()
+{
+ setTitle(UIWizardNewVM::tr("Additional Options"));
+
+ if (m_pProductKeyLabel)
+ m_pProductKeyLabel->setText(UIWizardNewVM::tr("&Product Key:"));
+
+ if (m_pStartHeadlessCheckBox)
+ {
+ m_pStartHeadlessCheckBox->setText(UIWizardNewVM::tr("&Install in Background"));
+ m_pStartHeadlessCheckBox->setToolTip(UIWizardNewVM::tr("When checked, headless boot (with no GUI) will be enabled for "
+ "unattended guest OS installation of newly created virtual machine."));
+ }
+
+ int iMaxWidth = 0;
+ if (m_pProductKeyLabel)
+ iMaxWidth = qMax(m_pProductKeyLabel->minimumSizeHint().width(), iMaxWidth);
+ if (m_pHostnameDomainNameEditor)
+ iMaxWidth = qMax(m_pHostnameDomainNameEditor->firstColumnWidth(), iMaxWidth);
+ if (iMaxWidth > 0)
+ {
+ m_pMainLayout->setColumnMinimumWidth(0, iMaxWidth);
+ m_pHostnameDomainNameEditor->setFirstColumnWidth(iMaxWidth);
+ }
+ if (m_pProductKeyLineEdit)
+ m_pProductKeyLineEdit->setToolTip(UIWizardNewVM::tr("Holds the product key."));
+}
+
+QString UIAdditionalUnattendedOptions::hostname() const
+{
+ if (m_pHostnameDomainNameEditor)
+ return m_pHostnameDomainNameEditor->hostname();
+ return QString();
+}
+
+void UIAdditionalUnattendedOptions::setHostname(const QString &strHostname)
+{
+ if (m_pHostnameDomainNameEditor)
+ return m_pHostnameDomainNameEditor->setHostname(strHostname);
+}
+
+QString UIAdditionalUnattendedOptions::domainName() const
+{
+ if (m_pHostnameDomainNameEditor)
+ return m_pHostnameDomainNameEditor->domainName();
+ return QString();
+}
+
+void UIAdditionalUnattendedOptions::setDomainName(const QString &strDomainName)
+{
+ if (m_pHostnameDomainNameEditor)
+ return m_pHostnameDomainNameEditor->setDomainName(strDomainName);
+}
+
+QString UIAdditionalUnattendedOptions::hostnameDomainName() const
+{
+ if (m_pHostnameDomainNameEditor)
+ return m_pHostnameDomainNameEditor->hostnameDomainName();
+ return QString();
+}
+
+bool UIAdditionalUnattendedOptions::isComplete() const
+{
+ return isHostnameComplete();
+}
+
+bool UIAdditionalUnattendedOptions::isHostnameComplete() const
+{
+ if (m_pHostnameDomainNameEditor)
+ return m_pHostnameDomainNameEditor->isComplete();
+ return false;
+}
+
+
+void UIAdditionalUnattendedOptions::mark()
+{
+ if (m_pHostnameDomainNameEditor)
+ m_pHostnameDomainNameEditor->mark();
+}
+
+void UIAdditionalUnattendedOptions::disableEnableProductKeyWidgets(bool fEnabled)
+{
+ if (m_pProductKeyLabel)
+ m_pProductKeyLabel->setEnabled(fEnabled);
+ if (m_pProductKeyLineEdit)
+ m_pProductKeyLineEdit->setEnabled(fEnabled);
+}
+
+/*********************************************************************************************************************************
+* UINewVMHardwareContainer implementation. *
+*********************************************************************************************************************************/
+
+UINewVMHardwareContainer::UINewVMHardwareContainer(QWidget *pParent /* = 0 */)
+ : QIWithRetranslateUI<QWidget>(pParent)
+ , m_pBaseMemoryEditor(0)
+ , m_pVirtualCPUEditor(0)
+ , m_pEFICheckBox(0)
+{
+ prepare();
+}
+
+void UINewVMHardwareContainer::setMemorySize(int iSize)
+{
+ if (m_pBaseMemoryEditor)
+ m_pBaseMemoryEditor->setValue(iSize);
+}
+
+void UINewVMHardwareContainer::setCPUCount(int iCount)
+{
+ if (m_pVirtualCPUEditor)
+ m_pVirtualCPUEditor->setValue(iCount);
+}
+
+void UINewVMHardwareContainer::setEFIEnabled(bool fEnabled)
+{
+ if (m_pEFICheckBox)
+ m_pEFICheckBox->setChecked(fEnabled);
+}
+
+void UINewVMHardwareContainer::prepare()
+{
+ QGridLayout *pHardwareLayout = new QGridLayout(this);
+ pHardwareLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_pBaseMemoryEditor = new UIBaseMemoryEditor;
+ m_pVirtualCPUEditor = new UIVirtualCPUEditor;
+ m_pEFICheckBox = new QCheckBox;
+ pHardwareLayout->addWidget(m_pBaseMemoryEditor, 0, 0, 1, 4);
+ pHardwareLayout->addWidget(m_pVirtualCPUEditor, 1, 0, 1, 4);
+ pHardwareLayout->addWidget(m_pEFICheckBox, 2, 0, 1, 1);
+
+
+ if (m_pBaseMemoryEditor)
+ connect(m_pBaseMemoryEditor, &UIBaseMemoryEditor::sigValueChanged,
+ this, &UINewVMHardwareContainer::sigMemorySizeChanged);
+ if (m_pVirtualCPUEditor)
+ connect(m_pVirtualCPUEditor, &UIVirtualCPUEditor::sigValueChanged,
+ this, &UINewVMHardwareContainer::sigCPUCountChanged);
+ if (m_pEFICheckBox)
+ connect(m_pEFICheckBox, &QCheckBox::toggled,
+ this, &UINewVMHardwareContainer::sigEFIEnabledChanged);
+
+
+ retranslateUi();
+}
+
+void UINewVMHardwareContainer::retranslateUi()
+{
+ if (m_pEFICheckBox)
+ {
+ m_pEFICheckBox->setText(UIWizardNewVM::tr("&Enable EFI (special OSes only)"));
+ m_pEFICheckBox->setToolTip(UIWizardNewVM::tr("When checked, the guest will support the Extended Firmware Interface (EFI), "
+ "which is required to boot certain guest OSes. Non-EFI aware OSes will not "
+ "be able to boot if this option is activated."));
+ }
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardNewVMEditors.h b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardNewVMEditors.h
new file mode 100644
index 00000000..8821910e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/editors/UIWizardNewVMEditors.h
@@ -0,0 +1,191 @@
+/* $Id: UIWizardNewVMEditors.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMEditors class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_editors_UIWizardNewVMEditors_h
+#define FEQT_INCLUDED_SRC_wizards_editors_UIWizardNewVMEditors_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QIcon>
+#include <QGroupBox>
+
+/* Local includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+class QIComboBox;
+class QILineEdit;
+class UIBaseMemoryEditor;
+class UIFilePathSelector;
+class UIHostnameDomainNameEditor;
+class UIPasswordLineEdit;
+class UIUserNamePasswordEditor;
+class UIVirtualCPUEditor;
+
+class UIUserNamePasswordGroupBox : public QIWithRetranslateUI<QGroupBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigUserNameChanged(const QString &strUserName);
+ void sigPasswordChanged(const QString &strPassword);
+
+public:
+
+ UIUserNamePasswordGroupBox(QWidget *pParent = 0);
+
+ /** @name Wrappers for UIUserNamePasswordEditor
+ * @{ */
+ QString userName() const;
+ void setUserName(const QString &strUserName);
+
+ QString password() const;
+ void setPassword(const QString &strPassword);
+ bool isComplete();
+ void setLabelsVisible(bool fVisible);
+ /** @} */
+
+private:
+
+ void prepare();
+ virtual void retranslateUi() /* override final */;
+
+ UIUserNamePasswordEditor *m_pUserNamePasswordEditor;
+};
+
+
+class UIGAInstallationGroupBox : public QIWithRetranslateUI<QGroupBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigPathChanged(const QString &strPath);
+
+public:
+
+ UIGAInstallationGroupBox(QWidget *pParent = 0);
+
+ /** @name Wrappers for UIFilePathSelector
+ * @{ */
+ QString path() const;
+ void setPath(const QString &strPath, bool fRefreshText = true);
+ void mark();
+ bool isComplete() const;
+ /** @} */
+
+private slots:
+
+ void sltToggleWidgetsEnabled(bool fEnabled);
+
+private:
+
+ virtual void retranslateUi() /* override final */;
+ void prepare();
+
+ QLabel *m_pGAISOPathLabel;
+ UIFilePathSelector *m_pGAISOFilePathSelector;
+};
+
+class UIAdditionalUnattendedOptions : public QIWithRetranslateUI<QGroupBox>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigHostnameDomainNameChanged(const QString &strHostnameDomainName, bool fIsComplete);
+ void sigProductKeyChanged(const QString &strHostnameDomainName);
+ void sigStartHeadlessChanged(bool fChecked);
+
+public:
+
+ UIAdditionalUnattendedOptions(QWidget *pParent = 0);
+
+ /** @name Wrappers for UIFilePathSelector
+ * @{ */
+ QString hostname() const;
+ void setHostname(const QString &strHostname);
+ QString domainName() const;
+ void setDomainName(const QString &strDomain);
+ QString hostnameDomainName() const;
+ bool isComplete() const;
+ bool isHostnameComplete() const;
+ void mark();
+ void disableEnableProductKeyWidgets(bool fEnabled);
+ /** @} */
+
+private:
+
+ void prepare();
+ virtual void retranslateUi() /* override final */;
+
+ QLabel *m_pProductKeyLabel;
+ QILineEdit *m_pProductKeyLineEdit;
+ UIHostnameDomainNameEditor *m_pHostnameDomainNameEditor;
+ QCheckBox *m_pStartHeadlessCheckBox;
+ QGridLayout *m_pMainLayout;
+};
+
+
+class UINewVMHardwareContainer : public QIWithRetranslateUI<QWidget>
+{
+ Q_OBJECT;
+
+signals:
+
+ void sigMemorySizeChanged(int iSize);
+ void sigCPUCountChanged(int iCount);
+ void sigEFIEnabledChanged(bool fEnabled);
+
+public:
+
+ UINewVMHardwareContainer(QWidget *pParent = 0);
+
+ /** @name Wrappers for UIFilePathSelector
+ * @{ */
+ void setMemorySize(int iSize);
+ void setCPUCount(int iCount);
+ void setEFIEnabled(bool fEnabled);
+ /** @} */
+
+private:
+
+ void prepare();
+ virtual void retranslateUi() /* override final */;
+
+ UIBaseMemoryEditor *m_pBaseMemoryEditor;
+ UIVirtualCPUEditor *m_pVirtualCPUEditor;
+ QCheckBox *m_pEFICheckBox;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_editors_UIWizardNewVMEditors_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportApp.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportApp.cpp
new file mode 100644
index 00000000..47b87d6d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportApp.cpp
@@ -0,0 +1,346 @@
+/* $Id: UIWizardExportApp.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportApp class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QFileInfo>
+#include <QPushButton>
+#include <QVariant>
+
+/* GUI includes: */
+#include "UIAddDiskEncryptionPasswordDialog.h"
+#include "UIMessageCenter.h"
+#include "UIModalWindowManager.h"
+#include "UINotificationCenter.h"
+#include "UIWizardExportApp.h"
+#include "UIWizardExportAppPageExpert.h"
+#include "UIWizardExportAppPageFormat.h"
+#include "UIWizardExportAppPageSettings.h"
+#include "UIWizardExportAppPageVMs.h"
+
+/* COM includes: */
+#include "CAppliance.h"
+#include "CVFSExplorer.h"
+
+
+UIWizardExportApp::UIWizardExportApp(QWidget *pParent,
+ const QStringList &predefinedMachineNames /* = QStringList() */,
+ bool fFastTraverToExportOCI /* = false */)
+ : UINativeWizard(pParent, WizardType_ExportAppliance, WizardMode_Auto,
+ fFastTraverToExportOCI ? "cloud-export-oci" : "ovf")
+ , m_predefinedMachineNames(predefinedMachineNames)
+ , m_fFastTraverToExportOCI(fFastTraverToExportOCI)
+ , m_fFormatCloudOne(false)
+ , m_enmMACAddressExportPolicy(MACAddressExportPolicy_KeepAllMACs)
+ , m_fManifestSelected(false)
+ , m_fIncludeISOsSelected(false)
+ , m_enmCloudExportMode(CloudExportMode_DoNotAsk)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign watermark: */
+ setPixmapName(":/wizard_ovf_export.png");
+#else
+ /* Assign background image: */
+ setPixmapName(":/wizard_ovf_export_bg.png");
+#endif
+}
+
+void UIWizardExportApp::goForward()
+{
+ wizardButton(WizardButtonType_Next)->click();
+}
+
+void UIWizardExportApp::disableButtons()
+{
+ wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ wizardButton(WizardButtonType_Back)->setEnabled(false);
+ wizardButton(WizardButtonType_Next)->setEnabled(false);
+}
+
+QString UIWizardExportApp::uri(bool fWithFile) const
+{
+ /* For Cloud formats: */
+ if (isFormatCloudOne())
+ return QString("%1://").arg(format());
+ else
+ {
+ /* Prepare storage path: */
+ QString strPath = path();
+ /* Append file name if requested: */
+ if (!fWithFile)
+ {
+ QFileInfo fi(strPath);
+ strPath = fi.path();
+ }
+
+ /* Just path by default: */
+ return strPath;
+ }
+}
+
+bool UIWizardExportApp::exportAppliance()
+{
+ /* Check whether there was cloud target selected: */
+ if (isFormatCloudOne())
+ {
+ /* Get appliance: */
+ CAppliance comAppliance = cloudAppliance();
+ AssertReturn(comAppliance.isNotNull(), false);
+
+ /* Export the VMs, on success we are finished: */
+ return exportVMs(comAppliance);
+ }
+ else
+ {
+ /* Get appliance: */
+ CAppliance comAppliance = localAppliance();
+ AssertReturn(comAppliance.isNotNull(), false);
+
+ /* We need to know every filename which will be created, so that we can ask the user for confirmation of overwriting.
+ * For that we iterating over all virtual systems & fetch all descriptions of the type HardDiskImage. Also add the
+ * manifest file to the check. In the .ova case only the target file itself get checked. */
+
+ /* Compose a list of all required files: */
+ QFileInfo fi(path());
+ QVector<QString> files;
+
+ /* Add arhive itself: */
+ files << fi.fileName();
+
+ /* If archive is of .ovf type: */
+ if (fi.suffix().toLower() == "ovf")
+ {
+ /* Add manifest file if requested: */
+ if (isManifestSelected())
+ files << fi.baseName() + ".mf";
+
+ /* Add all hard disk images: */
+ CVirtualSystemDescriptionVector vsds = comAppliance.GetVirtualSystemDescriptions();
+ for (int i = 0; i < vsds.size(); ++i)
+ {
+ QVector<KVirtualSystemDescriptionType> types;
+ QVector<QString> refs, origValues, configValues, extraConfigValues;
+ vsds[i].GetDescriptionByType(KVirtualSystemDescriptionType_HardDiskImage, types,
+ refs, origValues, configValues, extraConfigValues);
+ foreach (const QString &strValue, origValues)
+ files << QString("%2").arg(strValue);
+ }
+ }
+
+ /* Initialize VFS explorer: */
+ CVFSExplorer comExplorer = comAppliance.CreateVFSExplorer(uri(false /* fWithFile */));
+ if (!comAppliance.isOk())
+ {
+ UINotificationMessage::cannotCreateVfsExplorer(comAppliance, notificationCenter());
+ return false;
+ }
+
+ /* Update VFS explorer: */
+ UINotificationProgressVFSExplorerUpdate *pNotification =
+ new UINotificationProgressVFSExplorerUpdate(comExplorer);
+ if (!handleNotificationProgressNow(pNotification))
+ return false;
+
+ /* Confirm overwriting for existing files: */
+ QVector<QString> exists = comExplorer.Exists(files);
+ if (!msgCenter().confirmOverridingFiles(exists, this))
+ return false;
+
+ /* DELETE all the files which exists after everything is confirmed: */
+ if (!exists.isEmpty())
+ {
+ /* Remove files with VFS explorer: */
+ UINotificationProgressVFSExplorerFilesRemove *pNotification =
+ new UINotificationProgressVFSExplorerFilesRemove(comExplorer, exists);
+ if (!handleNotificationProgressNow(pNotification))
+ return false;
+ }
+
+ /* Export the VMs, on success we are finished: */
+ return exportVMs(comAppliance);
+ }
+}
+
+void UIWizardExportApp::createVsdLaunchForm()
+{
+ /* Acquire prepared client and description: */
+ CCloudClient comClient = cloudClient();
+ CVirtualSystemDescription comVSD = vsd();
+ AssertReturnVoid(comClient.isNotNull() && comVSD.isNotNull());
+
+ /* Create launch VSD form: */
+ UINotificationProgressLaunchVSDFormCreate *pNotification = new UINotificationProgressLaunchVSDFormCreate(comClient,
+ comVSD,
+ format(),
+ profileName());
+ connect(pNotification, &UINotificationProgressLaunchVSDFormCreate::sigVSDFormCreated,
+ this, &UIWizardExportApp::setVsdLaunchForm);
+ handleNotificationProgressNow(pNotification);
+}
+
+bool UIWizardExportApp::createCloudVM()
+{
+ /* Acquire prepared client and description: */
+ CCloudClient comClient = cloudClient();
+ CVirtualSystemDescription comVSD = vsd();
+ AssertReturn(comClient.isNotNull() && comVSD.isNotNull(), false);
+
+ /* Initiate cloud VM creation procedure: */
+ CCloudMachine comMachine;
+
+ /* Create cloud VM: */
+ UINotificationProgressCloudMachineCreate *pNotification = new UINotificationProgressCloudMachineCreate(comClient,
+ comMachine,
+ comVSD,
+ format(),
+ profileName());
+ connect(pNotification, &UINotificationProgressCloudMachineCreate::sigCloudMachineCreated,
+ &uiCommon(), &UICommon::sltHandleCloudMachineAdded);
+ gpNotificationCenter->append(pNotification);
+
+ /* Return result: */
+ return true;
+}
+
+void UIWizardExportApp::populatePages()
+{
+ /* Create corresponding pages: */
+ switch (mode())
+ {
+ case WizardMode_Basic:
+ {
+ addPage(new UIWizardExportAppPageVMs(m_predefinedMachineNames, m_fFastTraverToExportOCI));
+ addPage(new UIWizardExportAppPageFormat(m_fFastTraverToExportOCI));
+ addPage(new UIWizardExportAppPageSettings);
+ break;
+ }
+ case WizardMode_Expert:
+ {
+ addPage(new UIWizardExportAppPageExpert(m_predefinedMachineNames, m_fFastTraverToExportOCI));
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid mode: %d", mode()));
+ break;
+ }
+ }
+}
+
+void UIWizardExportApp::retranslateUi()
+{
+ /* Call to base-class: */
+ UINativeWizard::retranslateUi();
+
+ /* Translate wizard: */
+ setWindowTitle(tr("Export Virtual Appliance"));
+ /// @todo implement this?
+ //setButtonText(QWizard::FinishButton, tr("Export"));
+}
+
+bool UIWizardExportApp::exportVMs(CAppliance &comAppliance)
+{
+ /* Get the map of the password IDs: */
+ EncryptedMediumMap encryptedMedia;
+ foreach (const QString &strPasswordId, comAppliance.GetPasswordIds())
+ foreach (const QUuid &uMediumId, comAppliance.GetMediumIdsForPasswordId(strPasswordId))
+ encryptedMedia.insert(strPasswordId, uMediumId);
+
+ /* Ask for the disk encryption passwords if necessary: */
+ if (!encryptedMedia.isEmpty())
+ {
+ /* Modal dialog can be destroyed in own event-loop as a part of application
+ * termination procedure. We have to make sure that the dialog pointer is
+ * always up to date. So we are wrapping created dialog with QPointer. */
+ QPointer<UIAddDiskEncryptionPasswordDialog> pDlg =
+ new UIAddDiskEncryptionPasswordDialog(this,
+ window()->windowTitle(),
+ encryptedMedia);
+
+ /* Execute the dialog: */
+ if (pDlg->exec() != QDialog::Accepted)
+ {
+ /* Delete the dialog: */
+ delete pDlg;
+ return false;
+ }
+
+ /* Acquire the passwords provided: */
+ const EncryptionPasswordMap encryptionPasswords = pDlg->encryptionPasswords();
+
+ /* Delete the dialog: */
+ delete pDlg;
+
+ /* Provide appliance with passwords if possible: */
+ comAppliance.AddPasswords(encryptionPasswords.keys().toVector(),
+ encryptionPasswords.values().toVector());
+ if (!comAppliance.isOk())
+ {
+ UINotificationMessage::cannotAddDiskEncryptionPassword(comAppliance, notificationCenter());
+ return false;
+ }
+ }
+
+ /* Prepare export options: */
+ QVector<KExportOptions> options;
+ switch (macAddressExportPolicy())
+ {
+ case MACAddressExportPolicy_StripAllNonNATMACs: options.append(KExportOptions_StripAllNonNATMACs); break;
+ case MACAddressExportPolicy_StripAllMACs: options.append(KExportOptions_StripAllMACs); break;
+ default: break;
+ }
+ if (isManifestSelected())
+ options.append(KExportOptions_CreateManifest);
+ if (isIncludeISOsSelected())
+ options.append(KExportOptions_ExportDVDImages);
+
+ /* Is this VM being exported to cloud? */
+ if (isFormatCloudOne())
+ {
+ /* Export appliance: */
+ UINotificationProgressApplianceWrite *pNotification = new UINotificationProgressApplianceWrite(comAppliance,
+ format(),
+ options,
+ uri());
+ if (cloudExportMode() == CloudExportMode_DoNotAsk)
+ gpNotificationCenter->append(pNotification);
+ else
+ handleNotificationProgressNow(pNotification);
+ }
+ /* Is this VM being exported locally? */
+ else
+ {
+ /* Export appliance: */
+ UINotificationProgressApplianceWrite *pNotification = new UINotificationProgressApplianceWrite(comAppliance,
+ format(),
+ options,
+ uri());
+ gpNotificationCenter->append(pNotification);
+ }
+
+ /* Success finally: */
+ return true;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportApp.h b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportApp.h
new file mode 100644
index 00000000..1b543612
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportApp.h
@@ -0,0 +1,287 @@
+/* $Id: UIWizardExportApp.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportApp class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportApp_h
+#define FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportApp_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CAppliance.h"
+#include "CCloudClient.h"
+#include "CVirtualSystemDescription.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/** MAC address export policies. */
+enum MACAddressExportPolicy
+{
+ MACAddressExportPolicy_KeepAllMACs,
+ MACAddressExportPolicy_StripAllNonNATMACs,
+ MACAddressExportPolicy_StripAllMACs,
+ MACAddressExportPolicy_MAX
+};
+Q_DECLARE_METATYPE(MACAddressExportPolicy);
+
+/** Cloud export option modes. */
+enum CloudExportMode
+{
+ CloudExportMode_Invalid,
+ CloudExportMode_AskThenExport,
+ CloudExportMode_ExportThenAsk,
+ CloudExportMode_DoNotAsk
+};
+Q_DECLARE_METATYPE(CloudExportMode);
+
+/** Export Appliance wizard. */
+class UIWizardExportApp : public UINativeWizard
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Export Appliance wizard passing @a pParent to the base-class.
+ * @param predefinedMachineNames Brings the predefined list of machine names.
+ * @param fFastTraverToExportOCI Brings whether wizard should start with OCI target. */
+ UIWizardExportApp(QWidget *pParent,
+ const QStringList &predefinedMachineNames = QStringList(),
+ bool fFastTraverToExportOCI = false);
+
+ /** @name Common fields.
+ * @{ */
+ /** Returns a list of machine names. */
+ QStringList machineNames() const { return m_machineNames; }
+ /** Returns a list of machine IDs. */
+ QList<QUuid> machineIDs() const { return m_machineIDs; }
+
+ /** Returns format. */
+ QString format() const { return m_strFormat; }
+
+ /** Returns whether format is cloud one. */
+ bool isFormatCloudOne() const { return m_fFormatCloudOne; }
+ /** @} */
+
+ /** @name Local export fields.
+ * @{ */
+ /** Returns path. */
+ QString path() const { return m_strPath; }
+
+ /** Returns MAC address export policy. */
+ MACAddressExportPolicy macAddressExportPolicy() const { return m_enmMACAddressExportPolicy; }
+
+ /** Returns whether manifest is selected. */
+ bool isManifestSelected() const { return m_fManifestSelected; }
+
+ /** Returns whether include ISOs is selected. */
+ bool isIncludeISOsSelected() const { return m_fIncludeISOsSelected; }
+
+ /** Returns local appliance object. */
+ CAppliance localAppliance() const { return m_comLocalAppliance; }
+ /** @} */
+
+ /** @name Cloud export fields.
+ * @{ */
+ /** Returns profile name. */
+ QString profileName() const { return m_strProfileName; }
+
+ /** Returns cloud appliance object. */
+ CAppliance cloudAppliance() const { return m_comCloudAppliance; }
+
+ /** Returns cloud client object. */
+ CCloudClient cloudClient() const { return m_comCloudClient; }
+
+ /** Returns virtual system description object. */
+ CVirtualSystemDescription vsd() const { return m_comVsd; }
+
+ /** Returns virtual system description export form object. */
+ CVirtualSystemDescriptionForm vsdExportForm() const { return m_comVsdExportForm; }
+
+ /** Returns virtual system description launch form object. */
+ CVirtualSystemDescriptionForm vsdLaunchForm() const { return m_comVsdLaunchForm; }
+
+ /** Returns cloud export mode. */
+ CloudExportMode cloudExportMode() const { return m_enmCloudExportMode; }
+ /** @} */
+
+ /** @name Auxiliary stuff.
+ * @{ */
+ /** Goes forward. Required for fast travel to next page. */
+ void goForward();
+
+ /** Disables basic/expert and next/back buttons. */
+ void disableButtons();
+
+ /** Composes universal resource identifier.
+ * @param fWithFile Brings whether uri should include file name as well. */
+ QString uri(bool fWithFile = true) const;
+
+ /** Exports Appliance. */
+ bool exportAppliance();
+
+ /** Creates VSD Form. */
+ void createVsdLaunchForm();
+
+ /** Creates New Cloud VM. */
+ bool createCloudVM();
+ /** @} */
+
+public slots:
+
+ /** @name Common fields.
+ * @{ */
+ /** Defines a list of machine @a names. */
+ void setMachineNames(const QStringList &names) { m_machineNames = names; }
+ /** Defines a list of machine @a ids. */
+ void setMachineIDs(const QList<QUuid> &ids) { m_machineIDs = ids; }
+
+ /** Defines @a strFormat. */
+ void setFormat(const QString &strFormat) { m_strFormat = strFormat; }
+
+ /** Defines whether format is @a fCloudOne. */
+ void setFormatCloudOne(bool fCloudOne) { m_fFormatCloudOne = fCloudOne; }
+ /** @} */
+
+ /** @name Local export fields.
+ * @{ */
+ /** Defines @a strPath. */
+ void setPath(const QString &strPath) { m_strPath = strPath; }
+
+ /** Defines MAC address export @a enmPolicy. */
+ void setMACAddressExportPolicy(MACAddressExportPolicy enmPolicy) { m_enmMACAddressExportPolicy = enmPolicy; }
+
+ /** Defines whether manifest is @a fSelected. */
+ void setManifestSelected(bool fSelected) { m_fManifestSelected = fSelected; }
+
+ /** Defines whether include ISOs is @a fSelected. */
+ void setIncludeISOsSelected(bool fSelected) { m_fIncludeISOsSelected = fSelected; }
+
+ /** Defines local @a comAppliance object. */
+ void setLocalAppliance(const CAppliance &comAppliance) { m_comLocalAppliance = comAppliance; }
+ /** @} */
+
+ /** @name Cloud export fields.
+ * @{ */
+ /** Defines profile @a strName. */
+ void setProfileName(const QString &strName) { m_strProfileName = strName; }
+
+ /** Defines cloud @a comAppliance object. */
+ void setCloudAppliance(const CAppliance &comAppliance) { m_comCloudAppliance = comAppliance; }
+
+ /** Defines cloud @a comClient object. */
+ void setCloudClient(const CCloudClient &comClient) { m_comCloudClient = comClient; }
+
+ /** Defines virtual system @a comDescription object. */
+ void setVsd(const CVirtualSystemDescription &comDescription) { m_comVsd = comDescription; }
+
+ /** Defines virtual system description export @a comForm object. */
+ void setVsdExportForm(const CVirtualSystemDescriptionForm &comForm) { m_comVsdExportForm = comForm; }
+
+ /** Defines virtual system description launch @a comForm object. */
+ void setVsdLaunchForm(const CVirtualSystemDescriptionForm &comForm) { m_comVsdLaunchForm = comForm; }
+
+ /** Defines cloud export @a enmMode. */
+ void setCloudExportMode(const CloudExportMode &enmMode) { m_enmCloudExportMode = enmMode; }
+ /** @} */
+
+protected:
+
+ /** @name Virtual stuff.
+ * @{ */
+ /** Populates pages. */
+ virtual void populatePages() /* override final */;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+ /** @} */
+
+private:
+
+ /** @name Auxiliary stuff.
+ * @{ */
+ /** Exports VMs enumerated in @a comAppliance. */
+ bool exportVMs(CAppliance &comAppliance);
+ /** @} */
+
+ /** @name Arguments.
+ * @{ */
+ /** Holds the predefined list of machine names. */
+ QStringList m_predefinedMachineNames;
+ /** Holds whether we should fast travel to page 2. */
+ bool m_fFastTraverToExportOCI;
+ /** @} */
+
+ /** @name Common fields.
+ * @{ */
+ /** Holds the list of machine names. */
+ QStringList m_machineNames;
+ /** Holds the list of machine IDs. */
+ QList<QUuid> m_machineIDs;
+
+ /** Holds the format. */
+ QString m_strFormat;
+ /** Holds whether format is cloud one. */
+ bool m_fFormatCloudOne;
+ /** @} */
+
+ /** @name Local export fields.
+ * @{ */
+ /** Holds the path. */
+ QString m_strPath;
+ /** Holds the MAC address export policy. */
+ MACAddressExportPolicy m_enmMACAddressExportPolicy;
+ /** Holds whether manifest is selected. */
+ bool m_fManifestSelected;
+ /** Holds whether ISOs are included. */
+ bool m_fIncludeISOsSelected;
+ /** Holds local appliance object. */
+ CAppliance m_comLocalAppliance;
+ /** @} */
+
+ /** @name Cloud export fields.
+ * @{ */
+ /** Holds profile name. */
+ QString m_strProfileName;
+ /** Holds cloud appliance object. */
+ CAppliance m_comCloudAppliance;
+ /** Returns cloud client object. */
+ CCloudClient m_comCloudClient;
+ /** Returns virtual system description object. */
+ CVirtualSystemDescription m_comVsd;
+ /** Returns virtual system description export form object. */
+ CVirtualSystemDescriptionForm m_comVsdExportForm;
+ /** Returns virtual system description launch form object. */
+ CVirtualSystemDescriptionForm m_comVsdLaunchForm;
+ /** Returns cloud export mode. */
+ CloudExportMode m_enmCloudExportMode;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportApp_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageExpert.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageExpert.cpp
new file mode 100644
index 00000000..3e1bda86
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageExpert.cpp
@@ -0,0 +1,841 @@
+/* $Id: UIWizardExportAppPageExpert.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportAppPageExpert class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QListWidget>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QStackedWidget>
+#include <QTableWidget>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIToolButton.h"
+#include "UICommon.h"
+#include "UIApplianceExportEditorWidget.h"
+#include "UIConverter.h"
+#include "UIEmptyFilePathSelector.h"
+#include "UIFormEditorWidget.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+#include "UIToolBox.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualBoxManager.h"
+#include "UIWizardExportApp.h"
+#include "UIWizardExportAppPageExpert.h"
+#include "UIWizardExportAppPageFormat.h"
+#include "UIWizardExportAppPageSettings.h"
+#include "UIWizardExportAppPageVMs.h"
+
+/* Namespaces: */
+using namespace UIWizardExportAppFormat;
+using namespace UIWizardExportAppSettings;
+using namespace UIWizardExportAppVMs;
+
+
+/*********************************************************************************************************************************
+* Class UIWizardExportAppPageExpert implementation. *
+*********************************************************************************************************************************/
+
+UIWizardExportAppPageExpert::UIWizardExportAppPageExpert(const QStringList &selectedVMNames, bool fExportToOCIByDefault)
+ : m_selectedVMNames(selectedVMNames)
+ , m_fExportToOCIByDefault(fExportToOCIByDefault)
+ , m_pToolBox(0)
+ , m_pVMSelector(0)
+ , m_pFormatLayout(0)
+ , m_pFormatComboBoxLabel(0)
+ , m_pFormatComboBox(0)
+ , m_pSettingsWidget1(0)
+ , m_pSettingsLayout1(0)
+ , m_pFileSelectorLabel(0)
+ , m_pFileSelector(0)
+ , m_pMACComboBoxLabel(0)
+ , m_pMACComboBox(0)
+ , m_pAdditionalLabel(0)
+ , m_pManifestCheckbox(0)
+ , m_pIncludeISOsCheckbox(0)
+ , m_pSettingsLayout2(0)
+ , m_pProfileLabel(0)
+ , m_pProfileComboBox(0)
+ , m_pProfileToolButton(0)
+ , m_pExportModeLabel(0)
+ , m_pExportModeButtonGroup(0)
+ , m_pSettingsWidget2(0)
+ , m_pApplianceWidget(0)
+ , m_pFormEditor(0)
+ , m_fLaunching(false)
+{
+ /* Create widgets: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Create tool-box: */
+ m_pToolBox = new UIToolBox(this);
+ if (m_pToolBox)
+ {
+ /* Create VM selector: */
+ m_pVMSelector = new QListWidget(m_pToolBox);
+ if (m_pVMSelector)
+ {
+ m_pVMSelector->setAlternatingRowColors(true);
+ m_pVMSelector->setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(0, m_pVMSelector, QString());
+ }
+
+ /* Create settings widget container: */
+ QWidget *pWidgetSettings = new QWidget(m_pToolBox);
+ if (pWidgetSettings)
+ {
+ /* Create settings widget container layout: */
+ QVBoxLayout *pSettingsCntLayout = new QVBoxLayout(pWidgetSettings);
+ if (pSettingsCntLayout)
+ {
+ pSettingsCntLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ pSettingsCntLayout->setSpacing(5);
+#endif
+
+ /* Create format layout: */
+ m_pFormatLayout = new QGridLayout;
+ if (m_pFormatLayout)
+ {
+ m_pFormatLayout->setContentsMargins(0, 0, 0, 0);
+#ifdef VBOX_WS_MAC
+ m_pFormatLayout->setSpacing(10);
+#endif
+ m_pFormatLayout->setColumnStretch(0, 0);
+ m_pFormatLayout->setColumnStretch(1, 1);
+
+ /* Create format combo-box label: */
+ m_pFormatComboBoxLabel = new QLabel(pWidgetSettings);
+ if (m_pFormatComboBoxLabel)
+ {
+ m_pFormatComboBoxLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pFormatLayout->addWidget(m_pFormatComboBoxLabel, 0, 0);
+ }
+ /* Create format combo-box: */
+ m_pFormatComboBox = new QIComboBox(pWidgetSettings);
+ if (m_pFormatComboBox)
+ {
+ m_pFormatComboBoxLabel->setBuddy(m_pFormatComboBox);
+ m_pFormatLayout->addWidget(m_pFormatComboBox, 0, 1);
+ }
+
+ /* Add into layout: */
+ pSettingsCntLayout->addLayout(m_pFormatLayout);
+ }
+
+ /* Create 1st settings widget: */
+ m_pSettingsWidget1 = new QStackedWidget(pWidgetSettings);
+ if (m_pSettingsWidget1)
+ {
+ /* Create settings pane 1: */
+ QWidget *pSettingsPane1 = new QWidget(m_pSettingsWidget1);
+ if (pSettingsPane1)
+ {
+ /* Create settings layout 1: */
+ m_pSettingsLayout1 = new QGridLayout(pSettingsPane1);
+ if (m_pSettingsLayout1)
+ {
+#ifdef VBOX_WS_MAC
+ m_pSettingsLayout1->setSpacing(10);
+#endif
+ m_pSettingsLayout1->setContentsMargins(0, 0, 0, 0);
+ m_pSettingsLayout1->setColumnStretch(0, 0);
+ m_pSettingsLayout1->setColumnStretch(1, 1);
+
+ /* Create file selector label: */
+ m_pFileSelectorLabel = new QLabel(pSettingsPane1);
+ if (m_pFileSelectorLabel)
+ {
+ m_pFileSelectorLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pSettingsLayout1->addWidget(m_pFileSelectorLabel, 0, 0);
+ }
+ /* Create file selector: */
+ m_pFileSelector = new UIEmptyFilePathSelector(pSettingsPane1);
+ if (m_pFileSelector)
+ {
+ m_pFileSelectorLabel->setBuddy(m_pFileSelector);
+ m_pFileSelector->setMode(UIEmptyFilePathSelector::Mode_File_Save);
+ m_pFileSelector->setEditable(true);
+ m_pFileSelector->setButtonPosition(UIEmptyFilePathSelector::RightPosition);
+ m_pFileSelector->setDefaultSaveExt("ova");
+ m_pSettingsLayout1->addWidget(m_pFileSelector, 0, 1, 1, 2);
+ }
+
+ /* Create MAC policy combo-box label: */
+ m_pMACComboBoxLabel = new QLabel(pSettingsPane1);
+ if (m_pMACComboBoxLabel)
+ {
+ m_pMACComboBoxLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pSettingsLayout1->addWidget(m_pMACComboBoxLabel, 1, 0);
+ }
+ /* Create MAC policy combo-box: */
+ m_pMACComboBox = new QIComboBox(pSettingsPane1);
+ if (m_pMACComboBox)
+ {
+ m_pMACComboBoxLabel->setBuddy(m_pMACComboBox);
+ m_pSettingsLayout1->addWidget(m_pMACComboBox, 1, 1, 1, 2);
+ }
+
+ /* Create advanced label: */
+ m_pAdditionalLabel = new QLabel(pSettingsPane1);
+ if (m_pAdditionalLabel)
+ {
+ m_pAdditionalLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pSettingsLayout1->addWidget(m_pAdditionalLabel, 2, 0);
+ }
+ /* Create manifest check-box editor: */
+ m_pManifestCheckbox = new QCheckBox(pSettingsPane1);
+ if (m_pManifestCheckbox)
+ m_pSettingsLayout1->addWidget(m_pManifestCheckbox, 2, 1);
+ /* Create include ISOs check-box: */
+ m_pIncludeISOsCheckbox = new QCheckBox(pSettingsPane1);
+ if (m_pIncludeISOsCheckbox)
+ m_pSettingsLayout1->addWidget(m_pIncludeISOsCheckbox, 3, 1);
+
+ /* Create placeholder: */
+ QWidget *pPlaceholder = new QWidget(pSettingsPane1);
+ if (pPlaceholder)
+ m_pSettingsLayout1->addWidget(pPlaceholder, 4, 0, 1, 3);
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget1->addWidget(pSettingsPane1);
+ }
+
+ /* Create settings pane 2: */
+ QWidget *pSettingsPane2 = new QWidget(m_pSettingsWidget1);
+ if (pSettingsPane2)
+ {
+ /* Create settings layout 2: */
+ m_pSettingsLayout2 = new QGridLayout(pSettingsPane2);
+ if (m_pSettingsLayout2)
+ {
+#ifdef VBOX_WS_MAC
+ m_pSettingsLayout2->setSpacing(10);
+#endif
+ m_pSettingsLayout2->setContentsMargins(0, 0, 0, 0);
+ m_pSettingsLayout2->setColumnStretch(0, 0);
+ m_pSettingsLayout2->setColumnStretch(1, 1);
+
+ /* Create profile label: */
+ m_pProfileLabel = new QLabel(pSettingsPane2);
+ if (m_pProfileLabel)
+ {
+ m_pProfileLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pSettingsLayout2->addWidget(m_pProfileLabel, 0, 0);
+ }
+ /* Create sub-layout: */
+ QHBoxLayout *pSubLayout = new QHBoxLayout;
+ if (pSubLayout)
+ {
+ pSubLayout->setContentsMargins(0, 0, 0, 0);
+ pSubLayout->setSpacing(1);
+
+ /* Create profile combo-box: */
+ m_pProfileComboBox = new QIComboBox(pSettingsPane2);
+ if (m_pProfileComboBox)
+ {
+ m_pProfileLabel->setBuddy(m_pProfileComboBox);
+ pSubLayout->addWidget(m_pProfileComboBox);
+ }
+ /* Create profile tool-button: */
+ m_pProfileToolButton = new QIToolButton(pSettingsPane2);
+ if (m_pProfileToolButton)
+ {
+ m_pProfileToolButton->setIcon(UIIconPool::iconSet(":/cloud_profile_manager_16px.png",
+ ":/cloud_profile_manager_disabled_16px.png"));
+ pSubLayout->addWidget(m_pProfileToolButton);
+ }
+
+ /* Add into layout: */
+ m_pSettingsLayout2->addLayout(pSubLayout, 0, 1);
+ }
+
+ /* Create profile label: */
+ m_pExportModeLabel = new QLabel(pSettingsPane2);
+ if (m_pExportModeLabel)
+ {
+ m_pExportModeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_pSettingsLayout2->addWidget(m_pExportModeLabel, 1, 0);
+ }
+ /* Create button-group: */
+ m_pExportModeButtonGroup = new QButtonGroup(pSettingsPane2);
+ if (m_pExportModeButtonGroup)
+ {
+ /* Create Do Not Ask button: */
+ m_exportModeButtons[CloudExportMode_DoNotAsk] = new QRadioButton(pSettingsPane2);
+ if (m_exportModeButtons.value(CloudExportMode_DoNotAsk))
+ {
+ m_pExportModeButtonGroup->addButton(m_exportModeButtons.value(CloudExportMode_DoNotAsk));
+ m_pSettingsLayout2->addWidget(m_exportModeButtons.value(CloudExportMode_DoNotAsk), 1, 1);
+ }
+ /* Create Ask Then Export button: */
+ m_exportModeButtons[CloudExportMode_AskThenExport] = new QRadioButton(pSettingsPane2);
+ if (m_exportModeButtons.value(CloudExportMode_AskThenExport))
+ {
+ m_pExportModeButtonGroup->addButton(m_exportModeButtons.value(CloudExportMode_AskThenExport));
+ m_pSettingsLayout2->addWidget(m_exportModeButtons.value(CloudExportMode_AskThenExport), 2, 1);
+ }
+ /* Create Export Then Ask button: */
+ m_exportModeButtons[CloudExportMode_ExportThenAsk] = new QRadioButton(pSettingsPane2);
+ if (m_exportModeButtons.value(CloudExportMode_ExportThenAsk))
+ {
+ m_pExportModeButtonGroup->addButton(m_exportModeButtons.value(CloudExportMode_ExportThenAsk));
+ m_pSettingsLayout2->addWidget(m_exportModeButtons.value(CloudExportMode_ExportThenAsk), 3, 1);
+ }
+ }
+
+ /* Create placeholder: */
+ QWidget *pPlaceholder = new QWidget(pSettingsPane2);
+ if (pPlaceholder)
+ m_pSettingsLayout2->addWidget(pPlaceholder, 4, 0, 1, 3);
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget1->addWidget(pSettingsPane2);
+ }
+
+ /* Add into layout: */
+ pSettingsCntLayout->addWidget(m_pSettingsWidget1);
+ }
+ }
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(1, pWidgetSettings, QString());
+ }
+
+ /* Create 2nd settings widget: */
+ m_pSettingsWidget2 = new QStackedWidget(m_pToolBox);
+ if (m_pSettingsWidget2)
+ {
+ /* Create appliance widget container: */
+ QWidget *pApplianceWidgetCnt = new QWidget(this);
+ if (pApplianceWidgetCnt)
+ {
+ /* Create appliance widget layout: */
+ QVBoxLayout *pApplianceWidgetLayout = new QVBoxLayout(pApplianceWidgetCnt);
+ if (pApplianceWidgetLayout)
+ {
+ pApplianceWidgetLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create appliance widget: */
+ m_pApplianceWidget = new UIApplianceExportEditorWidget(pApplianceWidgetCnt);
+ if (m_pApplianceWidget)
+ {
+ m_pApplianceWidget->setMinimumHeight(250);
+ m_pApplianceWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
+ pApplianceWidgetLayout->addWidget(m_pApplianceWidget);
+ }
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget2->addWidget(pApplianceWidgetCnt);
+ }
+
+ /* Create form editor container: */
+ QWidget *pFormEditorCnt = new QWidget(this);
+ if (pFormEditorCnt)
+ {
+ /* Create form editor layout: */
+ QVBoxLayout *pFormEditorLayout = new QVBoxLayout(pFormEditorCnt);
+ if (pFormEditorLayout)
+ {
+ pFormEditorLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create form editor widget: */
+ m_pFormEditor = new UIFormEditorWidget(pFormEditorCnt);
+ if (m_pFormEditor)
+ pFormEditorLayout->addWidget(m_pFormEditor);
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget2->addWidget(pFormEditorCnt);
+ }
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(2, m_pSettingsWidget2, QString());
+ }
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pToolBox);
+ }
+
+ /* Add stretch: */
+ pMainLayout->addStretch();
+ }
+
+ /* Setup connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UIWizardExportAppPageExpert::sltHandleFormatComboChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UIWizardExportAppPageExpert::sltHandleFormatComboChange);
+ connect(m_pVMSelector, &QListWidget::itemSelectionChanged,
+ this, &UIWizardExportAppPageExpert::sltHandleVMItemSelectionChanged);
+ connect(m_pFileSelector, &UIEmptyFilePathSelector::pathChanged,
+ this, &UIWizardExportAppPageExpert::sltHandleFileSelectorChange);
+ connect(m_pFormatComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardExportAppPageExpert::sltHandleFormatComboChange);
+ connect(m_pMACComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardExportAppPageExpert::sltHandleMACAddressExportPolicyComboChange);
+ connect(m_pManifestCheckbox, &QCheckBox::stateChanged,
+ this, &UIWizardExportAppPageExpert::sltHandleManifestCheckBoxChange);
+ connect(m_pIncludeISOsCheckbox, &QCheckBox::stateChanged,
+ this, &UIWizardExportAppPageExpert::sltHandleIncludeISOsCheckBoxChange);
+ connect(m_pProfileComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardExportAppPageExpert::sltHandleProfileComboChange);
+ connect(m_pExportModeButtonGroup, static_cast<void(QButtonGroup::*)(QAbstractButton*, bool)>(&QButtonGroup::buttonToggled),
+ this, &UIWizardExportAppPageExpert::sltHandleRadioButtonToggled);
+ connect(m_pProfileToolButton, &QIToolButton::clicked,
+ this, &UIWizardExportAppPageExpert::sltHandleProfileButtonClick);
+}
+
+UIWizardExportApp *UIWizardExportAppPageExpert::wizard() const
+{
+ return qobject_cast<UIWizardExportApp*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardExportAppPageExpert::retranslateUi()
+{
+ /* Translate objects: */
+ m_strDefaultApplianceName = UIWizardExportApp::tr("Appliance");
+ refreshFileSelectorName(m_strFileSelectorName, wizard()->machineNames(), m_strDefaultApplianceName, wizard()->isFormatCloudOne());
+ refreshFileSelectorPath(m_pFileSelector, m_strFileSelectorName, m_strFileSelectorExt, wizard()->isFormatCloudOne());
+
+ /* Translate tool-box: */
+ m_pToolBox->setPageTitle(0, UIWizardExportApp::tr("Virtual &machines"));
+ m_pToolBox->setPageTitle(1, UIWizardExportApp::tr("Format &settings"));
+ m_pToolBox->setPageTitle(2, UIWizardExportApp::tr("&Appliance settings"));
+
+ /* Translate File selector: */
+ m_pFileSelectorLabel->setText(UIWizardExportApp::tr("&File:"));
+ m_pFileSelector->setChooseButtonToolTip(UIWizardExportApp::tr("Choose a file to export the virtual appliance to..."));
+ m_pFileSelector->setFileDialogTitle(UIWizardExportApp::tr("Please choose a file to export the virtual appliance to"));
+
+ /* Translate hard-coded values of Format combo-box: */
+ m_pFormatComboBoxLabel->setText(UIWizardExportApp::tr("F&ormat:"));
+ m_pFormatComboBox->setItemText(0, UIWizardExportApp::tr("Open Virtualization Format 0.9"));
+ m_pFormatComboBox->setItemText(1, UIWizardExportApp::tr("Open Virtualization Format 1.0"));
+ m_pFormatComboBox->setItemText(2, UIWizardExportApp::tr("Open Virtualization Format 2.0"));
+ m_pFormatComboBox->setItemData(0, UIWizardExportApp::tr("Write in legacy OVF 0.9 format for compatibility "
+ "with other virtualization products."), Qt::ToolTipRole);
+ m_pFormatComboBox->setItemData(1, UIWizardExportApp::tr("Write in standard OVF 1.0 format."), Qt::ToolTipRole);
+ m_pFormatComboBox->setItemData(2, UIWizardExportApp::tr("Write in new OVF 2.0 format."), Qt::ToolTipRole);
+ /* Translate received values of Format combo-box.
+ * We are enumerating starting from 0 for simplicity: */
+ for (int i = 0; i < m_pFormatComboBox->count(); ++i)
+ if (isFormatCloudOne(m_pFormatComboBox, i))
+ {
+ m_pFormatComboBox->setItemText(i, m_pFormatComboBox->itemData(i, FormatData_Name).toString());
+ m_pFormatComboBox->setItemData(i, UIWizardExportApp::tr("Export to cloud service provider."), Qt::ToolTipRole);
+ }
+
+ /* Translate MAC address policy combo-box: */
+ m_pMACComboBoxLabel->setText(UIWizardExportApp::tr("MAC Address &Policy:"));
+ for (int i = 0; i < m_pMACComboBox->count(); ++i)
+ {
+ const MACAddressExportPolicy enmPolicy = m_pMACComboBox->itemData(i).value<MACAddressExportPolicy>();
+ switch (enmPolicy)
+ {
+ case MACAddressExportPolicy_KeepAllMACs:
+ {
+ m_pMACComboBox->setItemText(i, UIWizardExportApp::tr("Include all network adapter MAC addresses"));
+ m_pMACComboBox->setItemData(i, UIWizardExportApp::tr("Include all network adapter MAC addresses in exported "
+ "appliance archive."), Qt::ToolTipRole);
+ break;
+ }
+ case MACAddressExportPolicy_StripAllNonNATMACs:
+ {
+ m_pMACComboBox->setItemText(i, UIWizardExportApp::tr("Include only NAT network adapter MAC addresses"));
+ m_pMACComboBox->setItemData(i, UIWizardExportApp::tr("Include only NAT network adapter MAC addresses in exported "
+ "appliance archive."), Qt::ToolTipRole);
+ break;
+ }
+ case MACAddressExportPolicy_StripAllMACs:
+ {
+ m_pMACComboBox->setItemText(i, UIWizardExportApp::tr("Strip all network adapter MAC addresses"));
+ m_pMACComboBox->setItemData(i, UIWizardExportApp::tr("Strip all network adapter MAC addresses from exported "
+ "appliance archive."), Qt::ToolTipRole);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Translate addtional stuff: */
+ m_pAdditionalLabel->setText(UIWizardExportApp::tr("Additionally:"));
+ m_pManifestCheckbox->setToolTip(UIWizardExportApp::tr("Create a Manifest file for automatic data integrity checks on import."));
+ m_pManifestCheckbox->setText(UIWizardExportApp::tr("&Write Manifest file"));
+ m_pIncludeISOsCheckbox->setToolTip(UIWizardExportApp::tr("Include ISO image files into exported VM archive."));
+ m_pIncludeISOsCheckbox->setText(UIWizardExportApp::tr("&Include ISO image files"));
+
+ /* Translate profile stuff: */
+ m_pProfileLabel->setText(UIWizardExportApp::tr("&Profile:"));
+ m_pProfileToolButton->setToolTip(UIWizardExportApp::tr("Open Cloud Profile Manager..."));
+
+ /* Translate option label: */
+ m_pExportModeLabel->setText(UIWizardExportApp::tr("Machine Creation:"));
+ m_exportModeButtons.value(CloudExportMode_DoNotAsk)->setText(UIWizardExportApp::tr("Do not ask me about it, leave custom &image for future usage"));
+ m_exportModeButtons.value(CloudExportMode_AskThenExport)->setText(UIWizardExportApp::tr("Ask me about it &before exporting disk as custom image"));
+ m_exportModeButtons.value(CloudExportMode_ExportThenAsk)->setText(UIWizardExportApp::tr("Ask me about it &after exporting disk as custom image"));
+
+ /* Translate file selector's tooltip: */
+ if (m_pFileSelector)
+ m_pFileSelector->setToolTip(UIWizardExportApp::tr("Holds the path of the file selected for export."));
+
+ /* Adjust label widths: */
+ QList<QWidget*> labels;
+ labels << m_pFormatComboBoxLabel;
+ labels << m_pFileSelectorLabel;
+ labels << m_pMACComboBoxLabel;
+ labels << m_pAdditionalLabel;
+ labels << m_pProfileLabel;
+ labels << m_pExportModeLabel;
+ int iMaxWidth = 0;
+ foreach (QWidget *pLabel, labels)
+ iMaxWidth = qMax(iMaxWidth, pLabel->minimumSizeHint().width());
+ m_pFormatLayout->setColumnMinimumWidth(0, iMaxWidth);
+ m_pSettingsLayout1->setColumnMinimumWidth(0, iMaxWidth);
+ m_pSettingsLayout2->setColumnMinimumWidth(0, iMaxWidth);
+
+ /* Update tool-tips: */
+ updateFormatComboToolTip(m_pFormatComboBox);
+ updateMACAddressExportPolicyComboToolTip(m_pMACComboBox);
+}
+
+void UIWizardExportAppPageExpert::initializePage()
+{
+ /* Make sure form-editor knows notification-center: */
+ m_pFormEditor->setNotificationCenter(wizard()->notificationCenter());
+ /* Choose 1st tool to be chosen initially: */
+ m_pToolBox->setCurrentPage(0);
+ /* Populate VM items: */
+ populateVMItems(m_pVMSelector, m_selectedVMNames);
+ /* Populate formats: */
+ populateFormats(m_pFormatComboBox, wizard()->notificationCenter(), m_fExportToOCIByDefault);
+ /* Populate MAC address policies: */
+ populateMACAddressPolicies(m_pMACComboBox);
+ /* Translate page: */
+ retranslateUi();
+
+ /* Fetch it, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltHandleFormatComboChange", Qt::QueuedConnection);
+}
+
+bool UIWizardExportAppPageExpert::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* There should be at least one vm selected: */
+ if (fResult)
+ fResult = wizard()->machineNames().size() > 0;
+
+ /* Check appliance settings: */
+ if (fResult)
+ {
+ const bool fOVF = wizard()->format() == "ovf-0.9"
+ || wizard()->format() == "ovf-1.0"
+ || wizard()->format() == "ovf-2.0";
+ const bool fCSP = wizard()->isFormatCloudOne();
+
+ fResult = ( fOVF
+ && UICommon::hasAllowedExtension(wizard()->path().toLower(), OVFFileExts))
+ || ( fCSP
+ && wizard()->cloudAppliance().isNotNull()
+ && wizard()->cloudClient().isNotNull()
+ && wizard()->vsd().isNotNull()
+ && wizard()->vsdExportForm().isNotNull());
+ }
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardExportAppPageExpert::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check whether there was cloud target selected: */
+ if (wizard()->isFormatCloudOne())
+ {
+ /* Make sure table has own data committed: */
+ m_pFormEditor->makeSureEditorDataCommitted();
+
+ /* Init VSD form: */
+ CVirtualSystemDescriptionForm comForm;
+ /* Check whether we have proper VSD form: */
+ if (!m_fLaunching)
+ {
+ /* We are going to upload image: */
+ comForm = wizard()->vsdExportForm();
+ fResult = comForm.isNotNull();
+ }
+ else
+ {
+ /* We are going to launch VM: */
+ comForm = wizard()->vsdLaunchForm();
+ fResult = comForm.isNotNull();
+ }
+ /* Give changed VSD back: */
+ if (fResult)
+ {
+ comForm.GetVirtualSystemDescription();
+ fResult = comForm.isOk();
+ if (!fResult)
+ UINotificationMessage::cannotAcquireVirtualSystemDescriptionFormParameter(comForm, wizard()->notificationCenter());
+ }
+
+ /* Final stage? */
+ if (fResult)
+ {
+ if (!m_fLaunching)
+ {
+ /* For modes other than AskThenExport, try to export appliance first: */
+ if (wizard()->cloudExportMode() != CloudExportMode_AskThenExport)
+ fResult = wizard()->exportAppliance();
+
+ /* For modes other than DoNotAsk, switch from uploading image to launching VM: */
+ if ( fResult
+ && wizard()->cloudExportMode() != CloudExportMode_DoNotAsk)
+ {
+ /* Invert flags: */
+ fResult = false;
+ m_fLaunching = true;
+
+ /* Disable wizard buttons: */
+ wizard()->disableButtons();
+
+ /* Disable unrelated widgets: */
+ m_pToolBox->setCurrentPage(2);
+ m_pToolBox->setPageEnabled(0, false);
+ m_pToolBox->setPageEnabled(1, false);
+
+ /* Refresh corresponding widgets: */
+ wizard()->createVsdLaunchForm();
+ refreshFormPropertiesTable(m_pFormEditor, wizard()->vsdLaunchForm(), wizard()->isFormatCloudOne());
+ }
+ }
+ else
+ {
+ /* For AskThenExport mode, try to export appliance in the end: */
+ if (wizard()->cloudExportMode() == CloudExportMode_AskThenExport)
+ fResult = wizard()->exportAppliance();
+
+ /* Try to create cloud VM: */
+ if (fResult)
+ fResult = wizard()->createCloudVM();
+ }
+ }
+ }
+ /* Otherwise if there was local target selected: */
+ else
+ {
+ /* Ask user about machines which are in Saved state currently: */
+ QStringList savedMachines;
+ refreshSavedMachines(savedMachines, m_pVMSelector);
+ if (!savedMachines.isEmpty())
+ fResult = msgCenter().confirmExportMachinesInSaveState(savedMachines, this);
+
+ /* Prepare export: */
+ if (fResult)
+ m_pApplianceWidget->prepareExport();
+
+ /* Try to export appliance: */
+ if (fResult)
+ fResult = wizard()->exportAppliance();
+ }
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardExportAppPageExpert::sltHandleVMItemSelectionChanged()
+{
+ /* Update wizard fields: */
+ wizard()->setMachineNames(machineNames(m_pVMSelector));
+ wizard()->setMachineIDs(machineIDs(m_pVMSelector));
+
+ /* Refresh required settings: */
+ refreshFileSelectorName(m_strFileSelectorName, wizard()->machineNames(), m_strDefaultApplianceName, wizard()->isFormatCloudOne());
+ refreshFileSelectorPath(m_pFileSelector, m_strFileSelectorName, m_strFileSelectorExt, wizard()->isFormatCloudOne());
+
+ /* Update local stuff: */
+ updateLocalStuff();
+ /* Update cloud stuff: */
+ updateCloudStuff();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageExpert::sltHandleFormatComboChange()
+{
+ /* Update combo tool-tip: */
+ updateFormatComboToolTip(m_pFormatComboBox);
+
+ /* Update wizard fields: */
+ wizard()->setFormat(format(m_pFormatComboBox));
+ wizard()->setFormatCloudOne(isFormatCloudOne(m_pFormatComboBox));
+
+ /* Refresh settings widget state: */
+ UIWizardExportAppFormat::refreshStackedWidget(m_pSettingsWidget1, wizard()->isFormatCloudOne());
+ UIWizardExportAppSettings::refreshStackedWidget(m_pSettingsWidget2, wizard()->isFormatCloudOne());
+
+ /* Update export settings: */
+ refreshFileSelectorExtension(m_strFileSelectorExt, m_pFileSelector, wizard()->isFormatCloudOne());
+ refreshFileSelectorPath(m_pFileSelector, m_strFileSelectorName, m_strFileSelectorExt, wizard()->isFormatCloudOne());
+ refreshManifestCheckBoxAccess(m_pManifestCheckbox, wizard()->isFormatCloudOne());
+ refreshIncludeISOsCheckBoxAccess(m_pIncludeISOsCheckbox, wizard()->isFormatCloudOne());
+ refreshProfileCombo(m_pProfileComboBox, wizard()->notificationCenter(), wizard()->format(), wizard()->isFormatCloudOne());
+ refreshCloudExportMode(m_exportModeButtons, wizard()->isFormatCloudOne());
+
+ /* Update local stuff: */
+ updateLocalStuff();
+ /* Update profile: */
+ sltHandleProfileComboChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageExpert::sltHandleFileSelectorChange()
+{
+ /* Skip empty paths: */
+ if (m_pFileSelector->path().isEmpty())
+ return;
+
+ m_strFileSelectorName = QFileInfo(m_pFileSelector->path()).completeBaseName();
+ wizard()->setPath(m_pFileSelector->path());
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageExpert::sltHandleMACAddressExportPolicyComboChange()
+{
+ updateMACAddressExportPolicyComboToolTip(m_pMACComboBox);
+ wizard()->setMACAddressExportPolicy(m_pMACComboBox->currentData().value<MACAddressExportPolicy>());
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageExpert::sltHandleManifestCheckBoxChange()
+{
+ wizard()->setManifestSelected(m_pManifestCheckbox->isChecked());
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageExpert::sltHandleIncludeISOsCheckBoxChange()
+{
+ wizard()->setIncludeISOsSelected(m_pIncludeISOsCheckbox->isChecked());
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageExpert::sltHandleProfileComboChange()
+{
+ /* Update wizard fields: */
+ wizard()->setProfileName(profileName(m_pProfileComboBox));
+
+ /* Update export settings: */
+ refreshCloudProfile(m_comCloudProfile,
+ wizard()->notificationCenter(),
+ wizard()->format(),
+ wizard()->profileName(),
+ wizard()->isFormatCloudOne());
+
+ /* Update cloud stuff: */
+ updateCloudStuff();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageExpert::sltHandleRadioButtonToggled(QAbstractButton *pButton, bool fToggled)
+{
+ /* Handle checked buttons only: */
+ if (!fToggled)
+ return;
+
+ /* Update cloud export mode field value: */
+ wizard()->setCloudExportMode(m_exportModeButtons.key(pButton));
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageExpert::sltHandleProfileButtonClick()
+{
+ /* Open Cloud Profile Manager: */
+ if (gpManager)
+ gpManager->openCloudProfileManager();
+}
+
+void UIWizardExportAppPageExpert::updateLocalStuff()
+{
+ /* Create appliance: */
+ CAppliance comAppliance;
+ refreshLocalStuff(comAppliance, wizard(), wizard()->machineIDs(), wizard()->uri());
+ wizard()->setLocalAppliance(comAppliance);
+}
+
+void UIWizardExportAppPageExpert::updateCloudStuff()
+{
+ /* Create appliance, client, VSD and VSD export form: */
+ CAppliance comAppliance;
+ CCloudClient comClient;
+ CVirtualSystemDescription comDescription;
+ CVirtualSystemDescriptionForm comForm;
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ refreshCloudStuff(comAppliance,
+ comClient,
+ comDescription,
+ comForm,
+ wizard(),
+ m_comCloudProfile,
+ wizard()->machineIDs(),
+ wizard()->uri(),
+ wizard()->cloudExportMode());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ wizard()->setCloudAppliance(comAppliance);
+ wizard()->setCloudClient(comClient);
+ wizard()->setVsd(comDescription);
+ wizard()->setVsdExportForm(comForm);
+
+ /* Refresh corresponding widgets: */
+ refreshApplianceSettingsWidget(m_pApplianceWidget, wizard()->localAppliance(), wizard()->isFormatCloudOne());
+ refreshFormPropertiesTable(m_pFormEditor, wizard()->vsdExportForm(), wizard()->isFormatCloudOne());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageExpert.h b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageExpert.h
new file mode 100644
index 00000000..a66e741c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageExpert.h
@@ -0,0 +1,208 @@
+/* $Id: UIWizardExportAppPageExpert.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportAppPageExpert class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageExpert_h
+#define FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageExpert_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+#include "UIWizardExportApp.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CAppliance.h"
+#include "CCloudClient.h"
+#include "CCloudProfile.h"
+#include "CVirtualSystemDescription.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QButtonGroup;
+class QCheckBox;
+class QGridLayout;
+class QGroupBox;
+class QLabel;
+class QListWidget;
+class QStackedWidget;
+class QIComboBox;
+class QIToolButton;
+class UIApplianceExportEditorWidget;
+class UIEmptyFilePathSelector;
+class UIFormEditorWidget;
+class UIToolBox;
+class UIWizardExportApp;
+
+/** UINativeWizardPage extension for expert page of the Export Appliance wizard,
+ * based on UIWizardExportAppVMs, UIWizardExportAppFormat & UIWizardExportAppSettings namespace functions. */
+class UIWizardExportAppPageExpert : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs expert page.
+ * @param selectedVMNames Brings the list of selected VM names. */
+ UIWizardExportAppPageExpert(const QStringList &selectedVMNames, bool fExportToOCIByDefault);
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardExportApp *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Handles VM selector index change. */
+ void sltHandleVMItemSelectionChanged();
+
+ /** Handles format combo change. */
+ void sltHandleFormatComboChange();
+
+ /** Handles change in file-name selector. */
+ void sltHandleFileSelectorChange();
+
+ /** Handles change in MAC address export policy combo-box. */
+ void sltHandleMACAddressExportPolicyComboChange();
+
+ /** Handles change in manifest check-box. */
+ void sltHandleManifestCheckBoxChange();
+
+ /** Handles change in include ISOs check-box. */
+ void sltHandleIncludeISOsCheckBoxChange();
+
+ /** Handles change in profile combo-box. */
+ void sltHandleProfileComboChange();
+
+ /** Handles cloud export radio-button clicked. */
+ void sltHandleRadioButtonToggled(QAbstractButton *pButton, bool fToggled);
+
+ /** Handles profile tool-button click. */
+ void sltHandleProfileButtonClick();
+
+private:
+
+ /** Update local stuff. */
+ void updateLocalStuff();
+ /** Updates cloud stuff. */
+ void updateCloudStuff();
+
+ /** Holds the list of selected VM names. */
+ const QStringList m_selectedVMNames;
+ /** Holds whether default format should be Export to OCI. */
+ bool m_fExportToOCIByDefault;
+
+ /** Holds the default appliance name. */
+ QString m_strDefaultApplianceName;
+ /** Holds the file selector name. */
+ QString m_strFileSelectorName;
+ /** Holds the file selector ext. */
+ QString m_strFileSelectorExt;
+
+ /** Holds the Cloud Profile object reference. */
+ CCloudProfile m_comCloudProfile;
+
+
+ /** Holds the tool-box instance. */
+ UIToolBox *m_pToolBox;
+
+
+ /** Holds the VM selector instance. */
+ QListWidget *m_pVMSelector;
+
+
+ /** Holds the format layout. */
+ QGridLayout *m_pFormatLayout;
+ /** Holds the format combo-box label instance. */
+ QLabel *m_pFormatComboBoxLabel;
+ /** Holds the format combo-box instance. */
+ QIComboBox *m_pFormatComboBox;
+
+ /** Holds the settings widget 1 instance. */
+ QStackedWidget *m_pSettingsWidget1;
+
+ /** Holds the settings layout 1. */
+ QGridLayout *m_pSettingsLayout1;
+ /** Holds the file selector label instance. */
+ QLabel *m_pFileSelectorLabel;
+ /** Holds the file selector instance. */
+ UIEmptyFilePathSelector *m_pFileSelector;
+ /** Holds the MAC address policy combo-box label instance. */
+ QLabel *m_pMACComboBoxLabel;
+ /** Holds the MAC address policy check-box instance. */
+ QIComboBox *m_pMACComboBox;
+ /** Holds the additional label instance. */
+ QLabel *m_pAdditionalLabel;
+ /** Holds the manifest check-box instance. */
+ QCheckBox *m_pManifestCheckbox;
+ /** Holds the include ISOs check-box instance. */
+ QCheckBox *m_pIncludeISOsCheckbox;
+
+ /** Holds the settings layout 2. */
+ QGridLayout *m_pSettingsLayout2;
+ /** Holds the profile label instance. */
+ QLabel *m_pProfileLabel;
+ /** Holds the profile combo-box instance. */
+ QIComboBox *m_pProfileComboBox;
+ /** Holds the profile management tool-button instance. */
+ QIToolButton *m_pProfileToolButton;
+
+ /** Holds the export mode label instance. */
+ QLabel *m_pExportModeLabel;
+ /** Holds the export mode button group instance. */
+ QButtonGroup *m_pExportModeButtonGroup;
+ /** Holds the map of export mode button instances. */
+ QMap<CloudExportMode, QAbstractButton*> m_exportModeButtons;
+
+
+ /** Holds the settings widget 2 instance. */
+ QStackedWidget *m_pSettingsWidget2;
+
+ /** Holds the appliance widget reference. */
+ UIApplianceExportEditorWidget *m_pApplianceWidget;
+ /** Holds the Form Editor widget instance. */
+ UIFormEditorWidget *m_pFormEditor;
+
+ /** Holds whether cloud exporting is at launching stage. */
+ bool m_fLaunching;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageExpert_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageFormat.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageFormat.cpp
new file mode 100644
index 00000000..591920cb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageFormat.cpp
@@ -0,0 +1,1214 @@
+/* $Id: UIWizardExportAppPageFormat.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportAppPageFormat class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QDir>
+#include <QGridLayout>
+#include <QLabel>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QStackedWidget>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIRichTextLabel.h"
+#include "QIToolButton.h"
+#include "UICloudNetworkingStuff.h"
+#include "UICommon.h"
+#include "UIEmptyFilePathSelector.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UINotificationCenter.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualBoxManager.h"
+#include "UIWizardExportApp.h"
+#include "UIWizardExportAppPageFormat.h"
+
+/* COM includes: */
+#include "CMachine.h"
+#include "CSystemProperties.h"
+
+/* Namespaces: */
+using namespace UIWizardExportAppFormat;
+
+
+/*********************************************************************************************************************************
+* Class UIWizardExportAppFormat implementation. *
+*********************************************************************************************************************************/
+
+void UIWizardExportAppFormat::populateFormats(QIComboBox *pCombo, UINotificationCenter *pCenter, bool fExportToOCIByDefault)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (pCombo->currentIndex() != -1)
+ strOldData = pCombo->currentData(FormatData_ShortName).toString();
+ else
+ {
+ /* Otherwise "OCI" or "ovf-1.0" should be the default one: */
+ if (fExportToOCIByDefault)
+ strOldData = "OCI";
+ else
+ strOldData = "ovf-1.0";
+ }
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Compose hardcoded format list: */
+ QStringList formats;
+ formats << "ovf-0.9";
+ formats << "ovf-1.0";
+ formats << "ovf-2.0";
+ /* Add that list to combo: */
+ foreach (const QString &strShortName, formats)
+ {
+ /* Compose empty item, fill it's data: */
+ pCombo->addItem(QString());
+ pCombo->setItemData(pCombo->count() - 1, strShortName, FormatData_ShortName);
+ }
+
+ /* Iterate through existing providers: */
+ foreach (const CCloudProvider &comProvider, listCloudProviders(pCenter))
+ {
+ /* Skip if we have nothing to populate (file missing?): */
+ if (comProvider.isNull())
+ continue;
+ /* Acquire provider name: */
+ QString strProviderName;
+ if (!cloudProviderName(comProvider, strProviderName, pCenter))
+ continue;
+ /* Acquire provider short name: */
+ QString strProviderShortName;
+ if (!cloudProviderShortName(comProvider, strProviderShortName, pCenter))
+ continue;
+
+ /* Compose empty item, fill it's data: */
+ pCombo->addItem(QString());
+ pCombo->setItemData(pCombo->count() - 1, strProviderName, FormatData_Name);
+ pCombo->setItemData(pCombo->count() - 1, strProviderShortName, FormatData_ShortName);
+ pCombo->setItemData(pCombo->count() - 1, true, FormatData_IsItCloudFormat);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = pCombo->findData(strOldData, FormatData_ShortName);
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+}
+
+void UIWizardExportAppFormat::populateMACAddressPolicies(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+ /* We need top-level parent as well: */
+ QWidget *pParent = pCombo->window();
+ AssertPtrReturnVoid(pParent);
+
+ /* Map known export options to known MAC address export policies: */
+ QMap<KExportOptions, MACAddressExportPolicy> knownOptions;
+ knownOptions[KExportOptions_StripAllMACs] = MACAddressExportPolicy_StripAllMACs;
+ knownOptions[KExportOptions_StripAllNonNATMACs] = MACAddressExportPolicy_StripAllNonNATMACs;
+ /* Load currently supported export options: */
+ const QVector<KExportOptions> supportedOptions =
+ uiCommon().virtualBox().GetSystemProperties().GetSupportedExportOptions();
+ /* Check which of supported options/policies are known: */
+ QList<MACAddressExportPolicy> supportedPolicies;
+ foreach (const KExportOptions &enmOption, supportedOptions)
+ if (knownOptions.contains(enmOption))
+ supportedPolicies << knownOptions.value(enmOption);
+ /* Remember current item data to be able to restore it: */
+ MACAddressExportPolicy enmOldData = MACAddressExportPolicy_MAX;
+ if (pCombo->currentIndex() != -1)
+ enmOldData = pCombo->currentData().value<MACAddressExportPolicy>();
+ else
+ {
+ if (supportedPolicies.contains(MACAddressExportPolicy_StripAllNonNATMACs))
+ enmOldData = MACAddressExportPolicy_StripAllNonNATMACs;
+ else
+ enmOldData = MACAddressExportPolicy_KeepAllMACs;
+ }
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Add supported policies first: */
+ foreach (const MACAddressExportPolicy &enmPolicy, supportedPolicies)
+ pCombo->addItem(QString(), QVariant::fromValue(enmPolicy));
+
+ /* Add hardcoded policy finally: */
+ pCombo->addItem(QString(), QVariant::fromValue(MACAddressExportPolicy_KeepAllMACs));
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && enmOldData != MACAddressExportPolicy_MAX)
+ iNewIndex = pCombo->findData(QVariant::fromValue(enmOldData));
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+}
+
+QString UIWizardExportAppFormat::format(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pCombo, QString());
+
+ /* Give the actual result: */
+ return pCombo->currentData(FormatData_ShortName).toString();
+}
+
+bool UIWizardExportAppFormat::isFormatCloudOne(QIComboBox *pCombo, int iIndex /* = -1 */)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pCombo, false);
+
+ /* Handle special case, -1 means "current one": */
+ if (iIndex == -1)
+ iIndex = pCombo->currentIndex();
+
+ /* Give the actual result: */
+ return pCombo->itemData(iIndex, FormatData_IsItCloudFormat).toBool();
+}
+
+void UIWizardExportAppFormat::refreshStackedWidget(QStackedWidget *pStackedWidget, bool fIsFormatCloudOne)
+{
+ /* Update stack appearance according to chosen format: */
+ pStackedWidget->setCurrentIndex((int)fIsFormatCloudOne);
+}
+
+void UIWizardExportAppFormat::refreshFileSelectorName(QString &strFileSelectorName,
+ const QStringList &machineNames,
+ const QString &strDefaultApplianceName,
+ bool fIsFormatCloudOne)
+{
+ /* If format is cloud one: */
+ if (fIsFormatCloudOne)
+ {
+ /* We use no name: */
+ strFileSelectorName.clear();
+ }
+ /* If format is local one: */
+ else
+ {
+ /* If it's one VM only, we use the VM name as file-name: */
+ if (machineNames.size() == 1)
+ strFileSelectorName = machineNames.first();
+ /* Otherwise => we use the default file-name: */
+ else
+ strFileSelectorName = strDefaultApplianceName;
+ }
+}
+
+void UIWizardExportAppFormat::refreshFileSelectorExtension(QString &strFileSelectorExt,
+ UIEmptyFilePathSelector *pFileSelector,
+ bool fIsFormatCloudOne)
+{
+ /* If format is cloud one: */
+ if (fIsFormatCloudOne)
+ {
+ /* We use no extension: */
+ strFileSelectorExt.clear();
+
+ /* Update file chooser accordingly: */
+ pFileSelector->setFileFilters(QString());
+ }
+ /* If format is local one: */
+ else
+ {
+ /* We use the default (.ova) extension: */
+ strFileSelectorExt = ".ova";
+
+ /* Update file chooser accordingly: */
+ pFileSelector->setFileFilters(UIWizardExportApp::tr("Open Virtualization Format Archive (%1)").arg("*.ova") + ";;" +
+ UIWizardExportApp::tr("Open Virtualization Format (%1)").arg("*.ovf"));
+ }
+}
+
+void UIWizardExportAppFormat::refreshFileSelectorPath(UIEmptyFilePathSelector *pFileSelector,
+ const QString &strFileSelectorName,
+ const QString &strFileSelectorExt,
+ bool fIsFormatCloudOne)
+{
+ /* If format is cloud one: */
+ if (fIsFormatCloudOne)
+ {
+ /* Clear file selector path: */
+ pFileSelector->setPath(QString());
+ }
+ /* If format is local one: */
+ else
+ {
+ /* Compose file selector path: */
+ const QString strPath = QDir::toNativeSeparators(QString("%1/%2")
+ .arg(uiCommon().documentsPath())
+ .arg(strFileSelectorName + strFileSelectorExt));
+ pFileSelector->setPath(strPath);
+ }
+}
+
+void UIWizardExportAppFormat::refreshManifestCheckBoxAccess(QCheckBox *pCheckBox,
+ bool fIsFormatCloudOne)
+{
+ /* If format is cloud one: */
+ if (fIsFormatCloudOne)
+ {
+ /* Disable manifest check-box: */
+ pCheckBox->setChecked(false);
+ pCheckBox->setEnabled(false);
+ }
+ /* If format is local one: */
+ else
+ {
+ /* Enable and select manifest check-box: */
+ pCheckBox->setChecked(true);
+ pCheckBox->setEnabled(true);
+ }
+}
+
+void UIWizardExportAppFormat::refreshIncludeISOsCheckBoxAccess(QCheckBox *pCheckBox,
+ bool fIsFormatCloudOne)
+{
+ /* If format is cloud one: */
+ if (fIsFormatCloudOne)
+ {
+ /* Disable include ISO check-box: */
+ pCheckBox->setChecked(false);
+ pCheckBox->setEnabled(false);
+ }
+ /* If format is local one: */
+ else
+ {
+ /* Enable include ISO check-box: */
+ pCheckBox->setEnabled(true);
+ }
+}
+
+void UIWizardExportAppFormat::refreshLocalStuff(CAppliance &comLocalAppliance,
+ UIWizardExportApp *pWizard,
+ const QList<QUuid> &machineIDs,
+ const QString &strUri)
+{
+ /* Clear stuff: */
+ comLocalAppliance = CAppliance();
+
+ /* Create appliance: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CAppliance comAppliance = comVBox.CreateAppliance();
+ if (!comVBox.isOk())
+ return UINotificationMessage::cannotCreateAppliance(comVBox, pWizard->notificationCenter());
+
+ /* Remember appliance: */
+ comLocalAppliance = comAppliance;
+
+ /* Iterate over all the selected machine uuids: */
+ foreach (const QUuid &uMachineId, machineIDs)
+ {
+ /* Get the machine with the uMachineId: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CMachine comMachine = comVBox.FindMachine(uMachineId.toString());
+ if (!comVBox.isOk())
+ return UINotificationMessage::cannotFindMachineById(comVBox, uMachineId, pWizard->notificationCenter());
+ /* Add the export description to our appliance object: */
+ CVirtualSystemDescription comVsd = comMachine.ExportTo(comLocalAppliance, strUri);
+ if (!comMachine.isOk())
+ return UINotificationMessage::cannotExportMachine(comMachine, pWizard->notificationCenter());
+ /* Add some additional fields the user may change: */
+ comVsd.AddDescription(KVirtualSystemDescriptionType_Product, "", "");
+ comVsd.AddDescription(KVirtualSystemDescriptionType_ProductUrl, "", "");
+ comVsd.AddDescription(KVirtualSystemDescriptionType_Vendor, "", "");
+ comVsd.AddDescription(KVirtualSystemDescriptionType_VendorUrl, "", "");
+ comVsd.AddDescription(KVirtualSystemDescriptionType_Version, "", "");
+ comVsd.AddDescription(KVirtualSystemDescriptionType_License, "", "");
+ }
+}
+
+void UIWizardExportAppFormat::refreshProfileCombo(QIComboBox *pCombo,
+ UINotificationCenter *pCenter,
+ const QString &strFormat,
+ bool fIsFormatCloudOne)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* If format is cloud one: */
+ if (fIsFormatCloudOne)
+ {
+ /* Acquire provider: */
+ CCloudProvider comProvider = cloudProviderByShortName(strFormat, pCenter);
+ AssertReturnVoid(comProvider.isNotNull());
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (pCombo->currentIndex() != -1)
+ strOldData = pCombo->currentData(ProfileData_Name).toString();
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Acquire restricted accounts: */
+ const QStringList restrictedProfiles = gEDataManager->cloudProfileManagerRestrictions();
+
+ /* Iterate through existing profile names: */
+ QStringList allowedProfileNames;
+ QStringList restrictedProfileNames;
+ foreach (const CCloudProfile &comProfile, listCloudProfiles(comProvider, pCenter))
+ {
+ /* Skip if we have nothing to populate (wtf happened?): */
+ if (comProfile.isNull())
+ continue;
+ /* Acquire profile name: */
+ QString strCurrentProfileName;
+ if (!cloudProfileName(comProfile, strCurrentProfileName, pCenter))
+ continue;
+
+ /* Compose full profile name: */
+ const QString strFullProfileName = QString("/%1/%2").arg(strFormat).arg(strCurrentProfileName);
+ /* Append to appropriate list: */
+ if (restrictedProfiles.contains(strFullProfileName))
+ restrictedProfileNames.append(strCurrentProfileName);
+ else
+ allowedProfileNames.append(strCurrentProfileName);
+ }
+
+ /* Add allowed items: */
+ foreach (const QString &strAllowedProfileName, allowedProfileNames)
+ {
+ /* Compose item, fill it's data: */
+ pCombo->addItem(strAllowedProfileName);
+ pCombo->setItemData(pCombo->count() - 1, strAllowedProfileName, ProfileData_Name);
+ QFont fnt = pCombo->font();
+ fnt.setBold(true);
+ pCombo->setItemData(pCombo->count() - 1, fnt, Qt::FontRole);
+ }
+ /* Add restricted items: */
+ foreach (const QString &strRestrictedProfileName, restrictedProfileNames)
+ {
+ /* Compose item, fill it's data: */
+ pCombo->addItem(strRestrictedProfileName);
+ pCombo->setItemData(pCombo->count() - 1, strRestrictedProfileName, ProfileData_Name);
+ QBrush brsh;
+ brsh.setColor(Qt::gray);
+ pCombo->setItemData(pCombo->count() - 1, brsh, Qt::ForegroundRole);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = pCombo->findData(strOldData, ProfileData_Name);
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+ }
+ /* If format is local one: */
+ else
+ {
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo: */
+ pCombo->clear();
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+ }
+}
+
+void UIWizardExportAppFormat::refreshCloudProfile(CCloudProfile &comCloudProfile,
+ UINotificationCenter *pCenter,
+ const QString &strShortProviderName,
+ const QString &strProfileName,
+ bool fIsFormatCloudOne)
+{
+ /* If format is cloud one: */
+ if (fIsFormatCloudOne)
+ comCloudProfile = cloudProfileByName(strShortProviderName, strProfileName, pCenter);
+ /* If format is local one: */
+ else
+ comCloudProfile = CCloudProfile();
+}
+
+void UIWizardExportAppFormat::refreshCloudExportMode(const QMap<CloudExportMode, QAbstractButton*> &radios,
+ bool fIsFormatCloudOne)
+{
+ /* If format is cloud one: */
+ if (fIsFormatCloudOne)
+ {
+ /* Check if something already chosen: */
+ bool fSomethingChosen = false;
+ foreach (QAbstractButton *pButton, radios.values())
+ if (pButton->isChecked())
+ fSomethingChosen = true;
+ /* Choose default cloud export option: */
+ if (!fSomethingChosen)
+ radios.value(CloudExportMode_ExportThenAsk)->setChecked(true);
+ }
+ /* If format is local one: */
+ else
+ {
+ /* Make sure nothing chosen: */
+ foreach (QAbstractButton *pButton, radios.values())
+ pButton->setChecked(false);
+ }
+}
+
+void UIWizardExportAppFormat::refreshCloudStuff(CAppliance &comCloudAppliance,
+ CCloudClient &comCloudClient,
+ CVirtualSystemDescription &comCloudVsd,
+ CVirtualSystemDescriptionForm &comCloudVsdExportForm,
+ UIWizardExportApp *pWizard,
+ const CCloudProfile &comCloudProfile,
+ const QList<QUuid> &machineIDs,
+ const QString &strUri,
+ const CloudExportMode enmCloudExportMode)
+{
+ /* Clear stuff: */
+ comCloudAppliance = CAppliance();
+ comCloudClient = CCloudClient();
+ comCloudVsd = CVirtualSystemDescription();
+ comCloudVsdExportForm = CVirtualSystemDescriptionForm();
+
+ /* Sanity check: */
+ if (comCloudProfile.isNull())
+ return;
+ if (machineIDs.isEmpty())
+ return;
+
+ /* Perform cloud export procedure for first uuid only: */
+ const QUuid uMachineId = machineIDs.first();
+
+ /* Get the machine with the uMachineId: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CMachine comMachine = comVBox.FindMachine(uMachineId.toString());
+ if (!comVBox.isOk())
+ return UINotificationMessage::cannotFindMachineById(comVBox, uMachineId, pWizard->notificationCenter());
+
+ /* Create appliance: */
+ CAppliance comAppliance = comVBox.CreateAppliance();
+ if (!comVBox.isOk())
+ return UINotificationMessage::cannotCreateAppliance(comVBox, pWizard->notificationCenter());
+
+ /* Remember appliance: */
+ comCloudAppliance = comAppliance;
+
+ /* Add the export virtual system description to our appliance object: */
+ CVirtualSystemDescription comVsd = comMachine.ExportTo(comCloudAppliance, strUri);
+ if (!comMachine.isOk())
+ return UINotificationMessage::cannotExportMachine(comMachine, pWizard->notificationCenter());
+
+ /* Remember description: */
+ comCloudVsd = comVsd;
+
+ /* Add Launch Instance flag to virtual system description: */
+ switch (enmCloudExportMode)
+ {
+ case CloudExportMode_AskThenExport:
+ case CloudExportMode_ExportThenAsk:
+ comCloudVsd.AddDescription(KVirtualSystemDescriptionType_CloudLaunchInstance, "true", QString());
+ break;
+ default:
+ comCloudVsd.AddDescription(KVirtualSystemDescriptionType_CloudLaunchInstance, "false", QString());
+ break;
+ }
+ if (!comCloudVsd.isOk())
+ return UINotificationMessage::cannotChangeVirtualSystemDescriptionParameter(comCloudVsd, pWizard->notificationCenter());
+
+ /* Create Cloud Client: */
+ CCloudClient comClient = cloudClient(comCloudProfile);
+ if (comClient.isNull())
+ return;
+
+ /* Remember client: */
+ comCloudClient = comClient;
+
+ /* Read Cloud Client Export description form: */
+ CVirtualSystemDescriptionForm comVsdExportForm;
+ bool fResult = exportDescriptionForm(comCloudClient, comCloudVsd, comVsdExportForm, pWizard->notificationCenter());
+ if (!fResult)
+ return;
+
+ /* Remember export description form: */
+ comCloudVsdExportForm = comVsdExportForm;
+}
+
+QString UIWizardExportAppFormat::profileName(QIComboBox *pCombo)
+{
+ return pCombo->currentData(ProfileData_Name).toString();
+}
+
+void UIWizardExportAppFormat::updateFormatComboToolTip(QIComboBox *pCombo)
+{
+ AssertPtrReturnVoid(pCombo);
+ QString strCurrentToolTip;
+ if (pCombo->count() != 0)
+ {
+ strCurrentToolTip = pCombo->currentData(Qt::ToolTipRole).toString();
+ AssertMsg(!strCurrentToolTip.isEmpty(), ("Data not found!"));
+ }
+ pCombo->setToolTip(strCurrentToolTip);
+}
+
+void UIWizardExportAppFormat::updateMACAddressExportPolicyComboToolTip(QIComboBox *pCombo)
+{
+ AssertPtrReturnVoid(pCombo);
+ QString strCurrentToolTip;
+ if (pCombo->count() != 0)
+ {
+ strCurrentToolTip = pCombo->currentData(Qt::ToolTipRole).toString();
+ AssertMsg(!strCurrentToolTip.isEmpty(), ("Data not found!"));
+ }
+ pCombo->setToolTip(strCurrentToolTip);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWizardExportAppPageFormat implementation. *
+*********************************************************************************************************************************/
+
+UIWizardExportAppPageFormat::UIWizardExportAppPageFormat(bool fExportToOCIByDefault)
+ : m_fExportToOCIByDefault(fExportToOCIByDefault)
+ , m_pLabelFormat(0)
+ , m_pLabelSettings(0)
+ , m_pFormatLayout(0)
+ , m_pFormatComboBoxLabel(0)
+ , m_pFormatComboBox(0)
+ , m_pSettingsWidget1(0)
+ , m_pSettingsLayout1(0)
+ , m_pFileSelectorLabel(0)
+ , m_pFileSelector(0)
+ , m_pMACComboBoxLabel(0)
+ , m_pMACComboBox(0)
+ , m_pAdditionalLabel(0)
+ , m_pManifestCheckbox(0)
+ , m_pIncludeISOsCheckbox(0)
+ , m_pSettingsLayout2(0)
+ , m_pProfileLabel(0)
+ , m_pProfileComboBox(0)
+ , m_pProfileToolButton(0)
+ , m_pExportModeLabel(0)
+ , m_pExportModeButtonGroup(0)
+{
+ /* Create main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Create format label: */
+ m_pLabelFormat = new QIRichTextLabel(this);
+ if (m_pLabelFormat)
+ pMainLayout->addWidget(m_pLabelFormat);
+
+ /* Create format layout: */
+ m_pFormatLayout = new QGridLayout;
+ if (m_pFormatLayout)
+ {
+#ifdef VBOX_WS_MAC
+ m_pFormatLayout->setContentsMargins(0, 10, 0, 10);
+ m_pFormatLayout->setSpacing(10);
+#else
+ m_pFormatLayout->setContentsMargins(0, qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
+ 0, qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
+#endif
+ m_pFormatLayout->setColumnStretch(0, 0);
+ m_pFormatLayout->setColumnStretch(1, 1);
+
+ /* Create format combo-box label: */
+ m_pFormatComboBoxLabel = new QLabel(this);
+ if (m_pFormatComboBoxLabel)
+ {
+ m_pFormatComboBoxLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
+ m_pFormatLayout->addWidget(m_pFormatComboBoxLabel, 0, 0);
+ }
+ /* Create format combo-box: */
+ m_pFormatComboBox = new QIComboBox(this);
+ if (m_pFormatComboBox)
+ {
+ m_pFormatComboBoxLabel->setBuddy(m_pFormatComboBox);
+ m_pFormatLayout->addWidget(m_pFormatComboBox, 0, 1);
+ }
+
+ /* Add into layout: */
+ pMainLayout->addLayout(m_pFormatLayout);
+ }
+
+ /* Create settings label: */
+ m_pLabelSettings = new QIRichTextLabel(this);
+ if (m_pLabelSettings)
+ pMainLayout->addWidget(m_pLabelSettings);
+
+ /* Create settings widget 1: */
+ m_pSettingsWidget1 = new QStackedWidget(this);
+ if (m_pSettingsWidget1)
+ {
+ /* Create settings pane 1: */
+ QWidget *pSettingsPane1 = new QWidget(m_pSettingsWidget1);
+ if (pSettingsPane1)
+ {
+ /* Create settings layout 1: */
+ m_pSettingsLayout1 = new QGridLayout(pSettingsPane1);
+ if (m_pSettingsLayout1)
+ {
+#ifdef VBOX_WS_MAC
+ m_pSettingsLayout1->setContentsMargins(0, 10, 0, 10);
+ m_pSettingsLayout1->setSpacing(10);
+#else
+ m_pSettingsLayout1->setContentsMargins(0, qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
+ 0, qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
+#endif
+ m_pSettingsLayout1->setColumnStretch(0, 0);
+ m_pSettingsLayout1->setColumnStretch(1, 1);
+
+ /* Create file selector: */
+ m_pFileSelector = new UIEmptyFilePathSelector(pSettingsPane1);
+ if (m_pFileSelector)
+ {
+ m_pFileSelector->setMode(UIEmptyFilePathSelector::Mode_File_Save);
+ m_pFileSelector->setEditable(true);
+ m_pFileSelector->setButtonPosition(UIEmptyFilePathSelector::RightPosition);
+ m_pFileSelector->setDefaultSaveExt("ova");
+ m_pSettingsLayout1->addWidget(m_pFileSelector, 0, 1, 1, 2);
+ }
+ /* Create file selector label: */
+ m_pFileSelectorLabel = new QLabel(pSettingsPane1);
+ if (m_pFileSelectorLabel)
+ {
+ m_pFileSelectorLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
+ m_pFileSelectorLabel->setBuddy(m_pFileSelector);
+ m_pSettingsLayout1->addWidget(m_pFileSelectorLabel, 0, 0);
+ }
+
+ /* Create MAC policy combo-box: */
+ m_pMACComboBox = new QIComboBox(pSettingsPane1);
+ if (m_pMACComboBox)
+ m_pSettingsLayout1->addWidget(m_pMACComboBox, 1, 1, 1, 2);
+ /* Create format combo-box label: */
+ m_pMACComboBoxLabel = new QLabel(pSettingsPane1);
+ if (m_pMACComboBoxLabel)
+ {
+ m_pMACComboBoxLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
+ m_pMACComboBoxLabel->setBuddy(m_pMACComboBox);
+ m_pSettingsLayout1->addWidget(m_pMACComboBoxLabel, 1, 0);
+ }
+
+ /* Create advanced label: */
+ m_pAdditionalLabel = new QLabel(pSettingsPane1);
+ if (m_pAdditionalLabel)
+ {
+ m_pAdditionalLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
+ m_pSettingsLayout1->addWidget(m_pAdditionalLabel, 2, 0);
+ }
+ /* Create manifest check-box: */
+ m_pManifestCheckbox = new QCheckBox(pSettingsPane1);
+ if (m_pManifestCheckbox)
+ m_pSettingsLayout1->addWidget(m_pManifestCheckbox, 2, 1);
+ /* Create include ISOs check-box: */
+ m_pIncludeISOsCheckbox = new QCheckBox(pSettingsPane1);
+ if (m_pIncludeISOsCheckbox)
+ m_pSettingsLayout1->addWidget(m_pIncludeISOsCheckbox, 3, 1);
+
+ /* Create placeholder: */
+ QWidget *pPlaceholder = new QWidget(pSettingsPane1);
+ if (pPlaceholder)
+ m_pSettingsLayout1->addWidget(pPlaceholder, 4, 0, 1, 3);
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget1->addWidget(pSettingsPane1);
+ }
+
+ /* Create settings pane 2: */
+ QWidget *pSettingsPane2 = new QWidget(m_pSettingsWidget1);
+ if (pSettingsPane2)
+ {
+ /* Create settings layout 2: */
+ m_pSettingsLayout2 = new QGridLayout(pSettingsPane2);
+ if (m_pSettingsLayout2)
+ {
+#ifdef VBOX_WS_MAC
+ m_pSettingsLayout2->setContentsMargins(0, 10, 0, 10);
+ m_pSettingsLayout2->setSpacing(10);
+
+#else
+ m_pSettingsLayout2->setContentsMargins(0, qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
+ 0, qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
+#endif
+ m_pSettingsLayout2->setColumnStretch(0, 0);
+ m_pSettingsLayout2->setColumnStretch(1, 1);
+ m_pSettingsLayout2->setRowStretch(4, 1);
+
+ /* Create profile label: */
+ m_pProfileLabel = new QLabel(pSettingsPane2);
+ if (m_pProfileLabel)
+ {
+ m_pProfileLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
+ m_pSettingsLayout2->addWidget(m_pProfileLabel, 0, 0);
+ }
+ /* Create sub-layout: */
+ QHBoxLayout *pSubLayout = new QHBoxLayout;
+ if (pSubLayout)
+ {
+ pSubLayout->setContentsMargins(0, 0, 0, 0);
+ pSubLayout->setSpacing(1);
+
+ /* Create profile combo-box: */
+ m_pProfileComboBox = new QIComboBox(pSettingsPane2);
+ if (m_pProfileComboBox)
+ {
+ m_pProfileLabel->setBuddy(m_pProfileComboBox);
+ pSubLayout->addWidget(m_pProfileComboBox);
+ }
+ /* Create profile tool-button: */
+ m_pProfileToolButton = new QIToolButton(pSettingsPane2);
+ if (m_pProfileToolButton)
+ {
+ m_pProfileToolButton->setIcon(UIIconPool::iconSet(":/cloud_profile_manager_16px.png",
+ ":/cloud_profile_manager_disabled_16px.png"));
+ pSubLayout->addWidget(m_pProfileToolButton);
+ }
+
+ /* Add into layout: */
+ m_pSettingsLayout2->addLayout(pSubLayout, 0, 1);
+ }
+
+ /* Create profile label: */
+ m_pExportModeLabel = new QLabel(pSettingsPane2);
+ if (m_pExportModeLabel)
+ {
+ m_pExportModeLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
+ m_pSettingsLayout2->addWidget(m_pExportModeLabel, 1, 0);
+ }
+
+ /* Create button-group: */
+ m_pExportModeButtonGroup = new QButtonGroup(pSettingsPane2);
+ if (m_pExportModeButtonGroup)
+ {
+ /* Create Do Not Ask button: */
+ m_exportModeButtons[CloudExportMode_DoNotAsk] = new QRadioButton(pSettingsPane2);
+ if (m_exportModeButtons.value(CloudExportMode_DoNotAsk))
+ {
+ m_pExportModeButtonGroup->addButton(m_exportModeButtons.value(CloudExportMode_DoNotAsk));
+ m_pSettingsLayout2->addWidget(m_exportModeButtons.value(CloudExportMode_DoNotAsk), 1, 1);
+ }
+ /* Create Ask Then Export button: */
+ m_exportModeButtons[CloudExportMode_AskThenExport] = new QRadioButton(pSettingsPane2);
+ if (m_exportModeButtons.value(CloudExportMode_AskThenExport))
+ {
+ m_pExportModeButtonGroup->addButton(m_exportModeButtons.value(CloudExportMode_AskThenExport));
+ m_pSettingsLayout2->addWidget(m_exportModeButtons.value(CloudExportMode_AskThenExport), 2, 1);
+ }
+ /* Create Export Then Ask button: */
+ m_exportModeButtons[CloudExportMode_ExportThenAsk] = new QRadioButton(pSettingsPane2);
+ if (m_exportModeButtons.value(CloudExportMode_ExportThenAsk))
+ {
+ m_pExportModeButtonGroup->addButton(m_exportModeButtons.value(CloudExportMode_ExportThenAsk));
+ m_pSettingsLayout2->addWidget(m_exportModeButtons.value(CloudExportMode_ExportThenAsk), 3, 1);
+ }
+ }
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget1->addWidget(pSettingsPane2);
+ }
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pSettingsWidget1);
+ }
+ }
+
+ /* Setup connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UIWizardExportAppPageFormat::sltHandleFormatComboChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UIWizardExportAppPageFormat::sltHandleFormatComboChange);
+ connect(m_pFileSelector, &UIEmptyFilePathSelector::pathChanged,
+ this, &UIWizardExportAppPageFormat::sltHandleFileSelectorChange);
+ connect(m_pFormatComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardExportAppPageFormat::sltHandleFormatComboChange);
+ connect(m_pMACComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardExportAppPageFormat::sltHandleMACAddressExportPolicyComboChange);
+ connect(m_pManifestCheckbox, &QCheckBox::stateChanged,
+ this, &UIWizardExportAppPageFormat::sltHandleManifestCheckBoxChange);
+ connect(m_pIncludeISOsCheckbox, &QCheckBox::stateChanged,
+ this, &UIWizardExportAppPageFormat::sltHandleIncludeISOsCheckBoxChange);
+ connect(m_pProfileComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardExportAppPageFormat::sltHandleProfileComboChange);
+ connect(m_pExportModeButtonGroup, static_cast<void(QButtonGroup::*)(QAbstractButton*, bool)>(&QButtonGroup::buttonToggled),
+ this, &UIWizardExportAppPageFormat::sltHandleRadioButtonToggled);
+ connect(m_pProfileToolButton, &QIToolButton::clicked,
+ this, &UIWizardExportAppPageFormat::sltHandleProfileButtonClick);
+}
+
+UIWizardExportApp *UIWizardExportAppPageFormat::wizard() const
+{
+ return qobject_cast<UIWizardExportApp*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardExportAppPageFormat::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardExportApp::tr("Format settings"));
+
+ /* Translate objects: */
+ m_strDefaultApplianceName = UIWizardExportApp::tr("Appliance");
+ refreshFileSelectorName(m_strFileSelectorName, wizard()->machineNames(), m_strDefaultApplianceName, wizard()->isFormatCloudOne());
+ refreshFileSelectorPath(m_pFileSelector, m_strFileSelectorName, m_strFileSelectorExt, wizard()->isFormatCloudOne());
+
+ /* Translate format label: */
+ m_pLabelFormat->setText(UIWizardExportApp::
+ tr("<p>Please choose a format to export the virtual appliance to.</p>"
+ "<p>The <b>Open Virtualization Format</b> supports only <b>ovf</b> or <b>ova</b> extensions. "
+ "If you use the <b>ovf</b> extension, several files will be written separately. "
+ "If you use the <b>ova</b> extension, all the files will be combined into one Open "
+ "Virtualization Format archive.</p>"
+ "<p>The <b>Oracle Cloud Infrastructure</b> format supports exporting to remote cloud servers only. "
+ "Main virtual disk of each selected machine will be uploaded to remote server.</p>"));
+
+ /* Translate settings label: */
+ if (wizard()->isFormatCloudOne())
+ m_pLabelSettings->setText(UIWizardExportApp::
+ tr("Please choose one of cloud service profiles you have registered to export virtual "
+ "machines to. It will be used to establish network connection required to upload your "
+ "virtual machine files to a remote cloud facility."));
+ else
+ m_pLabelSettings->setText(UIWizardExportApp::
+ tr("Please choose a filename to export the virtual appliance to. Besides that you can "
+ "specify a certain amount of options which affects the size and content of resulting "
+ "archive."));
+
+ /* Translate file selector: */
+ m_pFileSelectorLabel->setText(UIWizardExportApp::tr("&File:"));
+ m_pFileSelector->setChooseButtonToolTip(UIWizardExportApp::tr("Choose a file to export the virtual appliance to..."));
+ m_pFileSelector->setFileDialogTitle(UIWizardExportApp::tr("Please choose a file to export the virtual appliance to"));
+
+ /* Translate hardcoded values of Format combo-box: */
+ m_pFormatComboBoxLabel->setText(UIWizardExportApp::tr("F&ormat:"));
+ m_pFormatComboBox->setItemText(0, UIWizardExportApp::tr("Open Virtualization Format 0.9"));
+ m_pFormatComboBox->setItemText(1, UIWizardExportApp::tr("Open Virtualization Format 1.0"));
+ m_pFormatComboBox->setItemText(2, UIWizardExportApp::tr("Open Virtualization Format 2.0"));
+ m_pFormatComboBox->setItemData(0, UIWizardExportApp::tr("Write in legacy OVF 0.9 format for compatibility "
+ "with other virtualization products."), Qt::ToolTipRole);
+ m_pFormatComboBox->setItemData(1, UIWizardExportApp::tr("Write in standard OVF 1.0 format."), Qt::ToolTipRole);
+ m_pFormatComboBox->setItemData(2, UIWizardExportApp::tr("Write in new OVF 2.0 format."), Qt::ToolTipRole);
+ /* Translate received values of Format combo-box.
+ * We are enumerating starting from 0 for simplicity: */
+ for (int i = 0; i < m_pFormatComboBox->count(); ++i)
+ if (isFormatCloudOne(m_pFormatComboBox, i))
+ {
+ m_pFormatComboBox->setItemText(i, m_pFormatComboBox->itemData(i, FormatData_Name).toString());
+ m_pFormatComboBox->setItemData(i, UIWizardExportApp::tr("Export to cloud service provider."), Qt::ToolTipRole);
+ }
+
+ /* Translate MAC address policy combo-box: */
+ m_pMACComboBoxLabel->setText(UIWizardExportApp::tr("MAC Address &Policy:"));
+ for (int i = 0; i < m_pMACComboBox->count(); ++i)
+ {
+ const MACAddressExportPolicy enmPolicy = m_pMACComboBox->itemData(i).value<MACAddressExportPolicy>();
+ switch (enmPolicy)
+ {
+ case MACAddressExportPolicy_KeepAllMACs:
+ {
+ m_pMACComboBox->setItemText(i, UIWizardExportApp::tr("Include all network adapter MAC addresses"));
+ m_pMACComboBox->setItemData(i, UIWizardExportApp::tr("Include all network adapter MAC addresses in exported "
+ "appliance archive."), Qt::ToolTipRole);
+ break;
+ }
+ case MACAddressExportPolicy_StripAllNonNATMACs:
+ {
+ m_pMACComboBox->setItemText(i, UIWizardExportApp::tr("Include only NAT network adapter MAC addresses"));
+ m_pMACComboBox->setItemData(i, UIWizardExportApp::tr("Include only NAT network adapter MAC addresses in exported "
+ "appliance archive."), Qt::ToolTipRole);
+ break;
+ }
+ case MACAddressExportPolicy_StripAllMACs:
+ {
+ m_pMACComboBox->setItemText(i, UIWizardExportApp::tr("Strip all network adapter MAC addresses"));
+ m_pMACComboBox->setItemData(i, UIWizardExportApp::tr("Strip all network adapter MAC addresses from exported "
+ "appliance archive."), Qt::ToolTipRole);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Translate addtional stuff: */
+ m_pAdditionalLabel->setText(UIWizardExportApp::tr("Additionally:"));
+ m_pManifestCheckbox->setToolTip(UIWizardExportApp::tr("Create a Manifest file for automatic data integrity checks on import."));
+ m_pManifestCheckbox->setText(UIWizardExportApp::tr("&Write Manifest file"));
+ m_pIncludeISOsCheckbox->setToolTip(UIWizardExportApp::tr("Include ISO image files into exported VM archive."));
+ m_pIncludeISOsCheckbox->setText(UIWizardExportApp::tr("&Include ISO image files"));
+
+ /* Translate profile stuff: */
+ m_pProfileLabel->setText(UIWizardExportApp::tr("&Profile:"));
+ m_pProfileToolButton->setToolTip(UIWizardExportApp::tr("Open Cloud Profile Manager..."));
+
+ /* Translate option label: */
+ m_pExportModeLabel->setText(UIWizardExportApp::tr("Machine Creation:"));
+ m_exportModeButtons.value(CloudExportMode_DoNotAsk)->setText(UIWizardExportApp::tr("Do not ask me about it, leave custom &image for future usage"));
+ m_exportModeButtons.value(CloudExportMode_AskThenExport)->setText(UIWizardExportApp::tr("Ask me about it &before exporting disk as custom image"));
+ m_exportModeButtons.value(CloudExportMode_ExportThenAsk)->setText(UIWizardExportApp::tr("Ask me about it &after exporting disk as custom image"));
+
+ /* Translate file selector's tooltip: */
+ if (m_pFileSelector)
+ m_pFileSelector->setToolTip(UIWizardExportApp::tr("Holds the path of the file selected for export."));
+
+ /* Adjust label widths: */
+ QList<QWidget*> labels;
+ labels << m_pFormatComboBoxLabel;
+ labels << m_pFileSelectorLabel;
+ labels << m_pMACComboBoxLabel;
+ labels << m_pAdditionalLabel;
+ labels << m_pProfileLabel;
+ labels << m_pExportModeLabel;
+ int iMaxWidth = 0;
+ foreach (QWidget *pLabel, labels)
+ iMaxWidth = qMax(iMaxWidth, pLabel->minimumSizeHint().width());
+ m_pFormatLayout->setColumnMinimumWidth(0, iMaxWidth);
+ m_pSettingsLayout1->setColumnMinimumWidth(0, iMaxWidth);
+ m_pSettingsLayout2->setColumnMinimumWidth(0, iMaxWidth);
+
+ /* Update tool-tips: */
+ updateFormatComboToolTip(m_pFormatComboBox);
+ updateMACAddressExportPolicyComboToolTip(m_pMACComboBox);
+}
+
+void UIWizardExportAppPageFormat::initializePage()
+{
+ /* Populate formats: */
+ populateFormats(m_pFormatComboBox, wizard()->notificationCenter(), m_fExportToOCIByDefault);
+ /* Populate MAC address policies: */
+ populateMACAddressPolicies(m_pMACComboBox);
+ /* Translate page: */
+ retranslateUi();
+
+ /* Choose initially focused widget: */
+ if (wizard()->isFormatCloudOne())
+ m_pProfileComboBox->setFocus();
+ else
+ m_pFileSelector->setFocus();
+
+ /* Fetch it, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltHandleFormatComboChange", Qt::QueuedConnection);
+}
+
+bool UIWizardExportAppPageFormat::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check whether there was cloud target selected: */
+ if (wizard()->isFormatCloudOne())
+ fResult = m_comCloudProfile.isNotNull();
+ else
+ fResult = UICommon::hasAllowedExtension(wizard()->path().toLower(), OVFFileExts);
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardExportAppPageFormat::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check whether there was cloud target selected: */
+ if (wizard()->isFormatCloudOne())
+ {
+ /* Update cloud stuff: */
+ updateCloudStuff();
+ /* Which is required to continue to the next page: */
+ fResult = wizard()->cloudAppliance().isNotNull()
+ && wizard()->cloudClient().isNotNull()
+ && wizard()->vsd().isNotNull()
+ && wizard()->vsdExportForm().isNotNull();
+ }
+ else
+ {
+ /* Update local stuff: */
+ updateLocalStuff();
+ /* Which is required to continue to the next page: */
+ fResult = wizard()->localAppliance().isNotNull();
+ }
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardExportAppPageFormat::sltHandleFormatComboChange()
+{
+ /* Update combo tool-tip: */
+ updateFormatComboToolTip(m_pFormatComboBox);
+
+ /* Update wizard fields: */
+ wizard()->setFormat(format(m_pFormatComboBox));
+ wizard()->setFormatCloudOne(isFormatCloudOne(m_pFormatComboBox));
+
+ /* Refresh settings widget state: */
+ refreshStackedWidget(m_pSettingsWidget1, wizard()->isFormatCloudOne());
+
+ /* Update export settings: */
+ refreshFileSelectorExtension(m_strFileSelectorExt, m_pFileSelector, wizard()->isFormatCloudOne());
+ refreshFileSelectorPath(m_pFileSelector, m_strFileSelectorName, m_strFileSelectorExt, wizard()->isFormatCloudOne());
+ refreshManifestCheckBoxAccess(m_pManifestCheckbox, wizard()->isFormatCloudOne());
+ refreshIncludeISOsCheckBoxAccess(m_pIncludeISOsCheckbox, wizard()->isFormatCloudOne());
+ refreshProfileCombo(m_pProfileComboBox, wizard()->notificationCenter(), wizard()->format(), wizard()->isFormatCloudOne());
+ refreshCloudExportMode(m_exportModeButtons, wizard()->isFormatCloudOne());
+
+ /* Update profile: */
+ sltHandleProfileComboChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageFormat::sltHandleFileSelectorChange()
+{
+ /* Skip empty paths: */
+ if (m_pFileSelector->path().isEmpty())
+ return;
+
+ m_strFileSelectorName = QFileInfo(m_pFileSelector->path()).completeBaseName();
+ wizard()->setPath(m_pFileSelector->path());
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageFormat::sltHandleMACAddressExportPolicyComboChange()
+{
+ updateMACAddressExportPolicyComboToolTip(m_pMACComboBox);
+ wizard()->setMACAddressExportPolicy(m_pMACComboBox->currentData().value<MACAddressExportPolicy>());
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageFormat::sltHandleManifestCheckBoxChange()
+{
+ wizard()->setManifestSelected(m_pManifestCheckbox->isChecked());
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageFormat::sltHandleIncludeISOsCheckBoxChange()
+{
+ wizard()->setIncludeISOsSelected(m_pIncludeISOsCheckbox->isChecked());
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageFormat::sltHandleProfileComboChange()
+{
+ /* Update wizard fields: */
+ wizard()->setProfileName(profileName(m_pProfileComboBox));
+
+ /* Update export settings: */
+ refreshCloudProfile(m_comCloudProfile,
+ wizard()->notificationCenter(),
+ wizard()->format(),
+ wizard()->profileName(),
+ wizard()->isFormatCloudOne());
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageFormat::sltHandleRadioButtonToggled(QAbstractButton *pButton, bool fToggled)
+{
+ /* Handle checked buttons only: */
+ if (!fToggled)
+ return;
+
+ /* Update cloud export mode field value: */
+ wizard()->setCloudExportMode(m_exportModeButtons.key(pButton));
+ emit completeChanged();
+}
+
+void UIWizardExportAppPageFormat::sltHandleProfileButtonClick()
+{
+ /* Open Cloud Profile Manager: */
+ if (gpManager)
+ gpManager->openCloudProfileManager();
+}
+
+void UIWizardExportAppPageFormat::updateLocalStuff()
+{
+ /* Create appliance: */
+ CAppliance comAppliance;
+ refreshLocalStuff(comAppliance, wizard(), wizard()->machineIDs(), wizard()->uri());
+ wizard()->setLocalAppliance(comAppliance);
+}
+
+void UIWizardExportAppPageFormat::updateCloudStuff()
+{
+ /* Create appliance, client, VSD and VSD export form: */
+ CAppliance comAppliance;
+ CCloudClient comClient;
+ CVirtualSystemDescription comDescription;
+ CVirtualSystemDescriptionForm comForm;
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ refreshCloudStuff(comAppliance,
+ comClient,
+ comDescription,
+ comForm,
+ wizard(),
+ m_comCloudProfile,
+ wizard()->machineIDs(),
+ wizard()->uri(),
+ wizard()->cloudExportMode());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ wizard()->setCloudAppliance(comAppliance);
+ wizard()->setCloudClient(comClient);
+ wizard()->setVsd(comDescription);
+ wizard()->setVsdExportForm(comForm);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageFormat.h b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageFormat.h
new file mode 100644
index 00000000..6e994576
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageFormat.h
@@ -0,0 +1,277 @@
+/* $Id: UIWizardExportAppPageFormat.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportAppPageFormat class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageFormat_h
+#define FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageFormat_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+#include "UIWizardExportApp.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CAppliance.h"
+#include "CCloudClient.h"
+#include "CCloudProfile.h"
+#include "CVirtualSystemDescription.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/* Forward declarations: */
+class QAbstractButton;
+class QButtonGroup;
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+class QStackedWidget;
+class QIComboBox;
+class QIRichTextLabel;
+class QIToolButton;
+class UIEmptyFilePathSelector;
+class UINotificationCenter;
+
+/** Format combo data fields. */
+enum
+{
+ FormatData_Name = Qt::UserRole + 1,
+ FormatData_ShortName = Qt::UserRole + 2,
+ FormatData_IsItCloudFormat = Qt::UserRole + 3
+};
+
+/** Profile combo data fields. */
+enum
+{
+ ProfileData_Name = Qt::UserRole + 1
+};
+
+/** Namespace for Format page of the Export Appliance wizard. */
+namespace UIWizardExportAppFormat
+{
+ /** Populates formats. */
+ void populateFormats(QIComboBox *pCombo, UINotificationCenter *pCenter, bool fExportToOCIByDefault);
+ /** Populates MAC address policies. */
+ void populateMACAddressPolicies(QIComboBox *pCombo);
+
+ /** Returns current format of @a pCombo specified. */
+ QString format(QIComboBox *pCombo);
+ /** Returns whether format under certain @a iIndex is cloud one. */
+ bool isFormatCloudOne(QIComboBox *pCombo, int iIndex = -1);
+
+ /** Refresh stacked widget. */
+ void refreshStackedWidget(QStackedWidget *pStackedWidget,
+ bool fIsFormatCloudOne);
+
+ /** Refresh file selector name. */
+ void refreshFileSelectorName(QString &strFileSelectorName,
+ const QStringList &machineNames,
+ const QString &strDefaultApplianceName,
+ bool fIsFormatCloudOne);
+ /** Refresh file selector extension. */
+ void refreshFileSelectorExtension(QString &strFileSelectorExt,
+ UIEmptyFilePathSelector *pFileSelector,
+ bool fIsFormatCloudOne);
+ /** Refresh file selector path. */
+ void refreshFileSelectorPath(UIEmptyFilePathSelector *pFileSelector,
+ const QString &strFileSelectorName,
+ const QString &strFileSelectorExt,
+ bool fIsFormatCloudOne);
+ /** Refresh Manifest check-box access. */
+ void refreshManifestCheckBoxAccess(QCheckBox *pCheckBox,
+ bool fIsFormatCloudOne);
+ /** Refresh Include ISOs check-box access. */
+ void refreshIncludeISOsCheckBoxAccess(QCheckBox *pCheckBox,
+ bool fIsFormatCloudOne);
+ /** Refresh local stuff. */
+ void refreshLocalStuff(CAppliance &comLocalAppliance,
+ UIWizardExportApp *pWizard,
+ const QList<QUuid> &machineIDs,
+ const QString &strUri);
+
+ /** Refresh profile combo. */
+ void refreshProfileCombo(QIComboBox *pCombo,
+ UINotificationCenter *pCenter,
+ const QString &strFormat,
+ bool fIsFormatCloudOne);
+ /** Refresh cloud profile. */
+ void refreshCloudProfile(CCloudProfile &comCloudProfile,
+ UINotificationCenter *pCenter,
+ const QString &strShortProviderName,
+ const QString &strProfileName,
+ bool fIsFormatCloudOne);
+ /** Refresh cloud export mode. */
+ void refreshCloudExportMode(const QMap<CloudExportMode, QAbstractButton*> &radios,
+ bool fIsFormatCloudOne);
+ /** Refresh cloud stuff. */
+ void refreshCloudStuff(CAppliance &comCloudAppliance,
+ CCloudClient &comCloudClient,
+ CVirtualSystemDescription &comCloudVsd,
+ CVirtualSystemDescriptionForm &comCloudVsdExportForm,
+ UIWizardExportApp *pWizard,
+ const CCloudProfile &comCloudProfile,
+ const QList<QUuid> &machineIDs,
+ const QString &strUri,
+ const CloudExportMode enmCloudExportMode);
+
+ /** Returns current profile name of @a pCombo specified. */
+ QString profileName(QIComboBox *pCombo);
+ /** Returns current cloud export mode chosen in @a radioButtons specified. */
+ CloudExportMode cloudExportMode(const QMap<CloudExportMode, QAbstractButton*> &radioButtons);
+
+ /** Updates format combo tool-tips. */
+ void updateFormatComboToolTip(QIComboBox *pCombo);
+ /** Updates MAC address export policy combo tool-tips. */
+ void updateMACAddressExportPolicyComboToolTip(QIComboBox *pCombo);
+}
+
+/** UINativeWizardPage extension for Format page of the Export Appliance wizard,
+ * based on UIWizardExportAppFormat namespace functions. */
+class UIWizardExportAppPageFormat : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Format page. */
+ UIWizardExportAppPageFormat(bool fExportToOCIByDefault);
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardExportApp *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Handles change in format combo-box. */
+ void sltHandleFormatComboChange();
+
+ /** Handles change in file-name selector. */
+ void sltHandleFileSelectorChange();
+
+ /** Handles change in MAC address export policy combo-box. */
+ void sltHandleMACAddressExportPolicyComboChange();
+
+ /** Handles change in manifest check-box. */
+ void sltHandleManifestCheckBoxChange();
+
+ /** Handles change in include ISOs check-box. */
+ void sltHandleIncludeISOsCheckBoxChange();
+
+ /** Handles change in profile combo-box. */
+ void sltHandleProfileComboChange();
+
+ /** Handles cloud export radio-button clicked. */
+ void sltHandleRadioButtonToggled(QAbstractButton *pButton, bool fToggled);
+
+ /** Handles profile tool-button click. */
+ void sltHandleProfileButtonClick();
+
+private:
+
+ /** Update local stuff. */
+ void updateLocalStuff();
+
+ /** Updates cloud stuff. */
+ void updateCloudStuff();
+
+ /** Holds whether default format should be Export to OCI. */
+ bool m_fExportToOCIByDefault;
+
+ /** Holds the default appliance name. */
+ QString m_strDefaultApplianceName;
+ /** Holds the file selector name. */
+ QString m_strFileSelectorName;
+ /** Holds the file selector ext. */
+ QString m_strFileSelectorExt;
+
+ /** Holds the Cloud Profile object instance. */
+ CCloudProfile m_comCloudProfile;
+
+
+ /** Holds the format label instance. */
+ QIRichTextLabel *m_pLabelFormat;
+ /** Holds the settings label instance. */
+ QIRichTextLabel *m_pLabelSettings;
+
+ /** Holds the format layout. */
+ QGridLayout *m_pFormatLayout;
+ /** Holds the format combo-box label instance. */
+ QLabel *m_pFormatComboBoxLabel;
+ /** Holds the format combo-box instance. */
+ QIComboBox *m_pFormatComboBox;
+
+ /** Holds the settings widget 1 instance. */
+ QStackedWidget *m_pSettingsWidget1;
+
+ /** Holds the settings layout 1. */
+ QGridLayout *m_pSettingsLayout1;
+ /** Holds the file selector label instance. */
+ QLabel *m_pFileSelectorLabel;
+ /** Holds the file selector instance. */
+ UIEmptyFilePathSelector *m_pFileSelector;
+ /** Holds the MAC address policy combo-box label instance. */
+ QLabel *m_pMACComboBoxLabel;
+ /** Holds the MAC address policy check-box instance. */
+ QIComboBox *m_pMACComboBox;
+ /** Holds the additional label instance. */
+ QLabel *m_pAdditionalLabel;
+ /** Holds the manifest check-box instance. */
+ QCheckBox *m_pManifestCheckbox;
+ /** Holds the include ISOs check-box instance. */
+ QCheckBox *m_pIncludeISOsCheckbox;
+
+ /** Holds the settings layout 2. */
+ QGridLayout *m_pSettingsLayout2;
+ /** Holds the profile label instance. */
+ QLabel *m_pProfileLabel;
+ /** Holds the profile combo-box instance. */
+ QIComboBox *m_pProfileComboBox;
+ /** Holds the profile management tool-button instance. */
+ QIToolButton *m_pProfileToolButton;
+
+ /** Holds the export mode label instance. */
+ QLabel *m_pExportModeLabel;
+ /** Holds the export mode button group instance. */
+ QButtonGroup *m_pExportModeButtonGroup;
+ /** Holds the map of export mode button instances. */
+ QMap<CloudExportMode, QAbstractButton*> m_exportModeButtons;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageFormat_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageSettings.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageSettings.cpp
new file mode 100644
index 00000000..60be1b2f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageSettings.cpp
@@ -0,0 +1,290 @@
+/* $Id: UIWizardExportAppPageSettings.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportAppPageSettings class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStackedWidget>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QILabelSeparator.h"
+#include "QIRichTextLabel.h"
+#include "UIApplianceExportEditorWidget.h"
+#include "UICommon.h"
+#include "UIFormEditorWidget.h"
+#include "UINotificationCenter.h"
+#include "UIWizardExportApp.h"
+#include "UIWizardExportAppPageSettings.h"
+
+/* COM includes: */
+#include "CMachine.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/* Namespaces: */
+using namespace UIWizardExportAppSettings;
+
+
+/*********************************************************************************************************************************
+* Class UIWizardExportAppSettings implementation. *
+*********************************************************************************************************************************/
+
+void UIWizardExportAppSettings::refreshStackedWidget(QStackedWidget *pStackedWidget,
+ bool fIsFormatCloudOne)
+{
+ /* Update stack appearance according to chosen format: */
+ pStackedWidget->setCurrentIndex((int)fIsFormatCloudOne);
+}
+
+void UIWizardExportAppSettings::refreshApplianceSettingsWidget(UIApplianceExportEditorWidget *pApplianceWidget,
+ const CAppliance &comAppliance,
+ bool fIsFormatCloudOne)
+{
+ /* Nothing for cloud case? */
+ if (fIsFormatCloudOne)
+ return;
+
+ /* Sanity check: */
+ AssertReturnVoid(comAppliance.isNotNull());
+
+ /* Make sure the settings widget get the new appliance: */
+ pApplianceWidget->setAppliance(comAppliance);
+}
+
+void UIWizardExportAppSettings::refreshFormPropertiesTable(UIFormEditorWidget *pFormEditor,
+ const CVirtualSystemDescriptionForm &comVsdForm,
+ bool fIsFormatCloudOne)
+{
+ /* Nothing for local case? */
+ if (!fIsFormatCloudOne)
+ return;
+
+ /* Sanity check: */
+ AssertReturnVoid(comVsdForm.isNotNull());
+
+ /* Make sure the properties table get the new description form: */
+ pFormEditor->setVirtualSystemDescriptionForm(comVsdForm);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWizardExportAppPageSettings implementation. *
+*********************************************************************************************************************************/
+
+UIWizardExportAppPageSettings::UIWizardExportAppPageSettings()
+ : m_pLabel(0)
+ , m_pSettingsWidget2(0)
+ , m_pApplianceWidget(0)
+ , m_pFormEditor(0)
+ , m_fLaunching(false)
+{
+ /* Create main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Create label: */
+ m_pLabel = new QIRichTextLabel(this);
+ if (m_pLabel)
+ pMainLayout->addWidget(m_pLabel);
+
+ /* Create settings widget 2: */
+ m_pSettingsWidget2 = new QStackedWidget(this);
+ if (m_pSettingsWidget2)
+ {
+ /* Create appliance widget container: */
+ QWidget *pApplianceWidgetCnt = new QWidget(this);
+ if (pApplianceWidgetCnt)
+ {
+ /* Create appliance widget layout: */
+ QVBoxLayout *pApplianceWidgetLayout = new QVBoxLayout(pApplianceWidgetCnt);
+ if (pApplianceWidgetLayout)
+ {
+ pApplianceWidgetLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create appliance widget: */
+ m_pApplianceWidget = new UIApplianceExportEditorWidget(pApplianceWidgetCnt);
+ if (m_pApplianceWidget)
+ {
+ m_pApplianceWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
+ pApplianceWidgetLayout->addWidget(m_pApplianceWidget);
+ }
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget2->addWidget(pApplianceWidgetCnt);
+ }
+
+ /* Create form editor container: */
+ QWidget *pFormEditorCnt = new QWidget(this);
+ if (pFormEditorCnt)
+ {
+ /* Create form editor layout: */
+ QVBoxLayout *pFormEditorLayout = new QVBoxLayout(pFormEditorCnt);
+ if (pFormEditorLayout)
+ {
+ pFormEditorLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Create form editor widget: */
+ m_pFormEditor = new UIFormEditorWidget(pFormEditorCnt);
+ if (m_pFormEditor)
+ pFormEditorLayout->addWidget(m_pFormEditor);
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget2->addWidget(pFormEditorCnt);
+ }
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pSettingsWidget2);
+ }
+ }
+}
+
+UIWizardExportApp *UIWizardExportAppPageSettings::wizard() const
+{
+ return qobject_cast<UIWizardExportApp*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardExportAppPageSettings::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardExportApp::tr("Appliance settings"));
+
+ /* Translate label: */
+ if (wizard()->isFormatCloudOne())
+ m_pLabel->setText(UIWizardExportApp::tr("This is the descriptive information which will be used to determine settings "
+ "for a cloud storage your VM being exported to. You can change it by double "
+ "clicking on individual lines."));
+ else
+ m_pLabel->setText(UIWizardExportApp::tr("This is the descriptive information which will be added to the virtual "
+ "appliance. You can change it by double clicking on individual lines."));
+}
+
+void UIWizardExportAppPageSettings::initializePage()
+{
+ /* Make sure form-editor knows notification-center: */
+ m_pFormEditor->setNotificationCenter(wizard()->notificationCenter());
+ /* Translate page: */
+ retranslateUi();
+
+ /* Refresh settings widget state: */
+ refreshStackedWidget(m_pSettingsWidget2, wizard()->isFormatCloudOne());
+ /* Refresh corresponding widgets: */
+ refreshApplianceSettingsWidget(m_pApplianceWidget, wizard()->localAppliance(), wizard()->isFormatCloudOne());
+ refreshFormPropertiesTable(m_pFormEditor, wizard()->vsdExportForm(), wizard()->isFormatCloudOne());
+
+ /* Choose initially focused widget: */
+ if (wizard()->isFormatCloudOne())
+ m_pFormEditor->setFocus();
+ else
+ m_pApplianceWidget->setFocus();
+}
+
+bool UIWizardExportAppPageSettings::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check whether there was cloud target selected: */
+ if (wizard()->isFormatCloudOne())
+ {
+ /* Make sure table has own data committed: */
+ m_pFormEditor->makeSureEditorDataCommitted();
+
+ /* Init VSD form: */
+ CVirtualSystemDescriptionForm comForm;
+ /* Check whether we have proper VSD form: */
+ if (!m_fLaunching)
+ {
+ /* We are going to upload image: */
+ comForm = wizard()->vsdExportForm();
+ fResult = comForm.isNotNull();
+ }
+ else
+ {
+ /* We are going to launch VM: */
+ comForm = wizard()->vsdLaunchForm();
+ fResult = comForm.isNotNull();
+ }
+ /* Give changed VSD back: */
+ if (fResult)
+ {
+ comForm.GetVirtualSystemDescription();
+ fResult = comForm.isOk();
+ if (!fResult)
+ UINotificationMessage::cannotAcquireVirtualSystemDescriptionFormParameter(comForm, wizard()->notificationCenter());
+ }
+
+ /* Final stage? */
+ if (fResult)
+ {
+ if (!m_fLaunching)
+ {
+ /* For modes other than AskThenExport, try to export appliance first: */
+ if (wizard()->cloudExportMode() != CloudExportMode_AskThenExport)
+ fResult = wizard()->exportAppliance();
+
+ /* For modes other than DoNotAsk, switch from uploading image to launching VM: */
+ if ( fResult
+ && wizard()->cloudExportMode() != CloudExportMode_DoNotAsk)
+ {
+ /* Invert flags: */
+ fResult = false;
+ m_fLaunching = true;
+
+ /* Disable wizard buttons: */
+ wizard()->disableButtons();
+
+ /* Refresh corresponding widgets: */
+ wizard()->createVsdLaunchForm();
+ refreshFormPropertiesTable(m_pFormEditor, wizard()->vsdLaunchForm(), wizard()->isFormatCloudOne());
+ }
+ }
+ else
+ {
+ /* For AskThenExport mode, try to export appliance in the end: */
+ if (wizard()->cloudExportMode() == CloudExportMode_AskThenExport)
+ fResult = wizard()->exportAppliance();
+
+ /* Try to create cloud VM: */
+ if (fResult)
+ fResult = wizard()->createCloudVM();
+ }
+ }
+ }
+ /* Otherwise if there was local target selected: */
+ else
+ {
+ /* Prepare export: */
+ m_pApplianceWidget->prepareExport();
+
+ /* Try to export appliance: */
+ if (fResult)
+ fResult = wizard()->exportAppliance();
+ }
+
+ /* Return result: */
+ return fResult;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageSettings.h b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageSettings.h
new file mode 100644
index 00000000..737f55f4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageSettings.h
@@ -0,0 +1,110 @@
+/* $Id: UIWizardExportAppPageSettings.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportAppPageSettings class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageSettings_h
+#define FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageSettings_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QList>
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CAppliance.h"
+
+/* Forward declarations: */
+class QStackedWidget;
+class QIRichTextLabel;
+class UIApplianceExportEditorWidget;
+class UIFormEditorWidget;
+class UIWizardExportApp;
+
+/** Namespace for Settings page of the Export Appliance wizard. */
+namespace UIWizardExportAppSettings
+{
+ /** Refresh stacked widget. */
+ void refreshStackedWidget(QStackedWidget *pStackedWidget,
+ bool fIsFormatCloudOne);
+
+ /** Refreshes appliance settings widget. */
+ void refreshApplianceSettingsWidget(UIApplianceExportEditorWidget *pApplianceWidget,
+ const CAppliance &comAppliance,
+ bool fIsFormatCloudOne);
+ /** Refreshes form properties table. */
+ void refreshFormPropertiesTable(UIFormEditorWidget *pFormEditor,
+ const CVirtualSystemDescriptionForm &comVsdForm,
+ bool fIsFormatCloudOne);
+}
+
+/** UINativeWizardPage extension for Settings page of the Export Appliance wizard,
+ * based on UIWizardExportAppSettings namespace functions. */
+class UIWizardExportAppPageSettings : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Settings page. */
+ UIWizardExportAppPageSettings();
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardExportApp *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private:
+
+ /** Holds the label instance. */
+ QIRichTextLabel *m_pLabel;
+
+ /** Holds the settings widget 2 instance. */
+ QStackedWidget *m_pSettingsWidget2;
+
+ /** Holds the appliance widget reference. */
+ UIApplianceExportEditorWidget *m_pApplianceWidget;
+ /** Holds the Form Editor widget instance. */
+ UIFormEditorWidget *m_pFormEditor;
+
+ /** Holds whether cloud exporting is at launching stage. */
+ bool m_fLaunching;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageSettings_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageVMs.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageVMs.cpp
new file mode 100644
index 00000000..3043cd26
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageVMs.cpp
@@ -0,0 +1,262 @@
+/* $Id: UIWizardExportAppPageVMs.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportAppPageVMs class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QListWidget>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "UICommon.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UIWizardExportApp.h"
+#include "UIWizardExportAppPageVMs.h"
+
+/* COM includes: */
+#include "CMachine.h"
+
+/* Namespaces: */
+using namespace UIWizardExportAppVMs;
+
+
+/** QListWidgetItem subclass for Export Appliance wizard VM list. */
+class UIVMListWidgetItem : public QListWidgetItem
+{
+public:
+
+ /** Constructs VM list item passing @a pixIcon, @a strText and @a pParent to the base-class.
+ * @param strUuid Brings the machine ID.
+ * @param fInSaveState Brings whether machine is in Saved state. */
+ UIVMListWidgetItem(QPixmap &pixIcon, QString &strText, QUuid uUuid, bool fInSaveState, QListWidget *pParent)
+ : QListWidgetItem(pixIcon, strText, pParent)
+ , m_uUuid(uUuid)
+ , m_fInSaveState(fInSaveState)
+ {}
+
+ /** Returns whether this item is less than @a other. */
+ bool operator<(const QListWidgetItem &other) const
+ {
+ return text().toLower() < other.text().toLower();
+ }
+
+ /** Returns the machine ID. */
+ QUuid uuid() const { return m_uUuid; }
+ /** Returns whether machine is in Saved state. */
+ bool isInSaveState() const { return m_fInSaveState; }
+
+private:
+
+ /** Holds the machine ID. */
+ QUuid m_uUuid;
+ /** Holds whether machine is in Saved state. */
+ bool m_fInSaveState;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIWizardExportAppVMs implementation. *
+*********************************************************************************************************************************/
+
+void UIWizardExportAppVMs::populateVMItems(QListWidget *pVMSelector, const QStringList &selectedVMNames)
+{
+ /* Add all VM items into VM selector: */
+ foreach (const CMachine &comMachine, uiCommon().virtualBox().GetMachines())
+ {
+ QPixmap pixIcon;
+ QString strName;
+ QUuid uUuid;
+ bool fInSaveState = false;
+ bool fEnabled = false;
+ const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
+ if (comMachine.GetAccessible())
+ {
+ pixIcon = generalIconPool().userMachinePixmapDefault(comMachine);
+ if (pixIcon.isNull())
+ pixIcon = generalIconPool().guestOSTypePixmapDefault(comMachine.GetOSTypeId());
+ strName = comMachine.GetName();
+ uUuid = comMachine.GetId();
+ fEnabled = comMachine.GetSessionState() == KSessionState_Unlocked;
+ fInSaveState = comMachine.GetState() == KMachineState_Saved || comMachine.GetState() == KMachineState_AbortedSaved;
+ }
+ else
+ {
+ QFileInfo fi(comMachine.GetSettingsFilePath());
+ strName = UICommon::hasAllowedExtension(fi.completeSuffix(), VBoxFileExts) ? fi.completeBaseName() : fi.fileName();
+ pixIcon = QPixmap(":/os_other.png").scaled(iIconMetric, iIconMetric, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ }
+ QListWidgetItem *pItem = new UIVMListWidgetItem(pixIcon, strName, uUuid, fInSaveState, pVMSelector);
+ if (!fEnabled)
+ pItem->setFlags(Qt::ItemFlags());
+ pVMSelector->addItem(pItem);
+ }
+ pVMSelector->sortItems();
+
+ /* Choose initially selected items (if passed): */
+ foreach (const QString &strSelectedVMName, selectedVMNames)
+ {
+ const QList<QListWidgetItem*> list = pVMSelector->findItems(strSelectedVMName, Qt::MatchExactly);
+ if (list.size() > 0)
+ {
+ if (pVMSelector->selectedItems().isEmpty())
+ pVMSelector->setCurrentItem(list.first());
+ else
+ list.first()->setSelected(true);
+ }
+ }
+}
+
+void UIWizardExportAppVMs::refreshSavedMachines(QStringList &savedMachines, QListWidget *pVMSelector)
+{
+ savedMachines.clear();
+ foreach (QListWidgetItem *pItem, pVMSelector->selectedItems())
+ if (static_cast<UIVMListWidgetItem*>(pItem)->isInSaveState())
+ savedMachines << pItem->text();
+}
+
+QStringList UIWizardExportAppVMs::machineNames(QListWidget *pVMSelector)
+{
+ /* Prepare list: */
+ QStringList names;
+ /* Iterate over all the selected items: */
+ foreach (QListWidgetItem *pItem, pVMSelector->selectedItems())
+ names << pItem->text();
+ /* Return result list: */
+ return names;
+}
+
+QList<QUuid> UIWizardExportAppVMs::machineIDs(QListWidget *pVMSelector)
+{
+ /* Prepare list: */
+ QList<QUuid> ids;
+ /* Iterate over all the selected items: */
+ foreach (QListWidgetItem *pItem, pVMSelector->selectedItems())
+ ids.append(static_cast<UIVMListWidgetItem*>(pItem)->uuid());
+ /* Return result list: */
+ return ids;
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWizardExportAppPageVMs implementation. *
+*********************************************************************************************************************************/
+
+UIWizardExportAppPageVMs::UIWizardExportAppPageVMs(const QStringList &selectedVMNames, bool fFastTravelToNextPage)
+ : m_selectedVMNames(selectedVMNames)
+ , m_fFastTravelToNextPage(fFastTravelToNextPage)
+ , m_pLabelMain(0)
+ , m_pVMSelector(0)
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare main label: */
+ m_pLabelMain = new QIRichTextLabel(this);
+ if (m_pLabelMain)
+ pLayoutMain->addWidget(m_pLabelMain);
+
+ /* Prepare VM selector: */
+ m_pVMSelector = new QListWidget(this);
+ if (m_pVMSelector)
+ {
+ m_pVMSelector->setAlternatingRowColors(true);
+ m_pVMSelector->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ pLayoutMain->addWidget(m_pVMSelector);
+ }
+ }
+
+ /* Setup connections: */
+ connect(m_pVMSelector, &QListWidget::itemSelectionChanged,
+ this, &UIWizardExportAppPageVMs::sltHandleVMItemSelectionChanged);
+}
+
+UIWizardExportApp *UIWizardExportAppPageVMs::wizard() const
+{
+ return qobject_cast<UIWizardExportApp*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardExportAppPageVMs::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardExportApp::tr("Virtual machines"));
+
+ /* Translate widgets: */
+ m_pLabelMain->setText(UIWizardExportApp::tr("<p>Please select the virtual machines that should be added to the appliance. "
+ "You can select more than one. Please note that these machines have to be "
+ "turned off before they can be exported.</p>"));
+}
+
+void UIWizardExportAppPageVMs::initializePage()
+{
+ /* Populate VM items: */
+ populateVMItems(m_pVMSelector, m_selectedVMNames);
+ /* Translate page: */
+ retranslateUi();
+
+ /* Now, when we are ready, we can
+ * fast traver to page 2 if requested: */
+ if (m_fFastTravelToNextPage)
+ wizard()->goForward();
+}
+
+bool UIWizardExportAppPageVMs::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* There should be at least one VM selected: */
+ fResult = wizard()->machineNames().size() > 0;
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardExportAppPageVMs::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Ask user about machines which are in Saved state currently: */
+ QStringList savedMachines;
+ refreshSavedMachines(savedMachines, m_pVMSelector);
+ if (!savedMachines.isEmpty())
+ fResult = msgCenter().confirmExportMachinesInSaveState(savedMachines, this);
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardExportAppPageVMs::sltHandleVMItemSelectionChanged()
+{
+ /* Update wizard fields: */
+ wizard()->setMachineNames(machineNames(m_pVMSelector));
+ wizard()->setMachineIDs(machineIDs(m_pVMSelector));
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageVMs.h b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageVMs.h
new file mode 100644
index 00000000..d4466146
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/exportappliance/UIWizardExportAppPageVMs.h
@@ -0,0 +1,109 @@
+/* $Id: UIWizardExportAppPageVMs.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardExportAppPageVMs class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageVMs_h
+#define FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageVMs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes */
+#include <QUuid>
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QListWidget;
+class QIRichTextLabel;
+class UIWizardExportApp;
+
+/** Namespace for VMs page of the Export Appliance wizard. */
+namespace UIWizardExportAppVMs
+{
+ /** Populates @a pVMSelector with items on the basis of passed @a selectedVMNames. */
+ void populateVMItems(QListWidget *pVMSelector, const QStringList &selectedVMNames);
+
+ /** Refresh a list of saved machines selected in @a pVMSelector. */
+ void refreshSavedMachines(QStringList &savedMachines, QListWidget *pVMSelector);
+
+ /** Returns a list of machine names selected in @a pVMSelector. */
+ QStringList machineNames(QListWidget *pVMSelector);
+ /** Returns a list of machine IDs selected in @a pVMSelector. */
+ QList<QUuid> machineIDs(QListWidget *pVMSelector);
+}
+
+/** UINativeWizardPage extension for VMs page of the Export Appliance wizard,
+ * based on UIWizardExportAppVMs namespace functions. */
+class UIWizardExportAppPageVMs : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs VMs page.
+ * @param selectedVMNames Brings the list of selected VM names.
+ * @param fFastTravelToNextPage Brings whether we should fast-travel to next page. */
+ UIWizardExportAppPageVMs(const QStringList &selectedVMNames, bool fFastTravelToNextPage);
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardExportApp *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Handles VM item selection change. */
+ void sltHandleVMItemSelectionChanged();
+
+private:
+
+ /** Holds the list of selected VM names. */
+ const QStringList m_selectedVMNames;
+ /** Holds whether we should fast travel to next page. */
+ bool m_fFastTravelToNextPage;
+
+ /** Holds the main label instance. */
+ QIRichTextLabel *m_pLabelMain;
+
+ /** Holds the VM selector instance. */
+ QListWidget *m_pVMSelector;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_exportappliance_UIWizardExportAppPageVMs_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIApplianceUnverifiedCertificateViewer.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIApplianceUnverifiedCertificateViewer.cpp
new file mode 100644
index 00000000..57dc5729
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIApplianceUnverifiedCertificateViewer.cpp
@@ -0,0 +1,128 @@
+/* $Id: UIApplianceUnverifiedCertificateViewer.cpp $ */
+/** @file
+ * VBox Qt GUI - UIApplianceUnverifiedCertificateViewer class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QLabel>
+#include <QPushButton>
+#include <QTextBrowser>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialogButtonBox.h"
+#include "UIApplianceUnverifiedCertificateViewer.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCertificate.h"
+
+
+UIApplianceUnverifiedCertificateViewer::UIApplianceUnverifiedCertificateViewer(QWidget *pParent,
+ const CCertificate &comCertificate)
+ : QIWithRetranslateUI<QIDialog>(pParent)
+ , m_comCertificate(comCertificate)
+ , m_pTextLabel(0)
+ , m_pTextBrowser(0)
+{
+ prepare();
+}
+
+void UIApplianceUnverifiedCertificateViewer::prepare()
+{
+ /* Create layout: */
+ QVBoxLayout *pLayout = new QVBoxLayout(this);
+ if (pLayout)
+ {
+ /* Create text-label: */
+ m_pTextLabel = new QLabel;
+ if (m_pTextLabel)
+ {
+ m_pTextLabel->setWordWrap(true);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pTextLabel);
+ }
+
+ /* Create text-browser: */
+ m_pTextBrowser = new QTextBrowser;
+ if (m_pTextBrowser)
+ {
+ m_pTextBrowser->setMinimumSize(500, 300);
+
+ /* Add into layout: */
+ pLayout->addWidget(m_pTextBrowser);
+ }
+
+ /* Create button-box: */
+ QIDialogButtonBox *pButtonBox = new QIDialogButtonBox;
+ if (pButtonBox)
+ {
+ pButtonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::No);
+ pButtonBox->button(QDialogButtonBox::Yes)->setShortcut(Qt::Key_Enter);
+ //pButtonBox->button(QDialogButtonBox::No)->setShortcut(Qt::Key_Esc);
+ connect(pButtonBox, &QIDialogButtonBox::accepted, this, &UIApplianceUnverifiedCertificateViewer::accept);
+ connect(pButtonBox, &QIDialogButtonBox::rejected, this, &UIApplianceUnverifiedCertificateViewer::reject);
+
+ /* Add into layout: */
+ pLayout->addWidget(pButtonBox);
+ }
+ }
+
+ /* Translate UI: */
+ retranslateUi();
+}
+
+void UIApplianceUnverifiedCertificateViewer::retranslateUi()
+{
+ /* Translate dialog title: */
+ setWindowTitle(tr("Unverifiable Certificate! Continue?"));
+
+ /* Translate text-label caption: */
+ if (m_comCertificate.GetSelfSigned())
+ m_pTextLabel->setText(tr("<b>The appliance is signed by an unverified self signed certificate issued by '%1'. "
+ "We recommend to only proceed with the importing if you are sure you should trust this entity.</b>"
+ ).arg(m_comCertificate.GetFriendlyName()));
+ else
+ m_pTextLabel->setText(tr("<b>The appliance is signed by an unverified certificate issued to '%1'. "
+ "We recommend to only proceed with the importing if you are sure you should trust this entity.</b>"
+ ).arg(m_comCertificate.GetFriendlyName()));
+
+ /* Translate text-browser contents: */
+ const QString strTemplateRow = tr("<tr><td>%1:</td><td>%2</td></tr>", "key: value");
+ QString strTableContent;
+ strTableContent += strTemplateRow.arg(tr("Issuer"), QStringList(m_comCertificate.GetIssuerName().toList()).join(", "));
+ strTableContent += strTemplateRow.arg(tr("Subject"), QStringList(m_comCertificate.GetSubjectName().toList()).join(", "));
+ strTableContent += strTemplateRow.arg(tr("Not Valid Before"), m_comCertificate.GetValidityPeriodNotBefore());
+ strTableContent += strTemplateRow.arg(tr("Not Valid After"), m_comCertificate.GetValidityPeriodNotAfter());
+ strTableContent += strTemplateRow.arg(tr("Serial Number"), m_comCertificate.GetSerialNumber());
+ strTableContent += strTemplateRow.arg(tr("Self-Signed"), m_comCertificate.GetSelfSigned() ? tr("True") : tr("False"));
+ strTableContent += strTemplateRow.arg(tr("Authority (CA)"), m_comCertificate.GetCertificateAuthority() ? tr("True") : tr("False"));
+// strTableContent += strTemplateRow.arg(tr("Trusted"), m_comCertificate.GetTrusted() ? tr("True") : tr("False"));
+ strTableContent += strTemplateRow.arg(tr("Public Algorithm"), tr("%1 (%2)", "value (clarification)").arg(m_comCertificate.GetPublicKeyAlgorithm()).arg(m_comCertificate.GetPublicKeyAlgorithmOID()));
+ strTableContent += strTemplateRow.arg(tr("Signature Algorithm"), tr("%1 (%2)", "value (clarification)").arg(m_comCertificate.GetSignatureAlgorithmName()).arg(m_comCertificate.GetSignatureAlgorithmOID()));
+ strTableContent += strTemplateRow.arg(tr("X.509 Version Number"), QString::number(m_comCertificate.GetVersionNumber()));
+ m_pTextBrowser->setText(QString("<table>%1</table>").arg(strTableContent));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIApplianceUnverifiedCertificateViewer.h b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIApplianceUnverifiedCertificateViewer.h
new file mode 100644
index 00000000..c291ac60
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIApplianceUnverifiedCertificateViewer.h
@@ -0,0 +1,73 @@
+/* $Id: UIApplianceUnverifiedCertificateViewer.h $ */
+/** @file
+ * VBox Qt GUI - UIApplianceUnverifiedCertificateViewer class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_importappliance_UIApplianceUnverifiedCertificateViewer_h
+#define FEQT_INCLUDED_SRC_wizards_importappliance_UIApplianceUnverifiedCertificateViewer_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QLabel;
+class QTextBrowser;
+class CCertificate;
+
+/** QIDialog extension
+ * asking for consent to continue with unverifiable certificate. */
+class UIApplianceUnverifiedCertificateViewer : public QIWithRetranslateUI<QIDialog>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs appliance @a comCertificate viewer for passed @a pParent. */
+ UIApplianceUnverifiedCertificateViewer(QWidget *pParent, const CCertificate &comCertificate);
+
+protected:
+
+ /** Prepares all. */
+ void prepare();
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+private:
+
+ /** Holds the certificate reference. */
+ const CCertificate &m_comCertificate;
+
+ /** Holds the text-label instance. */
+ QLabel *m_pTextLabel;
+ /** Holds the text-browser instance. */
+ QTextBrowser *m_pTextBrowser;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_importappliance_UIApplianceUnverifiedCertificateViewer_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportApp.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportApp.cpp
new file mode 100644
index 00000000..8077957e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportApp.cpp
@@ -0,0 +1,324 @@
+/* $Id: UIWizardImportApp.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardImportApp class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QPrintDialog>
+#include <QPrinter>
+#include <QPushButton>
+#include <QTextEdit>
+#include <QTextStream>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIDialog.h"
+#include "QIFileDialog.h"
+#include "UINotificationCenter.h"
+#include "UIWizardImportApp.h"
+#include "UIWizardImportAppPageExpert.h"
+#include "UIWizardImportAppPageSettings.h"
+#include "UIWizardImportAppPageSource.h"
+
+
+/* Import license viewer: */
+class UIImportLicenseViewer : public QIDialog
+{
+ Q_OBJECT;
+
+public:
+
+ /* Constructor: */
+ UIImportLicenseViewer(QWidget *pParent)
+ : QIDialog(pParent)
+ {
+ /* Create widgets: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ pMainLayout->setContentsMargins(12, 12, 12, 12);
+ m_pCaption = new QLabel(this);
+ m_pCaption->setWordWrap(true);
+ m_pLicenseText = new QTextEdit(this);
+ m_pLicenseText->setReadOnly(true);
+ m_pPrintButton = new QPushButton(this);
+ m_pSaveButton = new QPushButton(this);
+ m_pButtonBox = new QDialogButtonBox(QDialogButtonBox::No | QDialogButtonBox::Yes, Qt::Horizontal, this);
+ m_pButtonBox->addButton(m_pPrintButton, QDialogButtonBox::ActionRole);
+ m_pButtonBox->addButton(m_pSaveButton, QDialogButtonBox::ActionRole);
+ m_pButtonBox->button(QDialogButtonBox::Yes)->setDefault(true);
+ pMainLayout->addWidget(m_pCaption);
+ pMainLayout->addWidget(m_pLicenseText);
+ pMainLayout->addWidget(m_pButtonBox);
+
+ /* Translate */
+ retranslateUi();
+
+ /* Setup connections: */
+ connect(m_pButtonBox, &QDialogButtonBox::rejected, this, &UIImportLicenseViewer::reject);
+ connect(m_pButtonBox, &QDialogButtonBox::accepted, this, &UIImportLicenseViewer::accept);
+ connect(m_pPrintButton, &QPushButton::clicked, this, &UIImportLicenseViewer::sltPrint);
+ connect(m_pSaveButton, &QPushButton::clicked, this, &UIImportLicenseViewer::sltSave);
+ }
+
+ /* Content setter: */
+ void setContents(const QString &strName, const QString &strText)
+ {
+ m_strName = strName;
+ m_strText = strText;
+ retranslateUi();
+ }
+
+private slots:
+
+ /* Print stuff: */
+ void sltPrint()
+ {
+ QPrinter printer;
+ QPrintDialog pd(&printer, this);
+ if (pd.exec() == QDialog::Accepted)
+ m_pLicenseText->print(&printer);
+ }
+
+ /* Save stuff: */
+ void sltSave()
+ {
+ QString fileName = QIFileDialog::getSaveFileName(uiCommon().documentsPath(), tr("Text (*.txt)"),
+ this, tr("Save license to file..."));
+ if (!fileName.isEmpty())
+ {
+ QFile file(fileName);
+ if (file.open(QFile::WriteOnly | QFile::Truncate))
+ {
+ QTextStream out(&file);
+ out << m_pLicenseText->toPlainText();
+ }
+ }
+ }
+
+private:
+
+ /* Translation stuff: */
+ void retranslateUi()
+ {
+ /* Translate dialog: */
+ setWindowTitle(tr("Software License Agreement"));
+ /* Translate widgets: */
+ m_pCaption->setText(tr("<b>The virtual system \"%1\" requires that you agree to the terms and conditions "
+ "of the software license agreement shown below.</b><br /><br />Click <b>Agree</b> "
+ "to continue or click <b>Disagree</b> to cancel the import.").arg(m_strName));
+ m_pLicenseText->setText(m_strText);
+ m_pButtonBox->button(QDialogButtonBox::No)->setText(tr("&Disagree"));
+ m_pButtonBox->button(QDialogButtonBox::Yes)->setText(tr("&Agree"));
+ m_pPrintButton->setText(tr("&Print..."));
+ m_pSaveButton->setText(tr("&Save..."));
+ }
+
+ /* Variables: */
+ QLabel *m_pCaption;
+ QTextEdit *m_pLicenseText;
+ QDialogButtonBox *m_pButtonBox;
+ QPushButton *m_pPrintButton;
+ QPushButton *m_pSaveButton;
+ QString m_strName;
+ QString m_strText;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIWizardImportApp implementation. *
+*********************************************************************************************************************************/
+
+UIWizardImportApp::UIWizardImportApp(QWidget *pParent,
+ bool fImportFromOCIByDefault,
+ const QString &strFileName)
+ : UINativeWizard(pParent, WizardType_ImportAppliance, WizardMode_Auto, "ovf")
+ , m_fImportFromOCIByDefault(fImportFromOCIByDefault)
+ , m_strFileName(strFileName)
+ , m_fSourceCloudOne(false)
+ , m_enmMacAddressImportPolicy(MACAddressImportPolicy_MAX)
+ , m_fImportHDsAsVDI(false)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign watermark: */
+ setPixmapName(":/wizard_ovf_import.png");
+#else /* VBOX_WS_MAC */
+ /* Assign background image: */
+ setPixmapName(":/wizard_ovf_import_bg.png");
+#endif /* VBOX_WS_MAC */
+}
+
+bool UIWizardImportApp::setFile(const QString &strName)
+{
+ /* Clear object: */
+ m_comLocalAppliance = CAppliance();
+
+ if (strName.isEmpty())
+ return false;
+
+ /* Create an appliance object: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CAppliance comAppliance = comVBox.CreateAppliance();
+ if (!comVBox.isOk())
+ {
+ UINotificationMessage::cannotCreateAppliance(comVBox, notificationCenter());
+ return false;
+ }
+
+ /* Read the file to appliance: */
+ UINotificationProgressApplianceRead *pNotification = new UINotificationProgressApplianceRead(comAppliance, strName);
+ if (!handleNotificationProgressNow(pNotification))
+ return false;
+
+ /* Now we have to interpret that stuff: */
+ comAppliance.Interpret();
+ if (!comAppliance.isOk())
+ {
+ UINotificationMessage::cannotInterpretAppliance(comAppliance, notificationCenter());
+ return false;
+ }
+
+ /* Remember appliance: */
+ m_comLocalAppliance = comAppliance;
+
+ /* Success finally: */
+ return true;
+}
+
+bool UIWizardImportApp::importAppliance()
+{
+ /* Check whether there was cloud source selected: */
+ if (isSourceCloudOne())
+ {
+ /* Make sure cloud appliance valid: */
+ AssertReturn(m_comCloudAppliance.isNotNull(), false);
+
+ /* No options for cloud VMs for now: */
+ QVector<KImportOptions> options;
+
+ /* Import appliance: */
+ UINotificationProgressApplianceImport *pNotification = new UINotificationProgressApplianceImport(m_comCloudAppliance,
+ options);
+ gpNotificationCenter->append(pNotification);
+
+ /* Positive: */
+ return true;
+ }
+ else
+ {
+ /* Check if there are license agreements the user must confirm: */
+ QList < QPair <QString, QString> > licAgreements = licenseAgreements();
+ if (!licAgreements.isEmpty())
+ {
+ UIImportLicenseViewer ilv(this);
+ for (int i = 0; i < licAgreements.size(); ++i)
+ {
+ const QPair<QString, QString> &lic = licAgreements.at(i);
+ ilv.setContents(lic.first, lic.second);
+ if (ilv.exec() == QDialog::Rejected)
+ return false;
+ }
+ }
+
+ /* Gather import options: */
+ QVector<KImportOptions> options;
+ switch (macAddressImportPolicy())
+ {
+ case MACAddressImportPolicy_KeepAllMACs: options.append(KImportOptions_KeepAllMACs); break;
+ case MACAddressImportPolicy_KeepNATMACs: options.append(KImportOptions_KeepNATMACs); break;
+ default: break;
+ }
+ if (isImportHDsAsVDI())
+ options.append(KImportOptions_ImportToVDI);
+
+ /* Import appliance: */
+ UINotificationProgressApplianceImport *pNotification = new UINotificationProgressApplianceImport(m_comLocalAppliance,
+ options);
+ gpNotificationCenter->append(pNotification);
+
+ /* Positive: */
+ return true;
+ }
+}
+
+void UIWizardImportApp::populatePages()
+{
+ /* Create corresponding pages: */
+ switch (mode())
+ {
+ case WizardMode_Basic:
+ {
+ if (m_fImportFromOCIByDefault || m_strFileName.isEmpty())
+ addPage(new UIWizardImportAppPageSource(m_fImportFromOCIByDefault, m_strFileName));
+ addPage(new UIWizardImportAppPageSettings(m_strFileName));
+ break;
+ }
+ case WizardMode_Expert:
+ {
+ addPage(new UIWizardImportAppPageExpert(m_fImportFromOCIByDefault, m_strFileName));
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid mode: %d", mode()));
+ break;
+ }
+ }
+}
+
+void UIWizardImportApp::retranslateUi()
+{
+ /* Call to base-class: */
+ UINativeWizard::retranslateUi();
+
+ /* Translate wizard: */
+ setWindowTitle(tr("Import Virtual Appliance"));
+ /// @todo implement this?
+ //setButtonText(QWizard::FinishButton, tr("Import"));
+}
+
+QList<QPair<QString, QString> > UIWizardImportApp::licenseAgreements() const
+{
+ QList<QPair<QString, QString> > list;
+
+ foreach (CVirtualSystemDescription comVsd, m_comLocalAppliance.GetVirtualSystemDescriptions())
+ {
+ QVector<QString> strLicense;
+ strLicense = comVsd.GetValuesByType(KVirtualSystemDescriptionType_License,
+ KVirtualSystemDescriptionValueType_Original);
+ if (!strLicense.isEmpty())
+ {
+ QVector<QString> strName;
+ strName = comVsd.GetValuesByType(KVirtualSystemDescriptionType_Name,
+ KVirtualSystemDescriptionValueType_Auto);
+ list << QPair<QString, QString>(strName.first(), strLicense.first());
+ }
+ }
+
+ return list;
+}
+
+
+#include "UIWizardImportApp.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportApp.h b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportApp.h
new file mode 100644
index 00000000..6a12b482
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportApp.h
@@ -0,0 +1,166 @@
+/* $Id: UIWizardImportApp.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardImportApp class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportApp_h
+#define FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportApp_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CAppliance.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/** MAC address policies. */
+enum MACAddressImportPolicy
+{
+ MACAddressImportPolicy_KeepAllMACs,
+ MACAddressImportPolicy_KeepNATMACs,
+ MACAddressImportPolicy_StripAllMACs,
+ MACAddressImportPolicy_MAX
+};
+Q_DECLARE_METATYPE(MACAddressImportPolicy);
+
+/** Import Appliance wizard. */
+class UIWizardImportApp : public UINativeWizard
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Import Appliance wizard passing @a pParent to the base-class.
+ * @param fImportFromOCIByDefault Brings whether wizard should start with OCI target.
+ * @param strFileName Brings local file name to import OVF/OVA from. */
+ UIWizardImportApp(QWidget *pParent,
+ bool fImportFromOCIByDefault,
+ const QString &strFileName);
+
+ /** @name Common fields.
+ * @{ */
+ /** Returns whether source is cloud one. */
+ bool isSourceCloudOne() const { return m_fSourceCloudOne; }
+ /** Defines whether source is @a fCloudOne. */
+ void setSourceCloudOne(bool fCloudOne) { m_fSourceCloudOne = fCloudOne; }
+ /** @} */
+
+ /** @name Local import fields.
+ * @{ */
+ /** Returns local Appliance object. */
+ CAppliance localAppliance() const { return m_comLocalAppliance; }
+ /** Defines file @a strName. */
+ bool setFile(const QString &strName);
+
+ /** Returns MAC address import policy. */
+ MACAddressImportPolicy macAddressImportPolicy() const { return m_enmMacAddressImportPolicy; }
+ /** Defines MAC address import @a enmPolicy. */
+ void setMACAddressImportPolicy(MACAddressImportPolicy enmPolicy) { m_enmMacAddressImportPolicy = enmPolicy; }
+
+ /** Returns whether hard disks should be imported as VDIs. */
+ bool isImportHDsAsVDI() const { return m_fImportHDsAsVDI; }
+ /** Defines whether hard disks should be imported @a fAsVDI. */
+ void setImportHDsAsVDI(bool fAsVDI) { m_fImportHDsAsVDI = fAsVDI; }
+ /** @} */
+
+ /** @name Cloud import fields.
+ * @{ */
+ /** Returns cloud Appliance object. */
+ CAppliance cloudAppliance() const { return m_comCloudAppliance; }
+ /** Defines cloud @a comAppliance object. */
+ void setCloudAppliance(const CAppliance &comAppliance) { m_comCloudAppliance = comAppliance; }
+
+ /** Returns Virtual System Description import form object. */
+ CVirtualSystemDescriptionForm vsdImportForm() const { return m_comVsdImportForm; }
+ /** Defines Virtual System Description import @a comForm object. */
+ void setVsdImportForm(const CVirtualSystemDescriptionForm &comForm) { m_comVsdImportForm = comForm; }
+ /** @} */
+
+ /** @name Auxiliary stuff.
+ * @{ */
+ /** Imports appliance. */
+ bool importAppliance();
+ /** @} */
+
+protected:
+
+ /** @name Inherited stuff.
+ * @{ */
+ /** Populates pages. */
+ virtual void populatePages() /* override final */;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+ /** @} */
+
+private:
+
+ /** @name Auxiliary stuff.
+ * @{ */
+ /** Returns a list of license agreement pairs. */
+ QList<QPair<QString, QString> > licenseAgreements() const;
+ /** @} */
+
+ /** @name Arguments.
+ * @{ */
+ /** Holds whether default source should be Import from OCI. */
+ bool m_fImportFromOCIByDefault;
+ /** Handles the appliance file name. */
+ QString m_strFileName;
+ /** @} */
+
+ /** @name Common fields.
+ * @{ */
+ /** */
+ bool m_fSourceCloudOne;
+ /** @} */
+
+ /** @name Local import fields.
+ * @{ */
+ /** Holds the local appliance wrapper object. */
+ CAppliance m_comLocalAppliance;
+
+ /** Holds the MAC address import policy. */
+ MACAddressImportPolicy m_enmMacAddressImportPolicy;
+
+ /** Holds whether hard disks should be imported as VDIs. */
+ bool m_fImportHDsAsVDI;
+ /** @} */
+
+ /** @name Cloud import fields.
+ * @{ */
+ /** Holds the cloud appliance wrapper object. */
+ CAppliance m_comCloudAppliance;
+
+ /** Holds the Virtual System Description import form wrapper object. */
+ CVirtualSystemDescriptionForm m_comVsdImportForm;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportApp_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageExpert.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageExpert.cpp
new file mode 100644
index 00000000..d3de7e22
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageExpert.cpp
@@ -0,0 +1,648 @@
+/* $Id: UIWizardImportAppPageExpert.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardImportAppPageExpert class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QFileInfo>
+#include <QGroupBox>
+#include <QHeaderView>
+#include <QLabel>
+#include <QListWidget>
+#include <QPushButton>
+#include <QStackedWidget>
+#include <QTableWidget>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIToolButton.h"
+#include "UIApplianceImportEditorWidget.h"
+#include "UICommon.h"
+#include "UIEmptyFilePathSelector.h"
+#include "UIFilePathSelector.h"
+#include "UIFormEditorWidget.h"
+#include "UIIconPool.h"
+#include "UINotificationCenter.h"
+#include "UIToolBox.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualBoxManager.h"
+#include "UIWizardImportApp.h"
+#include "UIWizardImportAppPageExpert.h"
+#include "UIWizardImportAppPageSettings.h"
+#include "UIWizardImportAppPageSource.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+/* Namespaces: */
+using namespace UIWizardImportAppSettings;
+using namespace UIWizardImportAppSource;
+
+
+UIWizardImportAppPageExpert::UIWizardImportAppPageExpert(bool fImportFromOCIByDefault, const QString &strFileName)
+ : m_fImportFromOCIByDefault(fImportFromOCIByDefault)
+ , m_strFileName(strFileName)
+ , m_pToolBox(0)
+ , m_pSourceLayout(0)
+ , m_pSourceLabel(0)
+ , m_pSourceComboBox(0)
+ , m_pSettingsWidget1(0)
+ , m_pLocalContainerLayout(0)
+ , m_pFileSelector(0)
+ , m_pCloudContainerLayout(0)
+ , m_pProfileComboBox(0)
+ , m_pProfileToolButton(0)
+ , m_pProfileInstanceList(0)
+ , m_pSettingsWidget2(0)
+ , m_pApplianceWidget(0)
+ , m_pLabelImportFilePath(0)
+ , m_pEditorImportFilePath(0)
+ , m_pLabelMACImportPolicy(0)
+ , m_pComboMACImportPolicy(0)
+ , m_pLabelAdditionalOptions(0)
+ , m_pCheckboxImportHDsAsVDI(0)
+ , m_pFormEditor(0)
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Prepare tool-box: */
+ m_pToolBox = new UIToolBox(this);
+ if (m_pToolBox)
+ {
+ /* Prepare source widget: */
+ QWidget *pWidgetSource = new QWidget(m_pToolBox);
+ if (pWidgetSource)
+ {
+ /* Prepare source layout: */
+ m_pSourceLayout = new QGridLayout(pWidgetSource);
+ if (m_pSourceLayout)
+ {
+ m_pSourceLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare source combo: */
+ m_pSourceComboBox = new QIComboBox(pWidgetSource);
+ if (m_pSourceComboBox)
+ m_pSourceLayout->addWidget(m_pSourceComboBox, 0, 0);
+
+ /* Prepare settings widget 1: */
+ m_pSettingsWidget1 = new QStackedWidget(pWidgetSource);
+ if (m_pSettingsWidget1)
+ {
+ /* Prepare local container: */
+ QWidget *pContainerLocal = new QWidget(m_pSettingsWidget1);
+ if (pContainerLocal)
+ {
+ /* Prepare local widget layout: */
+ m_pLocalContainerLayout = new QGridLayout(pContainerLocal);
+ if (m_pLocalContainerLayout)
+ {
+ m_pLocalContainerLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLocalContainerLayout->setRowStretch(1, 1);
+
+ /* Prepare file-path selector: */
+ m_pFileSelector = new UIEmptyFilePathSelector(pContainerLocal);
+ if (m_pFileSelector)
+ {
+ m_pFileSelector->setHomeDir(uiCommon().documentsPath());
+ m_pFileSelector->setMode(UIEmptyFilePathSelector::Mode_File_Open);
+ m_pFileSelector->setButtonPosition(UIEmptyFilePathSelector::RightPosition);
+ m_pFileSelector->setEditable(true);
+ m_pLocalContainerLayout->addWidget(m_pFileSelector, 0, 0);
+ }
+ }
+
+ /* Add into widget: */
+ m_pSettingsWidget1->addWidget(pContainerLocal);
+ }
+
+ /* Prepare cloud container: */
+ QWidget *pContainerCloud = new QWidget(m_pSettingsWidget1);
+ if (pContainerCloud)
+ {
+ /* Prepare cloud container layout: */
+ m_pCloudContainerLayout = new QGridLayout(pContainerCloud);
+ if (m_pCloudContainerLayout)
+ {
+ m_pCloudContainerLayout->setContentsMargins(0, 0, 0, 0);
+ m_pCloudContainerLayout->setRowStretch(1, 1);
+
+ /* Prepare profile layout: */
+ QHBoxLayout *pLayoutProfile = new QHBoxLayout;
+ if (pLayoutProfile)
+ {
+ pLayoutProfile->setContentsMargins(0, 0, 0, 0);
+ pLayoutProfile->setSpacing(1);
+
+ /* Prepare profile combo-box: */
+ m_pProfileComboBox = new QIComboBox(pContainerCloud);
+ if (m_pProfileComboBox)
+ pLayoutProfile->addWidget(m_pProfileComboBox);
+
+ /* Prepare profile tool-button: */
+ m_pProfileToolButton = new QIToolButton(pContainerCloud);
+ if (m_pProfileToolButton)
+ {
+ m_pProfileToolButton->setIcon(UIIconPool::iconSet(":/cloud_profile_manager_16px.png",
+ ":/cloud_profile_manager_disabled_16px.png"));
+ pLayoutProfile->addWidget(m_pProfileToolButton);
+ }
+
+ /* Add into layout: */
+ m_pCloudContainerLayout->addLayout(pLayoutProfile, 0, 0);
+ }
+
+ /* Create profile instances table: */
+ m_pProfileInstanceList = new QListWidget(pContainerCloud);
+ if (m_pProfileInstanceList)
+ {
+ const QFontMetrics fm(m_pProfileInstanceList->font());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const int iFontWidth = fm.horizontalAdvance('x');
+#else
+ const int iFontWidth = fm.width('x');
+#endif
+ const int iTotalWidth = 50 * iFontWidth;
+ const int iFontHeight = fm.height();
+ const int iTotalHeight = 4 * iFontHeight;
+ m_pProfileInstanceList->setMinimumSize(QSize(iTotalWidth, iTotalHeight));
+// m_pProfileInstanceList->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ m_pProfileInstanceList->setAlternatingRowColors(true);
+ m_pCloudContainerLayout->addWidget(m_pProfileInstanceList, 1, 0);
+ }
+ }
+
+ /* Add into widget: */
+ m_pSettingsWidget1->addWidget(pContainerCloud);
+ }
+
+ /* Add into layout: */
+ m_pSourceLayout->addWidget(m_pSettingsWidget1, 1, 0);
+ }
+ }
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(0, pWidgetSource, QString());
+ }
+
+ /* Prepare settings widget 2: */
+ m_pSettingsWidget2 = new QStackedWidget(m_pToolBox);
+ if (m_pSettingsWidget2)
+ {
+ /* Prepare appliance container: */
+ QWidget *pContainerAppliance = new QWidget(m_pSettingsWidget2);
+ if (pContainerAppliance)
+ {
+ /* Prepare appliance layout: */
+ QGridLayout *pLayoutAppliance = new QGridLayout(pContainerAppliance);
+ if (pLayoutAppliance)
+ {
+ pLayoutAppliance->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare appliance widget: */
+ m_pApplianceWidget = new UIApplianceImportEditorWidget(pContainerAppliance);
+ if (m_pApplianceWidget)
+ {
+ m_pApplianceWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
+ pLayoutAppliance->addWidget(m_pApplianceWidget, 0, 0, 1, 3);
+ }
+
+ /* Prepare import path label: */
+ m_pLabelImportFilePath = new QLabel(pContainerAppliance);
+ if (m_pLabelImportFilePath)
+ {
+ m_pLabelImportFilePath->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutAppliance->addWidget(m_pLabelImportFilePath, 1, 0);
+ }
+ /* Prepare import path selector: */
+ m_pEditorImportFilePath = new UIFilePathSelector(pContainerAppliance);
+ if (m_pEditorImportFilePath)
+ {
+ m_pEditorImportFilePath->setResetEnabled(true);
+ m_pEditorImportFilePath->setDefaultPath(uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder());
+ m_pEditorImportFilePath->setPath(uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder());
+ m_pLabelImportFilePath->setBuddy(m_pEditorImportFilePath);
+ pLayoutAppliance->addWidget(m_pEditorImportFilePath, 1, 1, 1, 2);
+ }
+
+ /* Prepare MAC address policy label: */
+ m_pLabelMACImportPolicy = new QLabel(pContainerAppliance);
+ if (m_pLabelMACImportPolicy)
+ {
+ m_pLabelMACImportPolicy->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutAppliance->addWidget(m_pLabelMACImportPolicy, 2, 0);
+ }
+ /* Prepare MAC address policy combo: */
+ m_pComboMACImportPolicy = new QIComboBox(pContainerAppliance);
+ if (m_pComboMACImportPolicy)
+ {
+ m_pComboMACImportPolicy->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ m_pLabelMACImportPolicy->setBuddy(m_pComboMACImportPolicy);
+ pLayoutAppliance->addWidget(m_pComboMACImportPolicy, 2, 1, 1, 2);
+ }
+
+ /* Prepare additional options label: */
+ m_pLabelAdditionalOptions = new QLabel(pContainerAppliance);
+ if (m_pLabelAdditionalOptions)
+ {
+ m_pLabelAdditionalOptions->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutAppliance->addWidget(m_pLabelAdditionalOptions, 3, 0);
+ }
+ /* Prepare import HDs as VDIs checkbox: */
+ m_pCheckboxImportHDsAsVDI = new QCheckBox(pContainerAppliance);
+ {
+ m_pCheckboxImportHDsAsVDI->setCheckState(Qt::Checked);
+ pLayoutAppliance->addWidget(m_pCheckboxImportHDsAsVDI, 3, 1);
+ }
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget2->addWidget(pContainerAppliance);
+ }
+
+ /* Prepare form editor container: */
+ QWidget *pContainerFormEditor = new QWidget(m_pSettingsWidget2);
+ if (pContainerFormEditor)
+ {
+ /* Prepare form editor layout: */
+ QVBoxLayout *pLayoutFormEditor = new QVBoxLayout(pContainerFormEditor);
+ if (pLayoutFormEditor)
+ {
+ pLayoutFormEditor->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare form editor widget: */
+ m_pFormEditor = new UIFormEditorWidget(pContainerFormEditor);
+ if (m_pFormEditor)
+ pLayoutFormEditor->addWidget(m_pFormEditor);
+ }
+
+ /* Add into layout: */
+ m_pSettingsWidget2->addWidget(pContainerFormEditor);
+ }
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(1, m_pSettingsWidget2, QString());
+ }
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pToolBox);
+ }
+
+ /* Add stretch: */
+ pMainLayout->addStretch(1);
+ }
+
+ /* Setup connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UIWizardImportAppPageExpert::sltHandleSourceComboChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UIWizardImportAppPageExpert::sltHandleSourceComboChange);
+ connect(m_pSourceComboBox, &QIComboBox::activated,
+ this, &UIWizardImportAppPageExpert::sltHandleSourceComboChange);
+ connect(m_pFileSelector, &UIEmptyFilePathSelector::pathChanged,
+ this, &UIWizardImportAppPageExpert::sltHandleImportedFileSelectorChange);
+ connect(m_pProfileComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardImportAppPageExpert::sltHandleProfileComboChange);
+ connect(m_pProfileToolButton, &QIToolButton::clicked,
+ this, &UIWizardImportAppPageExpert::sltHandleProfileButtonClick);
+ connect(m_pProfileInstanceList, &QListWidget::currentRowChanged,
+ this, &UIWizardImportAppPageExpert::sltHandleInstanceListChange);
+ connect(m_pEditorImportFilePath, &UIFilePathSelector::pathChanged,
+ this, &UIWizardImportAppPageExpert::sltHandleImportPathEditorChange);
+ connect(m_pComboMACImportPolicy, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardImportAppPageExpert::sltHandleMACImportPolicyComboChange);
+ connect(m_pCheckboxImportHDsAsVDI, &QCheckBox::stateChanged,
+ this, &UIWizardImportAppPageExpert::sltHandleImportHDsAsVDICheckBoxChange);
+
+ /* Parse passed full group name if any: */
+ if ( m_fImportFromOCIByDefault
+ && !m_strFileName.isEmpty())
+ {
+ const QString strProviderShortName = m_strFileName.section('/', 1, 1);
+ const QString strProfileName = m_strFileName.section('/', 2, 2);
+ if (!strProviderShortName.isEmpty() && !strProfileName.isEmpty())
+ {
+ m_strSource = strProviderShortName;
+ m_strProfileName = strProfileName;
+ }
+ }
+}
+
+UIWizardImportApp *UIWizardImportAppPageExpert::wizard() const
+{
+ return qobject_cast<UIWizardImportApp*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardImportAppPageExpert::retranslateUi()
+{
+ /* Translate tool-box: */
+ if (m_pToolBox)
+ {
+ m_pToolBox->setPageTitle(0, UIWizardImportApp::tr("Source"));
+ m_pToolBox->setPageTitle(1, UIWizardImportApp::tr("Settings"));
+ }
+
+ /* Translate hardcoded values of Source combo-box: */
+ if (m_pSourceComboBox)
+ {
+ m_pSourceComboBox->setItemText(0, UIWizardImportApp::tr("Local File System"));
+ m_pSourceComboBox->setItemData(0, UIWizardImportApp::tr("Import from local file system."), Qt::ToolTipRole);
+
+ /* Translate received values of Source combo-box.
+ * We are enumerating starting from 0 for simplicity: */
+ for (int i = 0; i < m_pSourceComboBox->count(); ++i)
+ if (isSourceCloudOne(m_pSourceComboBox, i))
+ {
+ m_pSourceComboBox->setItemText(i, m_pSourceComboBox->itemData(i, SourceData_Name).toString());
+ m_pSourceComboBox->setItemData(i, UIWizardImportApp::tr("Import from cloud service provider."), Qt::ToolTipRole);
+ }
+ }
+
+ /* Translate file selector: */
+ if (m_pFileSelector)
+ {
+ m_pFileSelector->setChooseButtonToolTip(UIWizardImportApp::tr("Choose a virtual appliance file to import..."));
+ m_pFileSelector->setFileDialogTitle(UIWizardImportApp::tr("Please choose a virtual appliance file to import"));
+ m_pFileSelector->setFileFilters(UIWizardImportApp::tr("Open Virtualization Format (%1)").arg("*.ova *.ovf"));
+ }
+
+ /* Translate profile stuff: */
+ if (m_pProfileToolButton)
+ m_pProfileToolButton->setToolTip(UIWizardImportApp::tr("Open Cloud Profile Manager..."));
+
+ /* Translate path selector label: */
+ if (m_pLabelImportFilePath)
+ m_pLabelImportFilePath->setText(UIWizardImportApp::tr("&Machine Base Folder:"));
+
+ /* Translate MAC import policy label: */
+ if (m_pLabelMACImportPolicy)
+ m_pLabelMACImportPolicy->setText(UIWizardImportApp::tr("MAC Address &Policy:"));
+
+ /* Translate additional options label: */
+ if (m_pLabelAdditionalOptions)
+ m_pLabelAdditionalOptions->setText(UIWizardImportApp::tr("Additional Options:"));
+ /* Translate additional option check-box: */
+ if (m_pCheckboxImportHDsAsVDI)
+ {
+ m_pCheckboxImportHDsAsVDI->setText(UIWizardImportApp::tr("&Import hard drives as VDI"));
+ m_pCheckboxImportHDsAsVDI->setToolTip(UIWizardImportApp::tr("When checked, all the hard drives that belong to this "
+ "appliance will be imported in VDI format."));
+ }
+ /* Translate file selector's tooltip: */
+ if (m_pFileSelector)
+ m_pFileSelector->setToolTip(UIWizardImportApp::tr("Holds the path of the file selected for import."));
+
+ /* Translate separate stuff: */
+ retranslateMACImportPolicyCombo(m_pComboMACImportPolicy);
+
+ /* Update tool-tips: */
+ updateSourceComboToolTip(m_pSourceComboBox);
+ updateMACImportPolicyComboToolTip(m_pComboMACImportPolicy);
+}
+
+void UIWizardImportAppPageExpert::initializePage()
+{
+ /* Make sure form-editor knows notification-center: */
+ m_pFormEditor->setNotificationCenter(wizard()->notificationCenter());
+ /* Choose 1st tool to be chosen initially: */
+ m_pToolBox->setCurrentPage(0);
+ /* Populate sources: */
+ populateSources(m_pSourceComboBox,
+ wizard()->notificationCenter(),
+ m_fImportFromOCIByDefault,
+ m_strSource);
+ /* Translate page: */
+ retranslateUi();
+
+ /* Choose initially focused widget: */
+ if (wizard()->isSourceCloudOne())
+ m_pProfileInstanceList->setFocus();
+ else
+ m_pFileSelector->setFocus();
+
+ /* Fetch it, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltAsyncInit", Qt::QueuedConnection);
+}
+
+bool UIWizardImportAppPageExpert::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check whether there was cloud source selected: */
+ if (wizard()->isSourceCloudOne())
+ fResult = wizard()->cloudAppliance().isNotNull()
+ && wizard()->vsdImportForm().isNotNull();
+ else
+ fResult = wizard()->localAppliance().isNotNull();
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardImportAppPageExpert::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check whether there was cloud source selected: */
+ if (wizard()->isSourceCloudOne())
+ {
+ /* Make sure table has own data committed: */
+ m_pFormEditor->makeSureEditorDataCommitted();
+
+ /* Check whether we have proper VSD form: */
+ CVirtualSystemDescriptionForm comForm = wizard()->vsdImportForm();
+ fResult = comForm.isNotNull();
+
+ /* Give changed VSD back to appliance: */
+ if (fResult)
+ {
+ comForm.GetVirtualSystemDescription();
+ fResult = comForm.isOk();
+ if (!fResult)
+ UINotificationMessage::cannotAcquireVirtualSystemDescriptionFormParameter(comForm, wizard()->notificationCenter());
+ }
+ }
+ else
+ {
+ /* Make sure widget has own data committed: */
+ m_pApplianceWidget->prepareImport();
+ }
+
+ /* Try to import appliance: */
+ if (fResult)
+ fResult = wizard()->importAppliance();
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardImportAppPageExpert::sltAsyncInit()
+{
+ /* If we have file name passed for local OVA file: */
+ if ( !m_fImportFromOCIByDefault
+ && !m_strFileName.isEmpty())
+ m_pFileSelector->setPath(m_strFileName);
+
+ /* Refresh page widgets: */
+ sltHandleSourceComboChange();
+}
+
+void UIWizardImportAppPageExpert::sltHandleSourceComboChange()
+{
+ /* Update combo tool-tip: */
+ updateSourceComboToolTip(m_pSourceComboBox);
+
+ /* Update wizard fields: */
+ wizard()->setSourceCloudOne(isSourceCloudOne(m_pSourceComboBox));
+
+ /* Refresh page widgets: */
+ UIWizardImportAppSource::refreshStackedWidget(m_pSettingsWidget1,
+ wizard()->isSourceCloudOne());
+ UIWizardImportAppSettings::refreshStackedWidget(m_pSettingsWidget2,
+ wizard()->isSourceCloudOne());
+
+ // WORKAROUND:
+ // We want to free some vertical space from m_pSettingsWidget1:
+ const bool fCloudCase = wizard()->isSourceCloudOne();
+ m_pProfileComboBox->setVisible(fCloudCase);
+ m_pProfileToolButton->setVisible(fCloudCase);
+ m_pProfileInstanceList->setVisible(fCloudCase);
+
+ /* Refresh local stuff: */
+ sltHandleImportedFileSelectorChange();
+ refreshMACAddressImportPolicies(m_pComboMACImportPolicy,
+ wizard()->isSourceCloudOne());
+ sltHandleMACImportPolicyComboChange();
+ sltHandleImportHDsAsVDICheckBoxChange();
+
+ /* Refresh cloud stuff: */
+ refreshProfileCombo(m_pProfileComboBox,
+ wizard()->notificationCenter(),
+ source(m_pSourceComboBox),
+ m_strProfileName,
+ wizard()->isSourceCloudOne());
+ sltHandleProfileComboChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardImportAppPageExpert::sltHandleImportedFileSelectorChange()
+{
+ /* Update local stuff (only if something changed): */
+ if (m_pFileSelector->isModified())
+ {
+ /* Create local appliance: */
+ wizard()->setFile(path(m_pFileSelector));
+ m_pFileSelector->resetModified();
+ }
+
+ /* Refresh appliance widget: */
+ refreshApplianceWidget(m_pApplianceWidget,
+ wizard()->localAppliance(),
+ wizard()->isSourceCloudOne());
+
+ /* Update import path: */
+ sltHandleImportPathEditorChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardImportAppPageExpert::sltHandleProfileComboChange()
+{
+ /* Refresh profile instances: */
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ refreshCloudProfileInstances(m_pProfileInstanceList,
+ wizard()->notificationCenter(),
+ source(m_pSourceComboBox),
+ profileName(m_pProfileComboBox),
+ wizard()->isSourceCloudOne());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ sltHandleInstanceListChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardImportAppPageExpert::sltHandleProfileButtonClick()
+{
+ /* Open Cloud Profile Manager: */
+ if (gpManager)
+ gpManager->openCloudProfileManager();
+}
+
+void UIWizardImportAppPageExpert::sltHandleInstanceListChange()
+{
+ /* Create cloud appliance and VSD import form: */
+ CAppliance comAppliance;
+ CVirtualSystemDescriptionForm comForm;
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ refreshCloudStuff(comAppliance,
+ comForm,
+ wizard(),
+ machineId(m_pProfileInstanceList),
+ source(m_pSourceComboBox),
+ profileName(m_pProfileComboBox),
+ wizard()->isSourceCloudOne());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ wizard()->setCloudAppliance(comAppliance);
+ wizard()->setVsdImportForm(comForm);
+
+ /* Refresh form properties table: */
+ refreshFormPropertiesTable(m_pFormEditor,
+ wizard()->vsdImportForm(),
+ wizard()->isSourceCloudOne());
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardImportAppPageExpert::sltHandleImportPathEditorChange()
+{
+ AssertPtrReturnVoid(m_pApplianceWidget);
+ AssertPtrReturnVoid(m_pEditorImportFilePath);
+ m_pApplianceWidget->setVirtualSystemBaseFolder(m_pEditorImportFilePath->path());
+}
+
+void UIWizardImportAppPageExpert::sltHandleMACImportPolicyComboChange()
+{
+ /* Update combo tool-tip: */
+ updateMACImportPolicyComboToolTip(m_pComboMACImportPolicy);
+
+ /* Update wizard fields: */
+ wizard()->setMACAddressImportPolicy(macAddressImportPolicy(m_pComboMACImportPolicy));
+}
+
+void UIWizardImportAppPageExpert::sltHandleImportHDsAsVDICheckBoxChange()
+{
+ /* Update wizard fields: */
+ wizard()->setImportHDsAsVDI(isImportHDsAsVDI(m_pCheckboxImportHDsAsVDI));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageExpert.h b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageExpert.h
new file mode 100644
index 00000000..711153ef
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageExpert.h
@@ -0,0 +1,169 @@
+/* $Id: UIWizardImportAppPageExpert.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardImportAppPageExpert class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportAppPageExpert_h
+#define FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportAppPageExpert_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QLabel;
+class QListWidget;
+class QStackedWidget;
+class QIComboBox;
+class QIToolButton;
+class UIApplianceImportEditorWidget;
+class UIEmptyFilePathSelector;
+class UIFilePathSelector;
+class UIFormEditorWidget;
+class UIToolBox;
+class UIWizardImportApp;
+
+/** UINativeWizardPage extension for expert page of the Import Appliance wizard,
+ * based on UIWizardImportAppSource & UIWizardImportAppSettings namespace functions. */
+class UIWizardImportAppPageExpert : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs expert page.
+ * @param fImportFromOCIByDefault Brings whether we should propose import from OCI by default.
+ * @param strFileName Brings appliance file name. */
+ UIWizardImportAppPageExpert(bool fImportFromOCIByDefault, const QString &strFileName);
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardImportApp *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Inits page async way. */
+ void sltAsyncInit();
+
+ /** Handles source combo change. */
+ void sltHandleSourceComboChange();
+
+ /** Handles imported file selector change. */
+ void sltHandleImportedFileSelectorChange();
+ /** Handles profile combo change. */
+ void sltHandleProfileComboChange();
+ /** Handles profile tool-button click. */
+ void sltHandleProfileButtonClick();
+ /** Handles instance list change. */
+ void sltHandleInstanceListChange();
+
+ /** Handles import path editor change. */
+ void sltHandleImportPathEditorChange();
+ /** Handles MAC address import policy combo change. */
+ void sltHandleMACImportPolicyComboChange();
+ /** Handles import HDs as VDI check-box change. */
+ void sltHandleImportHDsAsVDICheckBoxChange();
+
+private:
+
+ /** Holds whether default source should be Import from OCI. */
+ bool m_fImportFromOCIByDefault;
+ /** Handles the appliance file name. */
+ QString m_strFileName;
+
+ /** Holds the cached source. */
+ QString m_strSource;
+ /** Holds the cached profile name. */
+ QString m_strProfileName;
+
+ /** Holds the tool-box instance. */
+ UIToolBox *m_pToolBox;
+
+ /** Holds the source layout instance. */
+ QGridLayout *m_pSourceLayout;
+ /** Holds the source type label instance. */
+ QLabel *m_pSourceLabel;
+ /** Holds the source type combo-box instance. */
+ QIComboBox *m_pSourceComboBox;
+
+ /** Holds the settings widget 1 instance. */
+ QStackedWidget *m_pSettingsWidget1;
+
+ /** Holds the local container layout instance. */
+ QGridLayout *m_pLocalContainerLayout;
+ /** Holds the file selector instance. */
+ UIEmptyFilePathSelector *m_pFileSelector;
+
+ /** Holds the cloud container layout instance. */
+ QGridLayout *m_pCloudContainerLayout;
+ /** Holds the profile combo-box instance. */
+ QIComboBox *m_pProfileComboBox;
+ /** Holds the profile management tool-button instance. */
+ QIToolButton *m_pProfileToolButton;
+ /** Holds the profile instance list instance. */
+ QListWidget *m_pProfileInstanceList;
+
+ /** Holds the settings widget 2 instance. */
+ QStackedWidget *m_pSettingsWidget2;
+
+ /** Holds the appliance widget instance. */
+ UIApplianceImportEditorWidget *m_pApplianceWidget;
+ /** Holds the import file-path label instance. */
+ QLabel *m_pLabelImportFilePath;
+ /** Holds the import file-path editor instance. */
+ UIFilePathSelector *m_pEditorImportFilePath;
+ /** Holds the MAC address label instance. */
+ QLabel *m_pLabelMACImportPolicy;
+ /** Holds the MAC address combo instance. */
+ QIComboBox *m_pComboMACImportPolicy;
+ /** Holds the additional options label instance. */
+ QLabel *m_pLabelAdditionalOptions;
+ /** Holds the 'import HDs as VDI' checkbox instance. */
+ QCheckBox *m_pCheckboxImportHDsAsVDI;
+ /** Holds the signature/certificate info label instance. */
+ QLabel *m_pCertLabel;
+
+ /** Holds the Form Editor widget instance. */
+ UIFormEditorWidget *m_pFormEditor;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportAppPageExpert_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSettings.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSettings.cpp
new file mode 100644
index 00000000..31829508
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSettings.cpp
@@ -0,0 +1,655 @@
+/* $Id: UIWizardImportAppPageSettings.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardImportAppPageSettings class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QLabel>
+#include <QStackedWidget>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIRichTextLabel.h"
+#include "UIApplianceImportEditorWidget.h"
+#include "UIApplianceUnverifiedCertificateViewer.h"
+#include "UICommon.h"
+#include "UIFilePathSelector.h"
+#include "UIFormEditorWidget.h"
+#include "UINotificationCenter.h"
+#include "UIWizardImportApp.h"
+#include "UIWizardImportAppPageSettings.h"
+
+/* COM includes: */
+#include "CAppliance.h"
+#include "CCertificate.h"
+#include "CSystemProperties.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/* Namespaces: */
+using namespace UIWizardImportAppSettings;
+
+
+/*********************************************************************************************************************************
+* Class UIWizardImportAppSettings implementation. *
+*********************************************************************************************************************************/
+
+void UIWizardImportAppSettings::refreshStackedWidget(QStackedWidget *pStackedWidget,
+ bool fIsSourceCloudOne)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pStackedWidget);
+
+ /* Update stack appearance according to chosen source: */
+ pStackedWidget->setCurrentIndex((int)fIsSourceCloudOne);
+}
+
+void UIWizardImportAppSettings::refreshApplianceWidget(UIApplianceImportEditorWidget *pApplianceWidget,
+ const CAppliance &comAppliance,
+ bool fIsSourceCloudOne)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pApplianceWidget);
+
+ /* If source is cloud one: */
+ if (fIsSourceCloudOne)
+ {
+ /* Clear form: */
+ pApplianceWidget->clear();
+ }
+ /* If source is local one: */
+ else
+ {
+ /* Make sure appliance widget get new appliance: */
+ if (comAppliance.isNotNull())
+ pApplianceWidget->setAppliance(comAppliance);
+ }
+}
+
+void UIWizardImportAppSettings::refreshMACAddressImportPolicies(QIComboBox *pCombo,
+ bool fIsSourceCloudOne)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* If source is cloud one: */
+ if (fIsSourceCloudOne)
+ {
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo: */
+ pCombo->clear();
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+ }
+ /* If source is local one: */
+ else
+ {
+ /* We need top-level parent as well: */
+ QWidget *pParent = pCombo->window();
+ AssertPtrReturnVoid(pParent);
+
+ /* Map known import options to known MAC address import policies: */
+ QMap<KImportOptions, MACAddressImportPolicy> knownOptions;
+ knownOptions[KImportOptions_KeepAllMACs] = MACAddressImportPolicy_KeepAllMACs;
+ knownOptions[KImportOptions_KeepNATMACs] = MACAddressImportPolicy_KeepNATMACs;
+ /* Load currently supported import options: */
+ const QVector<KImportOptions> supportedOptions =
+ uiCommon().virtualBox().GetSystemProperties().GetSupportedImportOptions();
+ /* Check which of supported options/policies are known: */
+ QList<MACAddressImportPolicy> supportedPolicies;
+ foreach (const KImportOptions &enmOption, supportedOptions)
+ if (knownOptions.contains(enmOption))
+ supportedPolicies << knownOptions.value(enmOption);
+ /* Remember current item data to be able to restore it: */
+ MACAddressImportPolicy enmOldData = MACAddressImportPolicy_MAX;
+ if (pCombo->currentIndex() != -1)
+ enmOldData = pCombo->currentData().value<MACAddressImportPolicy>();
+ else
+ {
+ if (supportedPolicies.contains(MACAddressImportPolicy_KeepNATMACs))
+ enmOldData = MACAddressImportPolicy_KeepNATMACs;
+ else
+ enmOldData = MACAddressImportPolicy_StripAllMACs;
+ }
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Cleanup combo: */
+ pCombo->clear();
+
+ /* Add supported policies first: */
+ foreach (const MACAddressImportPolicy &enmPolicy, supportedPolicies)
+ pCombo->addItem(QString(), QVariant::fromValue(enmPolicy));
+
+ /* Add hardcoded policy finally: */
+ pCombo->addItem(QString(), QVariant::fromValue(MACAddressImportPolicy_StripAllMACs));
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && enmOldData != MACAddressImportPolicy_MAX)
+ iNewIndex = pCombo->findData(QVariant::fromValue(enmOldData));
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+
+ /* Translate finally: */
+ retranslateMACImportPolicyCombo(pCombo);
+ }
+}
+
+void UIWizardImportAppSettings::refreshFormPropertiesTable(UIFormEditorWidget *pFormEditor,
+ const CVirtualSystemDescriptionForm &comForm,
+ bool fIsSourceCloudOne)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pFormEditor);
+
+ /* If source is cloud one: */
+ if (fIsSourceCloudOne)
+ {
+ /* Make sure properties table get new description form: */
+ if (comForm.isNotNull())
+ pFormEditor->setVirtualSystemDescriptionForm(comForm);
+ }
+ /* If source is local one: */
+ else
+ {
+ /* Clear form: */
+ pFormEditor->clearForm();
+ }
+}
+
+MACAddressImportPolicy UIWizardImportAppSettings::macAddressImportPolicy(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pCombo, MACAddressImportPolicy_MAX);
+
+ /* Give the actual result: */
+ return pCombo->currentData().value<MACAddressImportPolicy>();
+}
+
+bool UIWizardImportAppSettings::isImportHDsAsVDI(QCheckBox *pCheckBox)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pCheckBox, false);
+
+ /* Give the actual result: */
+ return pCheckBox->isChecked();
+}
+
+void UIWizardImportAppSettings::retranslateMACImportPolicyCombo(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* Enumerate combo items: */
+ for (int i = 0; i < pCombo->count(); ++i)
+ {
+ const MACAddressImportPolicy enmPolicy = pCombo->itemData(i).value<MACAddressImportPolicy>();
+ switch (enmPolicy)
+ {
+ case MACAddressImportPolicy_KeepAllMACs:
+ {
+ pCombo->setItemText(i, UIWizardImportApp::tr("Include all network adapter MAC addresses"));
+ pCombo->setItemData(i, UIWizardImportApp::tr("Include all network adapter MAC addresses "
+ "during importing."), Qt::ToolTipRole);
+ break;
+ }
+ case MACAddressImportPolicy_KeepNATMACs:
+ {
+ pCombo->setItemText(i, UIWizardImportApp::tr("Include only NAT network adapter MAC addresses"));
+ pCombo->setItemData(i, UIWizardImportApp::tr("Include only NAT network adapter MAC addresses "
+ "during importing."), Qt::ToolTipRole);
+ break;
+ }
+ case MACAddressImportPolicy_StripAllMACs:
+ {
+ pCombo->setItemText(i, UIWizardImportApp::tr("Generate new MAC addresses for all network adapters"));
+ pCombo->setItemData(i, UIWizardImportApp::tr("Generate new MAC addresses for all network adapters "
+ "during importing."), Qt::ToolTipRole);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void UIWizardImportAppSettings::retranslateCertificateLabel(QLabel *pLabel, const kCertText &enmType, const QString &strSignedBy)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pLabel);
+
+ /* Handle known types: */
+ switch (enmType)
+ {
+ case kCertText_Unsigned:
+ pLabel->setText(UIWizardImportApp::tr("Appliance is not signed"));
+ break;
+ case kCertText_IssuedTrusted:
+ pLabel->setText(UIWizardImportApp::tr("Appliance signed by %1 (trusted)").arg(strSignedBy));
+ break;
+ case kCertText_IssuedExpired:
+ pLabel->setText(UIWizardImportApp::tr("Appliance signed by %1 (expired!)").arg(strSignedBy));
+ break;
+ case kCertText_IssuedUnverified:
+ pLabel->setText(UIWizardImportApp::tr("Unverified signature by %1!").arg(strSignedBy));
+ break;
+ case kCertText_SelfSignedTrusted:
+ pLabel->setText(UIWizardImportApp::tr("Self signed by %1 (trusted)").arg(strSignedBy));
+ break;
+ case kCertText_SelfSignedExpired:
+ pLabel->setText(UIWizardImportApp::tr("Self signed by %1 (expired!)").arg(strSignedBy));
+ break;
+ case kCertText_SelfSignedUnverified:
+ pLabel->setText(UIWizardImportApp::tr("Unverified self signed signature by %1!").arg(strSignedBy));
+ break;
+ default:
+ AssertFailed();
+ RT_FALL_THRU();
+ case kCertText_Uninitialized:
+ pLabel->setText("<uninitialized page>");
+ break;
+ }
+}
+
+void UIWizardImportAppSettings::updateMACImportPolicyComboToolTip(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* Update tool-tip: */
+ const QString strCurrentToolTip = pCombo->currentData(Qt::ToolTipRole).toString();
+ pCombo->setToolTip(strCurrentToolTip);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWizardImportAppPageSettings implementation. *
+*********************************************************************************************************************************/
+
+UIWizardImportAppPageSettings::UIWizardImportAppPageSettings(const QString &strFileName)
+ : m_strFileName(strFileName)
+ , m_pLabelDescription(0)
+ , m_pSettingsWidget2(0)
+ , m_pApplianceWidget(0)
+ , m_pLabelImportFilePath(0)
+ , m_pEditorImportFilePath(0)
+ , m_pLabelMACImportPolicy(0)
+ , m_pComboMACImportPolicy(0)
+ , m_pLabelAdditionalOptions(0)
+ , m_pCheckboxImportHDsAsVDI(0)
+ , m_pCertLabel(0)
+ , m_enmCertText(kCertText_Uninitialized)
+ , m_pFormEditor(0)
+{
+ /* Create main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Prepare label: */
+ m_pLabelDescription = new QIRichTextLabel(this);
+ if (m_pLabelDescription)
+ pMainLayout->addWidget(m_pLabelDescription);
+
+ /* Prepare settings widget 2: */
+ m_pSettingsWidget2 = new QStackedWidget(this);
+ if (m_pSettingsWidget2)
+ {
+ /* Prepare appliance container: */
+ QWidget *pContainerAppliance = new QWidget(m_pSettingsWidget2);
+ if (pContainerAppliance)
+ {
+ /* Prepare appliance layout: */
+ QGridLayout *pLayoutAppliance = new QGridLayout(pContainerAppliance);
+ if (pLayoutAppliance)
+ {
+ pLayoutAppliance->setContentsMargins(0, 0, 0, 0);
+ pLayoutAppliance->setColumnStretch(0, 0);
+ pLayoutAppliance->setColumnStretch(1, 1);
+
+ /* Prepare appliance widget: */
+ m_pApplianceWidget = new UIApplianceImportEditorWidget(pContainerAppliance);
+ if (m_pApplianceWidget)
+ {
+ m_pApplianceWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
+ pLayoutAppliance->addWidget(m_pApplianceWidget, 0, 0, 1, 3);
+ }
+
+ /* Prepare path selector label: */
+ m_pLabelImportFilePath = new QLabel(pContainerAppliance);
+ if (m_pLabelImportFilePath)
+ {
+ m_pLabelImportFilePath->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutAppliance->addWidget(m_pLabelImportFilePath, 1, 0);
+ }
+ /* Prepare path selector editor: */
+ m_pEditorImportFilePath = new UIFilePathSelector(pContainerAppliance);
+ if (m_pEditorImportFilePath)
+ {
+ m_pEditorImportFilePath->setResetEnabled(true);
+ m_pEditorImportFilePath->setDefaultPath(uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder());
+ m_pEditorImportFilePath->setPath(uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder());
+ m_pLabelImportFilePath->setBuddy(m_pEditorImportFilePath);
+ pLayoutAppliance->addWidget(m_pEditorImportFilePath, 1, 1, 1, 2);
+ }
+
+ /* Prepare MAC address policy label: */
+ m_pLabelMACImportPolicy = new QLabel(pContainerAppliance);
+ if (m_pLabelMACImportPolicy)
+ {
+ m_pLabelMACImportPolicy->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutAppliance->addWidget(m_pLabelMACImportPolicy, 2, 0);
+ }
+ /* Prepare MAC address policy combo: */
+ m_pComboMACImportPolicy = new QIComboBox(pContainerAppliance);
+ if (m_pComboMACImportPolicy)
+ {
+ m_pComboMACImportPolicy->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ m_pLabelMACImportPolicy->setBuddy(m_pComboMACImportPolicy);
+ pLayoutAppliance->addWidget(m_pComboMACImportPolicy, 2, 1, 1, 2);
+ }
+
+ /* Prepare additional options label: */
+ m_pLabelAdditionalOptions = new QLabel(pContainerAppliance);
+ if (m_pLabelAdditionalOptions)
+ {
+ m_pLabelAdditionalOptions->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLayoutAppliance->addWidget(m_pLabelAdditionalOptions, 3, 0);
+ }
+ /* Prepare import HDs as VDIs checkbox: */
+ m_pCheckboxImportHDsAsVDI = new QCheckBox(pContainerAppliance);
+ {
+ m_pCheckboxImportHDsAsVDI->setCheckState(Qt::Checked);
+ pLayoutAppliance->addWidget(m_pCheckboxImportHDsAsVDI, 3, 1);
+ }
+
+ /* Prepare certificate label: */
+ m_pCertLabel = new QLabel(pContainerAppliance);
+ if (m_pCertLabel)
+ pLayoutAppliance->addWidget(m_pCertLabel, 4, 0, 1, 3);
+ }
+
+ /* Add into widget: */
+ m_pSettingsWidget2->addWidget(pContainerAppliance);
+ }
+
+ /* Prepare form editor container: */
+ QWidget *pContainerFormEditor = new QWidget(m_pSettingsWidget2);
+ if (pContainerFormEditor)
+ {
+ /* Prepare form editor layout: */
+ QVBoxLayout *pLayoutFormEditor = new QVBoxLayout(pContainerFormEditor);
+ if (pLayoutFormEditor)
+ {
+ pLayoutFormEditor->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare form editor widget: */
+ m_pFormEditor = new UIFormEditorWidget(pContainerFormEditor);
+ if (m_pFormEditor)
+ pLayoutFormEditor->addWidget(m_pFormEditor);
+ }
+
+ /* Add into widget: */
+ m_pSettingsWidget2->addWidget(pContainerFormEditor);
+ }
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pSettingsWidget2);
+ }
+ }
+
+ /* Setup connections: */
+ connect(m_pEditorImportFilePath, &UIFilePathSelector::pathChanged,
+ this, &UIWizardImportAppPageSettings::sltHandleImportPathEditorChange);
+ connect(m_pComboMACImportPolicy, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardImportAppPageSettings::sltHandleMACImportPolicyComboChange);
+ connect(m_pCheckboxImportHDsAsVDI, &QCheckBox::stateChanged,
+ this, &UIWizardImportAppPageSettings::sltHandleImportHDsAsVDICheckBoxChange);
+}
+
+UIWizardImportApp *UIWizardImportAppPageSettings::wizard() const
+{
+ return qobject_cast<UIWizardImportApp*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardImportAppPageSettings::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardImportApp::tr("Appliance settings"));
+
+ /* Translate description label: */
+ if (m_pLabelDescription)
+ {
+ if (wizard()->isSourceCloudOne())
+ m_pLabelDescription->setText(UIWizardImportApp::tr("These are the the suggested settings of the cloud VM import "
+ "procedure, they are influencing the resulting local VM instance. "
+ "You can change many of the properties shown by double-clicking "
+ "on the items and disable others using the check boxes below."));
+ else
+ m_pLabelDescription->setText(UIWizardImportApp::tr("These are the virtual machines contained in the appliance "
+ "and the suggested settings of the imported VirtualBox machines. "
+ "You can change many of the properties shown by double-clicking "
+ "on the items and disable others using the check boxes below."));
+ }
+
+ /* Translate path selector label: */
+ if (m_pLabelImportFilePath)
+ m_pLabelImportFilePath->setText(UIWizardImportApp::tr("&Machine Base Folder:"));
+
+ /* Translate MAC import policy label: */
+ if (m_pLabelMACImportPolicy)
+ m_pLabelMACImportPolicy->setText(UIWizardImportApp::tr("MAC Address &Policy:"));
+
+ /* Translate additional options label: */
+ if (m_pLabelAdditionalOptions)
+ m_pLabelAdditionalOptions->setText(UIWizardImportApp::tr("Additional Options:"));
+ /* Translate additional option check-box: */
+ if (m_pCheckboxImportHDsAsVDI)
+ {
+ m_pCheckboxImportHDsAsVDI->setText(UIWizardImportApp::tr("&Import hard drives as VDI"));
+ m_pCheckboxImportHDsAsVDI->setToolTip(UIWizardImportApp::tr("When checked, all the hard drives that belong to this "
+ "appliance will be imported in VDI format."));
+ }
+
+ /* Translate separate stuff: */
+ retranslateMACImportPolicyCombo(m_pComboMACImportPolicy);
+ retranslateCertificateLabel(m_pCertLabel, m_enmCertText, m_strSignedBy);
+
+ /* Update tool-tips: */
+ updateMACImportPolicyComboToolTip(m_pComboMACImportPolicy);
+}
+
+void UIWizardImportAppPageSettings::initializePage()
+{
+ /* Make sure form-editor knows notification-center: */
+ m_pFormEditor->setNotificationCenter(wizard()->notificationCenter());
+ /* Translate page: */
+ retranslateUi();
+
+ /* Choose initially focused widget: */
+ if (wizard()->isSourceCloudOne())
+ m_pFormEditor->setFocus();
+ else
+ m_pApplianceWidget->setFocus();
+
+ /* Fetch it, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltAsyncInit", Qt::QueuedConnection);
+}
+
+bool UIWizardImportAppPageSettings::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check whether there was cloud source selected: */
+ if (wizard()->isSourceCloudOne())
+ {
+ /* Make sure table has own data committed: */
+ m_pFormEditor->makeSureEditorDataCommitted();
+
+ /* Check whether we have proper VSD form: */
+ CVirtualSystemDescriptionForm comForm = wizard()->vsdImportForm();
+ fResult = comForm.isNotNull();
+
+ /* Give changed VSD back to appliance: */
+ if (fResult)
+ {
+ comForm.GetVirtualSystemDescription();
+ fResult = comForm.isOk();
+ if (!fResult)
+ UINotificationMessage::cannotAcquireVirtualSystemDescriptionFormParameter(comForm, wizard()->notificationCenter());
+ }
+ }
+ else
+ {
+ /* Make sure widget has own data committed: */
+ m_pApplianceWidget->prepareImport();
+ }
+
+ /* Try to import appliance: */
+ if (fResult)
+ fResult = wizard()->importAppliance();
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardImportAppPageSettings::sltAsyncInit()
+{
+ /* If we have local source and file name passed,
+ * check if specified file contains valid appliance: */
+ if ( !wizard()->isSourceCloudOne()
+ && !m_strFileName.isEmpty()
+ && !wizard()->setFile(m_strFileName))
+ {
+ wizard()->reject();
+ return;
+ }
+
+ /* Refresh page widgets: */
+ refreshStackedWidget(m_pSettingsWidget2,
+ wizard()->isSourceCloudOne());
+ refreshApplianceWidget(m_pApplianceWidget,
+ wizard()->localAppliance(),
+ wizard()->isSourceCloudOne());
+ refreshMACAddressImportPolicies(m_pComboMACImportPolicy,
+ wizard()->isSourceCloudOne());
+ refreshFormPropertiesTable(m_pFormEditor,
+ wizard()->vsdImportForm(),
+ wizard()->isSourceCloudOne());
+
+ /* Init wizard fields: */
+ sltHandleImportPathEditorChange();
+ sltHandleMACImportPolicyComboChange();
+ sltHandleImportHDsAsVDICheckBoxChange();
+
+ /* Handle appliance certificate: */
+ if (!wizard()->isSourceCloudOne())
+ handleApplianceCertificate();
+}
+
+void UIWizardImportAppPageSettings::sltHandleImportPathEditorChange()
+{
+ AssertPtrReturnVoid(m_pApplianceWidget);
+ AssertPtrReturnVoid(m_pEditorImportFilePath);
+ m_pApplianceWidget->setVirtualSystemBaseFolder(m_pEditorImportFilePath->path());
+}
+
+void UIWizardImportAppPageSettings::sltHandleMACImportPolicyComboChange()
+{
+ /* Update combo tool-tip: */
+ updateMACImportPolicyComboToolTip(m_pComboMACImportPolicy);
+
+ /* Update wizard fields: */
+ wizard()->setMACAddressImportPolicy(macAddressImportPolicy(m_pComboMACImportPolicy));
+}
+
+void UIWizardImportAppPageSettings::sltHandleImportHDsAsVDICheckBoxChange()
+{
+ /* Update wizard fields: */
+ wizard()->setImportHDsAsVDI(isImportHDsAsVDI(m_pCheckboxImportHDsAsVDI));
+}
+
+void UIWizardImportAppPageSettings::handleApplianceCertificate()
+{
+ /* Handle certificate: */
+ CAppliance comAppliance = wizard()->localAppliance();
+ CCertificate comCertificate = comAppliance.GetCertificate();
+ if (comCertificate.isNull())
+ m_enmCertText = kCertText_Unsigned;
+ else
+ {
+ /* Pick a 'signed-by' name: */
+ m_strSignedBy = comCertificate.GetFriendlyName();
+
+ /* If trusted, just select the right message: */
+ if (comCertificate.GetTrusted())
+ {
+ if (comCertificate.GetSelfSigned())
+ m_enmCertText = !comCertificate.GetExpired() ? kCertText_SelfSignedTrusted : kCertText_SelfSignedExpired;
+ else
+ m_enmCertText = !comCertificate.GetExpired() ? kCertText_IssuedTrusted : kCertText_IssuedExpired;
+ }
+ else
+ {
+ /* Not trusted! Must ask the user whether to continue in this case: */
+ m_enmCertText = comCertificate.GetSelfSigned() ? kCertText_SelfSignedUnverified : kCertText_IssuedUnverified;
+
+ /* Translate page early: */
+ retranslateUi();
+
+ /* Instantiate the dialog: */
+ QPointer<UIApplianceUnverifiedCertificateViewer> pDialog =
+ new UIApplianceUnverifiedCertificateViewer(this, comCertificate);
+
+ /* Show viewer in modal mode: */
+ const int iResultCode = pDialog->exec();
+ /* Leave if viewer destroyed prematurely: */
+ if (!pDialog)
+ return;
+ /* Delete viewer finally: */
+ delete pDialog;
+
+ /* Dismiss the entire import-appliance wizard if user rejects certificate: */
+ if (iResultCode == QDialog::Rejected)
+ wizard()->reject();
+ }
+ }
+
+ /* Translate certificate label: */
+ retranslateCertificateLabel(m_pCertLabel, m_enmCertText, m_strSignedBy);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSettings.h b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSettings.h
new file mode 100644
index 00000000..a095c10d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSettings.h
@@ -0,0 +1,174 @@
+/* $Id: UIWizardImportAppPageSettings.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardImportAppPageSettings class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportAppPageSettings_h
+#define FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportAppPageSettings_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+#include "UIWizardImportApp.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QIComboBox;
+class QLabel;
+class QStackedWidget;
+class QIRichTextLabel;
+class UIApplianceImportEditorWidget;
+class UIFilePathSelector;
+class UIFormEditorWidget;
+
+/** Certificate text template types. */
+enum kCertText
+{
+ kCertText_Uninitialized = 0,
+ kCertText_Unsigned,
+ kCertText_IssuedTrusted,
+ kCertText_IssuedExpired,
+ kCertText_IssuedUnverified,
+ kCertText_SelfSignedTrusted,
+ kCertText_SelfSignedExpired,
+ kCertText_SelfSignedUnverified
+};
+
+/** Namespace for Settings page of the Import Appliance wizard. */
+namespace UIWizardImportAppSettings
+{
+ /** Refresh stacked widget. */
+ void refreshStackedWidget(QStackedWidget *pStackedWidget,
+ bool fIsSourceCloudOne);
+
+ /** Refreshes appliance widget. */
+ void refreshApplianceWidget(UIApplianceImportEditorWidget *pApplianceWidget,
+ const CAppliance &comAppliance,
+ bool fIsSourceCloudOne);
+ /** Refresh MAC address import policies. */
+ void refreshMACAddressImportPolicies(QIComboBox *pCombo,
+ bool fIsSourceCloudOne);
+
+ /** Refreshes form properties table. */
+ void refreshFormPropertiesTable(UIFormEditorWidget *pFormEditor,
+ const CVirtualSystemDescriptionForm &comForm,
+ bool fIsSourceCloudOne);
+
+ /** Returns MAC address import policy. */
+ MACAddressImportPolicy macAddressImportPolicy(QIComboBox *pCombo);
+ /** Returns whether hard disks should be imported as VDIs. */
+ bool isImportHDsAsVDI(QCheckBox *pCheckBox);
+
+ /** Translates MAC import policy combo. */
+ void retranslateMACImportPolicyCombo(QIComboBox *pCombo);
+ /** Translates certificate label. */
+ void retranslateCertificateLabel(QLabel *pLabel, const kCertText &enmType, const QString &strSignedBy);
+
+ /** Updates MAC import policy combo tool-tips. */
+ void updateMACImportPolicyComboToolTip(QIComboBox *pCombo);
+}
+
+/** UINativeWizardPage extension for Settings page of the Import Appliance wizard,
+ * based on UIWizardImportAppSettings namespace functions. */
+class UIWizardImportAppPageSettings : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Settings page.
+ * @param strFileName Brings appliance file name. */
+ UIWizardImportAppPageSettings(const QString &strFileName);
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardImportApp *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Inits page async way. */
+ void sltAsyncInit();
+
+ /** Handles import path editor change. */
+ void sltHandleImportPathEditorChange();
+ /** Handles MAC address import policy combo change. */
+ void sltHandleMACImportPolicyComboChange();
+ /** Handles import HDs as VDI check-box change. */
+ void sltHandleImportHDsAsVDICheckBoxChange();
+
+private:
+
+ /** Handles appliance certificate. */
+ void handleApplianceCertificate();
+
+ /** Handles the appliance file name. */
+ QString m_strFileName;
+
+ /** Holds the description label instance. */
+ QIRichTextLabel *m_pLabelDescription;
+
+ /** Holds the settings widget 2 instance. */
+ QStackedWidget *m_pSettingsWidget2;
+
+ /** Holds the appliance widget instance. */
+ UIApplianceImportEditorWidget *m_pApplianceWidget;
+ /** Holds the import file-path label instance. */
+ QLabel *m_pLabelImportFilePath;
+ /** Holds the import file-path editor instance. */
+ UIFilePathSelector *m_pEditorImportFilePath;
+ /** Holds the MAC address label instance. */
+ QLabel *m_pLabelMACImportPolicy;
+ /** Holds the MAC address combo instance. */
+ QIComboBox *m_pComboMACImportPolicy;
+ /** Holds the additional options label instance. */
+ QLabel *m_pLabelAdditionalOptions;
+ /** Holds the 'import HDs as VDI' checkbox instance. */
+ QCheckBox *m_pCheckboxImportHDsAsVDI;
+ /** Holds the signature/certificate info label instance. */
+ QLabel *m_pCertLabel;
+
+ /** Holds the certificate text template type. */
+ kCertText m_enmCertText;
+
+ /** Holds the "signed by" information. */
+ QString m_strSignedBy;
+
+ /** Holds the Form Editor widget instance. */
+ UIFormEditorWidget *m_pFormEditor;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportAppPageSettings_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSource.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSource.cpp
new file mode 100644
index 00000000..20d116ed
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSource.cpp
@@ -0,0 +1,876 @@
+/* $Id: UIWizardImportAppPageSource.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardImportAppPageSource class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QListWidget>
+#include <QPushButton>
+#include <QStackedWidget>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIRichTextLabel.h"
+#include "QIToolButton.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIEmptyFilePathSelector.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UINotificationCenter.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualBoxManager.h"
+#include "UIWizardImportApp.h"
+#include "UIWizardImportAppPageSource.h"
+
+/* COM includes: */
+#include "CAppliance.h"
+#include "CVirtualSystemDescription.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/* Namespaces: */
+using namespace UIWizardImportAppSource;
+
+
+/*********************************************************************************************************************************
+* Class UIWizardImportAppSource implementation. *
+*********************************************************************************************************************************/
+
+void UIWizardImportAppSource::populateSources(QIComboBox *pCombo,
+ UINotificationCenter *pCenter,
+ bool fImportFromOCIByDefault,
+ const QString &strSource)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (pCombo->currentIndex() != -1)
+ strOldData = pCombo->currentData(SourceData_ShortName).toString();
+ else
+ {
+ /* Otherwise "OCI" or "local" should be the default one: */
+ if (fImportFromOCIByDefault)
+ strOldData = strSource.isEmpty() ? "OCI" : strSource;
+ else
+ strOldData = "local";
+ }
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Compose hardcoded sources list: */
+ QStringList sources;
+ sources << "local";
+ /* Add that list to combo: */
+ foreach (const QString &strShortName, sources)
+ {
+ /* Compose empty item, fill it's data: */
+ pCombo->addItem(QString());
+ pCombo->setItemData(pCombo->count() - 1, strShortName, SourceData_ShortName);
+ }
+
+ /* Iterate through existing providers: */
+ foreach (const CCloudProvider &comProvider, listCloudProviders(pCenter))
+ {
+ /* Skip if we have nothing to populate (file missing?): */
+ if (comProvider.isNull())
+ continue;
+ /* Acquire provider name: */
+ QString strProviderName;
+ if (!cloudProviderName(comProvider, strProviderName, pCenter))
+ continue;
+ /* Acquire provider short name: */
+ QString strProviderShortName;
+ if (!cloudProviderShortName(comProvider, strProviderShortName, pCenter))
+ continue;
+
+ /* Compose empty item, fill it's data: */
+ pCombo->addItem(QString());
+ pCombo->setItemData(pCombo->count() - 1, strProviderName, SourceData_Name);
+ pCombo->setItemData(pCombo->count() - 1, strProviderShortName, SourceData_ShortName);
+ pCombo->setItemData(pCombo->count() - 1, true, SourceData_IsItCloudFormat);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = pCombo->findData(strOldData, SourceData_ShortName);
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+}
+
+QString UIWizardImportAppSource::source(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pCombo, QString());
+
+ /* Give the actual result: */
+ return pCombo->currentData(SourceData_ShortName).toString();
+}
+
+bool UIWizardImportAppSource::isSourceCloudOne(QIComboBox *pCombo, int iIndex /* = -1 */)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pCombo, false);
+
+ /* Handle special case, -1 means "current one": */
+ if (iIndex == -1)
+ iIndex = pCombo->currentIndex();
+
+ /* Give the actual result: */
+ return pCombo->itemData(iIndex, SourceData_IsItCloudFormat).toBool();
+}
+
+void UIWizardImportAppSource::refreshStackedWidget(QStackedWidget *pStackedWidget,
+ bool fIsSourceCloudOne)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pStackedWidget);
+
+ /* Update stack appearance according to chosen source: */
+ pStackedWidget->setCurrentIndex((int)fIsSourceCloudOne);
+}
+
+void UIWizardImportAppSource::refreshProfileCombo(QIComboBox *pCombo,
+ UINotificationCenter *pCenter,
+ const QString &strSource,
+ const QString &strProfileName,
+ bool fIsSourceCloudOne)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* If source is cloud one: */
+ if (fIsSourceCloudOne)
+ {
+ /* Acquire provider: */
+ CCloudProvider comProvider = cloudProviderByShortName(strSource, pCenter);
+ AssertReturnVoid(comProvider.isNotNull());
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (pCombo->currentIndex() != -1)
+ strOldData = pCombo->currentData(ProfileData_Name).toString();
+ else if (!strProfileName.isEmpty())
+ strOldData = strProfileName;
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Acquire restricted accounts: */
+ const QStringList restrictedProfiles = gEDataManager->cloudProfileManagerRestrictions();
+
+ /* Iterate through existing profile names: */
+ QStringList allowedProfileNames;
+ QStringList restrictedProfileNames;
+ foreach (const CCloudProfile &comProfile, listCloudProfiles(comProvider, pCenter))
+ {
+ /* Skip if we have nothing to populate (wtf happened?): */
+ if (comProfile.isNull())
+ continue;
+ /* Acquire profile name: */
+ QString strCurrentProfileName;
+ if (!cloudProfileName(comProfile, strCurrentProfileName, pCenter))
+ continue;
+
+ /* Compose full profile name: */
+ const QString strFullProfileName = QString("/%1/%2").arg(strSource).arg(strCurrentProfileName);
+ /* Append to appropriate list: */
+ if (restrictedProfiles.contains(strFullProfileName))
+ restrictedProfileNames.append(strCurrentProfileName);
+ else
+ allowedProfileNames.append(strCurrentProfileName);
+ }
+
+ /* Add allowed items: */
+ foreach (const QString &strAllowedProfileName, allowedProfileNames)
+ {
+ /* Compose item, fill it's data: */
+ pCombo->addItem(strAllowedProfileName);
+ pCombo->setItemData(pCombo->count() - 1, strAllowedProfileName, ProfileData_Name);
+ QFont fnt = pCombo->font();
+ fnt.setBold(true);
+ pCombo->setItemData(pCombo->count() - 1, fnt, Qt::FontRole);
+ }
+ /* Add restricted items: */
+ foreach (const QString &strRestrictedProfileName, restrictedProfileNames)
+ {
+ /* Compose item, fill it's data: */
+ pCombo->addItem(strRestrictedProfileName);
+ pCombo->setItemData(pCombo->count() - 1, strRestrictedProfileName, ProfileData_Name);
+ QBrush brsh;
+ brsh.setColor(Qt::gray);
+ pCombo->setItemData(pCombo->count() - 1, brsh, Qt::ForegroundRole);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = pCombo->findData(strOldData, ProfileData_Name);
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+ }
+ /* If source is local one: */
+ else
+ {
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+ }
+}
+
+void UIWizardImportAppSource::refreshCloudProfileInstances(QListWidget *pListWidget,
+ UINotificationCenter *pCenter,
+ const QString &strSource,
+ const QString &strProfileName,
+ bool fIsSourceCloudOne)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pListWidget);
+
+ /* If source is cloud one: */
+ if (fIsSourceCloudOne)
+ {
+ /* We need top-level parent as well: */
+ QWidget *pParent = pListWidget->window();
+ AssertPtrReturnVoid(pParent);
+ /* Acquire client: */
+ CCloudClient comClient = cloudClientByName(strSource, strProfileName, pCenter);
+ AssertReturnVoid(comClient.isNotNull());
+
+ /* Block signals while updating: */
+ pListWidget->blockSignals(true);
+
+ /* Clear list initially: */
+ pListWidget->clear();
+
+ /* Gather instance names and ids: */
+ CStringArray comNames;
+ CStringArray comIDs;
+ if (listCloudInstances(comClient, comNames, comIDs, pCenter))
+ {
+ /* Push acquired names to list rows: */
+ const QVector<QString> names = comNames.GetValues();
+ const QVector<QString> ids = comIDs.GetValues();
+ for (int i = 0; i < names.size(); ++i)
+ {
+ /* Create list item: */
+ QListWidgetItem *pItem = new QListWidgetItem(names.at(i), pListWidget);
+ if (pItem)
+ {
+ pItem->setFlags(pItem->flags() & ~Qt::ItemIsEditable);
+ pItem->setData(Qt::UserRole, ids.at(i));
+ }
+ }
+ }
+
+ /* Choose the 1st one by default if possible: */
+ if (pListWidget->count())
+ pListWidget->setCurrentRow(0);
+
+ /* Unblock signals after update: */
+ pListWidget->blockSignals(false);
+ }
+ /* If source is local one: */
+ else
+ {
+ /* Block signals while updating: */
+ pListWidget->blockSignals(true);
+
+ /* Clear combo initially: */
+ pListWidget->clear();
+
+ /* Unblock signals after update: */
+ pListWidget->blockSignals(false);
+ }
+}
+
+void UIWizardImportAppSource::refreshCloudStuff(CAppliance &comCloudAppliance,
+ CVirtualSystemDescriptionForm &comCloudVsdImportForm,
+ UIWizardImportApp *pWizard,
+ const QString &strMachineId,
+ const QString &strSource,
+ const QString &strProfileName,
+ bool fIsSourceCloudOne)
+{
+ /* Clear stuff: */
+ comCloudAppliance = CAppliance();
+ comCloudVsdImportForm = CVirtualSystemDescriptionForm();
+
+ /* If source is NOT cloud one: */
+ if (!fIsSourceCloudOne)
+ return;
+
+ /* We need top-level parent as well: */
+ AssertPtrReturnVoid(pWizard);
+ /* Acquire client: */
+ CCloudClient comClient = cloudClientByName(strSource, strProfileName, pWizard->notificationCenter());
+ AssertReturnVoid(comClient.isNotNull());
+
+ /* Create appliance: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+ CAppliance comAppliance = comVBox.CreateAppliance();
+ if (!comVBox.isOk())
+ return UINotificationMessage::cannotCreateAppliance(comVBox, pWizard->notificationCenter());
+
+ /* Remember appliance: */
+ comCloudAppliance = comAppliance;
+
+ /* Read cloud instance info: */
+ UINotificationProgressApplianceRead *pNotification = new UINotificationProgressApplianceRead(comCloudAppliance,
+ QString("OCI://%1/%2").arg(strProfileName,
+ strMachineId));
+ if (!pWizard->handleNotificationProgressNow(pNotification))
+ return;
+
+ /* Acquire virtual system description: */
+ QVector<CVirtualSystemDescription> descriptions = comCloudAppliance.GetVirtualSystemDescriptions();
+ if (!comCloudAppliance.isOk())
+ return UINotificationMessage::cannotAcquireApplianceParameter(comCloudAppliance, pWizard->notificationCenter());
+
+ /* Make sure there is at least one virtual system description created: */
+ AssertReturnVoid(!descriptions.isEmpty());
+ CVirtualSystemDescription comDescription = descriptions.at(0);
+
+ /* Read Cloud Client description form: */
+ CVirtualSystemDescriptionForm comVsdImportForm;
+ bool fSuccess = importDescriptionForm(comClient, comDescription, comVsdImportForm, pWizard->notificationCenter());
+ if (!fSuccess)
+ return;
+
+ /* Remember form: */
+ comCloudVsdImportForm = comVsdImportForm;
+}
+
+QString UIWizardImportAppSource::path(UIEmptyFilePathSelector *pFileSelector)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pFileSelector, QString());
+
+ /* Give the actual result: */
+ return pFileSelector->path();
+}
+
+QString UIWizardImportAppSource::profileName(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pCombo, QString());
+
+ /* Give the actual result: */
+ return pCombo->currentData(ProfileData_Name).toString();
+}
+
+QString UIWizardImportAppSource::machineId(QListWidget *pListWidget)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pListWidget, QString());
+
+ /* Give the actual result: */
+ QListWidgetItem *pItem = pListWidget->currentItem();
+ return pItem ? pItem->data(Qt::UserRole).toString() : QString();
+}
+
+void UIWizardImportAppSource::updateSourceComboToolTip(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* Update tool-tip: */
+ const QString strCurrentToolTip = pCombo->currentData(Qt::ToolTipRole).toString();
+ pCombo->setToolTip(strCurrentToolTip);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWizardImportAppPageSource implementation. *
+*********************************************************************************************************************************/
+
+UIWizardImportAppPageSource::UIWizardImportAppPageSource(bool fImportFromOCIByDefault, const QString &strFileName)
+ : m_fImportFromOCIByDefault(fImportFromOCIByDefault)
+ , m_strFileName(strFileName)
+ , m_pLabelMain(0)
+ , m_pLabelDescription(0)
+ , m_pSourceLayout(0)
+ , m_pSourceLabel(0)
+ , m_pSourceComboBox(0)
+ , m_pSettingsWidget1(0)
+ , m_pLocalContainerLayout(0)
+ , m_pFileLabel(0)
+ , m_pFileSelector(0)
+ , m_pCloudContainerLayout(0)
+ , m_pProfileLabel(0)
+ , m_pProfileComboBox(0)
+ , m_pProfileToolButton(0)
+ , m_pProfileInstanceLabel(0)
+ , m_pProfileInstanceList(0)
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ if (pMainLayout)
+ {
+ /* Prepare main label: */
+ m_pLabelMain = new QIRichTextLabel(this);
+ if (m_pLabelMain)
+ pMainLayout->addWidget(m_pLabelMain);
+
+ /* Prepare source layout: */
+ m_pSourceLayout = new QGridLayout;
+ if (m_pSourceLayout)
+ {
+ m_pSourceLayout->setContentsMargins(0, 0, 0, 0);
+ m_pSourceLayout->setColumnStretch(0, 0);
+ m_pSourceLayout->setColumnStretch(1, 1);
+
+ /* Prepare source label: */
+ m_pSourceLabel = new QLabel(this);
+ if (m_pSourceLabel)
+ m_pSourceLayout->addWidget(m_pSourceLabel, 0, 0, Qt::AlignRight);
+ /* Prepare source selector: */
+ m_pSourceComboBox = new QIComboBox(this);
+ if (m_pSourceComboBox)
+ {
+ m_pSourceLabel->setBuddy(m_pSourceComboBox);
+ m_pSourceLayout->addWidget(m_pSourceComboBox, 0, 1);
+ }
+
+ /* Add into layout: */
+ pMainLayout->addLayout(m_pSourceLayout);
+ }
+
+ /* Prepare description label: */
+ m_pLabelDescription = new QIRichTextLabel(this);
+ if (m_pLabelDescription)
+ pMainLayout->addWidget(m_pLabelDescription);
+
+ /* Prepare settings widget: */
+ m_pSettingsWidget1 = new QStackedWidget(this);
+ if (m_pSettingsWidget1)
+ {
+ /* Prepare local container: */
+ QWidget *pContainerLocal = new QWidget(m_pSettingsWidget1);
+ if (pContainerLocal)
+ {
+ /* Prepare local container layout: */
+ m_pLocalContainerLayout = new QGridLayout(pContainerLocal);
+ if (m_pLocalContainerLayout)
+ {
+ m_pLocalContainerLayout->setContentsMargins(0, 0, 0, 0);
+ m_pLocalContainerLayout->setColumnStretch(0, 0);
+ m_pLocalContainerLayout->setColumnStretch(1, 1);
+ m_pLocalContainerLayout->setRowStretch(1, 1);
+
+ /* Prepare file label: */
+ m_pFileLabel = new QLabel(pContainerLocal);
+ if (m_pFileLabel)
+ m_pLocalContainerLayout->addWidget(m_pFileLabel, 0, 0, Qt::AlignRight);
+
+ /* Prepare file-path selector: */
+ m_pFileSelector = new UIEmptyFilePathSelector(pContainerLocal);
+ if (m_pFileSelector)
+ {
+ m_pFileLabel->setBuddy(m_pFileSelector);
+ m_pFileSelector->setHomeDir(uiCommon().documentsPath());
+ m_pFileSelector->setMode(UIEmptyFilePathSelector::Mode_File_Open);
+ m_pFileSelector->setButtonPosition(UIEmptyFilePathSelector::RightPosition);
+ m_pFileSelector->setEditable(true);
+ m_pLocalContainerLayout->addWidget(m_pFileSelector, 0, 1);
+ }
+ }
+
+ /* Add into widget: */
+ m_pSettingsWidget1->addWidget(pContainerLocal);
+ }
+
+ /* Prepare cloud container: */
+ QWidget *pContainerCloud = new QWidget(m_pSettingsWidget1);
+ if (pContainerCloud)
+ {
+ /* Prepare cloud container layout: */
+ m_pCloudContainerLayout = new QGridLayout(pContainerCloud);
+ if (m_pCloudContainerLayout)
+ {
+ m_pCloudContainerLayout->setContentsMargins(0, 0, 0, 0);
+ m_pCloudContainerLayout->setColumnStretch(0, 0);
+ m_pCloudContainerLayout->setColumnStretch(1, 1);
+ m_pCloudContainerLayout->setRowStretch(1, 0);
+ m_pCloudContainerLayout->setRowStretch(2, 1);
+
+ /* Prepare profile label: */
+ m_pProfileLabel = new QLabel(pContainerCloud);
+ if (m_pProfileLabel)
+ m_pCloudContainerLayout->addWidget(m_pProfileLabel, 0, 0, Qt::AlignRight);
+
+ /* Prepare sub-layout: */
+ QHBoxLayout *pSubLayout = new QHBoxLayout;
+ if (pSubLayout)
+ {
+ pSubLayout->setContentsMargins(0, 0, 0, 0);
+ pSubLayout->setSpacing(1);
+
+ /* Prepare profile combo-box: */
+ m_pProfileComboBox = new QIComboBox(pContainerCloud);
+ if (m_pProfileComboBox)
+ {
+ m_pProfileLabel->setBuddy(m_pProfileComboBox);
+ pSubLayout->addWidget(m_pProfileComboBox);
+ }
+
+ /* Prepare profile tool-button: */
+ m_pProfileToolButton = new QIToolButton(pContainerCloud);
+ if (m_pProfileToolButton)
+ {
+ m_pProfileToolButton->setIcon(UIIconPool::iconSet(":/cloud_profile_manager_16px.png",
+ ":/cloud_profile_manager_disabled_16px.png"));
+ pSubLayout->addWidget(m_pProfileToolButton);
+ }
+
+ /* Add into layout: */
+ m_pCloudContainerLayout->addLayout(pSubLayout, 0, 1);
+ }
+
+ /* Prepare profile instance label: */
+ m_pProfileInstanceLabel = new QLabel(pContainerCloud);
+ if (m_pProfileInstanceLabel)
+ m_pCloudContainerLayout->addWidget(m_pProfileInstanceLabel, 1, 0, Qt::AlignRight);
+
+ /* Prepare profile instances table: */
+ m_pProfileInstanceList = new QListWidget(pContainerCloud);
+ if (m_pProfileInstanceList)
+ {
+ m_pProfileInstanceLabel->setBuddy(m_pProfileInstanceLabel);
+ const QFontMetrics fm(m_pProfileInstanceList->font());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const int iFontWidth = fm.horizontalAdvance('x');
+#else
+ const int iFontWidth = fm.width('x');
+#endif
+ const int iTotalWidth = 50 * iFontWidth;
+ const int iFontHeight = fm.height();
+ const int iTotalHeight = 4 * iFontHeight;
+ m_pProfileInstanceList->setMinimumSize(QSize(iTotalWidth, iTotalHeight));
+// m_pProfileInstanceList->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ m_pProfileInstanceList->setAlternatingRowColors(true);
+ m_pCloudContainerLayout->addWidget(m_pProfileInstanceList, 1, 1, 2, 1);
+ }
+ }
+
+ /* Add into widget: */
+ m_pSettingsWidget1->addWidget(pContainerCloud);
+ }
+
+ /* Add into layout: */
+ pMainLayout->addWidget(m_pSettingsWidget1);
+ }
+ }
+
+ /* Setup connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UIWizardImportAppPageSource::sltHandleSourceComboChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UIWizardImportAppPageSource::sltHandleSourceComboChange);
+ connect(m_pSourceComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardImportAppPageSource::sltHandleSourceComboChange);
+ connect(m_pFileSelector, &UIEmptyFilePathSelector::pathChanged,
+ this, &UIWizardImportAppPageSource::completeChanged);
+ connect(m_pProfileComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardImportAppPageSource::sltHandleProfileComboChange);
+ connect(m_pProfileToolButton, &QIToolButton::clicked,
+ this, &UIWizardImportAppPageSource::sltHandleProfileButtonClick);
+ connect(m_pProfileInstanceList, &QListWidget::currentRowChanged,
+ this, &UIWizardImportAppPageSource::completeChanged);
+
+ /* Parse passed full group name if any: */
+ if ( m_fImportFromOCIByDefault
+ && !m_strFileName.isEmpty())
+ {
+ const QString strProviderShortName = m_strFileName.section('/', 1, 1);
+ const QString strProfileName = m_strFileName.section('/', 2, 2);
+ if (!strProviderShortName.isEmpty() && !strProfileName.isEmpty())
+ {
+ m_strSource = strProviderShortName;
+ m_strProfileName = strProfileName;
+ }
+ }
+}
+
+UIWizardImportApp *UIWizardImportAppPageSource::wizard() const
+{
+ return qobject_cast<UIWizardImportApp*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardImportAppPageSource::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardImportApp::tr("Appliance to import"));
+
+ /* Translate main label: */
+ if (m_pLabelMain)
+ m_pLabelMain->setText(UIWizardImportApp::tr("Please choose the source to import appliance from. This can be a "
+ "local file system to import OVF archive or one of known cloud "
+ "service providers to import cloud VM from."));
+
+ /* Translate description label: */
+ if (m_pLabelDescription)
+ {
+ if (wizard()->isSourceCloudOne())
+ m_pLabelDescription->setText(UIWizardImportApp::
+ tr("Please choose one of cloud service profiles you have registered to import virtual "
+ "machine from. Corresponding machines list will be updated. To continue, "
+ "select one of machines to import below."));
+ else
+ m_pLabelDescription->setText(UIWizardImportApp::
+ tr("Please choose a file to import the virtual appliance from. VirtualBox currently "
+ "supports importing appliances saved in the Open Virtualization Format (OVF). "
+ "To continue, select the file to import below."));
+ }
+
+ if (m_pFileSelector)
+ m_pFileSelector->setToolTip(UIWizardImportApp::tr("Holds the path of the file selected for import."));
+
+ /* Translate source label: */
+ if (m_pSourceLabel)
+ m_pSourceLabel->setText(UIWizardImportApp::tr("&Source:"));
+ if (m_pSourceComboBox)
+ {
+ /* Translate hardcoded values of Source combo-box: */
+ m_pSourceComboBox->setItemText(0, UIWizardImportApp::tr("Local File System"));
+ m_pSourceComboBox->setItemData(0, UIWizardImportApp::tr("Import from local file system."), Qt::ToolTipRole);
+
+ /* Translate received values of Source combo-box.
+ * We are enumerating starting from 0 for simplicity: */
+ for (int i = 0; i < m_pSourceComboBox->count(); ++i)
+ if (isSourceCloudOne(m_pSourceComboBox, i))
+ {
+ m_pSourceComboBox->setItemText(i, m_pSourceComboBox->itemData(i, SourceData_Name).toString());
+ m_pSourceComboBox->setItemData(i, UIWizardImportApp::tr("Import from cloud service provider."), Qt::ToolTipRole);
+ }
+ }
+
+ /* Translate local stuff: */
+ if (m_pFileLabel)
+ m_pFileLabel->setText(UIWizardImportApp::tr("&File:"));
+ if (m_pFileSelector)
+ {
+ m_pFileSelector->setChooseButtonToolTip(UIWizardImportApp::tr("Choose a virtual appliance file to import..."));
+ m_pFileSelector->setFileDialogTitle(UIWizardImportApp::tr("Please choose a virtual appliance file to import"));
+ m_pFileSelector->setFileFilters(UIWizardImportApp::tr("Open Virtualization Format (%1)").arg("*.ova *.ovf"));
+ }
+
+ /* Translate profile stuff: */
+ if (m_pProfileLabel)
+ m_pProfileLabel->setText(UIWizardImportApp::tr("&Profile:"));
+ if (m_pProfileToolButton)
+ m_pProfileToolButton->setToolTip(UIWizardImportApp::tr("Open Cloud Profile Manager..."));
+ if (m_pProfileInstanceLabel)
+ m_pProfileInstanceLabel->setText(UIWizardImportApp::tr("&Machines:"));
+
+ /* Adjust label widths: */
+ QList<QWidget*> labels;
+ if (m_pFileLabel)
+ labels << m_pFileLabel;
+ if (m_pSourceLabel)
+ labels << m_pSourceLabel;
+ if (m_pProfileLabel)
+ labels << m_pProfileLabel;
+ if (m_pProfileInstanceLabel)
+ labels << m_pProfileInstanceLabel;
+ int iMaxWidth = 0;
+ foreach (QWidget *pLabel, labels)
+ iMaxWidth = qMax(iMaxWidth, pLabel->minimumSizeHint().width());
+ if (m_pSourceLayout)
+ m_pSourceLayout->setColumnMinimumWidth(0, iMaxWidth);
+ if (m_pLocalContainerLayout)
+ {
+ m_pLocalContainerLayout->setColumnMinimumWidth(0, iMaxWidth);
+ m_pCloudContainerLayout->setColumnMinimumWidth(0, iMaxWidth);
+ }
+
+ /* Update tool-tips: */
+ updateSourceComboToolTip(m_pSourceComboBox);
+}
+
+void UIWizardImportAppPageSource::initializePage()
+{
+ /* Populate sources: */
+ populateSources(m_pSourceComboBox,
+ wizard()->notificationCenter(),
+ m_fImportFromOCIByDefault,
+ m_strSource);
+ /* Translate page: */
+ retranslateUi();
+
+ /* Choose initially focused widget: */
+ if (wizard()->isSourceCloudOne())
+ m_pProfileInstanceList->setFocus();
+ else
+ m_pFileSelector->setFocus();
+
+ /* Fetch it, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltHandleSourceComboChange", Qt::QueuedConnection);
+}
+
+bool UIWizardImportAppPageSource::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check whether there was cloud source selected: */
+ if (wizard()->isSourceCloudOne())
+ fResult = !machineId(m_pProfileInstanceList).isEmpty();
+ else
+ fResult = UICommon::hasAllowedExtension(path(m_pFileSelector), OVFFileExts)
+ && QFile::exists(path(m_pFileSelector));
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardImportAppPageSource::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check whether there was cloud source selected: */
+ if (wizard()->isSourceCloudOne())
+ {
+ /* Update cloud stuff: */
+ updateCloudStuff();
+ /* Which is required to continue to the next page: */
+ fResult = wizard()->cloudAppliance().isNotNull()
+ && wizard()->vsdImportForm().isNotNull();
+ }
+ else
+ {
+ /* Update local stuff (only if something changed): */
+ if (m_pFileSelector->isModified())
+ {
+ updateLocalStuff();
+ m_pFileSelector->resetModified();
+ }
+ /* Which is required to continue to the next page: */
+ fResult = wizard()->localAppliance().isNotNull();
+ }
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardImportAppPageSource::sltHandleSourceComboChange()
+{
+ /* Update combo tool-tip: */
+ updateSourceComboToolTip(m_pSourceComboBox);
+
+ /* Update wizard fields: */
+ wizard()->setSourceCloudOne(isSourceCloudOne(m_pSourceComboBox));
+
+ /* Refresh page widgets: */
+ refreshStackedWidget(m_pSettingsWidget1,
+ wizard()->isSourceCloudOne());
+ refreshProfileCombo(m_pProfileComboBox,
+ wizard()->notificationCenter(),
+ source(m_pSourceComboBox),
+ m_strProfileName,
+ wizard()->isSourceCloudOne());
+
+ /* Update profile instances: */
+ sltHandleProfileComboChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardImportAppPageSource::sltHandleProfileComboChange()
+{
+ /* Refresh required settings: */
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ refreshCloudProfileInstances(m_pProfileInstanceList,
+ wizard()->notificationCenter(),
+ source(m_pSourceComboBox),
+ profileName(m_pProfileComboBox),
+ wizard()->isSourceCloudOne());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardImportAppPageSource::sltHandleProfileButtonClick()
+{
+ /* Open Cloud Profile Manager: */
+ if (gpManager)
+ gpManager->openCloudProfileManager();
+}
+
+void UIWizardImportAppPageSource::updateLocalStuff()
+{
+ /* Create local appliance: */
+ wizard()->setFile(path(m_pFileSelector));
+}
+
+void UIWizardImportAppPageSource::updateCloudStuff()
+{
+ /* Create cloud appliance and VSD import form: */
+ CAppliance comAppliance;
+ CVirtualSystemDescriptionForm comForm;
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ refreshCloudStuff(comAppliance,
+ comForm,
+ wizard(),
+ machineId(m_pProfileInstanceList),
+ source(m_pSourceComboBox),
+ profileName(m_pProfileComboBox),
+ wizard()->isSourceCloudOne());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ wizard()->setCloudAppliance(comAppliance);
+ wizard()->setVsdImportForm(comForm);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSource.h b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSource.h
new file mode 100644
index 00000000..b9bf7d26
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/importappliance/UIWizardImportAppPageSource.h
@@ -0,0 +1,209 @@
+/* $Id: UIWizardImportAppPageSource.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardImportAppPageSource class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportAppPageSource_h
+#define FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportAppPageSource_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QListWidget;
+class QStackedWidget;
+class QIComboBox;
+class QIRichTextLabel;
+class QIToolButton;
+class UIEmptyFilePathSelector;
+class UINotificationCenter;
+class UIWizardImportApp;
+class CAppliance;
+class CVirtualSystemDescriptionForm;
+
+/** Source combo data fields. */
+enum
+{
+ SourceData_Name = Qt::UserRole + 1,
+ SourceData_ShortName = Qt::UserRole + 2,
+ SourceData_IsItCloudFormat = Qt::UserRole + 3
+};
+
+/** Profile combo data fields. */
+enum
+{
+ ProfileData_Name = Qt::UserRole + 1
+};
+
+/** Namespace for Source page of the Import Appliance wizard. */
+namespace UIWizardImportAppSource
+{
+ /** Populates sources. */
+ void populateSources(QIComboBox *pCombo,
+ UINotificationCenter *pCenter,
+ bool fImportFromOCIByDefault,
+ const QString &strSource);
+
+ /** Returns current source of @a pCombo specified. */
+ QString source(QIComboBox *pCombo);
+ /** Returns whether source under certain @a iIndex is cloud one. */
+ bool isSourceCloudOne(QIComboBox *pCombo, int iIndex = -1);
+
+ /** Refresh stacked widget. */
+ void refreshStackedWidget(QStackedWidget *pStackedWidget,
+ bool fIsFormatCloudOne);
+
+ /** Refresh profile combo. */
+ void refreshProfileCombo(QIComboBox *pCombo,
+ UINotificationCenter *pCenter,
+ const QString &strSource,
+ const QString &strProfileName,
+ bool fIsSourceCloudOne);
+ /** Refresh profile instances. */
+ void refreshCloudProfileInstances(QListWidget *pListWidget,
+ UINotificationCenter *pCenter,
+ const QString &strSource,
+ const QString &strProfileName,
+ bool fIsSourceCloudOne);
+ /** Refresh cloud stuff. */
+ void refreshCloudStuff(CAppliance &comCloudAppliance,
+ CVirtualSystemDescriptionForm &comCloudVsdImportForm,
+ UIWizardImportApp *pWizard,
+ const QString &strMachineId,
+ const QString &strSource,
+ const QString &strProfileName,
+ bool fIsSourceCloudOne);
+
+ /** Returns imported file path. */
+ QString path(UIEmptyFilePathSelector *pFileSelector);
+
+ /** Returns profile name. */
+ QString profileName(QIComboBox *pCombo);
+ /** Returns machine ID. */
+ QString machineId(QListWidget *pListWidget);
+
+ /** Updates source combo tool-tips. */
+ void updateSourceComboToolTip(QIComboBox *pCombo);
+}
+
+/** UINativeWizardPage extension for Source page of the Import Appliance wizard,
+ * based on UIWizardImportAppSource namespace functions. */
+class UIWizardImportAppPageSource : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs Source page.
+ * @param fImportFromOCIByDefault Brings whether we should propose import from OCI by default.
+ * @param strFileName Brings appliance file name. */
+ UIWizardImportAppPageSource(bool fImportFromOCIByDefault, const QString &strFileName);
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardImportApp *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Handles source combo change. */
+ void sltHandleSourceComboChange();
+
+ /** Handles profile combo change. */
+ void sltHandleProfileComboChange();
+ /** Handles profile tool-button click. */
+ void sltHandleProfileButtonClick();
+
+private:
+
+ /** Update local stuff. */
+ void updateLocalStuff();
+ /** Updates cloud stuff. */
+ void updateCloudStuff();
+
+ /** Holds whether default source should be Import from OCI. */
+ bool m_fImportFromOCIByDefault;
+ /** Handles the appliance file name. */
+ QString m_strFileName;
+
+ /** Holds the cached source. */
+ QString m_strSource;
+ /** Holds the cached profile name. */
+ QString m_strProfileName;
+
+ /** Holds the main label instance. */
+ QIRichTextLabel *m_pLabelMain;
+ /** Holds the description label instance. */
+ QIRichTextLabel *m_pLabelDescription;
+
+ /** Holds the source layout instance. */
+ QGridLayout *m_pSourceLayout;
+ /** Holds the source type label instance. */
+ QLabel *m_pSourceLabel;
+ /** Holds the source type combo-box instance. */
+ QIComboBox *m_pSourceComboBox;
+
+ /** Holds the settings widget 1 instance. */
+ QStackedWidget *m_pSettingsWidget1;
+
+ /** Holds the local container layout instance. */
+ QGridLayout *m_pLocalContainerLayout;
+ /** Holds the file label instance. */
+ QLabel *m_pFileLabel;
+ /** Holds the file selector instance. */
+ UIEmptyFilePathSelector *m_pFileSelector;
+
+ /** Holds the cloud container layout instance. */
+ QGridLayout *m_pCloudContainerLayout;
+ /** Holds the profile label instance. */
+ QLabel *m_pProfileLabel;
+ /** Holds the profile combo-box instance. */
+ QIComboBox *m_pProfileComboBox;
+ /** Holds the profile management tool-button instance. */
+ QIToolButton *m_pProfileToolButton;
+ /** Holds the profile instance label instance. */
+ QLabel *m_pProfileInstanceLabel;
+ /** Holds the profile instance list instance. */
+ QListWidget *m_pProfileInstanceList;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_importappliance_UIWizardImportAppPageSource_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVM.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVM.cpp
new file mode 100644
index 00000000..e2df82f0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVM.cpp
@@ -0,0 +1,141 @@
+/* $Id: UIWizardNewCloudVM.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewCloudVM class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UINotificationCenter.h"
+#include "UIWizardNewCloudVM.h"
+#include "UIWizardNewCloudVMPageSource.h"
+#include "UIWizardNewCloudVMPageProperties.h"
+#include "UIWizardNewCloudVMPageExpert.h"
+
+/* COM includes: */
+#include "CCloudMachine.h"
+
+
+UIWizardNewCloudVM::UIWizardNewCloudVM(QWidget *pParent,
+ const QString &strFullGroupName /* = QString() */)
+ : UINativeWizard(pParent, WizardType_NewCloudVM)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign watermark: */
+ setPixmapName(":/wizard_new_cloud_vm.png");
+#else
+ /* Assign background image: */
+ setPixmapName(":/wizard_new_cloud_vm_bg.png");
+#endif
+
+ /* Parse passed full group name: */
+ const QString strProviderShortName = strFullGroupName.section('/', 1, 1);
+ const QString strProfileName = strFullGroupName.section('/', 2, 2);
+ if (!strProviderShortName.isEmpty() && !strProfileName.isEmpty())
+ {
+ m_strProviderShortName = strProviderShortName;
+ m_strProfileName = strProfileName;
+ }
+}
+
+void UIWizardNewCloudVM::createVSDForm()
+{
+ /* Acquire prepared client and description: */
+ CCloudClient comClient = client();
+ CVirtualSystemDescription comVSD = vsd();
+ AssertReturnVoid(comClient.isNotNull() && comVSD.isNotNull());
+
+ /* Create launch VSD form: */
+ UINotificationProgressLaunchVSDFormCreate *pNotification = new UINotificationProgressLaunchVSDFormCreate(comClient,
+ comVSD,
+ providerShortName(),
+ profileName());
+ connect(pNotification, &UINotificationProgressLaunchVSDFormCreate::sigVSDFormCreated,
+ this, &UIWizardNewCloudVM::setVSDForm);
+ handleNotificationProgressNow(pNotification);
+}
+
+bool UIWizardNewCloudVM::createCloudVM()
+{
+ /* Prepare result: */
+ bool fResult = false;
+
+ /* Acquire prepared client and description: */
+ CCloudClient comClient = client();
+ CVirtualSystemDescription comVSD = vsd();
+ AssertReturn(comClient.isNotNull() && comVSD.isNotNull(), false);
+
+ /* Initiate cloud VM creation procedure: */
+ CCloudMachine comMachine;
+
+ /* Create cloud VM: */
+ UINotificationProgressCloudMachineCreate *pNotification = new UINotificationProgressCloudMachineCreate(comClient,
+ comMachine,
+ comVSD,
+ providerShortName(),
+ profileName());
+ connect(pNotification, &UINotificationProgressCloudMachineCreate::sigCloudMachineCreated,
+ &uiCommon(), &UICommon::sltHandleCloudMachineAdded);
+ gpNotificationCenter->append(pNotification);
+
+ /* Positive: */
+ fResult = true;
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardNewCloudVM::populatePages()
+{
+ /* Create corresponding pages: */
+ switch (mode())
+ {
+ case WizardMode_Basic:
+ {
+ addPage(new UIWizardNewCloudVMPageSource);
+ addPage(new UIWizardNewCloudVMPageProperties);
+ break;
+ }
+ case WizardMode_Expert:
+ {
+ addPage(new UIWizardNewCloudVMPageExpert);
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid mode: %d", mode()));
+ break;
+ }
+ }
+}
+
+void UIWizardNewCloudVM::retranslateUi()
+{
+ /* Call to base-class: */
+ UINativeWizard::retranslateUi();
+
+ /* Translate wizard: */
+ setWindowTitle(tr("Create Cloud Virtual Machine"));
+ /// @todo implement this?
+ //setButtonText(QWizard::FinishButton, tr("Create"));
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVM.h b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVM.h
new file mode 100644
index 00000000..802abc32
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVM.h
@@ -0,0 +1,109 @@
+/* $Id: UIWizardNewCloudVM.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewCloudVM class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVM_h
+#define FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVM_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CCloudClient.h"
+#include "CVirtualSystemDescription.h"
+#include "CVirtualSystemDescriptionForm.h"
+
+/** New Cloud VM wizard. */
+class UIWizardNewCloudVM : public UINativeWizard
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs New Cloud VM wizard passing @a pParent & @a enmMode to the base-class.
+ * @param strFullGroupName Brings full group name (/provider/profile) to create VM in. */
+ UIWizardNewCloudVM(QWidget *pParent, const QString &strFullGroupName);
+
+ /** Returns provider short name. */
+ QString providerShortName() const { return m_strProviderShortName; }
+ /** Returns profile name. */
+ QString profileName() const { return m_strProfileName; }
+ /** Returns Cloud Client object. */
+ CCloudClient client() const { return m_comClient; }
+ /** Returns Virtual System Description object. */
+ CVirtualSystemDescription vsd() const { return m_comVSD; }
+ /** Returns Virtual System Description Form object. */
+ CVirtualSystemDescriptionForm vsdForm() const { return m_comVSDForm; }
+
+ /** Creates VSD Form. */
+ void createVSDForm();
+
+ /** Creates New Cloud VM. */
+ bool createCloudVM();
+
+public slots:
+
+ /** Defines @a strProviderShortName. */
+ void setProviderShortName(const QString &strProviderShortName) { m_strProviderShortName = strProviderShortName; }
+ /** Defines @a strProfileName. */
+ void setProfileName(const QString &strProfileName) { m_strProfileName = strProfileName; }
+ /** Defines Cloud @a comClient object. */
+ void setClient(const CCloudClient &comClient) { m_comClient = comClient; }
+ /** Defines Virtual System @a comVSD object. */
+ void setVSD(const CVirtualSystemDescription &comVSD) { m_comVSD = comVSD; }
+ /** Defines Virtual System Description @a comForm object. */
+ void setVSDForm(const CVirtualSystemDescriptionForm &comForm) { m_comVSDForm = comForm; }
+
+protected:
+
+ /** Populates pages. */
+ virtual void populatePages() /* override final */;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+private:
+
+ /** Holds the short provider name. */
+ QString m_strProviderShortName;
+ /** Holds the profile name. */
+ QString m_strProfileName;
+ /** Holds the Cloud Client object reference. */
+ CCloudClient m_comClient;
+ /** Holds the Virtual System Description object reference. */
+ CVirtualSystemDescription m_comVSD;
+ /** Holds the Virtual System Description Form object reference. */
+ CVirtualSystemDescriptionForm m_comVSDForm;
+};
+
+/** Safe pointer to new cloud vm wizard. */
+typedef QPointer<UIWizardNewCloudVM> UISafePointerWizardNewCloudVM;
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVM_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageExpert.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageExpert.cpp
new file mode 100644
index 00000000..4c6ec64a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageExpert.cpp
@@ -0,0 +1,370 @@
+/* $Id: UIWizardNewCloudVMPageExpert.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewCloudVMPageExpert class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QListWidget>
+#include <QPushButton>
+#include <QTabBar>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIToolButton.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIFormEditorWidget.h"
+#include "UIIconPool.h"
+#include "UINotificationCenter.h"
+#include "UIToolBox.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualBoxManager.h"
+#include "UIWizardNewCloudVM.h"
+#include "UIWizardNewCloudVMPageExpert.h"
+
+/* Namespaces: */
+using namespace UIWizardNewCloudVMSource;
+using namespace UIWizardNewCloudVMProperties;
+
+
+UIWizardNewCloudVMPageExpert::UIWizardNewCloudVMPageExpert()
+ : m_pToolBox(0)
+ , m_pProviderComboBox(0)
+ , m_pProfileComboBox(0)
+ , m_pProfileToolButton(0)
+ , m_pSourceTabBar(0)
+ , m_pSourceImageList(0)
+ , m_pFormEditor(0)
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare tool-box: */
+ m_pToolBox = new UIToolBox(this);
+ if (m_pToolBox)
+ {
+ /* Prepare location widget: */
+ QWidget *pWidgetLocation = new QWidget(m_pToolBox);
+ if (pWidgetLocation)
+ {
+ /* Prepare location layout: */
+ QVBoxLayout *pLayoutLocation = new QVBoxLayout(pWidgetLocation);
+ if (pLayoutLocation)
+ {
+ pLayoutLocation->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare provider combo-box: */
+ m_pProviderComboBox = new QIComboBox(pWidgetLocation);
+ if (m_pProviderComboBox)
+ pLayoutLocation->addWidget(m_pProviderComboBox);
+
+ /* Prepare profile layout: */
+ QHBoxLayout *pLayoutProfile = new QHBoxLayout;
+ if (pLayoutProfile)
+ {
+ pLayoutProfile->setContentsMargins(0, 0, 0, 0);
+ pLayoutProfile->setSpacing(1);
+
+ /* Prepare profile combo-box: */
+ m_pProfileComboBox = new QIComboBox(pWidgetLocation);
+ if (m_pProfileComboBox)
+ pLayoutProfile->addWidget(m_pProfileComboBox);
+
+ /* Prepare profile tool-button: */
+ m_pProfileToolButton = new QIToolButton(pWidgetLocation);
+ if (m_pProfileToolButton)
+ {
+ m_pProfileToolButton->setIcon(UIIconPool::iconSet(":/cloud_profile_manager_16px.png",
+ ":/cloud_profile_manager_disabled_16px.png"));
+ pLayoutProfile->addWidget(m_pProfileToolButton);
+ }
+
+ /* Add into layout: */
+ pLayoutLocation->addLayout(pLayoutProfile);
+ }
+ }
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(0, pWidgetLocation, QString());
+ }
+
+ /* Prepare source widget: */
+ QWidget *pWidgetSource = new QWidget(m_pToolBox);
+ if (pWidgetSource)
+ {
+ /* Prepare source layout: */
+ QVBoxLayout *pLayoutSource = new QVBoxLayout(pWidgetSource);
+ if (pLayoutSource)
+ {
+ pLayoutSource->setContentsMargins(0, 0, 0, 0);
+ pLayoutSource->setSpacing(0);
+
+ /* Prepare source tab-bar: */
+ m_pSourceTabBar = new QTabBar(pWidgetSource);
+ if (m_pSourceTabBar)
+ {
+ m_pSourceTabBar->addTab(QString());
+ m_pSourceTabBar->addTab(QString());
+
+ /* Add into layout: */
+ pLayoutSource->addWidget(m_pSourceTabBar);
+ }
+
+ /* Prepare source image list: */
+ m_pSourceImageList = new QListWidget(pWidgetSource);
+ if (m_pSourceImageList)
+ {
+ /* We want to have sorting enabled: */
+ m_pSourceImageList->setSortingEnabled(true);
+ /* A bit of look&feel: */
+ m_pSourceImageList->setAlternatingRowColors(true);
+
+ /* Add into layout: */
+ pLayoutSource->addWidget(m_pSourceImageList);
+ }
+ }
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(1, pWidgetSource, QString());
+ }
+
+ /* Prepare settings widget: */
+ QWidget *pWidgetSettings = new QWidget(m_pToolBox);
+ if (pWidgetSettings)
+ {
+ /* Prepare settings layout: */
+ QVBoxLayout *pLayoutSettings = new QVBoxLayout(pWidgetSettings);
+ if (pLayoutSettings)
+ {
+ pLayoutSettings->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare form editor widget: */
+ m_pFormEditor = new UIFormEditorWidget(pWidgetSettings);
+ if (m_pFormEditor)
+ {
+ /* Add into layout: */
+ pLayoutSettings->addWidget(m_pFormEditor);
+ }
+ }
+
+ /* Add into tool-box: */
+ m_pToolBox->insertPage(2, pWidgetSettings, QString());
+ }
+
+ /* Add into layout: */
+ pLayoutMain->addWidget(m_pToolBox);
+ }
+ }
+
+ /* Setup connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UIWizardNewCloudVMPageExpert::sltHandleProviderComboChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UIWizardNewCloudVMPageExpert::sltHandleProviderComboChange);
+ connect(m_pProviderComboBox, &QIComboBox::activated,
+ this, &UIWizardNewCloudVMPageExpert::sltHandleProviderComboChange);
+ connect(m_pProfileComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardNewCloudVMPageExpert::sltHandleProfileComboChange);
+ connect(m_pProfileToolButton, &QIToolButton::clicked,
+ this, &UIWizardNewCloudVMPageExpert::sltHandleProfileButtonClick);
+ connect(m_pSourceTabBar, &QTabBar::currentChanged,
+ this, &UIWizardNewCloudVMPageExpert::sltHandleSourceTabBarChange);
+ connect(m_pSourceImageList, &QListWidget::currentRowChanged,
+ this, &UIWizardNewCloudVMPageExpert::sltHandleSourceImageChange);
+}
+
+UIWizardNewCloudVM *UIWizardNewCloudVMPageExpert::wizard() const
+{
+ return qobject_cast<UIWizardNewCloudVM*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardNewCloudVMPageExpert::retranslateUi()
+{
+ /* Translate tool-box: */
+ if (m_pToolBox)
+ {
+ m_pToolBox->setPageTitle(0, UIWizardNewCloudVM::tr("Location"));
+ m_pToolBox->setPageTitle(1, UIWizardNewCloudVM::tr("Source"));
+ m_pToolBox->setPageTitle(2, UIWizardNewCloudVM::tr("Settings"));
+ }
+
+ /* Translate received values of Location combo-box.
+ * We are enumerating starting from 0 for simplicity: */
+ if (m_pProviderComboBox)
+ for (int i = 0; i < m_pProviderComboBox->count(); ++i)
+ {
+ m_pProviderComboBox->setItemText(i, m_pProviderComboBox->itemData(i, ProviderData_Name).toString());
+ m_pProviderComboBox->setItemData(i, UIWizardNewCloudVM::tr("Create VM for cloud service provider."), Qt::ToolTipRole);
+ }
+
+ /* Translate source tab-bar: */
+ if (m_pSourceTabBar)
+ {
+ m_pSourceTabBar->setTabText(0, UIWizardNewCloudVM::tr("&Images"));
+ m_pSourceTabBar->setTabText(1, UIWizardNewCloudVM::tr("&Boot Volumes"));
+ }
+
+ /* Translate profile stuff: */
+ if (m_pProfileToolButton)
+ m_pProfileToolButton->setToolTip(UIWizardNewCloudVM::tr("Open Cloud Profile Manager..."));
+
+ /* Update tool-tips: */
+ updateComboToolTip(m_pProviderComboBox);
+}
+
+void UIWizardNewCloudVMPageExpert::initializePage()
+{
+ /* Choose 1st tool to be chosen initially: */
+ m_pToolBox->setCurrentPage(0);
+ /* Make sure form-editor knows notification-center: */
+ m_pFormEditor->setNotificationCenter(wizard()->notificationCenter());
+ /* Populate providers: */
+ populateProviders(m_pProviderComboBox, wizard()->notificationCenter());
+ /* Translate providers: */
+ retranslateUi();
+ /* Make image list focused by default: */
+ m_pSourceImageList->setFocus();
+ /* Fetch it, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltHandleProviderComboChange", Qt::QueuedConnection);
+}
+
+bool UIWizardNewCloudVMPageExpert::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check cloud settings: */
+ fResult = wizard()->client().isNotNull()
+ && wizard()->vsd().isNotNull();
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardNewCloudVMPageExpert::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Make sure table has own data committed: */
+ m_pFormEditor->makeSureEditorDataCommitted();
+
+ /* Check whether we have proper VSD form: */
+ CVirtualSystemDescriptionForm comForm = wizard()->vsdForm();
+ /* Give changed VSD back: */
+ if (comForm.isNotNull())
+ {
+ comForm.GetVirtualSystemDescription();
+ fResult = comForm.isOk();
+ if (!fResult)
+ UINotificationMessage::cannotAcquireVirtualSystemDescriptionFormParameter(comForm, wizard()->notificationCenter());
+ }
+
+ /* Try to create cloud VM: */
+ if (fResult)
+ {
+ fResult = wizard()->createCloudVM();
+
+ /* If the final step failed we could try
+ * sugest user more valid form this time: */
+ if (!fResult)
+ {
+ wizard()->setVSDForm(CVirtualSystemDescriptionForm());
+ wizard()->createVSDForm();
+ updatePropertiesTable();
+ emit completeChanged();
+ }
+ }
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardNewCloudVMPageExpert::sltHandleProviderComboChange()
+{
+ /* Update combo tool-tip: */
+ updateComboToolTip(m_pProviderComboBox);
+
+ /* Update wizard fields: */
+ wizard()->setProviderShortName(m_pProviderComboBox->currentData(ProviderData_ShortName).toString());
+
+ /* Update profiles: */
+ populateProfiles(m_pProfileComboBox, wizard()->notificationCenter(), wizard()->providerShortName(), wizard()->profileName());
+ sltHandleProfileComboChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardNewCloudVMPageExpert::sltHandleProfileComboChange()
+{
+ /* Update wizard fields: */
+ wizard()->setProfileName(m_pProfileComboBox->currentData(ProfileData_Name).toString());
+ wizard()->setClient(cloudClientByName(wizard()->providerShortName(), wizard()->profileName(), wizard()->notificationCenter()));
+
+ /* Update source: */
+ sltHandleSourceTabBarChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardNewCloudVMPageExpert::sltHandleProfileButtonClick()
+{
+ if (gpManager)
+ gpManager->openCloudProfileManager();
+}
+
+void UIWizardNewCloudVMPageExpert::sltHandleSourceTabBarChange()
+{
+ /* Update source type: */
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ populateSourceImages(m_pSourceImageList, m_pSourceTabBar, wizard()->notificationCenter(), wizard()->client());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ sltHandleSourceImageChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardNewCloudVMPageExpert::sltHandleSourceImageChange()
+{
+ /* Update source image & VSD form: */
+ m_strSourceImageId = currentListWidgetData(m_pSourceImageList);
+ wizard()->setVSD(createVirtualSystemDescription(wizard()->notificationCenter()));
+ populateFormProperties(wizard()->vsd(), wizard(), m_pSourceTabBar, m_strSourceImageId);
+ wizard()->createVSDForm();
+ updatePropertiesTable();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardNewCloudVMPageExpert::updatePropertiesTable()
+{
+ refreshFormPropertiesTable(m_pFormEditor, wizard()->vsdForm());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageExpert.h b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageExpert.h
new file mode 100644
index 00000000..b2a999da
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageExpert.h
@@ -0,0 +1,115 @@
+/* $Id: UIWizardNewCloudVMPageExpert.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewCloudVMPageExpert class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVMPageExpert_h
+#define FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVMPageExpert_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UIWizardNewCloudVMPageSource.h"
+#include "UIWizardNewCloudVMPageProperties.h"
+
+/* Forward declarations: */
+class UIToolBox;
+class UIWizardNewCloudVM;
+
+/** UINativeWizardPage extension for Expert page of the New Cloud VM wizard,
+ * based on UIWizardNewCloudVMPage1 & UIWizardNewCloudVMPage2 namespace functions. */
+class UIWizardNewCloudVMPageExpert : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs expert page. */
+ UIWizardNewCloudVMPageExpert();
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardNewCloudVM *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Handles change in provider combo-box. */
+ void sltHandleProviderComboChange();
+
+ /** Handles change in profile combo-box. */
+ void sltHandleProfileComboChange();
+ /** Handles profile tool-button click. */
+ void sltHandleProfileButtonClick();
+
+ /** Handles change in source tab-bar. */
+ void sltHandleSourceTabBarChange();
+
+ /** Handles change in instance list. */
+ void sltHandleSourceImageChange();
+
+private:
+
+ /** Updates properties table. */
+ void updatePropertiesTable();
+
+ /** Holds whether we want full wizard form or short one. */
+ bool m_fFullWizard;
+ /** Holds the image ID. */
+ QString m_strSourceImageId;
+
+ /** Holds the tool-box instance. */
+ UIToolBox *m_pToolBox;
+
+ /** Holds the location type combo-box instance. */
+ QIComboBox *m_pProviderComboBox;
+ /** Holds the profile combo-box instance. */
+ QIComboBox *m_pProfileComboBox;
+ /** Holds the profile management tool-button instance. */
+ QIToolButton *m_pProfileToolButton;
+
+ /** Holds the source tab-bar instance. */
+ QTabBar *m_pSourceTabBar;
+ /** Holds the source image list instance. */
+ QListWidget *m_pSourceImageList;
+
+ /** Holds the Form Editor widget instance. */
+ UIFormEditorWidget *m_pFormEditor;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVMPageExpert_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageProperties.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageProperties.cpp
new file mode 100644
index 00000000..6f4c35fa
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageProperties.cpp
@@ -0,0 +1,177 @@
+/* $Id: UIWizardNewCloudVMPageProperties.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewCloudVMPageProperties class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "UIFormEditorWidget.h"
+#include "UINotificationCenter.h"
+#include "UIWizardNewCloudVM.h"
+#include "UIWizardNewCloudVMPageProperties.h"
+
+/* Namespaces: */
+using namespace UIWizardNewCloudVMProperties;
+
+
+/*********************************************************************************************************************************
+* Namespace UIWizardNewCloudVMProperties implementation. *
+*********************************************************************************************************************************/
+
+void UIWizardNewCloudVMProperties::refreshFormPropertiesTable(UIFormEditorWidget *pFormEditor,
+ const CVirtualSystemDescriptionForm &comForm)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pFormEditor);
+ AssertReturnVoid(comForm.isNotNull());
+
+ /* Make sure the properties table get the new description form: */
+ pFormEditor->setVirtualSystemDescriptionForm(comForm);
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWizardNewCloudVMPageProperties implementation. *
+*********************************************************************************************************************************/
+
+UIWizardNewCloudVMPageProperties::UIWizardNewCloudVMPageProperties()
+ : m_pLabel(0)
+ , m_pFormEditor(0)
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare label: */
+ m_pLabel = new QIRichTextLabel(this);
+ if (m_pLabel)
+ pLayoutMain->addWidget(m_pLabel);
+
+ /* Prepare form editor widget: */
+ m_pFormEditor = new UIFormEditorWidget(this);
+ if (m_pFormEditor)
+ {
+ /* Make form-editor fit 8 sections in height by default: */
+ const int iDefaultSectionHeight = m_pFormEditor->verticalHeader()
+ ? m_pFormEditor->verticalHeader()->defaultSectionSize()
+ : 0;
+ if (iDefaultSectionHeight > 0)
+ m_pFormEditor->setMinimumHeight(8 * iDefaultSectionHeight);
+
+ /* Add into layout: */
+ pLayoutMain->addWidget(m_pFormEditor);
+ }
+ }
+}
+
+UIWizardNewCloudVM *UIWizardNewCloudVMPageProperties::wizard() const
+{
+ return qobject_cast<UIWizardNewCloudVM*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardNewCloudVMPageProperties::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardNewCloudVM::tr("Cloud Virtual Machine settings"));
+
+ /* Translate description label: */
+ m_pLabel->setText(UIWizardNewCloudVM::tr("These are the the suggested settings of the cloud VM creation procedure, they are "
+ "influencing the resulting cloud VM instance. You can change many of the "
+ "properties shown by double-clicking on the items and disable others using the "
+ "check boxes below."));
+}
+
+void UIWizardNewCloudVMPageProperties::initializePage()
+{
+ /* Make sure form-editor knows notification-center: */
+ m_pFormEditor->setNotificationCenter(wizard()->notificationCenter());
+ /* Generate VSD form, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltInitShortWizardForm", Qt::QueuedConnection);
+}
+
+bool UIWizardNewCloudVMPageProperties::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check cloud settings: */
+ fResult = wizard()->client().isNotNull()
+ && wizard()->vsd().isNotNull();
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardNewCloudVMPageProperties::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Make sure table has own data committed: */
+ m_pFormEditor->makeSureEditorDataCommitted();
+
+ /* Check whether we have proper VSD form: */
+ CVirtualSystemDescriptionForm comForm = wizard()->vsdForm();
+ /* Give changed VSD back: */
+ if (comForm.isNotNull())
+ {
+ comForm.GetVirtualSystemDescription();
+ fResult = comForm.isOk();
+ if (!fResult)
+ UINotificationMessage::cannotAcquireVirtualSystemDescriptionFormParameter(comForm, wizard()->notificationCenter());
+ }
+
+ /* Try to create cloud VM: */
+ if (fResult)
+ {
+ fResult = wizard()->createCloudVM();
+
+ /* If the final step failed we could try
+ * sugest user more valid form this time: */
+ if (!fResult)
+ {
+ wizard()->setVSDForm(CVirtualSystemDescriptionForm());
+ sltInitShortWizardForm();
+ }
+ }
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardNewCloudVMPageProperties::sltInitShortWizardForm()
+{
+ /* Create Virtual System Description Form: */
+ if (wizard()->vsdForm().isNull())
+ wizard()->createVSDForm();
+
+ /* Refresh form properties table: */
+ refreshFormPropertiesTable(m_pFormEditor, wizard()->vsdForm());
+ emit completeChanged();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageProperties.h b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageProperties.h
new file mode 100644
index 00000000..165326ed
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageProperties.h
@@ -0,0 +1,92 @@
+/* $Id: UIWizardNewCloudVMPageProperties.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewCloudVMPageProperties class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVMPageProperties_h
+#define FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVMPageProperties_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QIRichTextLabel;
+class UIFormEditorWidget;
+class UIWizardNewCloudVM;
+class CVirtualSystemDescriptionForm;
+
+/** Namespace for properties page of the New Cloud VM wizard. */
+namespace UIWizardNewCloudVMProperties
+{
+ /** Refreshes @a pFormEditor on the basis of comForm specified. */
+ void refreshFormPropertiesTable(UIFormEditorWidget *pFormEditor, const CVirtualSystemDescriptionForm &comForm);
+}
+
+/** UINativeWizardPage extension for properties page of the New Cloud VM wizard,
+ * based on UIWizardNewCloudVMProperties namespace functions. */
+class UIWizardNewCloudVMPageProperties : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs properties basic page. */
+ UIWizardNewCloudVMPageProperties();
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardNewCloudVM *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Initializes short wizard form. */
+ void sltInitShortWizardForm();
+
+private:
+
+ /** Holds the label instance. */
+ QIRichTextLabel *m_pLabel;
+
+ /** Holds the Form Editor widget instance. */
+ UIFormEditorWidget *m_pFormEditor;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVMPageProperties_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageSource.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageSource.cpp
new file mode 100644
index 00000000..07c8b326
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageSource.cpp
@@ -0,0 +1,626 @@
+/* $Id: UIWizardNewCloudVMPageSource.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewCloudVMPageSource class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QGridLayout>
+#include <QLabel>
+#include <QListWidget>
+#include <QPushButton>
+#include <QTabBar>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIComboBox.h"
+#include "QIRichTextLabel.h"
+#include "QIToolButton.h"
+#include "UICloudNetworkingStuff.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UINotificationCenter.h"
+#include "UIVirtualBoxEventHandler.h"
+#include "UIVirtualBoxManager.h"
+#include "UIWizardNewCloudVM.h"
+#include "UIWizardNewCloudVMPageSource.h"
+
+/* COM includes: */
+#include "CStringArray.h"
+
+/* Namespaces: */
+using namespace UIWizardNewCloudVMSource;
+
+
+/*********************************************************************************************************************************
+* Namespace UIWizardNewCloudVMSource implementation. *
+*********************************************************************************************************************************/
+
+void UIWizardNewCloudVMSource::populateProviders(QIComboBox *pCombo, UINotificationCenter *pCenter)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (pCombo->currentIndex() != -1)
+ strOldData = pCombo->currentData(ProviderData_ShortName).toString();
+ /* Otherwise "OCI" should be the default one: */
+ else
+ strOldData = "OCI";
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Iterate through existing providers: */
+ foreach (const CCloudProvider &comProvider, listCloudProviders(pCenter))
+ {
+ /* Skip if we have nothing to populate (file missing?): */
+ if (comProvider.isNull())
+ continue;
+ /* Acquire provider name: */
+ QString strProviderName;
+ if (!cloudProviderName(comProvider, strProviderName, pCenter))
+ continue;
+ /* Acquire provider short name: */
+ QString strProviderShortName;
+ if (!cloudProviderShortName(comProvider, strProviderShortName, pCenter))
+ continue;
+
+ /* Compose empty item, fill the data: */
+ pCombo->addItem(QString());
+ pCombo->setItemData(pCombo->count() - 1, strProviderName, ProviderData_Name);
+ pCombo->setItemData(pCombo->count() - 1, strProviderShortName, ProviderData_ShortName);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = pCombo->findData(strOldData, ProviderData_ShortName);
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+}
+
+void UIWizardNewCloudVMSource::populateProfiles(QIComboBox *pCombo,
+ UINotificationCenter *pCenter,
+ const QString &strProviderShortName,
+ const QString &strProfileName)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+ /* Acquire provider: */
+ CCloudProvider comProvider = cloudProviderByShortName(strProviderShortName, pCenter);
+ AssertReturnVoid(comProvider.isNotNull());
+
+ /* Remember current item data to be able to restore it: */
+ QString strOldData;
+ if (pCombo->currentIndex() != -1)
+ strOldData = pCombo->currentData(ProfileData_Name).toString();
+ else if (!strProfileName.isEmpty())
+ strOldData = strProfileName;
+
+ /* Block signals while updating: */
+ pCombo->blockSignals(true);
+
+ /* Clear combo initially: */
+ pCombo->clear();
+
+ /* Acquire restricted accounts: */
+ const QStringList restrictedProfiles = gEDataManager->cloudProfileManagerRestrictions();
+
+ /* Iterate through existing profiles: */
+ QStringList allowedProfileNames;
+ QStringList restrictedProfileNames;
+ foreach (const CCloudProfile &comProfile, listCloudProfiles(comProvider, pCenter))
+ {
+ /* Skip if we have nothing to populate (wtf happened?): */
+ if (comProfile.isNull())
+ continue;
+ /* Acquire current profile name: */
+ QString strCurrentProfileName;
+ if (!cloudProfileName(comProfile, strCurrentProfileName, pCenter))
+ continue;
+
+ /* Compose full profile name: */
+ const QString strFullProfileName = QString("/%1/%2").arg(strProviderShortName).arg(strCurrentProfileName);
+ /* Append to appropriate list: */
+ if (restrictedProfiles.contains(strFullProfileName))
+ restrictedProfileNames.append(strCurrentProfileName);
+ else
+ allowedProfileNames.append(strCurrentProfileName);
+ }
+
+ /* Add allowed items: */
+ foreach (const QString &strAllowedProfileName, allowedProfileNames)
+ {
+ /* Compose item, fill it's data: */
+ pCombo->addItem(strAllowedProfileName);
+ pCombo->setItemData(pCombo->count() - 1, strAllowedProfileName, ProfileData_Name);
+ QFont fnt = pCombo->font();
+ fnt.setBold(true);
+ pCombo->setItemData(pCombo->count() - 1, fnt, Qt::FontRole);
+ }
+ /* Add restricted items: */
+ foreach (const QString &strRestrictedProfileName, restrictedProfileNames)
+ {
+ /* Compose item, fill it's data: */
+ pCombo->addItem(strRestrictedProfileName);
+ pCombo->setItemData(pCombo->count() - 1, strRestrictedProfileName, ProfileData_Name);
+ QBrush brsh;
+ brsh.setColor(Qt::gray);
+ pCombo->setItemData(pCombo->count() - 1, brsh, Qt::ForegroundRole);
+ }
+
+ /* Set previous/default item if possible: */
+ int iNewIndex = -1;
+ if ( iNewIndex == -1
+ && !strOldData.isNull())
+ iNewIndex = pCombo->findData(strOldData, ProfileData_Name);
+ if ( iNewIndex == -1
+ && pCombo->count() > 0)
+ iNewIndex = 0;
+ if (iNewIndex != -1)
+ pCombo->setCurrentIndex(iNewIndex);
+
+ /* Unblock signals after update: */
+ pCombo->blockSignals(false);
+}
+
+void UIWizardNewCloudVMSource::populateSourceImages(QListWidget *pList,
+ QTabBar *pTabBar,
+ UINotificationCenter *pCenter,
+ const CCloudClient &comClient)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pList);
+ AssertPtrReturnVoid(pTabBar);
+ AssertReturnVoid(comClient.isNotNull());
+
+ /* Block signals while updating: */
+ pList->blockSignals(true);
+
+ /* Clear list initially: */
+ pList->clear();
+
+ /* Gather source names and ids, depending on current source tab-bar index: */
+ CStringArray comNames;
+ CStringArray comIDs;
+ bool fResult = false;
+ switch (pTabBar->currentIndex())
+ {
+ /* Ask for cloud images, currently we are interested in Available images only: */
+ case 0: fResult = listCloudImages(comClient, comNames, comIDs, pCenter); break;
+ /* Ask for cloud boot-volumes, currently we are interested in Source boot-volumes only: */
+ case 1: fResult = listCloudSourceBootVolumes(comClient, comNames, comIDs, pCenter); break;
+ default: break;
+ }
+ if (fResult)
+ {
+ /* Push acquired names to list rows: */
+ const QVector<QString> names = comNames.GetValues();
+ const QVector<QString> ids = comIDs.GetValues();
+ for (int i = 0; i < names.size(); ++i)
+ {
+ /* Create list item: */
+ QListWidgetItem *pItem = new QListWidgetItem(names.at(i), pList);
+ if (pItem)
+ {
+ pItem->setFlags(pItem->flags() & ~Qt::ItemIsEditable);
+ pItem->setData(Qt::UserRole, ids.at(i));
+ }
+ }
+ }
+
+ /* Choose the 1st one by default if possible: */
+ if (pList->count())
+ pList->setCurrentRow(0);
+
+ /* Unblock signals after update: */
+ pList->blockSignals(false);
+}
+
+void UIWizardNewCloudVMSource::populateFormProperties(CVirtualSystemDescription comVSD,
+ UIWizardNewCloudVM *pWizard,
+ QTabBar *pTabBar,
+ const QString &strImageId)
+{
+ /* Sanity check: */
+ AssertReturnVoid(comVSD.isNotNull());
+ AssertPtrReturnVoid(pTabBar);
+
+ /* Depending on current source tab-bar index: */
+ switch (pTabBar->currentIndex())
+ {
+ /* Add image id to virtual system description: */
+ case 0: comVSD.AddDescription(KVirtualSystemDescriptionType_CloudImageId, strImageId, QString()); break;
+ /* Add boot-volume id to virtual system description: */
+ case 1: comVSD.AddDescription(KVirtualSystemDescriptionType_CloudBootVolumeId, strImageId, QString()); break;
+ default: break;
+ }
+ if (!comVSD.isOk())
+ UINotificationMessage::cannotChangeVirtualSystemDescriptionParameter(comVSD, pWizard->notificationCenter());
+}
+
+void UIWizardNewCloudVMSource::updateComboToolTip(QIComboBox *pCombo)
+{
+ /* Sanity check: */
+ AssertPtrReturnVoid(pCombo);
+
+ const int iCurrentIndex = pCombo->currentIndex();
+ if (iCurrentIndex != -1)
+ {
+ const QString strCurrentToolTip = pCombo->itemData(iCurrentIndex, Qt::ToolTipRole).toString();
+ AssertMsg(!strCurrentToolTip.isEmpty(), ("Tool-tip data not found!\n"));
+ pCombo->setToolTip(strCurrentToolTip);
+ }
+}
+
+QString UIWizardNewCloudVMSource::currentListWidgetData(QListWidget *pList)
+{
+ /* Sanity check: */
+ AssertPtrReturn(pList, QString());
+
+ QListWidgetItem *pItem = pList->currentItem();
+ return pItem ? pItem->data(Qt::UserRole).toString() : QString();
+}
+
+
+
+/*********************************************************************************************************************************
+* Class UIWizardNewCloudVMPageSource implementation. *
+*********************************************************************************************************************************/
+
+UIWizardNewCloudVMPageSource::UIWizardNewCloudVMPageSource()
+ : m_pLabelMain(0)
+ , m_pProviderLayout(0)
+ , m_pProviderLabel(0)
+ , m_pProviderComboBox(0)
+ , m_pLabelDescription(0)
+ , m_pOptionsLayout(0)
+ , m_pProfileLabel(0)
+ , m_pProfileComboBox(0)
+ , m_pProfileToolButton(0)
+ , m_pSourceImageLabel(0)
+ , m_pSourceTabBar(0)
+ , m_pSourceImageList(0)
+{
+ /* Prepare main layout: */
+ QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+ if (pLayoutMain)
+ {
+ /* Prepare main label: */
+ m_pLabelMain = new QIRichTextLabel(this);
+ if (m_pLabelMain)
+ pLayoutMain->addWidget(m_pLabelMain);
+
+ /* Prepare provider layout: */
+ m_pProviderLayout = new QGridLayout;
+ if (m_pProviderLayout)
+ {
+ m_pProviderLayout->setContentsMargins(0, 0, 0, 0);
+ m_pProviderLayout->setColumnStretch(0, 0);
+ m_pProviderLayout->setColumnStretch(1, 1);
+
+ /* Prepare provider label: */
+ m_pProviderLabel = new QLabel(this);
+ if (m_pProviderLabel)
+ m_pProviderLayout->addWidget(m_pProviderLabel, 0, 0, Qt::AlignRight);
+
+ /* Prepare provider combo-box: */
+ m_pProviderComboBox = new QIComboBox(this);
+ if (m_pProviderComboBox)
+ {
+ m_pProviderLabel->setBuddy(m_pProviderComboBox);
+ m_pProviderLayout->addWidget(m_pProviderComboBox, 0, 1);
+ }
+
+ /* Add into layout: */
+ pLayoutMain->addLayout(m_pProviderLayout);
+ }
+
+ /* Prepare description label: */
+ m_pLabelDescription = new QIRichTextLabel(this);
+ if (m_pLabelDescription)
+ pLayoutMain->addWidget(m_pLabelDescription);
+
+ /* Prepare options layout: */
+ m_pOptionsLayout = new QGridLayout;
+ if (m_pOptionsLayout)
+ {
+ m_pOptionsLayout->setContentsMargins(0, 0, 0, 0);
+ m_pOptionsLayout->setColumnStretch(0, 0);
+ m_pOptionsLayout->setColumnStretch(1, 1);
+ m_pOptionsLayout->setRowStretch(1, 0);
+ m_pOptionsLayout->setRowStretch(2, 1);
+
+ /* Prepare profile label: */
+ m_pProfileLabel = new QLabel(this);
+ if (m_pProfileLabel)
+ m_pOptionsLayout->addWidget(m_pProfileLabel, 0, 0, Qt::AlignRight);
+
+ /* Prepare profile layout: */
+ QHBoxLayout *pProfileLayout = new QHBoxLayout;
+ if (pProfileLayout)
+ {
+ pProfileLayout->setContentsMargins(0, 0, 0, 0);
+ pProfileLayout->setSpacing(1);
+
+ /* Prepare profile combo-box: */
+ m_pProfileComboBox = new QIComboBox(this);
+ if (m_pProfileComboBox)
+ {
+ m_pProfileLabel->setBuddy(m_pProfileComboBox);
+ pProfileLayout->addWidget(m_pProfileComboBox);
+ }
+
+ /* Prepare profile tool-button: */
+ m_pProfileToolButton = new QIToolButton(this);
+ if (m_pProfileToolButton)
+ {
+ m_pProfileToolButton->setIcon(UIIconPool::iconSet(":/cloud_profile_manager_16px.png",
+ ":/cloud_profile_manager_disabled_16px.png"));
+ pProfileLayout->addWidget(m_pProfileToolButton);
+ }
+
+ /* Add into layout: */
+ m_pOptionsLayout->addLayout(pProfileLayout, 0, 1);
+ }
+
+ /* Prepare source image label: */
+ m_pSourceImageLabel = new QLabel(this);
+ if (m_pSourceImageLabel)
+ m_pOptionsLayout->addWidget(m_pSourceImageLabel, 1, 0, Qt::AlignRight);
+
+ /* Prepare source image layout: */
+ QVBoxLayout *pSourceImageLayout = new QVBoxLayout;
+ if (pSourceImageLayout)
+ {
+ pSourceImageLayout->setSpacing(0);
+ pSourceImageLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare source tab-bar: */
+ m_pSourceTabBar = new QTabBar(this);
+ if (m_pSourceTabBar)
+ {
+ m_pSourceTabBar->addTab(QString());
+ m_pSourceTabBar->addTab(QString());
+
+ /* Add into layout: */
+ pSourceImageLayout->addWidget(m_pSourceTabBar);
+ }
+
+ /* Prepare source image list: */
+ m_pSourceImageList = new QListWidget(this);
+ if (m_pSourceImageList)
+ {
+ m_pSourceImageLabel->setBuddy(m_pSourceImageList);
+ /* Make source image list fit 50 symbols
+ * horizontally and 8 lines vertically: */
+ const QFontMetrics fm(m_pSourceImageList->font());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+ const int iFontWidth = fm.horizontalAdvance('x');
+#else
+ const int iFontWidth = fm.width('x');
+#endif
+ const int iTotalWidth = 50 * iFontWidth;
+ const int iFontHeight = fm.height();
+ const int iTotalHeight = 8 * iFontHeight;
+ m_pSourceImageList->setMinimumSize(QSize(iTotalWidth, iTotalHeight));
+ /* We want to have sorting enabled: */
+ m_pSourceImageList->setSortingEnabled(true);
+ /* A bit of look&feel: */
+ m_pSourceImageList->setAlternatingRowColors(true);
+
+ /* Add into layout: */
+ pSourceImageLayout->addWidget(m_pSourceImageList);
+ }
+
+ /* Add into layout: */
+ m_pOptionsLayout->addLayout(pSourceImageLayout, 1, 1, 2, 1);
+ }
+
+ /* Add into layout: */
+ pLayoutMain->addLayout(m_pOptionsLayout);
+ }
+ }
+
+ /* Setup connections: */
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
+ this, &UIWizardNewCloudVMPageSource::sltHandleProviderComboChange);
+ connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
+ this, &UIWizardNewCloudVMPageSource::sltHandleProviderComboChange);
+ connect(m_pProviderComboBox, &QIComboBox::activated,
+ this, &UIWizardNewCloudVMPageSource::sltHandleProviderComboChange);
+ connect(m_pProfileComboBox, static_cast<void(QIComboBox::*)(int)>(&QIComboBox::currentIndexChanged),
+ this, &UIWizardNewCloudVMPageSource::sltHandleProfileComboChange);
+ connect(m_pProfileToolButton, &QIToolButton::clicked,
+ this, &UIWizardNewCloudVMPageSource::sltHandleProfileButtonClick);
+ connect(m_pSourceTabBar, &QTabBar::currentChanged,
+ this, &UIWizardNewCloudVMPageSource::sltHandleSourceTabBarChange);
+ connect(m_pSourceImageList, &QListWidget::currentRowChanged,
+ this, &UIWizardNewCloudVMPageSource::sltHandleSourceImageChange);
+}
+
+UIWizardNewCloudVM *UIWizardNewCloudVMPageSource::wizard() const
+{
+ return qobject_cast<UIWizardNewCloudVM*>(UINativeWizardPage::wizard());
+}
+
+void UIWizardNewCloudVMPageSource::retranslateUi()
+{
+ /* Translate page: */
+ setTitle(UIWizardNewCloudVM::tr("Location to create"));
+
+ /* Translate main label: */
+ m_pLabelMain->setText(UIWizardNewCloudVM::tr("Please choose the location to create cloud virtual machine in. This can "
+ "be one of known cloud service providers below."));
+
+ /* Translate provider label: */
+ m_pProviderLabel->setText(UIWizardNewCloudVM::tr("&Location:"));
+ /* Translate received values of Location combo-box.
+ * We are enumerating starting from 0 for simplicity: */
+ for (int i = 0; i < m_pProviderComboBox->count(); ++i)
+ {
+ m_pProviderComboBox->setItemText(i, m_pProviderComboBox->itemData(i, ProviderData_Name).toString());
+ m_pProviderComboBox->setItemData(i, UIWizardNewCloudVM::tr("Create VM for cloud service provider."), Qt::ToolTipRole);
+ }
+
+ /* Translate description label: */
+ m_pLabelDescription->setText(UIWizardNewCloudVM::tr("Please choose one of cloud service profiles you have registered to "
+ "create virtual machine for. Existing images list will be "
+ "updated. To continue, select one of images to create virtual "
+ "machine on the basis of it."));
+
+ /* Translate profile stuff: */
+ m_pProfileLabel->setText(UIWizardNewCloudVM::tr("&Profile:"));
+ m_pProfileToolButton->setToolTip(UIWizardNewCloudVM::tr("Open Cloud Profile Manager..."));
+ m_pSourceImageLabel->setText(UIWizardNewCloudVM::tr("&Source:"));
+
+ /* Translate source tab-bar: */
+ m_pSourceTabBar->setTabText(0, UIWizardNewCloudVM::tr("&Images"));
+ m_pSourceTabBar->setTabText(1, UIWizardNewCloudVM::tr("&Boot Volumes"));
+
+ /* Adjust label widths: */
+ QList<QWidget*> labels;
+ labels << m_pProviderLabel;
+ labels << m_pProfileLabel;
+ labels << m_pSourceImageLabel;
+ int iMaxWidth = 0;
+ foreach (QWidget *pLabel, labels)
+ iMaxWidth = qMax(iMaxWidth, pLabel->minimumSizeHint().width());
+ m_pProviderLayout->setColumnMinimumWidth(0, iMaxWidth);
+ m_pOptionsLayout->setColumnMinimumWidth(0, iMaxWidth);
+
+ /* Update tool-tips: */
+ updateComboToolTip(m_pProviderComboBox);
+}
+
+void UIWizardNewCloudVMPageSource::initializePage()
+{
+ /* Populate providers: */
+ populateProviders(m_pProviderComboBox, wizard()->notificationCenter());
+ /* Translate providers: */
+ retranslateUi();
+ /* Fetch it, asynchronously: */
+ QMetaObject::invokeMethod(this, "sltHandleProviderComboChange", Qt::QueuedConnection);
+ /* Make image list focused by default: */
+ m_pSourceImageList->setFocus();
+}
+
+bool UIWizardNewCloudVMPageSource::isComplete() const
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Check cloud settings: */
+ fResult = wizard()->client().isNotNull()
+ && !m_strSourceImageId.isNull();
+
+ /* Return result: */
+ return fResult;
+}
+
+bool UIWizardNewCloudVMPageSource::validatePage()
+{
+ /* Initial result: */
+ bool fResult = true;
+
+ /* Populate vsd and form properties: */
+ wizard()->setVSD(createVirtualSystemDescription(wizard()->notificationCenter()));
+ populateFormProperties(wizard()->vsd(), wizard(), m_pSourceTabBar, m_strSourceImageId);
+ wizard()->createVSDForm();
+
+ /* And make sure they are not NULL: */
+ fResult = wizard()->vsd().isNotNull()
+ && wizard()->vsdForm().isNotNull();
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardNewCloudVMPageSource::sltHandleProviderComboChange()
+{
+ /* Update combo tool-tip: */
+ updateComboToolTip(m_pProviderComboBox);
+
+ /* Update wizard fields: */
+ wizard()->setProviderShortName(m_pProviderComboBox->currentData(ProviderData_ShortName).toString());
+
+ /* Update profiles: */
+ populateProfiles(m_pProfileComboBox, wizard()->notificationCenter(), wizard()->providerShortName(), wizard()->profileName());
+ sltHandleProfileComboChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardNewCloudVMPageSource::sltHandleProfileComboChange()
+{
+ /* Update wizard fields: */
+ wizard()->setProfileName(m_pProfileComboBox->currentData(ProfileData_Name).toString());
+ wizard()->setClient(cloudClientByName(wizard()->providerShortName(), wizard()->profileName(), wizard()->notificationCenter()));
+
+ /* Update source: */
+ sltHandleSourceTabBarChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardNewCloudVMPageSource::sltHandleProfileButtonClick()
+{
+ if (gpManager)
+ gpManager->openCloudProfileManager();
+}
+
+void UIWizardNewCloudVMPageSource::sltHandleSourceTabBarChange()
+{
+ /* Update source type: */
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(false);
+ populateSourceImages(m_pSourceImageList, m_pSourceTabBar, wizard()->notificationCenter(), wizard()->client());
+ wizard()->wizardButton(WizardButtonType_Expert)->setEnabled(true);
+ sltHandleSourceImageChange();
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
+
+void UIWizardNewCloudVMPageSource::sltHandleSourceImageChange()
+{
+ /* Update source image: */
+ m_strSourceImageId = currentListWidgetData(m_pSourceImageList);
+
+ /* Notify about changes: */
+ emit completeChanged();
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageSource.h b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageSource.h
new file mode 100644
index 00000000..dd10f331
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newcloudvm/UIWizardNewCloudVMPageSource.h
@@ -0,0 +1,180 @@
+/* $Id: UIWizardNewCloudVMPageSource.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewCloudVMPageSource class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVMPageSource_h
+#define FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVMPageSource_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CVirtualSystemDescription.h"
+
+/* Forward declarations: */
+class QGridLayout;
+class QLabel;
+class QListWidget;
+class QTabBar;
+class QIComboBox;
+class QIRichTextLabel;
+class QIToolButton;
+class UINotificationCenter;
+class UIWizardNewCloudVM;
+class CCloudClient;
+class CCloudProvider;
+
+/** Provider combo data fields. */
+enum
+{
+ ProviderData_Name = Qt::UserRole + 1,
+ ProviderData_ShortName = Qt::UserRole + 2
+};
+
+/** Profile combo data fields. */
+enum
+{
+ ProfileData_Name = Qt::UserRole + 1
+};
+
+/** Namespace for source page of the New Cloud VM wizard. */
+namespace UIWizardNewCloudVMSource
+{
+ /** Populates @a pCombo with known providers. */
+ void populateProviders(QIComboBox *pCombo, UINotificationCenter *pCenter);
+ /** Populates @a pCombo with known profiles.
+ * @param strProviderShortName Brings the short name of provider profiles related to.
+ * @param strProfileName Brings the name of profile to be chosen by default. */
+ void populateProfiles(QIComboBox *pCombo,
+ UINotificationCenter *pCenter,
+ const QString &strProviderShortName,
+ const QString &strProfileName);
+ /** Populates @a pList with source images.
+ @param pTabBar Brings the tab-bar source images should be acquired for.
+ @param comClient Brings the cloud client source images should be acquired from. */
+ void populateSourceImages(QListWidget *pList,
+ QTabBar *pTabBar,
+ UINotificationCenter *pCenter,
+ const CCloudClient &comClient);
+ /** Populates @a comVSD with form property.
+ * @param pWizard Brings the wizard used as parent for warnings inside.
+ * @param pTabBar Brings the tab-bar property should gather according to.
+ * @param strImageId Brings the image id which should be added as property. */
+ void populateFormProperties(CVirtualSystemDescription comVSD,
+ UIWizardNewCloudVM *pWizard,
+ QTabBar *pTabBar,
+ const QString &strImageId);
+
+ /** Updates @a pCombo tool-tips. */
+ void updateComboToolTip(QIComboBox *pCombo);
+
+ /** Returns current user data for @a pList specified. */
+ QString currentListWidgetData(QListWidget *pList);
+}
+
+/** UINativeWizardPage extension for source page of the New Cloud VM wizard,
+ * based on UIWizardNewCloudVMSource namespace functions. */
+class UIWizardNewCloudVMPageSource : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs source basic page. */
+ UIWizardNewCloudVMPageSource();
+
+protected:
+
+ /** Returns wizard this page belongs to. */
+ UIWizardNewCloudVM *wizard() const;
+
+ /** Handles translation event. */
+ virtual void retranslateUi() /* override final */;
+
+ /** Performs page initialization. */
+ virtual void initializePage() /* override final */;
+
+ /** Returns whether page is complete. */
+ virtual bool isComplete() const /* override final */;
+
+ /** Performs page validation. */
+ virtual bool validatePage() /* override final */;
+
+private slots:
+
+ /** Handles change in provider combo-box. */
+ void sltHandleProviderComboChange();
+
+ /** Handles change in profile combo-box. */
+ void sltHandleProfileComboChange();
+ /** Handles profile tool-button click. */
+ void sltHandleProfileButtonClick();
+
+ /** Handles change in source tab-bar. */
+ void sltHandleSourceTabBarChange();
+
+ /** Handles change in image list. */
+ void sltHandleSourceImageChange();
+
+private:
+
+ /** Holds the image ID. */
+ QString m_strSourceImageId;
+
+ /** Holds the main label instance. */
+ QIRichTextLabel *m_pLabelMain;
+
+ /** Holds the provider layout instance. */
+ QGridLayout *m_pProviderLayout;
+ /** Holds the provider type label instance. */
+ QLabel *m_pProviderLabel;
+ /** Holds the provider type combo-box instance. */
+ QIComboBox *m_pProviderComboBox;
+
+ /** Holds the description label instance. */
+ QIRichTextLabel *m_pLabelDescription;
+
+ /** Holds the options layout instance. */
+ QGridLayout *m_pOptionsLayout;
+ /** Holds the profile label instance. */
+ QLabel *m_pProfileLabel;
+ /** Holds the profile combo-box instance. */
+ QIComboBox *m_pProfileComboBox;
+ /** Holds the profile management tool-button instance. */
+ QIToolButton *m_pProfileToolButton;
+ /** Holds the source image label instance. */
+ QLabel *m_pSourceImageLabel;
+ /** Holds the source tab-bar instance. */
+ QTabBar *m_pSourceTabBar;
+ /** Holds the source image list instance. */
+ QListWidget *m_pSourceImageList;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newcloudvm_UIWizardNewCloudVMPageSource_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVD.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVD.cpp
new file mode 100644
index 00000000..bf551b28
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVD.cpp
@@ -0,0 +1,226 @@
+/* $Id: UIWizardNewVD.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVD class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIModalWindowManager.h"
+#include "UINotificationCenter.h"
+#include "UIWizardNewVD.h"
+#include "UIWizardNewVDFileTypePage.h"
+#include "UIWizardNewVDVariantPage.h"
+#include "UIWizardNewVDSizeLocationPage.h"
+#include "UIWizardNewVDExpertPage.h"
+
+UIWizardNewVD::UIWizardNewVD(QWidget *pParent,
+ const QString &strDefaultName,
+ const QString &strDefaultPath,
+ qulonglong uDefaultSize,
+ WizardMode mode)
+ : UINativeWizard(pParent, WizardType_NewVD, mode, "create-virtual-hard-disk-image" /* help keyword */)
+ , m_strDefaultName(strDefaultName)
+ , m_strDefaultPath(strDefaultPath)
+ , m_uDefaultSize(uDefaultSize)
+ , m_iMediumVariantPageIndex(-1)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign watermark: */
+ setPixmapName(":/wizard_new_harddisk.png");
+#else /* VBOX_WS_MAC */
+ /* Assign background image: */
+ setPixmapName(":/wizard_new_harddisk_bg.png");
+#endif /* VBOX_WS_MAC */
+}
+
+qulonglong UIWizardNewVD::mediumVariant() const
+{
+ return m_uMediumVariant;
+}
+
+void UIWizardNewVD::setMediumVariant(qulonglong uMediumVariant)
+{
+ m_uMediumVariant = uMediumVariant;
+}
+
+const CMediumFormat &UIWizardNewVD::mediumFormat()
+{
+ return m_comMediumFormat;
+}
+
+void UIWizardNewVD::setMediumFormat(const CMediumFormat &mediumFormat)
+{
+ m_comMediumFormat = mediumFormat;
+ if (mode() == WizardMode_Basic)
+ setMediumVariantPageVisibility();
+}
+
+const QString &UIWizardNewVD::mediumPath() const
+{
+ return m_strMediumPath;
+}
+
+void UIWizardNewVD::setMediumPath(const QString &strMediumPath)
+{
+ m_strMediumPath = strMediumPath;
+}
+
+qulonglong UIWizardNewVD::mediumSize() const
+{
+ return m_uMediumSize;
+}
+
+void UIWizardNewVD::setMediumSize(qulonglong uMediumSize)
+{
+ m_uMediumSize = uMediumSize;
+}
+
+QUuid UIWizardNewVD::mediumId() const
+{
+ return m_uMediumId;
+}
+
+void UIWizardNewVD::populatePages()
+{
+ switch (mode())
+ {
+ case WizardMode_Basic:
+ {
+ addPage(new UIWizardNewVDFileTypePage);
+ m_iMediumVariantPageIndex = addPage(new UIWizardNewVDVariantPage);
+ addPage(new UIWizardNewVDSizeLocationPage(m_strDefaultName, m_strDefaultPath, m_uDefaultSize));
+ break;
+ }
+ case WizardMode_Expert:
+ {
+ addPage(new UIWizardNewVDExpertPage(m_strDefaultName, m_strDefaultPath, m_uDefaultSize));
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid mode: %d", mode()));
+ break;
+ }
+ }
+}
+
+bool UIWizardNewVD::createVirtualDisk()
+{
+ AssertReturn(!m_strMediumPath.isNull(), false);
+ AssertReturn(m_uMediumSize > 0, false);
+
+ /* Get VBox object: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Create new virtual disk image: */
+ CMedium comVirtualDisk = comVBox.CreateMedium(m_comMediumFormat.GetName(),
+ m_strMediumPath, KAccessMode_ReadWrite, KDeviceType_HardDisk);
+ if (!comVBox.isOk())
+ {
+ UINotificationMessage::cannotCreateMediumStorage(comVBox, m_strMediumPath, notificationCenter());
+ return false;
+ }
+
+ /* Compose medium-variant: */
+ QVector<KMediumVariant> variants(sizeof(qulonglong) * 8);
+ for (int i = 0; i < variants.size(); ++i)
+ {
+ qulonglong temp = m_uMediumVariant;
+ temp &= Q_UINT64_C(1) << i;
+ variants[i] = (KMediumVariant)temp;
+ }
+
+ UINotificationProgressMediumCreate *pNotification = new UINotificationProgressMediumCreate(comVirtualDisk,
+ m_uMediumSize,
+ variants);
+ connect(pNotification, &UINotificationProgressMediumCreate::sigMediumCreated,
+ &uiCommon(), &UICommon::sltHandleMediumCreated);
+
+ m_uMediumId = comVirtualDisk.GetId();
+
+ gpNotificationCenter->append(pNotification);
+
+ /* Positive: */
+ return true;
+}
+
+/* static */
+QUuid UIWizardNewVD::createVDWithWizard(QWidget *pParent,
+ const QString &strMachineFolder /* = QString() */,
+ const QString &strMachineName /* = QString() */,
+ const QString &strMachineGuestOSTypeId /* = QString() */)
+{
+ /* Initialize variables: */
+ QString strDefaultFolder = strMachineFolder;
+ if (strDefaultFolder.isEmpty())
+ strDefaultFolder = uiCommon().defaultFolderPathForType(UIMediumDeviceType_HardDisk);
+
+ /* In case we dont have a 'guest os type id' default back to 'Other': */
+ const CGuestOSType comGuestOSType = uiCommon().virtualBox().GetGuestOSType( !strMachineGuestOSTypeId.isEmpty()
+ ? strMachineGuestOSTypeId
+ : "Other");
+ const QString strDiskName = uiCommon().findUniqueFileName(strDefaultFolder, !strMachineName.isEmpty()
+ ? strMachineName
+ : "NewVirtualDisk");
+
+ /* Show New VD wizard: */
+ UISafePointerWizardNewVD pWizard = new UIWizardNewVD(pParent,
+ strDiskName,
+ strDefaultFolder,
+ comGuestOSType.GetRecommendedHDD());
+ if (!pWizard)
+ return QUuid();
+ QWidget *pDialogParent = windowManager().realParentWindow(pParent);
+ windowManager().registerNewParent(pWizard, pDialogParent);
+ QUuid mediumId = pWizard->mediumId();
+ pWizard->exec();
+ delete pWizard;
+ return mediumId;
+}
+
+void UIWizardNewVD::retranslateUi()
+{
+ UINativeWizard::retranslateUi();
+ setWindowTitle(tr("Create Virtual Hard Disk"));
+}
+
+void UIWizardNewVD::setMediumVariantPageVisibility()
+{
+ AssertReturnVoid(!m_comMediumFormat.isNull());
+ ULONG uCapabilities = 0;
+ QVector<KMediumFormatCapabilities> capabilities;
+ capabilities = m_comMediumFormat.GetCapabilities();
+ for (int i = 0; i < capabilities.size(); i++)
+ uCapabilities |= capabilities[i];
+
+ int cTest = 0;
+ if (uCapabilities & KMediumFormatCapabilities_CreateDynamic)
+ ++cTest;
+ if (uCapabilities & KMediumFormatCapabilities_CreateFixed)
+ ++cTest;
+ if (uCapabilities & KMediumFormatCapabilities_CreateSplit2G)
+ ++cTest;
+ setPageVisible(m_iMediumVariantPageIndex, cTest > 1);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVD.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVD.h
new file mode 100644
index 00000000..96b4de19
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVD.h
@@ -0,0 +1,105 @@
+/* $Id: UIWizardNewVD.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVD class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVD_h
+#define FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVD_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMedium.h"
+#include "CMediumFormat.h"
+
+/** New Virtual Disk wizard. */
+class SHARED_LIBRARY_STUFF UIWizardNewVD : public UINativeWizard
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVD(QWidget *pParent,
+ const QString &strDefaultName, const QString &strDefaultPath,
+ qulonglong uDefaultSize, WizardMode mode = WizardMode_Auto);
+
+ bool createVirtualDisk();
+
+ /** Creates and shows a UIWizardNewVD wizard.
+ * @param pParent Passes the parent of the wizard,
+ * @param strMachineFolder Passes the machine folder,
+ * @param strMachineName Passes the name of the machine,
+ * @param strMachineGuestOSTypeId Passes the string of machine's guest OS type ID,
+ * returns QUuid of the created medium. */
+ static QUuid createVDWithWizard(QWidget *pParent,
+ const QString &strMachineFolder = QString(),
+ const QString &strMachineName = QString(),
+ const QString &strMachineGuestOSTypeId = QString());
+
+ /** @name Setter/getters for virtual disk parameters
+ * @{ */
+ qulonglong mediumVariant() const;
+ void setMediumVariant(qulonglong uMediumVariant);
+
+ const CMediumFormat &mediumFormat();
+ void setMediumFormat(const CMediumFormat &mediumFormat);
+
+ const QString &mediumPath() const;
+ void setMediumPath(const QString &strMediumPath);
+
+ qulonglong mediumSize() const;
+ void setMediumSize(qulonglong mediumSize);
+
+ QUuid mediumId() const;
+ /** @} */
+
+protected:
+
+ virtual void populatePages() /* final override */;
+
+private:
+
+ void retranslateUi();
+ /** Check medium capabilities and decide if medium variant page should be hidden. */
+ void setMediumVariantPageVisibility();
+ qulonglong m_uMediumVariant;
+ CMediumFormat m_comMediumFormat;
+ QString m_strMediumPath;
+ qulonglong m_uMediumSize;
+ QString m_strDefaultName;
+ QString m_strDefaultPath;
+ qulonglong m_uDefaultSize;
+ int m_iMediumVariantPageIndex;
+ QUuid m_uMediumId;
+};
+
+typedef QPointer<UIWizardNewVD> UISafePointerWizardNewVD;
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVD_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDExpertPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDExpertPage.cpp
new file mode 100644
index 00000000..81918a24
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDExpertPage.cpp
@@ -0,0 +1,229 @@
+/* $Id: UIWizardNewVDExpertPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVDExpertPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QGridLayout>
+
+/* GUI includes: */
+#include "UIWizardDiskEditors.h"
+#include "UIWizardNewVDExpertPage.h"
+#include "UIWizardNewVD.h"
+#include "UICommon.h"
+#include "UINotificationCenter.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+UIWizardNewVDExpertPage::UIWizardNewVDExpertPage(const QString &strDefaultName, const QString &strDefaultPath, qulonglong uDefaultSize)
+ : UINativeWizardPage()
+ , m_pSizeAndPathGroup(0)
+ , m_pFormatComboBox(0)
+ , m_pVariantWidget(0)
+ , m_pFormatVariantGroupBox(0)
+ , m_strDefaultName(strDefaultName)
+ , m_strDefaultPath(strDefaultPath)
+ , m_uDefaultSize(uDefaultSize)
+ , m_uMediumSizeMin(_4M)
+ , m_uMediumSizeMax(uiCommon().virtualBox().GetSystemProperties().GetInfoVDSize())
+{
+ prepare();
+}
+
+void UIWizardNewVDExpertPage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ m_pSizeAndPathGroup = new UIMediumSizeAndPathGroupBox(true /* fExpertMode */, 0 /* parent */, _4M /* minimum size */);
+ m_pFormatComboBox = new UIDiskFormatsComboBox(true /* fExpertMode */, KDeviceType_HardDisk, 0);
+ m_pVariantWidget = new UIDiskVariantWidget(0);
+
+ m_pFormatVariantGroupBox = new QGroupBox;
+ QHBoxLayout *pFormatVariantLayout = new QHBoxLayout(m_pFormatVariantGroupBox);
+ pFormatVariantLayout->addWidget(m_pFormatComboBox, 0, Qt::AlignTop);
+ pFormatVariantLayout->addWidget(m_pVariantWidget);
+
+ pMainLayout->addWidget(m_pSizeAndPathGroup);
+ pMainLayout->addWidget(m_pFormatVariantGroupBox);
+
+ connect(m_pFormatComboBox, &UIDiskFormatsComboBox::sigMediumFormatChanged,
+ this, &UIWizardNewVDExpertPage::sltMediumFormatChanged);
+ connect(m_pVariantWidget, &UIDiskVariantWidget::sigMediumVariantChanged,
+ this, &UIWizardNewVDExpertPage::sltMediumVariantChanged);
+ connect(m_pSizeAndPathGroup, &UIMediumSizeAndPathGroupBox::sigMediumLocationButtonClicked,
+ this, &UIWizardNewVDExpertPage::sltSelectLocationButtonClicked);
+ connect(m_pSizeAndPathGroup, &UIMediumSizeAndPathGroupBox::sigMediumSizeChanged,
+ this, &UIWizardNewVDExpertPage::sltMediumSizeChanged);
+ connect(m_pSizeAndPathGroup, &UIMediumSizeAndPathGroupBox::sigMediumPathChanged,
+ this, &UIWizardNewVDExpertPage::sltMediumPathChanged);
+
+ retranslateUi();
+}
+
+void UIWizardNewVDExpertPage::sltMediumSizeChanged(qulonglong uSize)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVD>());
+ wizardWindow<UIWizardNewVD>()->setMediumSize(uSize);
+ emit completeChanged();
+}
+
+void UIWizardNewVDExpertPage::sltMediumPathChanged(const QString &strPath)
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturnVoid(pWizard);
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(strPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk));
+ pWizard->setMediumPath(strMediumPath);
+ emit completeChanged();
+}
+
+void UIWizardNewVDExpertPage::sltMediumVariantChanged(qulonglong uVariant)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVD>());
+ wizardWindow<UIWizardNewVD>()->setMediumVariant(uVariant);
+ emit completeChanged();
+}
+
+void UIWizardNewVDExpertPage::sltMediumFormatChanged()
+{
+ AssertReturnVoid(m_pFormatComboBox);
+ AssertReturnVoid(wizardWindow<UIWizardNewVD>());
+ wizardWindow<UIWizardNewVD>()->setMediumFormat(m_pFormatComboBox->mediumFormat());
+ updateDiskWidgetsAfterMediumFormatChange();
+ completeChanged();
+}
+
+void UIWizardNewVDExpertPage::sltSelectLocationButtonClicked()
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturnVoid(pWizard);
+ CMediumFormat comMediumFormat(pWizard->mediumFormat());
+ QString strSelectedPath =
+ UIWizardDiskEditors::openFileDialogForDiskFile(pWizard->mediumPath(), comMediumFormat,
+ KDeviceType_HardDisk, pWizard);
+ if (strSelectedPath.isEmpty())
+ return;
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(strSelectedPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk));
+ QFileInfo mediumPath(strMediumPath);
+ m_pSizeAndPathGroup->setMediumFilePath(QDir::toNativeSeparators(mediumPath.absoluteFilePath()));
+ emit completeChanged();
+}
+
+void UIWizardNewVDExpertPage::retranslateUi()
+{
+ if (m_pFormatVariantGroupBox)
+ m_pFormatVariantGroupBox->setTitle(UIWizardNewVD::tr("Hard Disk File &Type and Variant"));
+}
+
+void UIWizardNewVDExpertPage::initializePage()
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturnVoid(pWizard);
+ /* First set the medium format of the wizard: */
+ AssertReturnVoid(m_pFormatComboBox);
+ const CMediumFormat &comMediumFormat = m_pFormatComboBox->mediumFormat();
+ AssertReturnVoid(!comMediumFormat.isNull());
+ pWizard->setMediumFormat(comMediumFormat);
+
+ QString strExtension = UIWizardDiskEditors::defaultExtension(comMediumFormat, KDeviceType_HardDisk);
+ QString strMediumFilePath =
+ UIWizardDiskEditors::constructMediumFilePath(UIWizardDiskEditors::appendExtension(m_strDefaultName,
+ strExtension), m_strDefaultPath);
+ m_pSizeAndPathGroup->blockSignals(true);
+ m_pSizeAndPathGroup->setMediumFilePath(strMediumFilePath);
+ m_pSizeAndPathGroup->blockSignals(false);
+ pWizard->setMediumPath(m_pSizeAndPathGroup->mediumFilePath());
+
+ m_pSizeAndPathGroup->blockSignals(true);
+ m_pSizeAndPathGroup->setMediumSize(m_uDefaultSize > m_uMediumSizeMin && m_uDefaultSize < m_uMediumSizeMax ? m_uDefaultSize : m_uMediumSizeMin);
+ m_pSizeAndPathGroup->blockSignals(false);
+ pWizard->setMediumSize(m_pSizeAndPathGroup->mediumSize());
+
+ m_pVariantWidget->blockSignals(true);
+ m_pVariantWidget->updateMediumVariantWidgetsAfterFormatChange(comMediumFormat);
+ m_pVariantWidget->blockSignals(false);
+
+ pWizard->setMediumVariant(m_pVariantWidget->mediumVariant());
+
+ retranslateUi();
+}
+
+bool UIWizardNewVDExpertPage::isComplete() const
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturn(pWizard, false);
+ if (pWizard->mediumFormat().isNull())
+ return false;
+ if (pWizard->mediumVariant() == (qulonglong)KMediumVariant_Max)
+ return false;
+ if (pWizard->mediumPath().isEmpty())
+ return false;
+ if (pWizard->mediumSize() > m_uMediumSizeMax || pWizard->mediumSize() < m_uMediumSizeMin)
+ return false;
+ return true;
+}
+
+bool UIWizardNewVDExpertPage::validatePage()
+{
+ bool fResult = true;
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturn(pWizard, false);
+ /* Make sure such file doesn't exist already: */
+ const QString strMediumPath(pWizard->mediumPath());
+ fResult = !QFileInfo(strMediumPath).exists();
+ if (!fResult)
+ {
+ UINotificationMessage::cannotOverwriteMediumStorage(strMediumPath, wizard()->notificationCenter());
+ return fResult;
+ }
+
+ /* Make sure we are passing FAT size limitation: */
+ fResult = UIWizardDiskEditors::checkFATSizeLimitation(pWizard->mediumVariant(),
+ pWizard->mediumPath(),
+ pWizard->mediumSize());
+ if (!fResult)
+ {
+ UINotificationMessage::cannotCreateMediumStorageInFAT(strMediumPath, wizard()->notificationCenter());
+ return fResult;
+ }
+
+ fResult = pWizard->createVirtualDisk();
+ return fResult;
+}
+
+void UIWizardNewVDExpertPage::updateDiskWidgetsAfterMediumFormatChange()
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturnVoid(pWizard && m_pVariantWidget && m_pSizeAndPathGroup && m_pFormatComboBox);
+ const CMediumFormat &comMediumFormat = pWizard->mediumFormat();
+ AssertReturnVoid(!comMediumFormat.isNull());
+
+ m_pVariantWidget->updateMediumVariantWidgetsAfterFormatChange(comMediumFormat);
+ m_pSizeAndPathGroup->updateMediumPath(comMediumFormat, m_pFormatComboBox->formatExtensions(), KDeviceType_HardDisk);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDExpertPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDExpertPage.h
new file mode 100644
index 00000000..c65ddf9c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDExpertPage.h
@@ -0,0 +1,92 @@
+/* $Id: UIWizardNewVDExpertPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVDExpertPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDExpertPage_h
+#define FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDExpertPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QGroupBox;
+class UIDiskFormatsComboBox;
+class UIDiskVariantWidget;
+class UIMediumSizeAndPathGroupBox;
+
+/** Expert page of the New Virtual Hard Drive wizard. */
+class SHARED_LIBRARY_STUFF UIWizardNewVDExpertPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVDExpertPage(const QString &strDefaultName, const QString &strDefaultPath, qulonglong uDefaultSize);
+
+private slots:
+
+ void sltMediumFormatChanged();
+ void sltSelectLocationButtonClicked();
+ void sltMediumVariantChanged(qulonglong uVariant);
+ void sltMediumPathChanged(const QString &strPath);
+ void sltMediumSizeChanged(qulonglong uSize);
+
+private:
+
+ /** Translation stuff. */
+ void retranslateUi();
+
+ /** Prepare stuff. */
+ void prepare();
+ void initializePage();
+
+ /** Validation stuff. */
+ bool isComplete() const;
+ bool validatePage();
+ void updateDiskWidgetsAfterMediumFormatChange();
+
+ /** @name Widgets
+ * @{ */
+ UIMediumSizeAndPathGroupBox *m_pSizeAndPathGroup;
+ UIDiskFormatsComboBox *m_pFormatComboBox;
+ UIDiskVariantWidget *m_pVariantWidget;
+ QGroupBox *m_pFormatVariantGroupBox;
+ /** @} */
+
+ /** @name Variable
+ * @{ */
+ QString m_strDefaultName;
+ QString m_strDefaultPath;
+ qulonglong m_uDefaultSize;
+ qulonglong m_uMediumSizeMin;
+ qulonglong m_uMediumSizeMax;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDExpertPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDFileTypePage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDFileTypePage.cpp
new file mode 100644
index 00000000..d399c28f
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDFileTypePage.cpp
@@ -0,0 +1,87 @@
+/* $Id: UIWizardNewVDFileTypePage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVDFileTypePage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIWizardDiskEditors.h"
+#include "UIWizardNewVDFileTypePage.h"
+#include "UIWizardNewVD.h"
+#include "QIRichTextLabel.h"
+
+UIWizardNewVDFileTypePage::UIWizardNewVDFileTypePage()
+ : m_pLabel(0)
+ , m_pFormatButtonGroup(0)
+{
+ prepare();
+}
+
+void UIWizardNewVDFileTypePage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ m_pLabel = new QIRichTextLabel(this);
+ pMainLayout->addWidget(m_pLabel);
+ m_pFormatButtonGroup = new UIDiskFormatsGroupBox(false, KDeviceType_HardDisk, 0);
+ pMainLayout->addWidget(m_pFormatButtonGroup, false);
+
+ pMainLayout->addStretch();
+ connect(m_pFormatButtonGroup, &UIDiskFormatsGroupBox::sigMediumFormatChanged,
+ this, &UIWizardNewVDFileTypePage::sltMediumFormatChanged);
+ retranslateUi();
+}
+
+void UIWizardNewVDFileTypePage::sltMediumFormatChanged()
+{
+ AssertReturnVoid(m_pFormatButtonGroup);
+ wizardWindow<UIWizardNewVD>()->setMediumFormat(m_pFormatButtonGroup->mediumFormat());
+ emit completeChanged();
+}
+
+void UIWizardNewVDFileTypePage::retranslateUi()
+{
+ setTitle(UIWizardNewVD::tr("Virtual Hard disk file type"));
+ m_pLabel->setText(UIWizardNewVD::tr("Please choose the type of file that you would like to use "
+ "for the new virtual hard disk. If you do not need to use it "
+ "with other virtualization software you can leave this setting unchanged."));
+}
+
+void UIWizardNewVDFileTypePage::initializePage()
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVD>());
+ retranslateUi();
+ if (m_pFormatButtonGroup)
+ wizardWindow<UIWizardNewVD>()->setMediumFormat(m_pFormatButtonGroup->mediumFormat());
+}
+
+bool UIWizardNewVDFileTypePage::isComplete() const
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ if (pWizard && !pWizard->mediumFormat().isNull())
+ return true;
+ return false;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDFileTypePage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDFileTypePage.h
new file mode 100644
index 00000000..e0438652
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDFileTypePage.h
@@ -0,0 +1,69 @@
+/* $Id: UIWizardNewVDFileTypePage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVDFileTypePage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDFileTypePage_h
+#define FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDFileTypePage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QIRichTextLabel;
+class UIDiskFormatsGroupBox;
+
+/** 1st page of the New Virtual Hard Drive wizard (basic extension). */
+class SHARED_LIBRARY_STUFF UIWizardNewVDFileTypePage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor. */
+ UIWizardNewVDFileTypePage();
+
+private slots:
+
+ void sltMediumFormatChanged();
+
+private:
+
+ void retranslateUi();
+ void prepare();
+ void initializePage();
+
+ /** Validation stuff. */
+ bool isComplete() const;
+
+ QIRichTextLabel *m_pLabel;
+ UIDiskFormatsGroupBox *m_pFormatButtonGroup;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDFileTypePage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDSizeLocationPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDSizeLocationPage.cpp
new file mode 100644
index 00000000..ceb29cb9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDSizeLocationPage.cpp
@@ -0,0 +1,182 @@
+/* $Id: UIWizardNewVDSizeLocationPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVDSizeLocationPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QDir>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIWizardNewVDSizeLocationPage.h"
+#include "UIWizardNewVD.h"
+#include "UICommon.h"
+#include "UINotificationCenter.h"
+#include "UIWizardDiskEditors.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+UIWizardNewVDSizeLocationPage::UIWizardNewVDSizeLocationPage(const QString &strDefaultName,
+ const QString &strDefaultPath, qulonglong uDefaultSize)
+ : m_pMediumSizePathGroup(0)
+ , m_uMediumSizeMin(_4M)
+ , m_uMediumSizeMax(uiCommon().virtualBox().GetSystemProperties().GetInfoVDSize())
+ , m_strDefaultName(strDefaultName.isEmpty() ? QString("NewVirtualDisk1") : strDefaultName)
+ , m_strDefaultPath(strDefaultPath)
+ , m_uDefaultSize(uDefaultSize)
+{
+ prepare();
+}
+
+void UIWizardNewVDSizeLocationPage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ AssertReturnVoid(pMainLayout);
+ m_pMediumSizePathGroup = new UIMediumSizeAndPathGroupBox(false /* fExpertMode */, 0 /* parent */, _4M /* minimum size */);
+ connect(m_pMediumSizePathGroup, &UIMediumSizeAndPathGroupBox::sigMediumSizeChanged,
+ this, &UIWizardNewVDSizeLocationPage::sltMediumSizeChanged);
+ connect(m_pMediumSizePathGroup, &UIMediumSizeAndPathGroupBox::sigMediumPathChanged,
+ this, &UIWizardNewVDSizeLocationPage::sltMediumPathChanged);
+ connect(m_pMediumSizePathGroup, &UIMediumSizeAndPathGroupBox::sigMediumLocationButtonClicked,
+ this, &UIWizardNewVDSizeLocationPage::sltSelectLocationButtonClicked);
+ pMainLayout->addWidget(m_pMediumSizePathGroup);
+ pMainLayout->addStretch();
+ retranslateUi();
+}
+
+void UIWizardNewVDSizeLocationPage::sltSelectLocationButtonClicked()
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturnVoid(pWizard);
+ QString strSelectedPath =
+ UIWizardDiskEditors::openFileDialogForDiskFile(pWizard->mediumPath(), pWizard->mediumFormat(),
+ KDeviceType_HardDisk, pWizard);
+
+ if (strSelectedPath.isEmpty())
+ return;
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(strSelectedPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk));
+ QFileInfo mediumPath(strMediumPath);
+ m_pMediumSizePathGroup->setMediumFilePath(QDir::toNativeSeparators(mediumPath.absoluteFilePath()));
+}
+
+void UIWizardNewVDSizeLocationPage::sltMediumSizeChanged(qulonglong uSize)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVD>());
+ m_userModifiedParameters << "MediumSize";
+ wizardWindow<UIWizardNewVD>()->setMediumSize(uSize);
+ emit completeChanged();
+}
+
+void UIWizardNewVDSizeLocationPage::sltMediumPathChanged(const QString &strPath)
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturnVoid(pWizard);
+ m_userModifiedParameters << "MediumPath";
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(strPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk));
+ pWizard->setMediumPath(strMediumPath);
+ emit completeChanged();
+}
+
+void UIWizardNewVDSizeLocationPage::retranslateUi()
+{
+ setTitle(UIWizardNewVD::tr("File location and size"));
+}
+
+void UIWizardNewVDSizeLocationPage::initializePage()
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturnVoid(pWizard && m_pMediumSizePathGroup);
+
+ QString strExtension = UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk);
+ QString strMediumFilePath;
+ /* Initialize the medium file path with default name and path if user has not exclusively modified them yet: */
+ if (!m_userModifiedParameters.contains("MediumPath"))
+ strMediumFilePath =
+ UIWizardDiskEditors::constructMediumFilePath(UIWizardDiskEditors::appendExtension(m_strDefaultName,
+ strExtension), m_strDefaultPath);
+ /* Initialize the medium file path with file path and file name from the location editor. This part is to update the
+ * file extention correctly in case user has gone back and changed the file format after modifying medium file path: */
+ else
+ strMediumFilePath =
+ UIWizardDiskEditors::constructMediumFilePath(UIWizardDiskEditors::appendExtension(m_pMediumSizePathGroup->mediumName(),
+ strExtension), m_pMediumSizePathGroup->mediumPath());
+ m_pMediumSizePathGroup->blockSignals(true);
+ m_pMediumSizePathGroup->setMediumFilePath(strMediumFilePath);
+ m_pMediumSizePathGroup->blockSignals(false);
+ pWizard->setMediumPath(m_pMediumSizePathGroup->mediumFilePath());
+
+ if (!m_userModifiedParameters.contains("MediumSize"))
+ {
+ m_pMediumSizePathGroup->blockSignals(true);
+ m_pMediumSizePathGroup->setMediumSize(m_uDefaultSize > m_uMediumSizeMin && m_uDefaultSize < m_uMediumSizeMax ? m_uDefaultSize : m_uMediumSizeMin);
+ m_pMediumSizePathGroup->blockSignals(false);
+ pWizard->setMediumSize(m_pMediumSizePathGroup->mediumSize());
+ }
+ retranslateUi();
+}
+
+bool UIWizardNewVDSizeLocationPage::isComplete() const
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturn(pWizard, false);
+ if (pWizard->mediumPath().isEmpty())
+ return false;
+ if (pWizard->mediumSize() > m_uMediumSizeMax || pWizard->mediumSize() < m_uMediumSizeMin)
+ return false;
+ return true;
+}
+
+bool UIWizardNewVDSizeLocationPage::validatePage()
+{
+ bool fResult = true;
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturn(pWizard, false);
+ /* Make sure such file doesn't exist already: */
+ const QString strMediumPath(pWizard->mediumPath());
+ fResult = !QFileInfo(strMediumPath).exists();
+ if (!fResult)
+ {
+ UINotificationMessage::cannotOverwriteMediumStorage(strMediumPath, wizard()->notificationCenter());
+ return fResult;
+ }
+
+ /* Make sure we are passing FAT size limitation: */
+ fResult = UIWizardDiskEditors::checkFATSizeLimitation(pWizard->mediumVariant(),
+ pWizard->mediumPath(),
+ pWizard->mediumSize());
+ if (!fResult)
+ {
+ UINotificationMessage::cannotCreateMediumStorageInFAT(strMediumPath, wizard()->notificationCenter());
+ return fResult;
+ }
+
+ fResult = pWizard->createVirtualDisk();
+ return fResult;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDSizeLocationPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDSizeLocationPage.h
new file mode 100644
index 00000000..8417881b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDSizeLocationPage.h
@@ -0,0 +1,72 @@
+/* $Id: UIWizardNewVDSizeLocationPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVDSizeLocationPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDSizeLocationPage_h
+#define FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDSizeLocationPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class UIMediumSizeAndPathGroupBox;
+
+class SHARED_LIBRARY_STUFF UIWizardNewVDSizeLocationPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVDSizeLocationPage(const QString &strDefaultName, const QString &strDefaultPath, qulonglong uDefaultSize);
+
+private slots:
+
+ void sltSelectLocationButtonClicked();
+ void sltMediumSizeChanged(qulonglong uSize);
+ void sltMediumPathChanged(const QString &strPath);
+
+private:
+
+ void retranslateUi();
+ void initializePage();
+ bool isComplete() const;
+ bool validatePage();
+ void prepare();
+
+ UIMediumSizeAndPathGroupBox *m_pMediumSizePathGroup;
+ qulonglong m_uMediumSizeMin;
+ qulonglong m_uMediumSizeMax;
+ QString m_strDefaultName;
+ QString m_strDefaultPath;
+ qulonglong m_uDefaultSize;
+ QSet<QString> m_userModifiedParameters;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDSizeLocationPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDVariantPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDVariantPage.cpp
new file mode 100644
index 00000000..b0c0bc85
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDVariantPage.cpp
@@ -0,0 +1,127 @@
+/* $Id: UIWizardNewVDVariantPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVDVariantPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UIWizardDiskEditors.h"
+#include "UIWizardNewVDVariantPage.h"
+#include "UIWizardNewVD.h"
+#include "QIRichTextLabel.h"
+
+/* COM includes: */
+#include "CMediumFormat.h"
+
+UIWizardNewVDVariantPage::UIWizardNewVDVariantPage()
+ : m_pDescriptionLabel(0)
+ , m_pDynamicLabel(0)
+ , m_pFixedLabel(0)
+ , m_pSplitLabel(0)
+ , m_pVariantWidget(0)
+{
+ prepare();
+}
+
+void UIWizardNewVDVariantPage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ AssertReturnVoid(pMainLayout);
+
+ m_pDescriptionLabel = new QIRichTextLabel;
+ m_pDynamicLabel = new QIRichTextLabel;
+ m_pFixedLabel = new QIRichTextLabel;
+ m_pSplitLabel = new QIRichTextLabel;
+
+ pMainLayout->addWidget(m_pDescriptionLabel);
+ pMainLayout->addWidget(m_pDynamicLabel);
+ pMainLayout->addWidget(m_pFixedLabel);
+ pMainLayout->addWidget(m_pSplitLabel);
+
+ m_pVariantWidget = new UIDiskVariantWidget(0);
+ pMainLayout->addWidget(m_pVariantWidget);
+ pMainLayout->addStretch();
+
+ connect(m_pVariantWidget, &UIDiskVariantWidget::sigMediumVariantChanged,
+ this, &UIWizardNewVDVariantPage::sltMediumVariantChanged);
+ retranslateUi();
+}
+
+void UIWizardNewVDVariantPage::retranslateUi()
+{
+ setTitle(UIWizardNewVD::tr("Storage on physical hard disk"));
+
+ if (m_pDescriptionLabel)
+ m_pDescriptionLabel->setText(UIWizardNewVD::tr("Please choose whether the new virtual hard disk file should grow as it is used "
+ "(dynamically allocated) or if it should be created at its maximum size (fixed size)."));
+ if (m_pDynamicLabel)
+ m_pDynamicLabel->setText(UIWizardNewVD::tr("<p>A <b>dynamically allocated</b> hard disk file will only use space "
+ "on your physical hard disk as it fills up (up to a maximum <b>fixed size</b>), "
+ "although it will not shrink again automatically when space on it is freed.</p>"));
+ if (m_pFixedLabel)
+ m_pFixedLabel->setText(UIWizardNewVD::tr("<p>A <b>fixed size</b> hard disk file may take longer to create on some "
+ "systems but is often faster to use.</p>"));
+ if (m_pSplitLabel)
+ m_pSplitLabel->setText(UIWizardNewVD::tr("<p>You can also choose to <b>split</b> the hard disk file into several files "
+ "of up to two gigabytes each. This is mainly useful if you wish to store the "
+ "virtual machine on removable USB devices or old systems, some of which cannot "
+ "handle very large files."));
+}
+
+void UIWizardNewVDVariantPage::initializePage()
+{
+ UIWizardNewVD *pWizard = wizardWindow<UIWizardNewVD>();
+ AssertReturnVoid(pWizard && m_pVariantWidget);
+ setWidgetVisibility(pWizard->mediumFormat());
+ pWizard->setMediumVariant(m_pVariantWidget->mediumVariant());
+ retranslateUi();
+}
+
+bool UIWizardNewVDVariantPage::isComplete() const
+{
+ if (m_pVariantWidget && m_pVariantWidget->mediumVariant() != (qulonglong)KMediumVariant_Max)
+ return true;
+ return false;
+}
+
+void UIWizardNewVDVariantPage::setWidgetVisibility(const CMediumFormat &mediumFormat)
+{
+ AssertReturnVoid(m_pVariantWidget);
+ m_pVariantWidget->updateMediumVariantWidgetsAfterFormatChange(mediumFormat);
+ if (m_pDynamicLabel)
+ m_pDynamicLabel->setHidden(!m_pVariantWidget->isCreateDynamicPossible());
+ if (m_pFixedLabel)
+ m_pFixedLabel->setHidden(!m_pVariantWidget->isCreateFixedPossible());
+ if (m_pSplitLabel)
+ m_pSplitLabel->setHidden(!m_pVariantWidget->isCreateSplitPossible());
+}
+
+void UIWizardNewVDVariantPage::sltMediumVariantChanged(qulonglong uVariant)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVD>());
+ wizardWindow<UIWizardNewVD>()->setMediumVariant(uVariant);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDVariantPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDVariantPage.h
new file mode 100644
index 00000000..7fb4e982
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvd/UIWizardNewVDVariantPage.h
@@ -0,0 +1,69 @@
+/* $Id: UIWizardNewVDVariantPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVDVariantPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDVariantPage_h
+#define FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDVariantPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class CMediumFormat;
+class QIRichTextLabel;
+class UIDiskVariantWidget;
+
+class SHARED_LIBRARY_STUFF UIWizardNewVDVariantPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVDVariantPage();
+
+private slots:
+
+ void sltMediumVariantChanged(qulonglong uVariant);
+
+private:
+
+ void retranslateUi();
+ void initializePage();
+ bool isComplete() const;
+ void prepare();
+ void setWidgetVisibility(const CMediumFormat &mediumFormat);
+
+ QIRichTextLabel *m_pDescriptionLabel;
+ QIRichTextLabel *m_pDynamicLabel;
+ QIRichTextLabel *m_pFixedLabel;
+ QIRichTextLabel *m_pSplitLabel;
+ UIDiskVariantWidget *m_pVariantWidget;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvd_UIWizardNewVDVariantPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVM.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVM.cpp
new file mode 100644
index 00000000..9c491522
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVM.cpp
@@ -0,0 +1,967 @@
+/* $Id: UIWizardNewVM.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVM class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QAbstractButton>
+#include <QLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "UIMedium.h"
+#include "UINotificationCenter.h"
+#include "UIWizardNewVM.h"
+#include "UIWizardNewVMNameOSTypePage.h"
+#include "UIWizardNewVMUnattendedPage.h"
+#include "UIWizardNewVMHardwarePage.h"
+#include "UIWizardNewVMDiskPage.h"
+#include "UIWizardNewVMExpertPage.h"
+#include "UIWizardNewVMSummaryPage.h"
+
+/* COM includes: */
+#include "CAudioAdapter.h"
+#include "CAudioSettings.h"
+#include "CBIOSSettings.h"
+#include "CGraphicsAdapter.h"
+#include "CExtPackManager.h"
+#include "CMediumFormat.h"
+#include "CStorageController.h"
+#include "CUSBController.h"
+#include "CUSBDeviceFilters.h"
+#include "CUnattended.h"
+
+/* Namespaces: */
+using namespace UIExtraDataDefs;
+
+
+UIWizardNewVM::UIWizardNewVM(QWidget *pParent,
+ UIActionPool *pActionPool,
+ const QString &strMachineGroup,
+ CUnattended &comUnattended,
+ const QString &strISOFilePath /* = QString() */)
+ : UINativeWizard(pParent, WizardType_NewVM, WizardMode_Auto, "create-vm-wizard" /* help keyword */)
+ , m_strMachineGroup(strMachineGroup)
+ , m_iIDECount(0)
+ , m_iSATACount(0)
+ , m_iSCSICount(0)
+ , m_iFloppyCount(0)
+ , m_iSASCount(0)
+ , m_iUSBCount(0)
+ , m_fInstallGuestAdditions(false)
+ , m_fSkipUnattendedInstall(false)
+ , m_fEFIEnabled(false)
+ , m_iCPUCount(1)
+ , m_iMemorySize(0)
+ , m_iUnattendedInstallPageIndex(-1)
+ , m_uMediumVariant(0)
+ , m_uMediumSize(0)
+ , m_enmDiskSource(SelectedDiskSource_New)
+ , m_fEmptyDiskRecommended(false)
+ , m_pActionPool(pActionPool)
+ , m_comUnattended(comUnattended)
+ , m_fStartHeadless(false)
+ , m_strInitialISOFilePath(strISOFilePath)
+{
+#ifndef VBOX_WS_MAC
+ /* Assign watermark: */
+ setPixmapName(":/wizard_new_welcome.png");
+#else /* VBOX_WS_MAC */
+ /* Assign background image: */
+ setPixmapName(":/wizard_new_welcome_bg.png");
+#endif /* VBOX_WS_MAC */
+ /* Register classes: */
+ qRegisterMetaType<CGuestOSType>();
+
+ connect(this, &UIWizardNewVM::rejected, this, &UIWizardNewVM::sltHandleWizardCancel);
+}
+
+void UIWizardNewVM::populatePages()
+{
+ switch (mode())
+ {
+ case WizardMode_Basic:
+ {
+ UIWizardNewVMNameOSTypePage *pNamePage = new UIWizardNewVMNameOSTypePage;
+ addPage(pNamePage);
+ if (!m_strInitialISOFilePath.isEmpty())
+ pNamePage->setISOFilePath(m_strInitialISOFilePath);
+ m_iUnattendedInstallPageIndex = addPage(new UIWizardNewVMUnattendedPage);
+ setUnattendedPageVisible(false);
+ addPage(new UIWizardNewVMHardwarePage);
+ addPage(new UIWizardNewVMDiskPage(m_pActionPool));
+ addPage(new UIWizardNewVMSummaryPage);
+ break;
+ }
+ case WizardMode_Expert:
+ {
+ UIWizardNewVMExpertPage *pExpertPage = new UIWizardNewVMExpertPage(m_pActionPool);
+ addPage(pExpertPage);
+ if (!m_strInitialISOFilePath.isEmpty())
+ pExpertPage->setISOFilePath(m_strInitialISOFilePath);
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Invalid mode: %d", mode()));
+ break;
+ }
+ }
+}
+
+void UIWizardNewVM::cleanWizard()
+{
+ /* Try to delete the hard disk in case we have created one: */
+ deleteVirtualDisk();
+ /* Cleanup the machine folder: */
+ UIWizardNewVMNameOSTypeCommon::cleanupMachineFolder(this, true);
+
+ if (!m_machine.isNull())
+ m_machine.detach();
+}
+
+bool UIWizardNewVM::createVM()
+{
+ CVirtualBox vbox = uiCommon().virtualBox();
+ QString strTypeId = m_comGuestOSType.GetId();
+
+ /* Create virtual machine: */
+ if (m_machine.isNull())
+ {
+ QVector<QString> groups;
+ if (!m_strMachineGroup.isEmpty())
+ groups << m_strMachineGroup;
+ m_machine = vbox.CreateMachine(m_strMachineFilePath,
+ m_strMachineBaseName,
+ groups, strTypeId, QString(),
+ QString(), QString(), QString());
+ if (!vbox.isOk())
+ {
+ UINotificationMessage::cannotCreateMachine(vbox, notificationCenter());
+ cleanWizard();
+ return false;
+ }
+ }
+
+#if 0
+ /* Configure the newly created vm here in GUI by several calls to API: */
+ configureVM(strTypeId, m_comGuestOSType);
+#else
+ /* The newer and less tested way of configuring vms: */
+ m_machine.ApplyDefaults(QString());
+ /* Apply user preferences again. IMachine::applyDefaults may have overwritten the user setting: */
+ m_machine.SetMemorySize(m_iMemorySize);
+ int iVPUCount = qMax(1, m_iCPUCount);
+ m_machine.SetCPUCount(iVPUCount);
+ /* Correct the VRAM size since API does not take fullscreen memory requirements into account: */
+ CGraphicsAdapter comGraphics = m_machine.GetGraphicsAdapter();
+ comGraphics.SetVRAMSize(qMax(comGraphics.GetVRAMSize(), (ULONG)(UICommon::requiredVideoMemory(strTypeId) / _1M)));
+ /* Enabled I/O APIC explicitly in we have more than 1 VCPU: */
+ if (iVPUCount > 1)
+ m_machine.GetBIOSSettings().SetIOAPICEnabled(true);
+
+ /* Set recommended firmware type: */
+ m_machine.SetFirmwareType(m_fEFIEnabled ? KFirmwareType_EFI : KFirmwareType_BIOS);
+#endif
+
+ /* Register the VM prior to attaching hard disks: */
+ vbox.RegisterMachine(m_machine);
+ if (!vbox.isOk())
+ {
+ UINotificationMessage::cannotRegisterMachine(vbox, m_machine.GetName(), notificationCenter());
+ cleanWizard();
+ return false;
+ }
+
+ if (!attachDefaultDevices())
+ {
+ cleanWizard();
+ return false;
+ }
+
+ if (isUnattendedEnabled())
+ {
+ m_comUnattended.SetMachine(m_machine);
+ if (!checkUnattendedInstallError(m_comUnattended))
+ {
+ cleanWizard();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool UIWizardNewVM::createVirtualDisk()
+{
+ /* Prepare result: */
+ bool fResult = false;
+
+ /* Check attributes: */
+ AssertReturn(!m_strMediumPath.isNull(), false);
+ AssertReturn(m_uMediumSize > 0, false);
+
+ /* Acquire VBox: */
+ CVirtualBox comVBox = uiCommon().virtualBox();
+
+ /* Create new virtual hard-disk: */
+ CMedium newVirtualDisk = comVBox.CreateMedium(m_comMediumFormat.GetName(), m_strMediumPath, KAccessMode_ReadWrite, KDeviceType_HardDisk);
+ if (!comVBox.isOk())
+ {
+ UINotificationMessage::cannotCreateMediumStorage(comVBox, m_strMediumPath, notificationCenter());
+ return fResult;
+ }
+
+ /* Create base storage for the new virtual-disk: */
+ UINotificationProgressMediumCreate *pNotification = new UINotificationProgressMediumCreate(newVirtualDisk,
+ m_uMediumSize,
+ mediumVariants());
+ if (!handleNotificationProgressNow(pNotification))
+ return fResult;
+
+ /* Inform UICommon about it: */
+ uiCommon().createMedium(UIMedium(newVirtualDisk, UIMediumDeviceType_HardDisk, KMediumState_Created));
+
+ /* Remember created virtual-disk: */
+ m_virtualDisk = newVirtualDisk;
+
+ /* True finally: */
+ fResult = true;
+
+ /* Return result: */
+ return fResult;
+}
+
+void UIWizardNewVM::deleteVirtualDisk()
+{
+ /* Do nothing if an existing disk has been selected: */
+ if (m_enmDiskSource == SelectedDiskSource_Existing)
+ return;
+
+ /* Make sure virtual-disk valid: */
+ if (m_virtualDisk.isNull())
+ return;
+
+ /* Delete storage of existing disk: */
+ UINotificationProgressMediumDeletingStorage *pNotification = new UINotificationProgressMediumDeletingStorage(m_virtualDisk);
+ if (!handleNotificationProgressNow(pNotification))
+ return;
+
+ /* Detach virtual-disk finally: */
+ m_virtualDisk.detach();
+}
+
+void UIWizardNewVM::configureVM(const QString &strGuestTypeId, const CGuestOSType &comGuestType)
+{
+ /* Get graphics adapter: */
+ CGraphicsAdapter comGraphics = m_machine.GetGraphicsAdapter();
+
+ /* RAM size: */
+ m_machine.SetMemorySize(m_iMemorySize);
+
+ /* VCPU count: */
+ int iVPUCount = qMax(1, m_iCPUCount);
+ m_machine.SetCPUCount(iVPUCount);
+
+ /* Enabled I/O APIC explicitly in we have more than 1 VCPU: */
+ if (iVPUCount > 1)
+ m_machine.GetBIOSSettings().SetIOAPICEnabled(true);
+
+ /* Graphics Controller type: */
+ comGraphics.SetGraphicsControllerType(comGuestType.GetRecommendedGraphicsController());
+
+ /* VRAM size - select maximum between recommended and minimum for fullscreen: */
+ comGraphics.SetVRAMSize(qMax(comGuestType.GetRecommendedVRAM(), (ULONG)(UICommon::requiredVideoMemory(strGuestTypeId) / _1M)));
+
+ /* Selecting recommended chipset type: */
+ m_machine.SetChipsetType(comGuestType.GetRecommendedChipset());
+
+ /* Selecting recommended Audio Controller: */
+ CAudioSettings const comAudioSettings = m_machine.GetAudioSettings();
+ CAudioAdapter comAdapter = comAudioSettings.GetAdapter();
+ comAdapter.SetAudioController(comGuestType.GetRecommendedAudioController());
+ /* And the Audio Codec: */
+ comAdapter.SetAudioCodec(comGuestType.GetRecommendedAudioCodec());
+ /* Enabling audio by default: */
+ comAdapter.SetEnabled(true);
+ comAdapter.SetEnabledOut(true);
+
+ /* Enable the OHCI and EHCI controller by default for new VMs. (new in 2.2): */
+ CUSBDeviceFilters usbDeviceFilters = m_machine.GetUSBDeviceFilters();
+ bool fOhciEnabled = false;
+ if (!usbDeviceFilters.isNull() && comGuestType.GetRecommendedUSB3() && m_machine.GetUSBProxyAvailable())
+ {
+ m_machine.AddUSBController("XHCI", KUSBControllerType_XHCI);
+ /* xHCI includes OHCI */
+ fOhciEnabled = true;
+ }
+ if ( !fOhciEnabled
+ && !usbDeviceFilters.isNull() && comGuestType.GetRecommendedUSB() && m_machine.GetUSBProxyAvailable())
+ {
+ m_machine.AddUSBController("OHCI", KUSBControllerType_OHCI);
+ fOhciEnabled = true;
+ m_machine.AddUSBController("EHCI", KUSBControllerType_EHCI);
+ }
+
+ /* Create a floppy controller if recommended: */
+ QString strFloppyName = getNextControllerName(KStorageBus_Floppy);
+ if (comGuestType.GetRecommendedFloppy())
+ {
+ m_machine.AddStorageController(strFloppyName, KStorageBus_Floppy);
+ CStorageController flpCtr = m_machine.GetStorageControllerByName(strFloppyName);
+ flpCtr.SetControllerType(KStorageControllerType_I82078);
+ }
+
+ /* Create recommended DVD storage controller: */
+ KStorageBus strDVDBus = comGuestType.GetRecommendedDVDStorageBus();
+ QString strDVDName = getNextControllerName(strDVDBus);
+ m_machine.AddStorageController(strDVDName, strDVDBus);
+
+ /* Set recommended DVD storage controller type: */
+ CStorageController dvdCtr = m_machine.GetStorageControllerByName(strDVDName);
+ KStorageControllerType dvdStorageControllerType = comGuestType.GetRecommendedDVDStorageController();
+ dvdCtr.SetControllerType(dvdStorageControllerType);
+
+ /* Create recommended HD storage controller if it's not the same as the DVD controller: */
+ KStorageBus ctrHDBus = comGuestType.GetRecommendedHDStorageBus();
+ KStorageControllerType hdStorageControllerType = comGuestType.GetRecommendedHDStorageController();
+ CStorageController hdCtr;
+ QString strHDName;
+ if (ctrHDBus != strDVDBus || hdStorageControllerType != dvdStorageControllerType)
+ {
+ strHDName = getNextControllerName(ctrHDBus);
+ m_machine.AddStorageController(strHDName, ctrHDBus);
+ hdCtr = m_machine.GetStorageControllerByName(strHDName);
+ hdCtr.SetControllerType(hdStorageControllerType);
+ }
+ else
+ {
+ /* The HD controller is the same as DVD: */
+ hdCtr = dvdCtr;
+ strHDName = strDVDName;
+ }
+
+ /* Limit the AHCI port count if it's used because windows has trouble with
+ too many ports and other guest (OS X in particular) may take extra long
+ to boot: */
+ if (hdStorageControllerType == KStorageControllerType_IntelAhci)
+ hdCtr.SetPortCount(1 + (dvdStorageControllerType == KStorageControllerType_IntelAhci));
+ else if (dvdStorageControllerType == KStorageControllerType_IntelAhci)
+ dvdCtr.SetPortCount(1);
+
+ /* Turn on PAE, if recommended: */
+ m_machine.SetCPUProperty(KCPUPropertyType_PAE, comGuestType.GetRecommendedPAE());
+
+ /* Set the recommended triple fault behavior: */
+ m_machine.SetCPUProperty(KCPUPropertyType_TripleFaultReset, comGuestType.GetRecommendedTFReset());
+
+ /* Set recommended firmware type: */
+ m_machine.SetFirmwareType(m_fEFIEnabled ? KFirmwareType_EFI : KFirmwareType_BIOS);
+
+ /* Set recommended human interface device types: */
+ if (comGuestType.GetRecommendedUSBHID())
+ {
+ m_machine.SetKeyboardHIDType(KKeyboardHIDType_USBKeyboard);
+ m_machine.SetPointingHIDType(KPointingHIDType_USBMouse);
+ if (!fOhciEnabled && !usbDeviceFilters.isNull())
+ m_machine.AddUSBController("OHCI", KUSBControllerType_OHCI);
+ }
+
+ if (comGuestType.GetRecommendedUSBTablet())
+ {
+ m_machine.SetPointingHIDType(KPointingHIDType_USBTablet);
+ if (!fOhciEnabled && !usbDeviceFilters.isNull())
+ m_machine.AddUSBController("OHCI", KUSBControllerType_OHCI);
+ }
+
+ /* Set HPET flag: */
+ m_machine.SetHPETEnabled(comGuestType.GetRecommendedHPET());
+
+ /* Set UTC flags: */
+ m_machine.SetRTCUseUTC(comGuestType.GetRecommendedRTCUseUTC());
+
+ /* Set graphic bits: */
+ if (comGuestType.GetRecommended2DVideoAcceleration())
+ comGraphics.SetAccelerate2DVideoEnabled(comGuestType.GetRecommended2DVideoAcceleration());
+
+ if (comGuestType.GetRecommended3DAcceleration())
+ comGraphics.SetAccelerate3DEnabled(comGuestType.GetRecommended3DAcceleration());
+}
+
+bool UIWizardNewVM::attachDefaultDevices()
+{
+ bool success = false;
+ QUuid uMachineId = m_machine.GetId();
+ CSession session = uiCommon().openSession(uMachineId);
+ if (!session.isNull())
+ {
+ CMachine machine = session.GetMachine();
+ if (!m_virtualDisk.isNull())
+ {
+ KStorageBus enmHDDBus = m_comGuestOSType.GetRecommendedHDStorageBus();
+ CStorageController comHDDController = m_machine.GetStorageControllerByInstance(enmHDDBus, 0);
+ if (!comHDDController.isNull())
+ {
+ machine.AttachDevice(comHDDController.GetName(), 0, 0, KDeviceType_HardDisk, m_virtualDisk);
+ if (!machine.isOk())
+ UINotificationMessage::cannotAttachDevice(machine, UIMediumDeviceType_HardDisk, m_strMediumPath,
+ StorageSlot(enmHDDBus, 0, 0), notificationCenter());
+ }
+ }
+
+ /* Attach optical drive: */
+ KStorageBus enmDVDBus = m_comGuestOSType.GetRecommendedDVDStorageBus();
+ CStorageController comDVDController = m_machine.GetStorageControllerByInstance(enmDVDBus, 0);
+ if (!comDVDController.isNull())
+ {
+ CMedium opticalDisk;
+ QString strISOFilePath = ISOFilePath();
+ if (!strISOFilePath.isEmpty() && !isUnattendedEnabled())
+ {
+ CVirtualBox vbox = uiCommon().virtualBox();
+ opticalDisk =
+ vbox.OpenMedium(strISOFilePath, KDeviceType_DVD, KAccessMode_ReadWrite, false);
+ if (!vbox.isOk())
+ UINotificationMessage::cannotOpenMedium(vbox, strISOFilePath, notificationCenter());
+ }
+ machine.AttachDevice(comDVDController.GetName(), 1, 0, KDeviceType_DVD, opticalDisk);
+ if (!machine.isOk())
+ UINotificationMessage::cannotAttachDevice(machine, UIMediumDeviceType_DVD, QString(),
+ StorageSlot(enmDVDBus, 1, 0), notificationCenter());
+ }
+
+ /* Attach an empty floppy drive if recommended */
+ if (m_comGuestOSType.GetRecommendedFloppy()) {
+ CStorageController comFloppyController = m_machine.GetStorageControllerByInstance(KStorageBus_Floppy, 0);
+ if (!comFloppyController.isNull())
+ {
+ machine.AttachDevice(comFloppyController.GetName(), 0, 0, KDeviceType_Floppy, CMedium());
+ if (!machine.isOk())
+ UINotificationMessage::cannotAttachDevice(machine, UIMediumDeviceType_Floppy, QString(),
+ StorageSlot(KStorageBus_Floppy, 0, 0), notificationCenter());
+ }
+ }
+
+ if (machine.isOk())
+ {
+ machine.SaveSettings();
+ if (machine.isOk())
+ success = true;
+ else
+ UINotificationMessage::cannotSaveMachineSettings(machine, notificationCenter());
+ }
+
+ session.UnlockMachine();
+ }
+ if (!success)
+ {
+ /* Unregister VM on failure: */
+ const QVector<CMedium> media = m_machine.Unregister(KCleanupMode_DetachAllReturnHardDisksOnly);
+ if (!m_machine.isOk())
+ UINotificationMessage::cannotRemoveMachine(m_machine, notificationCenter());
+ else
+ {
+ UINotificationProgressMachineMediaRemove *pNotification =
+ new UINotificationProgressMachineMediaRemove(m_machine, media);
+ handleNotificationProgressNow(pNotification);
+ }
+ }
+
+ /* Make sure we detach CMedium wrapper from IMedium pointer to avoid deletion of IMedium as m_virtualDisk
+ * is deallocated. Or in case of UINotificationProgressMachineMediaRemove handling, IMedium has been
+ * already deleted so detach in this case as well. */
+ if (!m_virtualDisk.isNull())
+ m_virtualDisk.detach();
+
+ return success;
+}
+
+void UIWizardNewVM::sltHandleWizardCancel()
+{
+ cleanWizard();
+}
+
+void UIWizardNewVM::retranslateUi()
+{
+ UINativeWizard::retranslateUi();
+ setWindowTitle(tr("Create Virtual Machine"));
+}
+
+QString UIWizardNewVM::getNextControllerName(KStorageBus type)
+{
+ QString strControllerName;
+ switch (type)
+ {
+ case KStorageBus_IDE:
+ {
+ strControllerName = "IDE";
+ ++m_iIDECount;
+ if (m_iIDECount > 1)
+ strControllerName = QString("%1 %2").arg(strControllerName).arg(m_iIDECount);
+ break;
+ }
+ case KStorageBus_SATA:
+ {
+ strControllerName = "SATA";
+ ++m_iSATACount;
+ if (m_iSATACount > 1)
+ strControllerName = QString("%1 %2").arg(strControllerName).arg(m_iSATACount);
+ break;
+ }
+ case KStorageBus_SCSI:
+ {
+ strControllerName = "SCSI";
+ ++m_iSCSICount;
+ if (m_iSCSICount > 1)
+ strControllerName = QString("%1 %2").arg(strControllerName).arg(m_iSCSICount);
+ break;
+ }
+ case KStorageBus_Floppy:
+ {
+ strControllerName = "Floppy";
+ ++m_iFloppyCount;
+ if (m_iFloppyCount > 1)
+ strControllerName = QString("%1 %2").arg(strControllerName).arg(m_iFloppyCount);
+ break;
+ }
+ case KStorageBus_SAS:
+ {
+ strControllerName = "SAS";
+ ++m_iSASCount;
+ if (m_iSASCount > 1)
+ strControllerName = QString("%1 %2").arg(strControllerName).arg(m_iSASCount);
+ break;
+ }
+ case KStorageBus_USB:
+ {
+ strControllerName = "USB";
+ ++m_iUSBCount;
+ if (m_iUSBCount > 1)
+ strControllerName = QString("%1 %2").arg(strControllerName).arg(m_iUSBCount);
+ break;
+ }
+ default:
+ break;
+ }
+ return strControllerName;
+}
+
+QUuid UIWizardNewVM::createdMachineId() const
+{
+ if (m_machine.isOk())
+ return m_machine.GetId();
+ return QUuid();
+}
+
+CMedium &UIWizardNewVM::virtualDisk()
+{
+ return m_virtualDisk;
+}
+
+void UIWizardNewVM::setVirtualDisk(const CMedium &medium)
+{
+ m_virtualDisk = medium;
+}
+
+void UIWizardNewVM::setVirtualDisk(const QUuid &mediumId)
+{
+ if (m_virtualDisk.isOk() && m_virtualDisk.GetId() == mediumId)
+ return;
+ CMedium medium = uiCommon().medium(mediumId).medium();
+ setVirtualDisk(medium);
+}
+
+const QString &UIWizardNewVM::machineGroup() const
+{
+ return m_strMachineGroup;
+}
+
+const QString &UIWizardNewVM::machineFilePath() const
+{
+ return m_strMachineFilePath;
+}
+
+void UIWizardNewVM::setMachineFilePath(const QString &strMachineFilePath)
+{
+ m_strMachineFilePath = strMachineFilePath;
+}
+
+QString UIWizardNewVM::machineFileName() const
+{
+ return QFileInfo(machineFilePath()).completeBaseName();
+}
+
+const QString &UIWizardNewVM::machineFolder() const
+{
+ return m_strMachineFolder;
+}
+
+void UIWizardNewVM::setMachineFolder(const QString &strMachineFolder)
+{
+ m_strMachineFolder = strMachineFolder;
+}
+
+const QString &UIWizardNewVM::machineBaseName() const
+{
+ return m_strMachineBaseName;
+}
+
+void UIWizardNewVM::setMachineBaseName(const QString &strMachineBaseName)
+{
+ m_strMachineBaseName = strMachineBaseName;
+}
+
+const QString &UIWizardNewVM::createdMachineFolder() const
+{
+ return m_strCreatedFolder;
+}
+
+void UIWizardNewVM::setCreatedMachineFolder(const QString &strCreatedMachineFolder)
+{
+ m_strCreatedFolder = strCreatedMachineFolder;
+}
+
+QString UIWizardNewVM::detectedOSTypeId() const
+{
+ AssertReturn(!m_comUnattended.isNull(), QString());
+ return m_comUnattended.GetDetectedOSTypeId();
+}
+
+const QString &UIWizardNewVM::guestOSFamilyId() const
+{
+ return m_strGuestOSFamilyId;
+}
+
+void UIWizardNewVM::setGuestOSFamilyId(const QString &strGuestOSFamilyId)
+{
+ m_strGuestOSFamilyId = strGuestOSFamilyId;
+}
+
+const CGuestOSType &UIWizardNewVM::guestOSType() const
+{
+ return m_comGuestOSType;;
+}
+
+void UIWizardNewVM::setGuestOSType(const CGuestOSType &guestOSType)
+{
+ m_comGuestOSType= guestOSType;
+}
+
+bool UIWizardNewVM::installGuestAdditions() const
+{
+ AssertReturn(!m_comUnattended.isNull(), false);
+ return m_comUnattended.GetInstallGuestAdditions();
+}
+
+void UIWizardNewVM::setInstallGuestAdditions(bool fInstallGA)
+{
+ AssertReturnVoid(!m_comUnattended.isNull());
+ m_comUnattended.SetInstallGuestAdditions(fInstallGA);
+ AssertReturnVoid(checkUnattendedInstallError(m_comUnattended));
+}
+
+bool UIWizardNewVM::startHeadless() const
+{
+ return m_fStartHeadless;
+}
+
+void UIWizardNewVM::setStartHeadless(bool fStartHeadless)
+{
+ m_fStartHeadless = fStartHeadless;
+}
+
+bool UIWizardNewVM::skipUnattendedInstall() const
+{
+ return m_fSkipUnattendedInstall;
+}
+
+void UIWizardNewVM::setSkipUnattendedInstall(bool fSkipUnattendedInstall)
+{
+ m_fSkipUnattendedInstall = fSkipUnattendedInstall;
+ /* We hide/show unattended install page depending on the value of isUnattendedEnabled: */
+ setUnattendedPageVisible(isUnattendedEnabled());
+}
+
+bool UIWizardNewVM::EFIEnabled() const
+{
+ return m_fEFIEnabled;
+}
+
+void UIWizardNewVM::setEFIEnabled(bool fEnabled)
+{
+ m_fEFIEnabled = fEnabled;
+}
+
+QString UIWizardNewVM::ISOFilePath() const
+{
+ AssertReturn(!m_comUnattended.isNull(), QString());
+ return m_comUnattended.GetIsoPath();
+}
+
+void UIWizardNewVM::setISOFilePath(const QString &strISOFilePath)
+{
+ AssertReturnVoid(!m_comUnattended.isNull());
+ m_comUnattended.SetIsoPath(strISOFilePath);
+ AssertReturnVoid(checkUnattendedInstallError(m_comUnattended));
+
+ m_comUnattended.DetectIsoOS();
+
+ const QVector<ULONG> &indices = m_comUnattended.GetDetectedImageIndices();
+ QVector<ulong> qIndices;
+ for (int i = 0; i < indices.size(); ++i)
+ qIndices << indices[i];
+ setDetectedWindowsImageNamesAndIndices(m_comUnattended.GetDetectedImageNames(), qIndices);
+ /* We hide/show unattended install page depending on the value of isUnattendedEnabled: */
+ setUnattendedPageVisible(isUnattendedEnabled());
+}
+
+QString UIWizardNewVM::userName() const
+{
+ AssertReturn(!m_comUnattended.isNull(), QString());
+ return m_comUnattended.GetUser();
+}
+
+void UIWizardNewVM::setUserName(const QString &strUserName)
+{
+ AssertReturnVoid(!m_comUnattended.isNull());
+ m_comUnattended.SetUser(strUserName);
+ AssertReturnVoid(checkUnattendedInstallError(m_comUnattended));
+}
+
+QString UIWizardNewVM::password() const
+{
+ AssertReturn(!m_comUnattended.isNull(), QString());
+ return m_comUnattended.GetPassword();
+}
+
+void UIWizardNewVM::setPassword(const QString &strPassword)
+{
+ AssertReturnVoid(!m_comUnattended.isNull());
+ m_comUnattended.SetPassword(strPassword);
+ AssertReturnVoid(checkUnattendedInstallError(m_comUnattended));
+}
+
+QString UIWizardNewVM::guestAdditionsISOPath() const
+{
+ AssertReturn(!m_comUnattended.isNull(), QString());
+ return m_comUnattended.GetAdditionsIsoPath();
+}
+
+void UIWizardNewVM::setGuestAdditionsISOPath(const QString &strGAISOPath)
+{
+ AssertReturnVoid(!m_comUnattended.isNull());
+ m_comUnattended.SetAdditionsIsoPath(strGAISOPath);
+ AssertReturnVoid(checkUnattendedInstallError(m_comUnattended));
+}
+
+QString UIWizardNewVM::hostnameDomainName() const
+{
+ AssertReturn(!m_comUnattended.isNull(), QString());
+ return m_comUnattended.GetHostname();
+}
+
+void UIWizardNewVM::setHostnameDomainName(const QString &strHostnameDomain)
+{
+ AssertReturnVoid(!m_comUnattended.isNull());
+ m_comUnattended.SetHostname(strHostnameDomain);
+ AssertReturnVoid(checkUnattendedInstallError(m_comUnattended));
+}
+
+QString UIWizardNewVM::productKey() const
+{
+ AssertReturn(!m_comUnattended.isNull(), QString());
+ return m_comUnattended.GetProductKey();
+}
+
+void UIWizardNewVM::setProductKey(const QString &productKey)
+{
+ AssertReturnVoid(!m_comUnattended.isNull());
+ m_comUnattended.SetProductKey(productKey);
+ AssertReturnVoid(checkUnattendedInstallError(m_comUnattended));
+}
+
+int UIWizardNewVM::CPUCount() const
+{
+ return m_iCPUCount;
+}
+
+void UIWizardNewVM::setCPUCount(int iCPUCount)
+{
+ m_iCPUCount = iCPUCount;
+}
+
+int UIWizardNewVM::memorySize() const
+{
+ return m_iMemorySize;
+}
+
+void UIWizardNewVM::setMemorySize(int iMemory)
+{
+ m_iMemorySize = iMemory;
+}
+
+
+qulonglong UIWizardNewVM::mediumVariant() const
+{
+ return m_uMediumVariant;
+}
+
+void UIWizardNewVM::setMediumVariant(qulonglong uMediumVariant)
+{
+ m_uMediumVariant = uMediumVariant;
+}
+
+const CMediumFormat &UIWizardNewVM::mediumFormat()
+{
+ return m_comMediumFormat;
+}
+
+void UIWizardNewVM::setMediumFormat(const CMediumFormat &mediumFormat)
+{
+ m_comMediumFormat = mediumFormat;
+}
+
+const QString &UIWizardNewVM::mediumPath() const
+{
+ return m_strMediumPath;
+}
+
+void UIWizardNewVM::setMediumPath(const QString &strMediumPath)
+{
+ m_strMediumPath = strMediumPath;
+}
+
+qulonglong UIWizardNewVM::mediumSize() const
+{
+ return m_uMediumSize;
+}
+
+void UIWizardNewVM::setMediumSize(qulonglong uMediumSize)
+{
+ m_uMediumSize = uMediumSize;
+}
+
+SelectedDiskSource UIWizardNewVM::diskSource() const
+{
+ return m_enmDiskSource;
+}
+
+void UIWizardNewVM::setDiskSource(SelectedDiskSource enmDiskSource)
+{
+ m_enmDiskSource = enmDiskSource;
+}
+
+bool UIWizardNewVM::emptyDiskRecommended() const
+{
+ return m_fEmptyDiskRecommended;
+}
+
+void UIWizardNewVM::setEmptyDiskRecommended(bool fEmptyDiskRecommended)
+{
+ m_fEmptyDiskRecommended = fEmptyDiskRecommended;
+}
+
+void UIWizardNewVM::setDetectedWindowsImageNamesAndIndices(const QVector<QString> &names, const QVector<ulong> &ids)
+{
+ AssertMsg(names.size() == ids.size(),
+ ("Sizes of the arrays for names and indices of the detected images should be equal."));
+ m_detectedWindowsImageNames = names;
+ m_detectedWindowsImageIndices = ids;
+}
+
+const QVector<QString> &UIWizardNewVM::detectedWindowsImageNames() const
+{
+ return m_detectedWindowsImageNames;
+}
+
+const QVector<ulong> &UIWizardNewVM::detectedWindowsImageIndices() const
+{
+ return m_detectedWindowsImageIndices;
+}
+
+void UIWizardNewVM::setSelectedWindowImageIndex(ulong uIndex)
+{
+ AssertReturnVoid(!m_comUnattended.isNull());
+ m_comUnattended.SetImageIndex(uIndex);
+ AssertReturnVoid(checkUnattendedInstallError(m_comUnattended));
+}
+
+ulong UIWizardNewVM::selectedWindowImageIndex() const
+{
+ AssertReturn(!m_comUnattended.isNull(), 0);
+ return m_comUnattended.GetImageIndex();
+}
+
+QVector<KMediumVariant> UIWizardNewVM::mediumVariants() const
+{
+ /* Compose medium-variant: */
+ QVector<KMediumVariant> variants(sizeof(qulonglong)*8);
+ for (int i = 0; i < variants.size(); ++i)
+ {
+ qulonglong temp = m_uMediumVariant;
+ temp &= UINT64_C(1)<<i;
+ variants[i] = (KMediumVariant)temp;
+ }
+ return variants;
+}
+
+bool UIWizardNewVM::isUnattendedEnabled() const
+{
+ if (m_comUnattended.isNull())
+ return false;
+ if (m_comUnattended.GetIsoPath().isEmpty())
+ return false;
+ if (m_fSkipUnattendedInstall)
+ return false;
+ if (!isUnattendedInstallSupported())
+ return false;
+ return true;
+}
+
+bool UIWizardNewVM::isUnattendedInstallSupported() const
+{
+ AssertReturn(!m_comUnattended.isNull(), false);
+ return m_comUnattended.GetIsUnattendedInstallSupported();
+}
+
+bool UIWizardNewVM::isGuestOSTypeWindows() const
+{
+ return m_strGuestOSFamilyId.contains("windows", Qt::CaseInsensitive);
+}
+
+void UIWizardNewVM::setUnattendedPageVisible(bool fVisible)
+{
+ if (m_iUnattendedInstallPageIndex != -1)
+ setPageVisible(m_iUnattendedInstallPageIndex, fVisible);
+}
+
+bool UIWizardNewVM::checkUnattendedInstallError(const CUnattended &comUnattended) const
+{
+ if (!comUnattended.isOk())
+ {
+ UINotificationMessage::cannotRunUnattendedGuestInstall(comUnattended);
+ return false;
+ }
+ return true;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVM.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVM.h
new file mode 100644
index 00000000..95ba5107
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVM.h
@@ -0,0 +1,247 @@
+/* $Id: UIWizardNewVM.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVM class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVM_h
+#define FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVM_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMachine.h"
+#include "CMedium.h"
+#include "CMediumFormat.h"
+#include "CGuestOSType.h"
+
+/* Forward declarations: */
+class UIActionPool;
+
+enum SelectedDiskSource
+{
+ SelectedDiskSource_Empty = 0,
+ SelectedDiskSource_New,
+ SelectedDiskSource_Existing,
+ SelectedDiskSource_Max
+};
+
+/** New Virtual Machine wizard: */
+class UIWizardNewVM : public UINativeWizard
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVM(QWidget *pParent,
+ UIActionPool *pActionPool,
+ const QString &strMachineGroup,
+ CUnattended &comUnattended,
+ const QString &strISOFilePath = QString());
+
+ bool isUnattendedEnabled() const;
+ bool isUnattendedInstallSupported() const;
+ bool isGuestOSTypeWindows() const;
+
+ bool createVM();
+ bool createVirtualDisk();
+
+ CMedium &virtualDisk();
+ void setVirtualDisk(const CMedium &medium);
+ void setVirtualDisk(const QUuid &mediumId);
+
+ const QString &machineGroup() const;
+ QUuid createdMachineId() const;
+
+ /** @name Setter/getters for vm parameters
+ * @{ */
+ const QString &machineFilePath() const;
+ void setMachineFilePath(const QString &strMachineFilePath);
+
+ /* The name of the .vbox file. Obtained from machineFilePath(). Unlike machine base name it cannot have characters like / etc. */
+ QString machineFileName() const;
+
+ const QString &machineFolder() const;
+ void setMachineFolder(const QString &strMachineFolder);
+
+ const QString &machineBaseName() const;
+ void setMachineBaseName(const QString &strMachineBaseName);
+
+ const QString &createdMachineFolder() const;
+ void setCreatedMachineFolder(const QString &strCreatedMachineFolder);
+
+ QString detectedOSTypeId() const;
+
+ const QString &guestOSFamilyId() const;
+ void setGuestOSFamilyId(const QString &strGuestOSFamilyId);
+
+ const CGuestOSType &guestOSType() const;
+ void setGuestOSType(const CGuestOSType &guestOSType);
+
+ bool installGuestAdditions() const;
+ void setInstallGuestAdditions(bool fInstallGA);
+
+ bool startHeadless() const;
+ void setStartHeadless(bool fStartHeadless);
+
+ bool skipUnattendedInstall() const;
+ void setSkipUnattendedInstall(bool fSkipUnattendedInstall);
+
+ bool EFIEnabled() const;
+ void setEFIEnabled(bool fEnabled);
+
+ QString ISOFilePath() const;
+ void setISOFilePath(const QString &strISOFilePath);
+
+ QString userName() const;
+ void setUserName(const QString &strUserName);
+
+ QString password() const;
+ void setPassword(const QString &strPassword);
+
+ QString guestAdditionsISOPath() const;
+ void setGuestAdditionsISOPath(const QString &strGAISOPath);
+
+ QString hostnameDomainName() const;
+ void setHostnameDomainName(const QString &strHostnameDomainName);
+
+ QString productKey() const;
+ void setProductKey(const QString &productKey);
+
+ int CPUCount() const;
+ void setCPUCount(int iCPUCount);
+
+ int memorySize() const;
+ void setMemorySize(int iMemory);
+
+ qulonglong mediumVariant() const;
+ void setMediumVariant(qulonglong uMediumVariant);
+
+ const CMediumFormat &mediumFormat();
+ void setMediumFormat(const CMediumFormat &mediumFormat);
+
+ const QString &mediumPath() const;
+ void setMediumPath(const QString &strMediumPath);
+
+ qulonglong mediumSize() const;
+ void setMediumSize(qulonglong mediumSize);
+
+ SelectedDiskSource diskSource() const;
+ void setDiskSource(SelectedDiskSource enmDiskSource);
+
+ bool emptyDiskRecommended() const;
+ void setEmptyDiskRecommended(bool fEmptyDiskRecommended);
+
+ void setDetectedWindowsImageNamesAndIndices(const QVector<QString> &names, const QVector<ulong> &ids);
+ const QVector<QString> &detectedWindowsImageNames() const;
+ const QVector<ulong> &detectedWindowsImageIndices() const;
+
+ void setSelectedWindowImageIndex(ulong uIndex);
+ ulong selectedWindowImageIndex() const;
+
+ QVector<KMediumVariant> mediumVariants() const;
+ /** @} */
+
+protected:
+
+ /** Populates pages. */
+ virtual void populatePages() /* final override */;
+ virtual void cleanWizard() /* final override */;
+ void configureVM(const QString &strGuestTypeId, const CGuestOSType &comGuestType);
+ bool attachDefaultDevices();
+
+private slots:
+
+ void sltHandleWizardCancel();
+
+private:
+
+ void retranslateUi();
+ QString getNextControllerName(KStorageBus type);
+ void setUnattendedPageVisible(bool fVisible);
+ void deleteVirtualDisk();
+ bool checkUnattendedInstallError(const CUnattended &comUnattended) const;
+ /** @name Variables
+ * @{ */
+ CMedium m_virtualDisk;
+ CMachine m_machine;
+ QString m_strMachineGroup;
+ int m_iIDECount;
+ int m_iSATACount;
+ int m_iSCSICount;
+ int m_iFloppyCount;
+ int m_iSASCount;
+ int m_iUSBCount;
+
+ /** Path of the folder created by this wizard page. Used to remove previously created
+ * folder. see cleanupMachineFolder();*/
+ QString m_strCreatedFolder;
+
+ /** Full path (including the file name) of the machine's configuration file. */
+ QString m_strMachineFilePath;
+ /** Path of the folder hosting the machine's configuration file. Generated from m_strMachineFilePath. */
+ QString m_strMachineFolder;
+ /** Base name of the machine. Can include characters / or \. */
+ QString m_strMachineBaseName;
+
+ /* Name and index lists of the images detected from an ISO. Currently only for Windows ISOs. */
+ QVector<QString> m_detectedWindowsImageNames;
+ QVector<ulong> m_detectedWindowsImageIndices;
+
+ /** Holds the VM OS family ID. */
+ QString m_strGuestOSFamilyId;
+ /** Holds the VM OS type. */
+ CGuestOSType m_comGuestOSType;
+
+ /** True if guest additions are to be installed during unattended install. */
+ bool m_fInstallGuestAdditions;
+ bool m_fSkipUnattendedInstall;
+ bool m_fEFIEnabled;
+
+ int m_iCPUCount;
+ int m_iMemorySize;
+ int m_iUnattendedInstallPageIndex;
+
+ qulonglong m_uMediumVariant;
+ CMediumFormat m_comMediumFormat;
+ QString m_strMediumPath;
+ qulonglong m_uMediumSize;
+ SelectedDiskSource m_enmDiskSource;
+ bool m_fEmptyDiskRecommended;
+ QVector<KMediumVariant> m_mediumVariants;
+ UIActionPool *m_pActionPool;
+ CUnattended &m_comUnattended;
+ bool m_fStartHeadless;
+ QString m_strInitialISOFilePath;
+ /** @} */
+};
+
+typedef QPointer<UIWizardNewVM> UISafePointerWizardNewVM;
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVM_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMDiskPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMDiskPage.cpp
new file mode 100644
index 00000000..572ad8ab
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMDiskPage.cpp
@@ -0,0 +1,497 @@
+/* $Id: UIWizardNewVMDiskPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMDiskPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QLabel>
+#include <QRadioButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIMediaComboBox.h"
+#include "UIMediumSelector.h"
+#include "UIMediumSizeEditor.h"
+#include "UICommon.h"
+#include "UIWizardNewVMDiskPage.h"
+#include "UIWizardDiskEditors.h"
+#include "UIWizardNewVM.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CGuestOSType.h"
+#include "CSystemProperties.h"
+
+QUuid UIWizardNewVMDiskCommon::getWithFileOpenDialog(const QString &strOSTypeID,
+ const QString &strMachineFolder,
+ QWidget *pCaller, UIActionPool *pActionPool)
+{
+ QUuid uMediumId;
+ int returnCode = UIMediumSelector::openMediumSelectorDialog(pCaller, UIMediumDeviceType_HardDisk,
+ QUuid() /* current medium id */,
+ uMediumId,
+ strMachineFolder,
+ QString() /* strMachineName */,
+ strOSTypeID,
+ false /* don't show/enable the create action: */,
+ QUuid() /* Machinie Id */, pActionPool);
+ if (returnCode != static_cast<int>(UIMediumSelector::ReturnCode_Accepted))
+ return QUuid();
+ return uMediumId;
+}
+
+UIWizardNewVMDiskPage::UIWizardNewVMDiskPage(UIActionPool *pActionPool)
+ : m_pDiskSourceButtonGroup(0)
+ , m_pDiskEmpty(0)
+ , m_pDiskNew(0)
+ , m_pDiskExisting(0)
+ , m_pDiskSelector(0)
+ , m_pDiskSelectionButton(0)
+ , m_pLabel(0)
+ , m_pMediumSizeEditorLabel(0)
+ , m_pMediumSizeEditor(0)
+ , m_pDescriptionLabel(0)
+ , m_pDynamicLabel(0)
+ , m_pFixedLabel(0)
+ , m_pFixedCheckBox(0)
+ , m_fVDIFormatFound(false)
+ , m_uMediumSizeMin(_4M)
+ , m_uMediumSizeMax(uiCommon().virtualBox().GetSystemProperties().GetInfoVDSize())
+ , m_pActionPool(pActionPool)
+{
+ prepare();
+}
+
+void UIWizardNewVMDiskPage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+
+ m_pLabel = new QIRichTextLabel(this);
+ pMainLayout->addWidget(m_pLabel);
+ pMainLayout->addWidget(createDiskWidgets());
+
+ pMainLayout->addStretch();
+
+ createConnections();
+}
+
+QWidget *UIWizardNewVMDiskPage::createNewDiskWidgets()
+{
+ QWidget *pWidget = new QWidget;
+ if (pWidget)
+ {
+ QVBoxLayout *pLayout = new QVBoxLayout(pWidget);
+ if (pLayout)
+ {
+ pLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare size layout: */
+ QGridLayout *pSizeLayout = new QGridLayout;
+ if (pSizeLayout)
+ {
+ pSizeLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare Hard disk size label: */
+ m_pMediumSizeEditorLabel = new QLabel(pWidget);
+ if (m_pMediumSizeEditorLabel)
+ {
+ m_pMediumSizeEditorLabel->setAlignment(Qt::AlignRight);
+ m_pMediumSizeEditorLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ pSizeLayout->addWidget(m_pMediumSizeEditorLabel, 0, 0, Qt::AlignBottom);
+ }
+ /* Prepare Hard disk size editor: */
+ m_pMediumSizeEditor = new UIMediumSizeEditor(pWidget);
+ if (m_pMediumSizeEditor)
+ {
+ m_pMediumSizeEditorLabel->setBuddy(m_pMediumSizeEditor);
+ pSizeLayout->addWidget(m_pMediumSizeEditor, 0, 1, 2, 1);
+ }
+ pLayout->addLayout(pSizeLayout);
+ }
+ /* Hard disk variant (dynamic vs. fixed) widgets: */
+ pLayout->addWidget(createMediumVariantWidgets(false /* bool fWithLabels */));
+ }
+ }
+ return pWidget;
+}
+
+void UIWizardNewVMDiskPage::createConnections()
+{
+ if (m_pDiskSourceButtonGroup)
+ connect(m_pDiskSourceButtonGroup, static_cast<void(QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked),
+ this, &UIWizardNewVMDiskPage::sltSelectedDiskSourceChanged);
+ if (m_pDiskSelector)
+ connect(m_pDiskSelector, static_cast<void(UIMediaComboBox::*)(int)>(&UIMediaComboBox::currentIndexChanged),
+ this, &UIWizardNewVMDiskPage::sltMediaComboBoxIndexChanged);
+ if (m_pDiskSelectionButton)
+ connect(m_pDiskSelectionButton, &QIToolButton::clicked,
+ this, &UIWizardNewVMDiskPage::sltGetWithFileOpenDialog);
+ if (m_pMediumSizeEditor)
+ connect(m_pMediumSizeEditor, &UIMediumSizeEditor::sigSizeChanged,
+ this, &UIWizardNewVMDiskPage::sltHandleSizeEditorChange);
+ if (m_pFixedCheckBox)
+ connect(m_pFixedCheckBox, &QCheckBox::toggled,
+ this, &UIWizardNewVMDiskPage::sltFixedCheckBoxToggled);
+}
+
+void UIWizardNewVMDiskPage::sltSelectedDiskSourceChanged()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(m_pDiskSelector && m_pDiskSourceButtonGroup && pWizard);
+ m_userModifiedParameters << "SelectedDiskSource";
+ if (m_pDiskSourceButtonGroup->checkedButton() == m_pDiskEmpty)
+ {
+ pWizard->setDiskSource(SelectedDiskSource_Empty);
+ pWizard->setVirtualDisk(QUuid());
+ pWizard->setMediumPath(QString());
+ }
+ else if (m_pDiskSourceButtonGroup->checkedButton() == m_pDiskExisting)
+ {
+ pWizard->setDiskSource(SelectedDiskSource_Existing);
+ pWizard->setVirtualDisk(m_pDiskSelector->id());
+ pWizard->setMediumPath(m_pDiskSelector->location());
+ }
+ else
+ {
+ pWizard->setDiskSource(SelectedDiskSource_New);
+ pWizard->setVirtualDisk(QUuid());
+ pWizard->setMediumPath(QString());
+ }
+
+ setEnableDiskSelectionWidgets(pWizard->diskSource() == SelectedDiskSource_Existing);
+ setEnableNewDiskWidgets(pWizard->diskSource() == SelectedDiskSource_New);
+
+ emit completeChanged();
+}
+
+void UIWizardNewVMDiskPage::sltMediaComboBoxIndexChanged()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard && m_pDiskSelector);
+ m_userModifiedParameters << "SelectedExistingMediumIndex";
+ pWizard->setVirtualDisk(m_pDiskSelector->id());
+ pWizard->setMediumPath(m_pDiskSelector->location());
+ emit completeChanged();
+}
+
+void UIWizardNewVMDiskPage::sltGetWithFileOpenDialog()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ const CGuestOSType &comOSType = pWizard->guestOSType();
+ AssertReturnVoid(!comOSType.isNull());
+ QUuid uMediumId = UIWizardNewVMDiskCommon::getWithFileOpenDialog(comOSType.GetId(),
+ pWizard->machineFolder(),
+ this, m_pActionPool);
+ if (!uMediumId.isNull())
+ {
+ m_pDiskSelector->setCurrentItem(uMediumId);
+ m_pDiskSelector->setFocus();
+ }
+}
+
+void UIWizardNewVMDiskPage::retranslateUi()
+{
+ setTitle(UIWizardNewVM::tr("Virtual Hard disk"));
+
+ if (m_pLabel)
+ m_pLabel->setText(UIWizardNewVM::tr("If you wish you can add a virtual hard disk to the new machine. "
+ "You can either create a new hard disk file or select an existing one. "
+ "Alternatively you can create a virtual machine without a virtual hard disk."));
+
+ if (m_pDiskEmpty)
+ m_pDiskEmpty->setText(UIWizardNewVM::tr("&Do Not Add a Virtual Hard Disk"));
+ if (m_pDiskNew)
+ m_pDiskNew->setText(UIWizardNewVM::tr("&Create a Virtual Hard Disk Now"));
+ if (m_pDiskExisting)
+ m_pDiskExisting->setText(UIWizardNewVM::tr("U&se an Existing Virtual Hard Disk File"));
+ if (m_pDiskSelectionButton)
+ m_pDiskSelectionButton->setToolTip(UIWizardNewVM::tr("Chooses a Virtual Hard Fisk File..."));
+
+ if (m_pMediumSizeEditorLabel)
+ m_pMediumSizeEditorLabel->setText(UIWizardNewVM::tr("D&isk Size:"));
+
+ if (m_pFixedCheckBox)
+ {
+ m_pFixedCheckBox->setText(UIWizardNewVM::tr("Pre-allocate &Full Size"));
+ m_pFixedCheckBox->setToolTip(UIWizardNewVM::tr("When checked, the virtual disk image is allocated with its full size during VM creation time"));
+ }
+
+ /* Translate rich text labels: */
+ if (m_pDescriptionLabel)
+ m_pDescriptionLabel->setText(UIWizardNewVM::tr("Please choose whether the new virtual hard disk file should grow as it is used "
+ "(dynamically allocated) or if it should be created at its maximum size (fixed size)."));
+ if (m_pDynamicLabel)
+ m_pDynamicLabel->setText(UIWizardNewVM::tr("<p>A <b>dynamically allocated</b> hard disk file will only use space "
+ "on your physical hard disk as it fills up (up to a maximum <b>fixed size</b>), "
+ "although it will not shrink again automatically when space on it is freed.</p>"));
+ if (m_pFixedLabel)
+ m_pFixedLabel->setText(UIWizardNewVM::tr("<p>A <b>fixed size</b> hard disk file may take longer to create on some "
+ "systems but is often faster to use.</p>"));
+}
+
+void UIWizardNewVMDiskPage::initializePage()
+{
+ retranslateUi();
+
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+
+ LONG64 iRecommendedSize = 0;
+ CGuestOSType type = pWizard->guestOSType();
+ if (!type.isNull() && !m_userModifiedParameters.contains("SelectedDiskSource"))
+ {
+ iRecommendedSize = type.GetRecommendedHDD();
+ if (iRecommendedSize != 0)
+ {
+ if (m_pDiskNew)
+ {
+ m_pDiskNew->setFocus();
+ m_pDiskNew->setChecked(true);
+ }
+ pWizard->setDiskSource(SelectedDiskSource_New);
+ pWizard->setEmptyDiskRecommended(false);
+ }
+ else
+ {
+ if (m_pDiskEmpty)
+ {
+ m_pDiskEmpty->setFocus();
+ m_pDiskEmpty->setChecked(true);
+ }
+ pWizard->setDiskSource(SelectedDiskSource_Empty);
+ pWizard->setEmptyDiskRecommended(true);
+ }
+ }
+
+ if (m_pDiskSelector && !m_userModifiedParameters.contains("SelectedExistingMediumIndex"))
+ m_pDiskSelector->setCurrentIndex(0);
+ setEnableDiskSelectionWidgets(pWizard->diskSource() == SelectedDiskSource_Existing);
+ setEnableNewDiskWidgets(pWizard->diskSource() == SelectedDiskSource_New);
+
+ if (!m_fVDIFormatFound)
+ {
+ /* We do not have any UI elements for HDD format selection since we default to VDI in case of guided wizard mode: */
+ CSystemProperties properties = uiCommon().virtualBox().GetSystemProperties();
+ const QVector<CMediumFormat> &formats = properties.GetMediumFormats();
+ foreach (const CMediumFormat &format, formats)
+ {
+ if (format.GetName() == "VDI")
+ {
+ pWizard->setMediumFormat(format);
+ m_fVDIFormatFound = true;
+ }
+ }
+ if (!m_fVDIFormatFound)
+ AssertMsgFailed(("No medium format corresponding to VDI could be found!"));
+ setWidgetVisibility(pWizard->mediumFormat());
+ }
+ QString strDefaultExtension = UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk);
+
+ /* We set the medium name and path according to machine name/path and do not allow user change these in the guided mode: */
+ QString strDefaultName = pWizard->machineFileName().isEmpty() ? QString("NewVirtualDisk1") : pWizard->machineFileName();
+ const QString &strMachineFolder = pWizard->machineFolder();
+ QString strMediumPath =
+ UIWizardDiskEditors::constructMediumFilePath(UIWizardDiskEditors::appendExtension(strDefaultName,
+ strDefaultExtension), strMachineFolder);
+ pWizard->setMediumPath(strMediumPath);
+
+ /* Set the recommended disk size if user has already not done so: */
+ if (m_pMediumSizeEditor && !m_userModifiedParameters.contains("MediumSize"))
+ {
+ m_pMediumSizeEditor->blockSignals(true);
+ m_pMediumSizeEditor->setMediumSize(iRecommendedSize);
+ m_pMediumSizeEditor->blockSignals(false);
+ pWizard->setMediumSize(iRecommendedSize);
+ }
+
+ /* Initialize medium variant parameter of the wizard (only if user has not touched the checkbox yet): */
+ if (!m_userModifiedParameters.contains("MediumVariant"))
+ {
+ if (m_pFixedCheckBox)
+ {
+ if (m_pFixedCheckBox->isChecked())
+ pWizard->setMediumVariant((qulonglong)KMediumVariant_Fixed);
+ else
+ pWizard->setMediumVariant((qulonglong)KMediumVariant_Standard);
+ }
+ else
+ pWizard->setMediumVariant((qulonglong)KMediumVariant_Standard);
+ }
+}
+
+bool UIWizardNewVMDiskPage::isComplete() const
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturn(pWizard, false);
+
+ const qulonglong uSize = pWizard->mediumSize();
+ if (pWizard->diskSource() == SelectedDiskSource_New)
+ return uSize >= m_uMediumSizeMin && uSize <= m_uMediumSizeMax;
+
+ if (pWizard->diskSource() == SelectedDiskSource_Existing)
+ return !pWizard->virtualDisk().isNull();
+
+ return true;
+}
+
+void UIWizardNewVMDiskPage::sltHandleSizeEditorChange(qulonglong uSize)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setMediumSize(uSize);
+ m_userModifiedParameters << "MediumSize";
+ emit completeChanged();
+}
+
+void UIWizardNewVMDiskPage::sltFixedCheckBoxToggled(bool fChecked)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ qulonglong uMediumVariant = (qulonglong)KMediumVariant_Max;
+ if (fChecked)
+ uMediumVariant = (qulonglong)KMediumVariant_Fixed;
+ else
+ uMediumVariant = (qulonglong)KMediumVariant_Standard;
+ wizardWindow<UIWizardNewVM>()->setMediumVariant(uMediumVariant);
+ m_userModifiedParameters << "MediumVariant";
+}
+
+void UIWizardNewVMDiskPage::setEnableNewDiskWidgets(bool fEnable)
+{
+ if (m_pMediumSizeEditor)
+ m_pMediumSizeEditor->setEnabled(fEnable);
+ if (m_pMediumSizeEditorLabel)
+ m_pMediumSizeEditorLabel->setEnabled(fEnable);
+ if (m_pFixedCheckBox)
+ m_pFixedCheckBox->setEnabled(fEnable);
+}
+
+QWidget *UIWizardNewVMDiskPage::createDiskWidgets()
+{
+ QWidget *pDiskContainer = new QWidget;
+ QGridLayout *pDiskLayout = new QGridLayout(pDiskContainer);
+ pDiskLayout->setContentsMargins(0, 0, 0, 0);
+ m_pDiskSourceButtonGroup = new QButtonGroup(this);
+ m_pDiskEmpty = new QRadioButton;
+ m_pDiskNew = new QRadioButton;
+ m_pDiskExisting = new QRadioButton;
+ m_pDiskSourceButtonGroup->addButton(m_pDiskEmpty);
+ m_pDiskSourceButtonGroup->addButton(m_pDiskNew);
+ m_pDiskSourceButtonGroup->addButton(m_pDiskExisting);
+ QStyleOptionButton options;
+ options.initFrom(m_pDiskExisting);
+ int iWidth = m_pDiskExisting->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, &options, m_pDiskExisting);
+ pDiskLayout->setColumnMinimumWidth(0, iWidth);
+ m_pDiskSelector = new UIMediaComboBox;
+ {
+ m_pDiskSelector->setType(UIMediumDeviceType_HardDisk);
+ m_pDiskSelector->repopulate();
+ }
+ m_pDiskSelectionButton = new QIToolButton;
+ {
+ m_pDiskSelectionButton->setAutoRaise(true);
+ m_pDiskSelectionButton->setIcon(UIIconPool::iconSet(":/select_file_16px.png", ":/select_file_disabled_16px.png"));
+ }
+ pDiskLayout->addWidget(m_pDiskNew, 0, 0, 1, 6);
+ pDiskLayout->addWidget(createNewDiskWidgets(), 1, 2, 3, 4);
+ pDiskLayout->addWidget(m_pDiskExisting, 4, 0, 1, 6);
+ pDiskLayout->addWidget(m_pDiskSelector, 5, 2, 1, 3);
+ pDiskLayout->addWidget(m_pDiskSelectionButton, 5, 5, 1, 1);
+ pDiskLayout->addWidget(m_pDiskEmpty, 6, 0, 1, 6);
+ return pDiskContainer;
+}
+
+QWidget *UIWizardNewVMDiskPage::createMediumVariantWidgets(bool fWithLabels)
+{
+ QWidget *pContainerWidget = new QWidget;
+ QVBoxLayout *pMainLayout = new QVBoxLayout(pContainerWidget);
+ if (pMainLayout)
+ {
+ QVBoxLayout *pVariantLayout = new QVBoxLayout;
+ if (pVariantLayout)
+ {
+ m_pFixedCheckBox = new QCheckBox;
+ pVariantLayout->addWidget(m_pFixedCheckBox);
+ }
+ if (fWithLabels)
+ {
+ m_pDescriptionLabel = new QIRichTextLabel;
+ m_pDynamicLabel = new QIRichTextLabel;
+ m_pFixedLabel = new QIRichTextLabel;
+
+ pMainLayout->addWidget(m_pDescriptionLabel);
+ pMainLayout->addWidget(m_pDynamicLabel);
+ pMainLayout->addWidget(m_pFixedLabel);
+ }
+ pMainLayout->addLayout(pVariantLayout);
+ pMainLayout->addStretch();
+ pMainLayout->setContentsMargins(0, 0, 0, 0);
+ }
+ return pContainerWidget;
+}
+
+void UIWizardNewVMDiskPage::setEnableDiskSelectionWidgets(bool fEnabled)
+{
+ if (!m_pDiskSelector || !m_pDiskSelectionButton)
+ return;
+
+ m_pDiskSelector->setEnabled(fEnabled);
+ m_pDiskSelectionButton->setEnabled(fEnabled);
+}
+
+void UIWizardNewVMDiskPage::setWidgetVisibility(const CMediumFormat &mediumFormat)
+{
+ ULONG uCapabilities = 0;
+ QVector<KMediumFormatCapabilities> capabilities;
+ capabilities = mediumFormat.GetCapabilities();
+ for (int i = 0; i < capabilities.size(); i++)
+ uCapabilities |= capabilities[i];
+
+ bool fIsCreateDynamicPossible = uCapabilities & KMediumFormatCapabilities_CreateDynamic;
+ bool fIsCreateFixedPossible = uCapabilities & KMediumFormatCapabilities_CreateFixed;
+ if (m_pFixedCheckBox)
+ {
+ if (!fIsCreateDynamicPossible)
+ {
+ m_pFixedCheckBox->setChecked(true);
+ m_pFixedCheckBox->setEnabled(false);
+ }
+ if (!fIsCreateFixedPossible)
+ {
+ m_pFixedCheckBox->setChecked(false);
+ m_pFixedCheckBox->setEnabled(false);
+ }
+ }
+ if (m_pDynamicLabel)
+ m_pDynamicLabel->setHidden(!fIsCreateDynamicPossible);
+ if (m_pFixedLabel)
+ m_pFixedLabel->setHidden(!fIsCreateFixedPossible);
+ if (m_pFixedCheckBox)
+ m_pFixedCheckBox->setHidden(!fIsCreateFixedPossible);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMDiskPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMDiskPage.h
new file mode 100644
index 00000000..75d6ee97
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMDiskPage.h
@@ -0,0 +1,126 @@
+/* $Id: UIWizardNewVMDiskPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMDiskPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMDiskPage_h
+#define FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMDiskPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSet>
+
+/* GUI includes: */
+#include "QIFileDialog.h"
+#include "UINativeWizardPage.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+#include "CMedium.h"
+
+/* Forward declarations: */
+class QButtonGroup;
+class QCheckBox;
+class QRadioButton;
+class QLabel;
+class QIRichTextLabel;
+class QIToolButton;
+class UIActionPool;
+class UIMediaComboBox;
+class UIMediumSizeEditor;
+
+namespace UIWizardNewVMDiskCommon
+{
+ QUuid getWithFileOpenDialog(const QString &strOSTypeID,
+ const QString &strMachineFolder,
+ QWidget *pCaller, UIActionPool *pActionPool);
+}
+
+
+class UIWizardNewVMDiskPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVMDiskPage(UIActionPool *pActionPool);
+
+protected:
+
+
+private slots:
+
+ void sltSelectedDiskSourceChanged();
+ void sltMediaComboBoxIndexChanged();
+ void sltGetWithFileOpenDialog();
+ void sltHandleSizeEditorChange(qulonglong uSize);
+ void sltFixedCheckBoxToggled(bool fChecked);
+
+private:
+
+ void prepare();
+ void createConnections();
+ QWidget *createNewDiskWidgets();
+ void setEnableNewDiskWidgets(bool fEnable);
+ QWidget *createDiskWidgets();
+ QWidget *createMediumVariantWidgets(bool fWithLabels);
+
+ virtual void retranslateUi() /* override final */;
+ virtual void initializePage() /* override final */;
+ virtual bool isComplete() const /* override final */;
+
+ void setEnableDiskSelectionWidgets(bool fEnabled);
+ void setWidgetVisibility(const CMediumFormat &mediumFormat);
+
+ /** @name Widgets
+ * @{ */
+ QButtonGroup *m_pDiskSourceButtonGroup;
+ QRadioButton *m_pDiskEmpty;
+ QRadioButton *m_pDiskNew;
+ QRadioButton *m_pDiskExisting;
+ UIMediaComboBox *m_pDiskSelector;
+ QIToolButton *m_pDiskSelectionButton;
+ QIRichTextLabel *m_pLabel;
+ QLabel *m_pMediumSizeEditorLabel;
+ UIMediumSizeEditor *m_pMediumSizeEditor;
+ QIRichTextLabel *m_pDescriptionLabel;
+ QIRichTextLabel *m_pDynamicLabel;
+ QIRichTextLabel *m_pFixedLabel;
+ QCheckBox *m_pFixedCheckBox;
+ /** @} */
+
+ /** @name Variables
+ * @{ */
+ QSet<QString> m_userModifiedParameters;
+ bool m_fVDIFormatFound;
+ qulonglong m_uMediumSizeMin;
+ qulonglong m_uMediumSizeMax;
+ /** @} */
+ UIActionPool *m_pActionPool;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMDiskPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMExpertPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMExpertPage.cpp
new file mode 100644
index 00000000..1b8f2ac8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMExpertPage.cpp
@@ -0,0 +1,1028 @@
+/* $Id: UIWizardNewVMExpertPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMExpertPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QRadioButton>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "UICommon.h"
+#include "QIToolButton.h"
+#include "UIIconPool.h"
+#include "UIMediaComboBox.h"
+#include "UIMedium.h"
+#include "UINameAndSystemEditor.h"
+#include "UINotificationCenter.h"
+#include "UIToolBox.h"
+#include "UIWizardNewVM.h"
+#include "UIWizardDiskEditors.h"
+#include "UIWizardNewVMDiskPage.h"
+#include "UIWizardNewVMEditors.h"
+#include "UIWizardNewVMExpertPage.h"
+#include "UIWizardNewVMNameOSTypePage.h"
+
+/* COM includes: */
+#include "CSystemProperties.h"
+
+UIWizardNewVMExpertPage::UIWizardNewVMExpertPage(UIActionPool *pActionPool)
+ : m_pToolBox(0)
+ , m_pDiskFormatVariantGroupBox(0)
+ , m_pDiskVariantWidget(0)
+ , m_pFormatComboBox(0)
+ , m_pSizeAndLocationGroup(0)
+ , m_pNameAndSystemEditor(0)
+ , m_pSkipUnattendedCheckBox(0)
+ , m_pNameAndSystemLayout(0)
+ , m_pHardwareWidgetContainer(0)
+ , m_pAdditionalOptionsContainer(0)
+ , m_pGAInstallationISOContainer(0)
+ , m_pDiskSourceButtonGroup(0)
+ , m_pDiskEmpty(0)
+ , m_pDiskNew(0)
+ , m_pDiskExisting(0)
+ , m_pDiskSelector(0)
+ , m_pDiskSelectionButton(0)
+ , m_fRecommendedNoDisk(false)
+ , m_uMediumSizeMin(_4M)
+ , m_uMediumSizeMax(uiCommon().virtualBox().GetSystemProperties().GetInfoVDSize())
+ , m_pActionPool(pActionPool)
+{
+ /* Create widgets: */
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+ {
+ m_pToolBox = new UIToolBox;
+ m_pToolBox->insertPage(ExpertToolboxItems_NameAndOSType, createNameOSTypeWidgets(), "");
+ m_pToolBox->insertPage(ExpertToolboxItems_Unattended, createUnattendedWidgets(), "");
+ m_pHardwareWidgetContainer = new UINewVMHardwareContainer;
+ m_pToolBox->insertPage(ExpertToolboxItems_Hardware, m_pHardwareWidgetContainer, "");
+ m_pToolBox->insertPage(ExpertToolboxItems_Disk, createDiskWidgets(), "");
+ m_pToolBox->setCurrentPage(ExpertToolboxItems_NameAndOSType);
+ pMainLayout->addWidget(m_pToolBox);
+ pMainLayout->addStretch();
+ }
+
+ createConnections();
+
+ /* Register classes: */
+ qRegisterMetaType<CMedium>();
+}
+
+void UIWizardNewVMExpertPage::setISOFilePath(const QString &strISOFilePath)
+{
+ QFileInfo isoFileInfo(strISOFilePath);
+ if (isoFileInfo.exists() && m_pNameAndSystemEditor)
+ m_pNameAndSystemEditor->setISOImagePath(strISOFilePath);
+}
+
+void UIWizardNewVMExpertPage::sltNameChanged(const QString &strNewName)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ /* Allow type guessing from name only if an OS type from ISO could not be detected: */
+ if (!m_userModifiedParameters.contains("GuestOSTypeFromISO") && m_pNameAndSystemEditor)
+ {
+ m_pNameAndSystemEditor->blockSignals(true);
+ if (UIWizardNewVMNameOSTypeCommon::guessOSTypeFromName(m_pNameAndSystemEditor, strNewName))
+ {
+ wizardWindow<UIWizardNewVM>()->setGuestOSType(m_pNameAndSystemEditor->type());
+ /* Since the type `possibly` changed: */
+ setOSTypeDependedValues();
+ m_userModifiedParameters << "GuestOSTypeFromName";
+ }
+ m_pNameAndSystemEditor->blockSignals(false);
+ }
+ UIWizardNewVMNameOSTypeCommon::composeMachineFilePath(m_pNameAndSystemEditor, wizardWindow<UIWizardNewVM>());
+ if (!m_userModifiedParameters.contains("MediumPath"))
+ updateVirtualMediumPathFromMachinePathName();
+ if (!m_userModifiedParameters.contains("HostnameDomainName"))
+ updateHostnameDomainNameFromMachineName();
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltPathChanged(const QString &strNewPath)
+{
+ Q_UNUSED(strNewPath);
+ UIWizardNewVMNameOSTypeCommon::composeMachineFilePath(m_pNameAndSystemEditor, wizardWindow<UIWizardNewVM>());
+ if (!m_userModifiedParameters.contains("MediumPath"))
+ updateVirtualMediumPathFromMachinePathName();
+}
+
+void UIWizardNewVMExpertPage::sltOsTypeChanged()
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ /* Don't add GuestOSType to the set since we want to adjust os type when installation ISO changes. No matter if user
+ * has already set the os type explicitly or not: */
+ //m_userModifiedParameters << "GuestOSType";
+ if (m_pNameAndSystemEditor)
+ wizardWindow<UIWizardNewVM>()->setGuestOSType(m_pNameAndSystemEditor->type());
+ setOSTypeDependedValues();
+}
+
+void UIWizardNewVMExpertPage::sltGetWithFileOpenDialog()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ const CGuestOSType &comOSType = pWizard->guestOSType();
+ AssertReturnVoid(!comOSType.isNull());
+ QUuid uMediumId = UIWizardNewVMDiskCommon::getWithFileOpenDialog(comOSType.GetId(),
+ pWizard->machineFolder(),
+ this, m_pActionPool);
+ if (!uMediumId.isNull())
+ {
+ m_pDiskSelector->setCurrentItem(uMediumId);
+ m_pDiskSelector->setFocus();
+ }
+}
+
+void UIWizardNewVMExpertPage::sltISOPathChanged(const QString &strISOPath)
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+
+ pWizard->setISOFilePath(strISOPath);
+
+ bool const fOsTypeFixed = UIWizardNewVMNameOSTypeCommon::guessOSTypeDetectedOSTypeString(m_pNameAndSystemEditor,
+ pWizard->detectedOSTypeId());
+ if (fOsTypeFixed)
+ m_userModifiedParameters << "GuestOSTypeFromISO";
+ else /* Remove GuestOSTypeFromISO from the set if it is there: */
+ m_userModifiedParameters.remove("GuestOSTypeFromISO");
+
+ /* Update the global recent ISO path: */
+ QFileInfo fileInfo(strISOPath);
+ if (fileInfo.exists() && fileInfo.isReadable())
+ uiCommon().updateRecentlyUsedMediumListAndFolder(UIMediumDeviceType_DVD, strISOPath);
+
+ /* Populate the editions selector: */
+ if (m_pNameAndSystemEditor)
+ m_pNameAndSystemEditor->setEditionNameAndIndices(pWizard->detectedWindowsImageNames(),
+ pWizard->detectedWindowsImageIndices());
+ setSkipCheckBoxEnable();
+ disableEnableUnattendedRelatedWidgets(isUnattendedEnabled());
+
+ /* Redetect the OS type using the name if detection or the step above failed: */
+ if (!fOsTypeFixed && m_pNameAndSystemEditor)
+ sltNameChanged(m_pNameAndSystemEditor->name());
+
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltGAISOPathChanged(const QString &strPath)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ m_userModifiedParameters << "GuestAdditionsISOPath";
+ wizardWindow<UIWizardNewVM>()->setGuestAdditionsISOPath(strPath);
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltInstallGACheckBoxToggle(bool fEnabled)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setInstallGuestAdditions(fEnabled);
+ m_userModifiedParameters << "InstallGuestAdditions";
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltOSFamilyTypeChanged(const QString &strGuestOSFamilyType)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ if (m_pAdditionalOptionsContainer)
+ m_pAdditionalOptionsContainer->disableEnableProductKeyWidgets(isProductKeyWidgetEnabled());
+ m_userModifiedParameters << "GuestOSFamilyId";
+ wizardWindow<UIWizardNewVM>()->setGuestOSFamilyId(strGuestOSFamilyType);
+}
+
+void UIWizardNewVMExpertPage::retranslateUi()
+{
+ if (m_pSkipUnattendedCheckBox)
+ {
+ m_pSkipUnattendedCheckBox->setText(UIWizardNewVM::tr("&Skip Unattended Installation"));
+ m_pSkipUnattendedCheckBox->setToolTip(UIWizardNewVM::tr("When checked, the unattended install is disabled and the selected ISO "
+ "is mounted on the vm."));
+ }
+
+ if (m_pToolBox)
+ {
+ m_pToolBox->setPageTitle(ExpertToolboxItems_NameAndOSType, QString(UIWizardNewVM::tr("Name and &Operating System")));
+ m_pToolBox->setPageTitle(ExpertToolboxItems_Unattended, UIWizardNewVM::tr("&Unattended Install"));
+ m_pToolBox->setPageTitle(ExpertToolboxItems_Disk, UIWizardNewVM::tr("Hard Dis&k"));
+ m_pToolBox->setPageTitle(ExpertToolboxItems_Hardware, UIWizardNewVM::tr("H&ardware"));
+ }
+
+ if (m_pDiskEmpty)
+ m_pDiskEmpty->setText(UIWizardNewVM::tr("&Do Not Add a Virtual Hard Disk"));
+ if (m_pDiskNew)
+ m_pDiskNew->setText(UIWizardNewVM::tr("&Create a Virtual Hard Disk Now"));
+ if (m_pDiskExisting)
+ m_pDiskExisting->setText(UIWizardNewVM::tr("U&se an Existing Virtual Hard Disk File"));
+ if (m_pDiskSelectionButton)
+ m_pDiskSelectionButton->setToolTip(UIWizardNewVM::tr("Chooses a Virtual Hard Fisk File..."));
+
+ if (m_pNameAndSystemLayout && m_pNameAndSystemEditor)
+ m_pNameAndSystemLayout->setColumnMinimumWidth(0, m_pNameAndSystemEditor->firstColumnWidth());
+
+ if (m_pDiskFormatVariantGroupBox)
+ m_pDiskFormatVariantGroupBox->setTitle(UIWizardNewVM::tr("Hard Disk File &Type and Variant"));
+}
+
+void UIWizardNewVMExpertPage::createConnections()
+{
+ /* Connections for Name, OS Type, and unattended install stuff: */
+ if (m_pNameAndSystemEditor)
+ {
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigNameChanged,
+ this, &UIWizardNewVMExpertPage::sltNameChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigPathChanged,
+ this, &UIWizardNewVMExpertPage::sltPathChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigOsTypeChanged,
+ this, &UIWizardNewVMExpertPage::sltOsTypeChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigOSFamilyChanged,
+ this, &UIWizardNewVMExpertPage::sltOSFamilyTypeChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigImageChanged,
+ this, &UIWizardNewVMExpertPage::sltISOPathChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigEditionChanged,
+ this, &UIWizardNewVMExpertPage::sltSelectedEditionChanged);
+ }
+
+ if (m_pHardwareWidgetContainer)
+ {
+ connect(m_pHardwareWidgetContainer, &UINewVMHardwareContainer::sigMemorySizeChanged,
+ this, &UIWizardNewVMExpertPage::sltMemorySizeChanged);
+ connect(m_pHardwareWidgetContainer, &UINewVMHardwareContainer::sigCPUCountChanged,
+ this, &UIWizardNewVMExpertPage::sltCPUCountChanged);
+ connect(m_pHardwareWidgetContainer, &UINewVMHardwareContainer::sigEFIEnabledChanged,
+ this, &UIWizardNewVMExpertPage::sltEFIEnabledChanged);
+ }
+ /* Connections for username, password, and hostname, etc: */
+ if (m_pGAInstallationISOContainer)
+ {
+ connect(m_pGAInstallationISOContainer, &UIGAInstallationGroupBox::sigPathChanged,
+ this, &UIWizardNewVMExpertPage::sltGAISOPathChanged);
+ connect(m_pGAInstallationISOContainer, &UIGAInstallationGroupBox::toggled,
+ this, &UIWizardNewVMExpertPage::sltInstallGACheckBoxToggle);
+ }
+
+ if (m_pUserNamePasswordGroupBox)
+ {
+ connect(m_pUserNamePasswordGroupBox, &UIUserNamePasswordGroupBox::sigPasswordChanged,
+ this, &UIWizardNewVMExpertPage::sltPasswordChanged);
+ connect(m_pUserNamePasswordGroupBox, &UIUserNamePasswordGroupBox::sigUserNameChanged,
+ this, &UIWizardNewVMExpertPage::sltUserNameChanged);
+ }
+
+ if (m_pAdditionalOptionsContainer)
+ {
+ connect(m_pAdditionalOptionsContainer, &UIAdditionalUnattendedOptions::sigHostnameDomainNameChanged,
+ this, &UIWizardNewVMExpertPage::sltHostnameDomainNameChanged);
+ connect(m_pAdditionalOptionsContainer, &UIAdditionalUnattendedOptions::sigProductKeyChanged,
+ this, &UIWizardNewVMExpertPage::sltProductKeyChanged);
+ connect(m_pAdditionalOptionsContainer, &UIAdditionalUnattendedOptions::sigStartHeadlessChanged,
+ this, &UIWizardNewVMExpertPage::sltStartHeadlessChanged);
+ }
+
+ /* Virtual disk related connections: */
+ if (m_pDiskSourceButtonGroup)
+ connect(m_pDiskSourceButtonGroup, static_cast<void(QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked),
+ this, &UIWizardNewVMExpertPage::sltSelectedDiskSourceChanged);
+
+ if (m_pSkipUnattendedCheckBox)
+ connect(m_pSkipUnattendedCheckBox, &QCheckBox::toggled,
+ this, &UIWizardNewVMExpertPage::sltSkipUnattendedCheckBoxChecked);
+
+ if (m_pSizeAndLocationGroup)
+ {
+ connect(m_pSizeAndLocationGroup, &UIMediumSizeAndPathGroupBox::sigMediumSizeChanged,
+ this, &UIWizardNewVMExpertPage::sltMediumSizeChanged);
+ connect(m_pSizeAndLocationGroup, &UIMediumSizeAndPathGroupBox::sigMediumPathChanged,
+ this, &UIWizardNewVMExpertPage::sltMediumPathChanged);
+ connect(m_pSizeAndLocationGroup, &UIMediumSizeAndPathGroupBox::sigMediumLocationButtonClicked,
+ this, &UIWizardNewVMExpertPage::sltMediumLocationButtonClicked);
+ }
+
+ if (m_pDiskSelectionButton)
+ connect(m_pDiskSelectionButton, &QIToolButton::clicked,
+ this, &UIWizardNewVMExpertPage::sltGetWithFileOpenDialog);
+
+ if (m_pDiskSelector)
+ connect(m_pDiskSelector, static_cast<void(UIMediaComboBox::*)(int)>(&UIMediaComboBox::currentIndexChanged),
+ this, &UIWizardNewVMExpertPage::sltMediaComboBoxIndexChanged);
+
+ if (m_pFormatComboBox)
+ connect(m_pFormatComboBox, &UIDiskFormatsComboBox::sigMediumFormatChanged,
+ this, &UIWizardNewVMExpertPage::sltMediumFormatChanged);
+
+ if (m_pDiskVariantWidget)
+ connect(m_pDiskVariantWidget, &UIDiskVariantWidget::sigMediumVariantChanged,
+ this, &UIWizardNewVMExpertPage::sltMediumVariantChanged);
+}
+
+void UIWizardNewVMExpertPage::setOSTypeDependedValues()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+
+ /* Get recommended 'ram' field value: */
+ const CGuestOSType &type = pWizard->guestOSType();
+ ULONG recommendedRam = type.GetRecommendedRAM();
+
+ if (m_pHardwareWidgetContainer)
+ {
+ m_pHardwareWidgetContainer->blockSignals(true);
+
+ /* Set memory size of the widget and the wizard: */
+ if (!m_userModifiedParameters.contains("MemorySize"))
+ {
+ m_pHardwareWidgetContainer->setMemorySize(recommendedRam);
+ pWizard->setMemorySize(recommendedRam);
+ }
+
+ /* Set Firmware Type of the widget and the wizard: */
+ KFirmwareType fwType = type.GetRecommendedFirmware();
+ if (!m_userModifiedParameters.contains("EFIEnabled"))
+ {
+ m_pHardwareWidgetContainer->setEFIEnabled(fwType != KFirmwareType_BIOS);
+ pWizard->setEFIEnabled(fwType != KFirmwareType_BIOS);
+ }
+
+ /* Initialize CPU count:*/
+ int iCPUCount = type.GetRecommendedCPUCount();
+ if (!m_userModifiedParameters.contains("CPUCount"))
+ {
+ m_pHardwareWidgetContainer->setCPUCount(iCPUCount);
+ pWizard->setCPUCount(iCPUCount);
+ }
+ m_pHardwareWidgetContainer->blockSignals(false);
+ }
+
+ LONG64 iRecommendedDiskSize = type.GetRecommendedHDD();
+ /* Prepare initial disk choice: */
+ if (!m_userModifiedParameters.contains("SelectedDiskSource"))
+ {
+ if (iRecommendedDiskSize != 0)
+ {
+ if (m_pDiskNew)
+ m_pDiskNew->setChecked(true);
+ pWizard->setDiskSource(SelectedDiskSource_New);
+ setEnableDiskSelectionWidgets(false);
+ setEnableNewDiskWidgets(true);
+ m_fRecommendedNoDisk = false;
+ }
+ else
+ {
+ if (m_pDiskEmpty)
+ m_pDiskEmpty->setChecked(true);
+ pWizard->setDiskSource(SelectedDiskSource_Empty);
+ setEnableDiskSelectionWidgets(false);
+ setEnableNewDiskWidgets(false);
+ m_fRecommendedNoDisk = true;
+ }
+ if (m_pDiskSelector)
+ m_pDiskSelector->setCurrentIndex(0);
+ }
+ /* Initialize the medium size widgets and the member parameter of the wizard: */
+ if (m_pSizeAndLocationGroup && !m_userModifiedParameters.contains("MediumSize"))
+ {
+ m_pSizeAndLocationGroup->setMediumSize(iRecommendedDiskSize);
+ pWizard->setMediumSize(iRecommendedDiskSize);
+ }
+}
+
+void UIWizardNewVMExpertPage::initializePage()
+{
+ /* We need not to check existence of parameter within m_userModifiedParameters since initializePage() runs
+ once the page loads before user has a chance to modify parameters explicitly: */
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ /* Initialize wizard properties: */
+ {
+ if (m_pNameAndSystemEditor)
+ {
+ /* Guest OS type: */
+ pWizard->setGuestOSFamilyId(m_pNameAndSystemEditor->familyId());
+ pWizard->setGuestOSType(m_pNameAndSystemEditor->type());
+ /* Vm name, folder, file path etc. will be initilized by composeMachineFilePath: */
+ }
+
+ /* Medium related properties: */
+ if (m_pFormatComboBox)
+ pWizard->setMediumFormat(m_pFormatComboBox->mediumFormat());
+ updateVirtualMediumPathFromMachinePathName();
+ }
+
+ /* Initialize user/password if they are not modified by the user: */
+ if (m_pUserNamePasswordGroupBox)
+ {
+ m_pUserNamePasswordGroupBox->blockSignals(true);
+ m_pUserNamePasswordGroupBox->setUserName(pWizard->userName());
+ m_pUserNamePasswordGroupBox->setPassword(pWizard->password());
+ m_pUserNamePasswordGroupBox->blockSignals(false);
+ }
+ updateHostnameDomainNameFromMachineName();
+
+ if (m_pGAInstallationISOContainer)
+ {
+ m_pGAInstallationISOContainer->blockSignals(true);
+ m_pGAInstallationISOContainer->setChecked(pWizard->installGuestAdditions());
+ m_pGAInstallationISOContainer->blockSignals(false);
+ }
+
+ setOSTypeDependedValues();
+ setSkipCheckBoxEnable();
+ disableEnableUnattendedRelatedWidgets(isUnattendedEnabled());
+ updateDiskWidgetsAfterMediumFormatChange();
+ retranslateUi();
+
+ /* Focus on the name field (rather than the help button): */
+ if (m_pNameAndSystemEditor)
+ m_pNameAndSystemEditor->setFocus();
+}
+
+void UIWizardNewVMExpertPage::markWidgets() const
+{
+ if (m_pNameAndSystemEditor)
+ {
+ m_pNameAndSystemEditor->markNameEditor(m_pNameAndSystemEditor->name().isEmpty());
+ m_pNameAndSystemEditor->markImageEditor(!UIWizardNewVMNameOSTypeCommon::checkISOFile(m_pNameAndSystemEditor),
+ UIWizardNewVM::tr("Invalid file path or unreadable file"));
+ }
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ if (pWizard && pWizard->installGuestAdditions() && m_pGAInstallationISOContainer)
+ m_pGAInstallationISOContainer->mark();
+ if (isUnattendedEnabled())
+ m_pAdditionalOptionsContainer->mark();
+}
+
+QWidget *UIWizardNewVMExpertPage::createUnattendedWidgets()
+{
+ QWidget *pContainerWidget = new QWidget;
+ QGridLayout *pLayout = new QGridLayout(pContainerWidget);
+ pLayout->setContentsMargins(0, 0, 0, 0);
+ int iRow = 0;
+ m_pUserNamePasswordGroupBox = new UIUserNamePasswordGroupBox;
+ AssertReturn(m_pUserNamePasswordGroupBox, 0);
+ pLayout->addWidget(m_pUserNamePasswordGroupBox, iRow, 0, 1, 2);
+
+ m_pAdditionalOptionsContainer = new UIAdditionalUnattendedOptions;
+ AssertReturn(m_pAdditionalOptionsContainer, 0);
+ pLayout->addWidget(m_pAdditionalOptionsContainer, iRow, 2, 1, 2);
+
+ ++iRow;
+
+ /* Guest additions installation: */
+ m_pGAInstallationISOContainer = new UIGAInstallationGroupBox;
+ AssertReturn(m_pGAInstallationISOContainer, 0);
+ pLayout->addWidget(m_pGAInstallationISOContainer, iRow, 0, 1, 4);
+
+ return pContainerWidget;
+}
+
+QWidget *UIWizardNewVMExpertPage::createNewDiskWidgets()
+{
+ QWidget *pNewDiskContainerWidget = new QWidget;
+ QGridLayout *pDiskContainerLayout = new QGridLayout(pNewDiskContainerWidget);
+
+ m_pSizeAndLocationGroup = new UIMediumSizeAndPathGroupBox(true, 0 /* parent */, _4M /* minimum size */);
+ pDiskContainerLayout->addWidget(m_pSizeAndLocationGroup, 0, 0, 2, 2);
+
+ m_pDiskFormatVariantGroupBox = new QGroupBox;
+ QHBoxLayout *pDiskFormatVariantLayout = new QHBoxLayout(m_pDiskFormatVariantGroupBox);
+
+ m_pFormatComboBox = new UIDiskFormatsComboBox(true, KDeviceType_HardDisk, 0);
+ pDiskFormatVariantLayout->addWidget(m_pFormatComboBox, 0 /* stretch */, Qt::AlignTop);
+
+ m_pDiskVariantWidget = new UIDiskVariantWidget(0);
+ pDiskFormatVariantLayout->addWidget(m_pDiskVariantWidget);
+
+ pDiskContainerLayout->addWidget(m_pDiskFormatVariantGroupBox, 2, 0, 2, 2);
+ return pNewDiskContainerWidget;
+}
+
+QWidget *UIWizardNewVMExpertPage::createDiskWidgets()
+{
+ QWidget *pDiskContainer = new QWidget;
+ QGridLayout *pDiskLayout = new QGridLayout(pDiskContainer);
+ pDiskLayout->setContentsMargins(0, 0, 0, 0);
+ m_pDiskSourceButtonGroup = new QButtonGroup(this);
+ m_pDiskEmpty = new QRadioButton;
+ m_pDiskNew = new QRadioButton;
+ m_pDiskExisting = new QRadioButton;
+ m_pDiskSourceButtonGroup->addButton(m_pDiskEmpty);
+ m_pDiskSourceButtonGroup->addButton(m_pDiskNew);
+ m_pDiskSourceButtonGroup->addButton(m_pDiskExisting);
+ QStyleOptionButton options;
+ options.initFrom(m_pDiskExisting);
+ int iWidth = m_pDiskExisting->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, &options, m_pDiskExisting);
+ pDiskLayout->setColumnMinimumWidth(0, iWidth);
+ m_pDiskSelector = new UIMediaComboBox;
+ {
+ m_pDiskSelector->setType(UIMediumDeviceType_HardDisk);
+ m_pDiskSelector->repopulate();
+ }
+ m_pDiskSelectionButton = new QIToolButton;
+ {
+ m_pDiskSelectionButton->setAutoRaise(true);
+ m_pDiskSelectionButton->setIcon(UIIconPool::iconSet(":/select_file_16px.png", ":/select_file_disabled_16px.png"));
+ }
+ pDiskLayout->addWidget(m_pDiskNew, 0, 0, 1, 6);
+ pDiskLayout->addWidget(createNewDiskWidgets(), 1, 2, 3, 4);
+ pDiskLayout->addWidget(m_pDiskExisting, 4, 0, 1, 6);
+ pDiskLayout->addWidget(m_pDiskSelector, 5, 2, 1, 3);
+ pDiskLayout->addWidget(m_pDiskSelectionButton, 5, 5, 1, 1);
+ pDiskLayout->addWidget(m_pDiskEmpty, 6, 0, 1, 6);
+ return pDiskContainer;
+}
+
+bool UIWizardNewVMExpertPage::isComplete() const
+{
+ markWidgets();
+ bool fIsComplete = true;
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_NameAndOSType, QIcon());
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_Unattended, QIcon());
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_Disk, QIcon());
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_Hardware, QIcon());
+
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturn(pWizard, false);
+
+ /* Check unattended install related stuff: */
+ if (isUnattendedEnabled())
+ {
+ /* Check the installation medium: */
+ if (!UIWizardNewVMNameOSTypeCommon::checkISOFile(m_pNameAndSystemEditor))
+ {
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_NameAndOSType,
+ UIIconPool::iconSet(":/status_error_16px.png"),
+ UIWizardNewVM::tr("Invalid path or unreadable ISO file"));
+ fIsComplete = false;
+ }
+ /* Check the GA installation medium: */
+ if (m_pGAInstallationISOContainer && !m_pGAInstallationISOContainer->isComplete())
+ {
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_Unattended,
+ UIIconPool::iconSet(":/status_error_16px.png"),
+ UIWizardNewVM::tr("Invalid path or unreadable ISO file"));
+
+ fIsComplete = false;
+ }
+ if (m_pUserNamePasswordGroupBox)
+ {
+ if (!m_pUserNamePasswordGroupBox->isComplete())
+ {
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_Unattended,
+ UIIconPool::iconSet(":/status_error_16px.png"),
+ UIWizardNewVM::tr("Invalid username and/or password"));
+ fIsComplete = false;
+ }
+ }
+ if (m_pAdditionalOptionsContainer)
+ {
+ if (!m_pAdditionalOptionsContainer->isComplete())
+ {
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_Unattended,
+ UIIconPool::iconSet(":/status_error_16px.png"),
+ UIWizardNewVM::tr("Invalid hostname or domain name"));
+ fIsComplete = false;
+ }
+ }
+ }
+
+ if (m_pNameAndSystemEditor)
+ {
+ if (m_pNameAndSystemEditor->name().isEmpty())
+ {
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_NameAndOSType,
+ UIIconPool::iconSet(":/status_error_16px.png"),
+ UIWizardNewVM::tr("Virtual machine name is invalid"));
+ fIsComplete = false;
+ }
+ if (!UIWizardNewVMNameOSTypeCommon::checkISOFile(m_pNameAndSystemEditor))
+ {
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_NameAndOSType,
+ UIIconPool::iconSet(":/status_error_16px.png"),
+ UIWizardNewVM::tr("Invalid ISO file"));
+ fIsComplete = false;
+ }
+ }
+
+ if (pWizard->diskSource() == SelectedDiskSource_Existing && uiCommon().medium(m_pDiskSelector->id()).isNull())
+ {
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_Disk,
+ UIIconPool::iconSet(":/status_error_16px.png"), UIWizardNewVM::tr("No valid disk is selected"));
+ fIsComplete = false;
+ }
+
+ if (pWizard->diskSource() == SelectedDiskSource_New)
+ {
+ qulonglong uSize = pWizard->mediumSize();
+ if( uSize < m_uMediumSizeMin || uSize > m_uMediumSizeMax)
+ {
+ m_pToolBox->setPageTitleIcon(ExpertToolboxItems_Disk,
+ UIIconPool::iconSet(":/status_error_16px.png"), UIWizardNewVM::tr("Invalid disk size"));
+ fIsComplete = false;
+ }
+ }
+ return fIsComplete;
+}
+
+bool UIWizardNewVMExpertPage::validatePage()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturn(pWizard, false);
+ bool fResult = UIWizardNewVMNameOSTypeCommon::createMachineFolder(m_pNameAndSystemEditor, wizardWindow<UIWizardNewVM>());
+ if (!fResult)
+ return false;
+
+ if (pWizard->diskSource() == SelectedDiskSource_New)
+ {
+ /* Check if the path we will be using for hard drive creation exists: */
+ const QString &strMediumPath = pWizard->mediumPath();
+ fResult = !QFileInfo(strMediumPath).exists();
+ if (!fResult)
+ {
+ UINotificationMessage::cannotOverwriteMediumStorage(strMediumPath, wizard()->notificationCenter());
+ return fResult;
+ }
+ qulonglong uSize = pWizard->mediumSize();
+ qulonglong uVariant = pWizard->mediumVariant();
+ /* Check FAT size limitation of the host hard drive: */
+ fResult = UIWizardDiskEditors::checkFATSizeLimitation(uVariant, strMediumPath, uSize);
+ if (!fResult)
+ {
+ UINotificationMessage::cannotCreateMediumStorageInFAT(strMediumPath, wizard()->notificationCenter());
+ return fResult;
+ }
+ /* Try to create the hard drive:*/
+ fResult = pWizard->createVirtualDisk();
+ /*Don't show any error message here since UIWizardNewVM::createVirtualDisk already does so: */
+ if (!fResult)
+ return fResult;
+ }
+
+ return pWizard->createVM();
+}
+
+bool UIWizardNewVMExpertPage::isProductKeyWidgetEnabled() const
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ if (!pWizard || !isUnattendedEnabled() || !pWizard->isGuestOSTypeWindows())
+ return false;
+ return true;
+}
+
+void UIWizardNewVMExpertPage::disableEnableUnattendedRelatedWidgets(bool fEnabled)
+{
+ if (m_pUserNamePasswordGroupBox)
+ m_pUserNamePasswordGroupBox->setEnabled(fEnabled);
+ if (m_pAdditionalOptionsContainer)
+ m_pAdditionalOptionsContainer->setEnabled(fEnabled);
+ if (m_pGAInstallationISOContainer)
+ m_pGAInstallationISOContainer->setEnabled(fEnabled);
+ if (m_pNameAndSystemEditor)
+ m_pNameAndSystemEditor->setEditionSelectorEnabled(fEnabled && !m_pNameAndSystemEditor->isEditionsSelectorEmpty());
+ m_pAdditionalOptionsContainer->disableEnableProductKeyWidgets(isProductKeyWidgetEnabled());
+}
+
+void UIWizardNewVMExpertPage::sltSkipUnattendedCheckBoxChecked(bool fSkip)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ m_userModifiedParameters << "SkipUnattendedInstall";
+ wizardWindow<UIWizardNewVM>()->setSkipUnattendedInstall(fSkip);
+ disableEnableUnattendedRelatedWidgets(isUnattendedEnabled());
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltMediumFormatChanged()
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ if (!m_pFormatComboBox)
+ return;
+
+ m_userModifiedParameters << "MediumFormat";
+ wizardWindow<UIWizardNewVM>()->setMediumFormat(m_pFormatComboBox->mediumFormat());
+ updateDiskWidgetsAfterMediumFormatChange();
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltMediumSizeChanged(qulonglong uSize)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ m_userModifiedParameters << "MediumSize";
+ wizardWindow<UIWizardNewVM>()->setMediumSize(uSize);
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltMediumPathChanged(const QString &strPath)
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ AssertReturnVoid(!strPath.isEmpty());
+ m_userModifiedParameters << "MediumPath";
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(strPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk));
+ pWizard->setMediumPath(strMediumPath);
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltMediumLocationButtonClicked()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ CMediumFormat comMediumFormat(pWizard->mediumFormat());
+
+ QString strMediumPath =
+ UIWizardDiskEditors::appendExtension(m_pSizeAndLocationGroup->mediumFilePath(),
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk));
+ QString strSelectedPath =
+ UIWizardDiskEditors::openFileDialogForDiskFile(strMediumPath, comMediumFormat, KDeviceType_HardDisk, pWizard);
+ if (strSelectedPath.isEmpty())
+ return;
+ strMediumPath =
+ UIWizardDiskEditors::appendExtension(strSelectedPath,
+ UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk));
+ QFileInfo mediumPath(strMediumPath);
+ m_pSizeAndLocationGroup->setMediumFilePath(QDir::toNativeSeparators(mediumPath.absoluteFilePath()));
+}
+
+void UIWizardNewVMExpertPage::sltMediumVariantChanged(qulonglong uVariant)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ m_userModifiedParameters << "MediumVariant";
+ wizardWindow<UIWizardNewVM>()->setMediumVariant(uVariant);
+}
+
+void UIWizardNewVMExpertPage::sltMediaComboBoxIndexChanged()
+{
+ AssertReturnVoid(m_pDiskSelector);
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+
+ /* Make sure to set m_virtualDisk: */
+ pWizard->setVirtualDisk(m_pDiskSelector->id());
+ pWizard->setMediumPath(m_pDiskSelector->location());
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltSelectedDiskSourceChanged()
+{
+ AssertReturnVoid(m_pDiskSelector && m_pDiskSourceButtonGroup);
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ m_userModifiedParameters << "SelectedDiskSource";
+
+ if (m_pDiskSourceButtonGroup->checkedButton() == m_pDiskEmpty)
+ {
+ pWizard->setDiskSource(SelectedDiskSource_Empty);
+ pWizard->setVirtualDisk(QUuid());
+ pWizard->setMediumPath(QString());
+ }
+ else if (m_pDiskSourceButtonGroup->checkedButton() == m_pDiskExisting)
+ {
+ pWizard->setDiskSource(SelectedDiskSource_Existing);
+ pWizard->setVirtualDisk(m_pDiskSelector->id());
+ pWizard->setMediumPath(m_pDiskSelector->location());
+ }
+ else
+ {
+ pWizard->setDiskSource(SelectedDiskSource_New);
+ pWizard->setVirtualDisk(QUuid());
+ pWizard->setMediumPath(QString());
+ }
+
+ setEnableDiskSelectionWidgets(pWizard->diskSource() == SelectedDiskSource_Existing);
+ setEnableNewDiskWidgets(pWizard->diskSource() == SelectedDiskSource_New);
+
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltMemorySizeChanged(int iValue)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setMemorySize(iValue);
+ m_userModifiedParameters << "MemorySize";
+}
+
+void UIWizardNewVMExpertPage::sltCPUCountChanged(int iCount)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setCPUCount(iCount);
+ m_userModifiedParameters << "CPUCount";
+}
+
+void UIWizardNewVMExpertPage::sltEFIEnabledChanged(bool fEnabled)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setEFIEnabled(fEnabled);
+ m_userModifiedParameters << "EFIEnabled";
+}
+
+void UIWizardNewVMExpertPage::sltPasswordChanged(const QString &strPassword)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setPassword(strPassword);
+ m_userModifiedParameters << "Password";
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltUserNameChanged(const QString &strUserName)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setUserName(strUserName);
+ m_userModifiedParameters << "UserName";
+ emit completeChanged();
+}
+
+void UIWizardNewVMExpertPage::sltHostnameDomainNameChanged(const QString &strHostnameDomainName, bool fIsComplete)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ emit completeChanged();
+
+ if (fIsComplete)
+ {
+ wizardWindow<UIWizardNewVM>()->setHostnameDomainName(strHostnameDomainName);
+ m_userModifiedParameters << "HostnameDomainName";
+ }
+}
+
+void UIWizardNewVMExpertPage::sltProductKeyChanged(const QString &strProductKey)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ m_userModifiedParameters << "ProductKey";
+ wizardWindow<UIWizardNewVM>()->setProductKey(strProductKey);
+}
+
+void UIWizardNewVMExpertPage::sltStartHeadlessChanged(bool fStartHeadless)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ m_userModifiedParameters << "StartHeadless";
+ wizardWindow<UIWizardNewVM>()->setStartHeadless(fStartHeadless);
+}
+
+void UIWizardNewVMExpertPage::sltSelectedEditionChanged(ulong uEditionIndex)
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ pWizard->setSelectedWindowImageIndex(uEditionIndex);
+ /* Update the OS type since IUnattended updates the detected OS type after edition (image index) changes: */
+ UIWizardNewVMNameOSTypeCommon::guessOSTypeDetectedOSTypeString(m_pNameAndSystemEditor, pWizard->detectedOSTypeId());
+}
+
+void UIWizardNewVMExpertPage::updateVirtualMediumPathFromMachinePathName()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ QString strDiskFileName = pWizard->machineFileName().isEmpty() ? QString("NewVirtualDisk1") : pWizard->machineFileName();
+ QString strMediumPath = pWizard->machineFolder();
+ if (strMediumPath.isEmpty())
+ {
+ if (m_pNameAndSystemEditor)
+ strMediumPath = m_pNameAndSystemEditor->path();
+ else
+ strMediumPath = uiCommon().virtualBox().GetSystemProperties().GetDefaultMachineFolder();
+ }
+ QString strExtension = UIWizardDiskEditors::defaultExtension(pWizard->mediumFormat(), KDeviceType_HardDisk);
+ if (m_pSizeAndLocationGroup)
+ {
+ QString strMediumFilePath =
+ UIWizardDiskEditors::constructMediumFilePath(UIWizardDiskEditors::appendExtension(strDiskFileName,
+ strExtension), strMediumPath);
+ m_pSizeAndLocationGroup->blockSignals(true);
+ m_pSizeAndLocationGroup->setMediumFilePath(strMediumFilePath);
+ m_pSizeAndLocationGroup->blockSignals(false);
+ pWizard->setMediumPath(m_pSizeAndLocationGroup->mediumFilePath());
+ }
+}
+
+void UIWizardNewVMExpertPage::updateDiskWidgetsAfterMediumFormatChange()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard && m_pDiskVariantWidget && m_pSizeAndLocationGroup && m_pFormatComboBox);
+ const CMediumFormat &comMediumFormat = pWizard->mediumFormat();
+ AssertReturnVoid(!comMediumFormat.isNull());
+
+ /* Block signals of the updated widgets to avoid calling corresponding slots since they add the parameters to m_userModifiedParameters: */
+ m_pDiskVariantWidget->blockSignals(true);
+ m_pDiskVariantWidget->updateMediumVariantWidgetsAfterFormatChange(comMediumFormat);
+ m_pDiskVariantWidget->blockSignals(false);
+
+ m_pSizeAndLocationGroup->blockSignals(true);
+ m_pSizeAndLocationGroup->updateMediumPath(comMediumFormat, m_pFormatComboBox->formatExtensions(), KDeviceType_HardDisk);
+ m_pSizeAndLocationGroup->blockSignals(false);
+ /* Update the wizard parameters explicitly since we blocked th signals: */
+ pWizard->setMediumPath(m_pSizeAndLocationGroup->mediumFilePath());
+ pWizard->setMediumVariant(m_pDiskVariantWidget->mediumVariant());
+}
+
+void UIWizardNewVMExpertPage::setEnableNewDiskWidgets(bool fEnable)
+{
+ if (m_pSizeAndLocationGroup)
+ m_pSizeAndLocationGroup->setEnabled(fEnable);
+ if (m_pFormatComboBox)
+ m_pFormatComboBox->setEnabled(fEnable);
+ if (m_pDiskVariantWidget)
+ m_pDiskVariantWidget->setEnabled(fEnable);
+}
+
+QWidget *UIWizardNewVMExpertPage::createNameOSTypeWidgets()
+{
+ QWidget *pContainerWidget = new QWidget;
+ AssertReturn(pContainerWidget, 0);
+ m_pNameAndSystemLayout = new QGridLayout(pContainerWidget);
+ AssertReturn(m_pNameAndSystemLayout, 0);
+ m_pNameAndSystemLayout->setContentsMargins(0, 0, 0, 0);
+ m_pNameAndSystemEditor = new UINameAndSystemEditor(0,
+ true /* fChooseName? */,
+ true /* fChoosePath? */,
+ true /* fChooseImage? */,
+ true /* fChoseEdition? */,
+ true /* fChooseType? */);
+ if (m_pNameAndSystemEditor)
+ m_pNameAndSystemLayout->addWidget(m_pNameAndSystemEditor, 0, 0, 1, 2);
+ m_pSkipUnattendedCheckBox = new QCheckBox;
+ if (m_pSkipUnattendedCheckBox)
+ m_pNameAndSystemLayout->addWidget(m_pSkipUnattendedCheckBox, 1, 1);
+ return pContainerWidget;
+}
+
+void UIWizardNewVMExpertPage::setSkipCheckBoxEnable()
+{
+ AssertReturnVoid(m_pSkipUnattendedCheckBox && m_pNameAndSystemEditor);
+ const QString &strPath = m_pNameAndSystemEditor->ISOImagePath();
+ if (strPath.isEmpty())
+ {
+ m_pSkipUnattendedCheckBox->setEnabled(false);
+ return;
+ }
+ if (!isUnattendedInstallSupported())
+ {
+ m_pSkipUnattendedCheckBox->setEnabled(false);
+ return;
+ }
+
+ m_pSkipUnattendedCheckBox->setEnabled(UIWizardNewVMNameOSTypeCommon::checkISOFile(m_pNameAndSystemEditor));
+}
+
+void UIWizardNewVMExpertPage::updateHostnameDomainNameFromMachineName()
+{
+ if (!m_pAdditionalOptionsContainer)
+ return;
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+
+ m_pAdditionalOptionsContainer->blockSignals(true);
+ m_pAdditionalOptionsContainer->setHostname(pWizard->machineBaseName());
+ m_pAdditionalOptionsContainer->setDomainName("myguest.virtualbox.org");
+ /* Initialize unattended hostname here since we cannot get the default value from CUnattended this early (unlike username etc): */
+ if (m_pAdditionalOptionsContainer->isHostnameComplete())
+ pWizard->setHostnameDomainName(m_pAdditionalOptionsContainer->hostnameDomainName());
+
+ m_pAdditionalOptionsContainer->blockSignals(false);
+}
+
+bool UIWizardNewVMExpertPage::isUnattendedEnabled() const
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturn(pWizard, false);
+ return pWizard->isUnattendedEnabled();
+}
+
+bool UIWizardNewVMExpertPage::isUnattendedInstallSupported() const
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturn(pWizard, false);
+ return pWizard->isUnattendedInstallSupported();
+}
+
+void UIWizardNewVMExpertPage::setEnableDiskSelectionWidgets(bool fEnabled)
+{
+ if (!m_pDiskSelector || !m_pDiskSelectionButton)
+ return;
+
+ m_pDiskSelector->setEnabled(fEnabled);
+ m_pDiskSelectionButton->setEnabled(fEnabled);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMExpertPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMExpertPage.h
new file mode 100644
index 00000000..97f49051
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMExpertPage.h
@@ -0,0 +1,168 @@
+/* $Id: UIWizardNewVMExpertPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMExpertPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMExpertPage_h
+#define FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMExpertPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSet>
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QButtonGroup;
+class QCheckBox;
+class QGridLayout;
+class QGroupBox;
+class QRadioButton;
+class QIToolButton;
+class UIActionPool;
+class UIAdditionalUnattendedOptions;
+class UIDiskFormatsComboBox;
+class UIDiskVariantWidget;
+class UIMediumSizeAndPathGroupBox;
+class UIGAInstallationGroupBox;
+class UIMediaComboBox;
+class UINameAndSystemEditor;
+class UINewVMHardwareContainer;
+class UIToolBox;
+class UIUserNamePasswordGroupBox;
+
+/** Expert page of the New Virtual Machine wizard. */
+class UIWizardNewVMExpertPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVMExpertPage(UIActionPool *pActionPool);
+ void setISOFilePath(const QString &strISOFilePath);
+
+private slots:
+
+ void sltNameChanged(const QString &strNewName);
+ void sltPathChanged(const QString &strNewPath);
+ void sltOsTypeChanged();
+ void sltMediaComboBoxIndexChanged();
+ void sltGetWithFileOpenDialog();
+ void sltISOPathChanged(const QString &strPath);
+ void sltGAISOPathChanged(const QString &strPath);
+ void sltOSFamilyTypeChanged(const QString &strGuestOSFamilyType);
+ void sltInstallGACheckBoxToggle(bool fEnabled);
+ void sltSkipUnattendedCheckBoxChecked(bool fSkip);
+ void sltMediumFormatChanged();
+ void sltMediumSizeChanged(qulonglong uSize);
+ void sltMediumPathChanged(const QString &strPath);
+ void sltMediumVariantChanged(qulonglong uVariant);
+ void sltSelectedDiskSourceChanged();
+ void sltMediumLocationButtonClicked();
+ void sltMemorySizeChanged(int iValue);
+ void sltCPUCountChanged(int iCount);
+ void sltEFIEnabledChanged(bool fEnabled);
+ void sltPasswordChanged(const QString &strPassword);
+ void sltUserNameChanged(const QString &strUserName);
+ void sltHostnameDomainNameChanged(const QString &strHostnameDomainName, bool fIsComplete);
+ void sltProductKeyChanged(const QString &strProductKey);
+ void sltStartHeadlessChanged(bool fStartHeadless);
+ void sltSelectedEditionChanged(ulong uEditionIndex);
+
+private:
+
+ enum ExpertToolboxItems
+ {
+ ExpertToolboxItems_NameAndOSType,
+ ExpertToolboxItems_Unattended,
+ ExpertToolboxItems_Hardware,
+ ExpertToolboxItems_Disk
+ };
+
+ /** Translation stuff. */
+ void retranslateUi();
+
+ /** Prepare stuff. */
+ void createConnections();
+ void initializePage();
+ void initializeWidgets();
+ /** Set the values of the widget if they depend on OS
+ * type like recommended RAM size. The widgets whose values are
+ * are explicitly modified are exempt from this. */
+ void setOSTypeDependedValues();
+ void cleanupPage();
+
+ /** Validation stuff. */
+ bool isComplete() const;
+ bool validatePage();
+
+ bool isProductKeyWidgetEnabled() const;
+ void disableEnableUnattendedRelatedWidgets(bool fEnabled);
+ void markWidgets() const;
+ QWidget *createUnattendedWidgets();
+ QWidget *createNewDiskWidgets();
+ QWidget *createDiskWidgets();
+ QWidget *createNameOSTypeWidgets();
+ void updateVirtualMediumPathFromMachinePathName();
+ void updateDiskWidgetsAfterMediumFormatChange();
+ void updateHostnameDomainNameFromMachineName();
+ void setEnableNewDiskWidgets(bool fEnable);
+ void setSkipCheckBoxEnable();
+ bool isUnattendedEnabled() const;
+ bool isUnattendedInstallSupported() const;
+ void setEnableDiskSelectionWidgets(bool fEnabled);
+
+ /** @name Variables
+ * @{ */
+ UIToolBox *m_pToolBox;
+ QGroupBox *m_pDiskFormatVariantGroupBox;
+ UIDiskVariantWidget *m_pDiskVariantWidget;
+ UIDiskFormatsComboBox *m_pFormatComboBox;
+ UIMediumSizeAndPathGroupBox *m_pSizeAndLocationGroup;
+ UINameAndSystemEditor *m_pNameAndSystemEditor;
+ QCheckBox *m_pSkipUnattendedCheckBox;
+ QGridLayout *m_pNameAndSystemLayout;
+ UINewVMHardwareContainer *m_pHardwareWidgetContainer;
+ UIAdditionalUnattendedOptions *m_pAdditionalOptionsContainer;
+ UIGAInstallationGroupBox *m_pGAInstallationISOContainer;
+ UIUserNamePasswordGroupBox *m_pUserNamePasswordGroupBox;
+ QButtonGroup *m_pDiskSourceButtonGroup;
+ QRadioButton *m_pDiskEmpty;
+ QRadioButton *m_pDiskNew;
+ QRadioButton *m_pDiskExisting;
+ UIMediaComboBox *m_pDiskSelector;
+ QIToolButton *m_pDiskSelectionButton;
+ QSet<QString> m_userModifiedParameters;
+ bool m_fRecommendedNoDisk;
+ qulonglong m_uMediumSizeMin;
+ qulonglong m_uMediumSizeMax;
+ UIActionPool *m_pActionPool;
+ /** @} */
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMExpertPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMHardwarePage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMHardwarePage.cpp
new file mode 100644
index 00000000..157bebf2
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMHardwarePage.cpp
@@ -0,0 +1,144 @@
+/* $Id: UIWizardNewVMHardwarePage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMHardwarePage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "UIBaseMemoryEditor.h"
+#include "UIVirtualCPUEditor.h"
+#include "UIWizardNewVM.h"
+#include "UIWizardNewVMEditors.h"
+#include "UIWizardNewVMHardwarePage.h"
+
+/* COM includes: */
+#include "CGuestOSType.h"
+
+UIWizardNewVMHardwarePage::UIWizardNewVMHardwarePage()
+ : m_pLabel(0)
+ , m_pHardwareWidgetContainer(0)
+{
+ prepare();
+ qRegisterMetaType<CMedium>();
+}
+
+void UIWizardNewVMHardwarePage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+
+ m_pLabel = new QIRichTextLabel(this);
+ pMainLayout->addWidget(m_pLabel);
+ m_pHardwareWidgetContainer = new UINewVMHardwareContainer;
+ AssertReturnVoid(m_pHardwareWidgetContainer);
+ pMainLayout->addWidget(m_pHardwareWidgetContainer);
+
+ pMainLayout->addStretch();
+ createConnections();
+}
+
+void UIWizardNewVMHardwarePage::createConnections()
+{
+ if (m_pHardwareWidgetContainer)
+ {
+ connect(m_pHardwareWidgetContainer, &UINewVMHardwareContainer::sigMemorySizeChanged,
+ this, &UIWizardNewVMHardwarePage::sltMemorySizeChanged);
+ connect(m_pHardwareWidgetContainer, &UINewVMHardwareContainer::sigCPUCountChanged,
+ this, &UIWizardNewVMHardwarePage::sltCPUCountChanged);
+ connect(m_pHardwareWidgetContainer, &UINewVMHardwareContainer::sigEFIEnabledChanged,
+ this, &UIWizardNewVMHardwarePage::sltEFIEnabledChanged);
+ }
+}
+
+void UIWizardNewVMHardwarePage::retranslateUi()
+{
+ setTitle(UIWizardNewVM::tr("Hardware"));
+
+ if (m_pLabel)
+ m_pLabel->setText(UIWizardNewVM::tr("You can modify virtual machine's hardware by changing amount of RAM and "
+ "virtual CPU count. Enabling EFI is also possible."));
+}
+
+void UIWizardNewVMHardwarePage::initializePage()
+{
+ retranslateUi();
+
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ if (pWizard && m_pHardwareWidgetContainer)
+ {
+ CGuestOSType type = pWizard->guestOSType();
+ if (!type.isNull())
+ {
+ m_pHardwareWidgetContainer->blockSignals(true);
+ if (!m_userModifiedParameters.contains("MemorySize"))
+ {
+ ULONG recommendedRam = type.GetRecommendedRAM();
+ m_pHardwareWidgetContainer->setMemorySize(recommendedRam);
+ pWizard->setMemorySize(recommendedRam);
+ }
+ if (!m_userModifiedParameters.contains("CPUCount"))
+ {
+ ULONG recommendedCPUs = type.GetRecommendedCPUCount();
+ m_pHardwareWidgetContainer->setCPUCount(recommendedCPUs);
+ pWizard->setCPUCount(recommendedCPUs);
+ }
+ if (!m_userModifiedParameters.contains("EFIEnabled"))
+ {
+ KFirmwareType fwType = type.GetRecommendedFirmware();
+ m_pHardwareWidgetContainer->setEFIEnabled(fwType != KFirmwareType_BIOS);
+ pWizard->setEFIEnabled(fwType != KFirmwareType_BIOS);
+ }
+ m_pHardwareWidgetContainer->blockSignals(false);
+ }
+ }
+}
+
+bool UIWizardNewVMHardwarePage::isComplete() const
+{
+ return true;
+}
+
+void UIWizardNewVMHardwarePage::sltMemorySizeChanged(int iValue)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setMemorySize(iValue);
+ m_userModifiedParameters << "MemorySize";
+}
+
+void UIWizardNewVMHardwarePage::sltCPUCountChanged(int iCount)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setCPUCount(iCount);
+ m_userModifiedParameters << "CPUCount";
+}
+
+void UIWizardNewVMHardwarePage::sltEFIEnabledChanged(bool fEnabled)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setEFIEnabled(fEnabled);
+ m_userModifiedParameters << "EFIEnabled";
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMHardwarePage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMHardwarePage.h
new file mode 100644
index 00000000..c1a19370
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMHardwarePage.h
@@ -0,0 +1,78 @@
+/* $Id: UIWizardNewVMHardwarePage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMHardwarePage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMHardwarePage_h
+#define FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMHardwarePage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QSet>
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QIRichTextLabel;
+class UINewVMHardwareContainer;
+
+class UIWizardNewVMHardwarePage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVMHardwarePage();
+
+private slots:
+
+ void sltMemorySizeChanged(int iValue);
+ void sltCPUCountChanged(int iCount);
+ void sltEFIEnabledChanged(bool fEnabled);
+
+private:
+
+ /** Prepare stuff. */
+ void prepare();
+ void createConnections();
+ virtual void retranslateUi() /* override final */;
+ virtual void initializePage() /* override final */;
+ virtual bool isComplete() const /* override final */;
+
+ /** @name Widgets
+ * @{ */
+ QIRichTextLabel *m_pLabel;
+ UINewVMHardwareContainer *m_pHardwareWidgetContainer;
+ /** @} */
+ /** This set is used to decide if we have to set wizard's parameters
+ * some default values or not. When user modifies a value through a widget we
+ * no longer touch user set value during page initilization. see initializePage. */
+ QSet<QString> m_userModifiedParameters;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMHardwarePage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMNameOSTypePage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMNameOSTypePage.cpp
new file mode 100644
index 00000000..9129c9a4
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMNameOSTypePage.cpp
@@ -0,0 +1,745 @@
+/* $Id: UIWizardNewVMNameOSTypePage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMPageBasicNameOSStype class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QCheckBox>
+#include <QDir>
+#include <QRegularExpression>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "UICommon.h"
+#include "UINameAndSystemEditor.h"
+#include "UINotificationCenter.h"
+#include "UIWizardNewVMNameOSTypePage.h"
+#include "UIWizardNewVM.h"
+
+/* COM includes: */
+#include "CHost.h"
+#include "CUnattended.h"
+
+/* Defines some patterns to guess the right OS type. Should be in sync with
+ * VirtualBox-settings-common.xsd in Main. The list is sorted by priority. The
+ * first matching string found, will be used. */
+struct osTypePattern
+{
+ QRegularExpression pattern;
+ const char *pcstId;
+};
+
+static const osTypePattern gs_OSTypePattern[] =
+{
+ /* DOS: */
+ { QRegularExpression("DOS", QRegularExpression::CaseInsensitiveOption), "DOS" },
+
+ /* Windows: */
+ { QRegularExpression( "Wi.*98", QRegularExpression::CaseInsensitiveOption), "Windows98" },
+ { QRegularExpression( "Wi.*95", QRegularExpression::CaseInsensitiveOption), "Windows95" },
+ { QRegularExpression( "Wi.*Me", QRegularExpression::CaseInsensitiveOption), "WindowsMe" },
+ { QRegularExpression( "(Wi.*NT)|(NT[-._v]*4)", QRegularExpression::CaseInsensitiveOption), "WindowsNT4" },
+ { QRegularExpression( "NT[-._v]*3[.,]*[51x]", QRegularExpression::CaseInsensitiveOption), "WindowsNT3x" },
+ { QRegularExpression("(Wi.*XP.*64)|(XP.*64)", QRegularExpression::CaseInsensitiveOption), "WindowsXP_64" },
+ { QRegularExpression("(XP)", QRegularExpression::CaseInsensitiveOption), "WindowsXP" },
+ { QRegularExpression("((Wi.*2003)|(W2K3)|(Win2K3)).*64", QRegularExpression::CaseInsensitiveOption), "Windows2003_64" },
+ { QRegularExpression("((Wi.*2003)|(W2K3)|(Win2K3)).*32", QRegularExpression::CaseInsensitiveOption), "Windows2003" },
+ { QRegularExpression("((Wi.*Vis)|(Vista)).*64", QRegularExpression::CaseInsensitiveOption), "WindowsVista_64" },
+ { QRegularExpression("((Wi.*Vis)|(Vista)).*32", QRegularExpression::CaseInsensitiveOption), "WindowsVista" },
+ { QRegularExpression( "(Wi.*2016)|(W2K16)|(Win2K16)", QRegularExpression::CaseInsensitiveOption), "Windows2016_64" },
+ { QRegularExpression( "(Wi.*2012)|(W2K12)|(Win2K12)", QRegularExpression::CaseInsensitiveOption), "Windows2012_64" },
+ { QRegularExpression("((Wi.*2008)|(W2K8)|(Win2k8)).*64", QRegularExpression::CaseInsensitiveOption), "Windows2008_64" },
+ { QRegularExpression("((Wi.*2008)|(W2K8)|(Win2K8)).*32", QRegularExpression::CaseInsensitiveOption), "Windows2008" },
+ { QRegularExpression( "(Wi.*2000)|(W2K)|(Win2K)", QRegularExpression::CaseInsensitiveOption), "Windows2000" },
+ { QRegularExpression( "(Wi.*7.*64)|(W7.*64)", QRegularExpression::CaseInsensitiveOption), "Windows7_64" },
+ { QRegularExpression( "(Wi.*7.*32)|(W7.*32)", QRegularExpression::CaseInsensitiveOption), "Windows7" },
+ { QRegularExpression( "(Wi.*8.*1.*64)|(W8.*64)", QRegularExpression::CaseInsensitiveOption), "Windows81_64" },
+ { QRegularExpression( "(Wi.*8.*1.*32)|(W8.*32)", QRegularExpression::CaseInsensitiveOption), "Windows81" },
+ { QRegularExpression( "(Wi.*8.*64)|(W8.*64)", QRegularExpression::CaseInsensitiveOption), "Windows8_64" },
+ { QRegularExpression( "(Wi.*8.*32)|(W8.*32)", QRegularExpression::CaseInsensitiveOption), "Windows8" },
+ { QRegularExpression( "(Wi.*10.*64)|(W10.*64)", QRegularExpression::CaseInsensitiveOption), "Windows10_64" },
+ { QRegularExpression( "(Wi.*10.*32)|(W10.*32)", QRegularExpression::CaseInsensitiveOption), "Windows10" },
+ { QRegularExpression( "(Wi.*11)|(W11)", QRegularExpression::CaseInsensitiveOption), "Windows11_64" },
+ { QRegularExpression( "Wi.*3.*1", QRegularExpression::CaseInsensitiveOption), "Windows31" },
+ /* Set Windows 10 as default for "Windows". */
+ { QRegularExpression( "Wi.*64", QRegularExpression::CaseInsensitiveOption), "Windows10_64" },
+ { QRegularExpression( "Wi.*32", QRegularExpression::CaseInsensitiveOption), "Windows10" },
+ /* ReactOS wants to be considered as Windows 2003 */
+ { QRegularExpression( "Reac.*", QRegularExpression::CaseInsensitiveOption), "Windows2003" },
+
+ /* Solaris: */
+ { QRegularExpression("((Op.*Sol)|(os20[01][0-9])|(India)|(Illum)|(Neva)).*64", QRegularExpression::CaseInsensitiveOption), "OpenSolaris_64" },
+ { QRegularExpression("((Op.*Sol)|(os20[01][0-9])|(India)|(Illum)|(Neva)).*32", QRegularExpression::CaseInsensitiveOption), "OpenSolaris" },
+ { QRegularExpression("(Sol.*10.*(10/09)|(9/10)|(8/11)|(1/13)).*64", QRegularExpression::CaseInsensitiveOption), "Solaris10U8_or_later_64" },
+ { QRegularExpression("(Sol.*10.*(10/09)|(9/10)|(8/11)|(1/13)).*32", QRegularExpression::CaseInsensitiveOption), "Solaris10U8_or_later" },
+ { QRegularExpression("(Sol.*10.*(U[89])|(U1[01])).*64", QRegularExpression::CaseInsensitiveOption), "Solaris10U8_or_later_64" },
+ { QRegularExpression("(Sol.*10.*(U[89])|(U1[01])).*32", QRegularExpression::CaseInsensitiveOption), "Solaris10U8_or_later" },
+ { QRegularExpression("(Sol.*10.*(1/06)|(6/06)|(11/06)|(8/07)|(5/08)|(10/08)|(5/09)).*64", QRegularExpression::CaseInsensitiveOption), "Solaris_64" }, // Solaris 10U7 (5/09) or earlier
+ { QRegularExpression("(Sol.*10.*(1/06)|(6/06)|(11/06)|(8/07)|(5/08)|(10/08)|(5/09)).*32", QRegularExpression::CaseInsensitiveOption), "Solaris" }, // Solaris 10U7 (5/09) or earlier
+ { QRegularExpression("((Sol.*10.*U[1-7])|(Sol.*10)).*64", QRegularExpression::CaseInsensitiveOption), "Solaris_64" }, // Solaris 10U7 (5/09) or earlier
+ { QRegularExpression("((Sol.*10.*U[1-7])|(Sol.*10)).*32", QRegularExpression::CaseInsensitiveOption), "Solaris" }, // Solaris 10U7 (5/09) or earlier
+ { QRegularExpression("((Sol.*11)|(Sol.*)).*64", QRegularExpression::CaseInsensitiveOption), "Solaris11_64" },
+
+ /* OS/2: */
+ { QRegularExpression("OS[/|!-]{,1}2.*W.*4.?5", QRegularExpression::CaseInsensitiveOption), "OS2Warp45" },
+ { QRegularExpression("OS[/|!-]{,1}2.*W.*4", QRegularExpression::CaseInsensitiveOption), "OS2Warp4" },
+ { QRegularExpression("OS[/|!-]{,1}2.*W", QRegularExpression::CaseInsensitiveOption), "OS2Warp3" },
+ { QRegularExpression("OS[/|!-]{,1}2.*e", QRegularExpression::CaseInsensitiveOption), "OS2eCS" },
+ { QRegularExpression("OS[/|!-]{,1}2.*Ar.*", QRegularExpression::CaseInsensitiveOption), "OS2ArcaOS" },
+ { QRegularExpression("OS[/|!-]{,1}2", QRegularExpression::CaseInsensitiveOption), "OS2" },
+ { QRegularExpression("(eComS.*|eCS.*)", QRegularExpression::CaseInsensitiveOption), "OS2eCS" },
+ { QRegularExpression("Arca.*", QRegularExpression::CaseInsensitiveOption), "OS2ArcaOS" },
+
+ /* Other: Must come before Ubuntu/Maverick and before Linux??? */
+ { QRegularExpression("QN", QRegularExpression::CaseInsensitiveOption), "QNX" },
+
+ /* Mac OS X: Must come before Ubuntu/Maverick and before Linux: */
+ { QRegularExpression("((mac.*10[.,]{0,1}4)|(os.*x.*10[.,]{0,1}4)|(mac.*ti)|(os.*x.*ti)|(Tig)).64", QRegularExpression::CaseInsensitiveOption), "MacOS_64" },
+ { QRegularExpression("((mac.*10[.,]{0,1}4)|(os.*x.*10[.,]{0,1}4)|(mac.*ti)|(os.*x.*ti)|(Tig)).32", QRegularExpression::CaseInsensitiveOption), "MacOS" },
+ { QRegularExpression("((mac.*10[.,]{0,1}5)|(os.*x.*10[.,]{0,1}5)|(mac.*leo)|(os.*x.*leo)|(Leop)).*64", QRegularExpression::CaseInsensitiveOption), "MacOS_64" },
+ { QRegularExpression("((mac.*10[.,]{0,1}5)|(os.*x.*10[.,]{0,1}5)|(mac.*leo)|(os.*x.*leo)|(Leop)).*32", QRegularExpression::CaseInsensitiveOption), "MacOS" },
+ { QRegularExpression("((mac.*10[.,]{0,1}6)|(os.*x.*10[.,]{0,1}6)|(mac.*SL)|(os.*x.*SL)|(Snow L)).*64", QRegularExpression::CaseInsensitiveOption), "MacOS106_64" },
+ { QRegularExpression("((mac.*10[.,]{0,1}6)|(os.*x.*10[.,]{0,1}6)|(mac.*SL)|(os.*x.*SL)|(Snow L)).*32", QRegularExpression::CaseInsensitiveOption), "MacOS106" },
+ { QRegularExpression( "(mac.*10[.,]{0,1}7)|(os.*x.*10[.,]{0,1}7)|(mac.*ML)|(os.*x.*ML)|(Mount)", QRegularExpression::CaseInsensitiveOption), "MacOS107_64" },
+ { QRegularExpression( "(mac.*10[.,]{0,1}8)|(os.*x.*10[.,]{0,1}8)|(Lion)", QRegularExpression::CaseInsensitiveOption), "MacOS108_64" },
+ { QRegularExpression( "(mac.*10[.,]{0,1}9)|(os.*x.*10[.,]{0,1}9)|(mac.*mav)|(os.*x.*mav)|(Mavericks)", QRegularExpression::CaseInsensitiveOption), "MacOS109_64" },
+ { QRegularExpression( "(mac.*yos)|(os.*x.*yos)|(Yosemite)", QRegularExpression::CaseInsensitiveOption), "MacOS1010_64" },
+ { QRegularExpression( "(mac.*cap)|(os.*x.*capit)|(Capitan)", QRegularExpression::CaseInsensitiveOption), "MacOS1011_64" },
+ { QRegularExpression( "(mac.*hig)|(os.*x.*high.*sierr)|(High Sierra)", QRegularExpression::CaseInsensitiveOption), "MacOS1013_64" },
+ { QRegularExpression( "(mac.*sie)|(os.*x.*sierr)|(Sierra)", QRegularExpression::CaseInsensitiveOption), "MacOS1012_64" },
+ { QRegularExpression("((Mac)|(Tig)|(Leop)|(Yose)|(os[ ]*x)).*64", QRegularExpression::CaseInsensitiveOption), "MacOS_64" },
+ { QRegularExpression("((Mac)|(Tig)|(Leop)|(Yose)|(os[ ]*x)).*32", QRegularExpression::CaseInsensitiveOption), "MacOS" },
+
+ /* Code names for Linux distributions: */
+ { QRegularExpression("((bianca)|(cassandra)|(celena)|(daryna)|(elyssa)|(felicia)|(gloria)|(helena)|(isadora)|(julia)|(katya)|(lisa)|(maya)|(nadia)|(olivia)|(petra)|(qiana)|(rebecca)|(rafaela)|(rosa)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu_64" },
+ { QRegularExpression("((bianca)|(cassandra)|(celena)|(daryna)|(elyssa)|(felicia)|(gloria)|(helena)|(isadora)|(julia)|(katya)|(lisa)|(maya)|(nadia)|(olivia)|(petra)|(qiana)|(rebecca)|(rafaela)|(rosa)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu" },
+ { QRegularExpression("((edgy)|(feisty)|(gutsy)|(hardy)|(intrepid)|(jaunty)|(karmic)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu_64" },
+ { QRegularExpression("((edgy)|(feisty)|(gutsy)|(hardy)|(intrepid)|(jaunty)|(karmic)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu" },
+ { QRegularExpression("((eft)|(fawn)|(gibbon)|(heron)|(ibex)|(jackalope)|(koala)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu_64" },
+ { QRegularExpression("((eft)|(fawn)|(gibbon)|(heron)|(ibex)|(jackalope)|(koala)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu" },
+ { QRegularExpression("((lucid)|(lynx)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu10_LTS_64" },
+ { QRegularExpression("((lucid)|(lynx)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu10_LTS" },
+ { QRegularExpression("((maverick)|(meerkat)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu10_64" },
+ { QRegularExpression("((maverick)|(meerkat)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu10" },
+ { QRegularExpression("((natty)|(narwhal)|(oneiric)|(ocelot)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu11_64" },
+ { QRegularExpression("((natty)|(narwhal)|(oneiric)|(ocelot)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu11" },
+ { QRegularExpression("((precise)|(pangolin)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu12_LTS_64" },
+ { QRegularExpression("((precise)|(pangolin)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu12_LTS" },
+ { QRegularExpression("((quantal)|(quetzal)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu12_64" },
+ { QRegularExpression("((quantal)|(quetzal)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu12" },
+ { QRegularExpression("((raring)|(ringtail)|(saucy)|(salamander)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu13_64" },
+ { QRegularExpression("((raring)|(ringtail)|(saucy)|(salamander)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu13" },
+ { QRegularExpression("((trusty)|(tahr)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu14_LTS_64" },
+ { QRegularExpression("((trusty)|(tahr)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu14_LTS" },
+ { QRegularExpression("((utopic)|(unicorn)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu14_64" },
+ { QRegularExpression("((utopic)|(unicorn)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu14" },
+ { QRegularExpression("((vivid)|(vervet)|(wily)|(werewolf)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu15_64" },
+ { QRegularExpression("((vivid)|(vervet)|(wily)|(werewolf)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu15" },
+ { QRegularExpression("((xenial)|(xerus)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu16_LTS_64" },
+ { QRegularExpression("((xenial)|(xerus)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu16_LTS" },
+ { QRegularExpression("((yakkety)|(yak)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu16_64" },
+ { QRegularExpression("((yakkety)|(yak)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu16" },
+ { QRegularExpression("((zesty)|(zapus)|(artful)|(aardvark)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu17_64" },
+ { QRegularExpression("((zesty)|(zapus)|(artful)|(aardvark)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu17" },
+ { QRegularExpression("((bionic)|(beaver)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu18_LTS_64" },
+ { QRegularExpression("((bionic)|(beaver)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu18_LTS" },
+ { QRegularExpression("((cosmic)|(cuttlefish)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu18_64" },
+ { QRegularExpression("((cosmic)|(cuttlefish)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu18" },
+ { QRegularExpression("((disco)|(dingo)|(eoan)|(ermine)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu19_64" },
+ { QRegularExpression("((disco)|(dingo)|(eoan)|(ermine)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu19" },
+ { QRegularExpression("((focal)|(fossa)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu20_LTS_64" },
+ { QRegularExpression("((groovy)|(gorilla)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu20_64" },
+ { QRegularExpression("((hirsute)|(hippo)|(impish)|(indri)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu21_64" },
+ { QRegularExpression("((jammy)|(jellyfish)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu22_LTS_64" },
+ { QRegularExpression("((kinetic)|(kudu)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu22_64" },
+ { QRegularExpression("((lunar)|(lobster)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu23_64" },
+ { QRegularExpression("sarge.*32", QRegularExpression::CaseInsensitiveOption), "Debian31" },
+ { QRegularExpression("^etch.*64", QRegularExpression::CaseInsensitiveOption), "Debian4_64" },
+ { QRegularExpression("^etch.*32", QRegularExpression::CaseInsensitiveOption), "Debian4" },
+ { QRegularExpression("lenny.*64", QRegularExpression::CaseInsensitiveOption), "Debian5_64" },
+ { QRegularExpression("lenny.*32", QRegularExpression::CaseInsensitiveOption), "Debian5" },
+ { QRegularExpression("squeeze.*64", QRegularExpression::CaseInsensitiveOption), "Debian6_64" },
+ { QRegularExpression("squeeze.*32", QRegularExpression::CaseInsensitiveOption), "Debian6" },
+ { QRegularExpression("wheezy.*64", QRegularExpression::CaseInsensitiveOption), "Debian7_64" },
+ { QRegularExpression("wheezy.*32", QRegularExpression::CaseInsensitiveOption), "Debian7" },
+ { QRegularExpression("jessie.*64", QRegularExpression::CaseInsensitiveOption), "Debian8_64" },
+ { QRegularExpression("jessie.*32", QRegularExpression::CaseInsensitiveOption), "Debian8" },
+ { QRegularExpression("stretch.*64", QRegularExpression::CaseInsensitiveOption), "Debian9_64" },
+ { QRegularExpression("stretch.*32", QRegularExpression::CaseInsensitiveOption), "Debian9" },
+ { QRegularExpression("buster.*64", QRegularExpression::CaseInsensitiveOption), "Debian10_64" },
+ { QRegularExpression("buster.*32", QRegularExpression::CaseInsensitiveOption), "Debian10" },
+ { QRegularExpression("bullseye.*64", QRegularExpression::CaseInsensitiveOption), "Debian11_64" },
+ { QRegularExpression("bullseye.*32", QRegularExpression::CaseInsensitiveOption), "Debian11" },
+ { QRegularExpression("bookworm.*64", QRegularExpression::CaseInsensitiveOption), "Debian12_64" },
+ { QRegularExpression("bookworm.*32", QRegularExpression::CaseInsensitiveOption), "Debian12" },
+ { QRegularExpression("((trixie)|(sid)).*64", QRegularExpression::CaseInsensitiveOption), "Debian_64" },
+ { QRegularExpression("((trixie)|(sid)).*32", QRegularExpression::CaseInsensitiveOption), "Debian" },
+ { QRegularExpression("((moonshine)|(werewolf)|(sulphur)|(cambridge)|(leonidas)|(constantine)|(goddard)|(laughlin)|(lovelock)|(verne)|(beefy)|(spherical)|(schrodinger)|(heisenberg)).*64", QRegularExpression::CaseInsensitiveOption), "Fedora_64" },
+ { QRegularExpression("((moonshine)|(werewolf)|(sulphur)|(cambridge)|(leonidas)|(constantine)|(goddard)|(laughlin)|(lovelock)|(verne)|(beefy)|(spherical)|(schrodinger)|(heisenberg)).*32", QRegularExpression::CaseInsensitiveOption), "Fedora" },
+ { QRegularExpression("((basilisk)|(emerald)|(teal)|(celadon)|(asparagus)|(mantis)|(dartmouth)|(bottle)|(harlequin)).*64", QRegularExpression::CaseInsensitiveOption), "OpenSUSE_64" },
+ { QRegularExpression("((basilisk)|(emerald)|(teal)|(celadon)|(asparagus)|(mantis)|(dartmouth)|(bottle)|(harlequin)).*32", QRegularExpression::CaseInsensitiveOption), "OpenSUSE" },
+
+ /* Regular names of Linux distributions: */
+ { QRegularExpression("Arc.*64", QRegularExpression::CaseInsensitiveOption), "ArchLinux_64" },
+ { QRegularExpression("Arc.*32", QRegularExpression::CaseInsensitiveOption), "ArchLinux" },
+ { QRegularExpression("Deb.*64", QRegularExpression::CaseInsensitiveOption), "Debian_64" },
+ { QRegularExpression("Deb.*32", QRegularExpression::CaseInsensitiveOption), "Debian" },
+ { QRegularExpression("SU.*Leap.*64", QRegularExpression::CaseInsensitiveOption), "OpenSUSE_Leap_64" },
+ { QRegularExpression("SU.*Tumble.*64", QRegularExpression::CaseInsensitiveOption), "OpenSUSE_Tumbleweed_64" },
+ { QRegularExpression("SU.*Tumble.*32", QRegularExpression::CaseInsensitiveOption), "OpenSUSE_Tumbleweed" },
+ { QRegularExpression("((SU)|(Nov)|(SLE)).*64", QRegularExpression::CaseInsensitiveOption), "OpenSUSE_64" },
+ { QRegularExpression("((SU)|(Nov)|(SLE)).*32", QRegularExpression::CaseInsensitiveOption), "OpenSUSE" },
+ { QRegularExpression("Fe.*64", QRegularExpression::CaseInsensitiveOption), "Fedora_64" },
+ { QRegularExpression("Fe.*32", QRegularExpression::CaseInsensitiveOption), "Fedora" },
+ { QRegularExpression("((Gen)|(Sab)).*64", QRegularExpression::CaseInsensitiveOption), "Gentoo_64" },
+ { QRegularExpression("((Gen)|(Sab)).*32", QRegularExpression::CaseInsensitiveOption), "Gentoo" },
+ { QRegularExpression("^Man.*64", QRegularExpression::CaseInsensitiveOption), "Mandriva_64" },
+ { QRegularExpression("^Man.*32", QRegularExpression::CaseInsensitiveOption), "Mandriva" },
+ { QRegularExpression("Op.*Man.*Lx.*64", QRegularExpression::CaseInsensitiveOption), "OpenMandriva_Lx_64" },
+ { QRegularExpression("Op.*Man.*Lx.*32", QRegularExpression::CaseInsensitiveOption), "OpenMandriva_Lx" },
+ { QRegularExpression("PCL.*OS.*64", QRegularExpression::CaseInsensitiveOption), "PCLinuxOS_64" },
+ { QRegularExpression("PCL.*OS.*32", QRegularExpression::CaseInsensitiveOption), "PCLinuxOS" },
+ { QRegularExpression("Mageia.*64", QRegularExpression::CaseInsensitiveOption), "Mageia_64" },
+ { QRegularExpression("Mageia.*32", QRegularExpression::CaseInsensitiveOption), "Mageia" },
+ { QRegularExpression("((Red)|(rhel)|(cen)).*64", QRegularExpression::CaseInsensitiveOption), "RedHat_64" },
+ { QRegularExpression("((Red)|(rhel)|(cen)).*32", QRegularExpression::CaseInsensitiveOption), "RedHat" },
+ { QRegularExpression("Tur.*64", QRegularExpression::CaseInsensitiveOption), "Turbolinux_64" },
+ { QRegularExpression("Tur.*32", QRegularExpression::CaseInsensitiveOption), "Turbolinux" },
+ { QRegularExpression("Lub.*64", QRegularExpression::CaseInsensitiveOption), "Lubuntu_64" },
+ { QRegularExpression("Lub.*32", QRegularExpression::CaseInsensitiveOption), "Lubuntu" },
+ { QRegularExpression("Xub.*64", QRegularExpression::CaseInsensitiveOption), "Xubuntu_64" },
+ { QRegularExpression("Xub.*32", QRegularExpression::CaseInsensitiveOption), "Xubuntu" },
+ { QRegularExpression("((Ub)|(Mint)).*64", QRegularExpression::CaseInsensitiveOption), "Ubuntu_64" },
+ { QRegularExpression("((Ub)|(Mint)).*32", QRegularExpression::CaseInsensitiveOption), "Ubuntu" },
+ { QRegularExpression("Xa.*64", QRegularExpression::CaseInsensitiveOption), "Xandros_64" },
+ { QRegularExpression("Xa.*32", QRegularExpression::CaseInsensitiveOption), "Xandros" },
+ { QRegularExpression("((Or)|(oel)|(^ol)).*64", QRegularExpression::CaseInsensitiveOption), "Oracle_64" },
+ { QRegularExpression("((Or)|(oel)|(^ol)).*32", QRegularExpression::CaseInsensitiveOption), "Oracle" },
+ { QRegularExpression("Knoppix", QRegularExpression::CaseInsensitiveOption), "Linux26" },
+ { QRegularExpression("Dsl", QRegularExpression::CaseInsensitiveOption), "Linux24" },
+ { QRegularExpression("((Lin)|(lnx)).*2.?2", QRegularExpression::CaseInsensitiveOption), "Linux22" },
+ { QRegularExpression("((Lin)|(lnx)).*2.?4.*64", QRegularExpression::CaseInsensitiveOption), "Linux24_64" },
+ { QRegularExpression("((Lin)|(lnx)).*2.?4.*32", QRegularExpression::CaseInsensitiveOption), "Linux24" },
+ { QRegularExpression("((((Lin)|(lnx)).*2.?6)|(LFS)).*64", QRegularExpression::CaseInsensitiveOption), "Linux26_64" },
+ { QRegularExpression("((((Lin)|(lnx)).*2.?6)|(LFS)).*32", QRegularExpression::CaseInsensitiveOption), "Linux26" },
+ { QRegularExpression("((Lin)|(lnx)).*64", QRegularExpression::CaseInsensitiveOption), "Linux26_64" },
+ { QRegularExpression("((Lin)|(lnx)).*32", QRegularExpression::CaseInsensitiveOption), "Linux26" },
+
+ /* Other: */
+ { QRegularExpression("L4", QRegularExpression::CaseInsensitiveOption), "L4" },
+ { QRegularExpression("((Fr.*B)|(fbsd)).*64", QRegularExpression::CaseInsensitiveOption), "FreeBSD_64" },
+ { QRegularExpression("((Fr.*B)|(fbsd)).*32", QRegularExpression::CaseInsensitiveOption), "FreeBSD" },
+ { QRegularExpression("Op.*B.*64", QRegularExpression::CaseInsensitiveOption), "OpenBSD_64" },
+ { QRegularExpression("Op.*B.*32", QRegularExpression::CaseInsensitiveOption), "OpenBSD" },
+ { QRegularExpression("Ne.*B.*64", QRegularExpression::CaseInsensitiveOption), "NetBSD_64" },
+ { QRegularExpression("Ne.*B.*32", QRegularExpression::CaseInsensitiveOption), "NetBSD" },
+ { QRegularExpression("Net", QRegularExpression::CaseInsensitiveOption), "Netware" },
+ { QRegularExpression("Rocki", QRegularExpression::CaseInsensitiveOption), "JRockitVE" },
+ { QRegularExpression("bs[23]{0,1}-", QRegularExpression::CaseInsensitiveOption), "VBoxBS_64" }, /* bootsector tests */
+ { QRegularExpression("Ot", QRegularExpression::CaseInsensitiveOption), "Other" },
+};
+
+static const QRegularExpression gs_Prefer32BitNamePatterns = QRegularExpression("(XP)", QRegularExpression::CaseInsensitiveOption);
+
+
+bool UIWizardNewVMNameOSTypeCommon::guessOSTypeFromName(UINameAndSystemEditor *pNameAndSystemEditor, QString strNewName)
+{
+ AssertReturn(pNameAndSystemEditor, false);
+
+ /* Append default architecture bit-count (64/32) if not already in the name, unless
+ it's XP or similar which is predominantly 32-bit: */
+ if (!strNewName.contains("32") && !strNewName.contains("64") && !strNewName.contains(gs_Prefer32BitNamePatterns))
+ strNewName += ARCH_BITS == 64 ? "64" : "32";
+
+ /* Search for a matching OS type based on the string the user typed already. */
+ for (size_t i = 0; i < RT_ELEMENTS(gs_OSTypePattern); ++i)
+ {
+ if (strNewName.contains(gs_OSTypePattern[i].pattern))
+ {
+ pNameAndSystemEditor->setType(uiCommon().vmGuestOSType(gs_OSTypePattern[i].pcstId));
+ return true;
+ }
+ }
+ return false;
+}
+
+bool UIWizardNewVMNameOSTypeCommon::guessOSTypeDetectedOSTypeString(UINameAndSystemEditor *pNameAndSystemEditor, QString strDetectedOSType)
+{
+ AssertReturn(pNameAndSystemEditor, false);
+ if (!strDetectedOSType.isEmpty())
+ {
+ CGuestOSType const osType = uiCommon().vmGuestOSType(strDetectedOSType);
+ if (!osType.isNull())
+ {
+ pNameAndSystemEditor->setType(osType);
+ return true;
+ }
+ /* The detectedOSType shall be a valid OS type ID. So, unless the UI is
+ out of sync with the types in main this shouldn't ever happen. */
+ AssertFailed();
+ }
+ pNameAndSystemEditor->setType(uiCommon().vmGuestOSType("Other"));
+ /* Return false to allow OS type guessing from name. See caller code: */
+ return false;
+}
+
+void UIWizardNewVMNameOSTypeCommon::composeMachineFilePath(UINameAndSystemEditor *pNameAndSystemEditor,
+ UIWizardNewVM *pWizard)
+{
+ if (!pNameAndSystemEditor || !pWizard)
+ return;
+ if (pNameAndSystemEditor->name().isEmpty() || pNameAndSystemEditor->path().isEmpty())
+ return;
+ /* Get VBox: */
+ CVirtualBox vbox = uiCommon().virtualBox();
+
+ /* Compose machine filename: */
+ pWizard->setMachineFilePath(vbox.ComposeMachineFilename(pNameAndSystemEditor->name(),
+ pWizard->machineGroup(),
+ QString(),
+ pNameAndSystemEditor->path()));
+ /* Compose machine folder/basename: */
+ const QFileInfo fileInfo(pWizard->machineFilePath());
+ pWizard->setMachineFolder(fileInfo.absolutePath());
+ pWizard->setMachineBaseName(pNameAndSystemEditor->name());
+}
+
+bool UIWizardNewVMNameOSTypeCommon::createMachineFolder(UINameAndSystemEditor *pNameAndSystemEditor,
+ UIWizardNewVM *pWizard)
+{
+ if (!pNameAndSystemEditor || !pWizard)
+ return false;
+ const QString &strMachineFolder = pWizard->machineFolder();
+ const QString &strCreatedFolder = pWizard->createdMachineFolder();
+
+ /* Cleanup previosly created folder if any: */
+ if (!cleanupMachineFolder(pWizard))
+ {
+ UINotificationMessage::cannotRemoveMachineFolder(strCreatedFolder, pWizard->notificationCenter());
+ return false;
+ }
+
+ /* Check if the folder already exists and check if it has been created by this wizard */
+ if (QDir(strMachineFolder).exists())
+ {
+ /* Looks like we have already created this folder for this run of the wizard. Just return */
+ if (strCreatedFolder == strMachineFolder)
+ return true;
+ /* The folder is there but not because of this wizard. Avoid overwriting a existing machine's folder */
+ else
+ {
+ UINotificationMessage::cannotOverwriteMachineFolder(strMachineFolder, pWizard->notificationCenter());
+ return false;
+ }
+ }
+
+ /* Try to create new folder (and it's predecessors): */
+ bool fMachineFolderCreated = QDir().mkpath(strMachineFolder);
+ if (!fMachineFolderCreated)
+ {
+ UINotificationMessage::cannotCreateMachineFolder(strMachineFolder, pWizard->notificationCenter());
+ return false;
+ }
+ pWizard->setCreatedMachineFolder(strMachineFolder);
+ return true;
+}
+
+bool UIWizardNewVMNameOSTypeCommon::cleanupMachineFolder(UIWizardNewVM *pWizard, bool fWizardCancel /* = false */)
+{
+ if (!pWizard)
+ return false;
+ const QString &strMachineFolder = pWizard->machineFolder();
+ const QString &strCreatedFolder = pWizard->createdMachineFolder();
+ /* Make sure folder was previosly created: */
+ if (strCreatedFolder.isEmpty())
+ return true;
+ /* Clean this folder if the machine folder has been changed by the user or we are cancelling the wizard: */
+ if (strCreatedFolder != strMachineFolder || fWizardCancel)
+ {
+ /* Try to cleanup folder (and it's predecessors): */
+ bool fMachineFolderRemoved = QDir(strCreatedFolder).removeRecursively();
+ /* Reset machine folder value: */
+ if (fMachineFolderRemoved)
+ pWizard->setCreatedMachineFolder(QString());
+ /* Return cleanup result: */
+ return fMachineFolderRemoved;
+ }
+ return true;
+}
+
+bool UIWizardNewVMNameOSTypeCommon::checkISOFile(UINameAndSystemEditor *pNameAndSystemEditor)
+{
+ if (!pNameAndSystemEditor)
+ return false;
+ const QString &strPath = pNameAndSystemEditor->ISOImagePath();
+ if (strPath.isNull() || strPath.isEmpty())
+ return true;
+ QFileInfo fileInfo(strPath);
+ if (!fileInfo.exists() || !fileInfo.isReadable())
+ return false;
+ return true;
+}
+
+UIWizardNewVMNameOSTypePage::UIWizardNewVMNameOSTypePage()
+ : m_pNameAndSystemLayout(0)
+ , m_pNameAndSystemEditor(0)
+ , m_pSkipUnattendedCheckBox(0)
+ , m_pNameOSTypeLabel(0)
+ , m_pInfoLabel(0)
+{
+ prepare();
+}
+
+void UIWizardNewVMNameOSTypePage::setISOFilePath(const QString &strISOFilePath)
+{
+ QFileInfo isoFileInfo(strISOFilePath);
+ if (isoFileInfo.exists() && m_pNameAndSystemEditor)
+ m_pNameAndSystemEditor->setISOImagePath(strISOFilePath);
+}
+
+void UIWizardNewVMNameOSTypePage::prepare()
+{
+ QVBoxLayout *pPageLayout = new QVBoxLayout(this);
+ if (pPageLayout)
+ {
+ m_pNameOSTypeLabel = new QIRichTextLabel(this);
+ if (m_pNameOSTypeLabel)
+ pPageLayout->addWidget(m_pNameOSTypeLabel);
+
+ /* Prepare Name and OS Type editor: */
+ pPageLayout->addWidget(createNameOSTypeWidgets());
+
+ pPageLayout->addStretch();
+ }
+
+ createConnections();
+}
+
+void UIWizardNewVMNameOSTypePage::createConnections()
+{
+ if (m_pNameAndSystemEditor)
+ {
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigNameChanged, this, &UIWizardNewVMNameOSTypePage::sltNameChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigPathChanged, this, &UIWizardNewVMNameOSTypePage::sltPathChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigOsTypeChanged, this, &UIWizardNewVMNameOSTypePage::sltOsTypeChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigImageChanged, this, &UIWizardNewVMNameOSTypePage::sltISOPathChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigOSFamilyChanged, this, &UIWizardNewVMNameOSTypePage::sltGuestOSFamilyChanged);
+ connect(m_pNameAndSystemEditor, &UINameAndSystemEditor::sigEditionChanged, this, &UIWizardNewVMNameOSTypePage::sltSelectedEditionChanged);
+ }
+ if (m_pSkipUnattendedCheckBox)
+ connect(m_pSkipUnattendedCheckBox, &QCheckBox::toggled, this, &UIWizardNewVMNameOSTypePage::sltSkipUnattendedInstallChanged);
+}
+
+bool UIWizardNewVMNameOSTypePage::isComplete() const
+{
+ markWidgets();
+ if (m_pNameAndSystemEditor->name().isEmpty())
+ return false;
+ return UIWizardNewVMNameOSTypeCommon::checkISOFile(m_pNameAndSystemEditor);
+}
+
+void UIWizardNewVMNameOSTypePage::sltNameChanged(const QString &strNewName)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ if (!m_userModifiedParameters.contains("GuestOSTypeFromISO"))
+ {
+ m_pNameAndSystemEditor->blockSignals(true);
+ if (UIWizardNewVMNameOSTypeCommon::guessOSTypeFromName(m_pNameAndSystemEditor, strNewName))
+ {
+ wizardWindow<UIWizardNewVM>()->setGuestOSType(m_pNameAndSystemEditor->type());
+ m_userModifiedParameters << "GuestOSTypeFromName";
+ }
+ m_pNameAndSystemEditor->blockSignals(false);
+ }
+ UIWizardNewVMNameOSTypeCommon::composeMachineFilePath(m_pNameAndSystemEditor, wizardWindow<UIWizardNewVM>());
+ emit completeChanged();
+}
+
+void UIWizardNewVMNameOSTypePage::sltPathChanged(const QString &strNewPath)
+{
+ Q_UNUSED(strNewPath);
+ UIWizardNewVMNameOSTypeCommon::composeMachineFilePath(m_pNameAndSystemEditor, wizardWindow<UIWizardNewVM>());
+}
+
+void UIWizardNewVMNameOSTypePage::sltOsTypeChanged()
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ //m_userModifiedParameters << "GuestOSType";
+ if (m_pNameAndSystemEditor)
+ wizardWindow<UIWizardNewVM>()->setGuestOSType(m_pNameAndSystemEditor->type());
+}
+
+void UIWizardNewVMNameOSTypePage::retranslateUi()
+{
+ setTitle(UIWizardNewVM::tr("Virtual machine Name and Operating System"));
+
+ if (m_pNameOSTypeLabel)
+ m_pNameOSTypeLabel->setText(UIWizardNewVM::tr("Please choose a descriptive name and destination folder for the new "
+ "virtual machine. The name you choose will be used throughout VirtualBox "
+ "to identify this machine. Additionally, you can select an ISO image which "
+ "may be used to install the guest operating system."));
+
+ if (m_pSkipUnattendedCheckBox)
+ {
+ m_pSkipUnattendedCheckBox->setText(UIWizardNewVM::tr("&Skip Unattended Installation"));
+ m_pSkipUnattendedCheckBox->setToolTip(UIWizardNewVM::tr("When checked, the unattended install is disabled and the selected ISO "
+ "is mounted on the vm."));
+ }
+
+ if (m_pNameAndSystemLayout && m_pNameAndSystemEditor)
+ m_pNameAndSystemLayout->setColumnMinimumWidth(0, m_pNameAndSystemEditor->firstColumnWidth());
+
+ updateInfoLabel();
+}
+
+void UIWizardNewVMNameOSTypePage::updateInfoLabel()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+
+ if (!m_pInfoLabel)
+ return;
+
+ /*
+ * Here are the scenarios we consider while updating the info label:
+ * - No ISO selected,
+ * - Unattended cannot determine OS type from the ISO,
+ * - Unattended can determine the OS type from the ISO but cannot install it,
+ * - User has disabled unattended explicitly,
+ * - Unattended install will kick off.
+ */
+ QString strMessage;
+ if (m_pNameAndSystemEditor->ISOImagePath().isEmpty())
+ strMessage = UIWizardNewVM::tr("No ISO image is selected, the guest OS will need to be installed manually.");
+ else if (pWizard->detectedOSTypeId().isEmpty())
+ strMessage = UIWizardNewVM::tr("OS type cannot be determined from the selected ISO, "
+ "the guest OS will need to be installed manually.");
+ else if (!pWizard->detectedOSTypeId().isEmpty())
+ {
+ QString strType = uiCommon().vmGuestOSTypeDescription(pWizard->detectedOSTypeId());
+ if (!pWizard->isUnattendedInstallSupported())
+ strMessage = UIWizardNewVM::tr("Detected OS type: %1. %2")
+ .arg(strType)
+ .arg(UIWizardNewVM::tr("This OS type cannot be installed unattendedly. "
+ "The install needs to be started manually."));
+ else if (pWizard->skipUnattendedInstall())
+ strMessage = UIWizardNewVM::tr("You have selected to skip unattended guest OS install, "
+ "the guest OS will need to be installed manually.");
+ else
+ strMessage = UIWizardNewVM::tr("Detected OS type: %1. %2")
+ .arg(strType)
+ .arg(UIWizardNewVM::tr("This OS type can be installed unattendedly. "
+ "The install will start after this wizard is closed."));
+ }
+
+ m_pInfoLabel->setText(QString("<img src=\":/session_info_16px.png\" style=\"vertical-align:top\"> %1").arg(strMessage));
+}
+
+void UIWizardNewVMNameOSTypePage::initializePage()
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+
+ retranslateUi();
+
+ /* Initialize this page's widgets etc: */
+ {
+ if (m_pNameAndSystemEditor)
+ {
+ m_pNameAndSystemEditor->setFocus();
+ setEditionSelectorEnabled();
+ }
+ setSkipCheckBoxEnable();
+ }
+
+ /* Initialize some of the wizard's parameters: */
+ {
+ if (m_pNameAndSystemEditor)
+ {
+ pWizard->setGuestOSFamilyId(m_pNameAndSystemEditor->familyId());
+ pWizard->setGuestOSType(m_pNameAndSystemEditor->type());
+ /* Vm name, folder, file path etc. will be initilized by composeMachineFilePath: */
+ }
+ }
+}
+
+bool UIWizardNewVMNameOSTypePage::validatePage()
+{
+ /* Try to create machine folder: */
+ return UIWizardNewVMNameOSTypeCommon::createMachineFolder(m_pNameAndSystemEditor, wizardWindow<UIWizardNewVM>());
+}
+
+void UIWizardNewVMNameOSTypePage::sltISOPathChanged(const QString &strPath)
+{
+ UIWizardNewVM *pWizard = qobject_cast<UIWizardNewVM*>(this->wizard());
+ AssertReturnVoid(pWizard);
+
+ pWizard->setISOFilePath(strPath);
+
+ bool const fOsTypeFixed = UIWizardNewVMNameOSTypeCommon::guessOSTypeDetectedOSTypeString(m_pNameAndSystemEditor,
+ pWizard->detectedOSTypeId());
+ if (fOsTypeFixed)
+ m_userModifiedParameters << "GuestOSTypeFromISO";
+ else /* Remove GuestOSTypeFromISO from the set if it is there: */
+ m_userModifiedParameters.remove("GuestOSTypeFromISO");
+
+ /* Update the global recent ISO path: */
+ QFileInfo fileInfo(strPath);
+ if (fileInfo.exists() && fileInfo.isReadable())
+ uiCommon().updateRecentlyUsedMediumListAndFolder(UIMediumDeviceType_DVD, strPath);
+
+ /* Populate the editions selector: */
+ if (m_pNameAndSystemEditor)
+ m_pNameAndSystemEditor->setEditionNameAndIndices(pWizard->detectedWindowsImageNames(),
+ pWizard->detectedWindowsImageIndices());
+
+ setSkipCheckBoxEnable();
+ setEditionSelectorEnabled();
+ updateInfoLabel();
+
+ /* Disable OS type selector(s) to prevent user from changing guest OS type manually: */
+ if (m_pNameAndSystemEditor)
+ {
+ m_pNameAndSystemEditor->setOSTypeStuffEnabled(!fOsTypeFixed);
+
+ /* Redetect the OS type using the name if detection or the step above failed: */
+ if (!fOsTypeFixed)
+ sltNameChanged(m_pNameAndSystemEditor->name());
+ }
+
+ emit completeChanged();
+}
+
+void UIWizardNewVMNameOSTypePage::sltGuestOSFamilyChanged(const QString &strGuestOSFamilyId)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setGuestOSFamilyId(strGuestOSFamilyId);
+}
+
+void UIWizardNewVMNameOSTypePage::sltSelectedEditionChanged(ulong uEditionIndex)
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ pWizard->setSelectedWindowImageIndex(uEditionIndex);
+ /* Update the OS type since IUnattended updates the detected OS type after edition (image index) changes: */
+ UIWizardNewVMNameOSTypeCommon::guessOSTypeDetectedOSTypeString(m_pNameAndSystemEditor, pWizard->detectedOSTypeId());
+}
+
+void UIWizardNewVMNameOSTypePage::sltSkipUnattendedInstallChanged(bool fSkip)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ m_userModifiedParameters << "SkipUnattendedInstall";
+ wizardWindow<UIWizardNewVM>()->setSkipUnattendedInstall(fSkip);
+ setEditionSelectorEnabled();
+ updateInfoLabel();
+}
+
+QWidget *UIWizardNewVMNameOSTypePage::createNameOSTypeWidgets()
+{
+ /* Prepare container widget: */
+ QWidget *pContainerWidget = new QWidget;
+ if (pContainerWidget)
+ {
+ /* Prepare layout: */
+ m_pNameAndSystemLayout = new QGridLayout(pContainerWidget);
+ if (m_pNameAndSystemLayout)
+ {
+ m_pNameAndSystemLayout->setContentsMargins(0, 0, 0, 0);
+
+ /* Prepare Name and OS Type editor: */
+ m_pNameAndSystemEditor = new UINameAndSystemEditor(0,
+ true /* fChooseName? */,
+ true /* fChoosePath? */,
+ true /* fChooseImage? */,
+ true /* fChooseEdition? */,
+ true /* fChooseType? */);
+ if (m_pNameAndSystemEditor)
+ m_pNameAndSystemLayout->addWidget(m_pNameAndSystemEditor, 0, 0, 1, 2);
+
+ /* Prepare Skip Unattended checkbox: */
+ m_pSkipUnattendedCheckBox = new QCheckBox;
+ if (m_pSkipUnattendedCheckBox)
+ m_pNameAndSystemLayout->addWidget(m_pSkipUnattendedCheckBox, 1, 1);
+ m_pInfoLabel = new QIRichTextLabel;
+ if (m_pInfoLabel)
+ m_pNameAndSystemLayout->addWidget(m_pInfoLabel, 2, 1);
+ }
+ }
+
+ /* Return container widget: */
+ return pContainerWidget;
+}
+
+void UIWizardNewVMNameOSTypePage::markWidgets() const
+{
+ if (m_pNameAndSystemEditor)
+ {
+ m_pNameAndSystemEditor->markNameEditor(m_pNameAndSystemEditor->name().isEmpty());
+ m_pNameAndSystemEditor->markImageEditor(!UIWizardNewVMNameOSTypeCommon::checkISOFile(m_pNameAndSystemEditor),
+ UIWizardNewVM::tr("Invalid file path or unreadable file"));
+ }
+}
+
+void UIWizardNewVMNameOSTypePage::setSkipCheckBoxEnable()
+{
+ AssertReturnVoid(m_pSkipUnattendedCheckBox && m_pNameAndSystemEditor);
+ const QString &strPath = m_pNameAndSystemEditor->ISOImagePath();
+ if (strPath.isEmpty())
+ {
+ m_pSkipUnattendedCheckBox->setEnabled(false);
+ return;
+ }
+ if (!isUnattendedInstallSupported())
+ {
+ m_pSkipUnattendedCheckBox->setEnabled(false);
+ return;
+ }
+
+ m_pSkipUnattendedCheckBox->setEnabled(UIWizardNewVMNameOSTypeCommon::checkISOFile(m_pNameAndSystemEditor));
+}
+
+bool UIWizardNewVMNameOSTypePage::isUnattendedEnabled() const
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturn(pWizard, false);
+ return pWizard->isUnattendedEnabled();
+}
+
+bool UIWizardNewVMNameOSTypePage::isUnattendedInstallSupported() const
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturn(pWizard, false);
+ return pWizard->isUnattendedInstallSupported();
+}
+
+
+void UIWizardNewVMNameOSTypePage::setEditionSelectorEnabled()
+{
+ if (!m_pNameAndSystemEditor || !m_pSkipUnattendedCheckBox)
+ return;
+ m_pNameAndSystemEditor->setEditionSelectorEnabled( !m_pNameAndSystemEditor->isEditionsSelectorEmpty()
+ && !m_pSkipUnattendedCheckBox->isChecked());
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMNameOSTypePage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMNameOSTypePage.h
new file mode 100644
index 00000000..6dcd6e6a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMNameOSTypePage.h
@@ -0,0 +1,114 @@
+/* $Id: UIWizardNewVMNameOSTypePage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMNameOSTypePage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMNameOSTypePage_h
+#define FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMNameOSTypePage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Local includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QCheckBox;
+class QGridLayout;
+class QIRichTextLabel;
+class UINameAndSystemEditor;
+class UIWizardNewVM;
+
+namespace UIWizardNewVMNameOSTypeCommon
+{
+ bool guessOSTypeFromName(UINameAndSystemEditor *pNameAndSystemEditor, QString strNewName);
+ bool guessOSTypeDetectedOSTypeString(UINameAndSystemEditor *pNameAndSystemEditor, QString strDetectedOSType);
+ bool createMachineFolder(UINameAndSystemEditor *pNameAndSystemEditor, UIWizardNewVM *pWizard);
+
+ /** Removes a previously created folder (if exists) before creating a new one.
+ * used during page cleanup and new folder creation. Called upon page Next/Back and
+ * wizard cancel */
+ bool cleanupMachineFolder(UIWizardNewVM *pWizard, bool fWizardCancel = false);
+ void composeMachineFilePath(UINameAndSystemEditor *pNameAndSystemEditor, UIWizardNewVM *pWizard);
+ /** Return false if ISO path is not empty but points to an missing or unreadable file. */
+ bool checkISOFile(UINameAndSystemEditor *pNameAndSystemEditor);
+}
+
+/** 1st page of the New Virtual Machine wizard (basic extension). */
+class UIWizardNewVMNameOSTypePage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructor. */
+ UIWizardNewVMNameOSTypePage();
+ void setISOFilePath(const QString &strISOFilePath);
+
+protected:
+
+ virtual bool isComplete() const; /* override final */
+ /** Validation stuff. */
+ virtual bool validatePage() RT_OVERRIDE;
+
+private slots:
+
+ void sltNameChanged(const QString &strNewText);
+ void sltPathChanged(const QString &strNewPath);
+ void sltOsTypeChanged();
+ void sltISOPathChanged(const QString &strPath);
+ void sltGuestOSFamilyChanged(const QString &strGuestOSFamilyId);
+ void sltSkipUnattendedInstallChanged(bool fSkip);
+ void sltSelectedEditionChanged(ulong uEditionIndex);
+
+private:
+
+ /** Translation stuff. */
+ void retranslateUi();
+
+ /** Prepare stuff. */
+ void prepare();
+ void createConnections();
+ void initializePage();
+ QWidget *createNameOSTypeWidgets();
+ void markWidgets() const;
+ void setSkipCheckBoxEnable();
+ bool isUnattendedEnabled() const;
+ bool isUnattendedInstallSupported() const;
+ void setEditionSelectorEnabled();
+ void updateInfoLabel();
+
+ /** @name Widgets
+ * @{ */
+ QGridLayout *m_pNameAndSystemLayout;
+ UINameAndSystemEditor *m_pNameAndSystemEditor;
+ QCheckBox *m_pSkipUnattendedCheckBox;
+ QIRichTextLabel *m_pNameOSTypeLabel;
+ QIRichTextLabel *m_pInfoLabel;
+ /** @} */
+ QSet<QString> m_userModifiedParameters;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMNameOSTypePage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMSummaryPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMSummaryPage.cpp
new file mode 100644
index 00000000..f469ce2e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMSummaryPage.cpp
@@ -0,0 +1,526 @@
+/* $Id: UIWizardNewVMSummaryPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMSummaryPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QHeaderView>
+#include <QList>
+#include <QFileInfo>
+#include <QVBoxLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "QITreeView.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINotificationCenter.h"
+#include "UITranslator.h"
+#include "UIWizardNewVMSummaryPage.h"
+#include "UIWizardDiskEditors.h"
+#include "UIWizardNewVM.h"
+
+/*********************************************************************************************************************************
+ * UIWizardNewVMSummaryItem definition. *
+*********************************************************************************************************************************/
+
+class UIWizardNewVMSummaryItem : public QITreeViewItem
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVMSummaryItem(QITreeView *pParentTree, const QString &strText,
+ const QVariant &data = QVariant(), const QIcon &icon = QIcon());
+ ~UIWizardNewVMSummaryItem();
+ virtual UIWizardNewVMSummaryItem *childItem(int iIndex) const /* override final */;
+ virtual int childCount() const /* override final */;
+ virtual QString text() const /* override final */;
+ const QVariant &data() const;
+ const QIcon &icon() const;
+
+ /** Returns the index of this item within its parent's children list. */
+ int row() const;
+ int childIndex(const UIWizardNewVMSummaryItem *pChild) const;
+ int columnCount() const;
+
+
+ UIWizardNewVMSummaryItem *addChild(const QString &strText,
+ const QVariant &data = QVariant(), const QIcon &icon = QIcon());
+
+
+ bool isSectionTitle() const;
+ void setIsSectionTitle(bool fIsSectionTitle);
+
+private:
+
+ UIWizardNewVMSummaryItem(UIWizardNewVMSummaryItem *pParentItem, const QString &strText,
+ const QVariant &data = QVariant(), const QIcon &icon = QIcon());
+
+ QString m_strText;
+ QVariant m_data;
+ QIcon m_icon;
+ QList<UIWizardNewVMSummaryItem*> m_childList;
+ bool m_fIsSectionTitle;
+};
+
+/*********************************************************************************************************************************
+ * UIWizardNewVMSummaryModel definition. *
+ *********************************************************************************************************************************/
+
+class UIWizardNewVMSummaryModel : public QAbstractItemModel
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVMSummaryModel(QITreeView *pParentTree);
+ ~UIWizardNewVMSummaryModel();
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const /* override final */;
+
+ QModelIndex index(int row, int column,
+ const QModelIndex &parent = QModelIndex()) const /* override final */;
+ QModelIndex parent(const QModelIndex &index) const /* override final */;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const /* override final */;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const /* override final */;
+
+ void populateData(UIWizardNewVM *pWizard);
+
+private:
+
+ UIWizardNewVMSummaryItem *m_pRootItem;
+
+};
+
+
+/*********************************************************************************************************************************
+* UIWizardNewVMSummaryItem implementation. *
+*********************************************************************************************************************************/
+
+
+UIWizardNewVMSummaryItem::UIWizardNewVMSummaryItem(QITreeView *pParentTree, const QString &strText,
+ const QVariant &data /* = QVariant() */, const QIcon &icon /* = QIcon() */)
+ : QITreeViewItem(pParentTree)
+ , m_strText(strText)
+ , m_data(data)
+ , m_icon(icon)
+ , m_fIsSectionTitle(false)
+{
+}
+
+UIWizardNewVMSummaryItem::UIWizardNewVMSummaryItem(UIWizardNewVMSummaryItem *pParentItem, const QString &strText,
+ const QVariant &data /* = QVariant() */, const QIcon &icon /* = QIcon() */)
+ : QITreeViewItem(pParentItem)
+ , m_strText(strText)
+ , m_data(data)
+ , m_icon(icon)
+ , m_fIsSectionTitle(false)
+{
+}
+
+UIWizardNewVMSummaryItem::~UIWizardNewVMSummaryItem()
+{
+ qDeleteAll(m_childList);
+}
+
+UIWizardNewVMSummaryItem *UIWizardNewVMSummaryItem::childItem(int iIndex) const
+{
+ if (iIndex >= m_childList.size())
+ return 0;
+ return m_childList[iIndex];
+}
+
+int UIWizardNewVMSummaryItem::childIndex(const UIWizardNewVMSummaryItem *pChild) const
+{
+ if (!pChild)
+ return 0;
+ return m_childList.indexOf(const_cast<UIWizardNewVMSummaryItem*>(pChild));
+}
+
+int UIWizardNewVMSummaryItem::row() const
+{
+ UIWizardNewVMSummaryItem *pParent = qobject_cast<UIWizardNewVMSummaryItem*>(parentItem());
+ if (!pParent)
+ return 0;
+ return pParent->childIndex(this);
+}
+
+
+int UIWizardNewVMSummaryItem::childCount() const
+{
+ return m_childList.size();
+}
+
+
+QString UIWizardNewVMSummaryItem::text() const
+{
+ return m_strText;
+}
+
+const QVariant &UIWizardNewVMSummaryItem::data() const
+{
+ return m_data;
+}
+
+const QIcon &UIWizardNewVMSummaryItem::icon() const
+{
+ return m_icon;
+}
+
+int UIWizardNewVMSummaryItem::columnCount() const
+{
+ if (m_data.isValid())
+ return 2;
+ return 1;
+}
+
+UIWizardNewVMSummaryItem *UIWizardNewVMSummaryItem::addChild(const QString &strText, const QVariant &data /* = QVariant() */, const QIcon &icon /* = QIcon() */)
+{
+ UIWizardNewVMSummaryItem *pNewItem = new UIWizardNewVMSummaryItem(this, strText, data, icon);
+ m_childList << pNewItem;
+ return pNewItem;
+}
+
+void UIWizardNewVMSummaryItem::setIsSectionTitle(bool fIsSectionTitle)
+{
+ m_fIsSectionTitle = fIsSectionTitle;
+}
+
+bool UIWizardNewVMSummaryItem::isSectionTitle() const
+{
+ return m_fIsSectionTitle;
+}
+
+
+/*********************************************************************************************************************************
+* UIWizardNewVMSummaryModel implementation. *
+*********************************************************************************************************************************/
+
+UIWizardNewVMSummaryModel::UIWizardNewVMSummaryModel(QITreeView *pParentTree)
+ : QAbstractItemModel(pParentTree)
+ , m_pRootItem(0)
+{
+}
+
+UIWizardNewVMSummaryModel::~UIWizardNewVMSummaryModel()
+{
+ delete m_pRootItem;
+}
+
+QVariant UIWizardNewVMSummaryModel::data(const QModelIndex &index, int role /* = Qt::DisplayRole */) const
+{
+ if (!index.isValid())
+ return QVariant();
+ UIWizardNewVMSummaryItem *pItem = static_cast<UIWizardNewVMSummaryItem*>(index.internalPointer());
+ if (!pItem)
+ return QVariant();
+ if (role == Qt::DisplayRole)
+ {
+ switch (index.column())
+ {
+ case 0:
+ return pItem->text();
+ break;
+ case 1:
+ return pItem->data();
+ break;
+ default:
+ break;
+ }
+ }
+ else if (role == Qt::DecorationRole)
+ {
+ if (index.column() == 0 && !pItem->icon().isNull())
+ return pItem->icon();
+ }
+ else if (role == Qt::FontRole)
+ {
+ UIWizardNewVMSummaryItem *pItem = static_cast<UIWizardNewVMSummaryItem*>(index.internalPointer());
+ if (pItem && pItem->isSectionTitle())
+ {
+ QFont font = qApp->font();
+ font.setBold(true);
+ return font;
+ }
+ }
+ return QVariant();
+}
+
+QModelIndex UIWizardNewVMSummaryModel::index(int row, int column, const QModelIndex &parent /* = QModelIndex() */) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ UIWizardNewVMSummaryItem *pParentItem;
+
+ if (!parent.isValid())
+ pParentItem = m_pRootItem;
+ else
+ pParentItem = static_cast<UIWizardNewVMSummaryItem*>(parent.internalPointer());
+
+ UIWizardNewVMSummaryItem *pChildItem = pParentItem->childItem(row);
+ if (pChildItem)
+ return createIndex(row, column, pChildItem);
+ else
+ return QModelIndex();
+}
+
+QModelIndex UIWizardNewVMSummaryModel::parent(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ UIWizardNewVMSummaryItem *pChildItem = static_cast<UIWizardNewVMSummaryItem*>(index.internalPointer());
+ if (!pChildItem)
+ return QModelIndex();
+
+ UIWizardNewVMSummaryItem *pParentItem = static_cast<UIWizardNewVMSummaryItem*>(pChildItem->parentItem());
+
+ if (pParentItem == m_pRootItem)
+ return QModelIndex();
+
+ return createIndex(pParentItem->row(), 0, pParentItem);
+}
+
+int UIWizardNewVMSummaryModel::rowCount(const QModelIndex &parent /* = QModelIndex() */) const
+{
+ if (parent.column() > 0)
+ return 0;
+ UIWizardNewVMSummaryItem *pItem = 0;
+ if (!parent.isValid())
+ pItem = m_pRootItem;
+ else
+ pItem = static_cast<UIWizardNewVMSummaryItem*>(parent.internalPointer());
+
+ if (pItem)
+ return pItem->childCount();
+ return 0;
+}
+
+int UIWizardNewVMSummaryModel::columnCount(const QModelIndex &parentIndex /* = QModelIndex() */) const
+{
+ Q_UNUSED(parentIndex);
+ return 2;
+#if 0
+ AssertReturn(m_pRootItem, 0);
+ if (parentIndex.isValid())
+ {
+ UIWizardNewVMSummaryItem *pParent = static_cast<UIWizardNewVMSummaryItem*>(parentIndex.internalPointer());
+ if (pParent)
+ return pParent->columnCount();
+
+ }
+ return m_pRootItem->columnCount();
+#endif
+}
+
+void UIWizardNewVMSummaryModel::populateData(UIWizardNewVM *pWizard)
+{
+ QITreeView *pParentTree = qobject_cast<QITreeView*>(QObject::parent());
+
+ AssertReturnVoid(pWizard && pParentTree);
+ if (m_pRootItem)
+ delete m_pRootItem;
+ m_pRootItem = new UIWizardNewVMSummaryItem(pParentTree, "root");
+
+ UIWizardNewVMSummaryItem *pNameRoot = m_pRootItem->addChild(UIWizardNewVM::tr("Machine Name and OS Type"),
+ QVariant(), UIIconPool::iconSet(":/name_16px.png"));
+ pNameRoot->setIsSectionTitle(true);
+
+ /* Name and OS Type page stuff: */
+ pNameRoot->addChild(UIWizardNewVM::tr("Machine Name"), pWizard->machineBaseName());
+ pNameRoot->addChild(UIWizardNewVM::tr("Machine Folder"), pWizard->machineFolder());
+ pNameRoot->addChild(UIWizardNewVM::tr("ISO Image"), pWizard->ISOFilePath());
+ pNameRoot->addChild(UIWizardNewVM::tr("Guest OS Type"), pWizard->guestOSType().GetDescription());
+
+ const QString &ISOPath = pWizard->ISOFilePath();
+ if (!ISOPath.isNull() && !ISOPath.isEmpty())
+ pNameRoot->addChild(UIWizardNewVM::tr("Skip Unattended Install"), pWizard->skipUnattendedInstall());
+
+ /* Unattended install related info: */
+ if (pWizard->isUnattendedEnabled())
+ {
+ UIWizardNewVMSummaryItem *pUnattendedRoot = m_pRootItem->addChild(UIWizardNewVM::tr("Unattended Install"), QVariant(),
+ UIIconPool::iconSet(":/extension_pack_install_16px.png"));
+ pUnattendedRoot->setIsSectionTitle(true);
+
+ pUnattendedRoot->addChild(UIWizardNewVM::tr("Username"), pWizard->userName());
+ pUnattendedRoot->addChild(UIWizardNewVM::tr("Product Key"), pWizard->installGuestAdditions());
+ pUnattendedRoot->addChild(UIWizardNewVM::tr("Hostname/Domain Name"), pWizard->hostnameDomainName());
+ pUnattendedRoot->addChild(UIWizardNewVM::tr("Install in Background"), pWizard->startHeadless());
+ pUnattendedRoot->addChild(UIWizardNewVM::tr("Install Guest Additions"), pWizard->installGuestAdditions());
+ if (pWizard->installGuestAdditions())
+ pUnattendedRoot->addChild(UIWizardNewVM::tr("Guest Additions ISO"), pWizard->guestAdditionsISOPath());
+ }
+
+
+ UIWizardNewVMSummaryItem *pHardwareRoot = m_pRootItem->addChild(UIWizardNewVM::tr("Hardware"), QVariant(),
+ UIIconPool::iconSet(":/cpu_16px.png"));
+ pHardwareRoot->setIsSectionTitle(true);
+ pHardwareRoot->addChild(UIWizardNewVM::tr("Base Memory"), pWizard->memorySize());
+ pHardwareRoot->addChild(UIWizardNewVM::tr("Processor(s)"), pWizard->CPUCount());
+ pHardwareRoot->addChild(UIWizardNewVM::tr("EFI Enable"), pWizard->EFIEnabled());
+
+ /* Disk related info: */
+ UIWizardNewVMSummaryItem *pDiskRoot = m_pRootItem->addChild(UIWizardNewVM::tr("Disk"), QVariant(),
+ UIIconPool::iconSet(":/hd_16px.png"));
+ pDiskRoot->setIsSectionTitle(true);
+ if (pWizard->diskSource() == SelectedDiskSource_New)
+ {
+ pDiskRoot->addChild(UIWizardNewVM::tr("Disk Size"), UITranslator::formatSize(pWizard->mediumSize()));
+ const QVector<KMediumVariant> &mediumVariants = pWizard->mediumVariants();
+ pDiskRoot->addChild(UIWizardNewVM::tr("Pre-allocate Full Size"),
+ (mediumVariants.contains(KMediumVariant_Fixed) ? true : false));
+ }
+ else if (pWizard->diskSource() == SelectedDiskSource_Existing)
+ pDiskRoot->addChild(UIWizardNewVM::tr("Attached Disk"), pWizard->mediumPath());
+ else if (pWizard->diskSource() == SelectedDiskSource_Empty)
+ pDiskRoot->addChild(UIWizardNewVM::tr("Attached Disk"), UIWizardNewVM::tr("None"));
+
+ Q_UNUSED(pDiskRoot);
+
+}
+
+
+/*********************************************************************************************************************************
+* UIWizardNewVMSummaryPage implementation. *
+*********************************************************************************************************************************/
+
+UIWizardNewVMSummaryPage::UIWizardNewVMSummaryPage()
+ : m_pLabel(0)
+ , m_pTree(0)
+{
+ prepare();
+}
+
+
+void UIWizardNewVMSummaryPage::prepare()
+{
+ QVBoxLayout *pMainLayout = new QVBoxLayout(this);
+
+ m_pLabel = new QIRichTextLabel(this);
+ pMainLayout->addWidget(m_pLabel);
+
+ m_pTree = new QITreeView;
+ QString sty("QTreeView::branch {"
+ "background: palette(base);"
+ "}");
+
+ if (m_pTree)
+ {
+ //m_pTree->setStyleSheet(sty);
+ m_pTree->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
+ m_pTree->setAlternatingRowColors(true);
+ m_pModel = new UIWizardNewVMSummaryModel(m_pTree);
+ m_pTree->setModel(m_pModel);
+ pMainLayout->addWidget(m_pTree);
+ }
+
+ //pMainLayout->addStretch();
+
+ createConnections();
+}
+
+void UIWizardNewVMSummaryPage::createConnections()
+{
+}
+
+void UIWizardNewVMSummaryPage::retranslateUi()
+{
+ setTitle(UIWizardNewVM::tr("Summary"));
+ if (m_pLabel)
+ m_pLabel->setText(UIWizardNewVM::tr("The following table summarizes the configuration you have"
+ " chosen for the new virtual machine. When you are happy with the configuration"
+ " press Finish to create the virtual machine. Alternatively you can go back"
+ " and modify the configuration."));
+}
+
+void UIWizardNewVMSummaryPage::initializePage()
+{
+ retranslateUi();
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard && m_pModel);
+ if (m_pModel)
+ m_pModel->populateData(pWizard);
+ if (m_pTree)
+ {
+ m_pTree->expandToDepth(4);
+ m_pTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
+ }
+}
+
+bool UIWizardNewVMSummaryPage::isComplete() const
+{
+ return true;
+}
+
+bool UIWizardNewVMSummaryPage::validatePage()
+{
+ bool fResult = true;
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturn(pWizard, false);
+
+ /* Make sure user really intents to creae a vm with no hard drive: */
+ if (pWizard->diskSource() == SelectedDiskSource_Empty)
+ {
+ /* Ask user about disk-less machine unless that's the recommendation: */
+ if (!pWizard->emptyDiskRecommended())
+ {
+ if (!msgCenter().confirmHardDisklessMachine(this))
+ return false;
+ }
+ }
+ else if (pWizard->diskSource() == SelectedDiskSource_New)
+ {
+ /* Check if the path we will be using for hard drive creation exists: */
+ const QString &strMediumPath = pWizard->mediumPath();
+ fResult = !QFileInfo(strMediumPath).exists();
+ if (!fResult)
+ {
+ UINotificationMessage::cannotOverwriteMediumStorage(strMediumPath, wizard()->notificationCenter());
+ return fResult;
+ }
+ /* Check FAT size limitation of the host hard drive: */
+ fResult = UIWizardDiskEditors::checkFATSizeLimitation(pWizard->mediumVariant(),
+ strMediumPath,
+ pWizard->mediumSize());
+ if (!fResult)
+ {
+ UINotificationMessage::cannotCreateMediumStorageInFAT(strMediumPath, wizard()->notificationCenter());
+ return fResult;
+ }
+
+ /* Try to create the hard drive:*/
+ fResult = pWizard->createVirtualDisk();
+ /*Don't show any error message here since UIWizardNewVM::createVirtualDisk already does so: */
+ if (!fResult)
+ return fResult;
+ }
+
+ return pWizard->createVM();
+}
+
+
+#include "UIWizardNewVMSummaryPage.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMSummaryPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMSummaryPage.h
new file mode 100644
index 00000000..a4756b01
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMSummaryPage.h
@@ -0,0 +1,66 @@
+/* $Id: UIWizardNewVMSummaryPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMSummaryPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMSummaryPage_h
+#define FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMSummaryPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QIRichTextLabel;
+class QITreeView;
+class UIWizardNewVMSummaryModel;
+
+class UIWizardNewVMSummaryPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVMSummaryPage();
+
+private:
+
+ void prepare();
+ void createConnections();
+ virtual void retranslateUi() /* override final */;
+ virtual void initializePage() /* override final */;
+ virtual bool isComplete() const /* override final */;
+ virtual bool validatePage() /* override final */;
+ /** @name Widgets
+ * @{ */
+ QIRichTextLabel *m_pLabel;
+ QITreeView *m_pTree;
+ /** @} */
+ UIWizardNewVMSummaryModel *m_pModel;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMSummaryPage_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMUnattendedPage.cpp b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMUnattendedPage.cpp
new file mode 100644
index 00000000..5203786e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMUnattendedPage.cpp
@@ -0,0 +1,258 @@
+/* $Id: UIWizardNewVMUnattendedPage.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMUnattendedPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QFileInfo>
+#include <QGridLayout>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "UIWizardNewVMEditors.h"
+#include "UIWizardNewVMUnattendedPage.h"
+#include "UIWizardNewVM.h"
+
+bool UIWizardNewVMUnattendedCommon::checkGAISOFile(const QString &strPath)
+{
+ if (strPath.isNull() || strPath.isEmpty())
+ return false;
+ QFileInfo fileInfo(strPath);
+ if (!fileInfo.exists() || !fileInfo.isReadable())
+ return false;
+ return true;
+}
+
+UIWizardNewVMUnattendedPage::UIWizardNewVMUnattendedPage()
+ : m_pLabel(0)
+ , m_pAdditionalOptionsContainer(0)
+ , m_pGAInstallationISOContainer(0)
+ , m_pUserNamePasswordGroupBox(0)
+{
+ prepare();
+}
+
+void UIWizardNewVMUnattendedPage::prepare()
+{
+ QGridLayout *pMainLayout = new QGridLayout(this);
+
+ m_pLabel = new QIRichTextLabel(this);
+ if (m_pLabel)
+ pMainLayout->addWidget(m_pLabel, 0, 0, 1, 2);
+
+ m_pUserNamePasswordGroupBox = new UIUserNamePasswordGroupBox;
+ AssertReturnVoid(m_pUserNamePasswordGroupBox);
+ pMainLayout->addWidget(m_pUserNamePasswordGroupBox, 1, 0, 1, 1);
+
+ m_pAdditionalOptionsContainer = new UIAdditionalUnattendedOptions;
+ AssertReturnVoid(m_pAdditionalOptionsContainer);
+ pMainLayout->addWidget(m_pAdditionalOptionsContainer, 1, 1, 1, 1);
+
+ m_pGAInstallationISOContainer = new UIGAInstallationGroupBox;
+ AssertReturnVoid(m_pGAInstallationISOContainer);
+ pMainLayout->addWidget(m_pGAInstallationISOContainer, 2, 0, 1, 2);
+
+ pMainLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 4, 0, 1, 2);
+
+ createConnections();
+}
+
+void UIWizardNewVMUnattendedPage::createConnections()
+{
+ if (m_pUserNamePasswordGroupBox)
+ {
+ connect(m_pUserNamePasswordGroupBox, &UIUserNamePasswordGroupBox::sigPasswordChanged,
+ this, &UIWizardNewVMUnattendedPage::sltPasswordChanged);
+ connect(m_pUserNamePasswordGroupBox, &UIUserNamePasswordGroupBox::sigUserNameChanged,
+ this, &UIWizardNewVMUnattendedPage::sltUserNameChanged);
+ }
+ if (m_pGAInstallationISOContainer)
+ {
+ connect(m_pGAInstallationISOContainer, &UIGAInstallationGroupBox::toggled,
+ this, &UIWizardNewVMUnattendedPage::sltInstallGACheckBoxToggle);
+ connect(m_pGAInstallationISOContainer, &UIGAInstallationGroupBox::sigPathChanged,
+ this, &UIWizardNewVMUnattendedPage::sltGAISOPathChanged);
+ }
+
+ if (m_pAdditionalOptionsContainer)
+ {
+ connect(m_pAdditionalOptionsContainer, &UIAdditionalUnattendedOptions::sigHostnameDomainNameChanged,
+ this, &UIWizardNewVMUnattendedPage::sltHostnameDomainNameChanged);
+ connect(m_pAdditionalOptionsContainer, &UIAdditionalUnattendedOptions::sigProductKeyChanged,
+ this, &UIWizardNewVMUnattendedPage::sltProductKeyChanged);
+ connect(m_pAdditionalOptionsContainer, &UIAdditionalUnattendedOptions::sigStartHeadlessChanged,
+ this, &UIWizardNewVMUnattendedPage::sltStartHeadlessChanged);
+ }
+}
+
+
+void UIWizardNewVMUnattendedPage::retranslateUi()
+{
+ setTitle(UIWizardNewVM::tr("Unattended Guest OS Install Setup"));
+ if (m_pLabel)
+ m_pLabel->setText(UIWizardNewVM::tr("You can configure the unattended guest OS install by modifying username, password, "
+ "and hostname. Additionally you can enable guest additions install. "
+ "For Microsoft Windows guests it is possible to provide a product key."));
+ if (m_pUserNamePasswordGroupBox)
+ m_pUserNamePasswordGroupBox->setTitle(UIWizardNewVM::tr("Username and Password"));
+}
+
+
+void UIWizardNewVMUnattendedPage::initializePage()
+{
+ if (m_pAdditionalOptionsContainer)
+ m_pAdditionalOptionsContainer->disableEnableProductKeyWidgets(isProductKeyWidgetEnabled());
+ retranslateUi();
+
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ AssertReturnVoid(pWizard);
+ /* Initialize user/password if they are not modified by the user: */
+ if (m_pUserNamePasswordGroupBox)
+ {
+ m_pUserNamePasswordGroupBox->blockSignals(true);
+ if (!m_userModifiedParameters.contains("UserName"))
+ m_pUserNamePasswordGroupBox->setUserName(pWizard->userName());
+ if (!m_userModifiedParameters.contains("Password"))
+ m_pUserNamePasswordGroupBox->setPassword(pWizard->password());
+ m_pUserNamePasswordGroupBox->blockSignals(false);
+ }
+ if (m_pAdditionalOptionsContainer)
+ {
+ m_pAdditionalOptionsContainer->blockSignals(true);
+
+ if (!m_userModifiedParameters.contains("HostnameDomainName"))
+ {
+ m_pAdditionalOptionsContainer->setHostname(pWizard->machineBaseName());
+ m_pAdditionalOptionsContainer->setDomainName("myguest.virtualbox.org");
+ /* Initialize unattended hostname here since we cannot get the efault value from CUnattended this early (unlike username etc): */
+ if (m_pAdditionalOptionsContainer->isHostnameComplete())
+ pWizard->setHostnameDomainName(m_pAdditionalOptionsContainer->hostnameDomainName());
+ }
+ m_pAdditionalOptionsContainer->blockSignals(false);
+ }
+ if (m_pGAInstallationISOContainer && !m_userModifiedParameters.contains("InstallGuestAdditions"))
+ {
+ m_pGAInstallationISOContainer->blockSignals(true);
+ m_pGAInstallationISOContainer->setChecked(pWizard->installGuestAdditions());
+ m_pGAInstallationISOContainer->blockSignals(false);
+ }
+
+ if (m_pGAInstallationISOContainer && !m_userModifiedParameters.contains("GuestAdditionsISOPath"))
+ {
+ m_pGAInstallationISOContainer->blockSignals(true);
+ m_pGAInstallationISOContainer->setPath(pWizard->guestAdditionsISOPath());
+ m_pGAInstallationISOContainer->blockSignals(false);
+ }
+}
+
+bool UIWizardNewVMUnattendedPage::isComplete() const
+{
+ markWidgets();
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ if (pWizard && pWizard->installGuestAdditions() &&
+ m_pGAInstallationISOContainer &&
+ !UIWizardNewVMUnattendedCommon::checkGAISOFile(m_pGAInstallationISOContainer->path()))
+ return false;
+ if (m_pUserNamePasswordGroupBox && !m_pUserNamePasswordGroupBox->isComplete())
+ return false;
+ if (m_pAdditionalOptionsContainer && !m_pAdditionalOptionsContainer->isComplete())
+ return false;
+ return true;
+}
+
+void UIWizardNewVMUnattendedPage::sltInstallGACheckBoxToggle(bool fEnabled)
+{
+ wizardWindow<UIWizardNewVM>()->setInstallGuestAdditions(fEnabled);
+ m_userModifiedParameters << "InstallGuestAdditions";
+ emit completeChanged();
+}
+
+void UIWizardNewVMUnattendedPage::sltGAISOPathChanged(const QString &strPath)
+{
+ wizardWindow<UIWizardNewVM>()->setGuestAdditionsISOPath(strPath);
+ m_userModifiedParameters << "GuestAdditionsISOPath";
+ emit completeChanged();
+}
+
+void UIWizardNewVMUnattendedPage::sltPasswordChanged(const QString &strPassword)
+{
+ wizardWindow<UIWizardNewVM>()->setPassword(strPassword);
+ m_userModifiedParameters << "Password";
+ emit completeChanged();
+}
+
+void UIWizardNewVMUnattendedPage::sltUserNameChanged(const QString &strUserName)
+{
+ wizardWindow<UIWizardNewVM>()->setUserName(strUserName);
+ m_userModifiedParameters << "UserName";
+ emit completeChanged();
+}
+
+bool UIWizardNewVMUnattendedPage::isProductKeyWidgetEnabled() const
+{
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ if (!pWizard || !pWizard->isUnattendedEnabled() || !pWizard->isGuestOSTypeWindows())
+ return false;
+ return true;
+}
+
+void UIWizardNewVMUnattendedPage::sltHostnameDomainNameChanged(const QString &strHostnameDomainName, bool fIsComplete)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ emit completeChanged();
+
+ if (fIsComplete)
+ {
+ wizardWindow<UIWizardNewVM>()->setHostnameDomainName(strHostnameDomainName);
+ m_userModifiedParameters << "HostnameDomainName";
+ }
+}
+
+void UIWizardNewVMUnattendedPage::sltProductKeyChanged(const QString &strProductKey)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ m_userModifiedParameters << "ProductKey";
+ wizardWindow<UIWizardNewVM>()->setProductKey(strProductKey);
+}
+
+void UIWizardNewVMUnattendedPage::sltStartHeadlessChanged(bool fStartHeadless)
+{
+ m_userModifiedParameters << "StartHeadless";
+ wizardWindow<UIWizardNewVM>()->setStartHeadless(fStartHeadless);
+}
+
+void UIWizardNewVMUnattendedPage::markWidgets() const
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ UIWizardNewVM *pWizard = wizardWindow<UIWizardNewVM>();
+ if (pWizard && pWizard->installGuestAdditions() && m_pGAInstallationISOContainer)
+ m_pGAInstallationISOContainer->mark();
+}
+
+void UIWizardNewVMUnattendedPage::sltSelectedWindowsImageChanged(ulong uImageIndex)
+{
+ AssertReturnVoid(wizardWindow<UIWizardNewVM>());
+ wizardWindow<UIWizardNewVM>()->setSelectedWindowImageIndex(uImageIndex);
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMUnattendedPage.h b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMUnattendedPage.h
new file mode 100644
index 00000000..17297a66
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/wizards/newvm/UIWizardNewVMUnattendedPage.h
@@ -0,0 +1,91 @@
+/* $Id: UIWizardNewVMUnattendedPage.h $ */
+/** @file
+ * VBox Qt GUI - UIWizardNewVMUnattendedPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMUnattendedPage_h
+#define FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMUnattendedPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Local includes: */
+#include "UINativeWizardPage.h"
+
+/* Forward declarations: */
+class QIRichTextLabel;
+class UIAdditionalUnattendedOptions;
+class UIGAInstallationGroupBox;
+class UIUserNamePasswordGroupBox;
+class UIWindowsISOImageSelector;
+
+namespace UIWizardNewVMUnattendedCommon
+{
+ /** Returns false if ISO path selector is non empty but has invalid file path. */
+ bool checkGAISOFile(const QString &strPatho);
+}
+
+class UIWizardNewVMUnattendedPage : public UINativeWizardPage
+{
+ Q_OBJECT;
+
+public:
+
+ UIWizardNewVMUnattendedPage();
+
+private slots:
+
+ void sltInstallGACheckBoxToggle(bool fChecked);
+ void sltGAISOPathChanged(const QString &strPath);
+ void sltPasswordChanged(const QString &strPassword);
+ void sltUserNameChanged(const QString &strUserName);
+ void sltHostnameDomainNameChanged(const QString &strHostnameDomainName, bool fIsComplete);
+ void sltProductKeyChanged(const QString &strProductKey);
+ void sltStartHeadlessChanged(bool fStartHeadless);
+ void sltSelectedWindowsImageChanged(ulong uImageIndex);
+
+private:
+
+ void prepare();
+ void createConnections();
+
+ void retranslateUi();
+ void initializePage();
+ bool isComplete() const;
+ /** Returns true if we show the widgets for guest os product key. */
+ bool isProductKeyWidgetEnabled() const;
+ void markWidgets() const;
+
+ /** @name Widgets
+ * @{ */
+ QIRichTextLabel *m_pLabel;
+ UIAdditionalUnattendedOptions *m_pAdditionalOptionsContainer;
+ UIGAInstallationGroupBox *m_pGAInstallationISOContainer;
+ UIUserNamePasswordGroupBox *m_pUserNamePasswordGroupBox;
+ /** @} */
+ QSet<QString> m_userModifiedParameters;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_newvm_UIWizardNewVMUnattendedPage_h */